use std::rc::Rc; use rug::Float; use super::{Node, NodeEnum, Precedence, constant::Constant, set::Set}; #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct Divide { left: Rc, right: Rc, } impl Node for Divide { fn evaluate(&self, env: &mut super::Environment) -> Result, String> { let evaluated_left = self.left.evaluate(env)?; let evaluated_right = self.right.evaluate(env)?; match (evaluated_left.as_ref(), evaluated_right.as_ref()) { // Error if dividing by zero (_, NodeEnum::Constant(zero)) if zero.get_value() == &0.0 => { Err("Division by Zero".into()) } (_, _) if evaluated_left == evaluated_right => { Ok(Rc::new(Constant::new_from_float(0.0, &env).into())) } // Zero rule (NodeEnum::Constant(zero), _) if zero.get_value() == &0.0 => Ok(evaluated_left), // Cancel out symbols and constants (NodeEnum::Multiply(m1), NodeEnum::Multiply(m2)) => { match ( (m1.get_left().as_ref(), m1.get_right().as_ref()), (m2.get_left().as_ref(), m2.get_right().as_ref()), ) { ((NodeEnum::Symbol(s1), o1), (NodeEnum::Symbol(s2), o2)) | ((o1, NodeEnum::Symbol(s1)), (NodeEnum::Symbol(s2), o2)) | ((NodeEnum::Symbol(s1), o1), (o2, NodeEnum::Symbol(s2))) | ((o1, NodeEnum::Symbol(s1)), (o2, NodeEnum::Symbol(s2))) => { if s1 == s2 { Divide::new(Rc::new(o1.clone()), Rc::new(o2.clone())).evaluate(env) } else { Ok(Divide::new_rc(evaluated_left, evaluated_right)) } } _ => Ok(Rc::new(Divide::new(evaluated_left, evaluated_right).into())), } } // Constant / Constant = Constant (NodeEnum::Constant(a), NodeEnum::Constant(b)) => Ok(Rc::new( Constant::new(Float::with_val_64( env.get_float_precision(), a.get_value() / b.get_value(), )) .into(), )), // Divide a set with a constant (NodeEnum::Set(s), NodeEnum::Constant(c)) => { let old_values = s.get_values(); let mut values = Vec::with_capacity(old_values.len()); let c: Rc = Rc::new(c.clone().into()); for value in old_values.iter() { let new_value = Divide::new(c.clone(), value.clone()); values.push(new_value.evaluate(env)?); } Ok(Set::new(values)) } // Divide a set with a symbol (NodeEnum::Set(s), NodeEnum::Symbol(c)) => { let old_values = s.get_values(); let mut values = Vec::with_capacity(old_values.len()); let c: Rc = Rc::new(c.clone().into()); for value in old_values.iter() { let new_value = Divide::new(c.clone(), value.clone()); values.push(new_value.evaluate(env)?); } Ok(Set::new(values)) } _ => Ok(Rc::new(Divide::new(evaluated_left, evaluated_right).into())), } } fn as_string(&self, env: Option<&super::Environment>) -> String { let left_string = if self.left.precedence() <= self.precedence() { format!("({})", self.left.as_string(env)) } else { self.left.as_string(env) }; let right_string = if self.right.precedence() <= self.precedence() { format!("({})", self.right.as_string(env)) } else { self.right.as_string(env) }; format!("{}/{}", left_string, right_string) } fn precedence(&self) -> Precedence { Precedence::Factor } } impl Divide { pub fn new(left: Rc, right: Rc) -> Self { Self { left, right } } pub fn new_rc(left: Rc, right: Rc) -> Rc { Rc::new(Self::new(left, right).into()) } pub fn get_left(&self) -> Rc { self.left.clone() } pub fn get_right(&self) -> Rc { self.right.clone() } }