gauss_quad/
data_api.rs

1//! The macros in this module define the common API for accessing the data that underlies the quadrature rules.
2//! The [`__impl_node_weight_rule!`] macro implements the API for a struct with both nodes and weights.
3//! It should be called in the module that defines the quadrature rule struct.
4//! [`__impl_node_rule!`] does the same thing as the previous macro
5//! but for a struct with only nodes and no weights.
6
7// The code in the macros uses fully qualified paths for every type, so it is quite verbose.
8// That is, instead of `usize` it uses `::core::primitive::usize` and so on. This makes it so that
9// the caller of the macro doesn't have to import anything into the module in order for the macro to compile,
10// and makes it compile even if the user has made custom types whose names shadow types used by the macro.
11
12/// A node in a quadrature rule.
13pub type Node = f64;
14/// A weight in a quadrature rule.
15pub type Weight = f64;
16
17/// This macro implements the data access API for the given quadrature rule struct that contains
18/// a field named `node_weight_pairs` of the type `Vec<(Node, Weight)>`.
19/// It takes in the name of the quadrature rule struct as well as the names it should give the iterators
20/// over its nodes, weights, and both, as well as the iterator returned by the [`IntoIterator`] trait.
21#[doc(hidden)]
22#[macro_export]
23macro_rules! __impl_node_weight_rule {
24    (
25        // The name of the quadrature rule struct, e.g. GaussLegendre.
26        $quadrature_rule:ident,
27        // The name that the iterator over the nodes should have, e.g. GaussLegendreNodes.
28        $quadrature_rule_nodes:ident,
29        // The name that the iterator over the weights should have, e.g. GaussLegendreWeights.
30        $quadrature_rule_weights:ident,
31        // The name that the iterator returned when calling the `iter` function should have,
32        // e.g. GaussLegendreIter.
33        $quadrature_rule_iter:ident,
34        // The name of the iterator returned by the by the IntoIterator trait.
35        $quadrature_rule_into_iter:ident
36    ) => {
37        // Implements functions for accessing the underlying data of the quadrature rule struct
38        // in a way the adheres to the API guidelines: <https://rust-lang.github.io/api-guidelines/naming.html>.
39        // The functions in these impl blocks all have an #[inline] directive because they are trivial.
40
41        // Lets the user do
42        // for (node, weight) in QuadratuleRule::new(...) {
43        //     ...
44        // }
45        impl ::core::iter::IntoIterator for $quadrature_rule {
46            type IntoIter = $quadrature_rule_into_iter;
47            type Item = ($crate::Node, $crate::Weight);
48            #[inline]
49            fn into_iter(self) -> Self::IntoIter {
50                $quadrature_rule_into_iter::new(self.node_weight_pairs.into_iter())
51            }
52        }
53
54        // Lets the user do
55        // let rule = QuadratureRule::new(...);
56        // for &(node, weight) in &rule {
57        //     ...
58        // }
59        // rule.integrate(...) // <-- still available
60        impl<'a> ::core::iter::IntoIterator for &'a $quadrature_rule {
61            type IntoIter = $quadrature_rule_iter<'a>;
62            type Item = &'a ($crate::Node, $crate::Weight);
63            #[inline]
64            fn into_iter(self) -> Self::IntoIter {
65                $quadrature_rule_iter::new(self.node_weight_pairs.iter())
66            }
67        }
68
69        impl $quadrature_rule {
70            /// Returns an iterator over the nodes of the quadrature rule.
71            #[inline]
72            pub fn nodes(&self) -> $quadrature_rule_nodes<'_> {
73                $quadrature_rule_nodes::new(self.node_weight_pairs.iter().map(|p| &p.0))
74            }
75
76            /// Returns an iterator over the weights of the quadrature rule.
77            #[inline]
78            pub fn weights(&self) -> $quadrature_rule_weights<'_> {
79                $quadrature_rule_weights::new(self.node_weight_pairs.iter().map(|p| &p.1))
80            }
81
82            /// Returns an iterator over the node-weight pairs of the quadrature rule.
83            #[inline]
84            pub fn iter(&self) -> $quadrature_rule_iter<'_> {
85                $quadrature_rule_iter::new(self.node_weight_pairs.iter())
86            }
87
88            /// Returns a slice of all the node-weight pairs of the quadrature rule.
89            #[inline]
90            pub fn as_node_weight_pairs(&self) -> &[($crate::Node, $crate::Weight)] {
91                &self.node_weight_pairs
92            }
93
94            /// Converts the quadrature rule into a vector of node-weight pairs.
95            ///
96            /// This function just returns the underlying vector without any computation or cloning.
97            #[inline]
98            #[must_use = "`self` will be dropped if the result is not used"]
99            pub fn into_node_weight_pairs(self) -> ::std::vec::Vec<($crate::Node, $crate::Weight)> {
100                self.node_weight_pairs
101            }
102
103            /// Returns the degree of the quadrature rule.
104            #[inline]
105            pub fn degree(&self) -> ::core::primitive::usize {
106                self.node_weight_pairs.len()
107            }
108        }
109
110        // region: QuadratureRuleNodes
111
112        /// An iterator over the nodes of the quadrature rule.
113        #[derive(::core::fmt::Debug, ::core::clone::Clone)]
114        #[must_use = "iterators are lazy and do nothing unless consumed"]
115        pub struct $quadrature_rule_nodes<'a>(
116            // This horrible type is just the fully qualified path of the type returned
117            // by `slice.iter().map(|(x, _)| x)`.
118            ::std::iter::Map<
119                ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
120                fn(&'a ($crate::Node, $crate::Weight)) -> &'a $crate::Node,
121            >,
122        );
123
124        impl<'a> $quadrature_rule_nodes<'a> {
125            #[inline]
126            const fn new(
127                iter_map: ::std::iter::Map<
128                    ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
129                    fn(&'a ($crate::Node, $crate::Weight)) -> &'a $crate::Node,
130                >,
131            ) -> Self {
132                Self(iter_map)
133            }
134        }
135
136        $crate::__impl_slice_iterator_newtype_traits!{$quadrature_rule_nodes<'a>, &'a $crate::Node}
137
138        // endregion: QuadratureRuleNodes
139
140        // region: QuadratureRuleWeights
141
142        /// An iterator over the weights of the quadrature rule.
143        #[derive(::core::fmt::Debug, ::core::clone::Clone)]
144        #[must_use = "iterators are lazy and do nothing unless consumed"]
145        pub struct $quadrature_rule_weights<'a>(
146            // Same as the previous horrible type, but maps out the weight instead of the node.
147            ::std::iter::Map<
148                ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
149                fn(&'a ($crate::Node, $crate::Weight)) -> &'a $crate::Weight,
150            >,
151        );
152
153        impl<'a> $quadrature_rule_weights<'a> {
154            #[inline]
155            const fn new(
156                iter_map: ::std::iter::Map<
157                    ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
158                    fn(&'a ($crate::Node, $crate::Weight)) -> &'a $crate::Weight,
159                >,
160            ) -> Self {
161                Self(iter_map)
162            }
163        }
164
165        $crate::__impl_slice_iterator_newtype_traits!{$quadrature_rule_weights<'a>, &'a $crate::Weight}
166
167        // endregion: QuadratureRuleWeights
168
169        // region: QuadratureRuleIter
170
171        /// An iterator over the node-weight pairs of the quadrature rule.
172        ///
173        /// Created by the `iter` function on the quadrature rule struct.
174        #[derive(::core::fmt::Debug, ::core::clone::Clone)]
175        #[must_use = "iterators are lazy and do nothing unless consumed"]
176        pub struct $quadrature_rule_iter<'a>(
177            ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
178        );
179
180        impl<'a> $quadrature_rule_iter<'a> {
181            #[inline]
182            const fn new(
183                node_weight_pairs: ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
184            ) -> Self {
185                Self(node_weight_pairs)
186            }
187
188            /// Views the underlying data as a subslice of the original data.
189            ///
190            /// See [`core::slice::Iter::as_slice`] for more information.
191            #[inline]
192            pub fn as_slice(&self) -> &'a [($crate::Node, $crate::Weight)] {
193                self.0.as_slice()
194            }
195        }
196
197        impl<'a> ::core::convert::AsRef<[($crate::Node, $crate::Weight)]>
198            for $quadrature_rule_iter<'a>
199        {
200            #[inline]
201            fn as_ref(&self) -> &[($crate::Node, $crate::Weight)] {
202                self.0.as_ref()
203            }
204        }
205
206        $crate::__impl_slice_iterator_newtype_traits!{$quadrature_rule_iter<'a>, &'a ($crate::Node, $crate::Weight)}
207
208        // endregion: QuadratureRuleIter
209
210        // region: QuadratureRuleIntoIter
211
212        /// An owning iterator over the node-weight pairs of the quadrature rule.
213        ///
214        /// Created by the [`IntoIterator`] trait implementation of the quadrature rule struct.
215        #[derive(::core::fmt::Debug, ::core::clone::Clone)]
216        #[must_use = "iterators are lazy and do nothing unless consumed"]
217        pub struct $quadrature_rule_into_iter(::std::vec::IntoIter<($crate::Node, $crate::Weight)>);
218
219        impl $quadrature_rule_into_iter {
220            #[inline]
221            const fn new(
222                node_weight_pairs: ::std::vec::IntoIter<($crate::Node, $crate::Weight)>,
223            ) -> Self {
224                Self(node_weight_pairs)
225            }
226
227            /// Views the underlying data as a subslice of the original data.
228            ///
229            /// See [`std::vec::IntoIter::as_slice`] for more information.
230            #[inline]
231            pub fn as_slice(&self) -> &[($crate::Node, $crate::Weight)] {
232                self.0.as_slice()
233            }
234        }
235
236        impl ::core::convert::AsRef<[($crate::Node, $crate::Weight)]>
237            for $quadrature_rule_into_iter
238        {
239            #[inline]
240            fn as_ref(&self) -> &[($crate::Node, $crate::Weight)] {
241                self.0.as_ref()
242            }
243        }
244
245        $crate::__impl_slice_iterator_newtype_traits!{$quadrature_rule_into_iter, ($crate::Node, $crate::Weight)}
246
247        // endregion: QuadratureRuleIntoIter
248    };
249}
250
251/// Implements the [`Iterator`], [`DoubleEndedIterator`], [`ExactSizeIterator`] and [`FusedIterator`](core::iter::FusedIterator) traits for a struct
252/// that wraps an iterator that has those traits. Takes in the name of the struct and optionally its lifetime
253/// as well as the type returned by the iterator.
254#[macro_export]
255#[doc(hidden)]
256macro_rules! __impl_slice_iterator_newtype_traits {
257    ($iterator:ident$(<$a:lifetime>)?, $item:ty) => {
258        impl$(<$a>)? ::core::iter::Iterator for $iterator<$($a)?> {
259            type Item = $item;
260            #[inline]
261            fn next(&mut self) -> ::core::option::Option<Self::Item> {
262                self.0.next()
263            }
264
265            #[inline]
266            fn size_hint(&self) -> (::core::primitive::usize, ::core::option::Option<::core::primitive::usize>) {
267                self.0.size_hint()
268            }
269
270            // These methods by default call the `next` method a lot to access data.
271            // This isn't needed in our case, since the data underlying the iterator is
272            // a slice, which has O(1) access to any element.
273            // As a result we reimplement them and just delegate to the builtin slice iterator methods.
274
275            #[inline]
276            fn nth(&mut self, index: ::core::primitive::usize) -> ::core::option::Option<Self::Item> {
277                self.0.nth(index)
278            }
279
280            #[inline]
281            fn count(self) -> ::core::primitive::usize {
282                self.0.count()
283            }
284
285            #[inline]
286            fn last(self) -> ::core::option::Option<Self::Item> {
287                self.0.last()
288            }
289        }
290
291        impl$(<$a>)? ::core::iter::DoubleEndedIterator for $iterator$(<$a>)? {
292            #[inline]
293            fn next_back(&mut self) -> ::core::option::Option<Self::Item> {
294                self.0.next_back()
295            }
296
297            #[inline]
298            fn nth_back(&mut self, n: ::core::primitive::usize) -> ::core::option::Option<Self::Item> {
299                self.0.nth_back(n)
300            }
301        }
302
303        impl$(<$a>)? ::core::iter::ExactSizeIterator for $iterator$(<$a>)? {
304            #[inline]
305            fn len(&self) -> ::core::primitive::usize {
306                self.0.len()
307            }
308        }
309        impl$(<$a>)? ::core::iter::FusedIterator for $iterator$(<$a>)? {}
310    };
311}
312
313/// This macro implements the data access API for rules that have only nodes and no weights.
314/// It takes in the name of the a rule struct that contans a field with the name `nodes`
315/// of the type `Vec<Node>`. As well as the names it should give the iterator over its
316/// nodes and the iterator returned by the [`IntoIterator`] trait.
317#[macro_export]
318#[doc(hidden)]
319macro_rules! __impl_node_rule {
320    ($quadrature_rule:ident, $quadrature_rule_iter:ident, $quadrature_rule_into_iter:ident) => {
321        // Lets the user do
322        // for node in QuadratureRule::new(...) {
323        //    ...
324        // }
325        impl ::core::iter::IntoIterator for $quadrature_rule {
326            type Item = $crate::Node;
327            type IntoIter = $quadrature_rule_into_iter;
328            #[inline]
329            fn into_iter(self) -> Self::IntoIter {
330                $quadrature_rule_into_iter::new(self.nodes.into_iter())
331            }
332        }
333
334        // Lets the user do
335        // let rule = QuadratureRule::new(...);
336        // for &node in &rule {
337        //     ...
338        // }
339        // rule.integrate(...) // <--- still available
340        impl<'a> ::core::iter::IntoIterator for &'a $quadrature_rule {
341            type IntoIter = $quadrature_rule_iter<'a>;
342            type Item = &'a $crate::Node;
343            #[inline]
344            fn into_iter(self) -> Self::IntoIter {
345                $quadrature_rule_iter::new(self.nodes.iter())
346            }
347        }
348
349        impl $quadrature_rule {
350            /// Returns an iterator over the nodes of the rule.
351            #[inline]
352            pub fn iter(&self) -> $quadrature_rule_iter<'_> {
353                $quadrature_rule_iter::new(self.nodes.iter())
354            }
355
356            /// Returns a slice of all the nodes of the rule.
357            #[inline]
358            pub fn as_nodes(&self) -> &[$crate::Node] {
359                &self.nodes
360            }
361
362            /// Converts the rule into a vector of nodes.
363            ///
364            /// This function just returns the underlying data without any computation or cloning.
365            #[inline]
366            #[must_use = "`self` will be dropped if the result is not used"]
367            pub fn into_nodes(self) -> Vec<$crate::Node> {
368                self.nodes
369            }
370
371            /// Returns the degree of the rule.
372            #[inline]
373            pub fn degree(&self) -> usize {
374                self.nodes.len()
375            }
376        }
377
378        // region: QuadratureRuleIter
379
380        /// An iterator of the nodes of the rule.
381        #[derive(Debug, Clone)]
382        #[must_use = "iterators are lazy and do nothing unless consumed"]
383        pub struct $quadrature_rule_iter<'a>(::core::slice::Iter<'a, $crate::Node>);
384
385        impl<'a> $quadrature_rule_iter<'a> {
386            #[inline]
387            const fn new(iter: ::core::slice::Iter<'a, $crate::Node>) -> Self {
388                Self(iter)
389            }
390
391            /// Views the underlying data as a subslice of the original data.
392            ///
393            /// See [`core::slice::Iter::as_slice`] for more information.
394            #[inline]
395            pub fn as_slice(&self) -> &'a [$crate::Node] {
396                self.0.as_slice()
397            }
398        }
399
400        impl<'a> ::core::convert::AsRef<[$crate::Node]> for $quadrature_rule_iter<'a> {
401            #[inline]
402            fn as_ref(&self) -> &[$crate::Node] {
403                self.0.as_ref()
404            }
405        }
406
407        $crate::__impl_slice_iterator_newtype_traits! {$quadrature_rule_iter<'a>, &'a $crate::Node}
408
409        // endregion: QuadratureRuleIter
410
411        // region: QuadratureRuleIntoIter
412
413        /// An owning iterator over the nodes of the rule.
414        ///
415        /// Created by the [`IntoIterator`] trait implementation of the rule struct.
416        #[derive(::core::fmt::Debug, ::core::clone::Clone)]
417        #[must_use = "iterators are lazy and do nothing unless consumed"]
418        pub struct $quadrature_rule_into_iter(::std::vec::IntoIter<$crate::Node>);
419
420        impl $quadrature_rule_into_iter {
421            #[inline]
422            const fn new(iter: ::std::vec::IntoIter<$crate::Node>) -> Self {
423                Self(iter)
424            }
425
426            /// Views the underlying data as a subslice of the original data.
427            ///
428            /// See [`std::vec::IntoIter::as_slice`] for more information.
429            #[inline]
430            pub fn as_slice(&self) -> &[$crate::Node] {
431                self.0.as_slice()
432            }
433        }
434
435        impl ::core::convert::AsRef<[$crate::Node]> for $quadrature_rule_into_iter {
436            #[inline]
437            fn as_ref(&self) -> &[$crate::Node] {
438                self.0.as_ref()
439            }
440        }
441
442        $crate::__impl_slice_iterator_newtype_traits! {$quadrature_rule_into_iter, $crate::Node}
443
444        // endregion: QuadratureRuleIntoIter
445    };
446}
447
448#[cfg(test)]
449mod tests {
450    use super::*;
451    use core::fmt;
452    use std::backtrace::Backtrace;
453
454    #[derive(Debug, Clone, PartialEq)]
455    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
456    pub struct MockQuadrature {
457        node_weight_pairs: Vec<(Node, Weight)>,
458    }
459
460    #[derive(Debug)]
461    pub struct MockQuadratureError(Backtrace);
462
463    impl MockQuadratureError {
464        pub fn backtrace(&self) -> &Backtrace {
465            &self.0
466        }
467    }
468
469    impl fmt::Display for MockQuadratureError {
470        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
471            write!(f, "wrong! bad! T_T")
472        }
473    }
474
475    impl std::error::Error for MockQuadratureError {}
476
477    impl MockQuadrature {
478        pub fn new(deg: usize) -> Result<Self, MockQuadratureError> {
479            if deg < 1 {
480                return Err(MockQuadratureError(Backtrace::capture()));
481            }
482
483            Ok(Self {
484                node_weight_pairs: (0..deg).map(|d| (d as f64, 1.0)).collect(),
485            })
486        }
487
488        pub fn integrate<F>(&self, a: f64, b: f64, integrand: F) -> f64
489        where
490            F: Fn(f64) -> f64,
491        {
492            let rect_width = (b - a) / self.node_weight_pairs.len() as f64;
493            let result: f64 = self
494                .node_weight_pairs
495                .iter()
496                .map(|(x_val, w_val)| integrand(a + rect_width * (0.5 + x_val)) * w_val)
497                .sum();
498            result * rect_width
499        }
500    }
501
502    __impl_node_weight_rule! {MockQuadrature, MockQuadratureNodes, MockQuadratureWeights, MockQuadratureIter, MockQuadratureIntoIter}
503
504    #[test]
505    fn test_macro_implementation() {
506        let quad = MockQuadrature::new(5).unwrap();
507        assert_eq!(quad.integrate(0.0, 1.0, |x| x), 0.5);
508
509        // Test iterator implementations
510        assert_eq!(quad.nodes().count(), 5);
511        assert_eq!(quad.weights().count(), 5);
512        assert_eq!(quad.iter().count(), 5);
513        assert_eq!(quad.as_node_weight_pairs().len(), 5);
514
515        // Test IntoIterator implementation
516        let vec: Vec<(Node, Weight)> = quad.clone().into_iter().collect();
517        assert_eq!(vec.len(), 5);
518
519        // Test into_node_weight_pairs
520        let pairs = quad.clone().into_node_weight_pairs();
521        assert_eq!(pairs.len(), 5);
522
523        // Test degree
524        assert_eq!(quad.degree(), 5);
525
526        // test iter functions
527        let mut quad_iter = (&quad).into_iter();
528        assert_eq!(quad_iter.next().unwrap().0, 0.0);
529        assert_eq!(quad_iter.next().unwrap().0, 1.0);
530
531        let quad_iter = (&quad).into_iter();
532        assert_eq!(quad_iter.size_hint(), (5, Some(5)));
533
534        let mut quad_iter = (&quad).into_iter();
535        assert_eq!(quad_iter.nth(2).unwrap().0, 2.0);
536
537        let quad_iter = (&quad).into_iter();
538        assert_eq!(quad_iter.count(), 5);
539
540        let quad_iter = (&quad).into_iter();
541        assert_eq!(quad_iter.last().unwrap().0, 4.0);
542
543        let mut quad_iter = (&quad).into_iter();
544        assert_eq!(quad_iter.next_back().unwrap().0, 4.0);
545
546        let mut quad_iter = (&quad).into_iter();
547        assert_eq!(quad_iter.nth_back(1).unwrap().0, 3.0);
548
549        let quad_iter = (&quad).into_iter();
550        assert_eq!(quad_iter.len(), 5);
551
552        // test slice
553        let quad_slice = (&quad).into_iter().as_slice();
554        assert_eq!(quad_slice.len(), 5);
555        assert_eq!(quad_slice[2].0, 2.0);
556
557        // test as_ref
558        let quad_iter = (&quad).into_iter();
559        let quad_ref = quad_iter.as_ref();
560        assert_eq!(quad_ref.len(), 5);
561        assert_eq!(quad_ref[2].0, 2.0);
562    }
563}