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}