github.com/ledgerwatch/erigon-lib@v1.0.0/recsplit/eliasfano32/elias_fano_test.go (about)

     1  /*
     2     Copyright 2022 Erigon contributors
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package eliasfano32
    18  
    19  import (
    20  	"bytes"
    21  	"math"
    22  	"math/bits"
    23  	"testing"
    24  
    25  	"github.com/ledgerwatch/erigon-lib/kv/iter"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestEliasFanoSeek(t *testing.T) {
    31  	count := uint64(1_000_000)
    32  	maxOffset := (count - 1) * 123
    33  	ef := NewEliasFano(count, maxOffset)
    34  	vals := make([]uint64, 0, count)
    35  	for offset := uint64(0); offset < count; offset++ {
    36  		val := offset * 123
    37  		vals = append(vals, val)
    38  		ef.AddOffset(val)
    39  	}
    40  	ef.Build()
    41  
    42  	t.Run("iter match vals", func(t *testing.T) {
    43  		it := ef.Iterator()
    44  		for i := 0; it.HasNext(); i++ {
    45  			n, err := it.Next()
    46  			require.NoError(t, err)
    47  			require.Equal(t, int(vals[i]), int(n))
    48  		}
    49  	})
    50  	t.Run("iter grow", func(t *testing.T) {
    51  		it := ef.Iterator()
    52  		prev, _ := it.Next()
    53  		for it.HasNext() {
    54  			n, _ := it.Next()
    55  			require.GreaterOrEqual(t, int(n), int(prev))
    56  		}
    57  	})
    58  
    59  	{
    60  		v2, ok2 := ef.Search(ef.Max())
    61  		require.True(t, ok2, v2)
    62  		require.Equal(t, ef.Max(), v2)
    63  		it := ef.Iterator()
    64  		//it.SeekDeprecated(ef.Max())
    65  		for i := 0; i < int(ef.Count()-1); i++ {
    66  			it.Next()
    67  		}
    68  		//save all fields values
    69  		//v1, v2, v3, v4, v5 := it.upperIdx, it.upperMask, it.lowerIdx, it.upper, it.idx
    70  		// seek to same item and check new fields
    71  		it.Seek(ef.Max())
    72  		//require.Equal(t, int(v1), int(it.upperIdx))
    73  		//require.Equal(t, int(v3), int(it.lowerIdx))
    74  		//require.Equal(t, int(v5), int(it.idx))
    75  		//require.Equal(t, bits.TrailingZeros64(v2), bits.TrailingZeros64(it.upperMask))
    76  		//require.Equal(t, int(v4), int(it.upper))
    77  
    78  		require.True(t, it.HasNext(), v2)
    79  		itV, err := it.Next()
    80  		require.NoError(t, err)
    81  		require.Equal(t, int(ef.Max()), int(itV))
    82  	}
    83  
    84  	{
    85  		v2, ok2 := ef.Search(ef.Min())
    86  		require.True(t, ok2, v2)
    87  		require.Equal(t, int(ef.Min()), int(v2))
    88  		it := ef.Iterator()
    89  		it.Seek(ef.Min())
    90  		require.True(t, it.HasNext(), v2)
    91  		itV, err := it.Next()
    92  		require.NoError(t, err)
    93  		require.Equal(t, int(ef.Min()), int(itV))
    94  	}
    95  
    96  	{
    97  		v2, ok2 := ef.Search(0)
    98  		require.True(t, ok2, v2)
    99  		require.Equal(t, int(ef.Min()), int(v2))
   100  		it := ef.Iterator()
   101  		it.Seek(0)
   102  		require.True(t, it.HasNext(), v2)
   103  		itV, err := it.Next()
   104  		require.NoError(t, err)
   105  		require.Equal(t, int(ef.Min()), int(itV))
   106  	}
   107  
   108  	{
   109  		v2, ok2 := ef.Search(math.MaxUint32)
   110  		require.False(t, ok2, v2)
   111  		it := ef.Iterator()
   112  		it.Seek(math.MaxUint32)
   113  		require.False(t, it.HasNext(), v2)
   114  	}
   115  
   116  	{
   117  		v2, ok2 := ef.Search((count+1)*123 + 1)
   118  		require.False(t, ok2, v2)
   119  		it := ef.Iterator()
   120  		it.Seek((count+1)*123 + 1)
   121  		require.False(t, it.HasNext(), v2)
   122  	}
   123  
   124  	t.Run("search and seek can't return smaller", func(t *testing.T) {
   125  		for i := uint64(0); i < count; i++ {
   126  			search := i * 123
   127  			v, ok2 := ef.Search(search)
   128  			require.True(t, ok2, search)
   129  			require.GreaterOrEqual(t, int(v), int(search))
   130  			it := ef.Iterator()
   131  			it.Seek(search)
   132  			itV, err := it.Next()
   133  			require.NoError(t, err)
   134  			require.GreaterOrEqual(t, int(itV), int(search), int(v))
   135  		}
   136  	})
   137  
   138  }
   139  
   140  func TestEliasFano(t *testing.T) {
   141  	offsets := []uint64{1, 4, 6, 8, 10, 14, 16, 19, 22, 34, 37, 39, 41, 43, 48, 51, 54, 58, 62}
   142  	count := uint64(len(offsets))
   143  	maxOffset := offsets[0]
   144  	for _, offset := range offsets {
   145  		if offset > maxOffset {
   146  			maxOffset = offset
   147  		}
   148  	}
   149  	ef := NewEliasFano(count, maxOffset)
   150  	for _, offset := range offsets {
   151  		ef.AddOffset(offset)
   152  	}
   153  	ef.Build()
   154  	for i, offset := range offsets {
   155  		offset1 := ef.Get(uint64(i))
   156  		assert.Equal(t, offset, offset1, "offset")
   157  	}
   158  	v, ok := ef.Search(37)
   159  	assert.True(t, ok, "search1")
   160  	assert.Equal(t, uint64(37), v, "search1")
   161  	v, ok = ef.Search(0)
   162  	assert.True(t, ok, "search2")
   163  	assert.Equal(t, uint64(1), v, "search2")
   164  	_, ok = ef.Search(100)
   165  	assert.False(t, ok, "search3")
   166  	v, ok = ef.Search(11)
   167  	assert.True(t, ok, "search4")
   168  	assert.Equal(t, uint64(14), v, "search4")
   169  
   170  	buf := bytes.NewBuffer(nil)
   171  	ef.Write(buf)
   172  	assert.Equal(t, ef.AppendBytes(nil), buf.Bytes())
   173  
   174  	ef2, _ := ReadEliasFano(buf.Bytes())
   175  	assert.Equal(t, ef.Min(), ef2.Min())
   176  	assert.Equal(t, ef.Max(), ef2.Max())
   177  	assert.Equal(t, ef2.Max(), Max(buf.Bytes()))
   178  	assert.Equal(t, ef2.Min(), Min(buf.Bytes()))
   179  	assert.Equal(t, ef2.Count(), Count(buf.Bytes()))
   180  }
   181  
   182  func TestIterator(t *testing.T) {
   183  	offsets := []uint64{1, 4, 6, 8, 10, 14, 16, 19, 22, 34, 37, 39, 41, 43, 48, 51, 54, 58, 62}
   184  	count := uint64(len(offsets))
   185  	maxOffset := offsets[0]
   186  	for _, offset := range offsets {
   187  		if offset > maxOffset {
   188  			maxOffset = offset
   189  		}
   190  	}
   191  	ef := NewEliasFano(count, maxOffset)
   192  	for _, offset := range offsets {
   193  		ef.AddOffset(offset)
   194  	}
   195  	ef.Build()
   196  	t.Run("scan", func(t *testing.T) {
   197  		efi := ef.Iterator()
   198  		i := 0
   199  		var values []uint64
   200  		for efi.HasNext() {
   201  			v, _ := efi.Next()
   202  			values = append(values, v)
   203  			assert.Equal(t, offsets[i], v, "iter")
   204  			i++
   205  		}
   206  		iter.ExpectEqualU64(t, iter.ReverseArray(values), ef.ReverseIterator())
   207  	})
   208  
   209  	t.Run("seek", func(t *testing.T) {
   210  		iter2 := ef.Iterator()
   211  		iter2.Seek(2)
   212  		n, err := iter2.Next()
   213  		require.NoError(t, err)
   214  		require.Equal(t, 4, int(n))
   215  
   216  		iter2.Seek(5)
   217  		n, err = iter2.Next()
   218  		require.NoError(t, err)
   219  		require.Equal(t, 6, int(n))
   220  
   221  		iter2.Seek(62)
   222  		n, err = iter2.Next()
   223  		require.NoError(t, err)
   224  		require.Equal(t, 62, int(n))
   225  
   226  		iter2.Seek(1024)
   227  		require.False(t, iter2.HasNext())
   228  	})
   229  }
   230  
   231  func TestIteratorAndSeekAreBasedOnSameFields(t *testing.T) {
   232  	vals := []uint64{1, 123, 789}
   233  	ef := NewEliasFano(uint64(len(vals)), vals[len(vals)-1])
   234  	for _, v := range vals {
   235  		ef.AddOffset(v)
   236  	}
   237  	ef.Build()
   238  
   239  	for i := range vals {
   240  		checkSeek(t, i, ef, vals)
   241  	}
   242  }
   243  
   244  func checkSeek(t *testing.T, j int, ef *EliasFano, vals []uint64) {
   245  	t.Helper()
   246  	efi := ef.Iterator()
   247  	// drain iterator to given item
   248  	for i := 0; i < j; i++ {
   249  		efi.Next()
   250  	}
   251  	//save all fields values
   252  	v1, v2, v3, v4, v5 := efi.upperIdx, efi.upperMask, efi.lowerIdx, efi.upper, efi.idx
   253  	// seek to same item and check new fields
   254  	efi.Seek(vals[j])
   255  	require.Equal(t, int(v1), int(efi.upperIdx))
   256  	require.Equal(t, int(v3), int(efi.lowerIdx))
   257  	require.Equal(t, int(v4), int(efi.upper))
   258  	require.Equal(t, int(v5), int(efi.idx))
   259  	require.Equal(t, bits.TrailingZeros64(v2), bits.TrailingZeros64(efi.upperMask))
   260  }
   261  
   262  func BenchmarkName(b *testing.B) {
   263  	count := uint64(1_000_000)
   264  	maxOffset := (count - 1) * 123
   265  	ef := NewEliasFano(count, maxOffset)
   266  	for offset := uint64(0); offset < count; offset++ {
   267  		ef.AddOffset(offset * 123)
   268  	}
   269  	ef.Build()
   270  	b.Run("next", func(b *testing.B) {
   271  		for i := 0; i < b.N; i++ {
   272  			it := ef.Iterator()
   273  			for it.HasNext() {
   274  				n, _ := it.Next()
   275  				if n > 1_000_000 {
   276  					break
   277  				}
   278  			}
   279  		}
   280  	})
   281  	b.Run("seek", func(b *testing.B) {
   282  		for i := 0; i < b.N; i++ {
   283  			it := ef.Iterator()
   284  			it.SeekDeprecated(1_000_000)
   285  		}
   286  	})
   287  	b.Run("seek2", func(b *testing.B) {
   288  		for i := 0; i < b.N; i++ {
   289  			it := ef.Iterator()
   290  			it.Seek(1_000_000)
   291  		}
   292  	})
   293  }