//! This module exercises the `ff_derive` procedural macros, to ensure that changes to the
//! `ff` crate are reflected in `ff_derive`. It also uses the resulting field to test some
//! of the APIs provided by `ff`, such as batch inversion.

#[macro_use]
extern crate ff;

/// The BLS12-381 scalar field.
#[derive(PrimeField)]
#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"]
#[PrimeFieldGenerator = "7"]
#[PrimeFieldReprEndianness = "little"]
struct Bls381K12Scalar([u64; 4]);

mod fermat {
    /// The largest known Fermat prime, used to test the case `t = 1`.
    #[derive(PrimeField)]
    #[PrimeFieldModulus = "65537"]
    #[PrimeFieldGenerator = "3"]
    #[PrimeFieldReprEndianness = "little"]
    struct Fermat65537Field([u64; 1]);
}

mod full_limbs {
    #[derive(PrimeField)]
    #[PrimeFieldModulus = "39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319"]
    #[PrimeFieldGenerator = "19"]
    #[PrimeFieldReprEndianness = "little"]
    struct F384p([u64; 7]);

    #[test]
    fn random_masking_does_not_overflow() {
        use ff::Field;
        use rand::rngs::OsRng;

        let _ = F384p::random(OsRng);
    }
}

#[test]
fn constants() {
    use ff::{Field, PrimeField};

    assert_eq!(
        Bls381K12Scalar::MODULUS,
        "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001",
    );

    assert_eq!(
        Bls381K12Scalar::from(2) * Bls381K12Scalar::TWO_INV,
        Bls381K12Scalar::ONE,
    );

    assert_eq!(
        Bls381K12Scalar::ROOT_OF_UNITY * Bls381K12Scalar::ROOT_OF_UNITY_INV,
        Bls381K12Scalar::ONE,
    );

    // ROOT_OF_UNITY^{2^s} mod m == 1
    assert_eq!(
        Bls381K12Scalar::ROOT_OF_UNITY.pow(&[1u64 << Bls381K12Scalar::S, 0, 0, 0]),
        Bls381K12Scalar::ONE,
    );

    // DELTA^{t} mod m == 1
    assert_eq!(
        Bls381K12Scalar::DELTA.pow(&[
            0xfffe5bfeffffffff,
            0x09a1d80553bda402,
            0x299d7d483339d808,
            0x73eda753,
        ]),
        Bls381K12Scalar::ONE,
    );
}

#[test]
fn from_u128() {
    use ff::{Field, PrimeField};

    assert_eq!(Bls381K12Scalar::from_u128(1), Bls381K12Scalar::ONE);
    assert_eq!(Bls381K12Scalar::from_u128(2), Bls381K12Scalar::from(2));
    assert_eq!(
        Bls381K12Scalar::from_u128(u128::MAX),
        Bls381K12Scalar::from_str_vartime("340282366920938463463374607431768211455").unwrap(),
    );
}

#[test]
fn batch_inversion() {
    use ff::{BatchInverter, Field};

    let one = Bls381K12Scalar::ONE;

    // [1, 2, 3, 4]
    let values: Vec<_> = (0..4)
        .scan(one, |acc, _| {
            let ret = *acc;
            *acc += &one;
            Some(ret)
        })
        .collect();

    // Test BatchInverter::invert_with_external_scratch
    {
        let mut elements = values.clone();
        let mut scratch_space = vec![Bls381K12Scalar::ZERO; elements.len()];
        BatchInverter::invert_with_external_scratch(&mut elements, &mut scratch_space);
        for (a, a_inv) in values.iter().zip(elements.into_iter()) {
            assert_eq!(*a * a_inv, one);
        }
    }

    // Test BatchInverter::invert_with_internal_scratch
    {
        let mut items: Vec<_> = values.iter().cloned().map(|p| (p, one)).collect();
        BatchInverter::invert_with_internal_scratch(
            &mut items,
            |item| &mut item.0,
            |item| &mut item.1,
        );
        for (a, (a_inv, _)) in values.iter().zip(items.into_iter()) {
            assert_eq!(*a * a_inv, one);
        }
    }

    // Test BatchInvert trait
    #[cfg(feature = "alloc")]
    {
        use ff::BatchInvert;
        let mut elements = values.clone();
        elements.iter_mut().batch_invert();
        for (a, a_inv) in values.iter().zip(elements.into_iter()) {
            assert_eq!(*a * a_inv, one);
        }
    }
}

#[test]
fn sqrt() {
    use ff::{Field, PrimeField};
    // A field modulo a prime such that p = 1 mod 4 and p != 1 mod 16
    #[derive(PrimeField)]
    #[PrimeFieldModulus = "357686312646216567629137"]
    #[PrimeFieldGenerator = "5"]
    #[PrimeFieldReprEndianness = "little"]
    struct Fp([u64; 2]);
    fn test(square_root: Fp) {
        let square = square_root.square();
        let square_root = square.sqrt().unwrap();
        assert_eq!(square_root.square(), square);
    }

    test(Fp::ZERO);
    test(Fp::ONE);
    use rand::rngs::OsRng;
    test(Fp::random(OsRng));
}
