1- // TODO: Keep track of rust-num/num-complex/issues/2. A common trait could help with duplication
2- // that exists between cmath and math.
31pub ( crate ) use cmath:: make_module;
2+
43#[ pymodule]
54mod cmath {
65 use crate :: vm:: {
@@ -9,137 +8,141 @@ mod cmath {
98 } ;
109 use num_complex:: Complex64 ;
1110
11+ use crate :: math:: pymath_exception;
12+
1213 // Constants
13- #[ pyattr]
14- use core:: f64:: consts:: { E as e, PI as pi, TAU as tau} ;
14+ #[ pyattr( name = "e" ) ]
15+ const E : f64 = pymath:: cmath:: E ;
16+ #[ pyattr( name = "pi" ) ]
17+ const PI : f64 = pymath:: cmath:: PI ;
18+ #[ pyattr( name = "tau" ) ]
19+ const TAU : f64 = pymath:: cmath:: TAU ;
1520 #[ pyattr( name = "inf" ) ]
16- const INF : f64 = f64 :: INFINITY ;
21+ const INF : f64 = pymath :: cmath :: INF ;
1722 #[ pyattr( name = "nan" ) ]
18- const NAN : f64 = f64 :: NAN ;
23+ const NAN : f64 = pymath :: cmath :: NAN ;
1924 #[ pyattr( name = "infj" ) ]
20- const INFJ : Complex64 = Complex64 :: new ( 0. , f64 :: INFINITY ) ;
25+ const INFJ : Complex64 = pymath :: cmath :: INFJ ;
2126 #[ pyattr( name = "nanj" ) ]
22- const NANJ : Complex64 = Complex64 :: new ( 0. , f64 :: NAN ) ;
27+ const NANJ : Complex64 = pymath :: cmath :: NANJ ;
2328
2429 #[ pyfunction]
25- fn phase ( z : ArgIntoComplex ) -> f64 {
26- z. into_complex ( ) . arg ( )
30+ fn phase ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < f64 > {
31+ pymath :: cmath :: phase ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
2732 }
2833
2934 #[ pyfunction]
30- fn polar ( x : ArgIntoComplex ) -> ( f64 , f64 ) {
31- x. into_complex ( ) . to_polar ( )
35+ fn polar ( x : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < ( f64 , f64 ) > {
36+ pymath :: cmath :: polar ( x. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
3237 }
3338
3439 #[ pyfunction]
35- fn rect ( r : ArgIntoFloat , phi : ArgIntoFloat ) -> Complex64 {
36- Complex64 :: from_polar ( r. into_float ( ) , phi. into_float ( ) )
40+ fn rect ( r : ArgIntoFloat , phi : ArgIntoFloat , vm : & VirtualMachine ) -> PyResult < Complex64 > {
41+ pymath:: cmath:: rect ( r. into_float ( ) , phi. into_float ( ) )
42+ . map_err ( |err| pymath_exception ( err, vm) )
3743 }
3844
3945 #[ pyfunction]
4046 fn isinf ( z : ArgIntoComplex ) -> bool {
41- let Complex64 { re, im } = z. into_complex ( ) ;
42- re. is_infinite ( ) || im. is_infinite ( )
47+ pymath:: cmath:: isinf ( z. into_complex ( ) )
4348 }
4449
4550 #[ pyfunction]
4651 fn isfinite ( z : ArgIntoComplex ) -> bool {
47- z. into_complex ( ) . is_finite ( )
52+ pymath :: cmath :: isfinite ( z. into_complex ( ) )
4853 }
4954
5055 #[ pyfunction]
5156 fn isnan ( z : ArgIntoComplex ) -> bool {
52- z. into_complex ( ) . is_nan ( )
57+ pymath :: cmath :: isnan ( z. into_complex ( ) )
5358 }
5459
5560 #[ pyfunction]
5661 fn exp ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
57- let z = z. into_complex ( ) ;
58- result_or_overflow ( z, z. exp ( ) , vm)
62+ pymath:: cmath:: exp ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err, vm) )
5963 }
6064
6165 #[ pyfunction]
62- fn sqrt ( z : ArgIntoComplex ) -> Complex64 {
63- z. into_complex ( ) . sqrt ( )
66+ fn sqrt ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
67+ pymath :: cmath :: sqrt ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
6468 }
6569
6670 #[ pyfunction]
67- fn sin ( z : ArgIntoComplex ) -> Complex64 {
68- z. into_complex ( ) . sin ( )
71+ fn sin ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
72+ pymath :: cmath :: sin ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
6973 }
7074
7175 #[ pyfunction]
72- fn asin ( z : ArgIntoComplex ) -> Complex64 {
73- z. into_complex ( ) . asin ( )
76+ fn asin ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
77+ pymath :: cmath :: asin ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
7478 }
7579
7680 #[ pyfunction]
77- fn cos ( z : ArgIntoComplex ) -> Complex64 {
78- z. into_complex ( ) . cos ( )
81+ fn cos ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
82+ pymath :: cmath :: cos ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
7983 }
8084
8185 #[ pyfunction]
82- fn acos ( z : ArgIntoComplex ) -> Complex64 {
83- z. into_complex ( ) . acos ( )
86+ fn acos ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
87+ pymath :: cmath :: acos ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
8488 }
8589
8690 #[ pyfunction]
87- fn log ( z : ArgIntoComplex , base : OptionalArg < ArgIntoComplex > ) -> Complex64 {
88- // TODO: Complex64.log with a negative base yields wrong results.
89- // Issue is with num_complex::Complex64 implementation of log
90- // which returns NaN when base is negative.
91- // log10(z) / log10(base) yields correct results but division
92- // doesn't handle pos/neg zero nicely. (i.e log(1, 0.5))
93- z. into_complex ( ) . log (
94- base. into_option ( )
95- . map ( |base| base. into_complex ( ) . re )
96- . unwrap_or ( core:: f64:: consts:: E ) ,
91+ fn log (
92+ z : ArgIntoComplex ,
93+ base : OptionalArg < ArgIntoComplex > ,
94+ vm : & VirtualMachine ,
95+ ) -> PyResult < Complex64 > {
96+ pymath:: cmath:: log (
97+ z. into_complex ( ) ,
98+ base. into_option ( ) . map ( |b| b. into_complex ( ) ) ,
9799 )
100+ . map_err ( |err| pymath_exception ( err, vm) )
98101 }
99102
100103 #[ pyfunction]
101- fn log10 ( z : ArgIntoComplex ) -> Complex64 {
102- z. into_complex ( ) . log ( 10.0 )
104+ fn log10 ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
105+ pymath :: cmath :: log10 ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
103106 }
104107
105108 #[ pyfunction]
106- fn acosh ( z : ArgIntoComplex ) -> Complex64 {
107- z. into_complex ( ) . acosh ( )
109+ fn acosh ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
110+ pymath :: cmath :: acosh ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
108111 }
109112
110113 #[ pyfunction]
111- fn atan ( z : ArgIntoComplex ) -> Complex64 {
112- z. into_complex ( ) . atan ( )
114+ fn atan ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
115+ pymath :: cmath :: atan ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
113116 }
114117
115118 #[ pyfunction]
116- fn atanh ( z : ArgIntoComplex ) -> Complex64 {
117- z. into_complex ( ) . atanh ( )
119+ fn atanh ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
120+ pymath :: cmath :: atanh ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
118121 }
119122
120123 #[ pyfunction]
121- fn tan ( z : ArgIntoComplex ) -> Complex64 {
122- z. into_complex ( ) . tan ( )
124+ fn tan ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
125+ pymath :: cmath :: tan ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
123126 }
124127
125128 #[ pyfunction]
126- fn tanh ( z : ArgIntoComplex ) -> Complex64 {
127- z. into_complex ( ) . tanh ( )
129+ fn tanh ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
130+ pymath :: cmath :: tanh ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
128131 }
129132
130133 #[ pyfunction]
131- fn sinh ( z : ArgIntoComplex ) -> Complex64 {
132- z. into_complex ( ) . sinh ( )
134+ fn sinh ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
135+ pymath :: cmath :: sinh ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
133136 }
134137
135138 #[ pyfunction]
136- fn cosh ( z : ArgIntoComplex ) -> Complex64 {
137- z. into_complex ( ) . cosh ( )
139+ fn cosh ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
140+ pymath :: cmath :: cosh ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
138141 }
139142
140143 #[ pyfunction]
141- fn asinh ( z : ArgIntoComplex ) -> Complex64 {
142- z. into_complex ( ) . asinh ( )
144+ fn asinh ( z : ArgIntoComplex , vm : & VirtualMachine ) -> PyResult < Complex64 > {
145+ pymath :: cmath :: asinh ( z. into_complex ( ) ) . map_err ( |err| pymath_exception ( err , vm ) )
143146 }
144147
145148 #[ derive( FromArgs ) ]
@@ -158,52 +161,10 @@ mod cmath {
158161 fn isclose ( args : IsCloseArgs , vm : & VirtualMachine ) -> PyResult < bool > {
159162 let a = args. a . into_complex ( ) ;
160163 let b = args. b . into_complex ( ) ;
161- let rel_tol = args. rel_tol . map_or ( 1e-09 , |v| v. into_float ( ) ) ;
162- let abs_tol = args. abs_tol . map_or ( 0.0 , |v| v. into_float ( ) ) ;
163-
164- if rel_tol < 0.0 || abs_tol < 0.0 {
165- return Err ( vm. new_value_error ( "tolerances must be non-negative" ) ) ;
166- }
167-
168- if a == b {
169- /* short circuit exact equality -- needed to catch two infinities of
170- the same sign. And perhaps speeds things up a bit sometimes.
171- */
172- return Ok ( true ) ;
173- }
174-
175- /* This catches the case of two infinities of opposite sign, or
176- one infinity and one finite number. Two infinities of opposite
177- sign would otherwise have an infinite relative tolerance.
178- Two infinities of the same sign are caught by the equality check
179- above.
180- */
181- if a. is_infinite ( ) || b. is_infinite ( ) {
182- return Ok ( false ) ;
183- }
164+ let rel_tol = args. rel_tol . into_option ( ) . map ( |v| v. into_float ( ) ) ;
165+ let abs_tol = args. abs_tol . into_option ( ) . map ( |v| v. into_float ( ) ) ;
184166
185- let diff = c_abs ( b - a) ;
186-
187- Ok ( diff <= ( rel_tol * c_abs ( b) ) || ( diff <= ( rel_tol * c_abs ( a) ) ) || diff <= abs_tol)
188- }
189-
190- #[ inline]
191- fn c_abs ( Complex64 { re, im } : Complex64 ) -> f64 {
192- re. hypot ( im)
193- }
194-
195- #[ inline]
196- fn result_or_overflow (
197- value : Complex64 ,
198- result : Complex64 ,
199- vm : & VirtualMachine ,
200- ) -> PyResult < Complex64 > {
201- if !result. is_finite ( ) && value. is_finite ( ) {
202- // CPython doesn't return `inf` when called with finite
203- // values, it raises OverflowError instead.
204- Err ( vm. new_overflow_error ( "math range error" ) )
205- } else {
206- Ok ( result)
207- }
167+ pymath:: cmath:: isclose ( a, b, rel_tol, abs_tol)
168+ . map_err ( |_| vm. new_value_error ( "tolerances must be non-negative" ) )
208169 }
209170}
0 commit comments