-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlib.rs
120 lines (109 loc) · 4.09 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
mod err;
mod gamma;
mod m;
#[cfg(test)]
mod test;
pub use err::{Error, Result};
pub use gamma::{gamma, lgamma};
macro_rules! libm {
// Reset errno and handle errno when return type contains Result
(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)
}
};
// Skip errno checking when return type is not Result
(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);
/// Call is_error when errno != 0, and where x is the result libm
/// returned. is_error will usually set up an exception and return
/// true (1), but may return false (0) without setting up an exception.
// fn is_error(x: f64) -> crate::Result<f64> {
// match errno::errno() {
// errno::Errno(0) => Ok(x),
// errno::Errno(libc::ERANGE) if x.abs() < 1.5 => Ok(0f64),
// errno::Errno(errno) => Err(errno.try_into().unwrap()),
// }
// }
use pyo3_proptest;