/*
 *
 * $Header: /usr/u/wjr/src/ADT/RCS/hist.c,v 1.6 1991/11/07 22:41:46 wjr Exp $
 *
 */
/*
 * Xerox may use this code without restriction.
 */

static char rcsid[] = "@(#)$Header: /usr/u/wjr/src/ADT/RCS/hist.c,v 1.6 1991/11/07 22:41:46 wjr Exp $";

/*
 *	hist.c - Histograms.
 *
 * What's a histogram, really? Well, it's got a bunch of bins, and you
 * accumulate counts in each bin. That's about it, really...
 *
 * Exports:
 *	type Histogram
 *
 *	constant Histogram NULLHIST
 *
 *	Histogram hiNew(unsigned nbins, double low, double high) - create a
 *		new, empty, histogram.
 *
 *	void hiFree(Histogram h) - free histogram h.
 *
 *	void hiAdd(Histogram h, double val, double howmuch) - accumulate
 *		into val's bin in h.
 *
 *	void hiInc(Histogram h, double val) - add 1 to val's bin in h.
 *
 *	double hiCount(Histogram h, unsigned n) - return the n'th
 *		count entry of h.
 *
 *	double hiLow(Histogram h) - return the low bound of h.
 *
 *	double hiHigh(Histogram h) - return the high bound of h.
 *
 *	unsigned hiNBins(Histogram h) - return the number of bins of h.
 *
 *	double hiBinL(Histogram h, unsigned n) - return the left edge of
 *		the n'th bin of h.
 *
 *	double hiBinR(Histogram h, unsigned n) - return the right edge of
 *		the n'th bin of h (which is the same as the left edge of
 *		the n+1'th bin).
 *
 *	double hiBinC(Histogram h, unsigned n) - return the centre of
 *		the n'th bin of h.
 *
 * hiNew creates a new Histogram, with the specified number of bins,
 *	which will be used to count values in the range [low,high).
 *
 * hiFree frees the storage associated with a Histogram.
 *
 * hiAdd adds the given value to the appropriate bin in the given
 *	histogram. If the binning value is not in the range [low,high) for that
 *	histogram, it is added to the (low or high) "end" bin. This is
 *	so that minor numerical errors around the edges do not cause
 *	assert failures.
 *
 * hiInc is just an abbreviation for hiAdd(h, val, 1.)
 *
 * hiCount returns the number of values which have fallen into that bin of
 *	that histogram. The bins are numbered 0..nbins-1, and n must lie
 *	in this range. This is actually an lvalue, and so the value of
 *	a particular bin may be modified.
 *
 * hiLow returns the low bound of the given histogram (the low value which
 *	was passed to the call to hiNew which created it).
 *
 * hiHigh returns the high bound of the given histogram (the high value which
 *	was passed to the call to hiNew which created it).
 *
 * hiNBins returns the number of bins in the given histogram.
 *
 */

#include "misc.h"
#include "hist.h"

Histogram
hiNew(unsigned nbins, double low, double high)
{
    Histogram newH;

    assert(low <= high);

    newH = (Histogram)malloc(sizeof(*newH));
    if (newH == NULLHIST) {
	return(NULLHIST);
	}
    newH->bins = (double *)calloc(nbins, sizeof(double));
    if (newH->bins == (double *)NULL) {
	free((char *)newH);
	return(NULLHIST);
	}
    newH->nbins = nbins;
    newH->low = low;
    newH->high = high;

    return(newH);
    }

void
hiFree(Histogram h)
{
    assert(h != NULLHIST);
    assert(h->bins != (double *)NULL);

    free((char *)h->bins);
    free((char *)h);
    }

void
hiAdd(Histogram h, double val, double howmuch)
{
    assert(h != NULLHIST);

    assert(h->nbins > 0);
    assert(h->bins != (double *)NULL);
    
    /* Handle out-of-range values by sticking them in the extreme bins */
    if (val < h->low) {
	h->bins[0] += howmuch;
	}
    else if (val >= h->high) {
	h->bins[h->nbins - 1] += howmuch;
	}
    else {
	/*
	 * Casting a floating point number to an integer is defined to truncate
	 * it (throw away any fractional part). This is therefore the right
	 * thing to do.
	 */
	(h->bins[(int)((val - h->low) / (h->high - h->low) * h->nbins)])
	    += howmuch;
	}
    }

/* The rest are implemented as macros in hist.h */
