001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 The ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| 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.conqat.lib.commons.math; 018 019import java.io.PrintStream; 020import java.util.Collection; 021import java.util.List; 022import java.util.Set; 023 024import org.conqat.lib.commons.assertion.CCSMAssert; 025import org.conqat.lib.commons.collections.CounterSet; 026 027/** 028 * Collection of math utility methods. 029 */ 030public class MathUtils { 031 032 /** 033 * Sum values. 034 * 035 * @see SumAggregator 036 * @see EAggregationStrategy#SUM 037 */ 038 public static double sum(Collection<? extends Number> collection) { 039 return aggregate(collection, EAggregationStrategy.SUM); 040 } 041 042 /** 043 * Find maximum. 044 * 045 * @see MaxAggregator 046 * @see EAggregationStrategy#MAX 047 */ 048 public static double max(Collection<? extends Number> collection) { 049 return aggregate(collection, EAggregationStrategy.MAX); 050 } 051 052 /** 053 * Find minimum. 054 * 055 * @see MinAggregator 056 * @see EAggregationStrategy#MIN 057 */ 058 public static double min(Collection<? extends Number> collection) { 059 return aggregate(collection, EAggregationStrategy.MIN); 060 } 061 062 /** 063 * Find mean. 064 * 065 * @return {@link Double#NaN} for empty input collection 066 * 067 * @see MeanAggregator 068 * @see EAggregationStrategy#MEAN 069 */ 070 public static double mean(Collection<? extends Number> collection) { 071 return aggregate(collection, EAggregationStrategy.MEAN); 072 } 073 074 /** 075 * Find median. 076 * 077 * @return {@link Double#NaN} for empty input collection 078 * 079 * @see PercentileAggregator 080 * @see EAggregationStrategy#MEDIAN 081 */ 082 public static double median(Collection<? extends Number> collection) { 083 return aggregate(collection, EAggregationStrategy.MEDIAN); 084 } 085 086 /** 087 * Find the 25-percentile. 088 * 089 * @return {@link Double#NaN} for empty input collection 090 * 091 * @see PercentileAggregator 092 * @see EAggregationStrategy#MEDIAN 093 */ 094 public static double percentile25(Collection<? extends Number> collection) { 095 return aggregate(collection, EAggregationStrategy.PERCENTILE25); 096 } 097 098 /** 099 * Find the 75-percentile. 100 * 101 * @return {@link Double#NaN} for empty input collection 102 * 103 * @see PercentileAggregator 104 * @see EAggregationStrategy#MEDIAN 105 */ 106 public static double percentile75(Collection<? extends Number> collection) { 107 return aggregate(collection, EAggregationStrategy.PERCENTILE75); 108 } 109 110 /** 111 * Calculate variance of the data set. 112 * 113 * @param sample 114 * if true sample variance is calculated, population variance 115 * otherwise 116 */ 117 public static double variance(Collection<? extends Number> collection, boolean sample) { 118 if (sample) { 119 return aggregate(collection, EAggregationStrategy.SAMPLE_VARIANCE); 120 } 121 return aggregate(collection, EAggregationStrategy.POPULATION_VARIANCE); 122 } 123 124 /** 125 * Calculate standard deviation of the data set. 126 * 127 * @param sample 128 * if true sample standard deviation is calculated, population 129 * variance otherwise 130 */ 131 public static double stdDev(Collection<? extends Number> collection, boolean sample) { 132 if (sample) { 133 return aggregate(collection, EAggregationStrategy.SAMPLE_STD_DEV); 134 } 135 return aggregate(collection, EAggregationStrategy.POPULATION_STD_DEV); 136 } 137 138 /** 139 * Aggregate collections of values with a given aggregation strategy. 140 * 141 * @return certain aggregation strategies may return {@link Double#NaN} for 142 * empty input collections 143 */ 144 public static double aggregate(Collection<? extends Number> values, EAggregationStrategy aggregation) { 145 return aggregation.getAggregator().aggregate(values); 146 } 147 148 /** 149 * Computes the factorial of n. Errors are not handled. If n is negative, 1 150 * will be returned. If n to too large, wrong results will be produced due 151 * to numerical overflow. 152 */ 153 public static long factorial(int n) { 154 long result = 1; 155 for (int i = 2; i <= n; ++i) { 156 result *= i; 157 } 158 return result; 159 } 160 161 /** Checks if the provided number is neither infinite nor NaN. */ 162 public static boolean isNormal(double number) { 163 return !Double.isInfinite(number) && !Double.isNaN(number); 164 } 165 166 /** 167 * Calculates the number of choices for k from n elements, also known as 168 * binomial coefficients. The input parameters may not be too large to avoid 169 * overflows. Both parameters must be non-negative. 170 */ 171 public static int choose(long n, long k) { 172 CCSMAssert.isTrue(n >= 0 && k >= 0, "Parameters must be positive."); 173 174 if (k == 0) { 175 return 1; 176 } 177 if (n == 0) { 178 return 0; 179 } 180 return choose(n - 1, k) + choose(n - 1, k - 1); 181 } 182 183 /** 184 * Computes a distribution of the given list of values and the given ranges. 185 * The result is a counter set of ranges, denoting how many values in the 186 * list are within a given range. 187 * <p> 188 * It is <B>not</B> checked whether the ranges are disjoint. Moreover, it is 189 * <B>not</B> checked whether every value can be mapped to a range. Thus, 190 * the resulting counter set may have an overall value that is less than or 191 * greater than the size of the list of given values. 192 * </p> 193 */ 194 public static CounterSet<Range> rangeDistribution(List<Double> values, Set<Range> ranges) { 195 CounterSet<Range> result = new CounterSet<Range>(); 196 for (Double value : values) { 197 for (Range range : ranges) { 198 if (range.contains(value)) { 199 result.inc(range); 200 } 201 } 202 } 203 return result; 204 } 205 206 /** 207 * Prints the min, max, mean, percentile25, median, and percentile75 of the 208 * given values to System.out. 209 */ 210 public static void printBasicDescriptiveStatistics(Collection<? extends Number> values) { 211 printBasicDescriptiveStatistics(values, System.out); 212 } 213 214 /** 215 * Prints the min, max, sum, mean, percentile25, median, and percentile75 of 216 * the given values to the given {@link PrintStream}. 217 */ 218 public static void printBasicDescriptiveStatistics(Collection<? extends Number> values, PrintStream printStream) { 219 printStream.println("Min : " + MathUtils.min(values)); 220 printStream.println("Max : " + MathUtils.max(values)); 221 printStream.println("Sum : " + MathUtils.sum(values)); 222 printStream.println("Mean : " + MathUtils.mean(values)); 223 printStream.println("Percentile25 : " + MathUtils.percentile25(values)); 224 printStream.println("Median : " + MathUtils.median(values)); 225 printStream.println("Percentile75 : " + MathUtils.percentile75(values)); 226 } 227 228 /** 229 * Constrains the given value, if it exceeds the given minimum or maximum. 230 * 231 * @return the given value, if it is within the given minimum and maximum 232 * (inclusive) or the minimum or maximum respectively, if it exceeds 233 * these. 234 */ 235 public static int constrainValue(int value, int min, int max) { 236 return (int) constrainValue((double) value, (double) min, (double) max); 237 } 238 239 /** 240 * Constrains the given value, if it exceeds the given minimum or maximum. 241 * 242 * @return the given value, if it is within the given minimum and maximum 243 * (inclusive) or the minimum or maximum respectively, if it exceeds 244 * these. 245 */ 246 public static double constrainValue(double value, double min, double max) { 247 if (value < min) { 248 return min; 249 } else if (value > max) { 250 return max; 251 } 252 return value; 253 } 254}