Merge pull request #21 from mlemanczyk/codex/modify-evenperfectbitscanner-for-prime-iteration

Skip composite residue outputs
This commit is contained in:
Marek Lemańczyk authored and GitHub committed 2025-09-16 11:23:23 +02:00
commit ababca03cd
3 files changed
+194 -15

No files matched your search

+82 -2
View File
@@ -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>
+108 -13
View File
@@ -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;
} }