no rounding error floats and some numeric instructions done/tested

This commit is contained in:
Rowan Torbitzky-Lane 2025-04-03 22:43:28 -05:00
parent 64fad0dff8
commit 13997cde73
6 changed files with 295 additions and 33 deletions

View File

@ -6,3 +6,5 @@ edition = "2024"
[dependencies] [dependencies]
rand = "0.9.0" rand = "0.9.0"
paste = "1.0.15" paste = "1.0.15"
rust_decimal = "1.37"
rust_decimal_macros = "1.37"

View File

@ -1,24 +1,40 @@
#[macro_use] #[macro_use]
pub mod macros { pub mod macros {
// A macro that makes a push instruction given: the name of the stack to use, /// A macro that makes a push instruction given: the name of the input stack to use,
// an internal function to call, and the type of a function. /// 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<i128>*. `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_export]
macro_rules! make_instruction { 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! { paste::item! {
fn [< $stack_name $fn_name >] (state: &mut PushState, num_inputs: usize) { fn [< $in_stack $fn_name >] (state: &mut PushState) {
if state.$stack_name.len() < num_inputs { if state.$in_stack.len() < $fn_arity {
return; return;
} }
let mut inputs: Vec<$fn_type> = Vec::with_capacity(num_inputs); let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity);
for n in 0..num_inputs { for n in 1..=$fn_arity {
inputs.push(state.$stack_name[n]); inputs.push(state.$in_stack[state.$in_stack.len() - n]);
} }
if let Some(result) = $fn_name(inputs) { if let Some(result) = $fn_name(inputs) {
for _ in 0..num_inputs { for _ in 0..$fn_arity {
state.$stack_name.pop(); state.$in_stack.pop();
} }
state.$stack_name.push(result); state.$out_stack.push(result);
} }
} }
} }

View File

@ -3,32 +3,82 @@
//! This file contains numeric instructions for int and float. //! This file contains numeric instructions for int and float.
use crate::push::state::{EMPTY_STATE, PushState}; 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. /// Adds two addable values together.
fn _add<T>(vals: Vec<T>) -> Option<T> fn _add<T>(vals: Vec<T>) -> Option<T>
where where
T: Add<Output = T>, T: Add<Output = T> + Copy,
T: Copy,
{ {
Some(vals[1] + vals[0]) 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. /// Subtracts two subtractable values from each other.
fn _sub<T>(vals: Vec<T>) -> Option<T> fn _sub<T>(vals: Vec<T>) -> Option<T>
where where
T: Sub<Output = T>, T: Sub<Output = T> + Copy,
T: Copy,
{ {
Some(vals[1] - vals[0]) Some(vals[1] - vals[0])
} }
make_instruction!(int, int, _sub, i128, 2);
make_instruction!(float, float, _sub, Decimal, 2);
/// Declares int_add /// Multiplies two multipliable values together.
make_instruction!(int, _add, i64); fn _mult<T>(vals: Vec<T>) -> Option<T>
where
T: Mul<Output = T> + 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<T>(vals: Vec<T>) -> Option<T>
where
T: Div<Output = T> + 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<T>(vals: Vec<T>) -> Option<T>
where
T: Div<Output = T> + 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<T>(vals: Vec<T>) -> Option<T>
where
T: Ord + Copy,
{
Some(max(vals[1], vals[0]))
}
/// Takes the min of two values
fn _min<T>(vals: Vec<T>) -> Option<T>
where
T: Ord + Copy,
{
Some(min(vals[1], vals[0]))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use rust_decimal_macros::dec;
/// Tests the _add function. /// Tests the _add function.
#[test] #[test]
@ -36,8 +86,8 @@ mod tests {
let vals: Vec<i64> = vec![1, 2]; let vals: Vec<i64> = vec![1, 2];
assert_eq!(Some(3), _add(vals)); assert_eq!(Some(3), _add(vals));
let vals: Vec<f64> = vec![1.1, 2.2]; let vals: Vec<Decimal> = vec![dec!(1.1), dec!(2.2)];
assert_eq!(Some(3.3), _add(vals)); assert_eq!(Some(dec!(3.3)), _add(vals));
} }
/// Tests the _sub function. /// Tests the _sub function.
@ -46,17 +96,170 @@ mod tests {
let vals: Vec<i64> = vec![1, 2]; let vals: Vec<i64> = vec![1, 2];
assert_eq!(Some(1), _sub(vals)); assert_eq!(Some(1), _sub(vals));
let vals: Vec<f64> = vec![1.1, 2.2]; let vals: Vec<Decimal> = vec![dec!(1.1), dec!(2.2)];
assert_eq!(Some(1.1), _sub(vals)); 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<i128> = vec![4, 5];
assert_eq!(Some(20), _mult(vals));
let vals: Vec<Decimal> = 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<i128> = vec![4, 20];
assert_eq!(Some(5), _div(vals));
let vals: Vec<i128> = vec![3, 20];
assert_eq!(Some(6), _div(vals));
let vals: Vec<Decimal> = vec![dec!(1.6), dec!(2.2)];
assert_eq!(Some(dec!(1.375)), _div(vals));
let vals: Vec<i128> = vec![0, 1];
assert_eq!(None, _div(vals));
}
/// Tests the _rem function
#[test]
fn rem_test() {
let vals: Vec<i128> = vec![3, 20];
assert_eq!(Some(2), _rem(vals));
let vals: Vec<i128> = vec![20, 20];
assert_eq!(Some(0), _rem(vals));
let vals: Vec<i128> = vec![0, 9];
assert_eq!(None, _rem(vals));
}
/// Tests the _max function
#[test]
fn max_test() {
let vals: Vec<i128> = vec![1, 2];
assert_eq!(Some(2), _max(vals));
let vals: Vec<i128> = vec![3, 0];
assert_eq!(Some(3), _max(vals));
let vals: Vec<Decimal> = vec![dec!(2.2), dec!(1.1)];
assert_eq!(Some(dec!(2.2)), _max(vals));
let vals: Vec<Decimal> = 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<i128> = vec![1, 2];
assert_eq!(Some(1), _min(vals));
let vals: Vec<i128> = vec![3, 0];
assert_eq!(Some(0), _min(vals));
let vals: Vec<Decimal> = vec![dec!(2.2), dec!(1.1)];
assert_eq!(Some(dec!(1.1)), _min(vals));
let vals: Vec<Decimal> = vec![dec!(3.3), dec!(-1.1)];
assert_eq!(Some(dec!(-1.1)), _min(vals));
}
/// Tests that the various addition functions.
#[test] #[test]
fn state_add() { fn state_add() {
let mut test_state = EMPTY_STATE; let mut test_state = EMPTY_STATE;
test_state.int = vec![1, 2]; test_state.int = vec![1, 2];
test_state.float = vec![1.1, 2.2]; test_state.float = vec![dec!(1.1), dec!(2.2)];
int_add(&mut test_state, 2);
assert_eq!(test_state.int, vec![3]); 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);
} }
} }

View File

@ -1 +1,26 @@
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use std::ops::Div;
pub trait CheckedDiv: Sized + Div<Output = Self> {
fn checked_div(self, v: Self) -> Option<Self>;
fn checked_mod(self, v: Self) -> Option<Self>;
}
impl CheckedDiv for Decimal {
fn checked_div(self, v: Self) -> Option<Self> {
if v == dec!(0.0) { None } else { Some(self / v) }
}
fn checked_mod(self, v: Self) -> Option<Self> {
if v == dec!(0.0) { None } else { Some(self % v) }
}
}
impl CheckedDiv for i128 {
fn checked_div(self, v: Self) -> Option<Self> {
if v == 0 { None } else { Some(self / v) }
}
fn checked_mod(self, v: Self) -> Option<Self> {
if v == 0 { None } else { Some(self % v) }
}
}

View File

@ -1,8 +1,18 @@
use rust_decimal::prelude::*;
use rust_decimal_macros::dec;
mod instructions; mod instructions;
mod push; mod push;
fn main() { fn main() {
let arr: Vec<i32> = vec![]; // let arr: Vec<i32> = vec![];
let slice = &arr[..2]; // let slice = &arr[..2];
println!("{:?}", slice); // println!("{:?}", slice);
// let arr: Vec<Decimal> = 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}");
} }

View File

@ -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)] #[derive(Debug, Clone)]
pub struct PushState { pub struct PushState {
pub int: Vec<i64>, pub int: Vec<i128>,
pub float: Vec<f64>, pub float: Vec<Decimal>,
pub string: Vec<Vec<u8>>, pub string: Vec<Vec<u8>>,
pub bool: Vec<bool>, pub bool: Vec<bool>,
pub char: Vec<u8>, pub char: Vec<u8>,
pub vector_int: Vec<Vec<i64>>, pub vector_int: Vec<Vec<i128>>,
pub vector_float: Vec<Vec<f64>>, pub vector_float: Vec<Vec<Decimal>>,
pub vector_string: Vec<Vec<Vec<u8>>>, pub vector_string: Vec<Vec<Vec<u8>>>,
pub vector_bool: Vec<Vec<bool>>, pub vector_bool: Vec<Vec<bool>>,
pub vector_char: Vec<u8>, pub vector_char: Vec<u8>,