// Javascript zur linearen Regression nach Gauß' Methode der kleinsten Fehlerquadrate
// von Arndt Brünner, Gelnhausen, September 2001
// Version: 30.8.2004
// am 5.1.2001: hyperbolische Funktionen implementiert

x=new Array(); // x-Werte
y=new Array(); // y-Werte
m=new Array(); // Matrix für Gauß-Algorithmus
f=new Array(); // Funktionen - Javascript-Syntax
F=new Array(); // Funktionen - normale Syntax
var ff="";     // Regressionsfunktion in Javascript-Syntax
var FF="";     // Regressionsfunktion in normaler Syntax
var nw=0,nf=0; // Anzahl Wertepaare und Anzahl Funktionen
var nds=6;     // Anzahl Dezimalstellen

var space="                   ";space+=space;space+=space;space+=space;

function regr()
{
	if (getWertepaare()<2) return;
	standardXY();
	if (getFunktionen()==0) return;
	for(z=0;z<nf;z++)
	{
	  for(s=0;s<=z;s++)
	  {
		i1=mi(z,s);i2=mi(s,z);
		m[i1]=GaussSummeX(f[s],f[z]);
		m[i2]=m[i1];
	  }
	  m[mi(z,nf)]=GaussSummeXY(f[z]);
      }
	GLSL(nf,nf+1);
	for(i=0;i<nf;i++) if(Math.abs(m[mi(i,nf)])<1e-12)m[mi(i,nf)]=0;
	t="";ff="";
	nds=Number(document.f.txtnds.value);
	for(i=0;i<nf;i++) ff+="+"+m[mi(i,nf)]+"*"+f[i];
	for (i=0;i<nf;i++) 
	{
		p=m[mi(i,nf)];
		pt=Runden(Math.abs(p),nds)+" * "+F[i]+"\n";
		if((t=="")||(t=="\n")){if(p<0) t+="- "+pt+"\n"; else if(p>0) t+="  "+pt+"\n"; else t+="\n"} 
		else {if((p<0)||(i==0)) t+="- "+pt+"\n"; else if(p>0) t+="+ "+pt+"\n"; else t+="\n"}
	}
	ttt="";
	for(i=0;i<nf;i++)ttt+=Runden(m[mi(i,nf)],nds)+"\n";
	t=t.replace(/\n+/g,"\n").replace(/ \* 1\n/g,"\n").replace(/ 1 \*/g,"");
	if(t==" ")t=" 0";
	FF=t.replace(/\n/g," ").replace(/\./g,",").replace(/=- /g,"= -");
//	document.f.regrresult2.value=t.replace(/\n/g," ").replace(/-,/g,"-0,").replace(/ ,/g," 0,");
	t=ttt+ "________________________\n\n f(x) ="+t.replace(/\n/g," ")+"\n________________________\n\n   S = "+getSA()+"\n________________________\n\n";
	for(i=0;i<nw;i++) t+="f("+x[i]+") = "+Runden(eval(ff.replace(/X/g,x[i])),nds)+"\n";
	t=t.replace(/= \+/,"= ").replace(/ +/g," ").replace(/\./g,",").replace(/=- /g,"= -");
	t=t.replace(/\n,/g,"\n0,").replace(/-,/g,"-0,").replace(/ ,/g," 0,");
	if(t.substr(0,1)==",")t="0"+t;
	if(FF.substr(0,1)==",")FF="0"+FF;
	document.f.regrresult.value=t+"\n\n\n\n\n\n\n\n\n\n\n\n\n";
	FF=FF.replace(/\./g,",").replace(/\(e\)/g,"(e_)").replace(/ ,/g," 0,").replace(/\-,/g,"-0,");
	document.plotter.parse(FF);
	document.plotter.flag=0;
	var dx=x[nw-1]-x[0],y0=-1.0e200,y1=1.0e200,zusatz="";
	if(dx==0)dx=1;
	for(i=0;i<nw;i++){if(y0<y[i])y0=y[i];if(y1>y[i])y1=y[i];
//		zusatz+="1;"+x[i]+((Math.floor(x[i])==x[i])?".0":"")+";"+y[i]+((Math.floor(y[i])==y[i])?".0":"")+";"+"0xAA0033;3|";}
		zusatz+="1;"+x[i]+((Math.floor(x[i])==x[i])?".0":"")+";"+y[i]+((Math.floor(y[i])==y[i])?".0":"")+";"+"0xFF6666;3|";}
	var FF1=("f(x) ="+FF).replace(/=\- /,"= -").replace(/ \* /g,"·").replace(/= \- /,"= -").replace(/\*/g,"·"),
	FF2="";
	for(i=0;i<FF1.length;i++)if(FF1.charAt(i)=="^"){
	I=i;if((FF1.substr(i+1,1).search(/\d/)==0)||( FF1.substr(i+1,1)=="x" ))
	{FF1=FF1.substring(0,i)+" "+FF1.substring(i+1,FF1.length);i++;
	while((FF1.substr(i,1).search(/\d/)==0)||(FF1.substr(i,1)==",")||( FF1.substr(i,1)=="x" ))
	{FF2+=space.substring(0,i-FF2.length-1)+FF1.substr(i,1);i++; if(i>=FF1.length)break;}
	FF1=FF1.substring(0,I)+space.substr(0,i-I-1)+FF1.substring(i,FF1.length);
	i=I+1;
	}};
//	zusatz+="0;5;15;0x000000;Regressionskurve nach Gauß|0;5;30;0x000000;"+FF1;
	zusatz+="0;5;15;0xEEEEEE;Regressionskurve nach Gauß|";
	zusatz+="0;5;28;0xEEEEEE;"+FF2+"|";
	zusatz+="0;5;35;0xEEEEEE;"+FF1+"|0;592;415;0x668866;© Arndt Brünner";
	var dy=y0-y1;if(dy==0)dy=1;
	document.plotter.minscald=25;
	//Farbschema 1
/*	document.plotter.setColor(0,"0xF0E8D0");//Hintergrund
	document.plotter.setColor(1,"0x220066");//Graph
	document.plotter.setColor(2,"0x999999");//Achsen
	document.plotter.setColor(3,"0xE6D2BE");//Feinskalierung
	document.plotter.setColor(4,"0x668866");//Beschriftung
*/
	//Farbschema 2
	document.plotter.setColor(0,"0x003300");//Hintergrund
	document.plotter.setColor(1,"0xFFFF99");//Graph
	document.plotter.setColor(2,"0x99bbaa");//Achsen
	document.plotter.setColor(3,"0x115511");//Feinskalierung
	document.plotter.setColor(4,"0x99aa99");//Beschriftung
	
	document.plotter.setRange(x[0]-dx/4.5,x[nw-1]+dx/4.5,y0+dy/3,y1-dy/4);
	document.plotter.zusatz=zusatz;
	document.plotter.plot();
}

function GaussSummeX(f1,f2)
{
	var s=0,i=0;
	for(i=0;i<nw;i++){
	s+=eval("("+f1.replace(/X/g,x[i])+")*("+f2.replace(/X/g,x[i])+")");
	}
	return s;
}

function GaussSummeXY(f)
{
	var s=0,i=0;
	for(i=0;i<nw;i++)
	s+=eval("("+f.replace(/X/g,x[i])+")*("+y[i]+")");
	return s;
}

function getSA()
{
	var t="("+ff+")";
	var sa=0.0,s=0.0, f=1/nw ;
	for(i=0;i<nw;i++)
	{
		term=y[i]+"-"+t.replace(/X/g,x[i]);
		s=eval(term);
		sa+=s*s*f;
	}
	return Math.sqrt(sa);
}

function mi(z,s)
{
	return z*(nf+1)+s;
}

var MultErg=new Array(/\dx/,/\d\(/,/\)\(/,/x\(/,/\)x/,/pi\(/,/e_\(/,/\dpi/,/\de_/);

function getFunktionen()
{
a=String(document.f.terme.value).replace(/ +/g,"").replace(/\r/g,";").replace(/\n/g,";").replace(/,/g,".");
while(a.indexOf(";;")>-1) {a=a.replace(/;;/g,";");}
if(a.indexOf(";")==0)a="0"+a;
if(a.lastIndexOf(";")==a.length-1)a=a.substr(0,a.length-1);
if(a.length==0){alert("Kein Term angegeben!");return 0;}
while(a.lastIndexOf(";")==a.length-1)a=a.substr(0,a.length-1);
F=a.replace(/\./g,",").split(";");
f=a.split(";");
for(i=0;i<f.length;i++) 
{
f[i]=translate(f[i]);
if (F[i].substr(0,1)=='-')F[i]="("+F[i]+")";
if (eval(f[i].replace(/X/g,x[0])+"*7")!=eval("("+f[i].replace(/X/g,x[0])+")*7"))
{F[i]="("+F[i]+")";f[i]="("+f[i]+")";}
}
nf=f.length;
return nf;
}

function getWertepaare()
{
nw=0;
a=String(document.f.xy.value).replace(/\,/g,".").replace(/\n/g,"/").replace(/\t/g,"/").replace(/ /g,"/").replace(/\r/g,"").replace(/;/g,"/");
a=a.replace(/\|/g,"/").replace(/\(/g," ").replace(/\)/g,"").replace(/w+/g,"");
a=a.replace(/\/+/g,"/");
if(a==""){alert("Es müssen zunächst mindestens zwei gültige Wertepaare eingegeben werden!");return 0;}
while (a.lastIndexOf("/")==a.length-1){a=a.substr(0,a.length-1);};
while (a.indexOf("/")==0) {a=a.substr(1,a.length-1);};
a=a.replace(/\r/g,"/").replace(/\/+/g,"/");
b=a.split("/");
if(b.length<4){ alert("Es müssen zunächst mindestens zwei gültige Wertepaare eingegeben werden!");return 0;}
if (b.length%2!=0) {alert("Ungerade Anzahl von Werten gefunden, bitte Eingabe überprüfen!");return 0;}
for(i=0;i<b.length/2;i++){x[i]=Number(b[i*2]);y[i]=Number(b[i*2+1]);}
nw= b.length/2;
quicksort();
return nw;
}

function getSample()
{
var tt=document.f.terme.value;
if ((tt.substr(tt.length-1,1)!="\n")&&(tt!=""))tt=document.f.terme.value+="\n";
var t=String(document.f.s.options[document.f.s.selectedIndex].value+'\n');
t=t.replace(/\//g,"\n");
document.f.terme.value+=(t);
repair();
//document.f.terme.value=t;
document.f.s.selectedIndex=0;
}

function repair()
{
t=String(document.f.terme.value);
t=t.replace(/ /g,"");
t=t.replace(/\r\n/g,"|");
a=t.split("|");
for(i=0;i<a.length;i++)
for(j=i+1;j<a.length;j++)
if(a[i]==a[j])a[j]="";
t=a.join("|");
while(t.indexOf("||")>-1)t=t.replace(/\|\|/g,"|");
t=t.replace(/\|/g,"\n");
document.f.terme.value=t;
}

function translate(t)
{
t="("+t+")";
n=0;
var j=0,k=0,i=0;
for(i=0;i<t.length;i++)
if (t.substr(i,1)=="^")
{j=parsefind(t,i,-1); k=parsefind(t,i,1);
t=t.substring(0,j+1)
+ "Math.pow(" 
+ t.substring(j+1,i)
+ ","+t.substring(i+1,k)+")"
+ t.substring(k,t.length);
n++;if(n>15)break;
}
	if(t=="") return "(0)";
	if(t==null) return;
	var tt=t;
	t=t.replace(/ /g,"").replace(/\n/g,"").replace(/\r/g,"");
	t=t.replace(/\++/g," + ");
	t=t.replace(/\-+/g," - ");
	t=t.replace(/\*+/g," * ");
	t=t.replace(/\/+/g," / ");
	t=t.replace(/( \^ )+/g,"^");
	t=t.replace(/Math\./g,"").replace(/sqrt/g,"sqr");
	t=t.replace(/pow/g,"Math.pow");
	t=t.replace(/sinh/gi,"Hyp1");
	t=t.replace(/cosh/gi,"Hyp2");
	t=t.replace(/tanh/gi,"Hyp3");
	t=t.replace(/asinh/gi,"Hyp4");
	t=t.replace(/acosh/gi,"Hyp5");
	t=t.replace(/atanh/gi,"Hyp6");
	t=t.replace(/atan/gi,"Math.at~an");
	t=t.replace(/asin/gi,"Math.as~in");
	t=t.replace(/acos/gi,"Math.ac~os");
	t=t.replace(/sin/gi,"Math.sin");
	t=t.replace(/cos/gi,"Math.cos");
	t=t.replace(/tan/gi,"Math.tan");
	t=t.replace(/Hyp1/gi,"sinh");
	t=t.replace(/Hyp2/gi,"cosh");
	t=t.replace(/Hyp3/gi,"tanh");
	t=t.replace(/Hyp4/gi,"asinh");
	t=t.replace(/Hyp5/gi,"acosh");
	t=t.replace(/Hyp6/gi,"atanh");
	t=t.replace(/sqr/gi,"Math.sqrt");
	t=t.replace(/abs/gi,"Math.abs");
	t=t.replace(/int/gi,"Math.floor");
	t=t.replace(/log/gi,"Math.log");
	t=t.replace(/ln/gi,"Math.log");
	t=t.replace(/exp/gi,"Math.°@p");
	t=t.replace(/x/g,"X").replace(/y/g,"Y");
	t=t.replace(/(pi)+/gi,"Math.PI");
	t=t.replace(/\(e\)+/gi,"Math.E");
	t=t.replace(/\°/g,"e").replace(/@/g,"x");
	t=t.replace(/\~/g,"");
if (t=="") t="0";
return t.substr(1,t.length-2);
}

function parsefind(t,i,r)
{
L=t.length; k=0; j=0;
for(j=i+r;(j<L)&&(j>=0);j+=r)
{
u=t.substr(j,1);
if(u=="(")k++; else if(u==")")k--;
if(k*r<0)return j;
if((k==0)&&((u=="+")||(u=="-")||(u=="*")||(u=="/")||(u==",")||(j+r==L+1)||(j+r==-1))) return j;

}
return j;
}

function sinh(x){return (Math.exp(x)-Math.exp(-x))/2;}
function cosh(x){return (Math.exp(x)+Math.exp(-x))/2;}
function tanh(x){var a=Math.exp(2*x);return (a-1)/(a+1);}
function asinh(x){return Math.log(Math.sqrt(x+1)+x);}
function acosh(x){return 2*Math.log(Math.sqrt(x-1)+Math.sqrt(x+1))-0.69314718055994530941;}
function atanh(x){return (Math.log(1+x)-Math.log(1-x))/2;}

function Runden(t,i)
{
	var d=Math.pow(10,i);
	return Math.round(t*d)/d;
}

function GLSL(nz, ns)
{
    var i, j, k ;
	var q;
	for (j = 0;j<ns-1;j++)
	{
        // Diagonalenfeld normalisieren
        q = m[j * ns + j];
        if (q == 0){
            //Gewährleisten, daß keine 0 in der Diagonale steht
            for (i = j+1 ;i< nz;i++)
			{
                // Suche Reihe mit Feld <> 0 und addiere dazu
                if (m[i * ns + j] != 0)
				{
                    for (k = 0 ; k<ns; k++)
					{
						m[j * ns + k] += m[i * ns + k];
                    }
                    q = m[j * ns + j];
                    break;
                }
            }
        }
        if (q != 0)
		{
            // Diagonalen auf 1 bringen
            for (k = 0;k< ns;k++)
			{
                m[j * ns + k] = m[j * ns + k] / q;
            }
        }
        // Spalten außerhalb der Diagonalen auf 0 bringen
        for (i = 0 ; i< nz ; i++)
		{
            if (i != j )
			{
                q = m[i * ns + j];
                for (k = 0; k< ns;k++)
				{
                    m[i * ns + k] -=  q * m[j * ns + k];
                }
			}
        }
    }
}

function rndtest(n)
{
	var t="";
	for(i=0;i<n;i++)	t+=String(Runden(Math.random()*20-10,2)+"  "+Runden(Math.random()*20-10,2)).replace(/\./g,",")+"\n";
	document.f.xy.value=t;
}

function standardXY()
{
	var t="",maxL=0,minL=100;
	L=new Array(nw-1);
	for(i=0;i<nw;i++)
	{
		L[i]=String(x[i]).length;
		if(x[i]<0)L[i]--;
		if(L[i]>maxL)maxL=L[i];
	}
	for (i=0;i<nw;i++)
	{
		if(x[i]>=0)t+=" ";
		t+=x[i]+"                 ".substr(0,maxL-L[i]+2);
		if(y[i]>=0)t+=" ";
		t+=y[i]+"\n";
	}
	t=t.replace(/\./g,",").replace(/\n,/g,"\n0,").replace(/ ,/g," 0,");
	t=t.replace(/\-,/g,"-0,");
	if(t.substr(0,1)==",")t="0"+t;
	document.f.xy.value=t;
}

function quicksort()
{
    L=new Array(32),R=new Array(32);
    var s, i, j, LL, rr, v, w, ii;
    s=0; L[0]=0; R[0]=nw-1;
    do
    {
        LL = L[s]; rr = R[s];
        s--;
        do
        {
            i = LL; j = rr;
            v = x[Math.floor((LL + rr) / 2)];
            do
		{
                while(x[i]<v) i++;
                while(x[j]>v) j--;
                if (i <= j)
                {
                    w = x[i]; x[i] = x[j]; x[j] = w;
                    w = y[i]; y[i] = y[j]; y[j] = w;
                    i++; j--;
                }
            } while (i <= j);
            if (j - LL >= rr - 1)
            {
                if (LL < j)
                {
                    s++;
                    L[s] = LL; R[s] = j;
                }
                LL = i;
            }
            else
            {
                if (i < rr)
                {
                    s++;
                    L[s] = i; R[s] = rr;
                }
                rr = j;
            }
        } while (LL < rr);
    } while (s >= 0);
}

