Skip to main content

rustymath_core/
reduce.rs

1//! Reductions and scans over a contiguous slice: sums, products, maximums and minimums, and their running versions. Each compiles inside rustymath once for each item type. A float sum or product may combine the items in any order, which lets its loop vectorize, so its last bits can differ from a sum taken item by item. Beside NaN, a float maximum or minimum gives NaN or the greatest or least of the items that aren't NaN, and never any other number. Which of the two it gives can depend on the target and the loop. An empty slice gives -∞ or ∞. `f16` items compute in `f32`. Their sum or product is an `f32` total. An integer sum or product computes at `i64`, or at `u64` for `u64` items. Where it doesn't fit, it wraps, and reports that it doesn't fit, as the running versions do.
2use crate::error::{Error, Kind};
3pub use crate::slice::running;
4use crate::{
5    Arg, Float, Native, agreement::{Mapping, Read, Walked, expanded_items}, arith::Overflowing, buffer::{Buffer, Cast, DType, Element, Source, View, Width},
6    float::sealed::{Core, Folds}, gather::Steps, layout::Lanes, linalg::Scalar, parallel, slice::{CHUNK, appended, written}
7};
8use half::f16;
9use num_complex::Complex;
10use std::ops::Range;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum Direction { Left, Right }
14
15#[derive(Clone, Copy)]
16pub enum Seed<'a> { Identity, Initial(crate::agreement::Read<'a>), Tail(crate::agreement::Read<'a>) }
17impl<'a> Seed<'a> {
18    /// The seed's items, or `None` for the identity.
19    pub fn read(self) -> Option<crate::agreement::Read<'a>> { match self { Self::Initial(read) | Self::Tail(read) => Some(read), Self::Identity => None } }
20}
21
22/// A compiled fold's total over `items` items, as its 8-byte slot holds it: a float as an f64's bits, an integer extended to 64 bits, and a Boolean as 0 or 1.
23#[derive(Clone, Copy, Debug)]
24pub struct Total { pub items: usize, pub bits: u64 }
25impl Total {
26    pub fn value<T: crate::buffer::Cast>(self) -> Result<T, Error> {
27        use crate::buffer::{Floats, Ints};
28        match T::DTYPE {
29            DType::Boolean => T::cast(View::Booleans(&[self.bits != 0]), 0),
30            DType::Integer(width) => {
31                let value = match width {
32                    Width::U8 => self.bits as u8 as i64, Width::I16 => self.bits as i16 as i64, Width::I32 => self.bits as i32 as i64,
33                    Width::I64 => self.bits as i64
34                };
35                T::cast(View::Integers(Ints::I64(&[value])), 0)
36            }
37            DType::Float(_) => T::cast(View::Floats(Floats::F64(&[f64::from_bits(self.bits)])), 0),
38            _ => Err(Error::new(Kind::Domain, "compiled totals have a real machine dtype")),
39        }
40    }
41}
42
43pub enum Piece<'a> { Items(View<'a>), Total(Total) }
44
45pub type Pieces<'a> = dyn Fn(std::ops::Range<usize>, &mut dyn FnMut(usize, Piece<'_>)) -> Result<(), Error> + Sync + 'a;
46
47/// The sum of `x`, 0 for an empty slice, and whether it fits. An integer sum that doesn't fit wraps.
48#[inline]
49pub fn sum<T: Copy>(x: &[T]) -> (<Sum as Reduction<T>>::Total, bool) where Sum: Reduction<T> { Sum::slice(x) }
50/// The product of `x`, 1 for an empty slice, and whether it fits. An integer product that doesn't fit wraps.
51#[inline]
52pub fn prod<T: Copy>(x: &[T]) -> (<Product as Reduction<T>>::Total, bool) where Product: Reduction<T> { Product::slice(x) }
53/// The greatest item of `x`.
54#[inline]
55pub fn max<T: Copy>(x: &[T]) -> T where Max: Reduction<T, Total = T> { Max::slice(x).0 }
56/// The least item of `x`.
57#[inline]
58pub fn min<T: Copy>(x: &[T]) -> T where Min: Reduction<T, Total = T> { Min::slice(x).0 }
59/// The least and the greatest item of `x`, in one pass of comparisons and selects. For floats, [`min`] and [`max`] take less time together, because their folds run AArch64's `fmin` and `fmax`.
60#[inline]
61pub fn extremes<T: Copy>(x: &[T]) -> (T, T) where Min: Reduction<T, Total = T>, Max: Reduction<T, Total = T> { extremes_of(x, |v| v) }
62
63/// The least and the greatest `key` of the items of `x`, in the pass [`extremes`] makes. A `key` that only reads an item as another type, as `char as u32` does, costs nothing.
64#[inline]
65pub(crate) fn extremes_of<A: Copy, T: Copy>(x: &[A], key: impl Fn(A) -> T) -> (T, T) where Min: Reduction<T, Total = T>, Max: Reduction<T, Total = T> {
66    let (min, max) = (<Min as Reduction<T>>::step, <Max as Reduction<T>>::step);
67    x.iter().fold((<Min as Reduction<T>>::unit(), <Max as Reduction<T>>::unit()), |(lo, hi), &a| {
68        let v = key(a);
69        (min(lo, v), max(hi, v))
70    })
71}
72
73/// The greater of x and y, as [`max`] gives the greatest item. Beside NaN, or where they're equal, it gives y. A fold that passes its running result as y keeps it beside a NaN item. NumPy calls it `maximum`.
74#[inline(always)]
75pub fn maximum<F: Float>(x: F, y: F) -> F { F::from_working(Core::maximum(x.working(), y.working())) }
76
77/// The lesser of x and y, as [`maximum`] gives the greater. NumPy calls it `minimum`.
78#[inline(always)]
79pub fn minimum<F: Float>(x: F, y: F) -> F { F::from_working(Core::minimum(x.working(), y.working())) }
80
81/// [`maximum`] as a function object, which also takes integers and Booleans. The maximum of two Booleans is their `or`.
82#[derive(Clone, Copy, Debug)]
83pub struct Maximum;
84forms!(#[inline] Maximum at real: |_, [x, y]| crate::slice::Total(maximum(x, y)));
85forms!(#[inline] Maximum at u8, i16, i32, i64: |_, [x, y]| x.max(y));
86forms!(#[inline] Maximum at bool: |_, [x, y]| x | y);
87
88/// [`minimum`] as a function object, which also takes integers and Booleans. The minimum of two Booleans is their `and`.
89#[derive(Clone, Copy, Debug)]
90pub struct Minimum;
91forms!(#[inline] Minimum at real: |_, [x, y]| crate::slice::Total(minimum(x, y)));
92forms!(#[inline] Minimum at u8, i16, i32, i64: |_, [x, y]| x.min(y));
93forms!(#[inline] Minimum at bool: |_, [x, y]| x & y);
94
95/// A reduction by [`Sum`], [`Product`], [`ScaledProduct`], [`Max`], [`Min`], [`And`], [`Or`], [`Xor`], [`Xnor`], [`SumAs`], [`Squares`] or [`Alternated`], implemented inside rustymath once for each item type it takes. `slice` folds a whole slice of consecutive items, in any order, `merge` combines the folds of two neighbouring runs, and `scan` gives the running folds. [`along`], [`fold_into`] and [`scan_along`] fold along an axis whose items aren't consecutive. [`Sum`] and [`Product`] take floats, integers and complex numbers. [`Max`] and [`Min`] take floats, integers and Booleans, [`And`], [`Or`] and [`Xor`] take integers and Booleans, [`Xnor`] takes signed integers, bytes and Booleans, [`SumAs`] takes Booleans and integers, [`Squares`] takes floats and complex numbers, [`ScaledProduct`] takes floats, `Alternated<Sum>` takes floats, integers and complex numbers, `Alternated<ScaledProduct>` takes floats, and `Alternated<Product>` takes complex numbers. An `f16` reduction computes in `f32`. A sum or product of `f16`s gives an `f32` total, for its caller to round once. A total that doesn't fit its type fails, with its value wrapped to that type.
96pub trait Reduction<T> {
97    /// The type of a fold: the item type, `i64` for a sum or product of integers narrower than 64 bits, `f32` for a sum or product of `f16`s, `i16` for an XNOR of bytes, the `C` of [`SumAs`], `f64` for [`Squares`], a [`Scaled`] product for [`ScaledProduct`], or a total with the parity of its count for [`Alternated`].
98    type Total: Copy + Send + Sync;
99    /// Whether disjoint subsequences may be merged in any order.
100    const COMMUTATIVE: bool = false;
101    /// The fold of no items: 0, 1, -∞ or ∞, the integer type's least or greatest value, all ones, or false or true.
102    fn unit() -> Self::Total;
103    /// The exact identity of `step`, which leaves every total unchanged. For float sums it's -0.0, because adding `unit()`, 0.0, turns a total of -0.0 into 0.0. For every other reduction it's `unit()`.
104    #[inline(always)]
105    fn identity() -> Self::Total { Self::unit() }
106    /// `acc` combined with one more item.
107    fn step(acc: Self::Total, x: T) -> Self::Total;
108    /// `step`, and whether its result fits. An integer sum or product wraps where it doesn't.
109    #[inline(always)]
110    fn checked(acc: Self::Total, x: T) -> (Self::Total, bool) { (Self::step(acc, x), true) }
111    /// The fold of the one item `x`, `checked(identity(), x)`, and whether it fits.
112    #[inline(always)]
113    fn one(x: T) -> (Self::Total, bool) { Self::checked(Self::identity(), x) }
114    /// The folds of two neighbouring runs of items combined into the fold of both, and whether it fits.
115    fn merge(a: Self::Total, b: Self::Total) -> (Self::Total, bool);
116    /// `merge`, for a chain of folds whose every step waits for the one before, as van Herk-Gil-Werman's suffixes and prefixes do. As in `merge`, `a` folds the earlier items. Float maximums and minimums override it with `Folds::step_max` and `step_min`, which use AArch64's one-instruction `fmaxnm` and `fminnm`. LLVM can't vectorize those, so they stay out of `merge`.
117    #[inline(always)]
118    fn chain(a: Self::Total, b: Self::Total) -> (Self::Total, bool) { Self::merge(a, b) }
119    /// The fold of `x`, which is `unit()` where `x` is empty, and whether it fits. The default takes each item in turn by `checked`.
120    fn slice(x: &[T]) -> (Self::Total, bool) where Self: Sized, T: Copy {
121        if x.is_empty() { return (Self::unit(), true); }
122        x.iter().fold((Self::identity(), true), |(t, fits), &v| { let (t, ok) = Self::checked(t, v); (t, fits & ok) })
123    }
124    /// Appends the running folds of `x` to `out`, the first combining `start` where it is given, and gives whether every one fits. The default takes each item in turn by `checked` (`checked_steps`).
125    fn scan(x: &[T], start: Option<Self::Total>, out: &mut Vec<Self::Total>) -> bool where Self: Sized, T: Copy { checked_steps::<T, Self>(x, start, out) }
126    /// Folds each row of `data`, a matrix with `columns` items in each row, into the matching item of `out`, and gives whether every fold fits. The default is `fold_rows`, which folds each row of 16 items or more by `slice`. Float sums and products, integer maximums and minimums, and bitwise folds fold rows of 16 to 63 items by `checked` instead, eight rows side by side (`rows_side_by_side`). Each of their steps is one instruction, so keeping several rows' steps in flight costs less than a call of `slice` for each row. Other reductions keep `fold_rows`, where `slice` vectorizes within the row: an integer sum's step checks for overflow, and a float maximum's or minimum's `slice` runs `Folds`' vector kernels. Nobody has timed float maximums side by side since their step became one select on x86.
127    #[inline]
128    fn rows(data: &[T], columns: usize, out: &mut [Self::Total]) -> bool where Self: Sized, T: Copy { fold_rows::<T, Self>(data, columns, out) }
129    /// Appends the running folds of `data`'s lanes of `len` consecutive items to `out`, each lane from `start`, through a route that runs many short lanes at once, and gives whether every fold fits. A reduction without such a route gives `None` and appends nothing, as the default does. [`scan_along`] then runs the lanes itself. The Boolean reductions run lanes of 1 to 64 items through [`Logical`], 64 items to a word.
130    #[inline]
131    fn short_lanes(_data: &[T], _len: usize, _start: Option<Self::Total>, _out: &mut Vec<Self::Total>) -> Option<bool> where Self: Sized { None }
132    /// The most classes for which [`crate::search::ClassNumbers`] folds each class into four totals by position. Each step of a class's total waits on the last through memory, so a reduction whose step takes longer gains from the split over more classes.
133    const SPLIT: usize = 8;
134}
135
136/// A fold's total as an item of a buffer: an [`Element`] stores itself, a [`Scaled`] product stores its value, and [`Alternated`]'s total stores its fold without the parity of its count.
137pub trait Totals: Copy {
138    /// The buffer that holds `totals`.
139    fn buffer_of(totals: Vec<Self>) -> crate::buffer::Buffer;
140    fn from_partial(_: Total) -> Result<Self, Error> { Err(Error::new(Kind::Domain, "this reduction cannot read compiled partial totals")) }
141}
142impl<T: crate::buffer::Cast> Totals for T {
143    #[inline]
144    fn buffer_of(totals: Vec<T>) -> crate::buffer::Buffer { T::buffer(totals) }
145    fn from_partial(total: Total) -> Result<Self, Error> { total.value() }
146}
147impl<S: Totals> Totals for (S, bool) {
148    #[inline]
149    fn buffer_of(totals: Vec<(S, bool)>) -> crate::buffer::Buffer { S::buffer_of(totals.into_iter().map(|(s, _)| s).collect()) }
150}
151impl<F: Native + Element> Totals for Scaled<F> {
152    #[inline]
153    fn buffer_of(totals: Vec<Scaled<F>>) -> crate::buffer::Buffer { F::buffer(totals.into_iter().map(Scaled::value).collect()) }
154}
155
156/// [`sum`] as a [`Reduction`].
157#[derive(Clone, Copy, Debug)]
158pub struct Sum;
159/// [`prod`] as a [`Reduction`].
160#[derive(Clone, Copy, Debug)]
161pub struct Product;
162/// [`max`] as a [`Reduction`]. Its step on floats is [`maximum`] of the item and the total, which a NaN item leaves unchanged. Every fold and scan starts at `-∞`, and AArch64's and wasm's vector folds keep the running result beside a NaN item, as that step does. So a NaN item changes no running result on any target, and a fold of NaN items alone gives `-∞`.
163#[derive(Clone, Copy, Debug)]
164pub struct Max;
165/// [`min`] as a [`Reduction`], as [`Max`] is [`max`], starting at `∞`.
166#[derive(Clone, Copy, Debug)]
167pub struct Min;
168/// The fold of integers by bitwise `&`, and of Booleans by `and`.
169#[derive(Clone, Copy, Debug)]
170pub struct And;
171/// The fold of integers by bitwise `|`, and of Booleans by `or`.
172#[derive(Clone, Copy, Debug)]
173pub struct Or;
174/// The fold of integers by bitwise `^`, and of Booleans by `!=`: true where an odd number of them are true.
175#[derive(Clone, Copy, Debug)]
176pub struct Xor;
177/// The fold of integers by bitwise XNOR, `!(a ^ b)`, and of Booleans by `==`: true where an even number of them are false.
178#[derive(Clone, Copy, Debug)]
179pub struct Xnor;
180forms!(Xor at bool: |_, [x, y]| x != y);
181forms!(Xnor at bool: |_, [x, y]| x == y);
182
183/// `x` folded by `add` from `unit`, through the running results that `running_results` gives, which `merge` combines.
184#[inline(always)]
185pub(crate) fn folded<A: Copy, S: Copy>(x: &[A], unit: S, add: impl Fn(S, A) -> S + Copy, merge: impl Fn(S, S) -> S) -> S {
186    let merge = &merge;
187    running_results(x, unit, add, merge, move |parts, rest| rest.iter().fold(parts.iter().fold(unit, |s, &p| merge(s, p)), |s, &v| add(s, v)))
188}
189
190/// The running results of `add` from `unit` over `x`: as many as `slice::running` gives, or fewer for a short `x`, so that each takes at least 8 items. A short slice then doesn't set up and merge more results than it has items for. Each takes the items at its own position in every whole group of that many. `finish` gets the results, and the items after the last whole group. Their count is always even. `merge` combines two running results, as [`tree`] does for a long `x`.
191#[inline(always)]
192fn running_results<A: Copy, S: Copy, T>(x: &[A], unit: S, add: impl Fn(S, A) -> S, merge: impl Fn(S, S) -> S, finish: impl FnOnce(&[S], &[A]) -> T) -> T {
193    match crate::slice::running(size_of::<S>()).min(x.len() / 8) {
194        ..8 => running_by::<A, S, T, 4>(x, unit, add, merge, finish), 8..16 => running_by::<A, S, T, 8>(x, unit, add, merge, finish),
195        16..32 => running_by::<A, S, T, 16>(x, unit, add, merge, finish), _ => running_by::<A, S, T, 32>(x, unit, add, merge, finish)
196    }
197}
198
199/// `running_results` with `R` of them, folded by [`tree`]. Float callers pass a strict `+` or `*`, not `algebraic_add`: under thin LTO the inner loop is unrolled before the loop vectorizer runs, and reassociation let it treat each running result as a reduction across chunks, which on wasm read every lane from a different chunk. A strict operation also fixes the order of a float total in every build, which changed some totals in their last bits from earlier builds, whose order varied by build and target.
200#[inline(always)]
201fn running_by<A: Copy, S: Copy, T, const R: usize>(
202    x: &[A], unit: S, add: impl Fn(S, A) -> S, merge: impl Fn(S, S) -> S, finish: impl FnOnce(&[S], &[A]) -> T) -> T {
203    let (chunks, rest) = x.as_chunks::<R>();
204    let parts = if chunks.len() * R <= LEAF { leaf(chunks, unit, &add) } else { tree(chunks, unit, &add, &merge) };
205    finish(&parts, rest)
206}
207
208/// The most items that one loop of [`tree`] folds. Each leaf adds a call and a merge. Leaves of 16,384 items cost a 65,536-item sum about 2% fewer instructions than 4,096, but left a million tenths' sum in `f32` with 3.4 times the error.
209const LEAF: usize = 4096;
210
211/// The `R` running results of `add` from `unit` over `chunks`, each taking the item at its own position in every chunk. Up to [`LEAF`] items fold in one loop, which updates the results in one local array and compiles to vector instructions. More items split into halves, whose results `merge` combines position by position. Each result's chain of steps then stays short, so the rounding error of a float total grows with the logarithm of its length past a leaf, not in proportion to it.
212fn tree<A: Copy, S: Copy, const R: usize>(chunks: &[[A; R]], unit: S, add: &impl Fn(S, A) -> S, merge: &impl Fn(S, S) -> S) -> [S; R] {
213    if chunks.len() * R > LEAF {
214        let (a, b) = chunks.split_at(chunks.len() / 2);
215        let (mut a, b) = (tree(a, unit, add, merge), tree(b, unit, add, merge));
216        for k in 0..R { a[k] = merge(a[k], b[k]) }
217        return a;
218    }
219    leaf(chunks, unit, add)
220}
221
222#[inline(always)]
223fn leaf<A: Copy, S: Copy, const R: usize>(chunks: &[[A; R]], unit: S, add: &impl Fn(S, A) -> S) -> [S; R] {
224    let mut parts = [unit; R];
225    for chunk in chunks { for k in 0..R { parts[k] = add(parts[k], chunk[k]) } }
226    parts
227}
228
229/// The groups of four items in each block of [`grouped`]'s first pass. Its buffer of running sums stays in L1 for every total type.
230const SCAN_BLOCK: usize = 64;
231
232/// Appends the running totals of `x` by `f` to `out`, the first combining `start` where it is given. `from` reads each item as a total, and `to` writes each total. Each group of four items scans within itself, then combines with the total before it. Only one operation in each group then waits for the group before, and `f` may combine items in any order. Set `reorders` for integer sums and bitwise steps, which the compiler may reorder: in one loop it rewrites them into one chain through the total, with one step waiting for each item. With `reorders`, a first pass over each block of `SCAN_BLOCK` groups writes each group's four running sums to a buffer, in the order the outputs take them. The second pass loads them as one vector, adds the total, and adds the group's last sum to the total, which the compiler can't rewrite. Floats keep the one loop. The compiler can't reorder float operations, so their loop already keeps one operation in each group waiting, and a first pass would only add work. Integer maximums and minimums keep the one loop too. On Apple silicon, the one loop costs them up to about twice as much in some processes as in others. With the first pass they cost the same in every process, but 64-bit items then cost about a quarter more than the one loop usually does. Sums into 64-bit totals keep the one loop for the same reason. Its chain of one-cycle additions, one for each item, took a fifth fewer instructions and about a quarter less time than the first pass, whose vectors hold only two 64-bit sums.
233#[inline(always)]
234pub(crate) fn grouped<A: Copy, S: Copy, O>(
235    x: &[A], start: Option<S>, out: &mut Vec<O>, from: impl Fn(A) -> S, f: impl Fn(S, S) -> S, to: impl Fn(S) -> O, reorders: bool) {
236    let Some((&first, rest)) = x.split_first() else { return };
237    unsafe {
238        appended(out, x.len(), |slots| {
239            let first = from(first);
240            let mut total = start.map_or(first, |s| f(s, first));
241            slots[0].write(to(total));
242            let ((groups, tail), (outs, last)) = (rest.as_chunks::<4>(), slots[1..].as_chunks_mut::<4>());
243            let scanned = |[a, b, c, d]: [A; 4]| {
244                let (a, b, c, d) = (from(a), from(b), from(c), from(d));
245                let ab = f(a, b);
246                [a, ab, f(ab, c), f(ab, f(c, d))]
247            };
248            let (mut sums, mut ends) = ([std::mem::MaybeUninit::<[S; 4]>::uninit(); SCAN_BLOCK], [std::mem::MaybeUninit::<S>::uninit(); SCAN_BLOCK]);
249            // Blocks by index: a loop over `chunks(SCAN_BLOCK)` zipped with `chunks_mut` cost `+\b` 90% more instructions.
250            let mut i0 = 0;
251            while i0 < groups.len() {
252                let n = (groups.len() - i0).min(SCAN_BLOCK);
253                let (groups, outs) = (&groups[i0..i0 + n], &mut outs[i0..i0 + n]);
254                if reorders {
255                    for g in 0..n {
256                        let s = scanned(groups[g]);
257                        sums[g].write(s);
258                        ends[g].write(s[3]);
259                    }
260                }
261                for g in 0..n {
262                    // SAFETY: with `reorders`, the first pass wrote entries 0 to `n`.
263                    let sums = if reorders { sums[g].assume_init() } else { scanned(groups[g]) };
264                    // Four writes, not `array::map`, which cost `+\b` 53% more instructions.
265                    for k in 0..4 { outs[g][k].write(to(f(total, sums[k]))); }
266                    // The group's sum from its own buffer, which the compiler can't see is the last of `sums`. Taken from `sums`, the total would wait on the vector the outputs use.
267                    total = f(total, if reorders { ends[g].assume_init() } else { sums[3] });
268                }
269                i0 += n;
270            }
271            for (o, &v) in last.iter_mut().zip(tail) {
272                total = f(total, from(v));
273                o.write(to(total));
274            }
275        })
276    }
277}
278
279/// The position of the first item of `x` that `test` picks, or its length where it picks none. Each block of 64 items is tested with no branch, so the test vectorizes.
280#[inline]
281fn first_where<T: Copy>(x: &[T], test: impl Fn(T) -> bool) -> usize {
282    let (blocks, _) = x.as_chunks::<64>();
283    let from = blocks.iter().position(|b| b.iter().fold(false, |any, &v| any | test(v))).unwrap_or(blocks.len()) * 64;
284    x[from..].iter().position(|&v| test(v)).map_or(x.len(), |i| from + i)
285}
286
287/// A pick after a running fold steps from `before` to `after` at row `k`: `k` where the fold changed, and `pick` where it didn't. A pick that only moves where its fold changes stays on the first item that gives the fold.
288#[inline(always)]
289fn moved<T: PartialEq>(pick: usize, before: T, after: T, k: usize) -> usize { if after != before { k } else { pick } }
290
291/// Each float reduction at `f32` and `f64`: its unit, its identity where that isn't its unit, its step, its `chain` where that differs from its merge, its fold of a slice, and its scan.
292macro_rules! floats {
293    ($($r:ident: $unit:ident, $(identity: $identity:expr,)? $(rows: $rows:ident,)? $(chain: $chain:expr,)? |$a:ident, $b:ident| $step:expr, $fold:expr, $scan:expr;)+) => {$(
294        impl<F: Native> Reduction<F> for $r {
295            type Total = F;
296            const COMMUTATIVE: bool = true;
297            const SPLIT: usize = 64;
298            #[inline(always)]
299            fn unit() -> F { F::$unit() }
300            #[inline(always)]
301            fn step($a: F, $b: F) -> F { $step }
302            #[inline(always)]
303            fn merge(a: F, b: F) -> (F, bool) { (Self::step(a, b), true) }
304            $(#[inline(always)]
305            fn identity() -> F { $identity })?
306            $(#[inline]
307            fn rows(data: &[F], columns: usize, out: &mut [F]) -> bool { $rows::<F, Self>(data, columns, out) })?
308            $(#[inline(always)]
309            fn chain(a: F, b: F) -> (F, bool) { (($chain)(a, b), true) })?
310            #[inline]
311            fn slice(x: &[F]) -> (F, bool) { (($fold)(x), true) }
312            #[inline]
313            fn scan(x: &[F], start: Option<F>, out: &mut Vec<F>) -> bool {
314                ($scan)(x, start, out);
315                true
316            }
317        }
318    )+};
319}
320floats! {
321    Sum: zero, identity: -F::zero(), rows: rows_side_by_side, |a, b| a.algebraic_add(b), |x: &[F]| if x.is_empty() { <Self as Reduction<F>>::unit() } else { folded(x, <Self as Reduction<F>>::identity(), |a: F, b: F| a + b, |a: F, b: F| a + b) }, F::scan_sum;
322    Product: one, rows: rows_side_by_side, |a, b| a.algebraic_mul(b), |x| folded(x, F::one(), |a: F, b: F| a * b, |a: F, b: F| a * b), |x, start, out| grouped(x, start, out, |v| v, |a: F, b: F| a * b, |t| t, false);
323    Max: neg_infinity, chain: F::step_max, |acc, x| x.maximum(acc), F::fold_max, F::scan_max;
324    Min: infinity, chain: F::step_min, |acc, x| x.minimum(acc), F::fold_min, F::scan_min;
325}
326
327/// Writes the reduction of `f16`s by each `$r`, which runs the same reduction of `f32`s and totals in `$total`. `$narrow` converts an `f32` total to a `$total`. `$wide` converts a `$total` back to `f32`. `$add` combines `f32` totals in `slice`: a strict operation for a sum or product, as `running_by` needs.
328macro_rules! halves {
329    ($total:ty, $wide:expr, $narrow:expr; $($r:ty: $add:expr),+) => {$(
330        impl Reduction<f16> for $r {
331            type Total = $total;
332            const COMMUTATIVE: bool = true;
333            #[inline(always)]
334            fn unit() -> $total { $narrow(<$r as Reduction<f32>>::unit()) }
335            #[inline(always)]
336            fn step(acc: $total, x: f16) -> $total { $narrow(<$r as Reduction<f32>>::step($wide(acc), x.to_f32())) }
337            #[inline(always)]
338            fn merge(a: $total, b: $total) -> ($total, bool) { ($narrow(<$r as Reduction<f32>>::merge($wide(a), $wide(b)).0), true) }
339            #[inline(always)]
340            fn identity() -> $total { $narrow(<$r as Reduction<f32>>::identity()) }
341            #[inline]
342            fn slice(x: &[f16]) -> ($total, bool) {
343                let (unit, add) = (<$r as Reduction<f32>>::identity(), $add);
344                ($narrow(if x.is_empty() { <$r as Reduction<f32>>::unit() } else { folded(x, unit, |s, v| add(s, v.to_f32()), add) }), true)
345            }
346            #[inline]
347            fn scan(x: &[f16], start: Option<$total>, out: &mut Vec<$total>) -> bool {
348                grouped(x, start.map($wide), out, |v: f16| <$r as Reduction<f32>>::one(v.to_f32()).0, <$r as Reduction<f32>>::step, $narrow, false);
349                true
350            }
351        }
352    )+};
353}
354// A sum or product of `f16`s totals in `f32`, for its caller to round once. A maximum or minimum is exact in `f16`.
355halves!(f32, |t: f32| t, |t: f32| t; Sum: |a: f32, b: f32| a + b, Product: |a: f32, b: f32| a * b);
356halves!(f16, f16::to_f32, f16::from_f32; Max: <Max as Reduction<f32>>::step, Min: <Min as Reduction<f32>>::step);
357
358/// Each sum of complex numbers adds their real parts and their imaginary parts, in any order.
359impl<F: Native> Reduction<Complex<F>> for Sum {
360    type Total = Complex<F>;
361    const COMMUTATIVE: bool = true;
362    #[inline(always)]
363    fn unit() -> Complex<F> { Complex::new(F::zero(), F::zero()) }
364    #[inline(always)]
365    fn step(acc: Complex<F>, x: Complex<F>) -> Complex<F> { Complex::new(acc.re.algebraic_add(x.re), acc.im.algebraic_add(x.im)) }
366    #[inline(always)]
367    fn merge(a: Complex<F>, b: Complex<F>) -> (Complex<F>, bool) { (<Self as Reduction<Complex<F>>>::step(a, b), true) }
368    #[inline(always)]
369    fn identity() -> Complex<F> { Complex::new(-F::zero(), -F::zero()) }
370    #[inline]
371    fn slice(x: &[Complex<F>]) -> (Complex<F>, bool) {
372        let add = |a: Complex<F>, b: Complex<F>| a + b;
373        (if x.is_empty() { <Self as Reduction<Complex<F>>>::unit() } else { folded(x, <Self as Reduction<Complex<F>>>::identity(), add, add) }, true)
374    }
375    #[inline]
376    fn scan(x: &[Complex<F>], start: Option<Complex<F>>, out: &mut Vec<Complex<F>>) -> bool {
377        grouped(x, start, out, |v| v, |a: Complex<F>, b| a + b, |t| t, false);
378        true
379    }
380}
381
382/// Complex products regroup multiplication through the same running totals as real products.
383impl<F: Native> Reduction<Complex<F>> for Product {
384    type Total = Complex<F>;
385    const COMMUTATIVE: bool = true;
386    #[inline(always)]
387    fn unit() -> Complex<F> { Complex::new(F::one(), F::zero()) }
388    #[inline(always)]
389    fn step(acc: Complex<F>, x: Complex<F>) -> Complex<F> { acc * x }
390    #[inline(always)]
391    fn merge(a: Complex<F>, b: Complex<F>) -> (Complex<F>, bool) { (a * b, true) }
392    #[inline]
393    fn slice(x: &[Complex<F>]) -> (Complex<F>, bool) {
394        let step = <Self as Reduction<Complex<F>>>::step;
395        (folded(x, <Self as Reduction<Complex<F>>>::unit(), step, step), true)
396    }
397    #[inline]
398    fn scan(x: &[Complex<F>], start: Option<Complex<F>>, out: &mut Vec<Complex<F>>) -> bool {
399        grouped(x, start, out, |v| v, |a: Complex<F>, b| a * b, |t| t, false);
400        true
401    }
402}
403
404/// AArch64's `fmaxnm` and `fminnm` give the argument that isn't NaN, and wasm's `pmax` and `pmin` give their first argument where either is NaN. A fold starts at `-∞` or `∞` and passes its running result first, so each gives what `Core::maximum` and `Core::minimum` give, in one instruction. A scan steps the same way. `Folds::step_max` and `step_min` hold that step. `fmaxnm` drops a NaN item wherever it stands, but `pmax` keeps a NaN running result, so a wasm scan turns each NaN item into the start as it reads it, as `records` describes. A NaN start fills a wasm scan with NaN, and `fmaxnm` drops it. Wasm's `max` and `min` give NaN beside NaN, and on x86-64 hosts take about eight instructions each. A fold runs the vector forms on `PARTS` running vectors. Each step of a scan depends on the step before. In wasm, the step takes lane 0 of the vector instruction on two splats. On AArch64 that code keeps a `dup` before each `fmaxnm` at `f32`, which has no one-lane vector type, and `f64::max`, LLVM's `maxnum`, quiets each argument with an `fminnm` of itself first. The step there is inline assembly. Each type's entry names its scan. AArch64's `f64` scan is `grouped`, at the speed of memory. Skipping blocks gained it nothing. Each block with a new maximum paid for a test it didn't use. The others skip blocks through `records`. AArch64's `f32` tests a block by comparing each item, as `below` does. Wasm tests it by its fold, then one comparison. In wasm, comparing each item took five instructions for two items. Each entry also names its running sum. `f32`'s is `sum_scan`, which scans four-lane vectors and makes `+\` about 1.3 times faster. `f64`'s is `grouped`, because a two-lane vector scan needs more operations per item than `grouped`'s two additions.
405#[cfg(any(target_arch = "aarch64", all(target_arch = "wasm32", target_feature = "simd128")))]
406mod lanes {
407    use super::*;
408    #[cfg(target_arch = "wasm32")]
409    use core::arch::wasm32::*;
410    #[cfg(target_arch = "aarch64")]
411    use core::arch::{aarch64::*, asm};
412    /// How many running vectors a fold keeps.
413    const PARTS: usize = 8;
414    macro_rules! lanes {
415        ($($t:ty: $lanes:literal, $splat:ident, $load:ident, $max:ident, $min:ident, $step:ident($arg:tt), $scan:ident, $sum:ident;)+) => {$(
416            impl Folds for $t {
417                #[inline(always)]
418                fn step_max(t: $t, v: $t) -> $t { lanes!(@$step $arg, "fmaxnm", $splat, $max, t, v) }
419                #[inline(always)]
420                fn step_min(t: $t, v: $t) -> $t { lanes!(@$step $arg, "fminnm", $splat, $min, t, v) }
421                fn fold_max(x: &[$t]) -> $t { lanes!(@fold x, $t, $lanes, NEG_INFINITY, $splat, $load, $max, Self::step_max) }
422                fn fold_min(x: &[$t]) -> $t { lanes!(@fold x, $t, $lanes, INFINITY, $splat, $load, $min, Self::step_min) }
423                fn scan_max(x: &[$t], start: Option<$t>, out: &mut Vec<$t>) {
424                    lanes!(@scan $scan, x, start, out, lanes!(@read $step, $t, NEG_INFINITY), Self::step_max, below, |b: &[$t], t: $t| Self::fold_max(b) < t)
425                }
426                fn scan_min(x: &[$t], start: Option<$t>, out: &mut Vec<$t>) {
427                    lanes!(@scan $scan, x, start, out, lanes!(@read $step, $t, INFINITY), Self::step_min, above, |b: &[$t], t: $t| Self::fold_min(b) > t)
428                }
429                fn scan_sum(x: &[$t], start: Option<$t>, out: &mut Vec<$t>) { lanes!(@sum $sum, x, start, out) }
430                #[inline(always)]
431                fn ordered_lanes<const W: usize>(a: [$t; W], c: [$t; W]) -> ([$t; W], [$t; W]) {
432                    const { assert!(W == $lanes) };
433                    // SAFETY: this module compiles only for targets with these instructions, and each row of `$lanes` items is one vector. `$max` takes `c` first so that equal items keep their places beside wasm's `pmax`, which gives its first argument where they're equal.
434                    unsafe {
435                        let (x, y) = ($load(a.as_ptr().cast()), $load(c.as_ptr().cast()));
436                        (core::mem::transmute_copy(&$min(x, y)), core::mem::transmute_copy(&$max(y, x)))
437                    }
438                }
439            }
440        )+};
441        (@fold $x:ident, $t:ty, $lanes:literal, $unit:ident, $splat:ident, $load:ident, $op:ident, $step:expr) => {{
442            let (chunks, rest) = $x.as_chunks::<{ PARTS * $lanes }>();
443            // SAFETY: this module compiles only for targets with these instructions. Each load reads `$lanes` items of one chunk, and a vector holds `$lanes` items of `$t`.
444            let lanes: [$t; $lanes] = unsafe {
445                let mut parts = [$splat(<$t>::$unit); PARTS];
446                for c in chunks { for k in 0..PARTS { parts[k] = $op(parts[k], $load(c.as_ptr().add(k * $lanes).cast())) } }
447                core::mem::transmute(parts.into_iter().reduce(|a, b| $op(a, b)).expect("a fold keeps `PARTS` vectors"))
448            };
449            lanes.into_iter().chain(rest.iter().copied()).fold(<$t>::$unit, $step)
450        }};
451        (@asm $reg:literal, $ins:literal, $splat:ident, $op:ident, $x:ident, $y:ident) => {{
452            let r;
453            // SAFETY: the instruction reads two registers and writes a third, with no memory access.
454            unsafe { asm!(concat!($ins, " {0:", $reg, "}, {1:", $reg, "}, {2:", $reg, "}"), lateout(vreg) r, in(vreg) $x, in(vreg) $y, options(pure, nomem, nostack)) }
455            r
456        }};
457        (@lane $lane:ident, $ins:literal, $splat:ident, $op:ident, $x:ident, $y:ident) => { $lane::<0>($op($splat($x), $splat($y))) };
458        // AArch64's `fmaxnm` drops a NaN item wherever it stands, so its scans read items as they are. Wasm's `pmax` keeps a NaN running result, so its scans turn each NaN item into the start as they read it, with a test for NaN and a select.
459        (@read asm, $t:ty, $unit:ident) => { |v: $t| v };
460        (@read lane, $t:ty, $unit:ident) => { |v: $t| if v.is_nan() { <$t>::$unit } else { v } };
461        (@scan grouped, $x:ident, $start:ident, $out:ident, $from:expr, $step:expr, $compare:expr, $fold:expr) => { grouped($x, $start, $out, $from, $step, |t| t, false) };
462        (@scan compare, $x:ident, $start:ident, $out:ident, $from:expr, $step:expr, $compare:expr, $fold:expr) => { records($x, $start, $out, $from, $step, $compare) };
463        (@scan fold, $x:ident, $start:ident, $out:ident, $from:expr, $step:expr, $compare:expr, $fold:expr) => { records($x, $start, $out, $from, $step, $fold) };
464        (@sum grouped, $x:ident, $start:ident, $out:ident) => { grouped($x, $start, $out, |v| v, |a, b| a + b, |t| t, false) };
465        (@sum vector, $x:ident, $start:ident, $out:ident) => { sum_scan($x, $start, $out) };
466    }
467    #[cfg(target_arch = "aarch64")]
468    lanes! {
469        f64: 2, vdupq_n_f64, vld1q_f64, vmaxnmq_f64, vminnmq_f64, asm("d"), grouped, grouped;
470        f32: 4, vdupq_n_f32, vld1q_f32, vmaxnmq_f32, vminnmq_f32, asm("s"), compare, vector;
471    }
472    /// Four-lane `f32` vectors, for `sum_scan`. `up1` and `up2` move a vector up one lane or two, filling the gap from `fill`. `last` copies lane 3 to every lane.
473    #[cfg(target_arch = "aarch64")]
474    mod quad {
475        use core::arch::aarch64::*;
476        pub type V = float32x4_t;
477        // SAFETY: AArch64 always has these instructions. The loads and stores leave their bounds to the caller.
478        #[inline(always)]
479        pub fn splat(x: f32) -> V { unsafe { vdupq_n_f32(x) } }
480        #[inline(always)]
481        pub unsafe fn load(p: *const f32) -> V { unsafe { vld1q_f32(p) } }
482        #[inline(always)]
483        pub unsafe fn store(p: *mut f32, v: V) { unsafe { vst1q_f32(p, v) } }
484        #[inline(always)]
485        pub fn add(a: V, b: V) -> V { unsafe { vaddq_f32(a, b) } }
486        #[inline(always)]
487        pub fn up1(fill: V, v: V) -> V { unsafe { vextq_f32::<3>(fill, v) } }
488        #[inline(always)]
489        pub fn up2(fill: V, v: V) -> V { unsafe { vextq_f32::<2>(fill, v) } }
490        #[inline(always)]
491        pub fn last(v: V) -> V { unsafe { vdupq_laneq_f32::<3>(v) } }
492        #[inline(always)]
493        pub fn first(v: V) -> f32 { unsafe { vgetq_lane_f32::<0>(v) } }
494    }
495    #[cfg(target_arch = "wasm32")]
496    mod quad {
497        use core::arch::wasm32::*;
498        pub type V = v128;
499        #[inline(always)]
500        pub fn splat(x: f32) -> V { f32x4_splat(x) }
501        #[inline(always)]
502        pub unsafe fn load(p: *const f32) -> V { unsafe { v128_load(p.cast()) } }
503        #[inline(always)]
504        pub unsafe fn store(p: *mut f32, v: V) { unsafe { v128_store(p.cast(), v) } }
505        #[inline(always)]
506        pub fn add(a: V, b: V) -> V { f32x4_add(a, b) }
507        #[inline(always)]
508        pub fn up1(fill: V, v: V) -> V { i32x4_shuffle::<3, 4, 5, 6>(fill, v) }
509        #[inline(always)]
510        pub fn up2(fill: V, v: V) -> V { i32x4_shuffle::<2, 3, 4, 5>(fill, v) }
511        #[inline(always)]
512        pub fn last(v: V) -> V { i32x4_shuffle::<3, 3, 3, 3>(v, v) }
513        #[inline(always)]
514        pub fn first(v: V) -> f32 { f32x4_extract_lane::<0>(v) }
515    }
516    /// The running sums of `x` after `start`, 16 items at a time. Each vector of four scans within itself in two shifted additions. Each vector then adds the total of everything before it: the running total, plus the sums of the vectors before it in its block. Only one addition in each block waits for the block before.
517    fn sum_scan(x: &[f32], start: Option<f32>, out: &mut Vec<f32>) {
518        use quad::*;
519        unsafe {
520            appended(out, x.len(), |slots| {
521                let ((chunks, rest), (outs, tail)) = (x.as_chunks::<16>(), slots.as_chunks_mut::<16>());
522                let (fill, mut c) = (splat(-0.0), splat(start.unwrap_or(-0.0)));
523                for (o, x) in outs.iter_mut().zip(chunks) {
524                    let scanned = |k: usize| {
525                        // SAFETY: the load reads four items of one chunk.
526                        let v = unsafe { load(x.as_ptr().add(4 * k)) };
527                        let v = add(v, up1(fill, v));
528                        add(v, up2(fill, v))
529                    };
530                    let (p0, p1, p2, p3) = (scanned(0), scanned(1), scanned(2), scanned(3));
531                    let s0 = last(p0);
532                    let s01 = add(s0, last(p1));
533                    let s012 = add(s01, last(p2));
534                    let o = o.as_mut_ptr().cast::<f32>();
535                    // SAFETY: each store writes four slots of one chunk.
536                    unsafe {
537                        store(o, add(c, p0));
538                        store(o.add(4), add(add(c, s0), p1));
539                        store(o.add(8), add(add(c, s01), p2));
540                        store(o.add(12), add(add(c, s012), p3));
541                    }
542                    c = add(c, add(s012, last(p3)));
543                }
544                let mut total = first(c);
545                for (o, &v) in tail.iter_mut().zip(rest) {
546                    total += v;
547                    o.write(total);
548                }
549            })
550        }
551    }
552    #[cfg(target_arch = "wasm32")]
553    lanes! {
554        f64: 2, f64x2_splat, v128_load, f64x2_pmax, f64x2_pmin, lane(f64x2_extract_lane), fold, grouped;
555        f32: 4, f32x4_splat, v128_load, f32x4_pmax, f32x4_pmin, lane(f32x4_extract_lane), fold, vector;
556    }
557}
558
559/// Elsewhere, the folds and scans use `Folds`' defaults. A fold's loop of `Core::maximum` vectorizes, and each step of a scan is one `maxsd` or `minsd` on x86-64. With AVX2, `ordered_lanes` compares each row of `search::network` as one vector, through `vminps` and `vmaxps` or their `f64` forms, which give what `Core::minimum` and `Core::maximum` give. The default leaves its lanes to LLVM, which vectorizes `f32`'s block of 8 rows of 8 lanes across its rows, with shuffles and spills, and stores each lesser row through a masked store.
560#[cfg(not(any(target_arch = "aarch64", all(target_arch = "wasm32", target_feature = "simd128"))))]
561mod plain {
562    #[cfg(all(target_arch = "x86_64", target_feature = "avx2"))]
563    use core::arch::x86_64::*;
564    macro_rules! plain {
565        ($($t:ty: $lanes:literal, $load:ident, $min:ident, $max:ident;)+) => {$(
566            impl super::Folds for $t {
567                #[cfg(all(target_arch = "x86_64", target_feature = "avx2"))]
568                #[inline(always)]
569                fn ordered_lanes<const W: usize>(a: [$t; W], c: [$t; W]) -> ([$t; W], [$t; W]) {
570                    const { assert!(W == $lanes) };
571                    // SAFETY: this code compiles only with AVX2, and each row of `$lanes` items is one 256-bit vector. `$min(y, x)` and `$max(x, y)` give `c[j].minimum(a[j])` and `a[j].maximum(c[j])`, as the default does.
572                    unsafe {
573                        let (x, y) = ($load(a.as_ptr()), $load(c.as_ptr()));
574                        (core::mem::transmute_copy(&$min(y, x)), core::mem::transmute_copy(&$max(x, y)))
575                    }
576                }
577            }
578        )+};
579    }
580    plain! {
581        f32: 8, _mm256_loadu_ps, _mm256_min_ps, _mm256_max_ps;
582        f64: 4, _mm256_loadu_pd, _mm256_min_pd, _mm256_max_pd;
583    }
584}
585
586/// The sum of 32-bit integers, wrapped to `i64`, and whether it fits. Sums of up to 2^31 of them fit `i64`, which their running results hold.
587#[inline(always)]
588fn narrow_sum<T: Copy + Into<i64>>(x: &[T]) -> (i64, bool) {
589    let total: i128 = x.chunks(1 << 31).map(|c| i128::from(folded(c, 0i64, |s, v| s + v.into(), |a, b| a + b))).sum();
590    (total as i64, i64::try_from(total).is_ok())
591}
592
593/// The sum of integers of 8 or 16 bits, which always fits `i64`. No block of 2^15 of them overflows an `i32`. Each block sums in `i32`, whose vectors hold twice as many running sums as `i64`'s.
594#[inline(always)]
595fn small_sum<T: Copy + Into<i32>>(x: &[T]) -> (i64, bool) {
596    (x.chunks(1 << 15).map(|c| i64::from(folded(c, 0i32, |s, v| s + v.into(), |a, b| a + b))).sum(), true)
597}
598
599/// The sum of 64-bit integers, wrapped, and whether it fits. Each item splits into its high and low 32 bits. Neither half's sum can overflow `i64` within 2^31 items, and both sums vectorize.
600#[inline(always)]
601fn wide_sum(x: &[i64]) -> (i64, bool) {
602    let halves = |c: &[i64]| folded(c, (0i64, 0i64), |(h, l), v| (h + (v >> 32), l + (v & 0xFFFF_FFFF)), |(h, l), (g, m)| (h + g, l + m));
603    let total: i128 = x.chunks(1 << 31).map(|c| {
604        let (h, l) = halves(c);
605        (i128::from(h) << 32) + i128::from(l)
606    }).sum();
607    (total as i64, i64::try_from(total).is_ok())
608}
609
610/// The running sums of `x` from `start` at `i64`, wrapped, and whether every one fits. A sum overflows where its sign differs from both its arguments' signs, as `Overflowing::plus` tests it. The loop gathers those sign bits in one word and tests it once at the end. A bool for each step, as `Overflowing::plus` gives, measured 7% slower.
611#[inline(always)]
612fn signed_cumsum<T: Copy + Into<i64>>(x: &[T], start: i64, out: &mut Vec<i64>) -> bool {
613    unsafe {
614        appended(out, x.len(), |slots| {
615            let (mut total, mut overflow) = (start, 0i64);
616            for (o, &v) in slots.iter_mut().zip(x) {
617                let v: i64 = v.into();
618                let sum = total.wrapping_add(v);
619                overflow |= (total ^ sum) & (v ^ sum);
620                total = sum;
621                o.write(sum);
622            }
623            overflow >= 0
624        })
625    }
626}
627
628/// Appends the running totals of `x` from `start` by `op`, which gives each total wrapped and whether it fits, and gives whether every total fits. Each step waits only for the wrapped total before it.
629#[inline(always)]
630fn checked_scan<T: Copy + Into<S>, S: Copy>(x: &[T], start: S, out: &mut Vec<S>, op: impl Fn(S, S) -> (S, bool)) -> bool {
631    unsafe {
632        appended(out, x.len(), |slots| {
633            let (mut total, mut ok) = (start, true);
634            for (o, &v) in slots.iter_mut().zip(x) {
635                let (next, fits) = op(total, v.into());
636                (total, ok) = (next, ok & fits);
637                o.write(next);
638            }
639            ok
640        })
641    }
642}
643
644/// [`Sum`] and [`Product`] for each integer type of compact buffers, at `i64`. `$sum` sums a slice. Each step and product wraps through [`Overflowing`], which also tests whether it fits.
645macro_rules! integers {
646    ($($t:ty: $sum:ident),+) => {$(
647        impl Reduction<$t> for Sum {
648            type Total = i64;
649            const COMMUTATIVE: bool = true;
650            #[inline(always)]
651            fn unit() -> i64 { 0 }
652            #[inline(always)]
653            fn step(acc: i64, x: $t) -> i64 { acc.plus(x.into()).0 }
654            #[inline(always)]
655            fn checked(acc: i64, x: $t) -> (i64, bool) { acc.plus(x.into()) }
656            #[inline(always)]
657            fn merge(a: i64, b: i64) -> (i64, bool) { a.plus(b) }
658            #[inline]
659            fn slice(x: &[$t]) -> (i64, bool) { $sum(x) }
660            #[inline]
661            fn scan(x: &[$t], start: Option<i64>, out: &mut Vec<i64>) -> bool { signed_cumsum(x, start.unwrap_or(0), out) }
662        }
663        impl Reduction<$t> for Product {
664            type Total = i64;
665            const COMMUTATIVE: bool = true;
666            #[inline(always)]
667            fn unit() -> i64 { 1 }
668            #[inline(always)]
669            fn step(acc: i64, x: $t) -> i64 { acc.times(x.into()).0 }
670            #[inline(always)]
671            fn checked(acc: i64, x: $t) -> (i64, bool) { acc.times(x.into()) }
672            #[inline(always)]
673            fn merge(a: i64, b: i64) -> (i64, bool) { a.times(b) }
674            #[inline]
675            fn slice(x: &[$t]) -> (i64, bool) {
676                x.iter().fold((1 as i64, true), |(p, ok), &v| { let (q, fits) = p.times(v.into()); (q, ok & fits) })
677            }
678            #[inline]
679            fn scan(x: &[$t], start: Option<i64>, out: &mut Vec<i64>) -> bool { checked_scan(x, start.unwrap_or(1), out, <i64>::times) }
680        }
681    )+};
682}
683integers!(u8: small_sum, i16: small_sum, i32: narrow_sum, i64: wide_sum);
684
685/// [`Sum`] of integers wrapped to `i64`, with no test of whether a total fits, as static kinds and fixed integers keep their sums. A sum of `i64`s is then one vector add for each item, where `Sum` splits each item into halves to test its total. Narrower items take `Sum`'s own sums, which either always fit or test once for each block.
686macro_rules! wrapped_sums {
687    ($($t:ty: $slice:expr),+) => {$(
688        impl Reduction<$t> for crate::Wrapped<Sum> {
689            type Total = i64;
690            const COMMUTATIVE: bool = true;
691            #[inline(always)]
692            fn unit() -> i64 { 0 }
693            #[inline(always)]
694            fn step(acc: i64, x: $t) -> i64 { acc.wrapping_add(x.into()) }
695            #[inline(always)]
696            fn merge(a: i64, b: i64) -> (i64, bool) { (a.wrapping_add(b), true) }
697            #[inline]
698            fn slice(x: &[$t]) -> (i64, bool) { (($slice)(x), true) }
699            #[inline]
700            fn scan(x: &[$t], start: Option<i64>, out: &mut Vec<i64>) -> bool { signed_cumsum(x, start.unwrap_or(0), out); true }
701        }
702    )+};
703}
704wrapped_sums!(u8: |x| small_sum(x).0, i16: |x| small_sum(x).0, i32: |x| narrow_sum(x).0, i64: |x: &[i64]| folded(x, 0i64, i64::wrapping_add, i64::wrapping_add));
705
706/// The XNOR fold from `!0` of `n` items whose XOR fold is `xor`: their XOR, complemented for an even count. The compiler vectorizes an XOR fold, and no spelling of an XNOR one.
707fn xnor_of<T: std::ops::Not<Output = T>>(xor: T, n: usize) -> T { if n % 2 == 0 { !xor } else { xor } }
708
709/// [`Max`], [`Min`], [`And`], [`Or`] and [`Xor`] for each integer type, and [`Xnor`] for each signed one, from each one's unit and step. No step overflows. A slice folds in one plain loop, which the compiler vectorizes and reorders as it does integer addition; [`Xnor`]'s folds through [`xnor_of`]. A maximum or minimum scans through [`records`]. A bitwise scan takes [`grouped`] with `reorders` set.
710macro_rules! closed {
711    (@one $r:ident, $t:ty, $unit:expr, $step:expr, $scan:ident) => { closed!(@one $r, $t, $unit, $step, $scan, |x: &[$t]| x.iter().copied().fold($unit, $step)); };
712    (@one $r:ident, $t:ty, $unit:expr, $step:expr, $scan:ident, $slice:expr) => {
713        impl Reduction<$t> for $r {
714            type Total = $t;
715            const COMMUTATIVE: bool = true;
716            #[inline(always)]
717            fn unit() -> $t { $unit }
718            #[inline(always)]
719            fn step(acc: $t, x: $t) -> $t { ($step)(acc, x) }
720            #[inline(always)]
721            fn merge(a: $t, b: $t) -> ($t, bool) { (($step)(a, b), true) }
722            #[inline]
723            fn slice(x: &[$t]) -> ($t, bool) { (($slice)(x), true) }
724            #[inline]
725            fn scan(x: &[$t], start: Option<$t>, out: &mut Vec<$t>) -> bool {
726                closed!(@$scan x, start, out, $t, $step);
727                true
728            }
729            #[inline]
730            fn rows(data: &[$t], columns: usize, out: &mut [$t]) -> bool { rows_side_by_side::<$t, Self>(data, columns, out) }
731        }
732    };
733    (@records $x:ident, $start:ident, $out:ident, $t:ty, $step:expr) => { records($x, $start, $out, |v| v, $step, |b: &[$t], t: $t| b.iter().fold(t, |m, &v| ($step)(m, v)) == t) };
734    (@grouped $x:ident, $start:ident, $out:ident, $t:ty, $step:expr) => { grouped($x, $start, $out, |v| v, $step, |t| t, true) };
735    (signed $($t:ty),+) => {$( closed!(@one Xnor, $t, !0, |a: $t, b: $t| !(a ^ b), grouped, |x: &[$t]| xnor_of(x.iter().fold(0, |a: $t, &b| a ^ b), x.len())); )+};
736    ($($t:ty),+) => {$(
737        closed!(@one Max, $t, <$t>::MIN, Ord::max, records);
738        closed!(@one Min, $t, <$t>::MAX, Ord::min, records);
739        closed!(@one And, $t, !0, |a: $t, b: $t| a & b, grouped);
740        closed!(@one Or, $t, 0, |a: $t, b: $t| a | b, grouped);
741        closed!(@one Xor, $t, 0, |a: $t, b: $t| a ^ b, grouped);
742    )+};
743}
744closed!(i16, i32, i64, u8, u16, u32, u64);
745closed!(signed i16, i32, i64);
746/// [`Xnor`] of bytes, totalled at `i16`. Each total lies between -256 and 255, and the XNOR of two bytes is negative.
747impl Reduction<u8> for Xnor {
748    type Total = i16;
749    const COMMUTATIVE: bool = true;
750    #[inline(always)]
751    fn unit() -> i16 { !0 }
752    #[inline(always)]
753    fn step(acc: i16, x: u8) -> i16 { !(acc ^ i16::from(x)) }
754    #[inline(always)]
755    fn merge(a: i16, b: i16) -> (i16, bool) { (!(a ^ b), true) }
756    #[inline]
757    fn slice(x: &[u8]) -> (i16, bool) { (xnor_of(x.iter().fold(0i16, |a, &b| a ^ i16::from(b)), x.len()), true) }
758    #[inline]
759    fn scan(x: &[u8], start: Option<i16>, out: &mut Vec<i16>) -> bool { grouped(x, start, out, i16::from, |a: i16, b: i16| !(a ^ b), |t| t, true); true }
760    #[inline]
761    fn rows(data: &[u8], columns: usize, out: &mut [i16]) -> bool { rows_side_by_side::<u8, Self>(data, columns, out) }
762}
763/// The reductions on Booleans, with one row for each truth table: the reductions that fold by it, its unit, its step and the table itself. Each scan runs through [`Logical`]. [`Max`] and [`Or`] fold Booleans by `or`, and [`Min`] and [`And`] by `and`.
764macro_rules! booleans {
765    ($($($r:ident)|+: $unit:expr, |$a:ident, $b:ident| $step:expr, $table:expr;)+) => {$($(
766        impl Reduction<bool> for $r {
767            type Total = bool;
768            const COMMUTATIVE: bool = true;
769            #[inline(always)]
770            fn unit() -> bool { $unit }
771            #[inline(always)]
772            fn step($a: bool, $b: bool) -> bool { $step }
773            #[inline(always)]
774            fn merge(a: bool, b: bool) -> (bool, bool) { (<Self as Reduction<bool>>::step(a, b), true) }
775            #[inline]
776            fn slice(x: &[bool]) -> (bool, bool) { (folded(x, $unit, <Self as Reduction<bool>>::step, <Self as Reduction<bool>>::step), true) }
777            #[inline]
778            fn scan(x: &[bool], start: Option<bool>, out: &mut Vec<bool>) -> bool {
779                Logical($table).scan(x, start, out);
780                true
781            }
782            #[inline]
783            fn short_lanes(data: &[bool], len: usize, start: Option<bool>, out: &mut Vec<bool>) -> Option<bool> {
784                (1..=64).contains(&len).then(|| { Logical($table).short_lanes(data, len, start, out); true })
785            }
786        }
787    )+)+};
788}
789booleans! {
790    Max | Or: false, |a, b| a | b, [[false, true], [true, true]];
791    Min | And: true, |a, b| a & b, [[false, false], [false, true]];
792    Xor: false, |a, b| a ^ b, [[false, true], [true, false]];
793    Xnor: true, |a, b| a == b, [[true, false], [false, true]];
794}
795
796/// The running folds of `x` from `start` by `step`, a maximum or minimum, reading each item through `from`. Where a float maximum's step keeps a NaN running result, its `from` turns a NaN item into `-∞`: `grouped` combines the items of each group of four with each other before it steps from the total, and a NaN that comes first in one of those combinations, the group's first or third item, would hide the items after it in the group. A block of 256 items that `keeps(block, total)` passes repeats the running result, with no step for each item. Other blocks scan as `grouped` does. On random items, almost every block repeats the running result. After four blocks in a row change the running result, blocks scan with no test until one leaves it unchanged. Their lengths start at 256 items and double up to 16,384. Items that rise steadily then pay for four tests and a few calls of `grouped`. A call for every 256 items added a third to their scan. Random items start such a run about one time in 25. After two blocks, they started one about half the time. The compiler vectorizes each block's test because the block's length isn't fixed. A loop of a fixed length unrolls into one item at a time.
797pub(crate) fn records<T: Copy + PartialEq>(
798    x: &[T], start: Option<T>, out: &mut Vec<T>, from: impl Fn(T) -> T + Copy, step: impl Fn(T, T) -> T + Copy, keeps: impl Fn(&[T], T) -> bool) {
799    let Some(&first) = x.first() else { return };
800    let first = from(first);
801    let (mut total, mut at, mut changes) = (start.map_or(first, |s| step(s, first)), 0, 0);
802    while at < x.len() {
803        let test = changes < 4;
804        let block = &x[at..x.len().min(at + if test { 256 } else { 256 << (changes - 4).min(6) })];
805        at += block.len();
806        if test && keeps(block, total) {
807            out.resize(out.len() + block.len(), total);
808            changes = 0;
809        } else {
810            grouped(block, Some(total), out, from, step, |t| t, false);
811            let last = out[out.len() - 1];
812            (total, changes) = (last, if last == total { 0 } else { changes + 1 });
813        }
814    }
815}
816
817/// Whether every item of `block` is less than `total`. Only such a block repeats a float maximum's running result. A tie, a signed zero or a NaN always goes through the step.
818#[inline(always)]
819pub(crate) fn below<F: PartialOrd + Copy>(block: &[F], total: F) -> bool { block.iter().fold(true, |k, &v| k & (v < total)) }
820
821/// Whether every item of `block` is greater than `total`. Only such a block repeats a float minimum's running result.
822#[inline(always)]
823pub(crate) fn above<F: PartialOrd + Copy>(block: &[F], total: F) -> bool { block.iter().fold(true, |k, &v| k & (v > total)) }
824
825/// The fewest items in each piece of [`fold_pieces`]. A lane of at most this many items always folds whole.
826const PIECE: usize = 1 << 18;
827/// The fewest rows in each piece of [`fold_pieces`]. The partial folds of the lanes that a piece shares with its neighbours then hold at most a 16th of its items, and merge on the calling thread. With 256 rows, an array of fewer rows than that folded on one thread: at 4 threads, 16 rows took 19% less time for `+⌿` on 64×65536 and 32% less for `⌊⌿` on 64×16384, as kmeans folds.
828const ROWS: usize = 16;
829
830/// The fewest items in each piece of [`fold_pieces`] for an array whose rows hold `inner` items: at least [`PIECE`] items and [`ROWS`] rows, in whole `CHUNK`s.
831fn piece_size(inner: usize) -> usize { PIECE.max(ROWS * inner).next_multiple_of(CHUNK) }
832
833/// The fold of the one item `x`, clearing `ok` where it doesn't fit.
834#[inline(always)]
835fn one_of<T, R: Reduction<T>>(x: T, ok: &mut bool) -> R::Total {
836    let (t, fits) = R::one(x);
837    *ok &= fits;
838    t
839}
840
841/// Steps each of `totals` by the matching item of `x`, as far as the shorter goes, and gives whether every fold fits. It loops by index, because column folds ran 1.6 times slower when `strip_by`'s running totals went through zipped slices.
842#[inline(always)]
843fn steps_of<T: Copy, R: Reduction<T>>(totals: &mut [R::Total], x: &[T]) -> bool {
844    let (n, mut fits) = (totals.len().min(x.len()), true);
845    for k in 0..n { let (t, f) = R::checked(totals[k], x[k]); (totals[k], fits) = (t, fits & f) }
846    fits
847}
848
849/// Appends to `out` the fold of each column of the first one, two or three of `rows`, as far as the shorter goes, and gives whether every fold fits. One pass reads those rows, where a pass for each row would also read and write `out` again.
850#[inline(always)]
851fn started<T: Copy, R: Reduction<T>>(out: &mut Vec<R::Total>, rows: &[&[T]]) -> bool {
852    #[inline(always)]
853    fn step<T, R: Reduction<T>>((t, f): (R::Total, bool), x: T) -> (R::Total, bool) { let (u, g) = R::checked(t, x); (u, f & g) }
854    let mut fits = true;
855    match *rows {
856        [] => {}
857        [a] => out.extend(a.iter().map(|&x| {
858            let (t, f) = R::one(x);
859            fits &= f;
860            t
861        })),
862        [a, b] => out.extend(a.iter().zip(b).map(|(&x, &y)| {
863            let (t, f) = step::<T, R>(R::one(x), y);
864            fits &= f;
865            t
866        })),
867        [a, b, c, ..] => out.extend(a.iter().zip(b).zip(c).map(|((&x, &y), &z)| {
868            let (t, f) = step::<T, R>(step::<T, R>(R::one(x), y), z);
869            fits &= f;
870            t
871        })),
872    }
873    fits
874}
875
876/// Appends to `out` the folds of `width` rows, `row(0)` up to `row(width - 1)`, item by item, as far as the shortest row goes, and gives whether every fold fits. One pass folds the first three rows (`started`), and each later row steps those folds while they're still in cache.
877#[inline(always)]
878fn rows_folded<'a, T: Copy + 'a, R: Reduction<T>>(out: &mut Vec<R::Total>, width: usize, row: impl Fn(usize) -> &'a [T]) -> bool {
879    let at = out.len();
880    let mut fits = match width {
881        1 => started::<T, R>(out, &[row(0)]), 2 => started::<T, R>(out, &[row(0), row(1)]), _ => started::<T, R>(out, &[row(0), row(1), row(2)])
882    };
883    for i in 3..width { fits &= steps_of::<T, R>(&mut out[at..], row(i)); }
884    fits
885}
886
887/// Merges each of `totals` with the matching total of `parts`, as far as the shorter goes, and gives whether every result fits. It loops by index, as [`steps_of`] does.
888#[inline(always)]
889fn merges_of<T, R: Reduction<T>>(totals: &mut [R::Total], parts: &[R::Total]) -> bool {
890    let (n, mut fits) = (totals.len().min(parts.len()), true);
891    for k in 0..n { let (t, f) = R::merge(totals[k], parts[k]); (totals[k], fits) = (t, fits & f) }
892    fits
893}
894
895/// The folds `a` and `b` of neighbouring runs combined, clearing `ok` where the result doesn't fit.
896#[inline(always)]
897fn merge_of<T, R: Reduction<T>>(a: R::Total, b: R::Total, ok: &mut bool) -> R::Total {
898    let (t, fits) = R::merge(a, b);
899    *ok &= fits;
900    t
901}
902
903/// The folds `a` and `b` of neighbouring runs combined by `chain`, clearing `ok` where the result doesn't fit.
904#[inline(always)]
905fn chain_of<T, R: Reduction<T>>(a: R::Total, b: R::Total, ok: &mut bool) -> R::Total {
906    let (t, fits) = R::chain(a, b);
907    *ok &= fits;
908    t
909}
910
911/// The length below which a lane of consecutive items runs through [`side_by_side`]'s loop in `rows_scan` and `rows_side_by_side`, [`LANES`] lanes side by side, because a slice form costs more to start than such a lane's items do.
912const SHORT_LANE: usize = 64;
913
914/// How many lanes of fewer than [`SHORT_LANE`] items `rows_scan` and `rows_side_by_side` run side by side. Each lane's running result waits on the step before it. Run one lane at a time, a loop keeps one chain in flight and leaves any overlap to the processor running ahead into the next lane, which changes with where the code sits. Side by side, `LANES` chains are always in flight.
915const LANES: usize = 8;
916
917/// Runs `first` and `step` along the lanes of `len` items in `data`, `B` lanes side by side, and gives whether every result fits. `data` holds a whole number of groups of `B` lanes. `each` gets each running result with its position in `data`, and `last` each lane's last result with the lane's number.
918#[inline(always)]
919fn side_by_side<const B: usize, T: Copy, S: Copy>(
920    data: &[T], len: usize, first: impl Fn(T) -> (S, bool), step: impl Fn(S, T) -> (S, bool), mut each: impl FnMut(usize, S), mut last: impl FnMut(usize, S)
921) -> bool {
922    let mut fits = true;
923    for (g, group) in data.chunks_exact(B * len).enumerate() {
924        let at = g * B * len;
925        let mut ok = [true; B];
926        let mut t: [S; B] = std::array::from_fn(|b| {
927            let (t, f) = first(group[b * len]);
928            ok[b] = f;
929            each(at + b * len, t);
930            t
931        });
932        for i in 1..len {
933            for b in 0..B {
934                // SAFETY: `group` holds `B * len` items, and `b < B` and `i < len`.
935                let (s, f) = step(t[b], unsafe { *group.get_unchecked(b * len + i) });
936                (t[b], ok[b]) = (s, ok[b] & f);
937                each(at + b * len + i, s);
938            }
939        }
940        for (b, &t) in t.iter().enumerate() { last(g * B + b, t) }
941        fits &= ok.into_iter().all(|f| f);
942    }
943    fits
944}
945
946/// An item that sums into any [`Tally`]: a Boolean, as 0 or 1, or an integer.
947pub trait Term: Copy {
948    /// `self` as a term of a sum in `C`. An integer wider than `C` keeps its low bits, which leaves exact every sum that fits `C`.
949    fn term<C: Tally>(self) -> C;
950    /// The sum of `items` in `C`.
951    #[inline]
952    fn terms<C: Tally>(items: &[Self]) -> C { items.iter().fold(C::default(), |s, &x| s.add(x.term())) }
953}
954impl Term for bool {
955    #[inline(always)]
956    fn term<C: Tally>(self) -> C { C::low(self as i64) }
957    /// Each chunk of 192 items counts into byte totals, 16 to a vector. In wasm, a running 64-bit total doesn't vectorise, and 16-bit totals cost about 4 times as much as bytes.
958    #[inline]
959    fn terms<C: Tally>(items: &[bool]) -> C { items.chunks(192).fold(C::default(), |s, c| s.add(C::low(c.iter().fold(0u8, |n, &b| n + u8::from(b)).into()))) }
960}
961macro_rules! terms {
962    ($($t:ty),+) => {$(
963        impl Term for $t {
964            #[inline(always)]
965            fn term<C: Tally>(self) -> C { C::low(self as i64) }
966        }
967    )+};
968}
969terms!(i16, i32, i64, u8);
970
971/// An integer type that sums [`Term`]s, wrapping where a sum doesn't fit: `u8`, `i16`, `i32` or `i64`. A caller picks one that holds every sum it needs. A count of fewer than 256 Booleans fits a byte, which takes an eighth of the memory of an `i64`.
972pub trait Tally: Copy + Default + Send + Sync {
973    /// `self + y`, wrapping.
974    fn add(self, y: Self) -> Self;
975    /// The low bits of `n`.
976    fn low(n: i64) -> Self;
977}
978macro_rules! tallies {
979    ($($t:ty),+) => {$(
980        impl Tally for $t {
981            #[inline(always)]
982            fn add(self, y: $t) -> $t { self.wrapping_add(y) }
983            #[inline(always)]
984            fn low(n: i64) -> $t { n as $t }
985        }
986    )+};
987}
988tallies!(u8, i16, i32, i64);
989
990/// Sums of [`Term`]s as a [`Reduction`], in the type `C`, which the caller picks to hold every sum, so no step checks whether it fits: counts of the trues among Booleans, or sums of integers.
991#[derive(Clone, Copy, Debug)]
992pub struct SumAs<C>(std::marker::PhantomData<C>);
993impl<T: Term, C: Tally> Reduction<T> for SumAs<C> {
994    type Total = C;
995    const COMMUTATIVE: bool = true;
996    #[inline(always)]
997    fn unit() -> C { C::default() }
998    #[inline(always)]
999    fn step(acc: C, x: T) -> C { acc.add(x.term()) }
1000    #[inline(always)]
1001    fn merge(a: C, b: C) -> (C, bool) { (a.add(b), true) }
1002    #[inline]
1003    fn slice(x: &[T]) -> (C, bool) { (T::terms(x), true) }
1004    #[inline]
1005    fn scan(x: &[T], start: Option<C>, out: &mut Vec<C>) -> bool { grouped(x, start, out, T::term, C::add, |t| t, size_of::<C>() < 8); true }
1006}
1007
1008/// The sum of the squares of floats' or complex numbers' magnitudes as a [`Reduction`], with each square computed and totalled in `f64`, where an `f32`'s square neither overflows nor underflows. It folds the items themselves, with no buffer of squares.
1009#[derive(Clone, Copy, Debug)]
1010pub struct Squares;
1011
1012/// `s` plus the square of `x`'s magnitude, computed in `f64`, each multiply-add rounding once where `FUSED` is true. A real item has no imaginary part to add.
1013#[inline(always)]
1014fn add_square<T: Scalar>(s: f64, x: T) -> f64 {
1015    let re = x.re().wide();
1016    let s = re.mul_acc(re, s);
1017    if T::COMPLEX { let im = x.im().wide(); im.mul_acc(im, s) } else { s }
1018}
1019
1020impl<T: Scalar> Reduction<T> for Squares {
1021    type Total = f64;
1022    const COMMUTATIVE: bool = true;
1023    #[inline(always)]
1024    fn unit() -> f64 { 0.0 }
1025    #[inline(always)]
1026    fn step(acc: f64, x: T) -> f64 { add_square(acc, x) }
1027    #[inline(always)]
1028    fn merge(a: f64, b: f64) -> (f64, bool) { (a + b, true) }
1029    #[inline]
1030    fn slice(x: &[T]) -> (f64, bool) { (folded(x, 0.0, add_square, |a, b| a + b), true) }
1031    #[inline]
1032    fn scan(x: &[T], start: Option<f64>, out: &mut Vec<f64>) -> bool { grouped(x, start, out, |v| add_square(0.0, v), |a, b| a + b, |t| t, false); true }
1033}
1034
1035/// The fold by `merge` from `unit` of the pairs of `x`, each combined into a total by `pair`, and then of the item left over where `x` has an odd length, combined by `last`.
1036#[inline(always)]
1037fn pairwise<A: Copy, S: Copy>(x: &[A], unit: S, pair: impl Fn(S, A, A) -> S, merge: impl Fn(S, S) -> S, last: impl Fn(S, A) -> S) -> S {
1038    let (pairs, rest) = x.as_chunks::<2>();
1039    let total = folded(pairs, unit, |s, [a, b]| pair(s, a, b), merge);
1040    rest.first().map_or(total, |&z| last(total, z))
1041}
1042
1043/// The product of the pairs' ratios of `x` by `divide` and `times`, with whether every running product and the result stay inside the normal range. A product that falls below that range stays out of it, or comes back with fewer significant bits, so each step keeps the least `size` of the running products. A product that overflows stays infinite or turns NaN, and a NaN stays NaN, so `finite` catches both in the result.
1044#[inline(always)]
1045fn quotient<T: Copy, R: Native>(
1046    x: &[T], one: T, divide: impl Fn(T, T) -> T, times: impl Fn(T, T) -> T + Copy, size: impl Fn(T) -> R + Copy, finite: impl Fn(T) -> bool) -> (T, bool) {
1047    let step = move |(p, low): (T, R), q: T| { let r = times(p, q); (r, low.min(size(r))) };
1048    let (total, low) = pairwise(x, (one, R::one()), |s, a, b| step(s, divide(a, b)), move |s, (q, l)| {
1049        let (r, m) = step(s, q);
1050        (r, m.min(l))
1051    }, step);
1052    (total, low >= R::min_positive_value() && finite(total))
1053}
1054
1055/// The sum by `add` and `merge` of the items of `x` at even positions, less the sum of those at odd positions. There is an even number of running results, so each one takes the items of one parity.
1056#[inline(always)]
1057fn alternated<A: Copy, S: Copy + std::ops::Sub<Output = S>>(x: &[A], unit: S, add: impl Fn(S, A) -> S + Copy, merge: impl Fn(S, S) -> S) -> S {
1058    let merge = &merge;
1059    running_results(x, unit, add, merge, move |parts, rest| {
1060        let ((pairs, _), (tail, last)) = (parts.as_chunks::<2>(), rest.as_chunks::<2>());
1061        let (even, odd) = pairs.iter().fold((unit, unit), |(e, o), &[p, q]| (merge(e, p), merge(o, q)));
1062        let (even, odd) = tail.iter().fold((even, odd), |(e, o), &[p, q]| (add(e, p), add(o, q)));
1063        last.first().map_or(even, |&z| add(even, z)) - odd
1064    })
1065}
1066
1067/// The running folds of `x` by `R`, each item taken in turn by `checked` from `start` or `R`'s identity, appended to `out`, and whether every one fits.
1068fn checked_steps<T: Copy, R: Reduction<T>>(x: &[T], start: Option<R::Total>, out: &mut Vec<R::Total>) -> bool {
1069    let (mut total, mut fits) = (start.unwrap_or_else(R::identity), true);
1070    out.extend(x.iter().map(|&v| {
1071        let ok;
1072        (total, ok) = R::checked(total, v);
1073        fits &= ok;
1074        total
1075    }));
1076    fits
1077}
1078
1079/// The right fold by the inverse of a reduction: `-` for [`Sum`], and `÷` for [`ScaledProduct`] on floats and [`Product`] on complex numbers. `x0 - (x1 - (x2 - …))` is the sum of the items at even positions less the sum of those at odd positions, and `x0 ÷ (x1 ÷ (x2 ÷ …))` is the quotient of their products. A total holds that sum or quotient for the items folded so far, with whether their number is odd. `step` takes an item at an odd position away, and `merge` takes the later run's total away where the earlier run's count is odd, each through [`Inverse`]. Every fold that takes a [`Reduction`] folds it, as [`along`], [`fold_into`], [`along_mapped`] and [`fold_pieces`] do. [`Totals`] stores its folds without the parity.
1080#[derive(Clone, Copy, Debug, Default)]
1081pub struct Alternated<R>(std::marker::PhantomData<R>);
1082
1083/// A reduction whose step can be undone, as [`Alternated`] folds by it: [`Sum`] subtracts, and [`ScaledProduct`] and [`Product`] divide. An integer difference fits only within `i64`, a complex quotient only inside the normal range, and a float quotient only where no item, or product of the running mantissa and an item, leaves the normal floats. An alternated fold can then fail where the right fold taken item by item, or its exact result, would fit.
1084pub trait Inverse<T>: Reduction<T> {
1085    /// `acc` with the item `x` taken away, and whether it fits.
1086    fn inverse(acc: Self::Total, x: T) -> (Self::Total, bool);
1087    /// The fold `a` with the fold `b` of the next run taken away, and whether it fits.
1088    fn undo(a: Self::Total, b: Self::Total) -> (Self::Total, bool);
1089    /// Whether a running total fits, as every one does apart from a float quotient outside the normal range.
1090    #[inline(always)]
1091    fn fits(_: Self::Total) -> bool { true }
1092    /// The right fold of the slice `x` by the inverse, and whether it fits: for sums through a sum at each parity (`alternated`), and for products through the pairs' quotients (`pairwise`).
1093    fn alternating(x: &[T]) -> (Self::Total, bool);
1094}
1095
1096impl<T: Copy, R: Inverse<T>> Reduction<T> for Alternated<R> {
1097    type Total = (R::Total, bool);
1098    #[inline(always)]
1099    fn unit() -> Self::Total { (R::unit(), false) }
1100    #[inline(always)]
1101    fn identity() -> Self::Total { (R::identity(), false) }
1102    #[inline(always)]
1103    fn step((s, odd): Self::Total, x: T) -> Self::Total { (if odd { R::inverse(s, x).0 } else { R::step(s, x) }, !odd) }
1104    #[inline(always)]
1105    fn checked((s, odd): Self::Total, x: T) -> (Self::Total, bool) {
1106        let (t, ok) = if odd { R::inverse(s, x) } else { R::checked(s, x) };
1107        ((t, !odd), ok && R::fits(t))
1108    }
1109    #[inline(always)]
1110    fn merge((a, odd): Self::Total, (b, more): Self::Total) -> (Self::Total, bool) {
1111        let (t, ok) = if odd { R::undo(a, b) } else { R::merge(a, b) };
1112        ((t, odd ^ more), ok && R::fits(t))
1113    }
1114    #[inline]
1115    fn slice(x: &[T]) -> (Self::Total, bool) { let (t, ok) = R::alternating(x); ((t, x.len() % 2 == 1), ok) }
1116}
1117
1118impl<F: Native> Inverse<F> for Sum {
1119    #[inline(always)]
1120    fn inverse(acc: F, x: F) -> (F, bool) { (acc.algebraic_add(-x), true) }
1121    #[inline(always)]
1122    fn undo(a: F, b: F) -> (F, bool) { (a.algebraic_add(-b), true) }
1123    #[inline]
1124    fn alternating(x: &[F]) -> (F, bool) { (alternated(x, F::zero(), |s, v| s + v, |a, b| a + b), true) }
1125}
1126
1127impl<F: Native> Inverse<Complex<F>> for Sum {
1128    #[inline(always)]
1129    fn inverse(acc: Complex<F>, x: Complex<F>) -> (Complex<F>, bool) { (<Sum as Reduction<Complex<F>>>::step(acc, -x), true) }
1130    #[inline(always)]
1131    fn undo(a: Complex<F>, b: Complex<F>) -> (Complex<F>, bool) { (<Sum as Reduction<Complex<F>>>::step(a, -b), true) }
1132    #[inline]
1133    fn alternating(x: &[Complex<F>]) -> (Complex<F>, bool) { (alternated(x, Complex::new(F::zero(), F::zero()), |s, v| s + v, |a, b| a + b), true) }
1134}
1135
1136/// [`Sum`]'s inverse on integers, which subtracts at `i64`. A slice sums the items at even positions less those at odd positions, which is exact: each run of `$run` items, an even number, sums at `$acc`, where it can't overflow, and the runs total at `i128`. Bytes and 16-bit integers sum at `i32`, which fits twice as many in each vector as `i64`. Separate running sums for each parity take less time than `pairwise`, which must split each pair out of the vector lanes.
1137macro_rules! alternated_integers {
1138    ($($t:ty => $acc:ty, $run:expr);+) => {$(
1139        impl Inverse<$t> for Sum {
1140            #[inline(always)]
1141            fn inverse(acc: i64, x: $t) -> (i64, bool) { let (t, over) = acc.overflowing_sub(i64::from(x)); (t, !over) }
1142            #[inline(always)]
1143            fn undo(a: i64, b: i64) -> (i64, bool) { let (t, over) = a.overflowing_sub(b); (t, !over) }
1144            #[inline]
1145            fn alternating(x: &[$t]) -> (i64, bool) {
1146                let run = |c: &[$t]| i128::from(alternated(c, 0 as $acc, |s, v| s + <$acc>::from(v), |s, t| s + t));
1147                let total: i128 = x.chunks($run).map(run).sum();
1148                (total as i64, i64::try_from(total).is_ok())
1149            }
1150        }
1151    )+};
1152}
1153alternated_integers!(i16 => i32, 1 << 15; u8 => i32, 1 << 15; i32 => i64, 1 << 31; i64 => i128, 1 << 31);
1154
1155/// Whether a quotient lies inside the normal range of floats, where [`Product`]'s inverse on complex numbers fits.
1156#[inline(always)]
1157fn normal<F: Native>(q: F) -> bool { q.abs() >= F::min_positive_value() && q.is_finite() }
1158
1159/// A product of floats held as `m × 2^e`, a mantissa `m` and a whole exponent `e` kept apart, so that the product never leaves the normal floats however many items it takes. A step leaves the mantissa where it is while its magnitude stays inside 2^±511 (2^±63 in `f32`), half the floats' exponents, so that the product of two mantissas stays normal, and moves the mantissa's exponent into `e` once it leaves. `normal` says whether every step so far was a nonzero normal float. [`ScaledProduct`] folds into it. The exponent sums exactly up to 2^53 in `f64` and 2^24 in `f32`, which a product that ends inside the floats' range never nears.
1160#[derive(Clone, Copy, Debug)]
1161pub struct Scaled<F> { m: F, e: F, normal: bool }
1162
1163impl<F: Native> Scaled<F> {
1164    /// `p × 2^e`, with `p`'s exponent moved into `e` where `p`'s magnitude lies outside 2^±511 (2^±63 in `f32`), still `normal` where `p` is a nonzero normal float.
1165    #[inline(always)]
1166    fn of(p: F, e: F, normal: bool) -> Self {
1167        let (lo, hi) = if F::WIDE { (1.4916681462400413e-154, 6.703903964971299e153) } else { (1.0842021724855044e-19, 9.223372036854776e18) };
1168        if p.abs() >= F::narrow(lo) && p.abs() <= F::narrow(hi) { Self { m: p, e, normal } } else { Self::rescaled(p, e, normal) }
1169    }
1170    /// `p × 2^e` with all of `p`'s exponent moved into `e`, its mantissa from 0.75 to 1.5 in magnitude, still `normal` where `p` is a nonzero normal float.
1171    #[inline]
1172    fn rescaled(p: F, e: F, normal: bool) -> Self {
1173        let (m, f) = p.abs().split();
1174        Self { m: if p < F::zero() { -m } else { m }, e: e + f, normal: normal & p.abs().positive_normal() }
1175    }
1176    /// This product times `q`.
1177    #[inline(always)]
1178    fn times(self, q: F) -> Self { Self::of(self.m * q, self.e, self.normal) }
1179    /// This product divided by `q`.
1180    #[inline(always)]
1181    fn over(self, q: F) -> Self { Self::of(self.m / q, self.e, self.normal) }
1182    /// This product times the product `b`.
1183    #[inline(always)]
1184    fn joined(self, b: Self) -> Self { Self::of(self.m * b.m, self.e + b.e, self.normal & b.normal) }
1185    /// This product divided by the product `b`.
1186    #[inline(always)]
1187    fn undone(self, b: Self) -> Self { Self::of(self.m / b.m, self.e - b.e, self.normal & b.normal) }
1188    /// The float nearest the product: infinite or zero where it lies past the floats' range, and rounded at most once, where it lies below the normal floats.
1189    #[inline]
1190    pub fn value(self) -> F {
1191        let Self { m, e, .. } = Self::rescaled(self.m, self.e, true);
1192        let far = F::narrow(if F::WIDE { 2040.0 } else { 250.0 });
1193        crate::float::pow2_times::<F, { crate::FUSED }>(m, e.max(-far).min(far))
1194    }
1195}
1196
1197/// The product of floats with its exponent kept apart, as [`Scaled`] holds it, so that a running product can't overflow or underflow where the whole product doesn't. A step fails only where an item, or a step's product, isn't a nonzero normal float: on zeros, infinities and NaNs, on subnormals, and on items so large or small that a mantissa times them leaves the normal floats. The exact fold takes those. [`Alternated`] folds `÷` on floats by it.
1198#[derive(Clone, Copy, Debug, Default)]
1199pub struct ScaledProduct;
1200
1201impl<F: Native> Reduction<F> for ScaledProduct {
1202    type Total = Scaled<F>;
1203    const COMMUTATIVE: bool = true;
1204    #[inline(always)]
1205    fn unit() -> Scaled<F> { Scaled { m: F::one(), e: F::zero(), normal: true } }
1206    #[inline(always)]
1207    fn step(acc: Scaled<F>, x: F) -> Scaled<F> { acc.times(x) }
1208    #[inline(always)]
1209    fn checked(acc: Scaled<F>, x: F) -> (Scaled<F>, bool) { let t = acc.times(x); (t, t.normal) }
1210    #[inline(always)]
1211    fn merge(a: Scaled<F>, b: Scaled<F>) -> (Scaled<F>, bool) { let t = a.joined(b); (t, t.normal) }
1212}
1213
1214impl<F: Native> Inverse<F> for ScaledProduct {
1215    #[inline(always)]
1216    fn inverse(acc: Scaled<F>, x: F) -> (Scaled<F>, bool) { let t = acc.over(x); (t, t.normal) }
1217    #[inline(always)]
1218    fn undo(a: Scaled<F>, b: Scaled<F>) -> (Scaled<F>, bool) { let t = a.undone(b); (t, t.normal) }
1219    #[inline]
1220    fn alternating(x: &[F]) -> (Scaled<F>, bool) {
1221        // The quotient of plain floats vectorizes, so it goes first. One that leaves the normal floats runs again with its exponent apart.
1222        let (q, fits) = quotient(x, F::one(), |a, b| a / b, F::algebraic_mul, F::abs, F::is_finite);
1223        let t = if fits { Scaled::of(q, F::zero(), true) }
1224        else { pairwise(x, <Self as Reduction<F>>::unit(), |s, a, b| s.times(a / b), Scaled::joined, Scaled::times) };
1225        (t, t.normal)
1226    }
1227}
1228
1229/// The product of two complex numbers, as the quotients of [`Product`]'s inverse on complex numbers take it.
1230#[inline(always)]
1231fn complex_times<F: Native>(p: Complex<F>, q: Complex<F>) -> Complex<F> {
1232    Complex::new(p.re.algebraic_mul(q.re).algebraic_add(-p.im.algebraic_mul(q.im)), p.re.algebraic_mul(q.im).algebraic_add(p.im.algebraic_mul(q.re)))
1233}
1234
1235/// The larger magnitude of a complex number's parts, which [`Product`]'s inverse on complex numbers keeps inside the normal range.
1236#[inline(always)]
1237fn complex_size<F: Native>(p: Complex<F>) -> F { p.re.abs().max(p.im.abs()) }
1238
1239/// [`Product`]'s inverse on complex numbers, which divides as [`crate::divide`] does.
1240impl<F: Native> Inverse<Complex<F>> for Product {
1241    #[inline(always)]
1242    fn inverse(acc: Complex<F>, x: Complex<F>) -> (Complex<F>, bool) { (crate::divide(acc, x), true) }
1243    #[inline(always)]
1244    fn undo(a: Complex<F>, b: Complex<F>) -> (Complex<F>, bool) { (crate::divide(a, b), true) }
1245    #[inline(always)]
1246    fn fits(q: Complex<F>) -> bool { normal(complex_size(q)) }
1247    #[inline]
1248    fn alternating(x: &[Complex<F>]) -> (Complex<F>, bool) {
1249        quotient(x, Complex::new(F::one(), F::zero()), crate::divide, complex_times, complex_size, |p| p.re.is_finite() & p.im.is_finite())
1250    }
1251}
1252
1253/// Runs `$body` with the type `$t` standing for the reduction that `$r` (a [`Reduce`]) names.
1254#[macro_export]
1255macro_rules! with_reduce {
1256    ($r:expr, |$t:ident| $body:expr) => {
1257        match $r {
1258            $crate::reduce::Reduce::Sum => { type $t = $crate::reduce::Sum; $body }
1259            $crate::reduce::Reduce::Product => { type $t = $crate::reduce::Product; $body }
1260            $crate::reduce::Reduce::Max => { type $t = $crate::reduce::Max; $body }
1261            $crate::reduce::Reduce::Min => { type $t = $crate::reduce::Min; $body }
1262            $crate::reduce::Reduce::And => { type $t = $crate::reduce::And; $body }
1263            $crate::reduce::Reduce::Or => { type $t = $crate::reduce::Or; $body }
1264            $crate::reduce::Reduce::Xor => { type $t = $crate::reduce::Xor; $body }
1265            $crate::reduce::Reduce::Xnor => { type $t = $crate::reduce::Xnor; $body }
1266        }
1267    };
1268}
1269
1270// Shared with rustymath-kernels, whose crate prelude imports the width-dispatch macros.
1271#[doc(hidden)]
1272#[macro_export]
1273macro_rules! native_reduction {
1274    (@inverse $kind:expr, $dtype:expr, |$R:ident, $T:ident| $body:expr) => {{
1275        use $crate::buffer::{DType, FloatWidth};
1276        use $crate::reduce::{Alternated, Product, Reduce, ScaledProduct, Sum};
1277        match ($dtype, $kind) {
1278            (DType::Integer(w), Reduce::Sum) => with_width!(w, I => $crate::native_reduction!(@at Alternated<Sum>, I, |$R, $T| $body)),
1279            (DType::Float(FloatWidth::F16 | FloatWidth::F32), Reduce::Sum) => $crate::native_reduction!(@at Alternated<Sum>, f32, |$R, $T| $body),
1280            (DType::Float(FloatWidth::F64), Reduce::Sum) => $crate::native_reduction!(@at Alternated<Sum>, f64, |$R, $T| $body),
1281            (DType::Float(FloatWidth::F16 | FloatWidth::F32), Reduce::Product) => $crate::native_reduction!(@at Alternated<ScaledProduct>, f32, |$R, $T| $body),
1282            (DType::Float(FloatWidth::F64), Reduce::Product) => $crate::native_reduction!(@at Alternated<ScaledProduct>, f64, |$R, $T| $body),
1283            (DType::Complex(w), Reduce::Sum) => with_complex_width!(w, C => $crate::native_reduction!(@at Alternated<Sum>, C, |$R, $T| $body)),
1284            (DType::Complex(w), Reduce::Product) => with_complex_width!(w, C => $crate::native_reduction!(@at Alternated<Product>, C, |$R, $T| $body)),
1285            _ => None,
1286        }
1287    }};
1288    (@at $r:ty, $t:ty, |$R:ident, $T:ident| $body:expr) => {{ type $R = $r; type $T = $t; $body }};
1289    ($kind:expr, $dtype:expr, $checked:expr, |$R:ident, $T:ident| $body:expr) => {{
1290        use $crate::buffer::DType;
1291        use $crate::reduce::Reduce;
1292        match ($dtype, $kind) {
1293            (DType::Boolean, Reduce::Sum) => $crate::native_reduction!(@at $crate::reduce::SumAs<i64>, bool, |$R, $T| $body),
1294            (DType::Boolean, Reduce::Product | Reduce::And | Reduce::Min) => $crate::native_reduction!(@at $crate::reduce::And, bool, |$R, $T| $body),
1295            (DType::Boolean, Reduce::Or | Reduce::Max) => $crate::native_reduction!(@at $crate::reduce::Or, bool, |$R, $T| $body),
1296            (DType::Boolean, Reduce::Xor) => $crate::native_reduction!(@at $crate::reduce::Xor, bool, |$R, $T| $body),
1297            (DType::Boolean, Reduce::Xnor) => $crate::native_reduction!(@at $crate::reduce::Xnor, bool, |$R, $T| $body),
1298            (DType::Integer(w), Reduce::Sum) if !$checked => with_width!(w, I => $crate::native_reduction!(@at $crate::Wrapped<$crate::reduce::Sum>, I, |$R, $T| $body)),
1299            (DType::Integer(w), Reduce::Sum) => with_width!(w, I => $crate::native_reduction!(@at $crate::reduce::Sum, I, |$R, $T| $body)),
1300            (DType::Integer(w), Reduce::Product) if !$checked => with_width!(w, I => $crate::native_reduction!(@at $crate::Wrapped<$crate::reduce::Product>, I, |$R, $T| $body)),
1301            (DType::Integer(w), Reduce::Product) => with_width!(w, I => $crate::native_reduction!(@at $crate::reduce::Product, I, |$R, $T| $body)),
1302            (DType::Integer(w), Reduce::Max) => with_width!(w, I => $crate::native_reduction!(@at $crate::reduce::Max, I, |$R, $T| $body)),
1303            (DType::Integer(w), Reduce::Min) => with_width!(w, I => $crate::native_reduction!(@at $crate::reduce::Min, I, |$R, $T| $body)),
1304            (DType::Integer(w), Reduce::And) => with_width!(w, I => $crate::native_reduction!(@at $crate::reduce::And, I, |$R, $T| $body)),
1305            (DType::Integer(w), Reduce::Or) => with_width!(w, I => $crate::native_reduction!(@at $crate::reduce::Or, I, |$R, $T| $body)),
1306            (DType::Integer(w), Reduce::Xor) => with_width!(w, I => $crate::native_reduction!(@at $crate::reduce::Xor, I, |$R, $T| $body)),
1307            (DType::Integer(w), Reduce::Xnor) => with_width!(w, I => $crate::native_reduction!(@at $crate::reduce::Xnor, I, |$R, $T| $body)),
1308            (DType::Float(w), Reduce::Sum) => with_float_width!(w, F => $crate::native_reduction!(@at $crate::reduce::Sum, F, |$R, $T| $body)),
1309            (DType::Float(w), Reduce::Product) => with_float_width!(w, F => $crate::native_reduction!(@at $crate::reduce::Product, F, |$R, $T| $body)),
1310            (DType::Float(w), Reduce::Max) => with_float_width!(w, F => $crate::native_reduction!(@at $crate::reduce::Max, F, |$R, $T| $body)),
1311            (DType::Float(w), Reduce::Min) => with_float_width!(w, F => $crate::native_reduction!(@at $crate::reduce::Min, F, |$R, $T| $body)),
1312            (DType::Complex(w), Reduce::Sum) => with_complex_width!(w, C => $crate::native_reduction!(@at $crate::reduce::Sum, C, |$R, $T| $body)),
1313            (DType::Complex(w), Reduce::Product) => with_complex_width!(w, C => $crate::native_reduction!(@at $crate::reduce::Product, C, |$R, $T| $body)),
1314            _ => None,
1315        }
1316    }};
1317}
1318pub use crate::native_reduction;
1319
1320macro_rules! wrapped_products {
1321    ($($t:ty),+) => {$(
1322        impl Reduction<$t> for crate::Wrapped<Product> {
1323            type Total = i64;
1324            const COMMUTATIVE: bool = true;
1325            fn unit() -> i64 { 1 }
1326            fn step(total: i64, x: $t) -> i64 { total.wrapping_mul(x.into()) }
1327            fn merge(x: i64, y: i64) -> (i64, bool) { (x.wrapping_mul(y), true) }
1328            fn slice(x: &[$t]) -> (i64, bool) { (folded(x, 1i64, |total, x| total.wrapping_mul(x.into()), i64::wrapping_mul), true) }
1329            fn scan(x: &[$t], start: Option<i64>, out: &mut Vec<i64>) -> bool {
1330                grouped(x, start, out, |x| x.into(), i64::wrapping_mul, |x| x, true);
1331                true
1332            }
1333        }
1334    )+};
1335}
1336wrapped_products!(u8, i16, i32, i64);
1337
1338mod along;
1339mod pieces;
1340mod run;
1341mod scans;
1342mod sliding;
1343pub use {along::*, pieces::*, run::*, scans::*, sliding::*};