Reapply modulo helpers and inline fast remainders

This commit is contained in:
Marek Lemańczyk committed 2025-09-22 19:26:07 +02:00
1 parent 5db0c4fa95
commit ca866ffc5b
42 files changed
+906 -58

No files matched your search

@@ -22,10 +22,10 @@ public class Mod10UInt128Benchmarks
ulong value = (ulong)Value; ulong value = (ulong)Value;
ulong high = value >> 64; ulong high = value >> 64;
ulong low = value; ulong low = value;
ulong highRem = high.Mod10(); ulong highRem = high % 10UL;
ulong lowRem = low.Mod10(); ulong lowRem = low % 10UL;
ulong combined = lowRem + highRem * 6UL; ulong combined = lowRem + highRem * 6UL;
return combined.Mod10(); return combined % 10UL;
} }
[Benchmark] [Benchmark]
@@ -42,11 +42,11 @@ public class Mod10UInt128Benchmarks
while (high != zero) while (high != zero)
{ {
// 2^64 ≡ 6 (mod 10) // 2^64 ≡ 6 (mod 10)
result = (result + (ulong)high * 6UL).Mod10(); result = (result + (ulong)high * 6UL) % 10UL;
high >>= 64; high >>= 64;
} }
return result.Mod10(); return result % 10UL;
} }
} }
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod10UIntBenchmarks
{
[Params(10U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 10U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod10();
}
}
@@ -11,6 +11,13 @@ public class Mod10_8_5_3Benchmarks
public ulong Value { get; set; } public ulong Value { get; set; }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModuloOperator()
{
ulong value = Value;
return (value % 10UL, value % 8UL, value % 3UL, value % 5UL);
}
[Benchmark]
public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo() public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo()
{ {
ulong mod10, mod8, mod3, mod5; ulong mod10, mod8, mod3, mod5;
@@ -26,7 +26,7 @@ public class Mod10_8_5_3StepsBenchmarks
{ {
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);
// Step residues for q += 2*p // Step residues for q += 2*p
mod10 = mod10.Mod10(); mod10 %= 10UL;
return (mod10, mod8, mod3, mod5); return (mod10, mod8, mod3, mod5);
} }
} }
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod11UInt128Benchmarks
{
[Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 11UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod11();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod11UIntBenchmarks
{
[Params(11U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 11U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod11();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod11ULongBenchmarks
{
[Params(11UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 11UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod11();
}
}
@@ -0,0 +1,32 @@
using BenchmarkDotNet.Attributes;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[MemoryDiagnoser]
public class Mod11_7_5_3Benchmarks
{
[Params(3UL, 8191UL, 131071UL, 2147483647UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) ModuloOperator()
{
ulong value = Value;
return (value % 11UL, value % 7UL, value % 5UL, value % 3UL);
}
[Benchmark]
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) ExtensionMethods()
{
ulong value = Value;
return (value.Mod11(), value.Mod7(), value.Mod5(), value.Mod3());
}
[Benchmark]
public (ulong Mod11, ulong Mod7, ulong Mod5, ulong Mod3) CombinedMethod()
{
Value.Mod11_7_5_3(out ulong mod11, out ulong mod7, out ulong mod5, out ulong mod3);
return (mod11, mod7, mod5, mod3);
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod128UInt128Benchmarks
{
[Params(128UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 511UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 128UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod128();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod128UIntBenchmarks
{
[Params(128U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 128U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod128();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod128ULongBenchmarks
{
[Params(128UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 128UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod128();
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod3UInt128Benchmarks
{
[Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 3UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod3();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod3UIntBenchmarks
{
[Params(3U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 3U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod3();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod3ULongBenchmarks
{
[Params(3UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 17UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 3UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod3();
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod5UInt128Benchmarks
{
[Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 5UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod5();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod5UIntBenchmarks
{
[Params(5U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 5U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod5();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod5ULongBenchmarks
{
[Params(5UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 31UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 5UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod5();
}
}
@@ -0,0 +1,81 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 3)]
[MemoryDiagnoser]
public class Mod6ComparisonBenchmarks
{
private const int Iterations = 10_000_000;
private const ulong Divisor = 6UL;
private static readonly ulong FastDivMul = (ulong)(((UInt128)1 << 64) / Divisor);
[Params(3UL, 131071UL, ulong.MaxValue - 1024UL)]
public ulong StartValue { get; set; }
[Benchmark(Baseline = true, OperationsPerInvoke = Iterations)]
public ulong ModuloOperator()
{
ulong checksum = 0UL;
ulong value = StartValue;
for (int i = 0; i < Iterations; i++)
{
checksum += value % Divisor;
value += (ulong)((i & 15) + 1);
}
return checksum;
}
[Benchmark(OperationsPerInvoke = Iterations)]
public ulong DivisionBased()
{
ulong checksum = 0UL;
ulong value = StartValue;
for (int i = 0; i < Iterations; i++)
{
ulong quotient = value / Divisor;
checksum += value - quotient * Divisor;
value += (ulong)((i & 15) + 1);
}
return checksum;
}
[Benchmark(OperationsPerInvoke = Iterations)]
public ulong FastDivHigh()
{
ulong checksum = 0UL;
ulong value = StartValue;
for (int i = 0; i < Iterations; i++)
{
ulong quotient = value.FastDiv64(Divisor, FastDivMul);
checksum += value - quotient * Divisor;
value += (ulong)((i & 15) + 1);
}
return checksum;
}
[Benchmark(OperationsPerInvoke = Iterations)]
public ulong ExtensionMethod()
{
ulong checksum = 0UL;
ulong value = StartValue;
for (int i = 0; i < Iterations; i++)
{
checksum += value.Mod6();
value += (ulong)((i & 15) + 1);
}
return checksum;
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod6UInt128Benchmarks
{
[Params(6UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 63UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 6UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod6();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod6UIntBenchmarks
{
[Params(6U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 6U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod6();
}
}
@@ -0,0 +1,43 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 3)]
[MemoryDiagnoser]
public class Mod6ULongBenchmarks
{
private static readonly ulong Divisor = 6UL;
private static readonly ulong FastDivMul = (ulong)(((UInt128)1 << 64) / Divisor);
[Params(3UL, 131071UL, ulong.MaxValue)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % Divisor;
}
[Benchmark]
public ulong DivisionBased()
{
ulong quotient = Value / Divisor;
return Value - quotient * Divisor;
}
[Benchmark]
public ulong FastDivHigh()
{
ulong quotient = Value.FastDiv64(Divisor, FastDivMul);
return Value - quotient * Divisor;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod6();
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod7UInt128Benchmarks
{
[Params(7UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 7UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod7();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod7UIntBenchmarks
{
[Params(7U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 7U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod7();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod7ULongBenchmarks
{
[Params(7UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 63UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 7UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod7();
}
}
@@ -0,0 +1,27 @@
using System.Numerics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod8UInt128Benchmarks
{
[Params(8UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 255UL)]
public UInt128 Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return (ulong)(Value % 8UL);
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod8();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod8UIntBenchmarks
{
[Params(8U, 2047U, 65535U, 2147483647U)]
public uint Value { get; set; }
[Benchmark(Baseline = true)]
public uint ModuloOperator()
{
return Value % 8U;
}
[Benchmark]
public uint ExtensionMethod()
{
return Value.Mod8();
}
}
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using PerfectNumbers.Core;
namespace EvenPerfectBitScanner.Benchmarks;
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
[MemoryDiagnoser]
public class Mod8ULongBenchmarks
{
[Params(8UL, 8191UL, 131071UL, 2147483647UL, ulong.MaxValue - 127UL)]
public ulong Value { get; set; }
[Benchmark(Baseline = true)]
public ulong ModuloOperator()
{
return Value % 8UL;
}
[Benchmark]
public ulong ExtensionMethod()
{
return Value.Mod8();
}
}
@@ -1,3 +1,4 @@
using System;
using BenchmarkDotNet.Running; using BenchmarkDotNet.Running;
namespace EvenPerfectBitScanner.Benchmarks; namespace EvenPerfectBitScanner.Benchmarks;
+1 -1
View File
@@ -1595,7 +1595,7 @@ internal static class Program
// Optional: RLE blacklist and binary-threshold filters on p (safe only when configured) // Optional: RLE blacklist and binary-threshold filters on p (safe only when configured)
if (p <= _rleHardMaxP) if (p <= _rleHardMaxP)
{ {
if (!_rleOnlyLast7 || p.Mod10() == 7UL) if (!_rleOnlyLast7 || p % 10UL == 7UL)
{ {
if (RleBlacklist.IsLoaded() && RleBlacklist.Matches(p)) if (RleBlacklist.IsLoaded() && RleBlacklist.Matches(p))
{ {
@@ -201,6 +201,14 @@ public class UInt128ExtensionsTests
value.Mod3().Should().Be((ulong)(value % 3)); 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] [Fact]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
public void Mod5_matches_operator() public void Mod5_matches_operator()
@@ -27,6 +27,17 @@ public class UIntExtensionsTests
value.Mod5().Should().Be(value % 5U); value.Mod5().Should().Be(value % 5U);
} }
[Theory]
[Trait("Category", "Fast")]
[InlineData(0U)]
[InlineData(1U)]
[InlineData(1234567890U)]
[InlineData(uint.MaxValue)]
public void Mod6_matches_operator(uint value)
{
value.Mod6().Should().Be(value % 6U);
}
[Theory] [Theory]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
[InlineData(0U)] [InlineData(0U)]
@@ -41,6 +41,16 @@ public class ULongExtensionsTests
value.Mod5().Should().Be(value % 5UL); value.Mod5().Should().Be(value % 5UL);
} }
[Theory]
[Trait("Category", "Fast")]
[InlineData(0UL)]
[InlineData(ulong.MaxValue)]
[InlineData(1234567890123456789UL)]
public void Mod6_matches_operator(ulong value)
{
value.Mod6().Should().Be(value % 6UL);
}
[Theory] [Theory]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
[InlineData(0UL)] [InlineData(0UL)]
@@ -101,6 +111,22 @@ public class ULongExtensionsTests
step3.Should().Be(expected3); step3.Should().Be(expected3);
} }
[Theory]
[Trait("Category", "Fast")]
[InlineData(0UL)]
[InlineData(1UL)]
[InlineData(1234567890123456789UL)]
[InlineData(ulong.MaxValue)]
public void Mod11_7_5_3_matches_operator(ulong value)
{
value.Mod11_7_5_3(out ulong mod11, out ulong mod7, out ulong mod5, out ulong mod3);
mod11.Should().Be(value % 11UL);
mod7.Should().Be(value % 7UL);
mod5.Should().Be(value % 5UL);
mod3.Should().Be(value % 3UL);
}
[Theory] [Theory]
[Trait("Category", "Fast")] [Trait("Category", "Fast")]
[InlineData(2UL, true)] [InlineData(2UL, true)]
@@ -8,11 +8,15 @@ public class MersenneNumberLucasLehmerCpuTester
// - If 3 | p and p != 3, then 7 | M_p -> composite. // - If 3 | p and p != 3, then 7 | M_p -> composite.
// - If p ≡ 1 (mod 4) and p shares a factor with (p-1), reject fast. // - If p ≡ 1 (mod 4) and p shares a factor with (p-1), reject fast.
// - If p is divisible by divisors specific to numbers ending with 1 or 7, reject fast. // - If p is divisible by divisors specific to numbers ending with 1 or 7, reject fast.
ulong mod3 = exponent.Mod3();
ulong mod5 = exponent.Mod5();
ulong mod7 = exponent.Mod7();
ulong mod11 = exponent.Mod11();
if ( if (
((exponent % 3UL) == 0UL && exponent != 3UL) || (mod3 == 0UL && exponent != 3UL) ||
((exponent % 5UL) == 0UL && exponent != 5UL) || (mod5 == 0UL && exponent != 5UL) ||
((exponent % 7UL) == 0UL && exponent != 7UL) || (mod7 == 0UL && exponent != 7UL) ||
((exponent % 11UL) == 0UL && exponent != 11UL) (mod11 == 0UL && exponent != 11UL)
) )
{ {
return false; return false;
@@ -21,7 +21,7 @@ public class MersenneNumberResidueCpuTester
q.Mod10_8_5_3(out ulong r10, out ulong r8, out ulong r5, out ulong r3); q.Mod10_8_5_3(out ulong r10, out ulong r8, out ulong r5, out ulong r3);
// Step residues for q += 2*p // Step residues for q += 2*p
twoP.Mod10_8_5_3(out ulong step10, out ulong step8, out ulong step5, out ulong step3); twoP.Mod10_8_5_3(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
step10 = step10.Mod10(); step10 %= 10UL;
// Allowed last-digit sets for q depending on last digit of M_p // Allowed last-digit sets for q depending on last digit of M_p
// lastIsSeven == true => allow {7,9} // lastIsSeven == true => allow {7,9}
@@ -51,7 +51,7 @@ public class MersenneNumberLucasLehmerGpuTester
// Early rejections aligned with incremental/order sieves, but safe for small p: // Early rejections aligned with incremental/order sieves, but safe for small p:
// - If 3 | p and p != 3, then 7 | M_p -> composite. // - If 3 | p and p != 3, then 7 | M_p -> composite.
// - If p ≡ 1 (mod 4) and p shares a factor with (p-1), reject fast. // - If p ≡ 1 (mod 4) and p shares a factor with (p-1), reject fast.
if ((exponent % 3UL) == 0UL && exponent != 3UL) if (exponent % 3UL == 0UL && exponent != 3UL)
{ {
return false; return false;
} }
@@ -548,7 +548,7 @@ public class MersenneNumberLucasLehmerGpuTester
} }
} }
int topBits = (int)(exponent % 128UL); int topBits = (int)exponent.Mod128();
if (topBits == 0) if (topBits == 0)
{ {
topBits = 128; topBits = 128;
@@ -672,7 +672,7 @@ public class MersenneNumberLucasLehmerGpuTester
} }
} }
int topBits = (int)(exponent % 128UL); int topBits = (int)exponent.Mod128();
if (topBits == 0) if (topBits == 0)
{ {
topBits = 128; topBits = 128;
@@ -803,7 +803,7 @@ public class MersenneNumberLucasLehmerGpuTester
vLow.Add(vHigh); vLow.Add(vHigh);
// Mask to keep only p low bits and fold the carry bits (v >> p) // Mask to keep only p low bits and fold the carry bits (v >> p)
int topBits = (int)(p % 128UL); int topBits = (int)p.Mod128();
if (topBits == 0) if (topBits == 0)
{ {
topBits = 128; topBits = 128;
@@ -27,7 +27,7 @@ public class MersenneNumberOrderGpuTester(GpuKernelType kernelType, bool useGpuO
var foundBuffer = accelerator.Allocate1D<int>(1); var foundBuffer = accelerator.Allocate1D<int>(1);
UInt128 exponent128 = exponent; UInt128 exponent128 = exponent;
exponent128.Mod10_8_5_3(out ulong exponentMod10, out ulong exponentMod8, out ulong exponentMod5, out ulong exponentMod3); exponent128.Mod10_8_5_3(out ulong exponentMod10, out ulong exponentMod8, out ulong exponentMod5, out ulong exponentMod3);
ulong step10 = (exponentMod10 << 1).Mod10(); ulong step10 = (exponentMod10 << 1) % 10UL;
ulong step8 = (exponentMod8 << 1) & 7UL; ulong step8 = (exponentMod8 << 1) & 7UL;
ulong step3 = (exponentMod3 << 1) % 3UL; ulong step3 = (exponentMod3 << 1) % 3UL;
ulong step5 = (exponentMod5 << 1) % 5UL; ulong step5 = (exponentMod5 << 1) % 5UL;
+7 -7
View File
@@ -67,7 +67,7 @@ public sealed class MersenneNumberTester(
for (; k <= limit; k++) for (; k <= limit; k++)
{ {
q = twoP * k + UInt128.One; q = twoP * k + UInt128.One;
remainder = q.Mod10(); remainder = (ulong)(q % 10UL);
bool shouldCheck = lastIsSeven bool shouldCheck = lastIsSeven
? remainder == 7UL || remainder == 9UL || remainder == 3UL ? remainder == 7UL || remainder == 9UL || remainder == 3UL
: remainder == 1UL || remainder == 3UL || remainder == 9UL; : remainder == 1UL || remainder == 3UL || remainder == 9UL;
@@ -75,12 +75,12 @@ public sealed class MersenneNumberTester(
// Early modular filters for odd order and small-prime rejections // Early modular filters for odd order and small-prime rejections
if (shouldCheck) if (shouldCheck)
{ {
ulong mod8 = q.Mod8(); ulong mod8 = (ulong)(q % 8UL);
if (mod8 != 1UL && mod8 != 7UL) if (mod8 != 1UL && mod8 != 7UL)
{ {
continue; continue;
} }
else if (q.Mod3() == 0UL || q.Mod5() == 0UL) else if ((ulong)(q % 3UL) == 0UL || (ulong)(q % 5UL) == 0UL)
{ {
continue; continue;
} }
@@ -106,7 +106,7 @@ public sealed class MersenneNumberTester(
for (; k2 <= limit; k2++) for (; k2 <= limit; k2++)
{ {
q2 = twoP * k2 + 1UL; q2 = twoP * k2 + 1UL;
remainder2 = q2.Mod10(); remainder2 = (ulong)(q2 % 10UL);
bool shouldCheck2 = lastIsSeven bool shouldCheck2 = lastIsSeven
? remainder2 == 7UL || remainder2 == 9UL || remainder2 == 3UL ? remainder2 == 7UL || remainder2 == 9UL || remainder2 == 3UL
: remainder2 == 1UL || remainder2 == 3UL || remainder2 == 9UL; : remainder2 == 1UL || remainder2 == 3UL || remainder2 == 9UL;
@@ -114,12 +114,12 @@ public sealed class MersenneNumberTester(
// Early modular filters for odd order and small-prime rejections // Early modular filters for odd order and small-prime rejections
if (shouldCheck2) if (shouldCheck2)
{ {
ulong mod8 = q2.Mod8(); ulong mod8 = (ulong)(q2 % 8UL);
if (mod8 != 1UL && mod8 != 7UL) if (mod8 != 1UL && mod8 != 7UL)
{ {
shouldCheck2 = false; shouldCheck2 = false;
} }
else if (q2.Mod3() == 0UL || q2.Mod5() == 0UL) else if ((ulong)(q2 % 3UL) == 0UL || (ulong)(q2 % 5UL) == 0UL)
{ {
shouldCheck2 = false; shouldCheck2 = false;
} }
@@ -203,7 +203,7 @@ public sealed class MersenneNumberTester(
{ {
// Safe early rejections using known orders for tiny primes: // Safe early rejections using known orders for tiny primes:
// 7 | M_p iff 3 | p. Avoid rejecting p == 3 where M_3 == 7 is prime. // 7 | M_p iff 3 | p. Avoid rejecting p == 3 where M_3 == 7 is prime.
if ((exponent % 3UL) == 0UL && exponent != 3UL) if (exponent % 3UL == 0UL && exponent != 3UL)
{ {
return false; return false;
} }
-21
View File
@@ -1,21 +0,0 @@
using System.Runtime.CompilerServices;
namespace PerfectNumbers.Core;
public static class PerfectNumbersMath
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint FastRemainder3(uint value)
{
uint quotient = (uint)(((ulong)value * 0xAAAAAAABUL) >> 33);
return value - quotient * 3U;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint FastRemainder5(uint value)
{
uint quotient = (uint)(((ulong)value * 0xCCCCCCCDUL) >> 34);
return value - quotient * 5U;
}
}
+4 -4
View File
@@ -53,13 +53,13 @@ public sealed class PrimeTester(bool useInternal = false)
{ {
result = false; result = false;
} }
else if (n > 5UL && (n % 5UL) == 0UL) else if (n > 5UL && n % 5UL == 0UL)
{ {
result = false; result = false;
} }
else else
{ {
if (n.Mod10() == 1UL && SharesFactorWithMaxExponent(n)) if (n % 10UL == 1UL && SharesFactorWithMaxExponent(n))
{ {
result = false; result = false;
} }
@@ -260,13 +260,13 @@ public sealed class PrimeTester(bool useInternal = false)
return; return;
} }
if ((n & 1UL) == 0UL || (n > 5UL && (n % 5UL) == 0UL)) if ((n & 1UL) == 0UL || (n > 5UL && n % 5UL == 0UL))
{ {
results[index] = 0; results[index] = 0;
return; return;
} }
if (n.Mod10() == 1UL) if (n % 10UL == 1UL)
{ {
// Early reject special GCD heuristic with floor(log2 n) // Early reject special GCD heuristic with floor(log2 n)
ulong m = 63UL - (ulong)ILGPU.Algorithms.XMath.LeadingZeroCount(n); ulong m = 63UL - (ulong)ILGPU.Algorithms.XMath.LeadingZeroCount(n);
+18 -2
View File
@@ -135,8 +135,8 @@ public static class UInt128Extensions
mod8 = (ulong)value & 7UL; mod8 = (ulong)value & 7UL;
uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum); uint mod5Value = byteSum.Mod5();
uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum); uint mod3Value = byteSum.Mod3();
mod10 = (mod8 & 1UL) == 0UL mod10 = (mod8 & 1UL) == 0UL
? mod5Value switch ? mod5Value switch
@@ -238,6 +238,22 @@ public static class UInt128Extensions
return remainder >= 5UL ? remainder - 5UL : remainder; return remainder >= 5UL ? remainder - 5UL : remainder;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Mod6(this UInt128 value)
{
ulong mod3 = value.Mod3();
ulong key = (mod3 << 1) | ((ulong)value & 1UL);
return key switch
{
0UL => 0UL,
1UL => 3UL,
2UL => 4UL,
3UL => 1UL,
4UL => 2UL,
_ => 5UL,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Mod7(this UInt128 value) public static ulong Mod7(this UInt128 value)
{ {
+20 -3
View File
@@ -4,11 +4,28 @@ namespace PerfectNumbers.Core;
public static class UIntExtensions public static class UIntExtensions
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] private static readonly byte[] Mod6Lookup = { 0, 3, 4, 1, 2, 5 };
public static uint Mod3(this uint value) => PerfectNumbersMath.FastRemainder3(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Mod5(this uint value) => PerfectNumbersMath.FastRemainder5(value); public static uint Mod3(this uint value)
{
uint quotient = (uint)(((ulong)value * 0xAAAAAAABUL) >> 33);
return value - quotient * 3U;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Mod5(this uint value)
{
uint quotient = (uint)(((ulong)value * 0xCCCCCCCDUL) >> 34);
return value - quotient * 5U;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Mod6(this uint value)
{
uint key = (value.Mod3() << 1) | (value & 1U);
return Mod6Lookup[(int)key];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Mod7(this uint value) public static uint Mod7(this uint value)
+74 -4
View File
@@ -57,6 +57,10 @@ public static class ULongExtensions
return q; return q;
} }
private static readonly byte[] Mod6Lookup = { 0, 3, 4, 1, 2, 5 };
private static readonly byte[] Mod7ByteCoefficients = { 1, 4, 2, 1, 4, 2, 1, 4 };
private static readonly byte[] Mod11ByteCoefficients = { 1, 3, 9, 5, 4, 1, 3, 9 };
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Mod10_8_5_3(this ulong value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3) public static void Mod10_8_5_3(this ulong value, out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3)
{ {
@@ -72,8 +76,8 @@ public static class ULongExtensions
mod8 = value & 7UL; mod8 = value & 7UL;
uint mod5Value = PerfectNumbersMath.FastRemainder5(byteSum); uint mod5Value = byteSum.Mod5();
uint mod3Value = PerfectNumbersMath.FastRemainder3(byteSum); uint mod3Value = byteSum.Mod3();
mod10 = (mod8 & 1UL) == 0UL mod10 = (mod8 & 1UL) == 0UL
? mod5Value switch ? mod5Value switch
@@ -97,6 +101,35 @@ public static class ULongExtensions
mod3 = mod3Value; mod3 = mod3Value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Mod11_7_5_3(this ulong value, out ulong mod11, out ulong mod7, out ulong mod5, out ulong mod3)
{
uint mod3Accumulator = 0U;
uint mod5Accumulator = 0U;
uint mod7Accumulator = 0U;
uint mod11Accumulator = 0U;
ulong temp = value;
int index = 0;
do
{
byte current = (byte)temp;
mod3Accumulator += current;
mod5Accumulator += current;
mod7Accumulator += (uint)(current * Mod7ByteCoefficients[index]);
mod11Accumulator += (uint)(current * Mod11ByteCoefficients[index]);
temp >>= 8;
index++;
}
while (temp != 0UL);
mod3 = mod3Accumulator.Mod3();
mod5 = mod5Accumulator.Mod5();
mod7 = ReduceMod7(mod7Accumulator);
mod11 = ReduceMod11(mod11Accumulator);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Mod10_8_5_3Steps(this ulong value, out ulong step10, out ulong step8, out ulong step5, out ulong step3) public static void Mod10_8_5_3Steps(this ulong value, out ulong step10, out ulong step8, out ulong step5, out ulong step3)
{ {
@@ -171,7 +204,7 @@ public static class ULongExtensions
sum += (uint)((value >> 32) & 0xFFFFUL); sum += (uint)((value >> 32) & 0xFFFFUL);
sum += (uint)((value >> 48) & 0xFFFFUL); sum += (uint)((value >> 48) & 0xFFFFUL);
return PerfectNumbersMath.FastRemainder3(sum); return sum.Mod3();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -182,7 +215,14 @@ public static class ULongExtensions
sum += (uint)((value >> 32) & 0xFFFFUL); sum += (uint)((value >> 32) & 0xFFFFUL);
sum += (uint)((value >> 48) & 0xFFFFUL); sum += (uint)((value >> 48) & 0xFFFFUL);
return PerfectNumbersMath.FastRemainder5(sum); return sum.Mod5();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Mod6(this ulong value)
{
ulong key = (value.Mod3() << 1) | (value & 1UL);
return Mod6Lookup[(int)key];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -218,6 +258,36 @@ public static class ULongExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Mod128(this ulong value) => value & 127UL; public static ulong Mod128(this ulong value) => value & 127UL;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong ReduceMod7(uint value)
{
while (value >= 7U)
{
value = (value >> 3) + (value & 7U);
if (value >= 7U && value < 14U)
{
value -= 7U;
}
}
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong ReduceMod11(uint value)
{
while (value >= 11U)
{
value = (value & 15U) + ((value >> 4) * 5U);
if (value >= 11U && value < 22U)
{
value -= 11U;
}
}
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UInt128 Mul64(this ulong a, ulong b) => ((UInt128)a.MulHigh(b) << 64) | (UInt128)(a * b); public static UInt128 Mul64(this ulong a, ulong b) => ((UInt128)a.MulHigh(b) << 64) | (UInt128)(a * b);