View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math3.stat.descriptive;
18  
19  import java.io.Serializable;
20  
21  import org.apache.commons.math3.exception.MathIllegalStateException;
22  import org.apache.commons.math3.exception.NullArgumentException;
23  import org.apache.commons.math3.exception.util.LocalizedFormats;
24  import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
25  import org.apache.commons.math3.stat.descriptive.moment.Mean;
26  import org.apache.commons.math3.stat.descriptive.moment.SecondMoment;
27  import org.apache.commons.math3.stat.descriptive.moment.Variance;
28  import org.apache.commons.math3.stat.descriptive.rank.Max;
29  import org.apache.commons.math3.stat.descriptive.rank.Min;
30  import org.apache.commons.math3.stat.descriptive.summary.Sum;
31  import org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;
32  import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
33  import org.apache.commons.math3.util.MathUtils;
34  import org.apache.commons.math3.util.Precision;
35  import org.apache.commons.math3.util.FastMath;
36  
37  /**
38   * <p>
39   * Computes summary statistics for a stream of data values added using the
40   * {@link #addValue(double) addValue} method. The data values are not stored in
41   * memory, so this class can be used to compute statistics for very large data
42   * streams.
43   * </p>
44   * <p>
45   * The {@link StorelessUnivariateStatistic} instances used to maintain summary
46   * state and compute statistics are configurable via setters. For example, the
47   * default implementation for the variance can be overridden by calling
48   * {@link #setVarianceImpl(StorelessUnivariateStatistic)}. Actual parameters to
49   * these methods must implement the {@link StorelessUnivariateStatistic}
50   * interface and configuration must be completed before <code>addValue</code>
51   * is called. No configuration is necessary to use the default, commons-math
52   * provided implementations.
53   * </p>
54   * <p>
55   * Note: This class is not thread-safe. Use
56   * {@link SynchronizedSummaryStatistics} if concurrent access from multiple
57   * threads is required.
58   * </p>
59   * @version $Id: SummaryStatistics.java 1416643 2012-12-03 19:37:14Z tn $
60   */
61  public class SummaryStatistics implements StatisticalSummary, Serializable {
62  
63      /** Serialization UID */
64      private static final long serialVersionUID = -2021321786743555871L;
65  
66      /** count of values that have been added */
67      private long n = 0;
68  
69      /** SecondMoment is used to compute the mean and variance */
70      private SecondMoment secondMoment = new SecondMoment();
71  
72      /** sum of values that have been added */
73      private Sum sum = new Sum();
74  
75      /** sum of the square of each value that has been added */
76      private SumOfSquares sumsq = new SumOfSquares();
77  
78      /** min of values that have been added */
79      private Min min = new Min();
80  
81      /** max of values that have been added */
82      private Max max = new Max();
83  
84      /** sumLog of values that have been added */
85      private SumOfLogs sumLog = new SumOfLogs();
86  
87      /** geoMean of values that have been added */
88      private GeometricMean geoMean = new GeometricMean(sumLog);
89  
90      /** mean of values that have been added */
91      private Mean mean = new Mean(secondMoment);
92  
93      /** variance of values that have been added */
94      private Variance variance = new Variance(secondMoment);
95  
96      /** Sum statistic implementation - can be reset by setter. */
97      private StorelessUnivariateStatistic sumImpl = sum;
98  
99      /** 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 }