Skip to content

Commit

Permalink
Add IntRange.TryFoo methods
Browse files Browse the repository at this point in the history
  • Loading branch information
nigeltao committed Nov 2, 2019
1 parent 7ec2528 commit 7ce58b6
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 46 deletions.
14 changes: 6 additions & 8 deletions lang/check/bounds.go
Expand Up @@ -1384,7 +1384,7 @@ func (q *checker) bcheckExprBinaryOp1(op t.ID, lhs *a.Expr, lb bounds, rhs *a.Ex
return bounds{}, fmt.Errorf("check: divide/modulus op argument %q is possibly non-positive", rhs.Str(q.tm))
}
if op == t.IDXBinarySlash {
nb, _ := lb.Quo(rb)
nb, _ := lb.TryQuo(rb)
return nb, nil
}
return bounds{
Expand Down Expand Up @@ -1413,14 +1413,14 @@ func (q *checker) bcheckExprBinaryOp1(op t.ID, lhs *a.Expr, lb bounds, rhs *a.Ex

switch op {
case t.IDXBinaryShiftL:
nb, _ := lb.Lsh(rb)
nb, _ := lb.TryLsh(rb)
return nb, nil
case t.IDXBinaryTildeModShiftL:
nb, _ := lb.Lsh(rb)
nb, _ := lb.TryLsh(rb)
nb[1] = min(nb[1], typeBounds[1])
return nb, nil
case t.IDXBinaryShiftR:
nb, _ := lb.Rsh(rb)
nb, _ := lb.TryRsh(rb)
return nb, nil
}

Expand All @@ -1435,11 +1435,9 @@ func (q *checker) bcheckExprBinaryOp1(op t.ID, lhs *a.Expr, lb bounds, rhs *a.Ex
}
switch op {
case t.IDXBinaryAmp:
nb, _ := lb.And(rb)
return nb, nil
return lb.And(rb), nil
case t.IDXBinaryPipe:
nb, _ := lb.Or(rb)
return nb, nil
return lb.Or(rb), nil
case t.IDXBinaryHat:
z := max(lb[1], rb[1])
// Return [0, z rounded up to the next power-of-2-minus-1]. This is
Expand Down
76 changes: 56 additions & 20 deletions lib/interval/interval.go
Expand Up @@ -189,6 +189,13 @@ func (x *biggerIntPair) fromIntRange(y IntRange) {
// *big.Int pointer values. Specifically, after "z = x.Add(y)", mutating
// "*z[0]" will not affect "*x[0]", "*x[1]", "*y[0]" or "*y[1]".
//
// Those operator-like methods come in two forms: Foo and TryFoo. The TryFoo
// forms return (z IntRange, ok bool). The bool indicates success, as
// operations like dividing by zero or shifting by a negative value can fail.
// When TryFoo can never fail, there is also a Foo method that omits the
// always-true bool. For example, there are Add, TryAdd and TryLsh methods, but
// no Lsh method.
//
// A subtle point is that an interval's minimum or maximum can be infinite, but
// if an integer value i is known to be within such an interval, i's possible
// values are arbitrarily large but not infinite. Specifically, 0*i is
Expand Down Expand Up @@ -413,6 +420,11 @@ func (x IntRange) Unite(y IntRange) (z IntRange) {
return z
}

// TryUnite returns (x.Unite(y), true).
func (x IntRange) TryUnite(y IntRange) (z IntRange, ok bool) {
return x.Unite(y), true
}

func (x *IntRange) inPlaceUnite(y IntRange) {
if y.Empty() {
return
Expand Down Expand Up @@ -478,6 +490,11 @@ func (x IntRange) Intersect(y IntRange) (z IntRange) {
return z
}

// TryIntersect returns (x.Intersect(y), true).
func (x IntRange) TryIntersect(y IntRange) (z IntRange, ok bool) {
return x.Intersect(y), true
}

// Add returns z = x + y.
func (x IntRange) Add(y IntRange) (z IntRange) {
if x.Empty() || y.Empty() {
Expand All @@ -492,6 +509,11 @@ func (x IntRange) Add(y IntRange) (z IntRange) {
return z
}

// TryAdd returns (x.Add(y), true).
func (x IntRange) TryAdd(y IntRange) (z IntRange, ok bool) {
return x.Add(y), true
}

// Sub returns z = x - y.
func (x IntRange) Sub(y IntRange) (z IntRange) {
if x.Empty() || y.Empty() {
Expand All @@ -506,17 +528,27 @@ func (x IntRange) Sub(y IntRange) (z IntRange) {
return z
}

// TrySub returns (x.Sub(y), true).
func (x IntRange) TrySub(y IntRange) (z IntRange, ok bool) {
return x.Sub(y), true
}

// Mul returns z = x * y.
func (x IntRange) Mul(y IntRange) (z IntRange) {
return x.mulLsh(y, false)
}

// Lsh returns z = x << y.
// TryMul returns (x.Mul(y), true).
func (x IntRange) TryMul(y IntRange) (z IntRange, ok bool) {
return x.Mul(y), true
}

// TryLsh returns z = x << y.
//
// ok is false (and z will be IntRange{nil, nil}) if x is non-empty and y
// contains at least one negative value, as it's invalid to shift by a negative
// number. Otherwise, ok is true.
func (x IntRange) Lsh(y IntRange) (z IntRange, ok bool) {
func (x IntRange) TryLsh(y IntRange) (z IntRange, ok bool) {
if !x.Empty() && y.ContainsNegative() {
return IntRange{}, false
}
Expand Down Expand Up @@ -602,12 +634,12 @@ func (x IntRange) mulLsh(y IntRange, shift bool) (z IntRange) {
return ret.toIntRange()
}

// Quo returns z = x / y. Like the big.Int.Quo method (and unlike the
// TryQuo returns z = x / y. Like the big.Int.Quo method (and unlike the
// big.Int.Div method), it truncates towards zero.
//
// ok is false (and z will be IntRange{nil, nil}) if x is non-empty and y
// contains zero, as it's invalid to divide by zero. Otherwise, ok is true.
func (x IntRange) Quo(y IntRange) (z IntRange, ok bool) {
func (x IntRange) TryQuo(y IntRange) (z IntRange, ok bool) {
if x.Empty() || y.Empty() {
return makeEmptyRange(), true
}
Expand Down Expand Up @@ -692,12 +724,12 @@ func (x IntRange) Quo(y IntRange) (z IntRange, ok bool) {
return ret.toIntRange(), true
}

// Rsh returns z = x >> y.
// TryRsh returns z = x >> y.
//
// ok is false (and z will be IntRange{nil, nil}) if x is non-empty and y
// contains at least one negative value, as it's invalid to shift by a negative
// number. Otherwise, ok is true.
func (x IntRange) Rsh(y IntRange) (z IntRange, ok bool) {
func (x IntRange) TryRsh(y IntRange) (z IntRange, ok bool) {
if x.Empty() || y.Empty() {
return makeEmptyRange(), true
}
Expand Down Expand Up @@ -750,15 +782,12 @@ func (x IntRange) Rsh(y IntRange) (z IntRange, ok bool) {
}

// And returns z = x & y.
//
// ok is false (and z will be IntRange{nil, nil}) if x or y contains at least
// one negative value. Otherwise, ok is true.
func (x IntRange) And(y IntRange) (z IntRange, ok bool) {
func (x IntRange) And(y IntRange) (z IntRange) {
if x.Empty() || y.Empty() {
return makeEmptyRange(), true
return makeEmptyRange()
}
if !x.ContainsNegative() && !y.ContainsNegative() {
return andBothNonNeg(x, y), true
return andBothNonNeg(x, y)
}

negX, nonX, hasNegX, hasNonX := x.split2Ways()
Expand Down Expand Up @@ -791,7 +820,12 @@ func (x IntRange) And(y IntRange) (z IntRange, ok bool) {
z.inPlaceUnite(andBothNonNeg(nonX, nonY))
}
}
return z, true
return z
}

// TryAnd returns (x.And(y), true).
func (x IntRange) TryAnd(y IntRange) (z IntRange, ok bool) {
return x.And(y), true
}

func andBothNonNeg(x IntRange, y IntRange) (z IntRange) {
Expand Down Expand Up @@ -855,15 +889,12 @@ func andOneNegOneNonNeg(neg IntRange, non IntRange) (z IntRange) {
}

// Or returns z = x | y.
//
// ok is false (and z will be IntRange{nil, nil}) if x or y contains at least
// one negative value. Otherwise, ok is true.
func (x IntRange) Or(y IntRange) (z IntRange, ok bool) {
func (x IntRange) Or(y IntRange) (z IntRange) {
if x.Empty() || y.Empty() {
return makeEmptyRange(), true
return makeEmptyRange()
}
if !x.ContainsNegative() && !y.ContainsNegative() {
return orBothNonNeg(x, y), true
return orBothNonNeg(x, y)
}

negX, nonX, hasNegX, hasNonX := x.split2Ways()
Expand Down Expand Up @@ -896,7 +927,12 @@ func (x IntRange) Or(y IntRange) (z IntRange, ok bool) {
z.inPlaceUnite(orBothNonNeg(nonX, nonY))
}
}
return z, true
return z
}

// TryOr returns (x.Or(y), true).
func (x IntRange) TryOr(y IntRange) (z IntRange, ok bool) {
return x.Or(y), true
}

func orBothNonNeg(x IntRange, y IntRange) (z IntRange) {
Expand Down
36 changes: 18 additions & 18 deletions lib/interval/interval_test.go
Expand Up @@ -441,16 +441,16 @@ func testOp1(s string) error {
}

var intOperators = map[rune]func(IntRange, IntRange) (IntRange, bool){
'∪': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Unite(y), true },
'∩': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Intersect(y), true },
'+': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Add(y), true },
'-': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Sub(y), true },
'*': func(x IntRange, y IntRange) (z IntRange, ok bool) { return x.Mul(y), true },
'/': IntRange.Quo,
'«': IntRange.Lsh,
'»': IntRange.Rsh,
'&': IntRange.And,
'|': IntRange.Or,
'∪': IntRange.TryUnite,
'∩': IntRange.TryIntersect,
'+': IntRange.TryAdd,
'-': IntRange.TrySub,
'*': IntRange.TryMul,
'/': IntRange.TryQuo,
'«': IntRange.TryLsh,
'»': IntRange.TryRsh,
'&': IntRange.TryAnd,
'|': IntRange.TryOr,
}

var intOperatorsKeys []rune
Expand Down Expand Up @@ -913,10 +913,10 @@ func TestOpAndWithMinusOne(tt *testing.T) {
x := IntRange{x0, x1}
want := x

if got, _ := x.And(minusOne); !got.Eq(want) {
if got := x.And(minusOne); !got.Eq(want) {
tt.Fatalf("%v & -1: got %v, want %v", x, got, want)
}
if got, _ := minusOne.And(x); !got.Eq(want) {
if got := minusOne.And(x); !got.Eq(want) {
tt.Fatalf("-1 & %v: got %v, want %v", x, got, want)
}
}
Expand All @@ -933,10 +933,10 @@ func TestOpAndWithZero(tt *testing.T) {
want = sharedEmptyRange
}

if got, _ := x.And(zero); !got.Eq(want) {
if got := x.And(zero); !got.Eq(want) {
tt.Fatalf("%v & +0: got %v, want %v", x, got, want)
}
if got, _ := zero.And(x); !got.Eq(want) {
if got := zero.And(x); !got.Eq(want) {
tt.Fatalf("+0 & %v: got %v, want %v", x, got, want)
}
}
Expand All @@ -953,10 +953,10 @@ func TestOpOrWithMinusOne(tt *testing.T) {
want = sharedEmptyRange
}

if got, _ := x.Or(minusOne); !got.Eq(want) {
if got := x.Or(minusOne); !got.Eq(want) {
tt.Fatalf("%v | -1: got %v, want %v", x, got, want)
}
if got, _ := minusOne.Or(x); !got.Eq(want) {
if got := minusOne.Or(x); !got.Eq(want) {
tt.Fatalf("-1 | %v: got %v, want %v", x, got, want)
}
}
Expand All @@ -970,10 +970,10 @@ func TestOpOrWithZero(tt *testing.T) {
x := IntRange{x0, x1}
want := x

if got, _ := x.Or(zero); !got.Eq(want) {
if got := x.Or(zero); !got.Eq(want) {
tt.Fatalf("%v | +0: got %v, want %v", x, got, want)
}
if got, _ := zero.Or(x); !got.Eq(want) {
if got := zero.Or(x); !got.Eq(want) {
tt.Fatalf("+0 | %v: got %v, want %v", x, got, want)
}
}
Expand Down

0 comments on commit 7ce58b6

Please sign in to comment.