gt:                             		meanTrue = 10*meanTrue + 1;
context: /**
   * Notes:
   * 
   *  isaStack: need to label ALL examples in each world
   *            maybe have 2-3 worlds, with 1-2 blocks in each
   *            and all (most?) data labeled
   *  
   *  me TODO
   *     make sure ok if NO negs can be created (eg, since all data is labelled)
   *      - hand edit file
   *      why isnt isablock(X) :- support(X, Y)?
   *      
   *      small penalty for 'unused' vars?
   *      
   *      clean up printing of rules?
   * 
   * 
   * 
   */
  package edu.wisc.cs.will.ILP;
  
  import java.io.File;  import edu.wisc.cs.will.Utils.condor.CondorFile;
  import edu.wisc.cs.will.Utils.condor.CondorFileInputStream;
  import java.io.FileNotFoundException;
  import edu.wisc.cs.will.Utils.condor.CondorFileOutputStream;
  import edu.wisc.cs.will.Utils.condor.CondorFileReader;
  import java.io.IOException;
  import java.io.ObjectOutputStream;
  import java.io.Reader;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Comparator;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  import java.util.SortedSet;
  import java.util.TreeSet;
  import java.util.zip.GZIPInputStream;
  import java.util.zip.GZIPOutputStream;
  
  import edu.wisc.cs.will.Boosting.RDN.RegressionRDNExample;
  import edu.wisc.cs.will.Boosting.RDN.RunBoostedRDN;
  import edu.wisc.cs.will.DataSetUtils.Example;
  import edu.wisc.cs.will.DataSetUtils.RegressionExample;
  import edu.wisc.cs.will.FOPC.Clause;
  import edu.wisc.cs.will.FOPC.HandleFOPCstrings;
  import edu.wisc.cs.will.FOPC.Literal;
  import edu.wisc.cs.will.FOPC.PredicateNameAndArity;
  import edu.wisc.cs.will.FOPC.Term;
  import edu.wisc.cs.will.FOPC.Theory;
  import edu.wisc.cs.will.FOPC.TreeStructuredTheory;
  import edu.wisc.cs.will.FOPC.TreeStructuredTheoryInteriorNode;
  import edu.wisc.cs.will.FOPC.TreeStructuredTheoryLeaf;
  import edu.wisc.cs.will.FOPC.TreeStructuredTheoryNode;
  import edu.wisc.cs.will.FOPC_MLN_ILP_Parser.FileParser;
  import edu.wisc.cs.will.ILP.Regression.BranchStats;
  import edu.wisc.cs.will.ResThmProver.DefaultHornClauseContext;
  import edu.wisc.cs.will.ResThmProver.HornClauseContext;
  import edu.wisc.cs.will.Utils.MessageType;
  import edu.wisc.cs.will.Utils.NamedReader;
  import edu.wisc.cs.will.Utils.Stopwatch;
  import edu.wisc.cs.will.Utils.Utils;
  import edu.wisc.cs.will.Utils.VectorStatistics;
  import edu.wisc.cs.will.stdAIsearch.BestFirstSearch;
  import edu.wisc.cs.will.stdAIsearch.SearchInterrupted;
  import edu.wisc.cs.will.stdAIsearch.SearchMonitor;
  import edu.wisc.cs.will.stdAIsearch.SearchResult;
  import edu.wisc.cs.will.stdAIsearch.SearchStrategy;
  import java.io.BufferedReader;
  import java.util.Arrays;
  
  /**
   * @author shavlik
   *
   * 
   *
   * VERSION 0.1 of the Wisconsin Inductive Logic Learner (WILL) system (name subject to change :-)
   *
   *	This class provides a mechanism for performing the traditional outer loop of ILP algorithms.
   *	The basic process is as follows:
   * 		1) do a standard ILP run
   * 		2) repeat using some new seed(s) examples until some stopping criteria met
   * 		3) return a set of clauses (a 'theory') as the final 'model'
   * 
   *  Some properties of this ILP algorithm:
   *      1) examples can be weighted
   *      2) both standard ILP heuristic search and a random-sampling method (RRR) can be performed
   *      3) the Gleaner idea (see Gleaner.java) is used to keep not only the best rule for iteration, but also the best rule per interval of recall (eg, the best rule whose recall is between 0.50 and 0,.55).
   *      4) on each cycle, multiple seed examples can be specified; a node (ie, a possible clause) needs to satisfy some fraction of the pos seeds and no more than some fraction of the negative seeds.  
   *      5) during successive iterations of the outer ILP loop, the pos examples can be down weighted and the covered neg examples can be up weighted (eg, a boosting-like mechanism, though the code currently does not compute the wgts defined by the boosting algo, but that would be a straightforward extension).
   *      6) implementation detail: in the inner loop, each node records the examples REJECTED at this node (this means that on any path to a node, examples are stored at no more than one node)
   *      7) hash tables (Java's hash maps, actually) are used for efficiency (eg, compared to linear lookups in lists)
   *      8) currently this code does NOT construct a "bottom clause" - instead if uses the multiple seed idea mentioned above to guide the search of candidate clauses
   *      9) no Prolog is needed; everything needed is provided in a set of Java projects (of course this means the code is slower than, say, Aleph+YAP Prolog, but computers get faster every year :-)
   *     10) as in Aleph and other ILP systems, arguments are typed (eg, 'human' and 'dog') to help control search; in this code the typing is hierarchical.
   *     11) as in Aleph, there is the ability for users to define prune(node), by which the user can cut off search trees.  Related to this is the built-in capability to process "intervals" such as isInThisInterval(1, value, 5).  If the previous literal is already in a clause, there is no need to add isInThisInterval(2, value, 4), since the latter will always be true given the former.  Similarly, there is no need to include isInThisInterval(7, value, 9) since it will always be false.  See PruneILPsearchTree.java for more detials. 
   * 
   * 	Note that with Gleaner as the searchMonitor, can create a theory from just one std ILP run.
   *	In this code, each std ILP run has its own Gleaner data structure, plus one Gleaner stores the best over ALL iterations.  
   *  Several different ways of returning a theory are provided (to do), as are various ways of stopping the outer loop.
   *  
   *  No support for this code is promised nor implied.  Suggestions and bug fixes may be sent to shavlik@cs.wisc.edu but please do not expect a reply.
   *  
   *  This code was heavily influenced by experience with Ashwin Srinivasan's Aleph (www.comlab.ox.ac.uk/oucl/research/areas/machlearn/Aleph/)
   *
   *  to do: count resolutions in theorem proving and throw if some max-value exceeded?
   *         add bottom clause code?
   */
  public class ILPouterLoop implements GleanerFileNameProvider {
  	public  static final String systemName = "WILL"; // See comment above for explanation.
     
  	public  LearnOneClause innerLoopTask;  // LearnOnClause performs the inner loop of ILP.
  
  	/** The state of the outer loop.
       *
       * Everything that should be checkpointed goes inside the ILPouterLoopState.  Variables
       * defined in ILPouterLoop will not be checkpointed.
       *
       */
  	private ILPouterLoopState outerLoopState;
  
  	private String workingDirectory;
     
      // The weight on covered example functionality was not compatible with the
      // new example weighting system.  If this functionality is needed we will have
      // to reimplement using a different approach than previously.  -Trevor
  	//public  double         weightOnCoveredPosExample     =   1.0;  // When an already-covered example is covered by a new clause, this is the weight on it (should be in [0-1] but need only be non-negative) during the next iteration of the ILP inner loop.
  	//public  double         weightOnCoveredNegExample     =   1.0;  // If this or the above is 0, then it means "do not count" this example in precision, etc.  Could also use, say, Boosting to weight the examples.
  
      public  int            maxNumberOfCycles             = 100;   // Call the inner loop at most this many times.
  	public  int            maxNumberOfClauses            = 100;   // Same as above EXCEPT only counts if a clause was learned.
  	public  double         minFractionOfPosCoveredToStop = 0.90;  // Stop when this fraction of the positive examples are covered by some acceptable clause.
  	public  int            max_total_nodesExpanded       = Integer.MAX_VALUE;
  	public  int            max_total_nodesCreated        = Integer.MAX_VALUE;
      public  int            numberPosSeedsToUse           = 1;
  	public  int            numberNegSeedsToUse           = 0;
  	
  	public  double         minimalAcceptablePrecision    = 0.0; // If these cannot be met, even if the next clause learned covers ALL uncovered positive and covers NO negatives, then abort.
  	public  double         minimalAcceptableRecall       = 0.0;
  	public  double         minimalAcceptableAccuracy     = 0.0;
  	public  double         minimalAcceptableF1           = 0.0; // TODO - handle F(beta).
  
      ///////////////////////////////////////////////////////////////////
  	// Parameters that are used when learning tree-structured theories.
      public  boolean        learningTreeStructuredTheory        = false;
      public  boolean        sortTreeStructedNodesByMeanScore    = true; // If false, then sort by TOTAL score of the examples reaching that node.
      private int            maxNumberOfLiteralsAtAnInteriorNode =  2; // In calls to LearnOneClause, this is how much we are allowed to extend the current clause.  Initially it is the max body length, but since recursive calls extend the clause at the parent, we need to add this much to the current number of literals on the path to the current node (not counting the length of the tests that evaluate to false, i.e., for the FALSE branches).
      private int            maxTreeDepthInLiterals              = 25; // This is the sum of literals at all the nodes in a path from root to leaf, not just the number of interior nodes.
      private int			   maxTreeDepthInInteriorNodes         =  5; // Maximum height/depth of the tree in terms of interior nodes in the tree. NOTE: One node in the tree may have multiple literals.
      private String         prefixForExtractedRules             = "";
      private String         postfixForExtractedRules            = "";
      private boolean		   learnMLNTheory					   = false;	
      private boolean        learnMultiValPredicates             = false;
      private boolean 	   learnOCCTree						   = false;
      ///////////////////////////////////////////////////////////////////
      
  	public  boolean        writeGleanerFilesToDisk       = false; // Write 'gleaner' files periodically.
  	private String         gleanerFileName               = null;  // Please don't use this directly.  Null indicates the use of a default value.
  	private String         gleanerFileNameFlipFlopped    = null;  // Put the gleaner results for the flip-flopped case here.
  	private Gleaner        gleaner                       = null;  // These two hold on to gleaners when we do flip-flops.  The gleaner is 'really' stored in LearnOneClause.
  	private Gleaner        gleanerFlipFlopped            = null;
      private String         annotationForRun              = null;
  
  	private boolean        checkpointEnabled             = false; // Write 'gleaner' files periodically.
  	private String         checkpointFileName            = null;  // Please don't use this directly.  Null indicates the use of a default value.
  	private String         checkpointFileNameFlipFlopped = null;
  
  	// All of the fields below are now in the ILPouterLoopState object.
  	// Any information needed to restart a run in the middle (from the chkpt)
      // must reside in the ILPouterLoopState object.
  	//
  	// There are accessors for all of these variable. For example, to get and set
  	// numberOfCycles, there is now a getNumberOfCycles() and
  	// setNumberOfCycles().
  	// However, if you still want to access this variable directly, you can use
  	// 'outerLoopState.numberOfCycles'.
  
  	// private Set<Example> negExamplesUsedAsSeeds;
  
  	// The following assist N-fold cross validation.
  	// private int numberOfFolds = 1;
  
  	// private Gleaner[] gleaners;
  	// private Theory[] theories;
      // private String currentGleanerFileName = null;
  	private List<Example> evalSetPosExamples = new ArrayList<Example>(1);
  	private List<Example> evalSetNegExamples = new ArrayList<Example>(1);
  //	private List<Example> allPosExamples;
  //	private List<Example> allNegExamples;
  
  	// These two allow one to randomly select a subset of the modes for each cycle.  (Added by JWS 6/24/10.)
  	private Set<PredicateNameAndArity> holdBodyModes;
  	public double randomlySelectWithoutReplacementThisManyModes = -1; // If in (0, 1), then is a FRACTION of the modes. If negative, then do not sample.	
  
      public boolean waitWhenBodyModesAreEmpty = true;
  
      private ActiveAdvice createdActiveAdvice = null;
  
  	public ILPouterLoop(String workingDir, String[] args, SearchStrategy strategy, ScoreSingleClause scorer) throws IOException {
  		this(workingDir, null, args, strategy, scorer, new Gleaner(), new DefaultHornClauseContext(new HandleFOPCstrings()), false, false);
  	}
      public ILPouterLoop(String workingDir, String[] args) throws IOException {
          this(workingDir, null, args, new Gleaner(), new HandleFOPCstrings());
      }
      public ILPouterLoop(String workingDir, String[] args, HandleFOPCstrings stringHandler) throws IOException {
          this(workingDir, null, args, new Gleaner(), stringHandler);
      }
  
      public ILPouterLoop(String workingDir, String[] args, HornClauseContext context) throws IOException {
          this(workingDir, null, args, new Gleaner(), context);
      }
  
      public ILPouterLoop(String workingDir, String prefix, String[] args, SearchMonitor monitor, HandleFOPCstrings stringHandler) throws IOException {
          this(workingDir, prefix, args, monitor, new DefaultHornClauseContext(stringHandler));
      }
  
      public ILPouterLoop(String workingDir, String prefix, String[] args, SearchMonitor monitor, HandleFOPCstrings stringHandler, boolean deferLoadingExamples) throws IOException {
          this(workingDir, prefix, args, monitor, new DefaultHornClauseContext(stringHandler), deferLoadingExamples);
      }
  
      public ILPouterLoop(String workingDir, String prefix, String[] args, 
      					SearchStrategy strategy, ScoreSingleClause scorer, SearchMonitor monitor, 
      	                HornClauseContext context, boolean useRRR, boolean deferLoadingExamples) throws IOException {
          this(workingDir, prefix, 
                  getBufferedReaderFromString(ILPouterLoop.getInputArgWithDefaultValue(args, 0, "pos.txt")),
                  getBufferedReaderFromString(ILPouterLoop.getInputArgWithDefaultValue(args, 1, "neg.txt")),
                  getBufferedReaderFromString(ILPouterLoop.getInputArgWithDefaultValue(args, 2, "bk.txt")),
                  getBufferedReaderFromString(ILPouterLoop.getInputArgWithDefaultValue(args, 3, "facts.txt")),
                  strategy, scorer, monitor, context, useRRR, deferLoadingExamples);
      }
  
      public ILPouterLoop(String workingDir, String prefix, String[] args, SearchMonitor monitor, HornClauseContext context) throws IOException {
          this(workingDir, prefix, args, monitor, context, false);
      }
  
      public ILPouterLoop(String workingDir, String prefix, String[] args, SearchMonitor monitor, HornClauseContext context, boolean deferLoadingExamples) throws IOException {
          this(workingDir, prefix,
                  getBufferedReaderFromString(ILPouterLoop.getInputArgWithDefaultValue(args, 0, "pos.txt")),
                  getBufferedReaderFromString(ILPouterLoop.getInputArgWithDefaultValue(args, 1, "neg.txt")),
                  getBufferedReaderFromString(ILPouterLoop.getInputArgWithDefaultValue(args, 2, "bk.txt")),
                  getBufferedReaderFromString(ILPouterLoop.getInputArgWithDefaultValue(args, 3, "facts.txt")),
                  monitor, context, deferLoadingExamples);
  
          if (args.length >= 5) {
              this.innerLoopTask.setRelevanceFile(args[4]);
          }
      }
  
      public ILPouterLoop(String workingDir, String prefix, Reader posExamplesReader, Reader negExamplesReader, Reader backgroundReader, Reader factsReader, SearchMonitor monitor, HandleFOPCstrings stringHandler) {
                          this(workingDir, prefix, posExamplesReader, negExamplesReader, backgroundReader, factsReader, monitor, new DefaultHornClauseContext(stringHandler, new FileParser(stringHandler, workingDir)), false);
      }
      public ILPouterLoop(String workingDir, String prefix, Reader posExamplesReader, Reader negExamplesReader, Reader backgroundReader, Reader factsReader,
  		    			SearchMonitor monitor, HornClauseContext context, boolean deferLoadingExamples) {
      	this(workingDir, prefix, posExamplesReader, negExamplesReader, backgroundReader, factsReader,
      		 new BestFirstSearch(), new ScoreSingleClauseByAccuracy(), monitor, context, false, false);
      }
  	public ILPouterLoop(String workingDir, String prefix, Reader posExamplesReader, Reader negExamplesReader, Reader backgroundReader, Reader factsReader,
  					    SearchStrategy strategy, ScoreSingleClause scorer, SearchMonitor monitor, HornClauseContext context, boolean useRRR, boolean deferLoadingExamples) {
  
         outerLoopState = new ILPouterLoopState();
         
         setWorkingDirectory(workingDir);
  
         if ( prefix == null ) {
              // Oops, prefix was null.  This just means we probably used the shorter constructors.
              // Just extract it from the working directory name.
              prefix = new CondorFile(workingDirectory).getName();
          }
  
          setPrefix(prefix);
  
  		if (LearnOneClause.debugLevel >= 0) { Utils.println("\n% Welcome to the " + systemName + " ILP/SRL systems.\n"); }
  
          // LearnOneClause performs the inner loop of ILP.
  		// TODO maybe we should send in the full outer loop instance (ie, this).
  
  		innerLoopTask = new LearnOneClause(workingDir, prefix, posExamplesReader, negExamplesReader, backgroundReader, factsReader, strategy, scorer, monitor, context, deferLoadingExamples);
  		
  		if (outerLoopState.isFlipFlopPosAndNegExamples()) {
  			Utils.writeMe("This needs to be debugged.");
  			innerLoopTask.flipFlopPosAndNegExamples();
  		}
  		
  //		if (numberOfFolds > 1) {
  //			gleaners = new Gleaner[numberOfFolds];
  //			theories = new Theory[ numberOfFolds];
  //		}
  	      
          if (monitor instanceof Gleaner) {
              setGleaner((Gleaner) monitor);
          }
          else if (monitor != null) {
              Utils.waitHere("The Search Monitor is not an instance of Gleaner: " + monitor);
          } 
  
          setRRR(useRRR);
  	}
  	
  	private String getFoldInfoString() {
  		if (getCurrentFold() == -1) { return ""; }
  		return "_fold" + getCurrentFold();
  	}
  
      public void setCurrentFold(int currentFold) {
          outerLoopState.setCurrentFold(currentFold);
      }
  
      public int getCurrentFold() {
          return outerLoopState.getCurrentFold();
      }
  	
  	private static BufferedReader getBufferedReaderFromString(String string) throws IOException {
  		if (string == null) { return null; }
  		return new NamedReader(new CondorFileReader(string), string);
  	}
  	
  	// Needed to get around need that "constructor call must be the first statement in a constructor."
  	private static String getInputArgWithDefaultValue(String[] args, int N, String defaultString) {
  		Utils.println("\n% getInputArgWithDefaultValue: args=" + Arrays.asList(args).toString() + "\n%  for N=" + N + ": args[N]=" + args[N]);
  		return (args.length < (N + 1) ? defaultString : args[N]);
  	}
  
  	// In standard ILP, there are two common ways of dealing with previously covered positive examples: count them 100% or not at all.
  	// This method allows those two settings to be chosen.  Notice that the default values above might differ from either of these two.
      //
  	public void countPreviouslyCoveredExamples(boolean countThem) {
          throw new UnsupportedOperationException("This functionality has been removed.  See comments in variable declaration section");
  //		if (countThem) {
  //			weightOnCoveredPosExample = 1.0;
  //			weightOnCoveredNegExample = 1.0;
  //		}
  //		else {
  //			weightOnCoveredPosExample = 0.0;
  //			weightOnCoveredNegExample = 1.0; // Still want to be penalized for covering them (though one might argue that if one rule covers it is is WORSE for another to do so, since a 'voting' method cannot overcome this false positive as easily).
  //		}
  	}
  
  	// Set the sequence of positive seeds to use. This only allows for ONE positive seed per inner-loop invocation.
  	// When the list is exhausted, seeds are randomly selected.
  	public void setSequenceOfSeedsToUse(int[] posSeedIndices, boolean ifSeedAlreadyCoveredSkipOverIt) {
  		  setPosSeedIndicesToUse(posSeedIndices);
  		  setIndexIntoPosSeedArray(0);
  		  setLengthPosSeedArray(getPosSeedIndicesToUse().length);
  	}
  
  	public void setMinFractionOfPosToCoverWithTheory(double fraction) {
  		if (fraction <= 0.0 || fraction > 1.0) {
  			Utils.error("Fraction (" + fraction + ") must be in (0,1].");
  		}
  		minFractionOfPosCoveredToStop = fraction;
  	}
  
  	public void setMaxNumberOfInnerLoopCycles(int count) {
  		if (count <= 0) {
  			Utils.error("Count (" + count + ") must be a positive integer.");
  		}
  		maxNumberOfCycles = count;
  	}
  
  	public void setMinPosCoverageOfAcceptableClause(int count) {
  		if (count < 0) {
  			Utils.error("Count (" + count + ") must be a non-negative integer.");
  		}
  		  innerLoopTask.setMinPosCoverage(count);
  	}
  
  	public void setMinPrecisionOfAcceptableClause(double precision) {
  		if (precision < 0.0 || precision > 1.0) {
  			Utils.error("Min precision (" + precision + ") must be in [0,1].");
  		}
  		innerLoopTask.setMinPrecision(precision);
  	}
  	public double getMinPrecisionOfAcceptableClause() {
  		return innerLoopTask.getMinPrecision();
  	}
  
  	public void setMaxRecallOfAcceptableClause(double recall) {
  		if (recall < 0.0 || recall > 1.0) {
  			Utils.error("Max recall (" + recall + ") must be in [0,1].");
  		}
  		innerLoopTask.setMaxRecall(recall);
  	}
  	public double getMaxRecallOfAcceptableClause() {
  		return innerLoopTask.getMaxRecall();
  	}
  
  	public void setMaxBodyLength(int maxBodyLength) {
  		if (maxBodyLength < 0) {
  			Utils.error("Length (" + maxBodyLength + ") must be a non-negative integer.");
  		}
  		// Recall that in tree-structured runs, the length of all parent nodes is also included here.
  		innerLoopTask.maxBodyLength = Math.min(maxBodyLength, learningTreeStructuredTheory ? maxTreeDepthInLiterals : Integer.MAX_VALUE);
  	 //	Utils.waitHere("setMaxBodyLength = " + innerLoopTask.maxBodyLength + "  maxBodyLength = " + maxBodyLength + "  learningTreeStructuredTheory = " + learningTreeStructuredTheory);
  	}
  
  	private boolean isaGoodPosSeed(int index) { // If good, then set it as the positive seed to use.
  		if (index < 0 || index >= getNumberOfPosExamples()) {
  			return false;
  		} // Simply skip over indices that are out of bounds.
  		Example chosenExample = innerLoopTask.getPosExamples().get(index);
  		if ((innerLoopTask.allowPosSeedsToBeReselected || getSeedPosExamplesUsed() == null || !getSeedPosExamplesUsed().contains(chosenExample)) && // Make sure that this wasn't previously a seed.
  			!getCoveredPosExamples().contains(chosenExample)) { // Make sure this is an uncovered seed.
  			int[] posSeeds = new int[1];
  			posSeeds[0] = index;
  			if (LearnOneClause.debugLevel > -10) { Utils.println(MessageType.ILP_OUTERLOOP, "% Have selected pos example #" + Utils.comma(index) + " as the next seed: " + chosenExample); }
  			innerLoopTask.selectTheseSeedsByIndex(posSeeds, null, getSeedPosExamplesUsed(), getSeedNegExamplesUsed()); // Use no negative seeds.
  			return true;
  		}
  		return false;
  	}
  		
  	private boolean collectMultipleSeeds() {
  		if (numberPosSeedsToUse < 1) { numberPosSeedsToUse = 1; }
  		if (numberNegSeedsToUse < 0) { numberNegSeedsToUse = 0; }
  		
  		// TODO - figure out how to report wrt to the flip-flop state.
  		if (numberPosSeedsToUse > getNumberOfPosExamples()) { Utils.warning("% Have only " + Utils.comma(getNumberOfPosExamples()) + " positive examples, so cannot choose " + Utils.comma(numberPosSeedsToUse) + " of them."); }
  		if (numberNegSeedsToUse > getNumberOfNegExamples()) { Utils.warning("% Have only " + Utils.comma(getNumberOfNegExamples()) + " negative examples, so cannot choose " + Utils.comma(numberNegSeedsToUse) + " of them."); }
  		
  		numberPosSeedsToUse = Math.min(numberPosSeedsToUse, getNumberOfPosExamples());
  		numberNegSeedsToUse = Math.min(numberNegSeedsToUse, getNumberOfNegExamples());
  		
  		int[] posSeeds = new int[numberPosSeedsToUse];
  		int[] negSeeds = new int[numberNegSeedsToUse];
  		
  		int posCounter = 0;
  		int negCounter = 0;
  		Set<Integer> posChosen = new HashSet<Integer>(4);
  		Set<Integer> negChosen = new HashSet<Integer>(4);
  		
  		// Could be really slow if selecting nearly all of the examples, but we're limiting this to 10X tries, so don't worry about it.
  		if (innerLoopTask.allowPosSeedsToBeReselected) {
  			int counter = 0; // Put a limit on the number of cycles here.
  			while (posCounter < numberPosSeedsToUse && counter++ < 10 * numberPosSeedsToUse) {
  				int index = Utils.random0toNminus1(getNumberOfPosExamples());
  				if (!posChosen.contains(index)) {
  					posChosen.add(index);
  					posSeeds[posCounter++] = index;
  				}
  			}			
  		}
  		// It still looking, grab in order.
  		if (posCounter < numberPosSeedsToUse) {
  			int i = 0;
  			// Use the 1.1 to handle the case of not getting enough due to sampling effects.  This fraction is ratio of SEEDS_NEEDED over SEEDS_TO_SELECT_FROM.
  			double fraction = 1.1 * (numberPosSeedsToUse - posCounter) / (double) (innerLoopTask.getPosExamples().size() - getCoveredPosExamples().size() - posCounter);
  			for (Example pos : innerLoopTask.getPosExamples()) { // Would be nice to more cleanly randomly walk through the examples, but after the first cycle, will at least skip those already covered.
  				if (Utils.random() < fraction && !getCoveredPosExamples().contains(pos) && !posChosen.contains(i)) {
  					posChosen.add(i);
  					posSeeds[posCounter++] = i;
  					if (posCounter >= numberPosSeedsToUse) { break; }
  				}
  				i++;
  			}
  		}
  		
  		if (innerLoopTask.allowNegSeedsToBeReselected) {
  			int counter = 0;
  			while (negCounter < numberNegSeedsToUse && counter++ < 10 * numberNegSeedsToUse) {
  				int index = Utils.random0toNminus1(getNumberOfNegExamples());
  				if (!negChosen.contains(index)) {
  					negChosen.add(index);
  					negSeeds[posCounter++] = index;
  				}
  			}			
  		}
  		if (negCounter < numberNegSeedsToUse) {
  			int i = 0; // See comment above.
  			double fraction = 1.1 * (numberNegSeedsToUse - negCounter) / (double) (innerLoopTask.getNegExamples().size() - outerLoopState.getNegExamplesUsedAsSeeds().size() - negCounter);
  			for (Example neg : innerLoopTask.getNegExamples()) {
  				if (Utils.random() < fraction && !getNegExamplesUsedAsSeeds().contains(neg) && !negChosen.contains(i)) {
  					negChosen.add(i);
  					negSeeds[negCounter++] = i;
  					getNegExamplesUsedAsSeeds().add(neg); // TODO - when done using all the negative seeds, should reset to use all again. 
  					if (negCounter >= numberNegSeedsToUse) { break; }
  				}
  				i++;
  			}
  		}
  		// If arrays still not full, shorten them.
  		if (posCounter < numberPosSeedsToUse) {
  			int[] posSeedsShorter = new int[Math.max(0, posCounter)];
              System.arraycopy(posSeeds, 0, posSeedsShorter, 0, posCounter);
  			posSeeds = posSeedsShorter;
  		}
  		if (negCounter < numberNegSeedsToUse) {
  			int[] negSeedsShorter = new int[Math.max(0, negCounter)];
              System.arraycopy(negSeeds, 0, negSeedsShorter, 0, negCounter);
  			negSeeds = negSeedsShorter;
  			   getNegExamplesUsedAsSeeds().clear(); // If ran short on negative seeds, allow old ones to be used again (BUG: should add more now, but live with one cycle with a shortage of negative seeds).
  		}
  		if (LearnOneClause.debugLevel > -1) {
  			int maxToPrint = 100;
  			if (posSeeds.length > 0) { Utils.print("\n% Have these " + Utils.comma(posSeeds.length) + " positive seeds:"); for (int i = 0; i < Math.min(maxToPrint, posSeeds.length); i++) Utils.print(" " + posSeeds[i]); if (posSeeds.length > maxToPrint) Utils.println(" ..."); else Utils.println(""); }
  			if (negSeeds.length > 0) { Utils.print("\n% Have these " + Utils.comma(negSeeds.length) + " negative seeds:"); for (int i = 0; i < Math.min(maxToPrint, negSeeds.length); i++) Utils.print(" " + negSeeds[i]); if (negSeeds.length > maxToPrint) Utils.println(" ..."); else Utils.println(""); }
  		}
  		innerLoopTask.selectTheseSeedsByIndex(posSeeds, negSeeds, false, getSeedPosExamplesUsed(), getSeedNegExamplesUsed()); // OK if reused (e.g., if not covered or if a negative seed).
  		return (posCounter > 0); // Need at least one positive seed.
  	}
  
      /** Executes the outer ILP loop.
       *
       * This will attempt to find one or more clauses that cover the positive and
       * negative examples.  executeOuterLoop will completely reset the search and
       * start from scratch.
       *
       * @return The learned theory.
       * @throws edu.wisc.cs.will.stdAIsearch.SearchInterrupted Thrown if the search is interrupted prior
       * to completion.
       */
  	public Theory executeOuterLoop() throws SearchInterrupted {
          resetAll();
          ILPSearchAction action = innerLoopTask.fireOuterLoopStarting(this);
  
          if (action == ILPSearchAction.PERFORM_LOOP) {
  
              // Try to resume the run in the middle if there is a checkpoint file...
              if (isCheckpointEnabled()) {
                  boolean checkpointSuccessful = readCheckpoint();
                  if ( checkpointSuccessful ) {
                      Utils.println("\nRestarting ILP Outer loop from state in checkpoint file " + getCheckpointFileName() + "...");
                  }
              }
  
              setupAdvice();
  
              // If no body modes, no need to run.
              if (Utils.getSizeSafely(innerLoopTask.bodyModes) < 1) {
                  if ( waitWhenBodyModesAreEmpty ) {
                      Utils.waitHere("Have no body modes.");
                  }
                  else {
                      Utils.warning("Have no body modes.");
                  }
                  return new Theory(innerLoopTask.getStringHandler());
              }
  
              boolean stoppedBecauseNoMoreSeedsToTry         = false;
              boolean stoppedBecauseTreeStructuredQueueEmpty = false;
  
              if (learningTreeStructuredTheory && outerLoopState.queueOfTreeStructuredLearningTasksIsEmpty()) {
                  stoppedBecauseTreeStructuredQueueEmpty = true;
                  Utils.waitHere("Have learningTreeStructuredTheory=true but stack of tasks is empty (or equal to null).");
              }
  
              if (LearnOneClause.debugLevel > 1) {
                  reportOuterLooperStatus("\n% STARTING executeOuterLoop()");
                  //Utils.waitHere();
              }
  
              SingleClauseNode savedBestNode = null; // When learning tree-structured models, we need to remember the node learned at the parent.
              long start = 0;
              boolean foundUncoveredPosSeed = false;
              while (!stoppedBecauseNoMoreSeedsToTry && !stoppedBecauseTreeStructuredQueueEmpty &&
                     getNumberOfLearnedClauses()  < maxNumberOfClauses &&
                     getNumberOfCycles()          < maxNumberOfCycles  &&
                     getFractionOfPosCovered()    < minFractionOfPosCoveredToStop &&
                     getTotal_nodesConsidered()   < max_total_nodesExpanded &&
                     getTotal_nodesCreated()      < max_total_nodesCreated  &&
                     getClockTimeUsedInMillisec() < getMaximumClockTimeInMillisec() &&
                     canStillMeetPrecisionRecallAccuracyF1specs(false)              &&
                     true
                     ) {
  
                  start = System.currentTimeMillis();
                  if (learningTreeStructuredTheory) {
                      //Utils.println("% innerLoopTask.resetAllForReal(before calling): " + Utils.reportSystemInfo());
                      innerLoopTask.resetAllForReal(); // Clear OPEN and CLOSED (and other things).
                      //Utils.println("% innerLoopTask.resetAllForReal(after calling):  " + Utils.reportSystemInfo());
                      outerLoopState.setCurrentTreeLearningTask(outerLoopState.popQueueOfTreeStructuredLearningTasks()); // Set in outerloopState class instead of here?
                      savedBestNode = outerLoopState.getCurrentTreeLearningTask().getCreatingNode();
                      clearSeedPosExamplesUsed();
                      clearSeedNegExamplesUsed();
                      innerLoopTask.setPosExamples(   outerLoopState.getCurrentTreeLearningTask().getPosExamples()); // Need to do these AFTER the setMinPosCoverage since that might be a fraction.
                      innerLoopTask.setNegExamples(   outerLoopState.getCurrentTreeLearningTask().getNegExamples());
                      innerLoopTask.setMinPosCoverage(outerLoopState.getOverallMinPosWeight()); // Even when we have fewer examples, we want the minPosWeight to be that from the first call.
                      
                      if (savedBestNode != null) { // Have to recompute this because the examples have changed.
                          Utils.println("\n% Working on expanding this node: '" + savedBestNode + "'");
                          ((LearnOneClause)savedBestNode.task).currentStartingNode = savedBestNode.getStartingNodeForReset(); // Only setting this while resetting the score for savedBestNode.
                          savedBestNode.resetAssumingAllExamplesCovered();
                          savedBestNode.setDontAddMeToOPEN(false);
                          innerLoopTask.scorer.scoreThisNode(savedBestNode);
                          setMaxBodyLength(Math.min(maxTreeDepthInLiterals, savedBestNode.bodyLength() + maxNumberOfLiteralsAtAnInteriorNode));
                      } else {
                          setMaxBodyLength(Math.min(maxTreeDepthInLiterals, maxNumberOfLiteralsAtAnInteriorNode));
                      }
                      if (savedBestNode != null) {
                     // 	Utils.waitHere("Setting starting node as:" + savedBestNode.getClause());
                      }
                      innerLoopTask.currentStartingNode = savedBestNode;
                  }
  
                  setNumberOfCycles( getNumberOfCycles() + 1 );
  
                  Stopwatch stopwatch = new Stopwatch();
  
                  if (LearnOneClause.debugLevel > 1) { Utils.println(MessageType.ILP_OUTERLOOP,"\n% ********************\n% Outer Loop Iteration #" + Utils.comma(getNumberOfCycles()) + " with strategy=" + (isRRR() ? "RRR" : "heuristic") + " (created a total of " + Utils.comma(getTotal_nodesCreated()) + " clauses so far and explored " + Utils.comma(getTotal_nodesConsidered()) + ")"); }
                  if (LearnOneClause.debugLevel > 1 && getMaximumClockTimeInMillisec() < Long.MAX_VALUE) {
                                                     // Use 'err' here so we see these.
                                                     Utils.printlnErr(MessageType.ILP_OUTERLOOP,"%  The outer looper (iteration #" + Utils.comma(getNumberOfCycles()) + ") has been running " + Utils.convertMillisecondsToTimeSpan(getClockTimeUsedInMillisec()) + " and has " + Utils.convertMillisecondsToTimeSpan(getMaximumClockTimeInMillisec() - getClockTimeUsedInMillisec()) + " remaining."); }
                  if (LearnOneClause.debugLevel > 1) { Utils.println(MessageType.ILP_OUTERLOOP,"%  [Min precision = " + Utils.truncate(innerLoopTask.minPrecision, 3) + ", (weighted) " + (innerLoopTask.regressionTask ? "minPosCov" : "minCover") + " = " + Utils.truncate(innerLoopTask.getMinPosCoverage(), 2) + (innerLoopTask.regressionTask ? "" : ", and (weighted) maxNegCov = " + Utils.truncate(innerLoopTask.getMaxNegCoverage(), 2)) + "]\n% ********************"); }
  
                  if (true) { // WE NEED TO PICK A NEW SET OF SEEDS EACH TIME SINCE SEEDS CHOSEN AT THE ROOT WILL FOLLOW VARIOUS PATHS THROUGH THE TREE.  if (!foundUncoveredPosSeed || !learningTreeStructuredTheory) { // If tree-structured task, we want to use the same seeds for the full tree (otherwise maybe no [new] seeds reach the current interior node).
                      // Specify the specific seeds to use if requested.
                      foundUncoveredPosSeed = false;
                      if (numberPosSeedsToUse > 1 || numberNegSeedsToUse > 0) { foundUncoveredPosSeed = collectMultipleSeeds(); }
  
                      while (!foundUncoveredPosSeed && getPosSeedIndicesToUse() != null && getIndexIntoPosSeedArray() < getLengthPosSeedArray()) {
                              int index = getPosSeedIndicesToUse()[getIndexIntoPosSeedArray()]; // Increment the counter so that walking down this array.
                              setIndexIntoPosSeedArray(getIndexIntoPosSeedArray()+1);
                              foundUncoveredPosSeed = isaGoodPosSeed(index);
                      }
  
                      // Otherwise randomly select one positive seed be used.
                      int tries = 0;
                      while (!foundUncoveredPosSeed && tries++ < 1000) { // 1000 might be low if a very large number of pos ex's, but if hard to find, grabbing seeds in numerical order (next step) should be fine.
                          int index = Utils.random0toNminus1(getNumberOfPosExamples());
                          foundUncoveredPosSeed = isaGoodPosSeed(index);
                      }
                      // If still no luck, walk through in order until an uncovered seed is found. Should always be able to find an uncovered seed because we know fraction covered less than 1.0 ...
                      if (!foundUncoveredPosSeed) {
                          int randomOffset = Utils.random0toNminus1(getNumberOfPosExamples());  // Start at a random location in the array, and do a "wrap around" walk through the values.
                          for (int index = 0; index < getNumberOfPosExamples(); index++) {
                              int indexToUse = (randomOffset + index)	% getNumberOfPosExamples();
                              foundUncoveredPosSeed = isaGoodPosSeed(indexToUse);
                              if (LearnOneClause.debugLevel > 2) { Utils.println("  considering pos seed #" + indexToUse + ": acceptability=" + foundUncoveredPosSeed); }
                              if (foundUncoveredPosSeed) { break; }
                          }
                      }
                  }
  
                  if (!foundUncoveredPosSeed) { // Since there is a minimum coverage on the best clause, some seeds won't be covered and we might run out of seeds before meeting some other termination criterion.
                      stoppedBecauseNoMoreSeedsToTry = true;
                  } else {
                      // Do the ILP inner loop.
                      //    First clear Gleaner and set up a new Gleaner set (i.e., will keep best rules PER CYCLE).
                      Gleaner theGleaner = (Gleaner) innerLoopTask.searchMonitor;
                      theGleaner.clearBestNode();
  
                      innerLoopTask.caller     = this;
                      innerLoopTask.callerName = "outerLoop #" + getNumberOfCycles() + getFoldInfoString();
                      theGleaner.setCurrentMarker(innerLoopTask.callerName);
                      if (!learningTreeStructuredTheory) { innerLoopTask.stringHandler.resetVarCounters(); } // When learning a tree-structured theory, we need a consistent set of variables.
                      innerLoopTask.checkIfAcceptableClausePossible();
  
                      if (getMaximumClockTimeInMillisec() < Long.MAX_VALUE) {
                          double denominator = 1.0; // If there is a time limit, leave some for later cycles, but allow at least 25% of the time for this one.
                          if (maxNumberOfCycles > 1 && getMaximumClockTimeInMillisec() != Long.MAX_VALUE) { denominator = Math.max(1.0, Math.min(4.0, maxNumberOfCycles / (1 + getNumberOfCycles()))); }
                          long innerLoopTimeLimit = (long) (getTimeAvailableInMillisec() / denominator);
                          if (LearnOneClause.debugLevel > 1) { Utils.printlnErr("%  The inner looper is being allocated " + Utils.convertMillisecondsToTimeSpan(innerLoopTimeLimit) + "."); }
                          innerLoopTask.setMaximumClockTimePerIterationInMillisec(innerLoopTimeLimit);
                      } else {
                          innerLoopTask.setMaximumClockTimePerIterationInMillisec(getMaximumClockTimeInMillisec());
                      }
  
                      ((ChildrenClausesGenerator) innerLoopTask.childrenGenerator).countOfPruningDueToVariantChildren = 0;
  
                      // If we are going to be sampling the modes, make a copy of the full set.  AND RESET WHEN DONE.
                      if (randomlySelectWithoutReplacementThisManyModes > 0 && holdBodyModes == null) {
                          holdBodyModes = innerLoopTask.getBodyModes();
  
                          if (randomlySelectWithoutReplacementThisManyModes < 1) { // Interpret as a FRACTION.  TODO - should we keep the fraction?  Seems not worth the trouble.
                              randomlySelectWithoutReplacementThisManyModes = Math.round(randomlySelectWithoutReplacementThisManyModes * Utils.getSizeSafely(holdBodyModes));
                          }
                      }
                      if (randomlySelectWithoutReplacementThisManyModes > 0 && randomlySelectWithoutReplacementThisManyModes  < Utils.getSizeSafely(holdBodyModes)) {
                          Set<PredicateNameAndArity> newSetOfBodyModes = new HashSet<PredicateNameAndArity>((int) randomlySelectWithoutReplacementThisManyModes);
                          int bodyModeSize = Utils.getSizeSafely(holdBodyModes);
                          // If we are getting almost all of the body nodes, we really should make a copy then DELETE entries until small enough.  TODO.
                          int counter = 0;
                          while (Utils.getSizeSafely(newSetOfBodyModes) < randomlySelectWithoutReplacementThisManyModes ) {
                              int                   index = Utils.random0toNminus1(bodyModeSize);
                              PredicateNameAndArity pName = Utils.getIthItemInCollectionUnsafe(holdBodyModes, index);
  
                              newSetOfBodyModes.add(pName); // If a duplicate, won't be added.
                              if (++counter > 10 * randomlySelectWithoutReplacementThisManyModes) { Utils.waitHere("Stuck in an infinite loop?  randomlySelectWithoutReplacementThisManyModes=" + Utils.comma(randomlySelectWithoutReplacementThisManyModes)); counter = 0; }
                          }
                          innerLoopTask.setBodyModes(newSetOfBodyModes);
                      }
  
                      // If we are learning a tree-structured theory, then we continue where we left off.
                      if (isRRR()) {
                          innerLoopTask.performRRRsearch(learningTreeStructuredTheory ? savedBestNode : null);
                      } else {
                          SearchResult sr = innerLoopTask.performSearch(learningTreeStructuredTheory ? savedBestNode : null);
                          innerLoopTask.needToCheckTheAdviceProcessor = false; // No need to do this at least until this "outer looper" is done.
                          if (false) { Utils.println("Search result: " + sr); }
                      }
  
                      // Utils.println(innerLoopTask.reportSearchStats());
                      // Want limits on (and statistics about) the full ILP search as well.
                      setTotal_nodesConsidered(getTotal_nodesConsidered() + innerLoopTask.getNodesConsidered());
                      setTotal_nodesCreated(   getTotal_nodesCreated()    + innerLoopTask.getNodesCreated());
                      setTotal_nodesNotAddedToOPENsinceMaxScoreTooLow(    getTotal_nodesNotAddedToOPENsinceMaxScoreTooLow()     + innerLoopTask.nodesNotAddedToOPENsinceMaxScoreTooLow);
                      setTotal_nodesRemovedFromOPENsinceMaxScoreNowTooLow(getTotal_nodesRemovedFromOPENsinceMaxScoreNowTooLow() + innerLoopTask.nodesRemovedFromOPENsinceMaxScoreNowTooLow);
                      setTotal_countOfPruningDueToVariantChildren(getTotal_countOfPruningDueToVariantChildren() + ((ChildrenClausesGenerator) innerLoopTask.childrenGenerator).countOfPruningDueToVariantChildren);
                      // Report what happened (TODO have an output 'results' file).
                      SingleClauseNode bestNode = theGleaner.bestNode;
                      
                      Utils.println("\n% The best node found: " + bestNode); // TEMP
                      
                      if (bestNode != null && bestNode != savedBestNode) { // Also need to check to make sure we didn't simply return the previous root when doing tree-structured learning.
                          Utils.println("\n% The best node found: " + bestNode);
                          List<Example> coveredPosExamplesThisCycle = innerLoopTask.collectPosExamplesCovered(bestNode);
                          List<Example> coveredNegExamplesThisCycle = innerLoopTask.collectNegExamplesCovered(bestNode);
                          int newlyCoveredPosExamples = 0;
                          int newlyCoveredNegExamples = 0;
                          int coveredPosExamplesCount = Utils.getSizeSafely(coveredPosExamplesThisCycle);
                          int coveredNegExamplesCount = Utils.getSizeSafely(coveredNegExamplesThisCycle);
  
                          if (coveredPosExamplesCount > 0) {
                              for (Example ex : coveredPosExamplesThisCycle) {
                                  // Utils.println("   covered pos: " + ex);
                                  if (!getCoveredPosExamples().contains(ex)) {
                                      if (!learningTreeStructuredTheory) { // When learning trees we don't need to count coverings by possibly overlapping rules.
                                          getCoveredPosExamples().add(ex);
                                          setNumberOfPosExamplesCovered(getNumberOfPosExamplesCovered() + 1); // An awkward way to increment ...
                                      }
                                      newlyCoveredPosExamples++;
                                  }
                              }
                          }
                          if (coveredNegExamplesCount > 0) {
                              for (Example ex : coveredNegExamplesThisCycle) {
                                  // Utils.println(" covered neg: " + ex);
                                  if (!getCoveredNegExamples().contains(ex)) {
                                      if (!learningTreeStructuredTheory) {  // See comment above.
                                          getCoveredNegExamples().add(ex);
                                          setNumberOfNegExamplesCovered(getNumberOfNegExamplesCovered() + 1); // An awkward way to increment ...
                                      }
                                      newlyCoveredNegExamples++;
                                  }
                              }
                          }
  
                          // The following line of code allowed covered examples to be reweighted on the fly during theory search.
                          //
                          // While an interesting idea, there are all sorts of problems the way you implemented it
                          // (even in the old map-based weight implementation), the biggest being that you never retain
                          // their original weights, so post-learning scoring would have been wacky.  Covered examples
                          // would have been down weighted in the final coverage scores...this could happen on both
                          // negative and positive examples, so it would be hard to say exactly what the effect would
                          // be, but it wouldn't have been the expected final score.
                          //
                          // I am commenting it out for now, but we can re-implement later if desired.
                          // -Trevor
  
                          //innerLoopTask.setExampleWeights((Utils.diffDoubles(weightOnCoveredPosExample, 1.0) ? getCoveredPosExamples() : null),
                          //					              (Utils.diffDoubles(weightOnCoveredNegExample, 1.0) ? getCoveredNegExamples() : null));
  
                          if (coveredPosExamplesCount < 1) {
                              Utils.warning("Have a bestNode that covers no positive examples.  That shouldn't happen.  Best node = " + bestNode);
                          }
                          setNumberOfLearnedClauses(getNumberOfLearnedClauses() + 1);
                          Clause newClause = new LearnedClause(innerLoopTask, bestNode, getNumberOfCycles(),
                                                               getNumberOfPosExamplesCovered(), coveredPosExamplesCount, newlyCoveredPosExamples,
                                                               getNumberOfPosExamples(), getNumberOfNegExamplesCovered(),  coveredNegExamplesCount,
                                                               newlyCoveredNegExamples, getNumberOfNegExamples());
  
                          if (learningTreeStructuredTheory) {
                              if (!innerLoopTask.constantsAtLeaves) { Utils.error("Have not yet implemented constantsAtLeaves = false."); }
  
                              if (LearnOneClause.debugLevel > 1) {
                                  Utils.println("\n% New full clause: "  + newClause);
                                  Utils.println("\n% New LOCAL clause: " + bestNode.getLocallyAddedClause() + "\n");
                              }
  
                              TreeStructuredLearningTask       currentTask  = outerLoopState.getCurrentTreeLearningTask();
                              TreeStructuredTheoryInteriorNode interiorNode = currentTask.getNode();
                              interiorNode.setSearchNodeThatLearnedTheClause(bestNode); // Be sure to set this before the next call.
                              interiorNode.setNodeTestFromFullNodeTest(newClause);
                              // Set the task used to learn this node.
                              bestNode.setStartingNodeForReset(innerLoopTask.currentStartingNode);
                              if (LearnOneClause.debugLevel > -1) {
                                  Utils.println("\n% Expanding node at Level " + interiorNode.getLevel() + " with score = " + Utils.truncate(currentTask.getScore(), 3) + ".\n% Will extend: " + interiorNode.getSearchNodeThatLearnedTheClause());
                              }
  
                              boolean atMaxDepth = (interiorNode.getLevel() >= maxTreeDepthInInteriorNodes);
                              
                              TreeStructuredTheoryNode trueBranch;
                              TreeStructuredTheoryNode falseBranch;
                              boolean goodEnoughFitTrueBranch  = atMaxDepth || bestNode.acceptableScoreTrueBranch( outerLoopState.maxAcceptableNodeScoreToStop);
                              boolean goodEnoughFitFalseBranch = atMaxDepth || bestNode.acceptableScoreFalseBranch(outerLoopState.maxAcceptableNodeScoreToStop);
  
                              List<Example> trueBranchPosExamples  = null;
                              List<Example> falseBranchPosExamples = null;
                              List<Example> trueBranchNegExamples  = null;
                              List<Example> falseBranchNegExamples = null;
  
                              double wgtedCountTrueBranchPos  = 0.0;
                              double wgtedCountTrueBranchNeg  = 0.0;
                              double wgtedCountFalseBranchPos = 0.0;
                              double wgtedCountFalseBranchNeg = 0.0;
  
                              List<Example> posEx = currentTask.getPosExamples();
                              List<Example> negEx = currentTask.getNegExamples();
  
                              // Since we are collecting 'extra labels' for leaf nodes, we need always to collect examples.
                              if (posEx != null) {
                                  trueBranchPosExamples  = (false && goodEnoughFitTrueBranch  ? null : new ArrayList<Example>(8));
                                  falseBranchPosExamples = (false && goodEnoughFitFalseBranch ? null : new ArrayList<Example>(8));
                                  for (Example ex : posEx) {
                                      if (bestNode.matchesThisExample(ex, true)) {
                                          if (true || !goodEnoughFitTrueBranch)  { trueBranchPosExamples.add(ex);  }
                                          wgtedCountTrueBranchPos += ex.getWeightOnExample();
                                      } else {
                                          if (true || !goodEnoughFitFalseBranch) { falseBranchPosExamples.add(ex); }
                                          wgtedCountFalseBranchPos += ex.getWeightOnExample();
                                      }
                                  }
                              }
                              // Since we are collecting 'extra labels' for leaf nodes, we need always to collect examples.
                              if (negEx != null) {
                                  trueBranchNegExamples  = (false && goodEnoughFitTrueBranch  ? null : new ArrayList<Example>(8));
                                  falseBranchNegExamples = (false && goodEnoughFitFalseBranch ? null : new ArrayList<Example>(8));
                                  for (Example ex : negEx) {
                                      if (bestNode.matchesThisExample(ex, false)) {
                                          if (true || !goodEnoughFitTrueBranch)  { trueBranchNegExamples.add(ex);  }
                                          wgtedCountTrueBranchNeg += ex.getWeightOnExample();
                                      } else {
                                          if (true || !goodEnoughFitFalseBranch) { falseBranchNegExamples.add(ex); }
                                          wgtedCountFalseBranchNeg += ex.getWeightOnExample();
                                      }
                                  }
                              }
  
                              if (false && !atMaxDepth) {
                                  Utils.println("getOverallMinPosWeight() = " + outerLoopState.getOverallMinPosWeight());
                                  Utils.println("goodEnoughFitTrueBranch  = " + goodEnoughFitTrueBranch);
                                  Utils.println("goodEnoughFitFalseBranch = " + goodEnoughFitFalseBranch);
                                  Utils.println("wgtedCountTrueBranchPos  = " + wgtedCountTrueBranchPos);
                                  Utils.println("wgtedCountTrueBranchNeg  = " + wgtedCountTrueBranchNeg);
                                  Utils.println("wgtedCountFalseBranchPos = " + wgtedCountFalseBranchPos);
                                  Utils.println("wgtedCountFalseBranchNeg = " + wgtedCountFalseBranchNeg);
                              }
                              /*
              				for (Example eg : bestNode.posExamplesThatFailedHere) {
              					Utils.println("Negs: " + ((RegressionRDNExample)eg).toPrettyString());
              				}
              				for (Example eg : ((LearnOneClause)bestNode.task).getPosExamples()) {
              					Utils.println("Pos: " + ((RegressionRDNExample)eg).toPrettyString());
              				}*/
                              double meanTrue = 0;
                              double[] meanVecTrue = null;
                              
                              if (learnMLNTheory) {
                              	meanTrue = bestNode.mlnRegressionForTrue();
                              } else {
                              	if (!learnOCCTree) {
                              		meanTrue = bestNode.meanIfTrue();
                              	} else {
                              		meanTrue = 1;
                              		for (Boolean b : interiorNode.returnBoolPath()) {
  										meanTrue = 10*meanTrue + (b?1:0);
  									}
  