using System.Runtime.CompilerServices; using ILGPU; using ILGPU.Algorithms; using ILGPU.Runtime; using PerfectNumbers.Core.Gpu.Accelerators; namespace PerfectNumbers.Core.Gpu.Kernels; public enum HeuristicGpuDivisorTableKind : byte { GroupA = 0, GroupBEnding1 = 1, GroupBEnding7 = 7, GroupBEnding9 = 9, } internal static class PrimeTesterKernels { // GPU kernel: small-prime sieve only. Returns 1 if passes sieve (probable prime), 0 otherwise. public static void SmallPrimeSieveKernel( Index1D index, ArrayView numbers, ArrayView smallPrimesLastOne, ArrayView smallPrimesLastSeven, ArrayView smallPrimesLastThree, ArrayView smallPrimesLastNine, ArrayView smallPrimesPow2LastOne, ArrayView smallPrimesPow2LastSeven, ArrayView smallPrimesPow2LastThree, ArrayView smallPrimesPow2LastNine, ArrayView results) { ulong n = numbers[index]; byte result = 1; ArrayView primes; ArrayView primeSquares; ulong lastDigit = n % 10UL; switch (lastDigit) { case 1UL: primes = smallPrimesLastOne; primeSquares = smallPrimesPow2LastOne; break; case 3UL: primes = smallPrimesLastThree; primeSquares = smallPrimesPow2LastThree; break; case 7UL: primes = smallPrimesLastSeven; primeSquares = smallPrimesPow2LastSeven; break; case 9UL: primes = smallPrimesLastNine; primeSquares = smallPrimesPow2LastNine; break; default: return; } int length = (int)primes.Length; for (int i = 0; i < length; i++) { ulong primeSquare = primeSquares[i]; if (primeSquare > n) { break; } ulong prime = primes[i]; if (n % prime == 0UL) { result = 0; break; } } results[index] = result; } public static void HeuristicTrialCombinedDivisionKernel( Index1D index, // byte nMod10, ArrayView resultFlag, ulong n, ulong maxDivisorSquare, ArrayView divisors, ArrayView divisorSquares) { // var (divisors, divisorSquares) = tables.SelectDivisorsAndSquares(nMod10); int threadIndex = index; if (divisorSquares[threadIndex] <= maxDivisorSquare && (n % divisors[threadIndex] == 0UL)) { Atomic.Exchange(ref resultFlag[0], 1); } } public static void HeuristicTrialGroupABDivisionKernel( Index1D index, ArrayView resultFlag, ulong n, ulong maxDivisorSquare, HeuristicGpuDivisorTableKind tableKind, HeuristicGroupABGpuViews tables) { ArrayView1D divisors = tables.SelectDivisors(tableKind); ArrayView1D divisorSquares = tables.SelectDivisorSquares(tableKind); int divisorLength = (int)divisorSquares.Length; int threadIndex = index; if (threadIndex >= divisorLength) { return; } ulong divisorSquare = divisorSquares[threadIndex]; if (divisorSquare > maxDivisorSquare) { return; } ulong divisor = divisors[threadIndex]; divisor = n % divisor; if (divisor == 0UL) { resultFlag[0] = 1; } } public static void SharesFactorKernel(Index1D index, ArrayView numbers, ArrayView results) { ulong n = numbers[index]; ulong m = 63UL - (ulong)XMath.LeadingZeroCount(n); ulong gcd = BinaryGcdGpu(n, m); results[index] = gcd == 1UL ? (byte)0 : (byte)1; } private static ulong BinaryGcdGpu(ulong u, ulong v) { bool uIsZero = u == 0UL; bool vIsZero = v == 0UL; bool eitherZero = uIsZero | vIsZero; ulong zeroResult = uIsZero ? v : u; ulong combined = (u | v) | 1UL; int shift = XMath.TrailingZeroCount(combined); ulong currentU = u >> XMath.TrailingZeroCount(u | 1UL); ulong currentV = v >> XMath.TrailingZeroCount(v | 1UL); while (true) { currentV >>= XMath.TrailingZeroCount(currentV); bool swap = currentU > currentV; ulong minValue = ConditionalSelect(currentU, currentV, swap); ulong maxValue = ConditionalSelect(currentV, currentU, swap); currentV = maxValue - minValue; currentU = minValue; if (currentV == 0UL) { ulong gcd = currentU << shift; return eitherZero ? zeroResult : gcd; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong ConditionalSelect(ulong left, ulong right, bool useRight) { ulong mask = useRight ? ulong.MaxValue : 0UL; return left ^ ((left ^ right) & mask); } }