PrimeOrderCalculatorAccelerator.Rent is now created outside of the loops and re-used for all calculations.
This commit is contained in:
1 parent
57a50e2a37
commit
b9ca95bf8c
53 files changed
+4639
-4229
No files matched your search
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
@@ -7,59 +8,91 @@ namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
[Trait("Category", "Slow")]
|
||||
public class LucasLehmerPrimeTesterTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(3UL, true)]
|
||||
[InlineData(5UL, true)]
|
||||
[InlineData(11UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_gpu_returns_expected_results(ulong p, bool expected)
|
||||
{
|
||||
new MersenneNumberLucasLehmerGpuTester().IsPrime(p, true).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(3UL, true)]
|
||||
[InlineData(5UL, true)]
|
||||
[InlineData(11UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_gpu_returns_expected_results(ulong p, bool expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
new MersenneNumberLucasLehmerGpuTester().IsPrime(gpu, p, true).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_gpu_large_exponent_without_cpu_fallback()
|
||||
{
|
||||
// Use a modest exponent that still exercises the NTT path while keeping
|
||||
// runtime short enough for the unit test environment.
|
||||
const ulong exponent = 128UL;
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
tester.WarmUpNttParameters(exponent);
|
||||
tester.IsPrime(exponent, true).Should().BeFalse();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_gpu_large_exponent_without_cpu_fallback()
|
||||
{
|
||||
// Use a modest exponent that still exercises the NTT path while keeping
|
||||
// runtime short enough for the unit test environment.
|
||||
const ulong exponent = 128UL;
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
tester.WarmUpNttParameters(exponent);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, exponent, true).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrimeBatch_matches_individual()
|
||||
{
|
||||
ulong[] exponents = new ulong[] { 3UL, 5UL, 7UL, 11UL, 13UL, 17UL, 19UL, 23UL };
|
||||
bool[] expected = new bool[] { true, true, true, false, true, true, true, false };
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
bool[] results = new bool[exponents.Length];
|
||||
tester.IsMersennePrimeBatch(exponents, results);
|
||||
results.Should().Equal(expected);
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrimeBatch_matches_individual()
|
||||
{
|
||||
ulong[] exponents = new ulong[] { 3UL, 5UL, 7UL, 11UL, 13UL, 17UL, 19UL, 23UL };
|
||||
bool[] expected = new bool[] { true, true, true, false, true, true, true, false };
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
bool[] results = new bool[exponents.Length];
|
||||
tester.IsMersennePrimeBatch(exponents, results);
|
||||
results.Should().Equal(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(132UL)]
|
||||
[InlineData(1000UL)]
|
||||
[Trait("Category", "Slow")]
|
||||
public void WarmUpNttParameters_populates_cache_for_large_exponent(ulong exponent)
|
||||
{
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
tester.WarmUpNttParameters(exponent);
|
||||
tester.IsPrime(exponent, true).Should().BeFalse();
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(132UL)]
|
||||
[InlineData(1000UL)]
|
||||
[Trait("Category", "Slow")]
|
||||
public void WarmUpNttParameters_populates_cache_for_large_exponent(ulong exponent)
|
||||
{
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
tester.WarmUpNttParameters(exponent);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, exponent, true).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(3UL, true)]
|
||||
[InlineData(5UL, true)]
|
||||
[InlineData(11UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_cpu_matches_expected_results(ulong p, bool expected)
|
||||
{
|
||||
new MersenneNumberLucasLehmerCpuTester().IsPrime(p).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(3UL, true)]
|
||||
[InlineData(5UL, true)]
|
||||
[InlineData(11UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_cpu_matches_expected_results(ulong p, bool expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
new MersenneNumberLucasLehmerCpuTester().IsPrime(gpu, p).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Numerics;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -9,367 +10,383 @@ namespace PerfectNumbers.Core.Tests;
|
||||
[Trait("Category", "Fast")]
|
||||
public class PrimeOrderGpuHeuristicsTests
|
||||
{
|
||||
public PrimeOrderGpuHeuristicsTests()
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.Clear();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.Clear();
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
public PrimeOrderGpuHeuristicsTests()
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.Clear();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.Clear();
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_for_marked_prime()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(1UL, prime, out ulong remainder, divisorData);
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_for_marked_prime()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(1UL, prime, out ulong remainder, divisorData);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_success_for_supported_prime()
|
||||
{
|
||||
const ulong exponent = 138_000_001UL;
|
||||
const ulong prime = 138_000_001UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out ulong remainder, divisorData);
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_success_for_supported_prime()
|
||||
{
|
||||
const ulong exponent = 138_000_001UL;
|
||||
const ulong prime = 138_000_001UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out ulong remainder, divisorData);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
remainder.Should().Be(2UL);
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
remainder.Should().Be(2UL);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(1UL, prime, out ulong remainder, divisorData);
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(1UL, prime, out ulong remainder, divisorData);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_when_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
const ulong prime = 193UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(16UL, prime, out ulong remainder, divisorData);
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_when_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
const ulong prime = 193UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(16UL, prime, out ulong remainder, divisorData);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
|
||||
GpuPow2ModStatus followUp = PrimeOrderGpuHeuristics.TryPow2Mod(4UL, prime, out ulong followUpRemainder, divisorData);
|
||||
followUp.Should().Be(GpuPow2ModStatus.Success);
|
||||
followUpRemainder.Should().Be(16UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
GpuPow2ModStatus followUp = PrimeOrderGpuHeuristics.TryPow2Mod(4UL, prime, out ulong followUpRemainder, divisorData);
|
||||
followUp.Should().Be(GpuPow2ModStatus.Success);
|
||||
followUpRemainder.Should().Be(16UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_for_marked_prime()
|
||||
{
|
||||
const ulong prime = 193UL;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_for_marked_prime()
|
||||
{
|
||||
const ulong prime = 193UL;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
|
||||
ulong[] remainders = new ulong[2];
|
||||
ulong[] exponents = { 1UL, 2UL };
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
ulong[] remainders = new ulong[2];
|
||||
ulong[] exponents = { 1UL, 2UL };
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_success_for_supported_prime()
|
||||
{
|
||||
ulong[] remainders = { 123UL, 456UL };
|
||||
ulong[] exponents = { 138_000_001UL, 138_000_011UL };
|
||||
const ulong prime = 138_000_001UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_success_for_supported_prime()
|
||||
{
|
||||
ulong[] remainders = { 123UL, 456UL };
|
||||
ulong[] exponents = { 138_000_001UL, 138_000_011UL };
|
||||
const ulong prime = 138_000_001UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
|
||||
ulong expectedFirst = (ulong)BigInteger.ModPow(2, exponents[0], prime);
|
||||
ulong expectedSecond = (ulong)BigInteger.ModPow(2, exponents[1], prime);
|
||||
ulong expectedFirst = (ulong)BigInteger.ModPow(2, exponents[0], prime);
|
||||
ulong expectedSecond = (ulong)BigInteger.ModPow(2, exponents[1], prime);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
expectedFirst.Should().Be(2UL);
|
||||
expectedSecond.Should().Be(2048UL);
|
||||
remainders[0].Should().Be(expectedFirst);
|
||||
remainders[1].Should().Be(expectedSecond);
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
expectedFirst.Should().Be(2UL);
|
||||
expectedSecond.Should().Be(2048UL);
|
||||
remainders[0].Should().Be(expectedFirst);
|
||||
remainders[1].Should().Be(expectedSecond);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 1UL, 2UL };
|
||||
ulong[] remainders = { 5UL, 6UL };
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 1UL, 2UL };
|
||||
ulong[] remainders = { 5UL, 6UL };
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_when_any_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 2UL, 16UL };
|
||||
ulong[] remainders = { 9UL, 11UL };
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_when_any_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 2UL, 16UL };
|
||||
ulong[] remainders = { 9UL, 11UL };
|
||||
|
||||
const ulong prime = 193UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
const ulong prime = 193UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_for_marked_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 80) + 7;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide[prime] = 0;
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_for_marked_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 80) + 7;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide[prime] = 0;
|
||||
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(UInt128.One, prime, out UInt128 remainder);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, UInt128.One, prime, out UInt128 remainder);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_success_for_supported_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 96) + 11;
|
||||
UInt128 exponent = ((UInt128)1 << 64) + 3;
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_success_for_supported_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 96) + 11;
|
||||
UInt128 exponent = ((UInt128)1 << 64) + 3;
|
||||
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out UInt128 remainder);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, prime, out UInt128 remainder);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponent, (BigInteger)prime);
|
||||
remainder.Should().Be(expected);
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponent, (BigInteger)prime);
|
||||
remainder.Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("193", "31")]
|
||||
[InlineData("193", "255")]
|
||||
[InlineData("79228162514264337593543950347", "36893488147419103251")]
|
||||
public void Pow2ModKernelCore_matches_cpu_windowed_path(string modulusText, string exponentText)
|
||||
{
|
||||
UInt128 modulus = UInt128.Parse(modulusText);
|
||||
UInt128 exponent = UInt128.Parse(exponentText);
|
||||
[Theory]
|
||||
[InlineData("193", "31")]
|
||||
[InlineData("193", "255")]
|
||||
[InlineData("79228162514264337593543950347", "36893488147419103251")]
|
||||
public void Pow2ModKernelCore_matches_cpu_windowed_path(string modulusText, string exponentText)
|
||||
{
|
||||
UInt128 modulus = UInt128.Parse(modulusText);
|
||||
UInt128 exponent = UInt128.Parse(exponentText);
|
||||
|
||||
var method = typeof(PrimeOrderGpuHeuristics).GetMethod("Pow2ModKernelCore", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
method.Should().NotBeNull();
|
||||
var method = typeof(PrimeOrderGpuHeuristics).GetMethod("Pow2ModKernelCore", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
method.Should().NotBeNull();
|
||||
|
||||
object? gpuResultObject = method!.Invoke(null, new object[] { new GpuUInt128(exponent), new GpuUInt128(modulus) });
|
||||
GpuUInt128 gpuResult = (GpuUInt128)gpuResultObject!;
|
||||
UInt128 expected = exponent.Pow2MontgomeryModWindowed(modulus);
|
||||
object? gpuResultObject = method!.Invoke(null, new object[] { new GpuUInt128(exponent), new GpuUInt128(modulus) });
|
||||
GpuUInt128 gpuResult = (GpuUInt128)gpuResultObject!;
|
||||
UInt128 expected = exponent.Pow2MontgomeryModWindowed(modulus);
|
||||
|
||||
((UInt128)gpuResult).Should().Be(expected);
|
||||
}
|
||||
((UInt128)gpuResult).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 80) + 9;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(UInt128.One, prime, out UInt128 remainder);
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 80) + 9;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, UInt128.One, prime, out UInt128 remainder);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_when_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 4);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
UInt128 exponent = UInt128.One << 8;
|
||||
UInt128 prime = ((UInt128)1 << 70) + 3;
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_when_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 4);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
UInt128 exponent = UInt128.One << 8;
|
||||
UInt128 prime = ((UInt128)1 << 70) + 3;
|
||||
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out UInt128 remainder);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, prime, out UInt128 remainder);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_success_for_supported_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 90) + 19;
|
||||
UInt128[] exponents = { UInt128.One, ((UInt128)1 << 64) + 5 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_success_for_supported_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 90) + 19;
|
||||
UInt128[] exponents = { UInt128.One, ((UInt128)1 << 64) + 5 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
for (int i = 0; i < exponents.Length; i++)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponents[i], (BigInteger)prime);
|
||||
remainders[i].Should().Be(expected);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
for (int i = 0; i < exponents.Length; i++)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponents[i], (BigInteger)prime);
|
||||
remainders[i].Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 88) + 13;
|
||||
UInt128[] exponents = { UInt128.One, (UInt128)5 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders);
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 88) + 13;
|
||||
UInt128[] exponents = { UInt128.One, (UInt128)5 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
remainders[1].Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
remainders[1].Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_overflow_when_any_exponent_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 72) + 23;
|
||||
UInt128[] exponents = { UInt128.One << 5, UInt128.One << 9 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 6);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders);
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_overflow_when_any_exponent_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 72) + 23;
|
||||
UInt128[] exponents = { UInt128.One << 5, UInt128.One << 9 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 6);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
remainders[1].Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
remainders[1].Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
// This will never happen in production code because the caller ensures the remainder span is large enough.
|
||||
// [Fact]
|
||||
// public void TryPow2ModBatch_throws_when_remainder_span_is_too_small()
|
||||
// {
|
||||
// ulong[] remainders = new ulong[1];
|
||||
// ulong[] exponents = { 1UL, 2UL };
|
||||
// This will never happen in production code because the caller ensures the remainder span is large enough.
|
||||
// [Fact]
|
||||
// public void TryPow2ModBatch_throws_when_remainder_span_is_too_small()
|
||||
// {
|
||||
// ulong[] remainders = new ulong[1];
|
||||
// ulong[] exponents = { 1UL, 2UL };
|
||||
|
||||
// Action act = () =>
|
||||
// {
|
||||
// const ulong prime = 5UL;
|
||||
// MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
// PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
// };
|
||||
// Action act = () =>
|
||||
// {
|
||||
// const ulong prime = 5UL;
|
||||
// MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
// PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
// };
|
||||
|
||||
// act.Should().Throw<ArgumentException>();
|
||||
// }
|
||||
// act.Should().Throw<ArgumentException>();
|
||||
// }
|
||||
|
||||
[Fact]
|
||||
public void Calculate_falls_back_to_cpu_when_gpu_overflow_is_marked()
|
||||
{
|
||||
const ulong prime = 7UL;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
[Fact]
|
||||
public void Calculate_falls_back_to_cpu_when_gpu_overflow_is_marked()
|
||||
{
|
||||
const ulong prime = 7UL;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
ulong result = PrimeOrderCalculator.Calculate(prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(prime);
|
||||
ulong result = PrimeOrderCalculator.Calculate(gpu, prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
result.Should().Be(3UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
result.Should().Be(3UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
@@ -6,26 +7,39 @@ namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
[Trait("Category", "Fast")]
|
||||
public class PrimeTesterGpuTests
|
||||
{
|
||||
[Fact]
|
||||
public void IsPrimeGpu_accepts_known_primes()
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
[Fact]
|
||||
public void IsPrimeGpu_accepts_known_primes()
|
||||
{
|
||||
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
||||
foreach (ulong prime in primes)
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(prime).Should().BeTrue();
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(gpu, prime).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsPrimeGpu_rejects_composites()
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
[Fact]
|
||||
public void IsPrimeGpu_rejects_composites()
|
||||
{
|
||||
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
||||
foreach (ulong composite in composites)
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(composite).Should().BeFalse();
|
||||
foreach (ulong composite in composites)
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(gpu, composite).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +1,112 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneDivisorCyclesGenerateGpuTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void GenerateGpu_produces_expected_cycles_for_small_range()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
MersenneDivisorCycles.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0);
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong d = reader.ReadUInt64();
|
||||
ulong c = reader.ReadUInt64();
|
||||
pairs.Add((d, c));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void GenerateGpu_produces_expected_cycles_for_small_range()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MersenneDivisorCycles.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0);
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong d = reader.ReadUInt64();
|
||||
ulong c = reader.ReadUInt64();
|
||||
pairs.Add((d, c));
|
||||
}
|
||||
|
||||
var expected = new List<ulong>();
|
||||
for (ulong d = 3; d <= 50; d++)
|
||||
{
|
||||
if ((d & 1UL) == 0UL || d % 3UL == 0UL || d % 5UL == 0UL || d % 7UL == 0UL || d % 11UL == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var expected = new List<ulong>();
|
||||
for (ulong d = 3; d <= 50; d++)
|
||||
{
|
||||
if ((d & 1UL) == 0UL || d % 3UL == 0UL || d % 5UL == 0UL || d % 7UL == 0UL || d % 11UL == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
expected.Add(d);
|
||||
}
|
||||
expected.Add(d);
|
||||
}
|
||||
|
||||
pairs.Should().HaveCount(expected.Count);
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
expected.Should().Contain(pair.divisor);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor);
|
||||
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(pair.divisor, divisorData));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
pairs.Should().HaveCount(expected.Count);
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
expected.Should().Contain(pair.divisor);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor);
|
||||
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(gpu, pair.divisor, divisorData));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Generate_produces_unique_cycles_for_expected_divisors()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
MersenneDivisorCycles.Generate(path, maxDivisor: 50UL, threads: 4);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Generate_produces_unique_cycles_for_expected_divisors()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MersenneDivisorCycles.Generate(path, maxDivisor: 50UL, threads: 4);
|
||||
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong divisor = reader.ReadUInt64();
|
||||
ulong cycle = reader.ReadUInt64();
|
||||
pairs.Add((divisor, cycle));
|
||||
}
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong divisor = reader.ReadUInt64();
|
||||
ulong cycle = reader.ReadUInt64();
|
||||
pairs.Add((divisor, cycle));
|
||||
}
|
||||
|
||||
var expected = new List<ulong>();
|
||||
for (ulong divisor = 3; divisor <= 50; divisor++)
|
||||
{
|
||||
if ((divisor & 1UL) == 0UL || divisor % 3UL == 0UL || divisor % 5UL == 0UL || divisor % 7UL == 0UL || divisor % 11UL == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var expected = new List<ulong>();
|
||||
for (ulong divisor = 3; divisor <= 50; divisor++)
|
||||
{
|
||||
if ((divisor & 1UL) == 0UL || divisor % 3UL == 0UL || divisor % 5UL == 0UL || divisor % 7UL == 0UL || divisor % 11UL == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
expected.Add(divisor);
|
||||
}
|
||||
expected.Add(divisor);
|
||||
}
|
||||
|
||||
pairs.Should().HaveCount(expected.Count);
|
||||
pairs.Should().HaveCount(expected.Count);
|
||||
|
||||
HashSet<ulong> uniqueDivisors = new();
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
uniqueDivisors.Add(pair.divisor).Should().BeTrue();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor);
|
||||
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(pair.divisor, divisorData));
|
||||
}
|
||||
HashSet<ulong> uniqueDivisors = new();
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
uniqueDivisors.Add(pair.divisor).Should().BeTrue();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(pair.divisor);
|
||||
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(gpu, pair.divisor, divisorData));
|
||||
}
|
||||
|
||||
uniqueDivisors.Should().BeEquivalentTo(expected);
|
||||
uniqueDivisors.Should().BeEquivalentTo(expected);
|
||||
|
||||
var (_, completeCount) = MersenneDivisorCycles.FindLast(path);
|
||||
completeCount.Should().Be(expected.Count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
var (_, completeCount) = MersenneDivisorCycles.FindLast(path);
|
||||
completeCount.Should().Be(expected.Count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,35 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneDivisorCyclesTests
|
||||
{
|
||||
public static IEnumerable<object[]> Divisors() =>
|
||||
new[]
|
||||
{
|
||||
new object[] {3UL, 2UL},
|
||||
new object[] {7UL, 3UL},
|
||||
new object[] {11UL, 10UL},
|
||||
};
|
||||
public static IEnumerable<object[]> Divisors() =>
|
||||
new[]
|
||||
{
|
||||
new object[] {3UL, 2UL},
|
||||
new object[] {7UL, 3UL},
|
||||
new object[] {11UL, 10UL},
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(Divisors))]
|
||||
public void CalculateCycleLength_returns_expected_value(ulong divisor, ulong expected)
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor);
|
||||
MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData).Should().Be(expected);
|
||||
MersenneDivisorCycles.CalculateCycleLengthGpu(divisor).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(Divisors))]
|
||||
public void CalculateCycleLength_returns_expected_value(ulong divisor, ulong expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(divisor);
|
||||
MersenneDivisorCycles.CalculateCycleLength(gpu, divisor, divisorData).Should().Be(expected);
|
||||
MersenneDivisorCycles.CalculateCycleLengthGpu(divisor).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -57,7 +58,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
||||
return DivisorLimit;
|
||||
}
|
||||
|
||||
public bool IsPrime(ulong prime, out bool divisorsExhausted, out ulong divisor)
|
||||
public bool IsPrime(PrimeOrderCalculatorAccelerator gpu, ulong prime, out bool divisorsExhausted, out ulong divisor)
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
if (prime % 7UL == 0UL)
|
||||
@@ -76,7 +77,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
||||
return true;
|
||||
}
|
||||
|
||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu)
|
||||
{
|
||||
return new DummySession();
|
||||
}
|
||||
|
||||
@@ -1,107 +1,122 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberDivisorCpuTesterTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_tracks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(11UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_tracks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(11UL);
|
||||
|
||||
tester.IsPrime(5UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 5UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
|
||||
tester.IsPrime(7UL, out divisorsExhausted, out divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
tester.IsPrime(gpu, 7UL, out divisorsExhausted, out divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
|
||||
tester.IsPrime(11UL, out divisorsExhausted, out divisor).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
tester.IsPrime(gpu, 11UL, out divisorsExhausted, out divisor).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_checks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(13UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_checks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(13UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL));
|
||||
session.CheckDivisor(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL), cycle23, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(gpu, 23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL));
|
||||
session.CheckDivisor(23UL, MontgomeryDivisorDataPool.Shared.FromModulus(23UL), cycle23, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL));
|
||||
session.CheckDivisor(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL), cycle31, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
}
|
||||
}
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(gpu, 31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL));
|
||||
session.CheckDivisor(31UL, MontgomeryDivisorDataPool.Shared.FromModulus(31UL), cycle31, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 7UL, divisorData);
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
}
|
||||
}
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 11UL, divisorData);
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
}
|
||||
}
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,366 +2,440 @@ using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberDivisorGpuTesterTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(33UL, 7UL, true)]
|
||||
[InlineData(35UL, 31UL, true)]
|
||||
[InlineData(37UL, 223UL, true)]
|
||||
[InlineData(33UL, 13UL, false)]
|
||||
[InlineData(35UL, 73UL, false)]
|
||||
[InlineData(37UL, 227UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsDivisible_returns_expected(ulong exponent, ulong divisor, bool expected)
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
ReadOnlyGpuUInt128 divisorValue = new ReadOnlyGpuUInt128(divisor);
|
||||
tester.IsDivisible(exponent, in divisorValue).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(33UL, 7UL, true)]
|
||||
[InlineData(35UL, 31UL, true)]
|
||||
[InlineData(37UL, 223UL, true)]
|
||||
[InlineData(33UL, 13UL, false)]
|
||||
[InlineData(35UL, 73UL, false)]
|
||||
[InlineData(37UL, 227UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsDivisible_returns_expected(ulong exponent, ulong divisor, bool expected)
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
ReadOnlyGpuUInt128 divisorValue = new ReadOnlyGpuUInt128(divisor);
|
||||
tester.IsDivisible(exponent, in divisorValue).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsDivisible_handles_large_divisors()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
UInt128 divisor = (UInt128.One << 65) - UInt128.One;
|
||||
ReadOnlyGpuUInt128 divisorValue = new ReadOnlyGpuUInt128(divisor);
|
||||
tester.IsDivisible(65UL, in divisorValue).Should().BeTrue();
|
||||
divisorValue = new ReadOnlyGpuUInt128(divisor + 2); // Reusing divisorValue for the shifted candidate.
|
||||
tester.IsDivisible(65UL, in divisorValue).Should().BeFalse();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsDivisible_handles_large_divisors()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
UInt128 divisor = (UInt128.One << 65) - UInt128.One;
|
||||
ReadOnlyGpuUInt128 divisorValue = new ReadOnlyGpuUInt128(divisor);
|
||||
tester.IsDivisible(65UL, in divisorValue).Should().BeTrue();
|
||||
divisorValue = new ReadOnlyGpuUInt128(divisor + 2); // Reusing divisorValue for the shifted candidate.
|
||||
tester.IsDivisible(65UL, in divisorValue).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_sets_divisorsExhausted_false_when_search_range_not_exhausted()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
typeof(MersenneNumberDivisorGpuTester)
|
||||
.GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!
|
||||
.SetValue(null, Array.Empty<(ulong, uint)>());
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_sets_divisorsExhausted_false_when_search_range_not_exhausted()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
typeof(MersenneNumberDivisorGpuTester)
|
||||
.GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!
|
||||
.SetValue(null, Array.Empty<(ulong, uint)>());
|
||||
|
||||
tester.IsPrime(31UL, UInt128.Zero, 0UL, out bool divisorsExhausted).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeFalse();
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 31UL, UInt128.Zero, 0UL, out bool divisorsExhausted).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_sets_divisorsExhausted_true_when_divisible_by_specified_divisor()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
tester.IsPrime(35UL, 31UL, 0UL, out bool divisorsExhausted).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_sets_divisorsExhausted_true_when_divisible_by_specified_divisor()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 35UL, 31UL, 0UL, out bool divisorsExhausted).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_accepts_large_search_limits()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
tester.IsPrime(35UL, 31UL, ulong.MaxValue, out bool exhausted).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_tracks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_accepts_large_search_limits()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 35UL, 31UL, ulong.MaxValue, out bool exhausted).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
tester.IsPrime(31UL, out bool exhausted, out ulong divisor).Should().BeTrue();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_tracks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
tester.IsPrime(37UL, out exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 31UL, out bool exhausted, out ulong divisor).Should().BeTrue();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
|
||||
tester.IsPrime(43UL, out exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
tester.IsPrime(gpu, 37UL, out exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_defaults_to_zero_limit_before_configuration()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.IsPrime(gpu, 43UL, out exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
tester.DivisorLimit.Should().Be(0UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_defaults_to_zero_limit_before_configuration()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
|
||||
ulong[] primes = { 31UL, 37UL };
|
||||
ulong[] allowed = new ulong[primes.Length];
|
||||
tester.DivisorLimit.Should().Be(0UL);
|
||||
|
||||
tester.PrepareCandidates(primes, allowed);
|
||||
allowed.Should().OnlyContain(value => value == 0UL);
|
||||
}
|
||||
ulong[] primes = { 31UL, 37UL };
|
||||
ulong[] allowed = new ulong[primes.Length];
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_respects_filter_based_limit()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(41UL);
|
||||
tester.PrepareCandidates(primes, allowed);
|
||||
allowed.Should().OnlyContain(value => value == 0UL);
|
||||
}
|
||||
|
||||
tester.IsPrime(31UL, out _, out ulong divisor).Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
tester.IsPrime(37UL, out bool exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_respects_filter_based_limit()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(41UL);
|
||||
|
||||
tester.IsPrime(41UL, out bool divisorsExhausted, out divisor).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 31UL, out _, out ulong divisor).Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
tester.IsPrime(gpu, 37UL, out bool exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
|
||||
tester.IsPrime(gpu, 41UL, out bool divisorsExhausted, out divisor).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(71UL, false)]
|
||||
[InlineData(89UL, true)]
|
||||
[InlineData(127UL, true)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_matches_incremental_expectations(ulong exponent, bool expectedPrime)
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(exponent);
|
||||
[Theory]
|
||||
[InlineData(71UL, false)]
|
||||
[InlineData(89UL, true)]
|
||||
[InlineData(127UL, true)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_matches_incremental_expectations(ulong exponent, bool expectedPrime)
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(exponent);
|
||||
|
||||
tester.IsPrime(exponent, out bool exhausted, out ulong divisor).Should().Be(expectedPrime);
|
||||
exhausted.Should().BeTrue();
|
||||
if (expectedPrime)
|
||||
{
|
||||
divisor.Should().Be(0UL);
|
||||
}
|
||||
else
|
||||
{
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_tester_uses_cycle_remainders_per_divisor()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 8,
|
||||
};
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, exponent, out bool exhausted, out ulong divisor).Should().Be(expectedPrime);
|
||||
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
exhausted.Should().BeTrue();
|
||||
if (expectedPrime)
|
||||
{
|
||||
divisor.Should().Be(0UL);
|
||||
}
|
||||
else
|
||||
{
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
|
||||
tester.IsPrime(41UL, out bool divisorsExhausted, out ulong divisor).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_tester_uses_cycle_remainders_per_divisor()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 8,
|
||||
};
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_computes_cycle_when_zero_is_provided()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 41UL, out bool divisorsExhausted, out ulong divisor).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), 0UL, primes, hits);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_computes_cycle_when_zero_is_provided()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
}
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_checks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), 0UL, primes, hits);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0 });
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_checks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLength(13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL));
|
||||
session.CheckDivisor(13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL), cycle13367, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
}
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_session_reuses_cycle_remainders_across_batches()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 2,
|
||||
};
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0 });
|
||||
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLength(gpu, 13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL));
|
||||
session.CheckDivisor(13367UL, MontgomeryDivisorDataPool.Shared.FromModulus(13367UL), cycle13367, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_session_reuses_cycle_remainders_across_batches()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 2,
|
||||
};
|
||||
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits);
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
}
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 5,
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 5,
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
}
|
||||
}
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 7UL, divisorData);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 5,
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 5,
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
}
|
||||
}
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(gpu, 11UL, divisorData);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_tester_skips_divisors_excluded_by_small_cycle_generation()
|
||||
{
|
||||
var cycles = MersenneDivisorCycles.Shared;
|
||||
var tableField = typeof(MersenneDivisorCycles).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
var smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
var originalTable = (List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!;
|
||||
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var patchedTable = new List<(ulong Divisor, ulong Cycle)>();
|
||||
ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_tester_skips_divisors_excluded_by_small_cycle_generation()
|
||||
{
|
||||
var cycles = MersenneDivisorCycles.Shared;
|
||||
var tableField = typeof(MersenneDivisorCycles).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
var smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(191UL);
|
||||
patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLength(191UL, divisorData);
|
||||
var originalTable = (List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!;
|
||||
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
|
||||
|
||||
tableField.SetValue(cycles, patchedTable);
|
||||
smallCyclesField.SetValue(cycles, patchedSmall);
|
||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var patchedTable = new List<(ulong Divisor, ulong Cycle)>();
|
||||
ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(19UL);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(191UL);
|
||||
patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLength(gpu, 191UL, divisorData);
|
||||
|
||||
typeof(MersenneNumberDivisorByDivisorGpuTester)
|
||||
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
||||
.SetValue(tester, 200UL);
|
||||
tableField.SetValue(cycles, patchedTable);
|
||||
smallCyclesField.SetValue(cycles, patchedSmall);
|
||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||
|
||||
tester.IsPrime(19UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
tableField.SetValue(cycles, originalTable);
|
||||
smallCyclesField.SetValue(cycles, originalSmall);
|
||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||
}
|
||||
}
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(19UL);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_cpu_session_reuses_cycle_remainders_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester
|
||||
{
|
||||
BatchSize = 2,
|
||||
};
|
||||
typeof(MersenneNumberDivisorByDivisorGpuTester)
|
||||
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
||||
.SetValue(tester, 200UL);
|
||||
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
tester.IsPrime(gpu, 19UL, out bool divisorsExhausted, out ulong divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
tableField.SetValue(cycles, originalTable);
|
||||
smallCyclesField.SetValue(cycles, originalSmall);
|
||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_cpu_session_reuses_cycle_remainders_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester
|
||||
{
|
||||
BatchSize = 2,
|
||||
};
|
||||
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits);
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
}
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorDataPool.Shared.FromModulus(223UL), cycle223, primes, hits);
|
||||
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Return();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -7,30 +8,38 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberIncrementalCpuTesterTests
|
||||
{
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
[InlineData(GpuKernelType.Pow2Mod)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(GpuKernelType type)
|
||||
{
|
||||
var tester = new MersenneNumberIncrementalCpuTester(type);
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
[InlineData(GpuKernelType.Pow2Mod)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(GpuKernelType type)
|
||||
{
|
||||
var tester = new MersenneNumberIncrementalCpuTester(type);
|
||||
|
||||
if (type == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
RunCase(tester, 11UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 71UL, 341_860UL, expectedPrime: false);
|
||||
}
|
||||
if (type == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
RunCase(tester, 11UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 71UL, 341_860UL, expectedPrime: false);
|
||||
}
|
||||
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
|
||||
private static void RunCase(MersenneNumberIncrementalCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
private static void RunCase(MersenneNumberIncrementalCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.Scan(gpu, exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -7,31 +8,39 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberOrderCpuTesterTests
|
||||
{
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
[InlineData(GpuKernelType.Pow2Mod)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(GpuKernelType type)
|
||||
{
|
||||
var tester = new MersenneNumberOrderCpuTester(type);
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
[InlineData(GpuKernelType.Pow2Mod)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(GpuKernelType type)
|
||||
{
|
||||
var tester = new MersenneNumberOrderCpuTester(type);
|
||||
|
||||
if (type == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
RunCase(tester, 23UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 36UL, expectedPrime: false);
|
||||
}
|
||||
if (type == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
RunCase(tester, 23UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 36UL, expectedPrime: false);
|
||||
}
|
||||
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
|
||||
private static void RunCase(MersenneNumberOrderCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
private static void RunCase(MersenneNumberOrderCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.Scan(gpu, exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -7,24 +8,32 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberResidueCpuTesterTests
|
||||
{
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents()
|
||||
{
|
||||
var tester = new MersenneNumberResidueCpuTester();
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents()
|
||||
{
|
||||
var tester = new MersenneNumberResidueCpuTester();
|
||||
|
||||
RunCase(tester, 23UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 36UL, expectedPrime: false);
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
RunCase(tester, 23UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 36UL, expectedPrime: false);
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
|
||||
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.Scan(gpu, exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -7,30 +8,54 @@ namespace PerfectNumbers.Core.Tests;
|
||||
[Trait("Category", "Fast")]
|
||||
public class MersenneNumberTesterTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(136_000_002UL)]
|
||||
[InlineData(136_000_005UL)]
|
||||
public void IsMersennePrime_detects_small_prime_divisors_for_large_exponents(ulong p)
|
||||
{
|
||||
var tester = new MersenneNumberTester();
|
||||
tester.IsMersennePrime(p).Should().BeFalse();
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(136_000_002UL)]
|
||||
[InlineData(136_000_005UL)]
|
||||
public void IsMersennePrime_detects_small_prime_divisors_for_large_exponents(ulong p)
|
||||
{
|
||||
var tester = new MersenneNumberTester();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsMersennePrime(gpu, p).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(125UL)]
|
||||
public void WarmUpOrders_populates_cache_without_affecting_results(ulong p)
|
||||
{
|
||||
var tester = new MersenneNumberTester(useOrderCache: true, useGpuOrder: true);
|
||||
tester.WarmUpOrders(p, 1_000UL);
|
||||
tester.IsMersennePrime(p).Should().BeFalse();
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(125UL)]
|
||||
public void WarmUpOrders_populates_cache_without_affecting_results(ulong p)
|
||||
{
|
||||
var tester = new MersenneNumberTester(useOrderCache: true, useGpuOrder: true);
|
||||
tester.WarmUpOrders(p, 1_000UL);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsMersennePrime(gpu, p).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(125UL, true)]
|
||||
[InlineData(127UL, false)]
|
||||
public void SharesFactorWithExponentMinusOne_detects_common_factors(ulong p, bool expected)
|
||||
{
|
||||
p.SharesFactorWithExponentMinusOne().Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(125UL, true)]
|
||||
[InlineData(127UL, false)]
|
||||
public void SharesFactorWithExponentMinusOne_detects_common_factors(ulong p, bool expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
p.SharesFactorWithExponentMinusOne(gpu).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -6,27 +7,43 @@ namespace PerfectNumbers.Core.Tests;
|
||||
[Trait("Category", "Fast")]
|
||||
public class MersenneResidueModeTests
|
||||
{
|
||||
[Fact]
|
||||
public void IsMersennePrime_residue_mode_rejects_composite_exponent()
|
||||
{
|
||||
var tester = new MersenneNumberTester(
|
||||
useResidue: true,
|
||||
useIncremental: false,
|
||||
useGpuScan: false,
|
||||
useGpuOrder: false);
|
||||
tester.IsMersennePrime(136_000_002UL).Should().BeFalse();
|
||||
}
|
||||
[Fact]
|
||||
public void IsMersennePrime_residue_mode_rejects_composite_exponent()
|
||||
{
|
||||
var tester = new MersenneNumberTester(
|
||||
useResidue: true,
|
||||
useIncremental: false,
|
||||
useGpuScan: false,
|
||||
useGpuOrder: false);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsMersennePrime(gpu, 136_000_002UL).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsMersennePrime_residue_mode_accepts_prime_exponent()
|
||||
{
|
||||
var tester = new MersenneNumberTester(
|
||||
useResidue: true,
|
||||
useIncremental: true,
|
||||
useGpuScan: false,
|
||||
useGpuOrder: false,
|
||||
maxK: 1_000UL);
|
||||
tester.IsMersennePrime(31UL).Should().BeTrue();
|
||||
}
|
||||
[Fact]
|
||||
public void IsMersennePrime_residue_mode_accepts_prime_exponent()
|
||||
{
|
||||
var tester = new MersenneNumberTester(
|
||||
useResidue: true,
|
||||
useIncremental: true,
|
||||
useGpuScan: false,
|
||||
useGpuOrder: false,
|
||||
maxK: 1_000UL);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsMersennePrime(gpu, 31UL).Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -6,61 +7,93 @@ namespace PerfectNumbers.Core.Tests;
|
||||
[Trait("Category", "Fast")]
|
||||
public class ModResidueTrackerTests
|
||||
{
|
||||
[Fact]
|
||||
public void IsDivisible_advances_residue_for_identity()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
[Fact]
|
||||
public void IsDivisible_advances_residue_for_identity()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
|
||||
tracker.IsDivisible(5, 2).Should().BeFalse();
|
||||
tracker.IsDivisible(6, 2).Should().BeTrue();
|
||||
tracker.IsDivisible(7, 2).Should().BeFalse();
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tracker.IsDivisible(gpu, 5, 2).Should().BeFalse();
|
||||
tracker.IsDivisible(gpu, 6, 2).Should().BeTrue();
|
||||
tracker.IsDivisible(gpu, 7, 2).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(3UL, 7UL, true)]
|
||||
[InlineData(5UL, 31UL, true)]
|
||||
[InlineData(5UL, 7UL, false)]
|
||||
[InlineData(11UL, 23UL, true)]
|
||||
[InlineData(11UL, 31UL, false)]
|
||||
public void IsDivisible_handles_mersenne_model(ulong p, ulong divisor, bool expected)
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Mersenne);
|
||||
[Theory]
|
||||
[InlineData(3UL, 7UL, true)]
|
||||
[InlineData(5UL, 31UL, true)]
|
||||
[InlineData(5UL, 7UL, false)]
|
||||
[InlineData(11UL, 23UL, true)]
|
||||
[InlineData(11UL, 31UL, false)]
|
||||
public void IsDivisible_handles_mersenne_model(ulong p, ulong divisor, bool expected)
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Mersenne);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
tracker.IsDivisible(p, divisor).Should().Be(expected);
|
||||
}
|
||||
try
|
||||
{
|
||||
tracker.IsDivisible(gpu, p, divisor).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeOrAppend_tracks_existing_and_new_divisors()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
[Fact]
|
||||
public void MergeOrAppend_tracks_existing_and_new_divisors()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppend(10, 3, out bool d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppend(10, 5, out bool d5).Should().BeTrue();
|
||||
d5.Should().BeTrue();
|
||||
try
|
||||
{
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppend(gpu, 10, 3, out bool d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppend(gpu, 10, 5, out bool d5).Should().BeTrue();
|
||||
d5.Should().BeTrue();
|
||||
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppend(10, 4, out bool d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppend(gpu, 10, 4, out bool d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
|
||||
tracker.BeginMerge(11);
|
||||
tracker.MergeOrAppend(11, 3, out d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppend(11, 4, out d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
tracker.MergeOrAppend(11, 5, out d5).Should().BeTrue();
|
||||
d5.Should().BeFalse();
|
||||
}
|
||||
tracker.BeginMerge(11);
|
||||
tracker.MergeOrAppend(gpu, 11, 3, out d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppend(gpu, 11, 4, out d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
tracker.MergeOrAppend(gpu, 11, 5, out d5).Should().BeTrue();
|
||||
d5.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDivisible_requires_non_decreasing_numbers()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
tracker.IsDivisible(5, 2);
|
||||
[Fact]
|
||||
public void IsDivisible_requires_non_decreasing_numbers()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tracker.IsDivisible(gpu, 5, 2);
|
||||
|
||||
Action act = () => tracker.IsDivisible(4, 2);
|
||||
act.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
Action act = () => tracker.IsDivisible(gpu, 4, 2);
|
||||
act.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,198 +1,236 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class PrimeOrderCalculatorTests
|
||||
{
|
||||
public static IEnumerable<object[]> SmallPrimes()
|
||||
{
|
||||
yield return new object[] { 3UL };
|
||||
yield return new object[] { 5UL };
|
||||
yield return new object[] { 7UL };
|
||||
yield return new object[] { 11UL };
|
||||
yield return new object[] { 13UL };
|
||||
yield return new object[] { 17UL };
|
||||
yield return new object[] { 19UL };
|
||||
yield return new object[] { 31UL };
|
||||
yield return new object[] { 61UL };
|
||||
yield return new object[] { 89UL };
|
||||
yield return new object[] { 127UL };
|
||||
yield return new object[] { 521UL };
|
||||
}
|
||||
public static IEnumerable<object[]> SmallPrimes()
|
||||
{
|
||||
yield return new object[] { 3UL };
|
||||
yield return new object[] { 5UL };
|
||||
yield return new object[] { 7UL };
|
||||
yield return new object[] { 11UL };
|
||||
yield return new object[] { 13UL };
|
||||
yield return new object[] { 17UL };
|
||||
yield return new object[] { 19UL };
|
||||
yield return new object[] { 31UL };
|
||||
yield return new object[] { 61UL };
|
||||
yield return new object[] { 89UL };
|
||||
yield return new object[] { 127UL };
|
||||
yield return new object[] { 521UL };
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> PrimesWithPrevious()
|
||||
{
|
||||
yield return new object[] { 5UL, 3UL };
|
||||
yield return new object[] { 7UL, 5UL };
|
||||
yield return new object[] { 11UL, 7UL };
|
||||
yield return new object[] { 13UL, 11UL };
|
||||
yield return new object[] { 17UL, 13UL };
|
||||
yield return new object[] { 19UL, 17UL };
|
||||
yield return new object[] { 31UL, 29UL };
|
||||
yield return new object[] { 61UL, 59UL };
|
||||
yield return new object[] { 89UL, 83UL };
|
||||
yield return new object[] { 127UL, 113UL };
|
||||
yield return new object[] { 521UL, 509UL };
|
||||
}
|
||||
public static IEnumerable<object[]> PrimesWithPrevious()
|
||||
{
|
||||
yield return new object[] { 5UL, 3UL };
|
||||
yield return new object[] { 7UL, 5UL };
|
||||
yield return new object[] { 11UL, 7UL };
|
||||
yield return new object[] { 13UL, 11UL };
|
||||
yield return new object[] { 17UL, 13UL };
|
||||
yield return new object[] { 19UL, 17UL };
|
||||
yield return new object[] { 31UL, 29UL };
|
||||
yield return new object[] { 61UL, 59UL };
|
||||
yield return new object[] { 89UL, 83UL };
|
||||
yield return new object[] { 127UL, 113UL };
|
||||
yield return new object[] { 521UL, 509UL };
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(SmallPrimes))]
|
||||
public void Calculate_heuristic_mode_matches_naive_cycle_length(ulong prime)
|
||||
{
|
||||
ulong expected = ComputeOrderByDoubling(prime);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(SmallPrimes))]
|
||||
public void Calculate_heuristic_mode_matches_naive_cycle_length(ulong prime)
|
||||
{
|
||||
ulong expected = ComputeOrderByDoubling(prime);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(PrimesWithPrevious))]
|
||||
public void Calculate_heuristic_mode_matches_strict_mode_even_with_previous_order(ulong prime, ulong previousPrime)
|
||||
{
|
||||
ulong previousOrder = ComputeOrderByDoubling(previousPrime);
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
ulong heuristic = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(PrimesWithPrevious))]
|
||||
public void Calculate_heuristic_mode_matches_strict_mode_even_with_previous_order(ulong prime, ulong previousPrime)
|
||||
{
|
||||
ulong previousOrder = ComputeOrderByDoubling(previousPrime);
|
||||
|
||||
ulong strict = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
ulong heuristic = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
heuristic.Should().Be(strict);
|
||||
}
|
||||
ulong strict = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 1UL)]
|
||||
[InlineData(3UL, 2UL)]
|
||||
public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder)
|
||||
{
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
|
||||
result.Should().Be(expectedOrder);
|
||||
}
|
||||
heuristic.Should().Be(strict);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_heuristic_mode_returns_unresolved_when_phi_cannot_be_factored()
|
||||
{
|
||||
var config = new PrimeOrderCalculator.PrimeOrderSearchConfig(
|
||||
smallFactorLimit: 1,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 8,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 1UL)]
|
||||
[InlineData(3UL, 2UL)]
|
||||
public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
prime: 13UL,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(13UL),
|
||||
config,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be(expectedOrder);
|
||||
}
|
||||
|
||||
result.Should().Be(ComputeOrderByDoubling(13UL));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_heuristic_mode_returns_unresolved_when_phi_cannot_be_factored()
|
||||
{
|
||||
var config = new PrimeOrderCalculator.PrimeOrderSearchConfig(
|
||||
smallFactorLimit: 1,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 8,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_heuristic_mode_attempts_candidates_before_falling_back_to_strict_mode()
|
||||
{
|
||||
var heuristicConfig = new PrimeOrderCalculator.PrimeOrderSearchConfig(
|
||||
smallFactorLimit: 2,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 1,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime: 13UL,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorDataPool.Shared.FromModulus(13UL),
|
||||
config,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(239UL);
|
||||
ulong heuristic = PrimeOrderCalculator.Calculate(
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
heuristicConfig,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be(ComputeOrderByDoubling(13UL));
|
||||
}
|
||||
|
||||
heuristic.Should().Be(ComputeOrderByDoubling(239UL));
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_heuristic_mode_attempts_candidates_before_falling_back_to_strict_mode()
|
||||
{
|
||||
var heuristicConfig = new PrimeOrderCalculator.PrimeOrderSearchConfig(
|
||||
smallFactorLimit: 2,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 1,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
|
||||
|
||||
var strictConfig = new PrimeOrderCalculator.PrimeOrderSearchConfig(
|
||||
smallFactorLimit: 2,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 1,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Strict);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorDataPool.Shared.FromModulus(239UL);
|
||||
ulong heuristic = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
heuristicConfig,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
ulong strict = PrimeOrderCalculator.Calculate(
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
strictConfig,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
heuristic.Should().Be(ComputeOrderByDoubling(239UL));
|
||||
|
||||
strict.Should().Be(heuristic);
|
||||
}
|
||||
var strictConfig = new PrimeOrderCalculator.PrimeOrderSearchConfig(
|
||||
smallFactorLimit: 2,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 1,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Strict);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_handles_128_bit_prime()
|
||||
{
|
||||
UInt128 prime = UInt128.Parse("18446744073709641691");
|
||||
ulong strict = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
strictConfig,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
UInt128 result = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder: null,
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
strict.Should().Be(heuristic);
|
||||
|
||||
result.Should().Be((UInt128)1229782938247309446UL);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void GetCycle_uses_wide_prime_heuristic()
|
||||
{
|
||||
UInt128 prime = UInt128.Parse("18446744073709641691");
|
||||
MersenneDivisorCycles.GetCycle(prime).Should().Be((UInt128)1229782938247309446UL);
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_handles_128_bit_prime()
|
||||
{
|
||||
UInt128 prime = UInt128.Parse("18446744073709641691");
|
||||
|
||||
private static ulong ComputeOrderByDoubling(ulong prime)
|
||||
{
|
||||
if (prime == 2UL)
|
||||
{
|
||||
return 1UL;
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
UInt128 result = PrimeOrderCalculator.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
if (prime == 3UL)
|
||||
{
|
||||
return 2UL;
|
||||
}
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be((UInt128)1229782938247309446UL);
|
||||
}
|
||||
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % prime;
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void GetCycle_uses_wide_prime_heuristic()
|
||||
{
|
||||
UInt128 prime = UInt128.Parse("18446744073709641691");
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var mersenneDivisorCycles = MersenneDivisorCycles.GetCycle(gpu, prime).Should().Be((UInt128)1229782938247309446UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value * 2UL) % prime;
|
||||
order++;
|
||||
}
|
||||
private static ulong ComputeOrderByDoubling(ulong prime)
|
||||
{
|
||||
if (prime == 2UL)
|
||||
{
|
||||
return 1UL;
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
if (prime == 3UL)
|
||||
{
|
||||
return 2UL;
|
||||
}
|
||||
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % prime;
|
||||
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value * 2UL) % prime;
|
||||
order++;
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -6,20 +7,28 @@ namespace PerfectNumbers.Core.Tests;
|
||||
[Trait("Category", "Fast")]
|
||||
public class PrimeTesterGcdTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(81UL, true)]
|
||||
[InlineData(101UL, false)]
|
||||
public void SharesFactorWithMaxExponent_detects_gcd(ulong n, bool expected)
|
||||
{
|
||||
PrimeTester.SharesFactorWithMaxExponent(n).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(81UL, true)]
|
||||
[InlineData(101UL, false)]
|
||||
public void SharesFactorWithMaxExponent_detects_gcd(ulong n, bool expected)
|
||||
{
|
||||
PrimeTester.SharesFactorWithMaxExponent(n).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SharesFactorWithMaxExponentBatch_filters_values()
|
||||
{
|
||||
ulong[] values = [81UL, 101UL];
|
||||
Span<byte> results = stackalloc byte[values.Length];
|
||||
PrimeTester.SharesFactorWithMaxExponentBatch(values, results);
|
||||
results.ToArray().Should().Equal(new byte[] { 1, 0 });
|
||||
}
|
||||
[Fact]
|
||||
public void SharesFactorWithMaxExponentBatch_filters_values()
|
||||
{
|
||||
ulong[] values = [81UL, 101UL];
|
||||
Span<byte> results = stackalloc byte[values.Length];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeTester.SharesFactorWithMaxExponentBatch(gpu, values, results);
|
||||
results.ToArray().Should().Equal(new byte[] { 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +1,98 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class PrimeTesterIsPrimeTests
|
||||
{
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
// EvenPerfectBitScanner never routes the tiny values anymore; keep coverage focused on the admissible range handled by production scans so the guards in PrimeTester stay commented out without breaking validation.
|
||||
[InlineData(7UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(11UL, true)]
|
||||
[InlineData(13UL, true)]
|
||||
[InlineData(21UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
[InlineData(133UL, false)]
|
||||
[InlineData(137UL, true)]
|
||||
[InlineData(143UL, false)]
|
||||
[InlineData(199UL, true)]
|
||||
[InlineData(209UL, false)]
|
||||
[InlineData(403UL, false)]
|
||||
[InlineData(341UL, false)]
|
||||
public void IsPrime_returns_expected_results(ulong n, bool expected)
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
// EvenPerfectBitScanner never routes the tiny values anymore; keep coverage focused on the admissible range handled by production scans so the guards in PrimeTester stay commented out without breaking validation.
|
||||
[InlineData(7UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(11UL, true)]
|
||||
[InlineData(13UL, true)]
|
||||
[InlineData(21UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
[InlineData(133UL, false)]
|
||||
[InlineData(137UL, true)]
|
||||
[InlineData(143UL, false)]
|
||||
[InlineData(199UL, true)]
|
||||
[InlineData(209UL, false)]
|
||||
[InlineData(403UL, false)]
|
||||
[InlineData(341UL, false)]
|
||||
public void IsPrime_returns_expected_results(ulong n, bool expected)
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
|
||||
PrimeTester.IsPrime(n).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(139UL, true)]
|
||||
[InlineData(189UL, false)]
|
||||
[InlineData(193UL, true)]
|
||||
[InlineData(279UL, false)]
|
||||
public void IsPrimeGpu_uses_residue_specific_tables(ulong n, bool expected)
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(139UL, true)]
|
||||
[InlineData(189UL, false)]
|
||||
[InlineData(193UL, true)]
|
||||
[InlineData(279UL, false)]
|
||||
public void IsPrimeGpu_uses_residue_specific_tables(ulong n, bool expected)
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
|
||||
PrimeTester.IsPrimeGpu(n).Should().Be(expected);
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(gpu, n).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeGpu_falls_back_to_cpu_when_forced()
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
PrimeTester.IsPrime(137UL).Should().BeTrue();
|
||||
PrimeTester.IsPrime(341UL).Should().BeFalse();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeGpu_falls_back_to_cpu_when_forced()
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
PrimeTester.IsPrime(137UL).Should().BeTrue();
|
||||
PrimeTester.IsPrime(341UL).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeGpu_handles_parallel_calls_for_known_primes()
|
||||
{
|
||||
GpuPrimeWorkLimiter.SetLimit(64);
|
||||
PrimeTester.GpuBatchSize = 256_000;
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeGpu_handles_parallel_calls_for_known_primes()
|
||||
{
|
||||
GpuPrimeWorkLimiter.SetLimit(64);
|
||||
PrimeTester.GpuBatchSize = 256_000;
|
||||
|
||||
ulong[] primes = [107UL, 127UL, 521UL, 607UL];
|
||||
var results = new bool[primes.Length];
|
||||
ulong[] primes = [107UL, 127UL, 521UL, 607UL];
|
||||
var results = new bool[primes.Length];
|
||||
|
||||
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||
Parallel.For(0, primes.Length, i =>
|
||||
{
|
||||
results[i] = PrimeTester.IsPrimeGpu(primes[i]);
|
||||
});
|
||||
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Parallel.For(0, primes.Length, i =>
|
||||
{
|
||||
results[i] = PrimeTester.IsPrimeGpu(gpu, primes[i]);
|
||||
});
|
||||
|
||||
results.Should().AllBeEquivalentTo(true);
|
||||
results.Should().AllBeEquivalentTo(true);
|
||||
|
||||
Parallel.For(0, Environment.ProcessorCount * 16, _ =>
|
||||
{
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(prime).Should().BeTrue();
|
||||
}
|
||||
});
|
||||
}
|
||||
Parallel.For(0, Environment.ProcessorCount * 16, _ =>
|
||||
{
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(gpu, prime).Should().BeTrue();
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,284 +1,293 @@
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class UInt128ExtensionsTests
|
||||
{
|
||||
public static IEnumerable<object[]> BinaryGcdCases()
|
||||
{
|
||||
yield return new object[] { UInt128.Zero, (UInt128)5, (UInt128)5 };
|
||||
yield return new object[] { (UInt128)18, (UInt128)24, (UInt128)6 };
|
||||
public static IEnumerable<object[]> BinaryGcdCases()
|
||||
{
|
||||
yield return new object[] { UInt128.Zero, (UInt128)5, (UInt128)5 };
|
||||
yield return new object[] { (UInt128)18, (UInt128)24, (UInt128)6 };
|
||||
|
||||
UInt128 big1 = (UInt128.One << 96) + 12345;
|
||||
UInt128 big2 = (UInt128.One << 80) + 67890;
|
||||
yield return new object[]
|
||||
{
|
||||
big1,
|
||||
big2,
|
||||
(UInt128)BigInteger.GreatestCommonDivisor((BigInteger)big1, (BigInteger)big2)
|
||||
};
|
||||
}
|
||||
UInt128 big1 = (UInt128.One << 96) + 12345;
|
||||
UInt128 big2 = (UInt128.One << 80) + 67890;
|
||||
yield return new object[]
|
||||
{
|
||||
big1,
|
||||
big2,
|
||||
(UInt128)BigInteger.GreatestCommonDivisor((BigInteger)big1, (BigInteger)big2)
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(BinaryGcdCases))]
|
||||
public void BinaryGcd_matches_BigInteger(UInt128 a, UInt128 b, UInt128 expected)
|
||||
{
|
||||
a.BinaryGcd(b).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(BinaryGcdCases))]
|
||||
public void BinaryGcd_matches_BigInteger(UInt128 a, UInt128 b, UInt128 expected)
|
||||
{
|
||||
a.BinaryGcd(b).Should().Be(expected);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> TrailingZeroCases()
|
||||
{
|
||||
yield return new object[] { UInt128.Zero, 128 };
|
||||
yield return new object[] { (UInt128)1, 0 };
|
||||
yield return new object[] { (UInt128)8, 3 };
|
||||
yield return new object[] { UInt128.One << 80, 80 };
|
||||
}
|
||||
public static IEnumerable<object[]> TrailingZeroCases()
|
||||
{
|
||||
yield return new object[] { UInt128.Zero, 128 };
|
||||
yield return new object[] { (UInt128)1, 0 };
|
||||
yield return new object[] { (UInt128)8, 3 };
|
||||
yield return new object[] { UInt128.One << 80, 80 };
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(TrailingZeroCases))]
|
||||
public void CountTrailingZeros_returns_expected(UInt128 value, int expected)
|
||||
{
|
||||
value.CountTrailingZeros().Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(TrailingZeroCases))]
|
||||
public void CountTrailingZeros_returns_expected(UInt128 value, int expected)
|
||||
{
|
||||
value.CountTrailingZeros().Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(3UL)]
|
||||
[InlineData(5UL)]
|
||||
[InlineData(7UL)]
|
||||
public void CalculateOrder_matches_naive(ulong q)
|
||||
{
|
||||
UInt128 q128 = q;
|
||||
ulong expected = NaiveOrder(q);
|
||||
q128.CalculateOrder().Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(3UL)]
|
||||
[InlineData(5UL)]
|
||||
[InlineData(7UL)]
|
||||
public void CalculateOrder_matches_naive(ulong q)
|
||||
{
|
||||
UInt128 q128 = q;
|
||||
ulong expected = NaiveOrder(q);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
q128.CalculateOrder(gpu).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong NaiveOrder(ulong q)
|
||||
{
|
||||
if (q <= 2UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % q;
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value << 1) % q;
|
||||
order++;
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
public void IsPrimeCandidate_checks_small(ulong n, bool expected)
|
||||
{
|
||||
((UInt128)n).IsPrimeCandidate().Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
public void IsPrimeCandidate_checks_small(ulong n, bool expected)
|
||||
{
|
||||
((UInt128)n).IsPrimeCandidate().Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeCandidate_detects_large_composite()
|
||||
{
|
||||
UInt128 n = (UInt128.One << 64) + 1;
|
||||
n.IsPrimeCandidate().Should().BeFalse();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeCandidate_detects_large_composite()
|
||||
{
|
||||
UInt128 n = (UInt128.One << 64) + 1;
|
||||
n.IsPrimeCandidate().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod10_8_5_3_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345678901234567890UL;
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod10_8_5_3_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345678901234567890UL;
|
||||
|
||||
value.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3);
|
||||
value.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3);
|
||||
|
||||
ulong expected10 = (ulong)(value % 10);
|
||||
ulong expected8 = (ulong)(value % 8);
|
||||
ulong expected5 = (ulong)(value % 5);
|
||||
ulong expected3 = (ulong)(value % 3);
|
||||
ulong expected10 = (ulong)(value % 10);
|
||||
ulong expected8 = (ulong)(value % 8);
|
||||
ulong expected5 = (ulong)(value % 5);
|
||||
ulong expected3 = (ulong)(value % 3);
|
||||
|
||||
mod10.Should().Be(expected10);
|
||||
mod8.Should().Be(expected8);
|
||||
mod5.Should().Be(expected5);
|
||||
mod3.Should().Be(expected3);
|
||||
}
|
||||
mod10.Should().Be(expected10);
|
||||
mod8.Should().Be(expected8);
|
||||
mod5.Should().Be(expected5);
|
||||
mod3.Should().Be(expected3);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 0UL)]
|
||||
[InlineData(2UL, 1UL)]
|
||||
[InlineData(2UL, 63UL)]
|
||||
[InlineData(2UL, 127UL)]
|
||||
[InlineData(3UL, 5UL)]
|
||||
public void Pow_matches_BigInteger(ulong value, ulong exponent)
|
||||
{
|
||||
UInt128 v = value;
|
||||
UInt128 expected = (UInt128)BigPow(value, exponent);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 0UL)]
|
||||
[InlineData(2UL, 1UL)]
|
||||
[InlineData(2UL, 63UL)]
|
||||
[InlineData(2UL, 127UL)]
|
||||
[InlineData(3UL, 5UL)]
|
||||
public void Pow_matches_BigInteger(ulong value, ulong exponent)
|
||||
{
|
||||
UInt128 v = value;
|
||||
UInt128 expected = (UInt128)BigPow(value, exponent);
|
||||
|
||||
v.Pow(exponent).Should().Be(expected);
|
||||
}
|
||||
v.Pow(exponent).Should().Be(expected);
|
||||
}
|
||||
|
||||
private static BigInteger BigPow(ulong value, ulong exponent)
|
||||
{
|
||||
BigInteger result = BigInteger.One;
|
||||
BigInteger baseValue = value;
|
||||
private static BigInteger BigPow(ulong value, ulong exponent)
|
||||
{
|
||||
BigInteger result = BigInteger.One;
|
||||
BigInteger baseValue = value;
|
||||
|
||||
while (exponent != 0UL)
|
||||
{
|
||||
if ((exponent & 1UL) != 0UL)
|
||||
{
|
||||
result *= baseValue;
|
||||
}
|
||||
while (exponent != 0UL)
|
||||
{
|
||||
if ((exponent & 1UL) != 0UL)
|
||||
{
|
||||
result *= baseValue;
|
||||
}
|
||||
|
||||
baseValue *= baseValue;
|
||||
exponent >>= 1;
|
||||
}
|
||||
baseValue *= baseValue;
|
||||
exponent >>= 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 10UL, 17UL)]
|
||||
[InlineData(123456789UL, 3UL, 97UL)]
|
||||
public void ModPow_matches_BigInteger(ulong value, ulong exponent, ulong modulus)
|
||||
{
|
||||
UInt128 v = value;
|
||||
UInt128 e = exponent;
|
||||
UInt128 m = modulus;
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 10UL, 17UL)]
|
||||
[InlineData(123456789UL, 3UL, 97UL)]
|
||||
public void ModPow_matches_BigInteger(ulong value, ulong exponent, ulong modulus)
|
||||
{
|
||||
UInt128 v = value;
|
||||
UInt128 e = exponent;
|
||||
UInt128 m = modulus;
|
||||
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(value, exponent, modulus);
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(value, exponent, modulus);
|
||||
|
||||
v.ModPow(e, m).Should().Be(expected);
|
||||
}
|
||||
v.ModPow(e, m).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ModPow_handles_large_values()
|
||||
{
|
||||
UInt128 v = (UInt128.One << 96) + 12345;
|
||||
UInt128 e = 17;
|
||||
UInt128 m = 97;
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ModPow_handles_large_values()
|
||||
{
|
||||
UInt128 v = (UInt128.One << 96) + 12345;
|
||||
UInt128 e = 17;
|
||||
UInt128 m = 97;
|
||||
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow((BigInteger)v, (BigInteger)e, (BigInteger)m);
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow((BigInteger)v, (BigInteger)e, (BigInteger)m);
|
||||
|
||||
v.ModPow(e, m).Should().Be(expected);
|
||||
}
|
||||
v.ModPow(e, m).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL)]
|
||||
[InlineData(1UL)]
|
||||
[InlineData(123456789UL)]
|
||||
public void Mod10_matches_operator(ulong low)
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + low;
|
||||
value.Mod10().Should().Be((ulong)(value % 10));
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL)]
|
||||
[InlineData(1UL)]
|
||||
[InlineData(123456789UL)]
|
||||
public void Mod10_matches_operator(ulong low)
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + low;
|
||||
value.Mod10().Should().Be((ulong)(value % 10));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod8_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod8().Should().Be((ulong)(value % 8));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod8_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod8().Should().Be((ulong)(value % 8));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod3_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod3().Should().Be((ulong)(value % 3));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod3_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod3().Should().Be((ulong)(value % 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod6_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 9876543210UL;
|
||||
value.Mod6().Should().Be((ulong)(value % 6));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod6_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 9876543210UL;
|
||||
value.Mod6().Should().Be((ulong)(value % 6));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod5_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod5().Should().Be((ulong)(value % 5));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod5_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod5().Should().Be((ulong)(value % 5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod7_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 9876543210987654321UL;
|
||||
value.Mod7().Should().Be((ulong)(value % 7));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod7_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 9876543210987654321UL;
|
||||
value.Mod7().Should().Be((ulong)(value % 7));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod11_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 120) + 9876543210987654321UL;
|
||||
value.Mod11().Should().Be((ulong)(value % 11));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod11_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 120) + 9876543210987654321UL;
|
||||
value.Mod11().Should().Be((ulong)(value % 11));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod128_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 120) + 9876543210987654321UL;
|
||||
value.Mod128().Should().Be((ulong)(value % 128));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod128_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 120) + 9876543210987654321UL;
|
||||
value.Mod128().Should().Be((ulong)(value % 128));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mul64_matches_expected_product()
|
||||
{
|
||||
UInt128 a = (UInt128.One << 80) + 123456789;
|
||||
UInt128 b = (UInt128.One << 64) + 987654321;
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mul64_matches_expected_product()
|
||||
{
|
||||
UInt128 a = (UInt128.One << 80) + 123456789;
|
||||
UInt128 b = (UInt128.One << 64) + 987654321;
|
||||
|
||||
UInt128 expected = (UInt128)((UInt128)(ulong)a * b);
|
||||
UInt128 expected = (UInt128)((UInt128)(ulong)a * b);
|
||||
|
||||
a.Mul64(b).Should().Be(expected);
|
||||
}
|
||||
a.Mul64(b).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(123UL, 456UL, 789UL)]
|
||||
[InlineData(123456789UL, 987654321UL, 1009UL)]
|
||||
public void MulMod_matches_BigInteger(ulong a, ulong b, ulong modulus)
|
||||
{
|
||||
UInt128 ua = a % modulus;
|
||||
UInt128 ub = b % modulus;
|
||||
UInt128 mod = modulus;
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(123UL, 456UL, 789UL)]
|
||||
[InlineData(123456789UL, 987654321UL, 1009UL)]
|
||||
public void MulMod_matches_BigInteger(ulong a, ulong b, ulong modulus)
|
||||
{
|
||||
UInt128 ua = a % modulus;
|
||||
UInt128 ub = b % modulus;
|
||||
UInt128 mod = modulus;
|
||||
|
||||
UInt128 expected = (UInt128)(((BigInteger)ua * (BigInteger)ub) % modulus);
|
||||
UInt128 expected = (UInt128)(((BigInteger)ua * (BigInteger)ub) % modulus);
|
||||
|
||||
ua.MulMod(ub, mod).Should().Be(expected);
|
||||
}
|
||||
ua.MulMod(ub, mod).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void MulMod_handles_large_inputs()
|
||||
{
|
||||
UInt128 a = (UInt128.One << 80) + 12345;
|
||||
UInt128 b = (UInt128.One << 96) + 67890;
|
||||
UInt128 mod = (UInt128.One << 100) - 93;
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void MulMod_handles_large_inputs()
|
||||
{
|
||||
UInt128 a = (UInt128.One << 80) + 12345;
|
||||
UInt128 b = (UInt128.One << 96) + 67890;
|
||||
UInt128 mod = (UInt128.One << 100) - 93;
|
||||
|
||||
UInt128 expected = (UInt128)(((BigInteger)a * (BigInteger)b) % (BigInteger)mod);
|
||||
UInt128 expected = (UInt128)(((BigInteger)a * (BigInteger)b) % (BigInteger)mod);
|
||||
|
||||
a.MulMod(b, mod).Should().Be(expected);
|
||||
}
|
||||
a.MulMod(b, mod).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,225 +1,250 @@
|
||||
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)
|
||||
{
|
||||
ulong expected = value % 10UL;
|
||||
[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)
|
||||
{
|
||||
ulong expected = value % 10UL;
|
||||
|
||||
ULongExtensions.Mod10(value).Should().Be(expected);
|
||||
}
|
||||
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(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);
|
||||
[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;
|
||||
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);
|
||||
}
|
||||
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(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 CalculateOrder_matches_naive_algorithm(ulong q)
|
||||
{
|
||||
ulong expected = NaiveOrder(q);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(3UL)]
|
||||
[InlineData(5UL)]
|
||||
[InlineData(7UL)]
|
||||
[InlineData(11UL)]
|
||||
public void CalculateOrder_matches_naive_algorithm(ulong q)
|
||||
{
|
||||
ulong expected = NaiveOrder(q);
|
||||
|
||||
q.CalculateOrder().Should().Be(expected);
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
q.CalculateOrder(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);
|
||||
[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);
|
||||
}
|
||||
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);
|
||||
[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);
|
||||
}
|
||||
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(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 SharesFactorWithExponentMinusOne_detects_relationship(ulong exponent, bool expected)
|
||||
{
|
||||
exponent.SharesFactorWithExponentMinusOne().Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(15UL, true)]
|
||||
[InlineData(7UL, false)]
|
||||
[InlineData(9UL, false)]
|
||||
public void SharesFactorWithExponentMinusOne_detects_relationship(ulong exponent, bool expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
exponent.SharesFactorWithExponentMinusOne(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);
|
||||
[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);
|
||||
}
|
||||
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);
|
||||
[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);
|
||||
}
|
||||
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, 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;
|
||||
[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);
|
||||
}
|
||||
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);
|
||||
[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);
|
||||
}
|
||||
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);
|
||||
[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);
|
||||
}
|
||||
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);
|
||||
[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);
|
||||
}
|
||||
exponent.PowModWithCycle((UInt128)modulus, (UInt128)cycle).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(11UL, 13UL)]
|
||||
public void PowModCrt_matches_BigInteger(ulong exponent, ulong modulus)
|
||||
{
|
||||
MersenneDivisorCycles cycles = MersenneDivisorCycles.Shared;
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(11UL, 13UL)]
|
||||
public void PowModCrt_matches_BigInteger(ulong exponent, ulong modulus)
|
||||
{
|
||||
MersenneDivisorCycles cycles = MersenneDivisorCycles.Shared;
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
|
||||
exponent.PowModCrt((UInt128)modulus, cycles).Should().Be(expected);
|
||||
}
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
exponent.PowModCrt(gpu, (UInt128)modulus, cycles).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong NaiveOrder(ulong q)
|
||||
{
|
||||
if (q <= 2UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % q;
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value << 1) % q;
|
||||
order++;
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user