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     */
017    package org.apache.commons.math3.stat.descriptive;
018    
019    import java.io.Serializable;
020    import java.lang.reflect.InvocationTargetException;
021    import java.util.Arrays;
022    
023    import org.apache.commons.math3.exception.MathIllegalArgumentException;
024    import org.apache.commons.math3.exception.NullArgumentException;
025    import org.apache.commons.math3.exception.MathIllegalStateException;
026    import org.apache.commons.math3.exception.util.LocalizedFormats;
027    import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
028    import org.apache.commons.math3.stat.descriptive.moment.Kurtosis;
029    import org.apache.commons.math3.stat.descriptive.moment.Mean;
030    import org.apache.commons.math3.stat.descriptive.moment.Skewness;
031    import org.apache.commons.math3.stat.descriptive.moment.Variance;
032    import org.apache.commons.math3.stat.descriptive.rank.Max;
033    import org.apache.commons.math3.stat.descriptive.rank.Min;
034    import org.apache.commons.math3.stat.descriptive.rank.Percentile;
035    import org.apache.commons.math3.stat.descriptive.summary.Sum;
036    import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
037    import org.apache.commons.math3.util.MathUtils;
038    import org.apache.commons.math3.util.ResizableDoubleArray;
039    import 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     */
061    public 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    }