Skip to main content

stwo_gpu/core/fields/
qm31.rs

1use core::fmt::{Debug, Display};
2use core::ops::{
3    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
4};
5
6use serde::{Deserialize, Serialize};
7
8use super::{ComplexConjugate, FieldExpOps};
9use crate::core::fields::cm31::CM31;
10use crate::core::fields::m31::M31;
11use crate::{impl_extension_field, impl_field};
12
13pub const P4: u128 = 21267647892944572736998860269687930881; // (2 ** 31 - 1) ** 4
14pub const R: CM31 = CM31::from_u32_unchecked(2, 1);
15
16/// Extension field of CM31.
17/// Equivalent to CM31\[x\] over (x^2 - 2 - i) as the irreducible polynomial.
18/// Represented as ((a, b), (c, d)) of (a + bi) + (c + di)u.
19#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
20pub struct QM31(pub CM31, pub CM31);
21pub type SecureField = QM31;
22
23pub const SECURE_EXTENSION_DEGREE: usize =
24    <SecureField as ExtensionOf<super::m31::BaseField>>::EXTENSION_DEGREE;
25
26impl_field!(QM31, P4);
27impl_extension_field!(QM31, CM31);
28
29impl QM31 {
30    pub const fn from_u32_unchecked(a: u32, b: u32, c: u32, d: u32) -> Self {
31        Self(
32            CM31::from_u32_unchecked(a, b),
33            CM31::from_u32_unchecked(c, d),
34        )
35    }
36
37    pub const fn from_m31(a: M31, b: M31, c: M31, d: M31) -> Self {
38        Self(CM31::from_m31(a, b), CM31::from_m31(c, d))
39    }
40
41    pub const fn from_m31_array(array: [M31; SECURE_EXTENSION_DEGREE]) -> Self {
42        Self::from_m31(array[0], array[1], array[2], array[3])
43    }
44
45    pub const fn to_m31_array(self) -> [M31; SECURE_EXTENSION_DEGREE] {
46        [self.0 .0, self.0 .1, self.1 .0, self.1 .1]
47    }
48
49    /// Returns the combined value, given the values of its composing base field polynomials at that
50    /// point.
51    pub fn from_partial_evals(evals: [Self; SECURE_EXTENSION_DEGREE]) -> Self {
52        let mut res = evals[0];
53        res += evals[1] * Self::from_u32_unchecked(0, 1, 0, 0);
54        res += evals[2] * Self::from_u32_unchecked(0, 0, 1, 0);
55        res += evals[3] * Self::from_u32_unchecked(0, 0, 0, 1);
56        res
57    }
58
59    // Note: Adding this as a Mul impl drives rust insane, and it tries to infer Qm31*Qm31 as
60    // QM31*CM31.
61    pub fn mul_cm31(self, rhs: CM31) -> Self {
62        Self(self.0 * rhs, self.1 * rhs)
63    }
64}
65
66impl Display for QM31 {
67    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68        write!(f, "({}) + ({})u", self.0, self.1)
69    }
70}
71
72impl Debug for QM31 {
73    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74        write!(f, "({}) + ({})u", self.0, self.1)
75    }
76}
77
78impl Mul for QM31 {
79    type Output = Self;
80
81    fn mul(self, rhs: Self) -> Self::Output {
82        // (a + bu) * (c + du) = (ac + rbd) + (ad + bc)u.
83        Self(
84            self.0 * rhs.0 + R * self.1 * rhs.1,
85            self.0 * rhs.1 + self.1 * rhs.0,
86        )
87    }
88}
89
90impl From<usize> for QM31 {
91    fn from(value: usize) -> Self {
92        M31::from(value).into()
93    }
94}
95
96impl From<u32> for QM31 {
97    fn from(value: u32) -> Self {
98        M31::from(value).into()
99    }
100}
101
102impl From<i32> for QM31 {
103    fn from(value: i32) -> Self {
104        M31::from(value).into()
105    }
106}
107
108impl TryInto<M31> for QM31 {
109    type Error = ();
110
111    fn try_into(self) -> Result<M31, Self::Error> {
112        if self.1 != CM31::zero() {
113            return Err(());
114        }
115        self.0.try_into()
116    }
117}
118
119impl FieldExpOps for QM31 {
120    fn inverse(&self) -> Self {
121        assert!(!self.is_zero(), "0 has no inverse");
122        // (a + bu)^-1 = (a - bu) / (a^2 - (2+i)b^2).
123        let b2 = self.1.square();
124        let ib2 = CM31(-b2.1, b2.0);
125        let denom = self.0.square() - (b2 + b2 + ib2);
126        let denom_inverse = denom.inverse();
127        Self(self.0 * denom_inverse, -self.1 * denom_inverse)
128    }
129}
130
131#[cfg(test)]
132#[macro_export]
133macro_rules! qm31 {
134    ($m0:expr, $m1:expr, $m2:expr, $m3:expr) => {{
135        use $crate::core::fields::qm31::QM31;
136        QM31::from_u32_unchecked($m0, $m1, $m2, $m3)
137    }};
138}
139
140#[cfg(test)]
141mod tests {
142    use num_traits::One;
143
144    use super::QM31;
145    use crate::core::fields::m31::P;
146    use crate::core::fields::FieldExpOps;
147    use crate::m31;
148
149    #[test]
150    fn test_inverse() {
151        let qm = qm31!(1, 2, 3, 4);
152        let qm_inv = qm.inverse();
153        assert_eq!(qm * qm_inv, QM31::one());
154    }
155
156    #[test]
157    fn test_ops() {
158        let qm0 = qm31!(1, 2, 3, 4);
159        let qm1 = qm31!(4, 5, 6, 7);
160        let m = m31!(8);
161        let qm = QM31::from(m);
162        let qm0_x_qm1 = qm31!(P - 71, 93, P - 16, 50);
163
164        assert_eq!(qm0 + qm1, qm31!(5, 7, 9, 11));
165        assert_eq!(qm1 + m, qm1 + qm);
166        assert_eq!(qm0 * qm1, qm0_x_qm1);
167        assert_eq!(qm1 * m, qm1 * qm);
168        assert_eq!(-qm0, qm31!(P - 1, P - 2, P - 3, P - 4));
169        assert_eq!(qm0 - qm1, qm31!(P - 3, P - 3, P - 3, P - 3));
170        assert_eq!(qm1 - m, qm1 - qm);
171        assert_eq!(qm0_x_qm1 / qm1, qm31!(1, 2, 3, 4));
172        assert_eq!(qm1 / m, qm1 / qm);
173    }
174}