-
Notifications
You must be signed in to change notification settings - Fork 3
/
piecewiselinear.go
158 lines (148 loc) · 3.89 KB
/
piecewiselinear.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Package piecewiselinear is a tiny library for linear interpolation.
package piecewiselinear
import (
"math"
)
// Function is a piecewise-linear 1-dimensional function
type Function struct {
X []float64
Y []float64
}
// Area returns the definite integral of the function on its domain X.
//
// Time complexity: O(N), where N is the number of points.
// Space complexity: O(1)
func (f Function) Area() (area float64) {
X, Y := f.X, f.Y
for i := 1; i < len(X); i++ {
area += (X[i] - X[i-1]) * (Y[i] + Y[i-1]) / 2
}
return area
}
// AreaUpTo returns the definite integral of the function on its domain X intersected with [-Inf, x].
//
// Time complexity: O(N), where N is the number of points.
// Space complexity: O(1)
func (f Function) AreaUpTo(x float64) (area float64) {
X, Y := f.X, f.Y
for i := 1; i < len(X); i++ {
dX := X[i] - X[i-1]
if x < X[i] {
if x >= X[i-1] {
dxX := x - X[i-1]
w := dxX / dX
y := (1-w)*Y[i-1] + w*Y[i]
area += dxX * (y + Y[i-1]) / 2
}
return area
}
area += dX * (Y[i] + Y[i-1]) / 2
}
return area
}
// IsInterpolatedAt returns true if x is within the given range of points, false if outside of that range
func (f Function) IsInterpolatedAt(x float64) bool {
n := len(f.X)
if n == 0 {
return false
}
left, right := f.X[0], f.X[n-1]
return x >= left && x <= right
}
// At returns the function's value at the given point.
// Outside its domain X, the function is constant at 0.
//
// The function's X and Y slices are expected to be the same legnth. The length property is _not_ verified.
// The function's X slice is expected to be sorted in ascending order. The sortedness property is _not_ verified.
//
// Time complexity: O(log(N)), where N is the number of points.
// Space complexity: O(1)
func (f Function) At(x float64) float64 {
X, Y := f.X, f.Y
i, j := 0, len(X)
for i < j {
h := int(uint(i+j) >> 1)
if X[h] < x {
i = h + 1
} else {
j = h
}
}
if i == 0 {
if len(X) > 0 && x == X[0] {
return Y[0]
}
return 0
}
if i == len(X) {
return 0
}
w := (x - X[i-1]) / (X[i] - X[i-1])
return (1-w)*Y[i-1] + w*Y[i]
}
// Function is a piecewise-linear 1-dimensional function with uniformly spaced control points. Faster to interpolate than the equivalent `Function`.
type FunctionUniform struct {
Xmin, Xmax float64
Y []float64
}
// At returns the function's value at the given point.
// Outside its domain X, the function is constant at 0.
//
// Time complexity: O(1)
// Space complexity: O(1)
func (f FunctionUniform) At(x float64) float64 {
if x < f.Xmin {
return 0
}
if x > f.Xmax {
return 0
}
step := (f.Xmax - f.Xmin) / float64(len(f.Y)-1)
i := math.Floor(x / step)
j := math.Ceil(x / step)
if i == j {
return f.Y[int(i)]
}
Xi := f.Xmin + (i * step)
Xj := f.Xmin + (j * step)
w := (x - Xi) / (Xj - Xi)
return (1-w)*f.Y[int(i)] + w*f.Y[int(j)]
}
// Area returns the definite integral of the function on its domain X.
//
// Time complexity: O(N), where N is the number of points.
// Space complexity: O(1)
func (f FunctionUniform) Area() (area float64) {
Y := f.Y
step := (f.Xmax - f.Xmin) / float64(len(f.Y)-1)
for i := 1; i < len(Y); i++ {
Xi := f.Xmin + (float64(i) * step)
Xi1 := f.Xmin + (float64(i-1) * step)
area += (Xi - Xi1) * (Y[i] + Y[i-1]) / 2
}
return area
}
// AreaUpTo returns the definite integral of the function on its domain X intersected with [-Inf, x].
//
// Time complexity: O(N), where N is the number of points.
// Space complexity: O(1)
func (f FunctionUniform) AreaUpTo(x float64) (area float64) {
Y := f.Y
step := (f.Xmax - f.Xmin) / float64(len(f.Y)-1)
for i := 1; i < len(Y); i++ {
Xi := f.Xmin + (float64(i) * step)
Xi1 := f.Xmin + (float64(i-1) * step)
dX := Xi - Xi1
if x < Xi {
if x >= Xi1 {
dxX := x - Xi1
w := dxX / dX
y := (1-w)*Y[i-1] + w*Y[i]
area += dxX * (y + Y[i-1]) / 2
}
return area
}
area += dX * (Y[i] + Y[i-1]) / 2
}
return area
}