use crate::ast::Symbol;
use std::collections::{BTreeMap, HashMap};
use std::string::ToString;
use strum::{EnumProperty, IntoEnumIterator};
use strum_macros::{Display, EnumIter, EnumProperty, EnumString};
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct SpecialFunction(String);
impl SpecialFunction {
fn square(&self) -> Symbol {
Symbol {
unicode_repr: format!("{}²", self.0),
ascii_repr: format!("{}^2", self.0),
latex_repr: format!("\\{}^2", self.0),
other_reprs: vec![],
}
}
fn inv(&self) -> Symbol {
Symbol {
unicode_repr: format!("{}⁻¹", self.0),
ascii_repr: format!("{}^-1", self.0),
latex_repr: format!("\\{}^{{-1}}", self.0),
other_reprs: vec![],
}
}
}
impl From<SpecialFunction> for Symbol {
fn from(func: SpecialFunction) -> Self {
Symbol {
unicode_repr: func.0.clone(),
ascii_repr: func.0.clone(),
latex_repr: format!("\\{}", func.0),
other_reprs: vec![],
}
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, EnumString, EnumProperty, Display, EnumIter)]
pub enum GreekLetter {
#[strum(props(Lower = "α", Upper = "Α"))]
Alpha,
#[strum(props(Lower = "β", Upper = "Β"))]
Beta,
#[strum(props(Lower = "γ", Upper = "Γ"))]
Gamma,
#[strum(props(Lower = "δ", Upper = "Δ"))]
Delta,
#[strum(props(Lower = "ε", Upper = "Ε"))]
Epsilon,
#[strum(props(Lower = "ζ", Upper = "Ζ"))]
Zeta,
#[strum(props(Lower = "η", Upper = "Η"))]
Eta,
#[strum(props(Lower = "θ", Upper = "Θ"))]
Theta,
#[strum(props(Lower = "ι", Upper = "Ι"))]
Iota,
#[strum(props(Lower = "κ", Upper = "Κ"))]
Kappa,
#[strum(props(Lower = "λ", Upper = "Λ"))]
Lambda,
#[strum(props(Lower = "μ", Upper = "Μ"))]
Mu,
#[strum(props(Lower = "ν", Upper = "Ν"))]
Nu,
#[strum(props(Lower = "ξ", Upper = "Ξ"))]
Xi,
#[strum(props(Lower = "ο", Upper = "Ο"))]
Omicron,
#[strum(props(Lower = "π", Upper = "Π"))]
Pi,
#[strum(props(Lower = "ρ", Upper = "Ρ"))]
Rho,
#[strum(props(Lower = "σ", Upper = "Σ"))]
Sigma,
#[strum(props(Lower = "τ", Upper = "Τ"))]
Tau,
#[strum(props(Lower = "υ", Upper = "Υ"))]
Upsilon,
#[strum(props(Lower = "φ", Upper = "Φ"))]
Phi,
#[strum(props(Lower = "χ", Upper = "Χ"))]
Chi,
#[strum(props(Lower = "ψ", Upper = "Ψ"))]
Psi,
#[strum(props(Lower = "ω", Upper = "Ω"))]
Omega,
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, EnumIter)]
pub enum Case {
Uppercase,
Lowercase,
}
pub struct CasedGreekLetter {
pub letter: GreekLetter,
pub case: Case,
}
impl From<CasedGreekLetter> for Symbol {
fn from(cased: CasedGreekLetter) -> Self {
let unicode = match cased.case {
Case::Uppercase => cased.letter.get_str("Upper").unwrap(),
Case::Lowercase => cased.letter.get_str("Lower").unwrap(),
};
let letter = cased.letter.to_string();
let (ascii_start, ascii_rest) = letter.split_at(1);
let ascii_name = match cased.case {
Case::Uppercase => format!("{}{}", ascii_start.to_uppercase(), ascii_rest),
Case::Lowercase => format!("{}{}", ascii_start.to_lowercase(), ascii_rest),
};
Symbol {
unicode_repr: unicode.to_string(),
ascii_repr: ascii_name.clone(),
latex_repr: format!("\\{}", ascii_name),
other_reprs: vec![],
}
}
}
impl From<String> for Symbol {
fn from(sym: String) -> Self {
Symbol {
unicode_repr: sym.clone(),
ascii_repr: sym.clone(),
latex_repr: sym.clone(),
other_reprs: vec![],
}
}
}
impl From<&str> for Symbol {
fn from(sym: &str) -> Self {
Symbol {
unicode_repr: sym.to_string(),
ascii_repr: sym.to_string(),
latex_repr: sym.to_string(),
other_reprs: vec![],
}
}
}
lazy_static! {
pub static ref GREEK_SYMBOLS: HashMap<String, Symbol> = {
let mut syms: HashMap<String, Symbol> = HashMap::new();
for letter in GreekLetter::iter() {
for case in Case::iter() {
let sym: Symbol = CasedGreekLetter { letter, case }.into();
syms.insert(sym.ascii_repr.clone(), sym);
}
}
syms
};
pub static ref LATIN_SYMBOLS: HashMap<String, Symbol> = {
let mut syms: HashMap<String, Symbol> = HashMap::new();
let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".chars();
for letter in alphabet {
syms.insert(letter.to_string(), letter.to_string().into());
}
syms
};
pub static ref SPECIAL_FUNCS: BTreeMap<String, Symbol> = {
let mut syms: BTreeMap<String, Symbol> = BTreeMap::new();
let names = vec![
"exp", "log", "ln", "lg",
"sin", "cos", "tan", "sec", "csc", "cot",
"arcsin", "arccos", "arctan",
"sinh", "cosh", "tanh", "coth",
"max", "min",
"Pr",
"gcd",
"det", "dim", "ker",
"inf", "sup"
];
for name in names {
let spf = SpecialFunction(name.to_string());
syms.insert(format!("{}^2", name), spf.square());
syms.insert(format!("{}^-1", name), spf.inv());
syms.insert(name.to_string(), spf.into());
}
syms
};
pub static ref LE: Symbol = Symbol::new("≤", "<=", r"\le", vec![" le"]);
pub static ref GE: Symbol = Symbol::new("≥", ">=", r"\ge", vec![" ge"]);
pub static ref NEQ: Symbol = Symbol::new("≠", "!=", r"\neq", vec!["=/=", "/=", "neq"]);
pub static ref PLUS: Symbol = Symbol::new("+", "+", "+", vec!["plus"]);
pub static ref MINUS: Symbol = Symbol::new("-", "−", "-", vec!["minus"]);
pub static ref PM: Symbol = Symbol::new("±", "+/-", r"\pm", vec!["+-", "pm"]);
pub static ref POWER: Symbol = Symbol::new("^", "^", r"\^{}", vec![]);
pub static ref DIV: Symbol = Symbol::new("/", "/", r"/", vec![]);
pub static ref INF: Symbol = Symbol::new("∞", " inf", r"\infty", vec!["infinity", "oo"]);
pub static ref ELEM: Symbol = Symbol::new("∈", " in", r"\in", vec![" elem"]);
pub static ref SYM: Symbol = Symbol::new("∼", "~", r"\sym", vec![]);
pub static ref APPROX: Symbol = Symbol::new("≅", "~=", r"\approx", vec![]);
pub static ref MULT: Symbol = Symbol::new("·", "*", r"\cdot", vec![" times", "\times", "×"]);
pub static ref DEGREE: Symbol = Symbol::new("°", "o", r"^{\circ}", vec!["deg", "degrees"]);
pub static ref LEFT_PAR: Symbol = Symbol::new("(", "(", r"\left(", vec![]);
pub static ref RIGHT_PAR: Symbol = Symbol::new(")", ")", r"\right)", vec![]);
pub static ref LEFT_BRACKET: Symbol = Symbol::new("[", "[", r"\left[", vec![]);
pub static ref RIGHT_BRACKET: Symbol = Symbol::new("]", "]", r"\right]", vec![]);
pub static ref COMMA: Symbol = Symbol::from(",");
pub static ref DELIMS: Vec<Symbol> = {
vec![
LEFT_PAR.clone(),
RIGHT_PAR.clone()
]
};
pub static ref MISC: Vec<Symbol> = {
vec![
LE.clone(),
GE.clone(),
NEQ.clone(),
PM.clone(),
INF.clone(),
ELEM.clone(),
SYM.clone(),
APPROX.clone(),
MULT.clone(),
DEGREE.clone(),
]
};
pub static ref ALL_SYMBOLS: Vec<Symbol> = {
let mut symbols = vec![];
for (_k, sym) in GREEK_SYMBOLS.clone().drain() {
symbols.push(sym);
}
for (_k, sym) in LATIN_SYMBOLS.clone().drain() {
symbols.push(sym);
}
for (_k, sym) in SPECIAL_FUNCS.clone().into_iter() {
symbols.push(sym);
}
symbols.extend_from_slice(&*MISC.as_slice());
symbols
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_all_greek_letters() {
assert_eq!(GREEK_SYMBOLS.len(), 48);
}
#[test]
fn test_all_latin_letters() {
assert_eq!(LATIN_SYMBOLS.len(), 52);
}
#[test]
fn test_greek_letters() {
let sym1: Symbol = CasedGreekLetter {
letter: GreekLetter::Phi,
case: Case::Lowercase,
}
.into();
assert_eq!(sym1.unicode_repr, "φ");
assert_eq!(sym1.ascii_repr, "phi");
assert_eq!(sym1.latex_repr, r"\phi");
let sym2: Symbol = CasedGreekLetter {
letter: GreekLetter::Sigma,
case: Case::Uppercase,
}
.into();
assert_eq!(sym2.unicode_repr, "Σ");
assert_eq!(sym2.ascii_repr, "Sigma");
assert_eq!(sym2.latex_repr, r"\Sigma");
}
}