Skip to main content

malachite_base/vars/
mod.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 crate::chars::typst::fmt_typst_chars;
11use crate::strings::latex::ToLatex;
12use crate::strings::typst::ToTypst;
13use alloc::string::ToString;
14use core::fmt::{Debug, Display, Formatter, Result, Write};
15
16/// Determines whether a [`char`] is reserved, and so may not appear in a variable's name.
17///
18/// The reserved characters are the ASCII digits; the operators `+`, `-`, `*`, `/`, and `^`; the
19/// parentheses `(` and `)`; the comma; and whitespace. Between them these are everything that
20/// punctuates a polynomial written out in full, from the coefficients and exponents to the
21/// operators joining the terms, the parentheses grouping them, and the commas separating one
22/// variable from the next.
23///
24/// Keeping them out of the names is what lets a polynomial be written without separators and still
25/// be read back: in `3*x^2*y`, the variables are exactly the longest runs of unreserved characters,
26/// so no lookahead or escaping is needed to find where a name ends.
27///
28/// # Worst-case complexity
29/// Constant time and additional memory.
30///
31/// # Examples
32/// ```
33/// use malachite_base::vars::char_is_reserved;
34///
35/// assert_eq!(char_is_reserved('x'), false);
36/// assert_eq!(char_is_reserved('α'), false);
37/// assert_eq!(char_is_reserved('₀'), false);
38/// assert_eq!(char_is_reserved('0'), true);
39/// assert_eq!(char_is_reserved('^'), true);
40/// assert_eq!(char_is_reserved(' '), true);
41/// ```
42pub const fn char_is_reserved(c: char) -> bool {
43    c.is_ascii_digit()
44        || c.is_whitespace()
45        || matches!(c, '+' | '-' | '*' | '/' | '^' | '(' | ')' | ',')
46}
47
48// Writes a variable's plain name as a LaTeX math-mode fragment, for a scheme that has nothing
49// better to say.
50//
51// A one-letter name is a variable in the ordinary sense, and is written bare so that LaTeX sets it
52// in math italics. A longer name is set upright, as a multi-letter identifier should be, and goes
53// through the `char` machinery, which escapes whatever it holds.
54fn fmt_name_latex(name: &str, f: &mut Formatter) -> Result {
55    let mut cs = name.chars();
56    match (cs.next(), cs.next()) {
57        (Some(c), None) if c.is_ascii_alphabetic() => f.write_char(c),
58        _ => fmt_latex_chars(name.chars(), f),
59    }
60}
61
62// Writes a variable's plain name as a Typst math-mode fragment, for a scheme that has nothing
63// better to say. The reasoning is the same as `fmt_name_latex`'s.
64fn fmt_name_typst(name: &str, f: &mut Formatter) -> Result {
65    let mut cs = name.chars();
66    match (cs.next(), cs.next()) {
67        (Some(c), None) if c.is_ascii_alphabetic() => f.write_char(c),
68        _ => fmt_typst_chars(name.chars(), f),
69    }
70}
71
72/// A scheme for naming variables.
73///
74/// A polynomial's variables are numbered rather than named: the first is variable 0, the second is
75/// variable 1, and so on. A scheme is what turns those numbers into names, and names back into
76/// numbers, so that one polynomial may be shown as $x_0 + x_1$, or as $x + y$, or with whatever
77/// names the caller has in mind, while the polynomial itself knows nothing about any of them.
78///
79/// A scheme names a variable in three languages: as plain text, as LaTeX, and as Typst. They are
80/// the same name differently set, so that [`IndexedVars`](indexed::IndexedVars) writes `x₀` as
81/// plain text and `x_0` in both LaTeX and Typst. Only the plain name is read back;
82/// [`parse_var`](VarScheme::parse_var) is its inverse.
83///
84/// # Contract
85/// For every index below [`capacity`](VarScheme::capacity), an implementation guarantees that the
86/// plain name
87/// - is read back by [`parse_var`](VarScheme::parse_var) as the index it was written for;
88/// - is not empty;
89/// - holds no character that [`char_is_reserved`] rejects;
90/// - belongs to that variable alone.
91///
92/// The last three are what let a polynomial be written without separators and still be read back.
93/// `var_scheme_properties`, in the test utilities, checks all four.
94///
95/// # Typst
96/// Typst reads a run of two or more letters as a single identifier, so an `x` and a `y` written
97/// side by side are the unknown `xy` rather than a product. Anything that writes several variables
98/// in a row must separate them — a space is enough, and so is anything that is not a letter, such
99/// as the `_` or `^` of a script.
100pub trait VarScheme {
101    /// The number of variables the scheme can name, or `None` if it can name any number of them.
102    ///
103    /// Naming a variable whose index is not below this is a panic, not an error: a scheme is asked
104    /// for a name by something that already knows how many variables it has.
105    fn capacity(&self) -> Option<usize>;
106
107    /// Writes a variable's plain name.
108    ///
109    /// This is the name that [`parse_var`](VarScheme::parse_var) reads back.
110    ///
111    /// # Panics
112    /// Panics if `index` is not less than [`capacity`](VarScheme::capacity).
113    fn fmt_var(&self, index: usize, f: &mut Formatter) -> Result;
114
115    /// Reads a variable's index from its plain name.
116    ///
117    /// The whole string must be the name of one variable; a string that is more than a name, or
118    /// less than one, or the name of a variable the scheme cannot reach, gives `None`.
119    fn parse_var(&self, name: &str) -> Option<usize>;
120
121    /// Writes a variable's name as a LaTeX math-mode fragment.
122    ///
123    /// The default writes the plain name, as a bare letter when it is a single ASCII letter — so
124    /// that LaTeX sets it in math italics, as a variable should be — and as upright text
125    /// otherwise, with LaTeX's special characters escaped. A scheme whose names are not letters, or
126    /// that has a better spelling than the one its plain name suggests, overrides this.
127    ///
128    /// # Panics
129    /// Panics if `index` is not less than [`capacity`](VarScheme::capacity).
130    #[inline]
131    fn fmt_var_latex(&self, index: usize, f: &mut Formatter) -> Result {
132        fmt_name_latex(&Var::new(self, index).to_string(), f)
133    }
134
135    /// Writes a variable's name as a Typst math-mode fragment.
136    ///
137    /// The default writes the plain name, as a bare letter when it is a single ASCII letter — so
138    /// that Typst sets it in math italics, as a variable should be — and as a quoted string
139    /// otherwise. A scheme whose names are not letters, or that has a better spelling than the one
140    /// its plain name suggests, overrides this.
141    ///
142    /// # Panics
143    /// Panics if `index` is not less than [`capacity`](VarScheme::capacity).
144    #[inline]
145    fn fmt_var_typst(&self, index: usize, f: &mut Formatter) -> Result {
146        fmt_name_typst(&Var::new(self, index).to_string(), f)
147    }
148
149    /// Gives a handle to one of the scheme's variables.
150    ///
151    /// The handle is what carries the name around: it implements [`Display`], [`ToLatex`], and
152    /// [`ToTypst`], so that a variable can be written wherever any of those is expected. Nothing is
153    /// checked here; an index past the scheme's [`capacity`](VarScheme::capacity) panics when the
154    /// handle is written rather than when it is made.
155    ///
156    /// A scheme behind a `dyn` has no `var` of its own, since the handle's type mentions the
157    /// scheme's; [`Var::new`] does the same thing for one.
158    ///
159    /// # Worst-case complexity
160    /// Constant time and additional memory.
161    ///
162    /// # Examples
163    /// ```
164    /// use malachite_base::vars::VarScheme;
165    /// use malachite_base::vars::xyz::XyzVars;
166    ///
167    /// assert_eq!(XyzVars.var(0).to_string(), "x");
168    /// assert_eq!(XyzVars.var(1).to_string(), "y");
169    /// ```
170    #[inline]
171    fn var(&self, index: usize) -> Var<'_, Self>
172    where
173        Self: Sized,
174    {
175        Var::new(self, index)
176    }
177}
178
179/// One variable of a [`VarScheme`], which is to say a scheme together with an index.
180///
181/// It is returned by [`VarScheme::var`], and it is what a variable's name is written from: it
182/// implements [`Display`], [`ToLatex`], and [`ToTypst`].
183pub struct Var<'a, S: VarScheme + ?Sized> {
184    scheme: &'a S,
185    index: usize,
186}
187
188impl<'a, S: VarScheme + ?Sized> Var<'a, S> {
189    /// Makes a handle to one of a scheme's variables.
190    ///
191    /// [`VarScheme::var`] is the shorter way to say this, and works whenever the scheme's type is
192    /// known. This one also works for a scheme behind a `dyn`.
193    ///
194    /// # Worst-case complexity
195    /// Constant time and additional memory.
196    ///
197    /// # Examples
198    /// ```
199    /// use malachite_base::vars::abc::AbcVars;
200    /// use malachite_base::vars::{Var, VarScheme};
201    ///
202    /// let scheme: &dyn VarScheme = &AbcVars;
203    /// assert_eq!(Var::new(scheme, 2).to_string(), "c");
204    /// ```
205    pub const fn new(scheme: &'a S, index: usize) -> Self {
206        Var { scheme, index }
207    }
208
209    /// The variable's index.
210    ///
211    /// # Worst-case complexity
212    /// Constant time and additional memory.
213    ///
214    /// # Examples
215    /// ```
216    /// use malachite_base::vars::VarScheme;
217    /// use malachite_base::vars::xyz::XyzVars;
218    ///
219    /// assert_eq!(XyzVars.var(3).index(), 3);
220    /// ```
221    pub const fn index(&self) -> usize {
222        self.index
223    }
224
225    /// The scheme the variable belongs to.
226    ///
227    /// # Worst-case complexity
228    /// Constant time and additional memory.
229    ///
230    /// # Examples
231    /// ```
232    /// use malachite_base::vars::VarScheme;
233    /// use malachite_base::vars::abc::AbcVars;
234    ///
235    /// assert_eq!(AbcVars.var(3).scheme().capacity(), Some(26));
236    /// ```
237    pub const fn scheme(&self) -> &'a S {
238        self.scheme
239    }
240}
241
242impl<S: VarScheme + ?Sized> Clone for Var<'_, S> {
243    #[inline]
244    fn clone(&self) -> Self {
245        *self
246    }
247}
248
249impl<S: VarScheme + ?Sized> Copy for Var<'_, S> {}
250
251impl<S: VarScheme + ?Sized> Display for Var<'_, S> {
252    /// Converts a variable to a [`String`].
253    ///
254    /// This is the plain name, the one [`VarScheme::parse_var`] reads back.
255    ///
256    /// # Worst-case complexity
257    /// Same as the time and additional memory complexity of `fmt_var` for the scheme.
258    ///
259    /// # Examples
260    /// ```
261    /// use malachite_base::vars::VarScheme;
262    /// use malachite_base::vars::indexed::IndexedVars;
263    ///
264    /// assert_eq!(IndexedVars.var(10).to_string(), "x₁₀");
265    /// ```
266    #[inline]
267    fn fmt(&self, f: &mut Formatter) -> Result {
268        self.scheme.fmt_var(self.index, f)
269    }
270}
271
272impl<S: VarScheme + ?Sized> Debug for Var<'_, S> {
273    /// Converts a variable to a [`String`].
274    ///
275    /// This is the same as the [`Display`] implementation: a variable is its name, and a scheme is
276    /// not required to have a depiction of its own.
277    ///
278    /// # Worst-case complexity
279    /// Same as the time and additional memory complexity of `fmt_var` for the scheme.
280    ///
281    /// # Examples
282    /// ```
283    /// use malachite_base::strings::ToDebugString;
284    /// use malachite_base::vars::VarScheme;
285    /// use malachite_base::vars::indexed::IndexedVars;
286    ///
287    /// assert_eq!(IndexedVars.var(10).to_debug_string(), "x₁₀");
288    /// ```
289    #[inline]
290    fn fmt(&self, f: &mut Formatter) -> Result {
291        Display::fmt(self, f)
292    }
293}
294
295impl<S: VarScheme + ?Sized> ToLatex for Var<'_, S> {
296    /// Writes a variable as a LaTeX math-mode fragment.
297    ///
298    /// # Worst-case complexity
299    /// Same as the time and additional memory complexity of `fmt_var_latex` for the scheme.
300    ///
301    /// # Examples
302    /// ```
303    /// use malachite_base::strings::latex::ToLatex;
304    /// use malachite_base::vars::VarScheme;
305    /// use malachite_base::vars::greek::GreekVars;
306    /// use malachite_base::vars::indexed::IndexedVars;
307    ///
308    /// assert_eq!(IndexedVars.var(10).to_latex_string(), "x_{10}");
309    /// assert_eq!(GreekVars.var(0).to_latex_string(), r"\alpha");
310    /// ```
311    /// Those fragments render as $x_{10}$ and $\alpha$.
312    #[inline]
313    fn fmt_latex(&self, f: &mut Formatter) -> Result {
314        self.scheme.fmt_var_latex(self.index, f)
315    }
316}
317
318impl<S: VarScheme + ?Sized> ToTypst for Var<'_, S> {
319    /// Writes a variable as a Typst math-mode fragment.
320    ///
321    /// # Worst-case complexity
322    /// Same as the time and additional memory complexity of `fmt_var_typst` for the scheme.
323    ///
324    /// # Examples
325    /// ```
326    /// use malachite_base::strings::typst::ToTypst;
327    /// use malachite_base::vars::VarScheme;
328    /// use malachite_base::vars::greek::GreekVars;
329    /// use malachite_base::vars::indexed::IndexedVars;
330    ///
331    /// assert_eq!(IndexedVars.var(10).to_typst_string(), "x_(10)");
332    /// assert_eq!(GreekVars.var(0).to_typst_string(), "α");
333    /// ```
334    #[inline]
335    fn fmt_typst(&self, f: &mut Formatter) -> Result {
336        self.scheme.fmt_var_typst(self.index, f)
337    }
338}
339
340/// [`AbcVars`](abc::AbcVars) and [`AbcCapsVars`](abc::AbcCapsVars), which name variables with the
341/// letters of the alphabet in their usual order.
342pub mod abc;
343/// [`GreekVars`](greek::GreekVars) and [`GreekCapsVars`](greek::GreekCapsVars), which name
344/// variables with the letters of the Greek alphabet.
345pub mod greek;
346/// [`IndexedVars`](indexed::IndexedVars) and [`IndexedCapsVars`](indexed::IndexedCapsVars), which
347/// name variables with a letter and a subscript.
348pub mod indexed;
349/// [`ListVars`](list::ListVars), which names variables with names the caller supplies.
350pub mod list;
351/// [`XyzVars`](xyz::XyzVars) and [`XyzCapsVars`](xyz::XyzCapsVars), which name variables with the
352/// letters of the alphabet, beginning at the end.
353pub mod xyz;