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  }