@Override
  public void run() {
    List<String> events = new ArrayList<String>();
    events.addAll(inputs);
    FSM.EVENTS = events;
    FSM.NUMBER_OF_EVENTS = inputs.size();
    FSM fsm = new FSM(stateCode.size());
    String currentState = "000";
    int num = 0;
    for (Element e : data) {
      int from = stateCode.get(currentState);
      int to = stateCode.get(e.state);
      String event = e.input + "";
      int eventId = events.indexOf(event);
      String output = e.output + "";
      if (fsm.transitions[from][eventId].getEndState() != -1) {
        Transition oldTransition = fsm.transitions[from][eventId];
        if (!oldTransition.getAction().equals(output)) {
          System.err.println(
              num + ": Inconsistent output: old=" + oldTransition.getAction() + ", new=" + output);
        }
        if (oldTransition.getEndState() != to) {
          System.err.println(
              num
                  + ": Inconsistent destination state: old="
                  + oldTransition.getEndState()
                  + ", new="
                  + to);
        }
      } else {
        fsm.transitions[from][eventId] = new Transition(from, to, event, output);
      }
      currentState = e.state;
      num++;
    }

    new FsmMetaData(fsm, fsm.getTransitions(), 0).printToGraphViz(".");

    for (Entry<String, Integer> e : stateCode.entrySet()) {
      System.out.println(e.getValue() + " -> " + e.getKey());
    }
  }
  public TestsModelCheckingTaskWithConsistencyGraph(AbstractTaskConfig config) {
    super(config);
    FSM.setEvents(events);

    for (int i = 0; i < events.size(); i++) {
      eventsMap.put(events.get(i), i);
    }

    scenarioTree = new ScenariosTree();
    try {
      scenarioTree.load("sc");
    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (ParseException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    consistencyGraph = AdjacentCalculator.getAdjacent(scenarioTree);

    consistencyGraphBoolean = new boolean[consistencyGraph.size()][consistencyGraph.size()];
    hasInconsistency = new boolean[consistencyGraph.size()];

    Arrays.fill(hasInconsistency, false);
    for (int i = 0; i < consistencyGraph.size(); i++) {
      Arrays.fill(consistencyGraphBoolean[i], false);
    }

    for (Node n1 : consistencyGraph.keySet()) {
      for (Node n2 : consistencyGraph.get(n1)) {
        consistencyGraphBoolean[n1.getNumber()][n2.getNumber()] = true;
        consistencyGraphBoolean[n2.getNumber()][n1.getNumber()] = true;
      }
    }

    for (int i = 0; i < consistencyGraph.size(); i++) {
      for (int j = 0; j < consistencyGraph.size(); j++) {
        if (consistencyGraphBoolean[i][j]) {
          hasInconsistency[i] = true;
          break;
        }
      }
    }

    for (AutomatonTest test : tests) {
      for (String s : test.getInput()) {
        String[] eventGuard = s.split("\\[");
        String event = eventGuard[0].trim();
        efPairToEvent.put(s, event);

        MyBooleanExpression guard = null;

        try {
          if (eventGuard.length > 1) {
            guard = MyBooleanExpression.get(eventGuard[1].replace("]", "").trim());
          } else {
            guard = MyBooleanExpression.get("1");
          }
        } catch (ParseException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        efPairToBooleanExpression.put(s, guard);
      }
    }

    //		try {
    //			out = new PrintWriter(new File("tests-consistency-scatter"));
    //		} catch (FileNotFoundException e) {
    //			e.printStackTrace();
    //		}
  }
  @Override
  public InstanceMetaData<FSM> getInstanceMetaData(FSM fsm) {
    numberOfFitnessEvaluations++;

    FSM labelled = labelFSM(fsm, useScenarios);
    Set<Transition> visitedTransitions =
        new HashSet<Transition>(fsm.getNumberOfStates() * fsm.getNumberOfEvents());
    double f1 = 0;
    int numberOfSuccesses = 0;

    Set<Node>[] visits = new Set[fsm.getNumberOfStates()];
    for (int state = 0; state < visits.length; state++) {
      visits[state] = new HashSet<Node>();
    }

    for (AutomatonTest test : tests) {
      Node node = scenarioTree.getRoot();
      int currentState = fsm.getInitialState();

      List<String> answers = new ArrayList<String>();
      for (int i = 0; i < test.getInput().length; i++) {
        String efPair = test.getInput()[i];
        int eventIndex = events.indexOf(efPair);
        String answer = labelled.transitions[currentState][eventIndex].getAction();
        int nextState = labelled.transitions[currentState][eventIndex].getEndState();
        if (nextState == -1) {
          break;
        }
        visits[currentState].add(node);
        String event = efPairToEvent.get(efPair);
        MyBooleanExpression guard = efPairToBooleanExpression.get(efPair);

        visitedTransitions.add(new Transition(currentState, nextState, efPair, answer));
        currentState = nextState;
        answers.add(answer);

        if (node.hasTransition(event, guard)) {
          node = node.getTransition(event, guard).getDst();
        }
      }
      String answerArray[] = answers.toArray(new String[0]);

      double f =
          Math.max(test.getOutput().length, answerArray.length) == 0
              ? 1.0
              : test.getLevenshteinDistance(answerArray)
                  / Math.max(test.getOutput().length, answerArray.length);

      if (f < 1e-5) {
        numberOfSuccesses++;
      }
      f1 += 1.0 - f;
    }

    fsm.markUsedTransitions(visitedTransitions);

    double testsFF = testsCost;
    if (tests.length > 0) {
      testsFF = (numberOfSuccesses == tests.length) ? testsCost : testsCost * (f1 / tests.length);
    }

    FST fst = FstFactory.createFST(labelled, actions);

    if (fst.getUsedTransitionsCount() == 0) {
      return new FsmMetaData(fsm, visitedTransitions, 0);
    }

    double ltlFF = ltlCost;
    if (formulas.size() > 0) {
      verifier.configureStateMachine(fst);
      int verificationResult[] = verifier.verify();
      ltlFF = ltlCost * verificationResult[0] / formulas.size() / fst.getUsedTransitionsCount();
      if ((ltlFF > ltlCost) || (ltlFF < 0)) {
        throw new RuntimeException(String.valueOf(ltlFF));
      }
    }

    boolean[][] transitionUsedInCounterexample =
        new boolean[fsm.getNumberOfStates()][fsm.getNumberOfEvents()];
    for (int i = 0; i < transitionUsedInCounterexample.length; i++) {
      Arrays.fill(transitionUsedInCounterexample[i], false);
    }

    for (int state = 0; state < fsm.getNumberOfStates(); state++) {
      for (int event = 0; event < fsm.getNumberOfEvents(); event++) {
        ru.ifmo.ctddev.genetic.transducer.algorithm.Transition fstTransition =
            fst.getTransition(state, FSM.EVENTS.get(event));
        if (fstTransition == null) {
          continue;
        }
        if (fstTransition.isUsedByVerifier()) {
          transitionUsedInCounterexample[state][event] = true;
        }
      }
    }

    fsm.setTransitionsInCounterexample(transitionUsedInCounterexample);

    double consistencyFF = 1.0;
    if (testsFF < 1.0) {
      consistencyFF = 0;
      for (int state = 0; state < visits.length; state++) {
        if (visits[state].isEmpty()) {
          continue;
        }
        Set<NodePair> bad = new HashSet<NodePair>();
        Set<Node> set = visits[state];

        for (Node n1 : set) {
          if (!hasInconsistency[n1.getNumber()]) {
            continue;
          }
          for (Node n2 : set) {
            if (n2 == n1) {
              continue;
            }
            if (consistencyGraphBoolean[n1.getNumber()][n2.getNumber()]) {
              NodePair badPair = new NodePair(n1, n2);
              if (!bad.contains(badPair.getComplimentaryPair())) {
                bad.add(badPair);
              }
            }
          }
        }

        consistencyFF += bad.size() / Math.pow((double) visits[state].size(), 2);
      }
      consistencyFF /= fsm.getNumberOfStates();
      if (consistencyFF > 1.0 || consistencyFF < 0) {
        throw new RuntimeException("Consistency metric out of range: " + consistencyFF);
      }
      consistencyFF = 1.0 - consistencyFF;
    }

    double fitness = testsFF + ltlFF + consistencyFF;

    double transitionsFF = 0.0001 * (100 - fsm.getNumberOfTransitions()) / (double) tests.length;
    ;
    if (fitness < 3) {
      fitness = 0.75 * fitness + transitionsFF;
    } else {
      fitness += transitionsFF;
    }

    return new FsmMetaData(fsm, visitedTransitions, fitness);
  }