use super::HeaplessBigInt;
use crate::MachineWord;
use const_num_traits::{CarryingMul, CheckedMul, CheckedPow, Nct, One, StrictPow};
pub(crate) fn pow_impl<T, const CAP: usize>(
base: HeaplessBigInt<T, CAP, Nct>,
exp: u32,
) -> HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
let mut result =
<HeaplessBigInt<T, CAP, Nct> as One>::one().widened(core::cmp::max(1, base.len));
let mut b = base;
let mut e = exp;
while e > 0 {
if e & 1 == 1 {
result *= b;
}
e >>= 1;
if e > 0 {
b *= b;
}
}
result
}
impl<T, const CAP: usize> HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
pub fn pow(self, exp: u32) -> Self {
pow_impl(self, exp)
}
}
impl<T, const CAP: usize> CheckedPow for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
fn checked_pow(self, exp: u32) -> Option<Self> {
let mut result = <Self as One>::one().widened(core::cmp::max(1, self.len));
let mut base = self;
let mut e = exp;
while e > 0 {
if e & 1 == 1 {
result = CheckedMul::checked_mul(result, base)?;
}
e >>= 1;
if e > 0 {
base = CheckedMul::checked_mul(base, base)?;
}
}
Some(result)
}
}
impl<T, const CAP: usize> StrictPow for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
fn strict_pow(self, exp: u32) -> Self {
match <Self as CheckedPow>::checked_pow(self, exp) {
Some(v) => v,
None => panic!("HeaplessBigInt: strict_pow overflowed"),
}
}
}
impl<T, const CAP: usize> CheckedPow for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn checked_pow(self, exp: u32) -> Option<Self::Output> {
<HeaplessBigInt<T, CAP, Nct> as CheckedPow>::checked_pow(*self, exp)
}
}
impl<T, const CAP: usize> StrictPow for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn strict_pow(self, exp: u32) -> Self::Output {
<HeaplessBigInt<T, CAP, Nct> as StrictPow>::strict_pow(*self, exp)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::FixedUInt;
type H = HeaplessBigInt<u32, 8, Nct>;
#[test]
fn pow_value_width_and_overflow() {
let base = H::from_le_bytes(&2u32.to_le_bytes()); let r = base.pow(10);
assert_eq!(r.len, 1);
assert_eq!(r.limbs[0], 1024);
assert_eq!(base.pow(0).len, 1);
let base2 = base.widened(2);
assert_eq!(base2.pow(0).len, 2);
assert_eq!(base2.pow(0).limbs[0], 1);
assert_eq!(CheckedPow::checked_pow(base, 32), None);
assert_eq!(
CheckedPow::checked_pow(FixedUInt::<u32, 1, Nct>::from(2u8), 32),
None
);
assert_eq!(StrictPow::strict_pow(base, 10).limbs[0], 1024);
}
#[test]
#[should_panic(expected = "strict_pow overflowed")]
fn strict_pow_panics_on_overflow() {
let base = H::from_le_bytes(&2u32.to_le_bytes());
let _ = StrictPow::strict_pow(base, 32);
}
#[test]
fn byref_matches_value() {
let base = H::from_le_bytes(&2u32.to_le_bytes());
assert_eq!(
CheckedPow::checked_pow(&base, 10),
CheckedPow::checked_pow(base, 10)
);
assert_eq!(
StrictPow::strict_pow(&base, 10),
StrictPow::strict_pow(base, 10)
);
}
}