Skip to main content

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