@@ -21,34 +21,112 @@ class ModArith
2121 // / The modulus inversion, i.e. the number N' such that mod⋅N' = 2⁶⁴-1.
2222 const uint64_t m_mod_inv;
2323
24+ // / Compute the modulus inverse for Montgomery multiplication, i.e. N': mod⋅N' = 2⁶⁴-1.
25+ // /
26+ // / @param mod0 The least significant word of the modulus.
27+ static constexpr uint64_t compute_mod_inv (uint64_t mod0) noexcept
28+ {
29+ // TODO: Find what is this algorithm and why it works.
30+ uint64_t base = 0 - mod0;
31+ uint64_t result = 1 ;
32+ for (auto i = 0 ; i < 64 ; ++i)
33+ {
34+ result *= base;
35+ base *= base;
36+ }
37+ return result;
38+ }
39+
40+ // / Compute R² % mod.
41+ static constexpr UintT compute_r_squared (const UintT& mod) noexcept
42+ {
43+ // R is 2^num_bits, R² is 2^(2*num_bits) and needs 2*num_bits+1 bits to represent,
44+ // rounded to 2*num_bits+64) for intx requirements.
45+ constexpr auto r2 = intx::uint<UintT::num_bits * 2 + 64 >{1 } << (UintT::num_bits * 2 );
46+ return intx::udivrem (r2, mod).rem ;
47+ }
48+
49+ static constexpr std::pair<uint64_t , uint64_t > addmul (
50+ uint64_t t, uint64_t a, uint64_t b, uint64_t c) noexcept
51+ {
52+ const auto p = intx::umul (a, b) + t + c;
53+ return {p[1 ], p[0 ]};
54+ }
55+
2456public:
25- explicit ModArith (const UintT& modulus) noexcept ;
57+ constexpr explicit ModArith (const UintT& modulus) noexcept
58+ : mod{modulus},
59+ m_r_squared{compute_r_squared (modulus)},
60+ m_mod_inv{compute_mod_inv (modulus[0 ])}
61+ {}
2662
2763 // / Converts a value to Montgomery form.
2864 // /
2965 // / This is done by using Montgomery multiplication mul(x, R²)
3066 // / what gives aR²R⁻¹ % mod = aR % mod.
31- UintT to_mont (const UintT& x) const noexcept ;
67+ constexpr UintT to_mont (const UintT& x) const noexcept { return mul (x, m_r_squared); }
3268
3369 // / Converts a value in Montgomery form back to normal value.
3470 // /
3571 // / Given the x is the Montgomery form x = aR, the conversion is done by using
3672 // / Montgomery multiplication mul(x, 1) what gives aRR⁻¹ % mod = a % mod.
37- UintT from_mont (const UintT& x) const noexcept ;
73+ constexpr UintT from_mont (const UintT& x) const noexcept { return mul (x, 1 ); }
3874
3975 // / Performs a Montgomery modular multiplication.
4076 // /
4177 // / Inputs must be in Montgomery form: x = aR, y = bR.
4278 // / This computes Montgomery multiplication xyR⁻¹ % mod what gives aRbRR⁻¹ % mod = abR % mod.
4379 // / The result (abR) is in Montgomery form.
44- UintT mul (const UintT& x, const UintT& y) const noexcept ;
80+ constexpr UintT mul (const UintT& x, const UintT& y) const noexcept
81+ {
82+ // Coarsely Integrated Operand Scanning (CIOS) Method
83+ // Based on 2.3.2 from
84+ // High-Speed Algorithms & Architectures For Number-Theoretic Cryptosystems
85+ // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf
86+
87+ constexpr auto S = UintT::num_words; // TODO(C++23): Make it static
88+
89+ intx::uint<UintT::num_bits + 64 > t;
90+ for (size_t i = 0 ; i != S; ++i)
91+ {
92+ uint64_t c = 0 ;
93+ for (size_t j = 0 ; j != S; ++j)
94+ std::tie (c, t[j]) = addmul (t[j], x[j], y[i], c);
95+ auto tmp = intx::addc (t[S], c);
96+ t[S] = tmp.value ;
97+ const auto d = tmp.carry ; // TODO: Carry is 0 for sparse modulus.
98+
99+ const auto m = t[0 ] * m_mod_inv;
100+ std::tie (c, std::ignore) = addmul (t[0 ], m, mod[0 ], 0 );
101+ for (size_t j = 1 ; j != S; ++j)
102+ std::tie (c, t[j - 1 ]) = addmul (t[j], m, mod[j], c);
103+ tmp = intx::addc (t[S], c);
104+ t[S - 1 ] = tmp.value ;
105+ t[S] = d + tmp.carry ; // TODO: Carry is 0 for sparse modulus.
106+ }
107+
108+ if (t >= mod)
109+ t -= mod;
110+
111+ return static_cast <UintT>(t);
112+ }
45113
46114 // / Performs a modular addition. It is required that x < mod and y < mod, but x and y may be
47115 // / but are not required to be in Montgomery form.
48- UintT add (const UintT& x, const UintT& y) const noexcept ;
116+ constexpr UintT add (const UintT& x, const UintT& y) const noexcept
117+ {
118+ const auto s = addc (x, y); // TODO: cannot overflow if modulus is sparse (e.g. 255 bits).
119+ const auto d = subc (s.value , mod);
120+ return (!s.carry && d.carry ) ? s.value : d.value ;
121+ }
49122
50123 // / Performs a modular subtraction. It is required that x < mod and y < mod, but x and y may be
51124 // / but are not required to be in Montgomery form.
52- UintT sub (const UintT& x, const UintT& y) const noexcept ;
125+ constexpr UintT sub (const UintT& x, const UintT& y) const noexcept
126+ {
127+ const auto d = subc (x, y);
128+ const auto s = d.value + mod;
129+ return (d.carry ) ? s : d.value ;
130+ }
53131};
54132} // namespace evmmax
0 commit comments