		
		/**
		 *   signum()
		 *	@param <double> a
		 * 	@return <double> sig 
		 *	@description liefert 1, wenn a > 0 ist
		 *						 0, wenn a = 0 ist
		 *						-1, wenn a < 0 ist
		 *
		 *	@author Michael Loesler - http://derletztekick.com	
		 */			
		Math.signum = function(a) {
			if (a == 0.0)
				return 0.0;
			if (a > 0.0)
				return 1.0;
			return -1.0;	
		}
		
		/**
		 *   zeros()
		 *	@param <int> o ... Zeilen
		 *	@param <int> p ... Spalten
		 * 	@return <Array> A 
		 *	@description Erzeugt eine Matrix A(o,p), 
		 *				 die mit Nullen gefüllt ist
		 *
		 *	@author Michael Loesler - http://derletztekick.com	
		 */			
		Math.zeros = function(o,p) {
			var A = new Array(o);
			for (var i=0; i<o; i++) {
				A[i] = new Array(p);
				for (var j=0; j<p; j++)
					A[i][j]=0.0;
			}
			return A;
		}

		/**
		 *   multi()
		 *	@param <Array> A1
		 *	@param <Array> A2
		 * 	@return <Array> R = A1*A2 
		 *	@description Multipliziert zwei Matrizen A1(o,p), A2(l,k) 
		 *				 und gibt das Produkt R(p,l) zurück
		 *
		 *	@author Michael Loesler - http://derletztekick.com		 	 
		 */				
		Math.multi = function (A1, A2){
			if (A1[0].length == A2.length){
				var R = Math.zeros(A1.length,A2[0].length); 
				for (var i=0; i<A1.length; i++){
					for (var j=0; j<A2[0].length; j++){
						R[i][j] = 0.0;
						for (var k=0; k<A1[0].length; k++)
							R[i][j] += A1[i][k] * A2[k][j];
					}		
				}
				return R;
			}
			return null;
		}
				
		/**
		 *  randn()
		 *
		 * @param <Integer>count Anzahl der zu erzeugenden Zufallszahlen
		 * @return <Array>gaussianRandomNumbers Array der Zufallszahlen
		 * @description Erzeugt einen Vektor mit normlverteielten Zufallszahlen
		 *              Erweiterung der Math-Klasse
 	 	 *
		 * @author Michael Loesler - http://derletztekick.com
		 */

		Math.randn = function (count){
			count = count || 1;
			var gaussianRandomNumbers = [];
			while (gaussianRandomNumbers.length < count) {
				var x1 = x2 = w = 0.0;
				do {
					x1 = 2.0 * Math.random() - 1.0;
					x2 = 2.0 * Math.random() - 1.0;
					w = x1 * x1 + x2 * x2;
				} 
				while (w>= 1.0);
				w = Math.sqrt( (-2.0 * Math.log(w) ) / w );
				gaussianRandomNumbers.push( y1 = x1 * w );
				gaussianRandomNumbers.push( y2 = x2 * w );
			}
			return gaussianRandomNumbers.slice(0,count);
		}		
		
		function BackwardIntersection() {
			this.parameterTable = null;
			this.resultTableCas = null;
			this.resultTableCol = null;
			this.resultTableFlb = null;
			this.samplesField   = null;
			this.calcButton = null;
			this.samples = 2500;
			this.parameters = new Array();
			this.RHO = Math.PI / 200.0;
			
			this.init = function() {
				if (!document.getElementById || !document.createElement ||
						(this.parameterTable = document.getElementById("backwardIntersectionParameters")) == null || 
						(this.resultTableCas = document.getElementById("backwardIntersectionResultCassini")) == null ||
						(this.resultTableCol = document.getElementById("backwardIntersectionResultCollins")) == null ||
						(this.resultTableFlb = document.getElementById("backwardIntersectionResultFontllagunesbatlle")) == null)
					return false;
				
				this.replaceElement();
				this.parameters = this.parameterTable.getElementsByTagName("input");
				this.initButton();
				return true;
			}
			
			this.initButton = function() {
				var el = document.getElementById("calcButton");
				this.samplesField = this.createInputElement(this.samples, "text");
				this.calcButton = this.createInputElement("Berechnen", "button");
				var self = this;
				this.calcButton.onclick = function(e) {
					self.calculate();
				};
				el.appendChild( document.createTextNode("Anzahl der Simulationen m: ") );
				el.appendChild( this.samplesField );
				el.appendChild( this.calcButton );
			}
			
			this.replaceElement = function() {
				var codeEl = this.parameterTable.getElementsByTagName("code");

				while (codeEl.length > 0) {
					var value  = codeEl[0].firstChild.nodeValue;
					var parent = codeEl[0].parentNode;
					parent.replaceChild(this.createInputElement(value, "text"), codeEl[0]); 
					parent.style.textAlign = "left";
				}
			}
			
			this.createInputElement = function(value, type) {
				var el = document.createElement("input");
				el.type = type;
				el.value = value;
			
				return el;
			}
			
			this.showResult = function(table, x, y, coVar) {
				var pres = table.getElementsByTagName("pre");
				pres[0].firstChild.nodeValue = this.array2String(coVar);
				var tds = table.getElementsByTagName("td");
				tds[0].firstChild.nodeValue = x.toFixed(7);
				tds[2].firstChild.nodeValue = y.toFixed(7);
			}
			
			this.array2String = function(arr) {
				var str = "";
				for (var i=0; i<arr.length; i++) {
					for (var j=0; j<arr[i].length; j++)
						str += ((arr[i][j]<0)?arr[i][j].toFixed(10):"+"+ arr[i][j].toFixed(10)) + " \t";
					str += "\r\n";
				}
				return str;
			}
			
			this.calculate = function() {

				if (this.parameters == null || this.parameters.length != 9) 
					return;
					
				var n = (parseInt(this.samplesField.value) > 1)?parseInt(this.samplesField.value):this.samples;	
					
				var ay = parseFloat(this.parameters[0].value); //10.0;
				var ax = parseFloat(this.parameters[1].value); // 0.0;
				
				var by = parseFloat(this.parameters[2].value); //-5.0;
				var bx = parseFloat(this.parameters[3].value); //-5.0*sqrt(3);
				
				var cy = parseFloat(this.parameters[4].value); // 5.0*sqrt(3);
				var cx = parseFloat(this.parameters[5].value); //-5.0;
				
				var alpha = parseFloat(this.parameters[6].value)*this.RHO; //1.9068;
				var beta  = parseFloat(this.parameters[7].value)*this.RHO; //1.7503;
				
				var sigma = parseFloat(this.parameters[8].value)*this.RHO; //0.0000785;
				
				var Z = new Array(2);
				Z[0] = Math.randn(n);
				Z[1] = Math.randn(n);
				
				var Gab = [ [ Math.sqrt(2.0)*sigma, 0.0],
							[-Math.sqrt(0.5)*sigma, Math.sqrt(1.5)*sigma] ];
							
				var GZ = Math.multi(Gab,Z);
				if (GZ == null) {
					return;
				}
					
				var pCassini = new Array(n);
				var pCollins = new Array(n);
				var pFontllagunesbatlle = new Array(n);
				
				var meanCassiniX = 0;
				var meanCassiniY = 0;
				
				var meanCollinsX = 0;
				var meanCollinsY = 0;
				
				var meanFontllagunesbatlleX = 0;
				var meanFontllagunesbatlleY = 0;
				
				for (var i=0; i<n; i++) {
					var cas = this.cassini(ax,ay,bx,by,cx,cy,alpha+GZ[0][i],beta+GZ[1][i]);
					meanCassiniX += cas[0];
					meanCassiniY += cas[1];
					pCassini[i] = cas;
					
					var col = this.collins(ax,ay,bx,by,cx,cy,alpha+GZ[0][i],beta+GZ[1][i]);
					meanCollinsX += col[0];
					meanCollinsY += col[1];
					pCollins[i] = col;
					
					var flb = this.fontllagunesbatlle(ax,ay,bx,by,cx,cy,alpha+GZ[0][i],beta+GZ[1][i]);
					meanFontllagunesbatlleX += flb[0];
					meanFontllagunesbatlleY += flb[1];
					pFontllagunesbatlle[i] = flb;
				}
				
				meanCassiniX /= n;
				meanCassiniY /= n;
				
				meanCollinsX /= n;
				meanCollinsY /= n;
				
				meanFontllagunesbatlleX /= n;
                meanFontllagunesbatlleY /= n;
				
				var Ccas = Math.zeros(2,2);
				var Ccol = Math.zeros(2,2);
				var Cflb = Math.zeros(2,2);
				
				for (var i=0; i<n; i++) {
					Ccas[0][0] += (((pCassini[i][0]-meanCassiniX)*(pCassini[i][0]-meanCassiniX))/n);
					Ccas[0][1] += (((pCassini[i][0]-meanCassiniX)*(pCassini[i][1]-meanCassiniY))/n);
					Ccas[1][1] += (((pCassini[i][1]-meanCassiniY)*(pCassini[i][1]-meanCassiniY))/n);
					Ccas[1][0] = Ccas[0][1];
					
					Ccol[0][0] += (((pCollins[i][0]-meanCollinsX)*(pCollins[i][0]-meanCollinsX))/n);
					Ccol[0][1] += (((pCollins[i][0]-meanCollinsX)*(pCollins[i][1]-meanCollinsY))/n);
					Ccol[1][1] += (((pCollins[i][1]-meanCollinsY)*(pCollins[i][1]-meanCollinsY))/n);
					Ccol[1][0] = Ccol[0][1];
					
					Cflb[0][0] += (((pFontllagunesbatlle[i][0]-meanFontllagunesbatlleX)*(pFontllagunesbatlle[i][0]-meanFontllagunesbatlleX))/n);
					Cflb[0][1] += (((pFontllagunesbatlle[i][0]-meanFontllagunesbatlleX)*(pFontllagunesbatlle[i][1]-meanFontllagunesbatlleY))/n);
					Cflb[1][1] += (((pFontllagunesbatlle[i][1]-meanFontllagunesbatlleY)*(pFontllagunesbatlle[i][1]-meanFontllagunesbatlleY))/n);
					Cflb[1][0] = Cflb[0][1];
				}
				
				this.showResult( this.resultTableCas, meanCassiniX, meanCassiniY, Ccas ); 
				this.showResult( this.resultTableCol, meanCollinsX, meanCollinsY, Ccol ); 
				this.showResult( this.resultTableFlb, meanFontllagunesbatlleX, meanFontllagunesbatlleY, Cflb ); 
			}
			
			this.cassini = function(ax,ay,bx,by,cx,cy,alpha,beta) {
			    var h1y = ay - (ax-bx)/Math.tan(alpha);
				var h1x = ax + (ay-by)/Math.tan(alpha);

				var h2y = cy + (cx-bx)/Math.tan(beta);
				var h2x = cx - (cy-by)/Math.tan(beta);

				var t_h2_h1 = Math.atan2(  h1y-h2y, h1x-h2x);
				var t_b_n   = Math.atan2(-(h1x-h2x), h1y-h2y);

				var t_a_n = t_b_n - alpha;
				var t_c_n = t_b_n + beta;

				var xP1 = h2x + ((by-h2y)-(bx-h2x)*Math.tan(t_b_n)) / (Math.tan(t_h2_h1) - Math.tan(t_b_n));
				var yP1 = h2y + (xP1-h2x)*Math.tan(t_h2_h1);

				xP2 = ax  + ((cy-ay)-(cx-ax)*Math.tan(t_c_n)) / (Math.tan(t_a_n) - Math.tan(t_c_n));
				yP2 = ay  + (xP2-ax)*Math.tan(t_a_n);

				return [0.5*(xP1+xP2), 0.5*(yP1+yP2)];
			}
			
			this.collins = function(ax,ay,bx,by,cx,cy,alpha,beta) {
				var distAB = Math.sqrt((ax-bx)*(ax-bx) + (ay-by)*(ay-by));
				var t_a_b  = Math.atan2(by-ay, bx-ax);
				var distAC = Math.sqrt((ax-cx)*(ax-cx) + (ay-cy)*(ay-cy));
				var t_a_c  = Math.atan2(cy-ay, cx-ax);
				var gamma  = t_a_b - t_a_c;

				var distGH = -distAC * Math.sin(alpha)/Math.sin(alpha+beta)*Math.sin(beta);
				var distAG =  distAC * Math.sin(alpha)/Math.sin(alpha+beta)*Math.cos(beta);   
    
				var distFB = distAB * Math.sin(gamma);
				var distAF = distAB * Math.cos(gamma);
    
				var delta  = Math.atan2(distGH-distFB, distAG-distAF);
				if (delta>Math.PI)
					delta = delta - Math.PI;
   
				var t_a_p  = t_a_c + delta - alpha;
				var distAP = distAC * Math.sin(delta+beta)/Math.sin(alpha+beta);

				return [ax + distAP * Math.cos(t_a_p), ay + distAP * Math.sin(t_a_p)];
			
			}
			
			this.fontllagunesbatlle = function(ax,ay,bx,by,cx,cy,alpha,beta) {
			
				var pac = Math.sqrt( (ax+cx)*(ax+cx) + (ay+cy)*(ay+cy) );
				var pab = Math.sqrt( (ax+bx)*(ax+bx) + (ay+by)*(ay+by) );
				var pbc = Math.sqrt( (bx+cx)*(bx+cx) + (by+cy)*(by+cy) );

				var B = Math.acos((pac*pac - pab*pab - pbc*pbc)/(-2.0*pab*pbc));

				var o = Math.atan2(ay-by, ax-ay);

				var ma = 1.0/Math.tan(o);
				var mb = 1.0/Math.tan(o-alpha);
				var mc = 1.0/Math.tan(o-alpha-beta);
    
				var xPab = (ma*ax-mb*bx-ay+by) / (ma-mb);
				var yPab = ma*(xPab-ax) + ay;
    
				var xPbc = (mb*bx-mc*cx-by+cy) / (mb-mc);
				var yPbc = mb*(xPbc-bx) + by;
    
				var xPac = (ma*ax-mc*cx-ay+cy) / (ma-mc);
				var yPac = ma*(xPac-ax) + ay;
    
				var xCab = 0.5*(ax+bx+(ay-by)/Math.tan(alpha));
				var yCab = 0.5*(ay+by+(bx-ax)/Math.tan(alpha));
    
				var xCbc = 0.5*(bx+cx+(by-cy)/Math.tan(beta));
				var yCbc = 0.5*(by+cy+(cx-bx)/Math.tan(beta));
    
				var xCac = 0.5*(ax+cx+(ay-cy)/Math.tan(alpha+beta));
				var yCac = 0.5*(ay+cy+(cx-ax)/Math.tan(alpha+beta));
    

				var pP1 = Math.sqrt((xPab-xPbc)*(xPab-xPbc) + (yPab-yPbc)*(yPab-yPbc));
				var pC1 = Math.sqrt((xCab-xCbc)*(xCab-xCbc) + (yCab-yCbc)*(yCab-yCbc));
				
				var pP2 = Math.sqrt((xPab-xPac)*(xPab-xPac) + (yPab-yPac)*(yPab-yPac));
				var pC2 = Math.sqrt((xCab-xCac)*(xCab-xCac) + (yCab-yCac)*(yCab-yCac));

				var pP3 = Math.sqrt((xPbc-xPac)*(xPbc-xPac) + (yPbc-yPac)*(yPbc-yPac));
				var pC3 = Math.sqrt((xCbc-xCac)*(xCbc-xCac) + (yCbc-yCac)*(yCbc-yCac));
    
				var dO = [Math.asin(pP1/(2*pC1)), Math.asin(pP2/(2*pC2)), Math.asin(pP3/(2*pC3))].sort();

				var sig = [Math.signum( (xPab-xPbc)*(yCab-yCbc) - (xCab-xCbc)*(yPab-yPbc) ),
				           Math.signum( (xPab-xPac)*(yCab-yCac) - (xCab-xCac)*(yPab-yPac) ),
						   Math.signum( (xPbc-xPac)*(yCbc-yCac) - (xCbc-xCac)*(yPbc-yPac) )].sort();

				o += sig[1]*dO[1];
    
				var ma = 1.0/Math.tan(o);
				var mb = 1.0/Math.tan(o-alpha);
				var mc = 1.0/Math.tan(o-alpha-beta);
    
				var xPab = (ma*ax-mb*bx-ay+by) / (ma-mb);
				var yPab = ma*(xPab-ax) + ay;
    
				var xPbc = (mb*bx-mc*cx-by+cy) / (mb-mc);
				var yPbc = mb*(xPbc-bx) + by;
    
				var xPac = (ma*ax-mc*cx-ay+cy) / (ma-mc);
				var yPac = ma*(xPac-ax) + ay;
    
				var xn = [xPab, xPbc, xPac].sort();
				var yn = [yPab, yPbc, yPac].sort();
				
				return [xn[1], yn[1]];
			}
			
		}
		