use core::ops::{Deref, DerefMut};
use crate::Error;
use crate::low;
#[derive(Clone, Debug)]
pub(crate) struct PosInt<const N: usize> {
words: [u64; N],
used: usize,
}
impl<const N: usize> PosInt<N> {
pub(crate) fn zero() -> Self {
Self {
words: [0; N],
used: 0,
}
}
pub(crate) fn word(w: u64) -> Self {
let mut r = Self::zero();
r.push_word(w).unwrap();
r
}
pub(crate) fn one() -> Self {
let mut r = Self::zero();
r.words[0] = 1;
r.used = 1;
r
}
pub(crate) fn fixed_one(&self) -> Self {
let mut one = Self::one();
one.expand(self);
one
}
pub(crate) fn expand(&mut self, other: &Self) {
self.used = other.used;
}
fn push_word(&mut self, word: u64) -> Result<(), Error> {
if self.used == N {
return Err(Error::WrongLength);
}
self.words[self.used] = word;
self.used += 1;
Ok(())
}
pub(crate) fn from_words(words: &[u64]) -> Result<Self, Error> {
let mut r = Self::zero();
r.words
.get_mut(..words.len())
.ok_or(Error::WrongLength)?
.copy_from_slice(words);
r.used = words.len();
Ok(r)
}
pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
let mut r = Self::zero();
let bytes = trim_leading_zeroes(bytes);
if bytes.len() > N * 8 {
return Err(Error::WrongLength);
}
let mut words = bytes.rchunks_exact(8);
for word_bytes in words.by_ref() {
r.push_word(u64::from_be_bytes(word_bytes.try_into().unwrap()))?;
}
let remainder = words.remainder();
let mut final_word = 0;
for byte in remainder.iter() {
final_word = (final_word << 8) | (*byte as u64);
}
if final_word != 0 {
r.push_word(final_word)?;
}
Ok(r)
}
pub(crate) fn to_bytes<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], Error> {
let required_bytes = self.used * 8;
let out = out.get_mut(..required_bytes).ok_or(Error::WrongLength)?;
let (chunks, _) = out.as_chunks_mut::<8>();
for (chunk, word) in chunks.iter_mut().rev().zip(self.as_words().iter()) {
chunk.copy_from_slice(&word.to_be_bytes());
}
Ok(out)
}
pub(crate) fn to_bytes_asn1<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], Error> {
let required_bytes = self.used * 8 + 1;
let out = out.get_mut(..required_bytes).ok_or(Error::WrongLength)?;
out[0] = 0x00;
{
let (_, val) = out.split_at_mut(1);
let (chunks, _) = val.as_chunks_mut::<8>();
for (chunk, word) in chunks.iter_mut().rev().zip(self.as_words().iter()) {
chunk.copy_from_slice(&word.to_be_bytes());
}
}
Ok(out)
}
#[allow(dead_code)]
pub(crate) fn debug(&self, why: &str) {
let mut bytes = [0u8; 1024];
let bytes = self.to_bytes(&mut bytes).unwrap();
print!("{why} = 0x");
for b in bytes {
print!("{b:02x}");
}
println!();
}
pub(crate) fn len_bytes(&self) -> usize {
self.len_bits().wrapping_add(7) / 8
}
pub(crate) fn len_bits(&self) -> usize {
low::bignum_bitsize(self.as_words())
}
pub(crate) fn count_trailing_zeroes(&self) -> usize {
low::bignum_ctz(self.as_words())
}
pub(crate) fn is_even(&self) -> bool {
self.words[0] & 1 == 0
}
pub(crate) fn is_coprime(&self, other: &Self) -> bool {
let mut tmp = vec![0u64; N + N];
let r = low::bignum_coprime(self.as_words(), other.as_words(), &mut tmp);
low::zeroise(&mut tmp);
r
}
pub(crate) fn as_words(&self) -> &[u64] {
&self.words[..self.used]
}
fn as_mut_words(&mut self) -> &mut [u64] {
&mut self.words[..self.used]
}
fn as_words_with_len_of(&self, len: &Self) -> &[u64] {
&self.words[..len.used]
}
pub(crate) fn equals(&self, other: &Self) -> bool {
low::bignum_eq(self.as_words(), other.as_words())
}
#[cfg(test)]
pub(crate) fn pub_equals(&self, other: &Self) -> bool {
let mut s_used = self.used;
while s_used != 0 {
if self.words[s_used - 1] == 0 {
s_used -= 1;
} else {
break;
}
}
let mut o_used = other.used;
while o_used != 0 {
if other.words[o_used - 1] == 0 {
o_used -= 1;
} else {
break;
}
}
self.words[..s_used] == other.words[..o_used]
}
pub(crate) fn less_than(&self, other: &Self) -> bool {
low::bignum_cmp_lt(self.as_words(), other.as_words()) > 0
}
#[must_use]
pub(crate) fn montifier(&self) -> Self {
let mut tmp = Self::zero();
tmp.used = self.used;
let mut montifier = Self::zero();
montifier.used = self.used;
low::bignum_montifier(
montifier.as_mut_words(),
self.as_words(),
tmp.as_mut_words(),
);
montifier
}
#[must_use]
pub(crate) fn pub_montifier(&self) -> Self {
let k = self.used;
let Some(&top) = self.as_words().last().filter(|top| **top != 0) else {
return Self::zero();
};
let shift = top.leading_zeros();
let mut d = Self::zero();
d.used = k;
for i in 0..k {
let below = i.checked_sub(1).map_or(0, |i| self.words[i]);
d.words[i] = self.words[i] << shift | below.checked_shr(64 - shift).unwrap_or(0);
}
let mut r = Self::zero();
r.used = k;
r.words[k - 1] = 1 << shift;
for _ in 0..=k {
mul_word_mod(r.as_mut_words(), d.as_words());
}
for i in 0..k {
let above = r.words.get(i + 1).copied().filter(|_| i + 1 < k);
r.words[i] = r.words[i] >> shift
| above.map_or(0, |above| above.checked_shl(64 - shift).unwrap_or(0));
}
r
}
#[must_use]
pub(crate) fn to_montgomery(&self, montifier: &Self, n: &Self) -> Self {
self.mont_mul(montifier, n, n.mont_neg_inverse())
}
#[must_use]
pub(crate) fn mont_neg_inverse(&self) -> u64 {
let mut n0 = [0];
low::bignum_negmodinv(&mut n0, &self.words[..1]);
n0[0]
}
#[must_use]
pub(crate) fn mont_sqr(&self, n: &Self, n0: u64) -> Self {
let mut r = Self::zero();
self.mont_sqr_into(&mut r, n, n0);
r
}
pub(crate) fn mont_sqr_into(&self, out: &mut Self, n: &Self, n0: u64) {
out.used = n.used;
low::mont_sqr(
out.as_mut_words(),
self.as_words_with_len_of(n),
n.as_words(),
n0,
);
}
#[must_use]
pub(crate) fn mont_mul(&self, v: &Self, n: &Self, n0: u64) -> Self {
let mut r = Self::zero();
self.mont_mul_into(v, &mut r, n, n0);
r
}
pub(crate) fn mont_mul_into(&self, v: &Self, out: &mut Self, n: &Self, n0: u64) {
out.used = n.used;
low::mont_mul(
out.as_mut_words(),
self.as_words_with_len_of(n),
v.as_words_with_len_of(n),
n.as_words(),
n0,
);
}
#[must_use]
fn mont_redc<const M: usize>(&self, n: &PosInt<M>) -> PosInt<M> {
let mut tmp = PosInt::<M>::zero();
tmp.used = n.used;
low::bignum_montredc(
tmp.as_mut_words(),
self.as_words(),
n.as_words(),
n.as_words().len() as u64,
);
tmp
}
#[must_use]
pub(crate) fn reduce<const M: usize>(
&self,
n: &PosInt<M>,
n_montifier: &PosInt<M>,
) -> PosInt<M> {
assert!(M < N);
self.mont_redc(n).to_montgomery(n_montifier, n)
}
#[must_use]
#[allow(clippy::wrong_self_convention)]
pub(crate) fn from_montgomery(self, n: &Self) -> Self {
#[cfg(any(target_arch = "x86_64", target_arch = "arm"))]
return self.mont_mul(&n.fixed_one(), n, n.mont_neg_inverse());
#[cfg(target_arch = "aarch64")]
{
let mut demont = Self::zero();
demont.used = n.used;
low::bignum_demont(demont.as_mut_words(), self.as_words(), n.as_words());
demont
}
}
#[must_use]
pub(crate) fn mul<const M: usize>(a: &Self, b: &Self) -> PosInt<M> {
assert!(M >= N + N);
let mut r = PosInt::<M>::zero();
r.used = a.used + b.used;
low::bignum_mul(r.as_mut_words(), a.as_words(), b.as_words());
r
}
pub(crate) fn mod_exp_pair(pair: [ModExp<'_, N>; 2]) -> [Self; 2] {
#[cfg(target_arch = "x86_64")]
if let Some(r) = Self::mod_exp_pair_rsaz(&pair) {
return r;
}
pair.map(|x| x.base.mod_exp(x.e, x.n, x.n_montifier, x.n_0))
}
#[cfg(target_arch = "x86_64")]
fn mod_exp_pair_rsaz(pair: &[ModExp<'_, N>; 2]) -> Option<[Self; 2]> {
let k = pair[0].n.used;
if pair.iter().any(|x| x.n.used != k || x.e.used > k) {
return None;
}
let rsaz = low::RsazAvx512::check(k)?;
let mut r = [Self::zero(), Self::zero()];
let [r0, r1] = &mut r;
r0.used = k;
r1.used = k;
rsaz.mod_exp(
[&mut r0.words[..k], &mut r1.words[..k]],
pair.each_ref().map(|x| low::Operands {
x: &x.base.words[..k],
e: &x.e.words[..k],
m: &x.n.words[..k],
rr: &x.n_montifier.words[..k],
n0: x.n_0,
}),
);
Some(r)
}
fn mod_exp(&self, e: &Self, n: &Self, n_montifier: &Self, n_0: u64) -> Self {
#[cfg(target_arch = "x86_64")]
if let Some(r) = self.mod_exp_rsaz(e, n, n_montifier, n_0) {
return r;
}
let one_mont = n.fixed_one().mont_mul(n_montifier, n, n_0);
let self_mont = self.to_montgomery(n_montifier, n);
self_mont.mont_exp(one_mont, e, n, n_0).from_montgomery(n)
}
#[cfg(target_arch = "x86_64")]
fn mod_exp_rsaz(&self, e: &Self, n: &Self, n_montifier: &Self, n_0: u64) -> Option<Self> {
let rsaz = low::Rsaz::check()?;
if n.used != 16 || e.used > 16 {
return None;
}
let mut r = Self::zero();
r.used = n.used;
rsaz.mod_exp(
r.words.first_chunk_mut()?,
self.words.first_chunk()?,
e.words.first_chunk()?,
n.words.first_chunk()?,
n_montifier.words.first_chunk()?,
n_0,
);
Some(r)
}
#[cfg(target_arch = "x86_64")]
pub(crate) fn mont_exp(&self, one_mont: Self, e: &Self, n: &Self, n_0: u64) -> Self {
let mut r = Self::zero();
r.used = n.used;
low::mont_exp(
r.as_mut_words(),
self.as_words_with_len_of(n),
one_mont.as_words_with_len_of(n),
e.as_words(),
n.as_words(),
n_0,
);
r
}
#[cfg(not(target_arch = "x86_64"))]
pub(crate) fn mont_exp(&self, one_mont: Self, e: &Self, n: &Self, n_0: u64) -> Self {
const WINDOW: usize = 5;
let k = n.used;
let Some(top) = (e.used * 64).checked_sub(1) else {
return one_mont;
};
let mut table = vec![0; k << WINDOW];
table[..k].copy_from_slice(one_mont.as_words_with_len_of(n));
table[k..2 * k].copy_from_slice(self.as_words_with_len_of(n));
for i in 2..1 << WINDOW {
let (done, rest) = table.split_at_mut(i * k);
let row = |j: usize| &done[j * k..(j + 1) * k];
match i % 2 {
0 => low::mont_sqr(&mut rest[..k], row(i / 2), n.as_words(), n_0),
_ => low::mont_mul(&mut rest[..k], row(i - 1), row(1), n.as_words(), n_0),
}
}
let select = |z: &mut Self, i: u64| {
low::bignum_copy_row_from_table(z.as_mut_words(), &table, 1 << WINDOW, k as u64, i)
};
let mut term = Self::zero();
term.used = k;
let (mut a, mut b) = (term.clone(), term.clone());
let (mut accum, mut spare) = (&mut a, &mut b);
let mut lsb = top - top % WINDOW;
select(accum, window_bits(e.as_words(), lsb, top + 1 - lsb));
while lsb > 0 {
lsb -= WINDOW;
for _ in 0..WINDOW {
accum.mont_sqr_into(spare, n, n_0);
core::mem::swap(&mut accum, &mut spare);
}
select(&mut term, window_bits(e.as_words(), lsb, WINDOW));
accum.mont_mul_into(&term, spare, n, n_0);
core::mem::swap(&mut accum, &mut spare);
}
low::zeroise(&mut table);
accum.clone()
}
#[must_use]
pub(crate) fn mod_inverse(&self, n: &Self) -> Self {
let mut r = Self::zero();
r.used = n.used;
let mut temp = vec![0u64; N * 3];
low::bignum_modinv(
r.as_mut_words(),
self.as_words_with_len_of(n),
n.as_words(),
&mut temp,
);
low::zeroise(&mut temp);
r
}
#[must_use]
pub(crate) fn reduce_even<const M: usize>(&self, m: &PosInt<M>) -> PosInt<M> {
assert!(M < N);
let mut a = PosInt::<M>::zero();
a.used = m.used;
let mut b = a.clone();
let mut addend = a.clone();
let limit = a.used.saturating_sub(1);
let i = self.used.saturating_sub(limit);
let top = &self.words[i..self.used];
a.words[..top.len()].copy_from_slice(top);
let bits = if m.used > 1 { 64 } else { 1 };
let (mut r, mut next) = (&mut a, &mut b);
for j in (0..i).rev() {
for piece in (0..64 / bits).rev() {
for _ in 0..bits {
low::bignum_modadd(
next.as_mut_words(),
r.as_words(),
r.as_words(),
m.as_words(),
);
core::mem::swap(&mut r, &mut next);
}
addend.words[0] = self.words[j] >> (piece * bits) & u64::MAX >> (64 - bits);
low::bignum_modadd(
next.as_mut_words(),
r.as_words(),
addend.as_words(),
m.as_words(),
);
core::mem::swap(&mut r, &mut next);
}
}
r.clone()
}
#[must_use]
pub(crate) fn add(&self, b: &Self) -> Self {
let mut r = Self::zero();
low::bignum_add(&mut r.words, self.as_words(), b.as_words());
r.used = low::bignum_digitsize(&r.words);
r
}
#[must_use]
pub(crate) fn sub(&self, b: &Self) -> Self {
let mut r = Self::zero();
r.used = self.used;
low::bignum_optsub(
r.as_mut_words(),
self.as_words(),
b.as_words_with_len_of(self),
1,
);
r
}
#[must_use]
pub(crate) fn sub_mod(&self, b: &Self, p: &Self) -> Self {
let mut r = Self::zero();
r.used = p.used;
low::bignum_modsub(
r.as_mut_words(),
self.as_words(),
b.as_words(),
p.as_words(),
);
r
}
#[must_use]
pub(crate) fn widen<const M: usize>(&self) -> PosInt<M> {
assert!(M >= N);
let mut r = PosInt::<M>::zero();
r.words[..self.used].copy_from_slice(self.as_words());
r.used = self.used;
r
}
}
impl<const N: usize> low::Zeroable for PosInt<N> {}
pub(crate) struct ModExp<'a, const N: usize> {
pub(crate) base: &'a PosInt<N>,
pub(crate) e: &'a PosInt<N>,
pub(crate) n: &'a PosInt<N>,
pub(crate) n_montifier: &'a PosInt<N>,
pub(crate) n_0: u64,
}
pub(crate) struct SecretPosInt<const N: usize>(PosInt<N>);
impl<const N: usize> From<PosInt<N>> for SecretPosInt<N> {
fn from(pi: PosInt<N>) -> Self {
Self(PosInt {
used: pi.used,
words: low::ct::into_secret(pi.words),
})
}
}
impl<const N: usize> Deref for SecretPosInt<N> {
type Target = PosInt<N>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const N: usize> DerefMut for SecretPosInt<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<const N: usize> Drop for SecretPosInt<N> {
fn drop(&mut self) {
low::zeroise_value(&mut self.0);
}
}
#[cfg(not(target_arch = "x86_64"))]
fn window_bits(e: &[u64], lsb: usize, width: usize) -> u64 {
let (word, shift) = (lsb / 64, lsb % 64);
let mut bits = e[word] >> shift;
if shift + width > 64 {
bits |= e[word + 1] << (64 - shift);
}
bits & ((1 << width) - 1)
}
fn trim_leading_zeroes(mut bytes: &[u8]) -> &[u8] {
while let Some((first, rest)) = bytes.split_first() {
if *first == 0x00 {
bytes = rest;
} else {
return bytes;
}
}
bytes
}
fn mul_word_mod(u: &mut [u64], d: &[u64]) {
let k = d.len();
let (Some(&d1), Some(&u2)) = (d.last(), u.last()) else {
return;
};
let d0 = k.checked_sub(2).map_or(0, |i| d[i]);
let u1 = k.checked_sub(2).map_or(0, |i| u[i]);
let u0 = k.checked_sub(3).map_or(0, |i| u[i]);
u.copy_within(..k - 1, 1);
u[0] = 0;
let num = u128::from(u2) << 64 | u128::from(u1);
let mut q = match u2 < d1 {
true => num / u128::from(d1),
false => u128::from(u64::MAX),
};
let mut rem = num - q * u128::from(d1);
while rem >> 64 == 0 && q * u128::from(d0) > (rem << 64 | u128::from(u0)) {
q -= 1;
rem += u128::from(d1);
}
let mut carry = 0u64;
let mut borrow = false;
for (u, d) in u.iter_mut().zip(d) {
let product = q * u128::from(*d) + u128::from(carry);
carry = (product >> 64) as u64;
let (diff, b1) = u.overflowing_sub(product as u64);
let (diff, b2) = diff.overflowing_sub(u64::from(borrow));
*u = diff;
borrow = b1 | b2;
}
if u2 < carry || (u2 == carry && borrow) {
let mut carry = false;
for (u, d) in u.iter_mut().zip(d) {
let (sum, c1) = u.overflowing_add(*d);
let (sum, c2) = sum.overflowing_add(u64::from(carry));
*u = sum;
carry = c1 | c2;
}
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn pub_montifier() {
for bits in [1024usize, 1025, 2047, 2048, 2049, 3072, 4095, 8192] {
let len = bits.div_ceil(64);
let mut low = PosInt::<128>::zero();
low.used = len;
low.words[0] = 1;
low.words[(bits - 1) / 64] |= 1 << ((bits - 1) % 64);
let mut high = PosInt::<128>::zero();
high.used = len;
high.words[..len].fill(u64::MAX);
high.words[len - 1] >>= 64 * len - bits;
for n in [low, high] {
assert!(n.pub_montifier().pub_equals(&n.montifier()), "{bits} bits");
}
}
}
#[cfg(not(target_arch = "x86_64"))]
#[test]
fn mont_exp_matches_square_and_multiply() {
let mut state = 0x6a09_e667_f3bc_c908u64;
let mut word = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
for k in [1, 2, 7, 8, 16, 17] {
let mut n = PosInt::<32>::zero();
n.used = k;
n.words[..k].fill_with(&mut word);
n.words[0] |= 1;
n.words[k - 1] |= 1 << 63;
let n_0 = n.mont_neg_inverse();
let montifier = n.montifier();
let one_mont = n.fixed_one().to_montgomery(&montifier, &n);
for case in 0..5 {
let mut x = PosInt::<32>::zero();
x.used = k;
x.words[..k].fill_with(&mut word);
x.words[k - 1] >>= 1;
match case {
0 => x.words[..k].fill(0),
1 => {
x.words[..k].copy_from_slice(n.as_words());
x.words[0] -= 1;
}
_ => {}
}
let x = x.to_montgomery(&montifier, &n);
for len in 1..=5 {
let mut e = PosInt::<32>::zero();
e.used = len;
match case {
2 => {}
3 => e.words[..len].fill(u64::MAX),
_ => e.words[..len].fill_with(&mut word),
}
let mut want = one_mont.clone();
for i in (0..len * 64).rev() {
want = want.mont_sqr(&n, n_0);
if e.words[i / 64] >> (i % 64) & 1 == 1 {
want = want.mont_mul(&x, &n, n_0);
}
}
let got = x.mont_exp(one_mont.clone(), &e, &n, n_0);
assert!(
got.equals(&want),
"{k} words, case {case}, {len}-word exponent"
);
}
}
}
}
#[test]
fn mul_word_mod() {
const TOP: u64 = 1 << 63;
for (d, want, why) in [
([1, 0, TOP], [u64::MAX, u64::MAX, TOP - 1], "added back"),
([0, u64::MAX, TOP], [0, 1, TOP - 1], "refined"),
] {
let mut u = [0, 0, 1];
super::mul_word_mod(&mut u, &d);
assert_eq!(u, want, "{why}");
}
}
#[test]
fn from_bytes() {
assert!(
PosInt::<1>::from_bytes(&[])
.unwrap()
.pub_equals(&PosInt::<1>::zero())
);
assert!(
PosInt::<1>::from_bytes(&[0])
.unwrap()
.pub_equals(&PosInt::<1>::zero())
);
assert!(
PosInt::<1>::from_bytes(&[0; 8])
.unwrap()
.pub_equals(&PosInt::<1>::zero())
);
assert_eq!(
PosInt::<1>::from_bytes(&[1; 9]).unwrap_err(),
Error::WrongLength
);
PosInt::<1>::from_bytes(&[0; 9]).unwrap();
assert_eq!(
PosInt::<2>::from_bytes(&[1, 0, 0, 0, 0, 0, 0, 0, 2])
.unwrap()
.as_words(),
&[2, 1]
);
assert_eq!(
PosInt::<1>::from_bytes(&[0x11, 0x22, 0x33, 0x44])
.unwrap()
.as_words(),
&[0x11223344]
);
let p = PosInt::<2>::from_bytes(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44])
.unwrap();
let mut expected = PosInt::<2>::zero();
assert!(expected.push_word(0x11223344).is_ok());
assert!(p.pub_equals(&expected));
assert!(expected.push_word(0).is_ok());
assert!(p.pub_equals(&expected));
}
#[test]
fn to_bytes() {
let mut buf = [0xff; 8];
assert!(PosInt::<2>::zero().to_bytes(&mut buf).unwrap().is_empty());
let all_bits_set = PosInt::<2>::from_bytes(&[0xff; 16]).unwrap();
assert_eq!(
all_bits_set.to_bytes(&mut buf).unwrap_err(),
Error::WrongLength
);
assert_eq!(
all_bits_set.to_bytes_asn1(&mut buf).unwrap_err(),
Error::WrongLength
);
let mut buf16 = [0; 16];
all_bits_set.to_bytes(&mut buf16).unwrap();
assert_eq!(buf16, [0xff; 16]);
let mut buf_asn1 = [0; 17];
all_bits_set.to_bytes_asn1(&mut buf_asn1).unwrap();
assert_eq!(
buf_asn1,
[
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff
]
);
}
#[test]
fn mul() {
let zero_1 = PosInt::<1>::zero();
let zero_2 = PosInt::<2>::zero();
let one_1 = PosInt::<1>::one();
let one_2 = PosInt::<2>::one();
let r = PosInt::mul(&one_1, &one_1);
r.debug("1 x 1");
assert!(r.pub_equals(&one_2));
let r = PosInt::mul(&zero_1, &one_1);
r.debug("1 x 0");
assert!(r.pub_equals(&zero_2));
let r = PosInt::mul(&zero_1, &zero_1);
r.debug("0 x 0");
assert!(r.pub_equals(&zero_2));
let x_4 = PosInt::<4>::from_bytes(b"\xed\x1f\xde\xb5\xc6\x39\x43\x8f\xea\x1d\x05\x9c\xba\xa8\xd3\x7c\x13\x96\xf4\x96\x1c\x8e\x5f\x52\x8f\x3c\x4c\x3c\x45\xe5\x75\xa2").unwrap();
let y_4 = PosInt::<4>::from_bytes(b"\x38\x8f\xb5\xd8\xbd\xad\x46\xfd\xe2\x8e\x33\x11\xe4\xdd\xef\x79\x70\xfe\x4a\xb9\xef\x24\x85\xbe\x4f\xde\x81\x79\x36\x0c\x9c\x86").unwrap();
let xy_8: PosInt<8> = PosInt::mul(&x_4, &y_4);
println!("{xy_8:x?}");
let expect_8 = PosInt::<8>::from_bytes(b"\x34\x64\x15\xf5\x75\xf1\xb7\x01\x8b\x1d\xc4\x68\xde\x4b\xf7\x6e\x6f\x62\x87\xa1\x44\x08\x6f\xb1\x85\x9c\xf3\x84\x41\x64\x48\x9d\x16\xe7\xb0\xd0\xd3\x56\x13\xba\xa2\xb9\xa6\x12\x1a\x6c\x2f\x93\xcd\xe4\x20\xfa\x41\xa4\xef\xa2\xab\xcd\x8b\x48\x19\x62\x4a\xcc").unwrap();
assert!(xy_8.pub_equals(&expect_8));
}
#[test]
fn basics() {
let zero = PosInt::<1>::zero();
let one = PosInt::<1>::one();
let two = PosInt::<1>::word(2);
let max_one_word = PosInt::<1>::word(u64::MAX);
assert_eq!(zero.len_bits(), 0);
assert!(zero.is_even());
assert!(zero.equals(&zero));
assert_eq!(zero.count_trailing_zeroes(), 0);
assert!(zero.less_than(&one));
assert!(!one.less_than(&zero));
assert_eq!(one.len_bits(), 1);
assert!(!one.is_even());
assert_eq!(one.count_trailing_zeroes(), 0);
assert!(one.equals(&one));
assert_eq!(two.len_bits(), 2);
assert!(two.is_even());
assert_eq!(two.count_trailing_zeroes(), 1);
assert!(two.equals(&two));
assert_eq!(max_one_word.len_bits(), 64);
assert!(!max_one_word.is_even());
assert!(max_one_word.equals(&max_one_word));
assert!(one.less_than(&max_one_word));
let two_words = PosInt::<2>::from_bytes(&[0xff; 16]).unwrap();
assert_eq!(two_words.len_bits(), 128);
assert!(!two_words.is_even());
assert!(two_words.equals(&two_words));
assert!(two_words.fixed_one().less_than(&two_words));
let mut two_words = PosInt::<2>::zero();
const LOW_ORDER_WORD: u64 = 0xfedcba9876543210;
const HIGH_ORDER_WORD: u64 = 0x1234;
assert!(two_words.push_word(LOW_ORDER_WORD).is_ok());
assert!(two_words.push_word(HIGH_ORDER_WORD).is_ok());
assert!(two_words.push_word(0).is_err());
let mut out = [0; 15];
assert!(two_words.to_bytes(&mut out).is_err());
let mut out = [0; 16];
assert!(two_words.to_bytes(&mut out).is_ok());
assert_eq!(
u64::from_be_bytes(out[0..8].try_into().unwrap()),
HIGH_ORDER_WORD
);
assert_eq!(
u64::from_be_bytes(out[8..16].try_into().unwrap()),
LOW_ORDER_WORD
);
}
#[test]
fn sub_narrower() {
let w = PosInt::<2>::from_words(&[1, 1]).unwrap();
assert_eq!(w.sub(&PosInt::word(3)).as_words(), &[u64::MAX - 1, 0]);
}
#[test]
fn test_reduce_even() {
let mut a = PosInt::<32>::zero();
for i in 0..32 {
a.push_word(0x8000_1234_5678_0000 + i).unwrap();
}
let m = PosInt::<16> {
used: 16,
words: [0xff55_3322_ff55_3322; 16],
};
let c = a.reduce_even(&m);
assert_eq!(
c.as_words(),
&[
0x1e0a0609caf9e1b2,
0x1e0a0609caf9e1b4,
0x1e0a0609caf9e1b6,
0x1e0a0609caf9e1b8,
0x1e0a0609caf9e1ba,
0x1e0a0609caf9e1bc,
0x1e0a0609caf9e1be,
0x1e0a0609caf9e1c0,
0x1e0a0609caf9e1c2,
0x1e0a0609caf9e1c4,
0x1e0a0609caf9e1c6,
0x1e0a0609caf9e1c8,
0x1e0a0609caf9e1ca,
0x1e0a0609caf9e1cc,
0x1e0a0609caf9e1ce,
0x1e0a0609caf9e1d0
]
);
}
}