@@ -112,8 +112,19 @@ impl<'a> VM<'a> {
112112 self . i128_to_val ( ( o. as_int ( ) as i128 ) . abs ( ) ) ?
113113 } else if o. is_float ( ) {
114114 Val :: float ( o. as_float ( ) . abs ( ) )
115- } else if o. is_heap ( ) && let HeapObj :: BigInt ( b) = self . heap . get ( o) {
116- self . bigint_to_val ( b. abs ( ) ) ?
115+ } else if o. is_heap ( ) {
116+ match self . heap . get ( o) {
117+ HeapObj :: BigInt ( b) => self . bigint_to_val ( b. abs ( ) ) ?,
118+ // |a + b·j| = sqrt(a² + b²). f64::sqrt lowers to an LLVM
119+ // intrinsic on every supported target (incl. wasm32), so it
120+ // costs no extra dependency and stays accurate to ulp.
121+ HeapObj :: Complex ( re, im) => {
122+ let ( re, im) = ( * re, * im) ;
123+ let mag2 = re * re + im * im;
124+ Val :: float ( fsqrt ( mag2) )
125+ }
126+ _ => return Err ( cold_type ( "abs() requires a number" ) ) ,
127+ }
117128 } else {
118129 return Err ( cold_type ( "abs() requires a number" ) ) ;
119130 } ;
@@ -200,6 +211,30 @@ impl<'a> VM<'a> {
200211 let o = self . pop ( ) ?; self . push ( Val :: bool ( self . truthy ( o) ) ) ; Ok ( ( ) )
201212 }
202213
214+ /* complex() / complex(real) / complex(real, imag). Real and imag may be
215+ int, float, or bool. Passing a Complex as the sole argument returns it
216+ unchanged (CPython behaviour); passing Complex with a second argument
217+ follows the `(re_a, im_a) + j*(re_b, im_b)` rule, which collapses to
218+ `(re_a − im_b, im_a + re_b)`. */
219+ pub fn call_complex ( & mut self , argc : u16 ) -> Result < ( ) , VmErr > {
220+ let args = self . pop_n ( argc as usize ) ?;
221+ let ( re, im) = match args. as_slice ( ) {
222+ [ ] => ( 0.0 , 0.0 ) ,
223+ [ a] => self . to_complex_parts ( * a)
224+ . ok_or ( cold_type ( "complex() argument must be a number" ) ) ?,
225+ [ a, b] => {
226+ let ( ra, ia) = self . to_complex_parts ( * a)
227+ . ok_or ( cold_type ( "complex() argument must be a number" ) ) ?;
228+ let ( rb, ib) = self . to_complex_parts ( * b)
229+ . ok_or ( cold_type ( "complex() argument must be a number" ) ) ?;
230+ ( ra - ib, ia + rb)
231+ }
232+ _ => return Err ( cold_type ( "complex() takes 0 to 2 arguments" ) ) ,
233+ } ;
234+ let v = self . alloc_complex ( re, im) ?;
235+ self . push ( v) ; Ok ( ( ) )
236+ }
237+
203238 pub fn call_type ( & mut self ) -> Result < ( ) , VmErr > {
204239 let o = self . pop ( ) ?;
205240 let s = self . type_name ( o) ;
@@ -397,7 +432,7 @@ impl<'a> VM<'a> {
397432 ) ,
398433 HeapObj :: NativeFn ( id) => {
399434 let name = id. name ( ) ;
400- if !matches ! ( name, "int" |"str" |"bytes" |"float" |"bool" |"list" |"tuple" |"dict" |"set" ) {
435+ if !matches ! ( name, "int" |"str" |"bytes" |"float" |"complex" | " bool" |"list" |"tuple" |"dict" |"set" ) {
401436 return Err ( VmErr :: Type ( "isinstance() arg 2 must be a type or tuple of types" ) ) ;
402437 }
403438 Ok (
@@ -900,6 +935,61 @@ impl<'a> VM<'a> {
900935 handler. Caller picks the error message so each surface keeps its own
901936 diagnostic. */
902937 pub ( crate ) fn pow_vals ( & mut self , a : Val , b : Val , err_msg : & ' static str ) -> Result < Val , VmErr > {
938+ if ( self . is_complex ( a) || self . is_complex ( b) ) && b. is_int ( ) {
939+ // Integer exponent on a complex base: repeated multiplication via
940+ // exponentiation-by-squaring on the (re, im) pair. Negative exp
941+ // inverts the result via the standard `1 / z` formula. Real-valued
942+ // bases promoted through to_complex_parts so `2 ** (3+0j)` also
943+ // hits this path (Python returns `(8+0j)`).
944+ let ( mut re, mut im) = self . to_complex_parts ( a)
945+ . ok_or_else ( || cold_type ( err_msg) ) ?;
946+ let exp = b. as_int ( ) ;
947+ let neg = exp < 0 ;
948+ let mut e = ( exp as i64 ) . unsigned_abs ( ) as u32 ;
949+ let ( mut rr, mut ri) = ( 1.0 , 0.0 ) ;
950+ while e > 0 {
951+ if e & 1 != 0 {
952+ let ( a, b) = ( rr * re - ri * im, rr * im + ri * re) ;
953+ rr = a; ri = b;
954+ }
955+ let ( a, b) = ( re * re - im * im, 2.0 * re * im) ;
956+ re = a; im = b;
957+ e >>= 1 ;
958+ }
959+ if neg {
960+ let denom = rr * rr + ri * ri;
961+ if denom == 0.0 { return Err ( VmErr :: ZeroDiv ) ; }
962+ // `1/z = (1+0j) / z`, expanded so the imag term is `0 - ri`
963+ // (subtraction) rather than `-ri` (unary negation). They agree
964+ // mathematically but IEEE 754 disagrees on sign-of-zero —
965+ // `0 - 0` is +0, `-0` is −0 — and CPython's complex repr
966+ // surfaces the sign bit, so we'd print `(1-0j)` for `(1+0j)
967+ // ** -1` instead of the expected `(1+0j)` otherwise.
968+ rr /= denom; ri = ( 0.0 - ri) / denom;
969+ }
970+ return self . alloc_complex ( rr, ri) ;
971+ }
972+ if self . is_complex ( a) || self . is_complex ( b) {
973+ // z^w = exp(w · log(z)) where log(z) = ln|z| + arg(z)·j.
974+ let ( ar, ai) = self . to_complex_parts ( a)
975+ . ok_or_else ( || cold_type ( err_msg) ) ?;
976+ let ( br, bi) = self . to_complex_parts ( b)
977+ . ok_or_else ( || cold_type ( err_msg) ) ?;
978+ let mag2 = ar * ar + ai * ai;
979+ // Special-case the degenerate base so we don't take ln(0) — Python
980+ // returns 1 for 0**0, 0 for 0**(positive), raises for 0**(negative).
981+ if mag2 == 0.0 {
982+ if br == 0.0 && bi == 0.0 { return self . alloc_complex ( 1.0 , 0.0 ) ; }
983+ if br > 0.0 { return self . alloc_complex ( 0.0 , 0.0 ) ; }
984+ return Err ( VmErr :: ZeroDiv ) ;
985+ }
986+ let log_re = 0.5 * fln ( mag2) ;
987+ let log_im = fatan2 ( ai, ar) ;
988+ let prod_re = br * log_re - bi * log_im;
989+ let prod_im = br * log_im + bi * log_re;
990+ let mag = fexp ( prod_re) ;
991+ return self . alloc_complex ( mag * fcos ( prod_im) , mag * fsin ( prod_im) ) ;
992+ }
903993 if let Some ( ba) = self . to_bigint ( a) && b. is_int ( ) {
904994 let exp = b. as_int ( ) ;
905995 if exp >= 0 {
@@ -955,6 +1045,7 @@ impl<'a> VM<'a> {
9551045 HeapObj :: Str ( s) => s. hash ( & mut h) ,
9561046 HeapObj :: Bytes ( b) => b. hash ( & mut h) ,
9571047 HeapObj :: BigInt ( b) => { b. neg . hash ( & mut h) ; b. limbs . hash ( & mut h) ; }
1048+ HeapObj :: Complex ( re, im) => { re. to_bits ( ) . hash ( & mut h) ; im. to_bits ( ) . hash ( & mut h) ; }
9581049 HeapObj :: Tuple ( items) => {
9591050 for v in items { v. 0 . hash ( & mut h) ; }
9601051 }
0 commit comments