// JavaScript Document
var aDefs = new Array();
aDefs[0] = 'Date'; aDefs[1] = 'District'; aDefs[2] = 'House Type'; aDefs[3] = 'Income'; aDefs[4] = 'Previous Customer'; aDefs[5] = 'Outcome';
// Each value also has a definition for each attribute
var vDefs = new Array();
vDefs[0] = new Array();
vDefs[1] = new Array();
vDefs[2] = new Array();
vDefs[3] = new Array();
vDefs[4] = new Array();
vDefs[5] = new Array();
//
vDefs[0][0] = '3/10/03';
vDefs[0][1] = '14/9/03';
vDefs[0][2] = '2/4/03';
vDefs[0][3] = '18/1/03';
vDefs[0][4] = '3/4/03';
vDefs[0][5] = '15/10/02';
vDefs[0][6] = '2/3/01';
vDefs[0][7] = '4/5/03';
vDefs[0][8] = '2/1/03';
vDefs[0][9] = '8/4/03';
vDefs[0][10] = '6/5/02';
//
vDefs[1][0] = 'Suburban';
vDefs[1][1] = 'Rural';
vDefs[1][2] = 'Urban';
//
vDefs[2][0] = 'Detached';
vDefs[2][1] = 'Semi-detached';
vDefs[2][2] = 'Terrace';
//
vDefs[3][0] = 'High';
vDefs[3][1] = 'Low';
//
vDefs[4][0] = 'No';
vDefs[4][1] = 'Yes';
//
vDefs[5][0] = 'Nothing';
vDefs[5][1] = 'Responded';

// the data in a 2D array. The indices represent the table position and the values correspond to vDefs
var tData = new Array();
tData[0] = new Array();
tData[0] = [0,1,2,3,4,5,5,6,7,8,0,0,9,10];
tData[1] = new Array();
tData[1] = [0,0,1,2,2,2,1,0,0,2,0,1,1,2]; //first col etc..
tData[2] = new Array();
tData[2] = [0,0,0,1,1,1,1,2,1,2,2,2,0,2]; //sec col
tData[3] = new Array();
tData[3] = [0,0,0,0,1,1,1,0,1,1,1,0,1,0];
tData[4] = new Array();
tData[4] = [0,1,0,0,0,1,1,0,0,0,1,1,0,1];
tData[5] = new Array();
tData[5] = [0,0,1,1,1,0,1,0,1,1,1,1,1,0];

// Function to determine what happens when an attribute node is clicked
function displayMatchesOption(thisPath,nodeT,fC) //params to match function leafStats
{
		mydiv = getE('popupMenu'); // The popup we're gonna use
    	initPopup(mydiv);
		mL = ct('a');
		matchLinkT = "JAVASCRIPT:checkPaths(" +array2StringPlus(thisPath,-1) +"," +nodeT +"," +fC +")";
		mL.setAttribute('href',matchLinkT);
		mL.appendChild(document.createTextNode('Highlight matches in data'));
		mydiv.appendChild(mL);
		mydiv.appendChild(ct('br'));
		mydiv.appendChild(ct('hr'));
		return;
}

function nodeOptions(pathArray,cell,leafPath)
{
	wipePopup = true;
	
	//show an option for displaying matches
	displayMatchesOption(leafPath,1,1);
	wipePopup = false;
		 // don't need other stuff for leaf node (unless it is restructured - SORT IT)
	if (pathArray == null)
	{
			return;
	}
	
	var cellC = getE(cell).parentNode.parentNode.childNodes.length;
	//alert(cellC);
	// Check whether node has been expanded yet:
	//alert(cellC);
	if ((browser.ie) && ( (cell=='-1-1' && cellC>1) || cellC>2 )) //IE
	{
		showPruneList(cell);
		return;
	}
	else if ((cell=='-1-1' &&cellC>2) || cellC>2) //Mozilla
	{
		showPruneList(cell);
		return;
	}
	//showStatsOption(pathArray);
	// array of pairs, first is attname, sec is value name etc etc...
    // Build up list of Attributes to put into SelectionPopup
	var attOptions = new Array();
	var count = 0;

		if (pathArray.length == 0) // root node only
		{
			// put all atts in
             for (var i=0; i < aDefs.length-1; i++)
			 {
			      attOptions[i] = i;
			 }
		}

		// Loop thru and check no attributes used already by looking at pathArray
		for (var i=0; i < aDefs.length-1; i++)
		{
				//alert(pathArray[x]);
				//alert('compared to ' +i);
				stop = false;
				for (var x = 0; x <pathArray.length; x++)
				{

				    if ((x%2==0) && (pathArray[x]==i))
				    {
				       stop =true;
				    }
				}
				if (stop==false)
				    {
				       //alert('inclduing' +i);
			    	   attOptions[count] = i;//append to list of possibles
				       count++;
				       stop = true;
				    }
		}

	// should have list of all atts not on path so far of node clicked on:
	// Now show choices in a DIV by calling this:
	
	showBuildList(pathArray,attOptions,cell);
}

function initPopup(divRef)
{
	// REMOVE any children from before
		deleteAllChildren(divRef);
		// Set up the popup DIV:
		divRef.style.left = mX; // from globals
		divRef.style.top = mY;
		divRef.style.visibility = 'visible';
}

function showBuildList(pathArray,attsArray,cell)
{
	mydiv = getE('popupMenu'); // The popup we're gonna use
	mydiv.className = 'build'; //make it look different
	
    //initPopup(mydiv);
	if (infoGain == true)
	{
		eSpan = ct('span');
		eSpan.className = ('entropy');
		properEntropy = calculateCurrentEntropy(pathArray,-1)
		eVal = 'ENTROPY:' + properEntropy;
		eSpan.appendChild(document.createTextNode(eVal.substring(0,13)));
		mydiv.appendChild(eSpan);
		mydiv.appendChild(ct('br'));
	}
		
	 // Get all remaing attributes from param and put in DIV asd links with appropriate calls:
	for (var x = 0; x < attsArray.length; x++)
	{
		eachAtt = ct("a");
		eachAttShow = aDefs[(attsArray[x])];
		
		if (infoGain == true)
		{
			 eString = '' +	(properEntropy - doEntropies(pathArray,attsArray[x],eventToAdd));
			  if (eString.indexOf('-') != -1)// small val bug, should be zero
			 {eString = '0';}
			 eachAttShow += ' : ' +eString.substring(0,5);
			 if (gainRatio == true)
			 {
				 	IG = properEntropy - doEntropies(pathArray,attsArray[x],eventToAdd);
					 //the important calc.
				    erString = '' + (IG / calculateCurrentEntropy(pathArray,attsArray[x]));
					if (erString.indexOf('NaN') != -1)
					{erString = '0';
					}
					eachAttShow += ' : ' +erString.substring(0,5);
			 }
		}
		eachAtt.appendChild(document.createTextNode(eachAttShow));
		eachAtt.appendChild(ct("br"));
		
		var eventToAdd = 'JAVASCRIPT:expandNode(';

		//temppath[pathArray.length] = attsArray[x];

		eventToAdd += array2StringPlus(pathArray,attsArray[x]);
		//eventToAdd += attsArray[x];
		eventToAdd += ',' +'\'' +cell +'\')';

		eachAtt.setAttribute('href',eventToAdd);
		mydiv.appendChild(eachAtt);
	}

}
// Make a link object from an array of ints (the path) and a new int value
function makeLink(path4link,appendy,forMatches)
{
	al = ct("a");
	al.appendChild(document.createTextNode('???'));
	newID = array2StringPlus(path4link,appendy);
	matchString = array2StringPlus(forMatches,-1);
	var hrefbit = 'JAVASCRIPT:nodeOptions(';
	hrefbit += array2StringPlus(path4link,appendy);
	hrefbit += ',\'' +array2String(path4link) +appendy +'\',' +matchString +')';
	al.setAttribute('href',hrefbit);
	al.className = 'normalNode';
	return al;
}

// Called from clickable attributes in popup DIV. Expands node with
// all values from attribute that was clicked on:
// Complicated nesting.
function expandNode(pathSoFar,celltoBuildIn)
{
		started = true;
	// hide select list
	getE('popupMenu').style.visibility = 'hidden';
	// DONE HIDING
	var cn = getE(celltoBuildIn);
	//alert(cn.childNodes.length);
	if (cn.childNodes.length > 1) // REBUILDING A LEAF NODE PUT N FROM PRUNING
	{
		domDeleteNode(cn.childNodes[1]);
		domDeleteNode(cn.childNodes[1]);
		domDeleteNode(cn.childNodes[1]);
		domDeleteNode(cn.childNodes[1]);
		clearChildStats(cn);
		showStats();
	}
	// get last number off path which will be att number
	var thisAtt = pathSoFar[pathSoFar.length-1];
	cn.childNodes[0].childNodes[0].nodeValue = aDefs[thisAtt];
	//cn.childNodes[0].style.fontWeight = 'bold';
	cn.childNodes[0].className = 'exNode'; // class
	//cn.childNodes[0].nodeValue = aDefs[thisAtt];
	// make new row with table in it:
	nrow = ct('tr'); // append the following
	//xcell = ct('td');
	ncell = ct('td');
	ncell.setAttribute('align','center');
	nrow.appendChild(ncell);
	ntab = ct('table');
	//ntab.setAttribute('border',1);
	ncell.appendChild(ntab);
	nbod = ct('tbody');
	ntab.appendChild(nbod);

	nn_row = ct('tr'); // row with ¬ in it
    nn_cell = ct('td');
    nn_cell.appendChild(document.createTextNode('|'));
    nn_cell.style.borderBottom = '1px solid #000000';
    nn_cell.setAttribute('colSpan',vDefs[thisAtt].length);
    // spans as many cells as there are values to put in
    nn_row.appendChild(nn_cell);
    nbod.appendChild(nn_row);
	nnrow = ct('tr');
	var attCorrect = 0;
	var Atttotal = 0;
	for (var y = 0; y < vDefs[thisAtt].length; y++)
	{
		nncell = ct('td'); // new! table goes in here
		nncell.setAttribute('vAlign','top');
		nnrow.appendChild(nncell);
		nntab = ct('table');
		nncell.appendChild(nntab);
		nnbod = ct('tbody');
		nntab.appendChild(nnbod);
		nnnirow = ct('tr');	//1st row in 2nd nested table
		nnnicell = ct('td');
		nnnicell.className = 'branchNode';
		nnnirow.appendChild(nnnicell);
		// VALUE AS A BRANCH (WITH '|' B4 AND AFTER
		nnn_text = document.createTextNode('|');
		nnnicell.appendChild(nnn_text);
		nnnicell.appendChild(ct('br'));
		nnntext = document.createTextNode(vDefs[thisAtt][y]);
		nnnicell.appendChild(nnntext);
        nnbod.appendChild(nnnirow);

		// Now do stats for this value of whatever attribute it branches from
		//pathSoFar[pathSoFar.length] = y;
		if (ratios == true)
		{
			var stringValue = calculateValue(pathSoFar,y);
			//works out  classification percentage for node
			nnnicell.appendChild(ct('br'));
			nnnicell.appendChild(document.createTextNode(stringValue));
		}

		// Now put in | branch after node value
			nnnicell.appendChild(ct('br'));
			nnnn_text = document.createTextNode('|');
			nnnicell.appendChild(nnnn_text);
			leafrow = ct('tr');
			leafcell = ct('td');
			leafcell.id = array2String(pathSoFar)+y;
			// Check IF leaf node now:
		if (!deadEnd)
		{
			finalPath = cloneArray(pathSoFar);
			finalPath.push(y);
			finalPath.push(vDefs.length-1);
			finalPath.push(leafValueIndex);
			if (leafValue != null) // stat type at each node
			{
				leafspan = ct('span');
				leafspan.className = 'leafNode';
				// make link and so table rows are highlighted
				leaflink = ct('a');
				
				if (leafStats == true){leafObj = checkPaths(cloneArray(finalPath),1,0);}
				leafevent = 'JAVASCRIPT:nodeOptions(null,null,' +array2StringPlus(finalPath,-1) +')';
				leaflink.setAttribute('href',leafevent);
				leaflink.appendChild(document.createTextNode(vDefs[vDefs.length-1][leafValue]));
				leafspan.appendChild(leaflink);
				leafcell.appendChild(leafspan);
				//alert('hey ' +leafStats);
				if (leafStats == true)
				{
					leafcell.appendChild(ct('br'));
					leafstatspanC = ct('span');
					leafstatspanC.className = 'correctStat';
					leafstatspanC.appendChild(document.createTextNode(leafObj.tCorrect +':' +leafObj.vCorrect));
					leafcell.appendChild(leafstatspanC);
					leafcell.appendChild(ct('br'));
					leafstatspan = ct('span');
					leafstatspan.className = 'wrongStat';
					leafstatspan.appendChild(document.createTextNode(leafObj.tWrong +':' +leafObj.vWrong));
					leafcell.appendChild(leafstatspan);
				}
			}
			//  ELSE ??? Node after each value
			else
			{
				alink = makeLink(pathSoFar,y,finalPath);
				leafcell.appendChild(alink);
			}
		}
		else // DEADEND (NO EXAMPLES IN TRAINING SET CLASSIFIED FOR THIS COMBINATION)
		{
			// put a null link in
			    leafspan = ct('span');
				leafspan.className = 'leafNode';
				// make link and so table rows are highlighted
				
				finalPath = cloneArray(pathSoFar);
				finalPath.push(y);
				finalPath.push(vDefs.length-1);
				finalPath.push(-1);
				if (leafStats == true){leafObj = checkPaths(cloneArray(finalPath),1,0);}
				if (validation == true)
				{
					leaflink = ct('a');
					leaflink.appendChild(document.createTextNode('null'));
					leafevent = 'JAVASCRIPT:nodeOptions(null,null,' +array2StringPlus(finalPath,-1) +')';
					leaflink.setAttribute('href',leafevent);
					leafspan.appendChild(leaflink);
				}
				else
				{
					leafspan.appendChild(document.createTextNode('null'));
				}
				
				leafcell.appendChild(leafspan);
				if (leafStats == true)
				{
					leafcell.appendChild(ct('br'));
					leafstatspanC = ct('span');
					leafstatspanC.className = 'correctStat';
					leafstatspanC.appendChild(document.createTextNode(leafObj.tCorrect +':' +leafObj.vCorrect));
					leafcell.appendChild(leafstatspanC);
					leafcell.appendChild(ct('br'));
					leafstatspan = ct('span');
					leafstatspan.className = 'wrongStat';
					leafstatspan.appendChild(document.createTextNode(leafObj.tWrong +':' +leafObj.vWrong));
					leafcell.appendChild(leafstatspan);
				}
		}
		leafrow.appendChild(leafcell);
		nnbod.appendChild(leafrow);
	}
	nbod.appendChild(nnrow);

	(cn.parentNode.parentNode).appendChild(nrow); // Back to parent table
	debugOut();
}

iCurrentEntropy = 0;

function doEntropies(pathNow,nextA,hrefA)
{
	var matchedData = getMatches(pathNow);
	Amatches = new Array();

	thisEntropy = new Array();
	var targets = new Array();
	for (var x=0; x<matchedData.length;x++)
	{
		targets[x] = tData[tData.length-1][(matchedData[x])];
	}
	var Atargets = new Array();
	for (var x=0; x<matchedData.length;x++)
	{
		Atargets[x] = tData[nextA][(matchedData[x])];
	}

	// need sets of targetvals for each att val
	doneVal = new Array();
	for (var x=0; x<Atargets.length;x++)
	{
		thisE = 0;
		subsetVals = new Array();
		if (!isIn(doneVal,Atargets[x]))
		{
				for (var y=0; y<Atargets.length; y++)
				{
					if (Atargets[x] == Atargets[y])
					{
						subsetVals.push(targets[y]);
					}
				}
	
			doneVal.push(Atargets[x]);
			// got subset of targetvals for this att val.
			doneTVal = new Array();
	
			for (var b=0; b < subsetVals.length; b++)
			{
				if (!isIn(doneTVal,subsetVals[b]))
				{
					valc = countValue(subsetVals,subsetVals[b]);
					//alert(' hey you ' +valc);
					thisE -= valc * convertlog(valc,2);
				}
				doneTVal.push(subsetVals[b]);
			}
			//alert(thisE);
			thisE += subsetVals.length * convertlog(subsetVals.length,2);
			//alert(thisE);
			thisE /= subsetVals.length;
			thisE *= subsetVals.length / Atargets.length;
					//alert(thisE);
				thisEntropy.push(thisE);
			}
	}
	//alert('yeeeh ' +array2String(thisEntropy));
	// sum all entropies for each partition of nextA
	totalAEntropy = 0;
	for (var x=0; x< thisEntropy.length; x++)
	{
		totalAEntropy += thisEntropy[x];
	}
	//thisEntropy += matchedData.length * convertlog(matchedData.length,2);
	//thisEntropy /= matchedData.length;
	// else:
	return totalAEntropy;
}

function getMatches(mypath)
{
   var mymatches = new Array();
	for (var x=0; x < tData[0].length; x++) // LOOP THROUGH EACH COLOUMN OF DATA
	{
		// CHECK THAT PATH VALUES ARE IN THAT COLUMN
		if (iRows[x] != 1) //ONLY TRAINING DATA
		{
			allOk = true
			for (var y=0; y < mypath.length; y++)
			{
				att = mypath[y]; y++;
				val = mypath[y]; // got both pairs
				if (tData[att][x] != val)
				{
					allOk = false; //one value in path does not match that in table row
				}
			}
			if (allOk == true) //all values in path were in row!
			{
				mymatches.push(x);
			}
		}
	}
	return mymatches;
}

function calculateCurrentEntropy(path,overWhich)
{
	// GET AN ARRAY OF ALL TARGET VALS CORRESPONDING TO INDEXES IN MATCHES
	var matches = getMatches(path);
	var targets = new Array();
	for (var x=0; x<matches.length;x++)
	{
		if (overWhich == -1)
		{
			targets[x] = tData[tData.length-1][(matches[x])];
		}
		else // work out GainRatio for other attribute
		{
			targets[x] = tData[overWhich][(matches[x])];
		}
	}
	var doneVal = new Array();
	var EntropySum = 0;

	for (var x=0; x<targets.length; x++)
	{
	       if (!isIn(doneVal,targets[x]))
	       {
	          //
	       	  occurs = countValue(targets,targets[x]);
	       	  EntropySum -= occurs * convertlog(occurs,2);
	          doneVal.push(targets[x]);
	       }
	}
	EntropySum += matches.length * convertlog(matches.length,2);
	return (EntropySum / matches.length);
}

function convertlog(x,base) // CONVERTS LOGe TO LOG2
{
    return ((Math.log(x)) / (Math.log(base)));
}

function showPruneList(node_id)
{
	mydiv = getE('popupMenu'); // The popup we're gonna use
	//initPopup(mydiv);
	mydiv.className = 'prune'; //make it look different
	dlink = ct("a");
	devent = 'JAVASCRIPT:pruneNode(\'' +node_id +'\',\'???\')';
	dlink.setAttribute('href',devent);
	dlink.appendChild(document.createTextNode('Prune node completely'));
	mydiv.appendChild(dlink);
	if (fullPruning == true)
	{
		mydiv.appendChild(ct('br'));
		for (var x=0; x < vDefs[vDefs.length-1].length; x++)
		{
			slink = ct("a");
			sevent = 'JAVASCRIPT:replaceNode(\'' +node_id +'\',' +x +')';
			slink.setAttribute('href',sevent);
			slink.appendChild(document.createTextNode('Replace with ' +vDefs[vDefs.length-1][x]));
			mydiv.appendChild(slink);
			mydiv.appendChild(ct('br'));
		}
	}
}
function pruneNode(node_id,valInstead)
{
	cp = getE(node_id).parentNode.parentNode.lastChild; //table body that holds node_id cell
	newID = node_id;
	
	if (node_id == '-1-1') //root
	{
		newID = '';
	}
	if (leafStats == true)
	{
		clearChildStats(cp);
		showStats();
	}
	deleteAllChildren(cp);
	deleteNode(cp);
	getE('popupMenu').style.visibility = 'hidden'; //hide popup
	// Change node link to be '???' in different style if not being replaced
	nodelink = getE(node_id).childNodes[0];
	if (valInstead == '???')
	{
		nodelink.childNodes[0].nodeValue = '???'; //text node in link
		nodelink.className = 'normalNode';
	}
	else// PRUNING WITH REPLACEMENT
	{
		// get node_id into string but append leaf val to it first
		newID += vDefs.length-1 ;
		newID += valInstead;
		arrayIn = string2Array(newID);
		newID = array2StringPlus(arrayIn,-1);
		//leafLink = 'JAVASCRIPT:checkPaths(' +newID +',1,1)';
		//
		leafUndo = 'JAVASCRIPT:nodeOptions(';
		cellID = string2Array(node_id);
		if (cellID == '-,1,-,1'){cellID = '';}
		leafUndo += '[' +cellID +']';
		leafUndo += ',' +'\'' +node_id +'\',' +newID +')';
		//leafLink += ';' +leafUndo;
		//
		nodelink.setAttribute('href',leafUndo);
		nodelink.childNodes[0].nodeValue = vDefs[vDefs.length-1][valInstead]; // text node in link of VAL
		nodelink.className = 'leafNode';
		leafObj = checkPaths(cloneArray(arrayIn),1,0);
		// sort out stats at leaf here
		getE(node_id).appendChild(ct('br'));
		leafstatspanC = ct('span');
		leafstatspanC.className = 'correctStat';
		leafstatspanC.appendChild(document.createTextNode(leafObj.tCorrect +':' +leafObj.vCorrect));
		getE(node_id).appendChild(leafstatspanC);

		getE(node_id).appendChild(ct('br'));
		leafstatspan = ct('span');
		leafstatspan.className = 'wrongStat';
		leafstatspan.appendChild(document.createTextNode(leafObj.tWrong +':' +leafObj.vWrong));
		getE(node_id).appendChild(leafstatspan);
	}
	debugOut();
}

function replaceNode(cellID,targetVal)
{
	// SORT STATS OUT FIRST
	pruneNode(cellID,targetVal);
}

//cross-browser
function deleteNode(node)
{
	if (browser.ie)
	{
		node.removeNode(true);
		if (getE('tree').childNodes[0].childNodes.length == 1)
		{
			started = false; //restart
		}
	}
	else
	{
		node.parentNode.removeChild(node);
		if (getE('tree').childNodes[1].childNodes.length == 2)
		{
			started = false; //restart
		}
	}
	clearRows();
}

function domDeleteNode(node)
{
	if (browser.ie)
	{
		node.removeNode(true);
	}
	else
	{
		node.parentNode.removeChild(node);
	}
}

function deleteAllChildren(parent)
{
		temp = parent.childNodes.length;
		for (var j=0; j<temp; j++)
		{
				// MOZ needs remove child method here
				if (browser.ie == 1)
				{
				     parent.childNodes[0].removeNode(true);
				}
				else
				{
				    parent.removeChild(parent.childNodes[0]);
				}
		}
}

function leafNode(elemID,tc,tw, vc, vw )
{
	this.ID = elemID;
	this.tCorrect = tc;
	this.tWrong = tw;
	this.vCorrect = vc;
	this.vWrong = vw;
	return this;
}

function treeStat()
{
	this.nodeNum =0;
	this.leafNum =0;
	this.tCorrectTotal =0;
	this.tWrongTotal =0;
	this.vCorrectTotal =0;
	this.vWrongTotal =0;
	return this;
}
iStat = new treeStat();

// WORK OUT RATIOS AT EACH NODE / BRANCH
var leafValue = null;
var leafValueIndex = -1; //to check if finished the value of the target att will be put here or not
var deadEnd = false; // to check for dead end
function calculateValue(pathNow, valNow)
{
	leafValue = null; //reset globals
	deadEnd = false;
	var rowNumbers = new Array();

	for (var x=0; x<pathNow.length; x++)
	{
			att = pathNow[x];
			if (x==pathNow.length-1)
			{
				val = valNow
			}
			else
			{
				x++;
				val = pathNow[x];
			}
			//alert('val for this att in path = ' +val);
			for (var y=0; y<tData[att].length; y++) //go through data in coloumn of att off path
			{
				//alert(tData[att][y]);
				if (tData[att][y] == val && iRows[y] != 1)
				{
					//alert('matches at row ' +y);
					rowNumbers.push(y); //row num in attribute coloumn where value matches value in tree
				}
			}
	}
	// Now look at row numbers to see which value occurs (pathNow.length+1)/2 times
	var targetRowNumbers = new Array();
	for (var c=0; c<rowNumbers.length;c++)
	{
		if (!isIn(targetRowNumbers,rowNumbers[c])) // if we have looked at this row already
		{
			if (countValue(rowNumbers,rowNumbers[c]) == (pathNow.length+1)/2)
			{
				targetRowNumbers.push(rowNumbers[c]); //target row to look at
			}
		}
	}
	// iterate through target values of targetRow Numbers and work out a fraction to return as String
	var correct = new Array();
	for (var d=0; d<targetRowNumbers.length;d++)
	{
		correct.push(tData[tData.length-1][targetRowNumbers[d]]);
		//leafValueIndex = tData[tData.length-1][targetRowNumbers[d]];
	}
	// Now work out which of correct answers is a majority
	highest = 0;
	//alert(correct.length);
	for (var e=0; e<correct.length; e++)
	{
		check =	countValue(correct,correct[e]);
		if ( check > highest )
		{
			highest = check;
			leafValue = correct[e];
		}
	}
	leafValueIndex = leafValue;

	if (highest != correct.length)
	{
		leafValue = null;
	}
	if (correct.length == 0)
	{
		deadEnd = true;
	}
	return '' +highest +'/' +correct.length;

}


function ratiosOn()
{
	if (ratios == false)
	{
		ratios = true;
	}
	else
	{
		ratios = false;
	}
}

// functions


function showStats()
{
	getE('tC').childNodes[0].nodeValue = iStat.tCorrectTotal;
	getE('tW').childNodes[0].nodeValue = iStat.tWrongTotal;
	getE('vC').childNodes[0].nodeValue = iStat.vCorrectTotal;
	getE('vW').childNodes[0].nodeValue = iStat.vWrongTotal;
}

function clearChildStats(rootNode)
{
	//alert(rootNode.id);
	for (var a=0; a < iLeaves.length; a++)
	{
		if (iLeaves[a] && rootNode.id == iLeaves[a].ID)
		{
			//alert(iLeaves[a].ID +' has wrong ' +iLeaves[a].tWrong +' and ' +iLeaves[a].vWrong);
			iStat.tCorrectTotal -= iLeaves[a].tCorrect;
			iStat.tWrongTotal -= iLeaves[a].tWrong;
			iStat.vCorrectTotal -= iLeaves[a].vCorrect;
			iStat.vWrongTotal -= iLeaves[a].vWrong
			iLeaves[a] = null;
		}
	}
	for (var x=0; x < rootNode.childNodes.length; x++)
	{
		clearChildStats(rootNode.childNodes[x]);
	}
}

function checkPaths(pathArray,nodeType,fCall)
{
	/*if (fCall == 1)
	{
			mydiv = getE('popupMenu'); // The popup we're gonna use
			initPopup(mydiv);
			mL = ct('a');
			matchLinkT = "JAVASCRIPT:showMatches(" +array2String(pathArray) +nodeType +fCall +")";
			mL.setAttribute('href',matchLinkT);
			mL.appendChild(document.createTextNode('Highlight matches in data'));
			mydiv.appendChild(mL);
			var NORESET = true; //GLOBAL TO STOP POPUP RESETTING WHEN BUILD LIST APPEARS AT PRUNED LEAF
			return;
	}*/
	clearRows();
	var targetA = -1;
	var targetV = -1;
	var matchedIndexes = new Array();
	var largestClassIndexes = new Array();
	var otherClassIndexes = new Array();
	if (nodeType == 1) //i.e. LEAF NODE
	{
		targetV = pathArray.pop(); //last one
		targetA = pathArray.pop(); //2nd last one
	}
	for (var x=0; x < tData[0].length; x++) // LOOP THROUGH EACH COLOUMN OF DATA
	{
		// CHECK THAT PATH VALUES ARE IN THAT COLUMN
		allOk = true
		for (var y=0; y < pathArray.length; y++)
		{
			att = pathArray[y]; y++;
			val = pathArray[y]; //got both pairs
			if (tData[att][x] != val)
			{
				allOk = false; //one value in path does not match that in table row
			}
		}
		if (allOk == true) //all values in path were in row!
		{
			matchedIndexes.push(x);
		}
	}

	tTCnow = 0;
	tTWnow = 0;
	tVCnow = 0;
	tVWnow = 0;

	if (nodeType ==1)
	{
		for (var z=0; z < matchedIndexes.length; z++)
		{
			r = getE('d_' +matchedIndexes[z]);
			if (tData[tData.length-1][(matchedIndexes[z])] == targetV)
			{
				if (fCall == 1) //NODE CLICKED ON
				{
					r.className = 'correctColor';
				}
				else // LEAF REACHED
				{
					if (r.parentNode.parentNode.id == 'dataTable')
					{
						tTCnow++;
					}
					else
					{
						tVCnow++;
					}
				}
			}
			else
			{
				if (fCall == 1) //NODE CLICKED ON
				{
					r.className = 'wrongColor';
				}
				else
				{
					if (r.parentNode.parentNode.id == 'dataTable')
					{
						tTWnow++;
					}
					else
					{
						tVWnow++;
					}
				}
			}
		}
	}
	if (fCall == 0)
	{
		iStat.tCorrectTotal += tTCnow;
		iStat.vCorrectTotal += tVCnow;
		iStat.tWrongTotal += tTWnow;
		iStat.vWrongTotal += tVWnow;
		leafObj = new leafNode(array2String(pathArray),tTCnow,tTWnow,tVCnow,tVWnow )
		iLeaves.push(leafObj);
		// We need this (^) and so we can decrement stats when nodes are pruned or whatever
		showStats();
		return leafObj;
		// at this point
	}

	else //move doc scroll up to data
	{
		document.body.scrollTop = 0;
		if (oldLeafInspect != null)
		{
			oldLeafInspect.style.border = 'none';
		}
		oldLeafInspect = targetElem.parentNode.parentNode;
		targetElem.parentNode.parentNode.style.border = '2px solid #000000';
	}
}

function clearRows()
{
	var allRows = document.getElementsByTagName('tr');
	for (var x=0; x< allRows.length; x++)
	{
		if ( (allRows[x].className == 'correctColor') || (allRows[x].className == 'wrongColor') )
		{
			allRows[x].className = '';
		}
	}

}

function browser()
{
   if (navigator.userAgent.indexOf("MSIE") > 0)
   {
       this.ie = 1;
   }
   else
   {
       this.ie = 0;
   }
   return this;
}
var browser = new browser(); // grab a ref to it

function countValue(myList, myVal)
{
	var mycount = 0;
	for (var x=0; x<myList.length; x++)
	{
		if (myVal == myList[x])
		{
			mycount++;
		}
	}
	return mycount;
}

function cloneArray(copyA)
{
	var returnthis = new Array();
	for (var x=0; x< copyA.length; x++)
	{
		returnthis[x] = copyA[x];
	}
	return returnthis;
}
function isIn(myList, myVal)
{
	for (var x=0; x<myList.length; x++)
	{
		if (myVal == myList[x])
		{
			return true;
		}
	}
	return false;
}

function debugOut()
{
	//getE('info').innerText = getE('tree').outerHTML;
}

function ct(tagname)
{
	return document.createElement(tagname);
}

function getE(elemID)
{
	return document.getElementById(elemID);
}

// Converts an array [x,y,z] and an item a into a string '[x,y,z,a]'
function array2StringPlus(ar,appendit)
{
	var val = '[';
	if (ar.length != 0)
	{
			for (var y = 0; y < ar.length; y++)
			{
				val += ar[y];
				if (y!=ar.length-1) //not last
				{	val += ',';
				}
			}
	}
	if (appendit != -1)
	{
				if (ar.length != 0)
				{val += ',';}
				val += appendit;
	}
			val += ']';

	return val;

}
// Just with an array
function array2String(arr)
{
	var rval = '';
	if (arr.length != 0)
	{
			for (var y = 0; y < arr.length; y++)
			{
				rval += arr[y];
			}
	}

	return rval;
}

function string2Array(myString)
{
	var toReturn = new Array();
	for (var x=0; x<myString.length; x++)
	{
		toReturn[x] = myString.charAt(x);
	}
	return toReturn;
}

// ALL FOR MOUSEOVER COLOR ON ROWS:
function setup()
{
	allTRs = document.getElementsByTagName('tr');
	for (var x=0; x < allTRs.length; x++)
	{
		if (allTRs[x].id.indexOf('d_') != -1)
		{
			allTRs[x].onmouseover = colorIt;
			allTRs[x].onmouseout = stopIt;
		}
	}
}

function colorIt(evt)
{
	if (started == false)
	{
		evt = (evt) ? evt: (window.event) ? window.event : "";
		if (browser.ie)
		{
			hey = evt.srcElement.parentNode;
		}
		else
		{
			hey = evt.target.parentNode;
		}
		hey.style.backgroundColor = '#A9EAF1';
	}
}

function stopIt(evt)
{
	if (started == false)
	{
		evt = (evt) ? evt: (window.event) ? window.event : "";
		if (browser.ie)
		{
			hey = evt.srcElement.parentNode;
		}
		else
		{
			hey = evt.target.parentNode;
		}
		hey.style.backgroundColor = '';
	}
}

// Need to get mouse coords as globals to determine where to put the DIV
document.onclick = registerMouse;
var mX = 0; var mY = 0; //for popup
var targetElem;
function registerMouse(evt) // MOVES ROWS FROM TABLE TO TABLE
{
	evt = (evt) ? evt: (window.event) ? window.event : "";
	if (evt.pageX)
	{
		mX = evt.pageX +"px";
		mY = evt.pageY +"px";
		
	}
	else
	{
		mX = evt.x + document.body.scrollLeft + document.documentElement.scrollLeft;
		mY = evt.y + document.body.scrollTop + document.documentElement.scrollTop;
	}

	if (browser.ie)
	{
		targetElem = evt.srcElement;
	}
	else
	{
		targetElem = evt.target;
	}
	//alert(targetElem.parentNode.parentNode.parentNode.id);
	// VALIDATION DATA MOVEMENT
	if (validation == true && started == false && targetElem.parentNode.className != 'dataHeaders' && targetElem.parentNode.id.indexOf('d_') != -1)
	{
		whichTab = targetElem.parentNode.parentNode.parentNode;
		if (whichTab.id == 'dataTable')
		{
			if (browser.ie && whichTab.childNodes[0].childNodes.length > 2)
			{
				rowToMove = targetElem.parentNode.parentNode.removeChild(targetElem.parentNode);
				getE('validTable').childNodes[0].appendChild(rowToMove);
			}
			else if ( (!browser.ie) && (whichTab.childNodes[1].childNodes.length > (tData[0].length*2)+3-tData[0].length) )
			{
				//alert(whichTab.childNodes[1].childNodes.length);
				rowToMove = targetElem.parentNode.parentNode.removeChild(targetElem.parentNode);
				getE('validTable').childNodes[1].appendChild(rowToMove);
			}
			rowIndex = rowToMove.id.substring(2,rowToMove.id.length);
			iRows[rowIndex] = 1; // SETS A FLAG TO MAKE SURE WE CAN CHECK THAT THIS ROW IS IN VALID DATA TABLE
		}
		else if (whichTab.id == 'validTable') // move back to data table
		{
			if (browser.ie)
			{
				rowToMove = targetElem.parentNode.parentNode.removeChild(targetElem.parentNode);
				getE('dataTable').childNodes[0].appendChild(rowToMove);
			}
			else
			{
				rowToMove = targetElem.parentNode.parentNode.removeChild(targetElem.parentNode);
				getE('dataTable').childNodes[1].appendChild(rowToMove);
			}
			rowIndex = rowToMove.id.substring(2,rowToMove.id.length);
			iRows[rowIndex] = 0; // SETS A FLAG BACK TO 0 SO WE KNOW THIS ROW IS NOW BACK IN DATA TABLE
		}
		rowToMove.style.backgroundColor = '';
	}

}

var started = false;
var iRows = new Array();
var iLeaves = new Array();
var oldLeafInspect = null;
var ratios = true;
var leafStats = false;
var infoGain = true;
var gainRatio = false;
var fullPruning = false;
var validation = false;