/* 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;
}