#include #include #include #include #include #include #include #include static const char *argv0; void usage() { std::cerr << "Usage: " << argv0 << " [ ] []" << std::endl << "Prints a simple histogram of stdin data in your terminal, using" << std::endl << "either the given lower and upper bounds, or the minimum and" << std::endl << "maximum extracted from the data." << std::endl << "The number of bins can also be passed." << std::endl << "Data on stdin is assumed to be a list of floating-point values." << std::endl; } double parsefloat(const char *s, const char *errprefix) { char *endp; double v = strtod(s, &endp); if (!s[0] || *endp) { std::cerr << errprefix << " '" << s << "'" << std::endl; usage(); exit(1); } return v; } int64_t parseint(const char *s, const char *errprefix) { char *endp; int64_t v = strtoll(s, &endp, 10); if (!s[0] || *endp) { std::cerr << errprefix << " '" << s << "'" << std::endl; usage(); exit(1); } return v; } int main(int argc, char **argv) { argv0 = argv[0]; const int64_t BARWIDTH = 80; double low, high; bool havebounds; int64_t 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) { std::cerr << "histogram: Invalid number of bins '" << nbins << "'" << std::endl; usage(); return 1; } if (havebounds && high <= low) { std::cerr << "histogram: high <= low" << std::endl; usage(); return 1; } double minval = INFINITY, maxval = -INFINITY; std::vector values; while (true) { double v; std::cin >> v; if (!std::cin) break; values.push_back(v); if (v < minval) minval = v; if (v > maxval) maxval = v; } if (!havebounds) { low = minval; high = maxval; } std::sort(values.begin(), values.end()); std::vector histogram(nbins); int64_t binidx = 0, tally = 0, maxtally = -1; for (double v : values) { if (v < low || v > high) { // cerr << "Point " << v << " out of range!" << endl; continue; } int64_t 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; if (maxtally == 0) { std::cerr << "histogram: No data in range" << std::endl; return 1; } char fullbar[BARWIDTH + 1]; memset(fullbar, '#', BARWIDTH); fullbar[BARWIDTH] = '\0'; char emptybar[BARWIDTH + 1]; memset(emptybar, ' ', BARWIDTH); emptybar[BARWIDTH] = '\0'; for (size_t i = 0; i < histogram.size(); i++) { int64_t tally = histogram[i]; int64_t width = BARWIDTH * tally / maxtally; char terminator = ")]"[i == histogram.size() - 1]; std::cout << fullbar + BARWIDTH - width << emptybar + width << " [" << std::setw(11) << low + (double)i / nbins * (high - low) << " - " << std::setw(11) << low + (double)(i + 1) / nbins * (high - low) << terminator << " [" << tally << "]" << std::endl; } }