/// <summary>
        /// Calculate the number of threads used when running a sieve.
        /// </summary>
        /// <param name="sieve">The sieve that was run.</param>
        /// <param name="settings">The run settings.</param>
        /// <returns>Returns a tuple of external threads used (each one running one sieve) and internal threads used (how many threads each sieve uses).</returns>
        private static (int thraeds, int pThreads) GetThreadCount(ISieveRunner sieve, SettingsV2 settings)
        {
            int threads = 1;

            if (settings.MultiThreaded)
            {
                threads = settings.ThreadCount == 0 ? Environment.ProcessorCount : settings.ThreadCount;
            }

            int pThreads = 1;

            if (sieve.IsParallel)
            {
                pThreads = settings.PThreadCount == 0 ? Environment.ProcessorCount : settings.PThreadCount;
            }

            return(threads, pThreads);
        }
        /// <summary>
        /// Print the final output to the console.
        /// </summary>
        /// <param name="sieve">The sieve that was run.</param>
        /// <param name="passes">How many times it was run.</param>
        /// <param name="watch">The stopwatch used to time it.</param>
        /// <param name="settings">The settings that were in effect.</param>
        private static void PrintResults(ISieveRunner sieve, int passes, Stopwatch watch, SettingsV2 settings)
        {
            if (settings.Verbose)
            {
                string listing = sieve.GetFoundPrimes()
                                 .Select(a => a.ToString())
                                 .Aggregate((a, b) => $"{a}, {b}");

                Console.WriteLine(listing);
                Console.WriteLine();
            }

            (int threads, int pThreads) = GetThreadCount(sieve, settings);

            Console.WriteLine(GetLongOutputDescription(sieve, passes, watch, threads, pThreads));

            Console.WriteLine(GetCompactOutputDescription(sieve, passes, watch, threads, pThreads));
        }
        /// <summary>
        /// Get the compact output of the sieve run.
        /// </summary>
        private static string GetCompactOutputDescription(ISieveRunner sieve, int passes, Stopwatch watch, int threads, int pThreads)
        {
            // official syntax:
            // <label>;<pass_count>;<runtime>;<num_threads>;<tags (if any)>

            StringBuilder sb = new();

            // Fields:
            sb.Append($"kinematics_{sieve.Name};");
            sb.Append($"{passes};");
            sb.AppendFormat("{0:G6};", watch.Elapsed.TotalSeconds);
            sb.Append($"{threads * pThreads};");

            // Tags:
            sb.Append($"algorithm={sieve.AlgorithmType},");
            sb.Append($"faithful={(sieve.IsFaithful ? "yes" : "no")},");
            sb.Append($"bits={sieve.BitsPerPrime}");
            sb.AppendLine();

            return(sb.ToString());
        }
        /// <summary>
        /// Get the long form output of the sieve run.
        /// </summary>
        private static string GetLongOutputDescription(ISieveRunner sieve, int passes, Stopwatch watch, int threads, int pThreads)
        {
            List <string> results = new();

            results.Add($"Passes: {passes}");
            results.Add($"Time: {watch.Elapsed.TotalSeconds:G6} s");
            results.Add($"{((double)watch.ElapsedMilliseconds / passes):F6} ms/loop");
            results.Add($"Sieve Size: {sieve.SieveSize}");
            results.Add($"Thread Count: {threads}");
            if (sieve.IsParallel)
            {
                results.Add($"Parallel Thread Count: {pThreads}");
            }
            results.Add($"Primes: {sieve.CountPrimes()}");
            results.Add($"Valid: {PrimeData.IsCountCorrect(sieve.SieveSize, sieve.CountPrimes())?.ToString() ?? "Unable to determine"}");
            if (sieve.ClearCount > 0)
            {
                results.Add($"Clear Count: {sieve.ClearCount}");
            }

            return(results.Aggregate((a, b) => $"{a}, {b}"));
        }