Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 631
Add binary-op pow#79
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
9ad4a5a4f3cdb77fc970a9081d92d10889b4ddaa4ac4f6976d8091a950fbe2eFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
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) }) | ||
| ||
| } | ||
| 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. ContributorAuthor 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.