Merge pull request #4 from MieuxVoter/issue-3

Prepare for a more exhaustive test-suite, using JSON
pull/8/head
Dominique Merle 3 years ago committed by GitHub
commit ba83ad8224
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -19,7 +19,8 @@ repositories {
dependencies { dependencies {
// Use the JUnit test framework with assertions and benchmarks // Use the JUnit test framework with assertions and benchmarks
testImplementation 'org.junit.jupiter:junit-jupiter:5.6.3' testImplementation 'org.junit.jupiter:junit-jupiter:5.6.3'
testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.21' testImplementation 'net.joshka:junit-json-params:1.1.0'
//testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.21'
// This dependency is exported to consumers, that is to say found on their compile classpath. // This dependency is exported to consumers, that is to say found on their compile classpath.
//api 'org.apache.commons:commons-math3:3.6.1' //api 'org.apache.commons:commons-math3:3.6.1'

@ -6,7 +6,7 @@
<groupId>fr.mieuxvoter.mj</groupId> <groupId>fr.mieuxvoter.mj</groupId>
<artifactId>majority-judgment</artifactId> <artifactId>majority-judgment</artifactId>
<version>0.1.0</version> <version>0.1.1</version>
<name>majority-judgment</name> <name>majority-judgment</name>
<url>https://mieuxvoter.fr</url> <url>https://mieuxvoter.fr</url>
@ -24,7 +24,18 @@
<version>5.6.3</version> <version>5.6.3</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>net.joshka</groupId>
<artifactId>junit-json-params</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.4</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

@ -1,5 +1,6 @@
package fr.mieuxvoter.mj; package fr.mieuxvoter.mj;
import java.math.BigInteger;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
@ -24,7 +25,7 @@ public class MajorityJudgmentDeliberator implements DeliberatorInterface {
public ResultInterface deliberate(TallyInterface tally) { public ResultInterface deliberate(TallyInterface tally) {
ProposalTallyInterface[] tallies = tally.getProposalsTallies(); ProposalTallyInterface[] tallies = tally.getProposalsTallies();
Long amountOfJudges = tally.getAmountOfJudges(); BigInteger amountOfJudges = tally.getAmountOfJudges();
Integer amountOfProposals = tally.getAmountOfProposals(); Integer amountOfProposals = tally.getAmountOfProposals();
Result result = new Result(); Result result = new Result();
@ -69,13 +70,13 @@ public class MajorityJudgmentDeliberator implements DeliberatorInterface {
return result; return result;
} }
public String computeScore(ProposalTallyInterface tally, Long amountOfJudges) { public String computeScore(ProposalTallyInterface tally, BigInteger amountOfJudges) {
return computeScore(tally, amountOfJudges, true, false); return computeScore(tally, amountOfJudges, true, false);
} }
public String computeScore( public String computeScore(
ProposalTallyInterface tally, ProposalTallyInterface tally,
Long amountOfJudges, BigInteger amountOfJudges,
Boolean favorContestation, Boolean favorContestation,
Boolean onlyNumbers Boolean onlyNumbers
) { ) {
@ -99,15 +100,19 @@ public class MajorityJudgmentDeliberator implements DeliberatorInterface {
"%0"+digitsForGrade+"d", "%0"+digitsForGrade+"d",
analysis.getMedianGrade() analysis.getMedianGrade()
); );
if (! onlyNumbers) { if ( ! onlyNumbers) {
score += "_"; score += "_";
} }
score += String.format( score += String.format(
"%0"+digitsForGroup+"d", "%0"+digitsForGroup+"d",
amountOfJudges + analysis.getSecondMedianGroupSize() * analysis.getSecondMedianGroupSign() // amountOfJudges + secondMedianGroupSize * secondMedianGroupSign
amountOfJudges.add(
analysis.getSecondMedianGroupSize().multiply(
BigInteger.valueOf(analysis.getSecondMedianGroupSign())
)
)
); );
currentTally.moveJudgments(analysis.getMedianGrade(), analysis.getSecondMedianGrade()); currentTally.moveJudgments(analysis.getMedianGrade(), analysis.getSecondMedianGrade());

@ -1,33 +1,43 @@
package fr.mieuxvoter.mj; package fr.mieuxvoter.mj;
import java.math.BigInteger;
import java.util.Arrays; import java.util.Arrays;
public class ProposalTally implements ProposalTallyInterface { public class ProposalTally implements ProposalTallyInterface {
protected Long[] tally; protected BigInteger[] tally;
// Should we allow this as well? // Should we allow this as well?
//public ProposalTally() {} //public ProposalTally() {}
public ProposalTally(Integer[] tally) { public ProposalTally(Integer[] tally) {
int tallyLength = tally.length; int tallyLength = tally.length;
Long[] doublesTally = new Long[tallyLength]; BigInteger[] bigTally = new BigInteger[tallyLength];
for (int i = 0 ; i < tallyLength ; i++) { for (int i = 0 ; i < tallyLength ; i++) {
doublesTally[i] = Long.valueOf(tally[i]); bigTally[i] = BigInteger.valueOf(tally[i]);
} }
setTally(doublesTally); setTally(bigTally);
} }
public ProposalTally(Long[] tally) { public ProposalTally(Long[] tally) {
int tallyLength = tally.length;
BigInteger[] bigTally = new BigInteger[tallyLength];
for (int i = 0 ; i < tallyLength ; i++) {
bigTally[i] = BigInteger.valueOf(tally[i]);
}
setTally(bigTally);
}
public ProposalTally(BigInteger[] tally) {
setTally(tally); setTally(tally);
} }
public void setTally(Long[] tally) { public void setTally(BigInteger[] tally) {
this.tally = tally; this.tally = tally;
} }
@Override @Override
public Long[] getTally() { public BigInteger[] getTally() {
return this.tally; return this.tally;
} }
@ -38,8 +48,19 @@ public class ProposalTally implements ProposalTallyInterface {
@Override @Override
public void moveJudgments(Integer fromGrade, Integer intoGrade) { public void moveJudgments(Integer fromGrade, Integer intoGrade) {
this.tally[intoGrade] += this.tally[fromGrade]; // this.tally[intoGrade] += this.tally[fromGrade];
this.tally[fromGrade] = 0L; this.tally[intoGrade] = this.tally[intoGrade].add(this.tally[fromGrade]);
this.tally[fromGrade] = BigInteger.ZERO;
}
@Override
public BigInteger getAmountOfJudgments() {
BigInteger sum = BigInteger.ZERO;
int tallyLength = this.tally.length;
for (int i = 0 ; i < tallyLength ; i++) {
sum = sum.add(this.tally[i]);
}
return sum;
} }
} }

@ -1,35 +1,42 @@
package fr.mieuxvoter.mj; package fr.mieuxvoter.mj;
import java.math.BigInteger;
/** /**
* Collect useful data on a proposal tally. * Collect useful data on a proposal tally.
* Does NOT compute the rank, but provides all we need * Does NOT compute the rank, but provides all we need.
*
* This uses BigInteger because in a normalization scenario we use the
* smallest common multiple of the amounts of judges of proposals.
* It makes the code harder to read and understand, but it allows us
* to bypass the floating-point nightmare of the normalization of merit profiles,
* which is one way to handle default grades on some polls.
*/ */
public class ProposalTallyAnalysis { public class ProposalTallyAnalysis {
protected ProposalTallyInterface tally; protected ProposalTallyInterface tally;
protected Long totalSize = 0L; // amount of judges protected BigInteger totalSize = BigInteger.ZERO; // amount of judges
protected Integer medianGrade = 0; protected Integer medianGrade = 0;
protected Long medianGroupSize = 0L; // amount of judges in the median group protected BigInteger medianGroupSize = BigInteger.ZERO; // amount of judges in the median group
protected Integer contestationGrade = 0; // "best" grade of the contestation group protected Integer contestationGrade = 0; // "best" grade of the contestation group
protected Long contestationGroupSize = 0L; // of lower grades than median protected BigInteger contestationGroupSize = BigInteger.ZERO; // of lower grades than median
protected Integer adhesionGrade = 0; // "worst" grade of the adhesion group protected Integer adhesionGrade = 0; // "worst" grade of the adhesion group
protected Long adhesionGroupSize = 0L; // of higher grades than median protected BigInteger adhesionGroupSize = BigInteger.ZERO; // of higher grades than median
protected Integer secondMedianGrade = 0; // grade of the biggest group out of the median protected Integer secondMedianGrade = 0; // grade of the biggest group out of the median
protected Long secondMedianGroupSize = 0L; // either contestation or adhesion protected BigInteger secondMedianGroupSize = BigInteger.ZERO; // either contestation or adhesion
protected Integer secondMedianGroupSign = 0; // -1 for contestation, +1 for adhesion, 0 for empty group size protected Integer secondMedianGroupSign = 0; // -1 for contestation, +1 for adhesion, 0 for empty group size
public ProposalTallyAnalysis() {} public ProposalTallyAnalysis() {}
public ProposalTallyAnalysis(ProposalTallyInterface tally) { public ProposalTallyAnalysis(ProposalTallyInterface tally) {
@ -42,56 +49,58 @@ public class ProposalTallyAnalysis {
public void reanalyze(ProposalTallyInterface tally, Boolean favorContestation) { public void reanalyze(ProposalTallyInterface tally, Boolean favorContestation) {
this.tally = tally; this.tally = tally;
this.totalSize = 0L; this.totalSize = BigInteger.ZERO;
this.medianGrade = 0; this.medianGrade = 0;
this.medianGroupSize = 0L; this.medianGroupSize = BigInteger.ZERO;
this.contestationGrade = 0; this.contestationGrade = 0;
this.contestationGroupSize = 0L; this.contestationGroupSize = BigInteger.ZERO;
this.adhesionGrade = 0; this.adhesionGrade = 0;
this.adhesionGroupSize = 0L; this.adhesionGroupSize = BigInteger.ZERO;
this.secondMedianGrade = 0; this.secondMedianGrade = 0;
this.secondMedianGroupSize = 0L; this.secondMedianGroupSize = BigInteger.ZERO;
this.secondMedianGroupSign = 0; this.secondMedianGroupSign = 0;
Long[] gradesTallies = this.tally.getTally(); BigInteger[] gradesTallies = this.tally.getTally();
int amountOfGrades = gradesTallies.length; int amountOfGrades = gradesTallies.length;
for (int grade = 0; grade < amountOfGrades; grade++) { for (int grade = 0; grade < amountOfGrades; grade++) {
Long gradeTally = gradesTallies[grade]; BigInteger gradeTally = gradesTallies[grade];
assert(0 <= gradeTally); // Negative tallies are not allowed. //assert(0 <= gradeTally); // Negative tallies are not allowed.
this.totalSize += gradeTally; this.totalSize = this.totalSize.add(gradeTally);
} }
Integer medianOffset = 1; Integer medianOffset = 1;
if ( ! favorContestation) { if ( ! favorContestation) {
medianOffset = 2; medianOffset = 2;
} }
Long medianCursor = (long) Math.floor((this.totalSize + medianOffset) / 2.0); BigInteger medianCursor = this.totalSize.add(BigInteger.valueOf(medianOffset)).divide(BigInteger.TWO);
// Long medianCursor = (long) Math.floor((this.totalSize + medianOffset) / 2.0);
Long tallyBeforeCursor = 0L; BigInteger tallyBeforeCursor = BigInteger.ZERO;
Long tallyCursor = 0L; BigInteger tallyCursor = BigInteger.ZERO;
Boolean foundMedian = false; Boolean foundMedian = false;
Integer contestationGrade = 0; Integer contestationGrade = 0;
Integer adhesionGrade = 0; Integer adhesionGrade = 0;
for (int grade = 0; grade < amountOfGrades; grade++) { for (int grade = 0; grade < amountOfGrades; grade++) {
Long gradeTally = gradesTallies[grade]; BigInteger gradeTally = gradesTallies[grade];
tallyBeforeCursor = tallyCursor; tallyBeforeCursor = tallyCursor;
tallyCursor += gradeTally; tallyCursor = tallyCursor.add(gradeTally);
if ( ! foundMedian) { if ( ! foundMedian) {
if (tallyCursor >= medianCursor) { if (-1 < tallyCursor.compareTo(medianCursor)) { // tallyCursor >= medianCursor
foundMedian = true; foundMedian = true;
this.medianGrade = grade; this.medianGrade = grade;
this.contestationGroupSize = tallyBeforeCursor; this.contestationGroupSize = tallyBeforeCursor;
this.medianGroupSize = gradeTally; this.medianGroupSize = gradeTally;
this.adhesionGroupSize = this.totalSize - this.contestationGroupSize - this.medianGroupSize; // this.adhesionGroupSize = this.totalSize - this.contestationGroupSize - this.medianGroupSize;
this.adhesionGroupSize = this.totalSize.subtract(this.contestationGroupSize).subtract(this.medianGroupSize);
} else { } else {
if (0 < gradeTally) { if (1 == gradeTally.compareTo(BigInteger.ZERO)) { // 0 < gradeTally
contestationGrade = grade; contestationGrade = grade;
} }
} }
} else { } else {
if (0 < gradeTally && 0 == adhesionGrade) { if (1 == gradeTally.compareTo(BigInteger.ZERO) && 0 == adhesionGrade) {
adhesionGrade = grade; adhesionGrade = grade;
} }
} }
@ -99,12 +108,15 @@ public class ProposalTallyAnalysis {
this.contestationGrade = contestationGrade; this.contestationGrade = contestationGrade;
this.adhesionGrade = adhesionGrade; this.adhesionGrade = adhesionGrade;
this.secondMedianGroupSize = Math.max(this.contestationGroupSize, this.adhesionGroupSize); // this.secondMedianGroupSize = Math.max(this.contestationGroupSize, this.adhesionGroupSize);
this.secondMedianGroupSize = this.contestationGroupSize.max(this.adhesionGroupSize);
this.secondMedianGroupSign = 0; this.secondMedianGroupSign = 0;
if (this.contestationGroupSize < this.adhesionGroupSize) { // if (this.contestationGroupSize < this.adhesionGroupSize) {
if (1 == this.adhesionGroupSize.compareTo(this.contestationGroupSize)) {
this.secondMedianGrade = this.adhesionGrade; this.secondMedianGrade = this.adhesionGrade;
this.secondMedianGroupSign = 1; this.secondMedianGroupSign = 1;
} else if (this.contestationGroupSize > this.adhesionGroupSize) { // } else if (this.contestationGroupSize > this.adhesionGroupSize) {
} else if (1 == this.contestationGroupSize.compareTo(this.adhesionGroupSize)) {
this.secondMedianGrade = this.contestationGrade; this.secondMedianGrade = this.contestationGrade;
this.secondMedianGroupSign = -1; this.secondMedianGroupSign = -1;
} else { } else {
@ -116,16 +128,16 @@ public class ProposalTallyAnalysis {
this.secondMedianGroupSign = 1; this.secondMedianGroupSign = 1;
} }
} }
if (0 == this.secondMedianGroupSize) { if (0 == this.secondMedianGroupSize.compareTo(BigInteger.ZERO)) {
this.secondMedianGroupSign = 0; this.secondMedianGroupSign = 0;
} }
} }
public Long getTotalSize() { public BigInteger getTotalSize() {
return totalSize; return totalSize;
} }
public void setTotalSize(Long totalSize) { public void setTotalSize(BigInteger totalSize) {
this.totalSize = totalSize; this.totalSize = totalSize;
} }
@ -136,12 +148,12 @@ public class ProposalTallyAnalysis {
public void setMedianGrade(Integer medianGrade) { public void setMedianGrade(Integer medianGrade) {
this.medianGrade = medianGrade; this.medianGrade = medianGrade;
} }
public Long getMedianGroupSize() { public BigInteger getMedianGroupSize() {
return medianGroupSize; return medianGroupSize;
} }
public void setMedianGroupSize(Long medianGroupSize) { public void setMedianGroupSize(BigInteger medianGroupSize) {
this.medianGroupSize = medianGroupSize; this.medianGroupSize = medianGroupSize;
} }
@ -153,11 +165,11 @@ public class ProposalTallyAnalysis {
this.contestationGrade = contestationGrade; this.contestationGrade = contestationGrade;
} }
public Long getContestationGroupSize() { public BigInteger getContestationGroupSize() {
return contestationGroupSize; return contestationGroupSize;
} }
public void setContestationGroupSize(Long contestationGroupSize) { public void setContestationGroupSize(BigInteger contestationGroupSize) {
this.contestationGroupSize = contestationGroupSize; this.contestationGroupSize = contestationGroupSize;
} }
@ -168,15 +180,15 @@ public class ProposalTallyAnalysis {
public void setAdhesionGrade(Integer adhesionGrade) { public void setAdhesionGrade(Integer adhesionGrade) {
this.adhesionGrade = adhesionGrade; this.adhesionGrade = adhesionGrade;
} }
public Long getAdhesionGroupSize() { public BigInteger getAdhesionGroupSize() {
return adhesionGroupSize; return adhesionGroupSize;
} }
public void setAdhesionGroupSize(Long adhesionGroupSize) { public void setAdhesionGroupSize(BigInteger adhesionGroupSize) {
this.adhesionGroupSize = adhesionGroupSize; this.adhesionGroupSize = adhesionGroupSize;
} }
public Integer getSecondMedianGrade() { public Integer getSecondMedianGrade() {
return secondMedianGrade; return secondMedianGrade;
} }
@ -185,11 +197,11 @@ public class ProposalTallyAnalysis {
this.secondMedianGrade = secondMedianGrade; this.secondMedianGrade = secondMedianGrade;
} }
public Long getSecondMedianGroupSize() { public BigInteger getSecondMedianGroupSize() {
return secondMedianGroupSize; return secondMedianGroupSize;
} }
public void setSecondMedianGroupSize(Long secondMedianGroupSize) { public void setSecondMedianGroupSize(BigInteger secondMedianGroupSize) {
this.secondMedianGroupSize = secondMedianGroupSize; this.secondMedianGroupSize = secondMedianGroupSize;
} }

@ -1,11 +1,22 @@
package fr.mieuxvoter.mj; package fr.mieuxvoter.mj;
import java.math.BigInteger;
public interface ProposalTallyInterface { public interface ProposalTallyInterface {
/** /**
* The amount of judgments received for each Grade, from "worst" Grade to "best" Grade. * The tallies of each Grade, that is
* the amount of judgments received for each Grade by the Proposal,
* from "worst" ("most conservative") Grade to "best" Grade.
*/
public BigInteger[] getTally();
/**
* Should be the sum of getTally()
*
* @return The total amount of judgments received by this proposal.
*/ */
public Long[] getTally(); public BigInteger getAmountOfJudgments();
/** /**
* Homemade factory to skip the clone() shenanigans. * Homemade factory to skip the clone() shenanigans.

@ -1,19 +1,26 @@
package fr.mieuxvoter.mj; package fr.mieuxvoter.mj;
import java.math.BigInteger;
public class Tally implements TallyInterface { public class Tally implements TallyInterface {
protected ProposalTallyInterface[] proposalsTallies; protected ProposalTallyInterface[] proposalsTallies;
protected Long amountOfJudges = 0L; protected BigInteger amountOfJudges = BigInteger.ZERO;
public Tally(ProposalTallyInterface[] proposalsTallies, BigInteger amountOfJudges) {
setProposalsTallies(proposalsTallies);
setAmountOfJudges(amountOfJudges);
}
public Tally(ProposalTallyInterface[] proposalsTallies, Long amountOfJudges) { public Tally(ProposalTallyInterface[] proposalsTallies, Long amountOfJudges) {
setProposalsTallies(proposalsTallies); setProposalsTallies(proposalsTallies);
setAmountOfJudges(amountOfJudges); setAmountOfJudges(BigInteger.valueOf(amountOfJudges));
} }
public Tally(ProposalTallyInterface[] proposalsTallies, Integer amountOfJudges) { public Tally(ProposalTallyInterface[] proposalsTallies, Integer amountOfJudges) {
setProposalsTallies(proposalsTallies); setProposalsTallies(proposalsTallies);
setAmountOfJudges(Long.valueOf(amountOfJudges)); setAmountOfJudges(BigInteger.valueOf(amountOfJudges));
} }
public ProposalTallyInterface[] getProposalsTallies() { public ProposalTallyInterface[] getProposalsTallies() {
@ -28,11 +35,11 @@ public class Tally implements TallyInterface {
return proposalsTallies.length; return proposalsTallies.length;
} }
public Long getAmountOfJudges() { public BigInteger getAmountOfJudges() {
return amountOfJudges; return amountOfJudges;
} }
public void setAmountOfJudges(Long amountOfJudges) { public void setAmountOfJudges(BigInteger amountOfJudges) {
this.amountOfJudges = amountOfJudges; this.amountOfJudges = amountOfJudges;
} }

@ -1,11 +1,13 @@
package fr.mieuxvoter.mj; package fr.mieuxvoter.mj;
import java.math.BigInteger;
public interface TallyInterface { public interface TallyInterface {
public ProposalTallyInterface[] getProposalsTallies(); public ProposalTallyInterface[] getProposalsTallies();
public Long getAmountOfJudges(); public BigInteger getAmountOfJudges();
public Integer getAmountOfProposals(); public Integer getAmountOfProposals();
} }

@ -0,0 +1,41 @@
package fr.mieuxvoter.mj;
import java.math.BigInteger;
public class TallyWithDefaultGrade extends Tally implements TallyInterface {
protected Integer defaultGrade = 0;
public TallyWithDefaultGrade(ProposalTallyInterface[] proposalsTallies, BigInteger amountOfJudges, Integer defaultGrade) {
super(proposalsTallies, amountOfJudges);
this.defaultGrade = defaultGrade;
fillWithDefaultGrade();
}
public TallyWithDefaultGrade(ProposalTallyInterface[] proposalsTallies, Long amountOfJudges, Integer defaultGrade) {
super(proposalsTallies, amountOfJudges);
this.defaultGrade = defaultGrade;
fillWithDefaultGrade();
}
public TallyWithDefaultGrade(ProposalTallyInterface[] proposalsTallies, Integer amountOfJudges, Integer defaultGrade) {
super(proposalsTallies, amountOfJudges);
this.defaultGrade = defaultGrade;
fillWithDefaultGrade();
}
protected void fillWithDefaultGrade() {
int amountOfProposals = getAmountOfProposals();
for (int i = 0 ; i < amountOfProposals ; i++) {
ProposalTallyInterface proposal = getProposalsTallies()[i];
BigInteger amountOfJudgments = proposal.getAmountOfJudgments();
BigInteger missingAmount = this.amountOfJudges.subtract(amountOfJudgments);
int missingSign = missingAmount.compareTo(BigInteger.ZERO);
assert(0 <= missingSign); // ERROR: More judgments than judges!
if (0 < missingSign) {
proposal.getTally()[this.defaultGrade] = proposal.getTally()[this.defaultGrade].add(missingAmount);
}
}
}
}

@ -2,30 +2,36 @@ package fr.mieuxvoter.mj;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonValue;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import net.joshka.junit.json.params.JsonFileSource;
class MajorityJudgmentDeliberatorTest { class MajorityJudgmentDeliberatorTest {
@Test @Test
void testDemoUsage() { public void testDemoUsage() {
DeliberatorInterface mj = new MajorityJudgmentDeliberator(); DeliberatorInterface mj = new MajorityJudgmentDeliberator();
TallyInterface tally = new Tally(new ProposalTallyInterface[] { TallyInterface tally = new Tally(new ProposalTallyInterface[] {
new ProposalTally(new Integer[]{4, 5, 2, 1, 3, 1, 2}), new ProposalTally(new Integer[]{4, 5, 2, 1, 3, 1, 2}),
new ProposalTally(new Integer[]{3, 6, 2, 1, 3, 1, 2}), new ProposalTally(new Integer[]{3, 6, 2, 1, 3, 1, 2}),
}, 18L); }, 18L);
ResultInterface result = mj.deliberate(tally);
// System.out.println("Score 0: "+result.getProposalResults()[0].getScore()); ResultInterface result = mj.deliberate(tally);
// System.out.println("Score 1: "+result.getProposalResults()[1].getScore());
assertNotNull(result); assertNotNull(result);
assertEquals(2, result.getProposalResults().length); assertEquals(2, result.getProposalResults().length);
assertEquals(2, result.getProposalResults()[0].getRank()); assertEquals(2, result.getProposalResults()[0].getRank());
assertEquals(1, result.getProposalResults()[1].getRank()); assertEquals(1, result.getProposalResults()[1].getRank());
} }
@Test @Test
void testUsageWithBigNumbers() { public void testUsageWithBigNumbers() {
DeliberatorInterface mj = new MajorityJudgmentDeliberator(); DeliberatorInterface mj = new MajorityJudgmentDeliberator();
TallyInterface tally = new Tally(new ProposalTallyInterface[] { TallyInterface tally = new Tally(new ProposalTallyInterface[] {
new ProposalTally(new Long[]{11312415004L, 21153652410L, 24101523299L, 18758623562L}), new ProposalTally(new Long[]{11312415004L, 21153652410L, 24101523299L, 18758623562L}),
@ -44,4 +50,70 @@ class MajorityJudgmentDeliberatorTest {
assertEquals(1, result.getProposalResults()[1].getRank()); assertEquals(1, result.getProposalResults()[1].getRank());
} }
@DisplayName("Test majority judgment deliberation")
@ParameterizedTest(name="#{index} {0}")
@JsonFileSource(resources = "/assertions.json")
public void testFromJson(JsonObject datum) {
JsonArray jsonTallies = datum.getJsonArray("tallies");
int amountOfProposals = jsonTallies.size();
Long amountOfParticipants = Long.valueOf(datum.get("participants").toString());
ProposalTallyInterface[] tallies = new ProposalTallyInterface[amountOfProposals];
for (int i = 0; i < amountOfProposals; i++) {
JsonArray jsonTally = jsonTallies.getJsonArray(i);
int amountOfGrades = jsonTally.size();
Long[] tally = new Long[amountOfGrades];
for (int g = 0; g < amountOfGrades; g++) {
JsonValue amountForGrade = jsonTally.get(g);
tally[g] = Long.valueOf(amountForGrade.toString());
}
tallies[i] = new ProposalTally(tally);
}
DeliberatorInterface mj = new MajorityJudgmentDeliberator();
TallyInterface tally = new Tally(tallies, amountOfParticipants);
ResultInterface result = mj.deliberate(tally);
assertNotNull(result);
JsonArray jsonRanks = datum.getJsonArray("ranks");
for (int i = 0; i < amountOfProposals; i++) {
assertEquals(
jsonRanks.getInt(i),
result.getProposalResults()[i].getRank(),
"Rank of tally #"+i
);
}
}
@Test
public void testWithStaticDefaultGrade() {
DeliberatorInterface mj = new MajorityJudgmentDeliberator();
TallyInterface tally = new TallyWithDefaultGrade(new ProposalTallyInterface[] {
new ProposalTally(new Integer[]{ 0, 0, 1 }),
new ProposalTally(new Integer[]{ 0, 3, 0 }),
}, 3L, 0);
ResultInterface result = mj.deliberate(tally);
assertNotNull(result);
assertEquals(2, result.getProposalResults().length);
assertEquals(2, result.getProposalResults()[0].getRank());
assertEquals(1, result.getProposalResults()[1].getRank());
}
// @Test
// public void runBenchmarks() throws Exception {
// Options options = new OptionsBuilder()
// .include(this.getClass().getName() + ".*")
// .mode(Mode.AverageTime)
// .warmupTime(TimeValue.seconds(1))
// .warmupIterations(6)
// .threads(1)
// .measurementIterations(6)
// .forks(1)
// .shouldFailOnError(true)
// .shouldDoGC(true)
// .build();
//
// new Runner(options).run();
// }
} }

@ -0,0 +1,16 @@
Do add your test cases in the JSON file.
Some of the sample tallies were made using python.
```python
import numpy as np
def randofsum_unbalanced(s, n):
# Where s = sum (e.g. 40 in your case) and n is the output array length (e.g. 4 in your case)
r = np.random.rand(n)
a = np.array(np.round((r/np.sum(r))*s,0),dtype=int)
while np.sum(a) > s:
a[np.random.choice(n)] -= 1
while np.sum(a) < s:
a[np.random.choice(n)] += 1
return a
```

@ -0,0 +1,54 @@
[
{
"title": "Few participants",
"participants": 3,
"tallies": [
[1, 1, 1],
[1, 0, 2],
[3, 0, 0],
[2, 0, 1],
[0, 3, 0]
],
"ranks": [
3,
1,
5,
4,
2
]
},
{
"title": "Thousands of participants",
"participants": 37000,
"tallies": [
[11142, 6970, 4040, 1968, 9888, 2992],
[10141, 8971, 4043, 1965, 8884, 2996],
[14141, 8971, 1043, 1965, 7884, 2996]
],
"ranks": [
1,
2,
3
]
},
{
"title": "Millions of participants",
"participants": 72327456,
"tallies": [
[5272679, 19797001, 10732688, 9612936, 1379840, 16886281, 8646031],
[16354546, 11690342, 9451800, 14245973, 817593, 12461162, 7306040],
[9849171, 17970690, 14276861, 4606692, 16404594, 6760147, 2459301],
[2645563, 12907474, 1278331, 22843261, 8025412, 8964952, 15662463],
[16293252, 12277630, 38348, 14929905, 11087753, 10634266, 7066302]
],
"ranks": [
3,
4,
5,
1,
2
]
}
]
Loading…
Cancel
Save