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


RobboTripleBase* TABLE_TRIPLE_BASES;
int NUM_TRIPLE_BASES = 0;
uint64 START_CLOCK;

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

void un_windows_triple (char *A)
{
  int s = strlen (A);
  if (A[1] == 'w')
    A[0] = A[2] - 32;
  if (A[1] == 'b')
    A[0] = A[2];
  if (s > 3 && A[3] == 'w')
    A[1] = A[4] - 32;
  if (s > 3 && A[3] == 'b')
    A[1] = A[4];
  if (s > 5 && A[5] == 'w')
    A[2] = A[6] - 32;
  if (s > 5 && A[5] == 'b')
    A[2] = A[6];
  if (s > 7 && A[7] == 'w')
    A[3] = A[8] - 32;
  if (s > 7 && A[7] == 'b')
    A[3] = A[8];
  A[s >> 1] = 0;
}

void hack_windows_triple (char* NOME) /* comply worthless name */
{
  char TEMP[16];
  int i;
  strcpy (TEMP, NOME);
  NOME[0] = 'K';
  for (i = 0; i < 4; i++)
    {
      if (TEMP[i] == 0)
        NOME[2 * i + 1] =  NOME[2 * i + 2] = 0;
      else if (TEMP[i] >= 'A' && TEMP[i] <= 'Z')
        {
          NOME[2 * i + 1] = 'w';
          NOME[2 * i + 2] = TEMP[i] - 'A' + 'a';
        }
      else
        {
          NOME[2 * i + 1] = 'b';
          NOME[2 * i + 2] = TEMP[i];
        }
    }
  NOME[9] = 0; /* ensure */
}

void TripleEnumerateReset ()
{
  int i;
  for (i = 0; i < NUM_TRIPLE_BASES; i++)
    (TABLE_TRIPLE_BASES + i)->weak = (TABLE_TRIPLE_BASES + i)->hit = 0;
}

int AkinRobboInformatory (int x)
{
  RobboTripleBase* triple;
  int i;
  SEND ("RobboInformatory\n");
  for (i = 0; i < NUM_TRIPLE_BASES; i++)
    {
      triple = (TABLE_TRIPLE_BASES + i);
      if (!triple->p[0])
	continue;
      if (triple->weak || triple->hit || triple->direct) /* HACK */
	SEND ("%s %s " TYPE_64_BIT " " TYPE_64_BIT " %s\n", triple->nome,
	      triple->direct ? "true" : "false", triple->weak, triple->hit,
	      (triple->variant & ROBBO_DYNAMIC_LOAD) && triple->w[2] ? "true" : "false");
    }
  SEND ("DesistRobboInformatory\n");	  
  return TRUE; /* HACK */
}

RobboTripleBase* LookupTriple (char* I)
{
  int v, p[4] = { 0, 0, 0, 0 }, i;
  int EFGH = -1, IJ1 = -1, IJ2 = -1;
  int file, file1, file2;
  RobboTripleBase* rtb;
  for (i = 0; i < 4 && i < strlen (I); i++)
    {
      switch (I[i])
	{
	case 'Q':
	  p[i] = wEnumQ;
	  break;
	case 'q':
	  p[i] = bEnumQ;
	  break;
	case 'R':
	  p[i] = wEnumR;
	  break;
	case 'r':
	  p[i] = bEnumR;
	  break;
	case 'B':
	  p[i] = wEnumB;
	  break;
	case 'b':
	  p[i] = bEnumB;
	  break;
	case 'N':
	  p[i] = wEnumN;
	  break;
	case 'n':
	  p[i] = bEnumN;
	  break;
        case 'E': case 'F': case 'G': case 'H':
          EFGH = i;
        case 'e': case 'f': case 'g': case 'h':
          EFGH = i;
	case 'P':
	  p[i] = wEnumP;
	  break;
	case 'p':
	  p[i] = bEnumP;
	  break;
        case 'U': case 'V': case 'W': case 'X':
          EFGH = i;
	case 'Z':
	  p[i] = BlockedPawn;
	  break;
        case 'I': case 'J':
          if (IJ1 == -1)
            IJ1 = i;
          else
            IJ2 = i;
          p[i] = wEnumP;
          break;
        case 'i': case 'j':
          if (IJ1 == -1)
            IJ1 = i;
          else
            IJ2 = i;
          p[i] = bEnumP;
          break;
	default:
	  return NULL; /* HACK */
	}
      switch (I[i])
        {
	  case 'E': case 'e': case 'U':
	    file = 3;
	    break;
	  case 'F': case 'f': case 'V':
	    file = 2;
	    break;
	  case 'G': case 'g': case 'W':
	    file = 1;
	    break;
	  case 'H': case 'h': case 'X':
	    file = 0;
	    break;
        }
      switch (I[i])
        {
	  case 'I':
	    file1 = 0;
	    break;
	  case 'J':
	    file1 = 1;
	    break;
	  case 'i':
	    file2 = 0;
	    break;
	  case 'j':
	    file2 = 1;
	    break;
        }
    }
  v = TRIPLE_BASE_TABLE[p[0]][p[1]][p[2]][p[3]];
  if (v == -1)
    return NULL;
  v &= 0xffff;
  rtb = TABLE_TRIPLE_BASES + v;
  if (EFGH != -1)
    v = rtb->efgh[file];
  if (IJ1 != -1)
    v = rtb->efgh[file1 + 2 * file2];
  return (TABLE_TRIPLE_BASES + v);
}

#define SAFER_FREE(x) if ((x)) free ((x))
#define SAFER_FREE_ALIGNED(x) if ((x)) ALIGNED_FREE ((x))
uint64 bytesTRIPLE, indTRIPLE, indTRIPLEx;
void CleanupTriple ()
{
  int i, j, k, l;
  RobboTripleBase* rtb;
  for (i = 0; i < NUM_TRIPLE_BASES; i++)
    {
      rtb = (TABLE_TRIPLE_BASES + i);
      SAFER_FREE (rtb->ind);
      SAFER_FREE (rtb->data);
      for (j = 0; j < 8; j++) /* heaps */
	{
	  SAFER_FREE_ALIGNED (rtb->direct_cache[j].DATA);
	  SAFER_FREE_ALIGNED (rtb->direct_cache[j].INDEX);
	  SAFER_FREE_ALIGNED (rtb->huffman_table[j]);
	  SAFER_FREE (rtb->ind_split[j]);
	  SAFER_FREE (rtb->packet_offset[j]);
	}
    }
  for (i = 0; i < 16; i++)
    for (j = 0; j < 16; j++)
      for (k = 0; k < 16; k++)
        for (l = 0; l < 16; l++)
          TRIPLE_BASE_TABLE[i][j][k][l] = -1;
  NUM_TRIPLE_BASES = 0;
  bytesTRIPLE = indTRIPLE = indTRIPLEx = 0;
  BULK_BYTES = BULK_IND = 0;
  BULK_COUNT = LOAD_COUNT = DIRECT_COUNT = 0;
  TRIPLE_MAX_USAGE = 0;
  TRIVIALLY_COUNT = 0;
}

void INIT_TRIPLE_BASE ()
{
  int i, j, k, l;
  for (i = 0; i < 16; i++)
    for (j = 0; j < 16; j++)
      for (k = 0; k < 16; k++)
	for (l = 0; l < 16; l++)
	  TRIPLE_BASE_TABLE[i][j][k][l] = -1;
  TABLE_TRIPLE_BASES = malloc (TRIPLE_MAX * sizeof (RobboTripleBase));
  InitInitTripleBaseCache (1);
}

void RobboTriple_delete (char* NOME)
{
  RobboTripleBase* triple;
  int heaps, h;
  triple = LookupTriple (NOME);
  if (triple == NULL)
    return;
  heaps = (triple->size >> 30) + 1;
  for (h = 0; h < heaps; h++)
    free (triple->ind_split[h]);
  for (h = 0; h < heaps; h++)
    free (triple->packet_offset[h]);
  /* efgh comply ? */
}

void robbo_triple_utilita (RobboTripleBase* rtb, char* iNOME)
{
  int i, p[4];
  char NOME[256];
  int file;
  int u, v, w;

  rtb->disk = NULL;
  rtb->direct = FALSE;
  for (i = 0; i < 8; i++)
    rtb->direct_cache[i].DATA = NULL;
  for (i = 0; i < 8; i++)
    rtb->direct_cache[i].INDEX = NULL;
  for (i = 0; i < 8; i++)
    rtb->huffman_table[i] = NULL;
  strcpy (NOME, iNOME);
  if (NOME[0] == 'K' || NOME[0]=='k')
    un_windows (NOME); /* consistent */
  strcpy (rtb->nome, NOME);
  for (i = 0; NOME[i]; i++);
  for ( ; i < 4; i++)
    NOME[i] = 0;    
  rtb->pawns = FALSE;
  rtb->efgh1 = -1;
  rtb->efgh2 = -1;
  for (i = 0; i < 4; i++)
    {
      rtb->efgh_shift[i] = 0;
      rtb->efgh_file[i] = -1;
      rtb->efgh[i] = 0xffff;
      switch (NOME[i])
	{
	  case 'Q':
	    p[i] = wEnumQ;
	    break;
	  case 'q':
	    p[i] = bEnumQ;
	    break;
	  case 'R':
	    p[i] = wEnumR;
	    break;
	  case 'r':
	    p[i] = bEnumR;
	    break;
	  case 'B':
	    p[i] = wEnumB;
	    break;
	  case 'b':
	    p[i] = bEnumB;
	    break;
	  case 'N':
	    p[i] = wEnumN;
	    break;
	  case 'n':
	    p[i] = bEnumN;
	    break;
          case 'I': case 'J':
	    rtb->efgh1 = i;
	    p[i] = wEnumP;
	    break;
          case 'E': case 'F': case 'G': case 'H':
	    rtb->efgh1 = i;
	  case 'P':
	    p[i] = wEnumP;
	    break;
          case 'i': case 'j':
	    rtb->efgh2 = i; /* HACK: anterior efgh1 */
	    p[i] = bEnumP;
            break;
          case 'e': case 'f': case 'g': case 'h':
	    rtb->efgh1 = i;
	  case 'p':
	    p[i] = bEnumP;
	    break;
          case 'U': case 'V': case 'W': case 'X':
	    rtb->efgh1 = i;
	  case 'Z':
	    p[i] = BlockedPawn;
	    break;
	  default:
	    p[i] = 0;
	}
      switch (NOME[i])
        {
	  case 'E': case 'e': case 'U':
	    file = 3;
	    break;
	  case 'F': case 'f': case 'V':
	    file = 2;
	    break;
	  case 'G': case 'g': case 'W': case 'J': case 'j':
	    file = 1;
	    break;
	  case 'H': case 'h': case 'X': case 'I': case 'i':
	    file = 0;
        }
      if (p[i])
	{
	  if (p[i] == wEnumP || p[i] == bEnumP)
	    rtb->m[i] = 48;
	  else if (p[i] == BlockedPawn)
	    rtb->m[i] = 40;
	  else
	    rtb->m[i] = 64;
	  if (p[i] == wEnumP || p[i] == bEnumP || p[i] == BlockedPawn)
	    rtb->pawns = TRUE;
          if (rtb->efgh2 == i) /* HACK: anterior efgh1 */
            {
              rtb->m[i] >>= 1;
              rtb->efgh_shift[i] = 1;
              rtb->efgh_file[i] = file;
	      if (rtb->efgh1 != -1) /* Fact: anew consorts */
		{
		  rtb->m[rtb->efgh1] <<= 1;
		  rtb->efgh_shift[rtb->efgh1] = 1;
		  file = 2 * file + rtb->efgh_file[rtb->efgh1];
		}
            }
          else if (rtb->efgh1 == i)
            {
	      if (rtb->efgh2 != -1) /* Fact: deride nascents */
		{
		  rtb->m[i] >>= 1;
		  rtb->efgh_shift[i] = 1;
		  rtb->efgh_file[i] = file;
		  file = 2 * rtb->efgh_file[rtb->efgh2] + file;
		}
	      else
		{
		  rtb->m[i] >>= 2;
		  rtb->efgh_shift[i] = 2;
		  rtb->efgh_file[i] = file;
		}
            }
	}
      else
	rtb->m[i] = 1;
      rtb->p[i] = p[i];
    }
  if (p[0] && p[0] == p[3])
    {
      rtb->m[0] =(rtb->m[0] * (rtb->m[0] - 1) *
		  (rtb->m[0] - 2) * (rtb->m[0] - 3)) / 24;
      rtb->m[1] = rtb->m[2] = rtb->m[3] = 1;
    }
  else if (p[0] && p[0] == p[2])
    {
      rtb->m[0] = (rtb->m[0] * (rtb->m[0] - 1) * (rtb->m[0] - 2)) / 6;
      rtb->m[1] = rtb->m[2] = 1;
    }
  else if (p[1] && p[1] == p[3])
    {
      rtb->m[1] = (rtb->m[1] * (rtb->m[1] - 1) * (rtb->m[1] - 2)) / 6;
      rtb->m[2] = rtb->m[3] = 1;
    }
  else
    {
      if (p[0] && p[0] == p[1])
        {
          rtb->m[0] = (rtb->m[0] * (rtb->m[0] - 1)) / 2;
          rtb->m[1] = 1;
        }
      if (p[1] && p[1] == p[2])
        {
          rtb->m[1] = (rtb->m[1] * (rtb->m[1] - 1)) / 2;
          rtb->m[2] = 1;
        }
      if (p[2] && p[2] == p[3])
        {
          rtb->m[2] = (rtb->m[2] * (rtb->m[2] - 1)) / 2;
          rtb->m[3] = 1;
        }
    }

  rtb->num = NUM_TRIPLE_BASES;
  if (TRIPLE_BASE_TABLE[p[0]][p[1]][p[2]][p[3]] != -1)
    goto EFGH_ij_FIX;
  TRIPLE_BASE_TABLE[p[0]][p[1]][p[2]][p[3]] =
    NUM_TRIPLE_BASES + (0 << 16) + (1 << 18) + (2 << 20) + (3 << 22);
  TRIPLE_BASE_TABLE[p[1]][p[0]][p[2]][p[3]] =
    NUM_TRIPLE_BASES + (1 << 16) + (0 << 18) + (2 << 20) + (3 << 22);
  TRIPLE_BASE_TABLE[p[0]][p[2]][p[1]][p[3]] =
    NUM_TRIPLE_BASES + (0 << 16) + (2 << 18) + (1 << 20) + (3 << 22);
  TRIPLE_BASE_TABLE[p[1]][p[2]][p[0]][p[3]] =
    NUM_TRIPLE_BASES + (2 << 16) + (0 << 18) + (1 << 20) + (3 << 22);
  TRIPLE_BASE_TABLE[p[2]][p[0]][p[1]][p[3]] =
    NUM_TRIPLE_BASES + (1 << 16) + (2 << 18) + (0 << 20) + (3 << 22);
  TRIPLE_BASE_TABLE[p[2]][p[1]][p[0]][p[3]] =
    NUM_TRIPLE_BASES + (2 << 16) + (1 << 18) + (0 << 20) + (3 << 22);

  TRIPLE_BASE_TABLE[p[0]][p[1]][p[3]][p[2]] =
    NUM_TRIPLE_BASES + (0 << 16) + (1 << 18) + (3 << 20) + (2 << 22);
  TRIPLE_BASE_TABLE[p[0]][p[2]][p[3]][p[1]] =
    NUM_TRIPLE_BASES + (0 << 16) + (3 << 18) + (1 << 20) + (2 << 22);
  TRIPLE_BASE_TABLE[p[1]][p[0]][p[3]][p[2]] =
    NUM_TRIPLE_BASES + (1 << 16) + (0 << 18) + (3 << 20) + (2 << 22);
  TRIPLE_BASE_TABLE[p[1]][p[2]][p[3]][p[0]] =
    NUM_TRIPLE_BASES + (3 << 16) + (0 << 18) + (1 << 20) + (2 << 22);
  TRIPLE_BASE_TABLE[p[2]][p[0]][p[3]][p[1]] =
    NUM_TRIPLE_BASES + (1 << 16) + (3 << 18) + (0 << 20) + (2 << 22);
  TRIPLE_BASE_TABLE[p[2]][p[1]][p[3]][p[0]] =
    NUM_TRIPLE_BASES + (3 << 16) + (1 << 18) + (0 << 20) + (2 << 22);

  TRIPLE_BASE_TABLE[p[0]][p[3]][p[1]][p[2]] =
    NUM_TRIPLE_BASES + (0 << 16) + (2 << 18) + (3 << 20) + (1 << 22);
  TRIPLE_BASE_TABLE[p[0]][p[3]][p[2]][p[1]] =
    NUM_TRIPLE_BASES + (0 << 16) + (3 << 18) + (2 << 20) + (1 << 22);
  TRIPLE_BASE_TABLE[p[1]][p[3]][p[0]][p[2]] =
    NUM_TRIPLE_BASES + (2 << 16) + (0 << 18) + (3 << 20) + (1 << 22);
  TRIPLE_BASE_TABLE[p[1]][p[3]][p[2]][p[0]] =
    NUM_TRIPLE_BASES + (3 << 16) + (0 << 18) + (2 << 20) + (1 << 22);
  TRIPLE_BASE_TABLE[p[2]][p[3]][p[0]][p[1]] =
    NUM_TRIPLE_BASES + (2 << 16) + (3 << 18) + (0 << 20) + (1 << 22);
  TRIPLE_BASE_TABLE[p[2]][p[3]][p[1]][p[0]] =
    NUM_TRIPLE_BASES + (3 << 16) + (2 << 18) + (0 << 20) + (1 << 22);

  TRIPLE_BASE_TABLE[p[3]][p[0]][p[1]][p[2]] =
    NUM_TRIPLE_BASES + (1 << 16) + (2 << 18) + (3 << 20) + (0 << 22);
  TRIPLE_BASE_TABLE[p[3]][p[0]][p[2]][p[1]] =
    NUM_TRIPLE_BASES + (1 << 16) + (3 << 18) + (2 << 20) + (0 << 22);
  TRIPLE_BASE_TABLE[p[3]][p[1]][p[0]][p[2]] =
    NUM_TRIPLE_BASES + (2 << 16) + (1 << 18) + (3 << 20) + (0 << 22);
  TRIPLE_BASE_TABLE[p[3]][p[1]][p[2]][p[0]] =
    NUM_TRIPLE_BASES + (3 << 16) + (1 << 18) + (2 << 20) + (0 << 22);
  TRIPLE_BASE_TABLE[p[3]][p[2]][p[0]][p[1]] =
    NUM_TRIPLE_BASES + (2 << 16) + (3 << 18) + (1 << 20) + (0 << 22);
  TRIPLE_BASE_TABLE[p[3]][p[2]][p[1]][p[0]] =
    NUM_TRIPLE_BASES + (3 << 16) + (2 << 18) + (1 << 20) + (0 << 22);
  if (rtb->efgh1 != -1)
    rtb->efgh[file] = NUM_TRIPLE_BASES;
  NUM_TRIPLE_BASES++;
  return;

 EFGH_ij_FIX:
  v = TRIPLE_BASE_TABLE[p[0]][p[1]][p[2]][p[3]] & 0xffff;
  for (u = 0; u < 4; u++)
    if (((w = (TABLE_TRIPLE_BASES + v)->efgh[u])) != 0xffff)
      {
        (TABLE_TRIPLE_BASES + w)->efgh[file] = NUM_TRIPLE_BASES;
        rtb->efgh[u] = w;
      }
  rtb->efgh[file] = NUM_TRIPLE_BASES;
  NUM_TRIPLE_BASES++;  
}
