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}