Skip to main content

p3_goldilocks/
extension.rs

1use p3_field::extension::{
2    Binomial, BinomiallyExtendable, CubicTrinomial, CubicTrinomialExtendable, ExtensionAlgebra,
3    HasTwoAdicBinomialExtension, HasTwoAdicCubicExtension, binomial_mul, binomial_square,
4    cubic_square, trinomial_cubic_mul,
5};
6use p3_field::{PrimeCharacteristicRing, TwoAdicField, field_to_array};
7
8use crate::Goldilocks;
9
10impl ExtensionAlgebra<Self, 2, Binomial<Self>> for Goldilocks {
11    #[inline]
12    fn ext_mul(a: &[Self; 2], b: &[Self; 2], res: &mut [Self; 2]) {
13        binomial_mul::<Self, Self, Self, 2>(a, b, res, <Self as BinomiallyExtendable<2>>::W);
14    }
15
16    #[inline]
17    fn ext_square(a: &[Self; 2], res: &mut [Self; 2]) {
18        binomial_square::<Self, Self, 2>(a, res, <Self as BinomiallyExtendable<2>>::W);
19    }
20}
21
22impl BinomiallyExtendable<2> for Goldilocks {
23    fn binomial_algebra_id() -> alloc::vec::Vec<u8> {
24        use p3_field::PrimeField64;
25        alloc::format!(
26            "p3-power-basis-v1:X^2-{}",
27            <Self as BinomiallyExtendable<2>>::W.as_canonical_u64()
28        )
29        .into_bytes()
30    }
31
32    // Verifiable in Sage with
33    // `R.<x> = GF(p)[]; assert (x^2 - 7).is_irreducible()`.
34    const W: Self = Self::new(7);
35
36    // DTH_ROOT = W^((p - 1)/2).
37    const DTH_ROOT: Self = Self::new(18446744069414584320);
38
39    const EXT_GENERATOR: [Self; 2] = [
40        Self::new(18081566051660590251),
41        Self::new(16121475356294670766),
42    ];
43}
44
45impl HasTwoAdicBinomialExtension<2> for Goldilocks {
46    const EXT_TWO_ADICITY: usize = 33;
47
48    fn ext_two_adic_generator(bits: usize) -> [Self; 2] {
49        assert!(bits <= 33);
50
51        if bits == 33 {
52            [Self::ZERO, Self::new(15659105665374529263)]
53        } else {
54            [Self::two_adic_generator(bits), Self::ZERO]
55        }
56    }
57}
58
59impl ExtensionAlgebra<Self, 3, CubicTrinomial> for Goldilocks {
60    #[inline]
61    fn ext_mul(a: &[Self; 3], b: &[Self; 3], res: &mut [Self; 3]) {
62        trinomial_cubic_mul::<Self>(a, b, res);
63    }
64
65    #[inline]
66    fn ext_square(a: &[Self; 3], res: &mut [Self; 3]) {
67        cubic_square::<Self>(a, res);
68    }
69}
70
71impl CubicTrinomialExtendable for Goldilocks {
72    // Verifiable via:
73    // ```sage
74    // p = 2**64 - 2**32 + 1
75    // R.<x> = GF(p)[]
76    // assert (x^3 - x - 1).is_irreducible()
77    // ```
78    const FROBENIUS_MATRIX: [[Self; 3]; 3] = [
79        [
80            Self::ONE,
81            Self::new(10615703402128488253),
82            Self::new(6700183068485440220),
83        ],
84        [
85            Self::ZERO,
86            Self::new(10050274602728160328),
87            Self::new(14531223735771536287),
88        ],
89        [
90            Self::ZERO,
91            Self::new(11746561000929144102),
92            Self::new(8396469466686423992),
93        ],
94    ];
95
96    // Verifiable via:
97    // ```sage
98    // p = 2**64 - 2**32 + 1
99    // F = GF(p)
100    // R.<x> = F[]
101    // K.<a> = F.extension(x^3 - x - 1)
102    // g = 2 + a
103    // order = p^3 - 1
104    // assert g.multiplicative_order() == order
105    // ```
106    const EXT_GENERATOR: [Self; 3] = [Self::TWO, Self::ONE, Self::ZERO];
107}
108
109impl HasTwoAdicCubicExtension for Goldilocks {
110    const EXT_TWO_ADICITY: usize = 32;
111
112    fn ext_two_adic_generator(bits: usize) -> [Self; 3] {
113        assert!(bits <= 32);
114
115        field_to_array(Self::two_adic_generator(bits))
116    }
117}
118
119impl ExtensionAlgebra<Self, 5, Binomial<Self>> for Goldilocks {
120    #[inline]
121    fn ext_mul(a: &[Self; 5], b: &[Self; 5], res: &mut [Self; 5]) {
122        binomial_mul::<Self, Self, Self, 5>(a, b, res, <Self as BinomiallyExtendable<5>>::W);
123    }
124
125    #[inline]
126    fn ext_square(a: &[Self; 5], res: &mut [Self; 5]) {
127        binomial_square::<Self, Self, 5>(a, res, <Self as BinomiallyExtendable<5>>::W);
128    }
129}
130
131impl BinomiallyExtendable<5> for Goldilocks {
132    fn binomial_algebra_id() -> alloc::vec::Vec<u8> {
133        use p3_field::PrimeField64;
134        alloc::format!(
135            "p3-power-basis-v1:X^5-{}",
136            <Self as BinomiallyExtendable<5>>::W.as_canonical_u64()
137        )
138        .into_bytes()
139    }
140
141    // Verifiable via:
142    //  ```sage
143    //  # Define Fp
144    //  p = 2**64 - 2**32 + 1
145    //  F = GF(p)
146
147    //  # Define Fp[z]
148    //  R.<z> = PolynomialRing(F)
149
150    //  # The polynomial x^5-3 is irreducible
151    //  assert(R(z^5-3).is_irreducible())
152    //  ```
153    const W: Self = Self::new(3);
154
155    // 5-th root = w^((p - 1)/5)
156    const DTH_ROOT: Self = Self::new(1041288259238279555);
157
158    // Generator of the extension field
159    // Obtained by finding the smallest Hamming weight vector
160    // with appropriate order, starting at [0,1,0,0,0]
161    const EXT_GENERATOR: [Self; 5] = [Self::TWO, Self::ONE, Self::ZERO, Self::ZERO, Self::ZERO];
162}
163
164impl HasTwoAdicBinomialExtension<5> for Goldilocks {
165    const EXT_TWO_ADICITY: usize = 32;
166
167    fn ext_two_adic_generator(bits: usize) -> [Self; 5] {
168        assert!(bits <= 32);
169
170        field_to_array(Self::two_adic_generator(bits))
171    }
172}
173
174#[cfg(test)]
175mod test_quadratic_extension {
176
177    use num_bigint::BigUint;
178    use p3_field::extension::BinomialExtensionField;
179    use p3_field::{ExtensionField, PrimeCharacteristicRing};
180    use p3_field_testing::{
181        test_extension_field, test_field, test_packed_extension_field,
182        test_two_adic_extension_field,
183    };
184
185    use crate::Goldilocks;
186
187    type F = Goldilocks;
188    type EF = BinomialExtensionField<F, 2>;
189
190    // There is a redundant representation of zero but we already tested it
191    // when testing the base field.
192    const ZEROS: [EF; 1] = [EF::ZERO];
193    const ONES: [EF; 1] = [EF::ONE];
194
195    // Get the prime factorization of the order of the multiplicative group.
196    // i.e. the prime factorization of P^2 - 1.
197    fn multiplicative_group_prime_factorization() -> [(BigUint, u32); 9] {
198        [
199            (BigUint::from(2u8), 33),
200            (BigUint::from(3u8), 1),
201            (BigUint::from(5u8), 1),
202            (BigUint::from(7u8), 1),
203            (BigUint::from(17u8), 1),
204            (BigUint::from(179u8), 1),
205            (BigUint::from(257u16), 1),
206            (BigUint::from(65537u32), 1),
207            (BigUint::from(7361031152998637u64), 1),
208        ]
209    }
210
211    test_field!(
212        super::EF,
213        &super::ZEROS,
214        &super::ONES,
215        &super::multiplicative_group_prime_factorization()
216    );
217
218    test_extension_field!(super::F, super::EF);
219    test_two_adic_extension_field!(super::F, super::EF);
220
221    type Pef = <EF as ExtensionField<F>>::ExtensionPacking;
222    const PACKED_ZEROS: [Pef; 1] = [Pef::ZERO];
223    const PACKED_ONES: [Pef; 1] = [Pef::ONE];
224    test_packed_extension_field!(
225        super::F,
226        super::EF,
227        super::Pef,
228        &super::PACKED_ZEROS,
229        &super::PACKED_ONES
230    );
231    p3_field_testing::test_packed_binomial_extension_division!(F, 2);
232}
233
234#[cfg(test)]
235mod test_cubic_trinomial_extension {
236
237    use num_bigint::BigUint;
238    use p3_field::extension::{CubicTrinomialExtensionField, HasFrobenius};
239    use p3_field::{ExtensionField, PrimeCharacteristicRing};
240    use p3_field_testing::{
241        test_extension_field, test_field, test_frobenius, test_packed_extension_field,
242        test_two_adic_extension_field,
243    };
244    use rand::rngs::SmallRng;
245    use rand::{RngExt, SeedableRng};
246
247    use crate::Goldilocks;
248
249    type F = Goldilocks;
250    type EF = CubicTrinomialExtensionField<F>;
251
252    const ZEROS: [EF; 1] = [EF::ZERO];
253    const ONES: [EF; 1] = [EF::ONE];
254
255    fn multiplicative_group_prime_factorization() -> [(BigUint, u32); 9] {
256        [
257            (BigUint::from(2u8), 32),
258            (BigUint::from(3u8), 2),
259            (BigUint::from(5u8), 1),
260            (BigUint::from(17u8), 1),
261            (BigUint::from(257u16), 1),
262            (BigUint::from(937u16), 1),
263            (BigUint::from(65537u32), 1),
264            (BigUint::from(724723u32), 1),
265            (BigUint::from(167034643597991036904547663171u128), 1),
266        ]
267    }
268
269    // TODO: Consider generalizing and putting into test_extension_field!
270    #[test]
271    fn test_defining_relation() {
272        let x = EF::new([F::ZERO, F::ONE, F::ZERO]);
273        let x_cubed = x * x * x;
274        let x_plus_one = x + EF::ONE;
275        assert_eq!(x_cubed, x_plus_one, "X^3 should equal X + 1");
276    }
277
278    #[test]
279    fn test_frobenius_matches_exponentiation_oracle() {
280        const P: u64 = 0xFFFF_FFFF_0000_0001;
281
282        let edge_values = [0, 1, P - 1, P, P + 1, u64::MAX];
283        for a0 in edge_values {
284            for a1 in edge_values {
285                for a2 in edge_values {
286                    let x = EF::new([F::new(a0), F::new(a1), F::new(a2)]);
287                    assert_eq!(x.frobenius(), x.exp_u64(P), "x = {x:?}");
288                }
289            }
290        }
291
292        let mut rng = SmallRng::seed_from_u64(0x0F0B_31A5);
293        for _ in 0..128 {
294            let x = EF::new([
295                F::new(rng.random()),
296                F::new(rng.random()),
297                F::new(rng.random()),
298            ]);
299            assert_eq!(x.frobenius(), x.exp_u64(P), "x = {x:?}");
300        }
301    }
302
303    test_field!(
304        super::EF,
305        &super::ZEROS,
306        &super::ONES,
307        &super::multiplicative_group_prime_factorization()
308    );
309
310    test_extension_field!(super::F, super::EF);
311    test_two_adic_extension_field!(super::F, super::EF);
312    test_frobenius!(super::F, super::EF);
313
314    type Pef = <EF as ExtensionField<F>>::ExtensionPacking;
315    const PACKED_ZEROS: [Pef; 1] = [Pef::ZERO];
316    const PACKED_ONES: [Pef; 1] = [Pef::ONE];
317    test_packed_extension_field!(
318        super::F,
319        super::EF,
320        super::Pef,
321        &super::PACKED_ZEROS,
322        &super::PACKED_ONES
323    );
324}
325
326#[cfg(test)]
327mod test_quintic_extension {
328
329    use num_bigint::BigUint;
330    use p3_field::extension::BinomialExtensionField;
331    use p3_field::{ExtensionField, PrimeCharacteristicRing};
332    use p3_field_testing::{
333        test_extension_field, test_field, test_packed_extension_field,
334        test_two_adic_extension_field,
335    };
336
337    use crate::Goldilocks;
338
339    type F = Goldilocks;
340    type EF = BinomialExtensionField<F, 5>;
341
342    // There is a redundant representation of zero but we already tested it
343    // when testing the base field.
344    const ZEROS: [EF; 1] = [EF::ZERO];
345    const ONES: [EF; 1] = [EF::ONE];
346
347    // Get the prime factorization of the order of the multiplicative group.
348    // i.e. the prime factorization of P^5 - 1.
349    fn multiplicative_group_prime_factorization() -> [(num_bigint::BigUint, u32); 10] {
350        [
351            (BigUint::from(2u8), 32),
352            (BigUint::from(3u8), 1),
353            (BigUint::from(5u8), 2),
354            (BigUint::from(17u8), 1),
355            (BigUint::from(257u16), 1),
356            (BigUint::from(45971u16), 1),
357            (BigUint::from(65537u32), 1),
358            (BigUint::from(255006435240067831u64), 1),
359            (BigUint::from(280083648770327405561u128), 1),
360            (BigUint::from(7053197395277272939628824863222181u128), 1),
361        ]
362    }
363
364    test_field!(
365        super::EF,
366        &super::ZEROS,
367        &super::ONES,
368        &super::multiplicative_group_prime_factorization()
369    );
370
371    test_extension_field!(super::F, super::EF);
372    test_two_adic_extension_field!(super::F, super::EF);
373
374    type Pef = <EF as ExtensionField<F>>::ExtensionPacking;
375    const PACKED_ZEROS: [Pef; 1] = [Pef::ZERO];
376    const PACKED_ONES: [Pef; 1] = [Pef::ONE];
377    test_packed_extension_field!(
378        super::F,
379        super::EF,
380        super::Pef,
381        &super::PACKED_ZEROS,
382        &super::PACKED_ONES
383    );
384    p3_field_testing::test_packed_binomial_extension_division!(F, 5);
385}