2929import org .jcodings .specific .ASCIIEncoding ;
3030import org .jruby .anno .JRubyClass ;
3131import org .jruby .anno .JRubyMethod ;
32+ import org .jruby .api .Convert ;
3233import org .jruby .ast .util .ArgsUtil ;
3334import org .jruby .exceptions .RaiseException ;
3435import org .jruby .runtime .Arity ;
5455
5556import static org .jruby .api .Convert .asBoolean ;
5657import static org .jruby .api .Convert .asFixnum ;
58+ import static org .jruby .api .Convert .asFloat ;
5759import static org .jruby .api .Create .newArray ;
60+ import static org .jruby .api .Create .newRational ;
5861import static org .jruby .api .Define .defineClass ;
5962import static org .jruby .api .Error .*;
6063import static org .jruby .api .Warn .warn ;
@@ -494,16 +497,16 @@ private static IRubyObject convertCommon(ThreadContext context, IRubyObject recv
494497
495498 if (a1 instanceof RubyComplex ) {
496499 RubyComplex a1c = (RubyComplex ) a1 ;
497- if (k_exact_p ( a1c . image ) && f_zero_p (context , a1c .image )) a1 = a1c .real ;
500+ if (k_exact_zero_p (context , a1c .image )) a1 = a1c .real ;
498501 }
499502
500503 if (a2 instanceof RubyComplex ) {
501504 RubyComplex a2c = (RubyComplex ) a2 ;
502- if (k_exact_p ( a2c . image ) && f_zero_p (context , a2c .image )) a2 = a2c .real ;
505+ if (k_exact_zero_p (context , a2c .image )) a2 = a2c .real ;
503506 }
504507
505508 if (a1 instanceof RubyComplex ) {
506- if (singleArg || (k_exact_p ( a2 ) && f_zero_p (context , a2 ))) return a1 ;
509+ if (singleArg || (k_exact_zero_p (context , a2 ))) return a1 ;
507510 }
508511
509512 if (singleArg ) {
@@ -706,23 +709,30 @@ public IRubyObject fdiv(ThreadContext context, IRubyObject other) {
706709 return f_divide (context , this , other , (a , b ) -> fdiv .call (context , a , a , b ), fdiv );
707710 }
708711
709- /** nucomp_expt
710- *
712+ /**
713+ * MRI: nucomp_expt
711714 */
712715 @ JRubyMethod (name = "**" )
713716 public IRubyObject op_expt (ThreadContext context , IRubyObject other ) {
714- if (k_exact_p ( other ) && f_zero_p (context , other )) {
715- return newComplexBang (context , getMetaClass (), asFixnum (context , 0 ));
717+ if (other instanceof RubyNumeric && k_exact_zero_p (context , other )) {
718+ return newComplexBang (context , getMetaClass (), asFixnum (context , 1 ));
716719 }
717720
718- if (other instanceof RubyRational && f_one_p (context , f_denominator ( context , other ))) {
721+ if (other instanceof RubyRational otherRational && f_one_p (context , otherRational . getDenominator ( ))) {
719722 other = f_numerator (context , other );
720723 }
721724
722- if (other instanceof RubyComplex otherComplex && f_zero_p (context , otherComplex .image )) {
725+ if (other instanceof RubyComplex otherComplex && k_exact_zero_p (context , otherComplex .image )) {
723726 other = otherComplex .real ;
724727 }
725728
729+ if (other == RubyFixnum .one (context .runtime )) {
730+ return newComplex (context , metaClass , real , image );
731+ }
732+
733+ IRubyObject result = complexPowForSpecialAngle (context , other );
734+ if (result != UNDEF ) return result ;
735+
726736 if (other instanceof RubyComplex otherComplex ) {
727737 IRubyObject otherReal = otherComplex .real ;
728738 IRubyObject otherImage = otherComplex .image ;
@@ -807,6 +817,82 @@ else if (f_zero_p(context, xr)) {
807817 return coerceBin (context , sites (context ).op_exp , other );
808818 }
809819
820+ // MRI: complex_pow_for_special_angle
821+ private IRubyObject complexPowForSpecialAngle (ThreadContext context , IRubyObject other ) {
822+ if (!(other instanceof RubyInteger integer )) {
823+ return UNDEF ;
824+ }
825+
826+ IRubyObject x = UNDEF ;
827+ int dir ;
828+ if (f_zero_p (context , image )) {
829+ x = real ;
830+ dir = 0 ;
831+ }
832+ else if (f_zero_p (context , real )) {
833+ x = image ;
834+ dir = 2 ;
835+ }
836+ else if (Numeric .f_eqeq_p (context , real , image )) {
837+ x = real ;
838+ dir = 1 ;
839+ }
840+ else if (Numeric .f_eqeq_p (context , real , Numeric .f_negate (context , image ))) {
841+ x = image ;
842+ dir = 3 ;
843+ } else {
844+ dir = 0 ;
845+ }
846+
847+ if (x == UNDEF ) return x ;
848+
849+ if (f_negative_p (context , x )) {
850+ x = f_negate (context , x );
851+ dir += 4 ;
852+ }
853+
854+ IRubyObject zx ;
855+ if (dir % 2 == 0 ) {
856+ zx = num_pow (context , x , other );
857+ }
858+ else {
859+ RubyFixnum two = RubyFixnum .two (context .runtime );
860+ zx = num_pow (context ,
861+ sites (context ).op_times .call (context , two .op_mul (context , x ), x ),
862+ integer .div (context , two )
863+ );
864+ if (f_odd_p (context , other )) {
865+ zx = sites (context ).op_times .call (context , zx , x );
866+ }
867+ }
868+ int dirs [][] = {
869+ {1 , 0 }, {1 , 1 }, {0 , 1 }, {-1 , 1 }, {-1 , 0 }, {-1 , -1 }, {0 , -1 }, {1 , -1 }
870+ };
871+ int z_dir = fix2int (asFixnum (context , dir ).modulo (context , 8 ));
872+
873+ IRubyObject zr = context .fals , zi = context .fals ;
874+ switch (dirs [z_dir ][0 ]) {
875+ case 0 : zr = zero_for (context , zx ); break ;
876+ case 1 : zr = zx ; break ;
877+ case -1 : zr = f_negate (context , zx ); break ;
878+ }
879+ switch (dirs [z_dir ][1 ]) {
880+ case 0 : zi = zero_for (context , zx ); break ;
881+ case 1 : zi = zx ; break ;
882+ case -1 : zi = f_negate (context , zx ); break ;
883+ }
884+ return newComplex (context , metaClass , zr , zi );
885+ }
886+
887+ private static IRubyObject zero_for (ThreadContext context , IRubyObject x ) {
888+ if (x instanceof RubyFloat )
889+ return asFloat (context , 0 );
890+ if (x instanceof RubyRational )
891+ return newRational (context , 0 , 1 );
892+
893+ return asFixnum (context , 0 );
894+ }
895+
810896 /** nucomp_equal_p
811897 *
812898 */
@@ -1131,7 +1217,15 @@ public IRubyObject to_f(ThreadContext context) {
11311217 */
11321218 @ JRubyMethod (name = "to_r" )
11331219 public IRubyObject to_r (ThreadContext context ) {
1134- checkValidRational (context , "Rational" );
1220+ if (image instanceof RubyFloat imageFloat && imageFloat .isZero (context )) {
1221+ /* Do nothing here */
1222+ } else if (!k_exact_zero_p (context , image )) {
1223+ IRubyObject imag = Convert .checkToRational (context , image );
1224+ if (imag .isNil () || !k_exact_zero_p (context , imag )) {
1225+ throw rangeError (context , "can't convert " + this + " into Rational" );
1226+ }
1227+ }
1228+
11351229 return f_to_r (context , real );
11361230 }
11371231
@@ -1145,7 +1239,7 @@ public IRubyObject rationalize(ThreadContext context, IRubyObject[] args) {
11451239 }
11461240
11471241 private void checkValidRational (ThreadContext context , String type ) {
1148- if (k_inexact_p ( image ) || ! f_zero_p (context , image )) {
1242+ if (! k_exact_zero_p (context , image )) {
11491243 throw rangeError (context , "can't convert " + f_to_s (context , this ).convertToString () + " into " + type );
11501244 }
11511245 }
0 commit comments