github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/container/intsets/sparse_test.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package intsets_test 6 7 import ( 8 "fmt" 9 "log" 10 "math/rand" 11 "sort" 12 "strings" 13 "testing" 14 15 "golang.org/x/tools/container/intsets" 16 ) 17 18 func TestBasics(t *testing.T) { 19 var s intsets.Sparse 20 if len := s.Len(); len != 0 { 21 t.Errorf("Len({}): got %d, want 0", len) 22 } 23 if s := s.String(); s != "{}" { 24 t.Errorf("String({}): got %q, want \"{}\"", s) 25 } 26 if s.Has(3) { 27 t.Errorf("Has(3): got true, want false") 28 } 29 if err := s.Check(); err != nil { 30 t.Error(err) 31 } 32 33 if !s.Insert(3) { 34 t.Errorf("Insert(3): got false, want true") 35 } 36 if max := s.Max(); max != 3 { 37 t.Errorf("Max: got %d, want 3", max) 38 } 39 40 if !s.Insert(435) { 41 t.Errorf("Insert(435): got false, want true") 42 } 43 if s := s.String(); s != "{3 435}" { 44 t.Errorf("String({3 435}): got %q, want \"{3 435}\"", s) 45 } 46 if max := s.Max(); max != 435 { 47 t.Errorf("Max: got %d, want 435", max) 48 } 49 if len := s.Len(); len != 2 { 50 t.Errorf("Len: got %d, want 2", len) 51 } 52 53 if !s.Remove(435) { 54 t.Errorf("Remove(435): got false, want true") 55 } 56 if s := s.String(); s != "{3}" { 57 t.Errorf("String({3}): got %q, want \"{3}\"", s) 58 } 59 } 60 61 // Insert, Len, IsEmpty, Hash, Clear, AppendTo. 62 func TestMoreBasics(t *testing.T) { 63 set := new(intsets.Sparse) 64 set.Insert(456) 65 set.Insert(123) 66 set.Insert(789) 67 if set.Len() != 3 { 68 t.Errorf("%s.Len: got %d, want 3", set, set.Len()) 69 } 70 if set.IsEmpty() { 71 t.Errorf("%s.IsEmpty: got true", set) 72 } 73 if !set.Has(123) { 74 t.Errorf("%s.Has(123): got false", set) 75 } 76 if set.Has(1234) { 77 t.Errorf("%s.Has(1234): got true", set) 78 } 79 got := set.AppendTo([]int{-1}) 80 if want := []int{-1, 123, 456, 789}; fmt.Sprint(got) != fmt.Sprint(want) { 81 t.Errorf("%s.AppendTo: got %v, want %v", set, got, want) 82 } 83 84 set.Clear() 85 86 if set.Len() != 0 { 87 t.Errorf("Clear: got %d, want 0", set.Len()) 88 } 89 if !set.IsEmpty() { 90 t.Errorf("IsEmpty: got false") 91 } 92 if set.Has(123) { 93 t.Errorf("%s.Has: got false", set) 94 } 95 } 96 97 func TestTakeMin(t *testing.T) { 98 var set intsets.Sparse 99 set.Insert(456) 100 set.Insert(123) 101 set.Insert(789) 102 set.Insert(-123) 103 var got int 104 for i, want := range []int{-123, 123, 456, 789} { 105 if !set.TakeMin(&got) || got != want { 106 t.Errorf("TakeMin #%d: got %d, want %d", i, got, want) 107 } 108 } 109 if set.TakeMin(&got) { 110 t.Errorf("%s.TakeMin returned true", &set) 111 } 112 if err := set.Check(); err != nil { 113 t.Fatalf("check: %s: %#v", err, &set) 114 } 115 } 116 117 func TestMinAndMax(t *testing.T) { 118 values := []int{0, 456, 123, 789, -123} // elt 0 => empty set 119 wantMax := []int{intsets.MinInt, 456, 456, 789, 789} 120 wantMin := []int{intsets.MaxInt, 456, 123, 123, -123} 121 122 var set intsets.Sparse 123 for i, x := range values { 124 if i != 0 { 125 set.Insert(x) 126 } 127 if got, want := set.Min(), wantMin[i]; got != want { 128 t.Errorf("Min #%d: got %d, want %d", i, got, want) 129 } 130 if got, want := set.Max(), wantMax[i]; got != want { 131 t.Errorf("Max #%d: got %d, want %d", i, got, want) 132 } 133 } 134 135 set.Insert(intsets.MinInt) 136 if got, want := set.Min(), intsets.MinInt; got != want { 137 t.Errorf("Min: got %d, want %d", got, want) 138 } 139 140 set.Insert(intsets.MaxInt) 141 if got, want := set.Max(), intsets.MaxInt; got != want { 142 t.Errorf("Max: got %d, want %d", got, want) 143 } 144 } 145 146 func TestEquals(t *testing.T) { 147 var setX intsets.Sparse 148 setX.Insert(456) 149 setX.Insert(123) 150 setX.Insert(789) 151 152 if !setX.Equals(&setX) { 153 t.Errorf("Equals(%s, %s): got false", &setX, &setX) 154 } 155 156 var setY intsets.Sparse 157 setY.Insert(789) 158 setY.Insert(456) 159 setY.Insert(123) 160 161 if !setX.Equals(&setY) { 162 t.Errorf("Equals(%s, %s): got false", &setX, &setY) 163 } 164 165 setY.Insert(1) 166 if setX.Equals(&setY) { 167 t.Errorf("Equals(%s, %s): got true", &setX, &setY) 168 } 169 170 var empty intsets.Sparse 171 if setX.Equals(&empty) { 172 t.Errorf("Equals(%s, %s): got true", &setX, &empty) 173 } 174 175 // Edge case: some block (with offset=0) appears in X but not Y. 176 setY.Remove(123) 177 if setX.Equals(&setY) { 178 t.Errorf("Equals(%s, %s): got true", &setX, &setY) 179 } 180 } 181 182 // A pset is a parallel implementation of a set using both an intsets.Sparse 183 // and a built-in hash map. 184 type pset struct { 185 hash map[int]bool 186 bits intsets.Sparse 187 } 188 189 func makePset() *pset { 190 return &pset{hash: make(map[int]bool)} 191 } 192 193 func (set *pset) add(n int) { 194 prev := len(set.hash) 195 set.hash[n] = true 196 grewA := len(set.hash) > prev 197 198 grewB := set.bits.Insert(n) 199 200 if grewA != grewB { 201 panic(fmt.Sprintf("add(%d): grewA=%t grewB=%t", n, grewA, grewB)) 202 } 203 } 204 205 func (set *pset) remove(n int) { 206 prev := len(set.hash) 207 delete(set.hash, n) 208 shrankA := len(set.hash) < prev 209 210 shrankB := set.bits.Remove(n) 211 212 if shrankA != shrankB { 213 panic(fmt.Sprintf("remove(%d): shrankA=%t shrankB=%t", n, shrankA, shrankB)) 214 } 215 } 216 217 func (set *pset) check(t *testing.T, msg string) { 218 var eltsA []int 219 for elt := range set.hash { 220 eltsA = append(eltsA, int(elt)) 221 } 222 sort.Ints(eltsA) 223 224 eltsB := set.bits.AppendTo(nil) 225 226 if a, b := fmt.Sprint(eltsA), fmt.Sprint(eltsB); a != b { 227 t.Errorf("check(%s): hash=%s bits=%s (%s)", msg, a, b, &set.bits) 228 } 229 230 if err := set.bits.Check(); err != nil { 231 t.Fatalf("Check(%s): %s: %#v", msg, err, &set.bits) 232 } 233 } 234 235 // randomPset returns a parallel set of random size and elements. 236 func randomPset(prng *rand.Rand, maxSize int) *pset { 237 set := makePset() 238 size := int(prng.Int()) % maxSize 239 for i := 0; i < size; i++ { 240 // TODO(adonovan): benchmark how performance varies 241 // with this sparsity parameter. 242 n := int(prng.Int()) % 10000 243 set.add(n) 244 } 245 return set 246 } 247 248 // TestRandomMutations performs the same random adds/removes on two 249 // set implementations and ensures that they compute the same result. 250 func TestRandomMutations(t *testing.T) { 251 const debug = false 252 253 set := makePset() 254 prng := rand.New(rand.NewSource(0)) 255 for i := 0; i < 10000; i++ { 256 n := int(prng.Int())%2000 - 1000 257 if i%2 == 0 { 258 if debug { 259 log.Printf("add %d", n) 260 } 261 set.add(n) 262 } else { 263 if debug { 264 log.Printf("remove %d", n) 265 } 266 set.remove(n) 267 } 268 if debug { 269 set.check(t, "post mutation") 270 } 271 } 272 set.check(t, "final") 273 if debug { 274 log.Print(&set.bits) 275 } 276 } 277 278 func TestLowerBound(t *testing.T) { 279 // Use random sets of sizes from 0 to about 4000. 280 prng := rand.New(rand.NewSource(0)) 281 for i := uint(0); i < 12; i++ { 282 x := randomPset(prng, 1<<i) 283 for j := 0; j < 10000; j++ { 284 found := intsets.MaxInt 285 for e := range x.hash { 286 if e >= j && e < found { 287 found = e 288 } 289 } 290 if res := x.bits.LowerBound(j); res != found { 291 t.Errorf("%s: LowerBound(%d)=%d, expected %d", &x.bits, j, res, found) 292 } 293 } 294 } 295 } 296 297 // TestSetOperations exercises classic set operations: ∩ , ∪, \. 298 func TestSetOperations(t *testing.T) { 299 prng := rand.New(rand.NewSource(0)) 300 301 // Use random sets of sizes from 0 to about 4000. 302 // For each operator, we test variations such as 303 // Z.op(X, Y), Z.op(X, Z) and Z.op(Z, Y) to exercise 304 // the degenerate cases of each method implementation. 305 for i := uint(0); i < 12; i++ { 306 X := randomPset(prng, 1<<i) 307 Y := randomPset(prng, 1<<i) 308 309 // TODO(adonovan): minimise dependencies between stanzas below. 310 311 // Copy(X) 312 C := makePset() 313 C.bits.Copy(&Y.bits) // no effect on result 314 C.bits.Copy(&X.bits) 315 C.hash = X.hash 316 C.check(t, "C.Copy(X)") 317 C.bits.Copy(&C.bits) 318 C.check(t, "C.Copy(C)") 319 320 // U.Union(X, Y) 321 U := makePset() 322 U.bits.Union(&X.bits, &Y.bits) 323 for n := range X.hash { 324 U.hash[n] = true 325 } 326 for n := range Y.hash { 327 U.hash[n] = true 328 } 329 U.check(t, "U.Union(X, Y)") 330 331 // U.Union(X, X) 332 U.bits.Union(&X.bits, &X.bits) 333 U.hash = X.hash 334 U.check(t, "U.Union(X, X)") 335 336 // U.Union(U, Y) 337 U = makePset() 338 U.bits.Copy(&X.bits) 339 U.bits.Union(&U.bits, &Y.bits) 340 for n := range X.hash { 341 U.hash[n] = true 342 } 343 for n := range Y.hash { 344 U.hash[n] = true 345 } 346 U.check(t, "U.Union(U, Y)") 347 348 // U.Union(X, U) 349 U.bits.Copy(&Y.bits) 350 U.bits.Union(&X.bits, &U.bits) 351 U.check(t, "U.Union(X, U)") 352 353 // U.UnionWith(U) 354 U.bits.UnionWith(&U.bits) 355 U.check(t, "U.UnionWith(U)") 356 357 // I.Intersection(X, Y) 358 I := makePset() 359 I.bits.Intersection(&X.bits, &Y.bits) 360 for n := range X.hash { 361 if Y.hash[n] { 362 I.hash[n] = true 363 } 364 } 365 I.check(t, "I.Intersection(X, Y)") 366 367 // I.Intersection(X, X) 368 I.bits.Intersection(&X.bits, &X.bits) 369 I.hash = X.hash 370 I.check(t, "I.Intersection(X, X)") 371 372 // I.Intersection(I, X) 373 I.bits.Intersection(&I.bits, &X.bits) 374 I.check(t, "I.Intersection(I, X)") 375 376 // I.Intersection(X, I) 377 I.bits.Intersection(&X.bits, &I.bits) 378 I.check(t, "I.Intersection(X, I)") 379 380 // I.Intersection(I, I) 381 I.bits.Intersection(&I.bits, &I.bits) 382 I.check(t, "I.Intersection(I, I)") 383 384 // D.Difference(X, Y) 385 D := makePset() 386 D.bits.Difference(&X.bits, &Y.bits) 387 for n := range X.hash { 388 if !Y.hash[n] { 389 D.hash[n] = true 390 } 391 } 392 D.check(t, "D.Difference(X, Y)") 393 394 // D.Difference(D, Y) 395 D.bits.Copy(&X.bits) 396 D.bits.Difference(&D.bits, &Y.bits) 397 D.check(t, "D.Difference(D, Y)") 398 399 // D.Difference(Y, D) 400 D.bits.Copy(&X.bits) 401 D.bits.Difference(&Y.bits, &D.bits) 402 D.hash = make(map[int]bool) 403 for n := range Y.hash { 404 if !X.hash[n] { 405 D.hash[n] = true 406 } 407 } 408 D.check(t, "D.Difference(Y, D)") 409 410 // D.Difference(X, X) 411 D.bits.Difference(&X.bits, &X.bits) 412 D.hash = nil 413 D.check(t, "D.Difference(X, X)") 414 415 // D.DifferenceWith(D) 416 D.bits.Copy(&X.bits) 417 D.bits.DifferenceWith(&D.bits) 418 D.check(t, "D.DifferenceWith(D)") 419 420 // SD.SymmetricDifference(X, Y) 421 SD := makePset() 422 SD.bits.SymmetricDifference(&X.bits, &Y.bits) 423 for n := range X.hash { 424 if !Y.hash[n] { 425 SD.hash[n] = true 426 } 427 } 428 for n := range Y.hash { 429 if !X.hash[n] { 430 SD.hash[n] = true 431 } 432 } 433 SD.check(t, "SD.SymmetricDifference(X, Y)") 434 435 // X.SymmetricDifferenceWith(Y) 436 SD.bits.Copy(&X.bits) 437 SD.bits.SymmetricDifferenceWith(&Y.bits) 438 SD.check(t, "X.SymmetricDifference(Y)") 439 440 // Y.SymmetricDifferenceWith(X) 441 SD.bits.Copy(&Y.bits) 442 SD.bits.SymmetricDifferenceWith(&X.bits) 443 SD.check(t, "Y.SymmetricDifference(X)") 444 445 // SD.SymmetricDifference(X, X) 446 SD.bits.SymmetricDifference(&X.bits, &X.bits) 447 SD.hash = nil 448 SD.check(t, "SD.SymmetricDifference(X, X)") 449 450 // SD.SymmetricDifference(X, Copy(X)) 451 X2 := makePset() 452 X2.bits.Copy(&X.bits) 453 SD.bits.SymmetricDifference(&X.bits, &X2.bits) 454 SD.check(t, "SD.SymmetricDifference(X, Copy(X))") 455 456 // Copy(X).SymmetricDifferenceWith(X) 457 SD.bits.Copy(&X.bits) 458 SD.bits.SymmetricDifferenceWith(&X.bits) 459 SD.check(t, "Copy(X).SymmetricDifferenceWith(X)") 460 } 461 } 462 463 // TestUnionWithChanged checks the 'changed' result of UnionWith. 464 func TestUnionWithChanged(t *testing.T) { 465 setOf := func(elems ...int) *intsets.Sparse { 466 s := new(intsets.Sparse) 467 for _, elem := range elems { 468 s.Insert(elem) 469 } 470 return s 471 } 472 473 checkUnionWith := func(x, y *intsets.Sparse) { 474 xstr := x.String() 475 prelen := x.Len() 476 changed := x.UnionWith(y) 477 if (x.Len() > prelen) != changed { 478 t.Errorf("%s.UnionWith(%s) => %s, changed=%t", xstr, y, x, changed) 479 } 480 } 481 482 // The case marked "!" is a regression test for Issue 50352, 483 // which spuriously returned true when y ⊂ x. 484 485 // same block 486 checkUnionWith(setOf(1, 2), setOf(1, 2)) 487 checkUnionWith(setOf(1, 2, 3), setOf(1, 2)) // ! 488 checkUnionWith(setOf(1, 2), setOf(1, 2, 3)) 489 checkUnionWith(setOf(1, 2), setOf()) 490 491 // different blocks 492 checkUnionWith(setOf(1, 1000000), setOf(1, 1000000)) 493 checkUnionWith(setOf(1, 2, 1000000), setOf(1, 2)) 494 checkUnionWith(setOf(1, 2), setOf(1, 2, 1000000)) 495 checkUnionWith(setOf(1, 1000000), setOf()) 496 } 497 498 func TestIntersectionWith(t *testing.T) { 499 // Edge cases: the pairs (1,1), (1000,2000), (8000,4000) 500 // exercise the <, >, == cases in IntersectionWith that the 501 // TestSetOperations data is too dense to cover. 502 var X, Y intsets.Sparse 503 X.Insert(1) 504 X.Insert(1000) 505 X.Insert(8000) 506 Y.Insert(1) 507 Y.Insert(2000) 508 Y.Insert(4000) 509 X.IntersectionWith(&Y) 510 if got, want := X.String(), "{1}"; got != want { 511 t.Errorf("IntersectionWith: got %s, want %s", got, want) 512 } 513 } 514 515 func TestIntersects(t *testing.T) { 516 prng := rand.New(rand.NewSource(0)) 517 518 for i := uint(0); i < 12; i++ { 519 X, Y := randomPset(prng, 1<<i), randomPset(prng, 1<<i) 520 x, y := &X.bits, &Y.bits 521 522 // test the slow way 523 var z intsets.Sparse 524 z.Copy(x) 525 z.IntersectionWith(y) 526 527 if got, want := x.Intersects(y), !z.IsEmpty(); got != want { 528 t.Errorf("Intersects(%s, %s): got %v, want %v (%s)", x, y, got, want, &z) 529 } 530 531 // make it false 532 a := x.AppendTo(nil) 533 for _, v := range a { 534 y.Remove(v) 535 } 536 537 if got, want := x.Intersects(y), false; got != want { 538 t.Errorf("Intersects: got %v, want %v", got, want) 539 } 540 541 // make it true 542 if x.IsEmpty() { 543 continue 544 } 545 i := prng.Intn(len(a)) 546 y.Insert(a[i]) 547 548 if got, want := x.Intersects(y), true; got != want { 549 t.Errorf("Intersects: got %v, want %v", got, want) 550 } 551 } 552 } 553 554 func TestSubsetOf(t *testing.T) { 555 prng := rand.New(rand.NewSource(0)) 556 557 for i := uint(0); i < 12; i++ { 558 X, Y := randomPset(prng, 1<<i), randomPset(prng, 1<<i) 559 x, y := &X.bits, &Y.bits 560 561 // test the slow way 562 var z intsets.Sparse 563 z.Copy(x) 564 z.DifferenceWith(y) 565 566 if got, want := x.SubsetOf(y), z.IsEmpty(); got != want { 567 t.Errorf("SubsetOf: got %v, want %v", got, want) 568 } 569 570 // make it true 571 y.UnionWith(x) 572 573 if got, want := x.SubsetOf(y), true; got != want { 574 t.Errorf("SubsetOf: got %v, want %v", got, want) 575 } 576 577 // make it false 578 if x.IsEmpty() { 579 continue 580 } 581 a := x.AppendTo(nil) 582 i := prng.Intn(len(a)) 583 y.Remove(a[i]) 584 585 if got, want := x.SubsetOf(y), false; got != want { 586 t.Errorf("SubsetOf: got %v, want %v", got, want) 587 } 588 } 589 } 590 591 func TestBitString(t *testing.T) { 592 for _, test := range []struct { 593 input []int 594 want string 595 }{ 596 {nil, "0"}, 597 {[]int{0}, "1"}, 598 {[]int{0, 4, 5}, "110001"}, 599 {[]int{0, 7, 177}, "1" + strings.Repeat("0", 169) + "10000001"}, 600 {[]int{-3, 0, 4, 5}, "110001.001"}, 601 {[]int{-3}, "0.001"}, 602 } { 603 var set intsets.Sparse 604 for _, x := range test.input { 605 set.Insert(x) 606 } 607 if got := set.BitString(); got != test.want { 608 t.Errorf("BitString(%s) = %s, want %s", set.String(), got, test.want) 609 } 610 } 611 } 612 613 func TestFailFastOnShallowCopy(t *testing.T) { 614 var x intsets.Sparse 615 x.Insert(1) 616 617 y := x // shallow copy (breaks representation invariants) 618 defer func() { 619 got := fmt.Sprint(recover()) 620 want := "interface conversion: interface {} is nil, not intsets.to_copy_a_sparse_you_must_call_its_Copy_method" 621 if got != want { 622 t.Errorf("shallow copy: recover() = %q, want %q", got, want) 623 } 624 }() 625 _ = y.String() // panics 626 t.Error("didn't panic as expected") 627 } 628 629 // -- Benchmarks ------------------------------------------------------- 630 631 // TODO(adonovan): 632 // - Add benchmarks of each method. 633 // - Gather set distributions from pointer analysis. 634 // - Measure memory usage. 635 636 func benchmarkInsertProbeSparse(b *testing.B, size, spread int) { 637 prng := rand.New(rand.NewSource(0)) 638 // Generate our insertions and probes beforehand (we don't want to benchmark 639 // the prng). 640 insert := make([]int, size) 641 probe := make([]int, size*2) 642 for i := range insert { 643 insert[i] = prng.Int() % spread 644 } 645 for i := range probe { 646 probe[i] = prng.Int() % spread 647 } 648 649 b.ResetTimer() 650 var x intsets.Sparse 651 for tries := 0; tries < b.N; tries++ { 652 x.Clear() 653 for _, n := range insert { 654 x.Insert(n) 655 } 656 hits := 0 657 for _, n := range probe { 658 if x.Has(n) { 659 hits++ 660 } 661 } 662 // Use the variable so it doesn't get optimized away. 663 if hits > len(probe) { 664 b.Fatalf("%d hits, only %d probes", hits, len(probe)) 665 } 666 } 667 } 668 669 func BenchmarkInsertProbeSparse_2_10(b *testing.B) { 670 benchmarkInsertProbeSparse(b, 2, 10) 671 } 672 673 func BenchmarkInsertProbeSparse_10_10(b *testing.B) { 674 benchmarkInsertProbeSparse(b, 10, 10) 675 } 676 677 func BenchmarkInsertProbeSparse_10_1000(b *testing.B) { 678 benchmarkInsertProbeSparse(b, 10, 1000) 679 } 680 681 func BenchmarkInsertProbeSparse_100_100(b *testing.B) { 682 benchmarkInsertProbeSparse(b, 100, 100) 683 } 684 685 func BenchmarkInsertProbeSparse_100_10000(b *testing.B) { 686 benchmarkInsertProbeSparse(b, 100, 1000) 687 } 688 689 func BenchmarkUnionDifferenceSparse(b *testing.B) { 690 prng := rand.New(rand.NewSource(0)) 691 for tries := 0; tries < b.N; tries++ { 692 var x, y, z intsets.Sparse 693 for i := 0; i < 1000; i++ { 694 n := int(prng.Int()) % 100000 695 if i%2 == 0 { 696 x.Insert(n) 697 } else { 698 y.Insert(n) 699 } 700 } 701 z.Union(&x, &y) 702 z.Difference(&x, &y) 703 } 704 } 705 706 func BenchmarkUnionDifferenceHashTable(b *testing.B) { 707 prng := rand.New(rand.NewSource(0)) 708 for tries := 0; tries < b.N; tries++ { 709 x, y, z := make(map[int]bool), make(map[int]bool), make(map[int]bool) 710 for i := 0; i < 1000; i++ { 711 n := int(prng.Int()) % 100000 712 if i%2 == 0 { 713 x[n] = true 714 } else { 715 y[n] = true 716 } 717 } 718 // union 719 for n := range x { 720 z[n] = true 721 } 722 for n := range y { 723 z[n] = true 724 } 725 // difference 726 z = make(map[int]bool) 727 for n := range y { 728 if !x[n] { 729 z[n] = true 730 } 731 } 732 } 733 } 734 735 func BenchmarkAppendTo(b *testing.B) { 736 prng := rand.New(rand.NewSource(0)) 737 var x intsets.Sparse 738 for i := 0; i < 1000; i++ { 739 x.Insert(int(prng.Int()) % 10000) 740 } 741 var space [1000]int 742 for tries := 0; tries < b.N; tries++ { 743 x.AppendTo(space[:0]) 744 } 745 }