commit
0dc697feac
|
@ -25,8 +25,10 @@ spin = "0.4.9"
|
|||
num-traits = "0.2.5"
|
||||
pyo3cls = { path = "pyo3cls", version = "=0.5.0-alpha.1" }
|
||||
mashup = "0.1.7"
|
||||
num-complex = { version = "0.2", optional = true}
|
||||
|
||||
[dev-dependencies]
|
||||
assert_approx_eq = "1.0"
|
||||
docmatic = "0.1.2"
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -22,3 +22,33 @@ extern "C" {
|
|||
pub fn PyComplex_RealAsDouble(op: *mut PyObject) -> c_double;
|
||||
pub fn PyComplex_ImagAsDouble(op: *mut PyObject) -> c_double;
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Py_complex {
|
||||
pub real: c_double,
|
||||
pub imag: c_double,
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PyComplexObject {
|
||||
_ob_base: PyObject,
|
||||
pub cval: Py_complex,
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
||||
extern "C" {
|
||||
pub fn _Py_c_sum(left: Py_complex, right: Py_complex) -> Py_complex;
|
||||
pub fn _Py_c_diff(left: Py_complex, right: Py_complex) -> Py_complex;
|
||||
pub fn _Py_c_neg(complex: Py_complex) -> Py_complex;
|
||||
pub fn _Py_c_prod(left: Py_complex, right: Py_complex) -> Py_complex;
|
||||
pub fn _Py_c_quot(dividend: Py_complex, divisor: Py_complex) -> Py_complex;
|
||||
pub fn _Py_c_pow(num: Py_complex, exp: Py_complex) -> Py_complex;
|
||||
pub fn _Py_c_abs(arg: Py_complex) -> c_double;
|
||||
pub fn PyComplex_FromCComplex(v: Py_complex) -> *mut PyObject;
|
||||
pub fn PyComplex_AsCComplex(op: *mut PyObject) -> Py_complex;
|
||||
}
|
||||
|
|
|
@ -128,6 +128,9 @@ extern crate spin;
|
|||
// We need that reexport for wrap_function
|
||||
#[doc(hidden)]
|
||||
pub extern crate mashup;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate assert_approx_eq;
|
||||
|
||||
/// Rust FFI declarations for Python
|
||||
pub mod ffi;
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
use ffi;
|
||||
use instance::PyObjectWithToken;
|
||||
use object::PyObject;
|
||||
use python::{Python, ToPyPointer};
|
||||
#[cfg(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
use std::ops::*;
|
||||
use std::os::raw::c_double;
|
||||
|
||||
/// Represents a Python `complex`.
|
||||
#[repr(transparent)]
|
||||
pub struct PyComplex(PyObject);
|
||||
|
||||
pyobject_native_type!(PyComplex, ffi::PyComplex_Type, ffi::PyComplex_Check);
|
||||
|
||||
impl PyComplex {
|
||||
/// Creates a new Python `PyComplex` object, from its real and imaginary values.
|
||||
pub fn from_doubles<'p>(py: Python<'p>, real: c_double, imag: c_double) -> &'p PyComplex {
|
||||
unsafe {
|
||||
let ptr = ffi::PyComplex_FromDoubles(real, imag);
|
||||
py.from_owned_ptr(ptr)
|
||||
}
|
||||
}
|
||||
/// Returns the real value of `PyComplex`.
|
||||
pub fn real(&self) -> c_double {
|
||||
unsafe { ffi::PyComplex_RealAsDouble(self.as_ptr()) }
|
||||
}
|
||||
/// Returns the imaginary value of `PyComplex`.
|
||||
pub fn imag(&self) -> c_double {
|
||||
unsafe { ffi::PyComplex_ImagAsDouble(self.as_ptr()) }
|
||||
}
|
||||
/// Returns `|self|`.
|
||||
#[cfg(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
pub fn pow(&self, other: &PyComplex) -> &PyComplex {
|
||||
unsafe {
|
||||
self.py()
|
||||
.from_owned_ptr(complex_operation(self, other, ffi::_Py_c_pow))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
#[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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
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")]
|
||||
mod complex_conversion {
|
||||
extern crate num_complex;
|
||||
use self::num_complex::Complex;
|
||||
use super::*;
|
||||
use conversion::{FromPyObject, IntoPyObject, ToPyObject};
|
||||
use err::PyErr;
|
||||
use objects::PyObjectRef;
|
||||
use PyResult;
|
||||
impl PyComplex {
|
||||
/// Creates a new Python `PyComplex` object from num_complex::Complex.
|
||||
pub fn from_complex<'py, F: Into<c_double>>(
|
||||
py: Python<'py>,
|
||||
complex: Complex<F>,
|
||||
) -> &'py 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 {
|
||||
IntoPyObject::into_object(self.to_owned(), py)
|
||||
}
|
||||
}
|
||||
impl IntoPyObject for Complex<$float> {
|
||||
fn into_object(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_or_panic(py, raw_obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
impl<'source> FromPyObject<'source> for Complex<$float> {
|
||||
fn extract(obj: &'source PyObjectRef) -> 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(all(Py_LIMITED_API, Py_3))]
|
||||
impl<'source> FromPyObject<'source> for Complex<$float> {
|
||||
fn extract(obj: &'source PyObjectRef) -> 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);
|
||||
|
||||
#[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());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::PyComplex;
|
||||
use python::Python;
|
||||
#[test]
|
||||
fn test_from_double() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let complex = PyComplex::from_doubles(py, 3.0, 1.2);
|
||||
assert_eq!(complex.real(), 3.0);
|
||||
assert_eq!(complex.imag(), 1.2);
|
||||
}
|
||||
|
||||
#[cfg(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
#[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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
#[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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
#[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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
#[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.7886597938144329);
|
||||
assert_approx_eq!(res.imag(), -0.8505154639175257);
|
||||
}
|
||||
|
||||
#[cfg(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
#[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(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
#[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.2310988842807022);
|
||||
}
|
||||
|
||||
#[cfg(any(not(Py_LIMITED_API), not(Py_3)))]
|
||||
#[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.4193099970166037);
|
||||
assert_approx_eq!(val.imag(), -0.5412974660335446);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
pub use self::boolobject::PyBool;
|
||||
pub use self::bytearray::PyByteArray;
|
||||
pub use self::complex::PyComplex;
|
||||
pub use self::datetime::PyDeltaAccess;
|
||||
pub use self::datetime::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo};
|
||||
pub use self::datetime::{PyDateAccess, PyTimeAccess};
|
||||
|
@ -184,6 +185,7 @@ pyobject_native_type_convert!(PyObjectRef, ffi::PyBaseObject_Type, ffi::PyObject
|
|||
|
||||
mod boolobject;
|
||||
mod bytearray;
|
||||
mod complex;
|
||||
mod datetime;
|
||||
mod dict;
|
||||
pub mod exc;
|
||||
|
|
Loading…
Reference in New Issue