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 * Returns the sum of the {@link Math#log(double) natural logarithm} of available values. 021 * 022 * <ul> 023 * <li>The result is zero if no values are added. 024 * <li>The result is {@code NaN} if any of the values is {@code NaN}. 025 * <li>The result is {@code NaN} if any of the values is negative. 026 * </ul> 027 * 028 * <p>The sum follows the IEEE754 result for summing infinite values: 029 * 030 * <ul> 031 * <li>The result is {@code +infinity} if all values are in the range {@code (0, +infinity]} 032 * and at least one value is {@code +infinity}. 033 * <li>The result is {@code -infinity} if all values are in the range {@code [0, +infinity)} 034 * and at least one value is zero. 035 * <li>The result is {@code NaN} if all values are in the range {@code [0, +infinity]} 036 * and at least one value is zero, and one value is {@code +infinity}. 037 * </ul> 038 * 039 * <p>This class is designed to work with (though does not require) 040 * {@linkplain java.util.stream streams}. 041 * 042 * <p><strong>This instance is not thread safe.</strong> 043 * If multiple threads access an instance of this class concurrently, 044 * and at least one of the threads invokes the {@link java.util.function.DoubleConsumer#accept(double) accept} or 045 * {@link StatisticAccumulator#combine(StatisticResult) combine} method, it must be synchronized externally. 046 * 047 * <p>However, it is safe to use {@link java.util.function.DoubleConsumer#accept(double) accept} 048 * and {@link StatisticAccumulator#combine(StatisticResult) combine} 049 * as {@code accumulator} and {@code combiner} functions of 050 * {@link java.util.stream.Collector Collector} on a parallel stream, 051 * because the parallel instance of {@link java.util.stream.Stream#collect Stream.collect()} 052 * provides the necessary partitioning, isolation, and merging of results for 053 * safe and efficient parallel execution. 054 * 055 * @see org.apache.commons.numbers.core.Sum 056 * @see Math#log(double) 057 * @since 1.1 058 */ 059public final class SumOfLogs implements DoubleStatistic, StatisticAccumulator<SumOfLogs> { 060 061 /** {@link org.apache.commons.numbers.core.Sum Sum} used to compute the sum. */ 062 private final org.apache.commons.numbers.core.Sum delegate = 063 org.apache.commons.numbers.core.Sum.create(); 064 065 /** 066 * Create an instance. 067 */ 068 private SumOfLogs() { 069 // No-op 070 } 071 072 /** 073 * Creates an instance. 074 * 075 * <p>The initial result is zero. 076 * 077 * @return {@code SumOfLogs} instance. 078 */ 079 public static SumOfLogs create() { 080 return new SumOfLogs(); 081 } 082 083 /** 084 * Returns an instance populated using the input {@code values}. 085 * 086 * <p>The result is {@code NaN} if any of the values is {@code NaN} 087 * or negative; or the sum at any point is a {@code NaN}. 088 * 089 * <p>When the input is an empty array, the result is zero. 090 * 091 * @param values Values. 092 * @return {@code SumOfLogs} instance. 093 */ 094 public static SumOfLogs of(double... values) { 095 return Statistics.add(new SumOfLogs(), values); 096 } 097 098 /** 099 * Returns an instance populated using the specified range of {@code values}. 100 * 101 * <p>The result is {@code NaN} if any of the values is {@code NaN} 102 * or negative; or the sum at any point is a {@code NaN}. 103 * 104 * <p>When the range is empty, the result is zero. 105 * 106 * @param values Values. 107 * @param from Inclusive start of the range. 108 * @param to Exclusive end of the range. 109 * @return {@code SumOfLogs} instance. 110 * @throws IndexOutOfBoundsException if the sub-range is out of bounds 111 * @since 1.2 112 */ 113 public static SumOfLogs ofRange(double[] values, int from, int to) { 114 Statistics.checkFromToIndex(from, to, values.length); 115 return createFromRange(values, from, to); 116 } 117 118 /** 119 * Create an instance using the specified range of {@code values}. 120 * 121 * <p>Warning: No range checks are performed. 122 * 123 * @param values Values. 124 * @param from Inclusive start of the range. 125 * @param to Exclusive end of the range. 126 * @return {@code SumOfLogs} instance. 127 */ 128 static SumOfLogs createFromRange(double[] values, int from, int to) { 129 return Statistics.add(new SumOfLogs(), values, from, to); 130 } 131 132 /** 133 * Returns an instance populated using the input {@code values}. 134 * 135 * <p>The result is {@code NaN} if any of the values is negative. 136 * 137 * <p>When the input is an empty array, the result is zero. 138 * 139 * @param values Values. 140 * @return {@code SumOfLogs} instance. 141 */ 142 public static SumOfLogs of(int... values) { 143 return Statistics.add(new SumOfLogs(), values); 144 } 145 146 /** 147 * Returns an instance populated using the specified range of {@code values}. 148 * 149 * <p>The result is {@code NaN} if any of the values is negative. 150 * 151 * <p>When the range is empty, the result is zero. 152 * 153 * @param values Values. 154 * @param from Inclusive start of the range. 155 * @param to Exclusive end of the range. 156 * @return {@code SumOfLogs} instance. 157 * @throws IndexOutOfBoundsException if the sub-range is out of bounds 158 * @since 1.2 159 */ 160 public static SumOfLogs ofRange(int[] values, int from, int to) { 161 Statistics.checkFromToIndex(from, to, values.length); 162 return createFromRange(values, from, to); 163 } 164 165 /** 166 * Create an instance using the specified range of {@code values}. 167 * 168 * <p>Warning: No range checks are performed. 169 * 170 * @param values Values. 171 * @param from Inclusive start of the range. 172 * @param to Exclusive end of the range. 173 * @return {@code SumOfLogs} instance. 174 */ 175 static SumOfLogs createFromRange(int[] values, int from, int to) { 176 return Statistics.add(new SumOfLogs(), values, from, to); 177 } 178 179 /** 180 * Returns an instance populated using the input {@code values}. 181 * 182 * <p>The result is {@code NaN} if any of the values is negative. 183 * 184 * <p>When the input is an empty array, the result is zero. 185 * 186 * @param values Values. 187 * @return {@code SumOfLogs} instance. 188 */ 189 public static SumOfLogs of(long... values) { 190 return Statistics.add(new SumOfLogs(), values); 191 } 192 193 /** 194 * Returns an instance populated using the specified range of {@code values}. 195 * 196 * <p>The result is {@code NaN} if any of the values is negative. 197 * 198 * <p>When the range is empty, the result is zero. 199 * 200 * @param values Values. 201 * @param from Inclusive start of the range. 202 * @param to Exclusive end of the range. 203 * @return {@code SumOfLogs} instance. 204 * @throws IndexOutOfBoundsException if the sub-range is out of bounds 205 * @since 1.2 206 */ 207 public static SumOfLogs ofRange(long[] values, int from, int to) { 208 Statistics.checkFromToIndex(from, to, values.length); 209 return createFromRange(values, from, to); 210 } 211 212 /** 213 * Create an instance using the specified range of {@code values}. 214 * 215 * <p>Warning: No range checks are performed. 216 * 217 * @param values Values. 218 * @param from Inclusive start of the range. 219 * @param to Exclusive end of the range. 220 * @return {@code SumOfLogs} instance. 221 */ 222 static SumOfLogs createFromRange(long[] values, int from, int to) { 223 return Statistics.add(new SumOfLogs(), values, from, to); 224 } 225 226 /** 227 * Updates the state of the statistic to reflect the addition of {@code value}. 228 * 229 * @param value Value. 230 */ 231 @Override 232 public void accept(double value) { 233 delegate.accept(Math.log(value)); 234 } 235 236 /** 237 * Gets the sum of all input values. 238 * 239 * <p>When no values have been added, the result is zero. 240 * 241 * @return sum of all values. 242 */ 243 @Override 244 public double getAsDouble() { 245 return delegate.getAsDouble(); 246 } 247 248 @Override 249 public SumOfLogs combine(SumOfLogs other) { 250 delegate.add(other.delegate); 251 return this; 252 } 253}