blob: 17271957540b8b4c2e9796f01bdf587f39252087 [file] [log] [blame]
// 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)
}
}
}
}