debug
This commit is contained in:
		
							
								
								
									
										82
									
								
								include/condorcet/Tests/Examples/ExamplesTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								include/condorcet/Tests/Examples/ExamplesTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Examples; | ||||
|  | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class ExamplesTest extends TestCase | ||||
| { | ||||
|     /** | ||||
|      * @runInSeparateProcess | ||||
|      */ | ||||
|     public function testOverviewExample(): void | ||||
|     { | ||||
|         $r = true; | ||||
|  | ||||
|         try { | ||||
|             include __DIR__.'/../../Examples/1. Overview.php'; | ||||
|         } catch (\Exception $e) { | ||||
|             $r = false; | ||||
|             throw $e; | ||||
|         } | ||||
|  | ||||
|         self::assertTrue($r); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @runInSeparateProcess | ||||
|      */ | ||||
|     public function testAdvancedObjectManagementExample(): void | ||||
|     { | ||||
|         $r = true; | ||||
|  | ||||
|         try { | ||||
|             include __DIR__.'/../../Examples/2. AdvancedObjectManagement.php'; | ||||
|         } catch (\Exception $e) { | ||||
|             throw $e; | ||||
|             $r = false; | ||||
|         } | ||||
|  | ||||
|         self::assertTrue($r); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @runInSeparateProcess | ||||
|      */ | ||||
|     public function testGlobalHtmlExample(): void | ||||
|     { | ||||
|         $r = true; | ||||
|  | ||||
|         try { | ||||
|             ob_start(); | ||||
|             include __DIR__.'/../../Examples/Examples-with-html/A.Global_Example.php'; | ||||
|             ob_end_clean(); | ||||
|         } catch (\Exception $e) { | ||||
|             throw $e; | ||||
|             $r = false; | ||||
|         } | ||||
|  | ||||
|         self::assertTrue($r); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @runInSeparateProcess | ||||
|      */ | ||||
|     public function testRankingManipulationHtmlExample(): void | ||||
|     { | ||||
|         $r = true; | ||||
|  | ||||
|         try { | ||||
|             ob_start(); | ||||
|             include __DIR__.'/../../Examples/Examples-with-html/B.Ranking_Manipulation.php'; | ||||
|             ob_end_clean(); | ||||
|         } catch (\Exception $e) { | ||||
|             $r = false; | ||||
|             throw $e; | ||||
|         } | ||||
|  | ||||
|         self::assertTrue($r); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,6 @@ | ||||
|                 A > B > C | ||||
|             A > B > C * 4;tag1 || A > B > C*4 #Coucou | ||||
|  | ||||
|             A < B < C * 10 | ||||
|     D <> B | ||||
|             A > B > C | ||||
							
								
								
									
										10
									
								
								include/condorcet/Tests/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								include/condorcet/Tests/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| > **[Presentation](../README.md) | [Manual](https://github.com/julien-boudry/Condorcet/wiki) | [Methods References](../Documentation/README.md) | Tests** | ||||
|  | ||||
| #### ~300 tests and more than 1000 assertions to explore on this path | ||||
|  | ||||
| * The implementation tests proving the implementations of each method are in this directory **=>> [Algo Tests](lib/Algo/)** | ||||
|  | ||||
| #### Execute the tests suite | ||||
| ``` | ||||
| ./vendor/bin/phpunit | ||||
| ``` | ||||
							
								
								
									
										87
									
								
								include/condorcet/Tests/ReadmeQuickExampleTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								include/condorcet/Tests/ReadmeQuickExampleTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Candidate, Condorcet, Election, Result, Vote}; | ||||
| use CondorcetPHP\Condorcet\Utils\CondorcetUtil; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class ReadmeQuickExampleTest extends TestCase | ||||
| { | ||||
|     public function testReadmeQuickExample(): void | ||||
|     { | ||||
|         $myElection1 = new Election; | ||||
|  | ||||
|         // Create your own candidate object | ||||
|         $candidate1 = new Candidate('Candidate 1'); | ||||
|         $candidate2 = new Candidate('Candidate 2'); | ||||
|         $candidate3 = new Candidate('Candidate 3'); | ||||
|  | ||||
|         // Register your candidates | ||||
|         $myElection1->addCandidate($candidate1); | ||||
|         $myElection1->addCandidate($candidate2); | ||||
|         $myElection1->addCandidate($candidate3); | ||||
|         $candidate4 = $myElection1->addCandidate('Candidate 4'); | ||||
|  | ||||
|         // Add some votes, by some ways | ||||
|         $myElection1->addVote( | ||||
|             [ | ||||
|                 $candidate2, // 1 | ||||
|                 [$candidate1, $candidate4], // 2 - Tie | ||||
|                 // Last rank is optionnal. Here it's : $candidate3 | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         $myElection1->addVote('Candidate 2 > Candidate 3 > Candidate 4 = Candidate 1'); // last rank can also be omitted | ||||
|  | ||||
|         $myElection1->parseVotes( | ||||
|             'tagX || Candidate 1 > Candidate 2 = Candidate 4 > Candidate 3 * 4 | ||||
|                       tagX, tagY || Candidate 3 > Candidate 1 * 3' | ||||
|         ); // Powerfull, it add 7 votes | ||||
|  | ||||
|         $myElection1->addVote(new Vote( | ||||
|             [ | ||||
|                 $candidate4, | ||||
|                 $candidate2, | ||||
|                 // You can ignore the over. They will be at the last rank in the contexte of each election. | ||||
|             ] | ||||
|         )); | ||||
|  | ||||
|  | ||||
|         // Get Result | ||||
|  | ||||
|         // Natural Condorcet Winner | ||||
|         $myWinner = $myElection1->getCondorcetWinner(); // Return a candidate object | ||||
|         $this->assertEquals('My winner is Candidate 1<br>', 'My winner is ' . $myWinner->getName() . '<br>'); | ||||
|  | ||||
|         // Natural Condorcet Loser | ||||
|         $myLoser = $myElection1->getCondorcetLoser(); // Return a candidate object | ||||
|         $this->assertEquals('My loser is Candidate 3', 'My loser is ' . $myLoser->getName()); | ||||
|  | ||||
|         // Schulze Ranking | ||||
|         $myResultBySchulze = $myElection1->getResult('Schulze'); // Return a multi-dimensional array, filled with objects Candidate (multi-dimensional if tie on a rank) | ||||
|               # Echo it easily | ||||
|         $this->assertEquals([1=>'Candidate 1', 2=>'Candidate 2', 3=>'Candidate 4', 4=>'Candidate 3'], CondorcetUtil::format($myResultBySchulze)); | ||||
|  | ||||
|         // Get Schulze advanced computing data & stats | ||||
|         $mySchulzeStats = $myElection1->getResult('Schulze')->getStats(); | ||||
|  | ||||
|         // Get Copeland Ranking | ||||
|         $myResultByCopeland = $myElection1->getResult('Copeland'); | ||||
|  | ||||
|         // Get Pairwise | ||||
|         $myPairwise = $myElection1->getPairwise(); | ||||
|  | ||||
|  | ||||
|         // How long computation time behind us? | ||||
|         $timer = $myElection1->getGlobalTimer(); | ||||
|  | ||||
|         // SHA-2 checksum and sleep | ||||
|         $myChecksum = $myElection1->getChecksum(); | ||||
|         $toStore = serialize($myElection1); | ||||
|         $comeBack = unserialize($toStore); | ||||
|         $this->assertEquals($comeBack->getChecksum(), $myChecksum); // True | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,220 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\Borda; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class BordaCountTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     protected function tearDown(): void | ||||
|     { | ||||
|         $this->election->setMethodOption('Borda Count', 'Starting', 1); | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         # From https://fr.wikipedia.org/wiki/M%C3%A9thode_Borda | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 42 | ||||
|             B>C>D>A * 26 | ||||
|             C>D>B>A * 15 | ||||
|             D>C>B>A * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'B', | ||||
|                 2 => 'C', | ||||
|                 3 => 'A', | ||||
|                 4 => 'D', ], | ||||
|             $this->election->getResult('Borda Count')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [ | ||||
|                 'B' => 294, | ||||
|                 'C' => 273, | ||||
|                 'A' => 226, | ||||
|                 'D' => 207, ], | ||||
|             $this->election->getResult('Borda Count')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         # From https://fr.wikipedia.org/wiki/M%C3%A9thode_Borda | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             B>A>C>D * 30 | ||||
|             B>A>D>C * 30 | ||||
|             A>C>D>B * 25 | ||||
|             A>D>C>B ^ 15 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => 'B', | ||||
|                 3 => 'C', | ||||
|                 4 => 'D', ], | ||||
|             $this->election->getResult('Borda Count')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [ | ||||
|                 'A' => 340, | ||||
|                 'B' => 280, | ||||
|                 'C' => 195, | ||||
|                 'D' => 185, ], | ||||
|             $this->election->getResult('Borda Count')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => ['B', 'C'], ], | ||||
|             $this->election->getResult('Borda Count')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [ | ||||
|                 'A' => 3, | ||||
|                 'B' => 1.5, | ||||
|                 'C' => 1.5, ], | ||||
|             $this->election->getResult('Borda Count')->getStats() | ||||
|         ); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => ['B', 'C'], ], | ||||
|             $this->election->getResult('Borda Count')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [ | ||||
|                 'A' => 3, | ||||
|                 'B' => 0, | ||||
|                 'C' => 0, ], | ||||
|             $this->election->getResult('Borda Count')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_4(): void | ||||
|     { | ||||
|         # From https://fr.wikipedia.org/wiki/M%C3%A9thode_Borda | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 42 | ||||
|             B>C>D>A * 26 | ||||
|             C>D>B>A * 15 | ||||
|             D>C>B>A * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'B', | ||||
|                 2 => 'C', | ||||
|                 3 => 'A', | ||||
|                 4 => 'D', ], | ||||
|             $this->election->getResult('Borda Count')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [ | ||||
|                 'B' => 294, | ||||
|                 'C' => 273, | ||||
|                 'A' => 226, | ||||
|                 'D' => 207, ], | ||||
|             $this->election->getResult('Borda Count')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_variant(): void | ||||
|     { | ||||
|         # From https://fr.wikipedia.org/wiki/M%C3%A9thode_Borda | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 42 | ||||
|             B>C>D>A * 26 | ||||
|             C>D>B>A * 15 | ||||
|             D>C>B>A * 17 | ||||
|         '); | ||||
|  | ||||
|         $this->election->setMethodOption('Borda Count', 'Starting', 0); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'B', | ||||
|                 2 => 'C', | ||||
|                 3 => 'A', | ||||
|                 4 => 'D', ], | ||||
|             $this->election->getResult('Borda Count')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [ | ||||
|                 'B' => 294 - 100, | ||||
|                 'C' => 273 - 100, | ||||
|                 'A' => 226 - 100, | ||||
|                 'D' => 207 - 100, ], | ||||
|             $this->election->getResult('Borda Count')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testVeryHighVoteWeightAndPerformances(): void | ||||
|     { | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|         $this->election->parseCandidates('0;1'); | ||||
|  | ||||
|         $this->election->parseVotes('1 > 0 ^6973568802'); | ||||
|  | ||||
|         self::assertSame('1', $this->election->getResult('Borda Count')->getResultAsString()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,58 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\Borda; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use CondorcetPHP\Condorcet\Algo\Methods\Borda\DowdallSystem; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class DowdallSystemTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Borda_count | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('E'); | ||||
|         $this->election->addCandidate('F'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D>E>F | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => 'B', | ||||
|                 3 => 'C', | ||||
|                 4 => 'D', | ||||
|                 5 => 'E', | ||||
|                 6 => 'F', ], | ||||
|             $this->election->getResult('DowdallSystem')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEqualsWithDelta( | ||||
|             [ | ||||
|                 'A' => 1/1, | ||||
|                 'B' => 1/2, | ||||
|                 'C' => 1/3, | ||||
|                 'D' => 1/4, | ||||
|                 'E' => 1/5, | ||||
|                 'F' => 1/6, ], | ||||
|             $this->election->getResult('DowdallSystem')->getStats(), | ||||
|             1 / (0.1 ** DowdallSystem::DECIMAL_PRECISION) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										161
									
								
								include/condorcet/Tests/src/Algo/Methods/CondorcetBasicTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								include/condorcet/Tests/src/Algo/Methods/CondorcetBasicTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Condorcet, Election, Vote}; | ||||
| use CondorcetPHP\Condorcet\Algo\Methods\CondorcetBasic; | ||||
| use CondorcetPHP\Condorcet\Throwable\AlgorithmWithoutRankingFeatureException; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CondorcetBasicTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_Basic(): void | ||||
|     { | ||||
|         $this->election->addCandidate('a'); | ||||
|         $this->election->addCandidate('b'); | ||||
|         $this->election->addCandidate('c'); | ||||
|  | ||||
|         $vote = new Vote('a'); | ||||
|  | ||||
|         $this->election->addVote($vote); | ||||
|  | ||||
|         self::assertEquals('a', $this->election->getCondorcetWinner()); | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         $this->election->addCandidate('a'); | ||||
|         $this->election->addCandidate('b'); | ||||
|         $this->election->addCandidate('c'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             a > c > b * 23 | ||||
|             b > c > a * 19 | ||||
|             c > b > a * 16 | ||||
|             c > a > b * 2 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('c', $this->election->getCondorcetWinner()); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         $this->election->addCandidate('X'); | ||||
|         $this->election->addCandidate('Y'); | ||||
|         $this->election->addCandidate('Z'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             X > Y > Z * 41 | ||||
|             Y > Z > X * 33 | ||||
|             Z > X > Y * 22 | ||||
|         '); | ||||
|  | ||||
|         self::assertNull($this->election->getWinner()); | ||||
|  | ||||
|         // Schulze Substitution | ||||
|         self::assertEquals('X', $this->election->getWinner('Schulze')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         $this->election->addCandidate('Memphis'); | ||||
|         $this->election->addCandidate('Nashville'); | ||||
|         $this->election->addCandidate('Knoxville'); | ||||
|         $this->election->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Memphis > Nashville > Chattanooga * 42 | ||||
|             Nashville > Chattanooga > Knoxville * 26 | ||||
|             Chattanooga > Knoxville > Nashville * 15 | ||||
|             Knoxville > Chattanooga > Nashville * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('Nashville', $this->election->getCondorcetWinner()); | ||||
|         self::assertEquals('Memphis', $this->election->getCondorcetLoser()); | ||||
|     } | ||||
|  | ||||
|     public function testResult_4(): void | ||||
|     { | ||||
|         $this->election->addCandidate('Memphis'); | ||||
|         $this->election->addCandidate('Nashville'); | ||||
|         $this->election->addCandidate('Knoxville'); | ||||
|         $this->election->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Memphis > Chattanooga > Nashville * 42 | ||||
|             Nashville > Chattanooga > Knoxville * 26 | ||||
|             Chattanooga > Knoxville > Nashville * 15 | ||||
|             Knoxville > Chattanooga > Nashville * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('Chattanooga', $this->election->getCondorcetWinner()); | ||||
|     } | ||||
|  | ||||
|     public function testResult_5(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Condorcet_loser_criterion | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('L'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C * 1 | ||||
|             A > B > L * 1 | ||||
|             B > C > A * 3 | ||||
|             C > L > A * 1 | ||||
|             L > A > B * 1 | ||||
|             L > C > A * 2 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('L', $this->election->getCondorcetLoser()); | ||||
|         self::assertNull($this->election->getCondorcetWinner()); | ||||
|     } | ||||
|  | ||||
|     public function testResult_6(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Condorcet_loser_criterion | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('L'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > L | ||||
|             B > C > L | ||||
|             A > C > L | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('L', $this->election->getCondorcetLoser()); | ||||
|     } | ||||
|  | ||||
|     public function testNoResultObject(): never | ||||
|     { | ||||
|         $this->expectException(AlgorithmWithoutRankingFeatureException::class); | ||||
|         $this->expectExceptionMessage("This algortihm can't provide a full ranking (but only Winner and Loser): ".CondorcetBasic::METHOD_NAME[0]); | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('L'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > L | ||||
|             B > C > L | ||||
|             A > C > L | ||||
|         '); | ||||
|  | ||||
|         $this->election->getResult(CondorcetBasic::class); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,130 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\Copeland; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CopelandTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Copeland%27s_method | ||||
|  | ||||
|         $this->election->addCandidate('Memphis'); | ||||
|         $this->election->addCandidate('Nashville'); | ||||
|         $this->election->addCandidate('Knoxville'); | ||||
|         $this->election->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Memphis > Nashville > Chattanooga * 42 | ||||
|             Nashville > Chattanooga > Knoxville * 26 | ||||
|             Chattanooga > Knoxville > Nashville * 15 | ||||
|             Knoxville > Chattanooga > Nashville * 17 | ||||
|         '); | ||||
|  | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Nashville', | ||||
|                 2 => 'Chattanooga', | ||||
|                 3 => 'Knoxville', | ||||
|                 4 => 'Memphis', ], | ||||
|             $this->election->getResult('Copeland')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame($this->election->getWinner('Copeland'), $this->election->getWinner()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'Memphis' => [ | ||||
|                     'balance' => -3, | ||||
|                 ], | ||||
|                 'Nashville' => [ | ||||
|                     'balance' => 3, | ||||
|                 ], | ||||
|                 'Knoxville' => [ | ||||
|                     'balance' => -1, | ||||
|                 ], | ||||
|                 'Chattanooga' => [ | ||||
|                     'balance' => 1, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Copeland')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Copeland%27s_method | ||||
|  | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|         $candidateE = $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > E > C > D * 31 | ||||
|             B > A > E * 30 | ||||
|             C > D > B * 29 | ||||
|             D > A > E * 10 | ||||
|         '); | ||||
|  | ||||
|         self::assertNull($this->election->getWinner()); | ||||
|         self::assertSame($candidateA, $this->election->getWinner('Copeland')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => $candidateA, | ||||
|                 2 => [$candidateB, $candidateC, $candidateE], | ||||
|                 3 => $candidateD, | ||||
|             ], | ||||
|             $this->election->getResult('Copeland')->getResultAsArray() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         # From http://www.cs.wustl.edu/~legrand/rbvote/desc.html | ||||
|  | ||||
|         $this->election->addCandidate('Abby'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|         $this->election->addCandidate('Cora'); | ||||
|         $this->election->addCandidate('Dave'); | ||||
|         $this->election->addCandidate('Erin'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Abby>Cora>Erin>Dave>Brad * 98 | ||||
|             Brad>Abby>Erin>Cora>Dave * 64 | ||||
|             Brad>Abby>Erin>Dave>Cora * 12 | ||||
|             Brad>Erin>Abby>Cora>Dave * 98 | ||||
|             Brad>Erin>Abby>Dave>Cora * 13 | ||||
|             Brad>Erin>Dave>Abby>Cora * 125 | ||||
|             Cora>Abby>Erin>Dave>Brad * 124 | ||||
|             Cora>Erin>Abby>Dave>Brad * 76 | ||||
|             Dave>Abby>Brad>Erin>Cora * 21 | ||||
|             Dave>Brad>Abby>Erin>Cora * 30 | ||||
|             Dave>Brad>Erin>Cora>Abby * 98 | ||||
|             Dave>Cora>Abby>Brad>Erin * 139 | ||||
|             Dave>Cora>Brad>Abby>Erin * 23 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals(['Abby', 'Brad'], $this->election->getWinner('Copeland')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['Abby', 'Brad'], | ||||
|                 2 => ['Dave', 'Erin'], | ||||
|                 3 => 'Cora', ], | ||||
|             $this->election->getResult('Copeland')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										534
									
								
								include/condorcet/Tests/src/Algo/Methods/Dodgson/DodgsonTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										534
									
								
								include/condorcet/Tests/src/Algo/Methods/Dodgson/DodgsonTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,534 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\Dodgson; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class DodgsonTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         # From http://www.cs.wustl.edu/~legrand/rbvote/desc.html | ||||
|  | ||||
|         $CandidateCora = $this->election->addCandidate('Cora'); | ||||
|         $this->election->addCandidate('Abby'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|         $this->election->addCandidate('Dave'); | ||||
|         $this->election->addCandidate('Erin'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Abby>Cora>Erin>Dave>Brad * 98 | ||||
|             Brad>Abby>Erin>Cora>Dave * 64 | ||||
|             Brad>Abby>Erin>Dave>Cora * 12 | ||||
|             Brad>Erin>Abby>Cora>Dave * 98 | ||||
|             Brad>Erin>Abby>Dave>Cora * 13 | ||||
|             Brad>Erin>Dave>Abby>Cora * 125 | ||||
|             Cora>Abby>Erin>Dave>Brad * 124 | ||||
|             Cora>Erin>Abby>Dave>Brad * 76 | ||||
|             Dave>Abby>Brad>Erin>Cora * 21 | ||||
|             Dave>Brad>Abby>Erin>Cora * 30 | ||||
|             Dave>Brad>Erin>Cora>Abby * 98 | ||||
|             Dave>Cora>Abby>Brad>Erin * 139 | ||||
|             Dave>Cora>Brad>Abby>Erin * 23 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($CandidateCora, $this->election->getWinner('DodgsonTideman')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'Cora', | ||||
|                 2 => 'Abby', | ||||
|                 3 => 'Brad', | ||||
|                 4 => 'Dave', | ||||
|                 5 => 'Erin', ], | ||||
|             $this->election->getResult('DodgsonTideman')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'Cora' => [ | ||||
|                     'sum_defeat_margin' => 4, | ||||
|                 ], | ||||
|                 'Abby' => [ | ||||
|                     'sum_defeat_margin' => 5, | ||||
|                 ], | ||||
|                 'Brad' => [ | ||||
|                     'sum_defeat_margin' => 297, | ||||
|                 ], | ||||
|                 'Dave' => [ | ||||
|                     'sum_defeat_margin' => 348, | ||||
|                 ], | ||||
|                 'Erin' => [ | ||||
|                     'sum_defeat_margin' => 426, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonTideman')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     # Require real Dodgson method. This test fail with both approximations. | ||||
|     // public function testResult_2 (): void | ||||
|     // { | ||||
|     //     # From http://dss.in.tum.de/files/brandt-research/dodgson.pdf | ||||
|     # Table 1 | ||||
|  | ||||
|     //     $this->election->addCandidate('A'); | ||||
|     //     $this->election->addCandidate('B'); | ||||
|     //     $this->election->addCandidate('C'); | ||||
|     //     $this->election->addCandidate('D'); | ||||
|  | ||||
|     //     $this->election->parseVotes(' | ||||
|     //         D>C>A>B*2 | ||||
|     //         B>C>A>D*2 | ||||
|     //         C>A>B>D*2 | ||||
|     //         D>B>C>A*2 | ||||
|     //         A>B>C>D*2 | ||||
|     //         A>D>B>C*1 | ||||
|     //         D>A>B>C*1 | ||||
|     //     '); | ||||
|  | ||||
|     //     self::assertEquals( | ||||
|     //         'A', $this->election->getWinner('DodgsonQuick')); | ||||
|     // } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         # From http://dss.in.tum.de/files/brandt-research/dodgson.pdf | ||||
|         # Table 2 | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             D>C>A>B*6 | ||||
|             B>C>A>D*6 | ||||
|             C>A>B>D*6 | ||||
|             D>B>C>A*6 | ||||
|             A>B>C>D*6 | ||||
|             A>D>B>C*3 | ||||
|             D>A>B>C*3 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'D', | ||||
|             $this->election->getWinner('DodgsonQuick') | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'D' => 3.0, | ||||
|                 'A' => 6.0, | ||||
|                 'B' => 6.0, | ||||
|                 'C' => 6.0, | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonQuick')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     # Require real Dodgson method. This test fail with both approximations. | ||||
|     // public function testResult_4 (): void | ||||
|     // { | ||||
|     //     # From http://dss.in.tum.de/files/brandt-research/dodgson.pdf | ||||
|     # Table 3 | ||||
|  | ||||
|     //     $this->election->addCandidate('A'); | ||||
|     //     $this->election->addCandidate('B'); | ||||
|     //     $this->election->addCandidate('C'); | ||||
|     //     $this->election->addCandidate('D'); | ||||
|  | ||||
|     //     $this->election->parseVotes(' | ||||
|     //         C>A>D>B*15 | ||||
|     //         B>D>C>A*9 | ||||
|     //         A>B>D>C*9 | ||||
|     //         A>C>B>D*5 | ||||
|     //         B>A>C>D*5 | ||||
|     //     '); | ||||
|  | ||||
|     //     self::assertEquals( | ||||
|     //         'A', $this->election->getWinner('DodgsonQuick')); | ||||
|     // } | ||||
|  | ||||
|     public function testResult_5(): void | ||||
|     { | ||||
|         # From http://dss.in.tum.de/files/brandt-research/dodgson.pdf | ||||
|         # Table 4 | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             C>A>D>B*15 | ||||
|             B>D>C>A*9 | ||||
|             A>B>D>C*9 | ||||
|             A>C>B>D*5 | ||||
|             A>B>C>D*5 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'C', | ||||
|             $this->election->getWinner('DodgsonQuick') | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['C' => 2.0, | ||||
|                 'A' => 3.0, | ||||
|                 'B' => 13.0, | ||||
|                 'D' => 24.0, | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonQuick')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_6(): void | ||||
|     { | ||||
|         # From http://dss.in.tum.de/files/brandt-research/dodgson.pdf | ||||
|         # Table 5 | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             D>A>B>C*10 | ||||
|             B>C>A>D*8 | ||||
|             C>A>B>D*7 | ||||
|             D>C>A>B*4 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'D', | ||||
|             $this->election->getWinner('DodgsonQuick') | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['D' => 3.0, | ||||
|                 'C' => 4.0, | ||||
|                 'A' => 5.0, | ||||
|                 'B' => 7.0, | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonQuick')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_7(): void | ||||
|     { | ||||
|         # From http://dss.in.tum.de/files/brandt-research/dodgson.pdf | ||||
|         # Table 6 | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             C>B>A>D*10 | ||||
|             D>A>C>B*8 | ||||
|             D>B>A>C*7 | ||||
|             B>A>C>D*4 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'D', | ||||
|             $this->election->getWinner('DodgsonQuick') | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['B' => 5.0, | ||||
|                 'C' => 6.0, | ||||
|                 'A' => 8.0, | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonQuick')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_8(): void | ||||
|     { | ||||
|         # From http://dss.in.tum.de/files/brandt-research/dodgson.pdf | ||||
|         # Table 7 | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C*5 | ||||
|             B>C>A*4 | ||||
|             C>A>B*3 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'A', | ||||
|             $this->election->getWinner('DodgsonQuick') | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['A' => 1.0, | ||||
|                 'B' => 2.0, | ||||
|                 'C' => 3.0, | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonQuick')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     # Require real Dodgson method. This test fail with both approximations. | ||||
|     // public function testResult_9 (): void | ||||
|     // { | ||||
|     //     # From http://dss.in.tum.de/files/brandt-research/dodgson.pdf | ||||
|     # Table 8 | ||||
|  | ||||
|     //     $this->election->addCandidate('A'); | ||||
|     //     $this->election->addCandidate('B'); | ||||
|     //     $this->election->addCandidate('C'); | ||||
|     //     $this->election->addCandidate('Cp'); | ||||
|  | ||||
|     //     $this->election->parseVotes(' | ||||
|     //         A>B>C>Cp*5 | ||||
|     //         B>C>Cp>A*4 | ||||
|     //         C>Cp>A>B*3 | ||||
|     //     '); | ||||
|  | ||||
|     //     self::assertEquals( | ||||
|     //         'B', $this->election->getWinner('DodgsonQuick')); | ||||
|     // } | ||||
|  | ||||
|     public function testResult_10(): void | ||||
|     { | ||||
|         # From https://link.springer.com/article/10.1007/s003550000060 | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D*21 | ||||
|             C>D>B>A*12 | ||||
|             D>C>A>B*5 | ||||
|             B>D>A>C*12 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'B', | ||||
|             $this->election->getWinner('DodgsonQuick') | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'B', | ||||
|             $this->election->getWinner('DodgsonTideman') | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_11(): void | ||||
|     { | ||||
|         # From https://www.maa.org/sites/default/files/pdf/cmj_ftp/CMJ/September%202010/3%20Articles/6%2009-229%20Ratliff/Dodgson_CMJ_Final.pdf | ||||
|         # Figure 2 with Tideman Approximation | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D*3 | ||||
|             D>B>A>C*1 | ||||
|             D>C>A>B*1 | ||||
|             B>D>C>A*1 | ||||
|             C>D>B>A*1 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals(1, $this->election->getResult('DodgsonTideman')->getStats()['A']['sum_defeat_margin']); | ||||
|         self::assertEquals(1, $this->election->getResult('DodgsonTideman')->getStats()['B']['sum_defeat_margin']); | ||||
|         self::assertEquals(4, $this->election->getResult('DodgsonTideman')->getStats()['C']['sum_defeat_margin']); | ||||
|         self::assertEquals(2, $this->election->getResult('DodgsonTideman')->getStats()['D']['sum_defeat_margin']); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B'], | ||||
|                 2 => 'D', | ||||
|                 3 => 'C', | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonTideman')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_12(): void | ||||
|     { | ||||
|         # From https://www.maa.org/sites/default/files/pdf/cmj_ftp/CMJ/September%202010/3%20Articles/6%2009-229%20Ratliff/Dodgson_CMJ_Final.pdf | ||||
|         # Figure 3 with Tideman Approximation | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D*5 | ||||
|             D>C>A>B*6 | ||||
|             C>A>B>D*5 | ||||
|             D>B>C>A*5 | ||||
|             B>C>A>D*4 | ||||
|             D>A>B>C*4 | ||||
|             C>D>A>B*1 | ||||
|             B>A>C>D*1 | ||||
|             B>D>A>C*1 | ||||
|             C>A>B>D*1 | ||||
|             A>D>B>C*1 | ||||
|             C>B>A>D*1 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals(11, $this->election->getResult('DodgsonTideman')->getStats()['A']['sum_defeat_margin']); | ||||
|         self::assertEquals(11, $this->election->getResult('DodgsonTideman')->getStats()['B']['sum_defeat_margin']); | ||||
|         self::assertEquals(7, $this->election->getResult('DodgsonTideman')->getStats()['C']['sum_defeat_margin']); | ||||
|         self::assertEquals(3, $this->election->getResult('DodgsonTideman')->getStats()['D']['sum_defeat_margin']); | ||||
|  | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'D', | ||||
|             $this->election->getWinner('DodgsonTideman') | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'D', | ||||
|                 2 => 'C', | ||||
|                 3 => ['A', 'B'], | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonTideman')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_13(): void | ||||
|     { | ||||
|         # From https://www.maa.org/sites/default/files/pdf/cmj_ftp/CMJ/September%202010/3%20Articles/6%2009-229%20Ratliff/Dodgson_CMJ_Final.pdf | ||||
|         # Figure 4 | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('E'); | ||||
|         $this->election->addCandidate('F'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D>E>F*19 | ||||
|             F>A>B>C>D>E*12 | ||||
|             E>D>C>B>F>A*12 | ||||
|             B>A>C>D>E>F*9 | ||||
|             F>E>D>C>B>A*9 | ||||
|             F>B>A>C>D>E*10 | ||||
|             E>D>C>A>F>B*10 | ||||
|             E>B>A>C>D>F*10 | ||||
|             F>D>C>A>E>B*10 | ||||
|             D>B>A>C>E>F*10 | ||||
|             F>E>C>A>D>B*10 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'A', | ||||
|             $this->election->getWinner('DodgsonQuick') | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['A' => 3.0, | ||||
|                 'B' => 4.0, | ||||
|                 'C' => 20.0, | ||||
|                 'D' => 20.0, | ||||
|                 'E' => 30.0, | ||||
|                 'F' => 30.0, | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonQuick')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_14(): void | ||||
|     { | ||||
|         # From https://www.maa.org/sites/default/files/pdf/cmj_ftp/CMJ/September%202010/3%20Articles/6%2009-229%20Ratliff/Dodgson_CMJ_Final.pdf | ||||
|         # Figure 4: each voters add 4 friends. | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('E'); | ||||
|         $this->election->addCandidate('F'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D>E>F*95 | ||||
|             F>A>B>C>D>E*60 | ||||
|             E>D>C>B>F>A*60 | ||||
|             B>A>C>D>E>F*45 | ||||
|             F>E>D>C>B>A*45 | ||||
|             F>B>A>C>D>E*50 | ||||
|             E>D>C>A>F>B*50 | ||||
|             E>B>A>C>D>F*50 | ||||
|             F>D>C>A>E>B*50 | ||||
|             D>B>A>C>E>F*50 | ||||
|             F>E>C>A>D>B*50 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             13, | ||||
|             $this->election->getResult('DodgsonQuick')->getStats()['A'] | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             12, | ||||
|             $this->election->getResult('DodgsonQuick')->getStats()['B'] | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             'B', | ||||
|             $this->election->getWinner('DodgsonQuick') | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_15(): void | ||||
|     { | ||||
|         $this->election->addCandidate('Memphis'); | ||||
|         $this->election->addCandidate('Nashville'); | ||||
|         $this->election->addCandidate('Knoxville'); | ||||
|         $this->election->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Memphis > Chattanooga > Nashville * 42 | ||||
|             Nashville > Chattanooga > Knoxville * 26 | ||||
|             Chattanooga > Knoxville > Nashville * 15 | ||||
|             Knoxville > Chattanooga > Nashville * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($this->election->getWinner(null), $this->election->getWinner('DodgsonQuick')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_16(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A | ||||
|             B | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => ['A', 'B'], | ||||
|                 2 => ['C', 'D'], | ||||
|             ], | ||||
|             $this->election->getResult('DodgsonQuick')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,736 @@ | ||||
| # Specifications: https://github.com/CondorcetPHP/CondorcetElectionFormat | ||||
|  | ||||
| # This election has 989 votes | ||||
| #/Candidates: 1 ; 2 ; 3 ; 4 ; 5 ; 6 ; 7 ; 8 ; 9 ; 10 ; 11 ; 12 ; 13 ; 14 ; 15 | ||||
| #/Number of Seats: 7 | ||||
| #/Implicit Ranking: true | ||||
| #/Weight Allowed: false | ||||
|  | ||||
| 6 > 5 > 4 > 11 > 7 > 10 > 12 > 13 > 8 > 9 > 14 > 15 * 30 | ||||
| 6 * 14 | ||||
| 4 * 13 | ||||
| 8 * 13 | ||||
| 2 * 11 | ||||
| /EMPTY_RANKING/ * 8 | ||||
| 8 > 11 * 8 | ||||
| 2 > 7 * 6 | ||||
| 6 > 8 > 14 * 6 | ||||
| 8 > 5 > 11 > 9 * 6 | ||||
| 8 > 6 * 6 | ||||
| 14 * 6 | ||||
| 5 * 5 | ||||
| 6 > 8 * 5 | ||||
| 8 > 11 > 5 * 5 | ||||
| 8 > 12 * 5 | ||||
| 11 * 5 | ||||
| 1 > 3 > 13 * 4 | ||||
| 6 > 8 > 11 * 4 | ||||
| 6 > 8 > 12 * 4 | ||||
| 6 > 11 > 14 * 4 | ||||
| 8 > 6 > 14 * 4 | ||||
| 12 * 4 | ||||
| 1 * 3 | ||||
| 2 > 1 * 3 | ||||
| 2 > 7 > 9 * 3 | ||||
| 3 * 3 | ||||
| 4 > 6 > 5 * 3 | ||||
| 4 > 6 > 8 * 3 | ||||
| 5 > 6 > 8 * 3 | ||||
| 6 > 4 > 8 * 3 | ||||
| 6 > 4 > 11 * 3 | ||||
| 6 > 8 > 4 * 3 | ||||
| 6 > 11 * 3 | ||||
| 7 * 3 | ||||
| 8 > 5 * 3 | ||||
| 8 > 5 > 9 > 11 * 3 | ||||
| 8 > 6 > 11 * 3 | ||||
| 8 > 6 > 12 * 3 | ||||
| 8 > 11 > 5 > 9 * 3 | ||||
| 8 > 14 * 3 | ||||
| 9 * 3 | ||||
| 11 > 6 > 4 * 3 | ||||
| 11 > 14 > 6 * 3 | ||||
| 14 > 8 * 3 | ||||
| 1 > 2 > 7 * 2 | ||||
| 1 > 4 > 6 * 2 | ||||
| 1 > 6 * 2 | ||||
| 1 > 13 > 3 * 2 | ||||
| 2 > 5 > 10 * 2 | ||||
| 2 > 9 > 10 * 2 | ||||
| 2 > 10 > 9 * 2 | ||||
| 3 > 8 * 2 | ||||
| 4 > 5 > 3 * 2 | ||||
| 4 > 6 * 2 | ||||
| 4 > 6 > 3 * 2 | ||||
| 4 > 6 > 11 * 2 | ||||
| 4 > 8 > 6 * 2 | ||||
| 4 > 8 > 11 * 2 | ||||
| 4 > 11 * 2 | ||||
| 4 > 14 > 11 * 2 | ||||
| 5 > 6 > 11 * 2 | ||||
| 5 > 6 > 14 * 2 | ||||
| 5 > 8 * 2 | ||||
| 5 > 11 * 2 | ||||
| 5 > 11 > 4 * 2 | ||||
| 5 > 11 > 6 * 2 | ||||
| 5 > 11 > 8 * 2 | ||||
| 6 > 3 > 5 * 2 | ||||
| 6 > 4 > 8 > 12 > 11 > 14 > 3 > 5 > 13 * 2 | ||||
| 6 > 4 > 10 > 14 > 7 > 5 > 8 > 11 * 2 | ||||
| 6 > 4 > 12 * 2 | ||||
| 6 > 5 > 4 > 14 > 7 > 10 > 12 > 13 > 8 > 9 > 11 > 15 * 2 | ||||
| 6 > 5 > 8 > 11 * 2 | ||||
| 6 > 5 > 11 * 2 | ||||
| 6 > 11 > 12 * 2 | ||||
| 6 > 11 > 15 * 2 | ||||
| 6 > 12 * 2 | ||||
| 7 > 10 > 6 * 2 | ||||
| 8 > 5 > 6 > 11 * 2 | ||||
| 8 > 5 > 11 * 2 | ||||
| 8 > 5 > 11 > 12 * 2 | ||||
| 8 > 6 > 5 * 2 | ||||
| 8 > 9 > 12 * 2 | ||||
| 8 > 11 > 9 > 5 * 2 | ||||
| 8 > 11 > 14 * 2 | ||||
| 8 > 14 > 5 * 2 | ||||
| 9 > 10 > 12 > 6 > 14 > 13 > 1 * 2 | ||||
| 10 * 2 | ||||
| 11 > 4 > 6 * 2 | ||||
| 11 > 4 > 8 * 2 | ||||
| 11 > 4 > 14 > 7 > 8 > 3 * 2 | ||||
| 11 > 5 > 3 * 2 | ||||
| 11 > 5 > 6 * 2 | ||||
| 11 > 5 > 6 > 14 * 2 | ||||
| 11 > 6 > 5 * 2 | ||||
| 11 > 6 > 5 > 8 * 2 | ||||
| 11 > 6 > 12 * 2 | ||||
| 11 > 7 * 2 | ||||
| 11 > 8 * 2 | ||||
| 11 > 8 > 6 * 2 | ||||
| 11 > 12 > 14 * 2 | ||||
| 12 > 8 * 2 | ||||
| 12 > 14 * 2 | ||||
| 14 > 4 > 6 * 2 | ||||
| 14 > 5 * 2 | ||||
| 14 > 6 > 8 * 2 | ||||
| 14 > 6 > 10 * 2 | ||||
| 14 > 11 > 6 * 2 | ||||
| 1 > 2 > 3 > 4 > 5 > 6 > 7 > 8 > 9 > 10 > 11 > 12 > 13 > 14 * 1 | ||||
| 1 > 2 > 3 > 6 > 10 * 1 | ||||
| 1 > 2 > 3 > 7 > 8 > 15 > 10 > 13 * 1 | ||||
| 1 > 2 > 15 > 9 * 1 | ||||
| 1 > 3 > 2 > 15 > 6 * 1 | ||||
| 1 > 3 > 5 > 13 > 7 > 11 * 1 | ||||
| 1 > 3 > 7 * 1 | ||||
| 1 > 3 > 10 * 1 | ||||
| 1 > 3 > 13 > 4 * 1 | ||||
| 1 > 3 > 14 * 1 | ||||
| 1 > 3 > 15 * 1 | ||||
| 1 > 4 > 5 > 13 * 1 | ||||
| 1 > 4 > 6 > 11 > 12 > 13 > 15 > 8 > 14 > 7 > 5 > 2 > 9 > 10 * 1 | ||||
| 1 > 4 > 6 > 13 > 11 > 14 * 1 | ||||
| 1 > 5 * 1 | ||||
| 1 > 6 > 2 > 15 > 11 > 7 > 3 > 10 * 1 | ||||
| 1 > 6 > 4 > 11 * 1 | ||||
| 1 > 6 > 8 > 9 > 15 * 1 | ||||
| 1 > 7 * 1 | ||||
| 1 > 7 > 15 * 1 | ||||
| 1 > 8 > 4 > 11 * 1 | ||||
| 1 > 8 > 5 * 1 | ||||
| 1 > 8 > 12 * 1 | ||||
| 1 > 9 > 2 > 3 > 6 > 5 * 1 | ||||
| 1 > 9 > 10 > 13 > 3 > 5 * 1 | ||||
| 1 > 10 > 2 > 3 > 7 * 1 | ||||
| 1 > 10 > 2 > 8 * 1 | ||||
| 1 > 11 > 13 * 1 | ||||
| 1 > 13 > 2 > 3 > 7 * 1 | ||||
| 1 > 13 > 5 > 4 > 6 * 1 | ||||
| 1 > 14 > 15 > 11 * 1 | ||||
| 1 > 15 > 4 > 6 > 7 > 8 > 3 > 2 > 9 > 10 > 13 > 11 > 14 > 12 * 1 | ||||
| 2 > 1 > 4 * 1 | ||||
| 2 > 1 > 4 > 13 * 1 | ||||
| 2 > 1 > 7 * 1 | ||||
| 2 > 1 > 7 > 15 * 1 | ||||
| 2 > 1 > 10 * 1 | ||||
| 2 > 3 > 1 > 7 > 8 > 9 > 13 > 5 > 12 > 15 > 10 > 14 > 4 > 11 * 1 | ||||
| 2 > 3 > 1 > 13 * 1 | ||||
| 2 > 3 > 5 > 11 * 1 | ||||
| 2 > 6 * 1 | ||||
| 2 > 6 > 7 > 10 * 1 | ||||
| 2 > 6 > 7 > 10 > 1 > 13 > 3 * 1 | ||||
| 2 > 6 > 7 > 10 > 9 * 1 | ||||
| 2 > 6 > 10 * 1 | ||||
| 2 > 7 > 1 > 3 > 10 > 13 * 1 | ||||
| 2 > 7 > 1 > 6 > 10 > 15 * 1 | ||||
| 2 > 7 > 5 > 1 > 3 > 10 > 9 * 1 | ||||
| 2 > 7 > 9 > 1 > 3 * 1 | ||||
| 2 > 7 > 9 > 13 > 10 * 1 | ||||
| 2 > 7 > 10 > 9 > 8 * 1 | ||||
| 2 > 7 > 10 > 9 > 13 > 3 > 8 > 6 * 1 | ||||
| 2 > 7 > 12 > 13 > 9 > 10 > 6 * 1 | ||||
| 2 > 8 * 1 | ||||
| 2 > 8 > 6 * 1 | ||||
| 2 > 8 > 15 > 11 * 1 | ||||
| 2 > 9 > 6 * 1 | ||||
| 2 > 10 > 6 * 1 | ||||
| 2 > 10 > 7 > 9 * 1 | ||||
| 2 > 13 * 1 | ||||
| 2 > 13 > 6 > 3 > 8 * 1 | ||||
| 2 > 13 > 7 * 1 | ||||
| 2 > 13 > 9 > 10 > 7 * 1 | ||||
| 3 > 1 * 1 | ||||
| 3 > 1 > 7 > 13 > 15 * 1 | ||||
| 3 > 2 * 1 | ||||
| 3 > 2 > 7 > 15 * 1 | ||||
| 3 > 2 > 7 > 15 > 13 * 1 | ||||
| 3 > 4 > 10 * 1 | ||||
| 3 > 4 > 10 > 14 * 1 | ||||
| 3 > 4 > 12 > 13 > 11 > 8 > 9 * 1 | ||||
| 3 > 6 > 4 * 1 | ||||
| 3 > 6 > 4 > 1 * 1 | ||||
| 3 > 6 > 7 * 1 | ||||
| 3 > 6 > 11 * 1 | ||||
| 3 > 7 > 11 * 1 | ||||
| 3 > 8 > 6 * 1 | ||||
| 3 > 9 > 13 * 1 | ||||
| 3 > 11 > 6 > 10 > 14 * 1 | ||||
| 3 > 11 > 8 * 1 | ||||
| 3 > 13 * 1 | ||||
| 3 > 13 > 1 > 7 > 15 > 2 > 9 > 10 * 1 | ||||
| 3 > 14 > 5 > 10 > 13 * 1 | ||||
| 3 > 14 > 6 > 8 * 1 | ||||
| 4 > 1 > 5 * 1 | ||||
| 4 > 1 > 6 > 9 > 11 > 3 > 5 > 2 > 7 > 10 > 12 > 14 > 15 > 13 * 1 | ||||
| 4 > 1 > 6 > 11 * 1 | ||||
| 4 > 1 > 6 > 11 > 2 > 8 * 1 | ||||
| 4 > 2 > 7 > 8 > 5 > 13 > 14 > 9 > 10 > 3 > 1 > 6 > 15 > 11 * 1 | ||||
| 4 > 3 > 1 > 5 * 1 | ||||
| 4 > 3 > 5 > 8 > 14 * 1 | ||||
| 4 > 3 > 6 > 8 > 11 * 1 | ||||
| 4 > 3 > 13 * 1 | ||||
| 4 > 5 * 1 | ||||
| 4 > 5 > 6 > 7 > 11 > 14 * 1 | ||||
| 4 > 5 > 6 > 8 > 3 > 11 * 1 | ||||
| 4 > 5 > 6 > 8 > 11 > 14 > 15 > 13 > 12 > 3 > 1 > 2 > 7 > 9 * 1 | ||||
| 4 > 5 > 6 > 11 * 1 | ||||
| 4 > 5 > 6 > 11 > 12 > 15 * 1 | ||||
| 4 > 5 > 6 > 11 > 14 * 1 | ||||
| 4 > 5 > 6 > 11 > 14 > 12 > 8 > 7 > 15 > 3 > 10 * 1 | ||||
| 4 > 5 > 6 > 13 > 14 * 1 | ||||
| 4 > 5 > 7 > 11 * 1 | ||||
| 4 > 5 > 8 > 3 > 11 * 1 | ||||
| 4 > 5 > 11 * 1 | ||||
| 4 > 5 > 14 * 1 | ||||
| 4 > 6 > 1 * 1 | ||||
| 4 > 6 > 1 > 8 * 1 | ||||
| 4 > 6 > 1 > 11 * 1 | ||||
| 4 > 6 > 1 > 11 > 3 > 5 * 1 | ||||
| 4 > 6 > 2 * 1 | ||||
| 4 > 6 > 5 > 2 > 1 > 13 > 12 > 11 > 7 > 3 > 8 > 9 > 10 > 14 * 1 | ||||
| 4 > 6 > 5 > 11 * 1 | ||||
| 4 > 6 > 5 > 11 > 7 > 10 > 12 > 13 > 8 > 9 > 14 > 15 * 1 | ||||
| 4 > 6 > 7 > 5 > 11 * 1 | ||||
| 4 > 6 > 7 > 10 > 14 > 11 > 3 > 5 > 8 > 9 > 12 > 13 > 15 > 1 * 1 | ||||
| 4 > 6 > 8 > 1 * 1 | ||||
| 4 > 6 > 8 > 11 * 1 | ||||
| 4 > 6 > 8 > 11 > 12 > 13 > 3 > 5 > 14 * 1 | ||||
| 4 > 6 > 8 > 11 > 14 * 1 | ||||
| 4 > 6 > 8 > 13 > 15 > 5 > 10 * 1 | ||||
| 4 > 6 > 8 > 14 > 11 * 1 | ||||
| 4 > 6 > 10 * 1 | ||||
| 4 > 6 > 10 > 7 > 11 > 14 > 12 * 1 | ||||
| 4 > 6 > 10 > 11 > 5 > 12 > 8 * 1 | ||||
| 4 > 6 > 10 > 12 * 1 | ||||
| 4 > 6 > 11 > 1 > 3 * 1 | ||||
| 4 > 6 > 11 > 1 > 7 * 1 | ||||
| 4 > 6 > 11 > 1 > 8 > 13 > 2 > 14 * 1 | ||||
| 4 > 6 > 11 > 1 > 13 > 15 > 14 > 7 > 5 > 3 > 2 > 12 > 8 > 9 * 1 | ||||
| 4 > 6 > 11 > 5 > 10 > 12 > 14 * 1 | ||||
| 4 > 6 > 11 > 7 > 10 > 12 > 14 * 1 | ||||
| 4 > 6 > 11 > 8 > 5 * 1 | ||||
| 4 > 6 > 11 > 8 > 7 > 1 * 1 | ||||
| 4 > 6 > 11 > 10 * 1 | ||||
| 4 > 6 > 11 > 10 > 13 > 12 > 14 * 1 | ||||
| 4 > 6 > 11 > 12 > 14 > 10 > 8 > 5 > 13 > 7 > 15 > 3 > 2 > 1 * 1 | ||||
| 4 > 6 > 11 > 13 * 1 | ||||
| 4 > 6 > 11 > 14 * 1 | ||||
| 4 > 6 > 12 * 1 | ||||
| 4 > 6 > 12 > 13 * 1 | ||||
| 4 > 7 > 9 > 15 * 1 | ||||
| 4 > 8 * 1 | ||||
| 4 > 8 > 1 * 1 | ||||
| 4 > 8 > 2 > 7 * 1 | ||||
| 4 > 8 > 3 > 5 > 11 > 14 * 1 | ||||
| 4 > 8 > 3 > 6 > 11 > 9 > 14 > 15 > 2 > 1 > 5 > 13 > 7 > 12 * 1 | ||||
| 4 > 8 > 5 * 1 | ||||
| 4 > 8 > 6 > 1 > 14 > 11 * 1 | ||||
| 4 > 8 > 6 > 5 > 11 > 14 * 1 | ||||
| 4 > 8 > 6 > 11 * 1 | ||||
| 4 > 8 > 11 > 14 > 12 > 13 > 3 * 1 | ||||
| 4 > 8 > 12 * 1 | ||||
| 4 > 8 > 14 > 1 * 1 | ||||
| 4 > 8 > 14 > 3 * 1 | ||||
| 4 > 9 > 12 > 14 * 1 | ||||
| 4 > 9 > 15 > 1 > 3 > 2 > 5 > 7 * 1 | ||||
| 4 > 11 > 3 > 6 * 1 | ||||
| 4 > 11 > 5 > 14 > 6 * 1 | ||||
| 4 > 11 > 6 * 1 | ||||
| 4 > 11 > 6 > 5 > 14 * 1 | ||||
| 4 > 11 > 6 > 10 > 15 * 1 | ||||
| 4 > 11 > 8 * 1 | ||||
| 4 > 11 > 8 > 5 > 6 > 3 > 7 > 14 > 15 > 13 > 12 > 10 > 9 * 1 | ||||
| 4 > 11 > 8 > 6 * 1 | ||||
| 4 > 11 > 8 > 6 > 5 * 1 | ||||
| 4 > 11 > 12 * 1 | ||||
| 4 > 11 > 14 * 1 | ||||
| 4 > 11 > 14 > 8 * 1 | ||||
| 4 > 11 > 15 > 8 > 6 > 5 > 3 > 10 * 1 | ||||
| 4 > 12 > 5 * 1 | ||||
| 4 > 12 > 6 > 1 > 3 > 13 * 1 | ||||
| 4 > 12 > 8 > 5 > 11 * 1 | ||||
| 4 > 13 > 3 * 1 | ||||
| 4 > 13 > 3 > 5 > 7 > 10 > 11 > 14 * 1 | ||||
| 4 > 13 > 5 > 1 > 11 > 3 > 8 * 1 | ||||
| 4 > 14 > 11 > 8 * 1 | ||||
| 5 > 4 > 3 * 1 | ||||
| 5 > 4 > 6 > 11 > 13 > 1 > 3 > 8 * 1 | ||||
| 5 > 4 > 8 > 1 * 1 | ||||
| 5 > 4 > 11 > 15 * 1 | ||||
| 5 > 6 * 1 | ||||
| 5 > 6 > 1 > 7 > 8 > 15 > 13 > 11 > 12 > 4 > 3 > 9 > 14 > 2 * 1 | ||||
| 5 > 6 > 3 > 11 > 12 * 1 | ||||
| 5 > 6 > 4 > 11 > 8 * 1 | ||||
| 5 > 6 > 8 > 14 * 1 | ||||
| 5 > 6 > 11 > 4 > 14 > 8 * 1 | ||||
| 5 > 6 > 11 > 12 * 1 | ||||
| 5 > 6 > 14 > 8 > 7 * 1 | ||||
| 5 > 6 > 14 > 11 > 12 > 8 * 1 | ||||
| 5 > 6 > 15 > 8 > 11 * 1 | ||||
| 5 > 7 > 2 * 1 | ||||
| 5 > 7 > 8 > 11 > 14 > 13 > 4 > 1 > 15 > 12 > 6 > 3 > 9 > 10 * 1 | ||||
| 5 > 7 > 11 * 1 | ||||
| 5 > 8 > 1 * 1 | ||||
| 5 > 8 > 1 > 11 > 12 > 13 > 9 > 2 > 3 > 14 > 7 > 10 > 4 > 6 * 1 | ||||
| 5 > 8 > 6 > 14 * 1 | ||||
| 5 > 8 > 7 > 10 > 3 > 11 * 1 | ||||
| 5 > 8 > 9 * 1 | ||||
| 5 > 8 > 11 * 1 | ||||
| 5 > 8 > 11 > 2 > 6 > 14 * 1 | ||||
| 5 > 8 > 11 > 6 > 12 * 1 | ||||
| 5 > 8 > 11 > 12 * 1 | ||||
| 5 > 8 > 11 > 13 > 6 > 15 > 1 > 7 > 3 * 1 | ||||
| 5 > 8 > 11 > 14 > 7 > 6 * 1 | ||||
| 5 > 8 > 12 > 11 * 1 | ||||
| 5 > 8 > 13 * 1 | ||||
| 5 > 8 > 14 > 1 > 11 * 1 | ||||
| 5 > 9 > 11 * 1 | ||||
| 5 > 11 > 4 > 13 > 6 * 1 | ||||
| 5 > 11 > 6 > 8 > 1 * 1 | ||||
| 5 > 11 > 8 > 4 > 3 * 1 | ||||
| 5 > 11 > 8 > 4 > 6 * 1 | ||||
| 5 > 11 > 8 > 6 > 1 > 7 > 2 > 4 > 3 > 9 > 10 > 14 > 15 > 13 * 1 | ||||
| 5 > 11 > 8 > 14 * 1 | ||||
| 5 > 11 > 15 * 1 | ||||
| 5 > 11 > 15 > 14 > 8 * 1 | ||||
| 5 > 12 > 9 > 2 > 1 > 15 > 8 > 6 > 7 > 13 > 10 > 3 > 14 > 4 * 1 | ||||
| 5 > 14 > 4 * 1 | ||||
| 5 > 14 > 4 > 6 > 15 > 8 > 11 > 12 > 3 * 1 | ||||
| 5 > 14 > 8 * 1 | ||||
| 6 > 1 * 1 | ||||
| 6 > 1 > 3 * 1 | ||||
| 6 > 1 > 7 * 1 | ||||
| 6 > 1 > 7 > 5 * 1 | ||||
| 6 > 1 > 8 > 2 > 5 > 3 > 4 > 7 > 9 > 12 > 15 > 13 > 10 > 11 * 1 | ||||
| 6 > 2 * 1 | ||||
| 6 > 2 > 3 > 5 > 4 > 8 > 1 > 7 > 13 * 1 | ||||
| 6 > 2 > 5 * 1 | ||||
| 6 > 2 > 13 > 15 * 1 | ||||
| 6 > 3 > 4 * 1 | ||||
| 6 > 3 > 5 > 9 > 10 > 4 > 7 > 8 > 11 > 13 > 15 > 14 > 2 > 12 * 1 | ||||
| 6 > 3 > 8 * 1 | ||||
| 6 > 3 > 10 > 4 * 1 | ||||
| 6 > 4 > 1 > 13 * 1 | ||||
| 6 > 4 > 2 > 7 > 11 > 13 > 14 * 1 | ||||
| 6 > 4 > 3 * 1 | ||||
| 6 > 4 > 3 > 1 > 7 > 8 > 5 > 9 > 14 > 12 > 10 > 11 * 1 | ||||
| 6 > 4 > 3 > 1 > 13 * 1 | ||||
| 6 > 4 > 3 > 8 * 1 | ||||
| 6 > 4 > 5 > 11 > 14 > 8 > 15 > 12 * 1 | ||||
| 6 > 4 > 8 > 5 > 11 > 12 > 14 > 15 * 1 | ||||
| 6 > 4 > 8 > 7 > 5 > 3 > 14 > 15 * 1 | ||||
| 6 > 4 > 8 > 11 > 5 * 1 | ||||
| 6 > 4 > 8 > 11 > 7 > 5 > 14 > 10 > 3 * 1 | ||||
| 6 > 4 > 8 > 11 > 12 > 13 > 10 > 5 > 7 > 14 > 15 > 1 > 9 > 2 * 1 | ||||
| 6 > 4 > 8 > 11 > 14 * 1 | ||||
| 6 > 4 > 8 > 12 * 1 | ||||
| 6 > 4 > 10 > 11 > 7 * 1 | ||||
| 6 > 4 > 11 > 3 > 12 > 13 * 1 | ||||
| 6 > 4 > 11 > 5 > 12 > 8 > 10 * 1 | ||||
| 6 > 4 > 11 > 5 > 12 > 14 > 15 > 13 * 1 | ||||
| 6 > 4 > 11 > 7 * 1 | ||||
| 6 > 4 > 11 > 7 > 10 > 12 > 13 > 8 > 9 > 14 * 1 | ||||
| 6 > 4 > 11 > 7 > 10 > 12 > 13 > 8 > 9 > 14 > 15 * 1 | ||||
| 6 > 4 > 11 > 10 > 5 > 12 > 13 > 14 > 15 > 7 > 9 > 8 * 1 | ||||
| 6 > 4 > 11 > 12 > 8 * 1 | ||||
| 6 > 4 > 12 > 11 > 13 * 1 | ||||
| 6 > 4 > 13 > 14 * 1 | ||||
| 6 > 4 > 14 > 11 * 1 | ||||
| 6 > 5 * 1 | ||||
| 6 > 5 > 1 > 4 > 11 > 2 * 1 | ||||
| 6 > 5 > 3 > 4 > 8 > 13 * 1 | ||||
| 6 > 5 > 3 > 8 > 13 > 9 > 14 > 10 > 7 > 1 * 1 | ||||
| 6 > 5 > 4 > 8 > 9 > 10 > 11 * 1 | ||||
| 6 > 5 > 4 > 8 > 11 * 1 | ||||
| 6 > 5 > 4 > 11 > 7 > 3 * 1 | ||||
| 6 > 5 > 4 > 11 > 7 > 10 > 12 > 13 > 8 * 1 | ||||
| 6 > 5 > 4 > 11 > 7 > 10 > 12 > 13 > 14 > 9 > 8 > 15 * 1 | ||||
| 6 > 5 > 4 > 11 > 8 > 12 * 1 | ||||
| 6 > 5 > 4 > 13 * 1 | ||||
| 6 > 5 > 8 * 1 | ||||
| 6 > 5 > 8 > 3 > 13 > 7 > 15 > 14 > 12 > 1 > 11 > 9 > 10 > 4 * 1 | ||||
| 6 > 5 > 8 > 4 * 1 | ||||
| 6 > 5 > 8 > 4 > 11 * 1 | ||||
| 6 > 5 > 8 > 9 > 3 > 2 * 1 | ||||
| 6 > 5 > 8 > 9 > 12 > 11 > 13 * 1 | ||||
| 6 > 5 > 8 > 11 > 14 * 1 | ||||
| 6 > 5 > 8 > 11 > 14 > 15 > 9 > 4 * 1 | ||||
| 6 > 5 > 11 > 2 > 10 > 3 > 7 > 13 > 4 > 1 * 1 | ||||
| 6 > 5 > 11 > 7 > 4 > 10 > 12 > 13 > 8 > 9 > 14 > 15 * 1 | ||||
| 6 > 5 > 11 > 8 > 4 * 1 | ||||
| 6 > 5 > 11 > 12 > 14 > 13 > 4 > 15 * 1 | ||||
| 6 > 7 > 2 > 9 > 10 > 15 > 1 > 3 > 12 > 14 > 13 > 4 > 11 > 5 * 1 | ||||
| 6 > 7 > 2 > 10 > 9 * 1 | ||||
| 6 > 7 > 3 > 4 * 1 | ||||
| 6 > 7 > 4 > 3 > 5 > 2 > 14 > 8 > 1 > 15 * 1 | ||||
| 6 > 7 > 10 > 3 > 12 > 2 > 1 * 1 | ||||
| 6 > 7 > 10 > 12 > 13 > 3 > 4 * 1 | ||||
| 6 > 7 > 11 > 10 > 2 > 13 > 3 > 1 > 9 * 1 | ||||
| 6 > 8 > 1 * 1 | ||||
| 6 > 8 > 4 > 3 > 11 > 14 * 1 | ||||
| 6 > 8 > 4 > 11 * 1 | ||||
| 6 > 8 > 4 > 11 > 13 * 1 | ||||
| 6 > 8 > 4 > 14 > 11 * 1 | ||||
| 6 > 8 > 4 > 14 > 12 > 5 > 11 > 15 > 9 > 7 * 1 | ||||
| 6 > 8 > 9 > 5 > 11 * 1 | ||||
| 6 > 8 > 9 > 10 * 1 | ||||
| 6 > 8 > 11 > 4 > 3 > 13 > 14 > 12 > 15 * 1 | ||||
| 6 > 8 > 12 > 11 * 1 | ||||
| 6 > 8 > 14 > 4 * 1 | ||||
| 6 > 8 > 15 * 1 | ||||
| 6 > 9 > 7 > 13 > 15 > 14 > 8 * 1 | ||||
| 6 > 9 > 10 > 3 > 4 > 1 > 7 > 11 > 13 > 8 * 1 | ||||
| 6 > 10 > 2 * 1 | ||||
| 6 > 10 > 2 > 9 * 1 | ||||
| 6 > 10 > 2 > 11 > 7 > 14 > 12 > 13 * 1 | ||||
| 6 > 10 > 14 > 8 * 1 | ||||
| 6 > 11 > 1 * 1 | ||||
| 6 > 11 > 1 > 3 > 4 > 8 > 15 * 1 | ||||
| 6 > 11 > 2 > 1 > 13 > 3 * 1 | ||||
| 6 > 11 > 2 > 13 > 15 > 8 * 1 | ||||
| 6 > 11 > 3 > 12 > 7 * 1 | ||||
| 6 > 11 > 4 * 1 | ||||
| 6 > 11 > 4 > 10 > 14 > 5 * 1 | ||||
| 6 > 11 > 4 > 14 > 12 * 1 | ||||
| 6 > 11 > 5 * 1 | ||||
| 6 > 11 > 5 > 3 > 14 * 1 | ||||
| 6 > 11 > 5 > 15 * 1 | ||||
| 6 > 11 > 8 > 12 > 5 > 4 > 1 > 13 > 14 * 1 | ||||
| 6 > 11 > 13 > 4 > 14 > 8 * 1 | ||||
| 6 > 12 > 8 * 1 | ||||
| 6 > 12 > 8 > 3 * 1 | ||||
| 6 > 12 > 8 > 4 > 11 > 14 * 1 | ||||
| 6 > 13 * 1 | ||||
| 6 > 13 > 4 > 3 > 5 * 1 | ||||
| 6 > 13 > 5 * 1 | ||||
| 6 > 13 > 12 > 11 * 1 | ||||
| 6 > 13 > 12 > 11 > 9 > 15 * 1 | ||||
| 6 > 14 * 1 | ||||
| 6 > 14 > 8 * 1 | ||||
| 6 > 14 > 9 * 1 | ||||
| 6 > 14 > 11 * 1 | ||||
| 6 > 14 > 11 > 1 * 1 | ||||
| 6 > 14 > 12 > 10 > 8 > 9 > 11 > 1 > 4 > 13 * 1 | ||||
| 7 > 1 > 2 * 1 | ||||
| 7 > 1 > 2 > 3 > 10 > 9 * 1 | ||||
| 7 > 1 > 2 > 8 > 3 * 1 | ||||
| 7 > 1 > 3 > 2 > 6 > 13 > 15 * 1 | ||||
| 7 > 1 > 3 > 15 > 13 > 6 > 2 * 1 | ||||
| 7 > 1 > 4 * 1 | ||||
| 7 > 2 * 1 | ||||
| 7 > 2 > 3 > 1 > 13 > 15 * 1 | ||||
| 7 > 2 > 6 * 1 | ||||
| 7 > 2 > 8 > 10 * 1 | ||||
| 7 > 2 > 9 * 1 | ||||
| 7 > 2 > 13 * 1 | ||||
| 7 > 4 > 2 * 1 | ||||
| 7 > 4 > 3 > 1 > 6 > 13 > 14 > 15 > 5 > 2 > 8 > 9 > 10 > 12 * 1 | ||||
| 7 > 4 > 6 > 10 > 15 > 14 > 11 > 13 > 8 > 3 > 12 > 1 > 5 > 2 * 1 | ||||
| 7 > 4 > 8 > 6 > 5 * 1 | ||||
| 7 > 5 > 8 * 1 | ||||
| 7 > 5 > 12 * 1 | ||||
| 7 > 6 * 1 | ||||
| 7 > 6 > 1 * 1 | ||||
| 7 > 6 > 2 > 5 > 4 > 8 > 14 * 1 | ||||
| 7 > 6 > 10 > 11 > 1 * 1 | ||||
| 7 > 6 > 12 > 8 * 1 | ||||
| 7 > 8 > 2 > 13 > 6 > 3 * 1 | ||||
| 7 > 8 > 5 * 1 | ||||
| 7 > 8 > 12 * 1 | ||||
| 7 > 8 > 15 * 1 | ||||
| 7 > 9 > 10 * 1 | ||||
| 7 > 10 > 4 > 2 > 13 > 3 * 1 | ||||
| 7 > 10 > 6 > 4 * 1 | ||||
| 7 > 10 > 8 * 1 | ||||
| 7 > 11 > 12 > 3 > 4 > 6 > 5 > 8 * 1 | ||||
| 7 > 11 > 14 * 1 | ||||
| 7 > 13 > 6 * 1 | ||||
| 7 > 15 > 1 > 3 > 13 * 1 | ||||
| 7 > 15 > 3 * 1 | ||||
| 7 > 15 > 3 > 1 > 2 * 1 | ||||
| 7 > 15 > 5 * 1 | ||||
| 8 > 1 * 1 | ||||
| 8 > 1 > 3 * 1 | ||||
| 8 > 1 > 4 * 1 | ||||
| 8 > 1 > 6 * 1 | ||||
| 8 > 2 > 3 > 6 * 1 | ||||
| 8 > 2 > 7 > 10 > 9 * 1 | ||||
| 8 > 3 > 7 * 1 | ||||
| 8 > 3 > 10 > 13 > 1 * 1 | ||||
| 8 > 3 > 12 * 1 | ||||
| 8 > 3 > 13 > 10 * 1 | ||||
| 8 > 4 * 1 | ||||
| 8 > 4 > 3 > 6 > 1 > 2 > 5 > 7 > 13 > 11 > 10 > 15 > 14 > 9 * 1 | ||||
| 8 > 4 > 5 * 1 | ||||
| 8 > 4 > 5 > 11 > 14 * 1 | ||||
| 8 > 4 > 6 > 14 > 15 * 1 | ||||
| 8 > 4 > 11 * 1 | ||||
| 8 > 4 > 11 > 5 > 6 > 12 > 14 > 15 > 9 * 1 | ||||
| 8 > 4 > 12 > 14 > 15 > 3 > 6 > 5 > 1 > 2 > 7 > 9 > 10 > 11 * 1 | ||||
| 8 > 5 > 2 > 6 > 7 > 9 > 10 * 1 | ||||
| 8 > 5 > 3 > 4 > 9 > 11 > 14 * 1 | ||||
| 8 > 5 > 4 * 1 | ||||
| 8 > 5 > 4 > 6 > 1 > 11 > 14 > 7 > 10 > 3 > 9 > 15 * 1 | ||||
| 8 > 5 > 4 > 11 > 6 * 1 | ||||
| 8 > 5 > 6 * 1 | ||||
| 8 > 5 > 6 > 4 > 11 > 12 > 10 > 13 * 1 | ||||
| 8 > 5 > 9 > 3 > 11 * 1 | ||||
| 8 > 5 > 9 > 6 > 4 > 14 * 1 | ||||
| 8 > 5 > 11 > 4 > 12 > 14 * 1 | ||||
| 8 > 5 > 11 > 9 > 6 * 1 | ||||
| 8 > 5 > 11 > 12 > 6 > 4 * 1 | ||||
| 8 > 5 > 11 > 13 > 6 > 1 * 1 | ||||
| 8 > 5 > 11 > 14 * 1 | ||||
| 8 > 5 > 12 * 1 | ||||
| 8 > 5 > 12 > 4 > 6 * 1 | ||||
| 8 > 5 > 14 * 1 | ||||
| 8 > 6 > 1 * 1 | ||||
| 8 > 6 > 2 * 1 | ||||
| 8 > 6 > 3 > 11 > 14 * 1 | ||||
| 8 > 6 > 4 * 1 | ||||
| 8 > 6 > 4 > 3 * 1 | ||||
| 8 > 6 > 4 > 11 > 5 > 1 > 15 > 14 > 13 > 12 > 10 > 9 > 2 > 3 * 1 | ||||
| 8 > 6 > 5 > 9 * 1 | ||||
| 8 > 6 > 5 > 14 * 1 | ||||
| 8 > 6 > 5 > 14 > 11 > 12 > 15 * 1 | ||||
| 8 > 6 > 11 > 9 * 1 | ||||
| 8 > 6 > 11 > 14 * 1 | ||||
| 8 > 6 > 14 > 3 > 4 * 1 | ||||
| 8 > 6 > 14 > 12 * 1 | ||||
| 8 > 6 > 15 * 1 | ||||
| 8 > 7 > 1 * 1 | ||||
| 8 > 7 > 2 > 1 > 15 > 11 * 1 | ||||
| 8 > 7 > 6 * 1 | ||||
| 8 > 7 > 11 * 1 | ||||
| 8 > 9 > 5 * 1 | ||||
| 8 > 9 > 5 > 11 * 1 | ||||
| 8 > 9 > 11 * 1 | ||||
| 8 > 9 > 11 > 5 * 1 | ||||
| 8 > 9 > 11 > 5 > 6 * 1 | ||||
| 8 > 10 > 12 * 1 | ||||
| 8 > 11 > 2 * 1 | ||||
| 8 > 11 > 3 > 14 * 1 | ||||
| 8 > 11 > 4 * 1 | ||||
| 8 > 11 > 4 > 14 > 5 > 9 > 3 * 1 | ||||
| 8 > 11 > 5 > 4 > 6 > 9 > 14 > 15 * 1 | ||||
| 8 > 11 > 5 > 6 * 1 | ||||
| 8 > 11 > 5 > 9 > 6 * 1 | ||||
| 8 > 11 > 6 * 1 | ||||
| 8 > 11 > 6 > 3 > 4 > 14 * 1 | ||||
| 8 > 11 > 6 > 5 > 14 > 3 > 15 > 12 * 1 | ||||
| 8 > 11 > 6 > 12 * 1 | ||||
| 8 > 11 > 6 > 14 * 1 | ||||
| 8 > 11 > 10 * 1 | ||||
| 8 > 11 > 10 > 4 * 1 | ||||
| 8 > 11 > 14 > 12 * 1 | ||||
| 8 > 12 > 3 * 1 | ||||
| 8 > 12 > 6 * 1 | ||||
| 8 > 12 > 15 > 6 > 14 * 1 | ||||
| 8 > 13 > 15 * 1 | ||||
| 8 > 14 > 4 * 1 | ||||
| 8 > 14 > 10 * 1 | ||||
| 8 > 14 > 11 * 1 | ||||
| 8 > 14 > 13 > 6 * 1 | ||||
| 9 > 6 > 12 * 1 | ||||
| 9 > 6 > 12 > 14 > 8 * 1 | ||||
| 9 > 8 > 6 > 4 * 1 | ||||
| 9 > 8 > 11 > 13 > 3 > 4 > 10 * 1 | ||||
| 9 > 8 > 11 > 14 > 3 > 4 * 1 | ||||
| 9 > 8 > 11 > 14 > 3 > 4 > 10 * 1 | ||||
| 9 > 8 > 12 * 1 | ||||
| 9 > 10 > 2 * 1 | ||||
| 9 > 10 > 8 > 2 * 1 | ||||
| 9 > 12 > 6 * 1 | ||||
| 9 > 12 > 6 > 8 > 2 * 1 | ||||
| 9 > 14 > 8 * 1 | ||||
| 10 > 2 > 6 * 1 | ||||
| 10 > 2 > 7 > 9 * 1 | ||||
| 10 > 5 > 6 * 1 | ||||
| 10 > 6 > 4 > 11 > 12 * 1 | ||||
| 10 > 7 > 2 * 1 | ||||
| 10 > 8 > 7 * 1 | ||||
| 10 > 9 > 11 * 1 | ||||
| 10 > 11 > 14 * 1 | ||||
| 10 > 14 > 4 > 5 > 7 > 6 > 8 > 11 * 1 | ||||
| 10 > 14 > 6 * 1 | ||||
| 10 > 14 > 6 > 11 > 4 * 1 | ||||
| 10 > 14 > 7 > 6 > 4 > 12 > 11 * 1 | ||||
| 10 > 14 > 15 > 11 > 9 > 4 > 5 > 6 > 7 > 12 > 13 * 1 | ||||
| 11 > 1 > 4 > 2 * 1 | ||||
| 11 > 1 > 4 > 7 > 5 > 8 > 9 > 12 > 10 > 3 > 2 > 14 > 15 > 6 * 1 | ||||
| 11 > 2 > 6 * 1 | ||||
| 11 > 3 > 5 * 1 | ||||
| 11 > 3 > 8 * 1 | ||||
| 11 > 4 > 1 * 1 | ||||
| 11 > 4 > 5 > 6 > 14 > 3 > 12 * 1 | ||||
| 11 > 4 > 5 > 12 * 1 | ||||
| 11 > 4 > 6 > 1 > 7 > 10 > 5 > 14 * 1 | ||||
| 11 > 4 > 6 > 3 > 14 * 1 | ||||
| 11 > 4 > 6 > 8 * 1 | ||||
| 11 > 4 > 13 > 14 > 12 * 1 | ||||
| 11 > 4 > 14 > 6 * 1 | ||||
| 11 > 5 * 1 | ||||
| 11 > 5 > 4 > 6 * 1 | ||||
| 11 > 5 > 6 > 4 > 8 > 3 > 13 > 14 * 1 | ||||
| 11 > 5 > 6 > 8 * 1 | ||||
| 11 > 5 > 8 * 1 | ||||
| 11 > 5 > 8 > 3 > 4 > 14 * 1 | ||||
| 11 > 5 > 12 > 14 * 1 | ||||
| 11 > 5 > 14 > 4 * 1 | ||||
| 11 > 6 * 1 | ||||
| 11 > 6 > 4 > 3 * 1 | ||||
| 11 > 6 > 4 > 8 * 1 | ||||
| 11 > 6 > 5 > 14 * 1 | ||||
| 11 > 6 > 7 > 14 * 1 | ||||
| 11 > 6 > 8 * 1 | ||||
| 11 > 6 > 8 > 3 > 4 > 5 > 13 > 14 * 1 | ||||
| 11 > 6 > 8 > 3 > 4 > 13 > 14 > 15 > 12 > 7 > 2 > 1 > 9 > 10 * 1 | ||||
| 11 > 6 > 10 * 1 | ||||
| 11 > 6 > 12 > 14 > 1 > 3 > 4 > 7 > 13 * 1 | ||||
| 11 > 6 > 13 > 12 > 8 > 14 > 1 * 1 | ||||
| 11 > 6 > 14 * 1 | ||||
| 11 > 7 > 15 * 1 | ||||
| 11 > 8 > 2 * 1 | ||||
| 11 > 8 > 3 > 5 > 4 > 6 * 1 | ||||
| 11 > 8 > 4 > 3 > 5 * 1 | ||||
| 11 > 8 > 5 * 1 | ||||
| 11 > 8 > 5 > 6 * 1 | ||||
| 11 > 8 > 5 > 6 > 14 > 12 > 1 > 4 * 1 | ||||
| 11 > 8 > 6 > 10 > 12 * 1 | ||||
| 11 > 8 > 10 * 1 | ||||
| 11 > 8 > 12 * 1 | ||||
| 11 > 8 > 15 > 14 > 3 > 6 * 1 | ||||
| 11 > 9 * 1 | ||||
| 11 > 9 > 7 > 4 > 3 > 14 > 12 * 1 | ||||
| 11 > 10 > 7 > 2 > 6 > 14 > 15 > 12 > 13 > 9 > 1 > 8 > 5 > 3 * 1 | ||||
| 11 > 12 > 5 > 6 > 15 * 1 | ||||
| 11 > 12 > 6 * 1 | ||||
| 11 > 12 > 8 > 6 * 1 | ||||
| 11 > 13 > 12 > 2 > 14 * 1 | ||||
| 11 > 13 > 12 > 6 > 14 * 1 | ||||
| 11 > 14 * 1 | ||||
| 11 > 14 > 4 * 1 | ||||
| 11 > 14 > 5 * 1 | ||||
| 11 > 14 > 6 > 5 > 8 * 1 | ||||
| 11 > 14 > 6 > 5 > 8 > 4 * 1 | ||||
| 11 > 14 > 6 > 12 * 1 | ||||
| 11 > 14 > 8 * 1 | ||||
| 11 > 14 > 8 > 6 > 3 > 10 > 12 * 1 | ||||
| 11 > 14 > 9 * 1 | ||||
| 11 > 15 > 6 * 1 | ||||
| 11 > 15 > 14 > 12 > 8 > 6 * 1 | ||||
| 12 > 2 > 6 * 1 | ||||
| 12 > 3 > 4 > 6 > 13 * 1 | ||||
| 12 > 3 > 8 * 1 | ||||
| 12 > 4 > 13 * 1 | ||||
| 12 > 5 > 8 > 4 * 1 | ||||
| 12 > 6 * 1 | ||||
| 12 > 6 > 1 * 1 | ||||
| 12 > 6 > 1 > 8 * 1 | ||||
| 12 > 6 > 3 * 1 | ||||
| 12 > 6 > 5 > 4 > 11 > 7 > 10 > 13 > 8 > 9 > 14 > 15 * 1 | ||||
| 12 > 6 > 8 * 1 | ||||
| 12 > 8 > 4 * 1 | ||||
| 12 > 8 > 6 * 1 | ||||
| 12 > 8 > 6 > 11 > 5 * 1 | ||||
| 12 > 8 > 13 * 1 | ||||
| 12 > 9 * 1 | ||||
| 12 > 9 > 8 * 1 | ||||
| 12 > 11 > 5 * 1 | ||||
| 12 > 11 > 8 > 6 > 4 * 1 | ||||
| 12 > 14 > 6 * 1 | ||||
| 13 > 1 > 3 > 7 > 15 > 2 * 1 | ||||
| 13 > 1 > 6 * 1 | ||||
| 13 > 3 > 7 > 15 > 1 > 9 * 1 | ||||
| 13 > 6 * 1 | ||||
| 13 > 6 > 3 * 1 | ||||
| 13 > 7 > 6 * 1 | ||||
| 13 > 8 * 1 | ||||
| 13 > 8 > 4 > 6 > 1 * 1 | ||||
| 13 > 11 > 1 > 6 > 8 > 5 > 10 * 1 | ||||
| 13 > 11 > 6 > 5 * 1 | ||||
| 13 > 11 > 6 > 15 * 1 | ||||
| 13 > 12 > 4 * 1 | ||||
| 14 > 1 > 11 > 6 > 8 > 10 * 1 | ||||
| 14 > 3 > 8 > 10 > 13 * 1 | ||||
| 14 > 4 * 1 | ||||
| 14 > 4 > 5 * 1 | ||||
| 14 > 5 > 11 * 1 | ||||
| 14 > 6 * 1 | ||||
| 14 > 6 > 4 > 3 * 1 | ||||
| 14 > 6 > 4 > 11 > 10 > 5 > 8 > 1 * 1 | ||||
| 14 > 6 > 7 * 1 | ||||
| 14 > 6 > 8 > 5 > 4 > 11 > 3 > 12 * 1 | ||||
| 14 > 6 > 8 > 11 > 4 > 3 * 1 | ||||
| 14 > 6 > 11 * 1 | ||||
| 14 > 6 > 11 > 15 > 8 > 7 > 4 > 12 > 5 > 13 > 9 * 1 | ||||
| 14 > 8 > 6 > 2 * 1 | ||||
| 14 > 8 > 10 * 1 | ||||
| 14 > 8 > 10 > 6 * 1 | ||||
| 14 > 8 > 11 > 3 > 4 > 6 * 1 | ||||
| 14 > 10 > 8 * 1 | ||||
| 14 > 10 > 11 * 1 | ||||
| 14 > 11 > 4 * 1 | ||||
| 14 > 11 > 4 > 5 > 8 * 1 | ||||
| 14 > 11 > 4 > 5 > 13 * 1 | ||||
| 14 > 11 > 4 > 7 > 2 * 1 | ||||
| 14 > 11 > 8 > 12 * 1 | ||||
| 14 > 11 > 12 * 1 | ||||
| 14 > 12 * 1 | ||||
| 14 > 12 > 8 > 6 > 1 > 10 * 1 | ||||
| 14 > 12 > 8 > 11 > 10 * 1 | ||||
| 14 > 12 > 15 > 4 > 8 > 7 > 13 > 10 > 1 > 3 > 6 > 5 > 2 > 11 * 1 | ||||
| 14 > 15 > 6 > 11 > 10 > 5 * 1 | ||||
| 15 > 3 > 2 > 1 * 1 | ||||
| 15 > 3 > 13 > 7 > 10 * 1 | ||||
| 15 > 6 > 7 * 1 | ||||
| 15 > 7 * 1 | ||||
| 15 > 7 > 2 * 1 | ||||
| 15 > 7 > 2 > 11 > 10 * 1 | ||||
| 15 > 7 > 14 * 1 | ||||
| 15 > 8 > 6 * 1 | ||||
| 15 > 11 * 1 | ||||
| 15 > 11 > 4 > 2 > 6 > 8 > 14 > 13 > 12 > 1 > 3 > 5 > 7 > 10 * 1 | ||||
| 15 > 12 > 6 * 1 | ||||
| 15 > 12 > 11 * 1 | ||||
| 15 > 14 * 1 | ||||
| 15 > 14 > 11 > 3 > 4 * 1 | ||||
| @@ -0,0 +1,47 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\HighestAverage; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use CondorcetPHP\Condorcet\Tools\Converters\CondorcetElectionFormat; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class JeffersonTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     # https://fr.wikipedia.org/wiki/Scrutin_proportionnel_plurinominal#M%C3%A9thode_de_Jefferson_ou_m%C3%A9thode_D'Hondt | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(6); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes('A * 42; B ^31; C *15; D ^12'); // Mix weight and number | ||||
|  | ||||
|         self::assertSame(['A' =>3, 'B' => 2, 'C' => 1, 'D' => 0], $this->election->getResult('Jefferson')->getStats()['Seats per Candidates']); | ||||
|     } | ||||
|  | ||||
|     public function testResult_Tideman_A03(): void | ||||
|     { | ||||
|         $cef = new CondorcetElectionFormat(__DIR__.'/'.'A03.cvotes'); | ||||
|         $cef->setDataToAnElection($this->election); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); // Empty ranking was throw an error. | ||||
|  | ||||
|         $this->election->getResult('Jefferson'); | ||||
|  | ||||
|         self::assertTrue(true); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,161 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\HighestAverage; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class SainteLagueTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     protected function tearDown(): void | ||||
|     { | ||||
|         $this->election->setMethodOption('SainteLague', 'FirstDivisor', 1); | ||||
|     } | ||||
|  | ||||
|     # https://fr.wikipedia.org/wiki/Scrutin_proportionnel_plurinominal#M%C3%A9thode_de_Sainte-Lagu%C3%AB | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(7); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes('A ^53; B ^24; C ^23'); | ||||
|  | ||||
|         self::assertSame('A > B > C > A > A > B > C', $this->election->getResult('SainteLague')->getResultAsString()); | ||||
|  | ||||
|         self::assertSame([ | ||||
|             'Rounds' => [ | ||||
|                 1 => [ | ||||
|                     'A' => [ | ||||
|                         'Quotient' => 53.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 0, | ||||
|                     ], | ||||
|                     'B' => [ | ||||
|                         'Quotient' => 24.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 0, | ||||
|                     ], | ||||
|                     'C' => [ | ||||
|                         'Quotient' => 23.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 0, | ||||
|                     ], | ||||
|                 ], | ||||
|                 2 => [ | ||||
|                     'A' => [ | ||||
|                         'Quotient' => 17.666666666666668, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                     'B' => [ | ||||
|                         'Quotient' => 24.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 0, | ||||
|                     ], | ||||
|                     'C' => [ | ||||
|                         'Quotient' => 23.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 0, | ||||
|                     ], | ||||
|                 ], | ||||
|                 3 => [ | ||||
|                     'A' => [ | ||||
|                         'Quotient' => 17.666666666666668, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                     'B' => [ | ||||
|                         'Quotient' => 8.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                     'C' => [ | ||||
|                         'Quotient' => 23.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 0, | ||||
|                     ], | ||||
|                 ], | ||||
|                 4 => [ | ||||
|                     'A' => [ | ||||
|                         'Quotient' => 17.666666666666668, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                     'B' => [ | ||||
|                         'Quotient' => 8.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                     'C' => [ | ||||
|                         'Quotient' => 7.666666666666667, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                 ], | ||||
|                 5 => [ | ||||
|                     'A' => [ | ||||
|                         'Quotient' => 10.6, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 2, | ||||
|                     ], | ||||
|                     'B' => [ | ||||
|                         'Quotient' => 8.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                     'C' => [ | ||||
|                         'Quotient' => 7.666666666666667, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                 ], | ||||
|                 6 => [ | ||||
|                     'A' => [ | ||||
|                         'Quotient' => 7.571428571428571, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 3, | ||||
|                     ], | ||||
|                     'B' => [ | ||||
|                         'Quotient' => 8.0, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                     'C' => [ | ||||
|                         'Quotient' => 7.666666666666667, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                 ], | ||||
|                 7 => [ | ||||
|                     'A' => [ | ||||
|                         'Quotient' => 7.571428571428571, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 3, | ||||
|                     ], | ||||
|                     'B' => [ | ||||
|                         'Quotient' => 4.8, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 2, | ||||
|                     ], | ||||
|                     'C' => [ | ||||
|                         'Quotient' => 7.666666666666667, | ||||
|                         'NumberOfSeatsAllocatedBeforeRound' => 1, | ||||
|                     ], | ||||
|                 ], | ||||
|             ], | ||||
|             'Seats per Candidates' => [ | ||||
|                 'A' => 3, | ||||
|                 'B' => 2, | ||||
|                 'C' => 2, | ||||
|             ], | ||||
|         ], $this->election->getResult('SainteLague')->getStats()); | ||||
|     } | ||||
|  | ||||
|     # https://www.regjeringen.no/no/tema/valg-og-demokrati/den-norske-valgordningen/valgordningen/id456636/ | ||||
|     public function testNorwegianVariant_1(): void | ||||
|     { | ||||
|         $this->election->setMethodOption('SainteLague', 'FirstDivisor', 1.4); | ||||
|  | ||||
|         $this->election->parseCandidates('H;Ap;FrP;SV;SP;KrF'); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(11); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes('H ^81140; Ap ^80862; FrP ^39851; SV ^26295; SP ^12187; KrF ^11229'); | ||||
|  | ||||
|         self::assertSame('H > Ap > FrP > H > Ap > SV > H > Ap > FrP > H > Ap', $this->election->getResult('SainteLague')->getResultAsString()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,259 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\HighestAverage; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Algo\Tools\StvQuotas; | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class HighestAveragesAndLargestRemainderMethodsTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     protected function tearDown(): void | ||||
|     { | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::HARE); | ||||
|     } | ||||
|  | ||||
|     public function testFranceLegislatives2022_1erTour(): void | ||||
|     { | ||||
|         $this->election->setNumberOfSeats(577); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseCandidates( | ||||
|             'Divers extrême gauche | ||||
|             Parti radical de gauche | ||||
|             Nouvelle union populaire écologique et sociale | ||||
|             Divers gauche | ||||
|             Ecologistes | ||||
|             Divers | ||||
|             Régionaliste | ||||
|             Ensemble ! (Majorité présidentielle) | ||||
|             Divers centre | ||||
|             Union des Démocrates et des Indépendants | ||||
|             Les Républicains | ||||
|             Divers droite | ||||
|             Droite souverainiste | ||||
|             Reconquête ! | ||||
|             Rassemblement National | ||||
|             Divers extrême droite' | ||||
|         ); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Divers extrême gauche	^266412 | ||||
|             Parti radical de gauche	^126689 | ||||
|             Nouvelle union populaire écologique et sociale	^5836079 | ||||
|             Divers gauche	^713574 | ||||
|             Ecologistes	^608314 | ||||
|             Divers	^192624 | ||||
|             Régionaliste	^291384 | ||||
|             Ensemble ! (Majorité présidentielle)	^5857364 | ||||
|             Divers centre	^283612 | ||||
|             Union des Démocrates et des Indépendants	^198062 | ||||
|             Les Républicains	^2370440 | ||||
|             Divers droite	^530782 | ||||
|             Droite souverainiste	^249603 | ||||
|             Reconquête !	^964775 | ||||
|             Rassemblement National	^4248537 | ||||
|             Divers extrême droite	^6457 | ||||
|         '); | ||||
|  | ||||
|         // SainteLeague | ||||
|         self::assertSame([ | ||||
|             'Divers extrême gauche' => 7, | ||||
|             'Parti radical de gauche' => 3, | ||||
|             'Nouvelle union populaire écologique et sociale' => 148, | ||||
|             'Divers gauche' => 18, | ||||
|             'Ecologistes' => 15, | ||||
|             'Divers' => 5, | ||||
|             'Régionaliste' => 7, | ||||
|             'Ensemble ! (Majorité présidentielle)' => 149, | ||||
|             'Divers centre' => 7, | ||||
|             'Union des Démocrates et des Indépendants' => 5, | ||||
|             'Les Républicains' => 60, | ||||
|             'Divers droite' => 14, | ||||
|             'Droite souverainiste' => 6, | ||||
|             'Reconquête !' => 25, | ||||
|             'Rassemblement National' => 108, | ||||
|             'Divers extrême droite' => 0, | ||||
|         ], $this->election->getResult('SainteLague')->getStats()['Seats per Candidates']); | ||||
|  | ||||
|         $this->assertSame(577, array_sum($this->election->getResult('SainteLague')->getStats()['Seats per Candidates'])); | ||||
|         $this->assertCount(577, $this->election->getResult('SainteLague')->getResultAsArray()); | ||||
|  | ||||
|         // Jefferson | ||||
|         self::assertSame([ | ||||
|             'Divers extrême gauche' => 6, | ||||
|             'Parti radical de gauche' => 3, | ||||
|             'Nouvelle union populaire écologique et sociale' => 150, | ||||
|             'Divers gauche' => 18, | ||||
|             'Ecologistes' => 15, | ||||
|             'Divers' => 4, | ||||
|             'Régionaliste' => 7, | ||||
|             'Ensemble ! (Majorité présidentielle)' => 150, | ||||
|             'Divers centre' => 7, | ||||
|             'Union des Démocrates et des Indépendants' => 5, | ||||
|             'Les Républicains' => 60, | ||||
|             'Divers droite' => 13, | ||||
|             'Droite souverainiste' => 6, | ||||
|             'Reconquête !' => 24, | ||||
|             'Rassemblement National' => 109, | ||||
|             'Divers extrême droite' => 0, | ||||
|         ], $this->election->getResult('Jefferson')->getStats()['Seats per Candidates']); | ||||
|  | ||||
|         $this->assertSame(577, array_sum($this->election->getResult('Jefferson')->getStats()['Seats per Candidates'])); | ||||
|         $this->assertCount(577, $this->election->getResult('Jefferson')->getResultAsArray()); | ||||
|  | ||||
|         // Hare-LR | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::HARE); // Hare-LR | ||||
|         self::assertSame([ | ||||
|             'Divers extrême gauche' => 7, | ||||
|             'Parti radical de gauche' => 3, | ||||
|             'Nouvelle union populaire écologique et sociale' => 148, | ||||
|             'Divers gauche' => 18, | ||||
|             'Ecologistes' => 15, | ||||
|             'Divers' => 5, | ||||
|             'Régionaliste' => 7, | ||||
|             'Ensemble ! (Majorité présidentielle)' => 149, | ||||
|             'Divers centre' => 7, | ||||
|             'Union des Démocrates et des Indépendants' => 5, | ||||
|             'Les Républicains' => 60, | ||||
|             'Divers droite' => 14, | ||||
|             'Droite souverainiste' => 6, | ||||
|             'Reconquête !' => 25, | ||||
|             'Rassemblement National' => 108, | ||||
|             'Divers extrême droite' => 0, | ||||
|         ], $this->election->getResult('LargestRemainder')->getStats()['Seats per Candidates']); | ||||
|  | ||||
|         $this->assertSame(577, array_sum($this->election->getResult('LargestRemainder')->getStats()['Seats per Candidates'])); | ||||
|         $this->assertCount(577, $this->election->getResult('LargestRemainder')->getResultAsArray()); | ||||
|  | ||||
|         // Droop-LR | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::DROOP); // Droop-LR | ||||
|         self::assertSame([ | ||||
|             'Divers extrême gauche' => 7, | ||||
|             'Parti radical de gauche' => 3, | ||||
|             'Nouvelle union populaire écologique et sociale' => 148, | ||||
|             'Divers gauche' => 18, | ||||
|             'Ecologistes' => 15, | ||||
|             'Divers' => 5, | ||||
|             'Régionaliste' => 7, | ||||
|             'Ensemble ! (Majorité présidentielle)' => 149, | ||||
|             'Divers centre' => 7, | ||||
|             'Union des Démocrates et des Indépendants' => 5, | ||||
|             'Les Républicains' => 60, | ||||
|             'Divers droite' => 14, | ||||
|             'Droite souverainiste' => 6, | ||||
|             'Reconquête !' => 25, | ||||
|             'Rassemblement National' => 108, | ||||
|             'Divers extrême droite' => 0, | ||||
|         ], $this->election->getResult('LargestRemainder')->getStats()['Seats per Candidates']); | ||||
|  | ||||
|         $this->assertSame(577, array_sum($this->election->getResult('LargestRemainder')->getStats()['Seats per Candidates'])); | ||||
|         $this->assertCount(577, $this->election->getResult('LargestRemainder')->getResultAsArray()); | ||||
|  | ||||
|         //  Hagenbach-Bischoff-LR | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::HAGENBACH_BISCHOFF); //  Hagenbach-Bischoff-LR | ||||
|         self::assertSame([ | ||||
|             'Divers extrême gauche' => 7, | ||||
|             'Parti radical de gauche' => 3, | ||||
|             'Nouvelle union populaire écologique et sociale' => 148, | ||||
|             'Divers gauche' => 18, | ||||
|             'Ecologistes' => 15, | ||||
|             'Divers' => 5, | ||||
|             'Régionaliste' => 7, | ||||
|             'Ensemble ! (Majorité présidentielle)' => 149, | ||||
|             'Divers centre' => 7, | ||||
|             'Union des Démocrates et des Indépendants' => 5, | ||||
|             'Les Républicains' => 60, | ||||
|             'Divers droite' => 14, | ||||
|             'Droite souverainiste' => 6, | ||||
|             'Reconquête !' => 25, | ||||
|             'Rassemblement National' => 108, | ||||
|             'Divers extrême droite' => 0, | ||||
|         ], $this->election->getResult('LargestRemainder')->getStats()['Seats per Candidates']); | ||||
|  | ||||
|         $this->assertSame(577, array_sum($this->election->getResult('LargestRemainder')->getStats()['Seats per Candidates'])); | ||||
|         $this->assertCount(577, $this->election->getResult('LargestRemainder')->getResultAsArray()); | ||||
|  | ||||
|         //  Imperiali-LR | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::IMPERIALI); //  Imperiali-LR | ||||
|         self::assertSame([ | ||||
|             'Divers extrême gauche' => 7, | ||||
|             'Parti radical de gauche' => 3, | ||||
|             'Nouvelle union populaire écologique et sociale' => 149, | ||||
|             'Divers gauche' => 18, | ||||
|             'Ecologistes' => 15, | ||||
|             'Divers' => 5, | ||||
|             'Régionaliste' => 7, | ||||
|             'Ensemble ! (Majorité présidentielle)' => 149, | ||||
|             'Divers centre' => 7, | ||||
|             'Union des Démocrates et des Indépendants' => 5, | ||||
|             'Les Républicains' => 60, | ||||
|             'Divers droite' => 13, | ||||
|             'Droite souverainiste' => 6, | ||||
|             'Reconquête !' => 25, | ||||
|             'Rassemblement National' => 108, | ||||
|             'Divers extrême droite' => 0, | ||||
|         ], $this->election->getResult('LargestRemainder')->getStats()['Seats per Candidates']); | ||||
|  | ||||
|         $this->assertSame(577, array_sum($this->election->getResult('LargestRemainder')->getStats()['Seats per Candidates'])); | ||||
|         $this->assertCount(577, $this->election->getResult('LargestRemainder')->getResultAsArray()); | ||||
|     } | ||||
|  | ||||
|     # https://www.electoral-reform.org.uk/what-is-the-difference-between-dhondt-sainte-lague-and-hare/ | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         $this->election->parseCandidates('Con;Lab;LD;Brexit;Ash Ind;Green;Others'); | ||||
|         $this->election->setNumberOfSeats(11); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes('Con ^258794; Lab ^204011; LD ^33604; Brexit ^15728; Ash Ind ^13498; Green ^10375; Others ^9743'); | ||||
|  | ||||
|         self::assertSame('Con > Lab > Con > Lab > Con > Lab > Con > LD > Lab > Con > Con', $this->election->getResult('SainteLague')->getResultAsString()); | ||||
|         self::assertSame('Con > Lab > Con > Lab > Con > Lab > Con > Con > Lab > Con > Lab', $this->election->getResult('Jefferson')->getResultAsString()); | ||||
|  | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::HARE); // Hare-LR | ||||
|         self::assertSame('Con > Con > Lab > Con > Lab > Con > Lab > Con > Lab > LD > Brexit', $this->election->getResult('LargestRemainder')->getResultAsString()); | ||||
|     } | ||||
|  | ||||
|     # https://en.wikipedia.org/wiki/Webster/Sainte-Lagu%C3%AB_method | ||||
|     # https://en.wikipedia.org/wiki/D%27Hondt_method | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(8); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes('A ^100000; B ^80000; C ^30000; D ^20000'); | ||||
|  | ||||
|         self::assertSame('A > B > A > C > B > A > D > B', $this->election->getResult('SainteLague')->getResultAsString()); | ||||
|         self::assertSame('A > B > A > B > A > C > B > A', $this->election->getResult('Jefferson')->getResultAsString()); | ||||
|     } | ||||
|  | ||||
|     public function testTiesOnFirstRank(): void | ||||
|     { | ||||
|         $this->election->setNumberOfSeats(1); | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->addVote('A = B > C'); | ||||
|         self::assertSame([], $this->election->getResult('SainteLague')->getResultAsArray()); | ||||
|  | ||||
|         $this->election->addVote('B>A'); | ||||
|         self::assertSame('B', $this->election->getResult('SainteLague')->getResultAsString()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,185 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\InstantRunoff; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Election, Vote}; | ||||
| use CondorcetPHP\Condorcet\Tools\Converters\DavidHillFormat; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class InstantRunoffTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         # From https://fr.wikipedia.org/wiki/Vote_alternatif | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 42 | ||||
|             B>C>D>A * 26 | ||||
|             C>D>B>A * 15 | ||||
|             D>C>B>A * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'D', | ||||
|                 2 => 'A', | ||||
|                 3 => 'B', | ||||
|                 4 => 'C', ], | ||||
|             $this->election->getResult('InstantRunoff')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'majority' => 50.0, | ||||
|                 'rounds' => [ | ||||
|                     1 => [ | ||||
|                         'A' => 42, | ||||
|                         'B' => 26, | ||||
|                         'C' => 15, | ||||
|                         'D' => 17, | ||||
|                     ], | ||||
|                     2 => [ | ||||
|                         'A' => 42, | ||||
|                         'B' => 26, | ||||
|                         'D' => 32, | ||||
|                     ], | ||||
|                     3 => [ | ||||
|                         'A' => 42, | ||||
|                         'D' => 58, | ||||
|                     ], | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('InstantRunoff')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Instant-runoff_voting#Examples | ||||
|  | ||||
|         $this->election->addCandidate('bob'); | ||||
|         $this->election->addCandidate('sue'); | ||||
|         $this->election->addCandidate('bill'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             bob > bill > sue | ||||
|             sue > bob > bill | ||||
|             bill > sue > bob | ||||
|             bob > bill > sue | ||||
|             sue > bob > bill | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'sue', | ||||
|                 2 => 'bob', | ||||
|                 3 => 'bill', ], | ||||
|             $this->election->getResult('InstantRunoff')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         $this->election->addCandidate('bob'); | ||||
|         $this->election->addCandidate('sue'); | ||||
|         $this->election->addCandidate('bill'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             bob > bill > sue | ||||
|             sue > bob > bill | ||||
|             bill > sue > bob | ||||
|             bob > bill > sue | ||||
|             sue > bob > bill | ||||
|             bill > bob > sue | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'bob', | ||||
|                 2 => 'bill', | ||||
|                 3 => 'sue', ], | ||||
|             $this->election->getResult('InstantRunoff')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_4(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A=B=C | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => ['A', 'B', 'C'], ], | ||||
|             $this->election->getResult('InstantRunoff')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_Equality(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A | ||||
|             B | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => ['A', 'B'], ], | ||||
|             $this->election->getResult('InstantRunoff')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_TieBreaking(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A * 4 | ||||
|             B * 4 | ||||
|             C>A * 2 | ||||
|             D>C>B * 2 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => ['A', 'B'], | ||||
|                 3 => 'C', | ||||
|                 4 => 'D', | ||||
|             ], | ||||
|             $this->election->getResult('InstantRunoff')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testInfiniteLoopOnTidemanDataset3IfExplicitRanking(): void | ||||
|     { | ||||
|         $election = (new DavidHillFormat(__DIR__.'/../../../Tools/Converters/TidemanData/A3.HIL'))->setDataToAnElection(); | ||||
|  | ||||
|         $election->setImplicitRanking(false); | ||||
|  | ||||
|         self::assertSame('6 > 8 > 4 > 11 > 2 > 5 > 14 > 1 = 7 > 12 > 3 > 9 > 10 > 15 > 13', $election->getResult('InstantRunoff')->getResultAsString()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,205 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\KemenyYoung; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use CondorcetPHP\Condorcet\Algo\Methods\KemenyYoung\KemenyYoung; | ||||
| use CondorcetPHP\Condorcet\Algo\StatsVerbosity; | ||||
| use CondorcetPHP\Condorcet\Throwable\CandidatesMaxNumberReachedException; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class KemenyYoungTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         $this->election->addCandidate('Memphis'); | ||||
|         $this->election->addCandidate('Nashville'); | ||||
|         $this->election->addCandidate('Knoxville'); | ||||
|         $this->election->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Memphis > Nashville > Chattanooga * 42 | ||||
|             Nashville > Chattanooga > Knoxville * 26 | ||||
|             Chattanooga > Knoxville > Nashville * 15 | ||||
|             Knoxville > Chattanooga > Nashville * 17 | ||||
|         '); | ||||
|  | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Nashville', | ||||
|                 2 => 'Chattanooga', | ||||
|                 3 => 'Knoxville', | ||||
|                 4 => 'Memphis', | ||||
|             ], | ||||
|             $this->election->getResult('KemenyYoung')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame(393, $this->election->getResult('KemenyYoung')->getStats()['Best Score']); | ||||
|  | ||||
|         self::assertSame($this->election->getWinner(), $this->election->getWinner('KemenyYoung')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @preserveGlobalState disabled | ||||
|      */ | ||||
|     public function testResult2(): void | ||||
|     { | ||||
|         $this->election->parseCandidates('Elliot;Roland;Meredith;Selden'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Elliot > Roland ^30 | ||||
|             Elliot > Meredith ^60 | ||||
|             Elliot > Selden ^60 | ||||
|             Roland > Meredith ^70 | ||||
|             Roland > Selden ^60 | ||||
|             Meredith > Selden ^40 | ||||
|         '); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Elliot', | ||||
|                 2 => 'Roland', | ||||
|                 3 => 'Meredith', | ||||
|                 4 => 'Selden', | ||||
|             ], | ||||
|             $this->election->getResult('KemenyYoung')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testStats_1(): void | ||||
|     { | ||||
|         $this->election->setStatsVerbosity(StatsVerbosity::FULL); | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|  | ||||
|         $this->election->parseVotes($r = 'A > B'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $r, | ||||
|             $this->election->getResult('KemenyYoung')->getResultAsString() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'Best Score' => 1, | ||||
|                 'Ranking In Conflicts' => 0, | ||||
|                 'Ranking Scores' => [ | ||||
|                     [1 => 'A', 2 => 'B', 'score' => 1], | ||||
|                     [1 => 'B', 2 => 'A', 'score' => 0], | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('KemenyYoung')->getStats() | ||||
|         ); | ||||
|  | ||||
|         $this->election->setStatsVerbosity(StatsVerbosity::STD); | ||||
|  | ||||
|         self::assertArrayNotHasKey('rankingScores', $this->election->getResult('KemenyYoung')->getStats()); | ||||
|     } | ||||
|  | ||||
|     public function testMaxCandidates(): never | ||||
|     { | ||||
|         $this->expectException(CandidatesMaxNumberReachedException::class); | ||||
|         $this->expectExceptionMessage("Maximum number of candidates reached: The method 'Kemeny–Young' is configured to accept only ".KemenyYoung::$MaxCandidates.' candidates'); | ||||
|  | ||||
|         for ($i=0; $i < (KemenyYoung::$MaxCandidates + 1); $i++) { | ||||
|             $this->election->addCandidate(); | ||||
|         } | ||||
|  | ||||
|         $this->election->parseVotes('A'); | ||||
|  | ||||
|         $this->election->getWinner('KemenyYoung'); | ||||
|     } | ||||
|  | ||||
|     public function testConflicts(): void | ||||
|     { | ||||
|         $this->election->parseCandidates('A;B;C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C; | ||||
|             B>C>A; | ||||
|             C>A>B'); | ||||
|  | ||||
|         $result = $this->election->getResult('KemenyYoung'); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [0 => [ | ||||
|                 'type' => 42, | ||||
|                 'msg' => '3;5', | ||||
|             ], | ||||
|             ], | ||||
|             $result->getWarning(\CondorcetPHP\Condorcet\Algo\Methods\KemenyYoung\KemenyYoung::CONFLICT_WARNING_CODE) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [0 => [ | ||||
|                 'type' => 42, | ||||
|                 'msg' => '3;5', | ||||
|             ], | ||||
|             ], | ||||
|             $result->getWarning() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame(3, $result->getStats()['Ranking In Conflicts']); | ||||
|  | ||||
|         $this->election->addVote('A>B>C'); | ||||
|  | ||||
|         $result = $this->election->getResult('KemenyYoung'); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [], | ||||
|             $result->getWarning(\CondorcetPHP\Condorcet\Algo\Methods\KemenyYoung\KemenyYoung::CONFLICT_WARNING_CODE) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals('A', $this->election->getWinner('KemenyYoung')); | ||||
|     } | ||||
|  | ||||
|     public function testKemenyWithOnly1Candidate(): void | ||||
|     { | ||||
|         $candidate[] = $this->election->addCandidate(); | ||||
|  | ||||
|         $this->election->addVote($candidate); | ||||
|  | ||||
|         self::assertSame($candidate[0], $this->election->getWinner('KemenyYoung')); | ||||
|     } | ||||
|  | ||||
|     public function ManyCandidatesProvider(): array | ||||
|     { | ||||
|         return [ | ||||
|             9  => [9], | ||||
|             10  => [10], | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @group large | ||||
|      * @dataProvider  ManyCandidatesProvider | ||||
|      */ | ||||
|     public function testKemenyWithManyCandidates(int $candidatesCount): void | ||||
|     { | ||||
|         $original = KemenyYoung::$MaxCandidates; | ||||
|         KemenyYoung::$MaxCandidates = null; | ||||
|  | ||||
|         for ($i=0; $i<$candidatesCount; $i++) { | ||||
|             $candidates[] = $this->election->addCandidate(); | ||||
|         } | ||||
|  | ||||
|         $this->election->addVote($candidates); | ||||
|  | ||||
|         self::assertSame($candidates[0], $this->election->getWinner('KemenyYoung')); | ||||
|  | ||||
|         KemenyYoung::$MaxCandidates = $original; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,124 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\HighestAverage; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Algo\Tools\StvQuotas; | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class LargestRemainderTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     protected function tearDown(): void | ||||
|     { | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::HARE); | ||||
|     } | ||||
|  | ||||
|     # https://en.wikipedia.org/wiki/Largest_remainder_method | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         $this->election->parseCandidates('Yellows;Whites;Reds;Greens;Blues;Pinks'); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(10); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes('Yellows ^47000;Whites ^16000;Reds ^15800;Greens ^12000;Blues ^6100;Pinks ^3100'); | ||||
|  | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::HARE); // Hare-LR | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'Yellows' => 5, | ||||
|                 'Whites' => 2, | ||||
|                 'Reds' => 1, | ||||
|                 'Greens' => 1, | ||||
|                 'Blues' => 1, | ||||
|                 'Pinks' => 0, ], | ||||
|             $this->election->getResult('LR')->getStats()['Seats per Candidates'] | ||||
|         ); | ||||
|  | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::DROOP); // Hare-LR | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'Yellows' => 5, | ||||
|                 'Whites' => 2, | ||||
|                 'Reds' => 2, | ||||
|                 'Greens' => 1, | ||||
|                 'Blues' => 0, | ||||
|                 'Pinks' => 0, ], | ||||
|             $this->election->getResult('LR')->getStats()['Seats per Candidates'] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     # https://en.wikipedia.org/wiki/Largest_remainder_method | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         $this->election->parseCandidates('A;B;C;D;E;F'); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(25); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes('A ^1500;B ^1500;C ^900;D^500;E ^500;F ^200'); | ||||
|  | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::HARE); // Hare-LR | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'A' => 7, | ||||
|                 'B' => 7, | ||||
|                 'C' => 4, | ||||
|                 'D' => 3, | ||||
|                 'E' => 3, | ||||
|                 'F' => 1, ], | ||||
|             $this->election->getResult('LR')->getStats()['Seats per Candidates'] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     # https://en.wikipedia.org/wiki/Largest_remainder_method | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         $this->election->parseCandidates('A;B;C;D;E;F'); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(26); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes('A ^1500;B ^1500;C ^900;D^500;E ^500;F ^200'); | ||||
|  | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::HARE); // Hare-LR | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'A' => 8, | ||||
|                 'B' => 8, | ||||
|                 'C' => 5, | ||||
|                 'D' => 2, | ||||
|                 'E' => 2, | ||||
|                 'F' => 1, ], | ||||
|             $this->election->getResult('LR')->getStats()['Seats per Candidates'] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     // Fixing error with Droop Quotas in some cases | ||||
|     public function testResult_4_LR(): void | ||||
|     { | ||||
|         $this->election->parseCandidates('A;B;C'); | ||||
|         $this->election->setNumberOfSeats(99); | ||||
|  | ||||
|         $this->election->parseVotes('A>B>C;C>B>A;B>A>C'); | ||||
|  | ||||
|         $this->election->setMethodOption('LargestRemainder', 'Quota', StvQuotas::DROOP); // Hare-LR | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'A' => 33, | ||||
|                 'B' => 33, | ||||
|                 'C' => 33, | ||||
|             ], | ||||
|             $this->election->getResult('LR')->getStats()['Seats per Candidates'] | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,203 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\Majority; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class FirstPastThePostTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function testResult_French2002(): void | ||||
|     { | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->addCandidate('Chirac'); | ||||
|         $this->election->addCandidate('Jospin'); | ||||
|         $this->election->addCandidate('Le Pen'); | ||||
|         $this->election->addCandidate('Bayrou'); | ||||
|         $this->election->addCandidate('Laguiller'); | ||||
|         $this->election->addCandidate('Chevènement'); | ||||
|         $this->election->addCandidate('Mamère'); | ||||
|         $this->election->addCandidate('Besancenot'); | ||||
|         $this->election->addCandidate('Saint-Josse'); | ||||
|         $this->election->addCandidate('Madelin'); | ||||
|         $this->election->addCandidate('Robert Hue'); | ||||
|         $this->election->addCandidate('Mégret'); | ||||
|         $this->election->addCandidate('Taubira'); | ||||
|         $this->election->addCandidate('Lepage'); | ||||
|         $this->election->addCandidate('Boutin'); | ||||
|         $this->election->addCandidate('Gluckstein'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Chirac > Bayrou = Jospin = Madelin = Chevénement = Mamère = Robert Hue = Taubira = Lepage = Boutin > Saint-Josse ^1988 | ||||
|             Jospin > Chevénement = Taubira = Mamère > Bayrou > Robert Hue > Chirac = Lepage = Boutin > Madelin > Saint-Josse ^ 1618 | ||||
|             Le Pen ^1686 | ||||
|             Bayrou ^684 | ||||
|             Laguiller ^572 | ||||
|             Chevènement ^533 | ||||
|             Mamère ^525 | ||||
|             Besancenot ^425 | ||||
|             Saint-Josse ^423 | ||||
|             Madelin ^391 | ||||
|             Robert Hue ^337 | ||||
|             Mégret > Le Pen ^234 | ||||
|             Taubira ^232 | ||||
|             Lepage ^188 | ||||
|             Boutin ^119 | ||||
|             Gluckstein ^47 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Chirac', | ||||
|                 2 => 'Le Pen', | ||||
|                 3 => 'Jospin', | ||||
|                 4 => 'Bayrou', | ||||
|                 5 => 'Laguiller', | ||||
|                 6 => 'Chevènement', | ||||
|                 7 => 'Mamère', | ||||
|                 8 => 'Besancenot', | ||||
|                 9 => 'Saint-Josse', | ||||
|                 10 => 'Madelin', | ||||
|                 11 => 'Robert Hue', | ||||
|                 12 => 'Mégret', | ||||
|                 13 => 'Taubira', | ||||
|                 14 => 'Lepage', | ||||
|                 15 => 'Boutin', | ||||
|                 16 => 'Gluckstein', | ||||
|             ], | ||||
|             $this->election->getResult('Fptp')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1 => [ | ||||
|                 'Chirac' => 1988, | ||||
|                 'Le Pen' => 1686, | ||||
|                 'Jospin' => 1618, | ||||
|                 'Bayrou' => 684, | ||||
|                 'Laguiller' => 572, | ||||
|                 'Chevènement' => 533, | ||||
|                 'Mamère' => 525, | ||||
|                 'Besancenot' => 425, | ||||
|                 'Saint-Josse' => 423, | ||||
|                 'Madelin' => 391, | ||||
|                 'Robert Hue' => 337, | ||||
|                 'Mégret' => 234, | ||||
|                 'Taubira' => 232, | ||||
|                 'Lepage' => 188, | ||||
|                 'Boutin' => 119, | ||||
|                 'Gluckstein' => 47, | ||||
|             ]], | ||||
|             $this->election->getResult('Fptp')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 42 | ||||
|             B>C>D>A * 26 | ||||
|             C>D>B>A * 15 | ||||
|             D>C>B>A * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => 'B', | ||||
|                 3 => 'D', | ||||
|                 4 => 'C', ], | ||||
|             $this->election->getResult('Fptp')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1 => [ | ||||
|                 'A' => 42, | ||||
|                 'B' => 26, | ||||
|                 'D' => 17, | ||||
|                 'C' => 15, | ||||
|             ]], | ||||
|             $this->election->getResult('Fptp')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D ^ 42 | ||||
|             B>C>D>A * 26 | ||||
|             C>D>B>A ^ 15 | ||||
|             D>C>B>A * 17 | ||||
|             D>B=C=A ^ 25 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => ['A', 'D'], | ||||
|                 2 => 'B', | ||||
|                 3 => 'C', ], | ||||
|             $this->election->getResult('Fptp')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => [ | ||||
|                 'A' => (float) 42, | ||||
|                 'D' => (float) 42, | ||||
|                 'B' => (float) 26, | ||||
|                 'C' => (float) 15, | ||||
|             ]], | ||||
|             $this->election->getResult('Fptp')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C | ||||
|             A=C>B | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => 'C', | ||||
|                 3 => 'B', ], | ||||
|             $this->election->getResult('Fptp')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1 => [ | ||||
|                 'A' => 1 + 1/2, | ||||
|                 'C' => 1/2, | ||||
|                 'B' => 0, | ||||
|             ]], | ||||
|             $this->election->getResult('Fptp')->getStats() | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,198 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Methods\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Condorcet, Election}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class MultipleRoundsSystemTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     protected function tearDown(): void | ||||
|     { | ||||
|         $this->resetOptions(); | ||||
|     } | ||||
|  | ||||
|     protected function resetOptions(): void | ||||
|     { | ||||
|         $methodClass = Condorcet::getMethodClass('runoff voting'); | ||||
|  | ||||
|         $this->election->setMethodOption($methodClass, 'MAX_ROUND', 2); | ||||
|         $this->election->setMethodOption($methodClass, 'TARGET_NUMBER_OF_CANDIDATES_FOR_THE_NEXT_ROUND', 2); | ||||
|         $this->election->setMethodOption($methodClass, 'NUMBER_OF_TARGETED_CANDIDATES_AFTER_EACH_ROUND', 0); | ||||
|     } | ||||
|  | ||||
|     public function testResult_MajorityTest_systematic_triangular(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 42 | ||||
|             B>C>D>A * 26 | ||||
|             C>D>B>A * 15 | ||||
|             D>C>B>A * 17 | ||||
|         '); | ||||
|  | ||||
|         $methodClass = Condorcet::getMethodClass('Multiple Rounds System'); | ||||
|  | ||||
|         $this->election->setMethodOption($methodClass, 'MAX_ROUND', 2); | ||||
|         $this->election->setMethodOption($methodClass, 'TARGET_NUMBER_OF_CANDIDATES_FOR_THE_NEXT_ROUND', 3); | ||||
|         $this->election->setMethodOption($methodClass, 'NUMBER_OF_TARGETED_CANDIDATES_AFTER_EACH_ROUND', 0); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => 'D', | ||||
|                 3 => 'B', | ||||
|                 4 => 'C', ], | ||||
|             $this->election->getResult('Multiple Rounds System')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'A' => 42, | ||||
|                 'B' => 26, | ||||
|                 'D' => 17, | ||||
|                 'C' => 15, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'A' => 42, | ||||
|                     'D' => 32, | ||||
|                     'B' => 26, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Multiple Rounds System')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function testResult_MajorityTest_three_round(): void | ||||
|     { | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B ^10 | ||||
|             B ^12 | ||||
|             C>B ^10 | ||||
|             D>E>A>B ^9 | ||||
|             E>B ^5 | ||||
|         '); | ||||
|  | ||||
|         $methodClass = Condorcet::getMethodClass('runoff voting'); | ||||
|  | ||||
|         $this->election->setMethodOption($methodClass, 'MAX_ROUND', 3); | ||||
|         $this->election->setMethodOption($methodClass, 'TARGET_NUMBER_OF_CANDIDATES_FOR_THE_NEXT_ROUND', 2); | ||||
|         $this->election->setMethodOption($methodClass, 'NUMBER_OF_TARGETED_CANDIDATES_AFTER_EACH_ROUND', 0); | ||||
|  | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'B', 2 => 'A', 3 => 'C', 4=> 'D', 5=> 'E'], | ||||
|             $this->election->getResult('Multiple Rounds System')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'B' => 12, | ||||
|                 'A' => 10, | ||||
|                 'C' => 10, | ||||
|                 'D' => 9, | ||||
|                 'E' => 5, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'A' => 19, | ||||
|                     'B' => 17, | ||||
|                     'C' => 10, | ||||
|                 ], | ||||
|                 3=> [ | ||||
|                     'B' => 27, | ||||
|                     'A' => 19, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('runoff voting')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_MajorityTest_Many_Round(): void | ||||
|     { | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('E'); | ||||
|         $this->election->addCandidate('F'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D>E>F ^100 | ||||
|             B>A>C>D>E>F ^99 | ||||
|             C>A>B>D>E>F ^98 | ||||
|             D>A=B>C>E>F ^97 | ||||
|             E>B>A>C>D>F ^96 | ||||
|             F>A>B>C>D>E ^95 | ||||
|         '); | ||||
|  | ||||
|         $methodClass = Condorcet::getMethodClass('Multiple Rounds System'); | ||||
|  | ||||
|         $this->election->setMethodOption($methodClass, 'MAX_ROUND', 100); | ||||
|         $this->election->setMethodOption($methodClass, 'TARGET_NUMBER_OF_CANDIDATES_FOR_THE_NEXT_ROUND', 5); | ||||
|         $this->election->setMethodOption($methodClass, 'NUMBER_OF_TARGETED_CANDIDATES_AFTER_EACH_ROUND', -1); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'A', 2 => 'B', 3 => 'C', 4=> 'D', 5=> 'E', 6 => 'F'], | ||||
|             $this->election->getResult('Multiple Rounds System')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'A' => 100, | ||||
|                 'B' => 99, | ||||
|                 'C' => 98, | ||||
|                 'D' => 97, | ||||
|                 'E' => 96, | ||||
|                 'F' => 95, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'A' => 100 + 95, | ||||
|                     'B' => 99, | ||||
|                     'C' => 98, | ||||
|                     'D' => 97, | ||||
|                     'E' => 96, | ||||
|                 ], | ||||
|                 3=> [ | ||||
|                     'A' => 100 + 95, | ||||
|                     'B' => 99 + 96, | ||||
|                     'C' => 98, | ||||
|                     'D' => 97, | ||||
|                 ], | ||||
|                 4=> [ | ||||
|                     'A' => 100 + 95 + (97/2), | ||||
|                     'B' => 99 + 96 + (97/2), | ||||
|                     'C' => 98, | ||||
|                 ], | ||||
|                 5=> [ | ||||
|                     'A' => 100 + 95 + (97/2) + 98, | ||||
|                     'B' => 99 + 96 + (97/2), | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('runoff voting')->getStats() | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,544 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Methods\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use CondorcetPHP\Condorcet\Algo\Methods\Majority\MultipleRoundsSystem; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class TwoRoundSystemTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_French2002(): void | ||||
|     { | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|         $this->election->setImplicitRanking(false); | ||||
|  | ||||
|         $this->election->addCandidate('Chirac'); | ||||
|         $this->election->addCandidate('Jospin'); | ||||
|         $this->election->addCandidate('Le Pen'); | ||||
|         $this->election->addCandidate('Bayrou'); | ||||
|         $this->election->addCandidate('Laguiller'); | ||||
|         $this->election->addCandidate('Chevènement'); | ||||
|         $this->election->addCandidate('Mamère'); | ||||
|         $this->election->addCandidate('Besancenot'); | ||||
|         $this->election->addCandidate('Saint-Josse'); | ||||
|         $this->election->addCandidate('Madelin'); | ||||
|         $this->election->addCandidate('Robert Hue'); | ||||
|         $this->election->addCandidate('Mégret'); | ||||
|         $this->election->addCandidate('Taubira'); | ||||
|         $this->election->addCandidate('Lepage'); | ||||
|         $this->election->addCandidate('Boutin'); | ||||
|         $this->election->addCandidate('Gluckstein'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Chirac > Bayrou = Jospin = Madelin = Chevénement = Mamère = Robert Hue = Taubira = Lepage = Boutin > Saint-Josse ^1988 | ||||
|             Jospin > Chevénement = Taubira = Mamère > Bayrou > Robert Hue > Chirac = Lepage = Boutin > Madelin > Saint-Josse ^ 1618 | ||||
|             Le Pen > Mégret ^1686 | ||||
|             Bayrou > Chirac ^684 | ||||
|             Laguiller > Besancenot = Gluckstein  > ^572 | ||||
|             Chevènement > Chirac ^533 | ||||
|             Mamère > Jospin > Chirac ^525 | ||||
|             Besancenot > Gluckstein = Laguillier ^425 | ||||
|             Saint-Josse > Chirac > Jospin ^423 | ||||
|             Madelin > Chirac ^391 | ||||
|             Robert Hue > Jospin > Chirac ^337 | ||||
|             Mégret > Le Pen ^234 | ||||
|             Taubira > Jospin > Chirac ^232 | ||||
|             Lepage > Chirac ^188 | ||||
|             Boutin > Chirac ^119 | ||||
|             Gluckstein > Besancenot = Laguillier ^47 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Chirac', | ||||
|                 2 => 'Le Pen', | ||||
|                 3 => 'Jospin', | ||||
|                 4 => 'Bayrou', | ||||
|                 5 => 'Laguiller', | ||||
|                 6 => 'Chevènement', | ||||
|                 7 => 'Mamère', | ||||
|                 8 => 'Besancenot', | ||||
|                 9 => 'Saint-Josse', | ||||
|                 10 => 'Madelin', | ||||
|                 11 => 'Robert Hue', | ||||
|                 12 => 'Mégret', | ||||
|                 13 => 'Taubira', | ||||
|                 14 => 'Lepage', | ||||
|                 15 => 'Boutin', | ||||
|                 16 => 'Gluckstein', | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'Chirac' => 1988, | ||||
|                 'Le Pen' => 1686, | ||||
|                 'Jospin' => 1618, | ||||
|                 'Bayrou' => 684, | ||||
|                 'Laguiller' => 572, | ||||
|                 'Chevènement' => 533, | ||||
|                 'Mamère' => 525, | ||||
|                 'Besancenot' => 425, | ||||
|                 'Saint-Josse' => 423, | ||||
|                 'Madelin' => 391, | ||||
|                 'Robert Hue' => 337, | ||||
|                 'Mégret' => 234, | ||||
|                 'Taubira' => 232, | ||||
|                 'Lepage' => 188, | ||||
|                 'Boutin' => 119, | ||||
|                 'Gluckstein' => 47, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'Chirac' => 7038, | ||||
|                     'Le Pen' => 1920, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 42 | ||||
|             B>C>D>A * 26 | ||||
|             C>D>B>A * 15 | ||||
|             D>C>B>A * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'B', | ||||
|                 2 => 'A', | ||||
|                 3 => 'D', | ||||
|                 4 => 'C', ], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'A' => 42, | ||||
|                 'B' => 26, | ||||
|                 'D' => 17, | ||||
|                 'C' => 15, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'B' => 58, | ||||
|                     'A' => 42, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D ^ 42 | ||||
|             B>C>D>A * 26 | ||||
|             C>D>B>A ^ 15 | ||||
|             D>C>B>A * 17 | ||||
|             D>B=C=A ^ 25 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'D', | ||||
|                 2 => 'A', | ||||
|                 3 => 'B', | ||||
|                 4 => 'C', ], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'A' => 42, | ||||
|                 'D' => 42, | ||||
|                 'B' => 26, | ||||
|                 'C' => 15, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'D' => 83, | ||||
|                     'A' => 42, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C | ||||
|             A=C>B | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => 'C', | ||||
|                 3 => 'B', ], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'A' => 1.5, | ||||
|                 'C' => 0.5, | ||||
|                 'B' => 0, | ||||
|             ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_5(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 51 | ||||
|             B>C>D>A * 24 | ||||
|             C>D>B>A * 25 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => 'C', | ||||
|                 3 => 'B', | ||||
|                 4 => 'D', ], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'A' => 51, | ||||
|                 'C' => 25, | ||||
|                 'B' => 24, | ||||
|                 'D' => 0, | ||||
|             ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_6(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 50 | ||||
|             B>C>D>A * 50 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B'], 2 => 'C'], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'A' => 50, | ||||
|                 'B' => 50, | ||||
|                 'C' => 0, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'A' => 50, | ||||
|                     'B' => 50, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_7(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D * 50 | ||||
|             B>C>D>A * 50 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B']], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'A' => 50, | ||||
|                 'B' => 50, | ||||
|             ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_8(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             D>E * 50 | ||||
|             E>D * 50 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B', 'C']], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         $stats = $this->election->getResult('Two Rounds')->getStats(); | ||||
|         // \array_walk_recursive($stats, function (float &$value): float { | ||||
|         //     return $value = round($value, 10); | ||||
|         // }); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1=> [ | ||||
|                 'A' => round(100/3, MultipleRoundsSystem::DECIMAL_PRECISION), | ||||
|                 'B' => round(100/3, MultipleRoundsSystem::DECIMAL_PRECISION), | ||||
|                 'C' => round(100/3, MultipleRoundsSystem::DECIMAL_PRECISION), | ||||
|             ], | ||||
|             ], | ||||
|             $stats | ||||
|         ); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B', 'C']], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1=> [ | ||||
|                 'A' => 0.0, | ||||
|                 'B' => 0.0, | ||||
|                 'C' => 0.0, | ||||
|             ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_9(): void | ||||
|     { | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B ^10 | ||||
|             B ^12 | ||||
|             C ^10 | ||||
|             D>E>A>B ^9 | ||||
|             E>B ^5 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'A', 2 => 'B', 3 => 'C', 4=> 'D', 5=> 'E'], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'B' => 12, | ||||
|                 'A' => 10, | ||||
|                 'C' => 10, | ||||
|                 'D' => 9, | ||||
|                 'E' => 5, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'A' => 19, | ||||
|                     'B' => 17, | ||||
|                     'C' => 10, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|  | ||||
|         $this->election->addVote('E>B ^2'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B'], 2 => 'C', 3=> 'D', 4 => 'E'], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'B' => 12, | ||||
|                 'A' => 10, | ||||
|                 'C' => 10, | ||||
|                 'D' => 9, | ||||
|                 'E' => 7, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'A' => 19, | ||||
|                     'B' => 19, | ||||
|                     'C' => 10, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|  | ||||
|         $this->election->addVote('C'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'B', 2 => 'C', 3=> 'A', 4 => 'D', 5 => 'E'], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'B' => 12, | ||||
|                 'C' => 11, | ||||
|                 'A' => 10, | ||||
|                 'D' => 9, | ||||
|                 'E' => 7, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'B' => 38, | ||||
|                     'C' => 11, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_10(): void | ||||
|     { | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A ^10 | ||||
|             B ^10 | ||||
|             C ^10 | ||||
|             D>E ^9 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B', 'C'], 2 => 'D', 3 => 'E'], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1=> [ | ||||
|                 'A' => (float) 10, | ||||
|                 'B' => (float) 10, | ||||
|                 'C' => (float) 10, | ||||
|                 'D' => (float) 9, | ||||
|                 'E' => (float) 0, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'A' => (float) 13, | ||||
|                     'B' => (float) 13, | ||||
|                     'C' => (float) 13, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B', 'C'], 2 => 'D', 3 => 'E'], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1=> [ | ||||
|                 'A' => 10, | ||||
|                 'B' => 10, | ||||
|                 'C' => 10, | ||||
|                 'D' => 9, | ||||
|                 'E' => 0, | ||||
|             ], | ||||
|                 2=> [ | ||||
|                     'A' => 10, | ||||
|                     'B' => 10, | ||||
|                     'C' => 10, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_11(): void | ||||
|     { | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|         $this->election->setImplicitRanking(false); | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A ^10 | ||||
|             B ^10 | ||||
|             C ^10 | ||||
|             D>E ^9 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B', 'C']], | ||||
|             $this->election->getResult('Two Rounds')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1=> [ | ||||
|                 'A' => (float) 10, | ||||
|                 'B' => (float) 10, | ||||
|                 'C' => (float) 10, | ||||
|             ], | ||||
|             ], | ||||
|             $this->election->getResult('Two Rounds')->getStats() | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										273
									
								
								include/condorcet/Tests/src/Algo/Methods/Minimax/MinimaxTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								include/condorcet/Tests/src/Algo/Methods/Minimax/MinimaxTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,273 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\Minimax; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Condorcet, Election}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class MinimaxTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Minimax_Condorcet | ||||
|  | ||||
|         $this->election->addCandidate('Memphis'); | ||||
|         $this->election->addCandidate('Nashville'); | ||||
|         $this->election->addCandidate('Chattanooga'); | ||||
|         $this->election->addCandidate('Knoxville'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Memphis > Nashville > Chattanooga * 42 | ||||
|             Nashville > Chattanooga > Knoxville * 26 | ||||
|             Chattanooga > Knoxville > Nashville * 15 | ||||
|             Knoxville > Chattanooga > Nashville * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($this->election->getWinner(), $this->election->getWinner('Minimax Winning')); | ||||
|  | ||||
|         self::assertSame($this->election->getWinner(), $this->election->getWinner('Minimax Margin')); | ||||
|  | ||||
|         self::assertSame($this->election->getWinner(), $this->election->getWinner('Minimax Opposition')); | ||||
|  | ||||
|         $expectedRanking = [ | ||||
|             1 => 'Nashville', | ||||
|             2 => 'Memphis', | ||||
|             3 => 'Chattanooga', | ||||
|             4 => 'Knoxville', | ||||
|         ]; | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expectedRanking, | ||||
|             $this->election->getResult('Minimax Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expectedRanking, | ||||
|             $this->election->getResult('Minimax Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expectedRanking, | ||||
|             $this->election->getResult('Minimax Opposition')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['Memphis'       =>  ['worst_pairwise_defeat_winning' => 58], | ||||
|                 'Nashville'     =>  ['worst_pairwise_defeat_winning' => 0], | ||||
|                 'Chattanooga'   =>  ['worst_pairwise_defeat_winning' => 68], | ||||
|                 'Knoxville'     =>  ['worst_pairwise_defeat_winning' => 83], ], | ||||
|             $this->election->getResult('Minimax Winning')->getStats() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['Memphis'       =>  ['worst_pairwise_defeat_margin' => 16], | ||||
|                 'Nashville'     =>  ['worst_pairwise_defeat_margin' => -16], | ||||
|                 'Chattanooga'   =>  ['worst_pairwise_defeat_margin' => 36], | ||||
|                 'Knoxville'     =>  ['worst_pairwise_defeat_margin' => 66], ], | ||||
|             $this->election->getResult('Minimax Margin')->getStats() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['Memphis'       =>  ['worst_pairwise_opposition' => 58], | ||||
|                 'Nashville'     =>  ['worst_pairwise_opposition' => 42], | ||||
|                 'Chattanooga'   =>  ['worst_pairwise_opposition' => 68], | ||||
|                 'Knoxville'     =>  ['worst_pairwise_opposition' => 83], ], | ||||
|             $this->election->getResult('Minimax Opposition')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Minimax_Condorcet | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A = C > B * 4 | ||||
|             A > C > B * 47 | ||||
|             C > B > A * 43 | ||||
|             B > A = C * 6 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($this->election->getWinner(), $this->election->getWinner('Minimax Winning')); | ||||
|  | ||||
|         self::assertSame($this->election->getWinner(), $this->election->getWinner('Minimax Margin')); | ||||
|  | ||||
|         self::assertEquals('C', $this->election->getWinner('Minimax Opposition')); | ||||
|  | ||||
|         $expectedRanking1 = [ | ||||
|             1 => 'A', | ||||
|             2 => 'C', | ||||
|             3 => 'B', | ||||
|         ]; | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expectedRanking1, | ||||
|             $this->election->getResult('Minimax Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expectedRanking1, | ||||
|             $this->election->getResult('Minimax Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'C', | ||||
|                 2 => 'A', | ||||
|                 3 => 'B', ], | ||||
|             $this->election->getResult('Minimax Opposition')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['A'       =>  ['worst_pairwise_defeat_winning' => 0], | ||||
|                 'B'     =>  ['worst_pairwise_defeat_winning' => 94], | ||||
|                 'C'     =>  ['worst_pairwise_defeat_winning' => 47], ], | ||||
|             $this->election->getResult('Minimax Winning')->getStats() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['A'       =>  ['worst_pairwise_defeat_margin' => -2], | ||||
|                 'B'     =>  ['worst_pairwise_defeat_margin' => 88], | ||||
|                 'C'     =>  ['worst_pairwise_defeat_margin' => 4], ], | ||||
|             $this->election->getResult('Minimax Margin')->getStats() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['A'       =>  ['worst_pairwise_opposition' => 49], | ||||
|                 'B'     =>  ['worst_pairwise_opposition' => 94], | ||||
|                 'C'     =>  ['worst_pairwise_opposition' => 47], ], | ||||
|             $this->election->getResult('Minimax Opposition')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         # From http://www.cs.wustl.edu/~legrand/rbvote/desc.html | ||||
|  | ||||
|         $this->election->addCandidate('Abby'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|         $this->election->addCandidate('Cora'); | ||||
|         $this->election->addCandidate('Dave'); | ||||
|         $this->election->addCandidate('Erin'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Abby>Cora>Erin>Dave>Brad * 98 | ||||
|             Brad>Abby>Erin>Cora>Dave * 64 | ||||
|             Brad>Abby>Erin>Dave>Cora * 12 | ||||
|             Brad>Erin>Abby>Cora>Dave * 98 | ||||
|             Brad>Erin>Abby>Dave>Cora * 13 | ||||
|             Brad>Erin>Dave>Abby>Cora * 125 | ||||
|             Cora>Abby>Erin>Dave>Brad * 124 | ||||
|             Cora>Erin>Abby>Dave>Brad * 76 | ||||
|             Dave>Abby>Brad>Erin>Cora * 21 | ||||
|             Dave>Brad>Abby>Erin>Cora * 30 | ||||
|             Dave>Brad>Erin>Cora>Abby * 98 | ||||
|             Dave>Cora>Abby>Brad>Erin * 139 | ||||
|             Dave>Cora>Brad>Abby>Erin * 23 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('Cora', $this->election->getWinner('Minimax Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_4(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Condorcet_loser_criterion | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('L'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C * 1 | ||||
|             A > B > L * 1 | ||||
|             B > C > A * 3 | ||||
|             C > L > A * 1 | ||||
|             L > A > B * 1 | ||||
|             L > C > A * 2 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('L', $this->election->getWinner('Minimax Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_5(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Condorcet_loser_criterion | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > C > B > D * 30 | ||||
|             D > B > A > C * 15 | ||||
|             D > B > C > A * 14 | ||||
|             B > C > A > D * 6 | ||||
|             D > C > A = B * 4 | ||||
|             C > A = B * 16 | ||||
|             B > C * 14 | ||||
|             C > A * 3 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('A', $this->election->getWinner('Minimax Winning')); | ||||
|         self::assertEquals('B', $this->election->getWinner('Minimax Margin')); | ||||
|         self::assertEquals('D', $this->election->getWinner('Minimax Opposition')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['A'       =>  ['worst_pairwise_defeat_winning' => 35], | ||||
|                 'B'     =>  ['worst_pairwise_defeat_winning' => 50], | ||||
|                 'C'   =>  ['worst_pairwise_defeat_winning' => 45], | ||||
|                 'D'     =>  ['worst_pairwise_defeat_winning' => 36], ], | ||||
|             $this->election->getResult('Minimax Winning')->getStats() | ||||
|         ); | ||||
|         self::assertSame( | ||||
|             ['A'       =>  ['worst_pairwise_defeat_margin' => 5], | ||||
|                 'B'     =>  ['worst_pairwise_defeat_margin' => 1], | ||||
|                 'C'   =>  ['worst_pairwise_defeat_margin' => 2], | ||||
|                 'D'     =>  ['worst_pairwise_defeat_margin' => 3], ], | ||||
|             $this->election->getResult('Minimax Margin')->getStats() | ||||
|         ); | ||||
|         self::assertSame( | ||||
|             ['A'       =>  ['worst_pairwise_opposition' => 43], | ||||
|                 'B'     =>  ['worst_pairwise_opposition' => 50], | ||||
|                 'C'   =>  ['worst_pairwise_opposition' => 49], | ||||
|                 'D'     =>  ['worst_pairwise_opposition' => 36], ], | ||||
|             $this->election->getResult('Minimax Opposition')->getStats() | ||||
|         ); | ||||
|  | ||||
|         // Implicit Ranking | ||||
|         $this->election->setImplicitRanking(true); | ||||
|  | ||||
|         self::assertNotEquals('A', $this->election->getWinner('Minimax Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_6(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Minimax_Condorcet | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C | ||||
|             C > B > A | ||||
|         '); | ||||
|  | ||||
|         self::assertNotNull($this->election->getWinner('Minimax Margin')); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										176
									
								
								include/condorcet/Tests/src/Algo/Methods/PairwiseTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								include/condorcet/Tests/src/Algo/Methods/PairwiseTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\RankedPairs; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class PairwiseTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election1; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election1 = new Election; | ||||
|  | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('A>B>C'); | ||||
|     } | ||||
|  | ||||
|     public function testPairwiseOffsetGet(): void | ||||
|     { | ||||
|         $pairwise = $this->election1->getPairwise(); | ||||
|  | ||||
|         self::assertIsArray($pairwise[1]); | ||||
|  | ||||
|         self::assertNull($pairwise[42]); | ||||
|     } | ||||
|  | ||||
|     public function testExplicitPairwise(): void | ||||
|     { | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'A' => [ | ||||
|                     'win' => [ | ||||
|                         'B' => 1, | ||||
|                         'C' => 1, | ||||
|                     ], | ||||
|                     'null' => [ | ||||
|                         'B' => 0, | ||||
|                         'C' => 0, | ||||
|                     ], | ||||
|                     'lose' => [ | ||||
|                         'B' => 0, | ||||
|                         'C' => 0, | ||||
|                     ], | ||||
|                 ], | ||||
|                 'B' => [ | ||||
|                     'win' => [ | ||||
|                         'A' => 0, | ||||
|                         'C' => 1, | ||||
|                     ], | ||||
|                     'null' => [ | ||||
|                         'A' => 0, | ||||
|                         'C' => 0, | ||||
|                     ], | ||||
|                     'lose' => [ | ||||
|                         'A' => 1, | ||||
|                         'C' => 0, | ||||
|                     ], | ||||
|                 ], | ||||
|                 'C' => [ | ||||
|                     'win' => [ | ||||
|                         'A' => 0, | ||||
|                         'B' => 0, | ||||
|                     ], | ||||
|                     'null' => [ | ||||
|                         'A' => 0, | ||||
|                         'B' => 0, | ||||
|                     ], | ||||
|                     'lose' => [ | ||||
|                         'A' => 1, | ||||
|                         'B' => 1, | ||||
|                     ], | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election1->getPairwise()->getExplicitPairwise() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testVotesWeight(): void | ||||
|     { | ||||
|         $electionOff = new Election; | ||||
|  | ||||
|         $electionOff->addCandidate('A'); | ||||
|         $electionOff->addCandidate('B'); | ||||
|         $electionOff->addCandidate('C'); | ||||
|         $electionOff->addCandidate('D'); | ||||
|  | ||||
|         $electionOff->addVote('A>B>C=D ^3'); | ||||
|         $electionOff->addVote('A>B>C=D ^4'); | ||||
|  | ||||
|         $electionOn = clone $electionOff; | ||||
|         $electionOn->allowsVoteWeight(true); | ||||
|  | ||||
|         self::assertNotSame($electionOff->getExplicitPairwise(), $electionOn->getExplicitPairwise()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 'A' => [ | ||||
|                     'win' => [ | ||||
|                         'B' => 7, | ||||
|                         'C' => 7, | ||||
|                         'D' => 7, | ||||
|                     ], | ||||
|                     'null' => [ | ||||
|                         'B' => 0, | ||||
|                         'C' => 0, | ||||
|                         'D' => 0, | ||||
|                     ], | ||||
|                     'lose' => [ | ||||
|                         'B' => 0, | ||||
|                         'C' => 0, | ||||
|                         'D' => 0, | ||||
|                     ], | ||||
|                 ], | ||||
|                 'B' => [ | ||||
|                     'win' => [ | ||||
|                         'A' => 0, | ||||
|                         'C' => 7, | ||||
|                         'D' => 7, | ||||
|                     ], | ||||
|                     'null' => [ | ||||
|                         'A' => 0, | ||||
|                         'C' => 0, | ||||
|                         'D' => 0, | ||||
|                     ], | ||||
|                     'lose' => [ | ||||
|                         'A' => 7, | ||||
|                         'C' => 0, | ||||
|                         'D' => 0, | ||||
|                     ], | ||||
|                 ], | ||||
|                 'C' => [ | ||||
|                     'win' => [ | ||||
|                         'A' => 0, | ||||
|                         'B' => 0, | ||||
|                         'D' => 0, | ||||
|                     ], | ||||
|                     'null' => [ | ||||
|                         'A' => 0, | ||||
|                         'B' => 0, | ||||
|                         'D' => 7, | ||||
|                     ], | ||||
|                     'lose' => [ | ||||
|                         'A' => 7, | ||||
|                         'B' => 7, | ||||
|                         'D' => 0, | ||||
|                     ], | ||||
|                 ], | ||||
|                 'D' => [ | ||||
|                     'win' => [ | ||||
|                         'A' => 0, | ||||
|                         'B' => 0, | ||||
|                         'C' => 0, | ||||
|                     ], | ||||
|                     'null' => [ | ||||
|                         'A' => 0, | ||||
|                         'B' => 0, | ||||
|                         'C' => 7, | ||||
|                     ], | ||||
|                     'lose' => [ | ||||
|                         'A' => 7, | ||||
|                         'B' => 7, | ||||
|                         'C' => 0, | ||||
|                     ], | ||||
|                 ], | ||||
|             ], | ||||
|             $electionOn->getPairwise()->getExplicitPairwise() | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,464 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\RankedPairs; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Candidate, Condorcet, Election}; | ||||
| use CondorcetPHP\Condorcet\Throwable\CandidatesMaxNumberReachedException; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class RankedPairsTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         # From https://fr.wikipedia.org/wiki/M%C3%A9thode_Condorcet_avec_rangement_des_paires_par_ordre_d%C3%A9croissant | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > C > B > E * 5 | ||||
|             A > D > E > C * 5 | ||||
|             B > E > D > A * 8 | ||||
|             C > A > B > E * 3 | ||||
|             C > A > E > B * 7 | ||||
|             C > B > A > D * 2 | ||||
|             D > C > E > B * 7 | ||||
|             E > B > A > D * 8 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('A', $this->election->getWinner('Ranked Pairs Winning')); | ||||
|  | ||||
|         $expected = [1 => 'A', | ||||
|             2 => 'C', | ||||
|             3 => 'E', | ||||
|             4 => 'B', | ||||
|             5 => 'D', ]; | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             unserialize('a:2:{s:5:"tally";a:10:{i:0;a:1:{i:0;a:5:{s:4:"from";s:1:"B";s:2:"to";s:1:"D";s:3:"win";i:33;s:8:"minority";i:12;s:6:"margin";i:21;}}i:1;a:1:{i:0;a:5:{s:4:"from";s:1:"E";s:2:"to";s:1:"D";s:3:"win";i:31;s:8:"minority";i:14;s:6:"margin";i:17;}}i:2;a:1:{i:0;a:5:{s:4:"from";s:1:"A";s:2:"to";s:1:"D";s:3:"win";i:30;s:8:"minority";i:15;s:6:"margin";i:15;}}i:3;a:1:{i:0;a:5:{s:4:"from";s:1:"C";s:2:"to";s:1:"B";s:3:"win";i:29;s:8:"minority";i:16;s:6:"margin";i:13;}}i:4;a:1:{i:0;a:5:{s:4:"from";s:1:"D";s:2:"to";s:1:"C";s:3:"win";i:28;s:8:"minority";i:17;s:6:"margin";i:11;}}i:5;a:1:{i:0;a:5:{s:4:"from";s:1:"E";s:2:"to";s:1:"B";s:3:"win";i:27;s:8:"minority";i:18;s:6:"margin";i:9;}}i:6;a:1:{i:0;a:5:{s:4:"from";s:1:"A";s:2:"to";s:1:"C";s:3:"win";i:26;s:8:"minority";i:19;s:6:"margin";i:7;}}i:7;a:1:{i:0;a:5:{s:4:"from";s:1:"B";s:2:"to";s:1:"A";s:3:"win";i:25;s:8:"minority";i:20;s:6:"margin";i:5;}}i:8;a:1:{i:0;a:5:{s:4:"from";s:1:"C";s:2:"to";s:1:"E";s:3:"win";i:24;s:8:"minority";i:21;s:6:"margin";i:3;}}i:9;a:1:{i:0;a:5:{s:4:"from";s:1:"E";s:2:"to";s:1:"A";s:3:"win";i:23;s:8:"minority";i:22;s:6:"margin";i:1;}}}s:4:"arcs";a:7:{i:0;a:2:{s:4:"from";s:1:"B";s:2:"to";s:1:"D";}i:1;a:2:{s:4:"from";s:1:"E";s:2:"to";s:1:"D";}i:2;a:2:{s:4:"from";s:1:"A";s:2:"to";s:1:"D";}i:3;a:2:{s:4:"from";s:1:"C";s:2:"to";s:1:"B";}i:4;a:2:{s:4:"from";s:1:"E";s:2:"to";s:1:"B";}i:5;a:2:{s:4:"from";s:1:"A";s:2:"to";s:1:"C";}i:6;a:2:{s:4:"from";s:1:"C";s:2:"to";s:1:"E";}}}'), | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getStats() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             unserialize('a:2:{s:5:"tally";a:10:{i:0;a:1:{i:0;a:5:{s:4:"from";s:1:"B";s:2:"to";s:1:"D";s:3:"win";i:33;s:8:"minority";i:12;s:6:"margin";i:21;}}i:1;a:1:{i:0;a:5:{s:4:"from";s:1:"E";s:2:"to";s:1:"D";s:3:"win";i:31;s:8:"minority";i:14;s:6:"margin";i:17;}}i:2;a:1:{i:0;a:5:{s:4:"from";s:1:"A";s:2:"to";s:1:"D";s:3:"win";i:30;s:8:"minority";i:15;s:6:"margin";i:15;}}i:3;a:1:{i:0;a:5:{s:4:"from";s:1:"C";s:2:"to";s:1:"B";s:3:"win";i:29;s:8:"minority";i:16;s:6:"margin";i:13;}}i:4;a:1:{i:0;a:5:{s:4:"from";s:1:"D";s:2:"to";s:1:"C";s:3:"win";i:28;s:8:"minority";i:17;s:6:"margin";i:11;}}i:5;a:1:{i:0;a:5:{s:4:"from";s:1:"E";s:2:"to";s:1:"B";s:3:"win";i:27;s:8:"minority";i:18;s:6:"margin";i:9;}}i:6;a:1:{i:0;a:5:{s:4:"from";s:1:"A";s:2:"to";s:1:"C";s:3:"win";i:26;s:8:"minority";i:19;s:6:"margin";i:7;}}i:7;a:1:{i:0;a:5:{s:4:"from";s:1:"B";s:2:"to";s:1:"A";s:3:"win";i:25;s:8:"minority";i:20;s:6:"margin";i:5;}}i:8;a:1:{i:0;a:5:{s:4:"from";s:1:"C";s:2:"to";s:1:"E";s:3:"win";i:24;s:8:"minority";i:21;s:6:"margin";i:3;}}i:9;a:1:{i:0;a:5:{s:4:"from";s:1:"E";s:2:"to";s:1:"A";s:3:"win";i:23;s:8:"minority";i:22;s:6:"margin";i:1;}}}s:4:"arcs";a:7:{i:0;a:2:{s:4:"from";s:1:"B";s:2:"to";s:1:"D";}i:1;a:2:{s:4:"from";s:1:"E";s:2:"to";s:1:"D";}i:2;a:2:{s:4:"from";s:1:"A";s:2:"to";s:1:"D";}i:3;a:2:{s:4:"from";s:1:"C";s:2:"to";s:1:"B";}i:4;a:2:{s:4:"from";s:1:"E";s:2:"to";s:1:"B";}i:5;a:2:{s:4:"from";s:1:"A";s:2:"to";s:1:"C";}i:6;a:2:{s:4:"from";s:1:"C";s:2:"to";s:1:"E";}}}'), | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Ranked_pairs | ||||
|  | ||||
|         $this->election->addCandidate('Memphis'); | ||||
|         $this->election->addCandidate('Nashville'); | ||||
|         $this->election->addCandidate('Knoxville'); | ||||
|         $this->election->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Memphis > Nashville > Chattanooga * 42 | ||||
|             Nashville > Chattanooga > Knoxville * 26 | ||||
|             Chattanooga > Knoxville > Nashville * 15 | ||||
|             Knoxville > Chattanooga > Nashville * 17 | ||||
|         '); | ||||
|  | ||||
|         $expected = [1 => 'Nashville', | ||||
|             2 => 'Chattanooga', | ||||
|             3 => 'Knoxville', | ||||
|             4 => 'Memphis', ]; | ||||
|  | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             unserialize('a:2:{s:5:"tally";a:3:{i:0;a:1:{i:0;a:5:{s:4:"from";s:11:"Chattanooga";s:2:"to";s:9:"Knoxville";s:3:"win";i:83;s:8:"minority";i:17;s:6:"margin";i:66;}}i:1;a:2:{i:0;a:5:{s:4:"from";s:9:"Nashville";s:2:"to";s:9:"Knoxville";s:3:"win";i:68;s:8:"minority";i:32;s:6:"margin";i:36;}i:1;a:5:{s:4:"from";s:9:"Nashville";s:2:"to";s:11:"Chattanooga";s:3:"win";i:68;s:8:"minority";i:32;s:6:"margin";i:36;}}i:2;a:3:{i:0;a:5:{s:4:"from";s:9:"Nashville";s:2:"to";s:7:"Memphis";s:3:"win";i:58;s:8:"minority";i:42;s:6:"margin";i:16;}i:1;a:5:{s:4:"from";s:9:"Knoxville";s:2:"to";s:7:"Memphis";s:3:"win";i:58;s:8:"minority";i:42;s:6:"margin";i:16;}i:2;a:5:{s:4:"from";s:11:"Chattanooga";s:2:"to";s:7:"Memphis";s:3:"win";i:58;s:8:"minority";i:42;s:6:"margin";i:16;}}}s:4:"arcs";a:6:{i:0;a:2:{s:4:"from";s:11:"Chattanooga";s:2:"to";s:9:"Knoxville";}i:1;a:2:{s:4:"from";s:9:"Nashville";s:2:"to";s:9:"Knoxville";}i:2;a:2:{s:4:"from";s:9:"Nashville";s:2:"to";s:11:"Chattanooga";}i:3;a:2:{s:4:"from";s:9:"Nashville";s:2:"to";s:7:"Memphis";}i:4;a:2:{s:4:"from";s:9:"Knoxville";s:2:"to";s:7:"Memphis";}i:5;a:2:{s:4:"from";s:11:"Chattanooga";s:2:"to";s:7:"Memphis";}}}'), | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getStats() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             unserialize('a:2:{s:5:"tally";a:3:{i:0;a:1:{i:0;a:5:{s:4:"from";s:11:"Chattanooga";s:2:"to";s:9:"Knoxville";s:3:"win";i:83;s:8:"minority";i:17;s:6:"margin";i:66;}}i:1;a:2:{i:0;a:5:{s:4:"from";s:9:"Nashville";s:2:"to";s:9:"Knoxville";s:3:"win";i:68;s:8:"minority";i:32;s:6:"margin";i:36;}i:1;a:5:{s:4:"from";s:9:"Nashville";s:2:"to";s:11:"Chattanooga";s:3:"win";i:68;s:8:"minority";i:32;s:6:"margin";i:36;}}i:2;a:3:{i:0;a:5:{s:4:"from";s:9:"Nashville";s:2:"to";s:7:"Memphis";s:3:"win";i:58;s:8:"minority";i:42;s:6:"margin";i:16;}i:1;a:5:{s:4:"from";s:9:"Knoxville";s:2:"to";s:7:"Memphis";s:3:"win";i:58;s:8:"minority";i:42;s:6:"margin";i:16;}i:2;a:5:{s:4:"from";s:11:"Chattanooga";s:2:"to";s:7:"Memphis";s:3:"win";i:58;s:8:"minority";i:42;s:6:"margin";i:16;}}}s:4:"arcs";a:6:{i:0;a:2:{s:4:"from";s:11:"Chattanooga";s:2:"to";s:9:"Knoxville";}i:1;a:2:{s:4:"from";s:9:"Nashville";s:2:"to";s:9:"Knoxville";}i:2;a:2:{s:4:"from";s:9:"Nashville";s:2:"to";s:11:"Chattanooga";}i:3;a:2:{s:4:"from";s:9:"Nashville";s:2:"to";s:7:"Memphis";}i:4;a:2:{s:4:"from";s:9:"Knoxville";s:2:"to";s:7:"Memphis";}i:5;a:2:{s:4:"from";s:11:"Chattanooga";s:2:"to";s:7:"Memphis";}}}'), | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         # from http://www.cs.wustl.edu/~legrand/rbvote/desc.html | ||||
|  | ||||
|         $this->election->addCandidate('Abby'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|         $this->election->addCandidate('Cora'); | ||||
|         $this->election->addCandidate('Dave'); | ||||
|         $this->election->addCandidate('Erin'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Abby>Cora>Erin>Dave>Brad * 98 | ||||
|             Brad>Abby>Erin>Cora>Dave * 64 | ||||
|             Brad>Abby>Erin>Dave>Cora * 12 | ||||
|             Brad>Erin>Abby>Cora>Dave * 98 | ||||
|             Brad>Erin>Abby>Dave>Cora * 13 | ||||
|             Brad>Erin>Dave>Abby>Cora * 125 | ||||
|             Cora>Abby>Erin>Dave>Brad * 124 | ||||
|             Cora>Erin>Abby>Dave>Brad * 76 | ||||
|             Dave>Abby>Brad>Erin>Cora * 21 | ||||
|             Dave>Brad>Abby>Erin>Cora * 30 | ||||
|             Dave>Brad>Erin>Cora>Abby * 98 | ||||
|             Dave>Cora>Abby>Brad>Erin * 139 | ||||
|             Dave>Cora>Brad>Abby>Erin * 23 | ||||
|         '); | ||||
|  | ||||
|         $expected =[1 => 'Brad', | ||||
|             2 => 'Abby', | ||||
|             3 => 'Erin', | ||||
|             4 => 'Dave', | ||||
|             5 => 'Cora', ]; | ||||
|  | ||||
|         self::assertEquals('Brad', $this->election->getWinner('Ranked Pairs Winning')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_4(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Ranked_pairs | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B * 68 | ||||
|             B > C * 72 | ||||
|             C > A * 52 | ||||
|         '); | ||||
|  | ||||
|         // Not supporting not ranked candidate | ||||
|         self::assertNotEquals('A', $this->election->getWinner('Ranked Pairs Winning')); | ||||
|  | ||||
|         // Supporting not ranked candidate | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         self::assertEquals('A', $this->election->getWinner('Ranked Pairs Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_5(): void | ||||
|     { | ||||
|         # From http://ericgorr.net/condorcet/rankedpairs/example1/ | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C * 7 | ||||
|             B > A > C * 5 | ||||
|             C > A > B * 4 | ||||
|             B > C > A * 2 | ||||
|         '); | ||||
|  | ||||
|         $expected = [1 => 'A', | ||||
|             2 => 'B', | ||||
|             3 => 'C', ]; | ||||
|  | ||||
|         self::assertEquals('A', $this->election->getWinner('Ranked Pairs Winning')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_6(): void | ||||
|     { | ||||
|         # From http://ericgorr.net/condorcet/rankedpairs/example2/ | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C * 40 | ||||
|             B > C > A * 35 | ||||
|             C > A > B * 25 | ||||
|         '); | ||||
|  | ||||
|         $expected = [1 => 'A', | ||||
|             2 => 'B', | ||||
|             3 => 'C', ]; | ||||
|  | ||||
|         self::assertEquals('A', $this->election->getWinner('Ranked Pairs Winning')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_7(): void | ||||
|     { | ||||
|         # From http://ericgorr.net/condorcet/rankedpairs/example3/ | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C * 7 | ||||
|             B > A > C * 7 | ||||
|             C > A > B * 2 | ||||
|             C > B > A * 2 | ||||
|         '); | ||||
|  | ||||
|         $expected =  [1 => ['A', 'B'], | ||||
|             2 => 'C', ]; | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_8(): void | ||||
|     { | ||||
|         # From http://ericgorr.net/condorcet/rankedpairs/example4/ | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>D>C>B*12 | ||||
|             B>A>C>D*3 | ||||
|             B>C>A>D*25 | ||||
|             C>B>A>D*21 | ||||
|             D>A>B>C*12 | ||||
|             D>A>C>B*21 | ||||
|             D>B>A>C*6 | ||||
|         '); | ||||
|  | ||||
|         $expected = [1 => 'B', | ||||
|             2 => 'A', | ||||
|             3 => 'D', | ||||
|             4 => 'C', ]; | ||||
|  | ||||
|         self::assertEquals('B', $this->election->getWinner('Ranked Pairs Winning')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             unserialize('a:2:{s:5:"tally";a:4:{i:0;a:1:{i:0;a:5:{s:4:"from";s:1:"A";s:2:"to";s:1:"D";s:3:"win";i:61;s:8:"minority";i:39;s:6:"margin";i:22;}}i:1;a:1:{i:0;a:5:{s:4:"from";s:1:"B";s:2:"to";s:1:"A";s:3:"win";i:55;s:8:"minority";i:45;s:6:"margin";i:10;}}i:2;a:2:{i:0;a:5:{s:4:"from";s:1:"A";s:2:"to";s:1:"C";s:3:"win";i:54;s:8:"minority";i:46;s:6:"margin";i:8;}i:1;a:5:{s:4:"from";s:1:"C";s:2:"to";s:1:"B";s:3:"win";i:54;s:8:"minority";i:46;s:6:"margin";i:8;}}i:3;a:2:{i:0;a:5:{s:4:"from";s:1:"D";s:2:"to";s:1:"B";s:3:"win";i:51;s:8:"minority";i:49;s:6:"margin";i:2;}i:1;a:5:{s:4:"from";s:1:"D";s:2:"to";s:1:"C";s:3:"win";i:51;s:8:"minority";i:49;s:6:"margin";i:2;}}}s:4:"arcs";a:3:{i:0;a:2:{s:4:"from";s:1:"A";s:2:"to";s:1:"D";}i:1;a:2:{s:4:"from";s:1:"B";s:2:"to";s:1:"A";}i:2;a:2:{s:4:"from";s:1:"D";s:2:"to";s:1:"C";}}}'), | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getStats() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             unserialize('a:2:{s:5:"tally";a:4:{i:0;a:1:{i:0;a:5:{s:4:"from";s:1:"A";s:2:"to";s:1:"D";s:3:"win";i:61;s:8:"minority";i:39;s:6:"margin";i:22;}}i:1;a:1:{i:0;a:5:{s:4:"from";s:1:"B";s:2:"to";s:1:"A";s:3:"win";i:55;s:8:"minority";i:45;s:6:"margin";i:10;}}i:2;a:2:{i:0;a:5:{s:4:"from";s:1:"A";s:2:"to";s:1:"C";s:3:"win";i:54;s:8:"minority";i:46;s:6:"margin";i:8;}i:1;a:5:{s:4:"from";s:1:"C";s:2:"to";s:1:"B";s:3:"win";i:54;s:8:"minority";i:46;s:6:"margin";i:8;}}i:3;a:2:{i:0;a:5:{s:4:"from";s:1:"D";s:2:"to";s:1:"B";s:3:"win";i:51;s:8:"minority";i:49;s:6:"margin";i:2;}i:1;a:5:{s:4:"from";s:1:"D";s:2:"to";s:1:"C";s:3:"win";i:51;s:8:"minority";i:49;s:6:"margin";i:2;}}}s:4:"arcs";a:3:{i:0;a:2:{s:4:"from";s:1:"A";s:2:"to";s:1:"D";}i:1;a:2:{s:4:"from";s:1:"B";s:2:"to";s:1:"A";}i:2;a:2:{s:4:"from";s:1:"D";s:2:"to";s:1:"C";}}}'), | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_9(): void | ||||
|     { | ||||
|         # Test fix for rare bug | ||||
|  | ||||
|         for ($i=0; $i < 8; $i++) { | ||||
|             $this->election->addCandidate(); | ||||
|         } | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > E > B > H > G > F > D > C * 1 | ||||
|             B > F > E > H > C > A > G > D * 1 | ||||
|             G > F > B > C > D > E > H > A * 1 | ||||
|             H > A > B > F > E > C > D > G * 1 | ||||
|             B > H > A > E > G > F > D > C * 1 | ||||
|             E > D > H > C > B > A > F > G * 1 | ||||
|             C > A > F > B > E > D > H > G * 1 | ||||
|             G > H > D > C > E > F > B > A * 1 | ||||
|             F > E > H > A > B > C > G > D * 1 | ||||
|             D > B > F > C > G > E > A > H * 1 | ||||
|             H > G > A > E > B > C > F > D * 1 | ||||
|             E > D > G > F > A > B > H > C * 1 | ||||
|             C > D > G > A > E > H > B > F * 1 | ||||
|             H > C > B > G > A > D > F > E * 1 | ||||
|             C > B > G > A > D > H > F > E * 1 | ||||
|             B > D > F > H > G > E > A > C * 1 | ||||
|             B > C > E > F > G > H > D > A * 1 | ||||
|             C > G > H > F > D > E > A > B * 1 | ||||
|             E > A > H > C > F > D > G > B * 1 | ||||
|             C > D > G > H > B > A > E > F * 1 | ||||
|             B > D > A > C > G > F > E > H * 1 | ||||
|             C > A > B > G > E > D > H > F * 1 | ||||
|             E > G > H > A > D > C > F > B * 1 | ||||
|             F > G > B > H > E > C > D > A * 1 | ||||
|             A > H > D > C > F > E > B > G * 1 | ||||
|            '); | ||||
|  | ||||
|         self::assertEquals('B', $this->election->getWinner('Ranked Pairs Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_10(): void | ||||
|     { | ||||
|         # Tideman: Independence of Clones as a Criterion for Voting Rules (1987) | ||||
|         # Example 5 | ||||
|  | ||||
|         $this->election->addCandidate('v'); | ||||
|         $this->election->addCandidate('w'); | ||||
|         $this->election->addCandidate('x'); | ||||
|         $this->election->addCandidate('y'); | ||||
|         $this->election->addCandidate('z'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             v>w>x>y>z*7 | ||||
|             z>y>v>w>x*3 | ||||
|             y>z>w>x>v*6 | ||||
|             w>x>v>z>y*3 | ||||
|             z>x>v>w>y*5 | ||||
|             y>x>v>w>z*3 | ||||
|         '); | ||||
|  | ||||
|  | ||||
|         self::assertEquals('v', $this->election->getWinner('Ranked Pairs Winning')); | ||||
|  | ||||
|         $expected = [1 => 'v', | ||||
|             2 => 'w', | ||||
|             3 => 'x', | ||||
|             4 => 'y', | ||||
|             5 => 'z', ]; | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $expected, | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_11(): void | ||||
|     { | ||||
|         # From http://rangevoting.org/WinningVotes.htmls | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             B > C > A * 9 | ||||
|             C = A > B * 6 | ||||
|             A > B > C * 5 | ||||
|         '); | ||||
|  | ||||
|         self::assertNotEquals( | ||||
|             $this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true), | ||||
|             $this->election->getResult('Ranked Pairs Margin')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testMaxCandidates(): never | ||||
|     { | ||||
|         $this->expectException(CandidatesMaxNumberReachedException::class); | ||||
|         $this->expectExceptionMessage("Maximum number of candidates reached: The method 'Ranked Pairs Winning' is configured to accept only 60 candidates"); | ||||
|  | ||||
|         for ($i=0; $i < 61; $i++) { | ||||
|             $this->election->addCandidate(); | ||||
|         } | ||||
|  | ||||
|         $this->election->parseVotes('A'); | ||||
|  | ||||
|         $this->election->getWinner('Ranked Pairs Winning'); | ||||
|     } | ||||
|  | ||||
|     // public function testResult_stressTests (): void | ||||
|     // { | ||||
|     //     $rounds = 1; | ||||
|     //     $candidates = 332; | ||||
|     //     $votes = 500; | ||||
|  | ||||
|     //     # Test fix for rare bug | ||||
|     //     for ($j=0; $j < $rounds; $j++) { | ||||
|     //         $this->election = new Election; | ||||
|  | ||||
|     //         for ($i=0; $i < $candidates ; $i++) { | ||||
|     //             $this->election->addCandidate(); | ||||
|     //         } | ||||
|  | ||||
|  | ||||
|     //         $VoteModel = $this->election->getCandidatesList(); | ||||
|     //         \shuffle($VoteModel); | ||||
|  | ||||
|     //         for ($i = 0 ; $i < $votes ; $i++) { | ||||
|     //             \shuffle($VoteModel); | ||||
|     //             $this->election->addVote( $VoteModel ); | ||||
|     //         } | ||||
|  | ||||
|     //         \var_dump($j); | ||||
|  | ||||
|     //         \var_dump($this->election->getVotesListAsString()); | ||||
|  | ||||
|     //         \var_dump($this->election->getResult('Ranked Pairs Winning')->getResultAsArray(true)); | ||||
|  | ||||
|     //         self::assertEquals(true,true); | ||||
|     //     } | ||||
|     // } | ||||
| } | ||||
							
								
								
									
										319
									
								
								include/condorcet/Tests/src/Algo/Methods/STV/CPO_StvTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								include/condorcet/Tests/src/Algo/Methods/STV/CPO_StvTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,319 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\STV; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Algo\Methods\STV\CPO_STV; | ||||
| use CondorcetPHP\Condorcet\Algo\StatsVerbosity; | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use CondorcetPHP\Condorcet\Algo\Tools\StvQuotas; | ||||
| use CondorcetPHP\Condorcet\Throwable\MethodLimitReachedException; | ||||
| use CondorcetPHP\Condorcet\Tools\Converters\CondorcetElectionFormat; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CPO_StvTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     protected function tearDown(): void | ||||
|     { | ||||
|         $this->election->setMethodOption('STV', 'Quota', StvQuotas::DROOP); | ||||
|         $this->election->setMethodOption('CPO STV', 'Quota', StvQuotas::HAGENBACH_BISCHOFF); | ||||
|         $this->election->setMethodOption('CPO STV', 'CondorcetCompletionMethod', CPO_STV::DEFAULT_METHODS_CHAINING); | ||||
|         $this->election->setMethodOption('CPO STV', 'TieBreakerMethods', CPO_STV::DEFAULT_METHODS_CHAINING); | ||||
|     } | ||||
|  | ||||
|     # From https://en.wikipedia.org/wiki/CPO-STV | ||||
|     public function testCPO1(): void | ||||
|     { | ||||
|         $this->election->setStatsVerbosity(StatsVerbosity::FULL); | ||||
|  | ||||
|         $this->election->addCandidate('Andrea'); // key 0 | ||||
|         $this->election->addCandidate('Brad'); // key 1 | ||||
|         $this->election->addCandidate('Carter'); // key 2 | ||||
|         $this->election->addCandidate('Delilah'); // key 3 | ||||
|         $this->election->addCandidate('Scott'); // key 4 | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Andrea ^25 | ||||
|             Carter > Brad > Delilah ^34 | ||||
|             Brad > Delilah ^7 | ||||
|             Delilah > Brad ^8 | ||||
|             Delilah > Scott ^5 | ||||
|             Scott > Delilah ^21 | ||||
|         '); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(3); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Carter', | ||||
|                 2 => 'Andrea', | ||||
|                 3 => 'Delilah', | ||||
|             ], | ||||
|             $this->election->getResult('CPO STV')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         $stats = $this->election->getResult('CPO STV')->getStats(); | ||||
|  | ||||
|         self::assertSame(25.0, $stats['Votes Needed to Win']); | ||||
|         self::assertSame(['Andrea'=> 25.0, | ||||
|             'Brad'=> 7.0, | ||||
|             'Carter'=> 34.0, | ||||
|             'Delilah'=> 13.0, | ||||
|             'Scott'=> 21.0, | ||||
|         ], $stats['Initial Score Table']); | ||||
|  | ||||
|         self::assertSame(['Andrea', 'Carter'], $stats['Candidates elected from first round']); | ||||
|         self::assertSame(['Brad', 'Delilah', 'Scott'], $stats['Candidates eliminated from first round']); | ||||
|  | ||||
|         self::assertSame([ | ||||
|             ['Andrea', 'Carter', 'Scott'], | ||||
|             ['Andrea', 'Carter', 'Delilah'], | ||||
|             ['Andrea', 'Brad', 'Carter'], | ||||
|         ], $stats['Outcomes']); | ||||
|  | ||||
|         self::assertSame('Schulze Margin', $stats['Completion Method']); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['Outcome N° 0 compared to Outcome N° 1' => [ | ||||
|                 'candidates_excluded' => [ | ||||
|                     0 => 'Brad', | ||||
|                 ], | ||||
|                 'scores_after_exclusion' => [ | ||||
|                     'Andrea' => 25.0, | ||||
|                     'Carter' => 34.0, | ||||
|                     'Delilah' => 20.0, | ||||
|                     'Scott' => 21.0, | ||||
|                 ], | ||||
|                 'scores_after_surplus' => [ | ||||
|                     'Andrea' => 25.0, | ||||
|                     'Carter' => 25.0, | ||||
|                     'Delilah' => 29.0, | ||||
|                     'Scott' => 21.0, | ||||
|                 ], | ||||
|                 'outcomes_scores' => [ | ||||
|                     0 => 71.0, | ||||
|                     1 => 79.0, | ||||
|                 ], | ||||
|             ], | ||||
|                 'Outcome N° 0 compared to Outcome N° 2' => [ | ||||
|                     'candidates_excluded' => [ | ||||
|                         0 => 'Delilah', | ||||
|                     ], | ||||
|                     'scores_after_exclusion' => [ | ||||
|                         'Andrea' => 25.0, | ||||
|                         'Brad' => 15.0, | ||||
|                         'Carter' => 34.0, | ||||
|                         'Scott' => 26.0, | ||||
|                     ], | ||||
|                     'scores_after_surplus' => [ | ||||
|                         'Andrea' => 25.0, | ||||
|                         'Brad' => 24.0, | ||||
|                         'Carter' => 25.0, | ||||
|                         'Scott' => 26.0, | ||||
|                     ], | ||||
|                     'outcomes_scores' => [ | ||||
|                         0 => 76.0, | ||||
|                         2 => 74.0, | ||||
|                     ], | ||||
|                 ], | ||||
|                 'Outcome N° 1 compared to Outcome N° 2' => [ | ||||
|                     'candidates_excluded' => [ | ||||
|                         0 => 'Scott', | ||||
|                     ], | ||||
|                     'scores_after_exclusion' => [ | ||||
|                         'Andrea' => 25.0, | ||||
|                         'Brad' => 7.0, | ||||
|                         'Carter' => 34.0, | ||||
|                         'Delilah' => 34.0, | ||||
|                     ], | ||||
|                     'scores_after_surplus' => [ | ||||
|                         'Andrea' => 25.0, | ||||
|                         'Brad' => 16.0, | ||||
|                         'Carter' => 25.0, | ||||
|                         'Delilah' => 34.0, | ||||
|                     ], | ||||
|                     'outcomes_scores' => [ | ||||
|                         1 => 84.0, | ||||
|                         2 => 66.0, | ||||
|                     ], | ||||
|                 ], | ||||
|             ], | ||||
|             $stats['Outcomes Comparison'] | ||||
|         ); | ||||
|  | ||||
|         self::assertArrayHasKey('Condorcet Completion Method Stats', $stats); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     # From https://electowiki.org/wiki/CPO-STV | ||||
|     public function testCPO2(): void | ||||
|     { | ||||
|         // $this->election->setStatsVerbosity(StatsVerbosity::FULL); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $file = new \SplTempFileObject(-1); | ||||
|         $file->fwrite(<<<'CVOTES' | ||||
|             #/Number of Seats: 3 | ||||
|             Escher ^ 100 | ||||
|             Andre>Nader>Gore ^ 110 | ||||
|             Nader>Gore ^ 18 | ||||
|             Gore>Nader ^ 21 | ||||
|             Gore>Bush ^ 6 | ||||
|             Bush>Gore ^ 45 | ||||
|             CVOTES); | ||||
|  | ||||
|         $cef = new CondorcetElectionFormat($file); | ||||
|  | ||||
|         $cef->setDataToAnElection($this->election); | ||||
|  | ||||
|         $this->election->setMethodOption('CPO-STV', 'Quota', StvQuotas::HARE); | ||||
|  | ||||
|         self::assertSame('Andre > Escher > Gore', $this->election->getResult('CPO STV')->getResultAsString()); | ||||
|  | ||||
|         self::assertSame((float) 100, $this->election->getResult('CPO STV')->getStats()['Votes Needed to Win']); | ||||
|     } | ||||
|  | ||||
|     # From https://electowiki.org/wiki/CPO-STV | ||||
|     public function testCPO3(): void | ||||
|     { | ||||
|         $this->election->setStatsVerbosity(StatsVerbosity::FULL); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $file = new \SplTempFileObject(-1); | ||||
|         $file->fwrite(<<<'CVOTES' | ||||
|             #/Number of Seats: 2 | ||||
|             A>B>C>D * 5 | ||||
|             A>C>B>D * 17 | ||||
|             D * 8 | ||||
|             CVOTES); | ||||
|  | ||||
|         $cef = new CondorcetElectionFormat($file); | ||||
|  | ||||
|         $cef->setDataToAnElection($this->election); | ||||
|  | ||||
|         $this->election->setMethodOption('CPO-STV', 'Quota', StvQuotas::DROOP); | ||||
|  | ||||
|         self::assertSame('A > C', $this->election->getResult('CPO STV')->getResultAsString()); | ||||
|  | ||||
|         self::assertSame((float) 11, $this->election->getResult('CPO STV')->getStats()['Votes Needed to Win']); | ||||
|         self::assertSame([0=>19.0, 2=>22.0], $this->election->getResult('CPO STV')->getStats()['Outcomes Comparison']['Outcome N° 0 compared to Outcome N° 2']['outcomes_scores']); | ||||
|         self::assertSame([0=>19.0, 1=>22.0], $this->election->getResult('CPO STV')->getStats()['Outcomes Comparison']['Outcome N° 0 compared to Outcome N° 1']['outcomes_scores']); | ||||
|         self::assertSame([1=>19.5, 2=>13.5], $this->election->getResult('CPO STV')->getStats()['Outcomes Comparison']['Outcome N° 1 compared to Outcome N° 2']['outcomes_scores']); | ||||
|     } | ||||
|  | ||||
|     public function testLessOrEqualCandidatesThanSeats(): void | ||||
|     { | ||||
|         $expectedRanking = [ | ||||
|             1 => 'Memphis', | ||||
|             2 => 'Nashville', | ||||
|             3 => 'Chattanooga', | ||||
|             4 => 'Knoxville', | ||||
|         ]; | ||||
|  | ||||
|         // Ref | ||||
|         $this->election->setNumberOfSeats(4); | ||||
|  | ||||
|         $this->election->addCandidate('Memphis'); | ||||
|         $this->election->addCandidate('Nashville'); | ||||
|         $this->election->addCandidate('Knoxville'); | ||||
|         $this->election->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $this->election->parseVotes(' Memphis * 4 | ||||
|                                     Nashville * 3 | ||||
|                                     Chattanooga * 2 | ||||
|                                     Knoxville * 1'); | ||||
|  | ||||
|         self::assertSame($expectedRanking, $this->election->getResult('CPO STV')->getResultAsArray(true)); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(5); | ||||
|  | ||||
|         self::assertSame($expectedRanking, $this->election->getResult('CPO STV')->getResultAsArray(true)); | ||||
|     } | ||||
|  | ||||
|     public function testEquality1(): void | ||||
|     { | ||||
|         $this->election->setNumberOfSeats(2); | ||||
|  | ||||
|         $this->election->parseCandidates('A;B;C'); | ||||
|  | ||||
|         $this->election->addVote('A>B>C'); | ||||
|         $this->election->addVote('B>A>C'); | ||||
|         $this->election->addVote('B>C>A'); | ||||
|         $this->election->addVote('A>B>C'); | ||||
|  | ||||
|         self::assertSame([1=>['A', 'B']], $this->election->getResult('CPO STV')->getResultAsArray(true)); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(3); | ||||
|  | ||||
|         self::assertSame([1=>['A', 'B'], 3=> 'C'], $this->election->getResult('CPO STV')->getResultAsArray(true)); | ||||
|     } | ||||
|  | ||||
|     public function testEquality2(): void | ||||
|     { | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         $this->election->setNumberOfSeats(3); | ||||
|  | ||||
|         $this->election->parseCandidates('A;B;C;D'); | ||||
|  | ||||
|         $this->election->addVote('A>B>C>D'); | ||||
|         $this->election->addVote('A>B>D>C'); | ||||
|  | ||||
|         self::assertSame([1=>'A', 2=>['B', 'D']], $this->election->getResult('CPO STV')->getResultAsArray(true)); | ||||
|     } | ||||
|  | ||||
|     public function testLimit1(): void | ||||
|     { | ||||
|         $this->expectException(MethodLimitReachedException::class); | ||||
|         $this->expectExceptionMessage('CPO-STV is currently limited to 12000 comparisons in order to avoid unreasonable deadlocks due to non-polyminial runtime aspects of the algorithm. Consult the manual to increase or remove this limit.'); | ||||
|  | ||||
|  | ||||
|         $this->election->setNumberOfSeats(10); | ||||
|         $this->election->parseCandidates('1;2;3;4;5;6;7;8;9;10;11;12;13;14;15'); | ||||
|         $this->election->addVote('1>2'); | ||||
|  | ||||
|         $this->election->getResult('CPO STV'); | ||||
|     } | ||||
|  | ||||
|     public function testCPO40Candidates(): void | ||||
|     { | ||||
|         $this->expectException(MethodLimitReachedException::class); | ||||
|         $this->expectExceptionMessage('CPO-STV is currently limited to 12000 comparisons in order to avoid unreasonable deadlocks due to non-polyminial runtime aspects of the algorithm. Consult the manual to increase or remove this limit.'); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         $this->election->setNumberOfSeats((int) (40 / 3)); | ||||
|  | ||||
|         $candidates = []; | ||||
|         for ($i=0; $i < 40; $i++) { | ||||
|             $candidates[] = $this->election->addCandidate(); | ||||
|         } | ||||
|  | ||||
|         if (version_compare(\PHP_VERSION, '8.2') >= 0) { | ||||
|             $randomizer = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar('CondorcetReproductibleRandomSeed')); | ||||
|  | ||||
|             $shuffle = static fn (array $candidates): array => $randomizer->shuffleArray($candidates); | ||||
|         } else { | ||||
|             $shuffle = static function (array $candidates): array { | ||||
|                 $newCandidates = $candidates; | ||||
|                 shuffle($newCandidates); | ||||
|                 return $newCandidates; | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         for ($i = 0; $i < 100; $i++) { | ||||
|             $this->election->addVote($shuffle($candidates)); | ||||
|         } | ||||
|  | ||||
|         $this->election->getResult('CPO STV')->getResultAsString(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,477 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\STV; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use CondorcetPHP\Condorcet\Throwable\StvQuotaNotImplementedException; | ||||
| use CondorcetPHP\Condorcet\Algo\Methods\STV\SingleTransferableVote; | ||||
| use CondorcetPHP\Condorcet\Algo\StatsVerbosity; | ||||
| use CondorcetPHP\Condorcet\Algo\Tools\StvQuotas; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class SingleTransferableVoteTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     protected function tearDown(): void | ||||
|     { | ||||
|         $this->election->setMethodOption('STV', 'Quota', StvQuotas::DROOP); | ||||
|     } | ||||
|  | ||||
|     public function testQuotaOption(): never | ||||
|     { | ||||
|         self::assertSame(StvQuotas::DROOP, StvQuotas::make('droop')); | ||||
|  | ||||
|         self::assertTrue( | ||||
|             $this->election->setMethodOption('STV', 'Quota', StvQuotas::make('Hagenbach-Bischoff')) | ||||
|         ); | ||||
|  | ||||
|         $this->expectException(StvQuotaNotImplementedException::class); | ||||
|         $this->expectExceptionMessage('This STV quota is not implemented: "another quota"'); | ||||
|  | ||||
|         $this->election->setMethodOption('STV', 'Quota', StvQuotas::make('another quota')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         # From https://fr.wikipedia.org/wiki/Scrutin_%C3%A0_vote_unique_transf%C3%A9rable | ||||
|  | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('A'); | ||||
|  | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>B>C>D ^ 28 | ||||
|             A>C>D>B ^ 14 | ||||
|             B>C>A>D ^ 15 | ||||
|             C>A>B>D ^ 17 | ||||
|             D>B>C>A ^ 26 | ||||
|         '); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(2); | ||||
|  | ||||
|  | ||||
|         self::assertEqualsWithDelta( | ||||
|             [ | ||||
|                 1 => [ | ||||
|                     'A' => 42.0, | ||||
|                     'D' => 26.0, | ||||
|                     'C' => 17.0, | ||||
|                     'B' => 15.0, | ||||
|                 ], | ||||
|                 2 => [ | ||||
|                     'D' => 26.0, | ||||
|                     'B' => 20.33333333333, | ||||
|                     'C' => 19.66666666667, | ||||
|                 ], | ||||
|                 3 => [ | ||||
|                     'B' => 37.33333333333, | ||||
|                     'D' => 28.66666666667, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getStats()['rounds'], | ||||
|             1 / (0.1 ** SingleTransferableVote::DECIMAL_PRECISION) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             (float) 34, | ||||
|             $this->election->getResult('STV')->getStats()['Votes Needed to Win'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => 'B', | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Single_transferable_vote | ||||
|  | ||||
|         $this->election->addCandidate('Orange'); | ||||
|         $this->election->addCandidate('Pear'); | ||||
|         $this->election->addCandidate('Chocolate'); | ||||
|         $this->election->addCandidate('Strawberry'); | ||||
|         $this->election->addCandidate('Hamburger'); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(3); | ||||
|  | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Orange ^ 4 | ||||
|             Pear > Orange * 2 | ||||
|             Chocolate > Strawberry * 8 | ||||
|             Chocolate > Hamburger * 4 | ||||
|             Strawberry | ||||
|             Hamburger | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             (float) 6, | ||||
|             $this->election->getResult('STV')->getStats()['Votes Needed to Win'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => [ | ||||
|                     'Chocolate' => 12.0, | ||||
|                     'Orange' => 4.0, | ||||
|                     'Pear' => 2.0, | ||||
|                     'Strawberry' => 1.0, | ||||
|                     'Hamburger' => 1.0, | ||||
|                 ], | ||||
|                 2 => [ | ||||
|                     'Strawberry' => 5.0, | ||||
|                     'Orange' => 4.0, | ||||
|                     'Hamburger' => 3.0, | ||||
|                     'Pear' => 2.0, | ||||
|                 ], | ||||
|                 3 => [ | ||||
|                     'Orange' => 6.0, | ||||
|                     'Strawberry' => 5.0, | ||||
|                     'Hamburger' => 3.0, | ||||
|                 ], | ||||
|                 4 => [ | ||||
|                     'Strawberry' => 5.0, | ||||
|                     'Hamburger' => 3.0, | ||||
|                 ], | ||||
|                 5 => [ | ||||
|                     'Strawberry' => 5.0, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getStats()['rounds'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Chocolate', | ||||
|                 2 => 'Orange', | ||||
|                 3 => 'Strawberry', | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_3(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Schulze_STV | ||||
|  | ||||
|         $this->election->addCandidate('Andrea'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|         $this->election->addCandidate('Carter'); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(2); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Andrea > Brad > Carter ^ 12 | ||||
|             Andrea > Carter > Brad ^ 26 | ||||
|             Andrea > Carter > Brad ^ 12 | ||||
|             Carter > Andrea > Brad ^ 13 | ||||
|             Brad ^ 27 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             (float) 31, | ||||
|             $this->election->getResult('STV')->getStats()['Votes Needed to Win'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => [ | ||||
|                     'Andrea' => 50.0, | ||||
|                     'Brad' => 27.0, | ||||
|                     'Carter' => 13.0, | ||||
|                 ], | ||||
|                 2 => [ | ||||
|                     'Brad' => 31.56, | ||||
|                     'Carter' => 27.44, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getStats()['rounds'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Andrea', | ||||
|                 2 => 'Brad', | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         $this->election->setStatsVerbosity(StatsVerbosity::LOW); | ||||
|         self::assertArrayNotHasKey('rounds', $this->election->getResult('STV')->getStats()); | ||||
|     } | ||||
|  | ||||
|     public function testResult_4(): void | ||||
|     { | ||||
|         # From https://it.wikipedia.org/wiki/Voto_singolo_trasferibile | ||||
|  | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('A'); | ||||
|  | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A>D ^ 40 | ||||
|             B>A ^ 10 | ||||
|             B>C ^ 5 | ||||
|             C>B ^ 25 | ||||
|             D>B ^ 20 | ||||
|         '); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(3); | ||||
|  | ||||
|         self::assertSame( | ||||
|             (float) 26, | ||||
|             $this->election->getResult('STV')->getStats()['Votes Needed to Win'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'A', | ||||
|                 2 => 'D', | ||||
|                 3 => 'C', | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function testResult_AlternativeQuotas1(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Hagenbach-Bischoff_quota | ||||
|  | ||||
|         $this->election->addCandidate('Andrea'); | ||||
|         $this->election->addCandidate('Carter'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Andrea > Carter ^45 | ||||
|             Carter ^25 | ||||
|             Brad ^30 | ||||
|         '); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(2); | ||||
|         $this->election->setMethodOption('STV', 'Quota', StvQuotas::make('Hagenbach-Bischoff')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             round(33 + 1/3, SingleTransferableVote::DECIMAL_PRECISION, \PHP_ROUND_HALF_DOWN), | ||||
|             $this->election->getResult('STV')->getStats()['Votes Needed to Win'] | ||||
|         ); | ||||
|  | ||||
|         self::assertEqualsWithDelta( | ||||
|             [ | ||||
|                 1 => [ | ||||
|                     'Andrea' => 45.0, | ||||
|                     'Brad' => 30.0, | ||||
|                     'Carter' => 25.0, | ||||
|                 ], | ||||
|                 2 => [ | ||||
|                     'Carter' => 36.0 + 2/3, | ||||
|                     'Brad' => 30.0, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getStats()['rounds'], | ||||
|             delta: 1 / (0.1 ** SingleTransferableVote::DECIMAL_PRECISION) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Andrea', | ||||
|                 2 => 'Carter', | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertsame($this->election->getResult('STV')->getMethodOptions()['Quota'], StvQuotas::make('Hagenbach-Bischoff')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_AlternativeQuotas2(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Imperiali_quota | ||||
|  | ||||
|         $this->election->addCandidate('Andrea'); | ||||
|         $this->election->addCandidate('Carter'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Andrea > Carter ^65 | ||||
|             Carter ^15 | ||||
|             Brad ^20 | ||||
|         '); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(2); | ||||
|         $this->election->setMethodOption('STV', 'Quota', StvQuotas::IMPERIALI); | ||||
|  | ||||
|         self::assertSame( | ||||
|             (float) (100 / (2 + 2)), | ||||
|             $this->election->getResult('STV')->getStats()['Votes Needed to Win'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => [ | ||||
|                     'Andrea' => 65.0, | ||||
|                     'Brad' => 20.0, | ||||
|                     'Carter' => 15.0, | ||||
|                 ], | ||||
|                 2 => [ | ||||
|                     'Carter' => 55.0, | ||||
|                     'Brad' => 20.0, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getStats()['rounds'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Andrea', | ||||
|                 2 => 'Carter', | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertsame($this->election->getResult('STV')->getMethodOptions()['Quota'], StvQuotas::make('Imperiali quota')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_AlternativeQuotas3(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/Hare_quota | ||||
|  | ||||
|         $this->election->addCandidate('Andrea'); | ||||
|         $this->election->addCandidate('Carter'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Andrea > Carter ^60 | ||||
|             Carter ^14 | ||||
|             Brad ^26 | ||||
|         '); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(2); | ||||
|         $this->election->setMethodOption('STV', 'Quota', StvQuotas::make('Hare quota')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             (float) (100 / 2), | ||||
|             $this->election->getResult('STV')->getStats()['Votes Needed to Win'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => [ | ||||
|                     'Andrea' => 60.0, | ||||
|                     'Brad' => 26.0, | ||||
|                     'Carter' => 14.0, | ||||
|                 ], | ||||
|                 2 => [ | ||||
|                     'Brad' => 26.0, | ||||
|                     'Carter' => 24.0, | ||||
|                 ], | ||||
|                 3 => ['Brad' => 26.0], | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getStats()['rounds'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Andrea', | ||||
|                 2 => 'Brad', | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getResultAsArray(true) | ||||
|         ); | ||||
|  | ||||
|         self::assertsame($this->election->getResult('STV')->getMethodOptions()['Quota'], StvQuotas::HARE); | ||||
|     } | ||||
|  | ||||
|     public function testResult_AlternativeQuotas4(): void | ||||
|     { | ||||
|         # From https://en.wikipedia.org/wiki/CPO-STV | ||||
|  | ||||
|         $this->election->addCandidate('Andrea'); | ||||
|         $this->election->addCandidate('Carter'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|         $this->election->addCandidate('Delilah'); | ||||
|         $this->election->addCandidate('Scott'); | ||||
|  | ||||
|         $this->election->setImplicitRanking(false); | ||||
|         $this->election->allowsVoteWeight(true); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Andrea ^25 | ||||
|             Carter > Brad > Delilah ^34 | ||||
|             Brad > Delilah ^7 | ||||
|             Delilah > Brad ^8 | ||||
|             Delilah > Scott ^5 | ||||
|             Scott > Delilah ^21 | ||||
|         '); | ||||
|  | ||||
|         $this->election->setNumberOfSeats(3); | ||||
|         $this->election->setMethodOption('STV', 'Quota', StvQuotas::HAGENBACH_BISCHOFF); | ||||
|  | ||||
|         self::assertSame( | ||||
|             (float) 25, | ||||
|             $this->election->getResult('STV')->getStats()['Votes Needed to Win'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => [ | ||||
|                     'Carter' => 34.0, | ||||
|                     'Andrea' => 25.0, | ||||
|                     'Scott' => 21.0, | ||||
|                     'Delilah' => 13.0, | ||||
|                     'Brad' => 7.0, | ||||
|                 ], | ||||
|                 2 => [ | ||||
|                     'Scott' => 21.0, | ||||
|                     'Brad' => 16.0, | ||||
|                     'Delilah' => 13.0, | ||||
|                 ], | ||||
|                 3 => [ | ||||
|                     'Scott' => 26.0, | ||||
|                     'Brad' => 24.0, | ||||
|                 ], | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getStats()['rounds'] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [ | ||||
|                 1 => 'Carter', | ||||
|                 2 => 'Andrea', | ||||
|                 3 => 'Scott', | ||||
|             ], | ||||
|             $this->election->getResult('STV')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										367
									
								
								include/condorcet/Tests/src/Algo/Methods/Schulze/SchulzeTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										367
									
								
								include/condorcet/Tests/src/Algo/Methods/Schulze/SchulzeTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,367 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Methods\Schulze; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class SchulzeTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testResult_1(): void | ||||
|     { | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|         $this->election->addCandidate('D'); | ||||
|         $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > C > B > E * 5 | ||||
|             A > D > E > C * 5 | ||||
|             B > E > D > A * 8 | ||||
|             C > A > B > E * 3 | ||||
|             C > A > E > B * 7 | ||||
|             C > B > A > D * 2 | ||||
|             D > C > E > B * 7 | ||||
|             E > B > A > D * 8 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('E', $this->election->getWinner('Schulze Winning')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'E', | ||||
|                 2 => 'A', | ||||
|                 3 => 'C', | ||||
|                 4 => 'B', | ||||
|                 5 => 'D', ], | ||||
|             $this->election->getResult('Schulze Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testResult_2(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C * 3 | ||||
|             D > A > B * 2 | ||||
|             D > B > C * 2 | ||||
|             C > B > D * 2 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame([$candidateB, $candidateD], $this->election->getWinner('Schulze Winning')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['B', 'D'], | ||||
|                 2 => ['A', 'C'], ], | ||||
|             $this->election->getResult('Schulze Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_1(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > C > D * 8 | ||||
|             B > A > D * 2 | ||||
|             C > D > B * 4 | ||||
|             D > B > A * 4 | ||||
|             D > C > B * 3 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($candidateD, $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_2(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C * 3 | ||||
|             C > B > D * 2 | ||||
|             D > A > B * 2 | ||||
|             D > B > C * 2 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame([$candidateB, $candidateD], $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_3(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C * 12 | ||||
|             A > D > B * 6 | ||||
|             B > C > D * 9 | ||||
|             C > D > A * 15 | ||||
|             D > B > A * 21 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($candidateD, $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_4(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > C > D * 6 | ||||
|             B > A > D * 1 | ||||
|             C > B > D * 3 | ||||
|             D > B > A * 3 | ||||
|             D > C > B * 2 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame([$candidateA, $candidateD], $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_5(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|         $candidateE = $this->election->addCandidate('E'); | ||||
|         $candidateF = $this->election->addCandidate('F'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > D > E > B > C * 3 | ||||
|             B > F > E > C > D * 3 | ||||
|             C > A > B > F > D * 4 | ||||
|             D > B > C > E > F * 1 | ||||
|             D > E > F > A > B * 4 | ||||
|             E > C > B > D > F * 2 | ||||
|             F > A > C > D > B * 2 | ||||
|         '); | ||||
|  | ||||
|         # Situation 1 | ||||
|         self::assertSame($candidateA, $this->election->getWinner('Schulze Winning')); | ||||
|  | ||||
|         # Situation 2 | ||||
|         $this->election->parseVotes('A > E > F > C > B * 2'); | ||||
|  | ||||
|         self::assertSame($candidateD, $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_6_situation_1(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > D * 3 | ||||
|             A > D > B * 5 | ||||
|             A > D > C * 1 | ||||
|             B > A > D * 2 | ||||
|             B > D > C * 2 | ||||
|             C > A > B * 4 | ||||
|             C > B > A * 6 | ||||
|             D > B > C * 2 | ||||
|             D > C > A * 5 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($candidateA, $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_6_situation_2(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|         $candidateE = $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > D > E * 3 | ||||
|             A > D > E > B * 5 | ||||
|             A > D > E > C * 1 | ||||
|             B > A > D > E * 2 | ||||
|             B > D > E > C * 2 | ||||
|             C > A > B > D * 4 | ||||
|             C > B > A > D * 6 | ||||
|             D > B > E > C * 2 | ||||
|             D > E > C > A * 5 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($candidateB, $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_7(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > B > C > D * 6 | ||||
|             A = B * 8 | ||||
|             A = C * 8 | ||||
|             A = C > D * 18 | ||||
|             A = C = D * 8 | ||||
|             B * 40 | ||||
|             C > B > D * 4 | ||||
|             C > D > A * 9 | ||||
|             C = D * 8 | ||||
|             D > A > B * 14 | ||||
|             D > B > C * 11 | ||||
|             D > C > A * 4 | ||||
|         '); | ||||
|  | ||||
|         # Margin | ||||
|         self::assertSame($candidateA, $this->election->getWinner('Schulze Margin')); | ||||
|  | ||||
|         # Ratio | ||||
|         self::assertSame($candidateB, $this->election->getWinner('Schulze Ratio')); | ||||
|  | ||||
|         # Winning | ||||
|         self::assertSame($candidateD, $this->election->getWinner('Schulze Winning')); | ||||
|  | ||||
|         # Losing Votes | ||||
|         // not implemented | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_8(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|         $candidateE = $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > D > B > E * 9 | ||||
|             B > C > A > D * 6 | ||||
|             B > C > D > E * 5 | ||||
|             C > D > B > E * 2 | ||||
|             D > E > C > B * 6 | ||||
|             E > A > C > B * 14 | ||||
|             E > C > A > B * 2 | ||||
|             E > D > A > C * 1 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($candidateB, $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_9(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|         $candidateE = $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > D > B > E * 9 | ||||
|             B > A > C > E * 1 | ||||
|             C > B > A > D * 6 | ||||
|             C > D > B > E * 2 | ||||
|             C > D > E > A * 5 | ||||
|             D > E > C > A * 6 | ||||
|             E > B > A > C * 14 | ||||
|             E > B > C > A * 2 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($candidateE, $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeOfficialExampleResult_10(): void | ||||
|     { | ||||
|         $candidateA = $this->election->addCandidate('A'); | ||||
|         $candidateB = $this->election->addCandidate('B'); | ||||
|         $candidateC = $this->election->addCandidate('C'); | ||||
|         $candidateD = $this->election->addCandidate('D'); | ||||
|         $candidateE = $this->election->addCandidate('E'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             A > C > B > E * 5 | ||||
|             A > D > E > C * 5 | ||||
|             B > E > D > A * 8 | ||||
|             C > A > B > E * 3 | ||||
|             C > A > E > B * 7 | ||||
|             C > B > A > D * 2 | ||||
|             D > C > E > B * 7 | ||||
|             E > B > A > D * 8 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame($candidateE, $this->election->getWinner('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     public function testResult_11(): void | ||||
|     { | ||||
|         $this->election->addCandidate('Abby'); | ||||
|         $this->election->addCandidate('Brad'); | ||||
|         $this->election->addCandidate('Cora'); | ||||
|         $this->election->addCandidate('Dave'); | ||||
|         $this->election->addCandidate('Erin'); | ||||
|  | ||||
|         $this->election->parseVotes(' | ||||
|             Abby>Cora>Erin>Dave>Brad * 98 | ||||
|             Brad>Abby>Erin>Cora>Dave * 64 | ||||
|             Brad>Abby>Erin>Dave>Cora * 12 | ||||
|             Brad>Erin>Abby>Cora>Dave * 98 | ||||
|             Brad>Erin>Abby>Dave>Cora * 13 | ||||
|             Brad>Erin>Dave>Abby>Cora * 125 | ||||
|             Cora>Abby>Erin>Dave>Brad * 124 | ||||
|             Cora>Erin>Abby>Dave>Brad * 76 | ||||
|             Dave>Abby>Brad>Erin>Cora * 21 | ||||
|             Dave>Brad>Abby>Erin>Cora * 30 | ||||
|             Dave>Brad>Erin>Cora>Abby * 98 | ||||
|             Dave>Cora>Abby>Brad>Erin * 139 | ||||
|             Dave>Cora>Brad>Abby>Erin * 23 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('Abby', $this->election->getWinner('Schulze Winning')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'Abby', | ||||
|                 2 => 'Brad', | ||||
|                 3 => 'Erin', | ||||
|                 4 => 'Dave', | ||||
|                 5 => 'Cora', ], | ||||
|             $this->election->getResult('Schulze Winning')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testSchulzeRatioEquality(): void | ||||
|     { | ||||
|         $this->election->parseCandidates('A;B;C;D'); | ||||
|         $this->election->parseVotes('A>B=C>D * 10'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'A', | ||||
|                 2 => ['B', 'C'], | ||||
|                 3 => 'D', | ||||
|             ], | ||||
|             $this->election->getResult('Schulze Ratio')->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										76
									
								
								include/condorcet/Tests/src/Algo/Tools/CombinationsTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								include/condorcet/Tests/src/Algo/Tools/CombinationsTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Tools; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Algo\Tools\Combinations; | ||||
| use CondorcetPHP\Condorcet\Throwable\Internal\{CondorcetInternalException, IntegerOverflowException}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CombinationsTest extends TestCase | ||||
| { | ||||
|     protected function tearDown(): void | ||||
|     { | ||||
|         Combinations::$useBigIntegerIfAvailable = true; | ||||
|     } | ||||
|  | ||||
|     public function testCountPossibleCombinationsResultWithBigInt(): void | ||||
|     { | ||||
|         // Usual permutation for CPO STV 11 candidates and 3 seats left | ||||
|         self::assertSame( | ||||
|             13_530, | ||||
|             Combinations::getPossibleCountOfCombinations( | ||||
|                 count: Combinations::getPossibleCountOfCombinations( | ||||
|                     count: 11, | ||||
|                     length: 3 | ||||
|                 ), | ||||
|                 length: 2 | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame(2_598_960, Combinations::getPossibleCountOfCombinations(52, 5)); // Card Game | ||||
|  | ||||
|         self::assertSame(4_367_914_309_753_280, Combinations::getPossibleCountOfCombinations(78, 15)); // Tarot Card Game - 5 players | ||||
|         self::assertSame(212_566_476_905_162_380, Combinations::getPossibleCountOfCombinations(78, 18)); // Tarot Card Game - 4 players | ||||
|  | ||||
|         $this->expectException(IntegerOverflowException::class); | ||||
|         Combinations::getPossibleCountOfCombinations(78, 24); // Tarot Card Game - 3 players - Result is 79_065_487_387_985_398_300, it's above PHP_MAX_INT | ||||
|     } | ||||
|  | ||||
|     public function testCountPossibleCombinationsResultWithoutBigInt(): void | ||||
|     { | ||||
|         Combinations::$useBigIntegerIfAvailable = false; | ||||
|  | ||||
|         // Usual permutation for CPO STV 11 candidates and 3 seats left | ||||
|         self::assertSame( | ||||
|             13_530, | ||||
|             Combinations::getPossibleCountOfCombinations( | ||||
|                 count: Combinations::getPossibleCountOfCombinations( | ||||
|                     count: 11, | ||||
|                     length: 3 | ||||
|                 ), | ||||
|                 length: 2 | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame(2_598_960, Combinations::getPossibleCountOfCombinations(52, 5)); // Card Game - Result is - 4_367_914_309_753_280 | ||||
|  | ||||
|         $this->expectException(IntegerOverflowException::class); | ||||
|         Combinations::getPossibleCountOfCombinations(78, 15); // Tarot Card Game - 5 players -  - Result is 4_367_914_309_753_280, it's NOT above PHP_MAX_INT but the intermediate calculations are. | ||||
|     } | ||||
|  | ||||
|     public function testCountPossibleCombinationsBadParameters1(): void | ||||
|     { | ||||
|         $this->expectException(CondorcetInternalException::class); | ||||
|  | ||||
|         Combinations::getPossibleCountOfCombinations(2, 3); | ||||
|     } | ||||
|  | ||||
|     public function testIntegerOverflow(): void | ||||
|     { | ||||
|         $this->expectException(IntegerOverflowException::class); | ||||
|  | ||||
|         Combinations::getPossibleCountOfCombinations(\PHP_INT_MAX - 1, 2); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										75
									
								
								include/condorcet/Tests/src/Algo/Tools/PermutationsTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								include/condorcet/Tests/src/Algo/Tools/PermutationsTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Tools; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Algo\Tools\Permutations; | ||||
| use CondorcetPHP\Condorcet\Throwable\Internal\{CondorcetInternalException, IntegerOverflowException}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class PermutationsTest extends TestCase | ||||
| { | ||||
|     protected function tearDown(): void | ||||
|     { | ||||
|         Permutations::$useBigIntegerIfAvailable = true; | ||||
|     } | ||||
|  | ||||
|     public function testCountPossiblePermutations(): void | ||||
|     { | ||||
|         self::assertSame(6, Permutations::getPossibleCountOfPermutations(3)); | ||||
|  | ||||
|         Permutations::$useBigIntegerIfAvailable = false; | ||||
|         self::assertSame(6, Permutations::getPossibleCountOfPermutations(3)); | ||||
|  | ||||
|         $this->expectException(CondorcetInternalException::class); | ||||
|         Permutations::getPossibleCountOfPermutations(0); | ||||
|     } | ||||
|  | ||||
|     public function testIntegerOverflowWithBigInt(): void | ||||
|     { | ||||
|         $this->expectException(IntegerOverflowException::class); | ||||
|         Permutations::getPossibleCountOfPermutations(42); | ||||
|     } | ||||
|  | ||||
|     public function testIntegerOverflowWithoutBigInt(): void | ||||
|     { | ||||
|         Permutations::$useBigIntegerIfAvailable = false; | ||||
|  | ||||
|         $this->expectException(IntegerOverflowException::class); | ||||
|         Permutations::getPossibleCountOfPermutations(42); | ||||
|     } | ||||
|  | ||||
|     public function testPermutationsAllResultsFor3(): void | ||||
|     { | ||||
|         $p = new Permutations([0, 1, 2]); | ||||
|  | ||||
|         $r = $p->getResults(); | ||||
|  | ||||
|         self::assertInstanceOf(\SplFixedArray::class, $r); | ||||
|         self::assertSame(6, $r->getSize()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [[1=>0, 2=>1, 3=>2], | ||||
|                 [1=>1, 2=>0, 3=>2], | ||||
|                 [1=>1, 2=>2, 3=>0], | ||||
|                 [1=>0, 2=>2, 3=>1], | ||||
|                 [1=>2, 2=>0, 3=>1], | ||||
|                 [1=>2, 2=>1, 3=>0], | ||||
|             ], | ||||
|             $r->toArray() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testPermutationsAllResultsFor1(): void | ||||
|     { | ||||
|         $p = new Permutations([42]); | ||||
|  | ||||
|         $r = $p->getResults(); | ||||
|  | ||||
|         self::assertInstanceOf(\SplFixedArray::class, $r); | ||||
|         self::assertSame(1, $r->getSize()); | ||||
|  | ||||
|         self::assertSame([[1=>42]], $r->toArray()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										30
									
								
								include/condorcet/Tests/src/Algo/Tools/VirtualVoteTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								include/condorcet/Tests/src/Algo/Tools/VirtualVoteTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Algo\Tools; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Election, Vote}; | ||||
| use CondorcetPHP\Condorcet\Algo\Tools\VirtualVote; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class VirtualVoteTest extends TestCase | ||||
| { | ||||
|     public function testVirtualVote(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|         $election->parseCandidates('A;B;C'); | ||||
|  | ||||
|         $vote1 = new Vote('A>B>C'); | ||||
|         $election->addVote($vote1); | ||||
|  | ||||
|         $vote2 = VirtualVote::removeCandidates($vote1, ['B']); | ||||
|  | ||||
|         self::assertNotSame($vote1->getSimpleRanking(), $vote2->getSimpleRanking()); | ||||
|         self::assertSame('A > C', $vote2->getSimpleRanking()); | ||||
|  | ||||
|         self::assertSame(1, $vote1->countLinks()); | ||||
|         self::assertSame(0, $vote2->countLinks()); | ||||
|         self::assertSame(1, $election->countVotes()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										176
									
								
								include/condorcet/Tests/src/CandidateTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								include/condorcet/Tests/src/CandidateTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Candidate, Election}; | ||||
| use CondorcetPHP\Condorcet\Throwable\{CandidateExistsException, CandidateInvalidNameException}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CandidateTest extends TestCase | ||||
| { | ||||
|     private readonly Candidate $candidate1; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->candidate1 = new Candidate('candidate1.n1'); | ||||
|     } | ||||
|  | ||||
|     public function testCreateTimestamp(): void | ||||
|     { | ||||
|         self::assertEquals($this->candidate1->getCreateTimestamp(), $this->candidate1->getTimestamp()); | ||||
|     } | ||||
|  | ||||
|     public function testChangeName(): void | ||||
|     { | ||||
|         self::assertTrue($this->candidate1->setName('candidate1.n2')); | ||||
|  | ||||
|         self::assertEquals('candidate1.n2', $this->candidate1->getName()); | ||||
|  | ||||
|         self::assertLessThan($this->candidate1->getTimestamp(), $this->candidate1->getCreateTimestamp()); | ||||
|         self::assertCount(2, $this->candidate1->getHistory()); | ||||
|     } | ||||
|  | ||||
|     public function testTrimName(): void | ||||
|     { | ||||
|         $candidate = new Candidate(' candidateName '); | ||||
|         self::assertSame('candidateName', (string) $candidate); | ||||
|     } | ||||
|  | ||||
|     public function testMatchingAndTooLongName(): never | ||||
|     { | ||||
|         $name = ''; | ||||
|         while (mb_strlen($name) < Election::MAX_CANDIDATE_NAME_LENGTH) { | ||||
|             $name .= uniqid(); | ||||
|         } | ||||
|         $name = mb_substr($name, 0, Election::MAX_CANDIDATE_NAME_LENGTH); | ||||
|  | ||||
|         // The name is exactly as long as allowed. | ||||
|         $candidate = new Candidate($name); | ||||
|         $this->assertEquals($name, (string) $candidate); | ||||
|  | ||||
|         // Now the name is one character too long. | ||||
|         $name .= 'A'; | ||||
|  | ||||
|         $this->expectException(CandidateInvalidNameException::class); | ||||
|         $this->expectExceptionMessage("This name is not valid: {$name}"); | ||||
|  | ||||
|         new Candidate($name); | ||||
|     } | ||||
|  | ||||
|     public function testBadName(): never | ||||
|     { | ||||
|         $this->expectException(CandidateInvalidNameException::class); | ||||
|         $this->expectExceptionMessage('This name is not valid'); | ||||
|  | ||||
|         new Candidate('<$"'); | ||||
|     } | ||||
|  | ||||
|     public function testBadNameWithNewline(): never | ||||
|     { | ||||
|         $this->expectException(CandidateInvalidNameException::class); | ||||
|         $this->expectExceptionMessage('This name is not valid'); | ||||
|  | ||||
|         new Candidate("A name with\n a newline"); | ||||
|     } | ||||
|  | ||||
|     public function testCandidateBadClass(): never | ||||
|     { | ||||
|         $this->expectException(\TypeError::class); | ||||
|  | ||||
|         (new Election)->addCandidate(new \stdClass); | ||||
|     } | ||||
|  | ||||
|     public function testAddSameCandidate1(): never | ||||
|     { | ||||
|         $this->expectException(CandidateExistsException::class); | ||||
|         $this->expectExceptionMessage('This candidate already exists: Schizophrenic'); | ||||
|  | ||||
|         $election1 = new Election; | ||||
|  | ||||
|         $candidate = new Candidate('Schizophrenic'); | ||||
|  | ||||
|         $election1->addCandidate($candidate); | ||||
|         $election1->addCandidate($candidate); | ||||
|     } | ||||
|  | ||||
|     public function testAddSameCandidate2(): never | ||||
|     { | ||||
|         $this->expectException(CandidateExistsException::class); | ||||
|         $this->expectExceptionMessage('This candidate already exists: candidate1'); | ||||
|  | ||||
|         $election1 = new Election; | ||||
|  | ||||
|         $election1->parseCandidates('candidate1;candidate2;candidate1'); | ||||
|     } | ||||
|  | ||||
|     public function testAddSameCandidate3(): never | ||||
|     { | ||||
|         $this->expectException(CandidateExistsException::class); | ||||
|         $this->expectExceptionMessage('This candidate already exists: candidate1'); | ||||
|  | ||||
|         $election1 = new Election; | ||||
|  | ||||
|         $election1->addCandidate('candidate1'); | ||||
|         $election1->parseCandidates('candidate2;candidate1'); | ||||
|     } | ||||
|  | ||||
|     public function testAddSameCandidate4(): void | ||||
|     { | ||||
|         $election1 = new Election; | ||||
|  | ||||
|         $candidate1= $election1->addCandidate('candidate1'); | ||||
|  | ||||
|         try { | ||||
|             $election1->parseCandidates('candidate2;candidate1'); | ||||
|         } catch (\Exception) { | ||||
|         } | ||||
|  | ||||
|         self::assertsame([$candidate1], $election1->getCandidatesList()); | ||||
|     } | ||||
|  | ||||
|     public function testSameCandidateToMultipleElection(): void | ||||
|     { | ||||
|         $this->expectException(CandidateExistsException::class); | ||||
|         $this->expectExceptionMessage("This candidate already exists: the name 'Debussy' is taken by another candidate"); | ||||
|  | ||||
|         $election1 = new Election; | ||||
|         $election2 = new Election; | ||||
|         $election3 = new Election; | ||||
|  | ||||
|         // Add candidate to election | ||||
|         self::assertSame($this->candidate1, $election1->addCandidate($this->candidate1)); | ||||
|         self::assertSame($this->candidate1, $election2->addCandidate($this->candidate1)); | ||||
|         self::assertSame($this->candidate1, $election3->addCandidate($this->candidate1)); | ||||
|  | ||||
|         // Check Candidate Link | ||||
|         self::assertTrue($this->candidate1->haveLink($election1)); | ||||
|         self::assertTrue($this->candidate1->haveLink($election2)); | ||||
|         self::assertTrue($this->candidate1->haveLink($election3)); | ||||
|         self::assertCount(3, $this->candidate1->getLinks()); | ||||
|  | ||||
|         $election3->removeCandidates('candidate1.n1'); | ||||
|  | ||||
|         self::assertCount(2, $this->candidate1->getLinks()); | ||||
|  | ||||
|         // Add some conflicts | ||||
|         self::assertTrue($this->candidate1->setName('candidate1.n2')); | ||||
|         self::assertSame('candidate1.n2', $this->candidate1->getName()); | ||||
|         self::assertNotSame($this->candidate1, $election1->addCandidate('candidate1.n1')); | ||||
|  | ||||
|         $election2->addCandidate('Debussy'); | ||||
|         $this->candidate1->setName('Debussy'); | ||||
|     } | ||||
|  | ||||
|     public function testCloneCandidate(): void | ||||
|     { | ||||
|         ($election = new Election)->addCandidate($this->candidate1); | ||||
|  | ||||
|         self::assertsame(1, $this->candidate1->countLinks()); | ||||
|  | ||||
|         $cloneCandidate = clone $this->candidate1; | ||||
|  | ||||
|         self::assertsame(0, $cloneCandidate->countLinks()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										204
									
								
								include/condorcet/Tests/src/CondorcetTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								include/condorcet/Tests/src/CondorcetTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,204 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Condorcet, Result}; | ||||
| use CondorcetPHP\Condorcet\Algo\{Method, MethodInterface}; | ||||
| use CondorcetPHP\Condorcet\Throwable\AlgorithmException; | ||||
|  | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CondorcetTest extends TestCase | ||||
| { | ||||
|     public function testgetVersion(): void | ||||
|     { | ||||
|         self::assertSame(Condorcet::VERSION, CONDORCET::getVersion()); | ||||
|         self::assertMatchesRegularExpression('/^[1-9]+\.[0-9]+$/', CONDORCET::getVersion(true)); | ||||
|     } | ||||
|  | ||||
|     public function testAddExistingMethod(): void | ||||
|     { | ||||
|         $algoClassPath = Condorcet::getDefaultMethod(); | ||||
|  | ||||
|         self::assertEquals($algoClassPath, Condorcet::getMethodClass($algoClassPath)); | ||||
|  | ||||
|         self::assertFalse(Condorcet::addMethod($algoClassPath)); | ||||
|     } | ||||
|  | ||||
|     public function testBadClassMethod(): never | ||||
|     { | ||||
|         $this->expectException(AlgorithmException::class); | ||||
|         $this->expectExceptionMessage("The voting algorithm is not available: no class found for 'sjskkdlkkzksh'"); | ||||
|  | ||||
|         Condorcet::addMethod('sjskkdlkkzksh'); | ||||
|     } | ||||
|  | ||||
|     public function testAuthMethod(): void | ||||
|     { | ||||
|         self::assertFalse(Condorcet::isAuthMethod('skzljdpmzk')); | ||||
|         self::assertNull(Condorcet::getMethodClass('skzljdpmzk')); | ||||
|         self::assertSame(\CondorcetPHP\Condorcet\Algo\Methods\Schulze\SchulzeWinning::class, Condorcet::getMethodClass('Schulze Winning')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|       * @preserveGlobalState disabled | ||||
|       * @backupStaticAttributes disabled | ||||
|       * @runInSeparateProcess | ||||
|       */ | ||||
|     public function testAddMethod(): never | ||||
|     { | ||||
|         $this->expectException(AlgorithmException::class); | ||||
|         $this->expectExceptionMessage('The voting algorithm is not available: the given class is using an existing alias'); | ||||
|  | ||||
|         $algoClassPath = CondorcetTest_ValidAlgorithmName::class; | ||||
|  | ||||
|         self::assertTrue(Condorcet::addMethod($algoClassPath)); | ||||
|  | ||||
|         self::assertEquals($algoClassPath, Condorcet::getMethodClass($algoClassPath)); | ||||
|  | ||||
|         // Try to add existing alias | ||||
|         $algoClassPath = CondorcetTest_DuplicateAlgorithmAlias::class; | ||||
|  | ||||
|         self::assertFalse(Condorcet::addMethod($algoClassPath)); | ||||
|     } | ||||
|  | ||||
|     public function testAddUnvalidMethod(): never | ||||
|     { | ||||
|         $this->expectException(AlgorithmException::class); | ||||
|         $this->expectExceptionMessage('The voting algorithm is not available: the given class is not correct'); | ||||
|  | ||||
|         $algoClassPath = CondorcetTest_UnvalidAlgorithmName::class; | ||||
|  | ||||
|         self::assertFalse(Condorcet::addMethod($algoClassPath)); | ||||
|  | ||||
|         self::assertSame( | ||||
|             CondorcetTest_UnvalidAlgorithmName::class, | ||||
|             Condorcet::getMethodClass('FirstMethodName') | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testUnvalidDefaultMethod(): void | ||||
|     { | ||||
|         self::assertFalse(Condorcet::setDefaultMethod('dgfbdwcd')); | ||||
|     } | ||||
|  | ||||
|     public function testEmptyMethod(): never | ||||
|     { | ||||
|         $this->expectException(AlgorithmException::class); | ||||
|         $this->expectExceptionMessage('The voting algorithm is not available: no method name given'); | ||||
|  | ||||
|         Condorcet::isAuthMethod(''); | ||||
|     } | ||||
|  | ||||
|     public function testMethodAlias(): void | ||||
|     { | ||||
|         self::assertSame( | ||||
|             \CondorcetPHP\Condorcet\Algo\Methods\KemenyYoung\KemenyYoung::class, | ||||
|             Condorcet::getMethodClass('kemeny–Young') | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             \CondorcetPHP\Condorcet\Algo\Methods\KemenyYoung\KemenyYoung::class, | ||||
|             Condorcet::getMethodClass('Maximum likelihood Method') | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| class CondorcetTest_ValidAlgorithmName extends Method implements MethodInterface | ||||
| { | ||||
|     public const METHOD_NAME = ['FirstMethodName', 'Alias1', 'Alias_2', 'Alias 3']; | ||||
|  | ||||
|  | ||||
|     // Get the Result object | ||||
|     public function getResult($options = null): Result | ||||
|     { | ||||
|         // Cache | ||||
|         if ($this->Result !== null) { | ||||
|             return $this->Result; | ||||
|         } | ||||
|  | ||||
|         ////// | ||||
|  | ||||
|         // Ranking calculation | ||||
|         $this->makeRanking(); | ||||
|  | ||||
|         // Return | ||||
|         return $this->Result; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // Compute the Stats | ||||
|     protected function getStats(): array | ||||
|     { | ||||
|         return []; // You are free to do all you wants. Must be an array.; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /////////// COMPUTE /////////// | ||||
|  | ||||
|  | ||||
|     //:: ALGORITHM. ::// | ||||
|  | ||||
|     protected function makeRanking(): void | ||||
|     { | ||||
|         $this->selfElection->get()->getPairwise(); | ||||
|  | ||||
|         $result = [0=>1, 1=>2, 2=>3]; // Candidate must be valid candidates | ||||
|  | ||||
|         $this->Result = $this->createResult($result); | ||||
|     } | ||||
| } | ||||
|  | ||||
| class CondorcetTest_DuplicateAlgorithmAlias extends CondorcetTest_ValidAlgorithmName implements MethodInterface | ||||
| { | ||||
|     public const METHOD_NAME = ['SecondMethodName', 'Alias_2']; | ||||
| } | ||||
|  | ||||
|  | ||||
| class CondorcetTest_UnvalidAlgorithmName | ||||
| { | ||||
|     public const METHOD_NAME = ['FirstMethodName', 'Alias1', 'Alias_2', 'Alias 3']; | ||||
|  | ||||
|  | ||||
|     // Get the Result object | ||||
|     public function getResult($options = null): Result | ||||
|     { | ||||
|         // Cache | ||||
|         if ($thisResult !== null) { | ||||
|             return $this->Result; | ||||
|         } | ||||
|  | ||||
|         // Ranking calculation | ||||
|         $this->makeRanking(); | ||||
|  | ||||
|         // Return | ||||
|         return $this->Result; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // Compute the Stats | ||||
|     protected function getStats(): array | ||||
|     { | ||||
|         return []; // You are free to do all you wants. Must be an array.; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /////////// COMPUTE /////////// | ||||
|  | ||||
|  | ||||
|     //:: ALGORITHM. ::// | ||||
|  | ||||
|     protected function makeRanking(): void | ||||
|     { | ||||
|         $this->selfElection->getPairwise(); | ||||
|  | ||||
|         $result = [0=>0, 1=> [1, 2], 2=> 3]; // Candidate must be valid internal candidate key. | ||||
|  | ||||
|         $this->Result = $result; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								include/condorcet/Tests/src/CondorcetUtilTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								include/condorcet/Tests/src/CondorcetUtilTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Utils\CondorcetUtil; | ||||
| use CondorcetPHP\Condorcet\Vote; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CondorcetUtilTest extends TestCase | ||||
| { | ||||
|     public function testFormatVote(): void | ||||
|     { | ||||
|         $vote = new Vote('A>B>C'); | ||||
|  | ||||
|         $this->assertSame('A > B > C', CondorcetUtil::format($vote, true)); | ||||
|     } | ||||
|  | ||||
|     public function testDeleteComments(): void | ||||
|     { | ||||
|         $result = CondorcetUtil::prepareParse('A > B # This is a comment', false); | ||||
|  | ||||
|         $this->assertSame(['A > B'], $result); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								include/condorcet/Tests/src/CondorcetVersionTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								include/condorcet/Tests/src/CondorcetVersionTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Condorcet, Election}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CondorcetVersionTest extends TestCase | ||||
| { | ||||
|     public function testObjectVersion(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|  | ||||
|         self::assertSame(CONDORCET::getVersion(), $election->getObjectVersion()); | ||||
|         self::assertSame(CONDORCET::getVersion(true), $election->getObjectVersion(true)); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,404 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Console\Commands; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Console\Commands\ElectionCommand; | ||||
| use CondorcetPHP\Condorcet\Throwable\{CandidateExistsException, ResultRequestedWithoutVotesException}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
| use CondorcetPHP\Condorcet\Console\CondorcetApplication; | ||||
| use CondorcetPHP\Condorcet\Console\Style\CondorcetStyle; | ||||
| use Symfony\Component\Console\Output\OutputInterface; | ||||
| use Symfony\Component\Console\Tester\CommandTester; | ||||
|  | ||||
| class ElectionCommandTest extends TestCase | ||||
| { | ||||
|     private readonly CommandTester $electionCommand; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         CondorcetApplication::create(); | ||||
|  | ||||
|         $this->electionCommand = new CommandTester(CondorcetApplication::$SymfonyConsoleApplication->find('election')); | ||||
|     } | ||||
|  | ||||
|     public function testConsoleSimpleElection(): void | ||||
|     { | ||||
|         $this->electionCommand->execute( | ||||
|             [ | ||||
|                 '--candidates' => 'A;B;C', | ||||
|                 '--votes' => 'A>B>C;C>B>A;B>A>C', | ||||
|                 '--stats' => null, | ||||
|                 '--natural-condorcet' => null, | ||||
|                 '--allows-votes-weight' => null, | ||||
|                 '--no-tie' => null, | ||||
|                 '--list-votes' => null, | ||||
|                 '--deactivate-implicit-ranking' => null, | ||||
|                 '--show-pairwise' => null, | ||||
|             ], | ||||
|             [ | ||||
|                 'verbosity' => OutputInterface::VERBOSITY_VERBOSE, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertStringContainsString('3 candidates registered || 3 votes registered', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Schulze', $output); | ||||
|         self::assertStringContainsString('Registered candidates', $output); | ||||
|         self::assertStringContainsString('Stats - votes registration', $output); | ||||
|         self::assertStringContainsString('Registered Votes List', $output); | ||||
|         self::assertStringContainsString('Pairwise', $output); | ||||
|         self::assertStringContainsString('Stats:', $output); | ||||
|  | ||||
|         self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+TRUE/', $output); | ||||
|         self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+FALSE./', $output); | ||||
|         self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+FALSE/', $output); | ||||
|     } | ||||
|  | ||||
|     public function testConsoleSeats(): void | ||||
|     { | ||||
|         $this->electionCommand->execute( | ||||
|             [ | ||||
|                 '--candidates' => 'A;B;C', | ||||
|                 '--votes' => 'A>B>C;C>B>A;B>A>C', | ||||
|                 '--stats' => null, | ||||
|                 '--natural-condorcet' => null, | ||||
|                 '--allows-votes-weight' => null, | ||||
|                 '--no-tie' => null, | ||||
|                 '--list-votes' => null, | ||||
|                 '--deactivate-implicit-ranking' => null, | ||||
|                 '--show-pairwise' => null, | ||||
|  | ||||
|                 '--seats' => 42, | ||||
|                 'methods' => ['STV'], | ||||
|             ], | ||||
|             [ | ||||
|                 'verbosity' => OutputInterface::VERBOSITY_VERBOSE, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertStringContainsString('3 candidates registered || 3 votes registered', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Seats:', $output); | ||||
|         self::assertStringContainsString('42', $output); | ||||
|     } | ||||
|  | ||||
|     public function testQuotas(): void | ||||
|     { | ||||
|         $this->electionCommand->execute([ | ||||
|             '--candidates' => 'A;B;C', | ||||
|             '--votes' => 'A>B>C;C>B>A;B>A>C', | ||||
|  | ||||
|             'methods' => ['STV'], | ||||
|         ]); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+TRUE/', $output); | ||||
|         self::assertStringContainsString('Droop Quota', $output); | ||||
|  | ||||
|         $this->electionCommand->execute([ | ||||
|             '--candidates' => 'A;B;C', | ||||
|             '--votes' => 'A>B>C;C>B>A;B>A>C', | ||||
|  | ||||
|             'methods' => ['STV'], | ||||
|             '--quota' => 'imperiali', | ||||
|         ]); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertStringContainsString('Imperiali', $output); | ||||
|     } | ||||
|  | ||||
|     public function testConsoleAllMethodsArgument(): void | ||||
|     { | ||||
|         $this->electionCommand->execute([ | ||||
|             '--candidates' => 'A;B;C', | ||||
|             '--votes' => 'A>B>C;C>B>A;B>A>C', | ||||
|  | ||||
|             'methods' => ['all'], | ||||
|         ]); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|         // \var_dump($output); | ||||
|  | ||||
|         self::assertStringContainsString('Copeland', $output); | ||||
|     } | ||||
|  | ||||
|     public function testConsoleMultiplesMethods(): void | ||||
|     { | ||||
|         $this->electionCommand->execute([ | ||||
|             '--candidates' => 'A;B;C', | ||||
|             '--votes' => 'A>B>C;C>B>A;B>A>C', | ||||
|  | ||||
|             'methods' => ['Copeland', 'RankedPairs', 'Minimax'], | ||||
|         ]); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|         // \var_dump($output); | ||||
|  | ||||
|         self::assertStringContainsString('Copeland', $output); | ||||
|         self::assertStringContainsString('Ranked Pairs M', $output); | ||||
|         self::assertStringContainsString('Minimax Winning', $output); | ||||
|     } | ||||
|  | ||||
|     public function testConsoleFileInput(): void | ||||
|     { | ||||
|         $this->electionCommand->execute([ | ||||
|             '--candidates' => __DIR__.'/data.candidates', | ||||
|             '--votes' => __DIR__.'/data.votes', | ||||
|         ]); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|         // \var_dump($output); | ||||
|  | ||||
|         self::assertStringContainsString('Schulze', $output); | ||||
|         self::assertStringContainsString('A,B', $output); | ||||
|         self::assertStringContainsString('C '.CondorcetStyle::CONDORCET_LOSER_SYMBOL, $output); | ||||
|     } | ||||
|  | ||||
|     public function testInteractiveCommand(): void | ||||
|     { | ||||
|         $this->electionCommand->setInputs([ | ||||
|             'A', | ||||
|             'B', | ||||
|             'C', | ||||
|             '', | ||||
|             'A>B>C', | ||||
|             'B>A>C', | ||||
|             'A>C>B', | ||||
|             '', | ||||
|         ]); | ||||
|  | ||||
|         $this->electionCommand->execute([ | ||||
|             'command' => 'election', | ||||
|         ]); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|         // \var_dump($output); | ||||
|  | ||||
|         self::assertStringContainsString('Results: Schulze Winning', $output); | ||||
|     } | ||||
|  | ||||
|     public function testNonInteractionMode(): never | ||||
|     { | ||||
|         $this->expectException(ResultRequestedWithoutVotesException::class); | ||||
|         $this->expectExceptionMessage('The result cannot be requested without votes'); | ||||
|  | ||||
|         $this->electionCommand->execute([], ['interactive' => false]); | ||||
|  | ||||
|         // $output = $this->electionCommand->getDisplay(); | ||||
|         // \var_dump($output); | ||||
|     } | ||||
|  | ||||
|     public function testCustomizeVotesPerMb(): void | ||||
|     { | ||||
|         $this->electionCommand->execute([ | ||||
|             '--candidates' => 'A;B;C', | ||||
|             '--votes' => 'A>B>C;C>B>A;B>A>C', | ||||
|             '--votes-per-mb' => 42, | ||||
|         ]); | ||||
|  | ||||
|         self::assertSame(42, \CondorcetPHP\Condorcet\Console\Commands\ElectionCommand::$VotesPerMB); | ||||
|  | ||||
|         // $output = $this->electionCommand->getDisplay(); | ||||
|         // \var_dump($output); | ||||
|     } | ||||
|  | ||||
|     public function testVoteWithDb1(): void | ||||
|     { | ||||
|         ElectionCommand::$forceIniMemoryLimitTo = '128M'; | ||||
|  | ||||
|         $this->electionCommand->execute([ | ||||
|             '--candidates' => 'A;B;C', | ||||
|             '--votes-per-mb' => 1, | ||||
|             '--votes' => 'A>B>C * '.(((int) preg_replace('`[^0-9]`', '', ElectionCommand::$forceIniMemoryLimitTo)) + 1), # Must be superior to memory limit in MB | ||||
|         ], [ | ||||
|             'verbosity' => OutputInterface::VERBOSITY_DEBUG, | ||||
|         ]); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertMatchesRegularExpression('/Votes per Mb +1/', $output); | ||||
|         self::assertMatchesRegularExpression('/Db is used +yes, using path\\:/', $output); | ||||
|  | ||||
|         ElectionCommand::$forceIniMemoryLimitTo = null; | ||||
|  | ||||
|         # And absence of this error: unlink(path): Resource temporarily unavailable | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function testNaturalCondorcet(): void | ||||
|     { | ||||
|         $this->electionCommand->execute([ | ||||
|             '--candidates' => 'A;B;C', | ||||
|             '--votes' => 'A=B=C', | ||||
|             '--natural-condorcet' => true, | ||||
|         ]); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertStringContainsString(CondorcetStyle::CONDORCET_WINNER_SYMBOL.'  Condorcet Winner | -', $output); | ||||
|         self::assertStringContainsString(CondorcetStyle::CONDORCET_LOSER_SYMBOL.'  Condorcet Loser  | -', $output); | ||||
|     } | ||||
|  | ||||
|     public function testFromCondorcetElectionFormat_DoubleCandidates(): void | ||||
|     { | ||||
|         $this->expectException(CandidateExistsException::class); | ||||
|  | ||||
|         $this->electionCommand->execute( | ||||
|             [ | ||||
|                 '--candidates' => 'A;B;C', | ||||
|                 '--import-condorcet-election-format' => __DIR__.'/../../Tools/Converters/CondorcetElectionFormatData/test1.cvotes', | ||||
|             ], | ||||
|             [ | ||||
|                 'verbosity' => OutputInterface::VERBOSITY_VERBOSE, | ||||
|             ] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testFromCondorcetElectionFormat_ArgumentpriorityAndDoubleVoteArgument(): void | ||||
|     { | ||||
|         $this->electionCommand->execute( | ||||
|             [ | ||||
|                 '--import-condorcet-election-format' => __DIR__.'/../../Tools/Converters/CondorcetElectionFormatData/test1.cvotes', | ||||
|                 '--votes' => 'C>A', | ||||
|                 '--deactivate-implicit-ranking' => null, | ||||
|                 '--no-tie' => null, | ||||
|                 '--allows-votes-weight' => null, | ||||
|             ], | ||||
|             [ | ||||
|                 'verbosity' => OutputInterface::VERBOSITY_VERBOSE, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertStringContainsString('3 candidates registered || 2 votes registered', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Schulze', $output); | ||||
|         self::assertStringContainsString('Registered candidates', $output); | ||||
|         self::assertStringContainsString('Stats - votes registration', $output); | ||||
|  | ||||
|         self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+TRUE/', $output); | ||||
|         self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+FALSE./', $output); | ||||
|         self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+FALSE/', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Sum vote weight | 3', $output); | ||||
|     } | ||||
|  | ||||
|     public function testFromCondorcetElectionFormat_Arguments(): void | ||||
|     { | ||||
|         $this->electionCommand->execute( | ||||
|             [ | ||||
|                 '--import-condorcet-election-format' => __DIR__.'/../../Tools/Converters/CondorcetElectionFormatData/test2.cvotes', | ||||
|             ], | ||||
|             [ | ||||
|                 'verbosity' => OutputInterface::VERBOSITY_VERBOSE, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertStringContainsString('3 candidates registered || 2 votes registered', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Schulze', $output); | ||||
|         self::assertStringContainsString('Registered candidates', $output); | ||||
|         self::assertStringContainsString('Stats - votes registration', $output); | ||||
|  | ||||
|         self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+FALSE/', $output); | ||||
|         self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+FALSE./', $output); | ||||
|         self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+TRUE/', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Sum vote weight | 2', $output); | ||||
|  | ||||
|         self::assertStringContainsString('B '.CondorcetStyle::CONDORCET_WINNER_SYMBOL, $output); # Condorcet Winner | ||||
|     } | ||||
|  | ||||
|     public function testVoteWithDb_CondorcetElectionFormat(): void | ||||
|     { | ||||
|         ElectionCommand::$forceIniMemoryLimitTo = '128M'; | ||||
|  | ||||
|         $this->electionCommand->execute([ | ||||
|             '--votes-per-mb' => 1, | ||||
|             '--import-condorcet-election-format' => __DIR__.'/../../Tools/Converters/CondorcetElectionFormatData/test3.cvotes', | ||||
|         ], [ | ||||
|             'verbosity' => OutputInterface::VERBOSITY_DEBUG, | ||||
|         ]); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertMatchesRegularExpression('/Votes per Mb +1/', $output); | ||||
|         self::assertStringContainsString('Db is used', $output); | ||||
|         self::assertStringContainsString('yes, using path:', $output); | ||||
|  | ||||
|         ElectionCommand::$forceIniMemoryLimitTo = null; | ||||
|  | ||||
|         # And absence of this error: unlink(path): Resource temporarily unavailable | ||||
|     } | ||||
|  | ||||
|     public function testFromDebianFormat(): void | ||||
|     { | ||||
|         $this->electionCommand->execute( | ||||
|             [ | ||||
|                 '--import-debian-format' => __DIR__.'/../../Tools/Converters/DebianData/leader2020_tally.txt', | ||||
|                 'methods' => ['STV'], | ||||
|             ], | ||||
|             [ | ||||
|                 'verbosity' => OutputInterface::VERBOSITY_VERBOSE, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertStringContainsString('4 candidates registered || 339 votes registered', $output); | ||||
|  | ||||
|         self::assertStringContainsString('STV', $output); | ||||
|         self::assertStringContainsString('Registered candidates', $output); | ||||
|         self::assertStringContainsString('Stats - votes registration', $output); | ||||
|  | ||||
|         self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+FALSE/', $output); | ||||
|         self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+TRUE./', $output); | ||||
|         self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+TRUE/', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Sum vote weight | 339', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Jonathan Carter '.CondorcetStyle::CONDORCET_WINNER_SYMBOL, $output); # Condorcet Winner | ||||
|         self::assertMatchesRegularExpression('/Seats: *\| 1/', $output); | ||||
|     } | ||||
|  | ||||
|     public function testFromDavidHillFormat(): void | ||||
|     { | ||||
|         $this->electionCommand->execute( | ||||
|             [ | ||||
|                 '--import-david-hill-format' => __DIR__.'/../../Tools/Converters/TidemanData/A1.HIL', | ||||
|                 'methods' => ['STV'], | ||||
|             ], | ||||
|             [ | ||||
|                 'verbosity' => OutputInterface::VERBOSITY_VERBOSE, | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         $output = $this->electionCommand->getDisplay(); | ||||
|  | ||||
|         self::assertStringContainsString('10 candidates registered || 380 votes registered', $output); | ||||
|  | ||||
|         self::assertStringContainsString('STV', $output); | ||||
|         self::assertStringContainsString('Registered candidates', $output); | ||||
|         self::assertStringContainsString('Stats - votes registration', $output); | ||||
|  | ||||
|         self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+FALSE/', $output); | ||||
|         self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+TRUE./', $output); | ||||
|         self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+TRUE/', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Sum vote weight | 380', $output); | ||||
|  | ||||
|         self::assertStringContainsString('Candidate  1 '.CondorcetStyle::CONDORCET_WINNER_SYMBOL, $output); # Condorcet Winner | ||||
|         self::assertMatchesRegularExpression('/Seats: *\| 3/', $output); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,3 @@ | ||||
| A | ||||
| B | ||||
| C | ||||
							
								
								
									
										2
									
								
								include/condorcet/Tests/src/Console/Commands/data.votes
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								include/condorcet/Tests/src/Console/Commands/data.votes
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| A>B=C | ||||
| B>A>C | ||||
							
								
								
									
										140
									
								
								include/condorcet/Tests/src/ConstraintTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								include/condorcet/Tests/src/ConstraintTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Election, Vote, VoteConstraintInterface}; | ||||
| use CondorcetPHP\Condorcet\Constraints\NoTie; | ||||
| use CondorcetPHP\Condorcet\Throwable\VoteConstraintException; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class ConstraintTest extends TestCase | ||||
| { | ||||
|     private Election $election; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|  | ||||
|         $this->election->addCandidate('A'); | ||||
|         $this->election->addCandidate('B'); | ||||
|         $this->election->addCandidate('C'); | ||||
|     } | ||||
|  | ||||
|     public function testAddConstraintAndClear(): never | ||||
|     { | ||||
|         $this->expectException(VoteConstraintException::class); | ||||
|         $this->expectExceptionMessage('The vote constraint could not be set up: class is already registered'); | ||||
|  | ||||
|         $class = NoTie::class; | ||||
|  | ||||
|         self::assertTrue($this->election->addConstraint($class)); | ||||
|  | ||||
|         self::assertSame([$class], $this->election->getConstraints()); | ||||
|  | ||||
|         self::assertTrue($this->election->clearConstraints()); | ||||
|  | ||||
|         self::assertsame([], $this->election->getConstraints()); | ||||
|  | ||||
|         self::assertTrue($this->election->addConstraint($class)); | ||||
|  | ||||
|         $this->election->addConstraint($class); | ||||
|     } | ||||
|  | ||||
|     public function testPhantomClass(): never | ||||
|     { | ||||
|         $this->expectException(VoteConstraintException::class); | ||||
|         $this->expectExceptionMessage('The vote constraint could not be set up: class is not defined'); | ||||
|  | ||||
|         $this->election->addConstraint('WrongNamespace\AndWrongClass'); | ||||
|     } | ||||
|  | ||||
|     public function testBadClass(): never | ||||
|     { | ||||
|         $this->expectException(VoteConstraintException::class); | ||||
|         $this->expectExceptionMessage('The vote constraint could not be set up: class is not a valid subclass'); | ||||
|  | ||||
|         $class = Vote::class; | ||||
|  | ||||
|         $this->election->addConstraint($class); | ||||
|     } | ||||
|  | ||||
|     public function testConstraintsOnVote(): void | ||||
|     { | ||||
|         $NoTieImplementation = [NoTie::class, AlternativeNoTieConstraintClass::class]; | ||||
|  | ||||
|         foreach ($NoTieImplementation as $constraintClass) { | ||||
|             $this->setUp(); | ||||
|  | ||||
|             $this->election->parseVotes(' | ||||
|                 tag1 || A>B>C | ||||
|                 C>B=A * 3 | ||||
|                 B^42 | ||||
|             '); | ||||
|  | ||||
|             $this->election->allowsVoteWeight(); | ||||
|  | ||||
|             self::assertEquals('B', $this->election->getWinner()); | ||||
|  | ||||
|             $this->election->addConstraint($constraintClass); | ||||
|  | ||||
|             self::assertEquals('A', $this->election->getWinner()); | ||||
|  | ||||
|             $this->election->clearConstraints(); | ||||
|  | ||||
|             self::assertEquals('B', $this->election->getWinner()); | ||||
|  | ||||
|             $this->election->addConstraint($constraintClass); | ||||
|  | ||||
|             self::assertEquals('A', $this->election->getWinner()); | ||||
|  | ||||
|             self::assertEquals(1, $this->election->sumValidVotesWeightWithConstraints()); | ||||
|             self::assertEquals(46, $this->election->sumVotesWeight()); | ||||
|             self::assertEquals(5, $this->election->countVotes()); | ||||
|             self::assertEquals(1, $this->election->countValidVoteWithConstraints()); | ||||
|             self::assertEquals(4, $this->election->countInvalidVoteWithConstraints()); | ||||
|  | ||||
|             self::assertEquals('A', $this->election->getWinner('FTPT')); | ||||
|  | ||||
|             self::assertFalse($this->election->setImplicitRanking(false)); | ||||
|  | ||||
|             self::assertEquals('B', $this->election->getWinner('FTPT')); | ||||
|             self::assertEquals('A', $this->election->getWinner()); | ||||
|  | ||||
|             self::assertEquals(43, $this->election->sumValidVotesWeightWithConstraints()); | ||||
|             self::assertEquals(46, $this->election->sumVotesWeight()); | ||||
|             self::assertEquals(5, $this->election->countVotes()); | ||||
|             self::assertEquals(2, $this->election->countValidVoteWithConstraints()); | ||||
|             self::assertEquals(3, $this->election->countInvalidVoteWithConstraints()); | ||||
|  | ||||
|             self::assertTrue($this->election->setImplicitRanking(true)); | ||||
|  | ||||
|             self::assertEquals('A', $this->election->getWinner()); | ||||
|             self::assertEquals('A', $this->election->getWinner('FTPT')); | ||||
|  | ||||
|             self::assertEquals(1, $this->election->sumValidVotesWeightWithConstraints()); | ||||
|             self::assertEquals(46, $this->election->sumVotesWeight()); | ||||
|             self::assertEquals(5, $this->election->countVotes()); | ||||
|             self::assertEquals(1, $this->election->countValidVoteWithConstraints()); | ||||
|             self::assertEquals(4, $this->election->countInvalidVoteWithConstraints()); | ||||
|             self::assertCount(1, $this->election->getVotesValidUnderConstraintGenerator(['tag1'], true)); | ||||
|             self::assertCount(0, $this->election->getVotesValidUnderConstraintGenerator(['tag1'], false)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| class AlternativeNoTieConstraintClass implements VoteConstraintInterface | ||||
| { | ||||
|     public static function isVoteAllow(Election $election, Vote $vote): bool | ||||
|     { | ||||
|         foreach ($vote->getContextualRanking($election) as $oneRank) { | ||||
|             if (\count($oneRank) > 1) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										50
									
								
								include/condorcet/Tests/src/DataManager/ArrayManagerTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								include/condorcet/Tests/src/DataManager/ArrayManagerTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\DataManager; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\DataManager\ArrayManager; | ||||
| use CondorcetPHP\Condorcet\{Election, Vote}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class ArrayManagerTest extends TestCase | ||||
| { | ||||
|     private readonly ArrayManager $ArrayManager; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->ArrayManager = new class (new Election) extends ArrayManager { | ||||
|             protected function preDeletedTask($object): void | ||||
|             { | ||||
|             } | ||||
|  | ||||
|             protected function decodeOneEntity(string $data): Vote | ||||
|             { | ||||
|                 $vote = new Vote($data); | ||||
|                 $this->getElection()->checkVoteCandidate($vote); | ||||
|                 $vote->registerLink($this->Election->get()); | ||||
|  | ||||
|                 return $vote; | ||||
|             } | ||||
|  | ||||
|             protected function encodeOneEntity(Vote $data): string | ||||
|             { | ||||
|                 $data->destroyLink($this->getElection()); | ||||
|  | ||||
|                 return str_replace([' > ', ' = '], ['>', '='], (string) $data); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     public function testOffsetSetAndOffetsetGet(): void | ||||
|     { | ||||
|         self::assertNull($this->ArrayManager->key()); | ||||
|  | ||||
|         $this->ArrayManager[42] = 'foo'; | ||||
|  | ||||
|         self::assertSame('foo', $this->ArrayManager[42]); | ||||
|  | ||||
|         self::assertNull($this->ArrayManager[43]); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,304 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\DataManager\DataHandlerDrivers\PdoDriver; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use CondorcetPHP\Condorcet\DataManager\ArrayManager; | ||||
| use CondorcetPHP\Condorcet\DataManager\DataHandlerDrivers\PdoDriver\PdoHandlerDriver; | ||||
| use CondorcetPHP\Condorcet\Throwable\DataHandlerException; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| /** | ||||
|  * @preserveGlobalState disabled | ||||
|  * @backupStaticAttributes disabled | ||||
|  * @group DataHandlerDrivers | ||||
|  */ | ||||
| class PdoHandlerDriverTest extends TestCase | ||||
| { | ||||
|     protected function getPDO(): \PDO | ||||
|     { | ||||
|         return new \PDO('sqlite::memory:', '', '', [\PDO::ATTR_PERSISTENT => false]); | ||||
|     } | ||||
|  | ||||
|     protected function hashVotesList(Election $elec): string | ||||
|     { | ||||
|         $c = 0; | ||||
|         $voteCompil = ''; | ||||
|         foreach ($elec->getVotesManager() as $oneVote) { | ||||
|             $c++; | ||||
|             $voteCompil .= (string) $oneVote; | ||||
|         } | ||||
|  | ||||
|         return $c.'||'.hash('md5', $voteCompil); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function testManyVoteManipulation(): never | ||||
|     { | ||||
|         // Setup | ||||
|         ArrayManager::$CacheSize = 10; | ||||
|         ArrayManager::$MaxContainerLength = 10; | ||||
|  | ||||
|         $electionWithDb = new Election; | ||||
|         $electionInMemory = new Election; | ||||
|         $electionWithDb->setExternalDataHandler($handlerDriver = new PdoHandlerDriver($this->getPDO(), true)); | ||||
|  | ||||
|         // Run Test | ||||
|  | ||||
|         $electionWithDb->parseCandidates('A;B;C;D;E'); | ||||
|         $electionInMemory->parseCandidates('A;B;C;D;E'); | ||||
|  | ||||
|         // 45 Votes | ||||
|         $votes =   'A > C > B > E * 5 | ||||
|                     A > D > E > C * 5 | ||||
|                     B > E > D > A * 8 | ||||
|                     C > A > B > E * 3 | ||||
|                     C > A > E > B * 7 | ||||
|                     C > B > A > D * 2 | ||||
|                     D > C > E > B * 7 | ||||
|                     E > B > A > D * 8'; | ||||
|  | ||||
|         $electionWithDb->parseVotes($votes); | ||||
|         $electionInMemory->parseVotes($votes); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $electionWithDb->countVotes(), | ||||
|             $handlerDriver->countEntities() + $electionWithDb->getVotesManager()->getContainerSize() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame($electionInMemory->countVotes(), $electionWithDb->countVotes()); | ||||
|         self::assertSame($electionInMemory->getVotesListAsString(), $electionWithDb->getVotesListAsString()); | ||||
|         self::assertSame($this->hashVotesList($electionInMemory), $this->hashVotesList($electionWithDb)); | ||||
|  | ||||
|         self::assertEquals($electionInMemory->getPairwise()->getExplicitPairwise(), $electionWithDb->getPairwise()->getExplicitPairwise()); | ||||
|         self::assertEquals((string) $electionInMemory->getWinner('Ranked Pairs Winning'), (string) $electionWithDb->getWinner('Ranked Pairs Winning')); | ||||
|         self::assertEquals((string) $electionInMemory->getWinner(), (string) $electionWithDb->getWinner()); | ||||
|         self::assertEquals((string) $electionInMemory->getCondorcetWinner(), (string) $electionWithDb->getCondorcetWinner()); | ||||
|  | ||||
|  | ||||
|         // 58 Votes | ||||
|         $votes = 'A > B > C > E * 58'; | ||||
|  | ||||
|         $electionWithDb->parseVotes($votes); | ||||
|         $electionInMemory->parseVotes($votes); | ||||
|  | ||||
|         self::assertSame(58 % ArrayManager::$MaxContainerLength, $electionWithDb->getVotesManager()->getContainerSize()); | ||||
|         self::assertSame( | ||||
|             $electionWithDb->countVotes(), | ||||
|             $handlerDriver->countEntities() + $electionWithDb->getVotesManager()->getContainerSize() | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals('A', $electionWithDb->getWinner()); | ||||
|         self::assertEquals((string) $electionInMemory->getWinner(), (string) $electionWithDb->getWinner()); | ||||
|  | ||||
|         self::assertSame($electionInMemory->countVotes(), $electionWithDb->countVotes()); | ||||
|         self::assertSame($electionInMemory->getVotesListAsString(), $electionWithDb->getVotesListAsString()); | ||||
|         self::assertSame($this->hashVotesList($electionInMemory), $this->hashVotesList($electionWithDb)); | ||||
|         self::assertSame(0, $electionWithDb->getVotesManager()->getContainerSize()); | ||||
|         self::assertLessThanOrEqual(ArrayManager::$CacheSize, $electionWithDb->getVotesManager()->getCacheSize()); | ||||
|  | ||||
|         // Delete 3 votes | ||||
|         unset($electionInMemory->getVotesManager()[13]); | ||||
|         unset($electionInMemory->getVotesManager()[100]); | ||||
|         unset($electionInMemory->getVotesManager()[102]); | ||||
|         unset($electionWithDb->getVotesManager()[13]); | ||||
|         unset($electionWithDb->getVotesManager()[100]); | ||||
|         unset($electionWithDb->getVotesManager()[102]); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $electionWithDb->countVotes(), | ||||
|             $handlerDriver->countEntities() + $electionWithDb->getVotesManager()->getContainerSize() | ||||
|         ); | ||||
|         self::assertSame($electionInMemory->countVotes(), $electionWithDb->countVotes()); | ||||
|         self::assertSame($electionInMemory->getVotesListAsString(), $electionWithDb->getVotesListAsString()); | ||||
|         self::assertSame($this->hashVotesList($electionInMemory), $this->hashVotesList($electionWithDb)); | ||||
|         self::assertSame(0, $electionWithDb->getVotesManager()->getContainerSize()); | ||||
|         self::assertLessThanOrEqual(ArrayManager::$CacheSize, $electionWithDb->getVotesManager()->getCacheSize()); | ||||
|         self::assertArrayNotHasKey(13, $electionWithDb->getVotesManager()->debugGetCache()); | ||||
|         self::assertArrayNotHasKey(102, $electionWithDb->getVotesManager()->debugGetCache()); | ||||
|         self::assertArrayNotHasKey(100, $electionWithDb->getVotesManager()->debugGetCache()); | ||||
|         self::assertArrayHasKey(101, $electionWithDb->getVotesManager()->debugGetCache()); | ||||
|  | ||||
|  | ||||
|         // Unset Handler | ||||
|         $electionWithDb->removeExternalDataHandler(); | ||||
|  | ||||
|         self::assertEmpty($electionWithDb->getVotesManager()->debugGetCache()); | ||||
|         self::assertSame($electionInMemory->getVotesManager()->getContainerSize(), $electionWithDb->getVotesManager()->getContainerSize()); | ||||
|         self::assertSame($electionInMemory->countVotes(), $electionWithDb->countVotes()); | ||||
|         self::assertSame($electionInMemory->getVotesListAsString(), $electionWithDb->getVotesListAsString()); | ||||
|         self::assertSame($this->hashVotesList($electionInMemory), $this->hashVotesList($electionWithDb)); | ||||
|  | ||||
|         // Change my mind : Set again the a new handler | ||||
|         unset($handlerDriver); | ||||
|         $electionWithDb->setExternalDataHandler($handlerDriver = new PdoHandlerDriver($this->getPDO(), true)); | ||||
|  | ||||
|         self::assertEmpty($electionWithDb->getVotesManager()->debugGetCache()); | ||||
|         self::assertSame(0, $electionWithDb->getVotesManager()->getContainerSize()); | ||||
|         self::assertSame($electionInMemory->countVotes(), $electionWithDb->countVotes()); | ||||
|         self::assertSame($electionInMemory->getVotesListAsString(), $electionWithDb->getVotesListAsString()); | ||||
|         self::assertSame($this->hashVotesList($electionInMemory), $this->hashVotesList($electionWithDb)); | ||||
|  | ||||
|         self::assertTrue($electionWithDb->removeExternalDataHandler()); | ||||
|  | ||||
|         $this->expectException(DataHandlerException::class); | ||||
|         $this->expectExceptionMessage('Problem with data handler: external data handler cannot be removed, is already in use'); | ||||
|  | ||||
|         $electionWithDb->removeExternalDataHandler(); | ||||
|     } | ||||
|  | ||||
|     public function testVotePreserveTag(): void | ||||
|     { | ||||
|         // Setup | ||||
|         ArrayManager::$CacheSize = 10; | ||||
|         ArrayManager::$MaxContainerLength = 10; | ||||
|  | ||||
|         $electionWithDb = new Election; | ||||
|         $electionWithDb->setExternalDataHandler(new PdoHandlerDriver($this->getPDO(), true)); | ||||
|  | ||||
|         $electionWithDb->parseCandidates('A;B;C'); | ||||
|  | ||||
|         $electionWithDb->parseVotes('A > B > C * 5 | ||||
|                                         tag1 || B > A > C * 3'); | ||||
|  | ||||
|         self::assertSame(5, $electionWithDb->countVotes('tag1', false)); | ||||
|         self::assertSame(3, $electionWithDb->countVotes('tag1', true)); | ||||
|  | ||||
|         $electionWithDb->parseVotes('A > B > C * 5 | ||||
|                                         tag1 || B > A > C * 3'); | ||||
|  | ||||
|         self::assertSame(10, $electionWithDb->countVotes('tag1', false)); | ||||
|         self::assertSame(6, $electionWithDb->countVotes('tag1', true)); | ||||
|     } | ||||
|  | ||||
|     public function testVoteObjectIntoDataHandler(): void | ||||
|     { | ||||
|         // Setup | ||||
|         ArrayManager::$CacheSize = 10; | ||||
|         ArrayManager::$MaxContainerLength = 10; | ||||
|  | ||||
|         $electionWithDb = new Election; | ||||
|         $electionWithDb->setExternalDataHandler(new PdoHandlerDriver($this->getPDO(), true)); | ||||
|  | ||||
|         $electionWithDb->parseCandidates('A;B;C'); | ||||
|  | ||||
|         $myVote = $electionWithDb->addVote('A>B>C'); | ||||
|  | ||||
|         $electionWithDb->getVotesManager()->regularize(); | ||||
|         self::assertSame(0, $electionWithDb->getVotesManager()->getContainerSize()); | ||||
|  | ||||
|         // myVote is no longer a part of the election. Internally, it will work with clones. | ||||
|         self::assertSame(0, $myVote->countLinks()); | ||||
|         self::assertNotSame($electionWithDb->getVotesList()[0], $myVote); | ||||
|         self::assertTrue($electionWithDb->getVotesList()[0]->haveLink($electionWithDb)); | ||||
|     } | ||||
|  | ||||
|     public function testUpdateEntity(): void | ||||
|     { | ||||
|         // Setup | ||||
|         ArrayManager::$CacheSize = 10; | ||||
|         ArrayManager::$MaxContainerLength = 10; | ||||
|  | ||||
|         $electionWithDb = new Election; | ||||
|         $electionWithDb->setExternalDataHandler(new PdoHandlerDriver($this->getPDO(), true)); | ||||
|  | ||||
|         $electionWithDb->parseCandidates('A;B;C'); | ||||
|  | ||||
|         $electionWithDb->parseVotes('A>B>C * 19'); | ||||
|         $electionWithDb->addVote('C>B>A', 'voteToUpdate'); | ||||
|  | ||||
|         $vote = $electionWithDb->getVotesList('voteToUpdate', true)[19]; | ||||
|         $vote->setRanking('B>A>C'); | ||||
|         $vote = null; | ||||
|  | ||||
|         $electionWithDb->parseVotes('A>B>C * 20'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             "A > B > C * 39\n". | ||||
|             'B > A > C * 1', | ||||
|             $electionWithDb->getVotesListAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testGetVotesListGenerator(): void | ||||
|     { | ||||
|         $electionWithDb = new Election; | ||||
|         $electionWithDb->setExternalDataHandler(new PdoHandlerDriver($this->getPDO(), true)); | ||||
|  | ||||
|         $electionWithDb->parseCandidates('A;B;C'); | ||||
|  | ||||
|         $electionWithDb->parseVotes('A>B>C * 10;tag42 || C>B>A * 42'); | ||||
|  | ||||
|         $votesListGenerator = []; | ||||
|  | ||||
|         foreach ($electionWithDb->getVotesListGenerator() as $key => $value) { | ||||
|             $votesListGenerator[$key] = $value; | ||||
|         } | ||||
|  | ||||
|         self::assertCount(52, $votesListGenerator); | ||||
|  | ||||
|  | ||||
|         $votesListGenerator = []; | ||||
|  | ||||
|         foreach ($electionWithDb->getVotesListGenerator('tag42') as $key => $value) { | ||||
|             $votesListGenerator[$key] = $value; | ||||
|         } | ||||
|  | ||||
|         self::assertCount(42, $votesListGenerator); | ||||
|     } | ||||
|  | ||||
|     public function testSliceInput(): void | ||||
|     { | ||||
|         // Setup | ||||
|         ArrayManager::$CacheSize = 462; | ||||
|         ArrayManager::$MaxContainerLength = 462; | ||||
|  | ||||
|         $electionWithDb = new Election; | ||||
|         $electionWithDb->setExternalDataHandler(new PdoHandlerDriver($this->getPDO(), true)); | ||||
|  | ||||
|         $electionWithDb->parseCandidates('A;B;C'); | ||||
|  | ||||
|         $electionWithDb->parseVotes('A>B>C * 463'); | ||||
|  | ||||
|         self::assertSame(463, $electionWithDb->countVotes()); | ||||
|     } | ||||
|  | ||||
|     public function testMultipleHandler(): never | ||||
|     { | ||||
|         $this->expectException(DataHandlerException::class); | ||||
|         $this->expectExceptionMessage('external data handler cannot be imported'); | ||||
|  | ||||
|         $electionWithDb = new Election; | ||||
|         $electionWithDb->setExternalDataHandler(new PdoHandlerDriver($this->getPDO(), true)); | ||||
|         $electionWithDb->setExternalDataHandler(new PdoHandlerDriver($this->getPDO(), true)); | ||||
|     } | ||||
|  | ||||
|     public function testBadTableSchema1(): never | ||||
|     { | ||||
|         $this->expectException(DataHandlerException::class); | ||||
|         $this->expectExceptionMessage('Problem with data handler: invalid structure template for PdoHandler'); | ||||
|  | ||||
|         $pdo = $this->getPDO(); | ||||
|         $handlerDriver = new PdoHandlerDriver($pdo, true, ['tableName' => 'Entity', 'primaryColumnName' => 42]); | ||||
|     } | ||||
|  | ||||
|     public function testBadTableSchema2(): never | ||||
|     { | ||||
|         $this->expectException(\Exception::class); | ||||
|  | ||||
|         $pdo = $this->getPDO(); | ||||
|         $handlerDriver = new PdoHandlerDriver($pdo, true, ['tableName' => 'B@adName', 'primaryColumnName' => 'id', 'dataColumnName' => 'data']); | ||||
|     } | ||||
|  | ||||
|     public function testEmptyEntities(): void | ||||
|     { | ||||
|         $pdo = $this->getPDO(); | ||||
|         $handlerDriver = new PdoHandlerDriver($pdo, true, ['tableName' => 'Entity', 'primaryColumnName' => 'id', 'dataColumnName' => 'data']); | ||||
|  | ||||
|         self::assertFalse($handlerDriver->selectOneEntity(500)); | ||||
|  | ||||
|         self::assertSame([], $handlerDriver->selectRangeEntities(500, 5)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										104
									
								
								include/condorcet/Tests/src/DataManager/VotesManagerTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								include/condorcet/Tests/src/DataManager/VotesManagerTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\DataManager; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Election, Vote}; | ||||
| use CondorcetPHP\Condorcet\Throwable\{VoteManagerException, VoteNotLinkedException}; | ||||
| use CondorcetPHP\Condorcet\DataManager\VotesManager; | ||||
|  | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class VotesManagerTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election; | ||||
|     private readonly VotesManager $votes_manager; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election = new Election; | ||||
|         $this->election->parseCandidates('A;B;C'); | ||||
|  | ||||
|         $this->votes_manager = $this->election->getVotesManager(); | ||||
|     } | ||||
|  | ||||
|     public function testOffsetSet(): never | ||||
|     { | ||||
|         $this->expectException(VoteNotLinkedException::class); | ||||
|         $this->expectExceptionMessage('The vote is not linked to an election'); | ||||
|  | ||||
|         $vote = new Vote([]); | ||||
|  | ||||
|         // add valid vote | ||||
|         $this->votes_manager[] = $vote; | ||||
|         self::assertSame($vote, $this->votes_manager->getVotesList()[0]); | ||||
|  | ||||
|         // add invalid vote | ||||
|         $this->votes_manager[] = null; | ||||
|     } | ||||
|  | ||||
|     public function testOffsetSetArgumentType(): never | ||||
|     { | ||||
|         $this->expectException(VoteManagerException::class); | ||||
|  | ||||
|         // add invalid vote | ||||
|         $this->votes_manager[] = new \stdClass; | ||||
|     } | ||||
|  | ||||
|     public function testOffsetUnset(): void | ||||
|     { | ||||
|         $before_list = $this->votes_manager->getVotesList(); | ||||
|  | ||||
|         // unset non existent vote | ||||
|         unset($this->votes_manager[0]); | ||||
|         self::assertSame($before_list, $this->votes_manager->getVotesList()); | ||||
|  | ||||
|         // unset existing vote | ||||
|         $vote = new Vote([]); | ||||
|         $vote->registerLink($this->election); | ||||
|         $this->votes_manager[] = $vote; | ||||
|         unset($this->votes_manager[0]); | ||||
|         self::assertEmpty($this->votes_manager->getVotesList()); | ||||
|     } | ||||
|  | ||||
|     public function testGetVoteKey(): void | ||||
|     { | ||||
|         self::assertNull($this->votes_manager->getVoteKey(new Vote([]))); | ||||
|     } | ||||
|  | ||||
|     public function testGetVotesList(): void | ||||
|     { | ||||
|         // With Election | ||||
|         self::assertEmpty($this->votes_manager->getVotesList()); | ||||
|  | ||||
|         $this->election->addCandidate('candidate'); | ||||
|         $this->election->addVote(new Vote(['candidate'])); | ||||
|  | ||||
|         self::assertNotEmpty($this->votes_manager->getVotesList()); | ||||
|     } | ||||
|  | ||||
|     public function testGetVotesListGenerator(): void | ||||
|     { | ||||
|         $this->election->parseVotes('A>B>C * 10;tag42 || C>B>A * 42'); | ||||
|  | ||||
|         $votesListGenerator = []; | ||||
|  | ||||
|         foreach ($this->election->getVotesListGenerator() as $key => $value) { | ||||
|             $votesListGenerator[$key] = $value; | ||||
|         } | ||||
|  | ||||
|         self::assertEquals($this->election->getVotesList(), $votesListGenerator); | ||||
|         self::assertCount(52, $votesListGenerator); | ||||
|  | ||||
|  | ||||
|         $votesListGenerator = []; | ||||
|  | ||||
|         foreach ($this->election->getVotesListGenerator('tag42') as $key => $value) { | ||||
|             $votesListGenerator[$key] = $value; | ||||
|         } | ||||
|  | ||||
|         self::assertEquals($this->election->getVotesList('tag42'), $votesListGenerator); | ||||
|         self::assertCount(42, $votesListGenerator); | ||||
|     } | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										985
									
								
								include/condorcet/Tests/src/ElectionTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										985
									
								
								include/condorcet/Tests/src/ElectionTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,985 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\ElectionProcess\ElectionState; | ||||
| use CondorcetPHP\Condorcet\{Candidate, Condorcet, Election, Vote}; | ||||
| use CondorcetPHP\Condorcet\Throwable\{CandidateDoesNotExistException, CandidateExistsException, ElectionObjectVersionMismatchException, FileDoesNotExistException, NoCandidatesException, NoSeatsException, ResultRequestedWithoutVotesException, VoteException, VoteInvalidFormatException, VoteMaxNumberReachedException, VotingHasStartedException}; | ||||
| use CondorcetPHP\Condorcet\Tools\Converters\CondorcetElectionFormat; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class ElectionTest extends TestCase | ||||
| { | ||||
|     private Election $election1; | ||||
|     private Election $election2; | ||||
|  | ||||
|     private Candidate $candidate1; | ||||
|     private Candidate $candidate2; | ||||
|     private Candidate $candidate3; | ||||
|  | ||||
|     private Vote $vote1; | ||||
|     private Vote $vote2; | ||||
|     private Vote $vote3; | ||||
|     private Vote $vote4; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election1 = new Election; | ||||
|  | ||||
|         $this->candidate1 = $this->election1->addCandidate('candidate1'); | ||||
|         $this->candidate2 = $this->election1->addCandidate('candidate2'); | ||||
|         $this->candidate3 = $this->election1->addCandidate('candidate3'); | ||||
|  | ||||
|         $this->election1->addVote($this->vote1 = new Vote([$this->candidate1, $this->candidate2, $this->candidate3])); | ||||
|         $this->election1->addVote($this->vote2 = new Vote([$this->candidate2, $this->candidate3, $this->candidate1])); | ||||
|         $this->election1->addVote($this->vote3 = new Vote([$this->candidate3, $this->candidate1, $this->candidate2])); | ||||
|         $this->election1->addVote($this->vote4 = new Vote([$this->candidate1, $this->candidate2, $this->candidate3])); | ||||
|  | ||||
|         $this->election2 = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testRemoveVotes(): never | ||||
|     { | ||||
|         $this->expectException(VoteException::class); | ||||
|         $this->expectExceptionMessage('Problem handling vote: cannot remove vote, is not registered in this election'); | ||||
|  | ||||
|         self::assertTrue($this->election1->removeVote($this->vote2)); | ||||
|         self::assertCount(3, $this->election1->getVotesList()); | ||||
|  | ||||
|         $badRemoveVote = new Vote('A'); | ||||
|  | ||||
|         $this->election1->removeVote($badRemoveVote); | ||||
|     } | ||||
|  | ||||
|     public function testRemoveVotesByTags(): void | ||||
|     { | ||||
|         $this->vote1->addtags('tag1,tag2,tag3'); | ||||
|         $this->vote2->addtags('tag3,tag4'); | ||||
|         $this->vote3->addtags('tag3,tag4,tag5'); | ||||
|         $this->vote4->addtags('tag1,tag4'); | ||||
|  | ||||
|         self::assertCount(3, $r = $this->election1->removeVotesByTags(['tag1', 'tag5'])); | ||||
|  | ||||
|         self::assertSame([$this->vote1, $this->vote3, $this->vote4], $r); | ||||
|  | ||||
|         self::assertSame([1 => $this->vote2], $this->election1->getVotesList()); | ||||
|  | ||||
|         $this->setUp(); | ||||
|  | ||||
|         $this->vote1->addtags('tag1,tag2,tag3'); | ||||
|         $this->vote2->addtags('tag3,tag4'); | ||||
|         $this->vote3->addtags('tag3,tag4,tag5'); | ||||
|         $this->vote4->addtags('tag1,tag4'); | ||||
|  | ||||
|         self::assertCount(1, $r = $this->election1->removeVotesByTags('tag1,tag5', false)); | ||||
|  | ||||
|         self::assertSame([$this->vote2], $r); | ||||
|  | ||||
|         self::assertSame([0 => $this->vote1, 2=> $this->vote3, 3 => $this->vote4], $this->election1->getVotesList()); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function testTagsFilter(): void | ||||
|     { | ||||
|         $this->vote1->addtags('tag1,tag2,tag3'); | ||||
|         $this->vote2->addtags('tag3,tag4'); | ||||
|         $this->vote3->addtags('tag3,tag4,tag5'); | ||||
|         $this->vote4->addtags('tag1,tag4'); | ||||
|  | ||||
|         self::assertSame($this->election1->getVotesList('tag1,tag2', true), [0=>$this->vote1, 3=>$this->vote4]); | ||||
|         self::assertSame($this->election1->countVotes('tag1,tag2', true), 2); | ||||
|  | ||||
|         self::assertSame($this->election1->getVotesList('tag1,tag2', false), [1=>$this->vote2, 2=>$this->vote3]); | ||||
|         self::assertSame($this->election1->countVotes('tag1,tag2', false), 2); | ||||
|  | ||||
|         $resultGlobal = $this->election1->getResult('Schulze'); | ||||
|         $resultFilter1 = $this->election1->getResult('Schulze', ['tags' => 'tag1', 'withTag' => true]); | ||||
|         $resultFilter2 = $this->election1->getResult('Schulze', ['tags' => 'tag1', 'withTag' => false]); | ||||
|  | ||||
|         self::assertNotSame($resultGlobal, $resultFilter1); | ||||
|         self::assertNotSame($resultGlobal, $resultFilter2); | ||||
|         self::assertNotSame($resultFilter1, $resultFilter2); | ||||
|     } | ||||
|  | ||||
|     public function testParseCandidates(): void | ||||
|     { | ||||
|         self::assertCount( | ||||
|             4, | ||||
|             $this->election2->parseCandidates('Bruckner;   Mahler   ; | ||||
|                 Debussy | ||||
|                 Bibendum') | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['Bruckner', 'Mahler', 'Debussy', 'Bibendum'], | ||||
|             $this->election2->getCandidatesListAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testgetCandidateObjectFromName(): void | ||||
|     { | ||||
|         self::assertSame($this->candidate1, $this->election1->getCandidateObjectFromName('candidate1')); | ||||
|         self::assertNull($this->election1->getCandidateObjectFromName('candidate42')); | ||||
|     } | ||||
|  | ||||
|     public function testParseError(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage("The format of the vote is invalid: the value 'text' is not an integer."); | ||||
|  | ||||
|         $this->election1->parseVotes('candidate1>candidate2 * text'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|       * @preserveGlobalState disabled | ||||
|       * @backupStaticAttributes disabled | ||||
|       * @runInSeparateProcess | ||||
|       */ | ||||
|     public function testMaxParseIteration1(): never | ||||
|     { | ||||
|         $this->expectException(VoteMaxNumberReachedException::class); | ||||
|         $this->expectExceptionMessage('The maximal number of votes for the method is reached: 42'); | ||||
|  | ||||
|         self::assertSame(42, Election::setMaxParseIteration(42)); | ||||
|  | ||||
|         self::assertCount(42, $this->election1->parseVotes('candidate1>candidate2 * 42')); | ||||
|  | ||||
|         self::assertCount(42, $this->election1->parseVotes('candidate1>candidate2 * 42')); | ||||
|  | ||||
|         self::assertNull(Election::setMaxParseIteration(null)); | ||||
|  | ||||
|         self::assertCount(43, $this->election1->parseVotes('candidate1>candidate2 * 43')); | ||||
|  | ||||
|         self::assertSame(42, Election::setMaxParseIteration(42)); | ||||
|  | ||||
|         $this->election1->parseVotes('candidate1>candidate2 * 43'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|       * @preserveGlobalState disabled | ||||
|       * @backupStaticAttributes disabled | ||||
|       * @runInSeparateProcess | ||||
|       */ | ||||
|     public function testMaxParseIteration2(): never | ||||
|     { | ||||
|         $this->expectException(VoteMaxNumberReachedException::class); | ||||
|         $this->expectExceptionMessage('The maximal number of votes for the method is reached: 42'); | ||||
|  | ||||
|         self::assertSame(42, Election::setMaxParseIteration(42)); | ||||
|  | ||||
|         self::assertSame(42, $this->election1->parseVotes(' | ||||
|             candidate1>candidate2 * 21 | ||||
|             candidate1>candidate2 * 21 | ||||
|             candidate1>candidate2 * 21 | ||||
|         ')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|       * @preserveGlobalState disabled | ||||
|       * @backupStaticAttributes disabled | ||||
|       * @runInSeparateProcess | ||||
|       */ | ||||
|     public function testMaxParseIteration3(): never | ||||
|     { | ||||
|         $this->expectException(VoteMaxNumberReachedException::class); | ||||
|         $this->expectExceptionMessage('The maximal number of votes for the method is reached: 2'); | ||||
|  | ||||
|         self::assertSame(2, Election::setMaxParseIteration(2)); | ||||
|  | ||||
|         self::assertSame([0=>'candidate1', 1=>'candidate2'], $this->election2->parseCandidates('candidate1;candidate2')); | ||||
|  | ||||
|         self::assertSame([0=>'candidate3', 1=>'candidate4'], $this->election2->parseCandidates('candidate3;candidate4')); | ||||
|  | ||||
|         self::assertNull(Election::setMaxParseIteration(null)); | ||||
|  | ||||
|         self::assertSame([0=>'candidate5', 1=>'candidate6', 2=>'candidate7'], $this->election2->parseCandidates('candidate5;candidate6;candidate7')); | ||||
|  | ||||
|         self::assertSame(2, Election::setMaxParseIteration(2)); | ||||
|  | ||||
|         $this->election2->parseCandidates('candidate8;candidate9;candidate10'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|       * @preserveGlobalState disabled | ||||
|       * @backupStaticAttributes disabled | ||||
|       * @runInSeparateProcess | ||||
|       */ | ||||
|     public function testMaxVoteNumber(): never | ||||
|     { | ||||
|         $this->expectException(VoteMaxNumberReachedException::class); | ||||
|         $this->expectExceptionMessage('The maximal number of votes for the method is reached'); | ||||
|  | ||||
|         $election = new Election; | ||||
|         self::assertCount(3, $election->parseCandidates('candidate1;candidate2;candidate3')); | ||||
|  | ||||
|         self::assertSame(42, Election::setMaxVoteNumber(42)); | ||||
|  | ||||
|         self::assertSame(21, $election->parseVotes('candidate1>candidate2 * 21')); | ||||
|  | ||||
|         try { | ||||
|             $election->parseVotes('candidate1>candidate2 * 42'); | ||||
|             self::assertTrue(false); | ||||
|         } catch (VoteMaxNumberReachedException $e) { | ||||
|             $this->assertEquals('The maximal number of votes for the method is reached', $e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         self::assertSame(21, $election->countVotes()); | ||||
|  | ||||
|         $election->parseVotes('candidate1 * 21'); | ||||
|  | ||||
|         self::assertSame(42, $election->countVotes()); | ||||
|  | ||||
|         self::assertNull(Election::setMaxVoteNumber(null)); | ||||
|  | ||||
|         $election->addVote('candidate3'); | ||||
|  | ||||
|         self::assertSame(42, Election::setMaxVoteNumber(42)); | ||||
|  | ||||
|         try { | ||||
|             $election->addVote('candidate3'); | ||||
|         } catch (VoteMaxNumberReachedException $e) { | ||||
|             $reserveException = $e; | ||||
|         } | ||||
|  | ||||
|         self::assertNull(Election::setMaxVoteNumber(null)); | ||||
|  | ||||
|         throw $reserveException; | ||||
|     } | ||||
|  | ||||
|     public function testGetVotesListAsString(): void | ||||
|     { | ||||
|         $this->election1 = new Election; | ||||
|  | ||||
|         $this->election1->addCandidate('C'); | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('D'); | ||||
|         $this->election1->addCandidate('E'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|  | ||||
|         $this->election1->parseVotes(' | ||||
|             D * 6 | ||||
|             A * 6 | ||||
|             E = A = B *3 | ||||
|             A > C = B > E * 5 | ||||
|             Y > Z | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             "A > B = C = D = E * 6\n". | ||||
|         "D > A = B = C = E * 6\n". | ||||
|         "A > B = C > E > D * 5\n". | ||||
|         "A = B = E > C = D * 3\n". | ||||
|         'A = B = C = D = E * 1', | ||||
|             $this->election1->getVotesListAsString() | ||||
|         ); | ||||
|  | ||||
|         $this->election1->setImplicitRanking(false); | ||||
|  | ||||
|         self::assertSame( | ||||
|             "A * 6\n". | ||||
|         "D * 6\n". | ||||
|         "A > B = C > E * 5\n". | ||||
|         "A = B = E * 3\n". | ||||
|         '/EMPTY_RANKING/ * 1', | ||||
|             $this->election1->getVotesListAsString() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'VOTES' | ||||
|                 A * 6 | ||||
|                 D * 6 | ||||
|                 A > B = C > E * 5 | ||||
|                 A = B = E * 3 | ||||
|                 Y > Z * 1 | ||||
|                 VOTES, | ||||
|             $this->election1->getVotesListAsString(false) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testEmptyRankingExport(): void | ||||
|     { | ||||
|         $this->election2->parseCandidates('A;B;C'); | ||||
|         $this->election2->setImplicitRanking(false); | ||||
|  | ||||
|         $this->election2->addVote(new Vote('')); | ||||
|         $this->election2->addVote(new Vote('D>E')); | ||||
|  | ||||
|         self::assertSame('/EMPTY_RANKING/ * 2', $this->election2->getVotesListAsString(true)); | ||||
|         self::assertSame('/EMPTY_RANKING/ * 1'. "\n" .'D > E * 1', $this->election2->getVotesListAsString(false)); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $cvotes_explicit_without_context = | ||||
|                             <<<'CVOTES' | ||||
|                                 #/Candidates: A ; B ; C | ||||
|                                 #/Implicit Ranking: false | ||||
|                                 #/Weight Allowed: false | ||||
|  | ||||
|                                 /EMPTY_RANKING/ * 1 | ||||
|                                 D > E * 1 | ||||
|                                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: false) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             str_replace(' * 1', '', $cvotes_explicit_without_context), | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: false, inContext: false) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'CVOTES' | ||||
|                 #/Candidates: A ; B ; C | ||||
|                 #/Implicit Ranking: false | ||||
|                 #/Weight Allowed: false | ||||
|  | ||||
|                 /EMPTY_RANKING/ * 2 | ||||
|                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'CVOTES' | ||||
|                 #/Candidates: A ; B ; C | ||||
|                 #/Implicit Ranking: false | ||||
|                 #/Weight Allowed: false | ||||
|  | ||||
|                 /EMPTY_RANKING/ | ||||
|                 /EMPTY_RANKING/ | ||||
|                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: false, inContext: true) | ||||
|         ); | ||||
|  | ||||
|         $this->election2->setImplicitRanking(true); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'CVOTES' | ||||
|                 #/Candidates: A ; B ; C | ||||
|                 #/Implicit Ranking: true | ||||
|                 #/Weight Allowed: false | ||||
|  | ||||
|                 A = B = C * 2 | ||||
|                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'CVOTES' | ||||
|                 #/Candidates: A ; B ; C | ||||
|                 #/Implicit Ranking: true | ||||
|                 #/Weight Allowed: false | ||||
|  | ||||
|                 A = B = C | ||||
|                 A = B = C | ||||
|                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: false, inContext: true) | ||||
|         ); | ||||
|  | ||||
|         $this->election2 = new Election; | ||||
|         $this->election2->parseCandidates('A;B;C;D'); | ||||
|         $this->election2->setImplicitRanking(true); | ||||
|  | ||||
|         $this->election2->addVote(new Vote('A>B')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'CVOTES' | ||||
|                 #/Candidates: A ; B ; C ; D | ||||
|                 #/Implicit Ranking: true | ||||
|                 #/Weight Allowed: false | ||||
|  | ||||
|                 A > B > C = D * 1 | ||||
|                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: true) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $cvotes_implicit_without_context = | ||||
|                             <<<'CVOTES' | ||||
|                                 #/Candidates: A ; B ; C ; D | ||||
|                                 #/Implicit Ranking: true | ||||
|                                 #/Weight Allowed: false | ||||
|  | ||||
|                                 A > B * 1 | ||||
|                                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: false) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             str_replace(' * 1', '', $cvotes_implicit_without_context), | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: false, inContext: false) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testParseVoteCandidateCoherence(): void | ||||
|     { | ||||
|         $this->election1 = new Election; | ||||
|  | ||||
|         $cA = $this->election1->addCandidate('A'); | ||||
|         $cB = $this->election1->addCandidate('B'); | ||||
|         $cC = $this->election1->addCandidate('C'); | ||||
|  | ||||
|         self::assertSame(2, $this->election1->parseVotes(' | ||||
|             A>B>C * 2 | ||||
|         ')); | ||||
|  | ||||
|         $votes = $this->election1->getVotesList(); | ||||
|  | ||||
|         foreach ($votes as $vote) { | ||||
|             $ranking = $vote->getRanking(); | ||||
|  | ||||
|             self::assertSame($cA, $ranking[1][0]); | ||||
|             self::assertSame($cB, $ranking[2][0]); | ||||
|             self::assertSame($cC, $ranking[3][0]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function testParseVotesInvalidPath(): void | ||||
|     { | ||||
|         $this->expectException(FileDoesNotExistException::class); | ||||
|         $this->expectExceptionMessageMatches('/bad_file.txt$/'); | ||||
|  | ||||
|         $this->election1 = new Election; | ||||
|  | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('B'); | ||||
|  | ||||
|         $this->election1->parseVotes('bad_file.txt', true); | ||||
|     } | ||||
|  | ||||
|     public function testParseVotesWithoutFail(): void | ||||
|     { | ||||
|         $this->election1 = new Election; | ||||
|  | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         self::assertSame(2, $this->election1->parseVotesWithoutFail(' | ||||
|             A > B > C | ||||
|             A > B > C * 4;tag1 || A > B > C*4 #Coucou | ||||
|             A < B < C * 10 | ||||
|             D <> B | ||||
|             A > B > C | ||||
|         ')); | ||||
|  | ||||
|         self::assertSame(10, $this->election1->countVotes()); | ||||
|  | ||||
|         self::assertSame(2, $this->election1->parseVotesWithoutFail(__DIR__.'/../LargeElectionData/smallVote1.votes', true)); | ||||
|  | ||||
|         self::assertSame(20, $this->election1->countVotes()); | ||||
|  | ||||
|         self::assertSame(2, $this->election1->parseVotesWithoutFail(new \SplFileObject(__DIR__.'/../LargeElectionData/smallVote1.votes'), true)); | ||||
|  | ||||
|         self::assertSame(30, $this->election1->countVotes()); | ||||
|  | ||||
|         self::assertSame(2, $this->election1->parseVotesWithoutFail(new \SplFileInfo(__DIR__.'/../LargeElectionData/smallVote1.votes'), true)); | ||||
|  | ||||
|         self::assertSame(40, $this->election1->countVotes()); | ||||
|     } | ||||
|  | ||||
|     public function testParseVotesWithoutFailInvalidPath(): void | ||||
|     { | ||||
|         $this->expectException(FileDoesNotExistException::class); | ||||
|         $this->expectExceptionMessageMatches('/bad_file.txt$/'); | ||||
|  | ||||
|         $this->election1 = new Election; | ||||
|  | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('B'); | ||||
|  | ||||
|         $this->election1->parseVotesWithoutFail('bad_file.txt', true); | ||||
|     } | ||||
|  | ||||
|     public function testVoteWeight(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|  | ||||
|         $election->addCandidate('A'); | ||||
|         $election->addCandidate('B'); | ||||
|         $election->addCandidate('C'); | ||||
|         $election->addCandidate('D'); | ||||
|  | ||||
|         $election->parseVotes(' | ||||
|             A > C > D * 6 | ||||
|             B > A > D * 1 | ||||
|             C > B > D * 3 | ||||
|             D > B > A * 3 | ||||
|         '); | ||||
|  | ||||
|         $voteWithWeight = $election->addVote('D > C > B'); | ||||
|         $voteWithWeight->setWeight(2); | ||||
|  | ||||
|         self::assertSame( | ||||
|             14, | ||||
|             $election->sumVotesWeight() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'D > C > B ^2', | ||||
|             (string) $voteWithWeight | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'D > C > B > A', | ||||
|             $voteWithWeight->getSimpleRanking($election) | ||||
|         ); | ||||
|  | ||||
|         self::assertNotSame( | ||||
|             'A = D > C > B', | ||||
|             $election->getResult('Schulze Winning')->getResultAsString() | ||||
|         ); | ||||
|  | ||||
|         $election->allowsVoteWeight(true); | ||||
|  | ||||
|         self::assertSame( | ||||
|             15, | ||||
|             $election->sumVotesWeight() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'D > C > B > A ^2', | ||||
|             $voteWithWeight->getSimpleRanking($election) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'A = D > C > B', | ||||
|             $election->getResult('Schulze Winning')->getResultAsString() | ||||
|         ); | ||||
|  | ||||
|         $election->allowsVoteWeight(false); | ||||
|  | ||||
|         self::assertSame( | ||||
|             14, | ||||
|             $election->sumVotesWeight() | ||||
|         ); | ||||
|  | ||||
|         self::assertNotSame( | ||||
|             'A = D > C > B', | ||||
|             $election->getResult('Schulze Winning')->getResultAsString() | ||||
|         ); | ||||
|  | ||||
|         $election->allowsVoteWeight(!$election->isVoteWeightAllowed()); | ||||
|  | ||||
|         $election->removeVote($voteWithWeight); | ||||
|  | ||||
|         self::assertSame( | ||||
|             13, | ||||
|             $election->sumVotesWeight() | ||||
|         ); | ||||
|  | ||||
|         $election->parseVotes(' | ||||
|             D > C > B ^2 * 1 | ||||
|         '); | ||||
|  | ||||
|         self::assertSame( | ||||
|             15, | ||||
|             $election->sumVotesWeight() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'A = D > C > B', | ||||
|             $election->getResult('Schulze Winning')->getResultAsString() | ||||
|         ); | ||||
|  | ||||
|         $election->addVote('D > C > B'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'VOTES' | ||||
|                 A > C > D > B * 6 | ||||
|                 C > B > D > A * 3 | ||||
|                 D > B > A > C * 3 | ||||
|                 D > C > B > A ^2 * 1 | ||||
|                 B > A > D > C * 1 | ||||
|                 D > C > B > A * 1 | ||||
|                 VOTES, | ||||
|             $election->getVotesListAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testaddVotesFromJson(): never | ||||
|     { | ||||
|         $this->expectException(\JsonException::class); | ||||
|  | ||||
|         $election = new Election; | ||||
|  | ||||
|         $election->addCandidate('A'); | ||||
|         $election->addCandidate('B'); | ||||
|         $election->addCandidate('C'); | ||||
|  | ||||
|         $votes = []; | ||||
|  | ||||
|         $votes[]['vote'] = 'B>C>A'; | ||||
|         $votes[]['vote'] = new \stdClass; // Invalid Vote | ||||
|         $votes[]['vote'] = ['C', 'B', 'A']; | ||||
|  | ||||
|         self::assertSame(2, $election->addVotesFromJson(json_encode($votes))); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'VOTES' | ||||
|                 B > C > A * 1 | ||||
|                 C > B > A * 1 | ||||
|                 VOTES, | ||||
|             $election->getVotesListAsString() | ||||
|         ); | ||||
|  | ||||
|         $votes = []; | ||||
|  | ||||
|         $votes[0]['vote'] = 'A>B>C'; | ||||
|         $votes[0]['multi'] = 5; | ||||
|         $votes[0]['tag'] = 'tag1'; | ||||
|         $votes[0]['weight'] = '42'; | ||||
|  | ||||
|         $election->addVotesFromJson(json_encode($votes)); | ||||
|  | ||||
|         $election->allowsVoteWeight(true); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'VOTES' | ||||
|                 A > B > C ^42 * 5 | ||||
|                 B > C > A * 1 | ||||
|                 C > B > A * 1 | ||||
|                 VOTES, | ||||
|             $election->getVotesListAsString() | ||||
|         ); | ||||
|         self::assertSame(5, $election->countVotes('tag1')); | ||||
|  | ||||
|         $election->addVotesFromJson(json_encode($votes).'{42'); | ||||
|     } | ||||
|  | ||||
|     public function testaddCandidatesFromJson(): never | ||||
|     { | ||||
|         $this->expectException(CandidateExistsException::class); | ||||
|         $this->expectExceptionMessage('This candidate already exists: candidate2'); | ||||
|  | ||||
|         $election = new Election; | ||||
|  | ||||
|         $candidates = ['candidate1 ', 'candidate2']; | ||||
|  | ||||
|         $election->addCandidatesFromJson(json_encode($candidates)); | ||||
|  | ||||
|         self::assertSame(2, $election->countCandidates()); | ||||
|  | ||||
|         self::assertEquals(['candidate1', 'candidate2'], $election->getCandidatesListAsString()); | ||||
|  | ||||
|         $election->addCandidatesFromJson(json_encode(['candidate2'])); | ||||
|     } | ||||
|  | ||||
|     public function testaddCandidatesFromInvalidJson(): never | ||||
|     { | ||||
|         $this->expectException(\JsonException::class); | ||||
|  | ||||
|         $election = new Election; | ||||
|  | ||||
|         $election->addCandidatesFromJson(json_encode(['candidate3']).'{42'); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function testaddVotesFromJsonWithInvalidJson(): void | ||||
|     { | ||||
|         $errors = ['42', 'true', 'false', 'null', '', ' ', json_encode(new \stdClass)]; | ||||
|  | ||||
|         foreach ($errors as $oneError) { | ||||
|             try { | ||||
|                 $this->election2->addVotesFromJson($oneError); | ||||
|  | ||||
|                 // Else fail | ||||
|                 $this->fail("{$oneError} is not a valid Json for PHP"); | ||||
|             } catch (\JsonException) { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         self::assertEmpty($this->election2->getVotesList()); | ||||
|     } | ||||
|  | ||||
|     public function testCachingResult(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|  | ||||
|         $election->addCandidate('A'); | ||||
|         $election->addCandidate('B'); | ||||
|         $election->addCandidate('C'); | ||||
|         $election->addCandidate('D'); | ||||
|  | ||||
|         $election->addVote($vote1 = new Vote('A > C > D')); | ||||
|  | ||||
|         $result1 = $election->getResult('Schulze'); | ||||
|         self::assertSame($result1, $election->getResult('Schulze')); | ||||
|     } | ||||
|  | ||||
|     public function testElectionSerializing(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|  | ||||
|         $election->addCandidate('A'); | ||||
|         $election->addCandidate('B'); | ||||
|         $election->addCandidate('C'); | ||||
|         $election->addCandidate('D'); | ||||
|  | ||||
|         $election->addVote($vote1 = new Vote('A > C > D')); | ||||
|         $result1 = $election->getResult('Schulze'); | ||||
|  | ||||
|         $election = serialize($election); | ||||
|         // file_put_contents("Tests/src/ElectionData/serialized_election_v3.2.0.txt", $election); # For next test | ||||
|         $election = unserialize($election); | ||||
|  | ||||
|         self::assertNotSame($result1, $election->getResult('Schulze')); | ||||
|         self::assertSame($result1->getResultAsString(), $election->getResult('Schulze')->getResultAsString()); | ||||
|  | ||||
|         self::assertNotSame($vote1, $election->getVotesList()[0]); | ||||
|         self::assertSame($vote1->getSimpleRanking(), $election->getVotesList()[0]->getSimpleRanking()); | ||||
|         self::assertTrue($election->getVotesList()[0]->haveLink($election)); | ||||
|         self::assertFalse($vote1->haveLink($election)); | ||||
|     } | ||||
|  | ||||
|     public function testElectionUnserializing(): void | ||||
|     { | ||||
|         $this->expectException(ElectionObjectVersionMismatchException::class); | ||||
|         $this->expectExceptionMessage( | ||||
|             "Version mismatch: The election object has version '3.2' " . | ||||
|             "which is different from the current class version '".Condorcet::getVersion(true)."'" | ||||
|         ); | ||||
|  | ||||
|         unserialize( | ||||
|             file_get_contents('Tests/src/ElectionData/serialized_election_v3.2.0.txt') | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testCloneElection(): void | ||||
|     { | ||||
|         $this->election1->computeResult(); | ||||
|  | ||||
|         $cloneElection = clone $this->election1; | ||||
|  | ||||
|         self::assertNotSame($this->election1->getVotesManager(), $cloneElection->getVotesManager()); | ||||
|         self::assertSame($this->election1->getVotesList(), $cloneElection->getVotesList()); | ||||
|  | ||||
|         self::assertSame($this->election1->getCandidatesList(), $cloneElection->getCandidatesList()); | ||||
|  | ||||
|         self::assertNotSame($this->election1->getPairwise(), $cloneElection->getPairwise()); | ||||
|         self::assertEquals($this->election1->getExplicitPairwise(), $cloneElection->getExplicitPairwise()); | ||||
|  | ||||
|         self::assertNotSame($this->election1->getTimerManager(), $cloneElection->getTimerManager()); | ||||
|  | ||||
|         self::assertSame($this->election1->getVotesList()[0], $cloneElection->getVotesList()[0]); | ||||
|  | ||||
|         self::assertTrue($cloneElection->getVotesList()[0]->haveLink($this->election1)); | ||||
|         self::assertTrue($cloneElection->getVotesList()[0]->haveLink($cloneElection)); | ||||
|     } | ||||
|  | ||||
|     public function testPairwiseArrayAccess(): void | ||||
|     { | ||||
|         $this->election1->computeResult(); | ||||
|  | ||||
|         self::assertTrue($this->election1->getPairwise()->offsetExists(1)); | ||||
|     } | ||||
|  | ||||
|     public function testGetCandidateObjectFromKey(): void | ||||
|     { | ||||
|         self::assertSame($this->candidate2, $this->election1->getCandidateObjectFromKey(1)); | ||||
|  | ||||
|         self::assertNull($this->election1->getCandidateObjectFromKey(42)); | ||||
|     } | ||||
|  | ||||
|     public function testElectionState1(): never | ||||
|     { | ||||
|         $this->expectException(VotingHasStartedException::class); | ||||
|         $this->expectExceptionMessage("The voting has started: cannot add 'candidate4'"); | ||||
|  | ||||
|         $this->election1->addCandidate('candidate4'); | ||||
|     } | ||||
|  | ||||
|     public function testElectionState2(): never | ||||
|     { | ||||
|         $this->expectException(VotingHasStartedException::class); | ||||
|         $this->expectExceptionMessage('The voting has started'); | ||||
|  | ||||
|         $this->election1->removeCandidates('candidate4'); | ||||
|     } | ||||
|  | ||||
|     public function testElectionState3(): never | ||||
|     { | ||||
|         $this->expectException(NoCandidatesException::class); | ||||
|         $this->expectExceptionMessage('You need to specify one or more candidates before voting'); | ||||
|  | ||||
|         $election = new Election; | ||||
|         $election->setStateTovote(); | ||||
|     } | ||||
|  | ||||
|     public function testElectionState4(): never | ||||
|     { | ||||
|         $this->expectException(ResultRequestedWithoutVotesException::class); | ||||
|         $this->expectExceptionMessage('The result cannot be requested without votes'); | ||||
|  | ||||
|         $election = new Election; | ||||
|         $election->getResult(); | ||||
|     } | ||||
|  | ||||
|     public function testElectionState5(): void | ||||
|     { | ||||
|         $this->election1->getResult(); | ||||
|  | ||||
|         self::assertTrue($this->election1->setStateTovote()); | ||||
|  | ||||
|         self::assertSame(ElectionState::VOTES_REGISTRATION, $this->election1->getState()); | ||||
|     } | ||||
|  | ||||
|     public function testAddSameVote(): never | ||||
|     { | ||||
|         $this->expectException(VoteException::class); | ||||
|         $this->expectExceptionMessage('Problem handling vote: seats are already registered'); | ||||
|  | ||||
|         $this->election1->addVote($this->vote1); | ||||
|     } | ||||
|  | ||||
|     public function testDestroy(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|  | ||||
|         $election->addCandidate('candidate1'); | ||||
|         $election->addCandidate('candidate2'); | ||||
|         $election->addCandidate('candidate3'); | ||||
|  | ||||
|         $election->addVote('candidate1>candidate2'); | ||||
|  | ||||
|         $weakref = \WeakReference::create($election); | ||||
|  | ||||
|         // PHP circular reference can bug | ||||
|         // \debug_zval_dump($election); | ||||
|         unset($election); | ||||
|  | ||||
|         self::assertNull($weakref->get()); | ||||
|     } | ||||
|  | ||||
|     public function testRemoveCandidate(): never | ||||
|     { | ||||
|         $this->expectException(CandidateDoesNotExistException::class); | ||||
|         $this->expectExceptionMessage('This candidate does not exist: B'); | ||||
|  | ||||
|         $election = new Election; | ||||
|  | ||||
|         $election->addCandidate('candidate1'); | ||||
|  | ||||
|         $badCandidate = new Candidate('B'); | ||||
|  | ||||
|         $election->removeCandidates($badCandidate); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @dataProvider MethodsListProvider | ||||
|      */ | ||||
|     public function testRemoveCandidateResult(string $method): void | ||||
|     { | ||||
|         $votes = '  Memphis * 4 | ||||
|                     Nashville * 3 | ||||
|                     Chattanooga * 2 | ||||
|                     Knoxville * 1'; | ||||
|  | ||||
|         // Ref | ||||
|         $electionRef = new Election; | ||||
|  | ||||
|         $electionRef->addCandidate('Memphis'); | ||||
|         $electionRef->addCandidate('Nashville'); | ||||
|         $electionRef->addCandidate('Knoxville'); | ||||
|         $electionRef->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $electionRef->parseVotes($votes); | ||||
|  | ||||
|         // Test | ||||
|         $electionTest = new Election; | ||||
|  | ||||
|         $electionTest->addCandidate('Memphis'); | ||||
|         $electionTest->addCandidate('BadCandidate'); | ||||
|         $electionTest->addCandidate('Nashville'); | ||||
|         $electionTest->addCandidate('Knoxville'); | ||||
|         $electionTest->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $electionTest->removeCandidates('BadCandidate'); | ||||
|  | ||||
|         $electionTest->parseVotes($votes); | ||||
|  | ||||
|  | ||||
|         self::assertSame( | ||||
|             $electionRef->getResult($method)->getResultAsArray(true), | ||||
|             $electionTest->getResult($method)->getResultAsArray(true) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function MethodsListProvider(): array | ||||
|     { | ||||
|         $r = []; | ||||
|  | ||||
|         foreach (Condorcet::getAuthMethods() as $method) { | ||||
|             $r[] = [$method]; | ||||
|         } | ||||
|  | ||||
|         return $r; | ||||
|     } | ||||
|  | ||||
|     public function testAmbiguousCandidatesOnElectionSide(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid'); | ||||
|  | ||||
|         $vote = new Vote('candidate1>candidate2'); | ||||
|  | ||||
|         $election1 = new Election; | ||||
|         $election2 = new Election; | ||||
|  | ||||
|         $election1->addCandidate(new Candidate('candidate2')); | ||||
|         $election2->addCandidate(new Candidate('candidate1')); | ||||
|  | ||||
|         $election1->addVote($vote); | ||||
|         $election2->addVote($vote); | ||||
|     } | ||||
|  | ||||
|     public function testAmbiguousCandidatesOnVoteSide(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid: vote does not match candidate in this election'); | ||||
|  | ||||
|         $election1 = new Election; | ||||
|         $election2 = new Election; | ||||
|  | ||||
|         $election1->addCandidate(new Candidate('candidate1')); | ||||
|         $election2->addCandidate(new Candidate('candidate2')); | ||||
|  | ||||
|         $candidate3 = new Candidate('candidate3'); | ||||
|         $election1->addCandidate($candidate3); | ||||
|         $election2->addCandidate($candidate3); | ||||
|  | ||||
|         $vote = new Vote('candidate3'); | ||||
|  | ||||
|         $election1->addVote($vote); | ||||
|         $election2->addVote($vote); | ||||
|  | ||||
|         $election1->getResult(); | ||||
|         $election2->getResult(); | ||||
|  | ||||
|         try { | ||||
|             $vote->setRanking('candidate1>candidate2>candidate3'); | ||||
|         } catch (\Exception $e) { | ||||
|         } | ||||
|  | ||||
|         self::assertEmpty($election1->debugGetCalculator()); | ||||
|         self::assertEmpty($election2->debugGetCalculator()); | ||||
|  | ||||
|         throw $e; | ||||
|     } | ||||
|  | ||||
|     public function testInvalidSeats(): never | ||||
|     { | ||||
|         $this->expectException(NoSeatsException::class); | ||||
|         $this->expectExceptionMessage('No seats defined'); | ||||
|  | ||||
|         $this->election1->setNumberOfSeats(0); | ||||
|     } | ||||
|  | ||||
|     public function testSeats(): void | ||||
|     { | ||||
|         self::assertSame(100, $this->election1->getNumberOfSeats()); | ||||
|  | ||||
|         $this->election1->setNumberOfSeats(5); | ||||
|  | ||||
|         self::assertSame(5, $this->election1->getNumberOfSeats()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										334
									
								
								include/condorcet/Tests/src/ResultTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										334
									
								
								include/condorcet/Tests/src/ResultTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,334 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Algo\Methods\KemenyYoung\KemenyYoung; | ||||
| use CondorcetPHP\Condorcet\Algo\StatsVerbosity; | ||||
| use CondorcetPHP\Condorcet\{Condorcet, Election}; | ||||
| use CondorcetPHP\Condorcet\Throwable\{AlgorithmException, ResultException}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class ResultTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election1; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election1 = new Election; | ||||
|     } | ||||
|  | ||||
|     public function testGetResultAsString(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->parseVotes(' | ||||
|             B > A > C * 7 | ||||
|             A > B > C * 7 | ||||
|             C > A > B * 2 | ||||
|             C > B > A * 2 | ||||
|         '); | ||||
|  | ||||
|  | ||||
|         self::assertSame( | ||||
|             'A = B > C', | ||||
|             $this->election1->getResult('Ranked Pairs')->getResultAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testGetResultAsInternalKey(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->parseVotes(' | ||||
|             B > A > C * 7 | ||||
|             A > B > C * 7 | ||||
|             C > A > B * 2 | ||||
|             C > B > A * 2 | ||||
|         '); | ||||
|  | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => [0, 1], 2 => [2]], | ||||
|             $this->election1->getResult('Ranked Pairs')->getResultAsInternalKey() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testgetCondorcetElectionGeneratorVersion(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('C > B > A'); | ||||
|  | ||||
|         self::assertSame(Condorcet::getVersion(), $this->election1->getResult('Ranked Pairs')->getCondorcetElectionGeneratorVersion()); | ||||
|     } | ||||
|  | ||||
|     public function testResultClassgenerator(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('C > B > A'); | ||||
|  | ||||
|         self::assertSame(\CondorcetPHP\Condorcet\Algo\Methods\RankedPairs\RankedPairsMargin::class, $this->election1->getResult('Ranked Pairs')->getClassGenerator()); | ||||
|     } | ||||
|  | ||||
|     public function testMethod(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('C > B > A'); | ||||
|  | ||||
|         self::assertSame('Ranked Pairs Margin', $this->election1->getResult('Ranked Pairs')->getMethod()); | ||||
|     } | ||||
|  | ||||
|     public function testGetBuildTimeStamp(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('C > B > A'); | ||||
|  | ||||
|         self::assertIsFloat($this->election1->getResult('Ranked Pairs')->getBuildTimeStamp()); | ||||
|     } | ||||
|  | ||||
|     public function testGetWinner(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('a'); | ||||
|         $this->election1->addCandidate('b'); | ||||
|         $this->election1->addCandidate('c'); | ||||
|  | ||||
|         $this->election1->parseVotes(' | ||||
|             a > c > b * 23 | ||||
|             b > c > a * 19 | ||||
|             c > b > a * 16 | ||||
|             c > a > b * 2 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('c', $this->election1->getResult()->getWinner()); | ||||
|         self::assertEquals('c', $this->election1->getResult()->getCondorcetWinner()); | ||||
|     } | ||||
|  | ||||
|     public function testGetLoser(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('Memphis'); | ||||
|         $this->election1->addCandidate('Nashville'); | ||||
|         $this->election1->addCandidate('Knoxville'); | ||||
|         $this->election1->addCandidate('Chattanooga'); | ||||
|  | ||||
|         $this->election1->parseVotes(' | ||||
|             Memphis > Nashville > Chattanooga * 42 | ||||
|             Nashville > Chattanooga > Knoxville * 26 | ||||
|             Chattanooga > Knoxville > Nashville * 15 | ||||
|             Knoxville > Chattanooga > Nashville * 17 | ||||
|         '); | ||||
|  | ||||
|         self::assertEquals('Memphis', $this->election1->getResult()->getLoser()); | ||||
|         self::assertEquals('Memphis', $this->election1->getResult()->getCondorcetLoser()); | ||||
|     } | ||||
|  | ||||
|     public function testgetOriginalResultArrayWithString(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('a'); | ||||
|         $this->election1->addCandidate('b'); | ||||
|         $this->election1->addCandidate('c'); | ||||
|  | ||||
|         $this->election1->addVote('a > b > c'); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             [1 => 'a', | ||||
|                 2 => 'b', | ||||
|                 3 => 'c', | ||||
|             ], | ||||
|             $this->election1->getResult()->getOriginalResultArrayWithString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testOffsetSet(): never | ||||
|     { | ||||
|         $this->expectException(ResultException::class); | ||||
|         $this->expectExceptionMessage('Result cannot be changed'); | ||||
|  | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('C > B > A'); | ||||
|  | ||||
|         $result = $this->election1->getResult('Schulze'); | ||||
|  | ||||
|         $result[] = 42; | ||||
|     } | ||||
|  | ||||
|     public function testOffUnset(): never | ||||
|     { | ||||
|         $this->expectException(ResultException::class); | ||||
|         $this->expectExceptionMessage('Result cannot be changed'); | ||||
|  | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('C > B > A'); | ||||
|  | ||||
|         $result = $this->election1->getResult('Schulze'); | ||||
|  | ||||
|         self::assertTrue(isset($result[1])); | ||||
|  | ||||
|         unset($result[1]); | ||||
|     } | ||||
|  | ||||
|     public function testIterator(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $vote = $this->election1->addVote('C > B > A'); | ||||
|  | ||||
|         $result = $this->election1->getResult('Schulze'); | ||||
|  | ||||
|         foreach ($result as $key => $value) { | ||||
|             self::assertSame($vote->getRanking()[$key], $value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function testBadMethodName(): never | ||||
|     { | ||||
|         $this->expectException(AlgorithmException::class); | ||||
|         $this->expectExceptionMessage('The voting algorithm is not available: bad method'); | ||||
|  | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->parseVotes('A>B>C'); | ||||
|  | ||||
|         $this->election1->getResult('bad method'); | ||||
|     } | ||||
|  | ||||
|     public function testResultRankOrdering(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|         $this->election1->addCandidate('A'); | ||||
|  | ||||
|         $this->election1->addVote('C = A = B'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['A', 'B', 'C'], | ||||
|             ], | ||||
|             $this->election1->getResult()->getOriginalResultArrayWithString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testProportional(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('A'); | ||||
|  | ||||
|         $this->election1->setNumberOfSeats(2); | ||||
|  | ||||
|         $result = $this->election1->getResult('STV'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             2, | ||||
|             $result->getNumberOfSeats() | ||||
|         ); | ||||
|  | ||||
|         self::assertTrue( | ||||
|             $result->getClassGenerator()::IS_PROPORTIONAL | ||||
|         ); | ||||
|  | ||||
|         self::assertTrue( | ||||
|             $result->isProportional() | ||||
|         ); | ||||
|  | ||||
|         $result = $this->election1->getResult('Schulze'); | ||||
|  | ||||
|         self::assertNull( | ||||
|             $result->getNumberOfSeats() | ||||
|         ); | ||||
|  | ||||
|         self::assertFalse( | ||||
|             $result->getClassGenerator()::IS_PROPORTIONAL | ||||
|         ); | ||||
|  | ||||
|         self::assertFalse( | ||||
|             $result->isProportional() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testMethodOption(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('A>B>C'); | ||||
|  | ||||
|         $class = Condorcet::getMethodClass('Borda'); | ||||
|  | ||||
|         self::assertSame(1, $class::$optionStarting); | ||||
|  | ||||
|         $b1 = $this->election1->getResult('Borda'); | ||||
|         $c1 = $this->election1->getResult('Copeland'); | ||||
|  | ||||
|         self::assertSame(1, $b1->getMethodOptions()['Starting']); | ||||
|  | ||||
|         self::assertTrue($this->election1->setMethodOption('Borda Count', 'Starting', 0)); | ||||
|         self::assertSame(0, $class::$optionStarting); | ||||
|  | ||||
|         self::assertSame(1, $b1->getMethodOptions()['Starting']); | ||||
|  | ||||
|         $b2 = $this->election1->getResult('Borda'); | ||||
|         $c2 = $this->election1->getResult('Copeland'); | ||||
|  | ||||
|         self::assertNotSame($b1, $b2); | ||||
|         self::assertSame($c1, $c2); | ||||
|  | ||||
|         self::assertSame(0, $b2->getMethodOptions()['Starting']); | ||||
|  | ||||
|         self::assertFalse($this->election1->setMethodOption('Unregistered method', 'Starting', 0)); | ||||
|     } | ||||
|  | ||||
|     public function testVerbosityLevel(): void | ||||
|     { | ||||
|         $this->election1->addCandidate('A'); | ||||
|         $this->election1->addCandidate('B'); | ||||
|         $this->election1->addCandidate('C'); | ||||
|  | ||||
|         $this->election1->addVote('A>B>C'); | ||||
|  | ||||
|         $r1 = $this->election1->getResult(KemenyYoung::class); | ||||
|         self::assertSame(StatsVerbosity::STD, $r1->statsVerbosity); | ||||
|  | ||||
|         $this->election1->setStatsVerbosity(StatsVerbosity::STD); | ||||
|         $r2 = $this->election1->getResult(KemenyYoung::class); | ||||
|         self::assertSame($r1, $r2); | ||||
|  | ||||
|         $this->election1->setStatsVerbosity(StatsVerbosity::FULL); | ||||
|         $r3 = $this->election1->getResult(KemenyYoung::class); | ||||
|  | ||||
|         self::assertSame(StatsVerbosity::STD, $r1->statsVerbosity); | ||||
|         self::assertSame(StatsVerbosity::FULL, $r3->statsVerbosity); | ||||
|  | ||||
|         self::assertNotSame($r1, $r3); | ||||
|         self::assertArrayNotHasKey('Ranking Scores', $r1->getStats()); | ||||
|         self::assertArrayHasKey('Ranking Scores', $r3->getStats()); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,19 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Throwable\Internal\CondorcetInternalError; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CondorcetInternalErrorTest extends TestCase | ||||
| { | ||||
|     public function testMessage(): void | ||||
|     { | ||||
|         $this->expectException(CondorcetInternalError::class); | ||||
|         $this->expectExceptionMessage($message = 'Test message'); | ||||
|  | ||||
|         throw new CondorcetInternalError($message); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								include/condorcet/Tests/src/Timer/TimerTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								include/condorcet/Tests/src/Timer/TimerTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Timer; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Throwable\TimerException; | ||||
| use CondorcetPHP\Condorcet\Timer\{Chrono, Manager}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class TimerTest extends TestCase | ||||
| { | ||||
|     public function testInvalidChrono(): never | ||||
|     { | ||||
|         $this->expectException(TimerException::class); | ||||
|         $this->expectExceptionMessage('Only a chrono linked to this manager can be used'); | ||||
|  | ||||
|         $manager1 = new Manager; | ||||
|         $manager2 = new Manager; | ||||
|  | ||||
|         $chrono1 = new Chrono($manager1); | ||||
|         $chrono2 = new Chrono($manager2); | ||||
|  | ||||
|         $manager1->addTime($chrono1); | ||||
|         $manager1->addTime($chrono2); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,5 @@ | ||||
| #/Candidates:A;B;C | ||||
| #/Implicit Ranking: true | ||||
| #/Weight Allowed: false | ||||
|  | ||||
| A>B ^2 | ||||
| @@ -0,0 +1,7 @@ | ||||
| #/Candidates:A;B;C | ||||
| #/Implicit Ranking: false | ||||
| #/Weight Allowed: false | ||||
| #Number of Seats: 42 | ||||
|  | ||||
| B>A ^2 | ||||
| B>C | ||||
| @@ -0,0 +1,3 @@ | ||||
| #/Candidates:A;B;C | ||||
|  | ||||
| C>B>A * 700 | ||||
| @@ -0,0 +1,402 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use CondorcetPHP\Condorcet\Throwable\FileDoesNotExistException; | ||||
| use CondorcetPHP\Condorcet\Tools\Converters\CondorcetElectionFormat; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class CondorcetElectionFormatTest extends TestCase | ||||
| { | ||||
|     public function testCondorcetElectionFormat1_Simple(): void | ||||
|     { | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite(<<<'CVOTES' | ||||
|             #/Candidates: Richard Boháč ; Petr Němec ; Simona Slaná | ||||
|             Richard Boháč>Petr Němec ^42 | ||||
|             CVOTES); | ||||
|  | ||||
|         $condorcetFormat = new CondorcetElectionFormat($file); | ||||
|         self::assertFalse($condorcetFormat->CandidatesParsedFromVotes); | ||||
|  | ||||
|  | ||||
|         $election = $condorcetFormat->setDataToAnElection(); | ||||
|  | ||||
|         self::assertFalse($election->isVoteWeightAllowed()); | ||||
|         $election->allowsVoteWeight(true); | ||||
|         self::assertTrue($election->isVoteWeightAllowed()); | ||||
|  | ||||
|  | ||||
|         self::assertSame(['Petr Němec', 'Richard Boháč', 'Simona Slaná'], $election->getCandidatesListAsString()); | ||||
|  | ||||
|         self::assertSame(100, $election->getNumberOfSeats()); | ||||
|         self::assertTrue($election->getImplicitRankingRule()); | ||||
|  | ||||
|         self::assertSame(1, $election->countVotes()); | ||||
|         self::assertSame('Richard Boháč > Petr Němec > Simona Slaná ^42', $election->getVotesList()[0]->getSimpleRanking(context: $election, displayWeight: true)); | ||||
|         self::assertSame(0, $condorcetFormat->invalidBlocksCount); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function testCondorcetElectionFormat2_MultiplesErrorsAndComplications(): void | ||||
|     { | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite(<<<'CVOTES' | ||||
|             #/Candidates: A    ;B;C | ||||
|             #/Number of Seats:      6 | ||||
|             #/Implicit Ranking: false | ||||
|  | ||||
|                             A > B > C | ||||
|                         A > B > C * 4;tag1 || A > B > C*4 #Coucou | ||||
|             # A > B > C | ||||
|                         A < B < C * 10 | ||||
|                         A > E > A* 3 | ||||
|                 D <> B | ||||
|                         A > B > C | ||||
|             CVOTES); | ||||
|  | ||||
|         $condorcetFormat = new CondorcetElectionFormat($file); | ||||
|  | ||||
|         $election = $condorcetFormat->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame(['A', 'B', 'C'], $election->getCandidatesListAsString()); | ||||
|  | ||||
|         self::assertSame(6, $election->getNumberOfSeats()); | ||||
|  | ||||
|         self::assertFalse($election->getImplicitRankingRule()); | ||||
|  | ||||
|         self::assertSame(10, $election->countVotes()); | ||||
|  | ||||
|         self::assertSame(3, $condorcetFormat->invalidBlocksCount); | ||||
|  | ||||
|         self::assertSame(['tag1'], $election->getVotesList()[5]->getTags()); | ||||
|     } | ||||
|  | ||||
|     public function testCondorcetElectionFormat3_CustomElection1(): void | ||||
|     { | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite(<<<'CVOTES' | ||||
|             #/Candidates: Richard Boháč ; Petr Němec ; Simona Slaná | ||||
|             #/Number of Seats: 42 | ||||
|             #/Implicit Ranking: true | ||||
|             Richard Boháč>Petr Němec ^42 | ||||
|             CVOTES); | ||||
|  | ||||
|         $condorcetFormat = new CondorcetElectionFormat($file); | ||||
|  | ||||
|         $election = new Election; | ||||
|         $election->setImplicitRanking(false); | ||||
|         $election->setNumberOfSeats(66); | ||||
|  | ||||
|         $condorcetFormat->setDataToAnElection($election); | ||||
|  | ||||
|         self::assertSame(['Petr Němec', 'Richard Boháč', 'Simona Slaná'], $election->getCandidatesListAsString()); | ||||
|  | ||||
|         self::assertSame(42, $election->getNumberOfSeats()); | ||||
|         self::assertTrue($election->getImplicitRankingRule()); | ||||
|  | ||||
|         self::assertSame(1, $election->countVotes()); | ||||
|         self::assertSame('Richard Boháč > Petr Němec > Simona Slaná', $election->getVotesList()[0]->getSimpleRanking(context: $election, displayWeight: true)); | ||||
|         self::assertSame(0, $condorcetFormat->invalidBlocksCount); | ||||
|     } | ||||
|  | ||||
|     public function testCondorcetElectionFormat4_CustomElection2(): void | ||||
|     { | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite(<<<'CVOTES' | ||||
|             #/Candidates: Richard Boháč ; Petr Němec ; Simona Slaná | ||||
|             #/Weight allowed: true | ||||
|             Richard Boháč>Petr Němec ^42 | ||||
|             CVOTES); | ||||
|  | ||||
|         $condorcetFormat = new CondorcetElectionFormat($file); | ||||
|  | ||||
|         $election = new Election; | ||||
|         $election->setImplicitRanking(false); | ||||
|         $election->setNumberOfSeats(66); | ||||
|         $election->allowsVoteWeight(false); | ||||
|  | ||||
|         $condorcetFormat->setDataToAnElection($election); | ||||
|  | ||||
|         $election->allowsVoteWeight(true); // Must be forced by parameter | ||||
|  | ||||
|  | ||||
|         self::assertSame(['Petr Němec', 'Richard Boháč', 'Simona Slaná'], $election->getCandidatesListAsString()); | ||||
|  | ||||
|         self::assertSame(66, $election->getNumberOfSeats()); | ||||
|         self::assertFalse($election->getImplicitRankingRule()); | ||||
|  | ||||
|         self::assertSame(1, $election->countVotes()); | ||||
|         self::assertSame('Richard Boháč > Petr Němec ^42', $election->getVotesList()[0]->getSimpleRanking(context: $election, displayWeight: true)); | ||||
|         self::assertSame(0, $condorcetFormat->invalidBlocksCount); | ||||
|     } | ||||
|  | ||||
|     public function testCondorcetElectionFormat5_UnknowParametersAndEmptyLinesAndCase(): void | ||||
|     { | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite(<<<'CVOTES' | ||||
|             #/Candidates: Richard Boháč ; 郝文彦  ; Simona Slaná | ||||
|  | ||||
|             #/AnewParameters: 7 | ||||
|             #/numBer of Seats: 42 | ||||
|             #/implicit ranking: true | ||||
|  | ||||
|  | ||||
|  | ||||
|                  Richard Boháč>郝文彦 ^42 | ||||
|             CVOTES); | ||||
|  | ||||
|         $condorcetFormat = new CondorcetElectionFormat($file); | ||||
|  | ||||
|         $election = new Election; | ||||
|         $election->setImplicitRanking(false); | ||||
|         $election->setNumberOfSeats(66); | ||||
|  | ||||
|         $condorcetFormat->setDataToAnElection($election); | ||||
|  | ||||
|         self::assertSame(['Richard Boháč', 'Simona Slaná', '郝文彦'], $election->getCandidatesListAsString()); | ||||
|  | ||||
|         self::assertSame(42, $election->getNumberOfSeats()); | ||||
|         self::assertTrue($election->getImplicitRankingRule()); | ||||
|  | ||||
|         self::assertSame(1, $election->countVotes()); | ||||
|         self::assertSame('Richard Boháč > 郝文彦 > Simona Slaná', $election->getVotesList()[0]->getSimpleRanking(context: $election, displayWeight: true)); | ||||
|         self::assertSame(0, $condorcetFormat->invalidBlocksCount); | ||||
|     } | ||||
|  | ||||
|     public function testOfficialSpecificationValidExamples(): void | ||||
|     { | ||||
|         # Example with tags and implicit ranking | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite(<<<'CVOTES' | ||||
|             # My beautiful election | ||||
|             #/Candidates: Candidate A;Candidate B;Candidate C | ||||
|             #/Implicit Ranking: true | ||||
|             #/Weight allowed: true | ||||
|  | ||||
|             # Here the votes datas: | ||||
|             Candidate A > Candidate B > Candidate C * 42 | ||||
|             julien@condorcet.vote , signature:55073db57b0a859911 || Candidate A > Candidate B > Candidate C # Same as above, so there will be 43 votes with this ranking. And tags are registered by the software if able. | ||||
|             Candidate C > Candidate A = Candidate B ^7 * 8 # 8 votes with a weight of 7. | ||||
|             Candidate B = Candidate A > Candidate C | ||||
|             Candidate C # Interpreted as Candidate C > Candidate A = Candidate B, because implicit ranking is true (wich is also default, but it's better to say it) | ||||
|             Candidate B > Candidate C # Interpreted as Candidate B > Candidate C | ||||
|             CVOTES); | ||||
|  | ||||
|         $condorcetFormat = new CondorcetElectionFormat($file); | ||||
|         $election = $condorcetFormat->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame(54, $election->countVotes()); | ||||
|  | ||||
|         self::assertSame(<<<'VOTES' | ||||
|             Candidate C > Candidate A = Candidate B ^7 * 8 | ||||
|             Candidate A > Candidate B > Candidate C * 43 | ||||
|             Candidate A = Candidate B > Candidate C * 1 | ||||
|             Candidate B > Candidate C > Candidate A * 1 | ||||
|             Candidate C > Candidate A = Candidate B * 1 | ||||
|             VOTES | ||||
|             , $election->getVotesListAsString()); | ||||
|  | ||||
|  | ||||
|         self::assertCount(1, $election->getVotesList(tags: 'signature:55073db57b0a859911', with: true)); | ||||
|         self::assertCount(1, $election->getVotesList(tags: 'julien@condorcet.vote', with: true)); | ||||
|         self::assertCount(0, $election->getVotesList(tags: 'otherTag', with: true)); | ||||
|         self::assertSame('Candidate A > Candidate B > Candidate C', current($election->getVotesList(tags: 'julien@condorcet.vote', with: true))->getSimpleRanking()); | ||||
|  | ||||
|  | ||||
|         # Example without implicit ranking as weight | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite(<<<'CVOTES' | ||||
|             # My beautiful election | ||||
|             #/Candidates: Candidate A ; Candidate B ; Candidate C | ||||
|             #/Implicit Ranking: false | ||||
|             #/Weight allowed: false | ||||
|  | ||||
|             # Here the votes datas: | ||||
|             Candidate A > Candidate B > Candidate C ^7 *2 # Vote weight is disable, so ^7 is ignored. Two vote with weight of 1. | ||||
|             Candidate C>Candidate B # Vote is untouched. When compute pairwise, Candidate C win again Candidate B, no one beats the candidate or achieves a draw. | ||||
|             Candidate B # Vote is valid, but not have any effect on most election method, especially Condorcet methods. | ||||
|             CVOTES); | ||||
|  | ||||
|         $condorcetFormat = new CondorcetElectionFormat($file); | ||||
|         $election = $condorcetFormat->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame(4, $election->countVotes()); | ||||
|  | ||||
|         self::assertSame(<<<'VOTES' | ||||
|             Candidate A > Candidate B > Candidate C * 2 | ||||
|             Candidate B * 1 | ||||
|             Candidate C > Candidate B * 1 | ||||
|             VOTES | ||||
|             , $election->getVotesListAsString()); | ||||
|     } | ||||
|  | ||||
|     public function testexportElectionToCondorcetElectionFormat(): void | ||||
|     { | ||||
|         $input = new \SplTempFileObject; | ||||
|         $input->fwrite(<<<'CVOTES' | ||||
|             #/Weight allowed: true | ||||
|             #/Candidates: Richard Boháč ; Petr Němec ; Simona Slaná | ||||
|             #/Number of Seats: 42 | ||||
|             #/Implicit Ranking: true | ||||
|  | ||||
|             Richard Boháč>Petr Němec ^7 | ||||
|             Richard Boháč>Petr Němec | ||||
|             tag1 ,  tag b || Richard Boháč>Petr Němec | ||||
|             Simona Slaná * 2 | ||||
|             Petr Němec *1 | ||||
|             CVOTES); | ||||
|  | ||||
|         $election = (new CondorcetElectionFormat($input))->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $assertion1 = | ||||
|             <<<'CVOTES' | ||||
|                 #/Candidates: Petr Němec ; Richard Boháč ; Simona Slaná | ||||
|                 #/Number of Seats: 42 | ||||
|                 #/Implicit Ranking: true | ||||
|                 #/Weight Allowed: true | ||||
|  | ||||
|                 Richard Boháč > Petr Němec ^7 * 1 | ||||
|                 Richard Boháč > Petr Němec * 2 | ||||
|                 Simona Slaná * 2 | ||||
|                 Petr Němec * 1 | ||||
|                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $election) | ||||
|         ); | ||||
|  | ||||
|         self::assertStringNotContainsString('Number of Seats: 42', CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $election, includeNumberOfSeats: false)); | ||||
|  | ||||
|         $election->setImplicitRanking(false); | ||||
|         self::assertSame( | ||||
|             <<<'CVOTES' | ||||
|                 #/Candidates: Petr Němec ; Richard Boháč ; Simona Slaná | ||||
|                 #/Number of Seats: 42 | ||||
|                 #/Implicit Ranking: false | ||||
|                 #/Weight Allowed: true | ||||
|  | ||||
|                 Richard Boháč > Petr Němec ^7 * 1 | ||||
|                 Richard Boháč > Petr Němec * 2 | ||||
|                 Simona Slaná * 2 | ||||
|                 Petr Němec * 1 | ||||
|                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $election) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'CVOTES' | ||||
|                 #/Candidates: Petr Němec ; Richard Boháč ; Simona Slaná | ||||
|                 #/Number of Seats: 42 | ||||
|                 #/Implicit Ranking: false | ||||
|                 #/Weight Allowed: true | ||||
|  | ||||
|                 Richard Boháč > Petr Němec ^7 | ||||
|                 Richard Boháč > Petr Němec | ||||
|                 tag1,tag b || Richard Boháč > Petr Němec | ||||
|                 Simona Slaná | ||||
|                 Simona Slaná | ||||
|                 Petr Němec | ||||
|                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $election, aggregateVotes: false) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $assertion5 = | ||||
|             <<<'CVOTES' | ||||
|                 #/Candidates: Petr Němec ; Richard Boháč ; Simona Slaná | ||||
|                 #/Number of Seats: 42 | ||||
|                 #/Implicit Ranking: false | ||||
|                 #/Weight Allowed: true | ||||
|  | ||||
|                 Richard Boháč > Petr Němec ^7 | ||||
|                 Richard Boháč > Petr Němec | ||||
|                 Richard Boháč > Petr Němec | ||||
|                 Simona Slaná | ||||
|                 Simona Slaná | ||||
|                 Petr Němec | ||||
|                 CVOTES, | ||||
|             CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $election, aggregateVotes: false, includeTags: false) | ||||
|         ); | ||||
|  | ||||
|         $election->setImplicitRanking(true); | ||||
|         $output = new \SplTempFileObject; | ||||
|         self::assertNull(CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $election, file: $output)); | ||||
|         $output->rewind(); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $assertion1, | ||||
|             $output->fread(2048) | ||||
|         ); | ||||
|  | ||||
|         $election->setImplicitRanking(false); | ||||
|         $output = new \SplTempFileObject; | ||||
|         self::assertNull(CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $election, aggregateVotes: false, includeTags: false, file: $output)); | ||||
|         $output->rewind(); | ||||
|         self::assertSame( | ||||
|             $assertion5, | ||||
|             $output->fread(2048) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testEmptyRankingImport(): void | ||||
|     { | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite($input =  <<<'CVOTES' | ||||
|             #/Candidates: A ; B ; C | ||||
|             #/Number of Seats: 42 | ||||
|             #/Implicit Ranking: false | ||||
|             #/Weight Allowed: false | ||||
|  | ||||
|             /EMPTY_RANKING/ * 1 | ||||
|             D > E * 1 | ||||
|             CVOTES); | ||||
|  | ||||
|         $cef = new CondorcetElectionFormat($file); | ||||
|  | ||||
|         $election = $cef->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame('/EMPTY_RANKING/ * 2', $election->getVotesListAsString()); | ||||
|         self::assertSame([], $election->getVotesList()[0]->getRanking()); | ||||
|         self::assertSame($input, CondorcetElectionFormat::exportElectionToCondorcetElectionFormat($election)); | ||||
|     } | ||||
|  | ||||
|     public function testCandidatesFromVotes(): void | ||||
|     { | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite($input =  <<<'CVOTES' | ||||
|             #/Number of Seats: 42 | ||||
|             #/Implicit Ranking: false | ||||
|             #/Weight Allowed: false | ||||
|  | ||||
|             /EMPTY_RANKING/ * 1 | ||||
|             D > E^7 * 2 # Comment | ||||
|  | ||||
|             tag1, tag2 ||A= B > C = D > F | ||||
|             D > F = A | ||||
|             D>A>B>C>E>F | ||||
|             CVOTES); | ||||
|  | ||||
|         $cef = new CondorcetElectionFormat($file); | ||||
|  | ||||
|         self::assertSame(['A', 'B', 'C', 'D', 'E', 'F'], $cef->candidates); | ||||
|         self::assertTrue($cef->CandidatesParsedFromVotes); | ||||
|  | ||||
|         $election = $cef->setDataToAnElection(); | ||||
|  | ||||
|         self::assertFalse($election->getImplicitRankingRule()); | ||||
|         self::assertSame(42, $election->getNumberOfSeats()); | ||||
|  | ||||
|         self::assertEquals(['A', 'B', 'C', 'D', 'E', 'F'], $election->getCandidatesList()); | ||||
|         self::assertSame('D > A > B > C > E > F', $election->getResult()->getResultAsString()); | ||||
|     } | ||||
|  | ||||
|     public function testFileDoesNotExists(): void | ||||
|     { | ||||
|         $this->expectException(FileDoesNotExistException::class); | ||||
|  | ||||
|         new CondorcetElectionFormat(__DIR__.'noFile.txt'); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,380 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Condorcet, Election}; | ||||
| use CondorcetPHP\Condorcet\Tools\Converters\{CondorcetElectionFormat, DavidHillFormat}; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class DavidHillFormatTest extends TestCase | ||||
| { | ||||
|     private static DavidHillFormat $tidemanA77; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         self::$tidemanA77 ?? (self::$tidemanA77 = new DavidHillFormat(__DIR__.'/TidemanData/A77.HIL')); | ||||
|     } | ||||
|  | ||||
|     public function testA77_With_Implicit(): void | ||||
|     { | ||||
|         $election = self::$tidemanA77->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame(213, $election->countVotes()); | ||||
|         self::assertSame(1, $election->getNumberOfSeats()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'EOD' | ||||
|                 3 > 1 = 2 * 39 | ||||
|                 1 > 3 > 2 * 38 | ||||
|                 3 > 1 > 2 * 36 | ||||
|                 3 > 2 > 1 * 29 | ||||
|                 1 > 2 > 3 * 28 | ||||
|                 2 > 1 > 3 * 15 | ||||
|                 1 > 2 = 3 * 14 | ||||
|                 2 > 3 > 1 * 9 | ||||
|                 2 > 1 = 3 * 5 | ||||
|                 EOD, | ||||
|             $election->getVotesListAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testA77_With_Explicit(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|         $election->setImplicitRanking(false); | ||||
|  | ||||
|         self::$tidemanA77->setDataToAnElection($election); | ||||
|  | ||||
|         self::assertSame(213, $election->countVotes()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'EOD' | ||||
|                 3 * 39 | ||||
|                 1 > 3 * 38 | ||||
|                 3 > 1 * 36 | ||||
|                 3 > 2 * 29 | ||||
|                 1 > 2 * 28 | ||||
|                 2 > 1 * 15 | ||||
|                 1 * 14 | ||||
|                 2 > 3 * 9 | ||||
|                 2 * 5 | ||||
|                 EOD, | ||||
|             $election->getVotesListAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testA1_ForCandidatesNames(): void | ||||
|     { | ||||
|         $election = (new DavidHillFormat(__DIR__.'/TidemanData/A1.HIL'))->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame(380, $election->countVotes()); | ||||
|         self::assertSame(3, $election->getNumberOfSeats()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             <<<'EOD' | ||||
|                 Candidate  3 > Candidate  1 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 13 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 9 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  9 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 9 | ||||
|                 Candidate  2 > Candidate  8 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 6 | ||||
|                 Candidate  1 > Candidate  5 > Candidate  8 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 5 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  9 > Candidate  7 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 4 | ||||
|                 Candidate  1 > Candidate  8 > Candidate  4 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 4 | ||||
|                 Candidate  1 > Candidate  9 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 4 | ||||
|                 Candidate  3 > Candidate  6 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate  9 = Candidate 10 * 4 | ||||
|                 Candidate  4 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 4 | ||||
|                 Candidate  7 > Candidate  9 > Candidate  3 > Candidate  1 = Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 4 | ||||
|                 Candidate  9 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 4 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  7 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 4 | ||||
|                 Candidate  1 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 3 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  2 > Candidate  7 > Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate  9 = Candidate 10 * 3 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  4 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 3 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 3 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  9 > Candidate  3 > Candidate  8 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 3 | ||||
|                 Candidate  1 > Candidate  5 > Candidate  9 > Candidate  2 > Candidate  7 > Candidate 10 > Candidate  3 = Candidate  4 = Candidate  6 = Candidate  8 * 3 | ||||
|                 Candidate  1 > Candidate  7 > Candidate  9 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 3 | ||||
|                 Candidate  1 > Candidate  8 > Candidate  4 > Candidate  9 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 3 | ||||
|                 Candidate  1 > Candidate  9 > Candidate  5 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 3 | ||||
|                 Candidate  2 > Candidate  4 > Candidate  8 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 3 | ||||
|                 Candidate  2 > Candidate  9 > Candidate  7 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 3 | ||||
|                 Candidate  3 > Candidate  1 > Candidate  9 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 3 | ||||
|                 Candidate  7 > Candidate  9 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 3 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 3 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  2 > Candidate  7 > Candidate  9 > Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 2 | ||||
|                 Candidate  1 > Candidate  5 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 2 | ||||
|                 Candidate  1 > Candidate  7 > Candidate  9 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 2 | ||||
|                 Candidate  1 > Candidate  9 > Candidate  8 > Candidate  2 > Candidate  3 > Candidate  7 > Candidate  4 > Candidate 10 > Candidate  5 = Candidate  6 * 2 | ||||
|                 Candidate  1 > Candidate 10 > Candidate  9 > Candidate  2 > Candidate  3 > Candidate  4 > Candidate  5 > Candidate  6 > Candidate  7 > Candidate  8 * 2 | ||||
|                 Candidate  2 > Candidate  3 > Candidate  9 > Candidate 10 > Candidate  8 > Candidate  1 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 * 2 | ||||
|                 Candidate  2 > Candidate  7 > Candidate  8 > Candidate  9 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 2 | ||||
|                 Candidate  2 > Candidate  7 > Candidate  9 > Candidate  8 > Candidate 10 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 * 2 | ||||
|                 Candidate  2 > Candidate  8 > Candidate  7 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  9 = Candidate 10 * 2 | ||||
|                 Candidate  2 > Candidate  8 > Candidate  7 > Candidate  9 > Candidate  4 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate 10 * 2 | ||||
|                 Candidate  2 > Candidate  9 > Candidate  8 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 2 | ||||
|                 Candidate  4 > Candidate  1 > Candidate  9 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 2 | ||||
|                 Candidate  4 > Candidate  3 > Candidate  6 > Candidate  1 = Candidate  2 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 2 | ||||
|                 Candidate  4 > Candidate  9 > Candidate  6 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate 10 * 2 | ||||
|                 Candidate  6 > Candidate  7 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  9 = Candidate 10 * 2 | ||||
|                 Candidate  6 > Candidate  7 > Candidate  8 > Candidate  9 > Candidate  1 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate 10 * 2 | ||||
|                 Candidate  7 > Candidate  1 > Candidate  3 > Candidate  9 > Candidate  8 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 2 | ||||
|                 Candidate  8 > Candidate  1 > Candidate  3 > Candidate  4 > Candidate  2 > Candidate  5 > Candidate 10 > Candidate  9 > Candidate  7 > Candidate  6 * 2 | ||||
|                 Candidate  8 > Candidate  1 > Candidate  7 > Candidate  9 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 2 | ||||
|                 Candidate  8 > Candidate  1 > Candidate 10 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 * 2 | ||||
|                 Candidate  8 > Candidate  2 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 2 | ||||
|                 Candidate  8 > Candidate  2 > Candidate  1 > Candidate  3 > Candidate  4 > Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 2 | ||||
|                 Candidate  8 > Candidate  2 > Candidate  6 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate  9 = Candidate 10 * 2 | ||||
|                 Candidate  8 > Candidate  2 > Candidate  7 > Candidate  9 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 2 | ||||
|                 Candidate  8 > Candidate  3 > Candidate  1 > Candidate  6 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate  9 = Candidate 10 * 2 | ||||
|                 Candidate  8 > Candidate  3 > Candidate  1 > Candidate  6 > Candidate  9 > Candidate  7 > Candidate 10 > Candidate  2 = Candidate  4 = Candidate  5 * 2 | ||||
|                 Candidate  9 > Candidate  1 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 2 | ||||
|                 Candidate  9 > Candidate  2 > Candidate  7 > Candidate  8 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 2 | ||||
|                 Candidate  9 > Candidate  4 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 2 | ||||
|                 Candidate  9 > Candidate  4 > Candidate  3 > Candidate  1 = Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 2 | ||||
|                 Candidate  9 > Candidate  4 > Candidate  3 > Candidate  1 > Candidate  7 > Candidate  8 > Candidate  6 > Candidate  2 > Candidate 10 > Candidate  5 * 2 | ||||
|                 Candidate  9 > Candidate  7 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 2 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  3 > Candidate  6 > Candidate  7 > Candidate  2 > Candidate  1 = Candidate  4 = Candidate  5 = Candidate 10 * 2 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  4 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 2 | ||||
|                 Candidate  9 > Candidate 10 > Candidate  2 > Candidate  3 > Candidate  1 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 2 | ||||
|                 Candidate 10 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 2 | ||||
|                 Candidate 10 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 2 | ||||
|                 Candidate 10 > Candidate  6 > Candidate  1 > Candidate  8 > Candidate  3 > Candidate  5 > Candidate  2 = Candidate  4 = Candidate  7 = Candidate  9 * 2 | ||||
|                 Candidate 10 > Candidate  9 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 * 2 | ||||
|                 Candidate  1 > Candidate  2 > Candidate  3 > Candidate  7 > Candidate  8 > Candidate  9 > Candidate  5 > Candidate  6 > Candidate 10 > Candidate  4 * 1 | ||||
|                 Candidate  1 > Candidate  2 > Candidate  4 > Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  2 > Candidate  4 > Candidate  3 > Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  2 > Candidate  4 > Candidate  5 > Candidate  8 > Candidate 10 > Candidate  3 = Candidate  6 = Candidate  7 = Candidate  9 * 1 | ||||
|                 Candidate  1 > Candidate  2 > Candidate  5 > Candidate  3 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  4 > Candidate  8 > Candidate  6 > Candidate  5 > Candidate  7 > Candidate  9 > Candidate 10 > Candidate  2 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  4 > Candidate  9 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  4 > Candidate  9 > Candidate  5 > Candidate  6 > Candidate 10 > Candidate  2 > Candidate  8 > Candidate  7 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  5 > Candidate  2 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  5 > Candidate  9 > Candidate  2 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  6 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  7 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  7 > Candidate  8 > Candidate 10 > Candidate  9 > Candidate  2 > Candidate  6 > Candidate  4 > Candidate  5 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  7 > Candidate  9 > Candidate  2 > Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  8 > Candidate  4 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  8 > Candidate  9 > Candidate 10 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  9 > Candidate  8 > Candidate  4 > Candidate  2 > Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  3 > Candidate  9 > Candidate 10 > Candidate  7 > Candidate  8 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 * 1 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  3 > Candidate  6 > Candidate  8 > Candidate  5 > Candidate  2 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  3 > Candidate  6 > Candidate 10 > Candidate  2 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  5 > Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  5 > Candidate  8 > Candidate 10 > Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 = Candidate  9 * 1 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  5 > Candidate  9 > Candidate  8 > Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  8 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  8 > Candidate  7 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  4 > Candidate  9 > Candidate 10 > Candidate  6 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate  1 > Candidate  4 > Candidate 10 > Candidate  5 > Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate  1 > Candidate  5 > Candidate  4 > Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  5 > Candidate  4 > Candidate  9 > Candidate  2 > Candidate  3 > Candidate  7 > Candidate  6 > Candidate  8 > Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  6 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  6 > Candidate  5 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  7 > Candidate  9 > Candidate  6 > Candidate  2 > Candidate  3 > Candidate  4 = Candidate  5 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  7 > Candidate  9 > Candidate  8 > Candidate  3 > Candidate  4 > Candidate  6 > Candidate 10 > Candidate  5 > Candidate  2 * 1 | ||||
|                 Candidate  1 > Candidate  7 > Candidate  9 > Candidate  8 > Candidate  6 > Candidate  3 > Candidate  2 > Candidate 10 > Candidate  4 > Candidate  5 * 1 | ||||
|                 Candidate  1 > Candidate  8 > Candidate  3 > Candidate  4 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  8 > Candidate  3 > Candidate  9 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  8 > Candidate  6 > Candidate  4 > Candidate  2 > Candidate  3 > Candidate  5 > Candidate 10 > Candidate  9 > Candidate  7 * 1 | ||||
|                 Candidate  1 > Candidate  8 > Candidate  9 > Candidate  3 > Candidate  4 > Candidate  2 > Candidate  6 > Candidate  5 > Candidate  7 > Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  8 > Candidate  9 > Candidate 10 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate  2 > Candidate  4 > Candidate  8 > Candidate 10 > Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate  4 > Candidate  3 > Candidate  8 > Candidate 10 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate  4 > Candidate  7 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate  7 > Candidate  4 > Candidate  5 > Candidate  2 > Candidate 10 > Candidate  8 > Candidate  6 > Candidate  3 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate  7 > Candidate  6 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate  8 > Candidate  3 > Candidate 10 > Candidate  2 > Candidate  4 > Candidate  5 > Candidate  6 > Candidate  7 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate 10 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate 10 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate 10 > Candidate  4 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate 10 > Candidate  4 > Candidate  7 > Candidate  3 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  8 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate 10 > Candidate  4 > Candidate  8 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 * 1 | ||||
|                 Candidate  1 > Candidate  9 > Candidate 10 > Candidate  8 > Candidate  4 > Candidate  3 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 * 1 | ||||
|                 Candidate  1 > Candidate 10 > Candidate  3 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate  1 > Candidate 10 > Candidate  5 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate  1 > Candidate 10 > Candidate  9 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate  1 > Candidate 10 > Candidate  9 > Candidate  2 > Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate  1 > Candidate 10 > Candidate  9 > Candidate  4 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate  2 > Candidate  3 > Candidate  1 > Candidate  4 > Candidate  9 > Candidate 10 > Candidate  8 > Candidate  5 > Candidate  6 > Candidate  7 * 1 | ||||
|                 Candidate  2 > Candidate  3 > Candidate  9 > Candidate  1 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  2 > Candidate  4 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  2 > Candidate  4 > Candidate  9 > Candidate  7 > Candidate  3 > Candidate  8 > Candidate 10 > Candidate  6 > Candidate  1 > Candidate  5 * 1 | ||||
|                 Candidate  2 > Candidate  5 > Candidate  3 > Candidate  1 > Candidate  4 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  2 > Candidate  7 > Candidate  9 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  2 > Candidate  8 > Candidate  9 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  2 > Candidate  8 > Candidate  9 > Candidate  4 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  2 > Candidate  8 > Candidate 10 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 * 1 | ||||
|                 Candidate  2 > Candidate  9 > Candidate  1 > Candidate 10 > Candidate  4 > Candidate  3 > Candidate  8 > Candidate  5 = Candidate  6 = Candidate  7 * 1 | ||||
|                 Candidate  2 > Candidate  9 > Candidate  7 > Candidate  4 > Candidate  8 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  2 > Candidate  9 > Candidate  7 > Candidate  8 > Candidate  6 > Candidate 10 > Candidate  5 > Candidate  4 > Candidate  3 > Candidate  1 * 1 | ||||
|                 Candidate  3 > Candidate  1 > Candidate  2 > Candidate  4 > Candidate  5 > Candidate  6 > Candidate  7 > Candidate  8 > Candidate  9 > Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  1 > Candidate  5 > Candidate  2 > Candidate  4 > Candidate  7 > Candidate  6 > Candidate  9 > Candidate  8 > Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  1 > Candidate  6 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  1 > Candidate  8 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  4 > Candidate  1 > Candidate  9 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  4 > Candidate  5 > Candidate  8 > Candidate 10 > Candidate  1 > Candidate  2 > Candidate  9 > Candidate  6 > Candidate  7 * 1 | ||||
|                 Candidate  3 > Candidate  4 > Candidate  9 > Candidate  7 > Candidate  1 = Candidate  2 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  4 > Candidate 10 > Candidate  1 > Candidate  6 > Candidate  2 > Candidate  5 > Candidate  8 > Candidate  9 > Candidate  7 * 1 | ||||
|                 Candidate  3 > Candidate  5 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  6 > Candidate  1 = Candidate  2 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  7 > Candidate  1 > Candidate  5 > Candidate  2 = Candidate  4 = Candidate  6 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  8 > Candidate  2 > Candidate  7 > Candidate  1 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  9 > Candidate  1 > Candidate  8 > Candidate  7 > Candidate  5 > Candidate  4 > Candidate  2 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  3 > Candidate  9 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  1 > Candidate  3 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  1 > Candidate  5 > Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  2 > Candidate  7 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  3 > Candidate  1 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  3 > Candidate  1 > Candidate  2 > Candidate  9 > Candidate 10 > Candidate  8 > Candidate  6 > Candidate  7 > Candidate  5 * 1 | ||||
|                 Candidate  4 > Candidate  3 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  3 > Candidate  8 > Candidate  1 > Candidate 10 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 * 1 | ||||
|                 Candidate  4 > Candidate  3 > Candidate  8 > Candidate  9 > Candidate  1 = Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  5 > Candidate  1 > Candidate  8 > Candidate  9 > Candidate 10 > Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 * 1 | ||||
|                 Candidate  4 > Candidate  5 > Candidate  7 > Candidate 10 > Candidate  1 > Candidate  2 > Candidate  8 > Candidate  3 = Candidate  6 = Candidate  9 * 1 | ||||
|                 Candidate  4 > Candidate  5 > Candidate  8 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  5 > Candidate  9 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  7 > Candidate 10 > Candidate  3 > Candidate  8 > Candidate  1 > Candidate  2 > Candidate  6 > Candidate  5 > Candidate  9 * 1 | ||||
|                 Candidate  4 > Candidate  8 > Candidate  1 > Candidate  3 > Candidate  5 > Candidate  6 > Candidate 10 > Candidate  2 > Candidate  9 > Candidate  7 * 1 | ||||
|                 Candidate  4 > Candidate  9 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate  9 > Candidate  2 > Candidate  7 > Candidate  3 > Candidate  5 > Candidate 10 > Candidate  6 > Candidate  1 > Candidate  8 * 1 | ||||
|                 Candidate  4 > Candidate  9 > Candidate  7 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  4 > Candidate 10 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate  4 > Candidate 10 > Candidate  3 > Candidate  1 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate  4 > Candidate 10 > Candidate  5 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate  4 > Candidate 10 > Candidate  9 > Candidate  3 > Candidate  1 > Candidate  2 > Candidate  5 > Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate  5 > Candidate  8 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  5 > Candidate 10 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate  6 > Candidate  3 > Candidate  4 > Candidate  1 > Candidate  2 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  6 > Candidate  7 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  6 > Candidate  7 > Candidate  4 > Candidate 10 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  5 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate  6 > Candidate  8 > Candidate  9 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  6 > Candidate  9 > Candidate  2 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  6 > Candidate  9 > Candidate 10 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  7 * 1 | ||||
|                 Candidate  7 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate  1 > Candidate  8 > Candidate  9 > Candidate 10 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 * 1 | ||||
|                 Candidate  7 > Candidate  1 > Candidate  9 > Candidate  4 > Candidate  3 > Candidate  8 > Candidate 10 > Candidate  2 = Candidate  5 = Candidate  6 * 1 | ||||
|                 Candidate  7 > Candidate  2 > Candidate  6 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate  2 > Candidate  9 > Candidate  6 > Candidate  8 > Candidate  3 > Candidate 10 > Candidate  5 > Candidate  1 > Candidate  4 * 1 | ||||
|                 Candidate  7 > Candidate  2 > Candidate  9 > Candidate  8 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate  2 > Candidate  9 > Candidate  8 > Candidate  4 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate  3 > Candidate  4 > Candidate  1 = Candidate  2 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate  4 > Candidate  1 > Candidate  2 > Candidate  3 > Candidate  5 > Candidate  6 > Candidate  8 > Candidate  9 > Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate  4 > Candidate  2 > Candidate  9 > Candidate  8 > Candidate  1 > Candidate  3 > Candidate  6 > Candidate 10 > Candidate  5 * 1 | ||||
|                 Candidate  7 > Candidate  6 > Candidate  9 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate  8 > Candidate  3 > Candidate  1 = Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate  9 > Candidate  2 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate  9 > Candidate  6 > Candidate  8 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate 10 * 1 | ||||
|                 Candidate  7 > Candidate 10 > Candidate  1 > Candidate  4 > Candidate  3 > Candidate  8 > Candidate  9 > Candidate  2 = Candidate  5 = Candidate  6 * 1 | ||||
|                 Candidate  8 > Candidate  1 > Candidate  5 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  8 > Candidate  1 > Candidate  9 > Candidate  4 > Candidate  3 > Candidate  2 > Candidate 10 > Candidate  5 > Candidate  6 > Candidate  7 * 1 | ||||
|                 Candidate  8 > Candidate  2 > Candidate  7 > Candidate  3 > Candidate  1 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  8 > Candidate  4 > Candidate  2 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  8 > Candidate  7 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  8 > Candidate  7 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  9 = Candidate 10 * 1 | ||||
|                 Candidate  8 > Candidate  7 > Candidate  9 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  8 > Candidate  7 > Candidate  9 > Candidate 10 > Candidate  6 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 * 1 | ||||
|                 Candidate  8 > Candidate  9 > Candidate  1 > Candidate  2 > Candidate  7 > Candidate  4 > Candidate  3 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  8 > Candidate  9 > Candidate  2 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  8 > Candidate 10 > Candidate  3 > Candidate  1 > Candidate  2 > Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  9 * 1 | ||||
|                 Candidate  8 > Candidate 10 > Candidate  7 > Candidate  6 > Candidate  9 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 * 1 | ||||
|                 Candidate  8 > Candidate 10 > Candidate  9 > Candidate  7 > Candidate  6 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 * 1 | ||||
|                 Candidate  9 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  1 > Candidate  3 > Candidate  4 > Candidate  7 > Candidate 10 > Candidate  8 > Candidate  6 > Candidate  2 > Candidate  5 * 1 | ||||
|                 Candidate  9 > Candidate  1 > Candidate  4 > Candidate  3 > Candidate  7 > Candidate  8 > Candidate  2 > Candidate 10 > Candidate  6 > Candidate  5 * 1 | ||||
|                 Candidate  9 > Candidate  1 > Candidate  7 > Candidate  3 > Candidate  8 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  1 > Candidate  8 > Candidate  3 > Candidate  4 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  2 > Candidate  7 > Candidate  3 > Candidate  4 > Candidate  5 > Candidate  6 > Candidate  8 > Candidate  1 > Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  2 > Candidate  7 > Candidate  4 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  2 > Candidate  7 > Candidate  8 > Candidate  3 > Candidate  1 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  2 > Candidate  7 > Candidate  8 > Candidate  6 > Candidate 10 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 * 1 | ||||
|                 Candidate  9 > Candidate  2 > Candidate 10 > Candidate  6 > Candidate  5 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate  9 > Candidate  3 > Candidate  1 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  3 > Candidate  4 > Candidate  7 > Candidate  1 = Candidate  2 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  4 > Candidate  1 > Candidate  2 > Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  4 > Candidate  8 > Candidate  1 > Candidate  2 > Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  4 > Candidate  8 > Candidate  1 > Candidate  3 > Candidate  7 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  6 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  6 > Candidate  7 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  6 > Candidate  7 > Candidate  2 > Candidate  3 > Candidate  4 > Candidate  5 > Candidate  1 > Candidate  8 > Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  7 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  7 > Candidate  2 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  7 > Candidate  2 > Candidate  1 > Candidate  5 > Candidate  3 = Candidate  4 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  7 > Candidate  2 > Candidate  6 > Candidate  8 > Candidate  4 > Candidate  1 = Candidate  3 = Candidate  5 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  7 > Candidate  2 > Candidate 10 > Candidate  3 > Candidate  1 > Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 * 1 | ||||
|                 Candidate  9 > Candidate  7 > Candidate  3 > Candidate  1 > Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  7 > Candidate 10 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 * 1 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  3 > Candidate  7 > Candidate  1 = Candidate  2 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  4 > Candidate  1 > Candidate  3 > Candidate 10 > Candidate  2 = Candidate  5 = Candidate  6 = Candidate  7 * 1 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  6 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  7 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  6 > Candidate  3 > Candidate  2 > Candidate  7 > Candidate 10 > Candidate  1 > Candidate  4 > Candidate  5 * 1 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  7 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  7 > Candidate  2 > Candidate  1 > Candidate 10 > Candidate  3 > Candidate  4 > Candidate  6 > Candidate  5 * 1 | ||||
|                 Candidate  9 > Candidate  8 > Candidate  7 > Candidate  2 > Candidate  3 > Candidate  1 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate 10 * 1 | ||||
|                 Candidate  9 > Candidate 10 > Candidate  2 > Candidate  6 > Candidate  7 > Candidate  8 > Candidate  4 > Candidate  5 > Candidate  1 > Candidate  3 * 1 | ||||
|                 Candidate  9 > Candidate 10 > Candidate  7 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 * 1 | ||||
|                 Candidate  9 > Candidate 10 > Candidate  7 > Candidate  4 > Candidate  1 > Candidate  2 > Candidate  6 > Candidate  3 > Candidate  5 > Candidate  8 * 1 | ||||
|                 Candidate 10 > Candidate  1 > Candidate  3 > Candidate  4 > Candidate  2 > Candidate  5 > Candidate  6 > Candidate  9 > Candidate  8 > Candidate  7 * 1 | ||||
|                 Candidate 10 > Candidate  1 > Candidate  9 > Candidate  2 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate 10 > Candidate  3 > Candidate  5 > Candidate  1 = Candidate  2 = Candidate  4 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate 10 > Candidate  4 > Candidate  1 = Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate 10 > Candidate  4 > Candidate  1 > Candidate  2 = Candidate  3 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 = Candidate  9 * 1 | ||||
|                 Candidate 10 > Candidate  4 > Candidate  1 > Candidate  9 > Candidate  5 > Candidate  3 > Candidate  2 > Candidate  6 > Candidate  7 > Candidate  8 * 1 | ||||
|                 Candidate 10 > Candidate  4 > Candidate  9 > Candidate  1 > Candidate  3 > Candidate  2 > Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate 10 > Candidate  7 > Candidate  8 > Candidate  4 > Candidate  5 > Candidate  1 > Candidate  2 > Candidate  9 > Candidate  6 > Candidate  3 * 1 | ||||
|                 Candidate 10 > Candidate  9 > Candidate  2 > Candidate  1 = Candidate  3 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  7 = Candidate  8 * 1 | ||||
|                 Candidate 10 > Candidate  9 > Candidate  4 > Candidate  8 > Candidate  6 > Candidate  3 > Candidate  1 = Candidate  2 = Candidate  5 = Candidate  7 * 1 | ||||
|                 Candidate 10 > Candidate  9 > Candidate  7 > Candidate  3 > Candidate  2 > Candidate  1 = Candidate  4 = Candidate  5 = Candidate  6 = Candidate  8 * 1 | ||||
|                 EOD, | ||||
|             $election->getVotesListAsString() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame('Candidate  1 > Candidate  9 > Candidate  8', $election->getResult('STV')->getResultAsString()); | ||||
|     } | ||||
|  | ||||
|     public function testBugDavidHillRandomOrderAndStatsRound(): void | ||||
|     { | ||||
|         $hil = new DavidHillFormat(__DIR__.'/TidemanData/A60.HIL'); | ||||
|  | ||||
|         self::assertEquals([0=>'1', 1=>'2', 2=>'3', 3=>'4', 4=>'5', 5=>'6'], $hil->candidates); # Candidates are object, AssertEquals compare __toString | ||||
|  | ||||
|         $implicitElectionFromHill = $hil->setDataToAnElection(); | ||||
|  | ||||
|         // Without aggregate vote | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite(CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $implicitElectionFromHill, aggregateVotes: false)); | ||||
|         $implicitElectionFromCondorcetElection = (new CondorcetElectionFormat($file))->setDataToAnElection(); | ||||
|  | ||||
|         self::assertEquals($implicitElectionFromHill->getCandidatesListAsString(), $implicitElectionFromCondorcetElection->getCandidatesListAsString()); | ||||
|  | ||||
|         foreach (Condorcet::getAuthMethods() as $method) { | ||||
|             // Stats | ||||
|             self::assertSame($implicitElectionFromHill->getResult($method)->getStats(), $implicitElectionFromCondorcetElection->getResult($method)->getStats(), 'Method: '.$method); | ||||
|  | ||||
|             // Result | ||||
|             self::assertSame($implicitElectionFromHill->getResult($method)->getResultAsString(), $implicitElectionFromCondorcetElection->getResult($method)->getResultAsString(), 'Method: '.$method); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // With aggregate vote | ||||
|         $file = new \SplTempFileObject; | ||||
|         $file->fwrite(CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $implicitElectionFromHill, aggregateVotes: true)); | ||||
|         $implicitElectionFromCondorcetElection = (new CondorcetElectionFormat($file))->setDataToAnElection(); | ||||
|  | ||||
|         self::assertEquals($implicitElectionFromHill->getCandidatesListAsString(), $implicitElectionFromCondorcetElection->getCandidatesListAsString()); | ||||
|  | ||||
|         foreach (Condorcet::getAuthMethods() as $method) { | ||||
|             // Stats | ||||
|             self::assertEqualsWithDelta( | ||||
|                 $implicitElectionFromHill->getResult($method)->getStats(), | ||||
|                 $implicitElectionFromCondorcetElection->getResult($method)->getStats(), | ||||
|                 1 / (0.1 ** Condorcet::getMethodClass($method)::DECIMAL_PRECISION), | ||||
|                 'Method: '.$method | ||||
|             ); | ||||
|  | ||||
|             // Result | ||||
|             self::assertSame($implicitElectionFromHill->getResult($method)->getResultAsString(), $implicitElectionFromCondorcetElection->getResult($method)->getResultAsString(), 'Method: '.$method); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,445 @@ | ||||
|  | ||||
| 		       Tally Sheet for the votes cast.  | ||||
|   | ||||
|    The format is: | ||||
|        "V: vote 		MD5SUM" | ||||
|  You may locate the MD5SUM corresponding to a debian login ID, | ||||
|  given the associated secret token returned with the ack, | ||||
|  by simply running md5sum. Forexample, for login ID srivasta, and | ||||
|  the secret token 0123456789ABCDE, the following invocation works: | ||||
|    % echo "srivasta 0123456789ABCDE" | md5sum | ||||
|  The vote block represents the ranking given to each of the  | ||||
|  candidates by the voter.  | ||||
|  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | ||||
|  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | ||||
|  | ||||
|      Option 1--------->: Jeroen van Wolffelaar | ||||
|    /  Option 2-------->: Ari Pollak | ||||
|    |/  Option 3------->: Steve McIntyre | ||||
|    ||/  Option 4------>: Anthony Towns | ||||
|    |||/  Option 5----->: Andreas Schuldei | ||||
|    ||||/  Option 6---->: Jonathan aka Ted Walther | ||||
|    |||||/  Option 7--->: Bill Allombert | ||||
|    ||||||/  Option 8-->: None of the Above | ||||
|    |||||||/ | ||||
| V: 57213846 		1d7ba886e62a2cf64379a5c8f8032cc2 | ||||
| V: 3-311-56 		5983810b34693310479f3b26eda85177 | ||||
| V: 16313857 		1ccb15e79dc5734b217fb8e3fb296b9d | ||||
| V: 47125863 		52b444e0fb8778d31d52efb6b6e88401 | ||||
| V: 2-1----3 		b2c77c75a0747fc1b6019ca54d76aa42 | ||||
| V: -------1 		21acf319d1a9185b36fa065c649ca1da | ||||
| V: --21---- 		5778a0e6c4c15de35a92c85c66e1ecff | ||||
| V: 47321856 		9b76679e424a887ccf80f506ab8ba81c | ||||
| V: 26145837 		fdab256bb874774d3a67547f88f23ffc | ||||
| V: 1-423-5- 		b71d5f0a6e58b4e6ec2d81bce6160799 | ||||
| V: 43516728 		29a8468f09b317cea6b6c3defdccb513 | ||||
| V: 38214756 		b13ae01b3143d7287e099f3746e80211 | ||||
| V: 4-123-4- 		321ab097081bea5d4ee967f670fe9998 | ||||
| V: 18234756 		df985c5e22db36646fd9e6ceb594cefc | ||||
| V: --1----2 		0ffef79d2a2f58928e677160ffa6eaaf | ||||
| V: 31323735 		64fe866fc009aa01fddff32268b6d0fe | ||||
| V: 47134826 		205d5eea98ef53d8fdc77c04a5a55a08 | ||||
| V: 36124857 		cc9da13d222a4e987f3dcc1286e86827 | ||||
| V: 63472815 		0c7f3ca5b9d12d159428aaa3c28b319c | ||||
| V: 36172845 		38957e1d01d3bd1798f21d8ede7570f2 | ||||
| V: 56314827 		15251ab0ced1f920959a4fe6edcc4bc4 | ||||
| V: 47135826 		69b76a672ecc2038ff585f19129330dd | ||||
| V: --213--- 		618ae0bdab9d6c79a9fdb01636c579b2 | ||||
| V: 16432735 		be3d804ecf4b6d120b05acf9a51410b5 | ||||
| V: ----2--1 		7aa0f0fb4f7c64ae913ab0e56066d4cd | ||||
| V: 17244826 		25f689d866a88ee2a9759568fd3fe005 | ||||
| V: 36213837 		5cdaab4305428a8ca58a3cbfe59b95b3 | ||||
| V: 48213756 		06d4fb088c5f5ab2f64c18a34431b745 | ||||
| V: 25331746 		e365550e0d6b3b06e7ee847cadf96d76 | ||||
| V: 68456837 		def821da56979f9a5343e8794c7357ee | ||||
| V: --21---3 		d5ec4a853775b7fa030c6b75145d03b3 | ||||
| V: 45213867 		215d5062f2fcf07a9d59c35dcdda3a0d | ||||
| V: 3-12---4 		edb43166e6c262a6ac65d292187449d4 | ||||
| V: 27413856 		a0cb8268f78214afec42e6553ee02d2e | ||||
| V: 26314857 		4ce63b670a52419f8049c5e8f48f2374 | ||||
| V: 33313332 		6a8df1962f5c4e3366670b09cf584e49 | ||||
| V: 16341857 		652d3d17e3f31a54c6cac6bccc7bc88f | ||||
| V: 12437746 		0fdfbdf716e0ad87f34c8dc36f0dd787 | ||||
| V: ------1- 		add8d5d6a42dd64dabda529e5f1164a1 | ||||
| V: 35182764 		34364576fff96cc923019d044992dc4f | ||||
| V: 25143867 		572294825056c43dd29e7ceec5527807 | ||||
| V: 37142856 		a05bdb2c64d2cb6c815da770610dcf5a | ||||
| V: 26434415 		1df47bf79ec9072715c1f02becca192a | ||||
| V: 17172776 		6be1de6c656087ef4e3cddec0ca4f721 | ||||
| V: 22111423 		11c02dd825a181522f347b6f01f3d8cd | ||||
| V: 34313-45 		5cac77e9526f23ce4a1a16abb26db617 | ||||
| V: 17722877 		8cab4abee238fa92f915cad6c207d015 | ||||
| V: -1-2---3 		8e7d53af5d79a4f02e3b953ddf6a90a7 | ||||
| V: 2512143- 		9b8e13faa9eee964c4d036d6d2026cd8 | ||||
| V: 23222514 		4da78e380dd0348cc23b08d202b06b07 | ||||
| V: 35176824 		04941b0b82c4ad0ecaa0bd3fabb99747 | ||||
| V: 17245836 		40436efc84e638cb88ed622cf391ac95 | ||||
| V: 3-21465- 		c20db1801a0ce83d3a1708284fbf7d41 | ||||
| V: 26143857 		79da07c69774e5c02f4e965acfa12659 | ||||
| V: 56125847 		13466aa166e8bd33ecdd22225732b46f | ||||
| V: 37124856 		6ef35eb5863c7622f262b63762b06785 | ||||
| V: 35124876 		51eebd2937a95059c8f412ec1f65d37a | ||||
| V: 34217888 		64b30ef5c9727a7a5eb97188e853cce7 | ||||
| V: 36214857 		3486cec2aa47163e4c31cbfdd3cd8c18 | ||||
| V: 32143615 		4d559753e30e204760c1173e07bc12bc | ||||
| V: 47113865 		b72b0c7346dfb180fe295b9c983b8e06 | ||||
| V: 51426837 		d0bfe4b03c6987e34cab3b1f88d38d68 | ||||
| V: 35187624 		11747aa6d4fbdd0bc1c502abd5cc0420 | ||||
| V: 17365824 		39c0873fb3ea788c2e784b7a59de432e | ||||
| V: 27314756 		6c0e7bb7ed471b66889ae35209a0f65c | ||||
| V: -------1 		2fcb54e96e72615d584d25a764bb5bdf | ||||
| V: 57241836 		a59dd95cdf8f9320fb4316a0bd302b12 | ||||
| V: 16235874 		bcf0c7f540325d0565c55d2bc46e7d00 | ||||
| V: 57463841 		c7f910e0cdc01d325af5b4250154829a | ||||
| V: 33351432 		0c2c1c6a578b0d200e4043e51db02810 | ||||
| V: --21---- 		2e7d77538990c1e31d13b741940a4d02 | ||||
| V: 53412756 		943d20eb0ed12484d06d2d021b7d459f | ||||
| V: 84516732 		f658fcba86c86801db4ad3ea978b7761 | ||||
| V: 62417853 		d8ec1af3f585b8406f61540f0e66d04c | ||||
| V: 67854123 		f5e5faa5d9b53a8dab2adcdc0986e179 | ||||
| V: 13131312 		94d43e38f7c76a6d5524d247a2f45656 | ||||
| V: 52466813 		348339ee1016d2f9411784048cba11f9 | ||||
| V: 36214857 		9885e78df0246087d547089844cc9e8a | ||||
| V: 1--12--- 		dd6b581b2a8f63cdd34653d489356195 | ||||
| V: --213--- 		0ae4501dc5ce4048066354d5c3d4af7d | ||||
| V: 18321854 		69edd0c955e9bc8fa84ac20a77e20e20 | ||||
| V: -1-23-4- 		262face2bb4273375e9fd76c6a2509e5 | ||||
| V: 38214627 		1b3a3bdbd0a179980483e416e0795baa | ||||
| V: --2---13 		46bc6540158fc0daec9c70cf5bd82d84 | ||||
| V: 17263845 		f176ab1b4ae7b5e58dac00e245e03d48 | ||||
| V: 13212534 		bbe25dc3ce2685837c519f081ea64b2f | ||||
| V: 25413768 		c8a8f8868387fe1c329d3ae032db683f | ||||
| V: 16323837 		c1f1fe56c798749ee68b66cbefe622fe | ||||
| V: ------1- 		f243ecb479123642450e3dbda3206fbf | ||||
| V: 36217854 		fd6d9b3830630169e825007f13ebe8bf | ||||
| V: 1312243- 		d981046fdf12bc06d3323f4818b4ecf8 | ||||
| V: 56123748 		526319ee2e62ce669f3c423d19db5253 | ||||
| V: ----1--2 		b612cf7fa381d20e570a6ffe96c69c1f | ||||
| V: 36215847 		ce1ce9b1c098572142b1e14267f7119d | ||||
| V: 63571824 		2b78c2b02dd6ef88c3cbf08bf34b6ba9 | ||||
| V: --213--4 		2e6cf5d0919e44bab5da343986d4bdaa | ||||
| V: 47312658 		84ae6893f6d9ac51da736a8fd218d5ee | ||||
| V: 68324751 		89cdcd02e654e8a01c3bc22339b25c2a | ||||
| V: 18327564 		203b053779120f7f124741e55d4b6b29 | ||||
| V: 27145863 		9b780b86648711a1a6c88ed6062ebe58 | ||||
| V: 27222816 		2c8f95554a9870c50b5cf3efb76ce7d7 | ||||
| V: 35231654 		ffd9b278ed3fc7fa8c4035f36b37bb6a | ||||
| V: 53764812 		930472fe08a877860f3bd36f9732866d | ||||
| V: 76281543 		a47a0563c7c4bc859513d9ec77b0984c | ||||
| V: --31--24 		34d620519358d03577e5f4ecfa085193 | ||||
| V: 1--23--- 		89273c0a7ef50e10c75bac6225f3bca5 | ||||
| V: 36214758 		7d7a168f75df077736c4c8b5b16bbf8a | ||||
| V: ---1--23 		0566a2deea82f48a34909f42ae9c1d2f | ||||
| V: 37241856 		bd91b538b63952989e69a4e741d267f3 | ||||
| V: 37862415 		141aa11e71c9d7f13fd8581d6d121683 | ||||
| V: 35126847 		87cd81732ba07d494b1ad21c416e3c53 | ||||
| V: 66555847 		fed9917aad1b843920ab9e0fb6a76432 | ||||
| V: -------1 		a0d57e402df787ea900f5eeece60f3e8 | ||||
| V: 56283817 		699d933ac9270b5c0899393464e23445 | ||||
| V: ---21--3 		a92e12939d92e230cc4dcd7ac3b7500f | ||||
| V: ----1--2 		393df9cd1a4acd036136e1fc18a0c202 | ||||
| V: 47213856 		1b3e06fed30a0d10951c3bd6e23e684b | ||||
| V: 27314865 		075af50b08f18d3fe215f462a8c40186 | ||||
| V: 23212423 		443fd55c64badfef00ebfda88060560c | ||||
| V: 2-15---- 		bb4f3af2fab5aebe4040ca0b744d8bc6 | ||||
| V: --1---23 		93326e4606370bcce0447aa5ce18ba26 | ||||
| V: 14325867 		2e2226b7b3b0149cb44cc35b8197240c | ||||
| V: 48213756 		75859a51ce1826198d24cd3ff31e3405 | ||||
| V: 54137826 		7ebf1d0373d3e34891dfcc755b965b10 | ||||
| V: 57123846 		13bb5bb80516771d4ae60dd1a16124bb | ||||
| V: 3-2-4-15 		340999c0ec581eddc1d6acba03f969a4 | ||||
| V: 1-121-2- 		b6952712bcfc67e75d9c782d96843e07 | ||||
| V: 54312678 		58e6eeed6d3a00cb716cc87b7db3cbfb | ||||
| V: 56281743 		ba1699941ee575f6e854e62ec5007945 | ||||
| V: 3651246- 		290a2e2c02659c0ec9ab08a2d228bd50 | ||||
| V: ---1---2 		de1f95854235d0483efc9ba459e2d155 | ||||
| V: 47214836 		74898729967bb36fd06baa6c5dbbf901 | ||||
| V: 37214856 		c92e606be125ec4d1d5348223c021933 | ||||
| V: 35271846 		481a1776d8ec86ec99298e18464ae19d | ||||
| V: 47235816 		66956aa6b91cc91bbce99a525643a438 | ||||
| V: 26143857 		d4ca50a32d95adcbe73d15b846e5a63a | ||||
| V: 36512847 		774848bf48af2111487fb965a3be58d6 | ||||
| V: 12576834 		2551e91a8e7668cbb0f482ccc10f32ed | ||||
| V: 57214836 		a0f24ccf79099b4bac8832749743f2be | ||||
| V: 47123658 		4d0c9446881c4754e30bc6b3bb3fad0b | ||||
| V: 46132857 		d0d775364c3a7da51432c79ec8387bf1 | ||||
| V: 35512554 		b64c8d26c985b4703b63f55221828d2e | ||||
| V: 75261843 		3eaba62f35ffa84dfe02ed2e90370ba3 | ||||
| V: 4-123-56 		71255bd645f2f58654624ead16febd52 | ||||
| V: 3651364- 		eb77b4378a3ffb30e767d6e6f0b41509 | ||||
| V: 17235846 		04c2bd919aabbc157d74dda7e91b6bdd | ||||
| V: 37125846 		00711d1fbb56afd0595c77ff5dcfd988 | ||||
| V: 17245836 		4a12429299445d2bf6fd5436910f75f5 | ||||
| V: 34172856 		37af528b7e9a225e32477d730bdcfe59 | ||||
| V: 45212867 		587fa0665ae596a95d7ece13e2ce22cc | ||||
| V: 1--32--4 		3d51934b51002a07f91417cf34d2c554 | ||||
| V: 56314827 		0f761b0ce435d6ce62d64b3e22aba016 | ||||
| V: 1------2 		16c3c50d7d6b5328531543a784d54aae | ||||
| V: 37124856 		7cef1bfed93d560db0f7e586c036d63e | ||||
| V: 36214758 		9bc3d5b8ab15177cf1faacda4dd2f426 | ||||
| V: 47312865 		fed32a2c4312d80a1435f0c87413d924 | ||||
| V: 34213645 		f1475c21940d4e495f12a3627d81c640 | ||||
| V: --2-1--- 		8612593f5f075ad538bebb8165af866f | ||||
| V: 47123856 		0c3e2026ceeaf3bb82ecc8b83f1b5441 | ||||
| V: 36415872 		684beceb9005f15e19ed8c95a6af19f8 | ||||
| V: 27154836 		622a15b70c1d7ea1e7025072a2a7e2cd | ||||
| V: 14121113 		561b5f127729b6e0e562218fbb540e37 | ||||
| V: 2---1--- 		354690c06e2ee7233cdcd8cab7dbcef0 | ||||
| V: 4351267- 		0efe4848150319dfe4b4524d9f35dd74 | ||||
| V: 3----2-- 		aab4b685acc5e43beb6f8d98169a10f2 | ||||
| V: 65173842 		1ae6b329601fbff1b14a8b51f0729238 | ||||
| V: 25361-74 		722e6a02a32db6af4d96f2d5d78b5c10 | ||||
| V: 36174258 		9572bca2548943135baf0cebea660970 | ||||
| V: 21222827 		d4d735f042dec1bd930e08049675cba6 | ||||
| V: 18234756 		d993813864820064f475cde6cff78bb0 | ||||
| V: 1--32--4 		4dfad67ff5f70d006506079f9cda1498 | ||||
| V: 24121513 		b2a922593853e7dde04ec157b5eb2a22 | ||||
| V: 3714265- 		505ad37f22f3aeacb75e1c80ad4e98a2 | ||||
| V: 18311876 		3f34c5e7b1f8aea7c21ff1d9791dea70 | ||||
| V: 58261734 		9eafa3d8c4420ad1128cb4f092e85d0d | ||||
| V: -------1 		5fd942211bf7a1bc00e25baedd68337d | ||||
| V: 2-1-3--4 		1d970b99347c3280a291c795349be841 | ||||
| V: 32313534 		551853e032d3af216766cf5ce628b048 | ||||
| V: 33313332 		af6f53e4eecb7025fc1a2aaf56bbc8ea | ||||
| V: 47213856 		6b5b8478d87ea4cccb5d8bb47598c7fc | ||||
| V: 76372851 		e9c7cbd222a7208679e79883f086c980 | ||||
| V: 67435812 		77f7d77ae0c49c073bd10ca29972341f | ||||
| V: 2634175- 		2dde0c5c869860ab65fbf9a1989a4fae | ||||
| V: 15342746 		04d00c5ac8f72303e41ce4aed80031de | ||||
| V: 77182883 		e51f673ed77a5eeb528ca6fcf9d8ccf6 | ||||
| V: ---12-3- 		3e634a2cd14108e31728ec7297633408 | ||||
| V: 1111111- 		a2ddcdf092cf246bb6eebb19d49b735d | ||||
| V: 28211756 		7fcb58961f970342e55ada51e4ecdf3f | ||||
| V: 26315748 		5e3ec904295fbd5630c752f0de8cd691 | ||||
| V: 22222321 		da73a4f7b6d4a13ce0c3d05588bb236a | ||||
| V: 3--21--- 		606507e3764d93396765c376d9da1742 | ||||
| V: 45263718 		383bf17a69d06d7a5a21f54aac301802 | ||||
| V: 46132857 		e23ca002e58c630692b28dffa1ca297a | ||||
| V: 56214837 		20069e176e4483f8da1e51a0b6123867 | ||||
| V: 21233837 		e414567128a8f008ab77e198e1cc3f29 | ||||
| V: 3--1-2-- 		02330a44c97af838a81d6292573b4eda | ||||
| V: 47312756 		c8c545e83d619d2114c0fbccd7b2958d | ||||
| V: 16243857 		81a34863eddb1ace447f03e0ee8c5ed5 | ||||
| V: 3321142- 		471075a14f0204d43966bc7ed1836380 | ||||
| V: 27124856 		1f090b828641f58f23077ed9e63dfb9a | ||||
| V: 15252436 		cdcce2c16bb4f2c65e021adf8189ca54 | ||||
| V: 13542756 		c282080f91976a8fe8705e95304dbaae | ||||
| V: 24316857 		6f3d994c2b76ad7a3394bc780d7773f9 | ||||
| V: 46315827 		1a6ce57f379a856ae765619df2d11c9e | ||||
| V: 16213754 		bb36796a34d9d3018d43c00f5521877b | ||||
| V: 43412546 		8710ae17f79d0f7ca6f5867d6aa5cf1b | ||||
| V: 14121413 		76f747f526e28529b540f767b3d7ae86 | ||||
| V: 37122856 		181472faa895b41286461d7547d71b66 | ||||
| V: 3---1-2- 		a8d023d434d82734f3a5a80d04a1940c | ||||
| V: 46132785 		36b8cae2b98fc3db9eaf959f44311cad | ||||
| V: 45123716 		5f9a087e69c3bc94d0aaa7346dfa4785 | ||||
| V: 57218634 		1a1a17ae3c0c5e477cc7bb9a49460610 | ||||
| V: 2-134--- 		a43c376a51775634062ca0d3e6fb94ea | ||||
| V: 25553514 		260e30bedea0c52b1bbb0a2815e6816d | ||||
| V: 32313534 		b91f1cd1a266f0b83a19773de41a57b2 | ||||
| V: --34321- 		ba73f9b8cd0b382e601b0af0628ef9c7 | ||||
| V: 45132768 		a533eb573a0c666ebeaa75520fd8a0ad | ||||
| V: 13211423 		85447b4a780d6cb76155430f94ba2fc3 | ||||
| V: 12232827 		cea1a6ed78c00e4f6766f26d5680bc0b | ||||
| V: 37114865 		521f0599b9293761a8c6d5c4c9331b27 | ||||
| V: 17243856 		dc2f98656a8658910368c59f0dd29d64 | ||||
| V: 2-314--- 		73f124f4951b7fa4be929348f4f69893 | ||||
| V: 2---1-3- 		a4f78f197aebe3725b28ad6cd81ee791 | ||||
| V: 27354861 		833ec1a121582f4862b3918f3c217648 | ||||
| V: 3542155- 		28ca97fcaa707b3da1fdbc9ff16e9dd9 | ||||
| V: 27314856 		551578fff90d4efa1a448ae18662b966 | ||||
| V: 58213647 		ee72bed75ddee0ab99928a1ec84be049 | ||||
| V: 37213845 		7ecd14e14664e97b71ed6fd6ae9e3289 | ||||
| V: --13---- 		91fdfe530a9b91bb007556beb10bff1f | ||||
| V: 3-213--- 		68565639059e4fc6b990d804f9233fa5 | ||||
| V: -412--35 		b97c6eede494b947bb16ccb17ceeb69b | ||||
| V: 16342857 		06d98fd115fe376920e2d2ac512aa2ca | ||||
| V: --111--- 		8a82b70816aaad1d467758c5812d58ef | ||||
| V: 15132645 		66848b8170c2db2f2abe6b25d1e5c9c0 | ||||
| V: --12---- 		3f065a7957d7f390a3e061b7900c6f96 | ||||
| V: 25783812 		29cfdb2a407276bfbe3b15b376c3011e | ||||
| V: 4-352-16 		d2660f925cdf7652cd288a335c36c087 | ||||
| V: 46135827 		f682962b778365ed60bb1c5689922ddc | ||||
| V: 18243657 		baaa4948103cdcfd22a35bab497498c8 | ||||
| V: 35426817 		ab8689d544892cd98a8934d8177071e0 | ||||
| V: 37213546 		1efbf206b794d5fb465fb09e03963969 | ||||
| V: 48617352 		3aaeacf14b78212fc8fa472c8f4cf291 | ||||
| V: 46125837 		df78f6e49831939364f72a3b240b9445 | ||||
| V: 27163845 		0b0b628b347f4e8b1eacb899b3af83c8 | ||||
| V: 46123857 		b4b1a3fbd7496fec1aec1b05d975ce4e | ||||
| V: 16432857 		267a89c4fb685ae9469a29d520501c9e | ||||
| V: 2-321--- 		de972272af8ce405a6d182a9fd720bed | ||||
| V: 36412-57 		38bc1b80864acc40b59a5f3a87693a82 | ||||
| V: 67241538 		8509328895c9bcbcb30aaf4d77caf046 | ||||
| V: 26241-35 		f2a9a4977ed875007ebd63fdbbdf9c16 | ||||
| V: 1642735- 		2bdd09c10af28c9a7db48531e5055746 | ||||
| V: 2---1-3- 		bc60c46c4d479dd14d2cc82aeb76ae47 | ||||
| V: 58162347 		f5751d54419ec48b51a3aa539c2a06f2 | ||||
| V: 26431758 		48e4faceb12114a34875fb10c515c822 | ||||
| V: 44321325 		b5e89830a69176124166c80f0b73d943 | ||||
| V: 46531827 		2fa36fb883943603b77ef6bf7134650a | ||||
| V: 2-31---- 		edbce0a99d07f4a9fb0cc89c5a721adf | ||||
| V: 46413827 		e62a26583df4a6e9cf8327504ee1c440 | ||||
| V: 64312758 		0af4d0d55a6e24603b8cc2c6d171e3d8 | ||||
| V: 46321857 		beb78a9a14abd6efcf872f3d3eb2595a | ||||
| V: 25333614 		e40a9c140998105c697a3ffc81b2b18e | ||||
| V: 47213856 		47a15efec113b8dbcd8ea622e8ec3aa7 | ||||
| V: 2222122- 		b01b57e2760c298b03c10098b8667bf7 | ||||
| V: 34412645 		c0214391f78a07db85d9a510b3088a12 | ||||
| V: ---1---2 		b62553d8cda24607d9dbe18d2e796636 | ||||
| V: 37251846 		f3ff3af00d333aa9fd7984a25ea0ea4a | ||||
| V: 37241856 		ef27166a1513ed44afa951b0d6aad90b | ||||
| V: 4731286- 		c86ee3b3bf804b13420b7114d04ffd83 | ||||
| V: ---1---- 		d89199800cbeefe076ea5e42d0d6338e | ||||
| V: --32--1- 		a5689afcb25e65fe5558cb0f5f22699d | ||||
| V: 54612738 		b63567bab3d8fd1d73f39085391ac1c7 | ||||
| V: 15221634 		7f27ce42b144eae453bb61a6679623b1 | ||||
| V: 4-124-35 		9046bb5a0c4fed095c518dd8ea647cea | ||||
| V: ---12435 		7cb8db4791e5c0b553d0da00e8184616 | ||||
| V: 36245817 		89480e5e36bc818d11ad4da244c3c805 | ||||
| V: 57143628 		3081e523fa72f996c616dd249737d60d | ||||
| V: 37241756 		800a4c20e9e64739289bf4e806378988 | ||||
| V: 11111817 		039cfc7b5204fa3bbc8ed947957b277d | ||||
| V: 2713477- 		d951b48292a445b1e19c1c3d167b7c78 | ||||
| V: 7718287- 		6444ff6d1f40b53c4cae53de0439bb92 | ||||
| V: ---1---- 		a367913ddf67262c843a076f1ec663a8 | ||||
| V: 34313334 		08a9efef2a901bfa6d1cf0ece23943fb | ||||
| V: ---1---- 		18c3d5e7bce1aa7fd7e660db6e252bcd | ||||
| V: 1------- 		1b1aa1f2cc3e5f80232808ec00291fc7 | ||||
| V: 31264876 		555b6a094b7aa7955193ed50b8871719 | ||||
| V: 26621668 		114f8d914a5df96a3ae6689301f6af20 | ||||
| V: 4-3125-6 		3e022fc2787ac2bb6d663bc94c27cf0e | ||||
| V: 57216843 		0cfe14ad878eff2ab4614834bb9fd5c7 | ||||
| V: 85237416 		9828017e89e92b41ee082bb845663625 | ||||
| V: 12111413 		7b431d7d3418db13aa16e20da8a417fe | ||||
| V: 45683712 		ab0ecb700d02967f06c555fe36ecd42c | ||||
| V: 56217834 		cd4deb457556d9f90f4c8d19ab91f880 | ||||
| V: --213--4 		9e8ccce13ca29b67e0e2f7d4a5313444 | ||||
| V: --13---4 		b20a8c621150cb660bf724d9e655aaff | ||||
| V: 15324-55 		9e9268214073491f5471592b8d289cf2 | ||||
| V: 37141856 		2a0ca8001e353f88c40571d996f32bb3 | ||||
| V: 53214867 		c830ac007ae86215f5e3e421f8b05c8c | ||||
| V: 25313534 		9e0616daa64466947d9a786dc367e397 | ||||
| V: 22221223 		15ac14e7afb0425ca30b3594ec2e6d81 | ||||
| V: 15312-48 		9f7562106c9e7aca42589dceff287c0c | ||||
| V: 14212845 		880fcfe863faa4ce8dd22bb97387351f | ||||
| V: 143-2--5 		d8074543780301c50b56615e7007e6c9 | ||||
| V: 26456731 		bc5d66627de5e5fa2c3f22483fbd9ae0 | ||||
| V: 26145837 		9b4d5196c5861f43f84eb5a90d386732 | ||||
| V: 3-21---- 		6392474f4a775a992cb2506ed288ab40 | ||||
| V: --12--3- 		5cdf7d656bca6562e10328de20f0ed69 | ||||
| V: 3-123--- 		b27d3f0640a314d33bc6ad346b98b0e4 | ||||
| V: 1-3-2--4 		411f9f37f6f24b3c7b673f3648aeaacf | ||||
| V: 45673812 		32a2ff36efed47b5f45498c8acbb21d2 | ||||
| V: 24134815 		df5cc376c3ecba2825adc9947f20757a | ||||
| V: 14231-35 		80209162b8c7b1ba2ad71877e8d51153 | ||||
| V: 14122434 		9ffe0ca817cb02a9eaabebe53e824b4f | ||||
| V: 45431642 		56bce963ba4cd5f28860358950998828 | ||||
| V: 25153554 		cc3185d62934d4bd50e6edd9754b37c3 | ||||
| V: 23845716 		fa1b8ba74186465d36c7e7434c5784f5 | ||||
| V: 37261854 		e2326f5bd39399b9d28eb7be7d1823a7 | ||||
| V: ---12--- 		2121b9021124043d7349c32e2f858d22 | ||||
| V: 68215734 		fa16b6b9af98e29380a86a6fedcf0f00 | ||||
| V: 15221634 		33abe04e778991d8de766861d39324d5 | ||||
| V: 84158263 		c71db3061b853baeb3f9b6ad5e4fb4d3 | ||||
| V: 37215864 		240a1d5f7fc9227051d43d7f642bfc32 | ||||
| V: 23865714 		cdde362f02085d20f8912b6dec5470ed | ||||
| V: 25112-34 		b49af1186dbb0b248b8212e54b570f89 | ||||
| V: 47412857 		aa1333bb4777ab1561aa462433af27a1 | ||||
| V: 48344836 		aaa073ff487e6fa9f2cde048a420d1ed | ||||
| V: 33213534 		080b246a2cb565fe85393ddf1830ad79 | ||||
| V: 28612617 		1a307458ea414869b8e6d2a056f7819b | ||||
| V: 57314682 		fcef91d6f8104aba23e9033fa5a2e7a9 | ||||
| V: 24146827 		14be0f2cf36f0f77f921234203fe51d9 | ||||
| V: 35164827 		85c37d57cee1b7f9e31fdc1e34024b99 | ||||
| V: 2--1---- 		59d43bd00cc40321e33f961d34403d73 | ||||
| V: 26312745 		ea5c635a481beb24051676e2d426935d | ||||
| V: 36124758 		09d4bb9d0ace04fd0a9e67dbdc980a81 | ||||
| V: --12---- 		555df6c79e30b20a85b258744015a460 | ||||
| V: --1----- 		664aaae907754c218eac78449302f107 | ||||
| V: 25241857 		d789918d625bdc8ae57a87f0705620ed | ||||
| V: 23312867 		d7d14ed77eabdba10e897fbaf2508e70 | ||||
| V: 48125763 		d6e4dcdfcfd41c24c1de5b894c9702d7 | ||||
| V: 64125837 		1abd54f13f8097d2a939e213e859600c | ||||
| V: 27341856 		dd25c2f04bd766b0427e3de9ba9a6e95 | ||||
| V: 48213657 		28de4db63bb2206140a978135ffe3d61 | ||||
| V: 33232534 		20487b8523ed033494835b1c09ebf88c | ||||
| V: 1-234-56 		f7c3b8576bc0f154a8f22b148bee01e6 | ||||
| V: 46325871 		893ec7e08d3f0a4b55b9e5af1c018571 | ||||
| V: 88224416 		142ef6f2613c2c7a4c9f65f51f274f22 | ||||
| V: 76354812 		e992b4e6302811b0dacc8289bb937da8 | ||||
| V: 15126734 		1af31da3d1e8c52683b8bbb1454d0602 | ||||
| V: 77521876 		61a4382c2834aeb1b8de6db72539a121 | ||||
| V: 13122534 		c337202ec1ddd673ad1d71a41fffe614 | ||||
| V: --12---3 		acf1a7f99572aeca10950b8f4ee88bc2 | ||||
| V: 35213524 		88e1f5b9709fa6f5c2080ad5da7f87df | ||||
| V: 37124658 		0172f801214d54119c7320e04cb59ad7 | ||||
| V: -------1 		b2d836dc52f53c12697de54978316b00 | ||||
| V: 35212634 		5fef8b71aa06f647c9a52a7ead8bbfce | ||||
| V: ---1---- 		319f2f745f8282276b6a2af328eeffda | ||||
| V: 37421856 		f9579c15d964b1a777aed3162173866f | ||||
| V: -------1 		c23ba38e315a59cfd72d02a8276989de | ||||
| V: 28341756 		039d5c78c555881a2db18baad15f6086 | ||||
| V: --4213-- 		74c86a247f9a38a56fb06204b4da00b1 | ||||
| V: 35214867 		a661b740a120439c99dab3592bd05dcc | ||||
| V: 34312333 		ef39e11380318138a04cde2f0b0577b8 | ||||
| V: --123--- 		13408a29aa3f41d950ced5fa5e89a307 | ||||
| V: 15238478 		7eb1177e865f30c3f9a72a4a9f722d03 | ||||
| V: 47123658 		7dd94fc57a410a4d1c874cbb185f4b4f | ||||
| V: 48321675 		0b7ba5275881a9304c9616f9d7174095 | ||||
| V: 46382715 		8ae29c87282779a1ff916ef08cfd85bd | ||||
| V: 37214-56 		dcfbc0e94ee6f3e1a32ede7176501b64 | ||||
| V: 27135846 		99c1e35449f51f788bfbf950d5261ceb | ||||
| V: 24113635 		a8ae3bffdd433278f62ac6ad06daa43f | ||||
| V: 15223534 		9a3e83ef7a2582aa568c341e68823554 | ||||
| V: 55334518 		e0b11ad010d1d899e1f4a2c0b78cb5a1 | ||||
| V: 28415736 		aab8a4cf861fd4849b53eb06e100b165 | ||||
| V: ---1-3-2 		23ce463064353d726caea161a0abc3e4 | ||||
| V: 2-431--- 		04acdb573b3b20aa0746a6b0b499bd89 | ||||
| V: 67152834 		45757642be8c7fe6b2c99dc3433af51c | ||||
| V: 57214736 		dd71fe62536a3125f7e1a8c33fedcd81 | ||||
| V: 26315-47 		fc636c8e3c7845d365814f3b69ea2fb1 | ||||
| V: 45126837 		bb79a341d25da6da950b6a7da21ee856 | ||||
| V: 3-123-45 		8e8b4a402102c9ce44033364e4b33322 | ||||
| V: 36213745 		9df7140f924e1aba6d7022604a60db5c | ||||
| V: 2513474- 		e79947e2ef56bbc2e402589447c016f2 | ||||
| V: 76275854 		9bfc20fbf724f484d785f413124458d7 | ||||
| V: 83712645 		a7137d5e63595dc3abd876e227bd3d74 | ||||
| V: 56124837 		ee28b414256ed28d559c699519d0bdab | ||||
| V: 16234758 		dbc3b2ae38b6586b001983934b6a1dc8 | ||||
| V: 2311232- 		0b47129f951218caf8b1bf35f1fd0cc9 | ||||
| V: 46123857 		57d7997f3990f03a8d15d5192ca81830 | ||||
| V: 35134726 		d549175168c301e2c826ac5c00b45c3e | ||||
| V: 57216834 		9b8898579f751d6abfbc9b4b591b914f | ||||
| V: 16324857 		01299188960b4b3239a0cad2d4c42c5e | ||||
| V: 25134867 		174c2e9763e3269e5f62119e56671c30 | ||||
| V: 17252846 		286076c0ce373a2037e3d4fb50f3f2da | ||||
| V: 57324816 		e28c6ab23da73f62876fad302d2f5950 | ||||
| V: 54312876 		0bb9a47e90f076aa0242d26857cf733c | ||||
| V: 54125736 		a77e6c989eb7a4c4d5b19fedcef8705e | ||||
| V: 22123856 		8e7dd62b11322a6e742b2bbe8f2ef16d | ||||
| V: 35213634 		95e58cd7c48aea7e1a0cbab5c9ca608b | ||||
| V: 36123745 		d292ee2b1542abec738eaea6a7c35f51 | ||||
| V: 2-21--43 		6bc4bed870490203fe2def3874706a8c | ||||
| V: 76312845 		84c82862270ca5c33895c5a7edd0fba1 | ||||
| V: 38158824 		f9ded889c2208134f80b6f3ad150738f | ||||
| V: --123--4 		2fa85770cdc7a7e320f2fdc20b93e5b9 | ||||
| V: 42444413 		e3adcc29425fcbc749d7731ac264d691 | ||||
| V: 2-31--45 		fcdf69793004c8b7a09b6f0be12d53c4 | ||||
| V: 14231634 		30863ace9b616d8a4a697e9a029df9a4 | ||||
| V: 57314826 		8dd85a4c80afca93265d58cf1b37758d | ||||
| V: 35176824 		3dafd70470d24f4249c7da00b6301317 | ||||
| V: 2222122- 		62d875af374ae6b616b439767b517f79 | ||||
| V: 3-22132- 		931d0912646dae05376667f24d2d5b10 | ||||
| V: 8812288- 		1641b004e994796ee4667fa8b8b2c4a1 | ||||
| V: ---1-32- 		e3e90e7d278a0399b0c2668dae4abeb3 | ||||
| V: 14523--6 		c613606c0e07b2900ad7d0532228e1a7 | ||||
| V: 67341852 		2e46d7dbdd4a0e31fecfd9d26d97a836 | ||||
| V: 23211423 		d5ea3a2298070dd799f20a00ce8febed | ||||
| V: 23417865 		d2d1a54dba89b27b56559a2ab3e2ba80 | ||||
| V: 28314675 		d13925332cc5d4ada191a5df1ea404db | ||||
| V: 17243658 		191ed8bf19c5cb6d6871d7782eced295 | ||||
| V: 2-31---- 		f504e2dd66052cd618b2619f21afd3f6 | ||||
| V: 26114735 		aead9fe58962a052073c3eba978ce3e0 | ||||
| V: 27134865 		d4a556f9a1d5712704424e39c69bd139 | ||||
| V: 3--1-2-- 		47c90820c612b4b9288405afa3ec8dfa | ||||
| @@ -0,0 +1,507 @@ | ||||
|  | ||||
| 		       Tally Sheet for the votes cast.  | ||||
|   | ||||
|    The format is: | ||||
|        "V: vote 		MD5SUM" | ||||
|  You may locate the MD5SUM corresponding to a debian login ID, | ||||
|  given the associated secret token returned with the ack, | ||||
|  by simply running md5sum. Forexample, for login ID srivasta, and | ||||
|  the secret token 0123456789ABCDE, the following invocation works: | ||||
|    % echo "srivasta 0123456789ABCDE" | md5sum | ||||
|  The vote block represents the ranking given to each of the  | ||||
|  candidates by the voter.  | ||||
|  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | ||||
|  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | ||||
|  | ||||
|      Option 1---------->: Wouter Verhelst | ||||
|    /  Option 2--------->: Aigars Mahinovs | ||||
|    |/  Option 3-------->: Gustavo Franco | ||||
|    ||/  Option 4------->: Sam Hocevar | ||||
|    |||/  Option 5------>: Steve McIntyre | ||||
|    ||||/  Option 6----->: Rapha<68>l Hertzog | ||||
|    |||||/  Option 7---->: Anthony Towns | ||||
|    ||||||/  Option 8--->: Simon Richter | ||||
|    |||||||/  Option 9-->: None Of The Above | ||||
|    ||||||||/ | ||||
| V: 111111312 		5de2d9bdec162e7ad7d475f7a3a912e0 | ||||
| V: 296153487 		82779753e0665892a0168183b4d03db6 | ||||
| V: 222142443 		8fc3f20b27697bbc84c5d14782ce29cb | ||||
| V: ---321--- 		85a74c0916cb5df9b7c33fa49230ce73 | ||||
| V: ----21--- 		ec0aeebf04b664751df5a3113cdba64b | ||||
| V: ---31-245 		3bc131481a5f0ac8d07748b93e275f0c | ||||
| V: 344412645 		4e58d78db380234727c3170416c9720d | ||||
| V: 392156478 		737552a1b770c84bf44b12990d0344fc | ||||
| V: 72135648- 		5b6ecb7d1eff7fd31097659d49f7f9e7 | ||||
| V: 263754189 		0b946ed4c6fb3f3751e0886822eb7871 | ||||
| V: 16421553- 		c6fa84467050016ffaf55542b74fbb8b | ||||
| V: -----21-3 		c93cc619a255967030ffab7df2ef66d3 | ||||
| V: 483125576 		9897dfb2d83c8aa480ece204e81e2254 | ||||
| V: 355196924 		00115bf3d3b02e3449305ebb7dfe375c | ||||
| V: 885214893 		5cca377bbf7b08de0245764a31300820 | ||||
| V: --------1 		f71e69ff5942550e11505ef6896a0612 | ||||
| V: 163724865 		5e3bd536aaec6eb14614b2a33d55b855 | ||||
| V: 372451689 		a995aeb8bcb4b412ec7e795dfad9164b | ||||
| V: 5--2413-6 		2931ffdcf2d0e5e76131053141afbb28 | ||||
| V: ---1--2-- 		16fb70036d9ef9054ee5e18490e65ffa | ||||
| V: 34641425- 		fcd1442e37dcd5b93e80630452beba06 | ||||
| V: 183624579 		570a5ee57b184758b7101bef1376512c | ||||
| V: ---1----- 		58a680be2eba20a5c56e0136c755e46c | ||||
| V: 185324769 		8b4c2ac7df2dc0520d2dfc324f0938f4 | ||||
| V: 566213254 		9faa59f85f4ef321bd3b7b1eb95a962c | ||||
| V: ----231-- 		6fd0574788f5e39d2f3ef5f419ff45d7 | ||||
| V: 287134695 		3e4019943e1c2a06086b8fa9b7f26b13 | ||||
| V: 165324978 		7f12cd29047b33dbbd39f1baf299b2d6 | ||||
| V: 714365289 		b5164cdf1389b949b2e9c9bcef97bfe0 | ||||
| V: 1---2-3-- 		883b71a74f1dafdbabe41b7e9bd282dc | ||||
| V: 562155643 		6cede7d373127b3f776572492f2013df | ||||
| V: 492163785 		c57f0d386ac109a968890e96494c7d19 | ||||
| V: 581342766 		e2f6b90c93ada18c05c4577f432b8249 | ||||
| V: 991999992 		c8e99a4c0bce7b29dd319d798d917808 | ||||
| V: 2---5413- 		9f8220bf2318e743aedba861ea728594 | ||||
| V: 296331478 		b75d57ab25da0dd20ba763e25a1e2f8e | ||||
| V: 471325376 		38dd111235919341e256ca0beae75233 | ||||
| V: 352188839 		2e12a634b4335369041e17192818ea4f | ||||
| V: 6241-5-37 		3951ed0cce057646f9cd78208b87c5f4 | ||||
| V: 543815297 		c8875690b94b5698d52a48e068928d98 | ||||
| V: 492361587 		e205db0ec3a7fc454c6f4050e47aa992 | ||||
| V: --21----- 		6dbff1a985e92da42f3fcec7a19307a0 | ||||
| V: 199299993 		23101224c5d0d377cef8f5e4c2e45045 | ||||
| V: 187324695 		1eaaa132079955ea5589485e843646eb | ||||
| V: 583171964 		a2a6d73a291f23663332156edc849cf5 | ||||
| V: 491365782 		88ea4569c43c1736b4c505603916a859 | ||||
| V: 144332543 		90d3b334e399882da35d69bc72726027 | ||||
| V: --------1 		cf3b91dea6391a8a3331d13eddd10b59 | ||||
| V: 465155623 		e0dd82a30b72c1fa483215469f1b6972 | ||||
| V: 274138956 		d7dab11af00b8028015098df5a1d3cb6 | ||||
| V: ------1-- 		43b414518d6603d5345ec4bfd08c7a53 | ||||
| V: 48561327- 		b4b6417a49c0b1f38aa73adf0fde98fb | ||||
| V: ----2-1-- 		16dafaa67afe1f28ad17f007e1eafb85 | ||||
| V: ----1-2-3 		864d806937c7b6ee21e381f6e8b6d699 | ||||
| V: 25341355- 		abab795f67d507315e39b28884ee1247 | ||||
| V: ------1-2 		1435df948819103e326dd285a870d7be | ||||
| V: 4-212-4-5 		9a71f50326a4cc423e78b705efba39bc | ||||
| V: 195264873 		3d0d30383706da28452caee427ad520f | ||||
| V: 377815264 		6595f5ab403c46ae3e5ebee012b5b439 | ||||
| V: ---1-1--2 		3d0e67d584384018f0dd6960a604665e | ||||
| V: 537412176 		45f67677e48ce5733169537737c8e461 | ||||
| V: ----321-4 		62866e67f4dce3a01f3d970befc95175 | ||||
| V: 45551325- 		b73343c1560d714e065e72a8565036f3 | ||||
| V: 278514396 		97a23e6b79f7ee634d2521fbb1808ace | ||||
| V: 78261524- 		402b4bd1a358667dd1e695f510948f0a | ||||
| V: 476123589 		9008efe86a4ccaf437bf00c126f2a81c | ||||
| V: 5-61423-7 		072cc65cd6b7e037301594be54a56afa | ||||
| V: 443122635 		f8c7983399cfc9acb1e38054d970f9ed | ||||
| V: 372144576 		3458172b1523f8ea96785bc156581a31 | ||||
| V: 191122938 		2e78f4e0c2f641934c56fc7f8910b066 | ||||
| V: 2----13-3 		7c747a75c8a8afe77d20f0949b3aaa83 | ||||
| V: ---1----2 		f5fcaf54dce30ace7adc8688d2ba8aab | ||||
| V: 34441214- 		8b433ccb9ae7e3b131f703d1a271edeb | ||||
| V: 56412378- 		875dbffdbe89c5dbd8e832e6b892712f | ||||
| V: 4-31-2--- 		1def3be27460b7c59799672e535beef5 | ||||
| V: 372165498 		59b6c624b34091e14cc3ca2c490b3719 | ||||
| V: 582168997 		fb92a33b945d36f3f8051c0049a96646 | ||||
| V: 377914265 		3a0053ac583871542503eb0e8dba7fee | ||||
| V: 387415296 		0bbf5448890cfcf57a0fed19efa324cd | ||||
| V: 111111111 		e7db684ce9919802b57a0a3b55468098 | ||||
| V: 586432197 		f39d939d8095f8e539388aa988c0713c | ||||
| V: 571242776 		44d42fde51290d065e8efeb787eeacc7 | ||||
| V: 233331134 		a42d1851a9bbd882d354c2caabef93df | ||||
| V: 496512378 		035fd8c2be571cf93a1632554513f8cc | ||||
| V: 895132476 		030e90534cb75b16ea78229227fb81c8 | ||||
| V: 487921365 		b2bbee806824f770133b7aa3af04bf27 | ||||
| V: ----2-1-- 		8d2aaa61f2b975ca1de55236c2c9a36c | ||||
| V: 862183968 		c8ff3b650fcd6dc86d5337961f30d55c | ||||
| V: 456728931 		e99a33058dcf1248134f26daf9d9167c | ||||
| V: 642187935 		6feefc11d1ba5bbd386a5cbe2cc54c7e | ||||
| V: 26713548- 		4c8e050da3f0d40830aa649c7e7eef41 | ||||
| V: 6-51432-7 		f45968e0d9b48d70e9f19016d2e6a3bd | ||||
| V: 465932178 		94d47901ffa8a4cbc53abdbb7440bf77 | ||||
| V: 487532169 		a143468aa15a1fac903dad4bc7af7356 | ||||
| V: 256143978 		8388d60c8a8500ae717f3d47417a985e | ||||
| V: 197236854 		50bd4fd15d40600ffe7dd0812ec7d487 | ||||
| V: 295613487 		d60ebd8b7797248e8004fee92e1dc707 | ||||
| V: ------1-2 		f31d5f580cfa6b93b359d4250dfcccbb | ||||
| V: ----1-2-- 		9e56fde5c2185837f240f02771659a7b | ||||
| V: 2--5431-6 		1fc8967dfc0c77a494b799309ab589af | ||||
| V: 547231968 		9368b647e156b39acfb14c4862fc8ddf | ||||
| V: 111111212 		7a4c4f1b8f580c452f80b0e593376072 | ||||
| V: 4--3152-6 		26940b24a7153e4da66d8a52f8c66220 | ||||
| V: 987165432 		84b093a44ffc6d107ad7b469e127fe8f | ||||
| V: 683142579 		70aebffad84baefadee4ec11a6b10ee9 | ||||
| V: 173244665 		940ea2aff5130d4c31a871681bc6eb80 | ||||
| V: 486313547 		e283a692d7c5e21f3e5a06620e761753 | ||||
| V: 6-53214-7 		ed9c42f95476d5bd9aa25c322dfa2abb | ||||
| V: ------1-2 		3db6fc96af0bed387e7767d885d15129 | ||||
| V: 189732654 		401e66df6743eef21ac0a9521d03ba1e | ||||
| V: 583924176 		6a2f93593aa6209a0d2ed1033c8d0cea | ||||
| V: ------1-- 		30cc4fa4575d8924b52b7bd22938ccd5 | ||||
| V: 355523134 		8e290f67b1359e2b42030d0f4b3b14fe | ||||
| V: 662135547 		b81159450b1dbf5e247f6699978a3771 | ||||
| V: 3-21-3--- 		e35a7bcac43b3c7f49865a65d170efe0 | ||||
| V: -----1--2 		f2d740fc3d33021470ad78feb1787295 | ||||
| V: 487163259 		a8f3dbec826cecf4654d28cb7e006750 | ||||
| V: 574362189 		36c1c8a5adacaee09ec26e74e5098de3 | ||||
| V: 18652437- 		84a54ab0641b9672ed1e1c01ee747e3e | ||||
| V: 582168997 		dce81c3b9ecfbf1b1577c3f3ebf7637e | ||||
| V: 377231465 		d3e512c9b0b9475517276aa35e19b57f | ||||
| V: 3---142-- 		e04c48b370b8ec4f3d2a6fe55104d45d | ||||
| V: ------21- 		7deb96af26706c3e7faaecd287f6f9a7 | ||||
| V: 344134245 		067301f57174bdc1a190047ca71d8d28 | ||||
| V: 1---3-245 		b93c1808abc7145a2fb50fd805702b98 | ||||
| V: -----1--- 		4f2512ecc8efddc9978f9f3b4645097b | ||||
| V: 385126479 		1de2591052e74e0f671172e880a358ab | ||||
| V: 567312489 		0e4617eea72f2a15fb4e98e84b1000d0 | ||||
| V: 124873139 		c72d232fd42efce75e8b49c00458659a | ||||
| V: --12111-- 		4889813b13967043023bfd00795b41f2 | ||||
| V: 444144243 		cd594cd6d4b3a85bdd1dedfdd3149231 | ||||
| V: 762584139 		ec51b658d6c78a199790d601c5e4cfa4 | ||||
| V: 598123476 		67cf31ba28bb7dceb57ee8f68b4a311a | ||||
| V: ---1-32-- 		7e36458326b532e5fbdace7230536504 | ||||
| V: 876543129 		fd041dbe5623c65f56351d3d4b929456 | ||||
| V: 586413297 		4d58d5cf2f37d1440b2a07fab7151f54 | ||||
| V: --------1 		b09ed6026229caa68d66e730fb8a4a8b | ||||
| V: 2-22-3--- 		02d3929e6d994cb541659531e10dd7aa | ||||
| V: 266513164 		4c5ab909158a181c387fa45034e609ee | ||||
| V: 23332214- 		a71096e3aada5291d8c20019760ef560 | ||||
| V: 651243897 		a01ea206af27afcbe5264456be6348c7 | ||||
| V: ---1----2 		ce7e62d5da29d0823d96ca6e026d3dc4 | ||||
| V: 1---32--- 		35fea1c47efd4f97d02b482a001aa6be | ||||
| V: 346815279 		9b1fc26351bca7560c19d6e3990d5f2a | ||||
| V: 576312477 		144afb1c547f39986811fd947dec72a2 | ||||
| V: 592134687 		6098c043773d4fb0710e15c0a8b71231 | ||||
| V: 574216389 		a782449c23cf84a322be471a9c452cdf | ||||
| V: 287146953 		56506a20b0acefd21d9a6e354fdc36fd | ||||
| V: 385621497 		458b63db0a1dc0ec93adf00be7abc8f2 | ||||
| V: --3331139 		c46e4526db436392ed366f88a5d9d496 | ||||
| V: 265431978 		f871c7777a9e4012481323c57d89bcb7 | ||||
| V: ---1----2 		080d2758523efa3ccebbe873f6ef197c | ||||
| V: 2--41-3-5 		71496d6e09335081ac4e49a8c506e40c | ||||
| V: 3-412-3-- 		d943b69cf304d61feda9e36c8c4b3c00 | ||||
| V: --35412-6 		fdb2b8c51eb9739f9eca1138eab195f0 | ||||
| V: 45214354- 		4df1e71f298c322bc241fc3c94996b64 | ||||
| V: 244361745 		2e6c1b374f54014bdb31a41e5b9919dd | ||||
| V: 554134255 		10cdc8478283ac2642a3fd78d353935e | ||||
| V: 583162974 		b87266251cfc78a497895e4dc71208dd | ||||
| V: 596231487 		ff06deda5f6815ea89c5b43de59af03c | ||||
| V: 111111112 		2aacea3dacd4d5f4d05571faa1138a12 | ||||
| V: 154116732 		8b0caf9a0a525c75f012189dff836d36 | ||||
| V: 5--4231-6 		b75cde399f6e1d6be9706d6c16ae8f37 | ||||
| V: 222222221 		ae449dfeff4775355b9ca663e30a4fab | ||||
| V: 47352618- 		2f58a9c394d01d254755ec3bc9acc29a | ||||
| V: ---1-2--3 		e9a2552055e05d8261082c09d2d10e86 | ||||
| V: 4--12---3 		59b3cc4f7d1ef6c906101ca6bfdc6e77 | ||||
| V: 296143798 		de294802bfd760bcdfde37d1949ab495 | ||||
| V: 287643159 		7f1b781af70c0a2565851ad537c3aa59 | ||||
| V: 465213357 		e87f371d4979e1e96d10f4410516c9a2 | ||||
| V: --------1 		1d596053c880a9e08bd0e4e6375244ea | ||||
| V: 535551255 		3ab4be2a34d570562d6101145e7829bd | ||||
| V: 3--132--4 		0582f1ee87127997455fb714808a4a32 | ||||
| V: 267418953 		a4fbd4c9df212fcc42a6a556c1095454 | ||||
| V: 554125356 		480de81efc20be2b13db3df1c10c28cc | ||||
| V: ---1-2--3 		c431f05de380f86bffa3eff6dbf55910 | ||||
| V: 3---12--- 		76c519562f2b6ece724663f9057bfec6 | ||||
| V: 774325176 		eabd71100caacd9886ac112755b9c80a | ||||
| V: 564323157 		8677b137910cd002ae49b912effc89c6 | ||||
| V: 482153769 		c15eaa53b3868422a7491e4cee1e468b | ||||
| V: 385924176 		3eb456075d86629034f72794d01d77ec | ||||
| V: 333332134 		63bcbbf01456a69ed0dc1148a8fd7999 | ||||
| V: ---12---3 		f269f96fc40edf20bee614999098e2db | ||||
| V: 663324165 		07280de9e2136d41e3abba5df1eafc2d | ||||
| V: ----1-324 		5a3aedc715ab8b5a71d0af8d0f3cee7a | ||||
| V: 153312545 		f7c8d103b7493a94de336d5bfedfdea9 | ||||
| V: 1--42-3-5 		a4056d79e458ba9b240937109c5c961f | ||||
| V: 586342179 		88545fcc0c659eac3e994d9c80be08ab | ||||
| V: 888284889 		52bbdf64b4d6045954fdc56688ef66fa | ||||
| V: 365271864 		747a037ddc920c6d131ae249173ed0d0 | ||||
| V: 999991999 		dd7c59a028b7d3dd0bcfa56701d40bf6 | ||||
| V: 753196962 		f2d58336c99b6b56b96fe1bd70fcb506 | ||||
| V: 197235864 		f1a36539afe221432a673fddfacd84e9 | ||||
| V: 566623164 		be5c2112e5bd09fe9771dd7cba3c80c5 | ||||
| V: ----231-- 		f3de32932dfe8c46bf9af8368ddea379 | ||||
| V: 562178943 		018c4083746f12acae080ac80441d5ba | ||||
| V: 498321576 		b1b6ccdfc44a7489a0f530c19f83c935 | ||||
| V: 4---312-- 		210d724ac9d0af7aeb53d86a93bc501f | ||||
| V: 246381957 		493f32eb0149a3778cc9248bd7b1b623 | ||||
| V: 293949157 		53e63d6e4958e51e99d0bcb5f566fb6c | ||||
| V: 576132489 		eb34f24957923357cc0e4e1fa2e6a840 | ||||
| V: 694521897 		4561d375465b5cc82ba5b7da0647ca8a | ||||
| V: 3---1-2-- 		00ffe84df2b1202dfdc005900fc11f5c | ||||
| V: 382144789 		02ee19e06db684d192fd1e833b21287e | ||||
| V: ----1--23 		98f1438393ae5a3e6dccd71a34f3f88e | ||||
| V: 15433245- 		bdfb1dc25bcf3d9b8bf8edf095fb59ef | ||||
| V: 225222134 		cb6919843f9fb2e1bafefb6a413afd8a | ||||
| V: 111111111 		8844e5b2800219ada59963f2b54fdab5 | ||||
| V: ---1432-5 		86d8197150520a659727c56b269c6b2c | ||||
| V: 4314-4-42 		6014b669eaaf068938c4bd76fc0c6503 | ||||
| V: 444442413 		3984cf2163162502529cb13d78956780 | ||||
| V: 396257841 		98177070aac46939e188e030e3d62496 | ||||
| V: 4---123-5 		6d467c6f4cedf79ce0c4e9d8f4408ed8 | ||||
| V: 382174965 		49012c3bdd90f1745b8d16df135a5571 | ||||
| V: ---1----2 		0c08ea573d54ea48a8763efaa6d17337 | ||||
| V: 361342457 		296887cb5532784f210a1c7caa48db27 | ||||
| V: 333133932 		dab3b11f08a9e01169d6cdfd1cec9e47 | ||||
| V: -----123- 		f58b879a15f04ff62a942cb69d0fad16 | ||||
| V: 673524189 		39126fb06601fdfc55c10d7470a0e764 | ||||
| V: 596142387 		32a1fcb031835d8de9eba04ef66c8b22 | ||||
| V: 385214376 		6223e697bbeaa08d50cc4937e16196a9 | ||||
| V: 187425369 		a85b88c2163821431424202c3509bb74 | ||||
| V: 244443145 		513d840e74cc85a997055c4118844cbc | ||||
| V: ------1-2 		acda34550cf3809cb8752ce647109d78 | ||||
| V: ---21-3-4 		2e74a63c4d6b9e1a4eca6c8da108097f | ||||
| V: 2-22-1--- 		fc31b6fe22604434cf3e425fc3ffe475 | ||||
| V: 664193952 		02d0f4e29ce8b6c12c39098673eb50eb | ||||
| V: 3-21----- 		83e3167965de09ca3cded63c7dcb8e8c | ||||
| V: ----1---- 		5321943134fdaac12c2571361e3209c2 | ||||
| V: 273165846 		5ea27f991311bef0452ff35feeae5f1a | ||||
| V: 35214343- 		3a7bf3609002ea58d4357ebc5a17a691 | ||||
| V: 514523637 		a2c5fa69b9bc838371d7f3bb230bdde7 | ||||
| V: 265413457 		70f7918a20744aae8c73a3c7d09efeaa | ||||
| V: 3---12--- 		93596e6dd5046fec39b84360d73237b2 | ||||
| V: --21-3--4 		22dfa2efcfe4ce437a5896b2521424b5 | ||||
| V: 255322154 		c3079709aa5e629e38f289c9179eb487 | ||||
| V: 355524155 		852c672036e2c380fd3b72c1fcd3820a | ||||
| V: 522184866 		da7b8095312ae94fa3c3ec1c58f230af | ||||
| V: 26-154-37 		dcdd3b22679d894baf2dfdf578abeca6 | ||||
| V: 565215364 		aefc7247dc28f1e1f741258306679c4d | ||||
| V: 295314768 		ae3901f16520b90f7aa52ba483dd40ca | ||||
| V: 2---13--- 		8047f51cee67e205bbacaa777348e469 | ||||
| V: --------1 		372bd24e089a9d7726756e056802735b | ||||
| V: 473132356 		8ae4163bfa6a1b0bf41ce4c37b911506 | ||||
| V: 5--3214-6 		763173424abf54ffb1736849a11ff871 | ||||
| V: 343132645 		258a5db24ad7f2ecaec5e42a9d4705d7 | ||||
| V: 164235789 		51ee8107224ef5a0621e57733cc0d8d5 | ||||
| V: 675142398 		e4cf8161965f37b27645edc0c2ad4af2 | ||||
| V: 498132567 		94ea66e5524ad103fd2a4e825e80e5be | ||||
| V: 275513476 		d90bacaf87ee54e879d6916b9007638f | ||||
| V: 344525154 		3dc49a068780f6485d0af621b5b8e911 | ||||
| V: 643271965 		31cf29e73d41b93fdf933a091a5e3b22 | ||||
| V: 333313234 		4d398272cc170b83d1d4bb38e046b231 | ||||
| V: 341241145 		66a5fd284dd053f97f856ee85c064206 | ||||
| V: 4---231-5 		f6d23c2a3aa9a7b241bb6a4f1b9cd157 | ||||
| V: 169854327 		f5032d096a80595a75c731f0db8f9ac5 | ||||
| V: 152142554 		451d02c778b4afcdc4ce933e6b6e4cdb | ||||
| V: 574412376 		2f3ebbed8bc9ded4a725d93ce1c4973c | ||||
| V: 682145983 		8be65a7c019f5cc303ade7d1d3f607d4 | ||||
| V: 2---1-1-3 		d644146add46155ca62cb86ee7b1626d | ||||
| V: 244155643 		1dd2140c3af1fb9bbc08a46cfdbcad0d | ||||
| V: 255123456 		84a85cc820ae48b691b69f9ff507b878 | ||||
| V: 265311546 		1ec1989a8abac2bca278edad96c8f0ab | ||||
| V: 394122885 		d83c3ddd0ab56881474bae3be7adc135 | ||||
| V: 3--142--5 		f49c6238d3620d159421dc3c0c51e5ea | ||||
| V: 355125456 		742bc19e1efd4ea4529403ac053fe1ac | ||||
| V: 586134792 		a1d8322a946514463c0a47f2391e59ca | ||||
| V: --------1 		e3f5d8bdb0547e74aedb0e067def31e2 | ||||
| V: 654123879 		7b04ca30a2af51a6389ba498a7efbe7b | ||||
| V: 295615384 		ee8968ac72017b229193e8f2e775916a | ||||
| V: 197234586 		e571ea4a611d3982521507b44422f9cb | ||||
| V: 2----1--3 		bd4b6e615fa5c8154f0a7c92298d5e37 | ||||
| V: 233251554 		a656f68b43f6a033893898d9e59f5591 | ||||
| V: 2-----1-- 		27eba01b3562e3b6276fbeb2d77775cd | ||||
| V: 286621269 		f03249c241b60383117b06fcdd94cb13 | ||||
| V: 752186943 		667913ce66544f3c55ea195672a54ea3 | ||||
| V: 474144826 		3f87280c40b2088ab06d8a46be823698 | ||||
| V: 376542189 		bf2ce69341e0fe56af28e6c601e151d4 | ||||
| V: 492142687 		c85dec9e5ccb61ed8e6676fc02478fdb | ||||
| V: 2---3-1-- 		960d0f72870bafad41b2519819370285 | ||||
| V: 355162647 		56327ec0402144a7af744a7da21be341 | ||||
| V: 276134675 		e19b33e179295b1cde6f2c888ffe4720 | ||||
| V: 465423157 		01f1ba9478f9c851737cabdedb2918b7 | ||||
| V: 555213554 		06c43c5f70b81389c0f940c0e83811ce | ||||
| V: 58632417- 		75992f292891b0aafc6e2f5c6a0ba054 | ||||
| V: 111111-12 		f1983b48853dcba93afda7f745e9d724 | ||||
| V: --------1 		aa9ddaef3c62aec7e66552179a27f613 | ||||
| V: 355113354 		5ce16651123c955d0c75ea4416759586 | ||||
| V: --31-2--4 		e677ea5d9d11e73d5740d9dd98e80cc0 | ||||
| V: 2--221--9 		11704a4fd006ce5fe2dd8c7219b6b1ee | ||||
| V: 4---2-1-3 		2018f2fb975f57cbcad50930f27d9f92 | ||||
| V: 375914286 		8bc0c1a4347c03ade54ef74480c7f0b8 | ||||
| V: 4---32--1 		91c3356f3a8be093e9a439206742130b | ||||
| V: --------1 		44300321087cf840cdba98efa91e0d5c | ||||
| V: 974523186 		911309d8774584421cb2207b6688f6c8 | ||||
| V: 2--543167 		534dc18fa8968c8d6afbe6e7bb1bb4ab | ||||
| V: 162376948 		27b493deda85a020b32d87f2a866140c | ||||
| V: ----1-2-3 		eb59e40399d66c00c246325bdfb6dfea | ||||
| V: 133378924 		7fcc00f3a13c0ad091fba88aaba22878 | ||||
| V: 483127956 		097a5794e1fb240f5526780034d80156 | ||||
| V: 436125276 		b5308be450ff392d904ae96e23074a08 | ||||
| V: 578314296 		a1b4a901e1c09daaaf14499fe2ee0660 | ||||
| V: 4-3326--- 		750c674329b6075fb8274dfdbcf4d9c5 | ||||
| V: ------1-- 		448873ddcd452b462cf4c5dd0ffb82b6 | ||||
| V: 398726154 		3948e05c633c5791fcd756d1ccd76392 | ||||
| V: 355321254 		1a13f53484c844bda94aa55636aff06b | ||||
| V: 166324753 		747164c51703185ac5ec34ebc70db2df | ||||
| V: ---34215- 		97872544e77cc4c586d827660254e426 | ||||
| V: 366612465 		04a7bf5b89fb416de7a3a1e87fe0583b | ||||
| V: 4---213-5 		0396ea3ecdbe8ddc983c9b8ade292b32 | ||||
| V: ----231-- 		8931ea4448a7a7c42160a2fbd3b2a2c0 | ||||
| V: 175372476 		b0aa48e827229ff7b892d83101b40a4a | ||||
| V: 46512738- 		3ba3a82e8832bf7b8a0026227927807b | ||||
| V: 189723645 		7c85d2372017f0ee0fb704ed172eb683 | ||||
| V: --------1 		156186b54bff4c7907b5df6464d8feed | ||||
| V: 526943991 		b3231587079f02856a593300acadbbf5 | ||||
| V: 888819283 		7fb70747a0d5320877d28d97b770920b | ||||
| V: 3---1-2-4 		e26de25d1a566a20401ae8057f545102 | ||||
| V: 496512378 		764f8e7a378d3f6c76ac132e57108cde | ||||
| V: 264123335 		fd2b183c89c1fd2f05d3359d678caf89 | ||||
| V: 456782193 		7234ee67b6ca1902ab2e00ef7d720df2 | ||||
| V: 355134256 		1c0e8c39dbb2b38a75a4c3f19ac9259f | ||||
| V: 354513267 		aca0c504d4bd9914379976fe1d18b275 | ||||
| V: 476532168 		e0764d62ddad54b8b6e98fb089a17fe3 | ||||
| V: 466412375 		9769a406a06a0235a504476dd27df65b | ||||
| V: 1-------2 		e24806bcab753eb0ba0a3645ceab0539 | ||||
| V: 111111111 		44be34c4339cbd9e9518dd50174ae944 | ||||
| V: 374281956 		4500e4f45a2f577bd8143c772d67f5c1 | ||||
| V: 365187924 		a25a9bb597997a98ae0f32aeb41955ee | ||||
| V: 194372856 		cad4db5728398cf2f58caa33c919cb3e | ||||
| V: 596416287 		4cbc67bce81623d5f6dbf5b689855ecb | ||||
| V: 381254976 		e3799870d31e15c5fc2602f337564757 | ||||
| V: --31-2--4 		05e3025d083f1c723df5f62ff1a9f2d6 | ||||
| V: 3--321--- 		75c92f1fe4b33daf63318c3443d6eeb8 | ||||
| V: 785342169 		8f5b77fe9bb0791771c04c3bec9cf71c | ||||
| V: 23232114- 		608fd99471a69c86da4fd9b2d65e4041 | ||||
| V: 121133524 		3792a22312edbe0119f58f0429208ea0 | ||||
| V: 195324768 		fd7444f48ce4680da423975408624084 | ||||
| V: 254731689 		7bb119c375699411e19265067c0d488d | ||||
| V: 374152698 		65f8f21944b9a1cf3b5aafacb1595a19 | ||||
| V: 4---251-3 		fcd68ade4a0848e838425841f1f6e173 | ||||
| V: 456789123 		5ae533c50c1040e84e4e4c67889e6cf4 | ||||
| V: --------1 		d8d3bf624b18531382e159cd2beecef5 | ||||
| V: 896115792 		6c811386473a4cd9689934c381a2e223 | ||||
| V: --------1 		702f6278c262559addeab19aa451d47d | ||||
| V: --421-3-5 		163467838b8b0a764368392812d20cf0 | ||||
| V: 589137642 		872bb1d79a6c6efbfedd09fa4144b98d | ||||
| V: ------1-2 		838574ea391956187d09cef22818cca3 | ||||
| V: -----21-- 		ce617eff7e6139505741b9395fafe735 | ||||
| V: 273156984 		5a48e39939f7eccc20f36e873cece388 | ||||
| V: 199931498 		4127f49f613bd079a5c61bfaf27f9fb8 | ||||
| V: 952184763 		18669f7a2e8fd42eb615471c7a24988d | ||||
| V: 444211243 		8ea420445c49abc20903ad72e0115f01 | ||||
| V: 156723984 		cea35d528fb437c707a4f3da25e8ee04 | ||||
| V: 394152678 		cb1b0ff1dc395c3629f4d27db93c35be | ||||
| V: 495124478 		958f9f64e3fec695230fa900dd9d58f9 | ||||
| V: 76-1-438- 		eca2dce469616c429cb8ecb5050f3940 | ||||
| V: 4---231-5 		731c3fbc78d38d309c49ee6012bcea39 | ||||
| V: 3---122-- 		5ca9803e8d3b2752f96c95cb2f3f9af1 | ||||
| V: 475143286 		58f0a8ba4dd1b1606b58057f565a30fd | ||||
| V: 4-5-231-6 		5f38bdb077a31e32187baa761e7cc636 | ||||
| V: 562175834 		a466b5b91e43b9a61af656da6d530627 | ||||
| V: --21----3 		8b3755dc6ef27e43a346c9a9403e27ed | ||||
| V: 1-------2 		30c4333004e45f08bb7b8d84f4de638b | ||||
| V: 2--33-2-1 		62f3466069f39f60b3910e8d32edc347 | ||||
| V: 2-----1-- 		013cae7d31d7a577bbca3c9e2bb498d8 | ||||
| V: 399142498 		6daae1c6289aedde583e1e967615e82c | ||||
| V: 495932199 		91df45e7019d8976f0dd31f2c8dbcce8 | ||||
| V: -22131--4 		a505bedf414f1415bd751b6ae28804b5 | ||||
| V: 274414379 		96fcdcb871e4abe1af7547e0c231331c | ||||
| V: 798314265 		ea0bce5bb1aff2deeab387c4e4ed9c3a | ||||
| V: 597321468 		3ad68bd9961a5c5744f52647e2f2504e | ||||
| V: 255414553 		917ce5f81938ef83a4f3db51ae6178bd | ||||
| V: 174293958 		976ffc2fe7dc2d4cad3a8f07f68339c4 | ||||
| V: 366324165 		bdde81ba338ddcb33001f359c5214c90 | ||||
| V: 555314259 		95f8d0bd38affe6ae9c3d71d2199ac42 | ||||
| V: 478613295 		50b490bb14d6fed354dbab130f07da2e | ||||
| V: 14537682- 		eac404717952d119d722f2f15fd3cd00 | ||||
| V: 239814567 		2d5ea2b98b818352b34e4344a91f1fc1 | ||||
| V: 333331332 		5c1a7e3719bf2ce986c1d39d18548f34 | ||||
| V: --------1 		bd66e05d62ac660c5c74ee274107628e | ||||
| V: 2---51436 		6b78eb57b435fbf29633bf1abe9a27c3 | ||||
| V: 392164587 		24d7edbaee90b8e5fb8d800ed2ddf351 | ||||
| V: 173642589 		868b4a0cf7ac9e47f64659670b7c247d | ||||
| V: 37514268- 		8c40da82b9b3ab4983d5326de87c781b | ||||
| V: 345617289 		dfe43345c1dc20f29c8750eb06067da1 | ||||
| V: 285912457 		82b5dfb5c8756f3902cb2553309ac0d3 | ||||
| V: 222222221 		c5b28d9d485fb3841d31e82438e42ac2 | ||||
| V: 477215376 		175d997dcbc410c4386c00e826b51273 | ||||
| V: 342164645 		d91be484ccc0cbaf2ebbb274b14f71e7 | ||||
| V: ---1-2--3 		e5f837e3b0a4408b9a201a0014a43f07 | ||||
| V: 999299199 		452ee865caf3d481994da4d869701f76 | ||||
| V: 1--2233-4 		ae95067b3585cd16a60ac24685b2a0c4 | ||||
| V: 2---1-3-- 		e98d8eded90d2fec8bb9c3eaa5de2fcb | ||||
| V: 794316285 		a3e5a1018c069d7e9c86668465ce0a32 | ||||
| V: 498726153 		ae9aad17831770e8909ee41908a8f2dd | ||||
| V: ---31-2-4 		8573c551f716d78cad467d17a1df1121 | ||||
| V: 233241645 		1352574fa909e3b2eb1667a250596344 | ||||
| V: 46226---8 		4bac6c66bd4a909a4ba0bf446640aaa0 | ||||
| V: 163284957 		847d6aeab5fbb1a3849b7e44e6dea116 | ||||
| V: 1-------2 		04d3d08aae2a5406a086129a5a8281b3 | ||||
| V: 23331213- 		a00acc5da9cdae31356f3e4446d83487 | ||||
| V: 371243856 		bec3edbf12bb2f0cff96073fb638b078 | ||||
| V: 195726348 		073499ac9d86f36aa314e3a0fdd6d41b | ||||
| V: 3-21-2--- 		25cd17899934e7aec5c36d412b143fa1 | ||||
| V: 367524189 		36432746596bd9078c342e86b4803786 | ||||
| V: 387425169 		8b8c6575aedff7a0ce8363ad23de6965 | ||||
| V: ----21234 		c46c4edbb67cc243051f483fad0b27d9 | ||||
| V: 675132466 		9639e14905858627e4069e1362be836d | ||||
| V: 1---7-8-- 		2dd0cd8630c080193e998b85a9c6b343 | ||||
| V: 583153986 		e722c36ade585eeb09e41475fd6b2645 | ||||
| V: 3-15-24-- 		9598237dcc4e6c2d69ae1386004cf2ab | ||||
| V: 567432189 		5a550769adc0cd30c8c143b008536098 | ||||
| V: ---31-2-- 		df42a1a9e276a8f937aaa517f9989a63 | ||||
| V: 3--5241-6 		2cc98e3ff1202694eaf23af0203dcbe7 | ||||
| V: 454513256 		aa11975d8353c88727b59a6a65eec05e | ||||
| V: 472135869 		973b9384b33e21e06b79676b6132982e | ||||
| V: 876542913 		0b768ad1c7eba0d6af8ce4f9013d3067 | ||||
| V: 583421967 		a3ebcb8830c373594a6949bbab3ef605 | ||||
| V: 473562189 		fcdb1e90e4e87f0b313fa3c4ed30daa1 | ||||
| V: 492135687 		8270767ec9af8ff30163c1ba11219d57 | ||||
| V: 473152986 		c863b36f1e9bb6a2e0689b07c7cec405 | ||||
| V: 34321215- 		2048937296216731b4942f13234732c7 | ||||
| V: 234615337 		c722315d845bf3b8900873f01a1b6d80 | ||||
| V: 894125376 		1c74a47b123bf2fff4df65a6a82ba0d7 | ||||
| V: ------1-- 		b513e43d27e8ef8fbab1397c1ef0177c | ||||
| V: 378214596 		2e60fd12c3c8b523560d212c0f8a520e | ||||
| V: 466236165 		ed7047447d8c83123d10ce83371b048f | ||||
| V: ---3-12-4 		06a00e033e301fed22a75c8eed28c2b6 | ||||
| V: 465213265 		e57783d1b8b193db6d2b0cca96f0907d | ||||
| V: 663314165 		c9e848ed71b49de37adb3729c610981f | ||||
| V: 494613278 		104644f9f0daa43db0ecba9a425b7552 | ||||
| V: 184536924 		7d05d0a4355829223b4bcaaf18e0292c | ||||
| V: 695421378 		aa785047e901d85ceaddb8d39ac8fbb9 | ||||
| V: 196724853 		673220b63776cf4cdba842d83802a29f | ||||
| V: 2--11-1-3 		cfca0279d8f7a4c7a75288e05c3d7c23 | ||||
| V: ------1-2 		e26b476320b331b4f47d01807dce2e1f | ||||
| V: 173244536 		9091a67166331b5b8a0af81dcaebc1c7 | ||||
| V: 441244443 		a25d9aef343ebfcc2fd8eff632042e68 | ||||
| V: 3---142-- 		26e7e3c411611e9dfa6cb5e12041091a | ||||
| V: 388824189 		2be1f033b4144e56b1e29b207a014de3 | ||||
| V: 583246197 		a127843a35f49ec7900797879d5582aa | ||||
| V: 883312497 		ef7cbe1df6a2e3f9c18c0236739a4469 | ||||
| V: 164612435 		c0141e6786e1d7fa0ec6f14ab42af5db | ||||
| V: 587321496 		9880d041dcc6b49ad32ae1b87d20874e | ||||
| V: 483257169 		5a5536130285fa1f49e59b12f284bfb4 | ||||
| V: 47216358- 		36cddfe1a10d5d4ec18789f68652cce0 | ||||
| V: 781243569 		e81b6f4d2d685a1f0f7199fc6602e78f | ||||
| V: 682143597 		b47401bedd3e9c14d3083857473a2e0a | ||||
| V: 2---413-5 		38552e43c001aac5248885ded1cdb116 | ||||
| V: 575121673 		d85a7e66322100d2efcbc8ca51ce86f1 | ||||
| V: 273451896 		a3875fe4e1af9d07cf9efc661954e838 | ||||
| V: 193991499 		646a655c6c6b8ff073cb74cb8abdfdf0 | ||||
| V: ----1---- 		434d6c54c6ee22d9838c17dcde1e8714 | ||||
| V: 111111111 		db0ba98892501139afd8e3590c2388ef | ||||
| V: 14112234- 		c96fd6283ac8460dde8b4ef281ec6f03 | ||||
| V: 173242856 		af7339fc519870f144e700dfd1366b98 | ||||
| V: 3-11-29-- 		9054f0443ce90b42d1d5d8915e6670a7 | ||||
| V: ---1----2 		fdbd02faf74cf40038c7f81887c5b1e1 | ||||
| V: 372135574 		ecd69fce4975cfe9f7291bbeb992bd24 | ||||
| V: 471326589 		6dc06d9ae7f4482b0f54b52fe8b71bf4 | ||||
| V: 79518789- 		307439b033a02f983be37e6b0dec868e | ||||
| V: 273155467 		196cc4e7fca7d16acb693b92d6513e40 | ||||
| V: 613254978 		73d1b0b63a1d17919b20b8c0bea1f0a6 | ||||
| V: 265134466 		cb63ad319b5aa1a7c7aed178ddda311b | ||||
| V: 372156574 		21d94a46a5a9162b906f4c4f985131c2 | ||||
| V: 696174-97 		f077e7bd35a3194915b3002853a9e374 | ||||
| V: --41-32-- 		c93781659b330e3322699299d7b0263f | ||||
| V: 4-----1-5 		92a18fedc75e10f238b593a1068cdb2e | ||||
| V: 582167943 		9fb47f93ec4ad670378899f259e1cd2c | ||||
| V: 185274936 		0dd48de8586a03bc634d5b7f88f2ad09 | ||||
| V: 794523186 		6461f50716c1241c1e94c44d01b9d223 | ||||
| V: 444241443 		61b0198899c64e3f3893662524afbd13 | ||||
| V: 2---1---- 		bfa429a7aa1dd33851efb4deb1a182db | ||||
| V: 166146965 		8be478fdc7fc4f6c5f5ec767c26b7070 | ||||
| V: 361289754 		9ebf1d644affd657f6c8aacb7893ad70 | ||||
| V: 222222123 		a6ebe2e8cad2d93c0034ea8a1f800a36 | ||||
| V: 45123245- 		6524617fc241314119683ac3aa923033 | ||||
| V: 442134365 		8f663a21f0cbff38bd59272657674974 | ||||
| V: 455512359 		56947a39e87ec487c5587de1974d2266 | ||||
| V: 23331414- 		774b5d5acbfd0c28486987b6e2858ff2 | ||||
| V: 672185943 		7ad3a7548af383e4c6e556f064821e87 | ||||
| @@ -0,0 +1,359 @@ | ||||
|  | ||||
| 		       Tally Sheet for the votes cast.  | ||||
|   | ||||
|    The format is: | ||||
|        "V: vote 		HMAC_SHA256_HEX" | ||||
|  You may locate the HMAC_SHA256_HEX corresponding to a debian login ID, | ||||
|  given the associated secret token returned with the ack, | ||||
|  by running the sha256 hmac function. For example, for login ID srivasta, and | ||||
|  the secret token 0123456789ABCDE, the following invocation works: | ||||
|    % echo -n "srivasta" | openssl dgst -hmac 0123456789ABCDE -sha256 | ||||
|  The vote block represents the ranking given to each of the  | ||||
|  candidates by the voter.  | ||||
|  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | ||||
|  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= | ||||
|  | ||||
|      Option 1----->: Jonathan Carter | ||||
|    /  Option 2---->: Sruthi Chandran | ||||
|    |/  Option 3--->: Brian Gupta | ||||
|    ||/  Option 4-->: None Of The Above | ||||
|    |||/ | ||||
| V: 2313 		00230fd5e1d4c86edfefc517d88a8a6421826b429fd2eb50ba9671c127897e6a | ||||
| V: 1234 		00a38267cbca56da01221dc49c7e8a0d5c0c92ac6943736c47301b76f07fee55 | ||||
| V: 1234 		0104ae27ece19d7ec3c44235f5cf3d7f261da7199b90517045bfd1a9f9e2815b | ||||
| V: 122- 		01615815a269fe2204dba1f2ed62646b2c88406f3de4b4a6146360e1636b17fc | ||||
| V: 1243 		017d19eb2a0ec8f22b4f0da0903679e2a029a6ffd6fe7ac97247bb0ce7cd308b | ||||
| V: 1123 		02b68510f34e9dd4001b022630c1db0963d2dc5b631ecee71640c0d6d620b8cf | ||||
| V: 1324 		03587b33eddc978bfb281892a55551b723f72871791cfb3713f697dda46ce303 | ||||
| V: 1324 		03ddd8e7530ad80a89a0e8ae3bc09fb8d136b565a478dc506521c097cdccc39e | ||||
| V: 1342 		045f85c8bc3ae81c942f50e0683a31a2bbe34d3fde4adb6f211879aa87542e4f | ||||
| V: 1423 		0553d6b166e9cdcfc40b02a8d616fb0595b0c0365d4dae15355410b123169aac | ||||
| V: 1234 		05b31a059b06d5bfccba09fdb392cffb7e27451eb0b8a3cef66fa9ad99313a40 | ||||
| V: 122- 		05de119d845c3db2dc4f32cdf997408da48fc73f8a8de783ac7999c16c4fb466 | ||||
| V: 2314 		062c6f02a8458937f0f462010854b2292527a09f38d9fa82df990c0934ad8f03 | ||||
| V: 132- 		06b5c7c8f84fb0c88ad55c734c2523873029ff932e4d49b4f0fb5f95648da397 | ||||
| V: 1--2 		07b7623628d5dde1d3ced9ad560639940578dfaccf3739e50e4e7058111afa53 | ||||
| V: 21-3 		085dce24b409845a4deb8460237634ae3fc239a27b69ec08b6eb15b4f276efbc | ||||
| V: 1243 		087d057b2e69670448fd5f60d428f2b93cd3ae3f2cad1dc18bcb8daf85c5bff2 | ||||
| V: 1243 		08ba708a7bcd34d0a08dbff732ad6ab00349b77504507681514c993c1f8d3545 | ||||
| V: 132- 		0ba19a57a9598d8a755ce61da4a55acb4131a924c0491db78333b1649f72ec0d | ||||
| V: 1132 		0bab6662fd04a87d3a456da6ee1e965268a50301d2edb835c58d28daa863b4da | ||||
| V: 1143 		0bdb370e94eec3d92784bc19826c2e6221835c90660b8264e62bf14d68245e9f | ||||
| V: 1--2 		0cec914c29299b7a40084cb19207869baeb3f135bbcc86259802b85c8c2de03c | ||||
| V: 1342 		0d24800b8d57e28b571546ee17f184870bfcc4cef7cc8faf8b3ed1debc5d3c13 | ||||
| V: 213- 		0dc034010b256c0b1b72fa96d0ab7beba78d90a4d708d32d2a4495c453824e65 | ||||
| V: 1243 		0e5ab2229dfc9666e94a6c21f266744030383e4aa6c590f56e7236b447920fc6 | ||||
| V: 1423 		0ea1e5a3edec6da8fbf88ed662a19e6caf5891460f2fcec767f625e0134a4249 | ||||
| V: 1432 		0f3a6fd53289be2a20764c36f4a9ddb6b6561505b449d12d44f246ca3880cd77 | ||||
| V: 12-- 		1051b91e22d54b50e70ec2ec9eb90a7ebbb55f4795784d84bd60fa06d27baf3a | ||||
| V: 1324 		13bbd22d648f39b52e28e39324efbed56c107030187f9fb836828eb72b47cc5e | ||||
| V: 2-13 		1453adb5e5cb9adb37fb879054672c81466d37795b01e2163a6de88832623927 | ||||
| V: 132- 		14dc579f84696e02c6da84960ff5057d101ab52612caebf1bf82079b6035455f | ||||
| V: 2134 		151130777ad486910fcdaf47d71340d7bc97885e9e445551cbbf3965cbc4153b | ||||
| V: 1432 		161e7cca0fc2ed16289b488734a1743d85d66bbd548a10200dad251abe20c745 | ||||
| V: 1432 		186217749f7512384f01aae7e3b02f5b48387b8ea72b46bfcb112b446cfdf0d6 | ||||
| V: 1234 		18d533341ce06a37e6328fd7bf557b03e09ace5556d2e671c9eb1b976acddfca | ||||
| V: 1234 		1a096393a22ed2bfdb6cf02b59d9ba83057a50b6f339439a7d16e4da17c9e0bc | ||||
| V: 1112 		1a0ded7512e931634e34a6bd70079e7ba5dd8ce8dfd5e632850ce3ef17f14722 | ||||
| V: 2143 		1a126954cec275899d8ee55beebc50648d7ac4e7585890073152e69f107ba689 | ||||
| V: 1332 		1acf0431d98eb0d6888afd5f94713552d2118ee2a70f77a3618f9b584192ec30 | ||||
| V: 123- 		1b3bf8eebc3b4ae959f844f7ea40cf538f5cc04bdd231c5113f98be72f6d8912 | ||||
| V: 1132 		1eb417dd3c44eb7f69625af204d89c232c4b0e620fae9ec3f1b93d2b9844a416 | ||||
| V: --12 		1edc3a57f6b9919d80de8befecc9435ef9cd93ea52d52e445cacee0acdea213c | ||||
| V: 13-- 		1eeea04ed2aa31e13e343cd124cb96fda976ef90d533d6fac2415ba23c187482 | ||||
| V: 231- 		1f0b97ea0043978b4095ea3a58fd02fee5cde226a24b8c1b84d498df2c1a6620 | ||||
| V: 21-- 		1f38b144ef16134d85c49c34b5baa25cb541125c39e8d77ab1ff840e3ac4a0ab | ||||
| V: 1-1- 		1fd42b5fbb1400cb2b8815e9d595fb4a8de78336e3ae2c32fe7b972a08cde8bb | ||||
| V: 1234 		2069c55c2c2a39879251ee28085ea7fcafd474c0a9adf71a7cf05710a5c09db5 | ||||
| V: 1243 		216c76dcfd63d942e1453951d5ec89ce43e5f1e579a99bf5b232cae308915694 | ||||
| V: 1423 		21e9f11aa81008acb5ed4651cde7444e988f535929ee2bf48509f9f5a02a574a | ||||
| V: 123- 		23117fe84f1e2e4438d1637961f6eeac605b931477fcf8b41656b97f32032c89 | ||||
| V: 1432 		23b10cd32ba0ff66b69720d5dbeca6c4c93139a826d240825d808a91f2a64d28 | ||||
| V: 1234 		23ce2496abaa5eea4c66d1a93937b6062eb0449cef6262036279e5bc74267936 | ||||
| V: 1234 		2501bd56f7321c5658adad5bf40e62fe833e81f6eaf8573ed9db03ede7e29572 | ||||
| V: 1243 		260d9f85b8f240eade7ac80b4129b2e422cd76b3c965c61766671d7e5ff87fbb | ||||
| V: -213 		26620d1f5308853e02685ec9fa36185eca36d7e2c9183b037ebdc2ca28e62472 | ||||
| V: 132- 		26db6a1d63640b11d2c0f5a44e09c746aa506ad049d5cc5508bc1e2309a7d914 | ||||
| V: 1243 		284834e16622976c9ba3feae7caab326255bc937b547e4cb0a5db9b641847fa7 | ||||
| V: ---1 		292ba49a9d13ac6688dde6c06284b1f8e9a122ca974b6e64bdfb8a5a8ef16792 | ||||
| V: 132- 		29ddd123d7c1d7228b8f04a7c357b8bd525b0b985d1164e0666b6ab624e121d8 | ||||
| V: 2143 		2bc79ff7b56ba1b5f973e2d686cf0fe8284ed02192e56b336aff541a2f9cfaac | ||||
| V: 1324 		2c19f12a63d779d18bb786ac528ad8703e3d273fa766f9572de7c5d92c076d87 | ||||
| V: 1243 		2c309b4c136043e04cf5956a6c9bb8b730572112d1abc4898ef48d4b49dcdfea | ||||
| V: 1324 		2c4cbc9fa4f9ea291ffc2d22598a7f0425524824d39adc4424a91ffea815b4dd | ||||
| V: -214 		2d1a15cde460e780bbab7864858e05bf946229909053ca74c1c740b358d4bd2d | ||||
| V: 1234 		2eba3f1a686253d92f5b87c05946bba17bf3ef377cc99549416300ffe68bd119 | ||||
| V: ---1 		2ec9d0c0596db6bdb00c776f8f5a984946af44a2d223e99f03495e8d97e2a9bd | ||||
| V: 3142 		3090dc887814dca97535dd715044fb156ac3924beb40e278c9604ec3b094c73c | ||||
| V: 1234 		3173f1d70ffa85e2f68a7c8ef19d4cd7f51cc1a361a89dac928800d195027b9f | ||||
| V: 1243 		3184d63ca3cf69e8ab4b6065786da9675325ab781462f052a82e3903d68a3273 | ||||
| V: 1243 		33ee6bed02afa4a2b68099485168b759bd05ba9265f88f139b71cec1c7a3ba24 | ||||
| V: 132- 		37ad76134fdb50a50a2ec09baab6e0bb3e1c94c0055e8a0b2ac2e53659f01121 | ||||
| V: 1432 		37c2766c4d7c94ce4f3645656d814ac1b91d802740d159af0c6d623f9bc58789 | ||||
| V: 1223 		381b844d8cb7f3e4422b8a41e6a370416eff97d58040ef9d68ffa212beb47f84 | ||||
| V: 1--2 		39181f9cb392f8827ec6156b704a5c625c0eafc59356042ab9adbfe636ea4a61 | ||||
| V: 1243 		395bd339c374ce48ccb2c25626345d63286354c4e65bdc50390884fd860e8a26 | ||||
| V: 1--- 		3ad0fc89ce243ce38d98b6cc68df05ba7593f1a71dfd4986b1861d11f0b8454b | ||||
| V: 1324 		3b9d015bc558b6b18d66b1fe1d0fc6a2476b0612e051462653ac24d300a59537 | ||||
| V: 132- 		3bdbc10ffc8965aaa8364978c47e6c939bf3ed6dc6ccd2f76fddc116dfb35aff | ||||
| V: 3142 		3dea431f16502a73772a3d92d86e321a2a33d8f0477f0c1ae627012844d052b6 | ||||
| V: 2143 		3e7cb35db49524a7bdb03497210ea62402fd77ba6e7d50ef0ac9ab19faa5ccc0 | ||||
| V: 132- 		3f376d5d675041d3cf20c7ae80409363a26a2474d1b9182f44fd5537d7738a7b | ||||
| V: 2134 		3f831cbb195e8130e3d4d7b430703598e2e1d1280232b5e798c164017cdd79dd | ||||
| V: 321- 		3fdb669d233eac37cd3b4814f2492b1db3b00d9b7d58633a5901e19849d56d30 | ||||
| V: 1243 		3fffa918e99d969287ecb666e02d1ed0d94ed05d42554434f62044d1a151c1e8 | ||||
| V: 3142 		400458b4cb458c603567dac49b656a878c3fc92ad12e806e89d6f8c009e3b1ce | ||||
| V: 3134 		41dd69104f361489e952a4dd09cfe48e53db931769329e2bd0b0ea8ddbb06a40 | ||||
| V: 122- 		41ee96f092b29cb1c0ca37e5e35b468470b9895656650a375b4d6904082be689 | ||||
| V: 1332 		41f26f6bfc07601b29a5b37c5ee991cf64cd25b2aed7b48bfcf8a10e9684cb7a | ||||
| V: 1223 		43036914deeaca5a3192703254620fc558953937b86289681ad1443fee066e43 | ||||
| V: 1224 		44b7f1a5b9808acf6349f218abf41c6ffbc7c89e9a12d4dd2f1420dee05ba7cb | ||||
| V: 1234 		4512c7af3d347c0ac67661a0b7b118f129147b814bed56c99d1253c2fad1ba8e | ||||
| V: 1224 		477d6b7e8b19ff5ce5a992a6cdf2ad237d96304ade46c2c81ecfc99158e1d4e5 | ||||
| V: 2134 		47857bc01cfcc6e4650335007de8bc071b806ea92a2fd023ab566d0e89aa5464 | ||||
| V: 1324 		4912aa46d364ea1c951b35f95155bce5a8f2fd7510fe5310b898b80c54e317ae | ||||
| V: 1324 		491b838d26c32c9fa15b6a7f79c428cbcca0b4cdc9bbb8c5602b0471bcc82e46 | ||||
| V: 1223 		49d8a2109ccf6f4f0337a0675d866c7b9fc1eb8f8d603d57424ec7999c2c2141 | ||||
| V: 321- 		4a8af63961802228c24ca6c12ce8e9314ac2e47704133286c1534f76d85ce8c5 | ||||
| V: 1243 		4b2aac61a4d2fe9c81a3c4dce5b447d15f092cb348d8ba41311dfcdd28db2256 | ||||
| V: 2143 		4db60fead16f807ad0179aafca04ccd016b467451c5f7d69b0ac9c66cfa9be17 | ||||
| V: 1223 		4e9c0fce8c1d0a7135de0385d6bce3e96426477e4c3603ba426477e9d37b9f69 | ||||
| V: 231- 		4f7c1ced6a867a03ef9930f976d24877bbe8967d93e04df5f5a5d0019960d802 | ||||
| V: 3412 		4feaaa2111dd4e68de721ffa3d38d73efda5dffa4a1f6b1e5db373bef286d9a4 | ||||
| V: 1--- 		50704d439bb5fb104218313f34ab8af85e33bf2cde0d5c9e1d265297af6df010 | ||||
| V: 12-- 		509dd76a0698daad30c206b70dc20fad0b8e0b257a1b4fc0ef4c22870de3cb33 | ||||
| V: 3142 		50e8b2b548e000c6c799e9c5ccb8e695d307e4b939036837f19a0e3543c6669d | ||||
| V: 1324 		51635961ab65280f91b5dffa400d40ac4be172f79992b9854b196f764810fd79 | ||||
| V: 1243 		516f46fb19c7f540b47920f89d016b20ef0bd9ce109fb9e3560e6148aeaf80ba | ||||
| V: 1324 		518bb1cfb457acc983e07c75c225e6c580227e5b7eb58d15c2a4951fa3b24eff | ||||
| V: 1342 		51eed5a592fe0f6b602cd1b930744bbdf3c2acbba1b713cceeb0a47b41a61bfa | ||||
| V: 1324 		52072e9dfaa3e288ac7c6c4e996d9d213191a3b7e1ce41f35cba0f26c278a39a | ||||
| V: 2143 		5221af94b2dd98c45448b21f3fa1d647f06dbff91b3c2f5bb670e863406656e4 | ||||
| V: 122- 		524776b066a0246ea93c565f1124db9712c24295ae372040f6bf117b9c128c47 | ||||
| V: 2431 		5448422e27b967db46fe1025488ede682b1861069c2dda98657d400e6e0831a3 | ||||
| V: ---1 		547fb0314fe14791b18accfbfc2c565dfeffa209c16d9fe8bc4e1adadba54d20 | ||||
| V: 1243 		55fcc1800eb8048ba76308cb27ef8c04012a661a192f56f49eeaa18201a62dda | ||||
| V: 1234 		564245f302908cd54b512d64f179090fd31a8503b8773abbfea9d8e9e02cdcbf | ||||
| V: 213- 		575afc0d3ba0ca08d6586d010dc1c359fe9367ceec56103293385fdca96f959c | ||||
| V: 1234 		58ddb039c66074ac1690358db20491c6f50623d47fcc828c3dec927e4d621fb7 | ||||
| V: 1111 		591cb1fdc78455e36cd85e2f8bcddb74736def1f5ae7ad477fedef1e1fd8b3d8 | ||||
| V: 1243 		5a1175ca0601f5c174484f13d1236881612dfd5fbf322e72d6e2a1b511149616 | ||||
| V: 1--- 		5af419c1c3716030574ed08c976752e861f386e62e61fa7d3a5de889f527f745 | ||||
| V: 1423 		5b3b2777c80f107e3e7aa44b96dc8a3eb8499ca77c2bc8c3f84910d89c02d0f0 | ||||
| V: 2143 		5b6250931b5f647bd4cfa5509ffb998b42c6675797ac3abd0aa1da567f7a695d | ||||
| V: 1324 		5be337340d15c2d240e38fb5c71185e32bab25eb937a95cbc38bd904aef11d4a | ||||
| V: 1243 		5c39a41afe845b06980019ef07aace6e9c64352eeb77a071b532b81a66f32f5e | ||||
| V: 1234 		5cc6847c1882bc189c1885132781e4a48326d88feb2f58245e7cce405f4592ce | ||||
| V: 3142 		5d656881e0c3159be593b204c7ea776f44e0848b4923eb3f486e165609771608 | ||||
| V: 123- 		5db083f1f2e5b1e9c2744a103b87dc3821b7b1e1c04366f9316f39f387fdfeca | ||||
| V: 2143 		5ddb66fa11786f5e4c5310d62830f2207449b14f5c9e3fa2483c172d179cd17d | ||||
| V: 312- 		5e66c5958e186f66966ccedef22cc44c4f951c6b9370d4981318809b5353a258 | ||||
| V: 1324 		5f5656e1f7eb9f716682473619da394ceab35a2f057b6b710038e641e68c9364 | ||||
| V: -1-- 		5f995e23ffc5d0472a8c9c47e31d0d89b616619026af164cf9a825d8bc0ba316 | ||||
| V: 1--- 		60df9c45703d3070b2478958f39d1920e5f8ab47132bf166127d1b02cf1dc3d0 | ||||
| V: 1432 		60fd60a3e86b21489df394bd077a9c2ae852f7eda146e4854a3e979484cf7325 | ||||
| V: 3441 		611be55bcd57fc89c016bae02331ddba792f218ad1d8a19434f374e5ddedd6b3 | ||||
| V: 12-- 		6122856a4782805b2e6344f0b46b02303f630fcf48e051103e24ee1c742b0e0a | ||||
| V: 1--- 		61a109bf6db7dd040e3321cd335c4ead5d479901a51cc58de3c408272d722310 | ||||
| V: 1234 		638ff677bb694b99a45da3187b45146660b7c48a796a24add68188ead7d729b4 | ||||
| V: 1342 		641bcca8c7a20e1668dc94658f01c85c40e068d66e4f386d359b5e7191cf8fc5 | ||||
| V: 3421 		642682889d4ad50f32e25ee327d181bf04a17396a1763efed6a709672eb81a60 | ||||
| V: 1423 		64b2289a6d4be7c4bd71dbe24332e7cc998d7402f7eb2ec83f52353892f5794c | ||||
| V: 2413 		64f36c07e169e52fe1194e3a579e2572eb76ae2e4f6ef5f43e0b5b4d8f42e3ab | ||||
| V: 2134 		6567d243a933775e2ed9295b6119b3d0c7f7083621e3345aa231eb44d1d74327 | ||||
| V: 12-3 		65727b46046d08cc287a31437d1151926d633c56a59aa6c739d9504746302fae | ||||
| V: 3132 		65782607cad77a18d407affd55b7598698cb538aec207e461384ecd9fa907108 | ||||
| V: 1234 		665c01ea8f42470faea4865677c8e6f907fd2d7263f5f41c3af967c65b7ac87f | ||||
| V: 2134 		66651c00d9cdf52c46d3d38fb19970f605d1b352aa21fd8ee69ac3e8d7bb338a | ||||
| V: 1243 		668fa18d7a1d0dd597365487faf797bdfb83f8871290bcb35c240b2cb5de7d52 | ||||
| V: 1324 		670842dc2ea834a83df2179525537054e5f99c483ba1f3279b5ea2e77f75bf2d | ||||
| V: 3214 		675bbed303062069bb8e1484b551ffc198a723f4fd735f49515c964545bcfefa | ||||
| V: 1234 		67a766ddbce1a86891a9f15d4a01b5190da02ee9258368bb71d4e8bff0f70aa3 | ||||
| V: 1134 		67f771b1156d5a82b59187c4e4a490d8d4d5e88f383f9995067828ae91919333 | ||||
| V: 1243 		68f436c1a53db55420d006a73e9777ed2cad57b7fe9bddf0c3400014f0b309fb | ||||
| V: 132- 		693fd519a6ea39a355d655b1d985ebdad5dba4c5fb8806dfe84de5c09492316f | ||||
| V: 4321 		695317bcaa6cde855e4f2c47ec690e8c95e737ef4df95337e886c8b5acab27eb | ||||
| V: 3-1- 		69a8ef1bfb1a6f8ad9c26f5647e646e722d8d99f645a8651cb83823c2573a77b | ||||
| V: 231- 		69eeec23bc5161193d250ee139c8813d20a81ba524cfa783df1db781a22af54c | ||||
| V: 132- 		6a76e47a0e3f9868345e1fb532009514439407361fd48fa24cf4634ea2622120 | ||||
| V: 2314 		6b281305b6bf76b1f7edd6a6f26718b058990d09057bfa9abed1b2927bcd4aab | ||||
| V: 1123 		6bf7daa3756b8cbc45f65571bee50bc2b5f7c86c69d7d3da4e6cef1771736e95 | ||||
| V: 123- 		6bfa769a65845ee681a42d48cebd8c61697c2eaeec5def8091b96b63ed6b184d | ||||
| V: 1243 		6c4fd1bde48da1ab15e570dc4978c605c287440bea8b1031869f17d709265a9f | ||||
| V: 1234 		6e38c35452e2468af44eb307c96bc04f4306db04285706332fddce6284943049 | ||||
| V: 1243 		6e533b84daf0ac9b80a21249d00cc31b6e5b8b16ed2d5291975365f782ce9e8d | ||||
| V: --12 		6f3128090510e3b3a749a3da3718a347690b17515e472287c00424693a6c94f4 | ||||
| V: 2331 		6fce63ac5677b2432e969dfad5cd27369c163f2f8dea4bf9c28434b888104441 | ||||
| V: 3421 		710dc7db44eb6af16776c9e32155f45568a5132c1cf29f4298e453115e25456d | ||||
| V: 1234 		72392dc6cbdf61b2d0c35c654e1fc7c0f3a6821b5ef821942483eb620b438744 | ||||
| V: 1243 		725540f8ffde4cba4f5a77efb11154a68d42d8d1a17a953b8cf11340120aa7d8 | ||||
| V: 1332 		74c31d4c0ca6fc37392e93076f999398112c795c5d765455499a7559631ab47c | ||||
| V: 1234 		7777671d528f4c7acdb7857c3ea933a0504a9a08d9b24c6b8f500efc51fea4fb | ||||
| V: 1324 		7b59a47801e9b0852184c6521151992f8be634e272d2f7e53864e8de1ea87afe | ||||
| V: 1--2 		7b75c039d6aba1b5a61556bd4bcdba732a4c4922336b51340ab5f1154cbfc370 | ||||
| V: ---1 		7cfe95f01abb22402733c37c9654f19f3baf90233f515390eea05a2aea4fb9c6 | ||||
| V: 1324 		7d36731d43b81ef1a40193be3d227cc2c2f8e3137b8f383d011914e9c97e3a0f | ||||
| V: 122- 		7de6b2b78e6f33ee68a83885ec411e052c07f2a22e7a4c54fa103a716615694a | ||||
| V: 321- 		7e7da318d8ee61ddc5be7ab6ea5f16e8066ce1c60d40b78f7a58061370aacbb3 | ||||
| V: 1442 		7e94db0e97920f35e5616f635d18f7e3cb2d3c19fb4929c3419abd62c3fb161c | ||||
| V: 341- 		7ea7456c3b1b212f6754b9cc99a395d4256e81148d1e95ee930fb8b05b6f70ac | ||||
| V: 1423 		8033bfc957830b6f1e8d739736b19fe0db88dc159ca2ae6d7d3652fd27d40a44 | ||||
| V: 1234 		821d21fa4ef2127d73d1d9f1c3006d5a0dd51db4d0d687a1303ff4dadbb12dbd | ||||
| V: 2331 		82b981f25f52c7269dfa90351113c063db8aac32b8857c1ae8feaf27b0539683 | ||||
| V: 1234 		82e5a90d81c2846febd1674c8c2dfa46b9a677b1a6e6cae56d33cd2ca0c331e5 | ||||
| V: 213- 		83190b8340ed97e2e24862865c0ceef1a8231a8c57256909aa879fb62615f507 | ||||
| V: 1234 		83ec9391805a79332864d6ff4d6d7b541a6b36fd2c185e00594125e601672c6a | ||||
| V: 4313 		84f851c39fdfa82eb08b62fb6d881d50d20f226fdf5c77d00241aa0a876430b4 | ||||
| V: 123- 		8598965da7900f506c7385a33ca24cd9c321606e383b16f86031fd1c395ef616 | ||||
| V: 111- 		85ee8de9db19c908d4107d0fe81a43bd685ac2d56280354006b11ce3c4ad6372 | ||||
| V: 1234 		861f87d31834385501ff9e5a452443878c02fd7385c7b9e8cdce92c2dacf40e7 | ||||
| V: 1234 		866c240c9c28f0e2d31f11d925aab7d183eb724c4d0aeb83be07c05cd1d332d8 | ||||
| V: ---1 		86d1fc4713fbcca183523391c7f5497cfc4be98a5b2dd11aa6b727e1dbd5492f | ||||
| V: 2314 		86fdf98bf698febeb33300bc0e36942618f9e21b1885d5c77a0239898009748f | ||||
| V: 1223 		8931a8b26a2fcf6e39aee320db7ed3b15aaeb2c6c28fa41d642359001ac9ec0e | ||||
| V: 1423 		895443c7eeb83d121711181194ba5b8ec69eb5a97532e8b4839e9693b205071f | ||||
| V: 2314 		89c4b0f8625d54c44fe6d0d7b5d28606dafd36cdc0fe10bf77080e6b5b65233b | ||||
| V: 1233 		8a3d51b9c0a1249f5a4f42de84079e946b50a5a553cf6e575166d83685421086 | ||||
| V: 1324 		8a850f90b5282c48474e15f91b5503888c8d806ad07f6e75fee314c47f37ceb1 | ||||
| V: 3124 		8c07321c8a9c9247976f8179082a1fc5adc87201fbec0e327e89864bdf8a6ad4 | ||||
| V: 3214 		8fb98206cf3b14222eaac4b25c94a238236fab634ed0784bf0af1f7b7040f60a | ||||
| V: 1324 		900018d3dd3d39d7c592411ed5b483921e936a02901da2720f0062765ed70e53 | ||||
| V: 1324 		937c6eb3e558e859d4021507cc44e8bc8a0a8df7db2bea30bb141ea4f8c72b57 | ||||
| V: 1--4 		945705fa99e567f0f695c204737f76de576254d659f1e34e5042fb8a1da79117 | ||||
| V: 213- 		94c4e731e175fb1ddf4836cc9cc78c1ce93a953ee6888bc9a1438bfcb4574eaa | ||||
| V: 1--2 		95299541aff780d727f9b4014ca7d85b36e5fc822df919dac720408527cac2d2 | ||||
| V: 1332 		9619afb74f4bd92c8a8fd73bd89fa0cec98e555bb6621da1318e572b6372a4fa | ||||
| V: 1324 		9628e21866b0e71806d52d43f31716867d22320b88189203749ee4419757ad23 | ||||
| V: 1234 		9658c14a9d998f97728e9ac84bc6928e4efb6889d0a5c43e378bb3022b760e24 | ||||
| V: --1- 		966920501d6f5f38a7e8a5a1d9fb52339b72e6fd30263c63a95ed892b4713287 | ||||
| V: 1342 		974dbdc06aa63fa6a578f5a53811b3c176fd96c1c00691ed4aff0329a5e880e7 | ||||
| V: 2221 		9872b34803c05770209bde3a060f1f292e1e7cb88b44dffeddad41b0085783fa | ||||
| V: 1234 		99547a1d62c9268b50d18055e16547756960c6eedb1e0dca8457090de3d27d55 | ||||
| V: 1432 		995a9465cefdca35c49cf15e5ebb3cb9e8b28921aa87486a56cfbf0b991b0fd8 | ||||
| V: 1223 		995c49eafc96472d28dd64c0ce12ee2b95e17bbc4cfd50c5f4a3b7598e83c272 | ||||
| V: 1--2 		9a7722ac85e84660801705efc6435dafdb3ae761c261ac90c4cc4f30a9304e14 | ||||
| V: 1234 		9b0838f5891eeef1df87f1dd5971dbd32a42573927292fd4fb0c60258225199f | ||||
| V: 231- 		9d2ad16596a2fd273d94f3c44197ecc2ecd84317e7aed88dc40fa9615c7ff5df | ||||
| V: 1233 		9dbcd73dd95c769e47b95cdb7c09f199959775143529b113edcb904ca1413532 | ||||
| V: 1132 		9de1c25d174edd05d7c30535517226a1bc52a498d126c4468fe690fa6f524729 | ||||
| V: 2143 		9f18109633b37d11dca369c31ae3c41a0f82f102ae33ac609e10e96db53cb9ef | ||||
| V: 123- 		9fa2683057f83695a190663fc0e2f38fef669e0f74bbb5904d99b64aa8287d6d | ||||
| V: 132- 		9ff7950887b5103d5d8d251623ab7e887c153c7de4eaa15602d1ffb962873afb | ||||
| V: 3312 		a0a8965e15509244e9b4a87f5b05750dc92366561899f8fb5af3b1b1a5d29436 | ||||
| V: 2143 		a18fdad5c8c4dee869e3f8aba9d53dcf03dd32476d088a3ee6b0f33054a75deb | ||||
| V: 1324 		a209cf013669c8a13f31b2cfb23f0889d55f89f7ca8701c3dc0fa5d4536f1ced | ||||
| V: 12-- 		a241c88b992936b69ea99a1de315440ec715a9bc1a05fb8580a503e7885cf912 | ||||
| V: 2331 		a2488cc3e9e4a8e927173650d665c62f414ba90819fe2d9af5877ff454afe305 | ||||
| V: 3132 		a4ab151cad3197e7b9e80b60732f2283c2f8b0d4e3a3fa716f54fac325d2f30c | ||||
| V: 1243 		a4f4c16e3d740d1be9bfdea40f6678813b157d7d91b61e8bdbf8f3812be9f291 | ||||
| V: 1243 		a63f9d3a23b6159fabf45874c00bbe178c0d5bdbcd726c19a09b816a3eeabdc1 | ||||
| V: 132- 		a79eef235c0530dccab92913cc6bdc8ae62dd1a27e9c5f753d25a25865daee67 | ||||
| V: 2431 		a7e963cc242119d8583d541830fb3e6b5de7d0c28175e0a402793332fe7c74b5 | ||||
| V: 2413 		a8293b1c65fbed212bcdd2199c95ed1b323792dba3be913df45e29bffdf3fba3 | ||||
| V: 132- 		aa40bab0f832f3c0b6f8e3bec2c08a7dedc48ae527d6063dd1d7c72bf07b5f45 | ||||
| V: 4444 		aabd62acd6c8ca258a0a716b3c9fa9ca24cd3e3c4b8c77404da2d3883fb81e12 | ||||
| V: 1423 		abddd5dc2e3cfa1acddb5f386992f4a73316d758337d99053df21ae1fcf19ddc | ||||
| V: 1234 		acd0ff84b59571e14b98fe995697060303c8279e3e58d4269a2ab0cbeb040b4f | ||||
| V: 1-2- 		af0789d52fa246a8133df11e17bd4b60710dc3a3c70313fdb7b8b4f037f8bd48 | ||||
| V: 1243 		b028e3bc08d2c8fd5374b8ec5b65587cf501bff569c3bfb3c7088db54c581829 | ||||
| V: 2314 		b0bf1709ce7a978839d463c9e354ee0ae8e5b099c522baa1d083668e21dbb350 | ||||
| V: 1243 		b17d9dd78a6d16650581746d56331a67e9e9d5a8790b1068e930d17b0c85ca16 | ||||
| V: 2134 		b1ff74d6019227f860cfe394d33112090bc0fc8ab525e424f0ac393d2ed4477a | ||||
| V: 2134 		b2118cff55fc5c3c40fc1f35a718e87bf0ce4326bb01287c4a840feaf1266084 | ||||
| V: -1-2 		b2647fdbf96a8f9d5ba0722e1fc0575951f61e8e39f3360ec238b2bec4f31f43 | ||||
| V: 1324 		b2ad4279d784eeeb41cfbaed5396fc412421361cfcfe9d8f7a39773f2e47aeb4 | ||||
| V: 1234 		b2b8f07e2a6eb4e60e76eb00f86c721f0d52a7cd3ad9687893fd1697a7b0ec32 | ||||
| V: 1332 		b3282e1ace028119fb12431daca45c798ab54916a986d55c92b8106c9fa5b90c | ||||
| V: 1234 		b50c1b62fce7ce8befc477d9781ad6f036f2d281ad8d5424a94e03f7d5ca7d84 | ||||
| V: 1324 		b521fc2e8281a53bf3694c66d2a6394547bb9dfe1c838790053ba5282abcaeed | ||||
| V: 1234 		b55b7afeb537fa892f93996fb4b69a72ba07ec6c82529006b91faf931560af28 | ||||
| V: 132- 		b57abea86a397f16384d0c86d1049feadb7f1b639fd4c96f7454e44c70b64d0f | ||||
| V: 1342 		b7d63d41965da9e483d6660806f44084f60cf6d9d3ea91a687bd85e19e8e9f3d | ||||
| V: 1-2- 		b82f85d10f4ff97f0542815b64cf1c8884694adb1414b76b9db77e8ffdf784bf | ||||
| V: 1324 		b94990cb249ad507a294da3da2301e6741cf697f9ebb61a2b2698a75ac18ba38 | ||||
| V: 1223 		b9c6ab7341fe79f71bb68ede0b97bd58280187f71199522bc294ecf93a81ec24 | ||||
| V: 123- 		ba3c715b7f3b714394e82092c2a21cfb43e1af1c087f9c5cd4198bbfcb0c457d | ||||
| V: 1324 		badbdd9cc1f2cc99e903193ea3d6cd81a00195a3d1d243247621b3122497d70d | ||||
| V: 1332 		bb8a5f01bc35cbd83f156c2310699bd7f9fe6438855af3742a94977fe01305bc | ||||
| V: 3124 		bc815b156abac6bed6bbc8560fd33e8df18097b097b8d2752a17ffcc73bf0e92 | ||||
| V: 1312 		bd20e4e7057b9ddf916da20b67a64fef4307b3e8b5e6ed1fa2d3eb83cbb2eff8 | ||||
| V: 1-2- 		be58de50429f21fb6f069cb6191349a50567cb9f48eb0fa190db668280df79df | ||||
| V: 2134 		bef8ffb42ba8cf0973962d2320ae055e1f420e5f5e36aadb3bb15c1e28deb18d | ||||
| V: 2341 		c007d9c20bc7e5b1a97948315dd815ef95d0825ab755725b217e5c8350d342c3 | ||||
| V: 1243 		c06f5f8b93020633dba0dc34fcd4ce9e1bb82add3f59248f227c0dd20a157acf | ||||
| V: 1--2 		c15d433a517bc451df1d3b5e72371c11998657244593fe3df192d129e3b837f3 | ||||
| V: 3-14 		c2b77edc07f3af06a1e158ce55ae36ba0f3dfaa6e55f6071f7ee6b4eb381fefb | ||||
| V: 1243 		c3134402f85fe8ef5abebefb70654a0ff3bc4b7ed8d567b3f927189e33df6716 | ||||
| V: 2--1 		c55768b26d1e5a6b48442fc898fab4b2bf7777046b03cc663415c8fcf46dab84 | ||||
| V: 1342 		c6f41abafe4e8977af77d294a40b57ae3b931c716b11cfde884c7662a71b9645 | ||||
| V: 1332 		c7817348270724eb35c3a76de22498161d509359d95e4f160be337bb393f1c63 | ||||
| V: 122- 		c838f17d30d8b2b957cf49775a468cee64f3e13974577710fed81041741ff3f2 | ||||
| V: 123- 		cabfc096aa5ecf42e23a1391b3a236a50ced3da47c93a2e8bf8cdd67ef87ecab | ||||
| V: 1--- 		cae83e3c79fcee59017aa741439df1b6f8192f57e0aa527ad3ad626b049da031 | ||||
| V: 1224 		cccd5b5f7310307167c51806e0989a134c91a3a593779f4e4534afb5b62cdc2c | ||||
| V: 2134 		cd0c5ae8ab69cc44596d24a7c7300e9fdf1cb4e4f9b8f07440146c4096045440 | ||||
| V: 1324 		cf056bfbb7436ea53e6ac51517f1ec5e75736b81b98e7b87b5ecc110b643e65b | ||||
| V: 1432 		cf1bbd2e9ed2306307631135af81e850739483890ce405ab9a0a0c655b8fe738 | ||||
| V: 12-3 		d018469cb6a8ec6c80b6ff436279907e97ed5500944e9688fd62d039581e406d | ||||
| V: 123- 		d109ca02ebf28bb1f45a55b47c5ce4febbc2d74e4609528bb8e0894bc2762aef | ||||
| V: -1-2 		d1ad0078098f332d54243a6eb45897aaf24c7c8398001638c96a243cd127bd93 | ||||
| V: 1243 		d33ad007842ddb9a16a13b5b8042d267134553e700b59eef9febf109180e69d8 | ||||
| V: 1234 		d453d3a6732b845fae67430993f354acd5aa442a45f3fc57aa3a6004c45d1f10 | ||||
| V: 1342 		d46078625095fb5238c79e7015caa7e452d336458161def6a3ff0efafbd65b1e | ||||
| V: 1423 		d53e129f0e8de61631867d85c4f0e340bbc3bda995fe6f7174bf52ae3ab4a818 | ||||
| V: 2143 		d607c68d0baf141c404266193f73cf16a7b7d7ed3b4e7bd96c3f4fb1d9cdcc86 | ||||
| V: 1--- 		d6612fd623b2aa5dc1c74dcea2b25eea828695a0c929fff8a5c191801167aaa7 | ||||
| V: 143- 		d742a7a26ab23a4302d6f6a1538aea3d7a4a533bd8288f18623858d0f75d6b47 | ||||
| V: 1123 		d76800ee67faed96e04fc79fee0d277c477989055bfb583c4472ec22d1e16fef | ||||
| V: 2143 		d7eddb6bc259a081e6f260855ff203f377d520cfbb62acfa691059fb787aad16 | ||||
| V: 1243 		d808a6229a41c31a17fe17e4bf4eca2bb754ce45c825b29a8d1c5fe2a009a84b | ||||
| V: 1234 		d882c5a434b340dcfa9b4b1ab418d9248bed5ce5e3b47899b36e86de615c691c | ||||
| V: 1243 		d9cc4807186147d165e8502076dae5c0d6c34e8c81f3206b950cefd80bd06358 | ||||
| V: 1243 		db07d43c82af5e797695293a78398712c0a1015e14a74dfd438faaa69b8b9920 | ||||
| V: 2134 		db4417cafae7cf1fd8961b7d4722e116c583496183cb8b5b9ff54aaffd6d5011 | ||||
| V: 1324 		db66a3144ca4164b78d755e5e1d12c69b727c76117200c83433ca37056a64c8e | ||||
| V: 1123 		dba989d730342e8f9187a40724a9db2b51fb6d4f8829f676f2da1ff514f54b3a | ||||
| V: 1324 		ddd015d992805d78b0763837e13f6b12ed2ce98af64192cfcb0b4f7403e7ff77 | ||||
| V: 1234 		de0b883f0c62ab73d4b4cf51a02036828f3e50b828f419576d0f7de78163af9f | ||||
| V: 1243 		dee08b1cad06eed24ea5fa8296b3517c2d4e49e7509d7ccc70cee8f9baf767b6 | ||||
| V: 1234 		df7b23c1e82ab9ef09f4f160624a62bd31ed66254f384b379a7dc25197c0deb8 | ||||
| V: 1223 		df942ed4951f6e2c44063ef5bfc2948e9e5c60f8e7228ca41b47f5d2d293cf50 | ||||
| V: 231- 		e05b0c0307eb24eef7e3ad3246e3bb3a564688addcf03e26ec5b5bc89ebf8b4a | ||||
| V: 1324 		e0fe58087a6be3fba15bca5d426c758bf64482089d0ef1b7e7f88ccff26e2bab | ||||
| V: 1324 		e13eefeb5a11fa820951493620925cddb1f0b43fe32d57dc3620b0b0d0f5a965 | ||||
| V: 1243 		e2c239f9dfb960af46c10d63c0382c3b34a69324caef0f0dc198c81e40fb3d2a | ||||
| V: 1332 		e3031b38717fab10a755fda6a8c1afe8327f0cb56cb7f5bdec4f66417565d599 | ||||
| V: 1423 		e3786f46aec93a7d0d4833805fca583c6b3adc30535e7b6a6fd7f9914d091304 | ||||
| V: 231- 		e3c40e28c6ecd3496a9d0727c627ea82bb2cd473531dd343fefd20d051a21efd | ||||
| V: 1442 		e3d83ca355450bc9ef01d9ecd4cc524df6d26d3e1952a34d0e8cf6fadb0ce273 | ||||
| V: 2143 		e4f7c618fd859a049309344bce6dbb1ac82d026994da31dbcdead954343e0bb3 | ||||
| V: 1324 		e645b42cd8df792e993298820401e1dc1608bfe5aeb0c3197f1a5056312332b1 | ||||
| V: 1--- 		e6dd2e7a9026ae00224333bf733f012aa8ca9a62abe942749235bd20d5732c67 | ||||
| V: 1243 		e6ea5d80ace12a22759ab3a522df6efa2a1cd8b65e651fc6c5aa148683613b43 | ||||
| V: 2143 		e71eced655b7e055c1fa964d4f59a0a0d45469e6aedaed95ef6eeae89eb16162 | ||||
| V: 1324 		e7c84202b6389bb8a00c3a1560712ab3944a9e5ea197264ba034a79d1dcbefd6 | ||||
| V: 1332 		e9510ca600935ac70c5cf34f833bbc79b30ada9578050e878835bcffaeb711fe | ||||
| V: 2134 		e993930a9d4138d33e2d035f2cf200346b232fcc211d520d057b020b741f7241 | ||||
| V: 1234 		eb5039de58c0f0822ae1325a4098e84b2ee7d9c7dade967daea76ddf9665fa84 | ||||
| V: 2134 		eb990ffe5423e829f3c574c70ea2dd85afc54fab693c6f56a516de0cce7e73bf | ||||
| V: 2413 		ec8450663968fa79c74d718f8fd297508f0a9a61fa20986d60923029e0140245 | ||||
| V: 1342 		ed09fb58f17d81ec1445759c591947a1f3c3020cb94e1da95b1c24d3d43edf8d | ||||
| V: 4231 		ed814e84f97eeef24ed75d4e378ebe94e7989dbd556ad49e2d10969a110d8896 | ||||
| V: 122- 		ee831b442ffe9f78fabd636db19a3971fdbc6fc7492c35f8751332486e8da4b8 | ||||
| V: 132- 		eeaa12e828b2adc09d2153a9af5f3c9d65b67e7c2c9dce28a0818bbde1eb56b0 | ||||
| V: 12-3 		f0292d5ddfb3b59c6614f781096d4287e1080a42b64b7eb0cec0fd1a28c15f6c | ||||
| V: 2314 		f1038f35b8a74878b31a2fbca061b284be5ab60e685c538215f8f02ed859260a | ||||
| V: 1432 		f1b88d5505eb7886a7a6c307ca283f24bfd4ac6f347ef79b16b9446e690600a7 | ||||
| V: 132- 		f2fafb29cc8b773ff4e8eca280b80c4025e48ffbfbc35c754d865b21ab8581a3 | ||||
| V: 1324 		f39a4e391326f31e8eef0e1edcf606478b5860a7d2e68684d106966706abeae5 | ||||
| V: 1324 		f4f5af10cc6750cbe27630ee0102e6d650e8a8da75b3adc439dc86faf8e39970 | ||||
| V: 123- 		f69965dba3253f4cae3de1e910981d04e4c215af4257d58e231b9919f7cdc398 | ||||
| V: 1123 		f6fef1d957364983901f9fd70cd1784ee91c9944ce5403f6db2261f53a2449cb | ||||
| V: 1223 		f7d9f266a8860da5cae2f3f1985d0ec40ef8ba2646a7320eba6dcdf80e5ce8fc | ||||
| V: 2143 		f7f4751708682d25f8926bbb5e3225456ac39256cf37a66320890513008875bf | ||||
| V: 2143 		f9fda8051473bb5c506fa8cb3bf18019939003f85391f95252f5d3fefe613c65 | ||||
| V: 1--2 		fac6dbd7d23e398c32b281e7549f1cec2bc2afe057f22b112f2228ff70942cff | ||||
| V: 1243 		fc0d80e20c401136bf89eff2f55714179ca1d78e30eb33dee4a63258edab60f9 | ||||
| V: 1242 		fc11eaee428c4a0f42c77994c72efd96f25a79db17f250adca5b1e7847caed40 | ||||
| V: 12-- 		fce80a45bb9ec929c592dd441baa7d89ec08c6c511faead56fe35fcb454dec8c | ||||
| V: 1324 		feb645c3d70b34a37ac206a023afbbe9947fa9ef0b03695927c52a1a8499446d | ||||
| @@ -0,0 +1,115 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Election; | ||||
| use CondorcetPHP\Condorcet\Tools\Converters\DebianFormat; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class DebianFormatTest extends TestCase | ||||
| { | ||||
|     private static DebianFormat $debian2020; | ||||
|     private static DebianFormat $debian2007; | ||||
|     private static DebianFormat $debian2006; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         self::$debian2020 ?? (self::$debian2020 = new DebianFormat(__DIR__.'/DebianData/leader2020_tally.txt')); | ||||
|         self::$debian2007 ?? (self::$debian2007 = new DebianFormat(__DIR__.'/DebianData/leader2007_tally.txt')); | ||||
|         self::$debian2006 ?? (self::$debian2006 = new DebianFormat(__DIR__.'/DebianData/leader2006_tally.txt')); | ||||
|     } | ||||
|  | ||||
|     public function test2020_Implicit(): void | ||||
|     { | ||||
|         $election = self::$debian2020->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame(339, $election->countVotes()); | ||||
|         self::assertSame(1, $election->getNumberOfSeats()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'Jonathan Carter > Sruthi Chandran > Brian Gupta > None Of The Above', | ||||
|             $election->getResult('Schulze Margin')->getResultAsString() | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             unserialize('a:4:{s:15:"Jonathan Carter";a:3:{s:15:"Sruthi Chandran";i:201;s:11:"Brian Gupta";i:241;s:17:"None Of The Above";i:267;}s:15:"Sruthi Chandran";a:3:{s:15:"Jonathan Carter";i:0;s:11:"Brian Gupta";i:49;s:17:"None Of The Above";i:168;}s:11:"Brian Gupta";a:3:{s:15:"Jonathan Carter";i:0;s:15:"Sruthi Chandran";i:0;s:17:"None Of The Above";i:69;}s:17:"None Of The Above";a:3:{s:15:"Jonathan Carter";i:0;s:15:"Sruthi Chandran";i:0;s:11:"Brian Gupta";i:0;}}'), | ||||
|             $election->getResult('Schulze Margin')->getStats() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function test2020_Explicit(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|         $election->setImplicitRanking(false); | ||||
|         $election->setNumberOfSeats(1); | ||||
|  | ||||
|         self::$debian2020->setDataToAnElection($election); | ||||
|  | ||||
|         self::assertSame(339, $election->countVotes()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'Jonathan Carter > Sruthi Chandran > Brian Gupta > None Of The Above', | ||||
|             $election->getResult('Schulze Margin')->getResultAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function test2007_Implicit(): void | ||||
|     { | ||||
|         $election = self::$debian2007->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame(482, $election->countVotes()); | ||||
|         self::assertSame(1, $election->getNumberOfSeats()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'Sam Hocevar > Steve McIntyre > Raphaël Hertzog > Wouter Verhelst > Anthony Towns > Gustavo Franco > None Of The Above > Simon Richter > Aigars Mahinovs', | ||||
|             $election->getResult('Schulze Margin')->getResultAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function test2007_Explicit(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|         $election->setImplicitRanking(false); | ||||
|         $election->setNumberOfSeats(1); | ||||
|  | ||||
|         self::$debian2007->setDataToAnElection($election); | ||||
|  | ||||
|         self::assertSame(482, $election->countVotes()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'Sam Hocevar > Steve McIntyre > Raphaël Hertzog > Wouter Verhelst > Anthony Towns > Gustavo Franco > None Of The Above > Simon Richter > Aigars Mahinovs', | ||||
|             $election->getResult('Schulze Margin')->getResultAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function test2006_Implicit(): void | ||||
|     { | ||||
|         $election = self::$debian2006->setDataToAnElection(); | ||||
|  | ||||
|         self::assertSame(421, $election->countVotes()); | ||||
|         self::assertSame(1, $election->getNumberOfSeats()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'Anthony Towns > Steve McIntyre > Andreas Schuldei = Jeroen van Wolffelaar > Bill Allombert > None of the Above > Ari Pollak > Jonathan aka Ted Walther', | ||||
|             $election->getResult('Schulze Margin')->getResultAsString() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function test2006_Explicit(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|         $election->setImplicitRanking(false); | ||||
|         $election->setNumberOfSeats(1); | ||||
|  | ||||
|         self::$debian2006->setDataToAnElection($election); | ||||
|  | ||||
|         self::assertSame(421, $election->countVotes()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'Steve McIntyre > Anthony Towns > Jeroen van Wolffelaar > Andreas Schuldei > Bill Allombert > None of the Above > Ari Pollak > Jonathan aka Ted Walther', | ||||
|             $election->getResult('Schulze Margin')->getResultAsString() | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										384
									
								
								include/condorcet/Tests/src/Tools/Converters/TidemanData/A1.HIL
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								include/condorcet/Tests/src/Tools/Converters/TidemanData/A1.HIL
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,384 @@ | ||||
| 10 3 | ||||
| 1 2 9 7 0 | ||||
| 1 2 7 9 0 | ||||
| 1 2 9 7 0 | ||||
| 1 2 9 7 0 | ||||
| 1 1 2 5 0 | ||||
| 1 7 2 6 0 | ||||
| 1 9 8 7 2 3 0 | ||||
| 1 9 4 1 0 | ||||
| 1 9 10 2 6 7 8 4 5 1 0 | ||||
| 1 9 7 2 0 | ||||
| 1 9 7 8 0 | ||||
| 1 9 8 0 | ||||
| 1 9 6 7 2 3 4 5 1 8 0 | ||||
| 1 9 8 7 2 1 10 3 4 6 0 | ||||
| 1 9 8 7 0 | ||||
| 1 9 8 7 0 | ||||
| 1 9 8 7 0 | ||||
| 1 9 1 3 4 7 10 8 6 2 0 | ||||
| 1 9 7 2 1 5 0 | ||||
| 1 9 6 0 | ||||
| 1 9 8 7 0 | ||||
| 1 9 8 0 | ||||
| 1 9 10 2 3 0 | ||||
| 1 9 10 2 3 0 | ||||
| 1 9 3 4 7 0 | ||||
| 1 9 0 | ||||
| 1 9 6 7 0 | ||||
| 1 9 8 1 0 | ||||
| 1 9 7 2 6 8 4 0 | ||||
| 1 9 7 10 0 | ||||
| 1 9 1 4 3 7 8 2 10 6 0 | ||||
| 1 9 1 3 0 | ||||
| 1 9 8 6 0 | ||||
| 1 9 8 3 7 0 | ||||
| 1 9 1 3 0 | ||||
| 1 9 0 | ||||
| 1 9 1 7 3 8 0 | ||||
| 1 9 2 10 6 5 0 | ||||
| 1 9 7 0 | ||||
| 1 9 4 3 0 | ||||
| 1 9 8 4 1 3 10 0 | ||||
| 1 9 2 7 8 0 | ||||
| 1 9 8 3 6 7 2 0 | ||||
| 1 9 0 | ||||
| 1 9 8 3 6 7 2 0 | ||||
| 1 9 8 7 1 0 | ||||
| 1 9 10 7 4 1 2 6 3 5 0 | ||||
| 1 9 3 1 0 | ||||
| 1 9 10 7 1 0 | ||||
| 1 9 4 1 2 0 | ||||
| 1 9 2 7 8 3 0 | ||||
| 1 9 4 8 1 2 0 | ||||
| 1 9 7 2 10 3 1 0 | ||||
| 1 9 4 3 1 7 8 6 2 10 0 | ||||
| 1 9 4 3 1 7 8 6 2 10 0 | ||||
| 1 9 8 0 | ||||
| 1 9 1 0 | ||||
| 1 9 4 8 1 3 7 0 | ||||
| 1 9 4 1 0 | ||||
| 1 9 8 6 3 2 7 10 1 4 0 | ||||
| 1 9 8 4 0 | ||||
| 1 9 7 8 0 | ||||
| 1 9 8 4 0 | ||||
| 1 9 0 | ||||
| 1 9 1 8 3 4 0 | ||||
| 1 9 2 7 8 0 | ||||
| 1 9 2 7 3 4 5 6 8 1 0 | ||||
| 1 9 2 7 4 0 | ||||
| 1 9 2 7 8 6 10 0 | ||||
| 1 9 4 3 0 | ||||
| 1 9 7 3 1 0 | ||||
| 1 1 9 2 4 8 10 0 | ||||
| 1 1 9 10 4 8 0 | ||||
| 1 1 9 10 0 | ||||
| 1 1 9 7 4 5 2 10 8 6 0 | ||||
| 1 1 9 3 0 | ||||
| 1 1 9 7 6 3 0 | ||||
| 1 1 9 10 3 0 | ||||
| 1 1 9 4 3 8 10 0 | ||||
| 1 1 9 4 7 0 | ||||
| 1 1 9 8 2 3 7 4 10 0 | ||||
| 1 1 9 8 2 3 7 4 10 0 | ||||
| 1 1 9 3 0 | ||||
| 1 1 9 3 0 | ||||
| 1 1 9 3 0 | ||||
| 1 1 9 10 8 4 3 0 | ||||
| 1 1 9 5 3 0 | ||||
| 1 1 9 5 3 0 | ||||
| 1 1 9 5 3 0 | ||||
| 1 1 9 8 3 10 2 4 5 6 0 | ||||
| 1 1 9 10 4 0 | ||||
| 1 1 9 10 4 7 3 0 | ||||
| 1 6 9 2 0 | ||||
| 1 6 9 10 8 0 | ||||
| 1 1 5 9 2 7 10 0 | ||||
| 1 1 5 9 2 7 10 0 | ||||
| 1 1 5 9 2 7 10 0 | ||||
| 1 1 10 9 4 0 | ||||
| 1 1 10 9 0 | ||||
| 1 1 10 9 2 3 4 5 6 7 0 | ||||
| 1 1 10 9 2 0 | ||||
| 1 1 10 9 2 3 4 5 6 7 0 | ||||
| 1 10 9 7 3 2 0 | ||||
| 1 10 9 4 8 6 3 0 | ||||
| 1 10 1 9 0 | ||||
| 1 10 9 8 0 | ||||
| 1 10 9 8 0 | ||||
| 1 10 9 2 0 | ||||
| 1 1 7 9 8 3 4 6 10 5 0 | ||||
| 1 1 7 9 8 6 3 2 10 4 0 | ||||
| 1 1 7 9 3 0 | ||||
| 1 1 7 9 0 | ||||
| 1 1 7 9 0 | ||||
| 1 1 7 9 0 | ||||
| 1 1 7 9 6 2 3 0 | ||||
| 1 1 7 9 3 0 | ||||
| 1 7 6 9 0 | ||||
| 1 7 9 2 0 | ||||
| 1 7 9 8 0 | ||||
| 1 7 9 8 0 | ||||
| 1 7 1 9 4 3 8 10 0 | ||||
| 1 7 9 3 0 | ||||
| 1 7 9 3 0 | ||||
| 1 7 9 3 0 | ||||
| 1 7 9 3 0 | ||||
| 1 7 9 8 0 | ||||
| 1 7 9 6 8 0 | ||||
| 1 4 8 1 3 5 6 10 2 9 0 | ||||
| 1 4 5 7 10 1 2 8 0 | ||||
| 1 4 5 8 1 0 | ||||
| 1 7 4 2 9 8 1 3 6 10 0 | ||||
| 1 2 9 7 4 8 0 | ||||
| 1 2 4 8 0 | ||||
| 1 2 4 8 0 | ||||
| 1 2 4 8 0 | ||||
| 1 7 0 | ||||
| 1 6 7 0 | ||||
| 1 1 10 5 0 | ||||
| 1 10 1 0 | ||||
| 1 10 1 0 | ||||
| 1 10 0 | ||||
| 1 10 0 | ||||
| 1 5 10 0 | ||||
| 1 1 5 0 | ||||
| 1 1 5 0 | ||||
| 1 1 6 5 0 | ||||
| 1 1 0 | ||||
| 1 1 0 | ||||
| 1 1 0 | ||||
| 1 1 4 9 10 6 0 | ||||
| 1 1 4 10 5 0 | ||||
| 1 1 4 0 | ||||
| 1 1 4 0 | ||||
| 1 1 4 0 | ||||
| 1 1 4 5 0 | ||||
| 1 1 5 4 0 | ||||
| 1 1 2 4 0 | ||||
| 1 4 9 1 0 | ||||
| 1 4 10 1 0 | ||||
| 1 4 9 6 0 | ||||
| 1 4 9 6 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 2 7 0 | ||||
| 1 4 9 7 0 | ||||
| 1 4 1 9 0 | ||||
| 1 4 1 9 0 | ||||
| 1 4 10 5 0 | ||||
| 1 4 5 9 1 0 | ||||
| 1 4 1 5 0 | ||||
| 1 4 0 | ||||
| 1 6 7 4 10 0 | ||||
| 1 10 4 0 | ||||
| 1 10 4 1 0 | ||||
| 1 2 4 0 | ||||
| 1 1 4 3 6 10 0 | ||||
| 1 1 4 9 3 8 0 | ||||
| 1 1 4 9 3 8 0 | ||||
| 1 1 4 9 3 8 0 | ||||
| 1 1 4 3 6 8 5 0 | ||||
| 1 1 5 4 9 2 3 7 6 8 0 | ||||
| 1 1 2 4 3 0 | ||||
| 1 1 4 5 8 10 0 | ||||
| 1 1 4 8 7 0 | ||||
| 1 1 4 5 9 8 0 | ||||
| 1 1 4 8 0 | ||||
| 1 1 2 4 5 8 10 0 | ||||
| 1 8 9 2 0 | ||||
| 1 8 3 1 6 0 | ||||
| 1 8 10 3 1 2 0 | ||||
| 1 8 2 6 0 | ||||
| 1 8 2 1 3 4 0 | ||||
| 1 8 2 1 3 4 0 | ||||
| 1 8 2 0 | ||||
| 1 8 2 0 | ||||
| 1 8 2 6 0 | ||||
| 1 8 2 7 9 0 | ||||
| 1 8 3 1 6 0 | ||||
| 1 8 1 9 4 3 2 10 5 6 0 | ||||
| 1 8 2 7 3 0 | ||||
| 1 8 1 5 0 | ||||
| 1 8 7 0 | ||||
| 1 8 9 1 2 7 4 0 | ||||
| 1 8 1 7 9 0 | ||||
| 1 8 10 7 6 9 0 | ||||
| 1 8 10 9 7 6 0 | ||||
| 1 8 7 9 10 6 0 | ||||
| 1 8 1 7 9 0 | ||||
| 1 8 4 2 0 | ||||
| 1 8 7 9 0 | ||||
| 1 8 7 1 0 | ||||
| 1 8 1 3 4 2 5 10 9 7 0 | ||||
| 1 8 1 3 4 2 5 10 9 7 0 | ||||
| 1 8 2 7 9 0 | ||||
| 1 8 3 1 6 9 7 10 0 | ||||
| 1 8 3 1 6 9 7 10 0 | ||||
| 1 8 1 10 0 | ||||
| 1 8 1 10 0 | ||||
| 1 1 8 4 0 | ||||
| 1 1 8 6 4 2 3 5 10 9 0 | ||||
| 1 1 8 3 4 0 | ||||
| 1 1 8 9 3 4 2 6 5 7 0 | ||||
| 1 1 8 4 0 | ||||
| 1 1 8 9 10 0 | ||||
| 1 1 8 4 9 0 | ||||
| 1 1 8 4 9 0 | ||||
| 1 1 8 4 9 0 | ||||
| 1 1 8 3 9 0 | ||||
| 1 1 8 4 0 | ||||
| 1 1 8 4 0 | ||||
| 1 5 8 1 0 | ||||
| 1 6 8 9 0 | ||||
| 1 1 5 8 0 | ||||
| 1 1 5 8 0 | ||||
| 1 1 5 8 0 | ||||
| 1 1 5 8 0 | ||||
| 1 1 5 8 0 | ||||
| 1 10 6 1 8 3 5 0 | ||||
| 1 10 6 1 8 3 5 0 | ||||
| 1 7 1 8 9 10 0 | ||||
| 1 7 8 3 0 | ||||
| 1 10 7 8 4 5 1 2 9 6 0 | ||||
| 1 6 7 8 9 1 3 0 | ||||
| 1 6 7 8 9 1 3 0 | ||||
| 1 6 7 8 0 | ||||
| 1 6 7 8 0 | ||||
| 1 2 8 10 0 | ||||
| 1 2 8 7 9 4 0 | ||||
| 1 2 8 7 9 4 0 | ||||
| 1 2 8 0 | ||||
| 1 2 8 0 | ||||
| 1 2 8 7 0 | ||||
| 1 2 8 7 0 | ||||
| 1 2 7 8 9 0 | ||||
| 1 2 8 9 4 0 | ||||
| 1 2 7 8 9 0 | ||||
| 1 2 8 0 | ||||
| 1 2 8 0 | ||||
| 1 2 8 9 0 | ||||
| 1 2 8 0 | ||||
| 1 2 8 0 | ||||
| 1 2 9 8 0 | ||||
| 1 2 9 7 8 6 10 5 4 3 0 | ||||
| 1 2 7 9 8 10 0 | ||||
| 1 2 7 9 8 10 0 | ||||
| 1 2 9 8 0 | ||||
| 1 7 2 9 8 0 | ||||
| 1 7 2 9 6 8 3 10 5 1 0 | ||||
| 1 7 2 9 8 4 0 | ||||
| 1 4 3 6 0 | ||||
| 1 4 5 1 8 9 10 0 | ||||
| 1 4 3 8 0 | ||||
| 1 4 3 1 0 | ||||
| 1 4 3 6 0 | ||||
| 1 4 3 1 2 9 10 8 6 7 0 | ||||
| 1 4 9 2 7 3 5 10 6 1 0 | ||||
| 1 4 10 3 1 0 | ||||
| 1 4 1 3 0 | ||||
| 1 4 3 8 9 0 | ||||
| 1 4 10 9 3 1 2 5 0 | ||||
| 1 4 7 10 3 8 1 2 6 5 0 | ||||
| 1 4 3 8 1 10 0 | ||||
| 1 7 10 1 4 3 8 9 0 | ||||
| 1 7 4 1 2 3 5 6 8 9 0 | ||||
| 1 10 4 1 9 5 3 2 6 7 0 | ||||
| 1 10 4 9 1 3 2 0 | ||||
| 1 2 9 1 10 4 3 8 0 | ||||
| 1 2 4 9 7 3 8 10 6 1 0 | ||||
| 1 3 6 8 0 | ||||
| 1 3 6 8 0 | ||||
| 1 3 6 8 0 | ||||
| 1 3 6 8 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 8 0 | ||||
| 1 3 4 1 9 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 2 4 5 6 7 8 9 0 | ||||
| 1 3 1 6 0 | ||||
| 1 3 9 8 0 | ||||
| 1 3 7 1 5 0 | ||||
| 1 3 4 10 1 6 2 5 8 9 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 4 9 7 0 | ||||
| 1 3 5 8 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 9 0 | ||||
| 1 3 1 9 0 | ||||
| 1 3 1 9 0 | ||||
| 1 3 4 5 8 10 1 2 9 6 0 | ||||
| 1 3 6 0 | ||||
| 1 3 9 1 8 7 5 4 0 | ||||
| 1 3 1 5 2 4 7 6 9 8 0 | ||||
| 1 3 8 2 7 0 | ||||
| 1 1 2 3 7 8 9 5 6 10 0 | ||||
| 1 2 3 9 0 | ||||
| 1 2 3 9 10 8 0 | ||||
| 1 2 5 3 1 0 | ||||
| 1 2 3 1 4 9 10 8 5 6 0 | ||||
| 1 2 3 9 10 8 0 | ||||
| 1 1 3 9 8 4 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 4 9 5 6 10 2 8 0 | ||||
| 1 1 3 4 8 6 5 7 9 10 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 8 9 10 0 | ||||
| 1 1 3 4 0 | ||||
| 1 1 3 9 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 9 7 0 | ||||
| 1 1 3 9 7 0 | ||||
| 1 1 3 6 0 | ||||
| 1 1 3 7 8 10 9 2 6 4 0 | ||||
| 1 1 3 7 9 2 0 | ||||
| 1 1 3 2 7 9 0 | ||||
| 1 1 3 2 7 9 0 | ||||
| 1 1 3 9 0 | ||||
| 1 1 3 9 0 | ||||
| 1 1 3 9 0 | ||||
| 1 1 3 9 0 | ||||
| 1 1 3 9 7 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 9 0 | ||||
| 1 1 3 9 0 | ||||
| 1 1 3 9 0 | ||||
| 1 1 3 9 10 7 8 0 | ||||
| 1 1 3 5 9 0 | ||||
| 1 1 3 2 7 0 | ||||
| 1 1 3 2 7 0 | ||||
| 1 1 3 2 7 0 | ||||
| 1 1 3 9 7 0 | ||||
| 1 1 3 8 4 0 | ||||
| 1 1 3 4 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 5 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 9 0 | ||||
| 1 1 3 7 0 | ||||
| 1 1 3 4 9 0 | ||||
| 1 1 3 4 0 | ||||
| 1 6 3 4 1 0 | ||||
| 1 1 6 3 0 | ||||
| 1 1 10 3 0 | ||||
| 1 10 3 5 0 | ||||
| 1 10 1 3 4 2 5 6 9 8 0 | ||||
| 1 7 1 3 9 8 0 | ||||
| 1 7 1 3 9 8 0 | ||||
| 1 7 3 4 0 | ||||
| 0 | ||||
| "Candidate  1" "Candidate  2" "Candidate  3" "Candidate  4" "Candidate  5" "Candidate  6" "Candidate  7" "Candidate  8" "Candidate  9" "Candidate 10" "a1" | ||||
|  | ||||
							
								
								
									
										992
									
								
								include/condorcet/Tests/src/Tools/Converters/TidemanData/A3.HIL
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										992
									
								
								include/condorcet/Tests/src/Tools/Converters/TidemanData/A3.HIL
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,992 @@ | ||||
| 15 7 | ||||
| 1 0 | ||||
| 1 8 0 | ||||
| 1 8 0 | ||||
| 1 8 6 0 | ||||
| 1 8 6 0 | ||||
| 1 8 6 0 | ||||
| 1 8 0 | ||||
| 1 8 6 0 | ||||
| 1 8 0 | ||||
| 1 8 6 0 | ||||
| 1 8 0 | ||||
| 1 5 11 8 0 | ||||
| 1 5 11 6 8 1 0 | ||||
| 1 5 8 6 14 0 | ||||
| 1 4 11 14 0 | ||||
| 1 4 6 8 14 11 0 | ||||
| 1 4 6 11 1 13 15 14 7 5 3 2 12 8 9 0 | ||||
| 1 4 6 11 8 5 0 | ||||
| 1 4 6 0 | ||||
| 1 4 9 12 14 0 | ||||
| 1 4 8 0 | ||||
| 1 4 3 13 0 | ||||
| 1 4 6 10 7 11 14 12 0 | ||||
| 1 4 8 6 1 14 11 0 | ||||
| 1 4 11 0 | ||||
| 1 4 6 5 0 | ||||
| 1 4 6 0 | ||||
| 1 4 8 5 0 | ||||
| 1 4 8 6 0 | ||||
| 1 4 5 3 0 | ||||
| 1 4 5 3 0 | ||||
| 1 4 6 5 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 4 11 3 6 0 | ||||
| 1 4 11 6 5 14 0 | ||||
| 1 4 5 6 11 14 0 | ||||
| 1 4 13 3 5 7 10 11 14 0 | ||||
| 1 4 8 14 1 0 | ||||
| 1 4 6 1 11 0 | ||||
| 1 4 11 0 | ||||
| 1 4 5 6 7 11 14 0 | ||||
| 1 4 2 7 8 5 13 14 9 10 3 1 6 15 11 0 | ||||
| 1 4 8 3 6 11 9 14 15 2 1 5 13 7 12 0 | ||||
| 1 4 0 | ||||
| 1 4 14 11 0 | ||||
| 1 4 6 12 0 | ||||
| 1 4 6 5 2 1 13 12 11 7 3 8 9 10 14 0 | ||||
| 1 4 8 3 5 11 14 0 | ||||
| 1 4 6 5 0 | ||||
| 1 4 6 5 0 | ||||
| 1 4 5 6 8 3 11 0 | ||||
| 1 4 11 6 10 15 0 | ||||
| 1 4 5 7 11 0 | ||||
| 1 4 0 | ||||
| 1 4 6 8 0 | ||||
| 1 4 6 3 0 | ||||
| 1 4 11 8 6 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 13 3 0 | ||||
| 1 4 5 0 | ||||
| 1 4 6 11 0 | ||||
| 1 4 6 3 0 | ||||
| 1 4 6 11 8 7 1 0 | ||||
| 1 1 8 4 11 0 | ||||
| 1 4 8 6 0 | ||||
| 1 4 6 11 10 0 | ||||
| 1 4 6 7 10 14 11 3 5 8 9 12 13 15 1 0 | ||||
| 1 4 1 6 11 0 | ||||
| 1 4 8 6 5 11 14 0 | ||||
| 1 4 6 8 11 0 | ||||
| 1 4 1 6 11 2 8 0 | ||||
| 1 4 11 8 5 6 3 7 14 15 13 12 10 9 0 | ||||
| 1 4 1 5 0 | ||||
| 1 4 6 2 0 | ||||
| 1 3 1 7 13 15 0 | ||||
| 1 3 4 12 13 11 8 9 0 | ||||
| 1 3 0 | ||||
| 1 3 2 0 | ||||
| 1 3 6 4 1 0 | ||||
| 1 3 4 10 0 | ||||
| 1 3 11 6 10 14 0 | ||||
| 1 3 11 8 0 | ||||
| 1 3 8 6 0 | ||||
| 1 3 2 7 15 13 0 | ||||
| 1 3 14 5 10 13 0 | ||||
| 1 3 0 | ||||
| 1 3 6 7 0 | ||||
| 1 3 2 7 15 0 | ||||
| 1 3 13 1 7 15 2 9 10 0 | ||||
| 1 3 13 0 | ||||
| 1 3 8 0 | ||||
| 1 3 6 4 0 | ||||
| 1 3 6 11 0 | ||||
| 1 3 8 0 | ||||
| 1 3 4 10 14 0 | ||||
| 1 3 7 11 0 | ||||
| 1 3 1 0 | ||||
| 1 3 14 6 8 0 | ||||
| 1 3 0 | ||||
| 1 1 0 | ||||
| 1 1 13 2 3 7 0 | ||||
| 1 1 4 6 13 11 14 0 | ||||
| 1 1 3 10 0 | ||||
| 1 1 3 2 15 6 0 | ||||
| 1 1 5 0 | ||||
| 1 1 10 2 3 7 0 | ||||
| 1 1 13 3 0 | ||||
| 1 1 2 3 6 10 0 | ||||
| 1 4 0 | ||||
| 1 4 6 1 11 3 5 0 | ||||
| 1 4 6 11 1 3 0 | ||||
| 1 4 5 6 11 12 15 0 | ||||
| 1 4 0 | ||||
| 1 4 11 14 8 0 | ||||
| 1 4 0 | ||||
| 1 4 8 11 0 | ||||
| 1 4 6 10 0 | ||||
| 1 4 11 8 0 | ||||
| 1 4 11 6 0 | ||||
| 1 4 6 11 1 8 13 2 14 0 | ||||
| 1 4 14 11 8 0 | ||||
| 1 4 11 5 14 6 0 | ||||
| 1 4 3 1 5 0 | ||||
| 1 4 6 11 0 | ||||
| 1 4 5 6 11 0 | ||||
| 1 4 5 6 13 14 0 | ||||
| 1 4 5 11 0 | ||||
| 1 4 3 5 8 14 0 | ||||
| 1 4 5 8 3 11 0 | ||||
| 1 4 6 10 11 5 12 8 0 | ||||
| 1 4 11 8 6 5 0 | ||||
| 1 4 5 6 8 11 14 15 13 12 3 1 2 7 9 0 | ||||
| 1 4 6 11 10 13 12 14 0 | ||||
| 1 4 7 9 15 0 | ||||
| 1 4 6 10 12 0 | ||||
| 1 4 3 6 8 11 0 | ||||
| 1 4 6 11 7 10 12 14 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 6 1 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 6 5 11 0 | ||||
| 1 4 6 8 0 | ||||
| 1 4 12 6 1 3 13 0 | ||||
| 1 4 12 5 0 | ||||
| 1 4 8 6 11 0 | ||||
| 1 4 12 8 5 11 0 | ||||
| 1 4 13 5 1 11 3 8 0 | ||||
| 1 4 9 15 1 3 2 5 7 0 | ||||
| 1 4 6 11 14 0 | ||||
| 1 4 14 11 0 | ||||
| 1 4 6 11 1 7 0 | ||||
| 1 4 5 14 0 | ||||
| 1 4 8 2 7 0 | ||||
| 1 4 11 15 8 6 5 3 10 0 | ||||
| 1 4 6 8 11 14 0 | ||||
| 1 4 6 11 5 10 12 14 0 | ||||
| 1 4 5 6 11 14 12 8 7 15 3 10 0 | ||||
| 1 4 0 | ||||
| 1 4 6 11 12 14 10 8 5 13 7 15 3 2 1 0 | ||||
| 1 4 6 8 11 12 13 3 5 14 0 | ||||
| 1 4 8 1 0 | ||||
| 1 1 4 6 11 12 13 15 8 14 7 5 2 9 10 0 | ||||
| 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 0 | ||||
| 1 2 0 | ||||
| 1 2 7 9 0 | ||||
| 1 2 7 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 9 10 0 | ||||
| 1 2 1 0 | ||||
| 1 2 13 9 10 7 0 | ||||
| 1 2 1 4 0 | ||||
| 1 2 0 | ||||
| 1 2 7 1 3 10 13 0 | ||||
| 1 2 6 10 0 | ||||
| 1 2 13 6 3 8 0 | ||||
| 1 2 1 7 15 0 | ||||
| 1 2 7 0 | ||||
| 1 2 7 1 6 10 15 0 | ||||
| 1 2 7 9 13 10 0 | ||||
| 1 2 0 | ||||
| 1 2 9 6 0 | ||||
| 1 2 5 10 0 | ||||
| 1 2 5 10 0 | ||||
| 1 2 1 7 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 7 9 0 | ||||
| 1 2 10 7 9 0 | ||||
| 1 2 1 0 | ||||
| 1 2 9 10 0 | ||||
| 1 2 7 0 | ||||
| 1 2 0 | ||||
| 1 2 10 9 0 | ||||
| 1 2 8 6 0 | ||||
| 1 2 13 7 0 | ||||
| 1 2 7 12 13 9 10 6 0 | ||||
| 1 2 7 10 9 8 0 | ||||
| 1 2 7 9 0 | ||||
| 1 4 8 12 0 | ||||
| 1 4 6 8 13 15 5 10 0 | ||||
| 1 4 6 11 13 0 | ||||
| 1 4 11 12 0 | ||||
| 1 4 6 1 8 0 | ||||
| 1 4 8 11 0 | ||||
| 1 4 6 8 1 0 | ||||
| 1 4 6 7 5 11 0 | ||||
| 1 4 1 6 9 11 3 5 2 7 10 12 14 15 13 0 | ||||
| 1 4 8 11 14 12 13 3 0 | ||||
| 1 4 8 14 3 0 | ||||
| 1 4 6 8 0 | ||||
| 1 8 5 0 | ||||
| 1 8 4 12 14 15 3 6 5 1 2 7 9 10 11 0 | ||||
| 1 8 9 5 11 0 | ||||
| 1 8 5 6 0 | ||||
| 1 8 11 5 0 | ||||
| 1 8 11 0 | ||||
| 1 8 5 2 6 7 9 10 0 | ||||
| 1 8 9 5 0 | ||||
| 1 8 12 0 | ||||
| 1 8 4 5 11 14 0 | ||||
| 1 8 5 0 | ||||
| 1 8 5 6 11 0 | ||||
| 1 8 6 11 9 0 | ||||
| 1 8 2 7 10 9 0 | ||||
| 1 1 14 15 11 0 | ||||
| 1 1 7 15 0 | ||||
| 1 1 3 13 0 | ||||
| 1 1 6 2 15 11 7 3 10 0 | ||||
| 1 1 3 5 13 7 11 0 | ||||
| 1 1 6 0 | ||||
| 1 1 7 0 | ||||
| 1 1 3 7 0 | ||||
| 1 1 4 6 0 | ||||
| 1 1 6 8 9 15 0 | ||||
| 1 1 9 10 13 3 5 0 | ||||
| 1 1 8 5 0 | ||||
| 1 1 6 4 11 0 | ||||
| 1 1 13 3 0 | ||||
| 1 1 0 | ||||
| 1 1 6 0 | ||||
| 1 1 2 3 7 8 15 10 13 0 | ||||
| 1 4 6 12 13 0 | ||||
| 1 1 9 2 3 6 5 0 | ||||
| 1 1 3 14 0 | ||||
| 1 1 2 7 0 | ||||
| 1 1 0 | ||||
| 1 1 11 13 0 | ||||
| 1 1 2 7 0 | ||||
| 1 1 15 4 6 7 8 3 2 9 10 13 11 14 12 0 | ||||
| 1 1 3 13 0 | ||||
| 1 1 3 13 4 0 | ||||
| 1 1 2 15 9 0 | ||||
| 1 1 3 15 0 | ||||
| 1 1 13 5 4 6 0 | ||||
| 1 1 4 5 13 0 | ||||
| 1 1 4 6 0 | ||||
| 1 1 8 12 0 | ||||
| 1 1 10 2 8 0 | ||||
| 1 1 3 13 0 | ||||
| 1 1 3 13 0 | ||||
| 1 8 11 2 0 | ||||
| 1 8 5 4 6 1 11 14 7 10 3 9 15 0 | ||||
| 1 8 5 0 | ||||
| 1 8 3 13 10 0 | ||||
| 1 8 11 5 9 6 0 | ||||
| 1 8 7 11 0 | ||||
| 1 2 0 | ||||
| 1 2 7 10 9 13 3 8 6 0 | ||||
| 1 2 6 7 10 9 0 | ||||
| 1 2 6 0 | ||||
| 1 2 3 1 7 8 9 13 5 12 15 10 14 4 11 0 | ||||
| 1 2 3 5 11 0 | ||||
| 1 2 10 9 0 | ||||
| 1 2 6 7 10 0 | ||||
| 1 2 7 9 1 3 0 | ||||
| 1 2 6 7 10 1 13 3 0 | ||||
| 1 2 1 4 13 0 | ||||
| 1 2 7 0 | ||||
| 1 2 10 6 0 | ||||
| 1 2 7 0 | ||||
| 1 2 13 0 | ||||
| 1 2 8 15 11 0 | ||||
| 1 2 0 | ||||
| 1 2 3 1 13 0 | ||||
| 1 2 1 10 0 | ||||
| 1 2 8 0 | ||||
| 1 2 0 | ||||
| 1 2 7 5 1 3 10 9 0 | ||||
| 1 2 1 0 | ||||
| 1 2 7 0 | ||||
| 1 8 12 0 | ||||
| 1 8 5 9 11 0 | ||||
| 1 8 2 3 6 0 | ||||
| 1 8 3 10 13 1 0 | ||||
| 1 8 11 0 | ||||
| 1 8 5 11 9 0 | ||||
| 1 8 14 5 0 | ||||
| 1 8 11 5 0 | ||||
| 1 8 7 6 0 | ||||
| 1 8 14 4 0 | ||||
| 1 8 6 15 0 | ||||
| 1 8 4 3 6 1 2 5 7 13 11 10 15 14 9 0 | ||||
| 1 8 3 7 0 | ||||
| 1 8 12 3 0 | ||||
| 1 8 11 5 9 0 | ||||
| 1 8 11 10 0 | ||||
| 1 8 14 10 0 | ||||
| 1 8 5 9 3 11 0 | ||||
| 1 8 12 0 | ||||
| 1 8 12 6 0 | ||||
| 1 5 6 8 0 | ||||
| 1 5 6 4 11 8 0 | ||||
| 1 5 8 0 | ||||
| 1 5 11 15 14 8 0 | ||||
| 1 5 8 7 10 3 11 0 | ||||
| 1 5 11 8 0 | ||||
| 1 5 9 11 0 | ||||
| 1 5 8 11 2 6 14 0 | ||||
| 1 5 6 0 | ||||
| 1 5 8 12 11 0 | ||||
| 1 5 6 15 8 11 0 | ||||
| 1 5 4 6 11 13 1 3 8 0 | ||||
| 1 5 11 4 0 | ||||
| 1 5 4 3 0 | ||||
| 1 5 14 4 0 | ||||
| 1 5 8 0 | ||||
| 1 5 8 1 0 | ||||
| 1 5 8 1 11 12 13 9 2 3 14 7 10 4 6 0 | ||||
| 1 5 6 11 0 | ||||
| 1 5 11 4 0 | ||||
| 1 5 8 11 0 | ||||
| 1 8 14 0 | ||||
| 1 8 6 4 0 | ||||
| 1 8 14 0 | ||||
| 1 8 5 11 0 | ||||
| 1 8 14 0 | ||||
| 1 8 6 11 0 | ||||
| 1 8 5 14 0 | ||||
| 1 8 11 5 0 | ||||
| 1 8 5 6 11 0 | ||||
| 1 8 11 14 12 0 | ||||
| 1 8 11 0 | ||||
| 1 8 6 12 0 | ||||
| 1 8 6 14 0 | ||||
| 1 8 6 14 0 | ||||
| 1 8 5 6 4 11 12 10 13 0 | ||||
| 1 8 7 2 1 15 11 0 | ||||
| 1 8 3 12 0 | ||||
| 1 8 5 3 4 9 11 14 0 | ||||
| 1 8 6 5 0 | ||||
| 1 8 11 3 14 0 | ||||
| 1 8 11 9 5 0 | ||||
| 1 8 5 9 11 0 | ||||
| 1 8 11 0 | ||||
| 1 8 5 11 9 6 0 | ||||
| 1 8 11 6 12 0 | ||||
| 1 8 11 5 4 6 9 14 15 0 | ||||
| 1 8 4 11 5 6 12 14 15 9 0 | ||||
| 1 8 6 4 3 0 | ||||
| 1 8 6 5 0 | ||||
| 1 5 12 9 2 1 15 8 6 7 13 10 3 14 4 0 | ||||
| 1 5 11 15 0 | ||||
| 1 5 0 | ||||
| 1 5 11 0 | ||||
| 1 5 8 11 14 7 6 0 | ||||
| 1 5 8 14 1 11 0 | ||||
| 1 5 4 11 15 0 | ||||
| 1 5 0 | ||||
| 1 5 7 11 0 | ||||
| 1 5 6 1 7 8 15 13 11 12 4 3 9 14 2 0 | ||||
| 1 5 6 8 0 | ||||
| 1 5 0 | ||||
| 1 5 14 4 6 15 8 11 12 3 0 | ||||
| 1 5 11 0 | ||||
| 1 5 7 8 11 14 13 4 1 15 12 6 3 9 10 0 | ||||
| 1 5 11 6 0 | ||||
| 1 5 6 11 0 | ||||
| 1 5 6 3 11 12 0 | ||||
| 1 5 0 | ||||
| 1 5 6 14 0 | ||||
| 1 5 6 11 12 0 | ||||
| 1 5 11 8 14 0 | ||||
| 1 5 6 11 4 14 8 0 | ||||
| 1 5 14 8 0 | ||||
| 1 5 8 11 12 0 | ||||
| 1 5 6 14 8 7 0 | ||||
| 1 5 8 11 13 6 15 1 7 3 0 | ||||
| 1 6 9 7 13 15 14 8 0 | ||||
| 1 5 4 8 1 0 | ||||
| 1 5 7 2 0 | ||||
| 1 5 6 14 11 12 8 0 | ||||
| 1 5 11 4 13 6 0 | ||||
| 1 5 8 9 0 | ||||
| 1 5 6 8 0 | ||||
| 1 5 6 14 0 | ||||
| 1 5 11 8 6 1 7 2 4 3 9 10 14 15 13 0 | ||||
| 1 5 11 8 4 3 0 | ||||
| 1 5 6 8 14 0 | ||||
| 1 5 11 6 0 | ||||
| 1 5 11 8 4 6 0 | ||||
| 1 5 8 13 0 | ||||
| 1 5 8 11 6 12 0 | ||||
| 1 8 5 9 11 0 | ||||
| 1 8 5 11 13 6 1 0 | ||||
| 1 8 11 5 9 0 | ||||
| 1 8 1 4 0 | ||||
| 1 8 14 11 0 | ||||
| 1 8 5 11 0 | ||||
| 1 8 5 11 12 0 | ||||
| 1 8 9 11 5 6 0 | ||||
| 1 8 6 11 14 0 | ||||
| 1 7 6 2 5 4 8 14 0 | ||||
| 1 7 6 12 8 0 | ||||
| 1 7 13 6 0 | ||||
| 1 7 10 8 0 | ||||
| 1 8 1 3 0 | ||||
| 1 8 4 6 14 15 0 | ||||
| 1 8 6 14 0 | ||||
| 1 8 6 11 0 | ||||
| 1 8 6 11 0 | ||||
| 1 8 11 14 0 | ||||
| 1 8 6 5 9 0 | ||||
| 1 8 11 0 | ||||
| 1 8 6 0 | ||||
| 1 8 5 11 14 0 | ||||
| 1 8 0 | ||||
| 1 8 5 9 6 4 14 0 | ||||
| 1 8 11 0 | ||||
| 1 8 5 11 9 0 | ||||
| 1 8 0 | ||||
| 1 8 11 0 | ||||
| 1 8 1 6 0 | ||||
| 1 8 11 6 0 | ||||
| 1 8 5 11 4 12 14 0 | ||||
| 1 8 9 12 0 | ||||
| 1 8 5 11 9 0 | ||||
| 1 8 0 | ||||
| 1 8 11 4 0 | ||||
| 1 8 10 12 0 | ||||
| 1 8 6 5 14 0 | ||||
| 1 7 4 8 6 5 0 | ||||
| 1 7 10 6 0 | ||||
| 1 7 10 6 0 | ||||
| 1 7 10 4 2 13 3 0 | ||||
| 1 7 2 8 10 0 | ||||
| 1 7 0 | ||||
| 1 7 8 12 0 | ||||
| 1 7 11 14 0 | ||||
| 1 7 0 | ||||
| 1 7 1 4 0 | ||||
| 1 7 6 0 | ||||
| 1 7 5 12 0 | ||||
| 1 7 8 15 0 | ||||
| 1 7 4 3 1 6 13 14 15 5 2 8 9 10 12 0 | ||||
| 1 7 6 10 11 1 0 | ||||
| 1 7 1 3 2 6 13 15 0 | ||||
| 1 7 11 12 3 4 6 5 8 0 | ||||
| 1 10 2 6 0 | ||||
| 1 10 14 6 11 4 0 | ||||
| 1 10 14 7 6 4 12 11 0 | ||||
| 1 10 0 | ||||
| 1 10 0 | ||||
| 1 10 9 11 0 | ||||
| 1 10 2 7 9 0 | ||||
| 1 10 8 7 0 | ||||
| 1 10 5 6 0 | ||||
| 1 10 14 6 0 | ||||
| 1 10 7 2 0 | ||||
| 1 10 11 14 0 | ||||
| 1 9 8 6 4 0 | ||||
| 1 9 14 8 0 | ||||
| 1 9 12 6 8 2 0 | ||||
| 1 9 10 12 6 14 13 1 0 | ||||
| 1 9 0 | ||||
| 1 9 10 2 0 | ||||
| 1 9 0 | ||||
| 1 9 10 8 2 0 | ||||
| 1 9 8 12 0 | ||||
| 1 9 0 | ||||
| 1 9 10 12 6 14 13 1 0 | ||||
| 1 9 8 11 14 3 4 10 0 | ||||
| 1 9 8 11 14 3 4 0 | ||||
| 1 9 8 11 13 3 4 10 0 | ||||
| 1 9 6 12 14 8 0 | ||||
| 1 9 12 6 0 | ||||
| 1 9 6 12 0 | ||||
| 1 7 2 3 1 13 15 0 | ||||
| 1 7 1 2 0 | ||||
| 1 7 15 3 1 2 0 | ||||
| 1 7 6 1 0 | ||||
| 1 7 4 2 0 | ||||
| 1 7 1 3 15 13 6 2 0 | ||||
| 1 7 10 6 4 0 | ||||
| 1 7 15 3 0 | ||||
| 1 7 15 1 3 13 0 | ||||
| 1 7 4 6 10 15 14 11 13 8 3 12 1 5 2 0 | ||||
| 1 7 9 10 0 | ||||
| 1 7 8 2 13 6 3 0 | ||||
| 1 7 2 9 0 | ||||
| 1 7 0 | ||||
| 1 7 1 2 3 10 9 0 | ||||
| 1 7 15 5 0 | ||||
| 1 7 2 6 0 | ||||
| 1 7 2 0 | ||||
| 1 7 2 13 0 | ||||
| 1 7 8 5 0 | ||||
| 1 7 5 8 0 | ||||
| 1 7 1 2 8 3 0 | ||||
| 1 11 6 5 8 0 | ||||
| 1 11 7 15 0 | ||||
| 1 11 13 12 6 14 0 | ||||
| 1 11 15 6 0 | ||||
| 1 11 9 7 4 3 14 12 0 | ||||
| 1 11 7 0 | ||||
| 1 11 4 1 0 | ||||
| 1 11 5 3 0 | ||||
| 1 11 8 6 0 | ||||
| 1 11 10 7 2 6 14 15 12 13 9 1 8 5 3 0 | ||||
| 1 11 4 6 0 | ||||
| 1 11 4 5 12 0 | ||||
| 1 11 6 8 0 | ||||
| 1 11 8 12 0 | ||||
| 1 11 5 6 4 8 3 13 14 0 | ||||
| 1 11 6 5 14 0 | ||||
| 1 11 6 8 3 4 13 14 15 12 7 2 1 9 10 0 | ||||
| 1 11 12 14 0 | ||||
| 1 11 8 0 | ||||
| 1 11 14 6 5 8 0 | ||||
| 1 11 8 10 0 | ||||
| 1 11 5 12 14 0 | ||||
| 1 11 6 10 0 | ||||
| 1 11 4 14 7 8 3 0 | ||||
| 1 11 6 5 0 | ||||
| 1 11 4 14 7 8 3 0 | ||||
| 1 11 6 5 0 | ||||
| 1 11 6 4 0 | ||||
| 1 11 6 13 12 8 14 1 0 | ||||
| 1 11 6 5 8 0 | ||||
| 1 11 14 4 0 | ||||
| 1 11 5 6 8 0 | ||||
| 1 11 6 4 0 | ||||
| 1 11 5 6 0 | ||||
| 1 11 4 6 1 7 10 5 14 0 | ||||
| 1 11 8 6 10 12 0 | ||||
| 1 11 4 6 0 | ||||
| 1 11 13 12 2 14 0 | ||||
| 1 11 14 6 0 | ||||
| 1 11 6 12 0 | ||||
| 1 11 8 3 5 4 6 0 | ||||
| 1 11 6 4 0 | ||||
| 1 11 14 6 0 | ||||
| 1 11 4 8 0 | ||||
| 1 11 6 0 | ||||
| 1 11 4 14 6 0 | ||||
| 1 11 14 5 0 | ||||
| 1 11 8 0 | ||||
| 1 11 8 15 14 3 6 0 | ||||
| 1 10 14 15 11 9 4 5 6 7 12 13 0 | ||||
| 1 10 6 4 11 12 0 | ||||
| 1 10 14 4 5 7 6 8 11 0 | ||||
| 1 11 7 0 | ||||
| 1 11 5 0 | ||||
| 1 11 2 6 0 | ||||
| 1 11 0 | ||||
| 1 11 8 4 3 5 0 | ||||
| 1 11 1 4 2 0 | ||||
| 1 11 5 6 14 0 | ||||
| 1 11 4 6 3 14 0 | ||||
| 1 11 4 13 14 12 0 | ||||
| 1 11 4 8 0 | ||||
| 1 11 14 6 5 8 4 0 | ||||
| 1 11 0 | ||||
| 1 11 14 6 12 0 | ||||
| 1 11 0 | ||||
| 1 11 12 14 0 | ||||
| 1 11 8 5 0 | ||||
| 1 11 15 14 12 8 6 0 | ||||
| 1 11 4 5 6 14 3 12 0 | ||||
| 1 11 6 12 14 1 3 4 7 13 0 | ||||
| 1 11 3 8 0 | ||||
| 1 11 14 0 | ||||
| 1 11 4 6 8 0 | ||||
| 1 11 3 5 0 | ||||
| 1 11 12 8 6 0 | ||||
| 1 11 5 14 4 0 | ||||
| 1 11 5 8 0 | ||||
| 1 11 5 6 14 0 | ||||
| 1 11 12 6 0 | ||||
| 1 11 6 7 14 0 | ||||
| 1 11 8 5 6 14 12 1 4 0 | ||||
| 1 11 14 8 6 3 10 12 0 | ||||
| 1 11 5 4 6 0 | ||||
| 1 11 6 14 0 | ||||
| 1 11 8 2 0 | ||||
| 1 11 14 6 0 | ||||
| 1 11 8 6 0 | ||||
| 1 11 5 8 3 4 14 0 | ||||
| 1 11 12 5 6 15 0 | ||||
| 1 11 8 5 6 0 | ||||
| 1 11 6 12 0 | ||||
| 1 11 5 3 0 | ||||
| 1 11 5 6 0 | ||||
| 1 11 0 | ||||
| 1 11 6 4 8 0 | ||||
| 1 11 0 | ||||
| 1 11 14 8 0 | ||||
| 1 11 14 9 0 | ||||
| 1 11 6 8 3 4 5 13 14 0 | ||||
| 1 11 6 4 3 0 | ||||
| 1 11 1 4 7 5 8 9 12 10 3 2 14 15 6 0 | ||||
| 1 3 9 13 0 | ||||
| 1 0 | ||||
| 1 5 0 | ||||
| 1 6 0 | ||||
| 1 0 | ||||
| 1 0 | ||||
| 1 8 5 11 12 0 | ||||
| 1 8 4 0 | ||||
| 1 8 12 0 | ||||
| 1 8 6 14 0 | ||||
| 1 8 11 6 14 0 | ||||
| 1 8 11 4 14 5 9 3 0 | ||||
| 1 8 1 0 | ||||
| 1 8 5 4 0 | ||||
| 1 8 11 10 4 0 | ||||
| 1 8 9 11 5 0 | ||||
| 1 8 5 11 9 0 | ||||
| 1 8 14 13 6 0 | ||||
| 1 8 0 | ||||
| 1 8 6 5 14 11 12 15 0 | ||||
| 1 12 14 0 | ||||
| 1 12 8 6 0 | ||||
| 1 12 0 | ||||
| 1 12 3 4 6 13 0 | ||||
| 1 12 0 | ||||
| 1 11 9 0 | ||||
| 1 12 6 1 0 | ||||
| 1 12 11 8 6 4 0 | ||||
| 1 12 8 13 0 | ||||
| 1 12 14 0 | ||||
| 1 12 8 0 | ||||
| 1 12 6 1 8 0 | ||||
| 1 12 0 | ||||
| 1 12 6 5 4 11 7 10 13 8 9 14 15 0 | ||||
| 1 8 6 12 0 | ||||
| 1 8 9 11 0 | ||||
| 1 8 5 11 9 0 | ||||
| 1 8 11 5 0 | ||||
| 1 8 4 5 0 | ||||
| 1 8 11 0 | ||||
| 1 8 0 | ||||
| 1 8 11 14 0 | ||||
| 1 8 11 5 9 0 | ||||
| 1 8 6 14 3 4 0 | ||||
| 1 8 12 0 | ||||
| 1 8 5 12 0 | ||||
| 1 8 5 11 12 6 4 0 | ||||
| 1 8 11 5 6 0 | ||||
| 1 8 5 4 11 6 0 | ||||
| 1 8 0 | ||||
| 1 8 11 9 5 0 | ||||
| 1 8 11 6 5 14 3 15 12 0 | ||||
| 1 8 7 1 0 | ||||
| 1 8 13 15 0 | ||||
| 1 8 6 12 0 | ||||
| 1 8 4 11 0 | ||||
| 1 8 0 | ||||
| 1 8 12 15 6 14 0 | ||||
| 1 8 6 2 0 | ||||
| 1 8 11 5 0 | ||||
| 1 8 6 14 12 0 | ||||
| 1 8 6 1 0 | ||||
| 1 8 9 12 0 | ||||
| 1 8 5 12 4 6 0 | ||||
| 1 8 5 11 9 0 | ||||
| 1 8 0 | ||||
| 1 8 11 6 3 4 14 0 | ||||
| 1 8 14 5 0 | ||||
| 1 8 6 4 11 5 1 15 14 13 12 10 9 2 3 0 | ||||
| 1 8 6 3 11 14 0 | ||||
| 1 0 | ||||
| 1 0 | ||||
| 1 0 | ||||
| 1 0 | ||||
| 1 14 1 11 6 8 10 0 | ||||
| 1 14 4 6 0 | ||||
| 1 14 6 8 0 | ||||
| 1 14 6 11 15 8 7 4 12 5 13 9 0 | ||||
| 1 14 12 8 6 1 10 0 | ||||
| 1 14 6 8 0 | ||||
| 1 14 6 10 0 | ||||
| 1 14 11 6 0 | ||||
| 1 14 5 0 | ||||
| 1 14 12 0 | ||||
| 1 15 7 2 0 | ||||
| 1 15 3 13 7 10 0 | ||||
| 1 15 12 6 0 | ||||
| 1 15 7 14 0 | ||||
| 1 15 12 11 0 | ||||
| 1 15 11 4 2 6 8 14 13 12 1 3 5 7 10 0 | ||||
| 1 15 14 0 | ||||
| 1 15 8 6 0 | ||||
| 1 15 3 2 1 0 | ||||
| 1 15 11 0 | ||||
| 1 15 7 0 | ||||
| 1 15 6 7 0 | ||||
| 1 15 14 11 3 4 0 | ||||
| 1 15 7 2 11 10 0 | ||||
| 1 13 11 1 6 8 5 10 0 | ||||
| 1 13 8 0 | ||||
| 1 13 1 3 7 15 2 0 | ||||
| 1 13 3 7 15 1 9 0 | ||||
| 1 13 1 6 0 | ||||
| 1 13 12 4 0 | ||||
| 1 13 6 3 0 | ||||
| 1 13 7 6 0 | ||||
| 1 13 11 6 5 0 | ||||
| 1 13 11 6 15 0 | ||||
| 1 13 8 4 6 1 0 | ||||
| 1 12 2 6 0 | ||||
| 1 12 5 8 4 0 | ||||
| 1 12 9 8 0 | ||||
| 1 12 11 5 0 | ||||
| 1 12 8 0 | ||||
| 1 12 0 | ||||
| 1 12 9 0 | ||||
| 1 12 8 6 11 5 0 | ||||
| 1 12 6 0 | ||||
| 1 12 6 8 0 | ||||
| 1 12 14 6 0 | ||||
| 1 12 8 4 0 | ||||
| 1 12 6 3 0 | ||||
| 1 12 3 8 0 | ||||
| 1 12 4 13 0 | ||||
| 1 6 11 0 | ||||
| 1 6 5 4 13 0 | ||||
| 1 6 0 | ||||
| 1 6 1 3 0 | ||||
| 1 6 10 14 8 0 | ||||
| 1 6 5 11 7 4 10 12 13 8 9 14 15 0 | ||||
| 1 6 13 12 11 9 15 0 | ||||
| 1 6 8 12 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 4 11 10 5 12 13 14 15 7 9 8 0 | ||||
| 1 6 8 14 0 | ||||
| 1 6 13 4 3 5 0 | ||||
| 1 14 6 8 11 4 3 0 | ||||
| 1 14 8 11 3 4 6 0 | ||||
| 1 14 4 0 | ||||
| 1 14 0 | ||||
| 1 14 0 | ||||
| 1 14 8 10 0 | ||||
| 1 14 0 | ||||
| 1 14 5 11 0 | ||||
| 1 14 6 11 0 | ||||
| 1 14 6 8 5 4 11 3 12 0 | ||||
| 1 14 11 4 5 13 0 | ||||
| 1 14 8 10 6 0 | ||||
| 1 14 11 4 0 | ||||
| 1 14 6 4 3 0 | ||||
| 1 14 4 5 0 | ||||
| 1 14 5 0 | ||||
| 1 14 11 6 0 | ||||
| 1 14 0 | ||||
| 1 14 6 7 0 | ||||
| 1 14 8 0 | ||||
| 1 14 3 8 10 13 0 | ||||
| 1 14 4 6 0 | ||||
| 1 14 10 8 0 | ||||
| 1 14 6 10 0 | ||||
| 1 14 15 6 11 10 5 0 | ||||
| 1 14 0 | ||||
| 1 14 10 11 0 | ||||
| 1 14 11 8 12 0 | ||||
| 1 14 8 6 2 0 | ||||
| 1 14 6 4 11 10 5 8 1 0 | ||||
| 1 14 12 8 11 10 0 | ||||
| 1 14 8 0 | ||||
| 1 14 8 0 | ||||
| 1 14 11 4 7 2 0 | ||||
| 1 14 11 4 5 8 0 | ||||
| 1 14 12 15 4 8 7 13 10 1 3 6 5 2 11 0 | ||||
| 1 14 0 | ||||
| 1 14 11 12 0 | ||||
| 1 14 6 0 | ||||
| 1 6 4 10 14 7 5 8 11 0 | ||||
| 1 6 0 | ||||
| 1 6 12 8 3 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 11 4 14 12 0 | ||||
| 1 6 1 7 5 0 | ||||
| 1 6 8 4 0 | ||||
| 1 6 5 11 0 | ||||
| 1 6 7 10 3 12 2 1 0 | ||||
| 1 6 2 5 0 | ||||
| 1 6 5 11 2 10 3 7 13 4 1 0 | ||||
| 1 6 8 9 10 0 | ||||
| 1 6 11 4 10 14 5 0 | ||||
| 1 6 11 1 0 | ||||
| 1 6 13 0 | ||||
| 1 6 8 11 0 | ||||
| 1 6 5 8 11 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 8 15 0 | ||||
| 1 6 11 2 1 13 3 0 | ||||
| 1 6 8 0 | ||||
| 1 6 10 2 11 7 14 12 13 0 | ||||
| 1 6 4 5 11 14 8 15 12 0 | ||||
| 1 6 12 0 | ||||
| 1 6 11 2 13 15 8 0 | ||||
| 1 6 5 11 8 4 0 | ||||
| 1 6 0 | ||||
| 1 6 3 5 9 10 4 7 8 11 13 15 14 2 12 0 | ||||
| 1 6 8 0 | ||||
| 1 6 8 14 0 | ||||
| 1 6 3 8 0 | ||||
| 1 6 8 14 0 | ||||
| 1 6 5 11 12 14 13 4 15 0 | ||||
| 1 6 10 2 9 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 4 8 0 | ||||
| 1 6 5 8 11 14 0 | ||||
| 1 6 8 4 14 11 0 | ||||
| 1 6 8 11 0 | ||||
| 1 6 2 3 5 4 8 1 7 13 0 | ||||
| 1 6 4 12 0 | ||||
| 1 6 4 8 11 12 13 10 5 7 14 15 1 9 2 0 | ||||
| 1 6 3 4 0 | ||||
| 1 6 8 11 0 | ||||
| 1 6 11 4 0 | ||||
| 1 6 4 11 0 | ||||
| 1 6 11 3 12 7 0 | ||||
| 1 6 11 13 4 14 8 0 | ||||
| 1 6 4 8 12 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 8 11 4 3 13 14 12 15 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 4 3 8 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 11 15 0 | ||||
| 1 6 12 0 | ||||
| 1 6 8 4 3 11 14 0 | ||||
| 1 6 8 4 14 12 5 11 15 9 7 0 | ||||
| 1 6 12 8 4 11 14 0 | ||||
| 1 6 0 | ||||
| 1 13 6 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 11 15 0 | ||||
| 1 6 5 8 9 12 11 13 0 | ||||
| 1 6 8 12 0 | ||||
| 1 6 4 8 11 14 0 | ||||
| 1 6 11 14 0 | ||||
| 1 6 4 3 1 13 0 | ||||
| 1 6 5 4 11 7 3 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 2 13 15 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 11 1 3 4 8 15 0 | ||||
| 1 6 5 8 4 11 0 | ||||
| 1 6 8 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 11 14 0 | ||||
| 1 6 4 8 5 11 12 14 15 0 | ||||
| 1 6 4 1 13 0 | ||||
| 1 6 4 2 7 11 13 14 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 10 2 0 | ||||
| 1 6 8 14 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 4 8 0 | ||||
| 1 6 11 5 15 0 | ||||
| 1 6 5 4 14 7 10 12 13 8 9 11 15 0 | ||||
| 1 6 5 4 14 7 10 12 13 8 9 11 15 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 8 14 0 | ||||
| 1 6 8 4 11 0 | ||||
| 1 6 4 12 11 13 0 | ||||
| 1 6 5 4 11 8 12 0 | ||||
| 1 6 5 8 4 0 | ||||
| 1 6 5 4 8 11 0 | ||||
| 1 6 4 8 7 5 3 14 15 0 | ||||
| 1 6 4 14 11 0 | ||||
| 1 6 4 12 0 | ||||
| 1 6 11 0 | ||||
| 1 6 11 0 | ||||
| 1 6 8 14 4 0 | ||||
| 1 6 14 11 1 0 | ||||
| 1 6 5 3 4 8 13 0 | ||||
| 1 6 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 0 | ||||
| 1 6 8 12 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 14 12 10 8 9 11 1 4 13 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 5 8 0 | ||||
| 1 6 11 14 0 | ||||
| 1 6 11 5 0 | ||||
| 1 6 4 3 1 7 8 5 9 14 12 10 11 0 | ||||
| 1 6 4 13 14 0 | ||||
| 1 6 8 12 0 | ||||
| 1 6 4 11 7 0 | ||||
| 1 6 5 8 9 3 2 0 | ||||
| 1 6 8 12 11 0 | ||||
| 1 6 8 1 0 | ||||
| 1 6 14 0 | ||||
| 1 6 8 14 0 | ||||
| 1 6 4 11 5 12 8 10 0 | ||||
| 1 6 14 9 0 | ||||
| 1 6 3 10 4 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 4 3 0 | ||||
| 1 6 0 | ||||
| 1 6 7 11 10 2 13 3 1 9 0 | ||||
| 1 6 5 8 3 13 7 15 14 12 1 11 9 10 4 0 | ||||
| 1 6 11 12 0 | ||||
| 1 6 7 2 10 9 0 | ||||
| 1 6 9 10 3 4 1 7 11 13 8 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 5 0 | ||||
| 1 6 5 4 11 7 10 12 13 14 9 8 15 0 | ||||
| 1 6 11 8 12 5 4 1 13 14 0 | ||||
| 1 6 5 4 8 9 10 11 0 | ||||
| 1 6 3 5 0 | ||||
| 1 6 11 14 0 | ||||
| 1 6 0 | ||||
| 1 6 8 4 0 | ||||
| 1 6 4 11 12 8 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 8 4 11 13 0 | ||||
| 1 6 5 3 8 13 9 14 10 7 1 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 4 8 11 5 0 | ||||
| 1 6 5 11 0 | ||||
| 1 6 5 8 11 14 15 9 4 0 | ||||
| 1 6 1 7 0 | ||||
| 1 6 7 2 9 10 15 1 3 12 14 13 4 11 5 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 4 10 14 7 5 8 11 0 | ||||
| 1 6 5 8 11 0 | ||||
| 1 6 8 4 0 | ||||
| 1 6 13 5 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 4 8 0 | ||||
| 1 6 4 10 11 7 0 | ||||
| 1 6 4 8 11 7 5 14 10 3 0 | ||||
| 1 6 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 1 8 2 5 3 4 7 9 12 15 13 10 11 0 | ||||
| 1 6 8 9 5 11 0 | ||||
| 1 6 7 3 4 0 | ||||
| 1 6 8 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 11 5 3 14 0 | ||||
| 1 6 4 11 7 10 12 13 8 9 14 0 | ||||
| 1 6 4 8 12 11 14 3 5 13 0 | ||||
| 1 6 4 8 12 11 14 3 5 13 0 | ||||
| 1 6 4 11 5 12 14 15 13 0 | ||||
| 1 6 4 11 0 | ||||
| 1 6 8 0 | ||||
| 1 6 13 12 11 0 | ||||
| 1 6 7 10 12 13 3 4 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 14 8 0 | ||||
| 1 6 7 4 3 5 2 14 8 1 15 0 | ||||
| 1 6 4 11 3 12 13 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 14 11 0 | ||||
| 1 6 3 5 0 | ||||
| 1 6 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 11 12 0 | ||||
| 1 6 12 8 0 | ||||
| 1 6 2 0 | ||||
| 1 6 1 0 | ||||
| 1 6 0 | ||||
| 1 6 5 4 11 7 10 12 13 8 9 14 15 0 | ||||
| 1 6 4 11 0 | ||||
| 1 6 8 11 0 | ||||
| 1 6 5 1 4 11 2 0 | ||||
| 0 | ||||
| "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" "13" "14" "15" "a3" | ||||
							
								
								
									
										200
									
								
								include/condorcet/Tests/src/Tools/Converters/TidemanData/A60.HIL
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								include/condorcet/Tests/src/Tools/Converters/TidemanData/A60.HIL
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| 6 1 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 4 3 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 4 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 0 | ||||
| 1 4 3 5 0 | ||||
| 1 4 5 2 0 | ||||
| 1 3 5 0 | ||||
| 1 3 5 2 0 | ||||
| 1 5 3 4 0 | ||||
| 1 5 0 | ||||
| 1 5 4 3 0 | ||||
| 1 5 0 | ||||
| 1 5 2 4 0 | ||||
| 1 5 4 3 0 | ||||
| 1 5 4 3 0 | ||||
| 1 4 5 6 0 | ||||
| 1 5 6 0 | ||||
| 1 5 6 1 4 3 0 | ||||
| 1 5 6 0 | ||||
| 1 5 4 6 3 1 0 | ||||
| 1 5 6 2 1 4 0 | ||||
| 1 5 6 0 | ||||
| 1 5 4 6 1 2 0 | ||||
| 1 5 6 0 | ||||
| 1 5 3 2 4 6 0 | ||||
| 1 5 4 6 3 1 0 | ||||
| 1 5 6 0 | ||||
| 1 3 5 6 0 | ||||
| 1 3 2 5 4 6 0 | ||||
| 1 6 5 4 0 | ||||
| 1 6 4 1 0 | ||||
| 1 6 0 | ||||
| 1 6 2 4 0 | ||||
| 1 6 0 | ||||
| 1 6 2 3 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 1 4 2 3 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 5 3 0 | ||||
| 1 6 0 | ||||
| 1 6 4 5 3 1 0 | ||||
| 1 6 1 3 0 | ||||
| 1 6 0 | ||||
| 1 6 5 0 | ||||
| 1 6 1 3 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 1 3 0 | ||||
| 1 6 4 5 3 2 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 5 4 1 2 0 | ||||
| 1 6 4 0 | ||||
| 1 6 5 2 3 1 0 | ||||
| 1 6 5 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 2 3 1 4 0 | ||||
| 1 6 3 2 4 5 0 | ||||
| 1 6 0 | ||||
| 1 6 4 3 5 1 0 | ||||
| 1 6 5 4 0 | ||||
| 1 6 5 1 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 0 | ||||
| 1 6 5 1 0 | ||||
| 1 3 6 5 0 | ||||
| 1 3 6 0 | ||||
| 1 3 6 4 0 | ||||
| 1 3 6 4 0 | ||||
| 1 2 4 6 0 | ||||
| 1 4 6 3 0 | ||||
| 1 4 6 1 2 3 0 | ||||
| 1 4 3 6 0 | ||||
| 1 4 3 2 6 1 0 | ||||
| 1 4 2 6 3 1 0 | ||||
| 1 4 6 0 | ||||
| 1 4 5 1 0 | ||||
| 1 3 4 5 2 1 0 | ||||
| 1 5 1 6 2 3 0 | ||||
| 1 5 1 4 0 | ||||
| 1 5 1 0 | ||||
| 1 5 4 1 0 | ||||
| 1 5 1 0 | ||||
| 1 5 1 3 6 2 0 | ||||
| 1 5 1 0 | ||||
| 1 5 1 4 0 | ||||
| 1 5 1 0 | ||||
| 1 5 1 3 4 2 0 | ||||
| 1 1 4 3 0 | ||||
| 1 1 0 | ||||
| 1 1 0 | ||||
| 1 1 0 | ||||
| 1 1 2 3 6 5 0 | ||||
| 1 1 4 2 3 5 0 | ||||
| 1 1 4 6 0 | ||||
| 1 1 5 4 3 0 | ||||
| 1 1 4 3 0 | ||||
| 1 1 5 6 0 | ||||
| 1 1 6 4 0 | ||||
| 1 1 3 4 0 | ||||
| 1 1 2 6 3 4 0 | ||||
| 1 1 0 | ||||
| 1 1 0 | ||||
| 1 1 5 0 | ||||
| 1 1 0 | ||||
| 1 1 4 3 0 | ||||
| 1 1 6 2 4 3 0 | ||||
| 1 1 0 | ||||
| 1 1 0 | ||||
| 1 1 3 6 5 2 0 | ||||
| 1 1 6 2 3 4 0 | ||||
| 1 1 0 | ||||
| 1 1 6 5 3 4 0 | ||||
| 1 1 0 | ||||
| 1 1 6 0 | ||||
| 1 1 2 3 0 | ||||
| 1 1 6 3 5 4 0 | ||||
| 1 1 4 2 3 6 0 | ||||
| 1 1 0 | ||||
| 1 1 0 | ||||
| 1 1 3 0 | ||||
| 1 1 5 2 3 6 0 | ||||
| 1 1 3 4 0 | ||||
| 1 1 4 5 6 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 2 4 6 0 | ||||
| 1 1 0 | ||||
| 1 1 3 5 0 | ||||
| 1 1 0 | ||||
| 1 1 6 0 | ||||
| 1 1 5 6 3 2 0 | ||||
| 1 1 2 4 0 | ||||
| 1 1 5 3 4 6 0 | ||||
| 1 1 3 2 4 6 0 | ||||
| 1 1 0 | ||||
| 1 1 5 2 6 3 0 | ||||
| 1 1 4 6 2 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 5 4 3 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 0 | ||||
| 1 1 3 2 5 4 0 | ||||
| 1 1 5 0 | ||||
| 1 1 5 0 | ||||
| 1 1 5 0 | ||||
| 1 1 2 5 0 | ||||
| 1 1 3 4 0 | ||||
| 1 1 3 4 6 2 0 | ||||
| 1 1 2 6 4 5 0 | ||||
| 1 1 6 0 | ||||
| 1 1 5 4 3 0 | ||||
| 1 1 0 | ||||
| 1 1 4 3 0 | ||||
| 1 1 5 4 0 | ||||
| 1 1 2 5 0 | ||||
| 1 1 6 0 | ||||
| 1 2 1 3 4 5 0 | ||||
| 1 2 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 5 6 2 0 | ||||
| 1 3 1 5 0 | ||||
| 1 3 1 5 0 | ||||
| 1 3 1 4 0 | ||||
| 1 3 1 5 0 | ||||
| 1 3 1 4 0 | ||||
| 1 3 4 1 0 | ||||
| 1 4 1 6 5 3 0 | ||||
| 1 4 2 1 0 | ||||
| 1 4 1 5 6 2 0 | ||||
| 1 4 1 6 2 5 0 | ||||
| 1 4 1 3 2 6 0 | ||||
| 1 4 1 6 0 | ||||
| 0 | ||||
| "1" "2" "3" "4" "5" "6" "f:a60" | ||||
							
								
								
									
										216
									
								
								include/condorcet/Tests/src/Tools/Converters/TidemanData/A77.HIL
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								include/condorcet/Tests/src/Tools/Converters/TidemanData/A77.HIL
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | ||||
| 3 1 | ||||
| 1 1 2 0 | ||||
| 1 1 0 | ||||
| 1 1 2 0 | ||||
| 1 1 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 0 | ||||
| 1 1 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 1 0 | ||||
| 1 3 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 2 0 | ||||
| 1 2 3 0 | ||||
| 1 2 3 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 0 | ||||
| 1 2 3 0 | ||||
| 1 2 3 0 | ||||
| 1 2 3 0 | ||||
| 1 2 3 0 | ||||
| 1 2 3 0 | ||||
| 1 2 3 0 | ||||
| 1 2 3 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 2 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 1 3 0 | ||||
| 1 1 0 | ||||
| 1 1 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 3 0 | ||||
| 1 1 0 | ||||
| 1 1 3 0 | ||||
| 1 1 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 2 0 | ||||
| 1 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 1 3 1 0 | ||||
| 1 3 0 | ||||
| 1 3 2 0 | ||||
| 1 3 2 0 | ||||
| 1 3 0 | ||||
| 1 3 0 | ||||
| 1 3 1 0 | ||||
| 0 | ||||
| "1" "2" "3" "f:a77" | ||||
							
								
								
									
										190
									
								
								include/condorcet/Tests/src/Utils/VoteEntryParserTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								include/condorcet/Tests/src/Utils/VoteEntryParserTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Utils; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Throwable\VoteInvalidFormatException; | ||||
| use CondorcetPHP\Condorcet\Tools\Converters\CondorcetElectionFormat; | ||||
| use CondorcetPHP\Condorcet\Utils\VoteEntryParser; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class VoteEntryParserTest extends TestCase | ||||
| { | ||||
|     /** | ||||
|      * @dataProvider voteBadNumericValueProvider() | ||||
|      */ | ||||
|     public function testBadNumericValue(string $entry, string $message): void | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage($message); | ||||
|  | ||||
|         new VoteEntryParser($entry); | ||||
|     } | ||||
|  | ||||
|     public function voteBadNumericValueProvider(): iterable | ||||
|     { | ||||
|         yield [ | ||||
|             'entry' => 'A>B ^g', | ||||
|             'message' => "the value 'g' is not an integer.", | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => 'A>B ^a*b', | ||||
|             'message' => "the value 'b' is not an integer.", | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => 'A>B*b', | ||||
|             'message' => "the value 'b' is not an integer.", | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => 'A>B ^4Y ', | ||||
|             'message' => "the value '4Y' is not an integer.", | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @dataProvider voteEntriesProvider() | ||||
|      */ | ||||
|     public function testVotesEntries(string $entry, array $expected): void | ||||
|     { | ||||
|         $parser = new VoteEntryParser($entry); | ||||
|  | ||||
|         self::assertSame($entry, $parser->originalEntry); | ||||
|  | ||||
|         self::assertSame($expected['comment'], $parser->comment); | ||||
|         self::assertSame($expected['multiple'], $parser->multiple); | ||||
|         self::assertSame($expected['ranking'], $parser->ranking); | ||||
|         self::assertSame($expected['tags'], $parser->tags); | ||||
|         self::assertSame($expected['weight'], $parser->weight); | ||||
|     } | ||||
|  | ||||
|     public function voteEntriesProvider(): iterable | ||||
|     { | ||||
|         yield [ | ||||
|             'entry' => 'A >B = C>D', | ||||
|             'expected' => [ | ||||
|                 'comment' => null, | ||||
|                 'multiple' => 1, | ||||
|                 'ranking' => [ | ||||
|                     ['A'], | ||||
|                     ['B', 'C'], | ||||
|                     ['D'], | ||||
|                 ], | ||||
|                 'tags' => null, | ||||
|                 'weight' => 1, | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => 'tag1,     tag2   ||    A >B = C>D ^ 7 * 42 #      One Comment', | ||||
|             'expected' => [ | ||||
|                 'comment' => 'One Comment', | ||||
|                 'multiple' => 42, | ||||
|                 'ranking' => [ | ||||
|                     ['A'], | ||||
|                     ['B', 'C'], | ||||
|                     ['D'], | ||||
|                 ], | ||||
|                 'tags' => ['tag1', 'tag2'], | ||||
|                 'weight' => 7, | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => 'A >B = C>D *42#One Comment', | ||||
|             'expected' => [ | ||||
|                 'comment' => 'One Comment', | ||||
|                 'multiple' => 42, | ||||
|                 'ranking' => [ | ||||
|                     ['A'], | ||||
|                     ['B', 'C'], | ||||
|                     ['D'], | ||||
|                 ], | ||||
|                 'tags' => null, | ||||
|                 'weight' => 1, | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => 'A^     7#', | ||||
|             'expected' => [ | ||||
|                 'comment' => '', | ||||
|                 'multiple' => 1, | ||||
|                 'ranking' => [ | ||||
|                     ['A'], | ||||
|                 ], | ||||
|                 'tags' => null, | ||||
|                 'weight' => 7, | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => '   ', | ||||
|             'expected' => [ | ||||
|                 'comment' => null, | ||||
|                 'multiple' => 1, | ||||
|                 'ranking' => null, | ||||
|                 'tags' => null, | ||||
|                 'weight' => 1, | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => ' tag1,tag2||  ', | ||||
|             'expected' => [ | ||||
|                 'comment' => null, | ||||
|                 'multiple' => 1, | ||||
|                 'ranking' => null, | ||||
|                 'tags' => ['tag1', 'tag2'], | ||||
|                 'weight' => 1, | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => '^7*4  ', | ||||
|             'expected' => [ | ||||
|                 'comment' => null, | ||||
|                 'multiple' => 4, | ||||
|                 'ranking' => null, | ||||
|                 'tags' => null, | ||||
|                 'weight' => 7, | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => ' #One Comment', | ||||
|             'expected' => [ | ||||
|                 'comment' => 'One Comment', | ||||
|                 'multiple' => 1, | ||||
|                 'ranking' => null, | ||||
|                 'tags' => null, | ||||
|                 'weight' => 1, | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => 'tag1,tag2||'.CondorcetElectionFormat::SPECIAL_KEYWORD_EMPTY_RANKING.'^7*42#FeteDuDindon', | ||||
|             'expected' => [ | ||||
|                 'comment' => 'FeteDuDindon', | ||||
|                 'multiple' => 42, | ||||
|                 'ranking' => [], | ||||
|                 'tags' => ['tag1', 'tag2'], | ||||
|                 'weight' => 7, | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         yield [ | ||||
|             'entry' => ' '.CondorcetElectionFormat::SPECIAL_KEYWORD_EMPTY_RANKING.' ', | ||||
|             'expected' => [ | ||||
|                 'comment' => null, | ||||
|                 'multiple' => 1, | ||||
|                 'ranking' => [], | ||||
|                 'tags' => null, | ||||
|                 'weight' => 1, | ||||
|             ], | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										77
									
								
								include/condorcet/Tests/src/Utils/VoteUtilTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								include/condorcet/Tests/src/Utils/VoteUtilTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests\Utils; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\Utils\VoteUtil; | ||||
| use CondorcetPHP\Condorcet\Throwable\VoteInvalidFormatException; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class VoteUtilTest extends TestCase | ||||
| { | ||||
|     public function testTypeMismatchTagsThrowAnException(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid: every tag must be of type string, array given'); | ||||
|         VoteUtil::tagsConvert(['not', 'a', 'string:', []]); | ||||
|     } | ||||
|  | ||||
|     public function testEmptyTagsThrowAnException(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid: found empty tag'); | ||||
|         VoteUtil::tagsConvert('an , empty, tag , , in, the, middle'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @dataProvider tagsProvider() | ||||
|      */ | ||||
|     public function testTagsGetConverted($tags, $expected): void | ||||
|     { | ||||
|         $this->assertSame($expected, VoteUtil::tagsConvert($tags)); | ||||
|     } | ||||
|  | ||||
|     public function testGetRankingAsString(): void | ||||
|     { | ||||
|         // Empty ranking | ||||
|         $this->assertEquals('', VoteUtil::getRankingAsString([])); | ||||
|  | ||||
|         // String ranking | ||||
|         $this->assertEquals('A > B > C', VoteUtil::getRankingAsString(['A', 'B', 'C'])); | ||||
|  | ||||
|         // Array ranking | ||||
|         $this->assertEquals('A = B > C', VoteUtil::getRankingAsString([['A', 'B'], 'C'])); | ||||
|  | ||||
|         // Unsorted array ranking | ||||
|         $this->assertEquals('A = B > C', VoteUtil::getRankingAsString([['B', 'A'], 'C'])); | ||||
|     } | ||||
|  | ||||
|     public function tagsProvider(): iterable | ||||
|     { | ||||
|         yield 'null tags' => [ | ||||
|             'tags' => null, | ||||
|             'expected' => null, | ||||
|         ]; | ||||
|  | ||||
|         yield 'empty string' => [ | ||||
|             'tags' => '', | ||||
|             'expected' => null, | ||||
|         ]; | ||||
|  | ||||
|         yield 'empty array' => [ | ||||
|             'tags' => [], | ||||
|             'expected' => null, | ||||
|         ]; | ||||
|  | ||||
|         yield 'tags as string' => [ | ||||
|             'tags' => 'these, are,   some   , tags', | ||||
|             'expected' => ['these', 'are', 'some', 'tags'], | ||||
|         ]; | ||||
|  | ||||
|         yield 'tags as array' => [ | ||||
|             'tags' => ['these', 'are', 'some', 'more', 'tags'], | ||||
|             'expected' => ['these', 'are', 'some', 'more', 'tags'], | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										814
									
								
								include/condorcet/Tests/src/VoteTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										814
									
								
								include/condorcet/Tests/src/VoteTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,814 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace CondorcetPHP\Condorcet\Tests; | ||||
|  | ||||
| use CondorcetPHP\Condorcet\{Candidate, Election, Vote}; | ||||
| use CondorcetPHP\Condorcet\Throwable\{CandidateDoesNotExistException, VoteInvalidFormatException, VoteNotLinkedException}; | ||||
| use CondorcetPHP\Condorcet\Tools\Converters\CondorcetElectionFormat; | ||||
| use CondorcetPHP\Condorcet\Utils\CondorcetUtil; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class VoteTest extends TestCase | ||||
| { | ||||
|     private readonly Election $election1; | ||||
|  | ||||
|     private readonly Candidate $candidate1; | ||||
|     private readonly Candidate $candidate2; | ||||
|     private readonly Candidate $candidate3; | ||||
|     private readonly Candidate $candidate4; | ||||
|     private readonly Candidate $candidate5; | ||||
|     private readonly Candidate $candidate6; | ||||
|  | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         $this->election1 = new Election; | ||||
|  | ||||
|         $this->candidate1 = $this->election1->addCandidate('candidate1'); | ||||
|         $this->candidate2 = $this->election1->addCandidate('candidate2'); | ||||
|         $this->candidate3 = $this->election1->addCandidate('candidate3'); | ||||
|         $this->candidate4 = new Candidate('candidate4'); | ||||
|         $this->candidate5 = new Candidate('candidate5'); | ||||
|         $this->candidate6 = new Candidate('candidate6'); | ||||
|     } | ||||
|  | ||||
|     public function testTimestamp(): void | ||||
|     { | ||||
|         $vote1 = new Vote([$this->candidate1, $this->candidate2, $this->candidate3]); | ||||
|  | ||||
|         self::assertEquals($vote1->getCreateTimestamp(), $vote1->getTimestamp()); | ||||
|  | ||||
|         $vote1->setRanking([$this->candidate1, $this->candidate2, $this->candidate3]); | ||||
|  | ||||
|         self::assertLessThan($vote1->getTimestamp(), $vote1->getCreateTimestamp()); | ||||
|     } | ||||
|  | ||||
|     public function testDifferentRanking(): never | ||||
|     { | ||||
|         $this->expectException(VoteNotLinkedException::class); | ||||
|         $this->expectExceptionMessage('The vote is not linked to an election'); | ||||
|  | ||||
|         // Ranking 1 | ||||
|         $vote1 = new Vote([$this->candidate1, $this->candidate2, $this->candidate3]); | ||||
|  | ||||
|         $newRanking1 = $vote1->getRanking(); | ||||
|  | ||||
|         // Ranking 2 | ||||
|         self::assertTrue( | ||||
|             $vote1->setRanking( | ||||
|                 [$this->candidate1, $this->candidate2, $this->candidate3] | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $newRanking1, | ||||
|             $vote1->getRanking() | ||||
|         ); | ||||
|  | ||||
|         // Ranking 3 | ||||
|         self::assertTrue( | ||||
|             $vote1->setRanking( | ||||
|                 [4=> $this->candidate1, 6=> $this->candidate2, 14 => $this->candidate3] | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $newRanking1, | ||||
|             $vote1->getRanking() | ||||
|         ); | ||||
|  | ||||
|         // Add vote into an election | ||||
|         self::assertSame( | ||||
|             $this->election1->addVote($vote1), | ||||
|             $vote1 | ||||
|         ); | ||||
|  | ||||
|         // Ranking 4 | ||||
|         self::assertTrue( | ||||
|             $vote1->setRanking( | ||||
|                 [$this->candidate1, $this->candidate2] | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $newRanking1, | ||||
|             $vote1->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|         self::assertCount( | ||||
|             2, | ||||
|             $vote1->getRanking() | ||||
|         ); | ||||
|  | ||||
|         // Ranking 5 | ||||
|         self::assertTrue( | ||||
|             $vote1->setRanking( | ||||
|                 ['candidate1', 'candidate2'] | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $newRanking1, | ||||
|             $vote1->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|         // Ranking 6 | ||||
|         self::assertTrue( | ||||
|             $vote1->setRanking( | ||||
|                 [42 => 'candidate2', 142=> 'candidate1'] | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         self::assertNotSame( | ||||
|             $newRanking1, | ||||
|             $vote1->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|         // Ranking 7 | ||||
|         self::assertTrue( | ||||
|             $vote1->setRanking( | ||||
|                 'candidate1>Kim Jong>candidate2>Trump' | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $newRanking1, | ||||
|             $vote1->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|  | ||||
|         // Ranking 8 | ||||
|         self::assertTrue( | ||||
|             $vote1->setRanking([ | ||||
|                 2=> $this->candidate2, | ||||
|                 1=> $this->candidate1, | ||||
|                 3=> $this->candidate3, | ||||
|             ]) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $newRanking1, | ||||
|             $vote1->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|  | ||||
|         // Ranking 9 | ||||
|  | ||||
|         $vote = new Vote('candidate4 > candidate3 = candidate1 > candidate2'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             CondorcetUtil::format($vote->getRanking()), | ||||
|             [ | ||||
|                 1 => 'candidate4', | ||||
|                 2 => ['candidate1', 'candidate3'], | ||||
|                 3 => 'candidate2', | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         $election = new Election; | ||||
|         $election->parseCandidates('candidate2;candidate3;candidate4;candidate1'); | ||||
|         $election->addVote($vote); | ||||
|  | ||||
|         self::assertSame( | ||||
|             CondorcetUtil::format($vote->getContextualRanking($election)), | ||||
|             [ | ||||
|                 1 => 'candidate4', | ||||
|                 2 => ['candidate1', 'candidate3'], | ||||
|                 3 => 'candidate2', | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $election->getResult()->getResultAsArray(true), | ||||
|             [ | ||||
|                 1 => 'candidate4', | ||||
|                 2 => ['candidate1', 'candidate3'], | ||||
|                 3 => 'candidate2', | ||||
|             ] | ||||
|         ); | ||||
|  | ||||
|         // Contextual Ranking Fail | ||||
|  | ||||
|         $unexpectedElection = new Election; | ||||
|  | ||||
|         $vote1->getContextualRanking($unexpectedElection); | ||||
|     } | ||||
|  | ||||
|     public function testSimpleRanking(): void | ||||
|     { | ||||
|         // Ranking 1 | ||||
|         $vote1 = new Vote('candidate1 > candidate3 = candidate2 > candidate4'); | ||||
|  | ||||
|         self::assertSame($vote1->getSimpleRanking(), 'candidate1 > candidate2 = candidate3 > candidate4'); | ||||
|  | ||||
|         $this->election1->addVote($vote1); | ||||
|  | ||||
|         self::assertSame($vote1->getSimpleRanking($this->election1), 'candidate1 > candidate2 = candidate3'); | ||||
|     } | ||||
|  | ||||
|     public function testProvisionalCandidateObject(): void | ||||
|     { | ||||
|         // Ranking 1 | ||||
|         $vote1 = new Vote([$this->candidate1, $this->candidate2, $this->candidate3]); | ||||
|         $newRanking1 = $vote1->getRanking(); | ||||
|         $this->election1->addVote($vote1); | ||||
|  | ||||
|         // I | ||||
|         self::assertTrue( | ||||
|             $vote1->setRanking([ | ||||
|                 new Candidate('candidate1'), | ||||
|                 $this->candidate2, | ||||
|                 $this->candidate3, | ||||
|             ]) | ||||
|         ); | ||||
|  | ||||
|         self::assertNotSame( | ||||
|             $newRanking1, | ||||
|             $vote1->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => [$this->candidate2], | ||||
|                 2 => [$this->candidate3], | ||||
|                 3 => [$this->candidate1], ], | ||||
|             $vote1->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'candidate2', | ||||
|                 2 => 'candidate3', | ||||
|                 3 => 'candidate1', ], | ||||
|             $vote1->getContextualRankingAsString($this->election1) | ||||
|         ); | ||||
|  | ||||
|         // II | ||||
|         $vote2 = new Vote('candidate1>candidate2'); | ||||
|  | ||||
|         self::assertTrue($vote2->getRanking()[1][0]->getProvisionalState()); | ||||
|         $vote2_firstRanking = $vote2->getRanking(); | ||||
|  | ||||
|         $this->election1->addVote($vote2); | ||||
|  | ||||
|         self::assertFalse($vote2->getRanking()[1][0]->getProvisionalState()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => [$this->candidate1], | ||||
|                 2 => [$this->candidate2], | ||||
|                 3 => [$this->candidate3], ], | ||||
|             $vote2->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|         self::assertNotEquals( | ||||
|             $vote2_firstRanking, | ||||
|             $vote2->getRanking() | ||||
|         ); | ||||
|  | ||||
|  | ||||
|         // III | ||||
|         $otherCandidate1 = new candidate('candidate1'); | ||||
|         $otherCandidate2 = new candidate('candidate2'); | ||||
|  | ||||
|         $vote3 = new Vote([$otherCandidate1, $otherCandidate2, $this->candidate3]); | ||||
|  | ||||
|         self::assertFalse($vote3->getRanking()[1][0]->getProvisionalState()); | ||||
|         $vote3_firstRanking = $vote3->getRanking(); | ||||
|  | ||||
|         $this->election1->addVote($vote3); | ||||
|  | ||||
|         self::assertFalse($vote2->getRanking()[1][0]->getProvisionalState()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => [$this->candidate3], | ||||
|                 2 => [$this->candidate1, $this->candidate2], ], | ||||
|             $vote3->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => 'candidate3', | ||||
|                 2 => ['candidate1', 'candidate2'], ], | ||||
|             $vote3->getContextualRankingAsString($this->election1) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             $vote3_firstRanking, | ||||
|             $vote3->getRanking() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testDifferentElection(): void | ||||
|     { | ||||
|         $election1 = $this->election1; | ||||
|  | ||||
|         $election2 = new Election; | ||||
|         $election2->addCandidate($this->candidate1); | ||||
|         $election2->addCandidate($this->candidate2); | ||||
|         $election2->addCandidate($this->candidate4); | ||||
|  | ||||
|         $vote1 = new Vote([ | ||||
|             $this->candidate1, | ||||
|             $this->candidate2, | ||||
|             $this->candidate3, | ||||
|             $this->candidate4, | ||||
|         ]); | ||||
|         $vote1_originalRanking = $vote1->getRanking(); | ||||
|  | ||||
|         $election1->addVote($vote1); | ||||
|         $election2->addVote($vote1); | ||||
|  | ||||
|         self::assertSame($vote1_originalRanking, $vote1->getRanking()); | ||||
|         self::assertSame( | ||||
|             [1=>[$this->candidate1], 2=>[$this->candidate2], 3=>[$this->candidate3]], | ||||
|             $vote1->getContextualRanking($election1) | ||||
|         ); | ||||
|         self::assertSame( | ||||
|             [1=>[$this->candidate1], 2=>[$this->candidate2], 3=>[$this->candidate4]], | ||||
|             $vote1->getContextualRanking($election2) | ||||
|         ); | ||||
|         self::assertNotSame($vote1->getRanking(), $vote1->getContextualRanking($election2)); | ||||
|  | ||||
|         self::assertTrue($vote1->setRanking([ | ||||
|             [$this->candidate5, $this->candidate2], | ||||
|             $this->candidate3, | ||||
|         ])); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1=>[$this->candidate2], 2=>[$this->candidate3], 3=>[$this->candidate1]], | ||||
|             $vote1->getContextualRanking($election1) | ||||
|         ); | ||||
|         self::assertSame( | ||||
|             [1=>[$this->candidate2], 2=>[$this->candidate1, $this->candidate4]], | ||||
|             $vote1->getContextualRanking($election2) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testValidTags(): void | ||||
|     { | ||||
|         $vote1 = new Vote([$this->candidate1, $this->candidate2, $this->candidate3]); | ||||
|  | ||||
|         $targetTags = ['tag1', 'tag2', 'tag3']; | ||||
|  | ||||
|         self::assertTrue($vote1->addTags( | ||||
|             'tag1,tag2,tag3' | ||||
|         )); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $targetTags, | ||||
|             array_values($vote1->getTags()) | ||||
|         ); | ||||
|  | ||||
|         self::assertTrue($vote1->removeAllTags()); | ||||
|         self::assertSame( | ||||
|             [], | ||||
|             $vote1->getTags() | ||||
|         ); | ||||
|  | ||||
|         self::assertTrue($vote1->addTags( | ||||
|             ['tag1', 'tag2', 'tag3'] | ||||
|         )); | ||||
|  | ||||
|         self::assertSame( | ||||
|             $targetTags, | ||||
|             array_values($vote1->getTags()) | ||||
|         ); | ||||
|  | ||||
|         self::assertEquals(['tag2'], $vote1->removeTags('tag2')); | ||||
|  | ||||
|         self::assertEquals( | ||||
|             ['tag1', 'tag3'], | ||||
|             array_values($vote1->getTags()) | ||||
|         ); | ||||
|  | ||||
|         self::assertTrue($vote1->removeAllTags()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [], | ||||
|             $vote1->getTags() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testBadTagInput1(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid: every tag must be of type string, integer given'); | ||||
|  | ||||
|         $vote = new Vote('A'); | ||||
|         $vote->addTags(['tag1', 42]); | ||||
|     } | ||||
|  | ||||
|     public function testBadTagInput2(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid: found empty tag'); | ||||
|  | ||||
|         $vote = new Vote('A'); | ||||
|         $vote->addTags( | ||||
|             ['tag1 ', ' tag2', ' tag3 ', ' '] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [], | ||||
|             $vote->getTags() | ||||
|         ); | ||||
|  | ||||
|         self::assertTrue($vote->removeAllTags()); | ||||
|     } | ||||
|  | ||||
|     public function testBadTagInput3(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid: found empty tag'); | ||||
|  | ||||
|         $vote = new Vote('A'); | ||||
|         $vote->addTags( | ||||
|             ' tag1,tag2 , tag3 ,' | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [], | ||||
|             $vote->getTags() | ||||
|         ); | ||||
|  | ||||
|         self::assertTrue($vote->removeAllTags()); | ||||
|     } | ||||
|  | ||||
|     public function testBadTagInput4(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid: every tag must be of type string, NULL given'); | ||||
|  | ||||
|         $vote = new Vote('A'); | ||||
|         $vote->addTags( | ||||
|             [null] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [], | ||||
|             $vote->getTags() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testBadTagInput5(): void | ||||
|     { | ||||
|         $vote = new Vote('A'); | ||||
|         $vote->addTags( | ||||
|             [] | ||||
|         ); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [], | ||||
|             $vote->getTags() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testAddRemoveTags(): void | ||||
|     { | ||||
|         $vote1 = new Vote([$this->candidate1, $this->candidate2, $this->candidate3]); | ||||
|  | ||||
|         $vote1->addTags('tag1'); | ||||
|         $vote1->addTags(['tag2', 'tag3']); | ||||
|         self::assertTrue($vote1->addTags('tag4,tag5')); | ||||
|  | ||||
|         self::assertSame( | ||||
|             ['tag1', 'tag2', 'tag3', 'tag4', 'tag5'], | ||||
|             $vote1->getTags() | ||||
|         ); | ||||
|  | ||||
|         self::assertsame([], $vote1->removeTags('')); | ||||
|         $vote1->removeTags('tag1'); | ||||
|         $vote1->removeTags(['tag2', 'tag3']); | ||||
|         self::assertsame($vote1->removeTags('tag4,tag5,tag42'), ['tag4', 'tag5']); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [], | ||||
|             $vote1->getTags() | ||||
|         ); | ||||
|  | ||||
|         self::assertTrue($vote1->addTags('tag4,tag5')); | ||||
|         self::assertTrue($vote1->removeAllTags()); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [], | ||||
|             $vote1->getTags() | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testTagsOnConstructorByStringInput(): void | ||||
|     { | ||||
|         $vote1 = new Vote('tag1,tag2 ||A > B >C', 'tag3,tag4'); | ||||
|  | ||||
|         self::assertSame(['tag3', 'tag4', 'tag1', 'tag2'], $vote1->getTags()); | ||||
|  | ||||
|         self::assertSame('A > B > C', $vote1->getSimpleRanking()); | ||||
|  | ||||
|         $vote2 = new Vote((string) $vote1); | ||||
|  | ||||
|         self::assertSame((string) $vote1, (string) $vote2); | ||||
|     } | ||||
|  | ||||
|     public function testCloneVote(): void | ||||
|     { | ||||
|         // Ranking 1 | ||||
|         $vote1 = new Vote('candidate1 > candidate3 = candidate2 > candidate4'); | ||||
|  | ||||
|         $this->election1->addVote($vote1); | ||||
|  | ||||
|         $vote2 = clone $vote1; | ||||
|  | ||||
|         self::assertSame(0, $vote2->countLinks()); | ||||
|         self::assertSame(1, $vote1->countLinks()); | ||||
|     } | ||||
|  | ||||
|     public function testIterator(): void | ||||
|     { | ||||
|         $vote = new Vote('C > B > A'); | ||||
|  | ||||
|         foreach ($vote as $key => $value) { | ||||
|             self::assertSame($vote->getRanking()[$key], $value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function testWeight(): never | ||||
|     { | ||||
|         $vote = new Vote('A>B>C^42'); | ||||
|  | ||||
|         self::assertsame(42, $vote->getWeight()); | ||||
|         self::assertsame(2, $vote->setWeight(2)); | ||||
|         self::assertsame(2, $vote->getWeight()); | ||||
|         self::assertsame(1, $vote->getWeight($this->election1)); | ||||
|  | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage("The format of the vote is invalid: the value 'a' is not an integer."); | ||||
|  | ||||
|         $vote = new Vote('A>B>C^a'); | ||||
|     } | ||||
|  | ||||
|     public function testCustomTimestamp(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid: Timestamp format of vote is not correct'); | ||||
|  | ||||
|         $vote = new Vote( | ||||
|             'A>B>C', | ||||
|             null, | ||||
|             $createTimestamp = microtime(true) - (3600 * 1000) | ||||
|         ); | ||||
|  | ||||
|         self::assertSame($createTimestamp, $vote->getTimestamp()); | ||||
|  | ||||
|         $vote->setRanking('B>C>A', $ranking2Timestamp = microtime(true) - (60 * 1000)); | ||||
|  | ||||
|         self::assertSame($ranking2Timestamp, $vote->getTimestamp()); | ||||
|  | ||||
|         self::assertSame($createTimestamp, $vote->getCreateTimestamp()); | ||||
|  | ||||
|         self::assertSame($createTimestamp, $vote->getHistory()[0]['timestamp']); | ||||
|  | ||||
|         self::assertSame($ranking2Timestamp, $vote->getHistory()[1]['timestamp']); | ||||
|  | ||||
|         $vote->setRanking('A', 1); | ||||
|     } | ||||
|  | ||||
|     public function testHashCode(): void | ||||
|     { | ||||
|         $vote = new Vote('A>B>C'); | ||||
|  | ||||
|         $hashCode[1] = $vote->getHashCode(); | ||||
|  | ||||
|         $vote->addTags('tag1'); | ||||
|  | ||||
|         $hashCode[2] = $vote->getHashCode(); | ||||
|  | ||||
|         $vote->setRanking('C>A>B'); | ||||
|  | ||||
|         $hashCode[3] = $vote->getHashCode(); | ||||
|  | ||||
|         $vote->setRanking('C>A>B'); | ||||
|  | ||||
|         $hashCode[4] = $vote->getHashCode(); | ||||
|  | ||||
|         self::assertNotsame($hashCode[2], $hashCode[1]); | ||||
|         self::assertNotsame($hashCode[3], $hashCode[2]); | ||||
|         self::assertNotSame($hashCode[4], $hashCode[3]); | ||||
|     } | ||||
|  | ||||
|     public function testCountRankingCandidates(): void | ||||
|     { | ||||
|         $vote = new Vote('A>B>C'); | ||||
|  | ||||
|         self::assertsame(3, $vote->countRankingCandidates()); | ||||
|     } | ||||
|  | ||||
|     public function testInvalidWeight(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid: the vote weight can not be less than 1'); | ||||
|  | ||||
|         $vote = new Vote('A>B>C'); | ||||
|  | ||||
|         $vote->setWeight(0); | ||||
|     } | ||||
|  | ||||
|     public function testInvalidTag1(): never | ||||
|     { | ||||
|         $this->expectException(\TypeError::class); | ||||
|  | ||||
|         $vote = new Vote('A>B>C'); | ||||
|  | ||||
|         $vote->addTags(true); | ||||
|     } | ||||
|  | ||||
|     public function testInvalidTag2(): never | ||||
|     { | ||||
|         $this->expectException(\TypeError::class); | ||||
|  | ||||
|         $vote = new Vote('A>B>C'); | ||||
|  | ||||
|         $vote->addTags(42); | ||||
|     } | ||||
|  | ||||
|     public function testRemoveCandidate(): never | ||||
|     { | ||||
|         $vote1 = new Vote('candidate1 > candidate2 > candidate3 ^ 42'); | ||||
|  | ||||
|         $this->election1->addVote($vote1); | ||||
|  | ||||
|         self::assertSame('candidate1 > candidate2 > candidate3', $this->election1->getResult()->getResultAsString()); | ||||
|  | ||||
|         $vote1->removeCandidate('candidate2'); | ||||
|  | ||||
|         self::assertSame('candidate1 > candidate3 ^42', $vote1->getSimpleRanking()); | ||||
|  | ||||
|         self::assertSame('candidate1 > candidate3 > candidate2', $this->election1->getResult()->getResultAsString()); | ||||
|  | ||||
|         $vote1->removeCandidate($this->candidate3); | ||||
|  | ||||
|         self::assertSame('candidate1 > candidate2 = candidate3', $this->election1->getResult()->getResultAsString()); | ||||
|  | ||||
|         $this->expectException(CandidateDoesNotExistException::class); | ||||
|         $this->expectExceptionMessage('This candidate does not exist: candidate4'); | ||||
|  | ||||
|         $vote1->removeCandidate($this->candidate4); | ||||
|     } | ||||
|  | ||||
|     public function testRemoveCandidateInvalidInput(): never | ||||
|     { | ||||
|         $vote1 = new Vote('candidate1 > candidate2 > candidate3 ^ 42'); | ||||
|  | ||||
|         $this->expectException(\TypeError::class); | ||||
|  | ||||
|         $vote1->removeCandidate([]); | ||||
|     } | ||||
|  | ||||
|     public function testVoteHistory(): void | ||||
|     { | ||||
|         $this->election1->addCandidate($this->candidate4); | ||||
|         $this->election1->addCandidate($this->candidate5); | ||||
|         $this->election1->addCandidate($this->candidate6); | ||||
|  | ||||
|  | ||||
|         $vote1 = $this->election1->addVote(['candidate1', 'candidate2']); | ||||
|  | ||||
|         self::assertCount(1, $vote1->getHistory()); | ||||
|  | ||||
|         // ------- | ||||
|  | ||||
|         $vote2 = $this->election1->addVote('candidate1 > candidate2'); | ||||
|  | ||||
|         self::assertCount(1, $vote2->getHistory()); | ||||
|  | ||||
|         // ------- | ||||
|  | ||||
|         $vote3 = new Vote(['candidate1', 'candidate2']); | ||||
|  | ||||
|         $this->election1->addVote($vote3); | ||||
|  | ||||
|         self::assertCount(2, $vote3); | ||||
|  | ||||
|         // ------- | ||||
|  | ||||
|         $this->election1->parseVotes('voterParsed || candidate1 > candidate2'); | ||||
|  | ||||
|         $votes_lists = $this->election1->getVotesListGenerator('voterParsed', true); | ||||
|         $vote4 = $votes_lists->current(); | ||||
|  | ||||
|         self::assertCount(1, $vote4->getHistory()); | ||||
|  | ||||
|         // ------- | ||||
|  | ||||
|         $vote5 = new Vote([$this->candidate5, $this->candidate6]); | ||||
|  | ||||
|         $this->election1->addVote($vote5); | ||||
|  | ||||
|         self::assertCount(1, $vote5->getHistory()); | ||||
|  | ||||
|         // ------- | ||||
|  | ||||
|         $vote6 = new Vote([$this->candidate5, 'candidate6']); | ||||
|  | ||||
|         $this->election1->addVote($vote6); | ||||
|  | ||||
|         self::assertCount(2, $vote6->getHistory()); | ||||
|  | ||||
|         // ------- | ||||
|  | ||||
|         $vote7 = new Vote([$this->candidate6, 'candidate8']); | ||||
|  | ||||
|         $candidate8 = $vote7->getAllCandidates()[1]; | ||||
|  | ||||
|         self::assertsame('candidate8', $candidate8->getName()); | ||||
|  | ||||
|         self::assertTrue($candidate8->getProvisionalState()); | ||||
|  | ||||
|         $this->election1->addVote($vote7); | ||||
|  | ||||
|         self::assertTrue($candidate8->getProvisionalState()); | ||||
|  | ||||
|         self::assertCount(1, $vote7->getHistory()); | ||||
|     } | ||||
|  | ||||
|     public function testBadRankingInput1(): never | ||||
|     { | ||||
|         $this->expectException(\TypeError::class); | ||||
|  | ||||
|         $vote = new Vote(42); | ||||
|     } | ||||
|  | ||||
|     public function testBadRankingInput2(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid'); | ||||
|  | ||||
|         $candidate = new Candidate('A'); | ||||
|  | ||||
|         $vote = new Vote([$candidate, $candidate]); | ||||
|     } | ||||
|  | ||||
|     public function testEmptyVoteContextualInRanking(): void | ||||
|     { | ||||
|         $vote = $this->election1->addVote('candidate4 > candidate5'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => [$this->candidate1, $this->candidate2, $this->candidate3]], | ||||
|             $vote->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|         $cr = $vote->getContextualRankingAsString($this->election1); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['candidate1', 'candidate2', 'candidate3']], | ||||
|             $cr | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testNonEmptyVoteContextualInRanking(): void | ||||
|     { | ||||
|         $vote = $this->election1->addVote('candidate1 = candidate2 = candidate3'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => [$this->candidate1, $this->candidate2, $this->candidate3]], | ||||
|             $vote->getContextualRanking($this->election1) | ||||
|         ); | ||||
|  | ||||
|         $cr = $vote->getContextualRankingAsString($this->election1); | ||||
|  | ||||
|         self::assertSame( | ||||
|             [1 => ['candidate1', 'candidate2', 'candidate3']], | ||||
|             $cr | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // https://github.com/julien-boudry/Condorcet/issues/32 | ||||
|     public function testDuplicateCandidates1(): never | ||||
|     { | ||||
|         $this->expectException(VoteInvalidFormatException::class); | ||||
|         $this->expectExceptionMessage('The format of the vote is invalid'); | ||||
|  | ||||
|         new Vote('Spain>Japan>France>Netherlands>Australia>France'); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     // https://github.com/julien-boudry/Condorcet/issues/32 | ||||
|     public function testDuplicateCandidates2(): void | ||||
|     { | ||||
|         $election = new Election; | ||||
|         $election->parseCandidates('Spain;Japan;France;Netherlands;Australia'); | ||||
|  | ||||
|         $vote = $election->addVote('Spain>Japan>France>Netherlands>Australia>france'); | ||||
|  | ||||
|         self::assertSame( | ||||
|             'Spain > Japan > France > Netherlands > Australia', | ||||
|             $vote->getSimpleRanking($election) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function testEmptySpecialKeyWord(): void | ||||
|     { | ||||
|         $vote1 = new Vote(CondorcetElectionFormat::SPECIAL_KEYWORD_EMPTY_RANKING); | ||||
|         $vote2 = new Vote('  '.CondorcetElectionFormat::SPECIAL_KEYWORD_EMPTY_RANKING.'  '); | ||||
|  | ||||
|         self::assertSame([], $vote1->getRanking()); | ||||
|         self::assertSame([], $vote2->getRanking()); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user