
#define SHELTER_TO_G1(bits)		(((bits & 0x7) << (f2-a1)) | ((bits & 0x38) << (f3-d1)) | ((bits & 0x1C0) << (f4-g1)) | ((bits & 0xE00) << (f5-b2)))
#define STORM_TO_G1(bits)		(((bits & 0x7) << (f2-a1)) | ((bits & 0x38) << (f3-d1)) | ((bits & 0x1C0) << (f4-g1)) | ((bits & 0xE00) << (f5-b2)))

#define G1_TO_SHELTER(pawns)	(((pawns >>(f2-a1)) & 0x7) | ((pawns >>(f3-d1)) & 0x38) | ((pawns >>(f4-g1)) & 0x1C0) | ((pawns >>(f5-b2)) & 0xE00))
#define E1_TO_SHELTER(pawns)	(((pawns >>(d2-a1)) & 0x7) | ((pawns >>(d3-d1)) & 0x38) | ((pawns >>(d4-g1)) & 0x1C0) | ((pawns >>(d5-b2)) & 0xE00))
#define G8_TO_SHELTER(pawns)	(((pawns >>(f7-a1)) & 0x7) | ((pawns >>(f6-d1)) & 0x38) | ((pawns >>(f5-g1)) & 0x1C0) | ((pawns >>(f4-b2)) & 0xE00))
#define E8_TO_SHELTER(pawns)	(((pawns >>(d7-a1)) & 0x7) | ((pawns >>(d6-d1)) & 0x38) | ((pawns >>(d5-g1)) & 0x1C0) | ((pawns >>(d4-b2)) & 0xE00))
#define SHELTER_B_TO_G(pawns)	((((FileA & pawns) << (d1-a1)) | (pawns & (FileB|FileC))) << (f1-b1)) // b1 to g1 mask by mirror and shift
#define B1_TO_SHELTER(pawns)	(G1_TO_SHELTER(SHELTER_B_TO_G(pawns)))
#define B8_TO_SHELTER(pawns)	(G8_TO_SHELTER(SHELTER_B_TO_G(pawns)))

#define G1_TO_STORM(pawns)		(((pawns >>(f2-a1)) & 0x7) | ((pawns >>(f3-d1)) & 0x38) | ((pawns >>(f4-g1)) & 0x1C0) | ((pawns >>(f5-b2)) & 0xE00))
#define E1_TO_STORM(pawns)		(((pawns >>(d2-a1)) & 0x7) | ((pawns >>(d3-d1)) & 0x38) | ((pawns >>(d4-g1)) & 0x1C0) | ((pawns >>(d5-b2)) & 0xE00))
#define G8_TO_STORM(pawns)		(((pawns >>(f7-a1)) & 0x7) | ((pawns >>(f6-d1)) & 0x38) | ((pawns >>(f5-g1)) & 0x1C0) | ((pawns >>(f4-b2)) & 0xE00))
#define E8_TO_STORM(pawns)		(((pawns >>(d7-a1)) & 0x7) | ((pawns >>(d6-d1)) & 0x38) | ((pawns >>(d5-g1)) & 0x1C0) | ((pawns >>(d4-b2)) & 0xE00))
#define STORM_B_TO_G(pawns)		((((FileA & pawns) << (d1-a1)) | (pawns & (FileB|FileC))) << (f1-b1)) // b1 to g1 mask by mirror and shift
#define B1_TO_STORM(pawns)		(G1_TO_STORM(STORM_B_TO_G(pawns)))
#define B8_TO_STORM(pawns)		(G8_TO_STORM(STORM_B_TO_G(pawns)))

#define CENTRAL_FILE_NO			(FILE_H+1)

//#define DEBUG_PAWN_INFO

class PawnEntry {
public:	
	uint64 key;
	uint64 weak_pawns;
	uint64 passed_pawns;
	sint16 opening;
	sint16 endgame;
	sint16 shelter[2][3];		// [colour][wing]
	uint8 central_file;
	uint8 used;	
};

class PawnShelterEntry {
public:	
	sint16 value;	
};
class PawnStormEntry {
public:	
	sint16 value;	
};

class PawnInfo {
public:	
	PawnEntry *entry;
	PawnStormEntry *storm;
	PawnShelterEntry *shelter;
	uint64 retrieve_mask;
	uint32 read_success;
	uint32 read_total;
	uint32 cell_nb;
	uint32 used;

	// constructor
	PawnInfo() {				
		cell_nb = PAWN_INFO_SIZE;
		retrieve_mask = cell_nb-1;		
		entry = new PawnEntry[cell_nb];
		ASSERT(entry != NULL);
		ASSERT((cell_nb & retrieve_mask) == 0); // power of 2
		memset(entry, 0, cell_nb * sizeof(PawnEntry));	
		read_success = read_total = used = 0;	

		shelter = new PawnShelterEntry[PAWN_SHELTER_SIZE];		
		storm = new PawnStormEntry[PAWN_STORM_SIZE];
		memset(shelter, 0, PAWN_SHELTER_SIZE * sizeof(PawnShelterEntry));					
		memset(storm, 0, PAWN_STORM_SIZE * sizeof(PawnStormEntry));
		ASSERT(shelter != NULL);
		ASSERT(storm != NULL);
	}
	~PawnInfo() {
		free(entry);
		free(shelter);
		free(storm);
	}
	void SendInfo() {
		float read_percent = 100.0 * (float)read_success / (float)read_total;
		float used_percent = 100.0 * (float)used / (float)cell_nb;
		printf("info pawnhash hit %.2f\n", read_percent);		
		printf("info pawnhash used %.2f\n", used_percent);
	}
	PawnEntry *GetInfo(Board *b, Personality *p);
	void InitShelter(Personality *p);
	void InitStorm(Personality *p);
};

PawnInfo pawn_info[MAX_PERSONS];

PawnEntry* PawnInfo::GetInfo(Board *b, Personality *p) {
	PawnEntry *ent = entry + (b->pawn_key & retrieve_mask);
	read_total++;
	if(ent->key == b->pawn_key)	{		
		read_success++;
		return ent;
	}
	#ifdef DEBUG_PAWN_INFO
		if(!ent->used) {
			ent->used = 1;
			used++;
		}
	#endif

	// compute pawn info	
	uint colour, sq, rank;	
	sint16 opening = 0,	endgame = 0;	
	BitBoard me,
		opp, pawns,		
		white_pawns, 
		black_pawns, 
		weak_pawns = 0,
		passed_pawns = 0, 
		candidate_mask_me, 
		candidate_mask_opp,
		all = b->piece[PAWN]; // pawns only		
	bool passed, isolated, doubled, chain,
		backward, candidate, sneaker;
	sint8 min_file = 8, max_file = -1, file;
		
	for(colour = WHITE; colour <= BLACK; colour++) {

		me = pawns = all & b->occupied_by[colour];
		opp = all & b->occupied_by[colour^1];		

		while(pawns) {				
			sq = ExtractFirstBit(pawns);
			rank = RANK_COL(sq, colour);
			
			file = File(sq);
			if(file > max_file)	max_file = file;
			if(file < min_file)	min_file = file;

			passed = !(AntiPassedPawns[colour][sq] & opp)
				&& !(HalfOpenFileMask[colour][sq] & me);
			doubled = DoubledPawnMask[colour][sq] & me;
			isolated = !(IsolatedPawnMask[sq] & me);			
			chain = ChainPawnMask[colour][sq] & me;
			
			backward = false;
			if(!chain
			&& !passed 
			&& !isolated 			 
			&& !(Pawn[colour][sq] & opp) 
			&& !(ProtectedPawnMask[colour][sq] & me)) {
				if((BackwardPawnMask1[colour][sq] & opp)
				|| ((BackwardPawnMask2[colour][sq] & opp)
					&& !(Pawn[colour][sq] & me)))			backward = true;
			}

			candidate = !passed 
				&& !(HalfOpenFileMask[colour][sq] & opp)
				&& (CountFirstBits(CandidatePawnMask[colour][sq] & me) 
				   >= CountFirstBits(CandidateOppPawnMask[colour][sq] & opp));

			sneaker = !passed 
				&& (rank == RANK_5)				
				&& !(HalfOpenFileMask[colour][sq] & opp)
				&& (((Pawn[colour][sq] & me & L_CONTACT_FILES_BB(sq)) && !(CandidateOppPawnMask[colour][sq] & opp & R_CONTACT_FILES_BB(sq)))
				  ||((Pawn[colour][sq] & me & R_CONTACT_FILES_BB(sq)) && !(CandidateOppPawnMask[colour][sq] & opp & L_CONTACT_FILES_BB(sq))));

			if(passed) {
				passed_pawns |= BIT(sq);
				MTI(colour, OP_PASSED_PAWN, EG_PASSED_PAWN, rank);
				if(chain)		MTI(colour, OP_DEFENDED_PASSED_PAWN, EG_DEFENDED_PASSED_PAWN, rank);
			
			} if(isolated) {				
				if(!(HalfOpenFileMask[colour][sq] & opp)) {		MSI(colour, OP_WEAK_ISOLATED_PAWN_PENALTY, EG_WEAK_ISOLATED_PAWN_PENALTY);
					weak_pawns |= BIT(sq);
				} else {										MSI(colour, OP_ISOLATED_PAWN_PENALTY, EG_ISOLATED_PAWN_PENALTY);					
				} if(doubled)									ESI(colour, EG_ISOLATED_DOUBLED_PAWN_PENALTY);			
			
			} if(backward) {				
				if(!(HalfOpenFileMask[colour][sq] & opp)) {		MSI(colour, OP_WEAK_BACKWARD_PAWN_PENALTY, EG_WEAK_BACKWARD_PAWN_PENALTY);
					weak_pawns |= BIT(sq);
				} else {										MSI(colour, OP_BACKWARD_PAWN_PENALTY, EG_BACKWARD_PAWN_PENALTY)	;}
				
			} if(doubled) {										ESI(colour, EG_DOUBLED_PAWN_PENALTY);
			} if(!chain) {										ESI(colour, EG_UNPROTECTED_PAWN_PENALTY);			
			} if(candidate)	{									MTI(colour, OP_CANDIDATE_PASSED_PAWN, EG_CANDIDATE_PASSED_PAWN, rank);
			} if(sneaker) {										ESI(colour, EG_SNEAKER)	;}
		}
	}

	// king safety
	white_pawns = b->piece[PAWN] & b->White();
	black_pawns = b->piece[PAWN] & b->Black();
	ent->shelter[WHITE][QUEENSIDE] = shelter[B1_TO_SHELTER(white_pawns)].value;
	ent->shelter[WHITE][KINGSIDE] = shelter[G1_TO_SHELTER(white_pawns)].value;
	ent->shelter[WHITE][CENTER] = shelter[E1_TO_SHELTER(white_pawns)].value;	
	ent->shelter[BLACK][CENTER] = shelter[E8_TO_SHELTER(black_pawns)].value;
	ent->shelter[BLACK][KINGSIDE] = shelter[G8_TO_SHELTER(black_pawns)].value;
	ent->shelter[BLACK][QUEENSIDE] = shelter[B8_TO_SHELTER(black_pawns)].value;	

	ent->shelter[WHITE][QUEENSIDE] -= storm[B1_TO_STORM(black_pawns)].value;
	ent->shelter[WHITE][KINGSIDE] -= storm[G1_TO_STORM(black_pawns)].value;
	ent->shelter[WHITE][CENTER] -= storm[E1_TO_STORM(black_pawns)].value;	
	ent->shelter[BLACK][CENTER] -= storm[E8_TO_STORM(white_pawns)].value;
	ent->shelter[BLACK][KINGSIDE] -= storm[G8_TO_STORM(white_pawns)].value;
	ent->shelter[BLACK][QUEENSIDE] -= storm[B8_TO_STORM(white_pawns)].value;

	// store
	ent->key = b->pawn_key;
	ent->opening = opening;
	ent->endgame = endgame;
	ent->weak_pawns = weak_pawns;
	ent->passed_pawns = passed_pawns;
	ent->central_file = CENTRAL_FILE_NO;
	if(ABS(max_file - min_file) <= 5) 
		ent->central_file = (min_file + max_file) / 2;
	ASSERT(ent->central_file >= FILE_A && ent->central_file <= CENTRAL_FILE_NO);
	
	return ent;
}

// Shelter and Storm Init
// calculates values for WHITE KING on g1
// other squares (b1 and e1 for white and a8,e8,g8 for black)
// initializes by calculated values
// e1 -> by shift
// b1 -> by mirroring (!!!)

void PawnInfo::InitShelter(Personality *p) {	
		
	int i, r1, r2, r3, sq, value, max_value, files;
	BitBoard key, pawns, pure, second;

	max_value = p->INNER_SIDE_SHELTER[0] + p->CENTRAL_SHELTER[0] + p->OUTER_SIDE_SHELTER[0];

	for(i = 0; i < PAWN_SHELTER_SIZE; i++) {
		key = (BitBoard)(i);
		pawns = SHELTER_TO_G1(key);
		pure = (pawns & RANK_BB(f2));
		pure |= (pawns & RANK_BB(f3)) & ~(pure << 8);
		pure |= (pawns & RANK_BB(f4)) & ~(pure << 8) & ~(pure << 16);
		pure |= (pawns & RANK_BB(f5)) & ~(pure << 8) & ~(pure << 16) & ~(pure << 24);
		second = pawns ^ pure;

		// value
		value = 0;		
		files = 0;
		r1 = r2 = r3 = -1;
		
		// inner file
		for(sq = f2; sq <= f5; sq += 8) {
			if(BIT(sq) & pure) {
				r1 = Rank(sq) - 1;				
				value += p->INNER_SIDE_SHELTER[r1];				
				files++;
			} else if(BIT(sq) & second) {
				value += p->SECOND_INNER_SIDE_SHELTER[Rank(sq)-1];
			}
		} // central file
		for(sq = g2; sq <= g5; sq += 8) {
			if(BIT(sq) & pure) {
				r2 = Rank(sq) - 1;				
				value += p->CENTRAL_SHELTER[r2];				
				files++;
			} else if(BIT(sq) & second) {
				value += p->SECOND_CENTRAL_SHELTER[Rank(sq)-1];
			}
		} // outer file
		for(sq = h2; sq <= h5; sq += 8) {
			if(BIT(sq) & pure) {
				r3 = Rank(sq) - 1;				
				value += p->OUTER_SIDE_SHELTER[r3];				
				files++;
			} else if(BIT(sq) & second) {
				value += p->SECOND_OUTER_SIDE_SHELTER[Rank(sq)-1];
			}
		}
		
		// weak back rank
		if(r1 == r2 == r3 == 0)		value -= (p->OUTER_SIDE_SHELTER[0] - p->OUTER_SIDE_SHELTER[1]);
		// too much open inner side
		if(r1 != 0 && r2 != 0)      value += (value * p->STORM_SHELTER_PENALTY_1) / MAX(max_value, 1);

		// store
		value = MAX(0, value);
		shelter[i].value = MIN(max_value, value);		
	}
}

void PawnInfo::InitStorm(Personality *p) {	
	
	int i, r1, r2, r3, sq, value, max_value, files;
	BitBoard key, pawns, pure;

	max_value = p->INNER_SIDE_STORM[1] + p->CENTRAL_STORM[1] + p->OUTER_SIDE_STORM[1];

	for(i = 0; i < PAWN_STORM_SIZE; i++) {
		key = (BitBoard)(i);
		pawns = STORM_TO_G1(key);
		pure = (pawns & RANK_BB(f2));
		pure |= (pawns & RANK_BB(f3)) & ~(pure << 8);
		pure |= (pawns & RANK_BB(f4)) & ~(pure << 8) & ~(pure << 16);
		pure |= (pawns & RANK_BB(f5)) & ~(pure << 8) & ~(pure << 16) & ~(pure << 24);

		// value
		value = 0;
		files = 0;
		// inner file
		for(r1 = 0, sq = f2; sq <= f5; sq += 8, r1++) 
			if(BIT(sq) & pure) {
				ASSERT(r1 < 4);
				value += p->INNER_SIDE_STORM[r1];
				files++;
				break;
			}
		// central file
		for(r2 = 0, sq = g2; sq <= g5; sq += 8, r2++) 
			if(BIT(sq) & pure) {
				ASSERT(r2 < 4);
				value += p->CENTRAL_STORM[r2];
				files++;
				break;
			}
		// outer file
		for(r3 = 0, sq = h2; sq <= h5; sq += 8, r3++) 
			if(BIT(sq) & pure) {
				ASSERT(r3 < 4);
				value += p->OUTER_SIDE_STORM[r3];
				files++;
				break;
			}	

		// cases
		// no pawns on file
		if(r1 == 4)		value += p->INNER_SIDE_STORM[4];
		if(r2 == 4)		value += p->CENTRAL_STORM[4];
		if(r3 == 4)		value += p->OUTER_SIDE_STORM[4];
		if(files <= 1)  value -= p->INNER_SIDE_STORM[4];

		// pawn chain
		if((pawns & (F5|G5|H5)) == (F5|G5|H5))	value += p->STORM_CHAIN;
		if(CountBits(pawns & Rank_4) == 3)		value += p->STORM_CHAIN;		
		
		// store
		value = MAX(0, value);
		storm[i].value = MIN(max_value, value);		
	}
}

