/*
 *
 *
 *    favg.h
 *
 *    Fixed-point integrator/accumulator template.
 *
 *    Copyright (C) 2016 by Matt Roberts.
 *    License: GNU GPL3 (www.gnu.org)
 *
 *
 */

#ifndef __KK5JY_FAVG_H
#define __KK5JY_FAVG_H

#ifdef __linux__
#include <cstdlib>
#endif
#include "fixed.h"

namespace KK5JY {
	namespace DSP {
		/*
		 *
		 *   class FixedAverager<T> - a generic integrator/accumulator class
		 *
		 */
		template <typename sample_t, typename accum_t = sample_t>
		class FixedAverager {
			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:
				// the "divisor"
				FixedPoint<accum_t> divisor;

			public:
				void configure(size_t n);

			public:
				FixedAverager(size_t n) { configure(n); }

			private: // disallowed ctors
				FixedAverager(const FixedAverager&);

			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 the multiplier used to compute 'value'
				double factor(void) const { return divisor.toDouble(); }

			public:
				// 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);
		};


		//
		//  ctor
		//
		template <typename sample_t, typename accum_t>
		inline void FixedAverager<sample_t, accum_t>::configure(size_t n) {
			length = n;
			samples = new sample_t[length];
			ptr = samples;
			sum = 0;
			divisor = 1.0 / n;

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

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


		//
		//  value
		//
		template <typename sample_t, typename accum_t>
		inline accum_t FixedAverager<sample_t, accum_t>::value(void) const {
			return sum * divisor;
		}


		//
		//  run - add a sample and return the current value
		//
		template <typename sample_t, typename accum_t>
		inline accum_t FixedAverager<sample_t, accum_t>::run(const sample_t &s) {
			add(s);
			return value();
		}


		//
		//  add(s)
		//
		template <typename sample_t, typename accum_t>
		inline void FixedAverager<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
