Commit 3dc695ec authored by Mirko Dziadzka's avatar Mirko Dziadzka Committed by Dylan Trotter

Add binary-op pow (#79)

parent 92d89cb8
......@@ -362,6 +362,7 @@ class ExprVisitor(ast.NodeVisitor):
ast.LShift: 'πg.LShift(πF, {lhs}, {rhs})',
ast.Mod: 'πg.Mod(πF, {lhs}, {rhs})',
ast.Mult: 'πg.Mul(πF, {lhs}, {rhs})',
ast.Pow: 'πg.Pow(πF, {lhs}, {rhs})',
ast.RShift: 'πg.RShift(πF, {lhs}, {rhs})',
ast.Sub: 'πg.Sub(πF, {lhs}, {rhs})',
}
......
......@@ -76,13 +76,10 @@ class ExprVisitorTest(unittest.TestCase):
testBinOpArithmeticMod = _MakeExprTest('9 % 5')
testBinOpArithmeticMul = _MakeExprTest('3 * 2')
testBinOpArithmeticOr = _MakeExprTest('2 | 6')
testBinOpArithmeticPow = _MakeExprTest('2 ** 16')
testBinOpArithmeticSub = _MakeExprTest('10 - 3')
testBinOpArithmeticXor = _MakeExprTest('3 ^ 5')
def testBinOpNotImplemented(self):
self.assertRaisesRegexp(util.ParseError, 'binary op not implemented',
_ParseAndVisitExpr, 'x ** y')
testBoolOpTrueAndFalse = _MakeExprTest('True and False')
testBoolOpTrueAndTrue = _MakeExprTest('True and True')
testBoolOpTrueAndExpr = _MakeExprTest('True and 2 == 2')
......
......@@ -451,6 +451,10 @@ func Mul(f *Frame, v, w *Object) (*Object, *BaseException) {
return binaryOp(f, v, w, v.typ.slots.Mul, v.typ.slots.RMul, w.typ.slots.RMul, "*")
}
func Pow(f *Frame, v, w *Object) (*Object, *BaseException) {
return binaryOp(f, v, w, v.typ.slots.Pow, v.typ.slots.RPow, w.typ.slots.RPow, "**")
}
// Or returns the result of the bitwise or operator v | w according to
// __or/ror__.
func Or(f *Frame, v, w *Object) (*Object, *BaseException) {
......
......@@ -140,6 +140,8 @@ func TestBinaryOps(t *testing.T) {
{Mul, newObject(ObjectType), newObject(fooType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for *: 'object' and 'Foo'")},
{Or, NewInt(-42).ToObject(), NewInt(244).ToObject(), NewInt(-10).ToObject(), nil},
{Or, NewInt(42).ToObject(), NewStr("foo").ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for |: 'int' and 'str'")},
{Pow, NewInt(2).ToObject(), NewInt(-2).ToObject(), NewFloat(0.25).ToObject(), nil},
{Pow, NewInt(2).ToObject(), newObject(fooType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'int' and 'Foo'")},
{Sub, NewInt(3).ToObject(), NewInt(-3).ToObject(), NewInt(6).ToObject(), nil},
{Xor, NewInt(-42).ToObject(), NewInt(244).ToObject(), NewInt(-222).ToObject(), nil},
{Xor, NewInt(42).ToObject(), NewStr("foo").ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for ^: 'int' and 'str'")},
......
......@@ -194,6 +194,10 @@ func floatNonZero(f *Frame, o *Object) (*Object, *BaseException) {
return GetBool(toFloatUnsafe(o).Value() != 0).ToObject(), nil
}
func floatPow(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__pow__", v, w, func(v, w float64) float64 { return math.Pow(v, w) })
}
func floatRAdd(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__radd__", v, w, func(v, w float64) float64 { return w + v })
}
......@@ -221,6 +225,10 @@ func floatRMul(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__rmul__", v, w, func(v, w float64) float64 { return w * v })
}
func floatRPow(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__rpow", v, w, func(v, w float64) float64 { return math.Pow(w, v) })
}
func floatRSub(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__rsub__", v, w, func(v, w float64) float64 { return w - v })
}
......@@ -249,11 +257,13 @@ func initFloatType(dict map[string]*Object) {
FloatType.slots.Neg = &unaryOpSlot{floatNeg}
FloatType.slots.New = &newSlot{floatNew}
FloatType.slots.NonZero = &unaryOpSlot{floatNonZero}
FloatType.slots.Pow = &binaryOpSlot{floatPow}
FloatType.slots.RAdd = &binaryOpSlot{floatRAdd}
FloatType.slots.RDiv = &binaryOpSlot{floatRDiv}
FloatType.slots.Repr = &unaryOpSlot{floatRepr}
FloatType.slots.RMod = &binaryOpSlot{floatRMod}
FloatType.slots.RMul = &binaryOpSlot{floatRMul}
FloatType.slots.RPow = &binaryOpSlot{floatRPow}
FloatType.slots.RSub = &binaryOpSlot{floatRSub}
FloatType.slots.Sub = &binaryOpSlot{floatSub}
}
......
......@@ -78,6 +78,10 @@ func TestFloatArithmeticOps(t *testing.T) {
{Mul, NewFloat(math.Inf(1)).ToObject(), NewInt(-5).ToObject(), NewFloat(math.Inf(-1)).ToObject(), nil},
{Mul, False.ToObject(), NewFloat(math.Inf(1)).ToObject(), NewFloat(math.NaN()).ToObject(), nil},
{Mul, None, NewFloat(1.5).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for *: 'NoneType' and 'float'")},
{Pow, NewFloat(2.0).ToObject(), NewInt(10).ToObject(), NewFloat(1024.0).ToObject(), nil},
{Pow, NewFloat(2.0).ToObject(), NewFloat(-2.0).ToObject(), NewFloat(0.25).ToObject(), nil},
{Pow, newObject(ObjectType), NewFloat(2.0).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'object' and 'float'")},
{Pow, NewFloat(2.0).ToObject(), newObject(ObjectType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'float' and 'object'")},
{Sub, NewFloat(21.3).ToObject(), NewFloat(35.6).ToObject(), NewFloat(-14.3).ToObject(), nil},
{Sub, True.ToObject(), NewFloat(1.5).ToObject(), NewFloat(-0.5).ToObject(), nil},
{Sub, NewFloat(1.0).ToObject(), NewList().ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for -: 'float' and 'list'")},
......
......@@ -16,6 +16,7 @@ package grumpy
import (
"fmt"
"math"
"math/big"
"reflect"
"strconv"
......@@ -243,6 +244,43 @@ func intOr(f *Frame, v, w *Object) (*Object, *BaseException) {
return NewInt(toIntUnsafe(v).Value() | toIntUnsafe(w).Value()).ToObject(), nil
}
func intPow(f *Frame, v, w *Object) (*Object, *BaseException) {
if w.isInstance(IntType) {
// First try to use the faster floating point arithmetic
// on the CPU, then falls back to slower methods.
// IEEE float64 has 52bit of precision, so the result should be
// less than MaxInt32 to be representable as an exact integer.
// This assumes that int is at least 32bit.
vInt := toIntUnsafe(v).Value()
wInt := toIntUnsafe(w).Value()
if 0 < vInt && vInt <= math.MaxInt32 && 0 < wInt && wInt <= math.MaxInt32 {
res := math.Pow(float64(vInt), float64(wInt))
// Can the result be interpreted as an int?
if !math.IsNaN(res) && !math.IsInf(res, 0) && res <= math.MaxInt32 {
return NewInt(int(res)).ToObject(), nil
}
}
// Special cases.
if vInt == 0 {
if wInt < 0 {
return nil, f.RaiseType(ZeroDivisionErrorType, "0.0 cannot be raised to a negative power")
}
if wInt == 0 {
return NewInt(1).ToObject(), nil
}
return NewInt(0).ToObject(), nil
}
// If w < 0, the result must be a floating point number.
// We convert both arguments to float and continue.
if wInt < 0 {
return floatPow(f, NewFloat(float64(vInt)).ToObject(), NewFloat(float64(wInt)).ToObject())
}
// Else we convert to Long and continue there.
return longPow(f, NewLong(big.NewInt(int64(vInt))).ToObject(), NewLong(big.NewInt(int64(wInt))).ToObject())
}
return NotImplemented, nil
}
func intRAdd(f *Frame, v, w *Object) (*Object, *BaseException) {
return intAddMulOp(f, "__radd__", v, w, intCheckedAdd, longAdd)
}
......@@ -328,6 +366,7 @@ func initIntType(dict map[string]*Object) {
IntType.slots.New = &newSlot{intNew}
IntType.slots.NonZero = &unaryOpSlot{intNonZero}
IntType.slots.Or = &binaryOpSlot{intOr}
IntType.slots.Pow = &binaryOpSlot{intPow}
IntType.slots.RAdd = &binaryOpSlot{intRAdd}
IntType.slots.RAnd = &binaryOpSlot{intAnd}
IntType.slots.RDiv = &binaryOpSlot{intRDiv}
......
......@@ -63,6 +63,10 @@ func TestIntBinaryOps(t *testing.T) {
{Or, NewInt(-100).ToObject(), NewInt(50).ToObject(), NewInt(-66).ToObject(), nil},
{Or, NewInt(MaxInt).ToObject(), NewInt(MinInt).ToObject(), NewInt(-1).ToObject(), nil},
{Or, newObject(ObjectType), NewInt(-100).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for |: 'object' and 'int'")},
{Pow, NewInt(2).ToObject(), NewInt(128).ToObject(), NewLong(big.NewInt(0).Exp(big.NewInt(2), big.NewInt(128), nil)).ToObject(), nil},
{Pow, NewInt(2).ToObject(), newObject(ObjectType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'int' and 'object'")},
{Pow, NewInt(2).ToObject(), NewInt(-2).ToObject(), NewFloat(0.25).ToObject(), nil},
{Pow, newObject(ObjectType), NewInt(2).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'object' and 'int'")},
{Sub, NewInt(22).ToObject(), NewInt(18).ToObject(), NewInt(4).ToObject(), nil},
{Sub, IntType.ToObject(), NewInt(42).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for -: 'type' and 'int'")},
{Sub, NewInt(MinInt).ToObject(), NewInt(1).ToObject(), NewLong(new(big.Int).Sub(minIntBig, big.NewInt(1))).ToObject(), nil},
......
......@@ -331,6 +331,8 @@ func initLongType(dict map[string]*Object) {
LongType.slots.New = &newSlot{longNew}
LongType.slots.NonZero = longUnaryBoolOpSlot(longNonZero)
LongType.slots.Or = longBinaryOpSlot(longOr)
// This operation can return a float, it must use binaryOpSlot directly.
LongType.slots.Pow = &binaryOpSlot{longPow}
LongType.slots.RAdd = longRBinaryOpSlot(longAdd)
LongType.slots.RAnd = longRBinaryOpSlot(longAnd)
LongType.slots.RDiv = longRDivModOpSlot(longDiv)
......@@ -339,6 +341,8 @@ func initLongType(dict map[string]*Object) {
LongType.slots.RMul = longRBinaryOpSlot(longMul)
LongType.slots.ROr = longRBinaryOpSlot(longOr)
LongType.slots.RLShift = longRShiftOpSlot(longLShift)
// This operation can return a float, it must use binaryOpSlot directly.
LongType.slots.RPow = &binaryOpSlot{longRPow}
LongType.slots.RRShift = longRShiftOpSlot(longRShift)
LongType.slots.RShift = longShiftOpSlot(longRShift)
LongType.slots.RSub = longRBinaryOpSlot(longSub)
......@@ -497,6 +501,56 @@ func longRBinaryBoolOpSlot(fun func(x, y *big.Int) bool) *binaryOpSlot {
return &binaryOpSlot{f}
}
func longPow(f *Frame, v, w *Object) (*Object, *BaseException) {
var wLong *big.Int
vLong := toLongUnsafe(v).Value()
if w.isInstance(LongType) {
wLong = toLongUnsafe(w).Value()
} else if w.isInstance(IntType) {
wLong = big.NewInt(int64(toIntUnsafe(w).Value()))
} else {
return NotImplemented, nil
}
if wLong.Sign() < 0 {
// The result will be a float, so we call the floating point function.
var vFloat, wFloat *Object
var raised *BaseException
vFloat, raised = longFloat(f, v)
if raised != nil {
return nil, raised
}
// w might be an int or a long
if w.isInstance(LongType) {
wFloat, raised = longFloat(f, w)
if raised != nil {
return nil, raised
}
} else if w.isInstance(IntType) {
wFloat = NewFloat(float64(toIntUnsafe(w).Value())).ToObject()
} else {
// This point should not be reachable
return nil, f.RaiseType(SystemErrorType, "internal error in longPow")
}
return floatPow(f, vFloat, wFloat)
}
return NewLong(big.NewInt(0).Exp(vLong, wLong, nil)).ToObject(), nil
}
func longRPow(f *Frame, v, w *Object) (*Object, *BaseException) {
if w.isInstance(LongType) {
return longPow(f, w, v)
}
if w.isInstance(IntType) {
wLong := NewLong(big.NewInt(int64(toIntUnsafe(w).Value()))).ToObject()
return longPow(f, wLong, v)
}
return NotImplemented, nil
}
func longDivMod(x, y, z, m *big.Int) {
z.QuoRem(x, y, m)
if m.Sign() == -y.Sign() {
......
......@@ -156,6 +156,9 @@ func TestLongBinaryOps(t *testing.T) {
{Or, -100, 50, NewLong(big.NewInt(-66)).ToObject(), nil},
{Or, MaxInt, MinInt, NewLong(big.NewInt(-1)).ToObject(), nil},
{Or, newObject(ObjectType), 100, nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for |: 'object' and 'long'")},
{Pow, 2, 128, NewLong(big.NewInt(0).Exp(big.NewInt(2), big.NewInt(128), nil)).ToObject(), nil},
{Pow, 2, -2, NewFloat(0.25).ToObject(), nil},
{Pow, 2, newObject(ObjectType), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for **: 'long' and 'object'")},
{Sub, 22, 18, NewLong(big.NewInt(4)).ToObject(), nil},
{Sub, IntType.ToObject(), 42, nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for -: 'type' and 'long'")},
{Sub, MinInt, 1, NewLong(new(big.Int).Sub(minIntBig, big.NewInt(1))).ToObject(), nil},
......
......@@ -398,6 +398,7 @@ type typeSlots struct {
Int *unaryOpSlot
Invert *unaryOpSlot
IOr *binaryOpSlot
IPow *binaryOpSlot
ISub *binaryOpSlot
Iter *unaryOpSlot
IXor *binaryOpSlot
......@@ -415,6 +416,7 @@ type typeSlots struct {
Next *unaryOpSlot
NonZero *unaryOpSlot
Or *binaryOpSlot
Pow *binaryOpSlot
RAdd *binaryOpSlot
RAnd *binaryOpSlot
RDiv *binaryOpSlot
......@@ -423,6 +425,7 @@ type typeSlots struct {
RMod *binaryOpSlot
RMul *binaryOpSlot
ROr *binaryOpSlot
RPow *binaryOpSlot
RRShift *binaryOpSlot
RShift *binaryOpSlot
RSub *binaryOpSlot
......
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
assert 2.0 ** -2 == 0.25, "2.0 ** -2"
assert 2.0 ** -1 == 0.5, "2.0 ** -1"
assert 2.0 ** 0 == 1, "2.0 ** 0"
assert 2.0 ** 1 == 2, "2.0 ** 1"
assert 2.0 ** 2 == 4, "2.0 ** 2"
assert (-2.0) ** -2 == 0.25, "(-2.0) ** -2"
assert (-2.0) ** -1 == -0.5, "(-2.0) ** -1"
assert (-2.0) ** 0 == 1, "(-2.0) ** 0"
assert (-2.0) ** 1 == -2, "(-2.0) ** 1"
assert (-2.0) ** 2 == 4, "(-2.0) ** 2"
assert 2 ** -2 == 0.25, "2 ** -2"
assert 2 ** -1 == 0.5, "2 ** -1"
assert 2 ** 0 == 1, "2 ** 0"
assert 2 ** 1 == 2, "2 ** 1"
assert 2 ** 2 == 4, "2 ** 2"
assert 2L ** -2 == 0.25, "2L ** -2"
assert 2L ** -1 == 0.5, "2L ** -1"
assert 2L ** 0 == 1, "2L ** 0"
assert 2L ** 1 == 2, "2L ** 1"
assert 2L ** 2 == 4, "2L ** 2"
# Test the rpow operator on long
assert 2 ** -2L == 0.25, "2 ** -2L"
assert 2 ** -1L == 0.5, "2 ** -1L"
assert 2 ** 0L == 1, "2 ** 0L"
assert 2 ** 1L == 2, "2 ** 1L"
assert 2 ** 2L == 4, "2 ** 2L"
for zero in (0, 0L, 0.0):
try:
result = zero ** -2
assert "0 ** -2"
except ZeroDivisionError:
pass
try:
result = zero ** -1
assert "0 ** -1"
except ZeroDivisionError:
pass
assert zero ** 0 == 1, '0 ** 0'
assert zero ** 1 == 0, '0 ** 1'
assert zero ** 2 == 0, '0 ** 2'
assert 2 ** zero == 1
assert (-2.0) ** zero == 1
assert 3L ** zero == 1
assert (-2) ** -2 == 0.25, '(-2) ** -2'
assert (-2) ** -1 == -0.5, '(-2) ** -1'
assert (-2) ** 0 == 1, '(-2) ** 0'
assert (-2) ** 1 == -2, '(-2) ** 1'
assert (-2) ** 2 == 4, '(-2) ** 2'
assert 2 ** 128 == 340282366920938463463374607431768211456, "2 ** 128"
# chose something which can be represented exact as an IEEE floating point number
large_number = (2 ** 128 + 2 ** 127)
assert large_number ** -1 == (1.0 / large_number), "large_number ** -1 == (1.0 / large_number)"
assert large_number ** 0 == 1, "large_number ** 0 == 1"
assert large_number ** 1 == large_number, "large_number ** 1 == large_number"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment