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
118 changes: 118 additions & 0 deletions csharp/Platform.Collections.Tests/BitStringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,124 @@ public static void BitParallelVectorXorTest()
w.Xor(v);
});
}

[Fact]
public static void LengthIncreaseTest()
{
var bitString = new BitString(32);
bitString[5] = true;
bitString[15] = true;
bitString[31] = true;

Assert.Equal(32, bitString.Length);

// Increase length
bitString.Length = 64;
Assert.Equal(64, bitString.Length);

// Original bits should be preserved
Assert.True(bitString[5]);
Assert.True(bitString[15]);
Assert.True(bitString[31]);

// New bits should be false
for (int i = 32; i < 64; i++)
{
Assert.False(bitString[i]);
}
}

[Fact]
public static void LengthDecreaseTest()
{
var bitString = new BitString(64);
bitString[5] = true;
bitString[15] = true;
bitString[25] = true;
bitString[45] = true;
bitString[55] = true;

// Decrease length
bitString.Length = 32;
Assert.Equal(32, bitString.Length);

// Bits within new length should be preserved
Assert.True(bitString[5]);
Assert.True(bitString[15]);
Assert.True(bitString[25]);
}

[Fact]
public static void LengthSetToZeroTest()
{
var bitString = new BitString(32);
bitString[5] = true;
bitString[15] = true;

bitString.Length = 0;
Assert.Equal(0, bitString.Length);
}

[Fact]
public static void LengthCrossWordBoundaryTest()
{
var bitString = new BitString(63); // Just under 64 (1 word)
bitString[62] = true;

// Increase to cross word boundary
bitString.Length = 128; // 2 words
Assert.Equal(128, bitString.Length);
Assert.True(bitString[62]);

// Set bit in second word
bitString[100] = true;
Assert.True(bitString[100]);

// Decrease back
bitString.Length = 63;
Assert.Equal(63, bitString.Length);
Assert.True(bitString[62]);
}

[Fact]
public static void LengthPartialWordMaskingTest()
{
var bitString = new BitString(70); // 1 full word + 6 bits in second word

// Set bits in both words
bitString[63] = true; // Last bit of first word
bitString[64] = true; // First bit of second word
bitString[69] = true; // Last valid bit

Assert.True(bitString[63]);
Assert.True(bitString[64]);
Assert.True(bitString[69]);

// Decrease to middle of second word - should mask off bit 69
bitString.Length = 67; // Should keep first 3 bits of second word

Assert.Equal(67, bitString.Length);
Assert.True(bitString[63]);
Assert.True(bitString[64]);

// Increase back and check that bit 69 is cleared (masked off)
bitString.Length = 70;
Assert.False(bitString[69]); // Should be false because it was masked off
}

[Fact]
public static void LengthSameValueTest()
{
var bitString = new BitString(32);
bitString[5] = true;
bitString[15] = true;

bitString.Length = 32; // Set to same value

Assert.Equal(32, bitString.Length);
Assert.True(bitString[5]);
Assert.True(bitString[15]);
}
private static void TestToOperationsWithSameMeaning(Action<BitString, BitString, BitString, BitString> test)
{
const int n = 5654;
Expand Down
68 changes: 55 additions & 13 deletions csharp/Platform.Collections/BitString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,34 +59,76 @@ public long Length
return;
}
Ensure.Always.ArgumentInRange(value, GetValidLengthRange(), nameof(Length));
// Currently we never shrink the array

if (value > _length)
{
var words = GetWordsCountFromIndex(value);
// Expanding the bit string
var newWords = GetWordsCountFromIndex(value);
var oldWords = GetWordsCountFromIndex(_length);
if (words > _array.LongLength)

if (newWords > _array.LongLength)
{
var copy = new long[words];
// Need to expand the array
var copy = new long[newWords];
Array.Copy(_array, copy, _array.LongLength);
_array = copy;
}
else

// Clear any new words that were added
if (newWords > oldWords)
{
// What is going on here?
Array.Clear(_array, (int)oldWords, (int)(words - oldWords));
Array.Clear(_array, (int)oldWords, (int)(newWords - oldWords));
}
// What is going on here?
var mask = (int)(_length % 64);
if (mask > 0)

// Mask off any extra bits in the last word of the old length
// This ensures that bits beyond the old length are cleared
var oldBitsInLastWord = (int)(_length % 64);
if (oldBitsInLastWord > 0 && oldWords > 0)
{
_array[oldWords - 1] &= (1L << mask) - 1;
var lastOldWordIndex = oldWords - 1;
_array[lastOldWordIndex] &= (1L << oldBitsInLastWord) - 1;
}
}
else
{
// Looks like minimum and maximum positive words are not updated
throw new NotImplementedException();
// Shrinking the bit string
var newWords = GetWordsCountFromIndex(value);
var oldWords = GetWordsCountFromIndex(_length);

// Clear any words that are now beyond the new length
if (newWords < oldWords)
{
Array.Clear(_array, (int)newWords, (int)(oldWords - newWords));
}

// Mask off any extra bits in the last word of the new length
var newBitsInLastWord = (int)(value % 64);
if (newBitsInLastWord > 0 && newWords > 0)
{
var lastNewWordIndex = newWords - 1;
_array[lastNewWordIndex] &= (1L << newBitsInLastWord) - 1;
}
else if (value == 0)
{
// Special case: length is 0, clear everything
if (_array.Length > 0)
{
Array.Clear(_array, 0, (int)Math.Min(_array.Length, oldWords));
}
}

// Update the borders to reflect the new state
if (value == 0)
{
MarkBordersAsAllBitsReset();
}
else
{
// Recalculate borders since we may have cleared some set bits
TryShrinkBorders();
}
}

_length = value;
}
}
Expand Down
149 changes: 149 additions & 0 deletions experiments/LengthPropertyTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System;
using Xunit;
using Platform.Collections;

namespace Platform.Collections.Tests
{
public class LengthPropertyTests
{
[Fact]
public void Length_IncreaseFromZero_ShouldWork()
{
var bitString = new BitString(0);
Assert.Equal(0, bitString.Length);

// Should be able to increase length
bitString.Length = 64;
Assert.Equal(64, bitString.Length);

// All bits should be false initially
for (int i = 0; i < 64; i++)
{
Assert.False(bitString[i]);
}
}

[Fact]
public void Length_IncreaseFromNonZero_ShouldPreserveExistingBits()
{
var bitString = new BitString(32);

// Set some bits
bitString[5] = true;
bitString[15] = true;
bitString[25] = true;

// Increase length
bitString.Length = 64;
Assert.Equal(64, bitString.Length);

// Original bits should be preserved
Assert.True(bitString[5]);
Assert.True(bitString[15]);
Assert.True(bitString[25]);

// New bits should be false
for (int i = 32; i < 64; i++)
{
Assert.False(bitString[i]);
}
}

[Fact]
public void Length_DecreaseLength_ShouldWork()
{
var bitString = new BitString(64);

// Set some bits
bitString[5] = true;
bitString[15] = true;
bitString[25] = true;
bitString[45] = true;
bitString[55] = true;

// Decrease length
bitString.Length = 32;
Assert.Equal(32, bitString.Length);

// Bits within new length should be preserved
Assert.True(bitString[5]);
Assert.True(bitString[15]);
Assert.True(bitString[25]);

// Should not be able to access bits beyond new length
Assert.Throws<ArgumentOutOfRangeException>(() => bitString[45]);
Assert.Throws<ArgumentOutOfRangeException>(() => bitString[55]);
}

[Fact]
public void Length_SetToSameValue_ShouldNotChangeAnything()
{
var bitString = new BitString(32);
bitString[5] = true;
bitString[15] = true;

bitString.Length = 32; // Set to same value

Assert.Equal(32, bitString.Length);
Assert.True(bitString[5]);
Assert.True(bitString[15]);
}

[Fact]
public void Length_CrossWordBoundaries_ShouldWork()
{
var bitString = new BitString(63); // Just under 64 (1 word)
bitString[62] = true;

// Increase to cross word boundary
bitString.Length = 128; // 2 words
Assert.Equal(128, bitString.Length);
Assert.True(bitString[62]);

// Set bit in second word
bitString[100] = true;
Assert.True(bitString[100]);

// Decrease back
bitString.Length = 63;
Assert.Equal(63, bitString.Length);
Assert.True(bitString[62]);
}

[Fact]
public void Length_SetToZero_ShouldWork()
{
var bitString = new BitString(64);
bitString[5] = true;
bitString[15] = true;

bitString.Length = 0;
Assert.Equal(0, bitString.Length);

// Should not be able to access any bits
Assert.Throws<ArgumentOutOfRangeException>(() => bitString[0]);
}

[Fact]
public void Length_HandlePartialWordMasking_ShouldWork()
{
var bitString = new BitString(70); // 1 full word + 6 bits in second word

// Set bits in both words
bitString[63] = true; // Last bit of first word
bitString[64] = true; // First bit of second word
bitString[69] = true; // Last valid bit

// Decrease to middle of second word
bitString.Length = 67; // Should keep first 3 bits of second word

Assert.Equal(67, bitString.Length);
Assert.True(bitString[63]);
Assert.True(bitString[64]);
Assert.Throws<ArgumentOutOfRangeException>(() => bitString[69]);

// The bit at index 69 should be cleared from internal storage
// even though we can't access it anymore
}
}
}
14 changes: 14 additions & 0 deletions experiments/LengthTest/LengthTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ο»Ώ<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../../csharp/Platform.Collections/Platform.Collections.csproj" />
</ItemGroup>

</Project>
Loading
Loading