#include #include #include #include #include typedef long double real; typedef unsigned long long count_t; #define REALRFMT "%Lf" #define REALPFMT "%.2Lf" #define REALPFMTA "%*.1Lf" #if !defined(__cplusplus) typedef unsigned char bool; #define false 0 #define true 1 #endif // Parameters static count_t file_count = 0; static bool show_help = false; static bool force_stats = false; static bool show_histogram = false; static bool individual_stats = false; static bool all_stats = false; static bool show_version = false; static char unknown_param = 0; // Statistics static count_t count = 0; static real minimum = DBL_MAX; static real maximum = -DBL_MAX; static real value = 0.0; static real sum = 0.0; static real square_sum = 0.0; // Array of data for median, mode and histogram // Histogram #define HISTSIZE 10 static size_t histogram[10] = {0}; static real histogram_break_width = 0.0; void reset_statistics() { count = 0; minimum = DBL_MAX; maximum = -DBL_MAX; value = 0.0; sum = 0.0; square_sum = 0.0; histogram_break_width = 0.0; for ( size_t i = 0; i < HISTSIZE; ++i ) histogram[i] = 0; } int eprintf(int result, const char* fmt, ...) { va_list list; va_start(list, fmt); vfprintf(stderr, fmt, list); va_end(list); return result; } int digits(long long value) { int result = value < 0 ? 2 : 1; for ( value /= 10; value; value /= 10 ) ++result; return result; } int print_help() { printf("%s", "Usage: stats [-afhiV] [FILES]\n" "Get statistics such as average and standard deviation from\n" "numeric data stored in FILES. Options:\n" "\n" " -a print all statistics including median and mode\n" " -f force to get statistics from no valid files\n" " -h [N] print a histogram with N breaks\n" " -i print statistics for each individual file\n" " -V print version of this program\n" "\n" "Warning: -a and -h options may require high memory consumption\n"); return 0; } int print_version() { printf("%s", "stats v2.0 [2013-04-12] Jeisson Hidalgo-Cespedes \n" "\n" "stats comes with absolutely no warranty. This is free software, and you are\n" "welcome to redistribute it under Creative Commons Attribution 3.0 Unported\n" "(CC BY 3.0) license: http://creativecommons.org/licenses/by/3.0/\n"); return 0; } void process_params(int argc, char* argv[]) { while ( --argc > 0 ) { char* flags = *++argv; if ( *flags == '-' ) { if ( strcmp(flags, "--help") == 0 ) show_help = true; if ( strcmp(flags, "--version") == 0 ) show_version = true; else for ( ++flags; *flags; ++flags) switch ( *flags ) { case 'a': all_stats = true; break; case 'f': force_stats = true; break; case 'h': show_histogram = true; break; case 'i': individual_stats = true; break; case 'V': show_version = true; break; default: unknown_param = *flags; break; } // switch } // if else ++file_count; } } int process_file(FILE* source, const char* filename, bool build_histogram) { int result = 0; while ( true ) { while ( (result = fscanf(source, REALRFMT, &value) ) > 0 ) { if ( build_histogram ) { histogram_break_width = (maximum - minimum) / HISTSIZE; size_t index = (value - minimum) / histogram_break_width; if ( index >= HISTSIZE ) index = HISTSIZE - 1; ++histogram[index]; } else { ++count; if ( value < minimum ) minimum = value; if ( value > maximum ) maximum = value; sum += value; square_sum += value * value; } } if ( result == EOF ) return 0; if ( force_stats ) fgetc(source); else return eprintf(2, "stats:%s: invalid file format\n", filename); } } void print_histogram() { putchar('\n'); int maximum_digits = digits(maximum) + 2; for ( size_t i = 0; i < HISTSIZE; ++i ) { printf(REALPFMTA " | ", maximum_digits, minimum + i * histogram_break_width); for ( size_t j = 0; j < histogram[i]; ++j ) putchar('*'); putchar('\n'); } } void print_stats() { if ( count > 0 ) { printf("Count: %llu\n", count); printf("Minimum: " REALPFMT "\n", minimum); printf("Maximum: " REALPFMT "\n", maximum); real average = sum / count; printf("Sum: " REALPFMT "\n", sum); printf("Average: " REALPFMT "\n", average); real variance = (count * average * average * -1 + square_sum) / (count - 1); printf("Variance: " REALPFMT "\n", variance); printf("Standard deviation: " REALPFMT "\n", sqrtl(variance)); if ( show_histogram ) print_histogram(); } } int process_filename(const char* filename, bool build_histogram) { if ( *filename == '-' ) return 0; FILE* source = fopen(filename, "r"); if ( ! source ) return eprintf(1, "stats:%s: could not open file\n", filename); if ( individual_stats && ! build_histogram ) reset_statistics(); int result = process_file(source, filename, build_histogram); fclose(source); if ( individual_stats && ( ! show_histogram || build_histogram ) ) { printf("\n%s:\n", filename); print_stats(); } return result; } int main(int argc, char* argv[]) { int result = 0, temp = 0; process_params(argc, argv); if ( show_help ) return print_help(); if ( show_version ) return print_version(); if ( unknown_param ) return eprintf(1, "stats: -%c unknown option\n", unknown_param); if ( show_histogram && file_count == 0 ) return eprintf(3, "stats: -h option available only for files\n"); if ( file_count == 0 ) process_file(stdin, "stdin", false); else while ( --argc > 0) if ( ( temp = process_filename( *++argv, false ) ) != 0 ) result = temp; else if ( show_histogram ) process_filename(*argv, true); if ( ! individual_stats || file_count == 0 ) print_stats(); return result; }