/// <summary>
        /// Obtém a matriz de compatiblidade através dos custos.
        /// </summary>
        /// <param name="costsMatrix">A matriz dos custos.</param>
        /// <param name="sortedElements">
        /// O contentor para o conjunto de elementos ordenados por custo.
        /// </param>
        /// <returns>
        /// A matriz de compatibilidade ordenada de acordo como os elementos se encontram na matriz.
        /// </returns>
        private BitArraySymmetricMatrix GetCompatibilityMatrix(
            ILongSparseMathMatrix <CostsType> costsMatrix,
            InsertionSortedCollection <CoordsElement> sortedElements)
        {
            // Efectua a contagem de elementos atribuídos na matriz.
            var elementsNumber = 0;

            foreach (var line in costsMatrix.GetLines())
            {
                foreach (var column in line.Value)
                {
                    ++elementsNumber;
                }
            }

            var result           = new BitArraySymmetricMatrix(elementsNumber, elementsNumber, false);
            var processedColumns = new List <CoordsElement> [costsMatrix.GetLength(0)];

            for (int i = 0; i < processedColumns.Length; ++i)
            {
                processedColumns[i] = new List <CoordsElement>();
            }

            var currentIndex = 0;

            foreach (var line in costsMatrix.GetLines())
            {
                foreach (var column in line.Value)
                {
                    if (column.Key != line.Key)
                    {
                        var element = new CoordsElement(line.Key, column.Key, currentIndex, column.Value);
                        sortedElements.Add(element);
                        var processedColumn = processedColumns[column.Key];
                        foreach (var processed in processedColumn)
                        {
                            result[processed.CompatibilityIndex, currentIndex] = true;
                        }

                        processedColumn.Add(element);

                        // Processa o pivor
                        processedColumn = processedColumns[line.Key];
                        foreach (var processed in processedColumn)
                        {
                            result[processed.CompatibilityIndex, currentIndex] = true;
                        }

                        ++currentIndex;
                    }
                }
            }


            return(result);
        }
        /// <summary>
        /// Permite obter a próxima variável não compatível associada à matriz de compatibilidade.
        /// </summary>
        /// <param name="last">A última variável a ser escolhida.</param>
        /// <param name="board">A intersecção de todas as linhas de compatibilidade.</param>
        /// <param name="compatibility">A matriz de compatibilidade.</param>
        /// <returns>O índice da próxima variável e -1 caso não seja possível obtê-lo.</returns>
        private int GetNextVariableIndex(
            int last,
            InsertionSortedCollection <CoordsElement> orderedCosts,
            BitArray board,
            BitArraySymmetricMatrix compatibility)
        {
            var result = -1;
            var length = compatibility.GetLength(0);

            for (int i = last + 1; i < length; ++i)
            {
                var lineIndex       = orderedCosts[i].CompatibilityIndex;
                var isNotCompatible = true;
                for (int j = 0; j < i; ++j)
                {
                    var columnIndex       = orderedCosts[j].CompatibilityIndex;
                    var compatibilityItem = compatibility[lineIndex, columnIndex];
                    if (compatibilityItem && board[columnIndex])
                    {
                        isNotCompatible = false;
                        j = i;
                    }
                }

                if (isNotCompatible)
                {
                    for (int j = i + 1; j < length; ++j)
                    {
                        var columnIndex       = orderedCosts[j].CompatibilityIndex;
                        var compatibilityItem = compatibility[lineIndex, columnIndex];
                        if (compatibilityItem && board[j])
                        {
                            isNotCompatible = false;
                            j = length;
                        }
                    }

                    if (isNotCompatible)
                    {
                        result = i;
                        board[orderedCosts[i].CompatibilityIndex] = true;
                        i = length;
                    }
                }
            }

            return(result);
        }
        /// <summary>
        /// Inicializa todas as variáveis estabelecendo uma solução inicial.
        /// </summary>
        /// <param name="affectations">A matriz das afectações.</param>
        /// <param name="results">O vector que contém os resultados.</param>
        /// <returns>A solução inicial.</returns>
        /// <exception cref="ArgumentNullException">Se as afectações forem nulas.</exception>
        /// <exception cref="MathematicsException">Se não existirem elementos para afectar.</exception>
        public InsertionSortedCollection <int>[] Init(
            int[][] affectations,
            int[] results)
        {
            if (affectations == null)
            {
                throw new ArgumentNullException("affectations");
            }

            if (affectations.Length == 0)
            {
                throw new MathematicsException("There's no elements to affect.");
            }

            results = new int[affectations.Length];
            this.ClearResults(results);
            var innerAffectations = new InsertionSortedCollection <int> [affectations.Length];

            for (int i = 0; i < affectations.Length; ++i)
            {
                if (affectations[i] == null)
                {
                    throw new MathematicsException(string.Format(
                                                       "Element {0} has no positions to be affected.", i));
                }

                if (affectations[i].Length == 0)
                {
                    throw new MathematicsException(string.Format(
                                                       "Element {0} has no positions to be affected.", i));
                }

                innerAffectations[i] = new InsertionSortedCollection <int>(Comparer <int> .Default, true);
                foreach (var item in affectations[i])
                {
                    innerAffectations[i].Add(item);
                }
            }

            return(innerAffectations);
        }
        /// <summary>
        /// Corre o algoritmo.
        /// </summary>
        /// <returns>O resultado da execução.</returns>
        /// <param name="affectations">A lista de correspondências possíveis.</param>
        public HungarianMatchingOutput Run(int[][] affectations)
        {
            var results               = default(int[]);
            var innerAffectations     = this.Init(affectations, results);
            var maximumDomainAffected = default(int[]);
            var maximumTargetAffected = default(int[]);
            var hasSolution           = false;
            InsertionSortedCollection <int> domainSubset = new InsertionSortedCollection <int>(
                Comparer <int> .Default,
                true);
            InsertionSortedCollection <int> targetSubset = new InsertionSortedCollection <int>(
                Comparer <int> .Default,
                true);
            InsertionSortedCollection <int> neighboursSubset = new InsertionSortedCollection <int>(
                Comparer <int> .Default,
                true);
            List <Tuple <int, int> > alternatingPath = new List <Tuple <int, int> >();
            int domainFree     = -1;
            int found          = -1;
            int targetUncommon = -1;
            int state          = 0;

            while (state != -1)
            {
                switch (state)
                {
                case 0:
                    for (int i = 0; i < innerAffectations.Length; ++i)
                    {
                        if (innerAffectations[i].Count != 0)
                        {
                            int temp = innerAffectations[i].First;
                            results[i] = temp;
                            domainSubset.Add(i);
                            i = innerAffectations.Length;      // Termina o ciclo de forma elegante.
                        }
                    }

                    state = 1;
                    break;

                case 1:
                    targetSubset.Clear();
                    domainSubset.Clear();
                    neighboursSubset.Clear();
                    alternatingPath.Clear();
                    domainFree = -1;
                    for (int i = 0; i < results.Length; ++i)
                    {
                        if (results[i] == -1)
                        {
                            domainFree = i;
                            domainSubset.Add(domainFree);
                            i = results.Length;
                        }
                    }

                    // Uma solução existe.
                    if (domainFree == -1)
                    {
                        domainSubset.Clear();
                        neighboursSubset.Clear();
                        for (int i = 0; i < innerAffectations.Length; ++i)
                        {
                            domainSubset.Add(i);
                        }

                        foreach (var item in domainSubset)
                        {
                            foreach (var possibleAffectations in innerAffectations[item])
                            {
                                neighboursSubset.Add(possibleAffectations);
                            }
                        }

                        maximumDomainAffected = domainSubset.ToArray();
                        maximumTargetAffected = neighboursSubset.ToArray();
                        hasSolution           = true;
                        state = -1;
                    }
                    else
                    {
                        state = 2;
                    }

                    break;

                case 2:
                    foreach (var item in domainSubset)
                    {
                        foreach (var possibleAffectations in innerAffectations[item])
                        {
                            neighboursSubset.Add(possibleAffectations);
                        }
                    }

                    targetUncommon = -1;
                    if (!neighboursSubset.TryFindValueNotIn(targetSubset, out targetUncommon))
                    {
                        maximumDomainAffected = domainSubset.ToArray();
                        maximumTargetAffected = neighboursSubset.ToArray();
                        state = -1;
                    }
                    else
                    {
                        found = -1;
                        for (int i = 0; i < results.Length; ++i)
                        {
                            if (results[i] == targetUncommon)
                            {
                                found = i;
                                domainSubset.Add(found);
                                targetSubset.Add(targetUncommon);
                                i = results.Length;
                            }
                        }

                        if (found == -1)
                        {
                            results[domainFree] = targetUncommon;
                            state = 1;
                        }
                        else
                        {
                            alternatingPath.Add(new Tuple <int, int>(domainFree, targetUncommon));
                            state = 3;
                        }
                    }

                    break;

                case 3:
                    foreach (var item in domainSubset)
                    {
                        foreach (var possibleAffectations in innerAffectations[item])
                        {
                            neighboursSubset.Add(possibleAffectations);
                        }
                    }

                    targetUncommon = -1;
                    if (!neighboursSubset.TryFindValueNotIn(targetSubset, out targetUncommon))
                    {
                        maximumDomainAffected = domainSubset.ToArray();
                        maximumTargetAffected = neighboursSubset.ToArray();
                        state = -1;
                    }
                    else
                    {
                        alternatingPath.Add(new Tuple <int, int>(found, targetUncommon));

                        found = -1;
                        for (int i = 0; i < results.Length; ++i)
                        {
                            if (results[i] == targetUncommon)
                            {
                                found = i;
                                domainSubset.Add(found);
                                targetSubset.Add(targetUncommon);
                                i = results.Length;
                            }
                        }

                        if (found == -1)
                        {
                            foreach (var pair in alternatingPath)
                            {
                                results[pair.Item1] = pair.Item2;
                            }

                            state = 1;
                        }
                    }

                    break;
                }
            }

            return(new HungarianMatchingOutput(
                       results,
                       maximumDomainAffected,
                       maximumTargetAffected,
                       hasSolution));
        }
        /// <summary>
        /// Implementa o algoritmo de compatibilidade sobre um conjunto de componentes conexas.
        /// </summary>
        /// <param name="mediansNumber">O número de medianas a serem escolhidas.</param>
        /// <param name="costsMatrix">A matriz de custos.</param>
        /// <returns>O resultado da execução do algoritmo.</returns>
        public GreedyAlgSolution <CostsType>[] Run(int mediansNumber, List <ILongSparseMathMatrix <CostsType> > costsMatrix)
        {
            if (mediansNumber < costsMatrix.Count)
            {
                throw new ArgumentException(
                          "The number of medians to choose must be at least equal to the number of components.");
            }
            if (costsMatrix == null)
            {
                throw new ArgumentNullException("costsMatrix");
            }
            else if (costsMatrix.Count == 0)
            {
                throw new ArgumentException("No costs matrix was provided.");
            }
            else
            {
                var nodesNumber = this.CountNodes(costsMatrix);
                List <InsertionSortedCollection <CoordsElement> > sortedCosts =
                    new List <InsertionSortedCollection <CoordsElement> >();
                List <BitArraySymmetricMatrix> compatibilityMatrices = new List <BitArraySymmetricMatrix>();
                for (int i = 0; i < costsMatrix.Count; ++i)
                {
                    var costsComparer       = new CoordsElementComparer(this.costsComparer);
                    var insertedSorted      = new InsertionSortedCollection <CoordsElement>(costsComparer, false);
                    var compatibilityMatrix = this.GetCompatibilityMatrix(costsMatrix[i], insertedSorted);
                    compatibilityMatrices.Add(compatibilityMatrix);
                    sortedCosts.Add(insertedSorted);
                }

                var chosenNumber   = nodesNumber - mediansNumber;
                var chosenIndices  = new List <CoordsElement> [costsMatrix.Count];
                var currentBoards  = new BitArray[costsMatrix.Count];
                var currentIndices = new int[costsMatrix.Count];

                // Obtém as primeiras variáveis
                for (int i = 0; i < compatibilityMatrices.Count; ++i)
                {
                    var currentCompatibilityMatrix = compatibilityMatrices[i];
                    var matrixLines = currentCompatibilityMatrix.GetLength(0);
                    var array       = new BitArray(matrixLines);
                    chosenIndices[i]  = new List <CoordsElement>();
                    currentBoards[i]  = array;
                    currentIndices[i] = 0;
                    array[sortedCosts[i][0].CompatibilityIndex] = true;
                }

                var resultCosts = new CostsType[costsMatrix.Count];
                for (int i = 0; i < resultCosts.Length; ++i)
                {
                    resultCosts[i] = this.costsRing.AdditiveUnity;
                }

                for (int i = 0; i < chosenNumber; ++i)
                {
                    var j     = 0;
                    var found = currentIndices[j++];
                    while (j < currentIndices.Length && found == -1)
                    {
                        found = currentIndices[j++];
                    }

                    if (found == -1)
                    {
                        return(this.GetSolution(resultCosts, chosenIndices, costsMatrix));
                    }
                    else
                    {
                        var foundIndex        = j - 1;
                        var currentSorteCosts = sortedCosts[foundIndex];
                        var minCost           = currentSorteCosts[found].Value;
                        while (j < currentIndices.Length)
                        {
                            var currentIndex = currentIndices[j];
                            var currentCost  = currentSorteCosts[currentIndex].Value;
                            if (this.costsComparer.Compare(currentCost, minCost) < 0)
                            {
                                foundIndex = j;
                                found      = currentIndex;
                                minCost    = currentCost;
                            }

                            ++j;
                        }

                        // Actualiza os valores.
                        chosenIndices[foundIndex].Add(currentSorteCosts[found]);
                        resultCosts[foundIndex] = this.costsRing.Add(resultCosts[foundIndex], minCost);
                        var currentCompatibility = compatibilityMatrices[foundIndex];
                        var currentBoard         = currentBoards[foundIndex];
                        currentIndices[foundIndex] = this.GetNextVariableIndex(
                            found,
                            sortedCosts[foundIndex],
                            currentBoard,
                            currentCompatibility);
                    }
                }

                return(this.GetSolution(resultCosts, chosenIndices, costsMatrix));
            }
        }