/// <summary> /// Adds epsilon transitions to the given automaton. This method adds extra /// character interval transitions that are equivalent to the given set of /// epsilon transitions. /// </summary> /// <param name="a"> Automaton. </param> /// <param name="pairs"> Collection of <see cref="StatePair"/> objects representing pairs of /// source/destination states where epsilon transitions should be /// added. </param> public static void AddEpsilons(Automaton a, ICollection <StatePair> pairs) { a.ExpandSingleton(); Dictionary <State, JCG.HashSet <State> > forward = new Dictionary <State, JCG.HashSet <State> >(); Dictionary <State, JCG.HashSet <State> > back = new Dictionary <State, JCG.HashSet <State> >(); foreach (StatePair p in pairs) { if (!forward.TryGetValue(p.s1, out JCG.HashSet <State> to)) { to = new JCG.HashSet <State>(); forward[p.s1] = to; } to.Add(p.s2); if (!back.TryGetValue(p.s2, out JCG.HashSet <State> from)) { from = new JCG.HashSet <State>(); back[p.s2] = from; } from.Add(p.s1); } // calculate epsilon closure LinkedList <StatePair> worklist = new LinkedList <StatePair>(pairs); JCG.HashSet <StatePair> workset = new JCG.HashSet <StatePair>(pairs); while (worklist.Count > 0) { StatePair p = worklist.First.Value; worklist.Remove(p); workset.Remove(p); #pragma warning disable IDE0018 // Inline variable declaration JCG.HashSet <State> from; #pragma warning restore IDE0018 // Inline variable declaration if (forward.TryGetValue(p.s2, out JCG.HashSet <State> to)) { foreach (State s in to) { StatePair pp = new StatePair(p.s1, s); if (!pairs.Contains(pp)) { pairs.Add(pp); forward[p.s1].Add(s); back[s].Add(p.s1); worklist.AddLast(pp); workset.Add(pp); if (back.TryGetValue(p.s1, out from)) { foreach (State q in from) { StatePair qq = new StatePair(q, p.s1); if (!workset.Contains(qq)) { worklist.AddLast(qq); workset.Add(qq); } } } } } } } // add transitions foreach (StatePair p in pairs) { p.s1.AddEpsilon(p.s2); } a.deterministic = false; //a.clearHashCode(); a.ClearNumberedStates(); a.CheckMinimizeAlways(); }
/// <summary> /// Returns an automaton that accepts the intersection of the languages of the /// given automata. Never modifies the input automata languages. /// <para/> /// Complexity: quadratic in number of states. /// </summary> public static Automaton Intersection(Automaton a1, Automaton a2) { if (a1.IsSingleton) { if (BasicOperations.Run(a2, a1.singleton)) { return(a1.CloneIfRequired()); } else { return(BasicAutomata.MakeEmpty()); } } if (a2.IsSingleton) { if (BasicOperations.Run(a1, a2.singleton)) { return(a2.CloneIfRequired()); } else { return(BasicAutomata.MakeEmpty()); } } if (a1 == a2) { return(a1.CloneIfRequired()); } Transition[][] transitions1 = a1.GetSortedTransitions(); Transition[][] transitions2 = a2.GetSortedTransitions(); Automaton c = new Automaton(); Queue <StatePair> worklist = new Queue <StatePair>(); // LUCENENET specific - Queue is much more performant than LinkedList Dictionary <StatePair, StatePair> newstates = new Dictionary <StatePair, StatePair>(); StatePair p = new StatePair(c.initial, a1.initial, a2.initial); worklist.Enqueue(p); newstates[p] = p; while (worklist.Count > 0) { p = worklist.Dequeue(); p.s.accept = p.s1.accept && p.s2.accept; Transition[] t1 = transitions1[p.s1.number]; Transition[] t2 = transitions2[p.s2.number]; for (int n1 = 0, b2 = 0; n1 < t1.Length; n1++) { while (b2 < t2.Length && t2[b2].max < t1[n1].min) { b2++; } for (int n2 = b2; n2 < t2.Length && t1[n1].max >= t2[n2].min; n2++) { if (t2[n2].max >= t1[n1].min) { StatePair q = new StatePair(t1[n1].to, t2[n2].to); if (!newstates.TryGetValue(q, out StatePair r) || r is null) { q.s = new State(); worklist.Enqueue(q); newstates[q] = q; r = q; } int min = t1[n1].min > t2[n2].min ? t1[n1].min : t2[n2].min; int max = t1[n1].max < t2[n2].max ? t1[n1].max : t2[n2].max; p.s.AddTransition(new Transition(min, max, r.s)); } } } } c.deterministic = a1.deterministic && a2.deterministic; c.RemoveDeadTransitions(); c.CheckMinimizeAlways(); return(c); }
/// <summary> /// Returns true if the language of <paramref name="a1"/> is a subset of the language /// of <paramref name="a2"/>. As a side-effect, <paramref name="a2"/> is determinized if /// not already marked as deterministic. /// <para/> /// Complexity: quadratic in number of states. /// </summary> public static bool SubsetOf(Automaton a1, Automaton a2) { if (a1 == a2) { return(true); } if (a1.IsSingleton) { if (a2.IsSingleton) { return(a1.singleton.Equals(a2.singleton, StringComparison.Ordinal)); } return(BasicOperations.Run(a2, a1.singleton)); } a2.Determinize(); Transition[][] transitions1 = a1.GetSortedTransitions(); Transition[][] transitions2 = a2.GetSortedTransitions(); Queue <StatePair> worklist = new Queue <StatePair>(); // LUCENENET specific - Queue is much more performant than LinkedList JCG.HashSet <StatePair> visited = new JCG.HashSet <StatePair>(); StatePair p = new StatePair(a1.initial, a2.initial); worklist.Enqueue(p); visited.Add(p); while (worklist.Count > 0) { p = worklist.Dequeue(); if (p.s1.accept && !p.s2.accept) { return(false); } Transition[] t1 = transitions1[p.s1.number]; Transition[] t2 = transitions2[p.s2.number]; for (int n1 = 0, b2 = 0; n1 < t1.Length; n1++) { while (b2 < t2.Length && t2[b2].max < t1[n1].min) { b2++; } int min1 = t1[n1].min, max1 = t1[n1].max; for (int n2 = b2; n2 < t2.Length && t1[n1].max >= t2[n2].min; n2++) { if (t2[n2].min > min1) { return(false); } if (t2[n2].max < Character.MaxCodePoint) { min1 = t2[n2].max + 1; } else { min1 = Character.MaxCodePoint; max1 = Character.MinCodePoint; } StatePair q = new StatePair(t1[n1].to, t2[n2].to); if (!visited.Contains(q)) { worklist.Enqueue(q); visited.Add(q); } } if (min1 <= max1) { return(false); } } } return(true); }
/// <summary> /// Returns true if the language of <paramref name="a1"/> is a subset of the language /// of <paramref name="a2"/>. As a side-effect, <paramref name="a2"/> is determinized if /// not already marked as deterministic. /// <para/> /// Complexity: quadratic in number of states. /// </summary> public static bool SubsetOf(Automaton a1, Automaton a2) { if (a1 == a2) { return(true); } if (a1.IsSingleton) { if (a2.IsSingleton) { return(a1.singleton.Equals(a2.singleton, StringComparison.Ordinal)); } return(BasicOperations.Run(a2, a1.singleton)); } a2.Determinize(); Transition[][] transitions1 = a1.GetSortedTransitions(); Transition[][] transitions2 = a2.GetSortedTransitions(); LinkedList <StatePair> worklist = new LinkedList <StatePair>(); HashSet <StatePair> visited = new HashSet <StatePair>(); StatePair p = new StatePair(a1.initial, a2.initial); worklist.AddLast(p); visited.Add(p); while (worklist.Count > 0) { p = worklist.First.Value; worklist.Remove(p); if (p.S1.accept && !p.S2.accept) { return(false); } Transition[] t1 = transitions1[p.S1.number]; Transition[] t2 = transitions2[p.S2.number]; for (int n1 = 0, b2 = 0; n1 < t1.Length; n1++) { while (b2 < t2.Length && t2[b2].max < t1[n1].min) { b2++; } int min1 = t1[n1].min, max1 = t1[n1].max; for (int n2 = b2; n2 < t2.Length && t1[n1].max >= t2[n2].min; n2++) { if (t2[n2].min > min1) { return(false); } if (t2[n2].max < Character.MAX_CODE_POINT) { min1 = t2[n2].max + 1; } else { min1 = Character.MAX_CODE_POINT; max1 = Character.MIN_CODE_POINT; } StatePair q = new StatePair(t1[n1].to, t2[n2].to); if (!visited.Contains(q)) { worklist.AddLast(q); visited.Add(q); } } if (min1 <= max1) { return(false); } } } return(true); }