// JavaScript Document

var shiftf = false //function keys with f
var shiftg = false //function keys constant
var shifta = false //function keys math
var shifts = false //store keys with s
var shiftr = false //recall keys with r
var shiftc = false //clear keys with c
var shifto = false //notation fixed with x decimals
var shiftt = false //function keys statistics
var shiftl = false //cash flow
var shifth = false //show help
var shiftv = false //show convert
var computed = true;
var comma = 0 //default comma instead of dot
var dgmode = 1; // rad mode is default
var rnd = 17;//number of decimals
var not = "norm";//notation
var enterpressed = false;
var stack=new Array(5);
var undostack=new Array(5);
var lastxvalue=0;
var memory = new Array(5);
for (i = 0; i < memory.length; ++ i) memory[i] = new Array (1);
var mmode=0//default memory[mmode]
var nmem = 20;
var stacklevel = 0; //level of stack that is filled
var stackpointer = 0; //defines which stacks are visible
var oldx = 0; //value of x before latest calculation
var historytext = "";
var m = new Array(8); //array with menu items
for (i = 0; i < m.length; ++ i)
	m[i] = new Array (1);
var notation = new Array(4); //array with conversion constants
var clear = new Array(4); //array with conversion constants
var hlptxt = "";
defineconstants();

function escape(f) { //escapes from two-key menu
	shiftf = false 
	shiftg = false
	shifta = false
	shifts = false
	shiftr = false
	shiftc = false
	shifto = false
	shiftt = false
	shiftl = false
	shifth = false
	shiftv = false
	if (document.layers) {
		document.layers['menushift'].visibility='hide';
	} else {
		document.getElementById('menushift').style.visibility='hidden';
	}
	f.sf.style.backgroundColor = '#D6D6D6';
	f.sf.value = "";
}

function round(x) { //remove trailing zeros
	x=x.toPrecision(15);
	var third="";
	var ePos=x.indexOf("e");
	if (ePos>-1){
		third=x.substring(ePos,x.length);
		x=x.substring(0,ePos);
	}
	var decPos=x.indexOf(".");
	if (decPos>-1){
		first=x.substring(0,decPos);
		second=x.substring(decPos,x.length);
		while (second.charAt(second.length-1)=="0") {
			second=second.substring(0,second.length-1);
		}
		if (second.length>1) {
			return first+second+third;
		}else{
			return first+third;
		}
	}else{
		return x;
	}
}

function display(f){
		//alert(round(stack[1])+" "+round(stack[2]));
	//round and display
	var sp = stackpointer;
	//alert(sp);
	stack[sp+0]=(isNaN(stack[sp+0])?0:stack[sp+0]);
	stack[sp+1]=(isNaN(stack[sp+1])?0:stack[sp+1]);
	stack[sp+2]=(isNaN(stack[sp+2])?0:stack[sp+2]);
	stack[sp+3]=(isNaN(stack[sp+3])?0:stack[sp+3]);
	stack[sp+4]=(isNaN(stack[sp+4])?0:stack[sp+4]);
	
	f.displaylabel.value=(sp==0)?"x:":sp+":";
	f.stacklabel.value=(sp==0)?"y:":sp+1+":";
	f.stacklabel1.value=sp+2+":";
	f.stacklabel2.value=sp+3+":";
	f.stacklabel3.value=sp+4+":";
	
	if (not=="norm") {
//		f.display.value=round_extra(parseFloat(stack[0]));
		f.display.value=round(parseFloat(stack[sp+0]));
		f.stack.value=round(parseFloat(stack[sp+1]));
		f.stack1.value=round(parseFloat(stack[sp+2]));
		f.stack2.value=round(parseFloat(stack[sp+3]));
		f.stack3.value=round(parseFloat(stack[sp+4]));
	} else if (not=="sci"||not=="eng") {
		var n = false;
		if (not=="eng") {
			n = true;
		}
		f.display.value=to_sci(parseFloat(stack[sp+0]).toPrecision(rnd),n);
		f.stack.value=to_sci(parseFloat(stack[sp+1]).toPrecision(rnd),n);
		f.stack1.value=to_sci(parseFloat(stack[sp+2]).toPrecision(rnd),n);
		f.stack2.value=to_sci(parseFloat(stack[sp+3]).toPrecision(rnd),n);
		f.stack3.value=to_sci(parseFloat(stack[sp+4]).toPrecision(rnd),n);
	} else if (not=="fxd") {
		f.display.value=parseFloat(stack[sp+0]).toFixed(rnd);
		f.stack.value=parseFloat(stack[sp+1]).toFixed(rnd);
		f.stack1.value=parseFloat(stack[sp+2]).toFixed(rnd);
		f.stack2.value=parseFloat(stack[sp+3]).toFixed(rnd);
		f.stack3.value=parseFloat(stack[sp+4]).toFixed(rnd);
	} else if (not=="fin") {
		f.display.value=parseFloat(stack[sp+0]).toFixed(2);
		f.stack.value=parseFloat(stack[sp+1]).toFixed(2);
		f.stack1.value=parseFloat(stack[sp+2]).toFixed(2);
		f.stack2.value=parseFloat(stack[sp+3]).toFixed(2);
		f.stack3.value=parseFloat(stack[sp+4]).toFixed(2);
	}
	if(stack[1]==0) f.stack.value="";
	if(stack[2]==0) f.stack1.value="";
	if(stack[3]==0) f.stack2.value="";
	if(stack[4]==0) f.stack3.value="";
	if (comma==1){
		f.display.value=f.display.value.replace(/\./,",");
		f.stack.value=f.stack.value.replace(/\./,",");
		f.stack1.value=f.stack1.value.replace(/\./,",");
		f.stack2.value=f.stack2.value.replace(/\./,",");
		f.stack3.value=f.stack3.value.replace(/\./,",");
	}
	//make cookies
	var tmp;
	var stackstr="";
	stackstr=stack.join(","); //all stacks in string
	stackstr=stackstr.replace(/NaN/g,""); //skip emptie ones
	stackstr=stackstr+","; 
//	alert(stackstr);
	createCookie("stackstr",stackstr,365);
	//define stacklevel
	stacklevel = 5;
	for (var i = 4; i >= 0; i--) {
		if (parseFloat(stack[i])==0.0){
			stacklevel -= 1;
		}
	}
	//copy value to clipboard
//	var x;
//	x = f.display.value;
//	copy(x)
}

function copyorg(text2copy) { 
//copy value to clipboard
//flash file _clipboard.swf is needed as Firefox does not support the standard feature:
//window.clipboardData
//For IE we also use it as it doesn't give ugly warning to user
//thanks to Jeff Larson and Mark Percival
	if (window.clipboardData) {
		window.clipboardData.setData("Text",text2copy);
	} else {
		var flashcopier = 'flashcopier';
		if(!document.getElementById(flashcopier)) {
			var divholder = document.createElement('div');
			divholder.id = flashcopier;
			document.body.appendChild(divholder);
		}
		document.getElementById(flashcopier).innerHTML = '';
		var divinfo = '<embed type="application/x-shockwave-flash" height="0" width="0" FlashVars="clipboard='+escape(text2copy)+'" src="_clipboard.swf">';
		document.getElementById(flashcopier).innerHTML = divinfo;
	}
}

//out of use
function copy(text){
  var clip = new ZeroClipboard.Client();
  clip.setText(text);
  clip.glue( 'd_clip_button' );
  //var html = clip.getHTML( 150, 20 );
  //document.write(html);
}

//out of use
function copymem() {
	var x;
	x="";
	x += (isNaN(memory[mmode][nmem-1]))?"":memory[mmode][nmem-1];
	for (var i = nmem-1; i >= 0; i--) {
		x += (isNaN(memory[mmode][i]))?"\n":"\n"+memory[mmode][i];
	}
	if (comma==1){
		x=x.replace(/\./g,",");
	}
	copy(x);
}

function displaymem(f) {
	var x 
	x = "";
	if (mmode<2){
		x += (isNaN(memory[mmode][19]))?"j:  ":"j:  "+memory[mmode][19];
		x += (isNaN(memory[mmode][18]))?"\n"+"i:  ":"\n"+"i:  "+memory[mmode][18];
		x += (isNaN(memory[mmode][17]))?"\n"+"h:  ":"\n"+"h:  "+memory[mmode][17];
		x += (isNaN(memory[mmode][16]))?"\n"+"g:  ":"\n"+"g:  "+memory[mmode][16];
		x += (isNaN(memory[mmode][15]))?"\n"+"f:  ":"\n"+"f:  "+memory[mmode][15];
		x += (isNaN(memory[mmode][14]))?"\n"+"e:  ":"\n"+"e:  "+memory[mmode][14];
		x += (isNaN(memory[mmode][13]))?"\n"+"d:  ":"\n"+"d:  "+memory[mmode][13];
		x += (isNaN(memory[mmode][12]))?"\n"+"c:  ":"\n"+"c:  "+memory[mmode][12];
		x += (isNaN(memory[mmode][11]))?"\n"+"b:  ":"\n"+"b:  "+memory[mmode][11];
		x += (isNaN(memory[mmode][10]))?"\n"+"a:  ":"\n"+"a:  "+memory[mmode][10];
		for (var i = 9; i >= 0; i--) {
			x += (isNaN(memory[mmode][i]))?"\n"+i+":  ":"\n"+i+":  "+memory[mmode][i];
		}
	}else {
		x += "*** COMPOUND INTEREST ******";
		x += "\n--- present value";
		x += (isNaN(memory[mmode][7]))?"\n"+"7:  ":"\n"+"7:  "+memory[mmode][7];
		x += "\n--- future value ";
		x += (isNaN(memory[mmode][6]))?"\n"+"6:  ":"\n"+"6:  "+memory[mmode][6];
		x += "\n--- periods";
		x += (isNaN(memory[mmode][5]))?"\n"+"5:  ":"\n"+"5:  "+memory[mmode][5];
		x += "\n--- CAGR (interest)";
		x += (isNaN(memory[mmode][4]))?"\n"+"4:  ":"\n"+"4:  "+memory[mmode][4];
		x += "\n============================";
		x += "\n";
		x += "\n*** ANNUITY ****************";
		x += "\n--- principal or PV";
		x += (isNaN(memory[mmode][3]))?"\n"+"3:  ":"\n"+"3:  "+memory[mmode][3];
		x += "\n--- payment per period";
		x += (isNaN(memory[mmode][2]))?"\n"+"2:  ":"\n"+"2:  "+memory[mmode][2];
		x += "\n--- periods";
		x += (isNaN(memory[mmode][1]))?"\n"+"1:  ":"\n"+"1:  "+memory[mmode][1];
		x += "\n--- interest per period";
		x += (isNaN(memory[mmode][0]))?"\n"+"0:  ":"\n"+"0:  "+memory[mmode][0];
	}
	if (comma==1){
		x=x.replace(/\./g,",")
	}
	switch (mmode){
		case 1:
			f.memory.style.backgroundColor = '#feffe1';
			f.memheader1.value="Memory Y";
			break;
		case 2:
			f.memory.style.backgroundColor = '#dff7ff';
			f.memheader1.value="Finance";
			break;
		default:
			f.memory.style.backgroundColor = '#eeeeee';
			f.memheader1.value="Memory X";
	}
	//f.memheader2.value="sw";
	f.memory.value=x
}

function history(f,name){
//alert("hist");
	var tab="";
	if (name!=""){
		name=" "+name;
	}
	for (var i = 1; i<stacklevel; i++){
		tab += "  ";
	}
	if (oldx != undostack[0] && enterpressed==false) { //new entry has been given
		historytext += "\n" + tab + round(parseFloat(undostack[0]));
	}
	if (historytext != ""){
		historytext += name + "\n";
		} else {
		historytext += name; //history still empty, so no new line
		}
	historytext += tab + round(parseFloat(stack[0]));
	oldx=stack[0];
	if (comma==1){
		historytext=historytext.replace(/\./g,",")
	}
	f.history.value=historytext;
}

function memmode(f) {
	mmode+=1;
	if (mmode>2) mmode=0;
	displaymem(f);
	createCookie('mmode',mmode,365);
}

function showmenushift(layer,value) {
	var j=0
	var txt, color
	switch (layer){
		case "algebra":
			txt="<b>Algebra</b><br><br>";
			j=5;
			color='#FFFF66';
			break;
		case "financial":
			txt="<b>Finance</b><br><br>";
			j=4;
			color='#99CCFF';
			break;
		case "statistics":
			txt="<b>Statistics</b><br><br>";
			j=6;
			color='#FFFF66';
			break;
		case "cashflow":
			txt="<b>CashFlow</b><br><br>";
			j=7;
			color='#99CCFF';
			break;
		case "constants":
			txt="<b>Fysical constants</b><br><br>";
			j=0;
			color='#FFCC99';
			break;
		case "menuv":
			txt="<b>Convert</b><br><br>";
			j=1;
			color='#FFCC99';
			break;
		case "clear":
			txt="<b>Clear/copy</b><br><br>";
			j=3;
			color='#CCCCCC';
			break;
		case "notation":
			txt="<b>Notation</b><br><br>";
			j=2;
			color='#CCCCCC';
			break;
		default:
//			alert("default");
	}
	if (value==1){
		//	alert(j+" "+m[j].length);
		for (var i=0; i<m[j].length; i++){
			txt += "<span class=label3>" + m[j][i][0] + "</span>  "+ m[j][i][1] + "<br>";
		}
		document.getElementById("menushift").innerHTML=txt
		document.getElementById("menushift").style.backgroundColor = color;
	}
	if (value==0) {
		if (document.layers) {
			document.layers['menushift'].visibility='hide';
		} else {
			document.getElementById('menushift').style.visibility='hidden';
		}
	} else if (value==1) {
		if (document.layers){
			document.layers['menushift'].visibility='show';
		} else {
			document.getElementById('menushift').style.visibility='visible';
		}
	}
}

function Init(){
	var f=document.rpncal;
	if (readCookie('deg') == "deg"){
		dgmode = Math.PI/180.0;
		f.deg.value="deg";
	}else { dgmode = 1;
		f.deg.value="rad";
	}
	if (readCookie('comma') == "c"){
		comma = 1;
	}else { comma = 0;
	}
	var memstr=""
	for (var j=0;j<3;j++){ //get data from all 3 memories 
		memstr=String(readCookie("memstr"+j));
		for (var i=0;i<nmem;i++){ //20 memories per memstring
			var n=memstr.search(/,/);
			if (n!=0){
				memory[j][i]=memstr.substring(0,n);
			}
			memstr=memstr.substring(n+1,memstr.length);
			if (n<0) {break}
		}
	}
	displaymem(f);

	var stackstr=String(readCookie("stackstr"));
	//read first 20 stack registers
	for (var i = 0; i < 20; i++) {
		var n=stackstr.search(/,/);
		if (n!=0){
			stack[i]=stackstr.substring(0,n);
		}
		stackstr=stackstr.substring(n+1,stackstr.length);
		if (n<0) {break}
	}

	var r = readCookie("rnd");
	if (isNaN(r)||r=="") { 
		rnd = 17;
	} else {
		rnd = r;
	}
	not = readCookie("not");
	if (not=="") { 
		not="norm";
	}
	//if (not=="norm") { rnd = 18;}
		//alert(not+" "+rnd);
	if (not=="fin") { //no decimals in display needed
		f.numpres.value = not;
	} else {
		f.numpres.value = not+ " " +rnd;
	}
	stacklevel = 5; //define stacklevel
	for (var i = 4; i >= 0; i--) {
		if (parseFloat(stack[i])==0.0){
			stacklevel -= 1;
		}
	}
//fill history in right order
	for (var i = stacklevel - 1; i >= 0; i--) {
		var tab="";
		for (var j = 0; j<stacklevel-1-i; j++) {
			tab += "  ";
		}
		historytext += tab + round(parseFloat(stack[i]));
		if (i!=0){
			historytext += "\n";
		}
		if (comma==1){
			historytext=historytext.replace(/\./g,",")
		}
	}
	showmenushift('algebra',0)
	f.history.value=historytext;
	f.memheader1.value="Memory X";
	//f.memheader2.value="sw";
	f.stackheader.value="Stack";
	display(f);
	f.display.focus();
}

function rpncalchelp(f){
	window.location="rpncalchelp.html"
}

function to_sci(s,eng){ //by Stephen Ostermiller
	var the_exp=0;
	var is_negative=false;
	if(s.length>0&&s.charAt(0)=='-'){
		is_negative=true;
		s=s.substring(1,s.length);
	}
	var regex_splitter=s.split(new RegExp('[eE]'));
	if(regex_splitter.length>1){
		the_exp=parseInt(regex_splitter[1]);
		s=regex_splitter[0];
	}
	regex_splitter=s.split(/[\.]/);
	if(regex_splitter.length>1){
		s=regex_splitter[0]+regex_splitter[1];
		the_exp+=regex_splitter[0].length-1;
	}else{
		the_exp+=s.length-1;
	}
	var leading_zeros=0;
	for(leading_zeros=0;leading_zeros<s.length&&s.charAt(leading_zeros)=='0';leading_zeros++){
		the_exp=the_exp-1;
	}
	s=s.substring(leading_zeros,s.length);
	var move_dec;
	if(eng){
		if(the_exp>=0){
			move_dec=(the_exp%3)+1;
		}else{
			move_dec=4-((-the_exp)%3);
			if(move_dec==4){
				move_dec=1;
			}
		}
		the_exp-=(move_dec-1);
	}else{
		move_dec=1;
	}
	var trailing_zeros='';
	for(var i=s.length;i<move_dec;i++){
		trailing_zeros+='0';
	}
	return(
		(is_negative?'-':'')+
		((s.length==0)?'0':s.substring(0,move_dec))+
		((s.length<=move_dec)?trailing_zeros:('.'+s.substring(move_dec,s.length)))+
		((s.length==0||the_exp==0)?'':('e'+the_exp))
	);
}

function recall(f,n) {
	fillundostack(f);
	if(isNaN(memory[mmode][n]) || ! isFinite(memory[mmode][n])){
		memory[mmode][n]=0;
	}
	pushStack(f);
	stack[0]=memory[mmode][n];
	display(f);
	computed = true;
}

function clearm(f,n) {
	memory[mmode][n] = "";
	displaymem(f);
	memstr=memory[mmode].join(","); //all memories in string
	memstr=memstr.replace(/NaN/g,""); //skip emptie ones
	memstr=memstr+","; 
	createCookie("memstr"+mmode,memstr,365);
//	createCookie("mem"+n,"",365);
}

function store(f,n) {
	var memstr="";
	memory[mmode][n] = parseFloat(stack[0]);
	displaymem(f);
	memstr=memory[mmode].join(","); //all memories in string
	memstr=memstr.replace(/NaN/g,""); //skip emptie ones
	memstr=memstr+","; 
	createCookie("memstr"+mmode,memstr,365);
//	createCookie("mem"+n,memory[mmode][n],365);
	enterpressed = true;
}

function storefree(f) {
	if (enterpressed == false && computed == false) {
		enterx(f);
 	}
	for (var n=0; n<nmem; n++) {
		if (isNaN(memory[mmode][n]) || memory[mmode][n]=="") {
			memory[mmode][n]=parseFloat(stack[0]); 
			break;
		}
	}
	displaymem(f);
	memstr=memory[mmode].join(","); //all memories in string
	memstr=memstr.replace(/NaN/g,""); //skip emptie ones
	memstr=memstr+","; 
	createCookie("memstr"+mmode,memstr,365);
//	createCookie("mem"+n,memory[mmode][n],365);
}

function createCookie(name,value,days) {
//	alert(value);
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return "";
}

function pushStack(f) {
	for (var j=49;j>0;j--) {
		stack[j] = stack[j-1];
	}
	display(f);
}

function popStackDisplay(f) {// select yellow box
	fillundostack(f);
	pushStack(f);
	stack[0]=stack[stackpointer+1];
	stackpointer=0;
	display(f);
	history(f,"")
	computed = true;
}

function popStack(f) {
	for (var j=1;j<50;j++) {
		stack[j] = stack[j+1];
	}
	stack[50] = 0;
	display(f);
}

function fillundostack(f) {
	for (var j=0;j<50;j++) {
		undostack[j] = stack[j];
	}
	lastxvalue=stack[0];
}

function undoall(f) {
	for (var j=0;j<50;j++) {
		stack[j] = undostack[j];
	}
	display(f);
	computed=true;
}

function lastx(f) {
	if(computed) pushStack(f);
	stack[0]=lastxvalue;
	display(f);
	computed=true;
}

function isnotafinatenumber(f) {// make sure we have a number in the display
	var tmp;
	tmp=parseFloat(stack[0]);
	if(isNaN(tmp) || ! isFinite(tmp)){
		return(true);
		//alert("true" +tmp);
	}
	return(false);
	//alert("false" +tmp);
}

function enterx(f) {// the enter button
	fillundostack(f);
	if(isnotafinatenumber(f)){
		//alert("not finate"+ stack[0]);
		stack[0]="0";
		display(f);
	}else{
	//alert("push"+ stack[0]);
	pushStack(f);
	}
	enterpressed = true;
	computed = false;
	history(f,"");
}

function cx(f) {// clear x
	fillundostack(f);
	stack[0] = stack[1];
	popStack(f);
//	pushStack(f);
//	display(f);
	enterpressed = true;
	computed = true;
}

function clrstack(f) {
	fillundostack(f);
	for (var j=0;j<50;j++) {
		stack[j] = 0;
	}
	stackpointer = 0;
	display(f);
	computed = true;
}

function clrhistory(f) {
	historytext="";
	f.history.value=historytext;
}

function clrmem(f) {
	for (var i = 0; i < nmem; i++) {
		clearm(f,i);
	}
}

function addChar(f, character) {// add a new character to the display
	var tmpvar;
	if (computed || enterpressed) {
		fillundostack(f);
	}
	// auto-push the stack if the last value was computed
	if(computed) {
		if(isnotafinatenumber(f)){
			stack[0]="0";
			f.display.value += character;
		}
		pushStack(f);
		stack[0] = "";
		f.display.value += character;
		computed = false;
	}
	if(enterpressed) {
		stack[0] = "";
		computed = false;
		enterpressed = false;
	}
	tmpvar=stack[0];

	// make sure stack[0] is a string
	if(tmpvar.match(/^[0-9\.\-eE]+$/)){
		stack[0] += character;
		f.display.value += character;
	} else {
		stack[0] = character;
		f.display.value = character;
	}
}

function deleteChar(f) {
	//alert("back");
	if (computed || enterpressed) {
		fillundostack(f);
		stack[0] = "";
		computed = false;
		enterpressed = false;
	display(f);
	}else{
		f.display.value = f.display.value.substring(0, f.display.value.length - 1);
	stack[0]=f.display.value;
	}
}

function powxy(f) {
	var tmpvar;
	fillundostack(f);
	tmpvar = Math.pow(parseFloat(stack[1]),parseFloat(stack[0]));
	stack[0] = tmpvar;
	computed = true;
	popStack(f);
	display(f);
	history(f,"y^x")
}

function square(f) {
	fillundostack(f);
	stack[0] = parseFloat(stack[0]) * parseFloat(stack[0]);
	computed = true;
	display(f);
	history(f,"^2")
}

function sqrtx(f) {
	fillundostack(f);
	stack[0] = Math.sqrt(parseFloat(stack[0]));
	display(f);
	computed = true;
	history(f,"sqrt")
}

function expx(f) {
	fillundostack(f);
	stack[0] = Math.exp(parseFloat(stack[0]));
	computed = true;
	display(f);
	history(f,"e^x")
}

function lnx(f) {
	fillundostack(f);
	stack[0] = Math.log(parseFloat(stack[0]));
	computed = true;
	display(f);
	history(f,"ln")
}

function log10(f) {// the 0.000...1 is to handle rounding errors better
	fillundostack(f);
	stack[0] = Math.log(parseFloat(stack[0]))/(Math.LN10 - 0.00000000000000001);
	computed = true;
	display(f);
	history(f,"10log")
}

// ln(gamma(x))
// x is the actual value not a form object
function internal_loggamma(x) {
	with(Math) {
		var v=1;
		var w=0; 
		var z=0; 
		while ( x<8 ) { v*=x; x++ }
		w=1/(x*x); 
		return ((((((((-3617/122400)*w + 7/1092)*w
		 -691/360360)*w + 5/5940)*w
		 -1/1680)*w + 1/1260)*w
		 -1/360)*w + 1/12)/x + 0.5 * log(2*PI)-log(v)-x+(x-0.5)*log(x) ;
	 } 
}

// gamma function
// x is the actual value not a form object
function internal_gamma(x) {
	with(Math) {
		if ( x <= 0 ) {
			if (abs(x)-floor(abs(x))==0 )
				// should be complex infinity but we do not have
				// complex numbers
				return Number.POSITIVE_INFINITY; 
			else 
				return PI/( sin(PI*x) * exp( internal_loggamma(1-x) ) );
		}else 
			return exp(internal_loggamma(x)) ;
	} 
}

// calculate the factorial including non integer factorial
// Integer factorial is: n!= n* (n-1)!
// Non interger is: n!=gamma(n+1)
function internal_factorial(n) {
  with(Math) {
	  if (n<0)  /* if negative */
		return internal_gamma(n+1);
	  else if ((n == 0) || (n == 1))
		return 1;
	  else if (abs(n)-floor(abs(n))==0 ) { // positive integer
		var buf = 1;
		var i;
		for (i=1;i<=n;i++) {
			buf = buf*i;
		}
		return buf;
	  }else		 // if non-integer 
		return internal_gamma(n+1);
  } 
}

function st_fact(n) { //standard factorial
  with(Math) {
	  if (n<0)  /* if negative */
		return "error";
	  else if ((n == 0) || (n == 1))
		return 1;
	  else if (abs(n)-floor(abs(n))==0 ) { // positive integer
		var buf = 1;
		var i;
		for (i=1;i<=n;i++) {
			buf = buf*i;
		}
		return buf;
	  }else		 // if non-integer 
		return "error";
  } 
}

function permutations(f) {
	fillundostack(f);
	//var u = stack[1] - stack[0];
	stack[0] = st_fact(stack[1]) / st_fact(stack[1] - stack[0]);
	computed = true;
	popStack(f);
	history(f,"perm")
}

function combinations(f) {
	fillundostack(f);
	stack[0] = st_fact(stack[1]) / (st_fact(stack[1] - stack[0]) * st_fact(stack[0]));
	computed = true;
	popStack(f);
	history(f,"comb")
}

function random(f) {
	fillundostack(f);
	pushStack(f);
	stack[0] = Math.random();
	computed = true;
	display(f);
	history(f,"rnd")
}

function randominteger(f) {
	fillundostack(f);
	var t=0; 
	var r=stack[0];
	var s=stack[1];
	if (r<0) r=-r;
	if (s<0) s=-s;
	if (r<s) {t=r;r=s;s=t};
	r=Math.floor(r);
	s=Math.floor(s);
	stack[0] = Math.floor((r-s+1)*Math.random())+s;
	computed = true;
	popStack(f);
	history(f,"rndn");
}

function npv(f) {
	fillundostack(f);
	var i = parseFloat(stack[0]);
	var x = parseFloat(memory[mmode][0]);
	var y = 1/(1+i);
	//alert(x+" "+y)
	for (var n=1; n<nmem; n++) {
		if (! memory[mmode][n]==""){
			x += y * parseFloat(memory[mmode][n]);
			//alert(n+" "+ memory[mmode][n] + " " + x + " " + y);
		}
		y = y/(1+i);
	}
	stack[0] = x;
	computed = true;
	display(f);
	history(f,"npv")
}

function avg(f) {
	fillundostack(f);
	var n = 0;
	var x = 0.0;
	for (var i=0; i<nmem; i++) {
		if (! memory[mmode][i]==""){
			x += parseFloat(memory[mmode][i]);
			n += 1;
		}
	}
	if(computed) {
		pushStack(f);
	}
	stack[0] = x/n;
	computed = true;
	display(f);
	history(f,"avg")
}

function sum(f) {
	fillundostack(f);
	var x = 0;
	for (var i=0; i<nmem; i++) {
		if (! memory[mmode][i]==""){
			x += parseFloat(memory[mmode][i]);
		}
	}
	if(computed) {
		pushStack(f);
	}
	stack[0] = x;
	computed = true;
	display(f);
	history(f,"sum")
}

function sumsquares(f) {
	fillundostack(f);
	var x = 0;
	for (var i=0; i<nmem; i++) {
		if (! memory[mmode][i]==""){
			x += parseFloat(memory[mmode][i])*parseFloat(memory[mmode][i]);
		}
	}
	if(computed) {
		pushStack(f);
	}
	stack[0] = x;
	computed = true;
	display(f);
	history(f,"sumx2")
}

function dev(f) {
	fillundostack(f);
	var n = 0;
	var x = 0;
	var d = 0;
	for (var i=0; i<nmem; i++) {
		if (! memory[mmode][i]==""){
			x += parseFloat(memory[mmode][i]);
			n += 1;
		}
	}
	x = x/n; //average
	for (var i=0; i<nmem; i++) {
		if (! memory[mmode][i]==""){
			d += (parseFloat(memory[mmode][i]) - x)*(parseFloat(memory[mmode][i]) - x);
		}
	}
	d = d/n;
	if(computed) {
		pushStack(f);
	}
	stack[0]=Math.sqrt(d);
	computed = true;
	display(f);
	history(f,"dev")
}

function covariance(f) { //covariance
	fillundostack(f);
	var n=0;
	var m=0;
	var x=0;
	var y=0;
	var result = 0;
	for (var i=0; i<nmem; i++) {
		if(isNaN(parseFloat(memory[0][i]))){
			break;
		}else{
			x += parseFloat(memory[0][i]);
			n += 1;
		}
	}
	for (var i=0; i<nmem; i++) {
		if(isNaN(parseFloat(memory[1][i]))){
			break;
		}else{
			y += parseFloat(memory[1][i]);
			m += 1;
		}
	}
	if (n!=m) {
		alert("Error: different number of variables in Memory X and Memory Y");
	}else if (n==0 || m==0){
		alert("Error: 0 variables in Memory X and/or Memory Y");
	}else{
		x = x/n; //average
		y = y/m; //average
//		alert (x+" "+y+" "+n+" "+m);
		for (var i=0; i<n; i++) {
			result += (parseFloat(memory[0][i]) - x)*(parseFloat(memory[1][i]) - y);
		}
		result = result/n;
		if(computed) {
			pushStack(f);
		}
		stack[0]=result;
		computed = true;
		display(f);
		history(f,"cov")
	}
}

function correlation(f) { 
	fillundostack(f);
	var n=0;
	var m=0;
	var x=0;
	var y=0;
	var devx=0;
	var devy=0;
	var result = 0;
	for (var i=0; i<nmem; i++) {
		if(isNaN(parseFloat(memory[0][i]))){
			break;
		}else{
			x += parseFloat(memory[0][i]);
			n += 1;
		}
	}
	for (var i=0; i<nmem; i++) {
		if(isNaN(parseFloat(memory[1][i]))){
			break;
		}else{
			y += parseFloat(memory[1][i]);
			m += 1;
		}
	}
	if (n!=m) {
		alert("Error: different number of variables in Memory X and Memory Y");
	}else if (n==0 || m==0){
		alert("Error: 0 variables in Memory X and/or Memory Y");
	}else{
		x = x/n; //average
		y = y/m; //average
		for (var i=0; i<n; i++) {
			devx += ((parseFloat(memory[0][i]) - x)*(parseFloat(memory[0][i]) - x));
		}
		devx = Math.sqrt(devx/n);
		for (var i=0; i<m; i++) {
			devy += ((parseFloat(memory[1][i]) - y)*(parseFloat(memory[1][i]) - y));
		}
		devy = Math.sqrt(devy/m);
		for (var i=0; i<n; i++) {
			result += (parseFloat(memory[0][i]) - x)*(parseFloat(memory[1][i]) - y);
		}
		result = result/n;
		result = result/(devx*devy)
		if(computed) {
			pushStack(f);
		}
		stack[0]=result;
		computed = true;
		display(f);
		history(f,"cor")
	}
}

function irr(f){ //Method: Regula Falsi
	fillundostack(f);
	var delta=.0000001;
	var max=200;
	var satisfied=false;
	var dx, c, yc;
	var a=-.4;
	var b=.4;
	var ya=npv2(a);
	var yb=npv2(b);
	if(ya*yb>0){ //ya and yb have same sign; first try lower a or higher b
		for (var i=1;i<=10;i++){
			if(ya<0){
				a=a-.4;
				b=b-.4;
			}else{
				a=a+.4;
				b=b+.4;
			}
			ya=npv2(a);
			yb=npv2(b);
			if(ya*yb<0){
				break;
			}
		}
	}
	if(ya*yb>0){ //still same sign: error
		stack[0] = "Error";
		history(f,"error")
		computed = true;
		display(f);
	}else{
		for(var i = 1; i <= max ; i++) {
			if(satisfied == true){
				break;
			}
			dx=yb*(b-a)/(yb-ya);// Change in iterate
			c=b-dx; // New iterate
			yc=npv2(c); // Function value of new iterate
			if(yc==0) { 
				satisfied=true; // Exact root is found
			}else{
				if(yb*yc>0){
					b=c; // Squeeze from the right
					yb=yc;
				}else {
					a=c; // Squeeze from the left
					ya=yc;
				}
				if (Math.abs(yc)<delta){
					satisfied=true;
				}
			}
		}
		stack[0]=c
		history(f,"irr")
		computed = true;
		display(f);
	}
}

function npv2(irr){ //npv calculation for irr iteration
	var result = memory[mmode][0]; //init with startup costs
	for (var j = 1; j < nmem; j++) { //for each cash floe
		if (! memory[mmode][j]==""){
			result += memory[mmode][j] / Math.pow(1.0 + irr, j);
		}
	}//next cash flow
	return result;
}

function interest(f) { //interest for annuity
	fillundostack(f);
	if(isNaN(memory[mmode][3])||isNaN(memory[mmode][2])||isNaN(memory[mmode][1])){
		alert("Give value for"+'\n'+"\- pv in memory 3"+'\n'+"\- payment in memory 2"
		+'\n'+"\- periods in memory 1");
	}else{
		var pv = memory[mmode][3];
		var pmt = memory[mmode][2];
		var periods = memory[mmode][1];
		var result;
		//alert ((pv-(pmt*periods)));
		if (Math.abs(pv-pmt*periods)<1e-6){
			result = 0.0;
		}else if((pv-pmt*periods)>0){
			result="Error";
		}else{
			var delta=.0000001;
			var max=200;
			var satisfied=false;
			var dx, c, yc;
			var a=0.000001;
			var b=.8;
			var ya=pmt-pmt2(pv,a,periods);
			var yb=pmt-pmt2(pv,b,periods);
			if(ya*yb>0){ //still same sign: error
				stack[0] = "Error";
				history(f,"error")
				computed = true;
				display(f);
			}else{
				for(var i = 1; i <= max ; i++) {
					if(satisfied == true){
						break;
					}
					dx=yb*(b-a)/(yb-ya);// Change in iterate
					c=b-dx; // New iterate
					yc=pmt-pmt2(pv,c,periods); // Function value of new iterate
					if(yc==0) { 
						satisfied=true; // Exact root is found
					}else{
						if(yb*yc>0){
							b=c; // Squeeze from the right
							yb=yc;
						}else {
							a=c; // Squeeze from the left
							ya=yc;
						}
						if (Math.abs(yc)<delta){
							satisfied=true;
						}
					}
				}
				if(computed) {
					pushStack(f);
				}
				stack[0]=c
				history(f,"i%")
				computed = true;
				display(f);
			}
		}
	}
}

function pmt2(pv,interest,periods){ //payment calculation for interest iteration
	var result;
	result=Math.pow(parseFloat(1+interest),parseFloat(periods));
	result=1-(1/result);
	result=pv*interest/result;
	return result;
}

function pmt(f) { //payment pr period for annuity
	fillundostack(f);
	if(isNaN(memory[mmode][3])||isNaN(memory[mmode][1])||isNaN(memory[mmode][0])){
		alert("Give value for"+'\n'+"\- pv in memory 3"+'\n'+"\- periods in memory 1"
		+'\n'+"\- interest in memory 0");
//		alert("give value for pv in memory[mmode] 3, periods in memory[mmode] 1 and interest in memory[mmode] 0");
	}else{
		var pv = memory[mmode][3];
		var periods = memory[mmode][1];
		var interest = memory[mmode][0];
		var result;
		if (interest==0){
			result = pv/periods;
		}else{
			result = Math.pow(parseFloat(1+interest),parseFloat(periods));
			//alert(result);
			result = 1-(1/result);
			result = pv*interest/result;
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"pmt");
	}
}

function presentvalue(f) { //present value cagr
	fillundostack(f);
	if(isNaN(memory[mmode][6])||isNaN(memory[mmode][5])||isNaN(memory[mmode][4])){
		alert("Give value for"+'\n'+"\- FV in memory 6"+'\n'+"\- periods in memory 5"
		+'\n'+"\- cagr in memory 4");
//		alert("give value for FV in memory 6, periods in memory 5 and cagr in memory 4");
	}else{
		var fv = parseFloat(memory[mmode][6]);
		var periods = parseFloat(memory[mmode][5]);
		var interest = parseFloat(memory[mmode][4]);
		var result;
		if (interest==0){
			result = fv;
		}else{
			result = Math.pow(1+interest,periods);
			//alert(result);
			result = parseFloat(fv)/result;
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"pv");
	}
}

function cagr(f) { //periods value cagr
	fillundostack(f);
	if(isNaN(memory[mmode][7])||isNaN(memory[mmode][6])||isNaN(memory[mmode][5])){
		alert("Give value for"+'\n'+"\- PV in memory 7"+'\n'+"\- FV in memory 6"
		+'\n'+"\- periods in memory 5");
//		alert("give value for PV in memory 7, FV in memory 6 and periods in memory 5");
	}else{
		var pv = parseFloat(memory[mmode][7]);
		var fv = parseFloat(memory[mmode][6]);
		var periods = parseFloat(memory[mmode][5]);
		var result;
		if (pv==0||periods==0){
			result = "Error";
		}else{
			result = Math.pow((fv/pv),(1/periods))-1
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"n");
	}
}

function periodscagr(f) { //periods value cagr
	fillundostack(f);
	if(isNaN(memory[mmode][7])||isNaN(memory[mmode][6])||isNaN(memory[mmode][4])){
		alert("Give value for"+'\n'+"\- PV in memory 3"+'\n'+"\- FV in memory 6"
		+'\n'+"\- cagr in memory 4");
//		alert("give value for PV in memory 7, FV in memory 6 and cagr in memory 4");
	}else{
		var pv = parseFloat(memory[mmode][7]);
		var fv = parseFloat(memory[mmode][6]);
		var interest = parseFloat(memory[mmode][4]);
		var result;
		if (interest==0 ||pv==0){
			result = "Error";
		}else{
			result = (Math.log(fv/pv))/(Math.log(1+interest));
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"n");
	}
}

function futurevalue(f) { //future value cagr
	fillundostack(f);
	if(isNaN(memory[mmode][7])||isNaN(memory[mmode][5])||isNaN(memory[mmode][4])){
		alert("Give value for"+'\n'+"\- PV in memory 7"+'\n'+"\- periods in memory 5"
		+'\n'+"\- cagr in memory 4");
//		alert("give value for PV in memory 7, periods in memory 5 and cagr in memory 4");
	}else{
		var pv = parseFloat(memory[mmode][7]);
		var periods = parseFloat(memory[mmode][5]);
		var interest = parseFloat(memory[mmode][4]);
		var result;
		if (interest==0){
			result = pv;
		}else{
			result = Math.pow(1+interest,periods);
			result = pv*result;
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"fv");
	}
}

function principal(f) { //present value for annuity
	fillundostack(f);
	if(isNaN(memory[mmode][2])||isNaN(memory[mmode][1])||isNaN(memory[mmode][0])){
		alert("Give value for"+'\n'+"\- payment in memory 2"+'\n'+"\- periods in memory 1"
		+'\n'+"\- interest in memory 0");
//		alert("give value for payment in memory 2, periods in memory 1 and interest in memory 0");
	}else{
		var pmt = memory[mmode][2];
		var periods = memory[mmode][1];
		var interest = memory[mmode][0];
		var result;
		if (interest==0){
			result = pmt*periods;
		}else{
			result = Math.pow(parseFloat(1+interest),parseFloat(periods));
			//alert(result);
			result = 1-(1/result);
			result = pmt*result/interest;
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"prnc");
	}
}

function periods(f) { //number of periods for annuity
	fillundostack(f);
	if(isNaN(memory[mmode][3])||isNaN(memory[mmode][2])||isNaN(memory[mmode][0])){
		alert("Give value for"+'\n'+"\- present value in memory 3"+'\n'+"\- payment in memory 2"
		+'\n'+"\- interest in memory 0");
//		alert("give value for present value in memory 3, payment in memory 2 and interest in memory 0");
	}else{
		var pmt = memory[mmode][2];
		var pv = memory[mmode][3];
		var interest = memory[mmode][0];
		var result;
		if (interest==0){
			result = pv/pmt;
		}else{
			result=parseFloat(interest*pv);
			result=1-(result/pmt);
			result=Math.log(result);
			result = -result/Math.log(parseFloat(1+interest));
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"pv");
	}
}

function principalpart(f) { //principal part of payment x for annuity
	fillundostack(f);
	var j=parseFloat(stack[0]);
	if(isNaN(memory[mmode][3])||isNaN(memory[mmode][2])||isNaN(memory[mmode][1])||isNaN(memory[mmode][0])){
		var str="give value for pv in memory 3, payment in memory 2, periods in memory 1 ";
		str+="and interest in memory 0";
		alert(str);
	}else if(Math.abs(j)-Math.floor(Math.abs(j))!=0){ //no positive integer
		alert("Give number of payments in x");
	}else{
		var pv = memory[mmode][3];
		var pmt = memory[mmode][2];
		var periods = memory[mmode][1];
		var i = memory[mmode][0];
		var result;
		if(j>periods||j==0){
			result=0
		}else{
			result=(pmt-(i*pv))*Math.pow((1+i),(j-1))
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"prprt");
	}
}

function interestpart(f) { //principal part of payment x for annuity
	fillundostack(f);
	var j=parseFloat(stack[0]);
	if(isNaN(memory[mmode][3])||isNaN(memory[mmode][2])||isNaN(memory[mmode][1])||isNaN(memory[mmode][0])){
		var str="give value for pv in memory 3, payment in memory 2, periods in memory 1 ";
		str+="and interest in memory 0";
		alert(str);
	}else if(Math.abs(j)-Math.floor(Math.abs(j))!=0){ //no positive integer
		alert("Give number of payments in x");
	}else{
		var pv = memory[mmode][3];
		var pmt = memory[mmode][2];
		var periods = memory[mmode][1];
		var i = memory[mmode][0];
		var result;
		if(j>periods||j==0){
			result=0
		}else{
			result=pmt-((pmt-(i*pv))*Math.pow((1+i),(j-1)))
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"%iprt");
	}
}

function totalinterest(f) { //total interest paid for annuity
	fillundostack(f);
	var j=parseFloat(stack[0]);
	if(isNaN(memory[mmode][3])||isNaN(memory[mmode][2])||isNaN(memory[mmode][1])||isNaN(memory[mmode][0])){
		var str="give value for pv in memory 3, payment in memory 2, periods in memory 1 ";
		str+="and interest in memory 0";
		alert(str);
	}else{
		var pv = memory[mmode][3];
		var pmt = memory[mmode][2];
		var periods = memory[mmode][1];
		var i = memory[mmode][0];
		var result;
		result=(pmt*periods)-pv;
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"toti");
	}
}

function totalpayments(f) { //total interest paid for annuity
	fillundostack(f);
	var j=parseFloat(stack[0]);
	if(isNaN(memory[mmode][3])||isNaN(memory[mmode][2])||isNaN(memory[mmode][1])||isNaN(memory[mmode][0])){
		var str="give value for pv in memory 3, payment in memory 2, periods in memory 1 ";
		str+="and interest in memory 0";
		alert(str);
	}else{
		var pv = memory[mmode][3];
		var pmt = memory[mmode][2];
		var periods = memory[mmode][1];
		var i = memory[mmode][0];
		var result;
		result=pmt*periods;
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"tot");
	}
}

function remainingprincipal(f) { //remaining principal after payment x for annuity
	fillundostack(f);
	var j=parseFloat(stack[0]);
	if(isNaN(memory[mmode][3])||isNaN(memory[mmode][2])||isNaN(memory[mmode][1])||isNaN(memory[mmode][0])){
		var str="give value for pv in memory 3, payment in memory 2, periods in memory 1 ";
		str+="and interest in memory 0";
		alert(str);
	}else if(Math.abs(j)-Math.floor(Math.abs(j))!=0){ //no positive integer
		alert("Give number of payments in x");
	}else{
		var pv = memory[mmode][3];
		var pmt = memory[mmode][2];
		var periods = memory[mmode][1];
		var i = memory[mmode][0];
		var result;
		if(j>periods){
			result=0;//error
		}else if(j==periods){
			result=0;
		}else{
			result=pv-((pmt-(i*pv))*(Math.pow((1+i),j)-1)/i);
		}
		if(computed) {
			pushStack(f);
		}
		stack[0] = result;
		computed = true;
		display(f);
		history(f,"rem");
	}
}


// this function can be used directly from the gui
function factx(f) {
	fillundostack(f);
	stack[0] = internal_factorial(parseFloat(stack[0]));
	computed = true;
	display(f);
	history(f,"!")
}

function sin(f){
	fillundostack(f);
	stack[0] = Math.sin(parseFloat(stack[0])*dgmode);
	computed = true;
	display(f);
	history(f,"sin");
}

function asin(f){
	fillundostack(f);
	stack[0] = Math.asin(parseFloat(stack[0]))/dgmode;
	computed = true;
	display(f);
	history(f,"asin")
}

function cos(f){
	fillundostack(f);
	stack[0] = Math.cos(parseFloat(stack[0])*dgmode);
	computed = true;
	display(f);
	history(f,"cos")
}

function acos(f){
	fillundostack(f);
	stack[0] = Math.acos(parseFloat(stack[0]))/dgmode;
	computed = true;
	display(f);
	history(f,"acos")
}

function tan(f){
	fillundostack(f);
	stack[0] = Math.tan(parseFloat(stack[0])*dgmode);
	computed = true;
	display(f);
	history(f,"tan")
}

function atan(f){
	fillundostack(f);
	stack[0] = Math.atan(parseFloat(stack[0]))/dgmode;
	computed = true;
	display(f);
	history(f,"atan")
}

function sinh(f){
	fillundostack(f);
	var r=parseFloat(stack[0]);
	stack[0] = (Math.exp(r)-Math.exp(-r))/2;
	computed = true;
	display(f);
	history(f,"sinh");
}

function cosh(f){
	fillundostack(f);
	var r=parseFloat(stack[0]);
	stack[0] = (Math.exp(r)+Math.exp(-r))/2;
	computed = true;
	display(f);
	history(f,"cosh");
}

function tanh(f){
	fillundostack(f);
	var r=parseFloat(stack[0]);
	stack[0] = (Math.exp(r)-Math.exp(-r))/(Math.exp(r)+Math.exp(-r));
	computed = true;
	display(f);
	history(f,"tanh");
}

function pix(f){ // put pi into x
	fillundostack(f);
	if(computed) pushStack(f);
	stack[0] = Math.PI;
	computed = true;
	display(f);
	history(f,"")
}

function menu(a,b){
	fillundostack(f);
	var cs = "";
	var blow=b.toLowerCase();
	var f = document.rpncal;
	for (var i=0; i<m[a].length; i++) {
		if (blow==m[a][i][0]) cs=m[a][i][2];
	}
	if (! cs=="") {
		if(computed) {
			pushStack(f);
		}
		if(a==1){ //in case of conversion
			if (enterpressed == false && computed == false) {
				enterx(f);
		 	}
//			if (enterpressed==false){
//				enterx(document.rpncal);
//			}
			stack[0] = cs;
			computed = true;
			display(f);
			history(f,"")
			if(b==blow){ //in case char is lowercase
				multiply(f); //then multiply with stackx
			}else{
				divide(f); //otherwise divide
			}
		}else{ //no conversion; just display constants
			stack[0] = cs;
			computed = true;
			display(f);
			history(f,"")
		}
	}
}

function converttemp(a,f){ //fahrenheit to celcius
	fillundostack(f);
	//alert(a);
	if(a=="m"){
		stack[0]=((parseFloat(stack[0]))-32)/1.8;
		history(f,"f->c")
	}else if(a=="M") {
		stack[0]=(parseFloat(stack[0])*1.8)+32;
		history(f,"c->f")
	}
	computed = true;
	display(f);
}

function convertpolar(a,f){ //cartesian to polar
	fillundostack(f);
	//alert(a);
	var p=parseFloat(stack[1]); //x-coordinate or r-distance
	var q=parseFloat(stack[0]); //y-coordinate or theta-angle
	if(a=="o"){ //cartesian to polar
		stack[0]=Math.sqrt(Math.pow(p,2)+Math.pow(q,2));
		history(f,"pol")
		stack[1]=stack[0];
		enterpressed = true;
		computed = false;
		if (p==0){
			if (q==0){
				stack[0]=0.0;
			}else if (q>0){
				stack[0]=(Math.PI/2)/dgmode
			}else {
				stack[0]=(3*Math.PI/2)/dgmode
			}
		}else if (p>0){
			if (q>=0){
				stack[0]=Math.atan(q/p)/dgmode;
			}else { //q<0
				stack[0]=Math.atan(q/p);
				stack[0]=(stack[0]+2*Math.PI)/dgmode
			}
		}else { //p<0
			stack[0]=Math.atan(q/p)
			stack[0]=(stack[0]+Math.PI)/dgmode
		}
		history(f,"")
	}else if(a=="O") { //polar to cartesian
		stack[0]=p*Math.cos(q*dgmode);
		history(f,"cart");
		stack[1]=stack[0];
		enterpressed = true;
		computed = false;
		stack[0]=p*Math.sin(q*dgmode);
		history(f,"")
	}
	computed = true;
	display(f);
}

function defineconstants(){
	m[0][0] = ['a','atomic mass constant',1.660538782e-27];
	m[0][1] = ['b','Avogadro constant',6.02214179e23];
 	m[0][2] = ['c','Boltzmann constant',1.3806504e-23];
 	m[0][3] = ['d','electron mass',9.10938215e-31];
 	m[0][4] = ['e','electron volt',1.602176487e-19];
 	m[0][5] = ['f','molar gas constant',8.314472];
 	m[0][6] = ['g','gravitation constant',6.67428e-11];
 	m[0][7] = ['h','Planck constant',6.62606896e-34];
 	m[0][8] = ['i','proton mass',1.672621637e-27];
 	m[0][9] = ['j','speed of light in vacuum',299792458.0];
 	m[0][10] = ['k','Stefan-Boltzmann constant',5.670400e-8];
 
 	m[1][0] = ['','Give capital character to reverse',];
 	m[1][1] = ['a','nautical mile -> meter',1852.0];
 	m[1][2] = ['b','mile -> meter',1609.344];
 	m[1][3] = ['c','yard -> meter',0.9144];
 	m[1][4] = ['d','foot -> meter',0.3048];
 	m[1][5] = ['e','inch -> meter',0.0254];
 	m[1][6] = ['f','light year -> meter',9.4607304725808e15];
 	m[1][7] = ['g','barrel -> liter',158.987295];
 	m[1][8] = ['h','gallon -> liter', 3.785411784];
 	m[1][9] = ['i','lqd ounce -> liter',0.02957353];
 	m[1][10] = ['j','pound, lb -> gram', 453.59237];
 	m[1][11] = ['k','horse power -> watt',745.69987];
 	m[1][12] = ['l','calorie -> joule',4.1868];
 	m[1][13] = ['m','fahrenheit -> celcius',];
 	m[1][14] = ['n','degrees -> radians',0.0174532925199433];
 	m[1][15] = ['o','cartesian (y,x) -> polar (r,&theta)',];

	m[2][0] = ['n','normal','norm'];
	m[2][1] = ['f','financial (fixed, 2)','fin'];
	m[2][2] = ['x','fixed','fxd'];
	m[2][3] = ['s','scientific','sci'];
	m[2][4] = ['e','engineering','eng'];
	m[2][5] = ['0..9','# decimals','dec'];
	m[2][6] = ['d','dot/comma','dec'];

	m[3][0] = ['x','clear x'];
	m[3][1] = ['s','clear stack'];
	m[3][2] = ['m','clear all memory'];
	m[3][3] = ['h','clear history'];
	m[3][4] = ['a','clear all'];
	m[3][5] = ['',''];
	m[3][6] = ['c','select x (Ctrl-C to copy)'];
	m[3][7] = ['e','select memories (Ctrl-C to copy)'];
	m[3][8] = ['i','select history (Ctrl-C to copy)'];

	m[4][0] = ['==>','<b>Compound interest</b>',''];
	m[4][1] = ['','Store 3 out of 4 variables in memory.',''];
	m[4][2] = ['','To calculate unknown variable press:',''];
	m[4][3] = ['7','present value [memory 7]','pv'];
	m[4][4] = ['6','future value [memory 6]','fv'];
	m[4][5] = ['5','number of periods [memory 5]','n'];
	m[4][6] = ['4','compound average growth rate [memory 4]','i%'];
	m[4][7] = ['','',''];
	m[4][8] = ['==>','<b>Annuity</b>',''];
	m[4][9] = ['','Store 3 out of 4 variables in memory.',''];
	m[4][10] = ['','To calculate unknown variable press:',''];
	m[4][11] = ['3','principal [memory 3]','prnc'];
	m[4][12] = ['2','payment per period [memory 2]','pmt'];
	m[4][13] = ['1','number of periods [memory 1]','periods'];
	m[4][14] = ['0','interest per period [memory 0]','i%'];
	m[4][15] = ['','Put payment number in x to calculate:',''];
	m[4][16] = ['v','principal part of payment x','prprt'];
	m[4][17] = ['w','interest part of payment x','%iprt'];
	m[4][18] = ['x','remaining balance after payment x','rbal'];
	m[4][19] = ['y','total interest paid','%itot'];
	m[4][20] = ['z','total payments','tot'];

	m[5][0] = ['e','exponential e^x','e^x'];
	m[5][1] = ['n','ln(x) (natural logarithm)','ln'];
	m[5][2] = ['l','10log(x)','10log'];
	m[5][3] = ['q','square(x)','sqr'];
	m[5][4] = ['r','root(x)','root'];
	m[5][5] = ['','',''];
	m[5][6] = ['s','sin(x)','sin'];
	m[5][7] = ['c','cos(x)','cos'];
	m[5][8] = ['t','tan(x)','tan'];
	m[5][9] = ['i','arcsin(x)','asin'];
	m[5][10] = ['o','arccos(x)','acos'];
	m[5][11] = ['a','arctan(x)','atan'];
	m[5][12] = ['x','sinh(x)','sinh'];
	m[5][13] = ['y','cosh(x)','cosh'];
	m[5][14] = ['z','tanh(x)','tanh'];
	m[5][15] = ['','',''];
	m[5][16] = ['m','modulo y%x','y%x'];
	m[5][17] = ['g','greatest common divisor (y,x)','gcd'];
	m[5][18] = ['h','least common multiple (y,x)','lcm'];

	m[6][0] = ['','Put data into Memory X',''];
	m[6][1] = ['a','average memories','avg'];
	m[6][2] = ['d','standard deviation memories','dev'];
	m[6][3] = ['s','sum all memories','sum'];
	m[6][4] = ['z','square sum all memories','sumx2'];
	m[6][5] = ['','Put data into Memory X and Memory Y',''];
	m[6][6] = ['v','covariance X Y','cov'];
	m[6][7] = ['o','correlation X Y','cor'];
	m[6][8] = ['','',''];
	m[6][9] = ['c','combinations (y,x)','comb'];
	m[6][10] = ['p','permutations (y,x)','perm'];
	m[6][11] = ['r','random number between 0 and 1','rnd'];
	m[6][12] = ['i','random integer between y and x','rndn'];

	m[7][0] = ['','Put CFs into Memory X or Y (initial CF in mem 0)',''];
	m[7][1] = ['n','net present value memories (x = interest)','npv'];
	m[7][2] = ['i','internal rate of return memories','irr'];
	m[7][3] = ['','',''];

}             
              
function onebyx(f) {
	var tmpvar;  
	fillundostack(f);
	tmpvar = parseFloat(stack[0]);
	if (isNaN(tmpvar) || tmpvar == 0){
		stack[0] =Number.POSITIVE_INFINITY;
		computed = false;
	}else{       
		stack[0] = 1 / tmpvar;
		computed = true;
	}
	display(f);
	history(f,"1/x")
}

function swapxy(f) {
	var tmpvar;
	fillundostack(f);
	tmpvar = stack[0];
	if(isNaN(tmpvar) || tmpvar == "" ){
		tmpvar="0";
	}
	stack[0] = stack[1];
	stack[1] = tmpvar;
	computed = true;
	display(f);
	history(f,"")
}

function add(f) {
	fillundostack(f);
	stack[0] = parseFloat(stack[1]) + parseFloat(stack[0]);
	computed = true;
	popStack(f);
	history(f,"+");
}

function subtract(f) {
	fillundostack(f);
	stack[0] = stack[1] - stack[0];
	computed = true;
	popStack(f);
	history(f,"-")
}

function multiply(f) {
	fillundostack(f);
	stack[0] = stack[1] * stack[0];
	computed = true;
	popStack(f);
	history(f,"*")
}

function divide(f) {
	fillundostack(f);
	var divisor = parseFloat(stack[0]);
	if(divisor == 0) {
		stack[0] = Number.POSITIVE_INFINITY;
	}else{
		stack[0] = stack[1] / divisor;
	}
	computed = true;
	popStack(f);
	history(f,"/")
}

function mod(f) { //modulo (division remainder)
	fillundostack(f);
	var divisor = parseFloat(stack[0]);
	if(divisor == 0) {
		stack[0] = Number.POSITIVE_INFINITY;
	}else{
		stack[0] = stack[1] % divisor;
	}
	computed = true;
	popStack(f);
	history(f,"mod")
}

function gcd(f){ //greatest common divisor
	fillundostack(f);
	var a=parseFloat(stack[0]);
	var b=parseFloat(stack[1]);
	if (Math.round(a)==a && Math.round(b)==b && a>=0 && b>=0){
		stack[0] = gcd2(a,b)
	}else {
		stack[0]=Number.NaN;
	}
	computed = true;
	popStack(f);
	history(f,"gcd");
}

function lcm(f){ //least common multiple
	fillundostack(f);
	var a=parseFloat(stack[0]);
	var b=parseFloat(stack[1]);
	if (Math.round(a)==a && Math.round(b)==b && a>=0 && b>=0){
		stack[0] = (a*b)/gcd2(a,b)
	}else {
		stack[0]=Number.NaN;
	}
	computed = true;
	popStack(f);
	history(f,"lcm");
}

function gcd2(a,b){
	if (b == 0) {return a} //Euclidean algorithm
	else {return gcd2(b,a%b)}
}

function changeSign(f) {
	fillundostack(f);
	// we could use stack[0] = 0 - stack[0] but
	// we might get rounding errors
	stack[0]+=" ";
	if(stack[0].substring(0, 1) == "-"){
		//alert (stack[0]);
		stack[0] = parseFloat(stack[0].substring(1, stack[0].length-1));
	}else {
		stack[0] = parseFloat("-" + stack[0]);
	}
	display(f);
	history(f,"")
}

// keyboard interface

// handle the differences between MSIE and Netscape
function getkey(e) {
if (window.event)
	return window.event.keyCode;
else if (e)
	return e.which;
else
	return null;
}

function checkKeycode(e) {
var keycode;
if (window.event) keycode = window.event.keyCode;
else if (e) keycode = e.which;
//keychar = String.fromCharCode(keycode);
//alert("keycode: " + keycode + " " + keychar);
//if (key>1){
//return(keycode);
//}
}

function chkkey2(e) { //*************************************************************
	//is triggered when 'onkeydown'
	var key, keychar;
	var f=document.rpncal;
	//document.onkeydown = checkKeycode
	key = getkey(e);
	//alert(key);
	if (key == 38){
		// up arrow
		stackpointer += 1;
		display(f);
	}else if (key == 40){
		// down arrow
		stackpointer -= 1;
		if (stackpointer <0) stackpointer = 0;
		display(f);
	}else if (key == 27){
		// escape
		escape(f);
	}else if (key == 32){
		// space bar
		popStackDisplay(f);
	}else{
		stackpointer=0;
	}
}

function chkkey(e) { //*************************************************************
	//is triggered when 'onkeypress'
	var key, keychar;
	var f=document.rpncal;
	//document.onkeydown = checkKeycode
	key = getkey(e);
//	key = e;
	//alert(key);
	if (key == null) return true;
	if (key == 13){
		// enter pressed
		enterx(f);
	}else if (key == 8){
		// backspace
			if (e.preventDefault) {
				e.preventDefault();
			}
		deleteChar(f); //only works for FF
	}else{
		// get character
		keychar = String.fromCharCode(key);
		//keychar = keychar.toLowerCase();
		//alert(keychar);

		if (shiftf == true){  //fin:annuity *************************************************
			if (keychar == "3"){
				principal(f);
			}else if (keychar == "7"){
				presentvalue(f);
			}else if (keychar == "6"){
				futurevalue(f);
			}else if (keychar == "5"){
				periodscagr(f);
			}else if (keychar == "4"){
				cagr(f);
			}else if (keychar == "2"){
				pmt(f);
			}else if (keychar == "1"){
				periods(f);
			}else if (keychar == "0"){
				interest(f);
			}else if (keychar == "v"){
				principalpart(f);
			}else if (keychar == "w"){
				interestpart(f);
			}else if (keychar == "x"){
				remainingprincipal(f);
			}else if (keychar == "y"){
				totalinterest(f);
			}else if (keychar == "z"){
				totalpayments(f);
			}shiftf = false;
			showmenushift("financial",0);
		}else if (shiftl == true){  //cashflow *************************************************
			if (keychar == "n" ){
				npv(f);
			}else if (keychar == "i"){
				irr(f);
			}shiftl = false;
			showmenushift("cashflow",0);
		}else if (shiftt == true){  //statistics *************************************************
			if (keychar == "p"){
				permutations(f);
			}else if (keychar == "c"){
				combinations(f);
			}else if (keychar == "r"){
				random(f);
			}else if (keychar == "i"){
				randominteger(f);
			}else if (keychar == "a"){
				avg(f);
			}else if (keychar == "s"){
				sum(f);
			}else if (keychar == "z"){
				sumsquares(f);
			}else if (keychar == "d"){
				dev(f);
			}else if (keychar == "v"){
				covariance(f);
			}else if (keychar == "o"){
				correlation(f);
			}shiftt = false;
			showmenushift("statistics",0);
//		}else if (shifth == true){  //help ************************************************
//			if (keychar == "h"){
//				rpncalchelp(f);
//			}shifth = false;
//			showhide("help",0);
		}else if (shifta == true){  //algebra *********************************************
			if (keychar == "q" ){
				square(f);
			}else if (keychar == "e"){
				expx(f);
			}else if (keychar == "r"){
				sqrtx(f);
			}else if (keychar == "n"){
				lnx(f);
			}else if (keychar == "l"){
				log10(f);
			}else if (keychar == "s"){
				sin(f);
			}else if (keychar == "c"){
				cos(f);
			}else if (keychar == "t"){
				tan(f);
			}else if (keychar == "i"){
				asin(f);
			}else if (keychar == "o"){
				acos(f);
			}else if (keychar == "a"){
				atan(f);
			}else if (keychar == "x"){
				sinh(f);
			}else if (keychar == "y"){
				cosh(f);
			}else if (keychar == "z"){
				tanh(f);
			}else if (keychar == "m"){
				mod(f);
			}else if (keychar == "g"){
				gcd(f);
			}else if (keychar == "h"){
				lcm(f);
			}shifta = false;
			showmenushift("algebra",0);
		}else if (shifts == true){  //store in memory *************************************
			if ((("0123456789").indexOf(keychar) > -1)){
				store(f,keychar);
			}else if (keychar == "a"){
				store(f,10);
			}else if (keychar == "b"){
				store(f,11);
			}else if (keychar == "c"){
				store(f,12);
			}else if (keychar == "d"){
				store(f,13);
			}else if (keychar == "e"){
				store(f,14);
			}else if (keychar == "f"){
				store(f,15);
			}else if (keychar == "g"){
				store(f,16);
			}else if (keychar == "h"){
				store(f,17);
			}else if (keychar == "i"){
				store(f,18);
			}else if (keychar == "j"){
				store(f,19);
			}
			shifts = false;
			f.sf.style.backgroundColor = '#D6D6D6';
			f.sf.value = "";
		}else if (shiftr == true){  //recall from memory **********************************
			if ((("0123456789").indexOf(keychar) > -1)){
				recall(f,keychar);
			}else if (keychar == "a"){
				recall(f,10);
			}else if (keychar == "b"){
				recall(f,11);
			}else if (keychar == "c"){
				recall(f,12);
			}else if (keychar == "d"){
				recall(f,13);
			}else if (keychar == "e"){
				recall(f,14);
			}else if (keychar == "f"){
				recall(f,15);
			}else if (keychar == "g"){
				recall(f,16);
			}else if (keychar == "h"){
				recall(f,17);
			}else if (keychar == "i"){
				recall(f,18);
			}else if (keychar == "j"){
				recall(f,19);
			}
			shiftr = false;
			f.sf.style.backgroundColor = '#D6D6D6';
			f.sf.value = "";
		}else if (shiftc == true){  //clear copy ******************************************
			if ((("0123456789").indexOf(keychar) > -1)){
				clearm(f,keychar);
			}else if (keychar == m[3][2][0]){
				clrmem(f);
			}else if (keychar == m[3][0][0] ){
				cx(f);
			}else if (keychar == m[3][1][0]){
				clrstack(f);
			}else if (keychar == m[3][3][0]){
				clrhistory(f);
			}else if (keychar == m[3][4][0]){
				clrstack(f);
				clrmem(f);
				clrhistory(f);
			}else if (keychar == m[3][6][0]){
			  f.display.focus();
        f.display.select();
//				copy(f.display.value);
			}else if (keychar == m[3][7][0]){
			  f.memory.focus();
        f.memory.select();
//				copymem();
			}else if (keychar == m[3][8][0]){
			  f.history.focus();
        f.history.select();
//				copy(f.history.value);
			}shiftc = false;
			showmenushift("clear",0);
		}else if (shifto == true){  //notation ********************************************
			if (("0123456789").indexOf(keychar) > -1){
				rnd = keychar;
				createCookie("rnd",keychar,365);
				display(f);
				f.numpres.value = not+" "+rnd;
			}else if (keychar == "m"){
				rnd = 17;//maximum
				createCookie("rnd",17,365);
				display(f);
				f.numpres.value = not+" "+rnd;
			}else if (keychar == m[2][0][0]){ //normal
				not = m[2][0][2];
				rnd = 15;
				createCookie("not",m[2][0][2],365);
				createCookie("rnd",15,365);
				display(f);
				f.numpres.value = not;
			}else if (keychar == m[2][3][0]){ //scientific
				not = m[2][3][2];
				createCookie("not",m[2][3][2],365);
				display(f);
				f.numpres.value = not+" "+rnd;
			}else if (keychar == m[2][4][0]){ //engineering
				not = m[2][4][2];
				createCookie("not",m[2][4][2],365);
				display(f);
				f.numpres.value = not+" "+rnd;
			}else if (keychar == m[2][2][0]){ //fixed
				not = m[2][2][2];
				createCookie("not",m[2][2][2],365);
				display(f);
				f.numpres.value = not+" "+rnd;
			}else if (keychar == m[2][1][0]){ //financial: fixed 2
				not = m[2][2][2];
				rnd = 2;
				createCookie("not",m[2][1][2],365);
				display(f);
				f.numpres.value = "fin";
			}else if (keychar == m[2][6][0] ){
				if( comma == 1 ) {
					comma = 0; //dot
					createCookie('comma','d',365);
					historytext=historytext.replace(/,/g,".");
					f.memory.value=f.memory.value.replace(/,/g,".");
				} else {
					comma = 1; //comma
					createCookie('comma','c',365);
					historytext=historytext.replace(/\./g,",");
					f.memory.value=f.memory.value.replace(/\./g,",");
				}
				f.history.value=historytext;
				display(f);
	 		}
			shifto = false;
			showmenushift("notation",0);
		}else if (shiftg == true){  //physical constants **********************************
			showmenushift("constants",0);
	 		if ((("abcdefghijklmnopqrstuvwxyz").indexOf(keychar) > -1)){
				menu(0,keychar);
	 		}
			shiftg = false;
		}else if (shiftv == true){  //convert units ***************************************
			showmenushift("menuv",0);
			if (keychar=="m"||keychar=="M"){
				converttemp(keychar,f);
			}else if (keychar=="o"||keychar=="O"){
				convertpolar(keychar,f);
	 		}else if((("abcdefghijklnpqrstuvwxyzABCDEFGHIJKLNPQRSTUVWXYZ").indexOf(keychar)>-1)){
				menu(1,keychar);
	 		}
			shiftv = false;
		}else {  //no shifts active *******************************************************
			if ((("0123456789.e").indexOf(keychar) > -1)){
				addChar(f,keychar);
			}else if (keychar == "," ){
				keychar=".";
				addChar(f,keychar);
			}else if (keychar == "f" ){
				shiftf = true;
				mmode=2;
				displaymem(f);
				showmenushift("financial",1);
			}else if (keychar == "g" ){
				shiftg = true;
				showmenushift("constants",1);
			}else if (keychar == "h" ){
//				shifth = true;
//				showhide("help",1);
				rpncalchelp(f);
			}else if (keychar == "a" ){
				shifta = true;
				showmenushift("algebra",1);
			}else if (keychar == "t" ){
				shiftt = true;
				showmenushift("statistics",1);
			}else if (keychar == "s" ){
				shifts = true;
				f.sf.style.backgroundColor = '#FF3333';
				f.sf.value="store";
			}else if (keychar == "r" ){
				shiftr = true;
				f.sf.style.backgroundColor = '#66FF33';
				f.sf.value="recall";
			}else if (keychar == "c" ){
				shiftc = true;
				showmenushift("clear",1);
			}else if (keychar == "o" ){
				shifto = true;
				showmenushift("notation",1);
			}else if (keychar == "l" ){
				shiftl = true;
				showmenushift("cashflow",1);
			}else if (keychar == "v" ){
				shiftv = true;
				showmenushift("menuv",1);
			}else if (keychar == "!"){
				factx(f);
			}else if (keychar == "E" ){
				addChar(f, 'e-');
			}else if (keychar == "^"|| keychar=="y" ){
				powxy(f);
			}else if (keychar == "d" ){  //deg - rad
				if( dgmode == "1.0" ) {
					f.deg.value="deg";
						dgmode = Math.PI/180.0;
					createCookie('deg','deg',365);
				} else {
					f.deg.value="rad";
						dgmode = 1.0;
					createCookie('deg','rad',365);
				}
			}else if (keychar == "u" ){
				undoall(f);
			}else if (keychar == "m" ){
				storefree(f);
			}else if (keychar == "x" ){
				lastx(f);
			}else if (keychar == "n" ){
				changeSign(f);
			}else if (keychar == "w" ){
				swapxy(f);
			}else if (keychar == "i"){
				onebyx(f);
			}else if (keychar == "-" ){
				subtract(f);
			}else if (keychar == "+" ){
				add(f);
			}else if (keychar == "*" ){
				multiply(f);
			}else if (keychar == "q" ){
				square(f);
			}else if (keychar == "/" ){
				divide(f);
			}else if (keychar == "r" ){
				sqrtx(f);
			}else if (keychar == "p" ){
				pix(f);
			}else if (keychar == "z" ){ //mem mode
				memmode(f);
			}
		}
	}
}


