cpython/objectprotocol/
number.rs

1use std::cmp::Ordering;
2use std::fmt;
3
4use crate::conversion::ToPyObject;
5use crate::err::{self, PyErr, PyResult};
6use crate::ffi;
7use crate::objects::{PyObject, PyInt, PyLong, PyFloat};
8use crate::python::{Python, PythonObject, ToPythonPointer};
9
10
11use super::ObjectProtocol;
12
13/// Operations on numeric objects
14pub trait NumberProtocol: ObjectProtocol {
15    /// Perform addition (self + other)
16    ///
17    /// Invokes the `__add__` magic-method
18    #[inline]
19    fn add(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
20        other.with_borrowed_ptr(py, |other| unsafe {
21            err::result_from_owned_ptr(py, ffi::PyNumber_Add(self.as_ptr(), other))
22        })
23    }
24    /// Perform subtraction (self - other)
25    ///
26    /// Invokes the `__sub__` magic-method
27    #[inline]
28    fn subtract(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
29        other.with_borrowed_ptr(py, |other| unsafe {
30            err::result_from_owned_ptr(py, ffi::PyNumber_Subtract(self.as_ptr(), other))
31        })
32    }
33    /// Perform multiplication (self * other)
34    ///
35    /// Invokes the `__mul__` magic-method
36    #[inline]
37    fn multiply(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
38        other.with_borrowed_ptr(py, |other| unsafe {
39            err::result_from_owned_ptr(py, ffi::PyNumber_Multiply(self.as_ptr(), other))
40        })
41    }
42    /// Perform matrix multiplication, equivalent to the Python expression `self @ other`
43    ///
44    /// Invokes the `__matmul__` magic-method
45    ///
46    /// This method is only available with Python 3.
47    ///
48    /// See [PEP 0456](https://www.python.org/dev/peps/pep-0465/) for details.
49    #[inline]
50    #[cfg(feature = "python3-sys")]
51    fn matrix_multiply(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
52        other.with_borrowed_ptr(py, |other| unsafe {
53            err::result_from_owned_ptr(py, ffi::PyNumber_MatrixMultiply(self.as_ptr(), other))
54        })
55    }
56    /// Perform exponentiation, equivalent to the Python expression `self ** other`,
57    /// or the two-argument form of the builtin method pow: `pow(self, other)`
58    ///
59    /// Invokes the `__pow__` magic-method
60    ///
61    /// See also [NumberProtocol::power_modulo].
62    #[inline]
63    fn power(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
64        self.power_modulo(py, other, py.None())
65    }   
66    /// Perform exponentiation modulo an integer,
67    /// mathematically equivalent to `self ** other % mod`
68    /// but computed much more efficiently.
69    /// 
70    /// Equivalent to invoking the three-argument form
71    /// of the builtin `pow` method: `pow(self, other, z)`
72    ///
73    /// Invoking with a `None` for modulo is equivalent to
74    /// the regular power operation.
75    ///
76    /// Invokes the `__pow__` magic-method
77    #[inline]
78    fn power_modulo(&self, py: Python, exp: impl ToPyObject, z: impl ToPyObject) -> PyResult<PyObject> {
79        exp.with_borrowed_ptr(py, |exp| {
80            z.with_borrowed_ptr(py, |z| unsafe {
81                err::result_from_owned_ptr(py, ffi::PyNumber_Power(self.as_ptr(), exp, z))
82            })
83        })
84    }
85    /// Perform the "true division" operation,
86    /// equivalent to the Python expression `self / other`,
87    /// 
88    /// Invokes the `__truediv__` magic-method.
89    #[inline]   
90    fn true_divide(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
91        other.with_borrowed_ptr(py, |other| unsafe {
92            err::result_from_owned_ptr(py, ffi::PyNumber_TrueDivide(self.as_ptr(), other))
93        })
94    }
95    /// Perform the "floor division" operation,
96    /// equivalent to the Python expression `self // other`,
97    /// 
98    /// This method was added in Python 3.
99    /// If compiling against Python 2, it unconditional throws an error.
100    ///
101    /// Invokes the `__floordiv__` magic-method.
102    #[inline]
103    fn floor_divide(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
104        other.with_borrowed_ptr(py, |other| unsafe {
105            err::result_from_owned_ptr(py, ffi::PyNumber_FloorDivide(self.as_ptr(), other))
106        })
107    }
108    /// Return the remainder of dividing `self` by `other`,
109    /// equivalent to the Python expression `self % other`
110    ///
111    /// Invokes the `__mod__` magic-method.
112    #[inline]
113    fn modulo(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> { 
114        other.with_borrowed_ptr(py, |other| unsafe {
115            err::result_from_owned_ptr(py, ffi::PyNumber_Remainder(self.as_ptr(), other))
116        })
117    }
118    /// Perform combined division and modulo,
119    /// equivalent to the builtin method `divmod(self, other)`
120    ///
121    /// Invokes the `__divmod__` magic-method.
122    #[inline]
123    fn div_mod(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
124        other.with_borrowed_ptr(py, |other| unsafe {
125            err::result_from_owned_ptr(py, ffi::PyNumber_Divmod(self.as_ptr(), other))
126        })
127    }
128    /// Perform the negation of self (-self)
129    ///
130    /// Invokes the `__neg__` magic-method.
131    #[inline]
132    fn negative(&self, py: Python) -> PyResult<PyObject> {
133        unsafe {
134            err::result_from_owned_ptr(py, ffi::PyNumber_Negative(self.as_ptr()))
135        }
136    }
137    /// Invoke the 'positive' operation, equivalent to the
138    /// Python expression `+self`
139    ///
140    /// Invokes the `__pos__` magic-method
141    #[inline]
142    fn positive(&self, py: Python) -> PyResult<PyObject> {       
143        unsafe {
144            err::result_from_owned_ptr(py, ffi::PyNumber_Positive(self.as_ptr()))
145        }
146    }
147    /// Return the absolute value of self,
148    /// equivalent to calling the builtin function `abs`
149    ///
150    /// Invokes the `__abs__` magic-method.
151    #[inline]
152    fn absolute(&self, py: Python) -> PyResult<PyObject> {
153        unsafe {
154            err::result_from_owned_ptr(py, ffi::PyNumber_Absolute(self.as_ptr()))
155        }
156    }
157    /// Perform the bitwise negation of self,
158    /// equivalent to the Python expression `~self`
159    ///
160    /// Invokes the `__invert__` magic-method
161    #[inline]
162    fn bitwise_invert(&self, py: Python) -> PyResult<PyObject> {
163        unsafe {
164            err::result_from_owned_ptr(py, ffi::PyNumber_Invert(self.as_ptr()))
165        }
166    }
167    /// Shift this value to the left by the specified number of bits,
168    /// equivalent to the Python expression `self << bits`
169    ///
170    /// Invokes the `__lshift__` magic-method
171    #[inline]
172    fn left_shift(&self, py: Python, bits: impl ToPyObject) -> PyResult<PyObject> {
173        bits.with_borrowed_ptr(py, |other| unsafe {
174            err::result_from_owned_ptr(py, ffi::PyNumber_Lshift(self.as_ptr(), other))
175        })
176    }
177    /// Shift this value to the right by the specified number of bits,
178    /// equivalent to the Python expression `self >> bits`
179    ///
180    /// Invokes the `__rshift__` magic-method
181    #[inline]
182    fn right_shift(&self, py: Python, bits: impl ToPyObject) -> PyResult<PyObject> {
183        bits.with_borrowed_ptr(py, |other| unsafe {
184            err::result_from_owned_ptr(py, ffi::PyNumber_Rshift(self.as_ptr(), other))
185        })
186    }
187    /// Perform the "bitwise and" of `self & other`
188    ///
189    /// Invokes the `__and__` magic-method.
190    #[inline]
191    fn bitwise_and(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
192        other.with_borrowed_ptr(py, |other| unsafe {
193            err::result_from_owned_ptr(py, ffi::PyNumber_And(self.as_ptr(), other))
194        })
195    }
196    /// Perform the "bitwise exclusive or",
197    /// equivalent to Python expression `self ^ other`
198    ///
199    /// Invokes the `__xor__` magic-method.
200    #[inline]
201    fn bitwise_xor(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
202        other.with_borrowed_ptr(py, |other| unsafe {
203            err::result_from_owned_ptr(py, ffi::PyNumber_Xor(self.as_ptr(), other))
204        })
205    }
206    /// Perform the "bitwise or" of `self | other`
207    ///
208    /// Invokes the `__or__` magic-method.
209    #[inline]
210    fn bitwise_or(&self, py: Python, other: impl ToPyObject) -> PyResult<PyObject> {
211        other.with_borrowed_ptr(py, |other| unsafe {
212            err::result_from_owned_ptr(py, ffi::PyNumber_Or(self.as_ptr(), other))
213        })
214    }
215    /// Convert this object to an integer,
216    /// equivalent to the builtin function `int(self)`
217    ///
218    /// Invokes the `__int__` magic-method.
219    ///
220    /// Throws an exception if unable to perform
221    /// the conversion.
222    #[inline]
223    fn to_int(&self, py: Python) -> PyResult<PyLong> {
224        let obj = unsafe {
225            err::result_from_owned_ptr(py, ffi::PyNumber_Long(self.as_ptr()))?
226        };
227        Ok(obj.cast_into::<PyLong>(py)?)
228    }
229    /// Convert this object to a float,
230    /// equivalent to the builtin function `float(self)`
231    ///
232    /// Invokes the `__float__` magic-method.
233    ///
234    /// Throws an exception if unable to perform
235    /// the conversion.
236    #[inline]
237    fn to_float(&self, py: Python) -> PyResult<PyFloat> {
238        let obj = unsafe {
239            err::result_from_owned_ptr(py, ffi::PyNumber_Float(self.as_ptr()))?
240        };
241        Ok(obj.cast_into::<PyFloat>(py)?)
242    }
243    /// Losslessly convert this object to an integer index,
244    /// as if calling `operator.index()`
245    ///
246    /// The presence of this method indicates 
247    /// this object is an integer type.
248    ///
249    /// Calls the `__index__` magic-method.
250    ///
251    /// See also: [Documentation on the corresponding magic-method](https://docs.python.org/3/reference/datamodel.html?highlight=__index__#object.__index__)
252    #[inline]
253    fn to_index(&self, py: Python) -> PyResult<PyLong> {
254        let obj = unsafe {
255            err::result_from_owned_ptr(py, ffi::PyNumber_Index(self.as_ptr()))?
256        };
257        Ok(obj.cast_into::<PyLong>(py)?)
258    }
259}
260
261impl NumberProtocol for PyObject {}
262
263
264#[cfg(test)]
265mod test {
266    use crate::*;
267    use super::*;
268
269    #[test]
270    fn addition() {
271        let guard = Python::acquire_gil();
272        let py = guard.python();
273        let i1 = (5i32).to_py_object(py).into_object();
274        let i2 = (12i32).to_py_object(py).into_object();
275        let actual_res = i1.add(py, i2).unwrap();
276        let expected_res = (17i32).to_py_object(py).into_object();
277        assert_eq!(
278            actual_res.compare(py, expected_res).unwrap(),
279            Ordering::Equal
280        );
281    }
282}