1use core::num::NonZero;
3use crypto_bigint::{Integer, Limb, Monty, Odd, Square, Word};
4
5use super::{
6 gcd::gcd_vartime,
7 jacobi::{jacobi_symbol_vartime, JacobiSymbol},
8 Primality,
9};
10
11const MAX_ATTEMPTS: usize = 10_000;
15
16const ATTEMPTS_BEFORE_SQRT: usize = 30;
25
26pub trait LucasBase {
28 fn generate<T: Integer>(&self, n: &Odd<T>) -> Result<(Word, Word, bool), Primality>;
32}
33
34#[derive(Copy, Clone, Debug, PartialEq, Eq)]
46pub struct SelfridgeBase;
47
48impl LucasBase for SelfridgeBase {
49 fn generate<T: Integer>(&self, n: &Odd<T>) -> Result<(Word, Word, bool), Primality> {
50 let mut abs_d = 5;
51 let mut d_is_negative = false;
52 let n_is_small = n.bits_vartime() < Word::BITS; let small_n = n.as_ref().as_ref()[0].0;
54 let mut attempts = 0;
55 loop {
56 if attempts >= MAX_ATTEMPTS {
57 panic!("internal error: cannot find (D/n) = -1 for {:?}", n)
58 }
59
60 if attempts >= ATTEMPTS_BEFORE_SQRT {
61 let sqrt_n = n.sqrt_vartime();
62 if &sqrt_n.wrapping_mul(&sqrt_n) == n.as_ref() {
63 return Err(Primality::Composite);
64 }
65 }
66
67 let j = jacobi_symbol_vartime(abs_d, d_is_negative, n);
68
69 if j == JacobiSymbol::MinusOne {
70 break;
71 }
72 if j == JacobiSymbol::Zero {
73 if !(n_is_small && small_n == abs_d) {
80 return Err(Primality::Composite);
81 }
82 }
83
84 attempts += 1;
85 d_is_negative = !d_is_negative;
86 abs_d += 2;
87 }
88
89 let (abs_q, q_is_negative) = if d_is_negative {
92 ((abs_d + 1) / 4, false)
93 } else {
94 ((abs_d - 1) / 4, true)
95 };
96
97 Ok((1, abs_q, q_is_negative))
98 }
99}
100
101#[derive(Copy, Clone, Debug, PartialEq, Eq)]
111pub struct AStarBase;
112
113impl LucasBase for AStarBase {
114 fn generate<T: Integer>(&self, n: &Odd<T>) -> Result<(Word, Word, bool), Primality> {
115 SelfridgeBase.generate(n).map(|(p, abs_q, q_is_negative)| {
116 if abs_q == 1 && q_is_negative {
117 (5, 5, false)
118 } else {
119 (p, abs_q, q_is_negative)
120 }
121 })
122 }
123}
124
125#[derive(Copy, Clone, Debug, PartialEq, Eq)]
134pub struct BruteForceBase;
135
136impl LucasBase for BruteForceBase {
137 fn generate<T: Integer>(&self, n: &Odd<T>) -> Result<(Word, Word, bool), Primality> {
138 let mut p = 3;
139 let mut attempts = 0;
140
141 loop {
142 if attempts >= MAX_ATTEMPTS {
143 panic!("internal error: cannot find (D/n) = -1 for {:?}", n)
144 }
145
146 if attempts >= ATTEMPTS_BEFORE_SQRT {
147 let sqrt_n = n.sqrt_vartime();
148 if &sqrt_n.wrapping_mul(&sqrt_n) == n.as_ref() {
149 return Err(Primality::Composite);
150 }
151 }
152
153 let j = jacobi_symbol_vartime(p * p - 4, false, n);
155
156 if j == JacobiSymbol::MinusOne {
157 break;
158 }
159 if j == JacobiSymbol::Zero {
160 let primality = if n.as_ref() == &T::from_limb_like(Limb::from(p + 2), n.as_ref()) {
166 Primality::Prime
167 } else {
168 Primality::Composite
169 };
170 return Err(primality);
171 }
172
173 attempts += 1;
174 p += 1;
175 }
176
177 Ok((p, 1, false))
178 }
179}
180
181fn decompose<T: Integer>(n: &Odd<T>) -> (u32, Odd<T>) {
183 let one = T::one_like(n);
187 let s = n.trailing_ones_vartime();
188 let d = if s < n.bits_precision() {
189 n.as_ref()
193 .overflowing_shr_vartime(s)
194 .expect("shift should be within range by construction")
195 .checked_add(&one)
196 .expect("addition should not overflow by construction")
197 } else {
198 one
199 };
200
201 (s, Odd::new(d).expect("`d` should be odd by construction"))
202}
203
204#[derive(Copy, Clone, Debug, PartialEq, Eq)]
210pub enum LucasCheck {
211 Strong,
228
229 AlmostExtraStrong,
249
250 ExtraStrong,
273
274 LucasV,
285}
286
287pub fn lucas_test<T: Integer>(candidate: Odd<T>, base: impl LucasBase, check: LucasCheck) -> Primality {
291 let to_integer = |x: Word| T::from_limb_like(Limb::from(x), candidate.as_ref());
299
300 let (p, abs_q, q_is_negative) = match base.generate(&candidate) {
302 Ok(pq) => pq,
303 Err(primality) => return primality,
304 };
305
306 let (abs_d, d_is_negative) = if q_is_negative {
308 (p * p + 4 * abs_q, false)
309 } else {
310 let t1 = p * p;
311 let t2 = 4 * abs_q;
312 if t2 > t1 {
313 (t2 - t1, true)
314 } else {
315 (t1 - t2, false)
316 }
317 };
318
319 let p_is_one = p == 1;
321 let q_is_one = abs_q == 1 && !q_is_negative;
322
323 if abs_q != 1
334 && gcd_vartime(
335 candidate.as_ref(),
336 NonZero::new(abs_q).expect("q is not zero by construction"),
337 ) != 1
338 && candidate.as_ref() > &to_integer(abs_q)
339 {
340 return Primality::Composite;
341 }
342
343 let (s, d) = decompose(&candidate);
346
347 let params = <T as Integer>::Monty::new_params_vartime(candidate.clone());
351
352 let zero = <T as Integer>::Monty::zero(params.clone());
353 let one = <T as Integer>::Monty::one(params.clone());
354 let two = one.clone() + &one;
355 let minus_two = -two.clone();
356
357 let q = if q_is_one {
360 one.clone()
361 } else {
362 let abs_q = <T as Integer>::Monty::new(to_integer(abs_q), params.clone());
363 if q_is_negative {
364 -abs_q
365 } else {
366 abs_q
367 }
368 };
369
370 let p = if p_is_one {
373 one.clone()
374 } else {
375 <T as Integer>::Monty::new(to_integer(p), params.clone())
376 };
377
378 let mut vk = two.clone(); let mut uk = <T as Integer>::Monty::zero(params.clone()); let mut qk = one.clone(); let abs_d = <T as Integer>::Monty::new(to_integer(abs_d), params);
399 let d_m = if d_is_negative { -abs_d } else { abs_d };
400
401 for i in (0..d.bits_vartime()).rev() {
402 let u_2k = uk * &vk;
405 let v_2k = vk.square() - &(qk.clone() + &qk);
406 let q_2k = qk.square();
407
408 uk = u_2k;
409 vk = v_2k;
410 qk = q_2k;
411
412 if d.bit_vartime(i) {
413 let (p_uk, p_vk) = if p_is_one {
416 (uk.clone(), vk.clone())
417 } else {
418 (p.clone() * &uk, p.clone() * &vk)
419 };
420
421 let u_k1 = (p_uk + &vk).div_by_2();
422 let v_k1 = (d_m.clone() * &uk + &p_vk).div_by_2();
423 let q_k1 = qk * &q;
424
425 uk = u_k1;
426 vk = v_k1;
427 qk = q_k1;
428 }
429 }
430
431 if check == LucasCheck::Strong && uk == zero {
436 return Primality::ProbablyPrime;
438 } else if check == LucasCheck::ExtraStrong || check == LucasCheck::AlmostExtraStrong {
439 let vk_equals_two = !q_is_one || (vk == two || vk == minus_two);
450
451 if check == LucasCheck::ExtraStrong && uk == zero && vk_equals_two {
452 return Primality::ProbablyPrime;
453 }
454
455 if check == LucasCheck::AlmostExtraStrong && vk_equals_two {
459 return Primality::ProbablyPrime;
460 }
461 }
462
463 if check != LucasCheck::LucasV && vk == zero {
469 return Primality::ProbablyPrime;
470 }
471
472 for _ in 1..s {
473 if check != LucasCheck::LucasV && q_is_one && (vk == two || vk == minus_two) {
476 return Primality::Composite;
477 }
478
479 vk = vk.square() - &qk - &qk;
482
483 if check != LucasCheck::LucasV && vk == zero {
484 return Primality::ProbablyPrime;
485 }
486
487 if !q_is_one {
488 qk = qk.square();
489 }
490 }
491
492 if check == LucasCheck::LucasV {
493 vk = vk.square() - &qk - &qk; if vk == q.clone() + &q {
499 return Primality::ProbablyPrime;
500 }
501 }
502
503 Primality::Composite
504}
505
506#[cfg(test)]
507mod tests {
508
509 use alloc::format;
510
511 use crypto_bigint::{Integer, Odd, Uint, Word, U128, U64};
512
513 #[cfg(feature = "tests-exhaustive")]
514 use num_prime::nt_funcs::is_prime64;
515
516 use super::{decompose, lucas_test, AStarBase, BruteForceBase, LucasBase, LucasCheck, SelfridgeBase};
517 use crate::hazmat::{primes, pseudoprimes, Primality};
518
519 #[test]
520 fn bases_derived_traits() {
521 assert_eq!(format!("{SelfridgeBase:?}"), "SelfridgeBase");
522 assert_eq!(SelfridgeBase.clone(), SelfridgeBase);
523 assert_eq!(format!("{AStarBase:?}"), "AStarBase");
524 assert_eq!(AStarBase.clone(), AStarBase);
525 assert_eq!(format!("{BruteForceBase:?}"), "BruteForceBase");
526 assert_eq!(BruteForceBase.clone(), BruteForceBase);
527
528 assert_eq!(format!("{:?}", LucasCheck::Strong), "Strong");
529 assert_eq!(LucasCheck::Strong.clone(), LucasCheck::Strong);
530 }
531
532 #[test]
533 fn base_for_square() {
534 let num = Odd::new(U64::from(131u32).square()).unwrap();
537 assert_eq!(SelfridgeBase.generate(&num), Err(Primality::Composite));
538 assert_eq!(AStarBase.generate(&num), Err(Primality::Composite));
539 assert_eq!(BruteForceBase.generate(&num), Err(Primality::Composite));
540 }
541
542 #[test]
543 fn base_early_quit() {
544 assert_eq!(
546 BruteForceBase.generate(&Odd::new(U64::from(5u32)).unwrap()),
547 Err(Primality::Prime)
548 )
549 }
550
551 #[test]
552 fn gcd_check() {
553 struct TestBase;
560
561 impl LucasBase for TestBase {
562 fn generate<T: Integer>(&self, _n: &Odd<T>) -> Result<(Word, Word, bool), Primality> {
563 Ok((5, 5, false))
564 }
565 }
566
567 assert_eq!(
568 lucas_test(Odd::new(U64::from(15u32)).unwrap(), TestBase, LucasCheck::Strong),
569 Primality::Composite
570 );
571 }
572
573 #[test]
574 fn decomposition() {
575 assert_eq!(
576 decompose(&Odd::new(U128::MAX).unwrap()),
577 (128, Odd::new(U128::ONE).unwrap())
578 );
579 assert_eq!(
580 decompose(&Odd::new(U128::ONE).unwrap()),
581 (1, Odd::new(U128::ONE).unwrap())
582 );
583 assert_eq!(
584 decompose(&Odd::new(U128::from(7766015u32)).unwrap()),
585 (15, Odd::new(U128::from(237u32)).unwrap())
586 );
587 }
588
589 fn is_slpsp(num: u32) -> bool {
590 pseudoprimes::STRONG_LUCAS.iter().any(|x| *x == num)
591 }
592
593 fn is_aeslpsp(num: u32) -> bool {
594 pseudoprimes::ALMOST_EXTRA_STRONG_LUCAS.iter().any(|x| *x == num)
595 }
596
597 fn is_eslpsp(num: u32) -> bool {
598 pseudoprimes::EXTRA_STRONG_LUCAS.iter().any(|x| *x == num)
599 }
600
601 fn is_vpsp(num: u32) -> bool {
602 pseudoprimes::LUCAS_V.iter().any(|x| *x == num)
603 }
604
605 fn test_composites_selfridge(numbers: &[u32], expected_result: bool) {
606 for num in numbers.iter() {
607 let false_positive = is_slpsp(*num);
608 let actual_expected_result = if false_positive { true } else { expected_result };
609
610 assert_eq!(
612 lucas_test(
613 Odd::new(Uint::<1>::from(*num)).unwrap(),
614 SelfridgeBase,
615 LucasCheck::Strong
616 )
617 .is_probably_prime(),
618 actual_expected_result
619 );
620 assert_eq!(
621 lucas_test(
622 Odd::new(Uint::<2>::from(*num)).unwrap(),
623 SelfridgeBase,
624 LucasCheck::Strong
625 )
626 .is_probably_prime(),
627 actual_expected_result
628 );
629 }
630 }
631
632 fn test_composites_a_star(numbers: &[u32], expected_result: bool) {
633 for num in numbers.iter() {
634 let false_positive = is_vpsp(*num);
635 let actual_expected_result = if false_positive { true } else { expected_result };
636
637 assert_eq!(
639 lucas_test(Odd::new(Uint::<1>::from(*num)).unwrap(), AStarBase, LucasCheck::LucasV).is_probably_prime(),
640 actual_expected_result
641 );
642 assert_eq!(
643 lucas_test(Odd::new(Uint::<2>::from(*num)).unwrap(), AStarBase, LucasCheck::LucasV).is_probably_prime(),
644 actual_expected_result
645 );
646 }
647 }
648
649 fn test_composites_brute_force(numbers: &[u32], almost_extra: bool, expected_result: bool) {
650 for num in numbers.iter() {
651 let false_positive = if almost_extra {
652 is_aeslpsp(*num)
653 } else {
654 is_eslpsp(*num)
655 };
656 let actual_expected_result = if false_positive { true } else { expected_result };
657 let check = if almost_extra {
658 LucasCheck::AlmostExtraStrong
659 } else {
660 LucasCheck::ExtraStrong
661 };
662
663 assert_eq!(
665 lucas_test(Odd::new(Uint::<1>::from(*num)).unwrap(), BruteForceBase, check).is_probably_prime(),
666 actual_expected_result,
667 "Brute force base, n = {num}, almost_extra = {almost_extra}",
668 );
669 assert_eq!(
670 lucas_test(Odd::new(Uint::<2>::from(*num)).unwrap(), BruteForceBase, check).is_probably_prime(),
671 actual_expected_result
672 );
673 }
674 }
675
676 #[test]
677 fn strong_fibonacci_pseudoprimes() {
678 for num in pseudoprimes::STRONG_FIBONACCI.iter() {
682 assert!(!lucas_test(Odd::new(*num).unwrap(), SelfridgeBase, LucasCheck::Strong).is_probably_prime());
683 assert!(!lucas_test(Odd::new(*num).unwrap(), BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime());
684 }
685 }
686
687 #[test]
688 fn fibonacci_pseudoprimes() {
689 test_composites_selfridge(pseudoprimes::FIBONACCI, false);
690 test_composites_a_star(pseudoprimes::FIBONACCI, false);
691 test_composites_brute_force(pseudoprimes::FIBONACCI, false, false);
692 test_composites_brute_force(pseudoprimes::FIBONACCI, true, false);
693 }
694
695 #[test]
696 fn bruckman_lucas_pseudoprimes() {
697 test_composites_selfridge(pseudoprimes::BRUCKMAN_LUCAS, false);
698 test_composites_a_star(pseudoprimes::BRUCKMAN_LUCAS, false);
699 test_composites_brute_force(pseudoprimes::BRUCKMAN_LUCAS, false, false);
700 test_composites_brute_force(pseudoprimes::BRUCKMAN_LUCAS, true, false);
701 }
702
703 #[test]
704 fn almost_extra_strong_lucas_pseudoprimes() {
705 test_composites_selfridge(pseudoprimes::ALMOST_EXTRA_STRONG_LUCAS, false);
706 test_composites_a_star(pseudoprimes::ALMOST_EXTRA_STRONG_LUCAS, false);
707
708 test_composites_brute_force(pseudoprimes::ALMOST_EXTRA_STRONG_LUCAS, false, false);
710 test_composites_brute_force(pseudoprimes::ALMOST_EXTRA_STRONG_LUCAS, true, true);
711 }
712
713 #[test]
714 fn extra_strong_lucas_pseudoprimes() {
715 test_composites_selfridge(pseudoprimes::EXTRA_STRONG_LUCAS, false);
716 test_composites_a_star(pseudoprimes::EXTRA_STRONG_LUCAS, false);
717
718 test_composites_brute_force(pseudoprimes::EXTRA_STRONG_LUCAS, false, true);
721 }
722
723 #[test]
724 fn lucas_pseudoprimes() {
725 test_composites_selfridge(pseudoprimes::LUCAS, false);
726 test_composites_a_star(pseudoprimes::LUCAS, false);
727 test_composites_brute_force(pseudoprimes::LUCAS, false, false);
728 test_composites_brute_force(pseudoprimes::LUCAS, true, false);
729 }
730
731 #[test]
732 fn strong_lucas_pseudoprimes() {
733 test_composites_selfridge(pseudoprimes::STRONG_LUCAS, true);
736
737 test_composites_a_star(pseudoprimes::STRONG_LUCAS, false);
738 test_composites_brute_force(pseudoprimes::STRONG_LUCAS, false, false);
739 test_composites_brute_force(pseudoprimes::STRONG_LUCAS, true, false);
740 }
741
742 #[test]
743 fn strong_pseudoprimes_base_2() {
744 test_composites_selfridge(pseudoprimes::STRONG_BASE_2, false);
747 test_composites_a_star(pseudoprimes::STRONG_BASE_2, false);
748 test_composites_brute_force(pseudoprimes::STRONG_BASE_2, false, false);
749 test_composites_brute_force(pseudoprimes::STRONG_BASE_2, true, false);
750 }
751
752 #[test]
753 fn large_carmichael_number() {
754 let p = Odd::new(pseudoprimes::LARGE_CARMICHAEL_NUMBER).unwrap();
755 assert!(!lucas_test(p, SelfridgeBase, LucasCheck::Strong).is_probably_prime());
756 assert!(!lucas_test(p, AStarBase, LucasCheck::LucasV).is_probably_prime());
757 assert!(!lucas_test(p, BruteForceBase, LucasCheck::AlmostExtraStrong).is_probably_prime());
758 assert!(!lucas_test(p, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime());
759 }
760
761 fn test_large_primes<const L: usize>(nums: &[Uint<L>]) {
762 for num in nums {
763 let num = Odd::new(*num).unwrap();
764 assert!(lucas_test(num, SelfridgeBase, LucasCheck::Strong).is_probably_prime());
765 assert!(lucas_test(num, AStarBase, LucasCheck::LucasV).is_probably_prime());
766 assert!(lucas_test(num, BruteForceBase, LucasCheck::AlmostExtraStrong).is_probably_prime());
767 assert!(lucas_test(num, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime());
768 }
769 }
770
771 #[test]
772 fn large_primes() {
773 test_large_primes(primes::PRIMES_128);
774 test_large_primes(primes::PRIMES_256);
775 test_large_primes(primes::PRIMES_384);
776 test_large_primes(primes::PRIMES_512);
777 test_large_primes(primes::PRIMES_1024);
778 }
779
780 #[test]
781 fn test_lucas_v_pseudoprimes() {
782 for num in pseudoprimes::LARGE_LUCAS_V {
783 let num = Odd::new(*num).unwrap();
784 assert!(lucas_test(num, AStarBase, LucasCheck::LucasV).is_probably_prime());
786
787 assert!(!lucas_test(num, SelfridgeBase, LucasCheck::Strong).is_probably_prime());
789 assert!(!lucas_test(num, BruteForceBase, LucasCheck::AlmostExtraStrong).is_probably_prime());
790 assert!(!lucas_test(num, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime());
791 }
792 }
793
794 #[test]
795 fn corner_cases() {
796 let res = lucas_test(
798 Odd::new(U64::ONE).unwrap(),
799 BruteForceBase,
800 LucasCheck::AlmostExtraStrong,
801 );
802 assert_eq!(res, Primality::Composite);
803 }
804
805 #[cfg(feature = "tests-exhaustive")]
806 #[test]
807 fn exhaustive() {
808 for num in (3..pseudoprimes::EXHAUSTIVE_TEST_LIMIT).step_by(2) {
811 let res_ref = is_prime64(num.into());
812
813 let eslpsp = is_eslpsp(num);
814 let aeslpsp = is_aeslpsp(num);
815 let slpsp = is_slpsp(num);
816 let vpsp = is_vpsp(num);
817
818 let odd_num = Odd::new(Uint::<1>::from(num)).unwrap();
819
820 let res = lucas_test(odd_num, BruteForceBase, LucasCheck::AlmostExtraStrong).is_probably_prime();
821 let expected = aeslpsp || res_ref;
822 assert_eq!(
823 res, expected,
824 "Brute force base, almost extra strong: n={num}, expected={expected}, actual={res}",
825 );
826
827 let res = lucas_test(odd_num, BruteForceBase, LucasCheck::ExtraStrong).is_probably_prime();
828 let expected = eslpsp || res_ref;
829 assert_eq!(
830 res, expected,
831 "Brute force base: n={num}, expected={expected}, actual={res}",
832 );
833
834 let res = lucas_test(odd_num, SelfridgeBase, LucasCheck::Strong).is_probably_prime();
835 let expected = slpsp || res_ref;
836 assert_eq!(
837 res, expected,
838 "Selfridge base: n={num}, expected={expected}, actual={res}",
839 );
840
841 let res = lucas_test(odd_num, AStarBase, LucasCheck::LucasV).is_probably_prime();
842 let expected = vpsp || res_ref;
843
844 assert_eq!(
845 res, expected,
846 "A* base, Lucas-V: n={num}, expected={expected}, actual={res}",
847 );
848 }
849 }
850}