/******************************************************************************* * * Copyright 2010 Alexandru Craciun, and individual contributors as indicated * by the @authors tag. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. ******************************************************************************/ package org.netxilia.functions; import java.math.BigDecimal; import java.util.Iterator; import org.apache.commons.math.util.MathUtils; import org.netxilia.spi.formula.Function; import org.netxilia.spi.formula.Functions; /** * This are the mathematical functions implemented by Google Docs */ @Functions public class MathFunctions { public double ABS(double number) { return Math.abs(number); } public double ACOS(double number) { return Math.acos(number); } public double ACOSH(double x) { return Math.log(x + Math.sqrt(x * x - 1)); } public double ASIN(double number) { return Math.asin(number); } public double ASINH(double x) { return Math.log(x + Math.sqrt(x * x + 1)); } public double ATAN(double number) { return Math.atan(number); } public double ATAN2(double number_x, double number_y) { return Math.atan2(number_x, number_y); } public double ATANH(double x) { return Math.log((1 + x) / (1 - x)) / 2; } /** * Rounds the given number to the nearest integer or multiple of significance. Significance is the value to whose * multiple of ten the value is to be rounded up (.01, .1, 1, 10, etc.). Mode is an optional value. If it is * indicated and non-zero and if the number and significance are negative, rounding up is carried out based on that * value. * * @return */ public double CEILING(double number, double significance) { return Math.ceil(number / significance) * significance; } public long COMBIN(int count1, int count2) { if (count2 > count1) { throw new IllegalArgumentException("Second argument should be smaller the the first argument"); } return MathUtils.factorial(count1) / (MathUtils.factorial(count2) * MathUtils.factorial(count1 - count2)); } public double COS(double number) { return Math.cos(number); } public double COSH(double number) { return MathUtils.cosh(number); } public int COUNTBLANK(Iterator<String> range) { int n = 0; while (range.hasNext()) { String s = range.next(); if (s == null || s.isEmpty()) { n++; } } return n; } public int COUNTIF(Iterator<String> range, String criteria) { int n = 0; while (range.hasNext()) { String s = range.next(); if (criteria == null) { if (s == null) { n++; } } else if (criteria.equals(s)) { n++; } } return n; } public double DEGREES(double number) { return Math.toDegrees(number); } private long exactOrNextInteger(double n) { double f = Math.floor(n); if (n == f) { return (long) f; } return (long) (f + 1); } /** * Rounds the given number up to the nearest even integer. * * @param number * @return */ public long EVEN(double number) { if (number < 0) { return -EVEN(-number); } long n = exactOrNextInteger(number); if (n % 2 == 0) { return n; } return n + 1; } public double EXP(double number) { return Math.exp(number); } public long FACT(int number) { return MathUtils.factorial(number); } public double FACTDOUBLE(int number) { return MathUtils.factorialDouble(number); } public double FLOOR(double number, double significance) { return Math.floor(number / significance) * significance; } /** * Returns the greatest common divisor of one or more integers. * * @return */ public long GCD(Iterator<Long> values) { if (!values.hasNext()) { return 1; } long result = values.next(); while (values.hasNext()) { result = MathUtils.gcd(result, values.next()); } return result; } public long INT(double number) { return Math.round(number); } public boolean ISEVEN(double value) { return ((long) value) % 2 == 0; } public boolean ISODD(double value) { return ((long) value) % 2 == 1; } /** * Returns the least common multiple of one or more integers. Integer_1, integer_2,... integer_30 are integers whose * lowest common multiple is to be calculated. * * @param values * @return */ public long LCM(Iterator<Long> values) { if (!values.hasNext()) { return 1; } long result = values.next(); while (values.hasNext()) { result = MathUtils.lcm(result, values.next()); } return result; } public double LN(double number) { return Math.log(number); } public double LOG(double number, double base) { return Math.log(number) / Math.log(base); } public double LOG10(double number) { return Math.log10(number); } public long MOD(long dividend, long divisor) { return dividend % divisor; } /** * The result is the nearest integer multiple of the number. * * @param number * @param multiple * @return */ public double MROUND(double number, double multiple) { return Math.round(number / multiple) * multiple; } /** * Returns the factorial of the sum of the arguments divided by the product of the factorials of the arguments. * * @param values * @return */ public double MULTINOMIAL(Iterator<Integer> values) { int sum = 0; long product = 1; while (values.hasNext()) { Integer n = values.next(); sum += n; product *= MathUtils.factorial(n); } return MathUtils.factorial(sum) / product; } /** * Rounds the given number up to the nearest odd integer. * * @param number * @return */ public long ODD(double number) { if (number < 0) { return -ODD(-number); } long n = exactOrNextInteger(number); if (n % 2 == 1) { return n; } return n + 1; } public double POWER(double base, double power) { return Math.pow(base, power); } public double PRODUCT(Iterator<Double> values) { double n = 1; while (values.hasNext()) { n *= values.next(); } return n; } public long QUOTIENT(long numerator, long denominator) { return numerator / denominator; } public double RADIANS(double number) { return Math.toRadians(number); } @Function(cacheable = false) public double RAND() { return Math.random(); } @Function(cacheable = false) public double RANDBETWEEN(double bottom, double top) { return bottom + (top - bottom) * Math.random(); } /** * Rounds the given number to a certain number of decimal places according to valid mathematical criteria. Count * (optional) is the number of the places to which the value is to be rounded. If the count parameter is negative, * only the whole number portion is rounded. It is rounded to the place indicated by the count. * * @param number * @return */ public double ROUND(double number, int count) { return MathUtils.round(number, count, BigDecimal.ROUND_FLOOR); } public double ROUNDDOWN(double number, int count) { return MathUtils.round(number, count, BigDecimal.ROUND_DOWN); } public double ROUNDUP(double number, int count) { return MathUtils.round(number, count, BigDecimal.ROUND_UP); } /** * Returns a sum of powers of the number x in accordance with the following formula: SERIESSUM(x,n,m,coefficients) = * coefficient_1*x^n + coefficient_2*x^(n+m) + coefficient_3*x^(n+2m) +...+ coefficient_i*x^(n+(i-1)m). x is the * number as an independent variable. n is the starting power. m is the increment. Coefficients is a series of * coefficients. For each coefficient the series sum is extended by one section. * * @return */ public double SERIESSUM(double x, double n, double m, Iterator<Double> coefficients) { double result = 0; double pow = n; while (coefficients.hasNext()) { result += coefficients.next() * Math.pow(x, pow); pow += m; } return result; } public double SIGN(double number) { return Math.signum(number); } public double SIN(double number) { return Math.sin(number); } public double SINH(double number) { return Math.sinh(number); } public double SQRT(double number) { return Math.sqrt(number); } /** * Returns the square root of the product of the given number and PI. * * @param number * @return */ public double SQRTPI(double number) { return Math.sqrt(number * Math.PI); } public double TAN(double number) { return Math.tan(number); } public double TANH(double number) { return Math.tanh(number); } public double TRUNC(double number, int count) { return MathUtils.round(number, count, BigDecimal.ROUND_DOWN); } public double SUM(Iterator<Double> values) { double sum = 0; while (values.hasNext()) { sum += values.next(); } return sum; } /** * Adds the cells specified by a given criteria. Range is the range to which the criteria are to be applied. * Criteria is the cell in which the search criterion is shown, or the search criterion itself. Sum_range is the * range from which values are summed, if it has not been indicated, the values found in the Range are summed. * public double SUMIF(range, criteria, sum_range){ * * } */ public double SUMSQ(Iterator<Double> values) { double sum = 0; while (values.hasNext()) { double x = values.next(); sum += x * x; } return sum; } public double PI() { return Math.PI; } public static void main(String[] args) { System.out.println(new MathFunctions().ODD(5.5)); System.out.println(new MathFunctions().EVEN(12)); } }