use crate::slice::{Arg, each_usual, none, written};
use crate::{dd::Double, float::{Native, horner, ln2, sealed::Core, split_positive}};
use std::{f64::consts::{LN_2, LOG2_E, LOG10_E}, mem::MaybeUninit};
const POLY_64: [f64; 7] = [
0.153487338491425068243146, 0.152519917006351951593857, 0.181863266251982985677316, 0.222221366518767365905163, 0.285714294746548025383248,
0.399999999950799600689777, 0.6666666666667778740063,
];
const POLY_32: [f64; 4] = [0.30434924364089966, 0.3995267450809479, 0.6666705012321472, 2.0];
const POLY_NARROW: [f64; 5] = [0.24145279203110395, 0.28502925464850143, 0.40000986365881785, 0.6666666171721365, 2.0000000000396225];
#[inline]
fn series<R: Native, const FUSED: bool>(s: R, fine: bool) -> R { series_with(&crate::recipe::NativeOps::<FUSED>::DEFAULT, s, fine) }
#[inline]
fn based<R: Native, const FUSED: bool>(s: R, e: R, (hi, lo): (R, R), k: R, fine: bool) -> R {
based_with(&crate::recipe::NativeOps::<FUSED>::DEFAULT, s, e, (hi, lo), k, fine)
}
#[inline]
pub(crate) fn ln_parts<R: Native, const FUSED: bool>(s: R, e: R) -> R { let (base, k) = natural(); based::<R, FUSED>(s, e, base, k, R::WIDE) }
#[inline(always)]
fn natural<R: Native>() -> ((R, R), R) { (ln2(), R::one()) }
#[inline(always)]
fn usual<R: Native>(x: R) -> bool { x.positive_normal() }
#[inline(always)]
fn log_of<R: Native, const FUSED: bool>((m, e): (R, R), (base, k): ((R, R), R)) -> R { of_with(&crate::recipe::NativeOps::<FUSED>::DEFAULT, (m, e), (base, k)) }
#[inline]
fn log<R: Native, const FUSED: bool>(x: R, to: ((R, R), R)) -> R { log_with(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x, to) }
#[inline]
pub(crate) fn ln<R: Native, const FUSED: bool>(x: R) -> R { log::<R, FUSED>(x, natural()) }
#[inline(always)]
pub(crate) fn ln_narrow<const FUSED: bool>(x: f64) -> f64 {
let (m, e) = x.split();
let s = (m - 1.0) / (m + 1.0);
let [a4, a3, a2, a1, a0] = POLY_NARROW;
let u = s * s;
let u2 = u * u;
let p = u2.mla::<FUSED>(a4, u.mla::<FUSED>(a3, a2)).mla::<FUSED>(u2, u.mla::<FUSED>(a1, a0));
e.mla::<FUSED>(LN_2, s * p)
}
#[inline(always)]
pub(crate) fn ln_split<const FUSED: bool>(x: f64) -> Double<f64> {
let (m, e) = split_positive(x);
let (hi, lo): (f64, f64) = ln2();
Double::sum(e * hi, e.mla::<FUSED>(lo, series::<f64, FUSED>((m - 1.0) / (m + 1.0), true)))
}
#[inline(always)]
pub(crate) fn base2<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O) -> ((O::V, O::V), O::V) {
((o.number(F::one()), o.number(F::zero())), o.number(F::narrow(LOG2_E)))
}
#[inline(always)]
pub(crate) fn base10<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O) -> ((O::V, O::V), O::V) {
let (hi, lo) =
if o.width() == crate::buffer::FloatWidth::F64 { (0.30102999566361177, 3.694239077158931e-13) } else { (0.30103302001953125, -3.024355464731343e-6) };
((o.number(F::narrow(hi)), o.number(F::narrow(lo))), o.number(F::narrow(LOG10_E)))
}
#[inline]
pub(crate) fn log2<R: Native, const FUSED: bool>(x: R) -> R { log::<R, FUSED>(x, base2(&crate::recipe::Floats)) }
#[inline]
pub(crate) fn log10<R: Native, const FUSED: bool>(x: R) -> R { log::<R, FUSED>(x, base10(&crate::recipe::Floats)) }
#[inline(always)]
fn by_base<R: Native, T>(b: R, [two, ten, other]: [T; 3]) -> T { if b == R::narrow(2.0) { two } else if b == R::narrow(10.0) { ten } else { other } }
#[inline(always)]
fn for_base<R: Native, const FUSED: bool>(b: R) -> (((R, R), R), R) {
let lb = ln::<R, FUSED>(b);
(by_base(b, [base2(&crate::recipe::Floats), base10(&crate::recipe::Floats), natural()]), by_base(b, [R::one(), R::one(), lb]))
}
#[inline(always)]
pub(crate) fn logb<R: Native, const FUSED: bool>(b: R, x: R) -> R { let (to, d) = for_base::<R, FUSED>(b); log::<R, FUSED>(x, to) / d }
#[derive(Clone, Copy, Debug)]
pub struct Logb;
#[derive(Clone, Copy, Debug)]
pub struct MathishLogb;
#[inline(always)]
pub(crate) fn logb_mode<R: Native, const MATHISH: bool>(b: R, x: R) -> R {
let y = logb::<_, { crate::FUSED }>(b, x);
if MATHISH && b == R::one() && x == R::one() { R::zero() } else { y }
}
pub(crate) fn logb_many<R: Native, const MATHISH: bool>([b, x]: [Arg<'_, R>; 2], n: usize, out: &mut [MaybeUninit<R>]) -> bool {
let out = &mut out[..n];
match (b, x) {
(Arg::One(c), _) => {
let (to, d) = for_base::<_, { crate::FUSED }>(c);
if MATHISH && c == R::one() {
each!([b, x], n, out, |[_, x]| if x == R::one() { R::zero() } else { log::<_, { crate::FUSED }>(x, to) / d })
} else if d == R::one() {
each_usual(
x, n, out, (usual, #[inline(always)] |x| log_of::<_, { crate::FUSED }>(x.split(), to),), none(), #[inline(always)]
|x| log::<_, { crate::FUSED }>(x, to))
} else { each!([b, x], n, out, |[_, x]| log::<_, { crate::FUSED }>(x, to) / d) }
}
(Arg::Many(bs), Arg::One(c)) => {
let l = ln::<_, { crate::FUSED }>(c);
let ok = if MATHISH && c == R::one() {
each!([b, x], n, out, |[b, _]| if b == R::one() { R::zero() } else { l / ln::<_, { crate::FUSED }>(b) })
} else { each!([b, x], n, out, |[b, _]| l / ln::<_, { crate::FUSED }>(b)) };
let exact = [log2::<_, { crate::FUSED }>(c), log10::<_, { crate::FUSED }>(c)];
for (r, &b) in unsafe { written(out) }.iter_mut().zip(bs) { *r = by_base(b, [exact[0], exact[1], *r]); }
ok
}
_ => each!([b, x], n, out, |[b, x]| logb_mode::<_, MATHISH>(b, x)),
}
}
#[inline(always)]
fn log1p_of<R: Native, const FUSED: bool>(x: R, u: R, split: (R, R)) -> R { log_of::<R, FUSED>(split, natural()) + (x - (u - R::one())) / u }
#[inline]
pub(crate) fn log1p<R: Native, const FUSED: bool>(x: R) -> R { log1p_formula(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x) }
#[inline(always)]
fn log1p_usual<R: Native, const FUSED: bool>(x: R) -> R { let u = x + R::one(); log1p_of::<R, FUSED>(x, u, u.split()) }
#[inline(always)]
fn usual1p<R: Native>(x: R) -> bool { usual(x + R::one()) & (x != R::zero()) }
usual_object!( Ln: ln, usual => #[inline(always)] |x| log_of::<_, { crate::FUSED }>(x.split(), natural()));
usual_object!( Log2: log2, usual => #[inline(always)] |x| log_of::<_, { crate::FUSED }>(x.split(), base2(&crate::recipe::Floats)));
usual_object!( Log10: log10, usual => #[inline(always)] |x| log_of::<_, { crate::FUSED }>(x.split(), base10(&crate::recipe::Floats)));
usual_object!( Log1p: log1p, usual1p => log1p_usual::<_, { crate::FUSED }>);
#[derive(Clone, Copy, Debug)]
pub struct RealLn;
const LN2: (f64, f64) = (0.693147180559945286226764, 2.319046813846299558417771e-17);
const CENTRES: [(f64, f64, f64); 4] = [
(0.8408964152537145, -0.17328679513998638, 9.61928934371463e-19), (1.0, 0.0, 0.0), (1.189207115002721, 0.1732867951399863, 6.856931399313918e-20),
(1.4142135623730951, 0.3465735902799727, 2.4442169414592898e-17),
];
const BOUNDS: [f64; 3] = [0.9170040432046712, 1.0905077326652577, 1.2968395546510096];
const TAIL: [f64; 7] = [2.0 / 15.0, 2.0 / 13.0, 2.0 / 11.0, 2.0 / 9.0, 2.0 / 7.0, 2.0 / 5.0, 2.0 / 3.0];
#[inline(always)]
pub(crate) fn ln_double<const FUSED: bool>(x: f64) -> Double<f64> {
let (m, e) = split_positive(x);
let [b0, b1, b2] = BOUNDS;
let (c, lc, lc_lo) = if m < b0 { CENTRES[0] } else if m < b1 { CENTRES[1] } else if m < b2 { CENTRES[2] } else { CENTRES[3] };
let s = Double::new(m - c, 0.0).div::<FUSED>(Double::sum(m, c));
let s2 = s.hi * s.hi;
let ln_m = Double::sum(lc, s.hi + s.hi);
let ln_m = Double::new(ln_m.hi, ln_m.lo + lc_lo + (s.lo + s.lo) + s.hi * s2 * horner::<f64, FUSED, 7>(s2, TAIL)).normalize();
let l = Double::new(LN2.0, LN2.1).mul_float::<FUSED>(e).add(ln_m);
let l = if x.is_nan() | (x == f64::INFINITY) { Double::new(x, 0.0) } else { l };
if x == 0.0 { Double::new(f64::NEG_INFINITY, 0.0) } else { l }
}
#[inline(always)]
fn series_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, s: O::V, fine: bool) -> O::V {
let s2 = o.multiply(s, s);
if fine { o.multiply_add(o.multiply(s, s2), crate::recipe::horner(o, s2, &POLY_64), o.add(s, s)) }
else { o.multiply(s, crate::recipe::horner(o, s2, &POLY_32)) }
}
#[inline(always)]
fn based_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, s: O::V, e: O::V, (hi, lo): (O::V, O::V), k: O::V, fine: bool) -> O::V {
o.multiply_add(e, hi, o.multiply_add(series_with(o, s, fine), k, o.multiply(e, lo)))
}
#[inline(always)]
fn of_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, (m, e): (O::V, O::V), (base, k): ((O::V, O::V), O::V)) -> O::V {
let one = o.number(F::one());
based_with(o, o.divide(o.subtract(m, one), o.add(m, one)), e, base, k, o.width() == crate::buffer::FloatWidth::F64)
}
#[inline(always)]
pub(crate) fn log_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V, to: ((O::V, O::V), O::V)) -> O::V {
let y = of_with(o, crate::recipe::frexp(o, x), to);
let zero = o.number(F::zero());
let y = o.select(o.equal(x, o.number(F::infinity())), x, y);
let y = o.select(o.equal(x, zero), o.number(F::neg_infinity()), y);
o.select(o.less_equal(zero, x), y, o.number(F::nan()))
}
#[inline(always)]
pub(crate) fn formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
log_with(o, x, (crate::recipe::ln2(o), o.number(F::one())))
}
#[inline]
pub(crate) fn log1p_formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
let (zero, one) = (o.number(F::zero()), o.number(F::one()));
let u = o.add(x, one);
let correction = o.divide(o.subtract(x, o.subtract(u, one)), u);
let y = o.add(of_with(o, crate::recipe::frexp(o, u), (crate::recipe::ln2(o), one)), correction);
let y = o.select(o.or(o.equal(x, o.number(F::infinity())), o.equal(x, zero)), x, y);
let y = o.select(o.equal(u, zero), o.number(F::neg_infinity()), y);
o.select(o.less_equal(zero, u), y, o.number(F::nan()))
}