1
0

test adding condorcet vote

This commit is contained in:
2022-09-21 12:39:11 +02:00
parent 1e8adfa5d5
commit 0a30f39eb4
349 changed files with 36658 additions and 0 deletions

View 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);
}
}

View File

@ -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

View 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
```

View 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
}
}

View File

@ -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());
}
}

View File

@ -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)
);
}
}

View 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);
}
}

View File

@ -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)
);
}
}

View 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)
);
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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 'KemenyYoung' 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;
}
}

View File

@ -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']
);
}
}

View File

@ -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()
);
}
}

View File

@ -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()
);
}
}

View File

@ -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()
);
}
}

View 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'));
}
}

View 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()
);
}
}

View File

@ -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);
// }
// }
}

View 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();
}
}

View File

@ -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)
);
}
}

View 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)
);
}
}

View 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);
}
}

View 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());
}
}

View 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());
}
}

View 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());
}
}

View 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('kemenyYoung')
);
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;
}
}

View 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);
}
}

View 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));
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,3 @@
A
B
C

View File

@ -0,0 +1,2 @@
A>B=C
B>A>C

View 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;
}
}

View 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]);
}
}

View File

@ -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));
}
}

View 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);
}
}

View 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());
}
}

View 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());
}
}

View File

@ -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);
}
}

View 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);
}
}

View File

@ -0,0 +1,5 @@
#/Candidates:A;B;C
#/Implicit Ranking: true
#/Weight Allowed: false
A>B ^2

View File

@ -0,0 +1,7 @@
#/Candidates:A;B;C
#/Implicit Ranking: false
#/Weight Allowed: false
#Number of Seats: 42
B>A ^2
B>C

View File

@ -0,0 +1,3 @@
#/Candidates:A;B;C
C>B>A * 700

View File

@ -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');
}
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()
);
}
}

View 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"


View 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"

View 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"

View 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"

View 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,
],
];
}
}

View 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'],
];
}
}

View 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());
}
}