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