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,68 @@
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
+ require 'spec_helper'
20
+
21
+ describe Sapor::PortugalWithAAndIlWithoutPaf, '#area_code' do
22
+ it 'returns PT∪{A,IL}\{PàF} as the area code' do
23
+ expect(Sapor::PortugalWithAAndIlWithoutPaf.instance.area_code).to \
24
+ eq('PT∪{A,IL}\{PàF}')
25
+ end
26
+ end
27
+
28
+ describe Sapor::PortugalWithAAndIlWithoutPaf, '#no_of_seats' do
29
+ it 'returns 230 as the number of seats' do
30
+ expect(Sapor::PortugalWithAAndIlWithoutPaf.instance.no_of_seats).to eq(230)
31
+ end
32
+ end
33
+
34
+ describe Sapor::PortugalWithAAndIlWithoutPaf, '#population_size' do
35
+ it 'returns a population size of 5,206,113' do
36
+ expect(Sapor::PortugalWithAAndIlWithoutPaf.instance.population_size).to eq(5_206_113)
37
+ end
38
+ end
39
+
40
+ describe Sapor::PortugalWithAAndIlWithoutPaf, '#seats' do
41
+ it 'calculates the number of seats for the election of 2015 adjusted for' \
42
+ ' the seat distribution for 2019' do
43
+ PortugalWithAAndIlWithoutPaf = Sapor::PortugalWithAAndIlWithoutPaf.instance
44
+ results = PortugalWithAAndIlWithoutPaf.overall_election_results_of_2015
45
+ seats = Sapor::PortugalWithAAndIlWithoutPaf.instance.seats(results)
46
+ expect(seats['Partido Social Democrata']).to eq(5 + 37)
47
+ expect(seats['Aliança']).to eq(0 + 37)
48
+ expect(seats['Iniciativa Liberal']).to eq(0 + 80)
49
+ expect(seats['CDS–Partido Popular']).to eq(0 + 30)
50
+ expect(seats['Partido Socialista']).to eq(86 - 54)
51
+ expect(seats['Bloco de Esquerda']).to eq(19 - 15)
52
+ expect(seats['Coligação Democrática Unitária']).to eq(17 - 12)
53
+ expect(seats['Pessoas–Animais–Natureza']).to eq(1 - 1)
54
+ expect(seats['Partido Democrático Republicano']).to eq(0)
55
+ expect(seats['Partido Comunista dos Trabalhadores Portugueses']).to eq(0)
56
+ expect(seats['LIVRE']).to eq(0)
57
+ expect(seats['Partido Nacional Renovador']).to eq(0)
58
+ expect(seats['Partido da Terra']).to eq(0)
59
+ expect(seats['AGIR']).to eq(0)
60
+ expect(seats['Partido Trabalhista Português']).to eq(0)
61
+ expect(seats['Nós, Cidadãos!']).to eq(0)
62
+ expect(seats['Partido Popular Monárquico']).to eq(0)
63
+ expect(seats['Juntos pelo Povo']).to eq(0)
64
+ expect(seats['Partido Unido dos Reformados e Pensionistas']).to eq(0)
65
+ expect(seats['Aliança Açores']).to eq(0)
66
+ expect(seats['Partido Cidadania e Democracia Cristã']).to eq(0)
67
+ end
68
+ end
@@ -0,0 +1,69 @@
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
+ require 'spec_helper'
20
+
21
+ describe Sapor::PortugalWithAChAndIlWithoutPaf, '#area_code' do
22
+ it 'returns PT∪{A,CH,IL}\{PàF} as the area code' do
23
+ expect(Sapor::PortugalWithAChAndIlWithoutPaf.instance.area_code).to \
24
+ eq('PT∪{A,CH,IL}\{PàF}')
25
+ end
26
+ end
27
+
28
+ describe Sapor::PortugalWithAChAndIlWithoutPaf, '#no_of_seats' do
29
+ it 'returns 230 as the number of seats' do
30
+ expect(Sapor::PortugalWithAChAndIlWithoutPaf.instance.no_of_seats).to eq(230)
31
+ end
32
+ end
33
+
34
+ describe Sapor::PortugalWithAChAndIlWithoutPaf, '#population_size' do
35
+ it 'returns a population size of 5,206,113' do
36
+ expect(Sapor::PortugalWithAChAndIlWithoutPaf.instance.population_size).to eq(5_206_113)
37
+ end
38
+ end
39
+
40
+ describe Sapor::PortugalWithAChAndIlWithoutPaf, '#seats' do
41
+ it 'calculates the number of seats for the election of 2015 adjusted for' \
42
+ ' the seat distribution for 2019' do
43
+ PortugalWithAChAndIlWithoutPaf = Sapor::PortugalWithAChAndIlWithoutPaf.instance
44
+ results = PortugalWithAChAndIlWithoutPaf.overall_election_results_of_2015
45
+ seats = Sapor::PortugalWithAChAndIlWithoutPaf.instance.seats(results)
46
+ expect(seats['Partido Social Democrata']).to eq(5 + 33)
47
+ expect(seats['Aliança']).to eq(0 + 33)
48
+ expect(seats['Chega']).to eq(0 + 28)
49
+ expect(seats['Iniciativa Liberal']).to eq(0 + 70)
50
+ expect(seats['CDS–Partido Popular']).to eq(0 + 26)
51
+ expect(seats['Partido Socialista']).to eq(86 - 58)
52
+ expect(seats['Bloco de Esquerda']).to eq(19 - 15)
53
+ expect(seats['Coligação Democrática Unitária']).to eq(17 - 14)
54
+ expect(seats['Pessoas–Animais–Natureza']).to eq(1 - 1)
55
+ expect(seats['Partido Democrático Republicano']).to eq(0)
56
+ expect(seats['Partido Comunista dos Trabalhadores Portugueses']).to eq(0)
57
+ expect(seats['LIVRE']).to eq(0)
58
+ expect(seats['Partido Nacional Renovador']).to eq(0)
59
+ expect(seats['Partido da Terra']).to eq(0)
60
+ expect(seats['AGIR']).to eq(0)
61
+ expect(seats['Partido Trabalhista Português']).to eq(0)
62
+ expect(seats['Nós, Cidadãos!']).to eq(0)
63
+ expect(seats['Partido Popular Monárquico']).to eq(0)
64
+ expect(seats['Juntos pelo Povo']).to eq(0)
65
+ expect(seats['Partido Unido dos Reformados e Pensionistas']).to eq(0)
66
+ expect(seats['Aliança Açores']).to eq(0)
67
+ expect(seats['Partido Cidadania e Democracia Cristã']).to eq(0)
68
+ end
69
+ end
@@ -0,0 +1,67 @@
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
+ require 'spec_helper'
20
+
21
+ describe Sapor::PortugalWithAWithoutPaf, '#area_code' do
22
+ it 'returns PT∪{A}\{PàF} as the area code' do
23
+ expect(Sapor::PortugalWithAWithoutPaf.instance.area_code).to \
24
+ eq('PT∪{A}\{PàF}')
25
+ end
26
+ end
27
+
28
+ describe Sapor::PortugalWithAWithoutPaf, '#no_of_seats' do
29
+ it 'returns 230 as the number of seats' do
30
+ expect(Sapor::PortugalWithAWithoutPaf.instance.no_of_seats).to eq(230)
31
+ end
32
+ end
33
+
34
+ describe Sapor::PortugalWithAWithoutPaf, '#population_size' do
35
+ it 'returns a population size of 5,206,113' do
36
+ expect(Sapor::PortugalWithAWithoutPaf.instance.population_size).to eq(5_206_113)
37
+ end
38
+ end
39
+
40
+ describe Sapor::PortugalWithAWithoutPaf, '#seats' do
41
+ it 'calculates the number of seats for the election of 2015 adjusted for' \
42
+ ' the seat distribution for 2019' do
43
+ PortugalWithAWithoutPaf = Sapor::PortugalWithAWithoutPaf.instance
44
+ results = PortugalWithAWithoutPaf.overall_election_results_of_2015
45
+ seats = Sapor::PortugalWithAWithoutPaf.instance.seats(results)
46
+ expect(seats['Partido Social Democrata']).to eq(5 + 60)
47
+ expect(seats['Aliança']).to eq(0 + 57)
48
+ expect(seats['CDS–Partido Popular']).to eq(0 + 47)
49
+ expect(seats['Partido Socialista']).to eq(86 - 41)
50
+ expect(seats['Bloco de Esquerda']).to eq(19 - 10)
51
+ expect(seats['Coligação Democrática Unitária']).to eq(17 - 10)
52
+ expect(seats['Pessoas–Animais–Natureza']).to eq(1 - 1)
53
+ expect(seats['Partido Democrático Republicano']).to eq(0)
54
+ expect(seats['Partido Comunista dos Trabalhadores Portugueses']).to eq(0)
55
+ expect(seats['LIVRE']).to eq(0)
56
+ expect(seats['Partido Nacional Renovador']).to eq(0)
57
+ expect(seats['Partido da Terra']).to eq(0)
58
+ expect(seats['AGIR']).to eq(0)
59
+ expect(seats['Partido Trabalhista Português']).to eq(0)
60
+ expect(seats['Nós, Cidadãos!']).to eq(0)
61
+ expect(seats['Partido Popular Monárquico']).to eq(0)
62
+ expect(seats['Juntos pelo Povo']).to eq(0)
63
+ expect(seats['Partido Unido dos Reformados e Pensionistas']).to eq(0)
64
+ expect(seats['Aliança Açores']).to eq(0)
65
+ expect(seats['Partido Cidadania e Democracia Cristã']).to eq(0)
66
+ end
67
+ end
@@ -0,0 +1,68 @@
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
+ require 'spec_helper'
20
+
21
+ describe Sapor::PortugalWithChAndIlWithoutPaf, '#area_code' do
22
+ it 'returns PT∪{CH,IL}\{PàF} as the area code' do
23
+ expect(Sapor::PortugalWithChAndIlWithoutPaf.instance.area_code).to \
24
+ eq('PT∪{CH,IL}\{PàF}')
25
+ end
26
+ end
27
+
28
+ describe Sapor::PortugalWithChAndIlWithoutPaf, '#no_of_seats' do
29
+ it 'returns 230 as the number of seats' do
30
+ expect(Sapor::PortugalWithChAndIlWithoutPaf.instance.no_of_seats).to eq(230)
31
+ end
32
+ end
33
+
34
+ describe Sapor::PortugalWithChAndIlWithoutPaf, '#population_size' do
35
+ it 'returns a population size of 5,206,113' do
36
+ expect(Sapor::PortugalWithChAndIlWithoutPaf.instance.population_size).to eq(5_206_113)
37
+ end
38
+ end
39
+
40
+ describe Sapor::PortugalWithChAndIlWithoutPaf, '#seats' do
41
+ it 'calculates the number of seats for the election of 2015 adjusted for' \
42
+ ' the seat distribution for 2019' do
43
+ PortugalWithChAndIlWithoutPaf = Sapor::PortugalWithChAndIlWithoutPaf.instance
44
+ results = PortugalWithChAndIlWithoutPaf.overall_election_results_of_2015
45
+ seats = Sapor::PortugalWithChAndIlWithoutPaf.instance.seats(results)
46
+ expect(seats['Partido Social Democrata']).to eq(5 + 37)
47
+ expect(seats['Chega']).to eq(0 + 37)
48
+ expect(seats['Iniciativa Liberal']).to eq(0 + 80)
49
+ expect(seats['CDS–Partido Popular']).to eq(0 + 30)
50
+ expect(seats['Partido Socialista']).to eq(86 - 54)
51
+ expect(seats['Bloco de Esquerda']).to eq(19 - 15)
52
+ expect(seats['Coligação Democrática Unitária']).to eq(17 - 12)
53
+ expect(seats['Pessoas–Animais–Natureza']).to eq(1 - 1)
54
+ expect(seats['Partido Democrático Republicano']).to eq(0)
55
+ expect(seats['Partido Comunista dos Trabalhadores Portugueses']).to eq(0)
56
+ expect(seats['LIVRE']).to eq(0)
57
+ expect(seats['Partido Nacional Renovador']).to eq(0)
58
+ expect(seats['Partido da Terra']).to eq(0)
59
+ expect(seats['AGIR']).to eq(0)
60
+ expect(seats['Partido Trabalhista Português']).to eq(0)
61
+ expect(seats['Nós, Cidadãos!']).to eq(0)
62
+ expect(seats['Partido Popular Monárquico']).to eq(0)
63
+ expect(seats['Juntos pelo Povo']).to eq(0)
64
+ expect(seats['Partido Unido dos Reformados e Pensionistas']).to eq(0)
65
+ expect(seats['Aliança Açores']).to eq(0)
66
+ expect(seats['Partido Cidadania e Democracia Cristã']).to eq(0)
67
+ end
68
+ end
@@ -0,0 +1,66 @@
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
+ require 'spec_helper'
20
+
21
+ describe Sapor::PortugalWithoutPaf, '#area_code' do
22
+ it 'returns PT\{PàF} as the area code' do
23
+ expect(Sapor::PortugalWithoutPaf.instance.area_code).to \
24
+ eq('PT\{PàF}')
25
+ end
26
+ end
27
+
28
+ describe Sapor::PortugalWithoutPaf, '#no_of_seats' do
29
+ it 'returns 230 as the number of seats' do
30
+ expect(Sapor::PortugalWithoutPaf.instance.no_of_seats).to eq(230)
31
+ end
32
+ end
33
+
34
+ describe Sapor::PortugalWithoutPaf, '#population_size' do
35
+ it 'returns a population size of 5,206,113' do
36
+ expect(Sapor::PortugalWithoutPaf.instance.population_size).to eq(5_206_113)
37
+ end
38
+ end
39
+
40
+ describe Sapor::PortugalWithoutPaf, '#seats' do
41
+ it 'calculates the number of seats for the election of 2015 adjusted for' \
42
+ ' the seat distribution for 2019' do
43
+ PortugalWithoutPaf = Sapor::PortugalWithoutPaf.instance
44
+ results = PortugalWithoutPaf.overall_election_results_of_2015
45
+ seats = Sapor::PortugalWithoutPaf.instance.seats(results)
46
+ expect(seats['Partido Social Democrata']).to eq(5 + 73)
47
+ expect(seats['CDS–Partido Popular']).to eq(0 + 67)
48
+ expect(seats['Partido Socialista']).to eq(86 - 23)
49
+ expect(seats['Bloco de Esquerda']).to eq(19 - 6)
50
+ expect(seats['Coligação Democrática Unitária']).to eq(17 - 8)
51
+ expect(seats['Pessoas–Animais–Natureza']).to eq(1 - 1)
52
+ expect(seats['Partido Democrático Republicano']).to eq(0)
53
+ expect(seats['Partido Comunista dos Trabalhadores Portugueses']).to eq(0)
54
+ expect(seats['LIVRE']).to eq(0)
55
+ expect(seats['Partido Nacional Renovador']).to eq(0)
56
+ expect(seats['Partido da Terra']).to eq(0)
57
+ expect(seats['AGIR']).to eq(0)
58
+ expect(seats['Partido Trabalhista Português']).to eq(0)
59
+ expect(seats['Nós, Cidadãos!']).to eq(0)
60
+ expect(seats['Partido Popular Monárquico']).to eq(0)
61
+ expect(seats['Juntos pelo Povo']).to eq(0)
62
+ expect(seats['Partido Unido dos Reformados e Pensionistas']).to eq(0)
63
+ expect(seats['Aliança Açores']).to eq(0)
64
+ expect(seats['Partido Cidadania e Democracia Cristã']).to eq(0)
65
+ end
66
+ end
@@ -0,0 +1,82 @@
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 'spec_helper'
21
+
22
+ describe Sapor::PseudoRandomMultiRangeEnumerator, '#each' do
23
+ it 'sets the size correctly for one dimension' do
24
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([5]).each
25
+ expect(enum.size).to eq(5)
26
+ end
27
+
28
+ it 'sets the size correctly for two dimensions' do
29
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([5, 3]).each
30
+ expect(enum.size).to eq(15)
31
+ end
32
+
33
+ it 'returns a simple enumeration for one dimension' do
34
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([5]).each
35
+ expect(enum.to_a).to eq([[0], [1], [2], [3], [4]])
36
+ end
37
+
38
+ it 'returns [0] as the first element for one dimension' do
39
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([5]).each
40
+ expect(enum.next).to eq([0])
41
+ end
42
+
43
+ it 'returns [0, 0] as the first element for two dimensions' do
44
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([5, 3]).each
45
+ expect(enum.next).to eq([0, 0])
46
+ end
47
+
48
+ it 'returns [1] as the second element for one dimension' do
49
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([5]).each
50
+ enum.next
51
+ expect(enum.next).to eq([1])
52
+ end
53
+
54
+ it 'returns [1, 2] as the second element of a 3×5 hyper-rectangle' do
55
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([3, 5]).each
56
+ enum.next
57
+ expect(enum.next).to eq([1, 2])
58
+ end
59
+
60
+ it 'returns [2, 1] as the second element of a 5×3 hyper-rectangle' do
61
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([5, 3]).each
62
+ enum.next
63
+ expect(enum.next).to eq([2, 1])
64
+ end
65
+
66
+ it 'returns [1, 3] as the second element of a 2×5 hyper-rectangle' do
67
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([2, 5]).each
68
+ enum.next
69
+ expect(enum.next).to eq([1, 3])
70
+ end
71
+
72
+ it 'returns [1, 2, 5] as the second element of a 9×9×9 hyper-rectangle' do
73
+ enum = Sapor::PseudoRandomMultiRangeEnumerator.new([9, 9, 9]).each
74
+ enum.next
75
+ expect(enum.next).to eq([1, 2, 5])
76
+ end
77
+
78
+ it "raises an ArgumentError when it can't construct incrementers" do
79
+ expect { Sapor::PseudoRandomMultiRangeEnumerator.new([3, 2]) }.to \
80
+ raise_error(ArgumentError, 'Could not construct suitable incrementers.')
81
+ end
82
+ end
@@ -0,0 +1,289 @@
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 'spec_helper'
21
+
22
+ class ReferendumTestArea
23
+ include Singleton
24
+
25
+ def area_code
26
+ 'RA'
27
+ end
28
+
29
+ def population_size
30
+ 1_000
31
+ end
32
+ end
33
+
34
+ REFERENDUM_TEST_AREA = ReferendumTestArea.instance
35
+
36
+ def referendum_pentachotomy(max_error = 0.01)
37
+ results = { 'Red' => 1, 'Green' => 2, 'Blue' => 3, 'Yellow' => 6,
38
+ 'Other' => 1 }
39
+ dichotomies = Sapor::Dichotomies.new(results, 1000)
40
+ dichotomies.refine
41
+ dichotomies.refine
42
+ dichotomies.refine
43
+ Sapor::ReferendumPolychotomy.new(results, REFERENDUM_TEST_AREA, dichotomies, max_error)
44
+ end
45
+
46
+ describe Sapor::ReferendumPolychotomy, '#new' do
47
+ it 'extracts the 99.99% confidence intervals as ranges for max 1% error' do
48
+ expect(referendum_pentachotomy.range('Red').size).to eq(17)
49
+ expect(referendum_pentachotomy.range('Green').size).to eq(19)
50
+ expect(referendum_pentachotomy.range('Blue').size).to eq(20)
51
+ expect(referendum_pentachotomy.range('Yellow').size).to eq(22)
52
+ end
53
+
54
+ it 'extracts the 99.9999% confidence intervals as ranges for max 0.1%' \
55
+ ' error' do
56
+ expect(referendum_pentachotomy(0.001).range('Red').size).to eq(20)
57
+ expect(referendum_pentachotomy(0.001).range('Green').size).to eq(22)
58
+ expect(referendum_pentachotomy(0.001).range('Blue').size).to eq(23)
59
+ expect(referendum_pentachotomy(0.001).range('Yellow').size).to eq(25)
60
+ end
61
+
62
+ # TODO: A test case where the construction of the enumerator fails
63
+ end
64
+
65
+ describe Sapor::ReferendumPolychotomy, '#error_estimate' do
66
+ it 'is 1 by default (no refinement)' do
67
+ polychotomy = referendum_pentachotomy
68
+ expect(polychotomy.error_estimate).to eq(1.0)
69
+ end
70
+
71
+ it 'is 1 after the first refinement' do
72
+ polychotomy = referendum_pentachotomy
73
+ polychotomy.refine
74
+ expect(polychotomy.error_estimate).to eq(1.0)
75
+ end
76
+
77
+ it 'is at least the difference between the most probable fractions (two' \
78
+ ' refinements)' do
79
+ polychotomy = referendum_pentachotomy
80
+ polychotomy.refine
81
+ polychotomy.refine
82
+ expect(polychotomy.error_estimate.round(3)).to eq(0.333)
83
+ end
84
+
85
+ it 'is at least the difference between the most probable fractions (three' \
86
+ ' refinements)' do
87
+ polychotomy = referendum_pentachotomy
88
+ polychotomy.refine
89
+ polychotomy.refine
90
+ polychotomy.refine
91
+ expect(polychotomy.error_estimate.round(3)).to eq(0.259)
92
+ end
93
+ end
94
+
95
+ describe Sapor::ReferendumPolychotomy, '#most_probable_fraction' do
96
+ it 'is nil by default (no refinement)' do
97
+ polychotomy = referendum_pentachotomy
98
+ expect(polychotomy.most_probable_fraction('Red')).to be_nil
99
+ expect(polychotomy.most_probable_fraction('Green')).to be_nil
100
+ expect(polychotomy.most_probable_fraction('Blue')).to be_nil
101
+ expect(polychotomy.most_probable_fraction('Yellow')).to be_nil
102
+ end
103
+
104
+ it 'returns a value after the first refinement' do
105
+ polychotomy = referendum_pentachotomy
106
+ polychotomy.refine
107
+ expect(polychotomy.most_probable_fraction('Red').round(2)).to eq(0.02)
108
+ expect(polychotomy.most_probable_fraction('Green').round(2)).to eq(0.02)
109
+ expect(polychotomy.most_probable_fraction('Blue').round(2)).to eq(0.02)
110
+ expect(polychotomy.most_probable_fraction('Yellow').round(2)).to eq(0.09)
111
+ end
112
+
113
+ it 'returns an updated value after the second refinement' do
114
+ polychotomy = referendum_pentachotomy
115
+ polychotomy.refine
116
+ polychotomy.refine
117
+ expect(polychotomy.most_probable_fraction('Red').round(2)).to eq(0.28)
118
+ expect(polychotomy.most_probable_fraction('Green').round(2)).to eq(0.09)
119
+ expect(polychotomy.most_probable_fraction('Blue').round(2)).to eq(0.35)
120
+ expect(polychotomy.most_probable_fraction('Yellow').round(2)).to eq(0.20)
121
+ end
122
+
123
+ it 'returns an updated value after the third refinement' do
124
+ polychotomy = referendum_pentachotomy
125
+ polychotomy.refine
126
+ polychotomy.refine
127
+ polychotomy.refine
128
+ expect(polychotomy.most_probable_fraction('Red').round(2)).to eq(0.35)
129
+ expect(polychotomy.most_probable_fraction('Green').round(2)).to eq(0.09)
130
+ expect(polychotomy.most_probable_fraction('Blue').round(2)).to eq(0.09)
131
+ expect(polychotomy.most_probable_fraction('Yellow').round(2)).to eq(0.39)
132
+ end
133
+ end
134
+
135
+ describe Sapor::ReferendumPolychotomy, '#no_of_data_points' do
136
+ it 'is 0 by default (no refinement)' do
137
+ polychotomy = referendum_pentachotomy
138
+ expect(polychotomy.no_of_data_points).to eq(0)
139
+ end
140
+
141
+ it 'is 1 after 1 refinement' do
142
+ polychotomy = referendum_pentachotomy
143
+ polychotomy.refine
144
+ expect(polychotomy.no_of_data_points).to eq(1)
145
+ end
146
+
147
+ it 'is 19 after 2 refinements' do
148
+ polychotomy = referendum_pentachotomy
149
+ polychotomy.refine
150
+ polychotomy.refine
151
+ expect(polychotomy.no_of_data_points).to eq(8)
152
+ end
153
+
154
+ it 'is 38 after 3 refinements' do
155
+ polychotomy = referendum_pentachotomy
156
+ polychotomy.refine
157
+ polychotomy.refine
158
+ polychotomy.refine
159
+ expect(polychotomy.no_of_data_points).to eq(27)
160
+ end
161
+
162
+ # TODO: A test case that runs all the simulations
163
+ end
164
+
165
+ describe Sapor::ReferendumPolychotomy, '#no_of_simulations' do
166
+ it 'is 0 by default (no refinement)' do
167
+ polychotomy = referendum_pentachotomy
168
+ expect(polychotomy.no_of_simulations).to eq(0)
169
+ end
170
+
171
+ it 'is 1 after 1 refinement' do
172
+ polychotomy = referendum_pentachotomy
173
+ polychotomy.refine
174
+ expect(polychotomy.no_of_simulations).to eq(1)
175
+ end
176
+
177
+ it 'is 2 after 2 refinements' do
178
+ polychotomy = referendum_pentachotomy
179
+ polychotomy.refine
180
+ polychotomy.refine
181
+ expect(polychotomy.no_of_simulations).to eq(2)
182
+ end
183
+
184
+ it 'is 4 after 3 refinements' do
185
+ polychotomy = referendum_pentachotomy
186
+ polychotomy.refine
187
+ polychotomy.refine
188
+ polychotomy.refine
189
+ expect(polychotomy.no_of_simulations).to eq(4)
190
+ end
191
+
192
+ # TODO: A test case that runs until the end of the enumerator
193
+ end
194
+
195
+ describe Sapor::ReferendumPolychotomy, '#progress_report' do
196
+ it 'reports progress with the number of simulations and data points,' \
197
+ ' together with the search space size and the fraction that already has' \
198
+ ' been searched' do
199
+ expected_report = '1 simulations out of 1 data points, 1 / 142,120 of' \
200
+ ' search space size (142,120).'
201
+ polychotomy = referendum_pentachotomy
202
+ polychotomy.refine
203
+ expect(polychotomy.progress_report).to eq(expected_report)
204
+ end
205
+
206
+ it 'reports no of simulations and data points with thousands separator' do
207
+ expected_report = '1,024 simulations out of 9,287 data points, 1 / 15' \
208
+ ' of search space size (142,120).'
209
+ polychotomy = referendum_pentachotomy
210
+ 11.times { polychotomy.refine }
211
+ expect(polychotomy.progress_report).to eq(expected_report)
212
+ end
213
+
214
+ it 'rounds the fraction of the space size searched with 1 decimal if' \
215
+ ' larger than one tenth' do
216
+ expected_report = '2,048 simulations out of 19,433 data points, 1 / 7.3' \
217
+ ' of search space size (142,120).'
218
+ polychotomy = referendum_pentachotomy
219
+ 12.times { polychotomy.refine }
220
+ expect(polychotomy.progress_report).to eq(expected_report)
221
+ end
222
+ end
223
+
224
+ describe Sapor::ReferendumPolychotomy, '#report' do
225
+ it 'produces a report after first refinement for short choice labels' do
226
+ expected_report = 'Most probable rounded fractions, fractions and 95%' \
227
+ " confidence intervals:\n" \
228
+ "Choice Result MPRF MPF CI(95%) P(>↓) P(R↑) P(R) P(R↓)\n" \
229
+ "Yellow 46.2% 5.6% 9.3% 9.3%– 11.1% 100.0% 0.0% 100.0%\n" + # TODO: Shouldn't MPRF be 5.6% and CI 9.3%–9.3%?
230
+ "Blue 23.1% 1.9% 1.9% 1.9%– 3.7% 0.0% 0.0% 0.0% 0.0%\n" + # TODO: Shouldn't CI be 1.9%–1.9%?
231
+ "Green 15.4% 1.9% 1.9% 1.9%– 3.7% 0.0% 0.0% 0.0% 100.0%\n" + # TODO: Shouldn't CI be 1.9%–1.9%?
232
+ "Red 7.7% 1.9% 1.9% 1.9%– 3.7% 100.0% 0.0%" # TODO: Shouldn't CI be 1.9%–1.9%?
233
+ polychotomy = referendum_pentachotomy
234
+ polychotomy.refine
235
+ expect(polychotomy.report).to eq(expected_report)
236
+ end
237
+
238
+ it 'produces a report by default for long choice labels' do
239
+ results = { 'Dark Red' => 1, 'Light Green' => 2, 'Medium Blue' => 3,
240
+ 'Other' => 1 }
241
+ dichotomies = Sapor::Dichotomies.new(results, 1000)
242
+ dichotomies.refine
243
+ dichotomies.refine
244
+ dichotomies.refine
245
+ polychotomy = Sapor::ReferendumPolychotomy.new(results, REFERENDUM_TEST_AREA, dichotomies, 0.01)
246
+ polychotomy.refine
247
+ expected_report = 'Most probable rounded fractions, fractions and 95%' \
248
+ " confidence intervals:\n" \
249
+ "Choice Result MPRF MPF CI(95%) P(>↓) P(R↑) P(R) P(R↓)\n" \
250
+ "Medium Blue 42.9% 1.9% 1.9% 1.9%– 3.7% 0.0% 0.0% 0.0%\n" + # TODO: Shouldn't CI be 1.9%–1.9%?
251
+ "Light Green 28.6% 1.9% 1.9% 1.9%– 3.7% 0.0% 0.0% 0.0% 100.0%\n" + # TODO: Shouldn't CI be 1.9%–1.9%?
252
+ "Dark Red 14.3% 1.9% 1.9% 1.9%– 3.7% 100.0% 0.0%" # TODO: Shouldn't CI be 1.9%–1.9%?
253
+ expect(polychotomy.report).to eq(expected_report)
254
+ end
255
+
256
+ it 'produces a report by default for short choice labels' do
257
+ results = { 'Red' => 1, 'Green' => 2, 'Blue' => 3, 'Other' => 1 }
258
+ dichotomies = Sapor::Dichotomies.new(results, 1000)
259
+ dichotomies.refine
260
+ dichotomies.refine
261
+ dichotomies.refine
262
+ polychotomy = Sapor::ReferendumPolychotomy.new(results, REFERENDUM_TEST_AREA, dichotomies, 0.01)
263
+ polychotomy.refine
264
+ expected_report = 'Most probable rounded fractions, fractions and 95%' \
265
+ " confidence intervals:\n" \
266
+ "Choice Result MPRF MPF CI(95%) P(>↓) P(R↑) P(R) P(R↓)\n" \
267
+ "Blue 42.9% 1.9% 1.9% 1.9%– 3.7% 0.0% 0.0% 0.0%\n" + # TODO: Shouldn't CI be 1.9%–1.9%?
268
+ "Green 28.6% 1.9% 1.9% 1.9%– 3.7% 0.0% 0.0% 0.0% 100.0%\n" + # TODO: Shouldn't CI be 1.9%–1.9%?
269
+ "Red 14.3% 1.9% 1.9% 1.9%– 3.7% 100.0% 0.0%" # TODO: Shouldn't CI be 1.9%–1.9%?
270
+ expect(polychotomy.report).to eq(expected_report)
271
+ end
272
+
273
+ it 'sorts line with the same results alphabetically' do
274
+ results = { 'Red' => 1, 'Green' => 1, 'Blue' => 1, 'Other' => 1 }
275
+ dichotomies = Sapor::Dichotomies.new(results, 1000)
276
+ dichotomies.refine
277
+ dichotomies.refine
278
+ dichotomies.refine
279
+ polychotomy = Sapor::ReferendumPolychotomy.new(results, REFERENDUM_TEST_AREA, dichotomies, 0.01)
280
+ polychotomy.refine
281
+ expected_report = 'Most probable rounded fractions, fractions and 95%' \
282
+ " confidence intervals:\n" \
283
+ "Choice Result MPRF MPF CI(95%) P(>↓) P(R↑) P(R) P(R↓)\n" \
284
+ "Blue 25.0% 1.9% 1.9% 1.9%– 3.7% 0.0% 0.0% 0.0%\n" + # TODO: Shouldn't CI be 1.9%–1.9%?
285
+ "Green 25.0% 1.9% 1.9% 1.9%– 3.7% 0.0% 0.0% 0.0% 100.0%\n" + # TODO: Shouldn't CI be 1.9%–1.9%?
286
+ "Red 25.0% 1.9% 1.9% 1.9%– 3.7% 100.0% 0.0%" # TODO: Shouldn't CI be 1.9%–1.9%?
287
+ expect(polychotomy.report).to eq(expected_report)
288
+ end
289
+ end