use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::PrimeField;
pub fn sqrt_tonelli_shanks<F: PrimeField, S: AsRef<[u64]>>(f: &F, tm1d2: S) -> CtOption<F> {
let w = f.pow_vartime(tm1d2);
let mut v = F::S;
let mut x = w * f;
let mut b = x * w;
let mut z = F::ROOT_OF_UNITY;
for max_v in (1..=F::S).rev() {
let mut k = 1;
let mut b2k = b.square();
let mut j_less_than_v: Choice = 1.into();
for j in 2..max_v {
let b2k_is_one = b2k.ct_eq(&F::ONE);
let squared = F::conditional_select(&b2k, &z, b2k_is_one).square();
b2k = F::conditional_select(&squared, &b2k, b2k_is_one);
let new_z = F::conditional_select(&z, &squared, b2k_is_one);
j_less_than_v &= !j.ct_eq(&v);
k = u32::conditional_select(&j, &k, b2k_is_one);
z = F::conditional_select(&z, &new_z, j_less_than_v);
}
let result = x * z;
x = F::conditional_select(&result, &x, b.ct_eq(&F::ONE));
z = z.square();
b *= z;
v = k;
}
CtOption::new(
x,
(x * x).ct_eq(f), )
}
pub fn sqrt_ratio_generic<F: PrimeField>(num: &F, div: &F) -> (Choice, F) {
let a = div.invert().unwrap_or(F::ZERO) * num;
let b = a * F::ROOT_OF_UNITY;
let sqrt_a = a.sqrt();
let sqrt_b = b.sqrt();
let num_is_zero = num.is_zero();
let div_is_zero = div.is_zero();
let is_square = sqrt_a.is_some();
let is_nonsquare = sqrt_b.is_some();
assert!(bool::from(
num_is_zero | div_is_zero | (is_square ^ is_nonsquare)
));
(
is_square & (num_is_zero | !div_is_zero),
CtOption::conditional_select(&sqrt_b, &sqrt_a, is_square).unwrap(),
)
}