From 36a29489595210c1fbba91c4110f2ab859168bc3 Mon Sep 17 00:00:00 2001 From: nicoo Date: Sun, 24 May 2020 19:10:34 +0200 Subject: [PATCH] factor::miller_rabin: Avoid unecessary exponentiation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of computing a^r and a^(n-1) = a^(r 2ⁱ) separately, compute the latter by repeatedly squaring the former. 33.6% performance improvement --- src/uu/factor/src/miller_rabin.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/uu/factor/src/miller_rabin.rs b/src/uu/factor/src/miller_rabin.rs index cdc1722fd..f8ad493dd 100644 --- a/src/uu/factor/src/miller_rabin.rs +++ b/src/uu/factor/src/miller_rabin.rs @@ -20,6 +20,7 @@ impl Result { // Deterministic Miller-Rabin primality-checking algorithm, adapted to extract // (some) dividers; it will fail to factor strong pseudoprimes. +#[allow(clippy::many_single_char_names)] pub(crate) fn test(n: u64) -> Result { use self::Result::*; @@ -30,18 +31,30 @@ pub(crate) fn test(n: u64) -> Result { return if n == 2 { Prime } else { Composite(2) }; } - let r = (n - 1) >> (n - 1).trailing_zeros(); + // n-1 = r 2ⁱ + let i = (n - 1).trailing_zeros(); + let r = (n - 1) >> i; for a in BASIS.iter() { - let mut x = a % n; - if x == 0 { + let a = a % n; + if a == 0 { break; } - if A::pow(x, n - 1, n) != 1 { - return Pseudoprime; + // x = a^r mod n + let mut x = A::pow(a, r, n); + + { + // y = ((x²)²...)² i times = x ^ (2ⁱ) = a ^ (r 2ⁱ) = x ^ (n - 1) + let mut y = x; + for _ in 0..i { + y = A::mul(y, y, n) + } + if y != 1 { + return Pseudoprime; + }; } - x = A::pow(x, r, n); + if x == 1 || x == n - 1 { break; }