/* SVN FILE: $Id: calulators.js 220 2007-12-21 02:11:34Z ryan.blunden $ */
/**
 * Calculating functions, primary for compounding interest calculations
 *
 * Copyright 2008, Freeman Fox Ltd
 *
 * Licensed under The Creative Commons License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright		Copyright 2007, FreemanFox Ltd
 * @version			$Revision: 220 $
 * @modifiedby		$LastChangedBy: ryan.blunden $
 * @lastmodified	$Date: 2007-12-21 12:11:34 +1000 (Fri, 21 Dec 2007) $
 * @license			http://creativecommons.org/licenses/by/3.0/ The Creative Commons License
 */

var Calculator = 
{
	compound: function(args)
	{
		p = args.current_principle;
		c = args.monthly_addition * 12;
		n = args.compound_times_yearly;
		r = args.interest_rate/100;
		y = args.years;
		
		return this.basicInvestment(p, r/n, y*n, c/n);
	},
	
	makeNumeric: function(s)
	{
		return this.filterChars(s, '1234567890.-');
	},
	
	filterChars: function(s, charList)
	{
		var s1 = "" + s; // force s1 to be a string data type
		var i;
		for (i = 0; i < s1.length; )
		{
			if (charList.indexOf(s1.charAt(i)) < 0)
				s1 = s1.substring(0,i) + s1.substring(i+1, s1.length);
			else
				i++;
		}
		return s1;
	},
	
	numval: function(val, digits, minval, maxval)
	{
		val = this.makeNumeric(val);
		if (val == '' || isNaN(val)) 
		{
				val = 0;
		}
		val = parseFloat(val);
		if (digits != null)
		{
			var dec = Math.pow(10,digits);
			val = (Math.round(val * dec))/dec;
		}
		if (minval != null && val < minval) 
		{
			val = minval;
		}
		if (maxval != null && val > maxval) 
		{
			val = maxval;
		}
		return parseFloat(val);
	},

	formatNumber: function(val, digits, minval, maxval)
	{
		var sval = '' + this.numval(val, digits, minval, maxval);
		var i;
		var iDecpt = sval.indexOf(".");
		if (iDecpt < 0) 
		{
			iDecpt = sval.length;
		}
		if (digits != null && digits > 0)
		{
			if (iDecpt == sval.length)
			{
				sval = sval + '.';
			}
			var places = sval.length - sval.indexOf(".") - 1;
			for (i = 0; i < digits - places; i++)
			{
				sval = sval + "0";
			}
		}
		var firstNumchar = 0;
		if (sval.charAt(0) == "-") 
		{
			firstNumchar = 1;
		}
		for (i = iDecpt - 3; i > firstNumchar; i-= 3)
		{
			sval = sval.substring(0, i) + "," + sval.substring(i);
		}	
		return sval;
	},
	
	presentValue: function(fv, r, y)
	{
		return fv/Math.pow(1+r, y);
	},
	
	futureValue: function(p, r, y)
	{
		return p*Math.pow(1+r, y);
	},
	
	returnRate: function(pv, fv, y)
	{
		return Math.pow(fv/pv,1.0/y) - 1.0;
	},
	
	geomSeries: function(z, m, n)
	{
		var amt;
		if (z == 1.0) amt = n + 1;
		else amt = (Math.pow(z,n + 1) - 1)/(z - 1);
		if (m >= 1) amt -= this.geomSeries(z,0,m-1);
		return amt;
	},
	
	basicInvestment: function(p, r, y, c)
	{
		if (c === null) 
		{
			c = 0;
		}
		
		// Ryno Edit - Round to 2 decimal places so it matches Naomi's Wealth Engineer figures
		c = Math.round(c*100)/100;
		
		// Ryno Edit - Minus y by 1 (compounding monthly) because in first month, there is no contribution
		return this.futureValue(p, r, y) + c*this.geomSeries(1+r, 1, y-1);
	},
	
	annuityPayout: function(p, r, y)
	{
		return this.futureValue(p,r,y-1)/this.geomSeries(1+r,0,y-1);
	},
	
	mortgagePayment: function(p, r, y)
	{
		return this.futureValue(p, r, y)/this.geomSeries(1+r, 0, y-1);
	},
	
	randN: function(m, s)
	{
		return s*Math.sqrt(-2*Math.log(Math.random()))*Math.cos(2*Math.PI*Math.random()) + m;
	},
	
	logNmean: function(m, s)
	{
		return Math.log(m) - (Math.pow(this.logNsigma(m, s), 2)/2);
	},
	
	logNsigma: function(m, s)
	{
		return Math.sqrt(Math.log(Math.pow(s/m,2) + 1));
	},
	
	gmEst: function(r_am,s)
	{
		return Math.sqrt(Math.pow(1 + r_am, 2) - Math.pow(s,2)) - 1;
	},
	
	numOrder: function(n, m)
	{
		return n - m;
	}
};