use crate::{Fq, Fr};
use ark_ec::{
    models::{ModelParameters, MontgomeryModelParameters, TEModelParameters},
    twisted_edwards_extended::{GroupAffine, GroupProjective},
};
use ark_ff::{field_new, Field};

mod glv;

pub use glv::{multi_scalar_mul, GLVParameters};

#[cfg(test)]
mod tests;

pub type EdwardsAffine = GroupAffine<EdwardsParameters>;
pub type EdwardsProjective = GroupProjective<EdwardsParameters>;

/// `banersnatch` is a twisted Edwards curve. These curves have equations of the
/// form: ax² + y² = 1 - dx²y².
/// over some base finite field Fq.
///
/// banersnatch's curve equation: -5x² + y² = 1 - dx²y²
///
/// q = 52435875175126190479447740508185965837690552500527637822603658699938581184513.
///
/// a = 52435875175126190479447740508185965837690552500527637822603658699938581184508.
/// d = (138827208126141220649022263972958607803/
///     171449701953573178309673572579671231137) mod q
///   = 45022363124591815672509500913686876175488063829319466900776701791074614335719.
///
/// Sage script to calculate these:
///
/// ```text
/// q = 52435875175126190479447740508185965837690552500527637822603658699938581184513
/// Fq = GF(q)
/// d = (Fq(138827208126141220649022263972958607803)/Fq(171449701953573178309673572579671231137))
/// ```
/// These parameters and the sage script obtained from:
/// <https://github.com/asanso/Bandersnatch/>
#[derive(Clone, Default, PartialEq, Eq)]
pub struct EdwardsParameters;

impl ModelParameters for EdwardsParameters {
    type BaseField = Fq;
    type ScalarField = Fr;
}

impl TEModelParameters for EdwardsParameters {
    /// COEFF_A = -5
    const COEFF_A: Fq = field_new!(Fq, "-5");

    /// COEFF_D = (138827208126141220649022263972958607803/
    /// 171449701953573178309673572579671231137) mod q
    const COEFF_D: Fq = field_new!(
        Fq,
        "45022363124591815672509500913686876175488063829319466900776701791074614335719"
    );

    /// COFACTOR = 4
    const COFACTOR: &'static [u64] = &[4];

    /// COFACTOR^(-1) mod r =
    /// 9831726595336160714896451345284868594481866920080427688839802480047265754601
    const COFACTOR_INV: Fr = field_new!(
        Fr,
        "9831726595336160714896451345284868594481866920080427688839802480047265754601"
    );

    /// AFFINE_GENERATOR_COEFFS = (GENERATOR_X, GENERATOR_Y)
    const AFFINE_GENERATOR_COEFFS: (Self::BaseField, Self::BaseField) =
        (GENERATOR_X, GENERATOR_Y);

    type MontgomeryModelParameters = EdwardsParameters;

    /// Multiplication by `a` is multiply by `-5`.
    #[inline(always)]
    fn mul_by_a(elem: &Self::BaseField) -> Self::BaseField {
        let t = (*elem).double().double();
        -(t + *elem)
    }
}

impl MontgomeryModelParameters for EdwardsParameters {
    /// COEFF_A = 29978822694968839326280996386011761570173833766074948509196803838190355340952
    const COEFF_A: Fq = field_new!(
        Fq,
        "29978822694968839326280996386011761570173833766074948509196803838190355340952"
    );

    /// COEFF_B = 25465760566081946422412445027709227188579564747101592991722834452325077642517
    const COEFF_B: Fq = field_new!(
        Fq,
        "25465760566081946422412445027709227188579564747101592991722834452325077642517"
    );

    type TEModelParameters = EdwardsParameters;
}

// using the generator from bench.py (in affine form)
// P = BandersnatchPoint(
//      13738737789055671334382939318077718462576533426798874551591468520593954805549,
//      11575885077368931610486103676191793534029821920164915325066801506752632626968,
//      14458123306641001284399433086015669988340559992755622870694102351476334505845,
//      C)

const GENERATOR_X: Fq = field_new!(
    Fq,
    "29627151942733444043031429156003786749302466371339015363120350521834195802525"
);

const GENERATOR_Y: Fq = field_new!(
    Fq,
    "27488387519748396681411951718153463804682561779047093991696427532072116857978"
);
