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;