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;
020
021import org.apache.commons.math3.exception.MathIllegalStateException;
022import org.apache.commons.math3.exception.NullArgumentException;
023import org.apache.commons.math3.exception.util.LocalizedFormats;
024import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
025import org.apache.commons.math3.stat.descriptive.moment.Mean;
026import org.apache.commons.math3.stat.descriptive.moment.SecondMoment;
027import org.apache.commons.math3.stat.descriptive.moment.Variance;
028import org.apache.commons.math3.stat.descriptive.rank.Max;
029import org.apache.commons.math3.stat.descriptive.rank.Min;
030import org.apache.commons.math3.stat.descriptive.summary.Sum;
031import org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;
032import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
033import org.apache.commons.math3.util.MathUtils;
034import org.apache.commons.math3.util.Precision;
035import 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 1520076 2013-09-04 17:24:02Z psteitz $
060 */
061public 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 cannot be activated after data has been added - i.e.,
422     * after {@link #addValue(double) addValue} has been used to add data.
423     * If it is activated after data has been added, an IllegalStateException
424     * will be thrown.
425     * </p>
426     * @param sumImpl the StorelessUnivariateStatistic instance to use for
427     *        computing the Sum
428     * @throws MathIllegalStateException if data has already been added (i.e if n >0)
429     * @since 1.2
430     */
431    public void setSumImpl(StorelessUnivariateStatistic sumImpl)
432    throws MathIllegalStateException {
433        checkEmpty();
434        this.sumImpl = sumImpl;
435    }
436
437    /**
438     * Returns the currently configured sum of squares implementation
439     * @return the StorelessUnivariateStatistic implementing the sum of squares
440     * @since 1.2
441     */
442    public StorelessUnivariateStatistic getSumsqImpl() {
443        return sumsqImpl;
444    }
445
446    /**
447     * <p>
448     * Sets the implementation for the sum of squares.
449     * </p>
450     * <p>
451     * This method cannot be activated after data has been added - i.e.,
452     * after {@link #addValue(double) addValue} has been used to add data.
453     * If it is activated after data has been added, an IllegalStateException
454     * will be thrown.
455     * </p>
456     * @param sumsqImpl the StorelessUnivariateStatistic instance to use for
457     *        computing the sum of squares
458     * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
459     * @since 1.2
460     */
461    public void setSumsqImpl(StorelessUnivariateStatistic sumsqImpl)
462    throws MathIllegalStateException {
463        checkEmpty();
464        this.sumsqImpl = sumsqImpl;
465    }
466
467    /**
468     * Returns the currently configured minimum implementation
469     * @return the StorelessUnivariateStatistic implementing the minimum
470     * @since 1.2
471     */
472    public StorelessUnivariateStatistic getMinImpl() {
473        return minImpl;
474    }
475
476    /**
477     * <p>
478     * Sets the implementation for the minimum.
479     * </p>
480     * <p>
481     * This method cannot be activated after data has been added - i.e.,
482     * after {@link #addValue(double) addValue} has been used to add data.
483     * If it is activated after data has been added, an IllegalStateException
484     * will be thrown.
485     * </p>
486     * @param minImpl the StorelessUnivariateStatistic instance to use for
487     *        computing the minimum
488     * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
489     * @since 1.2
490     */
491    public void setMinImpl(StorelessUnivariateStatistic minImpl)
492    throws MathIllegalStateException {
493        checkEmpty();
494        this.minImpl = minImpl;
495    }
496
497    /**
498     * Returns the currently configured maximum implementation
499     * @return the StorelessUnivariateStatistic implementing the maximum
500     * @since 1.2
501     */
502    public StorelessUnivariateStatistic getMaxImpl() {
503        return maxImpl;
504    }
505
506    /**
507     * <p>
508     * Sets the implementation for the maximum.
509     * </p>
510     * <p>
511     * This method cannot be activated after data has been added - i.e.,
512     * after {@link #addValue(double) addValue} has been used to add data.
513     * If it is activated after data has been added, an IllegalStateException
514     * will be thrown.
515     * </p>
516     * @param maxImpl the StorelessUnivariateStatistic instance to use for
517     *        computing the maximum
518     * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
519     * @since 1.2
520     */
521    public void setMaxImpl(StorelessUnivariateStatistic maxImpl)
522    throws MathIllegalStateException {
523        checkEmpty();
524        this.maxImpl = maxImpl;
525    }
526
527    /**
528     * Returns the currently configured sum of logs implementation
529     * @return the StorelessUnivariateStatistic implementing the log sum
530     * @since 1.2
531     */
532    public StorelessUnivariateStatistic getSumLogImpl() {
533        return sumLogImpl;
534    }
535
536    /**
537     * <p>
538     * Sets the implementation for the sum of logs.
539     * </p>
540     * <p>
541     * This method cannot be activated after data has been added - i.e.,
542     * after {@link #addValue(double) addValue} has been used to add data.
543     * If it is activated after data has been added, an IllegalStateException
544     * will be thrown.
545     * </p>
546     * @param sumLogImpl the StorelessUnivariateStatistic instance to use for
547     *        computing the log sum
548     * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
549     * @since 1.2
550     */
551    public void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl)
552    throws MathIllegalStateException {
553        checkEmpty();
554        this.sumLogImpl = sumLogImpl;
555        geoMean.setSumLogImpl(sumLogImpl);
556    }
557
558    /**
559     * Returns the currently configured geometric mean implementation
560     * @return the StorelessUnivariateStatistic implementing the geometric mean
561     * @since 1.2
562     */
563    public StorelessUnivariateStatistic getGeoMeanImpl() {
564        return geoMeanImpl;
565    }
566
567    /**
568     * <p>
569     * Sets the implementation for the geometric mean.
570     * </p>
571     * <p>
572     * This method cannot be activated after data has been added - i.e.,
573     * after {@link #addValue(double) addValue} has been used to add data.
574     * If it is activated after data has been added, an IllegalStateException
575     * will be thrown.
576     * </p>
577     * @param geoMeanImpl the StorelessUnivariateStatistic instance to use for
578     *        computing the geometric mean
579     * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
580     * @since 1.2
581     */
582    public void setGeoMeanImpl(StorelessUnivariateStatistic geoMeanImpl)
583    throws MathIllegalStateException {
584        checkEmpty();
585        this.geoMeanImpl = geoMeanImpl;
586    }
587
588    /**
589     * Returns the currently configured mean implementation
590     * @return the StorelessUnivariateStatistic implementing the mean
591     * @since 1.2
592     */
593    public StorelessUnivariateStatistic getMeanImpl() {
594        return meanImpl;
595    }
596
597    /**
598     * <p>
599     * Sets the implementation for the mean.
600     * </p>
601     * <p>
602     * This method cannot be activated after data has been added - i.e.,
603     * after {@link #addValue(double) addValue} has been used to add data.
604     * If it is activated after data has been added, an IllegalStateException
605     * will be thrown.
606     * </p>
607     * @param meanImpl the StorelessUnivariateStatistic instance to use for
608     *        computing the mean
609     * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
610     * @since 1.2
611     */
612    public void setMeanImpl(StorelessUnivariateStatistic meanImpl)
613    throws MathIllegalStateException {
614        checkEmpty();
615        this.meanImpl = meanImpl;
616    }
617
618    /**
619     * Returns the currently configured variance implementation
620     * @return the StorelessUnivariateStatistic implementing the variance
621     * @since 1.2
622     */
623    public StorelessUnivariateStatistic getVarianceImpl() {
624        return varianceImpl;
625    }
626
627    /**
628     * <p>
629     * Sets the implementation for the variance.
630     * </p>
631     * <p>
632     * This method cannot be activated after data has been added - i.e.,
633     * after {@link #addValue(double) addValue} has been used to add data.
634     * If it is activated after data has been added, an IllegalStateException
635     * will be thrown.
636     * </p>
637     * @param varianceImpl the StorelessUnivariateStatistic instance to use for
638     *        computing the variance
639     * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
640     * @since 1.2
641     */
642    public void setVarianceImpl(StorelessUnivariateStatistic varianceImpl)
643    throws MathIllegalStateException {
644        checkEmpty();
645        this.varianceImpl = varianceImpl;
646    }
647
648    /**
649     * Throws IllegalStateException if n > 0.
650     * @throws MathIllegalStateException if data has been added
651     */
652    private void checkEmpty() throws MathIllegalStateException {
653        if (n > 0) {
654            throw new MathIllegalStateException(
655                LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC, n);
656        }
657    }
658
659    /**
660     * Returns a copy of this SummaryStatistics instance with the same internal state.
661     *
662     * @return a copy of this
663     */
664    public SummaryStatistics copy() {
665        SummaryStatistics result = new SummaryStatistics();
666        // No try-catch or advertised exception because arguments are guaranteed non-null
667        copy(this, result);
668        return result;
669    }
670
671    /**
672     * Copies source to dest.
673     * <p>Neither source nor dest can be null.</p>
674     *
675     * @param source SummaryStatistics to copy
676     * @param dest SummaryStatistics to copy to
677     * @throws NullArgumentException if either source or dest is null
678     */
679    public static void copy(SummaryStatistics source, SummaryStatistics dest)
680        throws NullArgumentException {
681        MathUtils.checkNotNull(source);
682        MathUtils.checkNotNull(dest);
683        dest.maxImpl = source.maxImpl.copy();
684        dest.minImpl = source.minImpl.copy();
685        dest.sumImpl = source.sumImpl.copy();
686        dest.sumLogImpl = source.sumLogImpl.copy();
687        dest.sumsqImpl = source.sumsqImpl.copy();
688        dest.secondMoment = source.secondMoment.copy();
689        dest.n = source.n;
690
691        // Keep commons-math supplied statistics with embedded moments in synch
692        if (source.getVarianceImpl() instanceof Variance) {
693            dest.varianceImpl = new Variance(dest.secondMoment);
694        } else {
695            dest.varianceImpl = source.varianceImpl.copy();
696        }
697        if (source.meanImpl instanceof Mean) {
698            dest.meanImpl = new Mean(dest.secondMoment);
699        } else {
700            dest.meanImpl = source.meanImpl.copy();
701        }
702        if (source.getGeoMeanImpl() instanceof GeometricMean) {
703            dest.geoMeanImpl = new GeometricMean((SumOfLogs) dest.sumLogImpl);
704        } else {
705            dest.geoMeanImpl = source.geoMeanImpl.copy();
706        }
707
708        // Make sure that if stat == statImpl in source, same
709        // holds in dest; otherwise copy stat
710        if (source.geoMean == source.geoMeanImpl) {
711            dest.geoMean = (GeometricMean) dest.geoMeanImpl;
712        } else {
713            GeometricMean.copy(source.geoMean, dest.geoMean);
714        }
715        if (source.max == source.maxImpl) {
716            dest.max = (Max) dest.maxImpl;
717        } else {
718            Max.copy(source.max, dest.max);
719        }
720        if (source.mean == source.meanImpl) {
721            dest.mean = (Mean) dest.meanImpl;
722        } else {
723            Mean.copy(source.mean, dest.mean);
724        }
725        if (source.min == source.minImpl) {
726            dest.min = (Min) dest.minImpl;
727        } else {
728            Min.copy(source.min, dest.min);
729        }
730        if (source.sum == source.sumImpl) {
731            dest.sum = (Sum) dest.sumImpl;
732        } else {
733            Sum.copy(source.sum, dest.sum);
734        }
735        if (source.variance == source.varianceImpl) {
736            dest.variance = (Variance) dest.varianceImpl;
737        } else {
738            Variance.copy(source.variance, dest.variance);
739        }
740        if (source.sumLog == source.sumLogImpl) {
741            dest.sumLog = (SumOfLogs) dest.sumLogImpl;
742        } else {
743            SumOfLogs.copy(source.sumLog, dest.sumLog);
744        }
745        if (source.sumsq == source.sumsqImpl) {
746            dest.sumsq = (SumOfSquares) dest.sumsqImpl;
747        } else {
748            SumOfSquares.copy(source.sumsq, dest.sumsq);
749        }
750    }
751}