Skip to content

Commit 0ac5641

Browse files
Add some more experimental stuff
1 parent 09b8eab commit 0ac5641

2 files changed

Lines changed: 169 additions & 0 deletions

File tree

CommonExtendedObjectsTests/IntArrayTests.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,5 +144,68 @@ public void CompareIntArrayvsIntArrayDotNetPerformance()
144144
Assert.IsTrue(customTime < 2000, $"IntArray took too long: {customTime}ms");
145145
Assert.IsTrue(nativeTime < 2000, $"int[] took too long: {nativeTime}ms");
146146
}
147+
148+
/// <summary>
149+
/// Removes the multiple should remove sequential indices.
150+
/// </summary>
151+
[TestMethod]
152+
public void RemoveMultipleShouldRemoveSequentialIndices()
153+
{
154+
var arr = new IntArray(10);
155+
for (int i = 0; i < arr.Length; i++)
156+
arr[i] = i + 1; // [1..10]
157+
158+
var toRemove = new int[] { 3, 4, 5 }; // remove elements at indices 3,4,5 (4th,5th,6th elements)
159+
160+
var sw = Stopwatch.StartNew();
161+
arr.RemoveMultiple(toRemove);
162+
sw.Stop();
163+
164+
Console.WriteLine($"RemoveMultiple (sequential) took {sw.ElapsedTicks} ticks");
165+
166+
Assert.AreEqual(7, arr.Length);
167+
168+
// Remaining should be: 1,2,3,7,8,9,10 (indices:0,1,2,3,4,5,6)
169+
Assert.AreEqual(1, arr[0]);
170+
Assert.AreEqual(2, arr[1]);
171+
Assert.AreEqual(3, arr[2]);
172+
Assert.AreEqual(7, arr[3]);
173+
Assert.AreEqual(8, arr[4]);
174+
Assert.AreEqual(9, arr[5]);
175+
Assert.AreEqual(10, arr[6]);
176+
177+
arr.Dispose();
178+
}
179+
180+
/// <summary>
181+
/// Removes the multiple should remove non sequential indices.
182+
/// </summary>
183+
[TestMethod]
184+
public void RemoveMultipleShouldRemoveNonSequentialIndices()
185+
{
186+
var arr = new IntArray(10);
187+
for (int i = 0; i < arr.Length; i++)
188+
arr[i] = i + 1; // [1..10]
189+
190+
var toRemove = new int[] { 1, 3, 6 }; // remove elements at indices 1,3,6
191+
192+
var sw = Stopwatch.StartNew();
193+
arr.RemoveMultiple(toRemove);
194+
sw.Stop();
195+
196+
Console.WriteLine($"RemoveMultiple (non-sequential) took {sw.ElapsedTicks} ticks");
197+
198+
Assert.AreEqual(7, arr.Length);
199+
200+
// Remaining elements: indices 0,2,4,5,7,8,9
201+
// Values: 1, 3, 5, 6, 8, 9, 10
202+
int[] expected = { 1, 3, 5, 6, 8, 9, 10 };
203+
for (int i = 0; i < arr.Length; i++)
204+
{
205+
Assert.AreEqual(expected[i], arr[i]);
206+
}
207+
208+
arr.Dispose();
209+
}
147210
}
148211
}

ExtendedSystemObjects/IntArray.cs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// ReSharper disable MemberCanBeInternal
1212

1313
using System;
14+
using System.Collections.Generic;
1415
using System.Runtime.CompilerServices;
1516
using System.Runtime.InteropServices;
1617

@@ -102,6 +103,90 @@ public void RemoveAt(int index)
102103
_length--; // Reduces logical size, but memory is not freed
103104
}
104105

106+
107+
/// <summary>
108+
/// Remove multiple indices from IntArray efficiently
109+
/// Assumes indicesToRemove contains zero or more indices (in any order)
110+
/// Removes all specified indices from the array
111+
/// </summary>
112+
/// <param name="indicesToRemove">The indices to remove.</param>
113+
public void RemoveMultiple(int[] indicesToRemove)
114+
{
115+
if (indicesToRemove == null || indicesToRemove.Length == 0)
116+
return;
117+
118+
// Sort indices ascending (important!)
119+
Array.Sort(indicesToRemove);
120+
121+
// Validate indices within bounds
122+
#if DEBUG
123+
foreach (var idx in indicesToRemove)
124+
if (idx < 0 || idx >= _length)
125+
throw new IndexOutOfRangeException($"Index {idx} is out of range.");
126+
#endif
127+
128+
if (IsSequential(indicesToRemove))
129+
{
130+
// Optimized path for continuous block removal
131+
int start = indicesToRemove[0];
132+
int count = indicesToRemove.Length;
133+
134+
// Move tail elements left by count positions
135+
int tailLength = _length - (start + count);
136+
if (tailLength > 0)
137+
{
138+
Buffer.MemoryCopy(
139+
_ptr + start + count,
140+
_ptr + start,
141+
tailLength * sizeof(int),
142+
tailLength * sizeof(int)
143+
);
144+
}
145+
146+
_length -= count;
147+
}
148+
else
149+
{
150+
// General multi-block removal, no assumption on indices continuity
151+
152+
int srcIndex = 0;
153+
int dstIndex = 0;
154+
155+
foreach (var removeIndex in indicesToRemove)
156+
{
157+
int lengthToCopy = removeIndex - srcIndex;
158+
159+
if (lengthToCopy > 0)
160+
{
161+
Buffer.MemoryCopy(
162+
_ptr + srcIndex,
163+
_ptr + dstIndex,
164+
(_length - dstIndex) * sizeof(int),
165+
lengthToCopy * sizeof(int)
166+
);
167+
dstIndex += lengthToCopy;
168+
}
169+
170+
srcIndex = removeIndex + 1;
171+
}
172+
173+
// Copy remaining tail block after last removed index
174+
int tailLength = _length - srcIndex;
175+
if (tailLength > 0)
176+
{
177+
Buffer.MemoryCopy(
178+
_ptr + srcIndex,
179+
_ptr + dstIndex,
180+
(_length - dstIndex) * sizeof(int),
181+
tailLength * sizeof(int)
182+
);
183+
dstIndex += tailLength;
184+
}
185+
186+
_length = dstIndex;
187+
}
188+
}
189+
105190
/// <summary>
106191
/// Resizes the internal array to the specified new size.
107192
/// Contents will be preserved up to the minimum of old and new size.
@@ -132,6 +217,27 @@ public void Clear()
132217
/// <returns>A <see cref="Span{Int32}"/> over the internal buffer.</returns>
133218
public Span<int> AsSpan() => new((void*)_buffer, _length);
134219

220+
/// <summary>
221+
/// Determines whether the specified sorted indices is sequential.
222+
/// </summary>
223+
/// <param name="sortedIndices">The sorted indices.</param>
224+
/// <returns>
225+
/// <c>true</c> if the specified sorted indices is sequential; otherwise, <c>false</c>.
226+
/// </returns>
227+
private static bool IsSequential(IReadOnlyList<int> sortedIndices)
228+
{
229+
if (sortedIndices is not {Count: > 1})
230+
return true;
231+
232+
for (int i = 1; i < sortedIndices.Count; i++)
233+
{
234+
if (sortedIndices[i] != sortedIndices[i - 1] + 1)
235+
return false;
236+
}
237+
238+
return true;
239+
}
240+
135241
/// <summary>
136242
/// Finalizes an instance of the <see cref="IntArray"/> class.
137243
/// </summary>

0 commit comments

Comments
 (0)