halo2_base/utils/
mod.rs

1use core::hash::Hash;
2
3use crate::ff::{FromUniformBytes, PrimeField};
4#[cfg(not(feature = "halo2-axiom"))]
5use crate::halo2_proofs::arithmetic::CurveAffine;
6use crate::halo2_proofs::circuit::Value;
7#[cfg(feature = "halo2-axiom")]
8pub use crate::halo2_proofs::halo2curves::CurveAffineExt;
9
10use num_bigint::BigInt;
11use num_bigint::BigUint;
12use num_bigint::Sign;
13use num_traits::Signed;
14use num_traits::{One, Zero};
15
16/// Helper functions for raw halo2 operations to unify slight differences in API for halo2-axiom and halo2-pse
17pub mod halo2;
18#[cfg(any(test, feature = "test-utils"))]
19pub mod testing;
20
21/// Helper trait to convert to and from a [BigPrimeField] by converting a list of [u64] digits
22#[cfg(feature = "halo2-axiom")]
23pub trait BigPrimeField: ScalarField {
24    /// Converts a slice of [u64] to [BigPrimeField]
25    /// * `val`: the slice of u64
26    ///
27    /// # Assumptions
28    /// * `val` has the correct length for the implementation
29    /// * The integer value of `val` is already less than the modulus of `Self`
30    fn from_u64_digits(val: &[u64]) -> Self;
31}
32#[cfg(feature = "halo2-axiom")]
33impl<F> BigPrimeField for F
34where
35    F: ScalarField + From<[u64; 4]>, // Assume [u64; 4] is little-endian. We only implement ScalarField when this is true.
36{
37    #[inline(always)]
38    fn from_u64_digits(val: &[u64]) -> Self {
39        debug_assert!(val.len() <= 4);
40        let mut raw = [0u64; 4];
41        raw[..val.len()].copy_from_slice(val);
42        Self::from(raw)
43    }
44}
45
46/// Helper trait to represent a field element that can be converted into [u64] limbs.
47///
48/// Note: Since the number of bits necessary to represent a field element is larger than the number of bits in a u64, we decompose the integer representation of the field element into multiple [u64] values e.g. `limbs`.
49pub trait ScalarField: PrimeField + FromUniformBytes<64> + From<bool> + Hash + Ord {
50    /// Returns the base `2<sup>bit_len</sup>` little endian representation of the [ScalarField] element up to `num_limbs` number of limbs (truncates any extra limbs).
51    ///
52    /// Assumes `bit_len < 64`.
53    /// * `num_limbs`: number of limbs to return
54    /// * `bit_len`: number of bits in each limb
55    fn to_u64_limbs(self, num_limbs: usize, bit_len: usize) -> Vec<u64>;
56
57    /// Returns the little endian byte representation of the element.
58    fn to_bytes_le(&self) -> Vec<u8>;
59
60    /// Creates a field element from a little endian byte representation.
61    ///
62    /// The default implementation assumes that `PrimeField::from_repr` is implemented for little-endian.
63    /// It should be overriden if this is not the case.
64    fn from_bytes_le(bytes: &[u8]) -> Self {
65        let mut repr = Self::Repr::default();
66        repr.as_mut()[..bytes.len()].copy_from_slice(bytes);
67        Self::from_repr(repr).unwrap()
68    }
69
70    /// Gets the least significant 32 bits of the field element.
71    fn get_lower_32(&self) -> u32 {
72        let bytes = self.to_bytes_le();
73        let mut lower_32 = 0u32;
74        for (i, byte) in bytes.into_iter().enumerate().take(4) {
75            lower_32 |= (byte as u32) << (i * 8);
76        }
77        lower_32
78    }
79
80    /// Gets the least significant 64 bits of the field element.
81    fn get_lower_64(&self) -> u64 {
82        let bytes = self.to_bytes_le();
83        let mut lower_64 = 0u64;
84        for (i, byte) in bytes.into_iter().enumerate().take(8) {
85            lower_64 |= (byte as u64) << (i * 8);
86        }
87        lower_64
88    }
89}
90// See below for implementations
91
92// Later: will need to separate BigPrimeField from ScalarField when Goldilocks is introduced
93
94/// [ScalarField] that is ~256 bits long
95#[cfg(feature = "halo2-pse")]
96pub trait BigPrimeField: PrimeField<Repr = [u8; 32]> + ScalarField {}
97#[cfg(feature = "halo2-pse")]
98impl<F: PrimeField<Repr = [u8; 32]> + ScalarField> BigPrimeField for F {}
99
100/// Converts an [Iterator] of u64 digits into `number_of_limbs` limbs of `bit_len` bits returned as a [Vec].
101///
102/// Assumes: `bit_len < 64`.
103/// * `e`: Iterator of [u64] digits
104/// * `number_of_limbs`: number of limbs to return
105/// * `bit_len`: number of bits in each limb
106#[inline(always)]
107pub(crate) fn decompose_u64_digits_to_limbs(
108    e: impl IntoIterator<Item = u64>,
109    number_of_limbs: usize,
110    bit_len: usize,
111) -> Vec<u64> {
112    debug_assert!(bit_len < 64);
113
114    let mut e = e.into_iter();
115    // Mask to extract the bits from each digit
116    let mask: u64 = (1u64 << bit_len) - 1u64;
117    let mut u64_digit = e.next().unwrap_or(0);
118    let mut rem = 64;
119
120    // For each digit, we extract its individual limbs by repeatedly masking and shifting the digit based on how many bits we have left to extract.
121    (0..number_of_limbs)
122        .map(|_| match rem.cmp(&bit_len) {
123            // If `rem` > `bit_len`, we mask the bits from the `u64_digit` to return the first limb.
124            // We shift the digit to the right by `bit_len` bits and subtract `bit_len` from `rem`
125            core::cmp::Ordering::Greater => {
126                let limb = u64_digit & mask;
127                u64_digit >>= bit_len;
128                rem -= bit_len;
129                limb
130            }
131            // If `rem` == `bit_len`, then we mask the bits from the `u64_digit` to return the first limb
132            // We retrieve the next digit and reset `rem` to 64
133            core::cmp::Ordering::Equal => {
134                let limb = u64_digit & mask;
135                u64_digit = e.next().unwrap_or(0);
136                rem = 64;
137                limb
138            }
139            // If `rem` < `bit_len`, we retrieve the next digit, mask it, and shift left `rem` bits from the `u64_digit` to return the first limb.
140            // we shift the digit to the right by `bit_len` - `rem` bits to retrieve the start of the next limb and add 64 - bit_len to `rem` to get the remainder.
141            core::cmp::Ordering::Less => {
142                let mut limb = u64_digit;
143                u64_digit = e.next().unwrap_or(0);
144                limb |= (u64_digit & ((1u64 << (bit_len - rem)) - 1u64)) << rem;
145                u64_digit >>= bit_len - rem;
146                rem += 64 - bit_len;
147                limb
148            }
149        })
150        .collect()
151}
152
153/// Returns the number of bits needed to represent the value of `x`.
154pub const fn bit_length(x: u64) -> usize {
155    (u64::BITS - x.leading_zeros()) as usize
156}
157
158/// Returns the ceiling of the base 2 logarithm of `x`.
159///
160/// `log2_ceil(0)` returns 0.
161pub fn log2_ceil(x: u64) -> usize {
162    (u64::BITS - x.leading_zeros()) as usize - usize::from(x.is_power_of_two())
163}
164
165/// Returns the modulus of [BigPrimeField].
166pub fn modulus<F: BigPrimeField>() -> BigUint {
167    fe_to_biguint(&-F::ONE) + 1u64
168}
169
170/// Returns the [BigPrimeField] element of 2<sup>n</sup>.
171/// * `n`: the desired power of 2.
172pub fn power_of_two<F: BigPrimeField>(n: usize) -> F {
173    biguint_to_fe(&(BigUint::one() << n))
174}
175
176/// Converts an immutable reference to [BigUint] to a [BigPrimeField].
177/// * `e`: immutable reference to [BigUint]
178///
179/// # Assumptions:
180/// * `e` is less than the modulus of `F`
181pub fn biguint_to_fe<F: BigPrimeField>(e: &BigUint) -> F {
182    #[cfg(feature = "halo2-axiom")]
183    {
184        F::from_u64_digits(&e.to_u64_digits())
185    }
186
187    #[cfg(feature = "halo2-pse")]
188    {
189        let bytes = e.to_bytes_le();
190        F::from_bytes_le(&bytes)
191    }
192}
193
194/// Converts an immutable reference to [BigInt] to a [BigPrimeField].
195/// * `e`: immutable reference to [BigInt]
196///
197/// # Assumptions:
198/// * The absolute value of `e` is less than the modulus of `F`
199pub fn bigint_to_fe<F: BigPrimeField>(e: &BigInt) -> F {
200    #[cfg(feature = "halo2-axiom")]
201    {
202        let (sign, digits) = e.to_u64_digits();
203        if sign == Sign::Minus {
204            -F::from_u64_digits(&digits)
205        } else {
206            F::from_u64_digits(&digits)
207        }
208    }
209    #[cfg(feature = "halo2-pse")]
210    {
211        let (sign, bytes) = e.to_bytes_le();
212        let f_abs = F::from_bytes_le(&bytes);
213        if sign == Sign::Minus {
214            -f_abs
215        } else {
216            f_abs
217        }
218    }
219}
220
221/// Converts an immutable reference to an PrimeField element into a [BigUint] element.
222/// * `fe`: immutable reference to PrimeField element to convert
223pub fn fe_to_biguint<F: ScalarField>(fe: &F) -> BigUint {
224    BigUint::from_bytes_le(fe.to_bytes_le().as_ref())
225}
226
227/// Converts a [BigPrimeField] element into a [BigInt] element by sending `fe` in `[0, F::modulus())` to
228/// ```ignore
229/// fe,                 if fe < F::modulus() / 2
230/// fe - F::modulus(),  otherwise
231/// ```
232pub fn fe_to_bigint<F: BigPrimeField>(fe: &F) -> BigInt {
233    // TODO: `F` should just have modulus as lazy_static or something
234    let modulus = modulus::<F>();
235    let e = fe_to_biguint(fe);
236    if e <= &modulus / 2u32 {
237        BigInt::from_biguint(Sign::Plus, e)
238    } else {
239        BigInt::from_biguint(Sign::Minus, modulus - e)
240    }
241}
242
243/// Decomposes an immutable reference to a [BigPrimeField] element into `number_of_limbs` limbs of `bit_len` bits each and returns a [Vec] of [BigPrimeField] represented by those limbs.
244///
245/// Assumes `bit_len < 128`.
246/// * `e`: immutable reference to [BigPrimeField] element to decompose
247/// * `number_of_limbs`: number of limbs to decompose `e` into
248/// * `bit_len`: number of bits in each limb
249pub fn decompose<F: BigPrimeField>(e: &F, number_of_limbs: usize, bit_len: usize) -> Vec<F> {
250    if bit_len > 64 {
251        decompose_biguint(&fe_to_biguint(e), number_of_limbs, bit_len)
252    } else {
253        decompose_fe_to_u64_limbs(e, number_of_limbs, bit_len).into_iter().map(F::from).collect()
254    }
255}
256
257/// Decomposes an immutable reference to a [ScalarField] element into `number_of_limbs` limbs of `bit_len` bits each and returns a [Vec] of [u64] represented by those limbs.
258///
259/// Assumes `bit_len` < 64
260/// * `e`: immutable reference to [ScalarField] element to decompose
261/// * `number_of_limbs`: number of limbs to decompose `e` into
262/// * `bit_len`: number of bits in each limb
263pub fn decompose_fe_to_u64_limbs<F: ScalarField>(
264    e: &F,
265    number_of_limbs: usize,
266    bit_len: usize,
267) -> Vec<u64> {
268    #[cfg(feature = "halo2-axiom")]
269    {
270        e.to_u64_limbs(number_of_limbs, bit_len)
271    }
272
273    #[cfg(feature = "halo2-pse")]
274    {
275        decompose_u64_digits_to_limbs(fe_to_biguint(e).iter_u64_digits(), number_of_limbs, bit_len)
276    }
277}
278
279/// Decomposes an immutable reference to a [BigUint] into `num_limbs` limbs of `bit_len` bits each and returns a [Vec] of [BigPrimeField] represented by those limbs.
280///
281/// Assumes 64 <= `bit_len` < 128.
282/// * `e`: immutable reference to [BigInt] to decompose
283/// * `num_limbs`: number of limbs to decompose `e` into
284/// * `bit_len`: number of bits in each limb
285///
286/// Truncates to `num_limbs` limbs if `e` is too large.
287pub fn decompose_biguint<F: BigPrimeField>(
288    e: &BigUint,
289    num_limbs: usize,
290    bit_len: usize,
291) -> Vec<F> {
292    // bit_len must be between 64` and 128
293    debug_assert!((64..128).contains(&bit_len));
294    let mut e = e.iter_u64_digits();
295
296    // Grab first 128-bit limb from iterator
297    let mut limb0 = e.next().unwrap_or(0) as u128;
298    let mut rem = bit_len - 64;
299    let mut u64_digit = e.next().unwrap_or(0);
300    // Extract second limb (bit length 64) from e
301    limb0 |= ((u64_digit & ((1u64 << rem) - 1u64)) as u128) << 64u32;
302    u64_digit >>= rem;
303    rem = 64 - rem;
304
305    // Convert `limb0` into field element `F` and create an iterator by chaining `limb0` with the computing the remaining limbs
306    core::iter::once(F::from_u128(limb0))
307        .chain((1..num_limbs).map(|_| {
308            let mut limb = u64_digit as u128;
309            let mut bits = rem;
310            u64_digit = e.next().unwrap_or(0);
311            if bit_len >= 64 + bits {
312                limb |= (u64_digit as u128) << bits;
313                u64_digit = e.next().unwrap_or(0);
314                bits += 64;
315            }
316            rem = bit_len - bits;
317            limb |= ((u64_digit & ((1u64 << rem) - 1u64)) as u128) << bits;
318            u64_digit >>= rem;
319            rem = 64 - rem;
320            F::from_u128(limb)
321        }))
322        .collect()
323}
324
325/// Decomposes an immutable reference to a [BigInt] into `num_limbs` limbs of `bit_len` bits each and returns a [Vec] of [BigPrimeField] represented by those limbs.
326///
327/// Assumes `bit_len < 128`.
328/// * `e`: immutable reference to `BigInt` to decompose
329/// * `num_limbs`: number of limbs to decompose `e` into
330/// * `bit_len`: number of bits in each limb
331pub fn decompose_bigint<F: BigPrimeField>(e: &BigInt, num_limbs: usize, bit_len: usize) -> Vec<F> {
332    if e.is_negative() {
333        decompose_biguint::<F>(e.magnitude(), num_limbs, bit_len).into_iter().map(|x| -x).collect()
334    } else {
335        decompose_biguint(e.magnitude(), num_limbs, bit_len)
336    }
337}
338
339/// Decomposes an immutable reference to a [BigInt] into `num_limbs` limbs of `bit_len` bits each and returns a [Vec] of [BigPrimeField] represented by those limbs wrapped in [Value].
340///
341/// Assumes `bit_len` < 128.
342/// * `e`: immutable reference to `BigInt` to decompose
343/// * `num_limbs`: number of limbs to decompose `e` into
344/// * `bit_len`: number of bits in each limb
345pub fn decompose_bigint_option<F: BigPrimeField>(
346    value: Value<&BigInt>,
347    number_of_limbs: usize,
348    bit_len: usize,
349) -> Vec<Value<F>> {
350    value.map(|e| decompose_bigint(e, number_of_limbs, bit_len)).transpose_vec(number_of_limbs)
351}
352
353/// Wraps the internal value of `value` in an [Option].
354/// If the value is [None], then the function returns [None].
355/// * `value`: Value to convert.
356pub fn value_to_option<V>(value: Value<V>) -> Option<V> {
357    let mut v = None;
358    value.map(|val| {
359        v = Some(val);
360    });
361    v
362}
363
364/// Computes the value of an integer by passing as `input` a [Vec] of its limb values and the `bit_len` (bit length) used.
365///
366/// Returns the sum of all limbs scaled by 2<sup>(bit_len * i)</sup> where i is the index of the limb.
367/// * `input`: Limb values of the integer.
368/// * `bit_len`: Length of limb in bits
369pub fn compose(input: Vec<BigUint>, bit_len: usize) -> BigUint {
370    input.iter().rev().fold(BigUint::zero(), |acc, val| (acc << bit_len) + val)
371}
372
373/// Helper trait
374#[cfg(feature = "halo2-pse")]
375pub trait CurveAffineExt: CurveAffine {
376    /// Returns the raw affine (X, Y) coordinantes
377    fn into_coordinates(self) -> (Self::Base, Self::Base) {
378        let coordinates = self.coordinates().unwrap();
379        (*coordinates.x(), *coordinates.y())
380    }
381}
382#[cfg(feature = "halo2-pse")]
383impl<C: CurveAffine> CurveAffineExt for C {}
384
385mod scalar_field_impls {
386    use super::{decompose_u64_digits_to_limbs, ScalarField};
387    #[cfg(feature = "halo2-pse")]
388    use crate::ff::PrimeField;
389    use crate::halo2_proofs::halo2curves::{
390        bn256::{Fq as bn254Fq, Fr as bn254Fr},
391        secp256k1::{Fp as secpFp, Fq as secpFq},
392    };
393
394    /// To ensure `ScalarField` is only implemented for `ff:Field` where `Repr` is little endian, we use the following macro
395    /// to implement the trait for each field.
396    #[cfg(feature = "halo2-axiom")]
397    #[macro_export]
398    macro_rules! impl_scalar_field {
399        ($field:ident) => {
400            impl ScalarField for $field {
401                #[inline(always)]
402                fn to_u64_limbs(self, num_limbs: usize, bit_len: usize) -> Vec<u64> {
403                    // Basically same as `to_repr` but does not go further into bytes
404                    let tmp: [u64; 4] = self.into();
405                    decompose_u64_digits_to_limbs(tmp, num_limbs, bit_len)
406                }
407
408                #[inline(always)]
409                fn to_bytes_le(&self) -> Vec<u8> {
410                    let tmp: [u64; 4] = (*self).into();
411                    tmp.iter().flat_map(|x| x.to_le_bytes()).collect()
412                }
413
414                #[inline(always)]
415                fn get_lower_32(&self) -> u32 {
416                    let tmp: [u64; 4] = (*self).into();
417                    tmp[0] as u32
418                }
419
420                #[inline(always)]
421                fn get_lower_64(&self) -> u64 {
422                    let tmp: [u64; 4] = (*self).into();
423                    tmp[0]
424                }
425            }
426        };
427    }
428
429    /// To ensure `ScalarField` is only implemented for `ff:Field` where `Repr` is little endian, we use the following macro
430    /// to implement the trait for each field.
431    #[cfg(feature = "halo2-pse")]
432    #[macro_export]
433    macro_rules! impl_scalar_field {
434        ($field:ident) => {
435            impl ScalarField for $field {
436                #[inline(always)]
437                fn to_u64_limbs(self, num_limbs: usize, bit_len: usize) -> Vec<u64> {
438                    let bytes = self.to_repr();
439                    let digits = (0..4)
440                        .map(|i| u64::from_le_bytes(bytes[i * 8..(i + 1) * 8].try_into().unwrap()));
441                    decompose_u64_digits_to_limbs(digits, num_limbs, bit_len)
442                }
443
444                #[inline(always)]
445                fn to_bytes_le(&self) -> Vec<u8> {
446                    self.to_repr().to_vec()
447                }
448            }
449        };
450    }
451
452    impl_scalar_field!(bn254Fr);
453    impl_scalar_field!(bn254Fq);
454    impl_scalar_field!(secpFp);
455    impl_scalar_field!(secpFq);
456}
457
458/// Module for reading parameters for Halo2 proving system from the file system.
459pub mod fs {
460    use std::{
461        env::var,
462        fs::{self, File},
463        io::{BufReader, BufWriter},
464    };
465
466    use crate::halo2_proofs::{
467        halo2curves::{
468            bn256::{Bn256, G1Affine},
469            CurveAffine,
470        },
471        poly::{
472            commitment::{Params, ParamsProver},
473            kzg::commitment::ParamsKZG,
474        },
475    };
476    use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};
477
478    /// Reads the srs from a file found in `./params/kzg_bn254_{k}.srs` or `{dir}/kzg_bn254_{k}.srs` if `PARAMS_DIR` env var is specified.
479    /// * `k`: degree that expresses the size of circuit (i.e., 2^<sup>k</sup> is the number of rows in the circuit)
480    pub fn read_params(k: u32) -> ParamsKZG<Bn256> {
481        let dir = var("PARAMS_DIR").unwrap_or_else(|_| "./params".to_string());
482        ParamsKZG::<Bn256>::read(&mut BufReader::new(
483            File::open(format!("{dir}/kzg_bn254_{k}.srs").as_str())
484                .expect("Params file does not exist"),
485        ))
486        .unwrap()
487    }
488
489    /// Attempts to read the srs from a file found in `./params/kzg_bn254_{k}.srs` or `{dir}/kzg_bn254_{k}.srs` if `PARAMS_DIR` env var is specified, creates a file it if it does not exist.
490    /// * `k`: degree that expresses the size of circuit (i.e., 2^<sup>k</sup> is the number of rows in the circuit)
491    /// * `setup`: a function that creates the srs
492    pub fn read_or_create_srs<'a, C: CurveAffine, P: ParamsProver<'a, C>>(
493        k: u32,
494        setup: impl Fn(u32) -> P,
495    ) -> P {
496        let dir = var("PARAMS_DIR").unwrap_or_else(|_| "./params".to_string());
497        let path = format!("{dir}/kzg_bn254_{k}.srs");
498        match File::open(path.as_str()) {
499            Ok(f) => {
500                #[cfg(feature = "display")]
501                println!("read params from {path}");
502                let mut reader = BufReader::new(f);
503                P::read(&mut reader).unwrap()
504            }
505            Err(_) => {
506                #[cfg(feature = "display")]
507                println!("creating params for {k}");
508                fs::create_dir_all(dir).unwrap();
509                let params = setup(k);
510                params.write(&mut BufWriter::new(File::create(path).unwrap())).unwrap();
511                params
512            }
513        }
514    }
515
516    /// Generates the SRS for the KZG scheme and writes it to a file found in "./params/kzg_bn2_{k}.srs` or `{dir}/kzg_bn254_{k}.srs` if `PARAMS_DIR` env var is specified, creates a file it if it does not exist"
517    /// * `k`: degree that expresses the size of circuit (i.e., 2^<sup>k</sup> is the number of rows in the circuit)
518    pub fn gen_srs(k: u32) -> ParamsKZG<Bn256> {
519        read_or_create_srs::<G1Affine, _>(k, |k| {
520            ParamsKZG::<Bn256>::setup(k, ChaCha20Rng::from_seed(Default::default()))
521        })
522    }
523}
524
525#[cfg(test)]
526mod tests {
527    use crate::halo2_proofs::halo2curves::bn256::Fr;
528    use num_bigint::RandomBits;
529    use rand::{
530        rngs::{OsRng, StdRng},
531        Rng, SeedableRng,
532    };
533    use std::ops::Shl;
534
535    use super::*;
536
537    #[test]
538    fn test_signed_roundtrip() {
539        use crate::halo2_proofs::halo2curves::bn256::Fr;
540        assert_eq!(fe_to_bigint(&bigint_to_fe::<Fr>(&-BigInt::one())), -BigInt::one());
541    }
542
543    #[test]
544    fn test_decompose_biguint() {
545        let mut rng = OsRng;
546        const MAX_LIMBS: u64 = 5;
547        for bit_len in 64..128usize {
548            for num_limbs in 1..=MAX_LIMBS {
549                for _ in 0..10_000usize {
550                    let mut e: BigUint = rng.sample(RandomBits::new(num_limbs * bit_len as u64));
551                    let limbs = decompose_biguint::<Fr>(&e, num_limbs as usize, bit_len);
552
553                    let limbs2 = {
554                        let mut limbs = vec![];
555                        let mask = BigUint::one().shl(bit_len) - 1usize;
556                        for _ in 0..num_limbs {
557                            let limb = &e & &mask;
558                            let mut bytes_le = limb.to_bytes_le();
559                            bytes_le.resize(32, 0u8);
560                            limbs.push(Fr::from_bytes(&bytes_le.try_into().unwrap()).unwrap());
561                            e >>= bit_len;
562                        }
563                        limbs
564                    };
565                    assert_eq!(limbs, limbs2);
566                }
567            }
568        }
569    }
570
571    #[test]
572    fn test_decompose_u64_digits_to_limbs() {
573        let mut rng = OsRng;
574        const MAX_LIMBS: u64 = 5;
575        for bit_len in 0..64usize {
576            for num_limbs in 1..=MAX_LIMBS {
577                for _ in 0..10_000usize {
578                    let mut e: BigUint = rng.sample(RandomBits::new(num_limbs * bit_len as u64));
579                    let limbs = decompose_u64_digits_to_limbs(
580                        e.to_u64_digits(),
581                        num_limbs as usize,
582                        bit_len,
583                    );
584                    let limbs2 = {
585                        let mut limbs = vec![];
586                        let mask = BigUint::one().shl(bit_len) - 1usize;
587                        for _ in 0..num_limbs {
588                            let limb = &e & &mask;
589                            limbs.push(u64::try_from(limb).unwrap());
590                            e >>= bit_len;
591                        }
592                        limbs
593                    };
594                    assert_eq!(limbs, limbs2);
595                }
596            }
597        }
598    }
599
600    #[test]
601    fn test_log2_ceil_zero() {
602        assert_eq!(log2_ceil(0), 0);
603    }
604
605    #[test]
606    fn test_get_lower_32() {
607        let mut rng = StdRng::seed_from_u64(0);
608        for _ in 0..10_000usize {
609            let e: u32 = rng.gen_range(0..u32::MAX);
610            assert_eq!(Fr::from(e as u64).get_lower_32(), e);
611        }
612        assert_eq!(Fr::from((1u64 << 32_i32) + 1).get_lower_32(), 1);
613    }
614
615    #[test]
616    fn test_get_lower_64() {
617        let mut rng = StdRng::seed_from_u64(0);
618        for _ in 0..10_000usize {
619            let e: u64 = rng.gen_range(0..u64::MAX);
620            assert_eq!(Fr::from(e).get_lower_64(), e);
621        }
622    }
623}