public virtual DirectionRange <double>?ToDouble(DirectionRange <TAlgebraicNumber> range)
        {
            if (range is null)
            {
                throw new ArgumentNullException(nameof(range));
            }

            var start = ToDouble(range.Start);
            var end   = ToDouble(range.End);

            if (start != end || range.Start == range.End)
            {
                return(DoubleFactory.CreateDirectionRange(start, end, range.Orientation));
            }

            var isAlmostOmniRange =
                range.Orientation == Orientation.CounterClockwise
                    ? range.Start.CompareTo(range.End, range.Start.Opposite()) == DirectionOrder.After
                    : range.Start.CompareTo(range.End, range.Start.Opposite()) == DirectionOrder.Before;

            return(isAlmostOmniRange
                       ? DoubleFactory.CreateDirectionRange(start, end, range.Orientation)
                       : Policy switch
            {
                InvalidConversionPolicy.Throw =>
                throw new InvalidOperationException(
                    "The range collapsed to a single direction during conversion to double."),
                InvalidConversionPolicy.IgnoreSilently => null,
                _ => throw new NotSupportedException(
                    $"Only Silent and ThrowException modes are supported but got {Policy}.")
            });
        private static TestCaseData Case_RDegenerateRpDegenerate_SpInR()
        {
            var S = new Direction <double>(Calculator, x: 1.0, y: 0.0);

            var Sp = new Direction <double>(Calculator, x: 0.0, y: 1.0);

            var range1 = new DirectionRange <double>(
                Calculator,
                S, S,
                Orientation.CounterClockwise);

            var range2 = new DirectionRange <double>(
                Calculator,
                Sp, Sp,
                Orientation.CounterClockwise);

            var expectedIntersection1 = new DirectionRange <double>(
                Calculator,
                S, Sp,
                Orientation.CounterClockwise);

            var expectedIntersection2 = new DirectionRange <double>(
                Calculator,
                Sp, S,
                Orientation.CounterClockwise);

            var expectedIntersections = new List <DirectionRange <double> > {
                expectedIntersection1, expectedIntersection2
            };

            return(new TestCaseData(range1, range2, expectedIntersections)
                   .SetName($"{nameof(DirectionRangeExtensionsIntersectionTestCaseDataSource)} - {nameof(Case_RDegenerateRpDegenerate_SpInR)}"));
        }
        private static TestCaseData Case_SpEqualsE_EpInR()
        {
            var range1 = new DirectionRange <double>(
                Calculator,
                new Direction <double>(Calculator, x: 1.0, y: 0.0),
                new Direction <double>(Calculator, x: 0.0, y: 1.0),
                Orientation.CounterClockwise);

            var range2 = new DirectionRange <double>(
                Calculator,
                new Direction <double>(Calculator, x: 0.0, y: 1.0),
                new Direction <double>(Calculator, x: 1.0, y: 1.0),
                Orientation.CounterClockwise);

            var expectedIntersection = new DirectionRange <double>(
                Calculator,
                new Direction <double>(Calculator, x: 1.0, y: 0.0),
                new Direction <double>(Calculator, x: 1.0, y: 1.0),
                Orientation.CounterClockwise);

            var expectedIntersections = new List <DirectionRange <double> > {
                expectedIntersection
            };

            return(new TestCaseData(range1, range2, expectedIntersections)
                   .SetName($"{nameof(DirectionRangeExtensionsIntersectionTestCaseDataSource)} - {nameof(Case_SpEqualsE_EpInR)}"));
        }
        private static TestCaseData Case05()
        {
            var start1 = new Direction <double>(Calculator, x: 0.0, y: 1.0);
            var end1   = new Direction <double>(Calculator, x: 0.0, y: 1.0);

            var start2 = new Direction <double>(Calculator, x: 1.0, y: 0.0);
            var end2   = new Direction <double>(Calculator, x: -1.0, y: 0.0);

            var range1 = new DirectionRange <double>(Calculator, start1, end1, Orientation.CounterClockwise);
            var range2 = new DirectionRange <double>(Calculator, start2, end2, Orientation.CounterClockwise);

            var expectedRange1 = new DirectionRange <double>(
                Calculator,
                new Direction <double>(Calculator, x: 1.0, y: 0.0),
                new Direction <double>(Calculator, x: 0.0, y: 1.0),
                Orientation.CounterClockwise);

            var expectedRange2 = new DirectionRange <double>(
                Calculator,
                new Direction <double>(Calculator, x: 0.0, y: 1.0),
                new Direction <double>(Calculator, x: -1.0, y: 0.0),
                Orientation.CounterClockwise);

            var expected = new List <DirectionRange <double> > {
                expectedRange1, expectedRange2
            };

            return(new TestCaseData(range1, range2, expected)
                   .SetName($"{nameof(DirectionRangeExtensionsIntersectionTestCaseDataSource)} - {nameof(Case05)}"));
        }
        public virtual DirectionRange <TAlgebraicNumber> FromDouble(DirectionRange <double> range)
        {
            if (range is null)
            {
                throw new ArgumentNullException(nameof(range));
            }

            return(AlgebraicNumberFactory.CreateDirectionRange(
                       FromDouble(range.Start),
                       FromDouble(range.End),
                       range.Orientation));
        }
        public void When_calling_Union_with_null_range_Then_an_ArgumentNullException_Should_be_thrown()
        {
            // Arrange
            var start = new Direction <double>(_calculator, x: 1.0, y: 0.0);
            var end   = new Direction <double>(_calculator, x: 1.0, y: 0.0);

            var subject = new DirectionRange <double>(_calculator, start, end, Orientation.CounterClockwise);

            // Act
            Action action = () => _ = subject.Union(null);

            // Assert
            action.Should()
            .ThrowExactly <ArgumentNullException>()
            .And.ParamName.Should()
            .Be(expected: "other");
        }
        private static TestCaseData Case_SpInRBar_EpEqualsS()
        {
            var range1 = new DirectionRange <double>(
                Calculator,
                new Direction <double>(Calculator, x: 1.0, y: 0.0),
                new Direction <double>(Calculator, x: 0.0, y: 1.0),
                Orientation.CounterClockwise);

            var range2 = new DirectionRange <double>(
                Calculator,
                new Direction <double>(Calculator, x: -1.0, y: -1.0),
                new Direction <double>(Calculator, x: 1.0, y: 0.0),
                Orientation.CounterClockwise);

            var expectedIntersections = Enumerable.Empty <DirectionRange <double> >().ToList();

            return(new TestCaseData(range1, range2, expectedIntersections)
                   .SetName($"{nameof(DirectionRangeExtensionsIntersectionTestCaseDataSource)} - {nameof(Case_SpInRBar_EpEqualsS)}"));
        }
        public void When_calling_Intersection_with_valid_ranges_Then_the_correct_result_Should_be_returned(
            DirectionRange <double> range1,
            DirectionRange <double> range2,
            IReadOnlyList <DirectionRange <double> > expected)
        {
            // Act
            var actual1 = range1.Intersection(range2).ToList();
            var actual2 = range2.Intersection(range1).ToList();

            // Assert
            actual1.Should().HaveCount(expected.Count);
            actual1.Should().BeEquivalentTo(expected);

            actual2.Should().HaveCount(expected.Count);
            if (range1.Orientation == Orientation.CounterClockwise &&
                range2.Orientation == Orientation.CounterClockwise)
            {
                actual2.Should().BeEquivalentTo(expected);
            }
        }
        public void When_calling_Union_with_valid_ranges_Then_the_correct_result_Should_be_returned(
            DirectionRange <double> range1,
            DirectionRange <double> range2,
            IReadOnlyList <DirectionRange <double> > expected)
        {
            // Arrange
            var expectedReversed = expected.Select(t => t.Reverse());

            // Act
            var actual1 = range1.Union(range2).ToList();
            var actual2 = range2.Union(range1).ToList();

            // Assert
            actual1.Should().HaveCount(expected.Count);
            actual1.Should().BeEquivalentTo(expected);

            actual2.Should().HaveCount(expected.Count);

            // degenerate range
            if (expected.Count == 1 && expected[0].Start == expected[0].End)
            {
                actual2[0].Start.Should().BeEquivalentTo(actual2[0].End);
                actual2[0].Orientation.Should().BeEquivalentTo(range2.Orientation);
            }
            else
            {
                if (range1.Orientation == range2.Orientation)
                {
                    actual2.Should().BeEquivalentTo(expected);
                }
                else
                {
                    actual2.Should().BeEquivalentTo(expectedReversed);
                }
            }
        }