//
//  LFSR.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 19/01/2020.
//  Copyright © 2020 Thomas Harte. All rights reserved.
//

#ifndef LFSR_h
#define LFSR_h

namespace Numeric {

template <typename IntType> struct LSFRPolynomial {};

// The following were taken 'at random' from https://users.ece.cmu.edu/~koopman/lfsr/index.html
template <> struct LSFRPolynomial<uint64_t> {
	static constexpr uint64_t value = 0x80000000000019E2;
};

template <> struct LSFRPolynomial<uint32_t> {
	static constexpr uint32_t value = 0x80000C34;
};

template <> struct LSFRPolynomial<uint16_t> {
	static constexpr uint16_t value = 0x853E;
};

template <> struct LSFRPolynomial<uint8_t> {
	static constexpr uint8_t value = 0xAF;
};

/*!
	Provides a linear-feedback shift register with a random initial state; if no polynomial is supplied
	then one will be picked that is guaranteed to give the maximal number of LFSR states that can fit
	in the specified int type.
*/
template <typename IntType = uint64_t, IntType polynomial = LSFRPolynomial<IntType>::value> class LFSR {
	public:
		LFSR() {
			// Randomise the value, ensuring it doesn't end up being 0.
			while(!value_) {
				uint8_t *value_byte = reinterpret_cast<uint8_t *>(&value_);
				for(size_t c = 0; c < sizeof(IntType); ++c) {
					*value_byte = uint8_t(uint64_t(rand()) * 255 / RAND_MAX);
					++value_byte;
				}
			}
		}

		/*!
			Advances the LSFR, returning either an @c IntType of value @c 1 or @c 0,
			determining the bit that was just shifted out.
		*/
		IntType next() {
			const auto result = value_ & 1;
			value_ = (value_ >> 1) ^ (result * polynomial);
			return result;
		}

	private:
		IntType value_ = 0;
};

}

#endif /* LFSR_h */
