malachite_nz/integer_polynomial/comparison/cmp.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::cmp::Ordering::{self, *};
11use malachite_base::num::arithmetic::traits::Sign;
12use malachite_base::polynomial::Polynomial;
13
14impl PartialOrd for IntegerPolynomial {
15 /// Compares two [`IntegerPolynomial`]s.
16 ///
17 /// See the documentation for the [`Ord`] implementation.
18 #[inline]
19 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
20 Some(self.cmp(other))
21 }
22}
23
24impl Ord for IntegerPolynomial {
25 /// Compares two [`IntegerPolynomial`]s by how they behave for large arguments.
26 ///
27 /// The greater polynomial is the one that is eventually greater: the comparison is the one that
28 /// $p(x)$ and $q(x)$ eventually settle into as $x$ grows.
29 ///
30 /// $$
31 /// f(p, q) = \lim_{x \to \infty} \operatorname{cmp}(p(x), q(x)).
32 /// $$
33 ///
34 /// The limit always exists. $p - q$ is a polynomial, so it has finitely many roots, and past
35 /// the largest of them its sign is the sign of its leading coefficient and never changes again.
36 /// That also makes this a total order agreeing with [`Eq`]: the limit is $0$ exactly when $p -
37 /// q$ is the zero polynomial.
38 ///
39 /// Finding it needs no evaluation, but a higher degree alone does not settle it the way it does
40 /// over the [`Natural`](crate::natural::Natural)s. A polynomial of higher degree does dominate
41 /// one of lower degree, so the difference's leading coefficient is its own; but that
42 /// coefficient may be negative, in which case the dominating polynomial runs off to $-\infty$
43 /// and is the *smaller* of the two. So $-x^3 < x$, and the zero polynomial is above every
44 /// polynomial with a negative leading coefficient and below every polynomial with a positive
45 /// one. Polynomials of equal degree are decided by the highest-degree coefficient at which they
46 /// differ, since that term eventually outgrows the sum of everything below it.
47 ///
48 /// This is the order that makes the polynomials an ordered ring: it is unchanged by adding a
49 /// polynomial to both sides, and by multiplying both sides by a positive one. Restricted to the
50 /// constant polynomials it is the order on the [`Integer`](crate::integer::Integer)s, so the
51 /// embedding of a number as a polynomial preserves comparisons. It is not a well-order, and no
52 /// order compatible with addition can be: $x > x - 1 > x - 2 > \ldots$ descends forever.
53 ///
54 /// # Worst-case complexity
55 /// $T(n) = O(n)$
56 ///
57 /// $M(n) = O(1)$
58 ///
59 /// where $T$ is time, $M$ is additional memory, and $n$ is the smaller of the two polynomials'
60 /// total number of bits, summed over their coefficients. Polynomials of different degrees are
61 /// compared in constant time.
62 ///
63 /// # Examples
64 /// ```
65 /// use core::str::FromStr;
66 /// use malachite_nz::integer_polynomial::IntegerPolynomial;
67 ///
68 /// // A higher degree dominates, and a positive leading coefficient makes it the greater.
69 /// assert!(
70 /// IntegerPolynomial::from_str("x^2").unwrap()
71 /// > IntegerPolynomial::from_str("1000000*x").unwrap()
72 /// );
73 ///
74 /// // But a dominating polynomial with a negative leading coefficient runs off downwards, so
75 /// // it is the smaller one.
76 /// assert!(
77 /// IntegerPolynomial::from_str("-x^3").unwrap()
78 /// < IntegerPolynomial::from_str("x").unwrap()
79 /// );
80 ///
81 /// // The zero polynomial sits between the two signs.
82 /// assert!(
83 /// IntegerPolynomial::from_str("-x").unwrap() < IntegerPolynomial::from_str("0").unwrap()
84 /// );
85 /// assert!(
86 /// IntegerPolynomial::from_str("0").unwrap() < IntegerPolynomial::from_str("x").unwrap()
87 /// );
88 ///
89 /// // At equal degrees the highest coefficient at which they differ decides.
90 /// assert!(
91 /// IntegerPolynomial::from_str("-x^2+5").unwrap()
92 /// > IntegerPolynomial::from_str("-2*x^2+1000000").unwrap()
93 /// );
94 ///
95 /// // Constant polynomials compare as the numbers they are.
96 /// assert!(
97 /// IntegerPolynomial::from_str("-122").unwrap()
98 /// > IntegerPolynomial::from_str("-123").unwrap()
99 /// );
100 /// ```
101 fn cmp(&self, other: &Self) -> Ordering {
102 if core::ptr::eq(self, other) {
103 return Equal;
104 }
105 // The coefficients are held with no trailing zero, so the length is the degree plus one,
106 // and 0 for the zero polynomial; comparing lengths is comparing degrees.
107 match self.coefficients.len().cmp(&other.coefficients.len()) {
108 // Equal degrees: the iterators have the same length, so comparing them from the leading
109 // coefficient down is the comparison at the highest coefficient where the two differ.
110 // Two zero polynomials compare equal, both iterators being empty.
111 Equal => self
112 .coefficients
113 .iter()
114 .rev()
115 .cmp(other.coefficients.iter().rev()),
116 // Different degrees: the difference's leading coefficient is the dominating
117 // polynomial's own, so its sign decides. Normalization makes it nonzero, so there is no
118 // `Equal` to fall through to.
119 Greater => self.leading_coefficient().sign(),
120 Less => other.leading_coefficient().sign().reverse(),
121 }
122 }
123}