summaryrefslogtreecommitdiff
path: root/histogram.cpp
blob: a12cd75e2813c9b5f8ac3f68ecd2461bde62c105 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <iostream>
#include <iomanip>
#include <vector>
#include <cmath>
#include <cassert>

using namespace std;

const char *argv0;

void usage(){
	cerr<<"Usage: "<<argv0<<" [<lowbound> <highbound>] [<nbins>]"<<endl
	    <<"Prints a simple histogram of stdin data in your terminal, using"<<endl
	    <<"either the given lower and upper bounds, or the minimum and"<<endl
	    <<"maximum extracted from the data."<<endl
	    <<"The number of bins can also be passed."<<endl
	    <<"Data on stdin is assumed to be a list of floating-point values."<<endl;
}

double parsefloat(const char *s,const char *errprefix){
	char *endp;
	double v=strtod(s,&endp);
	if(!s[0]||*endp){
		cerr<<errprefix<<" '"<<s<<"'"<<endl;
		usage();
		exit(1);
	}
	return v;
}

int parseint(const char *s,const char *errprefix){
	char *endp;
	int v=strtol(s,&endp,10);
	if(!s[0]||*endp){
		cerr<<errprefix<<" '"<<s<<"'"<<endl;
		usage();
		exit(1);
	}
	return v;
}

int main(int argc,char **argv){
	argv0=argv[0];

	const int BARWIDTH=80;

	double low,high;
	bool havebounds;
	int nbins=10;

	if(argc==1){
		havebounds=false;
	} else if(argc==2){
		havebounds=false;
		nbins=parseint(argv[1],"Invalid number");
	} else if(argc==3){
		havebounds=true;
		low=parsefloat(argv[1],"Invalid number");
		high=parsefloat(argv[2],"Invalid number");
	} else if(argc==4){
		havebounds=true;
		low=parsefloat(argv[1],"Invalid number");
		high=parsefloat(argv[2],"Invalid number");
		nbins=parseint(argv[3],"Invalid number");
	} else {
		usage();
		return 1;
	}

	if(nbins<1){
		cerr<<"Invalid number of bins '"<<nbins<<"'"<<endl;
		usage();
		return 1;
	}

	double minval=INFINITY,maxval=-INFINITY;
	vector<double> values;
	while(true){
		double v;
		cin>>v;
		if(!cin)break;
		values.push_back(v);
		if(v<minval)minval=v;
		if(v>maxval)maxval=v;
	}
	if(!havebounds){
		low=minval;
		high=maxval;
	}

	sort(values.begin(),values.end());

	vector<int> histogram(nbins);

	int binidx=0,tally=0,maxtally=-1;
	for(double v : values){
		if(v<low||v>high){
			// cerr<<"Point "<<v<<" out of range!"<<endl;
			continue;
		}
		int bin=(v-low)/(high-low)*nbins;
		if(bin==nbins)bin--;
		assert(bin>=0&&bin<nbins);
		if(bin!=binidx){
			histogram[binidx]=tally;
			if(tally>maxtally)maxtally=tally;
			binidx=bin;
			tally=0;
		}
		tally++;
	}
	histogram[binidx]=tally;
	if(tally>maxtally)maxtally=tally;

	char fullbar[BARWIDTH+1];
	memset(fullbar,'#',BARWIDTH);
	fullbar[BARWIDTH]='\0';
	char emptybar[BARWIDTH+1];
	memset(emptybar,' ',BARWIDTH);
	emptybar[BARWIDTH]='\0';

	for(int i=0;i<(int)histogram.size();i++){
		int tally=histogram[i];
		int width=BARWIDTH*tally/maxtally;
		cout<<fullbar+BARWIDTH-width<<emptybar+width
		    <<" ("<<setw(11)<<low+(double)i/nbins*(high-low)<<" - "<<setw(11)<<low+(double)(i+1)/nbins*(high-low)<<")"
		    <<" ["<<tally<<"]"<<endl;
	}
}