use std::fmt::Display;
use crate::{FType, Func, F1D, F2D, F3D};
impl Display for Func {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::E => write!(f, "e"),
Self::PI => write!(f, "\u{1D70B}"),
Self::Var(char) => write!(f, "{}", char),
Self::Num(num) => write!(f, "{}", num),
Self::Param(par, _) => write!(f, "[{}]", par),
Self::S(kind, arg) => match kind {
FType::Sin => write!(f, "sin({})", arg),
FType::Cos => write!(f, "cos({})", arg),
FType::Tan => write!(f, "tan({})", arg),
FType::Cot => write!(f, "cot({})", arg),
FType::Sec => write!(f, "sec({})", arg),
FType::Csc => write!(f, "csc({})", arg),
FType::ASin => write!(f, "asin({})", arg),
FType::ACos => write!(f, "acos({})", arg),
FType::ATan => write!(f, "atan({})", arg),
FType::Sinh => write!(f, "sinh({})", arg),
FType::Cosh => write!(f, "cosh({})", arg),
FType::Tanh => write!(f, "tanh({})", arg),
FType::Coth => write!(f, "coth({})", arg),
FType::Sech => write!(f, "sech({})", arg),
FType::Csch => write!(f, "csch({})", arg),
FType::ASinh => write!(f, "asinh({})", arg),
FType::ACosh => write!(f, "acosh({})", arg),
FType::ATanh => write!(f, "atanh({})", arg),
FType::Abs => write!(f, "|{}|", arg),
FType::Ln => write!(f, "ln({})", arg),
},
Self::Add(add) => {
let mut output = String::from("");
for (i, el) in add.iter().enumerate() {
if i != 0 {
if let Func::Mul(mul) = el {
if !mul.is_empty() {
if let Func::Num(val) = mul[0] {
if val < 0 {
output += &format!("{}", el);
continue;
}
}
}
}
output += &format!("+{}", el);
} else {
output += &format!("{}", el);
}
}
write!(f, "{}", output)
}
Self::Mul(mul) => {
let mut output = String::from("");
let mut found_div = false;
let mut has_divs = false;
for (i, el) in mul.iter().enumerate() {
if let Func::Num(val) = el {
if *val == -1 {
output += "-";
continue;
}
}
if let Func::Pow(base, exp) = el {
if let Func::Num(e) = **exp {
if e < 0 {
if !found_div {
output += "/";
if i != mul.len() - 1 {
has_divs = true;
output += "(";
}
}
match **base {
Func::Add(_) | Func::Mul(_) => output += &format!("({})", base),
_ => output += &format!("{}", base),
};
if e != -1 {
output += &format!("^{}", -e);
}
found_div = true;
continue;
}
}
}
output += &format!("{}", el);
}
if has_divs {
output += ")";
}
write!(f, "{}", output)
}
Func::Pow(base, exp) => {
let mut output = String::new();
match **base {
Func::Add(_) | Func::Mul(_) => output += &format!("({})^", base),
_ => output += &format!("{}^", base),
};
match **exp {
Func::Add(_) | Func::Mul(_) => output += &format!("({})", exp),
_ => output += &format!("{}", exp),
};
write!(f, "{}", output)
}
}
}
}
impl Display for F1D {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Display for F2D {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Display for F3D {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Func {
pub(crate) fn latex(&self) -> String {
match self {
Func::Var(char) => char.to_string(),
Func::PI => String::from(r"\pi"),
Func::E => String::from("e"),
Func::Num(val) => format!("{}", val),
Func::Param(par, _) => format!(r"\text{{{par}}}"),
Func::Add(add) => {
let mut output = String::from("");
for (i, el) in add.iter().enumerate() {
if i != 0 {
if let Func::Mul(mul) = el {
if !mul.is_empty() {
if let Func::Num(val) = mul[0] {
if val < 0 {
output += &format!("{}", el);
continue;
}
}
}
}
output += &format!("+{}", el.latex());
} else {
output += &el.latex();
}
}
output
}
Func::Mul(mul) => {
let mut output = String::from("");
let mut found_div = false;
for el in mul {
if let Func::Pow(base, exp) = el {
if **exp < 0 {
if !found_div {
output = format!(r"\frac{{{}}}{{", output);
}
found_div = true;
let div = Func::Pow(base.clone(), Box::new(-1 * *exp.clone()));
output += &div.latex();
continue;
}
}
output += &el.latex();
}
if found_div {
output += "}";
}
output
}
Func::Pow(base, exp) => {
let mut output = String::new();
match **base {
Func::Add(_) | Func::Mul(_) | Func::Pow(..) => {
output += &format!("({})", base.latex())
}
_ => output += &base.latex(),
};
match **exp {
Func::Add(_) | Func::Mul(_) | Func::Pow(..) => {
output += &format!("^{{{}}}", exp.latex())
}
Func::Num(1) => (),
_ => output += &format!("^{}", exp.latex()),
};
output
}
Func::S(kind, arg) => match kind {
FType::Ln => format!("ln({})", arg.latex()),
FType::Sin => format!("sin({})", arg.latex()),
FType::Cos => format!("cos({})", arg.latex()),
FType::Tan => format!("tan({})", arg.latex()),
FType::Cot => format!("cot({})", arg.latex()),
FType::Sec => format!("sec({})", arg.latex()),
FType::Csc => format!("csc({})", arg.latex()),
FType::ACos => format!("acos({})", arg.latex()),
FType::ASin => format!("asin({})", arg.latex()),
FType::ATan => format!("atan({})", arg.latex()),
FType::Sinh => format!("sinh({})", arg.latex()),
FType::Cosh => format!("cosh({})", arg.latex()),
FType::Tanh => format!("tanh({})", arg.latex()),
FType::Coth => format!("coth({})", arg.latex()),
FType::Sech => format!("sech({})", arg.latex()),
FType::Csch => format!("csch({})", arg.latex()),
FType::ASinh => format!("asinh({})", arg.latex()),
FType::ACosh => format!("acosh({})", arg.latex()),
FType::ATanh => format!("atanh({})", arg.latex()),
FType::Abs => format!("|{}|", arg.latex()),
},
}
}
}
#[test]
fn test_display() {
use crate::{f1d, f2d, f3d};
assert_eq!(
format!("{}", f1d!("x+1+cos(x)/ln(x)/ln(15)*sinh(x)^2/7-2+e*pi")),
"-1+x+cos(x)sinh(x)^2/(7ln(x)ln(15))+𝜋e",
);
assert_eq!(
format!("{}", f1d!("[eta]*sin(x)+cosh(x)/tan(x^2)")),
"[eta]sin(x)+cosh(x)/tan(x^2)"
);
assert_eq!(
format!("{}", f1d!("cot(x)+sec(x)^2+csc(e)/acos(x)")),
"csc(e)/acos(x)+cot(x)+sec(x)^2"
);
assert_eq!(format!("{}", f2d!("x+(-5y)")), "x-5y");
assert_eq!(format!("{}", f2d!("-xy")), "-xy");
assert_eq!(format!("{}", f2d!("x/(x+y)^2")), "x/(x+y)^2");
assert_eq!(format!("{}", f2d!("(x+y)^(e+2)")), "(x+y)^(2+e)");
assert_eq!(
format!(
"{}",
f2d!(
"asin(x)+atan(x)+tanh(x)+coth(x)+sech(x)+csch(x)+asinh(x)+acosh(x)+atanh(x)+abs(x)"
)
),
"|x|+asin(x)+atan(x)+tanh(x)+coth(x)+sech(x)+csch(x)+asinh(x)+acosh(x)+atanh(x)"
);
assert_eq!(format!("{}", f3d!("xyz")), "xyz");
}
#[test]
fn test_latex() {
use crate::{f1d, f2d, f3d};
assert_eq!(
format!("{}", f3d!("x^(y^3)+sin(x)+e+pi").latex()),
r"\pi+e+sin(x)+x^{y^3}"
);
assert_eq!(
format!("{}", f3d!("z+3ln(x)/(xy)").latex()),
r"z+\frac{3ln(x)}{xy}"
);
assert_eq!(f3d!("(x+2)^3").latex(), "(2+x)^3");
assert_eq!(f3d!("(x+2)^(3+y)").latex(), "(2+x)^{3+y}");
assert_eq!(f3d!("x^(3+y)").latex(), "x^{3+y}");
assert_eq!(f3d!("-x+[eta]").latex(), r"\text{eta}-x");
assert_eq!(
format!(
"{}",
f2d!(
"asin(x)+atan(x)+tanh(x)+coth(x)+sech(x)+csch(x)+asinh(x)+acosh(x)+atanh(x)+abs(x)"
)
.latex()
),
"|x|+asin(x)+atan(x)+tanh(x)+coth(x)+sech(x)+csch(x)+asinh(x)+acosh(x)+atanh(x)"
);
assert_eq!(
format!(
"{}",
f1d!("cot(x)+sec(x)^2+csc(e)/acos(x)+cos(x)+tan(x)+sinh(x)cosh(x)").latex()
),
r"\frac{csc(e)}{acos(x)}+sinh(x)cosh(x)+cos(x)+tan(x)+cot(x)+sec(x)^2"
);
}