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 IntMean} 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 IntMean} 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     * Updates the state of the statistic to reflect the addition of {@code value}.
107     *
108     * @param value Value.
109     */
110    @Override
111    public void accept(long value) {
112        sum.add(value);
113        n++;
114    }
115
116    /**
117     * Gets the mean of all input values.
118     *
119     * <p>When no values have been added, the result is {@code NaN}.
120     *
121     * @return mean of all values.
122     */
123    @Override
124    public double getAsDouble() {
125        return computeMean(sum, n);
126    }
127
128    /**
129     * Compute the mean.
130     *
131     * <p>This is a helper method used in higher order moments.
132     *
133     * @param sum Sum of the values.
134     * @param n Count of the values.
135     * @return the mean
136     */
137    static double computeMean(Int128 sum, long n) {
138        // Fast option when the sum fits within
139        // the mantissa of a double.
140        // Handles n=0 as NaN
141        if (sum.hi64() == 0 && Math.abs(sum.lo64()) < SMALL_SUM) {
142            return (double) sum.lo64() / n;
143        }
144        // Extended precision
145        return IntMath.divide(sum, n);
146    }
147
148    @Override
149    public LongMean combine(LongMean other) {
150        sum.add(other.sum);
151        n += other.n;
152        return this;
153    }
154}