use crate::constants::H_EFF;
use crate::optimized_swu::{iso_map_g2, optimized_swu_g2};
use crate::pairing::CurveAffine;
use hkdf::Hkdf;
use num_bigint::BigUint;
use pairing::bls12_381::{Fq, Fq2, G2Affine, G2};
use pairing::ff::{Field, PrimeField};
use pairing::CurveProjective;
use sha2::Sha256;
pub fn hash_to_g2(message: &[u8], dst: &[u8]) -> G2 {
let u0 = hash_to_base_fq2(message, 0, dst);
let u1 = hash_to_base_fq2(message, 1, dst);
let q0 = map_to_curve_g2(u0);
let q1 = map_to_curve_g2(u1);
let mut r = G2::from(q0);
let r_2 = G2::from(q1);
r.add_assign(&r_2);
clear_cofactor_g2(r)
}
pub fn hash_to_base_fq2(message: &[u8], ctr: u8, dst: &[u8]) -> Fq2 {
let msg = [&message[..], &[0x0]].concat();
let hk = Hkdf::<Sha256>::new(Some(dst), &msg[..]);
let mut info_pfx = String::from("H2C").into_bytes();
info_pfx.push(ctr);
let mut e = vec![];
for i in 1..3 {
let mut info = info_pfx.clone();
info.push(i);
let mut okm = [0u8; 64];
hk.expand(&info, &mut okm)
.expect("64 is a valid length for Sha256 to output");
let a = BigUint::from_bytes_be(&okm);
let x = Fq::from_str(&a.to_str_radix(10))
.expect("Error getting Fq from str when trying to hash_to_base_fq2");
e.push(x);
}
Fq2 { c0: e[0], c1: e[1] }
}
pub fn map_to_curve_g2(u: Fq2) -> G2Affine {
let (optimized_swu_x, optimized_swu_y, optimized_swu_z) = optimized_swu_g2(u);
let (iso_map_u, iso_map_v, iso_map_t) =
iso_map_g2(optimized_swu_x, optimized_swu_y, optimized_swu_z);
let iso_map_t_inv = iso_map_t.inverse().expect("t should have an inverse");
let mut iso_map_u_norm = iso_map_u;
iso_map_u_norm.mul_assign(&iso_map_t_inv);
let mut iso_map_v_norm = iso_map_v;
iso_map_v_norm.mul_assign(&iso_map_t_inv);
G2Affine::from_xy_unchecked(iso_map_u_norm, iso_map_v_norm)
}
pub fn clear_cofactor_g2(f_g2: G2) -> G2 {
multiply(&f_g2, &(*H_EFF).clone())
}
pub fn multiply(pt: &G2, n: &BigUint) -> G2 {
let zero = &BigUint::from(0x0u8);
let one = &BigUint::from(0x1u8);
let two = &BigUint::from(0x2u8);
let pt_g2 = pt;
let mut dbl = *pt;
dbl.double();
if *n == *one {
*pt
} else if n % two == *zero {
multiply(&dbl, &(n / two))
} else {
let mut tmp: G2 = multiply(&dbl, &(n / two));
tmp.add_assign(&pt_g2);
tmp
}
}