mod err;
mod gamma;
mod m;
#[cfg(test)]
mod test;
pub use err::{Error, Result};
pub use gamma::{gamma, lgamma};
macro_rules! libm {
(fn $name:ident($arg:ident: $ty:ty) -> Result<$ret:ty>) => {
#[inline(always)]
pub fn $name($arg: $ty) -> Result<$ret> {
errno::set_errno(errno::Errno(0));
let r = unsafe { m::$name($arg) };
crate::is_error(r)
}
};
(fn $name:ident($arg:ident: $ty:ty) -> $ret:ty) => {
#[inline(always)]
pub fn $name($arg: $ty) -> $ret {
unsafe { m::$name($arg) }
}
};
}
macro_rules! pyo3_proptest {
($fn_name:ident(Result<_>), $test_name:ident, $proptest_name:ident, $edgetest_name:ident) => {
#[cfg(test)]
fn $test_name(x: f64) {
use pyo3::prelude::*;
let rs_result = $fn_name(x);
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let math = PyModule::import(py, "math").unwrap();
let py_func = math
.getattr(stringify!($fn_name))
.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}");
});
}
crate::pyo3_proptest!(@proptest, $test_name, $proptest_name);
crate::pyo3_proptest!(@edgetest, $test_name, $edgetest_name);
};
($fn_name:ident(_), $test_name:ident, $proptest_name:ident, $edgetest_name:ident) => {
#[cfg(test)]
fn $test_name(x: f64) {
use pyo3::prelude::*;
let rs_result = Ok($fn_name(x));
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let math = PyModule::import(py, "math").unwrap();
let py_func = math
.getattr(stringify!($fn_name))
.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}");
});
}
crate::pyo3_proptest!(@proptest, $test_name, $proptest_name);
};
(@proptest, $test_name:ident, $proptest_name:ident) => {
#[cfg(test)]
proptest::proptest! {
#[test]
fn $proptest_name(x: f64) {
$test_name(x);
}
}
};
(@edgetest, $test_name:ident, $edgetest_name:ident) => {
#[test]
fn $edgetest_name() {
$test_name(f64::MIN);
$test_name(-f64::MIN);
$test_name(f64::NAN);
$test_name(-f64::NAN);
$test_name(f64::INFINITY);
$test_name(-f64::NEG_INFINITY);
$test_name(0.0);
$test_name(-0.0);
}
};
}
libm!(fn erf(n: f64) -> f64);
pyo3_proptest!(erf(_), test_erf, proptest_erf, edgetest_erf);
libm!(fn erfc(n: f64) -> f64);
pyo3_proptest!(erfc(_), test_erfc, proptest_erfc, edgetest_erfc);
use pyo3_proptest;