diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index 2008fa4..35b8398 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -487,6 +487,13 @@ pub fn vector_int_instructions() -> Vec { vector_int_sort_reverse, vector_int_insert, vector_int_insert_vector, + vector_int_mean, + vector_int_maximum, + vector_int_minimum, + vector_int_sum, + vector_int_mode, + vector_int_two_norm, + vector_int_cumulative_sum, // common.rs vector_int_pop, ] @@ -533,6 +540,13 @@ pub fn vector_float_instructions() -> Vec { vector_float_sort_reverse, vector_float_insert, vector_float_insert_vector, + vector_float_mean, + vector_float_maximum, + vector_float_minimum, + vector_float_sum, + vector_float_mode, + vector_float_two_norm, + vector_float_cumulative_sum, // common.rs vector_float_pop, ] diff --git a/src/instructions/utils.rs b/src/instructions/utils.rs index 572c11b..649bc72 100644 --- a/src/instructions/utils.rs +++ b/src/instructions/utils.rs @@ -1,6 +1,6 @@ use rust_decimal::Decimal; use rust_decimal::prelude::*; -use std::ops::{Add, Div}; +use std::ops::{Add, Div, Mul}; /// This trait houses various methods for making instructions /// more generic instead of declaring a separate function for each @@ -8,7 +8,9 @@ use std::ops::{Add, Div}; /// /// Trig functions named safe rather than checked to not overlap /// with Decimal library's checked function names. -pub trait NumericTrait: Sized + Add + Div + Ord { +pub trait NumericTrait: + Sized + Add + Mul + Div + Ord +{ fn checked_div(self, v: Self) -> Option; fn checked_mod(self, v: Self) -> Option; fn increment(self) -> Self; @@ -74,7 +76,7 @@ impl NumericTrait for Decimal { dec!(0.0) } fn from_usize(num: usize) -> Self { - Decimal::new(num as i64, 0) + rust_decimal::prelude::FromPrimitive::from_usize(num).unwrap() } } diff --git a/src/instructions/vector.rs b/src/instructions/vector.rs index 606b989..655e3b2 100644 --- a/src/instructions/vector.rs +++ b/src/instructions/vector.rs @@ -1,7 +1,7 @@ use crate::instructions::utils::NumericTrait; use crate::push::state::{Gene, PushState}; use rust_decimal::Decimal; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::hash::Hash; /// Generates an index between 0 and length. Takes abs(num) and then mods it by length. @@ -1573,6 +1573,9 @@ make_instruction_aux!(string, string, _insert_vector, Vec, 2, int, 1, i128 /// Takes the mean of a vector pub fn _mean(vals: Vec>) -> Option { + if vals[0].is_empty() { + return Some(T::zero()); + } let mut fin_num = T::zero(); for num in vals[0].clone().into_iter() { fin_num = fin_num + num; @@ -1582,6 +1585,115 @@ pub fn _mean(vals: Vec>) -> Option { make_instruction_clone!(vector_int, int, _mean, Vec, 1); make_instruction_clone!(vector_float, float, _mean, Vec, 1); +/// Takes the max of a vector +pub fn _maximum(vals: Vec>) -> Option { + if vals[0].is_empty() { + return Some(T::zero()); + } + Some(vals[0].iter().max()?.clone()) +} +make_instruction_clone!(vector_int, int, _maximum, Vec, 1); +make_instruction_clone!(vector_float, float, _maximum, Vec, 1); + +/// Takes the min of a vector +pub fn _minimum(vals: Vec>) -> Option { + if vals[0].is_empty() { + return Some(T::zero()); + } + Some(vals[0].iter().min()?.clone()) +} +make_instruction_clone!(vector_int, int, _minimum, Vec, 1); +make_instruction_clone!(vector_float, float, _minimum, Vec, 1); + +/// Takes the sum of a vector +pub fn _sum(vals: Vec>) -> Option { + if vals[0].is_empty() { + return Some(T::zero()); + } + let mut fin_num = T::zero(); + for num in vals[0].clone().into_iter() { + fin_num = fin_num + num; + } + Some(fin_num) +} +make_instruction_clone!(vector_int, int, _sum, Vec, 1); +make_instruction_clone!(vector_float, float, _sum, Vec, 1); + +/// Takes the mode of a vector +pub fn _mode(vals: Vec>) -> Option { + if vals[0].is_empty() { + return Some(T::zero()); + } + let mut counts = HashMap::new(); + vals[0] + .iter() + .max_by_key(|&x| { + let count = counts.entry(x).or_insert(0); + *count += 1; + *count + }) + .copied() +} +make_instruction_clone!(vector_int, int, _mode, Vec, 1); +make_instruction_clone!(vector_float, float, _mode, Vec, 1); + +/// Adds the squares of all values in a vector and then takes the square root +pub fn _two_norm(vals: Vec>) -> Option { + if vals[0].is_empty() { + return Some(T::zero()); + } + let mut fin_num = T::zero(); + for num in vals[0].clone().into_iter() { + fin_num = fin_num + (num.clone() * num); + } + fin_num.safe_sqrt() +} +make_instruction_clone!(vector_int, int, _two_norm, Vec, 1); +make_instruction_clone!(vector_float, float, _two_norm, Vec, 1); + +/// Takes the cumulative sum of a vector +pub fn _cumulative_sum(vals: Vec>) -> Option> { + if vals[0].is_empty() { + return Some(vec![]); + } + let mut fin_num = T::zero(); + let mut ret_vec = vec![]; + for num in vals[0].clone().into_iter() { + fin_num = fin_num + num; + ret_vec.push(fin_num.clone()); + } + Some(ret_vec) +} +make_instruction_clone!(vector_int, vector_int, _cumulative_sum, Vec, 1); +make_instruction_clone!(vector_float, vector_float, _cumulative_sum, Vec, 1); + +/* /// Takes the cumulative mean of a vector +pub fn _cumulative_mean(vals: Vec>) -> Option> { + if vals[0].is_empty() { + return Some(vec![]); + } + // This is not an efficient implementation + let mut ret_vec = vec![]; + let mut cum_vec = vec![]; + for (idx, val) in vals[0].iter().enumerate() { + cum_vec.push(val.clone()); + let mut temp_sum = T::zero(); + for num in &cum_vec { + temp_sum = temp_sum + num.clone(); + } + temp_sum + } + Some(ret_vec) +} +make_instruction_clone!(vector_int, vector_int, _cumulative_mean, Vec, 1); +make_instruction_clone!( + vector_float, + vector_float, + _cumulative_mean, + Vec, + 1 +);*/ + #[cfg(test)] mod tests { use super::*; @@ -2330,4 +2442,64 @@ mod tests { vector_float_mean(&mut test_state); assert_eq!(vec![dec!(5.0)], test_state.float); } + + #[test] + fn minimum_maximum_test() { + let mut test_state = EMPTY_STATE; + + test_state.vector_int = vec![vec![0, 1, 2, 3, 4, 5, 2]]; + vector_int_maximum(&mut test_state); + assert_eq!(vec![5], test_state.int); + test_state.int.clear(); + + test_state.vector_int = vec![vec![0, 1, 2, 3, 4, 5, 2]]; + vector_int_minimum(&mut test_state); + assert_eq!(vec![0], test_state.int); + } + + #[test] + fn sum_test() { + let mut test_state = EMPTY_STATE; + + test_state.vector_int = vec![vec![0, 1, 2, 3, 4, 5, 2]]; + vector_int_sum(&mut test_state); + assert_eq!(vec![17], test_state.int); + } + + #[test] + fn mode_test() { + let mut test_state = EMPTY_STATE; + + test_state.vector_int = vec![vec![0, 1, 2, 3, 4, 5, 2]]; + vector_int_mode(&mut test_state); + assert_eq!(vec![2], test_state.int); + test_state.int.clear(); + + // returns the last mode to be calculated + test_state.vector_int = vec![vec![0, 1, 2, 3, 4, 5, 2, 4, 3]]; + vector_int_mode(&mut test_state); + assert_eq!(vec![3], test_state.int); + } + + #[test] + fn sqrt_test() { + let mut test_state = EMPTY_STATE; + + test_state.vector_int = vec![vec![5, 5, 5, 5]]; + vector_int_two_norm(&mut test_state); + assert_eq!(vec![10], test_state.int); + + test_state.vector_float = vec![vec![dec!(5.0), dec!(5.0), dec!(5.0), dec!(5.0)]]; + vector_float_two_norm(&mut test_state); + assert_eq!(vec!(dec!(10.0)), test_state.float); + } + + #[test] + fn cumulative_sum_test() { + let mut test_state = EMPTY_STATE; + + test_state.vector_int = vec![vec![0, 1, 2, 3, 4, 5, 2]]; + vector_int_cumulative_sum(&mut test_state); + assert_eq!(vec![vec![0, 1, 3, 6, 10, 15, 17]], test_state.vector_int); + } }