Skip to main content

field_cat/
bfield.rs

1//! The [`BFieldElement`] prime field: integers modulo
2//! `p = 2^64 - 2^32 + 1` (the Goldilocks prime).
3//!
4//! Goldilocks is the base field used by Triton VM, Risc0, and
5//! Plonky3 in its wider-field modes.  The modulus is chosen so
6//! that:
7//!
8//! - elements fit in a single `u64`,
9//! - reduction has a particularly efficient form on 64-bit
10//!   hardware (not yet exploited here),
11//! - two-adicity is 32, which is large enough for any practical
12//!   NTT-friendly size.
13//!
14//! This implementation is **naive but correct**: arithmetic uses
15//! `u128` intermediates and a `%` reduction.  A future revision
16//! will swap in the Montgomery / single-shot Goldilocks reduction
17//! used by Plonky3 and twenty-first for ~3-5x speedup.
18
19use crate::bytes::FieldBytes;
20use crate::error::Error;
21use crate::field::Field;
22
23/// The Goldilocks modulus: `2^64 - 2^32 + 1 = 18_446_744_069_414_584_321`.
24const P: u64 = 0xFFFF_FFFF_0000_0001;
25
26/// A field element in the Goldilocks prime field (mod `2^64 - 2^32 + 1`).
27///
28/// Stored as a canonical `u64` value in `[0, p)`.  Arithmetic
29/// promotes to `u128` to avoid `u64` overflow.
30///
31/// The name `BFieldElement` (Base Field Element) follows the
32/// convention from the Triton VM / twenty-first ecosystem.  In
33/// the Plonky3 ecosystem the same field is called `Goldilocks`.
34///
35/// # Examples
36///
37/// ```
38/// use field_cat::{BFieldElement, Field};
39///
40/// let a = BFieldElement::new(42);
41/// let b = BFieldElement::new(7);
42///
43/// assert_eq!((a * b).value(), 294);
44///
45/// // Multiplicative inverse via Fermat's little theorem.
46/// let a_inv = a.inv()?;
47/// assert_eq!(a * a_inv, BFieldElement::one());
48/// # Ok::<(), field_cat::Error>(())
49/// ```
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub struct BFieldElement(u64);
52
53impl BFieldElement {
54    /// Create a new field element, reducing modulo `p`.
55    #[must_use]
56    pub fn new(value: u64) -> Self {
57        Self(value % P)
58    }
59
60    /// The underlying integer value in `[0, p)`.
61    #[must_use]
62    pub fn value(self) -> u64 {
63        self.0
64    }
65
66    /// Reduce a `u128` intermediate to a canonical `u64` in `[0, p)`.
67    ///
68    /// The `try_from` is mathematically infallible (the reduced
69    /// value is strictly less than `p < 2^64`), so the
70    /// `unwrap_or_default()` only guards against a Rust-level
71    /// failure that the math rules out.
72    fn reduce(x: u128) -> u64 {
73        let p_128 = u128::from(P);
74        u64::try_from(x % p_128).unwrap_or_default()
75    }
76}
77
78impl core::fmt::Display for BFieldElement {
79    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
80        write!(f, "{}", self.0)
81    }
82}
83
84impl std::ops::Add for BFieldElement {
85    type Output = Self;
86    fn add(self, rhs: Self) -> Self {
87        Self(Self::reduce(u128::from(self.0) + u128::from(rhs.0)))
88    }
89}
90
91impl std::ops::Sub for BFieldElement {
92    type Output = Self;
93    fn sub(self, rhs: Self) -> Self {
94        // Add P first so the subtraction in u128 never underflows.
95        let lhs = u128::from(self.0) + u128::from(P);
96        Self(Self::reduce(lhs - u128::from(rhs.0)))
97    }
98}
99
100impl std::ops::Mul for BFieldElement {
101    type Output = Self;
102    fn mul(self, rhs: Self) -> Self {
103        Self(Self::reduce(u128::from(self.0) * u128::from(rhs.0)))
104    }
105}
106
107impl std::ops::Neg for BFieldElement {
108    type Output = Self;
109    fn neg(self) -> Self {
110        if self.0 == 0 {
111            Self(0)
112        } else {
113            Self(P - self.0)
114        }
115    }
116}
117
118impl Field for BFieldElement {
119    fn zero() -> Self {
120        Self(0)
121    }
122
123    fn one() -> Self {
124        Self(1)
125    }
126
127    fn inv(&self) -> Result<Self, Error> {
128        if self.0 == 0 {
129            Err(Error::DivisionByZero)
130        } else {
131            // Fermat's little theorem: a^(p-2) is the inverse for prime p.
132            Ok(pow(*self, P - 2))
133        }
134    }
135}
136
137impl FieldBytes for BFieldElement {
138    fn to_le_bytes(&self) -> Vec<u8> {
139        self.0.to_le_bytes().to_vec()
140    }
141
142    fn from_le_bytes(bytes: &[u8]) -> Result<Self, Error> {
143        bytes
144            .get(..8)
145            .ok_or(Error::InvalidFieldEncoding)
146            .and_then(|slice| <[u8; 8]>::try_from(slice).map_err(|_| Error::InvalidFieldEncoding))
147            .map(u64::from_le_bytes)
148            .map(Self::new)
149    }
150}
151
152/// Modular exponentiation `base^exp` in the Goldilocks field.
153///
154/// Builds the squares `base^(2^i)` lazily via `successors`, then
155/// folds the ones whose bit in `exp` is set.  Linear in the bit
156/// width of `exp`.
157fn pow(base: BFieldElement, exp: u64) -> BFieldElement {
158    std::iter::successors(Some(base), |&b| Some(b * b))
159        .zip(0..u64::BITS)
160        .filter(|&(_, i)| (exp >> i) & 1 == 1)
161        .map(|(p, _)| p)
162        .fold(BFieldElement::one(), |acc, p| acc * p)
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn modulus_constant_is_correct() {
171        // p = 2^64 - 2^32 + 1.  Verify by reconstructing.
172        assert_eq!(P, 0xFFFF_FFFF_0000_0001);
173        assert_eq!(P, 18_446_744_069_414_584_321);
174    }
175
176    #[test]
177    fn zero_is_additive_identity() {
178        let a = BFieldElement::new(123_456_789);
179        assert_eq!(a + BFieldElement::zero(), a);
180        assert_eq!(BFieldElement::zero() + a, a);
181    }
182
183    #[test]
184    fn one_is_multiplicative_identity() {
185        let a = BFieldElement::new(123_456_789);
186        assert_eq!(a * BFieldElement::one(), a);
187        assert_eq!(BFieldElement::one() * a, a);
188    }
189
190    #[test]
191    fn additive_inverse() {
192        let a = BFieldElement::new(999_999_999_999);
193        assert_eq!(a + (-a), BFieldElement::zero());
194    }
195
196    #[test]
197    fn multiplicative_inverse() -> Result<(), Error> {
198        let a = BFieldElement::new(42);
199        let a_inv = a.inv()?;
200        assert_eq!(a * a_inv, BFieldElement::one());
201        Ok(())
202    }
203
204    #[test]
205    fn inverse_of_zero_fails() {
206        let result = BFieldElement::zero().inv();
207        assert!(result.is_err());
208    }
209
210    #[test]
211    fn sample_inverses() -> Result<(), Error> {
212        let samples = [1u64, 2, 7, 100, 1_000_000, 1_000_000_000_000, P - 1, P - 2];
213        samples.iter().try_for_each(|&v| {
214            let a = BFieldElement::new(v);
215            let a_inv = a.inv()?;
216            assert_eq!(a * a_inv, BFieldElement::one(), "failed for {v}");
217            Ok(())
218        })
219    }
220
221    #[test]
222    fn subtraction_is_add_neg() {
223        let a = BFieldElement::new(123_456_789_000);
224        let b = BFieldElement::new(987_654_321);
225        assert_eq!(a - b, a + (-b));
226    }
227
228    #[test]
229    fn multiplication_is_commutative() {
230        let a = BFieldElement::new(12_345_678);
231        let b = BFieldElement::new(98_765_432);
232        assert_eq!(a * b, b * a);
233    }
234
235    #[test]
236    fn distributivity() {
237        let a = BFieldElement::new(111_111);
238        let b = BFieldElement::new(222_222);
239        let c = BFieldElement::new(333_333);
240        assert_eq!(a * (b + c), a * b + a * c);
241    }
242
243    #[test]
244    fn new_reduces_mod_p() {
245        assert_eq!(BFieldElement::new(P), BFieldElement::new(0));
246        assert_eq!(BFieldElement::new(P + 1), BFieldElement::new(1));
247    }
248
249    #[test]
250    fn p_minus_one_squared_is_one() -> Result<(), Error> {
251        // (p - 1) ≡ -1 (mod p), and (-1)^2 = 1.
252        let neg_one = BFieldElement::new(P - 1);
253        assert_eq!(neg_one * neg_one, BFieldElement::one());
254        // -1 is its own inverse.
255        let neg_one_inv = neg_one.inv()?;
256        assert_eq!(neg_one_inv, neg_one);
257        Ok(())
258    }
259
260    #[test]
261    fn high_u64_values_multiply_without_overflow() {
262        // Both inputs near 2^64 stress the u128 intermediate path.
263        let a = BFieldElement::new(P - 5);
264        let b = BFieldElement::new(P - 7);
265        // (p - 5) * (p - 7) ≡ 35 (mod p).
266        assert_eq!(a * b, BFieldElement::new(35));
267    }
268
269    #[test]
270    fn bytes_roundtrip() -> Result<(), Error> {
271        let a = BFieldElement::new(0xDEAD_BEEF_1234_5678);
272        let bytes = a.to_le_bytes();
273        let b = BFieldElement::from_le_bytes(&bytes)?;
274        assert_eq!(a, b);
275        Ok(())
276    }
277
278    #[test]
279    fn bytes_zero_roundtrip() -> Result<(), Error> {
280        let a = BFieldElement::zero();
281        let b = BFieldElement::from_le_bytes(&a.to_le_bytes())?;
282        assert_eq!(a, b);
283        Ok(())
284    }
285
286    #[test]
287    fn bytes_too_short_fails() {
288        let result = BFieldElement::from_le_bytes(&[1, 2, 3]);
289        assert!(result.is_err());
290    }
291
292    #[test]
293    fn bytes_empty_fails() {
294        let result = BFieldElement::from_le_bytes(&[]);
295        assert!(result.is_err());
296    }
297}