Source: compute/voteCasters/plurality/castPlurality.js

/** @module */

import * as types from '@paretoman/votekit-types'
import seedrandom from 'seedrandom'
import castPluralityIntervals1D from './castPluralityIntervals1D.js'
import castPluralityQuadrature2D from './castPluralityQuadrature2D.js'
import castPluralityGrid from './castPluralityGrid.js'

/**
 * Vote for one.
 * Voters cast votes for candidates.
 * @param {types.typesGeometry.geometry} geometry - geometry for casting votes
 * @param {types.typesCast.castOptions} castOptions - options for how to cast votes.
 * @returns {types.typesVotes.votes} votes
 */
export default function castPlurality(geometry, castOptions) {
    const { canPoints, voterGeoms, dimensions, parties, strategySeed, voterStrategyList } = geometry
    const { verbosity } = castOptions

    const someGaussian2D = voterGeoms.some((v) => v.densityProfile === 'gaussian') && dimensions === 2

    const someStrategy = voterStrategyList.some(
        (v) => v.strategy.some(
            (a) => (a.actionName !== 'closest' && a.actionWeight !== 0),
        ),
    )

    const castRegions = (dimensions === 1)
        ? castPluralityIntervals1D
        : castPluralityQuadrature2D
    const cast = (someGaussian2D || someStrategy) ? castPluralityGrid : castRegions

    // get fraction of votes for each candidate so we can summarize results
    const n = canPoints.length
    const votesByGeom = []
    const countByCan = (new Array(n)).fill(0)
    let totalVotes = 0
    const strategyRngs = [seedrandom(`green${strategySeed}`), seedrandom(`orange${strategySeed}`)]
    for (let i = 0; i < voterGeoms.length; i++) {
        const voterGeom = voterGeoms[i]
        const voterStrategy = voterStrategyList[i]

        const votesForGeom = cast(voterGeom, geometry, castOptions, strategyRngs, voterStrategy)
        const { countByCan: countByCanForGeom,
            totalVotes: totalVotesForGeom } = votesForGeom

        for (let k = 0; k < n; k++) {
            countByCan[k] += countByCanForGeom[k]
        }
        totalVotes += totalVotesForGeom

        if (verbosity < 2) continue

        votesByGeom[i] = votesForGeom
    }
    const voteFractionsByCan = countByCan.map((x) => x / totalVotes)

    const candidateTallies = { voteFractionsByCan }
    const numCans = canPoints.length
    const votes = { candidateTallies, parties, numCans }
    if (verbosity < 2) return votes

    votes.votesByGeom = votesByGeom
    return votes
}