// Javascript-Klasse und -Funktionen zur Bruchrechnung
// (c) Arndt Brünner, Juni 2002
//         Version: 3. 11. 2002


var autokuerzen=true;

function RationaleZahl(Z,N)
{	
	if(Z==null)Z=1;
	if(N==null)N=1;
	var z,n;
	if(String(Z).indexOf('_')==-1){this.z=Number(Z);}else{var x=String(Z).split('_');this.z=Number(x[0])*Number(N)+Number(x[1])*sgn(Number(x[0]));if(this.z==null)alert("Fehler bei Erzeugung eines Bruch-Objekts mit z="+Z);}
	this.n=N;
	if(n==0){alert("Division durch Null!");return;}
	this.set=new Function("z,n","this.n=n;if(String(z).indexOf('_')==-1)this.z=Number(z);else{var x=String(z).split('_');this.z=Number(x[0])*Number(n)+Number(x[1])*sgn(Number(x[0]));}return this;");
	this.setStr=new Function("t","if(t.indexOf('/')==-1)t=getBruchFromDez(t);var b=t.split('/');this.z=parseInt(b[0]);this.n=((b.length>1)&&(b[1]!=''))?parseInt(b[1]):1; return this.kuerzen(0);");
	this.setR=new Function("b","this.z=b.z;this.n=b.n;return this.kuerzen(0);");
	this.add=new Function("s","this.z=this.z*s.n+s.z*this.n;this.n*=s.n;return this.kuerzen(0);");
	this.sub=new Function("s","this.z=this.z*s.n-s.z*this.n;this.n*=s.n;return this.kuerzen(0);");
	this.mult=new Function("f","this.z*=f.z;this.n*=f.n;return this.kuerzen(0);");
	this.div=new Function("q","if(q.z==0){alert('Division durch Null!');return;} this.z*=q.n;this.n*=q.z;if(this.n<0){this.n=-this.n;this.z=-this.z;};return this.kuerzen(0);");
	this.pow=new Function("e","if(e==0){this.z=1;this.n=1;return this;}var ee=Math.abs(e),z=this.z;n=this.n;for(var i=1;i<ee;i++){this.z*=z;this.n*=n;}if(e<0)return this.kehrwert();return this;");
	this.powb=new Function("b","b.kuerzen(0);if(b.n!=1){alert('Exponent muß ganzzahlig sein!');return;}var e=b.z,ee=Math.abs(e),z=this.z;n=this.n;if(e==0){this.z=1;this.n=1;return this;}for(var i=1;i<ee;i++){this.z*=z;this.n*=n;}if(e<0)return this.kehrwert();return this;");
	this.kehrwert=new Function("","var a=this.n;this.n=this.z;this.z=a;if(this.n<0){this.z*=-1;this.n*=-1;}return this;");
	this.greater=new Function("b","return(b.val()>this.val());");
	this.smaller=new Function("b","return(b.val()<this.val());");
	this.equals=new Function("b","return(b.kuerzen(0).str()==this.kuerzen(0).str());");
	this.is1=new Function("","return this.z=this.n;");
	this.val=new Function("","return this.z/this.n;");
	this.kuerzen=new Function("a","if(a>1){if ((this.z%a!=0)||(this.n%a!=0)){alert('Bruch ist nicht durch '+a+' kürzbar');return;}this.z/=a;this.n/=a;return this;}var g=ggT(Math.abs(this.z),Math.abs(this.n));this.z/=g;this.n/=g;if(this.n<0){this.z*=-1;this.n*=-1;}return this;");
	this.str=new Function("","if((this.n==1)||(this.z==0))return String(this.z); return String(this.z)+'/'+String(this.n);");
	this.str2=new Function("","if((this.n==1)||(this.z==0))return String(this.z); if(Math.abs(this.z)>this.n)return ((this.z>0)?Math.floor(this.z/this.n):Math.ceil(this.z/this.n))+'_'+(Math.abs(this.z)%this.n)+'/'+this.n; return String(this.z)+'/'+String(this.n);");
	if(autokuerzen)this.kuerzen(0);
}

function RationaleZahl2(z,n)
{
	var z,n;
	this.z=z;
	this.n=n;
	this.set=new Function("z,n","this.n=n,this.z=z;return this;");
	this.add=new Function("z,n","this.z=this.z*n+z*this.n;this.n*=n;return this.kuerzen(0);");
	this.sub=new Function("z,n","this.z=this.z*n-z*this.n;this.n*=n;return this.kuerzen(0);");
	this.mult=new Function("z,n","this.z*=z;this.n*=n;return this.kuerzen(0);");
	this.div=new Function("z,n","if(z==0){alert('Division durch Null!');return;} this.z*=n;this.n*=z;return this.kuerzen(0);");
	this.pow=new Function("e","if(e==0){this.z=1;this.n=1;return this;}var ee=Math.abs(e),z=this.z;n=this.n;for(var i=0;i<ee;i++){this.z*=z;this.n*=n;}if(e<0)return this.kehrwert();return this;");
	this.powb=new Function("b","if(b.n!=1){alert('Exponent muß ganzzahlig sein!');return;}var e=b.z,ee=Math.abs(e),z=this.z;n=this.n;if(e==0){this.z=1;this.n=1;return this;}for(var i=0;i<ee;i++){this.z*=z;this.n*=n;}if(e<0)return this.kehrwert();return this;");
	this.kehrwert=new Function("","var a=this.n;this.n=this.z;this.z=a;return this;");
	this.greater=new Function("z,n","return(z/n>this.val());");
	this.smaller=new Function("z,n","return(z/n<this.val());");
	this.equals=new Function("z,n","return(z/n==this.kuerzen(0));");
	this.val=new Function("","return this.z/this.n;");
	this.kuerzen=new Function("a","if(a>1){if ((this.z%a!=0)||(this.n%a!=0)){alert('Bruch ist nicht durch '+a+' kürzbar');return;}this.z/=a;this.n/=a;return this;}var g=ggT(Math.abs(this.z),Math.abs(this.n));this.z/=g;this.n/=g;return this;");
	this.str=new Function("","if((this.n==1)||(this.z==0))return String(this.z); return String(this.z)+'/'+String(this.n);");
}

function ggT(a,b)
{
	if(a==0)return b;if(b==0)return a;if((a%1)!=0)return 1;if((b%1)!=0)return 1; 
	a=Math.abs(a);b=Math.abs(b);
	var c=0; do{c=a%b;a=b;b=c;}while(c!=0); return a;
}



var R=new Array();
var q=new Array();
var cC0="0".charCodeAt(0);
var cC9="9".charCodeAt(0);
var cCp="p".charCodeAt(0);
var cCk=".".charCodeAt(0);
//var cCk=",".charCodeAt(0);
var cCq="q".charCodeAt(0);
var cC_="_".charCodeAt(0);
var brT="";

//    6/(2/3)+5/8
//    a.div((b.div(c))).add(d.div(e))



function transform(t)
{	
	t=t.replace(/\d \d/g,"@");
	t=t.replace(/,/g,".").replace(/ /g,"");
	if(t.indexOf("@")>-1){alert("Fehler\nUnechte Brüche mit Unterstrich notieren");return "";}
	if(t.charAt(0)=="-")t="0"+t;
	t=t.replace(/\(\-/g,"(0-");
//	if(t.indexOf(",")>-1){alert("Keine Dezimalbrüche!");return "";}
	var kl=0;
	for(i=0;i<t.length;i++){if(t.charAt(i)=="(")kl++;else if(t.charAt(i)==")")kl--;if(kl<0){alert("Klammerfehler!");return "";}}
	if(kl!=0){alert("Klammerfehler!");return "";}
	var n=0,T="";

	// Zahl-Zahl-Potenzen berechnen
	for(i=0;i<t.length;i++)
	{
		if(t.charAt(i)=="^")
		{
			var c0=t.charCodeAt(i-1),c1=t.charCodeAt(i+1);
			if((c0<=cC9)&&(c0>=cC0)&&(c1<=cC9)&&(c1>=cC0))
			{
				var x=extractNumber(t,i-1,-1),y=extractNumber(t,i+1,1);
				t=t.substr(0,i-x.length)+Math.pow(x,y)+t.substr(i+y.length+1,t.length);
				//alert(t);
			}
		}
	}

	// Zahl/Zahl-Brüche in Objekte umwandeln 
	for(i=0;i<t.length;i++)
	{
		if(t.charAt(i)=="/")
		{
			var c0=t.charCodeAt(i-1),c1=t.charCodeAt(i+1);
			if((c0<=cC9)&&(c0>=cC0)&&(c1<=cC9)&&(c1>=cC0))
			{
				var x=extractNumber(t,i-1,-1),y=extractNumber(t,i+1,1);
				var xl=x.length,yl=y.length;
				if((t.charAt(i+yl+1)!="^")&&(t.charAt(i-xl-1)!="^"))
				{
					q[n]=new RationaleZahl(x,y);
					//alert(q[n].str());
					t=t.substr(0,i-xl)+"q["+n+"]"+t.substr(i+yl+1,t.length);
//					if(t.charAt(i-xl-1)=="(")if(t.charAt(i+String(n).length+2)==")")
//						t=t.substr(0,i-xl-1)+t.substring(i-xl,i+String(n).length+2)+t.substring(i+String(n).length+3,t.length);
					n++;
				}
			}
		}
	}

	// restliche Zahlen in Objekte umwandeln
	for(i=0;i<t.length;i++)
	{
		var c=t.charCodeAt(i);
		if(c==cCq){var j=t.indexOf("]",i);T+=t.substring(i,j);i=j;}
		if((c>=cC0)&&(c<=cC9)&&(t.charAt(i-1)!="["))
		{
			z=extractNumber(t,i,1); 
			if(z.indexOf(".")==-1)
				q[n]=new RationaleZahl(z,1);
			else
			{
				var Br=getBruchFromDez(z).split("/");
				q[n]=new RationaleZahl(Br[0],Br[1]);
			}
			//alert(q[n].str());
			T+="q["+n+"]";
			n++;
			i+=z.length-1;
		}
		else T+=t.substr(i,1);
	}
	var QT=T;
	
	for(i=0;i<T.length-1;i++)
	{
		var tt=T.substr(i,1);
		var TT="",TTT="",o=-1,op="";
		if(tt=="+"){o=0;op=".add(";}
		else if(tt=="-"){o=1;op=".sub(";}
		else if(tt=="*"){o=2;op=".mult(";}
		else if(tt=="/"){o=3;op=".div(";}
		else if(tt=="^"){o=4;op=".powb(";}
		if(o>-1)
		{
			j1=parse(T,i+1,1,o);
			T=T.substr(0,i)+op+T.substring(i+1,j1)+")"+T.substring(j1,T.length);
		}
	}
	var QQ="";for(i=0;i<n;i++)QQ+="q["+i+"] = "+ q[i].str()+"\n";
	T=clean(T);
	T=T.replace(/{/g,"(").replace(/}/g,")");
	//alert(QQ+"\n"+T);
	QT=QT.replace(/\+/g," + ").replace(/\+/g," + ").replace(/\-/g," - ").replace(/\//g," / ").replace(/\*/g," * ").replace(/\(/g,"( ").replace(/\)/g," )");
	brT="Brüche und Zahlen extrahieren und in Objekt-Array umwandeln:\n\n"+QQ+"\n\nErsetze die Zahlen im Term durch die entsprechenden Objektvariablen:\n\n"+QT+"\n\n\nDie Objekte stellen die Rechenoperationen als Methoden zur Verfügung.\nTerm entsprechend umwandeln:\n\n"+T;
	return T;
}

function extractNumber(t,j,d)
{
	var i=0;
	for(i=j;(i<t.length)&&(i>=0);i+=d)
	{
		c=t.charCodeAt(i);
		if(((c<cC0)||(c>cC9))&&(c!=cCk)&&(c!=cC_)&&(c!=cCp))break;
	}
	return (d==1)? t.substring(j,i):t.substring(i+1,j+1);
}

function parse(t,j,d,p)
{
	if(d==0)return "fehler";
	var kl=0,i=j,L=t.length,kk=false;
	do
	{
		var tt=t.substr(i,1);
		if(tt=="("){kl++;kk=true;}
		else if(tt==")"){kl--;kk=true;}
		//if((kk)&&(kl==0)){i+=d;break;}
		if(kl==-1)break;
		else if((tt=="+")&&(kl==0))break;
		else if((tt=="-")&&(kl==0))break;
		else if((tt=="*")&&(kl==0)&&(p>1))break;
		else if((tt=="/")&&(kl==0)&&(p>2))break;
		else if((tt=="^")&&(kl==0)&&(p>3))break;
		i+=d;
	}while((i<L)&&(i>=0));
	//alert(t+"\n"+j+"\n"+i+"\n"+d+"\n"+((d==1)? t.substring(j,i):t.substring(i+1,j+1)));
	return i;
}

function calcbruchterm(t)
{
	//var qq=new RationaleZahl(0,1);
	//alert(t+"\n"+eval("("+t+").str()"));
	t=transform(t);
	if (t=="")return "Fehler";
	return eval("("+t+")").str();
}
function calcbruchterm2(t)
{
	t=transform(t);
	if (t=="")return "Fehler";
	return eval("("+t+")").str2();
}

function clean(t)
{
	var i0=-1,i1,kl=0,i=0;
	do
	{
		i0=t.indexOf("((",i0);
		if(i0==-1)break;
		i1=i0;
		do
		{
			i1=t.indexOf("))",i1+1);
			if(i1==-1)break;
			kl=0;
			for(i=i0+1;i<=i1;i++)
			{
				if(t.charAt(i)=="(")kl++; 
				if(t.charAt(i)==")")kl--;
				if(kl==0)break;
			}
		}while(kl!=0);
		if((kl==0)&&(i1>-1)&&(i==i1))
		{
			//alert(t+"\ni0="+i0+"   i1="+i1+"    i="+i+"   kl="+kl+"\n"+t.substr(0,i0)+t.substring(i0+1,i1)+t.substring(i1+1,t.length));
			t=t.substr(0,i0)+t.substring(i0+1,i1)+t.substring(i1+1,t.length);i1--;
		}else i0++;
	}while(true);
	while(t.charAt(0)=="(")
	{	
		var tl=t.length;kl=0;
		for(i=0;i<tl;i++)
		{
			tt=t.charAt(i);
			if(tt=="(")kl++;
			if(tt==")")kl--;
			if(kl==0)break;
		}
		t=t.substring(1,i)+t.substring(i+1,tl);
	}
	return t;
}

function sgn(x){return (x<0)?-1:((x>0)?1:0);}

function getBruchFromDez(d)
{
d=String(d);
var g="";
var ub=false;
d=d.replace(/,/g,".").replace(/ +/g,"");
if(Number(d)<0){g="-";d=d.substring(1,d.length);}
var ik=d.indexOf(".");if(ik==-1)return g+d;
if(ub){g+=d.substr(0,ik)+" ";d="0."+d.substring(ik+1,d.length);ik=1;}
if(d=="")return "";
var ip=d.indexOf("p");if(ip<0)ip=d.length;
var bruch=getBruch(d.substr(0,ip));
if(ip==d.length) return g+bruch;
if(ik==-1)return "";
var p=d.substr(ip+1,d.length-ip);
if (p.length==0)return "";
var ib=bruch.indexOf("/");
var z1=Number(bruch.substr(0,ib));var n1=Number(bruch.substr(ib+1,bruch.length-ib));
var n2=Number("999999999999999999999".substr(0,p.length)+"00000000000000000".substring(ik+1,ip));
var nn=n1*n2/ggT(n1,n2)
var zz=z1*nn/n1+p*nn/n2;
ggt=ggT(nn,zz);
nn/=ggt;zz/=ggt;
if((ub)&&(g!=""))return g+zz+"/"+nn; else return zz+"/"+nn;
}
function getBruch(x)
{
var i=(t=String(x)).indexOf(".");
if(i==-1) return x;
if((t.length-i>20)||(t.length>25)) return "";
var z=Number(t.substring(0,i)+t.substring(i+1,t.length));
var n=Number(1+"00000000000000000000000000000000000000".substr(0,t.length-i-1));
ggt=ggT(z,n);
return z/ggt+"/"+n/ggt;
}

function kettenbruchapprox(x,b,nn,d)
{
if(d==null)d=1e-12;
if(nn==null)nn=1000000;
b.n=1;b.z=x;
if(Math.round(x)==x){return true;}
var q=Math.abs(x),r=1;
var vz=(x<0)?-1:1;
while(Math.round(q)!=q){q*=10;r*=10;}
var rr=q%r,c=r;
q=Math.floor(q/r);r=rr;
var a2=0,a1=1,a0=q,b2=1,b1=0,b0=1,i=0;
while((b0<=nn)&&(i<1000))
{
	i++;
	q=Math.floor(c/r);rr=c%r;c=r;r=rr;
	var aa=q*a0+a1;a2=a1;a1=a0;a0=aa;
	var bb=q*b0+b1;b2=b1;b1=b0;b0=bb;
	if(Math.abs(a1/b1-Math.abs(x))<d)break;
	//alert("a0="+a0+"\nb0="+b0+"\nb="+q+"\na1="+a1+"\nb1="+b1+"\na2="+a2+"\nb2="+b2);
	//if(b1>nn)break;
}//alert(Math.abs(a1/b1-Math.abs(x))+"\n"+d);
if(Math.abs(a1/b1-Math.abs(x))>d)return false;
nm=b1;zm=a1;
b.z=vz*zm;b.n=nm;
return true;
}
