rgsl/
polynomials.rs

1//
2// A rust binding for the GSL library by Guillaume Gomez (guillaume1.gomez@gmail.com)
3//
4
5/*!
6# Polynomials
7
8This chapter describes functions for evaluating and solving polynomials. There are routines for finding real and complex roots of quadratic
9and cubic equations using analytic methods. An iterative polynomial solver is also available for finding the roots of general polynomials
10with real coefficients (of any order).
11
12## References and Further Reading
13
14The balanced-QR method and its error analysis are described in the following papers,
15
16R.S. Martin, G. Peters and J.H. Wilkinson, “The QR Algorithm for Real Hessenberg Matrices”, Numerische Mathematik, 14 (1970), 219–231.
17B.N. Parlett and C. Reinsch, “Balancing a Matrix for Calculation of Eigenvalues and Eigenvectors”, Numerische Mathematik, 13 (1969), 293–304.
18A. Edelman and H. Murakami, “Polynomial roots from companion matrix eigenvalues”, Mathematics of Computation, Vol. 64, No. 210 (1995), 763–776.
19The formulas for divided differences are given in the following texts,
20
21Abramowitz and Stegun, Handbook of Mathematical Functions, Sections 25.1.4 and 25.2.26.
22R. L. Burden and J. D. Faires, Numerical Analysis, 9th edition, ISBN 0-538-73351-9, 2011.
23!*/
24
25/// The functions described here evaluate the polynomial
26/// `P(x) = c[0] + c[1] x + c[2] x^2 + \dots + c[len-1] x^{len-1}` using Horner’s method for
27/// stability.
28pub mod evaluation {
29    use crate::Value;
30    use std::mem::transmute;
31    use types::ComplexF64;
32
33    /// This function evaluates a polynomial with real coefficients for the real variable x.
34    #[doc(alias = "gsl_poly_eval")]
35    pub fn poly_eval(c: &[f64], x: f64) -> f64 {
36        unsafe { sys::gsl_poly_eval(c.as_ptr(), c.len() as i32, x) }
37    }
38
39    /// This function evaluates a polynomial with real coefficients for the complex variable z.
40    #[doc(alias = "gsl_poly_complex_eval")]
41    pub fn poly_complex_eval(c: &[f64], z: &ComplexF64) -> ComplexF64 {
42        unsafe {
43            transmute(sys::gsl_poly_complex_eval(
44                c.as_ptr(),
45                c.len() as i32,
46                transmute(*z),
47            ))
48        }
49    }
50
51    /// This function evaluates a polynomial with complex coefficients for the complex variable z.
52    #[doc(alias = "gsl_complex_poly_complex_eval")]
53    pub fn complex_poly_complex_eval(c: &[ComplexF64], z: &ComplexF64) -> ComplexF64 {
54        let mut tmp = Vec::new();
55
56        for it in c.iter() {
57            unsafe { tmp.push(transmute(*it)) };
58        }
59        unsafe {
60            transmute(sys::gsl_complex_poly_complex_eval(
61                tmp.as_ptr(),
62                tmp.len() as i32,
63                transmute(*z),
64            ))
65        }
66    }
67
68    /// This function evaluates a polynomial and its derivatives storing the results in the array res of size lenres. The output array contains
69    /// the values of d^k P/d x^k for the specified value of x starting with k = 0.
70    #[doc(alias = "gsl_poly_eval_derivs")]
71    pub fn poly_eval_derivs(c: &[f64], x: f64, res: &mut [f64]) -> Result<(), Value> {
72        let ret = unsafe {
73            sys::gsl_poly_eval_derivs(
74                c.as_ptr(),
75                c.len() as _,
76                x,
77                res.as_mut_ptr(),
78                res.len() as _,
79            )
80        };
81        result_handler!(ret, ())
82    }
83}
84
85/// The functions described here manipulate polynomials stored in Newton’s divided-difference representation. The use of divided-differences
86/// is described in Abramowitz & Stegun sections 25.1.4 and 25.2.26, and Burden and Faires, chapter 3, and discussed briefly below.
87///
88/// Given a function f(x), an nth degree interpolating polynomial P_{n}(x) can be constructed which agrees with f at n+1 distinct points x_0,
89/// x_1,...,x_{n}. This polynomial can be written in a form known as Newton’s divided-difference representation:
90///
91/// P_n(x) = f(x_0) + \sum_(k=1)^n [x_0,x_1,...,x_k] (x-x_0)(x-x_1)...(x-x_(k-1))
92///
93/// where the divided differences [x_0,x_1,...,x_k] are defined in section 25.1.4 of Abramowitz and Stegun. Additionally, it is possible to
94/// construct an interpolating polynomial of degree 2n+1 which also matches the first derivatives of f at the points x_0,x_1,...,x_n. This is
95/// called the Hermite interpolating polynomial and is defined as
96///
97/// H_(2n+1)(x) = f(z_0) + \sum_(k=1)^(2n+1) [z_0,z_1,...,z_k] (x-z_0)(x-z_1)...(x-z_(k-1))
98///
99/// where the elements of z = \{x_0,x_0,x_1,x_1,...,x_n,x_n\} are defined by z_{2k} = z_{2k+1} = x_k. The divided-differences [z_0,z_1,...,z_k]
100/// are discussed in Burden and Faires, section 3.4.
101pub mod divided_difference_representation {
102    use crate::Value;
103
104    /// This function computes a divided-difference representation of the interpolating polynomial for the points (x, y) stored in the arrays
105    /// xa and ya of length size. On output the divided-differences of (xa,ya) are stored in the array dd, also of length size. Using the
106    /// notation above, `dd[k] = [x_0,x_1,...,x_k]`.
107    #[doc(alias = "gsl_poly_dd_init")]
108    pub fn poly_dd_init(dd: &mut [f64], xa: &[f64], ya: &[f64]) -> Result<(), Value> {
109        let ret = unsafe {
110            sys::gsl_poly_dd_init(dd.as_mut_ptr(), xa.as_ptr(), ya.as_ptr(), dd.len() as _)
111        };
112        result_handler!(ret, ())
113    }
114
115    /// This function evaluates the polynomial stored in divided-difference form in the arrays dd and xa of length size at the point x.
116    #[doc(alias = "gsl_poly_dd_eval")]
117    pub fn poly_dd_eval(dd: &[f64], xa: &[f64], x: f64) -> f64 {
118        unsafe { sys::gsl_poly_dd_eval(dd.as_ptr(), xa.as_ptr(), dd.len() as _, x) }
119    }
120
121    /// This function converts the divided-difference representation of a polynomial to a Taylor expansion. The divided-difference representation
122    /// is supplied in the arrays dd and xa of length size. On output the Taylor coefficients of the polynomial expanded about the point xp are
123    /// stored in the array c also of length size. A workspace of length size must be provided in the array w.
124    #[doc(alias = "gsl_poly_dd_taylor")]
125    pub fn poly_dd_taylor(
126        c: &mut [f64],
127        xp: f64,
128        dd: &[f64],
129        xa: &[f64],
130        w: &mut [f64],
131    ) -> Result<(), Value> {
132        let ret = unsafe {
133            sys::gsl_poly_dd_taylor(
134                c.as_mut_ptr(),
135                xp,
136                dd.as_ptr(),
137                xa.as_ptr(),
138                dd.len() as _,
139                w.as_mut_ptr(),
140            )
141        };
142        result_handler!(ret, ())
143    }
144
145    /// This function computes a divided-difference representation of the interpolating Hermite polynomial for the points (x, y) stored in the
146    /// arrays xa and ya of length size. Hermite interpolation constructs polynomials which also match first derivatives dy/dx which are provided
147    /// in the array dya also of length size. The first derivatives can be incorported into the usual divided-difference algorithm by forming a
148    /// new dataset z = \{x_0,x_0,x_1,x_1,...\}, which is stored in the array za of length 2*size on output. On output the divided-differences
149    /// of the Hermite representation are stored in the array dd, also of length 2*size. Using the notation above, `dd[k] = [z_0,z_1,...,z_k]`.
150    /// The resulting Hermite polynomial can be evaluated by calling gsl_poly_dd_eval and using za for the input argument xa.
151    #[doc(alias = "gsl_poly_dd_hermite_init")]
152    pub fn poly_dd_hermite_init(
153        dd: &mut [f64],
154        za: &mut [f64],
155        xa: &[f64],
156        ya: &[f64],
157        dya: &[f64],
158    ) -> Result<(), Value> {
159        let ret = unsafe {
160            sys::gsl_poly_dd_hermite_init(
161                dd.as_mut_ptr(),
162                za.as_mut_ptr(),
163                xa.as_ptr(),
164                ya.as_ptr(),
165                dya.as_ptr(),
166                dd.len() as _,
167            )
168        };
169        result_handler!(ret, ())
170    }
171}
172
173pub mod quadratic_equations {
174    use crate::Value;
175    use std::mem::transmute;
176    use types::ComplexF64;
177
178    /// This function finds the real roots of the quadratic equation,
179    ///
180    /// a x^2 + b x + c = 0
181    ///
182    /// The number of real roots (either zero, one or two) is returned, and their locations are
183    /// stored in x0 and x1. If no real roots are found then x0 and x1 are not modified. If one real
184    /// root is found (i.e. if a=0) then it is stored in x0. When two real roots are found they
185    /// are stored in x0 and x1 in ascending order. The case of coincident roots is not considered
186    /// special. For example (x-1)^2=0 will have two roots, which happen to have exactly equal
187    /// values.
188    ///
189    /// The number of roots found depends on the sign of the discriminant b^2 - 4 a c. This will be
190    /// subject to rounding and cancellation errors when computed in double precision, and will also
191    /// be subject to errors if the coefficients of the polynomial are inexact. These errors
192    /// may cause a discrete change in the number of roots. However, for polynomials with small
193    /// integer coefficients the discriminant can always be computed exactly.
194    ///
195    /// Returns `(x0, x1)`.
196    #[doc(alias = "gsl_poly_solve_quadratic")]
197    pub fn poly_solve_quadratic(a: f64, b: f64, c: f64) -> Result<(f64, f64), Value> {
198        let mut x0 = 0.;
199        let mut x1 = 0.;
200        let ret = unsafe { sys::gsl_poly_solve_quadratic(a, b, c, &mut x0, &mut x1) };
201        result_handler!(ret, (x0, x1))
202    }
203
204    /// This function finds the complex roots of the quadratic equation,
205    ///
206    /// a z^2 + b z + c = 0
207    ///
208    /// The number of complex roots is returned (either one or two) and the locations of the roots are stored in z0 and z1. The roots are returned
209    /// in ascending order, sorted first by their real components and then by their imaginary components. If only one real root is found (i.e. if
210    /// a=0) then it is stored in z0.
211    #[doc(alias = "gsl_poly_complex_solve_quadratic")]
212    #[allow(unknown_lints, clippy::useless_transmute)]
213    pub fn poly_complex_solve_quadratic(
214        a: f64,
215        b: f64,
216        c: f64,
217        z0: &mut ComplexF64,
218        z1: &mut ComplexF64,
219    ) -> Result<(), Value> {
220        let ret =
221            unsafe { sys::gsl_poly_complex_solve_quadratic(a, b, c, transmute(z0), transmute(z1)) };
222        result_handler!(ret, ())
223    }
224}
225
226pub mod cubic_equations {
227    use crate::Value;
228    use std::mem::transmute;
229    use types::ComplexF64;
230
231    /// This function finds the real roots of the cubic equation,
232    ///
233    /// x^3 + a x^2 + b x + c = 0
234    ///
235    /// with a leading coefficient of unity. The number of real roots (either one or three) is
236    /// returned, and their locations are stored in x0, x1 and x2. If one real root is found then
237    /// only x0 is modified. When three real roots are found they are stored in x0, x1 and x2 in
238    /// ascending order. The case of coincident roots is not considered special. For example, the
239    /// equation (x-1)^3=0 will have three roots with exactly equal values. As in the quadratic
240    /// case, finite precision may cause equal or closely-spaced real roots to move off the
241    /// real axis into the complex plane, leading to a discrete change in the number of real roots.
242    ///
243    /// Returns `(x0, x1, x2)`.
244    #[doc(alias = "gsl_poly_solve_cubic")]
245    pub fn poly_solve_cubic(a: f64, b: f64, c: f64) -> Result<(f64, f64, f64), Value> {
246        let mut x0 = 0.;
247        let mut x1 = 0.;
248        let mut x2 = 0.;
249        let ret = unsafe { sys::gsl_poly_solve_cubic(a, b, c, &mut x0, &mut x1, &mut x2) };
250        result_handler!(ret, (x0, x1, x2))
251    }
252
253    /// This function finds the complex roots of the cubic equation,
254    ///
255    /// z^3 + a z^2 + b z + c = 0
256    ///
257    /// The number of complex roots is returned (always three) and the locations of the roots are
258    /// stored in z0, z1 and z2. The roots are returned in ascending order, sorted first by their
259    /// real components and then by their imaginary components.
260    #[doc(alias = "gsl_poly_complex_solve_cubic")]
261    #[allow(unknown_lints, clippy::useless_transmute)]
262    pub fn poly_complex_solve_cubic(
263        a: f64,
264        b: f64,
265        c: f64,
266        z0: &mut ComplexF64,
267        z1: &mut ComplexF64,
268        z2: &mut ComplexF64,
269    ) -> Result<(), Value> {
270        let ret = unsafe {
271            sys::gsl_poly_complex_solve_cubic(a, b, c, transmute(z0), transmute(z1), transmute(z2))
272        };
273        result_handler!(ret, ())
274    }
275}