/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Lachlan Dowding
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package permafrost.tundra.math;

/**
 * Class for incrementally calculating the mean and standard deviation.
 */
public class NormalDistributionEstimator {
    protected long count;
    protected double mean, sq, minimum, maximum;
    protected String unit = "";

    /**
     * Constructs a new estimator object.
     */
    public NormalDistributionEstimator() {
        reset();
    }

    /**
     * Constructs a new estimator object.
     *
     * @param unit The unit of measurement related to the measured samples.
     */
    public NormalDistributionEstimator(String unit) {
        this();
        this.unit = unit;
    }

    /**
     * Constructs a new estimator object seeded with the given samples.
     *
     * @param samples One or more initial samples to seed the estimator with.
     */
    public NormalDistributionEstimator(double... samples) {
        this();
        add(samples);
    }

    /**
     * Constructs a new estimator object seeded with the given samples.
     *
     * @param unit    The unit of measurement related to the measured samples.
     * @param samples One or more initial samples to seed the estimator with.
     */
    public NormalDistributionEstimator(String unit, double... samples) {
        this(unit);
        add(samples);
    }

    /**
     * Constructs a new estimator object seeded with the given collection of samples.
     *
     * @param samples An initial collection of samples to seed the estimator with.
     */
    public NormalDistributionEstimator(java.util.Collection<Double> samples) {
        this();
        add(samples);
    }

    /**
     * Constructs a new estimator object seeded with the given collection of samples.
     *
     * @param unit    The unit of measurement related to the measured samples.
     * @param samples An initial collection of samples to seed the estimator with.
     */
    public NormalDistributionEstimator(String unit, java.util.Collection<Double> samples) {
        this(unit);
        add(samples);
    }

    /**
     * Adds a new sample to the set of samples used for estimating the standard deviation.
     *
     * @param sample The sample to be added.
     * @return The estimator object itself, to support method chaining.
     */
    public synchronized final NormalDistributionEstimator add(double sample) {
        count++;
        if (count == 1) {
            mean = minimum = maximum = sample;
            sq = 0.0;
        } else {
            double previousMean = mean;
            mean = previousMean + ((sample - previousMean) / count);
            sq = sq + (sample - previousMean) * (sample - mean);

            if (minimum > sample) minimum = sample;
            if (maximum < sample) maximum = sample;
        }
        return this;
    }

    /**
     * Adds one or more samples to the set of samples used for estimating the standard deviation.
     *
     * @param samples One or more samples to be added.
     * @return The estimator object itself, to support method chaining.
     */
    public final NormalDistributionEstimator add(double... samples) {
        for (double sample : samples) {
            add(sample);
        }
        return this;
    }

    /**
     * Adds a collection of samples to the set of samples used for estimating the standard deviation.
     *
     * @param samples A collection of samples to be added.
     * @return The estimator object itself, to support method chaining.
     */
    public final NormalDistributionEstimator add(java.util.Collection<Double> samples) {
        for (double sample : samples) {
            add(sample);
        }
        return this;
    }

    /**
     * Resets the estimator back to a set of zero samples.
     *
     * @return The Estimator object itself, to support method chaining.
     */
    public synchronized NormalDistributionEstimator reset() {
        count = 0;
        mean = 0.0;
        sq = 0.0;
        minimum = 0.0;
        maximum = 0.0;
        return this;
    }

    /**
     * Returns the number of samples seen by this estimator.
     *
     * @return The number of samples seen by this estimator.
     */
    public synchronized long getCount() { return count; }

    /**
     * Returns the mean of the samples.
     *
     * @return The mean of the samples.
     */
    public synchronized double getMean() { return mean; }

    /**
     * Returns the minimum of the samples.
     *
     * @return The minimum of the samples.
     */
    public synchronized double getMinimum() {
        if (count == 0) throw new IllegalStateException("minimum value is not available when sample count is zero");
        return minimum;
    }

    /**
     * Returns the maximum of the samples.
     *
     * @return The maximum of the samples.
     */
    public synchronized double getMaximum() {
        if (count == 0) throw new IllegalStateException("maximum value is not available when sample count is zero");
        return maximum;
    }

    /**
     * Returns the variance estimate for the current set of samples.
     *
     * @return The variance estimate.
     */
    public synchronized double getVariance() {
        return count > 1 ? sq / (count - 1) : 0.0;
    }

    /**
     * Returns the standard deviation estimate for the current set of samples.
     *
     * @return The standard deviation estimate.
     */
    public double getStandardDeviation() {
        return Math.sqrt(getVariance());
    }

    /**
     * Returns the unit of measure related to the measured samples.
     *
     * @return The unit of measure.
     */
    public String getUnit() {
        return unit;
    }

    /**
     * Returns a string-based representation of the mean, standard deviation and number of samples for this estimator.
     *
     * @return String-based representation of this estimator.
     */
    @Override
    public String toString() {
        double mean, stdev, min, max;
        long count;
        String unit = getUnit();

        // thread synchronized to make sure all values are consistent
        synchronized (this) {
            mean = getMean();
            stdev = getStandardDeviation();
            min = getMinimum();
            max = getMaximum();
            count = getCount();
        }

        String output;

        if (unit == null || unit.equals("")) {
            output = String.format("average = %.3f, standard deviation = %.3f, minimum = %.3f, maximum = %.3f, sample count = %d", mean, stdev, min, max, count);
        } else {
            output = String.format("average = %.3f %s, standard deviation = %.3f %s, minimum = %.3f %s, maximum = %.3f %s, sample count = %d", mean, unit, stdev, unit, min, unit, max, unit, count);
        }

        return output;
    }
}

@@ -1175,16 +1175,46 @@
 .math;%0A%0A
+import java.util.Collection;%0A%0A
 /**%0A * C
@@ -2688,26 +2688,16 @@
 timator(
-java.util.
 Collecti
@@ -3086,26 +3086,16 @@
 g unit, 
-java.util.
 Collecti
@@ -4590,32 +4590,32 @@
 aining.%0A     */%0A
+
     public final
@@ -4647,26 +4647,16 @@
 tor add(
-java.util.
 Collecti

Change NormalDistributionEstimator to use imports statement rather than fully-qualified class names