| // Copyright 2018 The Wuffs Authors. |
| // |
| // 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 |
| // |
| // https://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. |
| |
| package interval |
| |
| import ( |
| "fmt" |
| "math/big" |
| "math/rand" |
| "sort" |
| "strconv" |
| "strings" |
| "testing" |
| "unicode/utf8" |
| ) |
| |
| var fromNeg7ToPos7 = []*big.Int{ |
| big.NewInt(-7), |
| big.NewInt(-6), |
| big.NewInt(-5), |
| big.NewInt(-4), |
| big.NewInt(-3), |
| big.NewInt(-2), |
| big.NewInt(-1), |
| big.NewInt(+0), |
| big.NewInt(+1), |
| big.NewInt(+2), |
| big.NewInt(+3), |
| big.NewInt(+4), |
| big.NewInt(+5), |
| big.NewInt(+6), |
| big.NewInt(+7), |
| nil, |
| } |
| |
| var fromNeg3ToPos3 = []*big.Int{ |
| big.NewInt(-3), |
| big.NewInt(-2), |
| big.NewInt(-1), |
| big.NewInt(+0), |
| big.NewInt(+1), |
| big.NewInt(+2), |
| big.NewInt(+3), |
| nil, |
| } |
| |
| // TestMotivatingExample tests the "motivating example" given in the package |
| // doc comment. |
| func TestMotivatingExample(tt *testing.T) { |
| i := IntRange{big.NewInt(0), big.NewInt(255)} |
| j := IntRange{big.NewInt(0), big.NewInt(3)} |
| four := IntRange{big.NewInt(4), big.NewInt(4)} |
| got := four.Mul(i).Add(j) |
| want := IntRange{big.NewInt(0), big.NewInt(1023)} |
| if !got.Eq(want) { |
| tt.Fatalf("got %v, want %v", got, want) |
| } |
| } |
| |
| func TestBigIntShifts(tt *testing.T) { |
| // These alternative implementations always take the fallback code path of |
| // bigIntLsh and bigIntRsh. This test checks that those fallback paths give |
| // the same results as *big.Int's Lsh and Rsh methods. There are fallback |
| // and non-fallback code paths in interval.go because those *big.Int |
| // methods aren't applicable if j's value doesn't fit in a uint. |
| |
| alternativeBigIntLsh := func(i *big.Int, j *big.Int) *big.Int { |
| // Fallback code path, copy-pasted from interval.go. |
| k := big.NewInt(2) |
| k.Exp(k, j, nil) |
| k.Mul(i, k) |
| return k |
| } |
| |
| alternativeBigIntRsh := func(i *big.Int, j *big.Int) *big.Int { |
| // Fallback code path, copy-pasted from interval.go. |
| k := big.NewInt(2) |
| k.Exp(k, j, nil) |
| k.Div(i, k) // This is explicitly Div, not Quo. |
| return k |
| } |
| |
| xs := []*big.Int{ |
| big.NewInt(-9), |
| big.NewInt(-8), |
| big.NewInt(-7), |
| |
| big.NewInt(-2), |
| big.NewInt(-1), |
| big.NewInt(+0), |
| big.NewInt(+1), |
| big.NewInt(+2), |
| |
| big.NewInt(+7), |
| big.NewInt(+8), |
| big.NewInt(+9), |
| } |
| |
| ys := []*big.Int{ |
| big.NewInt(+0), |
| big.NewInt(+1), |
| big.NewInt(+2), |
| big.NewInt(+3), |
| big.NewInt(+4), |
| } |
| |
| for _, x := range xs { |
| for _, y := range ys { |
| { |
| got := bigIntLsh(x, y) |
| want := alternativeBigIntLsh(x, y) |
| if got.Cmp(want) != 0 { |
| tt.Errorf("%v << %v: got %v, want %v", x, y, got, want) |
| } |
| } |
| |
| { |
| got := bigIntRsh(x, y) |
| want := alternativeBigIntRsh(x, y) |
| if got.Cmp(want) != 0 { |
| tt.Errorf("%v >> %v: got %v, want %v", x, y, got, want) |
| } |
| } |
| } |
| } |
| } |
| |
| func trimLeadingSpaces(s string) string { |
| for ; len(s) > 0 && s[0] == ' '; s = s[1:] { |
| } |
| return s |
| } |
| |
| func trimTrailingSpaces(s string) string { |
| for ; len(s) > 0 && s[len(s)-1] == ' '; s = s[:len(s)-1] { |
| } |
| return s |
| } |
| |
| func parseInt(s string) (x *big.Int, remaining string, err error) { |
| s = trimLeadingSpaces(s) |
| i := 0 |
| for ; i < len(s); i++ { |
| if c := s[i]; c == ')' || c == ',' || c == ']' { |
| break |
| } |
| } |
| s, remaining = trimTrailingSpaces(s[:i]), s[i:] |
| switch s { |
| case "-∞", "+∞": |
| // No-op. |
| default: |
| n, err := strconv.Atoi(s) |
| if err != nil { |
| return nil, "", fmt.Errorf("parseInt(%q): %v", s, err) |
| } |
| x = big.NewInt(int64(n)) |
| } |
| return x, remaining, nil |
| } |
| |
| // parseInterval parses a string like "[-3, +4] etcetera", returning the |
| // interval itself and the remaining string " etcetera". |
| // |
| // It also parses infinite (unbounded) intervals like "[0, +∞)", and the |
| // special syntax "[...empty..]" for an empty interval (one that contains no |
| // elements). |
| func parseInterval(s string) (x IntRange, remaining string, err error) { |
| const emptySyntax = "[...empty..]" |
| s = trimLeadingSpaces(s) |
| if strings.HasPrefix(s, emptySyntax) { |
| s = s[len(emptySyntax):] |
| return IntRange{big.NewInt(+1), big.NewInt(-1)}, s, nil |
| } |
| |
| if s[0] != '(' && s[0] != '[' { |
| return IntRange{}, "", fmt.Errorf("expected '(' or '['") |
| } |
| s = s[1:] |
| x0, s, err := parseInt(s) |
| if err != nil { |
| return IntRange{}, "", err |
| } |
| if s[0] != ',' { |
| return IntRange{}, "", fmt.Errorf("expected ','") |
| } |
| s = s[1:] |
| x1, s, err := parseInt(s) |
| if err != nil { |
| return IntRange{}, "", err |
| } |
| if s[0] != ')' && s[0] != ']' { |
| return IntRange{}, "", fmt.Errorf("expected ')' or ']'") |
| } |
| s = s[1:] |
| if x0 != nil && x1 != nil && x0.Cmp(x1) > 0 { |
| return IntRange{}, "", fmt.Errorf("invalid empty interval") |
| } |
| return IntRange{x0, x1}, s, nil |
| } |
| |
| func TestContainsEtc(tt *testing.T) { |
| testCases := []struct { |
| s string |
| cn uint32 |
| cz uint32 |
| cp uint32 |
| }{ |
| {"<empty-->", 0, 0, 0}, |
| {"<empty0->", 0, 0, 0}, |
| {"<empty+->", 0, 0, 0}, |
| {"<empty+0>", 0, 0, 0}, |
| {"<empty++>", 0, 0, 0}, |
| |
| {"(-∞, -1]", 1, 0, 0}, |
| {"(-∞, 0]", 1, 1, 0}, |
| {"(-∞, +1]", 1, 1, 1}, |
| {"(-∞, +∞)", 1, 1, 1}, |
| |
| {"[-1, -1]", 1, 0, 0}, |
| {"[-1, 0]", 1, 1, 0}, |
| {"[-1, +1]", 1, 1, 1}, |
| {"[-1, +∞)", 1, 1, 1}, |
| |
| {"[ 0, 0]", 0, 1, 0}, |
| {"[ 0, +1]", 0, 1, 1}, |
| {"[ 0, +∞)", 0, 1, 1}, |
| |
| {"[+1, +1]", 0, 0, 1}, |
| {"[+1, +∞)", 0, 0, 1}, |
| } |
| |
| // eqTestCases is appended to throughout the "range testCases" loop, but it |
| // always contains exactly one empty IntRange. |
| eqTestCases := []IntRange{ |
| makeEmptyRange(), |
| } |
| |
| for _, tc := range testCases { |
| x := IntRange{} |
| switch tc.s { |
| case "<empty-->": |
| x = IntRange{big.NewInt(-1), big.NewInt(-2)} |
| case "<empty0->": |
| x = IntRange{big.NewInt(00), big.NewInt(-2)} |
| case "<empty+->": |
| x = IntRange{big.NewInt(+2), big.NewInt(-2)} |
| case "<empty+0>": |
| x = IntRange{big.NewInt(+2), big.NewInt(00)} |
| case "<empty++>": |
| x = IntRange{big.NewInt(+2), big.NewInt(+1)} |
| default: |
| err := error(nil) |
| x, _, err = parseInterval(tc.s) |
| if err != nil { |
| tt.Errorf("%s: %v", tc.s, err) |
| continue |
| } |
| } |
| |
| if got, want := x.ContainsNegative(), tc.cn != 0; got != want { |
| tt.Errorf("%s.ContainsNegative(): got %t, want %t", tc.s, got, want) |
| } |
| if got, want := x.ContainsZero(), tc.cz != 0; got != want { |
| tt.Errorf("%s.ContainsZero(): got %t, want %t", tc.s, got, want) |
| } |
| if got, want := x.ContainsPositive(), tc.cp != 0; got != want { |
| tt.Errorf("%s.ContainsPositive(): got %t, want %t", tc.s, got, want) |
| } |
| |
| if got, want := x.Empty(), strings.Contains(tc.s, "empty"); got != want { |
| tt.Errorf("%s.Empty(): got %t, want %t", tc.s, got, want) |
| } else if !got { |
| eqTestCases = append(eqTestCases, x) |
| } else if !x.Eq(sharedEmptyRange) { |
| tt.Errorf("%v eq %v: got %t, want %t", x, sharedEmptyRange, got, want) |
| } |
| |
| if got, want := x.justZero(), tc.s == "[ 0, 0]"; got != want { |
| tt.Errorf("%s.justZero(): got %t, want %t", tc.s, got, want) |
| } |
| } |
| |
| for i, x := range eqTestCases { |
| for j, y := range eqTestCases { |
| got := x.Eq(y) |
| want := i == j |
| if got != want { |
| tt.Errorf("%v eq %v: got %t, want %t", x, y, got, want) |
| } |
| } |
| } |
| |
| for _, x := range eqTestCases { |
| neg, pos, hasNeg, hasZero, hasPos := x.split3Ways() |
| negEmpty, posEmpty := !hasNeg, !hasPos |
| if got, want := neg.Empty(), negEmpty; got != want { |
| tt.Errorf("%v: neg.Empty() == %t, negEmpty == %t", x, got, want) |
| } |
| if got, want := !x.ContainsNegative(), negEmpty; got != want { |
| tt.Errorf("%v: !x.ContainsNegative() == %t, negEmpty == %t", x, got, want) |
| } |
| if got, want := x.ContainsZero(), hasZero; got != want { |
| tt.Errorf("%v: x.ContainsZero() == %t, hasZero == %t", x, got, want) |
| } |
| if got, want := !x.ContainsPositive(), posEmpty; got != want { |
| tt.Errorf("%v: !x.ContainsPositive() == %t, posEmpty == %t", x, got, want) |
| } |
| if got, want := pos.Empty(), posEmpty; got != want { |
| tt.Errorf("%v: pos.Empty() == %t, posEmpty == %t", x, got, want) |
| } |
| } |
| } |
| |
| func shareBigIntPointers(x IntRange, y IntRange) bool { |
| return ((x[0] == y[0]) && (x[0] != nil)) || |
| ((x[0] == y[1]) && (x[0] != nil)) || |
| ((x[1] == y[0]) && (x[1] != nil)) || |
| ((x[1] == y[1]) && (x[1] != nil)) |
| } |
| |
| func testBruteForceAgrees(x IntRange, y IntRange, opKey rune) error { |
| brute, bruteOK := bruteForce(x, y, opKey) |
| got, gotOK := intOperators[opKey](x, y) |
| if shareBigIntPointers(got, x) || shareBigIntPointers(got, y) { |
| return fmt.Errorf("*big.Int pointers (pointing to mutable state) were re-used") |
| } |
| if !got.Eq(brute) || gotOK != bruteOK { |
| return fmt.Errorf("got %v, %t, brute force gave %v, %t", got, gotOK, brute, bruteOK) |
| } |
| return nil |
| } |
| |
| func TestBruteForceAgreesSystematically(tt *testing.T) { |
| for _, opKey := range intOperatorsKeys { |
| for _, x0 := range fromNeg7ToPos7 { |
| for _, x1 := range fromNeg7ToPos7 { |
| x := IntRange{x0, x1} |
| |
| for _, y0 := range fromNeg7ToPos7 { |
| for _, y1 := range fromNeg7ToPos7 { |
| y := IntRange{y0, y1} |
| |
| if err := testBruteForceAgrees(x, y, opKey); err != nil { |
| tt.Fatalf("%v %c %v: %v", x, opKey, y, err) |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| func TestBruteForceAgreesRandomly(tt *testing.T) { |
| gen := func(rng *rand.Rand) *big.Int { |
| r := rng.Intn(2*riRadius+2) - (riRadius + 1) |
| if r == -(riRadius + 1) { |
| return nil |
| } |
| return big.NewInt(int64(r)) |
| } |
| |
| rng := rand.New(rand.NewSource(0)) |
| for _, opKey := range intOperatorsKeys { |
| for i := 0; i < 10000; i++ { |
| x := IntRange{gen(rng), gen(rng)} |
| y := IntRange{gen(rng), gen(rng)} |
| if err := testBruteForceAgrees(x, y, opKey); err != nil { |
| tt.Fatalf("%v %c %v: %v", x, opKey, y, err) |
| } |
| } |
| } |
| } |
| |
| func testOp(tt *testing.T, testCases ...string) { |
| for _, tc := range testCases { |
| if err := testOp1(tc); err != nil { |
| tt.Errorf("%q: %v", tc, err) |
| } |
| } |
| } |
| |
| func testOp1(s string) error { |
| x, s, err := parseInterval(s) |
| if err != nil { |
| return err |
| } |
| s = trimLeadingSpaces(s) |
| |
| i := 0 |
| for ; i < len(s) && s[i] != ' '; i++ { |
| } |
| opKey, _ := utf8.DecodeRuneInString(s[:i]) |
| s = s[i:] |
| |
| y, s, err := parseInterval(s) |
| if err != nil { |
| return err |
| } |
| s = trimLeadingSpaces(s) |
| if !strings.HasPrefix(s, "==") { |
| return fmt.Errorf(`expected "=="`) |
| } |
| s = s[2:] |
| s = trimLeadingSpaces(s) |
| want, wantOK := IntRange{}, false |
| if s == "invalid" { |
| s = "" |
| } else { |
| wantOK = true |
| want, s, err = parseInterval(s) |
| if err != nil { |
| return err |
| } |
| s = trimLeadingSpaces(s) |
| if s != "" { |
| return fmt.Errorf("trailing specification %q", s) |
| } |
| } |
| |
| if err := testBruteForceAgrees(x, y, opKey); err != nil { |
| return err |
| } |
| |
| got, gotOK := intOperators[opKey](x, y) |
| if !got.Eq(want) || gotOK != wantOK { |
| return fmt.Errorf("got %v, %t, want %v, %t", got, gotOK, want, wantOK) |
| } |
| return nil |
| } |
| |
| var intOperators = map[rune]func(IntRange, IntRange) (IntRange, bool){ |
| '∪': IntRange.TryUnite, |
| '∩': IntRange.TryIntersect, |
| '+': IntRange.TryAdd, |
| '-': IntRange.TrySub, |
| '*': IntRange.TryMul, |
| '/': IntRange.TryQuo, |
| '«': IntRange.TryLsh, |
| '»': IntRange.TryRsh, |
| '&': IntRange.TryAnd, |
| '|': IntRange.TryOr, |
| } |
| |
| var intOperatorsKeys []rune |
| |
| func init() { |
| for k := range intOperators { |
| intOperatorsKeys = append(intOperatorsKeys, k) |
| } |
| sort.Slice(intOperatorsKeys, func(i, j int) bool { |
| return intOperatorsKeys[i] < intOperatorsKeys[j] |
| }) |
| } |
| |
| func TestOpUnite(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] ∪ [ -5, -5] == [ -5, 3]", |
| "[ 3, 3] ∪ [ 0, 0] == [ 0, 3]", |
| "[ 0, 0] ∪ [ -7, 7] == [ -7, 7]", |
| "[ 0, 2] ∪ [ 0, 5] == [ 0, 5]", |
| "[ 3, 6] ∪ [ 10, 15] == [ 3, 15]", |
| "[ 3, +∞) ∪ [ -4, -2] == [ -4, +∞)", |
| "[ 3, +∞) ∪ [ 10, 15] == [ 3, +∞)", |
| "[ 3, +∞) ∪ ( -∞, 15] == ( -∞, +∞)", |
| "[ 3, 6] ∪ ( -∞, 15] == ( -∞, 15]", |
| "[ 3, 6] ∪ ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) ∪ ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) ∪ [ 1, 2] == ( -∞, +∞)", |
| "( -∞, +∞) ∪ [ 0, 0] == ( -∞, +∞)", |
| "[ 3, 6] ∪ [...empty..] == [ 3, 6]", |
| "[...empty..] ∪ [ 10, 15] == [ 10, 15]", |
| "[...empty..] ∪ [...empty..] == [...empty..]", |
| "( -∞, +∞) ∪ [...empty..] == ( -∞, +∞)", |
| ) |
| } |
| |
| func TestOpIntersect(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] ∩ [ -5, -5] == [...empty..]", |
| "[ 3, 3] ∩ [ 0, 0] == [...empty..]", |
| "[ 0, 0] ∩ [ -7, 7] == [ 0, 0]", |
| "[ 0, 2] ∩ [ 0, 5] == [ 0, 2]", |
| "[ 3, 6] ∩ [ 10, 15] == [...empty..]", |
| "[ 3, +∞) ∩ [ -4, -2] == [...empty..]", |
| "[ 3, +∞) ∩ [ 10, 15] == [ 10, 15]", |
| "[ 3, +∞) ∩ ( -∞, 15] == [ 3, 15]", |
| "[ 3, 6] ∩ ( -∞, 15] == [ 3, 6]", |
| "[ 3, 6] ∩ ( -∞, +∞) == [ 3, 6]", |
| "( -∞, +∞) ∩ ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) ∩ [ 1, 2] == [ 1, 2]", |
| "( -∞, +∞) ∩ [ 0, 0] == [ 0, 0]", |
| "[ 3, 6] ∩ [...empty..] == [...empty..]", |
| "[...empty..] ∩ [ 10, 15] == [...empty..]", |
| "[...empty..] ∩ [...empty..] == [...empty..]", |
| "( -∞, +∞) ∩ [...empty..] == [...empty..]", |
| ) |
| } |
| |
| func TestOpAdd(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] + [ -5, -5] == [ -2, -2]", |
| "[ 3, 3] + [ 0, 0] == [ 3, 3]", |
| "[ 0, 0] + [ -7, 7] == [ -7, 7]", |
| "[ 0, 2] + [ 0, 5] == [ 0, 7]", |
| "[ 3, 6] + [ 10, 15] == [ 13, 21]", |
| "[ 3, +∞) + [ -4, -2] == [ -1, +∞)", |
| "[ 3, +∞) + [ 10, 15] == [ 13, +∞)", |
| "[ 3, +∞) + ( -∞, 15] == ( -∞, +∞)", |
| "[ 3, 6] + ( -∞, 15] == ( -∞, 21]", |
| "[ 3, 6] + ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) + ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) + [ 1, 2] == ( -∞, +∞)", |
| "( -∞, +∞) + [ 0, 0] == ( -∞, +∞)", |
| "[ 3, 6] + [...empty..] == [...empty..]", |
| "[...empty..] + [ 10, 15] == [...empty..]", |
| "[...empty..] + [...empty..] == [...empty..]", |
| "( -∞, +∞) + [...empty..] == [...empty..]", |
| ) |
| } |
| |
| func TestOpSub(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] - [ -5, -5] == [ 8, 8]", |
| "[ 3, 3] - [ 0, 0] == [ 3, 3]", |
| "[ 0, 0] - [ -7, 7] == [ -7, 7]", |
| "[ 0, 2] - [ 0, 5] == [ -5, 2]", |
| "[ 3, 6] - [ 10, 15] == [ -12, -4]", |
| "[ 3, +∞) - [ -4, -2] == [ 5, +∞)", |
| "[ 3, +∞) - [ 10, 15] == [ -12, +∞)", |
| "[ 3, +∞) - ( -∞, 15] == ( -∞, +∞)", |
| "[ 3, 6] - ( -∞, 15] == [ -12, +∞)", |
| "[ 3, 6] - ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) - ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) - [ 1, 2] == ( -∞, +∞)", |
| "( -∞, +∞) - [ 0, 0] == ( -∞, +∞)", |
| "[ 3, 6] - [...empty..] == [...empty..]", |
| "[...empty..] - [ 10, 15] == [...empty..]", |
| "[...empty..] - [...empty..] == [...empty..]", |
| "( -∞, +∞) - [...empty..] == [...empty..]", |
| ) |
| } |
| |
| func TestOpMul(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] * [ -5, -5] == [ -15, -15]", |
| "[ 3, 3] * [ 0, 0] == [ 0, 0]", |
| "[ 0, 0] * [ -7, 7] == [ 0, 0]", |
| "[ 0, 2] * [ 0, 5] == [ 0, 10]", |
| "[ 3, 6] * [ 10, 15] == [ 30, 90]", |
| "[ 3, +∞) * [ -4, -2] == ( -∞, -6]", |
| "[ 3, +∞) * [ 10, 15] == [ 30, +∞)", |
| "[ 3, +∞) * ( -∞, 15] == ( -∞, +∞)", |
| "[ 3, 6] * ( -∞, 15] == ( -∞, 90]", |
| "[ 3, 6] * ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) * ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) * [ 1, 2] == ( -∞, +∞)", |
| "( -∞, +∞) * [ 0, 0] == [ 0, 0]", |
| "[ 3, 6] * [...empty..] == [...empty..]", |
| "[...empty..] * [ 10, 15] == [...empty..]", |
| "[...empty..] * [...empty..] == [...empty..]", |
| "( -∞, +∞) * [...empty..] == [...empty..]", |
| |
| "[ -3, -1] * [ -11, -10] == [ 10, 33]", |
| "[ -3, 0] * [ -11, -10] == [ 0, 33]", |
| "[ -3, 1] * [ -11, -10] == [ -11, 33]", |
| "[ -3, 4] * [ -11, -10] == [ -44, 33]", |
| "[ -1, 4] * [ -11, -10] == [ -44, 11]", |
| "[ 0, 4] * [ -11, -10] == [ -44, 0]", |
| "[ 1, 4] * [ -11, -10] == [ -44, -10]", |
| |
| "[ -3, -1] * [ -6, 2] == [ -6, 18]", |
| "[ -3, 0] * [ -6, 2] == [ -6, 18]", |
| "[ -3, 1] * [ -6, 2] == [ -6, 18]", |
| "[ -3, 4] * [ -6, 2] == [ -24, 18]", |
| "[ -1, 4] * [ -6, 2] == [ -24, 8]", |
| "[ 0, 4] * [ -6, 2] == [ -24, 8]", |
| "[ 1, 4] * [ -6, 2] == [ -24, 8]", |
| |
| "[ -3, -1] * [ 0, 3] == [ -9, 0]", |
| "[ -3, 0] * [ 0, 3] == [ -9, 0]", |
| "[ -3, 1] * [ 0, 3] == [ -9, 3]", |
| "[ -3, 4] * [ 0, 3] == [ -9, 12]", |
| "[ -1, 4] * [ 0, 3] == [ -3, 12]", |
| "[ 0, 4] * [ 0, 3] == [ 0, 12]", |
| "[ 1, 4] * [ 0, 3] == [ 0, 12]", |
| |
| "[ -3, -1] * [ 2, 3] == [ -9, -2]", |
| "[ -3, 0] * [ 2, 3] == [ -9, 0]", |
| "[ -3, 1] * [ 2, 3] == [ -9, 3]", |
| "[ -3, 4] * [ 2, 3] == [ -9, 12]", |
| "[ -1, 4] * [ 2, 3] == [ -3, 12]", |
| "[ 0, 4] * [ 2, 3] == [ 0, 12]", |
| "[ 1, 4] * [ 2, 3] == [ 2, 12]", |
| |
| "[ -9, +∞) * [ 2, +∞) == ( -∞, +∞)", |
| "[ -1, +∞) * [ 2, +∞) == ( -∞, +∞)", |
| "[ 0, +∞) * [ 2, +∞) == [ 0, +∞)", |
| "[ 1, +∞) * [ 2, +∞) == [ 2, +∞)", |
| "[ 7, +∞) * [ 2, +∞) == [ 14, +∞)", |
| "[ -1, 1] * ( -∞, +∞) == ( -∞, +∞)", |
| "[ 0, 0] * ( -∞, +∞) == [ 0, 0]", |
| "[ 1, 1] * ( -∞, +∞) == ( -∞, +∞)", |
| ) |
| } |
| |
| func TestOpQuo(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] / [ -5, -5] == [ 0, 0]", |
| "[ 3, 3] / [ 0, 0] == invalid", |
| "[ 0, 0] / [ -7, 7] == invalid", |
| "[ 0, 2] / [ 0, 5] == invalid", |
| "[ 3, 6] / [ 10, 15] == [ 0, 0]", |
| "[ 3, +∞) / [ -4, -2] == ( -∞, 0]", |
| "[ 3, +∞) / [ 10, 15] == [ 0, +∞)", |
| "[ 3, +∞) / ( -∞, 15] == invalid", |
| "[ 3, 6] / ( -∞, 15] == invalid", |
| "[ 3, 6] / ( -∞, +∞) == invalid", |
| "( -∞, +∞) / ( -∞, +∞) == invalid", |
| "( -∞, +∞) / [ 1, 2] == ( -∞, +∞)", |
| "( -∞, +∞) / [ 0, 0] == invalid", |
| "[ 3, 6] / [...empty..] == [...empty..]", |
| "[...empty..] / [ 10, 15] == [...empty..]", |
| "[...empty..] / [...empty..] == [...empty..]", |
| "( -∞, +∞) / [...empty..] == [...empty..]", |
| |
| "[ 1, 4] / [ -11, -10] == [ 0, 0]", |
| |
| "[ 1, 4] / [ -6, 2] == invalid", |
| |
| "[ -3, -1] / [ 1, 3] == [ -3, 0]", |
| "[ -3, 0] / [ 1, 3] == [ -3, 0]", |
| "[ -3, 1] / [ 1, 3] == [ -3, 1]", |
| "[ -3, 4] / [ 1, 3] == [ -3, 4]", |
| "[ -1, 4] / [ 1, 3] == [ -1, 4]", |
| "[ 0, 4] / [ 1, 3] == [ 0, 4]", |
| "[ 1, 4] / [ 1, 3] == [ 0, 4]", |
| |
| "[ -3, -1] / [ 2, 3] == [ -1, 0]", |
| "[ -3, 0] / [ 2, 3] == [ -1, 0]", |
| "[ -3, 1] / [ 2, 3] == [ -1, 0]", |
| "[ -3, 4] / [ 2, 3] == [ -1, 2]", |
| "[ -1, 4] / [ 2, 3] == [ 0, 2]", |
| "[ 0, 4] / [ 2, 3] == [ 0, 2]", |
| "[ 1, 4] / [ 2, 3] == [ 0, 2]", |
| |
| "[ -9, +∞) / [ 2, +∞) == [ -4, +∞)", |
| "[ -1, +∞) / [ 2, +∞) == [ 0, +∞)", |
| "[ 0, +∞) / [ 2, +∞) == [ 0, +∞)", |
| "[ 1, +∞) / [ 2, +∞) == [ 0, +∞)", |
| "[ 7, +∞) / [ 2, +∞) == [ 0, +∞)", |
| "[ -1, 1] / ( -∞, +∞) == invalid", |
| "[ 0, 0] / ( -∞, +∞) == invalid", |
| "[ 1, 1] / ( -∞, +∞) == invalid", |
| ) |
| } |
| |
| func TestOpLsh(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] « [ -5, -5] == invalid", |
| "[ 3, 3] « [ 0, 0] == [ 3, 3]", |
| "[ 0, 0] « [ -7, 7] == invalid", |
| "[ 0, 2] « [ 0, 5] == [ 0, 64]", |
| "[ 3, 6] « [ 10, 15] == [3072, 196608]", |
| "[ 3, +∞) « [ -4, -2] == invalid", |
| "[ 3, +∞) « [ 10, 15] == [3072, +∞)", |
| "[ 3, +∞) « ( -∞, 15] == invalid", |
| "[ 3, 6] « ( -∞, 15] == invalid", |
| "[ 3, 6] « ( -∞, +∞) == invalid", |
| "( -∞, +∞) « ( -∞, +∞) == invalid", |
| "( -∞, +∞) « [ 1, 2] == ( -∞, +∞)", |
| "( -∞, +∞) « [ 0, 0] == ( -∞, +∞)", |
| "[ 3, 6] « [...empty..] == [...empty..]", |
| "[...empty..] « [ 10, 15] == [...empty..]", |
| "[...empty..] « [...empty..] == [...empty..]", |
| "( -∞, +∞) « [...empty..] == [...empty..]", |
| |
| "[ 1, 4] « [ -11, -10] == invalid", |
| |
| "[ 1, 4] « [ -6, 2] == invalid", |
| |
| "[ -3, -1] « [ 0, 3] == [ -24, -1]", |
| "[ -3, 0] « [ 0, 3] == [ -24, 0]", |
| "[ -3, 1] « [ 0, 3] == [ -24, 8]", |
| "[ -3, 4] « [ 0, 3] == [ -24, 32]", |
| "[ -1, 4] « [ 0, 3] == [ -8, 32]", |
| "[ 0, 4] « [ 0, 3] == [ 0, 32]", |
| "[ 1, 4] « [ 0, 3] == [ 1, 32]", |
| |
| "[ -3, -1] « [ 2, 3] == [ -24, -4]", |
| "[ -3, 0] « [ 2, 3] == [ -24, 0]", |
| "[ -3, 1] « [ 2, 3] == [ -24, 8]", |
| "[ -3, 4] « [ 2, 3] == [ -24, 32]", |
| "[ -1, 4] « [ 2, 3] == [ -8, 32]", |
| "[ 0, 4] « [ 2, 3] == [ 0, 32]", |
| "[ 1, 4] « [ 2, 3] == [ 4, 32]", |
| |
| "[ -9, +∞) « [ 2, +∞) == ( -∞, +∞)", |
| "[ -1, +∞) « [ 2, +∞) == ( -∞, +∞)", |
| "[ 0, +∞) « [ 2, +∞) == [ 0, +∞)", |
| "[ 1, +∞) « [ 2, +∞) == [ 4, +∞)", |
| "[ 7, +∞) « [ 2, +∞) == [ 28, +∞)", |
| "[ -1, 1] « ( -∞, +∞) == invalid", |
| "[ 0, 0] « ( -∞, +∞) == invalid", |
| "[ 1, 1] « ( -∞, +∞) == invalid", |
| ) |
| } |
| |
| func TestOpRsh(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] » [ -5, -5] == invalid", |
| "[ 3, 3] » [ 0, 0] == [ 3, 3]", |
| "[ 0, 0] » [ -7, 7] == invalid", |
| "[ 0, 2] » [ 0, 5] == [ 0, 2]", |
| "[ 3, 6] » [ 10, 15] == [ 0, 0]", |
| "[ 3, +∞) » [ -4, -2] == invalid", |
| "[ 3, +∞) » [ 10, 15] == [ 0, +∞)", |
| "[ 3, +∞) » ( -∞, 15] == invalid", |
| "[ 3, 6] » ( -∞, 15] == invalid", |
| "[ 3, 6] » ( -∞, +∞) == invalid", |
| "( -∞, +∞) » ( -∞, +∞) == invalid", |
| "( -∞, +∞) » [ 1, 2] == ( -∞, +∞)", |
| "( -∞, +∞) » [ 0, 0] == ( -∞, +∞)", |
| "[ 3, 6] » [...empty..] == [...empty..]", |
| "[...empty..] » [ 10, 15] == [...empty..]", |
| "[...empty..] » [...empty..] == [...empty..]", |
| "( -∞, +∞) » [...empty..] == [...empty..]", |
| |
| "[ 1, 4] » [ -11, -10] == invalid", |
| |
| "[ 1, 4] » [ -6, 2] == invalid", |
| |
| "[ -3, -1] » [ 0, 3] == [ -3, -1]", |
| "[ -3, 0] » [ 0, 3] == [ -3, 0]", |
| "[ -3, 1] » [ 0, 3] == [ -3, 1]", |
| "[ -3, 4] » [ 0, 3] == [ -3, 4]", |
| "[ -1, 4] » [ 0, 3] == [ -1, 4]", |
| "[ 0, 4] » [ 0, 3] == [ 0, 4]", |
| "[ 1, 4] » [ 0, 3] == [ 0, 4]", |
| |
| "[ -3, -1] » [ 1, 3] == [ -2, -1]", |
| "[ -3, 0] » [ 1, 3] == [ -2, 0]", |
| "[ -3, 1] » [ 1, 3] == [ -2, 0]", |
| "[ -3, 4] » [ 1, 3] == [ -2, 2]", |
| "[ -1, 4] » [ 1, 3] == [ -1, 2]", |
| "[ 0, 4] » [ 1, 3] == [ 0, 2]", |
| "[ 1, 4] » [ 1, 3] == [ 0, 2]", |
| |
| "[ -9, +∞) » [ 2, +∞) == [ -3, +∞)", |
| "[ -1, +∞) » [ 2, +∞) == [ -1, +∞)", |
| "[ 0, +∞) » [ 2, +∞) == [ 0, +∞)", |
| "[ 1, +∞) » [ 2, +∞) == [ 0, +∞)", |
| "[ 7, +∞) » [ 2, +∞) == [ 0, +∞)", |
| "[ -1, 1] » ( -∞, +∞) == invalid", |
| "[ 0, 0] » ( -∞, +∞) == invalid", |
| "[ 1, 1] » ( -∞, +∞) == invalid", |
| ) |
| } |
| |
| func TestOpAnd(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] & [ -5, -5] == [ 3, 3]", |
| "[ 3, 3] & [ 0, 0] == [ 0, 0]", |
| "[ 0, 0] & [ -7, 7] == [ 0, 0]", |
| "[ 0, 2] & [ 0, 5] == [ 0, 2]", |
| "[ 3, 6] & [ 10, 15] == [ 0, 6]", |
| "[ 3, +∞) & [ -4, -2] == [ 0, +∞)", |
| "[ 3, +∞) & [ 10, 15] == [ 0, 15]", |
| "[ 3, +∞) & ( -∞, 15] == [ 0, +∞)", |
| "[ 3, 6] & ( -∞, 15] == [ 0, 6]", |
| "[ 3, 6] & ( -∞, +∞) == [ 0, 6]", |
| "( -∞, +∞) & ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) & [ 1, 2] == [ 0, 2]", |
| "( -∞, +∞) & [ 0, 0] == [ 0, 0]", |
| "[ 3, 6] & [...empty..] == [...empty..]", |
| "[...empty..] & [ 10, 15] == [...empty..]", |
| "[...empty..] & [...empty..] == [...empty..]", |
| "( -∞, +∞) & [...empty..] == [...empty..]", |
| |
| "[ 1, 4] & [ -11, -10] == [ 0, 4]", |
| |
| "[ 1, 4] & [ -6, 2] == [ 0, 4]", |
| |
| "[ -3, -1] & [ 0, 3] == [ 0, 3]", |
| "[ -1, 4] & [ 0, 3] == [ 0, 3]", |
| "[ 0, 4] & [ 0, 3] == [ 0, 3]", |
| "[ 1, 4] & [ 0, 3] == [ 0, 3]", |
| |
| "[ -3, -1] & [ 2, 3] == [ 0, 3]", |
| "[ -1, 4] & [ 2, 3] == [ 0, 3]", |
| "[ 0, 4] & [ 2, 3] == [ 0, 3]", |
| "[ 1, 4] & [ 2, 3] == [ 0, 3]", |
| |
| "[ -9, +∞) & [ 2, +∞) == [ 0, +∞)", |
| "[ -1, +∞) & [ 2, +∞) == [ 0, +∞)", |
| "[ 0, +∞) & [ 2, +∞) == [ 0, +∞)", |
| "[ 1, +∞) & [ 2, +∞) == [ 0, +∞)", |
| "[ 7, +∞) & [ 2, +∞) == [ 0, +∞)", |
| "[ -1, 1] & ( -∞, +∞) == ( -∞, +∞)", |
| "[ 0, 0] & ( -∞, +∞) == [ 0, 0]", |
| "[ 1, 1] & ( -∞, +∞) == [ 0, 1]", |
| |
| "[ 1, 3] & [ 4, 9] == [ 0, 3]", |
| "[ 3, 4] & [ 5, 6] == [ 1, 4]", |
| "[ 4, 5] & [ 6, 7] == [ 4, 5]", |
| "[ 7, 7] & [ 12, 14] == [ 4, 6]", |
| |
| "[ 5, 6] & [ 6, 7] == [ 4, 6]", |
| "[ 5, 6] & [ 6, 8] == [ 0, 6]", |
| "[ 5, 9] & [ 6, 8] == [ 0, 8]", |
| "[ 5, 9] & [ 6, 9] == [ 0, 9]", |
| |
| "[ 5, 6] & [ 3, +∞) == [ 0, 6]", |
| "[ 5, 9] & [ 3, +∞) == [ 0, 9]", |
| "[ 5, 6] & [ 7, +∞) == [ 0, 6]", |
| "[ 5, 9] & [ 7, +∞) == [ 0, 9]", |
| "[ 5, 6] & [ 8, +∞) == [ 0, 6]", |
| "[ 5, 9] & [ 8, +∞) == [ 0, 9]", |
| "[ 5, 6] & [ 9, +∞) == [ 0, 6]", |
| "[ 5, 9] & [ 9, +∞) == [ 0, 9]", |
| "[ 5, 6] & [ 12, +∞) == [ 0, 6]", |
| "[ 5, 9] & [ 12, +∞) == [ 0, 9]", |
| |
| "( -∞, -3] & [ -3, -2] == ( -∞, -3]", |
| "[ 2, +∞) & [ 1, 2] == [ 0, 2]", |
| ) |
| } |
| |
| func TestOpOr(tt *testing.T) { |
| testOp(tt, |
| "[ 3, 3] | [ -5, -5] == [ -5, -5]", |
| "[ 3, 3] | [ 0, 0] == [ 3, 3]", |
| "[ 0, 0] | [ -7, 7] == [ -7, 7]", |
| "[ 0, 2] | [ 0, 5] == [ 0, 7]", |
| "[ 3, 6] | [ 10, 15] == [ 11, 15]", |
| "[ 3, +∞) | [ -4, -2] == [ -4, -1]", |
| "[ 3, +∞) | [ 10, 15] == [ 10, +∞)", |
| "[ 3, +∞) | ( -∞, 15] == ( -∞, +∞)", |
| "[ 3, 6] | ( -∞, 15] == ( -∞, 15]", |
| "[ 3, 6] | ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) | ( -∞, +∞) == ( -∞, +∞)", |
| "( -∞, +∞) | [ 1, 2] == ( -∞, +∞)", |
| "( -∞, +∞) | [ 0, 0] == ( -∞, +∞)", |
| "[ 3, 6] | [...empty..] == [...empty..]", |
| "[...empty..] | [ 10, 15] == [...empty..]", |
| "[...empty..] | [...empty..] == [...empty..]", |
| "( -∞, +∞) | [...empty..] == [...empty..]", |
| |
| "[ 1, 4] | [ -11, -10] == [ -11, -9]", |
| |
| "[ 1, 4] | [ -6, 2] == [ -6, 6]", |
| |
| "[ -3, -1] | [ 0, 3] == [ -3, -1]", |
| "[ -1, 4] | [ 0, 3] == [ -1, 7]", |
| "[ 0, 4] | [ 0, 3] == [ 0, 7]", |
| "[ 1, 4] | [ 0, 3] == [ 1, 7]", |
| |
| "[ -3, -1] | [ 2, 3] == [ -2, -1]", |
| "[ -1, 4] | [ 2, 3] == [ -1, 7]", |
| "[ 0, 4] | [ 2, 3] == [ 2, 7]", |
| "[ 1, 4] | [ 2, 3] == [ 2, 7]", |
| |
| "[ -9, +∞) | [ 2, +∞) == [ -9, +∞)", |
| "[ -1, +∞) | [ 2, +∞) == [ -1, +∞)", |
| "[ 0, +∞) | [ 2, +∞) == [ 2, +∞)", |
| "[ 1, +∞) | [ 2, +∞) == [ 2, +∞)", |
| "[ 7, +∞) | [ 2, +∞) == [ 7, +∞)", |
| "[ -1, 1] | ( -∞, +∞) == ( -∞, +∞)", |
| "[ 0, 0] | ( -∞, +∞) == ( -∞, +∞)", |
| "[ 1, 1] | ( -∞, +∞) == ( -∞, +∞)", |
| |
| "[ 1, 3] | [ 4, 9] == [ 5, 11]", |
| "[ 3, 4] | [ 5, 6] == [ 5, 7]", |
| "[ 4, 5] | [ 6, 7] == [ 6, 7]", |
| "[ 7, 7] | [ 12, 14] == [ 15, 15]", |
| |
| "[ 5, 6] | [ 6, 7] == [ 6, 7]", |
| "[ 5, 6] | [ 6, 8] == [ 6, 14]", |
| "[ 5, 9] | [ 6, 8] == [ 6, 15]", |
| "[ 5, 9] | [ 6, 9] == [ 6, 15]", |
| |
| "[ 5, 6] | [ 3, +∞) == [ 5, +∞)", |
| "[ 5, 9] | [ 3, +∞) == [ 5, +∞)", |
| "[ 5, 6] | [ 7, +∞) == [ 7, +∞)", |
| "[ 5, 9] | [ 7, +∞) == [ 7, +∞)", |
| "[ 5, 6] | [ 8, +∞) == [ 13, +∞)", |
| "[ 5, 9] | [ 8, +∞) == [ 8, +∞)", |
| "[ 5, 6] | [ 9, +∞) == [ 13, +∞)", |
| "[ 5, 9] | [ 9, +∞) == [ 9, +∞)", |
| "[ 5, 6] | [ 12, +∞) == [ 13, +∞)", |
| "[ 5, 9] | [ 12, +∞) == [ 12, +∞)", |
| |
| "( -∞, -3] | [ -3, -2] == [ -3, -1]", |
| "[ 2, +∞) | [ 1, 2] == [ 2, +∞)", |
| ) |
| } |
| |
| func TestOpAndWithMinusOne(tt *testing.T) { |
| minusOne := IntRange{big.NewInt(-1), big.NewInt(-1)} |
| for _, x0 := range fromNeg3ToPos3 { |
| for _, x1 := range fromNeg3ToPos3 { |
| x := IntRange{x0, x1} |
| want := x |
| |
| 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) { |
| tt.Fatalf("-1 & %v: got %v, want %v", x, got, want) |
| } |
| } |
| } |
| } |
| |
| func TestOpAndWithZero(tt *testing.T) { |
| zero := IntRange{big.NewInt(+0), big.NewInt(+0)} |
| for _, x0 := range fromNeg3ToPos3 { |
| for _, x1 := range fromNeg3ToPos3 { |
| x := IntRange{x0, x1} |
| want := zero |
| if x.Empty() { |
| want = sharedEmptyRange |
| } |
| |
| 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) { |
| tt.Fatalf("+0 & %v: got %v, want %v", x, got, want) |
| } |
| } |
| } |
| } |
| |
| func TestOpOrWithMinusOne(tt *testing.T) { |
| minusOne := IntRange{big.NewInt(-1), big.NewInt(-1)} |
| for _, x0 := range fromNeg3ToPos3 { |
| for _, x1 := range fromNeg3ToPos3 { |
| x := IntRange{x0, x1} |
| want := minusOne |
| if x.Empty() { |
| want = sharedEmptyRange |
| } |
| |
| 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) { |
| tt.Fatalf("-1 | %v: got %v, want %v", x, got, want) |
| } |
| } |
| } |
| } |
| |
| func TestOpOrWithZero(tt *testing.T) { |
| zero := IntRange{big.NewInt(+0), big.NewInt(+0)} |
| for _, x0 := range fromNeg3ToPos3 { |
| for _, x1 := range fromNeg3ToPos3 { |
| x := IntRange{x0, x1} |
| want := x |
| |
| 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) { |
| tt.Fatalf("+0 | %v: got %v, want %v", x, got, want) |
| } |
| } |
| } |
| } |