Commit 83bb9538 authored by Mansour Rahimi's avatar Mansour Rahimi Committed by Dylan Trotter

Implement builtin divmod() (#264)

parent d26e81b9
...@@ -358,6 +358,13 @@ func builtinDir(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { ...@@ -358,6 +358,13 @@ func builtinDir(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
return l.ToObject(), nil return l.ToObject(), nil
} }
func builtinDivMod(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
if raised := checkFunctionArgs(f, "divmod", args, ObjectType, ObjectType); raised != nil {
return nil, raised
}
return DivMod(f, args[0], args[1])
}
func builtinFrame(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { func builtinFrame(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
if raised := checkFunctionArgs(f, "__frame__", args); raised != nil { if raised := checkFunctionArgs(f, "__frame__", args); raised != nil {
return nil, raised return nil, raised
...@@ -703,6 +710,7 @@ func init() { ...@@ -703,6 +710,7 @@ func init() {
"cmp": newBuiltinFunction("cmp", builtinCmp).ToObject(), "cmp": newBuiltinFunction("cmp", builtinCmp).ToObject(),
"delattr": newBuiltinFunction("delattr", builtinDelAttr).ToObject(), "delattr": newBuiltinFunction("delattr", builtinDelAttr).ToObject(),
"dir": newBuiltinFunction("dir", builtinDir).ToObject(), "dir": newBuiltinFunction("dir", builtinDir).ToObject(),
"divmod": newBuiltinFunction("divmod", builtinDivMod).ToObject(),
"Ellipsis": Ellipsis, "Ellipsis": Ellipsis,
"False": False.ToObject(), "False": False.ToObject(),
"getattr": newBuiltinFunction("getattr", builtinGetAttr).ToObject(), "getattr": newBuiltinFunction("getattr", builtinGetAttr).ToObject(),
......
...@@ -154,6 +154,23 @@ func TestBuiltinFuncs(t *testing.T) { ...@@ -154,6 +154,23 @@ func TestBuiltinFuncs(t *testing.T) {
{f: "dir", args: wrapArgs(newObject(fooType)), want: fooTypeDir.ToObject()}, {f: "dir", args: wrapArgs(newObject(fooType)), want: fooTypeDir.ToObject()},
{f: "dir", args: wrapArgs(foo), want: fooDir.ToObject()}, {f: "dir", args: wrapArgs(foo), want: fooDir.ToObject()},
{f: "dir", args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'dir' requires 1 arguments")}, {f: "dir", args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'dir' requires 1 arguments")},
{f: "divmod", args: wrapArgs(12, 7), want: NewTuple2(NewInt(1).ToObject(), NewInt(5).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(-12, 7), want: NewTuple2(NewInt(-2).ToObject(), NewInt(2).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(12, -7), want: NewTuple2(NewInt(-2).ToObject(), NewInt(-2).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(-12, -7), want: NewTuple2(NewInt(1).ToObject(), NewInt(-5).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(MaxInt, MinInt), want: NewTuple2(NewInt(-1).ToObject(), NewInt(-1).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(MinInt, MaxInt), want: NewTuple2(NewInt(-2).ToObject(), NewInt(MaxInt-1).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(MinInt, -1), want: NewTuple2(NewLong(new(big.Int).Neg(minIntBig)).ToObject(), NewLong(big.NewInt(0)).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(big.NewInt(12), big.NewInt(7)), want: NewTuple2(NewLong(big.NewInt(1)).ToObject(), NewLong(big.NewInt(5)).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(big.NewInt(-12), big.NewInt(7)), want: NewTuple2(NewLong(big.NewInt(-2)).ToObject(), NewLong(big.NewInt(2)).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(big.NewInt(12), big.NewInt(-7)), want: NewTuple2(NewLong(big.NewInt(-2)).ToObject(), NewLong(big.NewInt(-2)).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(big.NewInt(-12), big.NewInt(-7)), want: NewTuple2(NewLong(big.NewInt(1)).ToObject(), NewLong(big.NewInt(-5)).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(3.25, 1.0), want: NewTuple2(NewFloat(3.0).ToObject(), NewFloat(0.25).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(-3.25, 1.0), want: NewTuple2(NewFloat(-4.0).ToObject(), NewFloat(0.75).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(3.25, -1.0), want: NewTuple2(NewFloat(-4.0).ToObject(), NewFloat(-0.75).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(-3.25, -1.0), want: NewTuple2(NewFloat(3.0).ToObject(), NewFloat(-0.25).ToObject()).ToObject()},
{f: "divmod", args: wrapArgs(NewStr("a"), NewStr("b")), wantExc: mustCreateException(TypeErrorType, "unsupported operand type(s) for divmod(): 'str' and 'str'")},
{f: "divmod", args: wrapArgs(), wantExc: mustCreateException(TypeErrorType, "'divmod' requires 2 arguments")},
{f: "getattr", args: wrapArgs(None, NewStr("foo").ToObject(), NewStr("bar").ToObject()), want: NewStr("bar").ToObject()}, {f: "getattr", args: wrapArgs(None, NewStr("foo").ToObject(), NewStr("bar").ToObject()), want: NewStr("bar").ToObject()},
{f: "getattr", args: wrapArgs(None, NewStr("foo").ToObject()), wantExc: mustCreateException(AttributeErrorType, "'NoneType' object has no attribute 'foo'")}, {f: "getattr", args: wrapArgs(None, NewStr("foo").ToObject()), wantExc: mustCreateException(AttributeErrorType, "'NoneType' object has no attribute 'foo'")},
{f: "hasattr", args: wrapArgs(newObject(ObjectType), NewStr("foo").ToObject()), want: False.ToObject()}, {f: "hasattr", args: wrapArgs(newObject(ObjectType), NewStr("foo").ToObject()), want: False.ToObject()},
......
...@@ -159,6 +159,12 @@ func Div(f *Frame, v, w *Object) (*Object, *BaseException) { ...@@ -159,6 +159,12 @@ func Div(f *Frame, v, w *Object) (*Object, *BaseException) {
return binaryOp(f, v, w, v.typ.slots.Div, v.typ.slots.RDiv, w.typ.slots.RDiv, "/") return binaryOp(f, v, w, v.typ.slots.Div, v.typ.slots.RDiv, w.typ.slots.RDiv, "/")
} }
// DivMod returns the result (quotient and remainder tuple) of dividing v by w
// according to the __divmod/rdivmod__ operator.
func DivMod(f *Frame, v, w *Object) (*Object, *BaseException) {
return binaryOp(f, v, w, v.typ.slots.DivMod, v.typ.slots.RDivMod, w.typ.slots.RDivMod, "divmod()")
}
// Eq returns the equality of v and w according to the __eq__ operator. // Eq returns the equality of v and w according to the __eq__ operator.
func Eq(f *Frame, v, w *Object) (*Object, *BaseException) { func Eq(f *Frame, v, w *Object) (*Object, *BaseException) {
r, raised := compareRich(f, compareOpEq, v, w) r, raised := compareRich(f, compareOpEq, v, w)
......
...@@ -71,6 +71,16 @@ func floatDiv(f *Frame, v, w *Object) (*Object, *BaseException) { ...@@ -71,6 +71,16 @@ func floatDiv(f *Frame, v, w *Object) (*Object, *BaseException) {
}) })
} }
func floatDivMod(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatDivAndModOp(f, "__divmod__", v, w, func(v, w float64) (float64, float64, bool) {
m, r := floatModFunc(v, w)
if !r {
return 0, 0, false
}
return math.Floor(v / w), m, true
})
}
func floatEq(f *Frame, v, w *Object) (*Object, *BaseException) { func floatEq(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatCompare(toFloatUnsafe(v), w, False, True, False), nil return floatCompare(toFloatUnsafe(v), w, False, True, False), nil
} }
...@@ -234,6 +244,16 @@ func floatRDiv(f *Frame, v, w *Object) (*Object, *BaseException) { ...@@ -234,6 +244,16 @@ func floatRDiv(f *Frame, v, w *Object) (*Object, *BaseException) {
}) })
} }
func floatRDivMod(f *Frame, v, w *Object) (*Object, *BaseException) {
return floatDivAndModOp(f, "__rdivmod__", v, w, func(v, w float64) (float64, float64, bool) {
m, r := floatModFunc(w, v)
if !r {
return 0, 0, false
}
return w / v, m, true
})
}
func floatRepr(f *Frame, o *Object) (*Object, *BaseException) { func floatRepr(f *Frame, o *Object) (*Object, *BaseException) {
return NewStr(strconv.FormatFloat(toFloatUnsafe(o).Value(), 'g', -1, 64)).ToObject(), nil return NewStr(strconv.FormatFloat(toFloatUnsafe(o).Value(), 'g', -1, 64)).ToObject(), nil
} }
...@@ -265,6 +285,7 @@ func initFloatType(dict map[string]*Object) { ...@@ -265,6 +285,7 @@ func initFloatType(dict map[string]*Object) {
FloatType.slots.Abs = &unaryOpSlot{floatAbs} FloatType.slots.Abs = &unaryOpSlot{floatAbs}
FloatType.slots.Add = &binaryOpSlot{floatAdd} FloatType.slots.Add = &binaryOpSlot{floatAdd}
FloatType.slots.Div = &binaryOpSlot{floatDiv} FloatType.slots.Div = &binaryOpSlot{floatDiv}
FloatType.slots.DivMod = &binaryOpSlot{floatDivMod}
FloatType.slots.Eq = &binaryOpSlot{floatEq} FloatType.slots.Eq = &binaryOpSlot{floatEq}
FloatType.slots.Float = &unaryOpSlot{floatFloat} FloatType.slots.Float = &unaryOpSlot{floatFloat}
FloatType.slots.GE = &binaryOpSlot{floatGE} FloatType.slots.GE = &binaryOpSlot{floatGE}
...@@ -285,6 +306,7 @@ func initFloatType(dict map[string]*Object) { ...@@ -285,6 +306,7 @@ func initFloatType(dict map[string]*Object) {
FloatType.slots.Pow = &binaryOpSlot{floatPow} FloatType.slots.Pow = &binaryOpSlot{floatPow}
FloatType.slots.RAdd = &binaryOpSlot{floatRAdd} FloatType.slots.RAdd = &binaryOpSlot{floatRAdd}
FloatType.slots.RDiv = &binaryOpSlot{floatRDiv} FloatType.slots.RDiv = &binaryOpSlot{floatRDiv}
FloatType.slots.RDivMod = &binaryOpSlot{floatRDivMod}
FloatType.slots.Repr = &unaryOpSlot{floatRepr} FloatType.slots.Repr = &unaryOpSlot{floatRepr}
FloatType.slots.RMod = &binaryOpSlot{floatRMod} FloatType.slots.RMod = &binaryOpSlot{floatRMod}
FloatType.slots.RMul = &binaryOpSlot{floatRMul} FloatType.slots.RMul = &binaryOpSlot{floatRMul}
...@@ -375,6 +397,21 @@ func floatDivModOp(f *Frame, method string, v, w *Object, fun func(v, w float64) ...@@ -375,6 +397,21 @@ func floatDivModOp(f *Frame, method string, v, w *Object, fun func(v, w float64)
return NewFloat(x).ToObject(), nil return NewFloat(x).ToObject(), nil
} }
func floatDivAndModOp(f *Frame, method string, v, w *Object, fun func(v, w float64) (float64, float64, bool)) (*Object, *BaseException) {
floatW, ok := floatCoerce(w)
if !ok {
if math.IsInf(floatW, 0) {
return nil, f.RaiseType(OverflowErrorType, "long int too large to convert to float")
}
return NotImplemented, nil
}
q, m, ok := fun(toFloatUnsafe(v).Value(), floatW)
if !ok {
return nil, f.RaiseType(ZeroDivisionErrorType, "float division or modulo by zero")
}
return NewTuple2(NewFloat(q).ToObject(), NewFloat(m).ToObject()).ToObject(), nil
}
func hashFloat(v float64) int { func hashFloat(v float64) int {
if math.IsNaN(v) { if math.IsNaN(v) {
return 0 return 0
......
...@@ -101,6 +101,48 @@ func TestFloatArithmeticOps(t *testing.T) { ...@@ -101,6 +101,48 @@ func TestFloatArithmeticOps(t *testing.T) {
} }
} }
func TestFloatDivMod(t *testing.T) {
cases := []invokeTestCase{
{args: wrapArgs(12.5, 4.0), want: NewTuple2(NewFloat(3).ToObject(), NewFloat(0.5).ToObject()).ToObject()},
{args: wrapArgs(-12.5, 4.0), want: NewTuple2(NewFloat(-4).ToObject(), NewFloat(3.5).ToObject()).ToObject()},
{args: wrapArgs(25.0, 5.0), want: NewTuple2(NewFloat(5).ToObject(), NewFloat(0).ToObject()).ToObject()},
{args: wrapArgs(-20.2, 40.0), want: NewTuple2(NewFloat(-1).ToObject(), NewFloat(19.8).ToObject()).ToObject()},
{args: wrapArgs(math.Inf(1), math.Inf(1)), want: NewTuple2(NewFloat(math.NaN()).ToObject(), NewFloat(math.NaN()).ToObject()).ToObject()},
{args: wrapArgs(math.Inf(1), math.Inf(-1)), want: NewTuple2(NewFloat(math.NaN()).ToObject(), NewFloat(math.NaN()).ToObject()).ToObject()},
{args: wrapArgs(math.Inf(-1), -20.0), want: NewTuple2(NewFloat(math.Inf(1)).ToObject(), NewFloat(math.NaN()).ToObject()).ToObject()},
{args: wrapArgs(1, math.Inf(1)), want: NewTuple2(NewFloat(0).ToObject(), NewFloat(1).ToObject()).ToObject()},
{args: wrapArgs(newObject(ObjectType), 1.1), wantExc: mustCreateException(TypeErrorType, "unsupported operand type(s) for divmod(): 'object' and 'float'")},
{args: wrapArgs(True.ToObject(), 0.0), wantExc: mustCreateException(ZeroDivisionErrorType, "float division or modulo by zero")},
{args: wrapArgs(math.Inf(1), 0.0), wantExc: mustCreateException(ZeroDivisionErrorType, "float division or modulo by zero")},
{args: wrapArgs(1.0, bigLongNumber), wantExc: mustCreateException(OverflowErrorType, "long int too large to convert to float")},
}
for _, cas := range cases {
switch got, result := checkInvokeResult(wrapFuncForTest(DivMod), cas.args, cas.want, cas.wantExc); result {
case checkInvokeResultExceptionMismatch:
t.Errorf("float.__divmod__%v raised %v, want %v", cas.args, got, cas.wantExc)
case checkInvokeResultReturnValueMismatch:
// Handle NaN specially, since NaN != NaN.
if got == nil || cas.want == nil || !got.isInstance(TupleType) || !cas.want.isInstance(TupleType) ||
!isNaNTupleFloat(got, cas.want) {
t.Errorf("float.__divmod__%v = %v, want %v", cas.args, got, cas.want)
}
}
}
}
func isNaNTupleFloat(got, want *Object) bool {
if toTupleUnsafe(got).Len() != toTupleUnsafe(want).Len() {
return false
}
for i := 0; i < toTupleUnsafe(got).Len(); i++ {
if math.IsNaN(toFloatUnsafe(toTupleUnsafe(got).GetItem(i)).Value()) &&
math.IsNaN(toFloatUnsafe(toTupleUnsafe(want).GetItem(i)).Value()) {
return true
}
}
return false
}
func TestFloatCompare(t *testing.T) { func TestFloatCompare(t *testing.T) {
cases := []invokeTestCase{ cases := []invokeTestCase{
{args: wrapArgs(1.0, 1.0), want: compareAllResultEq}, {args: wrapArgs(1.0, 1.0), want: compareAllResultEq},
......
...@@ -90,6 +90,10 @@ func intDiv(f *Frame, v, w *Object) (*Object, *BaseException) { ...@@ -90,6 +90,10 @@ func intDiv(f *Frame, v, w *Object) (*Object, *BaseException) {
return intDivModOp(f, "__div__", v, w, intCheckedDiv, longDiv) return intDivModOp(f, "__div__", v, w, intCheckedDiv, longDiv)
} }
func intDivMod(f *Frame, v, w *Object) (*Object, *BaseException) {
return intDivAndModOp(f, "__divmod__", v, w, intCheckedDivMod, longDivAndMod)
}
func intEq(f *Frame, v, w *Object) (*Object, *BaseException) { func intEq(f *Frame, v, w *Object) (*Object, *BaseException) {
return intCompare(compareOpEq, toIntUnsafe(v), w), nil return intCompare(compareOpEq, toIntUnsafe(v), w), nil
} }
...@@ -317,6 +321,14 @@ func intRDiv(f *Frame, v, w *Object) (*Object, *BaseException) { ...@@ -317,6 +321,14 @@ func intRDiv(f *Frame, v, w *Object) (*Object, *BaseException) {
}) })
} }
func intRDivMod(f *Frame, v, w *Object) (*Object, *BaseException) {
return intDivAndModOp(f, "__rdivmod__", v, w, func(v, w int) (int, int, divModResult) {
return intCheckedDivMod(w, v)
}, func(z, m, x, y *big.Int) {
longDivAndMod(z, m, y, x)
})
}
func intRepr(f *Frame, o *Object) (*Object, *BaseException) { func intRepr(f *Frame, o *Object) (*Object, *BaseException) {
return NewStr(strconv.FormatInt(int64(toIntUnsafe(o).Value()), 10)).ToObject(), nil return NewStr(strconv.FormatInt(int64(toIntUnsafe(o).Value()), 10)).ToObject(), nil
} }
...@@ -370,6 +382,7 @@ func initIntType(dict map[string]*Object) { ...@@ -370,6 +382,7 @@ func initIntType(dict map[string]*Object) {
IntType.slots.Add = &binaryOpSlot{intAdd} IntType.slots.Add = &binaryOpSlot{intAdd}
IntType.slots.And = &binaryOpSlot{intAnd} IntType.slots.And = &binaryOpSlot{intAnd}
IntType.slots.Div = &binaryOpSlot{intDiv} IntType.slots.Div = &binaryOpSlot{intDiv}
IntType.slots.DivMod = &binaryOpSlot{intDivMod}
IntType.slots.Eq = &binaryOpSlot{intEq} IntType.slots.Eq = &binaryOpSlot{intEq}
IntType.slots.GE = &binaryOpSlot{intGE} IntType.slots.GE = &binaryOpSlot{intGE}
IntType.slots.GT = &binaryOpSlot{intGT} IntType.slots.GT = &binaryOpSlot{intGT}
...@@ -397,6 +410,7 @@ func initIntType(dict map[string]*Object) { ...@@ -397,6 +410,7 @@ func initIntType(dict map[string]*Object) {
IntType.slots.RAdd = &binaryOpSlot{intRAdd} IntType.slots.RAdd = &binaryOpSlot{intRAdd}
IntType.slots.RAnd = &binaryOpSlot{intAnd} IntType.slots.RAnd = &binaryOpSlot{intAnd}
IntType.slots.RDiv = &binaryOpSlot{intRDiv} IntType.slots.RDiv = &binaryOpSlot{intRDiv}
IntType.slots.RDivMod = &binaryOpSlot{intRDivMod}
IntType.slots.Repr = &unaryOpSlot{intRepr} IntType.slots.Repr = &unaryOpSlot{intRepr}
IntType.slots.RMod = &binaryOpSlot{intRMod} IntType.slots.RMod = &binaryOpSlot{intRMod}
IntType.slots.RMul = &binaryOpSlot{intRMul} IntType.slots.RMul = &binaryOpSlot{intRMul}
...@@ -536,6 +550,20 @@ func intDivModOp(f *Frame, method string, v, w *Object, fun func(v, w int) (int, ...@@ -536,6 +550,20 @@ func intDivModOp(f *Frame, method string, v, w *Object, fun func(v, w int) (int,
return NewInt(x).ToObject(), nil return NewInt(x).ToObject(), nil
} }
func intDivAndModOp(f *Frame, method string, v, w *Object, fun func(v, w int) (int, int, divModResult), bigFun func(z, m, x, y *big.Int)) (*Object, *BaseException) {
if !w.isInstance(IntType) {
return NotImplemented, nil
}
q, m, r := fun(toIntUnsafe(v).Value(), toIntUnsafe(w).Value())
switch r {
case divModOverflow:
return longCallBinaryTuple(bigFun, intToLong(toIntUnsafe(v)), intToLong(toIntUnsafe(w))), nil
case divModZeroDivision:
return nil, f.RaiseType(ZeroDivisionErrorType, "integer division or modulo by zero")
}
return NewTuple2(NewInt(q).ToObject(), NewInt(m).ToObject()).ToObject(), nil
}
func intShiftOp(f *Frame, v, w *Object, fun func(int, int) (int, int, bool)) (*Object, *BaseException) { func intShiftOp(f *Frame, v, w *Object, fun func(int, int) (int, int, bool)) (*Object, *BaseException) {
if !w.isInstance(IntType) { if !w.isInstance(IntType) {
return NotImplemented, nil return NotImplemented, nil
......
...@@ -39,6 +39,13 @@ func TestIntBinaryOps(t *testing.T) { ...@@ -39,6 +39,13 @@ func TestIntBinaryOps(t *testing.T) {
{Div, NewList().ToObject(), NewInt(21).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for /: 'list' and 'int'")}, {Div, NewList().ToObject(), NewInt(21).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for /: 'list' and 'int'")},
{Div, NewInt(1).ToObject(), NewInt(0).ToObject(), nil, mustCreateException(ZeroDivisionErrorType, "integer division or modulo by zero")}, {Div, NewInt(1).ToObject(), NewInt(0).ToObject(), nil, mustCreateException(ZeroDivisionErrorType, "integer division or modulo by zero")},
{Div, NewInt(MinInt).ToObject(), NewInt(-1).ToObject(), NewLong(new(big.Int).Neg(minIntBig)).ToObject(), nil}, {Div, NewInt(MinInt).ToObject(), NewInt(-1).ToObject(), NewLong(new(big.Int).Neg(minIntBig)).ToObject(), nil},
{DivMod, NewInt(7).ToObject(), NewInt(3).ToObject(), NewTuple2(NewInt(2).ToObject(), NewInt(1).ToObject()).ToObject(), nil},
{DivMod, NewInt(3).ToObject(), NewInt(-7).ToObject(), NewTuple2(NewInt(-1).ToObject(), NewInt(-4).ToObject()).ToObject(), nil},
{DivMod, NewInt(MaxInt).ToObject(), NewInt(MinInt).ToObject(), NewTuple2(NewInt(-1).ToObject(), NewInt(-1).ToObject()).ToObject(), nil},
{DivMod, NewInt(MinInt).ToObject(), NewInt(MaxInt).ToObject(), NewTuple2(NewInt(-2).ToObject(), NewInt(MaxInt-1).ToObject()).ToObject(), nil},
{DivMod, NewList().ToObject(), NewInt(21).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for divmod(): 'list' and 'int'")},
{DivMod, NewInt(1).ToObject(), NewInt(0).ToObject(), nil, mustCreateException(ZeroDivisionErrorType, "integer division or modulo by zero")},
{DivMod, NewInt(MinInt).ToObject(), NewInt(-1).ToObject(), NewTuple2(NewLong(new(big.Int).Neg(minIntBig)).ToObject(), NewLong(big.NewInt(0)).ToObject()).ToObject(), nil},
{LShift, NewInt(2).ToObject(), NewInt(4).ToObject(), NewInt(32).ToObject(), nil}, {LShift, NewInt(2).ToObject(), NewInt(4).ToObject(), NewInt(32).ToObject(), nil},
{LShift, NewInt(-12).ToObject(), NewInt(10).ToObject(), NewInt(-12288).ToObject(), nil}, {LShift, NewInt(-12).ToObject(), NewInt(10).ToObject(), NewInt(-12288).ToObject(), nil},
{LShift, NewInt(10).ToObject(), NewInt(100).ToObject(), NewLong(new(big.Int).Lsh(big.NewInt(10), 100)).ToObject(), nil}, {LShift, NewInt(10).ToObject(), NewInt(100).ToObject(), NewLong(new(big.Int).Lsh(big.NewInt(10), 100)).ToObject(), nil},
......
...@@ -112,6 +112,10 @@ func longDiv(z, x, y *big.Int) { ...@@ -112,6 +112,10 @@ func longDiv(z, x, y *big.Int) {
longDivMod(x, y, z, &m) longDivMod(x, y, z, &m)
} }
func longDivAndMod(z, m, x, y *big.Int) {
longDivMod(x, y, z, m)
}
func longEq(x, y *big.Int) bool { func longEq(x, y *big.Int) bool {
return x.Cmp(y) == 0 return x.Cmp(y) == 0
} }
...@@ -337,6 +341,7 @@ func initLongType(dict map[string]*Object) { ...@@ -337,6 +341,7 @@ func initLongType(dict map[string]*Object) {
LongType.slots.Add = longBinaryOpSlot(longAdd) LongType.slots.Add = longBinaryOpSlot(longAdd)
LongType.slots.And = longBinaryOpSlot(longAnd) LongType.slots.And = longBinaryOpSlot(longAnd)
LongType.slots.Div = longDivModOpSlot(longDiv) LongType.slots.Div = longDivModOpSlot(longDiv)
LongType.slots.DivMod = longDivAndModOpSlot(longDivAndMod)
LongType.slots.Eq = longBinaryBoolOpSlot(longEq) LongType.slots.Eq = longBinaryBoolOpSlot(longEq)
LongType.slots.Float = &unaryOpSlot{longFloat} LongType.slots.Float = &unaryOpSlot{longFloat}
LongType.slots.GE = longBinaryBoolOpSlot(longGE) LongType.slots.GE = longBinaryBoolOpSlot(longGE)
...@@ -365,6 +370,7 @@ func initLongType(dict map[string]*Object) { ...@@ -365,6 +370,7 @@ func initLongType(dict map[string]*Object) {
LongType.slots.RAdd = longRBinaryOpSlot(longAdd) LongType.slots.RAdd = longRBinaryOpSlot(longAdd)
LongType.slots.RAnd = longRBinaryOpSlot(longAnd) LongType.slots.RAnd = longRBinaryOpSlot(longAnd)
LongType.slots.RDiv = longRDivModOpSlot(longDiv) LongType.slots.RDiv = longRDivModOpSlot(longDiv)
LongType.slots.RDivMod = longRDivAndModOpSlot(longDivAndMod)
LongType.slots.Repr = &unaryOpSlot{longRepr} LongType.slots.Repr = &unaryOpSlot{longRepr}
LongType.slots.RMod = longRDivModOpSlot(longMod) LongType.slots.RMod = longRDivModOpSlot(longMod)
LongType.slots.RMul = longRBinaryOpSlot(longMul) LongType.slots.RMul = longRBinaryOpSlot(longMul)
...@@ -397,6 +403,13 @@ func longCallBinary(fun func(z, x, y *big.Int), v, w *Long) *Object { ...@@ -397,6 +403,13 @@ func longCallBinary(fun func(z, x, y *big.Int), v, w *Long) *Object {
return l.ToObject() return l.ToObject()
} }
func longCallBinaryTuple(fun func(z, m, x, y *big.Int), v, w *Long) *Object {
l := Long{Object: Object{typ: LongType}}
ll := Long{Object: Object{typ: LongType}}
fun(&l.value, &ll.value, &v.value, &w.value)
return NewTuple2(l.ToObject(), ll.ToObject()).ToObject()
}
func longCallBinaryBool(fun func(x, y *big.Int) bool, v, w *Long) *Object { func longCallBinaryBool(fun func(x, y *big.Int) bool, v, w *Long) *Object {
return GetBool(fun(&v.value, &w.value)).ToObject() return GetBool(fun(&v.value, &w.value)).ToObject()
} }
...@@ -420,6 +433,13 @@ func longCallDivMod(fun func(z, x, y *big.Int), f *Frame, v, w *Long) (*Object, ...@@ -420,6 +433,13 @@ func longCallDivMod(fun func(z, x, y *big.Int), f *Frame, v, w *Long) (*Object,
return longCallBinary(fun, v, w), nil return longCallBinary(fun, v, w), nil
} }
func longCallDivAndMod(fun func(z, m, x, y *big.Int), f *Frame, v, w *Long) (*Object, *BaseException) {
if w.value.Sign() == 0 {
return nil, f.RaiseType(ZeroDivisionErrorType, "integer division or modulo by zero")
}
return longCallBinaryTuple(fun, v, w), nil
}
func longUnaryOpSlot(fun func(z, x *big.Int)) *unaryOpSlot { func longUnaryOpSlot(fun func(z, x *big.Int)) *unaryOpSlot {
f := func(_ *Frame, v *Object) (*Object, *BaseException) { f := func(_ *Frame, v *Object) (*Object, *BaseException) {
return longCallUnary(fun, toLongUnsafe(v)), nil return longCallUnary(fun, toLongUnsafe(v)), nil
...@@ -482,6 +502,30 @@ func longRDivModOpSlot(fun func(z, x, y *big.Int)) *binaryOpSlot { ...@@ -482,6 +502,30 @@ func longRDivModOpSlot(fun func(z, x, y *big.Int)) *binaryOpSlot {
return &binaryOpSlot{f} return &binaryOpSlot{f}
} }
func longDivAndModOpSlot(fun func(z, m, x, y *big.Int)) *binaryOpSlot {
f := func(f *Frame, v, w *Object) (*Object, *BaseException) {
if w.isInstance(IntType) {
w = intToLong(toIntUnsafe(w)).ToObject()
} else if !w.isInstance(LongType) {
return NotImplemented, nil
}
return longCallDivAndMod(fun, f, toLongUnsafe(v), toLongUnsafe(w))
}
return &binaryOpSlot{f}
}
func longRDivAndModOpSlot(fun func(z, m, x, y *big.Int)) *binaryOpSlot {
f := func(f *Frame, v, w *Object) (*Object, *BaseException) {
if w.isInstance(IntType) {
w = intToLong(toIntUnsafe(w)).ToObject()
} else if !w.isInstance(LongType) {
return NotImplemented, nil
}
return longCallDivAndMod(fun, f, toLongUnsafe(w), toLongUnsafe(v))
}
return &binaryOpSlot{f}
}
func longShiftOpSlot(fun func(z, x *big.Int, n uint)) *binaryOpSlot { func longShiftOpSlot(fun func(z, x *big.Int, n uint)) *binaryOpSlot {
f := func(f *Frame, v, w *Object) (*Object, *BaseException) { f := func(f *Frame, v, w *Object) (*Object, *BaseException) {
if w.isInstance(IntType) { if w.isInstance(IntType) {
......
...@@ -133,6 +133,14 @@ func TestLongBinaryOps(t *testing.T) { ...@@ -133,6 +133,14 @@ func TestLongBinaryOps(t *testing.T) {
{Div, NewList().ToObject(), NewLong(big.NewInt(21)).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for /: 'list' and 'long'")}, {Div, NewList().ToObject(), NewLong(big.NewInt(21)).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for /: 'list' and 'long'")},
{Div, 1, 0, nil, mustCreateException(ZeroDivisionErrorType, "integer division or modulo by zero")}, {Div, 1, 0, nil, mustCreateException(ZeroDivisionErrorType, "integer division or modulo by zero")},
{Div, MinInt, -1, NewLong(new(big.Int).Neg(minIntBig)).ToObject(), nil}, {Div, MinInt, -1, NewLong(new(big.Int).Neg(minIntBig)).ToObject(), nil},
{DivMod, 7, 3, NewTuple2(NewLong(big.NewInt(2)).ToObject(), NewLong(big.NewInt(1)).ToObject()).ToObject(), nil},
{DivMod, 3, -7, NewTuple2(NewLong(big.NewInt(-1)).ToObject(), NewLong(big.NewInt(-4)).ToObject()).ToObject(), nil},
{DivMod, MaxInt, MinInt, NewTuple2(NewLong(big.NewInt(-1)).ToObject(), NewLong(big.NewInt(-1)).ToObject()).ToObject(), nil},
{DivMod, MinInt, MaxInt, NewTuple2(NewLong(big.NewInt(-2)).ToObject(), NewLong(big.NewInt(MaxInt-1)).ToObject()).ToObject(), nil},
{DivMod, MinInt, 1, NewTuple2(NewLong(big.NewInt(MinInt)).ToObject(), NewLong(big.NewInt(0)).ToObject()).ToObject(), nil},
{DivMod, MinInt, -1, NewTuple2(NewLong(new(big.Int).Neg(minIntBig)).ToObject(), NewLong(big.NewInt(0)).ToObject()).ToObject(), nil},
{DivMod, NewList().ToObject(), NewLong(big.NewInt(21)).ToObject(), nil, mustCreateException(TypeErrorType, "unsupported operand type(s) for divmod(): 'list' and 'long'")},
{DivMod, 1, 0, nil, mustCreateException(ZeroDivisionErrorType, "integer division or modulo by zero")},
{LShift, 2, 4, NewLong(big.NewInt(32)).ToObject(), nil}, {LShift, 2, 4, NewLong(big.NewInt(32)).ToObject(), nil},
{LShift, 12, 10, NewLong(big.NewInt(12288)).ToObject(), nil}, {LShift, 12, 10, NewLong(big.NewInt(12288)).ToObject(), nil},
{LShift, 10, 100, NewLong(new(big.Int).Lsh(big.NewInt(10), 100)).ToObject(), nil}, {LShift, 10, 100, NewLong(new(big.Int).Lsh(big.NewInt(10), 100)).ToObject(), nil},
......
...@@ -380,6 +380,7 @@ type typeSlots struct { ...@@ -380,6 +380,7 @@ type typeSlots struct {
Delete *deleteSlot Delete *deleteSlot
DelItem *delItemSlot DelItem *delItemSlot
Div *binaryOpSlot Div *binaryOpSlot
DivMod *binaryOpSlot
Eq *binaryOpSlot Eq *binaryOpSlot
Float *unaryOpSlot Float *unaryOpSlot
GE *binaryOpSlot GE *binaryOpSlot
...@@ -392,6 +393,7 @@ type typeSlots struct { ...@@ -392,6 +393,7 @@ type typeSlots struct {
IAdd *binaryOpSlot IAdd *binaryOpSlot
IAnd *binaryOpSlot IAnd *binaryOpSlot
IDiv *binaryOpSlot IDiv *binaryOpSlot
IDivMod *binaryOpSlot
ILShift *binaryOpSlot ILShift *binaryOpSlot
IMod *binaryOpSlot IMod *binaryOpSlot
IMul *binaryOpSlot IMul *binaryOpSlot
...@@ -425,6 +427,7 @@ type typeSlots struct { ...@@ -425,6 +427,7 @@ type typeSlots struct {
RAdd *binaryOpSlot RAdd *binaryOpSlot
RAnd *binaryOpSlot RAnd *binaryOpSlot
RDiv *binaryOpSlot RDiv *binaryOpSlot
RDivMod *binaryOpSlot
Repr *unaryOpSlot Repr *unaryOpSlot
RLShift *binaryOpSlot RLShift *binaryOpSlot
RMod *binaryOpSlot RMod *binaryOpSlot
......
...@@ -348,3 +348,40 @@ a = [1, 2, 3] ...@@ -348,3 +348,40 @@ a = [1, 2, 3]
assert map(None, a) == a assert map(None, a) == a
assert map(None, a) is not a assert map(None, a) is not a
assert map(None, (1, 2, 3)) == [1, 2, 3] assert map(None, (1, 2, 3)) == [1, 2, 3]
# divmod(v, w)
import sys
assert divmod(12, 7) == (1, 5)
assert divmod(-12, 7) == (-2, 2)
assert divmod(12, -7) == (-2, -2)
assert divmod(-12, -7) == (1, -5)
assert divmod(-sys.maxsize - 1, -1) == (sys.maxsize + 1, 0)
assert isinstance(divmod(12, 7), tuple)
assert isinstance(divmod(12, 7)[0], int)
assert isinstance(divmod(12, 7)[1], int)
assert divmod(long(7), long(3)) == (2L, 1L)
assert divmod(long(3), long(-7)) == (-1L, -4L)
assert divmod(long(sys.maxsize), long(-sys.maxsize)) == (-1L, 0L)
assert divmod(long(-sys.maxsize), long(1)) == (-sys.maxsize, 0L)
assert divmod(long(-sys.maxsize), long(-1)) == (sys.maxsize, 0L)
assert isinstance(divmod(long(7), long(3)), tuple)
assert isinstance(divmod(long(7), long(3))[0], long)
assert isinstance(divmod(long(7), long(3))[1], long)
assert divmod(3.25, 1.0) == (3.0, 0.25)
assert divmod(-3.25, 1.0) == (-4.0, 0.75)
assert divmod(3.25, -1.0) == (-4.0, -0.75)
assert divmod(-3.25, -1.0) == (3.0, -0.25)
assert isinstance(divmod(3.25, 1.0), tuple)
assert isinstance(divmod(3.25, 1.0)[0], float)
assert isinstance(divmod(3.25, 1.0)[1], float)
try:
divmod('a', 'b')
except TypeError as e:
assert str(e) == "unsupported operand type(s) for divmod(): 'str' and 'str'"
else:
assert AssertionError
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