private static int topoSortStatesRecurse(Automaton a, BitSet visited, int[] states, int upto, int state, int level) { if (level > MAX_RECURSION_LEVEL) { throw new IllegalArgumentException("input automaton is too large: " + level); } Transition t = new Transition(); int count = a.initTransition(state, t); for (int i=0;i<count;i++) { a.getNextTransition(t); if (!visited.get(t.dest)) { visited.set(t.dest); upto = topoSortStatesRecurse(a, visited, states, upto, t.dest, level+1); } } states[upto] = state; upto++; return upto; } }
/** Returns bitset marking states reachable from the initial state. */ private static BitSet getLiveStatesFromInitial(Automaton a) { int numStates = a.getNumStates(); BitSet live = new BitSet(numStates); if (numStates == 0) { return live; } ArrayDeque<Integer> workList = new ArrayDeque<>(); live.set(0); workList.add(0); Transition t = new Transition(); while (workList.isEmpty() == false) { int s = workList.removeFirst(); int count = a.initTransition(s, t); for(int i=0;i<count;i++) { a.getNextTransition(t); if (live.get(t.dest) == false) { live.set(t.dest); workList.add(t.dest); } } } return live; }
/** * Returns true if the language of this automaton is finite. The * automaton must not have any dead states. */ public static boolean isFinite(Automaton a) { if (a.getNumStates() == 0) { return true; } return isFinite(new Transition(), a, 0, new BitSet(a.getNumStates()), new BitSet(a.getNumStates()), 0); }
private static void articulationPointsRecurse(Automaton a, int state, int d, int[] depth, int[] low, int[] parent, BitSet visited, List<Integer> points) { visited.set(state); depth[state] = d; low[state] = d; int childCount = 0; boolean isArticulation = false; Transition t = new Transition(); int numT = a.initTransition(state, t); for (int i = 0; i < numT; i++) { a.getNextTransition(t); if (visited.get(t.dest) == false) { parent[t.dest] = state; articulationPointsRecurse(a, t.dest, d + 1, depth, low, parent, visited, points); childCount++; if (low[t.dest] >= depth[state]) { isArticulation = true; } low[state] = Math.min(low[state], low[t.dest]); } else if (t.dest != parent[state]) { low[state] = Math.min(low[state], depth[t.dest]); } } if ((parent[state] != -1 && isArticulation) || (parent[state] == -1 && childCount > 1)) { points.add(state); } } }
/** * Returns the longest string that is a prefix of all accepted strings and * visits each state at most once. The automaton must be deterministic. * * @return common prefix, which can be an empty (length 0) String (never null) */ public static String getCommonPrefix(Automaton a) { if (a.isDeterministic() == false) { throw new IllegalArgumentException("input automaton must be deterministic"); } StringBuilder b = new StringBuilder(); HashSet<Integer> visited = new HashSet<>(); int s = 0; boolean done; Transition t = new Transition(); do { done = true; visited.add(s); if (a.isAccept(s) == false && a.getNumTransitions(s) == 1) { a.getTransition(s, 0, t); if (t.min == t.max && !visited.contains(t.dest)) { b.appendCodePoint(t.min); s = t.dest; done = false; } } } while (!done); return b.toString(); }
Transition t = new Transition();
/** * Returns true if the given automaton accepts all strings for the specified min/max * range of the alphabet. The automaton must be minimized. */ public static boolean isTotal(Automaton a, int minAlphabet, int maxAlphabet) { if (a.isAccept(0) && a.getNumTransitions(0) == 1) { Transition t = new Transition(); a.getTransition(0, 0, t); return t.dest == 0 && t.min == minAlphabet && t.max == maxAlphabet; } return false; }
/** Sugar to get all transitions for all states. This is * object-heavy; it's better to iterate state by state instead. */ public Transition[][] getSortedTransitions() { int numStates = getNumStates(); Transition[][] transitions = new Transition[numStates][]; for(int s=0;s<numStates;s++) { int numTransitions = getNumTransitions(s); transitions[s] = new Transition[numTransitions]; for(int t=0;t<numTransitions;t++) { Transition transition = new Transition(); getTransition(s, t, transition); transitions[s][t] = transition; } } return transitions; }
/** * Returns the longest BytesRef that is a prefix of all accepted strings and * visits each state at most once. The automaton must be deterministic. * * @return common prefix, which can be an empty (length 0) BytesRef (never null) */ public static BytesRef getCommonPrefixBytesRef(Automaton a) { BytesRefBuilder builder = new BytesRefBuilder(); HashSet<Integer> visited = new HashSet<>(); int s = 0; boolean done; Transition t = new Transition(); do { done = true; visited.add(s); if (a.isAccept(s) == false && a.getNumTransitions(s) == 1) { a.getTransition(s, 0, t); if (t.min == t.max && !visited.contains(t.dest)) { builder.append((byte) t.min); s = t.dest; done = false; } } } while (!done); return builder.get(); }
seen.set(0); Transition t = new Transition(); while (workList.isEmpty() == false) { int state = workList.removeFirst();
/** If this automaton accepts a single input, return it. Else, return null. * The automaton must be deterministic. */ public static IntsRef getSingleton(Automaton a) { if (a.isDeterministic() == false) { throw new IllegalArgumentException("input automaton must be deterministic"); } IntsRefBuilder builder = new IntsRefBuilder(); HashSet<Integer> visited = new HashSet<>(); int s = 0; Transition t = new Transition(); while (true) { visited.add(s); if (a.isAccept(s) == false) { if (a.getNumTransitions(s) == 1) { a.getTransition(s, 0, t); if (t.min == t.max && !visited.contains(t.dest)) { builder.append(t.min); s = t.dest; continue; } } } else if (a.getNumTransitions(s) == 0) { return builder.get(); } // Automaton accepts more than one string: return null; } }
Transition t = new Transition(); int numStates = a.getNumStates(); for(int s=0;s<numStates;s++) {
/** Add a [virtual] epsilon transition between source and dest. * Dest state must already have all transitions added because this * method simply copies those same transitions over to source. */ public void addEpsilon(int source, int dest) { Transition t = new Transition(); int count = initTransition(dest, t); for(int i=0;i<count;i++) { getNextTransition(t); addTransition(source, t.dest, t.min, t.max); } if (isAccept(dest)) { setAccept(source, true); } }
/** Returns sink state, if present, else -1. */ private static int findSinkState(Automaton automaton) { int numStates = automaton.getNumStates(); Transition t = new Transition(); int foundState = -1; for (int s=0;s<numStates;s++) { if (automaton.isAccept(s)) { int count = automaton.initTransition(s, t); boolean isSinkState = false; for(int i=0;i<count;i++) { automaton.getNextTransition(t); if (t.dest == s && t.min == 0 && t.max == 0xff) { isSinkState = true; break; } } if (isSinkState) { foundState = s; break; } } } return foundState; }
Transition scratch = new Transition();
/** Copies over all states/transitions from other. */ public void copy(Automaton other) { int offset = getNumStates(); int otherNumStates = other.getNumStates(); // Copy all states copyStates(other); // Copy all transitions Transition t = new Transition(); for(int s=0;s<otherNumStates;s++) { int count = other.initTransition(s, t); for(int i=0;i<count;i++) { other.getNextTransition(t); addTransition(offset + s, offset + t.dest, t.min, t.max); } } }
Transition t = new Transition();
Transition t = new Transition(); for (int s=0;s<numStates;s++) { int numTransitions = a.getNumTransitions(s);
result.addTransition(deadState, deadState, Character.MIN_CODE_POINT, Character.MAX_CODE_POINT); Transition t = new Transition(); for(int i=0;i<numStates;i++) { int maxi = Character.MIN_CODE_POINT;
builder.copy(a); Transition t = new Transition(); int count = a.initTransition(0, t); for(int i=0;i<count;i++) {