github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/chat/flip/prng_test.go (about)

     1  package flip
     2  
     3  import (
     4  	"github.com/stretchr/testify/require"
     5  	"math/big"
     6  	"testing"
     7  )
     8  
     9  func TestPRNG(t *testing.T) {
    10  	var secret Secret
    11  	secret[0] = 1
    12  	prng := NewPRNG(secret)
    13  	expected := []int{13, 59, 52, 48, 40, 15, 11, 21, 64, 31, 53, 61, 20, 28, 52, 41, 53, 45,
    14  		54, 54, 23, 32, 19, 5, 53, 45, 18, 21, 13, 50, 57, 61, 26, 51, 62, 8, 62, 52, 10, 2, 64, 17,
    15  		53, 35, 35, 9, 30, 0, 49, 47, 10, 8, 39, 37, 14, 18, 17, 48, 23, 32, 11, 45, 40, 24, 40, 11, 46, 40,
    16  		46, 41, 3, 25, 16, 55, 16, 21, 8, 64, 22, 6, 9, 25, 49, 24, 12, 26, 48, 48, 28, 31, 34, 58, 21,
    17  		28, 52, 60, 28, 50, 49, 8}
    18  
    19  	for _, a := range expected {
    20  		b := prng.Int(65)
    21  		require.Equal(t, a, int(b))
    22  	}
    23  
    24  	n := "919209803230948230498223094203977777434098123"
    25  	var ni big.Int
    26  	ni.SetString(n, 10)
    27  	bigExpected := []string{
    28  		"614222739125617243553633697417534086505324514",
    29  		"355554091485763233176715002468010576256365854",
    30  		"445884861910366431685301365477451900735701042",
    31  		"117678505521424886688566746877893336950051289",
    32  		"246188573796042435697035169087245506434824132",
    33  		"684500989680877379218341481720344136659405599",
    34  		"26111147325353218353310266097685298806908568",
    35  		"824789626433629860440209186185356370803493878",
    36  		"831846934379293857466321253950305266684136426",
    37  		"733199753439315108451154616131739206730983101",
    38  	}
    39  
    40  	for _, a := range bigExpected {
    41  		b := prng.Big(&ni)
    42  		var ai big.Int
    43  		ai.SetString(a, 10)
    44  		require.Equal(t, b.Cmp(&ai), 0)
    45  	}
    46  
    47  	coinsExpected := []bool{
    48  		true, false, false, true, true, false, true, false, false, true, false, true, true, true, false, false,
    49  		false, true, true, false,
    50  	}
    51  
    52  	for _, b := range coinsExpected {
    53  		require.Equal(t, prng.Bool(), b)
    54  	}
    55  
    56  	expectedNegatives := []int{
    57  		-210, -25, -224, -221, -64, -49, -64, -246, -76, -32, -159, -200, -49, -166, -23, -120, -164, -174, -205, -25, -26,
    58  		-4, -31, -198, -176, -51, -244, -16, -81, -184, -141, -233, -197, -106, -101, -150, -191, -95, -147, -185, -249,
    59  		-3, -132, -76, -212, -106, -224, -71, -217, -190, -11, -29, -176, -158, -163, -54, -234, -254, -164, -152, -48,
    60  		-118, -53, -78, -116, -200, -141, -182, -156, -120, -45, -181, -191, -107, -246, -114, -244, -161, -33, -153,
    61  		-182, -206, -213, -22, -230, -35, -38, -204, -29, -220, -196, -65, -52, -39, -122, -205, -181, -178, -60,
    62  		-21, -120, -89, -129, -225, -34, -204, -134, -185, -50, -114, -55, -151, -239, -175, -43, -159, -64, -31,
    63  		-74, -234, -100, -86, -175, -175, -82, -21, -26, -151, -63, -142, -116, -40, -87, -161, -143, -182, -18,
    64  		-158, -59, -71, -216, -40, -65, -79, -243, -95, -12, -160, -63, -231, -56, -220, -70, -102, -61, -235,
    65  		-21, -163, -174, -65, -45, -130, -113, -45, -156, -138, -158, -52, -150, -35, -17, -228, -22, -112, -199,
    66  		-77, -168, -166, -16, -248, -39, -245, -110, -214, -28, -235, -54, -235, -160, -218, -133, -212, -139, -4,
    67  		-64, -132, -233, -222, -194, -237, -149, -207, -161, -100, -36, -241, -70, -167, -252, -54, -42,
    68  		-189, -234, -34, -192, -212, -49, -155, -189, -70, -62, -15, -114, -89, -234, -236, -21, -241, -217, -205,
    69  		-155, -182, -103, -220, -143, -123, -82, -35, -15, -130, -140, -224, -158, -119, -2, -201, -195, -41, -246,
    70  		-155, -156, -109, -41, -170, -142, -68, -28, -102, -176, -100, -213, -24, -46, -134, -191, -147, -178, -220,
    71  		-116, -45, -53, -248, -177, -6, -168, -139, -52, -129, -95, -135, -116, -250, -32, -242, -127, -127, -234,
    72  		-235, -92, -214, -243, -90, -11, -242, -150, -179, -218, -119, -156, -205, -204, -251, -143, -55, -15, -58,
    73  		-78, -110, -241, -142, -1, -35, -81, -102, -107, -90, -53, -134, -246, -14, -249, -82, -217, -3, -197,
    74  		-208, -64, -255, -202, -241, -70, -146, -20, -171, -182, -9, -213, -243, -221, -116, -171, -174, -121,
    75  		-19, -148, -23, -137, -43, -144, -210, -112, -192, -171, -251, -134, -178, -63, 0, -180, -94, -52, -137}
    76  
    77  	for _, a := range expectedNegatives {
    78  		b := prng.Int(-256)
    79  		require.Equal(t, a, int(b))
    80  	}
    81  }
    82  
    83  func TestPRNGRanges(t *testing.T) {
    84  
    85  	test := func(n int64) {
    86  		var secret Secret
    87  		secret[0] = 3
    88  		found0 := false
    89  		foundMax := false
    90  		prng := NewPRNG(secret)
    91  		max := n
    92  		if max > 0 {
    93  			max--
    94  		} else {
    95  			max++
    96  		}
    97  
    98  		for i := 0; i < 1000 && (!found0 || !foundMax); i++ {
    99  			val := prng.Int(n)
   100  			if val == 0 {
   101  				found0 = true
   102  			}
   103  			if val == max {
   104  				foundMax = true
   105  			}
   106  		}
   107  		require.True(t, found0)
   108  		require.True(t, foundMax)
   109  	}
   110  
   111  	for _, n := range []int{2, 4, 5, 16, 17, 32, 33, 64, 127, 128, 129, -2, -3, -4, -5, -8, -10, -15, -16, -17, -31, -32, -33} {
   112  		test(int64(n))
   113  	}
   114  }
   115  
   116  func TestPRNGCornerCases(t *testing.T) {
   117  	var secret Secret
   118  	secret[0] = 1
   119  	prng := NewPRNG(secret)
   120  
   121  	// By convention, we're returning 0 for 0 moduli, but it's
   122  	// a corner case.
   123  	m := big.NewInt(0)
   124  	r := prng.Big(m)
   125  	require.Equal(t, 0, m.Cmp(r))
   126  
   127  	// The caase of the 1 modulus is handled normally but let's test
   128  	// that it works.
   129  	m = big.NewInt(1)
   130  	r = prng.Big(m)
   131  	require.Equal(t, 0, r.Cmp(big.NewInt(0)))
   132  
   133  	// We had an earlier bug in our shuffle, a classic off-by-one, in which 0,1
   134  	// would always be shuffled 1,0. So, if we run 40 times in a row, we better
   135  	// get some number of (1,0) results that are >0 and <40. This test indeed
   136  	// failed when I went back and rebroke the shuffle function.
   137  	flips := 0
   138  	n := 40
   139  	for i := 0; i < n; i++ {
   140  		res := prng.Permutation(2)
   141  		if res[0] == 1 {
   142  			flips++
   143  		}
   144  	}
   145  	require.NotEqual(t, 0, flips)
   146  	require.NotEqual(t, n, flips)
   147  }