-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrcipe_stv.cpp
3485 lines (2802 loc) · 141 KB
/
rcipe_stv.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// -----------------------------------------------
// -----------------------------------------------
//
// rcipe_stv.cpp
//
// This software utility calculates election
// results for the following methods:
//
// * STV (the Single Transferable Vote)
//
// * RCIPE STV
//
// * IRV (Instant Runoff Voting)
//
// * RCIPE
//
// RCIPE is the abbreviation for "Ranked Choice
// Including Pairwise Elimination."
//
// IMPORTANT:
// For all these calculations, ballots on which a
// voter marks more than one candidate at the
// same preference level are counted instead of
// being discarded.
//
// When shared preference levels are encountered,
// the ballots are transfered in "whole" numbers,
// not by splitting a ballot into fractional or
// decimal portions. For example, during a
// counting cycle, if there are two ballots that
// rank candidates numbered 1 and 2 at the same
// highest ranking level, one of the ballots will
// transfer to candidate 1 and the other ballot
// will transfer to candidate 2.
//
// Unlike some simplistic versions of STV
// software, during each counting cycle either a
// candidate wins an available seat, or an
// unpopular candidate is eliminated, but not
// both in the same counting cycle. Also, only
// one candidate can get elected in each counting
// cycle because ballots are transfered after
// each seat is filled.
//
// The RCIPE and RCIPE STV methods are described
// at:
// https://electowiki.org/wiki/Ranked_Choice_Including_Pairwise_Elimination
//
// To specify IRV or RCIPE calculations, specify
// that just a single seat is available to be
// filled. To request STV or RCIPE STV
// calculations, specify the number of seats to
// be filled, with the typical number of seats
// being 2, 3, 4, or 5. Use voteinfo code -67
// followed by the number of seats to be filled.
// There is no default choice for this seat
// count.
//
// To specify IRV or STV instead of RCIPE or
// RCIPE STV, request that pairwise losing
// candidates not be eliminated when they occur.
// Otherwise, the default is to eliminate
// pairwise losing candidates when they occur.
// A pairwise losing candidate is a candidate who
// would lose every one-on-one contest against
// every other remaining candidate. This extra
// elimination prevents a ballot from getting
// "stuck" on a candidate who a majority of
// voters dislike, which causes that ballot's
// lower ranking marks to be ignored while other
// ballots control which of the more-popular
// candidates win and lose. The default choice
// is to eliminate pairwise losing candidates
// when they occur. Use voteinfo code -50 to
// request IRV or STV.
//
// When calculating STV or RCIPE STV results,
// either the Hare quota or the Droop quota can
// be selected. The default choice is the Hare
// quota. Use voteinfo code -68 to request the
// Droop quota.
//
// Ballot counting is done in ways that avoid
// getting different results if the supplied
// ballot sequence is changed. Electing only one
// candidate per counting cycle is one such way
// of avoiding inconsistent results.
//
// Additional details appear below within the
// code comments.
//
//
// -----------------------------------------------
//
// COPYRIGHT & LICENSE
//
// (c) Copyright 2022 by Richard Fobes at
// www.VoteFair.org. You can redistribute and/or
// modify this rcipe_stv software under the MIT
// software license terms that appear below.
// (Also a copy of the license is included in the
// "license" file.)
//
// Conversion of this code into another
// programming language is also covered by the
// above license terms.
//
//
// -----------------------------------------------
//
// VERSION
//
// Version 0.10 - In April 2022 Richard Fobes
// derived this code from the
// VoteFair_Ranking.cpp code that calculates
// VoteFair Ranking, and single-winner RCIPE,
// and other single-winner vote-counting methods.
//
// Version 0.15 - In May 2022 Richard Fobes
// did testing and debugging, and improved
// comments, and updated the version count to:
// 0.15 Yet more rigorous testing is needed
// before relying on this code for elections.
//
// Version 0.20 - In June 2022 removed a
// potential bug, after more successful
// testing.
//
// -----------------------------------------------
//
// USAGE
//
// The following sample code compiles and
// executes this software under a typical
// Windows environment with the g++ compiler and
// the mingw32 library already installed.
//
// path=C:\Program Files (x86)\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin\
//
// g++ rcipe_stv.cpp -o rcipe_stv
//
// .\rcipe_stv < input_rcipe_stv_case_123.txt > output_rcipe_stv_case_123.txt
//
// This usage assumes that file
// input_rcipe_stv_case_123.txt contains
// appropriately formatted election/survey/poll
// data for case numbered 123, and it writes
// the coded results to file
// output_rcipe_stv_case_123.txt.
//
// Typically the input file is generated by
// other software, and typically the output file
// is used as input to other software. An
// example of such software is the
// "VoteFair-polls" code on GitHub (in the
// CPSolver repository).
//
// The input file must contain integer codes that
// have the meanings specified in the constants
// that begin with "global_voteinfo_code_for_...".
//
// The mathematical algorithms of RCIPE and
// RCIPE STV are in the public domain.
//
//
// -----------------------------------------------
//
// SAMPLE INPUT FILE
//
// The following text (excluding the "//" text)
// represents the data in the example in the
// Wikipedia article titled "Comparison of the
// Hare and Droop quotas".
//
// -7
// -3 1000001
// -4 1
// -6 6
// -67 5
// -50
// -9 -4 1 -11 31 1 2 3 -10
// -9 -4 1 -11 30 3 1 2 -10
// -9 -4 1 -11 2 2 1 3 -10
// -9 -4 1 -11 20 4 5 6 -10
// -9 -4 1 -11 20 5 4 6 -10
// -9 -4 1 -11 17 6 4 5 -10
// -8
//
//
// -----------------------------------------------
//
// LICENSE
//
// This rcipe_stv software is licensed
// under the following MIT License terms.
//
// MIT License for rcipe_stv.cpp
//
// Copyright (c) 2022 by Richard Fobes at
// ww.VoteFair.org
//
// Permission is hereby granted, free of charge,
// to any person obtaining a copy of this software
// and associated documentation files (the
// "Software"), to deal in the Software without
// restriction, including without limitation the
// rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom
// the Software is furnished to do so, subject
// to the following conditions:
//
// The above copyright notice and this
// permission notice shall be included in all
// copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT
// WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//
// -----------------------------------------------
//
// IMPORTANT NOTES TO CODERS!
//
// Please report any bugs or feature requests on
// GitHub, at the CPSolver account, in the
// VoteFair-ranking-cpp project area. Thank you!
//
// If you offer improvements to this code,
// please follow the conventions used here, and
// please keep this code easy to convert into
// other programming languages.
//
// Please resist the temptation to add the
// handling of candidate names or any other text
// that appears on ballots. The absense of that
// text makes it obvious that this code is
// completely unbiased -- because candidate
// names, party names, and election names are
// not available to this software. Instead an
// election and candidates are identified by
// integer numbers (1, 2, 3, etc.) and only the
// software that assigned these numbers can
// correlate them with any names or identities.
//
//
// -----------------------------------------------
// -----------------------------------------------
// Begin code.
// -----------------------------------------------
// -----------------------------------------------
// Specify libraries needed.
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdio>
// -----------------------------------------------
// Declare global variables, lists, etc.
// Declare the case number as a variable.
// It is received from the input file and copied
// to the output file to allow verifying a match
// between the ballots and the results.
int global_case_number ;
// Declare global variables that are
// controlled by voteinfo codes.
int global_number_of_seats_to_fill ;
int global_true_or_false_request_no_pairwise_loser_elimination ;
int global_true_or_false_request_quota_droop ;
int global_true_or_false_request_ignore_shared_rankings ;
// Declare global variables.
int global_input_line_number ;
int global_current_voteinfo_number ;
int global_next_voteinfo_number ;
int global_previous_voteinfo_number ;
int global_pointer_to_voteinfo_number ;
int global_pointer_to_end_of_voteinfo_numbers ;
int global_within_ballots ;
int global_question_number ;
int global_candidate_number ;
int global_candidate_just_elected ;
int global_number_of_candidates ;
int global_number_of_remaining_candidates ;
int global_count_of_candidates_marked ;
int global_count_of_top_ranked_remaining_candidates ;
int global_number_of_seats_still_available ;
int global_ballot_info_repeat_count ;
int global_current_total_vote_count ;
int global_supporting_votes_for_elected_candidate ;
int global_supporting_vote_count_that_exceeds_quota ;
int global_need_to_initialize_group_ballot_count ;
int global_quota_count ;
int global_counting_cycle_number ;
int global_ballot_group_pointer ;
int global_total_count_of_ballot_groups ;
int global_count_of_unique_pattern_numbers ;
int global_pair_counter_maximum ;
int global_pointer_to_output_results ;
int global_length_of_result_info_list ;
int global_logging_info ;
// Specify the true and false values.
// These are used instead of language-specific codes
// because different programming languages use
// different conventions for their true/false logic.
const int global_true = 1 ;
const int global_false = 0 ;
// For speed reasons, arrays are declared here,
// not within a function, even if a single
// function uses them.
// Position one -- [ 1 ] -- is used as the
// starting position for these arrays, so the
// array lengths have to be longer than the
// number of items to be stored.
// These arrays have one item per candidate.
const int global_maximum_candidate_number = 100 ;
int global_true_or_false_winner_candidate[ 101 ] ;
int global_true_or_false_eliminated_candidate[ 101 ] ;
int global_true_or_false_available_candidate[ 101 ] ;
int global_true_or_false_is_top_ranked_candidate[ 101 ] ;
int global_true_or_false_pairwise_consider_candidate[ 101 ] ;
int global_ballot_preference_for_candidate[ 101 ] ;
int global_vote_transfer_count_for_candidate[ 101 ] ;
int global_win_count_for_candidate[ 101 ] ;
int global_loss_count_for_candidate[ 101 ] ;
int global_tally_uses_of_candidate_number[ 101 ] ;
int global_list_of_top_ranked_candidates[ 101 ] ;
int global_list_of_candidates_with_highest_vote_transfer_count[ 101 ] ;
int global_list_of_candidates_with_lowest_vote_transfer_count[ 101 ] ;
int global_list_of_candidates_tied[ 101 ] ;
// Declare the input-related list.
// Allow room for extra codes at the end.
const int global_maximum_vote_info_list_length = 200000 ;
int global_vote_info_list[ 200005 ] ;
// Declare the output-related list.
// Allow room for extra codes at the end.
const int global_maximum_output_results_length = 2000 ;
int global_output_results[ 2005 ] ;
// Declare pairwise lists.
const int global_maximum_candidate_pairs = 20000 ;
int global_first_candidate_number_in_pair[ 2001 ] ;
int global_second_candidate_number_in_pair[ 2001 ] ;
int global_tally_first_over_second_in_pair[ 2001 ] ;
int global_tally_second_over_first_in_pair[ 2001 ] ;
int global_tally_first_equal_second_in_pair[ 2001 ] ;
// Declare the lists that group identical ballots
// together for faster processing.
const int global_maximum_number_of_ballot_groups = 20000 ;
int global_ballot_count_remaining_for_ballot_group[ 20001 ] ;
int global_top_ranked_candidate_for_ballot_group[ 20001 ] ;
// Declare the lists that combine the counting of
// ballots that have the same equivalent top-ranked
// candidates (during that counting cycle).
const int global_maximum_number_of_pattern_numbers = 10000 ;
int global_pattern_number_for_pattern_number_pointer[ 10001 ] ;
int global_ballot_count_for_pattern_number_pointer[ 10001 ] ;
int global_top_candidate_count_for_pattern_number_pointer[ 10001 ] ;
// Note: Do NOT change these numbers! They
// match codes used in the
// VoteFair_Ranking.cpp application.
// Some of these codes supply ballot information
// and make requests for various options. Some
// of these codes supply calculated results.
// Some codes can be used for both input and
// output.
const int global_voteinfo_code_for_start_of_all_cases = -1 ;
const int global_voteinfo_code_for_end_of_all_cases = -2 ;
const int global_voteinfo_code_for_case_number = -3 ;
const int global_voteinfo_code_for_question_number = -4 ;
const int global_voteinfo_code_for_total_ballot_count = -5 ;
// In other software the following constant is named global_voteinfo_code_for_number_of_choices
const int global_voteinfo_code_for_number_of_candidates = -6 ;
const int global_voteinfo_code_for_start_of_all_vote_info = -7 ;
const int global_voteinfo_code_for_end_of_all_vote_info = -8 ;
const int global_voteinfo_code_for_start_of_ballot = -9 ;
const int global_voteinfo_code_for_end_of_ballot = -10 ;
// The following ballot count indicates how many ballots have the same ranking.
const int global_voteinfo_code_for_ballot_count = -11 ;
const int global_voteinfo_code_for_preference_level = -12 ;
const int global_voteinfo_code_for_choice = -13 ;
const int global_voteinfo_code_for_tie = -14 ;
const int global_voteinfo_code_for_start_of_votefair_popularity_ranking_sequence_results = -15 ;
const int global_voteinfo_code_for_end_of_votefair_popularity_ranking_sequence_results = -16 ;
const int global_voteinfo_code_for_start_of_votefair_popularity_ranking_levels_results = -17 ;
const int global_voteinfo_code_for_end_of_votefair_popularity_ranking_levels_results = -18 ;
const int global_voteinfo_code_for_start_of_votefair_representation_ranking_sequence_results = -19 ;
const int global_voteinfo_code_for_end_of_votefair_representation_ranking_sequence_results = -20 ;
const int global_voteinfo_code_for_start_of_votefair_representation_ranking_levels_results = -21 ;
const int global_voteinfo_code_for_end_of_votefair_representation_ranking_levels_results = -22 ;
const int global_voteinfo_code_for_start_of_votefair_party_ranking_sequence_results = -23 ;
const int global_voteinfo_code_for_end_of_votefair_party_ranking_sequence_results = -24 ;
const int global_voteinfo_code_for_start_of_votefair_party_ranking_levels_results = -25 ;
const int global_voteinfo_code_for_end_of_votefair_party_ranking_levels_results = -26 ;
const int global_voteinfo_code_for_ranking_level = -27 ;
const int global_voteinfo_code_for_next_ranking_level = -28 ;
const int global_voteinfo_code_for_early_end_of_ranking = -29 ;
const int global_voteinfo_code_for_start_of_tally_table_results = -30 ;
const int global_voteinfo_code_for_end_of_tally_table_results = -31 ;
const int global_voteinfo_code_for_first_choice = -32 ;
const int global_voteinfo_code_for_second_choice = -33 ;
const int global_voteinfo_code_for_tally_first_over_second = -34 ;
const int global_voteinfo_code_for_tally_second_over_first = -35 ;
const int global_voteinfo_code_for_start_of_plurality_results = -36 ;
const int global_voteinfo_code_for_end_of_plurality_results = -37 ;
const int global_voteinfo_code_for_plurality_count = -38 ;
const int global_voteinfo_code_for_skip_case = -39 ;
const int global_voteinfo_code_for_skip_question = -40 ;
const int global_voteinfo_code_for_request_votefair_representation_rank = -41 ;
const int global_voteinfo_code_for_request_no_votefair_representation_rank = -42 ;
const int global_voteinfo_code_for_request_votefair_party_rank = -43 ;
const int global_voteinfo_code_for_request_no_votefair_party_rank = -44 ;
const int global_voteinfo_code_for_request_only_plurality_results = -45 ;
const int global_voteinfo_code_for_request_pairwise_counts = -46 ;
const int global_voteinfo_code_for_request_no_pairwise_counts = -47 ;
const int global_voteinfo_code_for_number_of_representation_levels_to_compute = -48 ;
const int global_voteinfo_code_for_request_text_output = -49 ;
const int global_voteinfo_code_for_request_instant_runoff_voting = -50 ;
const int global_voteinfo_code_for_request_instant_pairwise_elimination = -51 ;
const int global_voteinfo_code_for_request_rcipe_voting = -52 ;
const int global_voteinfo_code_for_winner_instant_runoff_voting = -53 ;
const int global_voteinfo_code_for_winner_instant_pairwise_elimination = -54 ;
const int global_voteinfo_code_for_winner_rcipe_voting = -55 ;
const int global_voteinfo_code_for_request_star_voting = -56 ;
const int global_voteinfo_code_for_winner_star_voting = -57 ;
const int global_voteinfo_code_for_request_pairwise_loser_elimination = -58 ;
const int global_voteinfo_code_for_winner_pairwise_loser_elimination = -59 ;
const int global_voteinfo_code_for_winner_irv_bottom_two_runoff = -60 ;
const int global_voteinfo_code_for_winner_borda_count = -61 ;
const int global_voteinfo_code_for_flag_as_interesting = -62 ;
const int global_voteinfo_code_for_winner_approval_voting = -63 ;
const int global_voteinfo_code_for_winner_condorcet = -64 ;
const int global_voteinfo_code_for_request_logging_off = -65 ;
const int global_voteinfo_code_for_winner_pairwise_support_count = -66 ;
const int global_voteinfo_code_for_number_of_equivalent_seats = -67 ;
const int global_voteinfo_code_for_request_quota_droop_not_hare = -68 ;
const int global_voteinfo_code_for_winner_next_seat = -69 ;
const int global_voteinfo_code_for_begin_tied_for_next_seat = -70 ;
const int global_voteinfo_code_for_end_tied_for_next_seat = -71 ;
const int global_voteinfo_code_for_counting_cycle_number = -72 ;
const int global_voteinfo_code_for_pairwise_losing_candidate = -73 ;
const int global_voteinfo_code_for_eliminated_candidate = -74 ;
const int global_voteinfo_code_for_quota_count_this_cycle = -75 ;
const int global_voteinfo_code_for_candidate_and_transfer_count = -76 ;
const int global_voteinfo_code_for_candidate_to_ignore = -77 ;
const int global_voteinfo_code_for_request_ignore_shared_rankings = -78 ;
const int global_voteinfo_code_for_invalid_input_word = -200 ;
// Specify an extra output file that contains a log
// of actions for the purpose of monitoring or
// debugging intermediate calculations.
std::ofstream log_out ;
// Declare message strings.
std::string global_possible_error_message ;
std::string global_text_quota_type ;
std::string global_text_quota_type_hare ;
std::string global_text_quota_type_droop ;
std::string global_text_quota_type_majority ;
// -----------------------------------------------
// -----------------------------------------------
// convert_integer_to_text
//
// This function is used instead of "std::to_string"
// for compatibility with older C++ "string" libraries
// that have a bug. The bug is that the "to_string"
// function is not recognized as being within the
// "std" library, even though it is defined there.
std::string convert_integer_to_text( int supplied_integer )
{
int unused_string_length ;
char c_format_string[ 50 ] ;
try
{
unused_string_length = sprintf( c_format_string , "%1d" , supplied_integer ) ;
return ( std::string ) c_format_string ;
}
catch( ... )
{
return "NAN" ;
}
}
// -----------------------------------------------
// -----------------------------------------------
// convert_float_to_text
//
// To read why this function is here, see the comment
// above for function: convert_integer_to_text
std::string convert_float_to_text( float supplied_float )
{
std::string returned_string ;
char c_format_string[ 50 ] ;
int unused_string_length ;
try
{
unused_string_length = sprintf( c_format_string , "%1f" , supplied_float ) ;
returned_string = ( std::string ) c_format_string ;
// next line assumes the sprintf result always includes a decimal point
returned_string.erase( returned_string.find_last_not_of( "0" ) + 1 , std::string::npos ) ;
returned_string.erase( returned_string.find_last_not_of( "." ) + 1 , std::string::npos ) ;
return returned_string ;
}
catch( ... )
{
return "NAN" ;
}
}
// -----------------------------------------------
// -----------------------------------------------
// convert_text_to_integer
//
// To read why this function is here, see the comment
// above for function: convert_integer_to_text
int convert_text_to_integer( char * supplied_text )
{
int equivalent_integer ;
try
{
equivalent_integer = atoi( supplied_text ) ;
}
catch( ... )
{
equivalent_integer = -999 ;
}
return equivalent_integer ;
}
// -----------------------------------------------
// -----------------------------------------------
// do_main_initialization
//
// Does initialization at the very beginning.
void do_main_initialization( )
{
int pointer ;
int candidate_number ;
// -----------------------------------------------
// Reset logging flag.
global_logging_info = global_true ;
// -----------------------------------------------
// Open the output file that logs details.
log_out.open ( "output_rcipe_stv_log.txt" , std::ios::out ) ;
// -----------------------------------------------
// Initialize zero values.
global_current_voteinfo_number = 0 ;
global_previous_voteinfo_number = 0 ;
global_next_voteinfo_number = 0 ;
global_ballot_info_repeat_count = 0 ;
global_current_total_vote_count = 0 ;
global_pointer_to_voteinfo_number = 0 ;
global_pointer_to_end_of_voteinfo_numbers = 0 ;
global_pointer_to_output_results = 0 ;
global_case_number = 0 ;
global_length_of_result_info_list = 0 ;
global_number_of_candidates = 0 ;
global_ballot_group_pointer = 0 ;
global_input_line_number = 0 ;
// -----------------------------------------------
// Initialize flags.
global_true_or_false_request_quota_droop = global_false ;
global_true_or_false_request_no_pairwise_loser_elimination = global_false ;
global_true_or_false_request_ignore_shared_rankings = global_false ;
// -----------------------------------------------
// Initialize text values.
global_possible_error_message = "" ;
global_text_quota_type_hare = "Hare" ;
global_text_quota_type_droop = "Droop" ;
global_text_quota_type_majority = "majority" ;
global_text_quota_type = global_text_quota_type_hare ;
// -----------------------------------------------
// Initialize lists to zeros.
for ( pointer = 0 ; pointer <= global_maximum_vote_info_list_length ; pointer ++ )
{
global_vote_info_list[ pointer ] = 0 ;
}
for ( pointer = 0 ; pointer <= global_maximum_output_results_length ; pointer ++ )
{
global_output_results[ pointer ] = 0 ;
}
// -----------------------------------------------
// Initialize the candidate-specific flags.
for ( candidate_number = 0 ; candidate_number <= global_maximum_candidate_number ; candidate_number ++ )
{
global_true_or_false_available_candidate[ candidate_number ] = global_true ;
global_true_or_false_winner_candidate[ candidate_number ] = global_false ;
global_true_or_false_eliminated_candidate[ candidate_number ] = global_false ;
}
// -----------------------------------------------
// End of function do_main_initialization.
return ;
}
// -----------------------------------------------
// -----------------------------------------------
// save_ballot_info_number
//
// Puts the next voteinfo number into the list
// that stores the ballot-specific information.
void save_ballot_info_number( int voteinfo_number )
{
// -----------------------------------------------
// Increment the pointer to the voteinfo numbers
// being stored in the list.
// Note that list position zero ([0]) is not used!
global_pointer_to_voteinfo_number ++ ;
// -----------------------------------------------
// If the list has become too long, indicate that
// fatal error.
if ( global_pointer_to_voteinfo_number > global_maximum_vote_info_list_length )
{
if ( global_logging_info == global_true ) { log_out << "[error, too many vote-info numbers supplied, the available storage space must be increased]" ; } ;
std::cout << "Error: Too many vote-info numbers supplied, the available storage space must be increased." ;
exit( EXIT_FAILURE ) ;
}
// -----------------------------------------------
// Store the supplied ballot-specific voteinfo
// number.
global_vote_info_list[ global_pointer_to_voteinfo_number ] = voteinfo_number ;
// if ( global_logging_info == global_true ) { log_out << "[at " << global_pointer_to_voteinfo_number << " vicode " << voteinfo_number << "]" ; } ;
// -----------------------------------------------
// Insert an end-of-info code number at the next
// position, in case this is the last voteinfo
// number put into the list.
global_vote_info_list[ global_pointer_to_voteinfo_number + 1 ] = global_voteinfo_code_for_end_of_all_vote_info ;
// -----------------------------------------------
// End of function save_ballot_info_number.
return ;
}
// -----------------------------------------------
// -----------------------------------------------
// put_next_result_info_number
//
// Puts the next result-info number into the array
// that stores the result information.
void put_next_result_info_number( int current_result_info_number )
{
// -----------------------------------------------
// If the list has become too long, insert the
// code that indicates the end of the results,
// and then indicate an error.
if ( global_pointer_to_output_results >= global_maximum_output_results_length )
{
global_output_results[ global_pointer_to_output_results ] = global_voteinfo_code_for_end_of_all_cases ;
if ( global_logging_info == global_true ) { log_out << "[error, not enough room for all results (size limit is " << convert_integer_to_text( global_maximum_output_results_length ) << "]" ; } ;
global_possible_error_message = "Error: Not enough room for all results (size limit is " + convert_integer_to_text( global_maximum_output_results_length ) + ")." ;
return ;
}
// -----------------------------------------------
// Put the next result-info number into the list.
global_output_results[ global_pointer_to_output_results ] = current_result_info_number ;
// -----------------------------------------------
// Increment the list pointer, and increment the
// length of the list.
global_pointer_to_output_results ++ ;
global_length_of_result_info_list = global_pointer_to_output_results ;
// -----------------------------------------------
// End of function put_next_result_info_number.
return ;
}
// -----------------------------------------------
// -----------------------------------------------
// handle_one_voteinfo_number
//
// Handles each voteinfo number, one at a time.
// Some voteinfo numbers specify an integer
// number, such as the case number, the number of
// candidates, and the number of equivalent seats
// to fill. Those numbers are saved in special
// variables. The remaining numbers, which
// indicate ballot preferences, and repeat counts
// -- for identical ballots -- are stored in a
// list that will be accessed repeatedly during
// vote counting. Also this function checks for
// errors, and reports any that occur.
void handle_one_voteinfo_number( )
{
int candidate_number ;
int true_or_false_have_handled_one_unranked_candidate ;
// -----------------------------------------------
// If the code is a ballot repeat count, or is
// the end of the ballot data, and if not all the
// candidate numbers have been encountered for
// this ballot, rank these not-encountered
// candidates at the level below the last
// candidate encountered. Do not return yet
// because the ballot repeat count for the new
// ballot group needs to be saved.
if ( ( global_total_count_of_ballot_groups >= 1 ) && ( ( global_current_voteinfo_number == global_voteinfo_code_for_ballot_count ) || ( global_current_voteinfo_number == global_voteinfo_code_for_end_of_all_cases ) || ( global_current_voteinfo_number == global_voteinfo_code_for_end_of_all_vote_info ) ) )
{
true_or_false_have_handled_one_unranked_candidate = global_false ;
for ( candidate_number = 1 ; candidate_number <= global_number_of_candidates ; candidate_number ++ )
{
if ( global_tally_uses_of_candidate_number[ candidate_number ] < 1 )
{
if ( true_or_false_have_handled_one_unranked_candidate == global_true )
{
save_ballot_info_number( global_voteinfo_code_for_tie ) ;
}
save_ballot_info_number( candidate_number ) ;
true_or_false_have_handled_one_unranked_candidate = global_true ;
// if ( global_logging_info == global_true ) { log_out << "[unmarked candidate " << candidate_number << "]" ; } ;
}
}
}
// -----------------------------------------------
// If the current code indicates a ballot repeat
// count, clear the flags that track which
// candidate numbers are encountered in this
// ballot group. Do not return yet.
if ( global_current_voteinfo_number == global_voteinfo_code_for_ballot_count )
{
for ( candidate_number = 1 ; candidate_number <= global_number_of_candidates ; candidate_number ++ )
{
global_tally_uses_of_candidate_number[ candidate_number ] = 0 ;
}
}
// -----------------------------------------------
// Handle the end of a case or the end of the
// ballot info, check for any errors, and return.
if ( ( global_current_voteinfo_number == global_voteinfo_code_for_end_of_all_cases ) || ( global_current_voteinfo_number == global_voteinfo_code_for_end_of_all_vote_info ) )
{
if ( global_case_number > 0 )
{
if ( global_ballot_info_repeat_count == 0 )
{
if ( global_logging_info == global_true ) { log_out << "[error, no ballots found]" ; } ;
global_possible_error_message = "Error: No ballots found." ;
return ;
}
if ( global_number_of_seats_to_fill < 1 )
{
if ( global_logging_info == global_true ) { log_out << "[error, seats to fill is less than one]" ; } ;
global_possible_error_message = "Error: Seats to fill is less than one." ;
return ;
}
} else if ( global_case_number < 1 )
{
if ( global_logging_info == global_true ) { log_out << "[error, case number not specified]" ; } ;
global_possible_error_message = "Error: Case number was not specified." ;
return ;
}
return ;
}
// -----------------------------------------------
// Get the ballot repeat count, increment the
// counter for the number of ballot groups, and
// save the ballot repeat count in the
// ballot-info list, then return.
if ( global_previous_voteinfo_number == global_voteinfo_code_for_ballot_count )
{
global_ballot_info_repeat_count = global_current_voteinfo_number ;
save_ballot_info_number( global_voteinfo_code_for_ballot_count ) ;
save_ballot_info_number( global_ballot_info_repeat_count ) ;
global_count_of_candidates_marked = 0 ;
global_total_count_of_ballot_groups ++ ;
if ( global_logging_info == global_true ) { log_out << "[bc " << global_ballot_info_repeat_count << "]" ; } ;
if ( global_ballot_info_repeat_count < 1 )
{
if ( global_logging_info == global_true ) { log_out << "[error, ballot count number is less than one (" << global_ballot_info_repeat_count << ")]" ; } ;
global_possible_error_message = "Error: Ballot count number is less than one (" + convert_integer_to_text( global_ballot_info_repeat_count ) + ")." ;
} else if ( global_total_count_of_ballot_groups >= global_maximum_number_of_ballot_groups )
{
if ( global_logging_info == global_true ) { log_out << "[error, number of ballot groups (" << global_total_count_of_ballot_groups << ") exceeds available storage space]" ; } ;
global_possible_error_message = "Error: Number of ballot groups (" + convert_integer_to_text( global_total_count_of_ballot_groups ) + ") exceeds the available storage space." ;
}
return ;
}
// -----------------------------------------------
// Handle the code for a tie. Save it in the
// vote-info list, then return.
if ( global_current_voteinfo_number == global_voteinfo_code_for_tie )
{
save_ballot_info_number( global_voteinfo_code_for_tie ) ;
if ( global_logging_info == global_true ) { log_out << "[+]" ; } ;
if ( global_count_of_candidates_marked < 1 )
{
if ( global_logging_info == global_true ) { log_out << "[error, invalid nesting of tied preference vote-info number, at input line number " << global_input_line_number << "]" ; } ;
global_possible_error_message = "Error: Invalid nesting of tied preference vote-info number, at input line number " + convert_integer_to_text( global_input_line_number ) + "." ;
return ;
}
global_count_of_candidates_marked = 0 ;
return ;
}
// -----------------------------------------------
// Get the case number, then return.
if ( global_previous_voteinfo_number == global_voteinfo_code_for_case_number )
{
if ( global_case_number != 0 )
{
if ( global_logging_info == global_true ) { log_out << "[error, second case number encountered, which is not allowed]" ; } ;
global_possible_error_message = "Error: Second case number encountered, which is not valid." ;
return ;
}
global_case_number = global_current_voteinfo_number ;
global_within_ballots = global_false ;
if ( global_logging_info == global_true ) { log_out << "[case " << global_case_number << "]" ; } ;
if ( global_case_number < 1 )
{
if ( global_logging_info == global_true ) { log_out << "[error, case number is less than one, which is not valid]" ; } ;
global_possible_error_message = "Error: Case number is less than one, which is not valid." ;
}
return ;
}
// -----------------------------------------------
// Get the question number, which must be one,
// then return.
// Reminder: This software handles only one
// question, but the voteinfo codes support
// multiple questions.
if ( global_previous_voteinfo_number == global_voteinfo_code_for_question_number )
{
global_question_number = global_current_voteinfo_number ;
if ( global_question_number != 1 )
{
if ( global_logging_info == global_true ) { log_out << "[error, question number is not one (" << global_question_number << ")]" ; } ;
global_possible_error_message = "Error: Encountered question number that is not one (" + convert_integer_to_text( global_question_number ) + ")." ;
}
return ;
}
// -----------------------------------------------
// Get the count for the number of candidates,
// then return.
if ( global_previous_voteinfo_number == global_voteinfo_code_for_number_of_candidates )
{
global_number_of_candidates = global_current_voteinfo_number ;
if ( global_logging_info == global_true ) { log_out << "[candidate count " << global_number_of_candidates << "]" ; } ;
if ( global_number_of_candidates == 1 )
{
if ( global_logging_info == global_true ) { log_out << "[error, only one candidate]" ; } ;
global_possible_error_message = "Error: Only one candidate." ;
return ;
}
if ( global_number_of_candidates < 1 )
{
if ( global_logging_info == global_true ) { log_out << "[error, no candidates specified]" ; } ;