/////////////////////////////////////////////////////////////////////////////
// ELOstat way of computing confidence intervals
/////////////////////////////////////////////////////////////////////////////
void CBradleyTerry::ELOstatIntervals(double *peloLower,
                                     double *peloUpper) const
{
 //
 // Loop over players to compute confidence intervals
 //
 for (int j = crs.GetPlayers(); --j >= 0;)
 {
  //
  // Loop over opponents
  //
  double TotalGames = 0;
  double TotalScore = 0;
  for (int k = crs.GetOpponents(j); --k >= 0;)
  {
   const CCondensedResult &cr = crs.GetCondensedResult(j, k);
   TotalGames += cr.Games();
   TotalScore += cr.Score();
  }
  double MeanScore = TotalScore / TotalGames;

  //
  // Loop again for variance
  //
  double TotalVariance = 0;
  for (int k = crs.GetOpponents(j); --k >= 0;)
  {
   const CCondensedResult &cr = crs.GetCondensedResult(j, k);
   TotalVariance += (cr.w_ij + cr.l_ji)*(1 - MeanScore)*(1 - MeanScore) +
                    (cr.d_ij + cr.d_ji)*(0.5 - MeanScore)*(0.5 - MeanScore) +
                    (cr.w_ji + cr.l_ij)*(0 - MeanScore)*(0 - MeanScore);
  }
  if (TotalVariance < 0)
   TotalVariance = 0;
  double Variance = TotalVariance / (TotalGames * TotalGames);
  double StandardDeviation = std::sqrt(Variance);
  double pLower = ELOstatBound(MeanScore - 1.95996 * StandardDeviation);
  double pUpper = ELOstatBound(MeanScore + 1.95996 * StandardDeviation);
  double p = ELOstatBound(MeanScore);
  double DeltaLower = 400 * std::log10(pLower / (1 - pLower));
  double DeltaUpper = 400 * std::log10(pUpper / (1 - pUpper));
  double Delta = 400 * std::log10(p / (1 - p));
  peloLower[j] = Delta - DeltaLower;
  peloUpper[j] = DeltaUpper - Delta;
 }
}
/////////////////////////////////////////////////////////////////////////////
// ELOstat algorithm
/////////////////////////////////////////////////////////////////////////////
void CBradleyTerry::ELOstat(double Epsilon) {
  double *pElo = &v1[0];
  double *pNextElo = &v2[0];
  for (int i = crs.GetPlayers(); --i >= 0;)
    pElo[i] = 0;

  for (int i = 0; i < 10000; i++) {
    //
    // Start by copying ratings
    //
    for (int j = crs.GetPlayers(); --j >= 0;)
      pNextElo[j] = pElo[j];

    //
    // Loop over players to compute their new ratings
    //
    double TotalElo = 0;
    double GrandTotalGames = 0;
    for (int j = crs.GetPlayers(); --j >= 0;) {
      double TotalGames = 0;
      double TotalScore = 0;
      double TotalOpponentElo = 0;

      //
      // Loop over opponents
      //
      for (int k = crs.GetOpponents(j); --k >= 0;) {
        const CCondensedResult &cr = crs.GetCondensedResult(j, k);
        double Games = cr.Games();
        TotalGames += Games;
        TotalScore += cr.Score();
        TotalOpponentElo += Games * pElo[cr.Opponent];
      }

      double p = ELOstatBound(TotalScore / TotalGames);
      double Delta = 400 * std::log10(p / (1 - p));
      pNextElo[j] = TotalOpponentElo / TotalGames + Delta;
      TotalElo += TotalGames * pNextElo[j];
      GrandTotalGames += TotalGames;
    }

    //
    // Normalize and swap
    //
    for (int j = crs.GetPlayers(); --j >= 0;)
      pNextElo[j] -= TotalElo / GrandTotalGames;
    {
      double *pd = pElo;
      pElo = pNextElo;
      pNextElo = pd;
    }

    //
    // Compute difference
    //
    double MaxDiff = 0;
    for (int j = crs.GetPlayers(); --j >= 0;) {
      double Diff = std::fabs(pNextElo[j] - pElo[j]);
      if (Diff > MaxDiff)
        MaxDiff = Diff;
    }
    if (MaxDiff < Epsilon) {
      std::cout << i + 1 << " iterations\n";
      break;
    }
  }

  //
  // Store computed Elo ratings into vElo
  //
  for (int i = crs.GetPlayers(); --i >= 0;)
    velo[i] = pElo[i];
}