Add reusable group B divisor tables without trailing-three primes
This commit is contained in:
1 parent
b83bddf49a
commit
c693a05708
3 files changed
+136
-31
No files matched your search
@@ -36,15 +36,30 @@ public class PrimesGeneratorTests
|
||||
var lastThreePow2 = DivisorGenerator.SmallPrimesPow2LastThree;
|
||||
var lastNine = DivisorGenerator.SmallPrimesLastNine;
|
||||
var lastNinePow2 = DivisorGenerator.SmallPrimesPow2LastNine;
|
||||
var lastOneWithoutThree = DivisorGenerator.SmallPrimesLastOneWithoutLastThree;
|
||||
var lastOneWithoutThreePow2 = DivisorGenerator.SmallPrimesPow2LastOneWithoutLastThree;
|
||||
var lastSevenWithoutThree = DivisorGenerator.SmallPrimesLastSevenWithoutLastThree;
|
||||
var lastSevenWithoutThreePow2 = DivisorGenerator.SmallPrimesPow2LastSevenWithoutLastThree;
|
||||
var lastNineWithoutThree = DivisorGenerator.SmallPrimesLastNineWithoutLastThree;
|
||||
var lastNineWithoutThreePow2 = DivisorGenerator.SmallPrimesPow2LastNineWithoutLastThree;
|
||||
|
||||
lastOne.Length.Should().Be((int)PerfectNumberConstants.PrimesLimit);
|
||||
lastSeven.Length.Should().Be((int)PerfectNumberConstants.PrimesLimit);
|
||||
lastThree.Length.Should().Be((int)PerfectNumberConstants.PrimesLimit);
|
||||
lastNine.Length.Should().Be((int)PerfectNumberConstants.PrimesLimit);
|
||||
lastOneWithoutThree.Length.Should().BeGreaterThan(0);
|
||||
lastSevenWithoutThree.Length.Should().BeGreaterThan(0);
|
||||
lastNineWithoutThree.Length.Should().BeGreaterThan(0);
|
||||
lastOneWithoutThree.Length.Should().BeLessThan(lastOne.Length);
|
||||
lastSevenWithoutThree.Length.Should().BeLessThan(lastSeven.Length);
|
||||
lastNineWithoutThree.Length.Should().BeLessThan(lastNine.Length);
|
||||
lastOnePow2.Length.Should().Be(lastOne.Length);
|
||||
lastSevenPow2.Length.Should().Be(lastSeven.Length);
|
||||
lastThreePow2.Length.Should().Be(lastThree.Length);
|
||||
lastNinePow2.Length.Should().Be(lastNine.Length);
|
||||
lastOneWithoutThreePow2.Length.Should().Be(lastOneWithoutThree.Length);
|
||||
lastSevenWithoutThreePow2.Length.Should().Be(lastSevenWithoutThree.Length);
|
||||
lastNineWithoutThreePow2.Length.Should().Be(lastNineWithoutThree.Length);
|
||||
|
||||
lastOne[..5].Should().Equal([3U, 7U, 11U, 13U, 19U]);
|
||||
lastSeven[..5].Should().Equal([3U, 7U, 11U, 13U, 17U]);
|
||||
@@ -78,6 +93,47 @@ public class PrimesGeneratorTests
|
||||
lastThreePow2[i].Should().Be(prime * (ulong)prime);
|
||||
}
|
||||
|
||||
|
||||
lastOneWithoutThree.Should().NotBeEmpty();
|
||||
lastSevenWithoutThree.Should().NotBeEmpty();
|
||||
lastNineWithoutThree.Should().NotBeEmpty();
|
||||
|
||||
for (int i = 0; i < lastOneWithoutThree.Length; i++)
|
||||
{
|
||||
uint prime = lastOneWithoutThree[i];
|
||||
(prime % 10U != 3U).Should().BeTrue();
|
||||
if (i > 0)
|
||||
{
|
||||
prime.Should().BeGreaterThan(lastOneWithoutThree[i - 1]);
|
||||
}
|
||||
|
||||
lastOneWithoutThreePow2[i].Should().Be(prime * (ulong)prime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < lastSevenWithoutThree.Length; i++)
|
||||
{
|
||||
uint prime = lastSevenWithoutThree[i];
|
||||
(prime % 10U != 3U).Should().BeTrue();
|
||||
if (i > 0)
|
||||
{
|
||||
prime.Should().BeGreaterThan(lastSevenWithoutThree[i - 1]);
|
||||
}
|
||||
|
||||
lastSevenWithoutThreePow2[i].Should().Be(prime * (ulong)prime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < lastNineWithoutThree.Length; i++)
|
||||
{
|
||||
uint prime = lastNineWithoutThree[i];
|
||||
(prime % 10U != 3U).Should().BeTrue();
|
||||
if (i > 0)
|
||||
{
|
||||
prime.Should().BeGreaterThan(lastNineWithoutThree[i - 1]);
|
||||
}
|
||||
|
||||
lastNineWithoutThreePow2[i].Should().Be(prime * (ulong)prime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < lastNine.Length; i++)
|
||||
{
|
||||
uint prime = lastNine[i];
|
||||
|
||||
@@ -16,6 +16,12 @@ internal static class DivisorGenerator
|
||||
public static readonly ulong[] SmallPrimesPow2LastThree;
|
||||
public static readonly uint[] SmallPrimesLastNine;
|
||||
public static readonly ulong[] SmallPrimesPow2LastNine;
|
||||
public static readonly uint[] SmallPrimesLastOneWithoutLastThree;
|
||||
public static readonly ulong[] SmallPrimesPow2LastOneWithoutLastThree;
|
||||
public static readonly uint[] SmallPrimesLastSevenWithoutLastThree;
|
||||
public static readonly ulong[] SmallPrimesPow2LastSevenWithoutLastThree;
|
||||
public static readonly uint[] SmallPrimesLastNineWithoutLastThree;
|
||||
public static readonly ulong[] SmallPrimesPow2LastNineWithoutLastThree;
|
||||
|
||||
private const ushort DecimalMaskWhenLastIsSeven = (1 << 3) | (1 << 7) | (1 << 9);
|
||||
private const ushort DecimalMaskWhenLastIsThree = (1 << 1) | (1 << 3) | (1 << 7) | (1 << 9);
|
||||
@@ -34,7 +40,13 @@ internal static class DivisorGenerator
|
||||
out SmallPrimesLastThree,
|
||||
out SmallPrimesPow2LastThree,
|
||||
out SmallPrimesLastNine,
|
||||
out SmallPrimesPow2LastNine);
|
||||
out SmallPrimesPow2LastNine,
|
||||
out SmallPrimesLastOneWithoutLastThree,
|
||||
out SmallPrimesPow2LastOneWithoutLastThree,
|
||||
out SmallPrimesLastSevenWithoutLastThree,
|
||||
out SmallPrimesPow2LastSevenWithoutLastThree,
|
||||
out SmallPrimesLastNineWithoutLastThree,
|
||||
out SmallPrimesPow2LastNineWithoutLastThree);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -59,7 +71,13 @@ internal static class DivisorGenerator
|
||||
out uint[] lastThree,
|
||||
out ulong[] lastThreePow2,
|
||||
out uint[] lastNine,
|
||||
out ulong[] lastNinePow2)
|
||||
out ulong[] lastNinePow2,
|
||||
out uint[] lastOneWithoutThree,
|
||||
out ulong[] lastOneWithoutThreePow2,
|
||||
out uint[] lastSevenWithoutThree,
|
||||
out ulong[] lastSevenWithoutThreePow2,
|
||||
out uint[] lastNineWithoutThree,
|
||||
out ulong[] lastNineWithoutThreePow2)
|
||||
{
|
||||
uint target = PerfectNumberConstants.PrimesLimit;
|
||||
int targetInt = checked((int)target);
|
||||
@@ -75,6 +93,13 @@ internal static class DivisorGenerator
|
||||
lastNinePow2 = new ulong[targetInt];
|
||||
|
||||
List<uint> primes = new(targetInt * 4 / 3 + 16);
|
||||
List<uint> lastOneWithoutThreeList = new(targetInt);
|
||||
List<ulong> lastOneWithoutThreePow2List = new(targetInt);
|
||||
List<uint> lastSevenWithoutThreeList = new(targetInt);
|
||||
List<ulong> lastSevenWithoutThreePow2List = new(targetInt);
|
||||
List<uint> lastNineWithoutThreeList = new(targetInt);
|
||||
List<ulong> lastNineWithoutThreePow2List = new(targetInt);
|
||||
|
||||
uint candidate = 2U;
|
||||
int allCount = 0;
|
||||
int lastOneCount = 0;
|
||||
@@ -82,24 +107,22 @@ internal static class DivisorGenerator
|
||||
int lastThreeCount = 0;
|
||||
int lastNineCount = 0;
|
||||
|
||||
while (allCount < targetInt || lastOneCount < targetInt || lastSevenCount < targetInt || lastThreeCount < targetInt || lastNineCount < targetInt)
|
||||
while (allCount < targetInt ||
|
||||
lastOneCount < targetInt ||
|
||||
lastSevenCount < targetInt ||
|
||||
lastThreeCount < targetInt ||
|
||||
lastNineCount < targetInt)
|
||||
{
|
||||
bool isPrime = true;
|
||||
int primesCount = primes.Count;
|
||||
for (int i = 0; i < primesCount; i++)
|
||||
{
|
||||
uint p = primes[i];
|
||||
// TODO: Reuse a single squared local for `p` within this loop so we avoid recalculating
|
||||
// `p * p` repeatedly without storing every squared prime globally (the cache must remain
|
||||
// limited to the small-divisor tables to honor the memory constraints).
|
||||
if (p * p > candidate)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Replace this trial-division `%` with the sieve-based generator that avoids
|
||||
// per-candidate modulo work so building the small-prime tables stops dominating
|
||||
// startup time for large scans.
|
||||
if (candidate % p == 0U)
|
||||
{
|
||||
isPrime = false;
|
||||
@@ -110,54 +133,80 @@ internal static class DivisorGenerator
|
||||
if (isPrime)
|
||||
{
|
||||
primes.Add(candidate);
|
||||
ulong candidateSquared = checked((ulong)candidate * candidate);
|
||||
if (allCount < targetInt)
|
||||
{
|
||||
// TODO: Reuse a single cached `candidateSquared` per iteration so we avoid issuing
|
||||
// three separate 64-bit multiplications when populating the pow2 arrays here and
|
||||
// in the last-one/last-seven branches below.
|
||||
all[allCount] = candidate;
|
||||
allPow2[allCount] = checked((ulong)candidate * candidate);
|
||||
allPow2[allCount] = candidateSquared;
|
||||
allCount++;
|
||||
}
|
||||
|
||||
if (candidate != 2U)
|
||||
{
|
||||
LastDigit lastDigit = GetLastDigit(candidate);
|
||||
bool allowedForLastOne = IsAllowedForLastOne(candidate, lastDigit);
|
||||
bool allowedForLastSeven = IsAllowedForLastSeven(candidate, lastDigit);
|
||||
bool allowedForLastThree = IsAllowedForLastThree(candidate, lastDigit);
|
||||
bool allowedForLastNine = IsAllowedForLastNine(candidate, lastDigit);
|
||||
bool lastDigitIsThree = lastDigit == LastDigit.Three;
|
||||
|
||||
if (lastOneCount < targetInt && IsAllowedForLastOne(candidate, lastDigit))
|
||||
if (allowedForLastOne && lastOneCount < targetInt)
|
||||
{
|
||||
lastOne[lastOneCount] = candidate;
|
||||
lastOnePow2[lastOneCount] = checked((ulong)candidate * candidate);
|
||||
lastOnePow2[lastOneCount] = candidateSquared;
|
||||
lastOneCount++;
|
||||
}
|
||||
|
||||
if (lastSevenCount < targetInt && IsAllowedForLastSeven(candidate, lastDigit))
|
||||
if (allowedForLastSeven && lastSevenCount < targetInt)
|
||||
{
|
||||
lastSeven[lastSevenCount] = candidate;
|
||||
lastSevenPow2[lastSevenCount] = checked((ulong)candidate * candidate);
|
||||
lastSevenPow2[lastSevenCount] = candidateSquared;
|
||||
lastSevenCount++;
|
||||
}
|
||||
|
||||
if (lastThreeCount < targetInt && IsAllowedForLastThree(candidate, lastDigit))
|
||||
if (allowedForLastThree && lastThreeCount < targetInt)
|
||||
{
|
||||
lastThree[lastThreeCount] = candidate;
|
||||
lastThreePow2[lastThreeCount] = checked((ulong)candidate * candidate);
|
||||
lastThreePow2[lastThreeCount] = candidateSquared;
|
||||
lastThreeCount++;
|
||||
}
|
||||
|
||||
if (lastNineCount < targetInt && IsAllowedForLastNine(candidate, lastDigit))
|
||||
if (allowedForLastNine && lastNineCount < targetInt)
|
||||
{
|
||||
lastNine[lastNineCount] = candidate;
|
||||
lastNinePow2[lastNineCount] = checked((ulong)candidate * candidate);
|
||||
lastNinePow2[lastNineCount] = candidateSquared;
|
||||
lastNineCount++;
|
||||
}
|
||||
|
||||
if (!lastDigitIsThree && allowedForLastOne)
|
||||
{
|
||||
lastOneWithoutThreeList.Add(candidate);
|
||||
lastOneWithoutThreePow2List.Add(candidateSquared);
|
||||
}
|
||||
|
||||
if (!lastDigitIsThree && allowedForLastSeven)
|
||||
{
|
||||
lastSevenWithoutThreeList.Add(candidate);
|
||||
lastSevenWithoutThreePow2List.Add(candidateSquared);
|
||||
}
|
||||
|
||||
if (!lastDigitIsThree && allowedForLastNine)
|
||||
{
|
||||
lastNineWithoutThreeList.Add(candidate);
|
||||
lastNineWithoutThreePow2List.Add(candidateSquared);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Switch this increment to the Mod6 stepping table identified in the Mod6ComparisonBenchmarks
|
||||
// so we skip the composite residues without relying on per-iteration `%` checks.
|
||||
candidate = candidate == 2U ? 3U : candidate + 2U;
|
||||
}
|
||||
|
||||
lastOneWithoutThree = lastOneWithoutThreeList.ToArray();
|
||||
lastOneWithoutThreePow2 = lastOneWithoutThreePow2List.ToArray();
|
||||
lastSevenWithoutThree = lastSevenWithoutThreeList.ToArray();
|
||||
lastSevenWithoutThreePow2 = lastSevenWithoutThreePow2List.ToArray();
|
||||
lastNineWithoutThree = lastNineWithoutThreeList.ToArray();
|
||||
lastNineWithoutThreePow2 = lastNineWithoutThreePow2List.ToArray();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -470,7 +470,7 @@ Cleanup:
|
||||
|
||||
for (int i = 0; i < endingOrder.Length; i++)
|
||||
{
|
||||
ReadOnlySpan<int> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||
ReadOnlySpan<uint> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||
int index = indices[i];
|
||||
if ((uint)index >= (uint)divisors.Length)
|
||||
{
|
||||
@@ -507,7 +507,7 @@ Cleanup:
|
||||
{
|
||||
for (int i = 0; i < endingOrder.Length; i++)
|
||||
{
|
||||
ReadOnlySpan<int> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||
ReadOnlySpan<uint> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||
int index = indices[i];
|
||||
if ((uint)index >= (uint)divisors.Length)
|
||||
{
|
||||
@@ -527,13 +527,13 @@ Cleanup:
|
||||
return false;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<int> GetGroupBDivisors(byte ending) => ending switch
|
||||
private static ReadOnlySpan<uint> GetGroupBDivisors(byte ending) => ending switch
|
||||
{
|
||||
1 => HeuristicPrimeSieves.GroupBDivisorsEnding1,
|
||||
3 => HeuristicPrimeSieves.GroupBDivisorsEnding3,
|
||||
7 => HeuristicPrimeSieves.GroupBDivisorsEnding7,
|
||||
9 => HeuristicPrimeSieves.GroupBDivisorsEnding9,
|
||||
_ => ReadOnlySpan<int>.Empty,
|
||||
1 => DivisorGenerator.SmallPrimesLastOneWithoutLastThree,
|
||||
3 => ReadOnlySpan<uint>.Empty,
|
||||
7 => DivisorGenerator.SmallPrimesLastSevenWithoutLastThree,
|
||||
9 => DivisorGenerator.SmallPrimesLastNineWithoutLastThree,
|
||||
_ => ReadOnlySpan<uint>.Empty,
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<ushort> GetWheel210ResiduesForEnding(byte ending) => ending switch
|
||||
|
||||
Reference in new issue
Block a user