github.com/jgbaldwinbrown/perf@v0.1.1/pkg/stats/util_test.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 ( 8 "fmt" 9 "math" 10 "sort" 11 "strings" 12 "testing" 13 ) 14 15 func testDiscreteCDF(t *testing.T, name string, dist DiscreteDist) { 16 // Build the expected CDF out of the PMF. 17 l, h := dist.Bounds() 18 s := dist.Step() 19 want := map[float64]float64{l - 0.1: 0, h: 1} 20 sum := 0.0 21 for x := l; x < h; x += s { 22 sum += dist.PMF(x) 23 want[x] = sum 24 want[x+s/2] = sum 25 } 26 27 testFunc(t, name, dist.CDF, want) 28 } 29 30 func testInvCDF(t *testing.T, dist Dist, bounded bool) { 31 inv := InvCDF(dist) 32 name := fmt.Sprintf("InvCDF(%+v)", dist) 33 cdfName := fmt.Sprintf("CDF(%+v)", dist) 34 35 // Test bounds. 36 vals := map[float64]float64{-0.01: nan, 1.01: nan} 37 if !bounded { 38 vals[0] = -inf 39 vals[1] = inf 40 } 41 testFunc(t, name, inv, vals) 42 43 if bounded { 44 lo, hi := inv(0), inv(1) 45 vals := map[float64]float64{ 46 lo - 0.01: 0, lo: 0, 47 hi: 1, hi + 0.01: 1, 48 } 49 testFunc(t, cdfName, dist.CDF, vals) 50 if got := dist.CDF(lo + 0.01); !(got > 0) { 51 t.Errorf("%s(0)=%v, but %s(%v)=0", name, lo, cdfName, lo+0.01) 52 } 53 if got := dist.CDF(hi - 0.01); !(got < 1) { 54 t.Errorf("%s(1)=%v, but %s(%v)=1", name, hi, cdfName, hi-0.01) 55 } 56 } 57 58 // Test points between. 59 vals = map[float64]float64{} 60 for _, p := range vecLinspace(0, 1, 11) { 61 if p == 0 || p == 1 { 62 continue 63 } 64 x := inv(p) 65 vals[x] = x 66 } 67 testFunc(t, fmt.Sprintf("InvCDF(CDF(%+v))", dist), 68 func(x float64) float64 { 69 return inv(dist.CDF(x)) 70 }, 71 vals) 72 } 73 74 // aeq returns true if expect and got are equal to 8 significant 75 // figures (1 part in 100 million). 76 func aeq(expect, got float64) bool { 77 if expect < 0 && got < 0 { 78 expect, got = -expect, -got 79 } 80 return expect*0.99999999 <= got && got*0.99999999 <= expect 81 } 82 83 func testFunc(t *testing.T, name string, f func(float64) float64, vals map[float64]float64) { 84 xs := make([]float64, 0, len(vals)) 85 for x := range vals { 86 xs = append(xs, x) 87 } 88 sort.Float64s(xs) 89 90 for _, x := range xs { 91 want, got := vals[x], f(x) 92 if math.IsNaN(want) && math.IsNaN(got) || aeq(want, got) { 93 continue 94 } 95 var label string 96 if strings.Contains(name, "%v") { 97 label = fmt.Sprintf(name, x) 98 } else { 99 label = fmt.Sprintf("%s(%v)", name, x) 100 } 101 t.Errorf("want %s=%v, got %v", label, want, got) 102 } 103 } 104 105 // vecLinspace returns num values spaced evenly between lo and hi, 106 // inclusive. If num is 1, this returns an array consisting of lo. 107 func vecLinspace(lo, hi float64, num int) []float64 { 108 res := make([]float64, num) 109 if num == 1 { 110 res[0] = lo 111 return res 112 } 113 for i := 0; i < num; i++ { 114 res[i] = lo + float64(i)*(hi-lo)/float64(num-1) 115 } 116 return res 117 }