use core::mem::size_of;
use static_assertions::const_assert;
#[cfg(feature = "sqrt-table")]
use alloc::{boxed::Box, vec::Vec};
#[cfg(feature = "sqrt-table")]
use core::marker::PhantomData;
#[cfg(feature = "sqrt-table")]
use subtle::Choice;
const_assert!(size_of::<usize>() >= 4);
pub(crate) trait SqrtTableHelpers: ff::PrimeField {
fn pow_by_t_minus1_over2(&self) -> Self;
fn get_lower_32(&self) -> u32;
}
#[cfg(feature = "sqrt-table")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))]
#[derive(Debug)]
struct SqrtHasher<F: SqrtTableHelpers> {
hash_xor: u32,
hash_mod: usize,
marker: PhantomData<F>,
}
#[cfg(feature = "sqrt-table")]
impl<F: SqrtTableHelpers> SqrtHasher<F> {
fn hash(&self, x: &F) -> usize {
((x.get_lower_32() ^ self.hash_xor) as usize) % self.hash_mod
}
}
#[cfg(feature = "sqrt-table")]
#[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))]
#[derive(Debug)]
pub(crate) struct SqrtTables<F: SqrtTableHelpers> {
hasher: SqrtHasher<F>,
inv: Vec<u8>,
g0: Box<[F; 256]>,
g1: Box<[F; 256]>,
g2: Box<[F; 256]>,
g3: Box<[F; 129]>,
}
#[cfg(feature = "sqrt-table")]
impl<F: SqrtTableHelpers> SqrtTables<F> {
pub fn new(hash_xor: u32, hash_mod: usize) -> Self {
use alloc::vec;
let hasher = SqrtHasher {
hash_xor,
hash_mod,
marker: PhantomData,
};
let mut gtab = (0..4).scan(F::ROOT_OF_UNITY, |gi, _| {
let gtab_i: Vec<F> = (0..256)
.scan(F::ONE, |acc, _| {
let res = *acc;
*acc *= *gi;
Some(res)
})
.collect();
*gi = gtab_i[255] * *gi;
Some(gtab_i)
});
let gtab_0 = gtab.next().unwrap();
let gtab_1 = gtab.next().unwrap();
let gtab_2 = gtab.next().unwrap();
let mut gtab_3 = gtab.next().unwrap();
assert_eq!(gtab.next(), None);
let mut inv: Vec<u8> = vec![1; hash_mod];
for (j, gtab_3_j) in gtab_3.iter().enumerate() {
let hash = hasher.hash(gtab_3_j);
assert!(inv[hash] == 1);
inv[hash] = ((256 - j) & 0xFF) as u8;
}
gtab_3.truncate(129);
SqrtTables::<F> {
hasher,
inv,
g0: gtab_0.into_boxed_slice().try_into().unwrap(),
g1: gtab_1.into_boxed_slice().try_into().unwrap(),
g2: gtab_2.into_boxed_slice().try_into().unwrap(),
g3: gtab_3.into_boxed_slice().try_into().unwrap(),
}
}
pub fn sqrt_ratio(&self, num: &F, div: &F) -> (Choice, F) {
let sqr = |x: F, i: u32| (0..i).fold(x, |x, _| x.square());
let s = (0..5).fold(*div, |d: F, i| sqr(d, 1 << i) * d);
let t = s.square() * div;
let w = (t * num).pow_by_t_minus1_over2() * s;
let v = w * div;
let uv = w * num;
let res = self.sqrt_common(&uv, &v);
let sqdiv = res.square() * div;
let is_square = (sqdiv - num).is_zero();
let is_nonsquare = (sqdiv - F::ROOT_OF_UNITY * num).is_zero();
assert!(bool::from(
num.is_zero() | div.is_zero() | (is_square ^ is_nonsquare)
));
(is_square, res)
}
pub fn sqrt_alt(&self, u: &F) -> (Choice, F) {
let v = u.pow_by_t_minus1_over2();
let uv = *u * v;
let res = self.sqrt_common(&uv, &v);
let sq = res.square();
let is_square = (sq - u).is_zero();
let is_nonsquare = (sq - F::ROOT_OF_UNITY * u).is_zero();
assert!(bool::from(u.is_zero() | (is_square ^ is_nonsquare)));
(is_square, res)
}
fn sqrt_common(&self, uv: &F, v: &F) -> F {
let sqr = |x: F, i: u32| (0..i).fold(x, |x, _| x.square());
let inv = |x: F| self.inv[self.hasher.hash(&x)] as usize;
let x3 = *uv * v;
let x2 = sqr(x3, 8);
let x1 = sqr(x2, 8);
let x0 = sqr(x1, 8);
let mut t_ = inv(x0); assert!(t_ < 0x100);
let alpha = x1 * self.g2[t_];
t_ += inv(alpha) << 8; assert!(t_ < 0x10000);
let alpha = x2 * self.g1[t_ & 0xFF] * self.g2[t_ >> 8];
t_ += inv(alpha) << 16; assert!(t_ < 0x1000000);
let alpha = x3 * self.g0[t_ & 0xFF] * self.g1[(t_ >> 8) & 0xFF] * self.g2[t_ >> 16];
t_ += inv(alpha) << 24; t_ = (((t_ as u64) + 1) >> 1) as usize;
assert!(t_ <= 0x80000000);
*uv * self.g0[t_ & 0xFF]
* self.g1[(t_ >> 8) & 0xFF]
* self.g2[(t_ >> 16) & 0xFF]
* self.g3[t_ >> 24]
}
}
#[inline(always)]
pub(crate) const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) {
let ret = (a as u128) + (b as u128) + (carry as u128);
(ret as u64, (ret >> 64) as u64)
}
#[inline(always)]
pub(crate) const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) {
let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128));
(ret as u64, (ret >> 64) as u64)
}
#[inline(always)]
pub(crate) const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) {
let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128);
(ret as u64, (ret >> 64) as u64)
}