use super::alphabets;
pub struct AtomSpec {
pub latex: &'static str,
pub kind: AtomKind,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum AtomKind {
Sym,
BigOp,
}
const fn sym(latex: &'static str) -> AtomSpec {
AtomSpec {
latex,
kind: AtomKind::Sym,
}
}
const fn big(latex: &'static str) -> AtomSpec {
AtomSpec {
latex,
kind: AtomKind::BigOp,
}
}
pub static ATOMS: phf::Map<char, AtomSpec> = phf::phf_map! {
'α' => sym("alpha"),
'β' => sym("beta"),
'γ' => sym("gamma"),
'δ' => sym("delta"),
'ϵ' => sym("epsilon"),
'ε' => sym("varepsilon"),
'ζ' => sym("zeta"),
'η' => sym("eta"),
'θ' => sym("theta"),
'ι' => sym("iota"),
'κ' => sym("kappa"),
'λ' => sym("lambda"),
'μ' => sym("mu"),
'ν' => sym("nu"),
'ξ' => sym("xi"),
'π' => sym("pi"),
'ρ' => sym("rho"),
'σ' => sym("sigma"),
'τ' => sym("tau"),
'υ' => sym("upsilon"),
'ϕ' => sym("phi"),
'χ' => sym("chi"),
'ψ' => sym("psi"),
'ω' => sym("omega"),
'φ' => sym("varphi"),
'ϑ' => sym("vartheta"),
'ϰ' => sym("varkappa"),
'Γ' => sym("Gamma"),
'Δ' => sym("Delta"),
'Θ' => sym("Theta"),
'Λ' => sym("Lambda"),
'Ξ' => sym("Xi"),
'Π' => sym("Pi"),
'Σ' => sym("Sigma"),
'Υ' => sym("Upsilon"),
'Φ' => sym("Phi"),
'Ψ' => sym("Psi"),
'Ω' => sym("Omega"),
'±' => sym("pm"),
'∓' => sym("mp"),
'×' => sym("times"),
'÷' => sym("div"),
'⋅' => sym("cdot"),
'∘' => sym("circ"),
'⊕' => sym("oplus"),
'⊗' => sym("otimes"),
'∗' => sym("ast"),
'≤' => sym("le"),
'≥' => sym("ge"),
'≠' => sym("ne"),
'≈' => sym("approx"),
'≡' => sym("equiv"),
'∼' => sym("sim"),
'≃' => sym("simeq"),
'∝' => sym("propto"),
'≪' => sym("ll"),
'≫' => sym("gg"),
'→' => sym("to"),
'←' => sym("leftarrow"),
'⇒' => sym("Rightarrow"),
'⇐' => sym("Leftarrow"),
'↔' => sym("leftrightarrow"),
'⇔' => sym("Leftrightarrow"),
'↦' => sym("mapsto"),
'∈' => sym("in"),
'∉' => sym("notin"),
'∌' => sym("notni"),
'≮' => sym("nless"),
'≯' => sym("ngtr"),
'≰' => sym("nleq"),
'≱' => sym("ngeq"),
'⊈' => sym("nsubseteq"),
'⊉' => sym("nsupseteq"),
'≁' => sym("nsim"),
'≇' => sym("ncong"),
'≄' => sym("not\\simeq"),
'≉' => sym("not\\approx"),
'≢' => sym("not\\equiv"),
'≴' => sym("not\\lesssim"),
'≵' => sym("not\\gtrsim"),
'⊀' => sym("nprec"),
'⊁' => sym("nsucc"),
'⋠' => sym("npreceq"),
'⋡' => sym("nsucceq"),
'⊄' => sym("not\\subset"),
'⊅' => sym("not\\supset"),
'⋢' => sym("not\\sqsubseteq"),
'⋣' => sym("not\\sqsupseteq"),
'⋪' => sym("ntriangleleft"),
'⋫' => sym("ntriangleright"),
'⋬' => sym("ntrianglelefteq"),
'⋭' => sym("ntrianglerighteq"),
'⊬' => sym("nvdash"),
'⊭' => sym("nvDash"),
'⊮' => sym("nVdash"),
'∋' => sym("ni"),
'⊂' => sym("subset"),
'⊆' => sym("subseteq"),
'⊃' => sym("supset"),
'⊇' => sym("supseteq"),
'∪' => sym("cup"),
'∩' => sym("cap"),
'∖' => sym("setminus"),
'∅' => sym("emptyset"),
'∀' => sym("forall"),
'∃' => sym("exists"),
'∄' => sym("nexists"),
'¬' => sym("neg"),
'∧' => sym("land"),
'∨' => sym("lor"),
'⊢' => sym("vdash"),
'⊨' => sym("models"),
'∞' => sym("infty"),
'∂' => sym("partial"),
'∇' => sym("nabla"),
'ℏ' => sym("hbar"),
'ℓ' => sym("ell"),
'ℜ' => sym("Re"),
'ℑ' => sym("Im"),
'ℵ' => sym("aleph"),
'∠' => sym("angle"),
'⊥' => sym("perp"),
'∥' => sym("parallel"),
'∦' => sym("nparallel"),
'∣' => sym("mid"),
'∤' => sym("nmid"),
'′' => sym("prime"),
'°' => sym("degree"),
'⋯' => sym("cdots"),
'…' => sym("ldots"),
'⋮' => sym("vdots"),
'⋱' => sym("ddots"),
'␣' => sym("space"),
'↑' => sym("uparrow"),
'↓' => sym("downarrow"),
'↕' => sym("updownarrow"),
'↖' => sym("nwarrow"),
'↗' => sym("nearrow"),
'↘' => sym("searrow"),
'↙' => sym("swarrow"),
'↞' => sym("twoheadleftarrow"),
'↠' => sym("twoheadrightarrow"),
'↢' => sym("leftarrowtail"),
'↣' => sym("rightarrowtail"),
'↩' => sym("hookleftarrow"),
'↪' => sym("hookrightarrow"),
'⇄' => sym("rightleftarrows"),
'⇆' => sym("leftrightarrows"),
'⇇' => sym("leftleftarrows"),
'⇈' => sym("upuparrows"),
'⇉' => sym("rightrightarrows"),
'⇊' => sym("downdownarrows"),
'⇑' => sym("Uparrow"),
'⇓' => sym("Downarrow"),
'⇕' => sym("Updownarrow"),
'⇝' => sym("rightsquigarrow"),
'⟵' => sym("longleftarrow"),
'⟶' => sym("longrightarrow"),
'⟷' => sym("longleftrightarrow"),
'⟸' => sym("Longleftarrow"),
'⟹' => sym("Longrightarrow"),
'⟺' => sym("Longleftrightarrow"),
'⟼' => sym("longmapsto"),
'≅' => sym("cong"),
'≊' => sym("approxeq"),
'≔' => sym("coloneqq"),
'≕' => sym("eqqcolon"),
'≦' => sym("leqq"),
'≧' => sym("geqq"),
'≨' => sym("lneqq"),
'≩' => sym("gneqq"),
'≲' => sym("lesssim"),
'≳' => sym("gtrsim"),
'≺' => sym("prec"),
'≻' => sym("succ"),
'≼' => sym("preccurlyeq"),
'≽' => sym("succcurlyeq"),
'⊊' => sym("subsetneq"),
'⊋' => sym("supsetneq"),
'⊏' => sym("sqsubset"),
'⊐' => sym("sqsupset"),
'⊑' => sym("sqsubseteq"),
'⊒' => sym("sqsupseteq"),
'⊣' => sym("dashv"),
'⊤' => sym("top"),
'⊩' => sym("Vdash"),
'⊲' => sym("vartriangleleft"),
'⊳' => sym("vartriangleright"),
'⊴' => sym("trianglelefteq"),
'⊵' => sym("trianglerighteq"),
'⋤' => sym("sqsubsetneq"),
'⋥' => sym("sqsupsetneq"),
'⋦' => sym("lnsim"),
'⋧' => sym("gnsim"),
'⋘' => sym("lll"),
'⋙' => sym("ggg"),
'⪅' => sym("lessapprox"),
'⪆' => sym("gtrapprox"),
'⪇' => sym("lneq"),
'⪈' => sym("gneq"),
'⪉' => sym("lnapprox"),
'⪊' => sym("gnapprox"),
'⫅' => sym("subseteqq"),
'⫆' => sym("supseteqq"),
'⫋' => sym("subsetneqq"),
'⫌' => sym("supsetneqq"),
'∴' => sym("therefore"),
'∵' => sym("because"),
'†' => sym("dagger"),
'‡' => sym("ddagger"),
'∁' => sym("complement"),
'∔' => sym("dotplus"),
'∙' => sym("bullet"),
'⊓' => sym("sqcap"),
'⊔' => sym("sqcup"),
'⊖' => sym("ominus"),
'⊙' => sym("odot"),
'⊻' => sym("veebar"),
'⊼' => sym("barwedge"),
'⋆' => sym("star"),
'⋈' => sym("bowtie"),
'⋉' => sym("ltimes"),
'⋊' => sym("rtimes"),
'∭' => big("iiint"),
'∯' => big("oiint"),
'∰' => big("oiiint"),
'⨀' => big("bigodot"),
'⨅' => big("bigsqcap"),
'⨆' => big("bigsqcup"),
'∑' => big("sum"),
'∏' => big("prod"),
'∐' => big("coprod"),
'∫' => big("int"),
'∬' => big("iint"),
'∮' => big("oint"),
'⋃' => big("bigcup"),
'⋂' => big("bigcap"),
'⨁' => big("bigoplus"),
'⨂' => big("bigotimes"),
'⋁' => big("bigvee"),
'⋀' => big("bigwedge"),
};
pub fn atom_of(c: char) -> Option<&'static AtomSpec> {
ATOMS.get(&c)
}
pub static NAMES: phf::Map<&'static str, char> = phf::phf_map! {
"alpha" | "al" | "alp" => 'α',
"beta" | "be" | "bet" => 'β',
"gamma" | "ga" | "gam" | "gm" | "gmm" => 'γ',
"delta" | "de" | "del" | "dl" | "dlt" => 'δ',
"epsilon" | "ep" | "eps" => 'ϵ',
"varepsilon" | "varep" | "vareps" | "vep" | "veps" | "vepsilon" => 'ε',
"zeta" | "ze" | "zet" => 'ζ',
"eta" | "et" => 'η',
"theta" | "th" | "the" => 'θ',
"iota" | "io" | "iot" => 'ι',
"kappa" | "ka" | "kap" | "kp" | "kpp" => 'κ',
"lambda" | "la" | "lam" | "lm" => 'λ',
"mu" => 'μ',
"nu" => 'ν',
"xi" => 'ξ',
"pi" => 'π',
"rho" | "rh" => 'ρ',
"sigma" | "sg" | "sgm" | "si" | "sig" => 'σ',
"tau" | "ta" => 'τ',
"upsilon" | "ups" => 'υ',
"phi" | "ph" => 'ϕ',
"chi" | "ch" => 'χ',
"psi" | "ps" => 'ψ',
"omega" | "om" | "ome" | "omg" => 'ω',
"varphi" | "varph" | "vph" | "vphi" => 'φ',
"vartheta" | "varth" | "varthe" | "vth" | "vthe" | "vtheta" => 'ϑ',
"varkappa" | "varka" | "varkap" | "varkp" | "varkpp" | "vka" | "vkap" | "vkappa" | "vkp" | "vkpp" => 'ϰ',
"Gamma" | "Ga" | "Gam" | "Gm" | "Gmm" => 'Γ',
"Delta" | "De" | "Del" | "Dl" | "Dlt" => 'Δ',
"Theta" | "Th" | "The" => 'Θ',
"Lambda" | "La" | "Lam" | "Lm" => 'Λ',
"Xi" => 'Ξ',
"Pi" => 'Π',
"Sigma" | "Sg" | "Sgm" | "Si" | "Sig" => 'Σ',
"Upsilon" | "Ups" => 'Υ',
"Phi" | "Ph" => 'Φ',
"Psi" | "Ps" => 'Ψ',
"Omega" | "Om" | "Ome" | "Omg" => 'Ω',
"pm" | "+-" => '±',
"mp" | "-+" => '∓',
"times" | "x" => '×',
"div" => '÷',
"cdot" => '⋅',
"circ" => '∘',
"oplus" => '⊕',
"otimes" => '⊗',
"ast" => '∗',
"le" | "leq" => '≤',
"ge" | "geq" => '≥',
"ne" | "neq" | "/=" | "=/" | "=/=" => '≠',
"approx" | "~~" => '≈',
"equiv" | "-=" | "=-" => '≡',
"sim" => '∼',
"simeq" | "-~" | "~-" => '≃',
"propto" | "oc" | "prop" => '∝',
"ll" | "<<" => '≪',
"gg" | ">>" => '≫',
"to" | "rightarrow" | "right" | "->" => '→',
"leftarrow" | "left" | "<-" => '←',
"Rightarrow" | "Right" | "=>" => '⇒',
"Leftarrow" | "Left" | "<=" => '⇐',
"leftrightarrow" | "leftright" | "<->" => '↔',
"Leftrightarrow" | "LR" | "Leftrigh" | "Lr" | "<=>" => '⇔',
"mapsto" | "|->" => '↦',
"in" => '∈',
"notin" => '∉',
"notni" => '∌',
"nless" => '≮',
"ngtr" => '≯',
"nleq" | "nle" => '≰',
"ngeq" | "nge" => '≱',
"nsubseteq" => '⊈',
"nsupseteq" => '⊉',
"nsim" => '≁',
"ncong" => '≇',
"nsimeq" => '≄',
"napprox" => '≉',
"nequiv" => '≢',
"nlesssim" => '≴',
"ngtrsim" => '≵',
"nprec" => '⊀',
"nsucc" => '⊁',
"npreceq" => '⋠',
"nsucceq" => '⋡',
"nsubset" => '⊄',
"nsupset" => '⊅',
"nsqsubseteq" => '⋢',
"nsqsupseteq" => '⋣',
"ntriangleleft" => '⋪',
"ntriangleright" => '⋫',
"ntrianglelefteq" => '⋬',
"ntrianglerighteq" => '⋭',
"nvdash" => '⊬',
"nvDash" => '⊭',
"nVdash" => '⊮',
"ni" => '∋',
"subset" => '⊂',
"subseteq" => '⊆',
"supset" => '⊃',
"supseteq" => '⊇',
"cup" => '∪',
"cap" => '∩',
"setminus" | "setm" | "sm" => '∖',
"emptyset" | "empty" => '∅',
"forall" | "A" | "all" => '∀',
"exists" | "E" | "exist" => '∃',
"nexists" => '∄',
"neg" | "not" => '¬',
"land" | "and" | "wedge" => '∧',
"lor" | "or" | "vee" => '∨',
"vdash" => '⊢',
"models" | "vDash" => '⊨',
"infty" | "oo" => '∞',
"partial" | "p" | "par" => '∂',
"nabla" => '∇',
"hbar" | "hslash" => 'ℏ',
"ell" => 'ℓ',
"Re" => 'ℜ',
"Im" => 'ℑ',
"aleph" => 'ℵ',
"angle" => '∠',
"perp" | "bot" => '⊥',
"parallel" => '∥',
"nparallel" => '∦',
"mid" | "divides" => '∣',
"nmid" => '∤',
"prime" => '′',
"degree" => '°',
"cdots" | "---" => '⋯',
"ldots" | "dots" | "..." => '…',
"vdots" => '⋮',
"ddots" => '⋱',
"space" => '␣',
"uparrow" | "up" => '↑',
"downarrow" | "dn" | "down" => '↓',
"updownarrow" | "ud" | "updn" | "updown" => '↕',
"nwarrow" => '↖',
"nearrow" => '↗',
"searrow" => '↘',
"swarrow" => '↙',
"twoheadleftarrow" | "twoheadleft" | "<<-" => '↞',
"twoheadrightarrow" | "twoheadright" | "->>" => '↠',
"leftarrowtail" | "lefttail" | "<-<" => '↢',
"rightarrowtail" | "righttail" | ">->" => '↣',
"hookleftarrow" | "hookleft" | "<-C" | "<-c" => '↩',
"hookrightarrow" | "hookright" | "C->" | "c->" => '↪',
"rightleftarrows" => '⇄',
"leftrightarrows" => '⇆',
"leftleftarrows" | "leftleft" | "<-<-" => '⇇',
"upuparrows" | "upup" => '⇈',
"rightrightarrows" | "rightright" | "->->" => '⇉',
"downdownarrows" | "dndn" | "downdown" => '⇊',
"Uparrow" | "Up" => '⇑',
"Downarrow" | "Dn" | "Down" => '⇓',
"Updownarrow" | "UD" | "Ud" | "Updn" | "Updown" => '⇕',
"rightsquigarrow" | "leadsto" | "~>" => '⇝',
"longleftarrow" | "longleft" | "<--" => '⟵',
"longrightarrow" | "longright" | "-->" => '⟶',
"longleftrightarrow" | "longleftright" | "longlr" | "<-->" => '⟷',
"Longleftarrow" | "Longleft" | "<==" => '⟸',
"Longrightarrow" | "Longright" | "==>" => '⟹',
"Longleftrightarrow" | "Longleftright" | "Longlr" | "iff" | "<==>" => '⟺',
"longmapsto" | "|-->" => '⟼',
"cong" | "simeqq" | "=~" | "~=" => '≅',
"approxeq" | "-~~" | "~~-" => '≊',
"coloneqq" | "ceq" | "coloneq" | ":=" => '≔',
"eqqcolon" | "eqc" | "eqcolon" | "=:" => '≕',
"leqq" => '≦',
"geqq" => '≧',
"lneqq" => '≨',
"gneqq" => '≩',
"lesssim" | "lsim" => '≲',
"gtrsim" | "gsim" => '≳',
"prec" => '≺',
"succ" => '≻',
"preccurlyeq" | "preceq" => '≼',
"succcurlyeq" | "succeq" => '≽',
"subsetneq" => '⊊',
"supsetneq" => '⊋',
"sqsubset" => '⊏',
"sqsupset" => '⊐',
"sqsubseteq" => '⊑',
"sqsupseteq" => '⊒',
"dashv" => '⊣',
"top" => '⊤',
"Vdash" => '⊩',
"vartriangleleft" | "<|" => '⊲',
"vartriangleright" | "|>" => '⊳',
"trianglelefteq" | "-<|" | "<|-" => '⊴',
"trianglerighteq" | "-|>" | "|>-" => '⊵',
"sqsubsetneq" => '⋤',
"sqsupsetneq" => '⋥',
"lnsim" => '⋦',
"gnsim" => '⋧',
"lll" | "<<<" => '⋘',
"ggg" | ">>>" => '⋙',
"lessapprox" | "lapprox" => '⪅',
"gtrapprox" | "gapprox" => '⪆',
"lneq" => '⪇',
"gneq" => '⪈',
"lnapprox" => '⪉',
"gnapprox" => '⪊',
"subseteqq" => '⫅',
"supseteqq" => '⫆',
"subsetneqq" => '⫋',
"supsetneqq" => '⫌',
"therefore" => '∴',
"because" => '∵',
"dagger" | "+" => '†',
"ddagger" | "++" => '‡',
"complement" => '∁',
"dotplus" => '∔',
"bullet" => '∙',
"sqcap" => '⊓',
"sqcup" => '⊔',
"ominus" => '⊖',
"odot" => '⊙',
"veebar" | "xor" => '⊻',
"barwedge" | "nand" => '⊼',
"star" => '⋆',
"bowtie" | "|x|" => '⋈',
"ltimes" | "|x" => '⋉',
"rtimes" | "x|" => '⋊',
"iiint" | "III" => '∭',
"oiint" | "oII" => '∯',
"oiiint" | "oIII" => '∰',
"bigodot" => '⨀',
"bigsqcap" => '⨅',
"bigsqcup" => '⨆',
"sum" | "S" => '∑',
"prod" | "P" => '∏',
"coprod" => '∐',
"int" | "I" => '∫',
"iint" | "II" => '∬',
"oint" | "oI" => '∮',
"bigcup" => '⋃',
"bigcap" => '⋂',
"bigoplus" => '⨁',
"bigotimes" => '⨂',
"bigvee" => '⋁',
"bigwedge" => '⋀',
"CC" => 'ℂ',
"HH" => 'ℍ',
"NN" => 'ℕ',
"PP" => 'ℙ',
"QQ" => 'ℚ',
"RR" => 'ℝ',
"ZZ" => 'ℤ',
};
pub fn named_char(name: &str) -> Option<char> {
NAMES.get(name).copied()
}
pub static NEGATIONS: phf::Map<char, char> = phf::phf_map! {
'=' => '≠',
'~' => '≁',
'∈' => '∉',
'∋' => '∌',
'<' => '≮',
'>' => '≯',
'≤' => '≰',
'≥' => '≱',
'∼' => '≁',
'≃' => '≄',
'≅' => '≇',
'≈' => '≉',
'≡' => '≢',
'≲' => '≴',
'≳' => '≵',
'≺' => '⊀',
'≻' => '⊁',
'≼' => '⋠',
'≽' => '⋡',
'⊂' => '⊄',
'⊃' => '⊅',
'⊆' => '⊈',
'⊇' => '⊉',
'⊑' => '⋢',
'⊒' => '⋣',
'⊲' => '⋪',
'⊳' => '⋫',
'⊴' => '⋬',
'⊵' => '⋭',
'⊢' => '⊬',
'⊨' => '⊭',
'⊩' => '⊮',
'∣' => '∤',
'∥' => '∦',
'∃' => '∄',
};
pub fn negated(c: char) -> Option<char> {
NEGATIONS.get(&c).copied()
}
pub static UNNEGATIONS: phf::Map<char, char> = phf::phf_map! {
'≠' => '=',
'∉' => '∈',
'∌' => '∋',
'≮' => '<',
'≯' => '>',
'≰' => '≤',
'≱' => '≥',
'≁' => '∼',
'≄' => '≃',
'≇' => '≅',
'≉' => '≈',
'≢' => '≡',
'≴' => '≲',
'≵' => '≳',
'⊀' => '≺',
'⊁' => '≻',
'⋠' => '≼',
'⋡' => '≽',
'⊄' => '⊂',
'⊅' => '⊃',
'⊈' => '⊆',
'⊉' => '⊇',
'⋢' => '⊑',
'⋣' => '⊒',
'⋪' => '⊲',
'⋫' => '⊳',
'⋬' => '⊴',
'⋭' => '⊵',
'⊬' => '⊢',
'⊭' => '⊨',
'⊮' => '⊩',
'∤' => '∣',
'∦' => '∥',
'∄' => '∃',
};
pub fn unnegated(c: char) -> Option<char> {
UNNEGATIONS.get(&c).copied()
}
pub fn is_atom(c: char) -> bool {
use std::sync::OnceLock;
static ATOM_SET: OnceLock<std::collections::HashSet<char>> = OnceLock::new();
if c.is_ascii() {
return c.is_ascii_graphic()
&& !matches!(
c,
'(' | ')' | '[' | ']' | '{' | '}' | '_' | '"' | '\'' | '^' | '~' | '`' | '\\'
);
}
ATOM_SET
.get_or_init(|| {
NAMES
.values()
.copied()
.chain(alphabets::ALPHABETS.keys().flat_map(|style| {
('A'..='Z')
.chain('a'..='z')
.chain('0'..='9')
.filter_map(move |l| alphabets::alphabet_char(&format!("{}{}", style, l)))
}))
.collect()
})
.contains(&c)
}
#[cfg(test)]
mod tests {
use crate::symbols::*;
#[rustfmt::skip]
fn is_reserved_glyph(c: char) -> bool {
matches!(
c,
'─' | '┈' | '═' | '√' | '∛' | '∜' | '│' | '_' | '(' | ')' | '⎛' | '⎜' | '⎝' | '⎞' | '⎟' | '⎠'
| '[' | ']' | '⎡' | '⎢' | '⎣' | '⎤' | '⎥' | '⎦'
| '⌈' | '⌉' | '⌊' | '⌋' | '{' | '}' | '⎧' | '⎨' | '⎩' | '⎪' | '⎫' | '⎬' | '⎭'
| '⟨' | '⟩' | '╱' | '╲'
| '‖' | '┆' | '┊' | '┌' | '┬' | '┐' | '├' | '┼' | '┤' | '└' | '┴' | '┘'
| '╭' | '╮' | '╰' | '╯'
| '⬚' | '▌' | '"' | '\'' | '¯' | '˜' | '˷' | '˰' | '˯' | '˳' | '․' | '⇾' ) || crate::symbols::scripts::unsuperscript_char(c).is_some()
|| crate::symbols::scripts::unsubscript_char(c).is_some()
}
#[test]
fn reserved_glyphs_stay_out_of_the_tables() {
for (&name, &ch) in NAMES.entries() {
assert!(
!is_reserved_glyph(ch),
"\\{} hands out a reserved glyph",
name
);
}
for style in alphabets::ALPHABETS.keys() {
for l in ('A'..='Z').chain('a'..='z').chain('0'..='9') {
if let Some(c) = alphabets::alphabet_char(&format!("{}{}", style, l)) {
assert!(!is_reserved_glyph(c), "{:?}", c);
}
}
}
for c in (0u8..=127).map(char::from) {
let expect = c.is_ascii_graphic()
&& !is_reserved_glyph(c)
&& !matches!(c, '^' | '~' | '`' | '\\');
assert_eq!(is_atom(c), expect, "{:?}", c);
}
for c in (1..=0x2FFFFu32).filter_map(char::from_u32) {
if is_reserved_glyph(c) {
assert!(!is_atom(c), "{:?}", c);
}
}
}
#[test]
fn atom_rows_are_canonical() {
let mut names = std::collections::HashSet::new();
for (&ch, a) in ATOMS.entries() {
assert!(names.insert(a.latex), "\\{} is claimed twice", a.latex);
assert_eq!(latex_name(ch), Some(a.latex));
if a.latex.contains('\\') {
assert!(
NAMES.values().any(|&c| c == ch),
"\\{} has no input name",
a.latex
);
} else {
assert_eq!(named_char(a.latex), Some(ch), "\\{}", a.latex);
assert_eq!(symbol_by_name(a.latex), Some(ch), "\\{}", a.latex);
}
}
for (&name, &ch) in NAMES.entries() {
assert!(
crate::symbols::latex_of(ch).is_some(),
"\\{} targets a char with no LaTeX spelling",
name
);
assert_eq!(symbol_by_name(name), Some(ch), "\\{}", name);
}
}
#[test]
fn negations_are_atoms_and_typeable() {
let mut seen = std::collections::HashSet::new();
for (&base, &neg) in NEGATIONS.entries() {
assert!(
base.is_ascii() || NAMES.values().any(|&c| c == base),
"the base {} of {} is not typeable",
base,
neg
);
assert!(ATOMS.contains_key(&neg), "{} is not a curated atom", neg);
assert!(
NAMES.values().any(|&c| c == neg),
"{} has no direct input name",
neg
);
seen.insert(neg);
}
for (base, neg) in [('=', '≠'), ('<', '≮'), ('>', '≯'), ('~', '≁')] {
assert_eq!(negated(base), Some(neg));
assert_eq!(
crate::symbols::symbol_by_name(&format!("!{}", base)),
Some(neg)
);
assert_eq!(
crate::symbols::symbol_by_name(&format!("{}!", base)),
Some(neg)
);
}
assert_eq!(crate::symbols::symbol_by_name("!in"), Some('∉'));
assert_eq!(crate::symbols::symbol_by_name("in!"), Some('∉'));
assert_eq!(crate::symbols::symbol_by_name("subset!"), Some('⊄'));
assert!(seen.len() >= 30, "the table covers the negation family");
}
#[test]
fn negation_tables_mirror_each_other() {
for (&base, &neg) in NEGATIONS.entries() {
let back = unnegated(neg).unwrap_or_else(|| panic!("{} has no un-negation", neg));
assert_eq!(
negated(back),
Some(neg),
"{} → {} → {} does not close",
base,
neg,
back
);
}
for (&neg, &base) in UNNEGATIONS.entries() {
assert_eq!(negated(base), Some(neg), "{} ↔ {} mismatch", neg, base);
}
let distinct: std::collections::HashSet<char> = NEGATIONS.values().copied().collect();
assert_eq!(UNNEGATIONS.len(), distinct.len());
}
#[test]
fn every_atom_is_one_narrow_char() {
let wide = |c: char| {
matches!(c as u32,
0x1100..=0x115F | 0x2E80..=0xA4CF | 0xA960..=0xA97F
| 0xAC00..=0xD7A3 | 0xF900..=0xFAFF | 0xFE10..=0xFE19
| 0xFE30..=0xFE6F | 0xFF00..=0xFF60 | 0xFFE0..=0xFFE6
| 0x1F300..=0x1FAFF | 0x20000..=0x3FFFD)
};
let mut n = 0;
for c in char::from_u32(0)
.into_iter()
.chain((1..=0x2FFFFu32).filter_map(char::from_u32))
{
if !is_atom(c) {
continue;
}
n += 1;
assert!(!wide(c), "{:?} (U+{:04X}) is not narrow", c, c as u32);
assert!(
!matches!(c as u32, 0x300..=0x36F) && c != '\u{200B}',
"{:?} is a combining/zero-width char",
c
);
}
assert!(n > 500, "the atom set looks too small: {}", n);
assert!(!is_atom('⇾'));
for c in ['😀', '漢', '\u{0301}', '─', '┈'] {
assert!(!is_atom(c), "{:?} must be rejected", c);
}
}
#[test]
fn script_letters_resolve_without_eating_names() {
for (name, want) in [
("supset", '⊃'),
("subset", '⊂'),
("supseteq", '⊇'),
("subseteq", '⊆'),
] {
assert_eq!(symbol_by_name(name), Some(want), "\\{}", name);
}
assert_eq!(symbol_by_name("supA"), None);
assert!(!is_atom('ᴬ'), "a modifier letter is not an atom");
}
}