/**
  * Constructs this comparator.
  *
  * @param what what to compare
  * @param with with what
  * @param deep whether to compare not only the structure of graphs but also graph attributes
  */
 public Test_LearnerComparator(Learner what, Learner with, boolean deep) {
   super(what);
   whatToCompareWith = with;
   compareInDepth = deep;
   what.setTopLevelListener(this);
   with.setTopLevelListener(this);
 }
  @Override
  public boolean AddConstraints(
      LearnerGraph graph, LearnerGraph outcome, StringBuffer counterExampleHolder) {
    LearnerGraph copyOfOutcome = null;
    boolean result = false;

    // First, we call the expected method
    if (Thread.currentThread() == secondThread) {
      result = whatToCompareWith.AddConstraints(graph, outcome, counterExampleHolder);
      copyOfOutcome = new LearnerGraph(outcome, outcome.config);
      cGraph = copyOfOutcome;
      cResult = result;
    } else {
      result = decoratedLearner.AddConstraints(graph, outcome, counterExampleHolder);
      copyOfOutcome = new LearnerGraph(outcome, outcome.config);
    }
    syncOnCallOf(KIND_OF_METHOD.M_ADDCONSTRAINTS);

    if (Thread.currentThread() != secondThread) { // second thread, checking.

      if (!cResult.equals(new Boolean(result)))
        failureCode = new IllegalArgumentException("different success value of AddConstraints");
      else {
        if (result) checkGraphEquality(cGraph, copyOfOutcome);
      }
      cGraph = null;
      cResult = null; // reset stored data
    }

    syncOnCallOf(KIND_OF_METHOD.M_METHODEXIT); // aims to stop one of the threads running fast
    // from the first checkCall and overwriting the stored value before the other
    // thread had a chance to use it in a comparison.

    return result;
  }
  /**
   * Both arguments and the return value are stored by the simulator.
   *
   * @param plus value loaded from XML
   * @param minus value loaded from XML
   */
  @Override
  public synchronized LearnerGraph init(
      Collection<List<Label>> plus, Collection<List<Label>> minus) {
    LearnerGraph result = null, copyOfResult = null;
    // First, we call the expected method
    if (Thread.currentThread() == secondThread) {
      result = whatToCompareWith.init(plus, minus);
      copyOfResult = new LearnerGraph(result, result.config);
      iGraph = copyOfResult;
    } else {
      result = decoratedLearner.init(plus, minus);
      copyOfResult = new LearnerGraph(result, result.config);
    }
    syncOnCallOf(KIND_OF_METHOD.M_INIT);

    if (Thread.currentThread() != secondThread) { // second thread, checking.
      checkGraphEquality(iGraph, copyOfResult);

      iGraph = null; // reset stored data
    }

    syncOnCallOf(KIND_OF_METHOD.M_METHODEXIT); // aims to stop one of the threads running fast
    // from the first checkCall and overwriting the stored value before the other
    // thread had a chance to use it in a comparison.

    return result;
  }
  /**
   * Called by the simulator.
   *
   * @param pair loaded from XML.
   * @param original estimated value.
   * @param temp estimated value.
   * @return loaded from XML.
   */
  @Override
  public synchronized List<List<Label>> RecomputeQuestions(
      PairScore pair, LearnerGraph original, LearnerGraph temp) {
    List<List<Label>> result = null;
    // First, we call the expected method
    if (Thread.currentThread() == secondThread) {
      result = whatToCompareWith.ComputeQuestions(pair, original, temp);
      MqPair = pair;
      Mquestions = result;
    } else result = decoratedLearner.ComputeQuestions(pair, original, temp);

    syncOnCallOf(KIND_OF_METHOD.M_RECOMPUTEQUESTIONS);

    if (Thread.currentThread()
        != secondThread) { // checking, ignoring scores and accept-conditions.
      if (!MqPair.getQ().equals(pair.getQ()) || !MqPair.getR().equals(pair.getR()))
        failureCode =
            new IllegalArgumentException(
                "different RecomputeQuestions pair " + MqPair + " v.s. " + pair);
      if (!Mquestions.equals(result))
        failureCode =
            new IllegalArgumentException(
                "different RecomputeQuestions questions: \n" + Mquestions + "\n v.s.\n" + result);
      MqPair = null;
      Mquestions = null; // reset stored data
    }

    syncOnCallOf(KIND_OF_METHOD.M_METHODEXIT); // aims to stop one of the threads running fast
    // from the first checkCall and overwriting the stored value before the other
    // thread had a chance to use it in a comparison.

    return result;
  }
  /**
   * Does nothing in the simulator.
   *
   * @param pta is always null in the simulator.
   * @param ptaKind loaded from XML.
   * @param sequence loaded from XML.
   * @param accepted loaded from XML.
   * @param newColour loaded from XML.
   */
  @Override
  public synchronized void AugmentPTA(
      LearnerGraph pta,
      RestartLearningEnum ptaKind,
      List<Label> sequence,
      boolean accepted,
      JUConstants newColour) {

    AugmentPTAData data = new AugmentPTAData(ptaKind, sequence, accepted, newColour);

    // now call the expected method
    if (Thread.currentThread() == secondThread) {
      whatToCompareWith.AugmentPTA(pta, ptaKind, sequence, accepted, newColour);
      augmentData = data;
    } else decoratedLearner.AugmentPTA(pta, ptaKind, sequence, accepted, newColour);

    syncOnCallOf(KIND_OF_METHOD.M_AUGMENT);

    if (Thread.currentThread() != secondThread) {
      if (!data.equals(augmentData)) // second thread, checking.
      failureCode = new IllegalArgumentException("different augment PTA values");
      augmentData = null; // reset stored data
    }

    syncOnCallOf(KIND_OF_METHOD.M_METHODEXIT); // aims to stop one of the threads running fast
    // from the first checkCall and overwriting the stored value before the other
    // thread had a chance to use it in a comparison.

  }
  /**
   * Called by the simulator.
   *
   * @param graph estimated graph
   * @return loaded values from XML.
   */
  @Override
  public synchronized Stack<PairScore> ChooseStatePairs(LearnerGraph graph) {
    Stack<PairScore> pairsAndScores = new Stack<PairScore>();
    Stack<PairScore> result = null;
    if (Thread.currentThread() == secondThread) {
      result = whatToCompareWith.ChooseStatePairs(graph);
      pairsAndScores.addAll(result); // make sure that we do not depend on
      // subsequent modification of the stack of pairs (one thread may
      // outrun another one and make changes to it before another one
      // had a chance to compare the two stacks).
      pairs = pairsAndScores;
    } else { // first graph
      result = decoratedLearner.ChooseStatePairs(graph);
      pairsAndScores.addAll(result);
    }

    syncOnCallOf(KIND_OF_METHOD.M_CHOOSEPAIRS);

    if (Thread.currentThread()
        != secondThread) { // checking. pairsAndScores as well as graph is from the first graph.
      // pairs is from the second one.

      // Since accept/reject labelling is not stored in the XML file, we have to compare pairs
      // discounting accept/reject
      if (pairs.size() != pairsAndScores.size())
        throw new IllegalArgumentException(
            "different sizes of ChooseStatePairs collections of pairs, \n"
                + pairs
                + " v.s. \n"
                + pairsAndScores);

      Iterator<PairScore> ps1 = pairsAndScores.iterator(), ps2 = pairs.iterator();
      while (ps1.hasNext()) {
        PairScore p1 = ps1.next(), p2 = ps2.next();

        if (!p1.getQ().equals(p2.getQ())
            || !p1.getR().equals(p2.getR())
            || p1.getScore() != p2.getScore()
            || p1.getAnotherScore() != p2.getAnotherScore())
          throw new IllegalArgumentException(
              "different ChooseStatePairs pairs, " + p1 + " v.s. " + p2);
      }

      pairs = null; // reset stored data
    }

    syncOnCallOf(KIND_OF_METHOD.M_METHODEXIT); // aims to stop one of the threads running fast
    // from the first checkCall and overwriting the stored value before the other
    // thread had a chance to use it in a comparison.

    return result;
  }
  /**
   * Simulated check.
   *
   * @param g estimated graph, not loaded from XML.
   * @param question question loaded from XML or computed by a learner.
   * @param responseForNoRestart ignored.
   * @param lengthInHardFacts ignored.
   * @param options set to null by the simulator.
   * @return value loaded from XML or computed by the learner.
   */
  @Override
  public synchronized Pair<Integer, String> CheckWithEndUser(
      LearnerGraph graph,
      List<Label> argQuestion,
      int responseForNoRestart,
      List<Boolean> acceptedElements,
      PairScore pairBeingMerged,
      Object[] options) {
    Pair<Integer, String> result = null;
    // First, we call the expected method
    if (Thread.currentThread() == secondThread) {
      result =
          whatToCompareWith.CheckWithEndUser(
              graph, argQuestion, responseForNoRestart, acceptedElements, pairBeingMerged, options);
      question = argQuestion;
      cPair = result;
    } else
      result =
          decoratedLearner.CheckWithEndUser(
              graph, argQuestion, responseForNoRestart, acceptedElements, pairBeingMerged, options);

    syncOnCallOf(KIND_OF_METHOD.M_CHECKWITHUSER);

    if (Thread.currentThread() != secondThread) { // checking.
      if (!question.equals(argQuestion))
        failureCode = new IllegalArgumentException("different CheckWithEndUser questions");
      if (!cPair.equals(result))
        failureCode =
            new IllegalArgumentException(
                "different CheckWithEndUser results "
                    + cPair.firstElem
                    + " v.s. "
                    + result.firstElem
                    + " and "
                    + cPair.secondElem
                    + " v.s. "
                    + result.secondElem);
      cPair = null;
      question = null; // reset stored data
    }

    syncOnCallOf(KIND_OF_METHOD.M_METHODEXIT); // aims to stop one of the threads running fast
    // from the first checkCall and overwriting the stored value before the other
    // thread had a chance to use it in a comparison.

    return result;
  }
  /**
   * Does nothing in the simulator.
   *
   * @param mode value loaded from XML.
   */
  @Override
  public synchronized void Restart(RestartLearningEnum mode) {
    // First, we call the expected method
    if (Thread.currentThread() == secondThread) {
      whatToCompareWith.Restart(mode);
      rMode = mode;
    } else decoratedLearner.Restart(mode);

    syncOnCallOf(KIND_OF_METHOD.M_RESTART);

    if (Thread.currentThread() != secondThread) { // checking.
      if (!rMode.equals(mode)) failureCode = new IllegalArgumentException("different Restart mode");
      rMode = null; // reset stored data
    }

    syncOnCallOf(KIND_OF_METHOD.M_METHODEXIT); // aims to stop one of the threads running fast
    // from the first checkCall and overwriting the stored value before the other
    // thread had a chance to use it in a comparison.
  }
  /**
   * Returns the graph stored in XML.
   *
   * @param original graph to be processed, the simulator attempts to supply a relevant value,
   *     however it is not certain to be correct.
   * @param pair the pair to be merged. Loaded from XML file (without scores).
   * @return graph loaded from XML file.
   */
  @Override
  public synchronized LearnerGraph MergeAndDeterminize(LearnerGraph original, StatePair pair) {
    LearnerGraph result = null, copyOfResult = null;
    // First, we call the expected method
    if (Thread.currentThread() == secondThread) {
      result = whatToCompareWith.MergeAndDeterminize(original, pair);
      copyOfResult =
          new LearnerGraph(
              result,
              result
                  .config); // since a tread which produced result may exit and modify the graph, we
      // have to take a copy of it.
      mPair = pair;
      mGraph = copyOfResult;
    } else {
      result = decoratedLearner.MergeAndDeterminize(original, pair);
      copyOfResult = new LearnerGraph(result, result.config);
    }
    syncOnCallOf(KIND_OF_METHOD.M_MERGEANDDETERMINIZE);

    if (Thread.currentThread()
        != secondThread) { // checking, considering that acceptance conditions are not stored in
      // XML.
      if (!mPair.getQ().equals(pair.getQ()) || !mPair.getR().equals(pair.getR()))
        failureCode =
            new IllegalArgumentException(
                "different MergeAndDeterminize pair " + mPair + " v.s. " + pair);
      checkGraphEquality(mGraph, copyOfResult);

      mPair = null;
      mGraph = null; // reset stored data
    }

    syncOnCallOf(KIND_OF_METHOD.M_METHODEXIT); // aims to stop one of the threads running fast
    // from the first checkCall and overwriting the stored value before the other
    // thread had a chance to use it in a comparison.

    return result;
  }