private static IEnumerable <BigIntRange> EnumerateIncludedRanges(IEnumerable <BigIntRangeBound> sortedBounds)
        {
            BigInteger?prevBound = null;              //only non-null when the previous bound is incomplete and needs the next bound before anything can be returned

            foreach (var bound in sortedBounds)
            {
                if (bound.Direction == Direction.Nowhere)
                {
                    if (prevBound != null)
                    {
                        throw new InvalidOperationException("Underlying sorted set of range bounds is corrupt - previous bound (at value " + prevBound.Value + ") expected a matching closing bound next but current bound (at value " + bound.Bound + ") represents a single point.");
                    }
                    yield return(BigIntRange.Single(bound.Bound));
                }
                else if (bound.Direction == Direction.Up)
                {
                    if (prevBound != null)
                    {
                        throw new InvalidOperationException("Underlying sorted set of range bounds is corrupt - previous bound (at value " + prevBound.Value + ") expected a matching closing bound next but current bound (at value " + bound.Bound + ") represents the start of a new range.");
                    }
                    prevBound = bound.Bound;
                }
                else
                {
                    if (prevBound == null)
                    {
                        throw new InvalidOperationException("Underlying sorted set of range bounds is corrupt - current bound (at value " + bound.Bound + ") represents the end of a range, but the previous bound was a complete range or there was no previous bound");
                    }
                    yield return(BigIntRange.CreateStartEnd(prevBound.Value, bound.Bound));

                    prevBound = null;
                }
            }
        }
 public IEnumerable <BigInteger> EnumerateIncludedValues(bool reverse = false)
 {
     if (_sortedSet.Count == 0)
     {
         return(Enumerable.Empty <BigInteger>());
     }
     else
     {
         return(EnumerateIncludedValues(BigIntRange.CreateStartEnd(this.Min.Value, this.Max.Value), reverse));
     }
 }
        public static IEnumerable <BigInteger> IterateNonTrivialZeros(BigInteger maxZ, Action <ExpansionTerm> currentPathStartingPointPrinter = null)
        {
            var termOptions = new TermOptionsMatrix(maxZ);

            //	var sortedEliminatedRanges = new List<BigIntRange>();
            var eliminatedRanges = new RangeSet();

            BigInteger prevStartingPointEval = BigInteger.Zero;             //Starts lower than any evaluated starting point

            foreach (ExpansionTerm pathStartingPoint in EnumerateSortedTermOptions())
            {
                currentPathStartingPointPrinter?.Invoke(pathStartingPoint);
                //	Console.WriteLine("#25 current starting point: " + pathStartingPoint);

                //	//Debugging:
                //	eliminatedRanges.DebugBounds();
                //	foreach (var range in eliminatedRanges.IncludedRanges) {
                //		Console.WriteLine("#40: " + range.Start + " to " + range.End);
                //	}
                //	Console.WriteLine("#41: " + eliminatedRanges.Count);

                BigInteger startingPointEval = pathStartingPoint.Evaluate();

                if (startingPointEval > maxZ)
                {
                    //If gone past end of requested range, return all remaining stored values
                    //up to end of requested range, then stop enumerating.
                    var gaps = eliminatedRanges.EnumerateExcludedValues(
                        searchRange: BigIntRange.CreateStartEnd(
                            start: prevStartingPointEval + 1,
                            end: maxZ
                            )
                        );
                    foreach (BigInteger gap in gaps)
                    {
                        yield return(gap);
                    }
                    //	foreach (var range in eliminatedRanges.IncludedRanges) {
                    //		Console.WriteLine("#42: " + range.Start + " to " + range.End);
                    //	}
                    //	Console.WriteLine("#43: " + eliminatedRanges.Count);
                    yield break;
                }

                foreach (BigInteger summedPath in termOptions.EnumerateSummedExpansionPaths(pathStartingPoint, capToMaximum: true))
                {
                    //	Console.WriteLine("#44: " + summedPath + ", " + expansionPath.ToString(includeSpaces: false));

                    BigInteger newRangeMax = summedPath;
                    BigInteger newRangeMin = summedPath;

                    //	Console.WriteLine("#44.1: " + newRangeMin + ", " + newRangeMax);

                    //Exclude adjacent trivial zeros (multiples of 3) as well
                    if ((newRangeMax + 1) % 3 == 0)
                    {
                        newRangeMax += 1;
                    }
                    if ((newRangeMin - 1) % 3 == 0)
                    {
                        newRangeMin -= 1;
                    }

                    //	Console.WriteLine("#44.2: " + newRangeMin + ", " + newRangeMax);

                    //	Console.WriteLine("#45: " + eliminatedRanges.Count);
                    eliminatedRanges.AddRange(BigIntRange.CreateStartEnd(newRangeMin, newRangeMax));
                    //	Console.WriteLine("#46: " + eliminatedRanges.Count);
                }

                {
                    //	Console.WriteLine("#47: " + eliminatedRanges.Count);
                    var gaps = eliminatedRanges.EnumerateExcludedValues(
                        searchRange: BigIntRange.CreateStartEnd(
                            start: prevStartingPointEval + 1,
                            end: startingPointEval
                            )
                        );
                    foreach (BigInteger gap in gaps)
                    {
                        yield return(gap);
                    }
                    //	Console.WriteLine("#48");
                }

                prevStartingPointEval = startingPointEval;
            }
        }
        public bool TryGetSurroundingRange(BigInteger x, out BigIntRange range, out bool rangeIsIncluded)
        {
            BigIntRangeBound below;
            BigIntRangeBound above;

            if (TryGetSurroundingBounds(x, out below, out above))
            {
                switch (below.Direction)
                {
                case Direction.Nowhere:
                    range           = BigIntRange.Single(below.Bound);                   //below == above
                    rangeIsIncluded = true;
                    return(true);

                case Direction.Down:
                    switch (above.Direction)
                    {
                    case Direction.Down: throw new InvalidOperationException(
                                  "Underlying sorted set of range bounds is corrupt - the bound below or equal to " + nameof(x) + "=" + x + " "
                                  + "(at value " + below.Bound + ") represents the end of a range, but the bound above or equal "
                                  + "to " + nameof(x) + "=" + x + " (at value " + above.Bound + ") also represents the end of a range, meaning "
                                  + "this second range would have no starting value."
                                  );

                    case Direction.Nowhere:                                     //Same behaviour as for Direction.Up
                    case Direction.Up:
                        if (BigInteger.Abs(below.Bound - above.Bound) >= 2)     //If there is a gap between the bounds
                        {
                            range           = BigIntRange.CreateStartEnd(below.Bound + 1, above.Bound - 1);
                            rangeIsIncluded = false;
                            return(true);
                        }
                        else
                        {
                            range           = default(BigIntRange);
                            rangeIsIncluded = default(bool);
                            return(false);
                        }

                    default: break;
                    }
                    break;

                case Direction.Up:
                    switch (above.Direction)
                    {
                    case Direction.Nowhere: throw new InvalidOperationException(
                                  "Underlying sorted set of range bounds is corrupt - the bound below or equal to " + nameof(x) + "=" + x + " "
                                  + "(at value " + below.Bound + ") represents the start of a range, so and end to this range is expected next, "
                                  + "but the bound above or equal to " + nameof(x) + "=" + x + " (at value " + above.Bound + ") represents a range "
                                  + "containing only single value."
                                  );

                    case Direction.Up: throw new InvalidOperationException(
                                  "Underlying sorted set of range bounds is corrupt - the bound below or equal to " + nameof(x) + "=" + x + " "
                                  + "(at value " + below.Bound + ") represents the end of a range, so and end to this range is expected next, "
                                  + "but the bound above or equal to " + nameof(x) + "=" + x + " (at value " + above.Bound + ") represents "
                                  + "the start of another range."
                                  );

                    case Direction.Down:
                        range           = BigIntRange.CreateStartEnd(below.Bound, above.Bound);
                        rangeIsIncluded = true;
                        return(true);

                    default: break;
                    }
                    break;

                default: break;
                }
            }

            range           = default(BigIntRange);
            rangeIsIncluded = default(bool);
            return(false);
        }
 /// <summary>Gets a range from (-End) to (-Start)</summary>
 public BigIntRange GetNegative() => BigIntRange.CreateStartEnd(start: -this.End, end: -this.Start);
 //Not a clear name & difficult to define (it's not a union - what is it)
 //	public static BigIntRange Combine(BigIntRange a, BigIntRange b) => BigIntRange.CreateStartEnd(
 //		start: BigInteger.Min(a.Start, b.Start),
 //		end:   BigInteger.Max(a.End  , b.End  )
 //	);
 //Instead do:
 public BigIntRange ExpandToInclude(BigIntRange other) => BigIntRange.CreateStartEnd(
     start: BigInteger.Min(this.Start, other.Start),
     end:   BigInteger.Max(this.End, other.End)
     );
 public BigIntRange ExpandToInclude(BigInteger x) => BigIntRange.CreateStartEnd(
     start: BigInteger.Min(this.Start, x),
     end:   BigInteger.Max(this.End, x)
     );
 public BigIntRange Expand(BigInteger downBy, BigInteger upBy) => BigIntRange.CreateStartEnd(this.Start - downBy, this.End + upBy);