2016-07-06 21:54:00 -04:00
|
|
|
package chart
|
|
|
|
|
|
|
|
import (
|
2016-07-07 17:44:03 -04:00
|
|
|
"fmt"
|
2016-07-11 21:48:51 -04:00
|
|
|
"math"
|
2016-07-15 12:17:51 -04:00
|
|
|
"math/rand"
|
2016-07-06 21:54:00 -04:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2016-07-11 21:48:51 -04:00
|
|
|
// Float is an alias for float64 that provides a better .String() method.
|
|
|
|
type Float float64
|
|
|
|
|
|
|
|
// String returns the string representation of a float.
|
|
|
|
func (f Float) String() string {
|
|
|
|
return fmt.Sprintf("%.2f", f)
|
|
|
|
}
|
|
|
|
|
2016-07-11 02:06:14 -04:00
|
|
|
// TimeToFloat64 returns a float64 representation of a time.
|
|
|
|
func TimeToFloat64(t time.Time) float64 {
|
2016-07-17 17:25:42 -04:00
|
|
|
return float64(t.UnixNano())
|
2016-07-11 02:06:14 -04:00
|
|
|
}
|
|
|
|
|
2016-07-06 21:54:00 -04:00
|
|
|
// MinAndMax returns both the min and max in one pass.
|
|
|
|
func MinAndMax(values ...float64) (min float64, max float64) {
|
|
|
|
if len(values) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
min = values[0]
|
|
|
|
max = values[0]
|
|
|
|
for _, v := range values {
|
|
|
|
if max < v {
|
|
|
|
max = v
|
|
|
|
}
|
|
|
|
if min > v {
|
|
|
|
min = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// MinAndMaxOfTime returns the min and max of a given set of times
|
|
|
|
// in one pass.
|
|
|
|
func MinAndMaxOfTime(values ...time.Time) (min time.Time, max time.Time) {
|
|
|
|
if len(values) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
min = values[0]
|
|
|
|
max = values[0]
|
|
|
|
|
|
|
|
for _, v := range values {
|
|
|
|
if max.Before(v) {
|
|
|
|
max = v
|
|
|
|
}
|
|
|
|
if min.After(v) {
|
|
|
|
min = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2016-07-07 20:14:25 -04:00
|
|
|
|
|
|
|
// Slices generates N slices that span the total.
|
|
|
|
// The resulting array will be intermediate indexes until total.
|
2016-07-07 23:26:07 -04:00
|
|
|
func Slices(count int, total float64) []float64 {
|
|
|
|
var values []float64
|
|
|
|
sliceWidth := float64(total) / float64(count)
|
|
|
|
for cursor := 0.0; cursor < total; cursor += sliceWidth {
|
2016-07-07 20:14:25 -04:00
|
|
|
values = append(values, cursor)
|
|
|
|
}
|
|
|
|
return values
|
|
|
|
}
|
2016-07-08 20:57:14 -04:00
|
|
|
|
2016-07-11 21:48:51 -04:00
|
|
|
// GetRoundToForDelta returns a `roundTo` value for a given delta.
|
|
|
|
func GetRoundToForDelta(delta float64) float64 {
|
|
|
|
startingDeltaBound := math.Pow(10.0, 10.0)
|
|
|
|
for cursor := startingDeltaBound; cursor > 0; cursor /= 10.0 {
|
|
|
|
if delta > cursor {
|
|
|
|
return cursor / 10.0
|
|
|
|
}
|
|
|
|
}
|
2016-07-10 04:11:47 -04:00
|
|
|
|
2016-07-11 21:48:51 -04:00
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
|
|
|
|
// RoundUp rounds up to a given roundTo value.
|
|
|
|
func RoundUp(value, roundTo float64) float64 {
|
|
|
|
d1 := math.Ceil(value / roundTo)
|
|
|
|
return d1 * roundTo
|
|
|
|
}
|
|
|
|
|
|
|
|
// RoundDown rounds down to a given roundTo value.
|
|
|
|
func RoundDown(value, roundTo float64) float64 {
|
|
|
|
d1 := math.Floor(value / roundTo)
|
|
|
|
return d1 * roundTo
|
|
|
|
}
|
|
|
|
|
|
|
|
// MinInt returns the minimum of a set of integers.
|
|
|
|
func MinInt(values ...int) int {
|
|
|
|
min := math.MaxInt32
|
|
|
|
for _, v := range values {
|
|
|
|
if v < min {
|
|
|
|
min = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return min
|
|
|
|
}
|
|
|
|
|
|
|
|
// MaxInt returns the maximum of a set of integers.
|
|
|
|
func MaxInt(values ...int) int {
|
|
|
|
max := math.MinInt32
|
|
|
|
for _, v := range values {
|
|
|
|
if v > max {
|
|
|
|
max = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max
|
2016-07-08 20:57:14 -04:00
|
|
|
}
|
2016-07-12 19:47:52 -04:00
|
|
|
|
|
|
|
// AbsInt returns the absolute value of an integer.
|
|
|
|
func AbsInt(value int) int {
|
|
|
|
if value < 0 {
|
|
|
|
return -value
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
2016-07-13 14:50:22 -04:00
|
|
|
|
|
|
|
// Seq produces an array of floats from [start,end] by optional steps.
|
|
|
|
func Seq(start, end float64, steps ...float64) []float64 {
|
|
|
|
var values []float64
|
|
|
|
step := 1.0
|
|
|
|
if len(steps) > 0 {
|
|
|
|
step = steps[0]
|
|
|
|
}
|
2016-07-14 15:30:57 -04:00
|
|
|
|
|
|
|
if start < end {
|
|
|
|
for x := start; x <= end; x += step {
|
|
|
|
values = append(values, x)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for x := start; x >= end; x = x - step {
|
|
|
|
values = append(values, x)
|
|
|
|
}
|
2016-07-13 14:50:22 -04:00
|
|
|
}
|
|
|
|
return values
|
|
|
|
}
|
2016-07-13 18:46:51 -04:00
|
|
|
|
2016-07-15 12:17:51 -04:00
|
|
|
// SeqRand generates a random sequence.
|
|
|
|
func SeqRand(samples int, scale float64) []float64 {
|
|
|
|
rnd := rand.New(rand.NewSource(time.Now().Unix()))
|
|
|
|
values := make([]float64, samples)
|
|
|
|
|
|
|
|
for x := 0; x < samples; x++ {
|
|
|
|
values[x] = rnd.Float64() * scale
|
|
|
|
}
|
|
|
|
|
|
|
|
return values
|
|
|
|
}
|
|
|
|
|
2016-07-17 02:03:01 -04:00
|
|
|
// SeqDays generates a sequence of timestamps by day, from -days to today.
|
|
|
|
func SeqDays(days int) []time.Time {
|
|
|
|
var values []time.Time
|
|
|
|
for day := days; day >= 0; day-- {
|
|
|
|
values = append(values, time.Now().AddDate(0, 0, -day))
|
|
|
|
}
|
|
|
|
return values
|
|
|
|
}
|
|
|
|
|
2016-07-13 19:11:31 -04:00
|
|
|
// PercentDifference computes the percentage difference between two values.
|
2016-07-13 18:46:51 -04:00
|
|
|
// The formula is (v2-v1)/v1.
|
2016-07-13 19:11:31 -04:00
|
|
|
func PercentDifference(v1, v2 float64) float64 {
|
2016-07-13 18:46:51 -04:00
|
|
|
return (v2 - v1) / v1
|
|
|
|
}
|