Skip to main content

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}