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}