127 lines
4.4 KiB
Rust
127 lines
4.4 KiB
Rust
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<NodeEnum>,
|
|
right: Rc<NodeEnum>,
|
|
}
|
|
|
|
impl Node for Divide {
|
|
fn evaluate(&self, env: &mut super::Environment) -> Result<Rc<super::NodeEnum>, 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<NodeEnum> = 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<NodeEnum> = 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<NodeEnum>, right: Rc<NodeEnum>) -> Self {
|
|
Self { left, right }
|
|
}
|
|
|
|
pub fn new_rc(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Rc<NodeEnum> {
|
|
Rc::new(Self::new(left, right).into())
|
|
}
|
|
|
|
pub fn get_left(&self) -> Rc<NodeEnum> {
|
|
self.left.clone()
|
|
}
|
|
|
|
pub fn get_right(&self) -> Rc<NodeEnum> {
|
|
self.right.clone()
|
|
}
|
|
}
|