document + refactor numcomplex/bigint dependencies

This commit is contained in:
mejrs 2021-06-07 22:16:23 +02:00
parent a74c2d346f
commit d5d2cf9fda
6 changed files with 384 additions and 268 deletions

View File

@ -38,6 +38,8 @@ proptest = { version = "0.10.1", default-features = false, features = ["std"] }
# features needed to run the PyO3 test suite
pyo3 = { path = ".", default-features = false, features = ["macros", "auto-initialize"] }
serde_json = "1.0.61"
# needed for num-complex doctest
nalgebra = "0.27.1"
[build-dependencies]
pyo3-build-config = { path = "pyo3-build-config", version = "=0.14.0-alpha.0" }

View File

@ -69,6 +69,14 @@ The `nightly` feature needs the nightly Rust compiler. This allows PyO3 to use R
- `FromPyObject` for `Vec` and `[T;N]` can perform a `memcpy` when the object supports the Python buffer protocol.
- `ToBorrowedObject` can skip a reference count increase when the provided object is a Python native type.
### `num-bigint`
This feature adds a dependency on [num-bigint](https://docs.rs/num-bigint) and enables conversions into its [`BigInt`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html) and [`BigUint`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigUInt.html) types.
### `num-complex`
This feature adds a dependency on [num-complex](https://docs.rs/num-complex) and enables conversions into its [`Complex`](https://docs.rs/num-complex/latest/num_complex/struct.Complex.html) type.
### `serde`
The `serde` feature enables (de)serialization of Py<T> objects via [serde](https://serde.rs/).

View File

@ -80,12 +80,12 @@
//! [`#[pyclass]`](crate::proc_macro::pyclass). This adds a dependency on the
//! [`inventory`](https://docs.rs/inventory) crate, which is not supported on all platforms.
//
//! - `num-bigint`: Enables conversions between Python objects and
//! - [`num-bigint`](crate::num_bigint): Enables conversions between Python objects and
//! [num-bigint](https://docs.rs/num-bigint)'s
//! [`BigInt`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html) and
//! [`BigUint`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigUint.html) types.
//
//! - `num-complex`: Enables conversions between Python objects and
//! - [`num-complex`](crate::num_complex): Enables conversions between Python objects and
//! [num-complex](https://docs.rs/num-complex)'s
//! [`Complex`](https://docs.rs/num-complex/latest/num_complex/struct.Complex.html) type.
//
@ -300,6 +300,8 @@ pub mod types;
pub mod num_bigint;
pub mod num_complex;
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
#[cfg(feature = "serde")]
pub mod serde;

View File

@ -1,3 +1,7 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
//
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
#![cfg(all(feature = "num-bigint", not(any(Py_LIMITED_API, PyPy))))]
#![cfg_attr(
docsrs,
@ -5,8 +9,7 @@
)]
//! Conversions to and from [num-bigint](https://docs.rs/num-bigint)s [`BigInt`] and [`BigUint`] types.
//!
//! This is useful for converting Python integers, which have arbitrary precision,
//! when they may not fit in Rust's built-in integer types.
//! This is useful for converting Python integers when they may not fit in Rust's built-in integer types.
//!
//! # Setup
//!
@ -15,7 +18,7 @@
//! ```toml
//! [dependencies]
//! num-bigint = "0.4"
//! pyo3 = { version = "0.14.0", features = ["num-bigint"] }
//! pyo3 = { version = "0.14", features = ["num-bigint"] }
//! ```
//!
//! Note that you must use compatible versions of num-bigint and PyO3.
@ -23,18 +26,20 @@
//!
//! ## Examples
//!
//! [`BigInt`] and [`BigUint`] can be used represent arbitrary precision integers:
//! Using [`BigInt`] to correctly increment an arbitrary precision integer.
//! This is not possible with Rust's native integers if the Python integer is too large,
//! in which case it will fail its conversion and raise `OverflowError`.
//!
//! ```rust
//! use num_bigint::BigInt;
//! use pyo3::prelude::*;
//! use pyo3::wrap_pyfunction;
//!
//!
//! #[pyfunction]
//! fn add_one(n: BigInt) -> BigInt {
//! n + 1
//! }
//!
//!
//! #[pymodule]
//! fn my_module(_py: Python, m: &PyModule) -> PyResult<()> {
//! m.add_function(wrap_pyfunction!(add_one, m)?)?;
@ -45,14 +50,13 @@
//! Python code:
//! ```python
//! from my_module import add_one
//!
//!
//! n = 1 << 1337
//! value = add_one(n)
//!
//!
//! assert n + 1 == value
//! ```
use crate::{
err, ffi, types::*, AsPyPointer, FromPyObject, IntoPy, Py, PyAny, PyErr, PyNativeType,
PyObject, PyResult, Python, ToPyObject,

192
src/num_complex.rs Normal file
View File

@ -0,0 +1,192 @@
#![cfg(feature = "num-complex")]
#![cfg_attr(docsrs, doc(cfg(feature = "num-complex")))]
//! Conversions to and from [num-complex](https://docs.rs/num-complex)
//! [`Complex`]`<`[`f32`]`>` and [`Complex`]`<`[`f64`]`>`.
//!
//! num-complex [`Complex`] supports more operations than PyO3's [`PyComplex`]
//! and can be used with the rest of the Rust ecosystem.
//!
//! # Setup
//!
//! To use this feature, add this to your **`Cargo.toml`**:
//!
//! ```toml
//! [dependencies]
//! num-complex = "0.4"
//! pyo3 = { version = "0.14", features = ["num-complex"] }
//! ```
//!
//! Note that you must use compatible versions of num-complex and PyO3.
//! The required num-complex version may vary with older versions of PyO3.
//!
//! # Examples
//!
//! Using [num-complex](https://docs.rs/num-complex) and [nalgebra](https://docs.rs/nalgebra)
//! to create a pyfunction that calculates the eigenvalues of a 2x2 matrix.
//!
//! ```rust
//! use nalgebra::base::{dimension::Const, storage::Storage, Matrix};
//! use num_complex::Complex;
//! use pyo3::prelude::*;
//! use pyo3::wrap_pyfunction;
//!
//! type T = Complex<f64>;
//!
//! #[pyfunction]
//! fn get_eigenvalues(m11: T, m12: T, m21: T, m22: T) -> Vec<T> {
//! let mat = Matrix::<T, Const<2>, Const<2>, _>::new(m11, m12, m21, m22);
//!
//! match mat.eigenvalues() {
//! Some(e) => e.data.as_slice().to_vec(),
//! None => vec![],
//! }
//! }
//!
//! #[pymodule]
//! fn my_module(_py: Python, m: &PyModule) -> PyResult<()> {
//! m.add_function(wrap_pyfunction!(get_eigenvalues, m)?)?;
//! Ok(())
//! }
//! # // test
//! # use assert_approx_eq::assert_approx_eq;
//! # use nalgebra::ComplexField;
//! # use pyo3::types::PyComplex;
//! #
//! # fn main() -> PyResult<()> {
//! # Python::with_gil(|py| -> PyResult<()> {
//! # let module = PyModule::new(py, "my_module")?;
//! #
//! # module.add_function(wrap_pyfunction!(get_eigenvalues, module)?)?;
//! #
//! # let m11 = PyComplex::from_doubles(py, 0_f64, -1_f64);
//! # let m12 = PyComplex::from_doubles(py, 1_f64, 0_f64);
//! # let m21 = PyComplex::from_doubles(py, 2_f64, -1_f64);
//! # let m22 = PyComplex::from_doubles(py, -1_f64, 0_f64);
//! #
//! # let result = module
//! # .getattr("get_eigenvalues")?
//! # .call1((m11, m12, m21, m22))?;
//! # println!("eigenvalues: {:?}", result);
//! #
//! # let result = result.extract::<Vec<T>>()?;
//! # let e0 = result[0];
//! # let e1 = result[1];
//! #
//! # assert_approx_eq!(e0, Complex::new(1_f64, -1_f64));
//! # assert_approx_eq!(e1, Complex::new(-2_f64, 0_f64));
//! #
//! # Ok(())
//! # })
//! # }
//! ```
//!
//! Python code:
//! ```python
//! from my_module import get_eigenvalues
//!
//! m11 = complex(0,-1)
//! m12 = complex(1,0)
//! m21 = complex(2,-1)
//! m22 = complex(-1,0)
//!
//! result = get_eigenvalues(m11,m12,m21,m22)
//! assert result == [complex(1,-1), complex(-2,0)]
//! ```
use crate::{
ffi, types::PyComplex, AsPyPointer, FromPyObject, PyAny, PyErr, PyNativeType, PyObject,
PyResult, Python, ToPyObject,
};
use num_complex::Complex;
use std::os::raw::c_double;
impl PyComplex {
/// Creates a new Python `PyComplex` object from `num_complex`'s [`Complex`].
pub fn from_complex<F: Into<c_double>>(py: Python, complex: Complex<F>) -> &PyComplex {
unsafe {
let ptr = ffi::PyComplex_FromDoubles(complex.re.into(), complex.im.into());
py.from_owned_ptr(ptr)
}
}
}
macro_rules! complex_conversion {
($float: ty) => {
impl ToPyObject for Complex<$float> {
#[inline]
fn to_object(&self, py: Python) -> PyObject {
crate::IntoPy::<PyObject>::into_py(self.to_owned(), py)
}
}
impl crate::IntoPy<PyObject> for Complex<$float> {
fn into_py(self, py: Python) -> PyObject {
unsafe {
let raw_obj =
ffi::PyComplex_FromDoubles(self.re as c_double, self.im as c_double);
PyObject::from_owned_ptr(py, raw_obj)
}
}
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[allow(clippy::float_cmp)] // The comparison is for an error value
impl<'source> FromPyObject<'source> for Complex<$float> {
fn extract(obj: &'source PyAny) -> PyResult<Complex<$float>> {
unsafe {
let val = ffi::PyComplex_AsCComplex(obj.as_ptr());
if val.real == -1.0 && PyErr::occurred(obj.py()) {
Err(PyErr::fetch(obj.py()))
} else {
Ok(Complex::new(val.real as $float, val.imag as $float))
}
}
}
}
#[cfg(any(Py_LIMITED_API, PyPy))]
#[allow(clippy::float_cmp)] // The comparison is for an error value
impl<'source> FromPyObject<'source> for Complex<$float> {
fn extract(obj: &'source PyAny) -> PyResult<Complex<$float>> {
unsafe {
let ptr = obj.as_ptr();
let real = ffi::PyComplex_RealAsDouble(ptr);
if real == -1.0 && PyErr::occurred(obj.py()) {
return Err(PyErr::fetch(obj.py()));
}
let imag = ffi::PyComplex_ImagAsDouble(ptr);
Ok(Complex::new(real as $float, imag as $float))
}
}
}
};
}
complex_conversion!(f32);
complex_conversion!(f64);
#[cfg(test)]
mod test {
use super::*;
#[allow(clippy::float_cmp)] // The test wants to ensure that no precision was lost on the Python round-trip
#[test]
fn from_complex() {
let gil = Python::acquire_gil();
let py = gil.python();
let complex = Complex::new(3.0, 1.2);
let py_c = PyComplex::from_complex(py, complex);
assert_eq!(py_c.real(), 3.0);
assert_eq!(py_c.imag(), 1.2);
}
#[test]
fn to_from_complex() {
let gil = Python::acquire_gil();
let py = gil.python();
let val = Complex::new(3.0, 1.2);
let obj = val.to_object(py);
assert_eq!(obj.extract::<Complex<f64>>(py).unwrap(), val);
}
#[test]
fn from_complex_err() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = vec![1].to_object(py);
assert!(obj.extract::<Complex<f64>>(py).is_err());
}
}

View File

@ -1,11 +1,13 @@
#[cfg(all(not(PyPy), not(Py_LIMITED_API)))]
use crate::instance::PyNativeType;
use crate::{ffi, AsPyPointer, PyAny, Python};
#[cfg(all(not(PyPy), not(Py_LIMITED_API)))]
use std::ops::*;
use std::os::raw::c_double;
/// Represents a Python `complex`.
/// Represents a Python [`complex`](https://docs.python.org/3/library/functions.html#complex) object.
///
/// Note that `PyComplex` supports only basic operations. For advanced operations
/// consider using [num-bigint](https://docs.rs/num-bigint)'s [`Complex`] type instead.
/// This requires the [`num-complex`](crate::num_complex) feature flag.
///
/// [`Complex`]: https://docs.rs/num-complex/latest/num_complex/struct.Complex.html
#[repr(transparent)]
pub struct PyComplex(PyAny);
@ -17,7 +19,7 @@ pyobject_native_type!(
);
impl PyComplex {
/// Creates a new Python `complex` object, from its real and imaginary values.
/// Creates a new `PyComplex` from the given real and imaginary values.
pub fn from_doubles(py: Python, real: c_double, imag: c_double) -> &PyComplex {
unsafe {
let ptr = ffi::PyComplex_FromDoubles(real, imag);
@ -25,197 +27,183 @@ impl PyComplex {
}
}
/// Returns the real part of the complex number.
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn real(&self) -> c_double {
unsafe { ffi::PyComplex_RealAsDouble(self.as_ptr()) }
}
/// Returns the imaginary part the complex number.
/// Returns the imaginary part of the complex number.
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn imag(&self) -> c_double {
unsafe { ffi::PyComplex_ImagAsDouble(self.as_ptr()) }
}
/// Returns `|self|`.
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[cfg_attr(docsrs, doc(cfg(not(any(Py_LIMITED_API, PyPy)))))]
pub fn abs(&self) -> c_double {
unsafe {
let val = (*(self.as_ptr() as *mut ffi::PyComplexObject)).cval;
ffi::_Py_c_abs(val)
}
}
/// Returns `self ** other`
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[cfg_attr(docsrs, doc(cfg(not(any(Py_LIMITED_API, PyPy)))))]
pub fn pow(&self, other: &PyComplex) -> &PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_pow))
}
}
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[inline(always)]
unsafe fn complex_operation(
l: &PyComplex,
r: &PyComplex,
operation: unsafe extern "C" fn(ffi::Py_complex, ffi::Py_complex) -> ffi::Py_complex,
) -> *mut ffi::PyObject {
let l_val = (*(l.as_ptr() as *mut ffi::PyComplexObject)).cval;
let r_val = (*(r.as_ptr() as *mut ffi::PyComplexObject)).cval;
ffi::PyComplex_FromCComplex(operation(l_val, r_val))
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[cfg_attr(docsrs, doc(cfg(not(any(Py_LIMITED_API, PyPy)))))]
impl<'py> Add for &'py PyComplex {
type Output = &'py PyComplex;
fn add(self, other: &'py PyComplex) -> &'py PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_sum))
}
}
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[cfg_attr(docsrs, doc(cfg(not(any(Py_LIMITED_API, PyPy)))))]
impl<'py> Sub for &'py PyComplex {
type Output = &'py PyComplex;
fn sub(self, other: &'py PyComplex) -> &'py PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_diff))
}
}
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[cfg_attr(docsrs, doc(cfg(not(any(Py_LIMITED_API, PyPy)))))]
impl<'py> Mul for &'py PyComplex {
type Output = &'py PyComplex;
fn mul(self, other: &'py PyComplex) -> &'py PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_prod))
}
}
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[cfg_attr(docsrs, doc(cfg(not(any(Py_LIMITED_API, PyPy)))))]
impl<'py> Div for &'py PyComplex {
type Output = &'py PyComplex;
fn div(self, other: &'py PyComplex) -> &'py PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_quot))
}
}
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[cfg_attr(docsrs, doc(cfg(not(any(Py_LIMITED_API, PyPy)))))]
impl<'py> Neg for &'py PyComplex {
type Output = &'py PyComplex;
fn neg(self) -> &'py PyComplex {
unsafe {
let val = (*(self.as_ptr() as *mut ffi::PyComplexObject)).cval;
self.py()
.from_owned_ptr(ffi::PyComplex_FromCComplex(ffi::_Py_c_neg(val)))
}
}
}
#[cfg(feature = "num-complex")]
#[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))]
mod complex_conversion {
mod not_limited_impls {
use super::*;
use crate::{FromPyObject, PyErr, PyNativeType, PyObject, PyResult, ToPyObject};
use num_complex::Complex;
use crate::instance::PyNativeType;
use std::ops::{Add, Div, Mul, Neg, Sub};
impl PyComplex {
/// Creates a new Python `PyComplex` object from `num_complex`'s [`Complex`].
pub fn from_complex<F: Into<c_double>>(py: Python, complex: Complex<F>) -> &PyComplex {
/// Returns `|self|`.
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn abs(&self) -> c_double {
unsafe {
let ptr = ffi::PyComplex_FromDoubles(complex.re.into(), complex.im.into());
py.from_owned_ptr(ptr)
let val = (*(self.as_ptr() as *mut ffi::PyComplexObject)).cval;
ffi::_Py_c_abs(val)
}
}
/// Returns `self` raised to the power of `other`.
#[must_use = "method returns a new PyComplex and does not mutate the original value"]
pub fn pow(&self, other: &PyComplex) -> &PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_pow))
}
}
}
macro_rules! complex_conversion {
($float: ty) => {
impl ToPyObject for Complex<$float> {
#[inline]
fn to_object(&self, py: Python) -> PyObject {
crate::IntoPy::<PyObject>::into_py(self.to_owned(), py)
}
}
impl crate::IntoPy<PyObject> for Complex<$float> {
fn into_py(self, py: Python) -> PyObject {
unsafe {
let raw_obj =
ffi::PyComplex_FromDoubles(self.re as c_double, self.im as c_double);
PyObject::from_owned_ptr(py, raw_obj)
}
}
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[allow(clippy::float_cmp)] // The comparison is for an error value
impl<'source> FromPyObject<'source> for Complex<$float> {
fn extract(obj: &'source PyAny) -> PyResult<Complex<$float>> {
unsafe {
let val = ffi::PyComplex_AsCComplex(obj.as_ptr());
if val.real == -1.0 && PyErr::occurred(obj.py()) {
Err(PyErr::fetch(obj.py()))
} else {
Ok(Complex::new(val.real as $float, val.imag as $float))
}
}
}
}
#[cfg(any(Py_LIMITED_API, PyPy))]
#[allow(clippy::float_cmp)] // The comparison is for an error value
impl<'source> FromPyObject<'source> for Complex<$float> {
fn extract(obj: &'source PyAny) -> PyResult<Complex<$float>> {
unsafe {
let ptr = obj.as_ptr();
let real = ffi::PyComplex_RealAsDouble(ptr);
if real == -1.0 && PyErr::occurred(obj.py()) {
return Err(PyErr::fetch(obj.py()));
}
let imag = ffi::PyComplex_ImagAsDouble(ptr);
Ok(Complex::new(real as $float, imag as $float))
}
}
}
};
}
complex_conversion!(f32);
complex_conversion!(f64);
#[allow(clippy::float_cmp)] // The test wants to ensure that no precision was lost on the Python round-trip
#[test]
fn from_complex() {
let gil = Python::acquire_gil();
let py = gil.python();
let complex = Complex::new(3.0, 1.2);
let py_c = PyComplex::from_complex(py, complex);
assert_eq!(py_c.real(), 3.0);
assert_eq!(py_c.imag(), 1.2);
#[inline(always)]
unsafe fn complex_operation(
l: &PyComplex,
r: &PyComplex,
operation: unsafe extern "C" fn(ffi::Py_complex, ffi::Py_complex) -> ffi::Py_complex,
) -> *mut ffi::PyObject {
let l_val = (*(l.as_ptr() as *mut ffi::PyComplexObject)).cval;
let r_val = (*(r.as_ptr() as *mut ffi::PyComplexObject)).cval;
ffi::PyComplex_FromCComplex(operation(l_val, r_val))
}
#[test]
fn to_from_complex() {
let gil = Python::acquire_gil();
let py = gil.python();
let val = Complex::new(3.0, 1.2);
let obj = val.to_object(py);
assert_eq!(obj.extract::<Complex<f64>>(py).unwrap(), val);
impl<'py> Add for &'py PyComplex {
type Output = &'py PyComplex;
fn add(self, other: &'py PyComplex) -> &'py PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_sum))
}
}
}
#[test]
fn from_complex_err() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = vec![1].to_object(py);
assert!(obj.extract::<Complex<f64>>(py).is_err());
impl<'py> Sub for &'py PyComplex {
type Output = &'py PyComplex;
fn sub(self, other: &'py PyComplex) -> &'py PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_diff))
}
}
}
impl<'py> Mul for &'py PyComplex {
type Output = &'py PyComplex;
fn mul(self, other: &'py PyComplex) -> &'py PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_prod))
}
}
}
impl<'py> Div for &'py PyComplex {
type Output = &'py PyComplex;
fn div(self, other: &'py PyComplex) -> &'py PyComplex {
unsafe {
self.py()
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_quot))
}
}
}
impl<'py> Neg for &'py PyComplex {
type Output = &'py PyComplex;
fn neg(self) -> &'py PyComplex {
unsafe {
let val = (*(self.as_ptr() as *mut ffi::PyComplexObject)).cval;
self.py()
.from_owned_ptr(ffi::PyComplex_FromCComplex(ffi::_Py_c_neg(val)))
}
}
}
#[cfg(test)]
mod test {
use super::PyComplex;
use crate::Python;
use assert_approx_eq::assert_approx_eq;
#[test]
fn test_add() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.0, 2.6);
let res = l + r;
assert_approx_eq!(res.real(), 4.0);
assert_approx_eq!(res.imag(), 3.8);
}
#[test]
fn test_sub() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.0, 2.6);
let res = l - r;
assert_approx_eq!(res.real(), 2.0);
assert_approx_eq!(res.imag(), -1.4);
}
#[test]
fn test_mul() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.0, 2.6);
let res = l * r;
assert_approx_eq!(res.real(), -0.12);
assert_approx_eq!(res.imag(), 9.0);
}
#[test]
fn test_div() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.0, 2.6);
let res = l / r;
assert_approx_eq!(res.real(), 0.788_659_793_814_432_9);
assert_approx_eq!(res.imag(), -0.850_515_463_917_525_7);
}
#[test]
fn test_neg() {
let gil = Python::acquire_gil();
let py = gil.python();
let val = PyComplex::from_doubles(py, 3.0, 1.2);
let res = -val;
assert_approx_eq!(res.real(), -3.0);
assert_approx_eq!(res.imag(), -1.2);
}
#[test]
fn test_abs() {
let gil = Python::acquire_gil();
let py = gil.python();
let val = PyComplex::from_doubles(py, 3.0, 1.2);
assert_approx_eq!(val.abs(), 3.231_098_884_280_702_2);
}
#[test]
fn test_pow() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.2, 2.6);
let val = l.pow(r);
assert_approx_eq!(val.real(), -1.419_309_997_016_603_7);
assert_approx_eq!(val.imag(), -0.541_297_466_033_544_6);
}
}
}
@ -235,84 +223,4 @@ mod test {
assert_approx_eq!(complex.real(), 3.0);
assert_approx_eq!(complex.imag(), 1.2);
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_add() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.0, 2.6);
let res = l + r;
assert_approx_eq!(res.real(), 4.0);
assert_approx_eq!(res.imag(), 3.8);
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_sub() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.0, 2.6);
let res = l - r;
assert_approx_eq!(res.real(), 2.0);
assert_approx_eq!(res.imag(), -1.4);
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_mul() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.0, 2.6);
let res = l * r;
assert_approx_eq!(res.real(), -0.12);
assert_approx_eq!(res.imag(), 9.0);
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_div() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.0, 2.6);
let res = l / r;
assert_approx_eq!(res.real(), 0.788_659_793_814_432_9);
assert_approx_eq!(res.imag(), -0.850_515_463_917_525_7);
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_neg() {
let gil = Python::acquire_gil();
let py = gil.python();
let val = PyComplex::from_doubles(py, 3.0, 1.2);
let res = -val;
assert_approx_eq!(res.real(), -3.0);
assert_approx_eq!(res.imag(), -1.2);
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_abs() {
let gil = Python::acquire_gil();
let py = gil.python();
let val = PyComplex::from_doubles(py, 3.0, 1.2);
assert_approx_eq!(val.abs(), 3.231_098_884_280_702_2);
}
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
#[test]
fn test_pow() {
let gil = Python::acquire_gil();
let py = gil.python();
let l = PyComplex::from_doubles(py, 3.0, 1.2);
let r = PyComplex::from_doubles(py, 1.2, 2.6);
let val = l.pow(r);
assert_approx_eq!(val.real(), -1.419_309_997_016_603_7);
assert_approx_eq!(val.imag(), -0.541_297_466_033_544_6);
}
}