polymath_rs/
mathml.rs

1use itertools::Itertools;
2
3use crate::{
4    ast::{Expression, Expressions, Literal, Table, TableRow, AST},
5    tokens::{
6        types::{
7            Arrow, BinaryOperator, Function, Greek, LBrace, Logical, Misc, Operation, RBrace,
8            Relational, TokenType, UnaryOperator,
9        },
10        Token,
11    },
12};
13
14pub fn to_mathml(ast: &AST) -> String {
15    format!(
16        "<math display=\"block\">{}</math>",
17        expressions_to_mathml(&ast.expressions)
18    )
19}
20
21fn expressions_to_mathml(expressions: &Expressions) -> String {
22    expressions
23        .expressions
24        .iter()
25        .map(|expression| expression_to_mathml(expression))
26        .join("")
27}
28
29fn expression_to_mathml(expr: &Expression) -> String {
30    match expr {
31        Expression::Frac(frac) => frac_to_mathml(frac),
32        Expression::Sub(bi_expression) => sub_to_mathml(bi_expression),
33        Expression::Pow(bi_expression) => pow_to_mathml(bi_expression),
34        Expression::SubPow(tri_expression) => sub_pow_to_mathml(tri_expression),
35        Expression::Group(group) => group_to_mathml(group),
36        Expression::Unary(unary) => unary_to_mathml(unary),
37        Expression::Binary(binary) => binary_to_mathml(binary),
38        Expression::Literal(literal) => literal_to_mathml(literal),
39        Expression::Expressions(expressions) => expressions_to_mathml(expressions),
40        Expression::Unit => unreachable!(),
41    }
42}
43
44fn binary_to_mathml(binary: &crate::ast::Binary) -> String {
45    match binary.operator.token_type {
46        crate::tokens::types::TokenType::BinaryOperator(BinaryOperator::Root) => format!(
47            "<mroot><mrow>{}</mrow><mrow>{}</mrow></mroot>",
48            match binary.expression_2.as_ref() {
49                Expression::Group(group) => expressions_to_mathml(&group.expressions),
50                _ => expression_to_mathml(&binary.expression_2),
51            },
52            match binary.expression_1.as_ref() {
53                Expression::Group(group) => expressions_to_mathml(&group.expressions),
54                _ => expression_to_mathml(&binary.expression_1),
55            }
56        ),
57        crate::tokens::types::TokenType::BinaryOperator(BinaryOperator::Overset) => format!(
58            "<mover><mrow>{}</mrow><mrow>{}</mrow></mover>",
59            match binary.expression_2.as_ref() {
60                Expression::Group(group) => expressions_to_mathml(&group.expressions),
61                _ => expression_to_mathml(&binary.expression_2),
62            },
63            match binary.expression_1.as_ref() {
64                Expression::Group(group) => expressions_to_mathml(&group.expressions),
65                _ => expression_to_mathml(&binary.expression_1),
66            }
67        ),
68        crate::tokens::types::TokenType::BinaryOperator(BinaryOperator::Underset) => format!(
69            "<munder><mrow>{}</mrow><mrow>{}</mrow></munder>",
70            match binary.expression_2.as_ref() {
71                Expression::Group(group) => expressions_to_mathml(&group.expressions),
72                _ => expression_to_mathml(&binary.expression_2),
73            },
74            match binary.expression_1.as_ref() {
75                Expression::Group(group) => expressions_to_mathml(&group.expressions),
76                _ => expression_to_mathml(&binary.expression_1),
77            }
78        ),
79        crate::tokens::types::TokenType::BinaryOperator(BinaryOperator::Color) => format!(
80            "<mstyle mathcolor=\"{}\">{}</mstyle>",
81            match binary.expression_1.as_ref() {
82                Expression::Group(group) => group
83                    .expressions
84                    .expressions
85                    .iter()
86                    .map(|expression| match expression {
87                        Expression::Literal(Literal::Literal(literal)) => literal.span.text,
88                        _ => "",
89                    })
90                    .join(""),
91                _ => "".to_string(),
92            },
93            match binary.expression_2.as_ref() {
94                Expression::Group(group) => expressions_to_mathml(&group.expressions),
95                _ => expression_to_mathml(&binary.expression_2),
96            },
97        ),
98        _ => format!(
99            "{}{}{}",
100            binary.operator.span.text,
101            expression_to_mathml(&binary.expression_1),
102            expression_to_mathml(&binary.expression_2),
103        ),
104    }
105}
106
107fn group_to_mathml(group: &crate::ast::Group) -> String {
108    format!(
109        "<mrow>{}{}{}</mrow>",
110        l_brace_to_math_ml(&group.l_brace),
111        group
112            .expressions
113            .expressions
114            .iter()
115            .map(|expr| expression_to_mathml(expr))
116            .join(""),
117        r_brace_to_math_ml(&group.r_brace),
118    )
119}
120
121fn sub_pow_to_mathml(tri_expression: &crate::ast::TriExpression) -> String {
122    if let Expression::Literal(Literal::Literal(literal)) = tri_expression.expression_1.as_ref() {
123        match &literal.token_type {
124            TokenType::Operation(op) => match op {
125                Operation::Sum
126                | Operation::Prod
127                | Operation::BigWedge
128                | Operation::BigCap
129                | Operation::BigCup => {
130                    return format!(
131                        "<munderover>{}{}{}</munderover>",
132                        expression_to_mathml_braceless(&tri_expression.expression_1),
133                        expression_to_mathml_braceless(&tri_expression.expression_2),
134                        expression_to_mathml_braceless(&tri_expression.expression_3)
135                    )
136                }
137                _ => {}
138            },
139            TokenType::Misc(Misc::Lim) => {
140                return format!(
141                    "<munderover>{}{}{}</munderover>",
142                    expression_to_mathml_braceless(&tri_expression.expression_1),
143                    expression_to_mathml_braceless(&tri_expression.expression_2),
144                    expression_to_mathml_braceless(&tri_expression.expression_3)
145                )
146            }
147            _ => {}
148        }
149    } else if let Expression::Unary(unary) = tri_expression.expression_1.as_ref() {
150        match unary.operator.token_type {
151            TokenType::UnaryOperator(UnaryOperator::UBrace) => {
152                return format!(
153                    "<munderover><munder>{}</munder>{}{}</munderover>",
154                    expression_to_mathml_braceless(&tri_expression.expression_1),
155                    expression_to_mathml_braceless(&tri_expression.expression_2),
156                    expression_to_mathml_braceless(&tri_expression.expression_3)
157                )
158            }
159            TokenType::UnaryOperator(UnaryOperator::OBrace) => {
160                return format!(
161                    "<munderover><mover>{}</mover>{}{}</munderover>",
162                    expression_to_mathml_braceless(&tri_expression.expression_1),
163                    expression_to_mathml_braceless(&tri_expression.expression_2),
164                    expression_to_mathml_braceless(&tri_expression.expression_3)
165                )
166            }
167            _ => {}
168        }
169    }
170    format!(
171        "<msubsup>{}{}{}</msubsup>",
172        expression_to_mathml(&tri_expression.expression_1),
173        expression_to_mathml(&tri_expression.expression_2),
174        expression_to_mathml(&tri_expression.expression_3)
175    )
176}
177
178fn pow_to_mathml(bi_expression: &crate::ast::BiExpression) -> String {
179    if let Expression::Literal(Literal::Literal(token)) = bi_expression.expression_1.as_ref() {
180        match &token.token_type {
181            TokenType::Operation(op) => match op {
182                Operation::Sum
183                | Operation::Prod
184                | Operation::BigWedge
185                | Operation::BigCap
186                | Operation::BigCup => {
187                    return format!(
188                        "<mover>{}{}</mover>",
189                        expression_to_mathml_braceless(&bi_expression.expression_1),
190                        expression_to_mathml_braceless(&bi_expression.expression_2)
191                    )
192                }
193                _ => {}
194            },
195            TokenType::Misc(Misc::Lim) => {
196                return format!(
197                    "<mover>{}{}</mover>",
198                    expression_to_mathml_braceless(&bi_expression.expression_1),
199                    expression_to_mathml_braceless(&bi_expression.expression_2)
200                )
201            }
202            _ => {}
203        }
204    } else if let Expression::Unary(unary) = bi_expression.expression_1.as_ref() {
205        if let TokenType::UnaryOperator(UnaryOperator::OBrace) = unary.operator.token_type {
206            return format!(
207                "<mover><mover>{}</mover>{}</mover>",
208                expression_to_mathml_braceless(&bi_expression.expression_1),
209                expression_to_mathml_braceless(&bi_expression.expression_2),
210            );
211        }
212    }
213
214    format!(
215        "<msup>{}{}</msup>",
216        expression_to_mathml(&bi_expression.expression_1),
217        expression_to_mathml(&bi_expression.expression_2)
218    )
219}
220
221fn sub_to_mathml(bi_expression: &crate::ast::BiExpression) -> String {
222    if let Expression::Literal(Literal::Literal(literal)) = bi_expression.expression_1.as_ref() {
223        match &literal.token_type {
224            TokenType::Operation(op) => match op {
225                Operation::Sum
226                | Operation::Prod
227                | Operation::BigWedge
228                | Operation::BigCap
229                | Operation::BigCup => {
230                    return format!(
231                        "<munder>{}{}</munder>",
232                        expression_to_mathml_braceless(&bi_expression.expression_1),
233                        expression_to_mathml_braceless(&bi_expression.expression_2)
234                    )
235                }
236                _ => {}
237            },
238            TokenType::Misc(Misc::Lim) => {
239                return format!(
240                    "<munder>{}{}</munder>",
241                    expression_to_mathml_braceless(&bi_expression.expression_1),
242                    expression_to_mathml_braceless(&bi_expression.expression_2)
243                )
244            }
245            _ => {}
246        }
247    } else if let Expression::Unary(unary) = bi_expression.expression_1.as_ref() {
248        if let TokenType::UnaryOperator(UnaryOperator::UBrace) = unary.operator.token_type {
249            return format!(
250                "<munder><munder>{}</munder>{}</munder>",
251                expression_to_mathml_braceless(&bi_expression.expression_1),
252                expression_to_mathml_braceless(&bi_expression.expression_2),
253            );
254        }
255    }
256
257    format!(
258        "<msub>{}{}</msub>",
259        expression_to_mathml(&bi_expression.expression_1),
260        expression_to_mathml(&bi_expression.expression_2)
261    )
262}
263
264fn expression_to_mathml_braceless(expression: &Expression) -> String {
265    match expression {
266        Expression::Group(group) => {
267            format!("<mrow>{}</mrow>", expressions_to_mathml(&group.expressions))
268        }
269        _ => expression_to_mathml(expression),
270    }
271}
272
273fn frac_to_mathml(frac: &crate::ast::BiExpression) -> String {
274    format!(
275        "<mfrac>{}{}</mfrac>",
276        expression_to_mathml_braceless(&frac.expression_1),
277        expression_to_mathml_braceless(&frac.expression_2)
278    )
279}
280
281fn unary_to_mathml(unary: &crate::ast::Unary) -> String {
282    match unary.operator.token_type {
283        TokenType::UnaryOperator(UnaryOperator::Hat)
284        | TokenType::UnaryOperator(UnaryOperator::Bar)
285        | TokenType::UnaryOperator(UnaryOperator::Vec)
286        | TokenType::UnaryOperator(UnaryOperator::Tilde)
287        | TokenType::UnaryOperator(UnaryOperator::Dot)
288        | TokenType::UnaryOperator(UnaryOperator::DDot)
289        | TokenType::UnaryOperator(UnaryOperator::OBrace) => {
290            let symbol = match unary.operator.token_type {
291                TokenType::UnaryOperator(UnaryOperator::Hat) => "^",
292                TokenType::UnaryOperator(UnaryOperator::Bar) => "&#xAF;",
293                TokenType::UnaryOperator(UnaryOperator::Vec) => "&#x2192;",
294                TokenType::UnaryOperator(UnaryOperator::Tilde) => "~",
295                TokenType::UnaryOperator(UnaryOperator::Dot) => ".",
296                TokenType::UnaryOperator(UnaryOperator::DDot) => "..",
297                TokenType::UnaryOperator(UnaryOperator::OBrace) => "&#x23DE;",
298                _ => "",
299            };
300
301            format!(
302                "<mover>{}<mo>{}</mo></mover>",
303                expression_to_mathml_braceless(&unary.expression),
304                symbol
305            )
306        }
307        TokenType::UnaryOperator(UnaryOperator::Ul)
308        | TokenType::UnaryOperator(UnaryOperator::UBrace) => {
309            let symbol = match unary.operator.token_type {
310                TokenType::UnaryOperator(UnaryOperator::Ul) => "&#x332;",
311                TokenType::UnaryOperator(UnaryOperator::UBrace) => "&#x23DF;",
312                _ => "",
313            };
314
315            format!(
316                "<munder>{}<mo>{}</mo></munder>",
317                expression_to_mathml_braceless(&unary.expression),
318                symbol
319            )
320        }
321        TokenType::UnaryOperator(UnaryOperator::Abs)
322        | TokenType::UnaryOperator(UnaryOperator::Floor)
323        | TokenType::UnaryOperator(UnaryOperator::Ceil)
324        | TokenType::UnaryOperator(UnaryOperator::Norm) => {
325            let (left_symbol, right_symbol) = match unary.operator.token_type {
326                TokenType::UnaryOperator(UnaryOperator::Abs) => ("|", "|"),
327                TokenType::UnaryOperator(UnaryOperator::Floor) => ("&#x230A;", "&#x230B;"),
328                TokenType::UnaryOperator(UnaryOperator::Ceil) => ("&#x2308;", "&#x2309;"),
329                TokenType::UnaryOperator(UnaryOperator::Norm) => ("&#x2225;", "&#x2225;"),
330                _ => ("", ""),
331            };
332
333            format!(
334                "<mo>{}</mo>{}<mo>{}</mo>",
335                left_symbol,
336                expression_to_mathml_braceless(&unary.expression),
337                right_symbol
338            )
339        }
340        TokenType::UnaryOperator(UnaryOperator::Cancel) => format!(
341            "<menclose notation=\"updiagonalstrike\">{}</menclose>",
342            expression_to_mathml_braceless(&unary.expression)
343        ),
344        TokenType::UnaryOperator(UnaryOperator::Sqrt) => {
345            format!(
346                "<msqrt>{}</msqrt>",
347                expression_to_mathml_braceless(&unary.expression)
348            )
349        }
350        TokenType::UnaryOperator(UnaryOperator::Text) => {
351            format!(
352                "<mtext>{}</mtext>",
353                expression_to_mathml_braceless(&unary.expression)
354            )
355        }
356        _ => "".to_string(),
357    }
358}
359
360fn literal_to_mathml(literal: &crate::ast::Literal) -> String {
361    match literal {
362        Literal::Literal(token) => token_to_mathml(token),
363        Literal::Table(table) => table_to_mathml(table),
364    }
365}
366
367fn table_to_mathml(table: &crate::ast::Table) -> String {
368    format!(
369        "<mrow>{}<mtable {}>{}</mtable>{}</mrow>",
370        l_brace_to_math_ml(&table.l_brace),
371        format_column_line(table),
372        expressions_to_mathml_table(&table.rows),
373        r_brace_to_math_ml(&table.r_brace)
374    )
375}
376
377fn format_column_line(table: &Table) -> String {
378    table
379        .rows
380        .first()
381        .map(|row| {
382            format!(
383                "columnlines=\"{}\"",
384                row.cols
385                    .iter()
386                    .enumerate()
387                    .skip(1)
388                    .map(|(index, _)| {
389                        if table.seperators.contains(&index) {
390                            "solid".to_string()
391                        } else {
392                            "none".to_string()
393                        }
394                    })
395                    .join(" ")
396            )
397        })
398        .unwrap_or("".to_string())
399}
400
401fn expressions_to_mathml_table(rows: &[TableRow]) -> String {
402    rows.iter()
403        .map(|row| {
404            format!(
405                "<mtr>{}</mtr>",
406                row.cols
407                    .iter()
408                    .map(|col| format!("<mtd>{}</mtd>", expressions_to_mathml(col)))
409                    .join("")
410            )
411        })
412        .join("")
413}
414
415fn l_brace_to_math_ml(token: &Token) -> String {
416    match &token.token_type {
417        TokenType::LBrace(lbrace) => match lbrace {
418            LBrace::LParen => "<mo>(</mo>".to_string(),
419            LBrace::LBracket => "<mo>[</mo>".to_string(),
420            LBrace::LBrace => "<mo>{</mo>".to_string(),
421            LBrace::LColonBrace => "".to_string(),
422            LBrace::LAngle => "<mo><</mo>".to_string(),
423        },
424        _ => token.span.text.to_string(),
425    }
426}
427
428fn r_brace_to_math_ml(token: &Token) -> String {
429    match &token.token_type {
430        TokenType::RBrace(rbrace) => match rbrace {
431            RBrace::RParen => "<mo>)</mo>".to_string(),
432            RBrace::RBracket => "<mo>]</mo>".to_string(),
433            RBrace::RBrace => "<mo>}</mo>".to_string(),
434            RBrace::RColonBrace => "".to_string(),
435            RBrace::RAngle => "<mo>></mo>".to_string(),
436        },
437        _ => token.span.text.to_string(),
438    }
439}
440
441fn token_to_mathml(token: &Token) -> String {
442    match &token.token_type {
443        TokenType::Symbol => format!("<mi>{}</mi>", token.span.text),
444        TokenType::Greek(greek) => greek_to_mathml(greek),
445        TokenType::Operation(op) => format!("<mo>{}</mo>", operation_to_mathml(op)),
446        TokenType::Misc(misc) => misc_to_mathml(misc),
447        TokenType::Relational(relational) => relational_to_mathml(relational),
448        TokenType::Arrow(arrow) => format!("<mo>{}</mo>", arrow_to_mathml(arrow)),
449        TokenType::Logical(logical) => logical_to_mathml(logical),
450        TokenType::Number => format!("<mn>{}</mn>", token.span.text),
451        TokenType::Text => format!("<mtext>{}</mtext>", token.span.text),
452        TokenType::Function(function) => format!("<mi>{}</mi>", function_to_mathml(function)),
453        TokenType::None => "".to_string(),
454        _ => format!("<mi>{}</mi>", token.span.text),
455    }
456}
457
458fn greek_to_mathml(greek: &Greek) -> String {
459    let symbol = match greek {
460        Greek::Alpha => "&#x3B1;",
461        Greek::Beta => "&#x3B2;",
462        Greek::Gamma => "&#x3B2;",
463        Greek::UGamma => "&#x393;",
464        Greek::Delta => "&#x3B4;",
465        Greek::UDelta => "&#x394;",
466        Greek::Epsilon => "&#x3B5;",
467        Greek::VarEpsilon => "&#x25B;",
468        Greek::Zeta => "&#x3B6;",
469        Greek::Eta => "&#x3B7;",
470        Greek::Theta => "&#x3B8;",
471        Greek::UTheta => "&#x398;",
472        Greek::VarTheta => "&#x3D1;",
473        Greek::Iota => "&#x3B9;",
474        Greek::Kappa => "&#x3BA;",
475        Greek::Lambda => "&#x3BB;",
476        Greek::ULambda => "&#x39B;",
477        Greek::Mu => "&#x3BC;",
478        Greek::Nu => "&#x3BD;",
479        Greek::Xi => "&#x3BE;",
480        Greek::UXi => "&#x39E;",
481        Greek::Pi => "&#x3C0;",
482        Greek::UPi => "&#x3A0;",
483        Greek::Rho => "&#x3C1;",
484        Greek::Sigma => "&#x3C3;",
485        Greek::USigma => "&#x3A3;",
486        Greek::Tau => "&#x3C4;",
487        Greek::Upsilon => "&#x3C5;",
488        Greek::Phi => "&#x3D5;",
489        Greek::UPhi => "&#x3A6;",
490        Greek::VarPhi => "&#x3C6;",
491        Greek::Chi => "&#x3C7;",
492        Greek::Psi => "&#x3C8;",
493        Greek::UPsi => "&#x3A8;",
494        Greek::Omega => "&#x3C9;",
495        Greek::UOmega => "&#x3A9;",
496    };
497
498    format!("<mi>{symbol}</mi>")
499}
500
501fn logical_to_mathml(logical: &Logical) -> String {
502    match logical {
503        Logical::Not
504        | Logical::Implies
505        | Logical::Iff
506        | Logical::ForAll
507        | Logical::Exists
508        | Logical::Bot
509        | Logical::Top
510        | Logical::VDash
511        | Logical::Models => {
512            let symbol = match logical {
513                Logical::Not => "&#xAC;",
514                Logical::Implies => "&#x21D2;",
515                Logical::Iff => "&#x21D4;",
516                Logical::ForAll => "&#x2200;",
517                Logical::Exists => "&#x2203;",
518                Logical::Bot => "&#x22A5;",
519                Logical::Top => "&#x22A4;",
520                Logical::VDash => "&#x22A2;",
521                Logical::Models => "&#x22A8;",
522                _ => "",
523            };
524
525            format!("<mo>{symbol}</mo>")
526        }
527        Logical::And | Logical::Or | Logical::If => {
528            let symbol = match logical {
529                Logical::And => "and",
530                Logical::Or => "or",
531                Logical::If => "if",
532                _ => "",
533            };
534
535            format!("<mrow><mspace width=\"1ex\" /><mtext>{symbol}</mtext><msapce with=\"1ex\" /></mrow>")
536        }
537    }
538}
539
540fn function_to_mathml(function: &Function) -> &'static str {
541    match function {
542        Function::Sin => "sin",
543        Function::Cos => "cos",
544        Function::Tan => "tan",
545        Function::Sec => "sec",
546        Function::Csc => "csc",
547        Function::Cot => "cot",
548        Function::Arcsin => "arcsin",
549        Function::Arccos => "arccos",
550        Function::Arctan => "arctan",
551        Function::Sinh => "sinh",
552        Function::Cosh => "cosh",
553        Function::Tanh => "tanh",
554        Function::Sech => "sech",
555        Function::Csch => "csch",
556        Function::Coth => "coth",
557        Function::Exp => "exp",
558        Function::Log => "log",
559        Function::Ln => "ln",
560        Function::Det => "det",
561        Function::Dim => "dim",
562        Function::Mod => "mod",
563        Function::Gcd => "gcd",
564        Function::Lcm => "lcm",
565        Function::Lub => "lub",
566        Function::Glb => "glb",
567        Function::Min => "min",
568        Function::Max => "max",
569        Function::F => "f",
570        Function::G => "g",
571    }
572}
573
574fn arrow_to_mathml(arrow: &Arrow) -> &'static str {
575    match arrow {
576        Arrow::UpArrow => "&#x2191;",
577        Arrow::DownArrow => "&#x2193;",
578        Arrow::RightArrow => "&#x2192;",
579        Arrow::ToArrow => "&#x2192;",
580        Arrow::RightArrowTail => "&#x21A3;",
581        Arrow::RightArrowTwoHead => "&#x21A0;",
582        Arrow::RightArrowTwoHeadTail => "&#x2916;",
583        Arrow::MapsTo => "&#x21A6;",
584        Arrow::LeftArrow => "&#x2190;",
585        Arrow::LeftRightArrow => "&#x2194;",
586        Arrow::DoubleRightArrow => "&#x21D2;",
587        Arrow::DoubleLeftArrow => "&#x21D0;",
588        Arrow::DoubleLeftRightArrow => "&#x21D4;",
589    }
590}
591
592fn relational_to_mathml(relational: &Relational) -> String {
593    match relational {
594        Relational::Equals
595        | Relational::NotEquals
596        | Relational::Lt
597        | Relational::Gt
598        | Relational::Lte
599        | Relational::Gte
600        | Relational::Prec
601        | Relational::PrecEq
602        | Relational::Succ
603        | Relational::SuccEq
604        | Relational::In
605        | Relational::NotIn
606        | Relational::Sub
607        | Relational::Sup
608        | Relational::SubEq
609        | Relational::SupEq
610        | Relational::Equiv
611        | Relational::Cong
612        | Relational::Approx
613        | Relational::Prop => {
614            let symbol = match relational {
615                Relational::Equals => "=",
616                Relational::NotEquals => "&#x2260;",
617                Relational::Lt => "&lt;",
618                Relational::Gt => "&gt;",
619                Relational::Lte => "&#x2264;",
620                Relational::Gte => "&#x2265;",
621                Relational::Prec => "&#x227A;",
622                Relational::PrecEq => "&#x2AAF;",
623                Relational::Succ => "&#x227B;",
624                Relational::SuccEq => "&#x2AB0;",
625                Relational::In => "&#x2208;",
626                Relational::NotIn => "&#x2209;",
627                Relational::Sub => "&#x2282;",
628                Relational::SubEq => "&#x2286;",
629                Relational::Sup => "&#x2283;",
630                Relational::SupEq => "&#x2287;",
631                Relational::Equiv => "&#x2261;",
632                Relational::Cong => "&#x2245;",
633                Relational::Approx => "&#x2248;",
634                Relational::Prop => "&#x221D;",
635                _ => "",
636            };
637
638            format!("<mo>{}</mo>", symbol)
639        }
640        Relational::Mlt => "<mi>m</mi><mo>&lt;</mo>".to_string(),
641        Relational::Mgt => "<mi>m</mi><mo>&gt;</mo>".to_string(),
642    }
643}
644
645fn misc_to_mathml(misc: &Misc) -> String {
646    match misc {
647        Misc::DoublePipes => {
648            return "<mrow><mo>&#x2223;</mo></mrow><mrow><mo>&#x2223;</mo></mrow>".to_string()
649        }
650        Misc::DoublePipesQuad => {
651            return "<mrow><mo>|</mo><mo>&#xA0;&#xA0;</mo><mo>|</mo></mrow>".to_string()
652        }
653        _ => {}
654    }
655
656    let symbol = match misc {
657        Misc::Int => "&#x222B;",
658        Misc::OInt => "&#x222E;",
659        Misc::Del => "&#x2202;",
660        Misc::Grad => "&#x2207;",
661        Misc::PlusMinus => "&#xB1;",
662        Misc::EmptySet => "&#x2205;",
663        Misc::Infinity => "&#x221E;",
664        Misc::Aleph => "&#x2135;",
665        Misc::Therefore => "&#x2234;",
666        Misc::Because => "&#x2235;",
667        Misc::LDots => "...",
668        Misc::CDots => "&#x22EF;",
669        Misc::VDots => "&#x22EE;",
670        Misc::DDots => "&#x22F1;",
671        Misc::Angle => "&#x2220;",
672        Misc::Frown => "&#x2322;",
673        Misc::Triangle => "&#x25B3;",
674        Misc::Diamond => "&#x22C4;",
675        Misc::Square => "&#x25A1;",
676        Misc::LFloor => "&#x230A;",
677        Misc::RFloor => "&#x230B;",
678        Misc::LCeiling => "&#x2308;",
679        Misc::RCeiling => "&#x2309;",
680        Misc::CC => "&#x2102;",
681        Misc::NN => "&#x2115;",
682        Misc::QQ => "&#x211A;",
683        Misc::RR => "&#x211D;",
684        Misc::ZZ => "&#x2124;",
685        Misc::DoublePipes => "",
686        Misc::DoublePipesQuad => "",
687        Misc::Lim => "lim",
688    };
689
690    format!("<mo>{symbol}</mo>")
691}
692
693fn operation_to_mathml(op: &Operation) -> &'static str {
694    match op {
695        Operation::Plus => "+",
696        Operation::Minus => "-",
697        Operation::CDot => "&#x22C5;",
698        Operation::Ast => "&#x2217;",
699        Operation::Star => "&#x22C6;",
700        Operation::Slash => "/",
701        Operation::Backslash => "\\",
702        Operation::Times => "&#xD7;",
703        Operation::Div => "&#xF7;",
704        Operation::LTimes => "&#x22C9;",
705        Operation::RTimes => "&#x22CA;",
706        Operation::Bowtie => "&#x22C8;",
707        Operation::Circ => "&#x2218;",
708        Operation::OPlus => "&#x2295;",
709        Operation::OTimes => "&#x2297;",
710        Operation::ODot => "&#x2299;",
711        Operation::Sum => "&#x2211;",
712        Operation::Prod => "&#x220F;",
713        Operation::Wedge => "&#x2227;",
714        Operation::BigWedge => "&#x22C0;",
715        Operation::Vee => "&#x2228;",
716        Operation::BigVee => "&#x22C1;",
717        Operation::Cap => "&#x2229;",
718        Operation::BigCap => "&#x22C2;",
719        Operation::Cup => "&#x222A;",
720        Operation::BigCup => "&#x22C3;",
721    }
722}