Merge pull request #124 from mlemanczyk/codex/enhance-tests-for-mersenne-class-path
Add GPU by-divisor tests for small factors
This commit is contained in:
3 files changed
+236
No files matched your search
@@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FluentAssertions;
|
||||||
|
using PerfectNumbers.Core;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core.Tests;
|
||||||
|
|
||||||
|
public class MersenneNumberDivisorByDivisorTesterTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void Run_marks_candidates_divisible_by_seven_or_eleven_as_composite()
|
||||||
|
{
|
||||||
|
var candidates = new List<ulong> { 5UL, 7UL, 11UL, 13UL };
|
||||||
|
var tester = new FakeTester();
|
||||||
|
var results = new List<(ulong Prime, bool Searched, bool Detailed, bool Passed)>();
|
||||||
|
int compositeMarks = 0;
|
||||||
|
int clearedMarks = 0;
|
||||||
|
|
||||||
|
MersenneNumberDivisorByDivisorTester.Run(
|
||||||
|
candidates,
|
||||||
|
tester,
|
||||||
|
previousResults: null,
|
||||||
|
startPrime: 0UL,
|
||||||
|
markComposite: () => compositeMarks++,
|
||||||
|
clearComposite: () => clearedMarks++,
|
||||||
|
printResult: (prime, searched, detailed, passed) => results.Add((prime, searched, detailed, passed)),
|
||||||
|
threadCount: 1);
|
||||||
|
|
||||||
|
tester.ConfiguredMaxPrime.Should().Be(13UL);
|
||||||
|
compositeMarks.Should().Be(2);
|
||||||
|
clearedMarks.Should().Be(2);
|
||||||
|
results.Should().Equal(
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(5UL, true, true, true),
|
||||||
|
(7UL, true, true, false),
|
||||||
|
(11UL, true, true, false),
|
||||||
|
(13UL, true, true, true),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeTester : IMersenneNumberDivisorByDivisorTester
|
||||||
|
{
|
||||||
|
public ulong ConfiguredMaxPrime { get; private set; }
|
||||||
|
|
||||||
|
public int BatchSize { get; set; }
|
||||||
|
|
||||||
|
public ulong DivisorLimit => ConfiguredMaxPrime + 100UL;
|
||||||
|
|
||||||
|
public void ConfigureFromMaxPrime(ulong maxPrime)
|
||||||
|
{
|
||||||
|
ConfiguredMaxPrime = maxPrime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong GetAllowedMaxDivisor(ulong prime)
|
||||||
|
{
|
||||||
|
return DivisorLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
||||||
|
{
|
||||||
|
divisorsExhausted = true;
|
||||||
|
return prime % 7UL != 0UL && prime % 11UL != 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
||||||
|
{
|
||||||
|
return new DummySession();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareCandidates(ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < primes.Length; i++)
|
||||||
|
{
|
||||||
|
allowedMaxValues[i] = 100UL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class DummySession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
||||||
|
{
|
||||||
|
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,5 +44,41 @@ public class MersenneNumberDivisorCpuTesterTests
|
|||||||
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes, hits);
|
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes, hits);
|
||||||
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||||
|
tester.ConfigureFromMaxPrime(43UL);
|
||||||
|
|
||||||
|
using var session = tester.CreateDivisorSession();
|
||||||
|
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||||
|
byte[] hits = new byte[exponents.Length];
|
||||||
|
|
||||||
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||||
|
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||||
|
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||||
|
|
||||||
|
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Category", "Fast")]
|
||||||
|
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||||
|
{
|
||||||
|
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||||
|
tester.ConfigureFromMaxPrime(61UL);
|
||||||
|
|
||||||
|
using var session = tester.CreateDivisorSession();
|
||||||
|
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||||
|
byte[] hits = new byte[exponents.Length];
|
||||||
|
|
||||||
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||||
|
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||||
|
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||||
|
|
||||||
|
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
@@ -180,6 +181,112 @@ public class MersenneNumberDivisorGpuTesterTests
|
|||||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[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);
|
||||||
|
|
||||||
|
using var session = tester.CreateDivisorSession();
|
||||||
|
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||||
|
byte[] hits = new byte[exponents.Length];
|
||||||
|
|
||||||
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||||
|
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||||
|
|
||||||
|
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||||
|
|
||||||
|
var hostBufferField = typeof(MersenneNumberDivisorByDivisorGpuTester.DivisorScanSession)
|
||||||
|
.GetField("_hostBuffer", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
|
ulong[] hostBuffer = (ulong[])hostBufferField.GetValue(session)!;
|
||||||
|
Span<ulong> residues = hostBuffer.AsSpan(0, exponents.Length);
|
||||||
|
byte[] computedHits = new byte[exponents.Length];
|
||||||
|
for (int i = 0; i < residues.Length; i++)
|
||||||
|
{
|
||||||
|
computedHits[i] = residues[i] == divisorData.MontgomeryOne ? (byte)1 : (byte)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
computedHits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
[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);
|
||||||
|
|
||||||
|
using var session = tester.CreateDivisorSession();
|
||||||
|
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||||
|
byte[] hits = new byte[exponents.Length];
|
||||||
|
|
||||||
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||||
|
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||||
|
|
||||||
|
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||||
|
|
||||||
|
var hostBufferField = typeof(MersenneNumberDivisorByDivisorGpuTester.DivisorScanSession)
|
||||||
|
.GetField("_hostBuffer", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||||
|
ulong[] hostBuffer = (ulong[])hostBufferField.GetValue(session)!;
|
||||||
|
Span<ulong> residues = hostBuffer.AsSpan(0, exponents.Length);
|
||||||
|
byte[] computedHits = new byte[exponents.Length];
|
||||||
|
for (int i = 0; i < residues.Length; i++)
|
||||||
|
{
|
||||||
|
computedHits[i] = residues[i] == divisorData.MontgomeryOne ? (byte)1 : (byte)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
computedHits.Should().Equal(new byte[] { 1, 0, 1, 0, 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)!;
|
||||||
|
|
||||||
|
var originalTable = (List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!;
|
||||||
|
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var patchedTable = new List<(ulong Divisor, ulong Cycle)>();
|
||||||
|
ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||||
|
|
||||||
|
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(191UL);
|
||||||
|
patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLength(191UL, divisorData);
|
||||||
|
|
||||||
|
tableField.SetValue(cycles, patchedTable);
|
||||||
|
smallCyclesField.SetValue(cycles, patchedSmall);
|
||||||
|
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||||
|
|
||||||
|
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||||
|
tester.ConfigureFromMaxPrime(19UL);
|
||||||
|
|
||||||
|
typeof(MersenneNumberDivisorByDivisorGpuTester)
|
||||||
|
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
||||||
|
.SetValue(tester, 200UL);
|
||||||
|
|
||||||
|
tester.IsPrime(19UL, out bool divisorsExhausted).Should().BeTrue();
|
||||||
|
divisorsExhausted.Should().BeTrue();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tableField.SetValue(cycles, originalTable);
|
||||||
|
smallCyclesField.SetValue(cycles, originalSmall);
|
||||||
|
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||||
|
GpuContextPool.DisposeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Category", "Fast")]
|
[Trait("Category", "Fast")]
|
||||||
public void ByDivisor_cpu_session_reuses_cycle_remainders_across_primes()
|
public void ByDivisor_cpu_session_reuses_cycle_remainders_across_primes()
|
||||||
|
|||||||
Reference in new issue
Block a user