public static void UnweightedMinimize <T, S>(DFSA <T, S> dfsa)
        {
            ICollection <DFSAState <T, S> > states = dfsa.States();
            long time = Runtime.CurrentTimeMillis();

            if (debug)
            {
                time = Runtime.CurrentTimeMillis();
                log.Info("\nStarting on " + dfsa.dfsaID);
                log.Info(" -- " + states.Count + " states.");
            }
            int numStates = states.Count;
            // assign ids
            int id = 0;

            DFSAState <T, S>[] state = ErasureUtils.UncheckedCast <DFSAState <T, S>[]>(new DFSAState[numStates]);
            IDictionary <DFSAState <T, S>, int> stateToID = Generics.NewHashMap();

            foreach (DFSAState <T, S> state1 in states)
            {
                state[id]         = state1;
                stateToID[state1] = int.Parse(id);
                id++;
            }
            // initialize grid
            bool[][] distinct = new bool[numStates][];
            IList <DFSAMinimizer.IntPair>[][] dependentList = ErasureUtils.UncheckedCast <IList <DFSAMinimizer.IntPair>[][]>(new IList[numStates][]);
            for (int i = 0; i < numStates; i++)
            {
                for (int j = i + 1; j < numStates; j++)
                {
                    distinct[i][j] = state[i].IsAccepting() != state[j].IsAccepting();
                }
            }
            if (debug)
            {
                log.Info("Initialized: " + (Runtime.CurrentTimeMillis() - time));
                time = Runtime.CurrentTimeMillis();
            }
            // visit all non-distinct
            for (int i_1 = 0; i_1 < numStates; i_1++)
            {
                for (int j = i_1 + 1; j < numStates; j++)
                {
                    if (!distinct[i_1][j])
                    {
                        DFSAState <T, S>      state1_1 = state[i_1];
                        DFSAState <T, S>      state2   = state[j];
                        DFSAMinimizer.IntPair ip       = new DFSAMinimizer.IntPair(i_1, j);
                        // check if some input distinguishes this pair
                        ICollection <T> inputs = Generics.NewHashSet();
                        Sharpen.Collections.AddAll(inputs, state1_1.ContinuingInputs());
                        Sharpen.Collections.AddAll(inputs, state2.ContinuingInputs());
                        bool distinguishable = false;
                        ICollection <DFSAMinimizer.IntPair> pendingIPairs = Generics.NewHashSet();
                        IEnumerator <T> inputI = inputs.GetEnumerator();
                        while (inputI.MoveNext() && !distinguishable)
                        {
                            T input = inputI.Current;
                            DFSATransition <T, S> transition1 = state1_1.Transition(input);
                            DFSATransition <T, S> transition2 = state2.Transition(input);
                            if ((transition1 == null) != (transition2 == null))
                            {
                                distinguishable = true;
                            }
                            if (transition1 != null && transition2 != null)
                            {
                                DFSAState <T, S> target1 = transition1.GetTarget();
                                DFSAState <T, S> target2 = transition2.GetTarget();
                                int num1 = stateToID[target1];
                                int num2 = stateToID[target2];
                                DFSAMinimizer.IntPair targetIPair = new DFSAMinimizer.IntPair(num1, num2);
                                if (num1 != num2)
                                {
                                    if (distinct[num1][num2])
                                    {
                                        distinguishable = true;
                                    }
                                    else
                                    {
                                        pendingIPairs.Add(targetIPair);
                                    }
                                }
                            }
                        }
                        if (distinguishable)
                        {
                            // if the pair is distinguishable, record that
                            IList <DFSAMinimizer.IntPair> markStack = new List <DFSAMinimizer.IntPair>();
                            markStack.Add(ip);
                            while (!markStack.IsEmpty())
                            {
                                DFSAMinimizer.IntPair ipToMark = markStack[markStack.Count - 1];
                                markStack.Remove(markStack.Count - 1);
                                distinct[ipToMark.i][ipToMark.j] = true;
                                IList <DFSAMinimizer.IntPair> addList = dependentList[ipToMark.i][ipToMark.j];
                                if (addList != null)
                                {
                                    Sharpen.Collections.AddAll(markStack, addList);
                                }
                            }
                        }
                        else
                        {
                            // otherwise add it to any pending pairs
                            foreach (DFSAMinimizer.IntPair pendingIPair in pendingIPairs)
                            {
                                IList <DFSAMinimizer.IntPair> dependentList1 = dependentList[pendingIPair.i][pendingIPair.j];
                                if (dependentList1 == null)
                                {
                                    dependentList1 = new List <DFSAMinimizer.IntPair>();
                                    dependentList[pendingIPair.i][pendingIPair.j] = dependentList1;
                                }
                                dependentList1.Add(ip);
                            }
                        }
                    }
                }
            }
            if (debug)
            {
                log.Info("All pairs marked: " + (Runtime.CurrentTimeMillis() - time));
                time = Runtime.CurrentTimeMillis();
            }
            // decide what canonical state each state will map to...
            IDisjointSet <DFSAState <T, S> > stateClasses = new FastDisjointSet <DFSAState <T, S> >(states);

            for (int i_2 = 0; i_2 < numStates; i_2++)
            {
                for (int j = i_2 + 1; j < numStates; j++)
                {
                    if (!distinct[i_2][j])
                    {
                        DFSAState <T, S> state1_1 = state[i_2];
                        DFSAState <T, S> state2   = state[j];
                        stateClasses.Union(state1_1, state2);
                    }
                }
            }
            IDictionary <DFSAState <T, S>, DFSAState <T, S> > stateToRep = Generics.NewHashMap();

            foreach (DFSAState <T, S> state1_2 in states)
            {
                DFSAState <T, S> rep = stateClasses.Find(state1_2);
                stateToRep[state1_2] = rep;
            }
            if (debug)
            {
                log.Info("Canonical states chosen: " + (Runtime.CurrentTimeMillis() - time));
                time = Runtime.CurrentTimeMillis();
            }
            // reduce the DFSA by replacing transition targets with their reps
            foreach (DFSAState <T, S> state1_3 in states)
            {
                if (!state1_3.Equals(stateToRep[state1_3]))
                {
                    continue;
                }
                foreach (DFSATransition <T, S> transition in state1_3.Transitions())
                {
                    //if (!transition.target.equals(stateToRep.get(transition.target)))
                    //  System.out.println(Utils.pad(transition.target.toString(),30)+stateToRep.get(transition.target));
                    transition.target = stateToRep[transition.target];
                }
            }
            dfsa.initialState = stateToRep[dfsa.initialState];
            if (debug)
            {
                log.Info("Done: " + (Runtime.CurrentTimeMillis() - time));
            }
        }
        // done!
        internal static void UnweightedMinimizeOld <T, S>(DFSA <T, S> dfsa)
        {
            ICollection <DFSAState <T, S> > states = dfsa.States();
            IDictionary <UnorderedPair <DFSAState <T, S>, DFSAState <T, S> >, IList <UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > > > stateUPairToDependentUPairList = Generics.NewHashMap(states.Count * states.Count / 2 + 1);
            IDictionary <UnorderedPair <DFSAState <T, S>, DFSAState <T, S> >, bool> stateUPairToDistinguished = Generics.NewHashMap(states.Count * states.Count / 2 + 1);

            int[] c          = new int[states.Count * states.Count / 2 + 1];
            int   streak     = 0;
            int   collisions = 0;
            int   entries    = 0;
            long  time       = Runtime.CurrentTimeMillis();

            if (debug)
            {
                time = Runtime.CurrentTimeMillis();
                log.Info("Starting on " + dfsa.dfsaID);
                log.Info(" -- " + states.Count + " states.");
            }
            // initialize grid
            int numDone = 0;

            foreach (DFSAState <T, S> state1 in states)
            {
                foreach (DFSAState <T, S> state2 in states)
                {
                    UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > up = new UnorderedPair <DFSAState <T, S>, DFSAState <T, S> >(state1, state2);
                    if (state1.Equals(state2))
                    {
                        continue;
                    }
                    if (stateUPairToDistinguished.Contains(up))
                    {
                        continue;
                    }
                    int bucket = (up.GetHashCode() & unchecked ((int)(0x7FFFFFFF))) % (states.Count * states.Count / 2 + 1);
                    c[bucket]++;
                    entries++;
                    if (c[bucket] > 1)
                    {
                        collisions++;
                        streak = 0;
                    }
                    else
                    {
                        streak++;
                    }
                    if (state1.IsAccepting() != state2.IsAccepting())
                    {
                        //log.info(Utils.pad((String)state1.stateID, 20)+" "+state2.stateID);
                        stateUPairToDistinguished[up] = true;
                    }
                    else
                    {
                        stateUPairToDistinguished[up] = false;
                    }
                }
                //stateUPairToDependentUPairList.put(up, new ArrayList());
                numDone++;
                if (numDone % 20 == 0)
                {
                    log.Info("\r" + numDone + "  " + ((double)collisions / (double)entries));
                }
            }
            if (debug)
            {
                log.Info("\nInitialized: " + (Runtime.CurrentTimeMillis() - time));
                time = Runtime.CurrentTimeMillis();
            }
            // visit each undistinguished pair
            foreach (UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > up_1 in stateUPairToDistinguished.Keys)
            {
                DFSAState <T, S> state1_1 = up_1.first;
                DFSAState <T, S> state2   = up_1.second;
                if (stateUPairToDistinguished[up_1].Equals(true))
                {
                    continue;
                }
                // check if some input distinguishes this pair
                ICollection <T> inputs = Generics.NewHashSet(state1_1.ContinuingInputs());
                Sharpen.Collections.AddAll(inputs, state2.ContinuingInputs());
                bool distinguishable = false;
                ICollection <UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > > pendingUPairs = Generics.NewHashSet();
                IEnumerator <T> inputI = inputs.GetEnumerator();
                while (inputI.MoveNext() && !distinguishable)
                {
                    T input = inputI.Current;
                    DFSATransition <T, S> transition1 = state1_1.Transition(input);
                    DFSATransition <T, S> transition2 = state2.Transition(input);
                    if ((transition1 == null) != (transition2 == null))
                    {
                        distinguishable = true;
                    }
                    if (transition1 != null && transition2 != null)
                    {
                        DFSAState <T, S> target1 = transition1.GetTarget();
                        DFSAState <T, S> target2 = transition2.GetTarget();
                        UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > targetUPair = new UnorderedPair <DFSAState <T, S>, DFSAState <T, S> >(target1, target2);
                        if (!target1.Equals(target2))
                        {
                            if (stateUPairToDistinguished[targetUPair].Equals(true))
                            {
                                distinguishable = true;
                            }
                            else
                            {
                                pendingUPairs.Add(targetUPair);
                            }
                        }
                    }
                }
                // if the pair is distinguishable, record that
                if (distinguishable)
                {
                    IList <UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > > markStack = new List <UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > >();
                    markStack.Add(up_1);
                    while (!markStack.IsEmpty())
                    {
                        UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > upToMark = markStack[markStack.Count - 1];
                        markStack.Remove(markStack.Count - 1);
                        stateUPairToDistinguished[upToMark] = true;
                        IList <UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > > addList = stateUPairToDependentUPairList[upToMark];
                        if (addList != null)
                        {
                            Sharpen.Collections.AddAll(markStack, addList);
                            stateUPairToDependentUPairList[upToMark].Clear();
                        }
                    }
                }
                else
                {
                    // otherwise add it to any pending pairs
                    foreach (UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > pendingUPair in pendingUPairs)
                    {
                        IList <UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > > dependentList = stateUPairToDependentUPairList[pendingUPair];
                        if (dependentList == null)
                        {
                            dependentList = new List <UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > >();
                            stateUPairToDependentUPairList[pendingUPair] = dependentList;
                        }
                        dependentList.Add(up_1);
                    }
                }
            }
            if (debug)
            {
                log.Info("All pairs marked: " + (Runtime.CurrentTimeMillis() - time));
                time = Runtime.CurrentTimeMillis();
            }
            // decide what canonical state each state will map to...
            IDisjointSet <DFSAState <T, S> > stateClasses = new FastDisjointSet <DFSAState <T, S> >(states);

            foreach (UnorderedPair <DFSAState <T, S>, DFSAState <T, S> > up_2 in stateUPairToDistinguished.Keys)
            {
                if (stateUPairToDistinguished[up_2].Equals(false))
                {
                    DFSAState <T, S> state1_1 = up_2.first;
                    DFSAState <T, S> state2   = up_2.second;
                    stateClasses.Union(state1_1, state2);
                }
            }
            IDictionary <DFSAState <T, S>, DFSAState <T, S> > stateToRep = Generics.NewHashMap();

            foreach (DFSAState <T, S> state in states)
            {
                DFSAState <T, S> rep = stateClasses.Find(state);
                stateToRep[state] = rep;
            }
            if (debug)
            {
                log.Info("Canonical states chosen: " + (Runtime.CurrentTimeMillis() - time));
                time = Runtime.CurrentTimeMillis();
            }
            // reduce the DFSA by replacing transition targets with their reps
            foreach (DFSAState <T, S> state_1 in states)
            {
                if (!state_1.Equals(stateToRep[state_1]))
                {
                    continue;
                }
                foreach (DFSATransition <T, S> transition in state_1.Transitions())
                {
                    transition.target = stateClasses.Find(transition.target);
                }
            }
            dfsa.initialState = stateClasses.Find(dfsa.initialState);
            if (debug)
            {
                log.Info("Done: " + (Runtime.CurrentTimeMillis() - time));
            }
        }