diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 24 | ||||
-rw-r--r-- | histogram.cpp | 129 |
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; + } +} |