/*!
Aggregation / summarisation of information in branches of bisection trees.
*/

pub use crate::bounds::Bounds;
use crate::types::*;

/// Trait for aggregating information about a branch of a [bisection tree][super::BT].
///
/// Currently [`Bounds`] is the only provided aggregator.
/// It keeps track of upper and lower bounds of a function representeed by the `BT` by
/// summing [`Bounds`] produced by [`LocalAnalysis`][super::support::LocalAnalysis] of the
/// [`Support`][super::support::Support]s of the data stored in the tree.
/// For the `Bounds` aggregator:
///  * [`Self::aggregate`] sums input bounds to the current bound. This provides a conservative
///    estimate of the upper and lower bounds of a sum of functions.
///  * [`Self::summarise`] takes the maximum of the input bounds. This calculates the bounds
///    of a function on a greater domain from bounds on subdomains
///    (in practise [`Cube`][crate::sets::Cube]s).
///
pub trait Aggregator: Clone + Sync + Send + 'static + std::fmt::Debug {
    /// Aggregate a new data to current state.
    fn aggregate<I>(&mut self, aggregates: I)
    where
        I: Iterator<Item = Self>;

    /// Summarise several other aggregators, resetting current state.
    fn summarise<'a, I>(&'a mut self, aggregates: I)
    where
        I: Iterator<Item = &'a Self>;

    /// Create a new “empty” aggregate data.
    fn new() -> Self;
}

/// An [`Aggregator`] that doesn't aggregate anything.
#[derive(Clone, Debug)]
pub struct NullAggregator;

impl Aggregator for NullAggregator {
    fn aggregate<I>(&mut self, _aggregates: I)
    where
        I: Iterator<Item = Self>,
    {
    }

    fn summarise<'a, I>(&'a mut self, _aggregates: I)
    where
        I: Iterator<Item = &'a Self>,
    {
    }

    fn new() -> Self {
        NullAggregator
    }
}

impl<F: Float> Aggregator for Bounds<F> {
    #[inline]
    fn aggregate<I>(&mut self, aggregates: I)
    where
        I: Iterator<Item = Self>,
    {
        *self = aggregates.fold(*self, |a, b| a + b);
    }

    #[inline]
    fn summarise<'a, I>(&'a mut self, mut aggregates: I)
    where
        I: Iterator<Item = &'a Self>,
    {
        *self = match aggregates.next() {
            None => Bounds(F::ZERO, F::ZERO), // No parts in this cube; the function is zero
            Some(&bounds) => aggregates.fold(bounds, |a, b| a.common(b)),
        }
    }

    #[inline]
    fn new() -> Self {
        Bounds(F::ZERO, F::ZERO)
    }
}
