Skip to content
Merged
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
36 changes: 35 additions & 1 deletion TryAtSoftware.Extensions.Collections.Tests/BitmaskTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public void BitPositionShouldBeValidated()
}

[Fact]
public void FindLeastSignificantSetBitShouldWorkCorrectlyWithZeroBitmask()
public void FindLeastSignificantSetBitShouldWorkCorrectlyWithBitmaskOfZeros()
{
var bitmask = InstantiateBitmask();
Assert.Equal(-1, bitmask.FindLeastSignificantSetBit());
Expand Down Expand Up @@ -170,6 +170,40 @@ public void FindLeastSignificantSetBitShouldWorkCorrectlyWithRandomBitmask()
}
}

[Fact]
public void FindLeastSignificantUnsetBitShouldWorkCorrectlyWithBitmaskOfOnes()
{
var bitmask = InstantiateBitmask(initializeWithZeros: false);
Assert.Equal(-1, bitmask.FindLeastSignificantUnsetBit());
}

[Fact]
public void FindLeastSignificantUnsetBitShouldWorkCorrectlyInGeneral()
{
var bitmask = InstantiateBitmask(initializeWithZeros: true);
for (var i = bitmask.Length - 1; i >= 0; i--)
{
var result = bitmask.FindLeastSignificantUnsetBit();
Assert.Equal(i, result);

bitmask.Set(i);
}
}

[Fact]
public void FindLeastSignificantUnsetBitShouldWorkCorrectlyWithRandomBitmask()
{
for (var i = 0; i < 100; i++)
{
var bitmask = GenerateBitmask();

var result = bitmask.FindLeastSignificantUnsetBit();
Assert.False(bitmask.IsSet(result));

for (var j = result + 1; j < bitmask.Length; j++) Assert.True(bitmask.IsSet(j));
}
}

[Fact]
public void ToStringShouldReturnCorrectBitmaskRepresentation()
{
Expand Down
22 changes: 21 additions & 1 deletion TryAtSoftware.Extensions.Collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Bitmask notResult2 = ~bitmask2; // 10011010

#### Find the position of the least significant set bit

The `FindLeastSignificantSetBit` method can be used to find the position of the least significant set bit.
The `FindLeastSignificantSetBit` method can be used to find the position of the least significant **set** bit.
If there are no set bits, the returned value will be `-1`.

```C#
Expand All @@ -150,6 +150,26 @@ bitmask.Unset(1); // 00000000
var position3 = bitmask.FindLeastSignificantSetBit(); // -1
```

#### Find the position of the least significant unset bit

The `FindLeastSignificantUnsetBit` method can be used to find the position of the least significant **unset** bit.
If there are no unset bits, the returned value will be `-1`.

```C#
Bitmask bitmask = new Bitmask(8, initializeWithZeros: false);

// Unset the corresponding bits so the second bitmask looks like this: 11101101
bitmask.Unset(3); bitmask.Unset(6);

var position1 = bitmask.FindLeastSignificantUnsetBit(); // 6

bitmask.Set(6); // 11101111
var position2 = bitmask.FindLeastSignificantUnsetBit(); // 3

bitmask.Set(3); // 11111111
var position3 = bitmask.FindLeastSignificantUnsetBit(); // -1
```

## Collection extensions

### `OrEmptyIfNull`
Expand Down
40 changes: 28 additions & 12 deletions TryAtSoftware.Extensions.Collections/Bitmask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,13 @@ public bool IsSet(int position)
/// Use this method to find the position of the least significant (right-most) bit that is set.
/// </summary>
/// <returns>Returns the position of the least significant set bit. Returns -1 if there are no set bits.</returns>
public int FindLeastSignificantSetBit()
{
for (var i = this._segments.Count - 1; i >= 0; i--)
{
var currentSegment = this._segments[i];
if (currentSegment == ZeroSegment) continue;

return (i * BitsPerSegment + BitsPerSegment - (Bits.TrailingZeroCount(currentSegment) + 1));
}
public int FindLeastSignificantSetBit() => this.FindLeastSignificantSetBit(inverse: false);

return -1;
}
/// <summary>
/// Use this method to find the position of the least significant (right-most) bit that is unset.
/// </summary>
/// <returns>Returns the position of the least significant unset bit. Returns -1 if there are no set bits.</returns>
public int FindLeastSignificantUnsetBit() => this.FindLeastSignificantSetBit(inverse: true);

/// <inheritdoc />
public override string ToString()
Expand Down Expand Up @@ -167,6 +162,25 @@ private static Bitmask ExecuteBitwiseOperation(Bitmask a, Bitmask b, Func<ulong,

return result;
}

private int FindLeastSignificantSetBit(bool inverse)
{
for (var i = this._segments.Count - 1; i >= 0; i--)
{
var currentSegment = this._segments[i];
if (inverse)
{
currentSegment = ~currentSegment;
if (i == this._segments.Count - 1) currentSegment = this.ApplyLastSegmentMask(currentSegment);
}

if (currentSegment == ZeroSegment) continue;

return (i * BitsPerSegment + BitsPerSegment - (Bits.TrailingZeroCount(currentSegment) + 1));
}

return -1;
}

private void SetSegment(int index, ulong value)
{
Expand All @@ -185,5 +199,7 @@ private void SetSegment(int index, ulong value)
return (segmentIndex, BitsPerSegment - (bitIndex + 1));
}

private void NormalizeLastSegment() => this._segments[^1] &= this._lastSegmentMask;
private void NormalizeLastSegment() => this._segments[^1] = this.ApplyLastSegmentMask(this._segments[^1]);

private ulong ApplyLastSegmentMask(ulong segment) => segment & this._lastSegmentMask;
}