-
Notifications
You must be signed in to change notification settings - Fork 0
/
decaying_average.go
105 lines (87 loc) · 2.87 KB
/
decaying_average.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package lrc
import (
"fmt"
"math"
"time"
"github.com/lightningnetwork/lnd/clock"
)
// DecayingAverageStart represents an optional start value to create a
// decaying average with.
type DecayingAverageStart struct {
LastUpdate time.Time
Value float64
}
type decayingAverage struct {
lastUpdate time.Time
value float64
decayRate float64
clock clock.Clock
}
// calcualteDecayRate determines the decay rate required for the period over
// which a decaying average is calculated.
func calcualteDecayRate(period time.Duration) float64 {
return math.Pow(0.5, 2/period.Seconds())
}
// newDecayingAverage creates a decaying average over the period provided, with
// a starting value of 0 if no start value is provided and bootstrapping from
// existing values otherwise.
func newDecayingAverage(clock clock.Clock, period time.Duration,
startValue *DecayingAverageStart) *decayingAverage {
lastUpdate := time.Time{}
value := 0.0
if startValue != nil {
lastUpdate = startValue.LastUpdate
value = startValue.Value
}
return &decayingAverage{
lastUpdate: lastUpdate,
value: value,
decayRate: calcualteDecayRate(period),
clock: clock,
}
}
// update modifies the decaying average's value to reflect its value at the
// current time. This function *must* be used when reading and writing the
// average's value.
func (d *decayingAverage) update(updateTime time.Time) {
var (
lastUpdateDiff = updateTime.Sub(d.lastUpdate).Seconds()
)
// If no time has passed, we don't need to apply an update.
if lastUpdateDiff == 0 {
return
}
d.value = d.value * math.Pow(d.decayRate, lastUpdateDiff)
d.lastUpdate = updateTime
}
// getValue updates the decaying average to the present and returns its
// current value.
func (d *decayingAverage) getValue() float64 {
d.update(d.clock.Now())
return d.value
}
// add updates the decaying average to the present and adds the value provided.
func (d *decayingAverage) add(value float64) {
d.addAtTime(value, d.clock.Now())
}
// addAtTime adds a value to the decaying average at a specific timestamp. This
// should be called with the current time to apply current updates, and may
// be called with times in the past to bootstrap previous values. If
// bootstrapping, values must be provided in chronological order (we can't
// add a value at a timestamp that is before our last update).
func (d *decayingAverage) addAtTime(value float64, ts time.Time) error {
if !d.lastUpdate.IsZero() && ts.Before(d.lastUpdate) {
return fmt.Errorf("updated decaying average at: %v with "+
"value: %v after last update time of: %v",
ts, value, d.lastUpdate)
}
d.update(ts)
d.value += value
d.lastUpdate = ts
return nil
}
// DebugValue returns the value of the decaying average without updating its
// timestamp. This should *not* be used outside of debugging contexts.
func (d *decayingAverage) DebugValue() float64 {
return d.value
}