diff --git a/main/HSSF/Model/InternalWorkbook.cs b/main/HSSF/Model/InternalWorkbook.cs index 1d2e724dd..f580ad995 100644 --- a/main/HSSF/Model/InternalWorkbook.cs +++ b/main/HSSF/Model/InternalWorkbook.cs @@ -899,6 +899,8 @@ public void RemoveSheet(int sheetnum) // Bump down by one, so still points // at the same sheet nr.SheetNumber=(nr.SheetNumber - 1); + // also update the link-table as otherwise references might point at invalid sheets + linkTable.UpdateIndexToInternalSheet(i, -1); } } } @@ -3004,14 +3006,14 @@ public RecalcIdRecord RecalcId } /** - * Changes an external referenced file to another file. - * A formular in Excel which refers a cell in another file is saved in two parts: - * The referenced file is stored in an reference table. the row/cell information is saved separate. - * This method invokation will only change the reference in the lookup-table itself. - * @param oldUrl The old URL to search for and which is to be replaced - * @param newUrl The URL replacement - * @return true if the oldUrl was found and replaced with newUrl. Otherwise false - */ + * Changes an external referenced file to another file. + * A formular in Excel which refers a cell in another file is saved in two parts: + * The referenced file is stored in an reference table. the row/cell information is saved separate. + * This method invokation will only change the reference in the lookup-table itself. + * @param oldUrl The old URL to search for and which is to be replaced + * @param newUrl The URL replacement + * @return true if the oldUrl was found and replaced with newUrl. Otherwise false + */ public bool ChangeExternalReference(String oldUrl, String newUrl) { return linkTable.ChangeExternalReference(oldUrl, newUrl); diff --git a/main/HSSF/Model/LinkTable.cs b/main/HSSF/Model/LinkTable.cs index 507c3ea1a..42c27d19b 100644 --- a/main/HSSF/Model/LinkTable.cs +++ b/main/HSSF/Model/LinkTable.cs @@ -334,13 +334,18 @@ public void RemoveBuiltinRecord(byte name, int sheetIndex) // TODO - do we need "Workbook.records.Remove(...);" similar to that in Workbook.RemoveName(int namenum) {}? } /** - * @param extRefIndex as from a {@link Ref3DPtg} or {@link Area3DPtg} - * @return -1 if the reference is to an external book - */ + * @param extRefIndex as from a {@link Ref3DPtg} or {@link Area3DPtg} + * @return -1 if the reference is to an external book + */ public int GetIndexToInternalSheet(int extRefIndex) { return _externSheetRecord.GetFirstSheetIndexFromRefIndex(extRefIndex); } + + public void UpdateIndexToInternalSheet(int extRefIndex, int offset) + { + _externSheetRecord.AdjustIndex(extRefIndex, offset); + } public int NumNames { get diff --git a/main/HSSF/Record/ExternSheetRecord.cs b/main/HSSF/Record/ExternSheetRecord.cs index e7b173e8b..9325a35b1 100644 --- a/main/HSSF/Record/ExternSheetRecord.cs +++ b/main/HSSF/Record/ExternSheetRecord.cs @@ -35,7 +35,11 @@ public class RefSubRecord private int _firstSheetIndex; // may be -1 (0xFFFF) private int _lastSheetIndex; // may be -1 (0xFFFF) - + public void AdjustIndex(int offset) + { + _firstSheetIndex += offset; + _lastSheetIndex += offset; + } /** a Constructor for making new sub record */ public RefSubRecord(int extBookIndex, int firstSheetIndex, int lastSheetIndex) @@ -172,7 +176,7 @@ public int NumOfREFRecords return _list.Count; } } - + /** * @return number of REF structures */ @@ -196,6 +200,11 @@ private RefSubRecord GetRef(int i) { return (RefSubRecord)_list[i]; } + + public void AdjustIndex(int extRefIndex, int offset) + { + GetRef(extRefIndex).AdjustIndex(offset); + } public int GetExtbookIndexFromRefIndex(int refIndex) { return GetRef(refIndex).ExtBookIndex; @@ -233,7 +242,7 @@ public int GetFirstSheetIndexFromRefIndex(int extRefIndex) { return GetRef(extRefIndex).FirstSheetIndex; } - + public override String ToString() { StringBuilder sb = new StringBuilder(); diff --git a/main/SS/Formula/Atp/AnalysisToolPak.cs b/main/SS/Formula/Atp/AnalysisToolPak.cs index f584c08ad..f8fab56da 100644 --- a/main/SS/Formula/Atp/AnalysisToolPak.cs +++ b/main/SS/Formula/Atp/AnalysisToolPak.cs @@ -148,7 +148,7 @@ private static Hashtable CreateFunctionsMap() r(m, "NETWORKDAYS", NetworkdaysFunction.instance); r(m, "NOMINAL", null); r(m, "OCT2BIN", null); - r(m, "OCT2DEC", null); + r(m, "OCT2DEC", Oct2Dec.instance); r(m, "OCT2HEX", null); r(m, "ODDFPRICE", null); r(m, "ODDFYIELD", null); diff --git a/main/SS/Formula/Eval/FunctionEval.cs b/main/SS/Formula/Eval/FunctionEval.cs index 0f4d41f90..7147c1213 100644 --- a/main/SS/Formula/Eval/FunctionEval.cs +++ b/main/SS/Formula/Eval/FunctionEval.cs @@ -154,7 +154,7 @@ private static Function[] ProduceFunctions() retval[58] = FinanceFunction.NPER; // NPER retval[59] = FinanceFunction.PMT; // PMT retval[60] = new Rate(); // RATE - retval[61] = new NotImplementedFunction("MIRR"); // MIRR + retval[61] = new Mirr(); // MIRR retval[62] = new Irr(); // IRR retval[63] = new Rand(); // RAND retval[64] = new Match(); // MATCH @@ -407,7 +407,7 @@ private static Function[] ProduceFunctions() retval[325] = AggregateFunction.LARGE; // LARGE retval[326] = AggregateFunction.SMALL; // SMALL retval[327] = new NotImplementedFunction("QUARTILE"); // QUARTILE - retval[328] = new NotImplementedFunction("PERCENTILE"); // PERCENTILE + retval[328] = AggregateFunction.PERCENTILE; // PERCENTILE retval[329] = new NotImplementedFunction("PERCENTRANK"); // PERCENTRANK retval[330] = new Mode(); // MODE retval[331] = new NotImplementedFunction("TRIMMEAN"); // TRIMMEAN diff --git a/main/SS/Formula/Functions/AggregateFunction.cs b/main/SS/Formula/Functions/AggregateFunction.cs index 39b0f3897..ad5f8aa84 100644 --- a/main/SS/Formula/Functions/AggregateFunction.cs +++ b/main/SS/Formula/Functions/AggregateFunction.cs @@ -173,6 +173,136 @@ public override bool IsSubtotalCounted } } } + + + public class LargeSmall : Fixed2ArgFunction + { + private bool _isLarge; + protected LargeSmall(bool isLarge) + { + _isLarge = isLarge; + } + + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, + ValueEval arg1) + { + double dn; + try + { + ValueEval ve1 = OperandResolver.GetSingleValue(arg1, srcRowIndex, srcColumnIndex); + dn = OperandResolver.CoerceValueToDouble(ve1); + } + catch (EvaluationException e1) + { + // all errors in the second arg translate to #VALUE! + return ErrorEval.VALUE_INVALID; + } + // weird Excel behaviour on second arg + if (dn < 1.0) + { + // values between 0.0 and 1.0 result in #NUM! + return ErrorEval.NUM_ERROR; + } + // all other values are rounded up to the next integer + int k = (int)Math.Ceiling(dn); + + double result; + try + { + double[] ds = NPOI.SS.Formula.Functions.AggregateFunction.ValueCollector.CollectValues(arg0); + if (k > ds.Length) + { + return ErrorEval.NUM_ERROR; + } + result = _isLarge ? StatsLib.kthLargest(ds, k) : StatsLib.kthSmallest(ds, k); + NumericFunction.CheckValue(result); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + + return new NumberEval(result); + } + } + + /** + * Returns the k-th percentile of values in a range. You can use this function to establish a threshold of + * acceptance. For example, you can decide to examine candidates who score above the 90th percentile. + * + * PERCENTILE(array,k) + * Array is the array or range of data that defines relative standing. + * K is the percentile value in the range 0..1, inclusive. + * + * Remarks + * + */ + public class Percentile : Fixed2ArgFunction + { + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, + ValueEval arg1) + { + double dn; + try + { + ValueEval ve1 = OperandResolver.GetSingleValue(arg1, srcRowIndex, srcColumnIndex); + dn = OperandResolver.CoerceValueToDouble(ve1); + } + catch (EvaluationException e1) + { + // all errors in the second arg translate to #VALUE! + return ErrorEval.VALUE_INVALID; + } + if (dn < 0 || dn > 1) + { // has to be percentage + return ErrorEval.NUM_ERROR; + } + + double result; + try + { + double[] ds = NPOI.SS.Formula.Functions.AggregateFunction.ValueCollector.CollectValues(arg0); + int N = ds.Length; + + if (N == 0 || N > 8191) + { + return ErrorEval.NUM_ERROR; + } + + double n = (N - 1) * dn + 1; + if (n == 1d) + { + result = StatsLib.kthSmallest(ds, 1); + } + else if (n == N) + { + result = StatsLib.kthLargest(ds, 1); + } + else + { + int k = (int)n; + double d = n - k; + result = StatsLib.kthSmallest(ds, k) + d + * (StatsLib.kthSmallest(ds, k + 1) - StatsLib.kthSmallest(ds, k)); + } + + NumericFunction.CheckValue(result); + } + catch (EvaluationException e) + { + return e.GetErrorEval(); + } + + return new NumberEval(result); + } + } + + /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * @@ -220,5 +350,6 @@ protected AggregateFunction() public static readonly Function SUMSQ = new SUMSQ(); public static readonly Function VAR = new VAR(); public static readonly Function VARP = new VARP(); + public static readonly Function PERCENTILE = new Percentile(); } } diff --git a/main/SS/Formula/Functions/BaseNumberUtils.cs b/main/SS/Formula/Functions/BaseNumberUtils.cs new file mode 100644 index 000000000..c1e5525e2 --- /dev/null +++ b/main/SS/Formula/Functions/BaseNumberUtils.cs @@ -0,0 +1,98 @@ +/* ==================================================================== + 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. +==================================================================== */ +namespace NPOI.SS.Formula.Functions +{ + using System; + + /** + *

Some utils for Converting from and to any base

+ * + * @author cedric dot walter @ gmail dot com + */ + public class BaseNumberUtils + { + public static double ConvertToDecimal(String value, int base1, int maxNumberOfPlaces) + { + if (string.IsNullOrEmpty(value)) + { + return 0.0; + } + + long stringLength = value.Length; + if (stringLength > maxNumberOfPlaces) + { + throw new ArgumentException(); + } + + double decimalValue = 0.0; + + long signedDigit = 0; + bool hasSignedDigit = true; + char[] characters = value.ToCharArray(); + foreach (char character in characters) + { + long digit; + + if ('0' <= character && character <= '9') + { + digit = character - '0'; + } + else if ('A' <= character && character <= 'Z') + { + digit = 10 + (character - 'A'); + } + else if ('a' <= character && character <= 'z') + { + digit = 10 + (character - 'a'); + } + else + { + digit = base1; + } + + if (digit < base1) + { + if (hasSignedDigit) + { + hasSignedDigit = false; + signedDigit = digit; + } + decimalValue = decimalValue * base1 + digit; + } + else + { + throw new ArgumentException("character not allowed"); + } + } + + bool isNegative = (!hasSignedDigit && stringLength == maxNumberOfPlaces && (signedDigit >= base1 / 2)); + if (isNegative) + { + decimalValue = GetTwoComplement(base1, maxNumberOfPlaces, decimalValue); + decimalValue = decimalValue * -1.0; + } + + return decimalValue; + } + + private static double GetTwoComplement(double base1, double maxNumberOfPlaces, double decimalValue) + { + return (Math.Pow(base1, maxNumberOfPlaces) - decimalValue); + } + } + +} \ No newline at end of file diff --git a/main/SS/Formula/Functions/Hex2Dec.cs b/main/SS/Formula/Functions/Hex2Dec.cs index 121e61746..3fd8a11c1 100644 --- a/main/SS/Formula/Functions/Hex2Dec.cs +++ b/main/SS/Formula/Functions/Hex2Dec.cs @@ -47,21 +47,12 @@ public class Hex2Dec : Fixed1ArgFunction, FreeRefFunction public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval numberVE) { - String hex; - if (numberVE is RefEval) - { - RefEval re = (RefEval)numberVE; - hex = OperandResolver.CoerceValueToString(re.InnerValueEval); - } - else - { - hex = OperandResolver.CoerceValueToString(numberVE); - } + String hex = OperandResolver.CoerceValueToString(numberVE); try { - return new NumberEval(Convert.ToInt64(hex, HEXADECIMAL_BASE)); + return new NumberEval(BaseNumberUtils.ConvertToDecimal(hex, HEXADECIMAL_BASE, MAX_NUMBER_OF_PLACES)); } - catch (ArgumentException e) + catch (ArgumentException) { return ErrorEval.NUM_ERROR; } diff --git a/main/SS/Formula/Functions/LinearRegressionFunction.cs b/main/SS/Formula/Functions/LinearRegressionFunction.cs index 8c9a09442..cf8ba72b8 100644 --- a/main/SS/Formula/Functions/LinearRegressionFunction.cs +++ b/main/SS/Formula/Functions/LinearRegressionFunction.cs @@ -161,7 +161,6 @@ private double EvaluateInternal(ValueVector x, ValueVector y, int size) ErrorEval firstXerr = null; ErrorEval firstYerr = null; bool accumlatedSome = false; - double result = 0.0; // first pass: read in data, compute xbar and ybar double sumx = 0.0, sumy = 0.0; diff --git a/main/SS/Formula/Functions/Mirr.cs b/main/SS/Formula/Functions/Mirr.cs new file mode 100644 index 000000000..2b16f09be --- /dev/null +++ b/main/SS/Formula/Functions/Mirr.cs @@ -0,0 +1,130 @@ +/* ==================================================================== + 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. +==================================================================== */ + + +namespace NPOI.SS.Formula.Functions +{ + using System; + + using NPOI.SS.Formula.Eval; + using NPOI.SS.Formula.Eval; + + /** + * Calculates Modified internal rate of return. Syntax is MIRR(cash_flow_values, finance_rate, reinvest_rate) + * + *

Returns the modified internal rate of return for a series of periodic cash flows. MIRR considers both the cost + * of the investment and the interest received on reinvestment of cash.

+ * + * Values is an array or a reference to cells that contain numbers. These numbers represent a series of payments (negative values) and income (positive values) occurring at regular periods. + * + * + * Finance_rate is the interest rate you pay on the money used in the cash flows. + * Reinvest_rate is the interest rate you receive on the cash flows as you reinvest them. + * + * @author Carlos Delgado (carlos dot del dot est at gmail dot com) + * @author Cédric Walter (cedric dot walter at gmail dot com) + * + * @see Wikipedia on MIRR + * @see Excel MIRR + * @see {@link Irr} + */ + public class Mirr : MultiOperandNumericFunction + { + + public Mirr() + : base(false, false) + { + } + + + protected override int MaxNumOperands + { + get + { + return 3; + } + } + + + protected internal override double Evaluate(double[] values) + { + + double financeRate = values[values.Length - 1]; + double reinvestRate = values[values.Length - 2]; + + double[] mirrValues = new double[values.Length - 2]; + Array.Copy(values, 0, mirrValues, 0, mirrValues.Length); + + bool mirrValuesAreAllNegatives = true; + foreach (double mirrValue in mirrValues) + { + mirrValuesAreAllNegatives &= mirrValue < 0; + } + if (mirrValuesAreAllNegatives) + { + return -1.0d; + } + + bool mirrValuesAreAllPositives = true; + foreach (double mirrValue in mirrValues) + { + mirrValuesAreAllPositives &= mirrValue > 0; + } + if (mirrValuesAreAllPositives) + { + throw new EvaluationException(ErrorEval.DIV_ZERO); + } + + return mirr(mirrValues, financeRate, reinvestRate); + } + + private static double mirr(double[] in1, double financeRate, double reinvestRate) + { + double value = 0; + int numOfYears = in1.Length - 1; + double pv = 0; + double fv = 0; + + int indexN = 0; + foreach (double anIn in in1) + { + if (anIn < 0) + { + pv += anIn / Math.Pow(1 + financeRate + reinvestRate, indexN++); + } + } + + foreach (double anIn in in1) + { + if (anIn > 0) + { + fv += anIn * Math.Pow(1 + financeRate, numOfYears - indexN++); + } + } + + if (fv != 0 && pv != 0) + { + value = Math.Pow(-fv / pv, 1d / numOfYears) - 1; + } + return value; + } + } + +} \ No newline at end of file diff --git a/main/SS/Formula/Functions/MultiOperandNumericFunction.cs b/main/SS/Formula/Functions/MultiOperandNumericFunction.cs index 4e7f47962..6b0d93669 100644 --- a/main/SS/Formula/Functions/MultiOperandNumericFunction.cs +++ b/main/SS/Formula/Functions/MultiOperandNumericFunction.cs @@ -115,7 +115,7 @@ public void Add(double value) * Maximum number of operands accepted by this function. * Subclasses may override to Change default value. */ - protected int MaxNumOperands + protected virtual int MaxNumOperands { get { diff --git a/main/SS/Formula/Functions/Oct2Dec.cs b/main/SS/Formula/Functions/Oct2Dec.cs new file mode 100644 index 000000000..3755dbaea --- /dev/null +++ b/main/SS/Formula/Functions/Oct2Dec.cs @@ -0,0 +1,72 @@ +/* ==================================================================== + 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. +==================================================================== */ +namespace NPOI.SS.Formula.Functions +{ + using System; + + using NPOI.SS.Formula; + using NPOI.SS.Formula.Eval; + + /** + *

Implementation for Excel Oct2Dec() function.

+ *

+ * Converts an octal number to decimal. + *

+ *

+ * Syntax:
Oct2Dec (number ) + *

+ *

+ * Number is the octal number you want to Convert. Number may not contain more than 10 octal characters (30 bits). + * The most significant bit of number is the sign bit. The remaining 29 bits are magnitude bits. + * Negative numbers are represented using two's-complement notation.. + *

+ * If number is not a valid octal number, OCT2DEC returns the #NUM! error value. + * + * @author cedric dot walter @ gmail dot com + */ + public class Oct2Dec : Fixed1ArgFunction, FreeRefFunction + { + + public static FreeRefFunction instance = new Oct2Dec(); + + static int MAX_NUMBER_OF_PLACES = 10; + static int OCTAL_BASE = 8; + + public override ValueEval Evaluate(int srcRowIndex, int srcColumnIndex, ValueEval numberVE) + { + String octal = OperandResolver.CoerceValueToString(numberVE); + try + { + return new NumberEval(BaseNumberUtils.ConvertToDecimal(octal, OCTAL_BASE, MAX_NUMBER_OF_PLACES)); + } + catch (ArgumentException) + { + return ErrorEval.NUM_ERROR; + } + } + + public ValueEval Evaluate(ValueEval[] args, OperationEvaluationContext ec) + { + if (args.Length != 1) + { + return ErrorEval.VALUE_INVALID; + } + return Evaluate(ec.RowIndex, ec.ColumnIndex, args[0]); + } + } + +} \ No newline at end of file diff --git a/main/Util/AssertFailedException.cs b/main/Util/AssertFailedException.cs new file mode 100644 index 000000000..700ff0113 --- /dev/null +++ b/main/Util/AssertFailedException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NPOI.Util +{ + class AssertFailedException : ApplicationException + { + public AssertFailedException(string message) + : base(message) + { + + } + } +} diff --git a/main/main vs10.csproj b/main/main vs10.csproj index 499087900..4d9945379 100644 --- a/main/main vs10.csproj +++ b/main/main vs10.csproj @@ -200,6 +200,7 @@ + @@ -216,6 +217,8 @@ + + @@ -650,6 +653,7 @@ + diff --git a/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs b/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs index 8ae4465de..7f0a88c04 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs @@ -1119,5 +1119,58 @@ public void TestBug50298a() HSSFWorkbook read = HSSFTestDataSamples.WriteOutAndReadBack(wb); assertSheetOrder(wb, "Invoice", "Deferred", "Received", "Digest"); } + + [Test] + public void TestBug54500() + { + String nameName = "AName"; + String sheetName = "ASheet"; + IWorkbook wb = HSSFTestDataSamples.OpenSampleWorkbook("54500.xls"); + + assertSheetOrder(wb, "Sheet1", "Sheet2", "Sheet3"); + + wb.CreateSheet(sheetName); + + assertSheetOrder(wb, "Sheet1", "Sheet2", "Sheet3", "ASheet"); + + IName n = wb.CreateName(); + n.NameName = (/*setter*/nameName); + n.SheetIndex = (/*setter*/3); + n.RefersToFormula = (/*setter*/sheetName + "!A1"); + + assertSheetOrder(wb, "Sheet1", "Sheet2", "Sheet3", "ASheet"); + Assert.AreEqual("ASheet!A1", wb.GetName(nameName).RefersToFormula); + + MemoryStream stream = new MemoryStream(); + wb.Write(stream); + + assertSheetOrder(wb, "Sheet1", "Sheet2", "Sheet3", "ASheet"); + Assert.AreEqual("ASheet!A1", wb.GetName(nameName).RefersToFormula); + + wb.RemoveSheetAt(1); + + assertSheetOrder(wb, "Sheet1", "Sheet3", "ASheet"); + Assert.AreEqual("ASheet!A1", wb.GetName(nameName).RefersToFormula); + + MemoryStream stream2 = new MemoryStream(); + wb.Write(stream2); + + assertSheetOrder(wb, "Sheet1", "Sheet3", "ASheet"); + Assert.AreEqual("ASheet!A1", wb.GetName(nameName).RefersToFormula); + + expectName( + new HSSFWorkbook(new MemoryStream(stream.ToArray())), + nameName, "ASheet!A1"); + expectName( + new HSSFWorkbook( + new MemoryStream(stream2.ToArray())), + nameName, "ASheet!A1"); + } + + private void expectName(HSSFWorkbook wb, String name, String expect) + { + Assert.AreEqual(expect, wb.GetName(name).RefersToFormula); + } + } } diff --git a/testcases/main/SS/Formula/Functions/TestHex2Dec.cs b/testcases/main/SS/Formula/Functions/TestHex2Dec.cs new file mode 100644 index 000000000..0fc4023d3 --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestHex2Dec.cs @@ -0,0 +1,70 @@ +/* ==================================================================== + 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. +==================================================================== */ + +namespace NPOI.SS.Formula.Functions +{ + using System; + + using NPOI.SS.Formula.Eval; + using NUnit.Framework; + + /** + * Tests for {@link Hex2Dec} + * + * @author cedric dot walter @ gmail dot com + */ + [TestFixture] + public class TestHex2Dec + { + + private static ValueEval invokeValue(String number1) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1) }; + return new Hex2Dec().Evaluate(args, -1, -1); + } + + private static void ConfirmValue(String msg, String number1, String expected) + { + ValueEval result = invokeValue(number1); + Assert.AreEqual(typeof(NumberEval), result.GetType()); + Assert.AreEqual(expected, ((NumberEval)result).StringValue, msg); + } + + private static void ConfirmValueError(String msg, String number1, ErrorEval numError) + { + ValueEval result = invokeValue(number1); + Assert.AreEqual(typeof(ErrorEval), result.GetType()); + Assert.AreEqual(numError, result, msg); + } + + [Test] + public void TestBasic() + { + ConfirmValue("Converts octal 'A5' to decimal (165)", "A5", "165"); + ConfirmValue("Converts octal FFFFFFFF5B to decimal (-165)", "FFFFFFFF5B", "-165"); + ConfirmValue("Converts octal 3DA408B9 to decimal (-165)", "3DA408B9", "1034160313"); + } + + [Test] + public void TestErrors() + { + ConfirmValueError("not a valid octal number", "GGGGGGG", ErrorEval.NUM_ERROR); + ConfirmValueError("not a valid octal number", "3.14159", ErrorEval.NUM_ERROR); + } + } + +} \ No newline at end of file diff --git a/testcases/main/SS/Formula/Functions/TestMirr.cs b/testcases/main/SS/Formula/Functions/TestMirr.cs new file mode 100644 index 000000000..a988e3f9f --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestMirr.cs @@ -0,0 +1,196 @@ +/* ==================================================================== + 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. +==================================================================== */ + +namespace NPOI.SS.Formula.Functions +{ + using System; + using System.Text; + using NPOI.HSSF.UserModel; + using NPOI.SS.Formula.Eval; + using NPOI.SS.UserModel; + using NPOI.Util; + using NUnit.Framework; + using TestCases.HSSF; + + /** + * Tests for {@link NPOI.SS.Formula.Functions.Mirr} + * + * @author Carlos Delgado (carlos dot del dot est at gmail dot com) + * @author Cédric Walter (cedric dot walter at gmail dot com) + * @see {@link NPOI.SS.Formula.Functions.TestIrr} + */ + [TestFixture] + public class TestMirr + { + + [Test] + public void TestMirrBasic() + { + Mirr mirr = new Mirr(); + double mirrValue; + + double financeRate = 0.12; + double reinvestRate = 0.1; + double[] values = { -120000d, 39000d, 30000d, 21000d, 37000d, 46000d, reinvestRate, financeRate }; + try + { + mirrValue = mirr.Evaluate(values); + } + catch (EvaluationException e) + { + throw new AssertFailedException("MIRR should not failed with these parameters" + e); + } + Assert.AreEqual(0.126094130366, mirrValue, 0.0000000001, "mirr"); + + reinvestRate = 0.05; + financeRate = 0.08; + values = new double[] { -7500d, 3000d, 5000d, 1200d, 4000d, reinvestRate, financeRate }; + try + { + mirrValue = mirr.Evaluate(values); + } + catch (EvaluationException e) + { + throw new AssertFailedException("MIRR should not failed with these parameters" + e); + } + Assert.AreEqual(0.18736225093, mirrValue, 0.0000000001, "mirr"); + + reinvestRate = 0.065; + financeRate = 0.1; + values = new double[] { -10000, 3400d, 6500d, 1000d, reinvestRate, financeRate }; + try + { + mirrValue = mirr.Evaluate(values); + } + catch (EvaluationException e) + { + throw new AssertFailedException("MIRR should not failed with these parameters" + e); + } + Assert.AreEqual(0.07039493966, mirrValue, 0.0000000001, "mirr"); + + reinvestRate = 0.07; + financeRate = 0.01; + values = new double[] { -10000d, -3400d, -6500d, -1000d, reinvestRate, financeRate }; + try + { + mirrValue = mirr.Evaluate(values); + } + catch (EvaluationException e) + { + throw new AssertFailedException("MIRR should not failed with these parameters" + e); + } + Assert.AreEqual(-1, mirrValue, 0.0, "mirr"); + + } + + [Test] + public void TestMirrErrors_expectDIV0() + { + Mirr mirr = new Mirr(); + + double reinvestRate = 0.05; + double financeRate = 0.08; + double[] incomes = { 120000d, 39000d, 30000d, 21000d, 37000d, 46000d, reinvestRate, financeRate }; + try + { + mirr.Evaluate(incomes); + } + catch (EvaluationException e) + { + Assert.AreEqual(ErrorEval.DIV_ZERO, e.GetErrorEval()); + return; + } + throw new AssertFailedException("MIRR should failed with all these positives values"); + } + + + [Test] + public void TestEvaluateInSheet() + { + IWorkbook wb = new HSSFWorkbook(); + ISheet sheet = wb.CreateSheet("Sheet1"); + IRow row = sheet.CreateRow(0); + + row.CreateCell(0).SetCellValue(-7500d); + row.CreateCell(1).SetCellValue(3000d); + row.CreateCell(2).SetCellValue(5000d); + row.CreateCell(3).SetCellValue(1200d); + row.CreateCell(4).SetCellValue(4000d); + + row.CreateCell(5).SetCellValue(0.05d); + row.CreateCell(6).SetCellValue(0.08d); + + ICell cell = row.CreateCell(7); + cell.CellFormula = (/*setter*/"MIRR(A1:E1, F1, G1)"); + + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + fe.ClearAllCachedResultValues(); + fe.EvaluateFormulaCell(cell); + double res = cell.NumericCellValue; + Assert.AreEqual(0.18736225093, res, 0.00000001); + } + + [Test] + public void TestMirrFromSpreadsheet() + { + IWorkbook wb = HSSFTestDataSamples.OpenSampleWorkbook("mirrTest.xls"); + ISheet sheet = wb.GetSheet("Mirr"); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); + StringBuilder failures = new StringBuilder(); + int failureCount = 0; + int[] resultRows = { 9, 19, 29, 45 }; + + foreach (int rowNum in resultRows) + { + IRow row1 = sheet.GetRow(rowNum); + ICell cellA1 = row1.GetCell(0); + try + { + CellValue cv1 = fe.Evaluate(cellA1); + assertFormulaResult(cv1, cellA1); + } + catch (Exception e) + { + if (failures.Length > 0) failures.Append('\n'); + failures.Append("Row[").Append(cellA1.RowIndex + 1).Append("]: ").Append(cellA1.CellFormula).Append(" "); + failures.Append(e.Message); + failureCount++; + } + } + + IRow row = sheet.GetRow(37); + ICell cellA = row.GetCell(0); + CellValue cv = fe.Evaluate(cellA); + Assert.AreEqual(ErrorEval.DIV_ZERO.ErrorCode, cv.ErrorValue); + + if (failures.Length > 0) + { + throw new AssertFailedException(failureCount + " IRR assertions failed:\n" + failures.ToString()); + } + + } + + private static void assertFormulaResult(CellValue cv, ICell cell) + { + double actualValue = cv.NumberValue; + double expectedValue = cell.NumericCellValue; // cached formula result calculated by Excel + Assert.AreEqual(CellType.Numeric, cv.CellType, "Invalid formula result: " + cv.ToString()); + Assert.AreEqual(expectedValue, actualValue, 1E-8); + } + } + +} \ No newline at end of file diff --git a/testcases/main/SS/Formula/Functions/TestOct2Dec.cs b/testcases/main/SS/Formula/Functions/TestOct2Dec.cs new file mode 100644 index 000000000..4ff90422d --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestOct2Dec.cs @@ -0,0 +1,72 @@ +/* ==================================================================== + 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. +==================================================================== */ + +namespace NPOI.SS.Formula.Functions +{ + using System; + using NPOI.SS.Formula.Eval; + using NUnit.Framework; + + /** + * Tests for {@link NPOI.SS.Formula.Functions.Oct2Dec} + * + * @author cedric dot walter @ gmail dot com + */ + [TestFixture] + public class TestOct2Dec + { + + private static ValueEval invokeValue(String number1) + { + ValueEval[] args = new ValueEval[] { new StringEval(number1) }; + return new Oct2Dec().Evaluate(args, -1, -1); + } + + private static void ConfirmValue(String msg, String number1, String expected) + { + ValueEval result = invokeValue(number1); + Assert.AreEqual(typeof(NumberEval), result.GetType()); + Assert.AreEqual(expected, ((NumberEval)result).StringValue, msg); + } + + private static void ConfirmValueError(String msg, String number1, ErrorEval numError) + { + ValueEval result = invokeValue(number1); + Assert.AreEqual(typeof(ErrorEval), result.GetType()); + Assert.AreEqual(numError, result, msg); + } + + [Test] + public void TestBasic() + { + ConfirmValue("Converts octal '' to decimal (0)", "", "0"); + ConfirmValue("Converts octal 54 to decimal (44)", "54", "44"); + ConfirmValue("Converts octal 7777777533 to decimal (-165)", "7777777533", "-165"); + ConfirmValue("Converts octal 7000000000 to decimal (-134217728)", "7000000000", "-134217728"); + ConfirmValue("Converts octal 7776667533 to decimal (-299173)", "7776667533", "-299173"); + } + + [Test] + public void TestErrors() + { + ConfirmValueError("not a valid octal number", "ABCDEFGH", ErrorEval.NUM_ERROR); + ConfirmValueError("not a valid octal number", "99999999", ErrorEval.NUM_ERROR); + ConfirmValueError("not a valid octal number", "3.14159", ErrorEval.NUM_ERROR); + } + } + +} \ No newline at end of file diff --git a/testcases/main/SS/Formula/Functions/TestPercentile.cs b/testcases/main/SS/Formula/Functions/TestPercentile.cs new file mode 100644 index 000000000..6070ae9db --- /dev/null +++ b/testcases/main/SS/Formula/Functions/TestPercentile.cs @@ -0,0 +1,131 @@ +/* ==================================================================== + 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. +==================================================================== */ +namespace NPOI.SS.Formula.Atp +{ + + using NPOI.SS.Formula.Eval; + using NPOI.SS.Formula.Functions; + + using NUnit.Framework; + using TestCases.SS.Formula.Functions; + + /** + * Testcase for Excel function PERCENTILE() + * + * @author T. Gordon + */ + [TestFixture] + public class TestPercentile + { + + //[Test] + //public void TestPercentile() + //{ + // testBasic(); + // testUnusualArgs(); + // testUnusualArgs2(); + // testUnusualArgs3(); + // testErrors(); + // testErrors2(); + //} + + private static ValueEval invokePercentile(ValueEval[] args, ValueEval percentile) + { + AreaEval aeA = EvalFactory.CreateAreaEval("A1:A" + args.Length, args); + ValueEval[] args2 = { aeA, percentile }; + return AggregateFunction.PERCENTILE.Evaluate(args2, -1, -1); + } + + private void ConfirmPercentile(ValueEval percentile, ValueEval[] args, double expected) + { + ValueEval result = invokePercentile(args, percentile); + Assert.AreEqual(typeof(NumberEval), result.GetType()); + double delta = 0.00000001; + Assert.AreEqual(expected, ((NumberEval)result).NumberValue, delta); + } + + private void ConfirmPercentile(ValueEval percentile, ValueEval[] args, ErrorEval expectedError) + { + ValueEval result = invokePercentile(args, percentile); + Assert.AreEqual(typeof(ErrorEval), result.GetType()); + Assert.AreEqual(expectedError.ErrorCode, ((ErrorEval)result).ErrorCode); + } + + [Test] + public void TestBasic() + { + ValueEval[] values = { new NumberEval(210.128), new NumberEval(65.2182), new NumberEval(32.231), + new NumberEval(12.123), new NumberEval(45.32) }; + ValueEval percentile = new NumberEval(0.95); + ConfirmPercentile(percentile, values, 181.14604); + } + + [Test] + public void TestBlanks() + { + ValueEval[] values = { new NumberEval(210.128), new NumberEval(65.2182), new NumberEval(32.231), + BlankEval.instance, new NumberEval(45.32) }; + ValueEval percentile = new NumberEval(0.95); + ConfirmPercentile(percentile, values, 188.39153); + } + + [Test] + public void TestUnusualArgs() + { + ValueEval[] values = { new NumberEval(1), new NumberEval(2), BoolEval.TRUE, BoolEval.FALSE }; + ValueEval percentile = new NumberEval(0.95); + ConfirmPercentile(percentile, values, 1.95); + } + + //percentile has to be between 0 and 1 - here we test less than zero + [Test] + public void TestUnusualArgs2() + { + ValueEval[] values = { new NumberEval(1), new NumberEval(2), }; + ValueEval percentile = new NumberEval(-0.1); + ConfirmPercentile(percentile, values, ErrorEval.NUM_ERROR); + } + + //percentile has to be between 0 and 1 - here we test more than 1 + [Test] + public void TestUnusualArgs3() + { + ValueEval[] values = { new NumberEval(1), new NumberEval(2) }; + ValueEval percentile = new NumberEval(1.1); + ConfirmPercentile(percentile, values, ErrorEval.NUM_ERROR); + } + + //here we test where there are errors as part of inputs + [Test] + public void TestErrors() + { + ValueEval[] values = { new NumberEval(1), ErrorEval.NAME_INVALID, new NumberEval(3), ErrorEval.DIV_ZERO, }; + ValueEval percentile = new NumberEval(0.95); + ConfirmPercentile(percentile, values, ErrorEval.NAME_INVALID); + } + + //here we test where there are errors as part of inputs + [Test] + public void TestErrors2() + { + ValueEval[] values = { new NumberEval(1), new NumberEval(2), new NumberEval(3), ErrorEval.DIV_ZERO, }; + ValueEval percentile = new NumberEval(0.95); + ConfirmPercentile(percentile, values, ErrorEval.DIV_ZERO); + } + } + +} \ No newline at end of file diff --git a/testcases/main/testcases vs10.csproj b/testcases/main/testcases vs10.csproj index 7ce59ef8e..e33421a93 100644 --- a/testcases/main/testcases vs10.csproj +++ b/testcases/main/testcases vs10.csproj @@ -168,12 +168,16 @@ + + + +