use core::convert::TryFrom; use num_integer::Roots; use rand::prelude::SliceRandom; use scientific::{Decimals, Error, Precision, Scientific}; #[allow(clippy::type_complexity)] const FUNCTIONS: [( &str, fn(i128, i128) -> Result, fn(&Scientific, &Scientific) -> Result, Option, ); 5] = [ ( "add", |a, b| Ok(a + b), |a, b| Ok(a + b), Some(|a, b| { *a += b; }), ), ( "sub", |a, b| Ok(a - b), |a, b| Ok(a - b), Some(|a, b| { *a -= b; }), ), ( "mul", |a, b| Ok(a * b), |a, b| Ok(a * b), Some(|a, b| { *a *= b; }), ), ( "div", int_div, |a, b| a.div_truncate(b, Precision::INTEGER), None, ), ("rem", int_rem, |a, b| a.div_rem(b).map(|(_, r)| r), None), ]; fn int_div(a: i128, b: i128) -> Result { if b == 0 { Err(Error::DivisionByZero) } else { Ok(a / b) } } fn int_rem(a: i128, b: i128) -> Result { if b == 0 { Err(Error::DivisionByZero) } else { Ok(a % b) } } pub(crate) fn test_integer(numbers: I, sqrt_decimals: isize, randomize: bool) where I: Iterator, { let mut numbers = numbers .map(|n| (n, Scientific::from(n))) .collect::>(); if randomize { // to increase the chance that an error is caught earlier let mut rng = rand::thread_rng(); numbers.shuffle(&mut rng); } for (int_a, sci_a) in numbers.iter() { for (int_b, sci_b) in numbers.iter() { // several functions for (name, int_fn, sci_fn, sci_assign_fn) in FUNCTIONS.iter() { let int_result = int_fn(*int_a, *int_b); let sci_result = sci_fn(sci_a, sci_b); assert_eq!( int_result.map(|i| Scientific::from_string(i.to_string()).unwrap()), sci_result, "function {name}({int_a}, {int_b}) -> {int_result:?} = {sci_result:?}", ); if let Some(sci_assign_fn) = sci_assign_fn { let mut sci_a_clone = sci_a.clone(); sci_assign_fn(&mut sci_a_clone, sci_b); assert_eq!( sci_result, Ok(sci_a_clone.clone()), "function {name}({int_a}, {int_b}) assign mismatch -> {sci_result:?} = {sci_a_clone:?}", ); } } // compare let int_result = int_a.cmp(int_b); let sci_result = sci_a.cmp(sci_b); assert_eq!( int_result, sci_result, "function {}({}, {}) -> {:?} = {:?}", "cmp", int_a, int_b, int_result, sci_result, ); // div_rpsp let big = sci_a.div_truncate(sci_b, Decimals(20)).map(|mut b| { b.round_rpsp_assign(Decimals(1)); b }); let fast = sci_a.div_rpsp(sci_b, Decimals(1)); assert_eq!(big, fast, "div_rpsp vs. div_truncate and round_rpsp"); // div_truncate let big = sci_a.div_rpsp(sci_b, Decimals(1)).map(|mut b| { b.truncate_assign(Decimals(1)); b }); let fast = sci_a.div_truncate(sci_b, Decimals(1)); assert_eq!(big, fast, "div_truncate vs. div_rpsp and truncate"); } // i128 via string let i2s = sci_a; let i2s_str = Scientific::from_string(int_a.to_string()).unwrap(); assert_eq!(i2s, &i2s_str, "conversion from i128"); // i128 let s2i = i128::try_from(i2s); assert_eq!(Ok(*int_a), s2i, "conversion from/to i128"); // i64 let i2s = Scientific::from(*int_a as i64); let s2i = i64::try_from(&i2s); assert_eq!(Ok(*int_a as i64), s2i, "conversion from/to i64"); // i32 let i2s = Scientific::from(*int_a as i32); let s2i = i32::try_from(&i2s); assert_eq!(Ok(*int_a as i32), s2i, "conversion from/to i32"); // sqrt if *int_a >= 0 { // v1 let sci_cubed = sci_a * sci_a; let sci_result = sci_cubed.sqrt_truncate(Decimals(sqrt_decimals)); assert_eq!( Ok(sci_a.clone()), sci_result, "function {}({}) -> {:?} = {:?}", "sqrt_v1", int_a * int_a, int_a, sci_result, ); // v1.2 let sci_cubed = sci_a * sci_a; let sci_result = sci_cubed.sqrt_rpsp(Decimals(1)); assert_eq!( Ok(sci_a.clone()), sci_result, "function {}({}) -> {:?} = {:?}", "sqrt_v1.2", int_a * int_a, int_a, sci_result, ); // v2 let int_result = int_a.sqrt(); let sci_result = sci_a.sqrt_truncate(Decimals(0)); assert_eq!( Ok(Scientific::from(int_result)), sci_result, "function {}({}) -> {:?} = {:?}", "sqrt_v2", int_a, int_result, sci_result, ); // v2.1 let int_result = int_a.sqrt(); let sci_result = sci_a .sqrt_rpsp(Decimals(0)) .map(|s| s.truncate(Decimals(0))); assert_eq!( Ok(Scientific::from(int_result)), sci_result, "function {}({}) -> {:?} = {:?}", "sqrt_v2", int_a, int_result, sci_result, ); } } }