#include <vector>
#include <iostream>

#include "binary_state.hpp"

//! static members of the binary_state class
uint64_t binary_state::rightmask = ((1L<<32) - 1);
uint64_t binary_state::leftmask = binary_state::rightmask<<32;




/**
 shifts set bit in position i to position j
 returns the fermionic phase (+1 or -1) generated by the shift, or zero if the shift is impossible
 @param i bitmask of the source orbital (bit to shift from)
 @param j bitmask of the destination orbital (bit to shift to)
 */
int binary_state::one_body(uint64_t i, uint64_t j)
{
	if(b&j or !(b&i)) return(0);
	b = b - i + j;
	return interphase(i,j);
}






/**
 turn set bits at i and j into zero.
 returns the phase (+1 or -1) generated by the operation, or zero if the operation is impossible
 @param i bitmask of the first orbital to annihilate
 @param j bitmask of the second orbital to annihilate
 */
int binary_state::pair_annihilate(uint64_t i, uint64_t j)
{
	if(!(b&i) or !(b&j)) return(0);
	int pauli = 1;
	b = b - i - j;
	if(i<j) pauli = -1;
	return pauli*interphase(i,j);
}





/**
 turn set bits at i and j into zero.
 returns the phase (+1 or -1) generated by the operation, or zero if the operation is impossible
 @param i bitmask of the first orbital to create
 @param j bitmask of the second orbital to create
 */
int binary_state::pair_create(uint64_t i, uint64_t j)
{
  if((b&i) or (b&j)) return(0);
  int pauli = 1;
  b = b + i + j;
  if(i>j) pauli = -1;
  return pauli*interphase(i,j);
}







/**
 Makes a string out of the binary representation of integer \a n, below position \a m
 @param n integer giving rise to the binary representation
 @param s output string
 @param m number of bits
 */
void BinaryToString(size_t n, char *s, size_t m)
{
	size_t i, k;
	k=1;
	for(i=0; i<m; ++i){
		s[i] = n&k ? '1' : '0';
		k <<= 1;
	}
	s[m] = 0;
}






/**
 Prints the binary representation of integer !\a n on a stream, treating that representation as the concatenation of
 two representations, each of length \a m, for spin up and down, respectively
 @param flux output stream
 @param n integer giving rise to the binary representation
 @param m number of bits for each part of the representation
 */
void PrintBinaryDouble(ostream &flux, const uint64_t &n, size_t m)
{
	char s[m+1];
  BinaryToString(n,s,m);
	flux << '|' << s << ',';
  BinaryToString(n >> 32,s,m);
	flux << s << '>';
}




/**
 counts the number of '1' bits in 32-bit integer u
 Taken from Numerical Recipes, 3rd edition, p. 16
 @param u 32-bit integer (input)
 */
uint32_t bitcount32(uint32_t u)
{
	uint32_t uCount;
	
	uCount = u
	- ((u >> 1) & 033333333333)
	- ((u >> 2) & 011111111111);
	return (((uCount + (uCount >> 3)) & 030707070707) % 63);
}






const uint64_t m1  = 0x5555555555555555; //binary: 0101...
const uint64_t m2  = 0x3333333333333333; //binary: 00110011..
const uint64_t m4  = 0x0f0f0f0f0f0f0f0f; //binary:  4 zeros,  4 ones ...
const uint64_t m8  = 0x00ff00ff00ff00ff; //binary:  8 zeros,  8 ones ...
const uint64_t m16 = 0x0000ffff0000ffff; //binary: 16 zeros, 16 ones ...
const uint64_t m32 = 0x00000000ffffffff; //binary: 32 zeros, 32 ones
										 // const uint64_t hff = 0xffffffffffffffff; //binary: all ones
										 // const uint64_t h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3...

/**
 counts the number of '1' bits in 64-bit integer x
 taken from http://bajeed.blogspot.ca/2008/09/bit-counting-techniques.html
 @param x 64-bit unsigned integer (input)
 */
uint64_t bitcount64(uint64_t x) {
	x = (x & m1 ) + ((x >>  1) & m1 ); //put count of each  2 bits into those  2 bits
	x = (x & m2 ) + ((x >>  2) & m2 ); //put count of each  4 bits into those  4 bits
	x = (x & m4 ) + ((x >>  4) & m4 ); //put count of each  8 bits into those  8 bits
	x = (x & m8 ) + ((x >>  8) & m8 ); //put count of each 16 bits into those 16 bits
	x = (x & m16) + ((x >> 16) & m16); //put count of each 32 bits into those 32 bits
	x = (x & m32) + ((x >> 32) & m32); //put count of each 64 bits into those 64 bits
	return x;
}



uint64_t collapse(const uint64_t &b, const vector<int> &s)
{
	uint64_t c = 0L;
	for(int i=0; i<s.size(); i++){
		uint64_t I = (1L << i);
		uint64_t k = (1L << s[i]);
		if (b&k) c += I;
		k = (k << 32);
		I = (I << 32);
		if (b&k) c += I;
	}
	// cout << "(";
	// for(int i=0; i<s.size(); i++) cout << s[i] << ", ";
	// cout <<  ") : ";
	// PrintBinaryDouble(cout, b, 4); cout << "\t";
    // PrintBinaryDouble(cout, c, s.size()); cout << "\n";

	return c;
}