use super::cmp::ct_select;
use super::{HeaplessBigInt, is_zero, zero};
use crate::MachineWord;
use const_num_traits::{
BorrowingSub, Bounded, CarryingAdd, CarryingMul, CheckedAdd, CheckedMul, CheckedSub, Ct, Nct,
OverflowingAdd, OverflowingMul, OverflowingSub, Personality, PersonalityTag, SaturatingAdd,
SaturatingMul, SaturatingSub, WrappingAdd, WrappingMul, WrappingSub,
};
use core::marker::PhantomData;
#[inline]
pub(crate) fn max_at_len<T: MachineWord, const CAP: usize, P: Personality>(
len: u16,
) -> HeaplessBigInt<T, CAP, P> {
let mut limbs = [zero::<T>(); CAP];
for l in &mut limbs[..len as usize] {
*l = <T as Bounded>::max_value();
}
HeaplessBigInt {
limbs,
len,
_p: PhantomData,
}
}
#[inline]
fn panic_on_overflow_if_nct<P: Personality>(overflow: bool, msg: &'static str) {
match P::TAG {
PersonalityTag::Nct => assert!(!overflow, "{}", msg),
PersonalityTag::Ct => {}
}
}
macro_rules! forward_arith_receivers {
($imp:ident, $method:ident $($bound:tt)*) => {
impl<T: MachineWord $($bound)*, const CAP: usize, P: Personality> core::ops::$imp
for HeaplessBigInt<T, CAP, P>
{
type Output = Self;
fn $method(self, other: Self) -> Self {
(&self).$method(&other)
}
}
impl<T: MachineWord $($bound)*, const CAP: usize, P: Personality>
core::ops::$imp<&HeaplessBigInt<T, CAP, P>> for HeaplessBigInt<T, CAP, P>
{
type Output = Self;
fn $method(self, other: &Self) -> Self {
(&self).$method(other)
}
}
impl<T: MachineWord $($bound)*, const CAP: usize, P: Personality>
core::ops::$imp<HeaplessBigInt<T, CAP, P>> for &HeaplessBigInt<T, CAP, P>
{
type Output = HeaplessBigInt<T, CAP, P>;
fn $method(self, other: HeaplessBigInt<T, CAP, P>) -> HeaplessBigInt<T, CAP, P> {
self.$method(&other)
}
}
};
}
#[inline]
pub(crate) fn add_slice<T: MachineWord>(a: &[T], b: &[T], out: &mut [T], n: usize) -> bool {
let mut carry = false;
for ((&ai, &bi), oi) in a[..n].iter().zip(&b[..n]).zip(&mut out[..n]) {
let (sum, c) = <T as CarryingAdd>::carrying_add(ai, bi, carry);
*oi = sum;
carry = c;
}
carry
}
#[inline]
pub(crate) fn sub_slice<T: MachineWord>(a: &[T], b: &[T], out: &mut [T], n: usize) -> bool {
let mut borrow = false;
for ((&ai, &bi), oi) in a[..n].iter().zip(&b[..n]).zip(&mut out[..n]) {
let (diff, br) = <T as BorrowingSub>::borrowing_sub(ai, bi, borrow);
*oi = diff;
borrow = br;
}
borrow
}
#[inline]
pub(crate) fn mul_slice<T: MachineWord + CarryingMul<Unsigned = T, Output = T>>(
a: &[T],
a_n: usize,
b: &[T],
b_n: usize,
out: &mut [T],
out_n: usize,
) {
let a = &a[..a_n];
let b = &b[..b_n];
let out = &mut out[..out_n];
let mut i = 0;
while i < a_n {
let mut carry = zero::<T>();
let mut j = 0;
while j < b_n {
let pos = i + j;
if pos < out_n {
let (lo, hi) = <T as CarryingMul>::carrying_mul(a[i], b[j], carry);
let (sum, c1) = <T as CarryingAdd>::carrying_add(out[pos], lo, false);
out[pos] = sum;
let (new_carry, _) = <T as CarryingAdd>::carrying_add(hi, zero::<T>(), c1);
carry = new_carry;
}
j += 1;
}
let tail = i + b_n;
if tail < out_n {
let (sum, _) = <T as CarryingAdd>::carrying_add(out[tail], carry, false);
out[tail] = sum;
}
i += 1;
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> HeaplessBigInt<T, CAP, P> {
pub fn wrapping_add(&self, other: &Self) -> Self {
let out_len = core::cmp::max(self.len as usize, other.len as usize);
let mut out = Self::new_zero_with_len(out_len as u16);
let _carry = add_slice(&self.limbs, &other.limbs, &mut out.limbs, out_len);
debug_assert!(zero_tail_ok(&out.limbs, out_len));
out
}
pub fn overflowing_add(&self, other: &Self) -> (Self, bool) {
let out_len = core::cmp::max(self.len as usize, other.len as usize);
let mut out = Self::new_zero_with_len(out_len as u16);
let carry = add_slice(&self.limbs, &other.limbs, &mut out.limbs, out_len);
(out, carry)
}
pub fn checked_add(&self, other: &Self) -> Option<Self> {
let (res, overflow) = self.overflowing_add(other);
if overflow { None } else { Some(res) }
}
pub fn wrapping_sub(&self, other: &Self) -> Self {
let out_len = core::cmp::max(self.len as usize, other.len as usize);
let mut out = Self::new_zero_with_len(out_len as u16);
let _borrow = sub_slice(&self.limbs, &other.limbs, &mut out.limbs, out_len);
debug_assert!(zero_tail_ok(&out.limbs, out_len));
out
}
pub fn overflowing_sub(&self, other: &Self) -> (Self, bool) {
let out_len = core::cmp::max(self.len as usize, other.len as usize);
let mut out = Self::new_zero_with_len(out_len as u16);
let borrow = sub_slice(&self.limbs, &other.limbs, &mut out.limbs, out_len);
(out, borrow)
}
pub fn checked_sub(&self, other: &Self) -> Option<Self> {
let (res, borrow) = self.overflowing_sub(other);
if borrow { None } else { Some(res) }
}
}
impl<T: MachineWord + CarryingMul<Unsigned = T, Output = T>, const CAP: usize, P: Personality>
HeaplessBigInt<T, CAP, P>
{
pub fn wrapping_mul(&self, other: &Self) -> Self {
let out_len = core::cmp::max(self.len as usize, other.len as usize);
let mut out = Self::new_zero_with_len(out_len as u16);
mul_slice(
&self.limbs,
self.len as usize,
&other.limbs,
other.len as usize,
&mut out.limbs,
out_len,
);
debug_assert!(zero_tail_ok(&out.limbs, out_len));
out
}
pub fn overflowing_mul(&self, other: &Self) -> (Self, bool) {
let zero_v = <Self as const_num_traits::Zero>::zero();
let (lo, hi) = <Self as CarryingMul>::carrying_mul(*self, *other, zero_v);
(lo, !<Self as const_num_traits::Zero>::is_zero(&hi))
}
pub fn checked_mul(&self, other: &Self) -> Option<Self> {
let (res, overflow) = self.overflowing_mul(other);
if overflow { None } else { Some(res) }
}
}
impl<T, const CAP: usize> CheckedAdd for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = Self;
fn checked_add(self, v: Self) -> Option<Self> {
Self::checked_add(&self, &v)
}
}
impl<T, const CAP: usize> CheckedMul for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
fn checked_mul(self, v: Self) -> Option<Self> {
Self::checked_mul(&self, &v)
}
}
impl<T, const CAP: usize> CheckedSub for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = Self;
fn checked_sub(self, v: Self) -> Option<Self> {
Self::checked_sub(&self, &v)
}
}
impl<T, const CAP: usize> CheckedAdd for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn checked_add(self, v: Self) -> Option<Self::Output> {
HeaplessBigInt::<T, CAP, Nct>::checked_add(self, v)
}
}
impl<T, const CAP: usize> CheckedMul for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn checked_mul(self, v: Self) -> Option<Self::Output> {
HeaplessBigInt::<T, CAP, Nct>::checked_mul(self, v)
}
}
impl<T, const CAP: usize> CheckedSub for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn checked_sub(self, v: Self) -> Option<Self::Output> {
HeaplessBigInt::<T, CAP, Nct>::checked_sub(self, v)
}
}
#[cfg(feature = "num-traits")]
impl<T, const CAP: usize> num_traits::CheckedAdd for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
fn checked_add(&self, v: &Self) -> Option<Self> {
Self::checked_add(self, v)
}
}
#[cfg(feature = "num-traits")]
impl<T, const CAP: usize> num_traits::CheckedSub for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
fn checked_sub(&self, v: &Self) -> Option<Self> {
Self::checked_sub(self, v)
}
}
#[cfg(feature = "num-traits")]
impl<T, const CAP: usize> num_traits::CheckedMul for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
fn checked_mul(&self, v: &Self) -> Option<Self> {
Self::checked_mul(self, v)
}
}
impl<T, const CAP: usize> SaturatingAdd for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = Self;
fn saturating_add(self, v: Self) -> Self {
Self::saturating_add(&self, &v)
}
}
impl<T, const CAP: usize> SaturatingSub for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = Self;
fn saturating_sub(self, v: Self) -> Self {
Self::saturating_sub(&self, &v)
}
}
impl<T, const CAP: usize> SaturatingMul for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
fn saturating_mul(self, v: Self) -> Self {
Self::saturating_mul(&self, &v)
}
}
impl<T, const CAP: usize> SaturatingAdd for HeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + subtle::ConditionallySelectable,
{
type Output = Self;
fn saturating_add(self, v: Self) -> Self {
let (res, overflow) = OverflowingAdd::overflowing_add(self, v);
ct_select(&res, &max_at_len(res.len), overflow)
}
}
impl<T, const CAP: usize> SaturatingSub for HeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + subtle::ConditionallySelectable,
{
type Output = Self;
fn saturating_sub(self, v: Self) -> Self {
let (res, borrow) = OverflowingSub::overflowing_sub(self, v);
ct_select(&res, &Self::new_zero_with_len(res.len), borrow)
}
}
impl<T, const CAP: usize> SaturatingMul for HeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T> + subtle::ConditionallySelectable,
{
type Output = Self;
fn saturating_mul(self, v: Self) -> Self {
let (res, overflow) = OverflowingMul::overflowing_mul(self, v);
ct_select(&res, &max_at_len(res.len), overflow)
}
}
impl<T, const CAP: usize> SaturatingAdd for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn saturating_add(self, v: Self) -> Self::Output {
<HeaplessBigInt<T, CAP, Nct> as SaturatingAdd>::saturating_add(*self, *v)
}
}
impl<T, const CAP: usize> SaturatingSub for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn saturating_sub(self, v: Self) -> Self::Output {
<HeaplessBigInt<T, CAP, Nct> as SaturatingSub>::saturating_sub(*self, *v)
}
}
impl<T, const CAP: usize> SaturatingMul for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn saturating_mul(self, v: Self) -> Self::Output {
<HeaplessBigInt<T, CAP, Nct> as SaturatingMul>::saturating_mul(*self, *v)
}
}
impl<T, const CAP: usize> SaturatingAdd for &HeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + subtle::ConditionallySelectable,
{
type Output = HeaplessBigInt<T, CAP, Ct>;
fn saturating_add(self, v: Self) -> Self::Output {
<HeaplessBigInt<T, CAP, Ct> as SaturatingAdd>::saturating_add(*self, *v)
}
}
impl<T, const CAP: usize> SaturatingSub for &HeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + subtle::ConditionallySelectable,
{
type Output = HeaplessBigInt<T, CAP, Ct>;
fn saturating_sub(self, v: Self) -> Self::Output {
<HeaplessBigInt<T, CAP, Ct> as SaturatingSub>::saturating_sub(*self, *v)
}
}
impl<T, const CAP: usize> SaturatingMul for &HeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T> + subtle::ConditionallySelectable,
{
type Output = HeaplessBigInt<T, CAP, Ct>;
fn saturating_mul(self, v: Self) -> Self::Output {
<HeaplessBigInt<T, CAP, Ct> as SaturatingMul>::saturating_mul(*self, *v)
}
}
impl<T, const CAP: usize, P: Personality> const_num_traits::ops::ct::CtCheckedAdd
for HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
fn ct_checked_add(&self, v: &Self) -> subtle::CtOption<Self> {
let (val, overflow) = self.overflowing_add(v);
subtle::CtOption::new(val, subtle::Choice::from(!overflow as u8))
}
}
impl<T, const CAP: usize, P: Personality> const_num_traits::ops::ct::CtCheckedSub
for HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
fn ct_checked_sub(&self, v: &Self) -> subtle::CtOption<Self> {
let (val, borrow) = self.overflowing_sub(v);
subtle::CtOption::new(val, subtle::Choice::from(!borrow as u8))
}
}
impl<T, const CAP: usize, P: Personality> const_num_traits::ops::ct::CtCheckedMul
for HeaplessBigInt<T, CAP, P>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
fn ct_checked_mul(&self, v: &Self) -> subtle::CtOption<Self> {
let (val, overflow) = self.overflowing_mul(v);
subtle::CtOption::new(val, subtle::Choice::from(!overflow as u8))
}
}
#[cfg(feature = "num-traits")]
impl<T, const CAP: usize> num_traits::Saturating for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
fn saturating_add(self, v: Self) -> Self {
<Self as SaturatingAdd>::saturating_add(self, v)
}
fn saturating_sub(self, v: Self) -> Self {
<Self as SaturatingSub>::saturating_sub(self, v)
}
}
fn trim_content<T: MachineWord, const CAP: usize, P: Personality>(
mut v: HeaplessBigInt<T, CAP, P>,
) -> HeaplessBigInt<T, CAP, P> {
let mut new_len: u16 = 0;
let mut i = 0;
while i < v.len as usize {
if !is_zero(&v.limbs[i]) {
new_len = (i + 1) as u16;
}
i += 1;
}
v.len = new_len;
v
}
impl<T: MachineWord, const CAP: usize> HeaplessBigInt<T, CAP, Nct> {
#[inline]
pub fn trim(self) -> Self {
trim_content(self)
}
pub fn saturating_add(&self, other: &Self) -> Self {
let (res, overflow) = self.overflowing_add(other);
if overflow { max_at_len(res.len) } else { res }
}
pub fn saturating_sub(&self, other: &Self) -> Self {
let (res, borrow) = self.overflowing_sub(other);
if borrow {
Self::new_zero_with_len(res.len)
} else {
res
}
}
}
impl<T: MachineWord + CarryingMul<Unsigned = T, Output = T>, const CAP: usize>
HeaplessBigInt<T, CAP, Nct>
{
pub fn saturating_mul(&self, other: &Self) -> Self {
let (res, overflow) = self.overflowing_mul(other);
if overflow { max_at_len(res.len) } else { res }
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> core::ops::Add<&HeaplessBigInt<T, CAP, P>>
for &HeaplessBigInt<T, CAP, P>
{
type Output = HeaplessBigInt<T, CAP, P>;
fn add(self, other: &HeaplessBigInt<T, CAP, P>) -> Self::Output {
let (res, overflow) = self.overflowing_add(other);
panic_on_overflow_if_nct::<P>(overflow, "HeaplessBigInt::add overflow");
res
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> core::ops::Sub<&HeaplessBigInt<T, CAP, P>>
for &HeaplessBigInt<T, CAP, P>
{
type Output = HeaplessBigInt<T, CAP, P>;
fn sub(self, other: &HeaplessBigInt<T, CAP, P>) -> Self::Output {
let (res, borrow) = self.overflowing_sub(other);
panic_on_overflow_if_nct::<P>(borrow, "HeaplessBigInt::sub underflow");
res
}
}
impl<T: MachineWord + CarryingMul<Unsigned = T, Output = T>, const CAP: usize, P: Personality>
core::ops::Mul<&HeaplessBigInt<T, CAP, P>> for &HeaplessBigInt<T, CAP, P>
{
type Output = HeaplessBigInt<T, CAP, P>;
fn mul(self, other: &HeaplessBigInt<T, CAP, P>) -> Self::Output {
let (res, overflow) = self.overflowing_mul(other);
panic_on_overflow_if_nct::<P>(overflow, "HeaplessBigInt::mul overflow");
res
}
}
forward_arith_receivers!(Add, add);
forward_arith_receivers!(Sub, sub);
forward_arith_receivers!(Mul, mul + CarryingMul<Unsigned = T, Output = T>);
impl<T: MachineWord, const CAP: usize, P: Personality> core::ops::AddAssign
for HeaplessBigInt<T, CAP, P>
{
fn add_assign(&mut self, other: Self) {
self.add_assign(&other);
}
}
impl<T: MachineWord, const CAP: usize, P: Personality>
core::ops::AddAssign<&HeaplessBigInt<T, CAP, P>> for HeaplessBigInt<T, CAP, P>
{
fn add_assign(&mut self, other: &Self) {
let out_len = core::cmp::max(self.len as usize, other.len as usize);
let mut out = Self::new_zero_with_len(out_len as u16);
let overflow = add_slice(&self.limbs, &other.limbs, &mut out.limbs, out_len);
panic_on_overflow_if_nct::<P>(overflow, "HeaplessBigInt::add overflow");
*self = out;
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> core::ops::SubAssign
for HeaplessBigInt<T, CAP, P>
{
fn sub_assign(&mut self, other: Self) {
self.sub_assign(&other);
}
}
impl<T: MachineWord, const CAP: usize, P: Personality>
core::ops::SubAssign<&HeaplessBigInt<T, CAP, P>> for HeaplessBigInt<T, CAP, P>
{
fn sub_assign(&mut self, other: &Self) {
let out_len = core::cmp::max(self.len as usize, other.len as usize);
let mut out = Self::new_zero_with_len(out_len as u16);
let borrow = sub_slice(&self.limbs, &other.limbs, &mut out.limbs, out_len);
panic_on_overflow_if_nct::<P>(borrow, "HeaplessBigInt::sub underflow");
*self = out;
}
}
impl<T: MachineWord + CarryingMul<Unsigned = T, Output = T>, const CAP: usize, P: Personality>
core::ops::MulAssign for HeaplessBigInt<T, CAP, P>
{
fn mul_assign(&mut self, other: Self) {
self.mul_assign(&other);
}
}
impl<T: MachineWord + CarryingMul<Unsigned = T, Output = T>, const CAP: usize, P: Personality>
core::ops::MulAssign<&HeaplessBigInt<T, CAP, P>> for HeaplessBigInt<T, CAP, P>
{
fn mul_assign(&mut self, other: &Self) {
let zero_v = <Self as const_num_traits::Zero>::zero();
let (lo, hi) = <Self as CarryingMul>::carrying_mul(*self, *other, zero_v);
let overflow = !<Self as const_num_traits::Zero>::is_zero(&hi);
panic_on_overflow_if_nct::<P>(overflow, "HeaplessBigInt::mul overflow");
*self = lo;
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> WrappingAdd for HeaplessBigInt<T, CAP, P> {
type Output = Self;
fn wrapping_add(self, v: Self) -> Self::Output {
Self::wrapping_add(&self, &v)
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> WrappingSub for HeaplessBigInt<T, CAP, P> {
type Output = Self;
fn wrapping_sub(self, v: Self) -> Self::Output {
Self::wrapping_sub(&self, &v)
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> OverflowingAdd
for HeaplessBigInt<T, CAP, P>
{
type Output = Self;
fn overflowing_add(self, v: Self) -> (Self::Output, bool) {
Self::overflowing_add(&self, &v)
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> OverflowingSub
for HeaplessBigInt<T, CAP, P>
{
type Output = Self;
fn overflowing_sub(self, v: Self) -> (Self::Output, bool) {
Self::overflowing_sub(&self, &v)
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> WrappingAdd for &HeaplessBigInt<T, CAP, P> {
type Output = HeaplessBigInt<T, CAP, P>;
fn wrapping_add(self, v: Self) -> Self::Output {
HeaplessBigInt::wrapping_add(self, v)
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> WrappingSub for &HeaplessBigInt<T, CAP, P> {
type Output = HeaplessBigInt<T, CAP, P>;
fn wrapping_sub(self, v: Self) -> Self::Output {
HeaplessBigInt::wrapping_sub(self, v)
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> OverflowingAdd
for &HeaplessBigInt<T, CAP, P>
{
type Output = HeaplessBigInt<T, CAP, P>;
fn overflowing_add(self, v: Self) -> (Self::Output, bool) {
HeaplessBigInt::overflowing_add(self, v)
}
}
impl<T: MachineWord, const CAP: usize, P: Personality> OverflowingSub
for &HeaplessBigInt<T, CAP, P>
{
type Output = HeaplessBigInt<T, CAP, P>;
fn overflowing_sub(self, v: Self) -> (Self::Output, bool) {
HeaplessBigInt::overflowing_sub(self, v)
}
}
impl<T, const CAP: usize, P: Personality> WrappingMul for HeaplessBigInt<T, CAP, P>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
fn wrapping_mul(self, v: Self) -> Self::Output {
Self::wrapping_mul(&self, &v)
}
}
impl<T, const CAP: usize, P: Personality> WrappingMul for &HeaplessBigInt<T, CAP, P>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = HeaplessBigInt<T, CAP, P>;
fn wrapping_mul(self, v: Self) -> Self::Output {
HeaplessBigInt::wrapping_mul(self, v)
}
}
impl<T, const CAP: usize, P: Personality> OverflowingMul for HeaplessBigInt<T, CAP, P>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
fn overflowing_mul(self, v: Self) -> (Self::Output, bool) {
Self::overflowing_mul(&self, &v)
}
}
impl<T, const CAP: usize, P: Personality> OverflowingMul for &HeaplessBigInt<T, CAP, P>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = HeaplessBigInt<T, CAP, P>;
fn overflowing_mul(self, v: Self) -> (Self::Output, bool) {
HeaplessBigInt::overflowing_mul(self, v)
}
}
impl<T, const CAP: usize, P: Personality> CarryingAdd for HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
type Output = Self;
fn carrying_add(self, rhs: Self, carry_in: bool) -> (Self::Output, bool) {
let out_len = core::cmp::max(self.len as usize, rhs.len as usize);
let mut out_limbs = [zero::<T>(); CAP];
let mut carry = carry_in;
let mut i = 0;
while i < out_len {
let (sum, c) = <T as CarryingAdd>::carrying_add(self.limbs[i], rhs.limbs[i], carry);
out_limbs[i] = sum;
carry = c;
i += 1;
}
(
HeaplessBigInt {
limbs: out_limbs,
len: out_len as u16,
_p: PhantomData,
},
carry,
)
}
}
impl<T, const CAP: usize, P: Personality> CarryingAdd for &HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
type Output = HeaplessBigInt<T, CAP, P>;
fn carrying_add(self, rhs: Self, carry_in: bool) -> (Self::Output, bool) {
<HeaplessBigInt<T, CAP, P> as CarryingAdd>::carrying_add(*self, *rhs, carry_in)
}
}
impl<T, const CAP: usize, P: Personality> BorrowingSub for HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
type Output = Self;
fn borrowing_sub(self, rhs: Self, borrow_in: bool) -> (Self::Output, bool) {
let out_len = core::cmp::max(self.len as usize, rhs.len as usize);
let mut out_limbs = [zero::<T>(); CAP];
let mut borrow = borrow_in;
let mut i = 0;
while i < out_len {
let (diff, br) =
<T as BorrowingSub>::borrowing_sub(self.limbs[i], rhs.limbs[i], borrow);
out_limbs[i] = diff;
borrow = br;
i += 1;
}
(
HeaplessBigInt {
limbs: out_limbs,
len: out_len as u16,
_p: PhantomData,
},
borrow,
)
}
}
impl<T, const CAP: usize, P: Personality> BorrowingSub for &HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
type Output = HeaplessBigInt<T, CAP, P>;
fn borrowing_sub(self, rhs: Self, borrow_in: bool) -> (Self::Output, bool) {
<HeaplessBigInt<T, CAP, P> as BorrowingSub>::borrowing_sub(*self, *rhs, borrow_in)
}
}
impl<T, const CAP: usize, P: Personality> CarryingMul for HeaplessBigInt<T, CAP, P>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Unsigned = Self;
type Output = Self;
fn carrying_mul(self, rhs: Self, carry: Self) -> (Self::Unsigned, Self::Output) {
let zero_v = <Self as const_num_traits::Zero>::zero();
self.carrying_mul_add(rhs, carry, zero_v)
}
fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> (Self::Unsigned, Self::Output) {
let w = core::cmp::max(
core::cmp::max(self.len as usize, rhs.len as usize),
core::cmp::max(carry.len as usize, add.len as usize),
);
let mut lo_limbs = [zero::<T>(); CAP];
let mut hi_limbs = [zero::<T>(); CAP];
let a_n = self.len as usize;
let b_n = rhs.len as usize;
let mut i = 0;
while i < a_n {
let mut c = zero::<T>();
let mut j = 0;
while j < b_n {
let pos = i + j;
let (t_lo, t_hi) = <T as CarryingMul>::carrying_mul(self.limbs[i], rhs.limbs[j], c);
let existing = if pos < w {
lo_limbs[pos]
} else {
hi_limbs[pos - w]
};
let (sum, c1) = <T as CarryingAdd>::carrying_add(existing, t_lo, false);
if pos < w {
lo_limbs[pos] = sum;
} else {
hi_limbs[pos - w] = sum;
}
let (new_c, _) = <T as CarryingAdd>::carrying_add(t_hi, zero::<T>(), c1);
c = new_c;
j += 1;
}
let tail = i + b_n;
if tail < w {
let (sum, _) = <T as CarryingAdd>::carrying_add(lo_limbs[tail], c, false);
lo_limbs[tail] = sum;
} else {
let (sum, _) = <T as CarryingAdd>::carrying_add(hi_limbs[tail - w], c, false);
hi_limbs[tail - w] = sum;
}
i += 1;
}
for src in [&carry, &add] {
let mut cin = false;
let mut i = 0;
while i < w {
let (sum, c) = <T as CarryingAdd>::carrying_add(lo_limbs[i], src.limbs[i], cin);
lo_limbs[i] = sum;
cin = c;
i += 1;
}
let mut i = 0;
match P::TAG {
PersonalityTag::Nct => {
while cin && i < w {
let (sum, c) =
<T as CarryingAdd>::carrying_add(hi_limbs[i], zero::<T>(), true);
hi_limbs[i] = sum;
cin = c;
i += 1;
}
}
PersonalityTag::Ct => {
while i < w {
let (sum, c) =
<T as CarryingAdd>::carrying_add(hi_limbs[i], zero::<T>(), cin);
hi_limbs[i] = sum;
cin = c;
i += 1;
}
}
}
}
let lo = HeaplessBigInt {
limbs: lo_limbs,
len: w as u16,
_p: PhantomData,
};
let hi = HeaplessBigInt {
limbs: hi_limbs,
len: w as u16,
_p: PhantomData,
};
(lo, hi)
}
}
impl<T, const CAP: usize, P: Personality> CarryingMul for &HeaplessBigInt<T, CAP, P>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Unsigned = HeaplessBigInt<T, CAP, P>;
type Output = HeaplessBigInt<T, CAP, P>;
fn carrying_mul(self, rhs: Self, carry: Self) -> (Self::Unsigned, Self::Output) {
<HeaplessBigInt<T, CAP, P> as CarryingMul>::carrying_mul(*self, *rhs, *carry)
}
fn carrying_mul_add(self, rhs: Self, carry: Self, add: Self) -> (Self::Unsigned, Self::Output) {
<HeaplessBigInt<T, CAP, P> as CarryingMul>::carrying_mul_add(*self, *rhs, *carry, *add)
}
}
#[inline]
pub(crate) fn zero_tail_ok<T: MachineWord>(limbs: &[T], used: usize) -> bool {
let mut i = used;
while i < limbs.len() {
if !is_zero(&limbs[i]) {
return false;
}
i += 1;
}
true
}
#[cfg(test)]
mod tests {
use super::*;
use const_num_traits::Zero;
type H = HeaplessBigInt<u32, 8, Nct>;
#[test]
fn saturation_is_operand_width_not_cap() {
let a = H::from_le_bytes(&0xFFFF_FFFFu32.to_le_bytes()); let one = H::from_le_bytes(&1u32.to_le_bytes());
let s = SaturatingAdd::saturating_add(a, one);
assert_eq!(s.len, 1);
assert_eq!(s.limbs[0], 0xFFFF_FFFF);
let big = H::from_le_bytes(&0x1_0000u32.to_le_bytes());
let m = SaturatingMul::saturating_mul(big, big);
assert_eq!(m.len, 1);
assert_eq!(m.limbs[0], 0xFFFF_FFFF);
}
#[test]
fn saturating_sub_clamps_to_zero_at_width() {
let one = H::from_le_bytes(&1u32.to_le_bytes()); let two = H::from_le_bytes(&2u32.to_le_bytes());
let s = SaturatingSub::saturating_sub(one, two);
assert_eq!(s.len, 1);
assert!(<H as Zero>::is_zero(&s));
}
#[test]
fn checked_div_rem_by_zero_is_none() {
let a = H::from_le_bytes(&100u32.to_le_bytes());
let z = <H as Zero>::zero();
assert_eq!(a.checked_div(&z), None);
assert_eq!(a.checked_rem(&z), None);
let seven = H::from_le_bytes(&7u32.to_le_bytes());
assert_eq!(a.checked_div(&seven).unwrap().limbs[0], 14);
assert_eq!(a.checked_rem(&seven).unwrap().limbs[0], 2);
}
#[test]
fn ct_checked_arithmetic() {
use const_num_traits::ops::ct::{CtCheckedAdd, CtCheckedMul, CtCheckedSub};
type Cc = HeaplessBigInt<u8, 4, Ct>;
let a = Cc::from(100u32);
let b = Cc::from(50u32);
let s = a.ct_checked_add(&b);
assert!(bool::from(s.is_some()));
assert_eq!(s.unwrap(), Cc::from(150u32));
assert!(bool::from(a.ct_checked_sub(&b).is_some()));
assert!(bool::from(
Cc::from(7u32).ct_checked_mul(&Cc::from(9u32)).is_some()
));
assert!(!bool::from(
Cc::from(u32::MAX).ct_checked_add(&Cc::from(1u32)).is_some()
));
assert!(!bool::from(
Cc::from(0u32).ct_checked_sub(&Cc::from(1u32)).is_some()
));
assert!(!bool::from(
Cc::from(0x1_0000u32)
.ct_checked_mul(&Cc::from(0x1_0000u32))
.is_some()
));
}
#[test]
fn ct_saturating_matches_nct() {
type Cn = HeaplessBigInt<u8, 4, Nct>;
type Cc = HeaplessBigInt<u8, 4, Ct>;
let cases = [(100u32, 50u32), (u32::MAX, 1), (u32::MAX, u32::MAX), (5, 9)];
for (a, b) in cases {
assert_eq!(
SaturatingAdd::saturating_add(Cc::from(a), Cc::from(b)),
Cc::from(a.saturating_add(b)),
"ct saturating_add({a},{b})"
);
assert_eq!(
SaturatingSub::saturating_sub(Cc::from(a), Cc::from(b)),
Cc::from(a.saturating_sub(b))
);
assert_eq!(
SaturatingMul::saturating_mul(Cc::from(a), Cc::from(b)),
Cc::from(a.saturating_mul(b))
);
assert_eq!(
SaturatingAdd::saturating_add(Cn::from(a), Cn::from(b)),
Cn::from(a.saturating_add(b))
);
}
}
#[test]
fn by_ref_matches_value() {
let a = H::from(100u32);
let b = H::from(7u32);
assert_eq!(
CheckedAdd::checked_add(&a, &b),
CheckedAdd::checked_add(a, b)
);
assert_eq!(
CheckedSub::checked_sub(&a, &b),
CheckedSub::checked_sub(a, b)
);
assert_eq!(
CheckedMul::checked_mul(&a, &b),
CheckedMul::checked_mul(a, b)
);
assert_eq!(
SaturatingAdd::saturating_add(&a, &b),
SaturatingAdd::saturating_add(a, b)
);
assert_eq!(
SaturatingSub::saturating_sub(&a, &b),
SaturatingSub::saturating_sub(a, b)
);
assert_eq!(
SaturatingMul::saturating_mul(&a, &b),
SaturatingMul::saturating_mul(a, b)
);
assert_eq!(
CarryingAdd::carrying_add(&a, &b, true),
CarryingAdd::carrying_add(a, b, true)
);
assert_eq!(
BorrowingSub::borrowing_sub(&a, &b, true),
BorrowingSub::borrowing_sub(a, b, true)
);
let z = <H as Zero>::zero();
assert_eq!(
CarryingMul::carrying_mul(&a, &b, &z),
CarryingMul::carrying_mul(a, b, z)
);
assert_eq!(
CarryingMul::carrying_mul_add(&a, &b, &z, &b),
CarryingMul::carrying_mul_add(a, b, z, b)
);
type Cc = HeaplessBigInt<u8, 4, Ct>;
let ca = Cc::from(u32::MAX);
let cb = Cc::from(1u32);
assert_eq!(
SaturatingAdd::saturating_add(&ca, &cb),
SaturatingAdd::saturating_add(ca, cb)
);
}
}