use core::fmt::{self, Debug, Display, Formatter, LowerHex, UpperHex};
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};

use elliptic_curve::bigint::Encoding;
use elliptic_curve::{
    bigint::{
        consts::{U84, U88},
        NonZero, U448, U704,
    },
    generic_array::GenericArray as Array,
    hash2curve::{FromOkm, MapToCurve},
};
use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq};

#[cfg(feature = "zeroize")]
use zeroize::DefaultIsZeroes;

use super::ConstMontyType;
use crate::curve::twedwards::extended::ExtendedPoint as TwistedExtendedPoint;
use crate::{AffinePoint, EdwardsPoint};

#[derive(Clone, Copy, Default)]
pub(crate) struct FieldElement(pub(crate) ConstMontyType);

impl Display for FieldElement {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{:x}", self.0.retrieve())
    }
}

impl Debug for FieldElement {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "FieldElement({:x})", self.0.retrieve())
    }
}

impl LowerHex for FieldElement {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{:x}", self.0.retrieve())
    }
}

impl UpperHex for FieldElement {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{:X}", self.0.retrieve())
    }
}

impl ConstantTimeEq for FieldElement {
    fn ct_eq(&self, other: &Self) -> Choice {
        self.0.ct_eq(&other.0)
    }
}

impl ConditionallySelectable for FieldElement {
    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
        Self(ConstMontyType::conditional_select(&a.0, &b.0, choice))
    }
}

impl PartialEq for FieldElement {
    fn eq(&self, other: &FieldElement) -> bool {
        self.ct_eq(other).into()
    }
}
impl Eq for FieldElement {}

impl FromOkm for FieldElement {
    type Length = U84;

    fn from_okm(data: &Array<u8, Self::Length>) -> Self {
        const SEMI_WIDE_MODULUS: NonZero<U704> = NonZero::<U704>::const_new(U704::from_be_hex("0000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff")).0;
        let mut tmp = Array::<u8, U88>::default();
        tmp[4..].copy_from_slice(&data[..]);

        let mut num = U704::from_be_slice(&tmp[..]);
        num %= SEMI_WIDE_MODULUS;

        let bytes =
            <[u8; 56]>::try_from(&num.to_le_bytes()[..56]).expect("slice is the wrong length");
        FieldElement(ConstMontyType::new(&U448::from_le_slice(&bytes)))
    }
}

#[cfg(feature = "zeroize")]
impl DefaultIsZeroes for FieldElement {}

impl Add<&FieldElement> for &FieldElement {
    type Output = FieldElement;

    fn add(self, other: &FieldElement) -> FieldElement {
        FieldElement(self.0.add(&other.0))
    }
}

define_add_variants!(
    LHS = FieldElement,
    RHS = FieldElement,
    Output = FieldElement
);

impl AddAssign for FieldElement {
    fn add_assign(&mut self, other: FieldElement) {
        *self = *self + other;
    }
}

impl AddAssign<&FieldElement> for FieldElement {
    fn add_assign(&mut self, other: &FieldElement) {
        *self = *self + *other;
    }
}

impl Sub<&FieldElement> for &FieldElement {
    type Output = FieldElement;

    fn sub(self, other: &FieldElement) -> FieldElement {
        FieldElement(self.0.sub(&other.0))
    }
}

define_sub_variants!(
    LHS = FieldElement,
    RHS = FieldElement,
    Output = FieldElement
);

impl SubAssign for FieldElement {
    fn sub_assign(&mut self, other: FieldElement) {
        *self = *self - other;
    }
}

impl SubAssign<&FieldElement> for FieldElement {
    fn sub_assign(&mut self, other: &FieldElement) {
        *self = *self - *other;
    }
}

impl Mul<&FieldElement> for &FieldElement {
    type Output = FieldElement;

    fn mul(self, other: &FieldElement) -> FieldElement {
        FieldElement(self.0.mul(&other.0))
    }
}

define_mul_variants!(
    LHS = FieldElement,
    RHS = FieldElement,
    Output = FieldElement
);

impl MulAssign<&FieldElement> for FieldElement {
    fn mul_assign(&mut self, other: &FieldElement) {
        *self = *self * *other;
    }
}

impl MulAssign for FieldElement {
    fn mul_assign(&mut self, other: FieldElement) {
        *self = *self * other;
    }
}

impl Neg for &FieldElement {
    type Output = FieldElement;

    fn neg(self) -> FieldElement {
        -*self
    }
}

impl Neg for FieldElement {
    type Output = FieldElement;

    fn neg(self) -> FieldElement {
        Self(self.0.neg())
    }
}

impl MapToCurve for FieldElement {
    type Output = EdwardsPoint;

    fn map_to_curve(&self) -> Self::Output {
        self.map_to_curve_elligator2().to_edwards()
    }
}

impl FieldElement {
    pub const A_PLUS_TWO_OVER_FOUR: Self = Self(ConstMontyType::new(&U448::from_be_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000098aa")));
    pub const DECAF_FACTOR: Self = Self(ConstMontyType::new(&U448::from_be_hex("22d962fbeb24f7683bf68d722fa26aa0a1f1a7b8a5b8d54b64a2d780968c14ba839a66f4fd6eded260337bf6aa20ce529642ef0f45572736")));
    pub const EDWARDS_D: Self = Self(ConstMontyType::new(&U448::from_be_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756")));
    pub const J: Self = Self(ConstMontyType::new(&U448::from_u64(156326)));
    pub const MINUS_ONE: Self = Self(ConstMontyType::new(&U448::from_be_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffe")));
    pub const NEG_EDWARDS_D: Self = Self(ConstMontyType::new(&U448::from_be_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000098a9")));
    pub const NEG_FOUR_TIMES_TWISTED_D: Self = Self(ConstMontyType::new(&U448::from_be_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000262a8")));
    pub const ONE: Self = Self(ConstMontyType::new(&U448::ONE));
    pub const TWISTED_D: Self = Self(ConstMontyType::new(&U448::from_be_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6755")));
    pub const TWO_TIMES_TWISTED_D: Self = Self(ConstMontyType::new(&U448::from_be_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffeceab")));
    pub const Z: Self = Self(ConstMontyType::new(&U448::from_be_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffe")));
    pub const ZERO: Self = Self(ConstMontyType::new(&U448::ZERO));

    pub fn is_negative(&self) -> Choice {
        let bytes = self.to_bytes();
        (bytes[0] & 1).into()
    }

    /// Inverts a field element
    /// Previous chain length: 462, new length 460
    pub fn invert(&self) -> Self {
        const INV_EXP: U448 = U448::from_be_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffd");
        Self(self.0.pow(&INV_EXP))
    }

    pub fn square(&self) -> Self {
        Self(self.0.square())
    }

    /// Squares a field element  `n` times
    fn square_n(&self, mut n: u32) -> FieldElement {
        let mut result = self.square();

        // Decrease value by 1 since we just did a squaring
        n -= 1;

        for _ in 0..n {
            result = result.square();
        }

        result
    }

    pub fn is_square(&self) -> Choice {
        const IS_SQUARE_EXP: U448 = U448::from_le_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7f");
        self.0.pow(&IS_SQUARE_EXP).ct_eq(&FieldElement::ONE.0)
    }

    pub fn sqrt(&self) -> FieldElement {
        const SQRT_EXP: U448 = U448::from_be_hex("3fffffffffffffffffffffffffffffffffffffffffffffffffffffffc0000000000000000000000000000000000000000000000000000000");
        Self(self.0.pow(&SQRT_EXP))
    }

    pub fn to_bytes(self) -> [u8; 56] {
        let mut bytes = [0u8; 56];
        bytes.copy_from_slice(&self.0.retrieve().to_le_bytes()[..56]);
        bytes
    }

    pub fn to_bytes_extended(self) -> [u8; 57] {
        let mut bytes = [0u8; 57];
        bytes[..56].copy_from_slice(&self.to_bytes());
        bytes
    }

    pub fn from_bytes(bytes: &[u8; 56]) -> Self {
        Self(ConstMontyType::new(&U448::from_le_slice(bytes)))
    }

    pub fn double(&self) -> Self {
        Self(self.0.add(&self.0))
    }

    /// Computes the inverse square root of a field element
    /// Returns the result and a boolean to indicate whether self
    /// was a Quadratic residue
    pub(crate) fn inverse_square_root(&self) -> (FieldElement, Choice) {
        let (mut l0, mut l1, mut l2);

        l1 = self.square();
        l2 = l1 * self;
        l1 = l2.square();
        l2 = l1 * self;
        l1 = l2.square_n(3);
        l0 = l2 * l1;
        l1 = l0.square_n(3);
        l0 = l2 * l1;
        l2 = l0.square_n(9);
        l1 = l0 * l2;
        l0 = l1 * l1;
        l2 = l0 * self;
        l0 = l2.square_n(18);
        l2 = l1 * l0;
        l0 = l2.square_n(37);
        l1 = l2 * l0;
        l0 = l1.square_n(37);
        l1 = l2 * l0;
        l0 = l1.square_n(111);
        l2 = l1 * l0;
        l0 = l2.square();
        l1 = l0 * self;
        l0 = l1.square_n(223);
        l1 = l2 * l0;
        l2 = l1.square();
        l0 = l2 * self;

        let is_residue = l0.ct_eq(&FieldElement::ONE);
        (l1, is_residue)
    }

    /// Computes the square root ratio of two elements
    pub(crate) fn sqrt_ratio(u: &FieldElement, v: &FieldElement) -> (FieldElement, Choice) {
        // Compute sqrt(1/(uv))
        let x = *u * v;
        let (inv_sqrt_x, is_res) = x.inverse_square_root();
        // Return u * sqrt(1/(uv)) == sqrt(u/v). However, since this trick only works
        // for u != 0, check for that case explicitly (when u == 0 then inv_sqrt_x
        // will be zero, which is what we want, but is_res will be 0)
        let zero_u = u.ct_eq(&FieldElement::ZERO);
        (inv_sqrt_x * u, zero_u | is_res)
    }

    /// Computes the square root ratio of two elements
    ///
    /// The difference between this and `sqrt_ratio` is that
    /// if the input is non-square, the function returns a result with
    /// a defined relationship to the inputs.
    pub(crate) fn sqrt_ratio_i(u: &FieldElement, v: &FieldElement) -> (FieldElement, Choice) {
        const P_MINUS_THREE_DIV_4: U448 = U448::from_be_hex("3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffffffffffffffffffffffffffffffffffffffffffffffffff");
        let u = u.0;
        let v = v.0;

        let r = u * (u * v).pow(&P_MINUS_THREE_DIV_4);
        let check = v * r.square();
        let was_square = check.ct_eq(&u);

        let mut r = FieldElement(r);
        r.conditional_negate(r.is_negative());
        (r, was_square)
    }

    pub(crate) fn map_to_curve_elligator2(&self) -> AffinePoint {
        let mut t1 = self.square(); // 1.   t1 = u^2
        t1 *= Self::Z; // 2.   t1 = Z * t1              // Z * u^2
        let e1 = t1.ct_eq(&Self::MINUS_ONE); // 3.   e1 = t1 == -1            // exceptional case: Z * u^2 == -1
        t1.conditional_assign(&Self::ZERO, e1); // 4.   t1 = CMOV(t1, 0, e1)     // if t1 == -1, set t1 = 0
        let mut x1 = t1 + Self::ONE; // 5.   x1 = t1 + 1
        x1 = x1.invert(); // 6.   x1 = inv0(x1)
        x1 *= -Self::J; // 7.   x1 = -A * x1             // x1 = -A / (1 + Z * u^2)
        let mut gx1 = x1 + Self::J; // 8.  gx1 = x1 + A
        gx1 *= x1; // 9.  gx1 = gx1 * x1
        gx1 += Self::ONE; // 10. gx1 = gx1 + B
        gx1 *= x1; // 11. gx1 = gx1 * x1            // gx1 = x1^3 + A * x1^2 + B * x1
        let x2 = -x1 - Self::J; // 12.  x2 = -x1 - A
        let gx2 = t1 * gx1; // 13. gx2 = t1 * gx1
        let e2 = gx1.is_square(); // 14.  e2 = is_square(gx1)
        let x = Self::conditional_select(&x2, &x1, e2); // 15.   x = CMOV(x2, x1, e2)    // If is_square(gx1), x = x1, else x = x2
        let y2 = Self::conditional_select(&gx2, &gx1, e2); // 16.  y2 = CMOV(gx2, gx1, e2)  // If is_square(gx1), y2 = gx1, else y2 = gx2
        let mut y = y2.sqrt(); // 17.   y = sqrt(y2)
        let e3 = y.is_negative(); // 18.  e3 = sgn0(y) == 1
        y.conditional_negate(e2 ^ e3); //       y = CMOV(-y, y, e2 xor e3)
        AffinePoint { x, y }
    }

    pub(crate) fn map_to_curve_decaf448(&self) -> TwistedExtendedPoint {
        const ONE_MINUS_TWO_D: FieldElement =
            FieldElement(ConstMontyType::new(&U448::from_u64(78163)));

        let r = -self.square();
        let u0 = Self::EDWARDS_D * (r - Self::ONE);
        let u1 = (u0 + Self::ONE) * (u0 - r);

        let rhs = (r + Self::ONE) * u1;
        let (v, was_square) = Self::sqrt_ratio_i(&ONE_MINUS_TWO_D, &rhs);

        let mut v_prime = self * v;
        v_prime.conditional_assign(&v, was_square);
        let mut sgn = Self::MINUS_ONE;
        sgn.conditional_negate(was_square);

        let s = v_prime * (r + Self::ONE);
        let s2 = s.square();
        let s_abs = Self::conditional_select(&s, &s.neg(), s.is_negative());

        let w0 = s_abs + s_abs;
        let w1 = s2 + Self::ONE;
        let w2 = s2 - Self::ONE;
        let w3 = v_prime * s * (r - Self::ONE) * ONE_MINUS_TWO_D + sgn;

        EdwardsPoint {
            X: w0 * w3,
            Y: w2 * w1,
            Z: w1 * w3,
            T: w0 * w2,
        }
        .to_twisted()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXof, Expander};
    use hex_literal::hex;
    use sha3::Shake256;

    #[test]
    fn from_okm_curve448() {
        const DST: &[u8] = b"QUUX-V01-CS02-with-curve448_XOF:SHAKE256_ELL2_RO_";
        const MSGS: &[(&[u8], [u8; 56], [u8; 56])] = &[
            (b"", hex!("c704c7b3d3b36614cf3eedd0324fe6fe7d1402c50efd16cff89ff63f50938506280d3843478c08e24f7842f4e3ef45f6e3c4897f9d976148"), hex!("c25427dc97fff7a5ad0a78654e2c6c27b1c1127b5b53c7950cd1fd6edd2703646b25f341e73deedfebf022d1d3cecd02b93b4d585ead3ed7")),
            (b"abc", hex!("2dd95593dfee26fe0d218d3d9a0a23d9e1a262fd1d0b602483d08415213e75e2db3c69b0a5bc89e71bcefc8c723d2b6a0cf263f02ad2aa70"), hex!("272e4c79a1290cc6d2bc4f4f9d31bf7fbe956ca303c04518f117d77c0e9d850796fc3e1e2bcb9c75e8eaaded5e150333cae9931868047c9d")),
            (b"abcdef0123456789", hex!("6aab71a38391639f27e49eae8b1cb6b7172a1f478190ece293957e7cdb2391e7cc1c4261970d9c1bbf9c3915438f74fbd7eb5cd4d4d17ace"), hex!("c80b8380ca47a3bcbf76caa75cef0e09f3d270d5ee8f676cde11aedf41aaca6741bd81a86232bd336ccb42efad39f06542bc06a67b65909e")),
            (b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", hex!("cb5c27e51f9c18ee8ffdb6be230f4eb4f2c2481963b2293484f08da2241c1ff59f80978e6defe9d70e34abba2fcbe12dc3a1eb2c5d3d2e4a"), hex!("c895e8afecec5466e126fa70fc4aa784b8009063afb10e3ee06a9b22318256aa8693b0c85b955cf2d6540b8ed71e729af1b8d5ca3b116cd7")),
            (b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", hex!("8cba93a007bb2c801b1769e026b1fa1640b14a34cf3029db3c7fd6392745d6fec0f7870b5071d6da4402cedbbde28ae4e50ab30e1049a238"), hex!("4223746145069e4b8a981acc3404259d1a2c3ecfed5d864798a89d45f81a2c59e2d40eb1d5f0fe11478cbb2bb30246dd388cb932ad7bb330")),
        ];

        for (msg, expected_u0, expected_u1) in MSGS {
            let mut expander =
                ExpandMsgXof::<Shake256>::expand_message(&[msg], &[DST], 84 * 2).unwrap();
            let mut data = Array::<u8, U84>::default();
            expander.fill_bytes(&mut data);
            let u0 = FieldElement::from_okm(&data);
            let mut e_u0 = *expected_u0;
            e_u0.reverse();
            let mut e_u1 = *expected_u1;
            e_u1.reverse();
            assert_eq!(u0.to_bytes(), e_u0);
            expander.fill_bytes(&mut data);
            let u1 = FieldElement::from_okm(&data);
            assert_eq!(u1.to_bytes(), e_u1);
        }
    }

    #[test]
    fn from_okm_edwards448() {
        const DST: &[u8] = b"QUUX-V01-CS02-with-edwards448_XOF:SHAKE256_ELL2_RO_";
        const MSGS: &[(&[u8], [u8; 56], [u8; 56])] = &[
            (b"", hex!("0847c5ebf957d3370b1f98fde499fb3e659996d9fc9b5707176ade785ba72cd84b8a5597c12b1024be5f510fa5ba99642c4cec7f3f69d3e7"), hex!("f8cbd8a7ae8c8deed071f3ac4b93e7cfcb8f1eac1645d699fd6d3881cb295a5d3006d9449ed7cad412a77a1fe61e84a9e41d59ef384d6f9a")),
            (b"abc", hex!("04d975cd938ab49be3e81703d6a57cca84ed80d2ff6d4756d3f22947fb5b70ab0231f0087cbfb4b7cae73b41b0c9396b356a4831d9a14322"), hex!("2547ca887ac3db7b5fad3a098aa476e90078afe1358af6c63d677d6edfd2100bc004e0f5db94dd2560fc5b308e223241d00488c9ca6b0ef2")),
            (b"abcdef0123456789", hex!("10659ce25588db4e4be6f7c791a79eb21a7f24aaaca76a6ca3b83b80aaf95aa328fe7d569a1ac99f9cd216edf3915d72632f1a8b990e250c"), hex!("9243e5b6c480683fd533e81f4a778349a309ce00bd163a29eb9fa8dbc8f549242bef33e030db21cffacd408d2c4264b93e476c6a8590e7aa")),
            (b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", hex!("c80390020e578f009ead417029eff6cd0926110922db63ab98395e3bdfdd5d8a65b1a2b8d495dc8c5e59b7f3518731f7dfc0f93ace5dee4b"), hex!("1c4dc6653a445bbef2add81d8e90a6c8591a788deb91d0d3f1519a2e4a460313041b77c1b0817f2e80b388e5c3e49f37d787dc1f85e4324a")),
            (b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", hex!("163c79ab0210a4b5e4f44fb19437ea965bf5431ab233ef16606f0b03c5f16a3feb7d46a5a675ce8f606e9c2bf74ee5336c54a1e54919f13f"), hex!("f99666bde4995c4088333d6c2734687e815f80a99c6da02c47df4b51f6c9d9ed466b4fecf7d9884990a8e0d0be6907fa437e0b1a27f49265")),
        ];

        for (msg, expected_u0, expected_u1) in MSGS {
            let mut expander =
                ExpandMsgXof::<Shake256>::expand_message(&[msg], &[DST], 84 * 2).unwrap();
            let mut data = Array::<u8, U84>::default();
            expander.fill_bytes(&mut data);
            let u0 = FieldElement::from_okm(&data);
            let mut e_u0 = *expected_u0;
            e_u0.reverse();
            let mut e_u1 = *expected_u1;
            e_u1.reverse();
            assert_eq!(u0.to_bytes(), e_u0);
            expander.fill_bytes(&mut data);
            let u1 = FieldElement::from_okm(&data);
            assert_eq!(u1.to_bytes(), e_u1);
        }
    }

    #[test]
    fn get_constants() {
        let m1 = -FieldElement::ONE;
        assert_eq!(m1, FieldElement::MINUS_ONE);
    }

    #[test]
    fn sqrt() {
        let nine = FieldElement::from_bytes(&[
            0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        ]);
        let three = FieldElement::from_bytes(&[
            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        ]);
        assert_eq!(three, nine.sqrt());
    }
}
