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 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);
        }