use crate::algos::ln::ln_schoolbook::{SCHOOLBOOK_GUARD, 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]
pub(crate) fn log_schoolbook<C: WideTrigCore, const SCALE: u32>(
raw_x: C::Storage,
raw_b: C::Storage,
mode: RoundingMode,
) -> C::Storage {
if raw_x <= C::storage_zero() {
panic!("wide-tier log schoolbook: x must be positive");
}
if raw_b <= C::storage_zero() {
panic!("wide-tier log schoolbook: base must be positive");
}
if raw_b == C::storage_one(SCALE) {
panic!("wide-tier log schoolbook: base must not be 1");
}
C::round_to_storage_directed(C::GUARD, SCALE, mode, &mut |guard| {
let w = SCALE + guard;
let ln_x = C::ln_fixed::<SCALE>(C::to_work_scaled(raw_x, guard), w);
let ln_b = C::ln_fixed::<SCALE>(C::to_work_scaled(raw_b, guard), w);
C::div(ln_x, ln_b, w)
})
}
#[allow(dead_code)]
pub(crate) fn log_schoolbook_with(
raw_x: Int<2>,
raw_b: Int<2>,
scale: u32,
working_digits: u32,
mode: RoundingMode,
) -> Int<2> {
let xi = raw_x.as_i128();
let bi = raw_b.as_i128();
assert!(xi > 0, "log_schoolbook: x must be positive");
assert!(bi > 0, "log_schoolbook: base must be positive");
let one_s = 10_i128.pow(scale);
assert!(bi != one_s, "log_schoolbook: base must not be 1");
if xi % one_s == 0 && bi % one_s == 0 {
let x_int = xi / one_s;
let b_int = bi / one_s;
if b_int >= 2 {
let mut power: i128 = b_int;
let mut k: i128 = 1;
while power <= xi / one_s {
if power == x_int {
return Int::<2>::from_i128(k * one_s);
}
match power.checked_mul(b_int) {
Some(p) => power = p,
None => break,
}
k += 1;
}
}
}
let w = scale + working_digits;
let guard_pow = 10u128.pow(working_digits);
let x_w = Fixed::from_u128_mag(xi as u128, false).mul_u128(guard_pow);
let b_w = Fixed::from_u128_mag(bi as u128, false).mul_u128(guard_pow);
let ln_x = ln_schoolbook_fixed(x_w, w);
let ln_b = ln_schoolbook_fixed(b_w, w);
Int::<2>::from_i128(
ln_x.div(ln_b, w)
.round_to_i128_with(w, scale, mode)
.unwrap_or_else(|| {
crate::support::diagnostics::overflow_panic_with_scale(
"log_schoolbook",
scale,
)
}),
)
}
#[allow(dead_code)]
pub(crate) fn log_schoolbook_strict<const SCALE: u32>(
raw_x: Int<2>,
raw_b: Int<2>,
mode: RoundingMode,
) -> Int<2> {
log_schoolbook_with(raw_x, raw_b, SCALE, SCHOOLBOOK_GUARD, mode)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::algos::ln::ln_series_2limb::log_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>(x: i128, b: i128, mode: RoundingMode) {
let rx = Int::<2>::from_i128(x);
let rb = Int::<2>::from_i128(b);
let got = log_schoolbook_strict::<S>(rx, rb, mode);
let expected = log_strict::<S>(rx, rb, mode).expect("reference in range");
assert_eq!(got, expected,
"log schoolbook D38<{}> x={} b={} mode={:?}: {:?} != {:?}",
S, x, b, mode, got, expected);
}
#[test]
fn log_schoolbook_matches_log_ln_divide_d38_s12() {
let one: i128 = 10_i128.pow(12);
let cases = [
(2*one, 2*one), (4*one, 2*one), (8*one, 2*one),
(10*one, 10*one), (3*one, 2*one), (one+one/2, 2*one),
];
for (x, b) in cases {
for mode in MODES { check::<12>(x, b, mode); }
}
}
#[test]
fn log_schoolbook_matches_log_ln_divide_d38_s19() {
let one: i128 = 10_i128.pow(19);
let cases = [
(2*one, 2*one), (4*one, 2*one), (3*one, 2*one), (10*one, 10*one),
];
for (x, b) in cases {
for mode in MODES { check::<19>(x, b, 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: i128) -> Int<3> {
Int::<3>::from_i128(units * 10_i128.pow(10))
}
const CASES: [(i128, i128); 5] = [
(2_000_000_000, 2_000_000_000),
(4_000_000_000, 2_000_000_000),
(8_000_000_000, 2_000_000_000),
(3_000_000_000, 2_000_000_000),
(10_000_000_000, 10_000_000_000),
];
#[test]
fn log_schoolbook_matches_routed() {
for &(x, b) in &CASES {
let rx = raw9(x);
let rb = raw9(b);
for mode in MODES {
assert_eq!(
crate::algos::log::log_schoolbook::log_schoolbook::<Core, S>(rx, rb, mode),
D::<Int<3>, S>(rx).log_strict_with(D::<Int<3>, S>(rb), mode).0,
"D57 log schoolbook != routed at x={x} b={b} mode={mode:?}"
);
}
}
}
}
}