Skip to main content

malachite_base/unsigned_polynomial/conversion/string/
to_string.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::num::basic::unsigneds::PrimitiveUnsigned;
10use crate::strings::latex::ToLatex;
11use crate::strings::typst::ToTypst;
12use crate::unsigned_polynomial::UnsignedPolynomial;
13use crate::vars::xyz::XyzVars;
14use crate::vars::{Var, VarScheme};
15use core::fmt::{Debug, Display, Formatter, Result, Write};
16
17// The languages a polynomial can be written in.
18//
19// They differ in only three places: how the variable is spelled, whether a coefficient and a
20// variable need something between them, and how an exponent is attached. Everything else — which
21// terms there are, what order they come in, and which parts of a term are left off — is the same,
22// so one function writes all three.
23#[derive(Clone, Copy, Eq, PartialEq)]
24pub(crate) enum Language {
25    Plain,
26    Latex,
27    Typst,
28}
29
30impl<T: PrimitiveUnsigned> UnsignedPolynomial<T> {
31    // Writes a `UnsignedPolynomial<T>` whose variable is named by `var`, in one of three languages.
32    //
33    // The destination is anything that can be written to rather than a `Formatter`, since a
34    // `Formatter` cannot be made outside a `fmt` method, and the `to_*_string_with` functions need
35    // somewhere to write that is not one.
36    pub(crate) fn write_with_var<W: Write, S: VarScheme + ?Sized>(
37        &self,
38        var: Var<'_, S>,
39        language: Language,
40        w: &mut W,
41    ) -> Result {
42        let coefficients = self.coefficients_asc();
43        if coefficients.is_empty() {
44            return w.write_str("0");
45        }
46        let mut first = true;
47        for (exponent, coefficient) in coefficients.iter().enumerate().rev() {
48            if *coefficient == T::ZERO {
49                continue;
50            }
51            if first {
52                first = false;
53            } else {
54                w.write_char('+')?;
55            }
56            if exponent == 0 {
57                write!(w, "{coefficient}")?;
58                continue;
59            }
60            // A coefficient of 1 is left off, as it is when a polynomial is written by hand.
61            if *coefficient != T::ONE {
62                write!(w, "{coefficient}")?;
63                // Typeset, a coefficient sits right up against its variable, which is unambiguous
64                // because the one is a number and the other is not. The plain form is read back
65                // rather than typeset, and its `*` is what tells the two apart.
66                if language == Language::Plain {
67                    w.write_char('*')?;
68                }
69            }
70            match language {
71                Language::Plain => write!(w, "{var}")?,
72                Language::Latex => write!(w, "{}", var.to_latex())?,
73                Language::Typst => write!(w, "{}", var.to_typst())?,
74            }
75            if exponent != 1 {
76                // An exponent of one digit holds together on its own; a longer one has to be
77                // bracketed, or only its first digit would be raised. The plain form has no scripts
78                // to begin with, so it writes the digits and nothing else.
79                match language {
80                    Language::Plain => write!(w, "^{exponent}")?,
81                    _ if exponent < 10 => write!(w, "^{exponent}")?,
82                    Language::Latex => write!(w, "^{{{exponent}}}")?,
83                    Language::Typst => write!(w, "^({exponent})")?,
84                }
85            }
86        }
87        Ok(())
88    }
89}
90
91impl<T: PrimitiveUnsigned> Display for UnsignedPolynomial<T> {
92    /// Converts a [`UnsignedPolynomial`] to a [`String`].
93    ///
94    /// The variable is called `x`.
95    /// [`to_string_with`](crate::polynomial::Polynomial::to_string_with) is the way to call it
96    /// something else.
97    ///
98    /// The terms are written in order of decreasing degree and joined with `+`. A term is its
99    /// coefficient, then `*`, then the variable, then `^` and the exponent; but a coefficient of 1
100    /// is left off along with its `*`, an exponent of 1 is left off along with its `^`, and the
101    /// constant term is its coefficient alone. The zero polynomial, which has no terms at all, is
102    /// `0`.
103    ///
104    /// The syntax is the one [Azurite](https://github.com/mhogrefe/azurite) writes polynomials in,
105    /// and holds no characters that [`char_is_reserved`](crate::vars::char_is_reserved) allows in a
106    /// variable's name, so a polynomial can be read back whatever its variable is called.
107    ///
108    /// # Worst-case complexity
109    /// $T(n) = O(n \log n \log\log n)$
110    ///
111    /// $M(n) = O(n \log n)$
112    ///
113    /// where $T$ is time, $M$ is additional memory, and $n$ is the sum of the bits of the
114    /// coefficients.
115    ///
116    /// # Examples
117    /// ```
118    /// use core::str::FromStr;
119    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
120    ///
121    /// assert_eq!(
122    ///     UnsignedPolynomial::<u64>::from_str("x^2+3*x+2")
123    ///         .unwrap()
124    ///         .to_string(),
125    ///     "x^2+3*x+2"
126    /// );
127    /// assert_eq!(
128    ///     UnsignedPolynomial::<u64>::from_str("0")
129    ///         .unwrap()
130    ///         .to_string(),
131    ///     "0"
132    /// );
133    /// assert_eq!(
134    ///     UnsignedPolynomial::<u64>::from_str("5")
135    ///         .unwrap()
136    ///         .to_string(),
137    ///     "5"
138    /// );
139    /// assert_eq!(
140    ///     UnsignedPolynomial::<u64>::from_str("x")
141    ///         .unwrap()
142    ///         .to_string(),
143    ///     "x"
144    /// );
145    /// assert_eq!(
146    ///     UnsignedPolynomial::<u64>::from_str("2*x^3")
147    ///         .unwrap()
148    ///         .to_string(),
149    ///     "2*x^3"
150    /// );
151    ///
152    /// // The terms come out in decreasing degree, whatever order they went in, and an exponent
153    /// // of 1 is left off.
154    /// assert_eq!(
155    ///     UnsignedPolynomial::<u64>::from_str("2+3*x+x^2")
156    ///         .unwrap()
157    ///         .to_string(),
158    ///     "x^2+3*x+2"
159    /// );
160    /// assert_eq!(
161    ///     UnsignedPolynomial::<u64>::from_str("x^1")
162    ///         .unwrap()
163    ///         .to_string(),
164    ///     "x"
165    /// );
166    /// ```
167    #[inline]
168    fn fmt(&self, f: &mut Formatter) -> Result {
169        self.write_with_var(Var::new(&XyzVars, 0), Language::Plain, f)
170    }
171}
172
173impl<T: PrimitiveUnsigned> Debug for UnsignedPolynomial<T> {
174    /// Converts an [`UnsignedPolynomial`] to a [`String`].
175    ///
176    /// This is the same as the [`Display::fmt`] implementation, so that a collection of
177    /// [`UnsignedPolynomial`]s is written the same way its elements are displayed.
178    ///
179    /// # Worst-case complexity
180    /// $T(n) = O(n \log n \log\log n)$
181    ///
182    /// $M(n) = O(n \log n)$
183    ///
184    /// where $T$ is time, $M$ is additional memory, and $n$ is the sum of the bits of the
185    /// coefficients.
186    ///
187    /// # Examples
188    /// ```
189    /// use core::str::FromStr;
190    /// use malachite_base::strings::ToDebugString;
191    /// use malachite_base::unsigned_polynomial::UnsignedPolynomial;
192    ///
193    /// let xs = vec![
194    ///     UnsignedPolynomial::<u8>::from_str("x^2+3*x+2").unwrap(),
195    ///     UnsignedPolynomial::<u8>::from_str("0").unwrap(),
196    ///     UnsignedPolynomial::<u8>::from_str("5").unwrap(),
197    /// ];
198    /// assert_eq!(xs[0].to_debug_string(), "x^2+3*x+2");
199    /// assert_eq!(xs[1].to_debug_string(), "0");
200    /// assert_eq!(xs[2].to_debug_string(), "5");
201    /// assert_eq!(xs.to_debug_string(), "[x^2+3*x+2, 0, 5]");
202    /// ```
203    #[inline]
204    fn fmt(&self, f: &mut Formatter) -> Result {
205        Display::fmt(self, f)
206    }
207}