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.LongConsumer#accept(long) 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.LongConsumer#accept(long) 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 * @since 1.1
052 */
053public final class LongMean implements LongStatistic, StatisticAccumulator<LongMean> {
054    /** Limit where the absolute sum can exactly map to a double. Set to 2^53. */
055    private static final long SMALL_SUM = 1L << 53;
056
057    /** Sum of the values. */
058    private final Int128 sum;
059    /** Count of values that have been added. */
060    private long n;
061
062    /**
063     * Create an instance.
064     */
065    private LongMean() {
066        this(Int128.create(), 0);
067    }
068
069    /**
070     * Create an instance.
071     *
072     * @param sum Sum of the values.
073     * @param n Count of values that have been added.
074     */
075    private LongMean(Int128 sum, int n) {
076        this.sum = sum;
077        this.n = n;
078    }
079
080    /**
081     * Creates an instance.
082     *
083     * <p>The initial result is {@code NaN}.
084     *
085     * @return {@code LongMean} instance.
086     */
087    public static LongMean create() {
088        return new LongMean();
089    }
090
091    /**
092     * Returns an instance populated using the input {@code values}.
093     *
094     * @param values Values.
095     * @return {@code LongMean} instance.
096     */
097    public static LongMean of(long... values) {
098        final Int128 s = Int128.create();
099        for (final long x : values) {
100            s.add(x);
101        }
102        return new LongMean(s, values.length);
103    }
104
105    /**
106     * Returns an instance populated using the specified range of {@code values}.
107     *
108     * @param values Values.
109     * @param from Inclusive start of the range.
110     * @param to Exclusive end of the range.
111     * @return {@code LongMean} instance.
112     * @throws IndexOutOfBoundsException if the sub-range is out of bounds
113     * @since 1.2
114     */
115    public static LongMean ofRange(long[] values, int from, int to) {
116        Statistics.checkFromToIndex(from, to, values.length);
117        return createFromRange(values, from, to);
118    }
119
120    /**
121     * Create an instance using the specified range of {@code values}.
122     *
123     * <p>Warning: No range checks are performed.
124     *
125     * @param values Values.
126     * @param from Inclusive start of the range.
127     * @param to Exclusive end of the range.
128     * @return {@code LongMean} instance.
129     */
130    static LongMean createFromRange(long[] values, int from, int to) {
131        // Sum of an array cannot exceed a 64-bit long
132        final Int128 s = Int128.create();
133        for (int i = from; i < to; i++) {
134            s.add(values[i]);
135        }
136        // Convert
137        return new LongMean(s, to - from);
138    }
139
140    /**
141     * Updates the state of the statistic to reflect the addition of {@code value}.
142     *
143     * @param value Value.
144     */
145    @Override
146    public void accept(long value) {
147        sum.add(value);
148        n++;
149    }
150
151    /**
152     * Gets the mean of all input values.
153     *
154     * <p>When no values have been added, the result is {@code NaN}.
155     *
156     * @return mean of all values.
157     */
158    @Override
159    public double getAsDouble() {
160        return computeMean(sum, n);
161    }
162
163    /**
164     * Compute the mean.
165     *
166     * <p>This is a helper method used in higher order moments.
167     *
168     * @param sum Sum of the values.
169     * @param n Count of the values.
170     * @return the mean
171     */
172    static double computeMean(Int128 sum, long n) {
173        // Fast option when the sum fits within
174        // the mantissa of a double.
175        // Handles n=0 as NaN
176        if (sum.hi64() == 0 && Math.abs(sum.lo64()) < SMALL_SUM) {
177            return (double) sum.lo64() / n;
178        }
179        // Extended precision
180        return sum.divideToDouble(n);
181    }
182
183    @Override
184    public LongMean combine(LongMean other) {
185        sum.add(other.sum);
186        n += other.n;
187        return this;
188    }
189}