1
2
3
4
5 package trace
6
7 import (
8 "math"
9 "sort"
10 )
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 type mud struct {
27 sorted, unsorted []edge
28
29
30
31 trackMass float64
32
33
34
35 trackBucket int
36
37
38 trackSum float64
39
40
41 hist [mudDegree]float64
42 }
43
44 const (
45
46
47 mudDegree = 1024
48 )
49
50 type edge struct {
51
52 x, delta float64
53
54 dirac float64
55 }
56
57
58
59 func (d *mud) add(l, r, area float64) {
60 if area == 0 {
61 return
62 }
63
64 if r < l {
65 l, r = r, l
66 }
67
68
69 if l == r {
70 d.unsorted = append(d.unsorted, edge{l, 0, area})
71 } else {
72 delta := area / (r - l)
73 d.unsorted = append(d.unsorted, edge{l, delta, 0}, edge{r, -delta, 0})
74 }
75
76
77 h := &d.hist
78 lbFloat, lf := math.Modf(l * mudDegree)
79 lb := int(lbFloat)
80 if lb >= mudDegree {
81 lb, lf = mudDegree-1, 1
82 }
83 if l == r {
84 h[lb] += area
85 } else {
86 rbFloat, rf := math.Modf(r * mudDegree)
87 rb := int(rbFloat)
88 if rb >= mudDegree {
89 rb, rf = mudDegree-1, 1
90 }
91 if lb == rb {
92 h[lb] += area
93 } else {
94 perBucket := area / (r - l) / mudDegree
95 h[lb] += perBucket * (1 - lf)
96 h[rb] += perBucket * rf
97 for i := lb + 1; i < rb; i++ {
98 h[i] += perBucket
99 }
100 }
101 }
102
103
104 if thresh := float64(d.trackBucket) / mudDegree; l < thresh {
105 if r < thresh {
106 d.trackSum += area
107 } else {
108 d.trackSum += area * (thresh - l) / (r - l)
109 }
110 if d.trackSum >= d.trackMass {
111
112
113 d.setTrackMass(d.trackMass)
114 }
115 }
116 }
117
118
119
120
121
122
123 func (d *mud) setTrackMass(mass float64) {
124 d.trackMass = mass
125
126
127
128 sum := 0.0
129 for i, val := range d.hist[:] {
130 newSum := sum + val
131 if newSum > mass {
132
133 d.trackBucket = i
134 d.trackSum = sum
135 return
136 }
137 sum = newSum
138 }
139 d.trackBucket = len(d.hist)
140 d.trackSum = sum
141 }
142
143
144
145
146
147
148 func (d *mud) approxInvCumulativeSum() (float64, float64, bool) {
149 if d.trackBucket == len(d.hist) {
150 return math.NaN(), math.NaN(), false
151 }
152 return float64(d.trackBucket) / mudDegree, float64(d.trackBucket+1) / mudDegree, true
153 }
154
155
156
157
158
159
160
161
162 func (d *mud) invCumulativeSum(y float64) (float64, bool) {
163 if len(d.sorted) == 0 && len(d.unsorted) == 0 {
164 return math.NaN(), false
165 }
166
167
168 edges := d.unsorted
169 sort.Slice(edges, func(i, j int) bool {
170 return edges[i].x < edges[j].x
171 })
172
173 d.unsorted = nil
174 if d.sorted == nil {
175 d.sorted = edges
176 } else {
177 oldSorted := d.sorted
178 newSorted := make([]edge, len(oldSorted)+len(edges))
179 i, j := 0, 0
180 for o := range newSorted {
181 if i >= len(oldSorted) {
182 copy(newSorted[o:], edges[j:])
183 break
184 } else if j >= len(edges) {
185 copy(newSorted[o:], oldSorted[i:])
186 break
187 } else if oldSorted[i].x < edges[j].x {
188 newSorted[o] = oldSorted[i]
189 i++
190 } else {
191 newSorted[o] = edges[j]
192 j++
193 }
194 }
195 d.sorted = newSorted
196 }
197
198
199 csum, rate, prevX := 0.0, 0.0, 0.0
200 for _, e := range d.sorted {
201 newCsum := csum + (e.x-prevX)*rate
202 if newCsum >= y {
203
204
205 if rate == 0 {
206
207
208
209
210 return e.x, true
211 }
212 return (y-csum)/rate + prevX, true
213 }
214 newCsum += e.dirac
215 if newCsum >= y {
216
217 return e.x, true
218 }
219 csum, prevX = newCsum, e.x
220 rate += e.delta
221 }
222 return prevX, false
223 }
224
View as plain text