use alloc::vec::Vec;
use core::cmp::Ordering;
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct Zint {
neg: bool,
mag: Vec<u32>,
}
fn trim(v: &mut Vec<u32>) {
while v.last() == Some(&0) {
v.pop();
}
}
fn cmp_mag(a: &[u32], b: &[u32]) -> Ordering {
if a.len() != b.len() {
return a.len().cmp(&b.len());
}
for i in (0..a.len()).rev() {
if a[i] != b[i] {
return a[i].cmp(&b[i]);
}
}
Ordering::Equal
}
fn add_mag(a: &[u32], b: &[u32]) -> Vec<u32> {
let mut out = Vec::with_capacity(a.len().max(b.len()) + 1);
let mut carry = 0u64;
for i in 0..a.len().max(b.len()) {
let x = *a.get(i).unwrap_or(&0) as u64;
let y = *b.get(i).unwrap_or(&0) as u64;
let s = x + y + carry;
out.push(s as u32);
carry = s >> 32;
}
if carry != 0 {
out.push(carry as u32);
}
trim(&mut out);
out
}
fn sub_mag(a: &[u32], b: &[u32]) -> Vec<u32> {
let mut out = Vec::with_capacity(a.len());
let mut borrow = 0i64;
for i in 0..a.len() {
let x = a[i] as i64;
let y = *b.get(i).unwrap_or(&0) as i64;
let mut d = x - y - borrow;
if d < 0 {
d += 1 << 32;
borrow = 1;
} else {
borrow = 0;
}
out.push(d as u32);
}
trim(&mut out);
out
}
fn mul_mag(a: &[u32], b: &[u32]) -> Vec<u32> {
if a.is_empty() || b.is_empty() {
return Vec::new();
}
let mut out = alloc::vec![0u32; a.len() + b.len()];
for (i, &ai) in a.iter().enumerate() {
let mut carry = 0u64;
let aiv = ai as u64;
for (j, &bj) in b.iter().enumerate() {
let cur = out[i + j] as u64 + aiv * bj as u64 + carry;
out[i + j] = cur as u32;
carry = cur >> 32;
}
out[i + b.len()] += carry as u32;
}
trim(&mut out);
out
}
fn shl_mag(a: &[u32], bits: usize) -> Vec<u32> {
if a.is_empty() {
return Vec::new();
}
let words = bits / 32;
let rem = bits % 32;
let mut out = alloc::vec![0u32; words];
if rem == 0 {
out.extend_from_slice(a);
} else {
let mut carry = 0u64;
for &limb in a {
let v = ((limb as u64) << rem) | carry;
out.push(v as u32);
carry = v >> 32;
}
if carry != 0 {
out.push(carry as u32);
}
}
trim(&mut out);
out
}
fn shr_mag(a: &[u32], bits: usize) -> Vec<u32> {
let words = bits / 32;
let rem = bits % 32;
if words >= a.len() {
return Vec::new();
}
let mut out: Vec<u32> = a[words..].to_vec();
if rem != 0 {
let mut carry = 0u32;
for i in (0..out.len()).rev() {
let v = out[i];
out[i] = (v >> rem) | carry;
carry = v << (32 - rem);
}
}
trim(&mut out);
out
}
impl Zint {
pub(crate) fn zero() -> Zint {
Zint {
neg: false,
mag: Vec::new(),
}
}
pub(crate) fn from_i64(v: i64) -> Zint {
let neg = v < 0;
let mut x = (v as i128).unsigned_abs();
let mut mag = Vec::new();
while x != 0 {
mag.push((x & 0xFFFF_FFFF) as u32);
x >>= 32;
}
Zint { neg, mag }
}
#[cfg(test)]
pub(crate) fn from_i128(v: i128) -> Zint {
let neg = v < 0;
let mut x = v.unsigned_abs();
let mut mag = Vec::new();
while x != 0 {
mag.push((x & 0xFFFF_FFFF) as u32);
x >>= 32;
}
Zint { neg, mag }
}
#[cfg(test)]
pub(crate) fn to_i128(&self) -> Option<i128> {
if self.mag.len() > 4 {
return None;
}
let mut m: u128 = 0;
for &limb in self.mag.iter().rev() {
m = (m << 32) | limb as u128;
}
if self.neg {
if m <= (i128::MAX as u128) + 1 {
Some(m.wrapping_neg() as i128)
} else {
None
}
} else if m <= i128::MAX as u128 {
Some(m as i128)
} else {
None
}
}
fn from_parts(neg: bool, mag: Vec<u32>) -> Zint {
if mag.is_empty() {
Zint::zero()
} else {
Zint { neg, mag }
}
}
pub(crate) fn is_zero(&self) -> bool {
self.mag.is_empty()
}
pub(crate) fn is_negative(&self) -> bool {
self.neg
}
fn is_even(&self) -> bool {
self.mag.first().map(|&l| l & 1 == 0).unwrap_or(true)
}
pub(crate) fn neg(&self) -> Zint {
Zint::from_parts(!self.neg, self.mag.clone())
}
pub(crate) fn abs(&self) -> Zint {
Zint::from_parts(false, self.mag.clone())
}
pub(crate) fn cmp(&self, o: &Zint) -> Ordering {
match (self.neg, o.neg) {
(false, true) => Ordering::Greater,
(true, false) => Ordering::Less,
(false, false) => cmp_mag(&self.mag, &o.mag),
(true, true) => cmp_mag(&o.mag, &self.mag),
}
}
pub(crate) fn add(&self, o: &Zint) -> Zint {
if self.neg == o.neg {
Zint::from_parts(self.neg, add_mag(&self.mag, &o.mag))
} else {
match cmp_mag(&self.mag, &o.mag) {
Ordering::Equal => Zint::zero(),
Ordering::Greater => Zint::from_parts(self.neg, sub_mag(&self.mag, &o.mag)),
Ordering::Less => Zint::from_parts(o.neg, sub_mag(&o.mag, &self.mag)),
}
}
}
pub(crate) fn sub(&self, o: &Zint) -> Zint {
self.add(&o.neg())
}
pub(crate) fn mul(&self, o: &Zint) -> Zint {
Zint::from_parts(self.neg != o.neg, mul_mag(&self.mag, &o.mag))
}
pub(crate) fn shl(&self, bits: usize) -> Zint {
Zint::from_parts(self.neg, shl_mag(&self.mag, bits))
}
pub(crate) fn shr(&self, bits: usize) -> Zint {
Zint::from_parts(self.neg, shr_mag(&self.mag, bits))
}
fn halve(&self) -> Zint {
Zint::from_parts(self.neg, shr_mag(&self.mag, 1))
}
pub(crate) fn bitsize(&self) -> usize {
if self.mag.is_empty() {
return 0;
}
let top = *self.mag.last().unwrap();
let top_bytes = (4 - (top.leading_zeros() as usize / 8)).max(1);
((self.mag.len() - 1) * 4 + top_bytes) * 8
}
pub(crate) fn to_i64(&self) -> Option<i64> {
if self.mag.len() > 2 {
return None;
}
let lo = *self.mag.first().unwrap_or(&0) as u64;
let hi = *self.mag.get(1).unwrap_or(&0) as u64;
let m = lo | (hi << 32);
if self.neg {
if m <= (i64::MAX as u64) + 1 {
Some((m as i128).wrapping_neg() as i64)
} else {
None
}
} else if m <= i64::MAX as u64 {
Some(m as i64)
} else {
None
}
}
}
pub(crate) fn ext_gcd(x: &Zint, y: &Zint) -> (Zint, Zint, Zint) {
let xa = x.abs();
let ya = y.abs();
if xa.is_zero() {
return (ya.clone(), Zint::zero(), sign_unit(y));
}
if ya.is_zero() {
return (xa.clone(), sign_unit(x), Zint::zero());
}
let mut shift = 0usize;
let mut xx = xa.clone();
let mut yy = ya.clone();
while xx.is_even() && yy.is_even() {
xx = xx.halve();
yy = yy.halve();
shift += 1;
}
let x0 = xx.clone();
let y0 = yy.clone();
let mut u = xx;
let mut v = yy;
let mut a = Zint::from_i64(1);
let mut b = Zint::zero();
let mut c = Zint::zero();
let mut d = Zint::from_i64(1);
loop {
while u.is_even() {
u = u.halve();
if a.is_even() && b.is_even() {
a = a.halve();
b = b.halve();
} else {
a = a.add(&y0).halve();
b = b.sub(&x0).halve();
}
}
while v.is_even() {
v = v.halve();
if c.is_even() && d.is_even() {
c = c.halve();
d = d.halve();
} else {
c = c.add(&y0).halve();
d = d.sub(&x0).halve();
}
}
if u.cmp(&v) != Ordering::Less {
u = u.sub(&v);
a = a.sub(&c);
b = b.sub(&d);
} else {
v = v.sub(&u);
c = c.sub(&a);
d = d.sub(&b);
}
if u.is_zero() {
break;
}
}
let g = v.shl(shift);
let ca = if x.is_negative() { c.neg() } else { c };
let db = if y.is_negative() { d.neg() } else { d };
(g, ca, db)
}
fn sign_unit(z: &Zint) -> Zint {
if z.is_negative() {
Zint::from_i64(-1)
} else {
Zint::from_i64(1)
}
}
#[cfg(test)]
#[path = "zint_tests.rs"]
mod zint_tests;