mod aggregate;
#[cfg(feature = "_bigint")]
mod bigint;
mod exponential;
mod gamma;
#[cfg(feature = "_bigint")]
pub mod integer;
mod misc;
mod trigonometric;
pub use aggregate::{dist, fsum, prod, prod_int, sumprod, sumprod_int};
#[cfg(feature = "_bigint")]
pub use bigint::{comb_bigint, ldexp_bigint, log_bigint, log2_bigint, log10_bigint, perm_bigint};
pub use exponential::{cbrt, exp, exp2, expm1, log, log1p, log2, log10, pow, sqrt};
pub use gamma::{erf, erfc, gamma, lgamma};
pub use misc::{
ceil, copysign, fabs, floor, fma, fmod, frexp, isclose, isfinite, isinf, isnan, ldexp, modf,
nextafter, remainder, trunc, ulp,
};
pub use trigonometric::{
acos, acosh, asin, asinh, atan, atan2, atanh, cos, cosh, sin, sinh, tan, tanh,
};
macro_rules! libm_simple {
(@1 $($name:ident),* $(,)?) => {
$(
#[inline]
pub fn $name(x: f64) -> f64 {
crate::m::$name(x)
}
)*
};
(@2 $($name:ident),* $(,)?) => {
$(
#[inline]
pub fn $name(x: f64, y: f64) -> f64 {
crate::m::$name(x, y)
}
)*
};
}
pub(crate) use libm_simple;
#[inline]
pub(crate) fn math_1(x: f64, func: fn(f64) -> f64, can_overflow: bool) -> crate::Result<f64> {
crate::err::set_errno(0);
let r = func(x);
if r.is_nan() && !x.is_nan() {
return Err(crate::Error::EDOM);
}
if r.is_infinite() && x.is_finite() {
return Err(if can_overflow {
crate::Error::ERANGE
} else {
crate::Error::EDOM
});
}
#[cfg(not(any(windows, target_os = "macos")))]
if r.is_finite() && crate::err::get_errno() != 0 {
return crate::err::is_error(r);
}
Ok(r)
}
#[inline]
pub(crate) fn math_2(x: f64, y: f64, func: fn(f64, f64) -> f64) -> crate::Result<f64> {
let r = func(x, y);
if r.is_nan() && !x.is_nan() && !y.is_nan() {
return Err(crate::Error::EDOM);
}
if r.is_infinite() && x.is_finite() && y.is_finite() {
return Err(crate::Error::ERANGE);
}
Ok(r)
}
#[inline]
pub(crate) fn math_1a(x: f64, func: fn(f64) -> f64) -> crate::Result<f64> {
crate::err::set_errno(0);
let r = func(x);
crate::err::is_error(r)
}
#[inline]
pub fn hypot(coords: &[f64]) -> f64 {
let n = coords.len();
if n == 0 {
return 0.0;
}
let mut max = 0.0_f64;
let mut found_nan = false;
let mut abs_coords = Vec::with_capacity(n);
for &x in coords {
let ax = x.abs();
if ax.is_nan() {
found_nan = true;
} else if ax > max {
max = ax;
}
abs_coords.push(ax);
}
aggregate::vector_norm(&abs_coords, max, found_nan)
}
pub const PI: f64 = std::f64::consts::PI;
pub const E: f64 = std::f64::consts::E;
pub const TAU: f64 = std::f64::consts::TAU;
pub const INF: f64 = f64::INFINITY;
pub const NAN: f64 = f64::NAN;
#[inline]
pub fn degrees(x: f64) -> f64 {
x * (180.0 / PI)
}
#[inline]
pub fn radians(x: f64) -> f64 {
x * (PI / 180.0)
}
#[cfg(test)]
mod tests {
use super::*;
use pyo3::prelude::*;
fn test_degrees(x: f64) {
let rs_result = Ok(degrees(x));
pyo3::Python::attach(|py| {
let math = PyModule::import(py, "math").unwrap();
let py_func = math.getattr("degrees").unwrap();
let r = py_func.call1((x,));
let Some((py_result, rs_result)) = crate::test::unwrap(py, r, rs_result) else {
return;
};
let py_result_repr = py_result.to_bits();
let rs_result_repr = rs_result.to_bits();
assert_eq!(
py_result_repr, rs_result_repr,
"x = {x}, py_result = {py_result}, rs_result = {rs_result}"
);
});
}
fn test_radians(x: f64) {
let rs_result = Ok(radians(x));
pyo3::Python::attach(|py| {
let math = PyModule::import(py, "math").unwrap();
let py_func = math.getattr("radians").unwrap();
let r = py_func.call1((x,));
let Some((py_result, rs_result)) = crate::test::unwrap(py, r, rs_result) else {
return;
};
let py_result_repr = py_result.to_bits();
let rs_result_repr = rs_result.to_bits();
assert_eq!(
py_result_repr, rs_result_repr,
"x = {x}, py_result = {py_result}, rs_result = {rs_result}"
);
});
}
#[test]
fn edgetest_degrees() {
for &x in crate::test::EDGE_VALUES {
test_degrees(x);
}
}
#[test]
fn edgetest_radians() {
for &x in crate::test::EDGE_VALUES {
test_radians(x);
}
}
#[test]
fn test_constants() {
assert_eq!(PI, std::f64::consts::PI);
assert_eq!(E, std::f64::consts::E);
assert_eq!(TAU, std::f64::consts::TAU);
assert!(INF.is_infinite() && INF > 0.0);
assert!(NAN.is_nan());
}
fn test_hypot(coords: &[f64]) {
let rs_result = hypot(coords);
pyo3::Python::attach(|py| {
let math = PyModule::import(py, "math").unwrap();
let py_func = math.getattr("hypot").unwrap();
let py_args = pyo3::types::PyTuple::new(py, coords).unwrap();
let py_result: f64 = py_func.call1(py_args).unwrap().extract().unwrap();
if py_result.is_nan() && rs_result.is_nan() {
return;
}
assert_eq!(
py_result.to_bits(),
rs_result.to_bits(),
"hypot({:?}): py={} vs rs={}",
coords,
py_result,
rs_result
);
});
}
#[test]
fn test_hypot_basic() {
test_hypot(&[3.0, 4.0]); test_hypot(&[1.0, 2.0, 2.0]); test_hypot(&[]); test_hypot(&[5.0]); }
#[test]
fn edgetest_hypot() {
for &x in crate::test::EDGE_VALUES {
for &y in crate::test::EDGE_VALUES {
test_hypot(&[x, y]);
}
}
}
proptest::proptest! {
#[test]
fn proptest_degrees(x: f64) {
test_degrees(x);
}
#[test]
fn proptest_radians(x: f64) {
test_radians(x);
}
#[test]
fn proptest_hypot(x: f64, y: f64) {
test_hypot(&[x, y]);
}
}
}