function getPersons () {
#----------------------------------------------------------------------
  global $personsFromPersons, $personsFromResultsWithoutId;

  $personsFromResultsWithoutId = [];
  $persons = dbQueryHandle("
    SELECT DISTINCT personName, Results.countryId, year
    FROM Results
    JOIN Competitions ON Competitions.id=Results.competitionId
    WHERE personId=''
    ORDER BY personName
  ");
  while( $row = mysql_fetch_row( $persons ))
    $personsFromResultsWithoutId[] = $row;
  mysql_free_result( $persons );

  $personsFromPersons = [];
  if (count( $personsFromResultsWithoutId )) {
    $persons = dbQueryHandle("
      SELECT id, name, countryId
      FROM Persons
      ORDER BY name
    ");
    while( $row = mysql_fetch_row( $persons ))
      $personsFromPersons[] = $row;
    mysql_free_result( $persons );
  }

}
function computeRanks ( $valueSource, $valueName ) {
#----------------------------------------------------------------------

  startTimer();
  echo "<br />Building table Ranks$valueName...<br />\n";

  #--- Create empty table
  dbCommand( "DROP TABLE IF EXISTS Ranks$valueName" );
  dbCommand( "CREATE TABLE Ranks$valueName (
    `id` INTEGER NOT NULL AUTO_INCREMENT,
    `personId` VARCHAR(10) NOT NULL DEFAULT '',
    `eventId` VARCHAR(6) NOT NULL DEFAULT '',
    `best` INTEGER NOT NULL DEFAULT '0',
    `worldRank` INTEGER NOT NULL DEFAULT '0',
    `continentRank` INTEGER NOT NULL DEFAULT '0',
    `countryRank` INTEGER NOT NULL DEFAULT '0',
  PRIMARY KEY  (`id`),
  KEY `fk_persons` (`personId`),
  KEY `fk_events` (`eventId`)) COLLATE latin1_swedish_ci
  " );

  #--- Determine everybody's current country and continent
  $persons = dbQuery( "
    SELECT   person.id personId, countryId, continentId
    FROM     Persons person, Countries country
    WHERE    country.id=countryId
      AND    person.subId=1
  " );
  foreach( $persons as $person ) {
    extract( $person );
    $currentCountry  [$personId] = $countryId;
    $currentContinent[$personId] = $continentId;
  }
  unset( $persons );

  #--- Get all personal records (note: country-switchers appear once for each country)
  $personalRecords = dbQueryHandle( "
    SELECT   personId, countryId, continentId, eventId, min($valueSource) value
    FROM     Concise${valueName}Results
    WHERE    eventId <> '333mbo'
    GROUP BY personId, countryId, eventId
    ORDER BY eventId, value
  " );

  #--- Process the personal records
  $missingPersonIDs = false;
  while( $row = mysql_fetch_row( $personalRecords )){
    list( $personId, $countryId, $continentId, $eventId, $value ) = $row;
    if( ! $personId ){
      $missingPersonIDs = true;
      continue;
    }

    #--- At new event, store the ranks of the previous and reset
    if ( isset($latestEventId)  &&  $eventId != $latestEventId ) {
      storeRanks( $valueName, $latestEventId, $personRecord, $personWR, $personCR, $personNR );
      unset( $ctr, $rank, $record, $ranked, $personRecord, $personWR, $personCR, $personNR );
    }

    #--- Update the region states (unless we have ranked this person there already, for
    #--- example 2008SEAR01 twice in North America and World because of his two countries)
    foreach( array( 'World', $continentId, $countryId ) as $region ){
      if ( ! isset($ranked[$region][$personId]) ) {
        $ctr[$region] = isset($ctr[$region]) ? $ctr[$region] + 1 : 1;    # ctr always increases
        if ( !isset($record[$region])  ||  $value > $record[$region] )   # rank only if value worse than previous
          $rank[$region] = $ctr[$region];
        $record[$region] = $value;
        $ranked[$region][$personId] = true;
      }
    }

    #--- Set the person's data (first time the current location is matched)
    if ( ! isset($personRecord[$personId]) ) {
      $personRecord[$personId] = $value;
      $personWR[$personId] = $rank['World'];
    }
    if ( $continentId==$currentContinent[$personId] && ! isset($personCR[$personId]) )
      $personCR[$personId] = $rank[$continentId];
    if ( $countryId==$currentCountry[$personId] && ! isset($personNR[$personId]) )
      $personNR[$personId] = $rank[$countryId];

    $latestEventId = $eventId;
  }

  #--- Free the result handle
  mysql_free_result( $personalRecords );

  #--- Store the ranks of the last event  
  storeRanks( $valueName, $latestEventId, $personRecord, $personWR, $personCR, $personNR );

  if( $missingPersonIDs )
    noticeBox3( 0, 'Warning: some results are ignored because they are missing a personId' );
  stopTimer( "Ranks$valueName" );
  echo "... done<br /><br />\n";
}
function checkRelatively()
{
    #----------------------------------------------------------------------
    global $competitionCondition, $chosenWhich;
    echo "<hr /><p>Checking <b>" . $chosenWhich . " ranks</b>... (wait for the result message box at the end)</p>\n";
    #--- Get all results (except the trick-duplicated (old) multiblind)
    $rows = dbQueryHandle("\n    SELECT   result.id, competitionId, eventId, roundId, formatId, average, best, pos, personName\n    FROM     Results result, Competitions competition\n    WHERE    competition.id = competitionId\n      {$competitionCondition}\n      AND    (( eventId <> '333mbf' ) OR (( competition.year = 2009 ) AND ( competition.month > 1 )) OR ( competition.year > 2009 ))\n    ORDER BY year desc, month desc, day desc, competitionId, eventId, roundId, IF(formatId IN ('a','m') AND average>0, average, 2147483647), if(best>0, best, 2147483647), pos\n  ");
    #--- Begin the form
    echo "<form action='check_results_ACTION.php' method='post'>\n";
    #--- Check the pos values
    $prevRound = $shownRound = '';
    $wrongs = $wrongRounds = 0;
    $wrongComp = array();
    while ($row = mysql_fetch_row($rows)) {
        list($resultId, $competitionId, $eventId, $roundId, $formatId, $average, $best, $storedPos, $personName) = $row;
        $round = "{$competitionId}|{$eventId}|{$roundId}";
        if ($formatId == 'm' || $formatId == 'a') {
            $result = "{$average}|{$best}";
        } else {
            $result = $best;
        }
        if ($round != $prevRound) {
            $ctr = $calcedPos = 1;
        }
        if ($ctr > 1 && $result != $prevResult) {
            $calcedPos = $ctr;
        }
        if ($storedPos != $calcedPos) {
            #--- Before the first difference in a round, show the round's full results
            if ($round != $shownRound) {
                $wrongRounds++;
                $wrongComp[$competitionId] = true;
                echo "<p style='margin-top:2em; margin-bottom:0'><a href='https://www.worldcubeassociation.org/results/c.php?i={$competitionId}&allResults=1#e{$eventId}_{$roundId}'>{$competitionId} - {$eventId} - {$roundId}</a></p>";
                showCompetitionResults($competitionId, $eventId, $roundId);
                $shownRound = $round;
                #--- Show a check all and a check none button.
                printf("<button class='js-check-all' data-round='{$round}'>Check all</button>");
                printf("<button class='js-check-none' data-round='{$round}'>Check none</button>");
                printf("<br>");
            }
            #--- Show each difference, with a checkbox to agree
            $change = sprintf('%+d', $calcedPos - $storedPos);
            $checkbox = "<input type='checkbox' name='setpos{$resultId}' value='{$calcedPos}' data-round='{$round}' />";
            printf("{$checkbox} Place {$storedPos} should be place {$calcedPos} (change by {$change}) --- {$personName}<br />");
            $wrongs++;
        }
        $prevRound = $round;
        $prevResult = $result;
        $ctr++;
    }
    mysql_free_result($rows);
    #--- Tell the result.
    $date = wcaDate();
    noticeBox2(!$wrongs, "We agree about all checked places.<br />{$date}", "<p>Darn! We disagree: {$wrongs} possibly wrong places, in {$wrongRounds} rounds, in " . count($wrongComp) . " competitions<br /><br />{$date}</p>" . "<p>Choose the changes you agree with above, then click the 'Execute...' button below. It will result in something like the following.</p>" . "<pre>I'm doing this:\n" . "UPDATE Results SET pos=111 WHERE id=11111\n" . "UPDATE Results SET pos=222 WHERE id=22222\n" . "UPDATE Results SET pos=333 WHERE id=33333\n" . "</pre>");
    #--- If differences were found, offer to fix them.
    if ($wrongs) {
        echo "<center><input type='submit' value='Execute the agreed changes!' /></center>\n";
    }
    #--- Finish the form.
    echo "</form>\n";
}
function computeRegionalRecordMarkers($valueId, $valueName)
{
    #----------------------------------------------------------------------
    global $chosenAnything, $chosenCompetitionId, $differencesWereFound, $previousRecord, $pendingCompetitions, $startDate;
    # -----------------------------
    # Description of the main idea:
    # -----------------------------
    # Get all results that are potential regional records. Process them one
    # event at a time. Inside, process them one competition at a time, in
    # chronological order of start date. Each competition's results are only
    # compared against records of strictly previous competitions, not against
    # parallel competitions. For this, there are these main arrays:
    #
    # - $previousRecord[regionId] is a running collection of each region's record,
    #   covering all competitions *before* the current one.
    #
    # - $record[regionId] is based on $previousRecord and is used and updated
    #   inside the current competition.
    #
    # - $pendingCompetitions[regionId] holds $record of competitions already
    #   processed but not merged into $previousRecord. When a new competition is
    #   encountered, we merge those that ended before the new one into $previousRecord.
    #
    # - $baseRecord[eventId][regionId] is for when a user chose a specific
    #   competition to check. Then we quickly ask the database for the current
    #   region records from before that competition. This could be used for
    #   giving the user a year-option as well, but we don't have that (yet?).
    # -----------------------------
    #--- If a competition was chosen, we need all records from before it
    if ($chosenCompetitionId) {
        $startDate = getCompetitionValue($chosenCompetitionId, "year*10000 + month*100 + day");
        $results = dbQueryHandle("\n      SELECT eventId, result.countryId, continentId, min({$valueId}) value, event.format valueFormat\n      FROM Results result, Competitions competition, Countries country, Events event\n      WHERE {$valueId} > 0\n        AND competition.id = result.competitionId\n        AND country.id     = result.countryId\n        AND event.id       = result.eventId\n        AND year*10000 + if(endMonth,endMonth,month)*100 + if(endDay,endDay,day) < {$startDate}\n      GROUP BY eventId, countryId");
        while ($row = mysql_fetch_row($results)) {
            list($eventId, $countryId, $continentId, $value, $valueFormat) = $row;
            if (isSuccessValue($value, $valueFormat)) {
                foreach (array($countryId, $continentId, 'World') as $regionId) {
                    if (!isset($baseRecord[$eventId][$regionId]) || $value < $baseRecord[$eventId][$regionId]) {
                        $baseRecord[$eventId][$regionId] = $value;
                    }
                }
            }
        }
        mysql_free_result($results);
    } else {
        $competitions = dbQuery("\n      SELECT id, year*10000 + if(endMonth,endMonth,month)*100 + if(endDay,endDay,day) endDate\n      FROM   Competitions competition");
        foreach ($competitions as $competition) {
            $endDate[$competition['id']] = $competition['endDate'];
        }
    }
    #--- The IDs of relevant results (those already marked as region record and those that could be)
    $queryRelevantIds = "\n   (SELECT id FROM Results WHERE regional{$valueName}Record<>'' " . eventCondition() . competitionCondition() . ")\n   UNION\n   (SELECT id\n    FROM\n      Results result,\n      (SELECT eventId, competitionId, roundId, countryId, min({$valueId}) value\n       FROM Results\n       WHERE {$valueId} > 0\n       " . eventCondition() . competitionCondition() . "\n       GROUP BY eventId, competitionId, roundId, countryId) helper\n    WHERE result.eventId       = helper.eventId\n      AND result.competitionId = helper.competitionId\n      AND result.roundId       = helper.roundId\n      AND result.countryId     = helper.countryId\n      AND result.{$valueId}      = helper.value)";
    #--- Get the results, ordered appropriately
    $results = dbQueryHandle("\n    SELECT\n      year*10000 + month*100 + day startDate,\n      result.id resultId,\n      result.eventId,\n      result.competitionId,\n      result.roundId,\n      result.personId,\n      result.personName,\n      result.countryId,\n      result.regional{$valueName}Record storedMarker,\n      {$valueId} value,\n      continentId,\n      continent.recordName continentalRecordName,\n      event.format valueFormat\n    FROM\n      ({$queryRelevantIds}) relevantIds,\n      Results      result,\n      Competitions competition,\n      Countries    country,\n      Continents   continent,\n      Events       event,\n      Rounds       round\n    WHERE 1\n      AND result.id      = relevantIds.id\n      AND competition.id = result.competitionId\n      AND round.id       = result.roundId\n      AND country.id     = result.countryId\n      AND continent.id   = country.continentId\n      AND event.id       = result.eventId\n    ORDER BY event.rank, startDate, competitionId, round.rank, {$valueId}\n  ");
    #--- For displaying the dates, fetch all competitions
    $allCompetitions = array();
    foreach (dbQuery("SELECT * FROM Competitions") as $row) {
        $allCompetitions[$row['id']] = $row;
    }
    #--- Process each result.
    $currentEventId = $announcedEventId = $announcedRoundId = $announcedCompoId = NULL;
    while ($row = mysql_fetch_row($results)) {
        list($startDate, $resultId, $eventId, $competitionId, $roundId, $personId, $personName, $countryId, $storedMarker, $value, $continentId, $continentalRecordName, $valueFormat) = $row;
        #--- Handle failures of multi-attempts.
        if (!isSuccessValue($value, $valueFormat)) {
            continue;
        }
        #--- At new events, reset everything
        if ($eventId != $currentEventId) {
            $currentEventId = $eventId;
            $currentCompetitionId = false;
            $record = $previousRecord = isset($baseRecord[$eventId]) ? $baseRecord[$eventId] : array();
            $pendingCompetitions = array();
        }
        #--- Handle new competitions.
        if ($competitionId != $currentCompetitionId) {
            #--- Add the records of the previously current competition to the set of pending competition records
            if ($currentCompetitionId) {
                $pendingCompetitions[] = array($endDate[$currentCompetitionId], $record);
            }
            #--- Note the current competition
            $currentCompetitionId = $competitionId;
            #--- Prepare the records this competition will be based on
            $pendingCompetitions = array_filter($pendingCompetitions, "handlePendingCompetition");
            $record = $previousRecord;
        }
        #--- Calculate whether it's a new region record and update the records.
        $calcedMarker = '';
        if (!isset($record[$countryId]) || $value <= $record[$countryId]) {
            $calcedMarker = 'NR';
            $record[$countryId] = $value;
            if (!isset($record[$continentId]) || $value <= $record[$continentId]) {
                $calcedMarker = $continentalRecordName;
                $record[$continentId] = $value;
                if (!isset($record['World']) || $value <= $record['World']) {
                    $calcedMarker = 'WR';
                    $record['World'] = $value;
                }
            }
        }
        #--- If stored or calculated marker say it's some regional record at all...
        if ($storedMarker || $calcedMarker) {
            #--- Do stored and calculated agree? Choose colors and update list of differences.
            $same = $storedMarker == $calcedMarker;
            $storedColor = $same ? '999' : 'F00';
            $calcedColor = $same ? '999' : '0E0';
            if (!$same) {
                $selectedIds[] = $resultId;
                $differencesWereFound = true;
            }
            #--- If no filter was chosen, only show differences.
            if (!$chosenAnything && $same) {
                continue;
            }
            #--- Highlight regions if the calculated marker thinks it's a record for them.
            $countryName = $countryId;
            $continentName = substr($continentId, 1);
            $worldName = 'World';
            if ($calcedMarker) {
                $countryName = "<b>{$countryName}</b>";
            }
            if ($calcedMarker && $calcedMarker != 'NR') {
                $continentName = "<b>{$continentName}</b>";
            }
            if ($calcedMarker == 'WR') {
                $worldName = "<b>{$worldName}</b>";
            }
            #--- Recognize new events/rounds/competitions.
            $announceEvent = $eventId != $announcedEventId;
            $announcedEventId = $eventId;
            $announceRound = $roundId != $announcedRoundId;
            $announcedRoundId = $roundId;
            $announceCompo = $competitionId != $announcedCompoId;
            $announcedCompoId = $competitionId;
            #--- If new event, announce it.
            if ($announceEvent) {
                tableCaption(false, "{$eventId} {$valueName}");
                tableHeader(explode('|', 'Date|Competition|Round|Person|Event|Country|Continent|World|Value|Stored|Computed|Agree'), array(7 => "class='R2'"));
            }
            #--- If new round/competition inside an event, add a separator row.
            if (($announceRound || $announceCompo) && !$announceEvent) {
                tableRowEmpty();
            }
            #--- Prepare the checkbox.
            $checkbox = "<input type='checkbox' name='update{$valueName}{$resultId}' value='{$calcedMarker}' />";
            #--- Show the result.
            tableRow(array(competitionDate($allCompetitions[$competitionId]), competitionLink($competitionId, $competitionId), $roundId, personLink($personId, $personName), $eventId, $countryName, $continentName, $worldName, formatValue($value, $valueFormat), "<span style='font-weight:bold;color:#{$storedColor}'>{$storedMarker}</span>", "<span style='font-weight:bold;color:#{$calcedColor}'>{$calcedMarker}</span>", $same ? '' : $checkbox));
        }
    }
    mysql_free_result($results);
}
function getIso2FromCountryId($countryId) {
#----------------------------------------------------------------------
  $country = dbQueryHandle("
    SELECT iso2
    FROM Countries
    WHERE id='$countryId'
  ");
  $row = mysql_fetch_row( $country );
  if ( !$row ){
      showErrorMessage( "'$countryId' is not a known country ID'" );
      return null;
  }
  return $row[0];
}
function buildGraph($eventName, $eventId, $divide)
{
    #----------------------------------------------------------------------
    #pretty("buildGraph( $eventName, $eventId, $divide ) ...");
    $series = array();
    $highestCurrent = 0;
    $yMax = 0;
    #--- Build the series of this event
    foreach (array('1 best green', '1 average green', '10 best blue', '10 average blue', '100 best red', '100 average red') as $lineData) {
        list($n, $valueId, $color) = explode(' ', $lineData);
        #--- Get this event's results
        $rows = dbQueryHandle("\n      SELECT personId, {$valueId} value, year + datediff(year*10000+month*100+day,year*10000+101)/365.25 date\n      FROM Results result, Competitions competition\n      WHERE competition.id = competitionId\n        AND eventId = '{$eventId}' AND {$valueId} > 0\n      ORDER BY year, month, day\n    ");
        // Gather the data
        $xdata = array();
        // x-coordinates for the line-points of the graph
        $ydata = array();
        // y-coordinates for the line-points of the graph
        $data = array();
        // coordinates for the line-points of the graph
        $records = array();
        // $records[$personId] = record of that person
        $nthPlaceRecord = false;
        // record of the n-th place person
        $direction = $eventId == '333mbf' ? -1 : 1;
        while ($row = mysql_fetch_row($rows)) {
            list($personId, $value, $date) = $row;
            if ($eventId == '333mbf') {
                $value = ($value - $value % 10000000) / 10000000 - 99;
            }
            if ($eventId == '333fm' && $valueId == 'average') {
                $value /= 100;
            }
            #      if ( $eventId == '333mbf' ) echo "$value ";
            //--- Skip values worse than n-th place
            if ($nthPlaceRecord && $value >= $nthPlaceRecord) {
                continue;
            }
            //--- Update this person's record
            $oldRecord = isset($records[$personId]) ? $records[$personId] : 1000000000;
            if ($value < $oldRecord) {
                $records[$personId] = $value;
            }
            //--- If we just reached n persons or this person just entered the top n...
            if (!$nthPlaceRecord && count($records) == $n || $oldRecord >= $nthPlaceRecord && $value < $nthPlaceRecord) {
                #echo "$nthPlaceRecord $oldRecord $value<br />";
                //--- Sort the records, determine the n-th place value, and just keep the top n
                asort($records);
                $keep = array();
                foreach ($records as $personId => $rec) {
                    $nthPlaceRecord = $keep[$personId] = $rec;
                    if (count($keep) == $n) {
                        break;
                    }
                }
                $records = $keep;
                //--- Remove previous line points for the same date
                while (end($xdata) == $date) {
                    array_pop($xdata);
                    array_pop($ydata);
                    array_pop($data);
                }
                if ($nthPlaceRecord * $direction == end($ydata)) {
                    continue;
                }
                //--- Add the line point
                $xdata[] = $date;
                $ydata[] = $nthPlaceRecord * $direction;
                $data[] = array(round($date, 2), $nthPlaceRecord * $direction);
                #printf( "<pre>%6.2f %6.2f</pre>", $date, $nthPlaceRecord );
                #echo "<pre>"; print_r( $records ); echo "</pre>";
            }
        }
        //--- Free the query handle
        mysql_free_result($rows);
        //--- Skip if we didn't even find n persons
        if (!$nthPlaceRecord) {
            continue;
        }
        //--- Remember the worst current shown record for later y-scale adjustment (worst will be at 1/3 height)
        $highestCurrent = max($highestCurrent, $nthPlaceRecord * $direction);
        #echo "<p>$eventName: $highestCurrent</p>";
        //--- Repeat the last height in the far future to finish with a straight line
        $xdata[] = 2222;
        $ydata[] = end($ydata);
        $data[] = array(2222, end($ydata));
        $yMax = max($yMax, max($ydata));
        $valueName = $valueId == 'best' ? 'Single' : 'Average';
        $series[] = "{ name: '#{$n} {$valueName}', color:'{$color}', data: " . json_encode($data) . '}';
    }
    #--- Build and return the javascript for this event's graph
    $chartJs = file_get_contents('charts_template.js');
    $chartJs = str_replace('$eventId', $eventId, $chartJs);
    $chartJs = str_replace('$xMax', 1970 + time() / 60 / 60 / 24 / 365.25, $chartJs);
    $chartJs = str_replace('$yMax', min($yMax, ($direction > 0 ? 3 : 1) * $highestCurrent), $chartJs);
    $chartJs = str_replace('$divide', $divide, $chartJs);
    $chartJs = str_replace('$eventName', json_encode(eventName($eventId)), $chartJs);
    $chartJs = str_replace('$series', implode(",\n    ", $series), $chartJs);
    return $chartJs;
}