{"version":3,"sources":["../../src/actors/house.ts","../../src/actors/vote.ts"],"sourcesContent":["import { Counter } from \"@gouvernathor/python/collections\";\nimport { Election } from \"../election\";\nimport { Collection } from \"@gouvernathor/python/collections/abc\";\nimport { Vote } from \"./vote\";\nimport { DisagreementFunction } from \"../election/voting\";\n\n/**\n * An electoral district relating to a House.\n *\n * Several district objects can represent the same territory and have the same\n * voter pool, but relate to different Houses : this is normal.\n * For example, the districts for the US state of Wyoming would be as follows:\n * - one for the three presidential electors\n * - one for the lone federal representative\n * - one or possibly two (depending on the implementation) for the two federal senators\n * All three or four of these districts would have the same voter pool, yet be\n * different objects because they relate to different Houses, even assuming\n * they have the same election method.\n */\nexport class District<Voter, Party> {\n    identifier?: string | number;\n    nSeats?: number;\n    constructor(\n        public electionMethod: Election<Voter, Party>,\n        public voters: Voter[],\n        { identifier, nSeats }: { identifier?: string | number, nSeats?: number } = {},\n    ) {\n        this.identifier = identifier;\n        // if (nSeats === undefined) {\n        //     try {\n        //         nSeats = (electionMethod as BaseElection<Voter, Party, any>).attributionMethod.nSeats;\n        //     } catch (e) { }\n        // }\n        this.nSeats = nSeats;\n    }\n\n    election(candidates: Collection<Party>): Counter<Party> {\n        return this.electionMethod(this.voters, candidates);\n    }\n}\n\n/**\n * A whole House of Parliament.\n * Some constraints:\n * - all members have the same voting power\n * - staggering is not supported - that is, when general elections don't\n *   renew all the seats at once, like in the french or US senates - though\n *   subclasses may implement it by overriding the election method and subclassing\n *   District, for instance.\n */\nexport class House<Voter, Party> {\n    districts: Map<District<Voter, Party>, Counter<Party>>;\n    name?: string;\n    majority?: number;\n    constructor(\n        districts: Iterable<District<Voter, Party>> | Map<District<Voter, Party>, Counter<Party>>,\n        { name, majority = .5 }: { name?: string, majority?: number } = {},\n    ) {\n        if (!(districts instanceof Map)) {\n            districts = new Map([...districts].map(d => [d, new Counter()]));\n        }\n        this.districts = districts as Map<District<Voter, Party>, Counter<Party>>;\n        this.name = name;\n        this.majority = majority;\n    }\n\n    /**\n     * Returns a Counter linking each party to the number of seats it holds,\n     * regardless of the district.\n     * For the array (with repetitions) of the individual members, use the\n     * members.elements() method.\n     */\n    get members(): Counter<Party> {\n        const coun = new Counter<Party>();\n        for (const dmembers of this.districts.values()) {\n            coun.update(dmembers);\n        }\n        return coun;\n    }\n\n    /**\n     * If all districts support providing a theoretical number of seats,\n     * returns the total. Otherwise, returns undefined.\n     */\n    get nSeats(): number | undefined {\n        let nSeats = 0;\n        for (const district of this.districts.keys()) {\n            if (district.nSeats === undefined) {\n                return undefined;\n            }\n            nSeats += district.nSeats;\n        }\n        return nSeats;\n    }\n\n    /**\n     * Triggers an election in each electoral district, returns the members result.\n     */\n    election(candidates: Collection<Party>): Counter<Party> {\n        const members = new Counter<Party>();\n        for (const district of this.districts.keys()) {\n            const elected = district.election(candidates);\n            this.districts.set(district, elected);\n            members.update(elected);\n        }\n        return members;\n    }\n\n    /**\n     * Returns the state of the vote on something having an opinion.\n     * The object of the vote may be a motion or bill, but also a person to elect\n     * or to confirm.\n     */\n    vote<T>(\n        target: T,\n        { disagree }: { disagree: DisagreementFunction<Party, T> },\n    ): Vote {\n        let votesFor = 0;\n        let votesAgainst = 0;\n        for (const [party, nSeats] of this.members) {\n            const disag = disagree(party, target);\n            if (disag > 0) {\n                votesFor += nSeats;\n            } else if (disag < 0) {\n                votesAgainst += nSeats;\n            }\n        }\n        return new Vote(votesFor, votesAgainst);\n    }\n}\n","/**\n * The results of a binary vote.\n * The blank votes are not counted. To calculate a threshold on the whole\n * number of members, use vote.votesFor / house.nSeats.\n * To calculate the threshold on the number of duly elected members, use\n * vote.votesFor / sum(house.members.values()).\n */\nexport class Vote {\n    constructor(\n        public readonly votesFor: number,\n        public readonly votesAgainst: number,\n    ) { }\n\n    /**\n     * Returns the reverse of the vote, inverting the for/against ratio.\n     * Simulates a vote on the opposite motion.\n     */\n    get neg(): Vote {\n        return new Vote(this.votesAgainst, this.votesFor);\n    }\n\n    get votesCast(): number {\n        return this.votesFor + this.votesAgainst;\n    }\n\n    /**\n     * Returns the ratio of votes for over the total number of votes cast.\n     * If there are no votes cast, returns an Infinity.\n     */\n    get ratio(): number {\n        return this.votesFor / this.votesCast;\n    }\n\n    /**\n     * Returns the votes in order of decreasing ratio.\n     * The ties are ordered by decreasing number of positive votes,\n     * and then by the order they came in.\n     */\n    static order(votes: Vote[]): Vote[] {\n        return votes\n            .sort((a, b) => (b.ratio - a.ratio) || (b.votesFor - a.votesFor));\n    }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAwB;;;ACOjB,IAAM,OAAN,MAAM,MAAK;AAAA,EACd,YACoB,UACA,cAClB;AAFkB;AACA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMJ,IAAI,MAAY;AACZ,WAAO,IAAI,MAAK,KAAK,cAAc,KAAK,QAAQ;AAAA,EACpD;AAAA,EAEA,IAAI,YAAoB;AACpB,WAAO,KAAK,WAAW,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,QAAgB;AAChB,WAAO,KAAK,WAAW,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,MAAM,OAAuB;AAChC,WAAO,MACF,KAAK,CAAC,GAAG,MAAO,EAAE,QAAQ,EAAE,SAAW,EAAE,WAAW,EAAE,QAAS;AAAA,EACxE;AACJ;;;ADvBO,IAAM,WAAN,MAA6B;AAAA,EAGhC,YACW,gBACA,QACP,EAAE,YAAY,OAAO,IAAuD,CAAC,GAC/E;AAHS;AACA;AAGP,SAAK,aAAa;AAMlB,SAAK,SAAS;AAAA,EAClB;AAAA,EAEA,SAAS,YAA+C;AACpD,WAAO,KAAK,eAAe,KAAK,QAAQ,UAAU;AAAA,EACtD;AACJ;AAWO,IAAM,QAAN,MAA0B;AAAA,EAI7B,YACI,WACA,EAAE,MAAM,WAAW,IAAG,IAA0C,CAAC,GACnE;AACE,QAAI,EAAE,qBAAqB,MAAM;AAC7B,kBAAY,IAAI,IAAI,CAAC,GAAG,SAAS,EAAE,IAAI,OAAK,CAAC,GAAG,IAAI,2BAAQ,CAAC,CAAC,CAAC;AAAA,IACnE;AACA,SAAK,YAAY;AACjB,SAAK,OAAO;AACZ,SAAK,WAAW;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,UAA0B;AAC1B,UAAM,OAAO,IAAI,2BAAe;AAChC,eAAW,YAAY,KAAK,UAAU,OAAO,GAAG;AAC5C,WAAK,OAAO,QAAQ;AAAA,IACxB;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,SAA6B;AAC7B,QAAI,SAAS;AACb,eAAW,YAAY,KAAK,UAAU,KAAK,GAAG;AAC1C,UAAI,SAAS,WAAW,QAAW;AAC/B,eAAO;AAAA,MACX;AACA,gBAAU,SAAS;AAAA,IACvB;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,YAA+C;AACpD,UAAM,UAAU,IAAI,2BAAe;AACnC,eAAW,YAAY,KAAK,UAAU,KAAK,GAAG;AAC1C,YAAM,UAAU,SAAS,SAAS,UAAU;AAC5C,WAAK,UAAU,IAAI,UAAU,OAAO;AACpC,cAAQ,OAAO,OAAO;AAAA,IAC1B;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KACI,QACA,EAAE,SAAS,GACP;AACJ,QAAI,WAAW;AACf,QAAI,eAAe;AACnB,eAAW,CAAC,OAAO,MAAM,KAAK,KAAK,SAAS;AACxC,YAAM,QAAQ,SAAS,OAAO,MAAM;AACpC,UAAI,QAAQ,GAAG;AACX,oBAAY;AAAA,MAChB,WAAW,QAAQ,GAAG;AAClB,wBAAgB;AAAA,MACpB;AAAA,IACJ;AACA,WAAO,IAAI,KAAK,UAAU,YAAY;AAAA,EAC1C;AACJ;","names":[]}