11//! Lucas primality test.
22use crypto_bigint:: {
33 modular:: runtime_mod:: { DynResidue , DynResidueParams } ,
4- Integer , Invert , Limb , Uint , Word ,
4+ Integer , Limb , Uint , Word ,
55} ;
66
77use super :: {
@@ -197,7 +197,10 @@ fn decompose<const L: usize>(n: &Uint<L>) -> (u32, Uint<L>) {
197197#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
198198pub enum LucasCheck {
199199 /// Introduced by Baillie & Wagstaff[^Baillie1980].
200- /// If any of `V(d*2^r) == 0` for `0 <= r < s`, and `U(d) == 0`, report the number as prime.
200+ /// If either of the following is true:
201+ /// - any of `V(d*2^r) == 0` for `0 <= r < s`,
202+ /// - `U(d) == 0`,
203+ /// report the number as prime.
201204 ///
202205 /// If the base is [`SelfridgeBase`], known false positives constitute OEIS:A217255[^A217255].
203206 ///
@@ -210,8 +213,14 @@ pub enum LucasCheck {
210213 /// [^A217255]: <https://oeis.org/A217255>
211214 Strong ,
212215
213- /// If any of `V(d*2^r) == 0` for `0 <= r < s`, and `V(d) == ±2` report the number as prime.
214- /// The second condition is only checked if `Q == 1`, otherwise it is considered to be true.
216+ /// A [`LucasCheck::ExtraStrong`] without checking for `U(d)`.
217+ /// That is, if either of the following is true:
218+ /// - any of `V(d*2^r) == 0` for `0 <= r < s`,
219+ /// - `V(d) == ±2`,
220+ /// report the number as prime.
221+ ///
222+ /// Note: the second condition is only checked if `Q == 1`,
223+ /// otherwise it is considered to be true.
215224 ///
216225 /// If the base is [`BruteForceBase`], some known false positives
217226 /// are listed by Jacobsen[^Jacobsen].
@@ -225,7 +234,9 @@ pub enum LucasCheck {
225234 AlmostExtraStrong ,
226235
227236 /// Introduced by Mo[^Mo1993], and also described by Grantham[^Grantham2001].
228- /// If [`LucasCheck::Strong`] check passes, and `V(d) == ±2`,
237+ /// If either of the following is true:
238+ /// - any of `V(d*2^r) == 0` for `0 <= r < s`,
239+ /// - `U(d) == 0` and `V(d) == ±2`,
229240 /// report the number as prime.
230241 ///
231242 /// Note that this check only differs from [`LucasCheck::Strong`] if `Q == 1`.
@@ -335,102 +346,89 @@ pub fn lucas_test<const L: usize>(
335346 DynResidue :: < L > :: new ( & Uint :: < L > :: from ( p) , params)
336347 } ;
337348
338- // Compute d-th element of Lucas sequence V_d(P, Q), where:
339- //
340- // V_0 = 2
341- // V_1 = P
342- // V_k = P V_{k-1} - Q V_{k-2}.
349+ // Compute d-th element of Lucas sequence (U_d(P, Q), V_d(P, Q)), where:
343350 //
344- // In general V(k) = α^k + β^k, where α and β are roots of x^2 - Px + Q.
345- // [^Crandall2005], eq. (3.14) observe that for 0 <= j <= k,
351+ // V_0 = 2
352+ // U_0 = 1
346353 //
347- // V_{j+k} = V_j V_k - Q^j * V_{k-j}.
354+ // U_{2k} = U_k V_k
355+ // V_{2k} = V_k^2 - 2 Q^k
348356 //
349- // So in particular, to quickly double the subscript:
350- //
351- // V_{2k} = V_k^2 - 2 * Q^k
352- // V_{2k+1} = V_k V_{k+1} - Q^k
357+ // U_{k+1} = (P U_k + V_k) / 2
358+ // V_{k+1} = (D U_k + P V_k) / 2
353359 //
360+ // (The propagation method is due to [^Baillie2021], Eqs. 13, 14, 16, 17)
354361 // We can therefore start with k=0 and build up to k=d in log2(d) steps.
355362
363+ // Starting with k = 0
356364 let mut vk = two; // keeps V_k
357- let mut vk1 = p ; // keeps V_{k+1}
365+ let mut uk = DynResidue :: < L > :: zero ( params ) ; // keeps U_k
358366 let mut qk = one; // keeps Q^k
359- let mut qk_times_p = if p_is_one { one } else { p } ; // keeps P Q^{k}
367+
368+ // D in Montgomery representation - note that it can be negative.
369+ let abs_d = DynResidue :: < L > :: new ( & Uint :: < L > :: from ( discriminant. abs_diff ( 0 ) ) , params) ;
370+ let d_m = if discriminant < 0 { -abs_d } else { abs_d } ;
360371
361372 for i in ( 0 ..d. bits_vartime ( ) ) . rev ( ) {
362- if d. bit_vartime ( i) {
363- // k' = 2k+1
373+ // k' = k * 2
364374
365- // V_k' = V_{2k+1} = V_k V_{k+1} - P Q^k
366- vk = vk * vk1 - qk_times_p;
375+ let u_2k = uk * vk;
376+ let v_2k = vk. square ( ) - ( qk + qk) ;
377+ let q_2k = qk. square ( ) ;
367378
368- // V_{k'+1} = V_{2k+2} = V_{k+1}^2 - 2 Q^{k+1}
369- let qk1 = qk * q; // Q^{k+1}
370- let two_qk1 = if q_is_one { two } else { qk1 + qk1 } ; // 2 Q^{k+1}
371- vk1 = vk1. square ( ) - two_qk1;
372- qk *= qk1;
373- } else {
374- // k' = 2k
379+ uk = u_2k;
380+ vk = v_2k;
381+ qk = q_2k;
375382
376- // V_{k'+1} = V_{2k+1} = V_k V_{k+1} - P Q^k
377- vk1 = vk * vk1 - qk_times_p ;
383+ if d . bit_vartime ( i ) {
384+ // k' = k + 1
378385
379- // V_k' = V_{2k} = V_k^2 - 2 Q^k
380- let two_qk = if q_is_one { two } else { qk + qk } ; // 2 Q^k
381- vk = vk. square ( ) - two_qk;
382- qk = qk. square ( ) ;
383- }
386+ let ( p_uk, p_vk) = if p_is_one { ( uk, vk) } else { ( p * uk, p * vk) } ;
384387
385- if p_is_one {
386- qk_times_p = qk;
387- } else {
388- qk_times_p = qk * p;
388+ let u_k1 = ( p_uk + vk) . div_by_2 ( ) ;
389+ let v_k1 = ( d_m * uk + p_vk) . div_by_2 ( ) ;
390+ let q_k1 = qk * q;
391+
392+ uk = u_k1;
393+ vk = v_k1;
394+ qk = q_k1;
389395 }
390396 }
391397
392- // Now k=d, so vk = V_d, vk_1 = V_{d+1} .
398+ // Now k=d, so vk = V_d and uk = U_d .
393399
394- // Extra strong check (from [^Mo1993]): `V_d == ±2 mod n`.
395- // Do it first since it is cheap.
396- //
397- // Note that it only applies if Q = 1, since it is a consequence
398- // of a property of Lucas series: V_k^2 - 4 Q^k = D U_k^2 mod n.
399- // If Q = 1 we can easily decompose the left side of the equation leading to the check above.
400- let vk_equals_two = if q_is_one {
401- vk == two || vk == minus_two
402- } else {
403- true
404- } ;
400+ // Check for the first sufficient condition in various strong checks.
405401
406- if vk_equals_two {
407- // Strong check:`U_d == 0 mod n`.
408- // As suggested by [^Jacobsen], apply Eq. (3.13) from [^Crandall2005]:
402+ if check == LucasCheck :: Strong && uk == zero {
403+ // Strong check: `U_d == 0 mod n`.
404+ return Primality :: ProbablyPrime ;
405+ } else if check == LucasCheck :: ExtraStrong || check == LucasCheck :: AlmostExtraStrong {
406+ // Extra strong check (from [^Mo1993]): `V_d == ±2 mod n` and `U_d == 0 mod n`.
409407 //
410- // U_k = D^{-1} (2 V_{k+1} - P V_k)
408+ // Note that the first identity only applies if `Q = 1`, since it is a consequence
409+ // of a property of Lucas series: `V_k^2 - 4 Q^k = D U_k^2 mod n`.
410+ // If `Q = 1` we can easily decompose the left side of the equation
411+ // leading to the check above.
411412 //
412- // Some implementations just test for 2 V_{k+1} == P V_{k},
413- // but we don't have any reference pseudoprime lists for this, so we are not doing it.
414- if check == LucasCheck :: Strong || check == LucasCheck :: ExtraStrong {
415- let abs_d = DynResidue :: < L > :: new ( & Uint :: < L > :: from ( discriminant. abs_diff ( 0 ) ) , params) ;
416- let d_m = if discriminant < 0 { -abs_d } else { abs_d } ;
417- // `d` is guaranteed non-zero by construction, so we can safely unwrap
418- let inv_d = <DynResidue < L > as Invert >:: invert ( & d_m) . unwrap ( ) ;
419-
420- let vk_times_p = if p_is_one { vk } else { vk * p } ;
421- let uk = inv_d * ( vk1 + vk1 - vk_times_p) ;
422-
423- if uk == zero {
424- return Primality :: ProbablyPrime ;
425- }
426- } else {
427- // This is "almost extra strong check": we only checked for `V_d` earlier.
428- if check == LucasCheck :: AlmostExtraStrong {
429- return Primality :: ProbablyPrime ;
430- }
413+ // If `Q != 1` we just consider it passed (we don't have a corresponding
414+ // pseudoprime list anyway).
415+
416+ let vk_equals_two = !q_is_one || ( vk == two || vk == minus_two) ;
417+
418+ if check == LucasCheck :: ExtraStrong && uk == zero && vk_equals_two {
419+ return Primality :: ProbablyPrime ;
420+ }
421+
422+ // "Almost extra strong" check skips the `U_d` check.
423+ // Since we have `U_d` anyway, it does not improve performance,
424+ // so it is only here for testing purposes, since we have a corresponding pseudoprime list.
425+ if check == LucasCheck :: AlmostExtraStrong && vk_equals_two {
426+ return Primality :: ProbablyPrime ;
431427 }
432428 }
433429
430+ // Second sufficient condition requires further propagating `V_k` up to `V_{n+1}`.
431+
434432 // Check if V_{2^t d} == 0 mod n for some 0 <= t < s.
435433 // (unless we're in Lucas-V mode, then we just propagate V_k)
436434
@@ -446,7 +444,7 @@ pub fn lucas_test<const L: usize>(
446444 }
447445
448446 // k' = 2k
449- // V(k') = V(2k) = V(k)² - 2 * Q^k
447+ // V_{k'} = V_k^2 - 2 Q^k
450448 vk = vk * vk - qk - qk;
451449
452450 if check != LucasCheck :: LucasV && vk == zero {
0 commit comments