--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.ss.formula.functions;\r
+\r
+import org.apache.poi.ss.formula.eval.ErrorEval;\r
+import org.apache.poi.ss.formula.eval.EvaluationException;\r
+import org.apache.poi.ss.formula.eval.NumberEval;\r
+import org.apache.poi.ss.formula.eval.OperandResolver;\r
+import org.apache.poi.ss.formula.eval.ValueEval;\r
+\r
+/**\r
+ * Implements the Excel Rate function\r
+ */\r
+public class Rate implements Function {\r
+ public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {\r
+ if (args.length < 3) { //First 3 parameters are mandatory\r
+ return ErrorEval.VALUE_INVALID;\r
+ }\r
+\r
+ double periods, payment, present_val, future_val = 0, type = 0, estimate = 0.1, rate;\r
+\r
+ try {\r
+ ValueEval v1 = OperandResolver.getSingleValue(args[0], srcRowIndex, srcColumnIndex);\r
+ ValueEval v2 = OperandResolver.getSingleValue(args[1], srcRowIndex, srcColumnIndex);\r
+ ValueEval v3 = OperandResolver.getSingleValue(args[2], srcRowIndex, srcColumnIndex);\r
+ ValueEval v4 = null;\r
+ if (args.length >= 4)\r
+ v4 = OperandResolver.getSingleValue(args[3], srcRowIndex, srcColumnIndex);\r
+ ValueEval v5 = null;\r
+ if (args.length >= 5)\r
+ v5 = OperandResolver.getSingleValue(args[4], srcRowIndex, srcColumnIndex);\r
+ ValueEval v6 = null;\r
+ if (args.length >= 6)\r
+ v6 = OperandResolver.getSingleValue(args[5], srcRowIndex, srcColumnIndex);\r
+\r
+ periods = OperandResolver.coerceValueToDouble(v1); \r
+ payment = OperandResolver.coerceValueToDouble(v2);\r
+ present_val = OperandResolver.coerceValueToDouble(v3);\r
+ if (args.length >= 4)\r
+ future_val = OperandResolver.coerceValueToDouble(v4);\r
+ if (args.length >= 5)\r
+ type = OperandResolver.coerceValueToDouble(v5);\r
+ if (args.length >= 6)\r
+ estimate = OperandResolver.coerceValueToDouble(v6);\r
+ rate = calculateRate(periods, payment, present_val, future_val, type, estimate) ;\r
+\r
+ checkValue(rate);\r
+ } catch (EvaluationException e) {\r
+ e.printStackTrace() ;\r
+ return e.getErrorEval();\r
+ }\r
+\r
+ return new NumberEval( rate ) ;\r
+ }\r
+\r
+ private double calculateRate(double nper, double pmt, double pv, double fv, double type, double guess) {\r
+ //FROM MS http://office.microsoft.com/en-us/excel-help/rate-HP005209232.aspx\r
+ int FINANCIAL_MAX_ITERATIONS = 20;//Bet accuracy with 128\r
+ double FINANCIAL_PRECISION = 0.0000001;//1.0e-8\r
+\r
+ double y, y0, y1, x0, x1 = 0, f = 0, i = 0;\r
+ double rate = guess;\r
+ if (Math.abs(rate) < FINANCIAL_PRECISION) {\r
+ y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;\r
+ } else {\r
+ f = Math.exp(nper * Math.log(1 + rate));\r
+ y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;\r
+ }\r
+ y0 = pv + pmt * nper + fv;\r
+ y1 = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;\r
+\r
+ // find root by Newton secant method\r
+ i = x0 = 0.0;\r
+ x1 = rate;\r
+ while ((Math.abs(y0 - y1) > FINANCIAL_PRECISION) && (i < FINANCIAL_MAX_ITERATIONS)) {\r
+ rate = (y1 * x0 - y0 * x1) / (y1 - y0);\r
+ x0 = x1;\r
+ x1 = rate;\r
+\r
+ if (Math.abs(rate) < FINANCIAL_PRECISION) {\r
+ y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;\r
+ } else {\r
+ f = Math.exp(nper * Math.log(1 + rate));\r
+ y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;\r
+ }\r
+\r
+ y0 = y1;\r
+ y1 = y;\r
+ ++i;\r
+ }\r
+ return rate;\r
+ }\r
+\r
+ /**\r
+ * Excel does not support infinities and NaNs, rather, it gives a #NUM! error in these cases\r
+ * \r
+ * @throws EvaluationException (#NUM!) if <tt>result</tt> is <tt>NaN</> or <tt>Infinity</tt>\r
+ */\r
+ static final void checkValue(double result) throws EvaluationException {\r
+ if (Double.isNaN(result) || Double.isInfinite(result)) {\r
+ throw new EvaluationException(ErrorEval.NUM_ERROR);\r
+ }\r
+ }\r
+}\r