Path: blob/main/Modules/_decimal/libmpdec/literature/mulmod-64.txt
12 views
12(* Copyright (c) 2011-2020 Stefan Krah. All rights reserved. *)345==========================================================================6Calculate (a * b) % p using special primes7==========================================================================89A description of the algorithm can be found in the apfloat manual by10Tommila [1].111213Definitions:14------------1516In the whole document, "==" stands for "is congruent with".1718Result of a * b in terms of high/low words:1920(1) hi * 2**64 + lo = a * b2122Special primes:2324(2) p = 2**64 - z + 1, where z = 2**n2526Single step modular reduction:2728(3) R(hi, lo) = hi * z - hi + lo293031Strategy:32---------3334a) Set (hi, lo) to the result of a * b.3536b) Set (hi', lo') to the result of R(hi, lo).3738c) Repeat step b) until 0 <= hi' * 2**64 + lo' < 2*p.3940d) If the result is less than p, return lo'. Otherwise return lo' - p.414243The reduction step b) preserves congruence:44-------------------------------------------4546hi * 2**64 + lo == hi * z - hi + lo (mod p)4748Proof:49~~~~~~5051hi * 2**64 + lo = (2**64 - z + 1) * hi + z * hi - hi + lo5253= p * hi + z * hi - hi + lo5455== z * hi - hi + lo (mod p)565758Maximum numbers of step b):59---------------------------6061# To avoid unnecessary formalism, define:6263def R(hi, lo, z):64return divmod(hi * z - hi + lo, 2**64)6566# For simplicity, assume hi=2**64-1, lo=2**64-1 after the67# initial multiplication a * b. This is of course impossible68# but certainly covers all cases.6970# Then, for p1:71hi=2**64-1; lo=2**64-1; z=2**3272p1 = 2**64 - z + 17374hi, lo = R(hi, lo, z) # First reduction75hi, lo = R(hi, lo, z) # Second reduction76hi * 2**64 + lo < 2 * p1 # True7778# For p2:79hi=2**64-1; lo=2**64-1; z=2**3480p2 = 2**64 - z + 18182hi, lo = R(hi, lo, z) # First reduction83hi, lo = R(hi, lo, z) # Second reduction84hi, lo = R(hi, lo, z) # Third reduction85hi * 2**64 + lo < 2 * p2 # True8687# For p3:88hi=2**64-1; lo=2**64-1; z=2**4089p3 = 2**64 - z + 19091hi, lo = R(hi, lo, z) # First reduction92hi, lo = R(hi, lo, z) # Second reduction93hi, lo = R(hi, lo, z) # Third reduction94hi * 2**64 + lo < 2 * p3 # True959697Step d) preserves congruence and yields a result < p:98-----------------------------------------------------99100Case hi = 0:101102Case lo < p: trivial.103104Case lo >= p:105106lo == lo - p (mod p) # result is congruent107108p <= lo < 2*p -> 0 <= lo - p < p # result is in the correct range109110Case hi = 1:111112p < 2**64 /\ 2**64 + lo < 2*p -> lo < p # lo is always less than p1131142**64 + lo == 2**64 + (lo - p) (mod p) # result is congruent115116= lo - p # exactly the same value as the previous RHS117# in uint64_t arithmetic.118119p < 2**64 + lo < 2*p -> 0 < 2**64 + (lo - p) < p # correct range120121122123[1] http://www.apfloat.org/apfloat/2.40/apfloat.pdf124125126127128129