public static void minimizeHopcroft()

in oak-lucene/src/main/java/org/apache/lucene/util/automaton/MinimizationOperations.java [72:218]


  public static void minimizeHopcroft(Automaton a) {
    a.determinize();
    if (a.initial.numTransitions == 1) {
      Transition t = a.initial.transitionsArray[0];
      if (t.to == a.initial && t.min == Character.MIN_CODE_POINT
          && t.max == Character.MAX_CODE_POINT) return;
    }
    a.totalize();

    // initialize data structures
    final int[] sigma = a.getStartPoints();
    final State[] states = a.getNumberedStates();
    final int sigmaLen = sigma.length, statesLen = states.length;
    @SuppressWarnings({"rawtypes","unchecked"}) final ArrayList<State>[][] reverse =
      (ArrayList<State>[][]) new ArrayList[statesLen][sigmaLen];
    @SuppressWarnings({"rawtypes","unchecked"}) final HashSet<State>[] partition =
      (HashSet<State>[]) new HashSet[statesLen];
    @SuppressWarnings({"rawtypes","unchecked"}) final ArrayList<State>[] splitblock =
      (ArrayList<State>[]) new ArrayList[statesLen];
    final int[] block = new int[statesLen];
    final StateList[][] active = new StateList[statesLen][sigmaLen];
    final StateListNode[][] active2 = new StateListNode[statesLen][sigmaLen];
    final LinkedList<IntPair> pending = new LinkedList<IntPair>();
    final BitSet pending2 = new BitSet(sigmaLen*statesLen);
    final BitSet split = new BitSet(statesLen), 
      refine = new BitSet(statesLen), refine2 = new BitSet(statesLen);
    for (int q = 0; q < statesLen; q++) {
      splitblock[q] = new ArrayList<State>();
      partition[q] = new HashSet<State>();
      for (int x = 0; x < sigmaLen; x++) {
        active[q][x] = new StateList();
      }
    }
    // find initial partition and reverse edges
    for (int q = 0; q < statesLen; q++) {
      final State qq = states[q];
      final int j = qq.accept ? 0 : 1;
      partition[j].add(qq);
      block[q] = j;
      for (int x = 0; x < sigmaLen; x++) {
        final ArrayList<State>[] r =
          reverse[qq.step(sigma[x]).number];
        if (r[x] == null)
          r[x] = new ArrayList<State>();
        r[x].add(qq);
      }
    }
    // initialize active sets
    for (int j = 0; j <= 1; j++) {
      for (int x = 0; x < sigmaLen; x++) {
        for (final State qq : partition[j]) {
          if (reverse[qq.number][x] != null)
            active2[qq.number][x] = active[j][x].add(qq);
        }
      }
    }
    // initialize pending
    for (int x = 0; x < sigmaLen; x++) {
      final int j = (active[0][x].size <= active[1][x].size) ? 0 : 1;
      pending.add(new IntPair(j, x));
      pending2.set(x*statesLen + j);
    }
    // process pending until fixed point
    int k = 2;
    while (!pending.isEmpty()) {
      final IntPair ip = pending.removeFirst();
      final int p = ip.n1;
      final int x = ip.n2;
      pending2.clear(x*statesLen + p);
      // find states that need to be split off their blocks
      for (StateListNode m = active[p][x].first; m != null; m = m.next) {
        final ArrayList<State> r = reverse[m.q.number][x];
        if (r != null) for (final State s : r) {
          final int i = s.number;
          if (!split.get(i)) {
            split.set(i);
            final int j = block[i];
            splitblock[j].add(s);
            if (!refine2.get(j)) {
              refine2.set(j);
              refine.set(j);
            }
          }
        }
      }
      // refine blocks
      for (int j = refine.nextSetBit(0); j >= 0; j = refine.nextSetBit(j+1)) {
        final ArrayList<State> sb = splitblock[j];
        if (sb.size() < partition[j].size()) {
          final HashSet<State> b1 = partition[j];
          final HashSet<State> b2 = partition[k];
          for (final State s : sb) {
            b1.remove(s);
            b2.add(s);
            block[s.number] = k;
            for (int c = 0; c < sigmaLen; c++) {
              final StateListNode sn = active2[s.number][c];
              if (sn != null && sn.sl == active[j][c]) {
                sn.remove();
                active2[s.number][c] = active[k][c].add(s);
              }
            }
          }
          // update pending
          for (int c = 0; c < sigmaLen; c++) {
            final int aj = active[j][c].size,
              ak = active[k][c].size,
              ofs = c*statesLen;
            if (!pending2.get(ofs + j) && 0 < aj && aj <= ak) {
              pending2.set(ofs + j);
              pending.add(new IntPair(j, c));
            } else {
              pending2.set(ofs + k);
              pending.add(new IntPair(k, c));
            }
          }
          k++;
        }
        refine2.clear(j);
        for (final State s : sb)
          split.clear(s.number);
        sb.clear();
      }
      refine.clear();
    }
    // make a new state for each equivalence class, set initial state
    State[] newstates = new State[k];
    for (int n = 0; n < newstates.length; n++) {
      final State s = new State();
      newstates[n] = s;
      for (State q : partition[n]) {
        if (q == a.initial) a.initial = s;
        s.accept = q.accept;
        s.number = q.number; // select representative
        q.number = n;
      }
    }
    // build transitions and set acceptance
    for (int n = 0; n < newstates.length; n++) {
      final State s = newstates[n];
      s.accept = states[s.number].accept;
      for (Transition t : states[s.number].getTransitions())
        s.addTransition(new Transition(t.min, t.max, newstates[t.to.number]));
    }
    a.clearNumberedStates();
    a.removeDeadTransitions();
  }