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