#![allow(non_camel_case_types)]
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 {
match self {
Expr::Var(name) => name == var_name,
Expr::Const(_) => false,
Expr::Add(left, right)
| Expr::Sub(left, right)
| Expr::Mul(left, right)
| Expr::Div(left, right) => {
left.contains_variable(var_name) || right.contains_variable(var_name)
}
Expr::Pow(base, exp) => {
base.contains_variable(var_name) || exp.contains_variable(var_name)
}
Expr::Exp(expr) => expr.contains_variable(var_name),
Expr::Ln(expr) => expr.contains_variable(var_name),
Expr::sin(expr) => expr.contains_variable(var_name),
Expr::cos(expr) => expr.contains_variable(var_name),
Expr::tg(expr) => expr.contains_variable(var_name),
Expr::ctg(expr) => expr.contains_variable(var_name),
Expr::arcsin(expr) => expr.contains_variable(var_name),
Expr::arccos(expr) => expr.contains_variable(var_name),
Expr::arctg(expr) => expr.contains_variable(var_name),
Expr::arcctg(expr) => expr.contains_variable(var_name),
_ => false,
}
}
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)
}
}
#[derive(Debug, Clone)]
struct StructuralContext {
parent_id: Option<usize>,
}
#[derive(Debug, Clone)]
struct LayoutElement {
content: String,
level: i32,
position: usize,
structure_id: usize,
}
#[derive(Debug, Clone)]
struct LeveledLayout {
elements: Vec<LayoutElement>,
baseline: i32,
total_width: usize,
level_heights: std::collections::HashMap<i32, usize>,
contexts: std::collections::HashMap<usize, StructuralContext>,
next_structure_id: usize,
}
impl LeveledLayout {
fn new() -> Self {
Self {
elements: Vec::new(),
baseline: 0,
total_width: 0,
level_heights: std::collections::HashMap::new(),
contexts: std::collections::HashMap::new(),
next_structure_id: 0,
}
}
fn create_structure_context(
&mut self,
_path: Vec<String>,
parent_id: Option<usize>,
_depth: usize,
) -> usize {
let structure_id = self.next_structure_id;
self.next_structure_id += 1;
let context = StructuralContext { parent_id };
self.contexts.insert(structure_id, context.clone());
structure_id
}
fn add_element_with_context(&mut self, content: String, level: i32, structure_id: usize) {
let position = self.total_width;
self.elements.push(LayoutElement {
content: content.clone(),
level,
position,
structure_id,
});
self.total_width += content.chars().count();
*self.level_heights.entry(level).or_insert(0) =
(*self.level_heights.get(&level).unwrap_or(&0)).max(1);
}
fn add_brackets(&mut self) {
let bracket_structure_id =
self.create_structure_context(vec!["bracket".to_string()], None, 0);
for element in &mut self.elements {
element.position += 1;
}
self.elements.insert(
0,
LayoutElement {
content: "(".to_string(),
level: self.baseline,
position: 0,
structure_id: bracket_structure_id,
},
);
self.elements.push(LayoutElement {
content: ")".to_string(),
level: self.baseline,
position: self.total_width + 1,
structure_id: bracket_structure_id,
});
self.total_width += 2;
}
fn merge_horizontal(
&mut self,
mut other: LeveledLayout,
operator: &str,
op_level: i32,
right_needs_brackets: bool,
) {
if right_needs_brackets {
other.add_brackets();
}
let current_width = self.total_width;
let op_width = operator.len() + 2;
let op_structure_id = self.create_structure_context(
vec!["operator".to_string(), operator.to_string()],
None,
0,
);
self.elements.push(LayoutElement {
content: format!(" {} ", operator),
level: op_level,
position: current_width,
structure_id: op_structure_id,
});
for (id, context) in other.contexts {
self.contexts.insert(id, context);
}
for mut element in other.elements {
element.position += current_width + op_width;
self.elements.push(element);
}
self.total_width += op_width + other.total_width;
for (level, height) in other.level_heights {
*self.level_heights.entry(level).or_insert(0) =
*self.level_heights.get(&level).unwrap_or(&0).max(&height);
}
}
fn merge_vertical(
&mut self,
num_layout: LeveledLayout,
den_layout: LeveledLayout,
division_level: i32,
) {
let max_width = num_layout.total_width.max(den_layout.total_width).max(3);
let div_structure_id = self.create_structure_context(vec!["division".to_string()], None, 1);
self.elements.clear();
for (id, context) in num_layout.contexts {
self.contexts.insert(id, context);
}
for (id, context) in den_layout.contexts {
self.contexts.insert(id, context);
}
let num_offset = if num_layout.total_width < max_width {
(max_width - num_layout.total_width) / 2
} else {
0
};
let den_offset = if den_layout.total_width < max_width {
(max_width - den_layout.total_width) / 2
} else {
0
};
for mut element in num_layout.elements {
element.position = num_offset + element.position;
self.elements.push(element);
}
self.elements.push(LayoutElement {
content: "─".repeat(max_width),
level: division_level,
position: 0,
structure_id: div_structure_id,
});
for mut element in den_layout.elements {
element.position = den_offset + element.position;
self.elements.push(element);
}
self.level_heights.clear();
for (&level, &height) in &num_layout.level_heights {
*self.level_heights.entry(level).or_insert(0) = height;
}
for (&level, &height) in &den_layout.level_heights {
*self.level_heights.entry(level).or_insert(0) = height;
}
*self.level_heights.entry(division_level).or_insert(0) = 1;
self.total_width = max_width;
self.baseline = division_level;
}
#[allow(dead_code)]
fn _merge_exponent(&mut self, exp_layout: LeveledLayout) {
self.elements.extend(exp_layout.elements);
for (level, height) in exp_layout.level_heights {
*self.level_heights.entry(level).or_insert(0) =
*self.level_heights.get(&level).unwrap_or(&0).max(&height);
}
self.total_width += exp_layout.total_width;
}
fn merge_power_exponent(&mut self, exp_layout: LeveledLayout) {
let base_width = self.total_width;
let base_baseline = self.baseline;
let _power_structure_id = self.create_structure_context(
vec!["power".to_string(), "exponent".to_string()],
None,
2,
);
for (id, context) in exp_layout.contexts {
self.contexts.insert(id, context);
}
let exp_min_level = *exp_layout.level_heights.keys().min().unwrap_or(&0);
let level_offset = (base_baseline + 1) - exp_min_level;
for mut element in exp_layout.elements {
element.level += level_offset;
element.position += base_width;
self.elements.push(element);
}
for (level, height) in exp_layout.level_heights {
*self.level_heights.entry(level + level_offset).or_insert(0) = height;
}
self.total_width = base_width + exp_layout.total_width;
}
fn merge_inline(&mut self, other: LeveledLayout) {
let current_width = self.total_width;
for (id, context) in other.contexts {
self.contexts.insert(id, context);
}
for mut element in other.elements {
element.position += current_width;
self.elements.push(element);
}
for (level, height) in other.level_heights {
*self.level_heights.entry(level).or_insert(0) =
*self.level_heights.get(&level).unwrap_or(&0).max(&height);
}
self.total_width += other.total_width;
}
fn merge_function_with_multiline(&mut self, func_name: &str, inner_layout: LeveledLayout) {
let func_structure_id = self.create_structure_context(
vec!["function".to_string(), func_name.to_string()],
None,
1,
);
let min_level = *inner_layout.level_heights.keys().min().unwrap_or(&0);
let max_level = *inner_layout.level_heights.keys().max().unwrap_or(&0);
for (id, context) in inner_layout.contexts {
self.contexts.insert(id, context);
}
if min_level == max_level {
let content = inner_layout
.elements
.iter()
.map(|e| e.content.clone())
.collect::<Vec<_>>()
.join("");
let func_with_content = format!("{}({})", func_name, content);
self.elements.push(LayoutElement {
content: func_with_content.clone(),
level: self.baseline,
position: self.total_width,
structure_id: func_structure_id,
});
self.total_width += func_with_content.chars().count();
self.level_heights.extend(inner_layout.level_heights);
return;
}
let current_pos = self.total_width;
let opening = format!("{}(", func_name);
self.elements.push(LayoutElement {
content: opening.clone(),
level: self.baseline,
position: current_pos,
structure_id: func_structure_id,
});
let func_offset = opening.chars().count();
for mut element in inner_layout.elements {
element.position = current_pos + func_offset + element.position;
self.elements.push(element);
}
let inner_width = inner_layout.total_width;
self.elements.push(LayoutElement {
content: ")".to_string(),
level: self.baseline,
position: current_pos + func_offset + inner_width,
structure_id: func_structure_id,
});
self.level_heights.extend(inner_layout.level_heights);
self.total_width = current_pos + func_offset + inner_width + 1;
}
fn render(&self) -> Vec<String> {
if self.elements.is_empty() {
return vec![String::new()];
}
let _min_level = *self.level_heights.keys().min().unwrap_or(&0);
let _max_level = *self.level_heights.keys().max().unwrap_or(&0);
let mut lines = Vec::new();
let max_width = self
.elements
.iter()
.map(|e| e.position + e.content.chars().count())
.max()
.unwrap_or(0);
let _structural_families = self.group_by_structural_families();
let mut levels_with_content: Vec<i32> = self
.level_heights
.keys()
.filter(|&&level| {
self.elements
.iter()
.any(|e| e.level == level && !e.content.trim().is_empty())
})
.cloned()
.collect();
levels_with_content.sort_by(|a, b| b.cmp(a));
for (i, level) in levels_with_content.iter().enumerate() {
let mut level_elements: Vec<&LayoutElement> = self
.elements
.iter()
.filter(|e| e.level == *level && !e.content.trim().is_empty())
.collect();
level_elements.sort_by(|a, b| {
let family_a = self.get_structure_family(a.structure_id);
let family_b = self.get_structure_family(b.structure_id);
family_a.cmp(&family_b).then(a.position.cmp(&b.position))
});
if level_elements.is_empty() {
continue;
}
let mut line_chars: Vec<char> = " ".repeat(max_width).chars().collect();
for element in level_elements {
let content = &element.content;
if content.is_empty() {
continue;
}
let start_pos = element.position;
let content_chars: Vec<char> = content.chars().collect();
for (i, &ch) in content_chars.iter().enumerate() {
let pos = start_pos + i;
if pos < line_chars.len() {
if line_chars[pos] != ' '
&& !self.are_compatible_structures(element.structure_id, pos)
{
continue;
}
line_chars[pos] = ch;
}
}
}
let line_str: String = line_chars.into_iter().collect();
lines.push(line_str.trim_end().to_string());
if i < levels_with_content.len() - 1 {
let current_level = *level;
let next_level = levels_with_content[i + 1];
let gap = (current_level - next_level).abs();
let has_division_line = self.elements.iter().any(|e| {
e.content.contains('─')
&& (e.level == current_level
|| e.level == next_level
|| (e.level > next_level && e.level < current_level))
});
if !has_division_line && gap > 2 {
lines.push(String::new());
}
}
}
while lines.last().map_or(false, |line| line.trim().is_empty()) {
lines.pop();
}
if lines.is_empty() {
vec![String::new()]
} else {
lines
}
}
fn group_by_structural_families(&self) -> std::collections::HashMap<usize, Vec<usize>> {
let mut families = std::collections::HashMap::new();
for element in &self.elements {
let family_id = self.get_structure_family(element.structure_id);
families
.entry(family_id)
.or_insert_with(Vec::new)
.push(element.structure_id);
}
families
}
fn get_structure_family(&self, structure_id: usize) -> usize {
let mut current_id = structure_id;
while let Some(context) = self.contexts.get(¤t_id) {
if let Some(parent_id) = context.parent_id {
current_id = parent_id;
} else {
break;
}
}
current_id
}
fn are_compatible_structures(&self, _structure_id: usize, _position: usize) -> bool {
true
}
}
#[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)
};
}