liquid_feedback_core
diff lf_update_suggestion_order.c @ 372:5f3cd73f328d
Added comments to lf_update_suggestion_order.c
author | jbe |
---|---|
date | Sun Mar 17 20:03:01 2013 +0100 (2013-03-17) |
parents | 8344753f2ac1 |
children | 88322e31107b |
line diff
1.1 --- a/lf_update_suggestion_order.c Sun Mar 17 17:13:45 2013 +0100 1.2 +++ b/lf_update_suggestion_order.c Sun Mar 17 20:03:01 2013 +0100 1.3 @@ -25,18 +25,21 @@ 1.4 free(ptr); 1.5 } 1.6 1.7 +// column numbers when querying "individual_suggestion_ranking" view in function main(): 1.8 #define COL_MEMBER_ID 0 1.9 #define COL_WEIGHT 1 1.10 #define COL_PREFERENCE 2 1.11 #define COL_SUGGESTION_ID 3 1.12 1.13 +// data structure for a candidate (in this case a suggestion) to the proportional runoff system: 1.14 struct candidate { 1.15 - char *key; 1.16 - double score_per_step; 1.17 - double score; 1.18 - int seat; 1.19 + char *key; // identifier of the candidate, which is the "suggestion_id" string 1.20 + double score_per_step; // added score per step 1.21 + double score; // current score of candidate; a score of 1.0 is needed to survive a round 1.22 + int seat; // equals 0 for unseated candidates, or contains rank number 1.23 }; 1.24 1.25 +// compare two integers stored as strings (invocation like strcmp): 1.26 static int compare_id(char *id1, char *id2) { 1.27 int ldiff; 1.28 ldiff = strlen(id1) - strlen(id2); 1.29 @@ -44,13 +47,16 @@ 1.30 else return strcmp(id1, id2); 1.31 } 1.32 1.33 +// compare two candidates by their key (invocation like strcmp): 1.34 static int compare_candidate(struct candidate *c1, struct candidate *c2) { 1.35 return compare_id(c1->key, c2->key); 1.36 } 1.37 1.38 +// candidates are stored as global variables due to the constrained twalk() interface: 1.39 static int candidate_count; 1.40 static struct candidate *candidates; 1.41 1.42 +// function to be passed to twalk() to store candidates ordered in candidates[] array: 1.43 static void register_candidate(char **candidate_key, VISIT visit, int level) { 1.44 if (visit == postorder || visit == leaf) { 1.45 struct candidate *candidate; 1.46 @@ -60,6 +66,7 @@ 1.47 } 1.48 } 1.49 1.50 +// performs a binary search in candidates[] array to lookup a candidate by its key (which is the suggestion_id): 1.51 static struct candidate *candidate_by_key(char *candidate_key) { 1.52 struct candidate *candidate; 1.53 struct candidate compare; 1.54 @@ -72,29 +79,36 @@ 1.55 return candidate; 1.56 } 1.57 1.58 +// section of a ballot with equally ranked candidates: 1.59 struct ballot_section { 1.60 int count; 1.61 struct candidate **candidates; 1.62 }; 1.63 1.64 +// ballot of the proportional runoff system: 1.65 struct ballot { 1.66 - int weight; 1.67 - struct ballot_section sections[4]; 1.68 + int weight; // if weight is greater than 1, then the ballot is counted multiple times 1.69 + struct ballot_section sections[4]; // 4 sections, most preferred candidates first 1.70 }; 1.71 1.72 +// determine candidate, which is assigned the next seat (starting with the worst rank): 1.73 static struct candidate *loser(int round_number, struct ballot *ballots, int ballot_count) { 1.74 - int i, j, k; 1.75 - int remaining; 1.76 + int i, j, k; // index variables for loops 1.77 + int remaining; // remaining candidates to be seated 1.78 + // reset scores of all candidates: 1.79 for (i=0; i<candidate_count; i++) { 1.80 candidates[i].score = 0.0; 1.81 } 1.82 + // calculate remaining candidates to be seated: 1.83 remaining = candidate_count - round_number; 1.84 - while (1) { 1.85 - double scale; 1.86 - if (remaining <= 1) break; 1.87 + // repeat following loop, if there is more than one remaining candidate: 1.88 + if (remaining > 1) while (1) { 1.89 + double scale; // factor to be later multiplied with score_per_step: 1.90 + // reset score_per_step for all candidates: 1.91 for (i=0; i<candidate_count; i++) { 1.92 candidates[i].score_per_step = 0.0; 1.93 } 1.94 + // calculate score_per_step for all candidates: 1.95 for (i=0; i<ballot_count; i++) { 1.96 for (j=0; j<4; j++) { 1.97 int matches = 0; 1.98 @@ -117,7 +131,8 @@ 1.99 } 1.100 } 1.101 } 1.102 - scale = (double)0.0; 1.103 + // calculate scale factor: 1.104 + scale = (double)0.0; // 0.0 is used to indicate that there is no value yet 1.105 for (i=0; i<candidate_count; i++) { 1.106 double max_scale; 1.107 if (candidates[i].score_per_step > 0.0) { 1.108 @@ -127,28 +142,34 @@ 1.109 } 1.110 } 1.111 } 1.112 + // add scale*score_per_step to each candidates score: 1.113 for (i=0; i<candidate_count; i++) { 1.114 if (candidates[i].score_per_step > 0.0) { 1.115 double max_scale; 1.116 max_scale = (1.0-candidates[i].score) / candidates[i].score_per_step; 1.117 if (max_scale == scale) { 1.118 + // score of 1.0 should be reached, so we set score directly to avoid floating point errors: 1.119 candidates[i].score = 1.0; 1.120 remaining--; 1.121 } else { 1.122 candidates[i].score += scale * candidates[i].score_per_step; 1.123 if (candidates[i].score >= 1.0) remaining--; 1.124 } 1.125 + // when there is only one candidate remaining, then break loop: 1.126 if (remaining <= 1) break; 1.127 } 1.128 } 1.129 } 1.130 - for (i=candidate_count-1; i>=0; i--) { 1.131 + // return remaining candidate: 1.132 + for (i=0; i<candidate_count; i++) { 1.133 if (candidates[i].score < 1.0 && !candidates[i].seat) return candidates+i; 1.134 } 1.135 + // if there is no remaining candidate, then something went wrong: 1.136 fprintf(stderr, "No remaining candidate (should not happen)."); 1.137 abort(); 1.138 } 1.139 1.140 +// write results to database: 1.141 static int write_ranks(PGconn *db, char *escaped_initiative_id, int final) { 1.142 PGresult *res; 1.143 char *cmd; 1.144 @@ -226,19 +247,24 @@ 1.145 } 1.146 } 1.147 1.148 +// calculate ordering of suggestions for an initiative and call write_ranks() to write it to database: 1.149 static int process_initiative(PGconn *db, PGresult *res, char *escaped_initiative_id, int final) { 1.150 - int err; 1.151 - int ballot_count = 1; 1.152 - struct ballot *ballots; 1.153 - int i; 1.154 + int err; // variable to store an error condition (0 = success) 1.155 + int ballot_count = 1; // number of ballots, must be initiatized to 1, due to loop below 1.156 + struct ballot *ballots; // data structure containing the ballots 1.157 + int i; // index variable for loops 1.158 + // create candidates[] and ballots[] arrays: 1.159 { 1.160 - void *candidate_tree = NULL; 1.161 - int tuple_count; 1.162 - char *old_member_id = NULL; 1.163 - struct ballot *ballot; 1.164 - int candidates_in_sections[4] = {0, }; 1.165 + void *candidate_tree = NULL; // temporary structure to create a sorted unique list of all candidate keys 1.166 + int tuple_count; // number of tuples returned from the database 1.167 + char *old_member_id = NULL; // old member_id to be able to detect a new ballot in loops 1.168 + struct ballot *ballot; // pointer to current ballot 1.169 + int candidates_in_sections[4] = {0, }; // number of candidates that have been placed in each section 1.170 + // reset candidate count: 1.171 candidate_count = 0; 1.172 + // determine number of tuples: 1.173 tuple_count = PQntuples(res); 1.174 + // trivial case, when there are no tuples: 1.175 if (!tuple_count) { 1.176 if (final) { 1.177 printf("No suggestions found, but marking initiative as finally calculated.\n"); 1.178 @@ -250,6 +276,7 @@ 1.179 return 0; 1.180 } 1.181 } 1.182 + // calculate ballot_count and generate set of candidate keys (suggestion_id is used as key): 1.183 for (i=0; i<tuple_count; i++) { 1.184 char *member_id, *suggestion_id; 1.185 member_id = PQgetvalue(res, i, COL_MEMBER_ID); 1.186 @@ -264,21 +291,28 @@ 1.187 if (old_member_id && strcmp(old_member_id, member_id)) ballot_count++; 1.188 old_member_id = member_id; 1.189 } 1.190 + // print candidate count: 1.191 printf("Candidate count: %i\n", candidate_count); 1.192 + // allocate memory for candidates[] array: 1.193 candidates = malloc(candidate_count * sizeof(struct candidate)); 1.194 if (!candidates) { 1.195 fprintf(stderr, "Insufficient memory while creating candidate list.\n"); 1.196 abort(); 1.197 } 1.198 - candidate_count = 0; 1.199 + // transform tree of candidate keys into sorted array: 1.200 + candidate_count = 0; // needed by register_candidate() 1.201 twalk(candidate_tree, (void *)register_candidate); 1.202 + // free memory of tree structure (tdestroy() is not available on all platforms): 1.203 while (candidate_tree) tdelete(*(void **)candidate_tree, &candidate_tree, (void *)compare_id); 1.204 + // print ballot count: 1.205 printf("Ballot count: %i\n", ballot_count); 1.206 + // allocate memory for ballots[] array: 1.207 ballots = calloc(ballot_count, sizeof(struct ballot)); 1.208 if (!ballots) { 1.209 fprintf(stderr, "Insufficient memory while creating ballot list.\n"); 1.210 abort(); 1.211 } 1.212 + // set ballot weights, determine ballot section sizes, and verify preference values: 1.213 ballot = ballots; 1.214 old_member_id = NULL; 1.215 for (i=0; i<tuple_count; i++) { 1.216 @@ -305,6 +339,7 @@ 1.217 ballot->sections[preference].count++; 1.218 old_member_id = member_id; 1.219 } 1.220 + // allocate memory for ballot sections: 1.221 for (i=0; i<ballot_count; i++) { 1.222 int j; 1.223 for (j=0; j<4; j++) { 1.224 @@ -317,6 +352,7 @@ 1.225 } 1.226 } 1.227 } 1.228 + // fill ballot sections with candidate references: 1.229 old_member_id = NULL; 1.230 ballot = ballots; 1.231 for (i=0; i<tuple_count; i++) { 1.232 @@ -338,12 +374,14 @@ 1.233 } 1.234 } 1.235 1.236 + // calculate ranks based on constructed data structures: 1.237 for (i=0; i<candidate_count; i++) { 1.238 struct candidate *candidate = loser(i, ballots, ballot_count); 1.239 candidate->seat = candidate_count - i; 1.240 printf("Assigning rank #%i to suggestion #%s.\n", candidate_count - i, candidate->key); 1.241 } 1.242 1.243 + // free ballots[] array: 1.244 for (i=0; i<ballot_count; i++) { 1.245 int j; 1.246 for (j=0; j<4; j++) { 1.247 @@ -354,6 +392,7 @@ 1.248 } 1.249 free(ballots); 1.250 1.251 + // write results to database: 1.252 if (final) { 1.253 printf("Writing final ranks to database.\n"); 1.254 } else { 1.255 @@ -362,8 +401,10 @@ 1.256 err = write_ranks(db, escaped_initiative_id, final); 1.257 printf("Done.\n"); 1.258 1.259 + // free candidates[] array: 1.260 free(candidates); 1.261 1.262 + // return error code of write_ranks() call 1.263 return err; 1.264 } 1.265