-
Notifications
You must be signed in to change notification settings - Fork 636
Add binary-op pow #79
Changes from all commits
9ad4a5a
4f3cdb7
7fc970a
9081d92
d10889b
4ddaa4a
c4f6976
d8091a9
50fbe2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -184,6 +184,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) }) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
func floatPow(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatArithmeticOp(f, "__pow__", v, w, math.Pow)
}Same goes for RPow below.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RPow below can not changed this way, because the order of the arguments is reversed. |
||
| } | ||
|
|
||
| func floatRAdd(f *Frame, v, w *Object) (*Object, *BaseException) { | ||
| return floatArithmeticOp(f, "__radd__", v, w, func(v, w float64) float64 { return w + v }) | ||
| } | ||
|
|
@@ -211,6 +215,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 }) | ||
| } | ||
|
|
@@ -237,11 +245,13 @@ func initFloatType(dict map[string]*Object) { | |
| FloatType.slots.NE = &binaryOpSlot{floatNE} | ||
| 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} | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "unsupported operand type" case would be good here. |
||
| {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'")}, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A couple more test cases (e.g. TypeError case) wouldn't go amiss. |
||
| {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}, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -321,6 +321,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} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason not to use longBinaryOpSlot() here? It's a well established pattern here and it takes care of a little bit of the type coercion for you.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. longBinaryOpSlot expect a function which always return a Long. But in **, when the exponent is negative, we have to return a Float. So this function can not be used (or I'm missing something here).
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. No I don't think you're missing anything. It's probably worth adding a comment about why we don't use longBinaryOpSlot for this method. |
||
| LongType.slots.RAdd = longRBinaryOpSlot(longAdd) | ||
| LongType.slots.RAnd = longRBinaryOpSlot(longAnd) | ||
| LongType.slots.RDiv = longRDivModOpSlot(longDiv) | ||
|
|
@@ -329,6 +331,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) | ||
|
|
@@ -487,6 +491,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() { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A couple more test cases would be good |
||
| {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}, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| # | ||
| # 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. | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the file header:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor nit, but I don't think we need to create a whole new file for just this one |
||
| 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" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good to have some explicit long tests here, e.g. |
||
|
|
||
| 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" | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove excess trailing newlines (one is OK). |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple more test cases would be good here, e.g. negative numbers, floats, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the parser part. Does this really need more test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, this is consistent with what's there. It's fine to leave it the way it is.