/* reads in stdin and computes a stem and leaf plot of the input. * * Copyright 2007, 2008, 2009 W. Trevor King * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see * or . */ #include /* assert() ;) */ #include /* exit(), malloc(), realloc(), free() */ #include /* */ #include /* printf(), fscanf() */ #include /* getopt() */ #define VERSION "0.2" #define BLOCK_SIZE 100 enum bin_pos_enum { LOWER, CENTER, UPPER }; static const char options[] = "b:H:v:lcum:h"; static const char *func_name = NULL; static const char *description = "reads ascii data from stdin (fscanf %lg)\nprints a stem and leaf plot of the data\n"; static double bin_width = 2.0; static int head_width = 5; static int val_width = 3; static enum bin_pos_enum bin_position = LOWER; static double start_multiple = -1.0; static void help() { printf("usage: %s [-%s]\n", func_name, options); puts(description); printf("Version %s\n\n", VERSION); puts("Options:"); printf("-b #\tBin width of histogram, currently %d\n", bin_width); printf("-H #\tHeader width in characters, currently %d\n", head_width); printf("-v #\tValue width in characters, currently %d. Use a value < 0 to\n\tprint a count, rather than the list of leaves.\n", val_width); printf("-l\tPrint lower edge of bin in header (the default)\n"); printf("-c\tPrint center of bin in header\n"); printf("-u\tPrint upper edge of bin in header\n"); printf("-m #\tSet the lower edge of the first bin as a multiple of #. Use\n\ta value <= 0 to set the lower edge to match the smallest\n\tdata value (the default).\n"); printf("Try 'echo \"1 2 3 1 2 3 1 2 1\" | stem_leaf' and play around...\n"); exit(0); } /* Get a positive integer from a string, with some error checking */ static int get_pos_int(char *string) { int ret; ret = atoi(optarg); if (ret <= 0) { fprintf(stderr, "Invalid argument '%s', must be positive integer\n", string); exit(1); } return ret; } static double get_pos_double(char *string) { double ret; ret = atof(optarg); if (ret <= 0) { fprintf(stderr, "ret = %g\n", ret); fprintf(stderr, "Invalid argument '%s', must be positive double\n", string); exit(1); } return ret; } static int get_int(char *string) { return atoi(optarg); } static int getargs(int argc, char **argv) { int c; func_name = argv[0]; while ((c=getopt(argc, argv, options)) != -1) { switch (c) { case 'b': bin_width = get_pos_double(optarg); break; case 'H': head_width = get_int(optarg); break; case 'v': val_width = get_int(optarg); break; case 'l': bin_position = LOWER; break; case 'c': bin_position = CENTER; break; case 'u': bin_position = UPPER; break; case 'm': start_multiple = get_pos_double(optarg); break; case 'h': default: help(); break; } } return 0; } int main(int argc, char **argv) { double *x=NULL, max, min, bin_bottom; int i, n=0, alloc=0, count; char *tail=NULL; FILE *fid=NULL; getargs(argc, argv); /* I may add a file commandline arg someday. * Currently `cat data | stem_leaf` works fine. */ fid = stdin; /* read in data */ alloc += BLOCK_SIZE; assert( (x = malloc(sizeof(*x)*alloc)) != NULL ); while(fscanf(fid, "%lg", x + n++) == 1) { if (n==alloc) { alloc += BLOCK_SIZE; assert( (x = realloc(x, sizeof(*x)*alloc)) != NULL ); } } n--; /* last read failed, so we don't need it's memory */ /* If you're really picky, you could free the extra memory here. */ /* sanity checks in case you doubt the file readin. */ //printf("%d ELEMENTS READ:\n", n+1); //for (i=0; i max) max = x[i]; if (x[i] < min) min = x[i]; } //printf("max = %f, min = %f\n", max, min); if (start_multiple > 0) { /* sink the bottom of the first bin to the nearest multiple */ bin_bottom = ((int)(min/start_multiple))*start_multiple; if (min < 0) bin_bottom -= start_multiple; } else { bin_bottom = min; } /* Starting at the bottom, * sweep through the data and print anything in the current bin. */ while (bin_bottom <= max) { if (head_width > 0) { switch (bin_position) { case LOWER: printf("%*g : ", head_width, bin_bottom); break; case CENTER: printf("%*g : ", head_width, bin_bottom+bin_width/2.0); break; case UPPER: printf("%*g : ", head_width, bin_bottom+bin_width); break; default: fprintf(stderr, "Impossible bin_position %d", bin_position); exit(1); } } count = 0; for (i=0; i= bin_bottom) && (x[i] < bin_bottom+bin_width)) { count++; if (val_width > 0) printf("%*g", val_width, x[i]); } } if (val_width <= 0) printf("%d", count); printf("\n"); bin_bottom += bin_width; /* on to the next bin... */ } /* clean up */ free(x); return 0; }