src/Lucene.Net/Util/Automaton/Automaton.cs (605 lines of code) (raw):

using J2N; using J2N.Collections.Generic.Extensions; using Lucene.Net.Diagnostics; using Lucene.Net.Support; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; using JCG = J2N.Collections.Generic; /* * dk.brics.automaton * * Copyright (c) 2001-2009 Anders Moeller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * this SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * this SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ namespace Lucene.Net.Util.Automaton { /// <summary> /// Finite-state automaton with regular expression operations. /// <para/> /// Class invariants: /// <list type="bullet"> /// <item><description>An automaton is either represented explicitly (with <see cref="State"/> and /// <see cref="Transition"/> objects) or with a singleton string (see /// <see cref="Singleton"/> and <see cref="ExpandSingleton()"/>) in case the automaton /// is known to accept exactly one string. (Implicitly, all states and /// transitions of an automaton are reachable from its initial state.)</description></item> /// <item><description>Automata are always reduced (see <see cref="Reduce()"/>) and have no /// transitions to dead states (see <see cref="RemoveDeadTransitions()"/>).</description></item> /// <item><description>If an automaton is nondeterministic, then <see cref="IsDeterministic"/> /// returns <c>false</c> (but the converse is not required).</description></item> /// <item><description>Automata provided as input to operations are generally assumed to be /// disjoint.</description></item> /// </list> /// <para/> /// If the states or transitions are manipulated manually, the /// <see cref="RestoreInvariant()"/> method and <see cref="IsDeterministic"/> setter /// should be used afterwards to restore representation invariants that are /// assumed by the built-in automata operations. /// /// <para/> /// <para> /// Note: this class has internal mutable state and is not thread safe. It is /// the caller's responsibility to ensure any necessary synchronization if you /// wish to use the same Automaton from multiple threads. In general it is instead /// recommended to use a <see cref="RunAutomaton"/> for multithreaded matching: it is immutable, /// thread safe, and much faster. /// </para> /// @lucene.experimental /// </summary> public class Automaton // LUCENENET specific: Not implementing ICloneable per Microsoft's recommendation { /// <summary> /// Minimize using Hopcroft's O(n log n) algorithm. this is regarded as one of /// the most generally efficient algorithms that exist. /// </summary> /// <seealso cref="SetMinimization(int)"/> public const int MINIMIZE_HOPCROFT = 2; /// <summary> /// Selects minimization algorithm (default: <c>MINIMIZE_HOPCROFT</c>). </summary> internal static int minimization = MINIMIZE_HOPCROFT; /// <summary> /// Initial state of this automaton. </summary> internal State initial; /// <summary> /// If <c>true</c>, then this automaton is definitely deterministic (i.e., there are /// no choices for any run, but a run may crash). /// </summary> internal bool deterministic; /// <summary> /// Extra data associated with this automaton. </summary> internal object info; ///// <summary> ///// Hash code. Recomputed by <see cref="MinimizationOperations#minimize(Automaton)"/> ///// </summary> //int hash_code; /// <summary> /// Singleton string. Null if not applicable. </summary> internal string singleton; /// <summary> /// Minimize always flag. </summary> internal static bool minimize_always = false; /// <summary> /// Selects whether operations may modify the input automata (default: /// <c>false</c>). /// </summary> internal static bool allow_mutation = false; /// <summary> /// Constructs a new automaton that accepts the empty language. Using this /// constructor, automata can be constructed manually from <see cref="State"/> and /// <see cref="Transition"/> objects. /// </summary> /// <seealso cref="State"/> /// <seealso cref="Transition"/> public Automaton(State initial) { this.initial = initial; deterministic = true; singleton = null; } public Automaton() : this(new State()) { } /// <summary> /// Selects minimization algorithm (default: <c>MINIMIZE_HOPCROFT</c>). /// </summary> /// <param name="algorithm"> minimization algorithm </param> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetMinimization(int algorithm) { minimization = algorithm; } /// <summary> /// Sets or resets minimize always flag. If this flag is set, then /// <see cref="MinimizationOperations.Minimize(Automaton)"/> will automatically be /// invoked after all operations that otherwise may produce non-minimal /// automata. By default, the flag is not set. /// </summary> /// <param name="flag"> if <c>true</c>, the flag is set </param> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetMinimizeAlways(bool flag) { minimize_always = flag; } /// <summary> /// Sets or resets allow mutate flag. If this flag is set, then all automata /// operations may modify automata given as input; otherwise, operations will /// always leave input automata languages unmodified. By default, the flag is /// not set. /// </summary> /// <param name="flag"> if <c>true</c>, the flag is set </param> /// <returns> previous value of the flag </returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool SetAllowMutate(bool flag) { bool b = allow_mutation; allow_mutation = flag; return b; } /// <summary> /// Returns the state of the allow mutate flag. If this flag is set, then all /// automata operations may modify automata given as input; otherwise, /// operations will always leave input automata languages unmodified. By /// default, the flag is not set. /// </summary> /// <returns> current value of the flag </returns> internal static bool AllowMutate => allow_mutation; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal virtual void CheckMinimizeAlways() { if (minimize_always) { MinimizationOperations.Minimize(this); } } internal bool IsSingleton => singleton != null; /// <summary> /// Returns the singleton string for this automaton. An automaton that accepts /// exactly one string <i>may</i> be represented in singleton mode. In that /// case, this method may be used to obtain the string. /// </summary> /// <returns> String, <c>null</c> if this automaton is not in singleton mode. </returns> public virtual string Singleton => singleton; ///// <summary> ///// Sets initial state. ///// </summary> ///// <param name="s"> state </param> /* public void setInitialState(State s) { initial = s; singleton = null; } */ /// <summary> /// Gets initial state. /// </summary> /// <returns> state </returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual State GetInitialState() { ExpandSingleton(); return initial; } /// <summary> /// Returns deterministic flag for this automaton. /// </summary> /// <returns> <c>true</c> if the automaton is definitely deterministic, <c>false</c> if the /// automaton may be nondeterministic </returns> public virtual bool IsDeterministic { get => deterministic; set => deterministic = value; } /// <summary> /// Associates extra information with this automaton. /// </summary> /// <param name="value"> extra information </param> public virtual object Info { get => info; set => info = value; } // cached private State[] numberedStates; public virtual State[] GetNumberedStates() { if (numberedStates is null) { ExpandSingleton(); JCG.HashSet<State> visited = new JCG.HashSet<State>(); Queue<State> worklist = new Queue<State>(); // LUCENENET specific - Queue is much more performant than LinkedList State[] states = new State[4]; int upto = 0; worklist.Enqueue(initial); visited.Add(initial); initial.number = upto; states[upto] = initial; upto++; while (worklist.TryDequeue(out State s)) { for (int i = 0; i < s.numTransitions; i++) { Transition t = s.TransitionsArray[i]; if (!visited.Contains(t.to)) { visited.Add(t.to); worklist.Enqueue(t.to); t.to.number = upto; if (upto == states.Length) { // LUCENENET: Resize rather than copy Array.Resize(ref states, ArrayUtil.Oversize(1 + upto, RamUsageEstimator.NUM_BYTES_OBJECT_REF)); } states[upto] = t.to; upto++; } } } if (states.Length != upto) { // LUCENENET: Resize rather than copy Array.Resize(ref states, upto); } numberedStates = states; } return numberedStates; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void SetNumberedStates(State[] states) { SetNumberedStates(states, states.Length); } public virtual void SetNumberedStates(State[] states, int count) { if (Debugging.AssertsEnabled) Debugging.Assert(count <= states.Length); // TODO: maybe we can eventually allow for oversizing here... if (count < states.Length) { State[] newArray = new State[count]; Arrays.Copy(states, 0, newArray, 0, count); numberedStates = newArray; } else { numberedStates = states; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void ClearNumberedStates() { numberedStates = null; } /// <summary> /// Returns the set of reachable accept states. /// </summary> /// <returns> Set of <see cref="State"/> objects. </returns> public virtual ISet<State> GetAcceptStates() { ExpandSingleton(); JCG.HashSet<State> accepts = new JCG.HashSet<State>(); JCG.HashSet<State> visited = new JCG.HashSet<State>(); Queue<State> worklist = new Queue<State>(); // LUCENENET specific - Queue is much more performant than LinkedList worklist.Enqueue(initial); visited.Add(initial); while (worklist.TryDequeue(out State s)) { if (s.accept) { accepts.Add(s); } foreach (Transition t in s.GetTransitions()) { if (!visited.Contains(t.to)) { visited.Add(t.to); worklist.Enqueue(t.to); } } } return accepts; } /// <summary> /// Adds transitions to explicit crash state to ensure that transition function /// is total. /// </summary> internal virtual void Totalize() { State s = new State(); s.AddTransition(new Transition(Character.MinCodePoint, Character.MaxCodePoint, s)); foreach (State p in GetNumberedStates()) { int maxi = Character.MinCodePoint; p.SortTransitions(Transition.COMPARE_BY_MIN_MAX_THEN_DEST); foreach (Transition t in p.GetTransitions()) { if (t.min > maxi) { p.AddTransition(new Transition(maxi, (t.min - 1), s)); } if (t.max + 1 > maxi) { maxi = t.max + 1; } } if (maxi <= Character.MaxCodePoint) { p.AddTransition(new Transition(maxi, Character.MaxCodePoint, s)); } } ClearNumberedStates(); } /// <summary> /// Restores representation invariant. This method must be invoked before any /// built-in automata operation is performed if automaton states or transitions /// are manipulated manually. /// </summary> /// <seealso cref="IsDeterministic"/> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void RestoreInvariant() { RemoveDeadTransitions(); } /// <summary> /// Reduces this automaton. An automaton is "reduced" by combining overlapping /// and adjacent edge intervals with same destination. /// </summary> public virtual void Reduce() { State[] states = GetNumberedStates(); if (IsSingleton) { return; } foreach (State s in states) { s.Reduce(); } } /// <summary> /// Returns sorted array of all interval start points. /// </summary> public virtual int[] GetStartPoints() { State[] states = GetNumberedStates(); JCG.HashSet<int> pointset = new JCG.HashSet<int> { Character.MinCodePoint }; foreach (State s in states) { foreach (Transition t in s.GetTransitions()) { pointset.Add(t.min); if (t.max < Character.MaxCodePoint) { pointset.Add((t.max + 1)); } } } int[] points = new int[pointset.Count]; int n = 0; foreach (int m in pointset) { points[n++] = m; } Array.Sort(points); return points; } /// <summary> /// Returns the set of live states. A state is "live" if an accept state is /// reachable from it. /// </summary> /// <returns> Set of <see cref="State"/> objects. </returns> private State[] GetLiveStates() { State[] states = GetNumberedStates(); JCG.HashSet<State> live = new JCG.HashSet<State>(); foreach (State q in states) { if (q.Accept) { live.Add(q); } } // map<state, set<state>> ISet<State>[] map = new JCG.HashSet<State>[states.Length]; for (int i = 0; i < map.Length; i++) { map[i] = new JCG.HashSet<State>(); } foreach (State s in states) { for (int i = 0; i < s.numTransitions; i++) { map[s.TransitionsArray[i].to.Number].Add(s); } } LinkedList<State> worklist = new LinkedList<State>(live); // LUCENENET: Queue would be slower in this case because of the copy constructor while (worklist.Count > 0) { State s = worklist.First.Value; worklist.Remove(s); foreach (State p in map[s.number]) { if (!live.Contains(p)) { live.Add(p); worklist.AddLast(p); } } } return live.ToArray(/*new State[live.Count]*/); } /// <summary> /// Removes transitions to dead states and calls <see cref="Reduce()"/>. /// (A state is "dead" if no accept state is /// reachable from it.) /// </summary> public virtual void RemoveDeadTransitions() { State[] states = GetNumberedStates(); //clearHashCode(); if (IsSingleton) { return; } State[] live = GetLiveStates(); OpenBitSet liveSet = new OpenBitSet(states.Length); foreach (State s in live) { liveSet.Set(s.number); } foreach (State s in states) { // filter out transitions to dead states: int upto = 0; for (int i = 0; i < s.numTransitions; i++) { Transition t = s.TransitionsArray[i]; if (liveSet.Get(t.to.number)) { s.TransitionsArray[upto++] = s.TransitionsArray[i]; } } s.numTransitions = upto; } for (int i = 0; i < live.Length; i++) { live[i].number = i; } if (live.Length > 0) { SetNumberedStates(live); } else { // sneaky corner case -- if machine accepts no strings ClearNumberedStates(); } Reduce(); } /// <summary> /// Returns a sorted array of transitions for each state (and sets state /// numbers). /// </summary> public virtual Transition[][] GetSortedTransitions() { State[] states = GetNumberedStates(); Transition[][] transitions = new Transition[states.Length][]; foreach (State s in states) { s.SortTransitions(Transition.COMPARE_BY_MIN_MAX_THEN_DEST); s.TrimTransitionsArray(); transitions[s.number] = s.TransitionsArray; if (Debugging.AssertsEnabled) Debugging.Assert(s.TransitionsArray != null); } return transitions; } /// <summary> /// Expands singleton representation to normal representation. Does nothing if /// not in singleton representation. /// </summary> public virtual void ExpandSingleton() { if (IsSingleton) { State p = new State(); initial = p; int cp; // LUCENENET: Removed unnecessary assignment for (int i = 0; i < singleton.Length; i += Character.CharCount(cp)) { State q = new State(); p.AddTransition(new Transition(cp = Character.CodePointAt(singleton, i), q)); p = q; } p.accept = true; deterministic = true; singleton = null; } } /// <summary> /// Returns the number of states in this automaton. /// </summary> public virtual int GetNumberOfStates() { if (IsSingleton) { return singleton.CodePointCount(0, singleton.Length) + 1; } return GetNumberedStates().Length; } /// <summary> /// Returns the number of transitions in this automaton. This number is counted /// as the total number of edges, where one edge may be a character interval. /// </summary> public virtual int GetNumberOfTransitions() { if (IsSingleton) { return singleton.CodePointCount(0, singleton.Length); } int c = 0; foreach (State s in GetNumberedStates()) { c += s.NumTransitions; } return c; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (obj is Automaton other) return BasicOperations.SameLanguage(this, other); return false; // LUCENENET specific - need an Equals implementation for collection equality checking //throw UnsupportedOperationException.Create("use BasicOperations.sameLanguage instead"); } // LUCENENET specific - in .NET, we can't simply throw an exception here because // collections use this to determine equality. Most of this code was pieced together from // BasicOperations.SubSetOf (which, when done both ways determines equality). public override int GetHashCode() { if (IsSingleton) { return singleton.GetHashCode(); } int hash = 31; // arbitrary prime this.Determinize(); // LUCENENET: should we do this ? Transition[][] transitions = this.GetSortedTransitions(); Queue<State> worklist = new Queue<State>(); // LUCENENET specific - Queue is much more performant than LinkedList JCG.HashSet<State> visited = new JCG.HashSet<State>(); worklist.Enqueue(this.initial); visited.Add(this.initial); while (worklist.TryDequeue(out State current)) { unchecked { hash = 31 * hash + current.accept.GetHashCode(); } Transition[] t1 = transitions[current.number]; for (int n1 = 0; n1 < t1.Length; n1++) { int min1 = t1[n1].min, max1 = t1[n1].max; unchecked { hash = 31 * hash + min1.GetHashCode(); hash = 31 * hash + max1.GetHashCode(); } State next = t1[n1].to; if (!visited.Contains(next)) { worklist.Enqueue(next); visited.Add(next); } } } return hash; //throw UnsupportedOperationException.Create(); } ///// <summary> ///// Must be invoked when the stored hash code may no longer be valid. ///// </summary> /* void clearHashCode() { hash_code = 0; } */ /// <summary> /// Returns a string representation of this automaton. /// </summary> public override string ToString() { StringBuilder b = new StringBuilder(); if (IsSingleton) { b.Append("singleton: "); int length = singleton.CodePointCount(0, singleton.Length); int[] codepoints = new int[length]; int cp; // LUCENENET: Removed unnecessary assignment for (int i = 0, j = 0; i < singleton.Length; i += Character.CharCount(cp)) { codepoints[j++] = cp = singleton.CodePointAt(i); } foreach (int c in codepoints) { Transition.AppendCharString(c, b); } b.Append("\n"); } else { State[] states = GetNumberedStates(); b.Append("initial state: ").Append(initial.number).Append("\n"); foreach (State s in states) { b.Append(s.ToString()); } } return b.ToString(); } /// <summary> /// Returns <a href="http://www.research.att.com/sw/tools/graphviz/" /// target="_top">Graphviz Dot</a> representation of this automaton. /// </summary> public virtual string ToDot() { StringBuilder b = new StringBuilder("digraph Automaton {\n"); b.Append(" rankdir = LR;\n"); State[] states = GetNumberedStates(); foreach (State s in states) { b.Append(" ").Append(s.number); if (s.accept) { b.Append(" [shape=doublecircle,label=\"\"];\n"); } else { b.Append(" [shape=circle,label=\"\"];\n"); } if (s == initial) { b.Append(" initial [shape=plaintext,label=\"\"];\n"); b.Append(" initial -> ").Append(s.number).Append("\n"); } foreach (Transition t in s.GetTransitions()) { b.Append(" ").Append(s.number); t.AppendDot(b); } } return b.Append("}\n").ToString(); } /// <summary> /// Returns a clone of this automaton, expands if singleton. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] internal virtual Automaton CloneExpanded() { Automaton a = (Automaton)Clone(); a.ExpandSingleton(); return a; } /// <summary> /// Returns a clone of this automaton unless <see cref="allow_mutation"/> is /// set, expands if singleton. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] internal virtual Automaton CloneExpandedIfRequired() { if (allow_mutation) { ExpandSingleton(); return this; } else { return CloneExpanded(); } } /// <summary> /// Returns a clone of this automaton. /// </summary> public virtual object Clone() { Automaton a = (Automaton)base.MemberwiseClone(); if (!IsSingleton) { Dictionary<State, State> m = new Dictionary<State, State>(); State[] states = GetNumberedStates(); foreach (State s in states) { m[s] = new State(); } foreach (State s in states) { State p = m[s]; p.accept = s.accept; if (s == initial) { a.initial = p; } foreach (Transition t in s.GetTransitions()) { p.AddTransition(new Transition(t.min, t.max, m[t.to])); } } } a.ClearNumberedStates(); return a; } /// <summary> /// Returns a clone of this automaton, or this automaton itself if /// <see cref="allow_mutation"/> flag is set. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] internal virtual Automaton CloneIfRequired() { if (allow_mutation) { return this; } else { return (Automaton)Clone(); } } /// <summary> /// See <see cref="BasicOperations.Concatenate(Automaton, Automaton)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Automaton Concatenate(Automaton a) { return BasicOperations.Concatenate(this, a); } /// <summary> /// See <see cref="BasicOperations.Concatenate(IList{Automaton})"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Automaton Concatenate(IList<Automaton> l) { return BasicOperations.Concatenate(l); } /// <summary> /// See <see cref="BasicOperations.Optional(Automaton)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Automaton Optional() { return BasicOperations.Optional(this); } /// <summary> /// See <see cref="BasicOperations.Repeat(Automaton)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Automaton Repeat() { return BasicOperations.Repeat(this); } /// <summary> /// See <see cref="BasicOperations.Repeat(Automaton, int)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Automaton Repeat(int min) { return BasicOperations.Repeat(this, min); } /// <summary> /// See <see cref="BasicOperations.Repeat(Automaton, int, int)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Automaton Repeat(int min, int max) { return BasicOperations.Repeat(this, min, max); } /// <summary> /// See <see cref="BasicOperations.Complement(Automaton)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Automaton Complement() { return BasicOperations.Complement(this); } /// <summary> /// See <see cref="BasicOperations.Minus(Automaton, Automaton)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Automaton Minus(Automaton a) { return BasicOperations.Minus(this, a); } /// <summary> /// See <see cref="BasicOperations.Intersection(Automaton, Automaton)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Automaton Intersection(Automaton a) { return BasicOperations.Intersection(this, a); } /// <summary> /// See <see cref="BasicOperations.SubsetOf(Automaton, Automaton)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual bool SubsetOf(Automaton a) { return BasicOperations.SubsetOf(this, a); } /// <summary> /// See <see cref="BasicOperations.Union(Automaton, Automaton)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Automaton Union(Automaton a) { return BasicOperations.Union(this, a); } /// <summary> /// See <see cref="BasicOperations.Union(ICollection{Automaton})"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Automaton Union(ICollection<Automaton> l) { return BasicOperations.Union(l); } /// <summary> /// See <see cref="BasicOperations.Determinize(Automaton)"/>. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void Determinize() { BasicOperations.Determinize(this); } /// <summary> /// See <see cref="BasicOperations.IsEmptyString(Automaton)"/>. /// </summary> public virtual bool IsEmptyString => BasicOperations.IsEmptyString(this); /// <summary> /// See <see cref="MinimizationOperations.Minimize(Automaton)"/>. Returns the /// automaton being given as argument. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Automaton Minimize(Automaton a) { MinimizationOperations.Minimize(a); return a; } } }