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