Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dirs = "6.0.0"
num = "0.4.1"
num-bigint = "0.4.4"
num-traits = "0.2.18"
num-rational = "0.4"
bigdecimal = "0.4.2"
statrs = "0.18.0"

Expand Down
37 changes: 29 additions & 8 deletions src/rpn_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::{
};

use num::{BigInt, BigUint, One, Zero};
use num_rational::BigRational;
use num_traits::ToPrimitive;

static MALFORMED_ERR: &str = "Runtime Error: The mathematical expression is malformed.";
Expand Down Expand Up @@ -101,7 +102,10 @@ impl RpnResolver<'_> {
if right_value == zero {
return Err(anyhow!(DIVISION_ZERO_ERR));
}
left_value = Number::DecimalNumber(left_value.into());
left_value = Number::DecimalNumber(
BigRational::from_float(f64::from(left_value))
.expect("valid float"),
);
result_stack.push_back(left_value / right_value);
var_stack.push_back(None);
}
Expand All @@ -110,7 +114,10 @@ impl RpnResolver<'_> {
if left_value == zero {
return Err(anyhow!(DIVISION_ZERO_ERR));
}
left_value = Number::DecimalNumber(left_value.into());
left_value = Number::DecimalNumber(
BigRational::from_float(f64::from(left_value))
.expect("valid float"),
);
}
result_stack.push_back(left_value ^ right_value);
var_stack.push_back(None);
Expand Down Expand Up @@ -157,8 +164,11 @@ impl RpnResolver<'_> {
let var_name = v.to_lowercase();
debug!("Heap {:?}", self.local_heap);
let heap = self.local_heap.borrow();
let n = heap.get(&var_name).unwrap_or(&Number::DecimalNumber(0.));
result_stack.push_back(n.clone());
let n = heap
.get(&var_name)
.cloned()
.unwrap_or_else(|| Number::DecimalNumber(BigRational::from_integer(BigInt::zero())));
result_stack.push_back(n);
var_stack.push_back(Some(var_name));
}
Token::Function(fun) => {
Expand Down Expand Up @@ -212,7 +222,9 @@ impl RpnResolver<'_> {
MathFunction::Exp => f64::exp(value.into()),
MathFunction::None => return Err(anyhow!("This should never happen!")),
};
result_stack.push_back(Number::DecimalNumber(res));
result_stack.push_back(Number::DecimalNumber(
BigRational::from_float(res).expect("valid float"),
));
var_stack.push_back(None);
}
Token::SemiColon => {
Expand Down Expand Up @@ -452,12 +464,21 @@ mod tests {
fn test_max_min() {
let session = Session::init();
let mut resolver = session.process("max(1,2)");
assert_eq!(resolver.resolve().unwrap(), Number::DecimalNumber(2.0));
assert_eq!(
resolver.resolve().unwrap(),
Number::DecimalNumber(BigRational::from_float(2.0).unwrap())
);

let mut resolver = session.process("min(1,2)");
assert_eq!(resolver.resolve().unwrap(), Number::DecimalNumber(1.0));
assert_eq!(
resolver.resolve().unwrap(),
Number::DecimalNumber(BigRational::from_float(1.0).unwrap())
);

let mut resolver = session.process("min(max(1,2),3)");
assert_eq!(resolver.resolve().unwrap(), Number::DecimalNumber(2.0));
assert_eq!(
resolver.resolve().unwrap(),
Number::DecimalNumber(BigRational::from_float(2.0).unwrap())
);
}
}
50 changes: 39 additions & 11 deletions src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,31 @@ impl Session {
let mut local_heap: HashMap<String, Number> = HashMap::new();
local_heap.insert(
"pi".to_string(),
Number::DecimalNumber(std::f64::consts::PI),
Number::DecimalNumber(
num_rational::BigRational::from_float(std::f64::consts::PI).unwrap(),
),
);
local_heap.insert(
"e".to_string(),
Number::DecimalNumber(num_rational::BigRational::from_float(std::f64::consts::E).unwrap()),
);
local_heap.insert("e".to_string(), Number::DecimalNumber(std::f64::consts::E));
local_heap.insert(
"tau".to_string(),
Number::DecimalNumber(std::f64::consts::TAU),
Number::DecimalNumber(
num_rational::BigRational::from_float(std::f64::consts::TAU).unwrap(),
),
);
local_heap.insert(
"phi".to_string(),
Number::DecimalNumber((1.0 + 5.0f64.sqrt()) / 2.0),
Number::DecimalNumber(
num_rational::BigRational::from_float((1.0 + 5.0f64.sqrt()) / 2.0).unwrap(),
),
);
local_heap.insert(
"gamma".to_string(),
Number::DecimalNumber(0.577_215_664_901_532_9_f64),
Number::DecimalNumber(
num_rational::BigRational::from_float(0.577_215_664_901_532_9_f64).unwrap(),
),
);
local_heap
}
Expand Down Expand Up @@ -88,7 +99,10 @@ impl Session {
pub fn setf(&self, key: &str, value: f64) {
self.variable_heap
.borrow_mut()
.insert(key.to_lowercase(), Number::DecimalNumber(value));
.insert(
key.to_lowercase(),
Number::DecimalNumber(num_rational::BigRational::from_float(value).unwrap()),
);
}
}

Expand All @@ -102,7 +116,10 @@ mod tests {
fn test_session() {
let session = Session::init();
let mut resolver: RpnResolver = session.process("1+2*3/(4-5)");
assert_eq!(resolver.resolve().unwrap(), Number::DecimalNumber(-5.0));
assert_eq!(
resolver.resolve().unwrap(),
Number::DecimalNumber(num_rational::BigRational::from_float(-5.0).unwrap())
);
}

/// Test for setting an integer variable
Expand All @@ -111,7 +128,10 @@ mod tests {
let session = Session::init();
session.set("x", 4);
let mut resolver: RpnResolver = session.process("x+2*3/(4-5)");
assert_eq!(resolver.resolve().unwrap(), Number::DecimalNumber(-2.0));
assert_eq!(
resolver.resolve().unwrap(),
Number::DecimalNumber(num_rational::BigRational::from_float(-2.0).unwrap())
);
}

/// Test for setting a float variable
Expand All @@ -120,7 +140,10 @@ mod tests {
let session = Session::init();
session.setf("x", 4.5);
let mut resolver: RpnResolver = session.process("x+2*3/(4-5)");
assert_eq!(resolver.resolve().unwrap(), Number::DecimalNumber(-1.5));
assert_eq!(
resolver.resolve().unwrap(),
Number::DecimalNumber(num_rational::BigRational::from_float(-1.5).unwrap())
);
}

/// Test for the default variables initialization
Expand All @@ -130,7 +153,10 @@ mod tests {
let mut resolver: RpnResolver = session.process("pi + e");
assert_eq!(
resolver.resolve().unwrap(),
Number::DecimalNumber(std::f64::consts::PI + std::f64::consts::E)
Number::DecimalNumber(
num_rational::BigRational::from_float(std::f64::consts::PI).unwrap()
+ num_rational::BigRational::from_float(std::f64::consts::E).unwrap()
)
);
}

Expand All @@ -141,7 +167,9 @@ mod tests {
let mut resolver: RpnResolver = session.process("tau / 2");
assert_eq!(
resolver.resolve().unwrap(),
Number::DecimalNumber(std::f64::consts::TAU / 2.0)
Number::DecimalNumber(
num_rational::BigRational::from_float(std::f64::consts::TAU / 2.0).unwrap(),
)
);
}
}
65 changes: 43 additions & 22 deletions src/token.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use bigdecimal::ToPrimitive;
use num_traits::ToPrimitive;
use num_rational::BigRational;
use log::debug;
use num_bigint::BigInt;
use num_traits::FromPrimitive;
Expand All @@ -8,14 +9,14 @@ use std::{
};

/// Enum Type [Number]. Either an BigInt integer [`Number::NaturalNumber`]
/// or a f64 float [`Number::DecimalNumber`]
/// or a [`BigRational`] rational number [`Number::DecimalNumber`]
///
#[derive(Debug, PartialEq, Clone)]
pub enum Number {
/// an Integer [BigInt]
NaturalNumber(BigInt),
/// a Float [f64]
DecimalNumber(f64),
/// a Rational number [BigRational]
DecimalNumber(BigRational),
}

/// A binary or unary Math [`Operator`]
Expand Down Expand Up @@ -221,7 +222,9 @@ impl Token<'_> {
}

if let Ok(v) = t.parse::<f64>() {
return Some(Token::Operand(Number::DecimalNumber(v)));
if let Some(r) = BigRational::from_float(v) {
return Some(Token::Operand(Number::DecimalNumber(r)));
}
}

if let Some(fun) = Token::get_some(t) {
Expand Down Expand Up @@ -268,7 +271,10 @@ impl Display for Number {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Number::NaturalNumber(v) => write!(f, "{v}"),
Number::DecimalNumber(v) => write!(f, "{v}"),
Number::DecimalNumber(v) => {
let fl = v.to_f64().expect("Should not happen");
write!(f, "{fl}")
}
}
}
}
Expand All @@ -287,14 +293,17 @@ impl Display for Number {
fn apply_functional_token_operation<NF, DF>(ln: Number, rn: Number, nf: NF, df: DF) -> Number
where
NF: Fn(BigInt, BigInt) -> BigInt,
DF: Fn(f64, f64) -> f64,
DF: Fn(BigRational, BigRational) -> BigRational,
{
match (ln, rn.clone()) {
(Number::NaturalNumber(v1), Number::NaturalNumber(v2)) => Number::NaturalNumber(nf(v1, v2)),
(Number::NaturalNumber(v1), Number::DecimalNumber(v2)) => {
Number::DecimalNumber(df(ToPrimitive::to_f64(&v1).expect("Should not happen"), v2))
Number::DecimalNumber(df(BigRational::from(v1), v2))
}
(Number::DecimalNumber(v1), Number::NaturalNumber(v2)) => {
Number::DecimalNumber(df(v1, BigRational::from(v2)))
}
(Number::DecimalNumber(v1), _) => Number::DecimalNumber(df(v1, rn.into())),
(Number::DecimalNumber(v1), Number::DecimalNumber(v2)) => Number::DecimalNumber(df(v1, v2)),
}
}

Expand Down Expand Up @@ -339,7 +348,11 @@ impl BitXor for Number {
self,
rhs,
|a, b| BigInt::pow(&a, b.try_into().unwrap()),
f64::powf,
|a, b| {
let af = a.to_f64().expect("Should not happen");
let bf = b.to_f64().expect("Should not happen");
BigRational::from_float(f64::powf(af, bf)).expect("Should not happen")
},
)
}
}
Expand All @@ -350,11 +363,11 @@ impl PartialOrd for Number {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(Number::NaturalNumber(v1), Number::NaturalNumber(v2)) => v1.partial_cmp(&v2),
(Number::NaturalNumber(v1), Number::DecimalNumber(v2)) => ToPrimitive::to_f64(v1)
.expect("Should not happen")
.partial_cmp(v2),
(Number::NaturalNumber(v1), Number::DecimalNumber(v2)) => {
BigRational::from(v1.clone()).partial_cmp(v2)
}
(Number::DecimalNumber(v1), Number::NaturalNumber(v2)) => {
v1.partial_cmp(&(ToPrimitive::to_f64(v2).expect("Should not happen")))
v1.partial_cmp(&BigRational::from(v2.clone()))
}
(Number::DecimalNumber(v1), Number::DecimalNumber(v2)) => v1.partial_cmp(&v2),
}
Expand All @@ -365,7 +378,7 @@ impl From<Number> for f64 {
fn from(n: Number) -> f64 {
match n {
Number::NaturalNumber(v) => ToPrimitive::to_f64(&v).expect("Should not happen"),
Number::DecimalNumber(v) => v,
Number::DecimalNumber(v) => v.to_f64().expect("Should not happen"),
}
}
}
Expand All @@ -375,7 +388,9 @@ impl From<Number> for BigInt {
fn from(n: Number) -> BigInt {
match n {
Number::NaturalNumber(v) => v,
Number::DecimalNumber(v) => BigInt::from_f64(v).expect("Should not happen"),
Number::DecimalNumber(v) => {
BigInt::from_f64(v.to_f64().expect("Should not happen")).expect("Should not happen")
}
}
}
}
Expand All @@ -384,7 +399,7 @@ impl From<Number> for i32 {
fn from(n: Number) -> i32 {
match n {
Number::NaturalNumber(v) => ToPrimitive::to_i32(&v).expect("Should not happen"),
Number::DecimalNumber(v) => ToPrimitive::to_i32(&v).expect("Should not happen"), // not good
Number::DecimalNumber(v) => ToPrimitive::to_i32(&BigInt::from_f64(v.to_f64().expect("Should not happen")).expect("Should not happen")).expect("Should not happen"),
}
}
}
Expand All @@ -393,7 +408,7 @@ impl From<Number> for i64 {
fn from(n: Number) -> i64 {
match n {
Number::NaturalNumber(v) => ToPrimitive::to_i64(&v).expect("Should not happen"),
Number::DecimalNumber(v) => ToPrimitive::to_i64(&v).expect("Should not happen"), // not good
Number::DecimalNumber(v) => ToPrimitive::to_i64(&BigInt::from_f64(v.to_f64().expect("Should not happen")).expect("Should not happen")).expect("Should not happen"),
}
}
}
Expand All @@ -402,7 +417,7 @@ impl From<Number> for i128 {
fn from(n: Number) -> i128 {
match n {
Number::NaturalNumber(v) => ToPrimitive::to_i128(&v).expect("Should not happen"),
Number::DecimalNumber(v) => ToPrimitive::to_i128(&v).expect("Should not happen"), // not good
Number::DecimalNumber(v) => ToPrimitive::to_i128(&BigInt::from_f64(v.to_f64().expect("Should not happen")).expect("Should not happen")).expect("Should not happen"),
}
}
}
Expand Down Expand Up @@ -467,7 +482,9 @@ mod tests {
);
assert_eq!(
Token::tokenize(v[2]),
Some(Token::Operand(Number::DecimalNumber(2.1)))
Some(Token::Operand(Number::DecimalNumber(
BigRational::from_float(2.1).unwrap()
)))
);
}

Expand Down Expand Up @@ -511,7 +528,9 @@ mod tests {
);
assert_eq!(
Token::tokenize("3.14"),
Some(Token::Operand(Number::DecimalNumber(3.14)))
Some(Token::Operand(Number::DecimalNumber(
BigRational::from_float(3.14).unwrap()
)))
);
assert_eq!(Token::tokenize("("), Some(Token::Bracket(Bracket::Open)));
}
Expand All @@ -525,7 +544,9 @@ mod tests {
);
assert_eq!(
Token::tokenize("3.14"),
Some(Token::Operand(Number::DecimalNumber(3.14)))
Some(Token::Operand(Number::DecimalNumber(
BigRational::from_float(3.14).unwrap()
)))
);
assert_eq!(Token::tokenize("("), Some(Token::Bracket(Bracket::Open)));
}
Expand Down
Loading