#![allow(dead_code)]
const M62: u64 = u64::MAX >> 2;
#[derive(Clone, Copy)]
pub struct Signed62 {
pub v: [i64; 5],
}
pub struct ModInfo {
pub modulus: Signed62,
pub modulus_inv62: u64,
}
struct Trans2x2 {
u: i64,
v: i64,
q: i64,
r: i64,
}
fn normalize_62(r: &mut Signed62, sign: i64, modinfo: &ModInfo) {
let m = &modinfo.modulus;
let [mut r0, mut r1, mut r2, mut r3, mut r4] = r.v;
let m62 = M62 as i64;
let cond_add = r4 >> 63; r0 += m.v[0] & cond_add;
r1 += m.v[1] & cond_add;
r2 += m.v[2] & cond_add;
r3 += m.v[3] & cond_add;
r4 += m.v[4] & cond_add;
let cond_neg = sign >> 63; r0 = (r0 ^ cond_neg) - cond_neg;
r1 = (r1 ^ cond_neg) - cond_neg;
r2 = (r2 ^ cond_neg) - cond_neg;
r3 = (r3 ^ cond_neg) - cond_neg;
r4 = (r4 ^ cond_neg) - cond_neg;
r1 += r0 >> 62;
r0 &= m62;
r2 += r1 >> 62;
r1 &= m62;
r3 += r2 >> 62;
r2 &= m62;
r4 += r3 >> 62;
r3 &= m62;
let cond_add = r4 >> 63;
r0 += m.v[0] & cond_add;
r1 += m.v[1] & cond_add;
r2 += m.v[2] & cond_add;
r3 += m.v[3] & cond_add;
r4 += m.v[4] & cond_add;
r1 += r0 >> 62;
r0 &= m62;
r2 += r1 >> 62;
r1 &= m62;
r3 += r2 >> 62;
r2 &= m62;
r4 += r3 >> 62;
r3 &= m62;
r.v = [r0, r1, r2, r3, r4];
}
fn divsteps_62_var(mut eta: i64, f0: u64, g0: u64, t: &mut Trans2x2) -> i64 {
let mut u: u64 = 1;
let mut v: u64 = 0;
let mut q: u64 = 0;
let mut r: u64 = 1;
let mut f = f0;
let mut g = g0;
let mut i: i32 = 62;
loop {
let zeros = (g | (u64::MAX << i as u32)).trailing_zeros() as i32;
g >>= zeros;
u <<= zeros;
v <<= zeros;
eta -= zeros as i64;
i -= zeros;
if i == 0 {
break;
}
if eta < 0 {
eta = -eta;
let tmp = f;
f = g;
g = tmp.wrapping_neg();
let tmp = u;
u = q;
q = tmp.wrapping_neg();
let tmp = v;
v = r;
r = tmp.wrapping_neg();
let limit = ((eta as i32) + 1).min(i) as u32;
let m = (u64::MAX >> (64 - limit)) & 63u64;
let w = f
.wrapping_mul(g)
.wrapping_mul(f.wrapping_mul(f).wrapping_sub(2))
& m;
g = g.wrapping_add(f.wrapping_mul(w));
q = q.wrapping_add(u.wrapping_mul(w));
r = r.wrapping_add(v.wrapping_mul(w));
} else {
let limit = ((eta as i32) + 1).min(i) as u32;
let m = (u64::MAX >> (64 - limit)) & 15u64;
let mut w = f.wrapping_add((f.wrapping_add(1) & 4) << 1);
w = w.wrapping_neg().wrapping_mul(g) & m;
g = g.wrapping_add(f.wrapping_mul(w));
q = q.wrapping_add(u.wrapping_mul(w));
r = r.wrapping_add(v.wrapping_mul(w));
}
}
t.u = u as i64;
t.v = v as i64;
t.q = q as i64;
t.r = r as i64;
eta
}
fn update_de_62(d: &mut Signed62, e: &mut Signed62, t: &Trans2x2, modinfo: &ModInfo) {
let [d0, d1, d2, d3, d4] = d.v;
let [e0, e1, e2, e3, e4] = e.v;
let u = t.u;
let v = t.v;
let q = t.q;
let r = t.r;
let m = &modinfo.modulus;
let sd = d4 >> 63; let se = e4 >> 63;
let mut md = (u & sd) + (v & se);
let mut me = (q & sd) + (r & se);
let mut cd: i128 = (u as i128) * (d0 as i128) + (v as i128) * (e0 as i128);
let mut ce: i128 = (q as i128) * (d0 as i128) + (r as i128) * (e0 as i128);
md -= (modinfo
.modulus_inv62
.wrapping_mul(cd as u64)
.wrapping_add(md as u64)
& M62) as i64;
me -= (modinfo
.modulus_inv62
.wrapping_mul(ce as u64)
.wrapping_add(me as u64)
& M62) as i64;
cd += (m.v[0] as i128) * (md as i128);
ce += (m.v[0] as i128) * (me as i128);
debug_assert!((cd as u64) & M62 == 0);
debug_assert!((ce as u64) & M62 == 0);
cd >>= 62;
ce >>= 62;
cd += (u as i128) * (d1 as i128) + (v as i128) * (e1 as i128);
ce += (q as i128) * (d1 as i128) + (r as i128) * (e1 as i128);
if m.v[1] != 0 {
cd += (m.v[1] as i128) * (md as i128);
ce += (m.v[1] as i128) * (me as i128);
}
d.v[0] = (cd as u64 & M62) as i64;
cd >>= 62;
e.v[0] = (ce as u64 & M62) as i64;
ce >>= 62;
cd += (u as i128) * (d2 as i128) + (v as i128) * (e2 as i128);
ce += (q as i128) * (d2 as i128) + (r as i128) * (e2 as i128);
if m.v[2] != 0 {
cd += (m.v[2] as i128) * (md as i128);
ce += (m.v[2] as i128) * (me as i128);
}
d.v[1] = (cd as u64 & M62) as i64;
cd >>= 62;
e.v[1] = (ce as u64 & M62) as i64;
ce >>= 62;
cd += (u as i128) * (d3 as i128) + (v as i128) * (e3 as i128);
ce += (q as i128) * (d3 as i128) + (r as i128) * (e3 as i128);
if m.v[3] != 0 {
cd += (m.v[3] as i128) * (md as i128);
ce += (m.v[3] as i128) * (me as i128);
}
d.v[2] = (cd as u64 & M62) as i64;
cd >>= 62;
e.v[2] = (ce as u64 & M62) as i64;
ce >>= 62;
cd += (u as i128) * (d4 as i128) + (v as i128) * (e4 as i128);
ce += (q as i128) * (d4 as i128) + (r as i128) * (e4 as i128);
cd += (m.v[4] as i128) * (md as i128);
ce += (m.v[4] as i128) * (me as i128);
d.v[3] = (cd as u64 & M62) as i64;
cd >>= 62;
e.v[3] = (ce as u64 & M62) as i64;
ce >>= 62;
d.v[4] = cd as i64;
e.v[4] = ce as i64;
}
fn update_fg_62_var(len: usize, f: &mut Signed62, g: &mut Signed62, t: &Trans2x2) {
let u = t.u;
let v = t.v;
let q = t.q;
let r = t.r;
let fi = f.v[0];
let gi = g.v[0];
let mut cf: i128 = (u as i128) * (fi as i128) + (v as i128) * (gi as i128);
let mut cg: i128 = (q as i128) * (fi as i128) + (r as i128) * (gi as i128);
debug_assert!((cf as u64) & M62 == 0);
debug_assert!((cg as u64) & M62 == 0);
cf >>= 62;
cg >>= 62;
for i in 1..len {
let fi = f.v[i];
let gi = g.v[i];
cf += (u as i128) * (fi as i128) + (v as i128) * (gi as i128);
cg += (q as i128) * (fi as i128) + (r as i128) * (gi as i128);
f.v[i - 1] = (cf as u64 & M62) as i64;
cf >>= 62;
g.v[i - 1] = (cg as u64 & M62) as i64;
cg >>= 62;
}
f.v[len - 1] = cf as i64;
g.v[len - 1] = cg as i64;
}
pub fn modinv64_var(x: &mut Signed62, modinfo: &ModInfo) {
let mut d = Signed62 { v: [0; 5] };
let mut e = Signed62 { v: [1, 0, 0, 0, 0] };
let mut f = modinfo.modulus;
let mut g = *x;
let mut eta: i64 = -1; let mut len: usize = 5;
loop {
let mut t = Trans2x2 {
u: 0,
v: 0,
q: 0,
r: 0,
};
eta = divsteps_62_var(eta, f.v[0] as u64, g.v[0] as u64, &mut t);
update_de_62(&mut d, &mut e, &t, modinfo);
update_fg_62_var(len, &mut f, &mut g, &t);
if g.v[0] == 0 {
let mut cond: i64 = 0;
for j in 1..len {
cond |= g.v[j];
}
if cond == 0 {
break;
}
}
let fn_ = f.v[len - 1];
let gn = g.v[len - 1];
let mut cond: i64 = (len as i64 - 2) >> 63; cond |= fn_ ^ (fn_ >> 63); cond |= gn ^ (gn >> 63);
if cond == 0 {
f.v[len - 2] = (f.v[len - 2] as u64 | ((fn_ as u64) << 62)) as i64;
g.v[len - 2] = (g.v[len - 2] as u64 | ((gn as u64) << 62)) as i64;
len -= 1;
}
}
normalize_62(&mut d, f.v[len - 1], modinfo);
*x = d;
}
pub const FE_MODINFO: ModInfo = ModInfo {
modulus: Signed62 {
v: [-0x1000003D1i64, 0, 0, 0, 256],
},
modulus_inv62: 0x27C7_F6E2_2DDA_CACFu64,
};
pub const SCALAR_MODINFO: ModInfo = ModInfo {
modulus: Signed62 {
v: [
0x3FD25E8CD0364141i64,
0x2ABB739ABD2280EEi64,
-0x15i64,
0,
256,
],
},
modulus_inv62: 0x34F2_0099_AA77_4EC1u64,
};