All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.xfa.formcalc.BuiltinFinancial Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright 2007 Adobe Systems Incorporated All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property of
 * Adobe Systems Incorporated and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Adobe Systems
 * Incorporated and its suppliers and may be covered by U.S. and Foreign
 * Patents, patents in process, and are protected by trade secret or copyright
 * law. Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained from
 * Adobe Systems Incorporated.
 */
package com.adobe.xfa.formcalc;


/**
 * This class defines static methods to implement
 * the FormCalc financial calculations.
 *
 *  F I N A N C I A L   F U N C T I O N S
 *      apr, cterm, fv, ipmt, npv, pmt, ppmt, pv, rate, term.
 *
 * @author Mike P. Tardif
 *
 * @exclude from published api.
 */
final class BuiltinFinancial {

	/*
	 *  Disallow instances of this class.
	 */
	private BuiltinFinancial() {
	}

	/*
	 *  Apr
	 *      This function returns the annual percentage rate for a loan.
	 */
	static void Apr(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 3);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the args to a doubles.
			//
			double nPrincipal = oParser.getNumeric(oArgSym[0]);
			double nPayment = oParser.getNumeric(oArgSym[1]);
			int nPeriods = (int) oParser.getNumeric(oArgSym[2]);
			//
			// range check the args.
			//
			if (nPrincipal <= 0.  || nPayment <= 0.  || nPeriods  <= 0) {
				throw new CalcException();
			}
			//
			// compute the apr.
			//
			int maxIterations = 500;
			final double eps = 0.005;
			final double delta = 0.0000001;
			double nInterest = 0.05;
			double nPmtZero = nPrincipal / nPeriods;
			double nPmtCur = LoanPmt(nPrincipal, nInterest, nPeriods);
			int i = 1;
			do {
				if (Math.abs(nPmtCur - nPmtZero) < delta)
					 break;
				nInterest *= (nPayment - nPmtZero) / (nPmtCur - nPmtZero);
				nPmtCur = LoanPmt(nPrincipal, nInterest, nPeriods);
			} while (! (++i > maxIterations || Math.abs(nPayment - nPmtCur) < eps));
			double nRate = (Math.abs(nPmtCur - nPmtZero) < delta) ? 0.  : 12 * nInterest;
			oRetSym = new CalcSymbol(nRate);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Cterm
	 *      This function returns the number of periods needed for an
	 *      investment earning a fixed, but compounded interest rate to
	 *		grow to a future value.
	 */
	static void Cterm(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			///
			Builtins.minArgs(nArgs, 3);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the args to a doubles.
			//
			double nInterest = oParser.getNumeric(oArgSym[0]);
			double nFuture = oParser.getNumeric(oArgSym[1]);
			double nPresent = oParser.getNumeric(oArgSym[2]);
			//
			// range check the args.
			//
			if (nInterest <= 0. || nFuture <= 0. || nPresent < 0.) {
				throw new CalcException();
			}
			//
			// compute the number of periods.
			//
			double nPeriods = Math.log(nFuture / nPresent)
												/ Math.log(1 + nInterest);
			oRetSym = new CalcSymbol(nPeriods);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Fv
	 *      This function returns the future value of a series of equal payments
	 *      at a fixed interest rate.
	 */
	static void Fv(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 3);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the args to a doubles.
			//
			double nPayment = oParser.getNumeric(oArgSym[0]);
			double nInterest = oParser.getNumeric(oArgSym[1]);
			int nPeriods = (int) oParser.getNumeric(oArgSym[2]);
			//
			// range check the args.
			//
			if (nPeriods <= 0 || nPayment <= 0. || nInterest < 0.) {
				throw new CalcException();
			}
			//
			// compute the future value.
			//
			double nVal;
			if (nInterest == 0.) {
				nVal = nPayment * nPeriods;
			}
			//
			// This is the formula which gives the old Filler 4.x answer (?)
			// It assumes payment at the end of each period.
			//
			else {
				nVal = nPayment * (1 + nInterest)
					* (IntRate(nInterest, nPeriods - 1) - 1)
						/ nInterest + nPayment;
			}
			//
			// Apparently the correct MS Excel formula is (yeah, sure Bill):
			//          nPayment * (1 + nInterest)
			//      * (IntRate(nInterest, nPeriods) - 1) / nInterest;
			//
			oRetSym = new CalcSymbol(nVal);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Ipmt
	 *      This function returns the amount of interest paid on a loan over a
	 *      period of time.
	 */
	static void Ipmt(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 5);
			Builtins.maxArgs(nArgs, 5);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the args to a doubles.
			//
			double nPrincipal = oParser.getNumeric(oArgSym[0]);
			double nInterest = oParser.getNumeric(oArgSym[1]);
			double nPayment = oParser.getNumeric(oArgSym[2]);
			int nStart = (int) oParser.getNumeric(oArgSym[3]);
			int nMonths = (int) oParser.getNumeric(oArgSym[4]);
			//
			// range check the args.
			//
			if (nPrincipal <= 0. || nInterest <= 0. || nPayment <= 0.)
				throw new CalcException();
			if (nStart < 1 || nMonths < 1)
				throw new CalcException();
			//
			// compute the interest paid.
			//
			nInterest /= 12;
			if (nPayment <= nPrincipal * nInterest) {
				oRetSym = new CalcSymbol(0.);
			}
			else if (nMonths + nStart - 1
							> LoanTerm(nPrincipal, nInterest, nPayment)) {
				oRetSym = new CalcSymbol(0.);
			}
			else {
				double nPrincipalRemaining = nPrincipal;
				double nPrincipalPaidInPeriod = 0.;
				double nInterestPaidInPeriod = 0.;
				for (int i = 1; i < nStart; i++) {
					nInterestPaidInPeriod  = nPrincipalRemaining * nInterest;
					nPrincipalPaidInPeriod = nPayment - nInterestPaidInPeriod;
					nPrincipalRemaining   -= nPrincipalPaidInPeriod;
					if (nPrincipalRemaining <= 0.)
						break;
				}
				double nInterestPaid = 0.;
				for (int i = nStart; i < nStart + nMonths; i++) {
					nInterestPaidInPeriod  = nPrincipalRemaining * nInterest;
					nPrincipalPaidInPeriod = nPayment - nInterestPaidInPeriod;
					nPrincipalRemaining   -= nPrincipalPaidInPeriod;
					nInterestPaid += nInterestPaidInPeriod;
					if (nPrincipalRemaining <= 0.)
						break;
				}
				oRetSym = new CalcSymbol(nInterestPaid);
			}
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Npv
	 *      This function returns the net present value of an investment based
	 *      on a future series of periodic cash flows and a discount rate.
	 */
	static void Npv(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 1);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the arg to a double.
			//
			double nDiscountRate = oParser.getNumeric(oArgSym[0]);
			//
			// range check the args.
			//
			if (nDiscountRate <= 0.) {
				throw new CalcException();
			}
			//
			// compute the net present value.
			//
			double nVal = 0.;
			double nDenom = 1.;
			for (int i = 1; i < nArgs; i++) {
				nDenom *= (1 + nDiscountRate);
				nVal += oParser.getNumeric(oArgSym[i]) / nDenom;
			}
			oRetSym = new CalcSymbol(nVal);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Pmt
	 *      This function returns the loan payment for a given principal,
	 *      interest, and term.
	 */
	static void Pmt(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 3);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the args to doubles.
			//
			double nPrincipal = oParser.getNumeric(oArgSym[0]);
			double nInterest = oParser.getNumeric(oArgSym[1]);
			int nPeriods = (int) oParser.getNumeric(oArgSym[2]);
			//
			// range check the args.
			//
			if (nPrincipal <= 0. || nInterest <= 0. || nPeriods <= 0) {
				throw new CalcException();
			}
			//
			// compute the payment.
			//
			double nPayment = LoanPmt(nPrincipal, nInterest, nPeriods);
			oRetSym = new CalcSymbol(nPayment);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Ppmt
	 *      This function returns the amount of principal paid on a loan
	 *      over a period of time.
	 */
	static void Ppmt(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 5);
			Builtins.maxArgs(nArgs, 5);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the args to a doubles.
			//
			double nPrincipal = oParser.getNumeric(oArgSym[0]);
			double nInterest = oParser.getNumeric(oArgSym[1]);
			double nPayment = oParser.getNumeric(oArgSym[2]);
			int nStart = (int) oParser.getNumeric(oArgSym[3]);
			int nMonths = (int) oParser.getNumeric(oArgSym[4]);
			//
			// range check the args.
			//
			if (nPrincipal <= 0. || nInterest <= 0. || nPayment <= 0.)
				throw new CalcException();
			if (nStart < 1 || nMonths < 1)
				throw new CalcException();
			//
			// compute the principle paid.
			//
			nInterest /= 12;
			if (nPayment <= nPrincipal * nInterest) {
				oRetSym = new CalcSymbol(0.);
			}
			else if (nMonths + nStart - 1
								> LoanTerm(nPrincipal, nInterest, nPayment)) {
				oRetSym = new CalcSymbol(0.);
			}
			else {
				double nPrincipalRemaining = nPrincipal;
				double nPrincipalPaidInPeriod = 0.;
				double nInterestPaidInPeriod = 0.;
				for (int i = 1; i < nStart; i++) {
					nInterestPaidInPeriod  = nPrincipalRemaining * nInterest;
					nPrincipalPaidInPeriod = nPayment - nInterestPaidInPeriod;
					nPrincipalRemaining   -= nPrincipalPaidInPeriod;
					if (nPrincipalRemaining <= 0.)
						break;
				}
				double nPrinciplePaid = 0.;
				for (int i = nStart; i < nStart + nMonths; i++) {
					nInterestPaidInPeriod  = nPrincipalRemaining * nInterest;
					nPrincipalPaidInPeriod = nPayment - nInterestPaidInPeriod;
					nPrincipalRemaining   -= nPrincipalPaidInPeriod;
					nPrinciplePaid += nPrincipalPaidInPeriod;
					if (nPrincipalRemaining <= 0.)
						break;
				}
				oRetSym = new CalcSymbol(nPrinciplePaid);
			}
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Pv
	 *      This function returns the present value of an investment
	 *      given an equal payment per period, a specific interest rate,
	 *      and a number of periods.
	 */
	static void Pv(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 3);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the arg to a double.
			//
			double nPayment = oParser.getNumeric(oArgSym[0]);
			double nInterest = oParser.getNumeric(oArgSym[1]);
			int nPeriods = (int) oParser.getNumeric(oArgSym[2]);
			//
			// range check the args.
			//
			if (nPeriods <= 0 || nPayment <= 0.) {
				throw new CalcException();
			}
			//
			// compute the present value.
			//
			double nVal;
			if (nInterest == 0.) {
				nVal = nPayment * nPeriods;
			}
			else {
				nVal = nPayment * (1 - 1 / IntRate(nInterest, nPeriods))
																/ nInterest;
			}
			oRetSym = new CalcSymbol(nVal);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Rate
	 *      This function returns the compound interest rate per period
	 *      required for an investment to grow from present to future value
	 *      in a specified period.
	 */
	static void Rate(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 3);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the args to a doubles.
			//
			double nFuture = oParser.getNumeric(oArgSym[0]);
			double nPresent = oParser.getNumeric(oArgSym[1]);
			double nPeriods = (int) oParser.getNumeric(oArgSym[2]);
			//
			// range check the args.
			//
			if (nFuture <= 0. || nPresent <= 0. || nPeriods <= 0) {
				throw new CalcException();
			}
			//
			// compute the rate.
			//
			double nRate
				= Math.exp(Math.log(nFuture / nPresent)  / nPeriods) - 1;
			oRetSym = new CalcSymbol(nRate);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Term
	 *      This function returns the number of periods needed for an
	 *      investment earning a fixed, but compounded interest rate
	 *		to grow to a future value.
	 */
	static void Term(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 3);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// promote the args to a doubles.
			//
			double nPayment = oParser.getNumeric(oArgSym[0]);
			double nInterest = oParser.getNumeric(oArgSym[1]);
			double nFuture = oParser.getNumeric(oArgSym[2]);
			//
			// range check the args.
			//
			if (nPayment <= 0. || nInterest <= 0. || nFuture <= 0.) {
				throw new CalcException();
			}
			//
			// compute the number of periods.
			//
			double nPeriods;
			if (nFuture <= nPayment) {
				nPeriods = 1;
			}
			else {
				//
				// Modified formula: assumes payment at the end of each period
				//
				nPeriods = Math.log((nFuture - nPayment) / nPayment * nInterest
								+ (1 + nInterest)) / Math.log(1 + nInterest);
			}
			oRetSym = new CalcSymbol(nPeriods);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Returns the compound interest rate from the formula: (1 + i) ** p
	 */
	private static double IntRate(double nInterest, int nPeriods) {
		double nRate = 1.;
		for (int i = 0; i < nPeriods; i++)
			nRate *= 1. + nInterest;
		return nRate;
	}


	/*
	 * Returns the term of the loan.
	 */
	private static int LoanTerm(double nPrincipal,
										double nInterest, double nPayment) {
		double nRemaining = nPrincipal;
		int nMonths = 0;
		while (nRemaining > 0.0) {
			nRemaining = nRemaining - nPayment + nRemaining * nInterest;
			nMonths++;
		}
		return nMonths;
	}


	/*
	 * Returns the loan payment for a given principal, interest, and term.
	 */
	private static double LoanPmt(double nPrincipal,
										double nInterest, int nPeriods) {
		return (nPrincipal / ((1 - 1 / IntRate(nInterest, nPeriods))
															/ nInterest));
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy