// histogram.hpp
// Copyright (C) 2006 - 2009 MicroNeil Research Corporation
// Class to capture a histogram of events using a <set>

#ifndef mn_histogram_included
#define mn_histogram_included

#include <set>

using namespace std;

/** The Histogram class is managed set of HistogramRecords.
*** We play some naughty tricks with pointers to break the rules and
*** directly manipulate the counts of HistogramRecords stored in the
*** set - thus saving space, complexity, and cycles. The set allows us
*** to add new records as needed and locate existing records quickly.
*** At any point in time, the set contains all of the event (hit) counts
*** ordered by key.
**/

class HistogramRecord {                                                         // A record to assocate a key and count.
  public:
    int Key;                                                                    // Here is the key.
    int Count;                                                                  // Here is the count.
    HistogramRecord(const int NewKey) :                                         // We must have a key to make one.
      Key(NewKey), Count(0) {}                                                  // and a new one starts at count 0.

    bool operator<(const HistogramRecord& Right) const {                        // To live in a set we need to <
        return (Key < Right.Key);                                               // properly based on the key.
    }
};

class Histogram : public set<HistogramRecord> {                                 // A Histogram is a set of HistogramRecords
  private:                                                                      // and a private hit counter...
    int HitCount;
  public:
    Histogram() : HitCount(0) {}
                                                                                // with a few extra public functions. The
    int hit(const int EventKey, const int Adjustment = 1) {                     // hit() method increments a specific count.
        HistogramRecord E(EventKey);                                            // First, make a record for the event key.
        insert(E);                                                              // Insert the new record (if it's not there).
        set<HistogramRecord>::iterator iE =                                     // Find either the pre-existing or the new
          find(E);                                                              // record for this key.
        int* C;                                                                 // Play naughty pointer games to access
        C = const_cast<int*>(&((*iE).Count));                                   // the Count for this record inside the
        (*C) += Adjustment;                                                     // set and add our Adjustment to it.
        HitCount += Adjustment;                                                 // Accumulate the adjustments overall.
        return(*C);                                                             // Return the count for this key.
    }

    int Hits() { return HitCount; }                                             // Return the sum of hits so far.

    void reset() {                                                              // Reset the histogram to zero.
        HitCount = 0;                                                           // That means no counts, and
        clear();                                                                // an empty set of records.
    }
};

#endif