class ExponentialWelfordIntervalForecaster implements IntervalForecaster {

    @Override
    public IntervalForecast forecast(MetricData metricData, double pointForecast) {

        // https://en.wikipedia.org/wiki/Moving_average#Exponentially_weighted_moving_variance_and_standard_deviation
        // http://people.ds.cam.ac.uk/fanf2/hermes/doc/antiforgery/stats.pdf
        val observed = metricData.getValue();
        val residual = observed - pointForecast;
        val incr = params.getAlpha() * residual;

        // FIXME I believe this belongs here... [WLW]
        this.variance = (1.0 - params.getAlpha()) * (this.variance + residual * incr);

        val stdev = Math.sqrt(variance);
        val weakWidth = params.getWeakSigmas() * stdev;
        val strongWidth = params.getStrongSigmas() * stdev;

        // FIXME ...but this is where it is in the legacy code (and where the unit tests expect it). [WLW]
//        this.variance = (1.0 - params.getAlpha()) * (this.variance + residual * incr);

        return new IntervalForecast(
                pointForecast + strongWidth,
                pointForecast + weakWidth,
                pointForecast - weakWidth,
                pointForecast - strongWidth);
    }

    public  ExponentialWelfordIntervalForecaster();
    public  ExponentialWelfordIntervalForecaster(ExponentialWelfordIntervalForecasterParams params);

}