Skip to main content

fixed_bigint/heapless/
iter.rs

1//! `core::iter::Sum` / `Product` for `HeaplessBigInt`.
2//!
3//! A non-empty iterator settles at `max(operand len)`; an **empty** one yields
4//! the minimal-width identity. `Sum` folds from `zero()` (already len 0, so it
5//! never widens the result). `Product` can't fold from `one()` — the
6//! multiplicative identity is len 1, which would inject that width into a
7//! product of narrower operands — so it seeds from the first element instead,
8//! falling back to `one()` only when empty. To fix the accumulator width
9//! regardless of the operands, pin the seed with `WithPrecision` and fold
10//! manually rather than using these.
11
12use super::HeaplessBigInt;
13use crate::MachineWord;
14use const_num_traits::{CarryingMul, One, Personality, Zero};
15
16impl<T, const CAP: usize, P: Personality> core::iter::Sum for HeaplessBigInt<T, CAP, P>
17where
18    T: MachineWord,
19{
20    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
21        iter.fold(<Self as Zero>::zero(), |acc, x| acc + x)
22    }
23}
24
25impl<'a, T, const CAP: usize, P: Personality> core::iter::Sum<&'a Self>
26    for HeaplessBigInt<T, CAP, P>
27where
28    T: MachineWord,
29{
30    fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
31        iter.fold(<Self as Zero>::zero(), |acc, x| acc + *x)
32    }
33}
34
35impl<T, const CAP: usize, P: Personality> core::iter::Product for HeaplessBigInt<T, CAP, P>
36where
37    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
38{
39    fn product<I: Iterator<Item = Self>>(mut iter: I) -> Self {
40        match iter.next() {
41            None => <Self as One>::one(),
42            Some(first) => iter.fold(first, |acc, x| acc * x),
43        }
44    }
45}
46
47impl<'a, T, const CAP: usize, P: Personality> core::iter::Product<&'a Self>
48    for HeaplessBigInt<T, CAP, P>
49where
50    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
51{
52    fn product<I: Iterator<Item = &'a Self>>(mut iter: I) -> Self {
53        match iter.next() {
54            None => <Self as One>::one(),
55            Some(first) => iter.fold(*first, |acc, x| acc * *x),
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::HeaplessBigInt;
63
64    type H = HeaplessBigInt<u8, 8>;
65
66    #[test]
67    fn sum_product() {
68        let vals = [H::from(1u8), H::from(2u8), H::from(3u8), H::from(4u8)];
69        assert_eq!(vals.iter().copied().sum::<H>(), H::from(10u8));
70        assert_eq!(vals.iter().sum::<H>(), H::from(10u8));
71        assert_eq!(vals.iter().copied().product::<H>(), H::from(24u8));
72        assert_eq!(vals.iter().product::<H>(), H::from(24u8));
73    }
74
75    #[test]
76    fn empty_iter_is_identity() {
77        let empty: [H; 0] = [];
78        assert_eq!(empty.iter().copied().sum::<H>(), H::from(0u8));
79        assert_eq!(empty.iter().copied().product::<H>(), H::from(1u8));
80    }
81
82    // A non-empty product carries max(operand len), not the len-1 identity:
83    // seeding from the first element avoids injecting one()'s width.
84    #[test]
85    fn product_preserves_operand_width_not_identity() {
86        // Single len-0 (empty-shape zero) operand: result stays len 0.
87        let zeros = [H::new_zero_with_len(0)];
88        let p = zeros.iter().copied().product::<H>();
89        assert!(<H as const_num_traits::Zero>::is_zero(&p));
90        assert_eq!(p.len(), 0);
91
92        // Non-empty product settles at the widest operand.
93        let mixed = [H::from(2u8), H::from(3u8).widened(8)];
94        assert_eq!(mixed.iter().copied().product::<H>().len(), 8);
95    }
96}