Add the Strong Lucas Probable Prime test

This is more involved than the Miller-Rabin test, but we can tame the
complexity by breaking it down into helper functions, performing tasks
such as:

- Increment the index of the (U_k, V_k) sequence elements by one.
- Double the index of the (U_k, V_k) sequence elements.
- Find an appropriate D parameter.

etc.

With these helpers, the algorithm becomes straightforward (see, for
instance,
https://en.wikipedia.org/wiki/Lucas_pseudoprime#Strong_Lucas_pseudoprimes).
We start by ruling out perfect squares (because if we don't, then the
search for `D` will never terminate).  Then we find our `D`, and
decompose `n + 1` into `s` and `d` parameters (exactly as we did for
Miller-Rabin, except there we used `n - 1`).  At this point, the strong
test is easy: check whether `U_d` is 0, then check `V_d`, as well as `V`
for all successive doublings of the index less than `s`.

A similar testing strategy as for the Miller Rabin gives us sufficient
confidence.

1. Test that we get small primes right.
2. Test that we get known pseudoprimes "correctly wrong".
3. Test some really big primes.

(Remember, a probable prime test must mark every actual prime as
"probably prime".)

Helps #509.
This commit is contained in:
Chip Hogg committed 2024-11-15 10:53:26 -05:00
1 parent c901b73ee5
commit 9e8dfec265
2 files changed
+150

No files matched your search

+32
View File
@@ -165,4 +165,36 @@ static_assert(!is_perfect_square(BIG_SQUARE - 1u));
static_assert(is_perfect_square(BIG_SQUARE));
static_assert(!is_perfect_square(BIG_SQUARE + 1u));
// Tests for the Strong Lucas Probable Prime test.
static_assert(as_int(LucasDParameter{.mag = 5, .pos = true}) == 5);
static_assert(as_int(LucasDParameter{.mag = 7, .pos = false}) == -7);
static_assert(as_int(LucasDParameter{}) == 5, "First D parameter in the sequence is 5");
static_assert(as_int(successor(LucasDParameter{})) == -7, "Incrementing adds 2 to the mag, and flips the sign");
static_assert(as_int(successor(successor(LucasDParameter{}))) == 9);
static_assert(as_int(successor(successor(successor(LucasDParameter{})))) == -11);
static_assert(strong_lucas_probable_prime(3u), "Known small prime");
static_assert(strong_lucas_probable_prime(5u), "Known small prime");
static_assert(strong_lucas_probable_prime(7u), "Known small prime");
static_assert(!strong_lucas_probable_prime(9u), "Known small composite");
// Test some Miller-Rabin pseudoprimes (https://oeis.org/A001262), which should NOT be marked prime.
static_assert(!strong_lucas_probable_prime(2047u), "Miller-Rabin pseudoprime");
static_assert(!strong_lucas_probable_prime(3277u), "Miller-Rabin pseudoprime");
static_assert(!strong_lucas_probable_prime(486737u), "Miller-Rabin pseudoprime");
// Test some Strong Lucas pseudoprimes (https://oeis.org/A217255).
static_assert(strong_lucas_probable_prime(5459u), "Strong Lucas pseudoprime");
static_assert(strong_lucas_probable_prime(5777u), "Strong Lucas pseudoprime");
static_assert(strong_lucas_probable_prime(10877u), "Strong Lucas pseudoprime");
static_assert(strong_lucas_probable_prime(324899u), "Strong Lucas pseudoprime");
// Test some actual primes
static_assert(strong_lucas_probable_prime(225'653'407'801u), "Large known prime");
static_assert(strong_lucas_probable_prime(334'524'384'739u), "Large known prime");
static_assert(strong_lucas_probable_prime(9'007'199'254'740'881u), "Large known prime");
static_assert(strong_lucas_probable_prime(18'446'744'073'709'551'557u), "Largest 64-bit prime");
} // namespace