/*
    libmaus2
    Copyright (C) 2009-2013 German Tischler
    Copyright (C) 2011-2013 Genome Research Limited

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#if ! defined(POPCNT_HPP)
#define POPCNT_HPP

#include <libmaus2/types/types.hpp>
#include <string>
#include <sstream>
#include <iostream>
#include <bitset>

namespace libmaus2
{
	namespace rank
	{
		template<unsigned int intsize>
		struct PopCnt4Base
		{
		};
		template<unsigned int longsize>
		struct PopCnt8Base
		{
		};

		#if defined(__GNUC__)
		template<>
		struct PopCnt4Base<2>
		{
			/**
			 * Population count (number of 1 bits)
			 * using the gnu compilers builtin function for unsigned long types
			 * @param u number
			 * @return number of 1 bits in u
			 **/
			static unsigned int popcnt4(uint32_t const u)
			{
				return __builtin_popcountl(u);
			}
		};
		template<>
		struct PopCnt4Base<4>
		{
			/**
			 * Population count (number of 1 bits)
			 * using the gnu compilers builtin function for unsigned long types
			 * @param u number
			 * @return number of 1 bits in u
			 **/
			static unsigned int popcnt4(uint32_t const u)
			{
				return __builtin_popcount(u);
			}
		};

		template<>
		struct PopCnt8Base<4>
		{
			/**
			 * Population count (number of 1 bits)
			 * using the gnu compilers builtin function for unsigned long types
			 * @param u number
			 * @return number of 1 bits in u
			 **/
			static unsigned int popcnt8(uint64_t const u)
			{
				return (__builtin_popcountl(u>>32)) + (__builtin_popcountl(u&0x00000000FFFFFFFFULL));
			}
		};
		template<>
		struct PopCnt8Base<8>
		{
			/**
			 * Population count (number of 1 bits)
			 * using the gnu compilers builtin function for unsigned long types
			 * @param u number
			 * @return number of 1 bits in u
			 **/
			static unsigned int popcnt8(uint64_t const u)
			{
				return __builtin_popcountl(u);
			}
		};
		#else
		template<>
		struct PopCnt4Base<2>
		{
			/**
			 * Population count (number of 1 bits)
			 *
			 * @param x number
			 * @return number of 1 bits in x
			 **/
			static unsigned int popcnt4(uint32_t v)
			{
				return std::bitset<32>(v).count();
			}
		};
		template<>
		struct PopCnt4Base<4>
		{
			/**
			 * Population count (number of 1 bits)
			 *
			 * @param x number
			 * @return number of 1 bits in x
			 **/
			static unsigned int popcnt4(uint32_t v)
			{
				return std::bitset<32>(v).count();
			}
		};
		template<>
		struct PopCnt4Base<8>
		{
			/**
			 * Population count (number of 1 bits)
			 *
			 * @param x number
			 * @return number of 1 bits in x
			 **/
			static unsigned int popcnt4(uint32_t v)
			{
				return std::bitset<32>(v).count();
			}

		};
		template<>
		struct PopCnt8Base<4>
		{
			/**
			 * Population count (number of 1 bits)
			 *
			 * @param x number
			 * @return number of 1 bits in x
			 **/
			static unsigned int popcnt8(uint64_t v)
			{
				return
					std::bitset<32>(v&0xFFFFFFFF).count()
					+
					std::bitset<32>(v>>32).count();
			}

		};
		template<>
		struct PopCnt8Base<8>
		{
			/**
			 * Population count (number of 1 bits)
			 *
			 * @param x number
			 * @return number of 1 bits in x
			 **/
			static unsigned int popcnt8(uint64_t v)
			{
				return std::bitset<64>(v).count();
			}

		};
		#endif

		template<unsigned int intsize>
		struct PopCnt4 : public PopCnt4Base<intsize>
		{
			typedef uint32_t input_type;

			static unsigned int dataBits()
			{
				return 8*sizeof(input_type);
			}

			static input_type msb()
			{
				return static_cast<input_type>(1) << (dataBits()-1);
			}

			static unsigned int popcnt4(input_type v)
			{
				return PopCnt4Base<intsize>::popcnt4(v);
			}
			static unsigned int popcnt4(input_type v, unsigned int bits)
			{
				return popcnt4( v >> (8*sizeof(input_type)-bits) );
			}

			static std::string printP(input_type v, unsigned int numbits = dataBits() )
			{
				input_type const mask = msb();
				std::string s(numbits,' ');

				for ( unsigned int i = 0; i < numbits; ++i )
				{
					if ( v&mask )
						s[i] = ')';
					else
						s[i] = '(';

					v &= ~mask;
					v <<= 1;
				}

				return s;
			}
		};

		template<unsigned int longsize>
		struct PopCnt8 : public PopCnt8Base<longsize>
		{
			static unsigned int popcnt8(uint64_t v)
			{
				return PopCnt8Base<longsize>::popcnt8(v);
			}
			static unsigned int popcnt8(uint64_t v, unsigned int bits)
			{
				return popcnt8( v >> (8*sizeof(uint64_t)-bits) );
			}
		};

		static inline unsigned int bitcountpair(uint32_t const n)
		{
			return PopCnt4<sizeof(unsigned int)>::popcnt4(((n >> 1) | n) & 0x55555555ul);
		}

		static inline unsigned int bitcountpair(uint64_t const n)
		{
			return PopCnt8<sizeof(unsigned long)>::popcnt8(((n >> 1) | n) & 0x5555555555555555ull);
		}

		static inline unsigned int diffcountpair(uint32_t const a, uint32_t const b)
		{
			return bitcountpair( a ^ b );
		}

		static inline unsigned int diffcountpair(uint64_t const a, uint64_t const b)
		{
			return bitcountpair( a ^ b );
		}
	}
}
#endif
