Skip to main content

commonware_math/fields/
goldilocks.rs

1use crate::algebra::{Additive, Field, FieldNTT, Multiplicative, Object, Random, Ring};
2use commonware_codec::{Buf, FixedSize, Read, Write};
3use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
4use rand_core::CryptoRng;
5
6/// The modulus P := 2^64 - 2^32 + 1.
7///
8/// This is a prime number, and we use it to form a field of this order.
9const P: u64 = u64::wrapping_neg(1 << 32) + 1;
10
11/// An element of the [Goldilocks field](https://xn--2-umb.com/22/goldilocks/).
12#[derive(Clone, Copy, PartialEq, Eq)]
13pub struct F(u64);
14
15impl FixedSize for F {
16    const SIZE: usize = u64::SIZE;
17}
18
19impl Write for F {
20    fn write(&self, buf: &mut impl bytes::BufMut) {
21        self.0.write(buf)
22    }
23}
24
25impl Read for F {
26    type Cfg = <u64 as Read>::Cfg;
27
28    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
29        let x = u64::read_cfg(buf, cfg)?;
30        if x >= P {
31            return Err(commonware_codec::Error::Invalid("F", "out of range"));
32        }
33        Ok(Self(x))
34    }
35}
36
37impl core::fmt::Debug for F {
38    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39        write!(f, "{:016X}", self.0)
40    }
41}
42
43#[cfg(any(test, feature = "arbitrary"))]
44impl arbitrary::Arbitrary<'_> for F {
45    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
46        let x = u.arbitrary::<u64>()?;
47        Ok(Self::reduce_64(x))
48    }
49}
50
51impl F {
52    // The following constants are not randomly chosen, but computed in a specific
53    // way. They could be computed at compile time, with each definition actually
54    // doing the computation, but to avoid burdening compilation, we instead enforce
55    // where they originate from with tests.
56
57    /// Any non-zero element x = GENERATOR^k, for some k.
58    ///
59    /// This is chosen such that GENERATOR^((P - 1) / 64) = 8.
60    #[cfg(test)]
61    pub const GENERATOR: Self = Self(0xd64f951101aff9bf);
62
63    /// An element of order 2^32.
64    ///
65    /// This is specifically chosen such that ROOT_OF_UNITY^(2^26) = 8.
66    ///
67    /// That enables optimizations when doing NTTs, and things like that.
68    pub const ROOT_OF_UNITY: Self = Self(0xee41f5320c4ea145);
69
70    /// An element guaranteed not to be any power of [Self::ROOT_OF_UNITY].
71    pub const NOT_ROOT_OF_UNITY: Self = Self(0x79bc2f50acd74161);
72
73    /// The inverse of [Self::NOT_ROOT_OF_UNITY].
74    pub const NOT_ROOT_OF_UNITY_INV: Self = Self(0x1036c4023580ce8d);
75
76    /// The zero element of the field.
77    ///
78    /// This is the identity for addition.
79    const ZERO: Self = Self(0);
80
81    /// The one element of the field.
82    ///
83    /// This is the identity for multiplication.
84    const ONE: Self = Self(1);
85
86    const fn add_inner(self, b: Self) -> Self {
87        // We want to calculate self + b mod P.
88        // At a high level, this can be done by adding self + b, as integers,
89        // and then subtracting P as long as the result >= P.
90        //
91        // How many times do we need to do this?
92        //
93        // self <= P - 1
94        // b <= P - 1
95        // ∴ self + b <= 2P - 2
96        // ∴ self + b - P <= P - 1
97        //
98        // So, we need to subtract P at most once.
99
100        // addition + 2^64 * overflow = self + b
101        let (addition, overflow) = self.0.overflowing_add(b.0);
102        // In the case of overflow = 1, addition + 2^64 > P, so we need to
103        // subtract. The result of this subtraction will be < 2^64,
104        // so we can compute it by calculating addition - P, wrapping around.
105        let (subtraction, underflow) = addition.overflowing_sub(P);
106        // In the case of overflow, we use the subtraction (as mentioned above).
107        // Otherwise, use the subtraction as long as we didn't underflow
108        if overflow || !underflow {
109            Self(subtraction)
110        } else {
111            Self(addition)
112        }
113    }
114
115    const fn sub_inner(self, b: Self) -> Self {
116        // The strategy here is to perform the subtraction, and then (maybe) add back P.
117        // If no underflow happened, the result is reduced, since both values were < P.
118        // If an underflow happened, the largest result we can have is -1. Adding
119        // P gives us P - 1, which is < P, so everything works.
120        let (subtraction, underflow) = self.0.overflowing_sub(b.0);
121        if underflow {
122            Self(subtraction.wrapping_add(P))
123        } else {
124            Self(subtraction)
125        }
126    }
127
128    const fn reduce_64(x: u64) -> Self {
129        // 2 * P > 2^64 - 1 (by a long margin)
130        // We thus need to subtract P at most once.
131        let (subtraction, underflow) = x.overflowing_sub(P);
132        if underflow {
133            Self(x)
134        } else {
135            Self(subtraction)
136        }
137    }
138
139    /// Reduce a 128 bit integer into a field element.
140    const fn reduce_128(x: u128) -> Self {
141        // We exploit special properties of the field.
142        //
143        // First, 2^64 = 2^32 - 1 mod P.
144        //
145        // Second, 2^96 = 2^32(2^32 - 1) = 2^64 - 2^32 = -1 mod P.
146        //
147        // Thus, if we write a 128 bit integer x as:
148        //     x = c 2^96 + b 2^64 + a
149        // We have:
150        //     x = b (2^32 - 1) + (a - c) mod P
151        // And this expression will be our strategy for performing the reduction.
152        let a = x as u64;
153        let b = ((x >> 64) & 0xFF_FF_FF_FF) as u64;
154        let c = (x >> 96) as u64;
155
156        // While we lean on existing code, we need to be careful because some of
157        // these types are partially reduced.
158        //
159        // First, if we look at a - c, the end result with our field code can
160        // be any 64 bit value (consider c = 0). We can also make the same assumption
161        // for (b << 32) - b. The question then becomes, is Field(x) + Field(y)
162        // ok even if both x and y are arbitrary u64 values?
163        //
164        // Yes. Even if x and y have the maximum value, a single subtraction of P
165        // would suffice to make their sum < P. Thus, our strategy for field addition
166        // will always work.
167        //
168        // Note: (b << 32) - b = b * (2^32 - 1). Since b <= 2^32 - 1, this is at most
169        // (2^32 - 1)^2 = 2^64 - 2^33 + 1 < 2^64. Since b << 32 >= b always,
170        // this subtraction will never underflow.
171        Self(a).sub_inner(Self(c)).add_inner(Self((b << 32) - b))
172    }
173
174    const fn mul_inner(self, b: Self) -> Self {
175        // We do a u64 x u64 -> u128 multiplication, then reduce mod P
176        Self::reduce_128((self.0 as u128) * (b.0 as u128))
177    }
178
179    const fn neg_inner(self) -> Self {
180        Self::ZERO.sub_inner(self)
181    }
182
183    /// Return the multiplicative inverse of a field element.
184    ///
185    /// [Self::zero] will return [Self::zero].
186    pub fn inv(self) -> Self {
187        self.exp(&[P - 2])
188    }
189
190    /// Convert a stream of u64s into a stream of field elements.
191    pub fn stream_from_u64s(inner: impl Iterator<Item = u64>) -> impl Iterator<Item = Self> {
192        struct Iter<I> {
193            acc: u128,
194            acc_bits: u32,
195            inner: I,
196        }
197
198        impl<I: Iterator<Item = u64>> Iterator for Iter<I> {
199            type Item = F;
200
201            fn next(&mut self) -> Option<Self::Item> {
202                while self.acc_bits < 63 {
203                    let Some(x) = self.inner.next() else {
204                        break;
205                    };
206                    let x = u128::from(x);
207                    self.acc |= x << self.acc_bits;
208                    self.acc_bits += 64;
209                }
210                if self.acc_bits > 0 {
211                    self.acc_bits = self.acc_bits.saturating_sub(63);
212                    let out = F((self.acc as u64) & ((1 << 63) - 1));
213                    self.acc >>= 63;
214                    return Some(out);
215                }
216                None
217            }
218        }
219
220        Iter {
221            acc: 0,
222            acc_bits: 0,
223            inner,
224        }
225    }
226
227    /// Convert a stream produced by [F::stream_from_u64s] back to the original stream.
228    ///
229    /// This may produce a single extra 0 element.
230    pub fn stream_to_u64s(inner: impl Iterator<Item = Self>) -> impl Iterator<Item = u64> {
231        struct Iter<I> {
232            acc: u128,
233            acc_bits: u32,
234            inner: I,
235        }
236
237        impl<I: Iterator<Item = F>> Iterator for Iter<I> {
238            type Item = u64;
239
240            fn next(&mut self) -> Option<Self::Item> {
241                // Try and fill acc with 64 bits of data.
242                while self.acc_bits < 64 {
243                    let Some(F(x)) = self.inner.next() else {
244                        break;
245                    };
246                    // Ignore any upper bits of x
247                    let x = u128::from(x & ((1 << 63) - 1));
248                    self.acc |= x << self.acc_bits;
249                    self.acc_bits += 63;
250                }
251                if self.acc_bits > 0 {
252                    self.acc_bits = self.acc_bits.saturating_sub(64);
253                    let out = self.acc as u64;
254                    self.acc >>= 64;
255                    return Some(out);
256                }
257                None
258            }
259        }
260        Iter {
261            acc: 0,
262            acc_bits: 0,
263            inner,
264        }
265    }
266
267    /// How many elements are used to encode a given number of bits?
268    ///
269    /// This is based on what [F::stream_from_u64s] does.
270    pub const fn bits_to_elements(bits: usize) -> usize {
271        bits.div_ceil(63)
272    }
273
274    /// Convert this element to little-endian bytes.
275    pub const fn to_le_bytes(&self) -> [u8; 8] {
276        self.0.to_le_bytes()
277    }
278}
279
280impl Object for F {}
281
282impl Random for F {
283    fn random(mut rng: impl CryptoRng) -> Self {
284        // this fails only about once every 2^32 attempts
285        loop {
286            let x = rng.next_u64();
287            if x < P {
288                return Self(x);
289            }
290        }
291    }
292}
293
294impl Add for F {
295    type Output = Self;
296
297    fn add(self, b: Self) -> Self::Output {
298        self.add_inner(b)
299    }
300}
301
302impl<'a> Add<&'a Self> for F {
303    type Output = Self;
304
305    fn add(self, rhs: &'a Self) -> Self::Output {
306        self + *rhs
307    }
308}
309
310impl<'a> AddAssign<&'a Self> for F {
311    fn add_assign(&mut self, rhs: &'a Self) {
312        *self = *self + rhs
313    }
314}
315
316impl<'a> Sub<&'a Self> for F {
317    type Output = Self;
318
319    fn sub(self, rhs: &'a Self) -> Self::Output {
320        self - *rhs
321    }
322}
323
324impl<'a> SubAssign<&'a Self> for F {
325    fn sub_assign(&mut self, rhs: &'a Self) {
326        *self = *self - rhs;
327    }
328}
329
330impl Additive for F {
331    fn zero() -> Self {
332        Self::ZERO
333    }
334}
335
336impl Sub for F {
337    type Output = Self;
338
339    fn sub(self, b: Self) -> Self::Output {
340        self.sub_inner(b)
341    }
342}
343
344impl Mul for F {
345    type Output = Self;
346
347    fn mul(self, b: Self) -> Self::Output {
348        Self::mul_inner(self, b)
349    }
350}
351
352impl<'a> Mul<&'a Self> for F {
353    type Output = Self;
354
355    fn mul(self, rhs: &'a Self) -> Self::Output {
356        self * *rhs
357    }
358}
359
360impl<'a> MulAssign<&'a Self> for F {
361    fn mul_assign(&mut self, rhs: &'a Self) {
362        *self = *self * rhs;
363    }
364}
365
366impl Multiplicative for F {}
367
368impl Neg for F {
369    type Output = Self;
370
371    fn neg(self) -> Self::Output {
372        self.neg_inner()
373    }
374}
375
376impl From<u64> for F {
377    fn from(value: u64) -> Self {
378        Self::reduce_64(value)
379    }
380}
381
382impl Ring for F {
383    fn one() -> Self {
384        Self::ONE
385    }
386}
387
388impl Field for F {
389    fn inv(&self) -> Self {
390        Self::inv(*self)
391    }
392}
393
394impl FieldNTT for F {
395    const MAX_LG_ROOT_ORDER: u8 = 32;
396
397    fn root_of_unity(lg: u8) -> Option<Self> {
398        if lg > Self::MAX_LG_ROOT_ORDER {
399            return None;
400        }
401        let mut out = Self::ROOT_OF_UNITY;
402        for _ in 0..(Self::MAX_LG_ROOT_ORDER - lg) {
403            out = out * out;
404        }
405        Some(out)
406    }
407
408    fn coset_shift() -> Self {
409        Self::NOT_ROOT_OF_UNITY
410    }
411
412    fn coset_shift_inv() -> Self {
413        Self::NOT_ROOT_OF_UNITY_INV
414    }
415
416    fn div_2(&self) -> Self {
417        if self.0 & 1 == 0 {
418            Self(self.0 >> 1)
419        } else {
420            let (addition, carry) = self.0.overflowing_add(P);
421            Self((u64::from(carry) << 63) | (addition >> 1))
422        }
423    }
424}
425
426#[cfg(any(test, feature = "fuzz"))]
427pub mod fuzz {
428    use super::*;
429    use crate::algebra::test_suites;
430    use arbitrary::{Arbitrary, Unstructured};
431    use commonware_codec::{Encode as _, ReadExt as _};
432
433    #[derive(Debug)]
434    pub struct NonCanonicalU64(pub u64);
435
436    impl Arbitrary<'_> for NonCanonicalU64 {
437        fn arbitrary(u: &mut Unstructured<'_>) -> arbitrary::Result<Self> {
438            Ok(Self(u.int_in_range(P..=u64::MAX)?))
439        }
440    }
441
442    #[derive(Debug, Arbitrary)]
443    pub enum Plan {
444        StreamRoundtrip(Vec<u64>),
445        ReadRejectsOutOfRange(NonCanonicalU64),
446        FuzzField,
447    }
448
449    impl Plan {
450        pub fn run(self, u: &mut Unstructured<'_>) -> arbitrary::Result<()> {
451            match self {
452                Self::StreamRoundtrip(data) => {
453                    let mut roundtrip =
454                        F::stream_to_u64s(F::stream_from_u64s(data.clone().into_iter()))
455                            .collect::<Vec<_>>();
456                    roundtrip.truncate(data.len());
457                    assert_eq!(data, roundtrip);
458                }
459                Self::ReadRejectsOutOfRange(NonCanonicalU64(x)) => {
460                    let result = F::read(&mut x.encode());
461                    assert!(matches!(
462                        result,
463                        Err(commonware_codec::Error::Invalid("F", "out of range"))
464                    ));
465                }
466                Self::FuzzField => {
467                    test_suites::fuzz_field_ntt::<F>(u)?;
468                }
469            }
470            Ok(())
471        }
472    }
473
474    #[test]
475    fn test_fuzz() {
476        commonware_invariants::minifuzz::test(|u| u.arbitrary::<Plan>()?.run(u));
477    }
478
479    #[test]
480    fn test_read_cfg_rejects_modulus_regression_case() {
481        let mut u = Unstructured::new(&[]);
482        Plan::ReadRejectsOutOfRange(NonCanonicalU64(P))
483            .run(&mut u)
484            .expect("regression plan should succeed");
485    }
486}
487
488#[cfg(test)]
489mod test {
490    use super::*;
491
492    #[test]
493    fn test_generator_calculation() {
494        assert_eq!(F::GENERATOR, F(7).exp(&[133]));
495    }
496
497    #[test]
498    fn test_root_of_unity_calculation() {
499        assert_eq!(F::ROOT_OF_UNITY, F::GENERATOR.exp(&[(P - 1) >> 32]));
500    }
501
502    #[test]
503    fn test_not_root_of_unity_calculation() {
504        assert_eq!(F::NOT_ROOT_OF_UNITY, F::GENERATOR.exp(&[1 << 32]));
505    }
506
507    #[test]
508    fn test_not_root_of_unity_inv_calculation() {
509        assert_eq!(F::NOT_ROOT_OF_UNITY * F::NOT_ROOT_OF_UNITY_INV, F::one());
510    }
511
512    #[test]
513    fn test_root_of_unity_exp() {
514        assert_eq!(F::ROOT_OF_UNITY.exp(&[1 << 26]), F(8));
515    }
516
517    #[cfg(feature = "arbitrary")]
518    mod conformance {
519        use super::*;
520        use commonware_codec::conformance::CodecConformance;
521
522        commonware_conformance::conformance_tests! {
523            CodecConformance<F>
524        }
525    }
526}