diff --git a/majorityjudgment.go b/majorityjudgment.go index 7eeb75c..031432c 100644 --- a/majorityjudgment.go +++ b/majorityjudgment.go @@ -41,12 +41,27 @@ func (mj *MajorityJudgment) Deliberate(tally *PollTally) (_ *PollResult, err err } } - amountOfJudgments := tally.Proposals[0].CountJudgments() + maximumAmountOfJudgments := uint64(0) for _, proposalTally := range tally.Proposals { - if amountOfJudgments != proposalTally.CountJudgments() { - return nil, fmt.Errorf("unbalanced tally: " + - "some proposals hold more judgments than others ; " + - "use one of the tally balancers or make your own") + amountOfJudgments := proposalTally.CountJudgments() + if amountOfJudgments > maximumAmountOfJudgments { + maximumAmountOfJudgments = amountOfJudgments + } + } + + amountOfJudges := tally.AmountOfJudges + if amountOfJudges < maximumAmountOfJudgments { + return nil, fmt.Errorf("incoherent tally: " + + "some proposals hold more judgments than the specified amount of judges ; " + + "perhaps you forgot to set PollTally.AmountOfJudges") + } + + //amountOfJudgments := tally.Proposals[0].CountJudgments() + for proposalIndex, proposalTally := range tally.Proposals { + if amountOfJudges != proposalTally.CountJudgments() { + return nil, fmt.Errorf("unbalanced tally: "+ + "a proposal (#%d) holds less judgments than there are judges ; "+ + "use one of the PollTally.Balance() methods first", proposalIndex) } } @@ -81,7 +96,8 @@ func (mj *MajorityJudgment) Deliberate(tally *PollTally) (_ *PollResult, err err } result := &PollResult{ - Proposals: proposalsResults, + Proposals: proposalsResults, + ProposalsSorted: proposalsResultsSorted, } return result, nil @@ -92,11 +108,16 @@ func (mj *MajorityJudgment) ComputeScore(tally *ProposalTally, favorContestation score := "" analysis := &ProposalAnalysis{} - amountOfJudgments := tally.CountJudgments() amountOfGrades := tally.CountAvailableGrades() + amountOfJudgments := tally.CountJudgments() amountOfDigitsForGrade := countDigitsUint8(amountOfGrades) amountOfDigitsForAdhesionScore := countDigitsUint64(amountOfJudgments * 2) + amountOfJudgmentsInt := int(amountOfJudgments) + if amountOfJudgmentsInt < 0 { + return "", fmt.Errorf("too many judgments ; see branch|fork using math/big") + } + mutatedTally := tally.Copy() for i := uint8(0); i < amountOfGrades; i++ { analysis.Run(mutatedTally, favorContestation) diff --git a/majorityjudgment_test.go b/majorityjudgment_test.go index fd3bb5a..0719a87 100644 --- a/majorityjudgment_test.go +++ b/majorityjudgment_test.go @@ -1,8 +1,8 @@ package judgment import ( - "fmt" "github.com/stretchr/testify/assert" + "math" "testing" ) @@ -20,7 +20,6 @@ func TestReadmeDemo(t *testing.T) { deliberator := &MajorityJudgment{} result, err := deliberator.Deliberate(poll) assert.NoError(t, err, "Deliberation should succeed") - fmt.Printf("Result: %v\n", result) assert.Len(t, result.Proposals, len(poll.Proposals), "There should be as many results as there are tallies.") assert.Equal(t, 4, result.Proposals[0].Rank, "Rank of proposal A") assert.Equal(t, 1, result.Proposals[1].Rank, "Rank of proposal B") @@ -40,6 +39,20 @@ func TestNoProposals(t *testing.T) { assert.Len(t, result.Proposals, len(poll.Proposals), "There should be as many results as there are tallies.") } +func TestIncoherentTally(t *testing.T) { + poll := &PollTally{ + AmountOfJudges: 2, // not 8 as it should + Proposals: []*ProposalTally{ + {Tally: []uint64{4, 4}}, + {Tally: []uint64{2, 6}}, + }, + } + deliberator := &MajorityJudgment{} + result, err := deliberator.Deliberate(poll) + assert.Error(t, err, "Deliberation should fail") + assert.Nil(t, result, "Deliberation result should be nil") +} + func TestMishapedTally(t *testing.T) { poll := &PollTally{ AmountOfJudges: 10, @@ -67,3 +80,20 @@ func TestUnbalancedTally(t *testing.T) { assert.Error(t, err, "Deliberation should fail") assert.Nil(t, result, "Deliberation result should be nil") } + +func TestExcessivelyBigTally(t *testing.T) { + // math.MaxInt64 + 1 is a valid uint64 value, but we need to cast to int internally so it overflows + poll := &PollTally{ + AmountOfJudges: math.MaxInt64 + 1, + Proposals: []*ProposalTally{ + {Tally: []uint64{math.MaxInt64, 1}}, + {Tally: []uint64{math.MaxInt64, 1}}, + }, + } + deliberator := &MajorityJudgment{} + result, err := deliberator.Deliberate(poll) + if assert.Error(t, err, "Deliberation should fail") { + //println(err.Error()) + } + assert.Nil(t, result, "Deliberation result should be nil") +}