Completed
Branch dev-1.7.x (184d88)
by Boudry
05:25
created

InstantRunoff::compute()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 46
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 8.3646

Importance

Changes 0
Metric Value
cc 8
eloc 37
nc 10
nop 0
dl 0
loc 46
ccs 23
cts 28
cp 0.8214
crap 8.3646
rs 5.5555
c 0
b 0
f 0
1
<?php
2
/*
3
    Part of INSTANT-RUNOFF method Module - From the original Condorcet PHP
4
5
    Condorcet PHP - Election manager and results calculator.
6
    Designed for the Condorcet method. Integrating a large number of algorithms extending Condorcet. Expandable for all types of voting systems.
7
8
    By Julien Boudry and contributors - MIT LICENSE (Please read LICENSE.txt)
9
    https://github.com/julien-boudry/Condorcet
10
*/
11
declare(strict_types=1);
12
13
namespace Condorcet\Algo\Methods;
14
15
use Condorcet\Algo\Method;
16
use Condorcet\Algo\MethodInterface;
17
18
use Condorcet\Result;
19
20
class InstantRunoff extends Method implements MethodInterface
21
{
22
    // Method Name
23
    public const METHOD_NAME = ['Instant-runoff', 'InstantRunoff', 'preferential voting', 'ranked-choice voting', 'alternative vote', 'AlternativeVote', 'transferable vote', 'Vote alternatif'];
24
25
    public static $starting = 1;
26
27
    protected $_Stats;
28
29 4
    protected function getStats(): array
30
    {
31 4
        $stats = [];
32
33 4
        return $stats;
34
    }
35
36
37
/////////// COMPUTE ///////////
38
39
    //:: Alternative Vote ALGORITHM. :://
40
41 4
    protected function compute (): void
42
    {
43 4
        $candidateCount = $this->_selfElection->countCandidates();
44 4
        $candidateDone = [];
45 4
        $result = [];
46 4
        $CandidatesWinnerCount = 0;
47 4
        $CandidatesLoserCount = 0;
48
49 4
        while (count($candidateDone) < $candidateCount) :
50 4
            $score = $this->makeScore($candidateDone);
51 4
            $maxScore = max($score);
52 4
            $minScore = min($score);
53
54 4
            if ($maxScore > $this->_selfElection->sumVotesWeight()) :
55
                foreach ($score as $candidateKey => $candidateScore) :
56
                    if ($candidateScore !== $maxScore) :
57
                        continue;
58
                    else :
59
                        $candidateDone[] = $candidateKey;
60
                        $result[++$CandidatesWinnerCount][] = $candidateKey;
61
                    endif;
62
                endforeach;
63
            else :
64 4
                $LosersToRegister = [];
65
66 4
                foreach ($score as $candidateKey => $candidateScore) :
67 4
                    if ($candidateScore !== $minScore) :
68 4
                        continue;
69
                    else :
70 4
                        $LosersToRegister[] = $candidateKey;
71
                    endif;
72
                endforeach;
73
74 4
                if (count($LosersToRegister) > 1) :
75 2
                    $LosersToRegister = $this->tieBreaking($LosersToRegister);
76
                endif;
77
78 4
                $CandidatesLoserCount += count($LosersToRegister);
79 4
                $candidateDone = array_merge($candidateDone, $LosersToRegister);
80 4
                $result[$candidateCount - $CandidatesLoserCount + 1] = $LosersToRegister;
81
            endif;
82
83
        endwhile;
84
85 4
        $this->_Result = $this->createResult($result);
86 4
    }
87
88 4
    protected function makeScore (array $candidateDone): array
89
    {
90 4
        $score = [];
91 4
        foreach ($this->_selfElection->getCandidatesList() as $oneCandidate) :
92 4
            if (!in_array($this->_selfElection->getCandidateKey($oneCandidate), $candidateDone, true)) :
93 4
                $score[$this->_selfElection->getCandidateKey($oneCandidate)] = 0;
94
            endif;
95
        endforeach;
96
97 4
        foreach ($this->_selfElection->getVotesManager() as $oneVote) :
98 4
            $weight = ($this->_selfElection->isVoteWeightIsAllowed()) ? $oneVote->getWeight() : 1;
99
100 4
            for ($i = 0; $i < $weight; $i++) :
101 4
                $oneRanking = $oneVote->getContextualRanking($this->_selfElection);
102
103 4
                foreach ($oneRanking as $oneRank) :
104 4
                    foreach ($oneRank as $oneCandidate) :
105 4
                        if (count($oneRank) !== 1) :
106
                            break;
107 4
                        elseif (!in_array($this->_selfElection->getCandidateKey($oneCandidate), $candidateDone, true)) :
108 4
                            $score[$this->_selfElection->getCandidateKey(reset($oneRank))] += 1;
109 4
                            break 2;
110
                        endif;
111
                    endforeach;
112
                endforeach;
113
            endfor;
114
        endforeach;
115
116 4
        return $score;
117
    }
118
119 2
    protected function tieBreaking (array $candidatesKeys): array
120
    {
121 2
        $pairwise = $this->_selfElection->getPairwise(false);
122 2
        $tooKeep = [];
123
124 2
        foreach ($candidatesKeys as $oneCandidateKeyTotest) :
125 2
            $select = true;
126 2
            foreach ($candidatesKeys as $oneChallengerKey) :
127 2
                if ($oneCandidateKeyTotest === $oneChallengerKey) :
128 2
                    continue;
129
                endif;
130
131 2
                if ($pairwise[$oneCandidateKeyTotest]['win'][$oneChallengerKey] > $pairwise[$oneCandidateKeyTotest]['lose'][$oneChallengerKey]) :
132 2
                    $select = false;
133
                endif;
134
            endforeach;
135
136 2
            if ($select) :
137 2
                $tooKeep[] = $oneCandidateKeyTotest;
138
            endif;
139
        endforeach;
140
141 2
        return $tooKeep;
142
    }
143
}
144