#include #include #include #include #include #include #include #include static const char *argv0; void usage() { std::cerr << "Usage: " << argv0 << " [-h] [-v] [ ] []" << 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. (If unspecified, defaults" << std::endl << "to 10.)" << std::endl << "Data on stdin is assumed to be a list of floating-point values." << std::endl << std::endl << " -v When passed, prints a histogram with vertical bars. This" << std::endl << " usually fits more bars on a terminal, but is less informative" << std::endl << " since this view omits exact frequency numbers." << std::endl << " -h Show this help." << 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; const int64_t BARHEIGHT = 24; bool vertical = false; double low = 0, high = 1; // will be overwritten either from arguments or from data bool havebounds; int64_t nbins = 10; std::vector plainargs; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-v") == 0) { vertical = true; } else if (strcmp(argv[i], "-h") == 0) { usage(); return 0; } else { plainargs.push_back(argv[i]); } } switch (plainargs.size()) { case 0: havebounds = false; break; case 1: havebounds = false; nbins = parseint(plainargs[0], "Invalid number"); break; case 2: havebounds = true; low = parsefloat(plainargs[0], "Invalid number"); high = parsefloat(plainargs[1], "Invalid number"); break; case 3: havebounds = true; low = parsefloat(plainargs[0], "Invalid number"); high = parsefloat(plainargs[1], "Invalid number"); nbins = parseint(plainargs[2], "Invalid number"); break; default: 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; } // Read data and collect low and high bounds, if necessary std::vector values; { double minval = INFINITY, maxval = -INFINITY; while (true) { double v; std::cin >> v; if (!std::cin) { // Check whether it was invalid data or EOF that let the read fail std::cin.clear(); std::string str; std::cin >> str; if (std::cin) { std::cerr << "histogram: Invalid data starting with word '" << str << "'" << std::endl; usage(); return 1; } else { // EOF, so we're done break; } } values.push_back(v); if (v < minval) minval = v; if (v > maxval) maxval = v; } if (!havebounds) { low = minval; high = maxval; } } if (low == high) { std::cerr << "histogram: Plot range contains only one point: " << low << std::endl; usage(); return 1; } // Collect data in the histogram std::vector histogram(nbins, 0); 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); histogram[bin]++; } // Compute statistics int64_t maxtally = *std::max_element(histogram.begin(), histogram.end()); if (maxtally == 0) { std::cerr << "histogram: No data in range" << std::endl; return 1; } // Print histogram if (vertical) { for (int y = BARHEIGHT; y >= 1; y--) { for (size_t i = 0; i < histogram.size(); i++) { std::cout << " #"[BARHEIGHT * histogram[i] / maxtally >= y]; } std::cout << std::endl; } std::cout << std::left << std::setw(nbins / 2) << low << ' ' << std::right << std::setw(nbins - nbins / 2 - 1) << high << std::endl; } else { 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]; double binlow = low + (double)i / nbins * (high - low); double binhigh = low + (double)(i + 1) / nbins * (high - low); int64_t width = BARWIDTH * tally / maxtally; char terminator = ")]"[i == histogram.size() - 1]; std::cout << fullbar + BARWIDTH - width << emptybar + width << " [" << std::setw(11) << binlow << " - " << std::setw(11) << binhigh << terminator << " [" << tally << "]" << std::endl; } } }