halo2curves_axiom/pluto_eris/fields/
fp.rs

1use crate::arithmetic::{adc, mac, sbb};
2use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup};
3use crate::{
4    extend_field_legendre, field_arithmetic_7_limbs, field_bits_7_limbs, field_common_7_limbs,
5    impl_from_u64_7_limbs,
6};
7use crate::{
8    impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
9    impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
10    impl_sum_prod,
11};
12use core::convert::TryInto;
13use core::fmt;
14use core::ops::{Add, Mul, Neg, Sub};
15use rand::RngCore;
16use std::slice::Iter;
17use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
18
19#[cfg(feature = "derive_serde")]
20use serde::{Deserialize, Serialize};
21
22/// This represents an element of $\mathbb{F}_p$ where
23///
24/// `p = 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd300000001`
25///
26/// is the base field of the Pluto curve.
27/// The internal representation of this type is seven 64-bit unsigned
28/// integers in little-endian order which account for the 446 bits required to be represented.
29/// `Fp` values are always in Montgomery form; i.e., Fp(a) = aR mod p, with R = 2^448.
30#[derive(Clone, Copy, PartialEq, Eq, Hash)]
31#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))]
32pub struct Fp(pub(crate) [u64; 7]);
33
34/// Size of `Fp` element in bytes
35const SIZE: usize = 56;
36
37/// Constant representing the modulus
38/// p = 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd300000001
39const MODULUS: Fp = Fp([
40    0x9ffffcd300000001,
41    0xa2a7e8c30006b945,
42    0xe4a7a5fe8fadffd6,
43    0x443f9a5cda8a6c7b,
44    0xa803ca76f439266f,
45    0x0130e0000d7f70e4,
46    0x2400000000002400,
47]);
48
49/// The modulus as u32 limbs.
50#[cfg(not(target_pointer_width = "64"))]
51const MODULUS_LIMBS_32: [u32; 14] = [
52    0x00000001, 0x9ffffcd3, 0x0006b945, 0xa2a7e8c3, 0x8fadffd6, 0xe4a7a5fe, 0xda8a6c7b, 0x443f9a5c,
53    0xf439266f, 0xa803ca76, 0x0d7f70e4, 0x0130e000, 0x00002400, 0x24000000,
54];
55
56// pub const NEGATIVE_ONE: Fp = Fp([]);
57
58pub(crate) const MODULUS_STR: &str = "0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd300000001";
59
60/// INV = -r^{-1} mod 2^64
61/// `0x9ffffcd2ffffffff`
62const INV: u64 = 0x9ffffcd2ffffffff;
63
64/// Let M be the power of `2^64` nearest to `Self::MODULUS_BITS`. Then `R = M % Self::MODULUS`.
65/// `R = 2^448 mod p`
66/// `0x3ffffffffff03fff7a9dfffa183e9bf67e576bf526ff2f52242c7760637089cbf6a760a123e01218d68a2aaffd0ef18a000163afffffff9`
67const R: Fp = Fp([
68    0xa000163afffffff9,
69    0x8d68a2aaffd0ef18,
70    0xbf6a760a123e0121,
71    0x2242c7760637089c,
72    0x67e576bf526ff2f5,
73    0xf7a9dfffa183e9bf,
74    0x03ffffffffff03ff,
75]);
76
77/// `R^2 = 2^896 mod p`
78/// `0x1a4b16581f66e3cc8bcb0f20758aec8520b6db3d7481a84c734fd363b575c23e7a42067a8ccd154b4b20c07277ae01f1d9702c6d54dc0598`
79const R2: Fp = Fp([
80    0xd9702c6d54dc0598,
81    0x4b20c07277ae01f1,
82    0x7a42067a8ccd154b,
83    0x734fd363b575c23e,
84    0x20b6db3d7481a84c,
85    0x8bcb0f20758aec85,
86    0x1a4b16581f66e3cc,
87]);
88
89/// `R^3 = 2^1792 mod p`
90/// `0x1f51e40a048ddc1789010189f4df0ae1f3bc57efac4b3280b25aa8b46a40b225e5446680e4c4ea0449937d6b40e58f05c67afa3fe916dd69`
91const R3: Fp = Fp([
92    0xc67afa3fe916dd69,
93    0x49937d6b40e58f05,
94    0xe5446680e4c4ea04,
95    0xb25aa8b46a40b225,
96    0xf3bc57efac4b3280,
97    0x89010189f4df0ae1,
98    0x1f51e40a048ddc17,
99]);
100
101/// `GENERATOR = 10 mod p` is a generator of the `p - 1` order multiplicative
102/// subgroup, or in other words a primitive root of the field.
103const GENERATOR: Fp = Fp::from_raw([0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
104
105/// Size of the 2-adic sub-group of the field.
106const S: u32 = 32;
107
108/// GENERATOR^t where t * 2^s + 1 = p
109/// with t odd. In other words, this
110/// is a 2^s root of unity.
111/// `0x2d39f8c5f9adb3f35fe3f4222db17451ddd9602a013af5276bdbe3903ec85fc889232f5c8bc6857060c75e6f399661d6c7b82d31d563091`
112const ROOT_OF_UNITY: Fp = Fp::from_raw([
113    0x6c7b82d31d563091,
114    0x060c75e6f399661d,
115    0x889232f5c8bc6857,
116    0x76bdbe3903ec85fc,
117    0x1ddd9602a013af52,
118    0x35fe3f4222db1745,
119    0x02d39f8c5f9adb3f,
120]);
121
122/// 1 / ROOT_OF_UNITY mod p
123/// `0x17725d635b00cda4153eb10c7105919d012822bd86c08691803272fbc5c9f8378055eb56ae2d55f9272bf208aad57f666deaead2c693ff66`
124const ROOT_OF_UNITY_INV: Fp = Fp::from_raw([
125    0x6deaead2c693ff66,
126    0x272bf208aad57f66,
127    0x8055eb56ae2d55f9,
128    0x803272fbc5c9f837,
129    0x012822bd86c08691,
130    0x153eb10c7105919d,
131    0x17725d635b00cda4,
132]);
133
134/// 1 / 2 mod p
135/// `0x12000000000012000098700006bfb8725401e53b7a1c9337a21fcd2e6d45363df253d2ff47d6ffeb5153f46180035ca2cffffe6980000001`
136pub(crate) const TWO_INV: Fp = Fp::from_raw([
137    0xcffffe6980000001,
138    0x5153f46180035ca2,
139    0xf253d2ff47d6ffeb,
140    0xa21fcd2e6d45363d,
141    0x5401e53b7a1c9337,
142    0x0098700006bfb872,
143    0x1200000000001200,
144]);
145/// GENERATOR^{2^s} where t * 2^s + 1 = r with t odd. In other words, this is a t root of unity.
146/// `0xeacefc6504d028d42ed23fc8766d5a5f195b456887e1e0021fb760c53233e9170c23749b459b95cc6cbb5faf3754a1e1916b2007775db04`
147const DELTA: Fp = Fp::from_raw([
148    0x1916b2007775db04,
149    0xc6cbb5faf3754a1e,
150    0x70c23749b459b95c,
151    0x21fb760c53233e91,
152    0xf195b456887e1e00,
153    0x42ed23fc8766d5a5,
154    0x0eacefc6504d028d,
155]);
156
157/// `ZETA^3 = 1 mod p` where `ZETA^2 != 1 mod p`
158/// `0x480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37ffffffe`
159const ZETA: Fp = Fp::from_raw([
160    0x100004c37ffffffe,
161    0xc8ad8b38dffaf50c,
162    0xc956d01c903d720d,
163    0x50000d7ee0e4a803,
164    0x00000000360001c9,
165    0x0000000000004800,
166    0x0000000000000000,
167]);
168
169/// NEG_ONE ; -1 mod p
170pub(crate) const NEG_ONE: Fp = Fp::from_raw([
171    0x9ffffcd300000000,
172    0xa2a7e8c30006b945,
173    0xe4a7a5fe8fadffd6,
174    0x443f9a5cda8a6c7b,
175    0xa803ca76f439266f,
176    0x0130e0000d7f70e4,
177    0x2400000000002400,
178]);
179
180impl_binops_additive!(Fp, Fp);
181impl_binops_multiplicative!(Fp, Fp);
182field_common_7_limbs!(
183    Fp,
184    FpRepr,
185    MODULUS,
186    INV,
187    MODULUS_STR,
188    TWO_INV,
189    ROOT_OF_UNITY_INV,
190    DELTA,
191    ZETA,
192    R,
193    R2,
194    R3
195);
196impl_sum_prod!(Fp);
197impl_from_u64_7_limbs!(Fp, R2);
198field_arithmetic_7_limbs!(Fp, MODULUS, INV, sparse);
199
200#[cfg(target_pointer_width = "64")]
201field_bits_7_limbs!(Fp, MODULUS);
202#[cfg(not(target_pointer_width = "64"))]
203field_bits_7_limbs!(Fp, MODULUS, MODULUS_LIMBS_32);
204
205extend_field_legendre!(Fp);
206
207impl Fp {
208    pub const fn size() -> usize {
209        SIZE
210    }
211}
212
213impl ff::Field for Fp {
214    const ZERO: Self = Self::zero();
215    const ONE: Self = Self::one();
216
217    fn random(mut rng: impl RngCore) -> Self {
218        Self::from_u512([
219            rng.next_u64(),
220            rng.next_u64(),
221            rng.next_u64(),
222            rng.next_u64(),
223            rng.next_u64(),
224            rng.next_u64(),
225            rng.next_u64(),
226            rng.next_u64(),
227        ])
228    }
229
230    fn double(&self) -> Self {
231        self.double()
232    }
233
234    #[inline(always)]
235    fn square(&self) -> Self {
236        self.square()
237    }
238
239    /// Computes the multiplicative inverse of this element,
240    /// failing if the element is zero.
241    fn invert(&self) -> CtOption<Self> {
242        // self^(p - 2)
243        let tmp = self.pow([
244            0x9ffffcd2ffffffff,
245            0xa2a7e8c30006b945,
246            0xe4a7a5fe8fadffd6,
247            0x443f9a5cda8a6c7b,
248            0xa803ca76f439266f,
249            0x0130e0000d7f70e4,
250            0x2400000000002400,
251        ]);
252
253        CtOption::new(tmp, !self.ct_eq(&Self::zero()))
254    }
255
256    fn sqrt(&self) -> CtOption<Self> {
257        /// `(t - 1) // 2` where t * 2^s + 1 = p with t odd.
258        const T_MINUS1_OVER2: [u64; 7] = [
259            0x80035ca2cffffe69,
260            0x47d6ffeb5153f461,
261            0x6d45363df253d2ff,
262            0x7a1c9337a21fcd2e,
263            0x06bfb8725401e53b,
264            0x0000120000987000,
265            0x0000000012000000,
266        ];
267        ff::helpers::sqrt_tonelli_shanks(self, T_MINUS1_OVER2)
268    }
269
270    fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
271        ff::helpers::sqrt_ratio_generic(num, div)
272    }
273}
274
275#[derive(Clone, Copy, Debug)]
276/// Canonical little-endian representation of a `Fp` element.
277pub struct FpRepr {
278    pub repr: [u8; SIZE],
279}
280
281impl FpRepr {
282    /// Returns an iterator over the bytes of the canoncial representation of the element.
283    pub fn iter(&self) -> Iter<'_, u8> {
284        self.repr.iter()
285    }
286}
287
288impl Default for FpRepr {
289    fn default() -> Self {
290        FpRepr { repr: [0u8; SIZE] }
291    }
292}
293
294impl AsRef<[u8]> for FpRepr {
295    fn as_ref(&self) -> &[u8] {
296        self.repr.as_ref()
297    }
298}
299
300impl AsMut<[u8]> for FpRepr {
301    fn as_mut(&mut self) -> &mut [u8] {
302        self.repr.as_mut()
303    }
304}
305impl From<[u8; SIZE]> for FpRepr {
306    fn from(repr: [u8; SIZE]) -> Self {
307        Self { repr }
308    }
309}
310
311impl ff::PrimeField for Fp {
312    type Repr = FpRepr;
313
314    const NUM_BITS: u32 = 446;
315    const CAPACITY: u32 = 445;
316    const MODULUS: &'static str = MODULUS_STR;
317    const MULTIPLICATIVE_GENERATOR: Self = GENERATOR;
318    const ROOT_OF_UNITY: Self = ROOT_OF_UNITY;
319    const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV;
320    const TWO_INV: Self = TWO_INV;
321    const DELTA: Self = DELTA;
322    const S: u32 = S;
323
324    fn from_repr(repr: Self::Repr) -> CtOption<Self> {
325        let mut tmp = Self([0, 0, 0, 0, 0, 0, 0]);
326        let repr = repr.repr;
327
328        tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap());
329        tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap());
330        tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap());
331        tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap());
332        tmp.0[4] = u64::from_le_bytes(repr[32..40].try_into().unwrap());
333        tmp.0[5] = u64::from_le_bytes(repr[40..48].try_into().unwrap());
334        tmp.0[6] = u64::from_le_bytes(repr[48..56].try_into().unwrap());
335
336        // Try to subtract the modulus
337        let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
338        let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
339        let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
340        let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
341        let (_, borrow) = sbb(tmp.0[4], MODULUS.0[4], borrow);
342        let (_, borrow) = sbb(tmp.0[5], MODULUS.0[5], borrow);
343        let (_, borrow) = sbb(tmp.0[6], MODULUS.0[6], borrow);
344
345        // If the element is smaller than MODULUS then the
346        // subtraction will underflow, producing a borrow value
347        // of 0xffff...ffff. Otherwise, it'll be zero.
348        let is_some = (borrow as u8) & 1;
349
350        // Convert to Montgomery form by computing
351        // (a.R^0 * R^2) / R = a.R
352        tmp *= &R2;
353
354        CtOption::new(tmp, Choice::from(is_some))
355    }
356
357    fn to_repr(&self) -> Self::Repr {
358        // Turn into canonical form by computing
359        // (a.R) / R = a
360        let tmp = Self::montgomery_reduce(&[
361            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], 0, 0, 0,
362            0, 0, 0, 0,
363        ]);
364
365        let mut res = [0; SIZE];
366        res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
367        res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
368        res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
369        res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
370        res[32..40].copy_from_slice(&tmp.0[4].to_le_bytes());
371        res[40..48].copy_from_slice(&tmp.0[5].to_le_bytes());
372        res[48..56].copy_from_slice(&tmp.0[6].to_le_bytes());
373        res.into()
374    }
375
376    fn is_odd(&self) -> Choice {
377        Choice::from(self.to_repr().repr[0] & 1)
378    }
379}
380
381impl FromUniformBytes<64> for Fp {
382    /// Converts a 512-bit little endian integer into
383    /// an `Fp` by reducing by the modulus.
384    fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
385        Self::from_u512([
386            u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
387            u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
388            u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
389            u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
390            u64::from_le_bytes(bytes[32..40].try_into().unwrap()),
391            u64::from_le_bytes(bytes[40..48].try_into().unwrap()),
392            u64::from_le_bytes(bytes[48..56].try_into().unwrap()),
393            u64::from_le_bytes(bytes[56..64].try_into().unwrap()),
394        ])
395    }
396}
397
398impl WithSmallOrderMulGroup<3> for Fp {
399    const ZETA: Self = ZETA;
400}
401
402#[cfg(test)]
403mod test {
404    use crate::serde::SerdeObject;
405
406    use super::*;
407    use ark_std::{end_timer, start_timer};
408    use ff::Field;
409    use rand::SeedableRng;
410    use rand_core::OsRng;
411    use rand_xorshift::XorShiftRng;
412
413    #[test]
414    fn test_sqrt() {
415        let v = (Fp::TWO_INV).square().sqrt().unwrap();
416        assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV);
417
418        for _ in 0..10000 {
419            let a = Fp::random(OsRng);
420            let mut b = a;
421            b = b.square();
422
423            let b = b.sqrt().unwrap();
424            let mut negb = b;
425            negb = negb.neg();
426
427            assert!(a == b || a == negb);
428        }
429    }
430
431    #[test]
432    fn test_field() {
433        crate::tests::field::random_field_tests::<Fp>("Eris scalar".to_string());
434    }
435
436    #[test]
437    fn test_delta() {
438        assert_eq!(Fp::DELTA, GENERATOR.pow([1u64 << Fp::S]));
439        assert_eq!(Fp::DELTA, Fp::MULTIPLICATIVE_GENERATOR.pow([1u64 << Fp::S]));
440    }
441
442    #[test]
443    fn test_zeta() {
444        assert_eq!(Fp::ZETA * Fp::ZETA * Fp::ZETA, Fp::ONE);
445        assert_ne!(Fp::ZETA * Fp::ZETA, Fp::ONE);
446    }
447
448    #[test]
449    fn test_from_u512() {
450        const N_VECS: usize = 10;
451        let expected_results = [
452            Fp::from_raw([
453                0x93638251ffeffed3,
454                0x4d32f4d20020be11,
455                0x9c39ee168df390f0,
456                0xaeef355d313cce4b,
457                0xc97c592ef6030675,
458                0xd7bc83d286537318,
459                0x01d4a87b24f91154,
460            ]),
461            Fp::from_raw([
462                0x63e0a8f1beefc612,
463                0xbb28d56dae950a42,
464                0x5264111f4a5ea3ad,
465                0xbebe71c829f662f7,
466                0xa760708568d6060c,
467                0x617d8b0cda3f6328,
468                0x03096ea964e009c0,
469            ]),
470            Fp::from_raw([
471                0xdeaedbda63b3e431,
472                0x65892bc45ec174c8,
473                0x83ad8d96c18556c7,
474                0x3fce5f9d2c537fbe,
475                0x001666753a4972d1,
476                0x9f7f457a48d6d322,
477                0x20b2fadc6bf4004d,
478            ]),
479            Fp::from_raw([
480                0x6eea9cbd68b174cf,
481                0x63aa4abda18f73e6,
482                0x0a6ccc999b1c7864,
483                0x0f90b43928625cc2,
484                0x55f541b0680af76b,
485                0x2045de539849b035,
486                0x1d5d7b5f6e8cc333,
487            ]),
488            Fp::from_raw([
489                0x673df0f69b71a763,
490                0x215a1362cfe53e1e,
491                0x7028d2b3766b0f40,
492                0x996ac521f57a7f05,
493                0x5006663a5c8cea53,
494                0xd7ead2b7c71e460d,
495                0x0f7c36b781cba9ed,
496            ]),
497            Fp::from_raw([
498                0x2eed10e8f00b189d,
499                0xe6c79fb4600e94d4,
500                0x2a9066b23daac6d4,
501                0x476d275780b553fe,
502                0xc3f2296317f71051,
503                0xb1d2bb5373270c43,
504                0x0e18a3597be61302,
505            ]),
506            Fp::from_raw([
507                0x7fbbc6b3e494ca68,
508                0x2afcc7335152430b,
509                0x93d5bd3acbccf3b3,
510                0x61a76bb383622b8c,
511                0x93efc4d40d7fac4d,
512                0x0a791ad7698655a7,
513                0x22b10d5c1090eec8,
514            ]),
515            Fp::from_raw([
516                0x596eec60211ad67b,
517                0xf23f57b9f9db8c07,
518                0x33e66f105ffc5e45,
519                0xb10ef45226f3ae42,
520                0xb98a559ccfc0ba32,
521                0x819ba919d0b6e9b5,
522                0x20f73876330a90e8,
523            ]),
524            Fp::from_raw([
525                0xbade57a48e2d9868,
526                0xe61829ffe983fcfc,
527                0xd0d080b774c31996,
528                0xa1d712ef206b4a2f,
529                0x7957f20173071cf6,
530                0xf850f49359458652,
531                0x17ba9f9aa08b9ee2,
532            ]),
533            Fp::from_raw([
534                0xd0239c8282ccc372,
535                0xfa20a695ee8f6288,
536                0x269f2ef315e029a5,
537                0xcc915da35e10b4e6,
538                0x8406f6977aadce0f,
539                0xd7d5d8bc4497465a,
540                0x08ce8bee1323d4f9,
541            ]),
542        ];
543
544        let mut seeded_rng = XorShiftRng::seed_from_u64(0u64);
545        let uniform_bytes = std::iter::from_fn(|| {
546            let mut bytes = [0u8; 64];
547            seeded_rng.fill_bytes(&mut bytes);
548            Some(bytes)
549        })
550        .take(N_VECS)
551        .collect::<Vec<_>>();
552
553        for i in 0..N_VECS {
554            let p = Fp::from_uniform_bytes(&uniform_bytes[i]);
555            assert_eq!(expected_results[i], p);
556        }
557    }
558
559    #[test]
560    #[cfg(feature = "bits")]
561    fn test_bits() {
562        crate::tests::field::random_bits_tests::<Fp>("Fp".to_string());
563    }
564
565    #[test]
566    fn test_serialization() {
567        crate::tests::field::random_serialization_test::<Fp>("Fp".to_string());
568        #[cfg(feature = "derive_serde")]
569        crate::tests::field::random_serde_test::<Fp>("Fp".to_string());
570    }
571
572    fn is_less_than(x: &[u64; 7], y: &[u64; 7]) -> bool {
573        match x[6].cmp(&y[6]) {
574            core::cmp::Ordering::Less => return true,
575            core::cmp::Ordering::Greater => return false,
576            _ => {}
577        }
578        match x[5].cmp(&y[5]) {
579            core::cmp::Ordering::Less => return true,
580            core::cmp::Ordering::Greater => return false,
581            _ => {}
582        }
583        match x[4].cmp(&y[4]) {
584            core::cmp::Ordering::Less => return true,
585            core::cmp::Ordering::Greater => return false,
586            _ => {}
587        }
588        match x[3].cmp(&y[3]) {
589            core::cmp::Ordering::Less => return true,
590            core::cmp::Ordering::Greater => return false,
591            _ => {}
592        }
593        match x[2].cmp(&y[2]) {
594            core::cmp::Ordering::Less => return true,
595            core::cmp::Ordering::Greater => return false,
596            _ => {}
597        }
598        match x[1].cmp(&y[1]) {
599            core::cmp::Ordering::Less => return true,
600            core::cmp::Ordering::Greater => return false,
601            _ => {}
602        }
603        x[0].lt(&y[0])
604    }
605
606    #[test]
607    fn test_serialization_check() {
608        let mut rng = XorShiftRng::from_seed([
609            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
610            0xbc, 0xe5,
611        ]);
612        let start = start_timer!(|| "serialize Fp");
613        // failure check
614        for _ in 0..1000000 {
615            let rand_word = [(); 7].map(|_| rng.next_u64());
616            let a = Fp(rand_word);
617            let rand_bytes = a.to_raw_bytes();
618            match is_less_than(&rand_word, &MODULUS.0) {
619                false => {
620                    assert!(Fp::from_raw_bytes(&rand_bytes).is_none());
621                }
622                _ => {
623                    assert_eq!(Fp::from_raw_bytes(&rand_bytes), Some(a));
624                }
625            }
626        }
627        end_timer!(start);
628    }
629}