Skip to main content

malachite_nz/integer_polynomial/arithmetic/
derivative.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::Integer;
10use crate::integer_polynomial::IntegerPolynomial;
11use alloc::vec::Vec;
12use malachite_base::polynomial::{Derivative, DerivativeAssign};
13
14// The coefficients of the derivative of the polynomial whose coefficients `xs` holds in ascending
15// order: the coefficient of x^i, for i at least 1, times i. The leading coefficient of a
16// nonconstant polynomial, times its degree, is nonzero, so the result is normalized.
17fn derivative_ref(xs: &[Integer]) -> Vec<Integer> {
18    xs.iter()
19        .enumerate()
20        .skip(1)
21        .map(|(i, c)| c * Integer::from(i))
22        .collect()
23}
24
25fn derivative_in_place(xs: &mut Vec<Integer>) {
26    if xs.is_empty() {
27        return;
28    }
29    xs.remove(0);
30    for (i, c) in xs.iter_mut().enumerate() {
31        *c *= Integer::from(i + 1);
32    }
33}
34
35impl Derivative for IntegerPolynomial {
36    type Output = Self;
37
38    /// Computes the derivative of an [`IntegerPolynomial`], taking it by value.
39    ///
40    /// $$
41    /// f(p) = p' = \sum_{i=1}^n ia_ix^{i-1}.
42    /// $$
43    ///
44    /// The coefficient of $x^i$ is multiplied by $i$ and moves to $x^{i-1}$. A constant polynomial,
45    /// including zero, has derivative zero.
46    ///
47    /// # Worst-case complexity
48    /// $T(n, m) = O(n + m \log m)$
49    ///
50    /// $M(n, m) = O(n + m \log m)$
51    ///
52    /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
53    /// coefficients, and $m$ is `self.len()`.
54    ///
55    /// # Examples
56    /// ```
57    /// use core::str::FromStr;
58    /// use malachite_base::num::basic::traits::Zero;
59    /// use malachite_base::polynomial::Derivative;
60    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
61    ///
62    /// let p = IntegerPolynomial::from_str("x^3-3*x^2+2*x-5").unwrap();
63    /// assert_eq!(p.derivative().to_string(), "3*x^2-6*x+2");
64    ///
65    /// let p = IntegerPolynomial::from_str("7").unwrap();
66    /// assert_eq!(p.derivative(), IntegerPolynomial::ZERO);
67    /// ```
68    ///
69    /// This is equivalent to `fmpz_poly_derivative` from `fmpz_poly/derivative.c`, FLINT 3.6.0.
70    #[inline]
71    fn derivative(mut self) -> Self {
72        self.derivative_assign();
73        self
74    }
75}
76
77impl Derivative for &IntegerPolynomial {
78    type Output = IntegerPolynomial;
79
80    /// Computes the derivative of an [`IntegerPolynomial`], taking it by reference.
81    ///
82    /// $$
83    /// f(p) = p' = \sum_{i=1}^n ia_ix^{i-1}.
84    /// $$
85    ///
86    /// The coefficient of $x^i$ is multiplied by $i$ and moves to $x^{i-1}$. A constant polynomial,
87    /// including zero, has derivative zero.
88    ///
89    /// # Worst-case complexity
90    /// $T(n, m) = O(n + m \log m)$
91    ///
92    /// $M(n, m) = O(n + m \log m)$
93    ///
94    /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
95    /// coefficients, and $m$ is `self.len()`.
96    ///
97    /// # Examples
98    /// ```
99    /// use core::str::FromStr;
100    /// use malachite_base::num::basic::traits::Zero;
101    /// use malachite_base::polynomial::Derivative;
102    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
103    ///
104    /// let p = IntegerPolynomial::from_str("x^3-3*x^2+2*x-5").unwrap();
105    /// assert_eq!((&p).derivative().to_string(), "3*x^2-6*x+2");
106    ///
107    /// let p = IntegerPolynomial::from_str("7").unwrap();
108    /// assert_eq!((&p).derivative(), IntegerPolynomial::ZERO);
109    /// ```
110    ///
111    /// This is equivalent to `fmpz_poly_derivative` from `fmpz_poly/derivative.c`, FLINT 3.6.0.
112    #[inline]
113    fn derivative(self) -> IntegerPolynomial {
114        IntegerPolynomial {
115            coefficients: derivative_ref(&self.coefficients),
116        }
117    }
118}
119
120impl DerivativeAssign for IntegerPolynomial {
121    /// Replaces an [`IntegerPolynomial`] with its derivative.
122    ///
123    /// $$
124    /// p \gets p' = \sum_{i=1}^n ia_ix^{i-1}.
125    /// $$
126    ///
127    /// The coefficient of $x^i$ is multiplied by $i$ and moves to $x^{i-1}$. A constant polynomial,
128    /// including zero, has derivative zero.
129    ///
130    /// # Worst-case complexity
131    /// $T(n, m) = O(n + m \log m)$
132    ///
133    /// $M(n, m) = O(n + m \log m)$
134    ///
135    /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
136    /// coefficients, and $m$ is `self.len()`.
137    ///
138    /// # Examples
139    /// ```
140    /// use core::str::FromStr;
141    /// use malachite_base::num::basic::traits::Zero;
142    /// use malachite_base::polynomial::DerivativeAssign;
143    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
144    ///
145    /// let mut p = IntegerPolynomial::from_str("x^3-3*x^2+2*x-5").unwrap();
146    /// p.derivative_assign();
147    /// assert_eq!(p.to_string(), "3*x^2-6*x+2");
148    ///
149    /// let mut p = IntegerPolynomial::from_str("7").unwrap();
150    /// p.derivative_assign();
151    /// assert_eq!(p, IntegerPolynomial::ZERO);
152    /// ```
153    ///
154    /// This is equivalent to `fmpz_poly_derivative` from `fmpz_poly/derivative.c`, FLINT 3.6.0.
155    #[inline]
156    fn derivative_assign(&mut self) {
157        derivative_in_place(&mut self.coefficients);
158    }
159}