/// <summary>
        /// Builds the product.
        /// </summary>
        private void BuildProduct()
        {
            var numAutomata = _statesList.Count;
            var origin      = new int[numAutomata];
            var destination = new int[numAutomata];

            _statesStack = new Stack <StatesTuple>();

            var initialState = new StatesTuple(origin, _bits, _tupleSize);

            _statesStack.Push(initialState);

            _validStates = new Dictionary <StatesTuple, bool>(StatesTupleComparator.GetInstance())
            {
                { initialState, false }
            };

            while (_statesStack.Count > 0)
            {
                _statesStack.Pop().Get(origin, _bits, _maxSize);

                for (var e = 0; e < _eventsUnion.Length; ++e)
                {
                    var nextEvent = false;
                    for (var i = 0; i < numAutomata; ++i)
                    {
                        if (_adjacencyList[i].HasEvent(origin[i], e))
                        {
                            destination[i] = _adjacencyList[i][origin[i], e];
                        }
                        else
                        {
                            nextEvent = true;
                            break;
                        }
                    }

                    if (nextEvent)
                    {
                        continue;
                    }
                    var nextState = new StatesTuple(destination, _bits, _tupleSize);

                    if (_validStates.ContainsKey(nextState))
                    {
                        continue;
                    }
                    _statesStack.Push(nextState);
                    _validStates.Add(nextState, false);
                }
            }

            Size = (ulong)_validStates.Count;
        }
        /// <summary>
        /// Finds the supervisor.
        /// </summary>
        /// <param name="nPlant">The n plant.</param>
        /// <param name="nonBlocking">if set to <c>true</c> [non blocking].</param>
        private void FindSupervisor(int nPlant, bool nonBlocking)
        {
            _numberOfRunningThreads = 0;
            _statesStack            = new Stack <StatesTuple>();
            _removeBadStates        = new Stack <bool>();

            _validStates = new Dictionary <StatesTuple, bool>(StatesTupleComparator.GetInstance());

            MakeReverseTransitions();

            var initialIndex = new StatesTuple(new int[_statesList.Count], _bits, _tupleSize);

            _statesStack.Push(initialIndex);
            _removeBadStates.Push(false);

            var vThreads = new Task[NumberOfThreads - 1];

            for (var i = 0; i < NumberOfThreads - 1; ++i)
            {
                vThreads[i] = Task.Factory.StartNew(() => FindStates(nPlant));
            }

            FindStates(nPlant);

            for (var i = 0; i < NumberOfThreads - 1; ++i)
            {
                vThreads[i].Wait();
            }

            foreach (var s in _validStates.Reverse())
            {
                if (s.Value)
                {
                    _validStates.Remove(s.Key);
                }
            }

            bool vNewBadStates;

            do
            {
                vNewBadStates = DepthFirstSearch(false, true);
                GC.Collect();
                if (nonBlocking)
                {
                    vNewBadStates |= RemoveBlockingStates(true);
                }
            } while (vNewBadStates);
        }
        /// <summary>
        /// Removes the blocking states.
        /// </summary>
        /// <param name="checkForBadStates">if set to <c>true</c> [check for bad states].</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        private bool RemoveBlockingStates(bool checkForBadStates = false)
        {
            MakeReverseTransitions();
            int n = _statesList.Count, i;

            _numberOfRunningThreads = 0;
            var threads = new Task[NumberOfThreads - 1];

            var markedStates = new List <int> [n];
            var pos          = new int[n];
            var statePos     = new int[n];

            pos[n - 1]   = -1;
            _statesStack = new Stack <StatesTuple>();

            var vNotCheckValidState = false;

            if (_validStates == null)
            {
                vNotCheckValidState = true;
                _validStates        = new Dictionary <StatesTuple, bool>(StatesTupleComparator.GetInstance());
            }

            for (i = 0; i < n; ++i)
            {
                markedStates[i] = _statesList[i].Where(st => st.IsMarked)
                                  .Select(st => Array.IndexOf(_statesList[i], st)).ToList();
            }

            while (true)
            {
                for (i = n - 1; i >= 0; --i)
                {
                    ++pos[i];
                    if (pos[i] < markedStates[i].Count())
                    {
                        break;
                    }
                    pos[i] = 0;
                }

                if (i < 0)
                {
                    break;
                }

                for (i = 0; i < n; ++i)
                {
                    statePos[i] = markedStates[i][pos[i]];
                }
                var tuple = new StatesTuple(statePos, _bits, _tupleSize);
                if (!_validStates.TryGetValue(tuple, out var vValue) && !vNotCheckValidState || vValue)
                {
                    continue;
                }
                _validStates[tuple] = true;
                _statesStack.Push(tuple);
            }

            markedStates = null;
            Size         = 0;

            for (i = 0; i < NumberOfThreads - 1; ++i)
            {
                threads[i] = Task.Factory.StartNew(() => InverseSearchThread(vNotCheckValidState));
            }

            InverseSearchThread(vNotCheckValidState);

            for (i = 0; i < NumberOfThreads - 1; ++i)
            {
                threads[i].Wait();
            }

            _statesStack = null;
            var v_newBadStates            = false;
            var uncontrollableEventsCount = UncontrollableEvents.Count();

            if (checkForBadStates)
            {
                var removedStates = new List <StatesTuple>();
                foreach (var p in _validStates)
                {
                    if (!p.Value)
                    {
                        removedStates.Add(p.Key);
                    }
                }
                foreach (var p in removedStates)
                {
                    v_newBadStates |= RemoveBadStates(p, uncontrollableEventsCount, true);
                }
            }

            _reverseTransitionsList = null;

            foreach (var p in _validStates.Reverse())
            {
                if (!p.Value)
                {
                    _validStates.Remove(p.Key);
                }
                else
                {
                    _validStates[p.Key] = false;
                }
            }

            GC.Collect();
            return(v_newBadStates);
        }
        /// <summary>
        /// Controllabilities the and disabled events.
        /// </summary>
        /// <param name="plants">The plants.</param>
        /// <param name="getDisabledEvents">if set to <c>true</c> [get disabled events].</param>
        /// <returns>Tuple&lt;Controllability, Dictionary&lt;AbstractState, List&lt;AbstractEvent&gt;&gt;&gt;.</returns>
        /// <exception cref="Exception">Plant is invalid.</exception>
        public Tuple <Controllability, Dictionary <AbstractState, List <AbstractEvent> > > ControllabilityAndDisabledEvents(
            IEnumerable <DFA> plants, bool getDisabledEvents = true)
        {
            var           G               = ParallelComposition(plants, false);
            var           nG              = G._statesList.Count;
            var           nS              = _statesList.Count;
            var           pos2            = new int[nS];
            var           evs             = _eventsUnion.Union(G._eventsUnion).OrderBy(i => i.Controllability).ToArray();
            var           numUncontEvs    = 0;
            var           stackG          = new Stack <int[]>();
            var           stackS          = new Stack <int[]>();
            var           filteredStates  = _validStates != null;
            var           GfilteredStates = G._validStates != null;
            var           evsMapG         = new int[evs.Length];
            var           evsMapS         = new int[evs.Length];
            var           GTuple          = new StatesTuple(G._tupleSize);
            var           STuple          = new StatesTuple(_tupleSize);
            var           controllable    = UltraDES.Controllability.Controllable;
            var           disabled        = new Dictionary <AbstractState, List <AbstractEvent> >((int)Size);
            AbstractState currentState    = null;

            if (!filteredStates)
            {
                _validStates = new Dictionary <StatesTuple, bool>((int)Size, StatesTupleComparator.GetInstance());
            }

            for (var e = 0; e < evs.Length; ++e)
            {
                if (!evs[e].IsControllable)
                {
                    ++numUncontEvs;
                }
                evsMapG[e] = Array.IndexOf(G._eventsUnion, evs[e]);
                evsMapS[e] = Array.IndexOf(_eventsUnion, evs[e]);
            }

            stackG.Push(new int[nG]);
            STuple.Set(pos2, _bits);
            if (!filteredStates || _validStates.ContainsKey(STuple))
            {
                stackS.Push(pos2);
                if (filteredStates)
                {
                    _validStates[STuple] = true;
                }
                else
                {
                    _validStates.Add(new StatesTuple(pos2, _bits, _tupleSize), true);
                }
            }

            var stopSearch = false;

            while (stackS.Count > 0)
            {
                var pos1 = stackG.Pop();
                pos2 = stackS.Pop();

                if (getDisabledEvents)
                {
                    currentState = ComposeState(pos2);
                    disabled.Add(currentState, new List <AbstractEvent>());
                }

                for (var e = 0; e < evs.Length; ++e)
                {
                    var t        = CheckState(G, nG, nS, pos1, pos2, evsMapG[e], evsMapS[e]);
                    var GHasNext = t.Item1;
                    var SHasNext = t.Item2;

                    if (GHasNext && GfilteredStates)
                    {
                        GTuple.Set(t.Item3, G._bits);
                        GHasNext = G._validStates.ContainsKey(GTuple);
                    }

                    if (SHasNext && filteredStates)
                    {
                        STuple.Set(t.Item4, _bits);
                        SHasNext = _validStates.ContainsKey(STuple);
                    }

                    if (!GHasNext && SHasNext)
                    {
                        throw new Exception("Plant is invalid.");
                    }

                    if (GHasNext && !SHasNext)
                    {
                        if (e < numUncontEvs)
                        {
                            controllable = UltraDES.Controllability.Uncontrollable;
                            if (!getDisabledEvents)
                            {
                                stopSearch = true;
                                break;
                            }
                        }

                        if (getDisabledEvents)
                        {
                            disabled[currentState].Add(evs[e]);
                        }
                    }
                    else if (GHasNext && SHasNext)
                    {
                        if (filteredStates)
                        {
                            if (!_validStates[STuple])
                            {
                                _validStates[STuple] = true;
                                stackG.Push(t.Item3);
                                stackS.Push(t.Item4);
                            }
                        }
                        else
                        {
                            STuple.Set(t.Item4, _bits);
                            if (!_validStates.ContainsKey(STuple))
                            {
                                _validStates.Add(new StatesTuple(t.Item4, _bits, _tupleSize), true);
                                stackG.Push(t.Item3);
                                stackS.Push(t.Item4);
                            }
                        }
                    }
                }

                if (stopSearch)
                {
                    break;
                }
            }

            if (filteredStates)
            {
                foreach (var t in _validStates.Reverse())
                {
                    _validStates[t.Key] = false;
                }
            }
            else
            {
                _validStates = null;
            }

            return(new Tuple <Controllability, Dictionary <AbstractState, List <AbstractEvent> > >(controllable, disabled));
        }