sapor 0.3.6

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.
Files changed (343) hide show
  1. checksums.yaml +7 -0
  2. data/Area Class Diagram.dia +0 -0
  3. data/Area Class Diagram.png +0 -0
  4. data/Class Diagram.dia +0 -0
  5. data/Class Diagram.png +0 -0
  6. data/Example-Catalonia.md +361 -0
  7. data/Example-Flanders.md +486 -0
  8. data/Example-Greece.md +25 -0
  9. data/Example-Oslo.md +678 -0
  10. data/Example-UnitedKingdom-Referendum.md +132 -0
  11. data/Examples.md +15 -0
  12. data/LICENSE +674 -0
  13. data/README.md +103 -0
  14. data/Rakefile +18 -0
  15. data/Technical Documentation.md +14 -0
  16. data/bin/create_installation_package.sh +49 -0
  17. data/bin/install.sh +45 -0
  18. data/bin/sapor.rb +24 -0
  19. data/bin/sapor.sh +106 -0
  20. data/data/hu/hungary-2014.txt +1680 -0
  21. data/data/hu/hungary_2014_screen_scraper.rb +48 -0
  22. data/data/hu/hungary_2014_to_psv.rb +80 -0
  23. data/data/hu/index-2014.txt +106 -0
  24. data/data/ie/2016-04-28_general-election-2016-candidate-details-csv_en.csv +552 -0
  25. data/data/ie/ireland_2016_to_psv.rb +138 -0
  26. data/data/no/2020-01-01_partifordeling_1_st_2017.csv +335 -0
  27. data/data/no/norway_2017_to_psv.rb +61 -0
  28. data/data/pl/2015-gl-lis-okr.csv +42 -0
  29. data/data/pl/poland_2015_to_psv.rb +79 -0
  30. data/data/pl/poland_2015_to_psv_with_ko_and_rsw.rb +94 -0
  31. data/data/pl/poland_2015_to_psv_with_ko_konf_kp_l_and_zp.rb +100 -0
  32. data/data/pl/poland_2015_to_psv_with_ko_sld_and_wi.rb +92 -0
  33. data/data/pl/poland_2015_to_psv_with_sld.rb +84 -0
  34. data/data/pl/poland_2015_to_psv_with_sld_and_wi.rb +85 -0
  35. data/data/uk/inject_ukip_2015_as_brexit_2019_in_2017.rb +54 -0
  36. data/data/uk/united_kingdom_2015.txt +651 -0
  37. data/data/uk/united_kingdom_2015_to_psv.rb +104 -0
  38. data/data/uk/united_kingdom_2017.txt +651 -0
  39. data/data/uk/united_kingdom_2017_to_psv.rb +104 -0
  40. data/data/uk/united_kingdom_2017_to_psv_with_brexit_and_chuk.rb +113 -0
  41. data/data/uk/united_kingdom_2017_to_psv_with_tig.rb +111 -0
  42. data/lib/sapor.rb +147 -0
  43. data/lib/sapor/binomials_cache.rb +45 -0
  44. data/lib/sapor/combinations_distribution.rb +222 -0
  45. data/lib/sapor/denominators.rb +67 -0
  46. data/lib/sapor/dichotomies.rb +138 -0
  47. data/lib/sapor/dichotomy.rb +164 -0
  48. data/lib/sapor/first_past_the_post.rb +82 -0
  49. data/lib/sapor/largest_remainder.rb +118 -0
  50. data/lib/sapor/log4r_logger.rb +49 -0
  51. data/lib/sapor/log_facade.rb +40 -0
  52. data/lib/sapor/many_past_the_post.rb +113 -0
  53. data/lib/sapor/multi_district_leveled_proportional.rb +64 -0
  54. data/lib/sapor/multi_district_proportional.rb +123 -0
  55. data/lib/sapor/multi_district_variable_threshold_proportional.rb +128 -0
  56. data/lib/sapor/number_formatter.rb +45 -0
  57. data/lib/sapor/options.rb +73 -0
  58. data/lib/sapor/poll.rb +282 -0
  59. data/lib/sapor/polychotomy.rb +200 -0
  60. data/lib/sapor/pseudorandom_multirange_enumerator.rb +87 -0
  61. data/lib/sapor/referendum_polychotomy.rb +165 -0
  62. data/lib/sapor/regional_data/area.rb +100 -0
  63. data/lib/sapor/regional_data/austria.rb +84 -0
  64. data/lib/sapor/regional_data/belgium-brussels-2014.psv +46 -0
  65. data/lib/sapor/regional_data/belgium-brussels-20190526.psv +33 -0
  66. data/lib/sapor/regional_data/belgium-flanders-2014.psv +80 -0
  67. data/lib/sapor/regional_data/belgium-flanders-20190526.psv +74 -0
  68. data/lib/sapor/regional_data/belgium-wallonia-2014.psv +114 -0
  69. data/lib/sapor/regional_data/belgium-wallonia-20190526.psv +93 -0
  70. data/lib/sapor/regional_data/belgium.rb +97 -0
  71. data/lib/sapor/regional_data/belgium_brussels.rb +62 -0
  72. data/lib/sapor/regional_data/belgium_flanders.rb +64 -0
  73. data/lib/sapor/regional_data/belgium_wallonia.rb +63 -0
  74. data/lib/sapor/regional_data/catalonia-2012-2015.psv +100 -0
  75. data/lib/sapor/regional_data/catalonia-2012.psv +87 -0
  76. data/lib/sapor/regional_data/catalonia-2015-jxcat.psv +68 -0
  77. data/lib/sapor/regional_data/catalonia-2015-no-jxsi.psv +68 -0
  78. data/lib/sapor/regional_data/catalonia-2015.psv +63 -0
  79. data/lib/sapor/regional_data/catalonia-20171221-with-vox.psv +67 -0
  80. data/lib/sapor/regional_data/catalonia-20171221.psv +61 -0
  81. data/lib/sapor/regional_data/catalonia.rb +124 -0
  82. data/lib/sapor/regional_data/denmark-20150618-with-e-and-p.psv +164 -0
  83. data/lib/sapor/regional_data/denmark-20150618-with-e.psv +153 -0
  84. data/lib/sapor/regional_data/denmark-20150618-with-p.psv +153 -0
  85. data/lib/sapor/regional_data/denmark-20150618.psv +142 -0
  86. data/lib/sapor/regional_data/denmark.rb +128 -0
  87. data/lib/sapor/regional_data/denmark_with_e.rb +128 -0
  88. data/lib/sapor/regional_data/denmark_with_e_and_p.rb +128 -0
  89. data/lib/sapor/regional_data/denmark_with_p.rb +128 -0
  90. data/lib/sapor/regional_data/estonia.rb +88 -0
  91. data/lib/sapor/regional_data/european-union-great-britain-20140522-brexit-chuk.psv +172 -0
  92. data/lib/sapor/regional_data/european-union-great-britain-20140522.psv +146 -0
  93. data/lib/sapor/regional_data/european-union-great-britain-20190523.psv +141 -0
  94. data/lib/sapor/regional_data/european-union-ireland-2014-ia-ri-sd.psv +64 -0
  95. data/lib/sapor/regional_data/european-union-ireland-2014-ia-sd.psv +60 -0
  96. data/lib/sapor/regional_data/european-union-ireland-2014-ia.psv +56 -0
  97. data/lib/sapor/regional_data/european-union-ireland-2014-sd.psv +56 -0
  98. data/lib/sapor/regional_data/european-union-ireland-2014.psv +50 -0
  99. data/lib/sapor/regional_data/european-union-ireland-20190524-ia.psv +58 -0
  100. data/lib/sapor/regional_data/european-union-ireland-20190524.psv +52 -0
  101. data/lib/sapor/regional_data/european_union_27_austria.rb +76 -0
  102. data/lib/sapor/regional_data/european_union_27_croatia.rb +83 -0
  103. data/lib/sapor/regional_data/european_union_27_denmark.rb +77 -0
  104. data/lib/sapor/regional_data/european_union_27_estonia.rb +74 -0
  105. data/lib/sapor/regional_data/european_union_27_finland.rb +74 -0
  106. data/lib/sapor/regional_data/european_union_27_france.rb +84 -0
  107. data/lib/sapor/regional_data/european_union_27_ireland.rb +96 -0
  108. data/lib/sapor/regional_data/european_union_27_ireland_with_ia.rb +97 -0
  109. data/lib/sapor/regional_data/european_union_27_italy.rb +84 -0
  110. data/lib/sapor/regional_data/european_union_27_netherlands.rb +81 -0
  111. data/lib/sapor/regional_data/european_union_27_poland.rb +84 -0
  112. data/lib/sapor/regional_data/european_union_27_romania.rb +78 -0
  113. data/lib/sapor/regional_data/european_union_27_slovakia.rb +80 -0
  114. data/lib/sapor/regional_data/european_union_27_spain.rb +82 -0
  115. data/lib/sapor/regional_data/european_union_27_sweden.rb +76 -0
  116. data/lib/sapor/regional_data/european_union_austria.rb +76 -0
  117. data/lib/sapor/regional_data/european_union_bulgaria.rb +82 -0
  118. data/lib/sapor/regional_data/european_union_croatia.rb +83 -0
  119. data/lib/sapor/regional_data/european_union_cyprus.rb +72 -0
  120. data/lib/sapor/regional_data/european_union_czech_republic.rb +82 -0
  121. data/lib/sapor/regional_data/european_union_denmark.rb +77 -0
  122. data/lib/sapor/regional_data/european_union_estonia.rb +74 -0
  123. data/lib/sapor/regional_data/european_union_finland.rb +74 -0
  124. data/lib/sapor/regional_data/european_union_flanders.rb +74 -0
  125. data/lib/sapor/regional_data/european_union_france.rb +84 -0
  126. data/lib/sapor/regional_data/european_union_french_community_of_belgium.rb +73 -0
  127. data/lib/sapor/regional_data/european_union_germany.rb +86 -0
  128. data/lib/sapor/regional_data/european_union_great_britain.rb +98 -0
  129. data/lib/sapor/regional_data/european_union_greece.rb +77 -0
  130. data/lib/sapor/regional_data/european_union_hungary.rb +76 -0
  131. data/lib/sapor/regional_data/european_union_ireland.rb +96 -0
  132. data/lib/sapor/regional_data/european_union_ireland_with_ia.rb +97 -0
  133. data/lib/sapor/regional_data/european_union_italy.rb +84 -0
  134. data/lib/sapor/regional_data/european_union_latvia.rb +81 -0
  135. data/lib/sapor/regional_data/european_union_lithuania.rb +80 -0
  136. data/lib/sapor/regional_data/european_union_luxembourg.rb +75 -0
  137. data/lib/sapor/regional_data/european_union_malta.rb +71 -0
  138. data/lib/sapor/regional_data/european_union_netherlands.rb +81 -0
  139. data/lib/sapor/regional_data/european_union_northern_ireland.rb +75 -0
  140. data/lib/sapor/regional_data/european_union_poland.rb +84 -0
  141. data/lib/sapor/regional_data/european_union_portugal.rb +75 -0
  142. data/lib/sapor/regional_data/european_union_romania.rb +78 -0
  143. data/lib/sapor/regional_data/european_union_slovakia.rb +81 -0
  144. data/lib/sapor/regional_data/european_union_slovenia.rb +85 -0
  145. data/lib/sapor/regional_data/european_union_spain.rb +82 -0
  146. data/lib/sapor/regional_data/european_union_sweden.rb +76 -0
  147. data/lib/sapor/regional_data/finland-20150419-with-sin.psv +224 -0
  148. data/lib/sapor/regional_data/finland-20150419.psv +212 -0
  149. data/lib/sapor/regional_data/finland.rb +107 -0
  150. data/lib/sapor/regional_data/finland_with_sin.rb +107 -0
  151. data/lib/sapor/regional_data/flanders-2014.psv +96 -0
  152. data/lib/sapor/regional_data/flanders-20190526.psv +87 -0
  153. data/lib/sapor/regional_data/flanders.rb +115 -0
  154. data/lib/sapor/regional_data/france.rb +38 -0
  155. data/lib/sapor/regional_data/greece.rb +92 -0
  156. data/lib/sapor/regional_data/hungary-2014.psv +2104 -0
  157. data/lib/sapor/regional_data/hungary.rb +116 -0
  158. data/lib/sapor/regional_data/iceland-20161029-midflokkurinn.psv +94 -0
  159. data/lib/sapor/regional_data/iceland-20161029.psv +88 -0
  160. data/lib/sapor/regional_data/iceland-20171028-with-j.psv +94 -0
  161. data/lib/sapor/regional_data/iceland-20171028.psv +85 -0
  162. data/lib/sapor/regional_data/iceland.rb +149 -0
  163. data/lib/sapor/regional_data/ireland-20160226-2020-candidates.psv +322 -0
  164. data/lib/sapor/regional_data/ireland-20160226-2020.psv +344 -0
  165. data/lib/sapor/regional_data/ireland-20160226.psv +348 -0
  166. data/lib/sapor/regional_data/ireland.rb +165 -0
  167. data/lib/sapor/regional_data/latvia-20141004-kpv-p-par.psv +109 -0
  168. data/lib/sapor/regional_data/latvia-20141004-kpv-par.psv +103 -0
  169. data/lib/sapor/regional_data/latvia-20141004-kpv.psv +97 -0
  170. data/lib/sapor/regional_data/latvia-20141004.psv +89 -0
  171. data/lib/sapor/regional_data/latvia-20181006.psv +104 -0
  172. data/lib/sapor/regional_data/latvia.rb +111 -0
  173. data/lib/sapor/regional_data/luxembourg-20131020.psv +60 -0
  174. data/lib/sapor/regional_data/luxembourg-20181014.psv +59 -0
  175. data/lib/sapor/regional_data/luxembourg.rb +88 -0
  176. data/lib/sapor/regional_data/netherlands.rb +108 -0
  177. data/lib/sapor/regional_data/norway-20170911.psv +331 -0
  178. data/lib/sapor/regional_data/norway.rb +130 -0
  179. data/lib/sapor/regional_data/norwegian_municipality.rb +68 -0
  180. data/lib/sapor/regional_data/poland-20151025-with-ko-and-l-without-n-po-r-and-zl.psv +321 -0
  181. data/lib/sapor/regional_data/poland-20151025-with-ko-konf-kp-l-and-zp-without-k-k15-n-pis-po-psl-r-and-zl.psv +280 -0
  182. data/lib/sapor/regional_data/poland-20151025-with-ko-sld-and-wi-without-n-po-and-zl.psv +403 -0
  183. data/lib/sapor/regional_data/poland-20151025-with-sld-and-wi-without-zl.psv +444 -0
  184. data/lib/sapor/regional_data/poland-20151025-with-sld-without-zl.psv +403 -0
  185. data/lib/sapor/regional_data/poland-20151025.psv +403 -0
  186. data/lib/sapor/regional_data/poland.rb +125 -0
  187. data/lib/sapor/regional_data/poland_with_ko_and_l_without_n_po_r_and_zl.rb +122 -0
  188. data/lib/sapor/regional_data/poland_with_ko_konf_kp_l_and_zp_without_k_k15_n_pis_po_psl_r_and_zl.rb +123 -0
  189. data/lib/sapor/regional_data/poland_with_ko_sld_and_wi_without_n_po_and_zl.rb +125 -0
  190. data/lib/sapor/regional_data/poland_with_sld_and_wi_without_zl.rb +126 -0
  191. data/lib/sapor/regional_data/poland_with_sld_without_zl.rb +126 -0
  192. data/lib/sapor/regional_data/portugal-20151004-with-a-and-ch-without-paf.psv +438 -0
  193. data/lib/sapor/regional_data/portugal-20151004-with-a-and-il-without-paf.psv +438 -0
  194. data/lib/sapor/regional_data/portugal-20151004-with-a-ch-and-il-without-paf.psv +461 -0
  195. data/lib/sapor/regional_data/portugal-20151004-with-a-without-paf.psv +415 -0
  196. data/lib/sapor/regional_data/portugal-20151004-with-ch-and-il-without-paf.psv +438 -0
  197. data/lib/sapor/regional_data/portugal-20151004-without-paf.psv +392 -0
  198. data/lib/sapor/regional_data/portugal-20151004.psv +370 -0
  199. data/lib/sapor/regional_data/portugal.rb +101 -0
  200. data/lib/sapor/regional_data/portugal_with_a_and_ch_without_paf.rb +92 -0
  201. data/lib/sapor/regional_data/portugal_with_a_and_il_without_paf.rb +92 -0
  202. data/lib/sapor/regional_data/portugal_with_a_ch_and_il_without_paf.rb +92 -0
  203. data/lib/sapor/regional_data/portugal_with_a_without_paf.rb +92 -0
  204. data/lib/sapor/regional_data/portugal_with_ch_and_il_without_paf.rb +92 -0
  205. data/lib/sapor/regional_data/portugal_without_paf.rb +92 -0
  206. data/lib/sapor/regional_data/slovakia.rb +81 -0
  207. data/lib/sapor/regional_data/slovenia.rb +114 -0
  208. data/lib/sapor/regional_data/spain-20160626.psv +619 -0
  209. data/lib/sapor/regional_data/spain.rb +136 -0
  210. data/lib/sapor/regional_data/sweden.rb +92 -0
  211. data/lib/sapor/regional_data/sweden_20140914.rb +89 -0
  212. data/lib/sapor/regional_data/united_kingdom-2015.psv +4358 -0
  213. data/lib/sapor/regional_data/united_kingdom-20170608-brexit-chuk.psv +5154 -0
  214. data/lib/sapor/regional_data/united_kingdom-20170608-brexit.psv +4521 -0
  215. data/lib/sapor/regional_data/united_kingdom-20170608-tig.psv +4529 -0
  216. data/lib/sapor/regional_data/united_kingdom-20170608.psv +3894 -0
  217. data/lib/sapor/regional_data/united_kingdom.rb +94 -0
  218. data/lib/sapor/regional_data/united_kingdom_with_brexit.rb +110 -0
  219. data/lib/sapor/regional_data/united_kingdom_with_brexit_and_chuk.rb +111 -0
  220. data/lib/sapor/regional_data/united_kingdom_with_tig.rb +111 -0
  221. data/lib/sapor/regional_data/utopia.rb +66 -0
  222. data/lib/sapor/regional_data/wallonia-2014.psv +101 -0
  223. data/lib/sapor/regional_data/wallonia-20190526.psv +88 -0
  224. data/lib/sapor/regional_data/wallonia.rb +112 -0
  225. data/lib/sapor/representatives_polychotomy.rb +338 -0
  226. data/lib/sapor/single_district_proportional.rb +75 -0
  227. data/sapor.gemspec +35 -0
  228. data/spec/integration/area_spec.rb +28 -0
  229. data/spec/integration/poll_spec.rb +112 -0
  230. data/spec/integration/sample.poll +8 -0
  231. data/spec/spec_helper.rb +31 -0
  232. data/spec/unit/area_spec.rb +115 -0
  233. data/spec/unit/austria_spec.rb +76 -0
  234. data/spec/unit/belgium_brussels_spec.rb +58 -0
  235. data/spec/unit/belgium_flanders_spec.rb +62 -0
  236. data/spec/unit/belgium_spec.rb +26 -0
  237. data/spec/unit/belgium_wallonia_spec.rb +65 -0
  238. data/spec/unit/binomials_cache_spec.rb +34 -0
  239. data/spec/unit/catalonia_spec.rb +61 -0
  240. data/spec/unit/catalonia_with_vox_spec.rb +62 -0
  241. data/spec/unit/combinations_distribution_spec.rb +241 -0
  242. data/spec/unit/denmark_spec.rb +56 -0
  243. data/spec/unit/denmark_with_e_and_p_spec.rb +58 -0
  244. data/spec/unit/denmark_with_e_spec.rb +57 -0
  245. data/spec/unit/denmark_with_p_spec.rb +57 -0
  246. data/spec/unit/denominators_spec.rb +40 -0
  247. data/spec/unit/dichotomies_spec.rb +154 -0
  248. data/spec/unit/dichotomy_spec.rb +320 -0
  249. data/spec/unit/estonia_spec.rb +65 -0
  250. data/spec/unit/european_union_27_austria_spec.rb +61 -0
  251. data/spec/unit/european_union_27_croatia_spec.rb +60 -0
  252. data/spec/unit/european_union_27_denmark_spec.rb +62 -0
  253. data/spec/unit/european_union_27_estonia_spec.rb +94 -0
  254. data/spec/unit/european_union_27_finland_spec.rb +75 -0
  255. data/spec/unit/european_union_27_france_spec.rb +73 -0
  256. data/spec/unit/european_union_27_ireland_spec.rb +72 -0
  257. data/spec/unit/european_union_27_ireland_with_ia_spec.rb +74 -0
  258. data/spec/unit/european_union_27_italy_spec.rb +69 -0
  259. data/spec/unit/european_union_27_netherlands_spec.rb +81 -0
  260. data/spec/unit/european_union_27_poland_spec.rb +69 -0
  261. data/spec/unit/european_union_27_romania_spec.rb +67 -0
  262. data/spec/unit/european_union_27_slovakia_spec.rb +111 -0
  263. data/spec/unit/european_union_27_spain_spec.rb +130 -0
  264. data/spec/unit/european_union_27_sweden_spec.rb +89 -0
  265. data/spec/unit/european_union_austria_spec.rb +61 -0
  266. data/spec/unit/european_union_bulgaria_spec.rb +97 -0
  267. data/spec/unit/european_union_croatia_spec.rb +59 -0
  268. data/spec/unit/european_union_cyprus_spec.rb +65 -0
  269. data/spec/unit/european_union_czech_republic_spec.rb +125 -0
  270. data/spec/unit/european_union_denmark_spec.rb +61 -0
  271. data/spec/unit/european_union_estonia_spec.rb +93 -0
  272. data/spec/unit/european_union_finland_spec.rb +75 -0
  273. data/spec/unit/european_union_flanders_spec.rb +56 -0
  274. data/spec/unit/european_union_france_spec.rb +73 -0
  275. data/spec/unit/european_union_french_community_of_belgium_spec.rb +61 -0
  276. data/spec/unit/european_union_germany_spec.rb +90 -0
  277. data/spec/unit/european_union_great_britain_spec.rb +87 -0
  278. data/spec/unit/european_union_greece_spec.rb +148 -0
  279. data/spec/unit/european_union_hungary_spec.rb +57 -0
  280. data/spec/unit/european_union_ireland_spec.rb +72 -0
  281. data/spec/unit/european_union_ireland_with_ia_spec.rb +74 -0
  282. data/spec/unit/european_union_italy_spec.rb +69 -0
  283. data/spec/unit/european_union_latvia_spec.rb +76 -0
  284. data/spec/unit/european_union_lithuania_spec.rb +68 -0
  285. data/spec/unit/european_union_luxembourg_spec.rb +63 -0
  286. data/spec/unit/european_union_malta_spec.rb +60 -0
  287. data/spec/unit/european_union_netherlands_spec.rb +81 -0
  288. data/spec/unit/european_union_northern_ireland_spec.rb +66 -0
  289. data/spec/unit/european_union_poland_spec.rb +69 -0
  290. data/spec/unit/european_union_portugal_spec.rb +77 -0
  291. data/spec/unit/european_union_romania_spec.rb +67 -0
  292. data/spec/unit/european_union_slovakia_spec.rb +111 -0
  293. data/spec/unit/european_union_slovenia_spec.rb +77 -0
  294. data/spec/unit/european_union_spain_spec.rb +129 -0
  295. data/spec/unit/european_union_sweden_spec.rb +89 -0
  296. data/spec/unit/finland_spec.rb +65 -0
  297. data/spec/unit/finland_with_sin_spec.rb +67 -0
  298. data/spec/unit/first_past_the_post_spec.rb +54 -0
  299. data/spec/unit/flanders_spec.rb +70 -0
  300. data/spec/unit/france_spec.rb +32 -0
  301. data/spec/unit/greece_spec.rb +118 -0
  302. data/spec/unit/hungary_spec.rb +132 -0
  303. data/spec/unit/iceland_spec.rb +57 -0
  304. data/spec/unit/iceland_with_j_spec.rb +58 -0
  305. data/spec/unit/ireland_2016_spec.rb +62 -0
  306. data/spec/unit/ireland_spec.rb +62 -0
  307. data/spec/unit/largest_remainder_spec.rb +79 -0
  308. data/spec/unit/latvia_spec.rb +62 -0
  309. data/spec/unit/luxembourg_spec.rb +55 -0
  310. data/spec/unit/multi_district_leveled_proportional_spec.rb +49 -0
  311. data/spec/unit/multi_district_proportional_spec.rb +81 -0
  312. data/spec/unit/netherlands_spec.rb +107 -0
  313. data/spec/unit/norway_spec.rb +69 -0
  314. data/spec/unit/norwegian_municipality_spec.rb +89 -0
  315. data/spec/unit/number_formatter_spec.rb +173 -0
  316. data/spec/unit/poland_spec.rb +62 -0
  317. data/spec/unit/poland_with_ko_and_l_without_n_po_r_and_zl_spec.rb +60 -0
  318. data/spec/unit/poland_with_ko_konf_kp_l_and_zp_without_k_k15_n_pis_po_psl_r_and_zl_spec.rb +59 -0
  319. data/spec/unit/poland_with_ko_sld_and_wi_without_n_po_and_zl_spec.rb +62 -0
  320. data/spec/unit/poland_with_sld_and_wi_without_zl_spec.rb +63 -0
  321. data/spec/unit/poland_with_sld_without_zl_spec.rb +62 -0
  322. data/spec/unit/poll_spec.rb +110 -0
  323. data/spec/unit/portugal_spec.rb +66 -0
  324. data/spec/unit/portugal_with_a_and_ch_without_paf_spec.rb +68 -0
  325. data/spec/unit/portugal_with_a_and_il_without_paf_spec.rb +68 -0
  326. data/spec/unit/portugal_with_a_ch_and_il_without_paf_spec.rb +69 -0
  327. data/spec/unit/portugal_with_a_without_paf_spec.rb +67 -0
  328. data/spec/unit/portugal_with_ch_and_il_without_paf_spec.rb +68 -0
  329. data/spec/unit/portugal_without_paf_spec.rb +66 -0
  330. data/spec/unit/pseudorandom_multirange_enumerator_spec.rb +82 -0
  331. data/spec/unit/referendum_polychotomy_spec.rb +289 -0
  332. data/spec/unit/representatives_polychotomy_spec.rb +332 -0
  333. data/spec/unit/slovakia_spec.rb +99 -0
  334. data/spec/unit/slovenia_spec.rb +80 -0
  335. data/spec/unit/spain_spec.rb +101 -0
  336. data/spec/unit/sweden_20140914_spec.rb +112 -0
  337. data/spec/unit/sweden_spec.rb +113 -0
  338. data/spec/unit/united_kingdom_spec.rb +65 -0
  339. data/spec/unit/united_kingdom_with_brexit_and_chuk_spec.rb +67 -0
  340. data/spec/unit/united_kingdom_with_brexit_spec.rb +66 -0
  341. data/spec/unit/united_kingdom_with_tig_spec.rb +66 -0
  342. data/spec/unit/wallonia_spec.rb +70 -0
  343. metadata +502 -0
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Statistical Analysis of Polling Results (SAPoR)
4
+ # Copyright (C) 2020 Filip van Laenen <f.a.vanlaenen@ieee.org>
5
+ #
6
+ # This file is part of SAPoR.
7
+ #
8
+ # SAPoR is free software: you can redistribute it and/or modify it under the
9
+ # terms of the GNU General Public License as published by the Free Software
10
+ # Foundation, either version 3 of the License, or (at your option) any later
11
+ # version.
12
+ #
13
+ # SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY
14
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16
+ #
17
+ # You can find a copy of the GNU General Public License in /doc/gpl.txt
18
+ #
19
+
20
+ require 'prime'
21
+
22
+ module Sapor
23
+ #
24
+ # A pseudo-random multi-range enumerator. It uses prime numbers to iterate
25
+ # in a pseudo-random way through the hyper-rectangle with dimensions
26
+ # provided as the constructor parameter. If a suitable set of primes can't
27
+ # be found, the constructor will raise an exception.
28
+ #
29
+ class PseudoRandomMultiRangeEnumerator
30
+ def initialize(dimensions)
31
+ @dimensions = dimensions
32
+ @incrementers = calculate_incrementers
33
+ end
34
+
35
+ def each
36
+ return enum_for { @dimensions.inject(:*) } unless block_given?
37
+ counters = Array.new(@dimensions.size, 0)
38
+ loop do
39
+ yield(counters.dup)
40
+ @incrementers.each_with_index do |incrementer, i|
41
+ counters[i] = (counters[i] + incrementer).modulo(@dimensions[i])
42
+ end
43
+ break if all_zeros?(counters)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def calculate_incrementers
50
+ incrementers = []
51
+ incrementer = 0
52
+ calculate_incrementer_indexes.each do |i|
53
+ incrementer = next_incrementer(incrementer)
54
+ if @dimensions[i] < incrementer
55
+ fail(ArgumentError, 'Could not construct suitable incrementers.')
56
+ end
57
+ incrementers[i] = incrementer
58
+ end
59
+ incrementers
60
+ end
61
+
62
+ def calculate_incrementer_indexes
63
+ @dimensions.each_with_index.sort.map(&:last)
64
+ end
65
+
66
+ def next_incrementer(incrementer)
67
+ incrementer += 1
68
+ while incrementer > 1 &&
69
+ (!prime?(incrementer) || !relative_prime?(incrementer))
70
+ incrementer += 1
71
+ end
72
+ incrementer
73
+ end
74
+
75
+ def prime?(number)
76
+ Prime.prime?(number)
77
+ end
78
+
79
+ def relative_prime?(incrementer)
80
+ @dimensions.map { |dimension| dimension.gcd(incrementer) }.max == 1
81
+ end
82
+
83
+ def all_zeros?(counters)
84
+ counters.max == 0
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,165 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Statistical Analysis of Polling Results (SAPoR)
4
+ # Copyright (C) 2020 Filip van Laenen <f.a.vanlaenen@ieee.org>
5
+ #
6
+ # This file is part of SAPoR.
7
+ #
8
+ # SAPoR is free software: you can redistribute it and/or modify it under the
9
+ # terms of the GNU General Public License as published by the Free Software
10
+ # Foundation, either version 3 of the License, or (at your option) any later
11
+ # version.
12
+ #
13
+ # SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY
14
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16
+ #
17
+ # You can find a copy of the GNU General Public License in /doc/gpl.txt
18
+ #
19
+
20
+ module Sapor
21
+ #
22
+ # Represents a polychotomy for a referendum.
23
+ #
24
+ class ReferendumPolychotomy < Polychotomy
25
+ include NumberFormatter
26
+
27
+ def initialize(results, area, dichotomies, max_error)
28
+ super(results, area, dichotomies, max_error)
29
+ end
30
+
31
+ def refine
32
+ no_of_new_simulations = 0
33
+ new_votes = create_new_votes_distributions
34
+ while @no_of_data_points == 0 || no_of_new_simulations == 0 ||
35
+ no_of_new_simulations < @no_of_simulations
36
+ no_of_new_simulations += try_next_data_point(new_votes)
37
+ end
38
+ unless @no_of_simulations == 0
39
+ @error_estimate = calculate_error_estimate(new_votes)
40
+ end
41
+ @votes = merge_distributions(@votes, new_votes)
42
+ @no_of_simulations += no_of_new_simulations
43
+ end
44
+
45
+ def try_next_data_point(new_votes)
46
+ data_point = next_data_point
47
+ if data_point[OTHER] >= 0
48
+ simulate(new_votes, data_point)
49
+ new_simulation = 1
50
+ else
51
+ new_simulation = 0
52
+ end
53
+ @no_of_data_points += 1
54
+ new_simulation
55
+ end
56
+
57
+ def report
58
+ choice_lengths = @choices.map(&:length)
59
+ choice_lengths << 6
60
+ max_choice_width = choice_lengths.max
61
+ sorted_choices = sort_choices_by_result
62
+ choice_lines = sorted_choices.map.with_index do |choice, i|
63
+ next_choice = sorted_choices[i + 1]
64
+ create_choice_report_line(choice, i, next_choice, max_choice_width)
65
+ end
66
+ 'Most probable rounded fractions, fractions and 95% confidence' \
67
+ " intervals:\n" + 'Choice'.ljust(max_choice_width) +
68
+ ' Result MPRF MPF CI(95%) P(>↓) P(R↑) P(R)' \
69
+ " P(R↓)\n" + choice_lines.join("\n")
70
+ end
71
+
72
+ def progress_report
73
+ space_size_ratio = space_size.to_f / @no_of_data_points
74
+ if space_size_ratio > 10
75
+ space_size_ratio = with_thousands_separator(space_size_ratio.round)
76
+ else
77
+ space_size_ratio = space_size_ratio.round(1)
78
+ end
79
+ "#{with_thousands_separator(@no_of_simulations)} simulations out of " \
80
+ "#{with_thousands_separator(@no_of_data_points)} data" \
81
+ " points, 1 / #{space_size_ratio} of search space size" \
82
+ " (#{with_thousands_separator(space_size)})."
83
+ end
84
+
85
+ def write_outputs(filename)
86
+ write_rankings(filename)
87
+ end
88
+
89
+ private
90
+
91
+ def create_new_votes_distributions
92
+ votes = {}
93
+ @choices.each do |choice|
94
+ next if choice == OTHER
95
+ votes[choice] = CombinationsDistribution.new
96
+ @ranges[choice].each do |value|
97
+ votes[choice][value] = 0.to_lf
98
+ end
99
+ end
100
+ votes
101
+ end
102
+
103
+ def simulate(votes, data_point)
104
+ combinations = 1.to_lf
105
+ data_point.each do |choice, value|
106
+ combinations *= BinomialsCache.binomial(value, @results[choice])
107
+ end
108
+ @combinations_sum += combinations
109
+ data_point.each do |choice, value|
110
+ votes[choice][value] += combinations unless choice == OTHER
111
+ end
112
+ @choices.each do |a|
113
+ @choices.each do |b|
114
+ if data_point[a] > data_point[b]
115
+ @comparisons[a + '>' + b] += combinations
116
+ end
117
+ end
118
+ end
119
+ @choices.sort { |a, b| data_point[b] <=> data_point[a] }.each_with_index do |choice, i|
120
+ @rankings[choice + '@' + i.to_s] += combinations
121
+ end
122
+ end
123
+
124
+ def calculate_error_estimate(new_simulations)
125
+ error_estimate = 0
126
+ @choices.each do |choice|
127
+ next if choice == OTHER
128
+ mpv_new = calculate_most_probable_fraction(choice, new_simulations)
129
+ mpv_old = calculate_most_probable_fraction(choice, @votes)
130
+ mpv_delta = (mpv_new - mpv_old).abs
131
+ mprf_new = calculate_most_probable_rounded_fraction(choice, new_simulations)
132
+ mprf_old = calculate_most_probable_rounded_fraction(choice, @votes)
133
+ mprf_delta = (mprf_new - mprf_old).abs
134
+ error_estimate = [error_estimate, mpv_delta, mprf_delta].max
135
+ end
136
+ error_estimate
137
+ end
138
+
139
+ def merge_distributions(distributions1, distributions2)
140
+ merged_distributions = {}
141
+ @choices.each do |choice|
142
+ unless choice == OTHER
143
+ merged_distributions[choice] = distributions1[choice] + \
144
+ distributions2[choice]
145
+ end
146
+ end
147
+ merged_distributions
148
+ end
149
+
150
+ def create_choice_report_line(choice, i, next_choice, max_choice_width)
151
+ ci_values = @votes[choice].confidence_interval(0.95)
152
+ confidence_interval = ci_values.map { |x| x.to_f / @area.population_size }
153
+ choice.ljust(max_choice_width) + ' ' + \
154
+ six_char_percentage(result(choice)) + ' ' + \
155
+ six_char_percentage(most_probable_rounded_fraction(choice)) + ' ' + \
156
+ six_char_percentage(most_probable_fraction(choice)) + ' ' + \
157
+ six_char_percentage(confidence_interval.first) + '–' + \
158
+ six_char_percentage(confidence_interval.last) + ' ' + \
159
+ (next_choice.nil? ? ' ' : six_char_percentage(larger_than(choice, next_choice))) + ' ' + \
160
+ (i == 0 ? ' ' : six_char_percentage(ranking(choice, i - 1))) + ' ' + \
161
+ six_char_percentage(ranking(choice, i)) + \
162
+ (next_choice.nil? ? '' : ' ' + six_char_percentage(ranking(choice, i + 1)))
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,100 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Statistical Analysis of Polling Results (SAPoR)
4
+ # Copyright (C) 2020 Filip van Laenen <f.a.vanlaenen@ieee.org>
5
+ #
6
+ # This file is part of SAPoR.
7
+ #
8
+ # SAPoR is free software: you can redistribute it and/or modify it under the
9
+ # terms of the GNU General Public License as published by the Free Software
10
+ # Foundation, either version 3 of the License, or (at your option) any later
11
+ # version.
12
+ #
13
+ # SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY
14
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16
+ #
17
+ # You can find a copy of the GNU General Public License in /doc/gpl.txt
18
+ #
19
+
20
+ module Sapor
21
+ #
22
+ # Superclass for any area (country, region, city, etc...) for which
23
+ # polls can be registered.
24
+ #
25
+ class Area
26
+ def threshold
27
+ nil
28
+ end
29
+
30
+ def lines_to_election_results(lines, capped = false)
31
+ results = {}
32
+ candidates = {}
33
+ lines.each_line do |line|
34
+ add_line_to_election_results(results, candidates, line, capped)
35
+ end
36
+ [results, candidates]
37
+ end
38
+
39
+ def load_capped_election_results(filename)
40
+ load_full_election_results(filename, true)
41
+ end
42
+
43
+ def load_election_results(filename)
44
+ load_full_election_results(filename, false).first
45
+ end
46
+
47
+ def load_full_election_results(filename, capped)
48
+ dirname = File.dirname(File.expand_path(__FILE__))
49
+ full_filename = File.expand_path(filename, dirname)
50
+ lines = File.read(full_filename)
51
+ lines_to_election_results(lines, capped)
52
+ end
53
+
54
+ def summarize_election_results(election_results)
55
+ summary = {}
56
+ election_results.each_value do |local_results|
57
+ add_local_results_to_summary(summary, local_results)
58
+ end
59
+ summary
60
+ end
61
+
62
+ private
63
+
64
+ def add_line_to_election_results(results, candidates, line, capped)
65
+ return if line.strip.empty? || line.strip.start_with?('#')
66
+ values = line.split('|')
67
+ fail(ArgumentError, "Broken line: #{line}.") unless values.size.equal?(capped ? 4 : 3)
68
+ add_line_values_to_election_results(results, candidates, values, capped)
69
+ end
70
+
71
+ def add_line_values_to_election_results(results, candidates, values, capped)
72
+ constituency = values[0].strip
73
+ unless results.key?(constituency)
74
+ results[constituency] = {}
75
+ candidates[constituency] = {}
76
+ end
77
+ choice = values[1].strip
78
+ votes = values[2].gsub(',', '').to_i
79
+ if results[constituency].key?(choice)
80
+ fail(ArgumentError, "Choice #{choice} appeared twice in " \
81
+ "constituency #{constituency}.")
82
+ end
83
+ results[constituency][choice] = votes
84
+ if capped
85
+ cap = values[3].gsub(',', '').to_i
86
+ candidates[constituency][choice] = cap
87
+ end
88
+ end
89
+
90
+ def add_local_results_to_summary(summary, local_results)
91
+ local_results.each_pair do |choice, votes|
92
+ if summary.key?(choice)
93
+ summary[choice] += votes
94
+ else
95
+ summary[choice] = votes
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,84 @@
1
+ #
2
+ # Statistical Analysis of Polling Results (SAPoR)
3
+ # Copyright (C) 2020 Filip van Laenen <f.a.vanlaenen@ieee.org>
4
+ #
5
+ # This file is part of SAPoR.
6
+ #
7
+ # SAPoR is free software: you can redistribute it and/or modify it under the
8
+ # terms of the GNU General Public License as published by the Free Software
9
+ # Foundation, either version 3 of the License, or (at your option) any later
10
+ # version.
11
+ #
12
+ # SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY
13
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15
+ #
16
+ # You can find a copy of the GNU General Public License in /doc/gpl.txt
17
+ #
18
+
19
+ module Sapor
20
+ #
21
+ # The regional data for Austria.
22
+ #
23
+ class Austria < Area
24
+ include Singleton
25
+
26
+ def area_code
27
+ 'AT'
28
+ end
29
+
30
+ def coalitions
31
+ COALITIONS
32
+ end
33
+
34
+ def no_of_seats
35
+ NO_OF_SEATS
36
+ end
37
+
38
+ def population_size
39
+ POPULATION_SIZE
40
+ end
41
+
42
+ def seats(simulation)
43
+ electoral_system.project(simulation)
44
+ end
45
+
46
+ private
47
+
48
+ FPO_PARTY = 'Freiheitliche Partei Österreichs'.freeze
49
+ GRUENE_PARTY = 'Die Grünen–Die Grüne Alternative'.freeze
50
+ NEOS_PARTY = 'NEOS–Das Neue Österreich und Liberales Forum'.freeze
51
+ OVP_PARTY = 'Österreichische Volkspartei'.freeze
52
+ SPO_PARTY = 'Sozialdemokratische Partei Österreichs'.freeze
53
+
54
+ COALITIONS = [[FPO_PARTY, OVP_PARTY],
55
+ [FPO_PARTY, SPO_PARTY],
56
+ [GRUENE_PARTY, NEOS_PARTY, OVP_PARTY],
57
+ [GRUENE_PARTY, NEOS_PARTY, SPO_PARTY],
58
+ [GRUENE_PARTY, OVP_PARTY],
59
+ [GRUENE_PARTY, SPO_PARTY],
60
+ [NEOS_PARTY, OVP_PARTY],
61
+ [OVP_PARTY],
62
+ [OVP_PARTY, SPO_PARTY],
63
+ [SPO_PARTY]].freeze
64
+
65
+ NO_OF_SEATS = 183
66
+
67
+ # Voter turnout on 15 October 2017
68
+ # Source: Web page with the official results of the elections of 15 October
69
+ # 2017, downloaded on 29 May 2019,
70
+ # https://en.wikipedia.org/wiki/2017_Austrian_legislative_election
71
+ POPULATION_SIZE = 5_069_929
72
+
73
+ THRESHOLD = 0.04
74
+
75
+ def electoral_system
76
+ if @electoral_system.nil?
77
+ @electoral_system = SingleDistrictProportional.new(
78
+ NO_OF_SEATS, DhondtDenominators, THRESHOLD
79
+ )
80
+ end
81
+ @electoral_system
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,46 @@
1
+ # Statistical Analysis of Polling Results (SAPoR)
2
+ # Copyright (C) 2020 Filip van Laenen <f.a.vanlaenen@ieee.org>
3
+ #
4
+ # This file is part of SAPoR.
5
+ #
6
+ # SAPoR is free software: you can redistribute it and/or modify it under the
7
+ # terms of the GNU General Public License as published by the Free Software
8
+ # Foundation, either version 3 of the License, or (at your option) any later
9
+ # version.
10
+ #
11
+ # SAPoR is distributed in the hope that it will be useful, but WITHOUT ANY
12
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13
+ # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
+ #
15
+ # You can find a copy of the GNU General Public License in /doc/gpl.txt
16
+ #
17
+
18
+ # Source: Web page with the official results of the elections of 25 May 2014,
19
+ # downloaded on 3 November 2016,
20
+ # URL: http://verkiezingen2014.belgium.be/nl/cha/results/results_tab_CKR00000.html
21
+
22
+ # Replaced FDF with DéFI
23
+ # Replaced PTB-GO! with PTB
24
+
25
+ Bruxelles-Capitale / Brussel-Hoofdstad | PS | 123,985
26
+ Bruxelles-Capitale / Brussel-Hoofdstad | MR | 115,038
27
+ Bruxelles-Capitale / Brussel-Hoofdstad | DéFI | 55,306
28
+ Bruxelles-Capitale / Brussel-Hoofdstad | Ecolo | 51,133
29
+ Bruxelles-Capitale / Brussel-Hoofdstad | cdH | 46,441
30
+ Bruxelles-Capitale / Brussel-Hoofdstad | PTB | 19,135
31
+ Bruxelles-Capitale / Brussel-Hoofdstad | Open Vld | 13,283
32
+ Bruxelles-Capitale / Brussel-Hoofdstad | N-VA | 13,223
33
+ Bruxelles-Capitale / Brussel-Hoofdstad | Debout les Belges | 11,112
34
+ Bruxelles-Capitale / Brussel-Hoofdstad | sp.a | 9,613
35
+ Bruxelles-Capitale / Brussel-Hoofdstad | ISLAM | 9,387
36
+ Bruxelles-Capitale / Brussel-Hoofdstad | Parti Populaire | 8,641
37
+ Bruxelles-Capitale / Brussel-Hoofdstad | CD&V | 8,173
38
+ Bruxelles-Capitale / Brussel-Hoofdstad | Vlaams Belang | 5,157
39
+ Bruxelles-Capitale / Brussel-Hoofdstad | La Droite | 2,165
40
+ Bruxelles-Capitale / Brussel-Hoofdstad | NATION | 1,623
41
+ Bruxelles-Capitale / Brussel-Hoofdstad | GAUCHES COMMUNES | 1,445
42
+ Bruxelles-Capitale / Brussel-Hoofdstad | Égalitaires! | 953
43
+ Bruxelles-Capitale / Brussel-Hoofdstad | PARTI LIBERTARIEN | 750
44
+ Bruxelles-Capitale / Brussel-Hoofdstad | MG | 567
45
+ Bruxelles-Capitale / Brussel-Hoofdstad | Agora Erasmus | 382
46
+ Bruxelles-Capitale / Brussel-Hoofdstad | LaLutte-DeStrijd | 213