summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile24
-rw-r--r--histogram.cpp129
3 files changed, 156 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1bb99b6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+histogram
+*.o
+*.dSYM
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c3486fa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+CXX = g++
+CXXFLAGS = -Wall -Wextra -std=c++11 -fwrapv
+ifneq ($(DEBUG),)
+ CXXFLAGS += -g
+else
+ CXXFLAGS += -O2
+endif
+BIN = histogram
+
+.PHONY: all clean remake
+
+all: $(BIN)
+
+clean:
+ rm -rf $(BIN) *.o *.dSYM
+
+remake: clean all
+
+
+$(BIN): $(patsubst %.cpp,%.o,$(wildcard *.cpp))
+ $(CXX) -o $@ $^
+
+%.o: %.cpp $(wildcard *.h)
+ $(CXX) $(CXXFLAGS) -c -o $@ $<
diff --git a/histogram.cpp b/histogram.cpp
new file mode 100644
index 0000000..ab6d651
--- /dev/null
+++ b/histogram.cpp
@@ -0,0 +1,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;
+ }
+}