github.com/MetalBlockchain/metalgo@v1.11.9/utils/bloom/optimal_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package bloom
     5  
     6  import (
     7  	"fmt"
     8  	"math"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  const largestFloat64LessThan1 float64 = 1 - 1e-16
    15  
    16  func TestOptimalHashes(t *testing.T) {
    17  	tests := []struct {
    18  		numEntries     int
    19  		count          int
    20  		expectedHashes int
    21  	}{
    22  		{ // invalid params
    23  			numEntries:     0,
    24  			count:          1024,
    25  			expectedHashes: minHashes,
    26  		},
    27  		{ // invalid params
    28  			numEntries:     1024,
    29  			count:          0,
    30  			expectedHashes: maxHashes,
    31  		},
    32  		{
    33  			numEntries:     math.MaxInt,
    34  			count:          1,
    35  			expectedHashes: maxHashes,
    36  		},
    37  		{
    38  			numEntries:     1,
    39  			count:          math.MaxInt,
    40  			expectedHashes: minHashes,
    41  		},
    42  		{
    43  			numEntries:     1024,
    44  			count:          1024,
    45  			expectedHashes: 6,
    46  		},
    47  	}
    48  	for _, test := range tests {
    49  		t.Run(fmt.Sprintf("%d_%d", test.numEntries, test.count), func(t *testing.T) {
    50  			hashes := OptimalHashes(test.numEntries, test.count)
    51  			require.Equal(t, test.expectedHashes, hashes)
    52  		})
    53  	}
    54  }
    55  
    56  func TestOptimalEntries(t *testing.T) {
    57  	tests := []struct {
    58  		count                    int
    59  		falsePositiveProbability float64
    60  		expectedEntries          int
    61  	}{
    62  		{ // invalid params
    63  			count:                    0,
    64  			falsePositiveProbability: .5,
    65  			expectedEntries:          minEntries,
    66  		},
    67  		{ // invalid params
    68  			count:                    1,
    69  			falsePositiveProbability: 0,
    70  			expectedEntries:          math.MaxInt,
    71  		},
    72  		{ // invalid params
    73  			count:                    1,
    74  			falsePositiveProbability: 1,
    75  			expectedEntries:          minEntries,
    76  		},
    77  		{
    78  			count:                    math.MaxInt,
    79  			falsePositiveProbability: math.SmallestNonzeroFloat64,
    80  			expectedEntries:          math.MaxInt,
    81  		},
    82  		{
    83  			count:                    1024,
    84  			falsePositiveProbability: largestFloat64LessThan1,
    85  			expectedEntries:          minEntries,
    86  		},
    87  		{
    88  			count:                    1024,
    89  			falsePositiveProbability: .01,
    90  			expectedEntries:          1227,
    91  		},
    92  	}
    93  	for _, test := range tests {
    94  		t.Run(fmt.Sprintf("%d_%f", test.count, test.falsePositiveProbability), func(t *testing.T) {
    95  			entries := OptimalEntries(test.count, test.falsePositiveProbability)
    96  			require.Equal(t, test.expectedEntries, entries)
    97  		})
    98  	}
    99  }
   100  
   101  func TestEstimateEntries(t *testing.T) {
   102  	tests := []struct {
   103  		numHashes                int
   104  		numEntries               int
   105  		falsePositiveProbability float64
   106  		expectedEntries          int
   107  	}{
   108  		{ // invalid params
   109  			numHashes:                0,
   110  			numEntries:               2_048,
   111  			falsePositiveProbability: .5,
   112  			expectedEntries:          0,
   113  		},
   114  		{ // invalid params
   115  			numHashes:                1,
   116  			numEntries:               0,
   117  			falsePositiveProbability: .5,
   118  			expectedEntries:          0,
   119  		},
   120  		{ // invalid params
   121  			numHashes:                1,
   122  			numEntries:               1,
   123  			falsePositiveProbability: 2,
   124  			expectedEntries:          math.MaxInt,
   125  		},
   126  		{ // invalid params
   127  			numHashes:                1,
   128  			numEntries:               1,
   129  			falsePositiveProbability: -1,
   130  			expectedEntries:          0,
   131  		},
   132  		{
   133  			numHashes:                8,
   134  			numEntries:               2_048,
   135  			falsePositiveProbability: 0,
   136  			expectedEntries:          0,
   137  		},
   138  		{ // params from OptimalParameters(10_000, .01)
   139  			numHashes:                7,
   140  			numEntries:               11_982,
   141  			falsePositiveProbability: .01,
   142  			expectedEntries:          9_993,
   143  		},
   144  		{ // params from OptimalParameters(100_000, .001)
   145  			numHashes:                10,
   146  			numEntries:               179_720,
   147  			falsePositiveProbability: .001,
   148  			expectedEntries:          100_000,
   149  		},
   150  		{ // params from OptimalParameters(10_000, .01)
   151  			numHashes:                7,
   152  			numEntries:               11_982,
   153  			falsePositiveProbability: .05,
   154  			expectedEntries:          14_449,
   155  		},
   156  		{ // params from OptimalParameters(10_000, .01)
   157  			numHashes:                7,
   158  			numEntries:               11_982,
   159  			falsePositiveProbability: 1,
   160  			expectedEntries:          math.MaxInt,
   161  		},
   162  		{ // params from OptimalParameters(10_000, .01)
   163  			numHashes:                7,
   164  			numEntries:               11_982,
   165  			falsePositiveProbability: math.SmallestNonzeroFloat64,
   166  			expectedEntries:          0,
   167  		},
   168  		{ // params from OptimalParameters(10_000, .01)
   169  			numHashes:                7,
   170  			numEntries:               11_982,
   171  			falsePositiveProbability: largestFloat64LessThan1,
   172  			expectedEntries:          math.MaxInt,
   173  		},
   174  	}
   175  	for _, test := range tests {
   176  		t.Run(fmt.Sprintf("%d_%d_%f", test.numHashes, test.numEntries, test.falsePositiveProbability), func(t *testing.T) {
   177  			entries := EstimateCount(test.numHashes, test.numEntries, test.falsePositiveProbability)
   178  			require.Equal(t, test.expectedEntries, entries)
   179  		})
   180  	}
   181  }
   182  
   183  func FuzzOptimalHashes(f *testing.F) {
   184  	f.Fuzz(func(t *testing.T, numEntries, count int) {
   185  		hashes := OptimalHashes(numEntries, count)
   186  		require.GreaterOrEqual(t, hashes, minHashes)
   187  		require.LessOrEqual(t, hashes, maxHashes)
   188  	})
   189  }
   190  
   191  func FuzzOptimalEntries(f *testing.F) {
   192  	f.Fuzz(func(t *testing.T, count int, falsePositiveProbability float64) {
   193  		entries := OptimalEntries(count, falsePositiveProbability)
   194  		require.GreaterOrEqual(t, entries, minEntries)
   195  	})
   196  }
   197  
   198  func FuzzEstimateEntries(f *testing.F) {
   199  	f.Fuzz(func(t *testing.T, numHashes, numEntries int, falsePositiveProbability float64) {
   200  		entries := EstimateCount(numHashes, numEntries, falsePositiveProbability)
   201  		require.GreaterOrEqual(t, entries, 0)
   202  	})
   203  }