Merge pull request #21 from mlemanczyk/codex/modify-evenperfectbitscanner-for-prime-iteration
Skip composite residue outputs
This commit is contained in:
3 files changed
+194
-15
No files matched your search
@@ -1,10 +1,11 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using PerfectNumbers.Core;
|
|
||||||
using PerfectNumbers.Core.Gpu;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using PerfectNumbers.Core;
|
||||||
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
namespace EvenPerfectBitScanner.Tests;
|
namespace EvenPerfectBitScanner.Tests;
|
||||||
|
|
||||||
@@ -246,6 +247,85 @@ public class ProgramTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Composite_candidates_flagged_for_residue_output_skip()
|
||||||
|
{
|
||||||
|
var useDivisorField = typeof(Program).GetField("_useDivisor", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
var residueField = typeof(Program).GetField("PResidue", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
var compositeField = typeof(Program).GetField("_lastCompositeP", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||||
|
|
||||||
|
useDivisorField.SetValue(null, false);
|
||||||
|
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: false, maxK: 1_000UL), trackAllValues: true));
|
||||||
|
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||||
|
residueField.SetValue(null, new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||||
|
forceCpuProp!.SetValue(null, true);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
compositeField.SetValue(null, false);
|
||||||
|
|
||||||
|
Program.IsEvenPerfectCandidate(9UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
|
||||||
|
searched.Should().BeFalse();
|
||||||
|
detailed.Should().BeFalse();
|
||||||
|
((bool)compositeField.GetValue(null)!).Should().BeTrue();
|
||||||
|
|
||||||
|
Program.IsEvenPerfectCandidate(11UL, 0UL, out searched, out detailed);
|
||||||
|
searched.Should().BeTrue();
|
||||||
|
((bool)compositeField.GetValue(null)!).Should().BeFalse();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
mersenneField.SetValue(null, null);
|
||||||
|
primeField.SetValue(null, null);
|
||||||
|
residueField.SetValue(null, null);
|
||||||
|
compositeField.SetValue(null, false);
|
||||||
|
forceCpuProp!.SetValue(null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Residue_mode_skips_composite_results_in_output()
|
||||||
|
{
|
||||||
|
var residueModeField = typeof(Program).GetField("_useResidueMode", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
var outputField = typeof(Program).GetField("_outputBuilder", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
var writeIndexField = typeof(Program).GetField("_writeIndex", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
var compositeField = typeof(Program).GetField("_lastCompositeP", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
var printResult = typeof(Program).GetMethod("PrintResult", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||||
|
|
||||||
|
var previousResidueMode = (bool)residueModeField.GetValue(null)!;
|
||||||
|
var previousOutput = (StringBuilder?)outputField.GetValue(null);
|
||||||
|
var previousWriteIndex = (int)writeIndexField.GetValue(null)!;
|
||||||
|
var previousComposite = (bool)compositeField.GetValue(null)!;
|
||||||
|
|
||||||
|
var builder = StringBuilderPool.Rent();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
builder.Clear();
|
||||||
|
residueModeField.SetValue(null, true);
|
||||||
|
compositeField.SetValue(null, true);
|
||||||
|
outputField.SetValue(null, builder);
|
||||||
|
writeIndexField.SetValue(null, 0);
|
||||||
|
|
||||||
|
printResult.Invoke(null, new object[] { 9UL, false, false, false });
|
||||||
|
|
||||||
|
builder.Length.Should().Be(0);
|
||||||
|
((int)writeIndexField.GetValue(null)!).Should().Be(0);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
outputField.SetValue(null, previousOutput);
|
||||||
|
writeIndexField.SetValue(null, previousWriteIndex);
|
||||||
|
residueModeField.SetValue(null, previousResidueMode);
|
||||||
|
compositeField.SetValue(null, previousComposite);
|
||||||
|
builder.Clear();
|
||||||
|
StringBuilderPool.Return(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Category", "Fast")]
|
[Trait("Category", "Fast")]
|
||||||
public void Main_displays_help_when_requested()
|
public void Main_displays_help_when_requested()
|
||||||
|
|||||||
@@ -12,4 +12,8 @@
|
|||||||
<_Parameter1>EvenPerfectBitScanner.Tests</_Parameter1>
|
<_Parameter1>EvenPerfectBitScanner.Tests</_Parameter1>
|
||||||
</AssemblyAttribute>
|
</AssemblyAttribute>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Open.Numeric" Version="3.0.1" />
|
||||||
|
<PackageReference Include="Open.Numeric.Primes" Version="4.0.4" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Open.Numeric.Primes;
|
||||||
using PerfectNumbers.Core;
|
using PerfectNumbers.Core;
|
||||||
using PerfectNumbers.Core.Gpu;
|
using PerfectNumbers.Core.Gpu;
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ internal static class Program
|
|||||||
private static bool _primeFoundAfterInit;
|
private static bool _primeFoundAfterInit;
|
||||||
private static bool _useGcdFilter;
|
private static bool _useGcdFilter;
|
||||||
private static bool _useDivisor;
|
private static bool _useDivisor;
|
||||||
|
private static bool _useResidueMode;
|
||||||
private static UInt128 _divisor;
|
private static UInt128 _divisor;
|
||||||
private static MersenneNumberDivisorGpuTester? _divisorTester;
|
private static MersenneNumberDivisorGpuTester? _divisorTester;
|
||||||
private static ulong? _orderWarmupLimitOverride;
|
private static ulong? _orderWarmupLimitOverride;
|
||||||
@@ -34,6 +36,10 @@ internal static class Program
|
|||||||
private static bool _limitReached;
|
private static bool _limitReached;
|
||||||
private static string? _resultsDir;
|
private static string? _resultsDir;
|
||||||
private static string? _resultsPrefix;
|
private static string? _resultsPrefix;
|
||||||
|
private static readonly Optimized PrimeIterator = new();
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static bool _lastCompositeP;
|
||||||
|
|
||||||
// RLE/bit-pattern filtering options
|
// RLE/bit-pattern filtering options
|
||||||
private static string? _rleBlacklistPath;
|
private static string? _rleBlacklistPath;
|
||||||
@@ -409,8 +415,20 @@ internal static class Program
|
|||||||
Console.WriteLine("Divisor cycles are ready");
|
Console.WriteLine("Divisor cycles are ready");
|
||||||
|
|
||||||
_useDivisor = useDivisor;
|
_useDivisor = useDivisor;
|
||||||
|
_useResidueMode = useResidue;
|
||||||
_divisor = divisor;
|
_divisor = divisor;
|
||||||
_transformP = useBitTransform ? &TransformPBit : &TransformPAdd;
|
if (useBitTransform)
|
||||||
|
{
|
||||||
|
_transformP = &TransformPBit;
|
||||||
|
}
|
||||||
|
else if (useResidue)
|
||||||
|
{
|
||||||
|
_transformP = &TransformPAdd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_transformP = &TransformPAddPrimes;
|
||||||
|
}
|
||||||
PResidue = new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, initialNumber: currentP, initialized: true), trackAllValues: true);
|
PResidue = new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, initialNumber: currentP, initialized: true), trackAllValues: true);
|
||||||
PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
PrimeTesters = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||||
// Note: --primes-device controls default device for library kernels; p primality remains CPU here.
|
// Note: --primes-device controls default device for library kernels; p primality remains CPU here.
|
||||||
@@ -782,6 +800,11 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_useResidueMode && _lastCompositeP)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool primeFlag = false, printToConsole = false;
|
bool primeFlag = false, printToConsole = false;
|
||||||
|
|
||||||
StringBuilder localBuilder = StringBuilderPool.Rent();
|
StringBuilder localBuilder = StringBuilderPool.Rent();
|
||||||
@@ -853,6 +876,20 @@ internal static class Program
|
|||||||
return (ulong)BitOperations.PopCount(value);
|
return (ulong)BitOperations.PopCount(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static ulong GetNextAddDiff(ulong remainder)
|
||||||
|
{
|
||||||
|
return remainder switch
|
||||||
|
{
|
||||||
|
0UL => 1UL,
|
||||||
|
1UL => 4UL,
|
||||||
|
2UL => 3UL,
|
||||||
|
3UL => 2UL,
|
||||||
|
4UL => 1UL,
|
||||||
|
_ => 2UL,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static ulong TransformPBit(ulong value, ref ulong remainder)
|
internal static ulong TransformPBit(ulong value, ref ulong remainder)
|
||||||
{
|
{
|
||||||
@@ -894,26 +931,80 @@ internal static class Program
|
|||||||
internal static ulong TransformPAdd(ulong value, ref ulong remainder)
|
internal static ulong TransformPAdd(ulong value, ref ulong remainder)
|
||||||
{
|
{
|
||||||
ulong next = value;
|
ulong next = value;
|
||||||
value = remainder switch
|
ulong diff = GetNextAddDiff(remainder);
|
||||||
{
|
|
||||||
0UL => 1UL,
|
|
||||||
1UL => 4UL,
|
|
||||||
2UL => 3UL,
|
|
||||||
3UL => 2UL,
|
|
||||||
4UL => 1UL,
|
|
||||||
_ => 2UL,
|
|
||||||
}; // 'value' now holds diff
|
|
||||||
|
|
||||||
if (next > ulong.MaxValue - value)
|
if (next > ulong.MaxValue - diff)
|
||||||
{
|
{
|
||||||
Volatile.Write(ref _limitReached, true);
|
Volatile.Write(ref _limitReached, true);
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
remainder += value;
|
remainder += diff;
|
||||||
remainder -= (remainder >= 6UL) ? 6UL : 0UL;
|
remainder -= (remainder >= 6UL) ? 6UL : 0UL;
|
||||||
|
|
||||||
return next + value;
|
return next + diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static ulong TransformPAddPrimes(ulong value, ref ulong remainder)
|
||||||
|
{
|
||||||
|
ulong originalRemainder = remainder;
|
||||||
|
ulong addRemainder = remainder;
|
||||||
|
ulong candidate = value;
|
||||||
|
ulong primeCandidate = value;
|
||||||
|
bool advanceAdd = true;
|
||||||
|
bool advancePrime = true;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (advanceAdd)
|
||||||
|
{
|
||||||
|
ulong diff = GetNextAddDiff(addRemainder);
|
||||||
|
if (candidate > ulong.MaxValue - diff)
|
||||||
|
{
|
||||||
|
Volatile.Write(ref _limitReached, true);
|
||||||
|
remainder = originalRemainder;
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate += diff;
|
||||||
|
addRemainder += diff;
|
||||||
|
addRemainder -= (addRemainder >= 6UL) ? 6UL : 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advancePrime)
|
||||||
|
{
|
||||||
|
ulong nextPrime;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nextPrime = PrimeIterator.Next(in primeCandidate);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
Volatile.Write(ref _limitReached, true);
|
||||||
|
remainder = originalRemainder;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextPrime <= primeCandidate)
|
||||||
|
{
|
||||||
|
Volatile.Write(ref _limitReached, true);
|
||||||
|
remainder = originalRemainder;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
primeCandidate = nextPrime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate == primeCandidate)
|
||||||
|
{
|
||||||
|
remainder = addRemainder;
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
advanceAdd = candidate < primeCandidate;
|
||||||
|
advancePrime = candidate > primeCandidate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -923,9 +1014,11 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
searchedMersenne = false;
|
searchedMersenne = false;
|
||||||
detailedCheck = false;
|
detailedCheck = false;
|
||||||
|
_lastCompositeP = false;
|
||||||
|
|
||||||
if (_useGcdFilter && IsCompositeByGcd(p))
|
if (_useGcdFilter && IsCompositeByGcd(p))
|
||||||
{
|
{
|
||||||
|
_lastCompositeP = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -969,6 +1062,7 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
searchedMersenne = false;
|
searchedMersenne = false;
|
||||||
detailedCheck = false;
|
detailedCheck = false;
|
||||||
|
_lastCompositeP = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -979,6 +1073,7 @@ internal static class Program
|
|||||||
: PrimeTesters.Value!.IsPrimeGpu(p, CancellationToken.None);
|
: PrimeTesters.Value!.IsPrimeGpu(p, CancellationToken.None);
|
||||||
if (!isPrimeP)
|
if (!isPrimeP)
|
||||||
{
|
{
|
||||||
|
_lastCompositeP = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user