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