1use float_cmp::{ApproxEq, F64Margin};
2use std::fmt;
3use std::ops::{Add, Div, Mul, Neg, Sub};
4
5#[derive(Debug, Clone, Copy)]
11pub struct Measurement {
12 pub mean: f64, pub sigma: f64, }
15
16impl Measurement {
17 pub fn new(mean: f64, sigma: f64) -> Measurement {
18 Measurement { mean, sigma }
19 }
20}
21
22impl Neg for Measurement {
23 type Output = Self;
24
25 fn neg(self) -> Self {
26 Self {
27 mean: -self.mean,
28 sigma: self.sigma,
29 }
30 }
31}
32
33impl Add for Measurement {
34 type Output = Self;
35
36 fn add(self, other: Self) -> Self {
37 Self {
38 mean: self.mean + other.mean,
39 sigma: quadrature(self.sigma, other.sigma).sqrt(),
40 }
41 }
42}
43
44impl Add<f64> for Measurement {
45 type Output = Self;
46
47 fn add(self, other: f64) -> Self {
48 Self {
49 mean: self.mean + other,
50 sigma: self.sigma,
51 }
52 }
53}
54
55impl Sub for Measurement {
56 type Output = Self;
57
58 fn sub(self, other: Self) -> Self {
59 Self {
60 mean: self.mean - other.mean,
61 sigma: quadrature(self.sigma, other.sigma).sqrt(),
62 }
63 }
64}
65
66impl Sub<f64> for Measurement {
67 type Output = Self;
68
69 fn sub(self, other: f64) -> Self {
70 Self {
71 mean: self.mean - other,
72 sigma: self.sigma,
73 }
74 }
75}
76
77impl Mul for Measurement {
78 type Output = Self;
79
80 fn mul(self, other: Self) -> Self {
81 let new_mean = self.mean * other.mean;
82 Self {
83 mean: new_mean,
84 sigma: quadrature(self.sigma / self.mean, other.sigma / other.mean).sqrt() * new_mean,
85 }
86 }
87}
88
89impl Mul<f64> for Measurement {
90 type Output = Self;
91
92 fn mul(self, other: f64) -> Self {
93 Self {
94 mean: self.mean * other,
95 sigma: self.sigma * other,
96 }
97 }
98}
99
100impl Div for Measurement {
101 type Output = Self;
102
103 fn div(self, other: Self) -> Self {
104 let new_mean = self.mean / other.mean;
105 Self {
106 mean: new_mean,
107 sigma: quadrature(self.sigma / self.mean, other.sigma / other.mean).sqrt() * new_mean,
108 }
109 }
110}
111
112impl Div<f64> for Measurement {
113 type Output = Self;
114
115 fn div(self, other: f64) -> Self {
116 Self {
117 mean: self.mean / other,
118 sigma: self.sigma / other,
119 }
120 }
121}
122
123impl fmt::Display for Measurement {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 write!(f, "{} ± {}", self.mean, self.sigma)
126 }
127}
128
129impl PartialEq for Measurement {
130 fn eq(&self, other: &Self) -> bool {
131 self.mean == other.mean && self.sigma == other.sigma
132 }
133}
134
135impl ApproxEq for Measurement {
136 type Margin = F64Margin;
137
138 fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
139 let margin = margin.into();
140 self.mean.approx_eq(other.mean, margin) && self.sigma.approx_eq(other.sigma, margin)
141 }
142}
143
144impl From<f64> for Measurement {
145 fn from(x: f64) -> Self {
146 Measurement {
147 mean: x,
148 sigma: 0.0,
149 }
150 }
151}
152
153pub fn pow(x: Measurement, y: Measurement) -> Measurement {
154 let [xm, ym, sx, sy] = [x.mean, y.mean, x.sigma, y.sigma];
156 let dfdx = ym * xm.powf(ym - 1.0);
157 let dfdy = xm.ln() * xm.powf(ym);
158
159 Measurement {
160 mean: xm.powf(ym),
161 sigma: quadrature(dfdx * sx, dfdy * sy).sqrt(),
162 }
163}
164
165fn quadrature(x: f64, y: f64) -> f64 {
166 x * x + y * y
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172 #[test]
173 fn simple_operations() {
174 let x = Measurement::new(1.0, 0.01);
175 let y = Measurement::new(2.0, 0.01);
176
177 let added = Measurement::new(3.0, 0.0002f64.sqrt());
178 let subtracted = Measurement::new(-1.0, 0.0002f64.sqrt());
179 let multiplied = Measurement::new(2.0, 2.0 * (quadrature(0.01 / 1.0, 0.01 / 2.0)).sqrt());
180 let divided = Measurement::new(0.5, 0.5 * (quadrature(0.01 / 1.0, 0.01 / 2.0)).sqrt());
181
182 assert!(added.approx_eq(x + y, F64Margin::default()));
183 assert!(subtracted.approx_eq(x - y, F64Margin::default()));
184 assert!(multiplied.approx_eq(x * y, F64Margin::default()));
185 assert!(divided.approx_eq(x / y, F64Margin::default()));
186 }
187 #[test]
188 fn approximate_equality() {
189 let x = Measurement::new(1.0, 0.01);
191 let y = Measurement::new(1.0, 0.001);
192 let x_prime = Measurement::new(1.0, 2.0 * 0.005);
193
194 assert!(x.approx_eq(x, F64Margin::default()));
196 assert_eq!(false, x.approx_eq(y, F64Margin::default()));
198 assert!(x.approx_eq(x_prime, F64Margin::default()));
200 }
201 #[test]
202 fn simple_pow() {
203 let x = Measurement::from(2.0);
204 let y = Measurement::from(3.0);
205 let r = Measurement {
206 mean: 8.0,
207 sigma: 0.0,
208 };
209 assert_eq!(pow(x, y), r);
210 }
211
212 #[test]
213 fn negative_pow() {
214 let x = Measurement::from(2.0);
215 let y = Measurement::from(-3.0);
216 let r = Measurement {
217 mean: 1.0 / 8.0,
218 sigma: 0.0,
219 };
220 assert_eq!(pow(x, y), r);
221 }
222}