no rounding error floats and some numeric instructions done/tested
This commit is contained in:
parent
64fad0dff8
commit
13997cde73
@ -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"
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
16
src/main.rs
16
src/main.rs
@ -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}");
|
||||||
}
|
}
|
||||||
|
@ -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>,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user