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 1520076 2013-09-04 17:24:02Z psteitz $
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 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 }