Files

283 lines
6.9 KiB
C#

using System.Numerics;
using FluentAssertions;
using PerfectNumbers.Core.Gpu.Accelerators;
using Xunit;
namespace PerfectNumbers.Core.Tests;
public class ULongExtensionsTests
{
[Theory]
[Trait("Category", "Fast")]
[InlineData(0UL)]
[InlineData(1UL)]
[InlineData(9UL)]
[InlineData(10UL)]
[InlineData(1234567890UL)]
[InlineData(ulong.MaxValue)]
public void Mod10_returns_same_as_operator(ulong value)
{
byte expected = (byte)(value % 10UL);
ULongExtensions.Mod10(value).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(0UL)]
[InlineData(ulong.MaxValue)]
[InlineData(1234567890123456789UL)]
public void Mod128_matches_operator(ulong value)
{
value.Mod128().Should().Be(value % 128UL);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(1UL)]
[InlineData(123456789UL)]
[InlineData(ulong.MaxValue)]
public void Mod10_8_5_3Steps_matches_manual_formula(ulong value)
{
value.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
ulong expected10 = ((value % 10UL) << 1) % 10UL;
ulong expected8 = ((value & 7UL) << 1) & 7UL;
ulong expected5 = ((value % 5UL) << 1) % 5UL;
ulong expected3 = ((value % 3UL) << 1) % 3UL;
step10.Should().Be(expected10);
step8.Should().Be(expected8);
step5.Should().Be(expected5);
step3.Should().Be(expected3);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(2UL, true)]
[InlineData(9UL, false)]
[InlineData(97UL, true)]
public void IsPrimeCandidate_filters_composites(ulong candidate, bool expected)
{
candidate.IsPrimeCandidate().Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(3UL)]
[InlineData(5UL)]
[InlineData(7UL)]
[InlineData(11UL)]
public void CalculateOrderCpu_matches_naive_algorithm(ulong q)
{
ulong expected = NaiveOrder(q);
q.CalculateOrderCpu().Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(3UL)]
[InlineData(5UL)]
[InlineData(7UL)]
[InlineData(11UL)]
public void CalculateOrderGpu_matches_naive_algorithm(ulong q)
{
ulong expected = NaiveOrder(q);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
q.CalculateOrderGpu(gpu).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(2UL, 10UL, 17UL)]
[InlineData(123456789UL, 3UL, 97UL)]
public void ModPow64_matches_BigInteger(ulong value, ulong exponent, ulong modulus)
{
ulong expected = (ulong)BigInteger.ModPow(value, exponent, modulus);
value.ModPow64(exponent, modulus).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(10UL, 17UL)]
[InlineData(123UL, 97UL)]
public void PowMod_returns_same_as_BigInteger(ulong exponent, ulong modulus)
{
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
exponent.PowMod((UInt128)modulus).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(5UL, 7UL, 3UL, 4UL)]
[InlineData(10UL, 7UL, 3UL, 2UL)]
public void PowModWithCycle_uses_cycle_length(ulong exponent, ulong modulus, ulong cycle, ulong expected)
{
((UInt128)exponent).PowModWithCycle((UInt128)modulus, cycle).Should().Be((UInt128)expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(15UL, true)]
[InlineData(7UL, false)]
[InlineData(9UL, false)]
public void SharesFactorWithExponentMinusOneCpu_detects_relationship(ulong exponent, bool expected)
{
exponent.SharesFactorWithExponentMinusOneCpu().Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(15UL, true)]
[InlineData(7UL, false)]
[InlineData(9UL, false)]
public void SharesFactorWithExponentMinusOneGpu_detects_relationship(ulong exponent, bool expected)
{
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
exponent.SharesFactorWithExponentMinusOneGpu(gpu).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(1234567890123456789UL, 3UL)]
[InlineData(ulong.MaxValue, 7UL)]
public void FastDiv64_matches_division(ulong value, ulong divisor)
{
ulong mul = (ulong)(((UInt128)1 << 64) / divisor);
value.FastDiv64(divisor, mul).Should().Be(value / divisor);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(0UL, 0UL)]
[InlineData(ulong.MaxValue, 1UL)]
[InlineData(123456789UL, 987654321UL)]
public void MulHighCpu_matches_high_bits(ulong x, ulong y)
{
ulong expected = (ulong)(((UInt128)x * y) >> 64);
x.MulHighCpu(y).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(0UL, 0UL)]
[InlineData(ulong.MaxValue, 1UL)]
[InlineData(123456789UL, 987654321UL)]
public void MulHighGpu_matches_cpu_baseline(ulong x, ulong y)
{
x.MulHighGpu(y).Should().Be(x.MulHighCpu(y));
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(0UL, 0UL)]
[InlineData(ulong.MaxValue, 2UL)]
[InlineData(123456789UL, 987654321UL)]
public void Mul64_matches_UInt128_product(ulong a, ulong b)
{
UInt128 expected = (UInt128)a * b;
a.Mul64(b).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(123456789UL, 987654321UL, 97UL)]
[InlineData(ulong.MaxValue, ulong.MaxValue, 1009UL)]
public void MulMod64_matches_BigInteger(ulong a, ulong b, ulong modulus)
{
ulong expected = (ulong)BigInteger.Remainder((BigInteger)a * b, modulus);
a.MulMod64(b, modulus).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(100UL, 7UL, 3UL)]
[InlineData(123UL, 9UL, 6UL)]
public void PowModWithCycle_ulong_overload_uses_cycle(ulong exponent, ulong modulus, ulong cycle)
{
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
exponent.PowModWithCycle((UInt128)modulus, cycle).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(100UL, 7UL, 3UL)]
[InlineData(123UL, 9UL, 6UL)]
public void PowModWithCycle_ulong_uint128_cycle_uses_cycle(ulong exponent, ulong modulus, ulong cycle)
{
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
exponent.PowModWithCycle((UInt128)modulus, (UInt128)cycle).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(11UL, 13UL)]
public void PowModCrtCpu_matches_BigInteger(ulong exponent, ulong modulus)
{
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
exponent.PowModCrtCpu((UInt128)modulus).Should().Be(expected);
}
[Theory]
[Trait("Category", "Fast")]
[InlineData(11UL, 13UL)]
public void PowModCrtGpu_matches_BigInteger(ulong exponent, ulong modulus)
{
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
try
{
exponent.PowModCrtGpu(gpu, (UInt128)modulus).Should().Be(expected);
}
finally
{
PrimeOrderCalculatorAccelerator.Return(gpu);
}
}
private static ulong NaiveOrder(ulong q)
{
if (q <= 2UL)
{
return 0UL;
}
ulong order = 1UL;
ulong value = 2UL % q;
while (value != 1UL)
{
value = (value << 1) % q;
order++;
}
return order;
}
}