private function BuildEconomyData(array $bowling)
 {
     $data = "";
     $balls_bowled = 0;
     $runs_conceded = 0;
     $total_performances = count($bowling);
     $show_every_second_performance = $total_performances > $this->max_x_axis_scale_points;
     for ($i = 0; $i < $total_performances; $i++) {
         $performance = $bowling[$i];
         /* @var $performance Bowling */
         # Always count the data for every performance
         if (!is_null($performance->GetOvers())) {
             $runs_conceded += $performance->GetRunsConceded();
             $balls_bowled += StoolballStatistics::OversToBalls($performance->GetOvers());
         }
         # Sample the performances plotted to prevent the scale getting too cluttered
         if (!$show_every_second_performance or $i % 2 === 0) {
             if (strlen($data)) {
                 $data .= ",";
             }
             if (is_null($performance->GetOvers())) {
                 $data .= "null";
             } else {
                 $data .= StoolballStatistics::BowlingEconomy(StoolballStatistics::BallsToOvers($balls_bowled), $runs_conceded);
             }
         }
     }
     return $data;
 }
 /**
  * If recently-added batting/bowling records are not reflected in statistics, run this method
  * @return void
  */
 public function RecalculateStatistics()
 {
     # Gather batting stats
     $this->total_innings = 0;
     $this->all_scores = array();
     $this->scores_out = array();
     $this->scores_not_out = array();
     $this->scores_with_balls_faced = array();
     $this->balls_faced = array();
     $this->batting_best = null;
     $this->batting_average = null;
     $this->batting_strike_rate = null;
     $this->batting_50 = 0;
     $this->batting_100 = 0;
     $this->how_out = array();
     foreach ($this->batting as $batting) {
         /* @var $batting Batting */
         if ($batting->GetHowOut() != Batting::DID_NOT_BAT) {
             $this->total_innings++;
         }
         if (!array_key_exists($batting->GetHowOut(), $this->how_out)) {
             $this->how_out[$batting->GetHowOut()] = 0;
         }
         $this->how_out[$batting->GetHowOut()]++;
         if (!is_null($batting->GetRuns())) {
             $this->all_scores[] = $batting->GetRuns();
             if ($this->IsNotOut($batting->GetHowOut())) {
                 $this->scores_not_out[] = $batting->GetRuns();
             } else {
                 $this->scores_out[] = $batting->GetRuns();
             }
             if ($batting->GetRuns() >= 50) {
                 $this->batting_50++;
             }
             if ($batting->GetRuns() >= 100) {
                 $this->batting_100++;
             }
             if (is_null($this->batting_best) or $batting->GetRuns() > intval($this->batting_best) or $batting->GetRuns() == intval($this->batting_best) && $this->IsNotOut($batting->GetHowOut())) {
                 $this->batting_best = $batting->GetRuns();
                 if ($this->IsNotOut($batting->GetHowOut())) {
                     $this->batting_best .= "*";
                 }
             }
             if (!is_null($batting->GetBallsFaced())) {
                 $this->scores_with_balls_faced[] = $batting->GetRuns();
                 $this->balls_faced[] = $batting->GetBallsFaced();
             }
         }
     }
     $this->not_outs = array_key_exists(Batting::NOT_OUT, $this->how_out) ? $this->how_out[Batting::NOT_OUT] : 0;
     if (count($this->all_scores)) {
         # retired is not out for purpose of average
         $retired = array_key_exists(Batting::RETIRED, $this->how_out) ? $this->how_out[Batting::RETIRED] : 0;
         $retired_hurt = array_key_exists(Batting::RETIRED_HURT, $this->how_out) ? $this->how_out[Batting::RETIRED_HURT] : 0;
         $total_innings_for_average = $this->total_innings - $this->not_outs - $retired - $retired_hurt;
         if ($total_innings_for_average > 0) {
             $this->batting_average = round(array_sum($this->all_scores) / $total_innings_for_average, 2);
         }
         # Work out the ranges scores are in
         $this->WorkOutScoreSpread();
     }
     if (count($this->scores_with_balls_faced)) {
         $total_balls_faced = array_sum($this->balls_faced);
         if ($total_balls_faced) {
             $this->batting_strike_rate = round(array_sum($this->scores_with_balls_faced) / $total_balls_faced * 100, 2);
         }
     }
     # Gather bowling stats
     $this->bowling_innings = 0;
     $this->balls = null;
     $this->overs = null;
     $this->maidens = null;
     $this->runs_against = null;
     $this->wickets_taken = 0;
     $this->wickets_taken_for_analysis = 0;
     $this->bowling_economy = null;
     $this->bowling_average = null;
     $this->bowling_strike_rate = null;
     $this->bowling_best = null;
     $best_wickets = 0;
     $best_runs = 0;
     $this->bowling_5 = 0;
     foreach ($this->bowling as $bowling) {
         /* @var $bowling Bowling */
         $this->bowling_innings += 1;
         if (!is_null($bowling->GetOvers())) {
             # Initialise balls, since we now know there are some recorded
             if (is_null($this->balls)) {
                 $this->balls = 0;
             }
             # Work out how many balls were bowled - have to assume 8 ball overs, even though on rare occasions that may not be the case
             $this->balls += StoolballStatistics::OversToBalls($bowling->GetOvers());
         }
         if (!is_null($bowling->GetMaidens())) {
             # Initialise maidens now we know the stat has been recorded
             if (is_null($this->maidens)) {
                 $this->maidens = 0;
             }
             # Add these maidens to total bowled
             $this->maidens += $bowling->GetMaidens();
         }
         if (!is_null($bowling->GetRunsConceded())) {
             # Initialise runs, since we now know there are some recorded
             if (is_null($this->runs_against)) {
                 $this->runs_against = 0;
             }
             # Add these runs to the total
             $this->runs_against += $bowling->GetRunsConceded();
         }
         if (!is_null($bowling->GetWickets())) {
             $this->wickets_taken += $bowling->GetWickets();
             if ($bowling->GetWickets() >= 5) {
                 $this->bowling_5++;
             }
             # Record wickets separately for average and strike rate, because otherwise wickets recorded on batting card
             # with no overs recorded on bowling card distort those statistics.
             if (!is_null($bowling->GetRunsConceded())) {
                 $this->wickets_taken_for_average += $bowling->GetWickets();
             }
             if (!is_null($bowling->GetOvers())) {
                 $this->wickets_taken_for_strike_rate += $bowling->GetWickets();
             }
         }
         if (!is_null($bowling->GetWickets())) {
             if ($bowling->GetWickets() > $best_wickets or $bowling->GetWickets() == $best_wickets and (!is_null($bowling->GetRunsConceded()) and (is_null($best_runs) or $bowling->GetRunsConceded() < $best_runs))) {
                 $best_wickets = $bowling->GetWickets();
                 $best_runs = $bowling->GetRunsConceded();
             }
         }
     }
     if (!is_null($this->balls) and $this->balls > 0 and !is_null($this->runs_against)) {
         $this->overs = StoolballStatistics::BallsToOvers($this->balls);
         $this->bowling_economy = StoolballStatistics::BowlingEconomy($this->overs, $this->runs_against);
     }
     if ($this->wickets_taken > 0) {
         $this->bowling_best = "{$best_wickets}/{$best_runs}";
     }
     if ($this->wickets_taken_for_average > 0 and !is_null($this->runs_against)) {
         $this->bowling_average = StoolballStatistics::BowlingAverage($this->runs_against, $this->wickets_taken_for_average);
     }
     if ($this->wickets_taken_for_strike_rate > 0 and $this->balls > 0) {
         $this->bowling_strike_rate = StoolballStatistics::BowlingStrikeRate($this->overs, $this->wickets_taken_for_strike_rate);
     }
 }