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 }