/* * */ #include "cdf.h" /* * Global configuration variables. These are set based on command-line * arguments, and read-only beyond that point. */ static bool ARG_REVERSE_CDF = false; static bool ARG_FP_INPUT = false; static bool ARG_UINT_INPUT = false; static bool ARG_HELP = false; static int parse_options(int argc, char *const *argv) { int arg_index = 0; int arg; bool error = false; while ((arg = getopt(argc, argv, "frhu")) != -1 && !error) { switch (arg) { case 'f': ARG_FP_INPUT = true; break; case 'r': ARG_REVERSE_CDF = true; break; case 'u': ARG_UINT_INPUT = true; break; case 'h': ARG_HELP = true; break; case '?': if (isprint(optopt)) { fprintf(stderr, "Error: unknown option `-%c`.\n", optopt); } else { fprintf(stderr, "Error: unknown option character `\\x%x`.\n", optopt); } error = true; break; default: error = true; break; } } if (ARG_UINT_INPUT && ARG_FP_INPUT) { fprintf(stderr, "Error: the -u and -f flags are mutually exclusive.\n"); error = true; } if (!error) { arg_index = optind; } return arg_index; } static void help() { fprintf(stderr, "Usage:\ncdf [-f|-u] [-r] [filename]\n"); } static DistRecord *expand_array(DistRecord *records, size_t *capacity) { (*capacity) *= 2; DistRecord *new = realloc(records, (*capacity * sizeof(DistRecord))); if (!new) { fprintf(stderr, "ERROR: Memory allocation failed\n"); return nullptr; } return new; } static int parse_line(DistRecord *record, char *line) { if (ARG_FP_INPUT) { return sscanf(line, "%lld %lf", &record->count, &record->data.d); } else if (ARG_UINT_INPUT) { return sscanf(line, "%lld %llu", &record->count, &record->data.u); } else { return sscanf(line, "%lld %lld", &record->count, &record->data.i); } } static ssize_t read_data(DistRecord **records, size_t capacity, FILE *file) { size_t reccnt = 0; char *line = nullptr; size_t line_len = 0; while (getline(&line, &line_len, file) != -1) { if (parse_line(*records + reccnt, line) != 2) { fprintf(stderr, "[W] Skipping invalid input line: %s\n", line); continue; } reccnt++; if (reccnt == capacity) { if (!(*records = expand_array(*records, &capacity))) { return -1; } } } return reccnt; } static void print_record(long double freq, DistRecord *record) { if (ARG_FP_INPUT) { fprintf(stdout, "%.15Lf\t%lf\n", freq, record->data.d); } else if (ARG_UINT_INPUT) { fprintf(stdout, "%.15Lf\t%llu\n", freq, record->data.u); } else { fprintf(stdout, "%.15Lf\t%lld\n", freq, record->data.i); } } static int print_data(DistRecord *records, long double *freqs, size_t cnt) { if (ARG_REVERSE_CDF) { long double total_freq = 1.0; for (size_t i = 0; i < cnt; i++) { print_record(total_freq, records + i); total_freq -= freqs[i]; } } else { for (size_t i = 0; i < cnt; i++) { print_record(freqs[i], records + i); } } return 1; } static int process_data(FILE *file) { int rc = 1; size_t reccap = 100; DistRecord *records = malloc(reccap * sizeof(DistRecord)); if (!records) { rc = 0; goto process_data_end; } ssize_t cnt = read_data(&records, reccap, file); /* propogate the error */ if (cnt == -1) { rc = 0; goto free_records; } /* calculate total sum of counts */ uint64_t total_count = 0; for (size_t i = 0; i < cnt; i++) { total_count += records[i].count; } /* calculate relative frequency for each item */ long double *freqs = malloc(sizeof(long double) * cnt); if (!freqs) { fprintf(stderr, "ERROR: memory allocation failure.\n"); goto free_freqs; } for (size_t i = 0; i < cnt; i++) { freqs[i] = (long double)(records[i].count) / (long double)(total_count); } rc = print_data(records, freqs, cnt); free_freqs: free(freqs); free_records: free(records); process_data_end: return rc; } int main(int argc, char **argv) { int rc = EXIT_SUCCESS; int file_index = 0; if (!(file_index = parse_options(argc, argv))) { help(); rc = EXIT_FAILURE; goto program_exit; } /* if the -h argument is supplied, print usage and exit successfully */ if (ARG_HELP) { help(); goto program_exit; } /* open the input file, if one is specified, otherwise default to stdin */ FILE *input_file; if (file_index < argc && strcmp(argv[file_index], "-") != 0) { if (!(input_file = fopen(argv[file_index], "r"))) { fprintf(stderr, "Error: Unable to open input file %s\n", argv[file_index]); rc = EXIT_FAILURE; goto program_exit; } } else { input_file = stdin; } if (!process_data(input_file)) { rc = EXIT_FAILURE; } close_file: if (input_file != stdin) { fclose(input_file); } program_exit: exit(rc); }