<?php

declare(strict_types=1);

namespace CondorcetPHP\Condorcet\Benchmarks;

use CondorcetPHP\Condorcet\Algo\Methods\KemenyYoung\KemenyYoung;
use CondorcetPHP\Condorcet\Algo\Methods\RankedPairs\RankedPairs_Core;
use CondorcetPHP\Condorcet\Algo\StatsVerbosity;
use CondorcetPHP\Condorcet\{Condorcet, Election};
use CondorcetPHP\Condorcet\Throwable\MethodLimitReachedException;
use PhpBench\Attributes as Bench;

ini_set('memory_limit', '51200M');

class MethodsNonProportionalBench
{
    public bool $IS_A_PROPORTIONAL_BENCH = false;

    public array $numberOfCandidates = [3, 5, 6, 7, 8, 9, 10, 11, 20, 30, 40, 50, 60, 70, 80, 90, 100];

    protected Election $election;

    public function __construct()
    {
        RankedPairs_Core::$MaxCandidates = null;
        KemenyYoung::$MaxCandidates = 11;
    }


    protected function buildElection(int $numberOfCandidates, int $numberOfVotes): void
    {
        $randomizer = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar('CondorcetReproductibleRandomSeed'));

        $this->election = $election = new Election;
        $this->election->setNumberOfSeats(max(1, (int) ($numberOfCandidates / 3)));
        $this->election->setStatsVerbosity(StatsVerbosity::STD);

        $candidates = [];

        for ($i=0; $i < $numberOfCandidates; $i++) {
            $candidates[] = $election->addCandidate();
        }

        for ($i = 0; $i < $numberOfVotes; $i++) {
            $oneVote = $randomizer->shuffleArray($candidates);
            $election->addVote($oneVote);
        }
    }

    public function provideMethods(): \Generator
    {
        foreach (Condorcet::getAuthMethods() as $method) {
            $class = Condorcet::getMethodClass($method);

            if ($class::IS_PROPORTIONAL === $this->IS_A_PROPORTIONAL_BENCH) {
                yield $method => ['method' => $method];
            }
        }
    }

    public function provideNumberOfCandidates(): \Generator
    {
        foreach ($this->numberOfCandidates as $n) {
            yield $n => ['numberOfCandidates' => $n];
        }
    }

    public function setUp(array $params): void
    {
        $this->buildElection($params['numberOfCandidates'], 1_000);
    }

    #[Bench\OutputTimeUnit('seconds')]
    #[Bench\ParamProviders(['provideMethods', 'provideNumberOfCandidates'])]
    #[Bench\BeforeMethods('setUp')]
    #[Bench\Warmup(1)]
    #[Bench\Iterations(3)]
    #[Bench\Revs(1)]
    public function benchByCandidates(array $params): void
    {
        try {
            $result = $this->election->getResult($params['method']);
        } catch (MethodLimitReachedException $e) {
        }

        $this->election->cleanupCalculator();
    }
}
