diff --git a/EasyIntervals.Tests/IntervalComparerTests.cs b/EasyIntervals.Tests/IntervalComparerTests.cs index bd3a039..6866e82 100644 --- a/EasyIntervals.Tests/IntervalComparerTests.cs +++ b/EasyIntervals.Tests/IntervalComparerTests.cs @@ -3,21 +3,21 @@ namespace EasyIntervals.Tests; public class IntervalComparerTests { [Fact] - public void SameStartFirstOpenSecondStartClosed_ShouldReturnOne() + public void SameStartFirstOpenSecondEndOpen_ShouldReturnOne() { var intervalComparer = IntervalComparer.Create(Comparer.Default); - var result = intervalComparer.Compare((5, 12), (5, 10, IntervalType.StartClosed)); + var result = intervalComparer.Compare((5, 12), (5, 10, IntervalType.EndOpen)); result.Should().Be(1); } [Fact] - public void SameStartFirstStartClosedSecondOpen_ShouldReturnMinusOne() + public void SameStartFirstEndOpenSecondOpen_ShouldReturnMinusOne() { var intervalComparer = IntervalComparer.Create(Comparer.Default); - var result = intervalComparer.Compare((5, 12, IntervalType.StartClosed), (5, 10)); + var result = intervalComparer.Compare((5, 12, IntervalType.EndOpen), (5, 10, IntervalType.Open)); result.Should().Be(-1); } @@ -27,7 +27,7 @@ public void SameStartSameStartTypeFirstGreaterEnd_ShouldReturnOne() { var intervalComparer = IntervalComparer.Create(Comparer.Default); - var result = intervalComparer.Compare((5, 12, IntervalType.StartClosed), (5, 10, IntervalType.StartClosed)); + var result = intervalComparer.Compare((5, 12, IntervalType.EndOpen), (5, 10, IntervalType.EndOpen)); result.Should().Be(1); } @@ -37,27 +37,27 @@ public void SameStartSameStartTypeFirstLowerEnd_ShouldReturnMinusOne() { var intervalComparer = IntervalComparer.Create(Comparer.Default); - var result = intervalComparer.Compare((5, 9, IntervalType.StartClosed), (5, 10, IntervalType.StartClosed)); + var result = intervalComparer.Compare((5, 9, IntervalType.EndOpen), (5, 10, IntervalType.EndOpen)); result.Should().Be(-1); } [Fact] - public void SameLimitsSameStartTypeFirstEndClosed_ShouldReturnOne() + public void SameLimitsSameStartTypeFirstStartOpen_ShouldReturnOne() { var intervalComparer = IntervalComparer.Create(Comparer.Default); - var result = intervalComparer.Compare((5, 10, IntervalType.Closed), (5, 10, IntervalType.StartClosed)); + var result = intervalComparer.Compare((5, 10, IntervalType.Closed), (5, 10, IntervalType.EndOpen)); result.Should().Be(1); } [Fact] - public void SameLimitsSameStartTypeSecondEndClosed_ShouldReturnMinusOne() + public void SameLimitsSameStartTypeSecondStartOpen_ShouldReturnMinusOne() { var intervalComparer = IntervalComparer.Create(Comparer.Default); - var result = intervalComparer.Compare((5, 10, IntervalType.StartClosed), (5, 10, IntervalType.Closed)); + var result = intervalComparer.Compare((5, 10, IntervalType.EndOpen), (5, 10, IntervalType.Closed)); result.Should().Be(-1); } @@ -67,7 +67,7 @@ public void SameLimitsSameTypes_ShouldReturnZero() { var intervalComparer = IntervalComparer.Create(Comparer.Default); - var result = intervalComparer.Compare((5, 10, IntervalType.StartClosed), (5, 10, IntervalType.StartClosed)); + var result = intervalComparer.Compare((5, 10, IntervalType.EndOpen), (5, 10, IntervalType.EndOpen)); result.Should().Be(0); } diff --git a/EasyIntervals.Tests/IntervalExtensionsTests.cs b/EasyIntervals.Tests/IntervalExtensionsTests.cs new file mode 100644 index 0000000..2e7ea1e --- /dev/null +++ b/EasyIntervals.Tests/IntervalExtensionsTests.cs @@ -0,0 +1,50 @@ +namespace EasyIntervals.Tests; + +public class IntervalExtensionsTests +{ + [Fact] + public void HasIntersection_IntersectingIntervals_ShouldReturnTrue() + { + Interval interval = (2, 5); + Interval other = (4, 8); + + var result = interval.HasIntersection(other, IntersectionType.Any); + + result.Should().BeTrue(); + } + + [Fact] + public void HasIntersection_NotIntersectingIntervals_ShouldReturnFalse() + { + Interval interval = (2, 5); + Interval other = (6, 8); + + var result = interval.HasIntersection(other, IntersectionType.Any); + + result.Should().BeFalse(); + } + + [Fact] + public void HasIntersection_IntersectingIntervalsWithCustomComparer_ShouldReturnTrue() + { + var comparer = Comparer.Create((i1, i2) => i2 - i1); + Interval interval = (5, 2, comparer); + Interval other = (8, 4, comparer); + + var result = interval.HasIntersection(other, IntersectionType.Any, comparer); + + result.Should().BeTrue(); + } + + [Fact] + public void HasIntersection_NotIntersectingIntervalsWithCustomComparer_ShouldReturnFalse() + { + var comparer = Comparer.Create((i1, i2) => i2 - i1); + Interval interval = (5, 2, comparer); + Interval other = (8, 6, comparer); + + var result = interval.HasIntersection(other, IntersectionType.Any, comparer); + + result.Should().BeFalse(); + } +} \ No newline at end of file diff --git a/EasyIntervals.Tests/IntervalSetTests.cs b/EasyIntervals.Tests/IntervalSetTests.cs index e83fa9b..a40c6df 100644 --- a/EasyIntervals.Tests/IntervalSetTests.cs +++ b/EasyIntervals.Tests/IntervalSetTests.cs @@ -12,17 +12,17 @@ public class IntervalSetTests private ISet> input = new HashSet> { (18, 34, IntervalType.Closed), (13, 18, IntervalType.Closed), - (1, 2), + (1, 2, IntervalType.Open), (7, 12, IntervalType.Closed), - (42, 68), - (73, 90), - (56, 65), - (24, 56), - (6, 11), - (2, 4), - (74, 80), - (69, 92), - (55, 58) + (42, 68, IntervalType.Open), + (73, 90, IntervalType.Open), + (56, 65, IntervalType.Open), + (24, 56, IntervalType.Open), + (6, 11, IntervalType.Open), + (2, 4, IntervalType.Open), + (74, 80, IntervalType.Open), + (69, 92, IntervalType.Open), + (55, 58, IntervalType.Open) }; private IntervalSet CreateIntervalSet(ISet> input) @@ -72,7 +72,7 @@ public void Contains_ExistingInterval_ShouldReturnTrue() { var intervalSet = CreateIntervalSet(input); - var result = intervalSet.Contains((1, 2)); + var result = intervalSet.Contains((1, 2, IntervalType.Open)); result.Should().BeTrue(); } @@ -93,11 +93,11 @@ public void Merge_NonEmptyCollection_ShouldReturnMergedIntervalsSorted() // Arrange var intervalSet = CreateIntervalSet(input); var expected = new Interval[] { - (1, 2), - (2, 4), - (6, 12, IntervalType.EndClosed), - (13, 68, IntervalType.StartClosed), - (69, 92), + (1, 2, IntervalType.Open), + (2, 4, IntervalType.Open), + (6, 12, IntervalType.StartOpen), + (13, 68, IntervalType.EndOpen), + (69, 92, IntervalType.Open), }; // Act @@ -113,11 +113,11 @@ public void Merge_NonEmptyCollection_ShouldReturnIntervalSetWithMergedIntervals( // Arrange var intervalSet = CreateIntervalSet(input); var expected = new Interval[] { - (1, 2), - (2, 4), - (6, 12, IntervalType.EndClosed), - (13, 68, IntervalType.StartClosed), - (69, 92), + (1, 2, IntervalType.Open), + (2, 4, IntervalType.Open), + (6, 12, IntervalType.StartOpen), + (13, 68, IntervalType.EndOpen), + (69, 92, IntervalType.Open), }; // Act @@ -134,9 +134,9 @@ public void Merge_NonEmptyCollection_ShouldReturnMergedIntervalsByMergeFunction( var intervalSet = new IntervalSet { (6, 11, 20m, IntervalType.Closed), - (7, 12, 10m, IntervalType.StartClosed) + (7, 12, 10m, IntervalType.EndOpen) }; - var expected = new Interval(6, 12, 30m, IntervalType.StartClosed); + var expected = new Interval(6, 12, 30m, IntervalType.EndOpen); // Act var result = intervalSet.Merge((itv1, itv2) => itv1.Value + itv2.Value); @@ -167,15 +167,15 @@ public void Merge_EmptyCollection_ShouldReturnEmptyIntervalSet() /// --6-----7-----------------------11----12------- /// [Fact] - public void Merge_PrecedingClosedFollowingStartClosed_ShouldReturnCorrectStartClosed() + public void Merge_PrecedingClosedFollowingEndOpen_ShouldReturnCorrectEndOpen() { // Arrange var intervalSet = new IntervalSet { (6, 11, IntervalType.Closed), - (7, 12, IntervalType.StartClosed) + (7, 12, IntervalType.EndOpen) }; - var expected = new Interval(6, 12, IntervalType.StartClosed); + var expected = new Interval(6, 12, IntervalType.EndOpen); // Act var result = intervalSet.Merge(); @@ -193,13 +193,13 @@ public void Merge_PrecedingClosedFollowingStartClosed_ShouldReturnCorrectStartCl /// --6-----7-----------------------11----12------- /// [Fact] - public void Merge_PrecedingStartClosedFollowingEndClosed_ShouldReturnCorrectClosed() + public void Merge_PrecedingEndOpenFollowingStartOpen_ShouldReturnCorrectClosed() { // Arrange var intervalSet = new IntervalSet { - (6, 11, IntervalType.StartClosed), - (7, 12, IntervalType.EndClosed) + (6, 11, IntervalType.EndOpen), + (7, 12, IntervalType.StartOpen) }; var expected = new Interval(6, 12, IntervalType.Closed); @@ -219,15 +219,15 @@ public void Merge_PrecedingStartClosedFollowingEndClosed_ShouldReturnCorrectClos /// --6-----7-----------------------11----12------- /// [Fact] - public void Merge_PrecedingOpenFollowingEndClosed_ShouldReturnCorrectEndClosed() + public void Merge_PrecedingOpenFollowingStartOpen_ShouldReturnCorrectStartOpen() { // Arrange var intervalSet = new IntervalSet { (6, 11, IntervalType.Open), - (7, 12, IntervalType.EndClosed) + (7, 12, IntervalType.StartOpen) }; - var expected = new Interval(6, 12, IntervalType.EndClosed); + var expected = new Interval(6, 12, IntervalType.StartOpen); // Act var result = intervalSet.Merge(); @@ -245,13 +245,13 @@ public void Merge_PrecedingOpenFollowingEndClosed_ShouldReturnCorrectEndClosed() /// --6-----7-----------------------11----12------- /// [Fact] - public void Merge_PrecedingOpenFollowingStartClosed_ShouldReturnCorrectOpen() + public void Merge_PrecedingOpenFollowingEndOpen_ShouldReturnCorrectOpen() { // Arrange var intervalSet = new IntervalSet { (6, 11, IntervalType.Open), - (7, 12, IntervalType.StartClosed) + (7, 12, IntervalType.EndOpen) }; var expected = new Interval(6, 12, IntervalType.Open); @@ -271,15 +271,15 @@ public void Merge_PrecedingOpenFollowingStartClosed_ShouldReturnCorrectOpen() /// --7-----------------------11----12------------- /// [Fact] - public void Merge_SameStartPrecedingOpenFollowingEndClosed_ShouldReturnCorrectEndClosed() + public void Merge_SameStartPrecedingOpenFollowingStartOpen_ShouldReturnCorrectStartOpen() { // Arrange var intervalSet = new IntervalSet { (7, 11, IntervalType.Open), - (7, 12, IntervalType.EndClosed) + (7, 12, IntervalType.StartOpen) }; - var expected = new Interval(7, 12, IntervalType.EndClosed); + var expected = new Interval(7, 12, IntervalType.StartOpen); // Act var result = intervalSet.Merge(); @@ -297,13 +297,13 @@ public void Merge_SameStartPrecedingOpenFollowingEndClosed_ShouldReturnCorrectEn /// --7-----------------------11----12------------- /// [Fact] - public void Merge_SameStartPrecedingStartClosedFollowingEndClosed_ShouldReturnCorrectClosed() + public void Merge_SameStartPrecedingEndOpenFollowingStartOpen_ShouldReturnCorrectClosed() { // Arrange var intervalSet = new IntervalSet { - (7, 11, IntervalType.StartClosed), - (7, 12, IntervalType.EndClosed) + (7, 11, IntervalType.EndOpen), + (7, 12, IntervalType.StartOpen) }; var expected = new Interval(7, 12, IntervalType.Closed); @@ -323,12 +323,12 @@ public void Merge_SameStartPrecedingStartClosedFollowingEndClosed_ShouldReturnCo /// --7-----------------------11----12------------- /// [Fact] - public void Merge_SameStartPrecedingStartClosedFollowingClosed_ShouldReturnCorrectClosed() + public void Merge_SameStartPrecedingEndOpenFollowingClosed_ShouldReturnCorrectClosed() { // Arrange var intervalSet = new IntervalSet { - (7, 11, IntervalType.StartClosed), + (7, 11, IntervalType.EndOpen), (7, 12, IntervalType.Closed) }; var expected = new Interval(7, 12, IntervalType.Closed); @@ -349,15 +349,15 @@ public void Merge_SameStartPrecedingStartClosedFollowingClosed_ShouldReturnCorre /// ---6------7-------8-----------12------------- /// [Fact] - public void Merge_SameEndPrecedingOpenFollowingEndClosed_ShouldReturnCorrectEndClosed() + public void Merge_SameEndPrecedingOpenFollowingStartOpen_ShouldReturnCorrectStartOpen() { // Arrange var intervalSet = new IntervalSet { (6, 12, IntervalType.Open), - (7, 12, IntervalType.EndClosed) + (7, 12, IntervalType.StartOpen) }; - var expected = new Interval(6, 12, IntervalType.EndClosed); + var expected = new Interval(6, 12, IntervalType.StartOpen); // Act var result = intervalSet.Merge(); @@ -375,13 +375,13 @@ public void Merge_SameEndPrecedingOpenFollowingEndClosed_ShouldReturnCorrectEndC /// ---6------7-------8-----------12------------- /// [Fact] - public void Merge_SameEndPrecedingStartClosedFollowingEndClosed_ShouldReturnCorrectClosed() + public void Merge_SameEndPrecedingEndOpenFollowingStartOpen_ShouldReturnCorrectClosed() { // Arrange var intervalSet = new IntervalSet { - (6, 12, IntervalType.StartClosed), - (7, 12, IntervalType.EndClosed) + (6, 12, IntervalType.EndOpen), + (7, 12, IntervalType.StartOpen) }; var expected = new Interval(6, 12, IntervalType.Closed); @@ -401,15 +401,15 @@ public void Merge_SameEndPrecedingStartClosedFollowingEndClosed_ShouldReturnCorr /// ---6------7-------8-----------12------------- /// [Fact] - public void Merge_SameEndPrecedingStartClosedFollowingOpen_ShouldReturnCorrectStartClosed() + public void Merge_SameEndPrecedingEndOpenFollowingOpen_ShouldReturnCorrectEndOpen() { // Arrange var intervalSet = new IntervalSet { - (6, 12, IntervalType.StartClosed), + (6, 12, IntervalType.EndOpen), (7, 12, IntervalType.Open) }; - var expected = new Interval(6, 12, IntervalType.StartClosed); + var expected = new Interval(6, 12, IntervalType.EndOpen); // Act var result = intervalSet.Merge(); @@ -453,11 +453,11 @@ public void Merge_TouchingIntervalsPrecedingOpenFollowingOpen_ShouldNotMergeInte /// ---6--------------8-----------12------------- /// [Fact] - public void Merge_TouchingIntervalsPrecedingEndClosedFollowingOpen_ShouldMergeIntervals() + public void Merge_TouchingIntervalsPrecedingStartOpenFollowingOpen_ShouldMergeIntervals() { // Arrange var intervalSet = new IntervalSet(); - var precedingInterval = new Interval(6, 8, IntervalType.EndClosed); + var precedingInterval = new Interval(6, 8, IntervalType.StartOpen); var followingInterval = new Interval(8, 12, IntervalType.Open); intervalSet.Add(precedingInterval); intervalSet.Add(followingInterval); @@ -479,12 +479,12 @@ public void Merge_TouchingIntervalsPrecedingEndClosedFollowingOpen_ShouldMergeIn /// ---6--------------8-----------12------------- /// [Fact] - public void Merge_TouchingIntervalsPrecedingOpenFollowingStartClosed_ShouldMergeIntervals() + public void Merge_TouchingIntervalsPrecedingOpenFollowingEndOpen_ShouldMergeIntervals() { // Arrange var intervalSet = new IntervalSet(); var precedingInterval = new Interval(6, 8, IntervalType.Open); - var followingInterval = new Interval(8, 12, IntervalType.StartClosed); + var followingInterval = new Interval(8, 12, IntervalType.EndOpen); intervalSet.Add(precedingInterval); intervalSet.Add(followingInterval); var expected = new Interval(6, 12, IntervalType.Open); @@ -496,6 +496,262 @@ public void Merge_TouchingIntervalsPrecedingOpenFollowingStartClosed_ShouldMerge result.Should().ContainEquivalentOf(expected); } + /// + /// Intersection interval: + /// [--------] + /// Set: + /// [--------------] + /// (--------------) (--------] + /// (--------) (--------) [--------------) + /// 2--3-----5-----7--8-----10-11-------14----16- + /// Expected result: true + /// + [Fact] + public void HasIntersection_GivenIntervalHasAnyIntersection_ShouldReturnTrue() + { + // Arrange + var expected = new Interval[] { + (3, 8, IntervalType.Closed), + (11, 16, IntervalType.EndOpen), + (7, 10), + }; + + var intervalSet = new IntervalSet + { + (2, 5), // (2, 5) + (7, 10), // (7, 10) + (3, 8, IntervalType.Open), // (3, 8) + (3, 8, IntervalType.Closed), // [3, 8] + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen) // (11, 14] + }; + + // Act + var result = intervalSet.HasIntersection((8, 11, IntervalType.Closed)); + + // Assert + result.Should().BeTrue(); + } + + /// + /// Intersection interval: + /// (--------) + /// Set: + /// [--------------] + /// (--------------) (--------] + /// (--------) [--------------) + /// 2--3-----5--------8--------11-------14----16- + /// Expected result: false + /// + [Fact] + public void HasIntersection_GivenIntervalHasNoIntersection_ShouldReturnFalse() + { + // Arrange + var intervalSet = new IntervalSet + { + (2, 5, IntervalType.Open), // (2, 5) + (3, 8, IntervalType.Open), // (3, 8) + (3, 8, IntervalType.Closed), // [3, 8] + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen) // (11, 14] + }; + + // Act + var result = intervalSet.HasIntersection((8, 11, IntervalType.Open)); + + // Assert + result.Should().BeFalse(); + } + + /// + /// Intersection interval: + /// [--------------------------------] + /// Set: + /// [--------------------------] + /// (--------) + /// [--------------) (--------] + /// 2--3-----5--------------10-11-12----14----16- + /// Expected result: true + /// + [Fact] + public void HasIntersection_GivenIntervalHasCoveringIntersection_ShouldReturnIntersectedIntervals() + { + // Arrange + var intervalSet = new IntervalSet + { + (2, 5), + (5, 10, IntervalType.EndOpen), + (3, 12, IntervalType.Closed), + (11, 14, IntervalType.StartOpen) + }; + + // Act + var result = intervalSet.HasIntersection((5, 16, IntervalType.Closed), IntersectionType.Cover); + + // Assert + result.Should().BeTrue(); + } + + /// + /// Intersection interval: + /// (--------------) + /// Set: + /// [--------------------------] + /// (--------) + /// [--------------) (--------] + /// 2--3-----5--------------10-11-12----14----16- + /// Expected result: false + /// + [Fact] + public void HasIntersection_GivenIntervalHasNoCoveringIntersection_ShouldReturnEmptyCollection() + { + // Arrange + var intervalSet = new IntervalSet + { + (2, 5, IntervalType.Open), + (5, 10, IntervalType.EndOpen), + (3, 12, IntervalType.Closed), + (11, 16, IntervalType.EndOpen) + }; + + // Act + var result = intervalSet.HasIntersection((5, 10, IntervalType.Open), IntersectionType.Cover); + + // Assert + result.Should().BeFalse(); + } + + /// + /// Intersection interval: + /// [-----------------] + /// Set: + /// [--------------------------] + /// (--------) + /// [--------------) (--------] + /// 2--3-----5--------------10-11-12----14----16- + /// Expected result: true + /// + [Fact] + public void HasIntersection_GivenIntervalHasWithinIntersection_ShouldReturnIntersectedIntervals() + { + // Arrange + var intervalSet = new IntervalSet + { + (2, 5), + (5, 10, IntervalType.EndOpen), + (3, 12, IntervalType.Closed), + (11, 16, IntervalType.EndOpen) + }; + + // Act + var result = intervalSet.HasIntersection((5, 11, IntervalType.Closed), IntersectionType.Within); + + // Assert + result.Should().BeTrue(); + } + + /// + /// Intersection interval: + /// (--------------------------------) + /// Set: + /// [--------------------------] + /// (--------) + /// [--------------) (--------] + /// 2--3-----5--------------10-11-12----14----16- + /// Expected result: false + /// + [Fact] + public void HasIntersection_GivenIntervalHasNoWithinIntersection_ShouldReturnEmptyCollection() + { + // Arrange + var intervalSet = new IntervalSet + { + (2, 5), + (5, 10, IntervalType.EndOpen), + (3, 12, IntervalType.Closed), + (11, 16, IntervalType.EndOpen) + }; + + // Act + var result = intervalSet.HasIntersection((5, 16), IntersectionType.Within); + + // Assert + result.Should().BeFalse(); + } + + /// + /// Intersection intervals: + /// [--------] ... [20, 30] ... [40, 50] + /// Set: + /// [--------------] + /// (--------------) (--------] + /// (--------) (--------) [--------------) + /// 2--3-----5-----7--8-----10-11-------14----16- + /// Expected result: true + /// + [Fact] + public void HasIntersection_AnyGivenIntervalHasAnyIntersection_ShouldReturnTrue() + { + // Arrange + var expected = new Interval[] { + (3, 8, IntervalType.Closed), + (11, 16, IntervalType.EndOpen), + (7, 10), + }; + + var intervalSet = new IntervalSet + { + (2, 5), // (2, 5) + (7, 10), // (7, 10) + (3, 8, IntervalType.Open), // (3, 8) + (3, 8, IntervalType.Closed), // [3, 8] + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen) // (11, 14] + }; + + // Act + var result = intervalSet.HasIntersection(new Interval[] + { + (8, 11, IntervalType.Closed), (20, 30, IntervalType.Closed), (40, 50, IntervalType.Closed) + }); + + // Assert + result.Should().BeTrue(); + } + + /// + /// Intersection interval: + /// (--------) ... [20, 30] ... [40, 50] + /// Set: + /// [--------------] + /// (--------------) (--------] + /// (--------) [--------------) + /// 2--3-----5--------8--------11-------14----16- + /// Expected result: false + /// + [Fact] + public void HasIntersection_NoGivenIntervalHasNoIntersection_ShouldReturnFalse() + { + // Arrange + var intervalSet = new IntervalSet + { + (2, 5), // (2, 5) + (3, 8, IntervalType.Open), // (3, 8) + (3, 8, IntervalType.Closed), // [3, 8] + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen) // (11, 14] + }; + + // Act + var result = intervalSet.HasIntersection(new Interval[] + { + (8, 11, IntervalType.Open), (20, 30), (40, 50) + }); + + // Assert + result.Should().BeFalse(); + } + /// /// Intersection interval: /// [--------] @@ -514,7 +770,7 @@ public void Intersect_GivenIntervalHasAnyIntersection_ShouldReturnIntersectedInt // Arrange var expected = new Interval[] { (3, 8, IntervalType.Closed), - (11, 16, IntervalType.StartClosed), + (11, 16, IntervalType.EndOpen), (7, 10), }; @@ -524,8 +780,8 @@ public void Intersect_GivenIntervalHasAnyIntersection_ShouldReturnIntersectedInt (7, 10), // (7, 10) (3, 8, IntervalType.Open), // (3, 8) (3, 8, IntervalType.Closed), // [3, 8] - (11, 16, IntervalType.StartClosed), // [11, 16) - (11, 14, IntervalType.EndClosed) // (11, 14] + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen) // (11, 14] }; // Act @@ -552,15 +808,15 @@ public void Intersect_GivenIntervalHasNoIntersection_ShouldReturnEmptyCollection // Arrange var intervalSet = new IntervalSet { - (2, 5), // (2, 5) + (2, 5, IntervalType.Open), // (2, 5) (3, 8, IntervalType.Open), // (3, 8) (3, 8, IntervalType.Closed), // [3, 8] - (11, 16, IntervalType.StartClosed), // [11, 16) - (11, 14, IntervalType.EndClosed) // (11, 14] + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen) // (11, 14] }; // Act - var result = intervalSet.Intersect((8, 11)); + var result = intervalSet.Intersect((8, 11, IntervalType.Open)); // Assert result.Should().BeEmpty(); @@ -582,16 +838,16 @@ public void Intersect_GivenIntervalHasCoveringIntersection_ShouldReturnIntersect { // Arrange var expected = new Interval[] { - (5, 10, IntervalType.StartClosed), - (11, 14, IntervalType.EndClosed) + (5, 10, IntervalType.EndOpen), + (11, 14, IntervalType.StartOpen) }; var intervalSet = new IntervalSet { (2, 5), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), - (11, 14, IntervalType.EndClosed) + (11, 14, IntervalType.StartOpen) }; // Act @@ -618,14 +874,14 @@ public void Intersect_GivenIntervalHasNoCoveringIntersection_ShouldReturnEmptyCo // Arrange var intervalSet = new IntervalSet { - (2, 5), - (5, 10, IntervalType.StartClosed), + (2, 5, IntervalType.Open), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), - (11, 16, IntervalType.StartClosed) + (11, 16, IntervalType.EndOpen) }; // Act - var result = intervalSet.Intersect((5, 10), IntersectionType.Cover); + var result = intervalSet.Intersect((5, 10, IntervalType.Open), IntersectionType.Cover); // Assert result.Should().BeEmpty(); @@ -653,9 +909,9 @@ public void Intersect_GivenIntervalHasWithinIntersection_ShouldReturnIntersected var intervalSet = new IntervalSet { (2, 5), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), - (11, 16, IntervalType.StartClosed) + (11, 16, IntervalType.EndOpen) }; // Act @@ -683,9 +939,9 @@ public void Intersect_GivenIntervalHasNoWithinIntersection_ShouldReturnEmptyColl var intervalSet = new IntervalSet { (2, 5), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), - (11, 16, IntervalType.StartClosed) + (11, 16, IntervalType.EndOpen) }; // Act @@ -701,8 +957,8 @@ public void Intersect_GivenIntervalHasManyIntersectedIntervals_ShouldReturnInter // Arrange var expected = new Interval[] { (3, 8, IntervalType.Closed), - (11, 16, IntervalType.StartClosed), - (11, 14, IntervalType.EndClosed), + (11, 16, IntervalType.EndOpen), + (11, 14, IntervalType.StartOpen), (12, 29, IntervalType.Closed), (21, 42, IntervalType.Closed), (22, 25, IntervalType.Closed) @@ -713,8 +969,8 @@ public void Intersect_GivenIntervalHasManyIntersectedIntervals_ShouldReturnInter (2, 5), // (2, 5) (3, 8, IntervalType.Open), // (3, 8) (3, 8, IntervalType.Closed), // [3, 8] - (11, 14, IntervalType.EndClosed), // (11, 14] - (11, 16, IntervalType.StartClosed), // [11, 16) + (11, 14, IntervalType.StartOpen), // (11, 14] + (11, 16, IntervalType.EndOpen), // [11, 16) (22, 25, IntervalType.Closed), // [22, 25] (45, 50, IntervalType.Closed), // [45, 50] (21, 42, IntervalType.Closed), // [21, 42] @@ -747,7 +1003,7 @@ public void Except_GivenIntervalHasAnyIntersection_ShouldReturnExceptedIntervals var expected = new Interval[] { (2, 5), (3, 8, IntervalType.Open), - (11, 14, IntervalType.EndClosed) + (11, 14, IntervalType.StartOpen) }; var intervalSet = new IntervalSet @@ -756,8 +1012,8 @@ public void Except_GivenIntervalHasAnyIntersection_ShouldReturnExceptedIntervals (7, 10), // (7, 10) (3, 8, IntervalType.Open), // (3, 8) (3, 8, IntervalType.Closed), // [3, 8] - (11, 16, IntervalType.StartClosed), // [11, 16) - (11, 14, IntervalType.EndClosed) // (11, 14] + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen) // (11, 14] }; // Act @@ -787,8 +1043,8 @@ public void Except_GivenIntervalHasAllIntersected_ShouldReturnEmptyCollection() (2, 5), // (2, 5) (3, 8, IntervalType.Open), // (3, 8) (3, 8, IntervalType.Closed), // [3, 8] - (11, 16, IntervalType.StartClosed), // [11, 16) - (11, 14, IntervalType.EndClosed) // (11, 14] + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen) // (11, 14] }; // Act @@ -822,9 +1078,9 @@ public void Except_GivenIntervalHasCoveringIntersection_ShouldReturnExceptedInte var intervalSet = new IntervalSet { (2, 5), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), - (11, 14, IntervalType.EndClosed) + (11, 14, IntervalType.StartOpen) }; // Act @@ -852,13 +1108,13 @@ public void Except_GivenIntervalHasAllCoveringIntersection_ShouldReturnEmptyColl var intervalSet = new IntervalSet { (2, 5), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), - (11, 16, IntervalType.StartClosed) + (11, 16, IntervalType.EndOpen) }; // Act - var result = intervalSet.Except((2, 16, IntervalType.StartClosed), IntersectionType.Cover); + var result = intervalSet.Except((2, 16, IntervalType.EndOpen), IntersectionType.Cover); // Assert result.Should().BeEmpty(); @@ -882,16 +1138,16 @@ public void Except_GivenIntervalHasWithinIntersection_ShouldReturnExceptedInterv // Arrange var expected = new Interval[] { (2, 5), - (5, 10, IntervalType.StartClosed), - (11, 14, IntervalType.StartClosed) + (5, 10, IntervalType.EndOpen), + (11, 14, IntervalType.EndOpen) }; var intervalSet = new IntervalSet { (2, 5), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), - (11, 14, IntervalType.StartClosed) + (11, 14, IntervalType.EndOpen) }; // Act @@ -918,13 +1174,13 @@ public void Except_GivenIntervalHasAllWithinIntersection_ShouldReturnEmptyCollec // Arrange var intervalSet = new IntervalSet { - (2, 10), - (5, 10, IntervalType.StartClosed), + (2, 10, IntervalType.Open), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; // Act - var result = intervalSet.Except((5, 10), IntersectionType.Within); + var result = intervalSet.Except((5, 10, IntervalType.Open), IntersectionType.Within); // Assert result.Should().BeEmpty(); @@ -937,7 +1193,7 @@ public void Union_OtherHasMatchingIntervals_ShouldUnionIntervalSetsWithoutRepeti var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; @@ -952,7 +1208,7 @@ public void Union_OtherHasMatchingIntervals_ShouldUnionIntervalSetsWithoutRepeti { (2, 10), (3, 12, IntervalType.Closed), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (25, 30), (33, 35) }; @@ -977,7 +1233,7 @@ public void Union_IntervalSetsHasIntervalsInBetween_ShouldUnionIntervalSets() var otherIntervalSet = new IntervalSet { - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (33, 35) }; @@ -985,7 +1241,7 @@ public void Union_IntervalSetsHasIntervalsInBetween_ShouldUnionIntervalSets() { (2, 10), (3, 12, IntervalType.Closed), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (25, 30), (33, 35) }; @@ -1005,7 +1261,7 @@ public void Union_OtherEmpty_ShouldUnionOnlyIntervalSet() { (2, 10), (3, 12, IntervalType.Closed), - (5, 10, IntervalType.StartClosed) + (5, 10, IntervalType.EndOpen) }; var otherIntervalSet = new IntervalSet(); @@ -1014,7 +1270,7 @@ public void Union_OtherEmpty_ShouldUnionOnlyIntervalSet() { (2, 10), (3, 12, IntervalType.Closed), - (5, 10, IntervalType.StartClosed) + (5, 10, IntervalType.EndOpen) }; // Act @@ -1034,14 +1290,14 @@ public void Union_CurrentEmpty_ShouldUnionOnlyOtherIntervalSet() { (2, 10), (3, 12, IntervalType.Closed), - (5, 10, IntervalType.StartClosed) + (5, 10, IntervalType.EndOpen) }; var expectedIntervals = new Interval[] { (2, 10), (3, 12, IntervalType.Closed), - (5, 10, IntervalType.StartClosed) + (5, 10, IntervalType.EndOpen) }; // Act @@ -1073,13 +1329,13 @@ public void SymmetricExceptWith_HasIntersection_ShouldExceptIntervalSet() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; var exceptIntervalSet = new IntervalSet { - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (25, 30), (33, 35) }; @@ -1104,20 +1360,20 @@ public void IntersectWith_HasIntersection_ShouldIntersectIntervalSet() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; var exceptIntervalSet = new IntervalSet { - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (25, 30), (33, 35) }; var expectedIntervals = new Interval[] { - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), }; // Act @@ -1134,7 +1390,7 @@ public void UnionWith_OtherWithDifferentIntervals_ShouldUnionIntervalSet() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; @@ -1148,7 +1404,7 @@ public void UnionWith_OtherWithDifferentIntervals_ShouldUnionIntervalSet() { (2, 10), (3, 12, IntervalType.Closed), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (25, 30), (33, 35) }; @@ -1167,7 +1423,7 @@ public void UnionWith_OtherWithRepeatingIntervals_ShouldUnionIntervalSetWithoutR var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; @@ -1182,7 +1438,7 @@ public void UnionWith_OtherWithRepeatingIntervals_ShouldUnionIntervalSetWithoutR { (2, 10), (3, 12, IntervalType.Closed), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (25, 30), (33, 35) }; @@ -1201,14 +1457,14 @@ public void IsSubsetOf_AllElementsAreInOtherIntervalSet_ShouldReturnTrue() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; var otherIntervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), (25, 30), (33, 35) @@ -1228,14 +1484,14 @@ public void IsSubsetOf_NotAllElementsAreInOtherIntervalSet_ShouldReturnFalse() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; var otherIntervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (25, 30), (33, 35) }; @@ -1254,7 +1510,7 @@ public void IsSupersetOf_AllOtherElementsAreInIntervalSet_ShouldReturnTrue() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), (25, 30), (33, 35) @@ -1263,7 +1519,7 @@ public void IsSupersetOf_AllOtherElementsAreInIntervalSet_ShouldReturnTrue() var otherIntervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; @@ -1281,7 +1537,7 @@ public void IsSupersetOf_NotAllOtherElementsAreInIntervalSet_ShouldReturnFalse() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (25, 30), (33, 35) }; @@ -1289,7 +1545,7 @@ public void IsSupersetOf_NotAllOtherElementsAreInIntervalSet_ShouldReturnFalse() var otherIntervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed) }; @@ -1307,7 +1563,7 @@ public void Overlaps_HasMatchingIntervals_ShouldReturnTrue() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), (25, 30), (33, 35) @@ -1316,7 +1572,7 @@ public void Overlaps_HasMatchingIntervals_ShouldReturnTrue() var otherIntervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (8, 14, IntervalType.Closed) }; @@ -1334,7 +1590,7 @@ public void Overlaps_HasNoMatchingIntervals_ShouldReturnFalse() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), (25, 30), (33, 35) @@ -1343,7 +1599,7 @@ public void Overlaps_HasNoMatchingIntervals_ShouldReturnFalse() var otherIntervalSet = new IntervalSet { (5, 10), - (5, 10, IntervalType.EndClosed), + (5, 10, IntervalType.StartOpen), (8, 14, IntervalType.Closed) }; @@ -1361,7 +1617,7 @@ public void SetEquals_HasSameIntervals_ShouldReturnTrue() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), (25, 30), (33, 35) @@ -1370,7 +1626,7 @@ public void SetEquals_HasSameIntervals_ShouldReturnTrue() var otherIntervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), (25, 30), (33, 35) @@ -1390,7 +1646,7 @@ public void SetEquals_HasNotSameIntervals_ShouldReturnFalse() var intervalSet = new IntervalSet { (2, 10), - (5, 10, IntervalType.StartClosed), + (5, 10, IntervalType.EndOpen), (3, 12, IntervalType.Closed), (25, 30), (33, 35) @@ -1401,7 +1657,7 @@ public void SetEquals_HasNotSameIntervals_ShouldReturnFalse() (2, 10), (5, 10), (3, 12, IntervalType.Closed), - (25, 30, IntervalType.StartClosed), + (25, 30, IntervalType.EndOpen), (33, 35) }; diff --git a/EasyIntervals.Tests/IntervalTests.cs b/EasyIntervals.Tests/IntervalTests.cs index 9f02d69..1b512f3 100644 --- a/EasyIntervals.Tests/IntervalTests.cs +++ b/EasyIntervals.Tests/IntervalTests.cs @@ -15,8 +15,8 @@ public void Initialization_StartGreaterThanEnd_ShouldThrowArgumentException() [Theory] [InlineData(IntervalType.Open)] - [InlineData(IntervalType.StartClosed)] - [InlineData(IntervalType.EndClosed)] + [InlineData(IntervalType.EndOpen)] + [InlineData(IntervalType.StartOpen)] public void Initialization_EqualLimitsNotClosedIntervalType_ShouldThrowArgumentException(IntervalType intervalType) { var start = 5; @@ -75,8 +75,8 @@ public void Equals_SameIntervalsDifferentMaxEnd_ShouldBeTrue() [Theory] [InlineData(2, 5, null, IntervalType.Open, "(2, 5)")] [InlineData(2, 5, null, IntervalType.Closed, "[2, 5]")] - [InlineData(2, 5, null, IntervalType.EndClosed, "(2, 5]")] - [InlineData(2, 5, null, IntervalType.StartClosed, "[2, 5)")] + [InlineData(2, 5, null, IntervalType.StartOpen, "(2, 5]")] + [InlineData(2, 5, null, IntervalType.EndOpen, "[2, 5)")] [InlineData(2, 5, 10, IntervalType.Open, "(2, 5): 10")] [InlineData(2, 5, 10, IntervalType.Closed, "[2, 5]: 10")] public void ToString_ShouldPrintCorrect(int start, int end, int? value, IntervalType intervalType, string expectedValue) diff --git a/EasyIntervals.Tests/IntervalToolsTests.cs b/EasyIntervals.Tests/IntervalToolsTests.cs index b5e3370..d5897a4 100644 --- a/EasyIntervals.Tests/IntervalToolsTests.cs +++ b/EasyIntervals.Tests/IntervalToolsTests.cs @@ -5,10 +5,10 @@ public class IntervalToolsTests [Fact] public void HasAnyIntersection_IntersectingIntervals_ShouldReturnTrue() { - var start = (2, 5); - var end = (4, 8); + var interval1 = (2, 5); + var interval2 = (4, 8); - var result = IntervalTools.HasAnyIntersection(start, end, Comparer.Default); + var result = IntervalTools.HasAnyIntersection(interval1, interval2, Comparer.Default); result.Should().BeTrue(); } @@ -16,18 +16,42 @@ public void HasAnyIntersection_IntersectingIntervals_ShouldReturnTrue() [Fact] public void HasAnyIntersection_NotIntersectingIntervals_ShouldReturnFalse() { - var start = (2, 5); - var end = (6, 8); + var interval1 = (6, 8); + var interval2 = (2, 5); - var result = IntervalTools.HasAnyIntersection(start, end, Comparer.Default); + var result = IntervalTools.HasAnyIntersection(interval1, interval2, Comparer.Default); + + result.Should().BeFalse(); + } + + [Fact] + public void HasAnyIntersection_IntersectingIntervalsWithCustomComparer_ShouldReturnTrue() + { + var comparer = Comparer.Create((i1, i2) => i2 - i1); + var interval = (5, 2, comparer); + var other = (8, 4, comparer); + + var result = IntervalTools.HasAnyIntersection(interval, other, comparer); + + result.Should().BeTrue(); + } + + [Fact] + public void HasAnyIntersection_NotIntersectingIntervalsWithCustomComparer_ShouldReturnFalse() + { + var comparer = Comparer.Create((i1, i2) => i2 - i1); + var interval = (5, 2, comparer); + var other = (8, 6, comparer); + + var result = IntervalTools.HasAnyIntersection(interval, other, comparer); result.Should().BeFalse(); } [Theory] [InlineData(2, 8, IntervalType.Open, 2, 8, IntervalType.Open)] - [InlineData(2, 8, IntervalType.StartClosed, 2, 8, IntervalType.Open)] - [InlineData(2, 8, IntervalType.EndClosed, 2, 8, IntervalType.Open)] + [InlineData(2, 8, IntervalType.EndOpen, 2, 8, IntervalType.Open)] + [InlineData(2, 8, IntervalType.StartOpen, 2, 8, IntervalType.Open)] public void Covers_IntervalCoversOther_ShouldReturnTrue( int intervalStart, int intervalEnd, IntervalType intervalType, int otherIntervalStart, int otherIntervalEnd, IntervalType otherIntervalType) @@ -42,8 +66,8 @@ public void Covers_IntervalCoversOther_ShouldReturnTrue( [Theory] [InlineData(3, 5, IntervalType.Open, 2, 8, IntervalType.Open)] - [InlineData(2, 8, IntervalType.Open, 2, 8, IntervalType.StartClosed)] - [InlineData(2, 8, IntervalType.Open, 2, 8, IntervalType.EndClosed)] + [InlineData(2, 8, IntervalType.Open, 2, 8, IntervalType.EndOpen)] + [InlineData(2, 8, IntervalType.Open, 2, 8, IntervalType.StartOpen)] public void Covers_IntervalNotCoversOther_ShouldReturnTrue( int intervalStart, int intervalEnd, IntervalType intervalType, int otherIntervalStart, int otherIntervalEnd, IntervalType otherIntervalType) diff --git a/EasyIntervals/Interval.cs b/EasyIntervals/Interval.cs index ff83e97..ed0a57f 100644 --- a/EasyIntervals/Interval.cs +++ b/EasyIntervals/Interval.cs @@ -6,9 +6,9 @@ namespace EasyIntervals; public enum IntervalType { Open = 0, - StartClosed = 1, - EndClosed = 2, - Closed = StartClosed | EndClosed, + EndOpen = 1, + StartOpen = 2, + Closed = EndOpen | StartOpen, } /// @@ -16,11 +16,11 @@ public enum IntervalType /// public struct Interval : IEquatable> { - public Interval(TLimit start, TLimit end, IntervalType type = IntervalType.Open, IComparer? comparer = null) + public Interval(TLimit start, TLimit end, IntervalType type = IntervalType.Closed, IComparer? comparer = null) : this(start, end, default, type, comparer) { } - public Interval(TLimit start, TLimit end, TValue? value, IntervalType type = IntervalType.Open, IComparer? comparer = null) + public Interval(TLimit start, TLimit end, TValue? value, IntervalType type = IntervalType.Closed, IComparer? comparer = null) { comparer ??= Comparer.Default; var startEndComparison = comparer.Compare(start, end); @@ -83,8 +83,8 @@ public bool Equals(Interval other) public override string ToString() { - var startBracket = (Type & IntervalType.StartClosed) == IntervalType.StartClosed ? '[' : '('; - var endBracket = (Type & IntervalType.EndClosed) == IntervalType.EndClosed ? ']' : ')'; + var startBracket = (Type & IntervalType.EndOpen) == IntervalType.EndOpen ? '[' : '('; + var endBracket = (Type & IntervalType.StartOpen) == IntervalType.StartOpen ? ']' : ')'; if (Value is null) { return $"{startBracket}{Start}, {End}{endBracket}"; diff --git a/EasyIntervals/IntervalComparer.cs b/EasyIntervals/IntervalComparer.cs index e872271..0a63692 100644 --- a/EasyIntervals/IntervalComparer.cs +++ b/EasyIntervals/IntervalComparer.cs @@ -22,8 +22,8 @@ public int Compare(Interval int1, Interval int2) return comparison; } - var startType1 = int1.Type & IntervalType.StartClosed; - var startType2 = int2.Type & IntervalType.StartClosed; + var startType1 = int1.Type & IntervalType.EndOpen; + var startType2 = int2.Type & IntervalType.EndOpen; if (startType1 != startType2) { return startType2.CompareTo(startType1); @@ -35,8 +35,8 @@ public int Compare(Interval int1, Interval int2) return endComparison; } - var endType1 = int1.Type & IntervalType.EndClosed; - var endType2 = int2.Type & IntervalType.EndClosed; + var endType1 = int1.Type & IntervalType.StartOpen; + var endType2 = int2.Type & IntervalType.StartOpen; return endType1.CompareTo(endType2); } } \ No newline at end of file diff --git a/EasyIntervals/IntervalExtensions.cs b/EasyIntervals/IntervalExtensions.cs new file mode 100644 index 0000000..b1a1ffa --- /dev/null +++ b/EasyIntervals/IntervalExtensions.cs @@ -0,0 +1,40 @@ +using EasyIntervals; + +public static class IntervalExtensions +{ + /// + /// Checks if interval has any intersection with other interval by intersectionType. + /// + /// + /// + /// + /// + /// intersectionType + /// + public static bool HasIntersection( + this Interval interval, + Interval other, + IntersectionType intersectionType) => HasIntersection(interval, other, intersectionType, Comparer.Default); + + /// + /// Checks if interval has any intersection with other interval by intersectionType and comparer. + /// + /// + /// + /// + /// + /// intersectionType + /// + /// + public static bool HasIntersection( + this Interval interval, + Interval other, + IntersectionType intersectionType, + IComparer comparer) => intersectionType switch + { + IntersectionType.Any => IntervalTools.HasAnyIntersection(interval, other, comparer), + IntersectionType.Cover => IntervalTools.Covers(interval, other, comparer), + IntersectionType.Within => IntervalTools.Covers(other, interval, comparer), + _ => throw new InvalidOperationException("Invalid intersection type.") + }; +} \ No newline at end of file diff --git a/EasyIntervals/IntervalSet.cs b/EasyIntervals/IntervalSet.cs index 5a20fe9..bc5ba8c 100644 --- a/EasyIntervals/IntervalSet.cs +++ b/EasyIntervals/IntervalSet.cs @@ -214,6 +214,61 @@ public bool Remove(TLimit limit) return intervalsToRemove.Select(itv => Remove(itv)).Any(); } + /// + /// Checks if interval has intersection by intersectionType with the set. + /// + /// interval + /// intersectionType + /// true if has intersection; otherwise, false. + public bool HasIntersection( + in Interval interval, IntersectionType intersectionType = IntersectionType.Any) => + HasIntersection(_aaTree.Root, interval, intersectionType); + + /// + /// Checks if any of input intervals has intersection by intersectionType with the set. + /// + /// intervals + /// intersectionType + /// true if has intersection; otherwise, false. + public bool HasIntersection( + IEnumerable> intervals, IntersectionType intersectionType = IntersectionType.Any) => intervals + .Any(itv => HasIntersection(_aaTree.Root, itv, intersectionType)); + + private bool HasIntersection(AATree>.Node? node, + in Interval interval, IntersectionType intersectionType) + { + if (node is null + || _limitComparer.Compare(interval.Start, node.Value.MaxEnd) > 0) + { + return false; + } + + var leftHasIntersection = HasIntersection(node.Left, interval, intersectionType); + if (leftHasIntersection) + { + return true; + } + + if ((intersectionType == IntersectionType.Any && IntervalTools.HasAnyIntersection(interval, node!.Value, _limitComparer)) + || (intersectionType == IntersectionType.Cover && IntervalTools.Covers(interval, node!.Value, _limitComparer)) + || (intersectionType == IntersectionType.Within && IntervalTools.Covers(node!.Value, interval, _limitComparer))) + { + return true; + } + + var endStartComparison = _limitComparer.Compare(interval.End, node!.Value.Start); + if (endStartComparison >= 0) + { + var rightHasIntersection = HasIntersection(node.Right, interval, intersectionType); + if (rightHasIntersection) + { + return true; + } + } + + return false; + } + /// /// Intersects the set with interval by intersectionType. /// @@ -309,9 +364,9 @@ private void ExceptRecursive(AATree>.Node? node, /// interval set with united intervals. public IntervalSet Union(IntervalSet other) { - if (_limitComparer.GetType() != other._limitComparer.GetType()) + if (!AreEqualComparers(_limitComparer, other._limitComparer)) { - throw new ArgumentException("Comparers types differ."); + throw new ArgumentException("The interval set comparer must be of the same type as the comparer of the given other interval set comparer."); } var otherCount = other.Count; diff --git a/EasyIntervals/IntervalTools.cs b/EasyIntervals/IntervalTools.cs index 9925665..0366b5a 100644 --- a/EasyIntervals/IntervalTools.cs +++ b/EasyIntervals/IntervalTools.cs @@ -33,12 +33,12 @@ public static bool HasAnyIntersection( } var overlapsStartEnd = startEndComparison < 0 - || ((interval1.Type & IntervalType.StartClosed) > 0 - && (interval2.Type & IntervalType.EndClosed) > 0 + || ((interval1.Type & IntervalType.EndOpen) > 0 + && (interval2.Type & IntervalType.StartOpen) > 0 && startEndComparison == 0); var overlapsEndStart = endStartComparison > 0 - || ((interval1.Type & IntervalType.EndClosed) > 0 - && (interval2.Type & IntervalType.StartClosed) > 0 + || ((interval1.Type & IntervalType.StartOpen) > 0 + && (interval2.Type & IntervalType.EndOpen) > 0 && endStartComparison == 0); return overlapsStartEnd && overlapsEndStart; } @@ -67,9 +67,9 @@ public static bool Covers(in Interval interval, var startsComparison = comparer.Compare(interval.Start, other.Start); var endsComparison = comparer.Compare(interval.End, other.End); return (startsComparison < 0 - || (startsComparison == 0 && (interval.Type & IntervalType.StartClosed) >= (other.Type & IntervalType.StartClosed))) + || (startsComparison == 0 && (interval.Type & IntervalType.EndOpen) >= (other.Type & IntervalType.EndOpen))) && (endsComparison > 0 - || (endsComparison == 0 && (interval.Type & IntervalType.EndClosed) >= (other.Type & IntervalType.EndClosed))); + || (endsComparison == 0 && (interval.Type & IntervalType.StartOpen) >= (other.Type & IntervalType.StartOpen))); } /// @@ -86,7 +86,7 @@ public static bool Covers(in Interval interval, /// internal static bool Touch(in Interval precedingInterval, in Interval followingInterval, IComparer comparer) => comparer.Compare(precedingInterval.End, followingInterval.Start) == 0 - && ((precedingInterval.Type & IntervalType.EndClosed) | (followingInterval.Type & IntervalType.StartClosed)) > 0; + && ((precedingInterval.Type & IntervalType.StartOpen) | (followingInterval.Type & IntervalType.EndOpen)) > 0; /// /// Merges 2 intervals. @@ -104,14 +104,14 @@ internal static Interval Merge(in Interval 0 - ? followingInterval.Type & IntervalType.EndClosed + ? followingInterval.Type & IntervalType.StartOpen : endComparison < 0 - ? precedingInterval.Type & IntervalType.EndClosed - : (followingInterval.Type | precedingInterval.Type) & IntervalType.EndClosed; + ? precedingInterval.Type & IntervalType.StartOpen + : (followingInterval.Type | precedingInterval.Type) & IntervalType.StartOpen; return ( precedingInterval.Start, endComparison > 0 ? followingInterval.End : precedingInterval.End, @@ -140,14 +140,14 @@ internal static Interval Merge( { var startComparison = comparer.Compare(followingInterval.Start, precedingInterval.Start); var startIntervalType = startComparison == 0 - ? (followingInterval.Type | precedingInterval.Type) & IntervalType.StartClosed - : precedingInterval.Type & IntervalType.StartClosed; + ? (followingInterval.Type | precedingInterval.Type) & IntervalType.EndOpen + : precedingInterval.Type & IntervalType.EndOpen; var endComparison = comparer.Compare(followingInterval.End, precedingInterval.End); var endIntervalType = endComparison > 0 - ? followingInterval.Type & IntervalType.EndClosed + ? followingInterval.Type & IntervalType.StartOpen : endComparison < 0 - ? precedingInterval.Type & IntervalType.EndClosed - : (followingInterval.Type | precedingInterval.Type) & IntervalType.EndClosed; + ? precedingInterval.Type & IntervalType.StartOpen + : (followingInterval.Type | precedingInterval.Type) & IntervalType.StartOpen; return ( precedingInterval.Start, endComparison > 0 ? followingInterval.End : precedingInterval.End, diff --git a/README.md b/README.md index 25250d6..e68c9ba 100644 --- a/README.md +++ b/README.md @@ -32,45 +32,43 @@ dotnet add package EasyIntervals ### Interval Basics -An Interval can be created with start, end and optional value input parameters. By default type of interval is **Open**. Interval type can be **Closed**, **StartClosed**, **EndClosed** and **Open**. +An Interval can be created with start, end and optional value input parameters. By default type of interval is **Open**. Interval type can be **Closed**, **EndOpen**, **StartOpen** and **Open**. ```CSharp -var interval1 = new Interval(10, 50); // open (10, 50) - excludes both limits -var interval2 = new Interval(10, 50, IntervalType.Closed); // closed [10, 50] - includes both limits -var interval3 = new Interval(10, 50, IntervalType.StartClosed); // start closed [10, 50) - includes start only -var interval4 = new Interval(10, 50, IntervalType.EndClosed); // end closed (10, 50] - includes end only -var interval5 = new Interval(10, 50, 2.5m, IntervalType.Closed); // closed [10, 50], value: 2.5 +var interval1 = new Interval(10, 50); // closed (by default) [10, 50] - includes both limits +var interval2 = new Interval(10, 50, IntervalType.Open); // open (10, 50) - excludes both limits +var interval3 = new Interval(10, 50, IntervalType.EndOpen); // start closed [10, 50) - includes start only +var interval4 = new Interval(10, 50, IntervalType.StartOpen); // end closed (10, 50] - includes end only +var interval5 = new Interval(10, 50, 2.5m); // closed [10, 50], value: 2.5 ``` Interval can also be created from value tuple: ```CSharp -Interval interval1 = (0.1d, 0.5d); // open (0.1, 0.5) -Interval interval2 = (0.1d, 0.5d, IntervalType.Closed); // closed [0.1, 0.5] -Interval interval3 = (0.1d, 0.5d, 2.5m, IntervalType.Closed); // closed [0.1, 0.5], value: 2.5 +Interval interval1 = (0.1d, 0.5d, IntervalType.Open); // open (0.1, 0.5) +Interval interval2 = (0.1d, 0.5d); // closed [0.1, 0.5] +Interval interval3 = (0.1d, 0.5d, 2.5m); // closed [0.1, 0.5], value: 2.5 ``` When no value is passed to create an Interval, a default value is assigned: ```CSharp // passing value -Interval interval1 = (0.1d, 0.5d, 2.5m); // open (0.1, 0.5), value: 2.5 +Interval interval1 = (0.1d, 0.5d, 2.5m, IntervalType.Open); // open (0.1, 0.5), value: 2.5 // not passing value -Interval interval2 = (0.1d, 0.5d); // open (0.1, 0.5), value: null -Interval interval3 = (0.1d, 0.5d); // open (0.1, 0.5), value: 0.0 +Interval interval2 = (0.1d, 0.5d, IntervalType.Open); // open (0.1, 0.5), value: null +Interval interval3 = (0.1d, 0.5d); // closed [0.1, 0.5], value: 0.0 ``` -Operations over specific intervals is done through the functions of **IntervalTools** class. - Here is an example how to check if 2 intervals intersect: ```CSharp -var result1 = IntervalTools.HasAnyIntersection((10, 20), (18, 30)); +var result1 = (10, 20).HasIntersection((18, 30)); Console.WriteLine(result1); // True -var result2 = IntervalTools.HasAnyIntersection((10, 20), (22, 30)); +var result2 = (10, 20).HasIntersection((22, 30)); Console.WriteLine(result2); // False ``` @@ -78,11 +76,11 @@ Console.WriteLine(result2); Here is an example how to check if an interval covers another interval: ```CSharp -var result1 = IntervalTools.Covers(interval: (10, 20), other: (12, 18)); +var result1 = (10, 20).HasIntersection((12, 18), IntersectionType.Cover) // Check if [10, 20] covers [12, 18] Console.WriteLine(result1); // True -var result2 = IntervalTools.Covers(interval: (10, 20), other: (10, 30)); +var result2 = (10, 20).HasIntersection((10, 30), IntersectionType.Cover) // Check if [10, 20] covers [10, 30] Console.WriteLine(result2); // False ``` @@ -99,9 +97,9 @@ Adding an interval. It returns `true` if it's added. When interval with same lim var intervalSet = new IntervalSet(); Console.WriteLine(intervalSet.Add((5, 10, 20.5m))); // True -Console.WriteLine($"[{string.Join(", ", intervalSet)}]"); // [(5, 10): 20.5] +Console.WriteLine($"[{string.Join(", ", intervalSet)}]"); // [[5, 10]: 20.5] Console.WriteLine(intervalSet.Add((5, 10, 25.5m))); // False -Console.WriteLine($"[{string.Join(", ", intervalSet)}]"); // [(5, 10): 20.5] +Console.WriteLine($"[{string.Join(", ", intervalSet)}]"); // [[5, 10]: 20.5] ``` Removing interval. It returns `true` if it's removed. When interval with same limits, type and value exists, it's being removed. @@ -113,7 +111,7 @@ var intervalSet = new IntervalSet() }; Console.WriteLine(intervalSet.Remove((5, 10, 25.5m))); // False -Console.WriteLine($"[{string.Join(", ", intervalSet)}]"); // [(5, 10): 20.5] +Console.WriteLine($"[{string.Join(", ", intervalSet)}]"); // [[5, 10]: 20.5] Console.WriteLine(intervalSet.Remove((5, 10, 20.5m))); // True Console.WriteLine($"[{string.Join(", ", intervalSet)}]"); // [] ``` @@ -125,18 +123,18 @@ Unions all unique intervals from the current and input interval set. If there ar ```CSharp var intervalSet1 = new IntervalSet { - (2, 5), // (2, 5) + (2, 5, IntervalType.Open), // (2, 5) (3, 8, IntervalType.Open), // (3, 8) - (7, 10, 2.5m), // (7, 10): 2.5 + (7, 10, 2.5m, IntervalType.Open), // (7, 10): 2.5 }; var intervalSet2 = new IntervalSet { - (3, 8, IntervalType.Closed), // [3, 8] - (7, 10, 3m), // (7, 10): 3.0 - (11, 16, 15.0m, IntervalType.StartClosed), // [11, 16): 15.0 - (11, 16, 10.0m, IntervalType.StartClosed), // [11, 16): 10.0 - (11, 14, IntervalType.EndClosed), // (11, 14] + (3, 8), // [3, 8] + (7, 10, 3m, IntervalType.Open), // (7, 10): 3.0 + (11, 16, 15.0m, IntervalType.EndOpen), // [11, 16): 15.0 + (11, 16, 10.0m, IntervalType.EndOpen), // [11, 16): 10.0 + (11, 14, IntervalType.StartOpen), // (11, 14] }; var unionIntervalSet = intervalSet1.Union(intervalSet2); @@ -151,18 +149,18 @@ Adds all unique intervals from given input enumeration of intervals. If there ar ```CSharp var intervalSet = new IntervalSet { - (2, 5), // (2, 5) + (2, 5, IntervalType.Open), // (2, 5) (3, 8, IntervalType.Open), // (3, 8) - (7, 10, 2.5m), // (7, 10): 2.5 + (7, 10, 2.5m, IntervalType.Open), // (7, 10): 2.5 }; var inputIntervals = new List> { - (3, 8, IntervalType.Closed), // [3, 8] - (7, 10, 3m), // (7, 10): 3 - (11, 16, 15.0m, IntervalType.StartClosed), // [11, 16): 15.0 - (11, 16, 10.0m, IntervalType.StartClosed), // [11, 16): 10.0 - (11, 14, IntervalType.EndClosed), // (11, 14] + (3, 8), // [3, 8] + (7, 10, 3m, IntervalType.Open), // (7, 10): 3 + (11, 16, 15.0m, IntervalType.EndOpen), // [11, 16): 15.0 + (11, 16, 10.0m, IntervalType.EndOpen), // [11, 16): 10.0 + (11, 14, IntervalType.StartOpen), // (11, 14] }; intervalSet.UnionWith(inputIntervals); @@ -195,12 +193,12 @@ Code: ```CSharp var intervalSet = new IntervalSet { - (2, 5), // (2, 5) + (2, 5, IntervalType.Open), // (2, 5) (3, 8, IntervalType.Open), // (3, 8) - (3, 8, IntervalType.Closed), // [3, 8] - (7, 10), // (7, 10) - (11, 16, IntervalType.StartClosed), // [11, 16) - (11, 14, IntervalType.EndClosed), // (11, 14] + (3, 8), // [3, 8] + (7, 10, IntervalType.Open), // (7, 10) + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen), // (11, 14] }; var intersectionInterval = new Interval(8, 11, IntervalType.Closed); @@ -246,10 +244,10 @@ Code: ```CSharp var intervalSet = new IntervalSet { - (2, 5), // (2, 5) + (2, 5, IntervalType.Open), // (2, 5) (3, 12, IntervalType.Closed), // [3, 12] - (5, 10, IntervalType.StartClosed), // [5, 10) - (11, 16, IntervalType.StartClosed), // [11, 16) + (5, 10, IntervalType.EndOpen), // [5, 10) + (11, 16, IntervalType.EndOpen), // [11, 16) }; var intersectionInterval = new Interval(5, 11, IntervalType.Closed); @@ -285,10 +283,10 @@ Code: ```CSharp var intervalSet = new IntervalSet { - (2, 5), // (2, 5) + (2, 5, IntervalType.Open), // (2, 5) (3, 12, IntervalType.Closed), // [3, 12] - (5, 10, IntervalType.StartClosed), // [5, 10) - (11, 16, IntervalType.StartClosed), // [11, 16) + (5, 10, IntervalType.EndOpen), // [5, 10) + (11, 16, IntervalType.EndOpen), // [11, 16) }; var intersectionInterval = new Interval(5, 11, IntervalType.Closed); @@ -326,12 +324,12 @@ Code: ```CSharp var intervalSet = new IntervalSet { - (2, 5), // (2, 5) + (2, 5, IntervalType.Open), // (2, 5) (3, 8, IntervalType.Open), // (3, 8) (3, 8, IntervalType.Closed), // [3, 8] - (7, 10), // (7, 10) - (11, 16, IntervalType.StartClosed), // [11, 16) - (11, 14, IntervalType.EndClosed), // (11, 14] + (7, 10, IntervalType.Open), // (7, 10) + (11, 16, IntervalType.EndOpen), // [11, 16) + (11, 14, IntervalType.StartOpen), // (11, 14] }; var intersectionInterval = (8, 11, IntervalType.Closed); @@ -366,10 +364,10 @@ Code: ```CSharp var intervalSet = new IntervalSet { - (2, 5), // (2, 5) + (2, 5, IntervalType.Open), // (2, 5) (3, 12, IntervalType.Closed), // [3, 8] - (5, 10, IntervalType.StartClosed), // [5, 10) - (11, 16, IntervalType.StartClosed), // [11, 16) + (5, 10, IntervalType.EndOpen), // [5, 10) + (11, 16, IntervalType.EndOpen), // [11, 16) }; var coveringInterval = new Interval(5, 11, IntervalType.Closed); @@ -405,10 +403,10 @@ Code: ```CSharp var intervalSet = new IntervalSet { - (2, 5), // (2, 5) + (2, 5, IntervalType.Open), // (2, 5) (3, 12, IntervalType.Closed), // [3, 8] - (5, 10, IntervalType.StartClosed), // [5, 10) - (11, 16, IntervalType.StartClosed), // [11, 16) + (5, 10, IntervalType.EndOpen), // [5, 10) + (11, 16, IntervalType.EndOpen), // [11, 16) }; var withinInterval = new Interval(5, 11, IntervalType.Closed); @@ -451,10 +449,10 @@ displayMode: compact --- var intervalSet = new IntervalSet { - (new DateOnly(2023, 09, 2), new DateOnly(2023, 09, 5)), // (2 - 5) - (new DateOnly(2023, 09, 5), new DateOnly(2023, 09, 10), IntervalType.StartClosed), // [5 - 10) + (new DateOnly(2023, 09, 2), new DateOnly(2023, 09, 5), IntervalType.Open), // (2 - 5) + (new DateOnly(2023, 09, 5), new DateOnly(2023, 09, 10), IntervalType.EndOpen), // [5 - 10) (new DateOnly(2023, 09, 12), new DateOnly(2023, 09, 16), IntervalType.Closed), // [12 - 16] - (new DateOnly(2023, 09, 14), new DateOnly(2023, 09, 26), IntervalType.StartClosed) // [14 - 26) + (new DateOnly(2023, 09, 14), new DateOnly(2023, 09, 26), IntervalType.EndOpen) // [14 - 26) }; var mergedIntervalSet = intervalSet.Merge(); @@ -486,10 +484,10 @@ Code: ```CSharp var intervalSet = new IntervalSet { - (new DateOnly(2023, 09, 2), new DateOnly(2023, 09, 5), 2.5m), // (2 - 5) - (new DateOnly(2023, 09, 5), new DateOnly(2023, 09, 10), 2.5m, IntervalType.StartClosed), // [5 - 10) + (new DateOnly(2023, 09, 2), new DateOnly(2023, 09, 5), 2.5m, IntervalType.Open), // (2 - 5) + (new DateOnly(2023, 09, 5), new DateOnly(2023, 09, 10), 2.5m, IntervalType.EndOpen), // [5 - 10) (new DateOnly(2023, 09, 12), new DateOnly(2023, 09, 16), 3.0m, IntervalType.Closed), // [12 - 16] - (new DateOnly(2023, 09, 14), new DateOnly(2023, 09, 26), 5.0m, IntervalType.StartClosed) // [14 - 26) + (new DateOnly(2023, 09, 14), new DateOnly(2023, 09, 26), 5.0m, IntervalType.EndOpen) // [14 - 26) }; var mergedIntervalSet = intervalSet.Merge((itv1, itv2) => itv1.Value + itv2.Value);