@@ -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`.
@@ -359,7 +370,7 @@ pub fn lucas_test<const L: usize>(
359370 let d_m = if discriminant < 0 { -abs_d } else { abs_d } ;
360371
361372 for i in ( 0 ..d. bits_vartime ( ) ) . rev ( ) {
362- // k = k * 2
373+ // k' = k * 2
363374
364375 let u_2k = uk * vk;
365376 let v_2k = vk. square ( ) - ( qk + qk) ;
@@ -370,7 +381,7 @@ pub fn lucas_test<const L: usize>(
370381 qk = q_2k;
371382
372383 if d. bit_vartime ( i) {
373- // k = k + 1
384+ // k' = k + 1
374385
375386 let ( p_uk, p_vk) = if p_is_one { ( uk, vk) } else { ( p * uk, p * vk) } ;
376387
@@ -384,35 +395,40 @@ pub fn lucas_test<const L: usize>(
384395 }
385396 }
386397
387- // Now k=d, so vk = V_d, vk_1 = V_{d+1} .
398+ // Now k=d, so vk = V_d and uk = U_d .
388399
389- // Extra strong check (from [^Mo1993]): `V_d == ±2 mod n`.
390- // Do it first since it is cheap.
391- //
392- // Note that it only applies if Q = 1, since it is a consequence
393- // of a property of Lucas series: V_k^2 - 4 Q^k = D U_k^2 mod n.
394- // If Q = 1 we can easily decompose the left side of the equation leading to the check above.
395- let vk_equals_two = if q_is_one {
396- vk == two || vk == minus_two
397- } else {
398- true
399- } ;
400+ // Check for the first sufficient condition in various strong checks.
400401
401- if vk_equals_two {
402- if check == LucasCheck :: Strong || check == LucasCheck :: ExtraStrong {
403- // Strong check:`U_d == 0 mod n`.
404- if uk == zero {
405- return Primality :: ProbablyPrime ;
406- }
407- } else {
408- // This is "almost extra strong check": we only checked for `V_d` earlier,
409- // and the check for `U_d` is skipped.
410- if check == LucasCheck :: AlmostExtraStrong {
411- return Primality :: ProbablyPrime ;
412- }
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`.
407+ //
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.
412+ //
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 ;
413427 }
414428 }
415429
430+ // Second sufficient condition requires further propagating `V_k` up to `V_{n+1}`.
431+
416432 // Check if V_{2^t d} == 0 mod n for some 0 <= t < s.
417433 // (unless we're in Lucas-V mode, then we just propagate V_k)
418434
@@ -428,7 +444,7 @@ pub fn lucas_test<const L: usize>(
428444 }
429445
430446 // k' = 2k
431- // V(k') = V(2k) = V(k)² - 2 * Q^k
447+ // V_{k'} = V_k^2 - 2 Q^k
432448 vk = vk * vk - qk - qk;
433449
434450 if check != LucasCheck :: LucasV && vk == zero {
0 commit comments