1#[cfg(feature = "rayon")]
23use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
24
25use crate::gamma::gamma;
26use crate::{
27 DMatrixf64, GaussChebyshevFirstKind, GaussChebyshevSecondKind, GaussLegendre, Node, Weight,
28 __impl_node_weight_rule,
29};
30
31use std::backtrace::Backtrace;
32
33#[derive(Debug, Clone, PartialEq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55pub struct GaussJacobi {
56 node_weight_pairs: Vec<(Node, Weight)>,
57 alpha: f64,
58 beta: f64,
59}
60
61impl GaussJacobi {
62 pub fn new(deg: usize, alpha: f64, beta: f64) -> Result<Self, GaussJacobiError> {
75 match (
76 deg >= 2,
77 (alpha.is_finite() && alpha > -1.0),
78 (beta.is_finite() && beta > -1.0),
79 ) {
80 (true, true, true) => Ok(()),
81 (false, true, true) => Err(GaussJacobiErrorReason::Degree),
82 (true, false, true) => Err(GaussJacobiErrorReason::Alpha),
83 (true, true, false) => Err(GaussJacobiErrorReason::Beta),
84 (true, false, false) => Err(GaussJacobiErrorReason::AlphaBeta),
85 (false, false, true) => Err(GaussJacobiErrorReason::DegreeAlpha),
86 (false, true, false) => Err(GaussJacobiErrorReason::DegreeBeta),
87 (false, false, false) => Err(GaussJacobiErrorReason::DegreeAlphaBeta),
88 }
89 .map_err(GaussJacobiError::new)?;
90
91 match (alpha, beta) {
98 (0.0, 0.0) => return Ok(GaussLegendre::new(deg).unwrap().into()),
99 (-0.5, -0.5) => return Ok(GaussChebyshevFirstKind::new(deg).unwrap().into()),
100 (0.5, 0.5) => return Ok(GaussChebyshevSecondKind::new(deg).unwrap().into()),
101 _ => (),
102 }
103
104 let mut companion_matrix = DMatrixf64::from_element(deg, deg, 0.0);
105
106 let mut diag = (beta - alpha) / (2.0 + beta + alpha);
107 for idx in 0..deg - 1 {
109 let idx_f64 = idx as f64;
110 let idx_p1 = idx_f64 + 1.0;
111 let denom_sum = 2.0 * idx_p1 + alpha + beta;
112 let off_diag = 2.0 / denom_sum
113 * (idx_p1 * (idx_p1 + alpha) * (idx_p1 + beta) * (idx_p1 + alpha + beta)
114 / ((denom_sum + 1.0) * (denom_sum - 1.0)))
115 .sqrt();
116 unsafe {
117 *companion_matrix.get_unchecked_mut((idx, idx)) = diag;
118 *companion_matrix.get_unchecked_mut((idx, idx + 1)) = off_diag;
119 *companion_matrix.get_unchecked_mut((idx + 1, idx)) = off_diag;
120 }
121 diag = (beta * beta - alpha * alpha) / (denom_sum * (denom_sum + 2.0));
122 }
123 unsafe {
124 *companion_matrix.get_unchecked_mut((deg - 1, deg - 1)) = diag;
125 }
126 let eigen = companion_matrix.symmetric_eigen();
128
129 let scale_factor =
130 (2.0f64).powf(alpha + beta + 1.0) * gamma(alpha + 1.0) * gamma(beta + 1.0)
131 / gamma(alpha + beta + 1.0)
132 / (alpha + beta + 1.0);
133
134 let mut node_weight_pairs: Vec<(f64, f64)> = eigen
136 .eigenvalues
137 .iter()
138 .copied()
139 .zip(
140 eigen
141 .eigenvectors
142 .row(0)
143 .iter()
144 .map(|x| x * x * scale_factor),
145 )
146 .collect();
147
148 node_weight_pairs.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
149
150 if deg % 2 == 1 {
154 node_weight_pairs[deg / 2].0 = 0.0;
155 }
156
157 Ok(Self {
158 node_weight_pairs,
159 alpha,
160 beta,
161 })
162 }
163
164 fn argument_transformation(x: f64, a: f64, b: f64) -> f64 {
165 0.5 * ((b - a) * x + (b + a))
166 }
167
168 fn scale_factor(a: f64, b: f64) -> f64 {
169 0.5 * (b - a)
170 }
171
172 pub fn integrate<F>(&self, a: f64, b: f64, integrand: F) -> f64
176 where
177 F: Fn(f64) -> f64,
178 {
179 let result: f64 = self
180 .node_weight_pairs
181 .iter()
182 .map(|(x_val, w_val)| integrand(Self::argument_transformation(*x_val, a, b)) * w_val)
183 .sum();
184 Self::scale_factor(a, b) * result
185 }
186
187 #[cfg(feature = "rayon")]
188 pub fn par_integrate<F>(&self, a: f64, b: f64, integrand: F) -> f64
190 where
191 F: Fn(f64) -> f64 + Sync,
192 {
193 let result: f64 = self
194 .node_weight_pairs
195 .par_iter()
196 .map(|(x_val, w_val)| integrand(Self::argument_transformation(*x_val, a, b)) * w_val)
197 .sum();
198 Self::scale_factor(a, b) * result
199 }
200
201 #[inline]
203 pub const fn alpha(&self) -> f64 {
204 self.alpha
205 }
206
207 #[inline]
209 pub const fn beta(&self) -> f64 {
210 self.beta
211 }
212}
213
214__impl_node_weight_rule! {GaussJacobi, GaussJacobiNodes, GaussJacobiWeights, GaussJacobiIter, GaussJacobiIntoIter}
215
216#[derive(Debug)]
219pub struct GaussJacobiError {
220 reason: GaussJacobiErrorReason,
221 backtrace: Backtrace,
222}
223
224impl GaussJacobiError {
225 #[inline]
227 pub(crate) fn new(reason: GaussJacobiErrorReason) -> Self {
228 Self {
229 reason,
230 backtrace: Backtrace::capture(),
231 }
232 }
233
234 #[inline]
236 pub fn reason(&self) -> GaussJacobiErrorReason {
237 self.reason
238 }
239
240 #[inline]
244 pub fn backtrace(&self) -> &Backtrace {
245 &self.backtrace
246 }
247}
248
249use core::fmt;
250impl fmt::Display for GaussJacobiError {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 const DEGREE_LIMIT: &str = "must be at least 2";
253 const EXPONENT_LIMIT: &str = "must be finite and larger than -1.0";
254 match self.reason() {
255 GaussJacobiErrorReason::Degree => write!(f, "degree {DEGREE_LIMIT}"),
256 GaussJacobiErrorReason::Alpha => write!(f, "alpha {EXPONENT_LIMIT}"),
257 GaussJacobiErrorReason::Beta => write!(f, "beta {EXPONENT_LIMIT}"),
258 GaussJacobiErrorReason::AlphaBeta => write!(f, "alpha and beta {EXPONENT_LIMIT}"),
259 GaussJacobiErrorReason::DegreeAlpha => {
260 write!(f, "degree {DEGREE_LIMIT} and alpha {EXPONENT_LIMIT}")
261 }
262 GaussJacobiErrorReason::DegreeBeta => {
263 write!(f, "degree {DEGREE_LIMIT} and beta {EXPONENT_LIMIT}")
264 }
265 GaussJacobiErrorReason::DegreeAlphaBeta => {
266 write!(f, "degree {DEGREE_LIMIT}, alpha and beta {EXPONENT_LIMIT}")
267 }
268 }
269 }
270}
271
272impl std::error::Error for GaussJacobiError {}
273
274impl From<GaussLegendre> for GaussJacobi {
276 fn from(value: GaussLegendre) -> Self {
277 let mut node_weight_pairs = value.into_node_weight_pairs();
278 node_weight_pairs.reverse();
282 Self {
283 node_weight_pairs,
284 alpha: 0.0,
285 beta: 0.0,
286 }
287 }
288}
289
290impl From<GaussChebyshevFirstKind> for GaussJacobi {
292 fn from(value: GaussChebyshevFirstKind) -> Self {
293 Self {
294 node_weight_pairs: value.into_node_weight_pairs(),
295 alpha: -0.5,
296 beta: -0.5,
297 }
298 }
299}
300
301impl From<GaussChebyshevSecondKind> for GaussJacobi {
303 fn from(value: GaussChebyshevSecondKind) -> Self {
304 Self {
305 node_weight_pairs: value.into_node_weight_pairs(),
306 alpha: 0.5,
307 beta: 0.5,
308 }
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
314#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
315pub enum GaussJacobiErrorReason {
316 Degree,
318 Alpha,
320 Beta,
322 AlphaBeta,
324 DegreeAlpha,
326 DegreeBeta,
328 DegreeAlphaBeta,
330}
331
332impl GaussJacobiErrorReason {
333 #[inline]
335 pub fn was_bad_degree(&self) -> bool {
336 matches!(
337 self,
338 Self::Degree | Self::DegreeAlpha | Self::DegreeBeta | Self::DegreeAlphaBeta
339 )
340 }
341
342 #[inline]
344 pub fn was_bad_alpha(&self) -> bool {
345 matches!(
346 self,
347 Self::Alpha | Self::DegreeAlpha | Self::AlphaBeta | Self::DegreeAlphaBeta
348 )
349 }
350
351 #[inline]
353 pub fn was_bad_beta(&self) -> bool {
354 matches!(
355 self,
356 Self::Beta | Self::DegreeBeta | Self::AlphaBeta | Self::DegreeAlphaBeta
357 )
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use approx::assert_abs_diff_eq;
364
365 use super::*;
366
367 #[test]
368 fn sanity_check_chebyshev_delegation() {
369 const DEG: usize = 200;
370 let jrule = GaussJacobi::new(DEG, -0.5, -0.5).unwrap();
371 let crule1 = GaussChebyshevFirstKind::new(DEG).unwrap();
372
373 assert_eq!(jrule.as_node_weight_pairs(), crule1.as_node_weight_pairs());
374
375 let jrule = GaussJacobi::new(DEG, 0.5, 0.5).unwrap();
376 let crule2 = GaussChebyshevSecondKind::new(DEG).unwrap();
377
378 assert_eq!(jrule.as_node_weight_pairs(), crule2.as_node_weight_pairs())
379 }
380
381 #[test]
382 fn sanity_check_legendre_delegation() {
383 const DEG: usize = 200;
384 let jrule = GaussJacobi::new(DEG, 0.0, 0.0).unwrap();
385 let lrule = GaussLegendre::new(DEG).unwrap();
386
387 assert_eq!(
388 jrule.as_node_weight_pairs(),
389 lrule.into_iter().rev().collect::<Vec<_>>(),
390 );
391 }
392
393 #[test]
394 fn check_alpha_beta_bounds() {
395 assert!(GaussJacobi::new(10, -1.0, -1.0).is_err());
396 }
397
398 #[test]
399 fn golub_welsch_5_alpha_0_beta_0() {
400 let (x, w): (Vec<_>, Vec<_>) = GaussJacobi::new(5, 0.0, 0.0).unwrap().into_iter().unzip();
401 let x_should = [
402 -0.906_179_845_938_664,
403 -0.538_469_310_105_683_1,
404 0.0,
405 0.538_469_310_105_683_1,
406 0.906_179_845_938_664,
407 ];
408 let w_should = [
409 0.236_926_885_056_189_08,
410 0.478_628_670_499_366_47,
411 0.568_888_888_888_888_9,
412 0.478_628_670_499_366_47,
413 0.236_926_885_056_189_08,
414 ];
415 for (i, x_val) in x_should.iter().enumerate() {
416 assert_abs_diff_eq!(*x_val, x[i], epsilon = 1e-15);
417 }
418 for (i, w_val) in w_should.iter().enumerate() {
419 assert_abs_diff_eq!(*w_val, w[i], epsilon = 1e-15);
420 }
421 }
422
423 #[test]
424 fn golub_welsch_2_alpha_1_beta_0() {
425 let (x, w): (Vec<_>, Vec<_>) = GaussJacobi::new(2, 1.0, 0.0).unwrap().into_iter().unzip();
426 let x_should = [-0.689_897_948_556_635_7, 0.289_897_948_556_635_64];
427 let w_should = [1.272_165_526_975_908_7, 0.727_834_473_024_091_3];
428 for (i, x_val) in x_should.iter().enumerate() {
429 assert_abs_diff_eq!(*x_val, x[i], epsilon = 1e-14);
430 }
431 for (i, w_val) in w_should.iter().enumerate() {
432 assert_abs_diff_eq!(*w_val, w[i], epsilon = 1e-14);
433 }
434 }
435
436 #[test]
437 fn golub_welsch_5_alpha_1_beta_0() {
438 let (x, w): (Vec<_>, Vec<_>) = GaussJacobi::new(5, 1.0, 0.0).unwrap().into_iter().unzip();
439 let x_should = [
440 -0.920_380_285_897_062_6,
441 -0.603_973_164_252_783_7,
442 0.0,
443 0.390_928_546_707_272_2,
444 0.802_929_828_402_347_2,
445 ];
446 let w_should = [
447 0.387_126_360_906_606_74,
448 0.668_698_552_377_478_2,
449 0.585_547_948_338_679_2,
450 0.295_635_480_290_466_66,
451 0.062_991_658_086_769_1,
452 ];
453 for (i, x_val) in x_should.iter().enumerate() {
454 assert_abs_diff_eq!(*x_val, x[i], epsilon = 1e-14);
455 }
456 for (i, w_val) in w_should.iter().enumerate() {
457 assert_abs_diff_eq!(*w_val, w[i], epsilon = 1e-14);
458 }
459 }
460
461 #[test]
462 fn golub_welsch_5_alpha_0_beta_1() {
463 let (x, w): (Vec<_>, Vec<_>) = GaussJacobi::new(5, 0.0, 1.0).unwrap().into_iter().unzip();
464 let x_should = [
465 -0.802_929_828_402_347_2,
466 -0.390_928_546_707_272_2,
467 0.0,
468 0.603_973_164_252_783_7,
469 0.920_380_285_897_062_6,
470 ];
471 let w_should = [
472 0.062_991_658_086_769_1,
473 0.295_635_480_290_466_66,
474 0.585_547_948_338_679_2,
475 0.668_698_552_377_478_2,
476 0.387_126_360_906_606_74,
477 ];
478 for (i, x_val) in x_should.iter().enumerate() {
479 assert_abs_diff_eq!(*x_val, x[i], epsilon = 1e-14);
480 }
481 for (i, w_val) in w_should.iter().enumerate() {
482 assert_abs_diff_eq!(*w_val, w[i], epsilon = 1e-14);
483 }
484 }
485
486 #[test]
487 fn golub_welsch_50_alpha_42_beta_23() {
488 let (x, w): (Vec<_>, Vec<_>) = GaussJacobi::new(50, 42.0, 23.0)
489 .unwrap()
490 .into_iter()
491 .unzip();
492 let x_should = [
493 -0.936_528_233_152_541_2,
494 -0.914_340_864_546_088_5,
495 -0.892_159_904_972_709_7,
496 -0.869_216_909_221_225_6,
497 -0.845_277_228_769_225_6,
498 -0.820_252_766_348_056_8,
499 -0.794_113_540_498_529_6,
500 -0.766_857_786_572_463_5,
501 -0.738_499_459_607_423_4,
502 -0.709_062_235_514_446_8,
503 -0.678_576_327_905_629_3,
504 -0.647_076_661_181_635_3,
505 -0.614_601_751_027_635_6,
506 -0.581_192_977_458_508_4,
507 -0.546_894_086_695_451_9,
508 -0.511_750_831_826_105_3,
509 -0.475_810_700_347_493_84,
510 -0.439_122_697_460_417_9,
511 -0.401_737_165_777_708_5,
512 -0.363_705_629_046_518_04,
513 -0.325_080_651_686_135_1,
514 -0.285_915_708_544_232_9,
515 -0.246_265_060_906_733_86,
516 -0.206_183_635_819_408_85,
517 -0.165_726_906_401_709_62,
518 -0.124_950_771_176_147_79,
519 -0.083_911_430_566_871_42,
520 -0.042_665_258_670_068_65,
521 -0.001_268_668_170_195_549_6,
522 0.040_222_034_151_539_98,
523 0.081_750_804_545_872_01,
524 0.123_262_036_301_197_46,
525 0.164_700_756_351_269_24,
526 0.206_012_852_393_607_17,
527 0.247_145_341_670_134_97,
528 0.288_046_697_452_241,
529 0.328_667_256_796_052_5,
530 0.368_959_744_983_174_2,
531 0.408_879_971_241_114_4,
532 0.448_387_782_372_734_86,
533 0.487_448_416_419_391_24,
534 0.526_034_498_798_180_8,
535 0.564_129_114_046_126_2,
536 0.601_730_771_388_207_7,
537 0.638_861_919_860_897_4,
538 0.675_584_668_752_041_4,
539 0.712_032_766_455_434_9,
540 0.748_486_131_436_470_7,
541 0.785_585_184_777_517_6,
542 0.825_241_342_102_355_2,
543 ];
544 let w_should = [
545 7.48575322545471E-18,
546 4.368160045795394E-15,
547 5.475_092_226_093_74E-13,
548 2.883_802_894_000_164_4E-11,
549 8.375_974_400_943_034E-10,
550 1.551_169_281_097_026_6E-8,
551 2.002_752_126_655_06E-7,
552 1.914_052_885_645_138E-6,
553 1.412_973_977_680_798E-5,
554 8.315_281_580_948_582E-5,
555 3.996_349_769_672_429E-4,
556 0.001_598_442_290_393_378_4,
557 0.005_401_484_462_492_892,
558 0.015_609_515_951_961_325,
559 0.038_960_859_894_776_14,
560 0.084_675_992_815_357_84,
561 0.161_320_272_041_780_37,
562 0.270_895_707_022_142,
563 0.402_766_052_144_190_03,
564 0.532_134_840_644_357_2,
565 0.626_561_850_396_477_3,
566 0.658_939_504_140_677_5,
567 0.619_968_794_555_102,
568 0.522_392_634_872_676_4,
569 0.394_418_806_923_720_8,
570 0.266_845_588_852_137_27,
571 0.161_693_943_297_351_4,
572 0.087_665_230_931_323_02,
573 0.042_462_146_242_945_82,
574 0.018_336_610_588_859_478,
575 0.007_040_822_524_198_700_5,
576 0.002_395_953_515_750_436_4,
577 7.196_709_691_248_771E-4,
578 1.898_822_582_266_401E-4,
579 4.375_352_582_937_183E-5,
580 8.744_218_873_447_381E-6,
581 1.503_255_708_913_270_4E-6,
582 2.201_263_417_180_834_2E-7,
583 2.713_269_374_479_116_4E-8,
584 2.774_921_681_532_996E-9,
585 2.313_546_085_591_984_2E-10,
586 1.538_220_559_204_994_4E-11,
587 7.931_012_545_002_62E-13,
588 3.057_666_218_185_739E-14,
589 8.393_076_986_026_449E-16,
590 1.531_180_072_630_389E-17,
591 1.675_381_720_821_777_5E-19,
592 9.300_961_857_933_663E-22,
593 1.912_538_194_408_499_4E-24,
594 6.645_776_758_516_211E-28,
595 ];
596 for (i, x_val) in x_should.iter().enumerate() {
597 assert_abs_diff_eq!(*x_val, x[i], epsilon = 1e-10);
598 }
599 for (i, w_val) in w_should.iter().enumerate() {
600 assert_abs_diff_eq!(*w_val, w[i], epsilon = 1e-10);
601 }
602 }
603
604 #[test]
605 fn check_derives() {
606 let quad = GaussJacobi::new(10, 0.0, 1.0).unwrap();
607 let quad_clone = quad.clone();
608 assert_eq!(quad, quad_clone);
609 let other_quad = GaussJacobi::new(10, 1.0, 0.0).unwrap();
610 assert_ne!(quad, other_quad);
611 }
612
613 #[test]
614 fn check_jacobi_error() {
615 let jacobi_rule = GaussJacobi::new(3, -2.0, 1.0);
616 assert!(jacobi_rule
617 .as_ref()
618 .is_err_and(|x| x.reason() == GaussJacobiErrorReason::Alpha));
619 assert_eq!(
620 format!("{}", jacobi_rule.err().unwrap()),
621 "alpha must be finite and larger than -1.0"
622 );
623
624 let jacobi_rule = GaussJacobi::new(3, 1.0, -2.0);
625 assert!(jacobi_rule
626 .as_ref()
627 .is_err_and(|x| x.reason() == GaussJacobiErrorReason::Beta));
628 assert_eq!(
629 format!("{}", jacobi_rule.err().unwrap()),
630 "beta must be finite and larger than -1.0"
631 );
632
633 let jacobi_rule = GaussJacobi::new(3, -2.0, -2.0);
634 assert!(jacobi_rule
635 .as_ref()
636 .is_err_and(|x| x.reason() == GaussJacobiErrorReason::AlphaBeta));
637 assert_eq!(
638 format!("{}", jacobi_rule.err().unwrap()),
639 "alpha and beta must be finite and larger than -1.0"
640 );
641 assert_eq!(
642 GaussJacobi::new(3, -2.0, 1.0).map_err(|e| e.reason()),
643 Err(GaussJacobiErrorReason::Alpha)
644 );
645
646 assert_eq!(
647 GaussJacobi::new(3, -1.0, 1.0).map_err(|e| e.reason()),
648 Err(GaussJacobiErrorReason::Alpha)
649 );
650
651 assert_eq!(
652 GaussJacobi::new(3, 1.0, -2.0).map_err(|e| e.reason()),
653 Err(GaussJacobiErrorReason::Beta)
654 );
655
656 assert_eq!(
657 GaussJacobi::new(3, -1.0, -1.0).map_err(|e| e.reason()),
658 Err(GaussJacobiErrorReason::AlphaBeta)
659 );
660
661 let jacobi_rule = GaussJacobi::new(0, 0.5, 0.5);
662 assert!(jacobi_rule
663 .as_ref()
664 .is_err_and(|x| x.reason() == GaussJacobiErrorReason::Degree));
665 assert_eq!(
666 format!("{}", jacobi_rule.err().unwrap()),
667 "degree must be at least 2"
668 );
669 assert_eq!(
670 GaussJacobi::new(1, 0.5, 0.5).map_err(|e| e.reason()),
671 Err(GaussJacobiErrorReason::Degree)
672 );
673
674 let jacobi_rule = GaussJacobi::new(0, -1.0, 0.5);
675 assert!(jacobi_rule
676 .as_ref()
677 .is_err_and(|x| x.reason() == GaussJacobiErrorReason::DegreeAlpha));
678 assert_eq!(
679 format!("{}", jacobi_rule.err().unwrap()),
680 "degree must be at least 2 and alpha must be finite and larger than -1.0"
681 );
682 assert_eq!(
683 GaussJacobi::new(0, -2.0, 0.5).map_err(|e| e.reason()),
684 Err(GaussJacobiErrorReason::DegreeAlpha)
685 );
686 assert_eq!(
687 GaussJacobi::new(1, -1.0, 0.5).map_err(|e| e.reason()),
688 Err(GaussJacobiErrorReason::DegreeAlpha)
689 );
690
691 let jacobi_rule = GaussJacobi::new(0, 0.5, -1.0);
692 assert!(jacobi_rule
693 .as_ref()
694 .is_err_and(|x| x.reason() == GaussJacobiErrorReason::DegreeBeta));
695 assert_eq!(
696 format!("{}", jacobi_rule.err().unwrap()),
697 "degree must be at least 2 and beta must be finite and larger than -1.0"
698 );
699 assert_eq!(
700 GaussJacobi::new(3, -2.0, -2.0).map_err(|e| e.reason()),
701 Err(GaussJacobiErrorReason::AlphaBeta)
702 );
703 assert_eq!(
704 GaussJacobi::new(3, -1.0, -2.0).map_err(|e| e.reason()),
705 Err(GaussJacobiErrorReason::AlphaBeta)
706 );
707
708 let jacobi_rule = GaussJacobi::new(0, -1.0, -1.0);
709 assert!(jacobi_rule
710 .as_ref()
711 .is_err_and(|x| x.reason() == GaussJacobiErrorReason::DegreeAlphaBeta));
712 assert_eq!(
713 format!("{}", jacobi_rule.err().unwrap()),
714 "degree must be at least 2, alpha and beta must be finite and larger than -1.0"
715 );
716 assert_eq!(
717 GaussJacobi::new(3, -2.0, -1.0).map_err(|e| e.reason()),
718 Err(GaussJacobiErrorReason::AlphaBeta)
719 );
720 assert_eq!(
721 GaussJacobi::new(3, -1.0, -1.0).map_err(|e| e.reason()),
722 Err(GaussJacobiErrorReason::AlphaBeta)
723 );
724
725 assert_eq!(
726 GaussJacobi::new(0, 0.5, 0.5).map_err(|e| e.reason()),
727 Err(GaussJacobiErrorReason::Degree)
728 );
729 assert_eq!(
730 GaussJacobi::new(1, 0.5, 0.5).map_err(|e| e.reason()),
731 Err(GaussJacobiErrorReason::Degree)
732 );
733
734 assert_eq!(
735 GaussJacobi::new(0, -1.0, 0.5).map_err(|e| e.reason()),
736 Err(GaussJacobiErrorReason::DegreeAlpha)
737 );
738 assert_eq!(
739 GaussJacobi::new(0, -2.0, 0.5).map_err(|e| e.reason()),
740 Err(GaussJacobiErrorReason::DegreeAlpha)
741 );
742 assert_eq!(
743 GaussJacobi::new(1, -1.0, 0.5).map_err(|e| e.reason()),
744 Err(GaussJacobiErrorReason::DegreeAlpha)
745 );
746 assert_eq!(
747 GaussJacobi::new(1, -2.0, 0.5).map_err(|e| e.reason()),
748 Err(GaussJacobiErrorReason::DegreeAlpha)
749 );
750
751 assert_eq!(
752 GaussJacobi::new(0, 0.5, -1.0).map_err(|e| e.reason()),
753 Err(GaussJacobiErrorReason::DegreeBeta)
754 );
755 assert_eq!(
756 GaussJacobi::new(0, 0.5, -2.0).map_err(|e| e.reason()),
757 Err(GaussJacobiErrorReason::DegreeBeta)
758 );
759 assert_eq!(
760 GaussJacobi::new(1, 0.5, -1.0).map_err(|e| e.reason()),
761 Err(GaussJacobiErrorReason::DegreeBeta)
762 );
763 assert_eq!(
764 GaussJacobi::new(1, 0.5, -2.0).map_err(|e| e.reason()),
765 Err(GaussJacobiErrorReason::DegreeBeta)
766 );
767
768 assert_eq!(
769 GaussJacobi::new(0, -1.0, -1.0).map_err(|e| e.reason()),
770 Err(GaussJacobiErrorReason::DegreeAlphaBeta)
771 );
772 assert_eq!(
773 GaussJacobi::new(0, -2.0, -2.0).map_err(|e| e.reason()),
774 Err(GaussJacobiErrorReason::DegreeAlphaBeta)
775 );
776 assert_eq!(
777 GaussJacobi::new(1, -1.0, -1.0).map_err(|e| e.reason()),
778 Err(GaussJacobiErrorReason::DegreeAlphaBeta)
779 );
780 assert_eq!(
781 GaussJacobi::new(1, -2.0, -2.0).map_err(|e| e.reason()),
782 Err(GaussJacobiErrorReason::DegreeAlphaBeta)
783 );
784 assert_eq!(
785 GaussJacobi::new(0, -1.0, -2.0).map_err(|e| e.reason()),
786 Err(GaussJacobiErrorReason::DegreeAlphaBeta)
787 );
788 assert_eq!(
789 GaussJacobi::new(0, -2.0, -1.0).map_err(|e| e.reason()),
790 Err(GaussJacobiErrorReason::DegreeAlphaBeta)
791 );
792 assert_eq!(
793 GaussJacobi::new(1, -1.0, -2.0).map_err(|e| e.reason()),
794 Err(GaussJacobiErrorReason::DegreeAlphaBeta)
795 );
796 assert_eq!(
797 GaussJacobi::new(1, -2.0, -1.0).map_err(|e| e.reason()),
798 Err(GaussJacobiErrorReason::DegreeAlphaBeta)
799 );
800 }
801
802 #[test]
803 fn check_iterators() {
804 let rule = GaussJacobi::new(2, -0.25, -0.5).unwrap();
805 let ans = 1.3298477657906902;
807
808 assert_abs_diff_eq!(
809 ans,
810 rule.iter().fold(0.0, |tot, (n, w)| tot + n * n * w),
811 epsilon = 1e-14
812 );
813
814 assert_abs_diff_eq!(
815 ans,
816 rule.nodes()
817 .zip(rule.weights())
818 .fold(0.0, |tot, (n, w)| tot + n * n * w),
819 epsilon = 1e-14
820 );
821
822 assert_abs_diff_eq!(
823 ans,
824 rule.into_iter().fold(0.0, |tot, (n, w)| tot + n * n * w),
825 epsilon = 1e-14
826 );
827 }
828
829 #[test]
830 fn check_some_integrals() {
831 let rule = GaussJacobi::new(10, -0.5, -0.25).unwrap();
832
833 assert_abs_diff_eq!(
834 rule.integrate(-1.0, 1.0, |x| x * x),
835 1.3298477657906902,
836 epsilon = 1e-14
837 );
838
839 assert_abs_diff_eq!(
840 rule.integrate(-1.0, 1.0, |x| x.cos()),
841 2.2239,
842 epsilon = 1e-5
843 );
844 }
845
846 #[cfg(feature = "rayon")]
847 #[test]
848 fn par_check_some_integrals() {
849 let rule = GaussJacobi::new(10, -0.5, -0.25).unwrap();
850
851 assert_abs_diff_eq!(
852 rule.par_integrate(-1.0, 1.0, |x| x * x),
853 1.3298477657906902,
854 epsilon = 1e-14
855 );
856
857 assert_abs_diff_eq!(
858 rule.par_integrate(-1.0, 1.0, |x| x.cos()),
859 2.2239,
860 epsilon = 1e-5
861 );
862 }
863}