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