ecclesia
Version:
Framework for political and electoral simulations
85 lines (83 loc) • 3.17 kB
JavaScript
import {
defaultMetric
} from "./chunk-UWP5NKUP.js";
// src/election/attribution/proportionalBase.ts
import { Counter } from "@gouvernathor/python/collections";
function proportionalFromRankIndexFunction({ nSeats, rankIndexFunction }) {
const attrib = (votes, rest = {}) => {
const allVotes = votes.total;
const fractions = new Map([...votes.entries()].map(([party, v]) => [party, v / allVotes]));
const rankIndexValues = new Map([...fractions.entries()].map(([party, f]) => [party, rankIndexFunction(f, 0)]));
const parties = [...votes.keys()].sort((a, b) => rankIndexValues.get(a) - rankIndexValues.get(b));
const seats = new Counter();
s: for (let sn = 0; sn < nSeats; sn++) {
const winner = parties.pop();
seats.increment(winner);
rankIndexValues.set(winner, rankIndexFunction(fractions.get(winner), seats.get(winner)));
for (let pn = 0; pn < parties.length; pn++) {
if (rankIndexValues.get(parties[pn]) >= rankIndexValues.get(winner)) {
parties.splice(pn, 0, winner);
continue s;
}
}
parties.push(winner);
}
return seats;
};
attrib.nSeats = nSeats;
return attrib;
}
function boundedRankIndexMethod({ minNSeats, maxNSeats, rankIndexFunction, metric = defaultMetric }) {
const attrib = (votes, rest = {}) => {
const allVotes = votes.total;
const fractions = new Map([...votes.entries()].map(([party, v]) => [party, v / allVotes]));
const rankIndexValues = new Map([...fractions.entries()].map(([party, f]) => [party, rankIndexFunction(f, 0)]));
const parties = [...votes.keys()].sort((a, b) => rankIndexValues.get(a) - rankIndexValues.get(b));
const seats = new Counter();
let bestSeats = seats.pos;
let bestSeatsMetric = Infinity;
s: for (let sn = 1; sn <= maxNSeats; sn++) {
const winner = parties.pop();
seats.increment(winner);
if (sn >= minNSeats) {
const newMetric = metric({ votes, seats });
if (newMetric < bestSeatsMetric) {
bestSeats = seats.pos;
bestSeatsMetric = newMetric;
}
}
rankIndexValues.set(winner, rankIndexFunction(fractions.get(winner), seats.get(winner)));
for (let pn = 0; pn < parties.length; pn++) {
if (rankIndexValues.get(parties[pn]) >= rankIndexValues.get(winner)) {
parties.splice(pn, 0, winner);
continue s;
}
}
parties.push(winner);
}
return bestSeats;
};
attrib.minNSeats = minNSeats;
attrib.maxNSeats = maxNSeats;
return attrib;
}
function stationaryDivisorFunction(r) {
return (k) => k + r;
}
function rankIndexFunctionFromDivisorFunction(divisorFunction) {
return (t, a) => t / divisorFunction(a);
}
function proportionalFromDivisorFunction({ nSeats, divisorFunction }) {
return proportionalFromRankIndexFunction({
nSeats,
rankIndexFunction: rankIndexFunctionFromDivisorFunction(divisorFunction)
});
}
export {
proportionalFromRankIndexFunction,
boundedRankIndexMethod,
stationaryDivisorFunction,
rankIndexFunctionFromDivisorFunction,
proportionalFromDivisorFunction
};
//# sourceMappingURL=chunk-PFWVE4EX.js.map