44#pragma once
55
66#include < intx/intx.hpp>
7+ #include < cassert>
78
89namespace evmmax
910{
10-
11- // / Compute the modulus inverse for Montgomery multiplication, i.e. N': mod⋅N' = 2⁶⁴-1.
12- // /
13- // / @param mod0 The least significant word of the modulus.
14- constexpr uint64_t compute_mod_inv (uint64_t mod0) noexcept
11+ // / Compute the modular inverse of the number modulo 2⁶⁴: inv⋅a = 1 mod 2⁶⁴.
12+ constexpr uint64_t inv_mod (uint64_t a) noexcept
1513{
16- // TODO: Find what is this algorithm and why it works.
17- uint64_t base = 0 - mod0;
18- uint64_t result = 1 ;
19- for (auto i = 0 ; i < 64 ; ++i)
20- {
21- result *= base;
22- base *= base;
23- }
24- return result;
14+ assert (a % 2 == 1 ); // The argument must be odd, otherwise the inverse does not exist.
15+
16+ // Use the Newton–Raphson numeric method, see e.g.
17+ // https://gmplib.org/~tege/divcnst-pldi94.pdf#page=9, formula (9.2)
18+ // Each iteration doubles the number of correct bits:
19+ // 2, 4, 8, ..., so for 64-bit value we need 6 iterations.
20+ // TODO(C++23): static
21+ constexpr auto ITERATIONS = std::countr_zero (sizeof (a) * 8 );
22+
23+ // Start with inversion mod 2.
24+ // TODO: This can be further accelerated by:
25+ // - computing the inversion with smaller type (e.g. uint32_t) first,
26+ // - using a better initial approximation (e.g. via lookup table for 4 bits).
27+ uint64_t inv = 1 ;
28+ for (auto i = 0 ; i < ITERATIONS ; ++i)
29+ inv *= 2 - a * inv; // Overflows are fine because they wrap around modulo 2⁶⁴.
30+ return inv;
2531}
2632
2733// / The modular arithmetic operations for EVMMAX (EVM Modular Arithmetic Extensions).
@@ -44,6 +50,14 @@ class ModArith
4450 return intx::udivrem (RR , mod).rem ;
4551 }
4652
53+ // / Compute the modulus inverse for Montgomery multiplication, i.e., N': mod⋅N' = 2⁶⁴-1.
54+ static constexpr uint64_t compute_mont_mod_inv (const UintT& mod) noexcept
55+ {
56+ // Compute the inversion mod[0]⁻¹ mod 2⁶⁴, then the final result is N' = -mod[0]⁻¹
57+ // because this gives mod⋅N' = -1 mod 2⁶⁴ = 2⁶⁴-1.
58+ return -inv_mod (mod[0 ]);
59+ }
60+
4761 static constexpr std::pair<uint64_t , uint64_t > addmul (
4862 uint64_t t, uint64_t a, uint64_t b, uint64_t c) noexcept
4963 {
@@ -53,7 +67,7 @@ class ModArith
5367
5468public:
5569 constexpr explicit ModArith (const UintT& mod) noexcept
56- : mod_{mod}, r_squared_{compute_r_squared (mod)}, mod_inv_{compute_mod_inv (mod[ 0 ] )}
70+ : mod_{mod}, r_squared_{compute_r_squared (mod)}, mod_inv_{compute_mont_mod_inv (mod)}
5771 {}
5872
5973 // / Returns the modulus.
0 commit comments