Source: compute/voteCasters/ranking/castRankingPoint.js

/** @module */

import { range } from '@paretoman/votekit-utilities'
import * as types from '@paretoman/votekit-types'
/**
 * Vote for the closest candidate.
 * @returns {types.typesVote.rankingVote}
 */
export default function castRankingPoint(canPoints, voterPoint, dimensions, verbosity) {
    const d2f = (dimensions === 1) ? d2f1 : d2f2

    const n = canPoints.length
    const dist2 = Array(n)
    for (let i = 0; i < n; i++) {
        dist2[i] = d2f(canPoints[i], voterPoint)
    }
    const indexInOrder = range(n).sort((a, b) => dist2[a] - dist2[b])
    // Note that ties are not handled.

    const cansByRank = Array(n)
    for (let i = 0; i < n; i++) {
        const can = indexInOrder[i]
        cansByRank[i] = [can]
    }

    if (verbosity === 0) return { cansByRank }

    const ranking = Array(n)
    const bordaScores = Array(n)
    const bordaFractions = Array(n)
    for (let i = 0; i < n; i++) {
        const can = indexInOrder[i]
        ranking[can] = i + 1
        bordaScores[can] = n - i - 1
        bordaFractions[can] = (n - i - 1) / (n - 1)
    }
    // Does i beat k? 1 if yes, -1 if opposite, 0 if tie
    const netWinsPairwise = Array(n)
    for (let i = 0; i < n; i++) {
        netWinsPairwise[i] = Array(n).fill(0)
        for (let k = 0; k < n; k++) {
            if (dist2[i] === dist2[k]) continue
            netWinsPairwise[i][k] = (dist2[i] < dist2[k]) ? 1 : -1
        }
    }

    return {
        cansByRank, ranking, netWinsPairwise, bordaFractions, bordaScores,
    }
}
function d2f1(a, b) {
    return (a - b) ** 2
}
function d2f2(a, b) {
    return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2
}