
#include "RobboLito.h"
#include "RobboTripleBase.h"
#include <string.h>
#include "RobboTotalBase.h"

#define wEnumB wEnumBL
#define bEnumB bEnumBL

static const uint8 NX[4][2] = { {0, 0}, {2, 3}, {1, 3}, {1, 2} };
extern MUTEX_TYPE TRIPLE_BASE_USAGE[1];

typedef struct
{
  int byte, bit;
  uint8* data;
} BitString;

#define NewBitString(bs, Data) { bs->byte = bs->bit = 0; bs->data = Data; }

static uint8 NextByteDecoded (BitString* BS, uint32* HUFFMAN) /* cap limit 16 */
{
  uint32 t, va = BS->data[BS->byte] >> BS->bit;
  if (BS->byte + 1 < 0x40) /* overrun */
    va |= (((uint32) (BS->data[BS->byte + 1])) << 8) >> BS->bit;
  t = HUFFMAN[va & 0xff];
  if (t >= (1 << 24))
    {
      BS->bit += (t >> 24);
      BS->byte += (BS->bit >> 3);
      BS->bit &= 0x7;
      return (t & 0xff);
    }
  if (BS->byte + 2 < 0x40) /* overrun */
    va |= (((uint32) (BS->data[BS->byte + 2])) << 16) >> BS->bit;
  t = HUFFMAN[0x100 * (t + 1) + ((va >> 8) & 0xff)];
  BS->bit += (t >> 24);
  BS->byte += (BS->bit >> 3);
  BS->bit &= 0x7;
  return (t & 0xff);
}

static int GetDataHuffman
 (uint8* DATA, int ba, uint8* AB, uint8* A, int PR, int sb, uint32* HUFFMAN)
{
  /* GetData from 64-byte array E, or 16-byte if so */
  /* sb is starting WLD, AB is TripleBase->WLD_mapping and A the inverse */
  /* ba is the index we demand, PR is 64 or 16 as the limit */
  int n, b, i = 0, r = A[sb];
  uint8 ch, tr;
  BitString BS[1];
  NewBitString (BS, DATA);
  while (ba >= 0 && BS->byte < PR)
    {
      ch = NextByteDecoded (BS, HUFFMAN);
      tr = (ch & 64) >> 6; /* caret for off result */
      n = ch & 31; /* run of size n unless larger */
      b = 0;
      if (ch & 32) /* flip size one */
        {
          if (!ba)
            return AB[NX[r][tr]];
          else
            ba--; /* size one */
        }
      else
        r = NX[r][tr]; /* update result */
      while (ch & 128) /* large run */
        {
          b++;
          ch = NextByteDecoded (BS, HUFFMAN);
          n += ((ch & 127) << (7 * b - 2));
        }
      ba -= n; /* size n */
    }
  return AB[r];
}

static int GetData
 (uint8* E, int ba, uint8* AB, uint8* A, int PR, int sb, uint32* HUFFMAN_TABLE)
{
  /* GetData from 64-byte array E, or 16-byte if so */
  /* sb is starting WLD, AB is TripleBase->WLD_mapping and A the inverse */
  /* ba is the index we demand, PR is 64 or 16 as the limit */
  int n, b, i = 0, r = A[sb];
  uint8 ch, tr;
  if (HUFFMAN_TABLE) /* HACK */ /* compiler */
    return GetDataHuffman (E, ba, AB, A, PR, sb, HUFFMAN_TABLE);
  while (ba >= 0 && i < PR) /* limit at 64 or when index decrements past 0 */
    {
      ch = E[i++];
      tr = (ch & 64) >> 6; /* caret for off result */
      n = ch & 31; /* run of size n unless larger */
      b = 0;
      if (ch & 32) /* flip size one */
        {
          if (!ba)
            return AB[NX[r][tr]];
          else
            ba--; /* size one */
        }
      else
        r = NX[r][tr]; /* update result */
      while (ch & 128) /* large run */
        {
          b++;
          ch = E[i++];
          n += ((ch & 127) << (7 * b - 2));
        }
      ba -= n; /* size n */
    }
  return AB[r];
}

static boolean Dynamic256kbBlockLookup 
(RobboTripleBase* rtb, int heap, uint64 ind, int w, int* res, boolean PROBING_WEAK, boolean PROBING_IMPALE)
{
  int f, i;
  uint32* I;
  uint8* D;
  uint32 z, cmp;
  TripleBaseCache* CACHE;
  if (!DemandDynamicTripleCache (rtb, heap, w, &CACHE, &D, &I, PROBING_WEAK, PROBING_IMPALE))
    return FALSE;
  z = 8;
  cmp = I[z] >> 2;
  z += ((ind > cmp) - (ind < cmp)) << 2;
  cmp = I[z] >> 2;
  z += ((ind > cmp) - (ind < cmp)) << 1;
  cmp = I[z] >> 2;
  z += ((ind > cmp) - (ind < cmp)) << 0;
  cmp = I[z] >> 2;
  z -= (ind < cmp);
  I += I[z + 16]; /* jump */ /* upper index 64 and 64 bytes */
  f = (z << 14); /* HACK */ /* rtb->prop_ind demands 64 */
  z = 8;
  cmp = I[z] >> 2;
  z += ((ind > cmp) - (ind < cmp)) << 2;
  cmp = I[z] >> 2;
  z += ((ind > cmp) - (ind < cmp)) << 1;
  cmp = I[z] >> 2;
  z += ((ind > cmp) - (ind < cmp)) << 0;
  cmp = I[z] >> 2;
  z -= (ind < cmp);
  I += I[z + 16]; /* jump */ /* 16 upper indexed, 128 bytes = 2k */
  f += (z << 10);
  z = 8;
  cmp = I[z] >> 2;
  z += ((ind > cmp) - (ind < cmp)) << 2;
  cmp = I[z] >> 2;
  z += ((ind > cmp) - (ind < cmp)) << 1;
  cmp = I[z] >> 2;
  z += ((ind > cmp) - (ind < cmp)) << 0;
  cmp = I[z] >> 2;
  z -= (ind < cmp);
  if (ind == 0x3fffffff) /* HACK */
    while ((I[z] >> 2) == 0x3fffffff)
      z--;
  f += (z << 6); /* 256 lower indexed, 64 bytes per accumulative 16k */
  *res = GetData (D + f, ind - (I[z] >> 2), rtb->w, rtb->wi, 64, I[z] & 3,
		  rtb->huffman_table[heap]);
  if (!rtb->direct)
    {
      LOCK (TRIPLE_BASE_USAGE);
      CACHE->user--;
      UNLOCK (TRIPLE_BASE_USAGE);
    }
  return TRUE;
}

static boolean RobboDynamic_value
(RobboTripleBase* rtb, uint64 ind, int* res, boolean PROBING_WEAK, boolean PROBING_IMPALE)
{
  int heap, l, w;
  uint32* I;
  heap = ind >> 30; /* conformed */
  ind &= 0x3fffffff;
  l = rtb->bs_split[heap];
  I = rtb->ind_split[heap];
  if (l == -1)
    w = 0;
  else
    {
      w = (2 << l) - 1;
      while (l >= 0)
        {
          uint32 u = (I[w] >> 2);
          w += ((ind > u) - (ind < u)) << l; /* HACK */
          l--;
        }
      while (ind >= (I[w] >> 2))
        w++;
      w--;
    }
  return Dynamic256kbBlockLookup (rtb, heap, ind, w, res, PROBING_WEAK, PROBING_IMPALE);
}

static boolean RobboTriple_valu
(RobboTripleBase* rtb, uint64 ind, int* res, boolean PROBING_WEAK, boolean PROBING_IMPALE)
{
  int w = 0, l, u;
  if (rtb->w[2] == 0)
    {    
      *res = (rtb->w[1]); 
      return TRUE;
    }
  if (rtb->variant & ROBBO_DYNAMIC_LOAD)
    return RobboDynamic_value (rtb, ind, res, PROBING_WEAK, PROBING_IMPALE);
  l = rtb->bs;
  if (l == -1)
    w = 0;
  else /* find index in index heap */
    {
      w = (2 << l) - 1;
      while (l >= 0)
        {
          u = rtb->ind[w] >> 2;
          w += ((ind > u) - (ind < u)) << l; /* HACK */
          l--;
        }
      while (ind >= (rtb->ind[w] >> 2))
        w++;
      w--;
    }
  *res = GetData ((rtb->data) + (rtb->prop_ind * w),
		  ind - (rtb->ind[w] >> 2), rtb->w,
		  rtb->wi, rtb->prop_ind, rtb->ind[w] & 3, NULL);
  return TRUE;
}

/************************/ /************************/ /************************/

static unsigned char nMUNE[64];
static unsigned char alfMUNE[64];
static unsigned char nepedMUNE[64];
static unsigned char bipedMUNE[64];
static unsigned char BLOCKED_MUNE[64];
static char ENUMn[65] =
  { A1, A2, B1, B2, A3, A4, B3, B4, C1, C2, D1, D2, C3, C4, D3, D4,
    A8, A7, B8, B7, A6, A5, B6, B5, C8, C7, D8, D7, C6, C5, D6, D5,
    H1, H2, G1, G2, H3, H4, G3, G4, F1, F2, E1, E2, F3, F4, E3, E4,
    H8, H7, G8, G7, H6, H5, G6, G5, F8, F7, E8, E7, F6, F5, E6, E5, -1 };

static char ENUM_BI_PED[49] =
  { A2, B2, C2, D2, E2, F2, G2, H2, A3, B3, C3, D3, E3, F3, G3, H3,
    A4, B4, C4, D4, E4, F4, G4, H4, A5, B5, C5, D5, E5, F5, G5, H5,
    A6, B6, C6, D6, E6, F6, G6, H6, A7, B7, C7, D7, E7, F7, G7, H7, -1 };

static char ENUM_NE_PED[49] =
  { A7, B7, C7, D7, E7, F7, G7, H7, A6, B6, C6, D6, E6, F6, G6, H6,
    A5, B5, C5, D5, E5, F5, G5, H5, A4, B4, C4, D4, E4, F4, G4, H4,
    A3, B3, C3, D3, E3, F3, G3, H3, A2, B2, C2, D2, E2, F2, G2, H2, -1 };

static char ENUM_ALF[65] =
  { A1, A3, B2, C1, A5, B4, C3, D2, E1, A7, B6, C5, D4, E3, F2, G1,
    B8, C7, D6, E5, F4, G3, H2, D8, E7, F6, G5, H4, F8, G7, H6, H8,
    H1, H3, G2, F1, H5, G4, F3, E2, D1, H7, G6, F5, E4, D3, C2, B1,
    G8, F7, E6, D5, C4, B3, A2, E8, D7, C6, B5, A4, C8, B7, A6, A8, -1 };

static char ENUM_BLOCKED[41] =
  { A2, B2, C2, D2, E2, F2, G2, H2, A3, B3, C3, D3, E3, F3, G3, H3,
    A4, B4, C4, D4, E4, F4, G4, H4, A5, B5, C5, D5, E5, F5, G5, H5,
    A6, B6, C6, D6, E6, F6, G6, H6, -1 };

int TRIPLE_BASE_TABLE[16][16][16][16];

#define reQU(x) \
  { if (P[x] == wEnumP) Q[x] = bipedMUNE[Q[x]]; \
    else if (P[x] == bEnumP) Q[x] = nepedMUNE[Q[x]]; \
    else if (P[x] == BlockedPawn) Q[x] = BLOCKED_MUNE[Q[x]]; \
    else if (P[x] == wEnumB || P[x] == bEnumB) Q[x] = alfMUNE[Q[x]]; \
    else Q[x] = nMUNE[Q[x]]; } /* multiple array? */

static boolean TripleIndex (type_PiSq* PISQ, int* tb_num, uint64* ind)
{
  int v, i, x, K1, K2, P[4], Q[4];
  int rf;
  RobboTripleBase* rtb;
  if (PISQ->wtm)
    {
      K1 = PISQ->wK;
      K2 = PISQ->bK;
    }
  else
    {
      K1 = PISQ->bK;
      K2 = PISQ->wK;
      for (i = 0; i < 4; i++)
	PISQ->pi[i] = col_scamb[PISQ->pi[i]];
    }
  v = TRIPLE_BASE_TABLE[PISQ->pi[0]][PISQ->pi[1]][PISQ->pi[2]][PISQ->pi[3]];
  if ((v % 65536) == -1)
    return FALSE;
  *tb_num = v % 65536;
  rtb = TABLE_TRIPLE_BASES + (v % 65536);
  if (rtb->w[2] == 0) /* una resulta */
    return TRUE;
  for (i = 0; i < 4; i++)
    {
      x = (v >> (16 + 2 * i)) & 3;
      Q[i] = PISQ->sq[x];
      P[i] = PISQ->pi[x];
    }
  if (PISQ->pedone)
    {
      int w = (2 * (FILE (K2) >= FE)) + (!PISQ->wtm);
      for (i = 0; i < 4; i++)
	{
	  LANCpez (w, Q[i]);
	  if (P[i] == BlockedPawn && !PISQ->wtm)
            Q[i] -= 8;
	}
      LANCpez (w, K1);
      LANCpez (w, K2);
      rf = RE_pedone[64 * K1 + K2];
    }
  else
    {
      uint8 w = oLANC[K1][K2];
      for (i = 0; i < 4; i++)
	LANCnor (w, Q[i]);
      LANCnor (w, K1);
      LANCnor (w, K2);
      rf = RE_normale[64 * K1 + K2];
    }
  if (rf == -1)
    return FALSE;
  for (i = 0; i < 4; i++)
    reQU (i);
  for (i = 0; i < 4; i++)
    if (!P[i])
      Q[i] = 0;
#if 1 /* efgh */ /* FACT: efgh/ij compound the selfsame! Retain upper stubs! */
  if (rtb->efgh2 != -1)
    {
      int f1 = FILE (Q[rtb->efgh1]);
      int f2 = FILE (Q[rtb->efgh2]);
      f1 = MIN (f1, (FH - f1)) & 1;
      f2 = MIN (f2, (FH - f2)) & 1;
      *tb_num = rtb->efgh[f1 + 2 * f2];
    }
  else if (rtb->efgh1 != -1)
    {
      int f = FILE (Q[rtb->efgh1]);
      *tb_num = rtb->efgh[MIN (f, (FH - f))];
    }
  if (*tb_num == -1 || *tb_num == 0xffff)
    return FALSE; /* HACK theprez */
  for (i = 0; i < 4; i++)
    Q[i] >>= rtb->efgh_shift[i];
#endif
/* 6 piece functionality */
  if (P[0] == P[3] && P[3])
    {
      int m0, m1, m2, m3, t0, t1, u0, u1;
      t0 = MAX (Q[0], Q[1]);
      u0 = MIN (Q[0], Q[1]);
      t1 = MAX (Q[2], Q[3]);
      u1 = MIN (Q[2], Q[3]);
      m0 = MAX (t0, t1);
      m1 = MIN (t0, t1);
      m2 = MAX (u0, u1);
      m3 = MIN (u0, u1);
      if (m1 < m2) SCAMB (m1, m2);
      Q[0]= (m0 * (m0 - 1) * (m0 - 2) * (m0 - 3)) / 24 +
        (m1 * (m1 - 1) * (m1 - 2)) / 6 + (m2 * (m2 - 1)) / 2 + m3;
      Q[1] = Q[2] = Q[3] = 0;
    }
  else if (P[0] == P[2] && P[2])
    {
      Q[0] = STESSO3[Q[0]][Q[1]][Q[2]];
      Q[1] = Q[2] = 0;
    }
  else if (P[1] == P[3] && P[3])
    {
      Q[1] = STESSO3[Q[1]][Q[2]][Q[3]];
      Q[2] = Q[3] = 0;
    }
  else
    {
      if (P[0] == P[1] && P[1])
        {
          Q[0] = STESSO2[Q[0]][Q[1]];
          Q[1] = 0;
        }
      if (P[1] == P[2] && P[2])
        {
          Q[1] = STESSO2[Q[1]][Q[2]];
          Q[2] = 0;
        }
      if (P[2] == P[3] && P[3])
        {
          Q[2] = STESSO2[Q[2]][Q[3]];
          Q[3] = 0;
        }
    }  
  *ind = ((((rf * rtb->m[0]) + Q[0]) * rtb->m[1] + Q[1])
	  * rtb->m[2] + Q[2]) * rtb->m[3] + Q[3];
  return TRUE;
}


static RobboTripleBase* ThisTripleBase (typePOS* POSITION)
{
  type_PiSq PiSq[1];
  boolean b;
  int i, v;
  uint64 U, V, W;
  PiSq->pi[0] = PiSq->pi[1] = PiSq->pi[2] = PiSq->pi[3] = 0;
  PiSq->sq[0] = PiSq->sq[1] = PiSq->sq[2] = PiSq->sq[3] = 0;
  U = wBitboardP & (bBitboardP >> 8);
  V = (U << 1) & U & ~FILEa;
  W = (V << 1) & V;
  PiSq->Blocked = (U & ~V) | (W & ~((W << 1) & W));
  b = BITBORDO_PQ (POSITION, PiSq);
  if (POSITION->wtm)
    for (i = 0; i < 4; i++)
      PiSq->pi[i] = col_scamb[PiSq->pi[i]];
  v = TRIPLE_BASE_TABLE[PiSq->pi[0]][PiSq->pi[1]][PiSq->pi[2]][PiSq->pi[3]];
  if ((v % 65536) != -1)
    return TABLE_TRIPLE_BASES + (v % 65536);
  while (PiSq->Blocked && PiSq->n < 4)
    {
      BitClear (0, PiSq->Blocked);
      b = BITBORDO_PQ (POSITION, PiSq);
      if (POSITION->wtm)
	for (i = 0; i < 4; i++)
	  PiSq->pi[i] = col_scamb[PiSq->pi[i]];
      v = TRIPLE_BASE_TABLE[PiSq->pi[0]][PiSq->pi[1]][PiSq->pi[2]][PiSq->pi[3]];
      if ((v % 65536) != -1)
	return TABLE_TRIPLE_BASES + (v % 65536);
    }
  return NULL;
}

static boolean triple_opera (typePOS *POSITION, int *r, boolean PROBING_WEAK, boolean PROBING_IMPALE)
{
  int tb;
  uint64 ind, U, V, W;
  type_PiSq PiSq[1];
  boolean b;
  PiSq->pi[0] = PiSq->pi[1] = PiSq->pi[2] = PiSq->pi[3] = 0;
  PiSq->sq[0] = PiSq->sq[1] = PiSq->sq[2] = PiSq->sq[3] = 0;
  U = wBitboardP & (bBitboardP >> 8);
  V = (U << 1) & U & ~FILEa;
  W = (V << 1) & V;
  PiSq->Blocked = (U & ~V) | (W & ~((W << 1) & W));
  b = BITBORDO_PQ (POSITION, PiSq);
  if (b)
    b = TripleIndex (PiSq, &tb, &ind);
  if (b)
    {
      RobboTripleBase* rtb = (TABLE_TRIPLE_BASES + tb);
      if (rtb->w[2] == 0)
	*r = 2 - rtb->w[1];
      else
	{
	  if (!RobboTriple_valu (rtb, ind, r, PROBING_WEAK, PROBING_IMPALE))
	    return FALSE;
	  *r = 2 - (*r);
	}
      POSITION->tbhits++;
      return TRUE;
    }
  while (PiSq->Blocked && PiSq->n < 4)
    {
      BitClear (0, PiSq->Blocked);
      b = BITBORDO_PQ (POSITION, PiSq);
      if (b)
	b = TripleIndex (PiSq, &tb, &ind);
      if (b)
	{
	  RobboTripleBase* rtb = (TABLE_TRIPLE_BASES + tb);
	  if (rtb->w[2] == 0)
	    *r = 2 - rtb->w[1];
	  else
	    {
	      if (!RobboTriple_valu (rtb, ind, r, PROBING_WEAK, PROBING_IMPALE))
		return FALSE;
	      *r = 2 - (*r);
	    }
	  POSITION->tbhits++;
	  return TRUE;
	}      
    }
  return FALSE;
}

#define vaVIT 1
#define vaPAT 0
#define vaPER -1

#define IN_CHECK \
  (POSITION->wtm ? \
   (wBitboardK & POSITION->DYN->bAtt) : (bBitboardK & POSITION->DYN->wAtt) )
#define IS_ILLEGAL \
  (!POSITION->wtm ? \
   (wBitboardK & POSITION->DYN->bAtt) : (bBitboardK & POSITION->DYN->wAtt) )

boolean TRIPLE_VALUE (typePOS* POSITION, int* Value, boolean PROBING_WEAK, boolean PROBING_IMPALE)
{
  int r = vaPER, b = vaPER, i, move;
  typeMoveList LIST[256], *list;
  if (POSITION->DYN->oo) /* allow ep */
    return FALSE;
  if (IS_ILLEGAL)
    return FALSE;
  if (IN_CHECK)
    {
      RobboTripleBase* rtb = ThisTripleBase (POSITION);
      if (!rtb || !rtb->scacco)
	goto NO_SCACCO;
      list = EvasionMoves (POSITION, LIST, 0xffffffffffffffff);
      if (list == LIST)
	{
	  *Value = vaPER;
	  return TRUE;
	}
      for (i = 0; i < list - LIST; i++)
	{
	  move = LIST[i].move & 0x7fff;
	  Make (POSITION, move);
	  Mobility (POSITION);
	  if (IS_ILLEGAL)
	    {
	      Undo (POSITION, move);
	      continue;
	    }
	  if (!TRIPLE_VALUE (POSITION, &r, PROBING_WEAK, PROBING_IMPALE))
	    {
	      Undo (POSITION, move);
	      return FALSE;
	    }
	  r = -r;
	  Undo (POSITION ,move);
	  if (r == vaVIT)
	    {
	      *Value = vaVIT;
	      return TRUE;
	    }
	  if (r == vaPAT)
	    b = vaPAT;
	}
      *Value = b;
      return TRUE;
    }
 NO_SCACCO:
  list = CaptureMoves (POSITION, LIST, 0xffffffffffffffff); /* attends ep */
  for (i = 0; i < list - LIST; i++)
    {
      move = LIST[i].move & 0x7fff;
      if ((move >> 12) == 0x4) /* N */
	continue;
      Make (POSITION, move);
      Mobility (POSITION);
      if (!IS_ILLEGAL)
	{
	  if (!TRIPLE_VALUE (POSITION, &r, PROBING_WEAK, PROBING_IMPALE))
	    {
	      Undo (POSITION, move);
	      return FALSE;
	    }
	  r = -r;
	}
      Undo (POSITION, move);
      if (r == vaVIT)
	{
	  *Value = r;
	  return TRUE;
	}
      if (r == vaPAT)
	b = vaPAT;
    }
  if (!triple_opera (POSITION, &r, PROBING_WEAK, PROBING_IMPALE))
    return FALSE;
  if (b > r)
    *Value = b;
  else
    *Value = r;
  return TRUE;
}

void READY_TRIPLE_BASE ()
{
  int i;
  for (i = A1; i <= H8; i++)
    nMUNE[ENUMn[i]] = i;
  for (i = A1; i <= H8; i++)
    alfMUNE[ENUM_ALF[i]] = i;
  for (i = 0; i < 48; i++)
    bipedMUNE[ENUM_BI_PED[i]] = i;
  for (i = 0; i < 48; i++)
    nepedMUNE[ENUM_NE_PED[i]] = i;
  for (i = 0; i < 40; i++)
    BLOCKED_MUNE[ENUM_BLOCKED[i]] = i;
  INIT_TRIPLE_BASE ();
}
