/** * Returns an automaton that accepts between <code>min</code> and <code>max</code> (including * both) concatenated repetitions of the language of the given automaton. * * <p>Complexity: linear in number of states and in <code>min</code> and <code>max</code>. */ public static Automaton repeat(Automaton a, int min, int max) { if (min > max) return BasicAutomata.makeEmpty(); max -= min; a.expandSingleton(); Automaton b; if (min == 0) b = BasicAutomata.makeEmptyString(); else if (min == 1) b = a.clone(); else { List<Automaton> as = new ArrayList<Automaton>(); while (min-- > 0) as.add(a); b = concatenate(as); } if (max > 0) { Automaton d = a.clone(); while (--max > 0) { Automaton c = a.clone(); for (State p : c.getAcceptStates()) p.addEpsilon(d.initial); d = c; } for (State p : b.getAcceptStates()) p.addEpsilon(d.initial); b.deterministic = false; // b.clearHashCode(); b.clearNumberedStates(); b.checkMinimizeAlways(); } return b; }
/** * Returns an automaton that accepts the Kleene star (zero or more concatenated repetitions) of * the language of the given automaton. Never modifies the input automaton language. * * <p>Complexity: linear in number of states. */ public static Automaton repeat(Automaton a) { a = a.cloneExpanded(); State s = new State(); s.accept = true; s.addEpsilon(a.initial); for (State p : a.getAcceptStates()) p.addEpsilon(s); a.initial = s; a.deterministic = false; // a.clearHashCode(); a.clearNumberedStates(); a.checkMinimizeAlways(); return a; }
/** * Returns an automaton that accepts the concatenation of the languages of the given automata. * * <p>Complexity: linear in number of states. */ public static Automaton concatenate(Automaton a1, Automaton a2) { if (a1.isSingleton() && a2.isSingleton()) return BasicAutomata.makeString(a1.singleton + a2.singleton); if (isEmpty(a1) || isEmpty(a2)) return BasicAutomata.makeEmpty(); // adding epsilon transitions with the NFA concatenation algorithm // in this case always produces a resulting DFA, preventing expensive // redundant determinize() calls for this common case. boolean deterministic = a1.isSingleton() && a2.isDeterministic(); if (a1 == a2) { a1 = a1.cloneExpanded(); a2 = a2.cloneExpanded(); } else { a1 = a1.cloneExpandedIfRequired(); a2 = a2.cloneExpandedIfRequired(); } for (State s : a1.getAcceptStates()) { s.accept = false; s.addEpsilon(a2.initial); } a1.deterministic = deterministic; // a1.clearHashCode(); a1.clearNumberedStates(); a1.checkMinimizeAlways(); return a1; }
/** * Returns an automaton that accepts the union of the empty string and the language of the given * automaton. * * <p>Complexity: linear in number of states. */ public static Automaton optional(Automaton a) { a = a.cloneExpandedIfRequired(); State s = new State(); s.addEpsilon(a.initial); s.accept = true; a.initial = s; a.deterministic = false; // a.clearHashCode(); a.clearNumberedStates(); a.checkMinimizeAlways(); return a; }
/** * Returns an automaton that accepts the concatenation of the languages of the given automata. * * <p>Complexity: linear in total number of states. */ public static Automaton concatenate(List<Automaton> l) { if (l.isEmpty()) return BasicAutomata.makeEmptyString(); boolean all_singleton = true; for (Automaton a : l) if (!a.isSingleton()) { all_singleton = false; break; } if (all_singleton) { StringBuilder b = new StringBuilder(); for (Automaton a : l) b.append(a.singleton); return BasicAutomata.makeString(b.toString()); } else { for (Automaton a : l) if (BasicOperations.isEmpty(a)) return BasicAutomata.makeEmpty(); Set<Integer> ids = new HashSet<Integer>(); for (Automaton a : l) ids.add(System.identityHashCode(a)); boolean has_aliases = ids.size() != l.size(); Automaton b = l.get(0); if (has_aliases) b = b.cloneExpanded(); else b = b.cloneExpandedIfRequired(); Set<State> ac = b.getAcceptStates(); boolean first = true; for (Automaton a : l) if (first) first = false; else { if (a.isEmptyString()) continue; Automaton aa = a; if (has_aliases) aa = aa.cloneExpanded(); else aa = aa.cloneExpandedIfRequired(); Set<State> ns = aa.getAcceptStates(); for (State s : ac) { s.accept = false; s.addEpsilon(aa.initial); if (s.accept) ns.add(s); } ac = ns; } b.deterministic = false; // b.clearHashCode(); b.clearNumberedStates(); b.checkMinimizeAlways(); return b; } }
/** * Returns an automaton that accepts the union of the languages of the given automata. * * <p>Complexity: linear in number of states. */ public static Automaton union(Collection<Automaton> l) { Set<Integer> ids = new HashSet<Integer>(); for (Automaton a : l) ids.add(System.identityHashCode(a)); boolean has_aliases = ids.size() != l.size(); State s = new State(); for (Automaton b : l) { if (BasicOperations.isEmpty(b)) continue; Automaton bb = b; if (has_aliases) bb = bb.cloneExpanded(); else bb = bb.cloneExpandedIfRequired(); s.addEpsilon(bb.initial); } Automaton a = new Automaton(); a.initial = s; a.deterministic = false; // a.clearHashCode(); a.clearNumberedStates(); a.checkMinimizeAlways(); return a; }
/** * Returns an automaton that accepts the union of the languages of the given automata. * * <p>Complexity: linear in number of states. */ public static Automaton union(Automaton a1, Automaton a2) { if ((a1.isSingleton() && a2.isSingleton() && a1.singleton.equals(a2.singleton)) || a1 == a2) return a1.cloneIfRequired(); if (a1 == a2) { a1 = a1.cloneExpanded(); a2 = a2.cloneExpanded(); } else { a1 = a1.cloneExpandedIfRequired(); a2 = a2.cloneExpandedIfRequired(); } State s = new State(); s.addEpsilon(a1.initial); s.addEpsilon(a2.initial); a1.initial = s; a1.deterministic = false; // a1.clearHashCode(); a1.clearNumberedStates(); a1.checkMinimizeAlways(); return a1; }