github.com/jgbaldwinbrown/perf@v0.1.1/pkg/stats/mathx.go (about)

     1  // Copyright 2015 The Go 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 stats
     6  
     7  import "math"
     8  
     9  // mathSign returns the sign of x: -1 if x < 0, 0 if x == 0, 1 if x > 0.
    10  // If x is NaN, it returns NaN.
    11  func mathSign(x float64) float64 {
    12  	if x == 0 {
    13  		return 0
    14  	} else if x < 0 {
    15  		return -1
    16  	} else if x > 0 {
    17  		return 1
    18  	}
    19  	return nan
    20  }
    21  
    22  const smallFactLimit = 20 // 20! => 62 bits
    23  var smallFact [smallFactLimit + 1]int64
    24  
    25  func init() {
    26  	smallFact[0] = 1
    27  	fact := int64(1)
    28  	for n := int64(1); n <= smallFactLimit; n++ {
    29  		fact *= n
    30  		smallFact[n] = fact
    31  	}
    32  }
    33  
    34  // mathChoose returns the binomial coefficient of n and k.
    35  func mathChoose(n, k int) float64 {
    36  	if k == 0 || k == n {
    37  		return 1
    38  	}
    39  	if k < 0 || n < k {
    40  		return 0
    41  	}
    42  	if n <= smallFactLimit { // Implies k <= smallFactLimit
    43  		// It's faster to do several integer multiplications
    44  		// than it is to do an extra integer division.
    45  		// Remarkably, this is also faster than pre-computing
    46  		// Pascal's triangle (presumably because this is very
    47  		// cache efficient).
    48  		numer := int64(1)
    49  		for n1 := int64(n - (k - 1)); n1 <= int64(n); n1++ {
    50  			numer *= n1
    51  		}
    52  		denom := smallFact[k]
    53  		return float64(numer / denom)
    54  	}
    55  
    56  	return math.Exp(lchoose(n, k))
    57  }
    58  
    59  // mathLchoose returns math.Log(mathChoose(n, k)).
    60  func mathLchoose(n, k int) float64 {
    61  	if k == 0 || k == n {
    62  		return 0
    63  	}
    64  	if k < 0 || n < k {
    65  		return math.NaN()
    66  	}
    67  	return lchoose(n, k)
    68  }
    69  
    70  func lchoose(n, k int) float64 {
    71  	a, _ := math.Lgamma(float64(n + 1))
    72  	b, _ := math.Lgamma(float64(k + 1))
    73  	c, _ := math.Lgamma(float64(n - k + 1))
    74  	return a - b - c
    75  }