Skip to main content

stwo_gpu/core/fields/
m31.rs

1use core::fmt::Display;
2use core::ops::{
3    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
4};
5
6use bytemuck::{Pod, Zeroable};
7use rand::distributions::{Distribution, Standard};
8use serde::{Deserialize, Serialize};
9
10use super::{ComplexConjugate, FieldExpOps};
11use crate::impl_field;
12pub const MODULUS_BITS: u32 = 31;
13pub const N_BYTES_FELT: usize = 4;
14pub const P: u32 = 2147483647; // 2 ** 31 - 1
15
16#[repr(transparent)]
17#[derive(
18    Copy,
19    Clone,
20    Debug,
21    Default,
22    PartialEq,
23    Eq,
24    PartialOrd,
25    Ord,
26    Hash,
27    Pod,
28    Zeroable,
29    Serialize,
30    Deserialize,
31)]
32pub struct M31(pub u32);
33pub type BaseField = M31;
34
35impl_field!(M31, P);
36
37impl M31 {
38    /// Returns `val % P` when `val` is in the range `[0, 2P)`.
39    ///
40    /// ```
41    /// use stwo::core::fields::m31::{M31, P};
42    ///
43    /// let val = 2 * P - 19;
44    /// assert_eq!(M31::partial_reduce(val), M31::from(P - 19));
45    /// ```
46    pub fn partial_reduce(val: u32) -> Self {
47        Self(val.checked_sub(P).unwrap_or(val))
48    }
49
50    /// Returns `val % P` when `val` is in the range `[0, P^2)`.
51    ///
52    /// ```
53    /// use stwo::core::fields::m31::{M31, P};
54    ///
55    /// let val = (P as u64).pow(2) - 19;
56    /// assert_eq!(M31::reduce(val), M31::from(P - 19));
57    /// ```
58    pub const fn reduce(val: u64) -> Self {
59        Self((((((val >> MODULUS_BITS) + val + 1) >> MODULUS_BITS) + val) & (P as u64)) as u32)
60    }
61
62    pub const fn from_u32_unchecked(arg: u32) -> Self {
63        Self(arg)
64    }
65
66    pub fn inverse(&self) -> Self {
67        assert!(!self.is_zero(), "0 has no inverse");
68        pow2147483645(*self)
69    }
70}
71
72impl Display for M31 {
73    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74        write!(f, "{}", self.0)
75    }
76}
77
78impl Add for M31 {
79    type Output = Self;
80
81    fn add(self, rhs: Self) -> Self::Output {
82        Self::partial_reduce(self.0 + rhs.0)
83    }
84}
85
86impl Neg for M31 {
87    type Output = Self;
88
89    fn neg(self) -> Self::Output {
90        Self::partial_reduce(P - self.0)
91    }
92}
93
94impl Sub for M31 {
95    type Output = Self;
96
97    fn sub(self, rhs: Self) -> Self::Output {
98        Self::partial_reduce(self.0 + P - rhs.0)
99    }
100}
101
102impl Mul for M31 {
103    type Output = Self;
104
105    fn mul(self, rhs: Self) -> Self::Output {
106        Self::reduce((self.0 as u64) * (rhs.0 as u64))
107    }
108}
109
110impl FieldExpOps for M31 {
111    /// ```
112    /// use num_traits::One;
113    /// use stwo::core::fields::m31::BaseField;
114    /// use stwo::core::fields::FieldExpOps;
115    ///
116    /// let v = BaseField::from(19);
117    /// assert_eq!(v.inverse() * v, BaseField::one());
118    /// ```
119    fn inverse(&self) -> Self {
120        self.inverse()
121    }
122}
123
124impl ComplexConjugate for M31 {
125    fn complex_conjugate(&self) -> Self {
126        *self
127    }
128}
129
130impl One for M31 {
131    fn one() -> Self {
132        Self(1)
133    }
134}
135
136impl Zero for M31 {
137    fn zero() -> Self {
138        Self(0)
139    }
140
141    fn is_zero(&self) -> bool {
142        *self == Self::zero()
143    }
144}
145
146impl From<usize> for M31 {
147    fn from(value: usize) -> Self {
148        M31::reduce(value.try_into().unwrap())
149    }
150}
151
152impl From<u32> for M31 {
153    fn from(value: u32) -> Self {
154        M31::reduce(value.into())
155    }
156}
157
158impl From<i32> for M31 {
159    fn from(value: i32) -> Self {
160        if value < 0 {
161            const P2: u64 = 2 * P as u64;
162            return M31::reduce(P2 - value.unsigned_abs() as u64);
163        }
164
165        M31::reduce(value.unsigned_abs() as u64)
166    }
167}
168
169impl Distribution<M31> for Standard {
170    // Not intended for cryptographic use. Should only be used in tests and benchmarks.
171    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> M31 {
172        M31(rng.gen_range(0..P))
173    }
174}
175
176#[cfg(test)]
177#[macro_export]
178macro_rules! m31 {
179    ($m:expr) => {
180        $crate::core::fields::m31::M31::from_u32_unchecked($m)
181    };
182}
183
184/// Computes `v^((2^31-1)-2)`.
185///
186/// Computes the multiplicative inverse of [`M31`] elements with 37 multiplications vs naive 60
187/// multiplications. Made generic to support both vectorized and non-vectorized implementations.
188/// Multiplication tree found with [addchain](https://github.com/mmcloughlin/addchain).
189///
190/// ```
191/// use stwo::core::fields::m31::{pow2147483645, BaseField};
192/// use stwo::core::fields::FieldExpOps;
193///
194/// let v = BaseField::from(19);
195/// assert_eq!(pow2147483645(v), v.pow(2147483645));
196/// ```
197pub fn pow2147483645<T: FieldExpOps>(v: T) -> T {
198    let t0 = sqn::<2, T>(v.clone()) * v.clone();
199    let t1 = sqn::<1, T>(t0.clone()) * t0.clone();
200    let t2 = sqn::<3, T>(t1.clone()) * t0.clone();
201    let t3 = sqn::<1, T>(t2.clone()) * t0.clone();
202    let t4 = sqn::<8, T>(t3.clone()) * t3.clone();
203    let t5 = sqn::<8, T>(t4.clone()) * t3.clone();
204    sqn::<7, T>(t5) * t2
205}
206
207/// Computes `v^(2*n)`.
208fn sqn<const N: usize, T: FieldExpOps>(mut v: T) -> T {
209    for _ in 0..N {
210        v = v.square();
211    }
212    v
213}
214
215#[cfg(test)]
216mod tests {
217    use rand::rngs::SmallRng;
218    use rand::{Rng, SeedableRng};
219
220    use super::{M31, P};
221
222    const fn mul_p(a: u32, b: u32) -> u32 {
223        ((a as u64 * b as u64) % P as u64) as u32
224    }
225
226    const fn add_p(a: u32, b: u32) -> u32 {
227        (a + b) % P
228    }
229
230    const fn neg_p(a: u32) -> u32 {
231        if a == 0 {
232            0
233        } else {
234            P - a
235        }
236    }
237
238    #[test]
239    fn test_basic_ops() {
240        let mut rng = SmallRng::seed_from_u64(0);
241        for _ in 0..10000 {
242            let x: u32 = rng.gen::<u32>() % P;
243            let y: u32 = rng.gen::<u32>() % P;
244            assert_eq!(m31!(add_p(x, y)), m31!(x) + m31!(y));
245            assert_eq!(m31!(mul_p(x, y)), m31!(x) * m31!(y));
246            assert_eq!(m31!(neg_p(x)), -m31!(x));
247        }
248    }
249
250    #[test]
251    fn test_m31_from_i32() {
252        assert_eq!(M31::from(-1_i32), M31::from(P - 1));
253        assert_eq!(M31::from(-10_i32), M31::from(P - 10));
254        assert_eq!(M31::from(1_i32), M31::from(1));
255        assert_eq!(M31::from(10_i32), M31::from(10));
256    }
257}