macro_rules! curve_impl {
    (
        $name:expr,
        $projective:ident,
        $affine:ident,
        $prepared:ident,
        $basefield:ident,
        $scalarfield:ident,
        $uncompressed:ident,
        $compressed:ident,
        $pairing:ident
    ) => {
        #[derive(Copy, Clone, PartialEq, Eq, Debug)]
        pub struct $affine {
            pub(crate) x: $basefield,
            pub(crate) y: $basefield,
            pub(crate) infinity: bool,
        }

        impl ::std::fmt::Display for $affine {
            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                if self.infinity {
                    write!(f, "{}(Infinity)", $name)
                } else {
                    write!(f, "{}(x={}, y={})", $name, self.x, self.y)
                }
            }
        }

        #[derive(Copy, Clone, Debug, Eq)]
        pub struct $projective {
            pub(crate) x: $basefield,
            pub(crate) y: $basefield,
            pub(crate) z: $basefield,
        }

        impl ::std::fmt::Display for $projective {
            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                write!(f, "{}", self.into_affine())
            }
        }

        impl PartialEq for $projective {
            fn eq(&self, other: &$projective) -> bool {
                if self.is_zero() {
                    return other.is_zero();
                }

                if other.is_zero() {
                    return false;
                }

                // The points (X, Y, Z) and (X', Y', Z')
                // are equal when (X * Z^2) = (X' * Z'^2)
                // and (Y * Z^3) = (Y' * Z'^3).

                let mut z1 = self.z.square();
                let mut z2 = other.z.square();

                let mut tmp1 = self.x;
                tmp1.mul_assign(&z2);

                let mut tmp2 = other.x;
                tmp2.mul_assign(&z1);

                if tmp1 != tmp2 {
                    return false;
                }

                z1.mul_assign(&self.z);
                z2.mul_assign(&other.z);
                z2.mul_assign(&self.y);
                z1.mul_assign(&other.y);

                if z1 != z2 {
                    return false;
                }

                true
            }
        }

        impl $affine {
            fn mul_bits<S: AsRef<[u64]>>(&self, bits: BitIterator<S>) -> $projective {
                let mut res = $projective::zero();
                for i in bits {
                    res.double();
                    if i {
                        res.add_assign(self)
                    }
                }
                res
            }

            /// Attempts to construct an affine point given an x-coordinate. The
            /// point is not guaranteed to be in the prime order subgroup.
            ///
            /// If and only if `greatest` is set will the lexicographically
            /// largest y-coordinate be selected.
            fn get_point_from_x(x: $basefield, greatest: bool) -> CtOption<$affine> {
                // Compute x^3 + b
                let mut x3b = x.square();
                x3b.mul_assign(&x);
                x3b.add_assign(&$affine::get_coeff_b());

                x3b.sqrt().map(|y| {
                    let negy = y.neg();

                    $affine {
                        x: x,
                        y: if (y < negy) ^ greatest { y } else { negy },
                        infinity: false,
                    }
                })
            }

            fn is_on_curve(&self) -> bool {
                if self.is_zero() {
                    true
                } else {
                    // Check that the point is on the curve
                    let y2 = self.y.square();

                    let mut x3b = self.x.square();
                    x3b.mul_assign(&self.x);
                    x3b.add_assign(&Self::get_coeff_b());

                    y2 == x3b
                }
            }

            fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool {
                self.mul($scalarfield::char()).is_zero()
            }
        }

        impl ::std::ops::Neg for $affine {
            type Output = Self;

            #[inline]
            fn neg(self) -> Self {
                let mut ret = self;
                if !ret.is_zero() {
                    ret.y = ret.y.neg();
                }
                ret
            }
        }

        impl CurveAffine for $affine {
            type Engine = Bls12;
            type Scalar = $scalarfield;
            type Base = $basefield;
            type Projective = $projective;
            type Uncompressed = $uncompressed;
            type Compressed = $compressed;

            fn zero() -> Self {
                $affine {
                    x: $basefield::zero(),
                    y: $basefield::one(),
                    infinity: true,
                }
            }

            fn one() -> Self {
                Self::get_generator()
            }

            fn is_zero(&self) -> bool {
                self.infinity
            }

            fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, by: S) -> $projective {
                let bits = BitIterator::new(by.into());
                self.mul_bits(bits)
            }

            fn into_projective(&self) -> $projective {
                (*self).into()
            }
        }

        impl PairingCurveAffine for $affine {
            type Prepared = $prepared;
            type Pair = $pairing;
            type PairingResult = Fq12;

            fn prepare(&self) -> Self::Prepared {
                $prepared::from_affine(*self)
            }

            fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
                self.perform_pairing(other)
            }
        }

        impl ::std::ops::Neg for $projective {
            type Output = Self;

            #[inline]
            fn neg(self) -> Self {
                let mut ret = self;
                if !ret.is_zero() {
                    ret.y = ret.y.neg();
                }
                ret
            }
        }

        impl<'r> ::std::ops::Add<&'r $projective> for $projective {
            type Output = Self;

            #[inline]
            fn add(self, other: &Self) -> Self {
                let mut ret = self;
                ret.add_assign(other);
                ret
            }
        }

        impl ::std::ops::Add for $projective {
            type Output = Self;

            #[inline]
            fn add(self, other: Self) -> Self {
                self + &other
            }
        }

        impl<'r> ::std::ops::AddAssign<&'r $projective> for $projective {
            fn add_assign(&mut self, other: &Self) {
                if self.is_zero() {
                    *self = *other;
                    return;
                }

                if other.is_zero() {
                    return;
                }

                // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl

                // Z1Z1 = Z1^2
                let z1z1 = self.z.square();

                // Z2Z2 = Z2^2
                let z2z2 = other.z.square();

                // U1 = X1*Z2Z2
                let mut u1 = self.x;
                u1.mul_assign(&z2z2);

                // U2 = X2*Z1Z1
                let mut u2 = other.x;
                u2.mul_assign(&z1z1);

                // S1 = Y1*Z2*Z2Z2
                let mut s1 = self.y;
                s1.mul_assign(&other.z);
                s1.mul_assign(&z2z2);

                // S2 = Y2*Z1*Z1Z1
                let mut s2 = other.y;
                s2.mul_assign(&self.z);
                s2.mul_assign(&z1z1);

                if u1 == u2 && s1 == s2 {
                    // The two points are equal, so we double.
                    self.double();
                } else {
                    // If we're adding -a and a together, self.z becomes zero as H becomes zero.

                    // H = U2-U1
                    let mut h = u2;
                    h.sub_assign(&u1);

                    // I = (2*H)^2
                    let i = h.double().square();

                    // J = H*I
                    let mut j = h;
                    j.mul_assign(&i);

                    // r = 2*(S2-S1)
                    let mut r = s2;
                    r.sub_assign(&s1);
                    r = r.double();

                    // V = U1*I
                    let mut v = u1;
                    v.mul_assign(&i);

                    // X3 = r^2 - J - 2*V
                    self.x = r.square();
                    self.x.sub_assign(&j);
                    self.x.sub_assign(&v);
                    self.x.sub_assign(&v);

                    // Y3 = r*(V - X3) - 2*S1*J
                    self.y = v;
                    self.y.sub_assign(&self.x);
                    self.y.mul_assign(&r);
                    s1.mul_assign(&j); // S1 = S1 * J * 2
                    s1 = s1.double();
                    self.y.sub_assign(&s1);

                    // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H
                    self.z.add_assign(&other.z);
                    self.z = self.z.square();
                    self.z.sub_assign(&z1z1);
                    self.z.sub_assign(&z2z2);
                    self.z.mul_assign(&h);
                }
            }
        }

        impl ::std::ops::AddAssign for $projective {
            #[inline]
            fn add_assign(&mut self, other: Self) {
                self.add_assign(&other);
            }
        }

        impl<'r> ::std::ops::Sub<&'r $projective> for $projective {
            type Output = Self;

            #[inline]
            fn sub(self, other: &Self) -> Self {
                let mut ret = self;
                ret.sub_assign(other);
                ret
            }
        }

        impl ::std::ops::Sub for $projective {
            type Output = Self;

            #[inline]
            fn sub(self, other: Self) -> Self {
                self - &other
            }
        }

        impl<'r> ::std::ops::SubAssign<&'r $projective> for $projective {
            fn sub_assign(&mut self, other: &Self) {
                self.add_assign(&other.neg());
            }
        }

        impl ::std::ops::SubAssign for $projective {
            #[inline]
            fn sub_assign(&mut self, other: Self) {
                self.sub_assign(&other);
            }
        }

        impl<'r> ::std::ops::Add<&'r <$projective as CurveProjective>::Affine> for $projective {
            type Output = Self;

            #[inline]
            fn add(self, other: &<$projective as CurveProjective>::Affine) -> Self {
                let mut ret = self;
                ret.add_assign(other);
                ret
            }
        }

        impl ::std::ops::Add<<$projective as CurveProjective>::Affine> for $projective {
            type Output = Self;

            #[inline]
            fn add(self, other: <$projective as CurveProjective>::Affine) -> Self {
                self + &other
            }
        }

        impl<'r> ::std::ops::AddAssign<&'r <$projective as CurveProjective>::Affine>
            for $projective
        {
            fn add_assign(&mut self, other: &<$projective as CurveProjective>::Affine) {
                if other.is_zero() {
                    return;
                }

                if self.is_zero() {
                    self.x = other.x;
                    self.y = other.y;
                    self.z = $basefield::one();
                    return;
                }

                // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl

                // Z1Z1 = Z1^2
                let z1z1 = self.z.square();

                // U2 = X2*Z1Z1
                let mut u2 = other.x;
                u2.mul_assign(&z1z1);

                // S2 = Y2*Z1*Z1Z1
                let mut s2 = other.y;
                s2.mul_assign(&self.z);
                s2.mul_assign(&z1z1);

                if self.x == u2 && self.y == s2 {
                    // The two points are equal, so we double.
                    self.double();
                } else {
                    // If we're adding -a and a together, self.z becomes zero as H becomes zero.

                    // H = U2-X1
                    let mut h = u2;
                    h.sub_assign(&self.x);

                    // HH = H^2
                    let hh = h.square();

                    // I = 4*HH
                    let i = hh.double().double();

                    // J = H*I
                    let mut j = h;
                    j.mul_assign(&i);

                    // r = 2*(S2-Y1)
                    let mut r = s2;
                    r.sub_assign(&self.y);
                    r = r.double();

                    // V = X1*I
                    let mut v = self.x;
                    v.mul_assign(&i);

                    // X3 = r^2 - J - 2*V
                    self.x = r.square();
                    self.x.sub_assign(&j);
                    self.x.sub_assign(&v);
                    self.x.sub_assign(&v);

                    // Y3 = r*(V-X3)-2*Y1*J
                    j.mul_assign(&self.y); // J = 2*Y1*J
                    j = j.double();
                    self.y = v;
                    self.y.sub_assign(&self.x);
                    self.y.mul_assign(&r);
                    self.y.sub_assign(&j);

                    // Z3 = (Z1+H)^2-Z1Z1-HH
                    self.z.add_assign(&h);
                    self.z = self.z.square();
                    self.z.sub_assign(&z1z1);
                    self.z.sub_assign(&hh);
                }
            }
        }

        impl ::std::ops::AddAssign<<$projective as CurveProjective>::Affine> for $projective {
            #[inline]
            fn add_assign(&mut self, other: <$projective as CurveProjective>::Affine) {
                self.add_assign(&other);
            }
        }

        impl<'r> ::std::ops::Sub<&'r <$projective as CurveProjective>::Affine> for $projective {
            type Output = Self;

            #[inline]
            fn sub(self, other: &<$projective as CurveProjective>::Affine) -> Self {
                let mut ret = self;
                ret.sub_assign(other);
                ret
            }
        }

        impl ::std::ops::Sub<<$projective as CurveProjective>::Affine> for $projective {
            type Output = Self;

            #[inline]
            fn sub(self, other: <$projective as CurveProjective>::Affine) -> Self {
                self - &other
            }
        }

        impl<'r> ::std::ops::SubAssign<&'r <$projective as CurveProjective>::Affine>
            for $projective
        {
            fn sub_assign(&mut self, other: &<$projective as CurveProjective>::Affine) {
                self.add_assign(&other.neg());
            }
        }

        impl ::std::ops::SubAssign<<$projective as CurveProjective>::Affine> for $projective {
            #[inline]
            fn sub_assign(&mut self, other: <$projective as CurveProjective>::Affine) {
                self.sub_assign(&other);
            }
        }

        impl CurveProjective for $projective {
            type Engine = Bls12;
            type Scalar = $scalarfield;
            type Base = $basefield;
            type Affine = $affine;

            fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
                loop {
                    let x = $basefield::random(rng);
                    let greatest = rng.next_u32() % 2 != 0;

                    let p = $affine::get_point_from_x(x, greatest);
                    if p.is_some().into() {
                        let p = p.unwrap().scale_by_cofactor();

                        if !p.is_zero() {
                            return p;
                        }
                    }
                }
            }

            // The point at infinity is always represented by
            // Z = 0.
            fn zero() -> Self {
                $projective {
                    x: $basefield::zero(),
                    y: $basefield::one(),
                    z: $basefield::zero(),
                }
            }

            fn one() -> Self {
                $affine::one().into()
            }

            // The point at infinity is always represented by
            // Z = 0.
            fn is_zero(&self) -> bool {
                self.z.is_zero()
            }

            fn is_normalized(&self) -> bool {
                self.is_zero() || self.z == $basefield::one()
            }

            fn batch_normalization(v: &mut [Self]) {
                // Montgomery’s Trick and Fast Implementation of Masked AES
                // Genelle, Prouff and Quisquater
                // Section 3.2

                // First pass: compute [a, ab, abc, ...]
                let mut prod = Vec::with_capacity(v.len());
                let mut tmp = $basefield::one();
                for g in v
                    .iter_mut()
                    // Ignore normalized elements
                    .filter(|g| !g.is_normalized())
                {
                    tmp.mul_assign(&g.z);
                    prod.push(tmp);
                }

                // Invert `tmp`.
                tmp = tmp.invert().unwrap(); // Guaranteed to be nonzero.

                // Second pass: iterate backwards to compute inverses
                for (g, s) in v
                    .iter_mut()
                    // Backwards
                    .rev()
                    // Ignore normalized elements
                    .filter(|g| !g.is_normalized())
                    // Backwards, skip last element, fill in one for last term.
                    .zip(
                        prod.into_iter()
                            .rev()
                            .skip(1)
                            .chain(Some($basefield::one())),
                    )
                {
                    // tmp := tmp * g.z; g.z := tmp * s = 1/z
                    let mut newtmp = tmp;
                    newtmp.mul_assign(&g.z);
                    g.z = tmp;
                    g.z.mul_assign(&s);
                    tmp = newtmp;
                }

                // Perform affine transformations
                for g in v.iter_mut().filter(|g| !g.is_normalized()) {
                    let mut z = g.z.square(); // 1/z^2
                    g.x.mul_assign(&z); // x/z^2
                    z.mul_assign(&g.z); // 1/z^3
                    g.y.mul_assign(&z); // y/z^3
                    g.z = $basefield::one(); // z = 1
                }
            }

            fn double(&mut self) {
                if self.is_zero() {
                    return;
                }

                // Other than the point at infinity, no points on E or E'
                // can double to equal the point at infinity, as y=0 is
                // never true for points on the curve. (-4 and -4u-4
                // are not cubic residue in their respective fields.)

                // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l

                // A = X1^2
                let a = self.x.square();

                // B = Y1^2
                let b = self.y.square();

                // C = B^2
                let mut c = b.square();

                // D = 2*((X1+B)2-A-C)
                let mut d = self.x;
                d.add_assign(&b);
                d = d.square();
                d.sub_assign(&a);
                d.sub_assign(&c);
                d = d.double();

                // E = 3*A
                let mut e = a.double();
                e.add_assign(&a);

                // F = E^2
                let f = e.square();

                // Z3 = 2*Y1*Z1
                self.z.mul_assign(&self.y);
                self.z = self.z.double();

                // X3 = F-2*D
                self.x = f;
                self.x.sub_assign(&d);
                self.x.sub_assign(&d);

                // Y3 = E*(D-X3)-8*C
                self.y = d;
                self.y.sub_assign(&self.x);
                self.y.mul_assign(&e);
                c = c.double().double().double();
                self.y.sub_assign(&c);
            }

            fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) {
                let mut res = Self::zero();

                let mut found_one = false;

                for i in BitIterator::new(other.into()) {
                    if found_one {
                        res.double();
                    } else {
                        found_one = i;
                    }

                    if i {
                        res.add_assign(&*self);
                    }
                }

                *self = res;
            }

            fn into_affine(&self) -> $affine {
                (*self).into()
            }

            fn recommended_wnaf_for_scalar(scalar: <Self::Scalar as PrimeField>::Repr) -> usize {
                Self::empirical_recommended_wnaf_for_scalar(scalar)
            }

            fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
                Self::empirical_recommended_wnaf_for_num_scalars(num_scalars)
            }
        }

        // The affine point X, Y is represented in the jacobian
        // coordinates with Z = 1.
        impl From<$affine> for $projective {
            fn from(p: $affine) -> $projective {
                if p.is_zero() {
                    $projective::zero()
                } else {
                    $projective {
                        x: p.x,
                        y: p.y,
                        z: $basefield::one(),
                    }
                }
            }
        }

        // The projective point X, Y, Z is represented in the affine
        // coordinates as X/Z^2, Y/Z^3.
        impl From<$projective> for $affine {
            fn from(p: $projective) -> $affine {
                if p.is_zero() {
                    $affine::zero()
                } else if p.z == $basefield::one() {
                    // If Z is one, the point is already normalized.
                    $affine {
                        x: p.x,
                        y: p.y,
                        infinity: false,
                    }
                } else {
                    // Z is nonzero, so it must have an inverse in a field.
                    let zinv = p.z.invert().unwrap();
                    let mut zinv_powered = zinv.square();

                    // X/Z^2
                    let mut x = p.x;
                    x.mul_assign(&zinv_powered);

                    // Y/Z^3
                    let mut y = p.y;
                    zinv_powered.mul_assign(&zinv);
                    y.mul_assign(&zinv_powered);

                    $affine {
                        x: x,
                        y: y,
                        infinity: false,
                    }
                }
            }
        }
    };
}

pub mod g1 {
    use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr};
    use super::g2::G2Affine;
    use crate::{Engine, PairingCurveAffine};
    use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
    use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
    use rand_core::RngCore;
    use std::fmt;
    use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
    use subtle::CtOption;

    curve_impl!(
        "G1",
        G1,
        G1Affine,
        G1Prepared,
        Fq,
        Fr,
        G1Uncompressed,
        G1Compressed,
        G2Affine
    );

    #[derive(Copy, Clone)]
    pub struct G1Uncompressed([u8; 96]);

    impl AsRef<[u8]> for G1Uncompressed {
        fn as_ref(&self) -> &[u8] {
            &self.0
        }
    }

    impl AsMut<[u8]> for G1Uncompressed {
        fn as_mut(&mut self) -> &mut [u8] {
            &mut self.0
        }
    }

    impl fmt::Debug for G1Uncompressed {
        fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
            self.0[..].fmt(formatter)
        }
    }

    impl EncodedPoint for G1Uncompressed {
        type Affine = G1Affine;

        fn empty() -> Self {
            G1Uncompressed([0; 96])
        }
        fn size() -> usize {
            96
        }
        fn into_affine(&self) -> Result<G1Affine, GroupDecodingError> {
            let affine = self.into_affine_unchecked()?;

            if !affine.is_on_curve() {
                Err(GroupDecodingError::NotOnCurve)
            } else if !affine.is_in_correct_subgroup_assuming_on_curve() {
                Err(GroupDecodingError::NotInSubgroup)
            } else {
                Ok(affine)
            }
        }
        fn into_affine_unchecked(&self) -> Result<G1Affine, GroupDecodingError> {
            // Create a copy of this representation.
            let mut copy = self.0;

            if copy[0] & (1 << 7) != 0 {
                // Distinguisher bit is set, but this should be uncompressed!
                return Err(GroupDecodingError::UnexpectedCompressionMode);
            }

            if copy[0] & (1 << 6) != 0 {
                // This is the point at infinity, which means that if we mask away
                // the first two bits, the entire representation should consist
                // of zeroes.
                copy[0] &= 0x3f;

                if copy.iter().all(|b| *b == 0) {
                    Ok(G1Affine::zero())
                } else {
                    Err(GroupDecodingError::UnexpectedInformation)
                }
            } else {
                if copy[0] & (1 << 5) != 0 {
                    // The bit indicating the y-coordinate should be lexicographically
                    // largest is set, but this is an uncompressed element.
                    return Err(GroupDecodingError::UnexpectedInformation);
                }

                // Unset the three most significant bits.
                copy[0] &= 0x1f;

                let mut x = FqRepr([0; 6]);
                let mut y = FqRepr([0; 6]);

                {
                    let mut reader = &copy[..];

                    x.read_be(&mut reader).unwrap();
                    y.read_be(&mut reader).unwrap();
                }

                Ok(G1Affine {
                    x: Fq::from_repr(x).map_err(|e| {
                        GroupDecodingError::CoordinateDecodingError("x coordinate", e)
                    })?,
                    y: Fq::from_repr(y).map_err(|e| {
                        GroupDecodingError::CoordinateDecodingError("y coordinate", e)
                    })?,
                    infinity: false,
                })
            }
        }
        fn from_affine(affine: G1Affine) -> Self {
            let mut res = Self::empty();

            if affine.is_zero() {
                // Set the second-most significant bit to indicate this point
                // is at infinity.
                res.0[0] |= 1 << 6;
            } else {
                let mut writer = &mut res.0[..];

                affine.x.into_repr().write_be(&mut writer).unwrap();
                affine.y.into_repr().write_be(&mut writer).unwrap();
            }

            res
        }
    }

    #[derive(Copy, Clone)]
    pub struct G1Compressed([u8; 48]);

    impl AsRef<[u8]> for G1Compressed {
        fn as_ref(&self) -> &[u8] {
            &self.0
        }
    }

    impl AsMut<[u8]> for G1Compressed {
        fn as_mut(&mut self) -> &mut [u8] {
            &mut self.0
        }
    }

    impl fmt::Debug for G1Compressed {
        fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
            self.0[..].fmt(formatter)
        }
    }

    impl EncodedPoint for G1Compressed {
        type Affine = G1Affine;

        fn empty() -> Self {
            G1Compressed([0; 48])
        }
        fn size() -> usize {
            48
        }
        fn into_affine(&self) -> Result<G1Affine, GroupDecodingError> {
            let affine = self.into_affine_unchecked()?;

            // NB: Decompression guarantees that it is on the curve already.

            if !affine.is_in_correct_subgroup_assuming_on_curve() {
                Err(GroupDecodingError::NotInSubgroup)
            } else {
                Ok(affine)
            }
        }
        fn into_affine_unchecked(&self) -> Result<G1Affine, GroupDecodingError> {
            // Create a copy of this representation.
            let mut copy = self.0;

            if copy[0] & (1 << 7) == 0 {
                // Distinguisher bit isn't set.
                return Err(GroupDecodingError::UnexpectedCompressionMode);
            }

            if copy[0] & (1 << 6) != 0 {
                // This is the point at infinity, which means that if we mask away
                // the first two bits, the entire representation should consist
                // of zeroes.
                copy[0] &= 0x3f;

                if copy.iter().all(|b| *b == 0) {
                    Ok(G1Affine::zero())
                } else {
                    Err(GroupDecodingError::UnexpectedInformation)
                }
            } else {
                // Determine if the intended y coordinate must be greater
                // lexicographically.
                let greatest = copy[0] & (1 << 5) != 0;

                // Unset the three most significant bits.
                copy[0] &= 0x1f;

                let mut x = FqRepr([0; 6]);

                {
                    let mut reader = &copy[..];

                    x.read_be(&mut reader).unwrap();
                }

                // Interpret as Fq element.
                let x = Fq::from_repr(x)
                    .map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?;

                let ret = G1Affine::get_point_from_x(x, greatest);
                if ret.is_some().into() {
                    Ok(ret.unwrap())
                } else {
                    Err(GroupDecodingError::NotOnCurve)
                }
            }
        }
        fn from_affine(affine: G1Affine) -> Self {
            let mut res = Self::empty();

            if affine.is_zero() {
                // Set the second-most significant bit to indicate this point
                // is at infinity.
                res.0[0] |= 1 << 6;
            } else {
                {
                    let mut writer = &mut res.0[..];

                    affine.x.into_repr().write_be(&mut writer).unwrap();
                }

                let negy = affine.y.neg();

                // Set the third most significant bit if the correct y-coordinate
                // is lexicographically largest.
                if affine.y > negy {
                    res.0[0] |= 1 << 5;
                }
            }

            // Set highest bit to distinguish this as a compressed element.
            res.0[0] |= 1 << 7;

            res
        }
    }

    impl G1Affine {
        fn scale_by_cofactor(&self) -> G1 {
            // G1 cofactor = (x - 1)^2 / 3  = 76329603384216526031706109802092473003
            let cofactor = BitIterator::new([0x8c00aaab0000aaab, 0x396c8c005555e156]);
            self.mul_bits(cofactor)
        }

        fn get_generator() -> Self {
            G1Affine {
                x: super::super::fq::G1_GENERATOR_X,
                y: super::super::fq::G1_GENERATOR_Y,
                infinity: false,
            }
        }

        fn get_coeff_b() -> Fq {
            super::super::fq::B_COEFF
        }

        fn perform_pairing(&self, other: &G2Affine) -> Fq12 {
            super::super::Bls12::pairing(*self, *other)
        }
    }

    impl G1 {
        fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize {
            let num_bits = scalar.num_bits() as usize;

            if num_bits >= 130 {
                4
            } else if num_bits >= 34 {
                3
            } else {
                2
            }
        }

        fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
            const RECOMMENDATIONS: [usize; 12] =
                [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569];

            let mut ret = 4;
            for r in &RECOMMENDATIONS {
                if num_scalars > *r {
                    ret += 1;
                } else {
                    break;
                }
            }

            ret
        }
    }

    #[derive(Clone, Debug)]
    pub struct G1Prepared(pub(crate) G1Affine);

    impl G1Prepared {
        pub fn is_zero(&self) -> bool {
            self.0.is_zero()
        }

        pub fn from_affine(p: G1Affine) -> Self {
            G1Prepared(p)
        }
    }

    #[test]
    fn g1_generator() {
        use crate::SqrtField;

        let mut x = Fq::zero();
        let mut i = 0;
        loop {
            // y^2 = x^3 + b
            let mut rhs = x.square();
            rhs.mul_assign(&x);
            rhs.add_assign(&G1Affine::get_coeff_b());

            let y = rhs.sqrt();
            if y.is_some().into() {
                let y = y.unwrap();
                let yrepr = y.into_repr();
                let negy = y.neg();
                let negyrepr = negy.into_repr();

                let p = G1Affine {
                    x,
                    y: if yrepr < negyrepr { y } else { negy },
                    infinity: false,
                };
                assert!(!p.is_in_correct_subgroup_assuming_on_curve());

                let g1 = p.scale_by_cofactor();
                if !g1.is_zero() {
                    assert_eq!(i, 4);
                    let g1 = G1Affine::from(g1);

                    assert!(g1.is_in_correct_subgroup_assuming_on_curve());

                    assert_eq!(g1, G1Affine::one());
                    break;
                }
            }

            i += 1;
            x.add_assign(&Fq::one());
        }
    }

    #[test]
    fn g1_test_is_valid() {
        // Reject point on isomorphic twist (b = 24)
        {
            let p = G1Affine {
                x: Fq::from_repr(FqRepr([
                    0xc58d887b66c035dc,
                    0x10cbfd301d553822,
                    0xaf23e064f1131ee5,
                    0x9fe83b1b4a5d648d,
                    0xf583cc5a508f6a40,
                    0xc3ad2aefde0bb13,
                ]))
                .unwrap(),
                y: Fq::from_repr(FqRepr([
                    0x60aa6f9552f03aae,
                    0xecd01d5181300d35,
                    0x8af1cdb8aa8ce167,
                    0xe760f57922998c9d,
                    0x953703f5795a39e5,
                    0xfe3ae0922df702c,
                ]))
                .unwrap(),
                infinity: false,
            };
            assert!(!p.is_on_curve());
            assert!(p.is_in_correct_subgroup_assuming_on_curve());
        }

        // Reject point on a twist (b = 3)
        {
            let p = G1Affine {
                x: Fq::from_repr(FqRepr([
                    0xee6adf83511e15f5,
                    0x92ddd328f27a4ba6,
                    0xe305bd1ac65adba7,
                    0xea034ee2928b30a8,
                    0xbd8833dc7c79a7f7,
                    0xe45c9f0c0438675,
                ]))
                .unwrap(),
                y: Fq::from_repr(FqRepr([
                    0x3b450eb1ab7b5dad,
                    0xa65cb81e975e8675,
                    0xaa548682b21726e5,
                    0x753ddf21a2601d20,
                    0x532d0b640bd3ff8b,
                    0x118d2c543f031102,
                ]))
                .unwrap(),
                infinity: false,
            };
            assert!(!p.is_on_curve());
            assert!(!p.is_in_correct_subgroup_assuming_on_curve());
        }

        // Reject point in an invalid subgroup
        // There is only one r-order subgroup, as r does not divide the cofactor.
        {
            let p = G1Affine {
                x: Fq::from_repr(FqRepr([
                    0x76e1c971c6db8fe8,
                    0xe37e1a610eff2f79,
                    0x88ae9c499f46f0c0,
                    0xf35de9ce0d6b4e84,
                    0x265bddd23d1dec54,
                    0x12a8778088458308,
                ]))
                .unwrap(),
                y: Fq::from_repr(FqRepr([
                    0x8a22defa0d526256,
                    0xc57ca55456fcb9ae,
                    0x1ba194e89bab2610,
                    0x921beef89d4f29df,
                    0x5b6fda44ad85fa78,
                    0xed74ab9f302cbe0,
                ]))
                .unwrap(),
                infinity: false,
            };
            assert!(p.is_on_curve());
            assert!(!p.is_in_correct_subgroup_assuming_on_curve());
        }
    }

    #[test]
    fn test_g1_addition_correctness() {
        let mut p = G1 {
            x: Fq::from_repr(FqRepr([
                0x47fd1f891d6e8bbf,
                0x79a3b0448f31a2aa,
                0x81f3339e5f9968f,
                0x485e77d50a5df10d,
                0x4c6fcac4b55fd479,
                0x86ed4d9906fb064,
            ]))
            .unwrap(),
            y: Fq::from_repr(FqRepr([
                0xd25ee6461538c65,
                0x9f3bbb2ecd3719b9,
                0xa06fd3f1e540910d,
                0xcefca68333c35288,
                0x570c8005f8573fa6,
                0x152ca696fe034442,
            ]))
            .unwrap(),
            z: Fq::one(),
        };

        p.add_assign(&G1 {
            x: Fq::from_repr(FqRepr([
                0xeec78f3096213cbf,
                0xa12beb1fea1056e6,
                0xc286c0211c40dd54,
                0x5f44314ec5e3fb03,
                0x24e8538737c6e675,
                0x8abd623a594fba8,
            ]))
            .unwrap(),
            y: Fq::from_repr(FqRepr([
                0x6b0528f088bb7044,
                0x2fdeb5c82917ff9e,
                0x9a5181f2fac226ad,
                0xd65104c6f95a872a,
                0x1f2998a5a9c61253,
                0xe74846154a9e44,
            ]))
            .unwrap(),
            z: Fq::one(),
        });

        let p = G1Affine::from(p);

        assert_eq!(
            p,
            G1Affine {
                x: Fq::from_repr(FqRepr([
                    0x6dd3098f22235df,
                    0xe865d221c8090260,
                    0xeb96bb99fa50779f,
                    0xc4f9a52a428e23bb,
                    0xd178b28dd4f407ef,
                    0x17fb8905e9183c69
                ]))
                .unwrap(),
                y: Fq::from_repr(FqRepr([
                    0xd0de9d65292b7710,
                    0xf6a05f2bcf1d9ca7,
                    0x1040e27012f20b64,
                    0xeec8d1a5b7466c58,
                    0x4bc362649dce6376,
                    0x430cbdc5455b00a
                ]))
                .unwrap(),
                infinity: false,
            }
        );
    }

    #[test]
    fn test_g1_doubling_correctness() {
        let mut p = G1 {
            x: Fq::from_repr(FqRepr([
                0x47fd1f891d6e8bbf,
                0x79a3b0448f31a2aa,
                0x81f3339e5f9968f,
                0x485e77d50a5df10d,
                0x4c6fcac4b55fd479,
                0x86ed4d9906fb064,
            ]))
            .unwrap(),
            y: Fq::from_repr(FqRepr([
                0xd25ee6461538c65,
                0x9f3bbb2ecd3719b9,
                0xa06fd3f1e540910d,
                0xcefca68333c35288,
                0x570c8005f8573fa6,
                0x152ca696fe034442,
            ]))
            .unwrap(),
            z: Fq::one(),
        };

        p.double();

        let p = G1Affine::from(p);

        assert_eq!(
            p,
            G1Affine {
                x: Fq::from_repr(FqRepr([
                    0xf939ddfe0ead7018,
                    0x3b03942e732aecb,
                    0xce0e9c38fdb11851,
                    0x4b914c16687dcde0,
                    0x66c8baf177d20533,
                    0xaf960cff3d83833
                ]))
                .unwrap(),
                y: Fq::from_repr(FqRepr([
                    0x3f0675695f5177a8,
                    0x2b6d82ae178a1ba0,
                    0x9096380dd8e51b11,
                    0x1771a65b60572f4e,
                    0x8b547c1313b27555,
                    0x135075589a687b1e
                ]))
                .unwrap(),
                infinity: false,
            }
        );
    }

    #[test]
    fn test_g1_same_y() {
        // Test the addition of two points with different x coordinates
        // but the same y coordinate.

        // x1 = 128100205326445210408953809171070606737678357140298133325128175840781723996595026100005714405541449960643523234125
        // x2 = 3821408151224848222394078037104966877485040835569514006839342061575586899845797797516352881516922679872117658572470
        // y = 2291134451313223670499022936083127939567618746216464377735567679979105510603740918204953301371880765657042046687078

        let a = G1Affine {
            x: Fq::from_repr(FqRepr([
                0xea431f2cc38fc94d,
                0x3ad2354a07f5472b,
                0xfe669f133f16c26a,
                0x71ffa8021531705,
                0x7418d484386d267,
                0xd5108d8ff1fbd6,
            ]))
            .unwrap(),
            y: Fq::from_repr(FqRepr([
                0xa776ccbfe9981766,
                0x255632964ff40f4a,
                0xc09744e650b00499,
                0x520f74773e74c8c3,
                0x484c8fc982008f0,
                0xee2c3d922008cc6,
            ]))
            .unwrap(),
            infinity: false,
        };

        let b = G1Affine {
            x: Fq::from_repr(FqRepr([
                0xe06cdb156b6356b6,
                0xd9040b2d75448ad9,
                0xe702f14bb0e2aca5,
                0xc6e05201e5f83991,
                0xf7c75910816f207c,
                0x18d4043e78103106,
            ]))
            .unwrap(),
            y: Fq::from_repr(FqRepr([
                0xa776ccbfe9981766,
                0x255632964ff40f4a,
                0xc09744e650b00499,
                0x520f74773e74c8c3,
                0x484c8fc982008f0,
                0xee2c3d922008cc6,
            ]))
            .unwrap(),
            infinity: false,
        };

        // Expected
        // x = 52901198670373960614757979459866672334163627229195745167587898707663026648445040826329033206551534205133090753192
        // y = 1711275103908443722918766889652776216989264073722543507596490456144926139887096946237734327757134898380852225872709
        let c = G1Affine {
            x: Fq::from_repr(FqRepr([
                0xef4f05bdd10c8aa8,
                0xad5bf87341a2df9,
                0x81c7424206b78714,
                0x9676ff02ec39c227,
                0x4c12c15d7e55b9f3,
                0x57fd1e317db9bd,
            ]))
            .unwrap(),
            y: Fq::from_repr(FqRepr([
                0x1288334016679345,
                0xf955cd68615ff0b5,
                0xa6998dbaa600f18a,
                0x1267d70db51049fb,
                0x4696deb9ab2ba3e7,
                0xb1e4e11177f59d4,
            ]))
            .unwrap(),
            infinity: false,
        };

        assert!(a.is_on_curve() && a.is_in_correct_subgroup_assuming_on_curve());
        assert!(b.is_on_curve() && b.is_in_correct_subgroup_assuming_on_curve());
        assert!(c.is_on_curve() && c.is_in_correct_subgroup_assuming_on_curve());

        let mut tmp1 = a.into_projective();
        tmp1.add_assign(&b.into_projective());
        assert_eq!(tmp1.into_affine(), c);
        assert_eq!(tmp1, c.into_projective());

        let mut tmp2 = a.into_projective();
        tmp2.add_assign(&b);
        assert_eq!(tmp2.into_affine(), c);
        assert_eq!(tmp2, c.into_projective());
    }

    #[test]
    fn g1_curve_tests() {
        use group::tests::curve_tests;
        curve_tests::<G1>();
    }
}

pub mod g2 {
    use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr};
    use super::g1::G1Affine;
    use crate::{Engine, PairingCurveAffine};
    use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
    use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
    use rand_core::RngCore;
    use std::fmt;
    use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
    use subtle::CtOption;

    curve_impl!(
        "G2",
        G2,
        G2Affine,
        G2Prepared,
        Fq2,
        Fr,
        G2Uncompressed,
        G2Compressed,
        G1Affine
    );

    #[derive(Copy, Clone)]
    pub struct G2Uncompressed([u8; 192]);

    impl AsRef<[u8]> for G2Uncompressed {
        fn as_ref(&self) -> &[u8] {
            &self.0
        }
    }

    impl AsMut<[u8]> for G2Uncompressed {
        fn as_mut(&mut self) -> &mut [u8] {
            &mut self.0
        }
    }

    impl fmt::Debug for G2Uncompressed {
        fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
            self.0[..].fmt(formatter)
        }
    }

    impl EncodedPoint for G2Uncompressed {
        type Affine = G2Affine;

        fn empty() -> Self {
            G2Uncompressed([0; 192])
        }
        fn size() -> usize {
            192
        }
        fn into_affine(&self) -> Result<G2Affine, GroupDecodingError> {
            let affine = self.into_affine_unchecked()?;

            if !affine.is_on_curve() {
                Err(GroupDecodingError::NotOnCurve)
            } else if !affine.is_in_correct_subgroup_assuming_on_curve() {
                Err(GroupDecodingError::NotInSubgroup)
            } else {
                Ok(affine)
            }
        }
        fn into_affine_unchecked(&self) -> Result<G2Affine, GroupDecodingError> {
            // Create a copy of this representation.
            let mut copy = self.0;

            if copy[0] & (1 << 7) != 0 {
                // Distinguisher bit is set, but this should be uncompressed!
                return Err(GroupDecodingError::UnexpectedCompressionMode);
            }

            if copy[0] & (1 << 6) != 0 {
                // This is the point at infinity, which means that if we mask away
                // the first two bits, the entire representation should consist
                // of zeroes.
                copy[0] &= 0x3f;

                if copy.iter().all(|b| *b == 0) {
                    Ok(G2Affine::zero())
                } else {
                    Err(GroupDecodingError::UnexpectedInformation)
                }
            } else {
                if copy[0] & (1 << 5) != 0 {
                    // The bit indicating the y-coordinate should be lexicographically
                    // largest is set, but this is an uncompressed element.
                    return Err(GroupDecodingError::UnexpectedInformation);
                }

                // Unset the three most significant bits.
                copy[0] &= 0x1f;

                let mut x_c0 = FqRepr([0; 6]);
                let mut x_c1 = FqRepr([0; 6]);
                let mut y_c0 = FqRepr([0; 6]);
                let mut y_c1 = FqRepr([0; 6]);

                {
                    let mut reader = &copy[..];

                    x_c1.read_be(&mut reader).unwrap();
                    x_c0.read_be(&mut reader).unwrap();
                    y_c1.read_be(&mut reader).unwrap();
                    y_c0.read_be(&mut reader).unwrap();
                }

                Ok(G2Affine {
                    x: Fq2 {
                        c0: Fq::from_repr(x_c0).map_err(|e| {
                            GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e)
                        })?,
                        c1: Fq::from_repr(x_c1).map_err(|e| {
                            GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e)
                        })?,
                    },
                    y: Fq2 {
                        c0: Fq::from_repr(y_c0).map_err(|e| {
                            GroupDecodingError::CoordinateDecodingError("y coordinate (c0)", e)
                        })?,
                        c1: Fq::from_repr(y_c1).map_err(|e| {
                            GroupDecodingError::CoordinateDecodingError("y coordinate (c1)", e)
                        })?,
                    },
                    infinity: false,
                })
            }
        }
        fn from_affine(affine: G2Affine) -> Self {
            let mut res = Self::empty();

            if affine.is_zero() {
                // Set the second-most significant bit to indicate this point
                // is at infinity.
                res.0[0] |= 1 << 6;
            } else {
                let mut writer = &mut res.0[..];

                affine.x.c1.into_repr().write_be(&mut writer).unwrap();
                affine.x.c0.into_repr().write_be(&mut writer).unwrap();
                affine.y.c1.into_repr().write_be(&mut writer).unwrap();
                affine.y.c0.into_repr().write_be(&mut writer).unwrap();
            }

            res
        }
    }

    #[derive(Copy, Clone)]
    pub struct G2Compressed([u8; 96]);

    impl AsRef<[u8]> for G2Compressed {
        fn as_ref(&self) -> &[u8] {
            &self.0
        }
    }

    impl AsMut<[u8]> for G2Compressed {
        fn as_mut(&mut self) -> &mut [u8] {
            &mut self.0
        }
    }

    impl fmt::Debug for G2Compressed {
        fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
            self.0[..].fmt(formatter)
        }
    }

    impl EncodedPoint for G2Compressed {
        type Affine = G2Affine;

        fn empty() -> Self {
            G2Compressed([0; 96])
        }
        fn size() -> usize {
            96
        }
        fn into_affine(&self) -> Result<G2Affine, GroupDecodingError> {
            let affine = self.into_affine_unchecked()?;

            // NB: Decompression guarantees that it is on the curve already.

            if !affine.is_in_correct_subgroup_assuming_on_curve() {
                Err(GroupDecodingError::NotInSubgroup)
            } else {
                Ok(affine)
            }
        }
        fn into_affine_unchecked(&self) -> Result<G2Affine, GroupDecodingError> {
            // Create a copy of this representation.
            let mut copy = self.0;

            if copy[0] & (1 << 7) == 0 {
                // Distinguisher bit isn't set.
                return Err(GroupDecodingError::UnexpectedCompressionMode);
            }

            if copy[0] & (1 << 6) != 0 {
                // This is the point at infinity, which means that if we mask away
                // the first two bits, the entire representation should consist
                // of zeroes.
                copy[0] &= 0x3f;

                if copy.iter().all(|b| *b == 0) {
                    Ok(G2Affine::zero())
                } else {
                    Err(GroupDecodingError::UnexpectedInformation)
                }
            } else {
                // Determine if the intended y coordinate must be greater
                // lexicographically.
                let greatest = copy[0] & (1 << 5) != 0;

                // Unset the three most significant bits.
                copy[0] &= 0x1f;

                let mut x_c1 = FqRepr([0; 6]);
                let mut x_c0 = FqRepr([0; 6]);

                {
                    let mut reader = &copy[..];

                    x_c1.read_be(&mut reader).unwrap();
                    x_c0.read_be(&mut reader).unwrap();
                }

                // Interpret as Fq element.
                let x = Fq2 {
                    c0: Fq::from_repr(x_c0).map_err(|e| {
                        GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e)
                    })?,
                    c1: Fq::from_repr(x_c1).map_err(|e| {
                        GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e)
                    })?,
                };

                let ret = G2Affine::get_point_from_x(x, greatest);
                if ret.is_some().into() {
                    Ok(ret.unwrap())
                } else {
                    Err(GroupDecodingError::NotOnCurve)
                }
            }
        }
        fn from_affine(affine: G2Affine) -> Self {
            let mut res = Self::empty();

            if affine.is_zero() {
                // Set the second-most significant bit to indicate this point
                // is at infinity.
                res.0[0] |= 1 << 6;
            } else {
                {
                    let mut writer = &mut res.0[..];

                    affine.x.c1.into_repr().write_be(&mut writer).unwrap();
                    affine.x.c0.into_repr().write_be(&mut writer).unwrap();
                }

                let negy = affine.y.neg();

                // Set the third most significant bit if the correct y-coordinate
                // is lexicographically largest.
                if affine.y > negy {
                    res.0[0] |= 1 << 5;
                }
            }

            // Set highest bit to distinguish this as a compressed element.
            res.0[0] |= 1 << 7;

            res
        }
    }

    impl G2Affine {
        fn get_generator() -> Self {
            G2Affine {
                x: Fq2 {
                    c0: super::super::fq::G2_GENERATOR_X_C0,
                    c1: super::super::fq::G2_GENERATOR_X_C1,
                },
                y: Fq2 {
                    c0: super::super::fq::G2_GENERATOR_Y_C0,
                    c1: super::super::fq::G2_GENERATOR_Y_C1,
                },
                infinity: false,
            }
        }

        fn get_coeff_b() -> Fq2 {
            Fq2 {
                c0: super::super::fq::B_COEFF,
                c1: super::super::fq::B_COEFF,
            }
        }

        fn scale_by_cofactor(&self) -> G2 {
            // G2 cofactor = (x^8 - 4 x^7 + 5 x^6) - (4 x^4 + 6 x^3 - 4 x^2 - 4 x + 13) // 9
            // 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5
            let cofactor = BitIterator::new([
                0xcf1c38e31c7238e5,
                0x1616ec6e786f0c70,
                0x21537e293a6691ae,
                0xa628f1cb4d9e82ef,
                0xa68a205b2e5a7ddf,
                0xcd91de4547085aba,
                0x91d50792876a202,
                0x5d543a95414e7f1,
            ]);
            self.mul_bits(cofactor)
        }

        fn perform_pairing(&self, other: &G1Affine) -> Fq12 {
            super::super::Bls12::pairing(*other, *self)
        }
    }

    impl G2 {
        fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize {
            let num_bits = scalar.num_bits() as usize;

            if num_bits >= 103 {
                4
            } else if num_bits >= 37 {
                3
            } else {
                2
            }
        }

        fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
            const RECOMMENDATIONS: [usize; 11] =
                [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071];

            let mut ret = 4;
            for r in &RECOMMENDATIONS {
                if num_scalars > *r {
                    ret += 1;
                } else {
                    break;
                }
            }

            ret
        }
    }

    #[derive(Clone, Debug)]
    pub struct G2Prepared {
        pub(crate) coeffs: Vec<(Fq2, Fq2, Fq2)>,
        pub(crate) infinity: bool,
    }

    #[test]
    fn g2_generator() {
        use crate::SqrtField;

        let mut x = Fq2::zero();
        let mut i = 0;
        loop {
            // y^2 = x^3 + b
            let mut rhs = x.square();
            rhs.mul_assign(&x);
            rhs.add_assign(&G2Affine::get_coeff_b());

            let y = rhs.sqrt();
            if y.is_some().into() {
                let y = y.unwrap();
                let negy = y.neg();

                let p = G2Affine {
                    x,
                    y: if y < negy { y } else { negy },
                    infinity: false,
                };

                assert!(!p.is_in_correct_subgroup_assuming_on_curve());

                let g2 = p.scale_by_cofactor();
                if !g2.is_zero() {
                    assert_eq!(i, 2);
                    let g2 = G2Affine::from(g2);

                    assert!(g2.is_in_correct_subgroup_assuming_on_curve());
                    assert_eq!(g2, G2Affine::one());
                    break;
                }
            }

            i += 1;
            x.add_assign(&Fq2::one());
        }
    }

    #[test]
    fn g2_test_is_valid() {
        // Reject point on isomorphic twist (b = 3 * (u + 1))
        {
            let p = G2Affine {
                x: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0xa757072d9fa35ba9,
                        0xae3fb2fb418f6e8a,
                        0xc1598ec46faa0c7c,
                        0x7a17a004747e3dbe,
                        0xcc65406a7c2e5a73,
                        0x10b8c03d64db4d0c,
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0xd30e70fe2f029778,
                        0xda30772df0f5212e,
                        0x5b47a9ff9a233a50,
                        0xfb777e5b9b568608,
                        0x789bac1fec71a2b9,
                        0x1342f02e2da54405,
                    ]))
                    .unwrap(),
                },
                y: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0xfe0812043de54dca,
                        0xe455171a3d47a646,
                        0xa493f36bc20be98a,
                        0x663015d9410eb608,
                        0x78e82a79d829a544,
                        0x40a00545bb3c1e,
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0x4709802348e79377,
                        0xb5ac4dc9204bcfbd,
                        0xda361c97d02f42b2,
                        0x15008b1dc399e8df,
                        0x68128fd0548a3829,
                        0x16a613db5c873aaa,
                    ]))
                    .unwrap(),
                },
                infinity: false,
            };
            assert!(!p.is_on_curve());
            assert!(p.is_in_correct_subgroup_assuming_on_curve());
        }

        // Reject point on a twist (b = 2 * (u + 1))
        {
            let p = G2Affine {
                x: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0xf4fdfe95a705f917,
                        0xc2914df688233238,
                        0x37c6b12cca35a34b,
                        0x41abba710d6c692c,
                        0xffcc4b2b62ce8484,
                        0x6993ec01b8934ed,
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0xb94e92d5f874e26,
                        0x44516408bc115d95,
                        0xe93946b290caa591,
                        0xa5a0c2b7131f3555,
                        0x83800965822367e7,
                        0x10cf1d3ad8d90bfa,
                    ]))
                    .unwrap(),
                },
                y: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0xbf00334c79701d97,
                        0x4fe714f9ff204f9a,
                        0xab70b28002f3d825,
                        0x5a9171720e73eb51,
                        0x38eb4fd8d658adb7,
                        0xb649051bbc1164d,
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0x9225814253d7df75,
                        0xc196c2513477f887,
                        0xe05e2fbd15a804e0,
                        0x55f2b8efad953e04,
                        0x7379345eda55265e,
                        0x377f2e6208fd4cb,
                    ]))
                    .unwrap(),
                },
                infinity: false,
            };
            assert!(!p.is_on_curve());
            assert!(!p.is_in_correct_subgroup_assuming_on_curve());
        }

        // Reject point in an invalid subgroup
        // There is only one r-order subgroup, as r does not divide the cofactor.
        {
            let p = G2Affine {
                x: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0x262cea73ea1906c,
                        0x2f08540770fabd6,
                        0x4ceb92d0a76057be,
                        0x2199bc19c48c393d,
                        0x4a151b732a6075bf,
                        0x17762a3b9108c4a7,
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0x26f461e944bbd3d1,
                        0x298f3189a9cf6ed6,
                        0x74328ad8bc2aa150,
                        0x7e147f3f9e6e241,
                        0x72a9b63583963fff,
                        0x158b0083c000462,
                    ]))
                    .unwrap(),
                },
                y: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0x91fb0b225ecf103b,
                        0x55d42edc1dc46ba0,
                        0x43939b11997b1943,
                        0x68cad19430706b4d,
                        0x3ccfb97b924dcea8,
                        0x1660f93434588f8d,
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0xaaed3985b6dcb9c7,
                        0xc1e985d6d898d9f4,
                        0x618bd2ac3271ac42,
                        0x3940a2dbb914b529,
                        0xbeb88137cf34f3e7,
                        0x1699ee577c61b694,
                    ]))
                    .unwrap(),
                },
                infinity: false,
            };
            assert!(p.is_on_curve());
            assert!(!p.is_in_correct_subgroup_assuming_on_curve());
        }
    }

    #[test]
    fn test_g2_addition_correctness() {
        let mut p = G2 {
            x: Fq2 {
                c0: Fq::from_repr(FqRepr([
                    0x6c994cc1e303094e,
                    0xf034642d2c9e85bd,
                    0x275094f1352123a9,
                    0x72556c999f3707ac,
                    0x4617f2e6774e9711,
                    0x100b2fe5bffe030b,
                ]))
                .unwrap(),
                c1: Fq::from_repr(FqRepr([
                    0x7a33555977ec608,
                    0xe23039d1fe9c0881,
                    0x19ce4678aed4fcb5,
                    0x4637c4f417667e2e,
                    0x93ebe7c3e41f6acc,
                    0xde884f89a9a371b,
                ]))
                .unwrap(),
            },
            y: Fq2 {
                c0: Fq::from_repr(FqRepr([
                    0xe073119472e1eb62,
                    0x44fb3391fe3c9c30,
                    0xaa9b066d74694006,
                    0x25fd427b4122f231,
                    0xd83112aace35cae,
                    0x191b2432407cbb7f,
                ]))
                .unwrap(),
                c1: Fq::from_repr(FqRepr([
                    0xf68ae82fe97662f5,
                    0xe986057068b50b7d,
                    0x96c30f0411590b48,
                    0x9eaa6d19de569196,
                    0xf6a03d31e2ec2183,
                    0x3bdafaf7ca9b39b,
                ]))
                .unwrap(),
            },
            z: Fq2::one(),
        };

        p.add_assign(&G2 {
            x: Fq2 {
                c0: Fq::from_repr(FqRepr([
                    0xa8c763d25910bdd3,
                    0x408777b30ca3add4,
                    0x6115fcc12e2769e,
                    0x8e73a96b329ad190,
                    0x27c546f75ee1f3ab,
                    0xa33d27add5e7e82,
                ]))
                .unwrap(),
                c1: Fq::from_repr(FqRepr([
                    0x93b1ebcd54870dfe,
                    0xf1578300e1342e11,
                    0x8270dca3a912407b,
                    0x2089faf462438296,
                    0x828e5848cd48ea66,
                    0x141ecbac1deb038b,
                ]))
                .unwrap(),
            },
            y: Fq2 {
                c0: Fq::from_repr(FqRepr([
                    0xf5d2c28857229c3f,
                    0x8c1574228757ca23,
                    0xe8d8102175f5dc19,
                    0x2767032fc37cc31d,
                    0xd5ee2aba84fd10fe,
                    0x16576ccd3dd0a4e8,
                ]))
                .unwrap(),
                c1: Fq::from_repr(FqRepr([
                    0x4da9b6f6a96d1dd2,
                    0x9657f7da77f1650e,
                    0xbc150712f9ffe6da,
                    0x31898db63f87363a,
                    0xabab040ddbd097cc,
                    0x11ad236b9ba02990,
                ]))
                .unwrap(),
            },
            z: Fq2::one(),
        });

        let p = G2Affine::from(p);

        assert_eq!(
            p,
            G2Affine {
                x: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0xcde7ee8a3f2ac8af,
                        0xfc642eb35975b069,
                        0xa7de72b7dd0e64b7,
                        0xf1273e6406eef9cc,
                        0xababd760ff05cb92,
                        0xd7c20456617e89
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0xd1a50b8572cbd2b8,
                        0x238f0ac6119d07df,
                        0x4dbe924fe5fd6ac2,
                        0x8b203284c51edf6b,
                        0xc8a0b730bbb21f5e,
                        0x1a3b59d29a31274
                    ]))
                    .unwrap(),
                },
                y: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0x9e709e78a8eaa4c9,
                        0xd30921c93ec342f4,
                        0x6d1ef332486f5e34,
                        0x64528ab3863633dc,
                        0x159384333d7cba97,
                        0x4cb84741f3cafe8
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0x242af0dc3640e1a4,
                        0xe90a73ad65c66919,
                        0x2bd7ca7f4346f9ec,
                        0x38528f92b689644d,
                        0xb6884deec59fb21f,
                        0x3c075d3ec52ba90
                    ]))
                    .unwrap(),
                },
                infinity: false,
            }
        );
    }

    #[test]
    fn test_g2_doubling_correctness() {
        let mut p = G2 {
            x: Fq2 {
                c0: Fq::from_repr(FqRepr([
                    0x6c994cc1e303094e,
                    0xf034642d2c9e85bd,
                    0x275094f1352123a9,
                    0x72556c999f3707ac,
                    0x4617f2e6774e9711,
                    0x100b2fe5bffe030b,
                ]))
                .unwrap(),
                c1: Fq::from_repr(FqRepr([
                    0x7a33555977ec608,
                    0xe23039d1fe9c0881,
                    0x19ce4678aed4fcb5,
                    0x4637c4f417667e2e,
                    0x93ebe7c3e41f6acc,
                    0xde884f89a9a371b,
                ]))
                .unwrap(),
            },
            y: Fq2 {
                c0: Fq::from_repr(FqRepr([
                    0xe073119472e1eb62,
                    0x44fb3391fe3c9c30,
                    0xaa9b066d74694006,
                    0x25fd427b4122f231,
                    0xd83112aace35cae,
                    0x191b2432407cbb7f,
                ]))
                .unwrap(),
                c1: Fq::from_repr(FqRepr([
                    0xf68ae82fe97662f5,
                    0xe986057068b50b7d,
                    0x96c30f0411590b48,
                    0x9eaa6d19de569196,
                    0xf6a03d31e2ec2183,
                    0x3bdafaf7ca9b39b,
                ]))
                .unwrap(),
            },
            z: Fq2::one(),
        };

        p.double();

        let p = G2Affine::from(p);

        assert_eq!(
            p,
            G2Affine {
                x: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0x91ccb1292727c404,
                        0x91a6cb182438fad7,
                        0x116aee59434de902,
                        0xbcedcfce1e52d986,
                        0x9755d4a3926e9862,
                        0x18bab73760fd8024
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0x4e7c5e0a2ae5b99e,
                        0x96e582a27f028961,
                        0xc74d1cf4ef2d5926,
                        0xeb0cf5e610ef4fe7,
                        0x7b4c2bae8db6e70b,
                        0xf136e43909fca0
                    ]))
                    .unwrap(),
                },
                y: Fq2 {
                    c0: Fq::from_repr(FqRepr([
                        0x954d4466ab13e58,
                        0x3ee42eec614cf890,
                        0x853bb1d28877577e,
                        0xa5a2a51f7fde787b,
                        0x8b92866bc6384188,
                        0x81a53fe531d64ef
                    ]))
                    .unwrap(),
                    c1: Fq::from_repr(FqRepr([
                        0x4c5d607666239b34,
                        0xeddb5f48304d14b3,
                        0x337167ee6e8e3cb6,
                        0xb271f52f12ead742,
                        0x244e6c2015c83348,
                        0x19e2deae6eb9b441
                    ]))
                    .unwrap(),
                },
                infinity: false,
            }
        );
    }

    #[test]
    fn g2_curve_tests() {
        use group::tests::curve_tests;
        curve_tests::<G2>();
    }
}

pub use self::g1::*;
pub use self::g2::*;
