1mod exponential;
7mod misc;
8mod trigonometric;
9
10pub use exponential::{exp, log, log10, sqrt};
11pub use misc::{abs, isclose, isfinite, isinf, isnan, phase, polar, rect};
12pub use trigonometric::{acos, acosh, asin, asinh, atan, atanh, cos, cosh, sin, sinh, tan, tanh};
13
14use num_complex::Complex64;
15
16pub const E: f64 = std::f64::consts::E;
20
21pub const PI: f64 = std::f64::consts::PI;
23
24pub const TAU: f64 = std::f64::consts::TAU;
26
27pub const INF: f64 = f64::INFINITY;
29
30pub const NAN: f64 = f64::NAN;
32
33pub const INFJ: Complex64 = Complex64::new(0.0, f64::INFINITY);
35
36pub const NANJ: Complex64 = Complex64::new(0.0, f64::NAN);
38
39#[cfg(test)]
40use crate::Result;
41use crate::m;
42
43const M_LN2: f64 = core::f64::consts::LN_2;
46
47const CM_LARGE_DOUBLE: f64 = f64::MAX / 4.0;
49const CM_LOG_LARGE_DOUBLE: f64 = 709.0895657128241; const P: f64 = core::f64::consts::PI;
53const P14: f64 = 0.25 * core::f64::consts::PI;
54const P12: f64 = 0.5 * core::f64::consts::PI;
55const P34: f64 = 0.75 * core::f64::consts::PI;
56const N: f64 = f64::NAN;
57#[allow(clippy::excessive_precision)]
58const U: f64 = -9.5426319407711027e33; #[inline]
62const fn c(re: f64, im: f64) -> num_complex::Complex64 {
63 num_complex::Complex64::new(re, im)
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68#[repr(usize)]
69enum SpecialType {
70 NInf = 0, Neg = 1, NZero = 2, PZero = 3, Pos = 4, PInf = 5, Nan = 6, }
78
79macro_rules! special_value {
81 ($z:expr, $table:expr) => {
82 if !$z.re.is_finite() || !$z.im.is_finite() {
83 return Ok($table[special_type($z.re) as usize][special_type($z.im) as usize]);
84 }
85 };
86}
87pub(crate) use special_value;
88
89#[inline]
91fn special_type(d: f64) -> SpecialType {
92 if d.is_finite() {
93 if d != 0.0 {
94 if m::copysign(1.0, d) == 1.0 {
95 SpecialType::Pos
96 } else {
97 SpecialType::Neg
98 }
99 } else if m::copysign(1.0, d) == 1.0 {
100 SpecialType::PZero
101 } else {
102 SpecialType::NZero
103 }
104 } else if d.is_nan() {
105 SpecialType::Nan
106 } else if m::copysign(1.0, d) == 1.0 {
107 SpecialType::PInf
108 } else {
109 SpecialType::NInf
110 }
111}
112
113#[cfg(test)]
114pub(crate) mod tests {
115 use super::*;
116
117 pub fn assert_complex_eq(py_re: f64, py_im: f64, rs: Complex64, func: &str, re: f64, im: f64) {
119 let check_component = |py: f64, rs: f64, component: &str| {
120 if py.is_nan() && rs.is_nan() {
121 } else if py.is_nan() || rs.is_nan() {
123 panic!("{func}({re}, {im}).{component}: py={py} vs rs={rs} (one is NaN)",);
124 } else if py.is_infinite() && rs.is_infinite() {
125 if py.is_sign_positive() != rs.is_sign_positive() {
127 panic!("{func}({re}, {im}).{component}: py={py} vs rs={rs} (sign mismatch)",);
128 }
129 } else if py.is_infinite() || rs.is_infinite() {
130 panic!("{func}({re}, {im}).{component}: py={py} vs rs={rs} (one is infinite)",);
131 } else {
132 let py_bits = py.to_bits() as i64;
134 let rs_bits = rs.to_bits() as i64;
135 let ulp_diff = (py_bits - rs_bits).abs();
136 if ulp_diff != 0 {
137 panic!(
138 "{func}({re}, {im}).{component}: py={py} (bits={:#x}) vs rs={rs} (bits={:#x}), ULP diff={ulp_diff}",
139 py.to_bits(),
140 rs.to_bits()
141 );
142 }
143 }
144 };
145 check_component(py_re, rs.re, "re");
146 check_component(py_im, rs.im, "im");
147 }
148
149 pub fn test_cmath_func<F>(func_name: &str, rs_func: F, re: f64, im: f64)
150 where
151 F: Fn(Complex64) -> Result<Complex64>,
152 {
153 use pyo3::prelude::*;
154
155 let rs_result = rs_func(Complex64::new(re, im));
156
157 pyo3::Python::attach(|py| {
158 let cmath = pyo3::types::PyModule::import(py, "cmath").unwrap();
159 let py_func = cmath.getattr(func_name).unwrap();
160 let py_result = py_func.call1((pyo3::types::PyComplex::from_doubles(py, re, im),));
161
162 match py_result {
163 Ok(result) => {
164 use pyo3::types::PyComplexMethods;
165 let c = result.cast::<pyo3::types::PyComplex>().unwrap();
166 let py_re = c.real();
167 let py_im = c.imag();
168 match rs_result {
169 Ok(rs) => {
170 assert_complex_eq(py_re, py_im, rs, func_name, re, im);
171 }
172 Err(e) => {
173 panic!(
174 "{func_name}({re}, {im}): py=({py_re}, {py_im}) but rs returned error {e:?}"
175 );
176 }
177 }
178 }
179 Err(e) => {
180 if let Ok(rs) = rs_result {
182 if e.is_instance_of::<pyo3::exceptions::PyValueError>(py) {
185 panic!(
186 "{func_name}({re}, {im}): py raised ValueError but rs=({}, {})",
187 rs.re, rs.im
188 );
189 } else if e.is_instance_of::<pyo3::exceptions::PyOverflowError>(py) {
190 panic!(
191 "{func_name}({re}, {im}): py raised OverflowError but rs=({}, {})",
192 rs.re, rs.im
193 );
194 }
195 }
196 }
198 }
199 });
200 }
201}