github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/tree/rbtree_test.go (about)

     1  package tree
     2  
     3  import (
     4  	randv2 "math/rand/v2"
     5  	"sort"
     6  	"testing"
     7  
     8  	"github.com/benz9527/xboot/lib/id"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestNilNode(t *testing.T) {
    13  	var nilNode RBNode[uint64, uint64] = nil
    14  	require.True(t, nilNode == nil)
    15  
    16  	var nilNode2 *rbNode[uint64, uint64] = nil
    17  	nilNode = nilNode2
    18  	require.True(t, nilNode != nil)
    19  	require.Nil(t, nilNode)
    20  }
    21  
    22  func TestRbtreeLeftAndRightRotate_Pred(t *testing.T) {
    23  	type checkData struct {
    24  		color RBColor
    25  		key   uint64
    26  	}
    27  
    28  	tree := &rbTree[uint64, uint64]{
    29  		isDesc:         false,
    30  		isRmBorrowSucc: false,
    31  	}
    32  
    33  	tree.Insert(52, 1)
    34  	expected := []checkData{
    35  		{Black, 52},
    36  	}
    37  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
    38  		require.Equal(t, expected[idx].color, color)
    39  		require.Equal(t, expected[idx].key, key)
    40  		return true
    41  	})
    42  	require.NoError(t, RedViolationValidate[uint64, uint64](tree))
    43  	require.NoError(t, BlackViolationValidate[uint64, uint64](tree))
    44  
    45  	tree.Insert(47, 1)
    46  	expected = []checkData{
    47  		{Red, 47}, {Black, 52},
    48  	}
    49  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
    50  		require.Equal(t, expected[idx].color, color)
    51  		require.Equal(t, expected[idx].key, key)
    52  		return true
    53  	})
    54  	require.NoError(t, RedViolationValidate[uint64, uint64](tree))
    55  
    56  	tree.Insert(3, 1)
    57  	expected = []checkData{
    58  		{Red, 3}, {Black, 47}, {Red, 52},
    59  	}
    60  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
    61  		require.Equal(t, expected[idx].color, color)
    62  		require.Equal(t, expected[idx].key, key)
    63  		return true
    64  	})
    65  	require.NoError(t, RedViolationValidate[uint64, uint64](tree))
    66  	require.NoError(t, BlackViolationValidate[uint64, uint64](tree))
    67  
    68  	tree.Insert(35, 1)
    69  	expected = []checkData{
    70  		{Black, 3},
    71  		{Red, 35},
    72  		{Black, 47},
    73  		{Black, 52},
    74  	}
    75  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
    76  		require.Equal(t, expected[idx].color, color)
    77  		require.Equal(t, expected[idx].key, key)
    78  		return true
    79  	})
    80  	require.NoError(t, RedViolationValidate[uint64, uint64](tree))
    81  	require.NoError(t, BlackViolationValidate[uint64, uint64](tree))
    82  
    83  	tree.Insert(24, 1)
    84  	expected = []checkData{
    85  		{Red, 3},
    86  		{Black, 24},
    87  		{Red, 35},
    88  		{Black, 47},
    89  		{Black, 52},
    90  	}
    91  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
    92  		require.Equal(t, expected[idx].color, color)
    93  		require.Equal(t, expected[idx].key, key)
    94  		return true
    95  	})
    96  	require.NoError(t, RedViolationValidate[uint64, uint64](tree))
    97  	require.NoError(t, BlackViolationValidate[uint64, uint64](tree))
    98  
    99  	// remove
   100  
   101  	x, err := tree.Remove(24)
   102  	require.NoError(t, err)
   103  	require.Equal(t, uint64(24), x.Key())
   104  	expected = []checkData{
   105  		{Black, 3},
   106  		{Red, 35},
   107  		{Black, 47},
   108  		{Black, 52},
   109  	}
   110  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   111  		require.Equal(t, expected[idx].color, color)
   112  		require.Equal(t, expected[idx].key, key)
   113  		return true
   114  	})
   115  	require.NoError(t, RedViolationValidate[uint64, uint64](tree))
   116  	require.NoError(t, BlackViolationValidate[uint64, uint64](tree))
   117  
   118  	x, err = tree.Remove(47)
   119  	require.NoError(t, err)
   120  	require.Equal(t, uint64(47), x.Key())
   121  	expected = []checkData{
   122  		{Black, 3},
   123  		{Black, 35},
   124  		{Black, 52},
   125  	}
   126  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   127  		require.Equal(t, expected[idx].color, color)
   128  		require.Equal(t, expected[idx].key, key)
   129  		return true
   130  	})
   131  	require.NoError(t, RedViolationValidate[uint64, uint64](tree))
   132  	require.NoError(t, BlackViolationValidate[uint64, uint64](tree))
   133  
   134  	x, err = tree.Remove(52)
   135  	require.NoError(t, err)
   136  	require.Equal(t, uint64(52), x.Key())
   137  	expected = []checkData{
   138  		{Red, 3}, {Black, 35},
   139  	}
   140  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   141  		require.Equal(t, expected[idx].color, color)
   142  		require.Equal(t, expected[idx].key, key)
   143  		return true
   144  	})
   145  	require.NoError(t, RedViolationValidate[uint64, uint64](tree))
   146  	require.NoError(t, BlackViolationValidate[uint64, uint64](tree))
   147  
   148  	x, err = tree.Remove(3)
   149  	require.NoError(t, err)
   150  	require.Equal(t, uint64(3), x.Key())
   151  	expected = []checkData{
   152  		{Black, 35},
   153  	}
   154  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   155  		require.Equal(t, expected[idx].color, color)
   156  		require.Equal(t, expected[idx].key, key)
   157  		return true
   158  	})
   159  	require.NoError(t, RedViolationValidate[uint64, uint64](tree))
   160  	require.NoError(t, BlackViolationValidate[uint64, uint64](tree))
   161  
   162  	x, err = tree.Remove(35)
   163  	require.NoError(t, err)
   164  	require.Equal(t, uint64(35), x.Key())
   165  	require.Equal(t, int64(0), tree.Len())
   166  }
   167  
   168  func TestRbtree_RemoveMin(t *testing.T) {
   169  	type checkData struct {
   170  		color RBColor
   171  		key   uint64
   172  	}
   173  
   174  	tree := &rbTree[uint64, uint64]{
   175  		isDesc:         false,
   176  		isRmBorrowSucc: false,
   177  	}
   178  
   179  	tree.Insert(52, 1)
   180  	tree.Insert(47, 1)
   181  	tree.Insert(3, 1)
   182  	tree.Insert(35, 1)
   183  	tree.Insert(24, 1)
   184  	expected := []checkData{
   185  		{Red, 3},
   186  		{Black, 24},
   187  		{Red, 35},
   188  		{Black, 47},
   189  		{Black, 52},
   190  	}
   191  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   192  		require.Equal(t, expected[idx].color, color)
   193  		require.Equal(t, expected[idx].key, key)
   194  		return true
   195  	})
   196  
   197  	// remove min
   198  
   199  	x, err := tree.RemoveMin()
   200  	require.NoError(t, err)
   201  	require.Equal(t, uint64(3), x.Key())
   202  	expected = []checkData{
   203  		{Black, 24},
   204  		{Red, 35},
   205  		{Black, 47},
   206  		{Black, 52},
   207  	}
   208  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   209  		require.Equal(t, expected[idx].color, color)
   210  		require.Equal(t, expected[idx].key, key)
   211  		return true
   212  	})
   213  	require.NoError(t, RedViolationValidate(tree))
   214  	require.NoError(t, BlackViolationValidate(tree))
   215  
   216  	x, err = tree.RemoveMin()
   217  	require.NoError(t, err)
   218  	require.Equal(t, uint64(24), x.Key())
   219  	expected = []checkData{
   220  		{Black, 35},
   221  		{Black, 47},
   222  		{Black, 52},
   223  	}
   224  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   225  		require.Equal(t, expected[idx].color, color)
   226  		require.Equal(t, expected[idx].key, key)
   227  		return true
   228  	})
   229  	require.NoError(t, RedViolationValidate(tree))
   230  	require.NoError(t, BlackViolationValidate(tree))
   231  
   232  	x, err = tree.RemoveMin()
   233  	require.NoError(t, err)
   234  	require.Equal(t, uint64(35), x.Key())
   235  	expected = []checkData{
   236  		{Black, 47}, {Red, 52},
   237  	}
   238  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   239  		require.Equal(t, expected[idx].color, color)
   240  		require.Equal(t, expected[idx].key, key)
   241  		return true
   242  	})
   243  	require.NoError(t, RedViolationValidate(tree))
   244  	require.NoError(t, BlackViolationValidate(tree))
   245  
   246  	x, err = tree.RemoveMin()
   247  	require.NoError(t, err)
   248  	require.Equal(t, uint64(47), x.Key())
   249  	expected = []checkData{
   250  		{Black, 52},
   251  	}
   252  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   253  		require.Equal(t, expected[idx].color, color)
   254  		require.Equal(t, expected[idx].key, key)
   255  		return true
   256  	})
   257  	require.NoError(t, RedViolationValidate(tree))
   258  	require.NoError(t, BlackViolationValidate(tree))
   259  
   260  	x, err = tree.RemoveMin()
   261  	require.NoError(t, err)
   262  	require.Equal(t, uint64(52), x.Key())
   263  	require.Equal(t, int64(0), tree.Len())
   264  }
   265  
   266  func rbtreeRandomInsertAndRemoveSequentialNumberRunCore(t *testing.T, rbRmBySucc bool) {
   267  	total := uint64(1000)
   268  	insertTotal := uint64(float64(total) * 0.8)
   269  	removeTotal := uint64(float64(total) * 0.2)
   270  
   271  	tree := &rbTree[uint64, uint64]{
   272  		isDesc:         false,
   273  		isRmBorrowSucc: rbRmBySucc,
   274  	}
   275  
   276  	for i := uint64(0); i < insertTotal; i++ {
   277  		tree.Insert(i, 1)
   278  		require.NoError(t, RedViolationValidate(tree))
   279  		require.NoError(t, BlackViolationValidate(tree))
   280  	}
   281  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   282  		require.Equal(t, uint64(idx), key)
   283  		return true
   284  	})
   285  
   286  	for i := insertTotal; i < removeTotal+insertTotal; i++ {
   287  		tree.Insert(i, 1)
   288  		require.NoError(t, RedViolationValidate(tree))
   289  		require.NoError(t, BlackViolationValidate(tree))
   290  	}
   291  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   292  		require.Equal(t, uint64(idx), key)
   293  		return true
   294  	})
   295  
   296  	for i := insertTotal; i < removeTotal+insertTotal; i++ {
   297  		if i == 92 {
   298  			x := tree.Search(tree.root, func(node RBNode[uint64, uint64]) int64 {
   299  				if i == node.Key() {
   300  					return 0
   301  				} else if i < node.Key() {
   302  					return -1
   303  				}
   304  				return 1
   305  			})
   306  			require.Equal(t, uint64(92), x.Key())
   307  		}
   308  		x, err := tree.Remove(i)
   309  		require.NoError(t, err)
   310  		require.Equal(t, i, x.Key())
   311  		require.NoError(t, RedViolationValidate(tree))
   312  		require.NoError(t, BlackViolationValidate(tree))
   313  	}
   314  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   315  		require.Equal(t, uint64(idx), key)
   316  		return true
   317  	})
   318  }
   319  
   320  func TestRbtreeRandomInsertAndRemove_SequentialNumber(t *testing.T) {
   321  	type testcase struct {
   322  		name       string
   323  		rbRmBySucc bool
   324  	}
   325  	testcases := []testcase{
   326  		{
   327  			name: "rm by pred",
   328  		},
   329  		{
   330  			name:       "rm by succ",
   331  			rbRmBySucc: true,
   332  		},
   333  	}
   334  	for _, tc := range testcases {
   335  		t.Run(tc.name, func(tt *testing.T) {
   336  			rbtreeRandomInsertAndRemoveSequentialNumberRunCore(tt, tc.rbRmBySucc)
   337  		})
   338  	}
   339  }
   340  
   341  func TestRBTreeRandomInsertAndRemove_SequentialNumber_Release(t *testing.T) {
   342  	insertTotal := uint64(100_000)
   343  
   344  	tree := &rbTree[uint64, uint64]{
   345  		isDesc:         false,
   346  		isRmBorrowSucc: false,
   347  	}
   348  
   349  	rand := uint64(randv2.Uint32() % 1_000)
   350  	for i := uint64(0); i < insertTotal; i++ {
   351  		tree.Insert(i, 1)
   352  		if i%1000 == rand {
   353  			require.NoError(t, RedViolationValidate(tree))
   354  			require.NoError(t, BlackViolationValidate(tree))
   355  		}
   356  	}
   357  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   358  		require.Equal(t, uint64(idx), key)
   359  		return true
   360  	})
   361  	tree.Release()
   362  	require.Equal(t, int64(0), tree.Len())
   363  	require.Nil(t, tree.Root())
   364  }
   365  
   366  func TestRbtreeRandomInsertAndRemove_ReverseSequentialNumber(t *testing.T) {
   367  	total := int64(10000)
   368  	insertTotal := int64(float64(total) * 0.8)
   369  	removeTotal := int64(float64(total) * 0.2)
   370  
   371  	tree := &rbTree[int64, uint64]{
   372  		isDesc:         true,
   373  		isRmBorrowSucc: false,
   374  	}
   375  
   376  	rand := int64(randv2.Uint32() % 1_000)
   377  	for i := insertTotal - 1; i >= 0; i-- {
   378  		tree.Insert(i, 1)
   379  		if i%1000 == rand {
   380  			require.NoError(t, RedViolationValidate(tree))
   381  			require.NoError(t, BlackViolationValidate(tree))
   382  		}
   383  	}
   384  	tree.Foreach(func(idx int64, color RBColor, key int64, val uint64) bool {
   385  		require.Equal(t, int64(insertTotal-1-idx), key)
   386  		return true
   387  	})
   388  
   389  	for i := removeTotal + insertTotal - 1; i >= insertTotal; i-- {
   390  		tree.Insert(i, 1)
   391  	}
   392  	tree.Foreach(func(idx int64, color RBColor, key int64, val uint64) bool {
   393  		require.Equal(t, int64(removeTotal+insertTotal-1-idx), key)
   394  		return true
   395  	})
   396  
   397  	for i := insertTotal; i < removeTotal+insertTotal; i++ {
   398  		if i == 92 {
   399  			x := tree.Search(tree.root, func(x RBNode[int64, uint64]) int64 {
   400  				if i == x.Key() {
   401  					return 0
   402  				} else if i < x.Key() {
   403  					return 1
   404  				}
   405  				return -1
   406  			})
   407  			require.Equal(t, int64(92), x.Key())
   408  		}
   409  		x, err := tree.Remove(i)
   410  		require.NoError(t, err)
   411  		require.Equal(t, i, x.Key())
   412  	}
   413  	tree.Foreach(func(idx int64, color RBColor, key int64, val uint64) bool {
   414  		require.Equal(t, int64(insertTotal-1-idx), key)
   415  		return true
   416  	})
   417  }
   418  
   419  func rbtreeRandomInsertAndRemove_RandomMonoNumberRunCore(t *testing.T, total uint64, rbRmBySucc bool, violationCheck bool) {
   420  	insertTotal := uint64(float64(total) * 0.8)
   421  	removeTotal := uint64(float64(total) * 0.2)
   422  
   423  	idGen, _ := id.MonotonicNonZeroID()
   424  	insertElements := make([]uint64, 0, insertTotal)
   425  	removeElements := make([]uint64, 0, removeTotal)
   426  
   427  	ignore := uint32(0)
   428  
   429  	for {
   430  		num := idGen.Number()
   431  		if ignore > 0 {
   432  			ignore--
   433  			continue
   434  		}
   435  		ignore = randv2.Uint32() % 100
   436  		if ignore&0x1 == 0 && uint64(len(insertElements)) < insertTotal {
   437  			insertElements = append(insertElements, num)
   438  		} else if ignore&0x1 == 1 && uint64(len(removeElements)) < removeTotal {
   439  			removeElements = append(removeElements, num)
   440  		}
   441  		if uint64(len(insertElements)) == insertTotal && uint64(len(removeElements)) == removeTotal {
   442  			break
   443  		}
   444  	}
   445  
   446  	shuffle := func(arr []uint64) {
   447  		count := uint32(len(arr) >> 2)
   448  		for i := uint32(0); i < count; i++ {
   449  			j := randv2.Uint32() % (i + 1)
   450  			arr[i], arr[j] = arr[j], arr[i]
   451  		}
   452  	}
   453  
   454  	shuffle(insertElements)
   455  	shuffle(removeElements)
   456  
   457  	tree := &rbTree[uint64, uint64]{
   458  		isDesc:         false,
   459  		isRmBorrowSucc: rbRmBySucc,
   460  	}
   461  
   462  	for i := uint64(0); i < insertTotal; i++ {
   463  		tree.Insert(insertElements[i], i)
   464  		if violationCheck {
   465  			require.NoError(t, RedViolationValidate(tree))
   466  			require.NoError(t, BlackViolationValidate(tree))
   467  		}
   468  	}
   469  	sort.Slice(insertElements, func(i, j int) bool {
   470  		return insertElements[i] < insertElements[j]
   471  	})
   472  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   473  		require.Equal(t, insertElements[idx], key)
   474  		return true
   475  	})
   476  
   477  	for i := uint64(0); i < removeTotal; i++ {
   478  		tree.Insert(removeElements[i], 1)
   479  		if violationCheck {
   480  			require.NoError(t, RedViolationValidate(tree))
   481  			require.NoError(t, BlackViolationValidate(tree))
   482  		}
   483  	}
   484  	require.NoError(t, RedViolationValidate(tree))
   485  	require.NoError(t, BlackViolationValidate(tree))
   486  
   487  	for i := uint64(0); i < removeTotal; i++ {
   488  		x, err := tree.Remove(removeElements[i])
   489  		require.NoError(t, err)
   490  		require.Equalf(t, removeElements[i], x.Key(), "value exp: %d, real: %d\n", removeElements[i], x.Key())
   491  		if violationCheck {
   492  			require.NoError(t, RedViolationValidate(tree))
   493  			require.NoError(t, BlackViolationValidate(tree))
   494  		}
   495  	}
   496  	tree.Foreach(func(idx int64, color RBColor, key uint64, val uint64) bool {
   497  		require.Equal(t, insertElements[idx], key)
   498  		return true
   499  	})
   500  }
   501  
   502  func TestRbtreeRandomInsertAndRemove_RandomMonotonicNumber(t *testing.T) {
   503  	type testcase struct {
   504  		name           string
   505  		rbRmBySucc     bool
   506  		total          uint64
   507  		violationCheck bool
   508  	}
   509  	testcases := []testcase{
   510  		{
   511  			name:  "rm by pred 1000000",
   512  			total: 1000000,
   513  		},
   514  		{
   515  			name:       "rm by succ 1000000",
   516  			rbRmBySucc: true,
   517  			total:      1000000,
   518  		},
   519  		{
   520  			name:           "violation check rm by pred 10000",
   521  			total:          10000,
   522  			violationCheck: true,
   523  		},
   524  		{
   525  			name:           "violation check rm by succ 10000",
   526  			rbRmBySucc:     true,
   527  			total:          10000,
   528  			violationCheck: true,
   529  		},
   530  		{
   531  			name:           "violation check rm by pred 20000",
   532  			total:          20000,
   533  			violationCheck: true,
   534  		},
   535  		{
   536  			name:           "violation check rm by succ 20000",
   537  			rbRmBySucc:     true,
   538  			total:          20000,
   539  			violationCheck: true,
   540  		},
   541  	}
   542  	t.Parallel()
   543  	for _, tc := range testcases {
   544  		t.Run(tc.name, func(tt *testing.T) {
   545  			rbtreeRandomInsertAndRemove_RandomMonoNumberRunCore(tt, tc.total, tc.rbRmBySucc, tc.violationCheck)
   546  		})
   547  	}
   548  }
   549  
   550  func BenchmarkRBTree_Random(b *testing.B) {
   551  	testByBytes := []byte(`abc`)
   552  
   553  	b.StopTimer()
   554  	tree := NewRBTree[int, []byte]()
   555  
   556  	rngArr := make([]int, 0, b.N)
   557  	for i := 0; i < b.N; i++ {
   558  		rngArr = append(rngArr, randv2.Int())
   559  	}
   560  
   561  	b.StartTimer()
   562  	for i := 0; i < b.N; i++ {
   563  		err := tree.Insert(rngArr[i], testByBytes)
   564  		if err != nil {
   565  			panic(err)
   566  		}
   567  	}
   568  }
   569  
   570  func BenchmarkRBTree_Serial(b *testing.B) {
   571  	testByBytes := []byte(`abc`)
   572  
   573  	b.StopTimer()
   574  	tree := NewRBTree[int, []byte]()
   575  
   576  	b.StartTimer()
   577  	for i := 0; i < b.N; i++ {
   578  		tree.Insert(i, testByBytes)
   579  	}
   580  }