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
019/**
020 * Computes the arithmetic mean of the available values. Uses the following definition
021 * of the <em>sample mean</em>:
022 *
023 * <p>\[ \frac{1}{n} \sum_{i=1}^n x_i \]
024 *
025 * <p>where \( n \) is the number of samples.
026 *
027 * <ul>
028 *   <li>The result is {@code NaN} if no values are added.
029 * </ul>
030 *
031 * <p>This class uses an exact integer sum to compute the mean.
032 * Supports up to 2<sup>63</sup> (exclusive) observations.
033 * This implementation does not check for overflow of the count.
034 *
035 * <p>This class is designed to work with (though does not require)
036 * {@linkplain java.util.stream streams}.
037 *
038 * <p><strong>This implementation is not thread safe.</strong>
039 * If multiple threads access an instance of this class concurrently,
040 * and at least one of the threads invokes the {@link java.util.function.IntConsumer#accept(int) accept} or
041 * {@link StatisticAccumulator#combine(StatisticResult) combine} method, it must be synchronized externally.
042 *
043 * <p>However, it is safe to use {@link java.util.function.IntConsumer#accept(int) accept}
044 * and {@link StatisticAccumulator#combine(StatisticResult) combine}
045 * as {@code accumulator} and {@code combiner} functions of
046 * {@link java.util.stream.Collector Collector} on a parallel stream,
047 * because the parallel implementation of {@link java.util.stream.Stream#collect Stream.collect()}
048 * provides the necessary partitioning, isolation, and merging of results for
049 * safe and efficient parallel execution.
050 *
051 * @see <a href="https://en.wikipedia.org/wiki/Mean">Mean (Wikipedia)</a>
052 * @since 1.1 */
053public final class IntMean implements IntStatistic, StatisticAccumulator<IntMean> {
054    /** Limit for small sample size where the sum can exactly map to a double.
055     * This is conservatively set using 2^21 values of 2^31 (2^21 ~ 2 million). */
056    private static final long SMALL_N = 1L << 21;
057
058    /** Sum of the values. */
059    private final Int128 sum;
060    /** Count of values that have been added. */
061    private long n;
062
063    /**
064     * Create an instance.
065     */
066    private IntMean() {
067        this(Int128.create(), 0);
068    }
069
070    /**
071     * Create an instance.
072     *
073     * @param sum Sum of the values.
074     * @param n Count of values that have been added.
075     */
076    private IntMean(Int128 sum, int n) {
077        this.sum = sum;
078        this.n = n;
079    }
080
081    /**
082     * Creates an instance.
083     *
084     * <p>The initial result is {@code NaN}.
085     *
086     * @return {@code IntMean} instance.
087     */
088    public static IntMean create() {
089        return new IntMean();
090    }
091
092    /**
093     * Returns an instance populated using the input {@code values}.
094     *
095     * @param values Values.
096     * @return {@code IntMean} instance.
097     */
098    public static IntMean of(int... values) {
099        // Sum of an array cannot exceed a 64-bit long
100        long s = 0;
101        for (final int x : values) {
102            s += x;
103        }
104        // Convert
105        return new IntMean(Int128.of(s), values.length);
106    }
107
108    /**
109     * Updates the state of the statistic to reflect the addition of {@code value}.
110     *
111     * @param value Value.
112     */
113    @Override
114    public void accept(int value) {
115        sum.add(value);
116        n++;
117    }
118
119    /**
120     * Gets the mean of all input values.
121     *
122     * <p>When no values have been added, the result is {@code NaN}.
123     *
124     * @return mean of all values.
125     */
126    @Override
127    public double getAsDouble() {
128        return computeMean(sum, n);
129    }
130
131    /**
132     * Compute the mean.
133     *
134     * <p>This is a helper method used in higher order moments.
135     *
136     * @param sum Sum of the values.
137     * @param n Count of the values.
138     * @return the mean
139     */
140    static double computeMean(Int128 sum, long n) {
141        // Fast option when the sum fits within
142        // the mantissa of a double.
143        // Handles n=0 as NaN
144        if (n < SMALL_N) {
145            return (double) sum.lo64() / n;
146        }
147        // Extended precision
148        return IntMath.divide(sum, n);
149    }
150
151    @Override
152    public IntMean combine(IntMean other) {
153        sum.add(other.sum);
154        n += other.n;
155        return this;
156    }
157}