Add RotateLeft and RotateRight for integer types

This commit is contained in:
Oliver Booth 2022-04-29 23:16:37 +01:00
parent 2922b628f2
commit cacdf148d5
No known key found for this signature in database
GPG Key ID: 32A00B35503AF634
17 changed files with 682 additions and 71 deletions

View File

@ -0,0 +1,42 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Numerics;
namespace X10D.Tests.Numerics;
[TestClass]
public class ByteTests
{
[TestMethod]
public void RotateLeft_ShouldRotateCorrectly()
{
const byte value = 181; // 10110101
const byte expected = 91; // 01011011
Assert.AreEqual(value, value.RotateLeft(0));
Assert.AreEqual(expected, value.RotateLeft(4));
}
[TestMethod]
public void RotateLeft_ShouldModForLargeCount()
{
const byte value = 181; // 10110101
Assert.AreEqual(value, value.RotateLeft(8));
}
[TestMethod]
public void RotateRight_ShouldRotateCorrectly()
{
const byte value = 181; // 10110101
const byte expected = 91; // 01011011
Assert.AreEqual(value, value.RotateRight(0));
Assert.AreEqual(expected, value.RotateRight(4));
}
[TestMethod]
public void RotateRight_ShouldModForLargeCount()
{
const byte value = 181; // 10110101
Assert.AreEqual(value, value.RotateRight(8));
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Numerics;
namespace X10D.Tests.Numerics;
[TestClass]
public class Int16Tests
{
[TestMethod]
public void RotateLeft_ShouldRotateCorrectly()
{
const short value = 2896; // 00001011 01010000
const short expected = 27137; // 01101010 00000001
Assert.AreEqual(value, value.RotateLeft(0));
Assert.AreEqual(expected, value.RotateLeft(5));
Assert.AreEqual(value, value.RotateLeft(16));
}
[TestMethod]
public void RotateLeft_ShouldModForLargeCount()
{
const short value = 2896; // 00001011 01010000
Assert.AreEqual(value, value.RotateLeft(16));
}
[TestMethod]
public void RotateRight_ShouldRotateCorrectly()
{
const short value = 2896; // 00001011 01010000
const short expected = -32678; // 01111111 10100110
Assert.AreEqual(value, value.RotateRight(0));
Assert.AreEqual(expected, value.RotateRight(5));
Assert.AreEqual(value, value.RotateRight(16));
}
[TestMethod]
public void RotateRight_ShouldModForLargeCount()
{
const short value = 2896; // 00001011 01010000
Assert.AreEqual(value, value.RotateRight(16));
}
}

View File

@ -0,0 +1,42 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Numerics;
namespace X10D.Tests.Numerics;
[TestClass]
public class Int32Tests
{
[TestMethod]
public void RotateLeft_ShouldRotateCorrectly()
{
const int value = 284719; // 00000000 00000100 01011000 00101111
const int expected = -1336016888; // 10110000 01011110 00000000 00001000
Assert.AreEqual(value, value.RotateLeft(0));
Assert.AreEqual(expected, value.RotateLeft(17));
}
[TestMethod]
public void RotateLeft_ShouldModForLargeCount()
{
const int value = 284719; // 00000000 00000100 01011000 00101111
Assert.AreEqual(value, value.RotateLeft(32));
}
[TestMethod]
public void RotateRight_ShouldRotateCorrectly()
{
const int value = 284719; // 00000000 00000100 01011000 00101111
const int expected = 739737602; // 00101100 00010111 10000000 00000010
Assert.AreEqual(value, value.RotateRight(0));
Assert.AreEqual(expected, value.RotateRight(17));
}
[TestMethod]
public void RotateRight_ShouldModForLargeCount()
{
const int value = 284719; // 00000000 00000100 01011000 00101111
Assert.AreEqual(value, value.RotateRight(32));
}
}

View File

@ -0,0 +1,42 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Numerics;
namespace X10D.Tests.Numerics;
[TestClass]
public class Int64Tests
{
[TestMethod]
public void RotateLeft_ShouldRotateCorrectly()
{
const long value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100
const long expected = -1588168355691398970; // 11101001 11110101 10110001 01001011 10000011 01111111 01111000 11000110
Assert.AreEqual(value, value.RotateLeft(0));
Assert.AreEqual(expected, value.RotateLeft(42));
}
[TestMethod]
public void RotateLeft_ShouldModForLargeCount()
{
const long value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100
Assert.AreEqual(value, value.RotateLeft(64));
}
[TestMethod]
public void RotateRight_ShouldRotateCorrectly()
{
const long value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100
const long expected = -608990218894919625; // 11110111 10001100 01101110 10011111 01011011 00010100 10111000 00110111
Assert.AreEqual(value, value.RotateRight(0));
Assert.AreEqual(expected, value.RotateRight(42));
}
[TestMethod]
public void RotateRight_ShouldModForLargeCount()
{
const long value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100
Assert.AreEqual(value, value.RotateRight(64));
}
}

View File

@ -0,0 +1,43 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Numerics;
namespace X10D.Tests.Numerics;
[TestClass]
[CLSCompliant(false)]
public class SByteTests
{
[TestMethod]
public void RotateLeft_ShouldRotateCorrectly()
{
const sbyte value = 117; // 01110101
const sbyte expected = 87; // 01010111
Assert.AreEqual(value, value.RotateLeft(0));
Assert.AreEqual(expected, value.RotateLeft(4));
}
[TestMethod]
public void RotateLeft_ShouldModForLargeCount()
{
const sbyte value = 117; // 01110101
Assert.AreEqual(value, value.RotateLeft(8));
}
[TestMethod]
public void RotateRight_ShouldRotateCorrectly()
{
const sbyte value = 117; // 01110101
const sbyte expected = 87; // 01010111
Assert.AreEqual(value, value.RotateRight(0));
Assert.AreEqual(expected, value.RotateRight(4));
}
[TestMethod]
public void RotateRight_ShouldModForLargeCount()
{
const sbyte value = 117; // 01110101
Assert.AreEqual(value, value.RotateRight(8));
}
}

View File

@ -0,0 +1,45 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Numerics;
namespace X10D.Tests.Numerics;
[TestClass]
[CLSCompliant(false)]
public class UInt16Tests
{
[TestMethod]
public void RotateLeft_ShouldRotateCorrectly()
{
const ushort value = 2896; // 00001011 01010000
const ushort expected = 27137; // 01101010 00000001
Assert.AreEqual(value, value.RotateLeft(0));
Assert.AreEqual(expected, value.RotateLeft(5));
Assert.AreEqual(value, value.RotateLeft(16));
}
[TestMethod]
public void RotateLeft_ShouldModForLargeCount()
{
const ushort value = 2896; // 00001011 01010000
Assert.AreEqual(value, value.RotateLeft(16));
}
[TestMethod]
public void RotateRight_ShouldRotateCorrectly()
{
const ushort value = 2896; // 00001011 01010000
const ushort expected = 32858; // 10000000 01011010
Assert.AreEqual(value, value.RotateRight(0));
Assert.AreEqual(expected, value.RotateRight(5));
Assert.AreEqual(value, value.RotateRight(16));
}
[TestMethod]
public void RotateRight_ShouldModForLargeCount()
{
const ushort value = 2896; // 00001011 01010000
Assert.AreEqual(value, value.RotateRight(16));
}
}

View File

@ -0,0 +1,43 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Numerics;
namespace X10D.Tests.Numerics;
[TestClass]
[CLSCompliant(false)]
public class UInt32Tests
{
[TestMethod]
public void RotateLeft_ShouldRotateCorrectly()
{
const uint value = 284719; // 00000000 00000100 01011000 00101111
const uint expected = 2958950408; // 10110000 01011110 00000000 00001000
Assert.AreEqual(value, value.RotateLeft(0));
Assert.AreEqual(expected, value.RotateLeft(17));
}
[TestMethod]
public void RotateLeft_ShouldModForLargeCount()
{
const uint value = 284719; // 00000000 00000100 01011000 00101111
Assert.AreEqual(value, value.RotateLeft(32));
}
[TestMethod]
public void RotateRight_ShouldRotateCorrectly()
{
const uint value = 284719; // 00000000 00000100 01011000 00101111
const uint expected = 739737602; // 00101100 00010111 10000000 00000010
Assert.AreEqual(value, value.RotateRight(0));
Assert.AreEqual(expected, value.RotateRight(17));
}
[TestMethod]
public void RotateRight_ShouldModForLargeCount()
{
const uint value = 284719; // 00000000 00000100 01011000 00101111
Assert.AreEqual(value, value.RotateRight(32));
}
}

View File

@ -0,0 +1,43 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Numerics;
namespace X10D.Tests.Numerics;
[TestClass]
[CLSCompliant(false)]
public class UInt64Tests
{
[TestMethod]
public void RotateLeft_ShouldRotateCorrectly()
{
const ulong value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100
const ulong expected = 16858575718018152646; // 11101001 11110101 10110001 01001011 10000011 01111111 01111000 11000110
Assert.AreEqual(value, value.RotateLeft(0));
Assert.AreEqual(expected, value.RotateLeft(42));
}
[TestMethod]
public void RotateLeft_ShouldModForLargeCount()
{
const ulong value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100
Assert.AreEqual(value, value.RotateLeft(64));
}
[TestMethod]
public void RotateRight_ShouldRotateCorrectly()
{
const ulong value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100
const ulong expected = 17837753854814631991; // 11110111 10001100 01101110 10011111 01011011 00010100 10111000 00110111
Assert.AreEqual(value, value.RotateRight(0));
Assert.AreEqual(expected, value.RotateRight(42));
}
[TestMethod]
public void RotateRight_ShouldModForLargeCount()
{
const ulong value = 5972019251303316844; // 01010010 11100000 11011111 11011110 00110001 10111010 01111101 01101100
Assert.AreEqual(value, value.RotateRight(64));
}
}

View File

@ -1,71 +0,0 @@
using X10D.Math;
namespace X10D;
/// <summary>
/// Extension methods for <see cref="int" />.
/// </summary>
public static class Int32Extensions
{
/// <summary>
/// Performs a circular bitwise left-shift on an integer, such that the most-significant bits that are truncated occupy
/// the least-significant bits.
/// </summary>
/// <param name="value">The value to shift.</param>
/// <param name="shift">The shift amount.</param>
/// <returns>The result of the shift.</returns>
public static int CircularLeftShift(this int value, int shift)
{
shift = shift.Mod(32);
if (shift == 0)
{
return value;
}
var pattern = 0;
for (var i = 0; i < shift; i++)
{
pattern |= 1 << (32 - i);
}
int cache = value & pattern;
cache >>= 32 - shift;
return (value << shift) | cache;
}
/// <summary>
/// Performs a circular bitwise right-shift on an integer, such that the least-significant bits that are truncated occupy
/// the most-significant bits.
/// </summary>
/// <param name="value">The value to shift.</param>
/// <param name="shift">The shift amount.</param>
/// <returns>The result of the shift.</returns>
public static int CircularRightShift(this int value, int shift)
{
shift = shift.Mod(32);
if (shift == 0)
{
return value;
}
var pattern = 0;
for (var i = 0; i < shift; i++)
{
pattern |= 1 << i;
}
int cache = value & pattern;
cache <<= 32 - shift;
return (value >> shift) | cache;
}
/// <summary>
/// Converts the current angle in degrees to its equivalent represented in radians.
/// </summary>
/// <param name="value">The angle in degrees to convert.</param>
/// <returns>The result of π * <paramref name="value" /> / 180.</returns>
public static float DegreesToRadians(this int value)
{
return ((float)value).DegreesToRadians();
}
}

View File

@ -0,0 +1,43 @@
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using X10D.Math;
namespace X10D.Numerics;
/// <summary>
/// Numeric-related extension methods for <see cref="byte" />.
/// </summary>
public static class ByteExtensions
{
/// <summary>
/// Rotates the current value left by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..7] is treated as congruent mod 8.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static byte RotateLeft(this byte value, int count)
{
count = count.Mod(8);
return (byte)((value << count) | (value >> (8 - count)));
}
/// <summary>
/// Rotates the current value right by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..7] is treated as congruent mod 8.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static byte RotateRight(this byte value, int count)
{
count = count.Mod(8);
return (byte)((value >> count) | (value << (8 - count)));
}
}

View File

@ -0,0 +1,42 @@
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
namespace X10D.Numerics;
/// <summary>
/// Numeric-related extension methods for <see cref="short" />.
/// </summary>
public static class Int16Extensions
{
/// <summary>
/// Rotates the current value left by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..15] is treated as congruent mod 16.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static short RotateLeft(this short value, int count)
{
var unsigned = unchecked((ushort)value);
return unchecked((short)unsigned.RotateLeft(count));
}
/// <summary>
/// Rotates the current value right by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..15] is treated as congruent mod 16.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static short RotateRight(this short value, int count)
{
var unsigned = unchecked((ushort)value);
return unchecked((short)unsigned.RotateRight(count));
}
}

View File

@ -0,0 +1,42 @@
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
namespace X10D.Numerics;
/// <summary>
/// Numeric-related extension methods for <see cref="int" />.
/// </summary>
public static class Int32Extensions
{
/// <summary>
/// Rotates the current value left by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..31] is treated as congruent mod 32.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int RotateLeft(this int value, int count)
{
var unsigned = unchecked((uint)value);
return unchecked((int)unsigned.RotateLeft(count));
}
/// <summary>
/// Rotates the current value right by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..31] is treated as congruent mod 32.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int RotateRight(this int value, int count)
{
var unsigned = unchecked((uint)value);
return unchecked((int)unsigned.RotateRight(count));
}
}

View File

@ -0,0 +1,42 @@
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
namespace X10D.Numerics;
/// <summary>
/// Numeric-related extension methods for <see cref="long" />.
/// </summary>
public static class Int64Extensions
{
/// <summary>
/// Rotates the current value left by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..63] is treated as congruent mod 64.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static long RotateLeft(this long value, int count)
{
var unsigned = unchecked((ulong)value);
return unchecked((long)unsigned.RotateLeft(count));
}
/// <summary>
/// Rotates the current value right by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..63] is treated as congruent mod 64.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static long RotateRight(this long value, int count)
{
var unsigned = unchecked((ulong)value);
return unchecked((long)unsigned.RotateRight(count));
}
}

View File

@ -0,0 +1,43 @@
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
namespace X10D.Numerics;
/// <summary>
/// Numeric-related extension methods for <see cref="sbyte" />.
/// </summary>
[CLSCompliant(false)]
public static class SByteExtensions
{
/// <summary>
/// Rotates the current value left by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..7] is treated as congruent mod 8.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static sbyte RotateLeft(this sbyte value, int count)
{
var signed = unchecked((byte)value);
return unchecked((sbyte)signed.RotateLeft(count));
}
/// <summary>
/// Rotates the current value right by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..7] is treated as congruent mod 8.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static sbyte RotateRight(this sbyte value, int count)
{
var signed = unchecked((byte)value);
return unchecked((sbyte)signed.RotateRight(count));
}
}

View File

@ -0,0 +1,42 @@
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace X10D.Numerics;
/// <summary>
/// Numeric-related extension methods for <see cref="ushort" />.
/// </summary>
[CLSCompliant(false)]
public static class UInt16Extensions
{
/// <summary>
/// Rotates the current value left by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..15] is treated as congruent mod 16.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ushort RotateLeft(this ushort value, int count)
{
return (ushort)((ushort)(value << count) | (ushort)(value >> (16 - count)));
}
/// <summary>
/// Rotates the current value right by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..15] is treated as congruent mod 16.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ushort RotateRight(this ushort value, int count)
{
return (ushort)((ushort)(value >> count) | (ushort)(value << (16 - count)));
}
}

View File

@ -0,0 +1,42 @@
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace X10D.Numerics;
/// <summary>
/// Numeric-related extension methods for <see cref="uint" />.
/// </summary>
[CLSCompliant(false)]
public static class UInt32Extensions
{
/// <summary>
/// Rotates the current value left by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..31] is treated as congruent mod 32.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static uint RotateLeft(this uint value, int count)
{
return BitOperations.RotateLeft(value, count);
}
/// <summary>
/// Rotates the current value right by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..31] is treated as congruent mod 32.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static uint RotateRight(this uint value, int count)
{
return BitOperations.RotateRight(value, count);
}
}

View File

@ -0,0 +1,42 @@
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace X10D.Numerics;
/// <summary>
/// Numeric-related extension methods for <see cref="ulong" />.
/// </summary>
[CLSCompliant(false)]
public static class UInt64Extensions
{
/// <summary>
/// Rotates the current value left by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..63] is treated as congruent mod 64.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong RotateLeft(this ulong value, int count)
{
return BitOperations.RotateLeft(value, count);
}
/// <summary>
/// Rotates the current value right by the specified number of bits.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="count">
/// The number of bits by which to rotate. Any value outside the range [0..63] is treated as congruent mod 64.
/// </param>
/// <returns>The rotated value.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static ulong RotateRight(this ulong value, int count)
{
return BitOperations.RotateRight(value, count);
}
}