Completed
Branch dev-1.7.x (349555)
by Boudry
04:56
created

InstantRunoff   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 126
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 126
ccs 62
cts 62
cp 1
rs 10
c 0
b 0
f 0
wmc 25
lcom 1
cbo 2

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getStats() 0 6 1
C compute() 0 48 8
D makeScore() 0 30 10
B tieBreaking() 0 24 6
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 5
    protected function getStats(): array
30
    {
31 5
        $stats = [];
32
33 5
        return $stats;
34
    }
35
36
37
/////////// COMPUTE ///////////
38
39
    //:: Alternative Vote ALGORITHM. :://
40
41 5
    protected function compute (): void
42
    {
43 5
        $candidateCount = $this->_selfElection->countCandidates();
44 5
        $majority = $this->_selfElection->sumVotesWeight() / 2;
45
46 5
        $candidateDone = [];
47 5
        $result = [];
48 5
        $CandidatesWinnerCount = 0;
49 5
        $CandidatesLoserCount = 0;
50
51 5
        while (count($candidateDone) < $candidateCount) :
52 5
            $score = $this->makeScore($candidateDone);
53 5
            $maxScore = max($score);
54 5
            $minScore = min($score);
55
56 5
            if ( $maxScore > $majority ) :
57 4
                foreach ($score as $candidateKey => $candidateScore) :
58 4
                    if ($candidateScore !== $maxScore) :
59 4
                        continue;
60
                    else :
61 4
                        $candidateDone[] = $candidateKey;
62 4
                        $result[++$CandidatesWinnerCount][] = $candidateKey;
63
                    endif;
64
                endforeach;
65
            else :
66 5
                $LosersToRegister = [];
67
68 5
                foreach ($score as $candidateKey => $candidateScore) :
69 5
                    if ($candidateScore !== $minScore) :
70 3
                        continue;
71
                    else :
72 5
                        $LosersToRegister[] = $candidateKey;
73
                    endif;
74
                endforeach;
75
76 5
                if (count($LosersToRegister) > 1) :
77 3
                    $LosersToRegister = $this->tieBreaking($LosersToRegister);
78
                endif;
79
80 5
                $CandidatesLoserCount += count($LosersToRegister);
81 5
                $candidateDone = array_merge($candidateDone, $LosersToRegister);
82 5
                $result[$candidateCount - $CandidatesLoserCount + 1] = $LosersToRegister;
83
            endif;
84
85
        endwhile;
86
87 5
        $this->_Result = $this->createResult($result);
88 5
    }
89
90 5
    protected function makeScore (array $candidateDone): array
91
    {
92 5
        $score = [];
93 5
        foreach ($this->_selfElection->getCandidatesList() as $oneCandidate) :
94 5
            if (!in_array($this->_selfElection->getCandidateKey($oneCandidate), $candidateDone, true)) :
95 5
                $score[$this->_selfElection->getCandidateKey($oneCandidate)] = 0;
96
            endif;
97
        endforeach;
98
99 5
        foreach ($this->_selfElection->getVotesManager() as $oneVote) :
100 5
            $weight = ($this->_selfElection->isVoteWeightIsAllowed()) ? $oneVote->getWeight() : 1;
101
102 5
            for ($i = 0; $i < $weight; $i++) :
103 5
                $oneRanking = $oneVote->getContextualRanking($this->_selfElection);
104
105 5
                foreach ($oneRanking as $oneRank) :
106 5
                    foreach ($oneRank as $oneCandidate) :
107 5
                        if (count($oneRank) !== 1) :
108 1
                            break;
109 4
                        elseif (!in_array($this->_selfElection->getCandidateKey($oneCandidate), $candidateDone, true)) :
110 4
                            $score[$this->_selfElection->getCandidateKey(reset($oneRank))] += 1;
111 5
                            break 2;
112
                        endif;
113
                    endforeach;
114
                endforeach;
115
            endfor;
116
        endforeach;
117
118 5
        return $score;
119
    }
120
121 3
    protected function tieBreaking (array $candidatesKeys): array
122
    {
123 3
        $pairwise = $this->_selfElection->getPairwise(false);
124 3
        $tooKeep = [];
125
126 3
        foreach ($candidatesKeys as $oneCandidateKeyTotest) :
127 3
            $select = true;
128 3
            foreach ($candidatesKeys as $oneChallengerKey) :
129 3
                if ($oneCandidateKeyTotest === $oneChallengerKey) :
130 3
                    continue;
131
                endif;
132
133 3
                if ($pairwise[$oneCandidateKeyTotest]['win'][$oneChallengerKey] > $pairwise[$oneCandidateKeyTotest]['lose'][$oneChallengerKey]) :
134 3
                    $select = false;
135
                endif;
136
            endforeach;
137
138 3
            if ($select) :
139 3
                $tooKeep[] = $oneCandidateKeyTotest;
140
            endif;
141
        endforeach;
142
143 3
        return $tooKeep;
144
    }
145
}
146