best_merit = ASEvaluator.evaluateSubset(best_group);
  public int[] search (int[] startSet, ASEvaluation ASEval, Instances data)    throws Exception  {    if (!(ASEval instanceof SubsetEvaluator)) {      throw  new Exception(ASEval.getClass().getName() 			   + " is not a " 			   + "Subset evaluator!");    }    if (startSet != null) {      m_starting = startSet;    }    if (ASEval instanceof UnsupervisedSubsetEvaluator) {      m_hasClass = false;    }    else {      m_hasClass = true;      m_classIndex = data.classIndex();    }    SubsetEvaluator ASEvaluator = (SubsetEvaluator)ASEval;    m_numAttribs = data.numAttributes();    int i, j;    int best_size = 0;    int size = 0;    int done;    int sd = m_searchDirection;    int evals = 0;    BitSet best_group, temp_group;    int stale;    double best_merit;    boolean ok = true;    double merit;    boolean z;    boolean added;    Link2 tl;    Hashtable lookup = new Hashtable((int)(200.0*m_numAttribs*1.5));    LinkedList2 bfList = new LinkedList2(m_maxStale);    best_merit = -Double.MAX_VALUE;    stale = 0;    best_group = new BitSet(m_numAttribs);    // If a starting subset has been supplied, then initialise the bitset    if (m_starting != null) {      for (i = 0; i < m_starting.length; i++) {	if ((m_starting[i]) != m_classIndex) {	  best_group.set(m_starting[i]);	}      }      // evaluate the initial set      best_merit = ASEvaluator.evaluateSubset(best_group);      best_size = m_starting.length;      m_totalEvals++;    }    else {if (m_searchDirection == -1) {      m_starting = new int[m_numAttribs];      // init initial subset to all attributes      for (i = 0, j = 0; i < m_numAttribs; i++) {	if (i != m_classIndex) {	  best_group.set(i);	  m_starting[j++] = i;	}      }      // evaluate the initial set      best_merit = ASEvaluator.evaluateSubset(best_group);      best_size = m_numAttribs - 1;      m_totalEvals++;    }    }    // add the initial group to the list and the hash table    bfList.addToList(best_group, best_merit);    BitSet tt = (BitSet)best_group.clone();    lookup.put(tt, "");    while (stale < m_maxStale) {      added = false;      if (m_searchDirection == 0) // bi-directional search	{	  done = 2;	  sd = 1;	}      else {	done = 1;      }      // finished search?      if (bfList.size() == 0) {	stale = m_maxStale;	break;      }      // copy the attribute set at the head of the list      tl = bfList.getLinkAt(0);      temp_group = (BitSet)(tl.getGroup().clone());      // remove the head of the list      bfList.removeLinkAt(0);      // count the number of bits set (attributes)      int kk;      for (kk = 0, size = 0; kk < m_numAttribs; kk++) {	if (temp_group.get(kk)) {	  size++;	}      }      do {	for (i = 0; i < m_numAttribs; i++) {	  if (sd == 1) {	    z = ((i != m_classIndex) && (!temp_group.get(i)));	  }	  else {	    z = ((i != m_classIndex) && (temp_group.get(i)));	  }	  if (z) {	    // set the bit (attribute to add/delete)	    if (sd == 1) {	      temp_group.set(i);	    }	    else {	      temp_group.clear(i);	    }	    /* if this subset has been seen before, then it is already 	       in the list (or has been fully expanded) */	    tt = (BitSet)temp_group.clone();	    if (lookup.containsKey(tt) == false) {	      merit = ASEvaluator.evaluateSubset(temp_group);	      m_totalEvals++;	      if (m_debug) {		System.out.print("Group: ");		printGroup(tt, m_numAttribs);		System.out.println("Merit: " + merit);	      }	      // is this better than the best?	      if (sd == 1) {		z = ((merit - best_merit) > 0.00001);	      }	      else {		z = ((merit >= best_merit) && ((size + sd) < best_size));	      }	      if (z) {		added = true;		stale = 0;		best_merit = merit;		best_size = (size + best_size);		best_group = (BitSet)(temp_group.clone());	      }	      // insert this one in the list and in the hash table	      bfList.addToList(tt, merit);	      lookup.put(tt, "");	    }	    // unset this addition(deletion)	    if (sd == 1) {	      temp_group.clear(i);	    }	    else {	      temp_group.set(i);	    }	  }	}	if (done == 2) {	  sd = -1;	}	done--;      } while (done > 0);      /* if we haven't added a new attribute subset then full expansion 	 of this node hasen't resulted in anything better */      if (!added) {	stale++;      }    }    m_bestMerit = best_merit;    return  attributeList(best_group);  }