squiid_engine/
engine.rs

1use std::collections::{HashMap, HashSet, VecDeque};
2
3use rust_decimal::{prelude::ToPrimitive, Decimal, MathematicalOps};
4use rust_decimal_macros::dec;
5
6use crate::{
7    bucket::{Bucket, BucketTypes, ConstantTypes, CONSTANT_IDENTIFIERS},
8    utils::ID_REGEX,
9    EngineSignal,
10};
11
12/// The core evaluation engine responsible for processing Reverse Polish Notation (RPN) operations.
13///
14/// The [`Engine`] maintains a stack, variable storage, and undo history to facilitate command execution
15/// and state management.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Engine {
18    /// The stack of bucket items
19    pub stack: Vec<Bucket>,
20    /// Hashmap of set variables
21    pub variables: HashMap<String, Bucket>,
22    /// History vecdeque for undo support
23    pub undo_history: VecDeque<Vec<Bucket>>,
24    /// Variables vecdeque for undo support
25    pub undo_variable_history: VecDeque<HashMap<String, Bucket>>,
26    /// Offset pointer to the current index of the undo history.
27    /// Index will be calculated by `history.len() - pointer - 1`
28    pub undo_state_pointer: u8,
29    /// Previous answer
30    pub previous_answer: Bucket,
31}
32
33impl Engine {
34    /// Initializes an empty stack, variable storage, and undo history, with the previous answer set to zero.
35    pub fn new() -> Engine {
36        Engine {
37            stack: Vec::new(),
38            variables: HashMap::new(),
39            undo_history: VecDeque::new(),
40            undo_variable_history: VecDeque::new(),
41            undo_state_pointer: 0,
42            previous_answer: Bucket::from(0),
43        }
44    }
45
46    /// Adds an item to the stack, resolving variables and constants if necessary.
47    ///
48    /// # Arguments
49    ///
50    /// * `item` - A [`Bucket`] containing the value to be added to the stack.
51    ///
52    /// # Behavior
53    ///
54    /// - If `item` is `"@"`, it is replaced with the value of `self.previous_answer`.
55    /// - If `item` is a variable (its string representation starts with `$`), it is resolved using `self.variables`.
56    ///   - If the variable is undefined, an error is returned.
57    /// - If `item` is a recognized constant, it is converted accordingly.
58    /// - If `item` is a numeric string, it is parsed into a `Float` bucket.
59    /// - Otherwise, `item` is stored as a `String` bucket.
60    ///
61    /// # Returns
62    ///
63    /// - `Ok(EngineSignal::StackUpdated)` if the item is successfully added to the stack.
64    /// - `Err(String)` if an undefined variable is referenced.
65    ///
66    /// # Errors
67    ///
68    /// Returns an error if the function encounters a reference to an undefined variable.
69    ///
70    /// # Side Effects
71    ///
72    /// - Modifies `self.stack` by pushing the resolved `Bucket`.
73    ///
74    /// # Example
75    ///
76    /// ```rust
77    /// use squiid_engine::bucket::Bucket;
78    /// use squiid_engine::engine::Engine;
79    ///
80    /// let mut engine = Engine::new();
81    /// engine.variables.insert("x".to_string(), Bucket::from(42.0));
82    /// assert!(engine.add_item_to_stack(Bucket::from("$x")).is_ok()); // Pushes 42.0 to the stack.
83    /// assert!(engine.add_item_to_stack(Bucket::from("$y")).is_err()); // Error: Undefined variable.
84    /// ```
85    pub fn add_item_to_stack(&mut self, item: Bucket) -> Result<EngineSignal, String> {
86        // Convert item to string
87        let mut item_string = item.to_string();
88
89        // substitute previous answer
90        if item_string == "@" {
91            item_string = self.previous_answer.to_string();
92        }
93
94        // Replace with value if item is a variable
95        if item_string.starts_with('$') {
96            // Remove $ prefix from name
97            item_string.remove(0);
98            // Get variable from hashmap
99            let unresolved_var = self.variables.get(&item_string);
100
101            match unresolved_var {
102                Some(value) => item_string = value.to_string(),
103                None => return Err(format!("reference to undefined variable: {}", item_string)),
104            }
105        }
106
107        // create a Float if item_string is numeric, else String
108        let item_pushable: Bucket = match item.bucket_type {
109            BucketTypes::Undefined => Bucket::new_undefined(),
110            BucketTypes::Constant(constant_type) => {
111                // bucket already has a constant type, use that
112                Bucket::from_constant(constant_type)
113            }
114            BucketTypes::Float | BucketTypes::String => {
115                // test all other options
116                match CONSTANT_IDENTIFIERS.get(item_string.as_str()) {
117                    Some(&constant) => Bucket::from_constant(constant),
118                    None => match item_string.parse::<f64>() {
119                        Ok(val) => Bucket::from(val),
120                        Err(_) => Bucket::from(item_string),
121                    },
122                }
123            }
124        };
125
126        // push the new item to the stack
127        self.stack.push(item_pushable);
128
129        Ok(EngineSignal::StackUpdated)
130    }
131
132    /// Retrieves a specified number of operands from the stack as `f64` values.
133    ///
134    /// # Arguments
135    ///
136    /// * `number` - The number of operands to retrieve from the stack.
137    ///
138    /// # Behavior
139    ///
140    /// - Ensures that the stack contains at least `number` elements.
141    /// - Checks that all requested operands are valid types ([`BucketTypes::Float`] or [`BucketTypes::Constant`]).
142    /// - Extracts the operands from the stack, converting them to `f64`.
143    /// - Returns the operands in the correct order.
144    ///
145    /// # Returns
146    ///
147    /// - `Ok(Vec<f64>)` containing the extracted operands.
148    /// - `Err(String)` if:
149    ///   - The stack does not contain enough items.
150    ///   - Any of the operands are of an invalid type ([`BucketTypes::String`] or [`BucketTypes::Undefined`]).
151    ///   - An operand is missing a value.
152    ///   - An operand cannot be parsed as `f64`.
153    ///
154    /// # Errors
155    ///
156    /// Returns an error if:
157    /// - The stack has fewer items than `number`.
158    /// - An operand is of an incompatible type.
159    /// - Parsing an operand as `f64` fails.
160    ///
161    /// # Side Effects
162    ///
163    /// - Modifies `self.stack` by removing the retrieved operands.
164    ///
165    /// # Example
166    ///
167    /// ```rust
168    /// use squiid_engine::bucket::Bucket;
169    /// use squiid_engine::engine::Engine;
170    ///
171    /// let mut engine = Engine::new();
172    /// engine.stack.push(Bucket::from(3.5));
173    /// engine.stack.push(Bucket::from(2.0));
174    ///
175    /// let result = engine.get_operands_as_f(2);
176    /// assert_eq!(result, Ok(vec![3.5, 2.0])); // Successfully retrieved operands.
177    /// ```
178    pub fn get_operands_as_f(&mut self, number: i32) -> Result<Vec<f64>, String> {
179        // Make sure there are actually enough items on the stack
180        if self.stack.len() as i32 >= number {
181            // Create vector to store operands
182            let mut operands = Vec::new();
183            // check that all items are of expected type
184            let requested_operands = &self.stack[self.stack.len() - number as usize..];
185            for item in requested_operands {
186                match item.bucket_type {
187                    BucketTypes::String | BucketTypes::Undefined => {
188                        return Err(String::from(
189                            "The operation cannot be performed on these operands",
190                        ));
191                    }
192                    BucketTypes::Float | BucketTypes::Constant(_) => (),
193                }
194            }
195
196            // Add requested number of operands from stack to vector and converts them to strings
197            for _ in 0..number {
198                let operand = self
199                    .stack
200                    .pop()
201                    .ok_or_else(|| String::from("Failed to pop operand"))?;
202
203                // this is safe as we tested above for invalid variants
204                let value = operand
205                    .value
206                    .ok_or_else(|| String::from("Operand value is missing"))?;
207                operands.push(
208                    value
209                        .parse::<f64>()
210                        .map_err(|e| format!("Failed to parse operand as f64: {}", e))?,
211                );
212            }
213            // Make the new vector's order match the stack
214            operands.reverse();
215            Ok(operands)
216        } else {
217            Err(String::from("Not enough items on stack for operation"))
218        }
219    }
220
221    /// Retrieves a specified number of operands from the stack as [`Decimal`] values.
222    ///
223    /// # Arguments
224    ///
225    /// * `number` - The number of operands to retrieve from the stack.
226    ///
227    /// # Behavior
228    ///
229    /// - Ensures that the stack contains at least `number` elements.
230    /// - Checks that all requested operands are valid types ([`BucketTypes::Float`] or [`BucketTypes::Constant`]).
231    /// - Converts recognized mathematical constants (`π`, `e`, etc.) into their corresponding [`Decimal`] values.
232    /// - Parses other valid numeric values into [`Decimal`].
233    /// - Returns the operands in the correct order.
234    ///
235    /// # Returns
236    ///
237    /// - `Ok(Vec<Decimal>)` containing the extracted operands.
238    /// - `Err(String)` if:
239    ///   - The stack does not contain enough items.
240    ///   - Any of the operands are of an invalid type ([`BucketTypes::String`] or [`BucketTypes::Undefined`]).
241    ///   - An operand is missing a value.
242    ///   - Parsing an operand as [`Decimal`] fails.
243    ///
244    /// # Errors
245    ///
246    /// Returns an error if:
247    /// - The stack has fewer items than `number`.
248    /// - An operand is of an incompatible type.
249    /// - Parsing an operand as [`Decimal`] fails.
250    ///
251    /// # Side Effects
252    ///
253    /// - Modifies `self.stack` by removing the retrieved operands.
254    ///
255    /// # Example
256    ///
257    /// ```rust
258    /// use squiid_engine::bucket::Bucket;
259    /// use squiid_engine::engine::Engine;
260    ///
261    /// let mut engine = Engine::new();
262    /// engine.stack.push(Bucket::from(3.1415926535));
263    /// engine.stack.push(Bucket::from(2.0));
264    ///
265    /// let result = engine.get_operands_as_dec(2);
266    /// assert!(result.is_ok()); // Successfully retrieved operands as Decimals.
267    /// ```
268    pub fn get_operands_as_dec(&mut self, number: i32) -> Result<Vec<Decimal>, String> {
269        // Make sure there are actually enough items on the stack
270        if self.stack.len() as i32 >= number {
271            // Create vector to store operands
272            let mut operands = Vec::new();
273            // check that all items are of expected type
274            let requested_operands = &self.stack[self.stack.len() - number as usize..];
275            for item in requested_operands {
276                match item.bucket_type {
277                    BucketTypes::String | BucketTypes::Undefined => {
278                        return Err(String::from(
279                            "The operation cannot be performed on these operands",
280                        ));
281                    }
282                    BucketTypes::Float | BucketTypes::Constant(_) => (),
283                }
284            }
285
286            // Add requested number of operands from stack to vector and converts them to strings
287            for _ in 0..number {
288                let operand = self
289                    .stack
290                    .pop()
291                    .ok_or_else(|| String::from("Failed to pop operand"))?;
292                operands.push(match operand.bucket_type {
293                    BucketTypes::Constant(ConstantTypes::Pi) => Decimal::PI,
294                    BucketTypes::Constant(ConstantTypes::E) => Decimal::E,
295                    BucketTypes::Constant(ConstantTypes::HalfPi) => Decimal::HALF_PI,
296                    BucketTypes::Constant(ConstantTypes::QuarterPi) => Decimal::QUARTER_PI,
297                    BucketTypes::Constant(ConstantTypes::TwoPi) => Decimal::TWO_PI,
298                    BucketTypes::Float
299                    | BucketTypes::Constant(ConstantTypes::C)
300                    | BucketTypes::Constant(ConstantTypes::G)
301                    | BucketTypes::Constant(ConstantTypes::ThirdPi)
302                    | BucketTypes::Constant(ConstantTypes::SixthPi)
303                    | BucketTypes::Constant(ConstantTypes::EighthPi)
304                    | BucketTypes::Constant(ConstantTypes::Phi) => {
305                        match Decimal::from_str_exact(
306                            &operand
307                                .value
308                                .ok_or_else(|| String::from("Operand value is missing"))?,
309                        ) {
310                            Ok(value) => value,
311                            Err(e) => return Err(e.to_string()),
312                        }
313                    }
314                    BucketTypes::String | BucketTypes::Undefined => {
315                        unreachable!("we've already checked that each operand on the stack is not an invalid type: operands as dec")
316                    }
317                });
318            }
319            // Make the new vector's order match the stack
320            operands.reverse();
321            Ok(operands)
322        } else {
323            Err(String::from("Not enough items on stack for operation"))
324        }
325    }
326
327    /// Retrieves a specified number of operands from the stack as [`String`] values.
328    ///
329    /// # Arguments
330    ///
331    /// * `number` - The number of operands to retrieve from the stack.
332    ///
333    /// # Behavior
334    ///
335    /// - Ensures that the stack contains at least `number` elements.
336    /// - Converts each operand to a [`String`].
337    /// - Maintains the correct order of retrieved operands.
338    ///
339    /// # Returns
340    ///
341    /// - `Ok(Vec<String>)` containing the extracted operands as strings.
342    /// - `Err(String)` if there are not enough items on the stack.
343    ///
344    /// # Errors
345    ///
346    /// Returns an error if the stack has fewer items than `number`.
347    ///
348    /// # Side Effects
349    ///
350    /// - Modifies `self.stack` by removing the retrieved operands.
351    ///
352    /// # Example
353    ///
354    /// ```rust
355    /// use squiid_engine::bucket::Bucket;
356    /// use squiid_engine::engine::Engine;
357    ///
358    /// let mut engine = Engine::new();
359    /// engine.stack.push(Bucket::from("hello"));
360    /// engine.stack.push(Bucket::from("world"));
361    ///
362    /// let result = engine.get_operands_as_string(2);
363    /// assert_eq!(result.unwrap(), vec!["hello", "world"]);
364    /// ```
365    pub fn get_operands_as_string(&mut self, number: i32) -> Result<Vec<String>, String> {
366        // Make sure there are actually enough items on the stack
367        if self.stack.len() as i32 >= number {
368            // Create vector to store operands
369            let mut operands = Vec::new();
370            // we can skip the type check since everything is already a string
371
372            // Add requested number of operands from stack to vector and converts them to strings
373            for _ in 0..number {
374                let operand = self
375                    .stack
376                    .pop()
377                    .ok_or_else(|| String::from("Failed to pop operand"))?;
378
379                operands.push(operand.to_string());
380            }
381            // Make the new vector's order match the stack
382            operands.reverse();
383            Ok(operands)
384        } else {
385            Err(String::from("Not enough items on stack for operation"))
386        }
387    }
388
389    /// Retrieves a specified number of operands from the stack as raw [`Bucket`] values.
390    ///
391    /// # Arguments
392    ///
393    /// * `number` - The number of operands to retrieve from the stack.
394    ///
395    /// # Behavior
396    ///
397    /// - Ensures that the stack contains at least `number` elements.
398    /// - Extracts the top `number` elements from the stack without modifying their types.
399    /// - Maintains the original order of retrieved operands.
400    ///
401    /// # Returns
402    ///
403    /// - `Ok(Vec<Bucket>)` containing the extracted operands in their raw form.
404    /// - `Err(String)` if there are not enough items on the stack.
405    ///
406    /// # Errors
407    ///
408    /// Returns an error if the stack has fewer items than `number`.
409    ///
410    /// # Side Effects
411    ///
412    /// - Modifies `self.stack` by removing the retrieved operands.
413    ///
414    /// # Example
415    ///
416    /// ```rust
417    /// use squiid_engine::bucket::Bucket;
418    /// use squiid_engine::engine::Engine;
419    ///
420    /// let mut engine = Engine::new();
421    /// engine.stack.push(Bucket::from(3.14));
422    /// engine.stack.push(Bucket::from("test"));
423    ///
424    /// let result = engine.get_operands_raw(2);
425    /// assert!(result.is_ok());
426    /// assert_eq!(result.unwrap(), vec![Bucket::from(3.14), Bucket::from("test")]);
427    /// ```
428    pub fn get_operands_raw(&mut self, number: i32) -> Result<Vec<Bucket>, String> {
429        if self.stack.len() as i32 >= number {
430            // Create vector to store operands
431            let mut operands = Vec::new();
432
433            // Add requested number of operands from stack to vector and converts them to strings
434            for _ in 0..number {
435                let operand = self
436                    .stack
437                    .pop()
438                    .ok_or_else(|| String::from("Failed to pop operand"))?;
439
440                operands.push(operand);
441            }
442            // Make the new vector's order match the stack
443            operands.reverse();
444            Ok(operands)
445        } else {
446            Err(String::from("Not enough items on stack for operation"))
447        }
448    }
449
450    /// Updates the `previous_answer` variable to the last item on the stack.
451    ///
452    /// # Behavior
453    ///
454    /// - If the stack is not empty, the last item is cloned and stored as `previous_answer`.
455    /// - If the stack is empty, returns an error.
456    ///
457    /// # Algebraic Mode Consideration
458    ///
459    /// - If you're application has an algebraic mode, this function **must** be called **after** a full
460    ///   algebraic expression has been executed. This ensures that `previous_answer` is updated at the
461    ///   **end** of the statement, rather than in the middle of execution.
462    ///
463    /// # Returns
464    ///
465    /// - `Ok(EngineSignal::NOP)` if `previous_answer` is successfully updated.
466    /// - `Err(String)` if the stack is empty.
467    ///
468    /// # Errors
469    ///
470    /// Returns an error if the stack is empty, as there is no value to store as `previous_answer`.
471    ///
472    /// # Example
473    ///
474    /// ```rust
475    /// use squiid_engine::bucket::Bucket;
476    /// use squiid_engine::engine::Engine;
477    ///
478    /// let mut engine = Engine::new();
479    /// engine.stack.push(Bucket::from(42));
480    ///
481    /// assert!(engine.update_previous_answer().is_ok());
482    /// assert_eq!(engine.previous_answer, Bucket::from(42));
483    /// ```
484    pub fn update_previous_answer(&mut self) -> Result<EngineSignal, String> {
485        match self.stack.last() {
486            Some(last) => {
487                self.previous_answer = last.clone();
488                Ok(EngineSignal::NOP)
489            }
490            None => Err(String::from("stack is empty")),
491        }
492    }
493
494    /// Performs addition on the top two operands of the stack.
495    ///
496    /// # Behavior
497    ///
498    /// - Retrieves the top two values from the stack as [`Decimal`] numbers.
499    /// - Computes the sum of these values.
500    /// - Pushes the result back onto the stack.
501    /// - Returns [`EngineSignal::StackUpdated`] to indicate a successful operation.
502    ///
503    /// # Returns
504    ///
505    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
506    /// - `Err(String)` if there are not enough operands on the stack or if the operands are invalid.
507    ///
508    /// # Errors
509    ///
510    /// This function will return an error if:
511    /// - The stack contains fewer than two operands.
512    /// - The operands are of an invalid type that cannot be converted to [`Decimal`].
513    ///
514    /// # Example
515    ///
516    /// ```rust
517    /// use squiid_engine::bucket::Bucket;
518    /// use squiid_engine::engine::Engine;
519    ///
520    /// let mut engine = Engine::new();
521    /// engine.stack.push(Bucket::from(5));
522    /// engine.stack.push(Bucket::from(10));
523    ///
524    /// assert!(engine.add().is_ok());
525    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(15));
526    /// ```
527    pub fn add(&mut self) -> Result<EngineSignal, String> {
528        let operands = self.get_operands_as_dec(2)?;
529
530        // Put result on stack
531        let result = operands[0] + operands[1];
532        let _ = self.add_item_to_stack(result.into());
533        Ok(EngineSignal::StackUpdated)
534    }
535
536    /// Performs subtraction on the top two operands of the stack.
537    ///
538    /// # Behavior
539    ///
540    /// - Retrieves the top two values from the stack as [`Decimal`] numbers.
541    /// - Computes the difference of these values.
542    /// - Pushes the result back onto the stack.
543    /// - Returns [`EngineSignal::StackUpdated`] to indicate a successful operation.
544    ///
545    /// # Returns
546    ///
547    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
548    /// - `Err(String)` if there are not enough operands on the stack or if the operands are invalid.
549    ///
550    /// # Errors
551    ///
552    /// This function will return an error if:
553    /// - The stack contains fewer than two operands.
554    /// - The operands are of an invalid type that cannot be converted to [`Decimal`].
555    ///
556    /// # Example
557    ///
558    /// ```rust
559    /// use squiid_engine::bucket::Bucket;
560    /// use squiid_engine::engine::Engine;
561    ///
562    /// let mut engine = Engine::new();
563    /// engine.stack.push(Bucket::from(5));
564    /// engine.stack.push(Bucket::from(10));
565    ///
566    /// assert!(engine.subtract().is_ok());
567    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(-5));
568    /// ```
569    pub fn subtract(&mut self) -> Result<EngineSignal, String> {
570        // Get operands
571        let operands = self.get_operands_as_dec(2)?;
572
573        // Put result on stack
574        let result = operands[0] - operands[1];
575        let _ = self.add_item_to_stack(result.into());
576        Ok(EngineSignal::StackUpdated)
577    }
578
579    /// Performs multiplication on the top two operands of the stack.
580    ///
581    /// # Behavior
582    ///
583    /// - Retrieves the top two values from the stack as [`Decimal`] numbers.
584    /// - Computes the product of these values.
585    /// - Pushes the result back onto the stack.
586    /// - Returns [`EngineSignal::StackUpdated`] to indicate a successful operation.
587    ///
588    /// # Returns
589    ///
590    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
591    /// - `Err(String)` if there are not enough operands on the stack or if the operands are invalid.
592    ///
593    /// # Errors
594    ///
595    /// This function will return an error if:
596    /// - The stack contains fewer than two operands.
597    /// - The operands are of an invalid type that cannot be converted to [`Decimal`].
598    ///
599    /// # Example
600    ///
601    /// ```rust
602    /// use squiid_engine::bucket::Bucket;
603    /// use squiid_engine::engine::Engine;
604    ///
605    /// let mut engine = Engine::new();
606    /// engine.stack.push(Bucket::from(5));
607    /// engine.stack.push(Bucket::from(10));
608    ///
609    /// assert!(engine.multiply().is_ok());
610    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(50));
611    /// ```
612    pub fn multiply(&mut self) -> Result<EngineSignal, String> {
613        // Get operands
614        let operands = self.get_operands_as_dec(2)?;
615
616        // manual handling for 2PI precision
617        let check_pi = HashSet::from([Decimal::PI, dec!(2.0)]);
618        let operands_set: HashSet<Decimal> = operands.clone().into_iter().collect();
619        let non_matching_operands = check_pi
620            .symmetric_difference(&operands_set)
621            .collect::<Vec<_>>();
622
623        let result = if non_matching_operands.is_empty() {
624            // the only things on the mulitplication stack are 2 and pi, replace with the constant
625            Bucket::from_constant(ConstantTypes::TwoPi)
626        } else {
627            // not 2*pi, perform normal mulitplication
628            Bucket::from(operands[0] * operands[1])
629        };
630        // Put result on stack
631        let _ = self.add_item_to_stack(result);
632        Ok(EngineSignal::StackUpdated)
633    }
634
635    /// Performs division on the top two operands of the stack.
636    ///
637    /// # Behavior
638    ///
639    /// - Retrieves the top two values from the stack as [`Decimal`] numbers.
640    /// - Computes the quotient of these values.
641    /// - Pushes the result back onto the stack.
642    /// - Returns [`EngineSignal::StackUpdated`] to indicate a successful operation.
643    ///
644    /// # Returns
645    ///
646    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
647    /// - `Err(String)` if there are not enough operands on the stack or if the operands are invalid.
648    ///
649    /// # Errors
650    ///
651    /// This function will return an error if:
652    /// - The stack contains fewer than two operands.
653    /// - The operands are of an invalid type that cannot be converted to [`Decimal`].
654    /// - The denominator is zero
655    ///
656    /// # Example
657    ///
658    /// ```rust
659    /// use squiid_engine::bucket::Bucket;
660    /// use squiid_engine::engine::Engine;
661    ///
662    /// let mut engine = Engine::new();
663    /// engine.stack.push(Bucket::from(10));
664    /// engine.stack.push(Bucket::from(5));
665    ///
666    /// assert!(engine.divide().is_ok());
667    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(2));
668    /// ```
669    pub fn divide(&mut self) -> Result<EngineSignal, String> {
670        // Get operands
671        let operands = self.get_operands_as_dec(2)?;
672
673        if operands[1] == dec!(0.0) {
674            return Err("cannot divide by 0".to_string());
675        }
676
677        // check for pi/x in order to replace with constants
678        let result = if operands[0] == Decimal::PI {
679            if operands[1] == dec!(2.0) {
680                // pi/2
681                Bucket::from_constant(ConstantTypes::HalfPi)
682            } else if operands[1] == dec!(4.0) {
683                // pi/4
684                Bucket::from_constant(ConstantTypes::QuarterPi)
685            } else if operands[1] == dec!(3.0) {
686                // pi/3
687                Bucket::from_constant(ConstantTypes::ThirdPi)
688            } else if operands[1] == dec!(6.0) {
689                // pi/6
690                Bucket::from_constant(ConstantTypes::SixthPi)
691            } else if operands[1] == dec!(8.0) {
692                // pi/8
693                Bucket::from_constant(ConstantTypes::EighthPi)
694            } else {
695                // denominator is not 2 or 4, eval normally
696                Bucket::from(operands[0] / operands[1])
697            }
698        } else {
699            // numerator is not pi, eval normally
700            Bucket::from(operands[0] / operands[1])
701        };
702
703        // Put result on stack
704        let _ = self.add_item_to_stack(result);
705        Ok(EngineSignal::StackUpdated)
706    }
707
708    /// Performs exponentiation on the top two operands of the stack.
709    ///
710    /// # Behavior
711    ///
712    /// - Retrieves the top two values from the stack as [`Decimal`] numbers.
713    /// - Treats the item on the `top-1` of the stack as the base and the
714    ///   item on the top of the stack as the exponent.
715    /// - If the exponent is an integer, uses `checked_powd` from `rust_decimal`.
716    /// - If the exponent is a decimal, converts both values to `f64` and uses `powf`.
717    /// - Pushes the result back onto the stack.
718    /// - Returns [`EngineSignal::StackUpdated`] to indicate a successful operation.
719    ///
720    /// # Returns
721    ///
722    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
723    /// - `Err(String)` if there are not enough operands on the stack or if the operands are invalid.
724    ///
725    /// # Errors
726    ///
727    /// This function will return an error if:
728    /// - The stack contains fewer than two operands.
729    /// - Either operand is of an invalid type that cannot be converted to [`Decimal`].
730    /// - Converting a value to `f64` fails.
731    /// - Overflow occurs during exponentiation.
732    ///
733    /// # Example
734    ///
735    /// ```rust
736    /// use squiid_engine::bucket::Bucket;
737    /// use squiid_engine::engine::Engine;
738    ///
739    /// let mut engine = Engine::new();
740    /// engine.stack.push(Bucket::from(2));
741    /// engine.stack.push(Bucket::from(3));
742    ///
743    /// assert!(engine.power().is_ok());
744    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(8));  // 2^3 = 8
745    /// ```
746    pub fn power(&mut self) -> Result<EngineSignal, String> {
747        // Get operands
748        let operands = self.get_operands_as_dec(2)?;
749
750        let base = operands[0];
751        let exponent = operands[1];
752
753        // TODO: consider adding the option to use both rust_decimal and rug
754        // detect if exponent is decimal, if so, don't use decimal library as that estimates
755        let result = if exponent.fract() == dec!(0.0) {
756            // is not a decimal
757            match base.checked_powd(exponent) {
758                Some(value) => value
759                    .to_f64()
760                    .ok_or_else(|| format!("unable to convert {} to f64 in power", value))?,
761                None => return Err("overflow when raising to a power".to_string()),
762            }
763        } else {
764            // is a decimal
765            let exponent = exponent
766                .to_f64()
767                .ok_or_else(|| format!("unable to convert {} to an f64 in power", exponent))?;
768            base.to_f64()
769                .ok_or_else(|| format!("unable to convert {} to an f64 in power", base))?
770                .powf(exponent)
771        };
772
773        // Put result on stack
774        let _ = self.add_item_to_stack(result.into());
775        Ok(EngineSignal::StackUpdated)
776    }
777
778    /// Calculates the square root of the top operand on the stack.
779    ///
780    /// # Behavior
781    ///
782    /// - Retrieves the top value from the stack as a [`Decimal`] number.
783    /// - Computes the square root of this value.
784    /// - Pushes the result back onto the stack.
785    /// - Returns [`EngineSignal::StackUpdated`] to indicate a successful operation.
786    ///
787    /// # Returns
788    ///
789    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
790    /// - `Err(String)` if there are not enough operands on the stack or if the operands are invalid.
791    ///
792    /// # Errors
793    ///
794    /// This function will return an error if:
795    /// - The stack is empty
796    /// - The operand is of an invalid type that cannot be converted to [`Decimal`].
797    ///
798    /// # Example
799    ///
800    /// ```rust
801    /// use squiid_engine::bucket::Bucket;
802    /// use squiid_engine::engine::Engine;
803    ///
804    /// let mut engine = Engine::new();
805    /// engine.stack.push(Bucket::from(16));
806    ///
807    /// assert!(engine.sqrt().is_ok());
808    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(4));
809    /// ```
810    pub fn sqrt(&mut self) -> Result<EngineSignal, String> {
811        // Get operands
812        let operands = self.get_operands_as_dec(1)?;
813
814        // Put result on stack
815        let Some(result) = operands[0].sqrt() else {
816            return Err("Error calculating sqrt".to_string());
817        };
818        let _ = self.add_item_to_stack(result.into());
819        Ok(EngineSignal::StackUpdated)
820    }
821
822    /// Performs modulo on the top two operands of the stack.
823    ///
824    /// # Behavior
825    ///
826    /// - Retrieves the top two values from the stack as [`Decimal`] numbers.
827    /// - Computes the euclidean modulo of these values.
828    /// - Pushes the result back onto the stack.
829    /// - Returns [`EngineSignal::StackUpdated`] to indicate a successful operation.
830    ///
831    /// # Returns
832    ///
833    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
834    /// - `Err(String)` if there are not enough operands on the stack or if the operands are invalid.
835    ///
836    /// # Errors
837    ///
838    /// This function will return an error if:
839    /// - The stack contains fewer than two operands.
840    /// - The operands are of an invalid type that cannot be converted to [`Decimal`].
841    /// - The denominator is zero
842    ///
843    /// # Example
844    ///
845    /// ```rust
846    /// use squiid_engine::bucket::Bucket;
847    /// use squiid_engine::engine::Engine;
848    ///
849    /// let mut engine = Engine::new();
850    /// engine.stack.push(Bucket::from(11));
851    /// engine.stack.push(Bucket::from(2));
852    ///
853    /// assert!(engine.modulo().is_ok());
854    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(1));
855    /// ```
856    pub fn modulo(&mut self) -> Result<EngineSignal, String> {
857        // Get operands
858        let operands = self.get_operands_as_f(2)?;
859
860        if operands[1] == 0.0 {
861            return Err("cannot divide by zero".to_owned());
862        }
863
864        // Put result on stack
865        // rem_euclid() only yields positive results so we need to write it ourselves
866        let r = operands[0] % operands[1];
867        let result = if (r < 0.0 && operands[1] > 0.0) || (r > 0.0 && operands[1] < 0.0) {
868            r + operands[1]
869        } else {
870            r
871        };
872        let _ = self.add_item_to_stack(result.into());
873        Ok(EngineSignal::StackUpdated)
874    }
875
876    /// Computes the sine of the top operand on the stack.
877    ///
878    /// # Behavior
879    ///
880    /// - Retrieves the top value from the stack.
881    /// - Computes the sine of the value.
882    /// - Pushes the result back onto the stack.
883    /// - Returns [`EngineSignal::StackUpdated`] upon success.
884    ///
885    /// # Returns
886    ///
887    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
888    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
889    ///
890    /// # Errors
891    ///
892    /// This function will return an error if:
893    /// - The stack contains no operands.
894    /// - The operand does not support the `sin` operation.
895    ///
896    /// # Example
897    ///
898    /// ```rust
899    /// use squiid_engine::bucket::Bucket;
900    /// use squiid_engine::engine::Engine;
901    ///
902    /// let mut engine = Engine::new();
903    /// engine.stack.push(Bucket::from(0.0));
904    ///
905    /// assert!(engine.sin().is_ok());
906    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(0.0)); // sin(0) = 0
907    /// ```
908    pub fn sin(&mut self) -> Result<EngineSignal, String> {
909        // Get operands
910        let operands = self.get_operands_raw(1)?;
911
912        // Put result on stack
913        let Some(result) = operands[0].sin() else {
914            return Err("could not sin operand".to_string());
915        };
916        let _ = self.add_item_to_stack(result);
917        Ok(EngineSignal::StackUpdated)
918    }
919
920    /// Computes the cosine of the top operand on the stack.
921    ///
922    /// # Behavior
923    ///
924    /// - Retrieves the top value from the stack.
925    /// - Computes the cosine of the value.
926    /// - Pushes the result back onto the stack.
927    /// - Returns [`EngineSignal::StackUpdated`] upon success.
928    ///
929    /// # Returns
930    ///
931    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
932    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
933    ///
934    /// # Errors
935    ///
936    /// This function will return an error if:
937    /// - The stack contains no operands.
938    /// - The operand does not support the `cos` operation.
939    ///
940    /// # Example
941    ///
942    /// ```rust
943    /// use squiid_engine::bucket::Bucket;
944    /// use squiid_engine::engine::Engine;
945    ///
946    /// let mut engine = Engine::new();
947    /// engine.stack.push(Bucket::from(0.0));
948    ///
949    /// assert!(engine.cos().is_ok());
950    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(1.0)); // cos(0) = 1
951    /// ```
952    pub fn cos(&mut self) -> Result<EngineSignal, String> {
953        // Get operands
954        let operands = self.get_operands_raw(1)?;
955
956        // Put result on stack
957        let Some(result) = operands[0].cos() else {
958            return Err("could not cos operand".to_string());
959        };
960        let _ = self.add_item_to_stack(result);
961        Ok(EngineSignal::StackUpdated)
962    }
963
964    /// Computes the tangent of the top operand on the stack.
965    ///
966    /// # Behavior
967    ///
968    /// - Retrieves the top value from the stack.
969    /// - Computes the tangent of the value.
970    /// - Pushes the result back onto the stack.
971    /// - Returns [`EngineSignal::StackUpdated`] upon success.
972    ///
973    /// # Returns
974    ///
975    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
976    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
977    ///
978    /// # Errors
979    ///
980    /// This function will return an error if:
981    /// - The stack contains no operands.
982    /// - The operand does not support the `tan` operation.
983    ///
984    /// # Example
985    ///
986    /// ```rust
987    /// use squiid_engine::bucket::Bucket;
988    /// use squiid_engine::engine::Engine;
989    ///
990    /// let mut engine = Engine::new();
991    /// engine.stack.push(Bucket::from(0.0));
992    ///
993    /// assert!(engine.tan().is_ok());
994    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(0.0)); // tan(0) = 0
995    /// ```
996    pub fn tan(&mut self) -> Result<EngineSignal, String> {
997        // Get operands
998        let operands = self.get_operands_raw(1)?;
999        // Put result on stack
1000        let Some(result) = operands[0].tan() else {
1001            return Err("could not tan operand".to_string());
1002        };
1003        let _ = self.add_item_to_stack(result);
1004        Ok(EngineSignal::StackUpdated)
1005    }
1006
1007    /// Computes the secant of the top operand on the stack.
1008    ///
1009    /// # Behavior
1010    ///
1011    /// - Retrieves the top value from the stack.
1012    /// - Computes the secant of the value.
1013    /// - Pushes the result back onto the stack.
1014    /// - Returns [`EngineSignal::StackUpdated`] upon success.
1015    ///
1016    /// # Returns
1017    ///
1018    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1019    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
1020    ///
1021    /// # Errors
1022    ///
1023    /// This function will return an error if:
1024    /// - The stack contains no operands.
1025    /// - The operand does not support the `sec` operation.
1026    ///
1027    /// # Example
1028    ///
1029    /// ```rust
1030    /// use squiid_engine::bucket::Bucket;
1031    /// use squiid_engine::engine::Engine;
1032    ///
1033    /// let mut engine = Engine::new();
1034    /// engine.stack.push(Bucket::from(0.0));
1035    ///
1036    /// assert!(engine.sec().is_ok());
1037    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(1.0)); // sec(0) = 1
1038    /// ```
1039    pub fn sec(&mut self) -> Result<EngineSignal, String> {
1040        // Get operands
1041        let operands = self.get_operands_raw(1)?;
1042
1043        // Put result on stack
1044        let Some(result) = operands[0].sec() else {
1045            return Err("could not sec operand".to_string());
1046        };
1047        let _ = self.add_item_to_stack(result);
1048        Ok(EngineSignal::StackUpdated)
1049    }
1050
1051    /// Computes the cosecant of the top operand on the stack.
1052    ///
1053    /// # Behavior
1054    ///
1055    /// - Retrieves the top value from the stack.
1056    /// - Computes the cosecant of the value.
1057    /// - Pushes the result back onto the stack.
1058    /// - Returns [`EngineSignal::StackUpdated`] upon success.
1059    ///
1060    /// # Returns
1061    ///
1062    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1063    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
1064    ///
1065    /// # Errors
1066    ///
1067    /// This function will return an error if:
1068    /// - The stack contains no operands.
1069    /// - The operand does not support the `csc` operation.
1070    ///
1071    /// # Example
1072    ///
1073    /// ```rust
1074    /// use squiid_engine::bucket::Bucket;
1075    /// use squiid_engine::engine::Engine;
1076    ///
1077    /// let mut engine = Engine::new();
1078    /// engine.stack.push(Bucket::from(0.0));
1079    ///
1080    /// assert!(engine.csc().is_ok());
1081    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::new_undefined()); // csc(0) = undefined
1082    /// ```
1083    pub fn csc(&mut self) -> Result<EngineSignal, String> {
1084        // Get operands
1085        let operands = self.get_operands_raw(1)?;
1086
1087        // Put result on stack
1088        let Some(result) = operands[0].csc() else {
1089            return Err("could not csc operand".to_string());
1090        };
1091        let _ = self.add_item_to_stack(result);
1092        Ok(EngineSignal::StackUpdated)
1093    }
1094
1095    /// Computes the cotangent of the top operand on the stack.
1096    ///
1097    /// # Behavior
1098    ///
1099    /// - Retrieves the top value from the stack.
1100    /// - Computes the cotangent of the value.
1101    /// - Pushes the result back onto the stack.
1102    /// - Returns [`EngineSignal::StackUpdated`] upon success.
1103    ///
1104    /// # Returns
1105    ///
1106    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1107    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
1108    ///
1109    /// # Errors
1110    ///
1111    /// This function will return an error if:
1112    /// - The stack contains no operands.
1113    /// - The operand does not support the `cot` operation.
1114    ///
1115    /// # Example
1116    ///
1117    /// ```rust
1118    /// use squiid_engine::bucket::Bucket;
1119    /// use squiid_engine::engine::Engine;
1120    ///
1121    /// let mut engine = Engine::new();
1122    /// engine.stack.push(Bucket::from(0.0));
1123    ///
1124    /// assert!(engine.csc().is_ok());
1125    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::new_undefined()); // cot(0) = undefined
1126    /// ```
1127    pub fn cot(&mut self) -> Result<EngineSignal, String> {
1128        // Get operands
1129        let operands = self.get_operands_raw(1)?;
1130
1131        // Put result on stack
1132        let Some(result) = operands[0].cot() else {
1133            return Err("could not sine operand".to_string());
1134        };
1135        let _ = self.add_item_to_stack(result);
1136        Ok(EngineSignal::StackUpdated)
1137    }
1138
1139    /// Computes the arcsine (inverse sine) of the top operand on the stack.
1140    ///
1141    /// # Behavior
1142    ///
1143    /// - Retrieves the top value from the stack.
1144    /// - Computes the arcsine of the value (in radians).
1145    /// - Pushes the result back onto the stack.
1146    /// - Returns `EngineSignal::StackUpdated` upon success.
1147    ///
1148    /// # Returns
1149    ///
1150    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1151    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
1152    ///
1153    /// # Errors
1154    ///
1155    /// This function will return an error if:
1156    /// - The stack contains no operands.
1157    /// - The operand is not a valid input for the arcsine function (e.g., out of the range [-1, 1]).
1158    ///
1159    /// # Example
1160    ///
1161    /// ```rust
1162    /// use squiid_engine::bucket::{Bucket, ConstantTypes};
1163    /// use squiid_engine::engine::Engine;
1164    ///
1165    /// let mut engine = Engine::new();
1166    /// engine.stack.push(Bucket::from(1.0));
1167    ///
1168    /// assert!(engine.asin().is_ok());
1169    /// // asin(1) = HalfPi
1170    /// assert_eq!(engine.stack.last().unwrap().value, Bucket::from_constant(ConstantTypes::HalfPi).value);
1171    /// ```
1172    pub fn asin(&mut self) -> Result<EngineSignal, String> {
1173        // Get operands
1174        let operands = self.get_operands_as_f(1)?;
1175
1176        // Put result on stack
1177        let _ = self.add_item_to_stack(operands[0].asin().into());
1178        Ok(EngineSignal::StackUpdated)
1179    }
1180
1181    /// Computes the arccosine (inverse cosine) of the top operand on the stack.
1182    ///
1183    /// # Behavior
1184    ///
1185    /// - Retrieves the top value from the stack.
1186    /// - Computes the arccosine of the value (in radians).
1187    /// - Pushes the result back onto the stack.
1188    /// - Returns `EngineSignal::StackUpdated` upon success.
1189    ///
1190    /// # Returns
1191    ///
1192    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1193    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
1194    ///
1195    /// # Errors
1196    ///
1197    /// This function will return an error if:
1198    /// - The stack contains no operands.
1199    /// - The operand is not a valid input for the arcsine function (e.g., out of the range [-1, 1]).
1200    ///
1201    /// # Example
1202    ///
1203    /// ```rust
1204    /// use squiid_engine::bucket::{Bucket, ConstantTypes};
1205    /// use squiid_engine::engine::Engine;
1206    ///
1207    /// let mut engine = Engine::new();
1208    /// engine.stack.push(Bucket::from(-1.0));
1209    ///
1210    /// assert!(engine.acos().is_ok());
1211    /// // acos(-1) = Pi
1212    /// assert_eq!(engine.stack.last().unwrap().value, Bucket::from_constant(ConstantTypes::Pi).value);
1213    /// ```
1214    pub fn acos(&mut self) -> Result<EngineSignal, String> {
1215        // Get operands
1216        let operands = self.get_operands_as_f(1)?;
1217
1218        // Put result on stack
1219        let _ = self.add_item_to_stack(operands[0].acos().into());
1220        Ok(EngineSignal::StackUpdated)
1221    }
1222
1223    /// Computes the arctangent (inverse tangent) of the top operand on the stack.
1224    ///
1225    /// # Behavior
1226    ///
1227    /// - Retrieves the top value from the stack.
1228    /// - Computes the arctangent of the value (in radians).
1229    /// - Pushes the result back onto the stack.
1230    /// - Returns `EngineSignal::StackUpdated` upon success.
1231    ///
1232    /// # Returns
1233    ///
1234    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1235    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
1236    ///
1237    /// # Errors
1238    ///
1239    /// This function will return an error if:
1240    /// - The stack contains no operands.
1241    /// - The operand is not a valid input for the arcsine function (e.g., out of the range [-sqrt(3), sqrt(3)]).
1242    ///
1243    /// # Example
1244    ///
1245    /// ```rust
1246    /// use squiid_engine::bucket::{Bucket, ConstantTypes};
1247    /// use squiid_engine::engine::Engine;
1248    ///
1249    /// let mut engine = Engine::new();
1250    /// engine.stack.push(Bucket::from(1.0));
1251    ///
1252    /// assert!(engine.atan().is_ok());
1253    /// // atan(1) = pi/4
1254    /// assert_eq!(engine.stack.last().unwrap().value, Bucket::from_constant(ConstantTypes::QuarterPi).value);
1255    /// ```
1256    pub fn atan(&mut self) -> Result<EngineSignal, String> {
1257        // Get operands
1258        let operands = self.get_operands_as_f(1)?;
1259
1260        // Put result on stack
1261        let _ = self.add_item_to_stack(operands[0].atan().into());
1262        Ok(EngineSignal::StackUpdated)
1263    }
1264
1265    /// Changes the sign (negates) of the top operand on the stack.
1266    ///
1267    /// # Behavior
1268    ///
1269    /// - Retrieves the top value from the stack.
1270    /// - Negates the value (multiplies by -1).
1271    /// - Pushes the negated result back onto the stack.
1272    /// - Returns `EngineSignal::StackUpdated` upon success.
1273    ///
1274    /// # Returns
1275    ///
1276    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1277    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
1278    ///
1279    /// # Errors
1280    ///
1281    /// This function will return an error if:
1282    /// - The stack contains no operands.
1283    /// - The operand cannot be processed (e.g., invalid type).
1284    ///
1285    /// # Example
1286    ///
1287    /// ```rust
1288    /// use squiid_engine::bucket::Bucket;
1289    /// use squiid_engine::engine::Engine;
1290    ///
1291    /// let mut engine = Engine::new();
1292    /// engine.stack.push(Bucket::from(10.0));
1293    ///
1294    /// assert!(engine.chs().is_ok());
1295    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(-10.0));
1296    /// ```
1297    pub fn chs(&mut self) -> Result<EngineSignal, String> {
1298        // Get operands
1299        let operands = self.get_operands_as_f(1)?;
1300
1301        // Put result on stack
1302        let result = operands[0] * -1.0;
1303        let _ = self.add_item_to_stack(result.into());
1304        Ok(EngineSignal::StackUpdated)
1305    }
1306
1307    /// Computes the base-10 logarithm of the top operand on the stack.
1308    ///
1309    /// # Behavior
1310    ///
1311    /// - Retrieves the top value from the stack.
1312    /// - Computes the base-10 logarithm (log10) of the value.
1313    /// - If the value is 0 or negative, it returns an error.
1314    /// - Pushes the result of the logarithm back onto the stack.
1315    ///
1316    /// # Returns
1317    ///
1318    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1319    /// - `Err(String)` if the operand is less than or equal to 0, or if the stack is empty.
1320    ///
1321    /// # Errors
1322    ///
1323    /// This function will return an error if:
1324    /// - The stack contains no operands.
1325    /// - The operand is less than or equal to zero (logarithm of 0 or negative number is undefined).
1326    ///
1327    /// # Example
1328    ///
1329    /// ```rust
1330    /// use squiid_engine::bucket::Bucket;
1331    /// use squiid_engine::engine::Engine;
1332    ///
1333    /// let mut engine = Engine::new();
1334    /// engine.stack.push(Bucket::from(100.0));
1335    ///
1336    /// assert!(engine.log().is_ok());
1337    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(2.0)); // log10(100) = 2
1338    /// ```
1339    pub fn log(&mut self) -> Result<EngineSignal, String> {
1340        // Get operands
1341        let operands = self.get_operands_as_dec(1)?;
1342
1343        // Put result on stack
1344        let Some(result) = operands[0].checked_log10() else {
1345            return Err("cannot take log10 of 0 or negative numbers".to_string());
1346        };
1347        let _ = self.add_item_to_stack(result.into());
1348        Ok(EngineSignal::StackUpdated)
1349    }
1350
1351    /// Computes the logarithm of a number with a specified base using the change of base formula.
1352    ///
1353    /// The change of base formula is:
1354    ///
1355    /// ```text
1356    /// log_b(a) = (log_d(a)) / (log_d(b))
1357    /// ```
1358    ///
1359    /// where:
1360    /// - `a` is the number whose logarithm is to be calculated.
1361    /// - `b` is the base of the logarithm.
1362    ///
1363    /// # Behavior
1364    ///
1365    /// - Retrieves two operands from the stack: the number `a` and the base `b`.
1366    /// - Uses the change of base formula to compute the logarithm of `a` with base `b`.
1367    /// - If either operand is 0 or negative, or if division by zero occurs, it returns an error.
1368    /// - Pushes the result back onto the stack.
1369    ///
1370    /// # Returns
1371    ///
1372    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1373    /// - `Err(String)` if any of the following errors occur:
1374    ///   - Either operand is 0 or negative.
1375    ///   - Division by zero occurs.
1376    ///
1377    /// # Errors
1378    ///
1379    /// This function will return an error if:
1380    /// - Either operand is less than or equal to 0.
1381    /// - Division by zero occurs during the calculation.
1382    ///
1383    /// # Example
1384    ///
1385    /// ```rust
1386    /// use squiid_engine::bucket::Bucket;
1387    /// use squiid_engine::engine::Engine;
1388    ///
1389    /// let mut engine = Engine::new();
1390    /// engine.stack.push(Bucket::from(100.0));  // 'a' value
1391    /// engine.stack.push(Bucket::from(10.0));   // 'b' value (base)
1392    ///
1393    /// assert!(engine.blog().is_ok());
1394    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(2.0)); // log_10(100) = 2
1395    /// ```
1396    pub fn blog(&mut self) -> Result<EngineSignal, String> {
1397        // Get operands
1398        let operands = self.get_operands_as_dec(2)?;
1399
1400        // change of base formula is defined as follows:
1401        // log_b(a) = (log_d(a))/(log_d(b))
1402
1403        let Some(top_log) = operands[0].checked_log10() else {
1404            return Err("cannot take log of 0 or negative numbers".to_string());
1405        };
1406        let Some(bottom_log) = operands[1].checked_log10() else {
1407            return Err("cannot take log with base of 0 or negative numbers".to_string());
1408        };
1409
1410        let Some(result) = top_log.checked_div(bottom_log) else {
1411            return Err("cannot divide by zero".to_string());
1412        };
1413
1414        // Put result on stack
1415        let _ = self.add_item_to_stack(result.into());
1416        Ok(EngineSignal::StackUpdated)
1417    }
1418
1419    /// Computes the natural logarithm (ln) of a number.
1420    ///
1421    /// The natural logarithm is the logarithm to the base of Euler's number (e).
1422    /// It is defined for positive real numbers.
1423    ///
1424    /// # Behavior
1425    ///
1426    /// - Retrieves one operand from the stack (the number `a`).
1427    /// - Computes the natural logarithm (ln) of the operand.
1428    /// - If the operand is 0 or negative, returns an error.
1429    /// - Pushes the result back onto the stack.
1430    ///
1431    /// # Returns
1432    ///
1433    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1434    /// - `Err(String)` if the operand is 0 or negative.
1435    ///
1436    /// # Errors
1437    ///
1438    /// This function will return an error if:
1439    /// - The operand is less than or equal to 0, as the natural logarithm is only defined for positive real numbers.
1440    ///
1441    /// # Example
1442    ///
1443    /// ```rust
1444    /// use squiid_engine::bucket::{Bucket, ConstantTypes};
1445    /// use squiid_engine::engine::Engine;
1446    ///
1447    /// let mut engine = Engine::new();
1448    /// engine.stack.push(Bucket::from_constant(ConstantTypes::E));  // Euler's number
1449    ///
1450    /// assert!(engine.ln().is_ok());
1451    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(1.0)); // ln(e) = 1
1452    /// ```
1453    pub fn ln(&mut self) -> Result<EngineSignal, String> {
1454        // Get operands
1455        let operands = self.get_operands_as_dec(1)?;
1456
1457        // Put result on stack
1458        let Some(result) = operands[0].checked_ln() else {
1459            return Err("cannot take log10 of 0 or negative numbers".to_string());
1460        };
1461        let _ = self.add_item_to_stack(result.into());
1462        Ok(EngineSignal::StackUpdated)
1463    }
1464
1465    /// Computes the absolute value of the top operand on the stack.
1466    ///
1467    /// # Behavior
1468    ///
1469    /// - Retrieves the top value from the stack.
1470    /// - Takes the absolute value
1471    /// - Pushes the negated result back onto the stack.
1472    /// - Returns `EngineSignal::StackUpdated` upon success.
1473    ///
1474    /// # Returns
1475    ///
1476    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1477    /// - `Err(String)` if the stack is empty or the operand cannot be processed.
1478    ///
1479    /// # Errors
1480    ///
1481    /// This function will return an error if:
1482    /// - The stack contains no operands.
1483    /// - The operand cannot be processed (e.g., invalid type).
1484    ///
1485    /// # Example
1486    ///
1487    /// ```rust
1488    /// use squiid_engine::bucket::Bucket;
1489    /// use squiid_engine::engine::Engine;
1490    ///
1491    /// let mut engine = Engine::new();
1492    /// engine.stack.push(Bucket::from(-10.0));
1493    ///
1494    /// assert!(engine.abs().is_ok());
1495    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(10.0));
1496    /// ```
1497    pub fn abs(&mut self) -> Result<EngineSignal, String> {
1498        // Get operands
1499        let operands = self.get_operands_as_f(1)?;
1500
1501        // Put result on stack
1502        let _ = self.add_item_to_stack(operands[0].abs().into());
1503        Ok(EngineSignal::StackUpdated)
1504    }
1505
1506    /// Compares two operands for equality.
1507    ///
1508    /// This function compares the top two operands on the stack to check if they are equal.
1509    ///
1510    /// # Behavior
1511    ///
1512    /// - Retrieves two operands from the stack (operands `a` and `b`).
1513    /// - Compares the operands for equality (`a == b`).
1514    /// - If the operands are equal, pushes `1` (as a `u32`) onto the stack; otherwise, pushes `0`.
1515    ///
1516    /// # Returns
1517    ///
1518    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1519    /// - The result is `1` if the operands are equal, otherwise `0`.
1520    ///
1521    /// # Errors
1522    ///
1523    /// This function does not explicitly handle errors related to operand types.
1524    /// It assumes that the operands are of compatible types (in this case, `f64`).
1525    /// If incompatible types are used, an error will be returned by the `get_operands_as_f` function.
1526    ///
1527    /// # Example
1528    ///
1529    /// ```rust
1530    /// use squiid_engine::bucket::Bucket;
1531    /// use squiid_engine::engine::Engine;
1532    ///
1533    /// let mut engine = Engine::new();
1534    /// engine.stack.push(Bucket::from(5.0));
1535    /// engine.stack.push(Bucket::from(5.0));
1536    ///
1537    /// assert!(engine.equal().is_ok());
1538    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(1)); // 5.0 == 5.0, so result is 1
1539    /// ```
1540    pub fn equal(&mut self) -> Result<EngineSignal, String> {
1541        // Get operands
1542        // TODO: maybe make this work with strings
1543        let operands = self.get_operands_as_f(2)?;
1544
1545        // Put result on stack
1546        let result = (operands[0] == operands[1]) as u32;
1547        let _ = self.add_item_to_stack(result.into());
1548        Ok(EngineSignal::StackUpdated)
1549    }
1550
1551    /// Compares two operands to check if the first is greater than the second.
1552    ///
1553    /// This function compares the top two operands on the stack to check if the first operand
1554    /// is greater than the second operand.
1555    ///
1556    /// # Behavior
1557    ///
1558    /// - Retrieves two operands from the stack (operands `a` and `b`).
1559    /// - Compares the operands to check if `a > b`.
1560    /// - If the first operand is greater, pushes `1` (as a `u32`) onto the stack; otherwise, pushes `0`.
1561    ///
1562    /// # Returns
1563    ///
1564    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1565    /// - The result is `1` if the first operand is greater than the second, otherwise `0`.
1566    ///
1567    /// # Errors
1568    ///
1569    /// This function does not explicitly handle errors related to operand types.
1570    /// It assumes that the operands are of compatible types (in this case, `f64`).
1571    /// If incompatible types are used, an error will be returned by the `get_operands_as_f` function.
1572    ///
1573    /// # Example
1574    ///
1575    /// ```rust
1576    /// use squiid_engine::bucket::Bucket;
1577    /// use squiid_engine::engine::Engine;
1578    ///
1579    /// let mut engine = Engine::new();
1580    /// engine.stack.push(Bucket::from(10.0));
1581    /// engine.stack.push(Bucket::from(5.0));
1582    ///
1583    /// assert!(engine.gt().is_ok());
1584    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(1)); // 10.0 > 5.0, so result is 1
1585    /// ```
1586    pub fn gt(&mut self) -> Result<EngineSignal, String> {
1587        // Get operands
1588        let operands = self.get_operands_as_f(2)?;
1589
1590        // Put result on stack
1591        let result = (operands[0] > operands[1]) as u32;
1592        let _ = self.add_item_to_stack(result.into());
1593        Ok(EngineSignal::StackUpdated)
1594    }
1595
1596    /// Compares two operands to check if the first is less than the second.
1597    ///
1598    /// This function compares the top two operands on the stack to check if the first operand
1599    /// is less than the second operand.
1600    ///
1601    /// # Behavior
1602    ///
1603    /// - Retrieves two operands from the stack (operands `a` and `b`).
1604    /// - Compares the operands to check if `a < b`.
1605    /// - If the first operand is less, pushes `1` (as a `u32`) onto the stack; otherwise, pushes `0`.
1606    ///
1607    /// # Returns
1608    ///
1609    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1610    /// - The result is `1` if the first operand is greater than the second, otherwise `0`.
1611    ///
1612    /// # Errors
1613    ///
1614    /// This function does not explicitly handle errors related to operand types.
1615    /// It assumes that the operands are of compatible types (in this case, `f64`).
1616    /// If incompatible types are used, an error will be returned by the `get_operands_as_f` function.
1617    ///
1618    /// # Example
1619    ///
1620    /// ```rust
1621    /// use squiid_engine::bucket::Bucket;
1622    /// use squiid_engine::engine::Engine;
1623    ///
1624    /// let mut engine = Engine::new();
1625    /// engine.stack.push(Bucket::from(5.0));
1626    /// engine.stack.push(Bucket::from(10.0));
1627    ///
1628    /// assert!(engine.lt().is_ok());
1629    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(1)); // 5.0 < 10.0, so result is 1
1630    /// ```
1631    pub fn lt(&mut self) -> Result<EngineSignal, String> {
1632        // Get operands
1633        let operands = self.get_operands_as_f(2)?;
1634
1635        // Put result on stack
1636        let result = (operands[0] < operands[1]) as u32;
1637        let _ = self.add_item_to_stack(result.into());
1638        Ok(EngineSignal::StackUpdated)
1639    }
1640
1641    /// Compares two operands to check if the first is greater than or equal to the second.
1642    ///
1643    /// This function compares the top two operands on the stack to check if the first operand
1644    /// is greater than or equal to the second operand.
1645    ///
1646    /// # Behavior
1647    ///
1648    /// - Retrieves two operands from the stack (operands `a` and `b`).
1649    /// - Compares the operands to check if `a >= b`.
1650    /// - If the first operand is greater or equal, pushes `1` (as a `u32`) onto the stack; otherwise, pushes `0`.
1651    ///
1652    /// # Returns
1653    ///
1654    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1655    /// - The result is `1` if the first operand is greater than or equal to the second, otherwise `0`.
1656    ///
1657    /// # Errors
1658    ///
1659    /// This function does not explicitly handle errors related to operand types.
1660    /// It assumes that the operands are of compatible types (in this case, `f64`).
1661    /// If incompatible types are used, an error will be returned by the `get_operands_as_f` function.
1662    ///
1663    /// # Example
1664    ///
1665    /// ```rust
1666    /// use squiid_engine::bucket::Bucket;
1667    /// use squiid_engine::engine::Engine;
1668    ///
1669    /// let mut engine = Engine::new();
1670    /// engine.stack.push(Bucket::from(10.0));
1671    /// engine.stack.push(Bucket::from(9.0));
1672    ///
1673    /// assert!(engine.geq().is_ok());
1674    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(1)); // 10.0 >= 9.0, so result is 1
1675    /// ```
1676    pub fn geq(&mut self) -> Result<EngineSignal, String> {
1677        // Get operands
1678        let operands = self.get_operands_as_f(2)?;
1679
1680        // Put result on stack
1681        let result = (operands[0] >= operands[1]) as u32;
1682        let _ = self.add_item_to_stack(result.into());
1683        Ok(EngineSignal::StackUpdated)
1684    }
1685
1686    /// Compares two operands to check if the first is less than or equal to the second.
1687    ///
1688    /// This function compares the top two operands on the stack to check if the first operand
1689    /// is less than or equal to the second operand.
1690    ///
1691    /// # Behavior
1692    ///
1693    /// - Retrieves two operands from the stack (operands `a` and `b`).
1694    /// - Compares the operands to check if `a <= b`.
1695    /// - If the first operand is less or equal, pushes `1` (as a `u32`) onto the stack; otherwise, pushes `0`.
1696    ///
1697    /// # Returns
1698    ///
1699    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1700    /// - The result is `1` if the first operand is less than or equal to the second, otherwise `0`.
1701    ///
1702    /// # Errors
1703    ///
1704    /// This function does not explicitly handle errors related to operand types.
1705    /// It assumes that the operands are of compatible types (in this case, `f64`).
1706    /// If incompatible types are used, an error will be returned by the `get_operands_as_f` function.
1707    ///
1708    /// # Example
1709    ///
1710    /// ```rust
1711    /// use squiid_engine::bucket::Bucket;
1712    /// use squiid_engine::engine::Engine;
1713    ///
1714    /// let mut engine = Engine::new();
1715    /// engine.stack.push(Bucket::from(9.0));
1716    /// engine.stack.push(Bucket::from(10.0));
1717    ///
1718    /// assert!(engine.leq().is_ok());
1719    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(1)); // 9.0 <= 10.0, so result is 1
1720    /// ```
1721    pub fn leq(&mut self) -> Result<EngineSignal, String> {
1722        // Get operands
1723        let operands = self.get_operands_as_f(2)?;
1724
1725        // Put result on stack
1726        let result = (operands[0] <= operands[1]) as u32;
1727        let _ = self.add_item_to_stack(result.into());
1728        Ok(EngineSignal::StackUpdated)
1729    }
1730
1731    /// Rounds a number to the nearest integer.
1732    ///
1733    /// This function takes the top operand from the stack and rounds it to the nearest integer.
1734    /// The result is then pushed back onto the stack.
1735    ///
1736    /// # Behavior
1737    ///
1738    /// - Retrieves one operand from the stack.
1739    /// - Rounds the operand to the nearest integer using Rust's [`f64::round`] method.
1740    /// - Pushes the rounded result back onto the stack.
1741    ///
1742    /// # Returns
1743    ///
1744    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful.
1745    /// - The result is the rounded value of the operand.
1746    ///
1747    /// # Errors
1748    ///
1749    /// This function does not explicitly handle errors related to operand types. It assumes that
1750    /// the operand is of type `f64`. If the operand is of an incompatible type, an error will be
1751    /// returned by the `get_operands_as_f` function.
1752    ///
1753    /// # Example
1754    ///
1755    /// ```rust
1756    /// use squiid_engine::bucket::Bucket;
1757    /// use squiid_engine::engine::Engine;
1758    ///
1759    /// let mut engine = Engine::new();
1760    /// engine.stack.push(Bucket::from(3.7));
1761    ///
1762    /// assert!(engine.round().is_ok());
1763    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(4.0)); // Rounded from 3.7 to 4.0
1764    /// ```
1765    pub fn round(&mut self) -> Result<EngineSignal, String> {
1766        // Get operand
1767        let operands = self.get_operands_as_f(1)?;
1768
1769        // Put result on stack
1770        let _ = self.add_item_to_stack(operands[0].round().into());
1771        Ok(EngineSignal::StackUpdated)
1772    }
1773
1774    /// Inverts the top operand on the stack.
1775    ///
1776    /// This function takes the top operand from the stack and computes its multiplicative inverse (1 / operand).
1777    /// If the operand is zero, an error is returned since division by zero is undefined.
1778    /// The result is then pushed back onto the stack.
1779    ///
1780    /// # Behavior
1781    ///
1782    /// - Retrieves one operand from the stack.
1783    /// - Checks if the operand is zero. If it is, returns an error (division by zero).
1784    /// - Computes the inverse of the operand (1 / operand) if it is non-zero.
1785    /// - Pushes the result back onto the stack.
1786    ///
1787    /// # Returns
1788    ///
1789    /// - `Ok(EngineSignal::StackUpdated)` if the operation is successful and the result is pushed onto the stack.
1790    /// - `Err(String)` if the operand is zero, indicating division by zero.
1791    ///
1792    /// # Errors
1793    ///
1794    /// - Returns an error if the operand is zero, as division by zero is not allowed.
1795    ///
1796    /// # Example
1797    ///
1798    /// ```rust
1799    /// use squiid_engine::bucket::Bucket;
1800    /// use squiid_engine::engine::Engine;
1801    ///
1802    /// let mut engine = Engine::new();
1803    /// engine.stack.push(Bucket::from(5.0));
1804    ///
1805    /// assert!(engine.invert().is_ok());
1806    /// assert_eq!(engine.stack.last().unwrap(), &Bucket::from(0.2)); // Inverted from 5.0 to 0.2
1807    ///
1808    /// engine.stack.push(Bucket::from(0.0));
1809    /// assert!(engine.invert().is_err()); // Division by zero error
1810    /// ```
1811    pub fn invert(&mut self) -> Result<EngineSignal, String> {
1812        // Get operand
1813        let operands = self.get_operands_as_f(1)?;
1814
1815        if operands[0] == 0.0 {
1816            return Err("cannot divide by zero".to_string());
1817        }
1818
1819        // Put result on stack
1820        let _ = self.add_item_to_stack((1_f64 / operands[0]).into());
1821        Ok(EngineSignal::StackUpdated)
1822    }
1823
1824    /// Removes the top item from the stack.
1825    ///
1826    /// This function pops the last item from the stack. If the stack is not empty, the item is removed.
1827    /// If the stack is empty, the function does nothing but still returns [`EngineSignal::StackUpdated`].
1828    ///
1829    /// # Behavior
1830    ///
1831    /// - Removes the top item from the stack.
1832    ///
1833    /// # Returns
1834    ///
1835    /// - `Ok(EngineSignal::StackUpdated)` when the operation is successfully completed and the stack is updated.
1836    ///
1837    /// # Errors
1838    ///
1839    /// This function doesn't return an error variant, and is only written this way to be
1840    /// compatible with the other engine functions.
1841    ///
1842    /// # Notes
1843    ///
1844    /// - If the stack is empty, the function will simply return without making changes, but still indicate that the stack has been updated.
1845    ///
1846    /// # Example
1847    ///
1848    /// ```rust
1849    /// use squiid_engine::bucket::Bucket;
1850    /// use squiid_engine::engine::Engine;
1851    ///
1852    /// let mut engine = Engine::new();
1853    /// engine.stack.push(Bucket::from(10.0));
1854    ///
1855    /// assert!(engine.drop().is_ok());
1856    /// assert!(engine.stack.is_empty()); // Stack is empty after dropping the item
1857    /// ```
1858    pub fn drop(&mut self) -> Result<EngineSignal, String> {
1859        // Remove last item from stack
1860        self.stack.pop();
1861        Ok(EngineSignal::StackUpdated)
1862    }
1863
1864    /// Swaps the top two items on the stack.
1865    ///
1866    /// This function takes the last two values from the stack, removes them, and then places them back in reverse order.
1867    ///
1868    /// # Behavior
1869    ///
1870    /// - Retrieves the top two items from the stack.
1871    /// - Swaps their positions on the stack.
1872    ///
1873    /// # Returns
1874    ///
1875    /// - `Ok(EngineSignal::StackUpdated)` when the operation is successfully completed and the stack is updated.
1876    ///
1877    /// # Errors
1878    ///
1879    /// This function doesn't return an error variant, and is only written this way to be
1880    /// compatible with the other engine functions.
1881    ///
1882    /// # Notes
1883    ///
1884    /// - If the stack has fewer than two items, the function will return an error indicating that not enough operands are present.
1885    ///
1886    /// # Example
1887    ///
1888    /// ```rust
1889    /// use squiid_engine::bucket::Bucket;
1890    /// use squiid_engine::engine::Engine;
1891    ///
1892    /// let mut engine = Engine::new();
1893    /// engine.stack.push(Bucket::from(5.0));
1894    /// engine.stack.push(Bucket::from(10.0));
1895    ///
1896    /// assert!(engine.swap().is_ok());
1897    /// assert_eq!(engine.stack[0], Bucket::from(10.0)); // Top item after swap
1898    /// assert_eq!(engine.stack[1], Bucket::from(5.0));  // Second item after swap
1899    /// ```
1900    pub fn swap(&mut self) -> Result<EngineSignal, String> {
1901        // Get last two values from stack
1902        let operands = self.get_operands_raw(2)?;
1903
1904        // Insert in reverse order
1905        let _ = self.add_item_to_stack(operands[1].clone());
1906        let _ = self.add_item_to_stack(operands[0].clone());
1907        Ok(EngineSignal::StackUpdated)
1908    }
1909
1910    /// Duplicates the last item on the stack.
1911    ///
1912    /// This function takes the top item from the stack and pushes it onto the stack twice, duplicating the top value.
1913    ///
1914    /// # Returns
1915    ///
1916    /// - `Ok(EngineSignal::StackUpdated)` when the operation is successfully completed and the stack is updated.
1917    ///
1918    /// # Errors
1919    ///
1920    /// This function doesn't return an error variant, and is only written this way to be
1921    /// compatible with the other engine functions.
1922    ///
1923    /// # Notes
1924    ///
1925    /// - If the stack is empty, the function will return an error indicating that no operand is available for duplication.
1926    ///
1927    /// # Example
1928    ///
1929    /// ```rust
1930    /// use squiid_engine::bucket::Bucket;
1931    /// use squiid_engine::engine::Engine;
1932    ///
1933    /// let mut engine = Engine::new();
1934    /// engine.stack.push(Bucket::from(5.0));
1935    ///
1936    /// assert!(engine.dup().is_ok());
1937    /// assert_eq!(engine.stack[0], Bucket::from(5.0)); // Top item after duplication
1938    /// assert_eq!(engine.stack[1], Bucket::from(5.0)); // Duplicate item
1939    /// ```
1940    pub fn dup(&mut self) -> Result<EngineSignal, String> {
1941        // Get the last value from the stack
1942        let operands = self.get_operands_raw(1)?;
1943
1944        // Insert twice
1945        let _ = self.add_item_to_stack(operands[0].clone());
1946        let _ = self.add_item_to_stack(operands[0].clone());
1947        Ok(EngineSignal::StackUpdated)
1948    }
1949
1950    /// Rolls the stack down, rotating the elements to the right.
1951    ///
1952    /// This function moves the topmost item to the bottom of the stack by rotating the stack right by one position.
1953    ///
1954    /// # Returns
1955    ///
1956    /// - `Ok(EngineSignal::StackUpdated)` when the operation is successfully completed and the stack is updated.
1957    ///
1958    /// # Errors
1959    ///
1960    /// This function will return an error if:
1961    /// - The stack is empty.
1962    ///
1963    /// # Notes
1964    ///
1965    /// - If the stack is empty, the function will return an error indicating that the operation cannot be performed.
1966    ///
1967    /// # Example
1968    ///
1969    /// ```rust
1970    /// use squiid_engine::bucket::Bucket;
1971    /// use squiid_engine::engine::Engine;
1972    ///
1973    /// let mut engine = Engine::new();
1974    /// engine.stack.push(Bucket::from(1.0));
1975    /// engine.stack.push(Bucket::from(2.0));
1976    /// engine.stack.push(Bucket::from(3.0));
1977    ///
1978    /// assert!(engine.roll_down().is_ok());
1979    /// assert_eq!(engine.stack[0], Bucket::from(3.0)); // Top item after roll
1980    /// assert_eq!(engine.stack[2], Bucket::from(2.0)); // Last item moved to the bottom
1981    /// ```
1982    pub fn roll_down(&mut self) -> Result<EngineSignal, String> {
1983        if self.stack.is_empty() {
1984            Err(String::from("Cannot roll empty stack"))
1985        } else {
1986            // Rotate stack right
1987            self.stack.rotate_right(1);
1988            Ok(EngineSignal::StackUpdated)
1989        }
1990    }
1991
1992    /// Rolls the stack up, rotating the elements to the left.
1993    ///
1994    /// This function moves the bottommost item to the top of the stack by rotating the stack left by one position.
1995    ///
1996    /// # Returns
1997    ///
1998    /// - `Ok(EngineSignal::StackUpdated)` when the operation is successfully completed and the stack is updated.
1999    ///
2000    /// # Errors
2001    ///
2002    /// This function will return an error if:
2003    /// - The stack is empty.
2004    ///
2005    /// # Notes
2006    ///
2007    /// - If the stack is empty, the function will return an error indicating that the operation cannot be performed.
2008    ///
2009    /// # Example
2010    ///
2011    /// ```rust
2012    /// use squiid_engine::bucket::Bucket;
2013    /// use squiid_engine::engine::Engine;
2014    ///
2015    /// let mut engine = Engine::new();
2016    /// engine.stack.push(Bucket::from(1.0));
2017    /// engine.stack.push(Bucket::from(2.0));
2018    /// engine.stack.push(Bucket::from(3.0));
2019    ///
2020    /// assert!(engine.roll_up().is_ok());
2021    /// assert_eq!(engine.stack[2], Bucket::from(1.0)); // Bottom item moved to the top
2022    /// assert_eq!(engine.stack[0], Bucket::from(2.0));
2023    /// ```
2024    pub fn roll_up(&mut self) -> Result<EngineSignal, String> {
2025        if self.stack.is_empty() {
2026            Err(String::from("Cannot roll empty stack"))
2027        } else {
2028            // Rotate stack left
2029            self.stack.rotate_left(1);
2030            Ok(EngineSignal::StackUpdated)
2031        }
2032    }
2033
2034    /// Stores a value in a variable.
2035    ///
2036    /// This function stores the topmost value from the stack into a variable, validating that the variable name follows a valid identifier pattern.
2037    ///
2038    /// # Returns
2039    ///
2040    /// - `Ok(EngineSignal::StackUpdated)` when the operation is successfully completed and the stack is updated.
2041    ///
2042    /// # Errors
2043    ///
2044    /// This function will return an error if:
2045    /// - The variable name is not a valid ID
2046    ///
2047    /// # Notes
2048    ///
2049    /// - If the second operand is not a valid variable name, an error is returned.
2050    ///
2051    /// # Example
2052    ///
2053    /// ```rust
2054    /// use squiid_engine::bucket::Bucket;
2055    /// use squiid_engine::engine::Engine;
2056    ///
2057    /// let mut engine = Engine::new();
2058    /// engine.stack.push(Bucket::from(5.0));
2059    /// engine.stack.push(Bucket::from("var1"));
2060    ///
2061    /// assert!(engine.store().is_ok());
2062    /// // Variable "var1" should now store the value 5.0
2063    /// assert!(engine.variables.get("var1").is_some());
2064    /// ```
2065    pub fn store(&mut self) -> Result<EngineSignal, String> {
2066        // Get 2 operands from stack
2067        let operands = self.get_operands_raw(2)?;
2068
2069        // Only store if matches the identifier pattern
2070        let varname = operands[1].to_string();
2071        if ID_REGEX.is_match(&varname) {
2072            // Add variable to hashmap
2073            self.variables.insert(varname, operands[0].clone());
2074        } else {
2075            // Error if attempted to store in name which is not a valid ID
2076            return Err(format!("Cannot store in non-variable object `{}`", varname));
2077        }
2078        Ok(EngineSignal::StackUpdated)
2079    }
2080
2081    /// Deletes a variable from memory.
2082    ///
2083    /// This function removes a variable from the internal variable store if it exists. It only works if the variable is a valid identifier.
2084    ///
2085    /// # Returns
2086    ///
2087    /// - `Ok(EngineSignal::StackUpdated)` when the operation is successfully completed and the stack is updated.
2088    ///
2089    /// # Errors
2090    ///
2091    /// This function will return an error if:
2092    /// - The variable name is not a valid ID
2093    /// - The variable is not set
2094    ///
2095    /// # Notes
2096    ///
2097    /// - If the variable does not exist or is not a valid identifier, an error is returned.
2098    ///
2099    /// # Example
2100    ///
2101    /// ```rust
2102    /// use squiid_engine::bucket::Bucket;
2103    /// use squiid_engine::engine::Engine;
2104    ///
2105    /// let mut engine = Engine::new();
2106    /// engine.stack.push(Bucket::from(5.0));
2107    /// engine.stack.push(Bucket::from("var1"));
2108    /// engine.store(); // Store a value in a variable
2109    /// assert!(engine.variables.get("var1").is_some());
2110    ///
2111    /// engine.stack.push(Bucket::from("var1"));
2112    /// assert!(engine.purge().is_ok());
2113    /// // Variable should now be removed
2114    /// assert!(engine.variables.get("var1").is_none());
2115    /// ```
2116    pub fn purge(&mut self) -> Result<EngineSignal, String> {
2117        // Get operand from stack
2118        let operands = self.get_operands_raw(1)?;
2119
2120        let varname = operands[0].to_string();
2121        if ID_REGEX.is_match(&varname) {
2122            if self.variables.contains_key(&varname) {
2123                // Remove variable from hashmap
2124                self.variables.remove(&varname);
2125            } else {
2126                return Err(format!("Variable `{}` does not exist", varname));
2127            }
2128        } else {
2129            // Error if attempted to purge name which is not a valid ID
2130            return Err(format!("Cannot delete non-variable object `{}`", varname));
2131        }
2132        Ok(EngineSignal::StackUpdated)
2133    }
2134
2135    /// Stores a value in a variable, with inverted argument order. Useful for how variable
2136    /// assignments are parsed in algebraic mode.
2137    ///
2138    /// This function swaps the top two values on the stack and then calls the `store` function to store the value in the variable.
2139    ///
2140    /// # Returns
2141    ///
2142    /// - `Ok(EngineSignal::StackUpdated)` when the operation is successfully completed and the stack is updated.
2143    ///
2144    /// # Errors
2145    ///
2146    /// This function will return an error if:
2147    /// - The variable name is not a valid ID
2148    ///
2149    /// # Notes
2150    ///
2151    /// - This function uses `swap()` and will return an error if swapping fails.
2152    ///
2153    /// # Example
2154    ///
2155    /// ```rust
2156    /// use squiid_engine::bucket::Bucket;
2157    /// use squiid_engine::engine::Engine;
2158    ///
2159    /// let mut engine = Engine::new();
2160    /// engine.stack.push(Bucket::from("var1"));
2161    /// engine.stack.push(Bucket::from(5.0));
2162    ///
2163    /// assert!(engine.invstore().is_ok());
2164    /// // Variable "var1" should now store the value 5.0
2165    /// assert!(engine.variables.get("var1").is_some());
2166    /// ```
2167    pub fn invstore(&mut self) -> Result<EngineSignal, String> {
2168        self.swap()?;
2169        self.store()
2170    }
2171
2172    /// Clears the entire stack.
2173    ///
2174    /// This function empties the stack, removing all items stored in it.
2175    ///
2176    /// # Returns
2177    ///
2178    /// - `Ok(EngineSignal::StackUpdated)` when the stack has been successfully cleared.
2179    ///
2180    /// # Errors
2181    ///
2182    /// This function doesn't return an error variant, and is only written this way to be
2183    /// compatible with the other engine functions.
2184    ///
2185    /// # Notes
2186    ///
2187    /// - This function does not return an error; the stack is simply cleared.
2188    ///
2189    /// # Example
2190    ///
2191    /// ```rust
2192    /// use squiid_engine::bucket::Bucket;
2193    /// use squiid_engine::engine::Engine;
2194    ///
2195    /// let mut engine = Engine::new();
2196    /// engine.stack.push(Bucket::from(5.0));
2197    /// engine.stack.push(Bucket::from(10.0));
2198    ///
2199    /// assert!(engine.clear().is_ok());
2200    /// assert!(engine.stack.is_empty()); // Stack is now empty
2201    /// ```
2202    pub fn clear(&mut self) -> Result<EngineSignal, String> {
2203        self.stack = Vec::new();
2204        Ok(EngineSignal::StackUpdated)
2205    }
2206
2207    /// Update stack and variables from the undo history
2208    fn update_engine_from_history(&mut self) {
2209        self.stack =
2210            self.undo_history[self.undo_history.len() - self.undo_state_pointer as usize].clone();
2211        self.variables = self.undo_variable_history
2212            [self.undo_variable_history.len() - self.undo_state_pointer as usize]
2213            .clone();
2214    }
2215
2216    /// Undoes the last operation by reverting the stack and variables to their previous state.
2217    ///
2218    /// This function reverts the stack and variables to the state they were in at the time of the last operation.
2219    /// If no operations have been performed or the undo history has been exhausted, an error is returned.
2220    ///
2221    /// # Returns
2222    ///
2223    /// - `Ok(EngineSignal::StackUpdated)` when the undo is successfully performed and the stack and variables are updated.
2224    /// - `Err(String)` if there is no operation to undo.
2225    ///
2226    /// # Errors
2227    ///
2228    /// This function will return an error if:
2229    /// - You try to undo further than the history allows
2230    ///
2231    /// # Notes
2232    ///
2233    /// - The undo history is stored, and each undo operation increments a pointer to track the state.
2234    ///
2235    /// # Example
2236    ///
2237    /// ```rust
2238    /// use squiid_engine::bucket::Bucket;
2239    /// use squiid_engine::engine::Engine;
2240    /// use squiid_engine;
2241    ///
2242    /// let mut engine = Engine::new();
2243    /// // after each command, we must push a copy of the stack to the engine history
2244    /// let _ = squiid_engine::handle_data(&mut engine, "1");
2245    /// let _ = squiid_engine::handle_data(&mut engine, "2");
2246    /// let _ = squiid_engine::handle_data(&mut engine, "test");
2247    ///
2248    /// // test undo of adding something to the stack
2249    /// let _ = engine.undo();
2250    /// assert_eq!(engine.stack, vec![Bucket::from(1), Bucket::from(2),]);
2251    /// ```
2252    pub fn undo(&mut self) -> Result<EngineSignal, String> {
2253        if self.undo_state_pointer < self.undo_history.len() as u8 {
2254            if self.undo_state_pointer == 0 {
2255                // add current stack and variables to history and increment pointer by 1
2256                self.undo_history.push_back(self.stack.clone());
2257                self.undo_variable_history.push_back(self.variables.clone());
2258                self.undo_state_pointer += 1;
2259            }
2260            self.undo_state_pointer += 1;
2261            self.update_engine_from_history();
2262            Ok(EngineSignal::StackUpdated)
2263        } else {
2264            Err(String::from("Cannot undo further"))
2265        }
2266    }
2267
2268    /// Redoes the last undone operation by restoring the stack and variables.
2269    ///
2270    /// This function restores the stack and variables to the state they were in at the time of the last undone operation.
2271    /// If no operations have been undone or the redo history is exhausted, an error is returned.
2272    ///
2273    /// # Returns
2274    ///
2275    /// - `Ok(EngineSignal::StackUpdated)` when the redo is successfully performed and the stack and variables are updated.
2276    /// - `Err(String)` if there are no operations to redo.
2277    ///
2278    /// # Errors
2279    ///
2280    /// This function will return an error if:
2281    /// - You try to redo further than the number of undone operations
2282    ///
2283    /// # Notes
2284    ///
2285    /// - The redo history is tracked using the undo state pointer to determine the next state to restore.
2286    ///
2287    /// # Example
2288    ///
2289    /// ```rust
2290    /// use squiid_engine::bucket::Bucket;
2291    /// use squiid_engine::engine::Engine;
2292    /// use squiid_engine;
2293    ///
2294    /// let mut engine = Engine::new();
2295    /// // after each command, we must push a copy of the stack to the engine history
2296    /// let _ = squiid_engine::handle_data(&mut engine, "1");
2297    /// let _ = squiid_engine::handle_data(&mut engine, "2");
2298    /// let _ = squiid_engine::handle_data(&mut engine, "test");
2299    ///
2300    /// // test undo of adding something to the stack
2301    /// let _ = engine.undo();
2302    /// assert_eq!(engine.stack, vec![Bucket::from(1), Bucket::from(2),]);
2303    ///
2304    /// // test redo
2305    /// let _ = engine.redo();
2306    /// assert_eq!(engine.stack, vec![Bucket::from(1), Bucket::from(2), Bucket::from("test")]);
2307    /// ```
2308    pub fn redo(&mut self) -> Result<EngineSignal, String> {
2309        if self.undo_state_pointer > 1 {
2310            self.undo_state_pointer -= 1;
2311            self.update_engine_from_history();
2312            Ok(EngineSignal::StackUpdated)
2313        } else {
2314            Err(String::from("Cannot redo further"))
2315        }
2316    }
2317
2318    /// Terminates the engine and signals the system to quit.
2319    ///
2320    /// This function sends a quit signal to the engine, indicating that it should stop processing and exit.
2321    ///
2322    /// # Returns
2323    ///
2324    /// - `Ok(EngineSignal::Quit)` when the quit signal is sent.
2325    ///
2326    /// # Errors
2327    ///
2328    /// This function doesn't return an error variant, and is only written this way to be
2329    /// compatible with the other engine functions.
2330    ///
2331    /// # Notes
2332    ///
2333    /// - This function does not affect the state of the stack or variables but instead signals the engine to stop execution.
2334    ///
2335    /// # Example
2336    ///
2337    /// ```rust
2338    /// use squiid_engine::bucket::Bucket;
2339    /// use squiid_engine::engine::Engine;
2340    ///
2341    /// let mut engine = Engine::new();
2342    ///
2343    /// assert!(engine.quit().is_ok()); // The engine is now quitting
2344    /// ```
2345    pub fn quit(&mut self) -> Result<EngineSignal, String> {
2346        Ok(EngineSignal::Quit)
2347    }
2348}
2349
2350impl Default for Engine {
2351    fn default() -> Self {
2352        Self::new()
2353    }
2354}