Completed
Push — master ( 5f55d5...14ff68 )
by Boudry
03:06
created

Election::jsonVotes()   D

Complexity

Conditions 10
Paths 19

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 10.6591

Importance

Changes 0
Metric Value
dl 0
loc 32
ccs 13
cts 16
cp 0.8125
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 22
nc 19
nop 1
crap 10.6591

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 26 and the first side effect is on line 120.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/*
3
    Condorcet PHP Class, with Schulze Methods and others !
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;
11
12
use Condorcet\Condorcet;
13
use Condorcet\CondorcetException;
14
use Condorcet\CondorcetVersion;
15
use Condorcet\Result;
16
use Condorcet\Util;
17
use Condorcet\Vote;
18
use Condorcet\Algo\Pairwise;
19
use Condorcet\DataManager\VotesManager;
20
use Condorcet\DataManager\DataHandlerDrivers\DataHandlerDriverInterface;
21
use Condorcet\Timer\Chrono as Timer_Chrono;
22
use Condorcet\Timer\Manager as Timer_Manager;
23
24
25
// Base Condorcet class
26
class Election
27
{
28
29
/////////// PROPERTIES ///////////
30
31
    public const MAX_LENGTH_CANDIDATE_ID = 30; // Max length for candidate identifiant string
32
33
    protected static $_maxParseIteration = null;
34
    protected static $_maxVoteNumber = null;
35
    protected static $_checksumMode = false;
36
37
/////////// STATICS METHODS ///////////
38
39
    // Change max parse iteration
40 1
    public static function setMaxParseIteration (?int $value) : ?int
41
    {
42 1
        self::$_maxParseIteration = $value;
43 1
        return self::$_maxParseIteration;
44
    }
45
46
    // Change max vote number
47 1
    public static function setMaxVoteNumber (?int $value) : ?int
48
    {
49 1
        self::$_maxVoteNumber = $value;
50 1
        return self::$_maxVoteNumber;
51
    }
52
53
54
/////////// CONSTRUCTOR ///////////
55
56
    use CondorcetVersion;
57
58
    // Data and global options
59
    protected $_Candidates = []; // Candidate list
60
    protected $_Votes; // Votes list
61
62
    // Mechanics
63
    protected $_i_CandidateId = 'A';
64
    protected $_State = 1; // 1 = Add Candidates / 2 = Voting / 3 = Some result have been computing
65
    protected $_timer;
66
    protected $_nextVoteTag = 0;
67
    protected $_ignoreStaticMaxVote = false;
68
69
    // Params
70
    protected $_ImplicitRanking = true;
71
    protected $_VoteWeightRule = false;
72
73
    // Result
74
    protected $_Pairwise;
75
    protected $_Calculator;
76
77
        //////
78
79 91
    public function __construct ()
80
    {
81 91
        $this->_Candidates = [];
82 91
        $this->_Votes = new VotesManager ($this);
83 91
        $this->_timer = new Timer_Manager;
84 91
    }
85
86 1
    public function __destruct ()
87
    {
88 1
        $this->destroyAllLink();
89 1
    }
90
91 1
    public function __sleep () : array
92
    {
93
        // Don't include others data
94
        $include = [
95 1
            '_Candidates',
96
            '_Votes',
97
98
            '_i_CandidateId',
99
            '_State',
100
            '_nextVoteTag',
101
            '_objectVersion',
102
            '_ignoreStaticMaxVote',
103
104
            '_ImplicitRanking',
105
            '_VoteWeightRule',
106
107
            '_Pairwise',
108
            '_Calculator',
109
        ];
110
111 1
        !self::$_checksumMode && array_push($include, '_timer');
112
113 1
        return $include;
114
    }
115
116
    public function __wakeup ()
117
    {
118
        if ( version_compare($this->getObjectVersion('MAJOR'),Condorcet::getVersion('MAJOR'),'!=') ) :
119
            throw new CondorcetException(11, 'Your object version is '.$this->getObjectVersion().' but the class engine version is '.Condorcet::getVersion('ENV'));
120
        endif;
121
    }
122
123
    public function __clone ()
124
    {
125
        $this->registerAllLinks();
126
    }
127
128
129
/////////// INTERNAL GENERIC REGULATION ///////////
130
131
132
    protected function registerAllLinks () : void
133
    {
134
        foreach ($this->_Candidates as $value) :
135
            $value->registerLink($this);
136
        endforeach;
137
138
        if ($this->_State > 1) :
139
            foreach ($this->_Votes as $value) :
140
                $value->registerLink($this);
141
            endforeach;
142
        endif;
143
    }
144
145 1
    protected function destroyAllLink () : void
146
    {
147 1
        foreach ($this->_Candidates as $value) :
148
            $value->destroyLink($this);
149
        endforeach;
150
151 1
        if ($this->_State > 1) :
152
            foreach ($this->_Votes as $value) :
153
                $value->destroyLink($this);
154
            endforeach;
155
        endif;
156 1
    }
157
158
        //////
159
160
161 2
    public function getGlobalTimer (bool $float = false) {
162 2
        return $this->_timer->getGlobalTimer($float);
163
    }
164
165 2
    public function getLastTimer (bool $float = false) {
166 2
        return $this->_timer->getLastTimer($float);
167
    }
168
169 67
    public function getTimerManager () : Timer_Manager {
170 67
        return $this->_timer;
171
    }
172
173 1
    public function getChecksum () : string
174
    {
175 1
        self::$_checksumMode = true;
176
177 1
        $r = hash_init('sha256');
178
179 1
        foreach ($this->_Candidates as $value) :
180 1
            hash_update($r, (string) $value);
181
        endforeach;
182
183 1
        foreach ($this->_Votes as $value) :
184 1
            hash_update($r, (string) $value);
185
        endforeach;
186
187 1
        $this->_Pairwise !== null
188 1
            && hash_update($r,serialize($this->_Pairwise->getExplicitPairwise()));
189
190 1
        hash_update($r, $this->getObjectVersion('major'));
191
192 1
        self::$_checksumMode = false;
193
194 1
        return hash_final($r);
195
    }
196
197
    public function ignoreMaxVote (bool $state = true) : bool
198
    {
199
        $this->_ignoreStaticMaxVote = $state;
200
        return $this->_ignoreStaticMaxVote;
201
    }
202
203 73
    public function getImplicitRankingRule () : bool
204
    {
205 73
        return $this->_ImplicitRanking;
206
    }
207
208 3
    public function setImplicitRanking (bool $rule = true) : bool
209
    {
210 3
        $this->_ImplicitRanking = $rule;
211 3
        $this->cleanupResult();
212 3
        return $this->getImplicitRankingRule();
213
    }
214
215 69
    public function isVoteWeightIsAllowed () : bool
216
    {
217 69
        return $this->_VoteWeightRule;
218
    }
219
220 1
    public function allowVoteWeight (bool $rule = true) : bool
221
    {
222 1
        $this->_VoteWeightRule = $rule;
223 1
        $this->cleanupResult();
224 1
        return $this->isVoteWeightIsAllowed();
225
    }
226
227
228
/////////// CANDIDATES ///////////
229
230
231
    // Add a vote candidate before voting
232 86
    public function addCandidate ($candidate_id = null) : Candidate
233
    {
234
        // only if the vote has not started
235 86
        if ( $this->_State > 1 ) :
236
            throw new CondorcetException(2);
237
        endif;
238
239
        // Filter
240 86
        if ( is_bool($candidate_id) || is_array($candidate_id) || (is_object($candidate_id) && !($candidate_id instanceof Candidate)) ) :
241
            throw new CondorcetException(1, $candidate_id);
242
        endif;
243
244
245
        // Process
246 86
        if ( empty($candidate_id) ) :
247 5
            while ( !$this->canAddCandidate($this->_i_CandidateId) ) :
248 5
                $this->_i_CandidateId++;
249
            endwhile;
250
251 5
            $newCandidate = new Candidate($this->_i_CandidateId);
252
        else : // Try to add the candidate_id
253 83
            $newCandidate = ($candidate_id instanceof Candidate) ? $candidate_id : new Candidate ((string) $candidate_id);
254
255 83
            if ( !$this->canAddCandidate($newCandidate) ) :
256
                throw new CondorcetException(3,$candidate_id);
257
            endif;
258
        endif;
259
260
        // Register it
261 86
        $this->_Candidates[] = $newCandidate;
262
263
        // Linking
264 86
        $newCandidate->registerLink($this);
265
266
        // Disallow other candidate object name matching 
267 86
        $newCandidate->setProvisionalState(false);
268
269 86
        return $newCandidate;
270
    }
271
272 86
        public function canAddCandidate ($candidate_id) : bool
273
        {
274 86
            return !$this->existCandidateId($candidate_id, false);
275
        }
276
277
278
    // Destroy a register vote candidate before voting
279 3
    public function removeCandidate ($list) : array
280
    {
281
        // only if the vote has not started
282 3
        if ( $this->_State > 1 ) :
283
            throw new CondorcetException(2);
284
        endif;
285
286
        
287 3
        if ( !is_array($list) ) :
288 3
            $list = [$list];
289
        endif;
290
291 3
        foreach ($list as &$candidate_id) :
292 3
            $candidate_key = $this->getCandidateKey($candidate_id);
293
294 3
            if ( $candidate_key === false ) :
295
                throw new CondorcetException(4,$candidate_id);
296
            endif;
297
298 3
            $candidate_id = $candidate_key;
299
        endforeach;
300
301 3
        $rem = [];
302 3
        foreach ($list as $candidate_key) :
303 3
            $this->_Candidates[$candidate_key]->destroyLink($this);
304
305 3
            $rem[] = $this->_Candidates[$candidate_key];
306
307 3
            unset($this->_Candidates[$candidate_key]);
308
        endforeach;
309
310 3
        return $rem;
311
    }
312
313
314
    public function jsonCandidates (string $input)
315
    {
316
        $input = Util::prepareJson($input);
317
        if ($input === false) :
318
            return $input;
319
        endif;
320
321
            //////
322
323
        $adding = [];
324
        foreach ($input as $candidate) :
325
            try {
326
                $adding[] = $this->addCandidate($candidate);
327
            }
328
            catch (CondorcetException $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
329
        endforeach;
330
331
        return $adding;
332
    }
333
334 6
    public function parseCandidates (string $input, bool $allowFile = true)
335
    {
336 6
        $input = Util::prepareParse($input, $allowFile);
337 6
        if ($input === false) :
338
            return $input;
339
        endif;
340
341 6
        $adding = [];
342 6
        foreach ($input as $line) :
343
            // Empty Line
344 6
            if (empty($line)) :
345 1
                continue;
346
            endif;
347
348
            // addCandidate
349
            try {
350 6
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
351
                    throw new CondorcetException(12, self::$_maxParseIteration);
352
                endif;
353
354 6
                $adding[] = $this->addCandidate($line);
355
            } catch (CondorcetException $e) {
356
                if ($e->getCode() === 12)
357 6
                    {throw $e;}
358
            }
359
        endforeach;
360
361 6
        return $adding;
362
    }
363
364
365
        //:: CANDIDATES TOOLS :://
366
367
        // Count registered candidates
368 73
        public function countCandidates () : int
369
        {
370 73
            return count($this->_Candidates);
371
        }
372
373
        // Get the list of registered CANDIDATES
374 74
        public function getCandidatesList (bool $stringMode = false) : array
375
        {
376 74
            if (!$stringMode) :
377 73
                return $this->_Candidates;
378
            else :
379 3
                $result = [];
380
381 3
                foreach ($this->_Candidates as $candidateKey => &$oneCandidate) :
382 3
                    $result[$candidateKey] = $oneCandidate->getName();
383
                endforeach;
384
385 3
                return $result;
386
            endif;
387
        }
388
389 78
        public function getCandidateKey ($candidate_id)
390
        {
391 78
            if ($candidate_id instanceof Candidate) :
392 67
                return array_search($candidate_id, $this->_Candidates, true);
393
            else:
394 77
                return array_search(trim((string) $candidate_id), $this->_Candidates, false);
395
            endif;
396
        }
397
398 63
        public function getCandidateId (int $candidate_key, bool $onlyName = false)
399
        {
400 63
            if (!array_key_exists($candidate_key, $this->_Candidates)) :
401
                return false;
402
            else :
403 63
                return ($onlyName) ? $this->_Candidates[$candidate_key]->getName() : $this->_Candidates[$candidate_key];
404
            endif;
405
        }
406
407 86
        public function existCandidateId ($candidate_id, bool $strict = true) : bool
408
        {
409 86
            return ($strict) ? in_array($candidate_id, $this->_Candidates, true) : in_array((string) $candidate_id, $this->_Candidates);
410
        }
411
412 2
        public function getCandidateObjectByName (string $s)
413
        {
414 2
            foreach ($this->_Candidates as $oneCandidate) :
415
416 2
                if ($oneCandidate->getName() === $s) :
417 2
                    return $oneCandidate;
418
                endif;
419
            endforeach;
420
421
            return false;
422
        }
423
424
425
426
/////////// VOTING ///////////
427
428
429
    // Close the candidate config, be ready for voting (optional)
430 81
    public function setStateToVote () : bool
431
    {
432 81
        if ( $this->_State === 1 ) :
433 81
                if (empty($this->_Candidates)) :
434
                    throw new CondorcetException(20);
435
                endif;
436
437 81
                $this->_State = 2;
438
439
        // If voting continues after a first set of results
440 79
        elseif ( $this->_State > 2 ) :
441 6
                $this->cleanupResult();
442
        endif;
443
444 81
        return true;
445
    }
446
447
448
    // Add a single vote. Array key is the rank, each candidate in a rank are separate by ',' It is not necessary to register the last rank.
449 81
    public function addVote ($vote, $tag = null) : Vote
450
    {
451 81
        $this->prepareVoteInput($vote, $tag);
452
453
        // Check Max Vote Count
454 81
        if ( self::$_maxVoteNumber !== null && !$this->_ignoreStaticMaxVote && $this->countVotes() >= self::$_maxVoteNumber ) :
455 1
            throw new CondorcetException(16, self::$_maxVoteNumber);
456
        endif;
457
458
459
        // Register vote
460 81
        return $this->registerVote($vote, $tag); // Return the vote object
461
    }
462
463
        // return True or throw an Exception
464 5
        public function prepareModifyVote (Vote $existVote)
465
        {
466
            try {
467 5
                $this->prepareVoteInput($existVote);
468 5
                $this->setStateToVote();
469
            }
470
            catch (\Exception $e) {
471
                throw $e;
472
            }
473 5
        }
474
475
        // Return the well formated vote to use.
476 81
        protected function prepareVoteInput (&$vote, $tag = null) : void
477
        {
478 81
            if (!($vote instanceof Vote)) :
479 72
                $vote = new Vote ($vote, $tag);
480
            endif;
481
482
            // Check array format && Make checkVoteCandidate
483 81
            if ( !$this->checkVoteCandidate($vote) ) :
484
                throw new CondorcetException(5);
485
            endif;
486 81
        }
487
488
489 81
        public function checkVoteCandidate (Vote $vote) : bool
490
        {
491 81
            $linkCount = $vote->countLinks();
492 81
            $links = $vote->getLinks();
493
494 81
            $mirror = $vote->getRanking();
495 81
            $change = false;
496 81
            foreach ($vote as $rank => $choice) :
497 81
                foreach ($choice as $choiceKey => $candidate) :
498 81
                    if ( !$this->existCandidateId($candidate, true) ) :
499 77
                        if ($candidate->getProvisionalState() && $this->existCandidateId($candidate, false)) :
500 76
                            if ( $linkCount === 0 || ($linkCount === 1 && reset($links) === $this) ) :
501 76
                                $mirror[$rank][$choiceKey] = $this->_Candidates[$this->getCandidateKey((string) $candidate)];
502 76
                                $change = true;
503
                            else :
504 81
                                return false;
505
                            endif;
506
                        endif;
507
                    endif;
508
                endforeach;
509
            endforeach;
510
511 81
            if ($change) :
512 76
                $vote->setRanking(
513 76
                                    $mirror,
514 76
                                    ( abs($vote->getTimestamp() - microtime(true)) > 0.5 ) ? ($vote->getTimestamp() + 0.001) : false
0 ignored issues
show
Bug introduced by
It seems like abs($vote->getTimestamp(...stamp() + 0.001 : false can also be of type double; however, Condorcet\Vote::setRanking() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
515
                );
516
            endif;
517
518 81
            return true;
519
        }
520
521
        // Write a new vote
522 81
        protected function registerVote (Vote $vote, $tag = null) : Vote
523
        {
524
            // Vote identifiant
525 81
            $vote->addTags($tag);
526
            
527
            // Register
528
            try {
529 81
                $vote->registerLink($this);
530 81
                $this->_Votes[] = $vote;
531
            } catch (CondorcetException $e) {
532
                // Security : Check if vote object not already register
533
                throw new CondorcetException(6,'Vote object already registred');
534
            }
535
536 81
            return $vote;
537
        }
538
539
540 5
    public function removeVote ($in, bool $with = true) : array
541
    {    
542 5
        $rem = [];
543
544 5
        if ($in instanceof Vote) :
545 4
            $key = $this->getVoteKey($in);
546 4
            if ($key !== false) :
547 4
                $this->_Votes[$key]->destroyLink($this);
548
549 4
                $rem[] = $this->_Votes[$key];
550
551 4
                unset($this->_Votes[$key]);
552
            endif;
553
        else :
554
            // Prepare Tags
555 3
            $tag = Vote::tagsConvert($in);
556
557
            // Deleting
558 3
            foreach ($this->getVotesList($tag, $with) as $key => $value) :
559 3
                $this->_Votes[$key]->destroyLink($this);
560
561 3
                $rem[] = $this->_Votes[$key];
562
563 3
                unset($this->_Votes[$key]);
564
            endforeach;
565
566
        endif;
567
568 5
        return $rem;
569
    }
570
571
572 2
    public function jsonVotes (string $input)
573
    {
574 2
        $input = Util::prepareJson($input);
575 1
        if ($input === false) :
576
            return $input;
577
        endif;
578
579
            //////
580
581 1
        $adding = [];
582
583 1
        foreach ($input as $record) :
584 1
            if (empty($record['vote'])) :
585 1
                continue;
586
            endif;
587
588 1
            $tags = (!isset($record['tag'])) ? null : $record['tag'];
589 1
            $multi = (!isset($record['multi'])) ? 1 : $record['multi'];
590
591 1
            for ($i = 0; $i < $multi; $i++) :
592 1
                if (self::$_maxParseIteration !== null && $this->countVotes() >= self::$_maxParseIteration) :
593
                    throw new CondorcetException(12, self::$_maxParseIteration);
594
                endif;
595
596
                try {
597 1
                    $adding[] = $this->addVote($record['vote'], $tags);
598
                } catch (\Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
599
            endfor;
600
        endforeach;
601
602 1
        return $adding;
603
    }
604
605 68
    public function parseVotes (string $input, bool $allowFile = true)
606
    {
607 68
        $input = Util::prepareParse($input, $allowFile);
608 68
        if ($input === false) :
609
            return $input;
610
        endif;
611
612
        // Check each lines
613 68
        $adding = [];
614 68
        foreach ($input as $line) :
615
            // Empty Line
616 68
            if (empty($line)) :
617 61
                continue;
618
            endif;
619
620
            // Multiples
621 68
            $is_multiple = mb_strpos($line, '*');
622 68
            if ($is_multiple !== false) :
623 61
                $multiple = trim( substr($line, $is_multiple + 1) );
624
625
                // Errors
626 61
                if ( !is_numeric($multiple) ) :
627
                    throw new CondorcetException(13, null);
628
                endif;
629
630 61
                $multiple = intval($multiple);
631
632
                // Reformat line
633 61
                $line = substr($line, 0, $is_multiple);
634
            else :
635 9
                $multiple = 1;
636
            endif;
637
638
            // Vote Weight
639 68
            $is_voteWeight = mb_strpos($line, '^');
640 68
            if ($is_voteWeight !== false) :
641 2
                $weight = trim( substr($line, $is_voteWeight + 1) );
642
643
                // Errors
644 2
                if ( !is_numeric($weight) ) :
645
                    throw new CondorcetException(13, null);
646
                endif;
647
648 2
                $weight = intval($weight);
649
650
                // Reformat line
651 2
                $line = substr($line, 0, $is_voteWeight);
652
            else :
653 67
                $weight = 1;
654
            endif;
655
656
            // Tags + vote
657 68
            if (mb_strpos($line, '||') !== false) :
658 4
                $data = explode('||', $line);
659
660 4
                $vote = $data[1];
661 4
                $tags = $data[0];
662
            // Vote without tags
663
            else :
664 67
                $vote = $line;
665 67
                $tags = null;
666
            endif;
667
668
            // addVote
669 68
            for ($i = 0; $i < $multiple; $i++) :
670 68
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
671 1
                    throw new CondorcetException(12, self::$_maxParseIteration);
672
                endif;
673
674
                try {
675 68
                    $adding[] = ($newVote = $this->addVote($vote, $tags));
676 68
                    $newVote->setWeight($weight);
677 1
                } catch (CondorcetException $e) {}
1 ignored issue
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
678
            endfor;
679
        endforeach;
680
681 68
        return $adding;
682
    }
683
684
685
    //:: LARGE ELECTION MODE :://
686
687 3
    public function setExternalDataHandler (DataHandlerDriverInterface $driver) : bool
688
    {
689 3
        if (!$this->_Votes->isUsingHandler()) :
690 3
            $this->_Votes->importHandler($driver);
691 3
            return true;
692
        else :
693
            throw new CondorcetException(24);
694
        endif;
695
    }
696
697 1
    public function removeExternalDataHandler () : bool
698
    {
699 1
        if ($this->_Votes->isUsingHandler()) :
700 1
            $this->_Votes->closeHandler();
701 1
            return true;
702
        else :
703
            throw new CondorcetException(23);
704
        endif;
705
    }
706
707
708
    //:: VOTING TOOLS :://
709
710
    // How many votes are registered ?
711 17
    public function countVotes ($tag = null, bool $with = true) : int
712
    {
713 17
        return $this->_Votes->countVotes(Vote::tagsConvert($tag),$with);
714
    }
715
716
    // Get the votes registered list
717 7
    public function getVotesList ($tag = null, bool $with = true) : array
718
    {
719 7
        return $this->_Votes->getVotesList(Vote::tagsConvert($tag), $with);
720
    }
721
722 4
    public function getVotesListAsString () : string
723
    {
724 4
        return $this->_Votes->getVotesListAsString();
725
    }
726
727 68
    public function getVotesManager () : VotesManager {
728 68
        return $this->_Votes;
729
    }
730
731 4
    public function getVoteKey (Vote $vote) {
732 4
        return $this->_Votes->getVoteKey($vote);
733
    }
734
735
    public function getVoteByKey (int $key) {
736
        if (!isset($this->_Votes[$key])) :
737
            return false;
738
        else :
739
            return $this->_Votes[$key];
740
        endif;
741
    }
742
743
744
/////////// RESULTS ///////////
745
746
747
    //:: PUBLIC FUNCTIONS :://
748
749
    // Generic function for default result with ability to change default object method
750 61
    public function getResult ($method = true, array $options = []) : Result
751
    {
752 61
        $options = $this->formatResultOptions($options);
753
754
        // Filter if tag is provided & return
755 61
        if ($options['%tagFilter']) :
756 2
            $chrono = new Timer_Chrono ($this->_timer, 'GetResult with filter');
757
758 2
            $filter = new self;
759
760 2
            foreach ($this->getCandidatesList() as $candidate) :
761 2
                $filter->addCandidate($candidate);
762
            endforeach;
763
764 2
            foreach ($this->getVotesList($options['tags'], $options['withTag']) as $vote) :
765 2
                $filter->addVote($vote);
766
            endforeach;
767
768 2
            unset($chrono);
769
770 2
            return $filter->getResult($method);
771
        endif;
772
773
            ////// Start //////
774
775
        // Prepare
776 61
        $this->prepareResult();
777
778
            //////
779
780 61
        $chrono = new Timer_Chrono ($this->_timer);
781
782 61
        if ($method === true) :
783 3
            $this->initResult(Condorcet::getDefaultMethod());
784 3
            $result = $this->_Calculator[Condorcet::getDefaultMethod()]->getResult();
785 61
        elseif ($method = Condorcet::isAuthMethod((string) $method)) :
786 61
            $this->initResult($method);
787 59
            $result = $this->_Calculator[$method]->getResult();
788
        else :
789
            throw new CondorcetException(8,$method);
790
        endif;
791
792 58
        $chrono->setRole('GetResult for '.$method);
793
794 58
        return $result;
795
    }
796
797
798 65
    public function getWinner (?string $substitution = null)
799
    {
800 65
        $algo = $this->condorcetBasicSubstitution($substitution);
801
802
            //////
803
804 65
        if ($algo === Condorcet::CONDORCET_BASIC_CLASS) :
805 63
            new Timer_Chrono ($this->_timer, 'GetWinner for CondorcetBasic');
806 63
            $this->initResult($algo);
807 63
            $result = $this->_Calculator[$algo]->getWinner();
808
809 63
            return ($result === null) ? null : $this->getCandidateId($result);
810
        else :
811 49
            return $this->getResult($algo)->getWinner();
812
        endif;
813
    }
814
815
816 61
    public function getLoser (?string $substitution = null)
817
    {
818 61
        $algo = $this->condorcetBasicSubstitution($substitution);
819
820
            //////
821
822 61
        if ($algo === Condorcet::CONDORCET_BASIC_CLASS) :
823 61
            new Timer_Chrono ($this->_timer, 'GetLoser for CondorcetBasic');
824 61
            $this->initResult($algo);
825 61
            $result = $this->_Calculator[$algo]->getLoser();
826
827 61
            return ($result === null) ? null : $this->getCandidateId($result);
828
        else :
829 2
            return $this->getResult($algo)->getLoser();
830
        endif;
831
    }
832
833 66
        protected function condorcetBasicSubstitution ($substitution) : string {
834 66
            if ( $substitution ) :
835 49
                if ($substitution === true) :
836
                    $substitution = Condorcet::getDefaultMethod();
837
                endif;
838
                
839 49
                if ( Condorcet::isAuthMethod($substitution) ) :
840 49
                    $algo = $substitution;
841
                else :
842 49
                    throw new CondorcetException(9,$substitution);
843
                endif;
844
            else :
845 64
                $algo = Condorcet::CONDORCET_BASIC_CLASS;
846
            endif;
847
848 66
            return $algo;
849
        }
850
851
852
    public function computeResult ($method = true) : void
853
    {
854
        $this->getResult($method);
855
    }
856
857
858
    //:: TOOLS FOR RESULT PROCESS :://
859
860
861
    // Prepare to compute results & caching system
862 67
    protected function prepareResult () : bool
863
    {
864 67
        if ($this->_State > 2) :
865 60
            return false;
866 67
        elseif ($this->_State === 2) :
867 67
            $this->cleanupResult();
868
869
            // Do Pairewise
870 67
            $this->_Pairwise = new Pairwise ($this);
871
872
            // Change state to result
873 67
            $this->_State = 3;
874
875
            // Return
876 67
            return true;
877
        else :
878
            throw new CondorcetException(6);
879
        endif;
880
    }
881
882
883 67
    protected function initResult (string $class) : void
884
    {
885 67
        if ( !isset($this->_Calculator[$class]) ) :
886 67
            $this->_Calculator[$class] = new $class($this);
887
        endif;
888 65
    }
889
890
891
    // Cleanup results to compute again with new votes
892 67
    protected function cleanupResult () : void
893
    {
894
        // Reset state
895 67
        if ($this->_State > 2) : 
896 8
            $this->_State = 2;
897
        endif;
898
899
            //////
900
901
        // Clean pairwise
902 67
        $this->_Pairwise = null;
903
904
        // Algos
905 67
        $this->_Calculator = null;
906 67
    }
907
908
909 61
    protected function formatResultOptions (array $arg) : array
910
    {
911
        // About tag filter
912 61
        if (isset($arg['tags'])):
913 2
            $arg['%tagFilter'] = true;
914
915 2
            if ( !isset($arg['withTag']) || !is_bool($arg['withTag']) ) :
916 2
                $arg['withTag'] = true;
917
            endif;
918
        else:
919 61
            $arg['%tagFilter'] = false;
920
        endif;
921
922 61
        return $arg;
923
    }
924
925
926
    //:: GET RAW DATA :://
927
928 64
    public function getPairwise (bool $explicit = true)
929
    {
930 64
        $this->prepareResult();
931
932 64
        return (!$explicit) ? $this->_Pairwise : $this->_Pairwise->getExplicitPairwise();
933
    }
934
935
}
936