sapor 0.3.3
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 +7 -0
- data/Area Class Diagram.dia +0 -0
- data/Area Class Diagram.png +0 -0
- data/Class Diagram.dia +0 -0
- data/Class Diagram.png +0 -0
- data/Example-Catalonia.md +361 -0
- data/Example-Flanders.md +486 -0
- data/Example-Greece.md +25 -0
- data/Example-Oslo.md +678 -0
- data/Example-UnitedKingdom-Referendum.md +132 -0
- data/Examples.md +15 -0
- data/LICENSE +674 -0
- data/README.md +103 -0
- data/Rakefile +18 -0
- data/Technical Documentation.md +14 -0
- data/bin/create_installation_package.sh +49 -0
- data/bin/install.sh +45 -0
- data/bin/sapor.rb +24 -0
- data/bin/sapor.sh +106 -0
- data/data/hu/hungary-2014.txt +1680 -0
- data/data/hu/hungary_2014_screen_scraper.rb +48 -0
- data/data/hu/hungary_2014_to_psv.rb +80 -0
- data/data/hu/index-2014.txt +106 -0
- data/data/pl/2015-gl-lis-okr.csv +42 -0
- data/data/pl/poland_2015_to_psv.rb +79 -0
- data/data/pl/poland_2015_to_psv_with_ko_and_rsw.rb +94 -0
- data/data/pl/poland_2015_to_psv_with_ko_konf_kp_l_and_zp.rb +100 -0
- data/data/pl/poland_2015_to_psv_with_ko_sld_and_wi.rb +92 -0
- data/data/pl/poland_2015_to_psv_with_sld.rb +84 -0
- data/data/pl/poland_2015_to_psv_with_sld_and_wi.rb +85 -0
- data/data/uk/inject_ukip_2015_as_brexit_2019_in_2017.rb +54 -0
- data/data/uk/united_kingdom_2015.txt +651 -0
- data/data/uk/united_kingdom_2015_to_psv.rb +104 -0
- data/data/uk/united_kingdom_2017.txt +651 -0
- data/data/uk/united_kingdom_2017_to_psv.rb +104 -0
- data/data/uk/united_kingdom_2017_to_psv_with_brexit_and_chuk.rb +113 -0
- data/data/uk/united_kingdom_2017_to_psv_with_tig.rb +111 -0
- data/lib/sapor.rb +150 -0
- data/lib/sapor/binomials_cache.rb +45 -0
- data/lib/sapor/combinations_distribution.rb +222 -0
- data/lib/sapor/denominators.rb +67 -0
- data/lib/sapor/dichotomies.rb +138 -0
- data/lib/sapor/dichotomy.rb +164 -0
- data/lib/sapor/first_past_the_post.rb +82 -0
- data/lib/sapor/largest_remainder.rb +118 -0
- data/lib/sapor/log4r_logger.rb +49 -0
- data/lib/sapor/log_facade.rb +40 -0
- data/lib/sapor/multi_district_leveled_proportional.rb +64 -0
- data/lib/sapor/multi_district_proportional.rb +123 -0
- data/lib/sapor/multi_district_variable_threshold_proportional.rb +128 -0
- data/lib/sapor/number_formatter.rb +45 -0
- data/lib/sapor/options.rb +73 -0
- data/lib/sapor/poll.rb +286 -0
- data/lib/sapor/polychotomy.rb +200 -0
- data/lib/sapor/pseudorandom_multirange_enumerator.rb +87 -0
- data/lib/sapor/referendum_polychotomy.rb +165 -0
- data/lib/sapor/regional_data/area.rb +82 -0
- data/lib/sapor/regional_data/austria.rb +84 -0
- data/lib/sapor/regional_data/belgium-brussels-2014.psv +46 -0
- data/lib/sapor/regional_data/belgium-brussels-20190526.psv +33 -0
- data/lib/sapor/regional_data/belgium-flanders-2014.psv +80 -0
- data/lib/sapor/regional_data/belgium-flanders-20190526.psv +74 -0
- data/lib/sapor/regional_data/belgium-wallonia-2014.psv +114 -0
- data/lib/sapor/regional_data/belgium-wallonia-20190526.psv +93 -0
- data/lib/sapor/regional_data/belgium.rb +97 -0
- data/lib/sapor/regional_data/belgium_brussels.rb +62 -0
- data/lib/sapor/regional_data/belgium_flanders.rb +64 -0
- data/lib/sapor/regional_data/belgium_wallonia.rb +63 -0
- data/lib/sapor/regional_data/catalonia-2012-2015.psv +100 -0
- data/lib/sapor/regional_data/catalonia-2012.psv +87 -0
- data/lib/sapor/regional_data/catalonia-2015-jxcat.psv +68 -0
- data/lib/sapor/regional_data/catalonia-2015-no-jxsi.psv +68 -0
- data/lib/sapor/regional_data/catalonia-2015.psv +63 -0
- data/lib/sapor/regional_data/catalonia-jxcat.rb +109 -0
- data/lib/sapor/regional_data/catalonia-no-jxsi.rb +96 -0
- data/lib/sapor/regional_data/catalonia.rb +96 -0
- data/lib/sapor/regional_data/denmark-20150618-with-e-and-p.psv +164 -0
- data/lib/sapor/regional_data/denmark-20150618-with-e.psv +153 -0
- data/lib/sapor/regional_data/denmark-20150618-with-p.psv +153 -0
- data/lib/sapor/regional_data/denmark-20150618.psv +142 -0
- data/lib/sapor/regional_data/denmark.rb +128 -0
- data/lib/sapor/regional_data/denmark_with_e.rb +128 -0
- data/lib/sapor/regional_data/denmark_with_e_and_p.rb +128 -0
- data/lib/sapor/regional_data/denmark_with_p.rb +128 -0
- data/lib/sapor/regional_data/estonia.rb +88 -0
- data/lib/sapor/regional_data/european-union-great-britain-20140522-brexit-chuk.psv +172 -0
- data/lib/sapor/regional_data/european-union-great-britain-20140522.psv +146 -0
- data/lib/sapor/regional_data/european-union-great-britain-20190523.psv +141 -0
- data/lib/sapor/regional_data/european-union-ireland-2014-ia-ri-sd.psv +64 -0
- data/lib/sapor/regional_data/european-union-ireland-2014-ia-sd.psv +60 -0
- data/lib/sapor/regional_data/european-union-ireland-2014-ia.psv +56 -0
- data/lib/sapor/regional_data/european-union-ireland-2014-sd.psv +56 -0
- data/lib/sapor/regional_data/european-union-ireland-2014.psv +50 -0
- data/lib/sapor/regional_data/european-union-ireland-20190524-ia.psv +58 -0
- data/lib/sapor/regional_data/european-union-ireland-20190524.psv +52 -0
- data/lib/sapor/regional_data/european_union_27_austria.rb +76 -0
- data/lib/sapor/regional_data/european_union_27_croatia.rb +81 -0
- data/lib/sapor/regional_data/european_union_27_denmark.rb +77 -0
- data/lib/sapor/regional_data/european_union_27_estonia.rb +74 -0
- data/lib/sapor/regional_data/european_union_27_finland.rb +74 -0
- data/lib/sapor/regional_data/european_union_27_ireland.rb +96 -0
- data/lib/sapor/regional_data/european_union_27_ireland_with_ia.rb +97 -0
- data/lib/sapor/regional_data/european_union_27_italy.rb +84 -0
- data/lib/sapor/regional_data/european_union_27_netherlands.rb +81 -0
- data/lib/sapor/regional_data/european_union_27_poland.rb +84 -0
- data/lib/sapor/regional_data/european_union_27_romania.rb +78 -0
- data/lib/sapor/regional_data/european_union_27_slovakia.rb +80 -0
- data/lib/sapor/regional_data/european_union_27_spain.rb +82 -0
- data/lib/sapor/regional_data/european_union_27_sweden.rb +76 -0
- data/lib/sapor/regional_data/european_union_austria.rb +76 -0
- data/lib/sapor/regional_data/european_union_bulgaria.rb +81 -0
- data/lib/sapor/regional_data/european_union_croatia.rb +81 -0
- data/lib/sapor/regional_data/european_union_cyprus.rb +72 -0
- data/lib/sapor/regional_data/european_union_czech_republic.rb +82 -0
- data/lib/sapor/regional_data/european_union_denmark.rb +77 -0
- data/lib/sapor/regional_data/european_union_estonia.rb +74 -0
- data/lib/sapor/regional_data/european_union_finland.rb +74 -0
- data/lib/sapor/regional_data/european_union_flanders.rb +74 -0
- data/lib/sapor/regional_data/european_union_france.rb +84 -0
- data/lib/sapor/regional_data/european_union_france_2019.rb +84 -0
- data/lib/sapor/regional_data/european_union_french_community_of_belgium.rb +73 -0
- data/lib/sapor/regional_data/european_union_germany.rb +86 -0
- data/lib/sapor/regional_data/european_union_great_britain.rb +98 -0
- data/lib/sapor/regional_data/european_union_greece.rb +77 -0
- data/lib/sapor/regional_data/european_union_hungary.rb +76 -0
- data/lib/sapor/regional_data/european_union_ireland.rb +96 -0
- data/lib/sapor/regional_data/european_union_ireland_with_ia.rb +97 -0
- data/lib/sapor/regional_data/european_union_italy.rb +84 -0
- data/lib/sapor/regional_data/european_union_latvia.rb +81 -0
- data/lib/sapor/regional_data/european_union_lithuania.rb +80 -0
- data/lib/sapor/regional_data/european_union_luxembourg.rb +75 -0
- data/lib/sapor/regional_data/european_union_malta.rb +71 -0
- data/lib/sapor/regional_data/european_union_netherlands.rb +81 -0
- data/lib/sapor/regional_data/european_union_northern_ireland.rb +75 -0
- data/lib/sapor/regional_data/european_union_poland.rb +84 -0
- data/lib/sapor/regional_data/european_union_portugal.rb +75 -0
- data/lib/sapor/regional_data/european_union_romania.rb +78 -0
- data/lib/sapor/regional_data/european_union_slovakia.rb +81 -0
- data/lib/sapor/regional_data/european_union_slovenia.rb +85 -0
- data/lib/sapor/regional_data/european_union_spain.rb +82 -0
- data/lib/sapor/regional_data/european_union_sweden.rb +76 -0
- data/lib/sapor/regional_data/finland-20150419-with-sin.psv +224 -0
- data/lib/sapor/regional_data/finland-20150419.psv +212 -0
- data/lib/sapor/regional_data/finland.rb +107 -0
- data/lib/sapor/regional_data/finland_with_sin.rb +107 -0
- data/lib/sapor/regional_data/flanders-2014.psv +96 -0
- data/lib/sapor/regional_data/flanders-20190526.psv +87 -0
- data/lib/sapor/regional_data/flanders.rb +115 -0
- data/lib/sapor/regional_data/france.rb +38 -0
- data/lib/sapor/regional_data/greece.rb +92 -0
- data/lib/sapor/regional_data/hungary-2014.psv +2104 -0
- data/lib/sapor/regional_data/hungary.rb +116 -0
- data/lib/sapor/regional_data/iceland-20161029-midflokkurinn.psv +94 -0
- data/lib/sapor/regional_data/iceland-20161029.psv +88 -0
- data/lib/sapor/regional_data/iceland-20171028.psv +85 -0
- data/lib/sapor/regional_data/iceland.rb +133 -0
- data/lib/sapor/regional_data/latvia-20141004-kpv-p-par.psv +109 -0
- data/lib/sapor/regional_data/latvia-20141004-kpv-par.psv +103 -0
- data/lib/sapor/regional_data/latvia-20141004-kpv.psv +97 -0
- data/lib/sapor/regional_data/latvia-20141004.psv +89 -0
- data/lib/sapor/regional_data/latvia.rb +112 -0
- data/lib/sapor/regional_data/latvia_kpv.rb +112 -0
- data/lib/sapor/regional_data/latvia_kpv_p_par.rb +112 -0
- data/lib/sapor/regional_data/latvia_kpv_par.rb +112 -0
- data/lib/sapor/regional_data/luxembourg-20131020.psv +76 -0
- data/lib/sapor/regional_data/luxembourg.rb +82 -0
- data/lib/sapor/regional_data/netherlands.rb +108 -0
- data/lib/sapor/regional_data/norway.rb +425 -0
- data/lib/sapor/regional_data/norwegian_municipality.rb +68 -0
- data/lib/sapor/regional_data/poland-20151025-with-ko-and-l-without-n-po-r-and-zl.psv +321 -0
- 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
- data/lib/sapor/regional_data/poland-20151025-with-ko-sld-and-wi-without-n-po-and-zl.psv +403 -0
- data/lib/sapor/regional_data/poland-20151025-with-sld-and-wi-without-zl.psv +444 -0
- data/lib/sapor/regional_data/poland-20151025-with-sld-without-zl.psv +403 -0
- data/lib/sapor/regional_data/poland-20151025.psv +403 -0
- data/lib/sapor/regional_data/poland.rb +125 -0
- data/lib/sapor/regional_data/poland_with_ko_and_l_without_n_po_r_and_zl.rb +122 -0
- 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
- data/lib/sapor/regional_data/poland_with_ko_sld_and_wi_without_n_po_and_zl.rb +125 -0
- data/lib/sapor/regional_data/poland_with_sld_and_wi_without_zl.rb +126 -0
- data/lib/sapor/regional_data/poland_with_sld_without_zl.rb +126 -0
- data/lib/sapor/regional_data/portugal-20151004-with-a-and-ch-without-paf.psv +438 -0
- data/lib/sapor/regional_data/portugal-20151004-with-a-and-il-without-paf.psv +438 -0
- data/lib/sapor/regional_data/portugal-20151004-with-a-ch-and-il-without-paf.psv +461 -0
- data/lib/sapor/regional_data/portugal-20151004-with-a-without-paf.psv +415 -0
- data/lib/sapor/regional_data/portugal-20151004-with-ch-and-il-without-paf.psv +438 -0
- data/lib/sapor/regional_data/portugal-20151004-without-paf.psv +392 -0
- data/lib/sapor/regional_data/portugal-20151004.psv +370 -0
- data/lib/sapor/regional_data/portugal.rb +101 -0
- data/lib/sapor/regional_data/portugal_with_a_and_ch_without_paf.rb +92 -0
- data/lib/sapor/regional_data/portugal_with_a_and_il_without_paf.rb +92 -0
- data/lib/sapor/regional_data/portugal_with_a_ch_and_il_without_paf.rb +92 -0
- data/lib/sapor/regional_data/portugal_with_a_without_paf.rb +92 -0
- data/lib/sapor/regional_data/portugal_with_ch_and_il_without_paf.rb +92 -0
- data/lib/sapor/regional_data/portugal_without_paf.rb +92 -0
- data/lib/sapor/regional_data/slovakia.rb +81 -0
- data/lib/sapor/regional_data/slovenia.rb +114 -0
- data/lib/sapor/regional_data/spain-20160626.psv +619 -0
- data/lib/sapor/regional_data/spain.rb +136 -0
- data/lib/sapor/regional_data/sweden.rb +92 -0
- data/lib/sapor/regional_data/sweden_20140914.rb +89 -0
- data/lib/sapor/regional_data/united_kingdom-2015.psv +4358 -0
- data/lib/sapor/regional_data/united_kingdom-20170608-brexit-chuk.psv +5154 -0
- data/lib/sapor/regional_data/united_kingdom-20170608-brexit.psv +4521 -0
- data/lib/sapor/regional_data/united_kingdom-20170608-tig.psv +4529 -0
- data/lib/sapor/regional_data/united_kingdom-20170608.psv +3894 -0
- data/lib/sapor/regional_data/united_kingdom.rb +94 -0
- data/lib/sapor/regional_data/united_kingdom_with_brexit.rb +110 -0
- data/lib/sapor/regional_data/united_kingdom_with_brexit_and_chuk.rb +111 -0
- data/lib/sapor/regional_data/united_kingdom_with_tig.rb +111 -0
- data/lib/sapor/regional_data/utopia.rb +66 -0
- data/lib/sapor/regional_data/wallonia-2014.psv +101 -0
- data/lib/sapor/regional_data/wallonia-20190526.psv +88 -0
- data/lib/sapor/regional_data/wallonia.rb +112 -0
- data/lib/sapor/representatives_polychotomy.rb +338 -0
- data/lib/sapor/single_district_proportional.rb +75 -0
- data/sapor.gemspec +35 -0
- data/spec/integration/area_spec.rb +28 -0
- data/spec/integration/poll_spec.rb +112 -0
- data/spec/integration/sample.poll +8 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/unit/area_spec.rb +115 -0
- data/spec/unit/austria_spec.rb +76 -0
- data/spec/unit/belgium_brussels_spec.rb +58 -0
- data/spec/unit/belgium_flanders_spec.rb +62 -0
- data/spec/unit/belgium_spec.rb +26 -0
- data/spec/unit/belgium_wallonia_spec.rb +65 -0
- data/spec/unit/binomials_cache_spec.rb +34 -0
- data/spec/unit/catalonia_spec.rb +74 -0
- data/spec/unit/combinations_distribution_spec.rb +241 -0
- data/spec/unit/denmark_spec.rb +56 -0
- data/spec/unit/denmark_with_e_and_p_spec.rb +58 -0
- data/spec/unit/denmark_with_e_spec.rb +57 -0
- data/spec/unit/denmark_with_p_spec.rb +57 -0
- data/spec/unit/denominators_spec.rb +40 -0
- data/spec/unit/dichotomies_spec.rb +154 -0
- data/spec/unit/dichotomy_spec.rb +320 -0
- data/spec/unit/estonia_spec.rb +65 -0
- data/spec/unit/european_union_27_austria_spec.rb +61 -0
- data/spec/unit/european_union_27_croatia_spec.rb +60 -0
- data/spec/unit/european_union_27_denmark_spec.rb +62 -0
- data/spec/unit/european_union_27_estonia_spec.rb +94 -0
- data/spec/unit/european_union_27_finland_spec.rb +75 -0
- data/spec/unit/european_union_27_ireland_spec.rb +72 -0
- data/spec/unit/european_union_27_ireland_with_ia_spec.rb +74 -0
- data/spec/unit/european_union_27_italy_spec.rb +69 -0
- data/spec/unit/european_union_27_netherlands_spec.rb +81 -0
- data/spec/unit/european_union_27_poland_spec.rb +69 -0
- data/spec/unit/european_union_27_romania_spec.rb +67 -0
- data/spec/unit/european_union_27_slovakia_spec.rb +111 -0
- data/spec/unit/european_union_27_spain_spec.rb +130 -0
- data/spec/unit/european_union_27_sweden_spec.rb +89 -0
- data/spec/unit/european_union_austria_spec.rb +61 -0
- data/spec/unit/european_union_bulgaria_spec.rb +97 -0
- data/spec/unit/european_union_croatia_spec.rb +59 -0
- data/spec/unit/european_union_cyprus_spec.rb +65 -0
- data/spec/unit/european_union_czech_republic_spec.rb +125 -0
- data/spec/unit/european_union_denmark_spec.rb +61 -0
- data/spec/unit/european_union_estonia_spec.rb +93 -0
- data/spec/unit/european_union_finland_spec.rb +75 -0
- data/spec/unit/european_union_flanders_spec.rb +56 -0
- data/spec/unit/european_union_france_2019_spec.rb +73 -0
- data/spec/unit/european_union_france_spec.rb +73 -0
- data/spec/unit/european_union_french_community_of_belgium_spec.rb +61 -0
- data/spec/unit/european_union_germany_spec.rb +90 -0
- data/spec/unit/european_union_great_britain_spec.rb +87 -0
- data/spec/unit/european_union_greece_spec.rb +148 -0
- data/spec/unit/european_union_hungary_spec.rb +57 -0
- data/spec/unit/european_union_ireland_spec.rb +72 -0
- data/spec/unit/european_union_ireland_with_ia_spec.rb +74 -0
- data/spec/unit/european_union_italy_spec.rb +69 -0
- data/spec/unit/european_union_latvia_spec.rb +76 -0
- data/spec/unit/european_union_lithuania_spec.rb +68 -0
- data/spec/unit/european_union_luxembourg_spec.rb +63 -0
- data/spec/unit/european_union_malta_spec.rb +60 -0
- data/spec/unit/european_union_netherlands_spec.rb +81 -0
- data/spec/unit/european_union_northern_ireland_spec.rb +66 -0
- data/spec/unit/european_union_poland_spec.rb +69 -0
- data/spec/unit/european_union_portugal_spec.rb +77 -0
- data/spec/unit/european_union_romania_spec.rb +67 -0
- data/spec/unit/european_union_slovakia_spec.rb +111 -0
- data/spec/unit/european_union_slovenia_spec.rb +77 -0
- data/spec/unit/european_union_spain_spec.rb +129 -0
- data/spec/unit/european_union_sweden_spec.rb +89 -0
- data/spec/unit/finland_spec.rb +65 -0
- data/spec/unit/finland_with_sin_spec.rb +67 -0
- data/spec/unit/first_past_the_post_spec.rb +54 -0
- data/spec/unit/flanders_spec.rb +70 -0
- data/spec/unit/france_spec.rb +32 -0
- data/spec/unit/greece_spec.rb +118 -0
- data/spec/unit/hungary_spec.rb +132 -0
- data/spec/unit/iceland_spec.rb +57 -0
- data/spec/unit/largest_remainder_spec.rb +79 -0
- data/spec/unit/latvia_kpv_p_par_spec.rb +38 -0
- data/spec/unit/latvia_kpv_par_spec.rb +38 -0
- data/spec/unit/latvia_kpv_spec.rb +38 -0
- data/spec/unit/latvia_spec.rb +60 -0
- data/spec/unit/luxembourg_spec.rb +54 -0
- data/spec/unit/multi_district_leveled_proportional_spec.rb +49 -0
- data/spec/unit/multi_district_proportional_spec.rb +81 -0
- data/spec/unit/netherlands_spec.rb +107 -0
- data/spec/unit/norway_spec.rb +64 -0
- data/spec/unit/norwegian_municipality_spec.rb +89 -0
- data/spec/unit/number_formatter_spec.rb +173 -0
- data/spec/unit/poland_spec.rb +62 -0
- data/spec/unit/poland_with_ko_and_l_without_n_po_r_and_zl_spec.rb +60 -0
- 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
- data/spec/unit/poland_with_ko_sld_and_wi_without_n_po_and_zl_spec.rb +62 -0
- data/spec/unit/poland_with_sld_and_wi_without_zl_spec.rb +63 -0
- data/spec/unit/poland_with_sld_without_zl_spec.rb +62 -0
- data/spec/unit/poll_spec.rb +110 -0
- data/spec/unit/portugal_spec.rb +66 -0
- data/spec/unit/portugal_with_a_and_ch_without_paf_spec.rb +68 -0
- data/spec/unit/portugal_with_a_and_il_without_paf_spec.rb +68 -0
- data/spec/unit/portugal_with_a_ch_and_il_without_paf_spec.rb +69 -0
- data/spec/unit/portugal_with_a_without_paf_spec.rb +67 -0
- data/spec/unit/portugal_with_ch_and_il_without_paf_spec.rb +68 -0
- data/spec/unit/portugal_without_paf_spec.rb +66 -0
- data/spec/unit/pseudorandom_multirange_enumerator_spec.rb +82 -0
- data/spec/unit/referendum_polychotomy_spec.rb +289 -0
- data/spec/unit/representatives_polychotomy_spec.rb +332 -0
- data/spec/unit/slovakia_spec.rb +99 -0
- data/spec/unit/slovenia_spec.rb +80 -0
- data/spec/unit/spain_spec.rb +101 -0
- data/spec/unit/sweden_20140914_spec.rb +112 -0
- data/spec/unit/sweden_spec.rb +113 -0
- data/spec/unit/united_kingdom_spec.rb +65 -0
- data/spec/unit/united_kingdom_with_brexit_and_chuk_spec.rb +67 -0
- data/spec/unit/united_kingdom_with_brexit_spec.rb +66 -0
- data/spec/unit/united_kingdom_with_tig_spec.rb +66 -0
- data/spec/unit/wallonia_spec.rb +70 -0
- metadata +490 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#
|
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
|
4
|
+
# Copyright (C) 2016 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 dichotomy.
|
|
23
|
+
#
|
|
24
|
+
class Dichotomy
|
|
25
|
+
STANDARD_CONFIDENCE_LEVELS = [0.8, 0.9, 0.95, 0.99]
|
|
26
|
+
DEFAULT_CONFIDENCE_LEVEL = 0.95
|
|
27
|
+
|
|
28
|
+
def initialize(number, sample_size, population_size)
|
|
29
|
+
@number = number
|
|
30
|
+
@sample_size = sample_size
|
|
31
|
+
@population_size = population_size
|
|
32
|
+
@distribution = CombinationsDistribution.new
|
|
33
|
+
middle = @population_size / 2
|
|
34
|
+
@distribution[middle] = combinations_for(middle)
|
|
35
|
+
@previous_distribution = @distribution.dup
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def combinations(value)
|
|
39
|
+
@distribution[value]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def confidence_interval(level = DEFAULT_CONFIDENCE_LEVEL)
|
|
43
|
+
value_confidence_interval(level).map { |x| x.to_f / @population_size }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def confidence_interval_values(level = DEFAULT_CONFIDENCE_LEVEL)
|
|
47
|
+
@distribution.confidence_interval_values(level)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def error_estimate
|
|
51
|
+
estimate_error
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def interval_probabilities(intervals)
|
|
55
|
+
@distribution.interval_probabilities(intervals, @population_size)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def most_probable_fraction
|
|
59
|
+
most_probable_value.to_f / @population_size
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def most_probable_value
|
|
63
|
+
@distribution.most_probable_value
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def refine
|
|
67
|
+
@previous_distribution = @distribution.dup
|
|
68
|
+
new_values = find_refinement_values
|
|
69
|
+
new_values.each do |value|
|
|
70
|
+
@distribution[value] = combinations_for(value)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def threshold_probability(threshold)
|
|
75
|
+
@distribution.threshold_probability(threshold, @population_size)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def value_confidence_interval(level = DEFAULT_CONFIDENCE_LEVEL)
|
|
79
|
+
@distribution.confidence_interval(level, @population_size)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def values
|
|
83
|
+
@distribution.values
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def combinations_for(value)
|
|
89
|
+
dual_value = @population_size - value
|
|
90
|
+
dual_number = @sample_size - @number
|
|
91
|
+
if value < @number || dual_value < dual_number
|
|
92
|
+
0.to_lf
|
|
93
|
+
else
|
|
94
|
+
BinomialsCache.binomial(value, @number) * \
|
|
95
|
+
BinomialsCache.binomial(dual_value, dual_number)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def find_refinement_value_at_bottom
|
|
100
|
+
bottom = values.min
|
|
101
|
+
if bottom == 0
|
|
102
|
+
[]
|
|
103
|
+
else
|
|
104
|
+
[(bottom.to_f / 3).round]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def find_refinement_values_between(low, high)
|
|
109
|
+
new_low = ((2 * low + high).to_f / 3).round
|
|
110
|
+
new_high = ((2 * high + low).to_f / 3).round
|
|
111
|
+
refinement_values = []
|
|
112
|
+
refinement_values << new_high unless new_high == high
|
|
113
|
+
refinement_values << new_low unless new_low == new_high || new_low == low
|
|
114
|
+
refinement_values
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def find_refinement_values_in_between
|
|
118
|
+
sorted_values = values.sort
|
|
119
|
+
refinement_values = (0..(@distribution.size - 2)).to_a.map do |i|
|
|
120
|
+
find_refinement_values_between(sorted_values[i], sorted_values[i + 1])
|
|
121
|
+
end
|
|
122
|
+
refinement_values.flatten
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def find_refinement_value_at_top
|
|
126
|
+
top = values.max
|
|
127
|
+
if top == @population_size
|
|
128
|
+
[]
|
|
129
|
+
else
|
|
130
|
+
[((2.to_f * @population_size + top) / 3).round]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def find_refinement_values
|
|
135
|
+
find_refinement_value_at_bottom + find_refinement_values_in_between +
|
|
136
|
+
find_refinement_value_at_top
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def previous_confidence_interval(level)
|
|
140
|
+
@previous_distribution.confidence_interval(level, @population_size).map do |x|
|
|
141
|
+
x.to_f / @population_size
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def previous_most_probable_fraction
|
|
146
|
+
@previous_distribution.most_probable_value.to_f / @population_size
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def estimate_error
|
|
150
|
+
if @distribution.size == @population_size + 1
|
|
151
|
+
0
|
|
152
|
+
else
|
|
153
|
+
ci_error_estimates = STANDARD_CONFIDENCE_LEVELS.map do |l|
|
|
154
|
+
ci = confidence_interval(l)
|
|
155
|
+
pci = previous_confidence_interval(l)
|
|
156
|
+
[(ci.first - pci.first).abs, (ci.last - pci.last).abs].max
|
|
157
|
+
end
|
|
158
|
+
[1.to_f / @distribution.size,
|
|
159
|
+
(most_probable_fraction - previous_most_probable_fraction).abs,
|
|
160
|
+
ci_error_estimates.max].max
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#
|
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
|
4
|
+
# Copyright (C) 2016 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
|
+
# Class representing an electoral system based on the First-Past-The-Post
|
|
23
|
+
# system.
|
|
24
|
+
#
|
|
25
|
+
class FirstPastThePost
|
|
26
|
+
def initialize(last_election_result, last_detailed_election_result)
|
|
27
|
+
@last_election_result = last_election_result
|
|
28
|
+
@last_detailed_election_result = last_detailed_election_result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def project(simulation)
|
|
32
|
+
multiplicators = calculate_multiplicators(simulation)
|
|
33
|
+
result = create_empty_result(simulation)
|
|
34
|
+
@last_detailed_election_result.each_value do |local_last_result|
|
|
35
|
+
winner = local_winner(local_last_result, multiplicators)
|
|
36
|
+
if result.key?(winner)
|
|
37
|
+
result[winner] += 1
|
|
38
|
+
else
|
|
39
|
+
result[winner] = 1
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
result
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def calculate_multiplicators(simulation)
|
|
48
|
+
simulation_sum = simulation.values.inject(:+)
|
|
49
|
+
last_election_sum = @last_election_result.values.inject(:+)
|
|
50
|
+
multiplicators = {}
|
|
51
|
+
simulation.each_key do |choice|
|
|
52
|
+
new_fraction = simulation[choice].to_f / simulation_sum
|
|
53
|
+
last_fraction = @last_election_result[choice].to_f / last_election_sum
|
|
54
|
+
multiplicators[choice] = new_fraction / last_fraction
|
|
55
|
+
end
|
|
56
|
+
multiplicators
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def create_empty_result(simulation)
|
|
60
|
+
result = {}
|
|
61
|
+
simulation.each_key do |choice|
|
|
62
|
+
result[choice] = 0
|
|
63
|
+
end
|
|
64
|
+
result[OTHER] = 0
|
|
65
|
+
result
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def local_winner(local_last_result, multiplicators)
|
|
69
|
+
new_local_result = {}
|
|
70
|
+
local_last_result.each_pair do |choice, votes|
|
|
71
|
+
if multiplicators.key?(choice)
|
|
72
|
+
new_local_result[choice] = votes * multiplicators[choice]
|
|
73
|
+
else
|
|
74
|
+
new_local_result[choice] = votes
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
max_votes = new_local_result.values.max
|
|
78
|
+
winner = new_local_result.select { |_k, v| v.equal?(max_votes) }.keys.first
|
|
79
|
+
multiplicators.key?(winner) ? winner : OTHER
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
|
2
|
+
# Copyright (C) 2016 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
|
+
module Sapor
|
|
19
|
+
#
|
|
20
|
+
# Class representing a largest remainder electoral system with only one
|
|
21
|
+
# district.
|
|
22
|
+
#
|
|
23
|
+
class LargestRemainder
|
|
24
|
+
def initialize(no_of_seats, quota_class, threshold = 0, full_quota = false,
|
|
25
|
+
bonus = 0, other_eligible = true)
|
|
26
|
+
@no_of_seats = no_of_seats
|
|
27
|
+
@quota_class = quota_class
|
|
28
|
+
@threshold = threshold
|
|
29
|
+
@full_quota = full_quota
|
|
30
|
+
@bonus = bonus
|
|
31
|
+
@other_eligible = other_eligible
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def project(simulation)
|
|
35
|
+
result = create_empty_result(simulation)
|
|
36
|
+
thresholded_simulation = apply_threshold(simulation)
|
|
37
|
+
total_votes = if @full_quota
|
|
38
|
+
simulation.values.inject(:+)
|
|
39
|
+
else
|
|
40
|
+
thresholded_simulation.values.inject(:+)
|
|
41
|
+
end
|
|
42
|
+
quota = @quota_class.get(total_votes, @no_of_seats)
|
|
43
|
+
add_automatic_seats(result, thresholded_simulation, quota)
|
|
44
|
+
add_remaining_seats(result, thresholded_simulation, quota)
|
|
45
|
+
result[simulation.max_by(&:last)[0]] += @bonus
|
|
46
|
+
result
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def add_automatic_seats(result, simulation, quota)
|
|
52
|
+
simulation.each_pair do |choice, votes|
|
|
53
|
+
result[choice] = (votes / quota).floor
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def add_remaining_seats(result, simulation, quota)
|
|
58
|
+
remainders = calculate_remainders(simulation, quota)
|
|
59
|
+
choices = simulation.keys.sort { |a, b| remainders[b] <=> remainders[a] }
|
|
60
|
+
remaining_seats = @no_of_seats - result.values.inject(:+)
|
|
61
|
+
choices.slice(0, remaining_seats).each { |c| result[c] += 1 }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def apply_threshold(simulation)
|
|
65
|
+
threshold = simulation.values.inject(:+).to_f * @threshold
|
|
66
|
+
thresholded_simulation = {}
|
|
67
|
+
simulation.each_pair do |choice, votes|
|
|
68
|
+
thresholded_simulation[choice] = votes >= threshold ? votes : 0
|
|
69
|
+
end
|
|
70
|
+
thresholded_simulation[OTHER] = 0 unless @other_eligible
|
|
71
|
+
thresholded_simulation
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def calculate_remainders(simulation, quota)
|
|
75
|
+
remainders = {}
|
|
76
|
+
simulation.each_pair do |choice, votes|
|
|
77
|
+
remainders[choice] = votes / quota - (votes / quota).floor
|
|
78
|
+
end
|
|
79
|
+
remainders
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def create_empty_result(simulation)
|
|
83
|
+
result = {}
|
|
84
|
+
simulation.each_key do |choice|
|
|
85
|
+
result[choice] = 0
|
|
86
|
+
end
|
|
87
|
+
result[OTHER] = 0
|
|
88
|
+
result
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# Class calculating the Hare quota.
|
|
94
|
+
#
|
|
95
|
+
class HareQuota
|
|
96
|
+
def self.get(total_votes, no_of_seats)
|
|
97
|
+
total_votes.to_f / no_of_seats
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
#
|
|
102
|
+
# Class calculating the Droop quota.
|
|
103
|
+
#
|
|
104
|
+
class DroopQuota
|
|
105
|
+
def self.get(total_votes, no_of_seats)
|
|
106
|
+
1 + total_votes.to_f / (1 + no_of_seats)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
# Class calculating the Hagenbach-Bischoff quota.
|
|
112
|
+
#
|
|
113
|
+
class HagenbachBischoffQuota
|
|
114
|
+
def self.get(total_votes, no_of_seats)
|
|
115
|
+
total_votes.to_f / (1 + no_of_seats)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#
|
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
|
4
|
+
# Copyright (C) 2016 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
|
+
# Builder to create Log4rLoggers.
|
|
23
|
+
#
|
|
24
|
+
class Log4rLoggerBuilder
|
|
25
|
+
def create_logger
|
|
26
|
+
Log4rLogger.new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
require 'rubygems'
|
|
31
|
+
require 'log4r'
|
|
32
|
+
#
|
|
33
|
+
# Logger using Log4r.
|
|
34
|
+
#
|
|
35
|
+
class Log4rLogger
|
|
36
|
+
def initialize
|
|
37
|
+
@logger = Log4r::Logger.new 'stdout'
|
|
38
|
+
stdout_outputter = Log4r::Outputter.stdout
|
|
39
|
+
@logger.outputters = stdout_outputter
|
|
40
|
+
stdout_format = Log4r::PatternFormatter.new(pattern: '%d %l: %m')
|
|
41
|
+
stdout_outputter.formatter = stdout_format
|
|
42
|
+
@logger.level = Log4r::INFO
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def info(message)
|
|
46
|
+
@logger.info(message)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#
|
|
3
|
+
# Statistical Analysis of Polling Results (SAPoR)
|
|
4
|
+
# Copyright (C) 2016 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
|
+
# A facade for logging.
|
|
23
|
+
#
|
|
24
|
+
class LogFacade
|
|
25
|
+
@logger_builder = nil
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
attr_writer :logger_builder
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.builder
|
|
32
|
+
@logger_builder = Log4rLoggerBuilder.new if @logger_builder.nil?
|
|
33
|
+
@logger_builder
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.create_logger
|
|
37
|
+
builder.create_logger
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|