polynomint/lib.rs
1pub mod add;
2pub mod index;
3pub mod iter;
4pub mod math;
5pub mod mul;
6pub mod rem;
7pub mod sub;
8
9/// A wrapper struct around a `Vec<isize>` which treats the entries of the `Vec` as the coefficients
10/// of a polynomial.
11///
12/// # Examples
13/// ```
14/// use polynomint::{Polynomial, poly};
15///
16/// let quadratic = poly![1, 2, 1]; // x^2 + 2x + 1
17/// let linear = poly![-6, 1]; // x - 6
18/// assert_eq!(&quadratic * 5, poly![5, 10, 5]);
19/// assert_eq!(&quadratic * &linear, poly![-6, -11, -4, 1]);
20///
21/// let mut resultant = &quadratic * &linear;
22/// resultant %= 5;
23/// assert_eq!(resultant, poly![-1, -1, -4, 1]);
24///
25/// let resultant2 = (&quadratic * &linear).rem_euclid(5);
26/// assert_eq!(resultant2, poly![4, 4, 1, 1]);
27/// ```
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct Polynomial {
30 coeffs: Vec<isize>,
31}
32
33impl Polynomial {
34 /// Creates a polynomial with the given coefficients, stored in increasing order, with
35 /// any trailing (higher-degree) zeroes removed.
36 ///
37 /// # Examples
38 /// ```
39 /// use polynomint::Polynomial;
40 ///
41 /// let quadratic = Polynomial::new(vec![1, 2, 3]); // 3x^2 + 2x + 1
42 /// let cubic = Polynomial::new(vec![8, 12, 6, 1]); // x^3 + 6x^2 + 12x + 8
43 /// ```
44 pub fn new(coeffs: Vec<isize>) -> Self {
45 let mut output = Self { coeffs };
46 output.reduce();
47 output
48 }
49
50 /// Creates the zero polynomial, which is stored internally as an empty vector.
51 pub fn zero() -> Self {
52 Self { coeffs: Vec::new() }
53 }
54
55 /// Creates a constant polynomial with coefficient equal to the argument passed;
56 /// if the argument passed is zero, it is stored internally as an empty vector
57 /// to match `zero()`.
58 pub fn constant(i: isize) -> Self {
59 if i == 0 {
60 Self::zero()
61 } else {
62 Self { coeffs: vec![i] }
63 }
64 }
65 /// Gives the highest power which has a nonzero coefficient; constants are degree zero, except
66 /// the constant polynomial 0, which has degree -1.
67 ///
68 /// # Examples
69 /// ```
70 /// use polynomint::{Polynomial, poly};
71 ///
72 /// let zero = Polynomial::zero();
73 /// let alt_zero = Polynomial::constant(0);
74 /// let three = Polynomial::constant(3);
75 /// let classic = poly![1, 2, 1, 0, 0]; // x^2 + 2x + 1
76 ///
77 /// assert_eq!(zero.degree(), -1);
78 /// assert_eq!(alt_zero.degree(), -1);
79 /// assert_eq!(three.degree(), 0);
80 /// assert_eq!(classic.degree(), 2);
81 /// ```
82 pub fn degree(&self) -> isize {
83 (self.coeffs.len() as isize) - 1
84 }
85
86 /// Checks whether a polynomial is the zero polynomial.
87 ///
88 /// # Examples
89 /// ```
90 /// use polynomint::{Polynomial, poly};
91 ///
92 /// let zero = Polynomial::zero();
93 /// let also_zero = Polynomial::constant(0);
94 /// let yet_again_zero = poly![0, 0, 0, 0];
95 /// let even_more_zero = poly![1, 2, 1] - poly![1, 2, 1];
96 /// let not_zero = poly![0, 1];
97 ///
98 /// assert!(zero.is_zero());
99 /// assert!(also_zero.is_zero());
100 /// assert!(yet_again_zero.is_zero());
101 /// assert!(even_more_zero.is_zero());
102 /// assert!(!not_zero.is_zero());
103 /// ```
104 pub fn is_zero(&self) -> bool {
105 self.degree() == -1
106 }
107
108 /// Returns a reference to `self`'s vector of coefficients, in order of ascending
109 /// degree (`poly.coeffs()[n]` is the `x^n` coefficient of `poly`).
110 pub fn coeffs(&self) -> &Vec<isize> {
111 &(self.coeffs)
112 }
113
114 /// Returns a mutable reference to `self`'s vector of coefficients, in order of
115 /// ascending degree (`poly.coeffs_mut()[n]` is the `x^n` coefficient of `poly`).
116 pub fn coeffs_mut(&mut self) -> &mut Vec<isize> {
117 &mut (self.coeffs)
118 }
119
120 /// Removes trailing zeroes from a polynomial. Used to make sure the API only exposes
121 /// polynomials with no stored zeroes of higher-order, both to keep them as lightweight
122 /// as possible and because this invariant is taken advantage of by functions like
123 /// degree().
124 fn reduce(&mut self) {
125 while self.coeffs.last() == Some(&0) {
126 self.coeffs.pop();
127 }
128 }
129}
130
131impl std::fmt::Display for Polynomial {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 let mut s = String::new();
134 // if our polynomial is zero, the big-ass else block doesn't do anything, so
135 // we just separately handle that case
136 if self.is_zero() {
137 s += &format!("{}", 0);
138 } else {
139 // this flag is basically "have we written a term yet", so we know whether
140 // to write plus/minus signs and to treat minus signs as operations rather
141 // than as prefixes
142 let mut plus_flag = false;
143 for (n, &i) in self.coeffs.iter().enumerate().rev() {
144 // only display terms with nonzero coefficients
145 if i != 0 {
146 // if we're past the first term, print the appropriate operation
147 // sign first
148 if plus_flag {
149 if i < 0 {
150 s += " - ";
151 } else {
152 s += " + "
153 }
154 }
155 // if our term is constant,
156 if n == 0 {
157 // just display that constant, or its absolute value if we already
158 // wrote a minus sign
159 s += &format!("{}", if plus_flag { i.abs() } else { i });
160 // if our term is linear,
161 } else if n == 1 {
162 // if it's 1, just write "x";
163 if i == 1 {
164 s += "x";
165 // if it's -1, and if we're writing the first term, put a minus sign
166 // in front
167 } else if i == -1 {
168 s += &format!("{}x", if plus_flag { "" } else { "-" });
169 // otherwise, just display the coefficient, or its absolute value
170 // if we already wrote a minus sign
171 } else {
172 s += &format!("{}x", if plus_flag { i.abs() } else { i });
173 }
174 // rest of cases as above, but with the powers being displayed as well
175 } else if i == 1 {
176 s += &format!("x^{}", n);
177 } else if i == -1 {
178 s += &format!("{}x^{}", if plus_flag { "" } else { "-" }, n);
179 } else {
180 s += &format!("{}x^{}", if plus_flag { i.abs() } else { i }, n);
181 }
182 plus_flag = true;
183 }
184 }
185 }
186 write!(f, "{}", s)
187 }
188}
189
190/// A convenience macro for writing polynomials; essentially a wrapper around `vec![...]`.
191#[macro_export]
192macro_rules! poly {
193 () => (
194 Polynomial::zero();
195 );
196 ($($x:expr),*) => (
197 Polynomial::new(vec![$($x),*]);
198 )
199}
200
201#[cfg(test)]
202mod tests {
203 use crate::{poly, Polynomial};
204 #[test]
205 fn it_works() {
206 let mut quadratic = poly![1, 2, 1]; // x^2 + 2x + 1
207 let linear = poly![-6, 1]; // x - 6
208 assert_eq!(&quadratic + &linear, poly![-5, 3, 1]);
209 assert_eq!(&quadratic - &linear, poly![7, 1, 1]);
210 assert_eq!(&quadratic * &linear, poly![-6, -11, -4, 1]);
211 quadratic -= &linear;
212 assert_eq!(quadratic, poly![7, 1, 1]);
213 quadratic += &linear;
214 assert_eq!(quadratic, poly![1, 2, 1]);
215 quadratic *= &linear;
216 assert_eq!(quadratic, poly![-6, -11, -4, 1]);
217 assert_eq!(quadratic.derivative(), poly![-11, -8, 3]);
218 let mut another = poly![1, 3, 3, 1]; // x^3 + 3x^2 + 3x + 1
219 let pair = poly![-5, 4, 2]; // 2x^2 + 4x - 5
220 assert_eq!(&another + &pair, poly![-4, 7, 5, 1]);
221 assert_eq!(&another - &pair, poly![6, -1, 1, 1]);
222 assert_eq!(&another * &pair, poly![-5, -11, -1, 13, 10, 2]);
223 another -= &pair;
224 assert_eq!(another, poly![6, -1, 1, 1]);
225 another += &pair;
226 assert_eq!(another, poly![1, 3, 3, 1]);
227 another *= &pair;
228 assert_eq!(another, poly![-5, -11, -1, 13, 10, 2]);
229 assert_eq!(another.derivative(), poly![-11, -2, 39, 40, 10]);
230 }
231}