#ifndef KECCAK_DETAIL_HPP
#define KECCAK_DETAIL_HPP

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <array>

namespace keccak
{
	namespace detail
	{
		template <unsigned long long A, unsigned long long B>
		struct static_max
		{
			static const auto value = A > B ? A : B;
		};

		template <unsigned N, unsigned C = 0>
		struct static_log2
		{
			static const unsigned value = static_log2<N / 2, C + 1>::value;
		};

		template <unsigned C>
		struct static_log2<1, C>
		{
			static const unsigned value = C;
		};

		template <unsigned C>
		struct static_log2<0, C>
		{
			static_assert(C >= 1, "Log2 result is not an integer.");
			static_assert(C < 1, "Log2 result is not an integer.");
		};

		template <typename StateType, typename LaneType>
		void round(StateType& s, LaneType round_constant);

		template <unsigned Rounds, typename StateType>
		void keccak_f(StateType& state);

		template <typename LaneType, std::size_t Capacity>
		class keccak_state
		{
		public:
			typedef LaneType lane_type;
			typedef lane_type plane_type[5];
			typedef std::array<plane_type, 5> state_type;

			static const std::size_t byte_bits = 8;

			static const auto width = sizeof(lane_type) * byte_bits;
			static const auto capacity = Capacity;
			static const auto rate = 25 * width - capacity;

			static const auto byte_width = width / byte_bits;
			static const auto byte_capacity = capacity / byte_bits;
			static const auto byte_rate = rate / byte_bits;

			static const auto rounds = 12 + 2 * static_log2<width>::value;

			static_assert(width % byte_bits == 0, "Sub-byte hacks are not supported.");
			static_assert(capacity % byte_bits == 0, "Sub-byte hacks are not supported.");
			static_assert(rate % byte_bits == 0, "Sub-byte hacks are not supported.");

			static_assert(rate % width == 0, "Rate / width combination impossible.");
			static_assert(byte_rate % byte_width == 0, "Rate / width combination not supported.");

			static_assert(capacity < 25 * width, "Capacity too high.");

		private:
			state_type state_;

		public:
			keccak_state()
				: state_()
			{}

			const plane_type& operator [] (std::size_t i) const
			{
				return state_[i];
			}

			plane_type& operator [] (std::size_t i)
			{
				return state_[i];
			}

			const lane_type* data() const
			{
				return state_.data()[0];
			}

			lane_type* data()
			{
				return state_.data()[0];
			}

			void transform()
			{
				keccak_f<rounds>(state_);
			}
		};

		template <typename StateType>
		class sponge_absorber
		{
		public:
			typedef StateType state_type;
			typedef typename state_type::lane_type lane_type;

		private:
			state_type state_;
			std::size_t bytes_absorbed_;

		public:
			sponge_absorber()
				: bytes_absorbed_()
			{}

			sponge_absorber(const void* data, std::size_t size)
				: bytes_absorbed_()
			{
				(*this)(data, size);
			}

			void operator () (const void* data, std::size_t size)
			{
				auto data_bytes = static_cast<const std::uint8_t*>(data);

				while (size != 0)
				{
					while (bytes_absorbed_ == 0 && size >= state_type::byte_rate)
					{
						for (std::size_t i = 0; i != state_type::byte_rate / state_type::byte_width; ++i)
						{
							lane_type lane;
							std::memcpy(&lane, data_bytes + i * state_type::byte_width, state_type::byte_width);
							*(state_[0] + i) ^= lane;
						}

						data_bytes += state_type::byte_rate;
						size -= state_type::byte_rate;

						state_.transform();
					}

					if (size != 0)
					{
						reinterpret_cast<std::uint8_t*>(state_.data())[bytes_absorbed_] ^= *data_bytes;

						++data_bytes;
						--size;

						if (++bytes_absorbed_ == state_type::byte_rate)
						{
							state_.transform();
							bytes_absorbed_ = 0;
						}
					}
				}
			}

			std::size_t bytes_absorbed() const
			{
				return bytes_absorbed_;
			}

			const state_type& internal_state() const
			{
				return state_;
			}

			state_type& internal_state()
			{
				return state_;
			}
		};

		template <typename StateType>
		class sponge_squeezer
		{
		public:
			typedef StateType state_type;
			typedef typename state_type::lane_type lane_type;

		private:
			state_type state_;
			std::size_t bytes_squeezed_;

		public:
			sponge_squeezer(sponge_absorber<state_type> absorber)
				: state_(absorber.internal_state())
				, bytes_squeezed_()
			{
				reinterpret_cast<std::uint8_t*>(state_.data())[absorber.bytes_absorbed()] ^= 1u;
				reinterpret_cast<std::uint8_t*>(state_.data())[state_type::byte_rate - 1] ^= 128u;
				state_.transform();
			}

			void operator () (void* buf, std::size_t size)
			{
				auto buf_bytes = static_cast<std::uint8_t*>(buf);

				while (size != 0)
				{
					while (bytes_squeezed_ == 0 && size >= state_type::byte_rate)
					{
						std::memcpy(buf_bytes, state_.data(), state_type::byte_rate);

						buf_bytes += state_type::byte_rate;
						size -= state_type::byte_rate;

						state_.transform();
					}

					if (size != 0)
					{
						const auto copy_size = std::min(size, state_type::byte_rate - bytes_squeezed_);
						const auto state_bytes = reinterpret_cast<std::uint8_t*>(state_.data());

						std::memcpy(buf_bytes, state_bytes + bytes_squeezed_, copy_size);

						buf_bytes += copy_size;
						size -= copy_size;
						bytes_squeezed_ += copy_size;

						if (bytes_squeezed_ == state_type::byte_rate)
						{
							state_.transform();
							bytes_squeezed_ = 0;
						}
					}
				}
			}

			std::size_t bytes_squeezed() const
			{
				return bytes_squeezed_;
			}

			const state_type& internal_state() const
			{
				return state_;
			}

			state_type& internal_state()
			{
				return state_;
			}
		};

		const std::uint64_t round_constants[24] =
		{
			0x0000000000000001ull,
			0x0000000000008082ull,
			0x800000000000808Aull,
			0x8000000080008000ull,
			0x000000000000808Bull,
			0x0000000080000001ull,
			0x8000000080008081ull,
			0x8000000000008009ull,
			0x000000000000008Aull,
			0x0000000000000088ull,
			0x0000000080008009ull,
			0x000000008000000Aull,
			0x000000008000808Bull,
			0x800000000000008Bull,
			0x8000000000008089ull,
			0x8000000000008003ull,
			0x8000000000008002ull,
			0x8000000000000080ull,
			0x000000000000800Aull,
			0x800000008000000Aull,
			0x8000000080008081ull,
			0x8000000000008080ull,
			0x0000000080000001ull,
			0x8000000080008008ull,
		};

		template <unsigned Rounds, typename StateType>
		void keccak_f(StateType& state)
		{
			for (unsigned i = 0; i != Rounds; ++i)
			{
				round(state, round_constants[i]);
			}
		}

		template <unsigned N, typename Word>
		Word shl(Word w)
		{
			return w << (N);
		}

		template <unsigned N, typename Word>
		Word shr(Word w)
		{
			return w >> (N);
		}

		template <unsigned N, typename Word>
		Word rotr(Word w)
		{
			return shr<N>(w) | shl<sizeof(Word) * 8 - N>(w);
		}

		template <unsigned N, typename Word>
		Word rotl(Word w)
		{
			return shl<N>(w) | shr<sizeof(Word) * 8 - N>(w);
		}

		template <typename Word>
		Word rotr(Word w, unsigned n)
		{
			return (w >> n) | (w << (sizeof(Word) * 8 - n));
		}

		template <typename Word>
		Word rotl(Word w, unsigned n)
		{
			return (w << n) | (w >> (sizeof(Word) * 8 - n));
		}

		typedef unsigned rotation_offset_type;

		const rotation_offset_type roffset_0_0 = 0;
		const rotation_offset_type roffset_0_1 = 36;
		const rotation_offset_type roffset_0_2 = 3;
		const rotation_offset_type roffset_0_3 = 41;
		const rotation_offset_type roffset_0_4 = 18;

		const rotation_offset_type roffset_1_0 = 1;
		const rotation_offset_type roffset_1_1 = 44;
		const rotation_offset_type roffset_1_2 = 10;
		const rotation_offset_type roffset_1_3 = 45;
		const rotation_offset_type roffset_1_4 = 2;

		const rotation_offset_type roffset_2_0 = 62;
		const rotation_offset_type roffset_2_1 = 6;
		const rotation_offset_type roffset_2_2 = 43;
		const rotation_offset_type roffset_2_3 = 15;
		const rotation_offset_type roffset_2_4 = 61;

		const rotation_offset_type roffset_3_0 = 28;
		const rotation_offset_type roffset_3_1 = 55;
		const rotation_offset_type roffset_3_2 = 25;
		const rotation_offset_type roffset_3_3 = 21;
		const rotation_offset_type roffset_3_4 = 56;

		const rotation_offset_type roffset_4_0 = 27;
		const rotation_offset_type roffset_4_1 = 20;
		const rotation_offset_type roffset_4_2 = 39;
		const rotation_offset_type roffset_4_3 = 8;
		const rotation_offset_type roffset_4_4 = 14;

		template <typename StateType, typename LaneType>
		void round(StateType& s, LaneType round_constant)
		{
			auto c0 = s[0][0] ^ s[1][0] ^ s[2][0] ^ s[3][0] ^ s[4][0];
			auto c1 = s[0][1] ^ s[1][1] ^ s[2][1] ^ s[3][1] ^ s[4][1];
			auto c2 = s[0][2] ^ s[1][2] ^ s[2][2] ^ s[3][2] ^ s[4][2];
			auto c3 = s[0][3] ^ s[1][3] ^ s[2][3] ^ s[3][3] ^ s[4][3];
			auto c4 = s[0][4] ^ s[1][4] ^ s[2][4] ^ s[3][4] ^ s[4][4];

			auto d0 = c4 ^ rotl<1>(c1);
			auto d1 = c0 ^ rotl<1>(c2);
			auto d2 = c1 ^ rotl<1>(c3);
			auto d3 = c2 ^ rotl<1>(c4);
			auto d4 = c3 ^ rotl<1>(c0);

			s[0][0] ^= d0;
			s[0][1] ^= d1;
			s[0][2] ^= d2;
			s[0][3] ^= d3;

			auto s00 = (s[0][0]); /* rotl<roffset_0_0> */
			auto s20 = rotl<roffset_1_0>(s[0][1]);
			auto s40 = rotl<roffset_2_0>(s[0][2]);
			auto s10 = rotl<roffset_3_0>(s[0][3]);

			s[0][4] ^= d4;
			s[1][0] ^= d0;
			s[1][1] ^= d1;
			s[1][2] ^= d2;

			auto s30 = rotl<roffset_4_0>(s[0][4]);
			auto s31 = rotl<roffset_0_1>(s[1][0]);
			auto s01 = rotl<roffset_1_1>(s[1][1]);
			auto s21 = rotl<roffset_2_1>(s[1][2]);

			s[1][3] ^= d3;
			s[1][4] ^= d4;
			s[2][0] ^= d0;
			s[2][1] ^= d1;

			auto s41 = rotl<roffset_3_1>(s[1][3]);
			auto s11 = rotl<roffset_4_1>(s[1][4]);
			auto s12 = rotl<roffset_0_2>(s[2][0]);
			auto s32 = rotl<roffset_1_2>(s[2][1]);

			s[2][2] ^= d2;
			s[2][3] ^= d3;
			s[2][4] ^= d4;
			s[3][0] ^= d0;

			auto s02 = rotl<roffset_2_2>(s[2][2]);
			auto s22 = rotl<roffset_3_2>(s[2][3]);
			auto s42 = rotl<roffset_4_2>(s[2][4]);
			auto s43 = rotl<roffset_0_3>(s[3][0]);

			s[3][1] ^= d1;
			s[3][2] ^= d2;
			s[3][3] ^= d3;
			s[3][4] ^= d4;

			auto s13 = rotl<roffset_1_3>(s[3][1]);
			auto s33 = rotl<roffset_2_3>(s[3][2]);
			auto s03 = rotl<roffset_3_3>(s[3][3]);
			auto s23 = rotl<roffset_4_3>(s[3][4]);

			s[4][0] ^= d0;
			s[4][1] ^= d1;
			s[4][2] ^= d2;
			s[4][3] ^= d3;

			auto s24 = rotl<roffset_0_4>(s[4][0]);
			auto s44 = rotl<roffset_1_4>(s[4][1]);
			auto s14 = rotl<roffset_2_4>(s[4][2]);
			auto s34 = rotl<roffset_3_4>(s[4][3]);

			s[4][4] ^= d4;
			auto s04 = rotl<roffset_4_4>(s[4][4]);

			// 
			s[0][0] = s00 ^ (~s01 & s02);
			s[0][1] = s01 ^ (~s02 & s03);
			s[0][2] = s02 ^ (~s03 & s04);
			s[0][3] = s03 ^ (~s04 & s00);
			s[0][4] = s04 ^ (~s00 & s01);

			s[1][0] = s10 ^ (~s11 & s12);
			s[1][1] = s11 ^ (~s12 & s13);
			s[1][2] = s12 ^ (~s13 & s14);
			s[1][3] = s13 ^ (~s14 & s10);
			s[1][4] = s14 ^ (~s10 & s11);

			s[2][0] = s20 ^ (~s21 & s22);
			s[2][1] = s21 ^ (~s22 & s23);
			s[2][2] = s22 ^ (~s23 & s24);
			s[2][3] = s23 ^ (~s24 & s20);
			s[2][4] = s24 ^ (~s20 & s21);

			s[3][0] = s30 ^ (~s31 & s32);
			s[3][1] = s31 ^ (~s32 & s33);
			s[3][2] = s32 ^ (~s33 & s34);
			s[3][3] = s33 ^ (~s34 & s30);
			s[3][4] = s34 ^ (~s30 & s31);

			s[4][0] = s40 ^ (~s41 & s42);
			s[4][1] = s41 ^ (~s42 & s43);
			s[4][2] = s42 ^ (~s43 & s44);
			s[4][3] = s43 ^ (~s44 & s40);
			s[4][4] = s44 ^ (~s40 & s41);

			s[0][0] ^= round_constant;
		}
	}
}

#endif




#ifndef KECCAK_HPP
#define KECCAK_HPP

#include <array>
#include <cstdint>
#include <limits>

//#include "keccak_detail.hpp"

namespace keccak
{
	template <std::size_t CollisionResistance, std::size_t PreimageResistance>
	class basic_hasher
	{
	public:
		static const auto collision_resistance = CollisionResistance;
		static const auto preimage_resistance = PreimageResistance;

		static const auto capacity = detail::static_max<collision_resistance * 2, preimage_resistance * 2>::value;
		static const auto hash_size = detail::static_max<collision_resistance * 2 / 8, preimage_resistance / 8>::value;

		typedef detail::keccak_state<std::uint64_t, capacity> state_type;

		typedef std::array<std::uint8_t, hash_size> hash_type;		

	private:
		detail::sponge_absorber<state_type> absorb_;

	public:
		void update(const void* data, std::size_t size)
		{
			absorb_(data, size);
		}

		void finish(void* buf, std::size_t size)
		{
			detail::sponge_squeezer<state_type> squeeze(std::move(absorb_));
			squeeze(buf, size);
			*this = basic_hasher();
		}

		hash_type finish()
		{
			hash_type hash;
			finish(hash.data(), hash.size());
			return hash;
		}
	};

	typedef basic_hasher<112, 224> sha3_244_hasher;
	typedef basic_hasher<128, 256> sha3_256_hasher;
	typedef basic_hasher<192, 384> sha3_384_hasher;
	typedef basic_hasher<256, 512> sha3_512_hasher;

	typedef basic_hasher<112, 112> s112_hasher;
	typedef basic_hasher<128, 128> s128_hasher;
	typedef basic_hasher<192, 192> s192_hasher;
	typedef basic_hasher<256, 256> s256_hasher;

	template <std::size_t SecurityStrength>
	class basic_stream_crypter
	{
	public:
		static const auto security_strength = SecurityStrength;

		typedef detail::keccak_state<std::uint64_t, security_strength * 2> state_type;
		typedef typename state_type::lane_type lane_type;

	private:
		detail::sponge_squeezer<state_type> squeeze_;

	public:
		basic_stream_crypter(const void* key, std::size_t keylen)
			: squeeze_(detail::sponge_absorber<state_type>(key, keylen))
		{}

		void operator () (void* buf, const void* data, std::size_t size)
		{
			auto dptr = static_cast<const std::uint8_t*>(data);
			auto bptr = static_cast<std::uint8_t*>(buf);
			auto msize = size / sizeof(lane_type);
			auto rsize = size % sizeof(lane_type);

			auto shift = apply<lane_type>(bptr, dptr, msize);
			apply<std::uint8_t>(bptr + shift, dptr + shift, rsize);
		}

	private:
		template <typename LaneType>
		std::size_t apply(void* buf, const void* data, std::size_t size)
		{
			auto dptr = static_cast<const std::uint8_t*>(data);
			auto bptr = static_cast<std::uint8_t*>(buf);

			for (auto i = 0; i != size; ++i)
			{
				LaneType l1, l2;

				squeeze_(&l1, sizeof l1);
				std::memcpy(&l2, dptr + i * sizeof(LaneType), sizeof l2);

				l1 ^= l2;

				std::memcpy(bptr + i * sizeof(LaneType), &l1, sizeof l1);
			}

			return size * sizeof(LaneType);
		}
	};
}

#endif




#include <iostream>
#include <string>
 
void dump(const void* memory, std::size_t size)
{
	static const char table[256][3] = 
	{
		"00", "01", "02", "03", "04", "05", "06", "07", 
		"08", "09", "0a", "0b", "0c", "0d", "0e", "0f", 
		"10", "11", "12", "13", "14", "15", "16", "17", 
		"18", "19", "1a", "1b", "1c", "1d", "1e", "1f", 
		"20", "21", "22", "23", "24", "25", "26", "27", 
		"28", "29", "2a", "2b", "2c", "2d", "2e", "2f", 
		"30", "31", "32", "33", "34", "35", "36", "37", 
		"38", "39", "3a", "3b", "3c", "3d", "3e", "3f", 
		"40", "41", "42", "43", "44", "45", "46", "47", 
		"48", "49", "4a", "4b", "4c", "4d", "4e", "4f", 
		"50", "51", "52", "53", "54", "55", "56", "57", 
		"58", "59", "5a", "5b", "5c", "5d", "5e", "5f", 
		"60", "61", "62", "63", "64", "65", "66", "67", 
		"68", "69", "6a", "6b", "6c", "6d", "6e", "6f", 
		"70", "71", "72", "73", "74", "75", "76", "77", 
		"78", "79", "7a", "7b", "7c", "7d", "7e", "7f", 
		"80", "81", "82", "83", "84", "85", "86", "87", 
		"88", "89", "8a", "8b", "8c", "8d", "8e", "8f", 
		"90", "91", "92", "93", "94", "95", "96", "97", 
		"98", "99", "9a", "9b", "9c", "9d", "9e", "9f", 
		"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", 
		"a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", 
		"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", 
		"b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", 
		"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", 
		"c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", 
		"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", 
		"d8", "d9", "da", "db", "dc", "dd", "de", "df", 
		"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", 
		"e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", 
		"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", 
		"f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", 
	};
 
	const unsigned char* p = static_cast<const unsigned char*>(memory);
 
	for (; size--; ++p)
		std::cout << table[*p];
 
	std::cout << '\n';
}
 
int main()
{
	std::cout << "Key: ";
	std::string key;
	std::getline(std::cin, key);
 
	keccak::basic_stream_crypter<128> encrypt(key.data(), key.size());
	keccak::basic_stream_crypter<128> decrypt(key.data(), key.size());
 
	std::string msg;
	while (std::cout << "Message: " && std::getline(std::cin, msg))
	{
		std::string enc(msg.size(), ' ');
		encrypt(&enc[0], msg.data(), msg.size());
		std::cout << "Encrypted: ";
		dump(enc.data(), enc.size());
 
		std::string dec(msg.size(), ' ');
 
		decrypt(&dec[0], enc.data(), enc.size());
		std::cout << "Decrypted: " << dec << '\n';
	}
}