/
list.go
88 lines (69 loc) · 2.16 KB
/
list.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
package cardiaccycle
import (
"container/ring"
"math"
"sort"
)
type List struct {
*ring.Ring
}
func (l *List) GetAdjacent(n int) []Entry {
out := make([]Entry, 0, 1+2*n)
out = append(out, entryValue(l.Value))
current := l.Ring
for i := 0; i < n; i++ {
current = current.Prev()
out = append(out, entryValue(current.Value))
}
current = l.Ring
for i := 0; i < n; i++ {
current = current.Next()
out = append(out, entryValue(current.Value))
}
return out
}
func (l *List) Extrema(adjacentN, discardN int) (Result, error) {
out := Result{}
synthetic := make([]synthEntry, 0, l.Len())
lastPixelArea := 0.0
for i := 0; i < l.Len(); i++ {
thisEntry := entryValue(l.Value)
adj := l.GetAdjacent(adjacentN)
mapped, err := discardExtremes(adj, discardN)
if err != nil {
return out, err
}
synthEntry := synthEntry{InstanceNumber: thisEntry.InstanceNumber, Metric: median(mapped), TrueMetric: thisEntry.Metric}
synthetic = append(synthetic, synthEntry)
if i > 0 {
if x := math.Abs(thisEntry.Metric - lastPixelArea); x > out.MaxOneStepShift {
out.MaxOneStepShift = x
}
}
lastPixelArea = thisEntry.Metric
l.Ring = l.Next()
}
// Note that we are sorting on the TrueMetric, not the median metric. So,
// while we do define a median metric, we don't record exactly where its max
// and min are.
sort.Slice(synthetic, func(i, j int) bool {
return synthetic[i].TrueMetric < synthetic[j].TrueMetric
})
max := synthetic[len(synthetic)-1]
min := synthetic[0]
// Scale the onestepshift value to represent a fraction of the total range
// starting at 0. Otherwise, people with absolutely higher values will look
// like they have higher onestepshifts, but from a fractional basis they
// might not. (e.g., 2->1 vs 5->2.5 both represent a 50% reduction, but
// unless scaled, the 5->2.5 will look more extreme).
out.MaxOneStepShift = out.MaxOneStepShift / (max.TrueMetric)
out.InstanceNumberAtMax = max.InstanceNumber
out.InstanceNumberAtMin = min.InstanceNumber
out.Max = max.TrueMetric
out.SmoothedMax = max.Metric
out.Min = min.TrueMetric
out.SmoothedMin = min.Metric
out.Discards = discardN
out.Window = adjacentN
return out, nil
}