pub(crate) use cmath::make_module;
#[pymodule]
mod cmath {
use crate::vm::{
function::{ArgIntoComplex, ArgIntoFloat, OptionalArg},
PyResult, VirtualMachine,
};
use num_complex::Complex64;
#[pyattr]
use std::f64::consts::{E as e, PI as pi, TAU as tau};
#[pyattr(name = "inf")]
const INF: f64 = f64::INFINITY;
#[pyattr(name = "nan")]
const NAN: f64 = f64::NAN;
#[pyattr(name = "infj")]
const INFJ: Complex64 = Complex64::new(0., f64::INFINITY);
#[pyattr(name = "nanj")]
const NANJ: Complex64 = Complex64::new(0., f64::NAN);
#[pyfunction]
fn phase(z: ArgIntoComplex) -> f64 {
z.arg()
}
#[pyfunction]
fn polar(x: ArgIntoComplex) -> (f64, f64) {
x.to_polar()
}
#[pyfunction]
fn rect(r: ArgIntoFloat, phi: ArgIntoFloat) -> Complex64 {
Complex64::from_polar(*r, *phi)
}
#[pyfunction]
fn isinf(z: ArgIntoComplex) -> bool {
let Complex64 { re, im } = *z;
re.is_infinite() || im.is_infinite()
}
#[pyfunction]
fn isfinite(z: ArgIntoComplex) -> bool {
z.is_finite()
}
#[pyfunction]
fn isnan(z: ArgIntoComplex) -> bool {
z.is_nan()
}
#[pyfunction]
fn exp(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
let z = *z;
result_or_overflow(z, z.exp(), vm)
}
#[pyfunction]
fn sqrt(z: ArgIntoComplex) -> Complex64 {
z.sqrt()
}
#[pyfunction]
fn sin(z: ArgIntoComplex) -> Complex64 {
z.sin()
}
#[pyfunction]
fn asin(z: ArgIntoComplex) -> Complex64 {
z.asin()
}
#[pyfunction]
fn cos(z: ArgIntoComplex) -> Complex64 {
z.cos()
}
#[pyfunction]
fn acos(z: ArgIntoComplex) -> Complex64 {
z.acos()
}
#[pyfunction]
fn log(z: ArgIntoComplex, base: OptionalArg<ArgIntoComplex>) -> Complex64 {
z.log(
base.into_option()
.map(|base| base.re)
.unwrap_or(std::f64::consts::E),
)
}
#[pyfunction]
fn log10(z: ArgIntoComplex) -> Complex64 {
z.log(10.0)
}
#[pyfunction]
fn acosh(z: ArgIntoComplex) -> Complex64 {
z.acosh()
}
#[pyfunction]
fn atan(z: ArgIntoComplex) -> Complex64 {
z.atan()
}
#[pyfunction]
fn atanh(z: ArgIntoComplex) -> Complex64 {
z.atanh()
}
#[pyfunction]
fn tan(z: ArgIntoComplex) -> Complex64 {
z.tan()
}
#[pyfunction]
fn tanh(z: ArgIntoComplex) -> Complex64 {
z.tanh()
}
#[pyfunction]
fn sinh(z: ArgIntoComplex) -> Complex64 {
z.sinh()
}
#[pyfunction]
fn cosh(z: ArgIntoComplex) -> Complex64 {
z.cosh()
}
#[pyfunction]
fn asinh(z: ArgIntoComplex) -> Complex64 {
z.asinh()
}
#[derive(FromArgs)]
struct IsCloseArgs {
#[pyarg(positional)]
a: ArgIntoComplex,
#[pyarg(positional)]
b: ArgIntoComplex,
#[pyarg(named, optional)]
rel_tol: OptionalArg<ArgIntoFloat>,
#[pyarg(named, optional)]
abs_tol: OptionalArg<ArgIntoFloat>,
}
#[pyfunction]
fn isclose(args: IsCloseArgs, vm: &VirtualMachine) -> PyResult<bool> {
let a = *args.a;
let b = *args.b;
let rel_tol = args.rel_tol.map_or(1e-09, Into::into);
let abs_tol = args.abs_tol.map_or(0.0, Into::into);
if rel_tol < 0.0 || abs_tol < 0.0 {
return Err(vm.new_value_error("tolerances must be non-negative".to_owned()));
}
if a == b {
return Ok(true);
}
if a.is_infinite() || b.is_infinite() {
return Ok(false);
}
let diff = c_abs(b - a);
Ok(diff <= (rel_tol * c_abs(b)) || (diff <= (rel_tol * c_abs(a))) || diff <= abs_tol)
}
#[inline]
fn c_abs(Complex64 { re, im }: Complex64) -> f64 {
re.hypot(im)
}
#[inline]
fn result_or_overflow(
value: Complex64,
result: Complex64,
vm: &VirtualMachine,
) -> PyResult<Complex64> {
if !result.is_finite() && value.is_finite() {
Err(vm.new_overflow_error("math range error".to_owned()))
} else {
Ok(result)
}
}
}