From 7ae56b56cc7db4c48e7024335364f94e17c55f2e Mon Sep 17 00:00:00 2001 From: domi41 Date: Fri, 14 May 2021 13:46:57 +0200 Subject: [PATCH] feat: add a CollectedTally to ease collection of judgments --- README.md | 47 ++++++++++++ .../java/fr/mieuxvoter/mj/CollectedTally.java | 72 +++++++++++++++++++ src/main/java/fr/mieuxvoter/mj/Tally.java | 3 + .../mj/MajorityJudgmentDeliberatorTest.java | 54 +++++++++++++- 4 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 src/main/java/fr/mieuxvoter/mj/CollectedTally.java diff --git a/README.md b/README.md index eb16e6d..4cc90ac 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,53 @@ TallyInterface tally = new TallyNormalized(new ProposalTallyInterface[] { > This normalization uses the Least Common Multiple, in order to skip floating-point arithmetic. +### Collect a Tally from judgments + +It's usually best to use structured queries (eg: in SQL) directly in your database to collect the tallies, since it scales better with high amounts of participants, but if you must you can collect the tally directly from individual judgments, with a `CollectedTally`. + +```java +Integer amountOfProposals = 3; +Integer amountOfGrades = 4; +DeliberatorInterface mj = new MajorityJudgmentDeliberator(); +CollectedTally tally = new CollectedTally(amountOfProposals, amountOfGrades); + +Integer firstProposal = 0; +Integer secondProposal = 1; +Integer thirdProposal = 2; +Integer gradeReject = 0; +Integer gradePassable = 1; +Integer gradeGood = 2; +Integer gradeExcellent = 3; + +// Collect the judgments one-by-one with `collect()`, for example: +tally.collect(firstProposal, gradeReject); +tally.collect(firstProposal, gradeReject); +tally.collect(firstProposal, gradePassable); +tally.collect(firstProposal, gradePassable); +tally.collect(firstProposal, gradePassable); +tally.collect(firstProposal, gradeExcellent); +tally.collect(firstProposal, gradeExcellent); + +tally.collect(secondProposal, gradeReject); +tally.collect(secondProposal, gradeReject); +tally.collect(secondProposal, gradeGood); +tally.collect(secondProposal, gradeGood); +tally.collect(secondProposal, gradeGood); +tally.collect(secondProposal, gradeExcellent); +tally.collect(secondProposal, gradeExcellent); + +tally.collect(thirdProposal, gradeReject); +tally.collect(thirdProposal, gradePassable); +tally.collect(thirdProposal, gradeGood); +tally.collect(thirdProposal, gradeGood); +tally.collect(thirdProposal, gradeGood); +tally.collect(thirdProposal, gradeExcellent); +tally.collect(thirdProposal, gradeExcellent); + +ResultInterface result = mj.deliberate(tally); +``` + + ## Roadmap - [x] Unit-Tests diff --git a/src/main/java/fr/mieuxvoter/mj/CollectedTally.java b/src/main/java/fr/mieuxvoter/mj/CollectedTally.java new file mode 100644 index 0000000..62c44fe --- /dev/null +++ b/src/main/java/fr/mieuxvoter/mj/CollectedTally.java @@ -0,0 +1,72 @@ +package fr.mieuxvoter.mj; + +import java.math.BigInteger; + +public class CollectedTally implements TallyInterface { + + Integer amountOfProposals = 0; + Integer amountOfGrades = 0; + + ProposalTally[] proposalsTallies; + + public CollectedTally(Integer amountOfProposals, Integer amountOfGrades) { + setAmountOfProposals(amountOfProposals); + setAmountOfGrades(amountOfGrades); + + proposalsTallies = new ProposalTally[amountOfProposals]; + for (int i = 0; i < amountOfProposals; i++) { + ProposalTally proposalTally = new ProposalTally(); + Integer[] tally = new Integer[amountOfGrades]; + for (int j = 0; j < amountOfGrades; j++) { + tally[j] = 0; + } + proposalTally.setTally(tally); + proposalsTallies[i] = proposalTally; + } + } + + @Override + public ProposalTallyInterface[] getProposalsTallies() { + return proposalsTallies; + } + + @Override + public BigInteger getAmountOfJudges() { + return guessAmountOfJudges(); + } + + @Override + public Integer getAmountOfProposals() { + return this.amountOfProposals; + } + + public void setAmountOfProposals(Integer amountOfProposals) { + this.amountOfProposals = amountOfProposals; + } + + public Integer getAmountOfGrades() { + return amountOfGrades; + } + + public void setAmountOfGrades(Integer amountOfGrades) { + this.amountOfGrades = amountOfGrades; + } + + protected BigInteger guessAmountOfJudges() { + BigInteger amountOfJudges = BigInteger.ZERO; + for (ProposalTallyInterface proposalTally : getProposalsTallies()) { + amountOfJudges = proposalTally.getAmountOfJudgments().max(amountOfJudges); + } + return amountOfJudges; + } + + public void collect(Integer proposal, Integer grade) { + assert(0 <= proposal); + assert(amountOfProposals > proposal); + assert(0 <= grade); + assert(amountOfGrades > grade); + + BigInteger[] tally = proposalsTallies[proposal].getTally(); + tally[grade] = tally[grade].add(BigInteger.ONE); + } +} diff --git a/src/main/java/fr/mieuxvoter/mj/Tally.java b/src/main/java/fr/mieuxvoter/mj/Tally.java index 9087a21..91273aa 100644 --- a/src/main/java/fr/mieuxvoter/mj/Tally.java +++ b/src/main/java/fr/mieuxvoter/mj/Tally.java @@ -2,6 +2,9 @@ package fr.mieuxvoter.mj; import java.math.BigInteger; +/** + * A Basic implementation of a TallyInterface that reads from an array of ProposalTallyInterface. + */ public class Tally implements TallyInterface { protected ProposalTallyInterface[] proposalsTallies; diff --git a/src/test/java/fr/mieuxvoter/mj/MajorityJudgmentDeliberatorTest.java b/src/test/java/fr/mieuxvoter/mj/MajorityJudgmentDeliberatorTest.java index 7a1ed55..d0d3215 100644 --- a/src/test/java/fr/mieuxvoter/mj/MajorityJudgmentDeliberatorTest.java +++ b/src/test/java/fr/mieuxvoter/mj/MajorityJudgmentDeliberatorTest.java @@ -94,13 +94,65 @@ class MajorityJudgmentDeliberatorTest { } } + @Test + public void testDemoUsageCollectedTally() { + Integer amountOfProposals = 3; + Integer amountOfGrades = 4; + DeliberatorInterface mj = new MajorityJudgmentDeliberator(); + CollectedTally tally = new CollectedTally(amountOfProposals, amountOfGrades); + + Integer firstProposal = 0; + Integer secondProposal = 1; + Integer thirdProposal = 2; + Integer gradeReject = 0; + Integer gradePassable = 1; + Integer gradeGood = 2; + Integer gradeExcellent = 3; + + tally.collect(firstProposal, gradeReject); + tally.collect(firstProposal, gradeReject); + tally.collect(firstProposal, gradePassable); + tally.collect(firstProposal, gradePassable); + tally.collect(firstProposal, gradePassable); + tally.collect(firstProposal, gradeExcellent); + tally.collect(firstProposal, gradeExcellent); + tally.collect(firstProposal, gradeExcellent); + + tally.collect(secondProposal, gradeReject); + tally.collect(secondProposal, gradeReject); + tally.collect(secondProposal, gradeGood); + tally.collect(secondProposal, gradeGood); + tally.collect(secondProposal, gradeGood); + tally.collect(secondProposal, gradeGood); + tally.collect(secondProposal, gradeExcellent); + tally.collect(secondProposal, gradeExcellent); + + tally.collect(thirdProposal, gradeReject); + tally.collect(thirdProposal, gradeReject); + tally.collect(thirdProposal, gradePassable); + tally.collect(thirdProposal, gradeGood); + tally.collect(thirdProposal, gradeGood); + tally.collect(thirdProposal, gradeGood); + tally.collect(thirdProposal, gradeExcellent); + tally.collect(thirdProposal, gradeExcellent); + + ResultInterface result = mj.deliberate(tally); + + assertNotNull(result); + assertEquals(3, result.getProposalResults().length); + assertEquals(3, result.getProposalResults()[0].getRank()); + assertEquals(1, result.getProposalResults()[1].getRank()); + assertEquals(2, result.getProposalResults()[2].getRank()); + } + @Test public void testWithStaticDefaultGrade() { DeliberatorInterface mj = new MajorityJudgmentDeliberator(); + Integer defaultGrade = 0; TallyInterface tally = new TallyWithDefaultGrade(new ProposalTallyInterface[] { new ProposalTally(new Integer[]{ 0, 0, 1 }), new ProposalTally(new Integer[]{ 0, 3, 0 }), - }, 3L, 0); + }, 3L, defaultGrade); ResultInterface result = mj.deliberate(tally);