001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.math3.stat.descriptive;
018
019 import java.io.Serializable;
020
021 import org.apache.commons.math3.exception.MathIllegalStateException;
022 import org.apache.commons.math3.exception.NullArgumentException;
023 import org.apache.commons.math3.exception.util.LocalizedFormats;
024 import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
025 import org.apache.commons.math3.stat.descriptive.moment.Mean;
026 import org.apache.commons.math3.stat.descriptive.moment.SecondMoment;
027 import org.apache.commons.math3.stat.descriptive.moment.Variance;
028 import org.apache.commons.math3.stat.descriptive.rank.Max;
029 import org.apache.commons.math3.stat.descriptive.rank.Min;
030 import org.apache.commons.math3.stat.descriptive.summary.Sum;
031 import org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;
032 import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
033 import org.apache.commons.math3.util.MathUtils;
034 import org.apache.commons.math3.util.Precision;
035 import org.apache.commons.math3.util.FastMath;
036
037 /**
038 * <p>
039 * Computes summary statistics for a stream of data values added using the
040 * {@link #addValue(double) addValue} method. The data values are not stored in
041 * memory, so this class can be used to compute statistics for very large data
042 * streams.
043 * </p>
044 * <p>
045 * The {@link StorelessUnivariateStatistic} instances used to maintain summary
046 * state and compute statistics are configurable via setters. For example, the
047 * default implementation for the variance can be overridden by calling
048 * {@link #setVarianceImpl(StorelessUnivariateStatistic)}. Actual parameters to
049 * these methods must implement the {@link StorelessUnivariateStatistic}
050 * interface and configuration must be completed before <code>addValue</code>
051 * is called. No configuration is necessary to use the default, commons-math
052 * provided implementations.
053 * </p>
054 * <p>
055 * Note: This class is not thread-safe. Use
056 * {@link SynchronizedSummaryStatistics} if concurrent access from multiple
057 * threads is required.
058 * </p>
059 * @version $Id: SummaryStatistics.java 1416643 2012-12-03 19:37:14Z tn $
060 */
061 public class SummaryStatistics implements StatisticalSummary, Serializable {
062
063 /** Serialization UID */
064 private static final long serialVersionUID = -2021321786743555871L;
065
066 /** count of values that have been added */
067 private long n = 0;
068
069 /** SecondMoment is used to compute the mean and variance */
070 private SecondMoment secondMoment = new SecondMoment();
071
072 /** sum of values that have been added */
073 private Sum sum = new Sum();
074
075 /** sum of the square of each value that has been added */
076 private SumOfSquares sumsq = new SumOfSquares();
077
078 /** min of values that have been added */
079 private Min min = new Min();
080
081 /** max of values that have been added */
082 private Max max = new Max();
083
084 /** sumLog of values that have been added */
085 private SumOfLogs sumLog = new SumOfLogs();
086
087 /** geoMean of values that have been added */
088 private GeometricMean geoMean = new GeometricMean(sumLog);
089
090 /** mean of values that have been added */
091 private Mean mean = new Mean(secondMoment);
092
093 /** variance of values that have been added */
094 private Variance variance = new Variance(secondMoment);
095
096 /** Sum statistic implementation - can be reset by setter. */
097 private StorelessUnivariateStatistic sumImpl = sum;
098
099 /** Sum of squares statistic implementation - can be reset by setter. */
100 private StorelessUnivariateStatistic sumsqImpl = sumsq;
101
102 /** Minimum statistic implementation - can be reset by setter. */
103 private StorelessUnivariateStatistic minImpl = min;
104
105 /** Maximum statistic implementation - can be reset by setter. */
106 private StorelessUnivariateStatistic maxImpl = max;
107
108 /** Sum of log statistic implementation - can be reset by setter. */
109 private StorelessUnivariateStatistic sumLogImpl = sumLog;
110
111 /** Geometric mean statistic implementation - can be reset by setter. */
112 private StorelessUnivariateStatistic geoMeanImpl = geoMean;
113
114 /** Mean statistic implementation - can be reset by setter. */
115 private StorelessUnivariateStatistic meanImpl = mean;
116
117 /** Variance statistic implementation - can be reset by setter. */
118 private StorelessUnivariateStatistic varianceImpl = variance;
119
120 /**
121 * Construct a SummaryStatistics instance
122 */
123 public SummaryStatistics() {
124 }
125
126 /**
127 * A copy constructor. Creates a deep-copy of the {@code original}.
128 *
129 * @param original the {@code SummaryStatistics} instance to copy
130 * @throws NullArgumentException if original is null
131 */
132 public SummaryStatistics(SummaryStatistics original) throws NullArgumentException {
133 copy(original, this);
134 }
135
136 /**
137 * Return a {@link StatisticalSummaryValues} instance reporting current
138 * statistics.
139 * @return Current values of statistics
140 */
141 public StatisticalSummary getSummary() {
142 return new StatisticalSummaryValues(getMean(), getVariance(), getN(),
143 getMax(), getMin(), getSum());
144 }
145
146 /**
147 * Add a value to the data
148 * @param value the value to add
149 */
150 public void addValue(double value) {
151 sumImpl.increment(value);
152 sumsqImpl.increment(value);
153 minImpl.increment(value);
154 maxImpl.increment(value);
155 sumLogImpl.increment(value);
156 secondMoment.increment(value);
157 // If mean, variance or geomean have been overridden,
158 // need to increment these
159 if (meanImpl != mean) {
160 meanImpl.increment(value);
161 }
162 if (varianceImpl != variance) {
163 varianceImpl.increment(value);
164 }
165 if (geoMeanImpl != geoMean) {
166 geoMeanImpl.increment(value);
167 }
168 n++;
169 }
170
171 /**
172 * Returns the number of available values
173 * @return The number of available values
174 */
175 public long getN() {
176 return n;
177 }
178
179 /**
180 * Returns the sum of the values that have been added
181 * @return The sum or <code>Double.NaN</code> if no values have been added
182 */
183 public double getSum() {
184 return sumImpl.getResult();
185 }
186
187 /**
188 * Returns the sum of the squares of the values that have been added.
189 * <p>
190 * Double.NaN is returned if no values have been added.
191 * </p>
192 * @return The sum of squares
193 */
194 public double getSumsq() {
195 return sumsqImpl.getResult();
196 }
197
198 /**
199 * Returns the mean of the values that have been added.
200 * <p>
201 * Double.NaN is returned if no values have been added.
202 * </p>
203 * @return the mean
204 */
205 public double getMean() {
206 return meanImpl.getResult();
207 }
208
209 /**
210 * Returns the standard deviation of the values that have been added.
211 * <p>
212 * Double.NaN is returned if no values have been added.
213 * </p>
214 * @return the standard deviation
215 */
216 public double getStandardDeviation() {
217 double stdDev = Double.NaN;
218 if (getN() > 0) {
219 if (getN() > 1) {
220 stdDev = FastMath.sqrt(getVariance());
221 } else {
222 stdDev = 0.0;
223 }
224 }
225 return stdDev;
226 }
227
228 /**
229 * Returns the (sample) variance of the available values.
230 *
231 * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in
232 * the denominator). Use {@link #getPopulationVariance()} for the non-bias-corrected
233 * population variance.</p>
234 *
235 * <p>Double.NaN is returned if no values have been added.</p>
236 *
237 * @return the variance
238 */
239 public double getVariance() {
240 return varianceImpl.getResult();
241 }
242
243 /**
244 * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">
245 * population variance</a> of the values that have been added.
246 *
247 * <p>Double.NaN is returned if no values have been added.</p>
248 *
249 * @return the population variance
250 */
251 public double getPopulationVariance() {
252 Variance populationVariance = new Variance(secondMoment);
253 populationVariance.setBiasCorrected(false);
254 return populationVariance.getResult();
255 }
256
257 /**
258 * Returns the maximum of the values that have been added.
259 * <p>
260 * Double.NaN is returned if no values have been added.
261 * </p>
262 * @return the maximum
263 */
264 public double getMax() {
265 return maxImpl.getResult();
266 }
267
268 /**
269 * Returns the minimum of the values that have been added.
270 * <p>
271 * Double.NaN is returned if no values have been added.
272 * </p>
273 * @return the minimum
274 */
275 public double getMin() {
276 return minImpl.getResult();
277 }
278
279 /**
280 * Returns the geometric mean of the values that have been added.
281 * <p>
282 * Double.NaN is returned if no values have been added.
283 * </p>
284 * @return the geometric mean
285 */
286 public double getGeometricMean() {
287 return geoMeanImpl.getResult();
288 }
289
290 /**
291 * Returns the sum of the logs of the values that have been added.
292 * <p>
293 * Double.NaN is returned if no values have been added.
294 * </p>
295 * @return the sum of logs
296 * @since 1.2
297 */
298 public double getSumOfLogs() {
299 return sumLogImpl.getResult();
300 }
301
302 /**
303 * Returns a statistic related to the Second Central Moment. Specifically,
304 * what is returned is the sum of squared deviations from the sample mean
305 * among the values that have been added.
306 * <p>
307 * Returns <code>Double.NaN</code> if no data values have been added and
308 * returns <code>0</code> if there is just one value in the data set.</p>
309 * <p>
310 * @return second central moment statistic
311 * @since 2.0
312 */
313 public double getSecondMoment() {
314 return secondMoment.getResult();
315 }
316
317 /**
318 * Generates a text report displaying summary statistics from values that
319 * have been added.
320 * @return String with line feeds displaying statistics
321 * @since 1.2
322 */
323 @Override
324 public String toString() {
325 StringBuilder outBuffer = new StringBuilder();
326 String endl = "\n";
327 outBuffer.append("SummaryStatistics:").append(endl);
328 outBuffer.append("n: ").append(getN()).append(endl);
329 outBuffer.append("min: ").append(getMin()).append(endl);
330 outBuffer.append("max: ").append(getMax()).append(endl);
331 outBuffer.append("mean: ").append(getMean()).append(endl);
332 outBuffer.append("geometric mean: ").append(getGeometricMean())
333 .append(endl);
334 outBuffer.append("variance: ").append(getVariance()).append(endl);
335 outBuffer.append("sum of squares: ").append(getSumsq()).append(endl);
336 outBuffer.append("standard deviation: ").append(getStandardDeviation())
337 .append(endl);
338 outBuffer.append("sum of logs: ").append(getSumOfLogs()).append(endl);
339 return outBuffer.toString();
340 }
341
342 /**
343 * Resets all statistics and storage
344 */
345 public void clear() {
346 this.n = 0;
347 minImpl.clear();
348 maxImpl.clear();
349 sumImpl.clear();
350 sumLogImpl.clear();
351 sumsqImpl.clear();
352 geoMeanImpl.clear();
353 secondMoment.clear();
354 if (meanImpl != mean) {
355 meanImpl.clear();
356 }
357 if (varianceImpl != variance) {
358 varianceImpl.clear();
359 }
360 }
361
362 /**
363 * Returns true iff <code>object</code> is a
364 * <code>SummaryStatistics</code> instance and all statistics have the
365 * same values as this.
366 * @param object the object to test equality against.
367 * @return true if object equals this
368 */
369 @Override
370 public boolean equals(Object object) {
371 if (object == this) {
372 return true;
373 }
374 if (object instanceof SummaryStatistics == false) {
375 return false;
376 }
377 SummaryStatistics stat = (SummaryStatistics)object;
378 return Precision.equalsIncludingNaN(stat.getGeometricMean(), getGeometricMean()) &&
379 Precision.equalsIncludingNaN(stat.getMax(), getMax()) &&
380 Precision.equalsIncludingNaN(stat.getMean(), getMean()) &&
381 Precision.equalsIncludingNaN(stat.getMin(), getMin()) &&
382 Precision.equalsIncludingNaN(stat.getN(), getN()) &&
383 Precision.equalsIncludingNaN(stat.getSum(), getSum()) &&
384 Precision.equalsIncludingNaN(stat.getSumsq(), getSumsq()) &&
385 Precision.equalsIncludingNaN(stat.getVariance(), getVariance());
386 }
387
388 /**
389 * Returns hash code based on values of statistics
390 * @return hash code
391 */
392 @Override
393 public int hashCode() {
394 int result = 31 + MathUtils.hash(getGeometricMean());
395 result = result * 31 + MathUtils.hash(getGeometricMean());
396 result = result * 31 + MathUtils.hash(getMax());
397 result = result * 31 + MathUtils.hash(getMean());
398 result = result * 31 + MathUtils.hash(getMin());
399 result = result * 31 + MathUtils.hash(getN());
400 result = result * 31 + MathUtils.hash(getSum());
401 result = result * 31 + MathUtils.hash(getSumsq());
402 result = result * 31 + MathUtils.hash(getVariance());
403 return result;
404 }
405
406 // Getters and setters for statistics implementations
407 /**
408 * Returns the currently configured Sum implementation
409 * @return the StorelessUnivariateStatistic implementing the sum
410 * @since 1.2
411 */
412 public StorelessUnivariateStatistic getSumImpl() {
413 return sumImpl;
414 }
415
416 /**
417 * <p>
418 * Sets the implementation for the Sum.
419 * </p>
420 * <p>
421 * This method 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 }