function ImperviousArea(pdArea, pdDistanceToWater, pdInfiltration)
{
  this.Area = pdArea;
  this.DistanceToWater = pdDistanceToWater;
  this.Infiltration = pdInfiltration;
  this.FirstCredit = 100;
  this.SecondCredit = 0;
  this.TotalCredit = 100;
}

function Disconnection(pdStormInches, pdNumHours)
{
  this.StormInches = pdStormInches;
  this.NumHours = pdNumHours;
  this.EventFrac = 0;
  this.AnnualVolume = 0;
}

// Uses in Primary (AN3), Secondary(AW3) And Base Case (AN54)
function RunOff(pdP, pdStorms)
{
  this.P = pdP;
  this.Storms = pdStorms;
}

function GroundCover(psName, pdInfiltrationConst, pdCurveMinCN, pdCurveConst, pdCurveLNMulti, pdHabitatPoints, pdBeautyPoints)
{
  this.Name = psName; /* T14 - T19 and T21 - T26 */
  this.Value01 = 0; /* M6 - M11 */
  this.Value02 = 0; /* P6 - P11 */
  this.InfiltrationConst = pdInfiltrationConst; /* U14 - U19 */
  this.CurveMinCN = pdCurveMinCN; /* U21 = U26 */
  this.CurveConst = pdCurveConst; /* V21 - V26 */
  this.CurveLNMulti = pdCurveLNMulti; /* W21 - W26 */
  this.HabitatPoints = pdHabitatPoints; /* AG6 - AG11 */
  this.BeautyPoints = pdBeautyPoints; /* AJ6 - AG11 */
  this.FinalInfillRate = 0; /* X14 Column */
  this.SecondaryInfillRate = 0;/* AB14 Column */
}


function Calculator()
{
  /* Inputs */
  this.AreaInput = 0; /* User Input - D6*/
  this.AreaUnit = "sqrt";
  this.Slope = 0; /* User Input - D7*/
  this.SoilTypeString = 'Silty';
  this.SoilType = 0; /* User Input -  Y10*/
  this.ImperviousAreas = []; //new Array();
  /* 0-35 feet */
  this.GroundCoverShrub01 = 0; /* M06 */
  this.GroundCoverGrassTall01 = 0; /* M07 */
  this.GroundCoverGrassShort01 = 0; /* M08 */
  this.GroundCoverBareSoil01 = 0; /* M09 */
  this.GroundCoverGravel01 = 0; /* M10 */
  this.GroundCoverImpervious01 = 0; /* M11 */
  this.TreeCanopy01 = 0; /* M14 */
  this.CompactionMitigation01 = 0; /* M16 */
  /* 35-75 feet */
  this.GroundCoverShrub02 = 0; /* P06 */
  this.GroundCoverGrassTall02 = 0; /* P07 */
  this.GroundCoverGrassShort02 = 0; /* P08 */
  this.GroundCoverBareSoil02 = 0; /* P09 */
  this.GroundCoverGravel02 = 0; /* P10 */
  this.GroundCoverImpervious02 = 0; /* P11 */
  this.TreeCanopy02 = 0; /* P14 */
  this.CompactionMitigation02 = 0; /* P16 */
  /* Constants */
  this.AnnualRain = 31; /* Hard Coded - AK20 - Annual Rain inches */
  this.Unknown = 0; /* Hard Coded - AK19 - Empty in Sheet*/
  this.PhosphorusConcentration = 0.3; /* Hard Coded - AK32 - P Conc mg/l */
  this.GroundCovers = []; //new Array();
  /* Name, U14, U21, V21, W21, AG6 */
  this.GroundCovers[0] = new GroundCover("Shrub", 1, 30, 37, 11.3, 10, 10);
  this.GroundCovers[1] = new GroundCover("GrassTall", 1, 30, 40.4, 12.5, 5, 10);
  this.GroundCovers[2] = new GroundCover("GrassShort", 0.5, 49, 56.5, 9.2, 2.5, 2.5);
  this.GroundCovers[3] = new GroundCover("BareSoil", 0.3, 72, 75.8, 4.5, 0, 0);
  this.GroundCovers[4] = new GroundCover("Gravel Roads", 0.5, 76, 79.3, 3.9, 0, 0);
  this.GroundCovers[5] = new GroundCover("Impervious", 0, 98, 98, 0, 0, 0);
  
  this.RunOffs = []; //new Array();
  this.RunOffs[0] = new RunOff(0.1, 51.10);
  this.RunOffs[1] = new RunOff(0.25, 35.37);
  this.RunOffs[2] = new RunOff(0.5, 9.27);
  this.RunOffs[3] = new RunOff(0.75, 8.43);
  this.RunOffs[4] = new RunOff(1, 4.93);
  this.RunOffs[5] = new RunOff(1.5, 0.80);
  this.RunOffs[6] = new RunOff(2, 0.23);
  
  /* Results */
  this.Area = 0;	
  this.BaseCN = 0; // AD21 
  this.PrimaryRunOffVolume = 0; // Y30
  this.SecondaryRunOffVolume = 0; // AB30
  this.RunOffVolumeBrush = 0; // Y31 and AB31
  this.AreaNoInfill = 0; // AK18 = Area*(1-% Infil) = =+X5*(100-AC5)/100+X6*(100-AC6)/100+X7*(100-AC7)/100+X8*(100-AC8)/100 
  this.ImpROVol = 0; // AK21 - ft3 
  this.GroundCoverTotal01 = 0; // M13
  this.GroundCoverTotal02 = 0; // P13
  this.ImperviousTotal = 0;
  this.ImperviousPercent = 0;
  this.ImperviousPercentFixed = 0;
  this.PhosphorusLbsAcreYear = 0;
  this.PhosphorusLbsYear = 0;
  this.ImperviousSurfaceScore = 0;
  this.PrimaryBufferScore = 0;
  this.SecondaryBufferScore = 0;
  this.TotalScore = 0;
  
  
  this.Round = function(pdValue, piDecimals)
  {
    pdValue = pdValue * Math.pow(10, piDecimals);
    pdValue = Math.round(pdValue);
    pdValue = pdValue / Math.pow(10, piDecimals);
    return pdValue;
  };
  
  this.CalculateSoilType = function()
  {
    switch (this.SoilTypeString.toLowerCase())
    {
      case 'clayey':
        this.SoilType = 0.04;
        break;
      case 'silty':
        this.SoilType = 0.24;
        break;
      case 'sandy':
        this.SoilType = 4.00;
        break;
      case 'user':
        this.SoilType = 100;/* User Input */
        /*'ldInfiltrationType = "in/hr"*/
        break;
      default:
        this.SoilType = 5000; /* What to do here?*/
        break;
    }
  };
  
  this.CalculateTotals = function()
  {
    switch (this.AreaUnit.toLowerCase())
    {
      case "sqft":
        this.Area = this.AreaInput;
        break;
      case "acres":
        this.Area = this.AreaInput * 43560;
        break;
    }
    var liLoop;
    this.GroundCovers[0].Value01 = this.GroundCoverShrub01;
    this.GroundCovers[0].Value02 = this.GroundCoverShrub02;
    
    this.GroundCovers[1].Value01 = this.GroundCoverGrassTall01;
    this.GroundCovers[1].Value02 = this.GroundCoverGrassTall02;
    
    this.GroundCovers[2].Value01 = this.GroundCoverGrassShort01;
    this.GroundCovers[2].Value02 = this.GroundCoverGrassShort02;
    
    this.GroundCovers[3].Value01 = this.GroundCoverBareSoil01;
    this.GroundCovers[3].Value02 = this.GroundCoverBareSoil02;
    
    this.GroundCovers[4].Value01 = this.GroundCoverGravel01;
    this.GroundCovers[4].Value02 = this.GroundCoverGravel02;
    
    this.GroundCovers[5].Value01 = this.GroundCoverImpervious01;
    this.GroundCovers[5].Value02 = this.GroundCoverImpervious02;
    
    
    this.GroundCoverTotal01 = 0;
    this.GroundCoverTotal02 = 0;
    for (liLoop = 0; liLoop < this.GroundCovers.length; liLoop++) 
    {
      this.GroundCoverTotal01 += this.GroundCovers[liLoop].Value01;
      this.GroundCoverTotal02 += this.GroundCovers[liLoop].Value02;
    }
    
    this.ImperviousTotal = 0;
    for (liLoop = 0; liLoop < this.ImperviousAreas.length; liLoop++) 
    {
      this.ImperviousTotal += this.ImperviousAreas[liLoop].Area;
    }
    this.ImperviousPercent = this.Round(this.ImperviousTotal / this.Area, 4);
    this.ImperviousPercentFixed = this.Round(this.ImperviousPercent * 100, 2);
  };
  
  this.CalculateImperviousSurface = function()
  {
    var liLoop;
    //Y11=+(AB14*AA21+AB15*AA22+AB16*AA23+AB17*AA24+AB18*AA25+AB19*AA26)  	
    var ldAverageSecInfillRate = 0;
    for (liLoop = 0; liLoop < this.GroundCovers.length; liLoop++) 
    {
      // W14 = =MAX(0.1,U14-2*V14)	  	
      // X14=+W14*Y$10	 	  	
      this.GroundCovers[liLoop].FinalInfillRate = this.Round(this.SoilType * Math.max(0.1, this.GroundCovers[liLoop].InfiltrationConst - 2 * this.ImperviousPercent), 2); /* 080918 SDM - Added rounding */
      // AA14 = P16
      // AB14=+X14+(AA14/50)*X14
      this.GroundCovers[liLoop].SecondaryInfillRate = this.Round(this.GroundCovers[liLoop].FinalInfillRate + (this.CompactionMitigation02 / 50) * this.GroundCovers[liLoop].FinalInfillRate, 2); /* 080918 SDM - Added rounding */
      //console.log('Final %d Second %d', this.GroundCovers[liLoop].FinalInfillRate, this.GroundCovers[liLoop].SecondaryInfillRate);
      ldAverageSecInfillRate += this.Round(this.GroundCovers[liLoop].SecondaryInfillRate * this.GroundCovers[liLoop].Value02 / 100, 4); /* 080918 SDM - Added rounding */
    }
    //console.log('Average Infilate Rate : %d', ldAverageSecInfillRate)
    
    var arDisconnections = []; //new Array();
    arDisconnections[0] = new Disconnection(0.05, 476.25); /* AE20, AF20 */
    arDisconnections[1] = new Disconnection(0.15, 42.11);
    arDisconnections[2] = new Disconnection(0.25, 12.83);
    arDisconnections[3] = new Disconnection(0.35, 6.36);
    arDisconnections[4] = new Disconnection(0.45, 3.47);
    arDisconnections[5] = new Disconnection(0.55, 1.78);
    arDisconnections[6] = new Disconnection(0.65, 1.36);
    arDisconnections[7] = new Disconnection(0.75, 0.86);
    arDisconnections[8] = new Disconnection(0.85, 0.53);
    arDisconnections[9] = new Disconnection(0.95, 0.42);
    arDisconnections[10] = new Disconnection(1.05, 0.42);
    arDisconnections[11] = new Disconnection(1.15, 0.28);
    arDisconnections[12] = new Disconnection(1.25, 0.08);
    arDisconnections[13] = new Disconnection(1.40, 0.11);
    arDisconnections[14] = new Disconnection(1.75, 0.31);
    
    // A35=SUM(AH20:AH34)    
    var ldSumDisconnect = 0;
    for (liLoop = 0; liLoop < arDisconnections.length; liLoop++) 
    {
      /* AG20 = +(0.3*LN(AE20)+1.28)*EXP(-0.43*Y$11*AE20^-1.26) */
      arDisconnections[liLoop].EventFrac = this.Round((0.3 * Math.log(arDisconnections[liLoop].StormInches) + 1.28) * Math.exp(-0.43 * ldAverageSecInfillRate * Math.pow(arDisconnections[liLoop].StormInches, -1.26)), 2); /* Excel LN  & EXP*/ /* 080918 SDM - Added rounding */
      /* AH20 = AG20*AE20*AF20 */
      arDisconnections[liLoop].AnnualVolume = this.Round(arDisconnections[liLoop].StormInches * arDisconnections[liLoop].NumHours * arDisconnections[liLoop].EventFrac, 2);/* 080918 SDM - Added rounding */
      //console.log('EventFrac : %d - Annual Volume : %d', arDisconnections[liLoop].EventFrac, arDisconnections[liLoop].AnnualVolume);
      ldSumDisconnect += arDisconnections[liLoop].AnnualVolume;
      
    }
    // AH36 =AH35/27 	  
    var ldFractionThatRunsOff = this.Round(ldSumDisconnect / 27, 4); /* 080918 SDM - Added rounding */
    //AK18 = +X5*(100-AC5)/100+X6*(100-AC6)/100+X7*(100-AC7)/100+X8*(100-AC8)/100
    this.AreaNoInfill = 0;
    for (liLoop = 0; liLoop < this.ImperviousAreas.length; liLoop++) 
    {
      this.ImperviousAreas[liLoop].FirstCredit = 100;
      this.ImperviousAreas[liLoop].SecondCredit = 0;
      if (this.ImperviousAreas[liLoop].Area > 0) 
      {
        //AA5 = IF(X5>0,150*(Z5/X5)^(-0.032*LN(Y10)+0.2356),100)
        this.ImperviousAreas[liLoop].FirstCredit = 150 * Math.pow(this.ImperviousAreas[liLoop].Infiltration / this.ImperviousAreas[liLoop].Area, (-0.032 * Math.log(this.SoilType) + 0.2356)); /* Excel LN ^*/
        if (this.ImperviousAreas[liLoop].DistanceToWater > 35) 
        {
          // AB5 = 100*((1.175*((X5/500)*(40/(Y5-35)))^(-0.62))*(1-$AH$36)) When area is > 0 and distance > 35 */
          // AB5 = IF(X5>0,IF((Y5>35),100*((1.175*((X5/500)*(40/(Y5-35)))^(-0.62))*(1-$AH$36)),0),0)		 
          var ldSecondCredit = (this.ImperviousAreas[liLoop].Area / 500) * (40 / (this.ImperviousAreas[liLoop].DistanceToWater - 35));
          ldSecondCredit = 1.175 * Math.pow(ldSecondCredit, -0.62);
          this.ImperviousAreas[liLoop].SecondCredit = ldSecondCredit * (1 - ldFractionThatRunsOff) * 100;
        }
        /* AC5 = IF((AA5+AB5)>100,100,AA5+AB5) */
        this.ImperviousAreas[liLoop].TotalCredit = Math.min(100, this.ImperviousAreas[liLoop].FirstCredit + this.ImperviousAreas[liLoop].SecondCredit);
        /* X5[Area] * (100 - AC5[TotalInfillCredit]) / 100 */
        this.AreaNoInfill += this.ImperviousAreas[liLoop].Area * (100 - this.ImperviousAreas[liLoop].TotalCredit) / 100;
      }
      //console.log('First Credit : %d Second Credit : %d', this.ImperviousAreas[liLoop].FirstCredit, this.ImperviousAreas[liLoop].SecondCredit);
    }
    //console.log('AK18 : %d', this.AreaNoInfill)
    /* AK21 = AK18*(AK20/12)*(1-AK19/100) */
    this.ImpROVol = this.AreaNoInfill * (this.AnnualRain / 12) * (1 - this.Unknown / 100);
    //E21 =10*EXP(-3*AK21/10000)
    this.ImperviousSurfaceScore = this.Round(10 * Math.exp(-3 * this.ImpROVol / 10000), 1);
  };
  
  this.CalculateBase = function()
  {
    var liLoop;
    var ldAR;
    var ldStormQ;
    //AI55 = AD21 = MAX(U21,V21-W21*LN(3*Y10)) + (D7-5)/5 
    this.BaseCN = Math.max(this.GroundCovers[0].CurveMinCN, this.GroundCovers[0].CurveConst - this.GroundCovers[0].CurveLNMulti * Math.log(3 * this.SoilType)) + (this.Slope - 5) / 5; /* Excel LN */
    // AP55 =+(1000/AO55)-10
    var ldS = (1000 / this.BaseCN) - 10;
    // Y31=SUM(AU55:AU61)
    this.RunOffVolumeBrush = 0;
    for (liLoop = 0; liLoop < this.RunOffs.length; liLoop++) 
    {
      //AR55 = +AQ55 - 0.2 * AP55	  
      ldAR = this.RunOffs[liLoop].P - 0.2 * ldS;
      //AS55 = +IF(AR55>0,(AQ55-0.2*AP55)^2/(AQ55+0.8*AP55),0)	  
      ldStormQ = 0;
      if (ldAR > 0) 
      {
        ldStormQ = Math.pow(this.RunOffs[liLoop].P - 0.2 * ldS, 2) / (this.RunOffs[liLoop].P + 0.8 * ldS);
      }
      //AU55 =+AS55*AT55		
      this.RunOffVolumeBrush += this.RunOffs[liLoop].Storms * ldStormQ;
    }
  };
  
  this.CalculatePrimaryBuffer = function()
  {
    var liGroundCover;
    var liRunOff;
    var ldScore;
    var ldModifier;
    var ldFinalInfillRate;
    var ldPrimaryInfiltrationRate;
    var ldCN;
    var ldS;
    var ldRunOffTotal;
    var ldAR;
    var ldStormQ;
    
    //AK13 = SUM(AK6:AK11)
    var ldBeautyPrimaryScore = 0;
    //AH13 = SUM(AH6:AH11)	
    var ldHabitatPrimaryScore = 0;
    for (liGroundCover = 0; liGroundCover < this.GroundCovers.length; liGroundCover++) 
    {
      //AH6	= M6*AG6/100
      ldScore = this.GroundCovers[liGroundCover].HabitatPoints * this.GroundCovers[liGroundCover].Value01 / 100;
      ldHabitatPrimaryScore += ldScore;
      
      //AK6	= M6*AJ6/100
      ldScore = this.GroundCovers[liGroundCover].BeautyPoints * this.GroundCovers[liGroundCover].Value01 / 100;
      ldBeautyPrimaryScore += ldScore;
    }
    // AK14 = 5*M14/100
    var ldCanopyCoverageScore = 5 * this.TreeCanopy01 / 100;
    // AK15 = MIN(10, AK14+AK13)
    var ldAdjustedBeautyScore = Math.min(10, ldCanopyCoverageScore + ldBeautyPrimaryScore);
    
    //Y30 = SUM(Z21:Z27)	
    this.PrimaryRunOffVolume = 0;
    for (liGroundCover = 0; liGroundCover < this.GroundCovers.length; liGroundCover++) 
    {
      // W14 = MAX(0.1,U14-2*V14)
      ldModifier = Math.max(0.1, this.GroundCovers[liGroundCover].InfiltrationConst - 2 * this.ImperviousPercent);
      // X14 = W14 * Y$10
      ldFinalInfillRate = ldModifier * this.SoilType;
      // Z14 = X14+(Y14/50)*X14
      ldPrimaryInfiltrationRate = this.Round(ldFinalInfillRate + (this.CompactionMitigation01 / 50) * ldFinalInfillRate, 2);
      //Y21 = MIN(99,(MAX(U21,V21-W21*LN(Z14)) + (D7-5)/5))
      //Y21 = MAX(U21,V21-W21*LN(Z14)) + (D7-5)/5
      ldCN = Math.max(this.GroundCovers[liGroundCover].CurveMinCN, this.GroundCovers[liGroundCover].CurveConst - this.GroundCovers[liGroundCover].CurveLNMulti * Math.log(ldPrimaryInfiltrationRate)) + (this.Slope - 5) / 5;
      ldCN = Math.min(99, ldCN);
      //console.log('%s - %d', this.GroundCovers[liGroundCover].Name, ldCN);
      //AP4 = (1000/AO4)-10
      ldS = (1000 / ldCN) - 10;
      
      ldRunOffTotal = 0;
      for (liRunOff = 0; liRunOff < this.RunOffs.length; liRunOff++) 
      {
        // AR4 = AQ4-0.2*AP4
        ldAR = this.RunOffs[liRunOff].P - 0.2 * ldS;
        // AS4 = =+IF(AR4>0,(AQ4-0.2*AP4)^2/(AQ4+0.8*AP4),0)			
        ldStormQ = 0;
        if (ldAR > 0) 
        {
          ldStormQ = Math.pow(this.RunOffs[liRunOff].P - 0.2 * ldS, 2) / (this.RunOffs[liRunOff].P + 0.8 * ldS);
        }
        //AU04 = AS4 * AT4	// TotalQ = AS4 * this.RunOffs[x].Storms 			
        ldRunOffTotal += ldStormQ * this.RunOffs[liRunOff].Storms;
      }
      //Z21 = X21*SUM(AU4:AU10)		
      this.PrimaryRunOffVolume += this.GroundCovers[liGroundCover].Value01 / 100 * ldRunOffTotal;
    }
    
    //Y32 = +Y30 - Y31	  
    var ldDifferenceInRunOffVolume = this.PrimaryRunOffVolume - this.RunOffVolumeBrush;
    // Y33 = 3.33 * EXP(-3 * Y32)
    var ldWaterQualityScore = 3.33 * Math.exp(-3 * ldDifferenceInRunOffVolume);
    //E22 = IF(M13=100, +Y33+3.33*EXP(-3*(10-AH13)/10)+3.33*EXP(-3*(10-AK15)/10), "ERROR")	
    // =Y33 + 3.33 * EXP(-3*(10-AH13)/10)+3.33*EXP(-3*(10-AK15)/10)
    this.PrimaryBufferScore = this.Round(ldWaterQualityScore + 3.33 * Math.exp(-3 * (10 - ldHabitatPrimaryScore) / 10) + 3.33 * Math.exp(-3 * (10 - ldAdjustedBeautyScore) / 10), 1); /* Excel EXP */
  };
  
  this.CalculateSecondaryBuffer = function()
  {
    var ldScore;
    var liGroundCover;
    var ldModifier;
    var ldFinalInfillRate;
    var ldSecondaryInfillRate;
    var ldCN;
    var ldS;
    var ldRunOffTotal;
    var liRunOff;
    var BA4;
    var ldStormQ;
    
    //AI13 =SUM(AI6:AI11)    
    var ldHabitatSecondaryScore = 0;
    //AL13 = SUM(AL6:AL11)
    var ldBeautySecondaryScore = 0;
    for (liGroundCover = 0; liGroundCover < this.GroundCovers.length; liGroundCover++) 
    {
      //AI6	= =+P6*AG6/100
      ldScore = this.GroundCovers[liGroundCover].HabitatPoints * this.GroundCovers[liGroundCover].Value02 / 100;
      ldHabitatSecondaryScore += ldScore;
      
      //AL6 =  P6*AJ6/100      
      ldScore = this.GroundCovers[liGroundCover].BeautyPoints * this.GroundCovers[liGroundCover].Value02 / 100;
      ldBeautySecondaryScore += ldScore;
    }
    
    //AL14=5*P14/100
    var ldCanopyCoverageScore = 5 * this.TreeCanopy02 / 100;
    
    // AL15 =MIN(10,+AL14+AL13)
    var ldAdjustedBeautyScore = Math.min(10, ldCanopyCoverageScore + ldBeautySecondaryScore);
    
    //AB30=SUM(AC21:AC27)
    this.SecondaryRunOffVolume = 0;
    for (liGroundCover = 0; liGroundCover < this.GroundCovers.length; liGroundCover++) 
    {
      // W14 =MAX(0.1,U14-2*V14)
      ldModifier = Math.max(0.1, this.GroundCovers[liGroundCover].InfiltrationConst - 2 * this.ImperviousPercent);
      
      //X14 = W14*Y$10	  	
      ldFinalInfillRate = ldModifier * this.SoilType;
      
      //AB14 = +X14+(AA14/50)*X14  
      ldSecondaryInfillRate = ldFinalInfillRate + (this.CompactionMitigation02 / 50) * ldFinalInfillRate;
      
      // AX4 = AB21 = MAX(U21,V21-W21*LN(AB14)) + (D7-5)/5
      ldCN = Math.max(this.GroundCovers[liGroundCover].CurveMinCN, this.GroundCovers[liGroundCover].CurveConst - this.GroundCovers[liGroundCover].CurveLNMulti * Math.log(ldSecondaryInfillRate)) + (this.Slope - 5) / 5;
      //console.log('AB21 : %d', Round(ldCN, 0));
      
      // AY4=+(1000/AX4)-10
      ldS = (1000 / ldCN) - 10;
      ldRunOffTotal = 0;
      for (liRunOff = 0; liRunOff < this.RunOffs.length; liRunOff++) 
      {
        // BA4 = AZ4-0.2*AY4
        BA4 = this.RunOffs[liRunOff].P - 0.2 * ldS;
        
        //BB4=+IF(BA4>0,(AZ4-0.2*AY4)^2/(AZ4+0.8*AY4),0)
        ldStormQ = 0;
        if (BA4 > 0) 
        {
          ldStormQ = Math.pow(this.RunOffs[liRunOff].P - 0.2 * ldS, 2) / (this.RunOffs[liRunOff].P + 0.8 * ldS);
        }
        //BD4 = BB4 * BC4	  	
        ldRunOffTotal += ldStormQ * this.RunOffs[liRunOff].Storms;
      }
      //AC21=AA21*SUM(BD4:BD10)  		
      this.SecondaryRunOffVolume += this.GroundCovers[liGroundCover].Value02 / 100 * ldRunOffTotal;
    }
    
    // AB32= AB30-AB31 (AB31 is calculated in DoCalculateBase
    var ldDifferenceInRunOffVolume = this.SecondaryRunOffVolume - this.RunOffVolumeBrush;
    
    // AB33=3.33*EXP(-3*AB32)
    var ldWaterQualityScore = 3.33 * Math.exp(-3 * ldDifferenceInRunOffVolume);
    
    //E3 = IF(P13=100,+AB33+3.33*EXP(-3*(10-AI13)/10)+3.33*EXP(-3*(10-AL15)/10),"ERROR")  	
    this.SecondaryBufferScore = this.Round(ldWaterQualityScore + 3.33 * Math.exp(-3 * (10 - ldHabitatSecondaryScore) / 10) + 3.33 * Math.exp(-3 * (10 - ldAdjustedBeautyScore) / 10), 1);
  };
  
  this.CalculatePhosphorus = function()
  {
    var ldAverageRunOff = (this.PrimaryRunOffVolume + this.SecondaryRunOffVolume) / 2; /* AK28 */
    var ldTotalRunOff = (this.Area - this.AreaNoInfill) * (ldAverageRunOff / 12); /*Total RO ft3  = AK29 = (AK25-AK18)*(AK28/12)*/
    //AK31 = AK21+AK29
    var AK31 = this.ImpROVol + ldTotalRunOff; /*AK31 is also labelled as Total RO */
    //AK33 = = AK31*7.48*8.34*AK32/1000000
    var ldPhosphorusLbsYear = AK31 * 7.48 * 8.34 * this.PhosphorusConcentration / 1000000;
    this.PhosphorusLbsYear = this.Round(ldPhosphorusLbsYear, 2);
    this.PhosphorusLbsAcreYear = this.Round(ldPhosphorusLbsYear / (this.Area / 43560), 2); /* Convert Area to acres */
  };
  
  this.Calculate = function()
  {
    //this.CalculateSoilType();
    this.CalculateSoilType();
    this.CalculateTotals();
    this.CalculateImperviousSurface();
    this.CalculateBase();
    // TODO - In Excel, these return "Error if it isn't 100
    if (this.GroundCoverTotal01 === 100) 
    {
      this.CalculatePrimaryBuffer();
    }
    if (this.GroundCoverTotal02 === 100) 
    {
      this.CalculateSecondaryBuffer();
    }
    this.CalculatePhosphorus();
    this.TotalScore = this.Round(this.ImperviousSurfaceScore + this.PrimaryBufferScore + this.SecondaryBufferScore, 1);
    return true;
  };
}
