diff --git a/Source/SuperLinq/Move.cs b/Source/SuperLinq/Move.cs index b8623112..066dadc2 100644 --- a/Source/SuperLinq/Move.cs +++ b/Source/SuperLinq/Move.cs @@ -32,21 +32,30 @@ public static partial class SuperEnumerable /// /// This operator uses deferred execution and streams its results. /// - public static IEnumerable Move(this IEnumerable source, int fromIndex, int count, int toIndex) + public static IEnumerable Move( + this IEnumerable source, + int fromIndex, + int count, + int toIndex + ) { ArgumentNullException.ThrowIfNull(source); ArgumentOutOfRangeException.ThrowIfNegative(fromIndex); ArgumentOutOfRangeException.ThrowIfNegative(count); ArgumentOutOfRangeException.ThrowIfNegative(toIndex); - return - toIndex == fromIndex || count == 0 - ? source : - toIndex < fromIndex - ? Core(source, toIndex, fromIndex - toIndex, count) - : Core(source, fromIndex, count, toIndex - fromIndex); + return toIndex == fromIndex || count == 0 + ? source + : toIndex < fromIndex + ? Core(source, toIndex, fromIndex - toIndex, count) + : Core(source, fromIndex, count, toIndex - fromIndex); - static IEnumerable Core(IEnumerable source, int bufferStartIndex, int bufferSize, int bufferYieldIndex) + static IEnumerable Core( + IEnumerable source, + int bufferStartIndex, + int bufferSize, + int bufferYieldIndex + ) { var hasMore = true; bool MoveNext(IEnumerator e) => hasMore && (hasMore = e.MoveNext()); @@ -72,4 +81,199 @@ static IEnumerable Core(IEnumerable source, int bufferStartIndex, int buff yield return e.Current; } } + + /// + /// Returns a sequence with a range of elements in the source sequence moved to a new offset. + /// + /// + /// Type of the source sequence. + /// + /// + /// The source sequence. + /// + /// + /// The range of values to move. + /// + /// + /// The index where the specified range will be moved. + /// + /// A sequence with the specified range moved to the new position. + /// + /// + /// is . + /// + /// + /// 's start is less than 0 or 's end is before start in the sequence. + /// + /// + /// This operator uses deferred executing and streams its results. + /// + public static IEnumerable Move(this IEnumerable source, Range range, Index to) + { + if (!range.Start.IsFromEnd && !range.End.IsFromEnd && !to.IsFromEnd) + { + foreach ( + var e in Move( + source, + range.Start.Value, + range.End.Value - range.Start.Value, + to.Value + ) + ) + { + yield return e; + } + } + else + { + if (source.TryGetCollectionCount() is int count) + { + var startIndex = range.Start.GetOffset(count); + var endIndex = range.End.GetOffset(count); + var toIndex = to.GetOffset(count); + yield return (T)Move(source, startIndex, endIndex - startIndex, toIndex); + } + else + { + switch ((range.Start.IsFromEnd, range.End.IsFromEnd, to.IsFromEnd)) + { + case (false, false, true): + using (var e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + yield break; + } + + var bufferCap = to.Value; + var moveCap = range.End.Value - range.Start.Value; + var buffer = new Queue(bufferCap); + var move = new Queue(moveCap); + + buffer.Enqueue(e.Current); + count = 1; + + while (e.MoveNext()) + { + buffer.Enqueue(e.Current); + checked + { + ++count; + } + if (count > to.Value) + { + var idx = count - bufferCap; + if (idx > range.Start.Value && idx <= range.End.Value) + { + move.Enqueue(buffer.Dequeue()); + } + else + { + yield return buffer.Dequeue(); + } + } + } + + var preQSize = range.Start.Value - to.GetOffset(count); + var preQ = new Queue(Math.Abs(preQSize)); + for (var i = 0; i < preQSize; i++) + preQ.Enqueue(buffer.Dequeue()); + + if (move.Count == 0) + { + for (var i = 0; i < moveCap && buffer.TryDequeue(out var el); i++) + yield return el; + } + else + { + while (move.TryDequeue(out var element)) + yield return element; + } + + while (preQ.TryDequeue(out var el)) + yield return el; + + while (buffer.TryDequeue(out var element)) + yield return element; + } + yield break; + case (false, true, false): + using (var e = source.GetEnumerator()) + { + if (!e.MoveNext()) + { + yield break; + } + + count = 1; + var toMove = new Queue(); + var b = new Queue(range.End.Value); + var min = Math.Min(range.Start.Value, to.Value); + b.Enqueue(e.Current); + + while (e.MoveNext()) + { + if (count <= min) + { + yield return b.Dequeue(); + } + else + { + if (count - min >= range.End.Value) + { + toMove.Enqueue(b.Dequeue()); + } + } + b.Enqueue(e.Current); + checked + { + ++count; + } + } + + var dir = range.Start.Value - to.Value; + for (; dir < 0; dir++) + yield return b.Dequeue(); + + var tmpQ = new Queue(Math.Abs(dir)); + for (; dir > 0; dir--) + { + tmpQ.Enqueue(toMove.Dequeue()); + } + + while (toMove.TryDequeue(out var el)) + yield return el; + + while (tmpQ.TryDequeue(out var el)) + yield return el; + + while (b.TryDequeue(out var el)) + yield return el; + } + break; + case (false, true, true): + // [4, 5, 2, 4, 1, §, 5] Move(1..^4, ^2) + // Optimisitc approach - yield elements until start. + break; + case (true, false, false): + // [4, 5, 2, 4, 1, §, 5] Move(^5..4, 2) + + break; + case (true, false, true): + // [4, 5, 2, 4, 1, §, 5] Move(^5..4, ^2) + break; + case (true, true, false): + if (range.End.Value > range.Start.Value) + { + // Invalid range provided + yield break; + } + break; + case (true, true, true): + // [4, 5, 2, 4, 1, §, 5] Move(^5..^3, ^2) + break; + } + } + } + } } diff --git a/Source/SuperLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt b/Source/SuperLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt index cf38aa4c..bd7a717e 100644 --- a/Source/SuperLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt +++ b/Source/SuperLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt @@ -109,6 +109,7 @@ static SuperLinq.SuperEnumerable.LeftOuterLoopJoin static SuperLinq.SuperEnumerable.LeftOuterLoopJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft Left, TRight? Right)>! static SuperLinq.SuperEnumerable.LeftOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! leftResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.LeftOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft Left, TRight? Right)>! +static SuperLinq.SuperEnumerable.Move(this System.Collections.Generic.IEnumerable! source, System.Range range, System.Index to) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.RightOuterHashJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! rightResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.RightOuterHashJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft? Left, TRight Right)>! static SuperLinq.SuperEnumerable.RightOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! rightResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable! diff --git a/Source/SuperLinq/PublicAPI/net7.0/PublicAPI.Unshipped.txt b/Source/SuperLinq/PublicAPI/net7.0/PublicAPI.Unshipped.txt index cf38aa4c..bd7a717e 100644 --- a/Source/SuperLinq/PublicAPI/net7.0/PublicAPI.Unshipped.txt +++ b/Source/SuperLinq/PublicAPI/net7.0/PublicAPI.Unshipped.txt @@ -109,6 +109,7 @@ static SuperLinq.SuperEnumerable.LeftOuterLoopJoin static SuperLinq.SuperEnumerable.LeftOuterLoopJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft Left, TRight? Right)>! static SuperLinq.SuperEnumerable.LeftOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! leftResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.LeftOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft Left, TRight? Right)>! +static SuperLinq.SuperEnumerable.Move(this System.Collections.Generic.IEnumerable! source, System.Range range, System.Index to) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.RightOuterHashJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! rightResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.RightOuterHashJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft? Left, TRight Right)>! static SuperLinq.SuperEnumerable.RightOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! rightResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable! diff --git a/Source/SuperLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/Source/SuperLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt index cf38aa4c..bd7a717e 100644 --- a/Source/SuperLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/Source/SuperLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -109,6 +109,7 @@ static SuperLinq.SuperEnumerable.LeftOuterLoopJoin static SuperLinq.SuperEnumerable.LeftOuterLoopJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft Left, TRight? Right)>! static SuperLinq.SuperEnumerable.LeftOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! leftResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.LeftOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft Left, TRight? Right)>! +static SuperLinq.SuperEnumerable.Move(this System.Collections.Generic.IEnumerable! source, System.Range range, System.Index to) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.RightOuterHashJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! rightResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.RightOuterHashJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft? Left, TRight Right)>! static SuperLinq.SuperEnumerable.RightOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! rightResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable! diff --git a/Source/SuperLinq/PublicAPI/netcoreapp3.1/PublicAPI.Unshipped.txt b/Source/SuperLinq/PublicAPI/netcoreapp3.1/PublicAPI.Unshipped.txt index cf38aa4c..bd7a717e 100644 --- a/Source/SuperLinq/PublicAPI/netcoreapp3.1/PublicAPI.Unshipped.txt +++ b/Source/SuperLinq/PublicAPI/netcoreapp3.1/PublicAPI.Unshipped.txt @@ -109,6 +109,7 @@ static SuperLinq.SuperEnumerable.LeftOuterLoopJoin static SuperLinq.SuperEnumerable.LeftOuterLoopJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft Left, TRight? Right)>! static SuperLinq.SuperEnumerable.LeftOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! leftResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.LeftOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft Left, TRight? Right)>! +static SuperLinq.SuperEnumerable.Move(this System.Collections.Generic.IEnumerable! source, System.Range range, System.Index to) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.RightOuterHashJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! rightResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable! static SuperLinq.SuperEnumerable.RightOuterHashJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Collections.Generic.IEqualityComparer? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft? Left, TRight Right)>! static SuperLinq.SuperEnumerable.RightOuterMergeJoin(this System.Collections.Generic.IEnumerable! left, System.Collections.Generic.IEnumerable! right, System.Func! leftKeySelector, System.Func! rightKeySelector, System.Func! rightResultSelector, System.Func! bothResultSelector, System.Collections.Generic.IComparer? comparer = null) -> System.Collections.Generic.IEnumerable! diff --git a/Tests/SuperLinq.Test/MoveTest.cs b/Tests/SuperLinq.Test/MoveTest.cs index 5bb076c1..1d8af375 100644 --- a/Tests/SuperLinq.Test/MoveTest.cs +++ b/Tests/SuperLinq.Test/MoveTest.cs @@ -5,22 +5,37 @@ public class MoveTest [Fact] public void MoveWithNegativeFromIndex() { - _ = Assert.Throws(() => - new[] { 1 }.Move(-1, 0, 0)); + _ = Assert.Throws(() => new[] { 1 }.Move(-1, 0, 0)); + } + + [Fact] + public void MoveRangeWithNegativeStartIndex() + { + _ = Assert.Throws(() => new[] { 1 }.Move(-1..-1, 0)); } [Fact] public void MoveWithNegativeCount() { - _ = Assert.Throws(() => - new[] { 1 }.Move(0, -1, 0)); + _ = Assert.Throws(() => new[] { 1 }.Move(0, -1, 0)); + } + + [Fact] + public void MoveRangeWithDecendingRange() + { + _ = Assert.Throws(() => new[] { 1 }.Move(0..-1, 0)); } [Fact] public void MoveWithNegativeToIndex() { - _ = Assert.Throws(() => - new[] { 1 }.Move(0, 0, -1)); + _ = Assert.Throws(() => new[] { 1 }.Move(0, 0, -1)); + } + + [Fact] + public void MoveRangeWithNegativeToIndex() + { + _ = Assert.Throws(() => new[] { 1 }.Move(0..0, -1)); } [Fact] @@ -44,11 +59,25 @@ public void Move(int length, int fromIndex, int count, int toIndex) result.AssertSequenceEqual(expectations); } + [Theory, MemberData(nameof(MoveRangeSource))] + public void MoveRange(int length, Range range, int toIndex) + { + var source = Enumerable.Range(0, length); + + using var test = source.AsTestingSequence(); + + var result = test.Move(range, toIndex); + + var slice = source.Take(range); + var exclude = source.Exclude(range.Start.Value, range.End.Value - range.Start.Value); + var expectations = exclude.Take(toIndex).Concat(slice).Concat(exclude.Skip(toIndex)); + result.AssertSequenceEqual(expectations); + } + public static IEnumerable MoveSource() { const int Length = 10; - return - from index in Enumerable.Range(0, Length) + return from index in Enumerable.Range(0, Length) from count in Enumerable.Range(0, Length + 1) from tcd in new object[][] { @@ -58,8 +87,26 @@ from count in Enumerable.Range(0, Length + 1) select tcd; } + public static IEnumerable MoveRangeSource() + { + const int Length = 10; + return from index in Enumerable.Range(0, Length) + from count in Enumerable.Range(0, Length + 1) + from tcd in new object[][] + { + [Length, index..(index + count), Math.Max(0, index - 1),], + [Length, index..(index + count), index + 1,], + } + select tcd; + } + [Theory, MemberData(nameof(MoveWithSequenceShorterThanToIndexSource))] - public void MoveWithSequenceShorterThanToIndex(int length, int fromIndex, int count, int toIndex) + public void MoveWithSequenceShorterThanToIndex( + int length, + int fromIndex, + int count, + int toIndex + ) { var source = Enumerable.Range(0, length); @@ -67,13 +114,32 @@ public void MoveWithSequenceShorterThanToIndex(int length, int fromIndex, int co var result = test.Move(fromIndex, count, toIndex); - var expectations = source.Exclude(fromIndex, count).Concat(source.Take(fromIndex..(fromIndex + count))); + var expectations = source + .Exclude(fromIndex, count) + .Concat(source.Take(fromIndex..(fromIndex + count))); + Assert.Equal(expectations, result); + } + + [Theory, MemberData(nameof(MoveRangeWithSequenceShorterThanToIndexSource))] + public void MoveRangeWithSequenceShorterThanToIndex(int length, Range range, int toIndex) + { + var source = Enumerable.Range(0, length); + + using var test = source.AsTestingSequence(); + + var result = test.Move(range, toIndex); + + var expectations = source + .Exclude(range.Start.Value, range.End.Value - range.Start.Value) + .Concat(source.Take(range)); Assert.Equal(expectations, result); } public static IEnumerable MoveWithSequenceShorterThanToIndexSource() => - Enumerable.Range(10, 10 + 5) - .Select(toIndex => new object[] { 10, 5, 2, toIndex, }); + Enumerable.Range(10, 10 + 5).Select(toIndex => new object[] { 10, 5, 2, toIndex, }); + + public static IEnumerable MoveRangeWithSequenceShorterThanToIndexSource() => + Enumerable.Range(10, 10 + 5).Select(toIndex => new object[] { 10, 5..7, toIndex, }); [Fact] public void MoveIsRepeatable() @@ -84,6 +150,15 @@ public void MoveIsRepeatable() Assert.Equal(result, result.ToArray()); } + [Fact] + public void MoveRangeIsRepeatable() + { + using var source = Enumerable.Range(0, 10).AsTestingSequence(maxEnumerations: 2); + + var result = source.Move(0..5, 10); + Assert.Equal(result, result.ToArray()); + } + [Fact] public void MoveWithFromIndexEqualsToIndex() { @@ -93,6 +168,15 @@ public void MoveWithFromIndexEqualsToIndex() result.AssertSequenceEqual(Enumerable.Range(0, 10)); } + [Fact] + public void MoveRangeWithFomrIndexEqualsToIndex() + { + using var source = Enumerable.Range(0, 10).AsTestingSequence(); + + var result = source.Move(5..1004, 5); + result.AssertSequenceEqual(Enumerable.Range(0, 10)); + } + [Fact] public void MoveWithCountEqualsZero() { @@ -101,4 +185,45 @@ public void MoveWithCountEqualsZero() var result = source.Move(5, 0, 999); result.AssertSequenceEqual(Enumerable.Range(0, 10)); } + + [Fact] + public void MoveRngeWithCountEqualsZero() + { + using var source = Enumerable.Range(0, 10).AsTestingSequence(); + + var result = source.Move(5..5, 999); + result.AssertSequenceEqual(Enumerable.Range(0, 10)); + } + + [Fact] + public void MoveRangeFromEndIndex_Forward() + { + using var source = Enumerable.Range(0, 8).AsTestingSequence(); + var result = source.Move(1..4, ^3); + result.AssertSequenceEqual([0, 4, 1, 2, 3, 5, 6, 7]); + } + + [Fact] + public void MoveRangeFromEndIndex_Backward() + { + using var source = Enumerable.Range(0, 10).AsTestingSequence(); + var result = source.Move(3..4, ^9); + result.AssertSequenceEqual([0, 3, 1, 2, 4, 5, 6, 7, 8, 9]); + } + + [Fact] + public void MoveRangeWithRangeEndFromEnd_Forward() + { + using var source = Enumerable.Range(0, 8).AsTestingSequence(); + var result = source.Move(1..^4, 2); + result.AssertSequenceEqual([0, 4, 1, 2, 3, 5, 6, 7]); + } + + [Fact] + public void MoveRangeWithRangeEndFromEnd_Backward() + { + using var source = Enumerable.Range(0, 8).AsTestingSequence(); + var result = source.Move(1..^4, 0); + result.AssertSequenceEqual([1, 2, 3, 0, 4, 5, 6, 7]); + } }