use crate::expression::{
error::AstError,
parser::{ExprToken, Expression},
};
use std::collections::{HashMap, VecDeque};
pub type ExprNum = f64;
pub type NodeIdx = usize;
pub const EXPR_EPSILON: f64 = 1e-6;
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Clone, Copy)]
pub enum BinaryOpr {
Add,
Sub,
Mul,
Div,
}
impl<'a> BinaryOpr {
#[inline]
pub fn from_opr(opr: &'a str) -> Result<Self, AstError<'a>> {
match opr {
"+" => Ok(BinaryOpr::Add),
"-" => Ok(BinaryOpr::Sub),
"*" => Ok(BinaryOpr::Mul),
"/" => Ok(BinaryOpr::Div),
unkn => Err(AstError::UnknownOperator(unkn)),
}
}
#[inline]
pub fn get_all_operators() -> [Self; 4] {
[
BinaryOpr::Add,
BinaryOpr::Sub,
BinaryOpr::Mul,
BinaryOpr::Div,
]
}
#[inline]
pub fn get_precedence(&self) -> u8 {
match self {
BinaryOpr::Mul | BinaryOpr::Div => 2,
BinaryOpr::Add | BinaryOpr::Sub => 1,
}
}
#[inline]
pub fn is_commutation(&self) -> bool {
matches!(self, BinaryOpr::Add | BinaryOpr::Mul)
}
pub fn calc(&self, left: &ExprNum, right: &ExprNum) -> Result<ExprNum, AstError<'a>> {
match self {
BinaryOpr::Add => BinaryOpr::validate_calc_res(*left + *right),
BinaryOpr::Sub => BinaryOpr::validate_calc_res(*left - *right),
BinaryOpr::Mul => BinaryOpr::validate_calc_res(*left * *right),
BinaryOpr::Div => {
if right.abs() < EXPR_EPSILON {
return Err(AstError::DivisionByZero);
}
BinaryOpr::validate_calc_res(*left / *right)
}
}
}
#[inline]
fn validate_calc_res(res: ExprNum) -> Result<ExprNum, AstError<'a>> {
if res.is_subnormal() {
Err(AstError::CalculationResultError(res))
} else {
Ok(res)
}
}
#[inline]
pub fn as_str(&self) -> &'static str {
match self {
BinaryOpr::Add => "+",
BinaryOpr::Sub => "-",
BinaryOpr::Mul => "*",
BinaryOpr::Div => "/",
}
}
}
#[cfg_attr(test, derive(Debug, PartialEq))]
pub enum ExprNode<'a> {
Literal(ExprNum),
Variable(&'a str),
BinaryOpr {
opr: BinaryOpr,
l_idx: NodeIdx,
r_idx: NodeIdx,
},
}
pub struct ExprTree<'a> {
nodes: Vec<ExprNode<'a>>,
root_idx: Option<NodeIdx>,
pre_v: Vec<Option<ExprNum>>,
}
impl<'a> ExprTree<'a> {
pub fn new() -> Self {
ExprTree {
nodes: Vec::new(),
root_idx: None,
pre_v: Vec::new(),
}
}
pub fn from_literal(value: ExprNum) -> Self {
ExprTree {
nodes: vec![ExprNode::Literal(value)],
root_idx: Some(0),
pre_v: vec![Some(value)],
}
}
pub fn from_variable(name: &'a str) -> Self {
ExprTree {
nodes: vec![ExprNode::Variable(name)],
root_idx: Some(0),
pre_v: vec![None],
}
}
pub fn from_expr(infix_expr: &'a str) -> Result<Self, AstError<'a>> {
ExprTree::from_postfix_expression(Expression::from_expr(infix_expr)?.to_postfix()?)
}
pub fn from_infix_expression(expr: &mut Expression<'a>) -> Result<Self, AstError<'a>> {
ExprTree::from_postfix_expression(expr.to_postfix()?)
}
pub fn from_postfix_expression(expr: &Expression<'a>) -> Result<Self, AstError<'a>> {
let mut tree = ExprTree::new();
let mut stack = Vec::new();
for tk in expr.tokens.iter() {
let node_idx = match tk {
ExprToken::Literal(num) => tree.add_node(ExprNode::Literal(*num))?,
ExprToken::Variable(var) => tree.add_node(ExprNode::Variable(var))?,
ExprToken::BinaryOpr(opr) => {
let r_idx = stack.pop().ok_or(AstError::InvalidOperandCount {
operator: opr.as_str(),
expected: 2,
actual: stack.len(),
})?;
let l_idx = stack.pop().ok_or(AstError::InvalidOperandCount {
operator: opr.as_str(),
expected: 2,
actual: stack.len(),
})?;
tree.add_node(ExprNode::BinaryOpr {
opr: *opr,
l_idx,
r_idx,
})?
}
ExprToken::LeftParen | ExprToken::RightParen => {
return Err(AstError::InvalidExpressionStructure(
"Parentheses shouldn't appear in the postfix expression",
));
}
};
stack.push(node_idx);
}
tree.root_idx = Some(tree.nodes.len() - 1);
Ok(tree)
}
pub fn combine_with(
&mut self,
right_subtree: &mut Self,
root_opr: BinaryOpr,
) -> Result<&Self, AstError<'a>> {
let orig_left_len = self.nodes.len();
let root_l_idx = self.get_root_idx()?;
let root_r_idx = orig_left_len + right_subtree.get_root_idx()?;
for node in right_subtree.nodes.iter_mut() {
match node {
ExprNode::BinaryOpr { l_idx, r_idx, .. } => {
*l_idx += orig_left_len;
*r_idx += orig_left_len;
}
ExprNode::Variable(_) | ExprNode::Literal(_) => continue,
}
}
self.nodes.append(&mut right_subtree.nodes);
self.pre_v.append(&mut right_subtree.pre_v);
self.add_root(root_opr, root_l_idx, root_r_idx)?;
right_subtree.root_idx = None;
Ok(self)
}
#[inline]
pub fn add_node(&mut self, new_node: ExprNode<'a>) -> Result<usize, AstError<'a>> {
let new_node_idx = self.nodes.len();
self.nodes.push(new_node);
self.pre_calc(new_node_idx)?; Ok(new_node_idx)
}
#[inline]
pub fn add_root(
&mut self,
opr: BinaryOpr,
l_idx: usize,
r_idx: usize,
) -> Result<usize, AstError<'a>> {
let new_node_idx = self.nodes.len();
let new_root = ExprNode::BinaryOpr { opr, l_idx, r_idx };
self.nodes.push(new_root);
self.pre_calc(new_node_idx)?;
self.root_idx = Some(new_node_idx);
Ok(new_node_idx)
}
#[inline]
pub fn pre_calc(&mut self, node_idx: usize) -> Result<Option<ExprNum>, AstError<'a>> {
if node_idx > self.pre_v.len() {
return Err(AstError::InvalidPrecalcIndex {
index: node_idx,
total_nodes: self.pre_v.len(),
});
} else if node_idx == self.pre_v.len() {
self.pre_v.push(None);
}
match self.get_node(node_idx)? {
ExprNode::Literal(num) => {
self.pre_v[node_idx] = Some(*num);
}
ExprNode::Variable(_) => {
self.pre_v[node_idx] = None; }
ExprNode::BinaryOpr { opr, l_idx, r_idx } => {
if let (Some(left), Some(right)) = (self.pre_v[*l_idx], self.pre_v[*r_idx]) {
self.pre_v[node_idx] = opr.calc(&left, &right).ok();
}
}
}
Ok(self.pre_v[node_idx])
}
#[inline]
pub fn get_node(&self, index: NodeIdx) -> Result<&ExprNode<'a>, AstError<'a>> {
self.nodes.get(index).ok_or(AstError::InvalidNodeIndex {
index,
total_nodes: self.nodes.len(),
})
}
#[inline]
pub fn get_root_idx(&self) -> Result<NodeIdx, AstError<'a>> {
self.root_idx.ok_or(AstError::EmptyExpression)
}
#[inline]
pub fn get_pre_value(&self, index: NodeIdx) -> Result<&Option<ExprNum>, AstError<'a>> {
self.pre_v.get(index).ok_or(AstError::InvalidNodeIndex {
index,
total_nodes: self.nodes.len(),
})
}
#[inline]
pub fn pop_node(&mut self) -> Option<ExprNode<'a>> {
self.pre_v.pop();
self.nodes.pop()
}
pub fn check_variables(&self) -> Vec<&str> {
self.nodes
.iter()
.filter_map(|node| {
if let ExprNode::Variable(var) = node {
Some(*var)
} else {
None
}
})
.collect()
}
fn iter_level_order(&self) -> Result<impl Iterator<Item = NodeIdx>, AstError<'a>> {
let mut levels_order = Vec::new();
let mut queue = VecDeque::new();
queue.push_back(self.get_root_idx()?);
while let Some(cur_idx) = queue.pop_front() {
match self.get_node(cur_idx)? {
ExprNode::Variable(_) | ExprNode::Literal(_) => continue,
ExprNode::BinaryOpr { l_idx, r_idx, .. } => {
levels_order.push(cur_idx);
queue.push_back(*l_idx);
queue.push_back(*r_idx);
}
}
}
Ok(levels_order.into_iter().rev())
}
pub fn eval(&self, variables: &HashMap<&str, ExprNum>) -> Result<ExprNum, AstError<'a>> {
if let Some(pre_value) = self.pre_v[self.get_root_idx()?] {
return Ok(pre_value);
}
let mut node_results = HashMap::new();
for node_idx in self.iter_level_order()? {
let node_res = match self.get_node(node_idx)? {
ExprNode::Literal(num) => *num,
ExprNode::Variable(var) => *variables
.get(var)
.ok_or(AstError::UninitializedVariable(var))?,
ExprNode::BinaryOpr { l_idx, r_idx, opr } => {
let left = match self.get_node(*l_idx)? {
ExprNode::Literal(num) => num,
ExprNode::Variable(var) => variables
.get(var)
.ok_or(AstError::UninitializedVariable(var))?,
_ => node_results
.get(l_idx)
.ok_or(AstError::ExpressionEvaluationError(
"Operator left child nodes have not been calculated",
))?,
};
let right = match self.get_node(*r_idx)? {
ExprNode::Literal(num) => num,
ExprNode::Variable(var) => variables
.get(var)
.ok_or(AstError::UninitializedVariable(var))?,
_ => node_results
.get(r_idx)
.ok_or(AstError::ExpressionEvaluationError(
"Operator right child nodes have not been calculated",
))?,
};
opr.calc(left, right)?
}
};
node_results.insert(node_idx, node_res);
}
Ok(node_results
.get(&self.get_root_idx()?)
.ok_or(AstError::ExpressionEvaluationError(
"Root node have not been calculated",
))?
.to_owned())
}
}
impl<'a> Default for ExprTree<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> std::fmt::Display for ExprTree<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
fn write_infix(
tree: &ExprTree,
f: &mut std::fmt::Formatter,
idx: NodeIdx,
parent_opr: Option<BinaryOpr>,
is_right_tree: bool,
) -> std::fmt::Result {
match tree.nodes.get(idx).ok_or(std::fmt::Error)? {
ExprNode::Literal(n) => write!(f, "{}", n),
ExprNode::Variable(v) => write!(f, "{}", v),
ExprNode::BinaryOpr { opr, l_idx, r_idx } => {
let need_paren = parent_opr.is_some_and(|p_opr| {
let p_prec = p_opr.get_precedence();
let cur_prec = opr.get_precedence();
cur_prec < p_prec ||
(cur_prec == p_prec && (
(!p_opr.is_commutation() && is_right_tree) ||
(!opr.is_commutation() && !is_right_tree)
))
});
if need_paren {
write!(f, "(")?;
}
write_infix(tree, f, *l_idx, Some(*opr), false)?;
write!(f, " {} ", opr.as_str())?;
write_infix(tree, f, *r_idx, Some(*opr), true)?;
if need_paren {
write!(f, ")")?;
}
Ok(())
}
}
}
match self.root_idx {
Some(idx) => write_infix(self, f, idx, None, false),
None => write!(f, ""),
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
#[test]
fn test_build_expression_tree() {
let tree = ExprTree::from_expr("a_b_c+(22.2-((abc55*4_400) / 55))").unwrap();
assert_eq!(
tree.nodes,
vec![
ExprNode::Variable("a_b_c"),
ExprNode::Literal(22.2),
ExprNode::Variable("abc55"),
ExprNode::Literal(4_400.0),
ExprNode::BinaryOpr {
opr: BinaryOpr::Mul,
l_idx: 2,
r_idx: 3
},
ExprNode::Literal(55.0),
ExprNode::BinaryOpr {
opr: BinaryOpr::Div,
l_idx: 4,
r_idx: 5
},
ExprNode::BinaryOpr {
opr: BinaryOpr::Sub,
l_idx: 1,
r_idx: 6,
},
ExprNode::BinaryOpr {
opr: BinaryOpr::Add,
l_idx: 0,
r_idx: 7
},
]
);
}
#[test]
fn test_tree_levels_order() {
let tree = ExprTree::from_expr("((5+2)-3)*(8/2)-15").unwrap();
assert_eq!(
tree.iter_level_order().unwrap().collect::<Vec<NodeIdx>>(),
vec![2, 7, 4, 8, 10]
);
}
#[test]
fn test_tree_display() {
let tree = ExprTree::from_expr("(((5+(2)-3)*(8/2)))-15").unwrap();
assert_eq!(tree.to_string(), "(5 + 2 - 3) * 8 / 2 - 15".to_string());
let tree = ExprTree::from_expr("5 * 4 - (1 - 5)").unwrap();
assert_eq!(tree.to_string(), "5 * 4 - (1 - 5)".to_string());
}
#[test]
fn test_expression_tree_variable() {
let tree = ExprTree::from_expr("((a+b)-c)*(d/zero)-e").unwrap();
assert_eq!(
tree.check_variables(),
vec!["a", "b", "c", "d", "zero", "e"]
);
assert!(tree.eval(&HashMap::from([("only_zero", 0.0)])).is_err());
assert_eq!(
tree.eval(&HashMap::from([
("a", 5.0),
("b", 2.0),
("c", 3.0),
("d", 8.0),
("e", 15.0),
("zero", 2.0),
]))
.unwrap(),
1.0
);
}
#[test]
fn test_eval_expression_tree() {
let tree = ExprTree::from_expr("((5+2)-3)*(8/2)-15").unwrap();
assert_eq!(tree.eval(&HashMap::new()).unwrap(), 1.0);
let tree = ExprTree::from_expr("((5+2)-3)*(8/div_zero)-15").unwrap();
assert_eq!(tree.check_variables(), vec!["div_zero"]);
assert!(tree.eval(&HashMap::from([("div_zero", 0.0)])).is_err());
let tree = ExprTree::from_expr("((5+2)-3)*(8/div_zero)-15").unwrap();
assert_eq!(
tree.eval(&HashMap::from([("div_zero", 4.0)])).unwrap(),
-7.0
);
}
#[test]
fn test_combine_tree() {
let values = [2.0, 4.0, 6.0, 8.0, 10.0];
let mut trees = values
.iter()
.map(|v| ExprTree::from_literal(*v))
.collect::<Vec<_>>();
let mut final_tree = ExprTree::from_literal(1.0);
let mut s_tmp = String::from_str("1").unwrap();
for (i, t) in trees.iter_mut().enumerate() {
let ct = final_tree.combine_with(t, BinaryOpr::Add).unwrap();
s_tmp.push_str(&format!(" + {}", values[i]));
assert_eq!(format!("{}", ct), s_tmp);
}
}
#[test]
fn test_pre_eval() {
let values = [2.0, 4.0, 6.0, 8.0, 10.0];
let mut trees = values
.iter()
.map(|v| ExprTree::from_literal(*v))
.collect::<Vec<_>>();
let mut final_tree = ExprTree::from_literal(1.0);
let mut s_tmp = String::from_str("1").unwrap();
for (i, t) in trees.iter_mut().enumerate() {
let ct = final_tree.combine_with(t, BinaryOpr::Mul).unwrap();
println!("{:?}\n{:?}\n{:?}", ct.nodes, ct.root_idx, ct.pre_v);
s_tmp.push_str(&format!(" * {}", values[i]));
assert_eq!(format!("{}", ct), s_tmp);
assert_eq!(
ct.eval(&HashMap::new()).unwrap(),
ct.pre_v.last().unwrap().unwrap()
);
}
}
}