diff --git a/Cargo.toml b/Cargo.toml index e36f24a..71eda49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,5 +12,7 @@ tokio = { version = "1.12.0", features = ["full"] } # for our async runtime tokio-test = "*" serde = { version = "1.0.103", default-features = false, features = ["derive"] } bech32 = "0.9.0" - +regex = "1" # regular expressions support +lazy_static = "1.4.0" +num-bigint = "0.4.3" [dev-dependencies] diff --git a/src/core/auth/mod.rs b/src/core/auth/mod.rs index 44b0bc2..a9a1ebf 100644 --- a/src/core/auth/mod.rs +++ b/src/core/auth/mod.rs @@ -1 +1,2 @@ -pub mod strings; \ No newline at end of file +pub mod strings; +pub mod numeric; \ No newline at end of file diff --git a/src/core/auth/numeric.rs b/src/core/auth/numeric.rs new file mode 100644 index 0000000..fd759a0 --- /dev/null +++ b/src/core/auth/numeric.rs @@ -0,0 +1,192 @@ +use std::ops::{ Add, Sub, Mul, Div }; +use std::fmt; +use regex::*; +use lazy_static::lazy_static; +use crate::core::errors::SecretError; + +/* +The goal of this is to be able to use the Dec type with normal operators. +TODO: Figure out how to do that, what do the rust operators call +*/ + +const DEC_NUM_DIGITS: i128 = 18; +const DEC_ONE: i128 = 10_i128.pow(18_u32); + +pub enum Number { + Str(&'static str), + Int(i32), + Float(f32), +} + +pub fn convert_to_dec_bignum(arg: Number) -> Result { + // TODO: Better Error messages + match arg { + Number::Str(str) => { + return match from_str(str) { + Some(BigInt) => Ok(BigInt), + None => Err(SecretError::Error("Error: Invalid String Input".to_string())) + } + }, + Number::Float(float) => { + let float_string = float.to_string(); + return match from_str(&float_string) { + Some(BigInt) => Ok(BigInt), + None => Err(SecretError::Error("Error: Invalid Float Input".to_string())) + } + } + Number::Int(int) => { return Ok(int as i128 * DEC_ONE) } + } + + fn from_str(arg: &str) -> Option { + lazy_static! { + static ref RE: Regex = Regex::new(r"^(\-)?(\d+)(\.(\d+))?\Z").unwrap(); + } + let parts = RE.captures(arg)?; + let mut result: i128 = parts.get(2)? + .as_str() + .trim() + .parse::() + .expect("Invalid String: NAN") * DEC_ONE; + if let Some(_) = parts.get(3) { + let fraction: i128 = parts.get(4)? + .as_str() + .trim() // TODO: slice + .parse::() + .expect("Invalid String: NAN"); + result += fraction; + } + if let Some(_) = parts.get(1) { + result *= -1; + } + return Some(result as i128); + } +} + +fn chop_precision_and_round(d: i128) -> i128 { + if d < 0 { + return -1 * chop_precision_and_round(d * -1); + } + + let quo: i128 = d / DEC_ONE; + let rem: i128 = d % DEC_ONE; + + if rem == 0 { + return quo; + } + + if rem < DEC_ONE / 2 { + return quo; + } else if rem > DEC_ONE / 2 { + return quo + 1; + } else { + if quo % 2 == 0 { + return quo; + } + return quo; + } +} + + +#[derive(Default)] +pub struct Dec { + i: i128, +} + +impl Dec { + + pub fn from(arg: Number) -> Result { + Ok(Dec { i: convert_to_dec_bignum(arg)?, }) + } + + pub fn zero() -> Result { Dec::from(Number::Int(0)) } + + pub fn one() -> Result { Dec::from(Number::Int(1)) } + + pub fn whole(&self) -> String { + format!("{}", self.i.abs() / DEC_ONE) + } + + pub fn frac(&self) -> String { + format!("{}", self.i.abs() % DEC_ONE).trim().to_string() + } + + pub fn parity(&self) -> i32 { + if self.i < 0 { -1 } else { 1 } + } + + pub fn add_dec(&self, addend: Dec) -> i128 { + self.i + addend.i + } + + pub fn sub_dec(&self, subtrahend: Dec) -> i128 { + self.i - subtrahend.i + } + + pub fn mul_dec(&self, multiplier: Dec) -> i128 { + let x = self.i; + let y = multiplier.i; + chop_precision_and_round(x * y) + } + + pub fn div_dec(&self, divisor: Dec) -> i128 { + if divisor.i == 0 { + panic!("Error: Tried to divide by 0 for {} / {}", self.i, divisor.i); + } else { + chop_precision_and_round((self.i * DEC_ONE * DEC_ONE) / divisor.i) + } + } + + + // traits to implement: Add, Sub, Mul, Div +} + +impl fmt::Display for Dec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.i == 0 { + write!(f, "{}", "0.".to_owned() + &"0".repeat(DEC_NUM_DIGITS as usize)); + } + let parity = if self.i > 0 { "-" } else { "" }; + write!(f, "{}{}.{}", parity, self.whole(), self.frac()) + } + +} + +impl Add for Dec { + type Output = Self; + + fn add(self, addend: Self) -> Self { + Self { + i: self.add_dec(addend), + } + } +} + +impl Sub for Dec { + type Output = Self; + + fn sub(self, addend: Self) -> Self { + Self { + i: self.sub_dec(addend), + } + } +} + +impl Mul for Dec { + type Output = Self; + + fn mul(self, multiplier: Self) -> Self { + Self { + i: self.mul_dec(multiplier), + } + } +} + +impl Div for Dec { + type Output = Self; + + fn div(self, multiplier: Self) -> Self { + Self { + i: self.div_dec(multiplier), + } + } +} diff --git a/src/core/errors.rs b/src/core/errors.rs index ae26a26..24f4bb1 100644 --- a/src/core/errors.rs +++ b/src/core/errors.rs @@ -1,4 +1,5 @@ +#[derive(Debug)] pub enum SecretError { Bech32Error(String), Error(String), -} \ No newline at end of file +}