use crate::symbolic::symbolic_engine::Expr;
use std::collections::{BTreeMap, HashMap};
impl Expr {
#[allow(dead_code)]
fn nozeros(&self) -> Expr {
match self {
Expr::Var(_) => self.clone(),
Expr::Const(_) => self.clone(),
Expr::Add(lhs, rhs) => {
let simplified_lhs = lhs.nozeros();
let simplified_rhs = rhs.nozeros();
if simplified_lhs == Expr::Const(0.0) && simplified_rhs == Expr::Const(0.0) {
Expr::Const(0.0)
} else {
Expr::Add(Box::new(simplified_lhs), Box::new(simplified_rhs))
}
}
Expr::Sub(lhs, rhs) => {
let simplified_lhs = lhs.nozeros();
let simplified_rhs = rhs.nozeros();
if simplified_lhs == Expr::Const(0.0) && simplified_rhs == Expr::Const(0.0) {
Expr::Const(0.0)
} else {
Expr::Sub(Box::new(simplified_lhs), Box::new(simplified_rhs))
}
}
Expr::Mul(lhs, rhs) => {
let simplified_lhs = lhs.nozeros();
let simplified_rhs = rhs.nozeros();
if simplified_lhs == Expr::Const(0.0) || simplified_rhs == Expr::Const(0.0) {
Expr::Const(0.0)
} else {
Expr::Mul(Box::new(simplified_lhs), Box::new(simplified_rhs))
}
}
Expr::Div(lhs, rhs) => {
let simplified_lhs = lhs.nozeros();
let simplified_rhs = rhs.nozeros();
if simplified_lhs == Expr::Const(0.0) {
Expr::Const(0.0)
} else {
Expr::Div(Box::new(simplified_lhs), Box::new(simplified_rhs))
}
}
Expr::Pow(base, exp) => Expr::Pow(Box::new(base.nozeros()), Box::new(exp.nozeros())),
Expr::Exp(expr) => Expr::Exp(Box::new(expr.nozeros())),
Expr::Ln(expr) => Expr::Ln(Box::new(expr.nozeros())),
Expr::sin(expr) => Expr::sin(Box::new(expr.nozeros())),
Expr::cos(expr) => Expr::cos(Box::new(expr.nozeros())),
Expr::tg(expr) => Expr::tg(Box::new(expr.nozeros())),
Expr::ctg(expr) => Expr::ctg(Box::new(expr.nozeros())),
Expr::arcsin(expr) => Expr::arcsin(Box::new(expr.nozeros())),
Expr::arccos(expr) => Expr::arccos(Box::new(expr.nozeros())),
Expr::arctg(expr) => Expr::arctg(Box::new(expr.nozeros())),
Expr::arcctg(expr) => Expr::arcctg(Box::new(expr.nozeros())),
}
}
pub fn simplify_numbers(&self) -> Expr {
match self {
Expr::Var(_) => self.clone(),
Expr::Const(_) => self.clone(),
Expr::Add(lhs, rhs) => {
let lhs_simplified = lhs.simplify_numbers();
let rhs_simplified = rhs.simplify_numbers();
match (lhs_simplified, rhs_simplified) {
(Expr::Const(a), Expr::Const(b)) => Expr::Const(a + b),
(lhs, rhs) => Expr::Add(Box::new(lhs), Box::new(rhs)),
}
}
Expr::Sub(lhs, rhs) => {
let lhs_simplified = lhs.simplify_numbers();
let rhs_simplified = rhs.simplify_numbers();
match (lhs_simplified, rhs_simplified) {
(Expr::Const(a), Expr::Const(b)) => Expr::Const(a - b),
(lhs, rhs) => Expr::Sub(Box::new(lhs), Box::new(rhs)),
}
}
Expr::Mul(lhs, rhs) => {
let lhs_simplified = lhs.simplify_numbers();
let rhs_simplified = rhs.simplify_numbers();
match (lhs_simplified, rhs_simplified) {
(Expr::Const(a), Expr::Const(b)) => Expr::Const(a * b),
(lhs, rhs) => Expr::Mul(Box::new(lhs), Box::new(rhs)),
}
}
Expr::Div(lhs, rhs) => {
let lhs_simplified = lhs.simplify_numbers();
let rhs_simplified = rhs.simplify_numbers();
match (lhs_simplified, rhs_simplified) {
(Expr::Const(a), Expr::Const(b)) => Expr::Const(a / b),
(lhs, rhs) => Expr::Div(Box::new(lhs), Box::new(rhs)),
}
}
Expr::Pow(base, exp) => Expr::Pow(
Box::new(base.simplify_numbers()),
Box::new(exp.simplify_numbers()),
),
Expr::Exp(expr) => Expr::Exp(Box::new(expr.simplify_numbers())),
Expr::Ln(expr) => Expr::Ln(Box::new(expr.simplify_numbers())),
Expr::sin(expr) => Expr::sin(Box::new(expr.simplify_numbers())),
Expr::cos(expr) => Expr::cos(Box::new(expr.simplify_numbers())),
Expr::tg(expr) => Expr::tg(Box::new(expr.simplify_numbers())),
Expr::ctg(expr) => Expr::ctg(Box::new(expr.simplify_numbers())),
Expr::arcsin(expr) => Expr::arcsin(Box::new(expr.simplify_numbers())),
Expr::arccos(expr) => Expr::arccos(Box::new(expr.simplify_numbers())),
Expr::arctg(expr) => Expr::arctg(Box::new(expr.simplify_numbers())),
Expr::arcctg(expr) => Expr::arcctg(Box::new(expr.simplify_numbers())),
}
}
pub fn simplify_(&self) -> Expr {
match self {
Expr::Var(_) => self.clone(),
Expr::Const(_) => self.clone(),
Expr::Add(lhs, rhs) => {
let lhs = lhs.simplify_();
let rhs = rhs.simplify_();
match (&lhs, &rhs) {
(Expr::Const(a), Expr::Const(b)) => Expr::Const(a + b), (Expr::Const(0.0), _) => rhs, (_, Expr::Const(0.0)) => lhs, _ => {
let expr = Expr::Add(Box::new(lhs), Box::new(rhs));
Self::simplify_polynomial(&expr).unwrap_or(expr)
}
}
}
Expr::Sub(lhs, rhs) => {
let lhs = lhs.simplify_();
let rhs = rhs.simplify_();
match (&lhs, &rhs) {
(Expr::Const(a), Expr::Const(b)) => Expr::Const(a - b), (_, Expr::Const(0.0)) => lhs, _ if lhs == rhs => Expr::Const(0.0),
_ => {
let neg_rhs =
Expr::Mul(Box::new(Expr::Const(-1.0)), Box::new(rhs)).simplify_();
let add_expr = Expr::Add(Box::new(lhs), Box::new(neg_rhs));
Self::simplify_polynomial(&add_expr).unwrap_or(add_expr)
}
}
}
Expr::Mul(lhs, rhs) => {
let lhs = lhs.simplify_();
let rhs = rhs.simplify_();
match (&lhs, &rhs) {
(Expr::Const(a), Expr::Const(b)) => Expr::Const(a * b), (Expr::Const(0.0), _) | (_, Expr::Const(0.0)) => Expr::Const(0.0), (Expr::Const(1.0), _) => rhs, (_, Expr::Const(1.0)) => lhs, (Expr::Pow(base1, exp1), Expr::Pow(base2, exp2)) if base1 == base2 => {
let new_exp = Expr::Add(exp1.clone(), exp2.clone()).simplify_();
Expr::Pow(base1.clone(), Box::new(new_exp))
}
(Expr::Var(v1), Expr::Pow(base, exp))
| (Expr::Pow(base, exp), Expr::Var(v1)) => {
if let Expr::Var(v2) = base.as_ref() {
if v1 == v2 {
let new_exp =
Expr::Add(Box::new(Expr::Const(1.0)), exp.clone()).simplify_();
return Expr::Pow(
Box::new(Expr::Var(v1.clone())),
Box::new(new_exp),
);
}
}
Expr::Mul(Box::new(lhs), Box::new(rhs))
}
(Expr::Var(v1), Expr::Var(v2)) if v1 == v2 => {
Expr::Pow(Box::new(Expr::Var(v1.clone())), Box::new(Expr::Const(2.0)))
}
(Expr::Mul(inner_lhs, inner_rhs), Expr::Const(c)) => {
match (inner_lhs.as_ref(), inner_rhs.as_ref()) {
(Expr::Const(c1), _) => {
Expr::Mul(Box::new(Expr::Const(c1 * c)), inner_rhs.clone())
.simplify_()
}
(_, Expr::Const(c1)) => {
Expr::Mul(Box::new(Expr::Const(c1 * c)), inner_lhs.clone())
.simplify_()
}
_ => Expr::Mul(Box::new(lhs), Box::new(rhs)),
}
}
(Expr::Const(c), Expr::Mul(inner_lhs, inner_rhs)) => {
match (inner_lhs.as_ref(), inner_rhs.as_ref()) {
(Expr::Const(c1), _) => {
Expr::Mul(Box::new(Expr::Const(c * c1)), inner_rhs.clone())
.simplify_()
}
(_, Expr::Const(c1)) => {
Expr::Mul(Box::new(Expr::Const(c * c1)), inner_lhs.clone())
.simplify_()
}
_ => Expr::Mul(Box::new(lhs), Box::new(rhs)),
}
}
_ => Expr::Mul(Box::new(lhs), Box::new(rhs)),
}
}
Expr::Div(lhs, rhs) => {
let lhs = lhs.simplify_();
let rhs = rhs.simplify_();
match (&lhs, &rhs) {
(Expr::Const(a), Expr::Const(b)) if *b != 0.0 => Expr::Const(a / b), (Expr::Const(0.0), _) => Expr::Const(0.0), (_, Expr::Const(1.0)) => lhs, (Expr::Pow(base1, exp1), Expr::Pow(base2, exp2)) if base1 == base2 => {
let new_exp = Expr::Sub(exp1.clone(), exp2.clone()).simplify_();
match new_exp {
Expr::Const(0.0) => Expr::Const(1.0),
_ => Expr::Pow(base1.clone(), Box::new(new_exp)),
}
}
(Expr::Var(v1), Expr::Pow(base, exp)) => {
if let Expr::Var(v2) = base.as_ref() {
if v1 == v2 {
let new_exp =
Expr::Sub(Box::new(Expr::Const(1.0)), exp.clone()).simplify_();
match new_exp {
Expr::Const(0.0) => return Expr::Const(1.0),
_ => {
return Expr::Pow(
Box::new(Expr::Var(v1.clone())),
Box::new(new_exp),
);
}
}
}
}
Expr::Div(Box::new(lhs), Box::new(rhs))
}
(Expr::Pow(base, exp), Expr::Var(v2)) => {
if let Expr::Var(v1) = base.as_ref() {
if v1 == v2 {
let new_exp =
Expr::Sub(exp.clone(), Box::new(Expr::Const(1.0))).simplify_();
match new_exp {
Expr::Const(0.0) => return Expr::Const(1.0),
_ => {
return Expr::Pow(
Box::new(Expr::Var(v1.clone())),
Box::new(new_exp),
);
}
}
}
}
Expr::Div(Box::new(lhs), Box::new(rhs))
}
(Expr::Var(v1), Expr::Var(v2)) if v1 == v2 => Expr::Const(1.0),
(Expr::Mul(inner_lhs, inner_rhs), Expr::Const(c)) if *c != 0.0 => {
match (inner_lhs.as_ref(), inner_rhs.as_ref()) {
(Expr::Const(c1), _) => {
Expr::Mul(Box::new(Expr::Const(c1 / c)), inner_rhs.clone())
.simplify_()
}
(_, Expr::Const(c1)) => {
Expr::Mul(Box::new(Expr::Const(c1 / c)), inner_lhs.clone())
.simplify_()
}
_ => Expr::Div(Box::new(lhs), Box::new(rhs)),
}
}
(_, Expr::Mul(inner_lhs, inner_rhs)) => {
match (inner_lhs.as_ref(), inner_rhs.as_ref()) {
(Expr::Const(c1), Expr::Const(c2)) => {
Expr::Div(Box::new(lhs), Box::new(Expr::Const(c1 * c2))).simplify_()
}
_ => Expr::Div(Box::new(lhs), Box::new(rhs)),
}
}
_ => Expr::Div(Box::new(lhs), Box::new(rhs)),
}
}
Expr::Pow(base, exp) => {
let base = base.simplify_();
let exp = exp.simplify_();
match (&base, &exp) {
(Expr::Const(a), Expr::Const(b)) => Expr::Const(a.powf(*b)), (_, Expr::Const(0.0)) => Expr::Const(1.0), (_, Expr::Const(1.0)) => base, (Expr::Const(0.0), _) => Expr::Const(0.0), (Expr::Const(1.0), _) => Expr::Const(1.0), (Expr::Pow(inner_base, inner_exp), _) => {
let new_exp = Expr::Mul(inner_exp.clone(), Box::new(exp)).simplify_();
Expr::Pow(inner_base.clone(), Box::new(new_exp))
}
_ => Expr::Pow(Box::new(base), Box::new(exp)),
}
}
Expr::Exp(expr) => {
let expr = expr.simplify_();
match &expr {
Expr::Const(0.0) => Expr::Const(1.0),
_ => Expr::Exp(Box::new(expr)),
}
}
Expr::Ln(expr) => {
let expr = expr.simplify_();
match &expr {
Expr::Const(1.0) => Expr::Const(0.0),
_ => Expr::Ln(Box::new(expr)),
}
}
Expr::sin(expr) => {
let expr = expr.simplify_();
match &expr {
Expr::Const(0.0) => Expr::Const(0.0),
_ => Expr::sin(Box::new(expr)),
}
}
Expr::cos(expr) => {
let expr = expr.simplify_();
match &expr {
Expr::Const(0.0) => Expr::Const(1.0),
_ => Expr::cos(Box::new(expr)),
}
} Expr::tg(expr) => {
let expr = expr.simplify_();
match &expr {
Expr::Const(0.0) => Expr::Const(0.0),
_ => Expr::tg(Box::new(expr)),
}
} Expr::ctg(expr) => {
let expr = expr.simplify_();
match &expr {
_ => Expr::ctg(Box::new(expr)),
}
}
Expr::arcsin(expr) => {
let expr = expr.simplify_();
match &expr {
Expr::Const(0.0) => Expr::Const(0.0),
_ => Expr::arcsin(Box::new(expr)),
}
} Expr::arccos(expr) => {
let expr = expr.simplify_();
match &expr {
Expr::Const(1.0) => Expr::Const(0.0),
_ => Expr::arccos(Box::new(expr)),
}
} Expr::arctg(expr) => {
let expr = expr.simplify_();
match &expr {
Expr::Const(0.0) => Expr::Const(0.0),
_ => Expr::arctg(Box::new(expr)),
}
}
Expr::arcctg(expr) => {
let expr = expr.simplify_();
match &expr {
_ => Expr::arcctg(Box::new(expr)),
}
} }
}
fn simplify_polynomial(expr: &Expr) -> Option<Expr> {
let mut terms = Vec::new();
flatten_add(expr, &mut terms);
if terms.len() < 2 {
return None;
}
let mut has_non_poly = false;
for term in &terms {
let (_, coeff) = extract_monomial(term);
if coeff == 0.0 && !matches!(term, Expr::Const(0.0)) {
has_non_poly = true;
break;
}
}
if has_non_poly {
return None;
}
let poly_map = collect_add_terms(&terms);
if poly_map.len() == terms.len() {
return None;
}
let mut result_terms = Vec::new();
for (monomial, coeff) in poly_map {
if coeff == 0.0 {
continue;
}
let term = Self::build_monomial_term(&monomial, coeff);
result_terms.push(term);
}
if result_terms.is_empty() {
Some(Expr::Const(0.0))
} else if result_terms.len() == 1 {
Some(result_terms.into_iter().next().unwrap())
} else {
Some(
result_terms
.into_iter()
.reduce(|a, b| Expr::Add(Box::new(a), Box::new(b)))
.unwrap(),
)
}
}
fn build_monomial_term(monomial: &MonomialKey, coeff: f64) -> Expr {
if monomial.0.is_empty() {
return Expr::Const(coeff);
}
let mut factors = Vec::new();
if coeff != 1.0 {
factors.push(Expr::Const(coeff));
}
for (var, exp) in &monomial.0 {
let var_expr = Expr::Var(var.clone());
if *exp == 1 {
factors.push(var_expr);
} else if *exp > 1 {
factors.push(Expr::Pow(
Box::new(var_expr),
Box::new(Expr::Const(*exp as f64)),
));
}
}
if factors.is_empty() {
Expr::Const(1.0)
} else if factors.len() == 1 {
factors.into_iter().next().unwrap()
} else {
factors
.into_iter()
.reduce(|a, b| Expr::Mul(Box::new(a), Box::new(b)))
.unwrap()
}
}
pub fn simplify(&self) -> Expr {
let zeros_proceeded = self.simplify_();
zeros_proceeded
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct MonomialKey(pub BTreeMap<String, i32>);
fn flatten_add(expr: &Expr, out: &mut Vec<Expr>) {
match expr {
Expr::Add(a, b) => {
flatten_add(a, out);
flatten_add(b, out);
}
Expr::Sub(a, b) => {
flatten_add(a, out);
let neg_b = Expr::Mul(Box::new(Expr::Const(-1.0)), b.clone());
flatten_add(&neg_b, out);
}
Expr::Mul(lhs, rhs) => {
if let Expr::Const(-1.0) = lhs.as_ref() {
match rhs.as_ref() {
Expr::Add(a, b) => {
let neg_a = Expr::Mul(Box::new(Expr::Const(-1.0)), a.clone());
let neg_b = Expr::Mul(Box::new(Expr::Const(-1.0)), b.clone());
flatten_add(&neg_a, out); flatten_add(&neg_b, out); }
_ => out.push(expr.clone()), }
} else if let Expr::Const(-1.0) = rhs.as_ref() {
match lhs.as_ref() {
Expr::Add(a, b) => {
let neg_a = Expr::Mul(Box::new(Expr::Const(-1.0)), a.clone());
let neg_b = Expr::Mul(Box::new(Expr::Const(-1.0)), b.clone());
flatten_add(&neg_a, out);
flatten_add(&neg_b, out);
}
_ => out.push(expr.clone()), }
} else {
out.push(expr.clone());
}
}
_ => out.push(expr.clone()),
}
}
fn flatten_mul(expr: &Expr, out: &mut Vec<Expr>) {
match expr {
Expr::Mul(a, b) => {
flatten_mul(a, out);
flatten_mul(b, out);
}
_ => out.push(expr.clone()),
}
}
fn collect_add_terms(terms: &[Expr]) -> HashMap<MonomialKey, f64> {
let mut poly = HashMap::new();
for t in terms {
let (mon, coeff) = extract_monomial(t);
*poly.entry(mon).or_insert(0.0) += coeff;
}
poly
}
fn extract_monomial(expr: &Expr) -> (MonomialKey, f64) {
match expr {
Expr::Const(c) => (MonomialKey(BTreeMap::new()), *c),
Expr::Var(v) => {
let mut m = BTreeMap::new();
m.insert(v.clone(), 1);
(MonomialKey(m), 1.0)
}
Expr::Mul(lhs, rhs) => {
match (lhs.as_ref(), rhs.as_ref()) {
(Expr::Const(-1.0), other) | (other, Expr::Const(-1.0)) => {
let (mon, coeff) = extract_monomial(other);
(mon, -coeff) }
(Expr::Const(c), other) | (other, Expr::Const(c)) => {
let (mon, coeff) = extract_monomial(other);
(mon, c * coeff) }
_ => {
let mut factors = Vec::new();
flatten_mul(expr, &mut factors);
let mut coeff = 1.0;
let mut map = BTreeMap::new();
let mut has_non_poly = false;
for f in factors {
match f {
Expr::Const(c) => coeff *= c,
Expr::Var(v) => *map.entry(v).or_insert(0) += 1,
Expr::Pow(base, exp) => {
if let Expr::Var(v) = *base {
if let Expr::Const(n) = *exp {
*map.entry(v).or_insert(0) += n as i32;
} else {
has_non_poly = true;
}
} else {
has_non_poly = true;
}
}
_ => has_non_poly = true,
}
}
if has_non_poly {
(MonomialKey(BTreeMap::new()), 0.0)
} else {
(MonomialKey(map), coeff)
}
}
}
}
Expr::Pow(base, exp) => {
if let (Expr::Var(v), Expr::Const(n)) = (base.as_ref(), exp.as_ref()) {
let mut m = BTreeMap::new();
m.insert(v.clone(), *n as i32);
(MonomialKey(m), 1.0)
} else {
(MonomialKey(BTreeMap::new()), 0.0)
}
}
_ => (MonomialKey(BTreeMap::new()), 0.0), }
}
#[allow(dead_code)]
fn horner_univariate(var: &str, terms: Vec<(i32, f64)>) -> Expr {
use Expr::*;
if terms.is_empty() {
return Const(0.0);
}
let max_deg = terms.iter().map(|(e, _)| *e).max().unwrap();
let mut coeffs = vec![0.0; (max_deg + 1) as usize];
for (exp, c) in terms {
if exp >= 0 {
coeffs[exp as usize] += c; }
}
let mut i = coeffs.len() - 1;
while i > 0 && coeffs[i] == 0.0 {
i -= 1;
}
let mut acc = Const(coeffs[i]);
let x = Var(var.to_string());
while i > 0 {
acc = Add(
Box::new(Mul(Box::new(acc), Box::new(x.clone()))),
Box::new(Const(coeffs[i - 1])),
);
i -= 1;
}
acc
}