kittycad_execution_plan/
arithmetic.rs

1use kittycad_execution_plan_traits::{NumericPrimitive, Primitive};
2use serde::{Deserialize, Serialize};
3
4use self::operator::{BinaryOperation, UnaryOperation};
5use crate::{events::EventWriter, ExecutionError, Memory, Operand};
6
7pub mod operator;
8
9/// Instruction to perform arithmetic on values in memory.
10#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
11pub struct BinaryArithmetic {
12    /// Apply this operation
13    pub operation: BinaryOperation,
14    /// First operand for the operation
15    pub operand0: Operand,
16    /// Second operand for the operation
17    pub operand1: Operand,
18}
19
20/// Instruction to perform arithmetic on values in memory.
21#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
22pub struct UnaryArithmetic {
23    /// Apply this operation
24    pub operation: UnaryOperation,
25    /// Operand for the operation
26    pub operand: Operand,
27}
28
29trait Power {
30    fn power(self, other: Self) -> Self;
31}
32
33macro_rules! power_float_impl {
34    ($($t:ty)*) => ($(
35        impl Power for $t {
36            fn power(self, other: $t) -> $t { self.powf(other) }
37        }
38
39    )*)
40}
41power_float_impl! { f32 f64 }
42
43macro_rules! power_int_impl {
44    ($($t:ty)*) => ($(
45        impl Power for $t {
46            fn power(self, other: $t) -> $t { self.overflowing_pow(other as u32).0 }
47        }
48
49    )*)
50}
51power_int_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 }
52
53macro_rules! execution_error_if_match_otherwise {
54 ([], $b:ident, $err:expr, $otherwise:expr) => { $otherwise };
55 ([u32$(, $as:ident)*], u32, $err:expr, $otherwise:expr) => {
56   $err
57 };
58 ([i64$(, $as:ident)*], i64, $err:expr, $otherwise:expr) => {
59   $err
60 };
61 ([f64$(, $as:ident)*], f64, $err:expr, $otherwise:expr) => {
62   $err
63 };
64 ([$a:ident$(, $as:ident)*], $b:ident, $err:expr, $otherwise:expr) => {
65   execution_error_if_match_otherwise!([$($as)*], $b, $err, $otherwise)
66 };
67}
68
69macro_rules! binary_arithmetic_body {
70    ($arith:ident, $mem:ident, $events:ident, $method:ident, $($types:tt)*) => {{
71        $events.push(crate::events::Event::new(
72            "Evaluating left operand".to_owned(),
73            crate::events::Severity::Debug,
74        ));
75        let l = $arith.operand0.eval($mem)?.clone();
76        $events.push({
77            let mut evt = crate::events::Event::new(format!("Left operand is {l:?}"), crate::events::Severity::Info);
78            if let Operand::Reference(a) = $arith.operand0 {
79                evt.related_addresses = vec![a];
80            }
81            evt
82        });
83        $events.push(crate::events::Event::new(
84            "Evaluating right operand".to_owned(),
85            crate::events::Severity::Debug,
86        ));
87        let r = $arith.operand1.eval($mem)?.clone();
88        $events.push({
89            let mut evt = crate::events::Event::new(format!("Right operand is {r:?}"), crate::events::Severity::Info);
90            if let Operand::Reference(a) = $arith.operand1 {
91                evt.related_addresses = vec![a];
92            }
93            evt
94        });
95        match (l, r) {
96            // If both operands are numeric, then do the arithmetic operation.
97            (Primitive::NumericValue(x), Primitive::NumericValue(y)) => {
98                let num = match (x, y) {
99                    (NumericPrimitive::UInteger(_x), NumericPrimitive::UInteger(_y)) => {
100                        execution_error_if_match_otherwise!(
101                          $($types)*, u32,
102                          Err(ExecutionError::CannotApplyOperation {
103                            op: $arith.operation.into(),
104                            operands: vec![
105                                $arith.operand0.eval($mem)?.clone().to_owned(),
106                                $arith.operand1.eval($mem)?.clone().to_owned(),
107                            ],
108                          })?,
109                          NumericPrimitive::UInteger(_x.$method(_y))
110                        )
111                    }
112                    (NumericPrimitive::UInteger(x), NumericPrimitive::Float(y)) => {
113                        NumericPrimitive::Float((x as f64).$method(y))
114                    }
115                    (NumericPrimitive::Float(x), NumericPrimitive::UInteger(y)) => {
116                        NumericPrimitive::Float(x.$method(y as f64))
117                    }
118                    (NumericPrimitive::Float(x), NumericPrimitive::Float(y)) => NumericPrimitive::Float(x.$method(y)),
119                    (NumericPrimitive::Integer(_x), NumericPrimitive::Integer(_y)) => {
120                        execution_error_if_match_otherwise!(
121                          $($types)*, i64,
122                          Err(ExecutionError::CannotApplyOperation {
123                            op: $arith.operation.into(),
124                            operands: vec![
125                                $arith.operand0.eval($mem)?.clone().to_owned(),
126                                $arith.operand1.eval($mem)?.clone().to_owned(),
127                            ],
128                          })?,
129                          NumericPrimitive::Integer(_x.$method(_y))
130                        )
131                    }
132                    (NumericPrimitive::Integer(x), NumericPrimitive::Float(y)) => {
133                        NumericPrimitive::Float((x as f64).$method(y))
134                    }
135                    (NumericPrimitive::Float(x), NumericPrimitive::Integer(y)) => {
136                        NumericPrimitive::Float(x.$method(y as f64))
137                    }
138                    (NumericPrimitive::Integer(_x), NumericPrimitive::UInteger(_y)) => {
139                        execution_error_if_match_otherwise!(
140                          $($types)*, i64,
141                          Err(ExecutionError::CannotApplyOperation {
142                            op: $arith.operation.into(),
143                            operands: vec![
144                                $arith.operand0.eval($mem)?.clone().to_owned(),
145                                $arith.operand1.eval($mem)?.clone().to_owned(),
146                            ],
147                          })?,
148                          NumericPrimitive::Integer(_x.$method(_y as i64))
149                        )
150                    }
151                    (NumericPrimitive::UInteger(_x), NumericPrimitive::Integer(_y)) => {
152                        execution_error_if_match_otherwise!(
153                          $($types)*, u32,
154                          Err(ExecutionError::CannotApplyOperation {
155                            op: $arith.operation.into(),
156                            operands: vec![
157                                $arith.operand0.eval($mem)?.clone().to_owned(),
158                                $arith.operand1.eval($mem)?.clone().to_owned(),
159                            ],
160                          })?,
161                          NumericPrimitive::Integer((_x as i64).$method(_y))
162                        )
163                    }
164                };
165                let prim = Primitive::NumericValue(num);
166                $events.push(crate::events::Event::new(
167                    format!("Output is {prim:?}"),
168                    crate::events::Severity::Info,
169                ));
170                Ok(prim)
171            }
172            // This operation can only be done on numeric types.
173            _ => Err(ExecutionError::CannotApplyOperation {
174                op: $arith.operation.into(),
175                operands: vec![
176                    $arith.operand0.eval($mem)?.clone().to_owned(),
177                    $arith.operand1.eval($mem)?.clone().to_owned(),
178                ],
179            }),
180        }
181    }};
182}
183
184macro_rules! unary_arithmetic_body {
185    ($arith:ident, $mem:ident, $events:ident, $op:ident, $($types:tt)*) => {{
186        $events.push(crate::events::Event::new(
187            "Evaluating operand".to_owned(),
188            crate::events::Severity::Debug,
189        ));
190        let operand = $arith.operand.eval($mem)?.clone();
191        $events.push({
192            let mut evt = crate::events::Event::new(format!("Operand is {operand:?}"), crate::events::Severity::Info);
193            if let Operand::Reference(a) = $arith.operand {
194                evt.related_addresses = vec![a];
195            }
196            evt
197        });
198        match operand {
199            // If both operands are numeric, then do the arithmetic operation.
200            Primitive::NumericValue(wrapped_x) => {
201                let num = match wrapped_x {
202                    // Looks like the compiler doesn't know x is actually used.
203                    NumericPrimitive::UInteger(_x) => {
204                      execution_error_if_match_otherwise!(
205                        $($types)*, u32,
206                        Err(ExecutionError::CannotApplyOperation {
207                          op: $arith.operation.into(),
208                          operands: vec![ $arith.operand.eval($mem)?.clone().to_owned(), ]
209                        })?,
210                        NumericPrimitive::Integer((_x as i64).$op())
211                      )
212                    }
213                    NumericPrimitive::Float(_x) => {
214                      execution_error_if_match_otherwise!(
215                        $($types)*, f64,
216                        Err(ExecutionError::CannotApplyOperation {
217                          op: $arith.operation.into(),
218                          operands: vec![ $arith.operand.eval($mem)?.clone().to_owned(), ]
219                        })?,
220                        NumericPrimitive::Float(_x.$op())
221                      )
222                    }
223                    NumericPrimitive::Integer(_x) => {
224                      execution_error_if_match_otherwise!(
225                        $($types)*, i64,
226                        Err(ExecutionError::CannotApplyOperation {
227                          op: $arith.operation.into(),
228                          operands: vec![ $arith.operand.eval($mem)?.clone().to_owned(), ]
229                        })?,
230                        NumericPrimitive::Integer(_x.$op())
231                      )
232                    }
233                };
234                let prim = Primitive::NumericValue(num);
235                $events.push(crate::events::Event::new(
236                    format!("Output is {prim:?}"),
237                    crate::events::Severity::Info,
238                ));
239                Ok(prim)
240            }
241            // This operation can only be done on numeric types.
242            _ => Err(ExecutionError::CannotApplyOperation {
243                op: $arith.operation.into(),
244                operands: vec![
245                    $arith.operand.eval($mem)?.clone().to_owned(),
246                ],
247            }),
248        }
249    }};
250}
251impl UnaryArithmetic {
252    /// Calculate unary operations
253    pub fn calculate(self, mem: &mut Memory, events: &mut EventWriter) -> Result<Primitive, ExecutionError> {
254        use std::ops::{Neg, Not};
255        match self.operation {
256            UnaryOperation::Not => {
257                unary_arithmetic_body!(self, mem, events, not, [f64])
258            }
259            UnaryOperation::Neg => {
260                unary_arithmetic_body!(self, mem, events, neg, [])
261            }
262            UnaryOperation::Abs => {
263                unary_arithmetic_body!(self, mem, events, abs, [])
264            }
265            UnaryOperation::Acos => {
266                unary_arithmetic_body!(self, mem, events, acos, [i64, u32])
267            }
268            UnaryOperation::Asin => {
269                unary_arithmetic_body!(self, mem, events, asin, [i64, u32])
270            }
271            UnaryOperation::Atan => {
272                unary_arithmetic_body!(self, mem, events, atan, [i64, u32])
273            }
274            UnaryOperation::Ceil => {
275                unary_arithmetic_body!(self, mem, events, ceil, [i64, u32])
276            }
277            UnaryOperation::Cos => {
278                unary_arithmetic_body!(self, mem, events, cos, [i64, u32])
279            }
280            UnaryOperation::Floor => {
281                unary_arithmetic_body!(self, mem, events, floor, [i64, u32])
282            }
283            UnaryOperation::Ln => {
284                unary_arithmetic_body!(self, mem, events, ln, [i64, u32])
285            }
286            UnaryOperation::Log10 => {
287                unary_arithmetic_body!(self, mem, events, log10, [i64, u32])
288            }
289            UnaryOperation::Log2 => {
290                unary_arithmetic_body!(self, mem, events, log2, [i64, u32])
291            }
292            UnaryOperation::Sin => {
293                unary_arithmetic_body!(self, mem, events, sin, [i64, u32])
294            }
295            UnaryOperation::Sqrt => {
296                unary_arithmetic_body!(self, mem, events, sqrt, [i64, u32])
297            }
298            UnaryOperation::Tan => {
299                unary_arithmetic_body!(self, mem, events, tan, [i64, u32])
300            }
301            UnaryOperation::ToDegrees => {
302                unary_arithmetic_body!(self, mem, events, to_degrees, [i64, u32])
303            }
304            UnaryOperation::ToRadians => {
305                unary_arithmetic_body!(self, mem, events, to_radians, [i64, u32])
306            }
307        }
308    }
309}
310impl BinaryArithmetic {
311    /// Calculate the the arithmetic equation.
312    /// May read values from the given memory.
313    pub fn calculate(self, mem: &mut Memory, events: &mut EventWriter) -> Result<Primitive, ExecutionError> {
314        use std::ops::{Add, Div, Mul, Rem, Sub};
315        match self.operation {
316            BinaryOperation::Add => {
317                binary_arithmetic_body!(self, mem, events, add, [])
318            }
319            BinaryOperation::Mul => {
320                binary_arithmetic_body!(self, mem, events, mul, [])
321            }
322            BinaryOperation::Sub => {
323                binary_arithmetic_body!(self, mem, events, sub, [])
324            }
325            BinaryOperation::Div => {
326                binary_arithmetic_body!(self, mem, events, div, [])
327            }
328            BinaryOperation::Mod => {
329                binary_arithmetic_body!(self, mem, events, rem, [])
330            }
331            BinaryOperation::Pow => {
332                binary_arithmetic_body!(self, mem, events, power, [])
333            }
334            BinaryOperation::Log => {
335                binary_arithmetic_body!(self, mem, events, log, [u32, i64])
336            }
337            BinaryOperation::Min => {
338                binary_arithmetic_body!(self, mem, events, min, [])
339            }
340            BinaryOperation::Max => {
341                binary_arithmetic_body!(self, mem, events, max, [])
342            }
343        }
344    }
345}