diff --git a/CHANGELOG.md b/CHANGELOG.md index 08dccfb..03e3f3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - X10D: Added `Point.ToVector2()` - X10D: Added `PointF.ToSizeF()` - X10D: Added `PointF.ToVector2()` for .NET < 6 +- X10D: Added `PopCount()` for built-in integer types - X10D: Added `RoundUpToPowerOf2()` for built-in integer types - X10D: Added `Size.ToPoint()` - X10D: Added `Size.ToPointF()` diff --git a/X10D.Tests/src/Numerics/ByteTests.cs b/X10D.Tests/src/Numerics/ByteTests.cs index c9cf215..b9f875a 100644 --- a/X10D.Tests/src/Numerics/ByteTests.cs +++ b/X10D.Tests/src/Numerics/ByteTests.cs @@ -6,6 +6,24 @@ namespace X10D.Tests.Numerics; [TestClass] public class ByteTests { + [TestMethod] + public void PopCount_ShouldBe0_Given0() + { + Assert.AreEqual(0, ((byte)0).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe5_Given11010101() + { + Assert.AreEqual(5, ((byte)0b11010101).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe8_Given11111111() + { + Assert.AreEqual(8, ((byte)0b11111111).PopCount()); + } + [TestMethod] public void RotateLeft_ShouldRotateCorrectly() { diff --git a/X10D.Tests/src/Numerics/Int16Tests.cs b/X10D.Tests/src/Numerics/Int16Tests.cs index c7bba00..1368cd2 100644 --- a/X10D.Tests/src/Numerics/Int16Tests.cs +++ b/X10D.Tests/src/Numerics/Int16Tests.cs @@ -6,6 +6,24 @@ namespace X10D.Tests.Numerics; [TestClass] public class Int16Tests { + [TestMethod] + public void PopCount_ShouldBe0_Given0() + { + Assert.AreEqual(0, ((short)0).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe5_Given11010101() + { + Assert.AreEqual(5, ((short)0b11010101).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe15_Given0111111111111111() + { + Assert.AreEqual(15, ((short)0b0111111111111111).PopCount()); + } + [TestMethod] public void RotateLeft_ShouldRotateCorrectly() { diff --git a/X10D.Tests/src/Numerics/Int32Tests.cs b/X10D.Tests/src/Numerics/Int32Tests.cs index 40b8116..121ac68 100644 --- a/X10D.Tests/src/Numerics/Int32Tests.cs +++ b/X10D.Tests/src/Numerics/Int32Tests.cs @@ -6,6 +6,24 @@ namespace X10D.Tests.Numerics; [TestClass] public class Int32Tests { + [TestMethod] + public void PopCount_ShouldBe0_Given0() + { + Assert.AreEqual(0, ((uint)0).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe5_Given11010101() + { + Assert.AreEqual(5, ((uint)0b11010101).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe31_Given11111111111111111111111111111111() + { + Assert.AreEqual(31, 0b01111111111111111111111111111111.PopCount()); + } + [TestMethod] public void RotateLeft_ShouldRotateCorrectly() { diff --git a/X10D.Tests/src/Numerics/Int64Tests.cs b/X10D.Tests/src/Numerics/Int64Tests.cs index 06bfe52..d151c6a 100644 --- a/X10D.Tests/src/Numerics/Int64Tests.cs +++ b/X10D.Tests/src/Numerics/Int64Tests.cs @@ -6,6 +6,24 @@ namespace X10D.Tests.Numerics; [TestClass] public class Int64Tests { + [TestMethod] + public void PopCount_ShouldBe0_Given0() + { + Assert.AreEqual(0, 0L.PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe5_Given11010101() + { + Assert.AreEqual(5, 0b11010101L.PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe63_Given0111111111111111111111111111111111111111111111111111111111111111() + { + Assert.AreEqual(63, 0b0111111111111111111111111111111111111111111111111111111111111111L.PopCount()); + } + [TestMethod] public void RotateLeft_ShouldRotateCorrectly() { diff --git a/X10D.Tests/src/Numerics/SByteTests.cs b/X10D.Tests/src/Numerics/SByteTests.cs index dafead3..b16dbb9 100644 --- a/X10D.Tests/src/Numerics/SByteTests.cs +++ b/X10D.Tests/src/Numerics/SByteTests.cs @@ -7,6 +7,24 @@ namespace X10D.Tests.Numerics; [CLSCompliant(false)] public class SByteTests { + [TestMethod] + public void PopCount_ShouldBe0_Given0() + { + Assert.AreEqual(0, ((sbyte)0).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe4_Given01010101() + { + Assert.AreEqual(4, ((sbyte)0b01010101).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe7_Given01111111() + { + Assert.AreEqual(7, ((sbyte)0b01111111).PopCount()); + } + [TestMethod] public void RotateLeft_ShouldRotateCorrectly() { diff --git a/X10D.Tests/src/Numerics/UInt16Tests.cs b/X10D.Tests/src/Numerics/UInt16Tests.cs index 27448c2..98072d1 100644 --- a/X10D.Tests/src/Numerics/UInt16Tests.cs +++ b/X10D.Tests/src/Numerics/UInt16Tests.cs @@ -7,6 +7,24 @@ namespace X10D.Tests.Numerics; [CLSCompliant(false)] public class UInt16Tests { + [TestMethod] + public void PopCount_ShouldBe0_Given0() + { + Assert.AreEqual(0, ((ushort)0).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe5_Given11010101() + { + Assert.AreEqual(5, ((ushort)0b11010101).PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe16_Given1111111111111111() + { + Assert.AreEqual(16, ((ushort)0b1111111111111111).PopCount()); + } + [TestMethod] public void RotateLeft_ShouldRotateCorrectly() { diff --git a/X10D.Tests/src/Numerics/UInt32Tests.cs b/X10D.Tests/src/Numerics/UInt32Tests.cs index 03438ae..dbaae05 100644 --- a/X10D.Tests/src/Numerics/UInt32Tests.cs +++ b/X10D.Tests/src/Numerics/UInt32Tests.cs @@ -7,6 +7,24 @@ namespace X10D.Tests.Numerics; [CLSCompliant(false)] public class UInt32Tests { + [TestMethod] + public void PopCount_ShouldBe0_Given0() + { + Assert.AreEqual(0, 0U.PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe5_Given11010101() + { + Assert.AreEqual(5, 0b11010101U.PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe32_Given11111111111111111111111111111111() + { + Assert.AreEqual(32, 0b11111111111111111111111111111111U.PopCount()); + } + [TestMethod] public void RotateLeft_ShouldRotateCorrectly() { diff --git a/X10D.Tests/src/Numerics/UInt64Tests.cs b/X10D.Tests/src/Numerics/UInt64Tests.cs index c2006f9..41211dc 100644 --- a/X10D.Tests/src/Numerics/UInt64Tests.cs +++ b/X10D.Tests/src/Numerics/UInt64Tests.cs @@ -7,6 +7,24 @@ namespace X10D.Tests.Numerics; [CLSCompliant(false)] public class UInt64Tests { + [TestMethod] + public void PopCount_ShouldBe0_Given0() + { + Assert.AreEqual(0, 0UL.PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe5_Given11010101() + { + Assert.AreEqual(5, 0b11010101UL.PopCount()); + } + + [TestMethod] + public void PopCount_ShouldBe64_Given1111111111111111111111111111111111111111111111111111111111111111() + { + Assert.AreEqual(64, 0b1111111111111111111111111111111111111111111111111111111111111111UL.PopCount()); + } + [TestMethod] public void RotateLeft_ShouldRotateCorrectly() { diff --git a/X10D/src/Numerics/ByteExtensions.cs b/X10D/src/Numerics/ByteExtensions.cs index 6435be4..c6eba5b 100644 --- a/X10D/src/Numerics/ByteExtensions.cs +++ b/X10D/src/Numerics/ByteExtensions.cs @@ -9,6 +9,26 @@ namespace X10D.Numerics; /// public static class ByteExtensions { + /// + /// Returns the population count (number of bits set) of a mask. + /// + /// The mask. + /// The population count of . + /// + /// This method is similar in behavior to the x86 instruction + /// POPCNT + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static int PopCount(this byte value) + { + return ((uint)value).PopCount(); + } + /// /// Rotates the current value left by the specified number of bits. /// diff --git a/X10D/src/Numerics/Int16Extensions.cs b/X10D/src/Numerics/Int16Extensions.cs index db48f91..aaa1005 100644 --- a/X10D/src/Numerics/Int16Extensions.cs +++ b/X10D/src/Numerics/Int16Extensions.cs @@ -8,6 +8,26 @@ namespace X10D.Numerics; /// public static class Int16Extensions { + /// + /// Returns the population count (number of bits set) of a mask. + /// + /// The mask. + /// The population count of . + /// + /// This method is similar in behavior to the x86 instruction + /// POPCNT + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static int PopCount(this short value) + { + return ((uint)value).PopCount(); + } + /// /// Rotates the current value left by the specified number of bits. /// diff --git a/X10D/src/Numerics/Int32Extensions.cs b/X10D/src/Numerics/Int32Extensions.cs index 74ebca4..8fc4937 100644 --- a/X10D/src/Numerics/Int32Extensions.cs +++ b/X10D/src/Numerics/Int32Extensions.cs @@ -8,6 +8,26 @@ namespace X10D.Numerics; /// public static class Int32Extensions { + /// + /// Returns the population count (number of bits set) of a mask. + /// + /// The mask. + /// The population count of . + /// + /// This method is similar in behavior to the x86 instruction + /// POPCNT + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static int PopCount(this int value) + { + return ((uint)value).PopCount(); + } + /// /// Rotates the current value left by the specified number of bits. /// diff --git a/X10D/src/Numerics/Int64Extensions.cs b/X10D/src/Numerics/Int64Extensions.cs index f3a3283..f115e08 100644 --- a/X10D/src/Numerics/Int64Extensions.cs +++ b/X10D/src/Numerics/Int64Extensions.cs @@ -8,6 +8,26 @@ namespace X10D.Numerics; /// public static class Int64Extensions { + /// + /// Returns the population count (number of bits set) of a mask. + /// + /// The mask. + /// The population count of . + /// + /// This method is similar in behavior to the x86 instruction + /// POPCNT + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static int PopCount(this long value) + { + return ((ulong)value).PopCount(); + } + /// /// Rotates the current value left by the specified number of bits. /// diff --git a/X10D/src/Numerics/SByteExtensions.cs b/X10D/src/Numerics/SByteExtensions.cs index 85c393d..bdc1d71 100644 --- a/X10D/src/Numerics/SByteExtensions.cs +++ b/X10D/src/Numerics/SByteExtensions.cs @@ -9,6 +9,26 @@ namespace X10D.Numerics; [CLSCompliant(false)] public static class SByteExtensions { + /// + /// Returns the population count (number of bits set) of a mask. + /// + /// The mask. + /// The population count of . + /// + /// This method is similar in behavior to the x86 instruction + /// POPCNT + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static int PopCount(this sbyte value) + { + return ((uint)value).PopCount(); + } + /// /// Rotates the current value left by the specified number of bits. /// diff --git a/X10D/src/Numerics/UInt16Extensions.cs b/X10D/src/Numerics/UInt16Extensions.cs index af4e344..c79965b 100644 --- a/X10D/src/Numerics/UInt16Extensions.cs +++ b/X10D/src/Numerics/UInt16Extensions.cs @@ -1,4 +1,5 @@ using System.Diagnostics.Contracts; +using System.Numerics; using System.Runtime.CompilerServices; namespace X10D.Numerics; @@ -9,6 +10,26 @@ namespace X10D.Numerics; [CLSCompliant(false)] public static class UInt16Extensions { + /// + /// Returns the population count (number of bits set) of a mask. + /// + /// The mask. + /// The population count of . + /// + /// This method is similar in behavior to the x86 instruction + /// POPCNT + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static int PopCount(this ushort value) + { + return ((uint)value).PopCount(); + } + /// /// Rotates the current value left by the specified number of bits. /// diff --git a/X10D/src/Numerics/UInt32Extensions.cs b/X10D/src/Numerics/UInt32Extensions.cs index 51940af..b02a205 100644 --- a/X10D/src/Numerics/UInt32Extensions.cs +++ b/X10D/src/Numerics/UInt32Extensions.cs @@ -10,6 +10,39 @@ namespace X10D.Numerics; [CLSCompliant(false)] public static class UInt32Extensions { + /// + /// Returns the population count (number of bits set) of a mask. + /// + /// The mask. + /// The population count of . + /// + /// This method is similar in behavior to the x86 instruction + /// POPCNT + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static int PopCount(this uint value) + { +#if NETCOREAPP3_1_OR_GREATER + return BitOperations.PopCount(value); +#else + const uint c1 = 0x_55555555u; + const uint c2 = 0x_33333333u; + const uint c3 = 0x_0F0F0F0Fu; + const uint c4 = 0x_01010101u; + + value -= (value >> 1) & c1; + value = (value & c2) + ((value >> 2) & c2); + value = (((value + (value >> 4)) & c3) * c4) >> 24; + + return (int)value; +#endif + } + /// /// Rotates the current value left by the specified number of bits. /// diff --git a/X10D/src/Numerics/UInt64Extensions.cs b/X10D/src/Numerics/UInt64Extensions.cs index d97c1f7..e4433fb 100644 --- a/X10D/src/Numerics/UInt64Extensions.cs +++ b/X10D/src/Numerics/UInt64Extensions.cs @@ -10,6 +10,39 @@ namespace X10D.Numerics; [CLSCompliant(false)] public static class UInt64Extensions { + /// + /// Returns the population count (number of bits set) of a mask. + /// + /// The mask. + /// The population count of . + /// + /// This method is similar in behavior to the x86 instruction + /// POPCNT + /// + [Pure] +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#else + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] +#endif + public static int PopCount(this ulong value) + { +#if NETCOREAPP3_1_OR_GREATER + return BitOperations.PopCount(value); +#else + const uint c1 = 0x_55555555u; + const uint c2 = 0x_33333333u; + const uint c3 = 0x_0F0F0F0Fu; + const uint c4 = 0x_01010101u; + + value -= (value >> 1) & c1; + value = (value & c2) + ((value >> 2) & c2); + value = (((value + (value >> 4)) & c3) * c4) >> 24; + + return (int)value; +#endif + } + /// /// Rotates the current value left by the specified number of bits. ///