@@ -71,10 +71,13 @@ func (r Record[C]) Map[NewC comparable](fn func(C) NewC) Record[NewC] {
7171 return res
7272}
7373
74- // Vote updates the preferences passed as the first argument with the Ballot
75- // values. A record of a complete and normalized preferences is returned that
76- // can be used to unvote.
77- func Vote [C comparable , N Number ](preferences []N , choices []C , b Ballot [C ]) (Record [C ], error ) {
74+ // VoteWeighted updates the preferences passed as the first argument with the Ballot
75+ // values scaled by the specified weight. A record of a complete and normalized preferences
76+ // is returned that can be used to unvote.
77+ func VoteWeighted [C comparable , N Number ](preferences []N , choices []C , b Ballot [C ], weight N ) (Record [C ], error ) {
78+ if weight <= 0 {
79+ return nil , ErrInvalidWeight
80+ }
7881 choicesCount := len (choices )
7982 if len (preferences ) < choicesCount * choicesCount {
8083 return nil , & InvalidPreferencesError {
@@ -94,7 +97,7 @@ func Vote[C comparable, N Number](preferences []N, choices []C, b Ballot[C]) (Re
9497 icc := int (i ) * choicesCount
9598 for _, choices2 := range rest {
9699 for _, j := range choices2 {
97- preferences [icc + int (j )] += 1
100+ preferences [icc + int (j )] += weight
98101 }
99102 }
100103 }
@@ -111,15 +114,15 @@ func Vote[C comparable, N Number](preferences []N, choices []C, b Ballot[C]) (Re
111114 if ranksLen > 0 {
112115 for _, choices1 := range ranks [:ranksLen - 1 ] {
113116 for _, i := range choices1 {
114- preferences [int (i )* choicesCount + int (i )] += 1
117+ preferences [int (i )* choicesCount + int (i )] += weight
115118 }
116119 }
117120 }
118121 } else {
119122 // all choices are ranked, treat diagonal values as a single not ranked
120123 // choice, deprioritizing them for all existing choices
121124 for i := range choicesCount {
122- preferences [i * choicesCount + i ] += 1
125+ preferences [i * choicesCount + i ] += weight
123126 }
124127 }
125128
@@ -145,10 +148,20 @@ func Vote[C comparable, N Number](preferences []N, choices []C, b Ballot[C]) (Re
145148 return r , nil
146149}
147150
148- // VoteRanked updates the preferences with a pre-ranked list of choices. Each
149- // element in rankedChoices is a slice of choices tied at that rank. Choices
150- // omitted from rankedChoices are considered unranked.
151- func VoteRanked [C comparable , N Number ](preferences []N , choices []C , rankedChoices [][]C ) (Record [C ], error ) {
151+ // Vote updates the preferences passed as the first argument with the Ballot
152+ // values. A record of a complete and normalized preferences is returned that
153+ // can be used to unvote.
154+ func Vote [C comparable , N Number ](preferences []N , choices []C , b Ballot [C ]) (Record [C ], error ) {
155+ return VoteWeighted (preferences , choices , b , 1 )
156+ }
157+
158+ // VoteRankedWeighted updates the preferences with a pre-ranked list of choices
159+ // scaled by the specified weight. Each element in rankedChoices is a slice of
160+ // choices tied at that rank. Choices omitted from rankedChoices are considered unranked.
161+ func VoteRankedWeighted [C comparable , N Number ](preferences []N , choices []C , rankedChoices [][]C , weight N ) (Record [C ], error ) {
162+ if weight <= 0 {
163+ return nil , ErrInvalidWeight
164+ }
152165 choicesCount := len (choices )
153166 if len (preferences ) < choicesCount * choicesCount {
154167 return nil , & InvalidPreferencesError {
@@ -168,7 +181,7 @@ func VoteRanked[C comparable, N Number](preferences []N, choices []C, rankedChoi
168181 icc := int (i ) * choicesCount
169182 for _, choices2 := range rest {
170183 for _, j := range choices2 {
171- preferences [icc + int (j )] += 1
184+ preferences [icc + int (j )] += weight
172185 }
173186 }
174187 }
@@ -180,13 +193,13 @@ func VoteRanked[C comparable, N Number](preferences []N, choices []C, rankedChoi
180193 if ranksLen > 0 {
181194 for _, choices1 := range ranks [:ranksLen - 1 ] {
182195 for _, i := range choices1 {
183- preferences [int (i )* choicesCount + int (i )] += 1
196+ preferences [int (i )* choicesCount + int (i )] += weight
184197 }
185198 }
186199 }
187200 } else {
188201 for i := range choicesCount {
189- preferences [i * choicesCount + i ] += 1
202+ preferences [i * choicesCount + i ] += weight
190203 }
191204 }
192205
@@ -210,8 +223,18 @@ func VoteRanked[C comparable, N Number](preferences []N, choices []C, rankedChoi
210223 return r , nil
211224}
212225
213- // Unvote removes the Ballot values from the preferences.
214- func Unvote [C comparable , N Number ](preferences []N , choices []C , r Record [C ]) error {
226+ // VoteRanked updates the preferences with a pre-ranked list of choices. Each
227+ // element in rankedChoices is a slice of choices tied at that rank. Choices
228+ // omitted from rankedChoices are considered unranked.
229+ func VoteRanked [C comparable , N Number ](preferences []N , choices []C , rankedChoices [][]C ) (Record [C ], error ) {
230+ return VoteRankedWeighted (preferences , choices , rankedChoices , 1 )
231+ }
232+
233+ // UnvoteWeighted removes the Ballot values scaled by the specified weight from the preferences.
234+ func UnvoteWeighted [C comparable , N Number ](preferences []N , choices []C , r Record [C ], weight N ) error {
235+ if weight <= 0 {
236+ return ErrInvalidWeight
237+ }
215238 choicesCount := len (choices )
216239 if len (preferences ) < choicesCount * choicesCount {
217240 return & InvalidPreferencesError {
@@ -225,20 +248,38 @@ func Unvote[C comparable, N Number](preferences []N, choices []C, r Record[C]) e
225248 return nil
226249 }
227250
251+ var getIndex func (c C ) choiceIndex
252+ if choicesCount <= 16 {
253+ getIndex = func (c C ) choiceIndex {
254+ return getChoiceIndex (choices , c )
255+ }
256+ } else {
257+ indexMap := make (map [C ]choiceIndex , choicesCount )
258+ for idx , c := range choices {
259+ indexMap [c ] = choiceIndex (idx )
260+ }
261+ getIndex = func (c C ) choiceIndex {
262+ if idx , ok := indexMap [c ]; ok {
263+ return idx
264+ }
265+ return - 1
266+ }
267+ }
268+
228269 for rank , choices1 := range r {
229270 rest := r [rank + 1 :]
230271 for _, choice1 := range choices1 {
231- i := getChoiceIndex ( choices , choice1 )
272+ i := getIndex ( choice1 )
232273 if i < 0 {
233274 continue
234275 }
235276 for _, choices2 := range rest {
236277 for _, choice2 := range choices2 {
237- j := getChoiceIndex ( choices , choice2 )
278+ j := getIndex ( choice2 )
238279 if j < 0 {
239280 continue
240281 }
241- preferences [int (i )* choicesCount + int (j )] -= 1
282+ preferences [int (i )* choicesCount + int (j )] -= weight
242283 }
243284 }
244285 }
@@ -252,18 +293,18 @@ func Unvote[C comparable, N Number](preferences []N, choices []C, r Record[C]) e
252293 // unranked choices even if it is empty
253294 for _, choices1 := range r [:recordLength - 1 ] {
254295 for _, choice1 := range choices1 {
255- i := getChoiceIndex ( choices , choice1 )
296+ i := getIndex ( choice1 )
256297 if i < 0 {
257298 continue
258299 }
259- preferences [int (i )* choicesCount + int (i )] -= 1
300+ preferences [int (i )* choicesCount + int (i )] -= weight
260301 knownChoices.set (uint64 (i ))
261302 rankedChoices.set (uint64 (i ))
262303 }
263304 }
264305 // mark the rest of the known choices in the Record
265306 for _, choice1 := range r [recordLength - 1 ] {
266- i := getChoiceIndex ( choices , choice1 )
307+ i := getIndex ( choice1 )
267308 if i < 0 {
268309 continue
269310 }
@@ -275,7 +316,7 @@ func Unvote[C comparable, N Number](preferences []N, choices []C, r Record[C]) e
275316 if rankedChoices.isSet (i ) {
276317 for j := range uint64 (choicesCount ) {
277318 if ! knownChoices.isSet (j ) {
278- preferences [int (i )* choicesCount + int (j )] -= 1
319+ preferences [int (i )* choicesCount + int (j )] -= weight
279320 }
280321 }
281322 }
@@ -284,6 +325,11 @@ func Unvote[C comparable, N Number](preferences []N, choices []C, r Record[C]) e
284325 return nil
285326}
286327
328+ // Unvote removes the Ballot values from the preferences.
329+ func Unvote [C comparable , N Number ](preferences []N , choices []C , r Record [C ]) error {
330+ return UnvoteWeighted (preferences , choices , r , 1 )
331+ }
332+
287333// SetChoices updates the preferences passed as the first argument by changing
288334// its values to accommodate the changes to the choices. It is required to
289335// pass the exact choices as the second parameter and complete updated choices
@@ -572,8 +618,26 @@ func ballotRanks[C comparable](choices []C, b Ballot[C]) (ranks [][]choiceIndex,
572618 rankedChoices = newBitset (uint64 (choicesLen ))
573619 }
574620
621+ var getIndex func (c C ) choiceIndex
622+ if choicesLen <= 16 {
623+ getIndex = func (c C ) choiceIndex {
624+ return getChoiceIndex (choices , c )
625+ }
626+ } else {
627+ indexMap := make (map [C ]choiceIndex , choicesLen )
628+ for i , c := range choices {
629+ indexMap [c ] = choiceIndex (i )
630+ }
631+ getIndex = func (c C ) choiceIndex {
632+ if idx , ok := indexMap [c ]; ok {
633+ return idx
634+ }
635+ return - 1
636+ }
637+ }
638+
575639 for choice , rank := range b {
576- index := getChoiceIndex ( choices , choice )
640+ index := getIndex ( choice )
577641 if index < 0 {
578642 return nil , false , & UnknownChoiceError [C ]{Choice : choice }
579643 }
@@ -619,6 +683,24 @@ func rankedChoicesRanks[C comparable](choices []C, rankedChoices [][]C) (ranks [
619683 choicesLen := len (choices )
620684 rankedBitset := newBitset (uint64 (choicesLen ))
621685
686+ var getIndex func (c C ) choiceIndex
687+ if choicesLen <= 16 {
688+ getIndex = func (c C ) choiceIndex {
689+ return getChoiceIndex (choices , c )
690+ }
691+ } else {
692+ indexMap := make (map [C ]choiceIndex , choicesLen )
693+ for i , c := range choices {
694+ indexMap [c ] = choiceIndex (i )
695+ }
696+ getIndex = func (c C ) choiceIndex {
697+ if idx , ok := indexMap [c ]; ok {
698+ return idx
699+ }
700+ return - 1
701+ }
702+ }
703+
622704 totalRanked := 0
623705 ranks = make ([][]choiceIndex , 0 , len (rankedChoices )+ 1 )
624706
@@ -628,7 +710,7 @@ func rankedChoicesRanks[C comparable](choices []C, rankedChoices [][]C) (ranks [
628710 }
629711 groupIndexes := make ([]choiceIndex , 0 , len (group ))
630712 for _, choice := range group {
631- idx := getChoiceIndex ( choices , choice )
713+ idx := getIndex ( choice )
632714 if idx < 0 {
633715 return nil , false , & UnknownChoiceError [C ]{Choice : choice }
634716 }
0 commit comments