uncertain_rs/operations/
arithmetic.rs1use crate::traits::Shareable;
2use crate::{Uncertain, computation::ComputationNode};
3use std::ops::{Add, Div, Mul, Neg, Sub};
4
5pub trait Arithmetic:
7 Add<Output = Self>
8 + Sub<Output = Self>
9 + Mul<Output = Self>
10 + Div<Output = Self>
11 + Clone
12 + Send
13 + Sync
14 + 'static
15{
16 fn zero() -> Self;
18
19 fn one() -> Self;
21}
22
23impl Arithmetic for f64 {
27 fn zero() -> Self {
28 0.0
29 }
30
31 fn one() -> Self {
32 1.0
33 }
34}
35
36impl Arithmetic for f32 {
37 fn zero() -> Self {
38 0.0
39 }
40
41 fn one() -> Self {
42 1.0
43 }
44}
45
46impl Arithmetic for i32 {
47 fn zero() -> Self {
48 0
49 }
50
51 fn one() -> Self {
52 1
53 }
54}
55
56impl Arithmetic for i64 {
57 fn zero() -> Self {
58 0
59 }
60
61 fn one() -> Self {
62 1
63 }
64}
65
66impl Arithmetic for u32 {
67 fn zero() -> Self {
68 0
69 }
70
71 fn one() -> Self {
72 1
73 }
74}
75
76impl Arithmetic for u64 {
77 fn zero() -> Self {
78 0
79 }
80
81 fn one() -> Self {
82 1
83 }
84}
85
86#[derive(Clone, Hash, PartialEq, Eq)]
88pub enum BinaryOperation {
89 Add,
90 Sub,
91 Mul,
92 Div,
93}
94
95impl BinaryOperation {
96 #[must_use]
97 pub fn apply<T>(&self, left: T, right: T) -> T
98 where
99 T: Arithmetic,
100 {
101 match self {
102 BinaryOperation::Add => left + right,
103 BinaryOperation::Sub => left - right,
104 BinaryOperation::Mul => left * right,
105 BinaryOperation::Div => left / right,
106 }
107 }
108}
109
110impl<T> Add for Uncertain<T>
112where
113 T: Arithmetic,
114{
115 type Output = Uncertain<T>;
116
117 fn add(self, rhs: Self) -> Self::Output {
118 let node = ComputationNode::BinaryOp {
119 left: Box::new(self.node),
120 right: Box::new(rhs.node),
121 operation: BinaryOperation::Add,
122 };
123 Uncertain::with_node(node)
124 }
125}
126
127impl<T> Add<T> for Uncertain<T>
128where
129 T: Arithmetic,
130{
131 type Output = Uncertain<T>;
132
133 fn add(self, rhs: T) -> Self::Output {
134 self + Uncertain::point(rhs)
135 }
136}
137
138impl Add<Uncertain<f64>> for f64 {
139 type Output = Uncertain<f64>;
140
141 fn add(self, rhs: Uncertain<f64>) -> Self::Output {
142 Uncertain::point(self) + rhs
143 }
144}
145
146impl<T> Sub for Uncertain<T>
148where
149 T: Arithmetic,
150{
151 type Output = Uncertain<T>;
152
153 fn sub(self, rhs: Self) -> Self::Output {
154 let node = ComputationNode::BinaryOp {
155 left: Box::new(self.node),
156 right: Box::new(rhs.node),
157 operation: BinaryOperation::Sub,
158 };
159 Uncertain::with_node(node)
160 }
161}
162
163impl<T> Sub<T> for Uncertain<T>
164where
165 T: Arithmetic,
166{
167 type Output = Uncertain<T>;
168
169 fn sub(self, rhs: T) -> Self::Output {
170 self - Uncertain::point(rhs)
171 }
172}
173
174impl Sub<Uncertain<f64>> for f64 {
175 type Output = Uncertain<f64>;
176
177 fn sub(self, rhs: Uncertain<f64>) -> Self::Output {
178 Uncertain::point(self) - rhs
179 }
180}
181
182impl<T> Mul for Uncertain<T>
184where
185 T: Arithmetic,
186{
187 type Output = Uncertain<T>;
188
189 fn mul(self, rhs: Self) -> Self::Output {
190 let node = ComputationNode::BinaryOp {
191 left: Box::new(self.node),
192 right: Box::new(rhs.node),
193 operation: BinaryOperation::Mul,
194 };
195 Uncertain::with_node(node)
196 }
197}
198
199impl<T> Mul<T> for Uncertain<T>
200where
201 T: Arithmetic,
202{
203 type Output = Uncertain<T>;
204
205 fn mul(self, rhs: T) -> Self::Output {
206 self * Uncertain::point(rhs)
207 }
208}
209
210impl Mul<Uncertain<f64>> for f64 {
211 type Output = Uncertain<f64>;
212
213 fn mul(self, rhs: Uncertain<f64>) -> Self::Output {
214 Uncertain::point(self) * rhs
215 }
216}
217
218impl<T> Div for Uncertain<T>
220where
221 T: Arithmetic,
222{
223 type Output = Uncertain<T>;
224
225 fn div(self, rhs: Self) -> Self::Output {
226 let node = ComputationNode::BinaryOp {
227 left: Box::new(self.node),
228 right: Box::new(rhs.node),
229 operation: BinaryOperation::Div,
230 };
231 Uncertain::with_node(node)
232 }
233}
234
235impl<T> Div<T> for Uncertain<T>
236where
237 T: Arithmetic,
238{
239 type Output = Uncertain<T>;
240
241 fn div(self, rhs: T) -> Self::Output {
242 self / Uncertain::point(rhs)
243 }
244}
245
246impl Div<Uncertain<f64>> for f64 {
247 type Output = Uncertain<f64>;
248
249 fn div(self, rhs: Uncertain<f64>) -> Self::Output {
250 Uncertain::point(self) / rhs
251 }
252}
253
254impl<T> Neg for Uncertain<T>
256where
257 T: Neg<Output = T> + Shareable,
258{
259 type Output = Uncertain<T>;
260
261 fn neg(self) -> Self::Output {
262 self.map(|x| -x)
263 }
264}
265
266impl Uncertain<f64> {
268 #[must_use]
278 pub fn pow(&self, exponent: f64) -> Uncertain<f64> {
279 self.map(move |x| x.powf(exponent))
280 }
281
282 #[must_use]
292 pub fn sqrt(&self) -> Uncertain<f64> {
293 self.map(f64::sqrt)
294 }
295
296 #[must_use]
306 pub fn ln(&self) -> Uncertain<f64> {
307 self.map(f64::ln)
308 }
309
310 #[must_use]
320 pub fn exp(&self) -> Uncertain<f64> {
321 self.map(f64::exp)
322 }
323
324 #[must_use]
334 pub fn abs(&self) -> Uncertain<f64> {
335 self.map(f64::abs)
336 }
337
338 #[must_use]
340 pub fn sin(&self) -> Uncertain<f64> {
341 self.map(f64::sin)
342 }
343
344 #[must_use]
346 pub fn cos(&self) -> Uncertain<f64> {
347 self.map(f64::cos)
348 }
349
350 #[must_use]
352 pub fn tan(&self) -> Uncertain<f64> {
353 self.map(f64::tan)
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360
361 #[test]
362 fn test_addition() {
363 let x = Uncertain::point(5.0);
364 let y = Uncertain::point(3.0);
365 let sum = x + y;
366 assert!((sum.sample() - 8.0_f64).abs() < f64::EPSILON);
367 }
368
369 #[test]
370 fn test_scalar_addition() {
371 let x = Uncertain::point(5.0);
372 let sum = x + 3.0;
373 assert!((sum.sample() - 8.0_f64).abs() < f64::EPSILON);
374
375 let sum2 = 3.0 + Uncertain::point(5.0);
376 assert!((sum2.sample() - 8.0_f64).abs() < f64::EPSILON);
377 }
378
379 #[test]
380 fn test_multiplication() {
381 let x = Uncertain::point(4.0);
382 let y = Uncertain::point(3.0);
383 let product = x * y;
384 assert!((product.sample() - 12.0_f64).abs() < f64::EPSILON);
385 }
386
387 #[test]
388 fn test_complex_expression() {
389 let x = Uncertain::point(2.0);
390 let y = Uncertain::point(3.0);
391 let result = (x + y) * 2.0 - 1.0;
392 assert!((result.sample() - 9.0_f64).abs() < f64::EPSILON); }
394
395 #[test]
396 fn test_mathematical_functions() {
397 let x = Uncertain::point(4.0);
398 assert!((x.sqrt().sample() - 2.0_f64).abs() < f64::EPSILON);
399
400 let y = Uncertain::point(2.0);
401 assert!((y.pow(3.0).sample() - 8.0_f64).abs() < f64::EPSILON);
402 }
403}