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 lastThreePow2 = DivisorGenerator.SmallPrimesPow2LastThree;
|
||||||
var lastNine = DivisorGenerator.SmallPrimesLastNine;
|
var lastNine = DivisorGenerator.SmallPrimesLastNine;
|
||||||
var lastNinePow2 = DivisorGenerator.SmallPrimesPow2LastNine;
|
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);
|
lastOne.Length.Should().Be((int)PerfectNumberConstants.PrimesLimit);
|
||||||
lastSeven.Length.Should().Be((int)PerfectNumberConstants.PrimesLimit);
|
lastSeven.Length.Should().Be((int)PerfectNumberConstants.PrimesLimit);
|
||||||
lastThree.Length.Should().Be((int)PerfectNumberConstants.PrimesLimit);
|
lastThree.Length.Should().Be((int)PerfectNumberConstants.PrimesLimit);
|
||||||
lastNine.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);
|
lastOnePow2.Length.Should().Be(lastOne.Length);
|
||||||
lastSevenPow2.Length.Should().Be(lastSeven.Length);
|
lastSevenPow2.Length.Should().Be(lastSeven.Length);
|
||||||
lastThreePow2.Length.Should().Be(lastThree.Length);
|
lastThreePow2.Length.Should().Be(lastThree.Length);
|
||||||
lastNinePow2.Length.Should().Be(lastNine.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]);
|
lastOne[..5].Should().Equal([3U, 7U, 11U, 13U, 19U]);
|
||||||
lastSeven[..5].Should().Equal([3U, 7U, 11U, 13U, 17U]);
|
lastSeven[..5].Should().Equal([3U, 7U, 11U, 13U, 17U]);
|
||||||
@@ -78,6 +93,47 @@ public class PrimesGeneratorTests
|
|||||||
lastThreePow2[i].Should().Be(prime * (ulong)prime);
|
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++)
|
for (int i = 0; i < lastNine.Length; i++)
|
||||||
{
|
{
|
||||||
uint prime = lastNine[i];
|
uint prime = lastNine[i];
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ internal static class DivisorGenerator
|
|||||||
public static readonly ulong[] SmallPrimesPow2LastThree;
|
public static readonly ulong[] SmallPrimesPow2LastThree;
|
||||||
public static readonly uint[] SmallPrimesLastNine;
|
public static readonly uint[] SmallPrimesLastNine;
|
||||||
public static readonly ulong[] SmallPrimesPow2LastNine;
|
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 DecimalMaskWhenLastIsSeven = (1 << 3) | (1 << 7) | (1 << 9);
|
||||||
private const ushort DecimalMaskWhenLastIsThree = (1 << 1) | (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 SmallPrimesLastThree,
|
||||||
out SmallPrimesPow2LastThree,
|
out SmallPrimesPow2LastThree,
|
||||||
out SmallPrimesLastNine,
|
out SmallPrimesLastNine,
|
||||||
out SmallPrimesPow2LastNine);
|
out SmallPrimesPow2LastNine,
|
||||||
|
out SmallPrimesLastOneWithoutLastThree,
|
||||||
|
out SmallPrimesPow2LastOneWithoutLastThree,
|
||||||
|
out SmallPrimesLastSevenWithoutLastThree,
|
||||||
|
out SmallPrimesPow2LastSevenWithoutLastThree,
|
||||||
|
out SmallPrimesLastNineWithoutLastThree,
|
||||||
|
out SmallPrimesPow2LastNineWithoutLastThree);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -59,7 +71,13 @@ internal static class DivisorGenerator
|
|||||||
out uint[] lastThree,
|
out uint[] lastThree,
|
||||||
out ulong[] lastThreePow2,
|
out ulong[] lastThreePow2,
|
||||||
out uint[] lastNine,
|
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;
|
uint target = PerfectNumberConstants.PrimesLimit;
|
||||||
int targetInt = checked((int)target);
|
int targetInt = checked((int)target);
|
||||||
@@ -75,6 +93,13 @@ internal static class DivisorGenerator
|
|||||||
lastNinePow2 = new ulong[targetInt];
|
lastNinePow2 = new ulong[targetInt];
|
||||||
|
|
||||||
List<uint> primes = new(targetInt * 4 / 3 + 16);
|
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;
|
uint candidate = 2U;
|
||||||
int allCount = 0;
|
int allCount = 0;
|
||||||
int lastOneCount = 0;
|
int lastOneCount = 0;
|
||||||
@@ -82,24 +107,22 @@ internal static class DivisorGenerator
|
|||||||
int lastThreeCount = 0;
|
int lastThreeCount = 0;
|
||||||
int lastNineCount = 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;
|
bool isPrime = true;
|
||||||
int primesCount = primes.Count;
|
int primesCount = primes.Count;
|
||||||
for (int i = 0; i < primesCount; i++)
|
for (int i = 0; i < primesCount; i++)
|
||||||
{
|
{
|
||||||
uint p = primes[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)
|
if (p * p > candidate)
|
||||||
{
|
{
|
||||||
break;
|
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)
|
if (candidate % p == 0U)
|
||||||
{
|
{
|
||||||
isPrime = false;
|
isPrime = false;
|
||||||
@@ -110,54 +133,80 @@ internal static class DivisorGenerator
|
|||||||
if (isPrime)
|
if (isPrime)
|
||||||
{
|
{
|
||||||
primes.Add(candidate);
|
primes.Add(candidate);
|
||||||
|
ulong candidateSquared = checked((ulong)candidate * candidate);
|
||||||
if (allCount < targetInt)
|
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;
|
all[allCount] = candidate;
|
||||||
allPow2[allCount] = checked((ulong)candidate * candidate);
|
allPow2[allCount] = candidateSquared;
|
||||||
allCount++;
|
allCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (candidate != 2U)
|
if (candidate != 2U)
|
||||||
{
|
{
|
||||||
LastDigit lastDigit = GetLastDigit(candidate);
|
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;
|
lastOne[lastOneCount] = candidate;
|
||||||
lastOnePow2[lastOneCount] = checked((ulong)candidate * candidate);
|
lastOnePow2[lastOneCount] = candidateSquared;
|
||||||
lastOneCount++;
|
lastOneCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastSevenCount < targetInt && IsAllowedForLastSeven(candidate, lastDigit))
|
if (allowedForLastSeven && lastSevenCount < targetInt)
|
||||||
{
|
{
|
||||||
lastSeven[lastSevenCount] = candidate;
|
lastSeven[lastSevenCount] = candidate;
|
||||||
lastSevenPow2[lastSevenCount] = checked((ulong)candidate * candidate);
|
lastSevenPow2[lastSevenCount] = candidateSquared;
|
||||||
lastSevenCount++;
|
lastSevenCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastThreeCount < targetInt && IsAllowedForLastThree(candidate, lastDigit))
|
if (allowedForLastThree && lastThreeCount < targetInt)
|
||||||
{
|
{
|
||||||
lastThree[lastThreeCount] = candidate;
|
lastThree[lastThreeCount] = candidate;
|
||||||
lastThreePow2[lastThreeCount] = checked((ulong)candidate * candidate);
|
lastThreePow2[lastThreeCount] = candidateSquared;
|
||||||
lastThreeCount++;
|
lastThreeCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastNineCount < targetInt && IsAllowedForLastNine(candidate, lastDigit))
|
if (allowedForLastNine && lastNineCount < targetInt)
|
||||||
{
|
{
|
||||||
lastNine[lastNineCount] = candidate;
|
lastNine[lastNineCount] = candidate;
|
||||||
lastNinePow2[lastNineCount] = checked((ulong)candidate * candidate);
|
lastNinePow2[lastNineCount] = candidateSquared;
|
||||||
lastNineCount++;
|
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;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@@ -470,7 +470,7 @@ Cleanup:
|
|||||||
|
|
||||||
for (int i = 0; i < endingOrder.Length; i++)
|
for (int i = 0; i < endingOrder.Length; i++)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<int> divisors = GetGroupBDivisors(endingOrder[i]);
|
ReadOnlySpan<uint> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||||
int index = indices[i];
|
int index = indices[i];
|
||||||
if ((uint)index >= (uint)divisors.Length)
|
if ((uint)index >= (uint)divisors.Length)
|
||||||
{
|
{
|
||||||
@@ -507,7 +507,7 @@ Cleanup:
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < endingOrder.Length; i++)
|
for (int i = 0; i < endingOrder.Length; i++)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<int> divisors = GetGroupBDivisors(endingOrder[i]);
|
ReadOnlySpan<uint> divisors = GetGroupBDivisors(endingOrder[i]);
|
||||||
int index = indices[i];
|
int index = indices[i];
|
||||||
if ((uint)index >= (uint)divisors.Length)
|
if ((uint)index >= (uint)divisors.Length)
|
||||||
{
|
{
|
||||||
@@ -527,13 +527,13 @@ Cleanup:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<int> GetGroupBDivisors(byte ending) => ending switch
|
private static ReadOnlySpan<uint> GetGroupBDivisors(byte ending) => ending switch
|
||||||
{
|
{
|
||||||
1 => HeuristicPrimeSieves.GroupBDivisorsEnding1,
|
1 => DivisorGenerator.SmallPrimesLastOneWithoutLastThree,
|
||||||
3 => HeuristicPrimeSieves.GroupBDivisorsEnding3,
|
3 => ReadOnlySpan<uint>.Empty,
|
||||||
7 => HeuristicPrimeSieves.GroupBDivisorsEnding7,
|
7 => DivisorGenerator.SmallPrimesLastSevenWithoutLastThree,
|
||||||
9 => HeuristicPrimeSieves.GroupBDivisorsEnding9,
|
9 => DivisorGenerator.SmallPrimesLastNineWithoutLastThree,
|
||||||
_ => ReadOnlySpan<int>.Empty,
|
_ => ReadOnlySpan<uint>.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static ReadOnlySpan<ushort> GetWheel210ResiduesForEnding(byte ending) => ending switch
|
private static ReadOnlySpan<ushort> GetWheel210ResiduesForEnding(byte ending) => ending switch
|
||||||
|
|||||||
Reference in new issue
Block a user