////////////////////////////////////////////////////////////////////////////
//
//                       Copyright (c) 2005-2006
//                       Future Team Aps 
//                       Denmark
//
//                       All Rights Reserved
//
//   This source file is subject to the terms and conditions of the
//   Future Team Software License Agreement which restricts the manner
//   in which it may be used.
//   Mail: hve@hvks.com
//
/////////////////////////////////////////////////////////////////////////////
//
// Module name     :   newton.js
// Module ID Nbr   :   
// Description     :   Javascript for finding root in a polynomial
// --------------------------------------------------------------------------
// Change Record   :   
//
// Version	Author/Date		Description of changes
// -------  -------------	----------------------
// 01.01	HVE/05-Oct-14	Initial release
// 01.02	HVE/06-Dec-08	sign and argument split didn't work under Firefox. A correting function removeEmpties() remedy the Firefox problem
// 01.03	HVE/06-Dec-14   Change to use the Polynomial class 
// 01.04	HVE/2009-Nov-7	Add convergence graph and function value graph
// End of Change Record
//
/////////////////////////////////////////////////////////////////////////////

// Function argument checkking 
function check(args,func_name)
 {var actual=args.length; var expected=args.callee.length;
  if(actual!=expected) throw Error("Wrong number of arguments in function: "+func_name+"; Expected: "+expected+"; Actually passed "+actual); }
  
// Print the polynominal to be solved
function print_polynomial(form,coeff)
	{ 
	form.eqField.value+="\nFor the "+(coeff.isReal()?"real":"complex")+" Polynomial:\n";
	form.eqField.value+=coeff.toString()+"\n";
	}

function calc_q_and_fv(coeff)
	{var i, j, it;
	it=coeff.it;
	qt=new Array;
	fvt=new Array;
	for(i=0;i<it.length;i++)
		{var trail=it[i];
		if(trail!=undefined&&trail.length>0) 
			{
			qt[i]=new Array; fvt[i]=new Array;
			if(trail instanceof Complex) ;
			else
			for(var j=0;j<trail.length;j++) 
				{
				fvt[i][j]=Math.sqrt(coeff.value(trail[j]).norm());
				if(j>1) qt[i][j]=Math.abs(Math.log(Complex.sub(trail[j],trail[j-1]).abs())/Math.log(Complex.sub(trail[j-1],trail[j-2]).abs()));
				}
			}
		}
	coeff.qt=qt;
	coeff.fvt=fvt;
	}
			
// Print the solutions
function print_solution(form,solutions)
	{ var i, j;
	form.eqField.value+="The Solutions are:\n";
	for(j=1,i=solutions.length-1;i>0;i--,j++)
		{ 
		form.eqField.value+="X"+j+"="+solutions[i].toStringShort();
		form.eqField.value+="\n";
		}
	}

// Print the solutions
function print_iteration_trail(form,coeff)
	{ var i, it;
	it=coeff.it; 
	form.eqField.value+="\nIterations trail: (q is the Convergence power for each step of the iteration.)\n";
	for(i=0;i<it.length;i++)
		{var trail=it[i]; 
		if(trail!=undefined&&trail.length>0) 
			{
			if(trail instanceof Complex) form.eqField.value+="["+i+"]="+trail.toStringShort()+"\n";
			else
			for(var j=0;j<trail.length;j++) 
				{form.eqField.value+="["+i+"]["+j+"]="+trail[j].toStringShort();
				form.eqField.value+=" F(x)="+coeff.fvt[i][j].toExponential(1);
				if(j>1) form.eqField.value+=" q="+coeff.qt[i][j].toFixed(2);
				form.eqField.value+="\n"; }
			}
		}
	}

// Graph the solutions
function graph_solution(jg,solutions,form,it)
	{
	var i, co;
	var max_x, max_y, min_x, min_y, dx, dy;

	jg.clear();
	jg.setPrintable(true);
	max_x=max_y=-Infinity;
	min_x=min_y=+Infinity;
	for(i=1;i<solutions.length;i++)
		{ 
		if(max_x<solutions[i].real()) max_x=solutions[i].real();
		if(min_x>solutions[i].real()) min_x=solutions[i].real();
		if(max_y<solutions[i].imag()) max_y=solutions[i].imag();
		if(min_y>solutions[i].imag()) min_y=solutions[i].imag();
		}
	max_x=Math.max(max_x,max_y); min_x=Math.min(min_x,min_y);
	max_y=max_x; min_y=min_x;		
	max_x=Math.ceil(max_x); min_x=Math.floor(min_x);
	max_y=Math.ceil(max_y); min_y=Math.floor(min_y);
	dx=max_x-min_x; dy=max_y-min_y;
	if(dx==0&&dy==0) {max_x=+1, min_x=-1; max_y=1; min_y=-1; dx=2; dy=2 }
//	alert("("+min_x+","+min_y+");("+max_x+","+max_y+") dx="+dx+" dy="+dy);
	co=new Coordinate(jg,400,400,min_x, min_y, max_x, max_y, Math.ceil(dx/10), Math.ceil(dy/10),false,false);
	co.addTitle("Roots for Polynominal");
	for(i=1;i<solutions.length;i++)
		{ 
		jg.setStroke(2);
		co.drawPoint(solutions[i].real(),solutions[i].imag());
		jg.setStroke(1);
		}
// Draw iterations step		
	if(it!=undefined)
		{
		co.addLegend("#ff0000","Iteration trail");
		for(var i=0;i<it.length;i++)
			{var trail=it[i];
			if(trail!=undefined&&trail.length>0) 
				{var xptrs=new Array, yptrs=new Array;
				if(trail instanceof Array)
					{for(var j=0;j<trail.length;j++) 
						{xptrs[j]=trail[j].real(); yptrs[j]=trail[j].imag();}
					jg.setColor("#ff0000");
					jg.setStroke(1);
					co.fillCircle(xptrs[0],yptrs[0],2);
					co.drawPolyline(xptrs,yptrs); 
					jg.setColor("#000000");
					}
				}
			}
		}
	co.paint();
	}

// Graph the solutions
function graph_convergence(jg,form,qt)
	{
	var i, j, co, info;
	var max_x, max_y, min_x, min_y, dx, dy;
	var xptrs=new Array;
	var yptrs=new Array;
	var color=new Array;
	
	jg.clear(); 
	for(var i=0, max_x=0;i<qt.length;i++) if(qt[i]!=undefined) max_x=Math.max(max_x,qt[i].length+2); 
	for(var i=0, max_y=0;i<qt.length;i++) if(qt[i]!=undefined) for(var j=0;j<qt[i].length;j++) if(qt[i][j]!=undefined) max_y=Math.max(max_y,qt[i][j]);
	min_x=0; min_y=0; 
	//alert("init xmin="+min_x+" xmax="+max_x+" ymin="+min_y+" ymax="+max_y);
	dx=Number(max_x-min_x);
	max=max_x; mix=min_x;
	//max_x=Math.max(max_x,max_y); min_x=Math.min(min_x,min_y);
	//max_y=max_x; min_y=min_x;		
	max_x=Math.ceil(max_x); min_x=Math.floor(min_x);
	max_y=Math.ceil(max_y); min_y=Math.floor(min_y);
	dx=max_x-min_x; dy=max_y-min_y;
	if(dx==0&&dy==0) {max_x=+1, min_x=-1; max_y=1; min_y=-1; dx=2; dy=2 }
	co=new Coordinate(jg,600,400,Number(min_x), Number(min_y), Number(max_x), Number(max_y), Math.ceil(dx/10), Math.ceil(dy/10),false,false);
	color[0]="#ff0000"; color[1]="#00ff00"; color[2]="#0000ff"; color[3]="#ffff00";
	color[4]="#ff00ff"; color[5]="#00ffff"; color[6]="#0fff00"; color[7]="#00fff0";
	//alert("Before loop xmin="+min_x+" xmax="+max_x+" ymin="+min_y+" ymax="+max_y+" dx="+dx+" dy="+dy);
	var info=false;
	for(var i=0;i<qt.length;i++)
		{var obj=qt[i];	
		if(obj==undefined) continue;
		if(obj[2]==undefined) continue;
		co.addLegend(color[i%8],"X"+Number(i+1)+" root"); xptrs.length=0; yptrs.length=0;
		for( var k=2,j=0;k<obj.length;j++,k++){xptrs[j]=k; yptrs[j]=obj[k]; info=true; /*alert("xp="+xptrs[j]+" yptrs="+yptrs[j]);*/}
		jg.setColor(color[i%8]);
		jg.setStroke(2);
		co.drawPolyline(xptrs,yptrs); 
		jg.setStroke(1);jg.setColor("#000000");
		}
	if(info==false) co.addTitle("No Convergence information available");
	else
		{
		co.addTitle("Convergence power per iteration step");
		co.addXname("Iterations"); co.addYname("q");
		}
	co.paint();
	//alert("After paint");
	}
	
	
// Graph the solutions
function graph_fvalue(jg,form,fvt,eq)
	{
	var i, j, co;
	var max_x, max_y, min_x, min_y, dx, dy;
	var resolve, ilow, ihigh;
	var xptrs=new Array;
	var yptrs=new Array;
	var color=new Array;
	
	jg.clear(); 
	for(var i=0, max_x=0;i<fvt.length;i++) if(fvt[i]!=undefined) max_x=Math.max(max_x,fvt[i].length+1); 
	for(var i=0, max_y=0;i<fvt.length;i++) if(fvt[i]!=undefined) for(var j=0;j<fvt[i].length;j++) if(fvt[i][j]!=undefined) max_y=Math.max(max_y,fvt[i][j]);
	for(var i=0, min_y=Number.MAX_VALUE;i<fvt.length;i++) if(fvt[i]!=undefined) for(var j=0;j<fvt[i].length;j++) min_y=Math.min(min_y,Math.abs(fvt[i][j]));
	//if(max_x==0) return; // Nothing to show
	min_x=0; if(min_y==0) min_y=1E-20; 
	dx=Number(max_x-min_x);
	max=max_x; mix=min_x;
//	alert("Linear "+max_y+":"+min_y);		
	max_y=Math.LOG10E*Math.log(max_y); min_y=Math.LOG10E*Math.log(min_y);
//	alert("log "+max_y+":"+min_y);		
	max_x=Math.ceil(max_x); min_x=Math.floor(min_x);
	max_y=Math.ceil(max_y); min_y=Math.floor(min_y);
	dx=max_x-min_x; dy=max_y-min_y;
	if(dx==0&&dy==0) {max_x=+1, min_x=-1; max_y=1; min_y=-1; dx=2; dy=2 }
	co=new Coordinate(jg,600,400,Number(min_x), Number(min_y), Number(max_x), Number(max_y), Math.ceil(dx/10), Math.ceil(dy/10),false,true);
	color[0]="#ff0000"; color[1]="#00ff00"; color[2]="#0000ff"; color[3]="#ffff00";
	color[4]="#ff00ff"; color[5]="#00ffff"; color[6]="#0fff00"; color[7]="#00fff0";
	//alert("Before loop xmin="+min_x+" xmax="+max_x+" ymin="+min_y+" ymax="+max_y+" dx="+dx+" dy="+dy);
	var info=false;
	for(var i=0;i<fvt.length;i++)
		{var obj=fvt[i]; var col=i%8;
		if(obj==undefined) continue;
		if(obj[0]==undefined) continue;
		co.addLegend(color[col],"X"+Number(i+1)+" root"); xptrs.length=0; yptrs.length=0;
		for(var k=0,j=0; k<obj.length;j++,k++){xptrs[j]=k; yptrs[j]=Math.max(Math.abs(obj[k]),1E-19); info=true; /* alert("xp="+xptrs[j]+" yptrs="+yptrs[j]); */}
		jg.setColor(color[col]);
		jg.setStroke(2);
		co.drawPolyline(xptrs,yptrs); 
		jg.setStroke(1);jg.setColor("#000000");
		}
	if(info==false) co.addTitle("No Function value information available");
	else
		{co.addTitle("F(x) of "+eq);
		co.addXname("iterations"); co.addYname("F(x)");
		}
	co.paint();
	}

var jg = new jsGraphics("theCanvas");
var jg2 = new jsGraphics("theCanvas2");
var jg3 = new jsGraphics("theCanvas3");

// Evaluate and parse the equation. If OK then soilve the equation and print the result
function solveit(form)
	{
	var z=new Complex;
	var coeff;
	// Test for complex coefficients characters
	
	eq=form.equation.value;
	try {
 	coeff=parsePolynomial(eq);
 	} catch(e) {if(e instanceof Error) form.eqField.value+=e.name+":"+e.message;}
	if(coeff!=undefined&&coeff instanceof Polynomial&&coeff.degree()>0)
		{var t0, t1;
		print_polynomial(form,coeff);
		solutions = new Array;
		t0=new Date();
		solutions=coeff.zeros(form.verboseField.checked,form.Composite.checked);
		t1=new Date();
		form.eqField.value+=solutions[0];  // Any verbose messages?
		print_solution(form,solutions);
		form.eqField.value += "Time used: "+(t1.getTime()-t0.getTime())+" msec.\n";
		calc_q_and_fv(coeff);
		if(form.IterationTrail.checked)
			{
			print_iteration_trail(form,coeff);
			graph_solution(jg,solutions,form,coeff.it);
			}
		else graph_solution(jg,solutions,form,undefined);
		graph_convergence(jg3,form,coeff.qt);
		//alert("after convergence");
		graph_fvalue(jg2,form,coeff.fvt,eq);
		//alert("after function value");
		}
	else
		form.eqField.value+="Error: Polynomial '"+ coeff.toString()+"' is a constant and has no root";
	}
	

function reset_textarea(form)
	{
	form.eqField.value="";
	jg.clear();
	jg2.clear();
	jg3.clear();
	}
function save_textarea(form)
	{
//	form.eqField.value.print();
	}

function print_textarea(form)
	{
	var printing=form.eqField.value;
	newWindow=null;
	newWindow=window.open("","newWin","toolbar=yes,location=yes,menubar,status,toolbar,scrollbars=yes,width=400,height=400,resizable=yes");
    newWindow.document.write('<html>\r\n\r\n');
    newWindow.document.write('<head>\r\n');
    newWindow.document.write('<meta http-equiv="Content-Language" content="en-us">\r\n');
	newWindow.document.write('<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">\r\n');    
    newWindow.document.write('<title>Web based Polynomial root finder result</title>\r\n');
    newWindow.document.write('</head>\r\n\r\n');
    newWindow.document.write('<body>\r\n');
//    newWindow.document.write('<form><input type="button" value="Print This Page" onClick="javascript:window.print();" /></form>');
    newWindow.document.write('<a onlick="javascript:window.print();" href="javascript:window.print();">Print This Page</a>');
    newWindow.document.write('\t');
    newWindow.document.write('<a href="javascript:window.close()">\tClose This Page </a>');
    newWindow.document.write('<pre>\r\n');
    newWindow.document.write(printing);
    newWindow.document.write('</pre>\r\n');
    newWindow.document.write('</body>\r\n\r\n');
    newWindow.document.write('</html>');
	}


