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.util.Objects;
20  import java.util.Set;
21  import java.util.function.DoubleConsumer;
22  import java.util.function.Function;
23  
24  /**
25   * Statistics for {@code double} values.
26   *
27   * <p>This class provides combinations of individual statistic implementations in the
28   * {@code org.apache.commons.statistics.descriptive} package.
29   *
30   * <p>Supports up to 2<sup>63</sup> (exclusive) observations.
31   * This implementation does not check for overflow of the count.
32   *
33   * @since 1.1
34   */
35  public final class DoubleStatistics implements DoubleConsumer {
36      /** Error message for non configured statistics. */
37      private static final String NO_CONFIGURED_STATISTICS = "No configured statistics";
38      /** Error message for an unsupported statistic. */
39      private static final String UNSUPPORTED_STATISTIC = "Unsupported statistic: ";
40  
41      /** Count of values recorded. */
42      private long count;
43      /** The consumer of values. */
44      private final DoubleConsumer consumer;
45      /** The {@link Min} implementation. */
46      private final Min min;
47      /** The {@link Max} implementation. */
48      private final Max max;
49      /** The moment implementation. May be any instance of {@link FirstMoment}. */
50      private final FirstMoment moment;
51      /** The {@link Sum} implementation. */
52      private final Sum sum;
53      /** The {@link Product} implementation. */
54      private final Product product;
55      /** The {@link SumOfSquares} implementation. */
56      private final SumOfSquares sumOfSquares;
57      /** The {@link SumOfLogs} implementation. */
58      private final SumOfLogs sumOfLogs;
59      /** Configuration options for computation of statistics. */
60      private StatisticsConfiguration config;
61  
62      /**
63       * A builder for {@link DoubleStatistics}.
64       */
65      public static final class Builder {
66          /** An empty double array. */
67          private static final double[] NO_VALUES = {};
68  
69          /** The {@link Min} constructor. */
70          private RangeFunction<double[], Min> min;
71          /** The {@link Max} constructor. */
72          private RangeFunction<double[], Max> max;
73          /** The moment constructor. May return any instance of {@link FirstMoment}. */
74          private RangeBiFunction<org.apache.commons.numbers.core.Sum, double[], FirstMoment> moment;
75          /** The {@link Sum} constructor. */
76          private Function<org.apache.commons.numbers.core.Sum, Sum> sum;
77          /** The {@link Product} constructor. */
78          private RangeFunction<double[], Product> product;
79          /** The {@link SumOfSquares} constructor. */
80          private RangeFunction<double[], SumOfSquares> sumOfSquares;
81          /** The {@link SumOfLogs} constructor. */
82          private RangeFunction<double[], SumOfLogs> sumOfLogs;
83          /** The order of the moment. It corresponds to the power computed by the {@link FirstMoment}
84           * instance constructed by {@link #moment}. This should only be increased from the default
85           * of zero (corresponding to no moment computation). */
86          private int momentOrder;
87          /** Configuration options for computation of statistics. */
88          private StatisticsConfiguration config = StatisticsConfiguration.withDefaults();
89  
90          /**
91           * Create an instance.
92           */
93          Builder() {
94              // Do nothing
95          }
96  
97          /**
98           * Add the statistic to the statistics to compute.
99           *
100          * @param statistic Statistic to compute.
101          * @return {@code this} instance
102          */
103         Builder add(Statistic statistic) {
104             // Exhaustive switch statement
105             switch (statistic) {
106             case GEOMETRIC_MEAN:
107             case SUM_OF_LOGS:
108                 sumOfLogs = SumOfLogs::createFromRange;
109                 break;
110             case KURTOSIS:
111                 createMoment(4);
112                 break;
113             case MAX:
114                 max = Max::createFromRange;
115                 break;
116             case MEAN:
117                 createMoment(1);
118                 break;
119             case MIN:
120                 min = Min::createFromRange;
121                 break;
122             case PRODUCT:
123                 product = Product::createFromRange;
124                 break;
125             case SKEWNESS:
126                 createMoment(3);
127                 break;
128             case STANDARD_DEVIATION:
129             case VARIANCE:
130                 createMoment(2);
131                 break;
132             case SUM:
133                 sum = Sum::new;
134                 break;
135             case SUM_OF_SQUARES:
136                 sumOfSquares = SumOfSquares::createFromRange;
137                 break;
138             }
139             return this;
140         }
141 
142         /**
143          * Creates the moment constructor for the specified {@code order},
144          * e.g. order=2 is sum of squared deviations.
145          *
146          * @param order Order.
147          */
148         private void createMoment(int order) {
149             if (order > momentOrder) {
150                 momentOrder = order;
151                 if (order == 4) {
152                     moment = SumOfFourthDeviations::createFromRange;
153                 } else if (order == 3) {
154                     moment = SumOfCubedDeviations::createFromRange;
155                 } else if (order == 2) {
156                     moment = SumOfSquaredDeviations::createFromRange;
157                 } else {
158                     // Assume order == 1
159                     moment = FirstMoment::createFromRange;
160                 }
161             }
162         }
163 
164         /**
165          * Sets the statistics configuration options for computation of statistics.
166          *
167          * @param v Value.
168          * @return the builder
169          * @throws NullPointerException if the value is null
170          */
171         public Builder setConfiguration(StatisticsConfiguration v) {
172             config = Objects.requireNonNull(v);
173             return this;
174         }
175 
176         /**
177          * Builds a {@code DoubleStatistics} instance.
178          *
179          * @return {@code DoubleStatistics} instance.
180          */
181         public DoubleStatistics build() {
182             return create(NO_VALUES, 0, 0);
183         }
184 
185         /**
186          * Builds a {@code DoubleStatistics} instance using the input {@code values}.
187          *
188          * <p>Note: {@code DoubleStatistics} computed using
189          * {@link DoubleStatistics#accept(double) accept} may be
190          * different from this instance.
191          *
192          * @param values Values.
193          * @return {@code DoubleStatistics} instance.
194          */
195         public DoubleStatistics build(double... values) {
196             Objects.requireNonNull(values, "values");
197             return create(values, 0, values.length);
198         }
199 
200         /**
201          * Builds a {@code DoubleStatistics} instance using the specified range of {@code values}.
202          *
203          * <p>Note: {@code DoubleStatistics} computed using
204          * {@link DoubleStatistics#accept(double) accept} may be
205          * different from this instance.
206          *
207          * @param values Values.
208          * @param from Inclusive start of the range.
209          * @param to Exclusive end of the range.
210          * @return {@code DoubleStatistics} instance.
211          * @throws IndexOutOfBoundsException if the sub-range is out of bounds
212          * @since 1.2
213          */
214         public DoubleStatistics build(double[] values, int from, int to) {
215             Statistics.checkFromToIndex(from, to, values.length);
216             return create(values, from, to);
217         }
218 
219         /**
220          * Builds a {@code DoubleStatistics} instance using the input {@code values}.
221          *
222          * <p>Note: {@code DoubleStatistics} computed using
223          * {@link DoubleStatistics#accept(double) accept} may be
224          * different from this instance.
225          *
226          * <p>Warning: No range checks are performed.
227          *
228          * @param values Values.
229          * @param from Inclusive start of the range.
230          * @param to Exclusive end of the range.
231          * @return {@code DoubleStatistics} instance.
232          */
233         private DoubleStatistics create(double[] values, int from, int to) {
234             // Create related statistics
235             FirstMoment m = null;
236             Sum sumStat = null;
237             if (moment != null || sum != null) {
238                 final org.apache.commons.numbers.core.Sum s = Statistics.sum(values, from, to);
239                 m = create(moment, s, values, from, to);
240                 sumStat = create(sum, s);
241             }
242             return new DoubleStatistics(
243                 to - from,
244                 create(min, values, from, to),
245                 create(max, values, from, to),
246                 m,
247                 sumStat,
248                 create(product, values, from, to),
249                 create(sumOfSquares, values, from, to),
250                 create(sumOfLogs, values, from, to),
251                 config);
252         }
253 
254         /**
255          * Creates the object from the {@code values}.
256          *
257          * @param <S> value type
258          * @param <T> object type
259          * @param constructor Constructor.
260          * @param values Values
261          * @return the instance
262          */
263         private static <S, T> T create(Function<S, T> constructor, S values) {
264             if (constructor != null) {
265                 return constructor.apply(values);
266             }
267             return null;
268         }
269 
270         /**
271          * Creates the object from the {@code values}.
272          *
273          * @param <S> value type
274          * @param <T> object type
275          * @param constructor Constructor.
276          * @param values Values
277          * @param from Inclusive start of the range.
278          * @param to Exclusive end of the range.
279          * @return the instance
280          */
281         private static <S, T> T create(RangeFunction<S, T> constructor, S values, int from, int to) {
282             if (constructor != null) {
283                 return constructor.apply(values, from, to);
284             }
285             return null;
286         }
287 
288         /**
289          * Creates the object from the values {@code r} and {@code s}.
290          *
291          * @param <R> value type
292          * @param <S> value type
293          * @param <T> object type
294          * @param constructor Constructor.
295          * @param r Value.
296          * @param s Value.
297          * @param from Inclusive start of the range.
298          * @param to Exclusive end of the range.
299          * @return the instance
300          */
301         private static <R, S, T> T create(RangeBiFunction<R, S, T> constructor, R r, S s, int from, int to) {
302             if (constructor != null) {
303                 return constructor.apply(r, s, from, to);
304             }
305             return null;
306         }
307     }
308 
309     /**
310      * Create an instance.
311      *
312      * @param count Count of values.
313      * @param min Min implementation.
314      * @param max Max implementation.
315      * @param moment Moment implementation.
316      * @param sum Sum implementation.
317      * @param product Product implementation.
318      * @param sumOfSquares Sum of squares implementation.
319      * @param sumOfLogs Sum of logs implementation.
320      * @param config Statistics configuration.
321      */
322     DoubleStatistics(long count, Min min, Max max, FirstMoment moment, Sum sum,
323                      Product product, SumOfSquares sumOfSquares, SumOfLogs sumOfLogs,
324                      StatisticsConfiguration config) {
325         this.count = count;
326         this.min = min;
327         this.max = max;
328         this.moment = moment;
329         this.sum = sum;
330         this.product = product;
331         this.sumOfSquares = sumOfSquares;
332         this.sumOfLogs = sumOfLogs;
333         this.config = config;
334         consumer = Statistics.composeDoubleConsumers(min, max, moment, sum, product,
335                                                      sumOfSquares, sumOfLogs);
336     }
337 
338     /**
339      * Returns a new instance configured to compute the specified {@code statistics}.
340      *
341      * <p>The statistics will be empty and so will return the default values for each
342      * computed statistic.
343      *
344      * @param statistics Statistics to compute.
345      * @return the instance
346      * @throws IllegalArgumentException if there are no {@code statistics} to compute.
347      */
348     public static DoubleStatistics of(Statistic... statistics) {
349         return builder(statistics).build();
350     }
351 
352     /**
353      * Returns a new instance configured to compute the specified {@code statistics}
354      * populated using the input {@code values}.
355      *
356      * <p>Use this method to create an instance populated with a (variable) array of
357      * {@code double[]} data:
358      *
359      * <pre>
360      * DoubleStatistics stats = DoubleStatistics.of(
361      *     EnumSet.of(Statistic.MIN, Statistic.MAX),
362      *     1, 1, 2, 3, 5, 8, 13);
363      * </pre>
364      *
365      * @param statistics Statistics to compute.
366      * @param values Values.
367      * @return the instance
368      * @throws IllegalArgumentException if there are no {@code statistics} to compute.
369      */
370     public static DoubleStatistics of(Set<Statistic> statistics, double... values) {
371         if (statistics.isEmpty()) {
372             throw new IllegalArgumentException(NO_CONFIGURED_STATISTICS);
373         }
374         final Builder b = new Builder();
375         statistics.forEach(b::add);
376         return b.build(values);
377     }
378 
379     /**
380      * Returns a new instance configured to compute the specified {@code statistics}
381      * populated using the specified range of {@code values}.
382      *
383      * <p>Use this method to create an instance populated with part of an array of
384      * {@code double[]} data, e.g. to use the first half of the data:
385      *
386      * <pre>
387      * double[] data = ...
388      * DoubleStatistics stats = DoubleStatistics.of(
389      *     EnumSet.of(Statistic.MIN, Statistic.MAX),
390      *     data, 0, data.length / 2);
391      * </pre>
392      *
393      * @param statistics Statistics to compute.
394      * @param values Values.
395      * @param from Inclusive start of the range.
396      * @param to Exclusive end of the range.
397      * @return the instance
398      * @throws IllegalArgumentException if there are no {@code statistics} to compute.
399      * @throws IndexOutOfBoundsException if the sub-range is out of bounds
400      * @since 1.2
401      */
402     public static DoubleStatistics ofRange(Set<Statistic> statistics, double[] values, int from, int to) {
403         if (statistics.isEmpty()) {
404             throw new IllegalArgumentException(NO_CONFIGURED_STATISTICS);
405         }
406         final Builder b = new Builder();
407         statistics.forEach(b::add);
408         return b.build(values, from, to);
409     }
410 
411     /**
412      * Returns a new builder configured to create instances to compute the specified
413      * {@code statistics}.
414      *
415      * <p>Use this method to create an instance populated with an array of {@code double[]}
416      * data using the {@link Builder#build(double...)} method:
417      *
418      * <pre>
419      * double[] data = ...
420      * DoubleStatistics stats = DoubleStatistics.builder(
421      *     Statistic.MIN, Statistic.MAX, Statistic.VARIANCE)
422      *     .build(data);
423      * </pre>
424      *
425      * <p>The builder can be used to create multiple instances of {@link DoubleStatistics}
426      * to be used in parallel, or on separate arrays of {@code double[]} data. These may
427      * be {@link #combine(DoubleStatistics) combined}. For example:
428      *
429      * <pre>
430      * double[][] data = ...
431      * DoubleStatistics.Builder builder = DoubleStatistics.builder(
432      *     Statistic.MIN, Statistic.MAX, Statistic.VARIANCE);
433      * DoubleStatistics stats = Arrays.stream(data)
434      *     .parallel()
435      *     .map(builder::build)
436      *     .reduce(DoubleStatistics::combine)
437      *     .get();
438      * </pre>
439      *
440      * <p>The builder can be used to create a {@link java.util.stream.Collector} for repeat
441      * use on multiple data:
442      *
443      * <pre>{@code
444      * DoubleStatistics.Builder builder = DoubleStatistics.builder(
445      *     Statistic.MIN, Statistic.MAX, Statistic.VARIANCE);
446      * Collector<double[], DoubleStatistics, DoubleStatistics> collector =
447      *     Collector.of(builder::build,
448      *                  (s, d) -> s.combine(builder.build(d)),
449      *                  DoubleStatistics::combine);
450      *
451      * // Repeated
452      * double[][] data = ...
453      * DoubleStatistics stats = Arrays.stream(data).collect(collector);
454      * }</pre>
455      *
456      * @param statistics Statistics to compute.
457      * @return the builder
458      * @throws IllegalArgumentException if there are no {@code statistics} to compute.
459      */
460     public static Builder builder(Statistic... statistics) {
461         if (statistics.length == 0) {
462             throw new IllegalArgumentException(NO_CONFIGURED_STATISTICS);
463         }
464         final Builder b = new Builder();
465         for (final Statistic s : statistics) {
466             b.add(s);
467         }
468         return b;
469     }
470 
471     /**
472      * Updates the state of the statistics to reflect the addition of {@code value}.
473      *
474      * @param value Value.
475      */
476     @Override
477     public void accept(double value) {
478         count++;
479         consumer.accept(value);
480     }
481 
482     /**
483      * Return the count of values recorded.
484      *
485      * @return the count of values
486      */
487     public long getCount() {
488         return count;
489     }
490 
491     /**
492      * Check if the specified {@code statistic} is supported.
493      *
494      * <p>Note: This method will not return {@code false} if the argument is {@code null}.
495      *
496      * @param statistic Statistic.
497      * @return {@code true} if supported
498      * @throws NullPointerException if the {@code statistic} is {@code null}
499      * @see #getAsDouble(Statistic)
500      */
501     public boolean isSupported(Statistic statistic) {
502         // Check for the appropriate underlying implementation
503         // Exhaustive switch statement
504         switch (statistic) {
505         case GEOMETRIC_MEAN:
506         case SUM_OF_LOGS:
507             return sumOfLogs != null;
508         case KURTOSIS:
509             return moment instanceof SumOfFourthDeviations;
510         case MAX:
511             return max != null;
512         case MEAN:
513             return moment != null;
514         case MIN:
515             return min != null;
516         case PRODUCT:
517             return product != null;
518         case SKEWNESS:
519             return moment instanceof SumOfCubedDeviations;
520         case STANDARD_DEVIATION:
521         case VARIANCE:
522             return moment instanceof SumOfSquaredDeviations;
523         case SUM:
524             return sum != null;
525         case SUM_OF_SQUARES:
526             return sumOfSquares != null;
527         }
528         // Unreachable code
529         throw new IllegalArgumentException(UNSUPPORTED_STATISTIC + statistic);
530     }
531 
532     /**
533      * Gets the value of the specified {@code statistic} as a {@code double}.
534      *
535      * @param statistic Statistic.
536      * @return the value
537      * @throws IllegalArgumentException if the {@code statistic} is not supported
538      * @see #isSupported(Statistic)
539      * @see #getResult(Statistic)
540      */
541     public double getAsDouble(Statistic statistic) {
542         return getResult(statistic).getAsDouble();
543     }
544 
545     /**
546      * Gets a supplier for the value of the specified {@code statistic}.
547      *
548      * <p>The returned function will supply the correct result after
549      * calls to {@link #accept(double) accept} or
550      * {@link #combine(DoubleStatistics) combine} further values into
551      * {@code this} instance.
552      *
553      * <p>This method can be used to perform a one-time look-up of the statistic
554      * function to compute statistics as values are dynamically added.
555      *
556      * @param statistic Statistic.
557      * @return the supplier
558      * @throws IllegalArgumentException if the {@code statistic} is not supported
559      * @see #isSupported(Statistic)
560      * @see #getAsDouble(Statistic)
561      */
562     public StatisticResult getResult(Statistic statistic) {
563         // Locate the implementation.
564         // Statistics that wrap an underlying implementation are created in methods.
565         // The return argument should be a method reference and not an instance
566         // of DoubleStatistic. This ensures the statistic implementation cannot
567         // be updated with new values by casting the result and calling accept(double).
568         StatisticResult stat = null;
569         // Exhaustive switch statement
570         switch (statistic) {
571         case GEOMETRIC_MEAN:
572             stat = getGeometricMean();
573             break;
574         case KURTOSIS:
575             stat = getKurtosis();
576             break;
577         case MAX:
578             stat = max;
579             break;
580         case MEAN:
581             stat = getMean();
582             break;
583         case MIN:
584             stat = min;
585             break;
586         case PRODUCT:
587             stat = product;
588             break;
589         case SKEWNESS:
590             stat = getSkewness();
591             break;
592         case STANDARD_DEVIATION:
593             stat = getStandardDeviation();
594             break;
595         case SUM:
596             stat = sum;
597             break;
598         case SUM_OF_LOGS:
599             stat = sumOfLogs;
600             break;
601         case SUM_OF_SQUARES:
602             stat = sumOfSquares;
603             break;
604         case VARIANCE:
605             stat = getVariance();
606             break;
607         }
608         if (stat != null) {
609             return stat instanceof DoubleStatistic ?
610                 ((DoubleStatistic) stat)::getAsDouble :
611                 stat;
612         }
613         throw new IllegalArgumentException(UNSUPPORTED_STATISTIC + statistic);
614     }
615 
616     /**
617      * Gets the geometric mean.
618      *
619      * @return a geometric mean supplier (or null if unsupported)
620      */
621     private StatisticResult getGeometricMean() {
622         if (sumOfLogs != null) {
623             // Return a function that has access to the count and sumOfLogs
624             return () -> GeometricMean.computeGeometricMean(count, sumOfLogs);
625         }
626         return null;
627     }
628 
629     /**
630      * Gets the kurtosis.
631      *
632      * @return a kurtosis supplier (or null if unsupported)
633      */
634     private StatisticResult getKurtosis() {
635         if (moment instanceof SumOfFourthDeviations) {
636             return new Kurtosis((SumOfFourthDeviations) moment)
637                 .setBiased(config.isBiased())::getAsDouble;
638         }
639         return null;
640     }
641 
642     /**
643      * Gets the mean.
644      *
645      * @return a mean supplier (or null if unsupported)
646      */
647     private StatisticResult getMean() {
648         if (moment != null) {
649             // Special case where wrapping with a Mean is not required
650             return moment::getFirstMoment;
651         }
652         return null;
653     }
654 
655     /**
656      * Gets the skewness.
657      *
658      * @return a skewness supplier (or null if unsupported)
659      */
660     private StatisticResult getSkewness() {
661         if (moment instanceof SumOfCubedDeviations) {
662             return new Skewness((SumOfCubedDeviations) moment)
663                 .setBiased(config.isBiased())::getAsDouble;
664         }
665         return null;
666     }
667 
668     /**
669      * Gets the standard deviation.
670      *
671      * @return a standard deviation supplier (or null if unsupported)
672      */
673     private StatisticResult getStandardDeviation() {
674         if (moment instanceof SumOfSquaredDeviations) {
675             return new StandardDeviation((SumOfSquaredDeviations) moment)
676                 .setBiased(config.isBiased())::getAsDouble;
677         }
678         return null;
679     }
680 
681     /**
682      * Gets the variance.
683      *
684      * @return a variance supplier (or null if unsupported)
685      */
686     private StatisticResult getVariance() {
687         if (moment instanceof SumOfSquaredDeviations) {
688             return new Variance((SumOfSquaredDeviations) moment)
689                 .setBiased(config.isBiased())::getAsDouble;
690         }
691         return null;
692     }
693 
694     /**
695      * Combines the state of the {@code other} statistics into this one.
696      * Only {@code this} instance is modified by the {@code combine} operation.
697      *
698      * <p>The {@code other} instance must be <em>compatible</em>. This is {@code true} if the
699      * {@code other} instance returns {@code true} for {@link #isSupported(Statistic)} for
700      * all values of the {@link Statistic} enum which are supported by {@code this}
701      * instance.
702      *
703      * <p>Note that this operation is <em>not symmetric</em>. It may be possible to perform
704      * {@code a.combine(b)} but not {@code b.combine(a)}. In the event that the {@code other}
705      * instance is not compatible then an exception is raised before any state is modified.
706      *
707      * @param other Another set of statistics to be combined.
708      * @return {@code this} instance after combining {@code other}.
709      * @throws IllegalArgumentException if the {@code other} is not compatible
710      */
711     public DoubleStatistics combine(DoubleStatistics other) {
712         // Check compatibility
713         Statistics.checkCombineCompatible(min, other.min);
714         Statistics.checkCombineCompatible(max, other.max);
715         Statistics.checkCombineCompatible(sum, other.sum);
716         Statistics.checkCombineCompatible(product, other.product);
717         Statistics.checkCombineCompatible(sumOfSquares, other.sumOfSquares);
718         Statistics.checkCombineCompatible(sumOfLogs, other.sumOfLogs);
719         Statistics.checkCombineAssignable(moment, other.moment);
720         // Combine
721         count += other.count;
722         Statistics.combine(min, other.min);
723         Statistics.combine(max, other.max);
724         Statistics.combine(sum, other.sum);
725         Statistics.combine(product, other.product);
726         Statistics.combine(sumOfSquares, other.sumOfSquares);
727         Statistics.combine(sumOfLogs, other.sumOfLogs);
728         Statistics.combineMoment(moment, other.moment);
729         return this;
730     }
731 
732     /**
733      * Sets the statistics configuration.
734      *
735      * <p>These options only control the final computation of statistics. The configuration
736      * will not affect compatibility between instances during a
737      * {@link #combine(DoubleStatistics) combine} operation.
738      *
739      * <p>Note: These options will affect any future computation of statistics. Supplier functions
740      * that have been previously created will not be updated with the new configuration.
741      *
742      * @param v Value.
743      * @return {@code this} instance
744      * @throws NullPointerException if the value is null
745      * @see #getResult(Statistic)
746      */
747     public DoubleStatistics setConfiguration(StatisticsConfiguration v) {
748         config = Objects.requireNonNull(v);
749         return this;
750     }
751 }