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.
///