histogram with underlying type of 'long'

A class that dumps a histogram in a bar graph like way to an output stream.

This should really be a template! Right now it takes a 'long' as the type.

see timehistogram for a specialization for minutes in a day.

Download histogram.zip

Synopsis:

histogram.h
histogram.cpp
histogram_test.cpp


histogram.h

Synopsis
#pragma once

#include <ostream>
using std::ostream;

namespace HistogramNS
  {
  class Histogram
    {
    public:
      Histogram(long increment, long start, long end);
      ~Histogram(void);
      void bump(long val);
      void print(std::ostream& os);

    private:
      class Private;
      Private& impl;
    } ;
  }

histogram.cpp

Synopsis
#include "histogram.h"

#include <iostream>
#include <iomanip>
#include <string>
#include <list>
using namespace std;

namespace HistogramNS
  {
  const unsigned long cMaxStars = 50;
  class Slot
    {
    public:
      Slot(long start, long end) 
        : mCount(0), mStart(start), mEnd(end)
        {
        }
      Slot() : mCount(0), mStart(0), mEnd(0)
        {
        }
      bool isEmpty()
        {
        return mCount == 0;
        }
      virtual void bump()
        {
        mCount++;
        }
      bool contains(long val)
        {
        return val >= mStart && val <= mEnd;
        }
      unsigned long getGreater(unsigned long max)
        {
        return max > mCount ? max : mCount;
        }
      void print(ostream& os, unsigned long maxcount)
        {
        if (isEmpty()) return;

        printPrefix(os);
        os << string(CalcNumStars(maxcount), '*') << endl;
        }
      int CalcNumStars(unsigned long maxcount)
        {
        return ((mCount * cMaxStars * 10) + 5) / (10 * maxcount);
        }
      virtual void printPrefix(ostream& os)
        {
        os << setw(4) << mStart 
          << " - " 
          << setw(4) << mEnd
          << setw(6) << mCount
          << " : ";
        }
      bool operator < (const Slot& other)
        {
        return mStart < other.mStart;
        }
    protected:
      unsigned long mCount;
      long mStart;
      long mEnd;
    } ;

  class OutOfBoundsSlot : public Slot
    {
    public:
      void printPrefix(ostream& os)
        {
        os << "   outside "
          << setw(6) << mCount
          << " : ";
        }
    } ;

  class Slots
    {
    public:
      Slots(long increment, long start, long end)
        : mInc(increment), mRangeStart(start), mRangeEnd(end), mMaxCount(0)
        {
        }
      void bump(long val)
        {
        bump(inRange(val) ? &(*findSlot(val)) : &mOutOfBounds);
        }
      void print(std::ostream& os)
        {
        mSlots.sort();
        for(SlotListIterator it = mSlots.begin(); it != mSlots.end(); ++it)
          (*it).print(os, mMaxCount);
        mOutOfBounds.print(os, mMaxCount);
        }
    private:
      typedef list<Slot> SlotList;
      typedef SlotList::iterator SlotListIterator;

      void bump(Slot* s)
        {
          s->bump();
          mMaxCount = s->getGreater(mMaxCount);
        }
      bool inRange(long val)
        {
        return val >= mRangeStart && val < mRangeEnd;
        }
      SlotListIterator findSlot(long val)
        {
        SlotList::iterator it;
        for(it = mSlots.begin(); it != mSlots.end(); ++it)
          {
          if ((*it).contains(val)) break;
          }
        return it == mSlots.end() ? CreateNewSlot(val) : it;
        }
      SlotListIterator CreateNewSlot(long val)
        {
        long start = (val / mInc) * mInc;
        Slot s(start, start + mInc - 1);
        SlotListIterator it = mSlots.insert(mSlots.begin(), s);
        return it;
        }

      unsigned long mMaxCount;
      long mInc;
      long mRangeStart;
      long mRangeEnd;
      OutOfBoundsSlot mOutOfBounds;
      SlotList mSlots;
    };

  class Histogram::Private
    {
    public:
      Private(long increment, long start, long end)
        : slots(increment, start, end)
        {
        }
      Slots slots;
    } ;

  Histogram::Histogram(long increment, long start, long end)
    : impl(* new Private(increment, start, end))
    {
    }

  Histogram::~Histogram(void)
    {
    delete &impl;
    }
  void Histogram::bump(long val)
    {
    impl.slots.bump(val);
    }
  void Histogram::print(std::ostream& os)
    {
    impl.slots.print(os);
    }
  }

histogram_test.cpp

Synopsis
#include <strstream>

#include "utx.h"

#include "histogram.h"
using namespace std;
using namespace HistogramNS;

TEST(histogram_empty)
  {
  Histogram h(10, 0, 100);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), "");
  }
TEST(histogram_outofrange_low)
  {
  Histogram h(10, 0, 100);
  h.bump(-1);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), "   outside      1 : **************************************************\n");
  }
TEST(histogram_outofrange_high)
  {
  Histogram h(10, 0, 100);
  h.bump(100);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), "   outside      1 : **************************************************\n");
  }

TEST(histogram_oneslot_once)
  {
  Histogram h(10, 0, 100);
  h.bump(0);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), "   0 -    9     1 : **************************************************\n");
  }

TEST(histogram_oneslot_twice)
  {
  Histogram h(10, 0, 100);
  h.bump(0);
  h.bump(0);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), "   0 -    9     2 : **************************************************\n");
  }
TEST(histogram_oneslot_entirerange)
  {
  Histogram h(10, 0, 100);
  for(long i = 0; i < 10; ++i)
    h.bump(i);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), "   0 -    9    10 : **************************************************\n");
  }
TEST(histogram_entirerange_plusone)
  {
  Histogram h(2, 0, 10);
  for(long i = 0; i <= 10; ++i)
    h.bump(i);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), 
    "   0 -    1     2 : **************************************************\n"
    "   2 -    3     2 : **************************************************\n"
    "   4 -    5     2 : **************************************************\n"
    "   6 -    7     2 : **************************************************\n"
    "   8 -    9     2 : **************************************************\n"
    "   outside      1 : *************************\n"
    );
  }

TEST(histogram_entirerange_plusone_2)
  {
  Histogram h(1, 0, 5);
  for(long i = 0; i <= 5; ++i)
    h.bump(i);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), 
    "   0 -    0     1 : **************************************************\n"
    "   1 -    1     1 : **************************************************\n"
    "   2 -    2     1 : **************************************************\n"
    "   3 -    3     1 : **************************************************\n"
    "   4 -    4     1 : **************************************************\n"
    "   outside      1 : **************************************************\n"
    );
  }
TEST(histogram_twoslots_onceeach)
  {
  Histogram h(10, 0, 100);
  h.bump(0);
  h.bump(10);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), 
    "   0 -    9     1 : **************************************************\n"
    "  10 -   19     1 : **************************************************\n"
    );
  }

TEST(histogram_threeslots_onceeach)
  {
  Histogram h(10, 0, 100);
  h.bump(40); //reverse order to test the sort works...
  h.bump(20);
  h.bump(0);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), 
    "   0 -    9     1 : **************************************************\n"
    "  20 -   29     1 : **************************************************\n"
    "  40 -   49     1 : **************************************************\n"
    );
  }

TEST(histogram_twoslots_1full_1half)
  {
  Histogram h(10, 0, 100);
  h.bump(0);
  h.bump(0);
  h.bump(11);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(), 
    "   0 -    9     2 : **************************************************\n"
    "  10 -   19     1 : *************************\n"
    );
  }

TEST(histogram_manyentry_half)
  {
  Histogram h(50, 0, 100);
  for(int i=0; i < 50; ++i)
    h.bump(i);
  for(int i=50; i < 75; ++i)
    h.bump(i);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(),
    "   0 -   49    50 : **************************************************\n"
    "  50 -   99    25 : *************************\n"
    );
  }

TEST(histogram_manyentry_oddsplits)
  {
  Histogram h(1, 0, 3);
  for(int i=0; i < 33; ++i)
    h.bump(0);
  for(int i=0; i < 22; ++i)
    h.bump(1);
  for(int i=0; i < 11; ++i)
    h.bump(2);
  strstream s;
  h.print(s);
  s << ends;
  utxassert(s.str(),
    "   0 -    0    33 : **************************************************\n"
    "   1 -    1    22 : *********************************\n"
    "   2 -    2    11 : ****************\n"
    );
  }






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010