From b509d2a132de3980ea45496cb35239b8df50ef50 Mon Sep 17 00:00:00 2001 From: Chiang Fong Lee Date: Sun, 3 Apr 2016 16:48:47 +0800 Subject: [PATCH] Implement builtin pow() - Wire up builtin org.Python.pow() with args and third argument checking - Implement for Float and Bool (latter delegating to Int) --- python/common/org/Python.java | 9 +++- python/common/org/python/types/Bool.java | 8 +++- python/common/org/python/types/Float.java | 38 ++++++++++++++- tests/builtins/test_pow.py | 58 ++++++++++++++++++++++- 4 files changed, 107 insertions(+), 6 deletions(-) diff --git a/python/common/org/Python.java b/python/common/org/Python.java index bf3813416a..2d2b867647 100644 --- a/python/common/org/Python.java +++ b/python/common/org/Python.java @@ -1206,9 +1206,16 @@ public static org.python.types.Int ord(org.python.Object c) { "\n" + "With two arguments, equivalent to x**y. With three arguments,\n" + "equivalent to (x**y) % z, but may be more efficient (e.g. for ints).\n", - default_args={"z"} + args = {"x", "y"}, + default_args = {"z"} ) public static org.python.Object pow(org.python.Object x, org.python.Object y, org.python.Object z) { + if (z != null && !((x instanceof org.python.types.Int) && (y instanceof org.python.types.Int))) { + throw new org.python.exceptions.TypeError("pow() 3rd argument not allowed unless all arguments are integers"); + } + if (z != null && ((org.python.types.Int) y).value < 0) { + throw new org.python.exceptions.TypeError("pow() 2nd argument cannot be negative when 3rd argument specified"); + } return x.__pow__(y, z); } diff --git a/python/common/org/python/types/Bool.java b/python/common/org/python/types/Bool.java index 40a08064f7..bec3aa442e 100644 --- a/python/common/org/python/types/Bool.java +++ b/python/common/org/python/types/Bool.java @@ -182,8 +182,12 @@ public org.python.Object __divmod__(org.python.Object other) { @org.python.Method( __doc__ = "" ) - public org.python.Object __pow__(org.python.Object other) { - throw new org.python.exceptions.NotImplementedError("bool.__pow__() has not been implemented."); + public org.python.Object __pow__(org.python.Object other, org.python.Object modulo) { + try { + return new org.python.types.Int(this.value ? 1 : 0).__pow__(other, modulo); + } catch (org.python.exceptions.TypeError e) { + throw new org.python.exceptions.TypeError("unsupported operand type(s) for ** or pow(): 'bool' and '" + other.typeName() + "'"); + } } @org.python.Method( diff --git a/python/common/org/python/types/Float.java b/python/common/org/python/types/Float.java index 50531ebb5b..05be908137 100644 --- a/python/common/org/python/types/Float.java +++ b/python/common/org/python/types/Float.java @@ -296,8 +296,42 @@ public org.python.Object __divmod__(org.python.Object other) { @org.python.Method( __doc__ = "" ) - public org.python.Object __pow__(org.python.Object other) { - throw new org.python.exceptions.NotImplementedError("float.__pow__() has not been implemented."); + public org.python.Object __pow__(org.python.Object other, org.python.Object modulo) { + if (modulo != null) { + throw new org.python.exceptions.NotImplementedError("float.__pow__() with modulo has not been implemented"); + } + + if (other instanceof org.python.types.Int) { + long other_val = ((org.python.types.Int) other).value; + if (other_val < 0) { + if (this.value == 0) { + throw new org.python.exceptions.ZeroDivisionError("0.0 cannot be raised to a negative power"); + } + + double result = 1.0; + for (long count = 0; count < -other_val; count++) { + result *= this.value; + } + return new org.python.types.Float(1.0 / result); + } else { + return new org.python.types.Float(java.lang.Math.pow(this.value, other_val)); + } + } else if (other instanceof org.python.types.Float) { + double other_val = ((org.python.types.Float) other).value; + if (this.value == 0 && other_val < 0.0) { + throw new org.python.exceptions.ZeroDivisionError("0.0 cannot be raised to a negative power"); + } + // TODO: if this.value < 0 && other_val is not an integer, this will be a Complex result, so change this.value to Complex and delegate it out + // return (new org.python.types.Complex(this.value, 0)).__pow__(other, modulo); + return new org.python.types.Float(java.lang.Math.pow(this.value, other_val)); + } else if (other instanceof org.python.types.Bool) { + if (((org.python.types.Bool) other).value) { + return new org.python.types.Float(this.value); + } else { + return new org.python.types.Float(1); + } + } + throw new org.python.exceptions.TypeError("unsupported operand type(s) for ** or pow(): 'float' and '" + other.typeName() + "'"); } @org.python.Method( diff --git a/tests/builtins/test_pow.py b/tests/builtins/test_pow.py index 843e5a5f8f..8fe0de5468 100644 --- a/tests/builtins/test_pow.py +++ b/tests/builtins/test_pow.py @@ -1,8 +1,64 @@ from .. utils import TranspileTestCase, BuiltinFunctionTestCase +from unittest import expectedFailure class PowTests(TranspileTestCase): - pass + @expectedFailure + def test_int_z(self): + self.assertCodeExecution(""" + x = 3 + y = 4 + z = 5 + print(pow(x, y, z)) + """) + + def test_int_neg_y_pos_z(self): + self.assertCodeExecution(""" + x = 3 + y = -4 + z = 5 + print(pow(x, y, z)) + """) + + def test_int_neg_y_neg_z(self): + self.assertCodeExecution(""" + x = 3 + y = -4 + z = -5 + print(pow(x, y, z)) + """) + + def test_float_x_with_z(self): + self.assertCodeExecution(""" + x = 3.3 + y = 4 + z = 5 + print(pow(x, y, z)) + """) + + def test_float_y_with_z(self): + self.assertCodeExecution(""" + x = 3 + y = 4.4 + z = 5 + print(pow(x, y, z)) + """) + + def test_float(self): + self.assertCodeExecution(""" + x = 3.3 + y = 4.4 + z = 5.5 + print(pow(x, y, z)) + """) + + def test_float_neg_y_with_z(self): + self.assertCodeExecution(""" + x = 3.3 + y = -4.4 + z = 5.5 + print(pow(x, y, z)) + """) class BuiltinPowFunctionTests(BuiltinFunctionTestCase, TranspileTestCase):