Source: compute/voteCasters/scoreFull/castScoreFull.js

/** @module */

import castScoreFullGrid from './castScoreFullGrid.js'

// The main difference between this and score is we need to return a set of votes from here.

/**
 * Voters cast votes for candidates.
 * @param {object} geometry - geometry for casting votes
 * @param {object[]} geometry.canPoints - For 2D, an array of arrays: [x,y]. For 1D, an array of numbers: x.
 * @param {object[]} geometry.voterGeoms - For 2D, an array of objects: {x,y,w}.
 * For 1D, an array of objects: {x,w,densityProfile}.
 * @param {object} geometry.parties
 * @param {object} castOptions - options for how to cast votes.
 * @returns {object} votes
 */
export default function castScoreFull(geometry, castOptions) {
    const { canPoints, voterGeoms, parties } = geometry
    const { verbosity } = castOptions

    // get fraction of votes for each candidate so we can summarize results
    const n = canPoints.length

    // find totalWeight of "voter area" over all the voterGeoms
    // then find normalization factor, which is just 1/totalWeight
    let totalVotes = 0
    const votesByGeom = []
    for (let i = 0; i < voterGeoms.length; i++) {
        const voterGeom = voterGeoms[i]

        const votesForGeom = castScoreFullGrid(voterGeom, geometry, castOptions)
        votesByGeom[i] = votesForGeom
        const { totalVotes: totalVotesForGeom } = votesForGeom

        totalVotes += totalVotesForGeom
    }
    const invTotalCount = 1 / totalVotes

    // tally votes
    // flatten voteSets into scoreVotes
    // voteFractions is number of voters with that vote as a fraction of total votes (usually 1 or lower)
    const scoreSumByCan = (new Array(n)).fill(0)
    const scoreVotes = []
    const voteFractions = []
    let index = 0
    for (let i = 0; i < voterGeoms.length; i++) {
        const {
            voteCounts, voteSet,
            scoreSumByCan: scoreSumByCanForGeom,
        } = votesByGeom[i]

        // use voteIndex to find flattened index
        // voteIndex = Number[] with first index as geometry and second index as voteSet index
        const voteIndex = []

        for (let j = 0; j < voteSet.length; j++) {
            scoreVotes[index] = voteSet[j].scoreVote
            voteIndex[j] = index
            voteFractions[index] = voteCounts[j] * invTotalCount
            index += 1
        }
        votesByGeom[i].voteIndex = voteIndex

        for (let k = 0; k < n; k++) {
            scoreSumByCan[k] += scoreSumByCanForGeom[k]
        }
    }
    const maxScore = 1
    const scoreFractionAverageByCan = scoreSumByCan.map((x) => x / (totalVotes * maxScore))

    const preferenceLists = { scoreVotes }
    const preferenceTallies = { voteFractions }
    const candidateTallies = { scoreFractionAverageByCan }
    const numCans = canPoints.length

    const votes = { preferenceLists, preferenceTallies, candidateTallies, parties, numCans }
    if (verbosity < 2) return votes

    votes.votesByGeom = votesByGeom
    return votes
}