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. * * 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 BigInteger totalSize = BigInteger.ZERO; // amount of judges protected Integer medianGrade = 0; protected BigInteger medianGroupSize = BigInteger.ZERO; // amount of judges in the median group protected Integer contestationGrade = 0; // "best" grade of the contestation group protected BigInteger contestationGroupSize = BigInteger.ZERO; // of lower grades than median protected Integer adhesionGrade = 0; // "worst" grade of the adhesion group protected BigInteger adhesionGroupSize = BigInteger.ZERO; // of higher grades than median protected Integer secondMedianGrade = 0; // grade of the biggest group out of the median 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) { reanalyze(tally); } public void reanalyze(ProposalTallyInterface tally) { reanalyze(tally, true); } public void reanalyze(ProposalTallyInterface tally, Boolean favorContestation) { this.tally = tally; this.totalSize = BigInteger.ZERO; this.medianGrade = 0; this.medianGroupSize = BigInteger.ZERO; this.contestationGrade = 0; this.contestationGroupSize = BigInteger.ZERO; this.adhesionGrade = 0; this.adhesionGroupSize = BigInteger.ZERO; this.secondMedianGrade = 0; this.secondMedianGroupSize = BigInteger.ZERO; this.secondMedianGroupSign = 0; BigInteger[] gradesTallies = this.tally.getTally(); int amountOfGrades = gradesTallies.length; for (int grade = 0; grade < amountOfGrades; grade++) { 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; } BigInteger medianCursor = this.totalSize.add(BigInteger.valueOf(medianOffset)).divide(BigInteger.TWO); // Long medianCursor = (long) Math.floor((this.totalSize + medianOffset) / 2.0); BigInteger tallyBeforeCursor = BigInteger.ZERO; BigInteger tallyCursor = BigInteger.ZERO; Boolean foundMedian = false; Integer contestationGrade = 0; Integer adhesionGrade = 0; for (int grade = 0; grade < amountOfGrades; grade++) { BigInteger gradeTally = gradesTallies[grade]; tallyBeforeCursor = tallyCursor; tallyCursor = tallyCursor.add(gradeTally); if ( ! foundMedian) { 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.subtract(this.contestationGroupSize).subtract(this.medianGroupSize); } else { if (1 == gradeTally.compareTo(BigInteger.ZERO)) { // 0 < gradeTally contestationGrade = grade; } } } else { if (1 == gradeTally.compareTo(BigInteger.ZERO) && 0 == adhesionGrade) { adhesionGrade = grade; } } } this.contestationGrade = contestationGrade; this.adhesionGrade = adhesionGrade; // this.secondMedianGroupSize = Math.max(this.contestationGroupSize, this.adhesionGroupSize); this.secondMedianGroupSize = this.contestationGroupSize.max(this.adhesionGroupSize); this.secondMedianGroupSign = 0; // 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 (1 == this.contestationGroupSize.compareTo(this.adhesionGroupSize)) { this.secondMedianGrade = this.contestationGrade; this.secondMedianGroupSign = -1; } else { if (favorContestation) { this.secondMedianGrade = this.contestationGrade; this.secondMedianGroupSign = -1; } else { this.secondMedianGrade = this.adhesionGrade; this.secondMedianGroupSign = 1; } } if (0 == this.secondMedianGroupSize.compareTo(BigInteger.ZERO)) { this.secondMedianGroupSign = 0; } } public BigInteger getTotalSize() { return totalSize; } public void setTotalSize(BigInteger totalSize) { this.totalSize = totalSize; } public Integer getMedianGrade() { return medianGrade; } public void setMedianGrade(Integer medianGrade) { this.medianGrade = medianGrade; } public BigInteger getMedianGroupSize() { return medianGroupSize; } public void setMedianGroupSize(BigInteger medianGroupSize) { this.medianGroupSize = medianGroupSize; } public Integer getContestationGrade() { return contestationGrade; } public void setContestationGrade(Integer contestationGrade) { this.contestationGrade = contestationGrade; } public BigInteger getContestationGroupSize() { return contestationGroupSize; } public void setContestationGroupSize(BigInteger contestationGroupSize) { this.contestationGroupSize = contestationGroupSize; } public Integer getAdhesionGrade() { return adhesionGrade; } public void setAdhesionGrade(Integer adhesionGrade) { this.adhesionGrade = adhesionGrade; } public BigInteger getAdhesionGroupSize() { return adhesionGroupSize; } public void setAdhesionGroupSize(BigInteger adhesionGroupSize) { this.adhesionGroupSize = adhesionGroupSize; } public Integer getSecondMedianGrade() { return secondMedianGrade; } public void setSecondMedianGrade(Integer secondMedianGrade) { this.secondMedianGrade = secondMedianGrade; } public BigInteger getSecondMedianGroupSize() { return secondMedianGroupSize; } public void setSecondMedianGroupSize(BigInteger secondMedianGroupSize) { this.secondMedianGroupSize = secondMedianGroupSize; } public Integer getSecondMedianGroupSign() { return secondMedianGroupSign; } public void setSecondMedianGroupSign(Integer sign) { this.secondMedianGroupSign = sign; } }