pub(crate) mod neg {
use {
crate::{Approx, constants, implementation::piecewise, neg::HugeArgument},
core::{cmp::Ordering, hint::unreachable_unchecked},
sigma_types::{Finite, Negative},
};
#[inline]
pub(crate) fn E1(
x: Negative<Finite<f64>>,
#[cfg(feature = "precision")] max_precision: usize,
) -> Result<Approx, HugeArgument> {
match (**x).partial_cmp(&-10_f64) {
Some(Ordering::Equal) => Ok(piecewise::le_neg_10(
x,
#[cfg(feature = "precision")]
max_precision,
)),
Some(Ordering::Less) => match (**x).partial_cmp(&constants::NXMAX) {
Some(Ordering::Greater) => Ok(piecewise::le_neg_10(
x,
#[cfg(feature = "precision")]
max_precision,
)),
Some(Ordering::Less | Ordering::Equal) => Err(HugeArgument(x)),
None => unsafe { unreachable_unchecked() },
},
Some(Ordering::Greater) => Ok(match (**x).partial_cmp(&-4_f64) {
Some(Ordering::Less | Ordering::Equal) => piecewise::le_neg_4(
x,
#[cfg(feature = "precision")]
max_precision,
),
Some(Ordering::Greater) => match (**x).partial_cmp(&-1_f64) {
Some(Ordering::Less | Ordering::Equal) => piecewise::le_neg_1(
x,
#[cfg(feature = "precision")]
max_precision,
),
Some(Ordering::Greater) => piecewise::le_pos_1(
x.also(),
#[cfg(feature = "precision")]
max_precision,
),
None => unsafe { unreachable_unchecked() },
},
None => unsafe { unreachable_unchecked() },
}),
None => unsafe { unreachable_unchecked() },
}
}
}
pub(crate) mod piecewise {
#![cfg_attr(
not(test),
expect(clippy::single_call_fn, reason = "disjoint, so that's kinda the point")
)]
use {
crate::{Approx, chebyshev, constants},
sigma_types::{Finite, Negative, NonZero, One as _, Positive},
};
#[cfg(feature = "error")]
use sigma_types::NonNegative;
#[cfg(feature = "precision")]
use sigma_types::usize::LessThan;
#[inline]
pub(crate) fn le_neg_1(
x: Negative<Finite<f64>>,
#[cfg(feature = "precision")] max_precision: usize,
) -> Approx {
#![expect(
clippy::arithmetic_side_effects,
reason = "property-based testing ensures this never happens"
)]
let abs = Finite::new(x.abs());
let ln = Finite::new(abs.ln());
let nln = -ln;
let cheb = chebyshev::eval(
Finite::all(&constants::E11),
((Finite::new(2_f64) * *x) + Finite::new(5_f64)) / Finite::new(3_f64),
#[cfg(feature = "precision")]
LessThan::new(max_precision.min(const { constants::size::E11 - 1 })),
);
let value = nln + cheb.value;
#[cfg(feature = "error")]
let epsilon = NonNegative::new(Finite::new(constants::GSL_DBL_EPSILON));
#[cfg(feature = "error")]
let init_err = cheb.error + epsilon * NonNegative::new(Finite::new(nln.abs()));
#[cfg(feature = "error")]
let addl_err = NonNegative::new(Finite::new(2_f64))
* epsilon
* NonNegative::new(Finite::new(value.abs()));
Approx {
value,
#[cfg(feature = "error")]
error: init_err + addl_err,
}
}
#[inline]
pub(crate) fn le_neg_10(
x: Negative<Finite<f64>>,
#[cfg(feature = "precision")] max_precision: usize,
) -> Approx {
#![expect(
clippy::arithmetic_side_effects,
reason = "property-based testing ensures this never happens"
)]
let s: Finite<f64> = (Finite::<f64>::ONE / *x) * (-*x).map(libm::exp);
let cheb = chebyshev::eval(
Finite::all(&constants::AE11),
(Finite::new(20_f64) / *x) + Finite::<f64>::ONE,
#[cfg(feature = "precision")]
LessThan::new(max_precision.min(const { constants::size::AE11 - 1 })),
);
let value = s * (Finite::<f64>::ONE + cheb.value);
#[cfg(feature = "error")]
let init_err = s * *cheb.error;
#[cfg(feature = "error")]
let addl_err = {
let abs_x: NonNegative<Finite<f64>> = x.map(|f| f.map(f64::abs));
let abs_value: NonNegative<Finite<f64>> = NonNegative::new(value.map(f64::abs));
let epsilon = NonNegative::new(Finite::new(constants::GSL_DBL_EPSILON));
let two = NonNegative::new(Finite::new(2_f64));
two * epsilon * (abs_x + NonNegative::<Finite<f64>>::ONE) * abs_value
};
Approx {
value,
#[cfg(feature = "error")]
error: NonNegative::new(init_err + addl_err.get()),
}
}
#[inline]
pub(crate) fn le_neg_4(
x: Negative<Finite<f64>>,
#[cfg(feature = "precision")] max_precision: usize,
) -> Approx {
#![expect(
clippy::arithmetic_side_effects,
reason = "property-based testing ensures this never happens"
)]
let s: Finite<f64> = (Finite::<f64>::ONE / *x) * (-*x).map(libm::exp);
let cheb = chebyshev::eval(
Finite::all(&constants::AE12),
((Finite::new(40_f64) / *x) + Finite::new(7_f64)) / Finite::new(3_f64),
#[cfg(feature = "precision")]
LessThan::new(max_precision.min(const { constants::size::AE12 - 1 })),
);
let value = s * (Finite::<f64>::ONE + cheb.value);
#[cfg(feature = "error")]
let init_err = s * *cheb.error;
#[cfg(feature = "error")]
let addl_err = {
let abs_value: NonNegative<Finite<f64>> = NonNegative::new(value.map(f64::abs));
let two = NonNegative::new(Finite::new(2_f64));
let epsilon = NonNegative::new(Finite::new(constants::GSL_DBL_EPSILON));
two * epsilon * abs_value
};
Approx {
value,
#[cfg(feature = "error")]
error: NonNegative::new(init_err + addl_err.get()),
}
}
#[inline]
pub(crate) fn le_pos_1(
x: NonZero<Finite<f64>>,
#[cfg(feature = "precision")] max_precision: usize,
) -> Approx {
#![expect(
clippy::arithmetic_side_effects,
reason = "property-based testing ensures this never happens"
)]
let abs = Finite::new(x.abs());
let ln = Finite::new(abs.ln());
let nln = -ln;
let cheb = chebyshev::eval(
Finite::all(&constants::E12),
*x,
#[cfg(feature = "precision")]
LessThan::new(max_precision.min(const { constants::size::E12 - 1 })),
);
let value = nln - Finite::new(0.6875_f64) + *x + cheb.value;
#[cfg(feature = "error")]
let epsilon = NonNegative::new(Finite::new(constants::GSL_DBL_EPSILON));
#[cfg(feature = "error")]
let init_err = cheb.error + epsilon * NonNegative::new(Finite::new(nln.abs()));
#[cfg(feature = "error")]
let addl_err = NonNegative::new(Finite::new(2_f64))
* epsilon
* NonNegative::new(Finite::new(value.abs()));
Approx {
value,
#[cfg(feature = "error")]
error: init_err + addl_err,
}
}
#[inline]
pub(crate) fn le_pos_4(
x: Positive<Finite<f64>>,
#[cfg(feature = "precision")] max_precision: usize,
) -> Approx {
#![expect(
clippy::arithmetic_side_effects,
reason = "property-based testing ensures this never happens"
)]
let s = (Finite::<f64>::ONE / *x) * (-*x).map(f64::exp);
let cheb = chebyshev::eval(
Finite::all(&constants::AE13),
(Finite::new(8_f64) / *x - Finite::new(5_f64)) / Finite::new(3_f64),
#[cfg(feature = "precision")]
LessThan::new(max_precision.min(const { constants::size::AE13 - 1 })),
);
let value = s * (Finite::<f64>::ONE + cheb.value);
#[cfg(feature = "error")]
let init_err = s * *cheb.error;
#[cfg(feature = "error")]
let addl_err = {
let epsilon = NonNegative::new(Finite::new(constants::GSL_DBL_EPSILON));
NonNegative::new(Finite::new(2_f64))
* epsilon
* NonNegative::new(Finite::new(value.abs()))
};
Approx {
value,
#[cfg(feature = "error")]
error: NonNegative::new(init_err + *addl_err),
}
}
#[inline]
pub(crate) fn le_pos_max(
x: Positive<Finite<f64>>,
#[cfg(feature = "precision")] max_precision: usize,
) -> Approx {
#![expect(
clippy::arithmetic_side_effects,
reason = "property-based testing ensures this never happens"
)]
let s = (Finite::<f64>::ONE / *x) * (-*x).map(f64::exp);
let cheb = chebyshev::eval(
Finite::all(&constants::AE14),
(Finite::new(8_f64) / *x) - Finite::new(1_f64),
#[cfg(feature = "precision")]
LessThan::new(max_precision.min(const { constants::size::AE14 - 1 })),
);
let value = s * (Finite::<f64>::ONE + cheb.value);
#[cfg(feature = "error")]
let epsilon = NonNegative::new(Finite::new(constants::GSL_DBL_EPSILON));
#[cfg(feature = "error")]
let init_err = s * *(epsilon + cheb.error);
#[cfg(feature = "error")]
let addl_err = {
let also_x: NonNegative<Finite<f64>> = x.also();
NonNegative::new(Finite::new(2_f64))
* (also_x + NonNegative::new(Finite::new(1_f64)))
* epsilon
* NonNegative::new(Finite::new(value.abs()))
};
Approx {
value,
#[cfg(feature = "error")]
error: NonNegative::new(init_err + *addl_err),
}
}
}
pub(crate) mod pos {
use {
crate::{Approx, constants, implementation::piecewise, pos::HugeArgument},
core::{cmp::Ordering, hint::unreachable_unchecked},
sigma_types::{Finite, Positive},
};
#[inline]
pub(crate) fn E1(
x: Positive<Finite<f64>>,
#[cfg(feature = "precision")] max_precision: usize,
) -> Result<Approx, HugeArgument> {
match (**x).partial_cmp(&4_f64) {
Some(Ordering::Equal) => Ok(piecewise::le_pos_4(
x,
#[cfg(feature = "precision")]
max_precision,
)),
Some(Ordering::Less) => Ok(match (**x).partial_cmp(&1_f64) {
Some(Ordering::Less | Ordering::Equal) => piecewise::le_pos_1(
x.also(),
#[cfg(feature = "precision")]
max_precision,
),
Some(Ordering::Greater) => piecewise::le_pos_4(
x,
#[cfg(feature = "precision")]
max_precision,
),
None => unsafe { unreachable_unchecked() },
}),
Some(Ordering::Greater) => match (**x).partial_cmp(&constants::XMAX) {
Some(Ordering::Less) => Ok(piecewise::le_pos_max(
x,
#[cfg(feature = "precision")]
max_precision,
)),
Some(Ordering::Equal | Ordering::Greater) => Err(HugeArgument(x)),
None => unsafe { unreachable_unchecked() },
},
None => unsafe { unreachable_unchecked() },
}
}
}
use {
crate::{Approx, Error},
core::{cmp::Ordering, hint::unreachable_unchecked},
sigma_types::{Finite, NonZero},
};
#[inline]
#[cfg_attr(
not(test),
expect(clippy::single_call_fn, reason = "to mirror the C implementation")
)]
#[expect(clippy::absolute_paths, reason = "always a collision except full path")]
pub(crate) fn E1(
x: NonZero<Finite<f64>>,
#[cfg(feature = "precision")] max_precision: usize,
) -> Result<Approx, Error> {
match (**x).partial_cmp(&0_f64) {
Some(Ordering::Less) => neg::E1(
x.also(),
#[cfg(feature = "precision")]
max_precision,
)
.map_err(|crate::neg::HugeArgument(arg)| Error::ArgumentTooNegative(arg)),
Some(Ordering::Greater) => pos::E1(
x.also(),
#[cfg(feature = "precision")]
max_precision,
)
.map_err(|crate::pos::HugeArgument(arg)| Error::ArgumentTooPositive(arg)),
Some(Ordering::Equal) | None => unsafe { unreachable_unchecked() },
}
}