function annuity_payment(principal, annual_rate, periods_per_year, num_years) {
   var total_payments = periods_per_year * num_years;
   var periodic_rate = annual_rate / periods_per_year;
   var periodic_payment = principal * (periodic_rate + (periodic_rate / (Math.pow(1 + periodic_rate, total_payments) - 1)));

   return periodic_payment;
}

function annuity_present_value(payment, annual_rate, periods_per_year, num_years) {
   var total_payments = periods_per_year * num_years;
   var periodic_rate = annual_rate / periods_per_year;
   var present_value = payment * ((1 - (1 / Math.pow(1 + periodic_rate, total_payments))) / periodic_rate);

   return present_value;
}

function nominal_to_effective(nominal_rate, periods_per_year) {
	var effective_rate = Math.pow(1 + nominal_rate / periods_per_year, periods_per_year) - 1;
	
	return effective_rate;
}

function effective_to_nominal(effective_rate, periods_per_year) {
	var nominal_rate = periods_per_year * (Math.pow(effective_rate + 1, 1 / periods_per_year) - 1);
	
	return nominal_rate;
}

function can_mortgage_payment(principal, annual_rate, num_years, periods_per_year, divisor) {
	var effective_rate = nominal_to_effective(annual_rate, 2);
	var nominal_rate = effective_to_nominal(effective_rate, periods_per_year);
	
	var payment = annuity_payment(principal, nominal_rate, periods_per_year, num_years) / divisor;
	
	return payment;
}

function can_mortgage_payment_purchase(home_price, downpayment, annual_rate, num_years, periods_per_year, divisor) {
	var downpayment_percent = null;
	if (downpayment >= 1) {
		downpayment_percent = downpayment / home_price;
		if (downpayment_percent < 0.05) {
			return null;
		}
	} else if (downpayment > 0) {
		downpayment_percent = downpayment;
		downpayment = home_price * downpayment_percent;
	} else {
		return null;
	}

	var loan_to_value = 1 - downpayment_percent;
	var principal = home_price - downpayment;
	principal = principal + principal * (cmhc_premium(loan_to_value) + cmhc_surcharge(loan_to_value, num_years));
	
	var payment = can_mortgage_payment(principal, annual_rate, num_years, periods_per_year, divisor);
	return payment;
}

function can_mortgage_principal(payment, annual_rate, num_years, periods_per_year, divisor) {
	var effective_rate = nominal_to_effective(annual_rate, 2);
	var nominal_rate = effective_to_nominal(effective_rate, periods_per_year);

	var principal = annuity_present_value(payment * divisor, nominal_rate, periods_per_year, num_years);

	return principal;
}

function cmhc_premium(loan_to_value)
{
	if (loan_to_value < 0.0 || loan_to_value > 0.95)
		return -1;
	
	if (loan_to_value <= 0.80)
		return 0.0;
	else if (loan_to_value <= 0.85)
		return 0.0175;
	else if (loan_to_value <= 0.90)
		return 0.02;
	else if (loan_to_value <= 0.95)
		return 0.0275;
}

function cmhc_surcharge(loan_to_value, num_years) {

	if (loan_to_value < 0.0 || loan_to_value > 1.0 || num_years < 1)
		return -1;

	if (loan_to_value <= 0.80) {
		return 0.0;
	}
	else {
		if (num_years > 30)
			return 0.004;
		else if (num_years > 25)
			return 0.002;
		else
			return 0.0;
	}
}

function cmhc_pst(cmhc_amt, province) {
	var tax_rates = { 'ON': 0.085, 'QC': 0.08 };
	
	if (typeof(tax_rates[province]) == 'undefined') {
		return 0;
	}
	
	return cmhc_amt * tax_rates[province];
}

function can_amortization_table(principal, annual_rate, num_years, periods_per_year, divisor) {
	if (isNaN(principal) || annual_rate == null || isNaN(num_years) || periods_per_year == null || divisor == null)
		return;
	
	var periodic_payment = can_mortgage_payment(principal, annual_rate, num_years, periods_per_year, divisor);
	// accelerated
	if (divisor == 2 && periods_per_year == 12) {
		periods_per_year = 26;
	}
	
	var effective_rate = nominal_to_effective(annual_rate, 2);
	var nominal_rate = effective_to_nominal(effective_rate, periods_per_year);
	var periodic_rate = nominal_rate / periods_per_year;

	var total_periods = num_years * periods_per_year;
	
	var table = [];
	
	var balance = principal;
	var annual_payment = 0;
	var annual_interest = 0;
	var annual_principal = 0;
	
	var done = false;
	for (var i = 1; i <= total_periods && !done; ++i) {
		var interest = balance * periodic_rate;
		var principal = periodic_payment - interest;
		var payment = periodic_payment;
		if (principal > balance) {
			payment -= (principal - balance);
			principal = balance;
			done = true;
		}
		
		balance -= principal;
		annual_payment += payment;
		annual_interest += interest;
		annual_principal += principal;
				
		if (i % periods_per_year == 0 || done) {
			
			if (i == total_periods && balance < 1)
				balance = 0;
			
			table.push({ 'payment': annual_payment, 'principal': annual_principal, 'interest': annual_interest, 'balance': balance });
			
			annual_payment = 0;
			annual_interest = 0;
			annual_principal = 0;
		}
	}
	
	return table;
}

function can_mortgage_affordability(annual_rate, num_years, downpayment, annual_income, monthly_living, monthly_finance) {
	var monthly_income = annual_income / 12;
	
	var gds = 0.32 * monthly_income - monthly_living;
	var tds = 0.40 * monthly_income - (monthly_living + monthly_finance);
	var max_payment = Math.min(gds, tds);
	
	var principal = can_mortgage_principal(max_payment, annual_rate, num_years, 12, 1);
	
	var downpayment_percent = null;
	if (downpayment >= 1) {
		downpayment_percent = downpayment / (principal + downpayment);
		
		if (num_years == 35) {
			downpayment_percent = 0.2;
			principal = downpayment * 5;
			max_payment = can_mortgage_payment(principal, annual_rate, num_years, 12, 1);
			
		} else if (downpayment_percent < 0.05) {
			downpayment_percent = 0.05;
			principal = downpayment * 20;
			max_payment = can_mortgage_payment(principal, annual_rate, num_years, 12, 1);
		}

	} else if (downpayment > 0) {
		downpayment_percent = downpayment;
		downpayment = (principal * downpayment_percent) / (1 - downpayment_percent);
	} else {
		return null;
	}
	
	var cmhc_percent = cmhc_premium(1 - downpayment_percent) + cmhc_surcharge(1 - downpayment_percent, num_years);
	var cmhc = principal / (1 + cmhc_percent) * cmhc_percent;
	var adjusted = principal + cmhc;
	var home_value = principal + downpayment;
		
	return { 
		'payment': max_payment, 'home_price': home_value,
		'downpayment': [ downpayment_percent, downpayment ], 'mortgage': principal, 
		'cmhc': cmhc, 'adjusted': adjusted 
	};
}

function provincial_ltt(province, principal) {
	if (province == "BC")
		return bc_ltt(principal);

	else if (province == "AB")
		return ab_ltt(principal);

	else if (province == "SK")
		return sk_ltt(principal);

	else if (province == "MB")
		return mb_ltt(principal);

	else if (province == "ON")
		return on_ltt(principal);

	else if (province == "QC")
		return qc_ltt(principal);

	else if (province == "NB")
		return nb_ltt(principal);

	else if (province == "NS")
		return ns_ltt(principal);

	else if (province == "PE")
		return pe_ltt(principal);

	else if (province == "NL")
		return nl_ltt(principal);

	else if (province == "YK")
		return yt_ltt(principal);

	else if (province == "NT")
		return nt_ltt(principal);

	else
		return 0;
}

function municipal_ltt(toronto, principal) {
	if (toronto)
		return to_ltt(principal);
	else
		return 0;
}

function ltt_rebate(province, toronto, principal) {
	var rebate = 0;
	if (province == "ON") {
		rebate += on_rebate(principal);
		if (toronto)
			rebate += to_rebate(principal);
	} else if (province == "BC") {
		rebate += bc_rebate(principal);
	} else if (province == "PE") {
		rebate += pe_rebate(principal);
	}
	
	return rebate;
}

function ab_ltt(principal) {
	return (principal * 0.0002) + 35;
}

function sk_ltt(principal) {
	if (principal > 8401)
		return principal * .003;

	else if (principal >500)
		return 25;

	else
		return 0;
}

function bc_ltt(principal) {
	if (principal > 200000)
		return (principal - 200000) * 0.02 + 2000;

	else 
		return principal * 0.01;
}

function mb_ltt(principal) {	

	if (principal > 200000)
		return (principal - 200000) * .02 + 1650;	

	else if (principal > 150000)
		return (principal - 150000) * 0.015 + 900;

	else if (principal > 90000)
		return (principal - 90000) * 0.01 + 300;

	else if (principal > 30000)
		return (principal - 30000) * 0.005; 

	else
		return 0;
}

function on_ltt(principal) {
	if (principal > 400000)
		return (principal - 400000) * 0.02 + 4475;

	else if (principal > 250000)
		return (principal - 250000) * 0.015 + 2225;
	
	else if (principal > 55000)
		return (principal - 55000) * 0.01 + 275;
	
	else 
		return principal * 0.005;
}

function to_ltt(principal) {
	if (principal > 400000)
		return (principal - 400000) * 0.02 + 3725;

	else if (principal > 55000)
		return (principal - 55000) * 0.01 + 275;

	else 
		return principal * 0.005;
}


function qc_ltt(principal) {
	if (principal > 250000)
		return (principal - 250000) * 0.015 + 2250;

	else if (principal > 50000)
		return (principal - 50000) * 0.01 + 250;

	else
		return principal * 0.005;
}

function nb_ltt(principal){
	return principal * 0.0025;
}

function pe_ltt(principal) {
	
	if (principal > 30000)
		return principal * 0.01;

	else
		return 0;
}
	
function nl_ltt(principal) {
	
	if (principal > 500)
		return (principal * .004) + 200;

	else
		return 200;
}

function ns_ltt(principal) {
	return principal * 0.015;
}

function yt_ltt(principal) {
	if (principal > 25000)
		return (principal - 25000) * 0.00025 + 42.75;

	else if (principal > 10000)
		return (principal - 10000) * 0.00075 + 31.5;

	else if (principal > 5000)
		return (principal - 5000) * 0.002 + 24;

	else if (principal > 3000)
		return 24;

	else if (principal > 1000)
		return 13.5;

	else
		return 6;
}


function nt_ltt(principal) {
	if (principal > 1000000)
		return (principal - 1000000) * 0.001 + 1500;
	else
		return principal * 0.0015;	
}

function on_rebate(principal, province) {
	if (on_ltt(principal) > 2000)
		return 2000;

	else 
		return on_ltt(principal);
}

function to_rebate(principal) {
	if (to_ltt(principal) > 3725)
		return 3725;

	else
		return to_ltt(principal);
}

function bc_rebate(principal) {
	if (principal < 425000)
		return bc_ltt(principal);

	else if (principal <= 450000)
		return (450000 - principal)/25000 * bc_ltt(principal);

	else
		return 0;
}

function pe_rebate(principal) {
	if (principal <= 200000)
		return pe_ltt(principal);

	else
		return 0;
}

function prop_tax(province, city) {

	if (province == "BC")
		return bc_prop_tax(city);

	else if (province == "AB")
		return ab_prop_tax(city);

	else if (province == "SK")
		return sk_prop_tax(city);

	else if (province == "MB")
		return mb_prop_tax(city);

	else if (province == "ON")
		return on_prop_tax(city);

	else if (province == "QC")
		return qc_prop_tax(city);

	else if (province == "NB")
		return nb_prop_tax(city);

	else if (province == "NS")
		return ns_prop_tax(city);

	else if (province == "PE")
		return pe_prop_tax(city);

	else if (province == "NL")
		return nl_prop_tax(city);

	else if (province == "YK")
		return yk_prop_tax(city);

	else if (province == "NT")
		return nt_prop_tax(city);

	else
		return 0;
}

function bc_prop_tax(city) {

	if (city == "Vancouver")
		return 0.0042;

	else
		return 0.0054;
}

function ab_prop_tax(city) {

	if (city == "Calgary")
		return 0.0059;

	else
		return 0.0059;
}

function sk_prop_tax(city) {
	
	if (city == "Regina")
		return 0.0164;

	else 
		return 0.0164;
}

function mb_prop_tax(city) {

	if (city == "Winnipeg")
		return 0.0143;
	
	else
		return 0.0143;
} 

function on_prop_tax(city) {

	if (city == "Ajax")
		return 0.01381;

	else if (city == "Aurora")
		return 0.0108;

	else if (city == "Barrie")
		return 0.0134;

	else if (city == "Brampton")
		return 0.0048;

	else if (city == "Toronto")
		return 0.0083;

	else
		return 0.0122;
}

function qc_prop_tax(city) {

	if (city == "Montreal")
		return 0.0095;

	else
		return 0.0095;
}

function nb_prop_tax(city) {

	if (city == "Fredericton")
		return 0.0144;

	else
		return 0.0144;
}


function pe_prop_tax(city) {
	
	if (city == "Charlottetown")
		return 0.01670;

	else
		return 0.01670;
}

function ns_prop_tax(city) {

	if (city == "Halifax")
		return 0.0127;

	else
		return 0.0127;
}

function nl_prop_tax(city) {

	if (city == "St. John`s")
		return 0.0101;

	else 
		return 0.0101;
}

function yk_prop_tax(city) {

	return 0.0101;
}

function nt_prop_tax(city) {

	return 0.0101;
}

function nu_prop_tax(city) {

	return 0.0101;
}


function income_tax(province, income) {
	return provincial_income_tax(province, income) + federal_income_tax(income);
}

function provincial_income_tax(province, income) {

	if (province == "BC")
		return bc_income_tax(income);

	else if (province == "AB")
		return ab_income_tax(income);

	else if (province == "SK")
		return sk_income_tax(income);

	else if (province == "MB")
		return mb_income_tax(income);

	else if (province == "ON")
		return on_income_tax(income);

	else if (province == "QC")
		return qc_income_tax(income);

	else if (province == "NB")
		return nb_income_tax(income);

	else if (province == "NS")
		return ns_income_tax(income);

	else if (province == "PE")
		return pe_income_tax(income);

	else if (province == "NL")
		return nl_income_tax(income);

	else if (province == "YK")
		return yk_income_tax(income);

	else if (province == "NT")
		return nt_income_tax(income);

	else
		return 0;
}

function federal_income_tax(income) {
	if (income > 128800)
		return (income - 128800) * .29 + 27256.40;

	else if (income > 83088)
		return (income - 83088) * .26 + 15371.28;

	else if (income > 41544)
		return (income - 41544) * .22 + 6231.60;
	
	else
		return income * .15;
}


function nf_income_tax(income) {
	
	if (income > 63807)
		return (income - 63807) * .133 + 6444.49; 

	else if (income > 31905)
		return (income - 31905) * .125 + 2456.61;

	else
		return income * .077;
}

function pe_income_tax(income) {

	if (income > 63969)
		return (income - 63969) * .167 + 7548.36;

	else if (income > 31984)
		return (income - 31984) * .138 + 3134.43;
	
	else
		return income * .098;
}

function ns_income_tax(income) {
	
	if (income > 150000)
		return (income - 150000) * .21 + 22637;

	else if (income > 93000)
		return (income - 93000) * .175 + 12662;

	else if (income > 59180)
		return (income - 59180) * .1667 + 7025;

	else if (income > 29590)
		return (income - 29590) * .1495 + 2601;

	else 
		return income * .0879;
}

function nb_income_tax(income) {

	if (income > 120067)
		return (income - 120067) * .127 + 13553.13;

	else if (income > 73751)
		return (income - 73751) * .124 + 7787.62;

	else if (income > 37149)
		return (income - 37149) * .121 + 3380.56;

	else
		return income * .091;
}

function on_income_tax(income) { 

	if (income > 75550)
		return (income - 75550) * .1116 + 5364.09;

	else if (income > 37774)
		return (income - 37774) * .0915 + 1907.59;

	else
		return income * .0505;
}

function mb_income_tax(income) {

	if (income > 67000)
		return (income - 67000) * .174 + 7938;

	else if (income > 31000)
		return (income - 31000) * .1275 + 3348;

	else
		return income * .1080;
}

function sk_income_tax(income) {

	if (income > 116911)
		return (income - 116911) * .15 + 14380.05;

	else if ( income > 40919)
		return (income - 40919) * .13 + 4501.09;

	else
		return income * .11;
}

function ab_income_tax(income){

	return income * .1;
}

function bc_income_tax(income){

	if (income > 10787)
		return (income - 100787) * .147 + 7922.55;

	else if (income > 83001)
		return (income - 83001) * .1229 + 5376.65;

	else if (income > 72293)
		return (income - 72293) * .1050 + 4612.31;

	else if (income > 36146)
		return (income - 36146) * .077 + 1828.99;

	else
		return income * .0506;
}

function yt_income_tax(income) {

	if (income > 128800)
		return (income - 128800) * .1276 + 12175.61;

	else if (income > 83088)
		return (income - 83088) * .1144 + 6946.16;

	else if (income > 41544)
		return (income - 41544) * .0968 + 2924.70;

	else
		return income * .0704;
}

function nt_income_tax(income) {

	if (income > 122345)
		return (income - 122345) * .1405 + 11201.09;

	else if (income > 75253)
		return (income - 75253) * .122	+ 5455.86;

	else if (income > 37626)
		return (income - 37626) * .086 + 2219.93;

	else
		return income * .059;
}

function nu_income_tax(income) {

	if (income > 128800)
		return (income - 128800) * .115 + 88919.16;

	else if (income > 79224)
		return (income - 79224) * .09 + 4357.32;

	else if (income > 39612)
		return (income - 39612) * .07 + 1584.48;

	else
		return income * .04;
}

var utilities_estimate_table = {
		"AB": [190, 215, 257, 289, 263, 315, 356],
		"BC": [131, 155, 202, 178, 191, 224, 259],
		"SK": [158, 223, 239, 277, 275, 290, 341],
		"MB": [158, 223, 239, 277, 275, 290, 341],
		"NB": [245, 285, 319, 347, 333, 316, 339],
		"NF": [245, 285, 319, 347, 333, 316, 339],
		"NS": [245, 285, 319, 347, 333, 316, 339],
		"PE": [245, 285, 319, 347, 333, 316, 339],
		"ON": [203, 276, 294, 311, 308, 390, 425],
		"QC": [94, 135, 168, 174, 187, 210, 235]
	};

function utilities_estimate(income, province) {
	if (income == null || province == null)
		return;
	
	var bracket = null;
	if (income > 150000)
		bracket = 6;
	else if (income > 100000)
		bracket = 5;
	else if (income > 80000)
		bracket = 4;
	else if (income > 60000)
		bracket = 3;
	else if (income > 40000)
		bracket = 2;
	else if (income > 20000)
		bracket = 1;
	else 
		bracket = 0;
	
	return utilities_estimate_table[province][bracket];
}


//Number formatting functions
Number.formatFunctions = {count:0};

//Constants useful for controlling the format of numbers in special cases.
Number.prototype.NaN         = 'NaN';
Number.prototype.posInfinity = 'Infinity';
Number.prototype.negInfinity = '-Infinity';

Number.prototype.numberFormat = function(format, context) {
 if (isNaN(this) ) {
     return Number.prototype.NaNstring;
 }
 else if (this == +Infinity ) {
     return Number.prototype.posInfinity;
 }
 else if ( this == -Infinity) {
     return Number.prototype.negInfinity;
 }
 else if (Number.formatFunctions[format] == null) {
     Number.createNewFormat(format);
 }
 return this[Number.formatFunctions[format]](context);
}

Number.createNewFormat = function(format) {
 var funcName = "format" + Number.formatFunctions.count++;
 Number.formatFunctions[format] = funcName;
 var code = "Number.prototype." + funcName + " = function(context){\n";

 // Decide whether the function is a terminal or a pos/neg/zero function
 var formats = format.split(";");
 switch (formats.length) {
     case 1:
         code += Number.createTerminalFormat(format);
         break;
     case 2:
         code += "return (this < 0) ? this.numberFormat(\""
             + String.escape(formats[1])
             + "\", 1) : this.numberFormat(\""
             + String.escape(formats[0])
             + "\", 2);";
         break;
     case 3:
         code += "return (this < 0) ? this.numberFormat(\""
             + String.escape(formats[1])
             + "\", 1) : ((this == 0) ? this.numberFormat(\""
             + String.escape(formats[2])
             + "\", 2) : this.numberFormat(\""
             + String.escape(formats[0])
             + "\", 3));";
         break;
     default:
         code += "throw 'Too many semicolons in format string';";
         break;
 }
 eval(code + "}");
}

Number.createTerminalFormat = function(format) {
 // If there is no work to do, just return the literal value
 if (format.length > 0 && format.search(/[0#?]/) == -1) {
     return "return '" + String.escape(format) + "';\n";
 }
 // Negative values are always displayed without a minus sign when section separators are used.
 var code = "var val = (context == null) ? new Number(this) : Math.abs(this);\n";
 var thousands = false;
 var lodp = format;
 var rodp = "";
 var ldigits = 0;
 var rdigits = 0;
 var scidigits = 0;
 var scishowsign = false;
 var sciletter = "";
 // Look for (and remove) scientific notation instructions, which can be anywhere
 m = format.match(/\..*(e)([+-]?)(0+)/i);
 if (m) {
     sciletter = m[1];
     scishowsign = (m[2] == "+");
     scidigits = m[3].length;
     format = format.replace(/(e)([+-]?)(0+)/i, "");
 }
 // Split around the decimal point
 var m = format.match(/^([^.]*)\.(.*)$/);
 if (m) {
     lodp = m[1].replace(/\./g, "");
     rodp = m[2].replace(/\./g, "");
 }
 // Look for %
 if (format.indexOf('%') >= 0) {
     code += "val *= 100;\n";
 }
 // Look for comma-scaling to the left of the decimal point
 m = lodp.match(/(,+)(?:$|[^0#?,])/);
 if (m) {
     code += "val /= " + Math.pow(1000, m[1].length) + "\n;";
 }
 // Look for comma-separators
 if (lodp.search(/[0#?],[0#?]/) >= 0) {
     thousands = true;
 }
 // Nuke any extraneous commas
 if ((m) || thousands) {
     lodp = lodp.replace(/,/g, "");
 }
 // Figure out how many digits to the l/r of the decimal place
 m = lodp.match(/0[0#?]*/);
 if (m) {
     ldigits = m[0].length;
 }
 m = rodp.match(/[0#?]*/);
 if (m) {
     rdigits = m[0].length;
 }
 // Scientific notation takes precedence over rounding etc
 if (scidigits > 0) {
     code += "var sci = Number.toScientific(val,"
         + ldigits + ", " + rdigits + ", " + scidigits + ", " + scishowsign + ");\n"
         + "var arr = [sci.l, sci.r];\n";
 }
 else {
     // If there is no decimal point, round to nearest integer, AWAY from zero
     if (format.indexOf('.') < 0) {
         code += "val = (val > 0) ? Math.ceil(val) : Math.floor(val);\n";
     }
     // Numbers are rounded to the correct number of digits to the right of the decimal
     code += "var arr = val.round(" + rdigits + ").toFixed(" + rdigits + ").split('.');\n";
     // There are at least "ldigits" digits to the left of the decimal, so add zeros if needed.
     code += "arr[0] = (val < 0 ? '-' : '') + String.leftPad((val < 0 ? arr[0].substring(1) : arr[0]), "
         + ldigits + ", '0');\n";
 }
 // Add thousands separators
 if (thousands) {
     code += "arr[0] = Number.addSeparators(arr[0]);\n";
 }
 // Insert the digits into the formatting string.  On the LHS, extra digits are copied
 // into the result.  On the RHS, rounding has chopped them off.
 code += "arr[0] = Number.injectIntoFormat(arr[0].reverse(), '"
     + String.escape(lodp.reverse()) + "', true).reverse();\n";
 if (rdigits > 0) {
     code += "arr[1] = Number.injectIntoFormat(arr[1], '" + String.escape(rodp) + "', false);\n";
 }
 if (scidigits > 0) {
     code += "arr[1] = arr[1].replace(/(\\d{" + rdigits + "})/, '$1" + sciletter + "' + sci.s);\n";
 }
 return code + "return arr.join('.');\n";
}

Number.toScientific = function(val, ldigits, rdigits, scidigits, showsign) {
 var result = {l:"", r:"", s:""};
 var ex = "";
 // Make ldigits + rdigits significant figures
 var before = Math.abs(val).toFixed(ldigits + rdigits + 1).trim('0');
 // Move the decimal point to the right of all digits we want to keep,
 // and round the resulting value off
 var after = Math.round(new Number(before.replace(".", "").replace(
     new RegExp("(\\d{" + (ldigits + rdigits) + "})(.*)"), "$1.$2"))).toFixed(0);
 // Place the decimal point in the new string
 if (after.length >= ldigits) {
     after = after.substring(0, ldigits) + "." + after.substring(ldigits);
 }
 else {
     after += '.';
 }
 // Find how much the decimal point moved.  This is #places to LODP in the original
 // number, minus the #places in the new number.  There are no left-padded zeroes in
 // the new number, so the calculation for it is simpler than for the old number.
 result.s = (before.indexOf(".") - before.search(/[1-9]/)) - after.indexOf(".");
 // The exponent is off by 1 when it gets moved to the left.
 if (result.s < 0) {
     result.s++;
 }
 // Split the value around the decimal point and pad the parts appropriately.
 result.l = (val < 0 ? '-' : '') + String.leftPad(after.substring(0, after.indexOf(".")), ldigits, "0");
 result.r = after.substring(after.indexOf(".") + 1);
 if (result.s < 0) {
     ex = "-";
 }
 else if (showsign) {
     ex = "+";
 }
 result.s = ex + String.leftPad(Math.abs(result.s).toFixed(0), scidigits, "0");
 return result;
}

Number.prototype.round = function(decimals) {
 if (decimals > 0) {
     var m = this.toFixed(decimals + 1).match(
         new RegExp("(-?\\d*)\.(\\d{" + decimals + "})(\\d)\\d*$"));
     if (m && m.length) {
         return new Number(m[1] + "." + String.leftPad(Math.round(m[2] + "." + m[3]), decimals, "0"));
     }
 }
 return this;
}

Number.injectIntoFormat = function(val, format, stuffExtras) {
 var i = 0;
 var j = 0;
 var result = "";
 var revneg = val.charAt(val.length - 1) == '-';
 if ( revneg ) {
    val = val.substring(0, val.length - 1);
 }
 while (i < format.length && j < val.length && format.substring(i).search(/[0#?]/) >= 0) {
     if (format.charAt(i).match(/[0#?]/)) {
         // It's a formatting character; copy the corresponding character
         // in the value to the result
         if (val.charAt(j) != '-') {
             result += val.charAt(j);
         }
         else {
             result += "0";
         }
         j++;
     }
     else {
         result += format.charAt(i);
     }
     ++i;
 }
 if ( revneg && j == val.length ) {
     result += '-';
 }
 if (j < val.length) {
     if (stuffExtras) {
         result += val.substring(j);
     }
     if ( revneg ) {
          result += '-';
     }
 }
 if (i < format.length) {
     result += format.substring(i);
 }
 return result.replace(/#/g, "").replace(/\?/g, " ");
}

Number.addSeparators = function(val) {
 return val.reverse().replace(/(\d{3})/g, "$1,").reverse().replace(/^(-)?,/, "$1");
}

String.prototype.reverse = function() {
 var res = "";
 for (var i = this.length; i > 0; --i) {
     res += this.charAt(i - 1);
 }
 return res;
}

String.prototype.trim = function(ch) {
 if (!ch) ch = ' ';
 return this.replace(new RegExp("^" + ch + "+|" + ch + "+$", "g"), "");
}

String.leftPad = function (val, size, ch) {
 var result = new String(val);
 if (ch == null) {
     ch = " ";
 }
 while (result.length < size) {
     result = ch + result;
 }
 return result;
}

String.escape = function(string) {
 return string.replace(/('|\\)/g, "\\$1");
}

	
