schulze-vote 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9866e914ebb502be11324a542dfb49de786a7408
4
- data.tar.gz: 6ade5e377c2b4e1f01444fd110e535e54df71ac4
3
+ metadata.gz: 3016d28cfc6bef2976750a075643923e602bd304
4
+ data.tar.gz: 5dcb2b785be020acc0a8761cda566981867e5486
5
5
  SHA512:
6
- metadata.gz: 03f614f995678224821bc5d81fa8e5d86945aa0086f767ad96b35d6d577d2d9742177b2bad6fa23ad4e7fa0560e3f17638cf1113d1738e463d40f912d76a2a5a
7
- data.tar.gz: 5088baad29d4959dee3df8d4aa232be97090dda1111648a9c388f65dc87e07f4ce6aac55b757e674bbd0f745866424d3eedeb7f69fa43a095ecd2764f89631d5
6
+ metadata.gz: edb55c862b09981e06dbe6e46e0b5093c622e22e3ceffa9ab2af0254081de28bc78566f91ab8756caded729ac0bc49fdf3a2aaec647a495e7ba45bb371299200
7
+ data.tar.gz: 7771b158d623a1efbc5ed29705949e7f5a297d7e29b1cbc8d6731a3986d8346e629f3bad7b7efeee54e4682b673b0c22fc56161a1671fca91adc81f27d1e79ac
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License
2
2
 
3
- Copyright (c) 2010-2015 Renuo GmbH. https://www.renuo.ch
3
+ Copyright (c) 2010-2016 Alessandro Rodi.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -6,6 +6,7 @@ a type of the Condorcet voting methods.
6
6
  ## Master
7
7
 
8
8
  [![Build Status](https://travis-ci.org/coorasse/schulze-vote.svg?branch=master)](https://travis-ci.org/coorasse/schulze-vote)
9
+ [![Gem Version](https://badge.fury.io/rb/schulze-vote.svg)](https://badge.fury.io/rb/schulze-vote)
9
10
 
10
11
  ## Develop
11
12
 
@@ -13,11 +14,6 @@ a type of the Condorcet voting methods.
13
14
  [![Code Climate](https://codeclimate.com/github/coorasse/schulze-vote/badges/gpa.svg)](https://codeclimate.com/github/coorasse/schulze-vote)
14
15
  [![Test Coverage](https://codeclimate.com/github/coorasse/schulze-vote/badges/coverage.svg)](https://codeclimate.com/github/coorasse/schulze-vote/coverage)
15
16
 
16
- Wikipedia:
17
-
18
- * [Schulze method](http://en.wikipedia.org/wiki/Schulze_method) ([deutsch](http://de.wikipedia.org/wiki/Schulze-Methode))
19
- * [Floyd–Warshall algorithm](http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm)
20
-
21
17
  ## Install
22
18
 
23
19
  ``` bash
@@ -34,21 +30,27 @@ gem 'schulze-vote', require: 'schulze_vote'
34
30
 
35
31
  ``` ruby
36
32
  require 'schulze_vote'
37
- vs = SchulzeBasic.do vote_list, candidate_count
38
- vs.ranks
33
+ vote_list = <<EOF
34
+ C;A;B
35
+ C;B;A
36
+ A;B;C
37
+ A;C;B
38
+ B;C;A
39
+ B;A;C
40
+ EOF
41
+ schulze_basic = SchulzeBasic.do(vote_list, candidate_count)
42
+ schulze_classifications.classification_with_ties # shows the final classification
39
43
  ```
40
44
 
41
- `SchulzeBasic.do` - SchulzeBasic is a short term for `Vote::Condorcet::Schulze::Basic` and `.do` is a method of this class!
42
-
43
45
  Input:
44
46
 
45
47
  * `vote_list`
46
- * Array of Arrays: votes of each voter as weights `[ [A,B,C,...],[A,B,C,...],[A,B,C,...] ]`
47
- * String: "A;B;C\nA;B;C\n;3=A;B;C..."
48
+ * Array of Arrays: votes of each voter as weights `[ [1,2,3],[3,2,1],[1,1,2] ]`
49
+ * String: "A;B;C\nB,A;C\n;3=C;B;A"
48
50
  * File: first line **must** be a single integer, following lines like vote_list type String (see vote lists under `examples` directory)
49
- * `candidate_count` Integer: number of candidates
51
+ * `candidate_count` Integer: number of options
50
52
  * **required** for vote_list types of Array and String
51
- * _leave empty if vote_list is a File handle!_
53
+ * leave empty if vote_list is a File
52
54
 
53
55
  ### String/File format:
54
56
 
@@ -84,14 +86,11 @@ Very easy: The reason is, that voters can leave out candidates (they give no spe
84
86
 
85
87
  So, schulze-vote needs to know, how many real candidates are in the voting process.
86
88
 
87
- Okay, for Array inputs it's currently a little bit overhead, because the voters array normally should have the size of the candidates count.
88
- See it as an visual reminder while coding with this gem.
89
-
90
89
  ### Examples
91
90
 
92
91
  #### Array
93
92
 
94
- (Only weight values, no letters here! See section "_preference order to weight_ example")
93
+ Only weight values, no letters here
95
94
 
96
95
  ``` ruby
97
96
  require 'schulze_vote'
@@ -99,6 +98,20 @@ vote_list_array = [[3,2,1],[1,3,2],[3,1,2]]
99
98
  vs = SchulzeBasic.do vote_list_array, 3
100
99
  ```
101
100
 
101
+ ```
102
+ voter => A D C B
103
+
104
+ weight => 4,1,2,3
105
+
106
+ A is on first position = highest prio == 4
107
+ B is on last position == 1
108
+ C is on third position == 2
109
+ D is on second position == 3
110
+ ```
111
+
112
+ Next versions will have an automatic Preference-to-Weight algorithm.
113
+ (Internally only integers are used for calculation of ranking.)
114
+
102
115
  #### String
103
116
 
104
117
  ``` ruby
@@ -120,23 +133,8 @@ require 'schulze_vote'
120
133
  vs = SchulzeBasic.do File.open('path/to/vote.list')
121
134
  ```
122
135
 
123
- ### _preference order to weight_ example
124
-
125
- ```
126
- voter => A D C B
127
-
128
- weight => 4,1,2,3
129
-
130
- A is on first position = highest prio == 4
131
- B is on last position == 1
132
- C is on third position == 2
133
- D is on second position == 3
134
- ```
135
-
136
- Later versions will have an automatic Preference-to-Weight algorithm.
137
- (Internally only integers are used for calculation of ranking.)
138
136
 
139
- ### _SchulzeBasic_
137
+ ### SchulzeBasic
140
138
 
141
139
  It doesn't matter if you start counting at 0 (zero) or 1 (one).
142
140
 
@@ -146,8 +144,16 @@ Internally it will only check if candidate X > candidate Y
146
144
 
147
145
  Output:
148
146
 
149
- * `.ranks` Array: numbers of total wins for each candidate `[candidate A, candidate B, candidate C, ...]`
147
+ * `.ranking` Array: numbers of total wins for each candidate `[candidate A, candidate B, candidate C, ...]`
150
148
  * `.winners_array` Array: set 1 if the candidate is a potential winner `[candidate A, candidate B, candidate C, ...]`
149
+ * `.vote_matrix` Matrix: it contains the pairwise defeats
150
+ * `.play_matrix` Matrix: it contains the strongest paths
151
+ * `.result_matrix` Matrix: it contains the beat pairwise
152
+ * `.beat_couples` Array of Pairs: it contains the beat pairwise in array format
153
+ * `.ties` Array of Array: it contains the ties between candidates
154
+ * `.potential_winners` Array: it contains the possible winners
155
+
156
+
151
157
 
152
158
  ## Example
153
159
 
@@ -168,7 +174,7 @@ votestring = <<EOF
168
174
  7=D;C;E;B;A
169
175
  8=E;B;A;D;C
170
176
  EOF
171
- vs = SchulzeBasic.do votestring, 5
177
+ vs = SchulzeBasic.do(votestring, 5)
172
178
  puts_m vs.vote_matrix
173
179
 
174
180
  #=> [0, 20, 26, 30, 22]
@@ -204,18 +210,17 @@ end
204
210
  #=> ["E", "A", "C", "B", "D"]
205
211
  ```
206
212
 
207
- which is the same result of the reference above.
208
213
 
209
214
  ## Classifications
210
215
 
211
- You have a `classifications(limit_results = false)` that you can call.
216
+ You have a `SchulzeClassifications.new(vs).classifications(limit_results = false)` that you can call.
212
217
  If the number of results is greater then the `limit_results` parameter then a `TooManyClassificationsException`
213
218
  is raised.
214
219
  If you set this parameter to any value other then `false` be careful to catch and manage the exception properly.
215
220
 
216
221
  ## Classification with ties
217
222
 
218
- You have a `classification_with_ties` that you can call.
223
+ You have a `SchulzeClassifications.new(vs).classification_with_ties` that you can call.
219
224
  This method return a uniq classification in array of arrays format to display results on screen.
220
225
  Please note that for cases like this: https://en.wikipedia.org/wiki/User:MarkusSchulze/Schulze_method_examples#Example_4
221
226
  it will return the following: [[B,D], [A,C]]
@@ -233,12 +238,18 @@ it will return the following: [[B,D], [A,C]]
233
238
 
234
239
  ## Problems? Questions?
235
240
 
236
- ![Alessandro Rodi](http://www.gravatar.com/avatar/32d80da41830a6e6c1bb3eb977537e3e)
241
+ [![Alessandro Rodi](http://www.gravatar.com/avatar/32d80da41830a6e6c1bb3eb977537e3e)](https://github.com/coorasse)
237
242
 
238
243
  ## Thanks
239
244
 
240
245
  Thanks to Christoph Grabo for providing the idea and base code of the gem
241
246
 
247
+ ## Wikipedia:
248
+
249
+ * [Schulze method](http://en.wikipedia.org/wiki/Schulze_method) ([deutsch](http://de.wikipedia.org/wiki/Schulze-Methode))
250
+ * [Schulze method examples](https://en.wikipedia.org/wiki/User:MarkusSchulze/Schulze_method_examples)
251
+ * [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm)
252
+
242
253
  ## Copyright
243
254
 
244
255
  See LICENSE for further details.
@@ -29,6 +29,8 @@ module Vote
29
29
  add_excludeds(result)
30
30
  end
31
31
 
32
+ private
33
+
32
34
  def add_excludeds(result)
33
35
  excludeds = (@schulze_basic.candidates - result.flatten) # all remaining elements (not in tie, not winners)
34
36
  excludeds.each do |excluded|
@@ -42,8 +44,6 @@ module Vote
42
44
  result
43
45
  end
44
46
 
45
- private
46
-
47
47
  def better_ranking?(a, b)
48
48
  @schulze_basic.ranking[a] > @schulze_basic.ranking[b]
49
49
  end
@@ -5,10 +5,9 @@ module Vote
5
5
  def initialize(vote_list, candidate_count = nil)
6
6
  @vote_list = vote_list
7
7
  @candidate_count = candidate_count
8
-
8
+ @candidates_abc = []
9
9
  if @candidate_count.nil?
10
10
  insert_vote_file(@vote_list) if vote_list.is_a?(File)
11
-
12
11
  else
13
12
  @vote_matrix = ::Matrix.scalar(@candidate_count, 0).extend(Vote::Matrix)
14
13
  insert_vote_array(@vote_list) if vote_list.is_a?(Array)
@@ -32,27 +31,30 @@ module Vote
32
31
  vs.split(/\n|\n\r|\r/).each do |voter|
33
32
  voter = voter.split(/=/)
34
33
  vcount = (voter.size == 1) ? 1 : voter[0].to_i
35
-
36
34
  vcount.times do
37
35
  tmp = voter.last.split(/;/)
38
36
  vote_array << extract_vote_string(tmp)
39
37
  end
40
38
  end
41
-
42
39
  insert_vote_array vote_array
43
40
  end
44
41
 
45
- def extract_vote_string(tmp) # array of preferences [['1, 2'], ['3']. ['4, 5']]
42
+ def extract_vote_string(tmp) # array of preferences [['1, 2'], ['3']. ['4, 5']]
43
+ tmp2 = flatten_votes(order_and_remap(tmp))
44
+ tmp2.map! { |e| [e[0].to_i, e[1]] } if all_numbers?(tmp2)
45
+ tmp2.sort.map { |e| e[1] } # order, strip & add
46
+ end
47
+
48
+ def flatten_votes(votes)
46
49
  tmp2 = []
47
- order_and_remap(tmp).
48
- map do |e| # find equal-weighted candidates
49
- if e[0].size > 1
50
- e[0].split(/,/).each { |f| tmp2 << [f, e[1]] }
51
- else
52
- tmp2 << e
53
- end
50
+ votes.map do |e| # find equal-weighted candidates
51
+ (e[0].size > 1) ? e[0].split(/,/).each { |f| tmp2 << [f, e[1]] } : tmp2 << e
54
52
  end
55
- tmp2.sort.map { |e| e[1] } # order, strip & add
53
+ tmp2
54
+ end
55
+
56
+ def all_numbers?(array)
57
+ array.map { |e| e[0] }.all? { |el| /\A\d+\z/.match(el) }
56
58
  end
57
59
 
58
60
  def order_and_remap(tmp)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schulze-vote
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Rodi