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
+ *
+ * - if array is empty or Contains more than 8,191 data points, PERCENTILE returns the #NUM! error value.
+ * - If k is nonnumeric, PERCENTILE returns the #VALUE! error value.
+ * - If k is < 0 or if k > 1, PERCENTILE returns the #NUM! error value.
+ * - If k is not a multiple of 1/(n - 1), PERCENTILE interpolates to determine the value at the k-th percentile.
+ *
+ */
+ 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.
+ *
+ * - Values must contain at least one positive value and one negative value to calculate the modified internal rate of return. Otherwise, MIRR returns the #DIV/0! error value.
+ * - If an array or reference argument Contains text, logical values, or empty cells, those values are ignored; however, cells with the value zero are included.
+ *
+ *
+ * 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 @@
+
+
+
+