gonum.org/v1/gonum@v0.14.0/stat/combin/combin.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"
     9  	"sort"
    10  )
    11  
    12  const (
    13  	errNegInput             = "combin: negative input"
    14  	badSetSize              = "combin: n < k"
    15  	badInput                = "combin: wrong input slice length"
    16  	errNonpositiveDimension = "combin: non-positive dimension"
    17  )
    18  
    19  // Binomial returns the binomial coefficient of (n,k), also commonly referred to
    20  // as "n choose k".
    21  //
    22  // The binomial coefficient, C(n,k), is the number of unordered combinations of
    23  // k elements in a set that is n elements big, and is defined as
    24  //
    25  //	C(n,k) = n!/((n-k)!k!)
    26  //
    27  // n and k must be non-negative with n >= k, otherwise Binomial will panic.
    28  // No check is made for overflow.
    29  func Binomial(n, k int) int {
    30  	if n < 0 || k < 0 {
    31  		panic(errNegInput)
    32  	}
    33  	if n < k {
    34  		panic(badSetSize)
    35  	}
    36  	// (n,k) = (n, n-k)
    37  	if k > n/2 {
    38  		k = n - k
    39  	}
    40  	b := 1
    41  	for i := 1; i <= k; i++ {
    42  		b = (n - k + i) * b / i
    43  	}
    44  	return b
    45  }
    46  
    47  // GeneralizedBinomial returns the generalized binomial coefficient of (n, k),
    48  // defined as
    49  //
    50  //	Γ(n+1) / (Γ(k+1) Γ(n-k+1))
    51  //
    52  // where Γ is the Gamma function. GeneralizedBinomial is useful for continuous
    53  // relaxations of the binomial coefficient, or when the binomial coefficient value
    54  // may overflow int. In the latter case, one may use math/big for an exact
    55  // computation.
    56  //
    57  // n and k must be non-negative with n >= k, otherwise GeneralizedBinomial will panic.
    58  func GeneralizedBinomial(n, k float64) float64 {
    59  	return math.Exp(LogGeneralizedBinomial(n, k))
    60  }
    61  
    62  // LogGeneralizedBinomial returns the log of the generalized binomial coefficient.
    63  // See GeneralizedBinomial for more information.
    64  func LogGeneralizedBinomial(n, k float64) float64 {
    65  	if n < 0 || k < 0 {
    66  		panic(errNegInput)
    67  	}
    68  	if n < k {
    69  		panic(badSetSize)
    70  	}
    71  	a, _ := math.Lgamma(n + 1)
    72  	b, _ := math.Lgamma(k + 1)
    73  	c, _ := math.Lgamma(n - k + 1)
    74  	return a - b - c
    75  }
    76  
    77  // CombinationGenerator generates combinations iteratively. The Combinations
    78  // function may be called to generate all combinations collectively.
    79  type CombinationGenerator struct {
    80  	n         int
    81  	k         int
    82  	previous  []int
    83  	remaining int
    84  }
    85  
    86  // NewCombinationGenerator returns a CombinationGenerator for generating the
    87  // combinations of k elements from a set of size n.
    88  //
    89  // n and k must be non-negative with n >= k, otherwise NewCombinationGenerator
    90  // will panic.
    91  func NewCombinationGenerator(n, k int) *CombinationGenerator {
    92  	return &CombinationGenerator{
    93  		n:         n,
    94  		k:         k,
    95  		remaining: Binomial(n, k),
    96  	}
    97  }
    98  
    99  // Next advances the iterator if there are combinations remaining to be generated,
   100  // and returns false if all combinations have been generated. Next must be called
   101  // to initialize the first value before calling Combination or Combination will
   102  // panic. The value returned by Combination is only changed during calls to Next.
   103  func (c *CombinationGenerator) Next() bool {
   104  	if c.remaining <= 0 {
   105  		// Next is called before combination, so c.remaining is set to zero before
   106  		// Combination is called. Thus, Combination cannot panic on zero, and a
   107  		// second sentinel value is needed.
   108  		c.remaining = -1
   109  		return false
   110  	}
   111  	if c.previous == nil {
   112  		c.previous = make([]int, c.k)
   113  		for i := range c.previous {
   114  			c.previous[i] = i
   115  		}
   116  	} else {
   117  		nextCombination(c.previous, c.n, c.k)
   118  	}
   119  	c.remaining--
   120  	return true
   121  }
   122  
   123  // Combination returns the current combination. If dst is non-nil, it must have
   124  // length k and the result will be stored in-place into dst. If dst
   125  // is nil a new slice will be allocated and returned. If all of the combinations
   126  // have already been constructed (Next() returns false), Combination will panic.
   127  //
   128  // Next must be called to initialize the first value before calling Combination
   129  // or Combination will panic. The value returned by Combination is only changed
   130  // during calls to Next.
   131  func (c *CombinationGenerator) Combination(dst []int) []int {
   132  	if c.remaining == -1 {
   133  		panic("combin: all combinations have been generated")
   134  	}
   135  	if c.previous == nil {
   136  		panic("combin: Combination called before Next")
   137  	}
   138  	if dst == nil {
   139  		dst = make([]int, c.k)
   140  	} else if len(dst) != c.k {
   141  		panic(badInput)
   142  	}
   143  	copy(dst, c.previous)
   144  	return dst
   145  }
   146  
   147  // Combinations generates all of the combinations of k elements from a
   148  // set of size n. The returned slice has length Binomial(n,k) and each inner slice
   149  // has length k.
   150  //
   151  // n and k must be non-negative with n >= k, otherwise Combinations will panic.
   152  //
   153  // CombinationGenerator may alternatively be used to generate the combinations
   154  // iteratively instead of collectively, or IndexToCombination for random access.
   155  func Combinations(n, k int) [][]int {
   156  	combins := Binomial(n, k)
   157  	data := make([][]int, combins)
   158  	if len(data) == 0 {
   159  		return data
   160  	}
   161  	data[0] = make([]int, k)
   162  	for i := range data[0] {
   163  		data[0][i] = i
   164  	}
   165  	for i := 1; i < combins; i++ {
   166  		next := make([]int, k)
   167  		copy(next, data[i-1])
   168  		nextCombination(next, n, k)
   169  		data[i] = next
   170  	}
   171  	return data
   172  }
   173  
   174  // nextCombination generates the combination after s, overwriting the input value.
   175  func nextCombination(s []int, n, k int) {
   176  	for j := k - 1; j >= 0; j-- {
   177  		if s[j] == n+j-k {
   178  			continue
   179  		}
   180  		s[j]++
   181  		for l := j + 1; l < k; l++ {
   182  			s[l] = s[j] + l - j
   183  		}
   184  		break
   185  	}
   186  }
   187  
   188  // CombinationIndex returns the index of the given combination.
   189  //
   190  // The functions CombinationIndex and IndexToCombination define a bijection
   191  // between the integers and the Binomial(n, k) number of possible combinations.
   192  // CombinationIndex returns the inverse of IndexToCombination.
   193  //
   194  // CombinationIndex panics if comb is not a sorted combination of the first
   195  // [0,n) integers, if n or k are negative, or if k is greater than n.
   196  func CombinationIndex(comb []int, n, k int) int {
   197  	if n < 0 || k < 0 {
   198  		panic(errNegInput)
   199  	}
   200  	if n < k {
   201  		panic(badSetSize)
   202  	}
   203  	if len(comb) != k {
   204  		panic("combin: bad length combination")
   205  	}
   206  	if !sort.IntsAreSorted(comb) {
   207  		panic("combin: input combination is not sorted")
   208  	}
   209  	contains := make(map[int]struct{}, k)
   210  	for _, v := range comb {
   211  		contains[v] = struct{}{}
   212  	}
   213  	if len(contains) != k {
   214  		panic("combin: comb contains non-unique elements")
   215  	}
   216  	// This algorithm iterates in reverse lexicograhpic order.
   217  	// Flip the index and values to swap the order.
   218  	rev := make([]int, k)
   219  	for i, v := range comb {
   220  		rev[len(comb)-i-1] = n - v - 1
   221  	}
   222  	idx := 0
   223  	for i, v := range rev {
   224  		if v >= i+1 {
   225  			idx += Binomial(v, i+1)
   226  		}
   227  	}
   228  	return Binomial(n, k) - 1 - idx
   229  }
   230  
   231  // IndexToCombination returns the combination corresponding to the given index.
   232  //
   233  // The functions CombinationIndex and IndexToCombination define a bijection
   234  // between the integers and the Binomial(n, k) number of possible combinations.
   235  // IndexToCombination returns the inverse of CombinationIndex (up to the order
   236  // of the elements).
   237  //
   238  // The combination is stored in-place into dst if dst is non-nil, otherwise
   239  // a new slice is allocated and returned.
   240  //
   241  // IndexToCombination panics if n or k are negative, if k is greater than n,
   242  // or if idx is not in [0, Binomial(n,k)-1]. IndexToCombination will also panic
   243  // if dst is non-nil and len(dst) is not k.
   244  func IndexToCombination(dst []int, idx, n, k int) []int {
   245  	if idx < 0 || idx >= Binomial(n, k) {
   246  		panic("combin: invalid index")
   247  	}
   248  	if dst == nil {
   249  		dst = make([]int, k)
   250  	} else if len(dst) != k {
   251  		panic(badInput)
   252  	}
   253  	// The base algorithm indexes in reverse lexicographic order
   254  	// flip the values and the index.
   255  	idx = Binomial(n, k) - 1 - idx
   256  	for i := range dst {
   257  		// Find the largest number m such that Binomial(m, k-i) <= idx.
   258  		// This is one less than the first number such that it is larger.
   259  		m := sort.Search(n, func(m int) bool {
   260  			if m < k-i {
   261  				return false
   262  			}
   263  			return Binomial(m, k-i) > idx
   264  		})
   265  		m--
   266  		// Normally this is put m into the last free spot, but we
   267  		// reverse the index and the value.
   268  		dst[i] = n - m - 1
   269  		if m >= k-i {
   270  			idx -= Binomial(m, k-i)
   271  		}
   272  	}
   273  	return dst
   274  }
   275  
   276  // Cartesian returns the Cartesian product of the slices in data. The Cartesian
   277  // product of two sets is the set of all combinations of the items. For example,
   278  // given the input
   279  //
   280  //	[]int{2, 3, 1}
   281  //
   282  // the returned matrix will be
   283  //
   284  //	[ 0 0 0 ]
   285  //	[ 0 1 0 ]
   286  //	[ 0 2 0 ]
   287  //	[ 1 0 0 ]
   288  //	[ 1 1 0 ]
   289  //	[ 1 2 0 ]
   290  //
   291  // Cartesian panics if any of the provided lengths are less than 1.
   292  func Cartesian(lens []int) [][]int {
   293  	rows := Card(lens)
   294  	if rows == 0 {
   295  		panic("combin: empty lengths")
   296  	}
   297  	out := make([][]int, rows)
   298  	for i := 0; i < rows; i++ {
   299  		out[i] = SubFor(nil, i, lens)
   300  	}
   301  	return out
   302  }
   303  
   304  // Card computes the cardinality of the multi-dimensional space whose dimensions have size specified by dims
   305  // All length values must be positive, otherwise this will panic.
   306  func Card(dims []int) int {
   307  	if len(dims) == 0 {
   308  		return 0
   309  	}
   310  	card := 1
   311  	for _, v := range dims {
   312  		if v < 0 {
   313  			panic("combin: length less than zero")
   314  		}
   315  		card *= v
   316  	}
   317  	return card
   318  }
   319  
   320  // NewCartesianGenerator returns a CartesianGenerator for iterating over Cartesian products which are generated on the fly.
   321  // All values in lens must be positive, otherwise this will panic.
   322  func NewCartesianGenerator(lens []int) *CartesianGenerator {
   323  	return &CartesianGenerator{
   324  		lens: lens,
   325  		rows: Card(lens),
   326  		idx:  -1,
   327  	}
   328  }
   329  
   330  // CartesianGenerator iterates over a Cartesian product set.
   331  type CartesianGenerator struct {
   332  	lens []int
   333  	rows int
   334  	idx  int
   335  }
   336  
   337  // Next moves to the next product of the Cartesian set.
   338  // It returns false if the generator reached the end of the Cartesian set end.
   339  func (g *CartesianGenerator) Next() bool {
   340  	if g.idx+1 < g.rows {
   341  		g.idx++
   342  		return true
   343  	}
   344  	g.idx = g.rows
   345  	return false
   346  }
   347  
   348  // Product generates one product of the Cartesian set according to the current index which is increased by Next().
   349  // Next needs to be called at least one time before this method, otherwise it will panic.
   350  func (g *CartesianGenerator) Product(dst []int) []int {
   351  	return SubFor(dst, g.idx, g.lens)
   352  }
   353  
   354  // IdxFor converts a multi-dimensional index into a linear index for a
   355  // multi-dimensional space. sub specifies the index for each dimension, and dims
   356  // specifies the size of each dimension. IdxFor is the inverse of SubFor.
   357  // IdxFor panics if any of the entries of sub are negative, any of the entries
   358  // of dim are non-positive, or if sub[i] >= dims[i] for any i.
   359  func IdxFor(sub, dims []int) int {
   360  	// The index returned is "row-major", that is the last index of sub is
   361  	// continuous.
   362  	var idx int
   363  	stride := 1
   364  	for i := len(dims) - 1; i >= 0; i-- {
   365  		v := sub[i]
   366  		d := dims[i]
   367  		if d <= 0 {
   368  			panic(errNonpositiveDimension)
   369  		}
   370  		if v < 0 || v >= d {
   371  			panic("combin: invalid subscript")
   372  		}
   373  		idx += v * stride
   374  		stride *= d
   375  	}
   376  	return idx
   377  }
   378  
   379  // SubFor returns the multi-dimensional subscript for the input linear index to
   380  // the multi-dimensional space. dims specifies the size of each dimension, and
   381  // idx specifies the linear index. SubFor is the inverse of IdxFor.
   382  //
   383  // If sub is non-nil the result is stored in-place into sub, and SubFor will panic
   384  // if len(sub) != len(dims). If sub is nil a new slice of the appropriate length
   385  // is allocated. SubFor panics if idx < 0 or if idx is greater than or equal to
   386  // the product of the dimensions.
   387  func SubFor(sub []int, idx int, dims []int) []int {
   388  	if sub == nil {
   389  		sub = make([]int, len(dims))
   390  	}
   391  	if len(sub) != len(dims) {
   392  		panic(badInput)
   393  	}
   394  	if idx < 0 {
   395  		panic(errNegInput)
   396  	}
   397  	stride := 1
   398  	for i := len(dims) - 1; i >= 1; i-- {
   399  		stride *= dims[i]
   400  	}
   401  	for i := 0; i < len(dims)-1; i++ {
   402  		v := idx / stride
   403  		d := dims[i]
   404  		if d < 0 {
   405  			panic(errNonpositiveDimension)
   406  		}
   407  		if v >= dims[i] {
   408  			panic("combin: index too large")
   409  		}
   410  		sub[i] = v
   411  		idx -= v * stride
   412  		stride /= dims[i+1]
   413  	}
   414  	if idx > dims[len(sub)-1] {
   415  		panic("combin: index too large")
   416  	}
   417  	sub[len(sub)-1] = idx
   418  	return sub
   419  }
   420  
   421  // NumPermutations returns the number of permutations when selecting k
   422  // objects from a set of n objects when the selection order matters.
   423  // No check is made for overflow.
   424  //
   425  // NumPermutations panics if either n or k is negative, or if k is
   426  // greater than n.
   427  func NumPermutations(n, k int) int {
   428  	if n < 0 {
   429  		panic("combin: n is negative")
   430  	}
   431  	if k < 0 {
   432  		panic("combin: k is negative")
   433  	}
   434  	if k > n {
   435  		panic("combin: k is greater than n")
   436  	}
   437  	p := 1
   438  	for i := n - k + 1; i <= n; i++ {
   439  		p *= i
   440  	}
   441  	return p
   442  }
   443  
   444  // Permutations generates all of the permutations of k elements from a
   445  // set of size n. The returned slice has length NumPermutations(n, k)
   446  // and each inner slice has length k.
   447  //
   448  // n and k must be non-negative with n >= k, otherwise Permutations will panic.
   449  //
   450  // PermutationGenerator may alternatively be used to generate the permutations
   451  // iteratively instead of collectively, or IndexToPermutation for random access.
   452  func Permutations(n, k int) [][]int {
   453  	nPerms := NumPermutations(n, k)
   454  	data := make([][]int, nPerms)
   455  	if len(data) == 0 {
   456  		return data
   457  	}
   458  	for i := 0; i < nPerms; i++ {
   459  		data[i] = IndexToPermutation(nil, i, n, k)
   460  	}
   461  	return data
   462  }
   463  
   464  // PermutationGenerator generates permutations iteratively. The Permutations
   465  // function may be called to generate all permutations collectively.
   466  type PermutationGenerator struct {
   467  	n           int
   468  	k           int
   469  	nPerm       int
   470  	idx         int
   471  	permutation []int
   472  }
   473  
   474  // NewPermutationGenerator returns a PermutationGenerator for generating the
   475  // permutations of k elements from a set of size n.
   476  //
   477  // n and k must be non-negative with n >= k, otherwise NewPermutationGenerator
   478  // will panic.
   479  func NewPermutationGenerator(n, k int) *PermutationGenerator {
   480  	return &PermutationGenerator{
   481  		n:           n,
   482  		k:           k,
   483  		nPerm:       NumPermutations(n, k),
   484  		idx:         -1,
   485  		permutation: make([]int, k),
   486  	}
   487  }
   488  
   489  // Next advances the iterator if there are permutations remaining to be generated,
   490  // and returns false if all permutations have been generated. Next must be called
   491  // to initialize the first value before calling Permutation or Permutation will
   492  // panic. The value returned by Permutation is only changed during calls to Next.
   493  func (p *PermutationGenerator) Next() bool {
   494  	if p.idx >= p.nPerm-1 {
   495  		p.idx = p.nPerm // so Permutation can panic.
   496  		return false
   497  	}
   498  	p.idx++
   499  	IndexToPermutation(p.permutation, p.idx, p.n, p.k)
   500  	return true
   501  }
   502  
   503  // Permutation returns the current permutation. If dst is non-nil, it must have
   504  // length k and the result will be stored in-place into dst. If dst
   505  // is nil a new slice will be allocated and returned. If all of the permutations
   506  // have already been constructed (Next() returns false), Permutation will panic.
   507  //
   508  // Next must be called to initialize the first value before calling Permutation
   509  // or Permutation will panic. The value returned by Permutation is only changed
   510  // during calls to Next.
   511  func (p *PermutationGenerator) Permutation(dst []int) []int {
   512  	if p.idx == p.nPerm {
   513  		panic("combin: all permutations have been generated")
   514  	}
   515  	if p.idx == -1 {
   516  		panic("combin: Permutation called before Next")
   517  	}
   518  	if dst == nil {
   519  		dst = make([]int, p.k)
   520  	} else if len(dst) != p.k {
   521  		panic(badInput)
   522  	}
   523  	copy(dst, p.permutation)
   524  	return dst
   525  }
   526  
   527  // PermutationIndex returns the index of the given permutation.
   528  //
   529  // The functions PermutationIndex and IndexToPermutation define a bijection
   530  // between the integers and the NumPermutations(n, k) number of possible permutations.
   531  // PermutationIndex returns the inverse of IndexToPermutation.
   532  //
   533  // PermutationIndex panics if perm is not a permutation of k of the first
   534  // [0,n) integers, if n or k are negative, or if k is greater than n.
   535  func PermutationIndex(perm []int, n, k int) int {
   536  	if n < 0 || k < 0 {
   537  		panic(errNegInput)
   538  	}
   539  	if n < k {
   540  		panic(badSetSize)
   541  	}
   542  	if len(perm) != k {
   543  		panic("combin: bad length permutation")
   544  	}
   545  	contains := make(map[int]struct{}, k)
   546  	for _, v := range perm {
   547  		if v < 0 || v >= n {
   548  			panic("combin: bad element")
   549  		}
   550  		contains[v] = struct{}{}
   551  	}
   552  	if len(contains) != k {
   553  		panic("combin: perm contains non-unique elements")
   554  	}
   555  	if n == k {
   556  		// The permutation is the ordering of the elements.
   557  		return equalPermutationIndex(perm)
   558  	}
   559  
   560  	// The permutation index is found by finding the combination index and the
   561  	// equalPermutation index. The combination index is found by just sorting
   562  	// the elements, and the permutation index is the ordering of the size
   563  	// of the elements.
   564  	tmp := make([]int, len(perm))
   565  	copy(tmp, perm)
   566  	idx := make([]int, len(perm))
   567  	for i := range idx {
   568  		idx[i] = i
   569  	}
   570  	s := sortInts{tmp, idx}
   571  	sort.Sort(s)
   572  	order := make([]int, len(perm))
   573  	for i, v := range idx {
   574  		order[v] = i
   575  	}
   576  	combIdx := CombinationIndex(tmp, n, k)
   577  	permIdx := equalPermutationIndex(order)
   578  	return combIdx*NumPermutations(k, k) + permIdx
   579  }
   580  
   581  type sortInts struct {
   582  	data []int
   583  	idx  []int
   584  }
   585  
   586  func (s sortInts) Len() int {
   587  	return len(s.data)
   588  }
   589  
   590  func (s sortInts) Less(i, j int) bool {
   591  	return s.data[i] < s.data[j]
   592  }
   593  
   594  func (s sortInts) Swap(i, j int) {
   595  	s.data[i], s.data[j] = s.data[j], s.data[i]
   596  	s.idx[i], s.idx[j] = s.idx[j], s.idx[i]
   597  }
   598  
   599  // IndexToPermutation returns the permutation corresponding to the given index.
   600  //
   601  // The functions PermutationIndex and IndexToPermutation define a bijection
   602  // between the integers and the NumPermutations(n, k) number of possible permutations.
   603  // IndexToPermutation returns the inverse of PermutationIndex.
   604  //
   605  // The permutation is stored in-place into dst if dst is non-nil, otherwise
   606  // a new slice is allocated and returned.
   607  //
   608  // IndexToPermutation panics if n or k are negative, if k is greater than n,
   609  // or if idx is not in [0, NumPermutations(n,k)-1]. IndexToPermutation will also panic
   610  // if dst is non-nil and len(dst) is not k.
   611  func IndexToPermutation(dst []int, idx, n, k int) []int {
   612  	nPerm := NumPermutations(n, k)
   613  	if idx < 0 || idx >= nPerm {
   614  		panic("combin: invalid index")
   615  	}
   616  	if dst == nil {
   617  		dst = make([]int, k)
   618  	} else if len(dst) != k {
   619  		panic(badInput)
   620  	}
   621  	if n == k {
   622  		indexToEqualPermutation(dst, idx)
   623  		return dst
   624  	}
   625  
   626  	// First, we index into the combination (which of the k items to choose)
   627  	// and then we index into the n == k permutation of those k items. The
   628  	// indexing acts like a matrix with nComb rows and factorial(k) columns.
   629  	kPerm := NumPermutations(k, k)
   630  	combIdx := idx / kPerm
   631  	permIdx := idx % kPerm
   632  	comb := IndexToCombination(nil, combIdx, n, k) // Gives us the set of integers.
   633  	perm := make([]int, len(dst))
   634  	indexToEqualPermutation(perm, permIdx) // Gives their order.
   635  	for i, v := range perm {
   636  		dst[i] = comb[v]
   637  	}
   638  	return dst
   639  }
   640  
   641  // equalPermutationIndex returns the index of the given permutation of the
   642  // first k integers.
   643  func equalPermutationIndex(perm []int) int {
   644  	// Note(btracey): This is an n^2 algorithm, but factorial increases
   645  	// very quickly (25! overflows int64) so this is not a problem in
   646  	// practice.
   647  	idx := 0
   648  	for i, u := range perm {
   649  		less := 0
   650  		for _, v := range perm[i:] {
   651  			if v < u {
   652  				less++
   653  			}
   654  		}
   655  		idx += less * factorial(len(perm)-i-1)
   656  	}
   657  	return idx
   658  }
   659  
   660  // indexToEqualPermutation returns the permutation for the first len(dst)
   661  // integers for the given index.
   662  func indexToEqualPermutation(dst []int, idx int) {
   663  	for i := range dst {
   664  		dst[i] = i
   665  	}
   666  	for i := range dst {
   667  		f := factorial(len(dst) - i - 1)
   668  		r := idx / f
   669  		v := dst[i+r]
   670  		copy(dst[i+1:i+r+1], dst[i:i+r])
   671  		dst[i] = v
   672  		idx %= f
   673  	}
   674  }
   675  
   676  // factorial returns a!.
   677  func factorial(a int) int {
   678  	f := 1
   679  	for i := 2; i <= a; i++ {
   680  		f *= i
   681  	}
   682  	return f
   683  }