Bugzilla 55043 and 55042: initial support for Complex and Quotient functions

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1488734 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2013-06-02 16:08:17 +00:00
parent 0dfb528355
commit 4d81d34d5d
4 changed files with 300 additions and 0 deletions

View File

@ -0,0 +1,119 @@
package org.apache.poi.ss.formula.functions;
import org.apache.poi.ss.formula.eval.*;
/**
* Implementation for Excel COMPLEX () function.<p/>
* <p/>
* <b>Syntax</b>:<br/> <b>COMPLEX </b>(<b>real_num</b>,<b>i_num</b>,<b>suffix </b> )<br/>
* <p/>
* Converts real and imaginary coefficients into a complex number of the form x + yi or x + yj.
* <p/>
* <p/>
* 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.
* <p/>
* <b>real_num</b> The real coefficient of the complex number.
* If this argument is nonnumeric, this function returns the #VALUE! error value.
* <p/>
* <p/>
* <b>i_num</b> The imaginary coefficient of the complex number.
* If this argument is nonnumeric, this function returns the #VALUE! error value.
* <p/>
* <p/>
* <b>suffix</b> The suffix for the imaginary component of the complex number.
* <ul>
* <li>If omitted, suffix is assumed to be "i".</li>
* <li>If suffix is neither "i" nor "j", COMPLEX returns the #VALUE! error value.</li>
* </ul>
*
* @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);
}
}

View File

@ -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.*;
/**
* <p>Implementation for Excel QUOTIENT () function.<p/>
* <p>
* <b>Syntax</b>:<br/> <b>QUOTIENT</b>(<b>Numerator</b>,<b>Denominator</b>)<br/>
* <p/>
* <p>
* 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.
* <p/>
*
* 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)));
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}