commonware_math/fields/
goldilocks.rs1use 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
6const P: u64 = u64::wrapping_neg(1 << 32) + 1;
10
11#[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 #[cfg(test)]
61 pub const GENERATOR: Self = Self(0xd64f951101aff9bf);
62
63 pub const ROOT_OF_UNITY: Self = Self(0xee41f5320c4ea145);
69
70 pub const NOT_ROOT_OF_UNITY: Self = Self(0x79bc2f50acd74161);
72
73 pub const NOT_ROOT_OF_UNITY_INV: Self = Self(0x1036c4023580ce8d);
75
76 const ZERO: Self = Self(0);
80
81 const ONE: Self = Self(1);
85
86 const fn add_inner(self, b: Self) -> Self {
87 let (addition, overflow) = self.0.overflowing_add(b.0);
102 let (subtraction, underflow) = addition.overflowing_sub(P);
106 if overflow || !underflow {
109 Self(subtraction)
110 } else {
111 Self(addition)
112 }
113 }
114
115 const fn sub_inner(self, b: Self) -> Self {
116 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 let (subtraction, underflow) = x.overflowing_sub(P);
132 if underflow {
133 Self(x)
134 } else {
135 Self(subtraction)
136 }
137 }
138
139 const fn reduce_128(x: u128) -> Self {
141 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 Self(a).sub_inner(Self(c)).add_inner(Self((b << 32) - b))
172 }
173
174 const fn mul_inner(self, b: Self) -> Self {
175 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 pub fn inv(self) -> Self {
187 self.exp(&[P - 2])
188 }
189
190 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 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 while self.acc_bits < 64 {
243 let Some(F(x)) = self.inner.next() else {
244 break;
245 };
246 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 pub const fn bits_to_elements(bits: usize) -> usize {
271 bits.div_ceil(63)
272 }
273
274 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 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}