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 */
017package org.apache.commons.statistics.descriptive;
018
019import java.math.BigInteger;
020import java.util.Objects;
021import java.util.Set;
022import java.util.function.DoubleConsumer;
023import java.util.function.IntConsumer;
024
025/**
026 * Statistics for {@code int} values.
027 *
028 * <p>This class provides combinations of individual statistic implementations in the
029 * {@code org.apache.commons.statistics.descriptive} package.
030 *
031 * <p>Supports up to 2<sup>63</sup> (exclusive) observations.
032 * This implementation does not check for overflow of the count.
033 *
034 * @since 1.1
035 */
036public final class IntStatistics implements IntConsumer {
037    /** Error message for non configured statistics. */
038    private static final String NO_CONFIGURED_STATISTICS = "No configured statistics";
039    /** Error message for an unsupported statistic. */
040    private static final String UNSUPPORTED_STATISTIC = "Unsupported statistic: ";
041
042    /** Count of values recorded. */
043    private long count;
044    /** The consumer of values. */
045    private final IntConsumer consumer;
046    /** The {@link IntMin} implementation. */
047    private final IntMin min;
048    /** The {@link IntMax} implementation. */
049    private final IntMax max;
050    /** The moment implementation. May be any instance of {@link FirstMoment}.
051     * This implementation uses only the third and fourth moments. */
052    private final FirstMoment moment;
053    /** The {@link IntSum} implementation. */
054    private final IntSum sum;
055    /** The {@link Product} implementation. */
056    private final Product product;
057    /** The {@link IntSumOfSquares} implementation. */
058    private final IntSumOfSquares sumOfSquares;
059    /** The {@link SumOfLogs} implementation. */
060    private final SumOfLogs sumOfLogs;
061    /** Configuration options for computation of statistics. */
062    private StatisticsConfiguration config;
063
064    /**
065     * A builder for {@link IntStatistics}.
066     */
067    public static final class Builder {
068        /** An empty double array. */
069        private static final int[] NO_VALUES = {};
070
071        /** The {@link IntMin} constructor. */
072        private RangeFunction<int[], IntMin> min;
073        /** The {@link IntMax} constructor. */
074        private RangeFunction<int[], IntMax> max;
075        /** The moment constructor. May return any instance of {@link FirstMoment}. */
076        private RangeFunction<int[], FirstMoment> moment;
077        /** The {@link IntSum} constructor. */
078        private RangeFunction<int[], IntSum> sum;
079        /** The {@link Product} constructor. */
080        private RangeFunction<int[], Product> product;
081        /** The {@link IntSumOfSquares} constructor. */
082        private RangeFunction<int[], IntSumOfSquares> sumOfSquares;
083        /** The {@link SumOfLogs} constructor. */
084        private RangeFunction<int[], SumOfLogs> sumOfLogs;
085        /** The order of the moment. It corresponds to the power computed by the {@link FirstMoment}
086         * instance constructed by {@link #moment}. This should only be increased from the default
087         * of zero (corresponding to no moment computation). */
088        private int momentOrder;
089        /** Configuration options for computation of statistics. */
090        private StatisticsConfiguration config = StatisticsConfiguration.withDefaults();
091
092        /**
093         * Create an instance.
094         */
095        Builder() {
096            // Do nothing
097        }
098
099        /**
100         * Add the statistic to the statistics to compute.
101         *
102         * @param statistic Statistic to compute.
103         * @return {@code this} instance
104         */
105        Builder add(Statistic statistic) {
106            // Exhaustive switch statement
107            switch (statistic) {
108            case GEOMETRIC_MEAN:
109            case SUM_OF_LOGS:
110                sumOfLogs = SumOfLogs::createFromRange;
111                break;
112            case KURTOSIS:
113                createMoment(4);
114                break;
115            case MAX:
116                max = IntMax::createFromRange;
117                break;
118            case MIN:
119                min = IntMin::createFromRange;
120                break;
121            case PRODUCT:
122                product = Product::createFromRange;
123                break;
124            case SKEWNESS:
125                createMoment(3);
126                break;
127            case STANDARD_DEVIATION:
128            case VARIANCE:
129                sum = IntSum::createFromRange;
130                sumOfSquares = IntSumOfSquares::createFromRange;
131                break;
132            case MEAN:
133            case SUM:
134                sum = IntSum::createFromRange;
135                break;
136            case SUM_OF_SQUARES:
137                sumOfSquares = IntSumOfSquares::createFromRange;
138                break;
139            }
140            return this;
141        }
142
143        /**
144         * Creates the moment constructor for the specified {@code order},
145         * e.g. order=3 is sum of cubed deviations.
146         *
147         * @param order Order.
148         */
149        private void createMoment(int order) {
150            if (order > momentOrder) {
151                momentOrder = order;
152                if (order == 4) {
153                    moment = SumOfFourthDeviations::ofRange;
154                } else {
155                    // Assume order == 3
156                    moment = SumOfCubedDeviations::ofRange;
157                }
158            }
159        }
160
161        /**
162         * Sets the statistics configuration options for computation of statistics.
163         *
164         * @param v Value.
165         * @return the builder
166         * @throws NullPointerException if the value is null
167         */
168        public Builder setConfiguration(StatisticsConfiguration v) {
169            config = Objects.requireNonNull(v);
170            return this;
171        }
172
173        /**
174         * Builds an {@code IntStatistics} instance.
175         *
176         * @return {@code IntStatistics} instance.
177         */
178        public IntStatistics build() {
179            return create(NO_VALUES, 0, 0);
180        }
181
182        /**
183         * Builds an {@code IntStatistics} instance using the input {@code values}.
184         *
185         * <p>Note: {@code IntStatistics} computed using
186         * {@link IntStatistics#accept(int) accept} may be
187         * different from this instance.
188         *
189         * @param values Values.
190         * @return {@code IntStatistics} instance.
191         */
192        public IntStatistics build(int... values) {
193            Objects.requireNonNull(values, "values");
194            return create(values, 0, values.length);
195        }
196
197        /**
198         * Builds an {@code IntStatistics} instance using the specified range of {@code values}.
199         *
200         * <p>Note: {@code IntStatistics} computed using
201         * {@link IntStatistics#accept(int) accept} may be
202         * different from this instance.
203         *
204         * @param values Values.
205         * @param from Inclusive start of the range.
206         * @param to Exclusive end of the range.
207         * @return {@code IntStatistics} instance.
208         * @throws IndexOutOfBoundsException if the sub-range is out of bounds
209         * @since 1.2
210         */
211        public IntStatistics build(int[] values, int from, int to) {
212            Statistics.checkFromToIndex(from, to, values.length);
213            return create(values, from, to);
214        }
215
216        /**
217         * Builds an {@code IntStatistics} instance using the input {@code values}.
218         *
219         * <p>Note: {@code IntStatistics} computed using
220         * {@link IntStatistics#accept(int) accept} may be
221         * different from this instance.
222         *
223         * <p>Warning: No range checks are performed.
224         *
225         * @param values Values.
226         * @param from Inclusive start of the range.
227         * @param to Exclusive end of the range.
228         * @return {@code IntStatistics} instance.
229         */
230        private IntStatistics create(int[] values, int from, int to) {
231            return new IntStatistics(
232                to - from,
233                create(min, values, from, to),
234                create(max, values, from, to),
235                create(moment, values, from, to),
236                create(sum, values, from, to),
237                create(product, values, from, to),
238                create(sumOfSquares, values, from, to),
239                create(sumOfLogs, values, from, to),
240                config);
241        }
242
243        /**
244         * Creates the object from the {@code values}.
245         *
246         * @param <S> value type
247         * @param <T> object type
248         * @param constructor Constructor.
249         * @param values Values
250         * @param from Inclusive start of the range.
251         * @param to Exclusive end of the range.
252         * @return the instance
253         */
254        private static <S, T> T create(RangeFunction<S, T> constructor, S values, int from, int to) {
255            if (constructor != null) {
256                return constructor.apply(values, from, to);
257            }
258            return null;
259        }
260    }
261
262    /**
263     * Create an instance.
264     *
265     * @param count Count of values.
266     * @param min IntMin implementation.
267     * @param max IntMax implementation.
268     * @param moment Moment implementation.
269     * @param sum IntSum implementation.
270     * @param product Product implementation.
271     * @param sumOfSquares Sum of squares implementation.
272     * @param sumOfLogs Sum of logs implementation.
273     * @param config Statistics configuration.
274     */
275    IntStatistics(long count, IntMin min, IntMax max, FirstMoment moment, IntSum sum,
276                  Product product, IntSumOfSquares sumOfSquares, SumOfLogs sumOfLogs,
277                  StatisticsConfiguration config) {
278        this.count = count;
279        this.min = min;
280        this.max = max;
281        this.moment = moment;
282        this.sum = sum;
283        this.product = product;
284        this.sumOfSquares = sumOfSquares;
285        this.sumOfLogs = sumOfLogs;
286        this.config = config;
287        // The final consumer should never be null as the builder is created
288        // with at least one statistic.
289        consumer = Statistics.composeIntConsumers(min, max, sum, sumOfSquares,
290                                                  composeAsInt(moment, product, sumOfLogs));
291    }
292
293    /**
294     * Chain the {@code consumers} into a single composite {@code IntConsumer}.
295     * Ignore any {@code null} consumer.
296     *
297     * @param consumers Consumers.
298     * @return a composed consumer (or null)
299     */
300    private static IntConsumer composeAsInt(DoubleConsumer... consumers) {
301        final DoubleConsumer c = Statistics.composeDoubleConsumers(consumers);
302        if (c != null) {
303            return c::accept;
304        }
305        return null;
306    }
307
308    /**
309     * Returns a new instance configured to compute the specified {@code statistics}.
310     *
311     * <p>The statistics will be empty and so will return the default values for each
312     * computed statistic.
313     *
314     * @param statistics Statistics to compute.
315     * @return the instance
316     * @throws IllegalArgumentException if there are no {@code statistics} to compute.
317     */
318    public static IntStatistics of(Statistic... statistics) {
319        return builder(statistics).build();
320    }
321
322    /**
323     * Returns a new instance configured to compute the specified {@code statistics}
324     * populated using the input {@code values}.
325     *
326     * <p>Use this method to create an instance populated with a (variable) array of
327     * {@code int[]} data:
328     *
329     * <pre>
330     * IntStatistics stats = IntStatistics.of(
331     *     EnumSet.of(Statistic.MIN, Statistic.MAX),
332     *     1, 1, 2, 3, 5, 8, 13);
333     * </pre>
334     *
335     * @param statistics Statistics to compute.
336     * @param values Values.
337     * @return the instance
338     * @throws IllegalArgumentException if there are no {@code statistics} to compute.
339     */
340    public static IntStatistics of(Set<Statistic> statistics, int... values) {
341        if (statistics.isEmpty()) {
342            throw new IllegalArgumentException(NO_CONFIGURED_STATISTICS);
343        }
344        final Builder b = new Builder();
345        statistics.forEach(b::add);
346        return b.build(values);
347    }
348
349    /**
350     * Returns a new instance configured to compute the specified {@code statistics}
351     * populated using the specified range of {@code values}.
352     *
353     * <p>Use this method to create an instance populated with part of an array of
354     * {@code int[]} data, e.g. to use the first half of the data:
355     *
356     * <pre>
357     * int[] data = ...
358     * IntStatistics stats = IntStatistics.of(
359     *     EnumSet.of(Statistic.MIN, Statistic.MAX),
360     *     data, 0, data.length / 2);
361     * </pre>
362     *
363     * @param statistics Statistics to compute.
364     * @param values Values.
365     * @param from Inclusive start of the range.
366     * @param to Exclusive end of the range.
367     * @return the instance
368     * @throws IllegalArgumentException if there are no {@code statistics} to compute.
369     * @throws IndexOutOfBoundsException if the sub-range is out of bounds
370     * @since 1.2
371     */
372    public static IntStatistics ofRange(Set<Statistic> statistics, int[] values, int from, int to) {
373        if (statistics.isEmpty()) {
374            throw new IllegalArgumentException(NO_CONFIGURED_STATISTICS);
375        }
376        final Builder b = new Builder();
377        statistics.forEach(b::add);
378        return b.build(values, from, to);
379    }
380
381    /**
382     * Returns a new builder configured to create instances to compute the specified
383     * {@code statistics}.
384     *
385     * <p>Use this method to create an instance populated with an array of {@code int[]}
386     * data using the {@link Builder#build(int...)} method:
387     *
388     * <pre>
389     * int[] data = ...
390     * IntStatistics stats = IntStatistics.builder(
391     *     Statistic.MIN, Statistic.MAX, Statistic.VARIANCE)
392     *     .build(data);
393     * </pre>
394     *
395     * <p>The builder can be used to create multiple instances of {@link IntStatistics}
396     * to be used in parallel, or on separate arrays of {@code int[]} data. These may
397     * be {@link #combine(IntStatistics) combined}. For example:
398     *
399     * <pre>
400     * int[][] data = ...
401     * IntStatistics.Builder builder = IntStatistics.builder(
402     *     Statistic.MIN, Statistic.MAX, Statistic.VARIANCE);
403     * IntStatistics stats = Arrays.stream(data)
404     *     .parallel()
405     *     .map(builder::build)
406     *     .reduce(IntStatistics::combine)
407     *     .get();
408     * </pre>
409     *
410     * <p>The builder can be used to create a {@link java.util.stream.Collector} for repeat
411     * use on multiple data:
412     *
413     * <pre>{@code
414     * IntStatistics.Builder builder = IntStatistics.builder(
415     *     Statistic.MIN, Statistic.MAX, Statistic.VARIANCE);
416     * Collector<int[], IntStatistics, IntStatistics> collector =
417     *     Collector.of(builder::build,
418     *                  (s, d) -> s.combine(builder.build(d)),
419     *                  IntStatistics::combine);
420     *
421     * // Repeated
422     * int[][] data = ...
423     * IntStatistics stats = Arrays.stream(data).collect(collector);
424     * }</pre>
425     *
426     * @param statistics Statistics to compute.
427     * @return the builder
428     * @throws IllegalArgumentException if there are no {@code statistics} to compute.
429     */
430    public static Builder builder(Statistic... statistics) {
431        if (statistics.length == 0) {
432            throw new IllegalArgumentException(NO_CONFIGURED_STATISTICS);
433        }
434        final Builder b = new Builder();
435        for (final Statistic s : statistics) {
436            b.add(s);
437        }
438        return b;
439    }
440
441    /**
442     * Updates the state of the statistics to reflect the addition of {@code value}.
443     *
444     * @param value Value.
445     */
446    @Override
447    public void accept(int value) {
448        count++;
449        consumer.accept(value);
450    }
451
452    /**
453     * Return the count of values recorded.
454     *
455     * @return the count of values
456     */
457    public long getCount() {
458        return count;
459    }
460
461    /**
462     * Check if the specified {@code statistic} is supported.
463     *
464     * <p>Note: This method will not return {@code false} if the argument is {@code null}.
465     *
466     * @param statistic Statistic.
467     * @return {@code true} if supported
468     * @throws NullPointerException if the {@code statistic} is {@code null}
469     * @see #getResult(Statistic)
470     */
471    public boolean isSupported(Statistic statistic) {
472        // Check for the appropriate underlying implementation
473        // Exhaustive switch statement
474        switch (statistic) {
475        case GEOMETRIC_MEAN:
476        case SUM_OF_LOGS:
477            return sumOfLogs != null;
478        case KURTOSIS:
479            return moment instanceof SumOfFourthDeviations;
480        case MAX:
481            return max != null;
482        case MIN:
483            return min != null;
484        case PRODUCT:
485            return product != null;
486        case SKEWNESS:
487            return moment instanceof SumOfCubedDeviations;
488        case STANDARD_DEVIATION:
489        case VARIANCE:
490            return sum != null && sumOfSquares != null;
491        case MEAN:
492        case SUM:
493            return sum != null;
494        case SUM_OF_SQUARES:
495            return sumOfSquares != null;
496        }
497        // Unreachable code
498        throw new IllegalArgumentException(UNSUPPORTED_STATISTIC + statistic);
499    }
500
501    /**
502     * Gets the value of the specified {@code statistic} as a {@code double}.
503     *
504     * @param statistic Statistic.
505     * @return the value
506     * @throws IllegalArgumentException if the {@code statistic} is not supported
507     * @see #isSupported(Statistic)
508     * @see #getResult(Statistic)
509     */
510    public double getAsDouble(Statistic statistic) {
511        return getResult(statistic).getAsDouble();
512    }
513
514    /**
515     * Gets the value of the specified {@code statistic} as an {@code int}.
516     *
517     * <p>Use this method to access the {@code int} result for exact integer statistics,
518     * for example {@link Statistic#MIN}.
519     *
520     * <p>Note: This method may throw an {@link ArithmeticException} if the result
521     * overflows an {@code int}.
522     *
523     * @param statistic Statistic.
524     * @return the value
525     * @throws IllegalArgumentException if the {@code statistic} is not supported
526     * @throws ArithmeticException if the {@code result} overflows an {@code int} or is not
527     * finite
528     * @see #isSupported(Statistic)
529     * @see #getResult(Statistic)
530     */
531    public int getAsInt(Statistic statistic) {
532        return getResult(statistic).getAsInt();
533    }
534
535    /**
536     * Gets the value of the specified {@code statistic} as a {@code long}.
537     *
538     * <p>Use this method to access the {@code long} result for exact integer statistics,
539     * for example {@link Statistic#SUM} for a {@link #getCount() count} less than or equal to
540     *2<sup>32</sup>.
541     *
542     * <p>Note: This method may throw an {@link ArithmeticException} if the result
543     * overflows an {@code long}.
544     *
545     * @param statistic Statistic.
546     * @return the value
547     * @throws IllegalArgumentException if the {@code statistic} is not supported
548     * @throws ArithmeticException if the {@code result} overflows an {@code long} or is not
549     * finite
550     * @see #isSupported(Statistic)
551     * @see #getResult(Statistic)
552     */
553    public long getAsLong(Statistic statistic) {
554        return getResult(statistic).getAsLong();
555    }
556
557    /**
558     * Gets the value of the specified {@code statistic} as a {@code BigInteger}.
559     *
560     * <p>Use this method to access the {@code BigInteger} result for exact integer statistics,
561     * for example {@link Statistic#SUM_OF_SQUARES}.
562     *
563     * <p>Note: This method may throw an {@link ArithmeticException} if the result
564     * is not finite.
565     *
566     * @param statistic Statistic.
567     * @return the value
568     * @throws IllegalArgumentException if the {@code statistic} is not supported
569     * @throws ArithmeticException if the {@code result} is not finite
570     * @see #isSupported(Statistic)
571     * @see #getResult(Statistic)
572     */
573    public BigInteger getAsBigInteger(Statistic statistic) {
574        return getResult(statistic).getAsBigInteger();
575    }
576
577    /**
578     * Gets a supplier for the value of the specified {@code statistic}.
579     *
580     * <p>The returned function will supply the correct result after
581     * calls to {@link #accept(int) accept} or
582     * {@link #combine(IntStatistics) combine} further values into
583     * {@code this} instance.
584     *
585     * <p>This method can be used to perform a one-time look-up of the statistic
586     * function to compute statistics as values are dynamically added.
587     *
588     * @param statistic Statistic.
589     * @return the supplier
590     * @throws IllegalArgumentException if the {@code statistic} is not supported
591     * @see #isSupported(Statistic)
592     * @see #getAsDouble(Statistic)
593     */
594    public StatisticResult getResult(Statistic statistic) {
595        // Locate the implementation.
596        // Statistics that wrap an underlying implementation are created in methods.
597        // The return argument should be an interface reference and not an instance
598        // of IntStatistic. This ensures the statistic implementation cannot
599        // be updated with new values by casting the result and calling accept(int).
600        StatisticResult stat = null;
601        // Exhaustive switch statement
602        switch (statistic) {
603        case GEOMETRIC_MEAN:
604            stat = getGeometricMean();
605            break;
606        case KURTOSIS:
607            stat = getKurtosis();
608            break;
609        case MAX:
610            stat = Statistics.getResultAsIntOrNull(max);
611            break;
612        case MEAN:
613            stat = getMean();
614            break;
615        case MIN:
616            stat = Statistics.getResultAsIntOrNull(min);
617            break;
618        case PRODUCT:
619            stat = Statistics.getResultAsDoubleOrNull(product);
620            break;
621        case SKEWNESS:
622            stat = getSkewness();
623            break;
624        case STANDARD_DEVIATION:
625            stat = getStandardDeviation();
626            break;
627        case SUM:
628            stat = Statistics.getResultAsBigIntegerOrNull(sum);
629            break;
630        case SUM_OF_LOGS:
631            stat = Statistics.getResultAsDoubleOrNull(sumOfLogs);
632            break;
633        case SUM_OF_SQUARES:
634            stat = Statistics.getResultAsBigIntegerOrNull(sumOfSquares);
635            break;
636        case VARIANCE:
637            stat = getVariance();
638            break;
639        }
640        if (stat != null) {
641            return stat;
642        }
643        throw new IllegalArgumentException(UNSUPPORTED_STATISTIC + statistic);
644    }
645
646    /**
647     * Gets the geometric mean.
648     *
649     * @return a geometric mean supplier (or null if unsupported)
650     */
651    private StatisticResult getGeometricMean() {
652        if (sumOfLogs != null) {
653            // Return a function that has access to the count and sumOfLogs
654            return () -> GeometricMean.computeGeometricMean(count, sumOfLogs);
655        }
656        return null;
657    }
658
659    /**
660     * Gets the kurtosis.
661     *
662     * @return a kurtosis supplier (or null if unsupported)
663     */
664    private StatisticResult getKurtosis() {
665        if (moment instanceof SumOfFourthDeviations) {
666            return new Kurtosis((SumOfFourthDeviations) moment)
667                .setBiased(config.isBiased())::getAsDouble;
668        }
669        return null;
670    }
671
672    /**
673     * Gets the mean.
674     *
675     * @return a mean supplier (or null if unsupported)
676     */
677    private StatisticResult getMean() {
678        if (sum != null) {
679            // Return a function that has access to the count and sum
680            final Int128 s = sum.getSum();
681            return () -> IntMean.computeMean(s, count);
682        }
683        return null;
684    }
685
686    /**
687     * Gets the skewness.
688     *
689     * @return a skewness supplier (or null if unsupported)
690     */
691    private StatisticResult getSkewness() {
692        if (moment instanceof SumOfCubedDeviations) {
693            return new Skewness((SumOfCubedDeviations) moment)
694                .setBiased(config.isBiased())::getAsDouble;
695        }
696        return null;
697    }
698
699    /**
700     * Gets the standard deviation.
701     *
702     * @return a standard deviation supplier (or null if unsupported)
703     */
704    private StatisticResult getStandardDeviation() {
705        return getVarianceOrStd(true);
706    }
707
708    /**
709     * Gets the variance.
710     *
711     * @return a variance supplier (or null if unsupported)
712     */
713    private StatisticResult getVariance() {
714        return getVarianceOrStd(false);
715    }
716
717    /**
718     * Gets the variance or standard deviation.
719     *
720     * @param std Flag to control if the statistic is the standard deviation.
721     * @return a variance/standard deviation supplier (or null if unsupported)
722     */
723    private StatisticResult getVarianceOrStd(boolean std) {
724        if (sum != null && sumOfSquares != null) {
725            // Return a function that has access to the count, sum and sum of squares
726            final Int128 s = sum.getSum();
727            final UInt128 ss = sumOfSquares.getSumOfSquares();
728            final boolean biased = config.isBiased();
729            return () -> IntVariance.computeVarianceOrStd(ss, s, count, biased, std);
730        }
731        return null;
732    }
733
734    /**
735     * Combines the state of the {@code other} statistics into this one.
736     * Only {@code this} instance is modified by the {@code combine} operation.
737     *
738     * <p>The {@code other} instance must be <em>compatible</em>. This is {@code true} if the
739     * {@code other} instance returns {@code true} for {@link #isSupported(Statistic)} for
740     * all values of the {@link Statistic} enum which are supported by {@code this}
741     * instance.
742     *
743     * <p>Note that this operation is <em>not symmetric</em>. It may be possible to perform
744     * {@code a.combine(b)} but not {@code b.combine(a)}. In the event that the {@code other}
745     * instance is not compatible then an exception is raised before any state is modified.
746     *
747     * @param other Another set of statistics to be combined.
748     * @return {@code this} instance after combining {@code other}.
749     * @throws IllegalArgumentException if the {@code other} is not compatible
750     */
751    public IntStatistics combine(IntStatistics other) {
752        // Check compatibility
753        Statistics.checkCombineCompatible(min, other.min);
754        Statistics.checkCombineCompatible(max, other.max);
755        Statistics.checkCombineCompatible(sum, other.sum);
756        Statistics.checkCombineCompatible(product, other.product);
757        Statistics.checkCombineCompatible(sumOfSquares, other.sumOfSquares);
758        Statistics.checkCombineCompatible(sumOfLogs, other.sumOfLogs);
759        Statistics.checkCombineAssignable(moment, other.moment);
760        // Combine
761        count += other.count;
762        Statistics.combine(min, other.min);
763        Statistics.combine(max, other.max);
764        Statistics.combine(sum, other.sum);
765        Statistics.combine(product, other.product);
766        Statistics.combine(sumOfSquares, other.sumOfSquares);
767        Statistics.combine(sumOfLogs, other.sumOfLogs);
768        Statistics.combineMoment(moment, other.moment);
769        return this;
770    }
771
772    /**
773     * Sets the statistics configuration.
774     *
775     * <p>These options only control the final computation of statistics. The configuration
776     * will not affect compatibility between instances during a
777     * {@link #combine(IntStatistics) combine} operation.
778     *
779     * <p>Note: These options will affect any future computation of statistics. Supplier functions
780     * that have been previously created will not be updated with the new configuration.
781     *
782     * @param v Value.
783     * @return {@code this} instance
784     * @throws NullPointerException if the value is null
785     * @see #getResult(Statistic)
786     */
787    public IntStatistics setConfiguration(StatisticsConfiguration v) {
788        config = Objects.requireNonNull(v);
789        return this;
790    }
791}