1use alloc::vec;
2use alloc::vec::Vec;
3use core::fmt::{Debug, Display, Formatter};
4use core::hash::{Hash, Hasher};
5use core::hint::assert_unchecked;
6use core::iter::{Product, Sum};
7use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
8use core::{array, fmt};
9
10use num_bigint::BigUint;
11use p3_field::exponentiation::exp_10540996611094048183;
12use p3_field::integers::QuotientMap;
13use p3_field::op_assign_macros::{
14 impl_add_assign, impl_div_methods, impl_mul_methods, impl_sub_assign,
15};
16use p3_field::{
17 Field, InjectiveMonomial, Packable, PermutationMonomial, PrimeCharacteristicRing, PrimeField,
18 PrimeField64, RawDataSerializable, TwoAdicField, UniformSamplingField,
19 impl_raw_serializable_primefield64, quotient_map_large_iint, quotient_map_large_uint,
20 quotient_map_small_int, tonelli_shanks_two_adic,
21};
22use p3_util::{branch_hint, flatten_to_base, gcd_inner};
23use rand::Rng;
24use rand::distr::{Distribution, StandardUniform};
25use serde::de::Error;
26use serde::{Deserialize, Deserializer, Serialize};
27
28pub(crate) const P: u64 = 0xFFFF_FFFF_0000_0001;
30
31#[derive(Copy, Clone, Default)]
35#[repr(transparent)] #[must_use]
37pub struct Goldilocks {
38 pub(crate) value: u64,
40}
41
42impl Serialize for Goldilocks {
43 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
44 let val = self.as_canonical_u64();
46 if serializer.is_human_readable() {
50 serializer.serialize_u64(val)
51 } else {
52 val.to_le_bytes().serialize(serializer)
53 }
54 }
55}
56
57impl<'de> Deserialize<'de> for Goldilocks {
58 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
59 let human_readable = d.is_human_readable();
60 let val = if human_readable {
61 u64::deserialize(d)?
62 } else {
63 u64::from_le_bytes(<[u8; 8]>::deserialize(d)?)
64 };
65 if val < P {
67 Ok(Self::new(val))
68 } else {
69 Err(D::Error::custom("Goldilocks value is out of range"))
70 }
71 }
72}
73
74impl Goldilocks {
75 #[inline]
80 pub const fn new(value: u64) -> Self {
81 Self { value }
82 }
83
84 #[inline]
88 pub const fn new_array<const N: usize>(input: [u64; N]) -> [Self; N] {
89 let mut output = [Self::ZERO; N];
90 let mut i = 0;
91 while i < N {
92 output[i].value = input[i];
93 i += 1;
94 }
95 output
96 }
97
98 #[inline]
102 pub const fn new_2d_array<const N: usize, const M: usize>(
103 input: [[u64; N]; M],
104 ) -> [[Self; N]; M] {
105 let mut output = [[Self::ZERO; N]; M];
106 let mut i = 0;
107 while i < M {
108 output[i] = Self::new_array(input[i]);
109 i += 1;
110 }
111 output
112 }
113
114 const NEG_ORDER: u64 = Self::ORDER_U64.wrapping_neg();
116
117 pub const TWO_ADIC_GENERATORS: [Self; 33] = Self::new_array([
121 0x0000000000000001,
122 0xffffffff00000000,
123 0x0001000000000000,
124 0xfffffffeff000001,
125 0xefffffff00000001,
126 0x00003fffffffc000,
127 0x0000008000000000,
128 0xf80007ff08000001,
129 0xbf79143ce60ca966,
130 0x1905d02a5c411f4e,
131 0x9d8f2ad78bfed972,
132 0x0653b4801da1c8cf,
133 0xf2c35199959dfcb6,
134 0x1544ef2335d17997,
135 0xe0ee099310bba1e2,
136 0xf6b2cffe2306baac,
137 0x54df9630bf79450e,
138 0xabd0a6e8aa3d8a0e,
139 0x81281a7b05f9beac,
140 0xfbd41c6b8caa3302,
141 0x30ba2ecd5e93e76d,
142 0xf502aef532322654,
143 0x4b2a18ade67246b5,
144 0xea9d5a1336fbc98b,
145 0x86cdcc31c307e171,
146 0x4bbaf5976ecfefd8,
147 0xed41d05b78d6e286,
148 0x10d78dd8915a171d,
149 0x59049500004a4485,
150 0xdfa8c93ba46d2666,
151 0x7e9bd009b86a0845,
152 0x400a7f755588e659,
153 0x185629dcda58878c,
154 ]);
155
156 const POWERS_OF_TWO: [Self; 96] = {
161 let mut powers_of_two = [Self::ONE; 96];
162
163 let mut i = 1;
164 while i < 64 {
165 powers_of_two[i] = Self::new(1 << i);
166 i += 1;
167 }
168 let mut var = Self::new(1 << 63);
169 while i < 96 {
170 var = const_add(var, var);
171 powers_of_two[i] = var;
172 i += 1;
173 }
174 powers_of_two
175 };
176}
177
178impl PartialEq for Goldilocks {
179 fn eq(&self, other: &Self) -> bool {
180 self.as_canonical_u64() == other.as_canonical_u64()
181 }
182}
183
184impl Eq for Goldilocks {}
185
186impl Packable for Goldilocks {}
187
188impl Hash for Goldilocks {
189 fn hash<H: Hasher>(&self, state: &mut H) {
190 state.write_u64(self.as_canonical_u64());
191 }
192}
193
194impl Ord for Goldilocks {
195 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
196 self.as_canonical_u64().cmp(&other.as_canonical_u64())
197 }
198}
199
200impl PartialOrd for Goldilocks {
201 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
202 Some(self.cmp(other))
203 }
204}
205
206impl Display for Goldilocks {
207 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
208 Display::fmt(&self.as_canonical_u64(), f)
209 }
210}
211
212impl Debug for Goldilocks {
213 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
214 Debug::fmt(&self.as_canonical_u64(), f)
215 }
216}
217
218impl Distribution<Goldilocks> for StandardUniform {
219 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Goldilocks {
220 loop {
221 let next_u64 = rng.next_u64();
222 let is_canonical = next_u64 < Goldilocks::ORDER_U64;
223 if is_canonical {
224 return Goldilocks::new(next_u64);
225 }
226 }
227 }
228}
229
230impl UniformSamplingField for Goldilocks {
231 const MAX_SINGLE_SAMPLE_BITS: usize = 32;
232 const SAMPLING_BITS_M: [u64; 64] = {
233 let prime: u64 = P;
234 let mut a = [0u64; 64];
235 let mut k = 0;
236 while k < 64 {
237 if k == 0 {
238 a[k] = prime; } else {
240 let mask = !((1u64 << k) - 1);
242 a[k] = prime & mask;
243 }
244 k += 1;
245 }
246 a
247 };
248}
249
250impl PrimeCharacteristicRing for Goldilocks {
251 type PrimeSubfield = Self;
252
253 const ZERO: Self = Self::new(0);
254 const ONE: Self = Self::new(1);
255 const TWO: Self = Self::new(2);
256 const NEG_ONE: Self = Self::new(Self::ORDER_U64 - 1);
257
258 #[inline]
259 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
260 f
261 }
262
263 #[inline]
264 fn from_bool(b: bool) -> Self {
265 Self::new(b.into())
266 }
267
268 #[inline]
269 fn halve(&self) -> Self {
270 const HALF_P_PLUS_1: u64 = (P + 1) >> 1; let lo_bit = self.value & 1;
275 let half = self.value >> 1;
276 let mask = 0u64.wrapping_sub(lo_bit); Self::new(half.wrapping_add(mask & HALF_P_PLUS_1))
278 }
279
280 #[inline]
281 fn mul_2exp_u64(&self, exp: u64) -> Self {
282 match exp {
284 0 => *self,
285 1 => *self + *self,
286 _ => {
287 if exp < 96 {
288 *self * Self::POWERS_OF_TWO[exp as usize]
289 } else if exp < 192 {
290 -*self * Self::POWERS_OF_TWO[(exp - 96) as usize]
291 } else {
292 self.mul_2exp_u64(exp % 192)
293 }
294 }
295 }
296 }
297
298 #[inline]
299 fn div_2exp_u64(&self, mut exp: u64) -> Self {
300 exp %= 192;
303 match exp {
304 0 => *self,
305 1 => self.halve(),
306 _ => self.mul_2exp_u64(192 - exp),
307 }
308 }
309
310 #[inline]
311 fn sum_array<const N: usize>(input: &[Self]) -> Self {
312 assert_eq!(N, input.len());
313 match N {
317 0 => Self::ZERO,
318 1 => input[0],
319 2 => input[0] + input[1],
320 3 => input[0] + input[1] + input[2],
321 _ => input.iter().copied().sum(),
322 }
323 }
324
325 #[inline]
326 fn dot_product<const N: usize>(lhs: &[Self; N], rhs: &[Self; N]) -> Self {
327 const OFFSET: u128 = ((P as u128) << 64) - (P as u128) + ((P as u128) << 32);
331 const {
332 assert!((N as u32) <= (1 << 31));
333 }
334 match N {
335 0 => Self::ZERO,
336 1 => lhs[0] * rhs[0],
337 2 => {
338 let long_prod_0 = (lhs[0].value as u128) * (rhs[0].value as u128);
341 let long_prod_1 = (lhs[1].value as u128) * (rhs[1].value as u128);
342
343 let (sum, over) = long_prod_0.overflowing_add(long_prod_1);
346 let sum_corr = sum.wrapping_sub(OFFSET);
348 if over {
349 reduce128(sum_corr)
350 } else {
351 reduce128(sum)
352 }
353 }
354 _ => {
355 let (lo_plus_hi, hi) = lhs
356 .iter()
357 .zip(rhs)
358 .map(|(x, y)| (x.value as u128) * (y.value as u128))
359 .fold((0_u128, 0_u64), |(acc_lo, acc_hi), val| {
360 let val_hi = (val >> 96) as u64;
362 unsafe { (acc_lo.wrapping_add(val), acc_hi.unchecked_add(val_hi)) }
365 });
366 let lo = lo_plus_hi.wrapping_sub((hi as u128) << 96);
368 let sum = unsafe { lo.unchecked_add(P.unchecked_sub(hi) as u128) };
371 reduce128(sum)
372 }
373 }
374 }
375
376 #[inline]
377 fn zero_vec(len: usize) -> Vec<Self> {
378 unsafe { flatten_to_base(vec![0u64; len]) }
383 }
384}
385
386impl InjectiveMonomial<7> for Goldilocks {}
390
391impl PermutationMonomial<7> for Goldilocks {
392 fn injective_exp_root_n(&self) -> Self {
396 exp_10540996611094048183(*self)
397 }
398}
399
400impl RawDataSerializable for Goldilocks {
401 impl_raw_serializable_primefield64!();
402}
403
404impl Field for Goldilocks {
405 #[cfg(all(
406 target_arch = "x86_64",
407 target_feature = "avx2",
408 not(target_feature = "avx512f")
409 ))]
410 type Packing = crate::PackedGoldilocksAVX2;
411
412 #[cfg(all(target_arch = "x86_64", target_feature = "avx512f"))]
413 type Packing = crate::PackedGoldilocksAVX512;
414
415 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
416 type Packing = crate::PackedGoldilocksNeon;
417
418 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
419 type Packing = crate::PackedGoldilocksWasmSimd128;
420
421 #[cfg(not(any(
422 all(
423 target_arch = "x86_64",
424 target_feature = "avx2",
425 not(target_feature = "avx512f")
426 ),
427 all(target_arch = "x86_64", target_feature = "avx512f"),
428 all(target_arch = "aarch64", target_feature = "neon"),
429 all(target_arch = "wasm32", target_feature = "simd128"),
430 )))]
431 type Packing = Self;
432
433 const GENERATOR: Self = Self::new(7);
435
436 const BENEFITS_FROM_LOCKSTEP_EVALUATION: bool = true;
438
439 fn is_zero(&self) -> bool {
440 self.value == 0 || self.value == Self::ORDER_U64
441 }
442
443 fn try_inverse(&self) -> Option<Self> {
444 if self.is_zero() {
445 return None;
446 }
447
448 Some(gcd_inversion(*self))
449 }
450
451 #[inline]
452 fn order() -> BigUint {
453 P.into()
454 }
455
456 #[inline]
457 fn try_sqrt(&self) -> Option<Self> {
458 tonelli_shanks_two_adic(*self)
459 }
460}
461
462quotient_map_small_int!(Goldilocks, u64, [u8, u16, u32]);
464quotient_map_small_int!(Goldilocks, i64, [i8, i16, i32]);
465quotient_map_large_uint!(
466 Goldilocks,
467 u64,
468 Goldilocks::ORDER_U64,
469 "`[0, 2^64 - 2^32]`",
470 "`[0, 2^64 - 1]`",
471 [u128]
472);
473quotient_map_large_iint!(
474 Goldilocks,
475 i64,
476 "`[-(2^63 - 2^31), 2^63 - 2^31]`",
477 "`[1 + 2^32 - 2^64, 2^64 - 1]`",
478 [(i128, u128)]
479);
480
481impl QuotientMap<u64> for Goldilocks {
482 #[inline]
487 fn from_int(int: u64) -> Self {
488 Self::new(int)
489 }
490
491 #[inline]
495 fn from_canonical_checked(int: u64) -> Option<Self> {
496 (int < Self::ORDER_U64).then(|| Self::new(int))
497 }
498
499 #[inline(always)]
505 unsafe fn from_canonical_unchecked(int: u64) -> Self {
506 Self::new(int)
507 }
508}
509
510impl QuotientMap<i64> for Goldilocks {
511 #[inline]
515 fn from_int(int: i64) -> Self {
516 if int >= 0 {
517 Self::new(int as u64)
518 } else {
519 Self::new(Self::ORDER_U64.wrapping_add_signed(int))
520 }
521 }
522
523 #[inline]
527 fn from_canonical_checked(int: i64) -> Option<Self> {
528 const POS_BOUND: i64 = (P >> 1) as i64;
529 const NEG_BOUND: i64 = -POS_BOUND;
530 match int {
531 0..=POS_BOUND => Some(Self::new(int as u64)),
532 NEG_BOUND..0 => Some(Self::new(Self::ORDER_U64.wrapping_add_signed(int))),
533 _ => None,
534 }
535 }
536
537 #[inline(always)]
543 unsafe fn from_canonical_unchecked(int: i64) -> Self {
544 Self::from_int(int)
545 }
546}
547
548impl PrimeField for Goldilocks {
549 fn as_canonical_biguint(&self) -> BigUint {
550 self.as_canonical_u64().into()
551 }
552}
553
554impl PrimeField64 for Goldilocks {
555 const ORDER_U64: u64 = P;
556
557 #[inline]
558 fn as_canonical_u64(&self) -> u64 {
559 let mut c = self.value;
560 if c >= Self::ORDER_U64 {
562 c -= Self::ORDER_U64;
563 }
564 c
565 }
566}
567
568impl TwoAdicField for Goldilocks {
569 const TWO_ADICITY: usize = 32;
570
571 fn two_adic_generator(bits: usize) -> Self {
572 assert!(bits <= Self::TWO_ADICITY);
573 Self::TWO_ADIC_GENERATORS[bits]
574 }
575}
576
577#[inline]
582const fn const_add(lhs: Goldilocks, rhs: Goldilocks) -> Goldilocks {
583 let (sum, over) = lhs.value.overflowing_add(rhs.value);
584 let (mut sum, over) = sum.overflowing_add((over as u64) * Goldilocks::NEG_ORDER);
585 if over {
586 sum += Goldilocks::NEG_ORDER;
587 }
588 Goldilocks::new(sum)
589}
590
591impl Add for Goldilocks {
592 type Output = Self;
593
594 #[inline]
595 fn add(self, rhs: Self) -> Self {
596 let (sum, over) = self.value.overflowing_add(rhs.value);
597 let (mut sum, over) = sum.overflowing_add(u64::from(over) * Self::NEG_ORDER);
598 if over {
599 unsafe {
607 assert_unchecked(self.value > Self::ORDER_U64 && rhs.value > Self::ORDER_U64);
608 }
609 branch_hint();
610 sum += Self::NEG_ORDER; }
612 Self::new(sum)
613 }
614}
615
616impl Sub for Goldilocks {
617 type Output = Self;
618
619 #[inline]
620 fn sub(self, rhs: Self) -> Self {
621 let (diff, under) = self.value.overflowing_sub(rhs.value);
622 let (mut diff, under) = diff.overflowing_sub(u64::from(under) * Self::NEG_ORDER);
623 if under {
624 unsafe {
632 assert_unchecked(self.value < Self::NEG_ORDER - 1 && rhs.value > Self::ORDER_U64);
633 }
634 branch_hint();
635 diff -= Self::NEG_ORDER; }
637 Self::new(diff)
638 }
639}
640
641impl Neg for Goldilocks {
642 type Output = Self;
643
644 #[inline]
645 fn neg(self) -> Self::Output {
646 Self::new(Self::ORDER_U64 - self.as_canonical_u64())
647 }
648}
649
650impl Mul for Goldilocks {
651 type Output = Self;
652
653 #[inline]
654 fn mul(self, rhs: Self) -> Self {
655 reduce128(u128::from(self.value) * u128::from(rhs.value))
656 }
657}
658
659impl_add_assign!(Goldilocks);
660impl_sub_assign!(Goldilocks);
661impl_mul_methods!(Goldilocks);
662impl_div_methods!(Goldilocks, Goldilocks);
663
664impl Sum for Goldilocks {
665 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
666 let sum = iter.map(|x| x.value as u128).sum::<u128>();
670 reduce128(sum)
671 }
672}
673
674#[inline]
677pub(crate) fn reduce128(x: u128) -> Goldilocks {
678 let (x_lo, x_hi) = split(x); let x_hi_hi = x_hi >> 32;
680 let x_hi_lo = x_hi & Goldilocks::NEG_ORDER;
681
682 let (mut t0, borrow) = x_lo.overflowing_sub(x_hi_hi);
683 if borrow {
684 branch_hint(); t0 -= Goldilocks::NEG_ORDER; }
687 let t1 = x_hi_lo * Goldilocks::NEG_ORDER;
688 let t2 = unsafe { add_no_canonicalize_trashing_input(t0, t1) };
689 Goldilocks::new(t2)
690}
691
692#[inline]
693#[allow(clippy::cast_possible_truncation)]
694const fn split(x: u128) -> (u64, u64) {
695 (x as u64, (x >> 64) as u64)
696}
697
698#[inline(always)]
704#[cfg(target_arch = "x86_64")]
705unsafe fn add_no_canonicalize_trashing_input(x: u64, y: u64) -> u64 {
706 unsafe {
707 let res_wrapped: u64;
708 let adjustment: u64;
709 core::arch::asm!(
710 "add {0}, {1}",
711 "sbb {1:e}, {1:e}",
720 inlateout(reg) x => res_wrapped,
721 inlateout(reg) y => adjustment,
722 options(pure, nomem, nostack),
723 );
724 assert_unchecked(x != 0 || (res_wrapped == y && adjustment == 0));
725 assert_unchecked(y != 0 || (res_wrapped == x && adjustment == 0));
726 res_wrapped + adjustment
729 }
730}
731
732#[inline(always)]
733#[cfg(not(target_arch = "x86_64"))]
734unsafe fn add_no_canonicalize_trashing_input(x: u64, y: u64) -> u64 {
735 let (res_wrapped, carry) = x.overflowing_add(y);
736 res_wrapped + Goldilocks::NEG_ORDER * u64::from(carry)
738}
739
740fn gcd_inversion(input: Goldilocks) -> Goldilocks {
749 let (mut a, mut b) = (input.value, P);
751
752 const ROUND_SIZE: usize = 63;
756
757 let (f00, _, f10, _) = gcd_inner::<ROUND_SIZE>(&mut a, &mut b);
761 let (_, _, f11, g11) = gcd_inner::<ROUND_SIZE>(&mut a, &mut b);
762
763 let u = from_unusual_int(f00);
766 let v = from_unusual_int(f10);
767 let u_fac11 = from_unusual_int(f11);
768 let v_fac11 = from_unusual_int(g11);
769
770 (u * u_fac11 + v * v_fac11).mul_2exp_u64(66)
773}
774
775const fn from_unusual_int(int: i64) -> Goldilocks {
777 if (int >= 0) || (int == i64::MIN) {
778 Goldilocks::new(int as u64)
779 } else {
780 Goldilocks::new(Goldilocks::ORDER_U64.wrapping_add_signed(int))
781 }
782}
783
784#[cfg(test)]
785mod tests {
786 use p3_field::extension::BinomialExtensionField;
787 use p3_field_testing::{
788 test_field, test_field_dft, test_prime_field, test_prime_field_64, test_two_adic_field,
789 };
790
791 use super::*;
792
793 type F = Goldilocks;
794 type EF = BinomialExtensionField<F, 5>;
795
796 #[test]
797 fn deserialize_rejects_non_canonical_encodings() {
798 for non_canonical in [P, P + 5, u64::MAX] {
802 let json = serde_json::to_string(&non_canonical).unwrap();
803 assert!(serde_json::from_str::<F>(&json).is_err());
804 }
805
806 let max_canonical_json = serde_json::to_string(&(P - 1)).unwrap();
808 let max_canonical: F = serde_json::from_str(&max_canonical_json).unwrap();
809 assert_eq!(max_canonical.as_canonical_u64(), P - 1);
810 }
811
812 #[test]
813 fn serialize_is_canonical() {
814 let non_canonical = F::new(P + 5);
818 let json = serde_json::to_string(&non_canonical).unwrap();
819 assert_eq!(json, "5");
820
821 let roundtrip: F = serde_json::from_str(&json).unwrap();
823 assert_eq!(roundtrip, non_canonical);
824 }
825
826 #[test]
827 fn test_goldilocks() {
828 let f = F::new(100);
829 assert_eq!(f.as_canonical_u64(), 100);
830
831 let f = F::new(u64::MAX);
836 assert_eq!(f.as_canonical_u64(), u32::MAX as u64 - 1);
837
838 let f = F::from_u64(u64::MAX);
839 assert_eq!(f.as_canonical_u64(), u32::MAX as u64 - 1);
840
841 let expected_multiplicative_group_generator = F::new(7);
843 assert_eq!(F::GENERATOR, expected_multiplicative_group_generator);
844 assert_eq!(F::GENERATOR.as_canonical_u64(), 7_u64);
845
846 let x = u128::MAX;
848 let y = reduce128(x);
849 let expected_result = -F::TWO.exp_power_of_2(5) - F::ONE;
858 assert_eq!(y, expected_result);
859
860 let f = F::new(100);
861 assert_eq!(f.injective_exp_n().injective_exp_root_n(), f);
862 assert_eq!(y.injective_exp_n().injective_exp_root_n(), y);
863 assert_eq!(F::TWO.injective_exp_n().injective_exp_root_n(), F::TWO);
864 }
865
866 const ZEROS: [Goldilocks; 2] = [Goldilocks::ZERO, Goldilocks::new(P)];
868 const ONES: [Goldilocks; 2] = [Goldilocks::ONE, Goldilocks::new(P + 1)];
869
870 fn multiplicative_group_prime_factorization() -> [(BigUint, u32); 6] {
873 [
874 (BigUint::from(2u8), 32),
875 (BigUint::from(3u8), 1),
876 (BigUint::from(5u8), 1),
877 (BigUint::from(17u8), 1),
878 (BigUint::from(257u16), 1),
879 (BigUint::from(65537u32), 1),
880 ]
881 }
882
883 test_field!(
884 crate::Goldilocks,
885 &super::ZEROS,
886 &super::ONES,
887 &super::multiplicative_group_prime_factorization()
888 );
889 test_prime_field!(crate::Goldilocks);
890 test_prime_field_64!(crate::Goldilocks, &super::ZEROS, &super::ONES);
891 test_two_adic_field!(crate::Goldilocks);
892
893 test_field_dft!(
894 radix2dit,
895 crate::Goldilocks,
896 super::EF,
897 p3_dft::Radix2Dit<_>
898 );
899 test_field_dft!(bowers, crate::Goldilocks, super::EF, p3_dft::Radix2Bowers);
900 test_field_dft!(
901 parallel,
902 crate::Goldilocks,
903 super::EF,
904 p3_dft::Radix2DitParallel<crate::Goldilocks>
905 );
906}