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}