# statistics.py : compute single-channel central measures in an accumulator
# No copyright, 2009-2021, Garth Zeglin.  This file is explicitly placed in the public domain.

import math

# CircuitPython lacks math.inf, here is a workaround:
try:
    inf_value = math.inf
except AttributeError:
    inf_value = math.exp(10000)


class CentralMeasures:
    def __init__(self):
        """Object to accumulate central measures statistics on a single channel of data."""

        self.samples = 0            # running sum of value^0, i.e., the number of samples
        self.total   = 0            # running sum of value^1, i.e., the accumulated total
        self.squared = 0            # running sum of value^2, i.e., the accumulated sum of squares
        self.minval  = inf_value    # smallest input seen
        self.maxval  = -inf_value   # largest input seen
        self.last = 0               # most recent input

        # computed statistics
        self.average  = 0           # mean value
        self.variance = 0           # square of the standard deviation

        return

    def update(self, value):
        """Apply a new sample to the accumulators and update the computed statistics.
        Returns the computed average and variance as a tuple."""
        self.total   += value
        self.squared += value * value

        if value < self.minval:
            self.minval = value

        if value > self.maxval:
            self.maxval = value

        self.samples += 1
        self.last = value

        # recalculate average and variance
        if self.samples > 0:
            self.average = self.total / self.samples

            if self.samples > 1:
                # The "standard deviation of the sample", which is only correct
                # if the population is normally distributed and a large sample
                # is available, otherwise tends to be too low:
                # self.sigma = math.sqrt((self.samples * self.squared - self.total*self.total) /
                #                         self.samples))

                # Instead compute the "sample standard deviation", an unbiased
                # estimator for the variance.  The standard deviation is the
                # square root of the variance.
                self.variance = ((self.samples * self.squared - self.total*self.total) /
                    (self.samples * (self.samples - 1)))

        return self.average, self.variance
