Skip to content

Commit 0522dd3

Browse files
author
Oren (electricessence)
committed
Investigation checkin.
1 parent 4d12547 commit 0522dd3

File tree

10 files changed

+662
-180
lines changed

10 files changed

+662
-180
lines changed

source/Open.Numeric.Primes.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<ItemGroup>
4040
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
4141
<PackageReference Include="Open.Collections" Version="2.8.0" />
42+
<PackageReference Include="System.Collections.Immutable" Version="1.7.1" />
4243
</ItemGroup>
4344

4445
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">

source/Optimized.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
using System.Linq;
1+
using System;
2+
using System.Linq;
23
using System.Numerics;
34

45
namespace Open.Numeric.Primes
56
{
67
public class Optimized : PrimalityU64Base
78
{
9+
const uint PERF_PIVOT = 805000000;
10+
811
protected override bool IsPrimeInternal(in ulong value)
9-
{
10-
return value < 805000000
11-
? Polynomial.IsPrimeInternal(in value)
12+
=> value < PERF_PIVOT
13+
? Polynomial.IsPrimeInternal(Convert.ToUInt32(value))
1214
: MillerRabin.IsPrime(in value);
13-
}
1415

1516
public readonly BigInt Big = new BigInt();
1617

@@ -19,7 +20,10 @@ public class BigInt : PrimalityBigIntBase
1920
protected override bool IsPrimeInternal(in BigInteger value)
2021
{
2122
if (value <= ulong.MaxValue)
23+
{
24+
if (value < PERF_PIVOT) Polynomial.IsPrimeInternal(Convert.ToUInt32(value));
2225
return MillerRabin.IsPrime((ulong)value);
26+
}
2327

2428
return MillerRabin.IsProbablePrime(in value) && Polynomial.IsPrime(in value, 6);
2529

source/Polynomial.cs

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace Open.Numeric.Primes
44
{
55
public static class Polynomial
66
{
7-
const ulong MAX_ULONG_DIVISOR = 25043747693UL;
7+
const ulong MAX_ULONG_DIVISOR = 4294967296UL;
88

99
internal static bool IsPrimeInternal(in ulong value)
1010
{
@@ -25,6 +25,52 @@ internal static bool IsPrimeInternal(in ulong value)
2525
return true;
2626
}
2727

28+
const uint MAX_UINT_DIVISOR = 65536U;
29+
30+
internal static bool IsPrimeInternal(uint value)
31+
{
32+
uint divisor = 6;
33+
while (divisor * divisor - 2 * divisor + 1 <= value)
34+
{
35+
if (value % (divisor - 1) == 0)
36+
return false;
37+
38+
if (value % (divisor + 1) == 0)
39+
return false;
40+
41+
divisor += 6;
42+
43+
if (divisor > MAX_UINT_DIVISOR)
44+
return IsPrime(value, divisor);
45+
}
46+
return true;
47+
}
48+
49+
/// Returns true if the value provided is prime.
50+
/// </summary>
51+
/// <param name="value">The value to validate.</param>
52+
/// <returns>True if the value provided is prime</returns>
53+
public static bool IsPrime(uint value)
54+
{
55+
switch (value)
56+
{
57+
// 0 and 1 are not prime numbers
58+
case 0U:
59+
case 1U:
60+
return false;
61+
case 2U:
62+
case 3U:
63+
return true;
64+
65+
default:
66+
67+
if (value % 2 == 0 || value % 3 == 0)
68+
return false;
69+
70+
return IsPrimeInternal(value);
71+
}
72+
}
73+
2874
/// <summary>
2975
/// Returns true if the value provided is prime.
3076
/// </summary>
@@ -61,7 +107,11 @@ public static bool IsBigPrime(in BigInteger value)
61107
if (value.IsZero)
62108
return false;
63109

64-
bool primeCheck(in BigInteger v)
110+
return value.Sign == -1
111+
? value != BigInteger.MinusOne && primeCheck(BigInteger.Abs(value))
112+
: !value.IsOne && primeCheck(in value);
113+
114+
static bool primeCheck(in BigInteger v)
65115
{
66116
if (v == BIG.TWO || v == BIG.THREE)
67117
return true;
@@ -75,10 +125,6 @@ bool primeCheck(in BigInteger v)
75125
return v % 3 != 0
76126
&& IsPrime(v, 6);
77127
}
78-
79-
return value.Sign == -1
80-
? value != BigInteger.MinusOne && primeCheck(BigInteger.Abs(value))
81-
: !value.IsOne && primeCheck(in value);
82128
}
83129

84130
internal static bool IsPrime(in BigInteger value, BigInteger divisor)
@@ -97,6 +143,13 @@ internal static bool IsPrime(in BigInteger value, BigInteger divisor)
97143
return true;
98144
}
99145

146+
public class U32 : PrimalityU32Base
147+
{
148+
// ReSharper disable once MemberHidesStaticFromOuterClass
149+
protected override bool IsPrimeInternal(uint value)
150+
=> Polynomial.IsPrimeInternal(value);
151+
}
152+
100153
public class U64 : PrimalityU64Base
101154
{
102155
// ReSharper disable once MemberHidesStaticFromOuterClass

source/PrimalityBase.cs

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,198 @@ public IEnumerable<T> Factors(T value, bool omitOneAndValue)
9595
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
9696
}
9797

98+
public abstract class PrimalityU32Base : PrimalityBase<uint>
99+
{
100+
// ReSharper disable once OptionalParameterHierarchyMismatch
101+
protected override IEnumerable<uint> ValidPrimeTests(uint staringAt = 2U)
102+
{
103+
var n = staringAt;
104+
if (n > 2U)
105+
{
106+
if (n % 2U == 0)
107+
n++;
108+
}
109+
else
110+
{
111+
yield return 2U;
112+
n = 3U;
113+
}
114+
115+
for (; n < uint.MaxValue - 1U; n += 2U)
116+
yield return n;
117+
}
118+
119+
/// <inheritdoc />
120+
public override IEnumerator<uint> GetEnumerator()
121+
=> StartingAt(2U).GetEnumerator();
122+
123+
/// <summary>
124+
/// Returns an enumerable that will iterate every prime starting at the starting value.
125+
/// </summary>
126+
/// <param name="value">Allows for skipping ahead any integer before checking for inclusive and subsequent primes. Passing a negative number here will produce a negative set of prime numbers.</param>
127+
/// <returns>An enumerable that will iterate every prime starting at the starting value</returns>
128+
[SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global")]
129+
public IEnumerable<int> StartingAt(int value)
130+
{
131+
var absStart = (uint)Math.Abs(value);
132+
if (value < 0)
133+
{
134+
return StartingAt(absStart)
135+
.TakeWhile(v => v < int.MaxValue)
136+
.Select(v => -Convert.ToInt32(v));
137+
}
138+
139+
return StartingAt(absStart)
140+
.TakeWhile(v => v < int.MaxValue)
141+
.Select(Convert.ToInt32);
142+
}
143+
144+
/// <inheritdoc />
145+
public override IEnumerable<KeyValuePair<uint, uint>> Indexed()
146+
{
147+
uint count = 0U;
148+
foreach (var n in this)
149+
{
150+
count++;
151+
yield return new KeyValuePair<uint, uint>(count, n);
152+
}
153+
}
154+
155+
/// <inheritdoc />
156+
public override ParallelQuery<uint> InParallel(in uint staringAt, ushort? degreeOfParallelism = null)
157+
{
158+
var tests = ValidPrimeTests(staringAt)
159+
.AsParallel().AsOrdered();
160+
161+
if (degreeOfParallelism.HasValue)
162+
tests = tests.WithDegreeOfParallelism(degreeOfParallelism.Value);
163+
164+
return tests.Where(IsPrime);
165+
}
166+
167+
/// <inheritdoc />
168+
public override ParallelQuery<uint> InParallel(ushort? degreeOfParallelism = null)
169+
=> InParallel(2U, degreeOfParallelism);
170+
171+
/// <inheritdoc />
172+
public override IEnumerable<uint> Factors(uint value)
173+
{
174+
if (value != 0U)
175+
{
176+
yield return 1U;
177+
if (value == 1U) yield break;
178+
var last = 1U;
179+
180+
// For larger numbers, a quick prime check can prevent large iterations.
181+
if (IsFactorable(value))
182+
{
183+
foreach (var p in this)
184+
{
185+
var stop = value / last; // The list of possibilities shrinks for each test.
186+
if (p > stop) break; // Exceeded possibilities?
187+
while ((value % p) == 0U)
188+
{
189+
value /= p;
190+
yield return p;
191+
if (value == 1U) yield break;
192+
}
193+
last = p;
194+
}
195+
}
196+
}
197+
198+
yield return value;
199+
}
200+
201+
/// <summary>
202+
/// Iterates the prime factors of the provided value.
203+
/// First multiple is always 0, 1 or -1.
204+
/// </summary>
205+
/// <param name="value">The value to factorize.</param>
206+
/// <returns>An enumerable that contains the prime factors of the provided value starting with 0, 1, or -1 for sign retention.</returns>
207+
public IEnumerable<int> Factors(int value)
208+
{
209+
if (value != 0L)
210+
{
211+
yield return value < 0 ? -1 : 1;
212+
if (value < 0) value = Math.Abs(value);
213+
if (value == 1)
214+
yield break;
215+
216+
var last = 1;
217+
218+
// For larger numbers, a quick prime check can prevent large iterations.
219+
if (IsFactorable(value))
220+
{
221+
foreach (var p in StartingAt(2))
222+
{
223+
var stop = value / last; // The list of possibilities shrinks for each test.
224+
if (p > stop) break; // Exceeded possibilities?
225+
while ((value % p) == 0)
226+
{
227+
value /= p;
228+
yield return p;
229+
if (value == 1) yield break;
230+
}
231+
last = p;
232+
}
233+
}
234+
}
235+
yield return value;
236+
}
237+
238+
protected bool IsFactorable(int value)
239+
=> !IsPrime(value);
240+
241+
/// <inheritdoc />
242+
public override uint Next(in uint after)
243+
=> StartingAt(after + 1).First();
244+
245+
/// <summary>
246+
/// Finds the next prime number after the number given.
247+
/// </summary>
248+
/// <param name="after">The excluded lower boundary to start with. If this number is negative, then the result will be the next greater magnitude value prime as negative number.</param>
249+
/// <returns>The next prime after the number provided.</returns>
250+
public int Next(in int after)
251+
=> StartingAt(after + 1).First();
252+
253+
/// <inheritdoc />
254+
public sealed override bool IsPrime(in uint value)
255+
{
256+
switch (value)
257+
{
258+
// 0 and 1 are not prime numbers
259+
case 0U:
260+
case 1U:
261+
return false;
262+
case 2U:
263+
case 3U:
264+
return true;
265+
266+
default:
267+
268+
if (value % 2U == 0 || value % 3U == 0)
269+
return false;
270+
271+
return IsPrimeInternal(value);
272+
}
273+
}
274+
275+
/// <summary>
276+
/// Returns true if the value provided is prime.
277+
/// </summary>
278+
/// <param name="value">The value to validate.</param>
279+
/// <returns>True if the value provided is prime</returns>
280+
public bool IsPrime(int value)
281+
=> IsPrime(Convert.ToUInt32(Math.Abs(value)));
282+
283+
/// <summary>
284+
/// Should only check for primes that aren't divisible by 2 or 3.
285+
/// </summary>
286+
protected abstract bool IsPrimeInternal(uint value);
287+
}
288+
289+
98290
public abstract class PrimalityU64Base : PrimalityBase<ulong>
99291
{
100292
// ReSharper disable once OptionalParameterHierarchyMismatch

source/Primes.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ namespace Open.Numeric.Primes
1313
/// </summary>
1414
public static class Number
1515
{
16+
/// <summary>
17+
/// Validates if a number is prime.
18+
/// </summary>
19+
/// <param name="value">Value to verify.</param>
20+
/// <returns>True if the provided value is a prime number</returns>
21+
public static bool IsPrime(uint value)
22+
=> Polynomial.IsPrimeInternal(value);
23+
1624
/// <summary>
1725
/// Validates if a number is prime.
1826
/// </summary>

0 commit comments

Comments
 (0)