Completed
Push — master ( c01904...dcbc34 )
by Boudry
02:56
created

Election::getTimerManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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 109
    public function __construct ()
80
    {
81 109
        $this->_Candidates = [];
82 109
        $this->_Votes = new VotesManager ($this);
83 109
        $this->_timer = new Timer_Manager;
84 109
    }
85
86 1
    public function __destruct ()
87
    {
88 1
        $this->destroyAllLink();
89 1
    }
90
91 2
    public function __sleep () : array
92
    {
93
        // Don't include others data
94
        $include = [
95 2
            '_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 2
        !self::$_checksumMode && array_push($include, '_timer');
112
113 2
        return $include;
114
    }
115
116 1
    public function __wakeup ()
117
    {
118 1
        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 1
    }
122
123 1
    public function __clone ()
124
    {
125 1
        $this->_Votes = clone $this->_Votes;
126 1
        $this->_Votes->setElection($this);      
127 1
        $this->registerAllLinks();
128
129 1
        $this->_timer = clone $this->_timer;
130
131 1
        if ($this->_Pairwise !== null) :
132 1
            $this->_Pairwise = clone $this->_Pairwise;
133 1
            $this->_Pairwise->setElection($this);
134
        endif;
135 1
    }
136
137
138
/////////// INTERNAL GENERIC REGULATION ///////////
139
140
141 1
    protected function registerAllLinks () : void
142
    {
143 1
        foreach ($this->_Candidates as $value) :
144 1
            $value->registerLink($this);
145
        endforeach;
146
147 1
        if ($this->_State > 1) :
148 1
            foreach ($this->_Votes as $value) :
149 1
                $value->registerLink($this);
150
            endforeach;
151
        endif;
152 1
    }
153
154 1
    protected function destroyAllLink () : void
155
    {
156 1
        foreach ($this->_Candidates as $value) :
157 1
            $value->destroyLink($this);
158
        endforeach;
159
160 1
        if ($this->_State > 1) :
161 1
            foreach ($this->_Votes as $value) :
162 1
                $value->destroyLink($this);
163
            endforeach;
164
        endif;
165 1
    }
166
167
        //////
168
169
170 2
    public function getGlobalTimer (bool $float = false) {
171 2
        return $this->_timer->getGlobalTimer($float);
172
    }
173
174 2
    public function getLastTimer (bool $float = false) {
175 2
        return $this->_timer->getLastTimer($float);
176
    }
177
178 80
    public function getTimerManager () : Timer_Manager {
179 80
        return $this->_timer;
180
    }
181
182 1
    public function getChecksum () : string
183
    {
184 1
        self::$_checksumMode = true;
185
186 1
        $r = hash_init('sha256');
187
188 1
        foreach ($this->_Candidates as $value) :
189 1
            hash_update($r, (string) $value);
190
        endforeach;
191
192 1
        foreach ($this->_Votes as $value) :
193 1
            hash_update($r, (string) $value);
194
        endforeach;
195
196 1
        $this->_Pairwise !== null
197 1
            && hash_update($r,serialize($this->_Pairwise->getExplicitPairwise()));
198
199 1
        hash_update($r, $this->getObjectVersion('major'));
200
201 1
        self::$_checksumMode = false;
202
203 1
        return hash_final($r);
204
    }
205
206
    public function ignoreMaxVote (bool $state = true) : bool
207
    {
208
        $this->_ignoreStaticMaxVote = $state;
209
        return $this->_ignoreStaticMaxVote;
210
    }
211
212 86
    public function getImplicitRankingRule () : bool
213
    {
214 86
        return $this->_ImplicitRanking;
215
    }
216
217 3
    public function setImplicitRanking (bool $rule = true) : bool
218
    {
219 3
        $this->_ImplicitRanking = $rule;
220 3
        $this->cleanupResult();
221 3
        return $this->getImplicitRankingRule();
222
    }
223
224 82
    public function isVoteWeightIsAllowed () : bool
225
    {
226 82
        return $this->_VoteWeightRule;
227
    }
228
229 1
    public function allowVoteWeight (bool $rule = true) : bool
230
    {
231 1
        $this->_VoteWeightRule = $rule;
232 1
        $this->cleanupResult();
233 1
        return $this->isVoteWeightIsAllowed();
234
    }
235
236
237
/////////// CANDIDATES ///////////
238
239
240
    // Add a vote candidate before voting
241 104
    public function addCandidate ($candidate_id = null) : Candidate
242
    {
243
        // only if the vote has not started
244 104
        if ( $this->_State > 1 ) :
245
            throw new CondorcetException(2);
246
        endif;
247
248
        // Filter
249 104
        if ( is_bool($candidate_id) || is_array($candidate_id) || (is_object($candidate_id) && !($candidate_id instanceof Candidate)) ) :
250
            throw new CondorcetException(1, $candidate_id);
251
        endif;
252
253
254
        // Process
255 104
        if ( empty($candidate_id) ) :
256 5
            while ( !$this->canAddCandidate($this->_i_CandidateId) ) :
257 5
                $this->_i_CandidateId++;
258
            endwhile;
259
260 5
            $newCandidate = new Candidate($this->_i_CandidateId);
261
        else : // Try to add the candidate_id
262 101
            $newCandidate = ($candidate_id instanceof Candidate) ? $candidate_id : new Candidate ((string) $candidate_id);
263
264 101
            if ( !$this->canAddCandidate($newCandidate) ) :
265
                throw new CondorcetException(3,$candidate_id);
266
            endif;
267
        endif;
268
269
        // Register it
270 104
        $this->_Candidates[] = $newCandidate;
271
272
        // Linking
273 104
        $newCandidate->registerLink($this);
274
275
        // Disallow other candidate object name matching 
276 104
        $newCandidate->setProvisionalState(false);
277
278 104
        return $newCandidate;
279
    }
280
281 104
        public function canAddCandidate ($candidate_id) : bool
282
        {
283 104
            return !$this->existCandidateId($candidate_id, false);
284
        }
285
286
287
    // Destroy a register vote candidate before voting
288 3
    public function removeCandidate ($list) : array
289
    {
290
        // only if the vote has not started
291 3
        if ( $this->_State > 1 ) :
292
            throw new CondorcetException(2);
293
        endif;
294
295
        
296 3
        if ( !is_array($list) ) :
297 3
            $list = [$list];
298
        endif;
299
300 3
        foreach ($list as &$candidate_id) :
301 3
            $candidate_key = $this->getCandidateKey($candidate_id);
302
303 3
            if ( $candidate_key === false ) :
304
                throw new CondorcetException(4,$candidate_id);
305
            endif;
306
307 3
            $candidate_id = $candidate_key;
308
        endforeach;
309
310 3
        $rem = [];
311 3
        foreach ($list as $candidate_key) :
312 3
            $this->_Candidates[$candidate_key]->destroyLink($this);
313
314 3
            $rem[] = $this->_Candidates[$candidate_key];
315
316 3
            unset($this->_Candidates[$candidate_key]);
317
        endforeach;
318
319 3
        return $rem;
320
    }
321
322
323 1
    public function jsonCandidates (string $input)
324
    {
325 1
        $input = Util::prepareJson($input);
326 1
        if ($input === false) :
327
            return $input;
328
        endif;
329
330
            //////
331
332 1
        $adding = [];
333 1
        foreach ($input as $candidate) :
334
            try {
335 1
                $adding[] = $this->addCandidate($candidate);
336
            }
337 1
            catch (CondorcetException $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
338
        endforeach;
339
340 1
        return $adding;
341
    }
342
343 7
    public function parseCandidates (string $input, bool $allowFile = true)
344
    {
345 7
        $input = Util::prepareParse($input, $allowFile);
346 7
        if ($input === false) :
347
            return $input;
348
        endif;
349
350 7
        $adding = [];
351 7
        foreach ($input as $line) :
352
            // Empty Line
353 7
            if (empty($line)) :
354 1
                continue;
355
            endif;
356
357
            // addCandidate
358
            try {
359 7
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
360
                    throw new CondorcetException(12, self::$_maxParseIteration);
361
                endif;
362
363 7
                $adding[] = $this->addCandidate($line);
364
            } catch (CondorcetException $e) {
365
                if ($e->getCode() === 12)
366 7
                    {throw $e;}
367
            }
368
        endforeach;
369
370 7
        return $adding;
371
    }
372
373
374
        //:: CANDIDATES TOOLS :://
375
376
        // Count registered candidates
377 87
        public function countCandidates () : int
378
        {
379 87
            return count($this->_Candidates);
380
        }
381
382
        // Get the list of registered CANDIDATES
383 88
        public function getCandidatesList (bool $stringMode = false) : array
384
        {
385 88
            if (!$stringMode) :
386 86
                return $this->_Candidates;
387
            else :
388 4
                $result = [];
389
390 4
                foreach ($this->_Candidates as $candidateKey => &$oneCandidate) :
391 4
                    $result[$candidateKey] = $oneCandidate->getName();
392
                endforeach;
393
394 4
                return $result;
395
            endif;
396
        }
397
398 93
        public function getCandidateKey ($candidate_id)
399
        {
400 93
            if ($candidate_id instanceof Candidate) :
401 80
                return array_search($candidate_id, $this->_Candidates, true);
402
            else:
403 91
                return array_search(trim((string) $candidate_id), $this->_Candidates, false);
404
            endif;
405
        }
406
407 76
        public function getCandidateId (int $candidate_key, bool $onlyName = false)
408
        {
409 76
            if (!array_key_exists($candidate_key, $this->_Candidates)) :
410
                return false;
411
            else :
412 76
                return ($onlyName) ? $this->_Candidates[$candidate_key]->getName() : $this->_Candidates[$candidate_key];
413
            endif;
414
        }
415
416 104
        public function existCandidateId ($candidate_id, bool $strict = true) : bool
417
        {
418 104
            return ($strict) ? in_array($candidate_id, $this->_Candidates, true) : in_array((string) $candidate_id, $this->_Candidates);
419
        }
420
421 3
        public function getCandidateObjectByName (string $s)
422
        {
423 3
            foreach ($this->_Candidates as $oneCandidate) :
424
425 3
                if ($oneCandidate->getName() === $s) :
426 3
                    return $oneCandidate;
427
                endif;
428
            endforeach;
429
430 1
            return false;
431
        }
432
433
434
435
/////////// VOTING ///////////
436
437
438
    // Close the candidate config, be ready for voting (optional)
439 98
    public function setStateToVote () : bool
440
    {
441 98
        if ( $this->_State === 1 ) :
442 98
                if (empty($this->_Candidates)) :
443
                    throw new CondorcetException(20);
444
                endif;
445
446 98
                $this->_State = 2;
447
448
        // If voting continues after a first set of results
449 86
        elseif ( $this->_State > 2 ) :
450 6
                $this->cleanupResult();
451
        endif;
452
453 98
        return true;
454
    }
455
456
457
    // 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.
458 98
    public function addVote ($vote, $tag = null) : Vote
459
    {
460 98
        $this->prepareVoteInput($vote, $tag);
461
462
        // Check Max Vote Count
463 98
        if ( self::$_maxVoteNumber !== null && !$this->_ignoreStaticMaxVote && $this->countVotes() >= self::$_maxVoteNumber ) :
464 1
            throw new CondorcetException(16, self::$_maxVoteNumber);
465
        endif;
466
467
468
        // Register vote
469 98
        return $this->registerVote($vote, $tag); // Return the vote object
470
    }
471
472
        // return True or throw an Exception
473 5
        public function prepareModifyVote (Vote $existVote)
474
        {
475
            try {
476 5
                $this->prepareVoteInput($existVote);
477 5
                $this->setStateToVote();
478
            }
479
            catch (\Exception $e) {
480
                throw $e;
481
            }
482 5
        }
483
484
        // Return the well formated vote to use.
485 98
        protected function prepareVoteInput (&$vote, $tag = null) : void
486
        {
487 98
            if (!($vote instanceof Vote)) :
488 82
                $vote = new Vote ($vote, $tag);
489
            endif;
490
491
            // Check array format && Make checkVoteCandidate
492 98
            if ( !$this->checkVoteCandidate($vote) ) :
493
                throw new CondorcetException(5);
494
            endif;
495 98
        }
496
497
498 98
        public function checkVoteCandidate (Vote $vote) : bool
499
        {
500 98
            $linkCount = $vote->countLinks();
501 98
            $links = $vote->getLinks();
502
503 98
            $mirror = $vote->getRanking();
504 98
            $change = false;
505 98
            foreach ($vote as $rank => $choice) :
506 98
                foreach ($choice as $choiceKey => $candidate) :
507 98
                    if ( !$this->existCandidateId($candidate, true) ) :
508 91
                        if ($candidate->getProvisionalState() && $this->existCandidateId($candidate, false)) :
509 90
                            if ( $linkCount === 0 || ($linkCount === 1 && reset($links) === $this) ) :
510 90
                                $mirror[$rank][$choiceKey] = $this->_Candidates[$this->getCandidateKey((string) $candidate)];
511 90
                                $change = true;
512
                            else :
513 98
                                return false;
514
                            endif;
515
                        endif;
516
                    endif;
517
                endforeach;
518
            endforeach;
519
520 98
            if ($change) :
521 90
                $vote->setRanking(
522 90
                                    $mirror,
523 90
                                    ( 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...
524
                );
525
            endif;
526
527 98
            return true;
528
        }
529
530
        // Write a new vote
531 98
        protected function registerVote (Vote $vote, $tag = null) : Vote
532
        {
533
            // Vote identifiant
534 98
            $vote->addTags($tag);
535
            
536
            // Register
537
            try {
538 98
                $vote->registerLink($this);
539 98
                $this->_Votes[] = $vote;
540
            } catch (CondorcetException $e) {
541
                // Security : Check if vote object not already register
542
                throw new CondorcetException(6,'Vote object already registred');
543
            }
544
545 98
            return $vote;
546
        }
547
548
549 5
    public function removeVote ($in, bool $with = true) : array
550
    {    
551 5
        $rem = [];
552
553 5
        if ($in instanceof Vote) :
554 4
            $key = $this->getVoteKey($in);
555 4
            if ($key !== false) :
556 4
                $this->_Votes[$key]->destroyLink($this);
557
558 4
                $rem[] = $this->_Votes[$key];
559
560 4
                unset($this->_Votes[$key]);
561
            endif;
562
        else :
563
            // Prepare Tags
564 3
            $tag = Vote::tagsConvert($in);
565
566
            // Deleting
567 3
            foreach ($this->getVotesList($tag, $with) as $key => $value) :
568 3
                $this->_Votes[$key]->destroyLink($this);
569
570 3
                $rem[] = $this->_Votes[$key];
571
572 3
                unset($this->_Votes[$key]);
573
            endforeach;
574
575
        endif;
576
577 5
        return $rem;
578
    }
579
580
581 2
    public function jsonVotes (string $input)
582
    {
583 2
        $input = Util::prepareJson($input);
584 1
        if ($input === false) :
585
            return $input;
586
        endif;
587
588
            //////
589
590 1
        $adding = [];
591
592 1
        foreach ($input as $record) :
593 1
            if (empty($record['vote'])) :
594 1
                continue;
595
            endif;
596
597 1
            $tags = (!isset($record['tag'])) ? null : $record['tag'];
598 1
            $multi = (!isset($record['multi'])) ? 1 : $record['multi'];
599
600 1
            for ($i = 0; $i < $multi; $i++) :
601 1
                if (self::$_maxParseIteration !== null && $this->countVotes() >= self::$_maxParseIteration) :
602
                    throw new CondorcetException(12, self::$_maxParseIteration);
603
                endif;
604
605
                try {
606 1
                    $adding[] = $this->addVote($record['vote'], $tags);
607
                } catch (\Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
608
            endfor;
609
        endforeach;
610
611 1
        return $adding;
612
    }
613
614 70
    public function parseVotes (string $input, bool $allowFile = true)
615
    {
616 70
        $input = Util::prepareParse($input, $allowFile);
617 70
        if ($input === false) :
618
            return $input;
619
        endif;
620
621
        // Check each lines
622 70
        $adding = [];
623 70
        foreach ($input as $line) :
624
            // Empty Line
625 70
            if (empty($line)) :
626 63
                continue;
627
            endif;
628
629
            // Multiples
630 70
            $is_multiple = mb_strpos($line, '*');
631 70
            if ($is_multiple !== false) :
632 63
                $multiple = trim( substr($line, $is_multiple + 1) );
633
634
                // Errors
635 63
                if ( !is_numeric($multiple) ) :
636
                    throw new CondorcetException(13, null);
637
                endif;
638
639 63
                $multiple = intval($multiple);
640
641
                // Reformat line
642 63
                $line = substr($line, 0, $is_multiple);
643
            else :
644 9
                $multiple = 1;
645
            endif;
646
647
            // Vote Weight
648 70
            $is_voteWeight = mb_strpos($line, '^');
649 70
            if ($is_voteWeight !== false) :
650 2
                $weight = trim( substr($line, $is_voteWeight + 1) );
651
652
                // Errors
653 2
                if ( !is_numeric($weight) ) :
654
                    throw new CondorcetException(13, null);
655
                endif;
656
657 2
                $weight = intval($weight);
658
659
                // Reformat line
660 2
                $line = substr($line, 0, $is_voteWeight);
661
            else :
662 69
                $weight = 1;
663
            endif;
664
665
            // Tags + vote
666 70
            if (mb_strpos($line, '||') !== false) :
667 4
                $data = explode('||', $line);
668
669 4
                $vote = $data[1];
670 4
                $tags = $data[0];
671
            // Vote without tags
672
            else :
673 69
                $vote = $line;
674 69
                $tags = null;
675
            endif;
676
677
            // addVote
678 70
            for ($i = 0; $i < $multiple; $i++) :
679 70
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
680 1
                    throw new CondorcetException(12, self::$_maxParseIteration);
681
                endif;
682
683
                try {
684 70
                    $adding[] = ($newVote = $this->addVote($vote, $tags));
685 70
                    $newVote->setWeight($weight);
686 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...
687
            endfor;
688
        endforeach;
689
690 70
        return $adding;
691
    }
692
693
694
    //:: LARGE ELECTION MODE :://
695
696 3
    public function setExternalDataHandler (DataHandlerDriverInterface $driver) : bool
697
    {
698 3
        if (!$this->_Votes->isUsingHandler()) :
699 3
            $this->_Votes->importHandler($driver);
700 3
            return true;
701
        else :
702
            throw new CondorcetException(24);
703
        endif;
704
    }
705
706 1
    public function removeExternalDataHandler () : bool
707
    {
708 1
        if ($this->_Votes->isUsingHandler()) :
709 1
            $this->_Votes->closeHandler();
710 1
            return true;
711
        else :
712
            throw new CondorcetException(23);
713
        endif;
714
    }
715
716
717
    //:: VOTING TOOLS :://
718
719
    // How many votes are registered ?
720 10
    public function countVotes ($tag = null, bool $with = true) : int
721
    {
722 10
        return $this->_Votes->countVotes(Vote::tagsConvert($tag),$with);
723
    }
724
725
    // Get the votes registered list
726 9
    public function getVotesList ($tag = null, bool $with = true) : array
727
    {
728 9
        return $this->_Votes->getVotesList(Vote::tagsConvert($tag), $with);
729
    }
730
731 4
    public function getVotesListAsString () : string
732
    {
733 4
        return $this->_Votes->getVotesListAsString();
734
    }
735
736 81
    public function getVotesManager () : VotesManager {
737 81
        return $this->_Votes;
738
    }
739
740 4
    public function getVoteKey (Vote $vote) {
741 4
        return $this->_Votes->getVoteKey($vote);
742
    }
743
744
745
/////////// RESULTS ///////////
746
747
748
    //:: PUBLIC FUNCTIONS :://
749
750
    // Generic function for default result with ability to change default object method
751 74
    public function getResult ($method = true, array $options = []) : Result
752
    {
753 74
        $options = $this->formatResultOptions($options);
754
755
        // Filter if tag is provided & return
756 74
        if ($options['%tagFilter']) :
757 2
            $chrono = new Timer_Chrono ($this->_timer, 'GetResult with filter');
758
759 2
            $filter = new self;
760
761 2
            foreach ($this->getCandidatesList() as $candidate) :
762 2
                $filter->addCandidate($candidate);
763
            endforeach;
764
765 2
            foreach ($this->getVotesList($options['tags'], $options['withTag']) as $vote) :
766 2
                $filter->addVote($vote);
767
            endforeach;
768
769 2
            unset($chrono);
770
771 2
            return $filter->getResult($method);
772
        endif;
773
774
            ////// Start //////
775
776
        // Prepare
777 74
        $this->prepareResult();
778
779
            //////
780
781 74
        $chrono = new Timer_Chrono ($this->_timer);
782
783 74
        if ($method === true) :
784 7
            $this->initResult(Condorcet::getDefaultMethod());
785 7
            $result = $this->_Calculator[Condorcet::getDefaultMethod()]->getResult();
786 70
        elseif ($method = Condorcet::isAuthMethod((string) $method)) :
787 70
            $this->initResult($method);
788 68
            $result = $this->_Calculator[$method]->getResult();
789
        else :
790
            throw new CondorcetException(8,$method);
791
        endif;
792
793 71
        $chrono->setRole('GetResult for '.$method);
794
795 71
        return $result;
796
    }
797
798
799 78
    public function getWinner (?string $substitution = null)
800
    {
801 78
        $algo = $this->condorcetBasicSubstitution($substitution);
802
803
            //////
804
805 78
        if ($algo === Condorcet::CONDORCET_BASIC_CLASS) :
806 76
            new Timer_Chrono ($this->_timer, 'GetWinner for CondorcetBasic');
807 76
            $this->initResult($algo);
808 76
            $result = $this->_Calculator[$algo]->getWinner();
809
810 76
            return ($result === null) ? null : $this->getCandidateId($result);
811
        else :
812 49
            return $this->getResult($algo)->getWinner();
813
        endif;
814
    }
815
816
817 74
    public function getLoser (?string $substitution = null)
818
    {
819 74
        $algo = $this->condorcetBasicSubstitution($substitution);
820
821
            //////
822
823 74
        if ($algo === Condorcet::CONDORCET_BASIC_CLASS) :
824 74
            new Timer_Chrono ($this->_timer, 'GetLoser for CondorcetBasic');
825 74
            $this->initResult($algo);
826 74
            $result = $this->_Calculator[$algo]->getLoser();
827
828 74
            return ($result === null) ? null : $this->getCandidateId($result);
829
        else :
830 2
            return $this->getResult($algo)->getLoser();
831
        endif;
832
    }
833
834 79
        protected function condorcetBasicSubstitution ($substitution) : string {
835 79
            if ( $substitution ) :
836 49
                if ($substitution === true) :
837
                    $substitution = Condorcet::getDefaultMethod();
838
                endif;
839
                
840 49
                if ( Condorcet::isAuthMethod($substitution) ) :
841 49
                    $algo = $substitution;
842
                else :
843 49
                    throw new CondorcetException(9,$substitution);
844
                endif;
845
            else :
846 77
                $algo = Condorcet::CONDORCET_BASIC_CLASS;
847
            endif;
848
849 79
            return $algo;
850
        }
851
852
853 1
    public function computeResult ($method = true) : void
854
    {
855 1
        $this->getResult($method);
856 1
    }
857
858
859
    //:: TOOLS FOR RESULT PROCESS :://
860
861
862
    // Prepare to compute results & caching system
863 80
    protected function prepareResult () : bool
864
    {
865 80
        if ($this->_State > 2) :
866 73
            return false;
867 80
        elseif ($this->_State === 2) :
868 80
            $this->cleanupResult();
869
870
            // Do Pairewise
871 80
            $this->_Pairwise = new Pairwise ($this);
872
873
            // Change state to result
874 80
            $this->_State = 3;
875
876
            // Return
877 80
            return true;
878
        else :
879
            throw new CondorcetException(6);
880
        endif;
881
    }
882
883
884 80
    protected function initResult (string $class) : void
885
    {
886 80
        if ( !isset($this->_Calculator[$class]) ) :
887 80
            $this->_Calculator[$class] = new $class($this);
888
        endif;
889 78
    }
890
891
892
    // Cleanup results to compute again with new votes
893 80
    protected function cleanupResult () : void
894
    {
895
        // Reset state
896 80
        if ($this->_State > 2) : 
897 8
            $this->_State = 2;
898
        endif;
899
900
            //////
901
902
        // Clean pairwise
903 80
        $this->_Pairwise = null;
904
905
        // Algos
906 80
        $this->_Calculator = null;
907 80
    }
908
909
910 74
    protected function formatResultOptions (array $arg) : array
911
    {
912
        // About tag filter
913 74
        if (isset($arg['tags'])):
914 2
            $arg['%tagFilter'] = true;
915
916 2
            if ( !isset($arg['withTag']) || !is_bool($arg['withTag']) ) :
917 2
                $arg['withTag'] = true;
918
            endif;
919
        else:
920 74
            $arg['%tagFilter'] = false;
921
        endif;
922
923 74
        return $arg;
924
    }
925
926
927
    //:: GET RAW DATA :://
928
929 77
    public function getPairwise (bool $explicit = true)
930
    {
931 77
        $this->prepareResult();
932
933 77
        return (!$explicit) ? $this->_Pairwise : $this->_Pairwise->getExplicitPairwise();
934
    }
935
936
}
937