    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    }