SummaryStatistics.java

  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. import org.apache.commons.math4.legacy.exception.MathIllegalStateException;
  19. import org.apache.commons.math4.legacy.exception.NullArgumentException;
  20. import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
  21. import org.apache.commons.math4.legacy.stat.descriptive.moment.GeometricMean;
  22. import org.apache.commons.math4.legacy.stat.descriptive.moment.Mean;
  23. import org.apache.commons.math4.legacy.stat.descriptive.moment.SecondMoment;
  24. import org.apache.commons.math4.legacy.stat.descriptive.moment.Variance;
  25. import org.apache.commons.math4.legacy.stat.descriptive.rank.Max;
  26. import org.apache.commons.math4.legacy.stat.descriptive.rank.Min;
  27. import org.apache.commons.math4.legacy.stat.descriptive.summary.Sum;
  28. import org.apache.commons.math4.legacy.stat.descriptive.summary.SumOfLogs;
  29. import org.apache.commons.math4.legacy.stat.descriptive.summary.SumOfSquares;
  30. import org.apache.commons.math4.core.jdkmath.JdkMath;
  31. import org.apache.commons.numbers.core.Precision;

  32. /**
  33.  * <p>
  34.  * Computes summary statistics for a stream of data values added using the
  35.  * {@link #addValue(double) addValue} method. The data values are not stored in
  36.  * memory, so this class can be used to compute statistics for very large data
  37.  * streams.
  38.  * </p>
  39.  * <p>
  40.  * The {@link StorelessUnivariateStatistic} instances used to maintain summary
  41.  * state and compute statistics are configurable via setters. For example, the
  42.  * default implementation for the variance can be overridden by calling
  43.  * {@link #setVarianceImpl(StorelessUnivariateStatistic)}. Actual parameters to
  44.  * these methods must implement the {@link StorelessUnivariateStatistic}
  45.  * interface and configuration must be completed before <code>addValue</code>
  46.  * is called. No configuration is necessary to use the default, commons-math
  47.  * provided implementations.
  48.  * </p>
  49.  * <p>
  50.  * Note: This class is not thread-safe. Use
  51.  * {@link SynchronizedSummaryStatistics} if concurrent access from multiple
  52.  * threads is required.
  53.  * </p>
  54.  */
  55. public class SummaryStatistics implements StatisticalSummary {
  56.     /** count of values that have been added. */
  57.     private long n;

  58.     /** SecondMoment is used to compute the mean and variance. */
  59.     private SecondMoment secondMoment = new SecondMoment();

  60.     /** sum of values that have been added. */
  61.     private Sum sum = new Sum();

  62.     /** sum of the square of each value that has been added. */
  63.     private SumOfSquares sumsq = new SumOfSquares();

  64.     /** min of values that have been added. */
  65.     private Min min = new Min();

  66.     /** max of values that have been added. */
  67.     private Max max = new Max();

  68.     /** sumLog of values that have been added. */
  69.     private SumOfLogs sumLog = new SumOfLogs();

  70.     /** geoMean of values that have been added. */
  71.     private GeometricMean geoMean = new GeometricMean(sumLog);

  72.     /** mean of values that have been added. */
  73.     private Mean mean = new Mean(secondMoment);

  74.     /** variance of values that have been added. */
  75.     private Variance variance = new Variance(secondMoment);

  76.     /** Sum statistic implementation - can be reset by setter. */
  77.     private StorelessUnivariateStatistic sumImpl = sum;

  78.     /** Sum of squares statistic implementation - can be reset by setter. */
  79.     private StorelessUnivariateStatistic sumsqImpl = sumsq;

  80.     /** Minimum statistic implementation - can be reset by setter. */
  81.     private StorelessUnivariateStatistic minImpl = min;

  82.     /** Maximum statistic implementation - can be reset by setter. */
  83.     private StorelessUnivariateStatistic maxImpl = max;

  84.     /** Sum of log statistic implementation - can be reset by setter. */
  85.     private StorelessUnivariateStatistic sumLogImpl = sumLog;

  86.     /** Geometric mean statistic implementation - can be reset by setter. */
  87.     private StorelessUnivariateStatistic geoMeanImpl = geoMean;

  88.     /** Mean statistic implementation - can be reset by setter. */
  89.     private StorelessUnivariateStatistic meanImpl = mean;

  90.     /** Variance statistic implementation - can be reset by setter. */
  91.     private StorelessUnivariateStatistic varianceImpl = variance;

  92.     /**
  93.      * Construct a SummaryStatistics instance.
  94.      */
  95.     public SummaryStatistics() {
  96.     }

  97.     /**
  98.      * A copy constructor. Creates a deep-copy of the {@code original}.
  99.      *
  100.      * @param original the {@code SummaryStatistics} instance to copy
  101.      * @throws NullArgumentException if original is null
  102.      */
  103.     public SummaryStatistics(SummaryStatistics original) throws NullArgumentException {
  104.         copy(original, this);
  105.     }

  106.     /**
  107.      * Return a {@link StatisticalSummaryValues} instance reporting current
  108.      * statistics.
  109.      * @return Current values of statistics
  110.      */
  111.     public StatisticalSummary getSummary() {
  112.         return new StatisticalSummaryValues(getMean(), getVariance(), getN(),
  113.                 getMax(), getMin(), getSum());
  114.     }

  115.     /**
  116.      * Add a value to the data.
  117.      * @param value the value to add
  118.      */
  119.     public void addValue(double value) {
  120.         sumImpl.increment(value);
  121.         sumsqImpl.increment(value);
  122.         minImpl.increment(value);
  123.         maxImpl.increment(value);
  124.         sumLogImpl.increment(value);
  125.         secondMoment.increment(value);
  126.         // If mean, variance or geomean have been overridden,
  127.         // need to increment these
  128.         if (meanImpl != mean) {
  129.             meanImpl.increment(value);
  130.         }
  131.         if (varianceImpl != variance) {
  132.             varianceImpl.increment(value);
  133.         }
  134.         if (geoMeanImpl != geoMean) {
  135.             geoMeanImpl.increment(value);
  136.         }
  137.         n++;
  138.     }

  139.     /**
  140.      * Returns the number of available values.
  141.      * @return The number of available values
  142.      */
  143.     @Override
  144.     public long getN() {
  145.         return n;
  146.     }

  147.     /**
  148.      * Returns the sum of the values that have been added.
  149.      * @return The sum or <code>Double.NaN</code> if no values have been added
  150.      */
  151.     @Override
  152.     public double getSum() {
  153.         return sumImpl.getResult();
  154.     }

  155.     /**
  156.      * Returns the sum of the squares of the values that have been added.
  157.      * <p>
  158.      * Double.NaN is returned if no values have been added.
  159.      * </p>
  160.      * @return The sum of squares
  161.      */
  162.     public double getSumsq() {
  163.         return sumsqImpl.getResult();
  164.     }

  165.     /**
  166.      * Returns the mean of the values that have been added.
  167.      * <p>
  168.      * Double.NaN is returned if no values have been added.
  169.      * </p>
  170.      * @return the mean
  171.      */
  172.     @Override
  173.     public double getMean() {
  174.         return meanImpl.getResult();
  175.     }

  176.     /**
  177.      * Returns the standard deviation of the values that have been added.
  178.      * <p>
  179.      * Double.NaN is returned if no values have been added.
  180.      * </p>
  181.      * @return the standard deviation
  182.      */
  183.     @Override
  184.     public double getStandardDeviation() {
  185.         double stdDev = Double.NaN;
  186.         if (getN() > 0) {
  187.             if (getN() > 1) {
  188.                 stdDev = JdkMath.sqrt(getVariance());
  189.             } else {
  190.                 stdDev = 0.0;
  191.             }
  192.         }
  193.         return stdDev;
  194.     }

  195.     /**
  196.      * Returns the quadratic mean, a.k.a.
  197.      * <a href="http://mathworld.wolfram.com/Root-Mean-Square.html">
  198.      * root-mean-square</a> of the available values
  199.      * @return The quadratic mean or {@code Double.NaN} if no values
  200.      * have been added.
  201.      */
  202.     public double getQuadraticMean() {
  203.         final long size = getN();
  204.         return size > 0 ? JdkMath.sqrt(getSumsq() / size) : Double.NaN;
  205.     }

  206.     /**
  207.      * Returns the (sample) variance of the available values.
  208.      *
  209.      * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in
  210.      * the denominator).  Use {@link #getPopulationVariance()} for the non-bias-corrected
  211.      * population variance.</p>
  212.      *
  213.      * <p>Double.NaN is returned if no values have been added.</p>
  214.      *
  215.      * @return the variance
  216.      */
  217.     @Override
  218.     public double getVariance() {
  219.         return varianceImpl.getResult();
  220.     }

  221.     /**
  222.      * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">
  223.      * population variance</a> of the values that have been added.
  224.      *
  225.      * <p>Double.NaN is returned if no values have been added.</p>
  226.      *
  227.      * @return the population variance
  228.      */
  229.     public double getPopulationVariance() {
  230.         Variance populationVariance = new Variance(secondMoment);
  231.         populationVariance.setBiasCorrected(false);
  232.         return populationVariance.getResult();
  233.     }

  234.     /**
  235.      * Returns the maximum of the values that have been added.
  236.      * <p>
  237.      * Double.NaN is returned if no values have been added.
  238.      * </p>
  239.      * @return the maximum
  240.      */
  241.     @Override
  242.     public double getMax() {
  243.         return maxImpl.getResult();
  244.     }

  245.     /**
  246.      * Returns the minimum of the values that have been added.
  247.      * <p>
  248.      * Double.NaN is returned if no values have been added.
  249.      * </p>
  250.      * @return the minimum
  251.      */
  252.     @Override
  253.     public double getMin() {
  254.         return minImpl.getResult();
  255.     }

  256.     /**
  257.      * Returns the geometric mean of the values that have been added.
  258.      * <p>
  259.      * Double.NaN is returned if no values have been added.
  260.      * </p>
  261.      * @return the geometric mean
  262.      */
  263.     public double getGeometricMean() {
  264.         return geoMeanImpl.getResult();
  265.     }

  266.     /**
  267.      * Returns the sum of the logs 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 sum of logs
  272.      * @since 1.2
  273.      */
  274.     public double getSumOfLogs() {
  275.         return sumLogImpl.getResult();
  276.     }

  277.     /**
  278.      * Returns a statistic related to the Second Central Moment.  Specifically,
  279.      * what is returned is the sum of squared deviations from the sample mean
  280.      * among the values that have been added.
  281.      * <p>
  282.      * Returns <code>Double.NaN</code> if no data values have been added and
  283.      * returns <code>0</code> if there is just one value in the data set.
  284.      * </p>
  285.      * @return second central moment statistic
  286.      * @since 2.0
  287.      */
  288.     public double getSecondMoment() {
  289.         return secondMoment.getResult();
  290.     }

  291.     /**
  292.      * Generates a text report displaying summary statistics from values that
  293.      * have been added.
  294.      * @return String with line feeds displaying statistics
  295.      * @since 1.2
  296.      */
  297.     @Override
  298.     public String toString() {
  299.         StringBuilder outBuffer = new StringBuilder();
  300.         String endl = "\n";
  301.         outBuffer.append("SummaryStatistics:").append(endl);
  302.         outBuffer.append("n: ").append(getN()).append(endl);
  303.         outBuffer.append("min: ").append(getMin()).append(endl);
  304.         outBuffer.append("max: ").append(getMax()).append(endl);
  305.         outBuffer.append("sum: ").append(getSum()).append(endl);
  306.         outBuffer.append("mean: ").append(getMean()).append(endl);
  307.         outBuffer.append("geometric mean: ").append(getGeometricMean())
  308.             .append(endl);
  309.         outBuffer.append("variance: ").append(getVariance()).append(endl);
  310.         outBuffer.append("population variance: ").append(getPopulationVariance()).append(endl);
  311.         outBuffer.append("second moment: ").append(getSecondMoment()).append(endl);
  312.         outBuffer.append("sum of squares: ").append(getSumsq()).append(endl);
  313.         outBuffer.append("standard deviation: ").append(getStandardDeviation())
  314.             .append(endl);
  315.         outBuffer.append("sum of logs: ").append(getSumOfLogs()).append(endl);
  316.         return outBuffer.toString();
  317.     }

  318.     /**
  319.      * Resets all statistics and storage.
  320.      */
  321.     public void clear() {
  322.         this.n = 0;
  323.         minImpl.clear();
  324.         maxImpl.clear();
  325.         sumImpl.clear();
  326.         sumLogImpl.clear();
  327.         sumsqImpl.clear();
  328.         geoMeanImpl.clear();
  329.         secondMoment.clear();
  330.         if (meanImpl != mean) {
  331.             meanImpl.clear();
  332.         }
  333.         if (varianceImpl != variance) {
  334.             varianceImpl.clear();
  335.         }
  336.     }

  337.     /**
  338.      * Returns true iff <code>object</code> is a
  339.      * <code>SummaryStatistics</code> instance and all statistics have the
  340.      * same values as this.
  341.      * @param object the object to test equality against.
  342.      * @return true if object equals this
  343.      */
  344.     @Override
  345.     public boolean equals(Object object) {
  346.         if (object == this) {
  347.             return true;
  348.         }
  349.         if (!(object instanceof SummaryStatistics)) {
  350.             return false;
  351.         }
  352.         SummaryStatistics stat = (SummaryStatistics)object;
  353.         return Precision.equalsIncludingNaN(stat.getGeometricMean(), getGeometricMean()) &&
  354.                Precision.equalsIncludingNaN(stat.getMax(),           getMax())           &&
  355.                Precision.equalsIncludingNaN(stat.getMean(),          getMean())          &&
  356.                Precision.equalsIncludingNaN(stat.getMin(),           getMin())           &&
  357.                Precision.equalsIncludingNaN(stat.getN(),             getN())             &&
  358.                Precision.equalsIncludingNaN(stat.getSum(),           getSum())           &&
  359.                Precision.equalsIncludingNaN(stat.getSumsq(),         getSumsq())         &&
  360.                Precision.equalsIncludingNaN(stat.getVariance(),      getVariance());
  361.     }

  362.     /**
  363.      * Returns hash code based on values of statistics.
  364.      * @return hash code
  365.      */
  366.     @Override
  367.     public int hashCode() {
  368.         int result = 31 + Double.hashCode(getGeometricMean());
  369.         result = result * 31 + Double.hashCode(getGeometricMean());
  370.         result = result * 31 + Double.hashCode(getMax());
  371.         result = result * 31 + Double.hashCode(getMean());
  372.         result = result * 31 + Double.hashCode(getMin());
  373.         result = result * 31 + Double.hashCode(getN());
  374.         result = result * 31 + Double.hashCode(getSum());
  375.         result = result * 31 + Double.hashCode(getSumsq());
  376.         result = result * 31 + Double.hashCode(getVariance());
  377.         return result;
  378.     }

  379.     // Getters and setters for statistics implementations
  380.     /**
  381.      * Returns the currently configured Sum implementation.
  382.      * @return the StorelessUnivariateStatistic implementing the sum
  383.      * @since 1.2
  384.      */
  385.     public StorelessUnivariateStatistic getSumImpl() {
  386.         return sumImpl;
  387.     }

  388.     /**
  389.      * <p>
  390.      * Sets the implementation for the Sum.
  391.      * </p>
  392.      * <p>
  393.      * This method cannot be activated after data has been added - i.e.,
  394.      * after {@link #addValue(double) addValue} has been used to add data.
  395.      * If it is activated after data has been added, an IllegalStateException
  396.      * will be thrown.
  397.      * </p>
  398.      * @param sumImpl the StorelessUnivariateStatistic instance to use for
  399.      *        computing the Sum
  400.      * @throws MathIllegalStateException if data has already been added (i.e if n &gt;0)
  401.      * @since 1.2
  402.      */
  403.     public void setSumImpl(StorelessUnivariateStatistic sumImpl)
  404.     throws MathIllegalStateException {
  405.         checkEmpty();
  406.         this.sumImpl = sumImpl;
  407.     }

  408.     /**
  409.      * Returns the currently configured sum of squares implementation.
  410.      * @return the StorelessUnivariateStatistic implementing the sum of squares
  411.      * @since 1.2
  412.      */
  413.     public StorelessUnivariateStatistic getSumsqImpl() {
  414.         return sumsqImpl;
  415.     }

  416.     /**
  417.      * <p>
  418.      * Sets the implementation for the sum of squares.
  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 sumsqImpl the StorelessUnivariateStatistic instance to use for
  427.      *        computing the sum of squares
  428.      * @throws MathIllegalStateException if data has already been added (i.e if n &gt; 0)
  429.      * @since 1.2
  430.      */
  431.     public void setSumsqImpl(StorelessUnivariateStatistic sumsqImpl)
  432.     throws MathIllegalStateException {
  433.         checkEmpty();
  434.         this.sumsqImpl = sumsqImpl;
  435.     }

  436.     /**
  437.      * Returns the currently configured minimum implementation.
  438.      * @return the StorelessUnivariateStatistic implementing the minimum
  439.      * @since 1.2
  440.      */
  441.     public StorelessUnivariateStatistic getMinImpl() {
  442.         return minImpl;
  443.     }

  444.     /**
  445.      * <p>
  446.      * Sets the implementation for the minimum.
  447.      * </p>
  448.      * <p>
  449.      * This method cannot be activated after data has been added - i.e.,
  450.      * after {@link #addValue(double) addValue} has been used to add data.
  451.      * If it is activated after data has been added, an IllegalStateException
  452.      * will be thrown.
  453.      * </p>
  454.      * @param minImpl the StorelessUnivariateStatistic instance to use for
  455.      *        computing the minimum
  456.      * @throws MathIllegalStateException if data has already been added (i.e if n &gt; 0)
  457.      * @since 1.2
  458.      */
  459.     public void setMinImpl(StorelessUnivariateStatistic minImpl)
  460.     throws MathIllegalStateException {
  461.         checkEmpty();
  462.         this.minImpl = minImpl;
  463.     }

  464.     /**
  465.      * Returns the currently configured maximum implementation.
  466.      * @return the StorelessUnivariateStatistic implementing the maximum
  467.      * @since 1.2
  468.      */
  469.     public StorelessUnivariateStatistic getMaxImpl() {
  470.         return maxImpl;
  471.     }

  472.     /**
  473.      * <p>
  474.      * Sets the implementation for the maximum.
  475.      * </p>
  476.      * <p>
  477.      * This method cannot be activated after data has been added - i.e.,
  478.      * after {@link #addValue(double) addValue} has been used to add data.
  479.      * If it is activated after data has been added, an IllegalStateException
  480.      * will be thrown.
  481.      * </p>
  482.      * @param maxImpl the StorelessUnivariateStatistic instance to use for
  483.      *        computing the maximum
  484.      * @throws MathIllegalStateException if data has already been added (i.e if n &gt; 0)
  485.      * @since 1.2
  486.      */
  487.     public void setMaxImpl(StorelessUnivariateStatistic maxImpl)
  488.     throws MathIllegalStateException {
  489.         checkEmpty();
  490.         this.maxImpl = maxImpl;
  491.     }

  492.     /**
  493.      * Returns the currently configured sum of logs implementation.
  494.      * @return the StorelessUnivariateStatistic implementing the log sum
  495.      * @since 1.2
  496.      */
  497.     public StorelessUnivariateStatistic getSumLogImpl() {
  498.         return sumLogImpl;
  499.     }

  500.     /**
  501.      * <p>
  502.      * Sets the implementation for the sum of logs.
  503.      * </p>
  504.      * <p>
  505.      * This method cannot be activated after data has been added - i.e.,
  506.      * after {@link #addValue(double) addValue} has been used to add data.
  507.      * If it is activated after data has been added, an IllegalStateException
  508.      * will be thrown.
  509.      * </p>
  510.      * @param sumLogImpl the StorelessUnivariateStatistic instance to use for
  511.      *        computing the log sum
  512.      * @throws MathIllegalStateException if data has already been added (i.e if n &gt; 0)
  513.      * @since 1.2
  514.      */
  515.     public void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl)
  516.     throws MathIllegalStateException {
  517.         checkEmpty();
  518.         this.sumLogImpl = sumLogImpl;
  519.         geoMean.setSumLogImpl(sumLogImpl);
  520.     }

  521.     /**
  522.      * Returns the currently configured geometric mean implementation.
  523.      * @return the StorelessUnivariateStatistic implementing the geometric mean
  524.      * @since 1.2
  525.      */
  526.     public StorelessUnivariateStatistic getGeoMeanImpl() {
  527.         return geoMeanImpl;
  528.     }

  529.     /**
  530.      * <p>
  531.      * Sets the implementation for the geometric mean.
  532.      * </p>
  533.      * <p>
  534.      * This method cannot be activated after data has been added - i.e.,
  535.      * after {@link #addValue(double) addValue} has been used to add data.
  536.      * If it is activated after data has been added, an IllegalStateException
  537.      * will be thrown.
  538.      * </p>
  539.      * @param geoMeanImpl the StorelessUnivariateStatistic instance to use for
  540.      *        computing the geometric mean
  541.      * @throws MathIllegalStateException if data has already been added (i.e if n &gt; 0)
  542.      * @since 1.2
  543.      */
  544.     public void setGeoMeanImpl(StorelessUnivariateStatistic geoMeanImpl)
  545.     throws MathIllegalStateException {
  546.         checkEmpty();
  547.         this.geoMeanImpl = geoMeanImpl;
  548.     }

  549.     /**
  550.      * Returns the currently configured mean implementation.
  551.      * @return the StorelessUnivariateStatistic implementing the mean
  552.      * @since 1.2
  553.      */
  554.     public StorelessUnivariateStatistic getMeanImpl() {
  555.         return meanImpl;
  556.     }

  557.     /**
  558.      * <p>
  559.      * Sets the implementation for the mean.
  560.      * </p>
  561.      * <p>
  562.      * This method cannot be activated after data has been added - i.e.,
  563.      * after {@link #addValue(double) addValue} has been used to add data.
  564.      * If it is activated after data has been added, an IllegalStateException
  565.      * will be thrown.
  566.      * </p>
  567.      * @param meanImpl the StorelessUnivariateStatistic instance to use for
  568.      *        computing the mean
  569.      * @throws MathIllegalStateException if data has already been added (i.e if n &gt; 0)
  570.      * @since 1.2
  571.      */
  572.     public void setMeanImpl(StorelessUnivariateStatistic meanImpl)
  573.     throws MathIllegalStateException {
  574.         checkEmpty();
  575.         this.meanImpl = meanImpl;
  576.     }

  577.     /**
  578.      * Returns the currently configured variance implementation.
  579.      * @return the StorelessUnivariateStatistic implementing the variance
  580.      * @since 1.2
  581.      */
  582.     public StorelessUnivariateStatistic getVarianceImpl() {
  583.         return varianceImpl;
  584.     }

  585.     /**
  586.      * <p>
  587.      * Sets the implementation for the variance.
  588.      * </p>
  589.      * <p>
  590.      * This method cannot be activated after data has been added - i.e.,
  591.      * after {@link #addValue(double) addValue} has been used to add data.
  592.      * If it is activated after data has been added, an IllegalStateException
  593.      * will be thrown.
  594.      * </p>
  595.      * @param varianceImpl the StorelessUnivariateStatistic instance to use for
  596.      *        computing the variance
  597.      * @throws MathIllegalStateException if data has already been added (i.e if n &gt; 0)
  598.      * @since 1.2
  599.      */
  600.     public void setVarianceImpl(StorelessUnivariateStatistic varianceImpl)
  601.     throws MathIllegalStateException {
  602.         checkEmpty();
  603.         this.varianceImpl = varianceImpl;
  604.     }

  605.     /**
  606.      * Throws IllegalStateException if n > 0.
  607.      * @throws MathIllegalStateException if data has been added
  608.      */
  609.     private void checkEmpty() throws MathIllegalStateException {
  610.         if (n > 0) {
  611.             throw new MathIllegalStateException(
  612.                 LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC, n);
  613.         }
  614.     }

  615.     /**
  616.      * Returns a copy of this SummaryStatistics instance with the same internal state.
  617.      *
  618.      * @return a copy of this
  619.      */
  620.     public SummaryStatistics copy() {
  621.         SummaryStatistics result = new SummaryStatistics();
  622.         // No try-catch or advertised exception because arguments are guaranteed non-null
  623.         copy(this, result);
  624.         return result;
  625.     }

  626.     /**
  627.      * Copies source to dest.
  628.      * <p>Neither source nor dest can be null.</p>
  629.      *
  630.      * @param source SummaryStatistics to copy
  631.      * @param dest SummaryStatistics to copy to
  632.      * @throws NullArgumentException if either source or dest is null
  633.      */
  634.     public static void copy(SummaryStatistics source, SummaryStatistics dest)
  635.         throws NullArgumentException {
  636.         NullArgumentException.check(source);
  637.         NullArgumentException.check(dest);
  638.         dest.maxImpl = source.maxImpl.copy();
  639.         dest.minImpl = source.minImpl.copy();
  640.         dest.sumImpl = source.sumImpl.copy();
  641.         dest.sumLogImpl = source.sumLogImpl.copy();
  642.         dest.sumsqImpl = source.sumsqImpl.copy();
  643.         dest.secondMoment = source.secondMoment.copy();
  644.         dest.n = source.n;

  645.         // Keep commons-math supplied statistics with embedded moments in synch
  646.         if (source.getVarianceImpl() instanceof Variance) {
  647.             dest.varianceImpl = new Variance(dest.secondMoment);
  648.         } else {
  649.             dest.varianceImpl = source.varianceImpl.copy();
  650.         }
  651.         if (source.meanImpl instanceof Mean) {
  652.             dest.meanImpl = new Mean(dest.secondMoment);
  653.         } else {
  654.             dest.meanImpl = source.meanImpl.copy();
  655.         }
  656.         if (source.getGeoMeanImpl() instanceof GeometricMean) {
  657.             dest.geoMeanImpl = new GeometricMean((SumOfLogs) dest.sumLogImpl);
  658.         } else {
  659.             dest.geoMeanImpl = source.geoMeanImpl.copy();
  660.         }

  661.         // Make sure that if stat == statImpl in source, same
  662.         // holds in dest; otherwise copy stat
  663.         if (source.geoMean == source.geoMeanImpl) {
  664.             dest.geoMean = (GeometricMean) dest.geoMeanImpl;
  665.         } else {
  666.             GeometricMean.copy(source.geoMean, dest.geoMean);
  667.         }
  668.         if (source.max == source.maxImpl) {
  669.             dest.max = (Max) dest.maxImpl;
  670.         } else {
  671.             Max.copy(source.max, dest.max);
  672.         }
  673.         if (source.mean == source.meanImpl) {
  674.             dest.mean = (Mean) dest.meanImpl;
  675.         } else {
  676.             Mean.copy(source.mean, dest.mean);
  677.         }
  678.         if (source.min == source.minImpl) {
  679.             dest.min = (Min) dest.minImpl;
  680.         } else {
  681.             Min.copy(source.min, dest.min);
  682.         }
  683.         if (source.sum == source.sumImpl) {
  684.             dest.sum = (Sum) dest.sumImpl;
  685.         } else {
  686.             Sum.copy(source.sum, dest.sum);
  687.         }
  688.         if (source.variance == source.varianceImpl) {
  689.             dest.variance = (Variance) dest.varianceImpl;
  690.         } else {
  691.             Variance.copy(source.variance, dest.variance);
  692.         }
  693.         if (source.sumLog == source.sumLogImpl) {
  694.             dest.sumLog = (SumOfLogs) dest.sumLogImpl;
  695.         } else {
  696.             SumOfLogs.copy(source.sumLog, dest.sumLog);
  697.         }
  698.         if (source.sumsq == source.sumsqImpl) {
  699.             dest.sumsq = (SumOfSquares) dest.sumsqImpl;
  700.         } else {
  701.             SumOfSquares.copy(source.sumsq, dest.sumsq);
  702.         }
  703.     }
  704. }