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    
021    import org.apache.commons.math3.exception.MathIllegalStateException;
022    import org.apache.commons.math3.exception.NullArgumentException;
023    import org.apache.commons.math3.exception.util.LocalizedFormats;
024    import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
025    import org.apache.commons.math3.stat.descriptive.moment.Mean;
026    import org.apache.commons.math3.stat.descriptive.moment.SecondMoment;
027    import org.apache.commons.math3.stat.descriptive.moment.Variance;
028    import org.apache.commons.math3.stat.descriptive.rank.Max;
029    import org.apache.commons.math3.stat.descriptive.rank.Min;
030    import org.apache.commons.math3.stat.descriptive.summary.Sum;
031    import org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;
032    import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
033    import org.apache.commons.math3.util.MathUtils;
034    import org.apache.commons.math3.util.Precision;
035    import org.apache.commons.math3.util.FastMath;
036    
037    /**
038     * <p>
039     * Computes summary statistics for a stream of data values added using the
040     * {@link #addValue(double) addValue} method. The data values are not stored in
041     * memory, so this class can be used to compute statistics for very large data
042     * streams.
043     * </p>
044     * <p>
045     * The {@link StorelessUnivariateStatistic} instances used to maintain summary
046     * state and compute statistics are configurable via setters. For example, the
047     * default implementation for the variance can be overridden by calling
048     * {@link #setVarianceImpl(StorelessUnivariateStatistic)}. Actual parameters to
049     * these methods must implement the {@link StorelessUnivariateStatistic}
050     * interface and configuration must be completed before <code>addValue</code>
051     * is called. No configuration is necessary to use the default, commons-math
052     * provided implementations.
053     * </p>
054     * <p>
055     * Note: This class is not thread-safe. Use
056     * {@link SynchronizedSummaryStatistics} if concurrent access from multiple
057     * threads is required.
058     * </p>
059     * @version $Id: SummaryStatistics.java 1416643 2012-12-03 19:37:14Z tn $
060     */
061    public class SummaryStatistics implements StatisticalSummary, Serializable {
062    
063        /** Serialization UID */
064        private static final long serialVersionUID = -2021321786743555871L;
065    
066        /** count of values that have been added */
067        private long n = 0;
068    
069        /** SecondMoment is used to compute the mean and variance */
070        private SecondMoment secondMoment = new SecondMoment();
071    
072        /** sum of values that have been added */
073        private Sum sum = new Sum();
074    
075        /** sum of the square of each value that has been added */
076        private SumOfSquares sumsq = new SumOfSquares();
077    
078        /** min of values that have been added */
079        private Min min = new Min();
080    
081        /** max of values that have been added */
082        private Max max = new Max();
083    
084        /** sumLog of values that have been added */
085        private SumOfLogs sumLog = new SumOfLogs();
086    
087        /** geoMean of values that have been added */
088        private GeometricMean geoMean = new GeometricMean(sumLog);
089    
090        /** mean of values that have been added */
091        private Mean mean = new Mean(secondMoment);
092    
093        /** variance of values that have been added */
094        private Variance variance = new Variance(secondMoment);
095    
096        /** Sum statistic implementation - can be reset by setter. */
097        private StorelessUnivariateStatistic sumImpl = sum;
098    
099        /** Sum of squares statistic implementation - can be reset by setter. */
100        private StorelessUnivariateStatistic sumsqImpl = sumsq;
101    
102        /** Minimum statistic implementation - can be reset by setter. */
103        private StorelessUnivariateStatistic minImpl = min;
104    
105        /** Maximum statistic implementation - can be reset by setter. */
106        private StorelessUnivariateStatistic maxImpl = max;
107    
108        /** Sum of log statistic implementation - can be reset by setter. */
109        private StorelessUnivariateStatistic sumLogImpl = sumLog;
110    
111        /** Geometric mean statistic implementation - can be reset by setter. */
112        private StorelessUnivariateStatistic geoMeanImpl = geoMean;
113    
114        /** Mean statistic implementation - can be reset by setter. */
115        private StorelessUnivariateStatistic meanImpl = mean;
116    
117        /** Variance statistic implementation - can be reset by setter. */
118        private StorelessUnivariateStatistic varianceImpl = variance;
119    
120        /**
121         * Construct a SummaryStatistics instance
122         */
123        public SummaryStatistics() {
124        }
125    
126        /**
127         * A copy constructor. Creates a deep-copy of the {@code original}.
128         *
129         * @param original the {@code SummaryStatistics} instance to copy
130         * @throws NullArgumentException if original is null
131         */
132        public SummaryStatistics(SummaryStatistics original) throws NullArgumentException {
133            copy(original, this);
134        }
135    
136        /**
137         * Return a {@link StatisticalSummaryValues} instance reporting current
138         * statistics.
139         * @return Current values of statistics
140         */
141        public StatisticalSummary getSummary() {
142            return new StatisticalSummaryValues(getMean(), getVariance(), getN(),
143                    getMax(), getMin(), getSum());
144        }
145    
146        /**
147         * Add a value to the data
148         * @param value the value to add
149         */
150        public void addValue(double value) {
151            sumImpl.increment(value);
152            sumsqImpl.increment(value);
153            minImpl.increment(value);
154            maxImpl.increment(value);
155            sumLogImpl.increment(value);
156            secondMoment.increment(value);
157            // If mean, variance or geomean have been overridden,
158            // need to increment these
159            if (meanImpl != mean) {
160                meanImpl.increment(value);
161            }
162            if (varianceImpl != variance) {
163                varianceImpl.increment(value);
164            }
165            if (geoMeanImpl != geoMean) {
166                geoMeanImpl.increment(value);
167            }
168            n++;
169        }
170    
171        /**
172         * Returns the number of available values
173         * @return The number of available values
174         */
175        public long getN() {
176            return n;
177        }
178    
179        /**
180         * Returns the sum of the values that have been added
181         * @return The sum or <code>Double.NaN</code> if no values have been added
182         */
183        public double getSum() {
184            return sumImpl.getResult();
185        }
186    
187        /**
188         * Returns the sum of the squares of the values that have been added.
189         * <p>
190         * Double.NaN is returned if no values have been added.
191         * </p>
192         * @return The sum of squares
193         */
194        public double getSumsq() {
195            return sumsqImpl.getResult();
196        }
197    
198        /**
199         * Returns the mean of the values that have been added.
200         * <p>
201         * Double.NaN is returned if no values have been added.
202         * </p>
203         * @return the mean
204         */
205        public double getMean() {
206            return meanImpl.getResult();
207        }
208    
209        /**
210         * Returns the standard deviation of the values that have been added.
211         * <p>
212         * Double.NaN is returned if no values have been added.
213         * </p>
214         * @return the standard deviation
215         */
216        public double getStandardDeviation() {
217            double stdDev = Double.NaN;
218            if (getN() > 0) {
219                if (getN() > 1) {
220                    stdDev = FastMath.sqrt(getVariance());
221                } else {
222                    stdDev = 0.0;
223                }
224            }
225            return stdDev;
226        }
227    
228        /**
229         * Returns the (sample) variance of the available values.
230         *
231         * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in
232         * the denominator).  Use {@link #getPopulationVariance()} for the non-bias-corrected
233         * population variance.</p>
234         *
235         * <p>Double.NaN is returned if no values have been added.</p>
236         *
237         * @return the variance
238         */
239        public double getVariance() {
240            return varianceImpl.getResult();
241        }
242    
243        /**
244         * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">
245         * population variance</a> of the values that have been added.
246         *
247         * <p>Double.NaN is returned if no values have been added.</p>
248         *
249         * @return the population variance
250         */
251        public double getPopulationVariance() {
252            Variance populationVariance = new Variance(secondMoment);
253            populationVariance.setBiasCorrected(false);
254            return populationVariance.getResult();
255        }
256    
257        /**
258         * Returns the maximum of the values that have been added.
259         * <p>
260         * Double.NaN is returned if no values have been added.
261         * </p>
262         * @return the maximum
263         */
264        public double getMax() {
265            return maxImpl.getResult();
266        }
267    
268        /**
269         * Returns the minimum of the values that have been added.
270         * <p>
271         * Double.NaN is returned if no values have been added.
272         * </p>
273         * @return the minimum
274         */
275        public double getMin() {
276            return minImpl.getResult();
277        }
278    
279        /**
280         * Returns the geometric mean of the values that have been added.
281         * <p>
282         * Double.NaN is returned if no values have been added.
283         * </p>
284         * @return the geometric mean
285         */
286        public double getGeometricMean() {
287            return geoMeanImpl.getResult();
288        }
289    
290        /**
291         * Returns the sum of the logs of the values that have been added.
292         * <p>
293         * Double.NaN is returned if no values have been added.
294         * </p>
295         * @return the sum of logs
296         * @since 1.2
297         */
298        public double getSumOfLogs() {
299            return sumLogImpl.getResult();
300        }
301    
302        /**
303         * Returns a statistic related to the Second Central Moment.  Specifically,
304         * what is returned is the sum of squared deviations from the sample mean
305         * among the values that have been added.
306         * <p>
307         * Returns <code>Double.NaN</code> if no data values have been added and
308         * returns <code>0</code> if there is just one value in the data set.</p>
309         * <p>
310         * @return second central moment statistic
311         * @since 2.0
312         */
313        public double getSecondMoment() {
314            return secondMoment.getResult();
315        }
316    
317        /**
318         * Generates a text report displaying summary statistics from values that
319         * have been added.
320         * @return String with line feeds displaying statistics
321         * @since 1.2
322         */
323        @Override
324        public String toString() {
325            StringBuilder outBuffer = new StringBuilder();
326            String endl = "\n";
327            outBuffer.append("SummaryStatistics:").append(endl);
328            outBuffer.append("n: ").append(getN()).append(endl);
329            outBuffer.append("min: ").append(getMin()).append(endl);
330            outBuffer.append("max: ").append(getMax()).append(endl);
331            outBuffer.append("mean: ").append(getMean()).append(endl);
332            outBuffer.append("geometric mean: ").append(getGeometricMean())
333                .append(endl);
334            outBuffer.append("variance: ").append(getVariance()).append(endl);
335            outBuffer.append("sum of squares: ").append(getSumsq()).append(endl);
336            outBuffer.append("standard deviation: ").append(getStandardDeviation())
337                .append(endl);
338            outBuffer.append("sum of logs: ").append(getSumOfLogs()).append(endl);
339            return outBuffer.toString();
340        }
341    
342        /**
343         * Resets all statistics and storage
344         */
345        public void clear() {
346            this.n = 0;
347            minImpl.clear();
348            maxImpl.clear();
349            sumImpl.clear();
350            sumLogImpl.clear();
351            sumsqImpl.clear();
352            geoMeanImpl.clear();
353            secondMoment.clear();
354            if (meanImpl != mean) {
355                meanImpl.clear();
356            }
357            if (varianceImpl != variance) {
358                varianceImpl.clear();
359            }
360        }
361    
362        /**
363         * Returns true iff <code>object</code> is a
364         * <code>SummaryStatistics</code> instance and all statistics have the
365         * same values as this.
366         * @param object the object to test equality against.
367         * @return true if object equals this
368         */
369        @Override
370        public boolean equals(Object object) {
371            if (object == this) {
372                return true;
373            }
374            if (object instanceof SummaryStatistics == false) {
375                return false;
376            }
377            SummaryStatistics stat = (SummaryStatistics)object;
378            return Precision.equalsIncludingNaN(stat.getGeometricMean(), getGeometricMean()) &&
379                   Precision.equalsIncludingNaN(stat.getMax(),           getMax())           &&
380                   Precision.equalsIncludingNaN(stat.getMean(),          getMean())          &&
381                   Precision.equalsIncludingNaN(stat.getMin(),           getMin())           &&
382                   Precision.equalsIncludingNaN(stat.getN(),             getN())             &&
383                   Precision.equalsIncludingNaN(stat.getSum(),           getSum())           &&
384                   Precision.equalsIncludingNaN(stat.getSumsq(),         getSumsq())         &&
385                   Precision.equalsIncludingNaN(stat.getVariance(),      getVariance());
386        }
387    
388        /**
389         * Returns hash code based on values of statistics
390         * @return hash code
391         */
392        @Override
393        public int hashCode() {
394            int result = 31 + MathUtils.hash(getGeometricMean());
395            result = result * 31 + MathUtils.hash(getGeometricMean());
396            result = result * 31 + MathUtils.hash(getMax());
397            result = result * 31 + MathUtils.hash(getMean());
398            result = result * 31 + MathUtils.hash(getMin());
399            result = result * 31 + MathUtils.hash(getN());
400            result = result * 31 + MathUtils.hash(getSum());
401            result = result * 31 + MathUtils.hash(getSumsq());
402            result = result * 31 + MathUtils.hash(getVariance());
403            return result;
404        }
405    
406        // Getters and setters for statistics implementations
407        /**
408         * Returns the currently configured Sum implementation
409         * @return the StorelessUnivariateStatistic implementing the sum
410         * @since 1.2
411         */
412        public StorelessUnivariateStatistic getSumImpl() {
413            return sumImpl;
414        }
415    
416        /**
417         * <p>
418         * Sets the implementation for the Sum.
419         * </p>
420         * <p>
421         * This method must be activated before any data has been added - i.e.,
422         * before {@link #addValue(double) addValue} has been used to add data;
423         * otherwise an IllegalStateException will be thrown.
424         * </p>
425         * @param sumImpl the StorelessUnivariateStatistic instance to use for
426         *        computing the Sum
427         * @throws MathIllegalStateException if data has already been added (i.e if n >0)
428         * @since 1.2
429         */
430        public void setSumImpl(StorelessUnivariateStatistic sumImpl)
431        throws MathIllegalStateException {
432            checkEmpty();
433            this.sumImpl = sumImpl;
434        }
435    
436        /**
437         * Returns the currently configured sum of squares implementation
438         * @return the StorelessUnivariateStatistic implementing the sum of squares
439         * @since 1.2
440         */
441        public StorelessUnivariateStatistic getSumsqImpl() {
442            return sumsqImpl;
443        }
444    
445        /**
446         * <p>
447         * Sets the implementation for the sum of squares.
448         * </p>
449         * <p>
450         * This method must be activated before any data has been added - i.e.,
451         * before {@link #addValue(double) addValue} has been used to add data;
452         * otherwise an IllegalStateException will be thrown.
453         * </p>
454         * @param sumsqImpl the StorelessUnivariateStatistic instance to use for
455         *        computing the sum of squares
456         * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
457         * @since 1.2
458         */
459        public void setSumsqImpl(StorelessUnivariateStatistic sumsqImpl)
460        throws MathIllegalStateException {
461            checkEmpty();
462            this.sumsqImpl = sumsqImpl;
463        }
464    
465        /**
466         * Returns the currently configured minimum implementation
467         * @return the StorelessUnivariateStatistic implementing the minimum
468         * @since 1.2
469         */
470        public StorelessUnivariateStatistic getMinImpl() {
471            return minImpl;
472        }
473    
474        /**
475         * <p>
476         * Sets the implementation for the minimum.
477         * </p>
478         * <p>
479         * This method must be activated before any data has been added - i.e.,
480         * before {@link #addValue(double) addValue} has been used to add data;
481         * otherwise an IllegalStateException will be thrown.
482         * </p>
483         * @param minImpl the StorelessUnivariateStatistic instance to use for
484         *        computing the minimum
485         * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
486         * @since 1.2
487         */
488        public void setMinImpl(StorelessUnivariateStatistic minImpl)
489        throws MathIllegalStateException {
490            checkEmpty();
491            this.minImpl = minImpl;
492        }
493    
494        /**
495         * Returns the currently configured maximum implementation
496         * @return the StorelessUnivariateStatistic implementing the maximum
497         * @since 1.2
498         */
499        public StorelessUnivariateStatistic getMaxImpl() {
500            return maxImpl;
501        }
502    
503        /**
504         * <p>
505         * Sets the implementation for the maximum.
506         * </p>
507         * <p>
508         * This method must be activated before any data has been added - i.e.,
509         * before {@link #addValue(double) addValue} has been used to add data;
510         * otherwise an IllegalStateException will be thrown.
511         * </p>
512         * @param maxImpl the StorelessUnivariateStatistic instance to use for
513         *        computing the maximum
514         * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
515         * @since 1.2
516         */
517        public void setMaxImpl(StorelessUnivariateStatistic maxImpl)
518        throws MathIllegalStateException {
519            checkEmpty();
520            this.maxImpl = maxImpl;
521        }
522    
523        /**
524         * Returns the currently configured sum of logs implementation
525         * @return the StorelessUnivariateStatistic implementing the log sum
526         * @since 1.2
527         */
528        public StorelessUnivariateStatistic getSumLogImpl() {
529            return sumLogImpl;
530        }
531    
532        /**
533         * <p>
534         * Sets the implementation for the sum of logs.
535         * </p>
536         * <p>
537         * This method must be activated before any data has been added - i.e.,
538         * before {@link #addValue(double) addValue} has been used to add data;
539         * otherwise an IllegalStateException will be thrown.
540         * </p>
541         * @param sumLogImpl the StorelessUnivariateStatistic instance to use for
542         *        computing the log sum
543         * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
544         * @since 1.2
545         */
546        public void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl)
547        throws MathIllegalStateException {
548            checkEmpty();
549            this.sumLogImpl = sumLogImpl;
550            geoMean.setSumLogImpl(sumLogImpl);
551        }
552    
553        /**
554         * Returns the currently configured geometric mean implementation
555         * @return the StorelessUnivariateStatistic implementing the geometric mean
556         * @since 1.2
557         */
558        public StorelessUnivariateStatistic getGeoMeanImpl() {
559            return geoMeanImpl;
560        }
561    
562        /**
563         * <p>
564         * Sets the implementation for the geometric mean.
565         * </p>
566         * <p>
567         * This method must be activated before any data has been added - i.e.,
568         * before {@link #addValue(double) addValue} has been used to add data;
569         * otherwise an IllegalStateException will be thrown.
570         * </p>
571         * @param geoMeanImpl the StorelessUnivariateStatistic instance to use for
572         *        computing the geometric mean
573         * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
574         * @since 1.2
575         */
576        public void setGeoMeanImpl(StorelessUnivariateStatistic geoMeanImpl)
577        throws MathIllegalStateException {
578            checkEmpty();
579            this.geoMeanImpl = geoMeanImpl;
580        }
581    
582        /**
583         * Returns the currently configured mean implementation
584         * @return the StorelessUnivariateStatistic implementing the mean
585         * @since 1.2
586         */
587        public StorelessUnivariateStatistic getMeanImpl() {
588            return meanImpl;
589        }
590    
591        /**
592         * <p>
593         * Sets the implementation for the mean.
594         * </p>
595         * <p>
596         * This method must be activated before any data has been added - i.e.,
597         * before {@link #addValue(double) addValue} has been used to add data;
598         * otherwise an IllegalStateException will be thrown.
599         * </p>
600         * @param meanImpl the StorelessUnivariateStatistic instance to use for
601         *        computing the mean
602         * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
603         * @since 1.2
604         */
605        public void setMeanImpl(StorelessUnivariateStatistic meanImpl)
606        throws MathIllegalStateException {
607            checkEmpty();
608            this.meanImpl = meanImpl;
609        }
610    
611        /**
612         * Returns the currently configured variance implementation
613         * @return the StorelessUnivariateStatistic implementing the variance
614         * @since 1.2
615         */
616        public StorelessUnivariateStatistic getVarianceImpl() {
617            return varianceImpl;
618        }
619    
620        /**
621         * <p>
622         * Sets the implementation for the variance.
623         * </p>
624         * <p>
625         * This method must be activated before any data has been added - i.e.,
626         * before {@link #addValue(double) addValue} has been used to add data;
627         * otherwise an IllegalStateException will be thrown.
628         * </p>
629         * @param varianceImpl the StorelessUnivariateStatistic instance to use for
630         *        computing the variance
631         * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
632         * @since 1.2
633         */
634        public void setVarianceImpl(StorelessUnivariateStatistic varianceImpl)
635        throws MathIllegalStateException {
636            checkEmpty();
637            this.varianceImpl = varianceImpl;
638        }
639    
640        /**
641         * Throws IllegalStateException if n > 0.
642         * @throws MathIllegalStateException if data has been added
643         */
644        private void checkEmpty() throws MathIllegalStateException {
645            if (n > 0) {
646                throw new MathIllegalStateException(
647                    LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC, n);
648            }
649        }
650    
651        /**
652         * Returns a copy of this SummaryStatistics instance with the same internal state.
653         *
654         * @return a copy of this
655         */
656        public SummaryStatistics copy() {
657            SummaryStatistics result = new SummaryStatistics();
658            // No try-catch or advertised exception because arguments are guaranteed non-null
659            copy(this, result);
660            return result;
661        }
662    
663        /**
664         * Copies source to dest.
665         * <p>Neither source nor dest can be null.</p>
666         *
667         * @param source SummaryStatistics to copy
668         * @param dest SummaryStatistics to copy to
669         * @throws NullArgumentException if either source or dest is null
670         */
671        public static void copy(SummaryStatistics source, SummaryStatistics dest)
672            throws NullArgumentException {
673            MathUtils.checkNotNull(source);
674            MathUtils.checkNotNull(dest);
675            dest.maxImpl = source.maxImpl.copy();
676            dest.minImpl = source.minImpl.copy();
677            dest.sumImpl = source.sumImpl.copy();
678            dest.sumLogImpl = source.sumLogImpl.copy();
679            dest.sumsqImpl = source.sumsqImpl.copy();
680            dest.secondMoment = source.secondMoment.copy();
681            dest.n = source.n;
682    
683            // Keep commons-math supplied statistics with embedded moments in synch
684            if (source.getVarianceImpl() instanceof Variance) {
685                dest.varianceImpl = new Variance(dest.secondMoment);
686            } else {
687                dest.varianceImpl = source.varianceImpl.copy();
688            }
689            if (source.meanImpl instanceof Mean) {
690                dest.meanImpl = new Mean(dest.secondMoment);
691            } else {
692                dest.meanImpl = source.meanImpl.copy();
693            }
694            if (source.getGeoMeanImpl() instanceof GeometricMean) {
695                dest.geoMeanImpl = new GeometricMean((SumOfLogs) dest.sumLogImpl);
696            } else {
697                dest.geoMeanImpl = source.geoMeanImpl.copy();
698            }
699    
700            // Make sure that if stat == statImpl in source, same
701            // holds in dest; otherwise copy stat
702            if (source.geoMean == source.geoMeanImpl) {
703                dest.geoMean = (GeometricMean) dest.geoMeanImpl;
704            } else {
705                GeometricMean.copy(source.geoMean, dest.geoMean);
706            }
707            if (source.max == source.maxImpl) {
708                dest.max = (Max) dest.maxImpl;
709            } else {
710                Max.copy(source.max, dest.max);
711            }
712            if (source.mean == source.meanImpl) {
713                dest.mean = (Mean) dest.meanImpl;
714            } else {
715                Mean.copy(source.mean, dest.mean);
716            }
717            if (source.min == source.minImpl) {
718                dest.min = (Min) dest.minImpl;
719            } else {
720                Min.copy(source.min, dest.min);
721            }
722            if (source.sum == source.sumImpl) {
723                dest.sum = (Sum) dest.sumImpl;
724            } else {
725                Sum.copy(source.sum, dest.sum);
726            }
727            if (source.variance == source.varianceImpl) {
728                dest.variance = (Variance) dest.varianceImpl;
729            } else {
730                Variance.copy(source.variance, dest.variance);
731            }
732            if (source.sumLog == source.sumLogImpl) {
733                dest.sumLog = (SumOfLogs) dest.sumLogImpl;
734            } else {
735                SumOfLogs.copy(source.sumLog, dest.sumLog);
736            }
737            if (source.sumsq == source.sumsqImpl) {
738                dest.sumsq = (SumOfSquares) dest.sumsqImpl;
739            } else {
740                SumOfSquares.copy(source.sumsq, dest.sumsq);
741            }
742        }
743    }