num_modular/lib.rs
1//! This crate provides efficient Modular arithmetic operations for various integer types,
2//! including primitive integers and `num-bigint`. The latter option is enabled optionally.
3//!
4//! To achieve fast modular arithmetics, convert integers to any [ModularInteger] implementation
5//! use static `new()` or associated [ModularInteger::convert()] functions. Some builtin implementations
6//! of [ModularInteger] includes [MontgomeryInt], [FixedMersenneInt] and [FixedSolinasInt].
7//!
8//! Example code:
9//! ```rust
10//! use num_modular::{ModularCoreOps, ModularInteger, MontgomeryInt};
11//!
12//! // directly using methods in ModularCoreOps
13//! let (x, y, m) = (12u8, 13u8, 5u8);
14//! assert_eq!(x.mulm(y, &m), x * y % m);
15//!
16//! // convert integers into ModularInteger
17//! let mx = MontgomeryInt::new(x, &m);
18//! let my = mx.convert(y); // faster than static MontgomeryInt::new(y, m)
19//! assert_eq!((mx * my).residue(), x * y % m);
20//! ```
21//!
22//! # Comparison of fast division / modular arithmetics
23//! Several fast division / modulo tricks are provided in these crate, the difference of them are listed below:
24//! - [PreModInv]: pre-compute modular inverse of the divisor, only applicable to exact division
25//! - Barrett (to be implemented): pre-compute (rational approximation of) the reciprocal of the divisor,
26//! applicable to fast division and modulo
27//! - [Montgomery]: Convert the dividend into a special form by shifting and pre-compute a modular inverse,
28//! only applicable to fast modulo, but faster than Barrett reduction
29//! - [FixedMersenne]: Specialization of modulo in form `2^P-K` under 2^127.
30//! - [FixedTrinomialSolinas]: Specialization of modulo in form `2^P1-2^P2+K` under 2^127.
31//!
32
33// XXX: Other fast modular arithmetic tricks
34// REF: https://github.com/lemire/fastmod & https://arxiv.org/pdf/1902.01961.pdf
35// REF: https://eprint.iacr.org/2014/040.pdf
36// REF: https://github.com/ridiculousfish/libdivide/
37// REF: Faster Interleaved Modular Multiplication Based on Barrett and Montgomery Reduction Methods (work for modulus in certain form)
38
39#![no_std]
40#[cfg(any(feature = "std", test))]
41extern crate std;
42
43use core::ops::{Add, Mul, Neg, Sub};
44
45/// Core modular arithmetic operations.
46///
47/// Note that all functions will panic if the modulus is zero.
48pub trait ModularCoreOps<Rhs = Self, Modulus = Self> {
49 type Output;
50
51 /// Return (self + rhs) % m
52 fn addm(self, rhs: Rhs, m: Modulus) -> Self::Output;
53
54 /// Return (self - rhs) % m
55 fn subm(self, rhs: Rhs, m: Modulus) -> Self::Output;
56
57 /// Return (self * rhs) % m
58 fn mulm(self, rhs: Rhs, m: Modulus) -> Self::Output;
59}
60
61/// Core unary modular arithmetics
62///
63/// Note that all functions will panic if the modulus is zero.
64pub trait ModularUnaryOps<Modulus = Self> {
65 type Output;
66
67 /// Return (-self) % m and make sure the result is normalized in range [0,m)
68 fn negm(self, m: Modulus) -> Self::Output;
69
70 /// Calculate modular inverse (x such that self*x = 1 mod m).
71 ///
72 /// This operation is only available for integer that is coprime to `m`. If not,
73 /// the result will be [None].
74 fn invm(self, m: Modulus) -> Option<Self::Output>;
75
76 /// Calculate modular double ( x+x mod m)
77 fn dblm(self, m: Modulus) -> Self::Output;
78
79 /// Calculate modular square ( x*x mod m )
80 fn sqm(self, m: Modulus) -> Self::Output;
81
82 // TODO: Modular sqrt aka Quadratic residue, follow the behavior of FLINT `n_sqrtmod`
83 // fn sqrtm(self, m: Modulus) -> Option<Self::Output>;
84 // REF: https://stackoverflow.com/questions/6752374/cube-root-modulo-p-how-do-i-do-this
85}
86
87/// Modular power functions
88pub trait ModularPow<Exp = Self, Modulus = Self> {
89 type Output;
90
91 /// Return (self ^ exp) % m
92 fn powm(self, exp: Exp, m: Modulus) -> Self::Output;
93}
94
95/// Math symbols related to modular arithmetics
96pub trait ModularSymbols<Modulus = Self> {
97 /// Calculate Legendre Symbol (a|n), where a is `self`.
98 ///
99 /// Note that this function doesn't perform a full primality check, since
100 /// is costly. So if n is not a prime, the result can be not reasonable.
101 ///
102 /// # Panics
103 /// Only if n is not prime
104 #[inline]
105 fn legendre(&self, n: Modulus) -> i8 {
106 self.checked_legendre(n).expect("n shoud be a prime")
107 }
108
109 /// Calculate Legendre Symbol (a|n), where a is `self`. Returns [None] only if n is
110 /// not a prime.
111 ///
112 /// Note that this function doesn't perform a full primality check, since
113 /// is costly. So if n is not a prime, the result can be not reasonable.
114 ///
115 /// # Panics
116 /// Only if n is not prime
117 fn checked_legendre(&self, n: Modulus) -> Option<i8>;
118
119 /// Calculate Jacobi Symbol (a|n), where a is `self`
120 ///
121 /// # Panics
122 /// if n is negative or even
123 #[inline]
124 fn jacobi(&self, n: Modulus) -> i8 {
125 self.checked_jacobi(n)
126 .expect("the Jacobi symbol is only defined for non-negative odd integers")
127 }
128
129 /// Calculate Jacobi Symbol (a|n), where a is `self`. Returns [None] if n is negative or even.
130 fn checked_jacobi(&self, n: Modulus) -> Option<i8>;
131
132 /// Calculate Kronecker Symbol (a|n), where a is `self`
133 fn kronecker(&self, n: Modulus) -> i8;
134}
135
136// TODO: Discrete log aka index, follow the behavior of FLINT `n_discrete_log_bsgs`
137// REF: https://github.com/vks/discrete-log
138// fn logm(self, base: Modulus, m: Modulus);
139
140/// Collection of common modular arithmetic operations
141pub trait ModularOps<Rhs = Self, Modulus = Self, Output = Self>:
142 ModularCoreOps<Rhs, Modulus, Output = Output>
143 + ModularUnaryOps<Modulus, Output = Output>
144 + ModularPow<Rhs, Modulus, Output = Output>
145 + ModularSymbols<Modulus>
146{
147}
148impl<T, Rhs, Modulus> ModularOps<Rhs, Modulus> for T where
149 T: ModularCoreOps<Rhs, Modulus, Output = T>
150 + ModularUnaryOps<Modulus, Output = T>
151 + ModularPow<Rhs, Modulus, Output = T>
152 + ModularSymbols<Modulus>
153{
154}
155
156/// Collection of operations similar to [ModularOps], but takes operands with references
157pub trait ModularRefOps: for<'r> ModularOps<&'r Self, &'r Self> + Sized {}
158impl<T> ModularRefOps for T where T: for<'r> ModularOps<&'r T, &'r T> {}
159
160/// Provides a utility function to convert signed integers into unsigned modular form
161pub trait ModularAbs<Modulus> {
162 /// Return self % m, but accepting signed integers
163 fn absm(self, m: &Modulus) -> Modulus;
164}
165
166/// Represents an number defined in a modulo ring ℤ/nℤ
167///
168/// The operators should panic if the modulus of two number
169/// are not the same.
170pub trait ModularInteger:
171 Sized
172 + PartialEq
173 + Add<Self, Output = Self>
174 + Sub<Self, Output = Self>
175 + Neg<Output = Self>
176 + Mul<Self, Output = Self>
177{
178 /// The underlying representation type of the integer
179 type Base;
180
181 /// Return the modulus of the ring
182 fn modulus(&self) -> Self::Base;
183
184 /// Return the normalized residue of this integer in the ring
185 fn residue(&self) -> Self::Base;
186
187 /// Check if the integer is zero
188 fn is_zero(&self) -> bool;
189
190 /// Convert an normal integer into the same ring.
191 ///
192 /// This method should be perferred over the static
193 /// constructor to prevent unnecessary overhead of pre-computation.
194 fn convert(&self, n: Self::Base) -> Self;
195
196 /// Calculate the value of self + self
197 fn double(self) -> Self;
198
199 /// Calculate the value of self * self
200 fn square(self) -> Self;
201}
202
203// XXX: implement ModularInteger for ff::PrimeField?
204// TODO: implement invm_range (Modular inverse in certain range) and crt (Chinese Remainder Theorem), REF: bubblemath crate
205
206/// Utility function for exact division, with precomputed helper values
207///
208/// # Available Pre-computation types:
209/// - `()`: No pre-computation, the implementation relies on native integer division
210/// - [PreModInv]: With Pre-computed modular inverse
211pub trait DivExact<Rhs, Precompute>: Sized {
212 type Output;
213
214 /// Check if d divides self with the help of the precomputation. If d divides self,
215 /// then the quotient is returned.
216 fn div_exact(self, d: Rhs, pre: &Precompute) -> Option<Self::Output>;
217}
218
219/// A modular reducer that can ensure that the operations on integers are all performed
220/// in a modular ring.
221///
222/// Essential information for performing the modulo operation will be stored in the reducer.
223pub trait Reducer<T> {
224 /// Create a reducer for a modulus m
225 fn new(m: &T) -> Self;
226
227 /// Transform a normal integer into reduced form
228 fn transform(&self, target: T) -> T;
229
230 /// Check whether target is a valid reduced form
231 fn check(&self, target: &T) -> bool;
232
233 /// Get the modulus in original integer type
234 fn modulus(&self) -> T;
235
236 /// Transform a reduced form back to normal integer
237 fn residue(&self, target: T) -> T;
238
239 /// Test if the residue() == 0
240 fn is_zero(&self, target: &T) -> bool;
241
242 /// Calculate (lhs + rhs) mod m in reduced form
243 fn add(&self, lhs: &T, rhs: &T) -> T;
244
245 #[inline]
246 fn add_in_place(&self, lhs: &mut T, rhs: &T) {
247 *lhs = self.add(lhs, rhs)
248 }
249
250 /// Calculate 2*target mod m
251 fn dbl(&self, target: T) -> T;
252
253 /// Calculate (lhs - rhs) mod m in reduced form
254 fn sub(&self, lhs: &T, rhs: &T) -> T;
255
256 #[inline]
257 fn sub_in_place(&self, lhs: &mut T, rhs: &T) {
258 *lhs = self.sub(lhs, rhs);
259 }
260
261 /// Calculate -monty mod m in reduced form
262 fn neg(&self, target: T) -> T;
263
264 /// Calculate (lhs * rhs) mod m in reduced form
265 fn mul(&self, lhs: &T, rhs: &T) -> T;
266
267 #[inline]
268 fn mul_in_place(&self, lhs: &mut T, rhs: &T) {
269 *lhs = self.mul(lhs, rhs);
270 }
271
272 /// Calculate target^-1 mod m in reduced form,
273 /// it may return None when there is no modular inverse.
274 fn inv(&self, target: T) -> Option<T>;
275
276 /// Calculate target^2 mod m in reduced form
277 fn sqr(&self, target: T) -> T;
278
279 /// Calculate base ^ exp mod m in reduced form
280 fn pow(&self, base: T, exp: &T) -> T;
281}
282
283mod barrett;
284mod double;
285mod mersenne;
286mod monty;
287mod preinv;
288mod prim;
289mod reduced;
290mod solinas;
291mod word;
292
293pub use barrett::{
294 Normalized2by1Divisor, Normalized3by2Divisor, PreMulInv1by1, PreMulInv2by1, PreMulInv3by2,
295};
296pub use double::{imax, udouble, umax};
297pub use mersenne::{FixedMersenne, FixedMersenne32, FixedMersenne64};
298pub use monty::Montgomery;
299pub use preinv::PreModInv;
300pub use reduced::{ReducedInt, Vanilla, VanillaInt};
301pub use solinas::{FixedTrinomialSolinas, FixedTrinomialSolinas32, FixedTrinomialSolinas64};
302
303/// An integer in modulo ring based on [Montgomery form](https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#Montgomery_form)
304pub type MontgomeryInt<T> = ReducedInt<T, Montgomery<T>>;
305
306/// An integer in modulo ring with a fixed (pseudo) Mersenne number as modulus
307pub type FixedMersenneInt<const P: u8, const K: umax> = ReducedInt<umax, FixedMersenne<P, K>>;
308
309/// An integer in modulo ring with a fixed (pseudo) Mersenne number as modulus (32-bit)
310pub type FixedMersenneInt32<const P: u8, const K: u32> = ReducedInt<u32, FixedMersenne32<P, K>>;
311
312/// An integer in modulo ring with a fixed (pseudo) Mersenne number as modulus (64-bit)
313pub type FixedMersenneInt64<const P: u8, const K: u64> = ReducedInt<u64, FixedMersenne64<P, K>>;
314
315/// An integer in modulo ring with a fixed Solinas number as modulus
316pub type FixedSolinasInt<const P1: u8, const P2: u8, const K: imax> =
317 ReducedInt<umax, FixedTrinomialSolinas<P1, P2, K>>;
318
319/// An integer in modulo ring with a fixed Solinas number as modulus (32-bit)
320pub type FixedSolinasInt32<const P1: u8, const P2: u8, const K: i32> =
321 ReducedInt<u32, FixedTrinomialSolinas32<P1, P2, K>>;
322
323/// An integer in modulo ring with a fixed Solinas number as modulus (64-bit)
324pub type FixedSolinasInt64<const P1: u8, const P2: u8, const K: i64> =
325 ReducedInt<u64, FixedTrinomialSolinas64<P1, P2, K>>;
326
327// pub type BarrettInt<T> = ReducedInt<T, BarrettInt<T>>;
328
329#[cfg(feature = "num-bigint")]
330mod bigint;