LongStatistics.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.statistics.descriptive;

  18. import java.math.BigInteger;
  19. import java.util.Objects;
  20. import java.util.Set;
  21. import java.util.function.DoubleConsumer;
  22. import java.util.function.LongConsumer;

  23. /**
  24.  * Statistics for {@code long} values.
  25.  *
  26.  * <p>This class provides combinations of individual statistic implementations in the
  27.  * {@code org.apache.commons.statistics.descriptive} package.
  28.  *
  29.  * <p>Supports up to 2<sup>63</sup> (exclusive) observations.
  30.  * This implementation does not check for overflow of the count.
  31.  *
  32.  * @since 1.1
  33.  */
  34. public final class LongStatistics implements LongConsumer {
  35.     /** Error message for non configured statistics. */
  36.     private static final String NO_CONFIGURED_STATISTICS = "No configured statistics";
  37.     /** Error message for an unsupported statistic. */
  38.     private static final String UNSUPPORTED_STATISTIC = "Unsupported statistic: ";

  39.     /** Count of values recorded. */
  40.     private long count;
  41.     /** The consumer of values. */
  42.     private final LongConsumer consumer;
  43.     /** The {@link LongMin} implementation. */
  44.     private final LongMin min;
  45.     /** The {@link LongMax} implementation. */
  46.     private final LongMax max;
  47.     /** The moment implementation. May be any instance of {@link FirstMoment}.
  48.      * This implementation uses only the third and fourth moments. */
  49.     private final FirstMoment moment;
  50.     /** The {@link LongSum} implementation. */
  51.     private final LongSum sum;
  52.     /** The {@link Product} implementation. */
  53.     private final Product product;
  54.     /** The {@link LongSumOfSquares} implementation. */
  55.     private final LongSumOfSquares sumOfSquares;
  56.     /** The {@link SumOfLogs} implementation. */
  57.     private final SumOfLogs sumOfLogs;
  58.     /** Configuration options for computation of statistics. */
  59.     private StatisticsConfiguration config;

  60.     /**
  61.      * A builder for {@link LongStatistics}.
  62.      */
  63.     public static final class Builder {
  64.         /** An empty double array. */
  65.         private static final long[] NO_VALUES = {};

  66.         /** The {@link LongMin} constructor. */
  67.         private RangeFunction<long[], LongMin> min;
  68.         /** The {@link LongMax} constructor. */
  69.         private RangeFunction<long[], LongMax> max;
  70.         /** The moment constructor. May return any instance of {@link FirstMoment}. */
  71.         private RangeFunction<long[], FirstMoment> moment;
  72.         /** The {@link LongSum} constructor. */
  73.         private RangeFunction<long[], LongSum> sum;
  74.         /** The {@link Product} constructor. */
  75.         private RangeFunction<long[], Product> product;
  76.         /** The {@link LongSumOfSquares} constructor. */
  77.         private RangeFunction<long[], LongSumOfSquares> sumOfSquares;
  78.         /** The {@link SumOfLogs} constructor. */
  79.         private RangeFunction<long[], SumOfLogs> sumOfLogs;
  80.         /** The order of the moment. It corresponds to the power computed by the {@link FirstMoment}
  81.          * instance constructed by {@link #moment}. This should only be increased from the default
  82.          * of zero (corresponding to no moment computation). */
  83.         private int momentOrder;
  84.         /** Configuration options for computation of statistics. */
  85.         private StatisticsConfiguration config = StatisticsConfiguration.withDefaults();

  86.         /**
  87.          * Create an instance.
  88.          */
  89.         Builder() {
  90.             // Do nothing
  91.         }

  92.         /**
  93.          * Add the statistic to the statistics to compute.
  94.          *
  95.          * @param statistic Statistic to compute.
  96.          * @return {@code this} instance
  97.          */
  98.         Builder add(Statistic statistic) {
  99.             // Exhaustive switch statement
  100.             switch (statistic) {
  101.             case GEOMETRIC_MEAN:
  102.             case SUM_OF_LOGS:
  103.                 sumOfLogs = SumOfLogs::createFromRange;
  104.                 break;
  105.             case KURTOSIS:
  106.                 createMoment(4);
  107.                 break;
  108.             case MAX:
  109.                 max = LongMax::createFromRange;
  110.                 break;
  111.             case MIN:
  112.                 min = LongMin::createFromRange;
  113.                 break;
  114.             case PRODUCT:
  115.                 product = Product::createFromRange;
  116.                 break;
  117.             case SKEWNESS:
  118.                 createMoment(3);
  119.                 break;
  120.             case STANDARD_DEVIATION:
  121.             case VARIANCE:
  122.                 sum = LongSum::createFromRange;
  123.                 sumOfSquares = LongSumOfSquares::createFromRange;
  124.                 break;
  125.             case MEAN:
  126.             case SUM:
  127.                 sum = LongSum::createFromRange;
  128.                 break;
  129.             case SUM_OF_SQUARES:
  130.                 sumOfSquares = LongSumOfSquares::createFromRange;
  131.                 break;
  132.             }
  133.             return this;
  134.         }

  135.         /**
  136.          * Creates the moment constructor for the specified {@code order},
  137.          * e.g. order=3 is sum of cubed deviations.
  138.          *
  139.          * @param order Order.
  140.          */
  141.         private void createMoment(int order) {
  142.             if (order > momentOrder) {
  143.                 momentOrder = order;
  144.                 if (order == 4) {
  145.                     moment = SumOfFourthDeviations::ofRange;
  146.                 } else {
  147.                     // Assume order == 3
  148.                     moment = SumOfCubedDeviations::ofRange;
  149.                 }
  150.             }
  151.         }

  152.         /**
  153.          * Sets the statistics configuration options for computation of statistics.
  154.          *
  155.          * @param v Value.
  156.          * @return the builder
  157.          * @throws NullPointerException if the value is null
  158.          */
  159.         public Builder setConfiguration(StatisticsConfiguration v) {
  160.             config = Objects.requireNonNull(v);
  161.             return this;
  162.         }

  163.         /**
  164.          * Builds a {@code LongStatistics} instance.
  165.          *
  166.          * @return {@code LongStatistics} instance.
  167.          */
  168.         public LongStatistics build() {
  169.             return create(NO_VALUES, 0, 0);
  170.         }

  171.         /**
  172.          * Builds a {@code LongStatistics} instance using the input {@code values}.
  173.          *
  174.          * <p>Note: {@code LongStatistics} computed using
  175.          * {@link LongStatistics#accept(long) accept} may be
  176.          * different from this instance.
  177.          *
  178.          * @param values Values.
  179.          * @return {@code LongStatistics} instance.
  180.          */
  181.         public LongStatistics build(long... values) {
  182.             Objects.requireNonNull(values, "values");
  183.             return create(values, 0, values.length);
  184.         }

  185.         /**
  186.          * Builds a {@code LongStatistics} instance using the specified range of {@code values}.
  187.          *
  188.          * <p>Note: {@code LongStatistics} computed using
  189.          * {@link LongStatistics#accept(long) accept} may be
  190.          * different from this instance.
  191.          *
  192.          * @param values Values.
  193.          * @param from Inclusive start of the range.
  194.          * @param to Exclusive end of the range.
  195.          * @return {@code LongStatistics} instance.
  196.          * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  197.          * @since 1.2
  198.          */
  199.         public LongStatistics build(long[] values, int from, int to) {
  200.             Statistics.checkFromToIndex(from, to, values.length);
  201.             return create(values, from, to);
  202.         }

  203.         /**
  204.          * Builds a {@code LongStatistics} instance using the input {@code values}.
  205.          *
  206.          * <p>Note: {@code LongStatistics} computed using
  207.          * {@link LongStatistics#accept(long) accept} may be
  208.          * different from this instance.
  209.          *
  210.          * <p>Warning: No range checks are performed.
  211.          *
  212.          * @param values Values.
  213.          * @param from Inclusive start of the range.
  214.          * @param to Exclusive end of the range.
  215.          * @return {@code LongStatistics} instance.
  216.          */
  217.         private LongStatistics create(long[] values, int from, int to) {
  218.             return new LongStatistics(
  219.                 to - from,
  220.                 create(min, values, from, to),
  221.                 create(max, values, from, to),
  222.                 create(moment, values, from, to),
  223.                 create(sum, values, from, to),
  224.                 create(product, values, from, to),
  225.                 create(sumOfSquares, values, from, to),
  226.                 create(sumOfLogs, values, from, to),
  227.                 config);
  228.         }

  229.         /**
  230.          * Creates the object from the {@code values}.
  231.          *
  232.          * @param <S> value type
  233.          * @param <T> object type
  234.          * @param constructor Constructor.
  235.          * @param values Values
  236.          * @param from Inclusive start of the range.
  237.          * @param to Exclusive end of the range.
  238.          * @return the instance
  239.          */
  240.         private static <S, T> T create(RangeFunction<S, T> constructor, S values, int from, int to) {
  241.             if (constructor != null) {
  242.                 return constructor.apply(values, from, to);
  243.             }
  244.             return null;
  245.         }
  246.     }

  247.     /**
  248.      * Create an instance.
  249.      *
  250.      * @param count Count of values.
  251.      * @param min LongMin implementation.
  252.      * @param max LongMax implementation.
  253.      * @param moment Moment implementation.
  254.      * @param sum LongSum implementation.
  255.      * @param product Product implementation.
  256.      * @param sumOfSquares Sum of squares implementation.
  257.      * @param sumOfLogs Sum of logs implementation.
  258.      * @param config Statistics configuration.
  259.      */
  260.     LongStatistics(long count, LongMin min, LongMax max, FirstMoment moment, LongSum sum,
  261.                   Product product, LongSumOfSquares sumOfSquares, SumOfLogs sumOfLogs,
  262.                   StatisticsConfiguration config) {
  263.         this.count = count;
  264.         this.min = min;
  265.         this.max = max;
  266.         this.moment = moment;
  267.         this.sum = sum;
  268.         this.product = product;
  269.         this.sumOfSquares = sumOfSquares;
  270.         this.sumOfLogs = sumOfLogs;
  271.         this.config = config;
  272.         // The final consumer should never be null as the builder is created
  273.         // with at least one statistic.
  274.         consumer = Statistics.composeLongConsumers(min, max, sum, sumOfSquares,
  275.                                                    composeAsLong(moment, product, sumOfLogs));
  276.     }

  277.     /**
  278.      * Chain the {@code consumers} into a single composite {@code LongConsumer}.
  279.      * Ignore any {@code null} consumer.
  280.      *
  281.      * @param consumers Consumers.
  282.      * @return a composed consumer (or null)
  283.      */
  284.     private static LongConsumer composeAsLong(DoubleConsumer... consumers) {
  285.         final DoubleConsumer c = Statistics.composeDoubleConsumers(consumers);
  286.         if (c != null) {
  287.             return c::accept;
  288.         }
  289.         return null;
  290.     }

  291.     /**
  292.      * Returns a new instance configured to compute the specified {@code statistics}.
  293.      *
  294.      * <p>The statistics will be empty and so will return the default values for each
  295.      * computed statistic.
  296.      *
  297.      * @param statistics Statistics to compute.
  298.      * @return the instance
  299.      * @throws IllegalArgumentException if there are no {@code statistics} to compute.
  300.      */
  301.     public static LongStatistics of(Statistic... statistics) {
  302.         return builder(statistics).build();
  303.     }

  304.     /**
  305.      * Returns a new instance configured to compute the specified {@code statistics}
  306.      * populated using the input {@code values}.
  307.      *
  308.      * <p>Use this method to create an instance populated with a (variable) array of
  309.      * {@code long[]} data:
  310.      *
  311.      * <pre>
  312.      * LongStatistics stats = LongStatistics.of(
  313.      *     EnumSet.of(Statistic.MIN, Statistic.MAX),
  314.      *     1, 1, 2, 3, 5, 8, 13);
  315.      * </pre>
  316.      *
  317.      * @param statistics Statistics to compute.
  318.      * @param values Values.
  319.      * @return the instance
  320.      * @throws IllegalArgumentException if there are no {@code statistics} to compute.
  321.      */
  322.     public static LongStatistics of(Set<Statistic> statistics, long... values) {
  323.         if (statistics.isEmpty()) {
  324.             throw new IllegalArgumentException(NO_CONFIGURED_STATISTICS);
  325.         }
  326.         final Builder b = new Builder();
  327.         statistics.forEach(b::add);
  328.         return b.build(values);
  329.     }

  330.     /**
  331.      * Returns a new instance configured to compute the specified {@code statistics}
  332.      * populated using the specified range of {@code values}.
  333.      *
  334.      * <p>Use this method to create an instance populated with part of an array of
  335.      * {@code long[]} data, e.g. to use the first half of the data:
  336.      *
  337.      * <pre>
  338.      * long[] data = ...
  339.      * LongStatistics stats = LongStatistics.of(
  340.      *     EnumSet.of(Statistic.MIN, Statistic.MAX),
  341.      *     data, 0, data.length / 2);
  342.      * </pre>
  343.      *
  344.      * @param statistics Statistics to compute.
  345.      * @param values Values.
  346.      * @param from Inclusive start of the range.
  347.      * @param to Exclusive end of the range.
  348.      * @return the instance
  349.      * @throws IllegalArgumentException if there are no {@code statistics} to compute.
  350.      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
  351.      * @since 1.2
  352.      */
  353.     public static LongStatistics ofRange(Set<Statistic> statistics, long[] values, int from, int to) {
  354.         if (statistics.isEmpty()) {
  355.             throw new IllegalArgumentException(NO_CONFIGURED_STATISTICS);
  356.         }
  357.         final Builder b = new Builder();
  358.         statistics.forEach(b::add);
  359.         return b.build(values, from, to);
  360.     }

  361.     /**
  362.      * Returns a new builder configured to create instances to compute the specified
  363.      * {@code statistics}.
  364.      *
  365.      * <p>Use this method to create an instance populated with an array of {@code long[]}
  366.      * data using the {@link Builder#build(long...)} method:
  367.      *
  368.      * <pre>
  369.      * long[] data = ...
  370.      * LongStatistics stats = LongStatistics.builder(
  371.      *     Statistic.MIN, Statistic.MAX, Statistic.VARIANCE)
  372.      *     .build(data);
  373.      * </pre>
  374.      *
  375.      * <p>The builder can be used to create multiple instances of {@link LongStatistics}
  376.      * to be used in parallel, or on separate arrays of {@code long[]} data. These may
  377.      * be {@link #combine(LongStatistics) combined}. For example:
  378.      *
  379.      * <pre>
  380.      * long[][] data = ...
  381.      * LongStatistics.Builder builder = LongStatistics.builder(
  382.      *     Statistic.MIN, Statistic.MAX, Statistic.VARIANCE);
  383.      * LongStatistics stats = Arrays.stream(data)
  384.      *     .parallel()
  385.      *     .map(builder::build)
  386.      *     .reduce(LongStatistics::combine)
  387.      *     .get();
  388.      * </pre>
  389.      *
  390.      * <p>The builder can be used to create a {@link java.util.stream.Collector} for repeat
  391.      * use on multiple data:
  392.      *
  393.      * <pre>{@code
  394.      * LongStatistics.Builder builder = LongStatistics.builder(
  395.      *     Statistic.MIN, Statistic.MAX, Statistic.VARIANCE);
  396.      * Collector<long[], LongStatistics, LongStatistics> collector =
  397.      *     Collector.of(builder::build,
  398.      *                  (s, d) -> s.combine(builder.build(d)),
  399.      *                  LongStatistics::combine);
  400.      *
  401.      * // Repeated
  402.      * long[][] data = ...
  403.      * LongStatistics stats = Arrays.stream(data).collect(collector);
  404.      * }</pre>
  405.      *
  406.      * @param statistics Statistics to compute.
  407.      * @return the builder
  408.      * @throws IllegalArgumentException if there are no {@code statistics} to compute.
  409.      */
  410.     public static Builder builder(Statistic... statistics) {
  411.         if (statistics.length == 0) {
  412.             throw new IllegalArgumentException(NO_CONFIGURED_STATISTICS);
  413.         }
  414.         final Builder b = new Builder();
  415.         for (final Statistic s : statistics) {
  416.             b.add(s);
  417.         }
  418.         return b;
  419.     }

  420.     /**
  421.      * Updates the state of the statistics to reflect the addition of {@code value}.
  422.      *
  423.      * @param value Value.
  424.      */
  425.     @Override
  426.     public void accept(long value) {
  427.         count++;
  428.         consumer.accept(value);
  429.     }

  430.     /**
  431.      * Return the count of values recorded.
  432.      *
  433.      * @return the count of values
  434.      */
  435.     public long getCount() {
  436.         return count;
  437.     }

  438.     /**
  439.      * Check if the specified {@code statistic} is supported.
  440.      *
  441.      * <p>Note: This method will not return {@code false} if the argument is {@code null}.
  442.      *
  443.      * @param statistic Statistic.
  444.      * @return {@code true} if supported
  445.      * @throws NullPointerException if the {@code statistic} is {@code null}
  446.      * @see #getResult(Statistic)
  447.      */
  448.     public boolean isSupported(Statistic statistic) {
  449.         // Check for the appropriate underlying implementation
  450.         // Exhaustive switch statement
  451.         switch (statistic) {
  452.         case GEOMETRIC_MEAN:
  453.         case SUM_OF_LOGS:
  454.             return sumOfLogs != null;
  455.         case KURTOSIS:
  456.             return moment instanceof SumOfFourthDeviations;
  457.         case MAX:
  458.             return max != null;
  459.         case MIN:
  460.             return min != null;
  461.         case PRODUCT:
  462.             return product != null;
  463.         case SKEWNESS:
  464.             return moment instanceof SumOfCubedDeviations;
  465.         case STANDARD_DEVIATION:
  466.         case VARIANCE:
  467.             return sum != null && sumOfSquares != null;
  468.         case MEAN:
  469.         case SUM:
  470.             return sum != null;
  471.         case SUM_OF_SQUARES:
  472.             return sumOfSquares != null;
  473.         }
  474.         // Unreachable code
  475.         throw new IllegalArgumentException(UNSUPPORTED_STATISTIC + statistic);
  476.     }

  477.     /**
  478.      * Gets the value of the specified {@code statistic} as a {@code double}.
  479.      *
  480.      * @param statistic Statistic.
  481.      * @return the value
  482.      * @throws IllegalArgumentException if the {@code statistic} is not supported
  483.      * @see #isSupported(Statistic)
  484.      * @see #getResult(Statistic)
  485.      */
  486.     public double getAsDouble(Statistic statistic) {
  487.         return getResult(statistic).getAsDouble();
  488.     }

  489.     /**
  490.      * Gets the value of the specified {@code statistic} as a {@code long}.
  491.      *
  492.      * <p>Use this method to access the {@code long} result for exact integer statistics,
  493.      * for example {@link Statistic#MIN}.
  494.      *
  495.      * <p>Note: This method may throw an {@link ArithmeticException} if the result
  496.      * overflows an {@code long}.
  497.      *
  498.      * @param statistic Statistic.
  499.      * @return the value
  500.      * @throws IllegalArgumentException if the {@code statistic} is not supported
  501.      * @throws ArithmeticException if the {@code result} overflows an {@code long} or is not
  502.      * finite
  503.      * @see #isSupported(Statistic)
  504.      * @see #getResult(Statistic)
  505.      */
  506.     public long getAsLong(Statistic statistic) {
  507.         return getResult(statistic).getAsLong();
  508.     }

  509.     /**
  510.      * Gets the value of the specified {@code statistic} as a {@code BigInteger}.
  511.      *
  512.      * <p>Use this method to access the {@code BigInteger} result for exact integer statistics,
  513.      * for example {@link Statistic#SUM_OF_SQUARES}.
  514.      *
  515.      * <p>Note: This method may throw an {@link ArithmeticException} if the result
  516.      * is not finite.
  517.      *
  518.      * @param statistic Statistic.
  519.      * @return the value
  520.      * @throws IllegalArgumentException if the {@code statistic} is not supported
  521.      * @throws ArithmeticException if the {@code result} is not finite
  522.      * @see #isSupported(Statistic)
  523.      * @see #getResult(Statistic)
  524.      */
  525.     public BigInteger getAsBigInteger(Statistic statistic) {
  526.         return getResult(statistic).getAsBigInteger();
  527.     }

  528.     /**
  529.      * Gets a supplier for the value of the specified {@code statistic}.
  530.      *
  531.      * <p>The returned function will supply the correct result after
  532.      * calls to {@link #accept(long) accept} or
  533.      * {@link #combine(LongStatistics) combine} further values into
  534.      * {@code this} instance.
  535.      *
  536.      * <p>This method can be used to perform a one-time look-up of the statistic
  537.      * function to compute statistics as values are dynamically added.
  538.      *
  539.      * @param statistic Statistic.
  540.      * @return the supplier
  541.      * @throws IllegalArgumentException if the {@code statistic} is not supported
  542.      * @see #isSupported(Statistic)
  543.      * @see #getAsDouble(Statistic)
  544.      */
  545.     public StatisticResult getResult(Statistic statistic) {
  546.         // Locate the implementation.
  547.         // Statistics that wrap an underlying implementation are created in methods.
  548.         // The return argument should be an interface reference and not an instance
  549.         // of LongStatistic. This ensures the statistic implementation cannot
  550.         // be updated with new values by casting the result and calling accept(long).
  551.         StatisticResult stat = null;
  552.         // Exhaustive switch statement
  553.         switch (statistic) {
  554.         case GEOMETRIC_MEAN:
  555.             stat = getGeometricMean();
  556.             break;
  557.         case KURTOSIS:
  558.             stat = getKurtosis();
  559.             break;
  560.         case MAX:
  561.             stat = Statistics.getResultAsLongOrNull(max);
  562.             break;
  563.         case MEAN:
  564.             stat = getMean();
  565.             break;
  566.         case MIN:
  567.             stat = Statistics.getResultAsLongOrNull(min);
  568.             break;
  569.         case PRODUCT:
  570.             stat = Statistics.getResultAsDoubleOrNull(product);
  571.             break;
  572.         case SKEWNESS:
  573.             stat = getSkewness();
  574.             break;
  575.         case STANDARD_DEVIATION:
  576.             stat = getStandardDeviation();
  577.             break;
  578.         case SUM:
  579.             stat = Statistics.getResultAsBigIntegerOrNull(sum);
  580.             break;
  581.         case SUM_OF_LOGS:
  582.             stat = Statistics.getResultAsDoubleOrNull(sumOfLogs);
  583.             break;
  584.         case SUM_OF_SQUARES:
  585.             stat = Statistics.getResultAsBigIntegerOrNull(sumOfSquares);
  586.             break;
  587.         case VARIANCE:
  588.             stat = getVariance();
  589.             break;
  590.         }
  591.         if (stat != null) {
  592.             return stat;
  593.         }
  594.         throw new IllegalArgumentException(UNSUPPORTED_STATISTIC + statistic);
  595.     }

  596.     /**
  597.      * Gets the geometric mean.
  598.      *
  599.      * @return a geometric mean supplier (or null if unsupported)
  600.      */
  601.     private StatisticResult getGeometricMean() {
  602.         if (sumOfLogs != null) {
  603.             // Return a function that has access to the count and sumOfLogs
  604.             return () -> GeometricMean.computeGeometricMean(count, sumOfLogs);
  605.         }
  606.         return null;
  607.     }

  608.     /**
  609.      * Gets the kurtosis.
  610.      *
  611.      * @return a kurtosis supplier (or null if unsupported)
  612.      */
  613.     private StatisticResult getKurtosis() {
  614.         if (moment instanceof SumOfFourthDeviations) {
  615.             return new Kurtosis((SumOfFourthDeviations) moment)
  616.                 .setBiased(config.isBiased())::getAsDouble;
  617.         }
  618.         return null;
  619.     }

  620.     /**
  621.      * Gets the mean.
  622.      *
  623.      * @return a mean supplier (or null if unsupported)
  624.      */
  625.     private StatisticResult getMean() {
  626.         if (sum != null) {
  627.             // Return a function that has access to the count and sum
  628.             final Int128 s = sum.getSum();
  629.             return () -> LongMean.computeMean(s, count);
  630.         }
  631.         return null;
  632.     }

  633.     /**
  634.      * Gets the skewness.
  635.      *
  636.      * @return a skewness supplier (or null if unsupported)
  637.      */
  638.     private StatisticResult getSkewness() {
  639.         if (moment instanceof SumOfCubedDeviations) {
  640.             return new Skewness((SumOfCubedDeviations) moment)
  641.                 .setBiased(config.isBiased())::getAsDouble;
  642.         }
  643.         return null;
  644.     }

  645.     /**
  646.      * Gets the standard deviation.
  647.      *
  648.      * @return a standard deviation supplier (or null if unsupported)
  649.      */
  650.     private StatisticResult getStandardDeviation() {
  651.         return getVarianceOrStd(true);
  652.     }

  653.     /**
  654.      * Gets the variance.
  655.      *
  656.      * @return a variance supplier (or null if unsupported)
  657.      */
  658.     private StatisticResult getVariance() {
  659.         return getVarianceOrStd(false);
  660.     }

  661.     /**
  662.      * Gets the variance or standard deviation.
  663.      *
  664.      * @param std Flag to control if the statistic is the standard deviation.
  665.      * @return a variance/standard deviation supplier (or null if unsupported)
  666.      */
  667.     private StatisticResult getVarianceOrStd(boolean std) {
  668.         if (sum != null && sumOfSquares != null) {
  669.             // Return a function that has access to the count, sum and sum of squares
  670.             final Int128 s = sum.getSum();
  671.             final UInt192 ss = sumOfSquares.getSumOfSquares();
  672.             final boolean biased = config.isBiased();
  673.             return () -> LongVariance.computeVarianceOrStd(ss, s, count, biased, std);
  674.         }
  675.         return null;
  676.     }

  677.     /**
  678.      * Combines the state of the {@code other} statistics into this one.
  679.      * Only {@code this} instance is modified by the {@code combine} operation.
  680.      *
  681.      * <p>The {@code other} instance must be <em>compatible</em>. This is {@code true} if the
  682.      * {@code other} instance returns {@code true} for {@link #isSupported(Statistic)} for
  683.      * all values of the {@link Statistic} enum which are supported by {@code this}
  684.      * instance.
  685.      *
  686.      * <p>Note that this operation is <em>not symmetric</em>. It may be possible to perform
  687.      * {@code a.combine(b)} but not {@code b.combine(a)}. In the event that the {@code other}
  688.      * instance is not compatible then an exception is raised before any state is modified.
  689.      *
  690.      * @param other Another set of statistics to be combined.
  691.      * @return {@code this} instance after combining {@code other}.
  692.      * @throws IllegalArgumentException if the {@code other} is not compatible
  693.      */
  694.     public LongStatistics combine(LongStatistics other) {
  695.         // Check compatibility
  696.         Statistics.checkCombineCompatible(min, other.min);
  697.         Statistics.checkCombineCompatible(max, other.max);
  698.         Statistics.checkCombineCompatible(sum, other.sum);
  699.         Statistics.checkCombineCompatible(product, other.product);
  700.         Statistics.checkCombineCompatible(sumOfSquares, other.sumOfSquares);
  701.         Statistics.checkCombineCompatible(sumOfLogs, other.sumOfLogs);
  702.         Statistics.checkCombineAssignable(moment, other.moment);
  703.         // Combine
  704.         count += other.count;
  705.         Statistics.combine(min, other.min);
  706.         Statistics.combine(max, other.max);
  707.         Statistics.combine(sum, other.sum);
  708.         Statistics.combine(product, other.product);
  709.         Statistics.combine(sumOfSquares, other.sumOfSquares);
  710.         Statistics.combine(sumOfLogs, other.sumOfLogs);
  711.         Statistics.combineMoment(moment, other.moment);
  712.         return this;
  713.     }

  714.     /**
  715.      * Sets the statistics configuration.
  716.      *
  717.      * <p>These options only control the final computation of statistics. The configuration
  718.      * will not affect compatibility between instances during a
  719.      * {@link #combine(LongStatistics) combine} operation.
  720.      *
  721.      * <p>Note: These options will affect any future computation of statistics. Supplier functions
  722.      * that have been previously created will not be updated with the new configuration.
  723.      *
  724.      * @param v Value.
  725.      * @return {@code this} instance
  726.      * @throws NullPointerException if the value is null
  727.      * @see #getResult(Statistic)
  728.      */
  729.     public LongStatistics setConfiguration(StatisticsConfiguration v) {
  730.         config = Objects.requireNonNull(v);
  731.         return this;
  732.     }
  733. }