
var abl_z=new Array(),abl_n=new Array(),abl_=new Array(),ns=new Array(),nsk=new Array(),TKD="";
var g=0,psym=false,asym=false;

function test()
{
	//x^3 - 4·x^2 + 6·x - 4
//-16/1197·x^4 - 64/1729·x³ + 17945/15561·x² - 1600/15561

	var kz=new Array(0,-1600,17945,-64,-16);
	var kn=new Array(1,15561,15561,1729,1197);

	//for(var i=0;i<8;i++){kz[i]=Math.random()*4-2;}

	kurvendiskussion(kz,kn);

}

function kurvendiskussion(kz,kn)
{
	var i,j,jj,k,y,nkn=(kn==null),SP=new Array(),o=false;
	g=grad(kz);
	if(nkn){kn=new Array(g+1);for(i=0;i<=g;i++)kn[i]=1;}
	abl_z=new Array(g+1);abl_n=new Array(g+1);abl_=new Array(g+1);
	abl_z[0]=new Array();abl_n[0]=new Array();abl_[0]=new Array();for(i=0;i<g+1;i++){abl_z[0][i]=kz[i];abl_n[0][i]=kn[i];abl_[0][i]=kz[i]/kn[i];}
	for(i=0;(i<=g)||(i<3);i++){abl_z[i+1]=new Array();abl_n[i+1]=new Array();abl_[i+1]=new Array();DIFF(abl_z[i],abl_n[i],abl_z[i+1],abl_n[i+1],abl_[i+1]);}
	
	for(i=0;(i<g);i++)
	{
		ns[i]=nullstellen(abl_[i],abl_[i+1],abl_z,abl_n[i],abl_z[i+1],abl_n[i+1]);
		if(quicksort!=null)quicksort(ns[i]);
		for(j=ns[i].length-1;j>0;j--)
		{
			if(ns[i][j]==ns[i][j-1])
			{
				for(k=j;k<ns[i].length-1;k++)ns[i][k]=ns[i][k+1];
				ns[i].pop();
			}
		}
	}
	for(i=g;i<3;i++)ns[i]=new Array();
	//alert(ns.join("\n\n"));

	for(i=0;i<ns[1].length;i++){if(Math.abs(horner(abl_[0],ns[1][i]))<1e-8){if(!drin(ns[0],ns[1][i])){ns[0][ns[0].length]=ns[1][i];quicksort(ns[0]);}}}

	psym=asym=true;
	for(i=0;i<=g;i+=2)if(kz[i]!=0){psym=false;break;}
	for(i=1;i<=g;i+=2)if(kz[i]!=0){asym=false;break;}
	o=false;
	TKD="Symmetrie: \n    ";
	if(g>3)
	{
		if(asym){TKD+="achsensymmetrisch zur y-Achse";o=true;}
		if(psym){TKD+="punktsymmetrisch zum Ursprung";o=true;}
		for(j=0;(j<ns[1+(g%2)].length)&&!o;j++)
		{
			x=ns[1+(g%2)][j];
			y=horner(abl_[0],x);
			for(i=1;i<4;i++)
			{
				//alert((horner(abl_[0],x-i/g))+"\n"+(horner(abl_[0],x+i/g)));
				if((g%2)==1)if(Math.abs(2*y-horner(abl_[0],x-i/g)-horner(abl_[0],x+i/g))>1e-8)break;
				if((g%2)==0)if(Math.abs(horner(abl_[0],x-i/g)-horner(abl_[0],x+i/g))>1e-8)break;
			}
			if((i==4)&&((g%2)==1)){TKD+="punktsymmetrisch zu ( "+R(x)+" | "+R(y)+" )";o=true;break;}
			if((i==4)&&((g%2)==0)){TKD+="achsensymmetrisch zu x = "+R(x);o=true;break;}
		}
	}
	else if(g==3)
	{
		x=ns[2][0];y=horner(abl_[0],x);
		for(i=1;i<4;i++){if(Math.abs(2*y-horner(abl_[0],x-i)-horner(abl_[0],x+i))>1e-12)break;}
		if(i==4){TKD+="punktsymmetrisch zu ( "+R(x)+" | "+R(y)+" )";o=true;}
	}
	else if(g==2)
	{
		TKD+="achsensymmetrisch zu x = "+R(ns[1][0]);o=true;
	}
	else if(g==1)
	{
		if(abl_[0][1]!=0){TKD+="punktsymmetrisch zu jedem Punkt der Gerade";o=true;}
		else{TKD+="punktsymmetrisch zu jedem Punkt der Gerade\n    und achsensymmetrisch zu jeder Parallelen zur y-Achse";o=true;}
	}
	if(!o)TKD+="keine erkannt";o=false;
	TKD+="\n\nAchsenschnittpunkte:\n    mit y-Achse: "+R(abl_[0][0]);
	TKD+="\n    mit x-Achse (Nullstellen):";
	if(g>1)
	{
		TKD+="\n        { ";
		for(i=0;i<ns[0].length;i++)if(ns[0][i]!=null)TKD+=R(ns[0][i])+((i<ns[0].length-1)?" ;  ":"");
		TKD+=" }\n\n";
		TKD=TKD.replace(/\n        \{  \}/," keine");  
	}
	else
	{
		if((abl_[0][0]==0)&&(abl_[0][1]==0))TKD+="  R (Graph verläuft auf x-Achse)\n\n";
		else if(abl_[0][1]!=0)TKD+=" bei x = "+R(ns[0][0])  +"\n\n";
		else TKD+=" keine (Gerade ist parallel zur x-Achse)\n\n";
	}
	TKD+="Extremwert(e):\n";SP=new Array();
	for(i=0;i<ns[1].length;i++)
	{
		if(ns[1][i]==null)continue;
		y=(horner(abl_[2],ns[1][i]));
		if(Math.abs(y)<1e-12)
		{
			var ii;
			for(ii=3;ii<=g;ii++)if(Math.abs(horner(abl_[ii],ns[1][i]))>1e-12)break;
			if((ii<=g)&&((ii%2)==0))
			{TKD+="    lokales "+((y<0)?"Maximum":"Minimum")+" bei  ( "+R(ns[1][i])+" | "+R(horner(abl_[0],ns[1][i]))+" )\n";o=true;}
			else
			SP[SP.length]="( "+R(ns[1][i])+" | "+R(horner(abl_[0],ns[1][i]))+" )";
		}
		else 
		{TKD+="    lokales "+((y<0)?"Maximum":"Minimum")+" bei  ( "+R(ns[1][i])+" | "+R(horner(abl_[0],ns[1][i]))+" )\n";o=true;}
	}
	if(!o)TKD+="    keine\n";o=false;
	if(SP.join("")!="")
	{
		TKD+="\nSattelpunkt"+((SP.length>1)?"e:  { ":":   ")+SP.join(" ;  ")+((SP.length>1)?" }\n":"\n");
	} 
	TKD+="\nWendepunkt(e):\n    ";
	for(i=0;i<ns[2].length;i++)
	{
		if(ns[2][i]==null)continue;
		y=horner(abl_[3],ns[2][i]);
		if((Math.abs(y)>1e-12)&&(Math.abs(horner(abl_[1],ns[2][i]))>1e-12)){TKD+="( "+R(ns[2][i])+" | "+R(horner(abl_[0],ns[2][i]))+" )\n    ";o=true;}
	}
	if(!o)TKD+="keine";
	TKD+="";
	return TKD;
}

function nullstellen(k,ka,kz,kn,kza,kzn)
{
	var x,xx,xr,xi,xk=new Array(0,0),y,Y=new Array(0,0),i,j=0,kk=new Array(),kkk=new Array(),gg=grad(k),ns=new Array(),nskn=0,nsn=0;
	for(i=0;i<k.length;i++)kk[i]=k[i];
	kkk=DIFF_(kk);//alert(kk+"\n"+kkk);
	for(x=-40;(x<=40)&&(gg>2);x++){while((gg>0)&&(Math.abs(horner(kk,x))<1e-14)){kk=hornerpd(kk,x);/*alert(x+"\n"+kk);*/kkk=DIFF_(kk);if(!drin(ns,x)){ns[nsn++]=x;}gg--;}}
	while(gg>2)
	{
		x=Math.random()*Math.random()*Math.random()*Math.random()*Math.random()*Math.random()*50;
		if(Math.random()>.5)x=-x;//alert(x+"\n"+horner(kk,x));
		x=newtonsteps(kk,kkk,x,10);i=0;
		while(((y=Math.abs(horner(kk,x)))<0.5)&&(i<5)){if(y<1e-14)break;newtonsteps(kk,kkk,x,10);i++;}
		if((y<1e-14)){kk=hornerpd(kk,x);kkk=DIFF_(kk);if(!drin(ns,x))ns[nsn++]=x;gg--;}

		if(j>5)
		{
		xr=Math.random()*Math.random()*Math.random()*Math.random()*Math.random()*Math.random()*50;
		xi=Math.random()*Math.random()*Math.random()*Math.random()*Math.random()*Math.random()*10;
		if(Math.random()>.5)xr=-xr;if(Math.random()>.5)xi=-xi;
		xk[0]=xr;xk[1]=xi;
		newtonstepsk(kk,kkk,xk,20);i=0;y=9999999999;
		while(((y=Math.abs(hornerk(kk,xk[0],xk[1],Y)))<0.5)&&(i<200)){if(y<1e-20)break;newtonstepsk(kk,kkk,xk,10);i++;}
		if(y<1e-20)
		{
			if(Math.abs(xk[1])<1e-14){kk=hornerpd(kk,xk[0]);kkk=DIFF_(kk);if(!drin(ns,xk[0]))ns[nsn++]=xk[0];gg--;}
			else
			{
				polydivRK(kk,grad(kk),xk[0],xk[1]);
				kkk=DIFF_(kk);gg-=2;nsk[nskn++]=new Array(xk[0],xk[1]);nsk[nskn++]=new Array(xk[0],-xk[1]);
			}
		}
		}
		j++;if(j>20)return ns;
	}
	//alert("g="+gg+"\n\nNullstellen:\n"+ns+"\nk="+kk);	
	if(gg==2)
	{
		kk[1]/=kk[2];kk[0]/=kk[2];kk[2]=1;kkk=DIFF_(kk);
		gg=0;if(kk[1]*kk[1]/4>=kk[0])
		{
			x=-kk[1]/2+Math.sqrt(kk[1]*kk[1]/4-kk[0]);if(!drin(ns,x))ns[nsn++]=x;
			x=-kk[1]/2-Math.sqrt(kk[1]*kk[1]/4-kk[0]);if(!drin(ns,x))ns[nsn++]=x;
		}
	}
	if((gg==1)&&(Math.abs(kk[1])>1e-12))
	{
		gg=0;if(!drin(ns,-kk[0]/kk[1]))ns[nsn++]=-kk[0]/kk[1];
	}
	for(i=0;i<nsn;i++)
	{
		var d=1e15;
		while((Math.abs(horner(k,Math.round(ns[i]*d/10)/d*10))<=Math.abs(horner(k,ns[i])))&&(d>100))d/=10;
		//alert(ns[i]+"\n"+d+"\n"+Math.round(ns[i]*d)/d+"\n"+horner(k,ns[i])+"\n"+horner(k,Math.round(ns[i]*d)/d));
		ns[i]=Math.round(ns[i]*d)/d;
	}
	return ns;
}

function DIFF(z,n,zz,nn,a)
{
	var g,i;
	zz[0]=0;nn[0]=1;
	for(i=0;i<z.length-1;i++){zz[i]=z[i+1]*(i+1);g=ggt_(zz[i],n[i+1]);zz[i]/=g;nn[i]=n[i+1]/g;a[i]=zz[i]/nn[i];}
}
function DIFF_(k)
{
	var g=grad(k),i,ka=new Array();
	for(i=0;i<g;i++){ka[i]=k[i+1]*(i+1);}
	return ka;
}
function grad(k)
{
	var i=1;
	for(i=k.length-1;i>1;i--){if(Math.abs(k[i])>1.0e-14)break;}
	return i;
}

function newtonsteps(k,kk,x,n)
{
	var j;
	for(j=0;j<n;j++)x-=horner(k,x)/horner(kk,x);
	return x;
}
function newtonstepsk(k,kk,X,n)
{
	var j,y=new Array(0,0),yy=new Array(0,0),q;
	for(j=0;j<n;j++)
	{
		hornerk(k,X[0],X[1],y);
		hornerk(kk,X[0],X[1],yy);
		q=yy[0]*yy[0]+yy[1]*yy[1];
		X[0]-=(y[1]*yy[1]+y[0]*yy[0])/q;
		X[1]-=(y[1]*yy[0]-y[0]*yy[1])/q;
	}
}

function horner(k,x)
{
	var g=k.length-1,y=k[g],i;
	for(i=g-1;i>=0;i--){y*=x;y+=k[i];}
	return y;
}
function hornerpd(k,x,kk)
{
	var g=k.length-1,y=k[g],i,kkk=(kk==null);
	if(kkk)kk=new Array();
	for(i=g-1;i>=0;i--){kk[i]=y;y*=x;y+=k[i];}
	return(kkk)?kk:y;
}
function hornerk(k,xr,xi,Y)
{
	var g=k.length-1,yy,i;
	Y[0]=k[g];Y[1]=0;
	for(i=g-1;i>=0;i--){yy=xr*Y[0]-xi*Y[1];Y[1]=xr*Y[1]+xi*Y[0];Y[0]=yy+k[i];}
	return Y[0]*Y[0]+Y[1]*Y[1];
}
function hornerpdk(k,xr,xi,kk)
{
	var g=k.length-1,yr=k[g],yi=0,yy,i,kkk=(kk==null);
	if(kkk)kk=new Array();
	for(i=g-1;i>=0;i--){kk[i]=yr;yy=xr*yr-xi*yi;yi=xr*yi+xi*yr;yr=yy+k[i];}
	return(kkk)?kk:yr*yr+yi*yi;
}
function polydivRK(k,g,xr,xi)
{
	q=new Array(g);
	kk=new Array(g+1);
	var i,gg=g,a=-2*xr,b=xr*xr+xi*xi;
	for(i=0;i<kk.length;i++)kk[i]=k[i];
	for(i=0;i<kk.length-1;i++)q[i]=0;
	while(g>0)
	{
	 	//status="Polynomdivision, g="+g+"  Rest: "+PStr("x",kk,gg);
		if(g>1)q[g-2]=kk[g];
		kk[g-1]-=kk[g]*a;
		if(g>1)kk[g-2]-=kk[g]*b;
		kk[g]=0;
		//alert("g: "+g+"\nq: "+q.join("  ")+"\nk: "+kk.join("  "));
		while((kk[g]==0)&&(g>0))g--;
	}
	//alert("fertig\ng="+g+"\nq: "+q.join("  ")+"\nk: "+kk.join("  "));
	if((Math.abs(kk[0])>1e-8)||(Math.abs(kk[1])>1e-8)||(g>0)){return false;}
	k[gg]=0;
	for(i=0;i<q.length;i++)k[i]=q[i];
	return true;
}

function hornerB(kz,kn,xz,xn)
{
	var g=kz.length-1,y=kz[g]/kn[g],i;
	for(i=g-1;i>=0;i--)
	{
		y*=xz/xn;
		y+=kz[i]/kn[i];
	}
	return new Array(y,1);
}

function hornerb(kz,kn,x,y)
{
	var g=kz.length-1,i;
	y[0]=kz[g];y[1]=kn[g];
	for(i=g-1;i>=0;i--)
	{
		y[0]*=xz;y[1]*=xn;
		add_(y[0],y[1],kz[i],kn[i],y);
	}
}
function drin(ns,x)
{
	var i;
	for(i=0;i<ns.length;i++){if(Math.abs(ns[i]-x)<1e-13){return true;}}
	return false;
}
function ggt_(a,b)
{
	//var r;a=Math.abs(a);b=Math.abs(b);if((a==0)||(b==0))return 1;if(isNaN(a)||isNaN(b))return 1;
	do{r=a%b;a=b;b=r;}while(r>0);
	return a;
}
function add_(z1,n1,z2,n2,x)
{
	var z=z1*n2+z2*n1,n=n1*n2,g=1;
	if(z==0)n=1;else g=ggt_(z,n);
	x[0]=z/g;x[1]=n/g;
}
function R(x)
{
	return (" "+String(rund(x,1000000000000))).replace(/\./,",").replace(/-,/,"-0,").replace(/ /,"");
}
function rund(x,d)
{
	return Math.round(x*d)/d;
}

