#![allow(non_camel_case_types)]
use super::pretty_print::LeveledLayout;
use crate::symbolic::symbolic_metadata::TraversalCache;
use std::collections::HashMap;
use std::f64;
use std::fmt;
#[derive(Clone, Debug, PartialEq)]
pub enum Expr {
Var(String),
Const(f64),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
Pow(Box<Expr>, Box<Expr>),
Exp(Box<Expr>),
Ln(Box<Expr>),
sin(Box<Expr>),
cos(Box<Expr>),
tg(Box<Expr>),
ctg(Box<Expr>),
arcsin(Box<Expr>),
arccos(Box<Expr>),
arctg(Box<Expr>),
arcctg(Box<Expr>),
}
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Var(name) => write!(f, "{}", name),
Expr::Const(val) => write!(f, "{}", val),
Expr::Add(lhs, rhs) => {
self.fmt_left_operand(f, lhs)?;
write!(f, " + ")?;
self.fmt_right_operand(f, rhs)
}
Expr::Sub(lhs, rhs) => {
self.fmt_left_operand(f, lhs)?;
write!(f, " - ")?;
self.fmt_right_operand(f, rhs)
}
Expr::Mul(lhs, rhs) => {
self.fmt_left_operand(f, lhs)?;
write!(f, " * ")?;
self.fmt_right_operand(f, rhs)
}
Expr::Div(lhs, rhs) => {
self.fmt_left_operand(f, lhs)?;
write!(f, " / ")?;
self.fmt_right_operand(f, rhs)
}
Expr::Pow(base, exp) => {
self.fmt_left_operand(f, base)?;
write!(f, " ^ ")?;
self.fmt_right_operand(f, exp)
}
Expr::Exp(expr) => write!(f, "exp({})", expr),
Expr::Ln(expr) => write!(f, "ln({})", expr),
Expr::sin(expr) => write!(f, "sin({})", expr),
Expr::cos(expr) => write!(f, "cos({})", expr),
Expr::tg(expr) => write!(f, "tg({})", expr),
Expr::ctg(expr) => write!(f, "ctg({})", expr),
Expr::arcsin(expr) => write!(f, "arcsin({})", expr),
Expr::arccos(expr) => write!(f, "arccos({})", expr),
Expr::arctg(expr) => write!(f, "arctg({})", expr),
Expr::arcctg(expr) => write!(f, "arcctg({})", expr),
}
}
}
impl std::ops::Add for Expr {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Expr::Add(self.boxed(), rhs.boxed())
}
}
impl std::ops::Sub for Expr {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Expr::Sub(self.boxed(), rhs.boxed())
}
}
impl std::ops::Mul for Expr {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Expr::Mul(self.boxed(), rhs.boxed())
}
}
impl std::ops::Div for Expr {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Expr::Div(self.boxed(), rhs.boxed())
}
}
impl std::ops::AddAssign for Expr {
fn add_assign(&mut self, rhs: Self) {
*self = Expr::Add(Box::new(self.clone()), Box::new(rhs));
}
}
impl std::ops::SubAssign for Expr {
fn sub_assign(&mut self, rhs: Self) {
*self = Expr::Sub(Box::new(self.clone()), Box::new(rhs));
}
}
impl std::ops::MulAssign for Expr {
fn mul_assign(&mut self, rhs: Self) {
*self = Expr::Mul(Box::new(self.clone()), Box::new(rhs));
}
}
impl std::ops::DivAssign for Expr {
fn div_assign(&mut self, rhs: Self) {
*self = Expr::Div(Box::new(self.clone()), Box::new(rhs));
}
}
impl std::ops::Neg for Expr {
type Output = Self;
fn neg(self) -> Self::Output {
Expr::Mul(Box::new(Expr::Const(-1.0)), Box::new(self))
}
}
impl Expr {
fn precedence(&self) -> u8 {
match self {
Expr::Var(_) | Expr::Const(_) => 100, Expr::Exp(_)
| Expr::Ln(_)
| Expr::sin(_)
| Expr::cos(_)
| Expr::tg(_)
| Expr::ctg(_)
| Expr::arcsin(_)
| Expr::arccos(_)
| Expr::arctg(_)
| Expr::arcctg(_) => 90, Expr::Pow(_, _) => 80, Expr::Mul(_, _) | Expr::Div(_, _) => 60, Expr::Add(_, _) | Expr::Sub(_, _) => 40, }
}
fn needs_brackets_left(&self, operand: &Expr) -> bool {
operand.precedence() < self.precedence()
}
fn needs_brackets_right(&self, operand: &Expr) -> bool {
match self {
Expr::Sub(_, _) | Expr::Div(_, _) | Expr::Pow(_, _) => {
operand.precedence() <= self.precedence()
}
_ => operand.precedence() < self.precedence(),
}
}
fn fmt_left_operand(&self, f: &mut fmt::Formatter, operand: &Expr) -> fmt::Result {
if self.needs_brackets_left(operand) {
write!(f, "({})", operand)
} else {
write!(f, "{}", operand)
}
}
fn fmt_right_operand(&self, f: &mut fmt::Formatter, operand: &Expr) -> fmt::Result {
if self.needs_brackets_right(operand) {
write!(f, "({})", operand)
} else {
write!(f, "{}", operand)
}
}
fn _format_function_horizontal(func_name: &str, inner_lines: &[String]) -> Vec<String> {
if inner_lines.len() <= 1 {
return vec![format!(
"{}({})",
func_name,
inner_lines.get(0).unwrap_or(&String::new())
)];
}
let mut result = Vec::new();
result.push(format!("{}( {} )", func_name, inner_lines[0]));
let content_offset = func_name.len() + 2; for i in 1..inner_lines.len() {
result.push(format!("{}{}", " ".repeat(content_offset), inner_lines[i]));
}
result
}
fn to_superscript_digit(digit: u8) -> char {
match digit {
0 => '⁰',
1 => '¹',
2 => '²',
3 => '³',
4 => '⁴',
5 => '⁵',
6 => '⁶',
7 => '⁷',
8 => '⁸',
9 => '⁹',
_ => '?',
}
}
fn to_superscript_char(c: char) -> Option<char> {
match c {
'a' => Some('ᵃ'),
'b' => Some('ᵇ'),
'c' => Some('ᶜ'),
'd' => Some('ᵈ'),
'e' => Some('ᵉ'),
'f' => Some('ᶠ'),
'g' => Some('ᵍ'),
'h' => Some('ʰ'),
'i' => Some('ⁱ'),
'j' => Some('ʲ'),
'k' => Some('ᵏ'),
'l' => Some('ˡ'),
'm' => Some('ᵐ'),
'n' => Some('ⁿ'),
'o' => Some('ᵒ'),
'p' => Some('ᵖ'),
'r' => Some('ʳ'),
's' => Some('ˢ'),
't' => Some('ᵗ'),
'u' => Some('ᵘ'),
'v' => Some('ᵛ'),
'w' => Some('ʷ'),
'x' => Some('ˣ'),
'y' => Some('ʸ'),
'z' => Some('ᶻ'),
'+' => Some('⁺'),
'-' => Some('⁻'),
'=' => Some('⁼'),
'(' => Some('⁽'),
')' => Some('⁾'),
_ => None,
}
}
fn can_use_unicode_superscript(&self) -> bool {
match self {
Expr::Const(n) => *n >= 0.0 && *n <= 9.0 && n.fract() == 0.0,
Expr::Var(name) => {
name.len() == 1 && Self::to_superscript_char(name.chars().next().unwrap()).is_some()
}
_ => false,
}
}
fn to_unicode_superscript(&self) -> String {
match self {
Expr::Const(n) => {
let digit = *n as u8;
Self::to_superscript_digit(digit).to_string()
}
Expr::Var(name) => {
let c = name.chars().next().unwrap();
Self::to_superscript_char(c).unwrap().to_string()
}
_ => format!("^({})", self),
}
}
fn convert_greek_letters(&self) -> Expr {
match self {
Expr::Var(name) => {
let lower_name = name.to_lowercase();
for (greek_name, greek_symbol) in [
("alpha", "α"),
("beta", "β"),
("gamma", "γ"),
("delta", "δ"),
("epsilon", "ε"),
("zeta", "ζ"),
("eta", "η"),
("theta", "θ"),
("iota", "ι"),
("kappa", "κ"),
("lambda", "λ"),
("mu", "μ"),
("nu", "ν"),
("xi", "ξ"),
("omicron", "ο"),
("pi", "π"),
("rho", "ρ"),
("sigma", "σ"),
("tau", "τ"),
("upsilon", "υ"),
("phi", "φ"),
("chi", "χ"),
("psi", "ψ"),
("omega", "ω"),
] {
if lower_name.starts_with(greek_name) {
let suffix = &lower_name[greek_name.len()..];
return Expr::Var(format!("{}{}", greek_symbol, suffix));
}
}
self.clone()
}
Expr::Add(lhs, rhs) => Expr::Add(
Box::new(lhs.convert_greek_letters()),
Box::new(rhs.convert_greek_letters()),
),
Expr::Sub(lhs, rhs) => Expr::Sub(
Box::new(lhs.convert_greek_letters()),
Box::new(rhs.convert_greek_letters()),
),
Expr::Mul(lhs, rhs) => Expr::Mul(
Box::new(lhs.convert_greek_letters()),
Box::new(rhs.convert_greek_letters()),
),
Expr::Div(lhs, rhs) => Expr::Div(
Box::new(lhs.convert_greek_letters()),
Box::new(rhs.convert_greek_letters()),
),
Expr::Pow(base, exp) => Expr::Pow(
Box::new(base.convert_greek_letters()),
Box::new(exp.convert_greek_letters()),
),
Expr::Exp(expr) => Expr::Exp(Box::new(expr.convert_greek_letters())),
Expr::Ln(expr) => Expr::Ln(Box::new(expr.convert_greek_letters())),
Expr::sin(expr) => Expr::sin(Box::new(expr.convert_greek_letters())),
Expr::cos(expr) => Expr::cos(Box::new(expr.convert_greek_letters())),
Expr::tg(expr) => Expr::tg(Box::new(expr.convert_greek_letters())),
Expr::ctg(expr) => Expr::ctg(Box::new(expr.convert_greek_letters())),
Expr::arcsin(expr) => Expr::arcsin(Box::new(expr.convert_greek_letters())),
Expr::arccos(expr) => Expr::arccos(Box::new(expr.convert_greek_letters())),
Expr::arctg(expr) => Expr::arctg(Box::new(expr.convert_greek_letters())),
Expr::arcctg(expr) => Expr::arcctg(Box::new(expr.convert_greek_letters())),
_ => self.clone(),
}
}
pub fn pretty_print(&self) -> String {
self.convert_greek_letters()
.pretty_print_internal()
.join("\n")
}
#[allow(dead_code)]
fn visual_width(&self) -> usize {
match self {
Expr::Var(name) => name.chars().count(),
Expr::Const(val) => format!("{}", val).chars().count(),
Expr::Div(num, den) => {
let num_width = num.visual_width();
let den_width = den.visual_width();
num_width.max(den_width).max(3)
}
Expr::Pow(base, exp) => {
if exp.can_use_unicode_superscript() {
base.visual_width() + exp.to_unicode_superscript().chars().count()
} else {
base.visual_width().max(exp.visual_width())
}
}
_ => {
match self {
Expr::Add(lhs, rhs) | Expr::Sub(lhs, rhs) | Expr::Mul(lhs, rhs) => {
lhs.visual_width() + 3 + rhs.visual_width() }
Expr::Exp(_)
| Expr::Ln(_)
| Expr::sin(_)
| Expr::cos(_)
| Expr::tg(_)
| Expr::ctg(_)
| Expr::arcsin(_)
| Expr::arccos(_)
| Expr::arctg(_)
| Expr::arcctg(_) => {
let func_name = match self {
Expr::Exp(_) => "exp",
Expr::Ln(_) => "ln",
Expr::sin(_) => "sin",
Expr::cos(_) => "cos",
Expr::tg(_) => "tg",
Expr::ctg(_) => "ctg",
Expr::arcsin(_) => "arcsin",
Expr::arccos(_) => "arccos",
Expr::arctg(_) => "arctg",
Expr::arcctg(_) => "arcctg",
_ => "",
};
let inner = match self {
Expr::Exp(e)
| Expr::Ln(e)
| Expr::sin(e)
| Expr::cos(e)
| Expr::tg(e)
| Expr::ctg(e)
| Expr::arcsin(e)
| Expr::arccos(e)
| Expr::arctg(e)
| Expr::arcctg(e) => e.visual_width(),
_ => 0,
};
func_name.chars().count() + 2 + inner }
_ => 10, }
}
}
}
fn pretty_print_internal(&self) -> Vec<String> {
let layout = self.analyze_levels_with_brackets(0, false);
layout.render()
}
fn analyze_levels_with_brackets(
&self,
current_level: i32,
force_brackets: bool,
) -> LeveledLayout {
let mut layout = LeveledLayout::new();
layout.baseline = current_level;
match self {
Expr::Var(name) => {
let structure_id =
layout.create_structure_context(vec!["var".to_string()], None, 0);
layout.add_element_with_context(name.clone(), current_level, structure_id);
}
Expr::Const(val) => {
let structure_id =
layout.create_structure_context(vec!["const".to_string()], None, 0);
layout.add_element_with_context(format!("{}", val), current_level, structure_id);
}
Expr::Add(lhs, rhs) => {
let left_needs_brackets = self.needs_brackets_left(lhs);
let right_needs_brackets = self.needs_brackets_right(rhs);
let mut left_layout = lhs.analyze_levels_with_brackets(current_level, false);
let right_layout = rhs.analyze_levels_with_brackets(current_level, false);
if left_needs_brackets {
left_layout.add_brackets();
}
layout = left_layout;
layout.merge_horizontal(right_layout, "+", current_level, right_needs_brackets);
if force_brackets {
layout.add_brackets();
}
}
Expr::Sub(lhs, rhs) => {
let left_needs_brackets = self.needs_brackets_left(lhs);
let right_needs_brackets = self.needs_brackets_right(rhs);
let mut left_layout = lhs.analyze_levels_with_brackets(current_level, false);
let right_layout = rhs.analyze_levels_with_brackets(current_level, false);
if left_needs_brackets {
left_layout.add_brackets();
}
layout = left_layout;
layout.merge_horizontal(right_layout, "-", current_level, right_needs_brackets);
if force_brackets {
layout.add_brackets();
}
}
Expr::Mul(lhs, rhs) => {
let left_needs_brackets = self.needs_brackets_left(lhs);
let right_needs_brackets = self.needs_brackets_right(rhs);
let mut left_layout = lhs.analyze_levels_with_brackets(current_level, false);
let right_layout = rhs.analyze_levels_with_brackets(current_level, false);
if left_needs_brackets {
left_layout.add_brackets();
}
layout = left_layout;
layout.merge_horizontal(right_layout, "*", current_level, right_needs_brackets);
if force_brackets {
layout.add_brackets();
}
}
Expr::Div(num, den) => {
let depth = self.calculate_nesting_depth();
let level_offset = if depth > 3 { depth as i32 } else { 1 };
let num_layout =
num.analyze_levels_with_brackets(current_level + level_offset, false);
let den_layout =
den.analyze_levels_with_brackets(current_level - level_offset, false);
layout.merge_vertical(num_layout, den_layout, current_level);
}
Expr::Pow(base, exp) => {
if exp.can_use_unicode_superscript() {
let base_needs_brackets = self.needs_brackets_left(base);
let mut base_layout = base.analyze_levels_with_brackets(current_level, false);
if base_needs_brackets {
base_layout.add_brackets();
}
layout = base_layout;
let structure_id =
layout.create_structure_context(vec!["superscript".to_string()], None, 0);
layout.add_element_with_context(
exp.to_unicode_superscript(),
current_level,
structure_id,
);
} else {
let base_needs_brackets = self.needs_brackets_left(base);
let mut base_layout = base.analyze_levels_with_brackets(current_level, false);
if base_needs_brackets {
base_layout.add_brackets();
}
let exp_layout = exp.analyze_levels_with_brackets(0, false);
layout = base_layout;
layout.merge_power_exponent(exp_layout);
}
if force_brackets {
layout.add_brackets();
}
}
Expr::Exp(expr) => {
if expr.can_use_unicode_superscript() {
let structure_id =
layout.create_structure_context(vec!["exp".to_string()], None, 0);
layout.add_element_with_context(
format!("e{}", expr.to_unicode_superscript()),
current_level,
structure_id,
);
} else {
let mut base_layout = LeveledLayout::new();
base_layout.baseline = current_level;
let structure_id =
base_layout.create_structure_context(vec!["exp_base".to_string()], None, 0);
base_layout.add_element_with_context(
"e".to_string(),
current_level,
structure_id,
);
let exp_layout = expr.analyze_levels_with_brackets(0, false);
layout = base_layout;
layout.merge_power_exponent(exp_layout);
}
}
_ => {
let func_name = match self {
Expr::Ln(_) => "ln",
Expr::sin(_) => "sin",
Expr::cos(_) => "cos",
Expr::tg(_) => "tg",
Expr::ctg(_) => "ctg",
Expr::arcsin(_) => "arcsin",
Expr::arccos(_) => "arccos",
Expr::arctg(_) => "arctg",
Expr::arcctg(_) => "arcctg",
_ => "func",
};
let inner_expr = match self {
Expr::Ln(e)
| Expr::sin(e)
| Expr::cos(e)
| Expr::tg(e)
| Expr::ctg(e)
| Expr::arcsin(e)
| Expr::arccos(e)
| Expr::arctg(e)
| Expr::arcctg(e) => e,
_ => return layout,
};
let inner_layout = inner_expr.analyze_levels_with_brackets(current_level, false);
let has_multiple_levels = inner_layout.level_heights.len() > 1;
if has_multiple_levels {
layout.merge_function_with_multiline(func_name, inner_layout);
} else {
let structure_id = layout.create_structure_context(
vec!["func".to_string(), func_name.to_string()],
None,
0,
);
layout.add_element_with_context(
format!("{}(", func_name),
current_level,
structure_id,
);
layout.merge_inline(inner_layout);
layout.add_element_with_context(")".to_string(), current_level, structure_id);
}
}
}
layout
}
fn calculate_nesting_depth(&self) -> usize {
match self {
Expr::Var(_) | Expr::Const(_) => 0,
Expr::Add(lhs, rhs) | Expr::Sub(lhs, rhs) | Expr::Mul(lhs, rhs) => {
1 + lhs
.calculate_nesting_depth()
.max(rhs.calculate_nesting_depth())
}
Expr::Div(num, den) => {
2 + num
.calculate_nesting_depth()
.max(den.calculate_nesting_depth())
}
Expr::Pow(base, exp) => {
2 + base
.calculate_nesting_depth()
.max(exp.calculate_nesting_depth())
}
Expr::Exp(e)
| Expr::Ln(e)
| Expr::sin(e)
| Expr::cos(e)
| Expr::tg(e)
| Expr::ctg(e)
| Expr::arcsin(e)
| Expr::arccos(e)
| Expr::arctg(e)
| Expr::arcctg(e) => 1 + e.calculate_nesting_depth(),
}
}
fn _needs_brackets_continued(&self, operand: &Expr) -> bool {
operand.precedence() < self.precedence()
}
pub fn Symbols(symbols: &str) -> Vec<Expr> {
let symbols = symbols.to_string();
let vec_trimmed: Vec<String> = symbols.split(',').map(|s| s.trim().to_string()).collect();
let vector_of_symbolic_vars: Vec<Expr> = vec_trimmed
.iter()
.filter(|s| !s.is_empty())
.map(|s| Expr::Var(s.to_string()))
.collect();
vector_of_symbolic_vars
}
pub fn set_variable(&self, var: &str, value: f64) -> Expr {
match self {
Expr::Var(name) if name == var => Expr::Const(value),
Expr::Add(lhs, rhs) => Expr::Add(
Box::new(lhs.set_variable(var, value)),
Box::new(rhs.set_variable(var, value)),
),
Expr::Sub(lhs, rhs) => Expr::Sub(
Box::new(lhs.set_variable(var, value)),
Box::new(rhs.set_variable(var, value)),
),
Expr::Mul(lhs, rhs) => Expr::Mul(
Box::new(lhs.set_variable(var, value)),
Box::new(rhs.set_variable(var, value)),
),
Expr::Div(lhs, rhs) => Expr::Div(
Box::new(lhs.set_variable(var, value)),
Box::new(rhs.set_variable(var, value)),
),
Expr::Pow(base, exp) => Expr::Pow(
Box::new(base.set_variable(var, value)),
Box::new(exp.set_variable(var, value)),
),
Expr::Exp(expr) => Expr::Exp(Box::new(expr.set_variable(var, value))),
Expr::Ln(expr) => Expr::Ln(Box::new(expr.set_variable(var, value))),
Expr::sin(expr) => Expr::sin(Box::new(expr.set_variable(var, value))),
Expr::cos(expr) => Expr::cos(Box::new(expr.set_variable(var, value))),
Expr::tg(expr) => Expr::tg(Box::new(expr.set_variable(var, value))),
Expr::ctg(expr) => Expr::ctg(Box::new(expr.set_variable(var, value))),
Expr::arcsin(expr) => Expr::arcsin(Box::new(expr.set_variable(var, value))),
Expr::arccos(expr) => Expr::arccos(Box::new(expr.set_variable(var, value))),
Expr::arctg(expr) => Expr::arctg(Box::new(expr.set_variable(var, value))),
Expr::arcctg(expr) => Expr::arcctg(Box::new(expr.set_variable(var, value))),
_ => self.clone(),
}
}
pub fn set_variable_from_map(&self, var_map: &HashMap<String, f64>) -> Expr {
match self {
Expr::Var(name) if var_map.contains_key(name) => Expr::Const(var_map[name]),
Expr::Add(lhs, rhs) => Expr::Add(
Box::new(lhs.set_variable_from_map(var_map)),
Box::new(rhs.set_variable_from_map(var_map)),
),
Expr::Sub(lhs, rhs) => Expr::Sub(
Box::new(lhs.set_variable_from_map(var_map)),
Box::new(rhs.set_variable_from_map(var_map)),
),
Expr::Mul(lhs, rhs) => Expr::Mul(
Box::new(lhs.set_variable_from_map(var_map)),
Box::new(rhs.set_variable_from_map(var_map)),
),
Expr::Div(lhs, rhs) => Expr::Div(
Box::new(lhs.set_variable_from_map(var_map)),
Box::new(rhs.set_variable_from_map(var_map)),
),
Expr::Pow(base, exp) => Expr::Pow(
Box::new(base.set_variable_from_map(var_map)),
Box::new(exp.set_variable_from_map(var_map)),
),
Expr::Exp(expr) => Expr::Exp(Box::new(expr.set_variable_from_map(var_map))),
Expr::Ln(expr) => Expr::Ln(Box::new(expr.set_variable_from_map(var_map))),
Expr::sin(expr) => Expr::sin(Box::new(expr.set_variable_from_map(var_map))),
Expr::cos(expr) => Expr::cos(Box::new(expr.set_variable_from_map(var_map))),
Expr::tg(expr) => Expr::tg(Box::new(expr.set_variable_from_map(var_map))),
Expr::ctg(expr) => Expr::ctg(Box::new(expr.set_variable_from_map(var_map))),
Expr::arcsin(expr) => Expr::arcsin(Box::new(expr.set_variable_from_map(var_map))),
Expr::arccos(expr) => Expr::arccos(Box::new(expr.set_variable_from_map(var_map))),
Expr::arctg(expr) => Expr::arctg(Box::new(expr.set_variable_from_map(var_map))),
Expr::arcctg(expr) => Expr::arcctg(Box::new(expr.set_variable_from_map(var_map))),
_ => self.clone(),
}
}
pub fn rename_variable(&self, old_var: &str, new_var: &str) -> Expr {
match self {
Expr::Var(name) if name == old_var => Expr::Var(new_var.to_string()),
Expr::Add(lhs, rhs) => Expr::Add(
Box::new(lhs.rename_variable(old_var, new_var)),
Box::new(rhs.rename_variable(old_var, new_var)),
),
Expr::Sub(lhs, rhs) => Expr::Sub(
Box::new(lhs.rename_variable(old_var, new_var)),
Box::new(rhs.rename_variable(old_var, new_var)),
),
Expr::Mul(lhs, rhs) => Expr::Mul(
Box::new(lhs.rename_variable(old_var, new_var)),
Box::new(rhs.rename_variable(old_var, new_var)),
),
Expr::Div(lhs, rhs) => Expr::Div(
Box::new(lhs.rename_variable(old_var, new_var)),
Box::new(rhs.rename_variable(old_var, new_var)),
),
Expr::Pow(base, exp) => Expr::Pow(
Box::new(base.rename_variable(old_var, new_var)),
Box::new(exp.rename_variable(old_var, new_var)),
),
Expr::Exp(expr) => Expr::Exp(Box::new(expr.rename_variable(old_var, new_var))),
Expr::Ln(expr) => Expr::Ln(Box::new(expr.rename_variable(old_var, new_var))),
Expr::sin(expr) => Expr::sin(Box::new(expr.rename_variable(old_var, new_var))),
Expr::cos(expr) => Expr::cos(Box::new(expr.rename_variable(old_var, new_var))),
Expr::tg(expr) => Expr::tg(Box::new(expr.rename_variable(old_var, new_var))),
Expr::ctg(expr) => Expr::ctg(Box::new(expr.rename_variable(old_var, new_var))),
Expr::arcsin(expr) => Expr::arcsin(Box::new(expr.rename_variable(old_var, new_var))),
Expr::arccos(expr) => Expr::arccos(Box::new(expr.rename_variable(old_var, new_var))),
Expr::arctg(expr) => Expr::arctg(Box::new(expr.rename_variable(old_var, new_var))),
Expr::arcctg(expr) => Expr::arcctg(Box::new(expr.rename_variable(old_var, new_var))),
_ => self.clone(),
}
}
pub fn rename_variables(&self, var_map: &HashMap<String, String>) -> Expr {
match self {
Expr::Var(name) if var_map.contains_key(name) => Expr::Var(var_map[name].to_string()),
Expr::Add(lhs, rhs) => Expr::Add(
Box::new(lhs.rename_variables(var_map)),
Box::new(rhs.rename_variables(var_map)),
),
Expr::Sub(lhs, rhs) => Expr::Sub(
Box::new(lhs.rename_variables(var_map)),
Box::new(rhs.rename_variables(var_map)),
),
Expr::Mul(lhs, rhs) => Expr::Mul(
Box::new(lhs.rename_variables(var_map)),
Box::new(rhs.rename_variables(var_map)),
),
Expr::Div(lhs, rhs) => Expr::Div(
Box::new(lhs.rename_variables(var_map)),
Box::new(rhs.rename_variables(var_map)),
),
Expr::Pow(base, exp) => Expr::Pow(
Box::new(base.rename_variables(var_map)),
Box::new(exp.rename_variables(var_map)),
),
Expr::Exp(expr) => Expr::Exp(Box::new(expr.rename_variables(var_map))),
Expr::Ln(expr) => Expr::Ln(Box::new(expr.rename_variables(var_map))),
Expr::sin(expr) => Expr::sin(Box::new(expr.rename_variables(var_map))),
Expr::cos(expr) => Expr::cos(Box::new(expr.rename_variables(var_map))),
Expr::tg(expr) => Expr::tg(Box::new(expr.rename_variables(var_map))),
Expr::ctg(expr) => Expr::ctg(Box::new(expr.rename_variables(var_map))),
Expr::arcsin(expr) => Expr::arcsin(Box::new(expr.rename_variables(var_map))),
Expr::arccos(expr) => Expr::arccos(Box::new(expr.rename_variables(var_map))),
Expr::arctg(expr) => Expr::arctg(Box::new(expr.rename_variables(var_map))),
_ => self.clone(),
}
}
pub fn substitute_variable(&self, var: &str, expr: &Expr) -> Expr {
match self {
Expr::Var(name) if name == var => expr.clone(),
Expr::Add(lhs, rhs) => Expr::Add(
Box::new(lhs.substitute_variable(var, expr)),
Box::new(rhs.substitute_variable(var, expr)),
),
Expr::Sub(lhs, rhs) => Expr::Sub(
Box::new(lhs.substitute_variable(var, expr)),
Box::new(rhs.substitute_variable(var, expr)),
),
Expr::Mul(lhs, rhs) => Expr::Mul(
Box::new(lhs.substitute_variable(var, expr)),
Box::new(rhs.substitute_variable(var, expr)),
),
Expr::Div(lhs, rhs) => Expr::Div(
Box::new(lhs.substitute_variable(var, expr)),
Box::new(rhs.substitute_variable(var, expr)),
),
Expr::Pow(base, exp) => Expr::Pow(
Box::new(base.substitute_variable(var, expr)),
Box::new(exp.substitute_variable(var, expr)),
),
Expr::Exp(expr_old) => Expr::Exp(Box::new(expr_old.substitute_variable(var, expr))),
Expr::Ln(expr_old) => Expr::Ln(Box::new(expr_old.substitute_variable(var, expr))),
Expr::sin(expr) => Expr::sin(Box::new(expr.substitute_variable(var, expr))),
Expr::cos(expr) => Expr::cos(Box::new(expr.substitute_variable(var, expr))),
Expr::tg(expr) => Expr::tg(Box::new(expr.substitute_variable(var, expr))),
Expr::ctg(expr) => Expr::ctg(Box::new(expr.substitute_variable(var, expr))),
Expr::arcsin(expr) => Expr::arcsin(Box::new(expr.substitute_variable(var, expr))),
Expr::arccos(expr) => Expr::arccos(Box::new(expr.substitute_variable(var, expr))),
Expr::arctg(expr) => Expr::arctg(Box::new(expr.substitute_variable(var, expr))),
Expr::arcctg(expr) => Expr::arcctg(Box::new(expr.substitute_variable(var, expr))),
_ => self.clone(),
}
}
pub fn contains_variable(&self, var_name: &str) -> bool {
let mut cache = TraversalCache::new();
cache.contains_variable(self, var_name)
}
pub fn boxed(self) -> Box<Self> {
Box::new(self)
}
pub fn var_expr(&mut self, var: &str) -> Expr {
let expr = Expr::Var(var.to_string());
*self = expr.clone();
expr
}
pub fn const_expr(&mut self, val: f64) {
*self = Expr::Const(val);
}
pub fn exp(mut self) -> Expr {
self = Expr::Exp(self.boxed());
self
}
pub fn ln(mut self) -> Expr {
self = Expr::Ln(self.boxed());
self
}
pub fn log10(mut self) -> Expr {
self = Expr::Ln(self.boxed()) / Expr::Const(2.30258509);
self
}
pub fn pow(mut self, rhs: Expr) -> Expr {
self = Expr::Pow(self.boxed(), rhs.boxed());
self
}
pub fn is_zero(&self) -> bool {
match self {
Expr::Const(val) => val == &0.0,
_ => false,
}
}
pub fn IndexedVar(index: usize, var_name: &str) -> Expr {
let indexed_var_name = format!("{}{}", var_name, index);
Expr::Var(indexed_var_name)
}
pub fn IndexedVars(num_vars: usize, var_name: &str) -> (Vec<Expr>, Vec<String>) {
let vec_of_expr = (0..num_vars)
.map(|i| Expr::IndexedVar(i, var_name))
.collect();
let vec_of_names = (0..num_vars)
.map(|i| format!("{}_{}", var_name, i))
.collect();
(vec_of_expr, vec_of_names)
}
pub fn IndexedVarsMatrix(
num_vars: usize,
var_names: Vec<String>,
) -> (Vec<Vec<Expr>>, Vec<Vec<String>>) {
let mut matrix = Vec::new();
let mut matrix_of_expr = Vec::new();
for i in 0..num_vars {
let mut matrix_i = Vec::new();
let mut matrix_of_expr_i = Vec::new();
for j in 0..var_names.len() {
let indexed_var_name = format!("{}_{}", var_names[j], i);
matrix_i.push(indexed_var_name.clone());
matrix_of_expr_i.push(Expr::Var(indexed_var_name));
}
matrix.push(matrix_i);
matrix_of_expr.push(matrix_of_expr_i);
}
(matrix_of_expr, matrix)
}
pub fn IndexedVar2D(index_row: usize, index_col: usize, var_name: &str) -> Expr {
let indexed_var_name = format!("{}_{}_{}", var_name, index_row, index_col);
Expr::Var(indexed_var_name)
}
pub fn IndexedVars2D(
num_rows: usize,
num_cols: usize,
var_name: &str,
) -> (Vec<Vec<Expr>>, Vec<String>) {
let mut vec_of_names: Vec<String> = Vec::new();
let matrix = (0..num_rows)
.map(|i| {
(0..num_cols)
.map(|j| {
let indexed_var_name = format!("{}_{}_{}", var_name, i, j);
Expr::Var(indexed_var_name)
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
for i in 0..num_rows {
for j in 0..num_cols {
let indexed_var_name = format!("{}_{}_{}", var_name, i, j);
vec_of_names.push(indexed_var_name);
}
}
(matrix, vec_of_names)
}
pub fn IndexedVars2Dflat(num_rows: usize, num_cols: usize, var_name: &str) -> Vec<Expr> {
(0..num_rows)
.flat_map(|i| (0..num_cols).map(move |j| Expr::IndexedVar2D(i, j, var_name)))
.collect()
}
pub fn polyval(degree: usize, var_name: &str) -> (Expr, Vec<String>) {
let mut eq: Expr = Self::Const(0.0);
let mut unknowns = Vec::new();
let arg_expr = Self::Var(var_name.to_string());
for i in 0..degree + 1 {
let coeff = Self::Var(format!("c{}", i));
unknowns.push(format!("c{}", i));
eq = eq + coeff * (arg_expr.clone().pow(Self::Const(i as f64))).simplify_();
}
(eq, unknowns)
}
}
#[macro_export]
macro_rules! symbols {
($($var:ident),+ $(,)?) => {
{
let var_names = stringify!($($var),+);
let vars = Expr::Symbols(var_names);
let mut iter = vars.into_iter();
($(
{
let $var = iter.next().unwrap();
$var
}
),+)
}
};
}
#[macro_export]
macro_rules! indexed_vars {
($count:expr, $name:expr) => {
Expr::IndexedVars($count, $name)
};
}
#[macro_export]
macro_rules! indexed_vars_2d {
($rows:expr, $cols:expr, $name:expr) => {
Expr::IndexedVars2D($rows, $cols, $name)
};
}
#[macro_export]
macro_rules! indexed_var {
($index:expr, $name:expr) => {
Expr::IndexedVar($index, $name)
};
}
#[macro_export]
macro_rules! indexed_var_2d {
($row:expr, $col:expr, $name:expr) => {
Expr::IndexedVar2D($row, $col, $name)
};
}