openbirch-rs/src/lib/node/divide.rs
2025-02-24 17:13:40 +01:00

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()
}
}