commonware_math/fields/
goldilocks.rs

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