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  }