func (e *Experiment) SampleAnnotatedDuration(name string, callback func(idx int) Annotation, samplingConfig SamplingConfig, args ...interface{}) {
	decorations := extractDecorations(args)
	e.Sample(func(idx int) {
		t := time.Now()
		decorations.annotation = callback(idx)
		duration := time.Since(t)
		e.recordDuration(name, duration, decorations)
	}, samplingConfig)
}
/*
SampleDuration samples the passed-in callback and times how long it takes to complete each sample.
The resulting durations are recorded on a Duration Measurement with the passed-in name.  If the Measurement does not exist it is created.

The callback is given a zero-based index that increments by one between samples.  The callback must return an Annotation - this annotation is attached to the measured duration.

The Sampling is configured via the passed-in SamplingConfig

SampleAnnotatedDuration supports the Style() and Precision() decorations.
package gmeasure
import (
	"fmt"
	"math"
	"reflect"
	"sync"
	"time"
	"github.com/onsi/gomega/gmeasure/table"
)
type SamplingConfig struct {
	N int
	Duration time.Duration
	MinSamplingInterval time.Duration
	NumParallel int
}
type Units string
type Annotation string
type Style string
type PrecisionBundle struct {
	Duration    time.Duration
	ValueFormat string
}
func Precision(p interface{}) PrecisionBundle {
	out := DefaultPrecisionBundle
	switch reflect.TypeOf(p) {
	case reflect.TypeOf(time.Duration(0)):
		out.Duration = p.(time.Duration)
	case reflect.TypeOf(int(0)):
		out.ValueFormat = fmt.Sprintf("%%.%df", p.(int))
	default:
		panic("invalid precision type, must be time.Duration or int")
	}
	return out
}
var DefaultPrecisionBundle = PrecisionBundle{
	Duration:    100 * time.Microsecond,
	ValueFormat: "%.3f",
}
type extractedDecorations struct {
	annotation      Annotation
	units           Units
	precisionBundle PrecisionBundle
	style           Style
}
func extractDecorations(args []interface{}) extractedDecorations {
	var out extractedDecorations
	out.precisionBundle = DefaultPrecisionBundle
	for _, arg := range args {
		switch reflect.TypeOf(arg) {
		case reflect.TypeOf(out.annotation):
			out.annotation = arg.(Annotation)
		case reflect.TypeOf(out.units):
			out.units = arg.(Units)
		case reflect.TypeOf(out.precisionBundle):
			out.precisionBundle = arg.(PrecisionBundle)
		case reflect.TypeOf(out.style):
			out.style = arg.(Style)
		default:
			panic(fmt.Sprintf("unrecognized argument %#v", arg))
		}
	}
	return out
}
type Experiment struct {
	Name string
	Measurements Measurements
	lock         *sync.Mutex
}
func NewExperiment(name string) *Experiment {
	experiment := &Experiment{
		Name: name,
		lock: &sync.Mutex{},
	}
	return experiment
}
func (e *Experiment) report(enableStyling bool) string {
	t := table.NewTable()
	t.TableStyle.EnableTextStyling = enableStyling
	t.AppendRow(table.R(
		table.C("Name"), table.C("N"), table.C("Min"), table.C("Median"), table.C("Mean"), table.C("StdDev"), table.C("Max"),
		table.Divider("="),
		"{{bold}}",
	))
	for _, measurement := range e.Measurements {
		r := table.R(measurement.Style)
		t.AppendRow(r)
		switch measurement.Type {
		case MeasurementTypeNote:
			r.AppendCell(table.C(measurement.Note))
		case MeasurementTypeValue, MeasurementTypeDuration:
			name := measurement.Name
			if measurement.Units != "" {
				name += " [" + measurement.Units + "]"
			}
			r.AppendCell(table.C(name))
			r.AppendCell(measurement.Stats().cells()...)
		}
	}
	out := e.Name + "\n"
	if enableStyling {
		out = "{{bold}}" + out + "{{/}}"
	}
	out += t.Render()
	return out
}
func (e *Experiment) ColorableString() string {
	return e.report(true)
}
func (e *Experiment) String() string {
	return e.report(false)
}
func (e *Experiment) RecordNote(note string, args ...interface{}) {
	decorations := extractDecorations(args)
	e.lock.Lock()
	defer e.lock.Unlock()
	e.Measurements = append(e.Measurements, Measurement{
		ExperimentName: e.Name,
		Type:           MeasurementTypeNote,
		Note:           note,
		Style:          string(decorations.style),
	})
}
func (e *Experiment) RecordDuration(name string, duration time.Duration, args ...interface{}) {
	decorations := extractDecorations(args)
	e.recordDuration(name, duration, decorations)
}
func (e *Experiment) MeasureDuration(name string, callback func(), args ...interface{}) time.Duration {
	t := time.Now()
	callback()
	duration := time.Since(t)
	e.RecordDuration(name, duration, args...)
	return duration
}
func (e *Experiment) SampleDuration(name string, callback func(idx int), samplingConfig SamplingConfig, args ...interface{}) {
	decorations := extractDecorations(args)
	e.Sample(func(idx int) {
		t := time.Now()
		callback(idx)
		duration := time.Since(t)
		e.recordDuration(name, duration, decorations)
	}, samplingConfig)
}