Mercurial > repos > nick > duplex
comparison consensus.c @ 18:e4d75f9efb90 draft
planemo upload commit b'4303231da9e48b2719b4429a29b72421d24310f4\n'-dirty
author | nick |
---|---|
date | Thu, 02 Feb 2017 18:44:31 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
17:836fa4fe9494 | 18:e4d75f9efb90 |
---|---|
1 #include <stdio.h> | |
2 #include <stdlib.h> | |
3 #include <string.h> | |
4 #include <ctype.h> | |
5 #include <limits.h> | |
6 | |
7 // N.B. This defines the valid bases, but it's also effectively defined in the switches in | |
8 // get_votes_simple(), get_votes_qual(), and get_base_prime(), and in the constant IUPAC_BASES. | |
9 #define N_BASES 6 | |
10 const char *BASES = "ACGTN-"; | |
11 /* A C G T N - A: 2 Compute IUPAC ambiguous base character by representing each base | |
12 A 4 6 10 14 22 26 C: 3 with a prime and multiplying. Then use a lookup table (an array | |
13 C 9 15 21 33 39 G: 5 where the index is the product of the two primes). | |
14 G 25 35 55 65 T: 7 | |
15 T 49 77 91 N: 11 | |
16 N 121 143 -: 13 1 2 3 4 5 6 7 | |
17 - 169 01234567890123456789012345678901234567890123456789012345678901234567890*/ | |
18 const char *IUPAC_BASES = "N...A.M..CR...WS.....YN..GN......N.K...N.........T.....N.........N....." | |
19 // 8 9 10 11 12 13 14 | |
20 "......N.............N.............................N..................." | |
21 // 15 16 17 | |
22 "..N.........................-"; | |
23 #define THRES_DEFAULT 0.5 | |
24 #define WIN_LEN 4 | |
25 #define GAP_CHAR ' ' | |
26 | |
27 int **get_votes_simple(char *align[], int n_seqs, int seq_len); | |
28 int **get_votes_qual(char *align[], char *quals[], int n_seqs, int seq_len, char thres); | |
29 int init_gap_qual_window(int *window, char *quals, int seq_len); | |
30 char get_gap_qual(int *window); | |
31 int push_qual(int *window, int win_edge, char *quals, int seq_len); | |
32 void print_window(int *window, int win_edge); | |
33 int **init_votes(int seq_len); | |
34 void free_votes(int *votes[], int seq_len); | |
35 void print_votes(char *consensus, int *votes[], int seq_len); | |
36 char *rm_gaps(char *consensus, int cons_len); | |
37 char *build_consensus(int *votes[], int seq_len, double thres); | |
38 char *build_consensus_duplex(int *votes1[], int *votes2[], int seq_len, double thres); | |
39 char *build_consensus_duplex_simple(char *cons1, char *cons2, int gapped); | |
40 int get_base_prime(char base); | |
41 char *get_consensus(char *align[], char *quals[], int n_seqs, int seq_len, double thres, | |
42 char qual_thres, int gapped); | |
43 char *get_consensus_duplex(char *align1[], char *align2[], char *quals1[], char *quals2[], | |
44 int n_seqs1, int n_seqs2, int seq_len, double cons_thres, | |
45 char qual_thres, int gapped, char *method); | |
46 | |
47 | |
48 // Tally the different bases at each position in an alignment. | |
49 // Returns an array of arrays: for each position in the alignment, an array of the number of times | |
50 // each base occurs at that position. The order of bases is as in the "BASES" constant. | |
51 int **get_votes_simple(char *align[], int n_seqs, int seq_len) { | |
52 int **votes = init_votes(seq_len); | |
53 | |
54 // Tally votes for each base. | |
55 int i, j; | |
56 for (i = 0; i < n_seqs; i++) { | |
57 for (j = 0; j < seq_len; j++) { | |
58 // N.B.: Could write this without hardcoded literals, but it's about 40% slower. | |
59 switch (toupper(align[i][j])) { | |
60 case 'A': | |
61 votes[j][0]++; | |
62 break; | |
63 case 'C': | |
64 votes[j][1]++; | |
65 break; | |
66 case 'G': | |
67 votes[j][2]++; | |
68 break; | |
69 case 'T': | |
70 votes[j][3]++; | |
71 break; | |
72 case 'N': | |
73 votes[j][4]++; | |
74 break; | |
75 case '-': | |
76 votes[j][5]++; | |
77 break; | |
78 } | |
79 } | |
80 } | |
81 | |
82 return votes; | |
83 } | |
84 | |
85 | |
86 // Tally votes for each base, ignoring bases with a quality score below "thres". | |
87 int **get_votes_qual(char *align[], char *quals[], int n_seqs, int seq_len, char thres) { | |
88 int **votes = init_votes(seq_len); | |
89 int *window = malloc(sizeof(int) * WIN_LEN * 2); | |
90 int win_edge; | |
91 | |
92 // Tally votes for each base. | |
93 char qual; | |
94 int i, j; | |
95 for (i = 0; i < n_seqs; i++) { | |
96 win_edge = init_gap_qual_window(window, quals[i], seq_len); | |
97 for (j = 0; j < seq_len; j++) { | |
98 // Figure out the quality score of the base (or gap). | |
99 if (align[i][j] == '-') { | |
100 qual = get_gap_qual(window); | |
101 } else { | |
102 win_edge = push_qual(window, win_edge, quals[i], seq_len); | |
103 qual = quals[i][j]; | |
104 } | |
105 // Don't count bases whose quality is less than the threshold. | |
106 if (qual < thres) { | |
107 continue; | |
108 } | |
109 // N.B.: Could write this without hardcoded literals, but it's about 40% slower. | |
110 switch (toupper(align[i][j])) { | |
111 case 'A': | |
112 votes[j][0]++; | |
113 break; | |
114 case 'C': | |
115 votes[j][1]++; | |
116 break; | |
117 case 'G': | |
118 votes[j][2]++; | |
119 break; | |
120 case 'T': | |
121 votes[j][3]++; | |
122 break; | |
123 case 'N': | |
124 votes[j][4]++; | |
125 break; | |
126 case '-': | |
127 votes[j][5]++; | |
128 break; | |
129 } | |
130 } | |
131 } | |
132 | |
133 free(window); | |
134 return votes; | |
135 } | |
136 | |
137 | |
138 /* Tally votes for each base, weighting by the PHRED score of the base. | |
139 * This is based on the theory of PHRED scores representing the literal probability of the base call | |
140 * being erroneous. Thus, if two reads show a C at a position, both with PHRED 20 (1/100 chance of | |
141 * error), then the chances of them both being wrong are 1/100 * 1/100 = 1/10000 (PHRED 40). | |
142 * So, theoretically and intuitively, it makes sense to trust two PHRED 20 C's over one PHRED 30 A. | |
143 * This seems better than arbitrarily deciding not to consider bases below a PHRED score threshold. | |
144 * How to decide when not to call the base? We could just say that if no base's score total is above | |
145 * a certain threshold, we call it an N. Theoretically, this threshold is the confidence we want in | |
146 * our final base calls. This could even replace the arbitrary 3 reads for a consensus threshold. | |
147 */ | |
148 int **get_votes_weighted(char *align[], char *quals[], int n_seqs, int seq_len) { | |
149 int **votes = init_votes(seq_len); | |
150 int *window = malloc(sizeof(int) * WIN_LEN * 2); | |
151 int win_edge; | |
152 | |
153 // Tally votes for each base. | |
154 char qual; | |
155 int i, j; | |
156 for (i = 0; i < n_seqs; i++) { | |
157 win_edge = init_gap_qual_window(window, quals[i], seq_len); | |
158 for (j = 0; j < seq_len; j++) { | |
159 // Figure out the quality score of the base (or gap). | |
160 if (align[i][j] == '-') { | |
161 qual = get_gap_qual(window); | |
162 } else { | |
163 win_edge = push_qual(window, win_edge, quals[i], seq_len); | |
164 qual = quals[i][j]; | |
165 } | |
166 // N.B.: Could write this without hardcoded literals, but it's about 40% slower. | |
167 switch (toupper(align[i][j])) { | |
168 case 'A': | |
169 votes[j][0] += qual; | |
170 break; | |
171 case 'C': | |
172 votes[j][1] += qual; | |
173 break; | |
174 case 'G': | |
175 votes[j][2] += qual; | |
176 break; | |
177 case 'T': | |
178 votes[j][3] += qual; | |
179 break; | |
180 case 'N': | |
181 votes[j][4] += qual; | |
182 break; | |
183 case '-': | |
184 votes[j][5] += qual; | |
185 break; | |
186 } | |
187 } | |
188 } | |
189 | |
190 free(window); | |
191 return votes; | |
192 } | |
193 | |
194 | |
195 /* Calculation of gap quality scores: | |
196 * "window" is an array of length 2*WIN_LEN, holding the quality scores of the non-gap bases WIN_LEN | |
197 * from the current one in both directions. When we're at the start or end, fill the slots beyond | |
198 * the edge of the sequence with -1 as a sentinel for "N/A". For example, after the 2nd base in the | |
199 * sequence, if WIN_LEN is 4 and there are no gaps yet, "window" should look something like this: | |
200 * base coordinates 0 1 2 3 4 5 | |
201 * quality scores = 9 A > E B | |
202 * array values [-1|-1|28|24||32|29|36|33] | |
203 * Usage: | |
204 * Allocate "window" and call init_gap_qual_window() to initialize it by filling its right side with | |
205 * the first WIN_LEN non-gap quality scores in the sequence. It will also return "win_edge", which | |
206 * tracks the coordinate of the rightmost quality score in "window". | |
207 * Then, start traversing the array of quality scores. For each score, if it's a gap character, call | |
208 * get_gap_qual() to get the computed quality score at the gap. Otherwise, call push_qual() to add | |
209 * another quality score to the end of the window. This will keep the window filled with all non-gap | |
210 * quality scores, and each time you call get_gap_qual(), you should be at the gap between the two | |
211 * quality scores at the center of the window. | |
212 */ | |
213 | |
214 // This does the initial fill of the window array, adding the first WIN_LEN bases to the right side | |
215 // and filling the left side with -1's. | |
216 int init_gap_qual_window(int *window, char *quals, int seq_len) { | |
217 // Fill left side with -1's (no quality information). | |
218 int i; | |
219 for (i = 0; i < WIN_LEN; i++) { | |
220 window[i] = -1; | |
221 } | |
222 // Fill right side with first WIN_LEN quality scores. Skip gaps, and if you run out of quality | |
223 // scores (if seq_len < WIN_LEN), fill the rest with -1. Leave win_edge at the last base we added. | |
224 i = WIN_LEN; | |
225 int win_edge = -1; | |
226 int quals_added = 0; | |
227 while (quals_added < WIN_LEN) { | |
228 win_edge++; | |
229 if (win_edge >= seq_len) { | |
230 win_edge = seq_len; | |
231 window[i] = -1; | |
232 i++; | |
233 quals_added++; | |
234 } else if (quals[win_edge] != GAP_CHAR) { | |
235 window[i] = quals[win_edge]; | |
236 i++; | |
237 quals_added++; | |
238 } | |
239 } | |
240 return win_edge; | |
241 } | |
242 | |
243 | |
244 // Push the next non-gap quality score onto the right side of the window. | |
245 int push_qual(int *window, int win_edge, char *quals, int seq_len) { | |
246 // Find the next quality score that's not a gap. | |
247 char next_qual = GAP_CHAR; | |
248 while (next_qual == GAP_CHAR) { | |
249 win_edge++; | |
250 if (win_edge < seq_len) { | |
251 next_qual = quals[win_edge]; | |
252 } else { | |
253 win_edge = seq_len; | |
254 next_qual = -1; | |
255 } | |
256 } | |
257 // Shift all the quality scores left add the new one. | |
258 int last_qual; | |
259 int i; | |
260 for (i = WIN_LEN*2 - 1; i >= 0; i--) { | |
261 last_qual = window[i]; | |
262 window[i] = next_qual; | |
263 next_qual = last_qual; | |
264 } | |
265 return win_edge; | |
266 } | |
267 | |
268 | |
269 // Compute the quality of the gap based on a weighted average of the quality scores in the window. | |
270 // The scores near the center of the window are weighted higher than the ones further away. | |
271 char get_gap_qual(int *window) { | |
272 int score_sum = 0; | |
273 int weight_sum = 0; | |
274 int weight = 1; | |
275 int i; | |
276 for (i = 0; i < WIN_LEN*2; i++) { | |
277 if (window[i] != -1) { | |
278 score_sum += window[i] * weight; | |
279 weight_sum += weight; | |
280 } | |
281 // Increase the weight until we get to the middle of the window (at WIN_LEN), then decrease it. | |
282 if (i < WIN_LEN - 1) { | |
283 weight++; | |
284 } else if (i > WIN_LEN - 1) { | |
285 weight--; | |
286 } | |
287 } | |
288 if (weight_sum > 0) { | |
289 // Divide by the sum of the weights to get the final quality score. | |
290 return (char) (score_sum/weight_sum); | |
291 } else { | |
292 return '\0'; | |
293 } | |
294 } | |
295 | |
296 | |
297 void print_window(int *window, int win_edge) { | |
298 printf("["); | |
299 int i; | |
300 for (i = 0; i < WIN_LEN*2; i++) { | |
301 printf("%c", window[i]); | |
302 if (i == WIN_LEN - 1) { | |
303 printf("||"); | |
304 } else if (i == WIN_LEN*2 - 1) { | |
305 printf("] %-2d\n", win_edge); | |
306 } else { | |
307 printf("|"); | |
308 } | |
309 } | |
310 } | |
311 | |
312 | |
313 int **init_votes(int seq_len) { | |
314 int **votes = malloc(sizeof(int *) * seq_len); | |
315 int i, j; | |
316 for (i = 0; i < seq_len; i++) { | |
317 votes[i] = malloc(sizeof(int) * N_BASES); | |
318 for (j = 0; j < N_BASES; j++) { | |
319 votes[i][j] = 0; | |
320 } | |
321 } | |
322 return votes; | |
323 } | |
324 | |
325 | |
326 void free_votes(int *votes[], int seq_len) { | |
327 int i; | |
328 for (i = 0; i < seq_len; i++) { | |
329 free(votes[i]); | |
330 } | |
331 free(votes); | |
332 } | |
333 | |
334 | |
335 void print_votes(char *consensus, int *votes[], int seq_len) { | |
336 int i, j; | |
337 printf(" "); | |
338 for (j = 0; j < N_BASES; j++) { | |
339 printf(" %c ", BASES[j]); | |
340 } | |
341 printf("\n"); | |
342 for (i = 0; i < seq_len; i++) { | |
343 printf("%c: ", consensus[i]); | |
344 for (j = 0; j < N_BASES; j++) { | |
345 if (votes[i][j]) { | |
346 printf("%2d ", votes[i][j]); | |
347 } else { | |
348 printf(" "); | |
349 } | |
350 } | |
351 printf("\n"); | |
352 } | |
353 } | |
354 | |
355 | |
356 // Take a consensus sequence which may have gaps ('-' characters) and remove them to produce the | |
357 // actual final sequence. "cons_len" should be the length of the original, gapped, sequence. | |
358 char *rm_gaps(char *consensus, int cons_len) { | |
359 char *output = malloc(sizeof(char) * cons_len + 1); | |
360 int i; | |
361 int j = 0; | |
362 for (i = 0; i < cons_len; i++) { | |
363 if (consensus[i] != '-') { | |
364 output[j] = consensus[i]; | |
365 j++; | |
366 } | |
367 } | |
368 output[j] = '\0'; | |
369 return output; | |
370 } | |
371 | |
372 | |
373 char *build_consensus(int *votes[], int seq_len, double thres) { | |
374 char *consensus = malloc(sizeof(char) * seq_len + 1); | |
375 | |
376 int i, j; | |
377 for (i = 0; i < seq_len; i++) { | |
378 int total = 0; | |
379 int max_vote = 0; | |
380 char max_base = 'N'; | |
381 for (j = 0; j < N_BASES; j++) { | |
382 total += votes[i][j]; | |
383 if (votes[i][j] > max_vote) { | |
384 max_vote = votes[i][j]; | |
385 max_base = BASES[j]; | |
386 } | |
387 if (total == 0) { | |
388 consensus[i] = 'N'; | |
389 } else if ((double)max_vote/total > thres) { | |
390 consensus[i] = max_base; | |
391 } else { | |
392 consensus[i] = 'N'; | |
393 } | |
394 } | |
395 } | |
396 | |
397 consensus[seq_len] = '\0'; | |
398 return consensus; | |
399 } | |
400 | |
401 | |
402 // Build a consensus sequence from two alignments by weighting each equally and considering only | |
403 // the frequency of each base in each alignment. | |
404 char *build_consensus_duplex(int *votes1[], int *votes2[], int seq_len, double thres) { | |
405 char *consensus = malloc(sizeof(char) * seq_len + 1); | |
406 | |
407 int i, j; | |
408 for (i = 0; i < seq_len; i++) { | |
409 // Sum the total votes at this position. | |
410 /*TODO: This does an extra loop through the votes to get the total so it can calculate actual | |
411 * frequencies in the second pass. Technically, this information could be gathered when | |
412 * originally tallying the votes in the get_votes functions. Or, the total could be assumed | |
413 * to be n_seqs if every base always contributes a vote (even when it's not in "ACGTN-"). | |
414 */ | |
415 int total1 = 0; | |
416 for (j = 0; j < N_BASES; j++) { | |
417 total1 += votes1[i][j]; | |
418 } | |
419 int total2 = 0; | |
420 for (j = 0; j < N_BASES; j++) { | |
421 total2 += votes2[i][j]; | |
422 } | |
423 double max_freq = 0.0; | |
424 char max_base = 'N'; | |
425 for (j = 0; j < N_BASES; j++) { | |
426 // Get the frequency of each base. | |
427 double freq1; | |
428 if (total1 > 0) { | |
429 freq1 = (double)votes1[i][j]/total1; | |
430 } | |
431 double freq2; | |
432 if (total2 > 0) { | |
433 freq2 = (double)votes2[i][j]/total2; | |
434 } | |
435 // frequency of the base = average of frequencies in the two sequences | |
436 double avg_freq; | |
437 if (total1 == 0 && total2 == 0) { | |
438 avg_freq = -1.0; | |
439 } else if (total1 == 0) { | |
440 avg_freq = freq2; | |
441 } else if (total2 == 0) { | |
442 avg_freq = freq1; | |
443 } else { | |
444 avg_freq = (freq1 + freq2) / 2; | |
445 } | |
446 // Track the highest frequency seen. | |
447 if (avg_freq > max_freq) { | |
448 max_freq = avg_freq; | |
449 max_base = BASES[j]; | |
450 } | |
451 } | |
452 if (max_freq > thres) { | |
453 consensus[i] = max_base; | |
454 } else { | |
455 consensus[i] = 'N'; | |
456 } | |
457 } | |
458 | |
459 consensus[seq_len] = '\0'; | |
460 return consensus; | |
461 } | |
462 | |
463 | |
464 // "cons1" and "cons2" must be null-terminated strings of equal lengths. | |
465 char *build_consensus_duplex_simple(char *cons1, char *cons2, int gapped) { | |
466 int seq_len = strlen(cons1); | |
467 char *cons = malloc(sizeof(char) * seq_len + 1); | |
468 int i = 0; | |
469 int base_prime1, base_prime2; | |
470 while (cons1[i] != '\0' && cons2[i] != '\0') { | |
471 base_prime1 = get_base_prime(cons1[i]); | |
472 base_prime2 = get_base_prime(cons2[i]); | |
473 cons[i] = IUPAC_BASES[base_prime1*base_prime2]; | |
474 i++; | |
475 } | |
476 cons[seq_len] = '\0'; | |
477 if (gapped) { | |
478 return cons; | |
479 } else { | |
480 return rm_gaps(cons, seq_len); | |
481 } | |
482 } | |
483 | |
484 | |
485 int get_base_prime(char base) { | |
486 switch (base) { | |
487 case 'A': | |
488 return 2; | |
489 case 'C': | |
490 return 3; | |
491 case 'G': | |
492 return 5; | |
493 case 'T': | |
494 return 7; | |
495 case 'N': | |
496 return 11; | |
497 case '-': | |
498 return 13; | |
499 default: | |
500 return 0; | |
501 } | |
502 } | |
503 | |
504 | |
505 // Convenience function to create a consensus in one step. | |
506 // Give 0 as "quals" to not use quality scores, and -1.0 as "cons_thres" to use the default | |
507 // consensus threshold when evaluating base votes. | |
508 char *get_consensus(char *align[], char *quals[], int n_seqs, int seq_len, double cons_thres, | |
509 char qual_thres, int gapped) { | |
510 if (cons_thres == -1.0) { | |
511 cons_thres = THRES_DEFAULT; | |
512 } | |
513 int **votes; | |
514 if (quals == 0) { | |
515 votes = get_votes_simple(align, n_seqs, seq_len); | |
516 } else { | |
517 votes = get_votes_qual(align, quals, n_seqs, seq_len, qual_thres); | |
518 } | |
519 char *consensus_gapped = build_consensus(votes, seq_len, cons_thres); | |
520 char *consensus; | |
521 if (gapped) { | |
522 consensus = consensus_gapped; | |
523 } else { | |
524 consensus = rm_gaps(consensus_gapped, seq_len); | |
525 } | |
526 free_votes(votes, seq_len); | |
527 return consensus; | |
528 } | |
529 | |
530 | |
531 char *get_consensus_duplex(char *align1[], char *align2[], char *quals1[], char *quals2[], | |
532 int n_seqs1, int n_seqs2, int seq_len, double cons_thres, | |
533 char qual_thres, int gapped, char *method) { | |
534 if (cons_thres == -1.0) { | |
535 cons_thres = THRES_DEFAULT; | |
536 } | |
537 int **votes1; | |
538 int **votes2; | |
539 if (quals1 == 0 || quals2 == 0) { | |
540 votes1 = get_votes_simple(align1, n_seqs1, seq_len); | |
541 votes2 = get_votes_simple(align2, n_seqs2, seq_len); | |
542 } else { | |
543 votes1 = get_votes_qual(align1, quals1, n_seqs1, seq_len, qual_thres); | |
544 votes2 = get_votes_qual(align2, quals2, n_seqs2, seq_len, qual_thres); | |
545 } | |
546 char *consensus_gapped; | |
547 if (!strncmp(method, "freq", 4)) { | |
548 consensus_gapped = build_consensus_duplex(votes1, votes2, seq_len, cons_thres); | |
549 } else if (!strncmp(method, "iupac", 5)) { | |
550 char *cons1 = build_consensus(votes1, seq_len, cons_thres); | |
551 char *cons2 = build_consensus(votes2, seq_len, cons_thres); | |
552 consensus_gapped = build_consensus_duplex_simple(cons1, cons2, 1); | |
553 } else { | |
554 return ""; | |
555 } | |
556 char *consensus; | |
557 if (gapped) { | |
558 consensus = consensus_gapped; | |
559 } else { | |
560 consensus = rm_gaps(consensus_gapped, seq_len); | |
561 } | |
562 free_votes(votes1, seq_len); | |
563 free_votes(votes2, seq_len); | |
564 return consensus; | |
565 } | |
566 | |
567 | |
568 void get_gap_quals(char *quals) { | |
569 int seq_len = strlen(quals); | |
570 int *window = malloc(sizeof(int) * WIN_LEN * 2); | |
571 int win_edge = init_gap_qual_window(window, quals, seq_len); | |
572 print_window(window, win_edge); | |
573 | |
574 int i; | |
575 char gap_qual; | |
576 for (i = 0; i < seq_len; i++) { | |
577 if (quals[i] == GAP_CHAR) { | |
578 gap_qual = get_gap_qual(window); | |
579 printf("gap %2d: %2d\n", i, gap_qual); | |
580 } else { | |
581 win_edge = push_qual(window, win_edge, quals, seq_len); | |
582 print_window(window, win_edge); | |
583 } | |
584 } | |
585 } | |
586 | |
587 | |
588 int main(int argc, char *argv[]) { | |
589 char **align = malloc(sizeof(char *) * (argc-1)); | |
590 | |
591 int seq_len = INT_MAX; | |
592 int i; | |
593 for (i = 1; i < argc; i++) { | |
594 if (strlen(argv[i]) < seq_len) { | |
595 seq_len = strlen(argv[i]); | |
596 } | |
597 align[i-1] = argv[i]; | |
598 } | |
599 | |
600 if (argc <= 1) { | |
601 return 1; | |
602 } | |
603 | |
604 get_gap_quals(align[0]); | |
605 return 0; | |
606 | |
607 int **votes = get_votes_simple(align, argc-1, seq_len); | |
608 char *consensus = build_consensus(votes, seq_len, THRES_DEFAULT); | |
609 print_votes(consensus, votes, seq_len); | |
610 printf("%s\n", consensus); | |
611 free_votes(votes, seq_len); | |
612 | |
613 return 0; | |
614 } |