Skip to content

Commit

Permalink
Initial commit of MES (#1)
Browse files Browse the repository at this point in the history
**Context:**

This pull request adds new test cases for the `MethodOfEqualShares` class within the `packages/backend/src/Tabulators/MethodOfEqualShares.ts` file. This includes importing the `fraction.js` package and several testing scenarios such as Basic Example, Influence Budget Redistribution, Random Tiebreaker, Random Tiebreaker with Defined Order, and Valid/Invalid/Under/Bullet Vote Counts.

**Key Changes:**

1. **New test file:**
   - Created a new test file at `packages/backend/src/Tabulators/MethodOfEqualShares.test.ts`.
   - Added comprehensive test cases to validate the functionality of the `MethodOfEqualShares`.

2. **Implementation of MethodOfEqualShares**
   - Added `packages/backend/src/Tabulators/MethodOfEqualShares.ts` to implement the MethodOfEqualShares function.
   - The function calculates election results based on various voting methods and scenarios.
   - It handles tiebreakers, influence budget redistribution, and validates different vote types.

**Testing Instructions:**

- Run the test suite in the `packages/backend/` directory to ensure all new and existing tests pass.

**Motivation:**

- To ensure the reliability and accuracy of the `MethodOfEqualShares` function.
- To handle edge cases and validate the workflow of the election model.
  • Loading branch information
fsargent authored Jul 14, 2024
2 parents 731fec7 + 713fcf9 commit 457cf81
Show file tree
Hide file tree
Showing 2 changed files with 449 additions and 0 deletions.
136 changes: 136 additions & 0 deletions packages/backend/src/Tabulators/MethodOfEqualShares.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { MethodOfEqualShares } from "./MethodOfEqualShares";
const Fraction = require("fraction.js");

describe("Method of Equal Shares Tests", () => {
test("Basic Example", () => {
const candidates = ["Allison", "Bill", "Carmen", "Doug"];
const votes = [
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 4, 4, 0],
[0, 0, 0, 3],
[0, 0, 4, 5],
[0, 0, 4, 5],
[0, 0, 4, 5],
[0, 0, 4, 5],
];
const results = MethodOfEqualShares(candidates, votes, 2, [], false, false);
expect(results.elected.length).toBe(2);
expect(results.elected[0].name).toBe("Allison");
expect(results.elected[1].name).toBe("Doug");
const weightedScoresRound0 =
results.summaryData.weightedScoresByRound[0].map((score) =>
parseFloat(score.toString()),
);
expect(weightedScoresRound0).toStrictEqual([25, 24, 24, 23]);
const weightedScoresRound1 =
results.summaryData.weightedScoresByRound[1].map((score) =>
parseFloat(score.toString()),
);
expect(weightedScoresRound1).toStrictEqual([0, 0, 16, 23]);
});

test("Influence Budget Redistribution", () => {
const candidates = ["Allison", "Bill", "Carmen", "Doug"];
const votes = [
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 4, 4, 0],
[0, 0, 0, 3],
[0, 0, 4, 5],
[0, 0, 4, 5],
[0, 0, 4, 5],
];
const results = MethodOfEqualShares(candidates, votes, 2, [], false, false);
expect(results.elected.length).toBe(2);
expect(results.elected[0].name).toBe("Allison");
expect(results.elected[1].name).toBe("Doug");
const weightedScoresRound0 =
results.summaryData.weightedScoresByRound[0].map((score) =>
parseFloat(score.toString()),
);
expect(weightedScoresRound0).toStrictEqual([40, 39, 23, 18]);
const weightedScoresRound1 =
results.summaryData.weightedScoresByRound[1].map((score) =>
parseFloat(score.toString()),
);
expect(weightedScoresRound1).toStrictEqual([0, 9.75, 14.75, 18]);
});

test("Random Tiebreaker", () => {
const candidates = ["Allison", "Bill", "Carmen", "Doug"];
const votes = [
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 4, 0],
[0, 0, 0, 3],
[0, 0, 4, 5],
[0, 0, 4, 5],
[0, 0, 4, 5],
[0, 0, 4, 5],
];
const results = MethodOfEqualShares(candidates, votes, 2, [], true, false);
expect(results.elected.length).toBe(2);
expect(results.tied[0].length).toBe(2); // two candidates tied in the first round
expect(results.elected[0].name).toBe("Allison"); // random tiebreaker, second place lower index 1
expect(results.elected[1].name).toBe("Doug");
});

test("Random Tiebreaker with Defined Order", () => {
const candidates = ["Allison", "Bill", "Carmen", "Doug"];
const votes = [
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 1, 0],
[5, 5, 4, 0],
[0, 0, 0, 3],
[0, 0, 4, 5],
[0, 0, 4, 5],
[0, 0, 4, 5],
[0, 0, 4, 5],
];
const results = MethodOfEqualShares(
candidates,
votes,
2,
[4, 3, 2, 1],
true,
false,
);
expect(results.elected.length).toBe(2);
expect(results.tied[0].length).toBe(2); // two candidates tied in the first round
expect(results.elected[0].name).toBe("Bill"); // random tiebreaker, second place lower index 1
expect(results.elected[1].name).toBe("Doug");
});

test("Valid/Invalid/Under/Bullet Vote Counts", () => {
const candidates = ["Allison", "Bill", "Carmen"];
const votes = [
[1, 3, 5],
[1, 3, 5],
[1, 3, 5],
[0, 0, 0],
[0, 0, 0],
[-1, 3, 5],
[0, 3, 6],
[5, 0, 0],
[0, 5, 0],
[0, 0, 5],
];
const results = MethodOfEqualShares(candidates, votes, 1, [], false, false);
expect(results.summaryData.nValidVotes).toBe(8);
expect(results.summaryData.nInvalidVotes).toBe(2);
expect(results.summaryData.nUnderVotes).toBe(2);
expect(results.summaryData.nBulletVotes).toBe(3);
});
});
Loading

0 comments on commit 457cf81

Please sign in to comment.