use crate::algos::exp::exp_schoolbook::{SCHOOLBOOK_GUARD, exp_schoolbook_fixed};
use crate::algos::ln::ln_schoolbook::ln_schoolbook_fixed;
use crate::algos::support::fixed::Fixed;
use crate::algos::support::wide_trig_core::WideTrigCore;
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
#[inline]
#[must_use]
#[allow(dead_code)]
pub(crate) fn pow_schoolbook<C: WideTrigCore, const SCALE: u32>(
base: C::Storage,
exponent: C::Storage,
mode: RoundingMode,
) -> C::Storage {
if base <= C::storage_zero() {
return C::storage_zero();
}
if exponent == C::storage_zero() {
return C::storage_one(SCALE);
}
if let Some(v) = crate::algos::pow::powi_exact::powi_exact_pin::<C::Storage, SCALE>(
base,
exponent,
C::storage_max(),
mode,
) {
return v;
}
C::round_to_storage_directed(C::GUARD, SCALE, mode, &mut |guard| {
let w = SCALE + guard;
let ln_base = C::ln_fixed::<SCALE>(C::to_work_scaled(base, guard), w);
let arg = C::mul(C::to_work_scaled(exponent, guard), ln_base, w);
C::exp_fixed::<SCALE>(arg, w)
})
}
#[allow(dead_code)]
pub(crate) fn pow_schoolbook_with(
base: Int<2>,
exponent: Int<2>,
scale: u32,
working_digits: u32,
mode: RoundingMode,
) -> Int<2> {
let base_i = base.as_i128();
if base_i <= 0 {
return Int::<2>::ZERO;
}
let exp_i = exponent.as_i128();
let one_s: i128 = 10_i128.pow(scale);
if exp_i == 0 {
return Int::<2>::from_i128(one_s);
}
let w = scale + working_digits;
let guard_pow = 10u128.pow(working_digits);
let base_w = Fixed::from_u128_mag(base_i as u128, false).mul_u128(guard_pow);
let ln_base = ln_schoolbook_fixed(base_w, w);
let negative_exp = exp_i < 0;
let exp_w = Fixed::from_u128_mag(exp_i.unsigned_abs(), false).mul_u128(guard_pow);
let exp_w = if negative_exp { exp_w.neg() } else { exp_w };
let arg = exp_w.mul(ln_base, w);
Int::<2>::from_i128(
exp_schoolbook_fixed(arg, w)
.round_to_i128_with(w, scale, mode)
.unwrap_or_else(|| {
crate::support::diagnostics::overflow_panic_with_scale(
"pow_schoolbook",
scale,
)
}),
)
}
#[allow(dead_code)]
pub(crate) fn pow_schoolbook_strict<const SCALE: u32>(
base: Int<2>,
exponent: Int<2>,
mode: RoundingMode,
) -> Int<2> {
pow_schoolbook_with(base, exponent, SCALE, SCHOOLBOOK_GUARD, mode)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::algos::pow::powf_series_2limb::powf_strict;
use crate::support::rounding::RoundingMode;
use crate::int::types::Int;
const MODES: [RoundingMode; 6] = [
RoundingMode::HalfToEven, RoundingMode::HalfAwayFromZero,
RoundingMode::HalfTowardZero, RoundingMode::Trunc,
RoundingMode::Floor, RoundingMode::Ceiling,
];
#[track_caller]
fn check<const S: u32>(base: i128, exp: i128, mode: RoundingMode) {
let rb = Int::<2>::from_i128(base);
let re = Int::<2>::from_i128(exp);
let got = pow_schoolbook_strict::<S>(rb, re, mode);
let expected = powf_strict::<S>(rb, re, mode).expect("reference in range");
assert_eq!(got, expected,
"pow schoolbook D38<{}> base={} exp={} mode={:?}: {:?} != {:?}",
S, base, exp, mode, got, expected);
}
#[test]
fn pow_schoolbook_matches_powf_series_d38_s12() {
let one: i128 = 10_i128.pow(12);
let cases = [
(2*one, one/2), (2*one, 3*one/2), (3*one, one/2),
(2*one, -(one/2)), (4*one, 3*one/4), (3*one/2, 5*one/2),
];
for (b, e) in cases {
for mode in MODES { check::<12>(b, e, mode); }
}
}
#[test]
fn pow_schoolbook_matches_powf_series_d38_s19() {
let one: i128 = 10_i128.pow(19);
let cases = [
(2*one, one/2), (2*one, 3*one/2), (3*one, one/2),
];
for (b, e) in cases {
for mode in MODES { check::<19>(b, e, mode); }
}
}
#[cfg(any(feature = "d57", feature = "wide"))]
mod wide_d57 {
use super::*;
use crate::types::widths::wide_trig_d57::Core;
use crate::D;
const S: u32 = 19;
fn raw9(units_milli: i128) -> Int<3> {
Int::<3>::from_i128(units_milli * 10_i128.pow(16))
}
const CASES: [(i128, i128); 5] = [
(2_000, 500), (2_000, 1_500), (3_000, 500), (4_000, 750), (2_000, -500), ];
#[test]
fn pow_schoolbook_matches_routed() {
for &(b, e) in &CASES {
let rb = raw9(b);
let re = raw9(e);
for mode in MODES {
assert_eq!(
crate::algos::pow::pow_schoolbook::pow_schoolbook::<Core, S>(rb, re, mode),
D::<Int<3>, S>(rb).powf_strict_with(D::<Int<3>, S>(re), mode).0,
"D57 pow schoolbook != routed at base={b} exp={e} mode={mode:?}"
);
}
}
}
#[test]
fn integer_reciprocal_is_directed_exact() {
let one = 10_i128.pow(S);
let one3 = Int::<3>::from_i128(one);
let cases: [(i128, i128, i128); 5] = [
(10, -2, 100), (20, -2, 400), (25, -3, 15_625), (4, -3, 64), (5, -3, 125), ];
for (b, e, div) in cases {
let rb = Int::<3>::from_i128(b) * one3;
let re = Int::<3>::from_i128(e) * one3;
let want = Int::<3>::from_i128(one / div);
for mode in MODES {
assert_eq!(
crate::algos::pow::pow_schoolbook::pow_schoolbook::<Core, S>(rb, re, mode),
want,
"D57 {b}^{e} mode={mode:?}"
);
}
}
}
}
}