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}