github.com/bytedance/gopkg@v0.0.0-20240514070511-01b2cbcf35e1/collection/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  	"math/rand"
    20  	"sort"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/bytedance/gopkg/lang/fastrand"
    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 TestFloat64SetRemoveRangeByScore(t *testing.T) {
   497  	const N = 1000
   498  	z := NewFloat64()
   499  	for i := 0; i < N; i++ {
   500  		z.Add(fastrand.Float64(), fmt.Sprint(i))
   501  	}
   502  
   503  	min, max := func(a, b float64) (float64, float64) {
   504  		if a < b {
   505  			return a, b
   506  		} else {
   507  			return b, a
   508  		}
   509  	}(fastrand.Float64(), fastrand.Float64())
   510  
   511  	expectNs := z.RangeByScore(min, max)
   512  	actualNs := z.RemoveRangeByScore(min, max)
   513  	assert.Equal(t, expectNs, actualNs)
   514  
   515  	// test whether removed
   516  	for _, n := range actualNs {
   517  		assert.False(t, z.Contains(n.Value))
   518  	}
   519  	assert.Equal(t, N, z.Len()+len(actualNs))
   520  }
   521  
   522  func TestFloat64SetRemoveRangeByScoreWithOpt(t *testing.T) {
   523  	testFloat64SetRemoveRangeByScoreWithOpt(t, RangeOpt{})
   524  	testFloat64SetRemoveRangeByScoreWithOpt(t, RangeOpt{true, true})
   525  	testFloat64SetRemoveRangeByScoreWithOpt(t, RangeOpt{true, false})
   526  	testFloat64SetRemoveRangeByScoreWithOpt(t, RangeOpt{false, false})
   527  }
   528  
   529  func testFloat64SetRemoveRangeByScoreWithOpt(t *testing.T, opt RangeOpt) {
   530  	const N = 1000
   531  	z := NewFloat64()
   532  	for i := 0; i < N; i++ {
   533  		z.Add(fastrand.Float64(), fmt.Sprint(i))
   534  	}
   535  
   536  	min, max := func(a, b float64) (float64, float64) {
   537  		if a < b {
   538  			return a, b
   539  		} else {
   540  			return b, a
   541  		}
   542  	}(fastrand.Float64(), fastrand.Float64())
   543  
   544  	expectNs := z.RangeByScoreWithOpt(min, max, opt)
   545  	actualNs := z.RemoveRangeByScoreWithOpt(min, max, opt)
   546  	assert.Equal(t, expectNs, actualNs)
   547  
   548  	// test whether removed
   549  	for _, n := range actualNs {
   550  		assert.False(t, z.Contains(n.Value))
   551  	}
   552  	assert.Equal(t, N, z.Len()+len(actualNs))
   553  }
   554  
   555  func TestUnionFloat64(t *testing.T) {
   556  	var zs []*Float64Set
   557  	for i := 0; i < 10; i++ {
   558  		z := NewFloat64()
   559  		for j := 0; j < 100; j++ {
   560  			if fastrand.Float64() > 0.8 {
   561  				z.Add(fastrand.Float64(), fmt.Sprint(i))
   562  			}
   563  		}
   564  		zs = append(zs, z)
   565  	}
   566  	z := UnionFloat64(zs...)
   567  	for _, n := range z.Range(0, z.Len()-1) {
   568  		var expectScore float64
   569  		for i := 0; i < 10; i++ {
   570  			s, _ := zs[i].Score(n.Value)
   571  			expectScore += s
   572  		}
   573  		assert.Equal(t, expectScore, n.Score)
   574  	}
   575  }
   576  
   577  func TestUnionFloat64_Empty(t *testing.T) {
   578  	z := UnionFloat64()
   579  	assert.Zero(t, z.Len())
   580  }
   581  
   582  func TestInterFloat64(t *testing.T) {
   583  	var zs []*Float64Set
   584  	for i := 0; i < 10; i++ {
   585  		z := NewFloat64()
   586  		for j := 0; j < 10; j++ {
   587  			if fastrand.Float64() > 0.8 {
   588  				z.Add(fastrand.Float64(), fmt.Sprint(i))
   589  			}
   590  		}
   591  		zs = append(zs, z)
   592  	}
   593  	z := InterFloat64(zs...)
   594  	for _, n := range z.Range(0, z.Len()-1) {
   595  		var expectScore float64
   596  		for i := 0; i < 10; i++ {
   597  			s, ok := zs[i].Score(n.Value)
   598  			assert.True(t, ok)
   599  			expectScore += s
   600  		}
   601  		assert.Equal(t, expectScore, n.Score)
   602  	}
   603  }
   604  
   605  func TestInterFloat64_Empty(t *testing.T) {
   606  	z := InterFloat64()
   607  	assert.Zero(t, z.Len())
   608  }
   609  
   610  func TestInterFloat64_Simple(t *testing.T) {
   611  	z1 := NewFloat64()
   612  	z1.Add(0, "1")
   613  	z2 := NewFloat64()
   614  	z2.Add(0, "1")
   615  	z3 := NewFloat64()
   616  	z3.Add(0, "2")
   617  
   618  	z := InterFloat64(z1, z2, z3)
   619  	assert.Zero(t, z.Len())
   620  }