From 13997cde733ed64d9d27656b7ede96fc9f8c0149 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Thu, 3 Apr 2025 22:43:28 -0500 Subject: [PATCH] no rounding error floats and some numeric instructions done/tested --- Cargo.toml | 2 + src/instructions/mod.rs | 38 ++++-- src/instructions/numeric.rs | 233 +++++++++++++++++++++++++++++++++--- src/instructions/utils.rs | 25 ++++ src/main.rs | 16 ++- src/push/state.rs | 14 ++- 6 files changed, 295 insertions(+), 33 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a1886f..1ac735b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,5 @@ edition = "2024" [dependencies] rand = "0.9.0" paste = "1.0.15" +rust_decimal = "1.37" +rust_decimal_macros = "1.37" diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index be92ad7..fac2c62 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -1,24 +1,40 @@ #[macro_use] pub mod macros { - // A macro that makes a push instruction given: the name of the stack to use, - // an internal function to call, and the type of a function. + /// A macro that makes a push instruction given: the name of the input stack to use, + /// the name of the output stack, an internal function to call, the type of a function, + /// and the arity of the internal function call. + /// + /// The `in_stack` argument refers to which push stack should this operate on. + /// The `out_stack` argument refers to which push stack should the result be pushed to. + /// The `fn_name` argement refers to the name of the function that is to operate + /// on the values popped from `in_stack`. + /// The `fn_type` argument refers to the type of `in_stack`. For example, the + /// int stack is type: *Vec*. `fn_type` is *i128* in this case. + /// The `fn_arity` argument refers to how many popped stack items are needed to + /// execute the instruction. If the amount of items in the stack is less than + /// this value, the instruction does nothing. + /// + /// What causes an instruction to NoOp: + /// 1) There aren't enough values on a stack to execute an instruction. + /// 2) The internal operation the instruction executes is unable to be ran without + /// erroring such as division by 0. #[macro_export] macro_rules! make_instruction { - ($stack_name:ident, $fn_name:ident, $fn_type:ty) => { + ($in_stack:ident, $out_stack:ident, $fn_name:ident, $fn_type:ty, $fn_arity:stmt) => { paste::item! { - fn [< $stack_name $fn_name >] (state: &mut PushState, num_inputs: usize) { - if state.$stack_name.len() < num_inputs { + fn [< $in_stack $fn_name >] (state: &mut PushState) { + if state.$in_stack.len() < $fn_arity { return; } - let mut inputs: Vec<$fn_type> = Vec::with_capacity(num_inputs); - for n in 0..num_inputs { - inputs.push(state.$stack_name[n]); + let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity); + for n in 1..=$fn_arity { + inputs.push(state.$in_stack[state.$in_stack.len() - n]); } if let Some(result) = $fn_name(inputs) { - for _ in 0..num_inputs { - state.$stack_name.pop(); + for _ in 0..$fn_arity { + state.$in_stack.pop(); } - state.$stack_name.push(result); + state.$out_stack.push(result); } } } diff --git a/src/instructions/numeric.rs b/src/instructions/numeric.rs index b1cad92..924e121 100644 --- a/src/instructions/numeric.rs +++ b/src/instructions/numeric.rs @@ -3,32 +3,82 @@ //! This file contains numeric instructions for int and float. use crate::push::state::{EMPTY_STATE, PushState}; -use std::ops::{Add, Sub}; +use rust_decimal::Decimal; +use std::cmp::{max, min}; +use std::ops::{Add, Div, Mul, Sub}; + +use super::utils::CheckedDiv; /// Adds two addable values together. fn _add(vals: Vec) -> Option where - T: Add, - T: Copy, + T: Add + Copy, { Some(vals[1] + vals[0]) } +make_instruction!(int, int, _add, i128, 2); +make_instruction!(float, float, _add, Decimal, 2); /// Subtracts two subtractable values from each other. fn _sub(vals: Vec) -> Option where - T: Sub, - T: Copy, + T: Sub + Copy, { Some(vals[1] - vals[0]) } +make_instruction!(int, int, _sub, i128, 2); +make_instruction!(float, float, _sub, Decimal, 2); -/// Declares int_add -make_instruction!(int, _add, i64); +/// Multiplies two multipliable values together. +fn _mult(vals: Vec) -> Option +where + T: Mul + Copy, +{ + Some(vals[1] * vals[0]) +} +make_instruction!(int, int, _mult, i128, 2); +make_instruction!(float, float, _mult, Decimal, 2); + +/// Divides two values from each other. +fn _div(vals: Vec) -> Option +where + T: Div + Copy + CheckedDiv, +{ + vals[1].checked_div(vals[0]) +} +make_instruction!(int, int, _div, i128, 2); +make_instruction!(float, float, _div, Decimal, 2); + +/// Takes the remainder of two values +fn _rem(vals: Vec) -> Option +where + T: Div + Copy + CheckedDiv, +{ + vals[1].checked_mod(vals[0]) +} +make_instruction!(int, int, _rem, i128, 2); +make_instruction!(float, float, _rem, Decimal, 2); + +/// Takes the max of two values +fn _max(vals: Vec) -> Option +where + T: Ord + Copy, +{ + Some(max(vals[1], vals[0])) +} + +/// Takes the min of two values +fn _min(vals: Vec) -> Option +where + T: Ord + Copy, +{ + Some(min(vals[1], vals[0])) +} #[cfg(test)] mod tests { use super::*; + use rust_decimal_macros::dec; /// Tests the _add function. #[test] @@ -36,8 +86,8 @@ mod tests { let vals: Vec = vec![1, 2]; assert_eq!(Some(3), _add(vals)); - let vals: Vec = vec![1.1, 2.2]; - assert_eq!(Some(3.3), _add(vals)); + let vals: Vec = vec![dec!(1.1), dec!(2.2)]; + assert_eq!(Some(dec!(3.3)), _add(vals)); } /// Tests the _sub function. @@ -46,17 +96,170 @@ mod tests { let vals: Vec = vec![1, 2]; assert_eq!(Some(1), _sub(vals)); - let vals: Vec = vec![1.1, 2.2]; - assert_eq!(Some(1.1), _sub(vals)); + let vals: Vec = vec![dec!(1.1), dec!(2.2)]; + assert_eq!(Some(dec!(1.1)), _sub(vals)); } - // Tests that the various state_add functions work. + /// Tests the _mult function. + #[test] + fn mult_test() { + let vals: Vec = vec![4, 5]; + assert_eq!(Some(20), _mult(vals)); + + let vals: Vec = vec![dec!(1.1), dec!(2.2)]; + assert_eq!(Some(dec!(2.42)), _mult(vals)); + } + + /// Tests the _div function + #[test] + fn div_test() { + let vals: Vec = vec![4, 20]; + assert_eq!(Some(5), _div(vals)); + + let vals: Vec = vec![3, 20]; + assert_eq!(Some(6), _div(vals)); + + let vals: Vec = vec![dec!(1.6), dec!(2.2)]; + assert_eq!(Some(dec!(1.375)), _div(vals)); + + let vals: Vec = vec![0, 1]; + assert_eq!(None, _div(vals)); + } + + /// Tests the _rem function + #[test] + fn rem_test() { + let vals: Vec = vec![3, 20]; + assert_eq!(Some(2), _rem(vals)); + + let vals: Vec = vec![20, 20]; + assert_eq!(Some(0), _rem(vals)); + + let vals: Vec = vec![0, 9]; + assert_eq!(None, _rem(vals)); + } + + /// Tests the _max function + #[test] + fn max_test() { + let vals: Vec = vec![1, 2]; + assert_eq!(Some(2), _max(vals)); + + let vals: Vec = vec![3, 0]; + assert_eq!(Some(3), _max(vals)); + + let vals: Vec = vec![dec!(2.2), dec!(1.1)]; + assert_eq!(Some(dec!(2.2)), _max(vals)); + + let vals: Vec = vec![dec!(3.3), dec!(-1.1)]; + assert_eq!(Some(dec!(3.3)), _max(vals)); + } + + /// Tests the _min function + #[test] + fn min_test() { + let vals: Vec = vec![1, 2]; + assert_eq!(Some(1), _min(vals)); + + let vals: Vec = vec![3, 0]; + assert_eq!(Some(0), _min(vals)); + + let vals: Vec = vec![dec!(2.2), dec!(1.1)]; + assert_eq!(Some(dec!(1.1)), _min(vals)); + + let vals: Vec = vec![dec!(3.3), dec!(-1.1)]; + assert_eq!(Some(dec!(-1.1)), _min(vals)); + } + + /// Tests that the various addition functions. #[test] fn state_add() { let mut test_state = EMPTY_STATE; test_state.int = vec![1, 2]; - test_state.float = vec![1.1, 2.2]; - int_add(&mut test_state, 2); - assert_eq!(test_state.int, vec![3]); + test_state.float = vec![dec!(1.1), dec!(2.2)]; + + int_add(&mut test_state); + assert_eq!(vec![3], test_state.int); + + float_add(&mut test_state); + assert_eq!(vec![dec!(3.3)], test_state.float); + } + + /// Tests the various subtraction functions. + #[test] + fn state_sub() { + let mut test_state = EMPTY_STATE; + test_state.int = vec![1, 2]; + test_state.float = vec![dec!(1.1), dec!(2.2)]; + + int_sub(&mut test_state); + assert_eq!(vec![-1], test_state.int); + + float_sub(&mut test_state); + assert_eq!(vec![dec!(-1.1)], test_state.float); + } + + /// Tests the various multiplication functions. + #[test] + fn state_mult() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![0]; + int_mult(&mut test_state); + assert_eq!(vec![0], test_state.int); + + test_state.int = vec![10, 3, 2]; + test_state.float = vec![dec!(1.1), dec!(2.2)]; + + int_mult(&mut test_state); + assert_eq!(vec![10, 6], test_state.int); + + float_mult(&mut test_state); + assert_eq!(vec![dec!(2.42)], test_state.float); + } + + /// Tests the division functions in the state + #[test] + fn state_div() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![0]; + int_div(&mut test_state); + assert_eq!(vec![0], test_state.int); + + test_state.int = vec![2, 0]; + int_div(&mut test_state); + assert_eq!(vec![2, 0], test_state.int); + + test_state.int = vec![6, 3]; + + int_div(&mut test_state); + assert_eq!(vec![2], test_state.int); + + test_state.float = vec![dec!(2.2), dec!(1.6)]; + float_div(&mut test_state); + assert_eq!(vec![dec!(1.375)], test_state.float); + } + + /// Tests the remainder functions in the state. + #[test] + fn state_rem() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![0]; + int_rem(&mut test_state); + assert_eq!(vec![0], test_state.int); + + test_state.int = vec![2, 0]; + int_rem(&mut test_state); + assert_eq!(vec![2, 0], test_state.int); + + test_state.int = vec![60, 80, 20, 3]; + int_rem(&mut test_state); + assert_eq!(vec![60, 80, 2], test_state.int); + + test_state.float = vec![dec!(2.7), dec!(1.2)]; + float_rem(&mut test_state); + assert_eq!(vec![dec!(0.3)], test_state.float); } } diff --git a/src/instructions/utils.rs b/src/instructions/utils.rs index 8b13789..8596275 100644 --- a/src/instructions/utils.rs +++ b/src/instructions/utils.rs @@ -1 +1,26 @@ +use rust_decimal::Decimal; +use rust_decimal_macros::dec; +use std::ops::Div; +pub trait CheckedDiv: Sized + Div { + fn checked_div(self, v: Self) -> Option; + fn checked_mod(self, v: Self) -> Option; +} + +impl CheckedDiv for Decimal { + fn checked_div(self, v: Self) -> Option { + if v == dec!(0.0) { None } else { Some(self / v) } + } + fn checked_mod(self, v: Self) -> Option { + if v == dec!(0.0) { None } else { Some(self % v) } + } +} + +impl CheckedDiv for i128 { + fn checked_div(self, v: Self) -> Option { + if v == 0 { None } else { Some(self / v) } + } + fn checked_mod(self, v: Self) -> Option { + if v == 0 { None } else { Some(self % v) } + } +} diff --git a/src/main.rs b/src/main.rs index 031ce84..26c103c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,18 @@ +use rust_decimal::prelude::*; +use rust_decimal_macros::dec; + mod instructions; mod push; fn main() { - let arr: Vec = vec![]; - let slice = &arr[..2]; - println!("{:?}", slice); + // let arr: Vec = vec![]; + // let slice = &arr[..2]; + // println!("{:?}", slice); + + // let arr: Vec = vec![dec!(2.2), dec!(1.1)]; + // println!("{arr:?}"); + + // let result = dec!(1.0) / dec!(0.0); + let result = dec!(2.7) % dec!(1.2); + println!("{result}"); } diff --git a/src/push/state.rs b/src/push/state.rs index aea2c74..c4aee36 100644 --- a/src/push/state.rs +++ b/src/push/state.rs @@ -1,12 +1,18 @@ +use rust_decimal::prelude::*; + +/// The declaration of the state that push operates on. +/// +/// I chose to use `rust_decimal` crate here because +/// there are round off errors with the build in `f64`. #[derive(Debug, Clone)] pub struct PushState { - pub int: Vec, - pub float: Vec, + pub int: Vec, + pub float: Vec, pub string: Vec>, pub bool: Vec, pub char: Vec, - pub vector_int: Vec>, - pub vector_float: Vec>, + pub vector_int: Vec>, + pub vector_float: Vec>, pub vector_string: Vec>>, pub vector_bool: Vec>, pub vector_char: Vec,