function CalculateAll()
{ // Calculates new rankings using all methods.
  CalculateExisting();
  CalculateNewJustin();
  CalculateNewAdaptedProposal();
  CalculateNewSilvio();
  return(false);
}

function CalculateExisting()
{ // Calculates rank change based on existing algorithm.
  var PlayerScore, OpponentScore, PlayerRank, OpponentRank;
  var WinnerScore, LooserScore, WinnerRankOld, LooserRankOld, WinnerRankNew, LooserRankNew;
  var LooserChange, WinnerChange;
  var change, worth, handicapOfWinner, handicapOfLooser;
  
  PlayerScore =  Number(document.gameresult.Spla.value);
  OpponentScore =Number(document.gameresult.Sopp.value);
  PlayerRank =   Number(document.gameresult.Rold.value);
  OpponentRank = Number(document.gameresult.Ropp.value);

  if (PlayerScore < OpponentScore)
  {   WinnerScore = OpponentScore;
    WinnerRankOld = OpponentRank;
    LooserScore = PlayerScore;
    LooserRankOld = PlayerRank;
  } else if (PlayerScore > OpponentScore)
  { WinnerScore = PlayerScore;
    WinnerRankOld = PlayerRank;
    LooserScore = OpponentScore;
    LooserRankOld = OpponentRank;
  }
  if (PlayerScore != OpponentScore)
  { change = Math.sqrt(WinnerScore - LooserScore);
    worth = 8 * change * Math.sqrt(LooserRankOld/WinnerRankOld);
    handicapOfWinner = Math.sqrt((1000 - WinnerRankOld)/1000);
    handicapOfLooser = Math.sqrt((1000 - LooserRankOld)/1000);
    WinnerChange = Math.floor(worth * handicapOfWinner);
    WinnerRankNew = WinnerRankOld + WinnerChange;
    if (LooserRankOld > 750)
      LooserChange = Math.floor(worth/2 * (1-handicapOfLooser));
    else
      LooserChange = Math.floor(worth * (1-handicapOfLooser));
    LooserRankNew = LooserRankOld - LooserChange;
    if (PlayerScore < OpponentScore)
    { PlayerRankNew = FormatResult(LooserRankNew, LooserRankOld);
      OpponentRankNew = FormatResult(WinnerRankNew, WinnerRankOld);
    } else
    { PlayerRankNew = FormatResult(WinnerRankNew, WinnerRankOld);
      OpponentRankNew = FormatResult(LooserRankNew, LooserRankOld);
    }
    document.newrank.Rnewopp.value = OpponentRankNew;
    document.newrank.Rnew.value = PlayerRankNew;
  } else
  {   document.newrank.Rnewopp.value = OpponentRank.toString() + " (no change)";
    document.newrank.Rnew.value = PlayerRank.toString() + " (no change)";
  }
}

function CalculateNewJustin()
{ // Calculates rank using the new method proposed by Justin. 
  var PlayersNewRank, OpponentsNewRank;
  var PlayerScore, OpponentScore, PlayerRank, OpponentRank;
  var PlayersGames, OpponentGames;
  
  // Get the values from the edit controls.
  PlayerScore =  Number(document.gameresult.Spla.value);
  OpponentScore= Number(document.gameresult.Sopp.value);
  PlayerRank =   Number(document.gameresult.Rold.value);
  OpponentRank = Number(document.gameresult.Ropp.value);
  PlayersGames = Number(document.gameresult.Gpla.value);
  OpponentGames= Number(document.gameresult.Gopp.value);
  
  PlayersNewRank = CalculateJustin(PlayerScore, OpponentScore, PlayerRank, OpponentRank, PlayersGames, OpponentGames,0);
  OpponentsNewRank = CalculateJustin(OpponentScore, PlayerScore, OpponentRank, PlayerRank, OpponentGames, PlayersGames,0);
  
  document.newrank.RnewJ.value = FormatResult(PlayersNewRank, PlayerRank);
  document.newrank.RnewoppJ.value = FormatResult(OpponentsNewRank, OpponentRank);
}

function CalculateNewAdaptedProposal()
{ // Calculates rank using the new method proposed by Justin. 
  var PlayersNewRank, OpponentsNewRank;
  var PlayerScore, OpponentScore, PlayerRank, OpponentRank;
  var PlayersGames, OpponentGames;
  
  // Get the values from the edit controls.
  PlayerScore =  Number(document.gameresult.Spla.value);
  OpponentScore= Number(document.gameresult.Sopp.value);
  PlayerRank =   Number(document.gameresult.Rold.value);
  OpponentRank = Number(document.gameresult.Ropp.value);
  PlayersGames = Number(document.gameresult.Gpla.value);
  OpponentGames= Number(document.gameresult.Gopp.value);
  
  PlayersNewRank = CalculateAdaptedProposal(PlayerScore, OpponentScore, PlayerRank, OpponentRank, PlayersGames, OpponentGames);
  OpponentsNewRank = CalculateAdaptedProposal(OpponentScore, PlayerScore, OpponentRank, PlayerRank, OpponentGames, PlayersGames);
  
  document.newrank.RnewJ2.value = FormatResult(PlayersNewRank, PlayerRank);
  document.newrank.RnewoppJ2.value = FormatResult(OpponentsNewRank, OpponentRank);
}

function CalculateAdaptedProposal(fSpla, fSopp, fRold, fRopp, fGpla, fGopp) {
  // calculate ranking for the adapted proposal  
  var tpsPlayer = CalculateTPs(fSpla - fSopp);
  var tpsOpponent = CalculateTPs(fSopp - fSpla);
  var rankingOfPlayer = fRold;
  if (fGpla < 30)  rankingOfPlayer += 14 * (30 - fGpla);
  if (fGopp < 30) {
    if (tpsPlayer > tpsOpponent || (tpsPlayer == tpsOpponent && rankingOfPlayer < fRopp - 26 * (30 - fGopp))) fRopp -= 26 * (30 - fGopp);
    else {
      if (tpsPlayer < tpsOpponent || (tpsPlayer == tpsOpponent && rankingOfPlayer > fRopp + 54 * (30 - fGopp))) fRopp += 54 * (30 - fGopp);
      else fRopp = rankingOfPlayer;
    }
  }
  var worth = Bound(-19, (fRopp - rankingOfPlayer) / 50,19) + tpsPlayer - tpsOpponent;
  if ((worth < 0 && tpsPlayer > tpsOpponent) || (worth > 0 && tpsPlayer < tpsOpponent)) worth = 0;
  else {
    if (worth > 0.02 && worth < 1) worth = 1;
    else if (worth < -0.02 && worth > -1) worth = -1;
    if  (tpsPlayer == tpsOpponent) worth /= 2;
  }
  return fRold + Math.round(fGpla < 30 ? Bound(-40,5.4 * worth / Math.sqrt(fGpla),40) + 14 : worth);
}

function Bound(aLowerBound, aNumber, anUpperBound) {
  // returns a number bounded by 2 bounds
 return aNumber < aLowerBound ? aLowerBound : (aNumber > anUpperBound ? anUpperBound : aNumber);
}

function CalculateTPs(aMargin) {
  // calculate Tournament Points using the current Tournament Point Formulae
  return Bound(0,aMargin > 0 ? Math.round(4 * Math.sqrt(aMargin) / 3) + 12 : (aMargin < 0 ? 8 - Math.round(4 * Math.sqrt(-aMargin) / 3) : 10),20);
}

function CalculateNewSilvio()
{ // Calculates the rank using the method proposed by Silvio.
  var PlayersNewRank, OpponentsNewRank;
  var PlayerScore, OpponentScore, PlayerRank, OpponentRank;
  var PlayersGames, OpponentGames;
  
  // Get the values from the edit controls.
  PlayerScore =  Number(document.gameresult.Spla.value);
  OpponentScore= Number(document.gameresult.Sopp.value);
  PlayerRank =   Number(document.gameresult.Rold.value);
  OpponentRank = Number(document.gameresult.Ropp.value);
  PlayersGames = Number(document.gameresult.Gpla.value);
  OpponentGames= Number(document.gameresult.Gopp.value);
  
  PlayersNewRank = CalculateJustin(PlayerScore, OpponentScore, PlayerRank, OpponentRank, PlayersGames, OpponentGames,1);
  OpponentsNewRank = CalculateJustin(OpponentScore, PlayerScore, OpponentRank, PlayerRank, OpponentGames, PlayersGames,1);
  
  document.newrank.RnewS.value = FormatResult(PlayersNewRank, PlayerRank);
  document.newrank.RnewoppS.value = FormatResult(OpponentsNewRank, OpponentRank);
}
  
function FormatResult(NewRank, OldRank)
{ // Formats a result for display.
  var change;
  
  change = NewRank - OldRank;
  if (NewRank < OldRank)
  { return (NewRank.toString() + " (down " + (-1*change).toString() +")");
  } else if (NewRank > OldRank)
  { return (NewRank.toString() + " (up " + (change).toString() +")");
  } else
  { return (NewRank.toString() + " (no change)");
  }
}

function CalculateJustin(fSpla, fSopp, fRold, fRopp, fGpla, fGopp, SilvioMod)
{ /* calculates ranking change for the given player
   fSpla = players score.
   fSopp = opponents score.
   fRold = players old ranking
   fRopp = opponents old ranking
   fGpla = games player has played counting this one.
   fGopp = games oponent has played counting this one. 
   SilvioMod : if 0, uses Justins method, if 1, uses Silvio's modification. */
   
    var worth, change;

  if (fGpla > 100) fGpla = 100;
  if (fGopp > 100) fGopp = 100;
  
  // Calculate worth and change.
  worth = (Math.sqrt(fRopp) - Math.sqrt(fRold))/3 + (fSpla - fSopp)/2;
  if (SilvioMod == 0)
  { if (worth >= -1 && worth <= 1)
    { change = Math.sqrt((fGopp/ fGpla)) * worth;
    } else if (worth < -1)
    { change = Math.sqrt((fGopp/ fGpla)) * (3-4*Math.sqrt(-worth));
    } else
    { change = Math.sqrt(fGopp/ fGpla) * (4 * Math.sqrt(worth) - 3);
    }
  } else
  { var RLoser, RWinner;
    if (fSpla < fSopp)
    { RLoser = fRold;
      RWinner = fRopp;
    } else
    { RLoser = fRopp;
      RWinner = fRold;
    }
    change = Math.round(Math.sqrt(RLoser/RWinner)*Math.pow(Math.abs(fSopp-fSpla),(2/3))*1.4);
    if (fSpla < fSopp)
      change *= -1;
  }
  // Calculate new ranking.
  if (fSopp == fSpla)
  { fRnew = fRold + Math.round(change/2);
  } else if ((fSpla < fSopp && change < 0) || (fSpla > fSopp && change > 0))
  { fRnew = fRold + Math.round(change);
  } else if ((fSpla < fSopp && change >= 0) || (fSpla > fSopp && change <= 0))
  { fRnew = fRold;
  }

  // Calculate bonuses.
  if (fSpla < fSopp && fGpla <= 100)
  { if (SilvioMod == 0)
      fRnew += Math.round((101-fGpla)/50);
    else
      fRnew += Math.round((101-fGpla)/30);
  }
  if (fSpla == fSopp && fGpla <= 100)
  { fRnew += Math.round((101 - fGpla)/20);
  }
  if (fSpla > fSopp && fGpla <=100)
  { if (SilvioMod == 0)
      fRnew += Math.round((101-fGpla)/12.5);
    else
      fRnew += Math.round((101-fGpla)/10);
  }

  // I don't understand the dealing with bonuses bit so ignore it.
  return (fRnew);
}

function SaveSettings()
{ // Saves values in a cookie so they will be available next time.
  var c, ExpiryDate, Today;
  
  Today = new Date();
  ExpiryDate = new Date(Today.getFullYear(), Today.getMonth(), Today.getDate()+14);
  document.cookie = "expires=" + ExpiryDate.toString() + ";";
  document.cookie = "PS="+document.forms.gameresult.Spla.value+";";
  document.cookie = "OS="+document.forms.gameresult.Sopp.value+";";
  document.cookie = "PR="+document.forms.gameresult.Rold.value+";";
  document.cookie = "OR="+document.forms.gameresult.Ropp.value+";";
  document.cookie = "PG="+document.forms.gameresult.Gpla.value+";";
  document.cookie = "OG="+document.forms.gameresult.Gopp.value+";";
}

function GetCookieValue(Key)
{ // Returns the value for the given cookie key.
  var startindex, endindex, cookievalue,tempcookie;
  tempcookie = document.cookie + ";";
  startindex = tempcookie.indexOf(Key + "=");
  endindex = tempcookie.indexOf(";", startindex);
  cookievalue = (document.cookie.substring(startindex+Key.length+1, endindex));
  if (startindex != -1 && endindex != -1 && cookievalue.length > 0)
    return (cookievalue);
  else
    return ("");
}

function Startup()
{   CalculateAll();
  document.forms.gameresult.Spla.value = GetCookieValue("PS");
  document.forms.gameresult.Sopp.value = GetCookieValue("OS");
  document.forms.gameresult.Rold.value = GetCookieValue("PR");
  document.forms.gameresult.Ropp.value = GetCookieValue("OR");
  document.forms.gameresult.Gpla.value = GetCookieValue("PG");
  document.forms.gameresult.Gopp.value = GetCookieValue("OG");
}


