Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6027a73839 |
No files matched your search
@@ -8,15 +8,6 @@ EvenPerfectBitScanner.ResultsParser/bin/*
|
||||
EvenPerfectBitScanner.ResultsParser/obj/*
|
||||
EvenPerfectBitScanner.Tests/bin/*
|
||||
EvenPerfectBitScanner.Tests/obj/*
|
||||
BuildDivisorTransitions/bin/*
|
||||
BuildDivisorTransitions/obj/*
|
||||
DeviceDependentGenerator/bin/*
|
||||
DeviceDependentGenerator/obj/*
|
||||
DeviceDependentGenerator.Tests/bin/*
|
||||
DeviceDependentGenerator.Tests/obj/*
|
||||
PerfectNumbers.Core.Tests/bin/*
|
||||
PerfectNumbers.Core.Tests/obj/*
|
||||
.DS_Store
|
||||
.vs/*
|
||||
*.generated.cs
|
||||
PerfectNumbers.Core/generated/*
|
||||
Vendored
+10
-38
@@ -8,8 +8,7 @@
|
||||
"options": [
|
||||
"EvenPerfectBitScanner",
|
||||
"EvenPerfectBitScanner.ResultsParser",
|
||||
"EvenPerfectBitScanner.Benchmarks",
|
||||
"BuildDivisorTransitions"
|
||||
"EvenPerfectBitScanner.Benchmarks"
|
||||
],
|
||||
"default": "EvenPerfectBitScanner"
|
||||
}
|
||||
@@ -20,7 +19,7 @@
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "Build Debug",
|
||||
"program": "${workspaceFolder}/${input:projectName}/bin/Debug/net9.0/${input:projectName}.dll",
|
||||
"program": "${workspaceFolder}/${input:projectName}/bin/Debug/net8.0/${input:projectName}.dll",
|
||||
"args": [
|
||||
"--increment=add",
|
||||
"--mersenne=bydivisor",
|
||||
@@ -47,7 +46,7 @@
|
||||
"request": "launch",
|
||||
"noDebug": true,
|
||||
"preLaunchTask": "Build Release",
|
||||
"program": "${workspaceFolder}/${input:projectName}/bin/Release/net9.0/${input:projectName}.dll",
|
||||
"program": "${workspaceFolder}/${input:projectName}/bin/Release/net8.0/${input:projectName}.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
@@ -61,34 +60,7 @@
|
||||
"request": "launch",
|
||||
"noDebug": true,
|
||||
"preLaunchTask": "Build EvenPerfectBitScanner Release",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner/bin/Release/net9.0/EvenPerfectBitScanner.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
"name": "Run BuildDivisorTransitions",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"noDebug": true,
|
||||
"preLaunchTask": "Build BuildDivisorTransitions Release",
|
||||
"program": "${workspaceFolder}/BuildDivisorTransitions/bin/Release/net9.0/BuildDivisorTransitions.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
"name": "Debug BuildDivisorTransitions",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "Build BuildDivisorTransitions Debug",
|
||||
"program": "${workspaceFolder}/BuildDivisorTransitions/bin/Debug/net9.0/BuildDivisorTransitions.dll",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner/bin/Release/net8.0/EvenPerfectBitScanner.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
@@ -102,7 +74,7 @@
|
||||
"request": "launch",
|
||||
"noDebug": true,
|
||||
"preLaunchTask": "Build EvenPerfectBitScanner.ResultsParser Release",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner.ResultsParser/bin/Release/net9.0/EvenPerfectBitScanner.ResultsParser.dll",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner.ResultsParser/bin/Release/net8.0/EvenPerfectBitScanner.ResultsParser.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
@@ -115,7 +87,7 @@
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "Build EvenPerfectBitScanner Debug",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner/bin/Debug/net9.0/EvenPerfectBitScanner.dll",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner/bin/Debug/net8.0/EvenPerfectBitScanner.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
@@ -128,7 +100,7 @@
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "Build EvenPerfectBitScanner.ResultsParser Debug",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner.ResultsParser/bin/Debug/net9.0/EvenPerfectBitScanner.ResultsParser.dll",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner.ResultsParser/bin/Debug/net8.0/EvenPerfectBitScanner.ResultsParser.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
@@ -142,7 +114,7 @@
|
||||
"request": "launch",
|
||||
"noDebug": true,
|
||||
"preLaunchTask": "Build EvenPerfectBitScanner.Benchmarks Release",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner.Benchmarks/bin/Release/net9.0/EvenPerfectBitScanner.Benchmarks.dll",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner.Benchmarks/bin/Release/net8.0/EvenPerfectBitScanner.Benchmarks.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
@@ -155,7 +127,7 @@
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "Build EvenPerfectBitScanner.Benchmarks Debug",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner.Benchmarks/bin/Debug/net9.0/EvenPerfectBitScanner.Benchmarks.dll",
|
||||
"program": "${workspaceFolder}/EvenPerfectBitScanner.Benchmarks/bin/Debug/net8.0/EvenPerfectBitScanner.Benchmarks.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
@@ -164,4 +136,4 @@
|
||||
"justMyCode": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Vendored
+1
-4
@@ -2,7 +2,6 @@
|
||||
"dotnet.defaultSolution": "FlexiCoder.sln",
|
||||
"chatgpt.openOnStartup": true,
|
||||
"cSpell.words": [
|
||||
"bydivisor",
|
||||
"CCCCCCCDUL",
|
||||
"cofactor",
|
||||
"ILGPU",
|
||||
@@ -10,7 +9,5 @@
|
||||
"Mersenne",
|
||||
"primality",
|
||||
"suff"
|
||||
],
|
||||
"geminicodeassist.project": "evenperfectbitscanner",
|
||||
"todo-tree.tree.showCountsInTree": true
|
||||
]
|
||||
}
|
||||
Vendored
+13
-60
@@ -11,8 +11,7 @@
|
||||
"--configuration",
|
||||
"Debug",
|
||||
"--framework",
|
||||
"net9.0",
|
||||
"/p:DeviceDependentGenerator_CopyGeneratedToProjectDir=true"
|
||||
"net8.0"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
@@ -30,8 +29,7 @@
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0",
|
||||
"/p:DeviceDependentGenerator_CopyGeneratedToProjectDir=true"
|
||||
"net8.0"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
@@ -46,7 +44,7 @@
|
||||
"--configuration",
|
||||
"Debug",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
@@ -61,7 +59,7 @@
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
@@ -76,7 +74,7 @@
|
||||
"--configuration",
|
||||
"Debug",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
@@ -91,37 +89,7 @@
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build BuildDivisorTransitions Debug",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/BuildDivisorTransitions/BuildDivisorTransitions.csproj",
|
||||
"--configuration",
|
||||
"Debug",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build BuildDivisorTransitions Release",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/BuildDivisorTransitions/BuildDivisorTransitions.csproj",
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
@@ -137,7 +105,7 @@
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
@@ -152,22 +120,7 @@
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Run BuildDivisorTransitions",
|
||||
"type": "process",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/BuildDivisorTransitions/BuildDivisorTransitions.csproj",
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
@@ -180,7 +133,7 @@
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
@@ -194,7 +147,7 @@
|
||||
"--configuration",
|
||||
"Debug",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
@@ -209,7 +162,7 @@
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
@@ -225,9 +178,9 @@
|
||||
"--configuration",
|
||||
"Release",
|
||||
"--framework",
|
||||
"net9.0"
|
||||
"net8.0"
|
||||
],
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,257 +0,0 @@
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace BuildDivisorTransitions;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
private const string OutputFileName = "NextKForecastTraning.csv";
|
||||
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
{
|
||||
PrintUsage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
string candidatesPath = args[0];
|
||||
string divisorsPath = args[1];
|
||||
|
||||
if (!File.Exists(candidatesPath))
|
||||
{
|
||||
Console.Error.WriteLine($"Candidates file not found: {candidatesPath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!File.Exists(divisorsPath))
|
||||
{
|
||||
Console.Error.WriteLine($"Divisors file not found: {divisorsPath}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
List<long> candidatePs = LoadCandidates(candidatesPath);
|
||||
Dictionary<long, BigInteger> divisors = LoadDivisors(divisorsPath);
|
||||
List<Transition> transitions = BuildTransitions(candidatePs, divisors);
|
||||
|
||||
WriteOutput(transitions);
|
||||
|
||||
Console.WriteLine($"Saved {transitions.Count} transitions to {OutputFileName}.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void PrintUsage()
|
||||
{
|
||||
Console.WriteLine("Usage: BuildDivisorTransitions <candidatesCsv> <divisorsCsv>");
|
||||
Console.WriteLine(" candidatesCsv: CSV with columns p,searchedMersenne,detailedCheck,passedAllTests");
|
||||
Console.WriteLine(" divisorsCsv: CSV with columns p,divisor");
|
||||
}
|
||||
|
||||
private static List<long> LoadCandidates(string path)
|
||||
{
|
||||
var result = new List<long>();
|
||||
var seen = new HashSet<long>();
|
||||
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string? line = reader.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string trimmedLine = line.Trim();
|
||||
if (trimmedLine.StartsWith("p", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = trimmedLine.Split(',', StringSplitOptions.TrimEntries);
|
||||
if (parts.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!long.TryParse(parts[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out long p))
|
||||
{
|
||||
throw new FormatException($"Invalid p value '{parts[0]}' in {path}.");
|
||||
}
|
||||
|
||||
if (seen.Add(p))
|
||||
{
|
||||
result.Add(p);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<long, BigInteger> LoadDivisors(string path)
|
||||
{
|
||||
var result = new Dictionary<long, BigInteger>();
|
||||
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string? line = reader.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string trimmedLine = line.Trim();
|
||||
if (trimmedLine.StartsWith("p", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = trimmedLine.Split(',', StringSplitOptions.TrimEntries);
|
||||
if (parts.Length < 2)
|
||||
{
|
||||
throw new FormatException($"Line '{line}' in {path} does not contain both p and divisor columns.");
|
||||
}
|
||||
|
||||
if (!long.TryParse(parts[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out long p))
|
||||
{
|
||||
throw new FormatException($"Invalid p value '{parts[0]}' in {path}.");
|
||||
}
|
||||
|
||||
if (!BigInteger.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out BigInteger divisor))
|
||||
{
|
||||
throw new FormatException($"Invalid divisor value '{parts[1]}' for p={p} in {path}.");
|
||||
}
|
||||
|
||||
if (IsProperMersenneDivisor(p, divisor))
|
||||
{
|
||||
result[p] = divisor;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine($"Skipping invalid divisor entry p={p}, divisor={divisor} (not a proper divisor of 2^{p}-1 or fails powmod check).");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<Transition> BuildTransitions(IReadOnlyList<long> candidatePs, IReadOnlyDictionary<long, BigInteger> divisors)
|
||||
{
|
||||
var transitions = new List<Transition>();
|
||||
|
||||
for (int i = 0; i < candidatePs.Count - 1; i++)
|
||||
{
|
||||
long currentP = candidatePs[i];
|
||||
long nextP = candidatePs[i + 1];
|
||||
|
||||
if (!divisors.TryGetValue(currentP, out BigInteger currentDivisor))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!divisors.TryGetValue(nextP, out BigInteger nextDivisor))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BigInteger currentK = CalculateK(currentP, currentDivisor);
|
||||
BigInteger nextK = CalculateK(nextP, nextDivisor);
|
||||
bool isNextKHigher = nextK > currentK;
|
||||
transitions.Add(new Transition(currentP, currentDivisor, LastDigit(currentP), currentK, LastMersenneDigit(currentP), isNextKHigher));
|
||||
}
|
||||
|
||||
return transitions;
|
||||
}
|
||||
|
||||
private static BigInteger CalculateK(long p, BigInteger divisor)
|
||||
{
|
||||
BigInteger numerator = divisor - BigInteger.One;
|
||||
BigInteger denominator = new BigInteger(p) << 1;
|
||||
|
||||
if (denominator <= BigInteger.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"Computed 2p is not positive for p={p}.");
|
||||
}
|
||||
|
||||
BigInteger remainder = numerator % denominator;
|
||||
if (remainder != BigInteger.Zero)
|
||||
{
|
||||
throw new InvalidOperationException($"Divisor {divisor} is not congruent to 1 mod 2p for p={p}.");
|
||||
}
|
||||
|
||||
BigInteger k = numerator / denominator;
|
||||
if (k < BigInteger.One)
|
||||
{
|
||||
throw new InvalidOperationException($"Calculated k={k} is less than 1 for p={p}, divisor={divisor}.");
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
private static bool IsProperMersenneDivisor(long p, in BigInteger divisor)
|
||||
{
|
||||
if (p <= 1 || divisor <= BigInteger.One)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int bitLength = GetBitLength(divisor);
|
||||
if (bitLength > p)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bitLength == p && IsPowerOfTwo(divisor + BigInteger.One))
|
||||
{
|
||||
// Divisor equals the Mersenne number itself.
|
||||
return false;
|
||||
}
|
||||
|
||||
return BigInteger.ModPow(2, p, divisor) == BigInteger.One;
|
||||
}
|
||||
|
||||
private static int GetBitLength(in BigInteger value)
|
||||
{
|
||||
if (value.IsZero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte[] bytes = value.ToByteArray(isUnsigned: true, isBigEndian: false);
|
||||
byte msb = bytes[^1];
|
||||
return ((bytes.Length - 1) * 8) + (BitOperations.Log2(msb) + 1);
|
||||
}
|
||||
|
||||
private static bool IsPowerOfTwo(in BigInteger value)
|
||||
{
|
||||
return value > BigInteger.One && (value & (value - BigInteger.One)) == BigInteger.Zero;
|
||||
}
|
||||
|
||||
private static void WriteOutput(IReadOnlyCollection<Transition> transitions)
|
||||
{
|
||||
using var writer = new StreamWriter(OutputFileName, false, Encoding.UTF8);
|
||||
writer.WriteLine("p,divisor,lastDigit,k,lastMersenneDigit,isNextKHigher");
|
||||
|
||||
foreach (Transition transition in transitions)
|
||||
{
|
||||
writer.WriteLine($"{transition.P.ToString(CultureInfo.InvariantCulture)},{FormatBigInteger(transition.Divisor)},{transition.LastDigit},{FormatBigInteger(transition.CurrentK)},{transition.LastMersenneDigit},{(transition.IsNextKHigher ? "1" : "0")}");
|
||||
}
|
||||
}
|
||||
|
||||
private static int LastDigit(long p) => (int)(Math.Abs(p) % 10);
|
||||
|
||||
private static int LastMersenneDigit(long p)
|
||||
{
|
||||
long mod = p & 3;
|
||||
return mod == 1 ? 1 : 7; // odd primes give p%4 == 1 or 3; 2^p mod 10 cycles -> 1 or 7 after subtracting 1
|
||||
}
|
||||
|
||||
private static string FormatBigInteger(in BigInteger value) => value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
private sealed record Transition(long P, BigInteger Divisor, int LastDigit, BigInteger CurrentK, int LastMersenneDigit, bool IsNextKHigher);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DeviceDependentGenerator\DeviceDependentGenerator.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace DeviceDependentGenerator.Tests;
|
||||
|
||||
public sealed class DeviceDependentGeneratorTests
|
||||
{
|
||||
[Fact]
|
||||
public void Generates_classes_per_enum_value_and_filters_device_blocks_with_suffix()
|
||||
{
|
||||
const string attributeAndEnum = """
|
||||
using System;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class DeviceDependentTemplateAttribute : Attribute
|
||||
{
|
||||
public DeviceDependentTemplateAttribute(Type enumType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = true)]
|
||||
public sealed class EnumDependentTemplateAttribute : Attribute
|
||||
{
|
||||
public EnumDependentTemplateAttribute(Type enumType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class NameSuffixAttribute : Attribute
|
||||
{
|
||||
public NameSuffixAttribute(string suffix) { }
|
||||
}
|
||||
|
||||
public enum CalculationDevice
|
||||
{
|
||||
Cpu,
|
||||
Hybrid,
|
||||
Gpu,
|
||||
}
|
||||
""";
|
||||
|
||||
const string template = """
|
||||
using System;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
[DeviceDependentTemplate(typeof(CalculationDevice))]
|
||||
[NameSuffix("Impl")]
|
||||
public static class PrimeCalculatorTemplate
|
||||
{
|
||||
#if DEVICE_CPU
|
||||
public static string Device => "CPU";
|
||||
#elif DEVICE_HYBRID
|
||||
public static string Device => "HYBRID";
|
||||
#elif DEVICE_GPU
|
||||
public static string Device => "GPU";
|
||||
#else
|
||||
#error Unknown device
|
||||
#endif
|
||||
}
|
||||
""";
|
||||
|
||||
CSharpCompilation compilation = CreateCompilation(attributeAndEnum, template);
|
||||
|
||||
var generator = new PerfectNumbers.Core.Generators.DeviceDependentGenerator();
|
||||
var parseOptions = (CSharpParseOptions)compilation.SyntaxTrees.First().Options;
|
||||
ISourceGenerator sourceGenerator = generator.AsSourceGenerator();
|
||||
GeneratorDriver driver = CSharpGeneratorDriver.Create(new[] { sourceGenerator }, parseOptions: parseOptions);
|
||||
driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out _, out ImmutableArray<Diagnostic> diagnostics);
|
||||
|
||||
diagnostics.Should().BeEmpty();
|
||||
|
||||
GeneratorDriverRunResult runResult = driver.GetRunResult();
|
||||
runResult.Diagnostics.Should().BeEmpty();
|
||||
|
||||
ImmutableArray<GeneratedSourceResult> generated = runResult.Results.Single().GeneratedSources;
|
||||
|
||||
string cpu = generated.Single(s => s.HintName.EndsWith("PrimeCalculator.CpuImpl.generated.cs", StringComparison.Ordinal)).SourceText.ToString();
|
||||
string hybrid = generated.Single(s => s.HintName.EndsWith("PrimeCalculator.HybridImpl.generated.cs", StringComparison.Ordinal)).SourceText.ToString();
|
||||
string gpu = generated.Single(s => s.HintName.EndsWith("PrimeCalculator.GpuImpl.generated.cs", StringComparison.Ordinal)).SourceText.ToString();
|
||||
|
||||
cpu.Should().Contain("class PrimeCalculatorCpuImpl");
|
||||
cpu.Should().Contain("=> \"CPU\"");
|
||||
cpu.Should().NotContain("#if");
|
||||
cpu.Should().NotContain("#endif");
|
||||
|
||||
hybrid.Should().Contain("class PrimeCalculatorHybridImpl");
|
||||
hybrid.Should().Contain("=> \"HYBRID\"");
|
||||
hybrid.Should().NotContain("#if");
|
||||
hybrid.Should().NotContain("#endif");
|
||||
|
||||
gpu.Should().Contain("class PrimeCalculatorGpuImpl");
|
||||
gpu.Should().Contain("=> \"GPU\"");
|
||||
gpu.Should().NotContain("#if");
|
||||
gpu.Should().NotContain("#endif");
|
||||
}
|
||||
|
||||
private static CSharpCompilation CreateCompilation(params string[] sources)
|
||||
{
|
||||
var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp12);
|
||||
var trees = sources.Select((text, i) => CSharpSyntaxTree.ParseText(text, parseOptions, path: "Test" + i + ".cs"));
|
||||
|
||||
var references = new[]
|
||||
{
|
||||
typeof(object),
|
||||
typeof(Attribute),
|
||||
typeof(Enumerable),
|
||||
typeof(System.Runtime.GCSettings),
|
||||
}
|
||||
.Select(static t => MetadataReference.CreateFromFile(t.Assembly.Location))
|
||||
.DistinctBy(static r => r.Display)
|
||||
.ToList();
|
||||
|
||||
return CSharpCompilation.Create(
|
||||
assemblyName: "DeviceDependentGenerator.Tests.Compilation",
|
||||
syntaxTrees: trees,
|
||||
references: references,
|
||||
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
|
||||
}
|
||||
}
|
||||
@@ -1,678 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace PerfectNumbers.Core.Generators;
|
||||
|
||||
[Generator(LanguageNames.CSharp)]
|
||||
public sealed class DeviceDependentGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string DeviceDependentTemplateAttributeMetadataName = "PerfectNumbers.Core.DeviceDependentTemplateAttribute";
|
||||
private const string EnumDependentTemplateAttributeMetadataName = "PerfectNumbers.Core.EnumDependentTemplateAttribute";
|
||||
private const string NameSuffixAttributeMetadataName = "PerfectNumbers.Core.NameSuffixAttribute";
|
||||
|
||||
private static readonly DiagnosticDescriptor TemplateEnumTypeMustBeEnum = new(
|
||||
id: "DDG001",
|
||||
title: "Template attribute requires an enum type",
|
||||
messageFormat: "Template attribute '{0}' requires an enum type, but '{1}' is not an enum.",
|
||||
category: "DeviceDependentGenerator",
|
||||
defaultSeverity: DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true);
|
||||
|
||||
private static readonly DiagnosticDescriptor TemplateMustBeTopLevel = new(
|
||||
id: "DDG002",
|
||||
title: "DeviceDependentTemplate must be top-level",
|
||||
messageFormat: "DeviceDependentTemplate can only be used on a top-level class/struct (not nested types).",
|
||||
category: "DeviceDependentGenerator",
|
||||
defaultSeverity: DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true);
|
||||
|
||||
private static readonly DiagnosticDescriptor TemplateNotFoundInDeviceParse = new(
|
||||
id: "DDG003",
|
||||
title: "Template not found after preprocessing",
|
||||
messageFormat: "Could not find template type '{0}' after preprocessing for combination '{1}'.",
|
||||
category: "DeviceDependentGenerator",
|
||||
defaultSeverity: DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true);
|
||||
|
||||
private static readonly DiagnosticDescriptor DuplicateSingleUseAttribute = new(
|
||||
id: "DDG004",
|
||||
title: "Template attribute used multiple times",
|
||||
messageFormat: "Attribute '{0}' can be applied only once to '{1}'.",
|
||||
category: "DeviceDependentGenerator",
|
||||
defaultSeverity: DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true);
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
IncrementalValuesProvider<TemplateInfo> templates = context.SyntaxProvider.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => node is TypeDeclarationSyntax typeDecl && typeDecl.AttributeLists.Count > 0,
|
||||
transform: static (syntaxContext, cancellationToken) => TryGetTemplateInfo(syntaxContext, cancellationToken))
|
||||
.Where(static info => info is not null)!
|
||||
.Select(static (info, _) => info!);
|
||||
|
||||
IncrementalValueProvider<GeneratorOptions> options = context.AnalyzerConfigOptionsProvider.Select(
|
||||
static (provider, _) => new GeneratorOptions(provider.GlobalOptions));
|
||||
|
||||
IncrementalValueProvider<(Compilation Compilation, GeneratorOptions Options)> compilationAndOptions =
|
||||
context.CompilationProvider.Combine(options).Select(static (pair, _) => (pair.Left, pair.Right));
|
||||
|
||||
context.RegisterSourceOutput(templates.Combine(compilationAndOptions), static (productionContext, pair) =>
|
||||
{
|
||||
GenerateForTemplate(productionContext, pair.Left, pair.Right.Compilation, pair.Right.Options);
|
||||
});
|
||||
}
|
||||
|
||||
private static TemplateInfo? TryGetTemplateInfo(GeneratorSyntaxContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (context.Node is not TypeDeclarationSyntax typeSyntax)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (context.SemanticModel.GetDeclaredSymbol(typeSyntax, cancellationToken) is not INamedTypeSymbol templateSymbol)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return CollectTemplateInfo(templateSymbol, typeSyntax, cancellationToken);
|
||||
}
|
||||
|
||||
private static void GenerateForTemplate(SourceProductionContext context, TemplateInfo template, Compilation compilation, GeneratorOptions options)
|
||||
{
|
||||
foreach (Diagnostic diagnostic in template.Diagnostics)
|
||||
{
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
|
||||
if (!template.Valid || !template.HasAnyTemplateAttribute)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (template.DeclaringSyntaxReference?.GetSyntax(context.CancellationToken) is not TypeDeclarationSyntax templateSyntax)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsTopLevel(templateSyntax))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(TemplateMustBeTopLevel, templateSyntax.Identifier.GetLocation()));
|
||||
return;
|
||||
}
|
||||
|
||||
string templateName = template.Name;
|
||||
string baseName = templateName.EndsWith("Template", StringComparison.Ordinal)
|
||||
? templateName.Substring(0, templateName.Length - "Template".Length)
|
||||
: templateName;
|
||||
|
||||
SourceText fileText = templateSyntax.SyntaxTree.GetText(context.CancellationToken);
|
||||
CSharpParseOptions baseParseOptions = (CSharpParseOptions)templateSyntax.SyntaxTree.Options;
|
||||
string filePath = templateSyntax.SyntaxTree.FilePath;
|
||||
|
||||
foreach (TemplateVariant variant in template.ExpandVariants())
|
||||
{
|
||||
CSharpParseOptions parseOptions = WithDefines(baseParseOptions, variant.PreprocessorSymbols);
|
||||
|
||||
SyntaxTree deviceTree = CSharpSyntaxTree.ParseText(
|
||||
fileText.ToString(),
|
||||
parseOptions,
|
||||
filePath,
|
||||
encoding: Encoding.UTF8);
|
||||
|
||||
CompilationUnitSyntax deviceRoot = deviceTree.GetCompilationUnitRoot(context.CancellationToken);
|
||||
TypeDeclarationSyntax? deviceTemplateSyntax = FindTemplateDeclaration(deviceRoot, templateName);
|
||||
if (deviceTemplateSyntax is null)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
TemplateNotFoundInDeviceParse,
|
||||
templateSyntax.Identifier.GetLocation(),
|
||||
templateName,
|
||||
variant.CombinationDisplay));
|
||||
continue;
|
||||
}
|
||||
|
||||
ReportDeviceSpecificTemplateErrors(context, deviceTree);
|
||||
|
||||
string generatedTypeName = baseName + variant.NameSuffix + template.NameSuffix;
|
||||
TypeDeclarationSyntax generatedTypeSyntax = TransformTemplateType(deviceTemplateSyntax, generatedTypeName);
|
||||
|
||||
CompilationUnitSyntax generatedRoot = BuildGeneratedFile(
|
||||
externs: deviceRoot.Externs,
|
||||
usings: deviceRoot.Usings,
|
||||
containingNamespace: template.ContainingNamespace,
|
||||
typeDeclaration: generatedTypeSyntax);
|
||||
|
||||
generatedRoot = (CompilationUnitSyntax)new ConditionalDirectiveAndDisabledTextStripper().Visit(generatedRoot)!;
|
||||
generatedRoot = generatedRoot.NormalizeWhitespace();
|
||||
|
||||
string generatedFileName = baseName + "." + variant.FileSuffix + template.NameSuffix + ".generated.cs";
|
||||
string hintName = options.BuildHintName(template.ContainingNamespace, generatedFileName);
|
||||
context.AddSource(
|
||||
hintName,
|
||||
SourceText.From("// <auto-generated/>\n#nullable enable\n" + generatedRoot.ToFullString(), Encoding.UTF8));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReportDeviceSpecificTemplateErrors(SourceProductionContext context, SyntaxTree deviceTree)
|
||||
{
|
||||
foreach (Diagnostic diagnostic in deviceTree.GetDiagnostics(context.CancellationToken))
|
||||
{
|
||||
if (diagnostic.Severity == DiagnosticSeverity.Error)
|
||||
{
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTopLevel(TypeDeclarationSyntax typeDeclaration)
|
||||
=> typeDeclaration.Parent is NamespaceDeclarationSyntax
|
||||
|| typeDeclaration.Parent is FileScopedNamespaceDeclarationSyntax
|
||||
|| typeDeclaration.Parent is CompilationUnitSyntax;
|
||||
|
||||
private static CSharpParseOptions WithDefines(CSharpParseOptions baseParseOptions, IEnumerable<string> defines)
|
||||
=> baseParseOptions.WithPreprocessorSymbols(baseParseOptions.PreprocessorSymbolNames.Concat(defines).Distinct());
|
||||
|
||||
private static TypeDeclarationSyntax? FindTemplateDeclaration(CompilationUnitSyntax root, string templateName)
|
||||
{
|
||||
foreach (TypeDeclarationSyntax typeDecl in root.DescendantNodes().OfType<TypeDeclarationSyntax>())
|
||||
{
|
||||
if (string.Equals(typeDecl.Identifier.ValueText, templateName, StringComparison.Ordinal))
|
||||
{
|
||||
return typeDecl;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static TypeDeclarationSyntax TransformTemplateType(TypeDeclarationSyntax templateType, string generatedTypeName)
|
||||
{
|
||||
TypeDeclarationSyntax withoutAttribute = RemoveTemplateAttributes(templateType);
|
||||
TypeDeclarationSyntax renamed = (TypeDeclarationSyntax)new SelfIdentifierRewriter(templateType.Identifier.ValueText, generatedTypeName).Visit(withoutAttribute)!;
|
||||
return renamed.WithIdentifier(SyntaxFactory.Identifier(generatedTypeName));
|
||||
}
|
||||
|
||||
private static TypeDeclarationSyntax RemoveTemplateAttributes(TypeDeclarationSyntax typeDeclaration)
|
||||
{
|
||||
if (typeDeclaration.AttributeLists.Count == 0)
|
||||
{
|
||||
return typeDeclaration;
|
||||
}
|
||||
|
||||
var newLists = new List<AttributeListSyntax>(typeDeclaration.AttributeLists.Count);
|
||||
foreach (AttributeListSyntax list in typeDeclaration.AttributeLists)
|
||||
{
|
||||
SeparatedSyntaxList<AttributeSyntax> attributes = list.Attributes;
|
||||
var newAttributes = new List<AttributeSyntax>(attributes.Count);
|
||||
foreach (AttributeSyntax attribute in attributes)
|
||||
{
|
||||
if (IsTemplateAttribute(attribute.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
newAttributes.Add(attribute);
|
||||
}
|
||||
|
||||
if (newAttributes.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
newLists.Add(list.WithAttributes(SyntaxFactory.SeparatedList(newAttributes)));
|
||||
}
|
||||
|
||||
return typeDeclaration.WithAttributeLists(SyntaxFactory.List(newLists));
|
||||
}
|
||||
|
||||
private sealed class SelfIdentifierRewriter : CSharpSyntaxRewriter
|
||||
{
|
||||
private readonly string templateName;
|
||||
private readonly string generatedName;
|
||||
|
||||
public SelfIdentifierRewriter(string templateName, string generatedName)
|
||||
{
|
||||
this.templateName = templateName;
|
||||
this.generatedName = generatedName;
|
||||
}
|
||||
|
||||
public override SyntaxToken VisitToken(SyntaxToken token)
|
||||
{
|
||||
if (token.IsKind(SyntaxKind.IdentifierToken) && string.Equals(token.ValueText, templateName, StringComparison.Ordinal))
|
||||
{
|
||||
return SyntaxFactory.Identifier(token.LeadingTrivia, generatedName, token.TrailingTrivia);
|
||||
}
|
||||
|
||||
return base.VisitToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTemplateAttribute(NameSyntax attributeName)
|
||||
{
|
||||
string text = attributeName.ToString();
|
||||
return text.EndsWith("DeviceDependentTemplate", StringComparison.Ordinal)
|
||||
|| text.EndsWith("DeviceDependentTemplateAttribute", StringComparison.Ordinal)
|
||||
|| text.EndsWith("EnumDependentTemplate", StringComparison.Ordinal)
|
||||
|| text.EndsWith("EnumDependentTemplateAttribute", StringComparison.Ordinal)
|
||||
|| text.EndsWith("NameSuffix", StringComparison.Ordinal)
|
||||
|| text.EndsWith("NameSuffixAttribute", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static CompilationUnitSyntax BuildGeneratedFile(
|
||||
SyntaxList<ExternAliasDirectiveSyntax> externs,
|
||||
SyntaxList<UsingDirectiveSyntax> usings,
|
||||
string containingNamespace,
|
||||
TypeDeclarationSyntax typeDeclaration)
|
||||
{
|
||||
MemberDeclarationSyntax member;
|
||||
if (string.IsNullOrEmpty(containingNamespace))
|
||||
{
|
||||
member = typeDeclaration;
|
||||
}
|
||||
else
|
||||
{
|
||||
member = SyntaxFactory
|
||||
.FileScopedNamespaceDeclaration(SyntaxFactory.ParseName(containingNamespace))
|
||||
.WithMembers(SyntaxFactory.SingletonList<MemberDeclarationSyntax>(typeDeclaration));
|
||||
}
|
||||
|
||||
return SyntaxFactory
|
||||
.CompilationUnit()
|
||||
.WithExterns(externs)
|
||||
.WithUsings(usings)
|
||||
.WithMembers(SyntaxFactory.SingletonList(member));
|
||||
}
|
||||
|
||||
private sealed class TemplateInfo
|
||||
{
|
||||
private TemplateInfo(
|
||||
INamedTypeSymbol templateType,
|
||||
ImmutableArray<EnumTemplateInfo> enumAttributes,
|
||||
DeviceTemplateInfo? deviceAttribute,
|
||||
string nameSuffix,
|
||||
ImmutableArray<Diagnostic> diagnostics)
|
||||
{
|
||||
TemplateType = templateType;
|
||||
EnumAttributes = enumAttributes;
|
||||
DeviceAttribute = deviceAttribute;
|
||||
NameSuffix = nameSuffix;
|
||||
Diagnostics = diagnostics;
|
||||
}
|
||||
|
||||
public INamedTypeSymbol TemplateType { get; }
|
||||
public ImmutableArray<EnumTemplateInfo> EnumAttributes { get; }
|
||||
public DeviceTemplateInfo? DeviceAttribute { get; }
|
||||
public string NameSuffix { get; }
|
||||
public ImmutableArray<Diagnostic> Diagnostics { get; }
|
||||
|
||||
public bool Valid => Diagnostics.IsDefaultOrEmpty;
|
||||
public bool HasAnyTemplateAttribute => EnumAttributes.Length > 0 || DeviceAttribute is not null;
|
||||
|
||||
public string Name => TemplateType.Name;
|
||||
|
||||
public string ContainingNamespace => TemplateType.ContainingNamespace?.IsGlobalNamespace == true
|
||||
? string.Empty
|
||||
: TemplateType.ContainingNamespace.ToDisplayString();
|
||||
|
||||
public SyntaxReference? DeclaringSyntaxReference => TemplateType.DeclaringSyntaxReferences.FirstOrDefault();
|
||||
|
||||
public IEnumerable<TemplateVariant> ExpandVariants()
|
||||
{
|
||||
var combinations = ImmutableArray.Create(new TemplateVariantBuilder());
|
||||
|
||||
foreach (EnumTemplateInfo enumInfo in EnumAttributes)
|
||||
{
|
||||
combinations = ExpandEnum(combinations, enumInfo);
|
||||
}
|
||||
|
||||
bool hasEnumAttributes = EnumAttributes.Length > 0;
|
||||
|
||||
if (DeviceAttribute is not null)
|
||||
{
|
||||
combinations = ExpandDevice(combinations, DeviceAttribute.Value, hasEnumAttributes);
|
||||
}
|
||||
|
||||
foreach (TemplateVariantBuilder builder in combinations)
|
||||
{
|
||||
yield return new TemplateVariant(builder.Symbols, builder.NameSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
private static ImmutableArray<TemplateVariantBuilder> ExpandEnum(
|
||||
ImmutableArray<TemplateVariantBuilder> current,
|
||||
EnumTemplateInfo enumInfo)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<TemplateVariantBuilder>(current.Length * enumInfo.Values.Length);
|
||||
foreach (TemplateVariantBuilder existing in current)
|
||||
{
|
||||
foreach (string value in enumInfo.Values)
|
||||
{
|
||||
builder.Add(existing.Append(
|
||||
symbol: $"{enumInfo.EnumType.Name}_{value}",
|
||||
namePart: $"For{value}{enumInfo.EnumType.Name}"));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
|
||||
private static ImmutableArray<TemplateVariantBuilder> ExpandDevice(
|
||||
ImmutableArray<TemplateVariantBuilder> current,
|
||||
DeviceTemplateInfo deviceInfo,
|
||||
bool prefixWithFor)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<TemplateVariantBuilder>(current.Length * deviceInfo.Values.Length);
|
||||
foreach (TemplateVariantBuilder existing in current)
|
||||
{
|
||||
foreach (string value in deviceInfo.Values)
|
||||
{
|
||||
string namePart = prefixWithFor ? $"For{value}" : value;
|
||||
builder.Add(existing.Append(
|
||||
symbol: "DEVICE_" + value.ToUpperInvariant(),
|
||||
namePart: namePart));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
|
||||
public static TemplateInfo Create(
|
||||
INamedTypeSymbol templateType,
|
||||
ImmutableArray<EnumTemplateInfo> enumAttributes,
|
||||
DeviceTemplateInfo? deviceAttribute,
|
||||
string? nameSuffix,
|
||||
ImmutableArray<Diagnostic> diagnostics)
|
||||
=> new(
|
||||
templateType,
|
||||
enumAttributes,
|
||||
deviceAttribute,
|
||||
nameSuffix ?? string.Empty,
|
||||
diagnostics);
|
||||
}
|
||||
|
||||
private readonly struct EnumTemplateInfo
|
||||
{
|
||||
public EnumTemplateInfo(INamedTypeSymbol enumType, ImmutableArray<string> values, Location attributeLocation)
|
||||
{
|
||||
EnumType = enumType;
|
||||
Values = values;
|
||||
AttributeLocation = attributeLocation;
|
||||
}
|
||||
|
||||
public INamedTypeSymbol EnumType { get; }
|
||||
public ImmutableArray<string> Values { get; }
|
||||
public Location AttributeLocation { get; }
|
||||
}
|
||||
|
||||
private readonly struct DeviceTemplateInfo
|
||||
{
|
||||
public DeviceTemplateInfo(INamedTypeSymbol enumType, ImmutableArray<string> values, Location attributeLocation)
|
||||
{
|
||||
EnumType = enumType;
|
||||
Values = values;
|
||||
AttributeLocation = attributeLocation;
|
||||
}
|
||||
|
||||
public INamedTypeSymbol EnumType { get; }
|
||||
public ImmutableArray<string> Values { get; }
|
||||
public Location AttributeLocation { get; }
|
||||
}
|
||||
|
||||
private readonly struct TemplateVariant
|
||||
{
|
||||
public TemplateVariant(ImmutableArray<string> preprocessorSymbols, string nameSuffix)
|
||||
{
|
||||
PreprocessorSymbols = preprocessorSymbols;
|
||||
NameSuffix = nameSuffix;
|
||||
}
|
||||
|
||||
public ImmutableArray<string> PreprocessorSymbols { get; }
|
||||
public string NameSuffix { get; }
|
||||
public string FileSuffix => NameSuffix;
|
||||
public string CombinationDisplay => string.Join(", ", PreprocessorSymbols);
|
||||
}
|
||||
|
||||
private sealed class TemplateVariantBuilder
|
||||
{
|
||||
public TemplateVariantBuilder()
|
||||
{
|
||||
Symbols = ImmutableArray<string>.Empty;
|
||||
NameSuffix = string.Empty;
|
||||
}
|
||||
|
||||
private TemplateVariantBuilder(ImmutableArray<string> symbols, string nameSuffix)
|
||||
{
|
||||
Symbols = symbols;
|
||||
NameSuffix = nameSuffix;
|
||||
}
|
||||
|
||||
public ImmutableArray<string> Symbols { get; }
|
||||
public string NameSuffix { get; }
|
||||
|
||||
public TemplateVariantBuilder Append(string symbol, string namePart)
|
||||
{
|
||||
return new TemplateVariantBuilder(Symbols.Add(symbol), NameSuffix + namePart);
|
||||
}
|
||||
}
|
||||
|
||||
private static TemplateInfo? CollectTemplateInfo(INamedTypeSymbol templateSymbol, TypeDeclarationSyntax typeSyntax, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
ImmutableArray<AttributeData> attributes = templateSymbol.GetAttributes();
|
||||
var enumAttributes = new List<EnumTemplateInfo>();
|
||||
DeviceTemplateInfo? deviceAttribute = null;
|
||||
string? nameSuffix = null;
|
||||
var diagnostics = ImmutableArray.CreateBuilder<Diagnostic>();
|
||||
|
||||
AttributeData? nameSuffixAttribute = null;
|
||||
AttributeData? deviceTemplateAttribute = null;
|
||||
foreach (AttributeData attribute in attributes)
|
||||
{
|
||||
string? metadataName = attribute.AttributeClass?.ToDisplayString();
|
||||
if (metadataName is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.Equals(metadataName, EnumDependentTemplateAttributeMetadataName, StringComparison.Ordinal))
|
||||
{
|
||||
enumAttributes.AddRange(CreateEnumTemplateInfo(attribute, TemplateEnumTypeMustBeEnum, diagnostics));
|
||||
}
|
||||
else if (string.Equals(metadataName, DeviceDependentTemplateAttributeMetadataName, StringComparison.Ordinal))
|
||||
{
|
||||
if (deviceTemplateAttribute is not null)
|
||||
{
|
||||
diagnostics.Add(Diagnostic.Create(
|
||||
DuplicateSingleUseAttribute,
|
||||
attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken)?.GetLocation() ?? typeSyntax.GetLocation(),
|
||||
DeviceDependentTemplateAttributeMetadataName,
|
||||
templateSymbol.Name));
|
||||
continue;
|
||||
}
|
||||
|
||||
deviceTemplateAttribute = attribute;
|
||||
}
|
||||
else if (string.Equals(metadataName, NameSuffixAttributeMetadataName, StringComparison.Ordinal))
|
||||
{
|
||||
if (nameSuffixAttribute is not null)
|
||||
{
|
||||
diagnostics.Add(Diagnostic.Create(
|
||||
DuplicateSingleUseAttribute,
|
||||
attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken)?.GetLocation() ?? typeSyntax.GetLocation(),
|
||||
NameSuffixAttributeMetadataName,
|
||||
templateSymbol.Name));
|
||||
continue;
|
||||
}
|
||||
|
||||
nameSuffixAttribute = attribute;
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceTemplateAttribute is not null)
|
||||
{
|
||||
var deviceInfo = CreateEnumTemplateInfo(deviceTemplateAttribute, TemplateEnumTypeMustBeEnum, diagnostics).FirstOrDefault();
|
||||
if (deviceInfo.EnumType is not null)
|
||||
{
|
||||
deviceAttribute = new DeviceTemplateInfo(deviceInfo.EnumType, deviceInfo.Values, deviceInfo.AttributeLocation);
|
||||
}
|
||||
}
|
||||
|
||||
if (nameSuffixAttribute is not null)
|
||||
{
|
||||
nameSuffix = ExtractSuffix(nameSuffixAttribute);
|
||||
}
|
||||
|
||||
if (!enumAttributes.Any() && deviceAttribute is null && nameSuffixAttribute is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ImmutableArray<EnumTemplateInfo> orderedEnums = enumAttributes
|
||||
.OrderBy(static e => e.AttributeLocation.SourceSpan.Start)
|
||||
.ToImmutableArray();
|
||||
|
||||
return TemplateInfo.Create(
|
||||
templateSymbol,
|
||||
orderedEnums,
|
||||
deviceAttribute,
|
||||
nameSuffix,
|
||||
diagnostics.ToImmutable());
|
||||
}
|
||||
|
||||
private static IEnumerable<EnumTemplateInfo> CreateEnumTemplateInfo(
|
||||
AttributeData attribute,
|
||||
DiagnosticDescriptor diagnosticDescriptor,
|
||||
ImmutableArray<Diagnostic>.Builder diagnostics)
|
||||
{
|
||||
if (attribute.ConstructorArguments.Length < 1)
|
||||
{
|
||||
return Array.Empty<EnumTemplateInfo>();
|
||||
}
|
||||
|
||||
if (attribute.ConstructorArguments[0].Value is not INamedTypeSymbol enumTypeSymbol)
|
||||
{
|
||||
return Array.Empty<EnumTemplateInfo>();
|
||||
}
|
||||
|
||||
if (enumTypeSymbol.TypeKind != TypeKind.Enum)
|
||||
{
|
||||
Location location = attribute.ApplicationSyntaxReference?.GetSyntax().GetLocation() ?? Location.None;
|
||||
diagnostics.Add(Diagnostic.Create(diagnosticDescriptor, location, attribute.AttributeClass?.Name ?? "TemplateAttribute", enumTypeSymbol.ToDisplayString()));
|
||||
return Array.Empty<EnumTemplateInfo>();
|
||||
}
|
||||
|
||||
ImmutableArray<string> enumValues = enumTypeSymbol
|
||||
.GetMembers()
|
||||
.OfType<IFieldSymbol>()
|
||||
.Where(static f => !f.IsImplicitlyDeclared && f.HasConstantValue)
|
||||
.Select(static f => f.Name)
|
||||
.ToImmutableArray();
|
||||
|
||||
Location attributeLocation = attribute.ApplicationSyntaxReference?.GetSyntax().GetLocation() ?? Location.None;
|
||||
return new[] { new EnumTemplateInfo(enumTypeSymbol, enumValues, attributeLocation) };
|
||||
}
|
||||
|
||||
private static string? ExtractSuffix(AttributeData attributeData)
|
||||
{
|
||||
if (attributeData.ConstructorArguments.Length > 0)
|
||||
{
|
||||
return attributeData.ConstructorArguments[0].Value as string;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, TypedConstant> argument in attributeData.NamedArguments)
|
||||
{
|
||||
if (string.Equals(argument.Key, "suffix", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return argument.Value.Value as string;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private readonly struct GeneratorOptions
|
||||
{
|
||||
private const string GeneratedDirectoryName = "generated";
|
||||
private const string GeneratedRootOptionName = "build_property.DeviceDependentGenerator_OutputRoot";
|
||||
|
||||
private readonly AnalyzerConfigOptions globalOptions;
|
||||
|
||||
public GeneratorOptions(AnalyzerConfigOptions globalOptions)
|
||||
{
|
||||
this.globalOptions = globalOptions;
|
||||
}
|
||||
|
||||
public string BuildHintName(string containingNamespace, string generatedFileName)
|
||||
{
|
||||
string root = GetGeneratedRoot();
|
||||
string trimmedRoot = string.IsNullOrWhiteSpace(root)
|
||||
? GeneratedDirectoryName
|
||||
: root.Replace('\\', '/').Trim('/').Trim();
|
||||
if (string.IsNullOrWhiteSpace(trimmedRoot))
|
||||
{
|
||||
trimmedRoot = GeneratedDirectoryName;
|
||||
}
|
||||
|
||||
string namespacePath = string.IsNullOrWhiteSpace(containingNamespace)
|
||||
? string.Empty
|
||||
: containingNamespace.Replace('.', '/').Trim('/');
|
||||
|
||||
if (namespacePath.Length == 0)
|
||||
{
|
||||
return trimmedRoot + "/" + generatedFileName;
|
||||
}
|
||||
|
||||
return trimmedRoot + "/" + namespacePath + "/" + generatedFileName;
|
||||
}
|
||||
|
||||
private string GetGeneratedRoot()
|
||||
{
|
||||
if (globalOptions.TryGetValue(GeneratedRootOptionName, out string? configuredRoot)
|
||||
&& !string.IsNullOrWhiteSpace(configuredRoot))
|
||||
{
|
||||
return configuredRoot;
|
||||
}
|
||||
|
||||
return GeneratedDirectoryName;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ConditionalDirectiveAndDisabledTextStripper : CSharpSyntaxRewriter
|
||||
{
|
||||
public override SyntaxToken VisitToken(SyntaxToken token)
|
||||
=> token.WithLeadingTrivia(Filter(token.LeadingTrivia)).WithTrailingTrivia(Filter(token.TrailingTrivia));
|
||||
|
||||
private static SyntaxTriviaList Filter(SyntaxTriviaList list)
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<SyntaxTrivia>(list.Count);
|
||||
foreach (SyntaxTrivia trivia in list)
|
||||
{
|
||||
SyntaxKind kind = trivia.Kind();
|
||||
if (kind is SyntaxKind.DisabledTextTrivia
|
||||
or SyntaxKind.IfDirectiveTrivia
|
||||
or SyntaxKind.ElifDirectiveTrivia
|
||||
or SyntaxKind.ElseDirectiveTrivia
|
||||
or SyntaxKind.EndIfDirectiveTrivia)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.Add(trivia);
|
||||
}
|
||||
|
||||
return SyntaxFactory.TriviaList(builder.ToImmutable());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<IsRoslynAnalyzer>true</IsRoslynAnalyzer>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,238 +0,0 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using ILGPU;
|
||||
using Open.Numeric.Primes;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
[ShortRunJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class AtomicBenchmarks
|
||||
{
|
||||
private ulong[] _candidates = [];
|
||||
private int[] _candidatesInt = [];
|
||||
|
||||
[ParamsSource(nameof(GetBenchmarkCases))]
|
||||
public PrimeBenchmarkCase BenchmarkCase { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_candidates = BenchmarkCase.Candidates;
|
||||
_candidatesInt = [.. _candidates.Select(x => (int)x)];
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int AtomicAddInt()
|
||||
{
|
||||
int[] values = _candidatesInt;
|
||||
int primeCount;
|
||||
int oldValue = 0;
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
primeCount = values[i];
|
||||
oldValue ^= Atomic.Add(ref primeCount, 1);
|
||||
}
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public int InterlockedIncInt()
|
||||
{
|
||||
int[] values = _candidatesInt;
|
||||
int primeCount;
|
||||
int oldValue = 0;
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
primeCount = values[i];
|
||||
oldValue ^= Interlocked.Increment(ref primeCount);
|
||||
}
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public ulong AtomicAddUlong()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
ulong primeCount;
|
||||
ulong oldValue = 0UL;
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
primeCount = values[i];
|
||||
oldValue ^= Atomic.Add(ref primeCount, 1);
|
||||
}
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public ulong InterlockedIncUlong()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
ulong primeCount;
|
||||
ulong oldValue = 0UL;
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
primeCount = values[i];
|
||||
oldValue ^= Interlocked.Increment(ref primeCount);
|
||||
}
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public static IEnumerable<PrimeBenchmarkCase> GetBenchmarkCases()
|
||||
{
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≤100",
|
||||
"Admissible odd values ≤ 100 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
100UL,
|
||||
int.MaxValue);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≤4_000_000",
|
||||
"Sampled admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥16_000_000",
|
||||
"Sampled admissible odd values ≥ 16,000,000 (k ≤ 5, not divisible by 5)",
|
||||
16_000_001UL,
|
||||
16_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥32_000_000",
|
||||
"Sampled admissible odd values ≥ 32,000,000 (k ≤ 5, not divisible by 5)",
|
||||
32_000_001UL,
|
||||
32_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥64_000_000",
|
||||
"Sampled admissible odd values ≥ 64,000,000 (k ≤ 5, not divisible by 5)",
|
||||
64_000_001UL,
|
||||
64_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥100_000_000",
|
||||
"Sampled admissible odd values ≥ 100,000,000 (k ≤ 5, not divisible by 5)",
|
||||
100_000_001UL,
|
||||
100_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥138_000_000",
|
||||
"Sampled admissible odd values ≥ 138,000,000 (k ≤ 5, not divisible by 5)",
|
||||
138_000_001UL,
|
||||
138_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥276_000_000",
|
||||
"Sampled admissible odd values ≥ 276,000,000 (k ≤ 5, not divisible by 5)",
|
||||
276_000_001UL,
|
||||
276_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥552_000_000",
|
||||
"Sampled admissible odd values ≥ 552,000,000 (k ≤ 5, not divisible by 5)",
|
||||
552_000_001UL,
|
||||
552_500_000UL,
|
||||
4_096);
|
||||
}
|
||||
|
||||
public readonly record struct PrimeBenchmarkCase(string Name, string Description, ulong[] Candidates)
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public static PrimeBenchmarkCase FromRange(
|
||||
string name,
|
||||
string description,
|
||||
ulong startInclusive,
|
||||
ulong endInclusive,
|
||||
int maxCount)
|
||||
{
|
||||
ulong[] values = CollectRange(startInclusive, endInclusive, maxCount);
|
||||
return new PrimeBenchmarkCase(name, description, values);
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong[] CollectRange(ulong startInclusive, ulong endInclusive, int maxCount)
|
||||
{
|
||||
maxCount = maxCount < 0 ? 0 : maxCount;
|
||||
if (maxCount == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
int estimatedLength = (int)Math.Min((ulong)maxCount, endInclusive - startInclusive + 1UL);
|
||||
ulong[] buffer = new ulong[estimatedLength];
|
||||
int count = 0;
|
||||
|
||||
for (ulong value = startInclusive; value <= endInclusive && count < maxCount; value++)
|
||||
{
|
||||
if (!IsAdmissibleMersenneDivisorCandidate(value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count == buffer.Length)
|
||||
{
|
||||
Array.Resize(ref buffer, buffer.Length * 2);
|
||||
}
|
||||
|
||||
buffer[count++] = value;
|
||||
}
|
||||
|
||||
if (count == buffer.Length)
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ulong[] result = new ulong[count];
|
||||
Array.Copy(buffer, result, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool IsAdmissibleMersenneDivisorCandidate(ulong value)
|
||||
{
|
||||
if ((value & 1UL) == 0UL || value % 5UL == 0UL || value <= 1UL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong qMinusOne = value - 1UL;
|
||||
|
||||
for (ulong k = 1; k <= 5; k++)
|
||||
{
|
||||
ulong denominator = k << 1;
|
||||
if (qMinusOne % denominator != 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong p = qMinusOne / denominator;
|
||||
if (p < 2UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Prime.Numbers.IsPrime(p))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using ILGPU;
|
||||
using Open.Numeric.Primes;
|
||||
using PerfectNumbers.Core;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
[ShortRunJob(RuntimeMoniker.Net80)]
|
||||
[MemoryDiagnoser]
|
||||
public class BTreeBenchmarks
|
||||
{
|
||||
private ulong[] _candidates = [];
|
||||
private int[] _candidatesInt = [];
|
||||
|
||||
[ParamsSource(nameof(GetBenchmarkCases))]
|
||||
public PrimeBenchmarkCase BenchmarkCase { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
_candidates = BenchmarkCase.Candidates;
|
||||
_candidatesInt = [.. _candidates.Select(x => (int)x)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all candidate ints into the keyed B-tree (key = value), using tiny/inline and mid-size fast paths; count comes from the benchmark case or defaults to 4,096 when unspecified.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public BTree<int, int> BTreeAddInt()
|
||||
{
|
||||
int[] values = _candidatesInt;
|
||||
int prime;
|
||||
BTree<int, int> result = new();
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
prime = values[i];
|
||||
result.Add(prime, prime);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all candidate ints into a dictionary keyed by their hash code (baseline); count comes from the benchmark case or defaults to 4,096 when unspecified.
|
||||
/// </summary>
|
||||
[Benchmark(Baseline = true)]
|
||||
public Dictionary<int, int> DictionaryAddInt()
|
||||
{
|
||||
int[] values = _candidatesInt;
|
||||
int prime;
|
||||
Dictionary<int, int> result = [];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
prime = values[i];
|
||||
result.Add(prime.GetHashCode(), prime);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all candidate ulongs into the keyed B-tree (key = value), using tiny/inline and mid-size fast paths; count comes from the benchmark case or defaults to 4,096 when unspecified.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public BTree<ulong, ulong> BTreeAddUlong()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
ulong prime;
|
||||
BTree<ulong, ulong> result = new();
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
prime = values[i];
|
||||
result.Add(prime, prime);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all candidate ulongs into a dictionary keyed by their hash code; count comes from the benchmark case or defaults to 4,096 when unspecified.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public Dictionary<int, ulong> DictionaryAddUlong()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
ulong prime;
|
||||
Dictionary<int, ulong> result = [];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
prime = values[i];
|
||||
result.Add(prime.GetHashCode(), prime);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IEnumerable<PrimeBenchmarkCase> GetBenchmarkCases()
|
||||
{
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≤100",
|
||||
"Admissible odd values ≤ 100 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
100UL,
|
||||
int.MaxValue);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"4x ≤4_000_000",
|
||||
"4x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
4);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"12x ≤4_000_000",
|
||||
"12x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
12);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"16x ≤4_000_000",
|
||||
"16x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
16);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"32x ≤4_000_000",
|
||||
"16x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
32);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"64x ≤4_000_000",
|
||||
"16x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
64);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"128x ≤4_000_000",
|
||||
"128x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
128);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"256x ≤4_000_000",
|
||||
"256x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
256);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"512x ≤4_000_000",
|
||||
"512x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
512);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"1024x ≤4_000_000",
|
||||
"1024x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
1024);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"2048x ≤4_000_000",
|
||||
"2048x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
2048);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≤4_000_000",
|
||||
"Sampled admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||
3UL,
|
||||
4_000_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥16_000_000",
|
||||
"Sampled admissible odd values ≥ 16,000,000 (k ≤ 5, not divisible by 5)",
|
||||
16_000_001UL,
|
||||
16_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥32_000_000",
|
||||
"Sampled admissible odd values ≥ 32,000,000 (k ≤ 5, not divisible by 5)",
|
||||
32_000_001UL,
|
||||
32_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥64_000_000",
|
||||
"Sampled admissible odd values ≥ 64,000,000 (k ≤ 5, not divisible by 5)",
|
||||
64_000_001UL,
|
||||
64_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥100_000_000",
|
||||
"Sampled admissible odd values ≥ 100,000,000 (k ≤ 5, not divisible by 5)",
|
||||
100_000_001UL,
|
||||
100_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥138_000_000",
|
||||
"Sampled admissible odd values ≥ 138,000,000 (k ≤ 5, not divisible by 5)",
|
||||
138_000_001UL,
|
||||
138_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥276_000_000",
|
||||
"Sampled admissible odd values ≥ 276,000,000 (k ≤ 5, not divisible by 5)",
|
||||
276_000_001UL,
|
||||
276_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥552_000_000",
|
||||
"Sampled admissible odd values ≥ 552,000,000 (k ≤ 5, not divisible by 5)",
|
||||
552_000_001UL,
|
||||
552_500_000UL,
|
||||
4_096);
|
||||
}
|
||||
|
||||
public readonly record struct PrimeBenchmarkCase(string Name, string Description, ulong[] Candidates)
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public static PrimeBenchmarkCase FromRange(
|
||||
string name,
|
||||
string description,
|
||||
ulong startInclusive,
|
||||
ulong endInclusive,
|
||||
int maxCount)
|
||||
{
|
||||
ulong[] values = CollectRange(startInclusive, endInclusive, maxCount);
|
||||
return new PrimeBenchmarkCase(name, description, values);
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong[] CollectRange(ulong startInclusive, ulong endInclusive, int maxCount)
|
||||
{
|
||||
maxCount = maxCount < 0 ? 0 : maxCount;
|
||||
if (maxCount == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
int estimatedLength = (int)Math.Min((ulong)maxCount, endInclusive - startInclusive + 1UL);
|
||||
ulong[] buffer = new ulong[estimatedLength];
|
||||
int count = 0;
|
||||
|
||||
for (ulong value = startInclusive; value <= endInclusive && count < maxCount; value++)
|
||||
{
|
||||
if (!IsAdmissibleMersenneDivisorCandidate(value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count == buffer.Length)
|
||||
{
|
||||
Array.Resize(ref buffer, buffer.Length * 2);
|
||||
}
|
||||
|
||||
buffer[count++] = value;
|
||||
}
|
||||
|
||||
if (count == buffer.Length)
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ulong[] result = new ulong[count];
|
||||
Array.Copy(buffer, result, count);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool IsAdmissibleMersenneDivisorCandidate(ulong value)
|
||||
{
|
||||
if ((value & 1UL) == 0UL || value % 5UL == 0UL || value <= 1UL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong qMinusOne = value - 1UL;
|
||||
|
||||
for (ulong k = 1; k <= 5; k++)
|
||||
{
|
||||
ulong denominator = k << 1;
|
||||
if (qMinusOne % denominator != 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong p = qMinusOne / denominator;
|
||||
if (p < 2UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Prime.Numbers.IsPrime(p))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using PerfectNumbers.Core;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using PerfectNumbers.Core;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
public enum ModNumericType
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -2,7 +2,6 @@ using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System.Numerics;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
@@ -10,46 +9,43 @@ namespace EvenPerfectBitScanner.Benchmarks;
|
||||
[SimpleJob(RuntimeMoniker.Net80, launchCount: 1, warmupCount: 1, iterationCount: 5)]
|
||||
public class Pow2MontgomeryModBenchmarks
|
||||
{
|
||||
private const int SampleCount = 256;
|
||||
private const int SampleCount = 256;
|
||||
|
||||
private readonly MontgomeryDivisorData[] _smallDivisors = new MontgomeryDivisorData[SampleCount];
|
||||
private readonly MontgomeryDivisorData[] _largeDivisors = new MontgomeryDivisorData[SampleCount];
|
||||
private readonly ulong[] _smallExponents = new ulong[SampleCount];
|
||||
private readonly ulong[] _largeExponents = new ulong[SampleCount];
|
||||
private readonly ulong[] _smallCycles = new ulong[SampleCount];
|
||||
private readonly ulong[] _largeCycles = new ulong[SampleCount];
|
||||
private readonly MontgomeryDivisorData[] _smallDivisors = new MontgomeryDivisorData[SampleCount];
|
||||
private readonly MontgomeryDivisorData[] _largeDivisors = new MontgomeryDivisorData[SampleCount];
|
||||
private readonly ulong[] _smallExponents = new ulong[SampleCount];
|
||||
private readonly ulong[] _largeExponents = new ulong[SampleCount];
|
||||
private readonly ulong[] _smallCycles = new ulong[SampleCount];
|
||||
private readonly ulong[] _largeCycles = new ulong[SampleCount];
|
||||
|
||||
private readonly Random _random = new(13);
|
||||
private readonly Random _random = new(13);
|
||||
|
||||
public enum InputScale
|
||||
{
|
||||
Small,
|
||||
Large
|
||||
}
|
||||
public enum InputScale
|
||||
{
|
||||
Small,
|
||||
Large
|
||||
}
|
||||
|
||||
[ParamsAllValues]
|
||||
public InputScale Scale { get; set; }
|
||||
[ParamsAllValues]
|
||||
public InputScale Scale { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
ulong smallModulus = NextSmallOddModulus();
|
||||
_smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus);
|
||||
_smallExponents[i] = NextSmallExponent();
|
||||
_smallCycles[i] = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, smallModulus, MontgomeryDivisorData.FromModulus(smallModulus));
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
ulong smallModulus = NextSmallOddModulus();
|
||||
_smallDivisors[i] = CreateMontgomeryDivisorData(smallModulus);
|
||||
_smallExponents[i] = NextSmallExponent();
|
||||
_smallCycles[i] = MersenneDivisorCycles.CalculateCycleLength(smallModulus, MontgomeryDivisorData.FromModulus(smallModulus));
|
||||
|
||||
Console.WriteLine($"Calculating large cycle {i + 1} with heuristics");
|
||||
(ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle();
|
||||
Console.WriteLine($"Calculating large cycle {i + 1} with heuristics");
|
||||
(ulong largeModulus, ulong largeCycle) = NextLargeModulusAndCycle();
|
||||
_largeDivisors[i] = CreateMontgomeryDivisorData(largeModulus);
|
||||
_largeExponents[i] = NextLargeExponent();
|
||||
_largeCycles[i] = largeCycle;
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
_largeExponents[i] = NextLargeExponent();
|
||||
_largeCycles[i] = largeCycle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Baseline right-to-left Montgomery ladder; measured 36.36 μs on the small sample set and 117.32 μs on the large one.
|
||||
@@ -62,466 +58,461 @@ public class Pow2MontgomeryModBenchmarks
|
||||
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWindowedConvertToStandardCpu(divisors[i]);
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWindowedCpu(divisors[i], keepMontgomery: false);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Baseline right-to-left Montgomery ladder; measured ??.?? μs on the small sample set and ??.?? μs on the large one.
|
||||
/// <summary>
|
||||
/// Baseline right-to-left Montgomery ladder; measured ??.?? μs on the small sample set and ??.?? μs on the large one.
|
||||
///
|
||||
/// This benchmark failed running, hanging up early before reaching workload testing.
|
||||
/// </summary>
|
||||
// [Benchmark]
|
||||
public ulong BaselineGpu()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
/// </summary>
|
||||
// [Benchmark]
|
||||
public ulong BaselineGpu()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWindowedConvertGpu(gpu, divisors[i]);
|
||||
}
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWindowedGpu(divisors[i], keepMontgomery: false);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return checksum;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a known divisor cycle to fold the exponent first, dropping runtimes to 26.04 μs on small inputs (0.72× baseline) and 9.53 μs on large inputs (0.08× baseline).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong BaselineWithKnownCycleCpu()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out var cycles);
|
||||
ulong checksum = 0UL;
|
||||
/// <summary>
|
||||
/// Uses a known divisor cycle to fold the exponent first, dropping runtimes to 26.04 μs on small inputs (0.72× baseline) and 9.53 μs on large inputs (0.08× baseline).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong BaselineWithKnownCycleCpu()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out var cycles);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWithCycleConvertToStandardCpu(cycles[i], divisors[i]);
|
||||
}
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWithCycleCpu(cycles[i], divisors[i]);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a known divisor cycle to fold the exponent first, dropping runtimes to 26.04 μs on small inputs (0.72× baseline) and 9.53 μs on large inputs (0.08× baseline).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong BaselineWithKnownCycleGpu()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out var cycles);
|
||||
ulong checksum = 0UL;
|
||||
/// <summary>
|
||||
/// Uses a known divisor cycle to fold the exponent first, dropping runtimes to 26.04 μs on small inputs (0.72× baseline) and 9.53 μs on large inputs (0.08× baseline).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong BaselineWithKnownCycleGpu()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out var cycles);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(gpu, cycles[i], divisors[i]);
|
||||
}
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= exponents[i].Pow2MontgomeryModWithCycleGpu(cycles[i], divisors[i]);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return checksum;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Left-to-right Montgomery scan that trails the baseline: 39.11 μs on small inputs (1.08×) and 126.38 μs on large ones (1.08×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong LeftToRight()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
/// <summary>
|
||||
/// Left-to-right Montgomery scan that trails the baseline: 39.11 μs on small inputs (1.08×) and 126.38 μs on large ones (1.08×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong LeftToRight()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2MontgomeryModLeftToRight(exponents[i], divisors[i]);
|
||||
}
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2MontgomeryModLeftToRight(exponents[i], divisors[i]);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes four exponent bits per loop, finishing at 38.74 μs on small cases (1.07× baseline) and 120.11 μs on large ones (1.02×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong Batched4()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
/// <summary>
|
||||
/// Processes four exponent bits per loop, finishing at 38.74 μs on small cases (1.07× baseline) and 120.11 μs on large ones (1.02×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong Batched4()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2MontgomeryModBatched4(exponents[i], divisors[i]);
|
||||
}
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2MontgomeryModBatched4(exponents[i], divisors[i]);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Windowed Montgomery ladder (width 5) that lags behind the baseline at 52.82 μs on small samples (1.45×) and 124.13 μs on large samples (1.06×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong SlidingWindow()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
/// <summary>
|
||||
/// Windowed Montgomery ladder (width 5) that lags behind the baseline at 52.82 μs on small samples (1.45×) and 124.13 μs on large samples (1.06×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong SlidingWindow()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2MontgomeryModSlidingWindow(exponents[i], divisors[i]);
|
||||
}
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2MontgomeryModSlidingWindow(exponents[i], divisors[i]);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Classic binary ladder without Montgomery reduction; runs 24.75 μs on small inputs (0.68× baseline) but collapses to 295.29 μs on large ones (2.52×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong BinaryMod()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
/// <summary>
|
||||
/// Classic binary ladder without Montgomery reduction; runs 24.75 μs on small inputs (0.68× baseline) but collapses to 295.29 μs on large ones (2.52×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong BinaryMod()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2ModBinary(exponents[i], divisors[i].Modulus);
|
||||
}
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2ModBinary(exponents[i], divisors[i].Modulus);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Precomputes all squarings once; 30.18 μs on small inputs (0.83× baseline) but 289.80 μs on large ones (2.47×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong PrecomputedTableMod()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
/// <summary>
|
||||
/// Precomputes all squarings once; 30.18 μs on small inputs (0.83× baseline) but 289.80 μs on large ones (2.47×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong PrecomputedTableMod()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2ModPrecomputedTable(exponents[i], divisors[i].Modulus);
|
||||
}
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2ModPrecomputedTable(exponents[i], divisors[i].Modulus);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relies on <see cref="BigInteger.ModPow"/>; fastest on small inputs at 17.21 μs (0.47× baseline) but slowest on large ones at 562.78 μs (4.80×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong BigIntegerMod()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
/// <summary>
|
||||
/// Relies on <see cref="BigInteger.ModPow"/>; fastest on small inputs at 17.21 μs (0.47× baseline) but slowest on large ones at 562.78 μs (4.80×).
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public ulong BigIntegerMod()
|
||||
{
|
||||
GetData(out var exponents, out var divisors, out _);
|
||||
ulong checksum = 0UL;
|
||||
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2ModBigInteger(exponents[i], divisors[i].Modulus);
|
||||
}
|
||||
for (int i = 0; i < SampleCount; i++)
|
||||
{
|
||||
checksum ^= Pow2ModBigInteger(exponents[i], divisors[i].Modulus);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
private void GetData(out ulong[] exponents, out MontgomeryDivisorData[] divisors, out ulong[] cycles)
|
||||
{
|
||||
if (Scale == InputScale.Small)
|
||||
{
|
||||
exponents = _smallExponents;
|
||||
divisors = _smallDivisors;
|
||||
cycles = _smallCycles;
|
||||
}
|
||||
else
|
||||
{
|
||||
exponents = _largeExponents;
|
||||
divisors = _largeDivisors;
|
||||
cycles = _largeCycles;
|
||||
}
|
||||
}
|
||||
private void GetData(out ulong[] exponents, out MontgomeryDivisorData[] divisors, out ulong[] cycles)
|
||||
{
|
||||
if (Scale == InputScale.Small)
|
||||
{
|
||||
exponents = _smallExponents;
|
||||
divisors = _smallDivisors;
|
||||
cycles = _smallCycles;
|
||||
}
|
||||
else
|
||||
{
|
||||
exponents = _largeExponents;
|
||||
divisors = _largeDivisors;
|
||||
cycles = _largeCycles;
|
||||
}
|
||||
}
|
||||
|
||||
private ulong NextSmallExponent() => (ulong)_random.NextInt64(1L, 1_000_000L);
|
||||
private ulong NextSmallExponent() => (ulong)_random.NextInt64(1L, 1_000_000L);
|
||||
|
||||
private ulong NextLargeExponent() => (ulong)_random.NextInt64(1L << 60, long.MaxValue);
|
||||
private ulong NextLargeExponent() => (ulong)_random.NextInt64(1L << 60, long.MaxValue);
|
||||
|
||||
private ulong NextSmallOddModulus()
|
||||
{
|
||||
ulong value = (ulong)_random.NextInt64(3L, 1_000_000L);
|
||||
return value | 1UL;
|
||||
}
|
||||
private ulong NextSmallOddModulus()
|
||||
{
|
||||
ulong value = (ulong)_random.NextInt64(3L, 1_000_000L);
|
||||
return value | 1UL;
|
||||
}
|
||||
|
||||
private (ulong modulus, ulong cycleLength) NextLargeModulusAndCycle()
|
||||
{
|
||||
int bitLength = _random.Next(48, 64);
|
||||
ulong modulus = (1UL << bitLength) - 1UL;
|
||||
return (modulus, (ulong)bitLength);
|
||||
}
|
||||
private (ulong modulus, ulong cycleLength) NextLargeModulusAndCycle()
|
||||
{
|
||||
int bitLength = _random.Next(48, 64);
|
||||
ulong modulus = (1UL << bitLength) - 1UL;
|
||||
return (modulus, (ulong)bitLength);
|
||||
}
|
||||
|
||||
private static MontgomeryDivisorData CreateMontgomeryDivisorData(ulong modulus)
|
||||
{
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return new MontgomeryDivisorData(modulus, 0UL, 0UL, 0UL, 0UL);
|
||||
}
|
||||
private static MontgomeryDivisorData CreateMontgomeryDivisorData(ulong modulus)
|
||||
{
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return new MontgomeryDivisorData(modulus, 0UL, 0UL, 0UL, 0UL);
|
||||
}
|
||||
|
||||
ulong nPrime = ComputeMontgomeryNPrime(modulus);
|
||||
ulong montgomeryOne = ComputeMontgomeryResidue(1UL, modulus);
|
||||
ulong montgomeryTwo = ComputeMontgomeryResidue(2UL, modulus);
|
||||
ulong montgomeryTwoSquared = ULongExtensions.MontgomeryMultiplyCpu(montgomeryTwo, montgomeryTwo, modulus, nPrime);
|
||||
ulong nPrime = ComputeMontgomeryNPrime(modulus);
|
||||
ulong montgomeryOne = ComputeMontgomeryResidue(1UL, modulus);
|
||||
ulong montgomeryTwo = ComputeMontgomeryResidue(2UL, modulus);
|
||||
ulong montgomeryTwoSquared = ULongExtensions.MontgomeryMultiplyCpu(montgomeryTwo, montgomeryTwo, modulus, nPrime);
|
||||
|
||||
return new MontgomeryDivisorData(
|
||||
modulus,
|
||||
nPrime,
|
||||
montgomeryOne,
|
||||
montgomeryTwo,
|
||||
montgomeryTwoSquared);
|
||||
}
|
||||
return new MontgomeryDivisorData(
|
||||
modulus,
|
||||
nPrime,
|
||||
montgomeryOne,
|
||||
montgomeryTwo,
|
||||
montgomeryTwoSquared);
|
||||
}
|
||||
|
||||
private static ulong ComputeMontgomeryResidue(ulong value, ulong modulus) => (ulong)((UInt128)value * (UInt128.One << 64) % modulus);
|
||||
private static ulong ComputeMontgomeryResidue(ulong value, ulong modulus) => (ulong)((UInt128)value * (UInt128.One << 64) % modulus);
|
||||
|
||||
private static ulong ComputeMontgomeryNPrime(ulong modulus)
|
||||
{
|
||||
ulong inv = modulus;
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
return unchecked(0UL - inv);
|
||||
}
|
||||
private static ulong ComputeMontgomeryNPrime(ulong modulus)
|
||||
{
|
||||
ulong inv = modulus;
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
inv *= unchecked(2UL - modulus * inv);
|
||||
return unchecked(0UL - inv);
|
||||
}
|
||||
|
||||
private static ulong Pow2MontgomeryModLeftToRight(ulong exponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
ulong modulus = divisor.Modulus;
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
private static ulong Pow2MontgomeryModLeftToRight(ulong exponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
ulong modulus = divisor.Modulus;
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
ulong result = divisor.MontgomeryOne;
|
||||
ulong baseVal = divisor.MontgomeryTwo;
|
||||
ulong nPrime = divisor.NPrime;
|
||||
ulong result = divisor.MontgomeryOne;
|
||||
ulong baseVal = divisor.MontgomeryTwo;
|
||||
ulong nPrime = divisor.NPrime;
|
||||
|
||||
if (exponent == 0UL)
|
||||
{
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
if (exponent == 0UL)
|
||||
{
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
|
||||
int msbIndex = 63 - BitOperations.LeadingZeroCount(exponent);
|
||||
int msbIndex = 63 - BitOperations.LeadingZeroCount(exponent);
|
||||
|
||||
for (int bitIndex = msbIndex; bitIndex >= 0; bitIndex--)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
for (int bitIndex = msbIndex; bitIndex >= 0; bitIndex--)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
|
||||
if (((exponent >> bitIndex) & 1UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(baseVal, modulus, nPrime);
|
||||
}
|
||||
}
|
||||
if (((exponent >> bitIndex) & 1UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(baseVal, modulus, nPrime);
|
||||
}
|
||||
}
|
||||
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
|
||||
private static ulong Pow2MontgomeryModBatched4(ulong exponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
ulong modulus = divisor.Modulus;
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
private static ulong Pow2MontgomeryModBatched4(ulong exponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
ulong modulus = divisor.Modulus;
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
ulong result = divisor.MontgomeryOne;
|
||||
ulong baseVal = divisor.MontgomeryTwo;
|
||||
ulong nPrime = divisor.NPrime;
|
||||
ulong remainingExponent = exponent;
|
||||
ulong result = divisor.MontgomeryOne;
|
||||
ulong baseVal = divisor.MontgomeryTwo;
|
||||
ulong nPrime = divisor.NPrime;
|
||||
ulong remainingExponent = exponent;
|
||||
|
||||
while (remainingExponent != 0UL)
|
||||
{
|
||||
ulong nibble = remainingExponent & 0xFUL;
|
||||
while (remainingExponent != 0UL)
|
||||
{
|
||||
ulong nibble = remainingExponent & 0xFUL;
|
||||
|
||||
ulong base0 = baseVal;
|
||||
ulong base1 = base0.MontgomeryMultiplyCpu(base0, modulus, nPrime);
|
||||
ulong base2 = base1.MontgomeryMultiplyCpu(base1, modulus, nPrime);
|
||||
ulong base3 = base2.MontgomeryMultiplyCpu(base2, modulus, nPrime);
|
||||
ulong base0 = baseVal;
|
||||
ulong base1 = base0.MontgomeryMultiplyCpu(base0, modulus, nPrime);
|
||||
ulong base2 = base1.MontgomeryMultiplyCpu(base1, modulus, nPrime);
|
||||
ulong base3 = base2.MontgomeryMultiplyCpu(base2, modulus, nPrime);
|
||||
|
||||
if ((nibble & 1UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(base0, modulus, nPrime);
|
||||
}
|
||||
if ((nibble & 1UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(base0, modulus, nPrime);
|
||||
}
|
||||
|
||||
if ((nibble & 2UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(base1, modulus, nPrime);
|
||||
}
|
||||
if ((nibble & 2UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(base1, modulus, nPrime);
|
||||
}
|
||||
|
||||
if ((nibble & 4UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(base2, modulus, nPrime);
|
||||
}
|
||||
if ((nibble & 4UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(base2, modulus, nPrime);
|
||||
}
|
||||
|
||||
if ((nibble & 8UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(base3, modulus, nPrime);
|
||||
}
|
||||
if ((nibble & 8UL) != 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(base3, modulus, nPrime);
|
||||
}
|
||||
|
||||
baseVal = base3.MontgomeryMultiplyCpu(base3, modulus, nPrime);
|
||||
remainingExponent >>= 4;
|
||||
}
|
||||
baseVal = base3.MontgomeryMultiplyCpu(base3, modulus, nPrime);
|
||||
remainingExponent >>= 4;
|
||||
}
|
||||
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
|
||||
// TODO: Update it with the latest implementations
|
||||
private static ulong Pow2MontgomeryModSlidingWindow(ulong exponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
private static ulong Pow2MontgomeryModSlidingWindow(ulong exponent, in MontgomeryDivisorData divisor)
|
||||
{
|
||||
ulong modulus = divisor.Modulus;
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
ulong nPrime = divisor.NPrime;
|
||||
ulong result = divisor.MontgomeryOne;
|
||||
ulong baseVal = divisor.MontgomeryTwo;
|
||||
ulong nPrime = divisor.NPrime;
|
||||
ulong result = divisor.MontgomeryOne;
|
||||
ulong baseVal = divisor.MontgomeryTwo;
|
||||
|
||||
if (exponent == 0UL)
|
||||
{
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
if (exponent == 0UL)
|
||||
{
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
|
||||
const int WindowSize = 5;
|
||||
Span<ulong> oddPowers = stackalloc ulong[1 << (WindowSize - 1)];
|
||||
oddPowers[0] = baseVal;
|
||||
ulong baseSquared = baseVal.MontgomeryMultiplyCpu(baseVal, modulus, nPrime);
|
||||
for (int i = 1; i < oddPowers.Length; i++)
|
||||
{
|
||||
oddPowers[i] = oddPowers[i - 1].MontgomeryMultiplyCpu(baseSquared, modulus, nPrime);
|
||||
}
|
||||
const int WindowSize = 5;
|
||||
Span<ulong> oddPowers = stackalloc ulong[1 << (WindowSize - 1)];
|
||||
oddPowers[0] = baseVal;
|
||||
ulong baseSquared = baseVal.MontgomeryMultiplyCpu(baseVal, modulus, nPrime);
|
||||
for (int i = 1; i < oddPowers.Length; i++)
|
||||
{
|
||||
oddPowers[i] = oddPowers[i - 1].MontgomeryMultiplyCpu(baseSquared, modulus, nPrime);
|
||||
}
|
||||
|
||||
int bitIndex = 63 - BitOperations.LeadingZeroCount(exponent);
|
||||
int bitIndex = 63 - BitOperations.LeadingZeroCount(exponent);
|
||||
|
||||
while (bitIndex >= 0)
|
||||
{
|
||||
if (((exponent >> bitIndex) & 1UL) == 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
bitIndex--;
|
||||
continue;
|
||||
}
|
||||
while (bitIndex >= 0)
|
||||
{
|
||||
if (((exponent >> bitIndex) & 1UL) == 0UL)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
bitIndex--;
|
||||
continue;
|
||||
}
|
||||
|
||||
int windowStart = Math.Max(bitIndex - WindowSize + 1, 0);
|
||||
ulong windowValue = (exponent >> windowStart) & ((1UL << (bitIndex - windowStart + 1)) - 1UL);
|
||||
int windowStart = Math.Max(bitIndex - WindowSize + 1, 0);
|
||||
ulong windowValue = (exponent >> windowStart) & ((1UL << (bitIndex - windowStart + 1)) - 1UL);
|
||||
|
||||
while ((windowValue & 1UL) == 0UL)
|
||||
{
|
||||
windowValue >>= 1;
|
||||
windowStart++;
|
||||
}
|
||||
while ((windowValue & 1UL) == 0UL)
|
||||
{
|
||||
windowValue >>= 1;
|
||||
windowStart++;
|
||||
}
|
||||
|
||||
int squares = bitIndex - windowStart + 1;
|
||||
for (int i = 0; i < squares; i++)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
}
|
||||
int squares = bitIndex - windowStart + 1;
|
||||
for (int i = 0; i < squares; i++)
|
||||
{
|
||||
result = result.MontgomeryMultiplyCpu(result, modulus, nPrime);
|
||||
}
|
||||
|
||||
result = result.MontgomeryMultiplyCpu(oddPowers[(int)(windowValue >> 1)], modulus, nPrime);
|
||||
bitIndex = windowStart - 1;
|
||||
}
|
||||
result = result.MontgomeryMultiplyCpu(oddPowers[(int)(windowValue >> 1)], modulus, nPrime);
|
||||
bitIndex = windowStart - 1;
|
||||
}
|
||||
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
return result.MontgomeryMultiplyCpu(1UL, modulus, nPrime);
|
||||
}
|
||||
|
||||
// TODO: Update it with the latest implementations, if it exists in production code. Otherwise just remove TODO
|
||||
private static ulong Pow2ModBinary(ulong exponent, ulong modulus)
|
||||
{
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
private static ulong Pow2ModBinary(ulong exponent, ulong modulus)
|
||||
{
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
ulong result = 1UL % modulus;
|
||||
ulong baseVal = 2UL % modulus;
|
||||
ulong remainingExponent = exponent;
|
||||
ulong result = 1UL % modulus;
|
||||
ulong baseVal = 2UL % modulus;
|
||||
ulong remainingExponent = exponent;
|
||||
|
||||
while (remainingExponent > 0UL)
|
||||
{
|
||||
if ((remainingExponent & 1UL) != 0UL)
|
||||
{
|
||||
result = MultiplyMod(result, baseVal, modulus);
|
||||
}
|
||||
while (remainingExponent > 0UL)
|
||||
{
|
||||
if ((remainingExponent & 1UL) != 0UL)
|
||||
{
|
||||
result = MultiplyMod(result, baseVal, modulus);
|
||||
}
|
||||
|
||||
remainingExponent >>= 1;
|
||||
if (remainingExponent == 0UL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
remainingExponent >>= 1;
|
||||
if (remainingExponent == 0UL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
baseVal = MultiplyMod(baseVal, baseVal, modulus);
|
||||
}
|
||||
baseVal = MultiplyMod(baseVal, baseVal, modulus);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ulong Pow2ModPrecomputedTable(ulong exponent, ulong modulus)
|
||||
{
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
private static ulong Pow2ModPrecomputedTable(ulong exponent, ulong modulus)
|
||||
{
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
if (exponent == 0UL)
|
||||
{
|
||||
return 1UL % modulus;
|
||||
}
|
||||
if (exponent == 0UL)
|
||||
{
|
||||
return 1UL % modulus;
|
||||
}
|
||||
|
||||
Span<ulong> powers = stackalloc ulong[64];
|
||||
powers[0] = 2UL % modulus;
|
||||
int highestBit = 63 - BitOperations.LeadingZeroCount(exponent);
|
||||
Span<ulong> powers = stackalloc ulong[64];
|
||||
powers[0] = 2UL % modulus;
|
||||
int highestBit = 63 - BitOperations.LeadingZeroCount(exponent);
|
||||
|
||||
for (int index = 1; index <= highestBit; index++)
|
||||
{
|
||||
powers[index] = MultiplyMod(powers[index - 1], powers[index - 1], modulus);
|
||||
}
|
||||
for (int index = 1; index <= highestBit; index++)
|
||||
{
|
||||
powers[index] = MultiplyMod(powers[index - 1], powers[index - 1], modulus);
|
||||
}
|
||||
|
||||
ulong result = 1UL % modulus;
|
||||
ulong remainingExponent = exponent;
|
||||
int bitIndex = 0;
|
||||
ulong result = 1UL % modulus;
|
||||
ulong remainingExponent = exponent;
|
||||
int bitIndex = 0;
|
||||
|
||||
while (remainingExponent > 0UL)
|
||||
{
|
||||
if ((remainingExponent & 1UL) != 0UL)
|
||||
{
|
||||
result = MultiplyMod(result, powers[bitIndex], modulus);
|
||||
}
|
||||
while (remainingExponent > 0UL)
|
||||
{
|
||||
if ((remainingExponent & 1UL) != 0UL)
|
||||
{
|
||||
result = MultiplyMod(result, powers[bitIndex], modulus);
|
||||
}
|
||||
|
||||
remainingExponent >>= 1;
|
||||
bitIndex++;
|
||||
}
|
||||
remainingExponent >>= 1;
|
||||
bitIndex++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ulong Pow2ModBigInteger(ulong exponent, ulong modulus)
|
||||
{
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
private static ulong Pow2ModBigInteger(ulong exponent, ulong modulus)
|
||||
{
|
||||
if (modulus <= 1UL || (modulus & 1UL) == 0UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
BigInteger mod = modulus;
|
||||
BigInteger result = BigInteger.ModPow(2, exponent, mod);
|
||||
return (ulong)result;
|
||||
}
|
||||
BigInteger mod = modulus;
|
||||
BigInteger result = BigInteger.ModPow(2, exponent, mod);
|
||||
return (ulong)result;
|
||||
}
|
||||
|
||||
private static ulong MultiplyMod(ulong x, ulong y, ulong modulus)
|
||||
{
|
||||
UInt128 product = (UInt128)x * y;
|
||||
return (ulong)(product % modulus);
|
||||
}
|
||||
private static ulong MultiplyMod(ulong x, ulong y, ulong modulus)
|
||||
{
|
||||
UInt128 product = (UInt128)x * y;
|
||||
return (ulong)(product % modulus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
@@ -61,13 +61,12 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider
|
||||
};
|
||||
|
||||
var cases = new Pow2MontgomeryModWindowedBenchmarkCase[seeds.Length];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < seeds.Length; i++)
|
||||
{
|
||||
Pow2MontgomeryModWindowedBenchmarkSeed seed = seeds[i];
|
||||
ulong modulus = 2UL * seed.Multiplier * seed.Exponent + 1UL;
|
||||
MontgomeryDivisorData divisor = MontgomeryDivisorData.FromModulus(modulus);
|
||||
ulong cycleLength = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, modulus, divisor);
|
||||
ulong cycleLength = MersenneDivisorCycles.CalculateCycleLength(modulus, divisor);
|
||||
ulong reducedExponent = seed.Exponent % cycleLength;
|
||||
cases[i] = new Pow2MontgomeryModWindowedBenchmarkCase(
|
||||
seed.Exponent,
|
||||
@@ -77,7 +76,6 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider
|
||||
reducedExponent);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return new Pow2MontgomeryModWindowedBenchmarkInputs(cases);
|
||||
}
|
||||
|
||||
@@ -94,4 +92,3 @@ internal static class Pow2MontgomeryModWindowedBenchmarkInputsProvider
|
||||
public ulong Multiplier { get; }
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -32,7 +32,7 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryCpuBenchmarks
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(current.Divisor);
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedCpu(current.Divisor, keepMontgomery: false);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
@@ -50,7 +50,7 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryCpuBenchmarks
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedKeepMontgomeryCpu(current.Divisor);
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedCpu(current.Divisor, keepMontgomery: true);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
@@ -68,7 +68,7 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryCpuBenchmarks
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWithCycleConvertToStandardCpu(current.CycleLength, current.Divisor);
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWithCycleCpu(current.CycleLength, current.Divisor);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
|
||||
+3
-8
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using PerfectNumbers.Core.Gpu.Kernels;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
@@ -32,14 +31,12 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryGpuBenchmarks
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedConvertGpu(gpu, current.Divisor);
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedGpu(current.Divisor, keepMontgomery: false);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return checksum;
|
||||
}
|
||||
|
||||
@@ -52,14 +49,12 @@ public class Pow2MontgomeryModWindowedKeepMontgomeryGpuBenchmarks
|
||||
ulong checksum = 0UL;
|
||||
Pow2MontgomeryModWindowedBenchmarkCase[] cases = _inputs.Cases;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < cases.Length; i++)
|
||||
{
|
||||
ref readonly Pow2MontgomeryModWindowedBenchmarkCase current = ref cases[i];
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedKeepGpu(gpu, current.Divisor);
|
||||
checksum ^= current.Exponent.Pow2MontgomeryModWindowedGpu(current.Divisor, keepMontgomery: true);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return checksum;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,415 +0,0 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using PerfectNumbers.Core;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks
|
||||
{
|
||||
public class Pow2ProviderBigIntegerBenchmarks
|
||||
{
|
||||
[Benchmark(Baseline = true)]
|
||||
public BigInteger ByShifting()
|
||||
{
|
||||
BigInteger result = BigInteger.Zero;
|
||||
for (int i = 0; i <= 63; i++)
|
||||
{
|
||||
result = BigInteger.One << i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public BigInteger ByMapping()
|
||||
{
|
||||
BigInteger result = BigInteger.Zero;
|
||||
for (int i = 0; i <= 63; i++)
|
||||
{
|
||||
result = Pow2Provider.BigInteger(i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class Pow2MinusOneeProviderBigIntegerBenchmarks
|
||||
{
|
||||
[Benchmark(Baseline = true)]
|
||||
public BigInteger ByShifting()
|
||||
{
|
||||
BigInteger result = BigInteger.Zero;
|
||||
for (int i = 0; i <= 63; i++)
|
||||
{
|
||||
result = (BigInteger.One << i) - BigInteger.One;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public BigInteger ByMapping()
|
||||
{
|
||||
BigInteger result = BigInteger.Zero;
|
||||
for (int i = 0; i <= 63; i++)
|
||||
{
|
||||
result = Pow2Provider.BigIntegerMinusOne(i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class Pow2ProviderULongBenchmarks
|
||||
{
|
||||
public static class ULongNumbers
|
||||
{
|
||||
public const ulong TwoMinusOne = 2L - 1L;
|
||||
public const ulong FourMinusOne = 4L - 1L;
|
||||
public const ulong EightMinusOne = 8L - 1L;
|
||||
public const ulong SixteenMinusOne = 16L - 1L;
|
||||
public const ulong ThirtyTwoMinusOne = 32L - 1L;
|
||||
public const ulong SixtyFourMinusOne = 64L - 1L;
|
||||
public const ulong OneHundredTwentyEightMinusOne = 128L - 1L;
|
||||
public const ulong TwoHundredFiftySixMinusOne = 256L - 1L;
|
||||
public const ulong FiveHundredTwelveMinusOne = 512L - 1L;
|
||||
public const ulong OneThousandTwentyFourMinusOne = 1_024L - 1L;
|
||||
public const ulong TwoThousandFortyEightMinusOne = 2_048L - 1L;
|
||||
public const ulong FourThousandNinetySixMinusOne = 4_096L - 1L;
|
||||
public const ulong EightThousandOneHundredNinetyTwoMinusOne = 8_192L - 1L;
|
||||
public const ulong SixteenThousandThreeHundredEightyFourMinusOne = 16_384L - 1L;
|
||||
public const ulong ThirtyTwoThousandSevenHundredSixtyEightMinusOne = 32_768L - 1L;
|
||||
public const ulong SixtyFiveThousandFiveHundredThirtySixMinusOne = 65_536L - 1L;
|
||||
public const ulong OneHundredThirtyOneThousandSeventyTwoMinusOne = 131_072L - 1L;
|
||||
public const ulong TwoHundredSixtyTwoThousandOneHundredFortyFourMinusOne = 262_144L - 1L;
|
||||
public const ulong FiveHundredTwentyFourThousandTwoHundredEightyEightMinusOne = 524_288L - 1L;
|
||||
public const ulong OneMillionFortyEightThousandFiveHundredSeventySixMinusOne = 1_048_576L - 1L;
|
||||
public const ulong TwoMillionNinetySevenThousandOneHundredFiftyTwoMinusOne = 2_097_152L - 1L;
|
||||
public const ulong FourMillionOneHundredNinetyFourThousandThreeHundredFourMinusOne = 4_194_304L - 1L;
|
||||
public const ulong EightMillionThreeHundredEightyEightThousandSixHundredEightMinusOne = 8_388_608L - 1L;
|
||||
public const ulong SixteenMillionSevenHundredSeventySevenThousandTwoHundredSixteenMinusOne = 16_777_216L - 1L;
|
||||
public const ulong ThirtyThreeMillionFiveHundredFiftyFourThousandFourHundredThirtyTwoMinusOne = 33_554_432L - 1L;
|
||||
public const ulong SixtySevenMillionOneHundredEightThousandEightHundredSixtyFourMinusOne = 67_108_864L - 1L;
|
||||
public const ulong OneHundredThirtyFourMillionTwoHundredSeventeenThousandSevenHundredTwentyEightMinusOne = 134_217_728L - 1L;
|
||||
public const ulong TwoHundredSixtyEightMillionFourHundredThirtyFiveThousandFourHundredFiftySixMinusOne = 268_435_456L - 1L;
|
||||
public const ulong FiveHundredThirtySixMillionEightHundredSeventyThousandNineHundredTwelveMinusOne = 536_870_912L - 1L;
|
||||
public const ulong OneBillionSeventyThreeMillionSevenHundredFortyOneThousandEightHundredTwentyFourMinusOne = 1_073_741_824L - 1L;
|
||||
public const ulong TwoBillionOneHundredFortySevenMillionFourHundredEightyThreeThousandSixHundredFortyEightMinusOne = 2_147_483_648L - 1L;
|
||||
public const ulong FourBillionTwoHundredNinetyFourMillionNineHundredSixtySevenThousandTwoHundredNinetySixMinusOne = 4_294_967_296L - 1L;
|
||||
public const ulong EightBillionFiveHundredEightyNineMillionNineHundredThirtyFourThousandFiveHundredNinetyTwoMinusOne = 8_589_934_592L - 1L;
|
||||
public const ulong SeventeenBillionOneHundredSeventyNineMillionEightHundredSixtyNineThousandOneHundredEightyFourMinusOne = 17_179_869_184L - 1L;
|
||||
public const ulong ThirtyFourBillionThreeHundredFiftyNineMillionSevenHundredThirtyEightThousandThreeHundredSixtyEightMinusOne = 34_359_738_368L - 1L;
|
||||
public const ulong SixtyEightBillionSevenHundredNineteenMillionFourHundredSeventySixThousandSevenHundredThirtySixMinusOne = 68_719_476_736L - 1L;
|
||||
public const ulong OneHundredThirtySevenBillionFourHundredThirtyEightMillionNineHundredFiftyThreeThousandFourHundredSeventyTwoMinusOne = 137_438_953_472L - 1L;
|
||||
public const ulong TwoHundredSeventyFourBillionEightHundredSeventySevenMillionNineHundredSixThousandNineHundredFortyFourMinusOne = 274_877_906_944L - 1L;
|
||||
public const ulong FiveHundredFortyNineBillionSevenHundredFiftyFiveMillionEightHundredThirteenThousandEightHundredEightyEightMinusOne = 549_755_813_888L - 1L;
|
||||
public const ulong OneTrillionNinetyNineBillionFiveHundredElevenMillionSixHundredTwentySevenThousandSevenHundredSeventySixMinusOne = 1_099_511_627_776L - 1L;
|
||||
public const ulong TwoTrillionOneHundredNinetyNineBillionTwentyThreeMillionTwoHundredFiftyFiveThousandFiveHundredFiftyTwoMinusOne = 2_199_023_255_552L - 1L;
|
||||
public const ulong FourTrillionThreeHundredNinetyEightBillionFortySixMillionFiveHundredElevenThousandOneHundredFourMinusOne = 4_398_046_511_104L - 1L;
|
||||
public const ulong EightTrillionSevenHundredNinetySixBillionNinetyThreeMillionTwentyTwoThousandTwoHundredEightMinusOne = 8_796_093_022_208L - 1L;
|
||||
public const ulong SeventeenTrillionFiveHundredNinetyTwoBillionOneHundredEightySixMillionFortyFourThousandFourHundredSixteenMinusOne = 17_592_186_044_416L - 1L;
|
||||
public const ulong ThirtyFiveTrillionOneHundredEightyFourBillionThreeHundredSeventyTwoMillionEightyEightThousandEightHundredThirtyTwoMinusOne = 35_184_372_088_832L - 1L;
|
||||
public const ulong SeventyTrillionThreeHundredSixtyEightBillionSevenHundredFortyFourMillionOneHundredSeventySevenThousandSixHundredSixtyFourMinusOne = 70_368_744_177_664L - 1L;
|
||||
public const ulong OneHundredFortyTrillionSevenHundredThirtySevenBillionFourHundredEightyEightMillionThreeHundredFiftyFiveThousandThreeHundredTwentyEightMinusOne = 140_737_488_355_328L - 1L;
|
||||
public const ulong TwoHundredEightyOneTrillionFourHundredSeventyFourBillionNineHundredSeventySixMillionSevenHundredTenThousandSixHundredFiftySixMinusOne = 281_474_976_710_656L - 1L;
|
||||
public const ulong FiveHundredSixtyTwoTrillionNineHundredFortyNineBillionNineHundredFiftyThreeMillionFourHundredTwentyOneThousandThreeHundredTwelveMinusOne = 562_949_953_421_312L - 1L;
|
||||
public const ulong OneQuadrillionOneHundredTwentyFiveTrillionEightHundredNinetyNineBillionNineHundredSixMillionEightHundredFortyTwoThousandSixHundredTwentyFourMinusOne = 1_125_899_906_842_624L - 1L;
|
||||
public const ulong TwoQuadrillionTwoHundredFiftyOneTrillionSevenHundredNinetyNineBillionEightHundredThirteenMillionSixHundredEightyFiveThousandTwoHundredFortyEightMinusOne = 2_251_799_813_685_248L - 1L;
|
||||
public const ulong FourQuadrillionFiveHundredThreeTrillionFiveHundredNinetyNineBillionSixHundredTwentySevenMillionThreeHundredSeventyThousandFourHundredNinetySixMinusOne = 4_503_599_627_370_496L - 1L;
|
||||
public const ulong NineQuadrillionSevenTrillionOneHundredNinetyNineBillionTwoHundredFiftyFourMillionSevenHundredFortyThousandNineHundredNinetyTwoMinusOne = 9_007_199_254_740_992L - 1L;
|
||||
public const ulong EighteenQuadrillionFourteenTrillionThreeHundredNinetyEightBillionFiveHundredEightMillionEightHundredEightyThousandOneHundredEightyFourMinusOne = 18_014_398_509_481_984L - 1L;
|
||||
public const ulong ThirtySixQuadrillionTwentyEightTrillionSevenHundredNinetySevenBillionSeventeenMillionSevenHundredSixtyThousandThreeHundredSixtyEightMinusOne = 36_028_797_018_963_968L - 1L;
|
||||
public const ulong SeventyTwoQuadrillionFiftySevenTrillionFiveHundredNinetyFourBillionThirtyFiveMillionFiveHundredTwentyThousandSevenHundredThirtySixMinusOne = 72_057_594_037_927_936L - 1L;
|
||||
public const ulong OneHundredFortyFourQuadrillionOneHundredFifteenTrillionOneHundredEightyEightBillionSeventyOneMillionFortyOneThousandFourHundredSeventyTwoMinusOne = 144_115_188_075_855_872L - 1L;
|
||||
public const ulong TwoHundredEightyEightQuadrillionTwoHundredThirtyTrillionThreeHundredSeventySixBillionOneHundredFortyTwoMillionEightyTwoThousandNineHundredFortyFourMinusOne = 288_230_376_151_711_744L - 1L;
|
||||
public const ulong FiveHundredSeventySixQuadrillionFourHundredSixtyTrillionSevenHundredFiftyTwoBillionTwoHundredEightyFourMillionOneHundredSixtyFiveThousandEightHundredEightyEightMinusOne = 576_460_752_303_423_488L - 1L;
|
||||
public const ulong OneQuintillionOneHundredFiftyTwoQuadrillionNineHundredTwentyOneTrillionFiveHundredFourBillionSixHundredSixtyEightMillionThreeHundredThirtyOneThousandSevenHundredSeventySixMinusOne = 1_152_921_504_606_846_976L - 1L;
|
||||
public const ulong TwoQuintillionThreeHundredFiveQuadrillionEightHundredFortyThreeTrillionNineBillionThreeHundredThirtySixMillionSixHundredSixtyThreeThousandFiveHundredFiftyTwoMinusOne = 2_305_843_009_213_693_952L - 1L;
|
||||
public const ulong FourQuintillionSixHundredElevenQuadrillionSixHundredEightySevenTrillionEightHundredSixteenBillionSixHundredSeventyThreeMillionTwoHundredTwentySevenThousandOneHundredFourMinusOne = 4_611_686_018_427_387_904L - 1L;
|
||||
public const ulong NineQuintillionTwoHundredTwentyThreeQuadrillionThreeHundredSeventyTwoTrillionThirtySixBillionEightHundredFiftyFourMillionSevenHundredSeventyFiveThousandEightHundredEightMinusOne = 9_223_372_036_854_775_808UL - 1UL;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static ulong ULongMinusOne(int i) => i switch
|
||||
{
|
||||
0 => 0UL,
|
||||
1 => ULongNumbers.TwoMinusOne,
|
||||
2 => ULongNumbers.FourMinusOne,
|
||||
3 => ULongNumbers.EightMinusOne,
|
||||
4 => ULongNumbers.SixteenMinusOne,
|
||||
5 => ULongNumbers.ThirtyTwoMinusOne,
|
||||
6 => ULongNumbers.SixtyFourMinusOne,
|
||||
7 => ULongNumbers.OneHundredTwentyEightMinusOne,
|
||||
8 => ULongNumbers.TwoHundredFiftySixMinusOne,
|
||||
9 => ULongNumbers.FiveHundredTwelveMinusOne,
|
||||
10 => ULongNumbers.OneThousandTwentyFourMinusOne,
|
||||
11 => ULongNumbers.TwoThousandFortyEightMinusOne,
|
||||
12 => ULongNumbers.FourThousandNinetySixMinusOne,
|
||||
13 => ULongNumbers.EightThousandOneHundredNinetyTwoMinusOne,
|
||||
14 => ULongNumbers.SixteenThousandThreeHundredEightyFourMinusOne,
|
||||
15 => ULongNumbers.ThirtyTwoThousandSevenHundredSixtyEightMinusOne,
|
||||
16 => ULongNumbers.SixtyFiveThousandFiveHundredThirtySixMinusOne,
|
||||
17 => ULongNumbers.OneHundredThirtyOneThousandSeventyTwoMinusOne,
|
||||
18 => ULongNumbers.TwoHundredSixtyTwoThousandOneHundredFortyFourMinusOne,
|
||||
19 => ULongNumbers.FiveHundredTwentyFourThousandTwoHundredEightyEightMinusOne,
|
||||
20 => ULongNumbers.OneMillionFortyEightThousandFiveHundredSeventySixMinusOne,
|
||||
21 => ULongNumbers.TwoMillionNinetySevenThousandOneHundredFiftyTwoMinusOne,
|
||||
22 => ULongNumbers.FourMillionOneHundredNinetyFourThousandThreeHundredFourMinusOne,
|
||||
23 => ULongNumbers.EightMillionThreeHundredEightyEightThousandSixHundredEightMinusOne,
|
||||
24 => ULongNumbers.SixteenMillionSevenHundredSeventySevenThousandTwoHundredSixteenMinusOne,
|
||||
25 => ULongNumbers.ThirtyThreeMillionFiveHundredFiftyFourThousandFourHundredThirtyTwoMinusOne,
|
||||
26 => ULongNumbers.SixtySevenMillionOneHundredEightThousandEightHundredSixtyFourMinusOne,
|
||||
27 => ULongNumbers.OneHundredThirtyFourMillionTwoHundredSeventeenThousandSevenHundredTwentyEightMinusOne,
|
||||
28 => ULongNumbers.TwoHundredSixtyEightMillionFourHundredThirtyFiveThousandFourHundredFiftySixMinusOne,
|
||||
29 => ULongNumbers.FiveHundredThirtySixMillionEightHundredSeventyThousandNineHundredTwelveMinusOne,
|
||||
30 => ULongNumbers.OneBillionSeventyThreeMillionSevenHundredFortyOneThousandEightHundredTwentyFourMinusOne,
|
||||
31 => ULongNumbers.TwoBillionOneHundredFortySevenMillionFourHundredEightyThreeThousandSixHundredFortyEightMinusOne,
|
||||
32 => ULongNumbers.FourBillionTwoHundredNinetyFourMillionNineHundredSixtySevenThousandTwoHundredNinetySixMinusOne,
|
||||
33 => ULongNumbers.EightBillionFiveHundredEightyNineMillionNineHundredThirtyFourThousandFiveHundredNinetyTwoMinusOne,
|
||||
34 => ULongNumbers.SeventeenBillionOneHundredSeventyNineMillionEightHundredSixtyNineThousandOneHundredEightyFourMinusOne,
|
||||
35 => ULongNumbers.ThirtyFourBillionThreeHundredFiftyNineMillionSevenHundredThirtyEightThousandThreeHundredSixtyEightMinusOne,
|
||||
36 => ULongNumbers.SixtyEightBillionSevenHundredNineteenMillionFourHundredSeventySixThousandSevenHundredThirtySixMinusOne,
|
||||
37 => ULongNumbers.OneHundredThirtySevenBillionFourHundredThirtyEightMillionNineHundredFiftyThreeThousandFourHundredSeventyTwoMinusOne,
|
||||
38 => ULongNumbers.TwoHundredSeventyFourBillionEightHundredSeventySevenMillionNineHundredSixThousandNineHundredFortyFourMinusOne,
|
||||
39 => ULongNumbers.FiveHundredFortyNineBillionSevenHundredFiftyFiveMillionEightHundredThirteenThousandEightHundredEightyEightMinusOne,
|
||||
40 => ULongNumbers.OneTrillionNinetyNineBillionFiveHundredElevenMillionSixHundredTwentySevenThousandSevenHundredSeventySixMinusOne,
|
||||
41 => ULongNumbers.TwoTrillionOneHundredNinetyNineBillionTwentyThreeMillionTwoHundredFiftyFiveThousandFiveHundredFiftyTwoMinusOne,
|
||||
42 => ULongNumbers.FourTrillionThreeHundredNinetyEightBillionFortySixMillionFiveHundredElevenThousandOneHundredFourMinusOne,
|
||||
43 => ULongNumbers.EightTrillionSevenHundredNinetySixBillionNinetyThreeMillionTwentyTwoThousandTwoHundredEightMinusOne,
|
||||
44 => ULongNumbers.SeventeenTrillionFiveHundredNinetyTwoBillionOneHundredEightySixMillionFortyFourThousandFourHundredSixteenMinusOne,
|
||||
45 => ULongNumbers.ThirtyFiveTrillionOneHundredEightyFourBillionThreeHundredSeventyTwoMillionEightyEightThousandEightHundredThirtyTwoMinusOne,
|
||||
46 => ULongNumbers.SeventyTrillionThreeHundredSixtyEightBillionSevenHundredFortyFourMillionOneHundredSeventySevenThousandSixHundredSixtyFourMinusOne,
|
||||
47 => ULongNumbers.OneHundredFortyTrillionSevenHundredThirtySevenBillionFourHundredEightyEightMillionThreeHundredFiftyFiveThousandThreeHundredTwentyEightMinusOne,
|
||||
48 => ULongNumbers.TwoHundredEightyOneTrillionFourHundredSeventyFourBillionNineHundredSeventySixMillionSevenHundredTenThousandSixHundredFiftySixMinusOne,
|
||||
49 => ULongNumbers.FiveHundredSixtyTwoTrillionNineHundredFortyNineBillionNineHundredFiftyThreeMillionFourHundredTwentyOneThousandThreeHundredTwelveMinusOne,
|
||||
50 => ULongNumbers.OneQuadrillionOneHundredTwentyFiveTrillionEightHundredNinetyNineBillionNineHundredSixMillionEightHundredFortyTwoThousandSixHundredTwentyFourMinusOne,
|
||||
51 => ULongNumbers.TwoQuadrillionTwoHundredFiftyOneTrillionSevenHundredNinetyNineBillionEightHundredThirteenMillionSixHundredEightyFiveThousandTwoHundredFortyEightMinusOne,
|
||||
52 => ULongNumbers.FourQuadrillionFiveHundredThreeTrillionFiveHundredNinetyNineBillionSixHundredTwentySevenMillionThreeHundredSeventyThousandFourHundredNinetySixMinusOne,
|
||||
53 => ULongNumbers.NineQuadrillionSevenTrillionOneHundredNinetyNineBillionTwoHundredFiftyFourMillionSevenHundredFortyThousandNineHundredNinetyTwoMinusOne,
|
||||
54 => ULongNumbers.EighteenQuadrillionFourteenTrillionThreeHundredNinetyEightBillionFiveHundredEightMillionEightHundredEightyThousandOneHundredEightyFourMinusOne,
|
||||
55 => ULongNumbers.ThirtySixQuadrillionTwentyEightTrillionSevenHundredNinetySevenBillionSeventeenMillionSevenHundredSixtyThousandThreeHundredSixtyEightMinusOne,
|
||||
56 => ULongNumbers.SeventyTwoQuadrillionFiftySevenTrillionFiveHundredNinetyFourBillionThirtyFiveMillionFiveHundredTwentyThousandSevenHundredThirtySixMinusOne,
|
||||
57 => ULongNumbers.OneHundredFortyFourQuadrillionOneHundredFifteenTrillionOneHundredEightyEightBillionSeventyOneMillionFortyOneThousandFourHundredSeventyTwoMinusOne,
|
||||
58 => ULongNumbers.TwoHundredEightyEightQuadrillionTwoHundredThirtyTrillionThreeHundredSeventySixBillionOneHundredFortyTwoMillionEightyTwoThousandNineHundredFortyFourMinusOne,
|
||||
59 => ULongNumbers.FiveHundredSeventySixQuadrillionFourHundredSixtyTrillionSevenHundredFiftyTwoBillionTwoHundredEightyFourMillionOneHundredSixtyFiveThousandEightHundredEightyEightMinusOne,
|
||||
60 => ULongNumbers.OneQuintillionOneHundredFiftyTwoQuadrillionNineHundredTwentyOneTrillionFiveHundredFourBillionSixHundredSixtyEightMillionThreeHundredThirtyOneThousandSevenHundredSeventySixMinusOne,
|
||||
61 => ULongNumbers.TwoQuintillionThreeHundredFiveQuadrillionEightHundredFortyThreeTrillionNineBillionThreeHundredThirtySixMillionSixHundredSixtyThreeThousandFiveHundredFiftyTwoMinusOne,
|
||||
62 => ULongNumbers.FourQuintillionSixHundredElevenQuadrillionSixHundredEightySevenTrillionEightHundredSixteenBillionSixHundredSeventyThreeMillionTwoHundredTwentySevenThousandOneHundredFourMinusOne,
|
||||
63 => ULongNumbers.NineQuintillionTwoHundredTwentyThreeQuadrillionThreeHundredSeventyTwoTrillionThirtySixBillionEightHundredFiftyFourMillionSevenHundredSeventyFiveThousandEightHundredEightMinusOne,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(i)),
|
||||
};
|
||||
|
||||
private static readonly ulong[] BitMask64 = CreateBitMask64();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong[] CreateBitMask64()
|
||||
{
|
||||
ulong[] masks = new ulong[64];
|
||||
ulong value = 1UL;
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
masks[i] = value;
|
||||
value <<= 1;
|
||||
}
|
||||
return masks;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public ulong ByShifting()
|
||||
{
|
||||
ulong result = 0UL;
|
||||
for (int i = 0; i <= 63; i++)
|
||||
{
|
||||
result = (1UL << i) - 1UL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public ulong ByMapping()
|
||||
{
|
||||
ulong result = 0UL;
|
||||
for (int i = 0; i <= 63; i++)
|
||||
{
|
||||
result = ULongMinusOne(i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public ulong StaticArray()
|
||||
{
|
||||
ulong result = 0UL;
|
||||
ulong[] bitMask64 = BitMask64;
|
||||
for (int i = 0; i <= 63; i++)
|
||||
{
|
||||
result = bitMask64[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class Pow2ProviderLongBenchmarks
|
||||
{
|
||||
public static class LongNumbers
|
||||
{
|
||||
public const long TwoMinusOne = 2L - 1L;
|
||||
public const long FourMinusOne = 4L - 1L;
|
||||
public const long EightMinusOne = 8L - 1L;
|
||||
public const long SixteenMinusOne = 16L - 1L;
|
||||
public const long ThirtyTwoMinusOne = 32L - 1L;
|
||||
public const long SixtyFourMinusOne = 64L - 1L;
|
||||
public const long OneHundredTwentyEightMinusOne = 128L - 1L;
|
||||
public const long TwoHundredFiftySixMinusOne = 256L - 1L;
|
||||
public const long FiveHundredTwelveMinusOne = 512L - 1L;
|
||||
public const long OneThousandTwentyFourMinusOne = 1_024L - 1L;
|
||||
public const long TwoThousandFortyEightMinusOne = 2_048L - 1L;
|
||||
public const long FourThousandNinetySixMinusOne = 4_096L - 1L;
|
||||
public const long EightThousandOneHundredNinetyTwoMinusOne = 8_192L - 1L;
|
||||
public const long SixteenThousandThreeHundredEightyFourMinusOne = 16_384L - 1L;
|
||||
public const long ThirtyTwoThousandSevenHundredSixtyEightMinusOne = 32_768L - 1L;
|
||||
public const long SixtyFiveThousandFiveHundredThirtySixMinusOne = 65_536L - 1L;
|
||||
public const long OneHundredThirtyOneThousandSeventyTwoMinusOne = 131_072L - 1L;
|
||||
public const long TwoHundredSixtyTwoThousandOneHundredFortyFourMinusOne = 262_144L - 1L;
|
||||
public const long FiveHundredTwentyFourThousandTwoHundredEightyEightMinusOne = 524_288L - 1L;
|
||||
public const long OneMillionFortyEightThousandFiveHundredSeventySixMinusOne = 1_048_576L - 1L;
|
||||
public const long TwoMillionNinetySevenThousandOneHundredFiftyTwoMinusOne = 2_097_152L - 1L;
|
||||
public const long FourMillionOneHundredNinetyFourThousandThreeHundredFourMinusOne = 4_194_304L - 1L;
|
||||
public const long EightMillionThreeHundredEightyEightThousandSixHundredEightMinusOne = 8_388_608L - 1L;
|
||||
public const long SixteenMillionSevenHundredSeventySevenThousandTwoHundredSixteenMinusOne = 16_777_216L - 1L;
|
||||
public const long ThirtyThreeMillionFiveHundredFiftyFourThousandFourHundredThirtyTwoMinusOne = 33_554_432L - 1L;
|
||||
public const long SixtySevenMillionOneHundredEightThousandEightHundredSixtyFourMinusOne = 67_108_864L - 1L;
|
||||
public const long OneHundredThirtyFourMillionTwoHundredSeventeenThousandSevenHundredTwentyEightMinusOne = 134_217_728L - 1L;
|
||||
public const long TwoHundredSixtyEightMillionFourHundredThirtyFiveThousandFourHundredFiftySixMinusOne = 268_435_456L - 1L;
|
||||
public const long FiveHundredThirtySixMillionEightHundredSeventyThousandNineHundredTwelveMinusOne = 536_870_912L - 1L;
|
||||
public const long OneBillionSeventyThreeMillionSevenHundredFortyOneThousandEightHundredTwentyFourMinusOne = 1_073_741_824L - 1L;
|
||||
public const long TwoBillionOneHundredFortySevenMillionFourHundredEightyThreeThousandSixHundredFortyEightMinusOne = 2_147_483_648L - 1L;
|
||||
public const long FourBillionTwoHundredNinetyFourMillionNineHundredSixtySevenThousandTwoHundredNinetySixMinusOne = 4_294_967_296L - 1L;
|
||||
public const long EightBillionFiveHundredEightyNineMillionNineHundredThirtyFourThousandFiveHundredNinetyTwoMinusOne = 8_589_934_592L - 1L;
|
||||
public const long SeventeenBillionOneHundredSeventyNineMillionEightHundredSixtyNineThousandOneHundredEightyFourMinusOne = 17_179_869_184L - 1L;
|
||||
public const long ThirtyFourBillionThreeHundredFiftyNineMillionSevenHundredThirtyEightThousandThreeHundredSixtyEightMinusOne = 34_359_738_368L - 1L;
|
||||
public const long SixtyEightBillionSevenHundredNineteenMillionFourHundredSeventySixThousandSevenHundredThirtySixMinusOne = 68_719_476_736L - 1L;
|
||||
public const long OneHundredThirtySevenBillionFourHundredThirtyEightMillionNineHundredFiftyThreeThousandFourHundredSeventyTwoMinusOne = 137_438_953_472L - 1L;
|
||||
public const long TwoHundredSeventyFourBillionEightHundredSeventySevenMillionNineHundredSixThousandNineHundredFortyFourMinusOne = 274_877_906_944L - 1L;
|
||||
public const long FiveHundredFortyNineBillionSevenHundredFiftyFiveMillionEightHundredThirteenThousandEightHundredEightyEightMinusOne = 549_755_813_888L - 1L;
|
||||
public const long OneTrillionNinetyNineBillionFiveHundredElevenMillionSixHundredTwentySevenThousandSevenHundredSeventySixMinusOne = 1_099_511_627_776L - 1L;
|
||||
public const long TwoTrillionOneHundredNinetyNineBillionTwentyThreeMillionTwoHundredFiftyFiveThousandFiveHundredFiftyTwoMinusOne = 2_199_023_255_552L - 1L;
|
||||
public const long FourTrillionThreeHundredNinetyEightBillionFortySixMillionFiveHundredElevenThousandOneHundredFourMinusOne = 4_398_046_511_104L - 1L;
|
||||
public const long EightTrillionSevenHundredNinetySixBillionNinetyThreeMillionTwentyTwoThousandTwoHundredEightMinusOne = 8_796_093_022_208L - 1L;
|
||||
public const long SeventeenTrillionFiveHundredNinetyTwoBillionOneHundredEightySixMillionFortyFourThousandFourHundredSixteenMinusOne = 17_592_186_044_416L - 1L;
|
||||
public const long ThirtyFiveTrillionOneHundredEightyFourBillionThreeHundredSeventyTwoMillionEightyEightThousandEightHundredThirtyTwoMinusOne = 35_184_372_088_832L - 1L;
|
||||
public const long SeventyTrillionThreeHundredSixtyEightBillionSevenHundredFortyFourMillionOneHundredSeventySevenThousandSixHundredSixtyFourMinusOne = 70_368_744_177_664L - 1L;
|
||||
public const long OneHundredFortyTrillionSevenHundredThirtySevenBillionFourHundredEightyEightMillionThreeHundredFiftyFiveThousandThreeHundredTwentyEightMinusOne = 140_737_488_355_328L - 1L;
|
||||
public const long TwoHundredEightyOneTrillionFourHundredSeventyFourBillionNineHundredSeventySixMillionSevenHundredTenThousandSixHundredFiftySixMinusOne = 281_474_976_710_656L - 1L;
|
||||
public const long FiveHundredSixtyTwoTrillionNineHundredFortyNineBillionNineHundredFiftyThreeMillionFourHundredTwentyOneThousandThreeHundredTwelveMinusOne = 562_949_953_421_312L - 1L;
|
||||
public const long OneQuadrillionOneHundredTwentyFiveTrillionEightHundredNinetyNineBillionNineHundredSixMillionEightHundredFortyTwoThousandSixHundredTwentyFourMinusOne = 1_125_899_906_842_624L - 1L;
|
||||
public const long TwoQuadrillionTwoHundredFiftyOneTrillionSevenHundredNinetyNineBillionEightHundredThirteenMillionSixHundredEightyFiveThousandTwoHundredFortyEightMinusOne = 2_251_799_813_685_248L - 1L;
|
||||
public const long FourQuadrillionFiveHundredThreeTrillionFiveHundredNinetyNineBillionSixHundredTwentySevenMillionThreeHundredSeventyThousandFourHundredNinetySixMinusOne = 4_503_599_627_370_496L - 1L;
|
||||
public const long NineQuadrillionSevenTrillionOneHundredNinetyNineBillionTwoHundredFiftyFourMillionSevenHundredFortyThousandNineHundredNinetyTwoMinusOne = 9_007_199_254_740_992L - 1L;
|
||||
public const long EighteenQuadrillionFourteenTrillionThreeHundredNinetyEightBillionFiveHundredEightMillionEightHundredEightyThousandOneHundredEightyFourMinusOne = 18_014_398_509_481_984L - 1L;
|
||||
public const long ThirtySixQuadrillionTwentyEightTrillionSevenHundredNinetySevenBillionSeventeenMillionSevenHundredSixtyThousandThreeHundredSixtyEightMinusOne = 36_028_797_018_963_968L - 1L;
|
||||
public const long SeventyTwoQuadrillionFiftySevenTrillionFiveHundredNinetyFourBillionThirtyFiveMillionFiveHundredTwentyThousandSevenHundredThirtySixMinusOne = 72_057_594_037_927_936L - 1L;
|
||||
public const long OneHundredFortyFourQuadrillionOneHundredFifteenTrillionOneHundredEightyEightBillionSeventyOneMillionFortyOneThousandFourHundredSeventyTwoMinusOne = 144_115_188_075_855_872L - 1L;
|
||||
public const long TwoHundredEightyEightQuadrillionTwoHundredThirtyTrillionThreeHundredSeventySixBillionOneHundredFortyTwoMillionEightyTwoThousandNineHundredFortyFourMinusOne = 288_230_376_151_711_744L - 1L;
|
||||
public const long FiveHundredSeventySixQuadrillionFourHundredSixtyTrillionSevenHundredFiftyTwoBillionTwoHundredEightyFourMillionOneHundredSixtyFiveThousandEightHundredEightyEightMinusOne = 576_460_752_303_423_488L - 1L;
|
||||
public const long OneQuintillionOneHundredFiftyTwoQuadrillionNineHundredTwentyOneTrillionFiveHundredFourBillionSixHundredSixtyEightMillionThreeHundredThirtyOneThousandSevenHundredSeventySixMinusOne = 1_152_921_504_606_846_976L - 1L;
|
||||
public const long TwoQuintillionThreeHundredFiveQuadrillionEightHundredFortyThreeTrillionNineBillionThreeHundredThirtySixMillionSixHundredSixtyThreeThousandFiveHundredFiftyTwoMinusOne = 2_305_843_009_213_693_952L - 1L;
|
||||
public const long FourQuintillionSixHundredElevenQuadrillionSixHundredEightySevenTrillionEightHundredSixteenBillionSixHundredSeventyThreeMillionTwoHundredTwentySevenThousandOneHundredFourMinusOne = 4_611_686_018_427_387_904L - 1L;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static long LongMinusOne(int i) => i switch
|
||||
{
|
||||
0 => 0L,
|
||||
1 => LongNumbers.TwoMinusOne,
|
||||
2 => LongNumbers.FourMinusOne,
|
||||
3 => LongNumbers.EightMinusOne,
|
||||
4 => LongNumbers.SixteenMinusOne,
|
||||
5 => LongNumbers.ThirtyTwoMinusOne,
|
||||
6 => LongNumbers.SixtyFourMinusOne,
|
||||
7 => LongNumbers.OneHundredTwentyEightMinusOne,
|
||||
8 => LongNumbers.TwoHundredFiftySixMinusOne,
|
||||
9 => LongNumbers.FiveHundredTwelveMinusOne,
|
||||
10 => LongNumbers.OneThousandTwentyFourMinusOne,
|
||||
11 => LongNumbers.TwoThousandFortyEightMinusOne,
|
||||
12 => LongNumbers.FourThousandNinetySixMinusOne,
|
||||
13 => LongNumbers.EightThousandOneHundredNinetyTwoMinusOne,
|
||||
14 => LongNumbers.SixteenThousandThreeHundredEightyFourMinusOne,
|
||||
15 => LongNumbers.ThirtyTwoThousandSevenHundredSixtyEightMinusOne,
|
||||
16 => LongNumbers.SixtyFiveThousandFiveHundredThirtySixMinusOne,
|
||||
17 => LongNumbers.OneHundredThirtyOneThousandSeventyTwoMinusOne,
|
||||
18 => LongNumbers.TwoHundredSixtyTwoThousandOneHundredFortyFourMinusOne,
|
||||
19 => LongNumbers.FiveHundredTwentyFourThousandTwoHundredEightyEightMinusOne,
|
||||
20 => LongNumbers.OneMillionFortyEightThousandFiveHundredSeventySixMinusOne,
|
||||
21 => LongNumbers.TwoMillionNinetySevenThousandOneHundredFiftyTwoMinusOne,
|
||||
22 => LongNumbers.FourMillionOneHundredNinetyFourThousandThreeHundredFourMinusOne,
|
||||
23 => LongNumbers.EightMillionThreeHundredEightyEightThousandSixHundredEightMinusOne,
|
||||
24 => LongNumbers.SixteenMillionSevenHundredSeventySevenThousandTwoHundredSixteenMinusOne,
|
||||
25 => LongNumbers.ThirtyThreeMillionFiveHundredFiftyFourThousandFourHundredThirtyTwoMinusOne,
|
||||
26 => LongNumbers.SixtySevenMillionOneHundredEightThousandEightHundredSixtyFourMinusOne,
|
||||
27 => LongNumbers.OneHundredThirtyFourMillionTwoHundredSeventeenThousandSevenHundredTwentyEightMinusOne,
|
||||
28 => LongNumbers.TwoHundredSixtyEightMillionFourHundredThirtyFiveThousandFourHundredFiftySixMinusOne,
|
||||
29 => LongNumbers.FiveHundredThirtySixMillionEightHundredSeventyThousandNineHundredTwelveMinusOne,
|
||||
30 => LongNumbers.OneBillionSeventyThreeMillionSevenHundredFortyOneThousandEightHundredTwentyFourMinusOne,
|
||||
31 => LongNumbers.TwoBillionOneHundredFortySevenMillionFourHundredEightyThreeThousandSixHundredFortyEightMinusOne,
|
||||
32 => LongNumbers.FourBillionTwoHundredNinetyFourMillionNineHundredSixtySevenThousandTwoHundredNinetySixMinusOne,
|
||||
33 => LongNumbers.EightBillionFiveHundredEightyNineMillionNineHundredThirtyFourThousandFiveHundredNinetyTwoMinusOne,
|
||||
34 => LongNumbers.SeventeenBillionOneHundredSeventyNineMillionEightHundredSixtyNineThousandOneHundredEightyFourMinusOne,
|
||||
35 => LongNumbers.ThirtyFourBillionThreeHundredFiftyNineMillionSevenHundredThirtyEightThousandThreeHundredSixtyEightMinusOne,
|
||||
36 => LongNumbers.SixtyEightBillionSevenHundredNineteenMillionFourHundredSeventySixThousandSevenHundredThirtySixMinusOne,
|
||||
37 => LongNumbers.OneHundredThirtySevenBillionFourHundredThirtyEightMillionNineHundredFiftyThreeThousandFourHundredSeventyTwoMinusOne,
|
||||
38 => LongNumbers.TwoHundredSeventyFourBillionEightHundredSeventySevenMillionNineHundredSixThousandNineHundredFortyFourMinusOne,
|
||||
39 => LongNumbers.FiveHundredFortyNineBillionSevenHundredFiftyFiveMillionEightHundredThirteenThousandEightHundredEightyEightMinusOne,
|
||||
40 => LongNumbers.OneTrillionNinetyNineBillionFiveHundredElevenMillionSixHundredTwentySevenThousandSevenHundredSeventySixMinusOne,
|
||||
41 => LongNumbers.TwoTrillionOneHundredNinetyNineBillionTwentyThreeMillionTwoHundredFiftyFiveThousandFiveHundredFiftyTwoMinusOne,
|
||||
42 => LongNumbers.FourTrillionThreeHundredNinetyEightBillionFortySixMillionFiveHundredElevenThousandOneHundredFourMinusOne,
|
||||
43 => LongNumbers.EightTrillionSevenHundredNinetySixBillionNinetyThreeMillionTwentyTwoThousandTwoHundredEightMinusOne,
|
||||
44 => LongNumbers.SeventeenTrillionFiveHundredNinetyTwoBillionOneHundredEightySixMillionFortyFourThousandFourHundredSixteenMinusOne,
|
||||
45 => LongNumbers.ThirtyFiveTrillionOneHundredEightyFourBillionThreeHundredSeventyTwoMillionEightyEightThousandEightHundredThirtyTwoMinusOne,
|
||||
46 => LongNumbers.SeventyTrillionThreeHundredSixtyEightBillionSevenHundredFortyFourMillionOneHundredSeventySevenThousandSixHundredSixtyFourMinusOne,
|
||||
47 => LongNumbers.OneHundredFortyTrillionSevenHundredThirtySevenBillionFourHundredEightyEightMillionThreeHundredFiftyFiveThousandThreeHundredTwentyEightMinusOne,
|
||||
48 => LongNumbers.TwoHundredEightyOneTrillionFourHundredSeventyFourBillionNineHundredSeventySixMillionSevenHundredTenThousandSixHundredFiftySixMinusOne,
|
||||
49 => LongNumbers.FiveHundredSixtyTwoTrillionNineHundredFortyNineBillionNineHundredFiftyThreeMillionFourHundredTwentyOneThousandThreeHundredTwelveMinusOne,
|
||||
50 => LongNumbers.OneQuadrillionOneHundredTwentyFiveTrillionEightHundredNinetyNineBillionNineHundredSixMillionEightHundredFortyTwoThousandSixHundredTwentyFourMinusOne,
|
||||
51 => LongNumbers.TwoQuadrillionTwoHundredFiftyOneTrillionSevenHundredNinetyNineBillionEightHundredThirteenMillionSixHundredEightyFiveThousandTwoHundredFortyEightMinusOne,
|
||||
52 => LongNumbers.FourQuadrillionFiveHundredThreeTrillionFiveHundredNinetyNineBillionSixHundredTwentySevenMillionThreeHundredSeventyThousandFourHundredNinetySixMinusOne,
|
||||
53 => LongNumbers.NineQuadrillionSevenTrillionOneHundredNinetyNineBillionTwoHundredFiftyFourMillionSevenHundredFortyThousandNineHundredNinetyTwoMinusOne,
|
||||
54 => LongNumbers.EighteenQuadrillionFourteenTrillionThreeHundredNinetyEightBillionFiveHundredEightMillionEightHundredEightyThousandOneHundredEightyFourMinusOne,
|
||||
55 => LongNumbers.ThirtySixQuadrillionTwentyEightTrillionSevenHundredNinetySevenBillionSeventeenMillionSevenHundredSixtyThousandThreeHundredSixtyEightMinusOne,
|
||||
56 => LongNumbers.SeventyTwoQuadrillionFiftySevenTrillionFiveHundredNinetyFourBillionThirtyFiveMillionFiveHundredTwentyThousandSevenHundredThirtySixMinusOne,
|
||||
57 => LongNumbers.OneHundredFortyFourQuadrillionOneHundredFifteenTrillionOneHundredEightyEightBillionSeventyOneMillionFortyOneThousandFourHundredSeventyTwoMinusOne,
|
||||
58 => LongNumbers.TwoHundredEightyEightQuadrillionTwoHundredThirtyTrillionThreeHundredSeventySixBillionOneHundredFortyTwoMillionEightyTwoThousandNineHundredFortyFourMinusOne,
|
||||
59 => LongNumbers.FiveHundredSeventySixQuadrillionFourHundredSixtyTrillionSevenHundredFiftyTwoBillionTwoHundredEightyFourMillionOneHundredSixtyFiveThousandEightHundredEightyEightMinusOne,
|
||||
60 => LongNumbers.OneQuintillionOneHundredFiftyTwoQuadrillionNineHundredTwentyOneTrillionFiveHundredFourBillionSixHundredSixtyEightMillionThreeHundredThirtyOneThousandSevenHundredSeventySixMinusOne,
|
||||
61 => LongNumbers.TwoQuintillionThreeHundredFiveQuadrillionEightHundredFortyThreeTrillionNineBillionThreeHundredThirtySixMillionSixHundredSixtyThreeThousandFiveHundredFiftyTwoMinusOne,
|
||||
62 => LongNumbers.FourQuintillionSixHundredElevenQuadrillionSixHundredEightySevenTrillionEightHundredSixteenBillionSixHundredSeventyThreeMillionTwoHundredTwentySevenThousandOneHundredFourMinusOne,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(i)),
|
||||
};
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public long ByShifting()
|
||||
{
|
||||
long result = 0L;
|
||||
for (int i = 0; i <= 62; i++)
|
||||
{
|
||||
result = (1L << i) - 1L;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public long ByMapping()
|
||||
{
|
||||
long result = 0L;
|
||||
for (int i = 0; i <= 62; i++)
|
||||
{
|
||||
result = LongMinusOne(i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using Open.Numeric.Primes;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner.Benchmarks;
|
||||
|
||||
@@ -11,6 +12,8 @@ namespace EvenPerfectBitScanner.Benchmarks;
|
||||
[MemoryDiagnoser]
|
||||
public class PrimeTesterBenchmarks
|
||||
{
|
||||
private static readonly HeuristicCombinedPrimeTester _heuristicCombinedTester = new();
|
||||
private static readonly HeuristicPrimeTester _heuristicTester = new();
|
||||
private ulong[] _candidates = [];
|
||||
|
||||
[ParamsSource(nameof(GetBenchmarkCases))]
|
||||
@@ -19,8 +22,6 @@ public class PrimeTesterBenchmarks
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
EnvironmentConfiguration.RollingAccelerators = 1;
|
||||
|
||||
_candidates = BenchmarkCase.Candidates;
|
||||
|
||||
if (_candidates.Length == 0)
|
||||
@@ -28,26 +29,21 @@ public class PrimeTesterBenchmarks
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
PrimeOrderCalculatorAccelerator.WarmUp();
|
||||
// HeuristicGroupABPrimeTesterAccelerator.WarmUp();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
_ = HeuristicPrimeTester.IsPrimeCpu(_candidates[0]);
|
||||
_ = HeuristicPrimeTester.IsPrimeGpu(gpu, _candidates[0]);
|
||||
_ = _heuristicTester.IsPrimeCpu(_candidates[0]);
|
||||
_ = _heuristicTester.IsPrimeGpu(_candidates[0]);
|
||||
_ = Prime.Numbers.IsPrime(_candidates[0]);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
|
||||
// [Benchmark]
|
||||
[Benchmark(Baseline = true)]
|
||||
public int HeuristicCpu()
|
||||
{
|
||||
HeuristicPrimeTester tester = _heuristicTester;
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (HeuristicPrimeTester.IsPrimeCpu(values[i]))
|
||||
if (tester.IsPrimeCpu(values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
@@ -56,35 +52,33 @@ public class PrimeTesterBenchmarks
|
||||
return primeCount;
|
||||
}
|
||||
|
||||
// [Benchmark]
|
||||
[Benchmark]
|
||||
public int HeuristicGpu()
|
||||
{
|
||||
HeuristicPrimeTester tester = _heuristicTester;
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (HeuristicPrimeTester.IsPrimeGpu(gpu, values[i]))
|
||||
if (tester.IsPrimeGpu(values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return primeCount;
|
||||
}
|
||||
|
||||
// [Benchmark]
|
||||
[Benchmark]
|
||||
public int HeuristicCombinedGpu()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (HeuristicCombinedPrimeTester.IsPrimeGpu(gpu, values[i]))
|
||||
if (HeuristicCombinedPrimeTester.IsPrimeGpu(values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
@@ -93,7 +87,7 @@ public class PrimeTesterBenchmarks
|
||||
return primeCount;
|
||||
}
|
||||
|
||||
// [Benchmark]
|
||||
[Benchmark]
|
||||
public int HeuristicCombinedCpu()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
@@ -110,26 +104,27 @@ public class PrimeTesterBenchmarks
|
||||
return primeCount;
|
||||
}
|
||||
|
||||
// [Benchmark]
|
||||
private static readonly PrimeTester _nonHeuristicTester = new();
|
||||
|
||||
[Benchmark]
|
||||
public int NonHeuristicGpu()
|
||||
{
|
||||
PrimeTester tester = _nonHeuristicTester;
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (PrimeTester.IsPrimeGpu(gpu, values[i]))
|
||||
if (tester.IsPrimeGpu(values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
return primeCount;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
[Benchmark]
|
||||
public int NonHeuristicCpu()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
@@ -137,7 +132,7 @@ public class PrimeTesterBenchmarks
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (PrimeTester.IsPrimeCpu(values[i]))
|
||||
if (PrimeTester.IsPrime(values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
@@ -147,40 +142,6 @@ public class PrimeTesterBenchmarks
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int ByLastDigitCpu()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (PrimeTesterByLastDigit.IsPrimeCpu(values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return primeCount;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int SevenOrOthersCpu()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
int primeCount = 0;
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (PrimeTesterSeverOrOthers.IsPrimeCpu(values[i]))
|
||||
{
|
||||
primeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return primeCount;
|
||||
}
|
||||
|
||||
// [Benchmark]
|
||||
public int OpenNumericPrimes()
|
||||
{
|
||||
ulong[] values = _candidates;
|
||||
@@ -213,54 +174,12 @@ public class PrimeTesterBenchmarks
|
||||
4_000_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥16_000_000",
|
||||
"Sampled admissible odd values ≥ 16,000,000 (k ≤ 5, not divisible by 5)",
|
||||
16_000_001UL,
|
||||
16_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥32_000_000",
|
||||
"Sampled admissible odd values ≥ 32,000,000 (k ≤ 5, not divisible by 5)",
|
||||
32_000_001UL,
|
||||
32_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥64_000_000",
|
||||
"Sampled admissible odd values ≥ 64,000,000 (k ≤ 5, not divisible by 5)",
|
||||
64_000_001UL,
|
||||
64_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥100_000_000",
|
||||
"Sampled admissible odd values ≥ 100,000,000 (k ≤ 5, not divisible by 5)",
|
||||
100_000_001UL,
|
||||
100_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥138_000_000",
|
||||
"Sampled admissible odd values ≥ 138,000,000 (k ≤ 5, not divisible by 5)",
|
||||
138_000_001UL,
|
||||
138_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥276_000_000",
|
||||
"Sampled admissible odd values ≥ 276,000,000 (k ≤ 5, not divisible by 5)",
|
||||
276_000_001UL,
|
||||
276_500_000UL,
|
||||
4_096);
|
||||
|
||||
yield return PrimeBenchmarkCase.FromRange(
|
||||
"≥552_000_000",
|
||||
"Sampled admissible odd values ≥ 552,000,000 (k ≤ 5, not divisible by 5)",
|
||||
552_000_001UL,
|
||||
552_500_000UL,
|
||||
4_096);
|
||||
}
|
||||
|
||||
public readonly record struct PrimeBenchmarkCase(string Name, string Description, ulong[] Candidates)
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Open.Numeric.Primes" Version="4.0.4" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.54.1-alpha.0.37" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Open.Numeric.Primes;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace EvenPerfectBitScanner.ResultsParser;
|
||||
|
||||
@@ -20,7 +19,7 @@ internal static class Program
|
||||
"/help"
|
||||
};
|
||||
|
||||
private const string DefaultHeader = "p,divisor,searchedMersenne,detailedCheck,passedAllTests";
|
||||
private const string DefaultHeader = "p,searchedMersenne,detailedCheck,passedAllTests";
|
||||
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
@@ -84,7 +83,7 @@ private const string DefaultHeader = "p,divisor,searchedMersenne,detailedCheck,p
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
AnsiConsole.WriteException(exception);
|
||||
Console.Error.WriteLine($"Processing failed: {exception.Message}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
@@ -3,10 +3,13 @@ using Xunit;
|
||||
using System.Reflection;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
|
||||
using EvenPerfectBitScanner;
|
||||
using EvenPerfectBitScanner.Candidates;
|
||||
using EvenPerfectBitScanner.Candidates.Transforms;
|
||||
using System.Numerics;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EvenPerfectBitScanner.Tests;
|
||||
|
||||
@@ -52,7 +55,7 @@ public class ProgramTests
|
||||
[InlineData(81UL, false)]
|
||||
public void IsPrime_identifies_primes_correctly(ulong n, bool expected)
|
||||
{
|
||||
PrimeTester.IsPrimeCpu(n).Should().Be(expected);
|
||||
PrimeTester.IsPrime(n).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -99,26 +102,27 @@ public class ProgramTests
|
||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var candidatesField = typeof(MersenneNumberDivisorGpuTester).GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
candidatesField.SetValue(null, new (ulong, uint)[] { (7UL, 3U), (23UL, 11U) });
|
||||
forceCpuProp!.SetValue(null, true);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, 11UL, 32UL, out _, out _).Should().BeFalse();
|
||||
Program.IsEvenPerfectCandidate(gpu, 5UL, 32UL, out _, out _).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(gpu, 127UL, 32UL, out _, out _).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(11UL, 32UL, out _, out _).Should().BeFalse();
|
||||
Program.IsEvenPerfectCandidate(5UL, 32UL, out _, out _).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(127UL, 32UL, out _, out _).Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
testerField.SetValue(null, null);
|
||||
candidatesField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -131,26 +135,27 @@ public class ProgramTests
|
||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var candidatesField = typeof(MersenneNumberDivisorGpuTester).GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
candidatesField.SetValue(null, Array.Empty<(ulong, uint)>());
|
||||
forceCpuProp!.SetValue(null, true);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, 11UL, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(11UL, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
detailedCheck.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
testerField.SetValue(null, null);
|
||||
candidatesField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -163,24 +168,25 @@ public class ProgramTests
|
||||
|
||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, true);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, 3UL, 4_000_000_000UL, out bool searched, out bool detailed).Should().BeFalse();
|
||||
Program.IsEvenPerfectCandidate(3UL, 4_000_000_000UL, out bool searched, out bool detailed).Should().BeFalse();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
testerField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -193,28 +199,29 @@ public class ProgramTests
|
||||
|
||||
var testerField = typeof(Program).GetField("_divisorTester", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
testerField.SetValue(null, new MersenneNumberDivisorGpuTester());
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, 11UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
|
||||
Program.IsEvenPerfectCandidate(11UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeTrue();
|
||||
|
||||
Program.IsEvenPerfectCandidate(gpu, 13UL, 0UL, out searched, out detailed).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(13UL, 0UL, out searched, out detailed).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
testerField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -226,28 +233,29 @@ public class ProgramTests
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<object>(() => new MersenneNumberTesterForResidueCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu( maxK: 1_000UL), trackAllValues: true));
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(useIncremental: true, useResidue: true, maxK: 1_000UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, 29UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
|
||||
Program.IsEvenPerfectCandidate(29UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeFalse();
|
||||
|
||||
Program.IsEvenPerfectCandidate(gpu, 31UL, 0UL, out searched, out detailed).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(31UL, 0UL, out searched, out detailed).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -259,30 +267,32 @@ public class ProgramTests
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<object>(() => new MersenneNumberTesterForResidueCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu(
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: true,
|
||||
useResidue: true,
|
||||
maxK: 1_024UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
ulong[] primes = [107UL, 127UL];
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, prime, 1024UL, out bool searched, out bool detailed).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(prime, 1024UL, out bool searched, out bool detailed).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -295,30 +305,32 @@ public class ProgramTests
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<object>(() => new MersenneNumberTesterForResidueCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu(
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: true,
|
||||
useResidue: true,
|
||||
maxK: 5_000_000UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
ulong[] primes = [107UL, 127UL, 521UL, 607UL];
|
||||
Parallel.ForEach(primes, prime =>
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, prime, 0UL, out bool searched, out bool detailed).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(prime, 0UL, out bool searched, out bool detailed).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeTrue();
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -330,30 +342,32 @@ public class ProgramTests
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<object>(() => new MersenneNumberTesterForResidueCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu(
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: true,
|
||||
useResidue: true,
|
||||
maxK: 5_000_000UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, 107UL, 0UL, out bool searched, out bool detailed).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(107UL, 0UL, out bool searched, out bool detailed).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeTrue();
|
||||
|
||||
Program.IsEvenPerfectCandidate(gpu, 127UL, 0UL, out searched, out detailed).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(127UL, 0UL, out searched, out detailed).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
detailed.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -366,25 +380,29 @@ public class ProgramTests
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<object>(() => new MersenneNumberTesterForResidueCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu(
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: true,
|
||||
useGpuScan: false,
|
||||
useGpuOrder: false,
|
||||
useResidue: true,
|
||||
maxK: 1_024UL), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, true);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -399,38 +417,32 @@ public class ProgramTests
|
||||
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<object>(() =>
|
||||
{
|
||||
if (configuration.UseGpuOrder)
|
||||
{
|
||||
return new MersenneNumberTesterForResidueCalculationMethodForDisabledCacheStatusForGpuOrderDeviceForCpu(
|
||||
|
||||
kernelType: configuration.KernelType,
|
||||
maxK: configuration.ResidueMaxK);
|
||||
}
|
||||
|
||||
return new MersenneNumberTesterForResidueCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu(
|
||||
|
||||
kernelType: configuration.KernelType,
|
||||
maxK: configuration.ResidueMaxK);
|
||||
}, trackAllValues: true));
|
||||
mersenneField.SetValue(null, new ThreadLocal<MersenneNumberTester>(() => new MersenneNumberTester(
|
||||
useIncremental: false,
|
||||
kernelType: configuration.KernelType,
|
||||
useGpuScan: true,
|
||||
useGpuOrder: configuration.UseGpuOrder,
|
||||
useResidue: true,
|
||||
maxK: configuration.ResidueMaxK), trackAllValues: true));
|
||||
primeField.SetValue(null, new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, exponent, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Program.IsEvenPerfectCandidate(gpu, exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
|
||||
Program.IsEvenPerfectCandidate(exponent, 0UL, out bool searched, out bool detailedCheck).Should().BeTrue();
|
||||
searched.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
GpuContextPool.DisposeAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,32 +506,33 @@ public class ProgramTests
|
||||
var mersenneField = typeof(Program).GetField("MersenneTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var primeField = typeof(Program).GetField("PrimeTesters", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var compositeField = typeof(Program).GetField("_lastCompositeP", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var forceCpuProp = typeof(GpuContextPool).GetProperty("ForceCpu");
|
||||
|
||||
mersenneField.SetValue(null, new ThreadLocal<object>(() => new MersenneNumberTesterForIncrementalCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu( maxK: 1_000UL), trackAllValues: true));
|
||||
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));
|
||||
CandidatesCalculator.OverrideResidueTrackers(new ThreadLocal<ModResidueTracker>(() => new ModResidueTracker(ResidueModel.Identity, 2UL, true), trackAllValues: true));
|
||||
forceCpuProp!.SetValue(null, true);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
compositeField.SetValue(null, false);
|
||||
|
||||
Program.IsEvenPerfectCandidate(gpu, 9UL, 0UL, out bool searched, out bool detailed).Should().BeFalse();
|
||||
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(gpu, 11UL, 0UL, out searched, out detailed);
|
||||
Program.IsEvenPerfectCandidate(11UL, 0UL, out searched, out detailed);
|
||||
searched.Should().BeTrue();
|
||||
((bool)compositeField.GetValue(null)!).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
mersenneField.SetValue(null, null);
|
||||
primeField.SetValue(null, null);
|
||||
CandidatesCalculator.ResetResidueTrackers();
|
||||
compositeField.SetValue(null, false);
|
||||
forceCpuProp!.SetValue(null, false);
|
||||
ResetCliArguments();
|
||||
}
|
||||
}
|
||||
@@ -544,9 +557,8 @@ public class ProgramTests
|
||||
|
||||
string output = writer.ToString();
|
||||
output.Should().Contain("Usage:");
|
||||
output.Should().Contain("--mersenne-device=cpu|gpu|hybrid");
|
||||
output.Should().Contain("--mersenne-device=cpu|gpu");
|
||||
output.Should().Contain("--primes-device=cpu|gpu");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
using System.Buffers.Text;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using PerfectNumbers.Core;
|
||||
|
||||
namespace EvenPerfectBitScanner;
|
||||
|
||||
internal static class CalculationResultHandler
|
||||
{
|
||||
private const string ResultsHeader = "p,divisor,searchedMersenne,detailedCheck,passedAllTests";
|
||||
private const string ResultsHeader = "p,searchedMersenne,detailedCheck,passedAllTests";
|
||||
private const string PrimeFoundSuffix = " (FOUND VALID CANDIDATES)";
|
||||
internal const int DefaultWriteBatchSize = 100;
|
||||
|
||||
@@ -95,46 +95,19 @@ internal static class CalculationResultHandler
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void HandleResult(ulong currentP, BigInteger divisor, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
||||
{
|
||||
Span<char> divisorBuffer = stackalloc char[256];
|
||||
if (!divisor.TryFormat(divisorBuffer, out int divisorChars, provider: CultureInfo.InvariantCulture))
|
||||
{
|
||||
string divisorString = divisor.ToString(CultureInfo.InvariantCulture);
|
||||
HandleResultInternal(currentP, divisorString.AsSpan(), searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
|
||||
return;
|
||||
}
|
||||
|
||||
HandleResultInternal(currentP, divisorBuffer[..divisorChars], searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
|
||||
}
|
||||
|
||||
internal static void HandleResult(ulong currentP, ulong divisor, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
||||
{
|
||||
Span<char> divisorBuffer = stackalloc char[32];
|
||||
if (!divisor.TryFormat(divisorBuffer, out int divisorChars, provider: CultureInfo.InvariantCulture))
|
||||
{
|
||||
string divisorString = divisor.ToString(CultureInfo.InvariantCulture);
|
||||
HandleResultInternal(currentP, divisorString.AsSpan(), searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
|
||||
return;
|
||||
}
|
||||
|
||||
HandleResultInternal(currentP, divisorBuffer[..divisorChars], searchedMersenne, detailedCheck, passedAllTests, lastWasComposite);
|
||||
}
|
||||
|
||||
private static void HandleResultInternal(ulong currentP, ReadOnlySpan<char> divisorText, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
||||
internal static void HandleResult(ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests, bool lastWasComposite)
|
||||
{
|
||||
// Program flow ensures Initialize(...) and InitializeOutputBuffer() ran before this call. Do not add initialization guards here.
|
||||
|
||||
Span<byte> stackBuffer = stackalloc byte[256];
|
||||
Span<byte> stackBuffer = stackalloc byte[128];
|
||||
byte[]? rentedBuffer = null;
|
||||
Span<byte> utf8Span = stackBuffer;
|
||||
int byteCount = TryFormatResultLine(utf8Span, currentP, divisorText, searchedMersenne, detailedCheck, passedAllTests);
|
||||
int byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
|
||||
if (byteCount < 0)
|
||||
{
|
||||
int minimumSize = Math.Max((divisorText.Length + 5) * 4, 256);
|
||||
rentedBuffer = ThreadStaticPools.BytePool.Rent(minimumSize);
|
||||
rentedBuffer = ThreadStaticPools.BytePool.Rent(256);
|
||||
utf8Span = rentedBuffer;
|
||||
byteCount = TryFormatResultLine(utf8Span, currentP, divisorText, searchedMersenne, detailedCheck, passedAllTests);
|
||||
byteCount = TryFormatResultLine(utf8Span, currentP, searchedMersenne, detailedCheck, passedAllTests);
|
||||
if (byteCount < 0)
|
||||
{
|
||||
ThreadStaticPools.BytePool.Return(rentedBuffer);
|
||||
@@ -143,7 +116,7 @@ internal static class CalculationResultHandler
|
||||
}
|
||||
|
||||
ReadOnlySpan<byte> utf8Line = utf8Span[..byteCount];
|
||||
Span<char> charBuffer = stackalloc char[256];
|
||||
Span<char> charBuffer = stackalloc char[128];
|
||||
int charCount = Utf8Encoding.GetChars(utf8Line, charBuffer);
|
||||
ReadOnlySpan<char> recordSpan = charBuffer[..charCount];
|
||||
|
||||
@@ -258,7 +231,7 @@ internal static class CalculationResultHandler
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int TryFormatResultLine(Span<byte> destination, ulong currentP, ReadOnlySpan<char> divisorText, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
|
||||
private static int TryFormatResultLine(Span<byte> destination, ulong currentP, bool searchedMersenne, bool detailedCheck, bool passedAllTests)
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
@@ -275,17 +248,6 @@ internal static class CalculationResultHandler
|
||||
|
||||
destination[offset++] = (byte)',';
|
||||
|
||||
int divisorByteCount = Utf8Encoding.GetByteCount(divisorText);
|
||||
if (offset + divisorByteCount >= destination.Length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
divisorByteCount = Utf8Encoding.GetBytes(divisorText, destination[offset..]);
|
||||
offset += divisorByteCount;
|
||||
|
||||
destination[offset++] = (byte)',';
|
||||
|
||||
if (!Utf8Formatter.TryFormat(searchedMersenne, destination[offset..], out written))
|
||||
{
|
||||
return -1;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace EvenPerfectBitScanner.Candidates;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using Open.Numeric.Primes;
|
||||
using PerfectNumbers.Core;
|
||||
using EvenPerfectBitScanner.Candidates.Transforms;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
|
||||
namespace EvenPerfectBitScanner.Candidates;
|
||||
|
||||
@@ -54,7 +54,7 @@ internal static class CandidatesCalculator
|
||||
return trackers.Value!;
|
||||
}
|
||||
|
||||
internal static bool IsCompositeByResiduesCpu(ulong p)
|
||||
internal static bool IsCompositeByResidues(ulong p)
|
||||
{
|
||||
ModResidueTracker tracker = AcquireResidueTracker();
|
||||
// Use ModResidueTracker with a small set of primes to pre-filter composite p.
|
||||
@@ -72,57 +72,7 @@ internal static class CandidatesCalculator
|
||||
break;
|
||||
}
|
||||
|
||||
if (tracker.MergeOrAppendCpu(p, primes[primeIndex], out bool divisible) && divisible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsCompositeByResiduesGpu(PrimeOrderCalculatorAccelerator gpu, ulong p)
|
||||
{
|
||||
ModResidueTracker tracker = AcquireResidueTracker();
|
||||
// Use ModResidueTracker with a small set of primes to pre-filter composite p.
|
||||
tracker.BeginMerge(p);
|
||||
// TODO: Integrate the divisor-cycle cache here so the small-prime sweep reuses precomputed remainders instead of
|
||||
// running MergeOrAppend for every candidate and missing the cycle-accelerated early exits.
|
||||
// Use the small prime list from PerfectNumbers.Core to the extent of sqrt(p)
|
||||
var primes = PrimesGenerator.SmallPrimes;
|
||||
var primesPow2 = PrimesGenerator.SmallPrimesPow2;
|
||||
int len = primes.Length;
|
||||
for (int primeIndex = 0; primeIndex < len; primeIndex++)
|
||||
{
|
||||
if (primesPow2[primeIndex] > p)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (tracker.MergeOrAppendGpu(gpu, p, primes[primeIndex], out bool divisible) && divisible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool IsCompositeByResiduesHybrid(PrimeOrderCalculatorAccelerator gpu, ulong p)
|
||||
{
|
||||
ModResidueTracker tracker = AcquireResidueTracker();
|
||||
tracker.BeginMerge(p);
|
||||
var primes = PrimesGenerator.SmallPrimes;
|
||||
var primesPow2 = PrimesGenerator.SmallPrimesPow2;
|
||||
int len = primes.Length;
|
||||
for (int primeIndex = 0; primeIndex < len; primeIndex++)
|
||||
{
|
||||
if (primesPow2[primeIndex] > p)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (tracker.MergeOrAppendHybrid(gpu, p, primes[primeIndex], out bool divisible) && divisible)
|
||||
if (tracker.MergeOrAppend(p, primes[primeIndex], out bool divisible) && divisible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -179,15 +129,18 @@ internal static class CandidatesCalculator
|
||||
return candidates;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static ulong AdvancePrime(ulong value, ref ulong remainder, ref bool limitReached) => _transformMode switch
|
||||
{
|
||||
PrimeTransformMode.Bit => CandidateBitTransform.Transform(value, ref remainder, ref limitReached),
|
||||
PrimeTransformMode.Add => CandidateAddTransform.Transform(value, ref remainder, ref limitReached),
|
||||
_ => CandidateAddPrimesTransform.Transform(value, ref remainder, ref limitReached),
|
||||
};
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static ulong AdvancePrime(ulong value, ref ulong remainder, ref bool limitReached)
|
||||
{
|
||||
return _transformMode switch
|
||||
{
|
||||
PrimeTransformMode.Bit => CandidateBitTransform.Transform(value, ref remainder, ref limitReached),
|
||||
PrimeTransformMode.Add => CandidateAddTransform.Transform(value, ref remainder, ref limitReached),
|
||||
_ => CandidateAddPrimesTransform.Transform(value, ref remainder, ref limitReached),
|
||||
};
|
||||
}
|
||||
|
||||
internal static int ReserveBlock(ulong[] buffer, int blockSize, ref bool limitReached)
|
||||
internal static int ReserveBlock(ulong[] buffer, int blockSize, ref bool limitReached)
|
||||
{
|
||||
Span<ulong> bufferSpan = new(buffer);
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using EvenPerfectBitScanner.Candidates;
|
||||
using Open.Numeric.Primes;
|
||||
|
||||
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using EvenPerfectBitScanner.Candidates;
|
||||
|
||||
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace EvenPerfectBitScanner.Candidates.Transforms;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using PerfectNumbers.Core;
|
||||
@@ -25,17 +26,14 @@ internal readonly struct CliArguments
|
||||
internal readonly bool UseResidue;
|
||||
internal readonly bool UseDivisor;
|
||||
internal readonly bool UseByDivisor;
|
||||
internal readonly ByDivisorKIncrementMode ByDivisorKIncrementMode;
|
||||
internal readonly int ByDivisorSpecialRange;
|
||||
internal readonly bool UseGcdFilter;
|
||||
internal readonly bool TestMode;
|
||||
internal readonly bool UseGpuCycles;
|
||||
internal readonly ComputationDevice MersenneDevice;
|
||||
internal readonly ComputationDevice OrderDevice;
|
||||
internal readonly bool UseMersenneOnGpu;
|
||||
internal readonly bool UseOrderOnGpu;
|
||||
internal readonly int ScanBatchSize;
|
||||
internal readonly int SliceSize;
|
||||
internal readonly ulong MaxKLimit;
|
||||
internal readonly bool MaxKConfigured;
|
||||
internal readonly ulong ResidueKMax;
|
||||
internal readonly string FilterFile;
|
||||
internal readonly bool ForcePrimeKernelsOnCpu;
|
||||
internal readonly ModReductionMode ModReductionMode;
|
||||
@@ -54,57 +52,50 @@ internal readonly struct CliArguments
|
||||
internal readonly double ZeroFractionHard;
|
||||
internal readonly double ZeroFractionConjecture;
|
||||
internal readonly int MaxZeroConjecture;
|
||||
internal readonly BigInteger MinK;
|
||||
internal readonly int GpuRatio;
|
||||
|
||||
private CliArguments(
|
||||
ulong startPrime,
|
||||
bool startPrimeProvided,
|
||||
UInt128 maxPrimeLimit,
|
||||
bool maxPrimeConfigured,
|
||||
int threadCount,
|
||||
int blockSize,
|
||||
int gpuPrimeThreads,
|
||||
int gpuPrimeBatch,
|
||||
GpuKernelType kernelType,
|
||||
bool useOrder,
|
||||
bool showHelp,
|
||||
bool useBitTransform,
|
||||
bool useLucas,
|
||||
bool useResidue,
|
||||
bool useDivisor,
|
||||
bool useByDivisor,
|
||||
bool useGcdFilter,
|
||||
ByDivisorKIncrementMode byDivisorKIncrementMode,
|
||||
int byDivisorSpecialRange,
|
||||
bool testMode,
|
||||
bool useGpuCycles,
|
||||
ComputationDevice mersenneDevice,
|
||||
ComputationDevice orderDevice,
|
||||
int scanBatchSize,
|
||||
int sliceSize,
|
||||
ulong maxKLimit,
|
||||
bool maxKConfigured,
|
||||
string filterFile,
|
||||
bool forcePrimeKernelsOnCpu,
|
||||
ModReductionMode modReductionMode,
|
||||
NttBackend nttBackend,
|
||||
string? resultsDirectory,
|
||||
string? resultsPrefix,
|
||||
int writeBatchSize,
|
||||
string cyclesPath,
|
||||
bool continueCyclesGeneration,
|
||||
int cyclesBatchSize,
|
||||
ulong divisorCyclesSearchLimit,
|
||||
ulong? orderWarmupLimitOverride,
|
||||
string? rleBlacklistPath,
|
||||
ulong rleHardMaxP,
|
||||
bool rleOnlyLast7,
|
||||
double zeroFractionHard,
|
||||
double zeroFractionConjecture,
|
||||
int maxZeroConjecture,
|
||||
BigInteger minK,
|
||||
int gpuRatio)
|
||||
ulong startPrime,
|
||||
bool startPrimeProvided,
|
||||
UInt128 maxPrimeLimit,
|
||||
bool maxPrimeConfigured,
|
||||
int threadCount,
|
||||
int blockSize,
|
||||
int gpuPrimeThreads,
|
||||
int gpuPrimeBatch,
|
||||
GpuKernelType kernelType,
|
||||
bool useOrder,
|
||||
bool showHelp,
|
||||
bool useBitTransform,
|
||||
bool useLucas,
|
||||
bool useResidue,
|
||||
bool useDivisor,
|
||||
bool useByDivisor,
|
||||
bool useGcdFilter,
|
||||
bool testMode,
|
||||
bool useGpuCycles,
|
||||
bool useMersenneOnGpu,
|
||||
bool useOrderOnGpu,
|
||||
int scanBatchSize,
|
||||
int sliceSize,
|
||||
ulong residueKMax,
|
||||
string filterFile,
|
||||
bool forcePrimeKernelsOnCpu,
|
||||
ModReductionMode modReductionMode,
|
||||
NttBackend nttBackend,
|
||||
string? resultsDirectory,
|
||||
string? resultsPrefix,
|
||||
int writeBatchSize,
|
||||
string cyclesPath,
|
||||
bool continueCyclesGeneration,
|
||||
int cyclesBatchSize,
|
||||
ulong divisorCyclesSearchLimit,
|
||||
ulong? orderWarmupLimitOverride,
|
||||
string? rleBlacklistPath,
|
||||
ulong rleHardMaxP,
|
||||
bool rleOnlyLast7,
|
||||
double zeroFractionHard,
|
||||
double zeroFractionConjecture,
|
||||
int maxZeroConjecture)
|
||||
{
|
||||
StartPrime = startPrime;
|
||||
StartPrimeProvided = startPrimeProvided;
|
||||
@@ -122,17 +113,14 @@ internal readonly struct CliArguments
|
||||
UseResidue = useResidue;
|
||||
UseDivisor = useDivisor;
|
||||
UseByDivisor = useByDivisor;
|
||||
ByDivisorKIncrementMode = byDivisorKIncrementMode;
|
||||
ByDivisorSpecialRange = byDivisorSpecialRange;
|
||||
UseGcdFilter = useGcdFilter;
|
||||
TestMode = testMode;
|
||||
UseGpuCycles = useGpuCycles;
|
||||
MersenneDevice = mersenneDevice;
|
||||
OrderDevice = orderDevice;
|
||||
UseMersenneOnGpu = useMersenneOnGpu;
|
||||
UseOrderOnGpu = useOrderOnGpu;
|
||||
ScanBatchSize = scanBatchSize;
|
||||
SliceSize = sliceSize;
|
||||
MaxKLimit = maxKLimit;
|
||||
MaxKConfigured = maxKConfigured;
|
||||
ResidueKMax = residueKMax;
|
||||
FilterFile = filterFile;
|
||||
ForcePrimeKernelsOnCpu = forcePrimeKernelsOnCpu;
|
||||
ModReductionMode = modReductionMode;
|
||||
@@ -151,8 +139,6 @@ internal readonly struct CliArguments
|
||||
ZeroFractionHard = zeroFractionHard;
|
||||
ZeroFractionConjecture = zeroFractionConjecture;
|
||||
MaxZeroConjecture = maxZeroConjecture;
|
||||
MinK = minK;
|
||||
GpuRatio = gpuRatio;
|
||||
}
|
||||
|
||||
internal bool UseFilter => !TestMode && !string.IsNullOrEmpty(FilterFile);
|
||||
@@ -175,17 +161,14 @@ internal readonly struct CliArguments
|
||||
bool useResidue = false;
|
||||
bool useDivisor = false;
|
||||
bool useByDivisor = false;
|
||||
ByDivisorKIncrementMode byDivisorKIncrementMode = ByDivisorKIncrementMode.Sequential;
|
||||
int byDivisorSpecialRange = 0;
|
||||
bool useGcdFilter = false;
|
||||
bool testMode = false;
|
||||
bool useGpuCycles = true;
|
||||
ComputationDevice mersenneDevice = ComputationDevice.Gpu;
|
||||
ComputationDevice orderDevice = ComputationDevice.Gpu;
|
||||
bool useMersenneOnGpu = true;
|
||||
bool useOrderOnGpu = true;
|
||||
int scanBatchSize = 2_097_152;
|
||||
int sliceSize = 32;
|
||||
ulong maxKLimit = 5_000_000UL;
|
||||
bool maxKConfigured = false;
|
||||
ulong residueKMax = 5_000_000UL;
|
||||
string filterFile = string.Empty;
|
||||
bool forcePrimeKernelsOnCpu = false;
|
||||
ModReductionMode modReductionMode = ModReductionMode.Auto;
|
||||
@@ -204,8 +187,6 @@ internal readonly struct CliArguments
|
||||
double zeroFractionHard = -1.0;
|
||||
double zeroFractionConjecture = -1.0;
|
||||
int maxZeroConjecture = -1;
|
||||
BigInteger minK = BigInteger.One;
|
||||
int gpuRatio = PerfectNumberConstants.GpuRatio;
|
||||
|
||||
foreach (string argument in args)
|
||||
{
|
||||
@@ -256,31 +237,12 @@ internal readonly struct CliArguments
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--gpu-ratio=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
gpuRatio = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--gpu-ratio=".Length)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--threads=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
threadCount = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--threads=".Length)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.Equals("--use-order", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
useOrder = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--max-k=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
maxKLimit = Utf8CliParser.ParseUInt64(argument.AsSpan("--max-k=".Length));
|
||||
maxKConfigured = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--mersenne=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ReadOnlySpan<char> value = argument.AsSpan("--mersenne=".Length);
|
||||
@@ -318,48 +280,51 @@ internal readonly struct CliArguments
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--bydivisor-k-increment=", StringComparison.OrdinalIgnoreCase))
|
||||
if (argument.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ReadOnlySpan<char> value = argument.AsSpan("--bydivisor-k-increment=".Length);
|
||||
if (value.Equals("pow2groups", StringComparison.OrdinalIgnoreCase))
|
||||
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--divisor-cycles-limit=".Length), out ulong parsedLimit))
|
||||
{
|
||||
byDivisorKIncrementMode = ByDivisorKIncrementMode.Pow2Groups;
|
||||
}
|
||||
else if (value.Equals("predictive", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
byDivisorKIncrementMode = ByDivisorKIncrementMode.Predictive;
|
||||
}
|
||||
else if (value.Equals("percentile", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
byDivisorKIncrementMode = ByDivisorKIncrementMode.Percentile;
|
||||
}
|
||||
else if (value.Equals("additive", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
byDivisorKIncrementMode = ByDivisorKIncrementMode.Additive;
|
||||
}
|
||||
else if (value.Equals("topdown", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
byDivisorKIncrementMode = ByDivisorKIncrementMode.TopDown;
|
||||
}
|
||||
else if (value.Equals("bitcontradiction", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
byDivisorKIncrementMode = ByDivisorKIncrementMode.BitContradiction;
|
||||
}
|
||||
else if (value.Equals("bittree", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
byDivisorKIncrementMode = ByDivisorKIncrementMode.BitTree;
|
||||
}
|
||||
else
|
||||
{
|
||||
byDivisorKIncrementMode = ByDivisorKIncrementMode.Sequential;
|
||||
divisorCyclesSearchLimit = parsedLimit;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--bydivisor-special-range=", StringComparison.OrdinalIgnoreCase))
|
||||
if (argument.StartsWith("--residue-max-k=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
byDivisorSpecialRange = Utf8CliParser.ParseInt32(argument.AsSpan("--bydivisor-special-range=".Length));
|
||||
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--residue-max-k=".Length), out ulong parsedResidueMax))
|
||||
{
|
||||
residueKMax = parsedResidueMax;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
useMersenneOnGpu = !argument.AsSpan("--mersenne-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.Equals("--test", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
testMode = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.Equals("--use-order", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
useOrder = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--order-warmup-limit=".Length), out ulong parsedLimit))
|
||||
{
|
||||
orderWarmupLimitOverride = parsedLimit;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -369,36 +334,131 @@ internal readonly struct CliArguments
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.Equals("--test-mode", StringComparison.OrdinalIgnoreCase))
|
||||
if (argument.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
testMode = true;
|
||||
ReadOnlySpan<char> value = argument.AsSpan("--ntt=".Length);
|
||||
nttBackend = value.Equals("staged", StringComparison.OrdinalIgnoreCase) ? NttBackend.Staged : NttBackend.Reference;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--mersenne-device=", StringComparison.OrdinalIgnoreCase))
|
||||
if (argument.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mersenneDevice = ParseDevice(argument.AsSpan("--mersenne-device=".Length));
|
||||
ReadOnlySpan<char> value = argument.AsSpan("--mod-reduction=".Length);
|
||||
if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
modReductionMode = ModReductionMode.Mont64;
|
||||
}
|
||||
else if (value.Equals("barrett128", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
modReductionMode = ModReductionMode.Barrett128;
|
||||
}
|
||||
else if (value.Equals("uint128", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
modReductionMode = ModReductionMode.GpuUInt128;
|
||||
}
|
||||
else
|
||||
{
|
||||
modReductionMode = ModReductionMode.Auto;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase) ||
|
||||
argument.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase) ||
|
||||
argument.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase))
|
||||
if (argument.StartsWith("--primes-device=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ReadOnlySpan<char> value = argument.Contains('=') ? argument.AsSpan(argument.IndexOf('=') + 1) : argument.AsSpan();
|
||||
forcePrimeKernelsOnCpu = value.Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||
forcePrimeKernelsOnCpu = argument.AsSpan("--primes-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--order-device=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
orderDevice = ParseDevice(argument.AsSpan("--order-device=".Length));
|
||||
useOrderOnGpu = !argument.AsSpan("--order-device=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--gpu-prime-batch=", StringComparison.OrdinalIgnoreCase))
|
||||
if (argument.StartsWith("--rle-blacklist=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
gpuPrimeBatch = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--gpu-prime-batch=".Length)));
|
||||
rleBlacklistPath = argument["--rle-blacklist=".Length..];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--rle-hard-max=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Utf8CliParser.TryParseUInt64(argument.AsSpan("--rle-hard-max=".Length), out ulong parsedMax))
|
||||
{
|
||||
rleHardMaxP = parsedMax;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--rle-only-last7=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ReadOnlySpan<char> value = argument.AsSpan("--rle-only-last7=".Length);
|
||||
rleOnlyLast7 = !value.Equals("false", StringComparison.OrdinalIgnoreCase) && !value.Equals("0", StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--zero-hard=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Utf8CliParser.TryParseDouble(argument.AsSpan("--zero-hard=".Length), out double zeroFraction))
|
||||
{
|
||||
zeroFractionHard = zeroFraction;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--zero-conj=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ReadOnlySpan<char> value = argument.AsSpan("--zero-conj=".Length);
|
||||
int colon = value.IndexOf(':');
|
||||
if (colon > 0)
|
||||
{
|
||||
if (Utf8CliParser.TryParseDouble(value[..colon], out double zeroFrac) &&
|
||||
Utf8CliParser.TryParseInt32(value[(colon + 1)..], out int maxZero))
|
||||
{
|
||||
zeroFractionConjecture = zeroFrac;
|
||||
maxZeroConjecture = maxZero;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--lucas=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
useMersenneOnGpu = !argument.AsSpan("--lucas=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--primes=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
forcePrimeKernelsOnCpu = argument.AsSpan("--primes=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--gpu-kernels=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
forcePrimeKernelsOnCpu = argument.AsSpan("--gpu-kernels=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--accelerator=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
forcePrimeKernelsOnCpu = argument.AsSpan("--accelerator=".Length).Equals("cpu", StringComparison.OrdinalIgnoreCase);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--results-dir=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
resultsDirectory = argument["--results-dir=".Length..];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--results-prefix=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
resultsPrefix = argument["--results-prefix=".Length..];
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -408,6 +468,12 @@ internal readonly struct CliArguments
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--gpu-prime-batch=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
gpuPrimeBatch = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--gpu-prime-batch=".Length)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--ll-slice=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sliceSize = Math.Max(1, Utf8CliParser.ParseInt32(argument.AsSpan("--ll-slice=".Length)));
|
||||
@@ -426,54 +492,6 @@ internal readonly struct CliArguments
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--mod-reduction=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ReadOnlySpan<char> value = argument.AsSpan("--mod-reduction=".Length);
|
||||
if (value.Equals("uint128", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
modReductionMode = ModReductionMode.GpuUInt128;
|
||||
}
|
||||
else if (value.Equals("mont64", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
modReductionMode = ModReductionMode.Mont64;
|
||||
}
|
||||
else if (value.Equals("barrett128", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
modReductionMode = ModReductionMode.Barrett128;
|
||||
}
|
||||
else
|
||||
{
|
||||
modReductionMode = ModReductionMode.Auto;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--ntt=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ReadOnlySpan<char> value = argument.AsSpan("--ntt=".Length);
|
||||
nttBackend = value.Equals("staged", StringComparison.OrdinalIgnoreCase) ? NttBackend.Staged : NttBackend.Reference;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--order-warmup-limit=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
orderWarmupLimitOverride = Utf8CliParser.ParseUInt64(argument.AsSpan("--order-warmup-limit=".Length));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--divisor-cycles-limit=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
divisorCyclesSearchLimit = Utf8CliParser.ParseUInt64(argument.AsSpan("--divisor-cycles-limit=".Length));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--min-k=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
minK = BigInteger.Parse(argument.AsSpan("--min-k=".Length), NumberStyles.None, CultureInfo.InvariantCulture);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argument.StartsWith("--filter-p=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
filterFile = argument["--filter-p=".Length..];
|
||||
@@ -507,58 +525,52 @@ internal readonly struct CliArguments
|
||||
if (argument.Equals("--divisor-cycles-continue", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continueCyclesGeneration = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return new CliArguments(
|
||||
startPrime,
|
||||
startPrimeProvided,
|
||||
maxPrimeLimit,
|
||||
maxPrimeConfigured,
|
||||
threadCount,
|
||||
blockSize,
|
||||
gpuPrimeThreads,
|
||||
gpuPrimeBatch,
|
||||
kernelType,
|
||||
useOrder,
|
||||
showHelp,
|
||||
useBitTransform,
|
||||
useLucas,
|
||||
useResidue,
|
||||
useDivisor,
|
||||
useByDivisor,
|
||||
useGcdFilter,
|
||||
byDivisorKIncrementMode,
|
||||
byDivisorSpecialRange,
|
||||
testMode,
|
||||
useGpuCycles,
|
||||
mersenneDevice,
|
||||
orderDevice,
|
||||
scanBatchSize,
|
||||
sliceSize,
|
||||
maxKLimit,
|
||||
maxKConfigured,
|
||||
filterFile,
|
||||
forcePrimeKernelsOnCpu,
|
||||
modReductionMode,
|
||||
nttBackend,
|
||||
resultsDirectory,
|
||||
resultsPrefix,
|
||||
writeBatchSize,
|
||||
cyclesPath,
|
||||
continueCyclesGeneration,
|
||||
cyclesBatchSize,
|
||||
divisorCyclesSearchLimit,
|
||||
orderWarmupLimitOverride,
|
||||
rleBlacklistPath,
|
||||
rleHardMaxP,
|
||||
rleOnlyLast7,
|
||||
zeroFractionHard,
|
||||
zeroFractionConjecture,
|
||||
maxZeroConjecture,
|
||||
minK,
|
||||
gpuRatio);
|
||||
startPrime,
|
||||
startPrimeProvided,
|
||||
maxPrimeLimit,
|
||||
maxPrimeConfigured,
|
||||
threadCount,
|
||||
blockSize,
|
||||
gpuPrimeThreads,
|
||||
gpuPrimeBatch,
|
||||
kernelType,
|
||||
useOrder,
|
||||
showHelp,
|
||||
useBitTransform,
|
||||
useLucas,
|
||||
useResidue,
|
||||
useDivisor,
|
||||
useByDivisor,
|
||||
useGcdFilter,
|
||||
testMode,
|
||||
useGpuCycles,
|
||||
useMersenneOnGpu,
|
||||
useOrderOnGpu,
|
||||
scanBatchSize,
|
||||
sliceSize,
|
||||
residueKMax,
|
||||
filterFile,
|
||||
forcePrimeKernelsOnCpu,
|
||||
modReductionMode,
|
||||
nttBackend,
|
||||
resultsDirectory,
|
||||
resultsPrefix,
|
||||
writeBatchSize,
|
||||
cyclesPath,
|
||||
continueCyclesGeneration,
|
||||
cyclesBatchSize,
|
||||
divisorCyclesSearchLimit,
|
||||
orderWarmupLimitOverride,
|
||||
rleBlacklistPath,
|
||||
rleHardMaxP,
|
||||
rleOnlyLast7,
|
||||
zeroFractionHard,
|
||||
zeroFractionConjecture,
|
||||
maxZeroConjecture);
|
||||
}
|
||||
|
||||
internal static void PrintHelp()
|
||||
@@ -566,25 +578,22 @@ internal readonly struct CliArguments
|
||||
Console.WriteLine("Usage: EvenPerfectBitScanner [options]");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Options:");
|
||||
Console.WriteLine(" --block-size=<value> values processed per thread batch");
|
||||
Console.WriteLine(" --prime=<value> starting exponent (p)");
|
||||
Console.WriteLine(" --max-prime=<value> inclusive upper bound for primes from filter files");
|
||||
Console.WriteLine(" --increment=bit|add exponent increment method (default add)");
|
||||
Console.WriteLine(" --increment=bit|add exponent increment method");
|
||||
Console.WriteLine(" --threads=<value> number of worker threads");
|
||||
Console.WriteLine(" --block-size=<value> values processed per thread batch");
|
||||
Console.WriteLine(" --mersenne=pow2mod|incremental|lucas|residue|divisor|bydivisor Mersenne test method");
|
||||
Console.WriteLine(" --bydivisor-k-increment=sequential|pow2groups|predictive|percentile|additive|topdown|bitcontradiction|bittree k iteration strategy for --mersenne=bydivisor (default sequential)");
|
||||
Console.WriteLine(" --bydivisor-special-range=<n> widen pow2groups special values to +/- n percent (default 0 = points)");
|
||||
Console.WriteLine(" --max-k=<value> max k for Mersenne tests (q = 2*p*k + 1), including --mersenne=bydivisor");
|
||||
Console.WriteLine(" --mersenne-device=cpu|gpu|hybrid Device for Mersenne method (default gpu)");
|
||||
Console.WriteLine(" --primes-device=cpu|gpu device for prime-scan kernels (default gpu)");
|
||||
Console.WriteLine($" --gpu-ratio=<n> ratio of GPU operations vs CPU in hybrid device mode (default {PerfectNumberConstants.GpuRatio})");
|
||||
Console.WriteLine(" --gpu-prime-threads=<value> max concurrent GPU prime checks (default 1)");
|
||||
Console.WriteLine(" --gpu-prime-batch=<n> batch size for GPU primality sieve (default 262144)");
|
||||
Console.WriteLine(" --gpu-scan-batch=<value> GPU q-scan batch size (default 2_097_152)");
|
||||
Console.WriteLine(" --order-device=cpu|gpu|hybrid Device for order computations (default gpu)");
|
||||
Console.WriteLine(" --ll-slice=<value> Lucas–Lehmer iterations per slice (default 32)");
|
||||
Console.WriteLine(" --mod-reduction=auto|uint128|mont64|barrett128 staged NTT reduction (default auto)");
|
||||
Console.WriteLine(" --residue-max-k=<value> max k for residue Mersenne test (q = 2*p*k + 1)");
|
||||
Console.WriteLine(" --mersenne-device=cpu|gpu Device for Mersenne method (default gpu)");
|
||||
Console.WriteLine(" --primes-device=cpu|gpu Device for prime-scan kernels (default gpu)");
|
||||
Console.WriteLine(" --gpu-prime-batch=<n> Batch size for GPU primality sieve (default 262144)");
|
||||
Console.WriteLine(" --order-device=cpu|gpu Device for order computations (default gpu)");
|
||||
Console.WriteLine(" --ntt=reference|staged GPU NTT backend (default staged)");
|
||||
Console.WriteLine(" --mod-reduction=auto|uint128|mont64|barrett128 staged NTT reduction (default auto)");
|
||||
Console.WriteLine(" --gpu-prime-threads=<value> max concurrent GPU prime checks (default 1)");
|
||||
Console.WriteLine(" --ll-slice=<value> Lucas–Lehmer iterations per slice (default 32)");
|
||||
Console.WriteLine(" --gpu-scan-batch=<value> GPU q-scan batch size (default 2_097_152)");
|
||||
Console.WriteLine(" --order-warmup-limit=<value> Warm-up order candidates (default 5_000_000)");
|
||||
Console.WriteLine(" --rle-blacklist=<path> enable RLE blacklist for p (hard filter up to --rle-hard-max)");
|
||||
Console.WriteLine(" --rle-hard-max=<p> apply RLE blacklist only for p <= this (default ulong.MaxValue = no limit)");
|
||||
@@ -602,22 +611,6 @@ internal readonly struct CliArguments
|
||||
Console.WriteLine(" --filter-p=<path> process only p from previous run results (required for --mersenne=bydivisor)");
|
||||
Console.WriteLine(" --write-batch-size=<value> overwrite frequency of disk writes (default 100 lines)");
|
||||
Console.WriteLine(" --gcd-filter enable early sieve based on GCD");
|
||||
Console.WriteLine(" --min-k=<value> starting k for --mersenne=bydivisor divisor scan (default 1)");
|
||||
Console.WriteLine(" --help, -help, --?, -?, /? show this help message");
|
||||
}
|
||||
|
||||
private static ComputationDevice ParseDevice(ReadOnlySpan<char> value)
|
||||
{
|
||||
if (value.Equals("cpu", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ComputationDevice.Cpu;
|
||||
}
|
||||
|
||||
if (value.Equals("hybrid", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ComputationDevice.Hybrid;
|
||||
}
|
||||
|
||||
return ComputationDevice.Gpu;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@@ -13,17 +13,7 @@
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ML" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.ML.FastTree" Version="3.0.1" />
|
||||
<PackageReference Include="Microsoft.ML.LightGbm" Version="3.0.1" />
|
||||
<PackageReference Include="Open.Numeric" Version="3.0.1" />
|
||||
<PackageReference Include="Open.Numeric.Primes" Version="4.0.4" />
|
||||
<PackageReference Include="Plotly.NET" Version="3.0.1" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.54.1-alpha.0.37" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="MLModel1">
|
||||
<None Include="MLModel1.mlnet">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,6 +1,7 @@
|
||||
using Open.Collections;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using EvenPerfectBitScanner;
|
||||
|
||||
namespace EvenPerfectBitScanner.IO;
|
||||
|
||||
@@ -100,7 +101,6 @@ internal static class CalculationResultsFile
|
||||
int first = 0;
|
||||
int second = 0;
|
||||
int third = 0;
|
||||
int fourth = 0;
|
||||
ulong parsedP = 0UL;
|
||||
ReadOnlySpan<char> detailedSpan = default;
|
||||
ReadOnlySpan<char> passedAllTestsSpan = default;
|
||||
@@ -140,15 +140,8 @@ internal static class CalculationResultsFile
|
||||
continue;
|
||||
}
|
||||
|
||||
span = span[(third + 1)..];
|
||||
fourth = span.IndexOf(',');
|
||||
if (fourth < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
detailedSpan = span[..fourth];
|
||||
passedAllTestsSpan = span[(fourth + 1)..];
|
||||
detailedSpan = span[..third];
|
||||
passedAllTestsSpan = span[(third + 1)..];
|
||||
|
||||
if (Utf8CliParser.TryParseBoolean(detailedSpan, out detailed) && Utf8CliParser.TryParseBoolean(passedAllTestsSpan, out passedAllTests))
|
||||
{
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
// This file was auto-generated by ML.NET Model Builder.
|
||||
using Microsoft.ML;
|
||||
using Microsoft.ML.Data;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
namespace EvenPerfectBitScanner
|
||||
{
|
||||
public partial class MLModel1
|
||||
{
|
||||
/// <summary>
|
||||
/// model input class for MLModel1.
|
||||
/// </summary>
|
||||
#region model input class
|
||||
public class ModelInput
|
||||
{
|
||||
[LoadColumn(0)]
|
||||
[ColumnName(@"p")]
|
||||
public float P { get; set; }
|
||||
|
||||
[LoadColumn(1)]
|
||||
[ColumnName(@"divisor")]
|
||||
public float Divisor { get; set; }
|
||||
|
||||
[LoadColumn(2)]
|
||||
[ColumnName(@"lastDigit")]
|
||||
public float LastDigit { get; set; }
|
||||
|
||||
[LoadColumn(3)]
|
||||
[ColumnName(@"k")]
|
||||
public float K { get; set; }
|
||||
|
||||
[LoadColumn(4)]
|
||||
[ColumnName(@"lastMersenneDigit")]
|
||||
public float LastMersenneDigit { get; set; }
|
||||
|
||||
[LoadColumn(5)]
|
||||
[ColumnName(@"isNextKHigher")]
|
||||
public float IsNextKHigher { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// model output class for MLModel1.
|
||||
/// </summary>
|
||||
#region model output class
|
||||
public class ModelOutput
|
||||
{
|
||||
[ColumnName(@"p")]
|
||||
public float P { get; set; }
|
||||
|
||||
[ColumnName(@"divisor")]
|
||||
public float Divisor { get; set; }
|
||||
|
||||
[ColumnName(@"lastDigit")]
|
||||
public float LastDigit { get; set; }
|
||||
|
||||
[ColumnName(@"k")]
|
||||
public float K { get; set; }
|
||||
|
||||
[ColumnName(@"lastMersenneDigit")]
|
||||
public float LastMersenneDigit { get; set; }
|
||||
|
||||
[ColumnName(@"isNextKHigher")]
|
||||
public float IsNextKHigher { get; set; }
|
||||
|
||||
[ColumnName(@"Features")]
|
||||
public float[] Features { get; set; }
|
||||
|
||||
[ColumnName(@"Score")]
|
||||
public float Score { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static string MLNetModelPath = Path.GetFullPath("MLModel1.mlnet");
|
||||
|
||||
public static readonly Lazy<PredictionEngine<ModelInput, ModelOutput>> PredictEngine = new Lazy<PredictionEngine<ModelInput, ModelOutput>>(() => CreatePredictEngine(), true);
|
||||
|
||||
|
||||
private static PredictionEngine<ModelInput, ModelOutput> CreatePredictEngine()
|
||||
{
|
||||
var mlContext = new MLContext();
|
||||
ITransformer mlModel = mlContext.Model.Load(MLNetModelPath, out var _);
|
||||
return mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(mlModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this method to predict on <see cref="ModelInput"/>.
|
||||
/// </summary>
|
||||
/// <param name="input">model input.</param>
|
||||
/// <returns><seealso cref=" ModelOutput"/></returns>
|
||||
public static ModelOutput Predict(ModelInput input)
|
||||
{
|
||||
var predEngine = PredictEngine.Value;
|
||||
return predEngine.Predict(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
// This file was auto-generated by ML.NET Model Builder.
|
||||
|
||||
using Microsoft.ML.Data;
|
||||
using Microsoft.ML;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Plotly.NET;
|
||||
using Plotly.NET.LayoutObjects;
|
||||
|
||||
namespace EvenPerfectBitScanner
|
||||
{
|
||||
public partial class MLModel1
|
||||
{
|
||||
/// <summary>
|
||||
/// Permutation feature importance (PFI) is a technique to determine the importance
|
||||
/// of features in a trained machine learning model. PFI works by taking a labeled dataset,
|
||||
/// choosing a feature, and permuting the values for that feature across all the examples,
|
||||
/// so that each example now has a random value for the feature and the original values for all other features.
|
||||
/// The evaluation metric (e.g. R-squared) is then calculated for this modified dataset,
|
||||
/// and the change in the evaluation metric from the original dataset is computed.
|
||||
/// The larger the change in the evaluation metric, the more important the feature is to the model.
|
||||
///
|
||||
/// PFI typically takes a long time to compute, as the evaluation metric is calculated
|
||||
/// many times to determine the importance of each feature.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="mlContext">The common context for all ML.NET operations.</param>
|
||||
/// <param name="trainData">IDataView used to evaluate the model.</param>
|
||||
/// <param name="model">Model to evaluate.</param>
|
||||
/// <param name="labelColumnName">Label column being predicted.</param>
|
||||
/// <returns>A list of each feature and its importance.</returns>
|
||||
public static List<Tuple<string, double>> CalculatePFI(MLContext mlContext, IDataView trainData, ITransformer model, string labelColumnName)
|
||||
{
|
||||
var preprocessedTrainData = model.Transform(trainData);
|
||||
|
||||
var permutationFeatureImportance =
|
||||
mlContext.Regression
|
||||
.PermutationFeatureImportance(
|
||||
model,
|
||||
preprocessedTrainData,
|
||||
labelColumnName: labelColumnName);
|
||||
|
||||
var featureImportanceMetrics =
|
||||
permutationFeatureImportance
|
||||
.Select((kvp) => new { kvp.Key, kvp.Value.RSquared })
|
||||
.OrderByDescending(myFeatures => Math.Abs(myFeatures.RSquared.Mean));
|
||||
|
||||
var featurePFI = new List<Tuple<string, double>>();
|
||||
foreach (var feature in featureImportanceMetrics)
|
||||
{
|
||||
var pfiValue = Math.Abs(feature.RSquared.Mean);
|
||||
featurePFI.Add(new Tuple<string, double>(feature.Key, pfiValue));
|
||||
}
|
||||
|
||||
return featurePFI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// R Squared is a measure of variation between the values predicted by the model and the true values.
|
||||
/// In a "perfect" model, there would be no variation between predictions and true values.
|
||||
///
|
||||
/// Here we will plot the predicted values vs the true values for the trained model. This RegressionChart.html
|
||||
/// is then saved to the location specified by <param name="folderPath"></param>.
|
||||
///
|
||||
/// See more information on R Squared at https://en.wikipedia.org/wiki/Coefficient_of_determination.
|
||||
/// </summary>
|
||||
/// <param name="trainData">IDataView used to train the model.</param>
|
||||
/// <param name="model">Model used for predictions.</param>
|
||||
/// <param name="labelColumnName">Name of the predicted label column.</param>
|
||||
/// <param name="folderPath">Folder path to save the RegressionChart.html file into.</param>
|
||||
public static void PlotRSquaredValues(IDataView trainData, ITransformer model, string labelColumnName, string folderPath)
|
||||
{
|
||||
// Number of rows to display in charts.
|
||||
int numberOfRows = 1000;
|
||||
// Use the model to make batch predictions on training data
|
||||
var testResults = model.Transform(trainData);
|
||||
|
||||
// Get the actual values from the dataset
|
||||
var trueValues = testResults.GetColumn<float>(labelColumnName).Take(numberOfRows); ;
|
||||
|
||||
// Get the predicted values from the test results
|
||||
var predictedValues = testResults.GetColumn<float>("Score").Take(numberOfRows);
|
||||
|
||||
// Setup what the graph looks like
|
||||
var title = Title.init(Text: "R-Squared Plot");
|
||||
var layout = Layout.init<IConvertible>(Title: title, PlotBGColor: Plotly.NET.Color.fromString("#e5ecf6"));
|
||||
var xAxis = LinearAxis.init<IConvertible, IConvertible, IConvertible, IConvertible, IConvertible, IConvertible>(
|
||||
Title: Title.init("True Values"),
|
||||
ZeroLineColor: Plotly.NET.Color.fromString("#ffff"),
|
||||
GridColor: Plotly.NET.Color.fromString("#ffff"),
|
||||
ZeroLineWidth: 2);
|
||||
var yAxis = LinearAxis.init<IConvertible, IConvertible, IConvertible, IConvertible, IConvertible, IConvertible>(
|
||||
Title: Title.init("Predicted Values"),
|
||||
ZeroLineColor: Plotly.NET.Color.fromString("#ffff"),
|
||||
GridColor: Plotly.NET.Color.fromString("#ffff"),
|
||||
ZeroLineWidth: 2);
|
||||
|
||||
// We will plot the line that shows the perfect result. Setup that line here.
|
||||
var maximumValue = Math.Max(trueValues.Max(), predictedValues.Max());
|
||||
var perfectX = new[] { 0, maximumValue };
|
||||
var perfectY = new[] { 0, maximumValue };
|
||||
|
||||
|
||||
|
||||
// Create the scatterplot that shows the true values vs the predicted values
|
||||
var trueAndPredictedValues = Chart2D.Chart.Scatter<float, float, string>(x: trueValues, y: predictedValues, mode: StyleParam.Mode.Markers)
|
||||
.WithLayout(layout)
|
||||
.WithXAxis(xAxis)
|
||||
.WithYAxis(yAxis);
|
||||
|
||||
// Setup the line that shows what a perfect prediction would look like
|
||||
var perfectLineGraph = Chart2D.Chart.Line<float, float, string>(x: perfectX, y: perfectY)
|
||||
.WithLayout(layout)
|
||||
.WithLine(Line.init(Width: 1.5));
|
||||
|
||||
var chartWithValuesAndIdealLine = Chart.Combine(new[] { trueAndPredictedValues, perfectLineGraph });
|
||||
var chartFilePath = folderPath + "\\RegressionChart.html";
|
||||
|
||||
chartWithValuesAndIdealLine.SaveHtml(chartFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,337 +0,0 @@
|
||||
{
|
||||
"Scenario": "Regression",
|
||||
"DataSource": {
|
||||
"Version": 3,
|
||||
"EscapeCharacter": "\"",
|
||||
"ReadMultiLines": false,
|
||||
"KeepDiacritics": false,
|
||||
"KeepPunctuations": false,
|
||||
"AllowQuoting": false,
|
||||
"Type": "TabularFile",
|
||||
"FilePath": "C:\\Users\\marek\\source\\repos\\even-perfect-numbers-scanner\\BuildDivisorTransitions\\bin\\Release\\net8.0\\NextKForecastTraning.csv",
|
||||
"Delimiter": ",",
|
||||
"DecimalMarker": ".",
|
||||
"HasHeader": true,
|
||||
"ColumnProperties": [
|
||||
{
|
||||
"ColumnName": "p",
|
||||
"ColumnPurpose": "Feature",
|
||||
"ColumnDataFormat": "Single",
|
||||
"IsCategorical": false,
|
||||
"Type": "Column",
|
||||
"Version": 5
|
||||
},
|
||||
{
|
||||
"ColumnName": "divisor",
|
||||
"ColumnPurpose": "Feature",
|
||||
"ColumnDataFormat": "Single",
|
||||
"IsCategorical": false,
|
||||
"Type": "Column",
|
||||
"Version": 5
|
||||
},
|
||||
{
|
||||
"ColumnName": "lastDigit",
|
||||
"ColumnPurpose": "Feature",
|
||||
"ColumnDataFormat": "Single",
|
||||
"IsCategorical": true,
|
||||
"Type": "Column",
|
||||
"Version": 5
|
||||
},
|
||||
{
|
||||
"ColumnName": "k",
|
||||
"ColumnPurpose": "Feature",
|
||||
"ColumnDataFormat": "Single",
|
||||
"IsCategorical": false,
|
||||
"Type": "Column",
|
||||
"Version": 5
|
||||
},
|
||||
{
|
||||
"ColumnName": "lastMersenneDigit",
|
||||
"ColumnPurpose": "Feature",
|
||||
"ColumnDataFormat": "Single",
|
||||
"IsCategorical": true,
|
||||
"Type": "Column",
|
||||
"Version": 5
|
||||
},
|
||||
{
|
||||
"ColumnName": "isNextKHigher",
|
||||
"ColumnPurpose": "Label",
|
||||
"ColumnDataFormat": "Single",
|
||||
"IsCategorical": true,
|
||||
"Type": "Column",
|
||||
"Version": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
"Environment": {
|
||||
"Type": "LocalCPU",
|
||||
"Version": 1
|
||||
},
|
||||
"RunHistory": {
|
||||
"Version": 3,
|
||||
"Type": "Result",
|
||||
"Trials": [
|
||||
{
|
||||
"Version": 1,
|
||||
"Type": "Trial",
|
||||
"TrainerName": "FastTreeRegression",
|
||||
"Score": 0.30006118613190746,
|
||||
"RuntimeInSeconds": 1.489,
|
||||
"Parameter": {
|
||||
"_SCHEMA_": "e0 * e1 * e2",
|
||||
"e0": {
|
||||
"OutputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
]
|
||||
},
|
||||
"e1": {
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"OutputColumnName": "Features"
|
||||
},
|
||||
"e2": {
|
||||
"NumberOfLeaves": 4,
|
||||
"MinimumExampleCountPerLeaf": 9,
|
||||
"NumberOfTrees": 8,
|
||||
"MaximumBinCountPerFeature": 317,
|
||||
"FeatureFraction": 0.98434331463123226,
|
||||
"LearningRate": 0.3857390791447961,
|
||||
"LabelColumnName": "isNextKHigher",
|
||||
"FeatureColumnName": "Features",
|
||||
"DiskTranspose": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Version": 1,
|
||||
"Type": "Trial",
|
||||
"TrainerName": "FastTreeRegression",
|
||||
"Score": 0.299969238277351,
|
||||
"RuntimeInSeconds": 1.496,
|
||||
"Parameter": {
|
||||
"_SCHEMA_": "e0 * e1 * e2",
|
||||
"e0": {
|
||||
"OutputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
]
|
||||
},
|
||||
"e1": {
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"OutputColumnName": "Features"
|
||||
},
|
||||
"e2": {
|
||||
"NumberOfLeaves": 4,
|
||||
"MinimumExampleCountPerLeaf": 9,
|
||||
"NumberOfTrees": 8,
|
||||
"MaximumBinCountPerFeature": 317,
|
||||
"FeatureFraction": 0.98444606904931908,
|
||||
"LearningRate": 0.38567641892007121,
|
||||
"LabelColumnName": "isNextKHigher",
|
||||
"FeatureColumnName": "Features",
|
||||
"DiskTranspose": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Version": 1,
|
||||
"Type": "Trial",
|
||||
"TrainerName": "FastTreeRegression",
|
||||
"Score": 0.29992424440705012,
|
||||
"RuntimeInSeconds": 0.993,
|
||||
"Parameter": {
|
||||
"_SCHEMA_": "e0 * e1 * e2",
|
||||
"e0": {
|
||||
"OutputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
]
|
||||
},
|
||||
"e1": {
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"OutputColumnName": "Features"
|
||||
},
|
||||
"e2": {
|
||||
"NumberOfLeaves": 4,
|
||||
"MinimumExampleCountPerLeaf": 9,
|
||||
"NumberOfTrees": 8,
|
||||
"MaximumBinCountPerFeature": 317,
|
||||
"FeatureFraction": 0.98445819026817927,
|
||||
"LearningRate": 0.38613186537816252,
|
||||
"LabelColumnName": "isNextKHigher",
|
||||
"FeatureColumnName": "Features",
|
||||
"DiskTranspose": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Version": 1,
|
||||
"Type": "Trial",
|
||||
"TrainerName": "FastTreeRegression",
|
||||
"Score": 0.29992046748552242,
|
||||
"RuntimeInSeconds": 1.389,
|
||||
"Parameter": {
|
||||
"_SCHEMA_": "e0 * e1 * e2",
|
||||
"e0": {
|
||||
"OutputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
]
|
||||
},
|
||||
"e1": {
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"OutputColumnName": "Features"
|
||||
},
|
||||
"e2": {
|
||||
"NumberOfLeaves": 4,
|
||||
"MinimumExampleCountPerLeaf": 9,
|
||||
"NumberOfTrees": 8,
|
||||
"MaximumBinCountPerFeature": 317,
|
||||
"FeatureFraction": 0.984395476164661,
|
||||
"LearningRate": 0.3858463322510558,
|
||||
"LabelColumnName": "isNextKHigher",
|
||||
"FeatureColumnName": "Features",
|
||||
"DiskTranspose": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Version": 1,
|
||||
"Type": "Trial",
|
||||
"TrainerName": "FastTreeRegression",
|
||||
"Score": 0.29984566377505206,
|
||||
"RuntimeInSeconds": 1.512,
|
||||
"Parameter": {
|
||||
"_SCHEMA_": "e0 * e1 * e2",
|
||||
"e0": {
|
||||
"OutputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
]
|
||||
},
|
||||
"e1": {
|
||||
"InputColumnNames": [
|
||||
"p",
|
||||
"divisor",
|
||||
"lastDigit",
|
||||
"k",
|
||||
"lastMersenneDigit"
|
||||
],
|
||||
"OutputColumnName": "Features"
|
||||
},
|
||||
"e2": {
|
||||
"NumberOfLeaves": 4,
|
||||
"MinimumExampleCountPerLeaf": 8,
|
||||
"NumberOfTrees": 8,
|
||||
"MaximumBinCountPerFeature": 317,
|
||||
"FeatureFraction": 0.98439134546037266,
|
||||
"LearningRate": 0.38521733136295422,
|
||||
"LabelColumnName": "isNextKHigher",
|
||||
"FeatureColumnName": "Features",
|
||||
"DiskTranspose": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Estimators": {
|
||||
"e0": "ReplaceMissingValues",
|
||||
"e1": "Concatenate",
|
||||
"e2": "FastTreeRegression",
|
||||
"e3": "FastForestRegression",
|
||||
"e4": "LightGbmRegression",
|
||||
"e5": "LbfgsPoissonRegressionRegression",
|
||||
"e6": "SdcaRegression"
|
||||
},
|
||||
"Schema": "e0 * e1 * (e2 + e3 + e4 + e5 + e6)",
|
||||
"MetricName": "RSquared",
|
||||
"ModelFilePath": "C:\\Users\\marek\\source\\repos\\even-perfect-numbers-scanner\\EvenPerfectBitScanner\\MLModel1.mlnet"
|
||||
},
|
||||
"Type": "TrainingConfig",
|
||||
"Version": 5,
|
||||
"TrainingOption": {
|
||||
"Version": 2,
|
||||
"Subsampling": false,
|
||||
"Type": "RegressionTrainingOption",
|
||||
"TrainingTime": 3600,
|
||||
"LabelColumn": "isNextKHigher",
|
||||
"ValidationOption": {
|
||||
"Version": 0,
|
||||
"Type": "CrossValidationValidationOption",
|
||||
"NumberOfFolds": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,100 +0,0 @@
|
||||
// This file was auto-generated by ML.NET Model Builder.
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.ML;
|
||||
using Microsoft.ML.Data;
|
||||
using Microsoft.ML.Trainers;
|
||||
using Microsoft.ML.Trainers.FastTree;
|
||||
|
||||
namespace EvenPerfectBitScanner
|
||||
{
|
||||
public partial class MLModel1
|
||||
{
|
||||
public const string RetrainFilePath = @"C:\Users\marek\source\repos\even-perfect-numbers-scanner\BuildDivisorTransitions\bin\Release\net8.0\NextKForecastTraning.csv";
|
||||
public const char RetrainSeparatorChar = ',';
|
||||
public const bool RetrainHasHeader = true;
|
||||
public const bool RetrainAllowQuoting = false;
|
||||
|
||||
/// <summary>
|
||||
/// Train a new model with the provided dataset.
|
||||
/// </summary>
|
||||
/// <param name="outputModelPath">File path for saving the model. Should be similar to "C:\YourPath\ModelName.mlnet"</param>
|
||||
/// <param name="inputDataFilePath">Path to the data file for training.</param>
|
||||
/// <param name="separatorChar">Separator character for delimited training file.</param>
|
||||
/// <param name="hasHeader">Boolean if training file has a header.</param>
|
||||
public static void Train(string outputModelPath, string inputDataFilePath = RetrainFilePath, char separatorChar = RetrainSeparatorChar, bool hasHeader = RetrainHasHeader, bool allowQuoting = RetrainAllowQuoting)
|
||||
{
|
||||
var mlContext = new MLContext();
|
||||
|
||||
var data = LoadIDataViewFromFile(mlContext, inputDataFilePath, separatorChar, hasHeader, allowQuoting);
|
||||
var model = RetrainModel(mlContext, data);
|
||||
SaveModel(mlContext, model, data, outputModelPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load an IDataView from a file path.
|
||||
/// </summary>
|
||||
/// <param name="mlContext">The common context for all ML.NET operations.</param>
|
||||
/// <param name="inputDataFilePath">Path to the data file for training.</param>
|
||||
/// <param name="separatorChar">Separator character for delimited training file.</param>
|
||||
/// <param name="hasHeader">Boolean if training file has a header.</param>
|
||||
/// <returns>IDataView with loaded training data.</returns>
|
||||
public static IDataView LoadIDataViewFromFile(MLContext mlContext, string inputDataFilePath, char separatorChar, bool hasHeader, bool allowQuoting)
|
||||
{
|
||||
return mlContext.Data.LoadFromTextFile<ModelInput>(inputDataFilePath, separatorChar, hasHeader, allowQuoting: allowQuoting);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Save a model at the specified path.
|
||||
/// </summary>
|
||||
/// <param name="mlContext">The common context for all ML.NET operations.</param>
|
||||
/// <param name="model">Model to save.</param>
|
||||
/// <param name="data">IDataView used to train the model.</param>
|
||||
/// <param name="modelSavePath">File path for saving the model. Should be similar to "C:\YourPath\ModelName.mlnet.</param>
|
||||
public static void SaveModel(MLContext mlContext, ITransformer model, IDataView data, string modelSavePath)
|
||||
{
|
||||
// Pull the data schema from the IDataView used for training the model
|
||||
DataViewSchema dataViewSchema = data.Schema;
|
||||
|
||||
using (var fs = File.Create(modelSavePath))
|
||||
{
|
||||
mlContext.Model.Save(model, dataViewSchema, fs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrain model using the pipeline generated as part of the training process.
|
||||
/// </summary>
|
||||
/// <param name="mlContext"></param>
|
||||
/// <param name="trainData"></param>
|
||||
/// <returns></returns>
|
||||
public static ITransformer RetrainModel(MLContext mlContext, IDataView trainData)
|
||||
{
|
||||
var pipeline = BuildPipeline(mlContext);
|
||||
var model = pipeline.Fit(trainData);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// build the pipeline that is used from model builder. Use this function to retrain model.
|
||||
/// </summary>
|
||||
/// <param name="mlContext"></param>
|
||||
/// <returns></returns>
|
||||
public static IEstimator<ITransformer> BuildPipeline(MLContext mlContext)
|
||||
{
|
||||
// Data process configuration with pipeline data transformations
|
||||
var pipeline = mlContext.Transforms.ReplaceMissingValues(new []{new InputOutputColumnPair(@"p", @"p"),new InputOutputColumnPair(@"divisor", @"divisor"),new InputOutputColumnPair(@"lastDigit", @"lastDigit"),new InputOutputColumnPair(@"k", @"k"),new InputOutputColumnPair(@"lastMersenneDigit", @"lastMersenneDigit")})
|
||||
.Append(mlContext.Transforms.Concatenate(@"Features", new []{@"p",@"divisor",@"lastDigit",@"k",@"lastMersenneDigit"}))
|
||||
.Append(mlContext.Regression.Trainers.FastTree(new FastTreeRegressionTrainer.Options(){NumberOfLeaves=4,MinimumExampleCountPerLeaf=9,NumberOfTrees=8,MaximumBinCountPerFeature=317,FeatureFraction=0.9843433146312323,LearningRate=0.3857390791447961,LabelColumnName=@"isNextKHigher",FeatureColumnName=@"Features",DiskTranspose=false}));
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Text;
|
||||
using System.Text;
|
||||
@@ -29,26 +30,11 @@ internal static class Utf8CliParser
|
||||
return result;
|
||||
}
|
||||
|
||||
public static uint ParseUInt32(ReadOnlySpan<char> value)
|
||||
{
|
||||
if (!TryParseUInt32(value, out uint result))
|
||||
{
|
||||
ThrowFormatException(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool TryParseUInt64(ReadOnlySpan<char> value, out ulong result)
|
||||
{
|
||||
return TryParse(value, static (ReadOnlySpan<byte> source, out ulong parsed, out int consumed) => Utf8Parser.TryParse(source, out parsed, out consumed), out result);
|
||||
}
|
||||
|
||||
public static bool TryParseUInt32(ReadOnlySpan<char> value, out uint result)
|
||||
{
|
||||
return TryParse(value, static (ReadOnlySpan<byte> source, out uint parsed, out int consumed) => Utf8Parser.TryParse(source, out parsed, out consumed), out result);
|
||||
}
|
||||
|
||||
public static bool TryParseInt32(ReadOnlySpan<char> value, out int result)
|
||||
{
|
||||
return TryParse(value, static (ReadOnlySpan<byte> source, out int parsed, out int consumed) => Utf8Parser.TryParse(source, out parsed, out consumed), out result);
|
||||
|
||||
+27
-122
@@ -1,5 +1,4 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
@@ -15,132 +14,38 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EvenPerfectBitScanner.Resul
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EvenPerfectBitScanner.Benchmarks", "EvenPerfectBitScanner.Benchmarks\EvenPerfectBitScanner.Benchmarks.csproj", "{83551FCC-41FA-4162-B541-AFCB5666A29F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildDivisorTransitions", "BuildDivisorTransitions\BuildDivisorTransitions.csproj", "{0A936837-5082-4C0B-AC3B-840AD2E11057}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceDependentGenerator", "DeviceDependentGenerator\DeviceDependentGenerator.csproj", "{E900CE3B-0DA4-4D7A-B825-851D6227ED67}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceDependentGenerator.Tests", "DeviceDependentGenerator.Tests\DeviceDependentGenerator.Tests.csproj", "{2C632996-9606-4CB5-B046-38AB91D6F6DC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Release|x86.Build.0 = Release|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0A936837-5082-4C0B-AC3B-840AD2E11057}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E900CE3B-0DA4-4D7A-B825-851D6227ED67}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2C632996-9606-4CB5-B046-38AB91D6F6DC}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FFD557C9-12AE-4D8D-A21A-44CEE197BFB6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA4063F7-CB3C-4A66-BE70-4C6535D90197}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{553D1735-214B-4097-B647-D73DB2CB9ADA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{65ED9CC9-BB94-4FA5-940E-B84B3EE87DA7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B07A718C-A71C-410D-A0D7-FA1674CEB38F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{83551FCC-41FA-4162-B541-AFCB5666A29F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,89 +0,0 @@
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.ByDivisor;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
[Trait("Category", "Fast")]
|
||||
public class BitContradictionIntegrationTests
|
||||
{
|
||||
// 2^11 - 1 = 2047 = 23 * 89
|
||||
[Fact]
|
||||
public void BitContradictionAcceptsKnownDivisorWithDecision()
|
||||
{
|
||||
BigInteger divisor = 23;
|
||||
ulong p = 11;
|
||||
bool decided = Invoke(divisor, p, out bool divides);
|
||||
decided.Should().BeTrue("small p should be decided exactly");
|
||||
divides.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitContradictionRejectsEvenDivisorQuickly()
|
||||
{
|
||||
BigInteger divisor = 24;
|
||||
ulong p = 11;
|
||||
bool decided = Invoke(divisor, p, out bool divides);
|
||||
decided.Should().BeTrue();
|
||||
divides.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitContradictionRejectsTooLongDivisor()
|
||||
{
|
||||
// q bit length >= p => impossible
|
||||
BigInteger divisor = (BigInteger.One << 15) + 1;
|
||||
ulong p = 11;
|
||||
bool decided = Invoke(divisor, p, out bool divides);
|
||||
decided.Should().BeTrue();
|
||||
divides.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitContradictionAcceptsKnownLargeDivisor()
|
||||
{
|
||||
BigInteger divisor = BigInteger.Parse("1209708008767");
|
||||
ulong p = 138000001;
|
||||
bool decided = Invoke(divisor, p, out bool divides);
|
||||
decided.Should().BeTrue();
|
||||
divides.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitContradictionRejectsNonDivisorWithDecision()
|
||||
{
|
||||
// 2^11 - 1 is not divisible by 45 (k=2, q=22*2+1).
|
||||
BigInteger divisor = 45;
|
||||
ulong p = 11;
|
||||
bool decided = Invoke(divisor, p, out bool divides);
|
||||
decided.Should().BeTrue("small p should be decided exactly");
|
||||
divides.Should().BeFalse();
|
||||
}
|
||||
|
||||
private static bool Invoke(BigInteger divisor, ulong p, out bool divides)
|
||||
{
|
||||
int bitLen = GetBitLength(divisor);
|
||||
Span<int> offsets = stackalloc int[bitLen];
|
||||
int count = 0;
|
||||
for (int i = 0; i < bitLen; i++)
|
||||
{
|
||||
if (((divisor >> i) & BigInteger.One) == BigInteger.One)
|
||||
{
|
||||
offsets[count++] = i;
|
||||
}
|
||||
}
|
||||
|
||||
return BitContradictionSolverWithAMultiplier.TryCheckDivisibilityFromOneOffsets(offsets[..count], p, out divides);
|
||||
}
|
||||
|
||||
private static int GetBitLength(BigInteger value)
|
||||
{
|
||||
if (value.IsZero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)Math.Floor(BigInteger.Log(value, 2.0)) + 1;
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Kernels;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class BitContradictionKernelsTests
|
||||
{
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(31UL, 0UL, 0)]
|
||||
[InlineData(31UL, 0UL, 5)]
|
||||
[InlineData(61UL, 7UL, 3)]
|
||||
[InlineData(36721UL, 1UL, 0)]
|
||||
public void TryBuildQWordsFromBatchIndex_matches_BigInteger(ulong exponent, ulong batchIndex, int batchOffset)
|
||||
{
|
||||
int wordCount = BitContradictionKernels.MaxQWordCount;
|
||||
ulong[] batchIndexWords = new ulong[wordCount];
|
||||
batchIndexWords[0] = batchIndex;
|
||||
|
||||
ulong[] qWords = new ulong[wordCount];
|
||||
bool ok = BitContradictionKernels.TryBuildQWordsFromBatchIndex(
|
||||
exponent,
|
||||
batchIndexWords,
|
||||
wordCount,
|
||||
batchOffset,
|
||||
qWords,
|
||||
out int qBitLen);
|
||||
|
||||
ok.Should().BeTrue();
|
||||
|
||||
BigInteger expectedK = ((BigInteger)batchIndex * BitContradictionKernels.BatchCount) + BigInteger.One + batchOffset;
|
||||
BigInteger expectedQ = (expectedK * (exponent << 1)) + BigInteger.One;
|
||||
BigInteger actualQ = ReadBigIntegerFromWords(qWords);
|
||||
|
||||
actualQ.Should().Be(expectedQ);
|
||||
qBitLen.Should().Be(GetBitLength(expectedQ));
|
||||
}
|
||||
|
||||
private static BigInteger ReadBigIntegerFromWords(ReadOnlySpan<ulong> words)
|
||||
{
|
||||
byte[] bytes = new byte[words.Length * sizeof(ulong)];
|
||||
for (int w = 0; w < words.Length; w++)
|
||||
{
|
||||
ulong word = words[w];
|
||||
int byteIndex = w << 3;
|
||||
bytes[byteIndex] = (byte)word;
|
||||
bytes[byteIndex + 1] = (byte)(word >> 8);
|
||||
bytes[byteIndex + 2] = (byte)(word >> 16);
|
||||
bytes[byteIndex + 3] = (byte)(word >> 24);
|
||||
bytes[byteIndex + 4] = (byte)(word >> 32);
|
||||
bytes[byteIndex + 5] = (byte)(word >> 40);
|
||||
bytes[byteIndex + 6] = (byte)(word >> 48);
|
||||
bytes[byteIndex + 7] = (byte)(word >> 56);
|
||||
}
|
||||
|
||||
return new BigInteger(bytes, isUnsigned: true, isBigEndian: false);
|
||||
}
|
||||
|
||||
private static int GetBitLength(BigInteger value)
|
||||
{
|
||||
if (value.IsZero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte[] bytes = value.ToByteArray(isUnsigned: true, isBigEndian: false);
|
||||
int last = bytes[^1];
|
||||
int bits = (bytes.Length - 1) * 8;
|
||||
while (last != 0)
|
||||
{
|
||||
bits++;
|
||||
last >>= 1;
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.ByDivisor;
|
||||
using Xunit;
|
||||
using static PerfectNumbers.Core.ByDivisor.BitContradictionSolverWithAMultiplier;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class BitContradictionSolverTests
|
||||
{
|
||||
[Fact]
|
||||
public void ComputeColumnBounds_TracksForcedAndPossibleOnes()
|
||||
{
|
||||
bool[] a = [true, false];
|
||||
bool[] q = [true, true];
|
||||
|
||||
ColumnBounds bounds = ComputeColumnBounds(a, q, 1);
|
||||
|
||||
bounds.ForcedOnes.Should().Be(1);
|
||||
bounds.PossibleOnes.Should().Be(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPropagateCarry_FailsWhenParityUnreachable()
|
||||
{
|
||||
CarryRange carry = CarryRange.Single(0);
|
||||
bool success = TryPropagateCarry(
|
||||
currentCarry: ref carry,
|
||||
forcedOnes: 0,
|
||||
possibleOnes: 0,
|
||||
requiredResultBit: 1);
|
||||
|
||||
success.Should().BeFalse();
|
||||
carry.Min.Should().Be(0);
|
||||
carry.Max.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ShouldPruneWithFinalPow2Shift_ReturnsTrue_WhenShiftedResidueStaysBelowModulusAndResidueIsNotOne()
|
||||
{
|
||||
bool shouldPrune = ShouldPruneWithFinalPow2Shift(
|
||||
prefixResidue: new BigInteger(3),
|
||||
modulus: new BigInteger(1000),
|
||||
tailShift: 8);
|
||||
|
||||
shouldPrune.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ShouldPruneWithFinalPow2Shift_ReturnsFalse_WhenResidueIsOne()
|
||||
{
|
||||
bool shouldPrune = ShouldPruneWithFinalPow2Shift(
|
||||
prefixResidue: BigInteger.One,
|
||||
modulus: new BigInteger(1000),
|
||||
tailShift: 8);
|
||||
|
||||
shouldPrune.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPropagateCarry_ComputesNextCarryRange()
|
||||
{
|
||||
CarryRange carry = new(0, 1);
|
||||
bool success = TryPropagateCarry(
|
||||
currentCarry: ref carry,
|
||||
forcedOnes: 1,
|
||||
possibleOnes: 3,
|
||||
requiredResultBit: 1);
|
||||
|
||||
success.Should().BeTrue();
|
||||
carry.Min.Should().Be(0);
|
||||
carry.Max.Should().Be(1);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class BitTreeSolverTests
|
||||
{
|
||||
[Fact]
|
||||
public void BitTree_FindsKnownDivisor()
|
||||
{
|
||||
// 2^11-1 = 2047 = 23 * 89, so k=1 yields divisor 23.
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForCpuOrder
|
||||
{
|
||||
DivisorLimit = new BigInteger(1000),
|
||||
MinK = BigInteger.One,
|
||||
};
|
||||
|
||||
bool isPrime = tester.IsPrime(11, out bool exhausted, out BigInteger divisor);
|
||||
|
||||
isPrime.Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().Be(23);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BitTree_ReportsPrimeWhenNoDivisorFound()
|
||||
{
|
||||
// 2^5-1 = 31 is prime.
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTesterWithForBitTreeDivisorSetForCpuOrder
|
||||
{
|
||||
DivisorLimit = new BigInteger(10_000),
|
||||
MinK = BigInteger.One,
|
||||
};
|
||||
|
||||
bool isPrime = tester.IsPrime(5, out bool exhausted, out BigInteger divisor);
|
||||
|
||||
isPrime.Should().BeTrue();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().Be(BigInteger.Zero);
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.ByDivisor;
|
||||
using Xunit;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public sealed class ByDivisorClassModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void LoadOrBuild_builds_model_from_results_file()
|
||||
{
|
||||
string tempPath = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(
|
||||
tempPath,
|
||||
new[]
|
||||
{
|
||||
"p,divisor,searchedMersenne,detailedCheck,passedAllTests",
|
||||
"5,31,true,true,false",
|
||||
"5,0,true,true,true",
|
||||
"13,8191,true,true,false",
|
||||
});
|
||||
|
||||
string modelPath = tempPath + ".model";
|
||||
ByDivisorScanTuning tuning = new()
|
||||
{
|
||||
CheapLimit = 32,
|
||||
};
|
||||
|
||||
ByDivisorClassModel model = ByDivisorClassModelLoader.LoadOrBuild(modelPath, tempPath, tuning, tuning.CheapLimit);
|
||||
|
||||
ByDivisorClassEntry classFive = model.Entries[5];
|
||||
classFive.K50.Should().Be(3d);
|
||||
classFive.K75.Should().Be(3d);
|
||||
classFive.GapProbability.Should().BeApproximately(0.5d, 1e-12);
|
||||
classFive.SampleCount.Should().Be(2);
|
||||
classFive.GapCount.Should().Be(1);
|
||||
|
||||
ByDivisorClassEntry classThirteen = model.Entries[13];
|
||||
classThirteen.K50.Should().Be(315d);
|
||||
classThirteen.K75.Should().Be(315d);
|
||||
classThirteen.GapProbability.Should().Be(0d);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryDelete(tempPath);
|
||||
TryDelete(tempPath + ".model");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildPlan_uses_aggressive_tuning_for_hard_class()
|
||||
{
|
||||
var model = new ByDivisorClassModel
|
||||
{
|
||||
CheapLimit = 50,
|
||||
};
|
||||
model.Entries[0] = new ByDivisorClassEntry
|
||||
{
|
||||
K50 = 1000d,
|
||||
K75 = 2000d,
|
||||
GapProbability = 0.8d,
|
||||
Stability = "escaping",
|
||||
};
|
||||
|
||||
ByDivisorScanTuning tuning = new()
|
||||
{
|
||||
CheapLimit = 50,
|
||||
Gamma50 = 0.8d,
|
||||
Gamma75 = 1.2d,
|
||||
AggressiveGamma50 = 1.0d,
|
||||
AggressiveGamma75 = 1.4d,
|
||||
Rho1 = 1.35d,
|
||||
Rho2 = 1.6d,
|
||||
AggressiveRho2 = 1.8d,
|
||||
HardGapThreshold = 0.75d,
|
||||
};
|
||||
|
||||
ByDivisorScanPlan plan = model.BuildPlan(2UL, BigInteger.Zero, tuning);
|
||||
|
||||
plan.CheapLimit.Should().Be(50d);
|
||||
plan.Start.Should().Be(1000d);
|
||||
plan.Target.Should().Be(2800d);
|
||||
plan.Rho1.Should().Be(1.35d);
|
||||
plan.Rho2.Should().Be(1.8d);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildPlan_exposes_percentiles_and_additive_delta()
|
||||
{
|
||||
var model = new ByDivisorClassModel
|
||||
{
|
||||
CheapLimit = 10_000,
|
||||
};
|
||||
var entries = new ByDivisorClassEntry[1024];
|
||||
entries[0] = new ByDivisorClassEntry
|
||||
{
|
||||
K10 = 10d,
|
||||
K25 = 20d,
|
||||
K50 = 30d,
|
||||
K75 = 40d,
|
||||
GapProbability = 0.1d,
|
||||
Stability = "stable",
|
||||
};
|
||||
model.Entries = entries;
|
||||
|
||||
ByDivisorScanTuning tuning = ByDivisorScanTuning.Default;
|
||||
|
||||
ByDivisorScanPlan plan = model.BuildPlan(0UL, BigInteger.Zero, tuning);
|
||||
|
||||
plan.K10.Should().Be(10d);
|
||||
plan.K25.Should().Be(20d);
|
||||
plan.K50.Should().Be(30d);
|
||||
plan.K75.Should().Be(40d);
|
||||
plan.Start.Should().Be(24d); // ceil(0.8 * 30)
|
||||
plan.Target.Should().Be(48d); // ceil(1.2 * 40)
|
||||
plan.DeltaK.Should().Be(4d); // ceil(24 * 0.15)
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildPlan_clamps_all_ranges_to_allowed_max()
|
||||
{
|
||||
var model = new ByDivisorClassModel
|
||||
{
|
||||
CheapLimit = 1000d,
|
||||
};
|
||||
var entries = new ByDivisorClassEntry[1024];
|
||||
entries[0] = new ByDivisorClassEntry
|
||||
{
|
||||
K10 = 50d,
|
||||
K25 = 60d,
|
||||
K50 = 70d,
|
||||
K75 = 80d,
|
||||
GapProbability = 0.1d,
|
||||
};
|
||||
model.Entries = entries;
|
||||
|
||||
ByDivisorScanTuning tuning = ByDivisorScanTuning.Default;
|
||||
// allowedMax = 41 => maxKAllowed = (41-1)/(2*5) = 4
|
||||
ByDivisorScanPlan plan = model.BuildPlan(5UL, new BigInteger(41), tuning);
|
||||
|
||||
plan.CheapLimit.Should().Be(4d);
|
||||
plan.K10.Should().Be(4d);
|
||||
plan.K25.Should().Be(4d);
|
||||
plan.K50.Should().Be(4d);
|
||||
plan.K75.Should().Be(4d);
|
||||
plan.Start.Should().Be(4d);
|
||||
plan.Target.Should().Be(4d);
|
||||
plan.DeltaK.Should().Be(4d);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoadOrBuild_rebuilds_when_results_newer_than_model()
|
||||
{
|
||||
string tempResults = Path.GetTempFileName();
|
||||
string tempModel = tempResults + ".model";
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(
|
||||
tempResults,
|
||||
new[]
|
||||
{
|
||||
"p,divisor,searchedMersenne,detailedCheck,passedAllTests",
|
||||
"5,31,true,true,false",
|
||||
});
|
||||
|
||||
// Write a stale model
|
||||
var staleModel = new ByDivisorClassModel
|
||||
{
|
||||
CheapLimit = 10,
|
||||
};
|
||||
File.WriteAllText(tempModel, JsonSerializer.Serialize(staleModel));
|
||||
File.SetLastWriteTimeUtc(tempModel, DateTime.UtcNow.AddMinutes(-10));
|
||||
File.SetLastWriteTimeUtc(tempResults, DateTime.UtcNow);
|
||||
|
||||
ByDivisorScanTuning tuning = new()
|
||||
{
|
||||
CheapLimit = 32,
|
||||
};
|
||||
|
||||
ByDivisorClassModel model = ByDivisorClassModelLoader.LoadOrBuild(tempModel, tempResults, tuning, tuning.CheapLimit);
|
||||
|
||||
model.Entries[5].K50.Should().Be(3d);
|
||||
File.GetLastWriteTimeUtc(tempModel).Should().BeAfter(File.GetLastWriteTimeUtc(tempResults).AddSeconds(-2));
|
||||
}
|
||||
finally
|
||||
{
|
||||
TryDelete(tempResults);
|
||||
TryDelete(tempModel);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryDelete(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore cleanup failure in tests
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using ILGPU;
|
||||
using ILGPU.Runtime;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using PerfectNumbers.Core.Gpu.Kernels;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
@@ -115,10 +114,14 @@ public class GpuKernelCompilationTests
|
||||
typeof(PrimeOrderGpuHeuristics).GetMethod("GetPow2ModKernel", BindingFlags.NonPublic | BindingFlags.Static)
|
||||
?? throw new InvalidOperationException("PrimeOrderGpuHeuristics.GetPow2ModKernel not found.");
|
||||
|
||||
private static readonly MethodInfo PrimeOrderGetPow2ModWideKernelMethod =
|
||||
typeof(PrimeOrderGpuHeuristics).GetMethod("GetPow2ModWideKernel", BindingFlags.NonPublic | BindingFlags.Static)
|
||||
?? throw new InvalidOperationException("PrimeOrderGpuHeuristics.GetPow2ModWideKernel not found.");
|
||||
|
||||
private static readonly Action<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>> MersenneDivisorCyclesKernel =
|
||||
DivisorCycleKernels.GpuDivisorCycleKernel;
|
||||
|
||||
private static readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> PrimeTesterSmallPrimeKernel =
|
||||
private static readonly Action<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>> PrimeTesterSmallPrimeKernel =
|
||||
PrimeTesterKernels.SmallPrimeSieveKernel;
|
||||
|
||||
private static readonly Action<Index1D, ArrayView<ulong>, ArrayView<byte>> PrimeTesterSharesFactorKernel =
|
||||
@@ -322,67 +325,56 @@ public class GpuKernelCompilationTests
|
||||
|
||||
private static void CompilePrimeOrderPow2ModWideKernel(Accelerator accelerator)
|
||||
{
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<GpuUInt128, Stride1D.Dense>, GpuUInt128, ArrayView1D<GpuUInt128, Stride1D.Dense>>(PrimeOrderKernels.Pow2ModKernelWide);
|
||||
_ = kernel;
|
||||
_ = PrimeOrderGetPow2ModWideKernelMethod.Invoke(null, new object[] { accelerator });
|
||||
}
|
||||
|
||||
private static void CompileMersenneDivisorCyclesKernel(Accelerator accelerator)
|
||||
{
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel(MersenneDivisorCyclesKernel);
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView1D<ulong, Stride1D.Dense>, ArrayView1D<ulong, Stride1D.Dense>>(MersenneDivisorCyclesKernel);
|
||||
_ = kernel;
|
||||
}
|
||||
|
||||
private static void CompilePrimeTesterSmallPrimeKernel(Accelerator accelerator)
|
||||
{
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel(PrimeTesterSmallPrimeKernel);
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<uint>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterSmallPrimeKernel);
|
||||
_ = kernel;
|
||||
}
|
||||
|
||||
private static void CompilePrimeTesterSharesFactorKernel(Accelerator accelerator)
|
||||
{
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel(PrimeTesterSharesFactorKernel);
|
||||
var kernel = accelerator.LoadAutoGroupedStreamKernel<Index1D, ArrayView<ulong>, ArrayView<byte>>(PrimeTesterSharesFactorKernel);
|
||||
_ = kernel;
|
||||
}
|
||||
|
||||
private static KernelType _kernelType = KernelType.Pow2ModKernelScan | KernelType.OrderKernelScan | KernelType.IncrementalKernelScan | KernelType.IncrementalOrderKernelScan | KernelType.Pow2ModOrderKernelScan;
|
||||
private static void CompileGpuKernelPoolKernels(Accelerator accelerator)
|
||||
{
|
||||
private static void CompileGpuKernelPoolKernels(Accelerator accelerator)
|
||||
{
|
||||
_ = accelerator;
|
||||
var lease = GpuKernelPool.GetKernel(useGpuOrder: false);
|
||||
try
|
||||
{
|
||||
_ = lease.OrderKernel;
|
||||
_ = lease.IncrementalKernel;
|
||||
_ = lease.Pow2ModKernel;
|
||||
_ = lease.IncrementalOrderKernel;
|
||||
_ = lease.Pow2ModOrderKernel;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lease.Dispose();
|
||||
}
|
||||
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
int acceleratorIndex = AcceleratorPool.Shared.Rent();
|
||||
AcceleratorStream stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||
try
|
||||
{
|
||||
var kernels = GpuKernelPool.GetOrAddKernels(acceleratorIndex, stream, _kernelType);
|
||||
_ = kernels.Order;
|
||||
_ = kernels.Incremental;
|
||||
_ = kernels.Pow2Mod;
|
||||
_ = kernels.IncrementalOrder;
|
||||
_ = kernels.Pow2ModOrder;
|
||||
}
|
||||
finally
|
||||
{
|
||||
stream.Synchronize();
|
||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||
// GpuPrimeWorkLimiter.Release();
|
||||
}
|
||||
|
||||
stream = AcceleratorStreamPool.Rent(acceleratorIndex);
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
try
|
||||
{
|
||||
var kernels = GpuKernelPool.GetOrAddKernels(acceleratorIndex, stream, _kernelType);
|
||||
_ = kernels.Order;
|
||||
_ = kernels.Incremental;
|
||||
_ = kernels.Pow2Mod;
|
||||
_ = kernels.IncrementalOrder;
|
||||
_ = kernels.Pow2ModOrder;
|
||||
}
|
||||
finally
|
||||
{
|
||||
stream.Synchronize();
|
||||
AcceleratorStreamPool.Return(acceleratorIndex, stream);
|
||||
// GpuPrimeWorkLimiter.Release();
|
||||
}
|
||||
}
|
||||
var orderLease = GpuKernelPool.GetKernel(useGpuOrder: true);
|
||||
try
|
||||
{
|
||||
_ = orderLease.OrderKernel;
|
||||
_ = orderLease.IncrementalKernel;
|
||||
_ = orderLease.Pow2ModKernel;
|
||||
_ = orderLease.IncrementalOrderKernel;
|
||||
_ = orderLease.Pow2ModOrderKernel;
|
||||
}
|
||||
finally
|
||||
{
|
||||
orderLease.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,19 +18,19 @@ public class GpuPrimeLimiterTests
|
||||
var startedSecond = new TaskCompletionSource<bool>();
|
||||
var sw = new Stopwatch();
|
||||
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
var first = GpuPrimeWorkLimiter.Acquire();
|
||||
sw.Start();
|
||||
|
||||
var secondTask = Task.Run(() =>
|
||||
{
|
||||
GpuPrimeWorkLimiter.Acquire();
|
||||
var second = GpuPrimeWorkLimiter.Acquire();
|
||||
try
|
||||
{
|
||||
startedSecond.TrySetResult(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
GpuPrimeWorkLimiter.Release();
|
||||
second.Dispose();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ public class GpuPrimeLimiterTests
|
||||
finally
|
||||
{
|
||||
// Release first after the initial wait so the second task can proceed.
|
||||
GpuPrimeWorkLimiter.Release();
|
||||
first.Dispose();
|
||||
}
|
||||
|
||||
await secondTask.WaitAsync(TimeSpan.FromSeconds(1));
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
@@ -8,91 +7,59 @@ namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
[Trait("Category", "Slow")]
|
||||
public class LucasLehmerPrimeTesterTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(3UL, true)]
|
||||
[InlineData(5UL, true)]
|
||||
[InlineData(11UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_gpu_returns_expected_results(ulong p, bool expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
new MersenneNumberLucasLehmerGpuTester().IsPrime(gpu, p, true).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(3UL, true)]
|
||||
[InlineData(5UL, true)]
|
||||
[InlineData(11UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_gpu_returns_expected_results(ulong p, bool expected)
|
||||
{
|
||||
new MersenneNumberLucasLehmerGpuTester().IsPrime(p, true).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_gpu_large_exponent_without_cpu_fallback()
|
||||
{
|
||||
// Use a modest exponent that still exercises the NTT path while keeping
|
||||
// runtime short enough for the unit test environment.
|
||||
const ulong exponent = 128UL;
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
tester.WarmUpNttParameters(exponent);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, exponent, true).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_gpu_large_exponent_without_cpu_fallback()
|
||||
{
|
||||
// Use a modest exponent that still exercises the NTT path while keeping
|
||||
// runtime short enough for the unit test environment.
|
||||
const ulong exponent = 128UL;
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
tester.WarmUpNttParameters(exponent);
|
||||
tester.IsPrime(exponent, true).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrimeBatch_matches_individual()
|
||||
{
|
||||
ulong[] exponents = new ulong[] { 3UL, 5UL, 7UL, 11UL, 13UL, 17UL, 19UL, 23UL };
|
||||
bool[] expected = new bool[] { true, true, true, false, true, true, true, false };
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
bool[] results = new bool[exponents.Length];
|
||||
tester.IsMersennePrimeBatch(exponents, results);
|
||||
results.Should().Equal(expected);
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrimeBatch_matches_individual()
|
||||
{
|
||||
ulong[] exponents = new ulong[] { 3UL, 5UL, 7UL, 11UL, 13UL, 17UL, 19UL, 23UL };
|
||||
bool[] expected = new bool[] { true, true, true, false, true, true, true, false };
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
bool[] results = new bool[exponents.Length];
|
||||
tester.IsMersennePrimeBatch(exponents, results);
|
||||
results.Should().Equal(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(132UL)]
|
||||
[InlineData(1000UL)]
|
||||
[Trait("Category", "Slow")]
|
||||
public void WarmUpNttParameters_populates_cache_for_large_exponent(ulong exponent)
|
||||
{
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
tester.WarmUpNttParameters(exponent);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, exponent, true).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(132UL)]
|
||||
[InlineData(1000UL)]
|
||||
[Trait("Category", "Slow")]
|
||||
public void WarmUpNttParameters_populates_cache_for_large_exponent(ulong exponent)
|
||||
{
|
||||
var tester = new MersenneNumberLucasLehmerGpuTester();
|
||||
tester.WarmUpNttParameters(exponent);
|
||||
tester.IsPrime(exponent, true).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(3UL, true)]
|
||||
[InlineData(5UL, true)]
|
||||
[InlineData(11UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_cpu_matches_expected_results(ulong p, bool expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
new MersenneNumberLucasLehmerCpuTester().IsPrime(gpu, p).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(3UL, true)]
|
||||
[InlineData(5UL, true)]
|
||||
[InlineData(11UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersennePrime_cpu_matches_expected_results(ulong p, bool expected)
|
||||
{
|
||||
new MersenneNumberLucasLehmerCpuTester().IsPrime(p).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,352 +13,367 @@ namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
[Collection("GpuNtt")]
|
||||
public class NttGpuMathTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ForwardGpu_reference_matches_cpu_forward()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Reference;
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
cpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
cpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
cpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
cpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
cpu.CopyTo(gpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Forward(cpu, modulus, primitiveRoot);
|
||||
NttGpuMath.ForwardGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ForwardGpu_reference_matches_cpu_forward()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Reference;
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
cpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
cpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
cpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
cpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
cpu.CopyTo(gpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Forward(cpu, modulus, primitiveRoot);
|
||||
NttGpuMath.ForwardGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ForwardGpu_staged_matches_cpu_forward()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
cpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
cpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
cpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
cpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
cpu.CopyTo(gpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Forward(cpu, modulus, primitiveRoot);
|
||||
NttGpuMath.ForwardGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ForwardGpu_staged_matches_cpu_forward()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
cpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
cpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
cpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
cpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
cpu.CopyTo(gpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Forward(cpu, modulus, primitiveRoot);
|
||||
NttGpuMath.ForwardGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void InverseGpu_reference_matches_cpu_inverse()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Reference;
|
||||
Span<GpuUInt128> input = stackalloc GpuUInt128[4];
|
||||
input[0] = new GpuUInt128(0UL, 5UL);
|
||||
input[1] = new GpuUInt128(0UL, 7UL);
|
||||
input[2] = new GpuUInt128(0UL, 9UL);
|
||||
input[3] = new GpuUInt128(0UL, 11UL);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
Span<GpuUInt128> transformed = stackalloc GpuUInt128[4];
|
||||
input.CopyTo(transformed);
|
||||
NttGpuMath.Forward(transformed, modulus, primitiveRoot);
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
transformed.CopyTo(cpu);
|
||||
NttGpuMath.Inverse(cpu, modulus, primitiveRoot);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
transformed.CopyTo(gpu);
|
||||
NttGpuMath.InverseGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void InverseGpu_reference_matches_cpu_inverse()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Reference;
|
||||
Span<GpuUInt128> input = stackalloc GpuUInt128[4];
|
||||
input[0] = new GpuUInt128(0UL, 5UL);
|
||||
input[1] = new GpuUInt128(0UL, 7UL);
|
||||
input[2] = new GpuUInt128(0UL, 9UL);
|
||||
input[3] = new GpuUInt128(0UL, 11UL);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
Span<GpuUInt128> transformed = stackalloc GpuUInt128[4];
|
||||
input.CopyTo(transformed);
|
||||
NttGpuMath.Forward(transformed, modulus, primitiveRoot);
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
transformed.CopyTo(cpu);
|
||||
NttGpuMath.Inverse(cpu, modulus, primitiveRoot);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
transformed.CopyTo(gpu);
|
||||
NttGpuMath.InverseGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void InverseGpu_staged_matches_cpu_inverse()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> input = stackalloc GpuUInt128[4];
|
||||
input[0] = new GpuUInt128(0UL, 5UL);
|
||||
input[1] = new GpuUInt128(0UL, 7UL);
|
||||
input[2] = new GpuUInt128(0UL, 9UL);
|
||||
input[3] = new GpuUInt128(0UL, 11UL);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
Span<GpuUInt128> transformed = stackalloc GpuUInt128[4];
|
||||
input.CopyTo(transformed);
|
||||
NttGpuMath.Forward(transformed, modulus, primitiveRoot);
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
transformed.CopyTo(cpu);
|
||||
NttGpuMath.Inverse(cpu, modulus, primitiveRoot);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
transformed.CopyTo(gpu);
|
||||
NttGpuMath.InverseGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void InverseGpu_staged_matches_cpu_inverse()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> input = stackalloc GpuUInt128[4];
|
||||
input[0] = new GpuUInt128(0UL, 5UL);
|
||||
input[1] = new GpuUInt128(0UL, 7UL);
|
||||
input[2] = new GpuUInt128(0UL, 9UL);
|
||||
input[3] = new GpuUInt128(0UL, 11UL);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
Span<GpuUInt128> transformed = stackalloc GpuUInt128[4];
|
||||
input.CopyTo(transformed);
|
||||
NttGpuMath.Forward(transformed, modulus, primitiveRoot);
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
transformed.CopyTo(cpu);
|
||||
NttGpuMath.Inverse(cpu, modulus, primitiveRoot);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
transformed.CopyTo(gpu);
|
||||
NttGpuMath.InverseGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Montgomery_twiddle_inverses_multiply_to_r2()
|
||||
{
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Montgomery_twiddle_inverses_multiply_to_r2()
|
||||
{
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
|
||||
var accelerator = SharedGpuContext.CreateAccelerator();
|
||||
var stream = accelerator.CreateStream();
|
||||
GpuContextPool.GpuContextLease gpu = GpuContextPool.Rent();
|
||||
try
|
||||
{
|
||||
var accelerator = gpu.Accelerator;
|
||||
|
||||
var method = typeof(NttGpuMath).GetMethod("GetSquareCache", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
method.Should().NotBeNull();
|
||||
var cache = method!.Invoke(null, new object[] { accelerator, 8, modulus, primitiveRoot })!;
|
||||
var cacheType = cache.GetType();
|
||||
var method = typeof(NttGpuMath).GetMethod("GetSquareCache", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
method.Should().NotBeNull();
|
||||
var cache = method!.Invoke(null, new object[] { accelerator, 8, modulus, primitiveRoot })!;
|
||||
var cacheType = cache.GetType();
|
||||
|
||||
var twiddlesMont = (MemoryBuffer1D<GpuUInt128, Stride1D.Dense>)cacheType.GetProperty("TwiddlesMont")!.GetValue(cache)!;
|
||||
var twiddlesInvMont = (MemoryBuffer1D<GpuUInt128, Stride1D.Dense>)cacheType.GetProperty("TwiddlesInvMont")!.GetValue(cache)!;
|
||||
var r2 = (ulong)cacheType.GetProperty("MontR2Mod64")!.GetValue(cache)!;
|
||||
var mod = (ulong)cacheType.GetProperty("ModulusLow")!.GetValue(cache)!;
|
||||
var twiddlesMont = (MemoryBuffer1D<GpuUInt128, Stride1D.Dense>)cacheType.GetProperty("TwiddlesMont")!.GetValue(cache)!;
|
||||
var twiddlesInvMont = (MemoryBuffer1D<GpuUInt128, Stride1D.Dense>)cacheType.GetProperty("TwiddlesInvMont")!.GetValue(cache)!;
|
||||
var r2 = (ulong)cacheType.GetProperty("MontR2Mod64")!.GetValue(cache)!;
|
||||
var mod = (ulong)cacheType.GetProperty("ModulusLow")!.GetValue(cache)!;
|
||||
|
||||
int count = 7; // length 8 -> 7 twiddles
|
||||
var forward = new GpuUInt128[count];
|
||||
var inverse = new GpuUInt128[count];
|
||||
twiddlesMont.CopyToCPU(stream, forward);
|
||||
twiddlesInvMont.CopyToCPU(stream, inverse);
|
||||
stream.Synchronize();
|
||||
stream.Dispose();
|
||||
accelerator.Dispose();
|
||||
int count = 7; // length 8 -> 7 twiddles
|
||||
var forward = new GpuUInt128[count];
|
||||
var inverse = new GpuUInt128[count];
|
||||
twiddlesMont.CopyToCPU(forward);
|
||||
twiddlesInvMont.CopyToCPU(inverse);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ulong prod = (ulong)((((BigInteger)forward[i].Low) * inverse[i].Low) % mod);
|
||||
prod.Should().Be(r2);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ulong prod = (ulong)((((BigInteger)forward[i].Low) * inverse[i].Low) % mod);
|
||||
prod.Should().Be(r2);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
gpu.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ConvolveGpu_reference_matches_cpu_convolve()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Reference;
|
||||
Span<GpuUInt128> leftCpu = stackalloc GpuUInt128[4];
|
||||
Span<GpuUInt128> rightCpu = stackalloc GpuUInt128[4];
|
||||
leftCpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
leftCpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
leftCpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
leftCpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
rightCpu[0] = new GpuUInt128(0UL, 5UL);
|
||||
rightCpu[1] = new GpuUInt128(0UL, 6UL);
|
||||
rightCpu[2] = new GpuUInt128(0UL, 7UL);
|
||||
rightCpu[3] = new GpuUInt128(0UL, 8UL);
|
||||
Span<GpuUInt128> leftGpu = stackalloc GpuUInt128[4];
|
||||
Span<GpuUInt128> rightGpu = stackalloc GpuUInt128[4];
|
||||
leftCpu.CopyTo(leftGpu);
|
||||
rightCpu.CopyTo(rightGpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Convolve(leftCpu, rightCpu, modulus, primitiveRoot);
|
||||
NttGpuMath.ConvolveGpu(leftGpu, rightGpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < leftCpu.Length; i++)
|
||||
{
|
||||
leftGpu[i].Should().Be(leftCpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ConvolveGpu_reference_matches_cpu_convolve()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Reference;
|
||||
Span<GpuUInt128> leftCpu = stackalloc GpuUInt128[4];
|
||||
Span<GpuUInt128> rightCpu = stackalloc GpuUInt128[4];
|
||||
leftCpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
leftCpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
leftCpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
leftCpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
rightCpu[0] = new GpuUInt128(0UL, 5UL);
|
||||
rightCpu[1] = new GpuUInt128(0UL, 6UL);
|
||||
rightCpu[2] = new GpuUInt128(0UL, 7UL);
|
||||
rightCpu[3] = new GpuUInt128(0UL, 8UL);
|
||||
Span<GpuUInt128> leftGpu = stackalloc GpuUInt128[4];
|
||||
Span<GpuUInt128> rightGpu = stackalloc GpuUInt128[4];
|
||||
leftCpu.CopyTo(leftGpu);
|
||||
rightCpu.CopyTo(rightGpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Convolve(leftCpu, rightCpu, modulus, primitiveRoot);
|
||||
NttGpuMath.ConvolveGpu(leftGpu, rightGpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < leftCpu.Length; i++)
|
||||
{
|
||||
leftGpu[i].Should().Be(leftCpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ConvolveGpu_staged_matches_cpu_convolve()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> leftCpu = stackalloc GpuUInt128[4];
|
||||
Span<GpuUInt128> rightCpu = stackalloc GpuUInt128[4];
|
||||
leftCpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
leftCpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
leftCpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
leftCpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
rightCpu[0] = new GpuUInt128(0UL, 5UL);
|
||||
rightCpu[1] = new GpuUInt128(0UL, 6UL);
|
||||
rightCpu[2] = new GpuUInt128(0UL, 7UL);
|
||||
rightCpu[3] = new GpuUInt128(0UL, 8UL);
|
||||
Span<GpuUInt128> leftGpu = stackalloc GpuUInt128[4];
|
||||
Span<GpuUInt128> rightGpu = stackalloc GpuUInt128[4];
|
||||
leftCpu.CopyTo(leftGpu);
|
||||
rightCpu.CopyTo(rightGpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Convolve(leftCpu, rightCpu, modulus, primitiveRoot);
|
||||
NttGpuMath.ConvolveGpu(leftGpu, rightGpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < leftCpu.Length; i++)
|
||||
{
|
||||
leftGpu[i].Should().Be(leftCpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ConvolveGpu_staged_matches_cpu_convolve()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> leftCpu = stackalloc GpuUInt128[4];
|
||||
Span<GpuUInt128> rightCpu = stackalloc GpuUInt128[4];
|
||||
leftCpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
leftCpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
leftCpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
leftCpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
rightCpu[0] = new GpuUInt128(0UL, 5UL);
|
||||
rightCpu[1] = new GpuUInt128(0UL, 6UL);
|
||||
rightCpu[2] = new GpuUInt128(0UL, 7UL);
|
||||
rightCpu[3] = new GpuUInt128(0UL, 8UL);
|
||||
Span<GpuUInt128> leftGpu = stackalloc GpuUInt128[4];
|
||||
Span<GpuUInt128> rightGpu = stackalloc GpuUInt128[4];
|
||||
leftCpu.CopyTo(leftGpu);
|
||||
rightCpu.CopyTo(rightGpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Convolve(leftCpu, rightCpu, modulus, primitiveRoot);
|
||||
NttGpuMath.ConvolveGpu(leftGpu, rightGpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < leftCpu.Length; i++)
|
||||
{
|
||||
leftGpu[i].Should().Be(leftCpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void SquareGpu_reference_matches_cpu_square()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Reference;
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
cpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
cpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
cpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
cpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
cpu.CopyTo(gpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Square(cpu, modulus, primitiveRoot);
|
||||
NttGpuMath.SquareGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void SquareGpu_reference_matches_cpu_square()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Reference;
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
cpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
cpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
cpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
cpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
cpu.CopyTo(gpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Square(cpu, modulus, primitiveRoot);
|
||||
NttGpuMath.SquareGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void SquareGpu_staged_matches_cpu_square()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
cpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
cpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
cpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
cpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
cpu.CopyTo(gpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Square(cpu, modulus, primitiveRoot);
|
||||
NttGpuMath.SquareGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void SquareGpu_staged_matches_cpu_square()
|
||||
{
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[4];
|
||||
cpu[0] = new GpuUInt128(0UL, 1UL);
|
||||
cpu[1] = new GpuUInt128(0UL, 2UL);
|
||||
cpu[2] = new GpuUInt128(0UL, 3UL);
|
||||
cpu[3] = new GpuUInt128(0UL, 4UL);
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[4];
|
||||
cpu.CopyTo(gpu);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
NttGpuMath.Square(cpu, modulus, primitiveRoot);
|
||||
NttGpuMath.SquareGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void BitReverseGpu_matches_cpu()
|
||||
{
|
||||
GpuUInt128[] cpu = new GpuUInt128[8];
|
||||
for (ulong i = 0; i < (ulong)cpu.Length; i++)
|
||||
{
|
||||
cpu[i] = new GpuUInt128(0UL, i);
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void BitReverseGpu_matches_cpu()
|
||||
{
|
||||
GpuUInt128[] cpu = new GpuUInt128[8];
|
||||
for (ulong i = 0; i < (ulong)cpu.Length; i++)
|
||||
{
|
||||
cpu[i] = new GpuUInt128(0UL, i);
|
||||
}
|
||||
|
||||
GpuUInt128[] gpu = (GpuUInt128[])cpu.Clone();
|
||||
NttGpuMath.BitReverse(cpu);
|
||||
NttGpuMath.BitReverseGpu(gpu);
|
||||
gpu.Should().Equal(cpu);
|
||||
}
|
||||
GpuUInt128[] gpu = (GpuUInt128[])cpu.Clone();
|
||||
NttGpuMath.BitReverse(cpu);
|
||||
NttGpuMath.BitReverseGpu(gpu);
|
||||
gpu.Should().Equal(cpu);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void DisposeAll_clears_ntt_caches()
|
||||
{
|
||||
// Ensure the cache starts empty.
|
||||
NttGpuMath.ClearCaches();
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void DisposeAll_clears_ntt_caches()
|
||||
{
|
||||
// Ensure the cache starts empty.
|
||||
NttGpuMath.ClearCaches();
|
||||
|
||||
Span<GpuUInt128> values = stackalloc GpuUInt128[2];
|
||||
values[0] = new GpuUInt128(0UL, 1UL);
|
||||
values[1] = new GpuUInt128(0UL, 2UL);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
Accelerator accelerator = SharedGpuContext.CreateAccelerator();
|
||||
var stream = accelerator.CreateStream();
|
||||
var buffer = accelerator.Allocate1D<GpuUInt128>(values.Length);
|
||||
buffer.View.CopyFromCPU(stream, ref values[0], values.Length);
|
||||
NttGpuMath.SquareDevice(accelerator, stream, buffer.View, modulus, primitiveRoot);
|
||||
stream.Synchronize();
|
||||
stream.Dispose();
|
||||
buffer.Dispose();
|
||||
accelerator.Dispose();
|
||||
Span<GpuUInt128> values = stackalloc GpuUInt128[2];
|
||||
values[0] = new GpuUInt128(0UL, 1UL);
|
||||
values[1] = new GpuUInt128(0UL, 2UL);
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
GpuContextPool.GpuContextLease gpu = GpuContextPool.Rent();
|
||||
try
|
||||
{
|
||||
var buffer = gpu.Accelerator.Allocate1D<GpuUInt128>(values.Length);
|
||||
try
|
||||
{
|
||||
buffer.View.CopyFromCPU(ref values[0], values.Length);
|
||||
NttGpuMath.SquareDevice(gpu.Accelerator, buffer.View, modulus, primitiveRoot);
|
||||
}
|
||||
finally
|
||||
{
|
||||
buffer.Dispose();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
gpu.Dispose();
|
||||
}
|
||||
|
||||
// Verify that the square cache now holds entries for the accelerator.
|
||||
var field = typeof(NttGpuMath).GetField("SquareCache", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var cacheObj = field.GetValue(null)!;
|
||||
int count = (int)cacheObj.GetType().GetProperty("Count")!.GetValue(cacheObj)!;
|
||||
count.Should().BeGreaterThan(0);
|
||||
// Verify that the square cache now holds entries for the accelerator.
|
||||
var field = typeof(NttGpuMath).GetField("SquareCache", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
var cacheObj = field.GetValue(null)!;
|
||||
int count = (int)cacheObj.GetType().GetProperty("Count")!.GetValue(cacheObj)!;
|
||||
count.Should().BeGreaterThan(0);
|
||||
|
||||
count = (int)cacheObj.GetType().GetProperty("Count")!.GetValue(cacheObj)!;
|
||||
count.Should().Be(0);
|
||||
}
|
||||
GpuContextPool.DisposeAll();
|
||||
|
||||
count = (int)cacheObj.GetType().GetProperty("Count")!.GetValue(cacheObj)!;
|
||||
count.Should().Be(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,83 +9,86 @@ namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
[Trait("Category", "Fast")]
|
||||
public class NttMontgomeryTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void SquareDevice_montgomery64_matches_cpu_square_on_small_input()
|
||||
{
|
||||
// Small 64-bit modulus and tiny input to keep runtime trivial.
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void SquareDevice_montgomery64_matches_cpu_square_on_small_input()
|
||||
{
|
||||
// Small 64-bit modulus and tiny input to keep runtime trivial.
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[8];
|
||||
for (ulong i = 0; i < (ulong)cpu.Length; i++)
|
||||
{
|
||||
cpu[(int)i] = new GpuUInt128(0UL, i + 1UL);
|
||||
}
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[16];
|
||||
cpu.CopyTo(gpu);
|
||||
for (int i = cpu.Length; i < gpu.Length; i++)
|
||||
{
|
||||
gpu[i] = new GpuUInt128(0UL, 0UL);
|
||||
}
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[8];
|
||||
for (ulong i = 0; i < (ulong)cpu.Length; i++)
|
||||
{
|
||||
cpu[(int)i] = new GpuUInt128(0UL, i + 1UL);
|
||||
}
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[16];
|
||||
cpu.CopyTo(gpu);
|
||||
for (int i = cpu.Length; i < gpu.Length; i++)
|
||||
{
|
||||
gpu[i] = new GpuUInt128(0UL, 0UL);
|
||||
}
|
||||
|
||||
// CPU reference
|
||||
NttGpuMath.Square(cpu, modulus, primitiveRoot);
|
||||
// CPU reference
|
||||
NttGpuMath.Square(cpu, modulus, primitiveRoot);
|
||||
|
||||
// Device execution
|
||||
var accelerator = SharedGpuContext.CreateAccelerator();
|
||||
var stream = accelerator.CreateStream();
|
||||
using var buffer = accelerator.Allocate1D<GpuUInt128>(gpu.Length);
|
||||
buffer.View.CopyFromCPU(stream, ref gpu[0], gpu.Length);
|
||||
NttGpuMath.SquareDevice(accelerator, stream, buffer.View, modulus, primitiveRoot);
|
||||
buffer.View.CopyToCPU(stream, ref gpu[0], gpu.Length);
|
||||
stream.Synchronize();
|
||||
stream.Dispose();
|
||||
accelerator.Dispose();
|
||||
// Device execution
|
||||
GpuContextPool.GpuContextLease context = GpuContextPool.Rent();
|
||||
try
|
||||
{
|
||||
var accelerator = context.Accelerator;
|
||||
using var buffer = accelerator.Allocate1D<GpuUInt128>(gpu.Length);
|
||||
buffer.View.CopyFromCPU(ref gpu[0], gpu.Length);
|
||||
NttGpuMath.SquareDevice(accelerator, buffer.View, modulus, primitiveRoot);
|
||||
buffer.View.CopyToCPU(ref gpu[0], gpu.Length);
|
||||
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
for (int i = 0; i < cpu.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void InverseDevice_montgomery64_matches_cpu_inverse_on_small_input()
|
||||
{
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void InverseDevice_montgomery64_matches_cpu_inverse_on_small_input()
|
||||
{
|
||||
var modulus = new GpuUInt128(0UL, 17UL);
|
||||
var primitiveRoot = new GpuUInt128(0UL, 3UL);
|
||||
Span<GpuUInt128> input = stackalloc GpuUInt128[8];
|
||||
for (ulong i = 0; i < (ulong)input.Length; i++)
|
||||
{
|
||||
input[(int)i] = new GpuUInt128(0UL, i + 1UL);
|
||||
}
|
||||
|
||||
Span<GpuUInt128> input = stackalloc GpuUInt128[8];
|
||||
for (ulong i = 0; i < (ulong)input.Length; i++)
|
||||
{
|
||||
input[(int)i] = new GpuUInt128(0UL, i + 1UL);
|
||||
}
|
||||
Span<GpuUInt128> transformed = stackalloc GpuUInt128[input.Length];
|
||||
input.CopyTo(transformed);
|
||||
NttGpuMath.Forward(transformed, modulus, primitiveRoot);
|
||||
|
||||
Span<GpuUInt128> transformed = stackalloc GpuUInt128[input.Length];
|
||||
input.CopyTo(transformed);
|
||||
NttGpuMath.Forward(transformed, modulus, primitiveRoot);
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[input.Length];
|
||||
transformed.CopyTo(cpu);
|
||||
NttGpuMath.Inverse(cpu, modulus, primitiveRoot);
|
||||
|
||||
Span<GpuUInt128> cpu = stackalloc GpuUInt128[input.Length];
|
||||
transformed.CopyTo(cpu);
|
||||
NttGpuMath.Inverse(cpu, modulus, primitiveRoot);
|
||||
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[input.Length];
|
||||
transformed.CopyTo(gpu);
|
||||
NttGpuMath.InverseGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
var original = NttGpuMath.GpuTransformBackend;
|
||||
try
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = NttBackend.Staged;
|
||||
Span<GpuUInt128> gpu = stackalloc GpuUInt128[input.Length];
|
||||
transformed.CopyTo(gpu);
|
||||
NttGpuMath.InverseGpu(gpu, modulus, primitiveRoot);
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
gpu[i].Should().Be(cpu[i]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
NttGpuMath.GpuTransformBackend = original;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,410 +2,374 @@ using System.Numerics;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
[Trait("Category", "Fast")]
|
||||
public class PrimeOrderGpuHeuristicsTests
|
||||
{
|
||||
public PrimeOrderGpuHeuristicsTests()
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.Clear();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.Clear();
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
public PrimeOrderGpuHeuristicsTests()
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.Clear();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.Clear();
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_for_marked_prime()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 1UL, prime, out ulong remainder, divisorData);
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_for_marked_prime()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(1UL, prime, out ulong remainder, divisorData);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_success_for_supported_prime()
|
||||
{
|
||||
const ulong exponent = 138_000_001UL;
|
||||
const ulong prime = 138_000_001UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, prime, out ulong remainder, divisorData);
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_success_for_supported_prime()
|
||||
{
|
||||
const ulong exponent = 138_000_001UL;
|
||||
const ulong prime = 138_000_001UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out ulong remainder, divisorData);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
remainder.Should().Be(2UL);
|
||||
}
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
remainder.Should().Be(2UL);
|
||||
}
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(1UL, prime, out ulong remainder, divisorData);
|
||||
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 1UL, prime, out ulong remainder, divisorData);
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_when_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
const ulong prime = 193UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(16UL, prime, out ulong remainder, divisorData);
|
||||
|
||||
[Fact]
|
||||
public void TryPow2Mod_returns_overflow_when_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
const ulong prime = 193UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 16UL, prime, out ulong remainder, divisorData);
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(0UL);
|
||||
GpuPow2ModStatus followUp = PrimeOrderGpuHeuristics.TryPow2Mod(4UL, prime, out ulong followUpRemainder, divisorData);
|
||||
followUp.Should().Be(GpuPow2ModStatus.Success);
|
||||
followUpRemainder.Should().Be(16UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
GpuPow2ModStatus followUp = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, 4UL, prime, out ulong followUpRemainder, divisorData);
|
||||
followUp.Should().Be(GpuPow2ModStatus.Success);
|
||||
followUpRemainder.Should().Be(16UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_for_marked_prime()
|
||||
{
|
||||
const ulong prime = 193UL;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_for_marked_prime()
|
||||
{
|
||||
const ulong prime = 193UL;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
ulong[] remainders = new ulong[2];
|
||||
ulong[] exponents = { 1UL, 2UL };
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
|
||||
ulong[] remainders = new ulong[2];
|
||||
ulong[] exponents = { 1UL, 2UL };
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData);
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_success_for_supported_prime()
|
||||
{
|
||||
ulong[] remainders = { 123UL, 456UL };
|
||||
ulong[] exponents = { 138_000_001UL, 138_000_011UL };
|
||||
const ulong prime = 138_000_001UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_success_for_supported_prime()
|
||||
{
|
||||
ulong[] remainders = { 123UL, 456UL };
|
||||
ulong[] exponents = { 138_000_001UL, 138_000_011UL };
|
||||
const ulong prime = 138_000_001UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
ulong expectedFirst = (ulong)BigInteger.ModPow(2, exponents[0], prime);
|
||||
ulong expectedSecond = (ulong)BigInteger.ModPow(2, exponents[1], prime);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
expectedFirst.Should().Be(2UL);
|
||||
expectedSecond.Should().Be(2048UL);
|
||||
remainders[0].Should().Be(expectedFirst);
|
||||
remainders[1].Should().Be(expectedSecond);
|
||||
}
|
||||
|
||||
ulong expectedFirst = (ulong)BigInteger.ModPow(2, exponents[0], prime);
|
||||
ulong expectedSecond = (ulong)BigInteger.ModPow(2, exponents[1], prime);
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 1UL, 2UL };
|
||||
ulong[] remainders = { 5UL, 6UL };
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
expectedFirst.Should().Be(2UL);
|
||||
expectedSecond.Should().Be(2048UL);
|
||||
remainders[0].Should().Be(expectedFirst);
|
||||
remainders[1].Should().Be(expectedSecond);
|
||||
}
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
const ulong prime = 97UL;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(6, 64);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 1UL, 2UL };
|
||||
ulong[] remainders = { 5UL, 6UL };
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData);
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_when_any_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 2UL, 16UL };
|
||||
ulong[] remainders = { 9UL, 11UL };
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
const ulong prime = 193UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatch_returns_overflow_when_any_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 3);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 2UL, 16UL };
|
||||
ulong[] remainders = { 9UL, 11UL };
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
const ulong prime = 193UL;
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders, divisorData);
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_for_marked_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 80) + 7;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide[prime] = 0;
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(0UL);
|
||||
remainders[1].Should().Be(0UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(UInt128.One, prime, out UInt128 remainder);
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_for_marked_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 80) + 7;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide[prime] = 0;
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, UInt128.One, prime, out UInt128 remainder);
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_success_for_supported_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 96) + 11;
|
||||
UInt128 exponent = ((UInt128)1 << 64) + 3;
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out UInt128 remainder);
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_success_for_supported_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 96) + 11;
|
||||
UInt128 exponent = ((UInt128)1 << 64) + 3;
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponent, (BigInteger)prime);
|
||||
remainder.Should().Be(expected);
|
||||
}
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, prime, out UInt128 remainder);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
[Theory]
|
||||
[InlineData("193", "31")]
|
||||
[InlineData("193", "255")]
|
||||
[InlineData("79228162514264337593543950347", "36893488147419103251")]
|
||||
public void Pow2ModKernelCore_matches_cpu_windowed_path(string modulusText, string exponentText)
|
||||
{
|
||||
UInt128 modulus = UInt128.Parse(modulusText);
|
||||
UInt128 exponent = UInt128.Parse(exponentText);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponent, (BigInteger)prime);
|
||||
remainder.Should().Be(expected);
|
||||
}
|
||||
var method = typeof(PrimeOrderGpuHeuristics).GetMethod("Pow2ModKernelCore", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
method.Should().NotBeNull();
|
||||
|
||||
[Theory]
|
||||
[InlineData("193", "31")]
|
||||
[InlineData("193", "255")]
|
||||
[InlineData("79228162514264337593543950347", "36893488147419103251")]
|
||||
public void Pow2ModKernelCore_matches_cpu_windowed_path(string modulusText, string exponentText)
|
||||
{
|
||||
UInt128 modulus = UInt128.Parse(modulusText);
|
||||
UInt128 exponent = UInt128.Parse(exponentText);
|
||||
object? gpuResultObject = method!.Invoke(null, new object[] { new GpuUInt128(exponent), new GpuUInt128(modulus) });
|
||||
GpuUInt128 gpuResult = (GpuUInt128)gpuResultObject!;
|
||||
UInt128 expected = exponent.Pow2MontgomeryModWindowed(modulus);
|
||||
|
||||
var method = typeof(PrimeOrderGpuHeuristics).GetMethod("Pow2ModKernelCore", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
method.Should().NotBeNull();
|
||||
((UInt128)gpuResult).Should().Be(expected);
|
||||
}
|
||||
|
||||
object? gpuResultObject = method!.Invoke(null, new object[] { new GpuUInt128(exponent), new GpuUInt128(modulus) });
|
||||
GpuUInt128 gpuResult = (GpuUInt128)gpuResultObject!;
|
||||
UInt128 expected = exponent.Pow2MontgomeryModWindowed(modulus);
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 80) + 9;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(UInt128.One, prime, out UInt128 remainder);
|
||||
|
||||
((UInt128)gpuResult).Should().Be(expected);
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 80) + 9;
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, UInt128.One, prime, out UInt128 remainder);
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_when_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 4);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
UInt128 exponent = UInt128.One << 8;
|
||||
UInt128 prime = ((UInt128)1 << 70) + 3;
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(exponent, prime, out UInt128 remainder);
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModWide_returns_overflow_when_exponent_exceeds_capability()
|
||||
{
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 4);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
UInt128 exponent = UInt128.One << 8;
|
||||
UInt128 prime = ((UInt128)1 << 70) + 3;
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2Mod(gpu, exponent, prime, out UInt128 remainder);
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_success_for_supported_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 90) + 19;
|
||||
UInt128[] exponents = { UInt128.One, ((UInt128)1 << 64) + 5 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainder.Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders);
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_success_for_supported_prime()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 90) + 19;
|
||||
UInt128[] exponents = { UInt128.One, ((UInt128)1 << 64) + 5 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
for (int i = 0; i < exponents.Length; i++)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponents[i], (BigInteger)prime);
|
||||
remainders[i].Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 88) + 13;
|
||||
UInt128[] exponents = { UInt128.One, (UInt128)5 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Success);
|
||||
for (int i = 0; i < exponents.Length; i++)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, (BigInteger)exponents[i], (BigInteger)prime);
|
||||
remainders[i].Should().Be(expected);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
remainders[1].Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_overflow_when_modulus_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 88) + 13;
|
||||
UInt128[] exponents = { UInt128.One, (UInt128)5 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(64, 128);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_overflow_when_any_exponent_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 72) + 23;
|
||||
UInt128[] exponents = { UInt128.One << 5, UInt128.One << 9 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 6);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders);
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
remainders[1].Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderGpuHeuristics.OverflowRegistryWide.TryRemove(prime, out _);
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
remainders[1].Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryPow2ModBatchWide_returns_overflow_when_any_exponent_exceeds_capability()
|
||||
{
|
||||
UInt128 prime = ((UInt128)1 << 72) + 23;
|
||||
UInt128[] exponents = { UInt128.One << 5, UInt128.One << 9 };
|
||||
UInt128[] remainders = { UInt128.Zero, UInt128.Zero };
|
||||
var capability = new PrimeOrderGpuHeuristics.PrimeOrderGpuCapability(128, 6);
|
||||
PrimeOrderGpuHeuristics.OverrideCapabilitiesForTesting(capability);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
GpuPow2ModStatus status = PrimeOrderGpuHeuristics.TryPow2ModBatch(gpu, exponents, prime, remainders);
|
||||
// This will never happen in production code because the caller ensures the remainder span is large enough.
|
||||
// [Fact]
|
||||
// public void TryPow2ModBatch_throws_when_remainder_span_is_too_small()
|
||||
// {
|
||||
// ulong[] remainders = new ulong[1];
|
||||
// ulong[] exponents = { 1UL, 2UL };
|
||||
|
||||
status.Should().Be(GpuPow2ModStatus.Overflow);
|
||||
remainders[0].Should().Be(UInt128.Zero);
|
||||
remainders[1].Should().Be(UInt128.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.ResetCapabilitiesForTesting();
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
// Action act = () =>
|
||||
// {
|
||||
// const ulong prime = 5UL;
|
||||
// MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
// PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
// };
|
||||
|
||||
// This will never happen in production code because the caller ensures the remainder span is large enough.
|
||||
// [Fact]
|
||||
// public void TryPow2ModBatch_throws_when_remainder_span_is_too_small()
|
||||
// {
|
||||
// ulong[] remainders = new ulong[1];
|
||||
// ulong[] exponents = { 1UL, 2UL };
|
||||
// act.Should().Throw<ArgumentException>();
|
||||
// }
|
||||
|
||||
// Action act = () =>
|
||||
// {
|
||||
// const ulong prime = 5UL;
|
||||
// MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
// PrimeOrderGpuHeuristics.TryPow2ModBatch(exponents, prime, remainders, divisorData);
|
||||
// };
|
||||
[Fact]
|
||||
public void Calculate_falls_back_to_cpu_when_gpu_overflow_is_marked()
|
||||
{
|
||||
const ulong prime = 7UL;
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
|
||||
// act.Should().Throw<ArgumentException>();
|
||||
// }
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
ulong result = PrimeOrderCalculator.Calculate(prime, null, divisorData, PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault, PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
[Fact]
|
||||
public void Calculate_falls_back_to_cpu_when_gpu_overflow_is_marked()
|
||||
{
|
||||
const ulong prime = 7UL;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry[prime] = 0;
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(prime);
|
||||
ulong result = PrimeOrderCalculatorGpu.Calculate(gpu, prime, null, divisorData, PrimeOrderCalculatorConfig.HeuristicDefault);
|
||||
|
||||
result.Should().Be(3UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
result.Should().Be(3UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderGpuHeuristics.OverflowRegistry.TryRemove(prime, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
@@ -7,39 +7,40 @@ namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
[Trait("Category", "Fast")]
|
||||
public class PrimeTesterGpuTests
|
||||
{
|
||||
[Fact]
|
||||
public void IsPrimeGpu_accepts_known_primes()
|
||||
{
|
||||
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(gpu, prime).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void IsPrimeGpu_accepts_known_primes()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
ulong[] primes = [31UL, 61UL, 89UL, 107UL, 127UL, 521UL];
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
tester.IsPrimeGpu(prime).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
GpuContextPool.DisposeAll();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsPrimeGpu_rejects_composites()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
||||
foreach (ulong composite in composites)
|
||||
{
|
||||
tester.IsPrimeGpu(composite).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
GpuContextPool.DisposeAll();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsPrimeGpu_rejects_composites()
|
||||
{
|
||||
ulong[] composites = [33UL, 39UL, 51UL, 77UL, 91UL];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
foreach (ulong composite in composites)
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(gpu, composite).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using Xunit;
|
||||
using PerfectNumbers.Core.Gpu.Kernels;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests.Gpu;
|
||||
|
||||
@@ -19,7 +19,7 @@ public class ULongExtensionsGpuTests
|
||||
public void Pow2ModWindowedGpu_matches_montgomery_cpu_for_known_values(ulong exponent, ulong modulus)
|
||||
{
|
||||
MontgomeryDivisorData divisor = MontgomeryDivisorData.FromModulus(modulus);
|
||||
ulong expected = exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisor);
|
||||
ulong expected = exponent.Pow2MontgomeryModWindowedCpu(divisor, keepMontgomery: false);
|
||||
|
||||
exponent.Pow2ModWindowedGpu(modulus).Should().Be(expected);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class ULongExtensionsGpuTests
|
||||
MontgomeryDivisorData divisor = MontgomeryDivisorData.FromModulus(modulus);
|
||||
foreach (ulong exponent in exponents)
|
||||
{
|
||||
ulong expected = exponent.Pow2MontgomeryModWindowedConvertToStandardCpu(divisor);
|
||||
ulong expected = exponent.Pow2MontgomeryModWindowedCpu(divisor, keepMontgomery: false);
|
||||
exponent.Pow2ModWindowedGpu(modulus).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Collections.Generic;
|
||||
using PerfectNumbers.Core;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
@@ -12,7 +16,10 @@ public class HeuristicCombinedPrimeTesterTests
|
||||
[InlineData((byte)9)]
|
||||
public void Enumerator_uses_one_a_one_b_schedule_by_default(byte lastDigit)
|
||||
{
|
||||
var enumerator = HeuristicCombinedPrimeTester.CreateHeuristicDivisorEnumerator(500UL * 500UL, lastDigit);
|
||||
Span<HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState> groupBState =
|
||||
stackalloc HeuristicCombinedPrimeTester.HeuristicGroupBSequenceState[4];
|
||||
|
||||
var enumerator = HeuristicCombinedPrimeTester.CreateHeuristicDivisorEnumerator(500UL * 500UL, lastDigit, groupBState);
|
||||
var results = new List<HeuristicCombinedPrimeTester.HeuristicDivisorCandidate>(32);
|
||||
|
||||
while (results.Count < 20 && enumerator.TryGetNext(out var candidate))
|
||||
|
||||
@@ -1,110 +1,107 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneDivisorCyclesGenerateGpuTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void GenerateGpu_produces_expected_cycles_for_small_range()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MersenneDivisorCyclesGpu.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0);
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong d = reader.ReadUInt64();
|
||||
ulong c = reader.ReadUInt64();
|
||||
pairs.Add((d, c));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void GenerateGpu_produces_expected_cycles_for_small_range()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
MersenneDivisorCycles.GenerateGpu(path, maxDivisor: 50UL, batchSize: 16, skipCount: 0, nextPosition: 0);
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong d = reader.ReadUInt64();
|
||||
ulong c = reader.ReadUInt64();
|
||||
pairs.Add((d, c));
|
||||
}
|
||||
|
||||
var expected = new List<ulong>();
|
||||
for (ulong d = 3; d <= 50; d++)
|
||||
{
|
||||
if ((d & 1UL) == 0UL || d % 3UL == 0UL || d % 5UL == 0UL || d % 7UL == 0UL || d % 11UL == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var expected = new List<ulong>();
|
||||
for (ulong d = 3; d <= 50; d++)
|
||||
{
|
||||
if ((d & 1UL) == 0UL || d % 3UL == 0UL || d % 5UL == 0UL || d % 7UL == 0UL || d % 11UL == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
expected.Add(d);
|
||||
}
|
||||
expected.Add(d);
|
||||
}
|
||||
|
||||
pairs.Should().HaveCount(expected.Count);
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
expected.Should().Contain(pair.divisor);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(pair.divisor);
|
||||
pair.cycle.Should().Be(MersenneDivisorCyclesCpu.CalculateCycleLength(pair.divisor, divisorData));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
pairs.Should().HaveCount(expected.Count);
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
expected.Should().Contain(pair.divisor);
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(pair.divisor);
|
||||
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(pair.divisor, divisorData));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Generate_produces_unique_cycles_for_expected_divisors()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
MersenneDivisorCyclesCpu.Generate(path, maxDivisor: 50UL, threads: 4);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Generate_produces_unique_cycles_for_expected_divisors()
|
||||
{
|
||||
string path = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
MersenneDivisorCycles.Generate(path, maxDivisor: 50UL, threads: 4);
|
||||
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong divisor = reader.ReadUInt64();
|
||||
ulong cycle = reader.ReadUInt64();
|
||||
pairs.Add((divisor, cycle));
|
||||
}
|
||||
var pairs = new List<(ulong divisor, ulong cycle)>();
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
using var reader = new BinaryReader(stream);
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
ulong divisor = reader.ReadUInt64();
|
||||
ulong cycle = reader.ReadUInt64();
|
||||
pairs.Add((divisor, cycle));
|
||||
}
|
||||
|
||||
var expected = new List<ulong>();
|
||||
for (ulong divisor = 3; divisor <= 50; divisor++)
|
||||
{
|
||||
if ((divisor & 1UL) == 0UL || divisor % 3UL == 0UL || divisor % 5UL == 0UL || divisor % 7UL == 0UL || divisor % 11UL == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var expected = new List<ulong>();
|
||||
for (ulong divisor = 3; divisor <= 50; divisor++)
|
||||
{
|
||||
if ((divisor & 1UL) == 0UL || divisor % 3UL == 0UL || divisor % 5UL == 0UL || divisor % 7UL == 0UL || divisor % 11UL == 0UL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
expected.Add(divisor);
|
||||
}
|
||||
expected.Add(divisor);
|
||||
}
|
||||
|
||||
pairs.Should().HaveCount(expected.Count);
|
||||
pairs.Should().HaveCount(expected.Count);
|
||||
|
||||
HashSet<ulong> uniqueDivisors = new();
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
uniqueDivisors.Add(pair.divisor).Should().BeTrue();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(pair.divisor);
|
||||
pair.cycle.Should().Be(MersenneDivisorCyclesCpu.CalculateCycleLength(pair.divisor, divisorData));
|
||||
}
|
||||
HashSet<ulong> uniqueDivisors = new();
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
uniqueDivisors.Add(pair.divisor).Should().BeTrue();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(pair.divisor);
|
||||
pair.cycle.Should().Be(MersenneDivisorCycles.CalculateCycleLength(pair.divisor, divisorData));
|
||||
}
|
||||
|
||||
uniqueDivisors.Should().BeEquivalentTo(expected);
|
||||
uniqueDivisors.Should().BeEquivalentTo(expected);
|
||||
|
||||
var (_, completeCount) = MersenneDivisorCyclesCpu.FindLast(path);
|
||||
completeCount.Should().Be(expected.Count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
var (_, completeCount) = MersenneDivisorCycles.FindLast(path);
|
||||
completeCount.Should().Be(expected.Count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,26 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneDivisorCyclesTests
|
||||
{
|
||||
public static IEnumerable<object[]> Divisors() =>
|
||||
new[]
|
||||
{
|
||||
new object[] {3UL, 2UL},
|
||||
new object[] {7UL, 3UL},
|
||||
new object[] {11UL, 10UL},
|
||||
};
|
||||
public static IEnumerable<object[]> Divisors() =>
|
||||
new[]
|
||||
{
|
||||
new object[] {3UL, 2UL},
|
||||
new object[] {7UL, 3UL},
|
||||
new object[] {11UL, 10UL},
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(Divisors))]
|
||||
public void CalculateCycleLength_returns_expected_value(ulong divisor, ulong expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
||||
MersenneDivisorCyclesCpu.CalculateCycleLength(divisor, divisorData).Should().Be(expected);
|
||||
MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, divisor, divisorData).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(Divisors))]
|
||||
public void CalculateCycleLength_returns_expected_value(ulong divisor, ulong expected)
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(divisor);
|
||||
MersenneDivisorCycles.CalculateCycleLength(divisor, divisorData).Should().Be(expected);
|
||||
MersenneDivisorCycles.CalculateCycleLengthGpu(divisor).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -13,7 +11,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
||||
{
|
||||
var candidates = new List<ulong> { 5UL, 7UL, 11UL, 13UL };
|
||||
var tester = new FakeTester();
|
||||
var results = new List<(ulong Prime, bool Searched, bool Detailed, bool Passed, BigInteger Divisor)>();
|
||||
var results = new List<(ulong Prime, bool Searched, bool Detailed, bool Passed)>();
|
||||
int compositeMarks = 0;
|
||||
int clearedMarks = 0;
|
||||
|
||||
@@ -24,7 +22,7 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
||||
startPrime: 0UL,
|
||||
markComposite: () => compositeMarks++,
|
||||
clearComposite: () => clearedMarks++,
|
||||
printResult: (prime, searched, detailed, passed, divisor) => results.Add((prime, searched, detailed, passed, divisor)),
|
||||
printResult: (prime, searched, detailed, passed) => results.Add((prime, searched, detailed, passed)),
|
||||
threadCount: 1,
|
||||
primesPerTask: 2);
|
||||
|
||||
@@ -32,12 +30,12 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
||||
compositeMarks.Should().Be(2);
|
||||
clearedMarks.Should().Be(2);
|
||||
results.Should().Equal(
|
||||
new (ulong Prime, bool Searched, bool Detailed, bool Passed, BigInteger Divisor)[]
|
||||
new[]
|
||||
{
|
||||
(5UL, true, true, true, BigInteger.Zero),
|
||||
(7UL, true, true, false, new BigInteger(7)),
|
||||
(11UL, true, true, false, new BigInteger(11)),
|
||||
(13UL, true, true, true, BigInteger.Zero),
|
||||
(5UL, true, true, true),
|
||||
(7UL, true, true, false),
|
||||
(11UL, true, true, false),
|
||||
(13UL, true, true, true),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,47 +45,31 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
||||
|
||||
public int BatchSize { get; set; }
|
||||
|
||||
public BigInteger MinK { get; set; }
|
||||
|
||||
public string? StateFilePath { get; set; }
|
||||
|
||||
public void ResetStateTracking()
|
||||
{
|
||||
}
|
||||
|
||||
public void ResumeFromState(in string stateFile, in BigInteger lastSavedK, in BigInteger minK)
|
||||
{
|
||||
}
|
||||
|
||||
public ulong DivisorLimit => ConfiguredMaxPrime + 100UL;
|
||||
|
||||
public bool IsPrime(ulong prime, out bool divisorsExhausted, out BigInteger divisor)
|
||||
public void ConfigureFromMaxPrime(ulong maxPrime)
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
if (prime % 7UL == 0UL)
|
||||
{
|
||||
divisor = 7UL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prime % 11UL == 0UL)
|
||||
{
|
||||
divisor = 11UL;
|
||||
return false;
|
||||
}
|
||||
|
||||
divisor = 0UL;
|
||||
return true;
|
||||
ConfiguredMaxPrime = maxPrime;
|
||||
}
|
||||
|
||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession(PrimeOrderCalculatorAccelerator gpu)
|
||||
public ulong GetAllowedMaxDivisor(ulong prime)
|
||||
{
|
||||
return DivisorLimit;
|
||||
}
|
||||
|
||||
public bool IsPrime(ulong prime, out bool divisorsExhausted)
|
||||
{
|
||||
divisorsExhausted = true;
|
||||
return prime % 7UL != 0UL && prime % 11UL != 0UL;
|
||||
}
|
||||
|
||||
public IMersenneNumberDivisorByDivisorTester.IDivisorScanSession CreateDivisorSession()
|
||||
{
|
||||
return new DummySession();
|
||||
}
|
||||
|
||||
public void PrepareCandidates(ulong maxPrime, in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
||||
public void PrepareCandidates(in ReadOnlySpan<ulong> primes, Span<ulong> allowedMaxValues)
|
||||
{
|
||||
ConfiguredMaxPrime = maxPrime;
|
||||
for (int i = 0; i < primes.Length; i++)
|
||||
{
|
||||
allowedMaxValues[i] = 100UL;
|
||||
@@ -96,12 +78,12 @@ public class MersenneNumberDivisorByDivisorTesterTests
|
||||
|
||||
private sealed class DummySession : IMersenneNumberDivisorByDivisorTester.IDivisorScanSession
|
||||
{
|
||||
public bool CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes)
|
||||
public void CheckDivisor(ulong divisor, in MontgomeryDivisorData divisorData, ulong divisorCycle, in ReadOnlySpan<ulong> primes, Span<byte> hits)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Return()
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,138 +1,104 @@
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder = PerfectNumbers.Core.Cpu.MersenneNumberDivisorByDivisorCpuTesterWithForSequentialDivisorSetForCpuOrder;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberDivisorCpuTesterTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_tracks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder();
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_tracks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(11UL);
|
||||
|
||||
ulong[] primes = [5UL, 7UL, 11UL];
|
||||
ulong[] allowedMax = new ulong[primes.Length];
|
||||
tester.PrepareCandidates(maxPrime: 11UL, primes, allowedMax);
|
||||
tester.IsPrime(5UL, out bool divisorsExhausted).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
|
||||
tester.ResumeFromState(Path.Combine(Path.GetTempPath(), "bydivisor-cpu-tests.bin"), BigInteger.Zero, BigInteger.One);
|
||||
tester.ResetStateTracking();
|
||||
tester.IsPrime(7UL, out divisorsExhausted).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
|
||||
tester.IsPrime(5UL, out bool divisorsExhausted, out BigInteger divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(BigInteger.Zero);
|
||||
tester.IsPrime(11UL, out divisorsExhausted).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
}
|
||||
|
||||
tester.IsPrime(7UL, out divisorsExhausted, out divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(BigInteger.Zero);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_checks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(13UL);
|
||||
|
||||
tester.IsPrime(11UL, out divisorsExhausted, out divisor).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||
}
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 5UL, 7UL, 11UL, 13UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_checks_divisors_across_primes()
|
||||
{
|
||||
var session = new MersenneCpuDivisorScanSessionWithCpuOrder();
|
||||
ulong cycle23 = MersenneDivisorCycles.CalculateCycleLength(23UL, MontgomeryDivisorData.FromModulus(23UL));
|
||||
session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
|
||||
ulong[] primes = [5UL, 7UL, 11UL, 13UL];
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle31 = MersenneDivisorCycles.CalculateCycleLength(31UL, MontgomeryDivisorData.FromModulus(31UL));
|
||||
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
ulong cycle23 = MersenneDivisorCyclesCpu.CalculateCycleLength(23UL, MontgomeryDivisorData.FromModulus(23UL));
|
||||
session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primes).Should().BeTrue();
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
ulong[] primesWithoutHit = [5UL, 7UL, 13UL];
|
||||
session.CheckDivisor(23UL, MontgomeryDivisorData.FromModulus(23UL), cycle23, primesWithoutHit).Should().BeFalse();
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
ulong cycle31 = MersenneDivisorCyclesCpu.CalculateCycleLength(31UL, MontgomeryDivisorData.FromModulus(31UL));
|
||||
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primes).Should().BeTrue();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
ulong[] primesBeforeHit = [7UL, 11UL, 13UL];
|
||||
session.CheckDivisor(31UL, MontgomeryDivisorData.FromModulus(31UL), cycle31, primesBeforeHit).Should().BeFalse();
|
||||
}
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||
{
|
||||
var session = new MersenneCpuDivisorScanSessionWithCpuOrder();
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester();
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
|
||||
ulong[] exponents = [6UL, 7UL, 9UL, 10UL, 12UL];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCyclesCpu.CalculateCycleLength(7UL, divisorData);
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents).Should().BeTrue();
|
||||
|
||||
ulong[] nonDivisibleExponents = [5UL, 11UL];
|
||||
session.CheckDivisor(7UL, divisorData, cycle, nonDivisibleExponents).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||
{
|
||||
var session = new MersenneCpuDivisorScanSessionWithCpuOrder();
|
||||
|
||||
ulong[] exponents = [10UL, 11UL, 20UL, 21UL, 30UL];
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCyclesCpu.CalculateCycleLength(11UL, divisorData);
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents).Should().BeTrue();
|
||||
|
||||
ulong[] nonDivisibleExponents = [5UL, 17UL];
|
||||
session.CheckDivisor(11UL, divisorData, cycle, nonDivisibleExponents).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_does_not_mark_mersenne_prime_as_composite_via_self_divisor()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder();
|
||||
|
||||
tester.DivisorLimit = (BigInteger.One << 30) - BigInteger.One;
|
||||
BigInteger kSelf = ((BigInteger.One << 31) - 2) / 62; // (2^p - 2) / (2p) for p=31.
|
||||
|
||||
tester.ResumeFromState(Path.Combine(Path.GetTempPath(), "bydivisor-selfdivisor.bin"), BigInteger.Zero, kSelf);
|
||||
tester.ResetStateTracking();
|
||||
|
||||
tester.IsPrime(31UL, out bool divisorsExhausted, out BigInteger divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(BigInteger.Zero);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(127UL, 127, true)]
|
||||
[InlineData(127UL, 89, false)]
|
||||
[InlineData(89UL, 61, false)]
|
||||
public void IsMersenneValue_flags_only_matching_exponent_as_trivial(ulong prime, int exponent, bool expected)
|
||||
{
|
||||
BigInteger divisor = (BigInteger.One << exponent) - BigInteger.One;
|
||||
InvokeIsMersenneValue(prime, divisor).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsMersenneValue_ignores_mersenne_shaped_divisors_above_prime()
|
||||
{
|
||||
ulong prime = 89UL;
|
||||
BigInteger divisor = (BigInteger.One << 101) - BigInteger.One;
|
||||
InvokeIsMersenneValue(prime, divisor).Should().BeFalse();
|
||||
}
|
||||
|
||||
private static bool InvokeIsMersenneValue(ulong prime, BigInteger divisor)
|
||||
{
|
||||
MethodInfo? method = typeof(MersenneNumberDivisorByDivisorCpuTesterWithCpuOrder)
|
||||
.GetMethod("IsMersenneValue", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
|
||||
method.Should().NotBeNull("IsMersenneValue should exist as a private static helper on the tester type.");
|
||||
return (bool)method!.Invoke(null, new object[] { prime, divisor })!;
|
||||
}
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,402 +1,352 @@
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberDivisorGpuTesterTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(33UL, 7UL, true)]
|
||||
[InlineData(35UL, 31UL, true)]
|
||||
[InlineData(37UL, 223UL, true)]
|
||||
[InlineData(33UL, 13UL, false)]
|
||||
[InlineData(35UL, 73UL, false)]
|
||||
[InlineData(37UL, 227UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsDivisible_returns_expected(ulong exponent, ulong divisor, bool expected)
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
ReadOnlyGpuUInt128 divisorValue = new ReadOnlyGpuUInt128(divisor);
|
||||
tester.IsDivisible(exponent, in divisorValue).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(33UL, 7UL, true)]
|
||||
[InlineData(35UL, 31UL, true)]
|
||||
[InlineData(37UL, 223UL, true)]
|
||||
[InlineData(33UL, 13UL, false)]
|
||||
[InlineData(35UL, 73UL, false)]
|
||||
[InlineData(37UL, 227UL, false)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsDivisible_returns_expected(ulong exponent, ulong divisor, bool expected)
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
ReadOnlyGpuUInt128 divisorValue = new ReadOnlyGpuUInt128(divisor);
|
||||
tester.IsDivisible(exponent, in divisorValue).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsDivisible_handles_large_divisors()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
UInt128 divisor = (UInt128.One << 65) - UInt128.One;
|
||||
ReadOnlyGpuUInt128 divisorValue = new ReadOnlyGpuUInt128(divisor);
|
||||
tester.IsDivisible(65UL, in divisorValue).Should().BeTrue();
|
||||
divisorValue = new ReadOnlyGpuUInt128(divisor + 2); // Reusing divisorValue for the shifted candidate.
|
||||
tester.IsDivisible(65UL, in divisorValue).Should().BeFalse();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsDivisible_handles_large_divisors()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
UInt128 divisor = (UInt128.One << 65) - UInt128.One;
|
||||
ReadOnlyGpuUInt128 divisorValue = new ReadOnlyGpuUInt128(divisor);
|
||||
tester.IsDivisible(65UL, in divisorValue).Should().BeTrue();
|
||||
divisorValue = new ReadOnlyGpuUInt128(divisor + 2); // Reusing divisorValue for the shifted candidate.
|
||||
tester.IsDivisible(65UL, in divisorValue).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_sets_divisorsExhausted_false_when_search_range_not_exhausted()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
typeof(MersenneNumberDivisorGpuTester)
|
||||
.GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!
|
||||
.SetValue(null, Array.Empty<(ulong, uint)>());
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_sets_divisorsExhausted_false_when_search_range_not_exhausted()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
typeof(MersenneNumberDivisorGpuTester)
|
||||
.GetField("_divisorCandidates", BindingFlags.NonPublic | BindingFlags.Static)!
|
||||
.SetValue(null, Array.Empty<(ulong, uint)>());
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 31UL, UInt128.Zero, 0UL, out bool divisorsExhausted).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
tester.IsPrime(31UL, UInt128.Zero, 0UL, out bool divisorsExhausted).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_sets_divisorsExhausted_true_when_divisible_by_specified_divisor()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 35UL, 31UL, 0UL, out bool divisorsExhausted).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_sets_divisorsExhausted_true_when_divisible_by_specified_divisor()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
tester.IsPrime(35UL, 31UL, 0UL, out bool divisorsExhausted).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_accepts_large_search_limits()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsPrime(gpu, 35UL, 31UL, ulong.MaxValue, out bool exhausted).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrime_accepts_large_search_limits()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorGpuTester();
|
||||
tester.IsPrime(35UL, 31UL, ulong.MaxValue, out bool exhausted).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
}
|
||||
|
||||
private static void ConfigureByDivisorTester(MersenneNumberDivisorByDivisorGpuTester tester, ulong maxPrime)
|
||||
{
|
||||
tester.PrepareCandidates(maxPrime, ReadOnlySpan<ulong>.Empty, Span<ulong>.Empty);
|
||||
tester.ResumeFromState(Path.Combine(Path.GetTempPath(), "bydivisor-gpu-tests.bin"), BigInteger.Zero, BigInteger.One);
|
||||
tester.ResetStateTracking();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_tracks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_tracks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: 43UL);
|
||||
tester.IsPrime(31UL, out bool exhausted).Should().BeTrue();
|
||||
exhausted.Should().BeTrue();
|
||||
|
||||
tester.IsPrime(31UL, out bool exhausted, out BigInteger divisor).Should().BeTrue();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().Be(BigInteger.Zero);
|
||||
tester.IsPrime(37UL, out exhausted).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
|
||||
tester.IsPrime(37UL, out exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||
tester.IsPrime(43UL, out exhausted).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
}
|
||||
|
||||
tester.IsPrime(43UL, out exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_defaults_to_zero_limit_before_configuration()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_defaults_to_zero_limit_before_prepare_candidates()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.DivisorLimit.Should().Be(0UL);
|
||||
tester.DivisorLimit.Should().Be(0UL);
|
||||
|
||||
ulong[] primes = [31UL, 37UL];
|
||||
ulong[] allowed = new ulong[primes.Length];
|
||||
tester.PrepareCandidates(maxPrime: 37UL, primes, allowed);
|
||||
ulong[] primes = { 31UL, 37UL };
|
||||
ulong[] allowed = new ulong[primes.Length];
|
||||
|
||||
tester.DivisorLimit.Should().BeGreaterThan(0UL);
|
||||
allowed.Should().OnlyContain(value => value > 0UL);
|
||||
}
|
||||
tester.PrepareCandidates(primes, allowed);
|
||||
allowed.Should().OnlyContain(value => value == 0UL);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_respects_filter_based_limit()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: 41UL);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_respects_filter_based_limit()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(41UL);
|
||||
|
||||
tester.IsPrime(31UL, out _, out BigInteger divisor).Should().BeTrue();
|
||||
divisor.Should().Be(BigInteger.Zero);
|
||||
tester.IsPrime(31UL, out _).Should().BeTrue();
|
||||
tester.IsPrime(37UL, out bool exhausted).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
|
||||
tester.IsPrime(37UL, out bool exhausted, out divisor).Should().BeFalse();
|
||||
exhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||
tester.IsPrime(41UL, out bool divisorsExhausted).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
}
|
||||
|
||||
tester.IsPrime(41UL, out bool divisorsExhausted, out divisor).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(71UL, false)]
|
||||
[InlineData(89UL, true)]
|
||||
[InlineData(127UL, true)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_matches_incremental_expectations(ulong exponent, bool expectedPrime)
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: exponent);
|
||||
[Theory]
|
||||
[InlineData(71UL, false)]
|
||||
[InlineData(89UL, true)]
|
||||
[InlineData(127UL, true)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_tester_matches_incremental_expectations(ulong exponent, bool expectedPrime)
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(exponent);
|
||||
|
||||
tester.IsPrime(exponent, out bool exhausted, out BigInteger divisor).Should().Be(expectedPrime);
|
||||
tester.IsPrime(exponent, out bool exhausted).Should().Be(expectedPrime);
|
||||
exhausted.Should().BeTrue();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_tester_uses_cycle_remainders_per_divisor()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 8,
|
||||
};
|
||||
|
||||
exhausted.Should().BeTrue();
|
||||
if (expectedPrime)
|
||||
{
|
||||
divisor.Should().Be(BigInteger.Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||
}
|
||||
}
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_tester_uses_cycle_remainders_per_divisor()
|
||||
{
|
||||
int originalBatchSize = EnvironmentConfiguration.GpuBatchSize;
|
||||
try
|
||||
{
|
||||
EnvironmentConfiguration.GpuBatchSize = 8;
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: 43UL);
|
||||
tester.IsPrime(41UL, out bool divisorsExhausted).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
}
|
||||
|
||||
tester.IsPrime(41UL, out bool divisorsExhausted, out BigInteger divisor).Should().BeFalse();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().BeGreaterThan(BigInteger.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EnvironmentConfiguration.GpuBatchSize = originalBatchSize;
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_computes_cycle_when_zero_is_provided()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_computes_cycle_when_zero_is_provided()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: 43UL);
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), 0UL, primes, hits);
|
||||
|
||||
ulong[] primes = [31UL, 37UL, 41UL];
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), 0UL, primes).Should().BeTrue();
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
ulong[] primesWithoutHit = [31UL, 41UL];
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), 0UL, primesWithoutHit).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_checks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_checks_divisors_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: 43UL);
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0 });
|
||||
|
||||
ulong[] primes = [31UL, 37UL, 41UL, 43UL];
|
||||
Array.Fill(hits, (byte)0);
|
||||
ulong cycle13367 = MersenneDivisorCycles.CalculateCycleLength(13367UL, MontgomeryDivisorData.FromModulus(13367UL));
|
||||
session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primes, hits);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 0, 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
ulong cycle223 = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes).Should().BeTrue();
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_session_reuses_cycle_remainders_across_batches()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 2,
|
||||
};
|
||||
|
||||
ulong[] primesBeforeHit = [31UL];
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse();
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
|
||||
ulong cycle13367 = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 13367UL, MontgomeryDivisorData.FromModulus(13367UL));
|
||||
session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primes).Should().BeTrue();
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
ulong[] primesWithoutMatchingExponent = [31UL, 37UL, 43UL];
|
||||
session.CheckDivisor(13367UL, MontgomeryDivisorData.FromModulus(13367UL), cycle13367, primesWithoutMatchingExponent).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes, hits);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_session_reuses_cycle_remainders_across_batches()
|
||||
{
|
||||
int originalBatchSize = EnvironmentConfiguration.GpuBatchSize;
|
||||
try
|
||||
{
|
||||
EnvironmentConfiguration.GpuBatchSize = 2;
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: 47UL);
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 5,
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(43UL);
|
||||
|
||||
ulong[] primes = [31UL, 37UL, 41UL, 43UL, 47UL];
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 6UL, 7UL, 9UL, 10UL, 12UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
ulong cycle223 = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes).Should().BeTrue();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(7UL, divisorData);
|
||||
|
||||
ulong[] primesBeforeHit = [31UL];
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
EnvironmentConfiguration.GpuBatchSize = originalBatchSize;
|
||||
}
|
||||
}
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_seven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: 43UL);
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
ulong[] exponents = [6UL, 7UL, 9UL, 10UL, 12UL];
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester
|
||||
{
|
||||
GpuBatchSize = 5,
|
||||
};
|
||||
tester.ConfigureFromMaxPrime(61UL);
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(7UL);
|
||||
ulong cycle = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 7UL, divisorData);
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] exponents = { 10UL, 11UL, 20UL, 21UL, 30UL };
|
||||
byte[] hits = new byte[exponents.Length];
|
||||
|
||||
session.CheckDivisor(7UL, divisorData, cycle, exponents).Should().BeTrue();
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCycles.CalculateCycleLength(11UL, divisorData);
|
||||
|
||||
ulong[] nonDivisibleExponents = [5UL, 11UL];
|
||||
session.CheckDivisor(7UL, divisorData, cycle, nonDivisibleExponents).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents, hits);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_session_marks_mersenne_numbers_divisible_by_eleven_as_composite()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: 61UL);
|
||||
hits.Should().Equal(new byte[] { 1, 0, 1, 0, 1 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var session = tester.CreateDivisorSession(gpu);
|
||||
ulong[] exponents = [10UL, 11UL, 20UL, 21UL, 30UL];
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_tester_skips_divisors_excluded_by_small_cycle_generation()
|
||||
{
|
||||
var cycles = MersenneDivisorCycles.Shared;
|
||||
var tableField = typeof(MersenneDivisorCycles).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
var smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(11UL);
|
||||
ulong cycle = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 11UL, divisorData);
|
||||
var originalTable = (List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!;
|
||||
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
|
||||
|
||||
session.CheckDivisor(11UL, divisorData, cycle, exponents).Should().BeTrue();
|
||||
try
|
||||
{
|
||||
var patchedTable = new List<(ulong Divisor, ulong Cycle)>();
|
||||
ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||
|
||||
ulong[] nonDivisibleExponents = [5UL, 17UL];
|
||||
session.CheckDivisor(11UL, divisorData, cycle, nonDivisibleExponents).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(191UL);
|
||||
patchedSmall[191] = MersenneDivisorCycles.CalculateCycleLength(191UL, divisorData);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_gpu_tester_skips_divisors_excluded_by_small_cycle_generation()
|
||||
{
|
||||
var cycles = MersenneDivisorCyclesGpu.Shared;
|
||||
var tableField = typeof(MersenneDivisorCyclesGpu).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
var smallCyclesField = typeof(MersenneDivisorCyclesGpu).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
tableField.SetValue(cycles, patchedTable);
|
||||
smallCyclesField.SetValue(cycles, patchedSmall);
|
||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||
|
||||
var originalTable = (List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!;
|
||||
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
tester.ConfigureFromMaxPrime(19UL);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var patchedTable = new List<(ulong Divisor, ulong Cycle)>();
|
||||
ulong[] patchedSmall = new ulong[PerfectNumberConstants.MaxQForDivisorCycles + 1];
|
||||
typeof(MersenneNumberDivisorByDivisorGpuTester)
|
||||
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
||||
.SetValue(tester, 200UL);
|
||||
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(191UL);
|
||||
patchedSmall[191] = MersenneDivisorCyclesGpu.CalculateCycleLength(gpu, 191UL, divisorData);
|
||||
tester.IsPrime(19UL, out bool divisorsExhausted).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
tableField.SetValue(cycles, originalTable);
|
||||
smallCyclesField.SetValue(cycles, originalSmall);
|
||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||
GpuContextPool.DisposeAll();
|
||||
}
|
||||
}
|
||||
|
||||
tableField.SetValue(cycles, patchedTable);
|
||||
smallCyclesField.SetValue(cycles, patchedSmall);
|
||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_cpu_session_reuses_cycle_remainders_across_primes()
|
||||
{
|
||||
var tester = new MersenneNumberDivisorByDivisorCpuTester
|
||||
{
|
||||
BatchSize = 2,
|
||||
};
|
||||
|
||||
var tester = new MersenneNumberDivisorByDivisorGpuTester();
|
||||
ConfigureByDivisorTester(tester, maxPrime: 19UL);
|
||||
tester.ConfigureFromMaxPrime(47UL);
|
||||
|
||||
typeof(MersenneNumberDivisorByDivisorGpuTester)
|
||||
.GetField("_divisorLimit", BindingFlags.NonPublic | BindingFlags.Instance)!
|
||||
.SetValue(tester, 200UL);
|
||||
var session = tester.CreateDivisorSession();
|
||||
try
|
||||
{
|
||||
ulong[] primes = { 31UL, 37UL, 41UL, 43UL, 47UL };
|
||||
byte[] hits = new byte[primes.Length];
|
||||
|
||||
tester.IsPrime(19UL, out bool divisorsExhausted, out BigInteger divisor).Should().BeTrue();
|
||||
divisorsExhausted.Should().BeTrue();
|
||||
divisor.Should().Be(BigInteger.Zero);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
tableField.SetValue(cycles, originalTable);
|
||||
smallCyclesField.SetValue(cycles, originalSmall);
|
||||
DivisorCycleCache.Shared.RefreshSnapshot();
|
||||
}
|
||||
}
|
||||
ulong cycle223 = MersenneDivisorCycles.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes, hits);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ByDivisor_cpu_session_checks_divisors_across_primes()
|
||||
{
|
||||
var session = new MersenneCpuDivisorScanSessionWithCpuOrder();
|
||||
|
||||
ulong[] primes = [31UL, 37UL, 41UL, 43UL, 47UL];
|
||||
ulong cycle223 = MersenneDivisorCyclesCpu.CalculateCycleLength(223UL, MontgomeryDivisorData.FromModulus(223UL));
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primes).Should().BeTrue();
|
||||
|
||||
ulong[] primesBeforeHit = [31UL];
|
||||
session.CheckDivisor(223UL, MontgomeryDivisorData.FromModulus(223UL), cycle223, primesBeforeHit).Should().BeFalse();
|
||||
}
|
||||
hits.Should().ContainInOrder(new byte[] { 0, 1, 0, 0, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
session.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -8,38 +8,30 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberIncrementalCpuTesterTests
|
||||
{
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
[InlineData(GpuKernelType.Pow2Mod)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(GpuKernelType type)
|
||||
{
|
||||
var tester = new MersenneNumberIncrementalCpuTester(type);
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
[InlineData(GpuKernelType.Pow2Mod)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(GpuKernelType type)
|
||||
{
|
||||
var tester = new MersenneNumberIncrementalCpuTester(type);
|
||||
|
||||
if (type == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
RunCase(tester, 11UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 71UL, 341_860UL, expectedPrime: false);
|
||||
}
|
||||
if (type == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
RunCase(tester, 11UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 71UL, 341_860UL, expectedPrime: false);
|
||||
}
|
||||
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
|
||||
private static void RunCase(MersenneNumberIncrementalCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.Scan(gpu, exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
private static void RunCase(MersenneNumberIncrementalCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -8,39 +8,31 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberOrderCpuTesterTests
|
||||
{
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
[InlineData(GpuKernelType.Pow2Mod)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(GpuKernelType type)
|
||||
{
|
||||
var tester = new MersenneNumberOrderCpuTester(type);
|
||||
[Theory]
|
||||
[InlineData(GpuKernelType.Incremental)]
|
||||
[InlineData(GpuKernelType.Pow2Mod)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(GpuKernelType type)
|
||||
{
|
||||
var tester = new MersenneNumberOrderCpuTester(type);
|
||||
|
||||
if (type == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
RunCase(tester, 23UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 36UL, expectedPrime: false);
|
||||
}
|
||||
if (type == GpuKernelType.Pow2Mod)
|
||||
{
|
||||
RunCase(tester, 23UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 36UL, expectedPrime: false);
|
||||
}
|
||||
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
|
||||
private static void RunCase(MersenneNumberOrderCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.Scan(gpu, exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
private static void RunCase(MersenneNumberOrderCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
@@ -45,15 +46,16 @@ public class MersenneNumberOrderGpuTesterTests
|
||||
writer.Write((ulong)uint.MaxValue);
|
||||
}
|
||||
|
||||
var cycles = MersenneDivisorCyclesGpu.Shared;
|
||||
var tableField = typeof(MersenneDivisorCyclesGpu).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
var smallCyclesField = typeof(MersenneDivisorCyclesGpu).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
var cycles = MersenneDivisorCycles.Shared;
|
||||
var tableField = typeof(MersenneDivisorCycles).GetField("_table", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
var smallCyclesField = typeof(MersenneDivisorCycles).GetField("_smallCycles", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
|
||||
var originalTable = ((List<(ulong Divisor, ulong Cycle)>)tableField.GetValue(cycles)!).Select(x => x).ToList();
|
||||
var originalSmall = (ulong[]?)smallCyclesField.GetValue(cycles);
|
||||
ulong[]? backupSmall = originalSmall is null ? null : (ulong[])originalSmall.Clone();
|
||||
|
||||
cycles.LoadFrom(tempPath);
|
||||
GpuContextPool.DisposeAll();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -73,6 +75,7 @@ public class MersenneNumberOrderGpuTesterTests
|
||||
{
|
||||
tableField.SetValue(cycles, originalTable);
|
||||
smallCyclesField.SetValue(cycles, backupSmall);
|
||||
GpuContextPool.DisposeAll();
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -88,3 +91,4 @@ public class MersenneNumberOrderGpuTesterTests
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Cpu;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
@@ -7,25 +8,24 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberResidueCpuTesterTests
|
||||
{
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents()
|
||||
{
|
||||
var tester = new MersenneNumberResidueCpuTester();
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents()
|
||||
{
|
||||
var tester = new MersenneNumberResidueCpuTester();
|
||||
|
||||
RunCase(tester, 23UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 36UL, expectedPrime: false);
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
RunCase(tester, 23UL, 1UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 36UL, expectedPrime: false);
|
||||
RunCase(tester, 89UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_000UL, expectedPrime: true);
|
||||
}
|
||||
|
||||
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
private static void RunCase(MersenneNumberResidueCpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
using UInt128 = System.UInt128;
|
||||
|
||||
@@ -8,36 +8,27 @@ namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class MersenneNumberResidueGpuTesterTests
|
||||
{
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
private static LastDigit GetLastDigit(ulong exponent) => (exponent & 3UL) == 3UL ? LastDigit.Seven : LastDigit.One;
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(bool useGpuOrder)
|
||||
{
|
||||
var tester = new MersenneNumberResidueGpuTester(useGpuOrder);
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Scan_handles_various_prime_exponents(bool useGpuOrder)
|
||||
{
|
||||
var tester = new MersenneNumberResidueGpuTester(useGpuOrder);
|
||||
|
||||
RunCase(tester, 23UL, 2UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 37UL, expectedPrime: false);
|
||||
RunCase(tester, 31UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 89UL, 1_001UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_001UL, expectedPrime: true);
|
||||
}
|
||||
RunCase(tester, 23UL, 2UL, expectedPrime: false);
|
||||
RunCase(tester, 29UL, 37UL, expectedPrime: false);
|
||||
RunCase(tester, 31UL, 1_000UL, expectedPrime: true);
|
||||
RunCase(tester, 89UL, 1_001UL, expectedPrime: true);
|
||||
RunCase(tester, 127UL, 1_001UL, expectedPrime: true);
|
||||
}
|
||||
|
||||
private static void RunCase(MersenneNumberResidueGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.Scan(gpu, exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
private static void RunCase(MersenneNumberResidueGpuTester tester, ulong exponent, ulong maxK, bool expectedPrime)
|
||||
{
|
||||
bool isPrime = true;
|
||||
tester.Scan(exponent, (UInt128)exponent << 1, GetLastDigit(exponent), (UInt128)maxK, ref isPrime);
|
||||
isPrime.Should().Be(expectedPrime);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -8,81 +7,30 @@ namespace PerfectNumbers.Core.Tests;
|
||||
[Trait("Category", "Fast")]
|
||||
public class MersenneNumberTesterTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(136_000_002UL)]
|
||||
[InlineData(136_000_005UL)]
|
||||
public void IsMersennePrime_detects_small_prime_divisors_for_large_exponents(ulong p)
|
||||
{
|
||||
var tester = new MersenneNumberTesterForIncrementalCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsMersennePrime(gpu, p).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(136_000_002UL)]
|
||||
[InlineData(136_000_005UL)]
|
||||
public void IsMersennePrime_detects_small_prime_divisors_for_large_exponents(ulong p)
|
||||
{
|
||||
var tester = new MersenneNumberTester();
|
||||
tester.IsMersennePrime(p).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(125UL)]
|
||||
public void WarmUpOrdersCpu_populates_cache_without_affecting_results(ulong p)
|
||||
{
|
||||
var tester = new MersenneNumberTesterForIncrementalCalculationMethodForEnabledCacheStatusForGpuOrderDeviceForCpu();
|
||||
tester.WarmUpOrders(p, 1_000UL);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsMersennePrime(gpu, p).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(125UL)]
|
||||
public void WarmUpOrders_populates_cache_without_affecting_results(ulong p)
|
||||
{
|
||||
var tester = new MersenneNumberTester(useOrderCache: true, useGpuOrder: true);
|
||||
tester.WarmUpOrders(p, 1_000UL);
|
||||
tester.IsMersennePrime(p).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(125UL)]
|
||||
public void WarmUpOrdersGpu_populates_cache_without_affecting_results(ulong p)
|
||||
{
|
||||
var tester = new MersenneNumberTesterForIncrementalCalculationMethodForEnabledCacheStatusForGpuOrderDeviceForGpu();
|
||||
tester.WarmUpOrders(p, 1_000UL);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsMersennePrime(gpu, p).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(125UL, true)]
|
||||
[InlineData(127UL, false)]
|
||||
public void SharesFactorWithExponentMinusOneCpu_detects_common_factors(ulong p, bool expected)
|
||||
{
|
||||
p.SharesFactorWithExponentMinusOneCpu().Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(125UL, true)]
|
||||
[InlineData(127UL, false)]
|
||||
public void SharesFactorWithExponentMinusOneGpu_detects_common_factors(ulong p, bool expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
p.SharesFactorWithExponentMinusOneGpu(gpu).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(125UL, true)]
|
||||
[InlineData(127UL, false)]
|
||||
public void SharesFactorWithExponentMinusOne_detects_common_factors(ulong p, bool expected)
|
||||
{
|
||||
p.SharesFactorWithExponentMinusOne().Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -7,37 +6,27 @@ namespace PerfectNumbers.Core.Tests;
|
||||
[Trait("Category", "Fast")]
|
||||
public class MersenneResidueModeTests
|
||||
{
|
||||
[Fact]
|
||||
public void IsMersennePrime_residue_mode_rejects_composite_exponent()
|
||||
{
|
||||
var tester = new MersenneNumberTesterForResidueCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu();
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsMersennePrime(gpu, 136_000_002UL).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void IsMersennePrime_residue_mode_rejects_composite_exponent()
|
||||
{
|
||||
var tester = new MersenneNumberTester(
|
||||
useResidue: true,
|
||||
useIncremental: false,
|
||||
useGpuScan: false,
|
||||
useGpuOrder: false);
|
||||
tester.IsMersennePrime(136_000_002UL).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsMersennePrime_residue_mode_accepts_prime_exponent()
|
||||
{
|
||||
var tester = new MersenneNumberTesterForResidueCalculationMethodForDisabledCacheStatusForCpuOrderDeviceForCpu(
|
||||
maxK: 1_000UL);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tester.IsMersennePrime(gpu, 31UL).Should().BeTrue();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void IsMersennePrime_residue_mode_accepts_prime_exponent()
|
||||
{
|
||||
var tester = new MersenneNumberTester(
|
||||
useResidue: true,
|
||||
useIncremental: true,
|
||||
useGpuScan: false,
|
||||
useGpuOrder: false,
|
||||
maxK: 1_000UL);
|
||||
tester.IsMersennePrime(31UL).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -7,150 +6,61 @@ namespace PerfectNumbers.Core.Tests;
|
||||
[Trait("Category", "Fast")]
|
||||
public class ModResidueTrackerTests
|
||||
{
|
||||
[Fact]
|
||||
public void IsDivisibleCpu_advances_residue_for_identity()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
[Fact]
|
||||
public void IsDivisible_advances_residue_for_identity()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
|
||||
tracker.IsDivisibleCpu(5, 2).Should().BeFalse();
|
||||
tracker.IsDivisibleCpu(6, 2).Should().BeTrue();
|
||||
tracker.IsDivisibleCpu(7, 2).Should().BeFalse();
|
||||
}
|
||||
tracker.IsDivisible(5, 2).Should().BeFalse();
|
||||
tracker.IsDivisible(6, 2).Should().BeTrue();
|
||||
tracker.IsDivisible(7, 2).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDivisibleGpu_advances_residue_for_identity()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
[Theory]
|
||||
[InlineData(3UL, 7UL, true)]
|
||||
[InlineData(5UL, 31UL, true)]
|
||||
[InlineData(5UL, 7UL, false)]
|
||||
[InlineData(11UL, 23UL, true)]
|
||||
[InlineData(11UL, 31UL, false)]
|
||||
public void IsDivisible_handles_mersenne_model(ulong p, ulong divisor, bool expected)
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Mersenne);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tracker.IsDivisibleGpu(gpu, 5, 2).Should().BeFalse();
|
||||
tracker.IsDivisibleGpu(gpu, 6, 2).Should().BeTrue();
|
||||
tracker.IsDivisibleGpu(gpu, 7, 2).Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
tracker.IsDivisible(p, divisor).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(3UL, 7UL, true)]
|
||||
[InlineData(5UL, 31UL, true)]
|
||||
[InlineData(5UL, 7UL, false)]
|
||||
[InlineData(11UL, 23UL, true)]
|
||||
[InlineData(11UL, 31UL, false)]
|
||||
public void IsDivisibleCpu_handles_mersenne_model(ulong p, ulong divisor, bool expected)
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Mersenne);
|
||||
tracker.IsDivisibleCpu(p, divisor).Should().Be(expected);
|
||||
}
|
||||
[Fact]
|
||||
public void MergeOrAppend_tracks_existing_and_new_divisors()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
|
||||
[Theory]
|
||||
[InlineData(3UL, 7UL, true)]
|
||||
[InlineData(5UL, 31UL, true)]
|
||||
[InlineData(5UL, 7UL, false)]
|
||||
[InlineData(11UL, 23UL, true)]
|
||||
[InlineData(11UL, 31UL, false)]
|
||||
public void IsDivisibleGpu_handles_mersenne_model(ulong p, ulong divisor, bool expected)
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Mersenne);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppend(10, 3, out bool d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppend(10, 5, out bool d5).Should().BeTrue();
|
||||
d5.Should().BeTrue();
|
||||
|
||||
try
|
||||
{
|
||||
tracker.IsDivisibleGpu(gpu, p, divisor).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppend(10, 4, out bool d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
|
||||
[Fact]
|
||||
public void MergeOrAppendCpu_tracks_existing_and_new_divisors()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
tracker.BeginMerge(11);
|
||||
tracker.MergeOrAppend(11, 3, out d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppend(11, 4, out d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
tracker.MergeOrAppend(11, 5, out d5).Should().BeTrue();
|
||||
d5.Should().BeFalse();
|
||||
}
|
||||
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppendCpu(10, 3, out bool d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppendCpu(10, 5, out bool d5).Should().BeTrue();
|
||||
d5.Should().BeTrue();
|
||||
[Fact]
|
||||
public void IsDivisible_requires_non_decreasing_numbers()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
tracker.IsDivisible(5, 2);
|
||||
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppendCpu(10, 4, out bool d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
|
||||
tracker.BeginMerge(11);
|
||||
tracker.MergeOrAppendCpu(11, 3, out d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppendCpu(11, 4, out d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
tracker.MergeOrAppendCpu(11, 5, out d5).Should().BeTrue();
|
||||
d5.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeOrAppendGpu_tracks_existing_and_new_divisors()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
|
||||
try
|
||||
{
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppendGpu(gpu, 10, 3, out bool d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppendGpu(gpu, 10, 5, out bool d5).Should().BeTrue();
|
||||
d5.Should().BeTrue();
|
||||
|
||||
tracker.BeginMerge(10);
|
||||
tracker.MergeOrAppendGpu(gpu, 10, 4, out bool d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
|
||||
tracker.BeginMerge(11);
|
||||
tracker.MergeOrAppendGpu(gpu, 11, 3, out d3).Should().BeTrue();
|
||||
d3.Should().BeFalse();
|
||||
tracker.MergeOrAppendGpu(gpu, 11, 4, out d4).Should().BeTrue();
|
||||
d4.Should().BeFalse();
|
||||
tracker.MergeOrAppendGpu(gpu, 11, 5, out d5).Should().BeTrue();
|
||||
d5.Should().BeFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDivisibleCpu_requires_non_decreasing_numbers()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
|
||||
tracker.IsDivisibleCpu(5, 2);
|
||||
|
||||
Action act = () => tracker.IsDivisibleCpu(4, 2);
|
||||
act.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsDivisibleGpu_requires_non_decreasing_numbers()
|
||||
{
|
||||
var tracker = new ModResidueTracker(ResidueModel.Identity);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
tracker.IsDivisibleGpu(gpu, 5, 2);
|
||||
|
||||
Action act = () => tracker.IsDivisibleGpu(gpu, 4, 2);
|
||||
act.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
Action act = () => tracker.IsDivisible(4, 2);
|
||||
act.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class PollingSemaphoreTests
|
||||
{
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void TryEnter_AllowsConfiguredConcurrency()
|
||||
{
|
||||
var semaphore = new PollingSemaphore(2, TimeSpan.Zero);
|
||||
|
||||
semaphore.TryEnter().Should().BeTrue();
|
||||
semaphore.TryEnter().Should().BeTrue();
|
||||
semaphore.TryEnter().Should().BeFalse();
|
||||
|
||||
semaphore.Release();
|
||||
semaphore.Release();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public async Task Wait_BlocksUntilRelease()
|
||||
{
|
||||
var semaphore = new PollingSemaphore(1, TimeSpan.FromMilliseconds(1));
|
||||
|
||||
semaphore.TryEnter().Should().BeTrue();
|
||||
|
||||
Task<bool> waiter = Task.Run(() => semaphore.Wait(TimeSpan.FromMilliseconds(200)));
|
||||
|
||||
await Task.Delay(20);
|
||||
semaphore.Release();
|
||||
|
||||
bool entered = await waiter;
|
||||
entered.Should().BeTrue();
|
||||
|
||||
semaphore.Release();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Wait_ReturnsFalseWhenTimeoutHits()
|
||||
{
|
||||
var semaphore = new PollingSemaphore(1, TimeSpan.FromMilliseconds(2));
|
||||
|
||||
semaphore.TryEnter().Should().BeTrue();
|
||||
|
||||
bool acquired = semaphore.Wait(TimeSpan.FromMilliseconds(30));
|
||||
|
||||
acquired.Should().BeFalse();
|
||||
|
||||
semaphore.Release();
|
||||
}
|
||||
}
|
||||
@@ -1,228 +1,198 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class PrimeOrderCalculatorTests
|
||||
{
|
||||
public static IEnumerable<object[]> SmallPrimes()
|
||||
{
|
||||
yield return new object[] { 3UL };
|
||||
yield return new object[] { 5UL };
|
||||
yield return new object[] { 7UL };
|
||||
yield return new object[] { 11UL };
|
||||
yield return new object[] { 13UL };
|
||||
yield return new object[] { 17UL };
|
||||
yield return new object[] { 19UL };
|
||||
yield return new object[] { 31UL };
|
||||
yield return new object[] { 61UL };
|
||||
yield return new object[] { 89UL };
|
||||
yield return new object[] { 127UL };
|
||||
yield return new object[] { 521UL };
|
||||
}
|
||||
public static IEnumerable<object[]> SmallPrimes()
|
||||
{
|
||||
yield return new object[] { 3UL };
|
||||
yield return new object[] { 5UL };
|
||||
yield return new object[] { 7UL };
|
||||
yield return new object[] { 11UL };
|
||||
yield return new object[] { 13UL };
|
||||
yield return new object[] { 17UL };
|
||||
yield return new object[] { 19UL };
|
||||
yield return new object[] { 31UL };
|
||||
yield return new object[] { 61UL };
|
||||
yield return new object[] { 89UL };
|
||||
yield return new object[] { 127UL };
|
||||
yield return new object[] { 521UL };
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> PrimesWithPrevious()
|
||||
{
|
||||
yield return new object[] { 5UL, 3UL };
|
||||
yield return new object[] { 7UL, 5UL };
|
||||
yield return new object[] { 11UL, 7UL };
|
||||
yield return new object[] { 13UL, 11UL };
|
||||
yield return new object[] { 17UL, 13UL };
|
||||
yield return new object[] { 19UL, 17UL };
|
||||
yield return new object[] { 31UL, 29UL };
|
||||
yield return new object[] { 61UL, 59UL };
|
||||
yield return new object[] { 89UL, 83UL };
|
||||
yield return new object[] { 127UL, 113UL };
|
||||
yield return new object[] { 521UL, 509UL };
|
||||
}
|
||||
public static IEnumerable<object[]> PrimesWithPrevious()
|
||||
{
|
||||
yield return new object[] { 5UL, 3UL };
|
||||
yield return new object[] { 7UL, 5UL };
|
||||
yield return new object[] { 11UL, 7UL };
|
||||
yield return new object[] { 13UL, 11UL };
|
||||
yield return new object[] { 17UL, 13UL };
|
||||
yield return new object[] { 19UL, 17UL };
|
||||
yield return new object[] { 31UL, 29UL };
|
||||
yield return new object[] { 61UL, 59UL };
|
||||
yield return new object[] { 89UL, 83UL };
|
||||
yield return new object[] { 127UL, 113UL };
|
||||
yield return new object[] { 521UL, 509UL };
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(SmallPrimes))]
|
||||
public void Calculate_heuristic_mode_matches_naive_cycle_length(ulong prime)
|
||||
{
|
||||
ulong expected = ComputeOrderByDoubling(prime);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(SmallPrimes))]
|
||||
public void Calculate_heuristic_mode_matches_naive_cycle_length(ulong prime)
|
||||
{
|
||||
ulong expected = ComputeOrderByDoubling(prime);
|
||||
|
||||
ulong result = PrimeOrderCalculatorGpu.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorData.FromModulus(prime),
|
||||
PrimeOrderCalculatorConfig.HeuristicDefault);
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorData.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
|
||||
result.Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(PrimesWithPrevious))]
|
||||
public void Calculate_heuristic_mode_matches_strict_mode_even_with_previous_order(ulong prime, ulong previousPrime)
|
||||
{
|
||||
ulong previousOrder = ComputeOrderByDoubling(previousPrime);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(PrimesWithPrevious))]
|
||||
public void Calculate_heuristic_mode_matches_strict_mode_even_with_previous_order(ulong prime, ulong previousPrime)
|
||||
{
|
||||
ulong previousOrder = ComputeOrderByDoubling(previousPrime);
|
||||
ulong heuristic = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder,
|
||||
MontgomeryDivisorData.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
ulong heuristic = PrimeOrderCalculatorGpu.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder,
|
||||
MontgomeryDivisorData.FromModulus(prime),
|
||||
PrimeOrderCalculatorConfig.HeuristicDefault);
|
||||
ulong strict = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorData.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.StrictDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
ulong strict = PrimeOrderCalculatorGpu.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorData.FromModulus(prime),
|
||||
PrimeOrderCalculatorConfig.StrictDefault);
|
||||
heuristic.Should().Be(strict);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 1UL)]
|
||||
[InlineData(3UL, 2UL)]
|
||||
public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder)
|
||||
{
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorData.FromModulus(prime),
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
heuristic.Should().Be(strict);
|
||||
}
|
||||
result.Should().Be(expectedOrder);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 1UL)]
|
||||
[InlineData(3UL, 2UL)]
|
||||
public void Calculate_handles_trivial_primes(ulong prime, ulong expectedOrder)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
ulong result = PrimeOrderCalculatorGpu.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorData.FromModulus(prime),
|
||||
PrimeOrderCalculatorConfig.HeuristicDefault);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_heuristic_mode_returns_unresolved_when_phi_cannot_be_factored()
|
||||
{
|
||||
var config = new PrimeOrderCalculator.PrimeOrderSearchConfig(
|
||||
smallFactorLimit: 1,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 8,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be(expectedOrder);
|
||||
}
|
||||
ulong result = PrimeOrderCalculator.Calculate(
|
||||
prime: 13UL,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorData.FromModulus(13UL),
|
||||
config,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_heuristic_mode_returns_unresolved_when_phi_cannot_be_factored()
|
||||
{
|
||||
var config = new PrimeOrderCalculatorConfig(
|
||||
smallFactorLimit: 1,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 8,
|
||||
strictMode: false);
|
||||
result.Should().Be(ComputeOrderByDoubling(13UL));
|
||||
}
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
ulong result = PrimeOrderCalculatorGpu.Calculate(
|
||||
gpu,
|
||||
prime: 13UL,
|
||||
previousOrder: null,
|
||||
MontgomeryDivisorData.FromModulus(13UL),
|
||||
config);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_heuristic_mode_attempts_candidates_before_falling_back_to_strict_mode()
|
||||
{
|
||||
var heuristicConfig = new PrimeOrderCalculator.PrimeOrderSearchConfig(
|
||||
smallFactorLimit: 2,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 1,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Heuristic);
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be(ComputeOrderByDoubling(13UL));
|
||||
}
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(239UL);
|
||||
ulong heuristic = PrimeOrderCalculator.Calculate(
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
heuristicConfig,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_heuristic_mode_attempts_candidates_before_falling_back_to_strict_mode()
|
||||
{
|
||||
var heuristicConfig = new PrimeOrderCalculatorConfig(
|
||||
smallFactorLimit: 2,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 1,
|
||||
strictMode: false);
|
||||
heuristic.Should().Be(ComputeOrderByDoubling(239UL));
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
MontgomeryDivisorData divisorData = MontgomeryDivisorData.FromModulus(239UL);
|
||||
ulong heuristic = PrimeOrderCalculatorGpu.Calculate(
|
||||
gpu,
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
heuristicConfig);
|
||||
var strictConfig = new PrimeOrderCalculator.PrimeOrderSearchConfig(
|
||||
smallFactorLimit: 2,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 1,
|
||||
mode: PrimeOrderCalculator.PrimeOrderMode.Strict);
|
||||
|
||||
heuristic.Should().Be(ComputeOrderByDoubling(239UL));
|
||||
ulong strict = PrimeOrderCalculator.Calculate(
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
strictConfig,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
var strictConfig = new PrimeOrderCalculatorConfig(
|
||||
smallFactorLimit: 2,
|
||||
pollardRhoMilliseconds: 0,
|
||||
maxPowChecks: 1,
|
||||
strictMode: true);
|
||||
strict.Should().Be(heuristic);
|
||||
}
|
||||
|
||||
ulong strict = PrimeOrderCalculatorGpu.Calculate(
|
||||
gpu,
|
||||
prime: 239UL,
|
||||
previousOrder: null,
|
||||
divisorData,
|
||||
strictConfig);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_handles_128_bit_prime()
|
||||
{
|
||||
UInt128 prime = UInt128.Parse("18446744073709641691");
|
||||
|
||||
strict.Should().Be(heuristic);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
UInt128 result = PrimeOrderCalculator.Calculate(
|
||||
prime,
|
||||
previousOrder: null,
|
||||
PrimeOrderCalculator.PrimeOrderSearchConfig.HeuristicDefault,
|
||||
PrimeOrderCalculator.PrimeOrderHeuristicDevice.Gpu);
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Calculate_handles_128_bit_prime()
|
||||
{
|
||||
UInt128 prime = UInt128.Parse("18446744073709641691");
|
||||
result.Should().Be((UInt128)1229782938247309446UL);
|
||||
}
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
UInt128 result = PrimeOrderCalculatorGpu.Calculate(
|
||||
gpu,
|
||||
prime,
|
||||
previousOrder: null,
|
||||
PrimeOrderCalculatorConfig.HeuristicDefault);
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void GetCycle_uses_wide_prime_heuristic()
|
||||
{
|
||||
UInt128 prime = UInt128.Parse("18446744073709641691");
|
||||
MersenneDivisorCycles.GetCycle(prime).Should().Be((UInt128)1229782938247309446UL);
|
||||
}
|
||||
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
result.Should().Be((UInt128)1229782938247309446UL);
|
||||
}
|
||||
private static ulong ComputeOrderByDoubling(ulong prime)
|
||||
{
|
||||
if (prime == 2UL)
|
||||
{
|
||||
return 1UL;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void GetCycle_uses_wide_prime_heuristic()
|
||||
{
|
||||
UInt128 prime = UInt128.Parse("18446744073709641691");
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
var mersenneDivisorCycles = MersenneDivisorCyclesGpu.GetCycle(gpu, prime).Should().Be((UInt128)1229782938247309446UL);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
if (prime == 3UL)
|
||||
{
|
||||
return 2UL;
|
||||
}
|
||||
|
||||
private static ulong ComputeOrderByDoubling(ulong prime)
|
||||
{
|
||||
if (prime == 2UL)
|
||||
{
|
||||
return 1UL;
|
||||
}
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % prime;
|
||||
|
||||
if (prime == 3UL)
|
||||
{
|
||||
return 2UL;
|
||||
}
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value * 2UL) % prime;
|
||||
order++;
|
||||
}
|
||||
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % prime;
|
||||
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value * 2UL) % prime;
|
||||
order++;
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
@@ -7,28 +6,20 @@ namespace PerfectNumbers.Core.Tests;
|
||||
[Trait("Category", "Fast")]
|
||||
public class PrimeTesterGcdTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(81UL, true)]
|
||||
[InlineData(101UL, false)]
|
||||
public void SharesFactorWithMaxExponent_detects_gcd(ulong n, bool expected)
|
||||
{
|
||||
PrimeTester.SharesFactorWithMaxExponent(n).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[InlineData(81UL, true)]
|
||||
[InlineData(101UL, false)]
|
||||
public void SharesFactorWithMaxExponent_detects_gcd(ulong n, bool expected)
|
||||
{
|
||||
PrimeTester.SharesFactorWithMaxExponent(n).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SharesFactorWithMaxExponentBatch_filters_values()
|
||||
{
|
||||
ulong[] values = [81UL, 101UL];
|
||||
Span<byte> results = stackalloc byte[values.Length];
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeTester.SharesFactorWithMaxExponentBatch(gpu, values, results);
|
||||
results.ToArray().Should().Equal(new byte[] { 1, 0 });
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void SharesFactorWithMaxExponentBatch_filters_values()
|
||||
{
|
||||
ulong[] values = [81UL, 101UL];
|
||||
Span<byte> results = stackalloc byte[values.Length];
|
||||
PrimeTester.SharesFactorWithMaxExponentBatch(values, results);
|
||||
results.ToArray().Should().Equal(new byte[] { 1, 0 });
|
||||
}
|
||||
}
|
||||
@@ -1,98 +1,81 @@
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class PrimeTesterIsPrimeTests
|
||||
{
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
// EvenPerfectBitScanner never routes the tiny values anymore; keep coverage focused on the admissible range handled by production scans so the guards in PrimeTester stay commented out without breaking validation.
|
||||
[InlineData(7UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(11UL, true)]
|
||||
[InlineData(13UL, true)]
|
||||
[InlineData(21UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
[InlineData(133UL, false)]
|
||||
[InlineData(137UL, true)]
|
||||
[InlineData(143UL, false)]
|
||||
[InlineData(199UL, true)]
|
||||
[InlineData(209UL, false)]
|
||||
[InlineData(403UL, false)]
|
||||
[InlineData(341UL, false)]
|
||||
public void IsPrime_returns_expected_results(ulong n, bool expected)
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
// EvenPerfectBitScanner never routes the tiny values anymore; keep coverage focused on the admissible range handled by production scans so the guards in PrimeTester stay commented out without breaking validation.
|
||||
[InlineData(7UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(11UL, true)]
|
||||
[InlineData(13UL, true)]
|
||||
[InlineData(21UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
[InlineData(133UL, false)]
|
||||
[InlineData(137UL, true)]
|
||||
[InlineData(143UL, false)]
|
||||
[InlineData(199UL, true)]
|
||||
[InlineData(209UL, false)]
|
||||
[InlineData(403UL, false)]
|
||||
[InlineData(341UL, false)]
|
||||
public void IsPrime_returns_expected_results(ulong n, bool expected)
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
|
||||
PrimeTester.IsPrimeCpu(n).Should().Be(expected);
|
||||
}
|
||||
PrimeTester.IsPrime(n).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(139UL, true)]
|
||||
[InlineData(189UL, false)]
|
||||
[InlineData(193UL, true)]
|
||||
[InlineData(279UL, false)]
|
||||
public void IsPrimeGpu_uses_residue_specific_tables(ulong n, bool expected)
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(139UL, true)]
|
||||
[InlineData(189UL, false)]
|
||||
[InlineData(193UL, true)]
|
||||
[InlineData(279UL, false)]
|
||||
public void IsPrimeGpu_uses_residue_specific_tables(ulong n, bool expected)
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(gpu, n).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
tester.IsPrimeGpu(n).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeGpu_falls_back_to_cpu_when_forced()
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
PrimeTester.IsPrimeCpu(137UL).Should().BeTrue();
|
||||
PrimeTester.IsPrimeCpu(341UL).Should().BeFalse();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeGpu_falls_back_to_cpu_when_forced()
|
||||
{
|
||||
var tester = new PrimeTester();
|
||||
PrimeTester.IsPrime(137UL).Should().BeTrue();
|
||||
PrimeTester.IsPrime(341UL).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeGpu_handles_parallel_calls_for_known_primes()
|
||||
{
|
||||
GpuPrimeWorkLimiter.SetLimit(64);
|
||||
PrimeTester.GpuBatchSize = 256_000;
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeGpu_handles_parallel_calls_for_known_primes()
|
||||
{
|
||||
GpuPrimeWorkLimiter.SetLimit(64);
|
||||
PrimeTester.GpuBatchSize = 256_000;
|
||||
|
||||
ulong[] primes = [107UL, 127UL, 521UL, 607UL];
|
||||
var results = new bool[primes.Length];
|
||||
ulong[] primes = [107UL, 127UL, 521UL, 607UL];
|
||||
var results = new bool[primes.Length];
|
||||
|
||||
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
Parallel.For(0, primes.Length, i =>
|
||||
{
|
||||
results[i] = PrimeTester.IsPrimeGpu(gpu, primes[i]);
|
||||
});
|
||||
var testers = new ThreadLocal<PrimeTester>(() => new PrimeTester(), trackAllValues: true);
|
||||
Parallel.For(0, primes.Length, i =>
|
||||
{
|
||||
results[i] = testers.Value!.IsPrimeGpu(primes[i]);
|
||||
});
|
||||
|
||||
results.Should().AllBeEquivalentTo(true);
|
||||
results.Should().AllBeEquivalentTo(true);
|
||||
|
||||
Parallel.For(0, Environment.ProcessorCount * 16, _ =>
|
||||
{
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
PrimeTester.IsPrimeGpu(gpu, prime).Should().BeTrue();
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
Parallel.For(0, Environment.ProcessorCount * 16, _ =>
|
||||
{
|
||||
foreach (ulong prime in primes)
|
||||
{
|
||||
testers.Value!.IsPrimeGpu(prime).Should().BeTrue();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,305 +1,284 @@
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class UInt128ExtensionsTests
|
||||
{
|
||||
public static IEnumerable<object[]> BinaryGcdCases()
|
||||
{
|
||||
yield return new object[] { UInt128.Zero, (UInt128)5, (UInt128)5 };
|
||||
yield return new object[] { (UInt128)18, (UInt128)24, (UInt128)6 };
|
||||
public static IEnumerable<object[]> BinaryGcdCases()
|
||||
{
|
||||
yield return new object[] { UInt128.Zero, (UInt128)5, (UInt128)5 };
|
||||
yield return new object[] { (UInt128)18, (UInt128)24, (UInt128)6 };
|
||||
|
||||
UInt128 big1 = (UInt128.One << 96) + 12345;
|
||||
UInt128 big2 = (UInt128.One << 80) + 67890;
|
||||
yield return new object[]
|
||||
{
|
||||
big1,
|
||||
big2,
|
||||
(UInt128)BigInteger.GreatestCommonDivisor((BigInteger)big1, (BigInteger)big2)
|
||||
};
|
||||
}
|
||||
UInt128 big1 = (UInt128.One << 96) + 12345;
|
||||
UInt128 big2 = (UInt128.One << 80) + 67890;
|
||||
yield return new object[]
|
||||
{
|
||||
big1,
|
||||
big2,
|
||||
(UInt128)BigInteger.GreatestCommonDivisor((BigInteger)big1, (BigInteger)big2)
|
||||
};
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(BinaryGcdCases))]
|
||||
public void BinaryGcd_matches_BigInteger(UInt128 a, UInt128 b, UInt128 expected)
|
||||
{
|
||||
a.BinaryGcd(b).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(BinaryGcdCases))]
|
||||
public void BinaryGcd_matches_BigInteger(UInt128 a, UInt128 b, UInt128 expected)
|
||||
{
|
||||
a.BinaryGcd(b).Should().Be(expected);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> TrailingZeroCases()
|
||||
{
|
||||
yield return new object[] { UInt128.Zero, 128 };
|
||||
yield return new object[] { (UInt128)1, 0 };
|
||||
yield return new object[] { (UInt128)8, 3 };
|
||||
yield return new object[] { UInt128.One << 80, 80 };
|
||||
}
|
||||
public static IEnumerable<object[]> TrailingZeroCases()
|
||||
{
|
||||
yield return new object[] { UInt128.Zero, 128 };
|
||||
yield return new object[] { (UInt128)1, 0 };
|
||||
yield return new object[] { (UInt128)8, 3 };
|
||||
yield return new object[] { UInt128.One << 80, 80 };
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(TrailingZeroCases))]
|
||||
public void CountTrailingZeros_returns_expected(UInt128 value, int expected)
|
||||
{
|
||||
value.CountTrailingZeros().Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[MemberData(nameof(TrailingZeroCases))]
|
||||
public void CountTrailingZeros_returns_expected(UInt128 value, int expected)
|
||||
{
|
||||
value.CountTrailingZeros().Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(3UL)]
|
||||
[InlineData(5UL)]
|
||||
[InlineData(7UL)]
|
||||
public void CalculateOrderCpu_matches_naive(ulong q)
|
||||
{
|
||||
UInt128 q128 = q;
|
||||
ulong expected = NaiveOrder(q);
|
||||
q128.CalculateOrderCpu().Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(3UL)]
|
||||
[InlineData(5UL)]
|
||||
[InlineData(7UL)]
|
||||
public void CalculateOrder_matches_naive(ulong q)
|
||||
{
|
||||
UInt128 q128 = q;
|
||||
ulong expected = NaiveOrder(q);
|
||||
q128.CalculateOrder().Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(3UL)]
|
||||
[InlineData(5UL)]
|
||||
[InlineData(7UL)]
|
||||
public void CalculateOrderGpu_matches_naive(ulong q)
|
||||
{
|
||||
UInt128 q128 = q;
|
||||
ulong expected = NaiveOrder(q);
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
q128.CalculateOrderGpu(gpu).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
private static ulong NaiveOrder(ulong q)
|
||||
{
|
||||
if (q <= 2UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
private static ulong NaiveOrder(ulong q)
|
||||
{
|
||||
if (q <= 2UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % q;
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value << 1) % q;
|
||||
order++;
|
||||
}
|
||||
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % q;
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value << 1) % q;
|
||||
order++;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
public void IsPrimeCandidate_checks_small(ulong n, bool expected)
|
||||
{
|
||||
((UInt128)n).IsPrimeCandidate().Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
public void IsPrimeCandidate_checks_small(ulong n, bool expected)
|
||||
{
|
||||
((UInt128)n).IsPrimeCandidate().Should().Be(expected);
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeCandidate_detects_large_composite()
|
||||
{
|
||||
UInt128 n = (UInt128.One << 64) + 1;
|
||||
n.IsPrimeCandidate().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void IsPrimeCandidate_detects_large_composite()
|
||||
{
|
||||
UInt128 n = (UInt128.One << 64) + 1;
|
||||
n.IsPrimeCandidate().Should().BeFalse();
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod10_8_5_3_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345678901234567890UL;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod10_8_5_3_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345678901234567890UL;
|
||||
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);
|
||||
ulong expected10 = (ulong)(value % 10);
|
||||
ulong expected8 = (ulong)(value % 8);
|
||||
ulong expected5 = (ulong)(value % 5);
|
||||
ulong expected3 = (ulong)(value % 3);
|
||||
|
||||
ulong expected10 = (ulong)(value % 10);
|
||||
ulong expected8 = (ulong)(value % 8);
|
||||
ulong expected5 = (ulong)(value % 5);
|
||||
ulong expected3 = (ulong)(value % 3);
|
||||
mod10.Should().Be(expected10);
|
||||
mod8.Should().Be(expected8);
|
||||
mod5.Should().Be(expected5);
|
||||
mod3.Should().Be(expected3);
|
||||
}
|
||||
|
||||
mod10.Should().Be(expected10);
|
||||
mod8.Should().Be(expected8);
|
||||
mod5.Should().Be(expected5);
|
||||
mod3.Should().Be(expected3);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 0UL)]
|
||||
[InlineData(2UL, 1UL)]
|
||||
[InlineData(2UL, 63UL)]
|
||||
[InlineData(2UL, 127UL)]
|
||||
[InlineData(3UL, 5UL)]
|
||||
public void Pow_matches_BigInteger(ulong value, ulong exponent)
|
||||
{
|
||||
UInt128 v = value;
|
||||
UInt128 expected = (UInt128)BigPow(value, exponent);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 0UL)]
|
||||
[InlineData(2UL, 1UL)]
|
||||
[InlineData(2UL, 63UL)]
|
||||
[InlineData(2UL, 127UL)]
|
||||
[InlineData(3UL, 5UL)]
|
||||
public void Pow_matches_BigInteger(ulong value, ulong exponent)
|
||||
{
|
||||
UInt128 v = value;
|
||||
UInt128 expected = (UInt128)BigPow(value, exponent);
|
||||
v.Pow(exponent).Should().Be(expected);
|
||||
}
|
||||
|
||||
v.Pow(exponent).Should().Be(expected);
|
||||
}
|
||||
private static BigInteger BigPow(ulong value, ulong exponent)
|
||||
{
|
||||
BigInteger result = BigInteger.One;
|
||||
BigInteger baseValue = value;
|
||||
|
||||
private static BigInteger BigPow(ulong value, ulong exponent)
|
||||
{
|
||||
BigInteger result = BigInteger.One;
|
||||
BigInteger baseValue = value;
|
||||
while (exponent != 0UL)
|
||||
{
|
||||
if ((exponent & 1UL) != 0UL)
|
||||
{
|
||||
result *= baseValue;
|
||||
}
|
||||
|
||||
while (exponent != 0UL)
|
||||
{
|
||||
if ((exponent & 1UL) != 0UL)
|
||||
{
|
||||
result *= baseValue;
|
||||
}
|
||||
baseValue *= baseValue;
|
||||
exponent >>= 1;
|
||||
}
|
||||
|
||||
baseValue *= baseValue;
|
||||
exponent >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 10UL, 17UL)]
|
||||
[InlineData(123456789UL, 3UL, 97UL)]
|
||||
public void ModPow_matches_BigInteger(ulong value, ulong exponent, ulong modulus)
|
||||
{
|
||||
UInt128 v = value;
|
||||
UInt128 e = exponent;
|
||||
UInt128 m = modulus;
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 10UL, 17UL)]
|
||||
[InlineData(123456789UL, 3UL, 97UL)]
|
||||
public void ModPow_matches_BigInteger(ulong value, ulong exponent, ulong modulus)
|
||||
{
|
||||
UInt128 v = value;
|
||||
UInt128 e = exponent;
|
||||
UInt128 m = modulus;
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(value, exponent, modulus);
|
||||
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(value, exponent, modulus);
|
||||
v.ModPow(e, m).Should().Be(expected);
|
||||
}
|
||||
|
||||
v.ModPow(e, m).Should().Be(expected);
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ModPow_handles_large_values()
|
||||
{
|
||||
UInt128 v = (UInt128.One << 96) + 12345;
|
||||
UInt128 e = 17;
|
||||
UInt128 m = 97;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void ModPow_handles_large_values()
|
||||
{
|
||||
UInt128 v = (UInt128.One << 96) + 12345;
|
||||
UInt128 e = 17;
|
||||
UInt128 m = 97;
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow((BigInteger)v, (BigInteger)e, (BigInteger)m);
|
||||
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow((BigInteger)v, (BigInteger)e, (BigInteger)m);
|
||||
v.ModPow(e, m).Should().Be(expected);
|
||||
}
|
||||
|
||||
v.ModPow(e, m).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL)]
|
||||
[InlineData(1UL)]
|
||||
[InlineData(123456789UL)]
|
||||
public void Mod10_matches_operator(ulong low)
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + low;
|
||||
value.Mod10().Should().Be((ulong)(value % 10));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL)]
|
||||
[InlineData(1UL)]
|
||||
[InlineData(123456789UL)]
|
||||
public void Mod10_matches_operator(ulong low)
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + low;
|
||||
value.Mod10().Should().Be((ulong)(value % 10));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod8_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod8().Should().Be((ulong)(value % 8));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod8_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod8().Should().Be((ulong)(value % 8));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod3_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod3().Should().Be((ulong)(value % 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod3_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
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]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod6_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 9876543210UL;
|
||||
value.Mod6().Should().Be((ulong)(value % 6));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod5_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod5().Should().Be((ulong)(value % 5));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod5_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 12345;
|
||||
value.Mod5().Should().Be((ulong)(value % 5));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod7_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 9876543210987654321UL;
|
||||
value.Mod7().Should().Be((ulong)(value % 7));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod7_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 100) + 9876543210987654321UL;
|
||||
value.Mod7().Should().Be((ulong)(value % 7));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod11_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 120) + 9876543210987654321UL;
|
||||
value.Mod11().Should().Be((ulong)(value % 11));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod11_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 120) + 9876543210987654321UL;
|
||||
value.Mod11().Should().Be((ulong)(value % 11));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod128_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 120) + 9876543210987654321UL;
|
||||
value.Mod128().Should().Be((ulong)(value % 128));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mod128_matches_operator()
|
||||
{
|
||||
UInt128 value = (UInt128.One << 120) + 9876543210987654321UL;
|
||||
value.Mod128().Should().Be((ulong)(value % 128));
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mul64_matches_expected_product()
|
||||
{
|
||||
UInt128 a = (UInt128.One << 80) + 123456789;
|
||||
UInt128 b = (UInt128.One << 64) + 987654321;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void Mul64_matches_expected_product()
|
||||
{
|
||||
UInt128 a = (UInt128.One << 80) + 123456789;
|
||||
UInt128 b = (UInt128.One << 64) + 987654321;
|
||||
UInt128 expected = (UInt128)((UInt128)(ulong)a * b);
|
||||
|
||||
UInt128 expected = (UInt128)((UInt128)(ulong)a * b);
|
||||
a.Mul64(b).Should().Be(expected);
|
||||
}
|
||||
|
||||
a.Mul64(b).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(123UL, 456UL, 789UL)]
|
||||
[InlineData(123456789UL, 987654321UL, 1009UL)]
|
||||
public void MulMod_matches_BigInteger(ulong a, ulong b, ulong modulus)
|
||||
{
|
||||
UInt128 ua = a % modulus;
|
||||
UInt128 ub = b % modulus;
|
||||
UInt128 mod = modulus;
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(123UL, 456UL, 789UL)]
|
||||
[InlineData(123456789UL, 987654321UL, 1009UL)]
|
||||
public void MulMod_matches_BigInteger(ulong a, ulong b, ulong modulus)
|
||||
{
|
||||
UInt128 ua = a % modulus;
|
||||
UInt128 ub = b % modulus;
|
||||
UInt128 mod = modulus;
|
||||
UInt128 expected = (UInt128)(((BigInteger)ua * (BigInteger)ub) % modulus);
|
||||
|
||||
UInt128 expected = (UInt128)(((BigInteger)ua * (BigInteger)ub) % modulus);
|
||||
ua.MulMod(ub, mod).Should().Be(expected);
|
||||
}
|
||||
|
||||
ua.MulMod(ub, mod).Should().Be(expected);
|
||||
}
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void MulMod_handles_large_inputs()
|
||||
{
|
||||
UInt128 a = (UInt128.One << 80) + 12345;
|
||||
UInt128 b = (UInt128.One << 96) + 67890;
|
||||
UInt128 mod = (UInt128.One << 100) - 93;
|
||||
|
||||
[Fact]
|
||||
[Trait("Category", "Fast")]
|
||||
public void MulMod_handles_large_inputs()
|
||||
{
|
||||
UInt128 a = (UInt128.One << 80) + 12345;
|
||||
UInt128 b = (UInt128.One << 96) + 67890;
|
||||
UInt128 mod = (UInt128.One << 100) - 93;
|
||||
UInt128 expected = (UInt128)(((BigInteger)a * (BigInteger)b) % (BigInteger)mod);
|
||||
|
||||
UInt128 expected = (UInt128)(((BigInteger)a * (BigInteger)b) % (BigInteger)mod);
|
||||
|
||||
a.MulMod(b, mod).Should().Be(expected);
|
||||
}
|
||||
a.MulMod(b, mod).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,282 +1,225 @@
|
||||
using System.Numerics;
|
||||
using FluentAssertions;
|
||||
using PerfectNumbers.Core.Gpu.Accelerators;
|
||||
using Xunit;
|
||||
|
||||
namespace PerfectNumbers.Core.Tests;
|
||||
|
||||
public class ULongExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL)]
|
||||
[InlineData(1UL)]
|
||||
[InlineData(9UL)]
|
||||
[InlineData(10UL)]
|
||||
[InlineData(1234567890UL)]
|
||||
[InlineData(ulong.MaxValue)]
|
||||
public void Mod10_returns_same_as_operator(ulong value)
|
||||
{
|
||||
byte expected = (byte)(value % 10UL);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL)]
|
||||
[InlineData(1UL)]
|
||||
[InlineData(9UL)]
|
||||
[InlineData(10UL)]
|
||||
[InlineData(1234567890UL)]
|
||||
[InlineData(ulong.MaxValue)]
|
||||
public void Mod10_returns_same_as_operator(ulong value)
|
||||
{
|
||||
ulong expected = value % 10UL;
|
||||
|
||||
ULongExtensions.Mod10(value).Should().Be(expected);
|
||||
}
|
||||
ULongExtensions.Mod10(value).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL)]
|
||||
[InlineData(ulong.MaxValue)]
|
||||
[InlineData(1234567890123456789UL)]
|
||||
public void Mod128_matches_operator(ulong value)
|
||||
{
|
||||
value.Mod128().Should().Be(value % 128UL);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL)]
|
||||
[InlineData(ulong.MaxValue)]
|
||||
[InlineData(1234567890123456789UL)]
|
||||
public void Mod128_matches_operator(ulong value)
|
||||
{
|
||||
value.Mod128().Should().Be(value % 128UL);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(1UL)]
|
||||
[InlineData(123456789UL)]
|
||||
[InlineData(ulong.MaxValue)]
|
||||
public void Mod10_8_5_3Steps_matches_manual_formula(ulong value)
|
||||
{
|
||||
value.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(1UL)]
|
||||
[InlineData(123456789UL)]
|
||||
[InlineData(ulong.MaxValue)]
|
||||
public void Mod10_8_5_3Steps_matches_manual_formula(ulong value)
|
||||
{
|
||||
value.Mod10_8_5_3Steps(out ulong step10, out ulong step8, out ulong step5, out ulong step3);
|
||||
|
||||
ulong expected10 = ((value % 10UL) << 1) % 10UL;
|
||||
ulong expected8 = ((value & 7UL) << 1) & 7UL;
|
||||
ulong expected5 = ((value % 5UL) << 1) % 5UL;
|
||||
ulong expected3 = ((value % 3UL) << 1) % 3UL;
|
||||
ulong expected10 = ((value % 10UL) << 1) % 10UL;
|
||||
ulong expected8 = ((value & 7UL) << 1) & 7UL;
|
||||
ulong expected5 = ((value % 5UL) << 1) % 5UL;
|
||||
ulong expected3 = ((value % 3UL) << 1) % 3UL;
|
||||
|
||||
step10.Should().Be(expected10);
|
||||
step8.Should().Be(expected8);
|
||||
step5.Should().Be(expected5);
|
||||
step3.Should().Be(expected3);
|
||||
}
|
||||
step10.Should().Be(expected10);
|
||||
step8.Should().Be(expected8);
|
||||
step5.Should().Be(expected5);
|
||||
step3.Should().Be(expected3);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
public void IsPrimeCandidate_filters_composites(ulong candidate, bool expected)
|
||||
{
|
||||
candidate.IsPrimeCandidate().Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, true)]
|
||||
[InlineData(9UL, false)]
|
||||
[InlineData(97UL, true)]
|
||||
public void IsPrimeCandidate_filters_composites(ulong candidate, bool expected)
|
||||
{
|
||||
candidate.IsPrimeCandidate().Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(3UL)]
|
||||
[InlineData(5UL)]
|
||||
[InlineData(7UL)]
|
||||
[InlineData(11UL)]
|
||||
public void CalculateOrderCpu_matches_naive_algorithm(ulong q)
|
||||
{
|
||||
ulong expected = NaiveOrder(q);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(3UL)]
|
||||
[InlineData(5UL)]
|
||||
[InlineData(7UL)]
|
||||
[InlineData(11UL)]
|
||||
public void CalculateOrder_matches_naive_algorithm(ulong q)
|
||||
{
|
||||
ulong expected = NaiveOrder(q);
|
||||
|
||||
q.CalculateOrderCpu().Should().Be(expected);
|
||||
}
|
||||
q.CalculateOrder().Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(3UL)]
|
||||
[InlineData(5UL)]
|
||||
[InlineData(7UL)]
|
||||
[InlineData(11UL)]
|
||||
public void CalculateOrderGpu_matches_naive_algorithm(ulong q)
|
||||
{
|
||||
ulong expected = NaiveOrder(q);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 10UL, 17UL)]
|
||||
[InlineData(123456789UL, 3UL, 97UL)]
|
||||
public void ModPow64_matches_BigInteger(ulong value, ulong exponent, ulong modulus)
|
||||
{
|
||||
ulong expected = (ulong)BigInteger.ModPow(value, exponent, modulus);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
q.CalculateOrderGpu(gpu).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
value.ModPow64(exponent, modulus).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(2UL, 10UL, 17UL)]
|
||||
[InlineData(123456789UL, 3UL, 97UL)]
|
||||
public void ModPow64_matches_BigInteger(ulong value, ulong exponent, ulong modulus)
|
||||
{
|
||||
ulong expected = (ulong)BigInteger.ModPow(value, exponent, modulus);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(10UL, 17UL)]
|
||||
[InlineData(123UL, 97UL)]
|
||||
public void PowMod_returns_same_as_BigInteger(ulong exponent, ulong modulus)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
|
||||
value.ModPow64(exponent, modulus).Should().Be(expected);
|
||||
}
|
||||
exponent.PowMod((UInt128)modulus).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(10UL, 17UL)]
|
||||
[InlineData(123UL, 97UL)]
|
||||
public void PowMod_returns_same_as_BigInteger(ulong exponent, ulong modulus)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(5UL, 7UL, 3UL, 4UL)]
|
||||
[InlineData(10UL, 7UL, 3UL, 2UL)]
|
||||
public void PowModWithCycle_uses_cycle_length(ulong exponent, ulong modulus, ulong cycle, ulong expected)
|
||||
{
|
||||
((UInt128)exponent).PowModWithCycle((UInt128)modulus, cycle).Should().Be((UInt128)expected);
|
||||
}
|
||||
|
||||
exponent.PowMod((UInt128)modulus).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(15UL, true)]
|
||||
[InlineData(7UL, false)]
|
||||
[InlineData(9UL, false)]
|
||||
public void SharesFactorWithExponentMinusOne_detects_relationship(ulong exponent, bool expected)
|
||||
{
|
||||
exponent.SharesFactorWithExponentMinusOne().Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(5UL, 7UL, 3UL, 4UL)]
|
||||
[InlineData(10UL, 7UL, 3UL, 2UL)]
|
||||
public void PowModWithCycle_uses_cycle_length(ulong exponent, ulong modulus, ulong cycle, ulong expected)
|
||||
{
|
||||
((UInt128)exponent).PowModWithCycle((UInt128)modulus, cycle).Should().Be((UInt128)expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(1234567890123456789UL, 3UL)]
|
||||
[InlineData(ulong.MaxValue, 7UL)]
|
||||
public void FastDiv64_matches_division(ulong value, ulong divisor)
|
||||
{
|
||||
ulong mul = (ulong)(((UInt128)1 << 64) / divisor);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(15UL, true)]
|
||||
[InlineData(7UL, false)]
|
||||
[InlineData(9UL, false)]
|
||||
public void SharesFactorWithExponentMinusOneCpu_detects_relationship(ulong exponent, bool expected)
|
||||
{
|
||||
exponent.SharesFactorWithExponentMinusOneCpu().Should().Be(expected);
|
||||
}
|
||||
value.FastDiv64(divisor, mul).Should().Be(value / divisor);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(15UL, true)]
|
||||
[InlineData(7UL, false)]
|
||||
[InlineData(9UL, false)]
|
||||
public void SharesFactorWithExponentMinusOneGpu_detects_relationship(ulong exponent, bool expected)
|
||||
{
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
exponent.SharesFactorWithExponentMinusOneGpu(gpu).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL, 0UL)]
|
||||
[InlineData(ulong.MaxValue, 1UL)]
|
||||
[InlineData(123456789UL, 987654321UL)]
|
||||
public void MulHighCpu_matches_high_bits(ulong x, ulong y)
|
||||
{
|
||||
ulong expected = (ulong)(((UInt128)x * y) >> 64);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(1234567890123456789UL, 3UL)]
|
||||
[InlineData(ulong.MaxValue, 7UL)]
|
||||
public void FastDiv64_matches_division(ulong value, ulong divisor)
|
||||
{
|
||||
ulong mul = (ulong)(((UInt128)1 << 64) / divisor);
|
||||
x.MulHighCpu(y).Should().Be(expected);
|
||||
}
|
||||
|
||||
value.FastDiv64(divisor, mul).Should().Be(value / divisor);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL, 0UL)]
|
||||
[InlineData(ulong.MaxValue, 1UL)]
|
||||
[InlineData(123456789UL, 987654321UL)]
|
||||
public void MulHighGpu_matches_cpu_baseline(ulong x, ulong y)
|
||||
{
|
||||
x.MulHighGpu(y).Should().Be(x.MulHighCpu(y));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL, 0UL)]
|
||||
[InlineData(ulong.MaxValue, 1UL)]
|
||||
[InlineData(123456789UL, 987654321UL)]
|
||||
public void MulHighCpu_matches_high_bits(ulong x, ulong y)
|
||||
{
|
||||
ulong expected = (ulong)(((UInt128)x * y) >> 64);
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL, 0UL)]
|
||||
[InlineData(ulong.MaxValue, 2UL)]
|
||||
[InlineData(123456789UL, 987654321UL)]
|
||||
public void Mul64_matches_UInt128_product(ulong a, ulong b)
|
||||
{
|
||||
UInt128 expected = (UInt128)a * b;
|
||||
|
||||
x.MulHighCpu(y).Should().Be(expected);
|
||||
}
|
||||
a.Mul64(b).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL, 0UL)]
|
||||
[InlineData(ulong.MaxValue, 1UL)]
|
||||
[InlineData(123456789UL, 987654321UL)]
|
||||
public void MulHighGpu_matches_cpu_baseline(ulong x, ulong y)
|
||||
{
|
||||
x.MulHighGpu(y).Should().Be(x.MulHighCpu(y));
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(123456789UL, 987654321UL, 97UL)]
|
||||
[InlineData(ulong.MaxValue, ulong.MaxValue, 1009UL)]
|
||||
public void MulMod64_matches_BigInteger(ulong a, ulong b, ulong modulus)
|
||||
{
|
||||
ulong expected = (ulong)BigInteger.Remainder((BigInteger)a * b, modulus);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(0UL, 0UL)]
|
||||
[InlineData(ulong.MaxValue, 2UL)]
|
||||
[InlineData(123456789UL, 987654321UL)]
|
||||
public void Mul64_matches_UInt128_product(ulong a, ulong b)
|
||||
{
|
||||
UInt128 expected = (UInt128)a * b;
|
||||
a.MulMod64(b, modulus).Should().Be(expected);
|
||||
}
|
||||
|
||||
a.Mul64(b).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(100UL, 7UL, 3UL)]
|
||||
[InlineData(123UL, 9UL, 6UL)]
|
||||
public void PowModWithCycle_ulong_overload_uses_cycle(ulong exponent, ulong modulus, ulong cycle)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(123456789UL, 987654321UL, 97UL)]
|
||||
[InlineData(ulong.MaxValue, ulong.MaxValue, 1009UL)]
|
||||
public void MulMod64_matches_BigInteger(ulong a, ulong b, ulong modulus)
|
||||
{
|
||||
ulong expected = (ulong)BigInteger.Remainder((BigInteger)a * b, modulus);
|
||||
exponent.PowModWithCycle((UInt128)modulus, cycle).Should().Be(expected);
|
||||
}
|
||||
|
||||
a.MulMod64(b, modulus).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(100UL, 7UL, 3UL)]
|
||||
[InlineData(123UL, 9UL, 6UL)]
|
||||
public void PowModWithCycle_ulong_uint128_cycle_uses_cycle(ulong exponent, ulong modulus, ulong cycle)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(100UL, 7UL, 3UL)]
|
||||
[InlineData(123UL, 9UL, 6UL)]
|
||||
public void PowModWithCycle_ulong_overload_uses_cycle(ulong exponent, ulong modulus, ulong cycle)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
exponent.PowModWithCycle((UInt128)modulus, (UInt128)cycle).Should().Be(expected);
|
||||
}
|
||||
|
||||
exponent.PowModWithCycle((UInt128)modulus, cycle).Should().Be(expected);
|
||||
}
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(11UL, 13UL)]
|
||||
public void PowModCrt_matches_BigInteger(ulong exponent, ulong modulus)
|
||||
{
|
||||
MersenneDivisorCycles cycles = MersenneDivisorCycles.Shared;
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(100UL, 7UL, 3UL)]
|
||||
[InlineData(123UL, 9UL, 6UL)]
|
||||
public void PowModWithCycle_ulong_uint128_cycle_uses_cycle(ulong exponent, ulong modulus, ulong cycle)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
exponent.PowModCrt((UInt128)modulus, cycles).Should().Be(expected);
|
||||
}
|
||||
|
||||
exponent.PowModWithCycle((UInt128)modulus, (UInt128)cycle).Should().Be(expected);
|
||||
}
|
||||
private static ulong NaiveOrder(ulong q)
|
||||
{
|
||||
if (q <= 2UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(11UL, 13UL)]
|
||||
public void PowModCrtCpu_matches_BigInteger(ulong exponent, ulong modulus)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % q;
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value << 1) % q;
|
||||
order++;
|
||||
}
|
||||
|
||||
exponent.PowModCrtCpu((UInt128)modulus).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Category", "Fast")]
|
||||
[InlineData(11UL, 13UL)]
|
||||
public void PowModCrtGpu_matches_BigInteger(ulong exponent, ulong modulus)
|
||||
{
|
||||
UInt128 expected = (UInt128)BigInteger.ModPow(2, exponent, modulus);
|
||||
|
||||
var gpu = PrimeOrderCalculatorAccelerator.Rent(1);
|
||||
try
|
||||
{
|
||||
exponent.PowModCrtGpu(gpu, (UInt128)modulus).Should().Be(expected);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PrimeOrderCalculatorAccelerator.Return(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong NaiveOrder(ulong q)
|
||||
{
|
||||
if (q <= 2UL)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
ulong order = 1UL;
|
||||
ulong value = 2UL % q;
|
||||
while (value != 1UL)
|
||||
{
|
||||
value = (value << 1) % q;
|
||||
order++;
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
public struct AscendingDivisorRemainderStepper
|
||||
{
|
||||
private readonly ulong _dividend;
|
||||
private ulong _maxDivisor;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
|
||||
public AscendingDivisorRemainderStepper(ulong value)
|
||||
{
|
||||
_dividend = value;
|
||||
ulong root = (ulong)Math.Sqrt(value);
|
||||
if ((UInt128)root * root < value)
|
||||
{
|
||||
root++;
|
||||
}
|
||||
|
||||
_maxDivisor = root;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public readonly bool ShouldContinue(ulong divisor) => divisor <= _maxDivisor;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public bool Divides(ulong divisor)
|
||||
{
|
||||
ulong remainder = _dividend;
|
||||
ulong quotient = remainder / divisor;
|
||||
remainder -= quotient * divisor;
|
||||
if (quotient < _maxDivisor)
|
||||
{
|
||||
_maxDivisor = quotient;
|
||||
}
|
||||
|
||||
return remainder == 0UL;
|
||||
}
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
[method: MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public sealed class BTree<TKey, TValue>()
|
||||
{
|
||||
private const int HashByteCount = sizeof(int);
|
||||
private const int DefaultCapacity = 64;
|
||||
private const int SmallBufferCapacity = 4;
|
||||
private const int RootInlineThreshold = 2048;
|
||||
private const int RootInlineInitialCapacity = 32;
|
||||
private const int ShallowTreeThreshold = 1024;
|
||||
private const int ShallowHashByteCount = 1;
|
||||
private const int LeafInitialCapacity = 8;
|
||||
private static readonly EqualityComparer<TKey> _defaultComparer = EqualityComparer<TKey>.Default;
|
||||
|
||||
private readonly TKey[] _smallKeys = new TKey[SmallBufferCapacity];
|
||||
private readonly TValue[] _smallValues = new TValue[SmallBufferCapacity];
|
||||
private int _smallCount;
|
||||
|
||||
private Node[] _nodes = [];
|
||||
private int _nodeCount;
|
||||
|
||||
public int Count;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
TKey[] smallKeys = _smallKeys;
|
||||
TValue[] smallValues = _smallValues;
|
||||
int smallCount = _smallCount;
|
||||
if (smallCount < SmallBufferCapacity)
|
||||
{
|
||||
smallKeys[smallCount] = key;
|
||||
smallValues[smallCount] = value;
|
||||
_smallCount = smallCount + 1;
|
||||
Count++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_nodeCount == 0)
|
||||
{
|
||||
EnsureRoot();
|
||||
}
|
||||
|
||||
Node[] nodes = _nodes;
|
||||
if (smallCount > 0)
|
||||
{
|
||||
ref Node rootNode = ref nodes[0];
|
||||
for (int i = 0; i < smallCount; i++)
|
||||
{
|
||||
rootNode.AddKeyValue(smallKeys[i], smallValues[i], RootInlineInitialCapacity);
|
||||
}
|
||||
|
||||
_smallCount = 0;
|
||||
}
|
||||
|
||||
if (Count < RootInlineThreshold)
|
||||
{
|
||||
ref Node root = ref nodes[0];
|
||||
root.AddKeyValue(key, value, RootInlineInitialCapacity);
|
||||
Count++;
|
||||
return;
|
||||
}
|
||||
|
||||
InsertIntoTree(GetHash(key), key, value, Count);
|
||||
Count++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||
public int CopyTo(Span<TValue> destination)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(destination.Length, Count);
|
||||
|
||||
var written = 0;
|
||||
|
||||
int smallCount = _smallCount;
|
||||
TValue[] smallValues = _smallValues;
|
||||
for (int i = 0; i < smallCount; i++)
|
||||
{
|
||||
destination[written++] = smallValues[i];
|
||||
}
|
||||
|
||||
int nodeCount = _nodeCount;
|
||||
if (nodeCount == 0)
|
||||
{
|
||||
return written;
|
||||
}
|
||||
|
||||
FixedCapacityStack<int> stack = new(nodeCount);
|
||||
stack.Push(0);
|
||||
|
||||
Node[]? nodes = _nodes;
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
int index = stack.Pop();
|
||||
ref readonly Node node = ref nodes[index];
|
||||
written += node.CopyValues(destination[written..]);
|
||||
|
||||
for (int childIndex = 0; childIndex < Node.ChildCount; childIndex++)
|
||||
{
|
||||
index = node.GetChild(childIndex);
|
||||
if (index != -1)
|
||||
{
|
||||
stack.Push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static uint GetHash(TKey key)
|
||||
{
|
||||
return key is not null
|
||||
? unchecked((uint)_defaultComparer.GetHashCode(key))
|
||||
: 0u;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||||
private void EnsureRoot()
|
||||
{
|
||||
_nodes = new Node[DefaultCapacity];
|
||||
_nodes[0] = new Node();
|
||||
_nodeCount = 1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private void InsertIntoTree(uint hash, TKey key, TValue value, int currentCount)
|
||||
{
|
||||
Node[]? nodes = _nodes;
|
||||
int currentIndex = 0;
|
||||
int depth = currentCount < ShallowTreeThreshold ? ShallowHashByteCount : HashByteCount;
|
||||
|
||||
int nodeCount = _nodeCount;
|
||||
for (int i = 0; i < depth; i++)
|
||||
{
|
||||
ref Node node = ref nodes[currentIndex];
|
||||
int bucket = (byte)(hash >> (i << 3)) >> 5;
|
||||
int nextIndex = node.GetChild(bucket);
|
||||
if (nextIndex == -1)
|
||||
{
|
||||
nextIndex = nodeCount;
|
||||
nodes = EnsureNodeCapacity(nodes, nextIndex, nodeCount);
|
||||
node = ref nodes[currentIndex];
|
||||
nodes[nextIndex] = new Node();
|
||||
node.SetChild(bucket, nextIndex);
|
||||
nodeCount++;
|
||||
}
|
||||
|
||||
currentIndex = nextIndex;
|
||||
}
|
||||
|
||||
_nodes = nodes;
|
||||
_nodeCount = nodeCount;
|
||||
ref Node leaf = ref nodes[currentIndex];
|
||||
leaf.AddKeyValue(key, value, LeafInitialCapacity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static Node[] EnsureNodeCapacity(in Node[] nodes, int requiredIndex, int liveNodeCount)
|
||||
{
|
||||
int currentLength = nodes.Length;
|
||||
if (requiredIndex < currentLength)
|
||||
{
|
||||
return nodes;
|
||||
}
|
||||
|
||||
int targetLength = currentLength == 0 ? DefaultCapacity : currentLength;
|
||||
while (targetLength <= requiredIndex)
|
||||
{
|
||||
targetLength <<= 1;
|
||||
}
|
||||
|
||||
Node[] newNodes = new Node[targetLength];
|
||||
if (liveNodeCount > currentLength)
|
||||
{
|
||||
liveNodeCount = currentLength;
|
||||
}
|
||||
|
||||
Array.Copy(nodes, newNodes, liveNodeCount);
|
||||
return newNodes;
|
||||
}
|
||||
|
||||
private struct Node
|
||||
{
|
||||
public const int ChildCount = 8;
|
||||
|
||||
private TKey[]? _keys;
|
||||
private TValue[]? _values;
|
||||
private int _entryCount;
|
||||
private int[]? _children;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public readonly int GetChild(int slot)
|
||||
{
|
||||
int[]? children = _children;
|
||||
return children == null ? -1 : children[slot];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void SetChild(int slot, int index)
|
||||
{
|
||||
int[]? children = _children;
|
||||
if (children == null)
|
||||
{
|
||||
children = new int[ChildCount];
|
||||
for (int i = 0; i < ChildCount; i++)
|
||||
{
|
||||
children[i] = -1;
|
||||
}
|
||||
|
||||
_children = children;
|
||||
}
|
||||
|
||||
children[slot] = index;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void AddKeyValue(TKey key, TValue value, int desiredInitialCapacity)
|
||||
{
|
||||
TKey[]? keys = _keys;
|
||||
TValue[]? values = _values;
|
||||
int entryCount = _entryCount;
|
||||
|
||||
int newLength;
|
||||
if (keys == null)
|
||||
{
|
||||
newLength = desiredInitialCapacity < 4 ? 4 : desiredInitialCapacity;
|
||||
keys = new TKey[newLength];
|
||||
values = new TValue[newLength];
|
||||
_keys = keys;
|
||||
_values = values;
|
||||
}
|
||||
else if (entryCount == keys.Length)
|
||||
{
|
||||
newLength = keys.Length << 1;
|
||||
TKey[] newKeys = new TKey[newLength];
|
||||
TValue[] newValues = new TValue[newLength];
|
||||
new Span<TKey>(keys, 0, entryCount).CopyTo(newKeys);
|
||||
new Span<TValue>(values, 0, entryCount).CopyTo(newValues);
|
||||
keys = newKeys;
|
||||
values = newValues;
|
||||
_keys = newKeys;
|
||||
_values = newValues;
|
||||
}
|
||||
|
||||
keys[entryCount] = key;
|
||||
values![entryCount] = value;
|
||||
_entryCount = entryCount + 1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public readonly int CopyValues(Span<TValue> destination)
|
||||
{
|
||||
TValue[]? values = _values;
|
||||
if (values == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int entryCount = _entryCount;
|
||||
new Span<TValue>(values, 0, entryCount).CopyTo(destination);
|
||||
return entryCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
public struct BidirectionalCycleRemainderStepper(ulong modulus)
|
||||
{
|
||||
private readonly ulong _modulus = modulus;
|
||||
private ulong _previous = 0UL;
|
||||
private ulong _remainder = 0UL;
|
||||
|
||||
public readonly ulong Modulus
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
get
|
||||
{
|
||||
return _modulus;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public void Initialize(ulong value)
|
||||
{
|
||||
_remainder = value.ReduceCycleRemainder(_modulus);
|
||||
_previous = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public ulong Step(ulong value)
|
||||
{
|
||||
long delta = unchecked((long)value - (long)_previous);
|
||||
_previous = value;
|
||||
if (delta == 0)
|
||||
{
|
||||
return _remainder;
|
||||
}
|
||||
|
||||
if (delta > 0)
|
||||
{
|
||||
ulong step = ((ulong)delta).ReduceCycleRemainder(_modulus);
|
||||
ulong sum = _remainder + step;
|
||||
if (sum >= _modulus)
|
||||
{
|
||||
sum -= _modulus;
|
||||
}
|
||||
|
||||
_remainder = sum;
|
||||
return _remainder;
|
||||
}
|
||||
|
||||
ulong backward = ((ulong)-delta).ReduceCycleRemainder(_modulus);
|
||||
if (backward > _remainder)
|
||||
{
|
||||
ulong diff = backward - _remainder;
|
||||
ulong wrap = diff.ReduceCycleRemainder(_modulus);
|
||||
_remainder = wrap == 0 ? 0UL : _modulus - wrap;
|
||||
return _remainder;
|
||||
}
|
||||
|
||||
_remainder -= backward;
|
||||
return _remainder;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Maintains rolling remainders of an increasing sequence modulo a BigInteger cycle length.
|
||||
/// Mirrors <see cref="CycleRemainderStepper"/> for values that exceed 64 bits.
|
||||
/// </summary>
|
||||
[method: MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public struct BigIntegerCycleRemainderStepper(byte cycleLength)
|
||||
{
|
||||
private readonly BigInteger _cycleLength = (BigInteger)cycleLength;
|
||||
private BigInteger _previousValue = BigInteger.Zero;
|
||||
private BigInteger _currentRemainder = 0;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public byte Initialize(BigInteger value)
|
||||
{
|
||||
_previousValue = value;
|
||||
_currentRemainder = value % _cycleLength;
|
||||
return (byte)_currentRemainder;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public bool NextIsNotDivisible(BigInteger value)
|
||||
{
|
||||
BigInteger delta = value - _previousValue;
|
||||
_previousValue = value;
|
||||
delta = (_currentRemainder + delta) % _cycleLength;
|
||||
_currentRemainder = delta;
|
||||
return !delta.IsZero;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public byte ComputeNext(BigInteger value)
|
||||
{
|
||||
BigInteger delta = value - _previousValue;
|
||||
_previousValue = value;
|
||||
delta = (_currentRemainder + delta) % _cycleLength;
|
||||
_currentRemainder = delta;
|
||||
|
||||
return (byte)delta;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks successive residues of <c>baseValue^exponent mod modulus</c> for ascending exponents using <see cref="BigInteger"/> inputs.
|
||||
/// This avoids recomputing full modular exponentiation when stepping forward by a known delta.
|
||||
/// </summary>
|
||||
public struct BigIntegerExponentStepper(BigInteger modulus, BigInteger baseValue)
|
||||
{
|
||||
private readonly BigInteger _modulus = modulus;
|
||||
private readonly BigInteger _baseValue = baseValue % modulus;
|
||||
private BigInteger PreviousExponent = BigInteger.Zero;
|
||||
private BigInteger CurrentResidue = BigInteger.One % modulus;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public BigInteger Initialize(BigInteger exponent)
|
||||
{
|
||||
CurrentResidue = BigInteger.ModPow(_baseValue, exponent, _modulus);
|
||||
PreviousExponent = exponent;
|
||||
return CurrentResidue;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool InitializeIsUnity(BigInteger exponent)
|
||||
{
|
||||
Initialize(exponent);
|
||||
return CurrentResidue.IsOne;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public BigInteger ComputeNext(BigInteger exponent)
|
||||
{
|
||||
if (exponent == PreviousExponent)
|
||||
{
|
||||
return CurrentResidue;
|
||||
}
|
||||
|
||||
BigInteger delta = exponent - PreviousExponent;
|
||||
BigInteger deltaResidue = BigInteger.ModPow(_baseValue, delta, _modulus);
|
||||
CurrentResidue = (CurrentResidue * deltaResidue) % _modulus;
|
||||
PreviousExponent = exponent;
|
||||
return CurrentResidue;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool ComputeNextIsUnity(BigInteger exponent)
|
||||
{
|
||||
ComputeNext(exponent);
|
||||
return CurrentResidue.IsOne;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset()
|
||||
{
|
||||
PreviousExponent = BigInteger.Zero;
|
||||
CurrentResidue = BigInteger.One % _modulus;
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PerfectNumbers.Core;
|
||||
|
||||
/// <summary>
|
||||
/// BigInteger helpers including cycle-remainder reduction using the subtract ladder that benchmarked best.
|
||||
/// Shared stats/warm set stay here so ulong reductions can reuse them through ULongExtensions.
|
||||
/// </summary>
|
||||
public static partial class BigIntegerExtensions
|
||||
{
|
||||
// private const int UnrolledSubtractCount = 6;
|
||||
// private static readonly HashSet<ulong> WarmModuli;
|
||||
// private static long _hits;
|
||||
// private static long _misses;
|
||||
|
||||
// static BigIntegerExtensions()
|
||||
// {
|
||||
// ulong[] snapshot = MersenneDivisorCycles.Shared.ExportSmallCyclesSnapshot();
|
||||
// WarmModuli = new HashSet<ulong>(snapshot.Length);
|
||||
// for (int i = 0; i < snapshot.Length; i++)
|
||||
// {
|
||||
// ulong cycle = snapshot[i];
|
||||
// if (cycle > 1UL)
|
||||
// {
|
||||
// WarmModuli.Add(cycle);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// public static void DumpCycleRemainderStats()
|
||||
// {
|
||||
// long hits = Interlocked.Read(ref _hits);
|
||||
// long misses = Interlocked.Read(ref _misses);
|
||||
// Console.WriteLine($"CycleRemainderReducerCache: hits={hits}, misses={misses}");
|
||||
// }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static BigInteger ReduceCycleRemainder(this BigInteger value, BigInteger modulus)
|
||||
{
|
||||
// Track(modulus);
|
||||
// DumpCycleRemainderStats();
|
||||
|
||||
if (value < modulus)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value -= modulus;
|
||||
if (value < modulus)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value -= modulus;
|
||||
if (value < modulus)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value -= modulus;
|
||||
if (value < modulus)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value -= modulus;
|
||||
if (value < modulus)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value -= modulus;
|
||||
if (value < modulus)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value -= modulus;
|
||||
if (value < modulus)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return value % modulus;
|
||||
}
|
||||
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// internal static void Track(in BigInteger modulus)
|
||||
// {
|
||||
// if (modulus <= ulong.MaxValue)
|
||||
// {
|
||||
// Track((ulong)modulus);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Interlocked.Increment(ref _misses);
|
||||
// }
|
||||
// }
|
||||
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// internal static void Track(ulong modulus)
|
||||
// {
|
||||
// if (WarmModuli.Contains(modulus))
|
||||
// {
|
||||
// Interlocked.Increment(ref _hits);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Interlocked.Increment(ref _misses);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace PerfectNumbers.Core
|
||||
{
|
||||
public static class BigIntegerNumbers
|
||||
{
|
||||
public static readonly BigInteger OneShiftedLeft63 = BigInteger.One << 63;
|
||||
public static readonly BigInteger OneShiftedLeft256MinusOne = (BigInteger.One << 256) - BigInteger.One;
|
||||
public static readonly BigInteger UlongMaxValue = (BigInteger)ulong.MaxValue;
|
||||
public static readonly BigInteger UlongMaxValuePlusOne = UlongMaxValue + BigInteger.One;
|
||||
public static readonly BigInteger UInt128MaxValue = (BigInteger)UInt128.MaxValue;
|
||||
public static readonly BigInteger UInt128MaxValuePlusOne = UInt128MaxValue + BigInteger.One;
|
||||
|
||||
public static readonly BigInteger Two = 2UL;
|
||||
public static readonly BigInteger Pow2Exact2 = 4UL;
|
||||
public static readonly BigInteger Pow2Exact3 = 8UL;
|
||||
public static readonly BigInteger Pow2Exact4 = 16UL;
|
||||
public static readonly BigInteger Pow2Exact5 = 32UL;
|
||||
public static readonly BigInteger Pow2Exact6 = 64UL;
|
||||
public static readonly BigInteger Pow2Exact7 = 128UL;
|
||||
public static readonly BigInteger Pow2Exact8 = 256UL;
|
||||
public static readonly BigInteger Pow2Exact9 = 512UL;
|
||||
public static readonly BigInteger Pow2Exact10 = 1_024UL;
|
||||
public static readonly BigInteger Pow2Exact11 = 2_048UL;
|
||||
public static readonly BigInteger Pow2Exact12 = 4_096UL;
|
||||
public static readonly BigInteger Pow2Exact13 = 8_192UL;
|
||||
public static readonly BigInteger Pow2Exact14 = 16_384UL;
|
||||
public static readonly BigInteger Pow2Exact15 = 32_768UL;
|
||||
public static readonly BigInteger Pow2Exact16 = 65_536UL;
|
||||
public static readonly BigInteger Pow2Exact17 = 131_072UL;
|
||||
public static readonly BigInteger Pow2Exact18 = 262_144UL;
|
||||
public static readonly BigInteger Pow2Exact19 = 524_288UL;
|
||||
public static readonly BigInteger Pow2Exact20 = 1_048_576UL;
|
||||
public static readonly BigInteger Pow2Exact21 = 2_097_152UL;
|
||||
public static readonly BigInteger Pow2Exact22 = 4_194_304UL;
|
||||
public static readonly BigInteger Pow2Exact23 = 8_388_608UL;
|
||||
public static readonly BigInteger Pow2Exact24 = 16_777_216UL;
|
||||
public static readonly BigInteger Pow2Exact25 = 33_554_432UL;
|
||||
public static readonly BigInteger Pow2Exact26 = 67_108_864UL;
|
||||
public static readonly BigInteger Pow2Exact27 = 134_217_728UL;
|
||||
public static readonly BigInteger Pow2Exact28 = 268_435_456UL;
|
||||
public static readonly BigInteger Pow2Exact29 = 536_870_912UL;
|
||||
public static readonly BigInteger Pow2Exact30 = 1_073_741_824UL;
|
||||
public static readonly BigInteger Pow2Exact31 = 2_147_483_648UL;
|
||||
public static readonly BigInteger Pow2Exact32 = 4_294_967_296UL;
|
||||
public static readonly BigInteger Pow2Exact33 = 8_589_934_592UL;
|
||||
public static readonly BigInteger Pow2Exact34 = 17_179_869_184UL;
|
||||
public static readonly BigInteger Pow2Exact35 = 34_359_738_368UL;
|
||||
public static readonly BigInteger Pow2Exact36 = 68_719_476_736UL;
|
||||
public static readonly BigInteger Pow2Exact37 = 137_438_953_472UL;
|
||||
public static readonly BigInteger Pow2Exact38 = 274_877_906_944UL;
|
||||
public static readonly BigInteger Pow2Exact39 = 549_755_813_888UL;
|
||||
public static readonly BigInteger Pow2Exact40 = 1_099_511_627_776UL;
|
||||
public static readonly BigInteger Pow2Exact41 = 2_199_023_255_552UL;
|
||||
public static readonly BigInteger Pow2Exact42 = 4_398_046_511_104UL;
|
||||
public static readonly BigInteger Pow2Exact43 = 8_796_093_022_208UL;
|
||||
public static readonly BigInteger Pow2Exact44 = 17_592_186_044_416UL;
|
||||
public static readonly BigInteger Pow2Exact45 = 35_184_372_088_832UL;
|
||||
public static readonly BigInteger Pow2Exact46 = 70_368_744_177_664UL;
|
||||
public static readonly BigInteger Pow2Exact47 = 140_737_488_355_328UL;
|
||||
public static readonly BigInteger Pow2Exact48 = 281_474_976_710_656UL;
|
||||
public static readonly BigInteger Pow2Exact49 = 562_949_953_421_312UL;
|
||||
public static readonly BigInteger Pow2Exact50 = 1_125_899_906_842_624UL;
|
||||
public static readonly BigInteger Pow2Exact51 = 2_251_799_813_685_248UL;
|
||||
public static readonly BigInteger Pow2Exact52 = 4_503_599_627_370_496UL;
|
||||
public static readonly BigInteger Pow2Exact53 = 9_007_199_254_740_992UL;
|
||||
public static readonly BigInteger Pow2Exact54 = 18_014_398_509_481_984UL;
|
||||
public static readonly BigInteger Pow2Exact55 = 36_028_797_018_963_968UL;
|
||||
public static readonly BigInteger Pow2Exact56 = 72_057_594_037_927_936UL;
|
||||
public static readonly BigInteger Pow2Exact57 = 144_115_188_075_855_872UL;
|
||||
public static readonly BigInteger Pow2Exact58 = 288_230_376_151_711_744UL;
|
||||
public static readonly BigInteger Pow2Exact59 = 576_460_752_303_423_488UL;
|
||||
public static readonly BigInteger Pow2Exact60 = 1_152_921_504_606_846_976UL;
|
||||
public static readonly BigInteger Pow2Exact61 = 2_305_843_009_213_693_952UL;
|
||||
public static readonly BigInteger Pow2Exact62 = 4_611_686_018_427_387_904UL;
|
||||
public static readonly BigInteger Pow2Exact63 = 9_223_372_036_854_775_808UL;
|
||||
|
||||
public static readonly BigInteger Pow2MinusOne1 = 2UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne2 = 4UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne3 = 8UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne4 = 16UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne5 = 32UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne6 = 64UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne7 = 128UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne8 = 256UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne9 = 512UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne10 = 1_024UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne11 = 2_048UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne12 = 4_096UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne13 = 8_192UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne14 = 16_384UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne15 = 32_768UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne16 = 65_536UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne17 = 131_072UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne18 = 262_144UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne19 = 524_288UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne20 = 1_048_576UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne21 = 2_097_152UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne22 = 4_194_304UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne23 = 8_388_608UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne24 = 16_777_216UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne25 = 33_554_432UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne26 = 67_108_864UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne27 = 134_217_728UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne28 = 268_435_456UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne29 = 536_870_912UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne30 = 1_073_741_824UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne31 = 2_147_483_648UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne32 = 4_294_967_296UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne33 = 8_589_934_592UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne34 = 17_179_869_184UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne35 = 34_359_738_368UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne36 = 68_719_476_736UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne37 = 137_438_953_472UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne38 = 274_877_906_944UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne39 = 549_755_813_888UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne40 = 1_099_511_627_776UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne41 = 2_199_023_255_552UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne42 = 4_398_046_511_104UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne43 = 8_796_093_022_208UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne44 = 17_592_186_044_416UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne45 = 35_184_372_088_832UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne46 = 70_368_744_177_664UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne47 = 140_737_488_355_328UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne48 = 281_474_976_710_656UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne49 = 562_949_953_421_312UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne50 = 1_125_899_906_842_624UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne51 = 2_251_799_813_685_248UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne52 = 4_503_599_627_370_496UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne53 = 9_007_199_254_740_992UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne54 = 18_014_398_509_481_984UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne55 = 36_028_797_018_963_968UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne56 = 72_057_594_037_927_936UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne57 = 144_115_188_075_855_872UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne58 = 288_230_376_151_711_744UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne59 = 576_460_752_303_423_488UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne60 = 1_152_921_504_606_846_976UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne61 = 2_305_843_009_213_693_952UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne62 = 4_611_686_018_427_387_904UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne63 = 9_223_372_036_854_775_808UL - 1UL;
|
||||
public static readonly BigInteger Pow2MinusOne1024 = (BigInteger.One << 1024) - BigInteger.One;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 263 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user