Skip to content

Commit

Permalink
Merge pull request #31 from lzear/fix-ties
Browse files Browse the repository at this point in the history
Fix ties
  • Loading branch information
lzear authored Dec 6, 2020
2 parents 661a32c + 613c37b commit a699adc
Show file tree
Hide file tree
Showing 33 changed files with 639 additions and 344 deletions.
1 change: 1 addition & 0 deletions .codacy.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
exclude_paths:
- '**/*.test.ts'
- 'tools/gh-pages-publish.ts'
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'prettier/@typescript-eslint',
Expand Down
64 changes: 32 additions & 32 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"
name: 'CodeQL'

on:
push:
Expand All @@ -29,34 +29,34 @@ jobs:
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection

steps:
- name: Checkout repository
uses: actions/checkout@v2

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language

#- run: |
# make bootstrap
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Checkout repository
uses: actions/checkout@v2

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language

#- run: |
# make bootstrap
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
7 changes: 5 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Code coverage"
name: 'Code coverage'

on: [push]

Expand All @@ -14,7 +14,10 @@ jobs:
node-version: '12'
- run: yarn install
- run: yarn test
- run: curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- run:
curl -L
https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64
> ./cc-test-reporter
- run: ./tools/codacy-cov.sh
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/semantic-release.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: "Semantic release"
name: 'Semantic release'

on:
push:
branches: [master,next]
branches: [master, next]

jobs:
release:
Expand Down
17 changes: 17 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
node_modules
coverage
.nyc_output
.DS_Store
*.log
.vscode
.idea
dist
compiled
.awcache
.rpt2_cache
docs
.env
.npmrc

**/*.rej
*.rej
5 changes: 3 additions & 2 deletions .prettierrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ module.exports = {
tabWidth: 2,
singleQuote: true,
printWidth: 80,
"semi": false,
};
proseWrap: 'always',
semi: false,
}
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ node_js:
script:
- npm run test:prod && npm run build
after_success:
- if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then npm run travis-deploy-once "npm run deploy-docs"; fi
- if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then
npm run travis-deploy-once "npm run deploy-docs"; fi
branches:
except:
- /^v\d+\.\d+\.\d+$/
73 changes: 68 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,78 @@
![language](https://img.shields.io/github/languages/top/lzear/votes)
[![license](https://img.shields.io/github/license/lzear/votes)](https://github.com/lzear/votes/blob/master/LICENSE)

> Implementation of some [electoral systems](https://en.wikipedia.org/wiki/Electoral_system)
> Implementation of some
> [electoral systems](https://en.wikipedia.org/wiki/Electoral_system)
## Install
## 🧑‍💻 Install

```sh
yarn install
yarn add votes
```

## 🗳️ Use

```typescript
import { utils as voteUtils, VotingSystem } from 'votes'

const scores = scoresFromBallots(
[
{ ranking: [['Lion'], ['Bear'], ['Sheep']], weight: 4 },
{ ranking: [['Sheep'], ['Bear'], ['Lion']], weight: 3 },
{ ranking: [['Bear', 'Sheep'], ['Lion']], weight: 2 },
],
['Lion', 'Bear', 'Sheep'],
VotingSystem.Schulze,
)
// -> { Lion: 0, Bear: 2, Sheep: 1 }

const ranking = scoresToRanking({ Bear: 2, Lion: 0, Sheep: 1 })
// -> [ [ 'Bear' ], [ 'Sheep' ], [ 'Lion' ] ]
```

## 📊 Voting systems

See
[Comparison of electoral systems (Wikipedia)](https://en.wikipedia.org/wiki/Comparison_of_electoral_systems)
for more information.

**Ranked pairs**: Using the duel results as edges, build an acyclic graph
starting by the strongest score differences. The roots of the graph are the
winners.

**Schulze method**: From the votes, compute the results of all possible duels.
Then remove the most indecisive (closest to 50/50) duels until there is an
undefeated candidate, the winner. This popular voting system is used by several
organizations (Ubuntu, Debian, Wikimedia...).

**Kemeny–Young method**: A relatively complex computation generating a
preference order aiming to minimize dissatisfaction of the voters. Also known as
Kemeny rule, VoteFair popularity ranking, the maximum likelihood method, and the
median relation.

**Minimax Condorcet method**: Ranking the candidates by smallest pairwise
defeat.

**Copeland's method**: Rank candidates by number of duels won against other
candidates.

**Approval voting**: Each voter can select (“approve”) any number of candidates.
The winner is the most-approved candidate.

**Borda's count**: For each voter, every candidate is given a number of points
which equals the number of candidates ranked lower in the voter's preference.

**Instant runoff**: Considering only the top choice of each voter, the candidate
with the fewest votes is eliminated. The election repeats until there is a
winner. This voting system is very similar to single transferable vote method.

**Two-round system**: If no candidate receives 50% of the votes in the first
round, then a second round of voting is held with only the top two candidates.

**Plurality**: Simple voting method where only the preferred candidate of each
voter gets 1 point. AKA first-past-the-post.

## 🤝 Contributing

Contributions, issues and feature requests are welcome!<br />
Feel free to check [issues page](https://github.com/lzear/votes/issues).
Contributions, issues and feature requests are welcome!<br /> Feel free to check
[issues page](https://github.com/lzear/votes/issues).
27 changes: 14 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
},
"scripts": {
"eslint": "eslint --ext .js --ext .ts .",
"fix:prettier": "prettier --write \"**/*.*\"",
"prebuild": "rimraf dist",
"build": "tsc --module commonjs && rollup -c rollup.config.js && typedoc --exclude \"**/*.test.ts\" --out docs --target es6 --theme minimal --mode file src",
"build": "tsc --module commonjs && rollup -c rollup.config.js && typedoc",
"build:watch": "tsc --module commonjs && rollup -c rollup.config.js -w",
"test": "jest --coverage",
"test:watch": "jest --coverage --watch",
Expand Down Expand Up @@ -52,33 +53,33 @@
},
"devDependencies": {
"@commitlint/cli": "^11.0.0",
"@rollup/plugin-commonjs": "^16.0.0",
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^10.0.0",
"@types/jest": "^26.0.15",
"@rollup/plugin-node-resolve": "^11.0.0",
"@types/jest": "^26.0.16",
"@types/lodash": "^4.14.165",
"@types/node": "^14.14.10",
"@typescript-eslint/eslint-plugin": "^4.8.2",
"@typescript-eslint/parser": "^4.8.2",
"@typescript-eslint/eslint-plugin": "^4.9.0",
"@typescript-eslint/parser": "^4.9.0",
"commitizen": "^4.2.2",
"dotenv": "^8.2.0",
"eslint": "^7.14.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.1.4",
"husky": "^4.3.0",
"eslint": "^7.15.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-prettier": "^3.2.0",
"husky": "^4.3.4",
"jest": "^26.6.3",
"lint-staged": "^10.5.2",
"lint-staged": "^10.5.3",
"prettier": "^2.2.1",
"rimraf": "^3.0.2",
"rollup": "^2.33.3",
"rollup": "^2.34.1",
"rollup-plugin-sizes": "^1.0.3",
"rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-typescript2": "^0.29.0",
"semantic-release": "^17.3.0",
"shelljs": "^0.8.4",
"travis-deploy-once": "^5.0.11",
"ts-jest": "^26.4.4",
"ts-node": "^9.0.0",
"ts-node": "^9.1.0",
"typedoc": "^0.19.2",
"typescript": "^4.1.2"
},
Expand Down
20 changes: 0 additions & 20 deletions src/axioms/monotonicity/examples/runoff-cexample.ts

This file was deleted.

Empty file removed src/axioms/monotonicity/readme.md
Empty file.
25 changes: 0 additions & 25 deletions src/axioms/strategic-manipulation/examples/borda-cexample.ts

This file was deleted.

Empty file.
16 changes: 2 additions & 14 deletions src/methods/approbation/index.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,14 @@
import zipObject from 'lodash/zipObject'
import {
SystemUsingRankings,
ScoreObject,
VotingSystem,
Ballot,
} from '../../types'
import { iterateFirstChoices } from '../first-past-the-post'

export const approbation: SystemUsingRankings = {
type: VotingSystem.Approbation,
computeFromBallots(ballots: Ballot[], candidates: string[]): ScoreObject {
const result: ScoreObject = zipObject(
candidates,
new Array(candidates.length).fill(0),
)
ballots.forEach((ballot) => {
if (ballot.ranking.length) {
const votes = ballot.ranking[0].filter((c) => candidates.includes(c))
votes.forEach((v) => {
result[v] += ballot.weight
})
}
})
return result
return iterateFirstChoices(ballots, candidates, () => 1)
},
}
7 changes: 2 additions & 5 deletions src/methods/borda/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import zipObject from 'lodash/zipObject'
import {
SystemUsingRankings,
ScoreObject,
VotingSystem,
Ballot,
} from '../../types'
import { scoresZero } from '../../utils/scoresZero'

export const borda: SystemUsingRankings = {
type: VotingSystem.Borda,
computeFromBallots(ballots: Ballot[], candidates: string[]): ScoreObject {
const result: ScoreObject = zipObject(
candidates,
new Array(candidates.length).fill(0),
)
const result: ScoreObject = scoresZero(candidates)
ballots.forEach((ballot) => {
let voteValue = candidates.length - 1
ballot.ranking.forEach((candidatesAtRank) => {
Expand Down
Loading

0 comments on commit a699adc

Please sign in to comment.