github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/structure/sets/zset/zset_test.go (about) 1 package zset 2 3 import ( 4 "fmt" 5 "math/rand" 6 "sort" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 12 "github.com/songzhibin97/go-baseutils/base/bcomparator" 13 "github.com/songzhibin97/go-baseutils/sys/fastrand" 14 ) 15 16 var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 17 18 func randString(prefix string) string { 19 b := make([]rune, 8) 20 for i := range b { 21 b[i] = letterRunes[fastrand.Intn(len(letterRunes))] 22 } 23 return prefix + string(b) 24 } 25 26 func TestSet(t *testing.T) { 27 z := New[string](bcomparator.StringComparator()) 28 assert.Zero(t, z.Len()) 29 } 30 31 func TestSetAdd(t *testing.T) { 32 z := New[string](bcomparator.StringComparator()) 33 v := randString("") 34 assert.True(t, z.AddB(1, v)) 35 assert.False(t, z.AddB(1, v)) 36 } 37 38 func TestSetContains(t *testing.T) { 39 z := New[string](bcomparator.StringComparator()) 40 v := randString("") 41 z.AddB(1, v) 42 assert.True(t, z.Contains(v)) 43 assert.False(t, z.Contains("no-such-"+v)) 44 } 45 46 func TestSetScore(t *testing.T) { 47 z := New[string](bcomparator.StringComparator()) 48 v := randString("") 49 s := rand.Float64() 50 z.AddB(s, v) 51 as, ok := z.Score(v) 52 assert.True(t, ok) 53 assert.Equal(t, s, as) 54 _, ok = z.Score("no-such-" + v) 55 assert.False(t, ok) 56 } 57 58 func TestSetIncr(t *testing.T) { 59 z := New[string](bcomparator.StringComparator()) 60 _, ok := z.Score("t") 61 assert.False(t, ok) 62 63 // test first insert 64 s, ok := z.IncrBy(1, "t") 65 assert.False(t, ok) 66 assert.Equal(t, 1.0, s) 67 68 // test regular incr 69 s, ok = z.IncrBy(2, "t") 70 assert.True(t, ok) 71 assert.Equal(t, 3.0, s) 72 } 73 74 func TestSetRemove(t *testing.T) { 75 z := New[string](bcomparator.StringComparator()) 76 // test first insert 77 ok := z.AddB(1, "t") 78 assert.True(t, ok) 79 _, ok = z.RemoveB("t") 80 assert.True(t, ok) 81 } 82 83 func TestSetRank(t *testing.T) { 84 z := New[string](bcomparator.StringComparator()) 85 v := randString("") 86 z.AddB(1, v) 87 // test rank of exist value 88 assert.Equal(t, 0, z.Rank(v)) 89 // test rank of non-exist value 90 assert.Equal(t, -1, z.Rank("no-such-"+v)) 91 } 92 93 func TestSetRank_Many(t *testing.T) { 94 const N = 1000 95 z := New[string](bcomparator.StringComparator()) 96 rand.Seed(time.Now().Unix()) 97 98 var vs []string 99 for i := 0; i < N; i++ { 100 v := randString("") 101 z.AddB(rand.Float64(), v) 102 vs = append(vs, v) 103 } 104 for _, v := range vs { 105 r := z.Rank(v) 106 assert.NotEqual(t, -1, r) 107 108 // verify rank by traversing level 0 109 actualRank := 0 110 x := z.list.header 111 for x != nil { 112 x = x.loadNext(0) 113 if x.value == v { 114 break 115 } 116 actualRank++ 117 } 118 assert.Equal(t, v, x.value) 119 assert.Equal(t, r, actualRank) 120 } 121 } 122 123 func TestSetRank_UpdateScore(t *testing.T) { 124 z := New[string](bcomparator.StringComparator()) 125 rand.Seed(time.Now().Unix()) 126 127 var vs []string 128 for i := 0; i < 100; i++ { 129 v := fmt.Sprint(i) 130 z.AddB(rand.Float64(), v) 131 vs = append(vs, v) 132 } 133 // Randomly update score 134 for i := 0; i < 100; i++ { 135 // 1/2 136 if rand.Float64() > 0.5 { 137 continue 138 } 139 z.AddB(float64(i), fmt.Sprint(i)) 140 } 141 142 for _, v := range vs { 143 r := z.Rank(v) 144 assert.NotEqual(t, -1, r) 145 assert.Greater(t, z.Len(), r) 146 147 // verify rank by traversing level 0 148 actualRank := 0 149 x := z.list.header 150 for x != nil { 151 x = x.loadNext(0) 152 if x.value == v { 153 break 154 } 155 actualRank++ 156 } 157 assert.Equal(t, v, x.value) 158 assert.Equal(t, r, actualRank) 159 } 160 } 161 162 // Test whether the ramdom inserted values sorted 163 func TestSetIsSorted(t *testing.T) { 164 const N = 1000 165 z := New[string](bcomparator.StringComparator()) 166 rand.Seed(time.Now().Unix()) 167 168 // Test whether the ramdom inserted values sorted 169 for i := 0; i < N; i++ { 170 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 171 } 172 testIsSorted(t, z) 173 testInternalSpan(t, z) 174 175 // Randomly update score 176 for i := 0; i < N; i++ { 177 // 1/2 178 if rand.Float64() > 0.5 { 179 continue 180 } 181 z.AddB(float64(i), fmt.Sprint(i)) 182 } 183 184 testIsSorted(t, z) 185 testInternalSpan(t, z) 186 187 // Randomly add or delete value 188 for i := 0; i < N; i++ { 189 // 1/2 190 if rand.Float64() > 0.5 { 191 continue 192 } 193 z.Remove(fmt.Sprint(i)) 194 } 195 testIsSorted(t, z) 196 testInternalSpan(t, z) 197 } 198 199 func testIsSorted(t *testing.T, z *Set[string]) { 200 var scores []float64 201 for _, n := range z.Range(0, z.Len()-1) { 202 scores = append(scores, n.Score) 203 } 204 assert.True(t, sort.Float64sAreSorted(scores)) 205 } 206 207 func testInternalSpan(t *testing.T, z *Set[string]) { 208 l := z.list 209 for i := l.highestLevel - 1; i >= 0; i-- { 210 x := l.header 211 for x.loadNext(i) != nil { 212 x = x.loadNext(i) 213 span := x.loadSpan(i) 214 from := x.value 215 fromScore := x.score 216 fromRank := l.Rank(fromScore, from) 217 assert.NotEqual(t, -1, fromRank) 218 219 if x.loadNext(i) != nil { // from -> to 220 to := x.loadNext(i).value 221 toScore := x.loadNext(i).score 222 toRank := l.Rank(toScore, to) 223 assert.NotEqual(t, -1, toRank) 224 225 // span = to.rank - from.rank 226 assert.Equalf(t, span, toRank-fromRank, "from %q (score: , rank: %d) to %q (score: %d, rank: %d), expect span: %d, actual: %d", 227 from, fromScore, fromRank, to, toScore, toRank, span, toRank-fromRank) 228 } else { // from -> nil 229 // span = skiplist.len - from.rank 230 assert.Equalf(t, l.length-fromRank, x.loadSpan(i), "%q (score: , rank: %d)", from, fromScore, fromRank) 231 } 232 } 233 } 234 } 235 236 func TestSetRange(t *testing.T) { 237 testFloat64SetRange(t, false) 238 } 239 240 func TestSetRevRange(t *testing.T) { 241 testFloat64SetRange(t, true) 242 } 243 244 func testFloat64SetRange(t *testing.T, rev bool) { 245 const N = 1000 246 z := New[string](bcomparator.StringComparator()) 247 for i := 0; i < N; i++ { 248 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 249 } 250 251 start, stop := func(a, b int) (int, int) { 252 if a < b { 253 return a, b 254 } else { 255 return b, a 256 } 257 }(fastrand.Intn(N), fastrand.Intn(N)) 258 var ns []Node[string] 259 if rev { 260 ns = z.RevRange(start, stop) 261 } else { 262 ns = z.Range(start, stop) 263 } 264 assert.Equal(t, stop-start+1, len(ns)) 265 for i, n := range ns { 266 if rev { 267 assert.Equal(t, z.Len()-1-(start+i), z.Rank(n.Value)) 268 } else { 269 assert.Equal(t, start+i, z.Rank(n.Value)) 270 } 271 } 272 } 273 274 func TestSetRange_Negative(t *testing.T) { 275 const N = 1000 276 z := New[string](bcomparator.StringComparator()) 277 for i := 0; i < N; i++ { 278 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 279 } 280 ns := z.Range(-1, -1) 281 assert.Equal(t, 1, len(ns)) 282 assert.Equal(t, z.Len()-1, z.Rank(ns[0].Value)) 283 } 284 285 func TestSetRevRange_Negative(t *testing.T) { 286 const N = 1000 287 z := New[string](bcomparator.StringComparator()) 288 for i := 0; i < N; i++ { 289 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 290 } 291 ns := z.RevRange(-1, -1) 292 assert.Equal(t, 1, len(ns)) 293 assert.Equal(t, 0, z.Rank(ns[0].Value)) 294 } 295 296 func TestSetRangeByScore(t *testing.T) { 297 testFloat64SetRangeByScore(t, false) 298 } 299 300 func TestSetRangeByScoreWithOpt(t *testing.T) { 301 z := New[string](bcomparator.StringComparator()) 302 z.AddB(1.0, "1") 303 z.AddB(1.1, "2") 304 z.AddB(2.0, "3") 305 306 ns := z.RangeByScoreWithOpt(1.0, 2.0, RangeOpt{ExcludeMin: true}) 307 assert.Equal(t, 2, len(ns)) 308 assert.Equal(t, 1.1, ns[0].Score) 309 assert.Equal(t, 2.0, ns[1].Score) 310 311 ns = z.RangeByScoreWithOpt(1.0, 2.0, RangeOpt{ExcludeMin: true, ExcludeMax: true}) 312 assert.Equal(t, 1, len(ns)) 313 assert.Equal(t, 1.1, ns[0].Score) 314 315 ns = z.RangeByScoreWithOpt(1.0, 2.0, RangeOpt{ExcludeMax: true}) 316 assert.Equal(t, 2, len(ns)) 317 assert.Equal(t, 1.0, ns[0].Score) 318 assert.Equal(t, 1.1, ns[1].Score) 319 320 ns = z.RangeByScoreWithOpt(2.0, 1.0, RangeOpt{}) 321 assert.Equal(t, 0, len(ns)) 322 ns = z.RangeByScoreWithOpt(2.0, 1.0, RangeOpt{ExcludeMin: true}) 323 assert.Equal(t, 0, len(ns)) 324 ns = z.RangeByScoreWithOpt(2.0, 1.0, RangeOpt{ExcludeMax: true}) 325 assert.Equal(t, 0, len(ns)) 326 327 ns = z.RangeByScoreWithOpt(1.0, 1.0, RangeOpt{ExcludeMax: true}) 328 assert.Equal(t, 0, len(ns)) 329 ns = z.RangeByScoreWithOpt(1.0, 1.0, RangeOpt{ExcludeMin: true}) 330 assert.Equal(t, 0, len(ns)) 331 ns = z.RangeByScoreWithOpt(1.0, 1.0, RangeOpt{}) 332 assert.Equal(t, 1, len(ns)) 333 } 334 335 func TestSetRevRangeByScoreWithOpt(t *testing.T) { 336 z := New[string](bcomparator.StringComparator()) 337 z.AddB(1.0, "1") 338 z.AddB(1.1, "2") 339 z.AddB(2.0, "3") 340 341 ns := z.RevRangeByScoreWithOpt(2.0, 1.0, RangeOpt{ExcludeMax: true}) 342 assert.Equal(t, 2, len(ns)) 343 assert.Equal(t, 1.1, ns[0].Score) 344 assert.Equal(t, 1.0, ns[1].Score) 345 346 ns = z.RevRangeByScoreWithOpt(2.0, 1.0, RangeOpt{ExcludeMax: true, ExcludeMin: true}) 347 assert.Equal(t, 1, len(ns)) 348 assert.Equal(t, 1.1, ns[0].Score) 349 350 ns = z.RevRangeByScoreWithOpt(2.0, 1.0, RangeOpt{ExcludeMin: true}) 351 assert.Equal(t, 2, len(ns)) 352 assert.Equal(t, 2.0, ns[0].Score) 353 assert.Equal(t, 1.1, ns[1].Score) 354 355 ns = z.RevRangeByScoreWithOpt(1.0, 2.0, RangeOpt{}) 356 assert.Equal(t, 0, len(ns)) 357 ns = z.RevRangeByScoreWithOpt(1.0, 2.0, RangeOpt{ExcludeMin: true}) 358 assert.Equal(t, 0, len(ns)) 359 ns = z.RevRangeByScoreWithOpt(1.0, 2.0, RangeOpt{ExcludeMax: true}) 360 assert.Equal(t, 0, len(ns)) 361 362 ns = z.RevRangeByScoreWithOpt(1.0, 1.0, RangeOpt{ExcludeMax: true}) 363 assert.Equal(t, 0, len(ns)) 364 ns = z.RevRangeByScoreWithOpt(1.0, 1.0, RangeOpt{ExcludeMin: true}) 365 assert.Equal(t, 0, len(ns)) 366 ns = z.RevRangeByScoreWithOpt(1.0, 1.0, RangeOpt{}) 367 assert.Equal(t, 1, len(ns)) 368 } 369 370 func TestSetRevRangeByScore(t *testing.T) { 371 testFloat64SetRangeByScore(t, true) 372 } 373 374 func testFloat64SetRangeByScore(t *testing.T, rev bool) { 375 const N = 1000 376 z := New[string](bcomparator.StringComparator()) 377 for i := 0; i < N; i++ { 378 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 379 } 380 381 min, max := func(a, b float64) (float64, float64) { 382 if a < b { 383 return a, b 384 } else { 385 return b, a 386 } 387 }(fastrand.Float64(), fastrand.Float64()) 388 389 var ns []Node[string] 390 if rev { 391 ns = z.RevRangeByScore(max, min) 392 } else { 393 ns = z.RangeByScore(min, max) 394 } 395 var prev *float64 396 for _, n := range ns { 397 assert.LessOrEqual(t, min, n.Score) 398 assert.GreaterOrEqual(t, max, n.Score) 399 if prev != nil { 400 if rev { 401 assert.True(t, *prev >= n.Score) 402 } else { 403 assert.True(t, *prev <= n.Score) 404 } 405 } 406 prev = &n.Score 407 } 408 } 409 410 func TestSetCountWithOpt(t *testing.T) { 411 testFloat64SetCountWithOpt(t, RangeOpt{}) 412 testFloat64SetCountWithOpt(t, RangeOpt{true, true}) 413 testFloat64SetCountWithOpt(t, RangeOpt{true, false}) 414 testFloat64SetCountWithOpt(t, RangeOpt{false, true}) 415 } 416 417 func testFloat64SetCountWithOpt(t *testing.T, opt RangeOpt) { 418 const N = 1000 419 z := New[string](bcomparator.StringComparator()) 420 for i := 0; i < N; i++ { 421 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 422 } 423 424 min, max := func(a, b float64) (float64, float64) { 425 if a < b { 426 return a, b 427 } else { 428 return b, a 429 } 430 }(fastrand.Float64(), fastrand.Float64()) 431 432 n := z.CountWithOpt(min, max, opt) 433 actualN := 0 434 for _, n := range z.Range(0, -1) { 435 if opt.ExcludeMin { 436 if n.Score <= min { 437 continue 438 } 439 } else { 440 if n.Score < min { 441 continue 442 } 443 } 444 if opt.ExcludeMax { 445 if n.Score >= max { 446 continue 447 } 448 } else { 449 if n.Score > max { 450 continue 451 } 452 } 453 actualN++ 454 } 455 assert.Equal(t, actualN, n) 456 } 457 458 func TestSetRemoveRangeByRank(t *testing.T) { 459 const N = 1000 460 z := New[string](bcomparator.StringComparator()) 461 for i := 0; i < N; i++ { 462 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 463 } 464 465 start, stop := func(a, b int) (int, int) { 466 if a < b { 467 return a, b 468 } else { 469 return b, a 470 } 471 }(fastrand.Intn(N), fastrand.Intn(N)) 472 473 expectNs := z.Range(start, stop) 474 actualNs := z.RemoveRangeByRank(start, stop) 475 assert.Equal(t, expectNs, actualNs) 476 477 // test whether removed 478 for _, n := range actualNs { 479 assert.False(t, z.Contains(n.Value)) 480 } 481 assert.Equal(t, N, z.Len()+len(actualNs)) 482 } 483 484 func TestSetRemoveRangeByScoreWithOpt(t *testing.T) { 485 testFloat64SetRemoveRangeByScoreWithOpt(t, RangeOpt{}) 486 testFloat64SetRemoveRangeByScoreWithOpt(t, RangeOpt{true, true}) 487 testFloat64SetRemoveRangeByScoreWithOpt(t, RangeOpt{true, false}) 488 testFloat64SetRemoveRangeByScoreWithOpt(t, RangeOpt{false, false}) 489 } 490 491 func testFloat64SetRemoveRangeByScoreWithOpt(t *testing.T, opt RangeOpt) { 492 const N = 1000 493 z := New[string](bcomparator.StringComparator()) 494 for i := 0; i < N; i++ { 495 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 496 } 497 498 min, max := func(a, b float64) (float64, float64) { 499 if a < b { 500 return a, b 501 } else { 502 return b, a 503 } 504 }(fastrand.Float64(), fastrand.Float64()) 505 506 expectNs := z.RangeByScoreWithOpt(min, max, opt) 507 actualNs := z.RemoveRangeByScoreWithOpt(min, max, opt) 508 assert.Equal(t, expectNs, actualNs) 509 510 // test whether removed 511 for _, n := range actualNs { 512 assert.False(t, z.Contains(n.Value)) 513 } 514 assert.Equal(t, N, z.Len()+len(actualNs)) 515 } 516 517 func TestUnionFloat64(t *testing.T) { 518 var zs []*Set[string] 519 for i := 0; i < 10; i++ { 520 z := New[string](bcomparator.StringComparator()) 521 for j := 0; j < 100; j++ { 522 if fastrand.Float64() > 0.8 { 523 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 524 } 525 } 526 zs = append(zs, z) 527 } 528 z := Union(bcomparator.StringComparator(), zs...) 529 for _, n := range z.Range(0, z.Len()-1) { 530 var expectScore float64 531 for i := 0; i < 10; i++ { 532 s, _ := zs[i].Score(n.Value) 533 expectScore += s 534 } 535 assert.Equal(t, expectScore, n.Score) 536 } 537 } 538 539 func TestUnionFloat64_Empty(t *testing.T) { 540 z := Union(bcomparator.StringComparator()) 541 assert.Zero(t, z.Len()) 542 } 543 544 func TestInterFloat64(t *testing.T) { 545 var zs []*Set[string] 546 for i := 0; i < 10; i++ { 547 z := New[string](bcomparator.StringComparator()) 548 for j := 0; j < 10; j++ { 549 if fastrand.Float64() > 0.8 { 550 z.AddB(fastrand.Float64(), fmt.Sprint(i)) 551 } 552 } 553 zs = append(zs, z) 554 } 555 z := Inter(bcomparator.StringComparator(), zs...) 556 for _, n := range z.Range(0, z.Len()-1) { 557 var expectScore float64 558 for i := 0; i < 10; i++ { 559 s, ok := zs[i].Score(n.Value) 560 assert.True(t, ok) 561 expectScore += s 562 } 563 assert.Equal(t, expectScore, n.Score) 564 } 565 } 566 567 func TestInterFloat64_Empty(t *testing.T) { 568 z := Inter(bcomparator.StringComparator()) 569 assert.Zero(t, z.Len()) 570 } 571 572 func TestInterFloat64_Simple(t *testing.T) { 573 z1 := New[string](bcomparator.StringComparator()) 574 z1.AddB(0, "1") 575 z2 := New[string](bcomparator.StringComparator()) 576 z2.AddB(0, "1") 577 z3 := New[string](bcomparator.StringComparator()) 578 z3.AddB(0, "2") 579 580 z := Inter(bcomparator.StringComparator(), z1, z2, z3) 581 assert.Zero(t, z.Len()) 582 }