use crate::Uint;
impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
#[must_use]
pub fn checked_pow(self, exp: Self) -> Option<Self> {
match self.overflowing_pow(exp) {
(x, false) => Some(x),
(_, true) => None,
}
}
#[must_use]
pub fn overflowing_pow(mut self, mut exp: Self) -> (Self, bool) {
if BITS == 0 {
return (self, false);
}
let mut overflow = false;
let mut base_overflow = false;
let mut result = Self::from(1);
while exp != Self::ZERO {
if exp.bit(0) {
let (r, o) = result.overflowing_mul(self);
result = r;
overflow |= o | base_overflow;
}
let (s, o) = self.overflowing_mul(self);
self = s;
base_overflow |= o;
exp >>= 1;
}
(result, overflow)
}
#[must_use]
pub fn pow(self, exp: Self) -> Self {
self.wrapping_pow(exp)
}
#[must_use]
pub fn saturating_pow(self, exp: Self) -> Self {
match self.overflowing_pow(exp) {
(x, false) => x,
(_, true) => Self::MAX,
}
}
#[must_use]
pub fn wrapping_pow(mut self, mut exp: Self) -> Self {
if BITS == 0 {
return self;
}
let mut result = Self::from(1);
while exp != Self::ZERO {
if exp.bit(0) {
result = result.wrapping_mul(self);
}
self = self.wrapping_mul(self);
exp >>= 1;
}
result
}
#[must_use]
pub fn approx_pow2(exp: f64) -> Option<Self> {
const LN2_1P5: f64 = 0.584_962_500_721_156_2_f64;
const EXP2_63: f64 = 9_223_372_036_854_775_808_f64;
#[allow(clippy::cast_precision_loss)] if exp < LN2_1P5 {
if exp < -1.0 {
return Some(Self::ZERO);
}
return Self::try_from(1).ok();
}
#[allow(clippy::cast_precision_loss)]
if exp > Self::BITS as f64 {
return None;
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] let shift = exp.trunc() as usize;
let fract = exp.fract();
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] let bits = (fract.exp2() * EXP2_63) as u64;
if shift >= 63 {
Some(Self::try_from(bits).ok()?.checked_shl(shift - 63)?)
} else {
let shift = 63 - shift;
let bits = (bits >> shift) + ((bits >> (shift - 1)) & 1);
Self::try_from(bits).ok()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{const_for, nlimbs};
use proptest::proptest;
use std::iter::repeat;
#[test]
fn test_pow2_shl() {
const_for!(BITS in NON_ZERO if (BITS >= 2) {
const LIMBS: usize = nlimbs(BITS);
type U = Uint<BITS, LIMBS>;
proptest!(|(e in 0..=BITS+1)| {
assert_eq!(U::from(2).pow(U::from(e)), U::from(1) << e);
});
});
}
#[test]
fn test_pow_product() {
const_for!(BITS in NON_ZERO if (BITS >= 64) {
const LIMBS: usize = nlimbs(BITS);
type U = Uint<BITS, LIMBS>;
proptest!(|(b in 2_u64..100, e in 0_usize..100)| {
let b = U::from(b);
let prod = repeat(b).take(e).product();
assert_eq!(b.pow(U::from(e)), prod);
});
});
}
}
#[cfg(feature = "bench")]
#[doc(hidden)]
pub mod bench {
use super::*;
use crate::{const_for, nlimbs};
use ::proptest::{
arbitrary::Arbitrary,
strategy::{Strategy, ValueTree},
test_runner::TestRunner,
};
use criterion::{black_box, BatchSize, Criterion};
pub fn group(criterion: &mut Criterion) {
const_for!(BITS in BENCH {
const LIMBS: usize = nlimbs(BITS);
bench_pow::<BITS, LIMBS>(criterion);
bench_overflowing_pow::<BITS, LIMBS>(criterion);
});
}
fn bench_pow<const BITS: usize, const LIMBS: usize>(criterion: &mut Criterion) {
let input = (
Uint::<BITS, LIMBS>::arbitrary(),
Uint::<BITS, LIMBS>::arbitrary(),
);
let mut runner = TestRunner::deterministic();
criterion.bench_function(&format!("pow/{BITS}"), move |bencher| {
bencher.iter_batched(
|| input.new_tree(&mut runner).unwrap().current(),
|(b, e)| black_box(black_box(b).pow(black_box(e))),
BatchSize::SmallInput,
);
});
}
fn bench_overflowing_pow<const BITS: usize, const LIMBS: usize>(criterion: &mut Criterion) {
let input = (
Uint::<BITS, LIMBS>::arbitrary(),
Uint::<BITS, LIMBS>::arbitrary(),
);
let mut runner = TestRunner::deterministic();
criterion.bench_function(&format!("overflowing_pow/{BITS}"), move |bencher| {
bencher.iter_batched(
|| input.new_tree(&mut runner).unwrap().current(),
|(b, e)| black_box(black_box(b).overflowing_pow(black_box(e))),
BatchSize::SmallInput,
);
});
}
}