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 }