1use num_traits::MulAdd;
6
7#[inline]
17pub fn muladd<T: MulAdd + MulAdd<Output = T>>(x: T, a: T, b: T) -> T {
18 x.mul_add(a, b)
19}
20#[macro_export]
46macro_rules! horner {
47 ( $x:expr, $a0:expr, $a1:expr ) => {
56 $crate::muladd($a1, $x, $a0)
57 };
58 ( $x:expr, $a0:expr, $( $a1:expr ),+ ) => {
59 $crate::muladd( $crate::horner!( $x, $( $a1 ),+ ), $x, $a0 )
60 };
61 ( $x:expr, $a0:expr ) => { $a0 }
62 }
64
65pub fn horner_loop<T>(x: T, coefficients: &[T]) -> T
80where
81 T: Copy + MulAdd + MulAdd<Output = T>,
82{
83 let n = coefficients.len();
84 if n > 0 {
85 let a_n = coefficients[n - 1];
86 coefficients[0..n - 1]
87 .iter()
88 .rfold(a_n, |result, &a| result.mul_add(x, a))
89 } else {
90 panic!("coefficients.len() must be greater than or equal to 1, got {}", n);
91 }
92}
93
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn internal_muladd_integer() {
101 macro_rules! test_muladd {
102 ($($t:ident)+) => {
103 $(
104 {
105 let x: $t = 2;
106 let a: $t = 19;
107 let b: $t = 4;
108
109 assert_eq!(muladd(x, a, b), (x * a + b));
110 }
111 )+
112 };
113 }
114
115 test_muladd!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
116 }
117
118 #[test]
119 fn internal_muladd_float() {
120 macro_rules! test_muladd {
121 ($($t:ident)+) => {
122 $(
123 {
124 use core::$t;
125
126 let x: $t = 12.0;
127 let a: $t = 3.4;
128 let b: $t = 5.6;
129
130 let abs_difference = (muladd(x, a, b) - (x * a + b)).abs();
131
132 assert!(abs_difference <= 46.4 * $t::EPSILON);
133 }
134 )+
135 };
136 }
137
138 test_muladd!(f32 f64);
139 }
140
141 #[test]
142 fn horner_integer() {
143 macro_rules! test_horner_integer {
144 ($($t:ident)+) => {
145 $(
146 {
147 let x: $t = 2;
148 let a0: $t = 1;
149 let a1: $t = 2;
150 let a2: $t = 3;
151
152 assert_eq!(17, horner!(x, a0, a1, a2));
153
154 assert_eq!(101, horner!(x, a0, a1, a1, a2, a1, a0));
155 }
156 )+
157 };
158 }
159
160 test_horner_integer!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
161 }
162
163 #[test]
164 fn horner_float() {
165 macro_rules! test_horner_float {
166 ($($t:ident)+) => {
167 $(
168 {
169 let x: $t = 2.0;
170 let a0: $t = 1.0;
171 let a1: $t = 2.0;
172 let a2: $t = 3.0;
173
174 assert_eq!(17.0, horner!(x, a0, a1, a2));
175
176 assert_eq!(101.0, horner!(x, a0, a1, a1, a2, a1, a0));
177
178 let y: $t = 5.5;
179 let abs_difference = 1.1985066439401153 - horner!(y, 7.72156649015328655494e-02, 6.73523010531292681824e-02, 7.38555086081402883957e-03, 1.19270763183362067845e-03, 2.20862790713908385557e-04, 2.52144565451257326939e-05);
180 assert!(abs_difference <= $t::EPSILON);
181
182 let a3: $t = 4.0;
183 let a4: $t = 5.0;
184 let a5: $t = 6.0;
185 let a6: $t = 7.0;
186 let a7: $t = 8.0;
187 let a8: $t = 9.0;
188
189 assert_eq!(4097.0, horner!(x, a0, a1, a2, a3, a4, a5, a6, a7, a8));
190 }
191 )+
192 };
193 }
194
195 test_horner_float!(f32 f64);
196 }
197
198 #[test]
199 fn horner_loop_integer() {
200 macro_rules! test_horner_loop_integer {
201 ($($t:ident)+) => {
202 $(
203 {
204 let x: $t = 2;
205 let c: Vec<$t> = vec![1, 2, 3];
206 let c1: Vec<$t> = vec![1];
207 assert_eq!(17, horner_loop(x, &c));
208 assert_eq!(1, horner_loop(x, &c1));
209 }
210 )+
211 }
212 }
213
214 test_horner_loop_integer!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
215 }
216
217 #[test]
218 fn horner_loop_float() {
219 macro_rules! test_horner_loop_float {
220 ($($t:ident)+) => {
221 $(
222 {
223 let x: $t = 2.0;
224 let c: Vec<$t> = vec![1.0, 2.0, 3.0];
225 let c1: Vec<$t> = vec![1.0];
226 assert_eq!(17.0, horner_loop(x, &c));
227 assert_eq!(1.0, horner_loop(x, &c1));
228 }
229 )+
230 }
231 }
232
233 test_horner_loop_float!(f32 f64);
234 }
235
236 #[test]
237 #[should_panic(expected = "coefficients.len() must be greater than")]
238 fn horner_loop_empty_vec() {
239 let x = 2.0;
240 let c: Vec<f64> = vec![];
241 horner_loop(x, &c);
242 }
243
244}