github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/fast_int_set_test.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package util
    12  
    13  import (
    14  	"fmt"
    15  	"reflect"
    16  	"testing"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/util/randutil"
    19  )
    20  
    21  func TestFastIntSet(t *testing.T) {
    22  	for _, mVal := range []int{1, 8, 30, smallCutoff, 2 * smallCutoff, 4 * smallCutoff} {
    23  		m := mVal
    24  		t.Run(fmt.Sprintf("%d", m), func(t *testing.T) {
    25  			t.Parallel() // SAFE FOR TESTING (this comment is for the linter)
    26  			rng, _ := randutil.NewPseudoRand()
    27  			in := make([]bool, m)
    28  			forEachRes := make([]bool, m)
    29  
    30  			var s FastIntSet
    31  			for i := 0; i < 1000; i++ {
    32  				v := rng.Intn(m)
    33  				if rng.Intn(2) == 0 {
    34  					in[v] = true
    35  					s.Add(v)
    36  				} else {
    37  					in[v] = false
    38  					s.Remove(v)
    39  				}
    40  				empty := true
    41  				for j := 0; j < m; j++ {
    42  					empty = empty && !in[j]
    43  					if in[j] != s.Contains(j) {
    44  						t.Fatalf("incorrect result for Contains(%d), expected %t", j, in[j])
    45  					}
    46  				}
    47  				if empty != s.Empty() {
    48  					t.Fatalf("incorrect result for Empty(), expected %t", empty)
    49  				}
    50  				// Test ForEach
    51  				for j := range forEachRes {
    52  					forEachRes[j] = false
    53  				}
    54  				s.ForEach(func(j int) {
    55  					forEachRes[j] = true
    56  				})
    57  				for j := 0; j < m; j++ {
    58  					if in[j] != forEachRes[j] {
    59  						t.Fatalf("incorrect ForEachResult for %d (%t, expected %t)", j, forEachRes[j], in[j])
    60  					}
    61  				}
    62  				// Cross-check Ordered and Next().
    63  				var vals []int
    64  				for i, ok := s.Next(0); ok; i, ok = s.Next(i + 1) {
    65  					vals = append(vals, i)
    66  				}
    67  				if o := s.Ordered(); !reflect.DeepEqual(vals, o) {
    68  					t.Fatalf("set built with Next doesn't match Ordered: %v vs %v", vals, o)
    69  				}
    70  				assertSame := func(orig, copy FastIntSet) {
    71  					if !orig.Equals(copy) || !copy.Equals(orig) {
    72  						t.Fatalf("expected equality: %v, %v", orig, copy)
    73  					}
    74  					if col, ok := copy.Next(0); ok {
    75  						copy.Remove(col)
    76  						if orig.Equals(copy) || copy.Equals(orig) {
    77  							t.Fatalf("unexpected equality: %v, %v", orig, copy)
    78  						}
    79  						copy.Add(col)
    80  						if !orig.Equals(copy) || !copy.Equals(orig) {
    81  							t.Fatalf("expected equality: %v, %v", orig, copy)
    82  						}
    83  					}
    84  				}
    85  				// Test Copy.
    86  				s2 := s.Copy()
    87  				assertSame(s, s2)
    88  				// Test CopyFrom.
    89  				var s3 FastIntSet
    90  				s3.CopyFrom(s)
    91  				assertSame(s, s3)
    92  				// Make sure CopyFrom into a non-empty set still works.
    93  				s.Shift(100)
    94  				s.CopyFrom(s3)
    95  				assertSame(s, s3)
    96  			}
    97  		})
    98  	}
    99  }
   100  
   101  func TestFastIntSetTwoSetOps(t *testing.T) {
   102  	rng, _ := randutil.NewPseudoRand()
   103  	// genSet creates a set of numElem values in [minVal, minVal + valRange)
   104  	// It also adds and then removes numRemoved elements.
   105  	genSet := func(numElem, numRemoved, minVal, valRange int) (FastIntSet, map[int]bool) {
   106  		var s FastIntSet
   107  		vals := rng.Perm(valRange)[:numElem+numRemoved]
   108  		used := make(map[int]bool, len(vals))
   109  		for _, i := range vals {
   110  			used[i] = true
   111  		}
   112  		for k := range used {
   113  			s.Add(k)
   114  		}
   115  		p := rng.Perm(len(vals))
   116  		for i := 0; i < numRemoved; i++ {
   117  			k := vals[p[i]]
   118  			s.Remove(k)
   119  			delete(used, k)
   120  		}
   121  		return s, used
   122  	}
   123  
   124  	// returns true if a is a subset of b
   125  	subset := func(a, b map[int]bool) bool {
   126  		for k := range a {
   127  			if !b[k] {
   128  				return false
   129  			}
   130  		}
   131  		return true
   132  	}
   133  
   134  	for _, minVal := range []int{-10, -1, 0, smallCutoff, 2 * smallCutoff} {
   135  		for _, valRange := range []int{0, 20, 200} {
   136  			for _, num1 := range []int{0, 1, 5, 10, 20} {
   137  				for _, removed1 := range []int{0, 1, 3, 8} {
   138  					s1, m1 := genSet(num1, removed1, minVal, num1+removed1+valRange)
   139  					for _, shift := range []int{-100, -10, -1, 1, 2, 10, 100} {
   140  						shifted := s1.Shift(shift)
   141  						failed := false
   142  						s1.ForEach(func(i int) {
   143  							failed = failed || !shifted.Contains(i+shift)
   144  						})
   145  						shifted.ForEach(func(i int) {
   146  							failed = failed || !s1.Contains(i-shift)
   147  						})
   148  						if failed {
   149  							t.Errorf("invalid shifted result: %s shifted by %d: %s", &s1, shift, &shifted)
   150  						}
   151  					}
   152  					for _, num2 := range []int{0, 1, 5, 10, 20} {
   153  						for _, removed2 := range []int{0, 1, 4, 10} {
   154  							s2, m2 := genSet(num2, removed2, minVal, num2+removed2+valRange)
   155  
   156  							subset1 := subset(m1, m2)
   157  							if subset1 != s1.SubsetOf(s2) {
   158  								t.Errorf("SubsetOf result incorrect: %s, %s", &s1, &s2)
   159  							}
   160  							subset2 := subset(m2, m1)
   161  							if subset2 != s2.SubsetOf(s1) {
   162  								t.Errorf("SubsetOf result incorrect: %s, %s", &s2, &s1)
   163  							}
   164  							eq := subset1 && subset2
   165  							if eq != s1.Equals(s2) || eq != s2.Equals(s1) {
   166  								t.Errorf("Equals result incorrect: %s, %s", &s1, &s2)
   167  							}
   168  
   169  							// Test union.
   170  
   171  							u := s1.Copy()
   172  							u.UnionWith(s2)
   173  
   174  							if !u.Equals(s1.Union(s2)) {
   175  								t.Errorf("inconsistency between UnionWith and Union on %s %s\n", s1, s2)
   176  							}
   177  							// Verify all elements from m1 and m2 are in u.
   178  							for _, m := range []map[int]bool{m1, m2} {
   179  								for x := range m {
   180  									if !u.Contains(x) {
   181  										t.Errorf("incorrect union result %s union %s = %s", &s1, &s2, &u)
   182  										break
   183  									}
   184  								}
   185  							}
   186  							// Verify all elements from u are in m2 or m1.
   187  							for x, ok := u.Next(minVal); ok; x, ok = u.Next(x + 1) {
   188  								if !(m1[x] || m2[x]) {
   189  									t.Errorf("incorrect union result %s union %s = %s", &s1, &s2, &u)
   190  									break
   191  								}
   192  							}
   193  
   194  							// Test intersection.
   195  							u = s1.Copy()
   196  							u.IntersectionWith(s2)
   197  							if s1.Intersects(s2) != !u.Empty() ||
   198  								s2.Intersects(s1) != !u.Empty() {
   199  								t.Errorf("inconsistency between IntersectionWith and Intersect on %s %s\n", s1, s2)
   200  							}
   201  							if !u.Equals(s1.Intersection(s2)) {
   202  								t.Errorf("inconsistency between IntersectionWith and Intersection on %s %s\n", s1, s2)
   203  							}
   204  							// Verify all elements from m1 and m2 are in u.
   205  							for x := range m1 {
   206  								if m2[x] && !u.Contains(x) {
   207  									t.Errorf("incorrect intersection result %s union %s = %s  x=%d", &s1, &s2, &u, x)
   208  									break
   209  								}
   210  							}
   211  							// Verify all elements from u are in m2 and m1.
   212  							for x, ok := u.Next(minVal); ok; x, ok = u.Next(x + 1) {
   213  								if !(m1[x] && m2[x]) {
   214  									t.Errorf("incorrect intersection result %s intersect %s = %s", &s1, &s2, &u)
   215  									break
   216  								}
   217  							}
   218  
   219  							// Test difference.
   220  							u = s1.Copy()
   221  							u.DifferenceWith(s2)
   222  
   223  							if !u.Equals(s1.Difference(s2)) {
   224  								t.Errorf("inconsistency between DifferenceWith and Difference on %s %s\n", s1, s2)
   225  							}
   226  
   227  							// Verify all elements in m1 but not in m2 are in u.
   228  							for x := range m1 {
   229  								if !m2[x] && !u.Contains(x) {
   230  									t.Errorf("incorrect difference result %s \\ %s = %s  x=%d", &s1, &s2, &u, x)
   231  									break
   232  								}
   233  							}
   234  							// Verify all elements from u are in m1.
   235  							for x, ok := u.Next(minVal); ok; x, ok = u.Next(x + 1) {
   236  								if !m1[x] {
   237  									t.Errorf("incorrect difference result %s \\ %s = %s", &s1, &s2, &u)
   238  									break
   239  								}
   240  							}
   241  						}
   242  					}
   243  				}
   244  			}
   245  		}
   246  	}
   247  }
   248  
   249  func TestFastIntSetAddRange(t *testing.T) {
   250  	assertSet := func(set *FastIntSet, from, to int) {
   251  		t.Helper()
   252  		// Iterate through the set and ensure that the values
   253  		// it contain are the values from 'from' to 'to' (inclusively).
   254  		expected := from
   255  		set.ForEach(func(actual int) {
   256  			t.Helper()
   257  			if actual > to {
   258  				t.Fatalf("expected last value in FastIntSet to be %d, got %d", to, actual)
   259  			}
   260  			if expected != actual {
   261  				t.Fatalf("expected next value in FastIntSet to be %d, got %d", expected, actual)
   262  			}
   263  			expected++
   264  		})
   265  	}
   266  
   267  	max := smallCutoff + 20
   268  	// Test all O(n^2) sub-intervals of [from,to] in the interval
   269  	// [-5, smallCutoff + 20].
   270  	for from := -5; from <= max; from++ {
   271  		for to := from; to <= max; to++ {
   272  			var set FastIntSet
   273  			set.AddRange(from, to)
   274  			assertSet(&set, from, to)
   275  		}
   276  	}
   277  }
   278  
   279  func TestFastIntSetString(t *testing.T) {
   280  	testCases := []struct {
   281  		vals []int
   282  		exp  string
   283  	}{
   284  		{
   285  			vals: []int{},
   286  			exp:  "()",
   287  		},
   288  		{
   289  			vals: []int{-5, -3, -2, -1, 0, 1, 2, 3, 4, 5},
   290  			exp:  "(-5,-3,-2,-1,0-5)",
   291  		},
   292  		{
   293  			vals: []int{0, 1, 3, 4, 5},
   294  			exp:  "(0,1,3-5)",
   295  		},
   296  	}
   297  	for i, tc := range testCases {
   298  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   299  			s := MakeFastIntSet(tc.vals...)
   300  			if str := s.String(); str != tc.exp {
   301  				t.Errorf("expected %s, got %s", tc.exp, str)
   302  			}
   303  		})
   304  	}
   305  }