MathgeomGLS
251 строка · 9.2 Кб
1{---------------------------------------------------------------------------}
2{ }
3{ File: Velthuis.BigIntegers.Primes.pas }
4{ Function: Prime functions for BigIntegers }
5{ Language: Delphi version XE2 or later }
6{ Author: Rudy Velthuis }
7{ Copyright: (c) 2017 Rudy Velthuis }
8{ Notes: See http://rvelthuis.de/programs/bigintegers.html }
9{ See https://github.com/rvelthuis/BigNumbers }
10{ }
11{ License: Redistribution and use in source and binary forms, with or }
12{ without modification, are permitted provided that the }
13{ following conditions are met: }
14{ }
15{ * Redistributions of source code must retain the above }
16{ copyright notice, this list of conditions and the following }
17{ disclaimer. }
18{ * Redistributions in binary form must reproduce the above }
19{ copyright notice, this list of conditions and the following }
20{ disclaimer in the documentation and/or other materials }
21{ provided with the distribution. }
22{ }
23{ Disclaimer: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" }
24{ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT }
25{ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND }
26{ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO }
27{ EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE }
28{ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, }
29{ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
30{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, }
31{ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED }
32{ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT }
33{ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) }
34{ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF }
35{ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. }
36{ }
37{---------------------------------------------------------------------------}
38
39unit Velthuis.BigIntegers.Primes;
40
41interface
42
43uses
44Velthuis.BigIntegers;
45
46type
47/// <summary>Type indicating primality of a number.</summary>
48TPrimality = (
49/// <summary>Number is definitely composite.</summary>
50primComposite,
51/// <summary>Number is probably prime.</summary>
52primProbablyPrime,
53/// <summary>Number is definitely prime.</summary>
54primPrime
55);
56
57/// <summary>Detects if N is probably prime according to a Miller-Rabin test.</summary>
58/// <param name="N">The number to be tested</param>
59/// <param name="Precision">Determines the probability of the test, which is 1.0 - 0.25^Precision</param>
60/// <returns>True if N is prime with the given precision, False if N is definitely composite.</returns>
61function IsProbablePrime(const N: BigInteger; Precision: Integer): Boolean;
62
63/// <summary>Detects if N is prime, probably prime or composite. Deterministically correct for N < 341,550,071,728,321, otherwise
64/// with a probability determined by the Precision parameter</summary>
65/// <param name="N">The number to be tested</param>
66/// <param name="Precision">For values >= 341,550,071,728,321, IsProbablyPrime(N, Precision) is called</param>
67/// <returns>Returns primComposite if N is definitely composite, primProbablyPrime if N is probably prime and
68/// primPrime if N is definitely prime.</returns>
69function IsPrime(const N: BigInteger; Precision: Integer): TPrimality;
70
71/// <summary>Returns a random probably prime number.</summary>
72/// <param name="NumBits">Maximum number of bits of th random number</param>
73/// <param name="Precision">Precision to be used for IsProbablePrime</param>
74/// <returns> a random prime number that is probably prime with the given precision.</returns>
75function RandomProbablePrime(NumBits: Integer; Precision: Integer): BigInteger;
76
77/// <summary>Returns a probable prime >= N.</summary>
78/// <param name="N">Number to start with</param>
79/// <param name="Precision">Precision for primality test</param>
80/// <returns>A number >= N that is probably prime with the given precision.</returns>
81function NextProbablePrime(const N: BigInteger; Precision: Integer): BigInteger;
82
83/// <summary>Checks if the (usually randomly chosen) number A witnesses N's compositeness.</summary>
84/// <param name="A">Value to test with</param>
85/// <param name="N">Number to be tested as composite</param>
86///
87/// <remarks>See https://en.wikipedia.org/wiki/Miller-Rabin_primality_test#Algorithm_and_running_time</remarks>
88function IsWitness(const A, N: BigInteger): Boolean;
89
90function IsComposite(const A, D, N: BigInteger; S: Integer): Boolean;
91
92implementation
93
94// See https://en.wikipedia.org/wiki/Miller-Rabin_primality_test.
95
96uses
97Velthuis.RandomNumbers, System.SysUtils;
98
99var
100Two: BigInteger;
101DeterminicityThreshold: BigInteger;
102CertainlyComposite: BigInteger;
103Random: IRandom;
104
105// Rabin-Miller test, deterministically correct for N < 341,550,071,728,321.
106// http://rosettacode.org/wiki/Miller-Rabin_primality_test#Python:_Proved_correct_up_to_large_N
107// If you want to improve upon this:
108// https://en.wikipedia.org/wiki/Miller-Rabin_primality_test#Deterministic_variants_of_the_test
109function IsPrime(const N: BigInteger; Precision: Integer): TPrimality;
110var
111R: Integer;
112I: Integer;
113PrimesToTest: Integer;
114D: BigInteger;
115N64: UInt64;
116const
117CPrimesToTest: array[0..6] of Integer = (2, 3, 5, 7, 11, 13, 17);
118CProbabilityResults: array[Boolean] of TPrimality = (primComposite, primProbablyPrime);
119begin
120if BigInteger.Compare(N, DeterminicityThreshold) > 0 then
121Exit(CProbabilityResults[IsProbablePrime(N, Precision)]);
122
123if BigInteger.Compare(N, CertainlyComposite) = 0 then
124Exit(primComposite);
125
126N64 := UInt64(N);
127if N64 > Int64(3474749660383) then
128PrimesToTest := 7
129else if N64 > Int64(2152302898747) then
130PrimesToTest := 6
131else if N64 > Int64(118670087467) then
132PrimesToTest := 5
133else if N64 > Int64(25326001) then
134PrimesToTest := 4
135else if N64 > Int64(1373653) then
136PrimesToTest := 3
137else
138PrimesToTest := 2;
139
140D := N - BigInteger.One;
141R := 0;
142
143while D.IsEven do
144begin
145D := D shr 1;
146Inc(R);
147end;
148
149for I := 0 to PrimesToTest - 1 do
150if IsComposite(CPrimesToTest[I], D, N, R) then
151Exit(primComposite);
152
153Result := primPrime;
154end;
155
156// Check if N is probably prime with a probability of at least 1.0 - 0.25^Precision
157function IsProbablePrime(const N: BigInteger; Precision: Integer): Boolean;
158var
159I: Integer;
160A, NLessOne: BigInteger;
161begin
162if (N = 2) or (N = 3) then
163Exit(True)
164else if (N = BigInteger.Zero) or (N = BigInteger.One) or (N.IsEven) then
165Exit(False);
166
167NLessOne := N;
168Dec(NLessOne);
169
170for I := 1 to Precision do
171begin
172repeat
173A := BigInteger.Create(N.BitLength, Random);
174until (A > BigInteger.One) and (A < NLessOne);
175
176if IsWitness(A, N) then
177Exit(False);
178end;
179Result := True;
180end;
181
182function RandomProbablePrime(NumBits: Integer; Precision: Integer): BigInteger;
183begin
184repeat
185Result := BigInteger.Create(NumBits, Random);
186until IsProbablePrime(Result, Precision);
187end;
188
189// Next probable prime >= N.
190function NextProbablePrime(const N: BigInteger; Precision: Integer): BigInteger;
191var
192Two: BigInteger;
193begin
194Two := 2;
195Result := N;
196if Result = Two then
197Exit;
198if Result.IsEven then
199Result := Result.FlipBit(0);
200while not IsProbablePrime(Result, Precision) do
201begin
202Result := Result + Two;
203end;
204Result := N;
205end;
206
207function IsWitness(const A, N: BigInteger): Boolean;
208var
209R: Integer;
210D: BigInteger;
211begin
212// Write N - 1 as (2 ^ Power) * Factor, where Factor is odd.
213// Repeatedly try to divide N - 1 by 2
214R := 1;
215D := (N - BigInteger.One) shr 1;
216while D.IsEven do
217begin
218D := D shr 1;
219Inc(R);
220end;
221
222// Now check if A is a witness to N's compositeness
223Result := IsComposite(A, D, N, R);
224end;
225
226function IsComposite(const A, D, N: BigInteger; S: Integer): Boolean;
227var
228I: Integer;
229NLessOne: BigInteger;
230begin
231NLessOne := N - BigInteger.One;
232
233// if A^(2^0 * D) ≡ 1 (mod N) then prime.
234if BigInteger.ModPow(A, D, N) = BigInteger.One then
235Exit(False);
236
237for I := 0 to S - 1 do
238// if A^(2^I * D) ≡ N - 1 (mod N) then prime.
239if BigInteger.ModPow(A, (BigInteger.One shl I) * D, N) = NLessOne then
240Exit(False);
241
242Result := True;
243end;
244
245initialization
246Two := 2;
247DeterminicityThreshold := UInt64(341550071728321);
248CertainlyComposite := UInt64(3215031751);
249Random := TRandom.Create(Round(Now * SecsPerDay * MSecsPerSec));
250
251end.
252
253
254