/*
 *
 *
 *    cavg.h
 *
 *    Integrator/accumulator template.
 *
 *    Copyright (C) 2007-2016 by Matt Roberts.
 *    License: GNU GPL3 (www.gnu.org)
 *
 *
 */

#ifndef __KK5JY_CAVG_H
#define __KK5JY_CAVG_H

#ifdef __linux__
#include <cstdlib>
#endif

namespace KK5JY {
	namespace DSP {
		/*
		 *
		 *   class Averager<T> - a generic integrator/accumulator class
		 *
		 */
		template <typename sample_t, typename accum_t = sample_t>
		class Averager {
			private:
				// the length of the integrator
				size_t length;
				// the list of samples
				sample_t * samples;
				// pointer for walking through the list of samples
				sample_t * ptr;
				// the accumulator
				accum_t sum;

			public:
				Averager(void) : length(0), samples(0), ptr(0), sum(0) {
					// nop
				}

				Averager(size_t len) : length(0), samples(0), ptr(0), sum(0) {
					resize(len);
				}
			private: // disallowed ctors
				Averager(const Averager&);

			public:
				// return the length of the accumulator
				size_t size(void) const { return length; };

				// return the accumulated value
				accum_t accum(void) const { return sum; };

				// return the mean
				accum_t value(void) const { return sum / static_cast<accum_t>(length); };

			public:
				// resize the accumulator
				void resize(size_t n);

				// add a new sample to the accumulator
				void add(const sample_t &s);

				// add a sample and return the current value
				accum_t run(const sample_t &s) { add(s); return value(); }
		};


		//
		//  resize(n)
		//
		template <typename sample_t, typename accum_t>
		inline void Averager<sample_t, accum_t>::resize(size_t n) {
			// resize the array
			if (samples) {
				delete[] samples;
			}
			length = n;
			samples = new sample_t[length];

			// initialize the samples
			for (ptr = samples; ptr != (samples + length); ++ptr) {
				*ptr = 0;
			}

			// zero out the sum
			sum = 0;

			// the i/o pointer starts at the first element
			ptr = samples;
		}

		//
		//  add(s)
		//
		template <typename sample_t, typename accum_t>
		inline void Averager<sample_t, accum_t>::add(const sample_t &s) {
			// subtract the value at the current i/o element
			sum -= *ptr;
			// add the new value to the sum
			sum += s;
			// store the new value in the current i/o element
			*ptr = s;
			// advance to the next element
			++ptr;

			// wrap the i/o pointer if needed
			if (ptr == (samples + length)) {
				ptr = samples;
			}
		}
	}
}

#endif
