View Javadoc
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  
19  import java.math.BigInteger;
20  import java.util.Objects;
21  import java.util.Set;
22  import java.util.function.DoubleConsumer;
23  import java.util.function.IntConsumer;
24  
25  /**
26   * Statistics for {@code int} values.
27   *
28   * <p>This class provides combinations of individual statistic implementations in the
29   * {@code org.apache.commons.statistics.descriptive} package.
30   *
31   * <p>Supports up to 2<sup>63</sup> (exclusive) observations.
32   * This implementation does not check for overflow of the count.
33   *
34   * @since 1.1
35   */
36  public final class IntStatistics implements IntConsumer {
37      /** Error message for non configured statistics. */
38      private static final String NO_CONFIGURED_STATISTICS = "No configured statistics";
39      /** Error message for an unsupported statistic. */
40      private static final String UNSUPPORTED_STATISTIC = "Unsupported statistic: ";
41  
42      /** Count of values recorded. */
43      private long count;
44      /** The consumer of values. */
45      private final IntConsumer consumer;
46      /** The {@link IntMin} implementation. */
47      private final IntMin min;
48      /** The {@link IntMax} implementation. */
49      private final IntMax max;
50      /** The moment implementation. May be any instance of {@link FirstMoment}.
51       * This implementation uses only the third and fourth moments. */
52      private final FirstMoment moment;
53      /** The {@link IntSum} implementation. */
54      private final IntSum sum;
55      /** The {@link Product} implementation. */
56      private final Product product;
57      /** The {@link IntSumOfSquares} implementation. */
58      private final IntSumOfSquares sumOfSquares;
59      /** The {@link SumOfLogs} implementation. */
60      private final SumOfLogs sumOfLogs;
61      /** Configuration options for computation of statistics. */
62      private StatisticsConfiguration config;
63  
64      /**
65       * A builder for {@link IntStatistics}.
66       */
67      public static final class Builder {
68          /** An empty double array. */
69          private static final int[] NO_VALUES = {};
70  
71          /** The {@link IntMin} constructor. */
72          private RangeFunction<int[], IntMin> min;
73          /** The {@link IntMax} constructor. */
74          private RangeFunction<int[], IntMax> max;
75          /** The moment constructor. May return any instance of {@link FirstMoment}. */
76          private RangeFunction<int[], FirstMoment> moment;
77          /** The {@link IntSum} constructor. */
78          private RangeFunction<int[], IntSum> sum;
79          /** The {@link Product} constructor. */
80          private RangeFunction<int[], Product> product;
81          /** The {@link IntSumOfSquares} constructor. */
82          private RangeFunction<int[], IntSumOfSquares> sumOfSquares;
83          /** The {@link SumOfLogs} constructor. */
84          private RangeFunction<int[], SumOfLogs> sumOfLogs;
85          /** The order of the moment. It corresponds to the power computed by the {@link FirstMoment}
86           * instance constructed by {@link #moment}. This should only be increased from the default
87           * of zero (corresponding to no moment computation). */
88          private int momentOrder;
89          /** Configuration options for computation of statistics. */
90          private StatisticsConfiguration config = StatisticsConfiguration.withDefaults();
91  
92          /**
93           * Create an instance.
94           */
95          Builder() {
96              // Do nothing
97          }
98  
99          /**
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 }