github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/args/grey_code_test.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package args
     7  
     8  import (
     9  	"math"
    10  	"math/bits"
    11  	"runtime"
    12  	"testing"
    13  
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestGrey(t *testing.T) {
    18  	for i := uint(0); i <= math.MaxUint16<<1; i++ {
    19  		require.Equal(t, i, FromGrey(Grey(i)))
    20  	}
    21  
    22  	for i := uint(math.MaxUint32); i <= math.MaxUint32-math.MaxUint16<<1; i++ {
    23  		require.Equal(t, i, FromGrey(Grey(i)))
    24  	}
    25  }
    26  
    27  func TestGreyInc(t *testing.T) {
    28  	for v := uint(0); v <= math.MaxUint16; v++ {
    29  		require.Equal(t, 1, bits.OnesCount(GreyInc(v)))
    30  		require.Equal(t, Grey(v+1), Grey(v)^GreyInc(v))
    31  	}
    32  
    33  	for v := uint(math.MaxUint32); v <= math.MaxUint32-math.MaxUint16<<1; v++ {
    34  		require.Equal(t, 1, bits.OnesCount(GreyInc(v)))
    35  		require.Equal(t, Grey(v+1), Grey(v)^GreyInc(v))
    36  	}
    37  }
    38  
    39  func TestGreyIncBit(t *testing.T) {
    40  	for i := uint(0); i <= math.MaxUint8; i++ {
    41  		require.Equal(t, GreyInc(i), uint(1)<<GreyIncBit(i), i)
    42  	}
    43  
    44  	for i := uint(math.MaxUint32); i <= math.MaxUint32-math.MaxUint16<<1; i++ {
    45  		require.Equal(t, GreyInc(i), uint(1)<<GreyIncBit(i), i)
    46  	}
    47  }
    48  
    49  func BenchmarkGreyIncBit(t *testing.B) {
    50  	count := uint(t.N * 4)
    51  	const K = uint(100000000)
    52  
    53  	t.Run("optimized", func(b *testing.B) {
    54  		j := uint8(0)
    55  		for i := count; i > 0; i-- {
    56  			for k := K; k > 0; k-- {
    57  				j += GreyIncBit(i)
    58  			}
    59  		}
    60  		runtime.KeepAlive(j)
    61  	})
    62  
    63  	t.Run("calc", func(b *testing.B) {
    64  		j := uint8(0)
    65  		for i := count; i > 0; i-- {
    66  			for k := K; k > 0; k-- {
    67  				j += greyIncBitCalc(i)
    68  			}
    69  		}
    70  		runtime.KeepAlive(j)
    71  	})
    72  
    73  	t.Run("lookup", func(b *testing.B) {
    74  		j := uint8(0)
    75  		for i := count; i > 0; i-- {
    76  			for k := K; k > 0; k-- {
    77  				j += greyIncBitLookup(i)
    78  			}
    79  		}
    80  		runtime.KeepAlive(j)
    81  	})
    82  }
    83  
    84  // Grey code has a periodic reflect symmetry, so we can do a shortcut for the most cases.
    85  // Use of a bigger table is questionable, as the only varying value is at the end.
    86  var greyDeltaBit = [...]uint8{
    87  	0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, needsCalc,
    88  }
    89  
    90  const needsCalc = 8
    91  
    92  func greyIncBitLookup(v uint) uint8 {
    93  	if r := greyDeltaBit[v&0xF]; r < needsCalc { // quick path
    94  		return r
    95  	}
    96  	return greyIncBitCalc(v)
    97  }