285 lines
9.5 KiB
Rust
285 lines
9.5 KiB
Rust
use decimal::d128;
|
|
use crate::{Token, Number};
|
|
use crate::units::{Unit, UnitType, convert, add, subtract, multiply, divide, modulo, pow};
|
|
use crate::parser::AstNode;
|
|
use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus};
|
|
use crate::Constant::{Pi, E};
|
|
use crate::UnaryOperator::{Percent, Factorial};
|
|
use crate::TextOperator::{To, Of};
|
|
use crate::FunctionIdentifier::*;
|
|
use crate::lookup::lookup_factorial;
|
|
|
|
/// Evaluate an [`AstNode`](struct.AstNode.html) into a [`Number`](struct.Number.html)
|
|
pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
|
|
let answer = evaluate_node(ast)?;
|
|
Ok(answer)
|
|
}
|
|
|
|
/// Returns the factorial of a [`d128`](../decimal/struct.d128.html) up to `1000!` without doing any math
|
|
///
|
|
/// Factorials do not work with decimal numbers.
|
|
///
|
|
/// All return values of this function are hard-coded.
|
|
pub fn factorial(input: d128) -> d128 {
|
|
return lookup_factorial(input.into());
|
|
}
|
|
|
|
/// Returns the square root of a [`d128`](../decimal/struct.d128.html)
|
|
pub fn sqrt(input: d128) -> d128 {
|
|
let mut n = d128!(1);
|
|
let half = d128!(0.5);
|
|
for _ in 0..10 {
|
|
n = (n + input/n) * half;
|
|
}
|
|
return n
|
|
}
|
|
|
|
/// Returns the cube root of a [`d128`](../decimal/struct.d128.html)
|
|
pub fn cbrt(input: d128) -> d128 {
|
|
let mut n: d128 = input;
|
|
// hope that 20 iterations makes it accurate enough
|
|
let three = d128!(3);
|
|
for _ in 0..20 {
|
|
let z2 = n*n;
|
|
n = n - ((n*z2 - input) / (three*z2));
|
|
}
|
|
return n
|
|
}
|
|
|
|
/// Returns the sine of a [`d128`](../decimal/struct.d128.html)
|
|
pub fn sin(mut input: d128) -> d128 {
|
|
let pi = d128!(3.141592653589793238462643383279503);
|
|
let pi2 = d128!(6.283185307179586476925286766559006);
|
|
|
|
input %= pi2;
|
|
|
|
let negative_correction = if input.is_negative() {
|
|
input -= pi;
|
|
d128!(-1)
|
|
} else {
|
|
d128!(1)
|
|
};
|
|
|
|
let one = d128!(1);
|
|
let two = d128!(2);
|
|
let neg_one = -one;
|
|
|
|
let precision = 37;
|
|
let mut result = d128!(0);
|
|
for i_int in 0..precision {
|
|
let i = d128::from(i_int);
|
|
let calc_result = two*i+one;
|
|
result += neg_one.pow(i) * (input.pow(calc_result) / factorial(calc_result));
|
|
}
|
|
|
|
return negative_correction * result;
|
|
|
|
}
|
|
|
|
/// Returns the cosine of a [`d128`](../decimal/struct.d128.html)
|
|
pub fn cos(input: d128) -> d128 {
|
|
let half_pi = d128!(1.570796326794896619231321691639751);
|
|
return sin(half_pi - input);
|
|
}
|
|
|
|
/// Returns the tangent of a [`d128`](../decimal/struct.d128.html)
|
|
pub fn tan(input: d128) -> d128 {
|
|
return sin(input) / cos(input);
|
|
}
|
|
|
|
/// Evaluate an [`AstNode`](struct.AstNode.html) into a [`Number`](struct.Number.html)
|
|
fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
|
let token = &ast_node.token;
|
|
let children = &ast_node.children;
|
|
match token {
|
|
Token::Number(number) => {
|
|
Ok(Number::new(number.clone(), Unit::NoUnit))
|
|
},
|
|
Token::Constant(constant) => {
|
|
match constant {
|
|
Pi => {
|
|
Ok(Number::new(d128!(3.141592653589793238462643383279503), Unit::NoUnit))
|
|
},
|
|
E => {
|
|
Ok(Number::new(d128!(2.718281828459045235360287471352662), Unit::NoUnit))
|
|
},
|
|
}
|
|
},
|
|
Token::FunctionIdentifier(function) => {
|
|
let child_node = children.get(0).ok_or("Paren has no child[0]")?;
|
|
let child_answer = evaluate_node(child_node)?;
|
|
match function {
|
|
Cbrt => {
|
|
if child_answer.unit.category() == UnitType::NoType {
|
|
let result = cbrt(child_answer.value);
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
} else {
|
|
return Err(format!("log() only accepts UnitType::NoType").to_string())
|
|
}
|
|
},
|
|
Sqrt => {
|
|
if child_answer.unit.category() == UnitType::NoType {
|
|
let result = sqrt(child_answer.value);
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
} else {
|
|
return Err(format!("log() only accepts UnitType::NoType").to_string())
|
|
}
|
|
},
|
|
Log => {
|
|
if child_answer.unit.category() == UnitType::NoType {
|
|
let result = child_answer.value.log10();
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
} else {
|
|
return Err(format!("log() only accepts UnitType::NoType").to_string())
|
|
}
|
|
},
|
|
Ln => {
|
|
if child_answer.unit.category() == UnitType::NoType {
|
|
let result = child_answer.value.ln();
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
} else {
|
|
return Err(format!("ln() only accepts UnitType::NoType").to_string())
|
|
}
|
|
},
|
|
Exp => {
|
|
if child_answer.unit.category() == UnitType::NoType {
|
|
let result = child_answer.value.exp(child_answer.value);
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
} else {
|
|
return Err(format!("exp() only accepts UnitType::NoType").to_string())
|
|
}
|
|
},
|
|
Round => {
|
|
// .quantize() rounds .5 to nearest even integer, so we correct that
|
|
let mut result = child_answer.value.quantize(d128!(1));
|
|
let rounding_change = result - child_answer.value;
|
|
// If the result was rounded down by 0.5, correct by +1
|
|
if rounding_change == d128!(-0.5) { result += d128!(1); }
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
},
|
|
Ceil => {
|
|
let mut result = child_answer.value.quantize(d128!(1));
|
|
let rounding_change = result - child_answer.value;
|
|
if rounding_change.is_negative() { result += d128!(1); }
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
},
|
|
Floor => {
|
|
let mut result = child_answer.value.quantize(d128!(1));
|
|
let rounding_change = result - child_answer.value;
|
|
if !rounding_change.is_negative() { result -= d128!(1); }
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
},
|
|
Abs => {
|
|
let mut result = child_answer.value.abs();
|
|
let rounding_change = result - child_answer.value;
|
|
if rounding_change == d128!(-0.5) { result += d128!(1); }
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
},
|
|
Sin => {
|
|
let result = sin(child_answer.value);
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
},
|
|
Cos => {
|
|
let result = cos(child_answer.value);
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
},
|
|
Tan => {
|
|
let result = tan(child_answer.value);
|
|
return Ok(Number::new(result, child_answer.unit))
|
|
},
|
|
}
|
|
}
|
|
Token::Unit(unit) => {
|
|
let child_node = children.get(0).ok_or("Unit has no child[0]")?;
|
|
let child_answer = evaluate_node(child_node)?;
|
|
Ok(Number::new(child_answer.value, unit.clone()))
|
|
},
|
|
Token::Negative => {
|
|
let child_node = children.get(0).ok_or("Negative has no child[0]")?;
|
|
let child_answer = evaluate_node(child_node)?;
|
|
Ok(Number::new(-child_answer.value, child_answer.unit))
|
|
},
|
|
Token::Paren => {
|
|
let child_node = children.get(0).ok_or("Paren has no child[0]")?;
|
|
return evaluate_node(child_node)
|
|
},
|
|
Token::UnaryOperator(operator) => {
|
|
let child_node = children.get(0).ok_or(format!("Token {:?} has no child[0]", token))?;
|
|
let child_answer = evaluate_node(child_node)?;
|
|
match operator {
|
|
Percent => {
|
|
Ok(Number::new(child_answer.value / d128!(100), child_answer.unit))
|
|
},
|
|
Factorial => {
|
|
let result = factorial(child_answer.value);
|
|
if result.is_nan() {
|
|
return Err("Can only perform factorial on integers from 0 to 1000".to_string());
|
|
}
|
|
Ok(Number::new(result, child_answer.unit))
|
|
},
|
|
}
|
|
},
|
|
Token::TextOperator(operator) => {
|
|
let left_child = children.get(0).ok_or(format!("Token {:?} has no child[0]", token))?;
|
|
let right_child = children.get(1).ok_or(format!("Token {:?} has no child[1]", token))?;
|
|
|
|
match operator {
|
|
To => {
|
|
if let Token::Unit(right_unit) = right_child.token {
|
|
let left = evaluate_node(left_child)?;
|
|
let result = convert(left, right_unit)?;
|
|
return Ok(result)
|
|
} else {
|
|
return Err("Right side of To operator needs to be a unit".to_string())
|
|
}
|
|
},
|
|
Of => {
|
|
let left = evaluate_node(left_child)?;
|
|
let right = evaluate_node(right_child)?;
|
|
if left.unit == Unit::NoUnit {
|
|
return Ok(Number::new(left.value * right.value, right.unit))
|
|
} else {
|
|
return Err("Left side of the Of operator must be NoUnit".to_string())
|
|
}
|
|
},
|
|
}
|
|
},
|
|
Token::Operator(operator) => {
|
|
let left_child = children.get(0).ok_or(format!("Token {:?} has no child[0]", token))?;
|
|
let right_child = children.get(1).ok_or(format!("Token {:?} has no child[1]", token))?;
|
|
let left = evaluate_node(left_child)?;
|
|
let right = evaluate_node(right_child)?;
|
|
match operator {
|
|
Plus => {
|
|
Ok(add(left, right)?)
|
|
},
|
|
Minus => {
|
|
Ok(subtract(left, right)?)
|
|
},
|
|
Multiply => {
|
|
Ok(multiply(left, right)?)
|
|
// }
|
|
},
|
|
Divide => {
|
|
Ok(divide(left, right)?)
|
|
// }
|
|
},
|
|
Modulo => {
|
|
Ok(modulo(left, right)?)
|
|
// }
|
|
},
|
|
Caret => {
|
|
Ok(pow(left, right)?)
|
|
// }
|
|
},
|
|
_ => {
|
|
Err(format!("Unexpected operator {:?}", operator))
|
|
}
|
|
}
|
|
},
|
|
_ => {
|
|
Err(format!("Unexpected token {:?}", token))
|
|
}
|
|
}
|
|
}
|