malachite_base/strings/latex.rs
1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::chars::latex::fmt_latex_chars;
10use alloc::string::{String, ToString};
11use core::fmt::{Display, Formatter, Result};
12
13/// Converts a value to a LaTeX math-mode fragment.
14///
15/// The output is a fragment rather than a complete expression: it carries no `$`, `\(`, `\[`, or
16/// environment of its own, leaving those to the caller. That is what lets one fragment be embedded
17/// in another, so that a value built out of smaller values can write its parts directly.
18///
19/// Every implementation guarantees that its output
20/// - is valid wherever LaTeX is in math mode, and remains valid when wrapped in a group, so that
21/// `{`output`}` is well-formed;
22/// - leaves no LaTeX state behind: braces are balanced, and nothing is defined or redefined.
23pub trait ToLatex {
24 /// Writes a value as a LaTeX math-mode fragment.
25 ///
26 /// This is the method implementors define. It takes a [`Formatter`] rather than returning a
27 /// `String` so that a value can write its parts into a caller's buffer, which is what makes a
28 /// fragment embeddable without an allocation per level of nesting.
29 ///
30 /// # Examples
31 /// ```
32 /// use malachite_base::strings::latex::ToLatex;
33 /// use std::fmt::{Display, Formatter, Result};
34 ///
35 /// // A type that embeds another value's fragment inside its own.
36 /// struct Negated(i32);
37 ///
38 /// impl Display for Negated {
39 /// fn fmt(&self, f: &mut Formatter) -> Result {
40 /// f.write_str("-\\left(")?;
41 /// self.0.fmt_latex(f)?;
42 /// f.write_str("\\right)")
43 /// }
44 /// }
45 ///
46 /// assert_eq!(Negated(5).to_string(), r"-\left(5\right)");
47 /// ```
48 /// That fragment renders as $-\left(5\right)$.
49 fn fmt_latex(&self, f: &mut Formatter) -> Result;
50
51 /// Converts a value to a LaTeX math-mode fragment.
52 ///
53 /// The returned [`LatexWrapper`] implements [`Display`], so it can be converted to a `String`
54 /// with `to_string`, or written directly with `write!` and friends.
55 ///
56 /// # Worst-case complexity
57 /// Constant time and additional memory.
58 ///
59 /// # Examples
60 /// ```
61 /// use malachite_base::strings::latex::ToLatex;
62 ///
63 /// assert_eq!(123u32.to_latex_string(), "123");
64 /// assert_eq!((-45i16).to_latex_string(), "-45");
65 ///
66 /// // The output is a fragment, so it can be embedded in a larger expression.
67 /// assert_eq!(format!("x^{{{}}}", 10u8.to_latex()), "x^{10}");
68 /// ```
69 /// Those fragments render as $123$, $-45$, and, once embedded, $x^{10}$.
70 #[inline]
71 fn to_latex(&self) -> LatexWrapper<'_, Self>
72 where
73 Self: Sized,
74 {
75 LatexWrapper { x: self }
76 }
77
78 /// Converts a value to a LaTeX math-mode fragment, as a [`String`].
79 ///
80 /// This is `to_latex().to_string()`, which is what a caller who wants the fragment itself,
81 /// rather than something to write into a [`Formatter`], would otherwise have to say.
82 ///
83 /// # Worst-case complexity
84 /// Same as the time and additional memory complexity of `fmt_latex` for `Self`.
85 ///
86 /// # Examples
87 /// ```
88 /// use malachite_base::strings::latex::ToLatex;
89 ///
90 /// assert_eq!(123u32.to_latex_string(), "123");
91 /// assert_eq!((-45i16).to_latex_string(), "-45");
92 /// assert_eq!("100% α".to_latex_string(), r"\text{100\% }\alpha");
93 /// ```
94 #[inline]
95 fn to_latex_string(&self) -> String
96 where
97 Self: Sized,
98 {
99 self.to_latex().to_string()
100 }
101}
102
103/// A `struct` that can be used to format a value as a LaTeX math-mode fragment.
104///
105/// It is returned by [`ToLatex::to_latex`].
106pub struct LatexWrapper<'a, T: ToLatex> {
107 pub(crate) x: &'a T,
108}
109
110impl<T: ToLatex> Display for LatexWrapper<'_, T> {
111 #[inline]
112 fn fmt(&self, f: &mut Formatter) -> Result {
113 self.x.fmt_latex(f)
114 }
115}
116
117impl ToLatex for &str {
118 /// Writes a string slice as a LaTeX math-mode fragment.
119 ///
120 /// The fragment depicts the string: ordinary characters are gathered into `\text{...}` groups
121 /// and typeset as themselves, with LaTeX's special characters escaped, while characters that
122 /// LaTeX spells with a math-mode macro are written as that macro, outside any group. A string
123 /// mixing the two therefore comes out as, for example, `\text{100\% }\alpha`. No quotation
124 /// marks are added; the fragment is the string's content and nothing else.
125 ///
126 /// A run of superscript or subscript characters becomes a single script, so that `"2¹⁰"` is
127 /// two raised to the tenth rather than two raised to the first and then to the zeroth. A run of
128 /// one kind does not run into the next: `"x¹₂"` keeps its one beside its two rather than
129 /// stacking them.
130 ///
131 /// The empty string becomes `\text{}` rather than nothing at all.
132 ///
133 /// # Worst-case complexity
134 /// $T(n) = O(n)$
135 ///
136 /// $M(n) = O(1)$
137 ///
138 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.chars().count()`.
139 ///
140 /// # Examples
141 /// ```
142 /// use malachite_base::strings::latex::ToLatex;
143 ///
144 /// assert_eq!("hello".to_latex_string(), r"\text{hello}");
145 /// assert_eq!("100%".to_latex_string(), r"\text{100\%}");
146 /// assert_eq!("100% α".to_latex_string(), r"\text{100\% }\alpha");
147 /// assert_eq!("A ≤ B".to_latex_string(), r"\text{A }\leq\text{ B}");
148 /// assert_eq!("--flag".to_latex_string(), r"\text{-{}-flag}");
149 /// assert_eq!("2¹⁰".to_latex_string(), r"\text{2}^{10}");
150 /// assert_eq!("H₂O".to_latex_string(), r"\text{H}_2\text{O}");
151 /// ```
152 ///
153 /// | value | fragment | renders as |
154 /// |------------|--------------------------|--------------------------|
155 /// | `"hello"` | `\text{hello}` | $\text{hello}$ |
156 /// | `"100%"` | `\text{100\%}` | $\text{100\\%}$ |
157 /// | `"100% α"` | `\text{100\% }\alpha` | $\text{100\\% }\alpha$ |
158 /// | `"A ≤ B"` | `\text{A }\leq\text{ B}` | $\text{A }\leq\text{ B}$ |
159 /// | `"--flag"` | `\text{-{}-flag}` | $\text{-{}-flag}$ |
160 /// | `"2¹⁰"` | `\text{2}^{10}` | $\text{2}^{10}$ |
161 /// | `"H₂O"` | `\text{H}_2\text{O}` | $\text{H}_2\text{O}$ |
162 #[inline]
163 fn fmt_latex(&self, f: &mut Formatter) -> Result {
164 fmt_latex_chars(self.chars(), f)
165 }
166}
167
168impl ToLatex for String {
169 /// Writes a [`String`] as a LaTeX math-mode fragment.
170 ///
171 /// This is the same as the [`&str`] implementation.
172 ///
173 /// The fragment depicts the string: ordinary characters are gathered into `\text{...}` groups
174 /// and typeset as themselves, with LaTeX's special characters escaped, while characters that
175 /// LaTeX spells with a math-mode macro are written as that macro, outside any group. A string
176 /// mixing the two therefore comes out as, for example, `\text{100\% }\alpha`. No quotation
177 /// marks are added; the fragment is the string's content and nothing else.
178 ///
179 /// A run of superscript or subscript characters becomes a single script, so that `"2¹⁰"` is
180 /// two raised to the tenth rather than two raised to the first and then to the zeroth. A run of
181 /// one kind does not run into the next: `"x¹₂"` keeps its one beside its two rather than
182 /// stacking them.
183 ///
184 /// The empty string becomes `\text{}` rather than nothing at all.
185 ///
186 /// # Worst-case complexity
187 /// $T(n) = O(n)$
188 ///
189 /// $M(n) = O(1)$
190 ///
191 /// where $T$ is time, $M$ is additional memory, and $n$ is `self.chars().count()`.
192 ///
193 /// # Examples
194 /// ```
195 /// use malachite_base::strings::latex::ToLatex;
196 ///
197 /// assert_eq!("hello".to_string().to_latex_string(), r"\text{hello}");
198 /// assert_eq!("100%".to_string().to_latex_string(), r"\text{100\%}");
199 /// assert_eq!(
200 /// "100% α".to_string().to_latex_string(),
201 /// r"\text{100\% }\alpha"
202 /// );
203 /// assert_eq!(
204 /// "A ≤ B".to_string().to_latex_string(),
205 /// r"\text{A }\leq\text{ B}"
206 /// );
207 /// assert_eq!("--flag".to_string().to_latex_string(), r"\text{-{}-flag}");
208 /// assert_eq!("2¹⁰".to_string().to_latex_string(), r"\text{2}^{10}");
209 /// assert_eq!("H₂O".to_string().to_latex_string(), r"\text{H}_2\text{O}");
210 /// ```
211 ///
212 /// | value | fragment | renders as |
213 /// |------------|--------------------------|--------------------------|
214 /// | `"hello"` | `\text{hello}` | $\text{hello}$ |
215 /// | `"100%"` | `\text{100\%}` | $\text{100\\%}$ |
216 /// | `"100% α"` | `\text{100\% }\alpha` | $\text{100\\% }\alpha$ |
217 /// | `"A ≤ B"` | `\text{A }\leq\text{ B}` | $\text{A }\leq\text{ B}$ |
218 /// | `"--flag"` | `\text{-{}-flag}` | $\text{-{}-flag}$ |
219 /// | `"2¹⁰"` | `\text{2}^{10}` | $\text{2}^{10}$ |
220 /// | `"H₂O"` | `\text{H}_2\text{O}` | $\text{H}_2\text{O}$ |
221 #[inline]
222 fn fmt_latex(&self, f: &mut Formatter) -> Result {
223 fmt_latex_chars(self.chars(), f)
224 }
225}
226
227impl<T: ToLatex> ToLatex for &T {
228 /// Writes a reference as a LaTeX math-mode fragment.
229 ///
230 /// The fragment is the referent's own, so a reference is invisible: `&5u8` and `5u8` have the
231 /// same fragment. That is what lets a value be written without being dereferenced first, and a
232 /// collection of references be written at all.
233 ///
234 /// [`&str`] and slices have implementations of their own rather than reaching this one, since
235 /// their referents are unsized. They write the same fragments either way.
236 ///
237 /// # Worst-case complexity
238 /// Same as the time and additional memory complexity of `fmt_latex` for `T`.
239 ///
240 /// # Examples
241 /// ```
242 /// use malachite_base::strings::latex::ToLatex;
243 ///
244 /// // A reference is invisible, which is what lets a collection of references be written.
245 /// assert_eq!(
246 /// vec![&1u8, &2u8].to_latex_string(),
247 /// vec![1u8, 2u8].to_latex_string()
248 /// );
249 ///
250 /// // A method call on a reference resolves to the referent's own implementation, so this is
251 /// // reached through a generic context instead.
252 /// fn fragment<T: ToLatex>(x: T) -> String {
253 /// x.to_latex_string()
254 /// }
255 /// let n = 5u8;
256 /// let n_ref: &u8 = &n;
257 /// assert_eq!(fragment(n_ref), "5");
258 /// ```
259 #[inline]
260 fn fmt_latex(&self, f: &mut Formatter) -> Result {
261 (**self).fmt_latex(f)
262 }
263}