001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math3.stat.descriptive;
018
019import java.io.Serializable;
020import java.lang.reflect.InvocationTargetException;
021import java.util.Arrays;
022
023import org.apache.commons.math3.exception.MathIllegalArgumentException;
024import org.apache.commons.math3.exception.NullArgumentException;
025import org.apache.commons.math3.exception.MathIllegalStateException;
026import org.apache.commons.math3.exception.util.LocalizedFormats;
027import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
028import org.apache.commons.math3.stat.descriptive.moment.Kurtosis;
029import org.apache.commons.math3.stat.descriptive.moment.Mean;
030import org.apache.commons.math3.stat.descriptive.moment.Skewness;
031import org.apache.commons.math3.stat.descriptive.moment.Variance;
032import org.apache.commons.math3.stat.descriptive.rank.Max;
033import org.apache.commons.math3.stat.descriptive.rank.Min;
034import org.apache.commons.math3.stat.descriptive.rank.Percentile;
035import org.apache.commons.math3.stat.descriptive.summary.Sum;
036import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
037import org.apache.commons.math3.util.MathUtils;
038import org.apache.commons.math3.util.ResizableDoubleArray;
039import org.apache.commons.math3.util.FastMath;
040
041
042/**
043 * Maintains a dataset of values of a single variable and computes descriptive
044 * statistics based on stored data. The {@link #getWindowSize() windowSize}
045 * property sets a limit on the number of values that can be stored in the
046 * dataset.  The default value, INFINITE_WINDOW, puts no limit on the size of
047 * the dataset.  This value should be used with caution, as the backing store
048 * will grow without bound in this case.  For very large datasets,
049 * {@link SummaryStatistics}, which does not store the dataset, should be used
050 * instead of this class. If <code>windowSize</code> is not INFINITE_WINDOW and
051 * more values are added than can be stored in the dataset, new values are
052 * added in a "rolling" manner, with new values replacing the "oldest" values
053 * in the dataset.
054 *
055 * <p>Note: this class is not threadsafe.  Use
056 * {@link SynchronizedDescriptiveStatistics} if concurrent access from multiple
057 * threads is required.</p>
058 *
059 */
060public class DescriptiveStatistics implements StatisticalSummary, Serializable {
061
062    /**
063     * Represents an infinite window size.  When the {@link #getWindowSize()}
064     * returns this value, there is no limit to the number of data values
065     * that can be stored in the dataset.
066     */
067    public static final int INFINITE_WINDOW = -1;
068
069    /** Serialization UID */
070    private static final long serialVersionUID = 4133067267405273064L;
071
072    /** Name of the setQuantile method. */
073    private static final String SET_QUANTILE_METHOD_NAME = "setQuantile";
074
075    /** hold the window size **/
076    protected int windowSize = INFINITE_WINDOW;
077
078    /**
079     *  Stored data values
080     */
081    private ResizableDoubleArray eDA = new ResizableDoubleArray();
082
083    /** Mean statistic implementation - can be reset by setter. */
084    private UnivariateStatistic meanImpl = new Mean();
085
086    /** Geometric mean statistic implementation - can be reset by setter. */
087    private UnivariateStatistic geometricMeanImpl = new GeometricMean();
088
089    /** Kurtosis statistic implementation - can be reset by setter. */
090    private UnivariateStatistic kurtosisImpl = new Kurtosis();
091
092    /** Maximum statistic implementation - can be reset by setter. */
093    private UnivariateStatistic maxImpl = new Max();
094
095    /** Minimum statistic implementation - can be reset by setter. */
096    private UnivariateStatistic minImpl = new Min();
097
098    /** Percentile statistic implementation - can be reset by setter. */
099    private UnivariateStatistic percentileImpl = new Percentile();
100
101    /** Skewness statistic implementation - can be reset by setter. */
102    private UnivariateStatistic skewnessImpl = new Skewness();
103
104    /** Variance statistic implementation - can be reset by setter. */
105    private UnivariateStatistic varianceImpl = new Variance();
106
107    /** Sum of squares statistic implementation - can be reset by setter. */
108    private UnivariateStatistic sumsqImpl = new SumOfSquares();
109
110    /** Sum statistic implementation - can be reset by setter. */
111    private UnivariateStatistic sumImpl = new Sum();
112
113    /**
114     * Construct a DescriptiveStatistics instance with an infinite window
115     */
116    public DescriptiveStatistics() {
117    }
118
119    /**
120     * Construct a DescriptiveStatistics instance with the specified window
121     *
122     * @param window the window size.
123     * @throws MathIllegalArgumentException if window size is less than 1 but
124     * not equal to {@link #INFINITE_WINDOW}
125     */
126    public DescriptiveStatistics(int window) throws MathIllegalArgumentException {
127        setWindowSize(window);
128    }
129
130    /**
131     * Construct a DescriptiveStatistics instance with an infinite window
132     * and the initial data values in double[] initialDoubleArray.
133     * If initialDoubleArray is null, then this constructor corresponds to
134     * DescriptiveStatistics()
135     *
136     * @param initialDoubleArray the initial double[].
137     */
138    public DescriptiveStatistics(double[] initialDoubleArray) {
139        if (initialDoubleArray != null) {
140            eDA = new ResizableDoubleArray(initialDoubleArray);
141        }
142    }
143
144    /**
145     * Copy constructor.  Construct a new DescriptiveStatistics instance that
146     * is a copy of original.
147     *
148     * @param original DescriptiveStatistics instance to copy
149     * @throws NullArgumentException if original is null
150     */
151    public DescriptiveStatistics(DescriptiveStatistics original) throws NullArgumentException {
152        copy(original, this);
153    }
154
155    /**
156     * Adds the value to the dataset. If the dataset is at the maximum size
157     * (i.e., the number of stored elements equals the currently configured
158     * windowSize), the first (oldest) element in the dataset is discarded
159     * to make room for the new value.
160     *
161     * @param v the value to be added
162     */
163    public void addValue(double v) {
164        if (windowSize != INFINITE_WINDOW) {
165            if (getN() == windowSize) {
166                eDA.addElementRolling(v);
167            } else if (getN() < windowSize) {
168                eDA.addElement(v);
169            }
170        } else {
171            eDA.addElement(v);
172        }
173    }
174
175    /**
176     * Removes the most recent value from the dataset.
177     *
178     * @throws MathIllegalStateException if there are no elements stored
179     */
180    public void removeMostRecentValue() throws MathIllegalStateException {
181        try {
182            eDA.discardMostRecentElements(1);
183        } catch (MathIllegalArgumentException ex) {
184            throw new MathIllegalStateException(LocalizedFormats.NO_DATA);
185        }
186    }
187
188    /**
189     * Replaces the most recently stored value with the given value.
190     * There must be at least one element stored to call this method.
191     *
192     * @param v the value to replace the most recent stored value
193     * @return replaced value
194     * @throws MathIllegalStateException if there are no elements stored
195     */
196    public double replaceMostRecentValue(double v) throws MathIllegalStateException {
197        return eDA.substituteMostRecentElement(v);
198    }
199
200    /**
201     * Returns the <a href="http://www.xycoon.com/arithmetic_mean.htm">
202     * arithmetic mean </a> of the available values
203     * @return The mean or Double.NaN if no values have been added.
204     */
205    public double getMean() {
206        return apply(meanImpl);
207    }
208
209    /**
210     * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
211     * geometric mean </a> of the available values
212     * @return The geometricMean, Double.NaN if no values have been added,
213     * or if the product of the available values is less than or equal to 0.
214     */
215    public double getGeometricMean() {
216        return apply(geometricMeanImpl);
217    }
218
219    /**
220     * Returns the (sample) variance of the available values.
221     *
222     * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in
223     * the denominator).  Use {@link #getPopulationVariance()} for the non-bias-corrected
224     * population variance.</p>
225     *
226     * @return The variance, Double.NaN if no values have been added
227     * or 0.0 for a single value set.
228     */
229    public double getVariance() {
230        return apply(varianceImpl);
231    }
232
233    /**
234     * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">
235     * population variance</a> of the available values.
236     *
237     * @return The population variance, Double.NaN if no values have been added,
238     * or 0.0 for a single value set.
239     */
240    public double getPopulationVariance() {
241        return apply(new Variance(false));
242    }
243
244    /**
245     * Returns the standard deviation of the available values.
246     * @return The standard deviation, Double.NaN if no values have been added
247     * or 0.0 for a single value set.
248     */
249    public double getStandardDeviation() {
250        double stdDev = Double.NaN;
251        if (getN() > 0) {
252            if (getN() > 1) {
253                stdDev = FastMath.sqrt(getVariance());
254            } else {
255                stdDev = 0.0;
256            }
257        }
258        return stdDev;
259    }
260
261    /**
262     * Returns the quadratic mean, a.k.a.
263     * <a href="http://mathworld.wolfram.com/Root-Mean-Square.html">
264     * root-mean-square</a> of the available values
265     * @return The quadratic mean or {@code Double.NaN} if no values
266     * have been added.
267     */
268    public double getQuadraticMean() {
269        final long n = getN();
270        return n > 0 ? FastMath.sqrt(getSumsq() / n) : Double.NaN;
271    }
272
273    /**
274     * Returns the skewness of the available values. Skewness is a
275     * measure of the asymmetry of a given distribution.
276     * @return The skewness, Double.NaN if no values have been added
277     * or 0.0 for a value set &lt;=2.
278     */
279    public double getSkewness() {
280        return apply(skewnessImpl);
281    }
282
283    /**
284     * Returns the Kurtosis of the available values. Kurtosis is a
285     * measure of the "peakedness" of a distribution
286     * @return The kurtosis, Double.NaN if no values have been added, or 0.0
287     * for a value set &lt;=3.
288     */
289    public double getKurtosis() {
290        return apply(kurtosisImpl);
291    }
292
293    /**
294     * Returns the maximum of the available values
295     * @return The max or Double.NaN if no values have been added.
296     */
297    public double getMax() {
298        return apply(maxImpl);
299    }
300
301    /**
302    * Returns the minimum of the available values
303    * @return The min or Double.NaN if no values have been added.
304    */
305    public double getMin() {
306        return apply(minImpl);
307    }
308
309    /**
310     * Returns the number of available values
311     * @return The number of available values
312     */
313    public long getN() {
314        return eDA.getNumElements();
315    }
316
317    /**
318     * Returns the sum of the values that have been added to Univariate.
319     * @return The sum or Double.NaN if no values have been added
320     */
321    public double getSum() {
322        return apply(sumImpl);
323    }
324
325    /**
326     * Returns the sum of the squares of the available values.
327     * @return The sum of the squares or Double.NaN if no
328     * values have been added.
329     */
330    public double getSumsq() {
331        return apply(sumsqImpl);
332    }
333
334    /**
335     * Resets all statistics and storage
336     */
337    public void clear() {
338        eDA.clear();
339    }
340
341
342    /**
343     * Returns the maximum number of values that can be stored in the
344     * dataset, or INFINITE_WINDOW (-1) if there is no limit.
345     *
346     * @return The current window size or -1 if its Infinite.
347     */
348    public int getWindowSize() {
349        return windowSize;
350    }
351
352    /**
353     * WindowSize controls the number of values that contribute to the
354     * reported statistics.  For example, if windowSize is set to 3 and the
355     * values {1,2,3,4,5} have been added <strong> in that order</strong> then
356     * the <i>available values</i> are {3,4,5} and all reported statistics will
357     * be based on these values. If {@code windowSize} is decreased as a result
358     * of this call and there are more than the new value of elements in the
359     * current dataset, values from the front of the array are discarded to
360     * reduce the dataset to {@code windowSize} elements.
361     *
362     * @param windowSize sets the size of the window.
363     * @throws MathIllegalArgumentException if window size is less than 1 but
364     * not equal to {@link #INFINITE_WINDOW}
365     */
366    public void setWindowSize(int windowSize) throws MathIllegalArgumentException {
367        if (windowSize < 1 && windowSize != INFINITE_WINDOW) {
368            throw new MathIllegalArgumentException(
369                    LocalizedFormats.NOT_POSITIVE_WINDOW_SIZE, windowSize);
370        }
371
372        this.windowSize = windowSize;
373
374        // We need to check to see if we need to discard elements
375        // from the front of the array.  If the windowSize is less than
376        // the current number of elements.
377        if (windowSize != INFINITE_WINDOW && windowSize < eDA.getNumElements()) {
378            eDA.discardFrontElements(eDA.getNumElements() - windowSize);
379        }
380    }
381
382    /**
383     * Returns the current set of values in an array of double primitives.
384     * The order of addition is preserved.  The returned array is a fresh
385     * copy of the underlying data -- i.e., it is not a reference to the
386     * stored data.
387     *
388     * @return returns the current set of numbers in the order in which they
389     *         were added to this set
390     */
391    public double[] getValues() {
392        return eDA.getElements();
393    }
394
395    /**
396     * Returns the current set of values in an array of double primitives,
397     * sorted in ascending order.  The returned array is a fresh
398     * copy of the underlying data -- i.e., it is not a reference to the
399     * stored data.
400     * @return returns the current set of
401     * numbers sorted in ascending order
402     */
403    public double[] getSortedValues() {
404        double[] sort = getValues();
405        Arrays.sort(sort);
406        return sort;
407    }
408
409    /**
410     * Returns the element at the specified index
411     * @param index The Index of the element
412     * @return return the element at the specified index
413     */
414    public double getElement(int index) {
415        return eDA.getElement(index);
416    }
417
418    /**
419     * Returns an estimate for the pth percentile of the stored values.
420     * <p>
421     * The implementation provided here follows the first estimation procedure presented
422     * <a href="http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm">here.</a>
423     * </p><p>
424     * <strong>Preconditions</strong>:<ul>
425     * <li><code>0 &lt; p &le; 100</code> (otherwise an
426     * <code>MathIllegalArgumentException</code> is thrown)</li>
427     * <li>at least one value must be stored (returns <code>Double.NaN
428     *     </code> otherwise)</li>
429     * </ul></p>
430     *
431     * @param p the requested percentile (scaled from 0 - 100)
432     * @return An estimate for the pth percentile of the stored data
433     * @throws MathIllegalStateException if percentile implementation has been
434     *  overridden and the supplied implementation does not support setQuantile
435     * @throws MathIllegalArgumentException if p is not a valid quantile
436     */
437    public double getPercentile(double p) throws MathIllegalStateException, MathIllegalArgumentException {
438        if (percentileImpl instanceof Percentile) {
439            ((Percentile) percentileImpl).setQuantile(p);
440        } else {
441            try {
442                percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
443                        new Class[] {Double.TYPE}).invoke(percentileImpl,
444                                new Object[] {Double.valueOf(p)});
445            } catch (NoSuchMethodException e1) { // Setter guard should prevent
446                throw new MathIllegalStateException(
447                      LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
448                      percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
449            } catch (IllegalAccessException e2) {
450                throw new MathIllegalStateException(
451                      LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
452                      SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
453            } catch (InvocationTargetException e3) {
454                throw new IllegalStateException(e3.getCause());
455            }
456        }
457        return apply(percentileImpl);
458    }
459
460    /**
461     * Generates a text report displaying univariate statistics from values
462     * that have been added.  Each statistic is displayed on a separate
463     * line.
464     *
465     * @return String with line feeds displaying statistics
466     */
467    @Override
468    public String toString() {
469        StringBuilder outBuffer = new StringBuilder();
470        String endl = "\n";
471        outBuffer.append("DescriptiveStatistics:").append(endl);
472        outBuffer.append("n: ").append(getN()).append(endl);
473        outBuffer.append("min: ").append(getMin()).append(endl);
474        outBuffer.append("max: ").append(getMax()).append(endl);
475        outBuffer.append("mean: ").append(getMean()).append(endl);
476        outBuffer.append("std dev: ").append(getStandardDeviation())
477            .append(endl);
478        try {
479            // No catch for MIAE because actual parameter is valid below
480            outBuffer.append("median: ").append(getPercentile(50)).append(endl);
481        } catch (MathIllegalStateException ex) {
482            outBuffer.append("median: unavailable").append(endl);
483        }
484        outBuffer.append("skewness: ").append(getSkewness()).append(endl);
485        outBuffer.append("kurtosis: ").append(getKurtosis()).append(endl);
486        return outBuffer.toString();
487    }
488
489    /**
490     * Apply the given statistic to the data associated with this set of statistics.
491     * @param stat the statistic to apply
492     * @return the computed value of the statistic.
493     */
494    public double apply(UnivariateStatistic stat) {
495        // No try-catch or advertised exception here because arguments are guaranteed valid
496        return eDA.compute(stat);
497    }
498
499    // Implementation getters and setter
500
501    /**
502     * Returns the currently configured mean implementation.
503     *
504     * @return the UnivariateStatistic implementing the mean
505     * @since 1.2
506     */
507    public synchronized UnivariateStatistic getMeanImpl() {
508        return meanImpl;
509    }
510
511    /**
512     * <p>Sets the implementation for the mean.</p>
513     *
514     * @param meanImpl the UnivariateStatistic instance to use
515     * for computing the mean
516     * @since 1.2
517     */
518    public synchronized void setMeanImpl(UnivariateStatistic meanImpl) {
519        this.meanImpl = meanImpl;
520    }
521
522    /**
523     * Returns the currently configured geometric mean implementation.
524     *
525     * @return the UnivariateStatistic implementing the geometric mean
526     * @since 1.2
527     */
528    public synchronized UnivariateStatistic getGeometricMeanImpl() {
529        return geometricMeanImpl;
530    }
531
532    /**
533     * <p>Sets the implementation for the gemoetric mean.</p>
534     *
535     * @param geometricMeanImpl the UnivariateStatistic instance to use
536     * for computing the geometric mean
537     * @since 1.2
538     */
539    public synchronized void setGeometricMeanImpl(
540            UnivariateStatistic geometricMeanImpl) {
541        this.geometricMeanImpl = geometricMeanImpl;
542    }
543
544    /**
545     * Returns the currently configured kurtosis implementation.
546     *
547     * @return the UnivariateStatistic implementing the kurtosis
548     * @since 1.2
549     */
550    public synchronized UnivariateStatistic getKurtosisImpl() {
551        return kurtosisImpl;
552    }
553
554    /**
555     * <p>Sets the implementation for the kurtosis.</p>
556     *
557     * @param kurtosisImpl the UnivariateStatistic instance to use
558     * for computing the kurtosis
559     * @since 1.2
560     */
561    public synchronized void setKurtosisImpl(UnivariateStatistic kurtosisImpl) {
562        this.kurtosisImpl = kurtosisImpl;
563    }
564
565    /**
566     * Returns the currently configured maximum implementation.
567     *
568     * @return the UnivariateStatistic implementing the maximum
569     * @since 1.2
570     */
571    public synchronized UnivariateStatistic getMaxImpl() {
572        return maxImpl;
573    }
574
575    /**
576     * <p>Sets the implementation for the maximum.</p>
577     *
578     * @param maxImpl the UnivariateStatistic instance to use
579     * for computing the maximum
580     * @since 1.2
581     */
582    public synchronized void setMaxImpl(UnivariateStatistic maxImpl) {
583        this.maxImpl = maxImpl;
584    }
585
586    /**
587     * Returns the currently configured minimum implementation.
588     *
589     * @return the UnivariateStatistic implementing the minimum
590     * @since 1.2
591     */
592    public synchronized UnivariateStatistic getMinImpl() {
593        return minImpl;
594    }
595
596    /**
597     * <p>Sets the implementation for the minimum.</p>
598     *
599     * @param minImpl the UnivariateStatistic instance to use
600     * for computing the minimum
601     * @since 1.2
602     */
603    public synchronized void setMinImpl(UnivariateStatistic minImpl) {
604        this.minImpl = minImpl;
605    }
606
607    /**
608     * Returns the currently configured percentile implementation.
609     *
610     * @return the UnivariateStatistic implementing the percentile
611     * @since 1.2
612     */
613    public synchronized UnivariateStatistic getPercentileImpl() {
614        return percentileImpl;
615    }
616
617    /**
618     * Sets the implementation to be used by {@link #getPercentile(double)}.
619     * The supplied <code>UnivariateStatistic</code> must provide a
620     * <code>setQuantile(double)</code> method; otherwise
621     * <code>IllegalArgumentException</code> is thrown.
622     *
623     * @param percentileImpl the percentileImpl to set
624     * @throws MathIllegalArgumentException if the supplied implementation does not
625     *  provide a <code>setQuantile</code> method
626     * @since 1.2
627     */
628    public synchronized void setPercentileImpl(UnivariateStatistic percentileImpl)
629    throws MathIllegalArgumentException {
630        try {
631            percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
632                    new Class[] {Double.TYPE}).invoke(percentileImpl,
633                            new Object[] {Double.valueOf(50.0d)});
634        } catch (NoSuchMethodException e1) {
635            throw new MathIllegalArgumentException(
636                  LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
637                  percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
638        } catch (IllegalAccessException e2) {
639            throw new MathIllegalArgumentException(
640                  LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
641                  SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
642        } catch (InvocationTargetException e3) {
643            throw new IllegalArgumentException(e3.getCause());
644        }
645        this.percentileImpl = percentileImpl;
646    }
647
648    /**
649     * Returns the currently configured skewness implementation.
650     *
651     * @return the UnivariateStatistic implementing the skewness
652     * @since 1.2
653     */
654    public synchronized UnivariateStatistic getSkewnessImpl() {
655        return skewnessImpl;
656    }
657
658    /**
659     * <p>Sets the implementation for the skewness.</p>
660     *
661     * @param skewnessImpl the UnivariateStatistic instance to use
662     * for computing the skewness
663     * @since 1.2
664     */
665    public synchronized void setSkewnessImpl(
666            UnivariateStatistic skewnessImpl) {
667        this.skewnessImpl = skewnessImpl;
668    }
669
670    /**
671     * Returns the currently configured variance implementation.
672     *
673     * @return the UnivariateStatistic implementing the variance
674     * @since 1.2
675     */
676    public synchronized UnivariateStatistic getVarianceImpl() {
677        return varianceImpl;
678    }
679
680    /**
681     * <p>Sets the implementation for the variance.</p>
682     *
683     * @param varianceImpl the UnivariateStatistic instance to use
684     * for computing the variance
685     * @since 1.2
686     */
687    public synchronized void setVarianceImpl(
688            UnivariateStatistic varianceImpl) {
689        this.varianceImpl = varianceImpl;
690    }
691
692    /**
693     * Returns the currently configured sum of squares implementation.
694     *
695     * @return the UnivariateStatistic implementing the sum of squares
696     * @since 1.2
697     */
698    public synchronized UnivariateStatistic getSumsqImpl() {
699        return sumsqImpl;
700    }
701
702    /**
703     * <p>Sets the implementation for the sum of squares.</p>
704     *
705     * @param sumsqImpl the UnivariateStatistic instance to use
706     * for computing the sum of squares
707     * @since 1.2
708     */
709    public synchronized void setSumsqImpl(UnivariateStatistic sumsqImpl) {
710        this.sumsqImpl = sumsqImpl;
711    }
712
713    /**
714     * Returns the currently configured sum implementation.
715     *
716     * @return the UnivariateStatistic implementing the sum
717     * @since 1.2
718     */
719    public synchronized UnivariateStatistic getSumImpl() {
720        return sumImpl;
721    }
722
723    /**
724     * <p>Sets the implementation for the sum.</p>
725     *
726     * @param sumImpl the UnivariateStatistic instance to use
727     * for computing the sum
728     * @since 1.2
729     */
730    public synchronized void setSumImpl(UnivariateStatistic sumImpl) {
731        this.sumImpl = sumImpl;
732    }
733
734    /**
735     * Returns a copy of this DescriptiveStatistics instance with the same internal state.
736     *
737     * @return a copy of this
738     */
739    public DescriptiveStatistics copy() {
740        DescriptiveStatistics result = new DescriptiveStatistics();
741        // No try-catch or advertised exception because parms are guaranteed valid
742        copy(this, result);
743        return result;
744    }
745
746    /**
747     * Copies source to dest.
748     * <p>Neither source nor dest can be null.</p>
749     *
750     * @param source DescriptiveStatistics to copy
751     * @param dest DescriptiveStatistics to copy to
752     * @throws NullArgumentException if either source or dest is null
753     */
754    public static void copy(DescriptiveStatistics source, DescriptiveStatistics dest)
755        throws NullArgumentException {
756        MathUtils.checkNotNull(source);
757        MathUtils.checkNotNull(dest);
758        // Copy data and window size
759        dest.eDA = source.eDA.copy();
760        dest.windowSize = source.windowSize;
761
762        // Copy implementations
763        dest.maxImpl = source.maxImpl.copy();
764        dest.meanImpl = source.meanImpl.copy();
765        dest.minImpl = source.minImpl.copy();
766        dest.sumImpl = source.sumImpl.copy();
767        dest.varianceImpl = source.varianceImpl.copy();
768        dest.sumsqImpl = source.sumsqImpl.copy();
769        dest.geometricMeanImpl = source.geometricMeanImpl.copy();
770        dest.kurtosisImpl = source.kurtosisImpl;
771        dest.skewnessImpl = source.skewnessImpl;
772        dest.percentileImpl = source.percentileImpl;
773    }
774}