Source: compute/socialChoiceMethods/sntv.js

/** @module */

import { range } from '@paretoman/votekit-utilities'
import * as types from '@paretoman/votekit-types'

/**
 * Single Non-Transferable Vote
 * @param {types.typesVotes.votes} votes - The object for vote data.
 * @param {types.typesSocialChoice.socialChoiceOptions} socialChoiceOptions - options to specify a social choice function.
 * @returns {types.typesSocialChoice.socialChoiceResults} - the results returned from a social choice function.
 */
export default function sntv(votes, socialChoiceOptions) {
    const { voteFractionsByCan } = votes.candidateTallies
    const { seats } = socialChoiceOptions

    const nk = voteFractionsByCan.length

    if (seats >= nk) {
        // more seats than candidates, so elect all candidates
        const allocation = Array(nk).fill(1)
        const socialChoiceResults = { allocation }
        return socialChoiceResults
    }

    // sort descending
    const cansSorted = range(nk).sort((a, b) => voteFractionsByCan[b] - voteFractionsByCan[a])

    const allocation = Array(nk).fill(0)
    for (let r = 0; r < seats; r++) { // TODO: handle edge cases, like ties or zeros
        const canI = cansSorted[r]
        allocation[canI] = 1
    }
    const socialChoiceResults = { allocation }
    return socialChoiceResults
}

/** @constant {object} - an object: this function and descriptions of its name, input, and output */
export const sntvMetadata = {
    name: 'Single Non-Transferable Vote',
    shortName: 'SNTV',
    functionName: 'sntv',
    voteCasterName: 'plurality',
    socialChoiceType: 'multiWinner',
    elect: sntv,
}