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