using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; using PerfectNumbers.Core; namespace EvenPerfectBitScanner.Benchmarks; [MemoryDiagnoser] public class Mod10_8_5_3Benchmarks { public ulong Mod10R; public ulong Mod8R; public ulong Mod3R; public ulong Mod5R; private static readonly ulong[] ValueCases = [ 3UL, 6UL, 128UL, 256UL, 2047UL, 8191UL, 65535UL, 131071UL, 2147483647UL, ulong.MaxValue - 1024UL, ulong.MaxValue - 511UL, ulong.MaxValue - 255UL, ulong.MaxValue - 127UL, ulong.MaxValue - 63UL, ulong.MaxValue - 31UL, ulong.MaxValue - 17UL, ]; public static IEnumerable GetValues() => ValueCases; /// /// Baseline scalar modulo path; previously 1.13-1.14 ns across the shared dataset and remains the reference point here. /// [Benchmark(Baseline = true)] [ArgumentsSource(nameof(GetValues))] public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModuloOperator(ulong value) { return (value % 10UL, value % 8UL, value % 3UL, value % 5UL); } /// /// Legacy folding helper; 2.83-3.07 ns in the original suite, now exercised on the combined dataset. /// [Benchmark] [ArgumentsSource(nameof(GetValues))] public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyModulo(ulong value) { ulong mod10; ulong mod8; ulong mod3; ulong mod5; UInt128 zero = UInt128.Zero; if (value == 0UL) { mod3 = 0UL; mod5 = 0UL; mod8 = 0UL; mod10 = 0UL; return (mod10, mod8, mod3, mod5); } UInt128 high = value >> 64; ulong result = value; mod8 = result & 7UL; ulong modRem = (result % 3UL) + ((ulong)high % 3UL); mod3 = modRem >= 3UL ? modRem - 3UL : modRem; modRem = (result % 5UL) + ((ulong)high % 5UL); mod5 = modRem >= 5UL ? modRem - 5UL : modRem; while (high != zero) { result = (result + (ulong)high * 6UL) % 10UL; high >>= 64; } mod10 = result % 10UL; return (mod10, mod8, mod3, mod5); } /// /// Method-based optimized helper; 1.63-2.16 ns in the original benchmarks, roughly 1.4-1.9× slower than the raw modulo. /// [Benchmark] [ArgumentsSource(nameof(GetValues))] public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodModulo(ulong value) { value.Mod10_8_5_3(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3); return (mod10, mod8, mod3, mod5); } /// /// Legacy property-based helper; 2.35-2.46 ns at small values and up to 2.46 ns near ulong.Max. /// [Benchmark] [ArgumentsSource(nameof(GetValues))] public (ulong Mod10, ulong Mod8, ulong Mod5, ulong Mod3) LegacyPropertyModulo(ulong value) { UInt128 wideValue = value; Mod10R = wideValue.Mod10(); Mod8R = Mod8(wideValue); Mod5R = Mod5(wideValue); Mod3R = Mod3(wideValue); return (Mod10R, Mod8R, Mod5R, Mod3R); } /// /// Method variant for the property helper; 2.79-4.34 ns in the original run, about 1.2-1.8× slower than the legacy property path. /// [Benchmark] [ArgumentsSource(nameof(GetValues))] public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodPropertyModulo(ulong value) { value.Mod10_8_5_3(out Mod10R, out Mod8R, out Mod5R, out Mod3R); return (Mod10R, Mod8R, Mod3R, Mod5R); } /// /// Legacy step-per-bit helper; 11.9-13.4 ns in the original suite and remains the slowest option. /// [Benchmark] [ArgumentsSource(nameof(GetValues))] public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) LegacyStepsModulo(ulong value) { UInt128 wideValue = value; ulong mod10 = (((ulong)(wideValue % 10UL)) << 1) % 10UL; ulong mod8 = (((ulong)(wideValue & 7UL)) << 1) & 7UL; ulong mod5 = (((ulong)(wideValue % 5UL)) << 1) % 5UL; ulong mod3 = (((ulong)(wideValue % 3UL)) << 1) % 3UL; return (mod10, mod8, mod3, mod5); } /// /// Step helper exposed via ; 3.58-3.77 ns previously, delivering 3.2-3.6× speedups over the legacy step path. /// [Benchmark] [ArgumentsSource(nameof(GetValues))] public (ulong Mod10, ulong Mod8, ulong Mod3, ulong Mod5) ModMethodStepsModulo(ulong value) { value.Mod10_8_5_3Steps(out ulong mod10, out ulong mod8, out ulong mod5, out ulong mod3); return (mod10, mod8, mod3, mod5); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Mod8(UInt128 value) => (ulong)value & 7UL; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Mod3(UInt128 value) { ulong high = (ulong)(value >> 64); ulong low = (ulong)value; ulong rem = (low % 3UL) + (high % 3UL); return rem >= 3UL ? rem - 3UL : rem; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Mod5(UInt128 value) { ulong high = (ulong)(value >> 64); ulong low = (ulong)value; ulong rem = (low % 5UL) + (high % 5UL); return rem >= 5UL ? rem - 5UL : rem; } }