diff --git a/src/main/java/fr/mieuxvoter/mj/MajorityJudgmentDeliberator.java b/src/main/java/fr/mieuxvoter/mj/MajorityJudgmentDeliberator.java index b222d86..53bfa83 100644 --- a/src/main/java/fr/mieuxvoter/mj/MajorityJudgmentDeliberator.java +++ b/src/main/java/fr/mieuxvoter/mj/MajorityJudgmentDeliberator.java @@ -1,5 +1,6 @@ package fr.mieuxvoter.mj; +import java.math.BigInteger; import java.util.Arrays; import java.util.Comparator; @@ -24,7 +25,7 @@ public class MajorityJudgmentDeliberator implements DeliberatorInterface { public ResultInterface deliberate(TallyInterface tally) { ProposalTallyInterface[] tallies = tally.getProposalsTallies(); - Long amountOfJudges = tally.getAmountOfJudges(); + BigInteger amountOfJudges = tally.getAmountOfJudges(); Integer amountOfProposals = tally.getAmountOfProposals(); Result result = new Result(); @@ -69,13 +70,13 @@ public class MajorityJudgmentDeliberator implements DeliberatorInterface { return result; } - public String computeScore(ProposalTallyInterface tally, Long amountOfJudges) { + public String computeScore(ProposalTallyInterface tally, BigInteger amountOfJudges) { return computeScore(tally, amountOfJudges, true, false); } public String computeScore( ProposalTallyInterface tally, - Long amountOfJudges, + BigInteger amountOfJudges, Boolean favorContestation, Boolean onlyNumbers ) { @@ -99,15 +100,19 @@ public class MajorityJudgmentDeliberator implements DeliberatorInterface { "%0"+digitsForGrade+"d", analysis.getMedianGrade() ); - + if ( ! onlyNumbers) { score += "_"; } - score += String.format( "%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()); diff --git a/src/main/java/fr/mieuxvoter/mj/ProposalTally.java b/src/main/java/fr/mieuxvoter/mj/ProposalTally.java index 2c4eedd..3ec19fe 100644 --- a/src/main/java/fr/mieuxvoter/mj/ProposalTally.java +++ b/src/main/java/fr/mieuxvoter/mj/ProposalTally.java @@ -5,30 +5,39 @@ import java.util.Arrays; public class ProposalTally implements ProposalTallyInterface { - protected Long[] tally; + protected BigInteger[] tally; // Should we allow this as well? //public ProposalTally() {} public ProposalTally(Integer[] tally) { int tallyLength = tally.length; - Long[] doublesTally = new Long[tallyLength]; + BigInteger[] bigTally = new BigInteger[tallyLength]; 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) { + 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); } - public void setTally(Long[] tally) { + public void setTally(BigInteger[] tally) { this.tally = tally; } @Override - public Long[] getTally() { + public BigInteger[] getTally() { return this.tally; } @@ -39,16 +48,17 @@ public class ProposalTally implements ProposalTallyInterface { @Override public void moveJudgments(Integer fromGrade, Integer intoGrade) { - this.tally[intoGrade] += this.tally[fromGrade]; - this.tally[fromGrade] = 0L; +// this.tally[intoGrade] += this.tally[fromGrade]; + this.tally[intoGrade] = this.tally[intoGrade].add(this.tally[fromGrade]); + this.tally[fromGrade] = BigInteger.ZERO; } @Override public BigInteger getAmountOfJudgments() { - BigInteger sum = BigInteger.valueOf(0); + BigInteger sum = BigInteger.ZERO; int tallyLength = this.tally.length; for (int i = 0 ; i < tallyLength ; i++) { - sum = sum.add(BigInteger.valueOf(this.tally[i])); + sum = sum.add(this.tally[i]); } return sum; } diff --git a/src/main/java/fr/mieuxvoter/mj/ProposalTallyAnalysis.java b/src/main/java/fr/mieuxvoter/mj/ProposalTallyAnalysis.java index 8fa71bf..a15e933 100644 --- a/src/main/java/fr/mieuxvoter/mj/ProposalTallyAnalysis.java +++ b/src/main/java/fr/mieuxvoter/mj/ProposalTallyAnalysis.java @@ -1,35 +1,42 @@ package fr.mieuxvoter.mj; +import java.math.BigInteger; /** * 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 { protected ProposalTallyInterface tally; - - protected Long totalSize = 0L; // amount of judges + + protected BigInteger totalSize = BigInteger.ZERO; // amount of judges 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 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 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 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 - + public ProposalTallyAnalysis() {} public ProposalTallyAnalysis(ProposalTallyInterface tally) { @@ -42,56 +49,58 @@ public class ProposalTallyAnalysis { public void reanalyze(ProposalTallyInterface tally, Boolean favorContestation) { this.tally = tally; - this.totalSize = 0L; + this.totalSize = BigInteger.ZERO; this.medianGrade = 0; - this.medianGroupSize = 0L; + this.medianGroupSize = BigInteger.ZERO; this.contestationGrade = 0; - this.contestationGroupSize = 0L; + this.contestationGroupSize = BigInteger.ZERO; this.adhesionGrade = 0; - this.adhesionGroupSize = 0L; + this.adhesionGroupSize = BigInteger.ZERO; this.secondMedianGrade = 0; - this.secondMedianGroupSize = 0L; + this.secondMedianGroupSize = BigInteger.ZERO; this.secondMedianGroupSign = 0; - Long[] gradesTallies = this.tally.getTally(); + BigInteger[] gradesTallies = this.tally.getTally(); int amountOfGrades = gradesTallies.length; for (int grade = 0; grade < amountOfGrades; grade++) { - Long gradeTally = gradesTallies[grade]; - assert(0 <= gradeTally); // Negative tallies are not allowed. - this.totalSize += gradeTally; + BigInteger gradeTally = gradesTallies[grade]; + //assert(0 <= gradeTally); // Negative tallies are not allowed. + this.totalSize = this.totalSize.add(gradeTally); } Integer medianOffset = 1; if ( ! favorContestation) { 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; - Long tallyCursor = 0L; + BigInteger tallyBeforeCursor = BigInteger.ZERO; + BigInteger tallyCursor = BigInteger.ZERO; Boolean foundMedian = false; Integer contestationGrade = 0; Integer adhesionGrade = 0; for (int grade = 0; grade < amountOfGrades; grade++) { - Long gradeTally = gradesTallies[grade]; + BigInteger gradeTally = gradesTallies[grade]; tallyBeforeCursor = tallyCursor; - tallyCursor += gradeTally; + tallyCursor = tallyCursor.add(gradeTally); if ( ! foundMedian) { - if (tallyCursor >= medianCursor) { + if (-1 < tallyCursor.compareTo(medianCursor)) { // tallyCursor >= medianCursor foundMedian = true; this.medianGrade = grade; this.contestationGroupSize = tallyBeforeCursor; 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 { - if (0 < gradeTally) { + if (1 == gradeTally.compareTo(BigInteger.ZERO)) { // 0 < gradeTally contestationGrade = grade; } } } else { - if (0 < gradeTally && 0 == adhesionGrade) { + if (1 == gradeTally.compareTo(BigInteger.ZERO) && 0 == adhesionGrade) { adhesionGrade = grade; } } @@ -99,12 +108,15 @@ public class ProposalTallyAnalysis { this.contestationGrade = contestationGrade; 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; - if (this.contestationGroupSize < this.adhesionGroupSize) { +// if (this.contestationGroupSize < this.adhesionGroupSize) { + if (1 == this.adhesionGroupSize.compareTo(this.contestationGroupSize)) { this.secondMedianGrade = this.adhesionGrade; 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.secondMedianGroupSign = -1; } else { @@ -116,16 +128,16 @@ public class ProposalTallyAnalysis { this.secondMedianGroupSign = 1; } } - if (0 == this.secondMedianGroupSize) { + if (0 == this.secondMedianGroupSize.compareTo(BigInteger.ZERO)) { this.secondMedianGroupSign = 0; } } - - public Long getTotalSize() { + + public BigInteger getTotalSize() { return totalSize; } - public void setTotalSize(Long totalSize) { + public void setTotalSize(BigInteger totalSize) { this.totalSize = totalSize; } @@ -136,12 +148,12 @@ public class ProposalTallyAnalysis { public void setMedianGrade(Integer medianGrade) { this.medianGrade = medianGrade; } - - public Long getMedianGroupSize() { + + public BigInteger getMedianGroupSize() { return medianGroupSize; } - public void setMedianGroupSize(Long medianGroupSize) { + public void setMedianGroupSize(BigInteger medianGroupSize) { this.medianGroupSize = medianGroupSize; } @@ -153,11 +165,11 @@ public class ProposalTallyAnalysis { this.contestationGrade = contestationGrade; } - public Long getContestationGroupSize() { + public BigInteger getContestationGroupSize() { return contestationGroupSize; } - public void setContestationGroupSize(Long contestationGroupSize) { + public void setContestationGroupSize(BigInteger contestationGroupSize) { this.contestationGroupSize = contestationGroupSize; } @@ -168,15 +180,15 @@ public class ProposalTallyAnalysis { public void setAdhesionGrade(Integer adhesionGrade) { this.adhesionGrade = adhesionGrade; } - - public Long getAdhesionGroupSize() { + + public BigInteger getAdhesionGroupSize() { return adhesionGroupSize; } - public void setAdhesionGroupSize(Long adhesionGroupSize) { + public void setAdhesionGroupSize(BigInteger adhesionGroupSize) { this.adhesionGroupSize = adhesionGroupSize; } - + public Integer getSecondMedianGrade() { return secondMedianGrade; } @@ -185,11 +197,11 @@ public class ProposalTallyAnalysis { this.secondMedianGrade = secondMedianGrade; } - public Long getSecondMedianGroupSize() { + public BigInteger getSecondMedianGroupSize() { return secondMedianGroupSize; } - public void setSecondMedianGroupSize(Long secondMedianGroupSize) { + public void setSecondMedianGroupSize(BigInteger secondMedianGroupSize) { this.secondMedianGroupSize = secondMedianGroupSize; } diff --git a/src/main/java/fr/mieuxvoter/mj/ProposalTallyInterface.java b/src/main/java/fr/mieuxvoter/mj/ProposalTallyInterface.java index 5907018..0059671 100644 --- a/src/main/java/fr/mieuxvoter/mj/ProposalTallyInterface.java +++ b/src/main/java/fr/mieuxvoter/mj/ProposalTallyInterface.java @@ -9,7 +9,7 @@ public interface ProposalTallyInterface { * the amount of judgments received for each Grade by the Proposal, * from "worst" ("most conservative") Grade to "best" Grade. */ - public Long[] getTally(); + public BigInteger[] getTally(); /** * Should be the sum of getTally() diff --git a/src/main/java/fr/mieuxvoter/mj/Tally.java b/src/main/java/fr/mieuxvoter/mj/Tally.java index 38e1a2f..fe406ea 100644 --- a/src/main/java/fr/mieuxvoter/mj/Tally.java +++ b/src/main/java/fr/mieuxvoter/mj/Tally.java @@ -1,19 +1,26 @@ package fr.mieuxvoter.mj; +import java.math.BigInteger; + public class Tally implements TallyInterface { 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) { setProposalsTallies(proposalsTallies); - setAmountOfJudges(amountOfJudges); + setAmountOfJudges(BigInteger.valueOf(amountOfJudges)); } public Tally(ProposalTallyInterface[] proposalsTallies, Integer amountOfJudges) { setProposalsTallies(proposalsTallies); - setAmountOfJudges(Long.valueOf(amountOfJudges)); + setAmountOfJudges(BigInteger.valueOf(amountOfJudges)); } public ProposalTallyInterface[] getProposalsTallies() { @@ -28,11 +35,11 @@ public class Tally implements TallyInterface { return proposalsTallies.length; } - public Long getAmountOfJudges() { + public BigInteger getAmountOfJudges() { return amountOfJudges; } - public void setAmountOfJudges(Long amountOfJudges) { + public void setAmountOfJudges(BigInteger amountOfJudges) { this.amountOfJudges = amountOfJudges; } diff --git a/src/main/java/fr/mieuxvoter/mj/TallyInterface.java b/src/main/java/fr/mieuxvoter/mj/TallyInterface.java index 308473f..70d53d8 100644 --- a/src/main/java/fr/mieuxvoter/mj/TallyInterface.java +++ b/src/main/java/fr/mieuxvoter/mj/TallyInterface.java @@ -1,11 +1,13 @@ package fr.mieuxvoter.mj; +import java.math.BigInteger; + public interface TallyInterface { - + public ProposalTallyInterface[] getProposalsTallies(); - - public Long getAmountOfJudges(); - + + public BigInteger getAmountOfJudges(); + public Integer getAmountOfProposals(); - + } diff --git a/src/main/java/fr/mieuxvoter/mj/TallyWithDefaultGrade.java b/src/main/java/fr/mieuxvoter/mj/TallyWithDefaultGrade.java index dea5891..f6e458e 100644 --- a/src/main/java/fr/mieuxvoter/mj/TallyWithDefaultGrade.java +++ b/src/main/java/fr/mieuxvoter/mj/TallyWithDefaultGrade.java @@ -6,7 +6,7 @@ public class TallyWithDefaultGrade extends Tally implements TallyInterface { protected Integer defaultGrade = 0; - public TallyWithDefaultGrade(ProposalTallyInterface[] proposalsTallies, Integer amountOfJudges, Integer defaultGrade) { + public TallyWithDefaultGrade(ProposalTallyInterface[] proposalsTallies, BigInteger amountOfJudges, Integer defaultGrade) { super(proposalsTallies, amountOfJudges); this.defaultGrade = defaultGrade; fillWithDefaultGrade(); @@ -18,16 +18,22 @@ public class TallyWithDefaultGrade extends Tally implements TallyInterface { 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 = BigInteger.valueOf(this.amountOfJudges).subtract(amountOfJudgments); + BigInteger missingAmount = this.amountOfJudges.subtract(amountOfJudgments); int missingSign = missingAmount.compareTo(BigInteger.ZERO); - assert(0 <= missingSign); // More judgments than judges! + assert(0 <= missingSign); // ERROR: More judgments than judges! if (0 < missingSign) { - proposal.getTally()[this.defaultGrade] = proposal.getTally()[this.defaultGrade] + missingAmount.longValue(); + proposal.getTally()[this.defaultGrade] = proposal.getTally()[this.defaultGrade].add(missingAmount); } } }