You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
majority-judgment-library-go/judgment/analysis.go

104 lines
3.9 KiB

package judgment
// Searching for good names
// ------------------------
// Rebuttal == Contestation == ???
// Rebuttal has a nice length isonomy with Adhesion
// Contestation ain't in some dictionaries
// Contestation feels more natural to a french thinker
// Rebuttal may be a little (too) intense
//
// uint8 for grades
// ----------------
// Not sure about that. Is it worth the hassle? May change. Advice welcome.
//
// ProposalAnalysis holds some data we need to compute the Score of a Proposal, and hence its Rank.
type ProposalAnalysis struct {
TotalSize uint64 `json:"totalSize"` // total amount of judges|judgments across all grades
MedianGrade uint8 `json:"medianGrade"` // 0 == "worst" grade, goes up to the amount of grades - 1
MedianGroupSize uint64 `json:"medianGroupSize"` // in judges|judgments
SecondMedianGrade uint8 `json:"secondMedianGrade"` // used in Majority Judgment deliberation
SecondGroupSize uint64 `json:"secondGroupSize"` // either adhesion or contestation, whichever is bigger
SecondGroupSign int `json:"secondGroupSign"` // -1 for contestation group, +1 for adhesion group
AdhesionGroupGrade uint8 `json:"adhesionGroupGrade"`
AdhesionGroupSize uint64 `json:"adhesionGroupSize"`
ContestationGroupGrade uint8 `json:"contestationGroupGrade"`
ContestationGroupSize uint64 `json:"contestationGroupSize"`
// Can't decide between Rebuttal and Contestation… Help!
//RebuttalGroupGrade uint8
//RebuttalGroupSize uint64
}
// Reset the ProposalAnalysis to default values.
func (analysis *ProposalAnalysis) Reset() {
analysis.TotalSize = 0
analysis.MedianGrade = 0
analysis.MedianGroupSize = 0
analysis.SecondMedianGrade = 0
analysis.SecondGroupSize = 0
analysis.SecondGroupSign = 0
analysis.AdhesionGroupGrade = 0
analysis.AdhesionGroupSize = 0
analysis.ContestationGroupGrade = 0
analysis.ContestationGroupSize = 0
}
// Run MUTATES THE ANALYSIS, but leaves the proposalTally intact, unchanged.
// MJ uses the low median by default (favors contestation), but there's a parameter if need be.
// This method is deemed complex by gocyclo ; there's no way around it.
func (analysis *ProposalAnalysis) Run(proposalTally *ProposalTally, favorContestation bool) {
analysis.Reset()
analysis.TotalSize = proposalTally.CountJudgments()
if 0 == analysis.TotalSize {
return
}
adjustedTotal := analysis.TotalSize
if favorContestation {
adjustedTotal = analysis.TotalSize - 1
}
medianIndex := adjustedTotal / 2 // Euclidean division
startIndex := uint64(0)
cursorIndex := uint64(0)
for gradeIndex, gradeTally := range proposalTally.Tally {
if 0 == gradeTally {
continue
}
startIndex = cursorIndex
cursorIndex += gradeTally
if (startIndex < medianIndex) && (cursorIndex <= medianIndex) {
analysis.ContestationGroupSize += gradeTally
analysis.ContestationGroupGrade = uint8(gradeIndex)
} else if (startIndex <= medianIndex) && (medianIndex < cursorIndex) {
analysis.MedianGroupSize = gradeTally
analysis.MedianGrade = uint8(gradeIndex)
} else if (startIndex > medianIndex) && (medianIndex < cursorIndex) {
analysis.AdhesionGroupSize += gradeTally
if 0 == analysis.AdhesionGroupGrade {
analysis.AdhesionGroupGrade = uint8(gradeIndex)
}
}
}
contestationIsBiggest := analysis.AdhesionGroupSize < analysis.ContestationGroupSize
if favorContestation {
contestationIsBiggest = analysis.AdhesionGroupSize <= analysis.ContestationGroupSize
}
if contestationIsBiggest {
analysis.SecondMedianGrade = analysis.ContestationGroupGrade
analysis.SecondGroupSize = analysis.ContestationGroupSize
if 0 < analysis.SecondGroupSize {
analysis.SecondGroupSign = -1
}
} else {
analysis.SecondMedianGrade = analysis.AdhesionGroupGrade
analysis.SecondGroupSize = analysis.AdhesionGroupSize
if 0 < analysis.SecondGroupSize {
analysis.SecondGroupSign = 1
}
}
}