1use crate::math::pow;
17
18use core::{fmt, num::ParseFloatError, str::FromStr};
19
20pub type Node = f64;
22pub type Weight = f64;
24
25#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27#[cfg_attr(
28 feature = "rkyv",
29 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
30)]
31#[cfg_attr(
32 feature = "zerocopy",
33 derive(
34 zerocopy::KnownLayout,
35 zerocopy::Immutable,
36 zerocopy::FromZeros,
37 zerocopy::IntoBytes
38 )
39)]
40pub struct FiniteAboveNegOneF64(f64);
42
43impl fmt::Display for FiniteAboveNegOneF64 {
44 #[inline]
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(f, "{}", self.0)
47 }
48}
49
50impl fmt::LowerExp for FiniteAboveNegOneF64 {
51 #[inline]
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "{:e}", self.0)
54 }
55}
56
57impl fmt::UpperExp for FiniteAboveNegOneF64 {
58 #[inline]
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(f, "{:E}", self.0)
61 }
62}
63
64impl FiniteAboveNegOneF64 {
65 #[inline]
67 pub const fn new(value: f64) -> Option<Self> {
68 if value > -1.0 && value.is_finite() {
69 Some(Self(value))
70 } else {
71 None
72 }
73 }
74
75 #[inline]
77 pub const fn get(&self) -> f64 {
78 self.0
79 }
80
81 #[inline]
82 pub const fn checked_add(self, other: f64) -> Option<Self> {
83 Self::new(self.get() + other)
84 }
85
86 #[inline]
87 pub const fn checked_sub(self, other: f64) -> Option<Self> {
88 Self::new(self.get() - other)
89 }
90
91 #[inline]
92 pub const fn checked_mul(self, other: f64) -> Option<Self> {
93 Self::new(self.get() * other)
94 }
95
96 #[inline]
97 pub const fn checked_div(self, denominator: f64) -> Option<Self> {
98 Self::new(self.get() / denominator)
99 }
100
101 #[inline]
102 pub fn checked_pow(self, exp: f64) -> Option<Self> {
103 Self::new(pow(self.get(), exp))
104 }
105}
106
107impl Default for FiniteAboveNegOneF64 {
108 #[inline]
110 fn default() -> Self {
111 Self(0.0)
112 }
113}
114
115#[derive(Debug, Copy, Clone, PartialEq, Eq)]
116pub struct InfNanNegOneOrLessError;
119
120impl fmt::Display for InfNanNegOneOrLessError {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 write!(
123 f,
124 "attempted to convert a value that is infinite, NAN, or less than or equal to -1.0 to a `FiniteAboveNegOneF64`"
125 )
126 }
127}
128
129impl core::error::Error for InfNanNegOneOrLessError {}
130
131impl TryFrom<f64> for FiniteAboveNegOneF64 {
132 type Error = InfNanNegOneOrLessError;
133
134 #[inline]
138 fn try_from(value: f64) -> Result<Self, Self::Error> {
139 FiniteAboveNegOneF64::new(value).ok_or(InfNanNegOneOrLessError)
140 }
141}
142
143impl From<FiniteAboveNegOneF64> for f64 {
144 #[inline]
145 fn from(value: FiniteAboveNegOneF64) -> Self {
146 value.0
147 }
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
151pub enum ParseFiniteAboveNegOneF64Error {
153 ParseError(ParseFloatError),
154 InvalidValue(InfNanNegOneOrLessError),
155}
156
157impl fmt::Display for ParseFiniteAboveNegOneF64Error {
158 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159 match self {
160 ParseFiniteAboveNegOneF64Error::ParseError(e) => write!(f, "{e}"),
161 ParseFiniteAboveNegOneF64Error::InvalidValue(e) => write!(f, "{e}"),
162 }
163 }
164}
165
166impl core::error::Error for ParseFiniteAboveNegOneF64Error {
167 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
168 match self {
169 ParseFiniteAboveNegOneF64Error::ParseError(e) => Some(e),
170 ParseFiniteAboveNegOneF64Error::InvalidValue(e) => Some(e),
171 }
172 }
173}
174
175impl FromStr for FiniteAboveNegOneF64 {
176 type Err = ParseFiniteAboveNegOneF64Error;
177
178 fn from_str(s: &str) -> Result<Self, Self::Err> {
179 match s.parse::<f64>() {
180 Ok(value) => value
181 .try_into()
182 .map_err(ParseFiniteAboveNegOneF64Error::InvalidValue),
183 Err(e) => Err(ParseFiniteAboveNegOneF64Error::ParseError(e)),
184 }
185 }
186}
187
188#[doc(hidden)]
193#[macro_export]
194macro_rules! __impl_node_weight_rule {
195 (
196 $quadrature_rule:ident,
198 $quadrature_rule_nodes:ident,
200 $quadrature_rule_weights:ident,
202 $quadrature_rule_iter:ident,
205 $quadrature_rule_into_iter:ident
207 ) => {
208 impl ::core::iter::IntoIterator for $quadrature_rule {
217 type IntoIter = $quadrature_rule_into_iter;
218 type Item = ($crate::Node, $crate::Weight);
219 #[inline]
220 fn into_iter(self) -> Self::IntoIter {
221 $quadrature_rule_into_iter::new(self.node_weight_pairs.into_iter())
222 }
223 }
224
225 impl<'a> ::core::iter::IntoIterator for &'a $quadrature_rule {
232 type IntoIter = $quadrature_rule_iter<'a>;
233 type Item = &'a ($crate::Node, $crate::Weight);
234 #[inline]
235 fn into_iter(self) -> Self::IntoIter {
236 $quadrature_rule_iter::new(self.node_weight_pairs.iter())
237 }
238 }
239
240 impl $quadrature_rule {
241 #[inline]
243 pub fn nodes(&self) -> $quadrature_rule_nodes<'_> {
244 $quadrature_rule_nodes::new(self.node_weight_pairs.iter().map(|p| &p.0))
245 }
246
247 #[inline]
249 pub fn weights(&self) -> $quadrature_rule_weights<'_> {
250 $quadrature_rule_weights::new(self.node_weight_pairs.iter().map(|p| &p.1))
251 }
252
253 #[inline]
255 pub fn iter(&self) -> $quadrature_rule_iter<'_> {
256 $quadrature_rule_iter::new(self.node_weight_pairs.iter())
257 }
258
259 #[inline]
261 pub fn as_node_weight_pairs(&self) -> &[($crate::Node, $crate::Weight)] {
262 &self.node_weight_pairs
263 }
264
265 #[inline]
269 #[must_use = "`self` will be dropped if the result is not used"]
270 pub fn into_node_weight_pairs(self) -> ::alloc::boxed::Box<[($crate::Node, $crate::Weight)]> {
271 self.node_weight_pairs
272 }
273
274 #[inline]
276 pub fn degree(&self) -> ::core::primitive::usize {
277 self.node_weight_pairs.len()
278 }
279 }
280
281 #[derive(::core::fmt::Debug, ::core::clone::Clone)]
285 #[must_use = "iterators are lazy and do nothing unless consumed"]
286 pub struct $quadrature_rule_nodes<'a>(
287 ::core::iter::Map<
290 ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
291 fn(&'a ($crate::Node, $crate::Weight)) -> &'a $crate::Node,
292 >,
293 );
294
295 impl<'a> $quadrature_rule_nodes<'a> {
296 #[inline]
297 const fn new(
298 iter_map: ::core::iter::Map<
299 ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
300 fn(&'a ($crate::Node, $crate::Weight)) -> &'a $crate::Node,
301 >,
302 ) -> Self {
303 Self(iter_map)
304 }
305 }
306
307 $crate::__impl_slice_iterator_newtype_traits!{$quadrature_rule_nodes<'a>, &'a $crate::Node}
308
309 #[derive(::core::fmt::Debug, ::core::clone::Clone)]
315 #[must_use = "iterators are lazy and do nothing unless consumed"]
316 pub struct $quadrature_rule_weights<'a>(
317 ::core::iter::Map<
319 ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
320 fn(&'a ($crate::Node, $crate::Weight)) -> &'a $crate::Weight,
321 >,
322 );
323
324 impl<'a> $quadrature_rule_weights<'a> {
325 #[inline]
326 const fn new(
327 iter_map: ::core::iter::Map<
328 ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
329 fn(&'a ($crate::Node, $crate::Weight)) -> &'a $crate::Weight,
330 >,
331 ) -> Self {
332 Self(iter_map)
333 }
334 }
335
336 $crate::__impl_slice_iterator_newtype_traits!{$quadrature_rule_weights<'a>, &'a $crate::Weight}
337
338 #[derive(::core::fmt::Debug, ::core::clone::Clone)]
346 #[must_use = "iterators are lazy and do nothing unless consumed"]
347 pub struct $quadrature_rule_iter<'a>(
348 ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
349 );
350
351 impl<'a> $quadrature_rule_iter<'a> {
352 #[inline]
353 const fn new(
354 node_weight_pairs: ::core::slice::Iter<'a, ($crate::Node, $crate::Weight)>,
355 ) -> Self {
356 Self(node_weight_pairs)
357 }
358
359 #[inline]
363 pub fn as_slice(&self) -> &'a [($crate::Node, $crate::Weight)] {
364 self.0.as_slice()
365 }
366 }
367
368 impl<'a> ::core::convert::AsRef<[($crate::Node, $crate::Weight)]>
369 for $quadrature_rule_iter<'a>
370 {
371 #[inline]
372 fn as_ref(&self) -> &[($crate::Node, $crate::Weight)] {
373 self.0.as_ref()
374 }
375 }
376
377 $crate::__impl_slice_iterator_newtype_traits!{$quadrature_rule_iter<'a>, &'a ($crate::Node, $crate::Weight)}
378
379 #[derive(::core::fmt::Debug, ::core::clone::Clone)]
387 #[must_use = "iterators are lazy and do nothing unless consumed"]
388 pub struct $quadrature_rule_into_iter(::alloc::vec::IntoIter<($crate::Node, $crate::Weight)>);
389
390 impl $quadrature_rule_into_iter {
391 #[inline]
392 const fn new(
393 node_weight_pairs: ::alloc::vec::IntoIter<($crate::Node, $crate::Weight)>,
394 ) -> Self {
395 Self(node_weight_pairs)
396 }
397
398 #[inline]
402 pub fn as_slice(&self) -> &[($crate::Node, $crate::Weight)] {
403 self.0.as_slice()
404 }
405 }
406
407 impl ::core::convert::AsRef<[($crate::Node, $crate::Weight)]>
408 for $quadrature_rule_into_iter
409 {
410 #[inline]
411 fn as_ref(&self) -> &[($crate::Node, $crate::Weight)] {
412 self.0.as_ref()
413 }
414 }
415
416 $crate::__impl_slice_iterator_newtype_traits!{$quadrature_rule_into_iter, ($crate::Node, $crate::Weight)}
417
418 };
420}
421
422#[macro_export]
426#[doc(hidden)]
427macro_rules! __impl_slice_iterator_newtype_traits {
428 ($iterator:ident$(<$a:lifetime>)?, $item:ty) => {
429 impl$(<$a>)? ::core::iter::Iterator for $iterator$(<$a>)? {
430 type Item = $item;
431 #[inline]
432 fn next(&mut self) -> ::core::option::Option<Self::Item> {
433 self.0.next()
434 }
435
436 #[inline]
437 fn size_hint(&self) -> (::core::primitive::usize, ::core::option::Option<::core::primitive::usize>) {
438 self.0.size_hint()
439 }
440
441 #[inline]
447 fn nth(&mut self, index: ::core::primitive::usize) -> ::core::option::Option<Self::Item> {
448 self.0.nth(index)
449 }
450
451 #[inline]
452 fn count(self) -> ::core::primitive::usize {
453 self.0.count()
454 }
455
456 #[inline]
457 fn last(mut self) -> ::core::option::Option<Self::Item> {
458 self.0.next_back()
459 }
460 }
461
462 impl$(<$a>)? ::core::iter::DoubleEndedIterator for $iterator$(<$a>)? {
463 #[inline]
464 fn next_back(&mut self) -> ::core::option::Option<Self::Item> {
465 self.0.next_back()
466 }
467
468 #[inline]
469 fn nth_back(&mut self, n: ::core::primitive::usize) -> ::core::option::Option<Self::Item> {
470 self.0.nth_back(n)
471 }
472 }
473
474 impl$(<$a>)? ::core::iter::ExactSizeIterator for $iterator$(<$a>)? {
475 #[inline]
476 fn len(&self) -> ::core::primitive::usize {
477 self.0.len()
478 }
479 }
480 impl$(<$a>)? ::core::iter::FusedIterator for $iterator$(<$a>)? {}
481 };
482}
483
484#[macro_export]
489#[doc(hidden)]
490macro_rules! __impl_node_rule {
491 ($quadrature_rule:ident, $quadrature_rule_iter:ident) => {
492 impl ::core::iter::IntoIterator for $quadrature_rule {
497 type Item = $crate::Node;
498 type IntoIter = $quadrature_rule_iter;
499 #[inline]
500 fn into_iter(self) -> Self::IntoIter {
501 self.iter()
502 }
503 }
504
505 impl<'a> ::core::iter::IntoIterator for &'a $quadrature_rule {
512 type IntoIter = $quadrature_rule_iter;
513 type Item = $crate::Node;
514 #[inline]
515 fn into_iter(self) -> Self::IntoIter {
516 self.iter()
517 }
518 }
519
520 impl $quadrature_rule {
521 #[inline]
523 pub fn iter(&self) -> $quadrature_rule_iter {
524 $quadrature_rule_iter::new((0..self.degree.get()).map(|d| d as $crate::Node))
525 }
526
527 #[inline]
529 pub fn degree(&self) -> ::core::num::NonZeroU32 {
530 self.degree
531 }
532 }
533
534 #[derive(Debug, Clone)]
538 #[must_use = "iterators are lazy and do nothing unless consumed"]
539 pub struct $quadrature_rule_iter(
540 ::core::iter::Map<core::ops::Range<u32>, fn(u32) -> $crate::Node>,
541 );
542
543 impl $quadrature_rule_iter {
544 #[inline]
545 const fn new(
546 iter: ::core::iter::Map<core::ops::Range<u32>, fn(u32) -> $crate::Node>,
547 ) -> Self {
548 Self(iter)
549 }
550 }
551
552 $crate::__impl_slice_iterator_newtype_traits! {$quadrature_rule_iter, $crate::Node}
553
554 };
556}
557
558#[cfg(test)]
559mod tests {
560 use super::*;
561 use alloc::{boxed::Box, vec::Vec};
562 use core::f64;
563 use core::num::NonZeroUsize;
564
565 #[derive(Debug, Clone, PartialEq)]
566 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
567 pub struct MockQuadrature {
568 node_weight_pairs: Box<[(Node, Weight)]>,
569 }
570
571 impl MockQuadrature {
572 pub fn new(deg: NonZeroUsize) -> Self {
573 Self {
574 node_weight_pairs: (0..deg.get()).map(|d| (d as f64, 1.0)).collect(),
575 }
576 }
577
578 pub fn integrate<F>(&self, a: f64, b: f64, integrand: F) -> f64
579 where
580 F: Fn(f64) -> f64,
581 {
582 let rect_width = (b - a) / self.node_weight_pairs.len() as f64;
583 let result: f64 = self
584 .node_weight_pairs
585 .iter()
586 .map(|(x_val, w_val)| integrand(a + rect_width * (0.5 + x_val)) * w_val)
587 .sum();
588 result * rect_width
589 }
590 }
591
592 __impl_node_weight_rule! {MockQuadrature, MockQuadratureNodes, MockQuadratureWeights, MockQuadratureIter, MockQuadratureIntoIter}
593
594 #[test]
595 fn test_macro_implementation() {
596 let quad = MockQuadrature::new(5.try_into().unwrap());
597 assert_eq!(quad.integrate(0.0, 1.0, |x| x), 0.5);
598
599 assert_eq!(quad.nodes().count(), 5);
601 assert_eq!(quad.weights().count(), 5);
602 assert_eq!(quad.iter().count(), 5);
603 assert_eq!(quad.as_node_weight_pairs().len(), 5);
604
605 let vec: Vec<(Node, Weight)> = quad.clone().into_iter().collect();
607 assert_eq!(vec.len(), 5);
608
609 let pairs = quad.clone().into_node_weight_pairs();
611 assert_eq!(pairs.len(), 5);
612
613 assert_eq!(quad.degree(), 5);
615
616 let mut quad_iter = (&quad).into_iter();
618 assert_eq!(quad_iter.next().unwrap().0, 0.0);
619 assert_eq!(quad_iter.next().unwrap().0, 1.0);
620
621 let quad_iter = (&quad).into_iter();
622 assert_eq!(quad_iter.size_hint(), (5, Some(5)));
623
624 let mut quad_iter = (&quad).into_iter();
625 assert_eq!(quad_iter.nth(2).unwrap().0, 2.0);
626
627 let quad_iter = (&quad).into_iter();
628 assert_eq!(quad_iter.count(), 5);
629
630 let quad_iter = (&quad).into_iter();
631 assert_eq!(quad_iter.last().unwrap().0, 4.0);
632
633 let mut quad_iter = (&quad).into_iter();
634 assert_eq!(quad_iter.next_back().unwrap().0, 4.0);
635
636 let mut quad_iter = (&quad).into_iter();
637 assert_eq!(quad_iter.nth_back(1).unwrap().0, 3.0);
638
639 let quad_iter = (&quad).into_iter();
640 assert_eq!(quad_iter.len(), 5);
641
642 let quad_slice = (&quad).into_iter().as_slice();
644 assert_eq!(quad_slice.len(), 5);
645 assert_eq!(quad_slice[2].0, 2.0);
646
647 let quad_iter = (&quad).into_iter();
649 let quad_ref = quad_iter.as_ref();
650 assert_eq!(quad_ref.len(), 5);
651 assert_eq!(quad_ref[2].0, 2.0);
652 }
653
654 #[test]
655 fn test_new_finite_above_neg_one_f64() {
656 assert!(FiniteAboveNegOneF64::new(0.0).is_some());
657 assert!(FiniteAboveNegOneF64::new(-0.5).is_some());
658 assert!(FiniteAboveNegOneF64::new(-1.0).is_none());
659 assert!(FiniteAboveNegOneF64::new(f64::NAN).is_none());
660 assert!(FiniteAboveNegOneF64::new(f64::INFINITY).is_none());
661 assert!(FiniteAboveNegOneF64::new(f64::NEG_INFINITY).is_none());
662 }
663
664 #[test]
665 fn test_try_from_f64() {
666 assert!(FiniteAboveNegOneF64::try_from(0.0).is_ok());
667 assert!(FiniteAboveNegOneF64::try_from(-0.5).is_ok());
668 assert!(FiniteAboveNegOneF64::try_from(-1.0).is_err());
669 assert!(FiniteAboveNegOneF64::try_from(f64::NAN).is_err());
670 assert!(FiniteAboveNegOneF64::try_from(f64::INFINITY).is_err());
671 assert!(FiniteAboveNegOneF64::try_from(f64::NEG_INFINITY).is_err());
672 }
673
674 #[test]
675 fn test_from_str() {
676 assert_eq!(FiniteAboveNegOneF64::from_str("0.0").unwrap().get(), 0.0);
677 assert_eq!(FiniteAboveNegOneF64::from_str("-0.5").unwrap().get(), -0.5);
678 assert_eq!(
679 FiniteAboveNegOneF64::from_str("-1.0"),
680 Err(ParseFiniteAboveNegOneF64Error::InvalidValue(
681 InfNanNegOneOrLessError
682 ))
683 );
684 assert_eq!(
685 FiniteAboveNegOneF64::from_str("NAN"),
686 Err(ParseFiniteAboveNegOneF64Error::InvalidValue(
687 InfNanNegOneOrLessError
688 ))
689 );
690 assert_eq!(
691 FiniteAboveNegOneF64::from_str("INF"),
692 Err(ParseFiniteAboveNegOneF64Error::InvalidValue(
693 InfNanNegOneOrLessError
694 ))
695 );
696 assert_eq!(
697 FiniteAboveNegOneF64::from_str("-INF"),
698 Err(ParseFiniteAboveNegOneF64Error::InvalidValue(
699 InfNanNegOneOrLessError
700 ))
701 );
702 }
703
704 #[test]
705 fn test_finite_above_neg_one_f64_arithmetic() {
706 let value = FiniteAboveNegOneF64::new(0.5).unwrap();
707 assert_eq!(value.checked_add(0.5).unwrap().get(), 1.0);
708 assert!(value.checked_add(f64::INFINITY).is_none());
709 assert!(value.checked_sub(2.0).is_none());
710 assert!(value.checked_add(f64::NAN).is_none());
711 assert_eq!(value.checked_mul(2.0).unwrap().get(), 1.0);
712 assert!(value.checked_div(0.0).is_none());
713 assert!(value.checked_div(0.0).is_none());
714 assert!(value.checked_div(f64::NAN).is_none());
715 assert_eq!(value.checked_pow(2.0).unwrap().get(), 0.25);
716 assert!(
717 FiniteAboveNegOneF64::new(2.0)
718 .unwrap()
719 .checked_pow(f64::INFINITY)
720 .is_none()
721 );
722 }
723
724 #[test]
725 fn test_from_impl() {
726 let value = FiniteAboveNegOneF64::new(0.5).unwrap();
727 let f64_value: f64 = value.into();
728 assert_eq!(f64_value, 0.5);
729
730 let value_from_f64: Result<FiniteAboveNegOneF64, _> = 0.5f64.try_into();
731 assert!(value_from_f64.is_ok());
732 assert_eq!(value_from_f64.unwrap().get(), 0.5);
733
734 let value_from_invalid: Result<FiniteAboveNegOneF64, _> = (-1.0f64).try_into();
735 assert!(value_from_invalid.is_err());
736 }
737
738 #[test]
739 fn test_default() {
740 let value = FiniteAboveNegOneF64::default();
741 assert_eq!(value.get(), 0.0);
742 }
743}