function getFullProgram(sched)
{ 	
	var sMPW = prParams["mpwe"];
	var diffCode = getDiffCode(prParams["diff"]);
	var isMara = isMarathon(); 
	
	// For people in the 6-10 mile per week category, we need to provide 
	// a special beginning program. Then we append the program from 11-15.
	if(sMPW <= 10)
	{ 
		var aw = new Array(); 
		for(var i = 0; i < prParams["slen"] && i < 4; i++)
			{ aw[i] = new lprepWeek(i, sched, diffCode); }
		
		if(prParams["slen"] > 4)
		{ 
			prParams["mpwe"] = 11; 
			prParams["slen"] = prParams["slen"] - 4;
			var nSched = new defaultScheduler(); 
			var newProg = getFullProgram(nSched);  
			
			for(var i = 0; i < nSched.getProgLength(); i++)
				{ aw[aw.length] = newProg[i]; } 
		} 
		return aw; 
	}
	
	var aw = new Array();
	// Currently, we seem to only have the codes for the marathon progression. 
	var weekCodes = isMara ? mWeekCodes(sched.getProgLength()) : bWeekCodes(sched.getProgLength());    
	
	var targDist = sMPW;
	var distMod = true;
	var prevWeek = null; 
	
	
	for(var i = 0; i < sched.getProgLength(); i++)
	{ 
		// calculate the mileage increment. 
		aw[i] = isMara ? maraWeekObj(targDist, sched, weekCodes[i]) : weekObj(targDist, sched, weekCodes[i], prevWeek);
		aw[i].setFromPrevWeeks(aw, sched); 
		aw[i].allocateDist(); 
				
		var a = 0; 
		if(aw[i].addDist())
		{ 
			if(diffCode <= 1) { a = sMPW > 30 ? 2 : 1; } 
			else
			{ 
				// Hard/Very Hard mileage increases - see 
				// "Mileage Increases" section of spec
				a = sMPW > 30 ? 2 : 1;
				a = distMod ? a + 1 : a; 
				distMod = !distMod; 
			}
			targDist += a; 
		}
		prevWeek = aw[i]; 
	} 	
	return aw; 
} 

/**
 * Factory method which returns a week object of the appropriate type. 
 * For standard training program, this can be a Basic Week, Recovery Week, 
 * or Taper Week
 */ 
function weekObj(targDist, sched, wkCde, prevWeek)
{
	var wInd = wkCde.match(/\d+/); 
	wInd = wInd*1.0; 

	if(wkCde.indexOf("r") != -1)
		{ return new rcovrWeek(Math.floor(targDist*.75), sched); } 
	if(wkCde.indexOf("t") != -1)
		{ return new btaprWeek(targDist*.75, sched); } 
	else
		{ return new basicWeek(targDist, sched, wInd); }  
}

function genWeek(tDist)
{ genWeekApply(this, tDist); } 

/** 
 * This is a hack to circumvent the fact that Mac IE does not support 
 * genWeek.apply(this, [...])
 */ 
function genWeekApply(gw, tDist)
{ 
	gw.targDist = tDist;
	gw.days = new Array(); 
	gw.dispData = "GENERIC";  
	
	for(var i = 0; i < 7; i++)
		{ gw.days[i] = new dayObj(); } 
} 

genWeek.prototype.getSpecRun = function()
{  
	for(var i = 0; i < 7; i++) 
	{ 
		if(isSpecRun(this.days[i])) 
		{ return this.days[i]; } 
	} 
	return null; 
} 

genWeek.prototype.getLongRun = function()
{ 
	for(var i = 0; i < 7; i++) 
	{ if(this.days[i].cde == "lgrn") { return this.days[i]; } } 
	return null; 
} 

genWeek.prototype.addDist = function()
{ return false; } 

genWeek.prototype.realDist = function()
{	 
	var x = 0; 
	for(var i = 0; i < 7; i++) 
	{
		if(this.days[i] == null) { logErr("Warning>> null days object."); }
		else { x = x + this.days[i].dst; }  
	}         
	return x; 
}

genWeek.prototype.totalDistDisp = function(met)
{
	var tot = 0;
	for(var i = 0; i < 7; i++) 
	{
		var dayDist = (this.days[i].dst * (met ? 1.6 : 1)).toFixed(); // round to nearest integer
		tot += parseInt(dayDist);
	}         

	return tot + (met ? " km" : " miles");
}

/** 
* Distributes weekly distance to the days that can "accept" more distance. 
*/ 
genWeek.prototype.allocateDist = function()
{ 
	var x = this.realDist(); 
	var curRegDist = 2;  
	
	// In principle, this should be an infinite loop, but that screws me 
	// up all the time. 
	for(var j = 0; j < 100; j++)
	{ 
		for(var i = 0; x < this.targDist && i < 7; i++)
		{ 
			if(this.days[i].addDist(curRegDist)) { x++; } 
		} 
		
		curRegDist += 1; 
		
		if(x >= this.targDist) { break; } 
	}
} 

/** 
* Sets the training days from sched to be regular (EZ) runs. 
*/ 
genWeek.prototype.setRegRuns = function(sched)
{ 
	// Training days per week depends on CURRENT mileage for standard training, 
	// but on STARTING mileage for marathon training. See "Workout Days" section 
	// in spec.
	var tds = isMarathon() ? 	sched.getTrainDays(prParams["mpwe"]) : 
	sched.getTrainDays(this.targDist);
	
	for(var i = 0; i < tds.length; i++)
		{ this.days[tds[i]] = new regRun(); } 
} 

genWeek.prototype.getDisplayInfo = function()
{ return this.dispData;  } 

genWeek.prototype.setFromPrevWeeks = function(prevWeeks)
{ } 

function dayObj()
{ dayObjApply(this); } 

function dayObjApply(dObj)
{
	/** 
	 * This is the total distance for the run. 
	 */ 
	dObj.dst = 0; 
	dObj.cde = "rust";
	dObj.paceWeek = -1; 
} 

dayObj.prototype.getDescLine = function(lNum)
{ 
	var dd = this.getDispData(); 
	if(lNum >= dd.length) 
		{ return ""; } 
	return dd[lNum]; 
} 

dayObj.prototype.getDispData = function()
{     
    return new Array("Rust /", " XT"); 
} 

dayObj.prototype.getDist = function()
{ return this.dst; } 

dayObj.prototype.getPaceCode = function()
{ return "xpacex"; }

/**
* Called to add distance to a day. BUT only has an effect for regular and special run 
* days! Returns true if the distance was successfully added. 
*/ 
dayObj.prototype.addDist = function(curRegDist)
{ return false; } 

regRun.prototype = new dayObj(); 
function regRun()
{ 
	dayObjApply(this); 
	this.cde = "Licht"; 
	
	// Regular runs must be at least two miles. 
	this.dst = 2; 
} 

regRun.prototype.addDist = function(curRegDist)
{ this.dst = this.dst + 1; return true; } 

regRun.prototype.getDispData = function()
{ 
	var dd = new Array(); 
	dd[0] = "Draven"; 
	dd[1] = "Afst:&nbsp\;" + getDistDispStr(this.dst, isMetric()) + "<br />in " + this.getPaceCode() + " min/km"; 
	return dd; 
} 


longRun.prototype = new dayObj(); 
function longRun(wkDst)
{ 
	dayObjApply(this); 
	this.dst = 4; 
	var dFlags = new Array(8, 12, 16, 20, 24, 28, 32, 36, 40, 47, 53, 60, 68, 77, 84, 93, 100); 
	
	for(var i = 0; i < dFlags.length; i++) 
		{ if(wkDst > dFlags[i]) this.dst++; }         
	this.str = "Duurloop"; 
	this.cde = "lgrn"; 	
} 

function longRunProg(sMPW, wInd)
{ 
	var lr = new longRun(); 	
	var bDst = Math.round((sMPW - 11)/5) + 5; 
	var addTo = Math.ceil(wInd/2-.005); 
	lr.dst = bDst + addTo;
	return lr; 
} 


longRun.prototype.getDispData = function()
{ 
	var dd = new Array(); 
	dd[0] = "Duurloop";
	dd[1] = "Afst:&nbsp\;" + getDistDispStr(this.dst, isMetric()) + "<br />in " + this.getPaceCode() + " min/km";
	return dd; 
} 



/**
* Returns the appropriate paced run object corresponding to the given week. This information can 
* now depend on the distinction between 5K/10K/half-marathon. 
*/ 
function pacedRunObj(cMPW, wInd)
{ 
	var mn = Math.floor((wInd-1)/3); 
	var wi = wInd % 3; 

	// This code implements the Marker system, by incrementing the starting 
	// month number based on the starting MPW.	
	var reInd = mn; var rsInd = mn; var trInd = mn; 
	if(prParams["mpwe"] > 45) {
		reInd += 1; 
		rsInd += 1; 
		trInd += 2; 
	} 
	else if(prParams["mpwe"] > 30) { 
		reInd += 1; 
		rsInd += 1; 
		trInd += 1; 
	} 
	
	var trAr = new Array(3, 4, 5, 6, 7, 8, 9); 
	
	if(prParams.rdst.indexOf("fivk") != -1) 
	{ 
		if(wi == 1)
			{ return new repeatEight(cMPW, reInd); } 
		if(wi == 0)
			{ return new repeatSixteen(cMPW, rsInd); } 
		if(wi == 2) 
			{ return new tempoRun(trAr[trInd]); } 
	} 
	else if(prParams.rdst.indexOf("tenk") != -1) 
	{ 
		if(wi == 2)
			{ return new repeatEight(cMPW, reInd); } 
		else
			{ return new tempoRun(trAr[trInd]);   } 
	} 
	else if(prParams.rdst.indexOf("half") != -1) 
	{ 
		if(wi == 2)
			{ return new repeatSixteen(cMPW, rsInd); } 
		else
			{ return new tempoRun(trAr[trInd]); } 
	} 

	return null; 
} 


tempoRun.prototype = new dayObj(); 

/** 
 * Creates a tempo Run from a distance which represents the actual length of 
 * the core run component. 2 miles of additional distance is added, and the 
 * total distance is rounded up. 
 */ 
function tempoRun(trDst)
{ 
	dayObjApply(this); 
	this.cde = "temp";

	this.num = trDst; 
	this.dst = Math.ceil(trDst) + 2; 
} 

function getTempoRunFromCMPW(cMPW)
{ 		
	logErr("Tempo run from current MPW: " + cMPW); 
	var trAr = new Array(3, 3.5, 4, 5);  
	var tFlags = new Array(15, 30, 45); 
	var x = 0; 
	
	for(var i = 0; i < tFlags.length; i++)
		{ if(cMPW > tFlags[i]) x++; }
 	
	logErr("Tempo run from current MPW: " + cMPW); 	
	logErr("TTempo run distance: " + trAr[x]);

	
	return new tempoRun(trAr[x]); 
} 

tempoRun.prototype.getDispData = function()
{   
	var dd = new Array(); 
	dd[0] = "Tempoloop";
    dd[1] = "Afst: " + getDistDispStr(this.dst, isMetric()) + ", inc.<br />"; 
    dd[1] = dd[1] + "Warm\; " + getDistDispStr(this.num, isMetric()) + " in " + this.getPaceCode() + " min/km\; uitlopen"; 
	return dd; 
} 

repeatEight.prototype = new dayObj(); 

/** 
 * Constructs a x800 special run from the current weekly mileage and the 
 * month number of the program. The cMPW is currently IGNORED, however, this is
 * a somewhat controversial point, so it remains in the function. See special 
 * run section in specs document for the origins of these distance progressions. 
 */ 
function repeatEight(cMPW, mn)
{ 
	dayObjApply(this); 
	this.cde = "y800"; 
	
	var trAr = new Array(3, 4, 5, 6, 7, 8); 
	var dsAr = new Array(5, 5, 6, 7, 8, 8); 

	/* 
	var mpwFlags = new Array(20, 30, 40); 
	var x = 0; 
	
	for(var i = 0; i < mpwFlags.length; i++)
		{ if(cMPW > mpwFlags[i]) x++; }
	*/ 
	
	this.num = trAr[mn]; 
	this.dst = dsAr[mn]; 	
} 

repeatEight.prototype.getDispData = function()
{ 
	var dd = new Array(); 
	dd[0] = "Snelheid";
    dd[1] = "Afst: " + getDistDispStr(this.dst, isMetric()) + ", inc.<br />"; 
    dd[1] = dd[1] + "Warm\; " + this.num + "x 800 in " + this.getPaceCode() + " min/800m<br>";
    dd[1] = dd[1] + "w/400 dribbelpauzes\; uitlopen"; 
	return dd; 
} 

repeatSixteen.prototype = new dayObj(); 

/** 
 * Constructs a x1600 object from the current weekly mileage and the month 
 * number. Currently the cMPW is IGNORED. 
 */ 
function repeatSixteen(cMPW, mn)
{ 
	dayObjApply(this);
	var trAr = new Array(2, 3, 4, 5, 6); 
	var dsAr = new Array(5, 7, 8, 10, 11); 
	
	/* 
	var mpwFlags = getRSFlags(); 
	var x = 0; 	
	for(var i = 0; i < mpwFlags.length; i++)
		{ if(cMPW > mpwFlags[i]) x++; }
	*/ 
		
	this.num = trAr[mn]; 
	this.dst = dsAr[mn]; 
	this.cde = "y160"; 
} 

repeatSixteen.prototype.getDispData = function()
{ 
	var dd = new Array(); 
	dd[0] = "Snelheidswerk";
    dd[1] = "Afst: " + getDistDispStr(this.dst, isMetric()) + ", inc.<br />"; 
    dd[1] = dd[1] + "Warm\; " + this.num + "x 1600 in " + this.getPaceCode() + " min/km<br />"; 
    dd[1] = dd[1] + "w/800 dribbelpauzes\; uitlopen"; 
	return dd; 
} 

repeatSixteen.prototype.addDist = function(curRegDist)
{ 
	// If curRegDist above srDst, increment; otherwise no. 
	// basically this should stay flat until reg run dist start to rise above it. 
	if(curRegDist > this.dst)
	{ 
		this.dst += 1; 
		return true; 
	} 
	return false; 
} 
repeatEight.prototype.addDist = repeatSixteen.prototype.addDist; 
tempoRun.prototype.addDist = repeatSixteen.prototype.addDist; 

function raceDay()
{ 
	dayObjApply(this);
	this.dst = raceLengthFromCode(prParams["rdst"]); 
	this.cde = "race"; 	
} 
raceDay.prototype = new dayObj(); 

raceDay.prototype.getDispData = function()
{ 
	var tg = new trainGoal(); 
	var dd = new Array(); 
	dd[0] = "Race Dag";
	dd[1] = "Dist:&nbsp;" + (new trainGoal()).opts[dataObj["rdst"]] + "<br>in " + this.getPaceCode() + " min/km Succes!";
	dd[1] = dd[1] + "<br />Tijd: xtimex";
	return dd; 
} 

function maraDispData()
{ 
	var dd = new Array(); 
	dd[0] = "Marathon Race Dag"; 
	dd[1] = "42.195 km  <br />in " + this.getPaceCode() + " min/km";
	dd[1] = dd[1] + "<br />Tijd: xtimex";
	dd[2] = "Succes!!!"; 
	return dd; 
} 

function raceLengthFromCode(cde)
{ 
	switch(cde) { 
		case "fivk" 	: return 3; 
		case "tenk1" 	: return 5; 
		case "tenk2"	: return 5; 
		case "tenk3"	: return 6; 
		case "tenk4" 	: return 8; 
		case "half1"	: return 9; 
		case "half2"	: return 10; 
		case "half4"	: return 13; 
		case "mara"		: return 26;
		default			: return 10; // shouldn't happen...
	} 
} 

/**
 * This is a hack to get around the fact that Mac IE does not support "instanceof" operation. 
 */ 
function isSpecRun(rObj)
{ 
	var c = rObj.cde; 
	return (c == "y800" || c == "temp" || c == "y160");
}

btaprWeek.prototype = new genWeek();

/** 
 * Taper week for standard training. Need to provide the special run from the 
 * second week of the program, which is copied and inserted into this week.
 * Specifically DO NOT call genWeekApply, because that work is done in 
 * setFromPrevWeeks() code
 */ 
function btaprWeek(tDist, sched)
{ 	
	this.dispData = "Standard-Tapering";  
	this.cde = "btapr"; 
} 


/** 
 * Most of the data for this week is taken from the previous weeks. 
 * The easy runs and long run are 50% of the immediately previous week, 
 * while the special run comes from the first week of the program. 
 */ 
btaprWeek.prototype.setFromPrevWeeks = function(prevWeeks, sched)
{
	var mpw = prParams["mpwe"]; 
	var dAr; 
	var sDist = 2; 
	
	if(mpw <= 15) 
	{ 
		dAr = new Array(); 
		dAr[0] = 2;  
	} 
	else if(mpw <= 25)
	{
		dAr = new Array(2, 3); 
		sDist = 3; 
	} 
	else if(mpw <= 40)
	{ 
		dAr = new Array(2, 3, 5);
		sDist = 4; 
	} 
	else
	{ 
		dAr = new Array(2, 3, 5); 
		sDist = 5; 
	} 	
	
	this.days = new Array(7); 
	for(var i = 0; i < 7; i++)
	{ this.days[i] = new dayObj(); } 
	
	for(var i = 0; i < dAr.length; i++)
	{ 
		this.days[dAr[i]] = new regRun(); 
		this.days[dAr[i]].dst = sDist; 
	} 
	
	// Special Run from first week that actually has a special run. 
	for(var i = 0; i < prevWeeks.length; i++)
	{ 
		if(prevWeeks[i].getSpecRun() != null)
		{ 
			this.days[4] = mClone(prevWeeks[i].getSpecRun()); 
			break; 
		} 
	} 
	
	// Race day is assumed to be on Sunday
	this.days[0] = new raceDay(); 
	
	this.targDist = this.realDist(); 
} 

btaprWeek.prototype.allocateDist = function() { } 


function Scheduler()
{ 
	// Should never be called
} 

Scheduler.prototype.longRunDay = function()
{ return this.lrday; } 

/**
 * Returns the paced run day for this scheduler. The paced run day is always the 
 * training day that is "farthest" from the Long Run day. 
 */
Scheduler.prototype.pacedRunDay = function(mpw)
{ 
	var trDays = this.getTrainDays(mpw); 
	var prtarg = (this.lrday + 3.5) % 7; 
	
	var prd = 10000; 
	
	for(var i = 0; i < trDays.length; i++)
	{ 
		prd = Math.abs(prd - prtarg) < Math.abs(trDays[i] - prtarg) ? prd : trDays[i]; 
	} 
	return prd; 
} 

/** 
 * This function returns the length of the program 
 */ 
Scheduler.prototype.getProgLength = function()
{ 
	// Dont *think* I need to check for errors here...?
	return Math.round(prParams["slen"]*1.0);  
} 

/**
* Returns an array of days the user trains on. 
* If this has not been set explicitly, returns the default workout days. 
* See "Workout Days" section of specs doc. 
*/ 
Scheduler.prototype.getTrainDays = function(mpw) 
{ 
	var nDays = 3;  	
	
	if(prParams.rdst == "mara")
	{ 
		if(mpw > 10) { nDays = 2; } 
		if(mpw > 15) { nDays = 3; } 
		if(mpw > 25) { nDays = 4; } 
		if(mpw > 40) { nDays = 5; } 
		if(mpw > 46) { nDays = 6; } 
	} 
	else
	{ 
		nDays = 3; 
		if(mpw > 15) { nDays = 4; } 
		if(mpw > 25) { nDays = 5; } 
		if(mpw > 40) { nDays = 6; } 
	} 
	
	var btds = baseTrainDays(nDays); 
	
	for(var i = 0; i < btds.length; i++)
		{ btds[i] = (btds[i] + this.lrday) % 7; }
	
	return btds; 
} 

function baseTrainDays(nDays)
{ 
	if(nDays == 2) 
		{ return new Array(0, 4); } 
	if(nDays == 3) 
		{ return new Array(0, 2, 4); } 
	if(nDays == 4)
		{ return new Array(0, 2, 4, 5); } 
	if(nDays == 5)
		{ return new Array(0, 2, 3, 4, 5); } 
	if(nDays == 6)
		{ return new Array(0, 2, 3, 4, 5, 6); } 	
} 

defaultScheduler.prototype = new Scheduler(); 

function defaultScheduler()
{ 
	//Scheduler.apply(this, [])
	this.lrday = prParams["lrdy"] * 1.0; 
} 

function getRaceDistDict()
{
	var pcob = new Object();
	pcob["onem"] = 1609;
	pcob["fivk"] = 5000;
	pcob["tenk1"] = 8000; // 8K
	pcob["tenk2"] = 5*1609; // 5 miles 
	pcob["tenk3"] = 10000; // 10 k
	pcob["tenk4"] = 12000; // 12 k
	pcob["half1"] = 15000; // 15k
	pcob["half2"] = 10*1609; // 10 miles
	pcob["half3"] = 20000; // 20 K 
	pcob["half4"] = 21097; // Half-Marathon
	pcob["mara"] = 42194;
	return pcob;
}

function logRegressionPaceCoef(meters)
{
 	// 1609=1.12, 5000=1.00, 10000=.96, 21097=.91, 42194=.87
	alph = -.0752;
	beta = 1.6592;
	
	return alph * Math.log(meters) + beta;
}


/**
* Constructs a Pacer object from the given time information and race length.  
* Calculates a modified base speed from the race length and time provided. 
* See Pace Fraction section in the specs document. 
*/ 
function Pacer(hour, mins, secs, rlen) 
{ 
	{ 
		var time = 0; 
		time += hour * 60; 
		time += mins * 1; 
		time += secs / 60; 
		this.time = time; 
	} 
	
	var dist = 0; 
	if(rlen.indexOf("onem") != -1) { dist = 1609; } 
	if(rlen.indexOf("fivk") != -1) { dist = 5000; } 
	if(rlen.indexOf("tenk") != -1) { dist = 10000; } 
	if(rlen.indexOf("half") != -1) { dist = 21097; } 
	if(rlen.indexOf("mara") != -1) { dist = 42194; }
	
	dist /= logRegressionPaceCoef(dist);
	
	this.baseSpeed = dist / this.time; 
} 

/** 
 * Returns a Pacer object calculated from the prParams. 
 */ 
function getPacerFromPR()
{ return new Pacer(prParams["hour"], prParams["mins"], prParams["secs"], prParams["rlen"]); } 

/** 
 * Returns a Pacer object calculated from information in the form (document.forms.trainform). 
 */ 
function getPacerFromForm()
{ return new Pacer(document.forms.trainform.hour.value, 
	document.forms.trainform.mins.value, 
	document.forms.trainform.secs.value, 
	document.forms.trainform.rlen.value); 
} 

/**
 * Returns a time string of the form min:sec per mile/km. 
 * Have to *divide* by coef because of wacky runners inverse speed system. 
 * This code implements pace increases based on progression in the schedule. 
 */
Pacer.prototype.getPaceString = function(dayO, met, wkNum)
{ 
	var coef = this.getPaceCoef(dayO); 
	var dMul = getDiffMultiplier(wkNum);
	
	// specRun pace coefficients are for SPECIFIC distances (800/1600m), 
	// so we don't need to modify anything for metric.
	var specRun = (dayO.cde == "y800" || dayO.cde == "y160"); 
	var mdist = ((met && !specRun) ? 1000 : 1609);
	var modSpeed = this.baseSpeed/(coef*dMul);
	return smartTimeString(mdist/modSpeed);
} 

Pacer.prototype.getRaceTime = function(met, wkNum)
{
	var rdistdict = getRaceDistDict();
	var ddist = rdistdict[prParams["rdst"]]; 
	var modSpeed = this.getBaseRaceSpeed(wkNum);
	return smartTimeString(ddist / modSpeed);
}

Pacer.prototype.getRacePace = function(met, wkNum)
{
	var modSpeed = this.getBaseRaceSpeed(wkNum);
	var mdist = (met ? 1000 : 1609);
	return smartTimeString(mdist/modSpeed);
}

Pacer.prototype.getBaseRaceSpeed = function(wkNum)
{
	var rdistdict = getRaceDistDict();
	var ddist = rdistdict[prParams["rdst"]]; 
	var dcoef =	logRegressionPaceCoef(ddist); // larger numbers-->slower
	var dMul = getDiffMultiplier(wkNum); // smaller numbers-->faster (ugh)
	var modSpeed = this.baseSpeed*dcoef/dMul; // meters per minute
	return modSpeed;
}

function getDiffMultiplier(wkNum)
{
	var dMul = 1.0; 
	{ 
		var mNum = Math.floor(wkNum/4); 
		if(prParams.diff == "mod" || prParams.diff == "hard")
			{ dMul = dMul - mNum * .01; } 
		if(prParams.diff == "vhard")
			{ dMul = dMul - mNum * .02; } 
	} 
	return dMul;
}

/** 
 * Returns a coefficient used to modify the base 5K pace for different 
 * workout types. The coefficient is fixed for the special runs, while 
 * for the easy and long runs the coefficient depends on the baseSpeed. 
 * Specifically, for fast runners the easy/long pace is a larger fractional 
 * increase over the 5K pace than for slower runners. See the pace fraction 
 * section of the spec. 
 */ 
Pacer.prototype.getPaceCoef = function(dObj)
{ 
	var pc = 1.19;
	if(dObj.cde == "easy" || dObj.cde == "lgrn")
	{ 
		// First convert to min/mile
		var mpm = 1/this.baseSpeed * 1609; 
		if(mpm < 4) 
			{ pc = 1.32; } 
		else if(mpm < 5)
			{ pc = (mpm - 4) * (-.02) + 1.32; } 
		else if(mpm < 7) 
			{ pc = (mpm - 5) * (-.01) + 1.30; } 
		else if(mpm < 9)
			{ pc = (mpm - 7) * (-.03/2) + 1.28; } 
		else if(mpm < 11)
			{ pc = (mpm - 9) * (-.03/2) + 1.25; }    
		else if(mpm < 13)
			{ pc = (mpm - 11) * (-.03/2) + 1.22; }
		else
			{ pc = 1.19; }
	} 
	
	// Tempo Run
	if(dObj.cde == "temp")
		{ pc = 1.07 + (dObj.num - 3) * .01; } 
	
	if(dObj.cde == "y800")
		{ pc = .48; } 
	
	if(dObj.cde == "y160")
		{ pc = 1.01; } 
	
	// Big TODO!! what is the correct coefficient here?
	if(dObj.cde == "race")
		{ pc = 1.19; }
	
	return pc;   
} 


basicWeek.prototype = new genWeek(); 

/** 
* Basic training week for non-marathon programs. 
*/ 
function basicWeek(tDist, sched, wInd)
{ 
	genWeekApply(this, tDist); 
	
	this.dispData = "STANDAARD TRAINING"; 
	
	// First set the EZ runs. 
	this.setRegRuns(sched); 
	
	var lrd = sched.longRunDay(tDist); 
	var prd = sched.pacedRunDay(tDist); 
	
	// Sets the lgrn run. 	  
	// this.days[lrd] = new longRun(tDist);
	this.days[lrd] = new longRunProg(prParams["mpwe"], wInd); 
	
	// All basic weeks have special runs.  
	this.days[prd] = pacedRunObj(tDist, wInd); 		 
}

basicWeek.prototype.addDist = function()
{ return true; } 

/**
* Returns an array of strings representing week codes such as "n4" which is 
* the fourth normal training week. Argument is the number of training weeks 
* in the program. See "Date Limited" section in spec. 
*/ 
function bWeekCodes(tw)
{ 
	var wcs = weekCodeSub(tw);
	var bwa = new Array(); 
	var j = 1; 
	
	for(var i = 0; i < wcs.length; i++)
	{ 
		for(var x = 0; x < wcs[i]; x++)
		{ 
			bwa[bwa.length] = ("n" + j);  
			j += 1; 
		} 
		if(i < wcs.length - 1)
			{ bwa[bwa.length] = ("r" + (i+1)); }  
	} 
	bwa[bwa.length] = "t1"; 
	return bwa;  
} 

function weekCodeSub(tw)
{ 
	switch (tw){
		case 16: return [3, 3, 3, 3]; 
		case 15: return [3, 3, 3, 2];
		case 14: return [4, 4, 3]; 
		case 13: return [4, 3, 3]; 
		case 12: return [3, 3, 3]; 
		case 11: return [5, 4]; 
		case 10: return [4, 4]; 
		case  9: return [4, 3]; 
		case  8: return [3, 3]; 
		case  7: return [3, 2]; 
		case  6: return [5]; 
		case  5: return [4]; 
		case  4: return [3]; 
		case  3: return [2]; 
		case  2: return [1]; 
		default: 
			logErr("Invalid argument to weekCodeSub:" + tw); 
			return [1]; 
	}
} 


