public void CreateQuiverInPlaneAnalysisResults_IQPAnalysisResultsOfTVertex_SetsMainResultCorrectly()
        {
            var defaultMaximalReps         = new Dictionary <int, IEnumerable <Path <int> > >();
            var defaultNakayamaPermutation = new NakayamaPermutation <int>(new Dictionary <int, int>());
            var defaultLongestPath         = new Path <int>(startingPoint: 1);

            var qpAnalysisResults = CreateQPAnalysisResults(
                QPAnalysisMainResults.Success,
                defaultMaximalReps,
                defaultNakayamaPermutation,
                defaultLongestPath);

            var results = AnalysisResultsFactory.CreateQuiverInPlaneAnalysisResults(qpAnalysisResults);

            Assert.That(results.MainResults, Is.EqualTo(QuiverInPlaneAnalysisMainResults.Success));

            qpAnalysisResults = CreateQPAnalysisResults(
                QPAnalysisMainResults.Success,
                defaultMaximalReps,
                defaultNakayamaPermutation,
                defaultLongestPath);
            results = AnalysisResultsFactory.CreateQuiverInPlaneAnalysisResults(qpAnalysisResults);
            Assert.That(results.MainResults, Is.EqualTo(QuiverInPlaneAnalysisMainResults.Success));
            Assert.That(results.MainResults.IndicatesSelfInjectivity());

            qpAnalysisResults = CreateQPAnalysisResults(
                QPAnalysisMainResults.Aborted,
                null,
                null,
                defaultLongestPath);
            results = AnalysisResultsFactory.CreateQuiverInPlaneAnalysisResults(qpAnalysisResults);
            Assert.That(results.MainResults, Is.EqualTo(QuiverInPlaneAnalysisMainResults.QPAnalysisAborted));

            qpAnalysisResults = CreateQPAnalysisResults(
                QPAnalysisMainResults.Cancelled,
                null,
                null,
                defaultLongestPath);
            results = AnalysisResultsFactory.CreateQuiverInPlaneAnalysisResults(qpAnalysisResults);
            Assert.That(results.MainResults, Is.EqualTo(QuiverInPlaneAnalysisMainResults.QPAnalysisCancelled));

            qpAnalysisResults = CreateQPAnalysisResults(
                QPAnalysisMainResults.NotCancellative,
                null,
                null,
                defaultLongestPath);
            results = AnalysisResultsFactory.CreateQuiverInPlaneAnalysisResults(qpAnalysisResults);
            Assert.That(results.MainResults, Is.EqualTo(QuiverInPlaneAnalysisMainResults.QPIsNotCancellative));

            QPAnalysisResults <int> CreateQPAnalysisResults(
                QPAnalysisMainResults mainResult,
                Dictionary <int, IEnumerable <Path <int> > > maximalPathRepresentatives,
                NakayamaPermutation <int> nakayamaPermutation,
                Path <int> longestPathEncountered)
            {
                return(new QPAnalysisResults <int>(mainResult, maximalPathRepresentatives, nakayamaPermutation, longestPathEncountered));
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="AnalysisResults{TVertex, TMainResult}"/> class.
        /// </summary>
        /// <param name="mainResult">The main result.</param>
        /// <param name="maximalPathRepresentatives">A dictionary mapping every vertex of the
        /// quiver to a collection of representatives of all maximal non-zero equivalence classes
        /// of paths starting at the vertex, or <see langword="null"/> depending on whether the
        /// analysis was successful.</param>
        /// <param name="nakayamaPermutation">The Nakayama permutation for the analyzed
        /// &quot;gadget&quot; or <see langword="null"/>, depending on whether the analysis was
        /// successful and the gadget has a Nakayama permutation.</param>
        /// <param name="longestPathEncountered">A path of maximal length of the paths encountered
        /// during the analysis, or <see langword="null"/> if no path was encountered (e.g., if a
        /// quiver-in-plane analysis is done on a non-plane quiver, or if the quiver is empty).</param>
        protected AnalysisResults(
            TMainResult mainResult,
            IReadOnlyDictionary <TVertex, IEnumerable <Path <TVertex> > > maximalPathRepresentatives,
            NakayamaPermutation <TVertex> nakayamaPermutation,
            Path <TVertex> longestPathEncountered)
        {
            MainResults = mainResult;
            MaximalPathRepresentatives = maximalPathRepresentatives;
            NakayamaPermutation        = nakayamaPermutation;

            LongestPathEncountered = longestPathEncountered;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="SemimonomialUnboundQuiverAnalysisResults{TVertex}"/> class.
        /// </summary>
        /// <param name="mainResults">The main results.</param>
        /// <param name="maximalPathRepresentatives">A dictionary mapping every vertex of the
        /// quiver to a collection of representatives of all maximal non-zero equivalence classes
        /// of paths starting at the vertex, or <see langword="null"/> depending on whether the
        /// analysis was successful.</param>
        /// <param name="nakayamaPermutation">The Nakayama permutation for the analyzed
        /// semimonomial unbound quiver or
        /// <see langword="null"/>, depending on whether the analysis was successful and the
        /// semimonomial unbound quiver has a Nakayama permutation.</param>
        /// <param name="longestPathEncountered">A path of maximal length of the paths encountered
        /// during the analysis, or <see langword="null"/> if no path was encountered during the
        /// analysis (i.e., if the quiver was empty).</param>
        public SemimonomialUnboundQuiverAnalysisResults(
            SemimonomialUnboundQuiverAnalysisMainResults mainResults,
            IReadOnlyDictionary <TVertex, IEnumerable <Path <TVertex> > > maximalPathRepresentatives,
            NakayamaPermutation <TVertex> nakayamaPermutation,
            Path <TVertex> longestPathEncountered)
            : base(mainResults, maximalPathRepresentatives, nakayamaPermutation, longestPathEncountered)
        {
            if (mainResults.HasFlag(SemimonomialUnboundQuiverAnalysisMainResults.Success) && maximalPathRepresentatives is null)
            {
                throw new ArgumentNullException(nameof(maximalPathRepresentatives));
            }

            if (mainResults.IndicatesSelfInjectivity() && nakayamaPermutation is null)
            {
                throw new ArgumentNullException(nameof(nakayamaPermutation));
            }
        }
        /// <summary>
        /// Analyzes a semimonomial unbound quiver with a default computer of maximal nonzero
        /// equivalence class representatives.
        /// </summary>
        /// <typeparam name="TVertex">The type of the vertices of the quiver.</typeparam>
        /// <param name="unboundQuiver">The unbound quiver.</param>
        /// <param name="settings">The settings for the analysis.</param>
        /// <param name="computer">A computer of maximal nonzero equivalence class representatives.</param>
        /// <returns>The results of the analysis.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="unboundQuiver"/> is
        /// <see langword="null"/>, or <paramref name="settings"/> is <see langword="null"/>,
        /// or <paramref name="computer"/> is <see langword="null"/>.</exception>
        /// <exception cref="NotSupportedException">The semimonomial ideal of
        /// <paramref name="unboundQuiver"/> has two distinct non-monomial generators
        /// <c>p1 - q1</c> and <c>p2 - q2</c> where <c>p1, q1, p2, q2</c> are not all distinct.</exception>
        /// <exception cref="ArgumentException">The semimonomial ideal of
        /// <paramref name="unboundQuiver"/> has a non-monomial generator <c>p - q</c> where the
        /// paths <c>p</c> and <c>q</c> do not have the same endpoints.</exception>
        public ISemimonomialUnboundQuiverAnalysisResults <TVertex> Analyze <TVertex>(
            SemimonomialUnboundQuiver <TVertex> unboundQuiver,
            SemimonomialUnboundQuiverAnalysisSettings settings,
            IMaximalNonzeroEquivalenceClassRepresentativeComputer computer)
            where TVertex : IEquatable <TVertex>, IComparable <TVertex>
        {
            if (unboundQuiver is null)
            {
                throw new ArgumentNullException(nameof(unboundQuiver));
            }
            if (settings is null)
            {
                throw new ArgumentNullException(nameof(settings));
            }
            if (computer is null)
            {
                throw new ArgumentNullException(nameof(computer));
            }

            var computationSettings           = AnalysisSettingsFactory.CreateMaximalNonzeroEquivalenceClassRepresentativeComputationSettings(settings);
            var transformationRuleTreeCreator = new TransformationRuleTreeCreator();
            var transformationRuleTree        = transformationRuleTreeCreator.CreateTransformationRuleTree(unboundQuiver);

            var maximalPathRepresentatives = new Dictionary <TVertex, IEnumerable <Path <TVertex> > >();
            var nakayamaPermutationDict    = new Dictionary <TVertex, TVertex>();

            var            mainResults = SemimonomialUnboundQuiverAnalysisMainResults.None;
            Path <TVertex> longestPath = null;

            foreach (var startingVertex in unboundQuiver.Quiver.Vertices)
            {
                var representativesResult = computer.ComputeMaximalNonzeroEquivalenceClassRepresentativesStartingAt(unboundQuiver.Quiver, startingVertex, transformationRuleTree, computationSettings);
                if (longestPath is null || representativesResult.LongestPathEncountered.Length > longestPath.Length)
                {
                    longestPath = representativesResult.LongestPathEncountered;
                }

                if (representativesResult.WeakCancellativityFailureDetected)
                {
                    mainResults |= SemimonomialUnboundQuiverAnalysisMainResults.NotWeaklyCancellative;
                }
                if (representativesResult.CancellativityFailureDetected)
                {
                    mainResults |= SemimonomialUnboundQuiverAnalysisMainResults.NotCancellative;
                }
                if (representativesResult.TooLongPathEncountered)
                {
                    mainResults |= SemimonomialUnboundQuiverAnalysisMainResults.Aborted;
                }

                if ((representativesResult.WeakCancellativityFailureDetected && settings.TerminateEarlyIfWeakCancellativityFails) ||
                    (representativesResult.CancellativityFailureDetected && settings.TerminateEarlyIfCancellativityFails) ||
                    (representativesResult.TooLongPathEncountered))
                {
                    return(new SemimonomialUnboundQuiverAnalysisResults <TVertex>(mainResults, null, null, longestPath));
                }

                var maximalNonzeroEquivalenceClassRepresentatives = representativesResult.MaximalNonzeroEquivalenceClassRepresentatives;
                maximalPathRepresentatives[startingVertex] = maximalNonzeroEquivalenceClassRepresentatives;
                int numMaximalNonzeroPathClasses = maximalNonzeroEquivalenceClassRepresentatives.Count();
                Debug.Assert(numMaximalNonzeroPathClasses > 0, "Analysis with starting vertex found 0 maximal nonzero classes.");
                if (numMaximalNonzeroPathClasses > 1)
                {
                    mainResults |= SemimonomialUnboundQuiverAnalysisMainResults.MultipleMaximalNonzeroClasses;
                    if (settings.TerminateEarlyOnMultiDimensionalSocle)
                    {
                        return(new SemimonomialUnboundQuiverAnalysisResults <TVertex>(mainResults, null, null, longestPath));
                    }
                }
                else
                {
                    var endingPoint = maximalNonzeroEquivalenceClassRepresentatives.Single().EndingPoint;
                    // Check if the tentative Nakayama permutation fails to be injective.
                    if (nakayamaPermutationDict.ContainsValue(endingPoint))
                    {
                        mainResults |= SemimonomialUnboundQuiverAnalysisMainResults.NonInjectiveTentativeNakayamaPermutation;
                        if (settings.TerminateEarlyIfNakayamaPermutationFails)
                        {
                            return(new SemimonomialUnboundQuiverAnalysisResults <TVertex>(mainResults, null, null, longestPath));
                        }
                    }
                    else
                    {
                        nakayamaPermutationDict[startingVertex] = endingPoint;
                    }
                }
            }

            mainResults |= SemimonomialUnboundQuiverAnalysisMainResults.Success;
            NakayamaPermutation <TVertex> nakayamaPermutation = null;

            if (mainResults.IndicatesSelfInjectivity())
            {
                nakayamaPermutation = new NakayamaPermutation <TVertex>(nakayamaPermutationDict);
            }

            return(new SemimonomialUnboundQuiverAnalysisResults <TVertex>(mainResults, maximalPathRepresentatives, nakayamaPermutation, longestPath));
        }