1#![deny(rustdoc::broken_intra_doc_links)]
2
3use crate::functions::Abs;
4use getset::Getters;
5use num::{Complex, Zero};
6use std::ops::{Add, AddAssign, Sub};
7
8pub(crate) fn neumaier_sum_and_compensation_real<RealType>(
10 value: RealType,
11 sum: &mut RealType,
12 compensation: &mut RealType,
13) where
14 RealType: Clone
15 + Add<RealType, Output = RealType>
16 + for<'a> Sub<&'a RealType, Output = RealType>
17 + AddAssign
18 + for<'a> AddAssign<&'a RealType>
19 + Abs<Output = RealType>
20 + PartialOrd,
21{
22 let sum_before_compensation = sum.clone();
23 *sum += &value;
24
25 *compensation += if sum_before_compensation.clone().abs() >= value.clone().abs() {
27 (sum_before_compensation - &*sum) + value
29 } else {
30 (value - &*sum) + sum_before_compensation
32 };
33}
34
35pub trait NeumaierAddable: Sized {
36 fn neumaier_compensated_sum(value: Self, sum: &mut Self, compensation: &mut Self);
37}
38
39impl NeumaierAddable for f64 {
40 fn neumaier_compensated_sum(value: Self, sum: &mut Self, compensation: &mut Self) {
41 neumaier_sum_and_compensation_real(value, sum, compensation)
42 }
43}
44
45impl NeumaierAddable for Complex<f64> {
46 fn neumaier_compensated_sum(value: Self, sum: &mut Self, compensation: &mut Self) {
47 neumaier_sum_and_compensation_real(value.re, &mut sum.re, &mut compensation.re);
48 neumaier_sum_and_compensation_real(value.im, &mut sum.im, &mut compensation.im);
49 }
50}
51
52#[cfg(feature = "rug")]
53mod rug_impls {
54 use super::*;
55
56 fn neumaier_sum_and_compensation_rug_float(
57 value: rug::Float,
58 sum: &mut rug::Float,
59 compensation: &mut rug::Float,
60 ) {
61 let sum_before_compensation = sum.clone();
62 *sum += &value;
63
64 *compensation += if sum_before_compensation.clone().abs() >= value.clone().abs() {
66 (sum_before_compensation - &*sum) + value
68 } else {
69 (value - &*sum) + sum_before_compensation
71 };
72 }
73
74 impl NeumaierAddable for rug::Float {
75 fn neumaier_compensated_sum(value: Self, sum: &mut Self, compensation: &mut Self) {
76 neumaier_sum_and_compensation_rug_float(value, sum, compensation)
77 }
78 }
79
80 impl NeumaierAddable for rug::Complex {
81 fn neumaier_compensated_sum(value: Self, sum: &mut Self, compensation: &mut Self) {
82 let (value_real, value_imag) = value.into_real_imag();
83
84 neumaier_sum_and_compensation_rug_float(
85 value_real,
86 sum.mut_real(),
87 compensation.mut_real(),
88 );
89
90 neumaier_sum_and_compensation_rug_float(
91 value_imag,
92 sum.mut_imag(),
93 compensation.mut_imag(),
94 );
95 }
96 }
97}
98
99#[derive(Debug, Clone, Getters)]
135pub struct NeumaierSum<ScalarType> {
136 #[getset(get = "pub")]
138 sum_before_compensation: ScalarType,
139
140 #[getset(get = "pub")]
143 compensation: ScalarType,
144}
145
146impl<ScalarType> NeumaierSum<ScalarType>
147where
148 ScalarType: Clone + Zero + for<'a> Add<&'a ScalarType, Output = ScalarType> + NeumaierAddable,
149{
150 pub fn new(value: ScalarType) -> Self {
153 Self {
154 sum_before_compensation: value,
155 compensation: ScalarType::zero(),
156 }
157 }
158
159 pub fn zero() -> Self {
161 Self::new(ScalarType::zero())
162 }
163
164 pub fn sum(&self) -> ScalarType {
166 self.sum_before_compensation.clone() + &self.compensation
167 }
168
169 pub fn reset(&mut self) {
171 self.sum_before_compensation = ScalarType::zero();
172 self.compensation = ScalarType::zero();
173 }
174
175 pub fn add(&mut self, value: ScalarType) {
177 <ScalarType as NeumaierAddable>::neumaier_compensated_sum(
178 value,
179 &mut self.sum_before_compensation,
180 &mut self.compensation,
181 );
182 }
183
184 pub fn new_sequential<I>(values: I) -> Self
198 where
199 I: IntoIterator<Item = ScalarType>,
200 {
201 let mut neumaier = Self::zero();
202 values.into_iter().for_each(|value| {
203 neumaier.add(value);
204 });
205 neumaier
206 }
207}
208
209#[cfg(test)]
213mod tests {
214 use super::*;
215
216 mod native64 {
217 use super::*;
218
219 mod real {
220 use super::*;
221
222 #[test]
223 fn new() {
224 let neumaier = NeumaierSum::new(1.0);
225 assert_eq!(neumaier.sum_before_compensation, 1.0);
226 assert_eq!(neumaier.compensation, 0.0);
227 }
228
229 #[test]
230 fn zero() {
231 let neumaier = NeumaierSum::<f64>::zero();
232 assert_eq!(neumaier.sum_before_compensation, 0.0);
233 assert_eq!(neumaier.compensation, 0.0);
234 }
235
236 #[test]
237 fn add() {
238 let mut neumaier = NeumaierSum::<f64>::zero();
239 neumaier.add(1.0);
240 neumaier.add(1e-16);
241 neumaier.add(-1.0);
242 assert_eq!(neumaier.sum_before_compensation, 0.0);
243 assert_eq!(neumaier.compensation, 1e-16);
244 }
245
246 #[test]
247 fn sum() {
248 let mut neumaier = NeumaierSum::<f64>::zero();
249 neumaier.add(1.0);
250 neumaier.add(1e-16);
251 neumaier.add(-1.0);
252 assert_eq!(neumaier.sum_before_compensation, 0.0);
253 assert_eq!(neumaier.compensation, 1e-16);
254 assert_eq!(neumaier.sum(), 1e-16);
255 println!("compensated sum = {}", neumaier.sum());
256 }
257
258 #[test]
259 fn reset() {
260 let mut neumaier = NeumaierSum::<f64>::zero();
261 neumaier.add(1.0);
262 neumaier.add(1e-16);
263 assert_eq!(neumaier.sum_before_compensation, 1.0);
264 assert_eq!(neumaier.compensation, 1e-16);
265
266 neumaier.reset();
267 assert_eq!(neumaier.sum_before_compensation, 0.0);
268 assert_eq!(neumaier.compensation, 0.0);
269 }
270
271 #[test]
272 fn sum_big_values() {
273 let values = vec![1.0, 1e100, 1.0, -1e100];
274 let sum = values.iter().sum::<f64>();
275 assert_eq!(sum, 0.0);
276
277 let neumaier = NeumaierSum::<f64>::new_sequential(values);
278 assert_eq!(neumaier.sum(), 2.0);
279 println!("compensated sum = {}", neumaier.sum());
280 }
281
282 #[test]
283 fn sum_small_values() {
284 let values = [1.0, 1e-100, -1.0];
285 let sum = values.iter().sum::<f64>();
286 assert_eq!(sum, 0.0);
287
288 let neumaier = NeumaierSum::<f64>::new_sequential(values);
289 assert_eq!(neumaier.sum(), 1e-100);
290 println!("compensated sum = {}", neumaier.sum());
291 }
292 }
293
294 mod complex {
295 use super::*;
296 use num::Complex;
297
298 #[test]
299 fn new() {
300 let neumaier = NeumaierSum::<Complex<f64>>::new(Complex::new(1.0, 2.0));
301 assert_eq!(neumaier.sum_before_compensation, Complex::new(1.0, 2.0));
302 assert_eq!(neumaier.compensation, Complex::new(0.0, 0.0));
303 }
304
305 #[test]
306 fn zero() {
307 let neumaier = NeumaierSum::<Complex<f64>>::zero();
308
309 let zero = Complex::new(0.0, 0.0);
310 assert_eq!(&neumaier.sum_before_compensation, &zero);
311 assert_eq!(&neumaier.compensation, &zero);
312 }
313
314 #[test]
315 fn add() {
316 let mut neumaier = NeumaierSum::<Complex<f64>>::zero();
317
318 let zero = Complex::new(0.0, 0.0);
319 let v = Complex::new(1e-16, 2e-16);
320
321 neumaier.add(Complex::new(1.0, 2.0));
322 neumaier.add(v);
323 neumaier.add(Complex::new(-1.0, -2.0));
324
325 assert_eq!(neumaier.sum_before_compensation, zero);
326 assert_eq!(neumaier.compensation, v);
327 }
328
329 #[test]
330 fn sum() {
331 let mut neumaier = NeumaierSum::<Complex<f64>>::zero();
332
333 let zero = Complex::new(0.0, 0.0);
334 let v = Complex::new(1e-16, 2e-16);
335
336 neumaier.add(Complex::new(1.0, 2.0));
337 neumaier.add(v);
338 neumaier.add(Complex::new(-1.0, -2.0));
339 assert_eq!(neumaier.sum_before_compensation, zero);
340 assert_eq!(neumaier.compensation, v);
341 assert_eq!(neumaier.sum(), v);
342 println!("compensated sum = {}", neumaier.sum());
343 }
344
345 #[test]
346 fn reset() {
347 let mut neumaier = NeumaierSum::<Complex<f64>>::zero();
348
349 let zero = Complex::new(0.0, 0.0);
350 let a = Complex::new(1.0, 2.0);
351 let v = Complex::new(1e-16, 2e-16);
352
353 neumaier.add(a);
354 neumaier.add(v);
355 assert_eq!(neumaier.sum_before_compensation, a);
356 assert_eq!(neumaier.compensation, v);
357
358 neumaier.reset();
359 assert_eq!(neumaier.sum_before_compensation, zero);
360 assert_eq!(neumaier.compensation, zero);
361 }
362
363 #[test]
364 fn sum_big_values() {
365 let values = vec![
366 Complex::new(1.0, 2.0),
367 Complex::new(1e100, 2e100),
368 Complex::new(1.0, 2.0),
369 Complex::new(-1e100, -2e100),
370 ];
371 let sum = values.iter().sum::<Complex<f64>>();
372 assert_eq!(sum, Complex::new(0.0, 0.0));
373
374 let neumaier = NeumaierSum::<Complex<f64>>::new_sequential(values);
375 assert_eq!(neumaier.sum(), Complex::new(2.0, 4.0));
376 println!("compensated sum = {}", neumaier.sum());
377 }
378
379 #[test]
380 fn sum_small_values() {
381 let v = Complex::new(1e-100, 2e-100);
382
383 let values = [Complex::new(1.0, 2.0), v, Complex::new(-1.0, -2.0)];
384 let sum = values.iter().sum::<Complex<f64>>();
385 assert_eq!(sum, Complex::new(0.0, 0.0));
386
387 let neumaier = NeumaierSum::<Complex<f64>>::new_sequential(values);
388 assert_eq!(neumaier.sum(), v);
389 println!("compensated sum = {}", neumaier.sum());
390 }
391 }
392 }
393
394 #[cfg(feature = "rug")]
395 mod rug100 {
396 use super::*;
397 use crate::{ComplexRugStrictFinite, RealRugStrictFinite};
398 use num::One;
399 use try_create::New;
400
401 const PRECISION: u32 = 100;
402
403 mod real {
404 use super::*;
405 use crate::RealScalar;
406
407 #[test]
408 fn new() {
409 let neumaier = NeumaierSum::new(RealRugStrictFinite::<PRECISION>::one());
410 assert_eq!(neumaier.sum_before_compensation, 1.0);
411 assert_eq!(neumaier.compensation, 0.0);
412 }
413
414 #[test]
415 fn zero() {
416 let neumaier = NeumaierSum::<RealRugStrictFinite<PRECISION>>::zero();
417 assert_eq!(neumaier.sum_before_compensation, 0.0);
418 assert_eq!(neumaier.compensation, 0.0);
419 }
420
421 #[test]
422 fn add() {
423 let mut neumaier = NeumaierSum::<RealRugStrictFinite<PRECISION>>::zero();
424
425 let v = RealRugStrictFinite::<PRECISION>::new(rug::Float::with_val(
426 PRECISION,
427 rug::Float::parse("1e-100").unwrap(),
428 ));
429
430 neumaier.add(RealRugStrictFinite::<PRECISION>::try_from_f64(1.0).unwrap());
431 neumaier.add(v.clone());
432 neumaier.add(RealRugStrictFinite::<PRECISION>::try_from_f64(-1.0).unwrap());
433
434 assert_eq!(
435 neumaier.sum_before_compensation,
436 RealRugStrictFinite::<PRECISION>::try_from_f64(0.0).unwrap()
437 );
438 assert_eq!(&neumaier.compensation, &v);
439 }
440
441 #[test]
442 fn sum() {
443 let mut neumaier = NeumaierSum::<RealRugStrictFinite<PRECISION>>::zero();
444
445 let v = RealRugStrictFinite::<PRECISION>::new(rug::Float::with_val(
446 PRECISION,
447 rug::Float::parse("1e-100").unwrap(),
448 ));
449
450 neumaier.add(RealRugStrictFinite::<PRECISION>::try_from_f64(1.0).unwrap());
451 neumaier.add(v.clone());
452 neumaier.add(RealRugStrictFinite::<PRECISION>::try_from_f64(-1.0).unwrap());
453
454 assert_eq!(neumaier.sum_before_compensation, 0.0);
455 assert_eq!(&neumaier.compensation, &v);
456 assert_eq!(neumaier.sum(), v);
457 println!("compensated sum = {}", neumaier.sum());
458 }
459
460 #[test]
461 fn reset() {
462 let mut neumaier = NeumaierSum::<RealRugStrictFinite<PRECISION>>::zero();
463
464 let zero = RealRugStrictFinite::<PRECISION>::zero();
465 let one = RealRugStrictFinite::<PRECISION>::one();
466 let v = RealRugStrictFinite::<PRECISION>::new(rug::Float::with_val(
467 PRECISION,
468 rug::Float::parse("1e-100").unwrap(),
469 ));
470
471 neumaier.add(one.clone());
472 neumaier.add(v.clone());
473
474 assert_eq!(&neumaier.sum_before_compensation, &one);
475 assert_eq!(&neumaier.compensation, &v);
476
477 neumaier.reset();
478 assert_eq!(&neumaier.sum_before_compensation, &zero);
479 assert_eq!(&neumaier.compensation, &zero);
480 }
481
482 #[test]
483 fn sum_big_values() {
484 let values = ["1.0", "1e100", "1.0", "-1e100"]
485 .iter()
486 .map(|v| {
487 RealRugStrictFinite::<PRECISION>::new(rug::Float::with_val(
488 PRECISION,
489 rug::Float::parse(v).unwrap(),
490 ))
491 })
492 .collect::<Vec<_>>();
493
494 let sum = values
495 .iter()
496 .fold(RealRugStrictFinite::<PRECISION>::zero(), |acc, x| acc + x);
497 assert_eq!(sum, 0.0);
498
499 let neumaier =
500 NeumaierSum::<RealRugStrictFinite<PRECISION>>::new_sequential(values);
501 assert_eq!(
502 neumaier.sum(),
503 RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap()
504 );
505 println!("compensated sum = {}", neumaier.sum());
506 }
507
508 #[test]
509 fn sum_small_values() {
510 let values = ["1.0", "1e-100", "-1.0"]
511 .iter()
512 .map(|v| {
513 RealRugStrictFinite::<PRECISION>::new(rug::Float::with_val(
514 PRECISION,
515 rug::Float::parse(v).unwrap(),
516 ))
517 })
518 .collect::<Vec<_>>();
519
520 let sum = values
521 .iter()
522 .fold(RealRugStrictFinite::<PRECISION>::zero(), |acc, x| acc + x);
523 assert_eq!(sum, RealRugStrictFinite::<PRECISION>::zero());
524
525 let neumaier =
526 NeumaierSum::<RealRugStrictFinite<PRECISION>>::new_sequential(values);
527 assert_eq!(
528 neumaier.sum(),
529 RealRugStrictFinite::<PRECISION>::new(rug::Float::with_val(
530 PRECISION,
531 rug::Float::parse("1e-100").unwrap(),
532 ))
533 );
534 println!("compensated sum = {}", neumaier.sum());
535 }
536 }
537
538 mod complex {
539 use super::*;
540
541 #[test]
542 fn new() {
543 let one = ComplexRugStrictFinite::<PRECISION>::one();
544 let neumaier = NeumaierSum::<ComplexRugStrictFinite<PRECISION>>::new(one.clone());
545 assert_eq!(neumaier.sum_before_compensation, one);
546 assert_eq!(
547 neumaier.compensation,
548 ComplexRugStrictFinite::<PRECISION>::zero()
549 );
550 }
551
552 #[test]
553 fn zero() {
554 let neumaier = NeumaierSum::<ComplexRugStrictFinite<PRECISION>>::zero();
555 assert_eq!(
556 neumaier.sum_before_compensation,
557 ComplexRugStrictFinite::<PRECISION>::zero()
558 );
559 assert_eq!(
560 neumaier.compensation,
561 ComplexRugStrictFinite::<PRECISION>::zero()
562 );
563 }
564
565 #[test]
566 fn add() {
567 let mut neumaier = NeumaierSum::<ComplexRugStrictFinite<PRECISION>>::zero();
568
569 let v = ComplexRugStrictFinite::<PRECISION>::new(rug::Complex::with_val(
570 PRECISION,
571 rug::Complex::parse("(1e-100,2e-100)").unwrap(),
572 ));
573
574 neumaier.add(
575 ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1.0, 2.0)).unwrap(),
576 );
577 neumaier.add(v.clone());
578 neumaier.add(
579 ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-1.0, -2.0))
580 .unwrap(),
581 );
582
583 assert_eq!(
584 neumaier.sum_before_compensation,
585 ComplexRugStrictFinite::<PRECISION>::zero()
586 );
587 assert_eq!(&neumaier.compensation, &v);
588 }
589
590 #[test]
591 fn sum() {
592 let mut neumaier = NeumaierSum::<ComplexRugStrictFinite<PRECISION>>::zero();
593
594 let v = ComplexRugStrictFinite::<PRECISION>::new(rug::Complex::with_val(
595 PRECISION,
596 rug::Complex::parse("(1e-100,2e-100)").unwrap(),
597 ));
598
599 neumaier.add(
600 ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1.0, 2.0)).unwrap(),
601 );
602 neumaier.add(v.clone());
603 neumaier.add(
604 ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-1.0, -2.0))
605 .unwrap(),
606 );
607
608 assert_eq!(
609 neumaier.sum_before_compensation,
610 ComplexRugStrictFinite::<PRECISION>::zero()
611 );
612 assert_eq!(&neumaier.compensation, &v);
613 assert_eq!(neumaier.sum(), v);
614 println!("compensated sum = {}", neumaier.sum());
615 }
616
617 #[test]
618 fn reset() {
619 let mut neumaier = NeumaierSum::<ComplexRugStrictFinite<PRECISION>>::zero();
620
621 let zero = ComplexRugStrictFinite::<PRECISION>::zero();
622 let one =
623 ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1.0, 2.0)).unwrap();
624 let v = ComplexRugStrictFinite::<PRECISION>::new(rug::Complex::with_val(
625 PRECISION,
626 rug::Complex::parse("(1e-100,2e-100)").unwrap(),
627 ));
628
629 neumaier.add(one.clone());
630 neumaier.add(v.clone());
631
632 assert_eq!(&neumaier.sum_before_compensation, &one);
633 assert_eq!(&neumaier.compensation, &v);
634
635 neumaier.reset();
636 assert_eq!(&neumaier.sum_before_compensation, &zero);
637 assert_eq!(&neumaier.compensation, &zero);
638 }
639
640 #[test]
641 fn sum_big_values() {
642 let values = ["(1.0,2.0)", "(1e100,2e100)", "(1.0,2.0)", "(-1e100,-2e100)"]
643 .iter()
644 .map(|v| {
645 ComplexRugStrictFinite::<PRECISION>::new(rug::Complex::with_val(
646 PRECISION,
647 rug::Complex::parse(v).unwrap(),
648 ))
649 })
650 .collect::<Vec<_>>();
651
652 let zero = ComplexRugStrictFinite::<PRECISION>::zero();
653 let sum = values.iter().fold(zero.clone(), |acc, x| acc + x);
654 assert_eq!(sum, zero);
655
656 let neumaier =
657 NeumaierSum::<ComplexRugStrictFinite<PRECISION>>::new_sequential(values);
658 assert_eq!(
659 neumaier.sum(),
660 ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(2.0, 4.0)).unwrap()
661 );
662 println!("compensated sum = {}", neumaier.sum());
663 }
664
665 #[test]
666 fn sum_small_values() {
667 let values = ["(1.0,2.0)", "(1e-100,2e-100)", "(-1.0,-2.0)"]
668 .iter()
669 .map(|v| {
670 ComplexRugStrictFinite::<PRECISION>::new(rug::Complex::with_val(
671 PRECISION,
672 rug::Complex::parse(v).unwrap(),
673 ))
674 })
675 .collect::<Vec<_>>();
676
677 let zero = ComplexRugStrictFinite::<PRECISION>::zero();
678 let sum = values.iter().fold(zero.clone(), |acc, x| acc + x);
679 assert_eq!(sum, zero);
680
681 let neumaier =
682 NeumaierSum::<ComplexRugStrictFinite<PRECISION>>::new_sequential(values);
683 assert_eq!(
684 neumaier.sum(),
685 ComplexRugStrictFinite::<PRECISION>::new(rug::Complex::with_val(
686 PRECISION,
687 rug::Complex::parse("(1e-100,2e-100)").unwrap(),
688 ))
689 );
690 println!("compensated sum = {}", neumaier.sum());
691 }
692 }
693 }
694}
695