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 :: {
@@ -335,57 +335,52 @@ pub fn lucas_test<const L: usize>(
335335 DynResidue :: < L > :: new ( & Uint :: < L > :: from ( p) , params)
336336 } ;
337337
338- // Compute d-th element of Lucas sequence V_d(P, Q), where:
338+ // Compute d-th element of Lucas sequence (U_d(P, Q), V_d(P, Q) ), where:
339339 //
340- // V_0 = 2
341- // V_1 = P
342- // V_k = P V_{k-1} - Q V_{k-2}.
340+ // V_0 = 2
341+ // U_0 = 1
343342 //
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,
343+ // U_{2k} = U_k V_k
344+ // V_{2k} = V_k^2 - 2 Q^k
346345 //
347- // V_{j+k} = V_j V_k - Q^j * V_{k-j}.
348- //
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
346+ // U_{k+1} = (P U_k + V_k) / 2
347+ // V_{k+1} = (D U_k + P V_k) / 2
353348 //
349+ // (The propagation method is due to [^Baillie2021], Eqs. 13, 14, 16, 17)
354350 // We can therefore start with k=0 and build up to k=d in log2(d) steps.
355351
352+ // Starting with k = 0
356353 let mut vk = two; // keeps V_k
357- let mut vk1 = p ; // keeps V_{k+1}
354+ let mut uk = DynResidue :: < L > :: zero ( params ) ; // keeps U_k
358355 let mut qk = one; // keeps Q^k
359- let mut qk_times_p = if p_is_one { one } else { p } ; // keeps P Q^{k}
356+
357+ // D in Montgomery representation - note that it can be negative.
358+ let abs_d = DynResidue :: < L > :: new ( & Uint :: < L > :: from ( discriminant. abs_diff ( 0 ) ) , params) ;
359+ let d_m = if discriminant < 0 { -abs_d } else { abs_d } ;
360360
361361 for i in ( 0 ..d. bits_vartime ( ) ) . rev ( ) {
362- if d. bit_vartime ( i) {
363- // k' = 2k+1
362+ // k = k * 2
364363
365- // V_k' = V_{2k+1} = V_k V_{k+1} - P Q^k
366- vk = vk * vk1 - qk_times_p;
364+ let u_2k = uk * vk;
365+ let v_2k = vk. square ( ) - ( qk + qk) ;
366+ let q_2k = qk. square ( ) ;
367367
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
368+ uk = u_2k;
369+ vk = v_2k;
370+ qk = q_2k;
371+
372+ if d. bit_vartime ( i) {
373+ // k = k + 1
375374
376- // V_{k'+1} = V_{2k+1} = V_k V_{k+1} - P Q^k
377- vk1 = vk * vk1 - qk_times_p;
375+ let ( p_uk, p_vk) = if p_is_one { ( uk, vk) } else { ( p * uk, p * vk) } ;
378376
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- }
377+ let u_k1 = ( p_uk + vk) . div_by_2 ( ) ;
378+ let v_k1 = ( d_m * uk + p_vk) . div_by_2 ( ) ;
379+ let q_k1 = qk * q;
384380
385- if p_is_one {
386- qk_times_p = qk;
387- } else {
388- qk_times_p = qk * p;
381+ uk = u_k1;
382+ vk = v_k1;
383+ qk = q_k1;
389384 }
390385 }
391386
@@ -404,27 +399,14 @@ pub fn lucas_test<const L: usize>(
404399 } ;
405400
406401 if vk_equals_two {
407- // Strong check:`U_d == 0 mod n`.
408- // As suggested by [^Jacobsen], apply Eq. (3.13) from [^Crandall2005]:
409- //
410- // U_k = D^{-1} (2 V_{k+1} - P V_k)
411- //
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.
414402 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-
403+ // Strong check:`U_d == 0 mod n`.
423404 if uk == zero {
424405 return Primality :: ProbablyPrime ;
425406 }
426407 } else {
427- // This is "almost extra strong check": we only checked for `V_d` earlier.
408+ // This is "almost extra strong check": we only checked for `V_d` earlier,
409+ // and the check for `U_d` is skipped.
428410 if check == LucasCheck :: AlmostExtraStrong {
429411 return Primality :: ProbablyPrime ;
430412 }
0 commit comments