Source: compute/electionSequence/electionNonPartisanPrimary.js

  1. /** @module */
  2. import electionPhase from '@paretoman/votekit-election-phase'
  3. import { range } from '@paretoman/votekit-utilities'
  4. import getGeometryForPhase from './getGeometryForPhase.js'
  5. import getElectionOptions from './getElectionOptions.js'
  6. /**
  7. * Here we are in the context of an election sequence with two phases, a non-partisan primary, and a general.
  8. * @param {*} geometry
  9. * @param {*} optionsBag
  10. * @returns {*} sequenceResults
  11. */
  12. export default function electionNonpartisanPrimary(geometry, optionsBag) {
  13. // primary phase
  14. const primaryGeometry = getGeometryForPhase('nonpartisanOpenPrimary', geometry)
  15. const allCanLabels = range(geometry.canPoints.length)
  16. primaryGeometry.canLabels = allCanLabels
  17. const primaryOptions = getElectionOptions('nonpartisanOpenPrimary', 'nonpartisanOpenPrimary', optionsBag)
  18. const primary = electionPhase(primaryGeometry, primaryOptions, optionsBag)
  19. // general phase
  20. const { generalGeometry, primaryWinners } = getGeneralGeometry(geometry, primary)
  21. const generalOptions = getElectionOptions('nonpartisanOpenPrimary', 'general', optionsBag)
  22. const general = electionPhase(generalGeometry, generalOptions, optionsBag)
  23. // combine primary and general results
  24. const results = combinePrimaryGeneral(primary, general, primaryWinners, geometry, optionsBag)
  25. return results
  26. }
  27. /** Get the winners of the primary to be candidates in the general. */
  28. function getGeneralGeometry(geometry, primary) {
  29. const g0 = getGeometryForPhase('general', geometry)
  30. const generalGeometry = { ...g0 }
  31. const { allocation } = primary.socialChoiceResults
  32. const primaryWinners = getWinnerList(allocation)
  33. generalGeometry.canPoints = primaryWinners.map((iWinner) => g0.canPoints[iWinner])
  34. generalGeometry.canLabels = primaryWinners
  35. generalGeometry.parties = { ...g0.parties }
  36. generalGeometry.parties.partiesByCan = primaryWinners.map((iWinner) => g0.parties.partiesByCan[iWinner])
  37. return { generalGeometry, primaryWinners }
  38. }
  39. function combinePrimaryGeneral(primary, general, primaryWinners, geometry, optionsBag) {
  40. const generalAllocation = general.socialChoiceResults.allocation
  41. const generalWinnerList = getWinnerList(generalAllocation)
  42. const iWinners = generalWinnerList.map((i) => primaryWinners[i])
  43. const numCans = geometry.canPoints.length
  44. const allocation = Array(numCans).fill(0)
  45. iWinners.forEach((iWinner) => {
  46. allocation[iWinner] = 1
  47. })
  48. const results = {
  49. phases: {
  50. nonpartisanOpenPrimary: primary,
  51. general,
  52. },
  53. phaseNames: [
  54. 'nonpartisanOpenPrimary',
  55. 'general',
  56. ],
  57. geometry,
  58. optionsBag,
  59. socialChoiceResults: {
  60. allocation,
  61. },
  62. }
  63. return results
  64. }
  65. function getWinnerList(allocation) {
  66. const iWinners = []
  67. allocation.forEach((winner, i) => {
  68. if (winner) {
  69. iWinners.push(i)
  70. }
  71. })
  72. return iWinners
  73. }