@@ -491,6 +491,11 @@ math_integer_isqrt(PyObject *module, PyObject *n)
491491 verified against the Feitsma-Galway exhaustive list of base-2 strong
492492 pseudoprimes below 2**64 (see http://miller-rabin.appspot.com/ and
493493 http://ntheory.org/pseudoprimes.html).
494+
495+ Modular exponentiation and squaring use Montgomery arithmetic with
496+ R = 2**64 to avoid division in their inner loops (P. L. Montgomery,
497+ "Modular multiplication without trial division", Mathematics of
498+ Computation 44 (1985), 519-521).
494499*/
495500
496501static const uint8_t small_primes [] = {
@@ -540,44 +545,96 @@ mulmod_u64(uint64_t a, uint64_t b, uint64_t n)
540545}
541546#endif
542547
543- static uint64_t
544- powmod_u64 (uint64_t b , uint64_t e , uint64_t n )
548+ /* High 64 bits of the full product a * b. */
549+ static inline uint64_t
550+ mulhi_u64 (uint64_t a , uint64_t b )
545551{
546- uint64_t result = 1 ;
547- b %= n ;
548- while (e ) {
549- if (e & 1 ) {
550- result = mulmod_u64 (result , b , n );
551- }
552- b = mulmod_u64 (b , b , n );
553- e >>= 1 ;
554- }
555- return result ;
552+ #if defined(HAVE_GCC_UINT128_T )
553+ return (uint64_t )(((__uint128_t )a * b ) >> 64 );
554+ #elif defined(_MSC_VER ) && defined(_M_X64 )
555+ uint64_t hi ;
556+ (void )_umul128 (a , b , & hi );
557+ return hi ;
558+ #else
559+ /* Split the operands into 32-bit limbs. Each sum fits in uint64_t. */
560+ uint64_t a0 = (uint32_t )a , a1 = a >> 32 ;
561+ uint64_t b0 = (uint32_t )b , b1 = b >> 32 ;
562+ uint64_t t = a1 * b0 + ((a0 * b0 ) >> 32 );
563+ uint64_t carry = t >> 32 ;
564+ t = a0 * b1 + (uint32_t )t ;
565+ return a1 * b1 + carry + (t >> 32 );
566+ #endif
556567}
557568
558- /* Strong probable prime test to the given base. n must be odd and
559- coprime to the base (a base divisible by n gives no information and
560- is treated as passing). */
569+ typedef struct {
570+ uint64_t n ;
571+ uint64_t inverse ; // n**-1 modulo 2**64
572+ uint64_t one ; // 2**64 modulo n
573+ uint64_t d ; // Odd part of n - 1
574+ int s ; // n - 1 = d * 2**s
575+ } montgomery_context ;
576+
577+ /* Precompute once for all bases. n must be odd and greater than 1. */
578+ static montgomery_context
579+ montgomery_init (uint64_t n )
580+ {
581+ montgomery_context ctx = {
582+ .n = n ,
583+ .inverse = 1 ,
584+ .one = (0 - n ) % n ,
585+ .d = n - 1 ,
586+ .s = 0 ,
587+ };
588+ /* Newton iteration doubles the number of correct inverse bits,
589+ starting with one correct bit because n is odd. */
590+ for (int i = 0 ; i < 6 ; i ++ ) {
591+ ctx .inverse *= 2 - n * ctx .inverse ;
592+ }
593+ while ((ctx .d & 1 ) == 0 ) {
594+ ctx .d >>= 1 ;
595+ ctx .s ++ ;
596+ }
597+ return ctx ;
598+ }
599+
600+ /* Return a * b * R**-1 modulo n, where R = 2**64 and a, b < n.
601+ With t = a*b and m = (t mod R) * n**-1 mod R, t - m*n is divisible
602+ by R, and (t - m*n) / R lies strictly between -n and n. Subtracting
603+ avoids the possible 129-bit carry of the additive Montgomery form.
604+ All uint64_t arithmetic intentionally wraps modulo R. */
605+ static inline uint64_t
606+ montgomery_mul (uint64_t a , uint64_t b , const montgomery_context * ctx )
607+ {
608+ uint64_t lo = a * b , hi = mulhi_u64 (a , b );
609+ uint64_t mhi = mulhi_u64 (lo * ctx -> inverse , ctx -> n );
610+ uint64_t result = hi - mhi ;
611+ return hi < mhi ? result + ctx -> n : result ;
612+ }
613+
614+ /* Strong probable prime test to the given base, in Montgomery form.
615+ A base divisible by n gives no information and is treated as passing. */
561616static int
562- sprp_u64 (uint64_t n , uint64_t base )
617+ sprp_u64 (const montgomery_context * ctx , uint64_t base )
563618{
564- base %= n ;
619+ base %= ctx -> n ;
565620 if (base == 0 ) {
566621 return 1 ;
567622 }
568- uint64_t d = n - 1 ;
569- int s = 0 ;
570- while ((d & 1 ) == 0 ) {
571- d >>= 1 ;
572- s ++ ;
623+ // Represent x by x*R modulo n throughout exponentiation.
624+ uint64_t b = mulmod_u64 (base , ctx -> one , ctx -> n );
625+ uint64_t a = ctx -> one ;
626+ for (uint64_t e = ctx -> d ; e ; e >>= 1 ) {
627+ if (e & 1 ) {
628+ a = montgomery_mul (a , b , ctx );
629+ }
630+ b = montgomery_mul (b , b , ctx );
573631 }
574- uint64_t a = powmod_u64 (base , d , n );
575- if (a == 1 || a == n - 1 ) {
632+ if (a == ctx -> one || a == ctx -> n - ctx -> one ) {
576633 return 1 ;
577634 }
578- for (int r = 1 ; r < s ; r ++ ) {
579- a = mulmod_u64 (a , a , n );
580- if (a == n - 1 ) {
635+ for (int r = 1 ; r < ctx -> s ; r ++ ) {
636+ a = montgomery_mul (a , a , ctx );
637+ if (a == ctx -> n - ctx -> one ) {
581638 return 1 ;
582639 }
583640 }
@@ -595,14 +652,15 @@ isprime_u64(uint64_t n)
595652 if (n < 67 * 67 ) {
596653 return n > 1 ;
597654 }
655+ montgomery_context ctx = montgomery_init (n );
598656 const uint32_t * bases = mr_bases ;
599657 size_t num_bases = Py_ARRAY_LENGTH (mr_bases );
600658 if (n < 4759123141 ) {
601659 bases = mr_bases_small ;
602660 num_bases = Py_ARRAY_LENGTH (mr_bases_small );
603661 }
604662 for (size_t i = 0 ; i < num_bases ; i ++ ) {
605- if (!sprp_u64 (n , bases [i ])) {
663+ if (!sprp_u64 (& ctx , bases [i ])) {
606664 return 0 ;
607665 }
608666 }
0 commit comments