Passed
Branch master (73ca69)
by Boudry
03:33
created

RankedPairs_Core::getResult()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 8
cts 8
cp 1
rs 9.2
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 0
crap 2
1
<?php
2
/*
3
    Ranked Pairs part of the Condorcet PHP Class
4
5
    By Julien Boudry - MIT LICENSE (Please read LICENSE.txt)
6
    https://github.com/julien-boudry/Condorcet
7
*/
8
declare(strict_types=1);
9
10
namespace Condorcet\Algo\Methods;
11
12
use Condorcet\Algo\Method;
13
use Condorcet\Algo\MethodInterface;
14
use Condorcet\Algo\Tools\PairwiseStats;
15
use Condorcet\Election;
16
use Condorcet\Result;
17
18
// Ranker Pairs is a Condorcet Algorithm | http://en.wikipedia.org/wiki/Ranked_Pairs
19
class RankedPairs_Core extends Method implements MethodInterface
20
{
21
    // Limits
22
        public static $_maxCandidates = 40;
23
24
    // Ranked Pairs
25
    protected $_PairwiseSort;
26
    protected $_Arcs;
27
    protected $_Stats;
28
    protected $_StatsDone = false;
29
30
31
/////////// PUBLIC ///////////
32
33
34
    // Get the Ranked Pairs ranking
35 1
    public function getResult () : Result
36
    {
37
        // Cache
38 1
        if ( $this->_Result !== null ) :
39 1
            return $this->_Result;
40
        endif;
41
42
            //////
43
44
        // Sort pairwise
45 1
        $this->_PairwiseSort = $this->pairwiseSort();
46
47
        // Ranking calculation
48 1
        $this->makeArcs();
49
50
        // Make Stats
51 1
        $this->_Stats['tally'] = $this->_PairwiseSort;
52 1
        $this->_Stats['arcs'] = $this->_Arcs;
53
54
        // Make Result      
55 1
        return $this->_Result = $this->createResult($this->makeResult());
56
    }
57
58
    // Get the Ranked Pair ranking
59 1
    protected function getStats () : array
60
    {
61 1
        if (!$this->_StatsDone) :
62 1 View Code Duplication
            foreach ($this->_Stats['tally'] as &$Roundvalue) :
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
63 1
                foreach ($Roundvalue as $ArcKey => &$Arcvalue) :
64 1
                    foreach ($Arcvalue as $key => &$value) :
65 1
                        if ($key === 'from' || $key === 'to') :
66 1
                            $value = $this->_selfElection->getCandidateId($value, true);
67
                        endif;
68
                    endforeach;
69
                endforeach;
70
            endforeach;
71
72 1 View Code Duplication
            foreach ($this->_Stats['arcs'] as $ArcKey => &$Arcvalue) :
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
73 1
                foreach ($Arcvalue as $key => &$value) :
74 1
                    if ($key === 'from' || $key === 'to') :
75 1
                        $value = $this->_selfElection->getCandidateId($value, true);
76
                    endif;
77
                endforeach;
78
            endforeach;
79
80 1
            $this->_StatsDone = true;
81
        endif;
82
83 1
        return $this->_Stats;
84
    }
85
86
87
/////////// COMPUTE ///////////
88
89
90
    //:: RANKED PAIRS ALGORITHM. :://
91
92 1
    protected function makeResult () : array
93
    {
94 1
        $result = [];
95 1
        $alreadyDone = [];
96 1
        $lastWinner = null;
0 ignored issues
show
Unused Code introduced by
$lastWinner is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
97
98 1
        $rang = 1;
99 1
        while (count($alreadyDone) < $this->_selfElection->countCandidates()) :
100 1
            $winners = $this->getWinners($alreadyDone);
101
102 1
            foreach ($this->_Arcs as $ArcKey => $Arcvalue) :
103 1
                foreach ($winners as $oneWinner) :
104 1
                    if ($Arcvalue['from'] === $oneWinner || $Arcvalue['to'] === $oneWinner ) :
105 1
                        unset($this->_Arcs[$ArcKey]);
106
                    endif;
107
                endforeach;
108
            endforeach;
109
110 1
            $result[$rang++] = $winners;
111 1
            $alreadyDone = array_merge($alreadyDone,$winners);
112
        endwhile;
113
114 1
        return $result;
115
    }
116
117 1
    protected function getWinners (array $alreadyDone) : array
118
    {
119 1
        $winners = [];
120
121 1
        foreach ($this->_selfElection->getCandidatesList() as $candidateKey => $candidateId) :
122 1
            if (!in_array($candidateKey, $alreadyDone, true)) :
123 1
                $win = true;
124 1
                foreach ($this->_Arcs as $ArcKey => $ArcValue) :
125 1
                    if ($ArcValue['to'] === $candidateKey) :
126 1
                        $win = false;
127
                    endif;
128
                endforeach;
129
130 1
                if ($win) :
131 1
                    $winners[] = $candidateKey;
132
                endif;
133
            endif;
134
        endforeach;
135
136 1
        return $winners;
137
    }
138
139
140 1
    protected function makeArcs () : void
141
    {
142 1
        $this->_Arcs = [];
143
144 1
        foreach ($this->_PairwiseSort as $newArcsRound) :
145 1
            $virtualArcs = $this->_Arcs;
146 1
            $testNewsArcs = [];
147 1
            $candidatesToCheck = [];
0 ignored issues
show
Unused Code introduced by
$candidatesToCheck is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
148
149 1
            $newKey = max((empty($highKey = array_keys($virtualArcs)) ? [-1] : $highKey)) + 1;
150 1
            foreach ($newArcsRound as $newArc) :
151 1
                $virtualArcs[$newKey] = [ 'from' => $newArc['from'], 'to' => $newArc['to'] ];
152 1
                $testNewsArcs[$newKey] = $virtualArcs[$newKey];
153 1
                $newKey++;
154
            endforeach;
155
156 1
            foreach ($this->getArcsInCycle($virtualArcs) as $cycleArcKey) :
157 1
                if (array_key_exists($cycleArcKey, $testNewsArcs)) :
158 1
                    unset($testNewsArcs[$cycleArcKey]);
159
                endif;
160
            endforeach;
161
162 1
            foreach ($testNewsArcs as $newArc) :
163 1
                $this->_Arcs[] = $newArc;
164
            endforeach;
165
166
        endforeach;
167 1
    }
168
169 1
    protected function getArcsInCycle (array $virtualArcs) : array
170
    {
171 1
        $cycles = [];
172
173 1
        foreach ($this->_selfElection->getCandidatesList() as $candidateKey => $candidateId) :
174 1
            $cycles = array_merge($cycles,$this->followCycle($virtualArcs,$candidateKey,$candidateKey));
175
        endforeach;
176
177 1
        return $cycles;
178
    }
179
180 1
    protected function followCycle (array $virtualArcs, int $startCandidateKey, int $searchCandidateKey, array &$done = [])
181
    {
182 1
        $arcsInCycle = [];
183
184 1
        foreach ($virtualArcs as $ArcKey => $ArcValue) :
185 1
            if ($ArcValue['from'] === $startCandidateKey) :
186 1
                if (in_array($ArcKey, $done, true)) :
187 1
                    continue;
188 1
                elseif ($ArcValue['to'] === $searchCandidateKey) :
189 1
                    $done[] = $ArcKey;
190 1
                    $arcsInCycle[] = $ArcKey;
191
                else :
192 1
                    $done[] = $ArcKey;
193 1
                    $arcsInCycle = array_merge($arcsInCycle,$this->followCycle($virtualArcs,$ArcValue['to'],$searchCandidateKey, $done));
194
                endif;
195
            endif;
196
        endforeach;
197
198 1
        return $arcsInCycle;
199
    }
200
201 1
    protected function pairwiseSort () : array
202
    {
203 1
        $pairs = [];  
204
205 1
        $i = 0;
206 1
        foreach ($this->_selfElection->getPairwise(false) as $candidate_key => $candidate_value) :
207 1
            foreach ($candidate_value['win'] as $challenger_key => $challenger_value) :
208
209 1
                if ($challenger_value > $candidate_value['lose'][$challenger_key]) :
210
211
                    // Victory
212 1
                    $pairs[$i]['from'] = $candidate_key;
213
                    // Defeat
214 1
                    $pairs[$i]['to'] = $challenger_key;
215
216 1
                    $pairs[$i]['win'] = $challenger_value;
217 1
                    $pairs[$i]['minority'] = $candidate_value['lose'][$challenger_key];
218 1
                    $pairs[$i]['margin'] = $candidate_value['win'][$challenger_key] - $candidate_value['lose'][$challenger_key];
219
220 1
                    $i++;
221
222
                endif;
223
224
            endforeach;
225
        endforeach;
226
227 1
        usort($pairs, function ($a, $b) : int {
228 1
            if ($a[static::RP_VARIANT_1] < $b[static::RP_VARIANT_1]) :
229 1
                return 1;
230 1
            elseif ($a[static::RP_VARIANT_1] > $b[static::RP_VARIANT_1]) :
231 1
                return -1;
232
            else : // Equal
233
                if ($a['minority'] > $b['minority']) :
234
                    return 1;
235
                elseif ($a['minority'] < $b['minority']) :
236
                    return -1;
237
                else : // Equal
238
                    return 0;
239
                endif;
240
            endif;
241 1
        });
242
243 1
        $newArcs = [];
244 1
        $i = 0;
245 1
        $f = true;
246 1
        foreach ($pairs as $pairsKey => $pairsValue) :
247 1
            if ($f === true) :
248 1
                $newArcs[$i][] = $pairs[$pairsKey];
249 1
                $f = false;
250 1
            elseif ($pairs[$pairsKey][static::RP_VARIANT_1] === $pairs[$pairsKey - 1][static::RP_VARIANT_1] && $pairs[$pairsKey]['minority'] === $pairs[$pairsKey - 1]['minority']) :
251
                $newArcs[$i][] = $pairs[$pairsKey];
252
            else :
253 1
                $newArcs[++$i][] = $pairs[$pairsKey];
254
            endif;
255
        endforeach;
256
257 1
        return $newArcs;
258
    }
259
260
}
261