gonum.org/v1/gonum@v0.14.0/stat/combin/combin_test.go (about)

     1  // Copyright ©2016 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package combin
     6  
     7  import (
     8  	"math/big"
     9  	"reflect"
    10  	"strconv"
    11  	"testing"
    12  	"unsafe"
    13  
    14  	"gonum.org/v1/gonum/floats/scalar"
    15  )
    16  
    17  // intSosMatch returns true if the two slices of slices are equal.
    18  func intSosMatch(a, b [][]int) bool {
    19  	if len(a) != len(b) {
    20  		return false
    21  	}
    22  	for i, s := range a {
    23  		if len(s) != len(b[i]) {
    24  			return false
    25  		}
    26  		for j, v := range s {
    27  			if v != b[i][j] {
    28  				return false
    29  			}
    30  		}
    31  	}
    32  	return true
    33  }
    34  
    35  var binomialTests = []struct {
    36  	n, k, ans int
    37  }{
    38  	{0, 0, 1},
    39  	{5, 0, 1},
    40  	{5, 1, 5},
    41  	{5, 2, 10},
    42  	{5, 3, 10},
    43  	{5, 4, 5},
    44  	{5, 5, 1},
    45  
    46  	{6, 0, 1},
    47  	{6, 1, 6},
    48  	{6, 2, 15},
    49  	{6, 3, 20},
    50  	{6, 4, 15},
    51  	{6, 5, 6},
    52  	{6, 6, 1},
    53  
    54  	{20, 0, 1},
    55  	{20, 1, 20},
    56  	{20, 2, 190},
    57  	{20, 3, 1140},
    58  	{20, 4, 4845},
    59  	{20, 5, 15504},
    60  	{20, 6, 38760},
    61  	{20, 7, 77520},
    62  	{20, 8, 125970},
    63  	{20, 9, 167960},
    64  	{20, 10, 184756},
    65  	{20, 11, 167960},
    66  	{20, 12, 125970},
    67  	{20, 13, 77520},
    68  	{20, 14, 38760},
    69  	{20, 15, 15504},
    70  	{20, 16, 4845},
    71  	{20, 17, 1140},
    72  	{20, 18, 190},
    73  	{20, 19, 20},
    74  	{20, 20, 1},
    75  }
    76  
    77  func TestBinomial(t *testing.T) {
    78  	for cas, test := range binomialTests {
    79  		ans := Binomial(test.n, test.k)
    80  		if ans != test.ans {
    81  			t.Errorf("Case %v: Binomial mismatch. Got %v, want %v.", cas, ans, test.ans)
    82  		}
    83  	}
    84  	var (
    85  		// Ensure that we have enough space to represent results.
    86  		// TODO(kortschak): Replace the unsafe.Sizeof(int(0)) expression with
    87  		// sizeof.Int if https://github.com/golang/go/issues/29982 is
    88  		// implemented. See also https://github.com/golang/go/issues/40168.
    89  		n = int(unsafe.Sizeof(int(0))*8 - 3)
    90  
    91  		want big.Int
    92  		got  big.Int
    93  	)
    94  	for k := 0; k <= n; k++ {
    95  		want.Binomial(int64(n), int64(k))
    96  		got.SetInt64(int64(Binomial(n, k)))
    97  		if want.Cmp(&got) != 0 {
    98  			t.Errorf("Case n=%v,k=%v: Binomial mismatch for large n. Got %v, want %v.", n, k, got, want)
    99  		}
   100  	}
   101  }
   102  
   103  func TestGeneralizedBinomial(t *testing.T) {
   104  	for cas, test := range binomialTests {
   105  		ans := GeneralizedBinomial(float64(test.n), float64(test.k))
   106  		if !scalar.EqualWithinAbsOrRel(ans, float64(test.ans), 1e-14, 1e-14) {
   107  			t.Errorf("Case %v: Binomial mismatch. Got %v, want %v.", cas, ans, test.ans)
   108  		}
   109  	}
   110  }
   111  
   112  func TestCombinations(t *testing.T) {
   113  	for cas, test := range []struct {
   114  		n, k int
   115  		data [][]int
   116  	}{
   117  		{
   118  			n:    1,
   119  			k:    1,
   120  			data: [][]int{{0}},
   121  		},
   122  		{
   123  			n:    2,
   124  			k:    1,
   125  			data: [][]int{{0}, {1}},
   126  		},
   127  		{
   128  			n:    2,
   129  			k:    2,
   130  			data: [][]int{{0, 1}},
   131  		},
   132  		{
   133  			n:    3,
   134  			k:    1,
   135  			data: [][]int{{0}, {1}, {2}},
   136  		},
   137  		{
   138  			n:    3,
   139  			k:    2,
   140  			data: [][]int{{0, 1}, {0, 2}, {1, 2}},
   141  		},
   142  		{
   143  			n:    3,
   144  			k:    3,
   145  			data: [][]int{{0, 1, 2}},
   146  		},
   147  		{
   148  			n:    4,
   149  			k:    1,
   150  			data: [][]int{{0}, {1}, {2}, {3}},
   151  		},
   152  		{
   153  			n:    4,
   154  			k:    2,
   155  			data: [][]int{{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}},
   156  		},
   157  		{
   158  			n:    4,
   159  			k:    3,
   160  			data: [][]int{{0, 1, 2}, {0, 1, 3}, {0, 2, 3}, {1, 2, 3}},
   161  		},
   162  		{
   163  			n:    4,
   164  			k:    4,
   165  			data: [][]int{{0, 1, 2, 3}},
   166  		},
   167  	} {
   168  		data := Combinations(test.n, test.k)
   169  		if !intSosMatch(data, test.data) {
   170  			t.Errorf("Cas %v: Generated combinations mismatch. Got %v, want %v.", cas, data, test.data)
   171  		}
   172  	}
   173  }
   174  
   175  func TestCombinationGenerator(t *testing.T) {
   176  	for n := 0; n <= 10; n++ {
   177  		for k := 1; k <= n; k++ {
   178  			combinations := Combinations(n, k)
   179  			cg := NewCombinationGenerator(n, k)
   180  			genCombs := make([][]int, 0, len(combinations))
   181  			for cg.Next() {
   182  				genCombs = append(genCombs, cg.Combination(nil))
   183  			}
   184  			if !intSosMatch(combinations, genCombs) {
   185  				t.Errorf("Combinations and generated combinations do not match. n = %v, k = %v", n, k)
   186  			}
   187  		}
   188  	}
   189  }
   190  
   191  func TestCombinationIndex(t *testing.T) {
   192  	for cas, s := range []struct {
   193  		n, k int
   194  	}{
   195  		{6, 3},
   196  		{4, 4},
   197  		{10, 1},
   198  		{8, 2},
   199  	} {
   200  		n := s.n
   201  		k := s.k
   202  		combs := make(map[string]struct{})
   203  		for i := 0; i < Binomial(n, k); i++ {
   204  			comb := IndexToCombination(nil, i, n, k)
   205  			idx := CombinationIndex(comb, n, k)
   206  			if idx != i {
   207  				t.Errorf("Cas %d: combination mismatch. Want %d, got %d", cas, i, idx)
   208  			}
   209  			combs[intSliceToKey(comb)] = struct{}{}
   210  		}
   211  		if len(combs) != Binomial(n, k) {
   212  			t.Errorf("Case %d: not all generated combinations were unique", cas)
   213  		}
   214  	}
   215  }
   216  
   217  func intSliceToKey(s []int) string {
   218  	var str string
   219  	for _, v := range s {
   220  		str += strconv.Itoa(v) + "_"
   221  	}
   222  	return str
   223  }
   224  
   225  // TestCombinationOrder tests that the different Combinations methods
   226  // agree on the iteration order.
   227  func TestCombinationOrder(t *testing.T) {
   228  	n := 7
   229  	k := 3
   230  	list := Combinations(n, k)
   231  	for i, v := range list {
   232  		idx := CombinationIndex(v, n, k)
   233  		if idx != i {
   234  			t.Errorf("Combinations and CombinationIndex mismatch")
   235  			break
   236  		}
   237  	}
   238  }
   239  
   240  func TestIdxSubFor(t *testing.T) {
   241  	for cas, dims := range [][]int{
   242  		{2, 2},
   243  		{3, 1, 6},
   244  		{2, 4, 6, 7},
   245  	} {
   246  		// Loop over all of the indexes. Confirm that the subscripts make sense
   247  		// and that IdxFor is the converse of SubFor.
   248  		maxIdx := 1
   249  		for _, v := range dims {
   250  			maxIdx *= v
   251  		}
   252  		into := make([]int, len(dims))
   253  		for idx := 0; idx < maxIdx; idx++ {
   254  			sub := SubFor(nil, idx, dims)
   255  			for i := range sub {
   256  				if sub[i] < 0 || sub[i] >= dims[i] {
   257  					t.Errorf("cas %v: bad subscript. dims: %v, sub: %v", cas, dims, sub)
   258  				}
   259  			}
   260  			SubFor(into, idx, dims)
   261  			if !reflect.DeepEqual(sub, into) {
   262  				t.Errorf("cas %v: subscript mismatch with supplied slice. Got %v, want %v", cas, into, sub)
   263  			}
   264  			idxOut := IdxFor(sub, dims)
   265  			if idxOut != idx {
   266  				t.Errorf("cas %v: returned index mismatch. Got %v, want %v", cas, idxOut, idx)
   267  			}
   268  		}
   269  	}
   270  }
   271  
   272  func TestCartesian(t *testing.T) {
   273  	// First, test with a known return.
   274  	lens := []int{2, 3, 4}
   275  	want := [][]int{
   276  		{0, 0, 0},
   277  		{0, 0, 1},
   278  		{0, 0, 2},
   279  		{0, 0, 3},
   280  		{0, 1, 0},
   281  		{0, 1, 1},
   282  		{0, 1, 2},
   283  		{0, 1, 3},
   284  		{0, 2, 0},
   285  		{0, 2, 1},
   286  		{0, 2, 2},
   287  		{0, 2, 3},
   288  		{1, 0, 0},
   289  		{1, 0, 1},
   290  		{1, 0, 2},
   291  		{1, 0, 3},
   292  		{1, 1, 0},
   293  		{1, 1, 1},
   294  		{1, 1, 2},
   295  		{1, 1, 3},
   296  		{1, 2, 0},
   297  		{1, 2, 1},
   298  		{1, 2, 2},
   299  		{1, 2, 3},
   300  	}
   301  	got := Cartesian(lens)
   302  	if !intSosMatch(want, got) {
   303  		t.Errorf("Cartesian data mismatch.\nwant:\n%v\ngot:\n%v", want, got)
   304  	}
   305  }
   306  
   307  func TestNumCartesianProducts(t *testing.T) {
   308  	want := 6
   309  	got := Card([]int{1, 2, 3})
   310  	if want != got {
   311  		t.Errorf("number of Cartesian products mismatch.\nwant:\n%v\ngot:\n%v", want, got)
   312  	}
   313  }
   314  
   315  func TestCartesianGenerator(t *testing.T) {
   316  	want := [][]int{
   317  		{0, 0, 0},
   318  		{0, 0, 1},
   319  		{0, 0, 2},
   320  		{0, 1, 0},
   321  		{0, 1, 1},
   322  		{0, 1, 2},
   323  	}
   324  	gen := NewCartesianGenerator([]int{1, 2, 3})
   325  	iterations := 0
   326  	for gen.Next() {
   327  		got := gen.Product(nil)
   328  		if !reflect.DeepEqual(got, want[iterations]) {
   329  			t.Errorf("Cartesian product does not match. want: %v got: %v", want[iterations], got)
   330  		}
   331  		iterations++
   332  	}
   333  
   334  	if iterations != len(want) {
   335  		t.Errorf("Number of products does not match. want: %v got: %v", len(want), iterations)
   336  	}
   337  }
   338  
   339  func TestPermutationIndex(t *testing.T) {
   340  	for cas, s := range []struct {
   341  		n, k int
   342  	}{
   343  		{6, 3},
   344  		{4, 4},
   345  		{10, 1},
   346  		{8, 2},
   347  	} {
   348  		n := s.n
   349  		k := s.k
   350  		perms := make(map[string]struct{})
   351  		for i := 0; i < NumPermutations(n, k); i++ {
   352  			perm := IndexToPermutation(nil, i, n, k)
   353  			idx := PermutationIndex(perm, n, k)
   354  			if idx != i {
   355  				t.Errorf("Cas %d: permutation mismatch. Want %d, got %d", cas, i, idx)
   356  			}
   357  			perms[intSliceToKey(perm)] = struct{}{}
   358  		}
   359  		if len(perms) != NumPermutations(n, k) {
   360  			t.Errorf("Case %d: not all generated combinations were unique", cas)
   361  		}
   362  	}
   363  }
   364  
   365  func TestPermutationGenerator(t *testing.T) {
   366  	for n := 0; n <= 7; n++ {
   367  		for k := 1; k <= n; k++ {
   368  			permutations := Permutations(n, k)
   369  			pg := NewPermutationGenerator(n, k)
   370  			genPerms := make([][]int, 0, len(permutations))
   371  			for pg.Next() {
   372  				genPerms = append(genPerms, pg.Permutation(nil))
   373  			}
   374  			if !intSosMatch(permutations, genPerms) {
   375  				t.Errorf("Permutations and generated permutations do not match. n = %v, k = %v", n, k)
   376  			}
   377  		}
   378  	}
   379  }