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  }