Skip to content

Commit 616028f

Browse files
Merge pull request #2624 from fredrik-johansson/isprime81
Faster primality test between 65 and 128 bits
2 parents fe1d6c6 + 02b81fa commit 616028f

File tree

7 files changed

+906
-37
lines changed

7 files changed

+906
-37
lines changed

doc/source/ulong_extras.rst

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,33 @@ precomputed quotient for `b`:
776776
5. compute `h = b \check{a} - q n` (two single-word multiplications)
777777
6. if `h \ge n` then `q \gets q+1`
778778

779+
Double-limb modular arithmetic
780+
--------------------------------------------------------------------------------
781+
782+
.. function:: void n_ll_small_2_powmod(nn_ptr res, nn_srcptr exp, nn_srcptr m, nn_srcptr minv)
783+
void n_ll_small_powmod_triple(nn_ptr res1, nn_ptr res2, nn_ptr res3, ulong b1, ulong b2, ulong b3, nn_srcptr exp, nn_srcptr m, nn_srcptr minv)
784+
785+
Computes `b^e \bmod m` where `b` is a small single-limb integer,
786+
`e` is a double-limb integer stored in the array ``exp`` and
787+
`m` is a double-limb integer accompanied by the two-limb
788+
precomputed inverse `\lfloor 2^{3 \cdot \mathtt{FLINT\_BITS}} / \rfloor`
789+
stored in ``minv``. The function :func:`n_ll_small_2_powmod` is
790+
specialized for base `b = 2`, writing the double-limb result to
791+
``res``, while :func:`n_ll_small_powmod_triple`
792+
performs three simultaneous exponentations, writing the results
793+
for bases `b_1 \le b_2 \le b_3` to ``res1``, ``res2`` and ``res3``
794+
respectively.
795+
796+
We require `m > 2^{\mathtt{FLINT\_BITS}}`. In addition, both `m` and
797+
`b` must be "small" for correctness, i.e. the whole double-limb
798+
operand range is not supported.
799+
A sufficient criterion for correctness is that
800+
`(12 m)^2 < 2^{3 \cdot \mathtt{FLINT\_BITS}}` for the base-2 test
801+
and `(6 b_3 m)^2 < 2^{3 \cdot \mathtt{FLINT\_BITS}}` for the general
802+
test.
803+
804+
These are helper functions used by :func:`n_ll_is_prime`.
805+
779806
Divisibility testing
780807
--------------------------------------------------------------------------------
781808

@@ -1090,6 +1117,26 @@ Primality testing
10901117

10911118
This function is obsolete and currently just wraps :func:`n_is_prime`.
10921119

1120+
.. function:: int n_ll_is_prime(ulong nhi, ulong nlo)
1121+
1122+
Primality test for a double-limb integer `n` represented by
1123+
low part ``nlo`` and high part ``nhi``. The high part must be nonzero.
1124+
Returns 1 if `n` is certainly prime, 0 if `n` is certainly composite,
1125+
and -1 if unknown.
1126+
1127+
For `n` up to about 81 bits on a 64-bit machine, this function first does
1128+
trial division and then performs a strong probable prime test (Miller-Rabin
1129+
test) with the first 13 primes as witnesses. This has been shown to prove
1130+
primality for integers in this range [SorWeb2016]_, so the return
1131+
value in this range is always 0 or 1.
1132+
1133+
For larger `n` on a 64-bit machine, this function does trial division
1134+
and a base-2 test, returning either 0 or -1.
1135+
A return value of -1 thus indicates that `n` is at least a base
1136+
strong probable prime. Users may fall back on
1137+
:func:`fmpz_is_prime` for a proved result in this case.
1138+
1139+
On 32-bit machines, this function currently always returns -1.
10931140

10941141
Chinese remaindering
10951142
--------------------------------------------------------------------------------

src/fmpz/is_prime.c

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ static int _fmpz_is_prime(const fmpz_t n, int proved)
2525
const ulong * primes;
2626
const double * pinv;
2727

28-
fmpz_t F1, Fsqr, Fcub, R, t;
28+
fmpz_t F1, Fsqr, Fcub, R;
2929
int res = -1;
3030

3131
if (!COEFF_IS_MPZ(*n))
@@ -34,9 +34,8 @@ static int _fmpz_is_prime(const fmpz_t n, int proved)
3434
if (v <= 1)
3535
return 0;
3636

37-
/* Note: we want n_is_prime rather than n_is_probabprime
38-
even when proved == 0, because ns_is_prime handles general
39-
input faster. */
37+
/* Note: n_is_prime and n_is_probabprime are currently identical,
38+
so we ignore the proved flag. */
4039
return n_is_prime(v);
4140
}
4241
else
@@ -57,45 +56,39 @@ static int _fmpz_is_prime(const fmpz_t n, int proved)
5756
if (d[0] % 2 == 0)
5857
return 0;
5958

60-
bits = size * FLINT_BITS + FLINT_BIT_COUNT(d[size-1]);
61-
trial_primes = bits;
59+
if (size == 2 && FLINT_BITS == 64)
60+
{
61+
/* n_ll_is_prime does trial division, a base-2 sprp test, and may
62+
additionally be able to certify some inputs. Currently the
63+
certifications in n_ll_is_prime are faster than a Lucas test,
64+
so use it even when !proved. */
65+
res = n_ll_is_prime(d[1], d[0]);
66+
if (res != -1)
67+
return res;
68+
}
69+
else
70+
{
71+
bits = size * FLINT_BITS + FLINT_BIT_COUNT(d[size-1]);
72+
trial_primes = bits;
6273

63-
if (flint_mpn_factor_trial(d, size, 1, trial_primes))
64-
return 0;
65-
}
74+
if (flint_mpn_factor_trial(d, size, 1, trial_primes))
75+
return 0;
6676

67-
/* todo: use fmpz_is_perfect_power? */
68-
if (fmpz_is_square(n))
69-
return 0;
77+
/* todo: use fmpz_is_perfect_power? */
78+
if (fmpz_is_square(n))
79+
return 0;
7080

71-
if (!proved)
72-
return fmpz_is_probabprime_BPSW(n);
73-
74-
/* Fast deterministic Miller-Rabin test up to about 81 bits. This choice of
75-
bases certifies primality for n < 3317044064679887385961981;
76-
see https://doi.org/10.1090/mcom/3134 */
77-
fmpz_init(t);
78-
fmpz_tdiv_q_2exp(t, n, 64);
79-
if (fmpz_cmp_ui(t, 179817) < 0)
80-
{
81-
static const char bases[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 0 };
82-
83-
for (i = 0; bases[i] != 0; i++)
84-
{
85-
fmpz_set_ui(t, bases[i]);
86-
if (!fmpz_is_strong_probabprime(n, t))
87-
return 0; /* no need to clear t since it is small */
81+
/* Do a single base-2 test to rule out most composites */
82+
fmpz base2 = 2;
83+
if (!fmpz_is_strong_probabprime(n, &base2))
84+
return 0;
8885
}
89-
90-
return 1;
9186
}
9287

93-
/* Do a single base-2 test to rule out most composites */
94-
fmpz_set_ui(t, 2);
95-
if (!fmpz_is_strong_probabprime(n, t))
96-
return 0;
97-
98-
fmpz_clear(t);
88+
/* At this point n has no small factor and is at least a base-2 sprp.
89+
Adding a Lucas test makes this a BPSW test. */
90+
if (!proved)
91+
return fmpz_is_probabprime_lucas(n);
9992

10093
logd = fmpz_dlog(n);
10194
limit = (ulong) (logd*logd*logd/100.0) + 20;

src/ulong_extras.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,11 @@ ulong n_invmod(ulong x, ulong y)
371371

372372
ulong n_binvert(ulong a);
373373

374+
void n_ll_small_preinv(nn_ptr minv, nn_srcptr m);
375+
void n_ll_small_powmod_triple(nn_ptr res1, nn_ptr res2, nn_ptr res3, ulong b1,
376+
ulong b2, ulong b3, nn_srcptr exp, nn_srcptr m, nn_srcptr minv);
377+
void n_ll_small_2_powmod(nn_ptr res, nn_srcptr exp, nn_srcptr m, nn_srcptr minv);
378+
374379
/* Modular multiplication with fixed operand **********************************/
375380

376381
ULONG_EXTRAS_INLINE
@@ -504,6 +509,8 @@ void n_prime_pi_bounds(ulong *lo, ulong *hi, ulong n);
504509

505510
ulong n_nextprime(ulong n, int FLINT_UNUSED(proved));
506511

512+
int n_ll_is_prime(ulong nhi, ulong nlo);
513+
507514
/* Factorisation *************************************************************/
508515

509516
#define FLINT_FACTOR_TRIAL_PRIMES_BEFORE_PRIMALITY_TEST 64

0 commit comments

Comments
 (0)