public void IsEmpty_NotEmptyQueue_ReturnsFalse()
        {
            var queue = new FastEquivalenceQueue <int>(new ModuloIntegerEqualityComparer(3, 2));

            queue.Enqueue(2);
            Assert.IsFalse(queue.IsEmpty);
        }
        public void Equeue_EqueueOne_Enqueues()
        {
            var queue = new FastEquivalenceQueue <int>(EqualityComparer <int> .Default);

            queue.Enqueue(1);

            var result = queue.DequeueEquivalenceSet().ToList();

            Assert.AreEqual(1, result.Single());
        }
        public void Count_TwoEquivalenceClasses_ReturnsTotalNumberOfItems()
        {
            var queue = new FastEquivalenceQueue <int>(new ModuloIntegerEqualityComparer(2));

            queue.Enqueue(1);
            queue.Enqueue(2);
            queue.Enqueue(3);

            Assert.AreEqual(3, queue.Count);
        }
        public void DequeueEquivalenceSet_EqueueNonUniqueItem_EnqueuesOnlyOnce()
        {
            var queue = new FastEquivalenceQueue <int>(EqualityComparer <int> .Default);

            queue.Enqueue(1);
            queue.Enqueue(2);
            queue.Enqueue(1);

            var result = queue.DequeueEquivalenceSet().ToList();

            Assert.AreEqual(1, result.Count);
        }
        public void DequeueEquivalenceSet_TwoEquivalenceClasses_ReturnsOnlyItemsFromFirstClass()
        {
            var queue = new FastEquivalenceQueue <int>(new ModuloIntegerEqualityComparer(2));

            queue.Enqueue(1);
            queue.Enqueue(2);
            queue.Enqueue(3);
            queue.Enqueue(4);

            var result = queue.DequeueEquivalenceSet().ToList();

            Assert.That(result, Is.EquivalentTo(new[] { 1, 3 }));
        }
        public void DequeueEquivalenceSet_ThreeEquivalenceClassesWithTwoHashesThirdDequeue_ReturnsOnlyItemsFromThirdClass()
        {
            var queue = new FastEquivalenceQueue <int>(new ModuloIntegerEqualityComparer(3, 2));

            queue.Enqueue(1);
            queue.Enqueue(2);
            queue.Enqueue(3);
            queue.Enqueue(4);
            queue.Enqueue(5);
            queue.Enqueue(6);

            var a      = queue.DequeueEquivalenceSet();
            var b      = queue.DequeueEquivalenceSet();
            var result = queue.DequeueEquivalenceSet().ToList();

            Assert.That(result, Is.EquivalentTo(new[] { 3, 6 }));
        }
        /// <summary>
        /// Optimizes the specified input machine.
        /// </summary>
        /// <param name="inputMachine">The input machine. Must have transitions in a common order. Must be in format as generated by the <see cref="StateMachineBuilder"/> (no loops other than regular back-transitions within a single path).</param>
        /// <returns>State machine with all equivalent states merged into one state.</returns>
        public StateMachine OptimizeSortedMachine(StateMachine inputMachine)
        {
            var parentStateMap = this.BuildParentStateMap(inputMachine);
            var finalStates    = this.GetFinalStates(inputMachine);

            var transitionComparer = new TransitionNonrecursiveEqualityComparer();
            var queue = new FastEquivalenceQueue <State>(new TransitionCountStateEqualityComparer());

            var oldToNewMap = new Dictionary <State, State>();
            var waitingForBacktransitionConfirmationMap = new Dictionary <State, WaitingForConfirmationInfo>();
            var backtTransitionsByState = this.GetBackTransitionsByState(inputMachine);

            foreach (var finalState in finalStates)
            {
                queue.Enqueue(finalState);
            }


            //int i = 0;
            while (!queue.IsEmpty)
            {
                //i++;

                var currentSet = queue.DequeueEquivalenceSet().ToList();

                /*Console.WriteLine("=======================");
                 * Console.WriteLine(i + " T " + queue.Count + ") " + string.Join(", ", currentSet.Select(p => p)));*/

                // First state is the "template" for the equivalency class (all the other states in the group have to be equivalent
                // to this state to be in its group)
                var firstState = currentSet.First();

                foreach (var state in currentSet)
                {
                    //Console.WriteLine("Current: " + state);

                    // If the previous template state was kicked out due to not having all its children processed,
                    // try to use the next state as a template.
                    if (firstState == null)
                    {
                        firstState = state;
                    }

                    var currentStateBackTransition = backtTransitionsByState.GetValueOrDefault(state);
                    var firstStateBackTransition   = backtTransitionsByState.GetValueOrDefault(firstState);

                    // Were all children of this state already processed? (skip back-transitions)
                    if (!state.Transitions.Where(p => currentStateBackTransition != p).All(p => oldToNewMap.ContainsKey(p.TargetState)))
                    {
                        // Another state will have to become the template.
                        if (state == firstState)
                        {
                            firstState = null;
                        }

                        //Console.WriteLine("Requeueing because children not processed");

                        queue.Enqueue(state);
                        continue;
                    }

                    // By now, at least one state is still in the equivalency class (and is the template).
                    // The template is obviously auto-included in the group.
                    if (firstState != state)
                    {
                        if (firstState.Transitions.Count != state.Transitions.Count)
                        {
                            //Console.WriteLine("Requeueing because not same number of transitions as template");

                            queue.Enqueue(state);
                            continue;
                        }

                        // Do a rough check of the back-transition (we can discard obviously non-equivalent back-transitions)
                        if (
                            !transitionComparer.Equals(currentStateBackTransition, firstStateBackTransition) ||
                            ((currentStateBackTransition != null) != (firstStateBackTransition != null)))
                        {
                            //Console.WriteLine("Requeueing because preliminary backtransition check failed");

                            queue.Enqueue(state);
                            continue;
                        }

                        // Check if the children states of the current state (and transitions leading to them) match the template.
                        var transitionPairs = state.Transitions.Where(p => currentStateBackTransition != p).Zip(
                            firstState.Transitions.Where(p => backtTransitionsByState.GetValueOrDefault(firstState) != p),
                            (currentStateTransition, firstStateTransition) => new { currentStateTransition, firstStateTransition }
                            );
                        if (transitionPairs.Any(pair =>
                        {
                            //Console.WriteLine("Compare transition: first " + pair.firstStateTransition.SortingKey + " current" + pair.firstStateTransition.SortingKey);

                            // The transitions must match either way (whether there is backtransition going around current state or not).
                            if (!transitionComparer.Equals(pair.currentStateTransition, pair.firstStateTransition))
                            {
                                return(true);
                            }

                            var waitingForConfirmationFirstStateInfo = waitingForBacktransitionConfirmationMap.GetValueOrDefault(pair.firstStateTransition.TargetState);
                            var waitingForConfirmationCurrentStateInfo = waitingForBacktransitionConfirmationMap.GetValueOrDefault(pair.currentStateTransition.TargetState);

                            /*Console.WriteLine(
                             *  "Waiting template: first " +
                             *  (waitingForConfirmationFirstStateInfo != null ?
                             *      waitingForConfirmationFirstStateInfo.TemplateState.HeadShift.ToString() : "x") +
                             *  " current " +
                             *  (waitingForConfirmationCurrentStateInfo != null ?
                             *      waitingForConfirmationCurrentStateInfo.TemplateState.HeadShift.ToString() : "x"));*/

                            if (waitingForConfirmationFirstStateInfo == null && waitingForConfirmationCurrentStateInfo == null)
                            {
                                return(oldToNewMap[pair.firstStateTransition.TargetState] != oldToNewMap[pair.currentStateTransition.TargetState]);
                            }
                            else if (waitingForConfirmationFirstStateInfo == null || waitingForConfirmationCurrentStateInfo == null)
                            {
                                return(true);
                            }
                            else
                            {
                                // Equivalent template state in the waiting-for-conf info means the children were processed and that they are equivalent.
                                return(waitingForConfirmationFirstStateInfo.TemplateState != waitingForConfirmationCurrentStateInfo.TemplateState);
                            }
                        }
                                                //oldToNewMap[pair.firstStateTransition.TargetState] != oldToNewMap[pair.currentStateTransition.TargetState] /*&&
                                                //waitingForBacktransitionConfirmationMap.GetValueOrDefault().TemplateState
                                                ))
                        {
                            //Console.WriteLine("Requeueing because children (or transitions to children) don't match");

                            queue.Enqueue(state);
                            continue;
                        }
                    }

                    // Were children of this state waiting for backtransition confirmation?
                    // TODO: Check if children are waiting for the same state (opposite would mean a bug in Compiler)
                    var childrenWaitingForConfirmationInfo = state.Transitions
                                                             .Where(p => currentStateBackTransition != p && waitingForBacktransitionConfirmationMap.ContainsKey(p.TargetState))
                                                             .Select(p => p.TargetState)
                                                             .Select(childState => waitingForBacktransitionConfirmationMap[childState])
                                                             .ToList();


                    // If there is a backtransition loop going from this state to this state, consider the state to be a child of itself for purpose
                    // of back-transition handling (to avoid having to handle this as a special case later).
                    var hasSelfBacktransition = currentStateBackTransition != null && currentStateBackTransition.TargetState == state;
                    if (hasSelfBacktransition)
                    {
                        childrenWaitingForConfirmationInfo.Add(new WaitingForConfirmationInfo
                        {
                            TemplateState             = firstState,
                            ConfirmingState           = state,
                            StatesWithBacktransitions = new List <State> {
                                state
                            }
                        });
                    }

                    /*Console.WriteLine("Following waiting for confirmation infos were passed by children:");
                     * foreach (var waitingForConfirmationInfo in childrenWaitingForConfirmationInfo)
                     * {
                     *  Console.WriteLine("\t" + waitingForConfirmationInfo);
                     * }*/

                    var isWaitingBacktransitionTarget =
                        childrenWaitingForConfirmationInfo.Count > 0 &&
                        childrenWaitingForConfirmationInfo.First().ConfirmingState == state;

                    /*Console.WriteLine("Reuse template?");
                     * Console.WriteLine("\tIs NOT the template:" + (state != firstState));
                     * Console.WriteLine("\tHas no backtransition:" + (currentStateBackTransition == null) + " OR it is self-backtransition :" + hasSelfBacktransition);
                     * Console.WriteLine("\tChildren have no open backtransitions:" + (childrenWaitingForConfirmationInfo.Count == 0) + " OR It is the target :" + isWaitingBacktransitionTarget);*/

                    // Merge this state's subtree into the template state's subtree, if it matches following conditions
                    State newState;
                    if (
                        state != firstState &&                                                           // Always create a new subree for the template state.
                        (currentStateBackTransition == null || hasSelfBacktransition) &&                 // If the state has backtransition, it must its target.
                        (childrenWaitingForConfirmationInfo.Count == 0 || isWaitingBacktransitionTarget) // There are no backtransitions going around it
                        )
                    {
                        newState = oldToNewMap[firstState];

                        //Console.WriteLine("Reusing template");
                    }
                    else
                    {
                        newState             = new State();
                        newState.Transitions = state.Transitions.Where(p => p != currentStateBackTransition).Select(t =>
                        {
                            var newTransition         = t.Clone();
                            newTransition.TargetState = oldToNewMap[t.TargetState];
                            return(newTransition);
                        }).ToList();

                        //Console.WriteLine("Creating new state");
                    }

                    oldToNewMap[state] = newState;

                    if (currentStateBackTransition != null && currentStateBackTransition.TargetState != state)
                    {
                        waitingForBacktransitionConfirmationMap.Add(
                            state,
                            new WaitingForConfirmationInfo
                        {
                            ConfirmingState           = currentStateBackTransition.TargetState,
                            TemplateState             = firstState,
                            StatesWithBacktransitions = new List <State> {
                                state
                            }
                        });

                        //Console.WriteLine("wrote waiting for template " + firstState + " because has backtrans");
                    }

                    if (childrenWaitingForConfirmationInfo.Count > 0)
                    {
                        if (!isWaitingBacktransitionTarget && currentStateBackTransition == null /*!stateComparer.Equals(childrenWaitingForConfirmationInfo.First().ConfirmingState, state)*/) // TODO: Co tady?
                        {
                            // This is not the state these states are waiting for -> forward the waiting for confirmation info further
                            waitingForBacktransitionConfirmationMap.Add(
                                state,
                                new WaitingForConfirmationInfo
                            {
                                ConfirmingState           = childrenWaitingForConfirmationInfo.First().ConfirmingState,
                                TemplateState             = firstState,
                                StatesWithBacktransitions = childrenWaitingForConfirmationInfo.SelectMany(p => p.StatesWithBacktransitions).ToList()
                            });

                            //Console.WriteLine("wrote waiting for template " + firstState + " not which waiting for");
                        }
                        else
                        {
                            // This is the state the unconfirmed states are waiting for!
                            // Add back-transitions from these states to the current state.
                            foreach (var stateWaitingForBacktransition in childrenWaitingForConfirmationInfo.SelectMany(p => p.StatesWithBacktransitions))
                            {
                                var backTransitionOrigin = oldToNewMap[stateWaitingForBacktransition];
                                backTransitionOrigin.Transitions.Add(new AlwaysTransition
                                {
                                    TargetState = newState,
                                    HeadShift   = backTransitionOrigin == newState ? 1 : 0 // Do not loop in place
                                });
                            }

                            //Console.WriteLine("skipped forwarding waiting for template " + firstState + " , is backtrans target (backtrans created)");
                        }
                    }

                    foreach (var parentState in parentStateMap[state])
                    {
                        // Parent map also includes backtransition -> exclude them now.
                        var parentBackTransition = backtTransitionsByState.GetValueOrDefault(parentState);
                        if (parentBackTransition != null && parentBackTransition.TargetState == state)
                        {
                            continue;
                        }

                        //Console.WriteLine("Enqueued parent state " + parentState);

                        // Tady bude treba resit, aby se do fronty vrcholy nepridavaly vicekrat
                        queue.Enqueue(parentState);
                    }
                }
            }

            return(new StateMachine(oldToNewMap[inputMachine.EntryState]));
        }