\dbsp

\lstset{language=awk}
\lstset{backgroundcolor=\color{listinggray}}
\lstset{frameround=tttt}
\lstset{basicstyle=\tiny}
\lstset{showstringspaces=false, showspaces=false, showtabs=false}
\begin{center}
\ssp
\begin{lstlisting}[frame=trBL]{}
#!/usr/bin/gawk -f
#/* vim: set filteype=awk : */ -*- awk -*-

# A Design for A Randomized Binary 
# Search Tree Discretization Method
# By DJ Boland

# Multipurpose implementation.  Allows dynamic tree sizing, tree size, and
# variable node size requirement for substitution

#------------------------------------------------------------------------------

BEGIN{	
	FS=" *, *";		#Fields seperated by a comma and its preceeding
	OFS=",";           	# and trailing spaces.
	SUBSEP="#"		#An pound sign will be used as the default seperator
	IGNORECASE=1;		#Set interpreter to be case-insensitive
	Val="val"; 		#Substitute the string "val" where Val used
	Left="left";  		#Substitute the string "left" where Left used
	Right="right";		#Substitute the string "right" where Right used
	Here="here";  		#Substitute the string "here" where Here used
	All="all"; 		#Substitute the string "all" where All used
	Min="min";  		#Substitute the string "min" where Min used
	Max="max"; 		#Substitute teh string "max" where Max used
	srand();  		#Randomize the seed used to generate random numbers
	BuildTree=0; 		#By default, don't add records to the trees 
                 		#0 - don't add; 1 - add.
	CurrentRecord=0; 	#Record currently being accessed
	Pass=0;		   	#Contains the current pass being made through data
	LastLine="";		#Contains last line seen from the file being read
	MaxSize=7;        	#The maximum size to allow the tree to grow to
	ReqRecs=35;     	#The minimum number of records that must be seen
        	                #between garbage collections
	Csize=0;   	        #Size fo the tree currently being manipulate
	Mode=0;
	RecordsIn=0;		#Number of records seen thus far
	IncSize=75;		#Number of records required before tree size grows
	GrowthMult=1;
#	Special			#If special equals 1, print Trees on discretization finish
	DynTree=0;		#DynTree = 1 allows dynamic tree size; else static
	SizeChoice=0;		#SizeChoice = 1 use size req of n/3; else sqrt(n)
}

#Skip blank and comment lines
           {sub(/\%.*/,"")}       #Substitute comments with blanks
           {gsub(/[\'\"\`]/,"",$0)} #Remove any single or double quotes from this line
	   {gsub("'", "", $0)}      #Remove any single quotes 
/^[ \t]*$/ {next}                 #When blank line observed, go to the next line


#PROCESS RELATION LINES
/@RELATION/{sub(/@RELATION/, "@relation")}
/@relation/{
   BuildTree=0; #Set flag for parsing lines to remember (add to tree) to false
   Attr=0;      #Set attribute counter to zero
   LastLine=""; #Set the LastLine seen to nothing
   RecordsIn=0;
   if(Pass==2){
     for (I in Trees)
       {
         if (CurrentSize[I] > MaxSize)
         {
           garbageCollect(CurrentRoot[I], Rbst, MaxSize)
         }
       }
     print $0
   }
   #COMMENTED CODE can be used to verify that the first pass created the BSTs for the numeric 
   #attributes and that it maintained class counts.  It prints the tree to the screen for review
   #if (Pass==2){    
   #    for (I in Numeric)
   #    {
   #       allvalues=""
   #       for (J in Classes){allvalues = allvalues " " Classes[J]}
   #       print "Classes are: [" allvalues "]"
   #       printtree(CurrentRoot[I], Rbst,"","", Classes)
   #       print ""
   #    }
   #}
}

#PROCESS ATTRIBUTE LINES
/@ATTRIBUTE/ {sub(/@ATTRIBUTE/, "@attribute")}
/@attribute/ {
  #On the first pass, determine which attributes should be put into trees
  #while preventing the class attribute from being put into the tree.
  if(Pass==1)
  {
    if (LastLine != "")
    {
      Attr++;    #For each new attribute, increase attribute counter

      # Obtain the name of each attribute and
      # store to the Names array
      gsub("_", "", LastLine)
      split(LastLine,a," ")
      Names[Attr]=a[2]

      #Create a list of the fields that should be put into trees
      Attrs[Attr]=1
      if ((match(LastLine, /numeric/) > 0) || (match(LastLine, /real/) > 0) || (match(LastLine,/integer/) > 0))
      {
        Trees[Attr]=1
      }
    }
    LastLine=$0  #Maintain a copy of the last attribute read, as if the @data
                 #symbol is seen then the LastLine attribute is the class 
                 #attribute
    next
  }

  #On the second pass, replace the original attribute values with an 
  #accurate list of the discretized classes
  if(Pass==2)
  {
    if(LastLine != "")
    {
      Attr++
      if (Attr in Trees)
      {
        outclasses = ""
	if (SizeChoice==1)
	{
	  outclasses = discClasses(Rbst, CurrentRoot[Attr], int((Rbst[CurrentRoot[Attr], All])/3), outclasses)
	}
	else
	{
	  outclasses = discClasses(Rbst, CurrentRoot[Attr], int(sqrt(Rbst[CurrentRoot[Attr], All])), outclasses)
	}
        sub(LastLine, "@attribute " Names[Attr] " {"outclasses"}", LastLine)
      }
      print LastLine
    }
    LastLine=$0
    next
  }
}


#PREPARE TO PROCESS DATA
/@DATA/{sub(/@DATA/, "@data")}
/@data/{
  BuildTree=1;      #The point has been reached where data should be put in tree
  CurrentRecord=1;  #The next nonblank line read will be the first record
  #On first pass, determine the the classes and set up the trees
  if (Pass==1)
  { 
    #Parse last line to obtain classess
    sub(/\}$/, "", LastLine)                      #Prune closing bracket from class
    sub(/@attribute /,"", LastLine)               #Prune @attribute sybmol from class
    sub(/^[[:alnum:][:blank:]]*\{/, "", LastLine) #Prune class name and opening bracket from classes
    gsub(/[ \t\n]*/, "", LastLine)                #Prune any extraneous spaces from classes
    split(LastLine, myClasses, ",")               #Split remaining information into different classes

#    COMMENTED CODE prints each class and creates a list of all the classes and prints it
#    for (J in myClasses){print myClasses[J]}
#    allvals = ""
#    for(J in myClasses){if(allvals==""){allvals=myClasses[J]}else{allvals = allvals " " myClasses[J]}}
#    print allvals

    #Initialize the roots of each tree to sybolize an empty tree, and new records for each tree to 0
    for(I in Trees){CurrentRoot[I] = "?"; CurrentSize[I]=0; NewRecords[I]=0}

  }

  #When outputting pruned dataset, ensure keeping the @data line
  if (Pass==2) {print LastLine; print $0}
  
  #Do not want to proceed to put the @data symbol in the tree, so go to the next line
  next
}   

#PROCESS DATA / BUILD TREES FROM DATA
#PASS ONE creates the binary search tree for each (numeric) attribute
Pass==1 && BuildTree && NF >1{
  for(I in Trees)             #For each non-class attribute
  { 
    NewRecords[I]++           #Add one to the number of records seen since last garbage collect
    field=$I;                 #field set to the value of the attribute in the row currently being processed
    CSize=CurrentSize[I]      #set CSize to the size of the current tree

    #If missing data, decrease new record count and go to next field
    if(field ~ /?/) {NewRecords[I]--; continue}  
    
    #Otherwise, data is valid, insert into the tree
    else returnvalues=rbstinsert(CurrentRoot[I], Rbst, field, Names[I], CurrentRecord,$NF, myClasses, CSize)
    
    #Split the returned value into the current root and size of the tree 
    #that was just processed
    split (returnvalues, vals, SUBSEP)
    CurrentRoot[I]=vals[1] SUBSEP vals[2]
    CurrentSize[I]=vals[3]

    #If the tree exceeds the Maximum size and the minumum numer of records
    #between garbage collections has been seen, Garbage Collect!
    if ((CurrentSize[I] > MaxSize)  && (NewRecords[I] >=ReqRecs))
    {
      garbageCollect(CurrentRoot[I], Rbst, MaxSize)
      NewRecords[I]=0
    }
    RecordsIn++;
    if(DynTree==1)
    {
      if(RecordsIn >= (IncSize * GrowthMult))
      {
        GrowthMult++;
        MaxSize= MaxSize + 2^(2+GrowthMulti) 
      }
    }
  }

  #Increase the counter of the number of records that have been processed
  CurrentRecord++;
}
  
#PASS TWO substitutes the discretized value from the search tree for each value/attribute
Pass==2 && BuildTree && NF >1{
  for(I in Trees)                #for each non-class attribute
  {

    field=$I                     #field set to the value of the attribute in the row currently being processed
    RootInstance=CurrentRoot[I]  #Set RootInstance to the root instance of the attribute currently being processed
    if(field ~ /?/) continue     #If missing data (symbolized by ?), go to the next field

    #Otherwise, data is valid, determine what discrete value to substitute with
    else
    {
      if(SizeChoice==1)
      {
    	$I=subwith(int((Rbst[RootInstance,All])/3),field, Rbst,CurrentRoot[I])
      }
      else
      {
	$I=subwith(int(sqrt(Rbst[RootInstance,All])),field, Rbst,CurrentRoot[I])
      }
    }
  }

  #Print the parsed lines
  print $0

}


END{
  if (Special==1) 
  {
    for (I in Trees)
    {
      print ""
      allvals = ""
      for(J in myClasses){if(allvals==""){allvals=myClasses[J]}else{allvals = allvals " " myClasses[J]}}
      print "Classes are: [" allvals "]"
      printtree(CurrentRoot[I], Rbst,"","", myClasses)
      print ""
    }
  }
}

#******************************************************************************
#bst.awk
#DJ's Binary Search Tree Functions

#Insert a a new node at the root of the tree
function insertR(RootInstance, Tree, Value, Attr, Rec, Class, Classes, CurrentSize)
{
  if (RootInstance == "?")
  {
    return newtree(Attr SUBSEP Rec, Tree, Value, Class, Classes, CurrentSize)
  }
  Tree[RootInstance,All]++
  Tree[RootInstance,"A" SUBSEP Class]++

  if (Value == Tree[RootInstance,Val])
  {
    Tree[RootInstance,Here]++
    Tree[RootInstance,"h" SUBSEP Class]++
    return RootInstance SUBSEP CurrentSize
  }

  if (Value < Tree[RootInstance,Val])
  {
    returnvals=insertR(Tree[RootInstance,Left], Tree, Value, Attr, Rec, Class, Classes, CurrentSize)
    split(returnvals, vals, SUBSEP)
    Tree[RootInstance,Left]=vals[1] SUBSEP vals[2]
    CurrentSize=vals[3]
    RootInstance = rotateR(RootInstance,Tree,Classes)
  }
  else
  {
    returnvals=insertR(Tree[RootInstance,Right], Tree, Value, Attr, Rec,Class, Classes, CurrentSize)
    split(returnvals,vals, SUBSEP)
    Tree[RootInstance,Right]=vals[1] SUBSEP vals[2]
    CurrentSize=vals[3]
    RootInstance = rotateL(RootInstance,Tree,Classes)
  }
  return RootInstance SUBSEP CurrentSize
}


#Rotate the current root's left subchild to right to make it become the current root
function rotateR(RootInstance, Tree, Classes,    temp)
{
  temp = Tree[RootInstance,Left]
  Tree[RootInstance,Left] = Tree[temp,Right]
  Tree[temp,Right] = RootInstance
  Tree[temp,All] = Tree[RootInstance,All]
  for(M in Classes)
  {
    Tree[temp, "A" SUBSEP Classes[M]] = Tree[RootInstance, "A" SUBSEP Classes[M]]
  }
  if((Tree[RootInstance,Left] != "?") && (Tree[RootInstance,Right] != "?"))
  {
    Tree[RootInstance,All]=\
    Tree[Tree[RootInstance,Left],All]+Tree[Tree[RootInstance,Right],All]+Tree[RootInstance,Here]
    for(M in Classes)
    {
      Tree[RootInstance,"A" SUBSEP Classes[M]]=\
		Tree[Tree[RootInstance,Left],"A" SUBSEP Classes[M]]+\
		Tree[Tree[RootInstance,Right],"A" SUBSEP Classes[M]]+\
		Tree[RootInstance,"h" SUBSEP Classes[M]]
    }
  }
  else
  {
    if((Tree[RootInstance,Left] != "?"))
    {
      Tree[RootInstance,All] = Tree[RootInstance,Here]+Tree[Tree[RootInstance,Left],All]
      for(M in Classes)
      {
        Tree[RootInstance, "A" SUBSEP Classes[M]] =\
		  Tree[RootInstance,"h" SUBSEP Classes[M]]+\
		  Tree[Tree[RootInstance,Left],"A" SUBSEP Classes[M]]
      }
    }
    else
    {
      if((Tree[RootInstance,Right] != "?"))
      {
         Tree[RootInstance,All] =\
			Tree[RootInstance,Here]+\
			Tree[Tree[RootInstance,Right],All]
        for(M in Classes)
			{
           Tree[RootInstance,"A" SUBSEP Classes[M]]=\
			  Tree[RootInstance,"h" SUBSEP Classes[M]]+\
			  Tree[Tree[RootInstance,Right],"A" SUBSEP Classes[M]]
			}
      }
      else
      {
	Tree[RootInstance,All] = Tree[RootInstance,Here]
        for(M in Classes)
	{
          Tree[RootInstance,"A" SUBSEP Classes[M]]=Tree[RootInstance,"h" SUBSEP Classes[M]]
	}
      }
    }
  }
  return temp
}



#Rotate the current roots right subchild up to the root (by rotating it to the left)
function rotateL(RootInstance, Tree, Classes,     temp)
{
  temp = Tree[RootInstance,Right]
  Tree[RootInstance,Right] = Tree[temp,Left]
  Tree[temp,Left] = RootInstance
  Tree[temp,All] = Tree[RootInstance,All]
  for(M in Classes)
  {
    Tree[temp,"A" SUBSEP Classes[M]]=Tree[RootInstance,"A" SUBSEP Classes[M]]
  }
  if((Tree[RootInstance,Left] != "?") && (Tree[RootInstance,Right] != "?"))
  {
    Tree[RootInstance,All]=\
    Tree[Tree[RootInstance,Left],All]+\
	 Tree[Tree[RootInstance,Right],All]+\
	 Tree[RootInstance,Here]
    for(M in Classes)
    {
      Tree[RootInstance,"A" SUBSEP Classes[M]] =\
		Tree[Tree[RootInstance,Left],"A" SUBSEP Classes[M]]+\
		Tree[Tree[RootInstance,Right],"A" SUBSEP Classes[M]]+\
		Tree[RootInstance,"h" SUBSEP Classes[M]]
    }
  }
  else
  {
    if((Tree[RootInstance,Left] != "?"))
    {
      Tree[RootInstance,All] = Tree[RootInstance,Here]+Tree[Tree[RootInstance,Left],All]
      for(M in Classes)
      {
         Tree[RootInstance,"A" SUBSEP Classes[M]]=\
			Tree[RootInstance, "h" SUBSEP Classes[M]] +\
			Tree[Tree[RootInstance,Left], "A" SUBSEP Classes[M]]
      }
    }
    else
    {
      if((Tree[RootInstance,Right] != "?"))
      {
        Tree[RootInstance,All] = Tree[RootInstance,Here]+Tree[Tree[RootInstance,Right],All]
        for(M in Classes)
		  {
          Tree[RootInstance,"A" SUBSEP Classes[M]]=\
			 Tree[RootInstance, "h" SUBSEP Classes[M]] +\
			 Tree[Tree[RootInstance,Right],"A" SUBSEP Classes[M]]
		  }
      }
      else
      {
	Tree[RootInstance,All] = Tree[RootInstance,Here]
        for(M in Classes)
			{
          Tree[RootInstance, "A" SUBSEP Classes[M]] = Tree[RootInstance, "h" SUBSEP Classes[M]]
			}
      }
    }
  }
  return temp
}


#Function to insert a new node into the tree
function newtree(RootInstance, Tree, Value, Class, Classes, CurrentSize)
{
  print RootInstance
  Tree[RootInstance,Left]="?"
  Tree[RootInstance,Right]="?"
  Tree[RootInstance,Val]=Value
  Tree[RootInstance,All]=1
  Tree[RootInstance,Here]=1

  #Initialize the here and All class counts for each class
  for(M in Classes) 
  {
    Tree[RootInstance,"h" SUBSEP Classes[M]]=0
    Tree[RootInstance,"A" SUBSEP Classes[M]]=0
  }

  Tree[RootInstance,"A" SUBSEP Class]=1
  Tree[RootInstance,"h" SUBSEP Class]=1
  CurrentSize++
  return RootInstance SUBSEP CurrentSize
}


#Function to print the current tree
function printtree(RootInstance, Tree, b4, indent, Classes,     allclasses, treeclasses)
{

  allclasses=""
  treeclasses=""
  for(M in Classes)
  { 
    if(allclasses==""){allclasses=Tree[RootInstance,"h" SUBSEP Classes[M]]}
    else allclasses=allclasses "  " Tree[RootInstance,"h" SUBSEP Classes[M]]
    if(treeclasses==""){treeclasses=Tree[RootInstance,"A" SUBSEP Classes[M]]}
    else treeclasses=treeclasses "  " Tree[RootInstance,"A" SUBSEP Classes[M]]
  }
  print indent b4 RootInstance " = " Tree[RootInstance,Val] " [here=" Tree[RootInstance,Here] "] [all=" Tree[RootInstance,All] "]" 
  print indent b4 RootInstance " Classes here are: [" allclasses "], all: [" treeclasses "]"
  if(Tree[RootInstance,Left] != "?")
    printtree(Tree[RootInstance,Left], Tree, "< ", indent "|    ",Classes,x)
  if(Tree[RootInstance,Right] != "?")
    printtree(Tree[RootInstance,Right], Tree, "> ", indent "|    ",Classes,x)
}


#Function for inserting a value into the tree. If the value is not at the root, 
#this function will, with a 1/n chance, insert the node at the current root.
function rbstinsert(RootInstance, Tree, Value, Attr, Rec, Class, Classes, CurrentSize)
{
  print RootInstance
  if (RootInstance == "?")
  {
    return newtree(Attr SUBSEP Rec, Tree, Value, Class, Classes, CurrentSize)
  }

  if (Value == Tree[RootInstance,Val])
  {
    Tree[RootInstance,Here]++
    Tree[RootInstance,All]++
    Tree[RootInstance, "A" SUBSEP Class]++
    Tree[RootInstance,"h" SUBSEP Class]++
    return RootInstance SUBSEP CurrentSize
  }
  
  else 
  {
    if ((rand()*Tree[RootInstance,All]) < 1.0)
    {
      return insertR(RootInstance,Tree,Value, Attr, Rec, Class, Classes, CurrentSize)
    }
    if (Value < Tree[RootInstance,Val])
    {
      returnvals=\
		rbstinsert(Tree[RootInstance,Left], Tree, Value, Attr, Rec, Class, Classes, CurrentSize)
      split(returnvals, vals, SUBSEP)
      Tree[RootInstance,Left]=vals[1] SUBSEP vals[2]
      CurrentSize=vals[3]
    }
    else
    {
      returnvals=\
		rbstinsert(Tree[RootInstance,Right], Tree, Value, Attr, Rec, Class, Classes, CurrentSize)
      split(returnvals, vals, SUBSEP)
      Tree[RootInstance,Right]=vals[1] SUBSEP vals[2]
      CurrentSize=vals[3]
    }
    Tree[RootInstance,All]++
    Tree[RootInstance,"A" SUBSEP Class]++
    return RootInstance SUBSEP CurrentSize
  }
}


#Function that determines which tree node to substitute for a given value
#For a tree node to be chosen as a viable substitute, it must have at least
#minSize nodes at in itself and/or in its children.
function subwith(minSize, Value, Tree, RootInstance)
{
#  print "This instance is: " Value " and it is being compared to: " Tree[RootInstance, Val]
  if(((Value == Tree[RootInstance,Val]) || (Value ~ Tree[RootInstance,Val]))\
	   && (Tree[RootInstance,All] >= minSize))
  {
#    print "They are equal"
    return RootInstance
  }
#  print "not equal"
  if(Value < Tree[RootInstance,Val])
  { 
    if (Tree[RootInstance,Left]!= "?")
    {
      if(Tree[Tree[RootInstance,Left],All] >= minSize)
      {
	return subwith(minSize,Value, Tree, Tree[RootInstance,Left])
      }
      else return RootInstance
    }
    else return RootInstance
  }
  if (Value > Tree[RootInstance,Val])
  { 
    if (Tree[RootInstance,Right] != "?")
    {
      if(Tree[Tree[RootInstance,Right],All] >= minSize)
      {
	return subwith(minSize, Value, Tree, Tree[RootInstance,Right])
      }
      else return RootInstance
    }
    else return RootInstance

  }
  else
  {
    if(int(2*rand()) > 1)
    {
      if (Tree[RootInstance,Right] != "?")
      {
        if(Tree[Tree[RootInstance,Right],All] >= minSize)
        {
  	  return subwith(minSize, Value, Tree, Tree[RootInstance,Right])
        }
        else return RootInstance
      }
      else return RootInstance
    }
    else
    {
      if (Tree[RootInstance,Left]!= "?")
      {
        if(Tree[Tree[RootInstance,Left],All] >= minSize)
        {
	  return subwith(minSize,Value, Tree, Tree[RootInstance,Left])
        }
        else return RootInstance
      }
       else return RootInstance
    }
  }
}

#Function to obtain the lists of discretized classes
#To be a part of this list, the given node must 
#be of at least minSize
function discClasses(Tree, RootInstance, minSize, myClasses)
{
  if(Tree[RootInstance,Left] != "?")
  {
    myClasses=discClasses(Tree, Tree[RootInstance,Left], minSize, myClasses)
  }
  if(Tree[RootInstance,All] >= minSize)
  {
    if(myClasses != "")
    {    
      myClasses = myClasses " , " RootInstance
    }
    else
    {
      myClasses = RootInstance
    }
  }
  if(Tree[RootInstance,Right] != "?")
  {
    myClasses=discClasses(Tree, Tree[RootInstance,Right], minSize, myClasses)
  }
  return myClasses
  
}

#Function to prune tree to an appropriate size/keep it from exceeding memory availability
function garbageCollect(RootInstance, Tree, MaxSize,        low, high, size)
{
  #Set up a set to track the tree nodes that have been "touched by the breadth first search
  size=0;
  high=1;
  low=0;
  NodeSet[0]=RootInstance
 
  #Function uses a breadth-first approach to span the tree, adding nodes to the set left to right
  #across each level and then moving down to the following one.  When the max size of the tree is 
  #reached,the nodes remaining in the set are removed, along with any of their children that were 
  #not discovered.
  while (high != low)
  {
    if (Tree[NodeSet[low],Left] != "?")
    {
      NodeSet[high]= Tree[NodeSet[low],Left];
      high++;
    }
    if (Tree[NodeSet[low],Right] != "?")
    {
      NodeSet[high]= Tree[NodeSet[low],Right];
      high++;
    }
    size++;
    low++;
    if (size == MaxSize)
    {
      high = high-1;
      while(low <= high)
      {
        removeVal(RootInstance, Tree, pruneNode(NodeSet[high], Tree))
        high = high - 1;
      }
    }
  }
}

#Function to remove a given node and any of its children from the tree
function pruneNode(RootInstance, Tree)
{
  #If this node has a left child, remove that left child before this node
  if(Tree[RootInstance,Left] != "?")
  {
    pruneNode(Tree[RootInstance,Left], Tree)
  }
  #If this node has a right child, remove that right child before this node
  if(Tree[RootInstance,Right] != "?")
  {
    pruneNode(Tree[RootInstance,Right],Tree)
  }

  #Set all relevant values of this tree to zero and disconnect the childrens nodes
  Tree[RootInstance,Left]="?"
  Tree[RootInstance,Right]="?"
  Tree[RootInstance,All]=0
  Tree[RootInstance,Here]=0
  return Tree[RootInstance,Val]
}

#Function to remove a node from the tree based on its value
#Starting root node will not be removed using this method
function removeVal(RootInstance, Tree, myVal)
{
  if(Tree[RootInstance,Val] > myVal)
  {
    if(Tree[Tree[RootInstance, Left], Val] == myVal)
    {
       Tree[RootInstance, Left]="?"
    }
    else
    {
      removeVal(Tree[RootInstance,Left], Tree, myVal)
    }
  }
  else
  {
    if(Tree[Tree[RootInstance, Right], Val] == myVal)
    {
       Tree[RootInstance, Right]="?"
    }
    else
    {
      removeVal(Tree[RootInstance, Right], Tree, myVal)
    }
  }
}
\end{lstlisting}
\end{center}

\ssp
