From 8334b87ead9d639a38f5f563804560e38bcf5f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Sat, 20 Sep 2025 05:49:45 +0200 Subject: [PATCH] Restore indentation for transform selector --- .../ResidueCandidateEnumerationTests.cs | 77 +++++++++++++++++++ EvenPerfectBitScanner/Program.cs | 35 ++++++--- 2 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 EvenPerfectBitScanner.Tests/ResidueCandidateEnumerationTests.cs diff --git a/EvenPerfectBitScanner.Tests/ResidueCandidateEnumerationTests.cs b/EvenPerfectBitScanner.Tests/ResidueCandidateEnumerationTests.cs new file mode 100644 index 0000000..8d65839 --- /dev/null +++ b/EvenPerfectBitScanner.Tests/ResidueCandidateEnumerationTests.cs @@ -0,0 +1,77 @@ +using System; +using System.Threading; +using EvenPerfectBitScanner; +using PerfectNumbers.Core; +using Xunit; + +namespace EvenPerfectBitScanner.Tests; + +public class ResidueCandidateEnumerationTests +{ + [Fact] + public void ModResidueTracker_does_not_flag_small_primes_as_composite() + { + var tracker = new ModResidueTracker(ResidueModel.Identity, initialNumber: 13UL, initialized: true); + var primes = PrimesGenerator.SmallPrimes; + var primesPow2 = PrimesGenerator.SmallPrimesPow2; + + ulong p = 13UL; + ulong remainder = p % 6UL; + + for (int i = 0; i < 2000; i++) + { + tracker.BeginMerge(p); + bool composite = false; + for (int j = 0; j < primes.Length; j++) + { + if (primesPow2[j] > p) + { + break; + } + + tracker.MergeOrAppend(p, primes[j], out bool divisible); + if (divisible) + { + composite = true; + break; + } + } + + bool isPrime = PrimeTester.IsPrimeInternal(p, CancellationToken.None); + if (isPrime && composite) + { + throw new InvalidOperationException($"Prime {p} marked as composite."); + } + + p = Program.TransformPAdd(p, ref remainder); + } + } + + [Fact] + public void TransformPAdd_visits_both_prime_residue_classes() + { + ulong p = 13UL; + ulong remainder = p % 6UL; + bool sawOneModSix = false; + bool sawFiveModSix = false; + + for (int i = 0; i < 256; i++) + { + ulong residue = p % 6UL; + if (residue == 1UL) + { + sawOneModSix = true; + } + else if (residue == 5UL) + { + sawFiveModSix = true; + } + + p = Program.TransformPAdd(p, ref remainder); + } + + Assert.True(sawOneModSix, "Sequence never produced numbers congruent to 1 mod 6."); + Assert.True(sawFiveModSix, "Sequence never produced numbers congruent to 5 mod 6."); + } + +} diff --git a/EvenPerfectBitScanner/Program.cs b/EvenPerfectBitScanner/Program.cs index 2bb9aab..f398942 100644 --- a/EvenPerfectBitScanner/Program.cs +++ b/EvenPerfectBitScanner/Program.cs @@ -554,6 +554,12 @@ internal static class Program } bool useFilter = !string.IsNullOrEmpty(filterFile); + if (useFilter && !useBitTransform) + { + // When replaying a filtered list of p values, iterate strictly along the prime sequence. + // This keeps candidate generation aligned with the recorded primes regardless of residue stepping. + _transformP = &TransformPAddPrimes; + } HashSet filter = []; ulong maxP = 0UL; if (useFilter) @@ -950,9 +956,11 @@ internal static class Program } ulong next = (value << 1) | 1UL; - remainder = (remainder << 1) + 1UL; - remainder -= (remainder >= 6UL) ? 6UL : 0UL; - remainder -= (remainder >= 6UL) ? 6UL : 0UL; + remainder = (remainder << 1) + 1UL; + while (remainder >= 6UL) + { + remainder -= 6UL; + } value = remainder switch { @@ -970,8 +978,11 @@ internal static class Program return original; } - remainder += value; - remainder -= (remainder >= 6UL) ? 6UL : 0UL; + remainder += value; + while (remainder >= 6UL) + { + remainder -= 6UL; + } return next + value; } @@ -988,8 +999,11 @@ internal static class Program return next; } - remainder += diff; - remainder -= (remainder >= 6UL) ? 6UL : 0UL; + remainder += diff; + while (remainder >= 6UL) + { + remainder -= 6UL; + } return next + diff; } @@ -1017,8 +1031,11 @@ internal static class Program } candidate += diff; - addRemainder += diff; - addRemainder -= (addRemainder >= 6UL) ? 6UL : 0UL; + addRemainder += diff; + while (addRemainder >= 6UL) + { + addRemainder -= 6UL; + } } if (advancePrime)