diff --git a/src/java/org/apache/poi/ss/formula/functions/Complex.java b/src/java/org/apache/poi/ss/formula/functions/Complex.java new file mode 100644 index 000000000..d37ab70de --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/functions/Complex.java @@ -0,0 +1,119 @@ +package org.apache.poi.ss.formula.functions; + +import org.apache.poi.ss.formula.eval.*; + +/** + * Implementation for Excel COMPLEX () function.

+ *

+ * Syntax:
COMPLEX (real_num,i_num,suffix )
+ *

+ * Converts real and imaginary coefficients into a complex number of the form x + yi or x + yj. + *

+ *

+ * All complex number functions accept "i" and "j" for suffix, but neither "I" nor "J". + * Using uppercase results in the #VALUE! error value. All functions that accept two + * or more complex numbers require that all suffixes match. + *

+ * real_num The real coefficient of the complex number. + * If this argument is nonnumeric, this function returns the #VALUE! error value. + *

+ *

+ * i_num The imaginary coefficient of the complex number. + * If this argument is nonnumeric, this function returns the #VALUE! error value. + *

+ *

+ * suffix The suffix for the imaginary component of the complex number. + *

+ * + * @author cedric dot walter @ gmail dot com + */ +public class Complex extends Var2or3ArgFunction { + + public static final String DEFAULT_SUFFIX = "i"; + public static final String SUPPORTED_SUFFIX = "j"; + + @Override + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval real_num, ValueEval i_num) { + return this.evaluate(srcRowIndex, srcColumnIndex, real_num, i_num, new StringEval(DEFAULT_SUFFIX)); + } + + @Override + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval real_num, ValueEval i_num, ValueEval suffix) { + ValueEval veText1; + try { + veText1 = OperandResolver.getSingleValue(real_num, srcRowIndex, srcColumnIndex); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + double realNum = 0; + try { + realNum = OperandResolver.coerceValueToDouble(veText1); + } catch (EvaluationException e) { + return ErrorEval.VALUE_INVALID; + } + + ValueEval veINum; + try { + veINum = OperandResolver.getSingleValue(i_num, srcRowIndex, srcColumnIndex); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + double realINum = 0; + try { + realINum = OperandResolver.coerceValueToDouble(veINum); + } catch (EvaluationException e) { + return ErrorEval.VALUE_INVALID; + } + + String suffixValue = OperandResolver.coerceValueToString(suffix); + if (suffixValue.isEmpty()) { + suffixValue = DEFAULT_SUFFIX; + } + if (suffixValue.equals(DEFAULT_SUFFIX.toUpperCase()) || suffixValue.equals(SUPPORTED_SUFFIX.toUpperCase())) { + return ErrorEval.VALUE_INVALID; + } + if (!(suffixValue.equals(DEFAULT_SUFFIX) || suffixValue.equals(SUPPORTED_SUFFIX))) { + return ErrorEval.VALUE_INVALID; + } + + StringBuffer strb = new StringBuffer(""); + if (realNum != 0) { + if (isDoubleAnInt(realNum)) { + strb.append(new Double(realNum).intValue()); + } else { + strb.append(realNum); + } + } + if (realINum != 0) { + if (strb.length() != 0) { + if (realINum > 0) { + strb.append("+"); + } + } + + if (realINum != 1 && realINum != -1) { + if (isDoubleAnInt(realINum)) { + strb.append(new Double(realINum).intValue()); + } else { + strb.append(realINum); + } + } + + strb.append(suffixValue); + } + + return new StringEval(strb.toString()); + } + + /** + * @param number + * @return + */ + private boolean isDoubleAnInt(double number) { + return (number == Math.floor(number)) && !Double.isInfinite(number); + } + +} diff --git a/src/java/org/apache/poi/ss/formula/functions/Quotient.java b/src/java/org/apache/poi/ss/formula/functions/Quotient.java new file mode 100644 index 000000000..b314f2f26 --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/functions/Quotient.java @@ -0,0 +1,49 @@ +package org.apache.poi.ss.formula.functions; + +import org.apache.poi.ss.formula.eval.ValueEval; + + +import org.apache.poi.ss.formula.eval.*; + +/** + *

Implementation for Excel QUOTIENT () function.

+ *

+ * Syntax:
QUOTIENT(Numerator,Denominator)
+ *

+ *

+ * Numerator is the dividend. + * Denominator is the divisor. + * + * Returns the integer portion of a division. Use this function when you want to discard the remainder of a division. + *

+ * + * If either enumerator/denominator is non numeric, QUOTIENT returns the #VALUE! error value. + * If denominator is equals to zero, QUOTIENT returns the #DIV/0! error value. + * + * @author cedric dot walter @ gmail dot com + */ +public class Quotient extends Fixed2ArgFunction { + @Override + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval venumerator, ValueEval vedenominator) { + + double enumerator = 0; + try { + enumerator = OperandResolver.coerceValueToDouble(venumerator); + } catch (EvaluationException e) { + return ErrorEval.VALUE_INVALID; + } + + double denominator = 0; + try { + denominator = OperandResolver.coerceValueToDouble(vedenominator); + } catch (EvaluationException e) { + return ErrorEval.VALUE_INVALID; + } + + if (denominator == 0) { + return ErrorEval.DIV_ZERO; + } + + return new StringEval(String.valueOf((int)(enumerator / denominator))); + } +} diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestComplex.java b/src/testcases/org/apache/poi/ss/formula/functions/TestComplex.java new file mode 100644 index 000000000..a37158898 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestComplex.java @@ -0,0 +1,69 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.ss.formula.functions; + +import junit.framework.TestCase; +import org.apache.poi.ss.formula.eval.ErrorEval; +import org.apache.poi.ss.formula.eval.StringEval; +import org.apache.poi.ss.formula.eval.ValueEval; + +/** + * Tests for {@link Complex} + * + * @author cedric dot walter @ gmail dot com + */ +public class TestComplex extends TestCase +{ + private static ValueEval invokeValue(String real_num, String i_num, String suffix) { + ValueEval[] args = new ValueEval[]{new StringEval(real_num), new StringEval(i_num), new StringEval(suffix)}; + return new Complex().evaluate(args, -1, -1); + } + + private static void confirmValue(String msg, String real_num, String i_num, String suffix, String expected) { + ValueEval result = invokeValue(real_num, i_num, suffix); + assertEquals(StringEval.class, result.getClass()); + assertEquals(msg, expected, ((StringEval) result).getStringValue()); + } + + private static void confirmValueError(String msg, String real_num, String i_num, String suffix, ErrorEval numError) { + ValueEval result = invokeValue(real_num, i_num, suffix); + assertEquals(ErrorEval.class, result.getClass()); + assertEquals(msg, numError, result); + } + + public void testBasic() { + confirmValue("Complex number with 3 and 4 as the real and imaginary coefficients (3 + 4i)", "3","4", "", "3+4i"); + confirmValue("Complex number with 3 and 4 as the real and imaginary coefficients, and j as the suffix (3 + 4j)", "3","4", "j", "3+4j"); + + confirmValue("Complex number with 0 and 1 as the real and imaginary coefficients (i)", "0","1", "", "i"); + confirmValue("Complex number with 1 and 0 as the real and imaginary coefficients (1)", "1","0", "", "1"); + + confirmValue("Complex number with 2 and 3 as the real and imaginary coefficients (2 + 3i)", "2","3", "", "2+3i"); + confirmValue("Complex number with -2 and -3 as the real and imaginary coefficients (-2-3i)", "-2","-3", "", "-2-3i"); + + confirmValue("Complex number with -2 and -3 as the real and imaginary coefficients (-0.5-3.2i)", "-0.5","-3.2", "", "-0.5-3.2i"); + } + + public void testErrors() { + confirmValueError("argument is nonnumeric", "ABCD", "","", ErrorEval.VALUE_INVALID); + confirmValueError("argument is nonnumeric", "1", "ABCD","", ErrorEval.VALUE_INVALID); + confirmValueError("f suffix is neither \"i\" nor \"j\"", "1", "1","k", ErrorEval.VALUE_INVALID); + + confirmValueError("never use \"I\" ", "1", "1","I", ErrorEval.VALUE_INVALID); + confirmValueError("never use \"J\" ", "1", "1","J", ErrorEval.VALUE_INVALID); + } +} \ No newline at end of file diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestQuotient.java b/src/testcases/org/apache/poi/ss/formula/functions/TestQuotient.java new file mode 100644 index 000000000..d9fef5fdd --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestQuotient.java @@ -0,0 +1,63 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.ss.formula.functions; + +import junit.framework.TestCase; +import org.apache.poi.ss.formula.eval.ErrorEval; +import org.apache.poi.ss.formula.eval.StringEval; +import org.apache.poi.ss.formula.eval.ValueEval; + +/** + * Tests for {@link Quotient} + * + * @author cedric dot walter @ gmail dot com + */ +public class TestQuotient extends TestCase { + private static ValueEval invokeValue(String numerator, String denominator) { + ValueEval[] args = new ValueEval[]{new StringEval(numerator), new StringEval(denominator)}; + return new Quotient().evaluate(args, -1, -1); + } + + private static void confirmValue(String msg, String numerator, String denominator, String expected) { + ValueEval result = invokeValue(numerator, denominator); + assertEquals(StringEval.class, result.getClass()); + assertEquals(msg, expected, ((StringEval) result).getStringValue()); + } + + private static void confirmValueError(String msg, String numerator, String denominator, ErrorEval numError) { + ValueEval result = invokeValue(numerator, denominator); + assertEquals(ErrorEval.class, result.getClass()); + assertEquals(msg, numError, result); + } + + public void testBasic() { + confirmValue("Integer portion of 5/2 (2)", "5", "2", "2"); + confirmValue("Integer portion of 4.5/3.1 (1)", "4.5", "3.1", "1"); + + confirmValue("Integer portion of -10/3 (-3)", "-10", "3", "-3"); + confirmValue("Integer portion of -5.5/2 (-2)", "-5.5", "2", "-2"); + + confirmValue("Integer portion of Pi/Avogadro (0)", "3.14159", "6.02214179E+23", "0"); + } + + public void testErrors() { + confirmValueError("numerator is nonnumeric", "ABCD", "", ErrorEval.VALUE_INVALID); + confirmValueError("denominator is nonnumeric", "", "ABCD", ErrorEval.VALUE_INVALID); + + confirmValueError("denominator is nonnumeric", "3.14159", "0", ErrorEval.DIV_ZERO); + } +} \ No newline at end of file