Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions csharp/Platform.Collections.Tests/BitStringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,93 @@ public static void BitParallelVectorXorTest()
w.Xor(v);
});
}

[Fact]
public static void AutomaticBorderRefreshingPropertyTest()
{
var bitString = new BitString(100);
Assert.True(bitString.AutomaticBorderRefreshing);

bitString.AutomaticBorderRefreshing = false;
Assert.False(bitString.AutomaticBorderRefreshing);

bitString.AutomaticBorderRefreshing = true;
Assert.True(bitString.AutomaticBorderRefreshing);
}

[Fact]
public static void CreateWithAutomaticBorderRefreshingTest()
{
var automaticBitString = BitString.Create(100, true);
Assert.True(automaticBitString.AutomaticBorderRefreshing);

var manualBitString = BitString.Create(100, false);
Assert.False(manualBitString.AutomaticBorderRefreshing);
}

[Fact]
public static void CreateWithDefaultValueAndAutomaticBorderRefreshingTest()
{
var automaticBitString = BitString.Create(100, false, true);
Assert.True(automaticBitString.AutomaticBorderRefreshing);

var manualBitString = BitString.Create(100, true, false);
Assert.False(manualBitString.AutomaticBorderRefreshing);
}

[Fact]
public static void ManualBorderRefreshTest()
{
var bitString = BitString.Create(1000, false);

// Set some bits
bitString.Set(100, true);
bitString.Set(500, true);
bitString.Set(900, true);

// Manually refresh borders
bool updated = bitString.RefreshBorders();
Assert.True(updated);

// Verify correct borders
Assert.Equal(100, bitString.GetFirstSetBitIndex());
Assert.Equal(900, bitString.GetLastSetBitIndex());
}

[Fact]
public static void AutomaticVsManualBorderBehaviorTest()
{
// Test with automatic border refreshing
var automaticBitString = new BitString(100);
automaticBitString.Set(50, true);
var automaticFirst = automaticBitString.GetFirstSetBitIndex();
var automaticLast = automaticBitString.GetLastSetBitIndex();

// Test with manual border refreshing
var manualBitString = BitString.Create(100, false);
manualBitString.Set(50, true);
manualBitString.RefreshBorders();
var manualFirst = manualBitString.GetFirstSetBitIndex();
var manualLast = manualBitString.GetLastSetBitIndex();

// Results should be the same
Assert.Equal(automaticFirst, manualFirst);
Assert.Equal(automaticLast, manualLast);
Assert.Equal(50, automaticFirst);
Assert.Equal(50, manualFirst);
}

[Fact]
public static void CopyConstructorPreservesAutomaticBorderRefreshingTest()
{
var originalAutomatic = new BitString(100);
var copyAutomatic = new BitString(originalAutomatic);
Assert.True(copyAutomatic.AutomaticBorderRefreshing);

var originalManual = BitString.Create(100, false);
var copyManual = new BitString(originalManual);
Assert.False(copyManual.AutomaticBorderRefreshing);
}
private static void TestToOperationsWithSameMeaning(Action<BitString, BitString, BitString, BitString> test)
{
const int n = 5654;
Expand Down
170 changes: 154 additions & 16 deletions csharp/Platform.Collections/BitString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class BitString : IEquatable<BitString>
private long _length;
private long _minPositiveWord;
private long _maxPositiveWord;
private bool _automaticBorderRefreshing;

/// <summary>
/// <para>
Expand Down Expand Up @@ -91,6 +92,22 @@ public long Length
}
}

/// <summary>
/// <para>
/// Gets or sets the automatic border refreshing value.
/// When enabled, borders (minimum and maximum non-zero words) are automatically updated on every bit change.
/// When disabled, borders need to be manually refreshed using RefreshBorders() method.
/// </para>
/// <para></para>
/// </summary>
public bool AutomaticBorderRefreshing
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _automaticBorderRefreshing;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => _automaticBorderRefreshing = value;
}

#region Constructors

/// <summary>
Expand Down Expand Up @@ -147,6 +164,7 @@ public BitString(BitString other)
_array = new long[GetWordsCountFromIndex(_length)];
_minPositiveWord = other._minPositiveWord;
_maxPositiveWord = other._maxPositiveWord;
_automaticBorderRefreshing = other._automaticBorderRefreshing;
Array.Copy(other._array, _array, _array.LongLength);
}

Expand All @@ -166,6 +184,7 @@ public BitString(long length)
Ensure.Always.ArgumentInRange(length, GetValidLengthRange(), nameof(length));
_length = length;
_array = new long[GetWordsCountFromIndex(_length)];
_automaticBorderRefreshing = true;
MarkBordersAsAllBitsReset();
}

Expand Down Expand Up @@ -193,8 +212,65 @@ public BitString(long length, bool defaultValue)
}
}


#endregion

/// <summary>
/// <para>
/// Creates a new <see cref="BitString"/> instance with specified automatic border refreshing behavior.
/// </para>
/// <para></para>
/// </summary>
/// <param name="length">
/// <para>A length.</para>
/// <para></para>
/// </param>
/// <param name="automaticBorderRefreshing">
/// <para>Whether to automatically refresh borders on every bit change.</para>
/// <para></para>
/// </param>
/// <returns>
/// <para>A new BitString instance.</para>
/// <para></para>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BitString Create(long length, bool automaticBorderRefreshing)
{
var bitString = new BitString(length);
bitString._automaticBorderRefreshing = automaticBorderRefreshing;
return bitString;
}

/// <summary>
/// <para>
/// Creates a new <see cref="BitString"/> instance with specified default value and automatic border refreshing behavior.
/// </para>
/// <para></para>
/// </summary>
/// <param name="length">
/// <para>A length.</para>
/// <para></para>
/// </param>
/// <param name="defaultValue">
/// <para>A default value.</para>
/// <para></para>
/// </param>
/// <param name="automaticBorderRefreshing">
/// <para>Whether to automatically refresh borders on every bit change.</para>
/// <para></para>
/// </param>
/// <returns>
/// <para>A new BitString instance.</para>
/// <para></para>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BitString Create(long length, bool defaultValue, bool automaticBorderRefreshing)
{
var bitString = new BitString(length, defaultValue);
bitString._automaticBorderRefreshing = automaticBorderRefreshing;
return bitString;
}

/// <summary>
/// <para>
/// Nots this instance.
Expand Down Expand Up @@ -894,26 +970,29 @@ static private void VectorXorLoop(long[] array, long[] otherArray, int step, int
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RefreshBordersByWord(long wordIndex)
{
if (_array[wordIndex] == 0)
{
if (wordIndex == _minPositiveWord && wordIndex != _array.LongLength - 1)
{
_minPositiveWord++;
}
if (wordIndex == _maxPositiveWord && wordIndex != 0)
{
_maxPositiveWord--;
}
}
else
if (_automaticBorderRefreshing)
{
if (wordIndex < _minPositiveWord)
if (_array[wordIndex] == 0)
{
_minPositiveWord = wordIndex;
if (wordIndex == _minPositiveWord && wordIndex != _array.LongLength - 1)
{
_minPositiveWord++;
}
if (wordIndex == _maxPositiveWord && wordIndex != 0)
{
_maxPositiveWord--;
}
}
if (wordIndex > _maxPositiveWord)
else
{
_maxPositiveWord = wordIndex;
if (wordIndex < _minPositiveWord)
{
_minPositiveWord = wordIndex;
}
if (wordIndex > _maxPositiveWord)
{
_maxPositiveWord = wordIndex;
}
}
}
}
Expand Down Expand Up @@ -958,6 +1037,65 @@ public bool TryShrinkBorders()
return bordersUpdated;
}

/// <summary>
/// <para>
/// Manually refreshes the borders (minimum and maximum non-zero words).
/// This method recalculates the accurate borders by scanning through the entire array.
/// Use this when AutomaticBorderRefreshing is disabled to update borders when needed.
/// </para>
/// <para></para>
/// </summary>
/// <returns>
/// <para>The borders updated.</para>
/// <para></para>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool RefreshBorders()
{
var oldMinPositiveWord = _minPositiveWord;
var oldMaxPositiveWord = _maxPositiveWord;

// Scan the entire array to find actual borders
long minPositiveWord = _array.LongLength - 1;
long maxPositiveWord = 0;
bool foundAnySet = false;

for (long i = 0; i < _array.LongLength; i++)
{
if (_array[i] != 0)
{
if (!foundAnySet)
{
minPositiveWord = i;
maxPositiveWord = i;
foundAnySet = true;
}
else
{
if (i < minPositiveWord)
{
minPositiveWord = i;
}
if (i > maxPositiveWord)
{
maxPositiveWord = i;
}
}
}
}

if (foundAnySet)
{
SetBorders(minPositiveWord, maxPositiveWord);
}
else
{
MarkBordersAsAllBitsReset();
}

return _minPositiveWord != oldMinPositiveWord || _maxPositiveWord != oldMaxPositiveWord;
}

/// <summary>
/// <para>
/// Determines whether this instance get.
Expand Down
Loading
Loading