/*
This Software is distributed with the following X11 License,
sometimes also known as MIT license.
 
Copyright (c) 2010 Miguel A. Ballicora

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
*/


/* NBBOTF will remove the internal bitbase on the fly */
#ifdef NBBOTF
	#ifdef WDL_PROBE
		#undef WDL_PROBE
	#endif
#else
	#define WDL_PROBE
#endif

/*-- Intended to be modified to make public --> Supporting functions the TB generator ---------------------*/

#ifdef GTB_SHARE
#define SHARED_forbuilding
#endif

/*---------------------------------------------------------------------------------------------------------*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "gtb-probe.h"

#if defined(SHARED_forbuilding)
	#include "gtb-prob2.h"
#else
	#define mySHARED static
	typedef unsigned char			SQ_CONTENT;
	typedef unsigned int 			SQUARE; 
#endif

#include "sysport.h"
#include "gtb-att.h"
#include "gtb-types.h"

/*---------------------------------------------------------------------------------------------------------*/
/*#include "posit_t.h"*/

#define MAX_LISTSIZE 17
#if 0
typedef unsigned 		sq_t;
typedef unsigned char 	pc_t;
typedef uint32_t		mv_t;
#endif

struct posit {
	sq_t 			ws[MAX_LISTSIZE];
	sq_t 			bs[MAX_LISTSIZE];
	pc_t 			wp[MAX_LISTSIZE];
	pc_t 			bp[MAX_LISTSIZE];
	sq_t 			ep;
	unsigned int 	stm;
	unsigned int 	cas;
};
typedef struct 	posit posit_t;

#if 0
typedef long int		tbkey_t;
#endif

/*---------------------------------------------------------------------------------------------------------*/
/*#include "bool_t.h"*/

#if !defined(H_BOOL)
typedef int						bool_t;
#endif

#if !defined(TRUE)
#define TRUE ((bool_t)1)
#endif

#if !defined(FALSE)
#define FALSE ((bool_t)0)
#endif

/*--------- private if external building code is not present ----------------------------------------------*/

#if !defined(SHARED_forbuilding)

#define MAX_EGKEYS 145
#define SLOTSIZE 1
#define NOINDEX ((index_t)(-1))

#if 0
typedef unsigned short int 	dtm_t;
typedef size_t 				index_t;
/*typedef int 				index_t;*/
#endif

enum Loading_status {	
				STATUS_ABSENT 		= 0, 
				STATUS_STATICRAM 	= 1, 
				STATUS_MALLOC 		= 2,
				STATUS_FILE   		= 3, 
				STATUS_REJECT 		= 4
};

struct endgamekey {
	int 		id;
	const char *str;
	index_t 	maxindex;
	index_t 	slice_n;
	void   		(*itopc) (index_t, SQUARE *, SQUARE *);
	bool_t 		(*pctoi) (const SQUARE *, const SQUARE *, index_t *);
	dtm_t *		egt_w;
	dtm_t *		egt_b;
	FILE *		fd;
	int 		status;
	int			pathn; 
};
#endif

/*----------------------------------------------------------------------------------------------------------*/

/* array for better moves */
#ifdef GTB_SHARE
mySHARED int		bettarr [2] [8] [8];
#endif

/*------------ ENUMS ----------------------------------------------------------*/

enum Mask_values {	
					RESMASK  = tb_RESMASK, 
					INFOMASK = tb_INFOMASK, 
					PLYSHIFT = tb_PLYSHIFT  
};

enum Info_values {	
					iDRAW    = tb_DRAW, 
					iWMATE   = tb_WMATE, 
					iBMATE   = tb_BMATE, 
					iFORBID  = tb_FORBID,
		 
					iDRAWt   = tb_DRAW  |4, 
					iWMATEt  = tb_WMATE |4, 
					iBMATEt  = tb_BMATE |4, 
					iUNKNOWN = tb_UNKNOWN,

					iUNKNBIT = (1<<2)
};

/*-------------------------- inherited from a previous maindef.h -----------*/

#define WHITES (1u<<6)
#define BLACKS (1u<<7)

#define NOPIECE 0u
#define PAWN    1u
#define KNIGHT  2u
#define BISHOP  3u
#define ROOK    4u
#define QUEEN   5u
#define KING    6u

#define WH 0
#define BL 1
#define Opp(x) ((x)^1)
#define wK (KING   | WHITES)

/*-------------------
       SQUARES
  -------------------*/

/* from 1-63 different squares posibles   */

/*squares*/
enum SQUARES {
	A1,B1,C1,D1,E1,F1,G1,H1,
	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,
	A8,B8,C8,D8,E8,F8,G8,H8,
	NOSQUARE,
	ERRSQUARE = 128
};

/*------------------- end of inherited from a previous maindef.h -----------*/
 
#if !defined(NDEBUG)
#define NDEBUG
#endif
#ifdef DEBUG
#undef NDEBUG
#endif
#include "assert.h"

/*------------------- general DEFINES--------------------------- -----------*/

#define gtbNOSIDE	((unsigned)-1)
#define gtbNOINDEX	((index_t)-1)

/*************************************************\
|
|				COMPRESSION SCHEMES 
|
\*************************************************/

#include "gtb-dec.h"

static const char *const Extension[] = {
							 ".gtb.cp0"
							,".gtb.cp1"
							,".gtb.cp2"
							,".gtb.cp3"
							,".gtb.cp4"
							,".gtb.cp5"
							,".gtb.cp6"
							,".gtb.cp7"
							,".gtb.cp8"
							,".gtb.cp9"
};

/*************************************************\
|
|					MOVES 
|
\*************************************************/

enum move_kind {
		NORMAL_MOVE = 0,
		CASTLE_MOVE,
		PASSNT_MOVE,
		PROMOT_MOVE
};

enum move_content {
		NOMOVE = 0	
};

#define MV_TYPE(mv)   ( (BYTE)       ((mv) >>6 & 3 )      )
#define MV_TO(mv)     ( (SQUARE)     ((mv) >>8 & 63)      )
#define MV_PT(mv)     ( (SQ_CONTENT) ((mv) >>(3+16) &7  ) )
#define MV_TK(mv)     ( (SQ_CONTENT) ((mv) >>(6+16) &7  ) )
#define MV_FROM(mv)   ( (SQUARE)     ((mv)     & 63)      )

/*
|   move,type,color,piece,from,to,taken,promoted
*------------------------------------------------------------------*/

#define MV_BUILD(mv,ty,co,pc,fr,to,tk,pm) (                        \
    (mv)    =  (fr)     | (to)<< 8      | (ty)<<  6     | (co)<<8  \
            |  (pc)<<16 | (pm)<< (3+16) | (tk)<< (6+16)            \
)

#define MV_ADD_TOTK(mv,to,tk) (          \
     mv     |= (uint32_t)(to) << 8       \
            |  (uint32_t)(tk) << (6+16)  \
)

#define map88(x)    (   (x) + ((x)&070)        )
#define unmap88(x)  ( ( (x) + ((x)& 07) ) >> 1 )

/*************************************************\
|
|				STATIC VARIABLES 
|
\*************************************************/

static int GTB_scheme = 4;

/*************************************************\
|
|	needed for 
|	PRE LOAD CACHE AND DEPENDENT FUNCTIONS 
|
\*************************************************/

#define EGTB_MAXBLOCKSIZE 65536

static int GTB_MAXOPEN = 4;

static bool_t 			Uncompressed = TRUE;
static unsigned char 	Buffer_zipped [EGTB_MAXBLOCKSIZE];
static unsigned char 	Buffer_packed [EGTB_MAXBLOCKSIZE];
static unsigned int		zipinfo_init (void);
static void 			zipinfo_done (void);

enum Flip_flags {
		WE_FLAG = 1, NS_FLAG = 2,  NW_SE_FLAG = 4
}; /* used in flipt */

struct filesopen {
		int n;
		tbkey_t *key;
};

/* STATIC GLOBALS */

static struct filesopen	fd = {0, NULL};

static bool_t 			TB_INITIALIZED = FALSE;
static bool_t			DTM_CACHE_INITIALIZED = FALSE;

static int				WDL_FRACTION = 64;
static int				WDL_FRACTION_MAX = 128;
	
static size_t			DTM_cache_size = 0;
static size_t			WDL_cache_size = 0;

static unsigned int		TB_AVAILABILITY = 0;

/* LOCKS */
static mythread_mutex_t	Egtb_lock;


/****************************************************************************\
 *
 *
 *			DEBUGGING or PRINTING ZONE
 *
 *
 ****************************************************************************/

#if 0
#define FOLLOW_EGTB
#ifndef DEBUG
#define DEBUG
#endif
#endif

#define validsq(x) ((x) >= A1 && (x) <= H8)

#if defined(DEBUG)
static void 	print_pos (const sq_t *ws, const sq_t *bs, const pc_t *wp, const pc_t *bp);
#endif

#if defined(DEBUG) || defined(FOLLOW_EGTB)
static void 	output_state (unsigned stm, const SQUARE *wSQ, const SQUARE *bSQ, 
								const SQ_CONTENT *wPC, const SQ_CONTENT *bPC);
static const char *Square_str[64] = {
 	"a1","b1","c1","d1","e1","f1","g1","h1",
 	"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",
 	"a8","b8","c8","d8","e8","f8","g8","h8"
};
static const char *P_str[] = {
	"--", "P", "N", "B", "R", "Q", "K"
};
#endif

#ifdef FOLLOW_EGTB
	#define STAB
	#define STABCONDITION 1 /*(stm == BL && whiteSQ[0]==H1 && whiteSQ[1]==D1 && whiteSQ[2]==D3 && blackSQ[0]==C2 )*/
	static bool_t GLOB_REPORT = TRUE;
#endif

#if defined(FOLLOW_EGTB)
static const char *Info_str[8] = {	
	" Draw", " Wmate", " Bmate", "Illegal", 
	"~Draw", "~Wmate", "~Bmate", "Unknown" 
};
#endif

static void		list_index (void);
static void 	fatal_error(void) {
    exit(EXIT_FAILURE);
}

#ifdef STAB
	#define FOLLOW_LU(x,y)  {if (GLOB_REPORT) printf ("************** %s: %lu\n", (x), (long unsigned)(y));}
#else
	#define FOLLOW_LU(x,y)
#endif

#ifdef STAB
	#define FOLLOW_LULU(x,y,z)  {if (GLOB_REPORT) printf ("************** %s: %lu, %lu\n", (x), (long unsigned)(y), (long unsigned)(z));}
#else
	#define FOLLOW_LULU(x,y,z)
#endif

#ifdef STAB
	#define FOLLOW_label(x) {if (GLOB_REPORT) printf ("************** %s\n", (x));}
#else
	#define FOLLOW_label(x)
#endif

#ifdef STAB
	#define FOLLOW_DTM(msg,dtm)  {if (GLOB_REPORT) printf ("************** %s: %lu, info:%s, plies:%lu \n"\
	, (msg), (long unsigned)(dtm), (Info_str[(dtm)&INFOMASK]), (long unsigned)((dtm)>>PLYSHIFT)\
	);}
#else
	#define FOLLOW_DTM(msg,dtm)
#endif


/*--------------------------------*\
|
|
|		INDEXING FUNCTIONS
|
|
*---------------------------------*/

#define IDX_set_empty(x) {x=0;x--;}
#define IDX_is_empty(x) (0==(1+(x)))

#define NO_KKINDEX NOINDEX
#define MAX_KKINDEX 462
#define MAX_PPINDEX 576
#define MAX_PpINDEX (24 * 48)
/*1128*/
#define MAX_AAINDEX ((63-62) + (62 * (127-62)/2) - 1 + 1)
#define MAX_AAAINDEX (64*21*31)
#define MAX_PP48_INDEX (1128)
/* (24*23*22/6) + 24 * (24*23/2) */
#define MAX_PPP48_INDEX 8648

/* VARIABLES */

static index_t			kkidx [64] [64];
static index_t			ppidx [24] [48];
static index_t 			pp48_idx[48][48];
static index_t 			ppp48_idx[48][48][48];

static sq_t				wksq [MAX_KKINDEX];
static sq_t				bksq [MAX_KKINDEX];
static sq_t				pp48_sq_x[MAX_PP48_INDEX];
static sq_t				pp48_sq_y[MAX_PP48_INDEX]; 

static index_t		 	pp_hi24 [MAX_PPINDEX]; /* was unsigned int */
static index_t		 	pp_lo48 [MAX_PPINDEX];
static unsigned int 	flipt [64] [64];
static index_t		 	aaidx [64] [64]; /* was unsigned int */
static unsigned char 	aabase [MAX_AAINDEX];

static uint8_t			ppp48_sq_x[MAX_PPP48_INDEX];
static uint8_t			ppp48_sq_y[MAX_PPP48_INDEX]; 
static uint8_t			ppp48_sq_z[MAX_PPP48_INDEX]; 

/* FUNCTIONS */

static void 	init_indexing (int verbosity);
static void 	norm_kkindex (SQUARE x, SQUARE y, /*@out@*/ SQUARE *pi, /*@out@*/ SQUARE *pj);
static void 	pp_putanchorfirst (SQUARE a, SQUARE b, /*@out@*/ SQUARE *out_anchor, /*@out@*/ SQUARE *out_loosen);

static index_t	wsq_to_pidx24 (SQUARE pawn);
static index_t 	wsq_to_pidx48 (SQUARE pawn);
static SQUARE 	pidx24_to_wsq (index_t a);
static SQUARE 	pidx48_to_wsq (index_t a);

static SQUARE 	flipWE    		(SQUARE x) { return x ^  07;}
static SQUARE 	flipNS    		(SQUARE x) { return x ^ 070;}
static SQUARE 	flipNW_SE 		(SQUARE x) { return ((x&7)<<3) | (x>>3);}
static SQUARE 	getcol    		(SQUARE x) { return x &  7;}
static SQUARE 	getrow    		(SQUARE x) { return x >> 3;}
static bool_t 	in_queenside	(sq_t x)   { return 0 == (x & (1<<2));}

/* 1:0 */
static void 	kxk_indextopc   (index_t i, SQUARE *pw, SQUARE *pb);

/* 2:0 */
static void 	kabk_indextopc  (index_t i, SQUARE *pw, SQUARE *pb);
static void 	kakb_indextopc  (index_t i, SQUARE *pw, SQUARE *pb);
static void		kaak_indextopc  (index_t i, SQUARE *pw, SQUARE *pb);

/* 2:1 */
static void 	kabkc_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kaakb_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

/* 3:0 */
static void 	kabck_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kaabk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kaaak_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kabbk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

/* one pawn */
static void 	kpk_indextopc   (index_t i, SQUARE *pw, SQUARE *pb);

/* 1:1 one pawn */
static void 	kakp_indextopc  (index_t i, SQUARE *pw, SQUARE *pb);

/* 2:0 one pawn */
static void 	kapk_indextopc  (index_t i, SQUARE *pw, SQUARE *pb);

/* 2:0 two pawns */
static void 	kppk_indextopc  (index_t i, SQUARE *pw, SQUARE *pb);

/*  2:1 one pawn */
static void		kapkb_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kabkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kaakp_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

/*  2:1 + 3:0 two pawns */
static void		kppka_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kappk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kapkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

/*  3:0 one pawn */
static void		kabpk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kaapk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

/*  three pawns */
static void		kpppk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);
static void		kppkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

/* 1:1 two pawns */
static void 	kpkp_indextopc  (index_t i, SQUARE *pw, SQUARE *pb);

/* corresponding pc to index */
static bool_t 	kxk_pctoindex   (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kabk_pctoindex  (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kakb_pctoindex  (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kpk_pctoindex   (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kakp_pctoindex  (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kapk_pctoindex  (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t   kppk_pctoindex  (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kaak_pctoindex  (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kabkc_pctoindex (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);

static bool_t 	kaakb_pctoindex (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);/**/

static bool_t 	kabck_pctoindex (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kaabk_pctoindex (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);/**/
static bool_t 	kaaak_pctoindex (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kabbk_pctoindex (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);/**/
static bool_t 	kapkb_pctoindex (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kabkp_pctoindex (const SQUARE *pw, const SQUARE *pb, /*@out@*/ index_t *out);
static bool_t 	kaakp_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out);
static bool_t 	kppka_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out);
static bool_t 	kappk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out);
static bool_t 	kapkp_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out);
static bool_t 	kabpk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out);
static bool_t 	kaapk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out);
static bool_t 	kppkp_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out);
static bool_t 	kpppk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out);
static bool_t 	kpkp_pctoindex  (const SQUARE *pw,     const SQUARE *pb, /*@out@*/ index_t *out);

/* testing functions */
static bool_t 	test_kppk  (void);
static bool_t 	test_kaakb (void);
static bool_t 	test_kaabk (void);
static bool_t 	test_kaaak (void);
static bool_t 	test_kabbk (void);
static bool_t 	test_kapkb (void);
static bool_t 	test_kabkp (void);
static bool_t 	test_kppka (void);
static bool_t 	test_kappk (void);
static bool_t 	test_kapkp (void);
static bool_t 	test_kabpk (void);
static bool_t 	test_kaapk (void);
static bool_t 	test_kaakp (void);
static bool_t 	test_kppkp (void);
static bool_t 	test_kpppk (void);

static unsigned flip_type (SQUARE x, SQUARE y);
static index_t 	init_kkidx (void);
static index_t	init_ppidx (void);
static void    	init_flipt (void);
static index_t 	init_aaidx (void);
static index_t	init_aaa   (void);
static index_t	init_pp48_idx (void);
static index_t	init_ppp48_idx (void);

enum TB_INDEXES
	 {	 MAX_KXK 	= MAX_KKINDEX*64 
		,MAX_kabk 	= MAX_KKINDEX*64*64 
		,MAX_kakb 	= MAX_KKINDEX*64*64
		,MAX_kpk	= 24*64*64
		,MAX_kakp	= 24*64*64*64
		,MAX_kapk	= 24*64*64*64		
		,MAX_kppk	= MAX_PPINDEX*64*64
		,MAX_kpkp	= MAX_PpINDEX*64*64
		,MAX_kaak	= MAX_KKINDEX*MAX_AAINDEX
		,MAX_kabkc	= MAX_KKINDEX*64*64*64
		,MAX_kabck	= MAX_KKINDEX*64*64*64
		,MAX_kaakb	= MAX_KKINDEX*MAX_AAINDEX*64
		,MAX_kaabk	= MAX_KKINDEX*MAX_AAINDEX*64
		,MAX_kabbk  = MAX_KKINDEX*MAX_AAINDEX*64
		,MAX_kaaak	= MAX_KKINDEX*MAX_AAAINDEX
		,MAX_kapkb  = 24*64*64*64*64
		,MAX_kabkp  = 24*64*64*64*64
		,MAX_kabpk  = 24*64*64*64*64
		,MAX_kppka  = MAX_kppk*64
		,MAX_kappk  = MAX_kppk*64
		,MAX_kapkp  = MAX_kpkp*64
		,MAX_kaapk  = 24*MAX_AAINDEX*64*64
		,MAX_kaakp  = 24*MAX_AAINDEX*64*64
		,MAX_kppkp  = 24*MAX_PP48_INDEX*64*64
		,MAX_kpppk  = MAX_PPP48_INDEX*64*64	
};

#if defined(SHARED_forbuilding)
extern index_t 
biggest_memory_needed (void) {
    return MAX_kabkc;
}
#endif

/*--------------------------------*\
|
|
|		CACHE PROTOTYPES
|
|
*---------------------------------*/

#if !defined(SHARED_forbuilding)
mySHARED bool_t		get_dtm (tbkey_t key, unsigned side, index_t idx, dtm_t *out, bool_t probe_hard);
#endif

static bool_t	 	get_dtm_from_cache (tbkey_t key, unsigned side, index_t idx, dtm_t *out);


/*--------------------------------*\
|
|
|			INIT
|
|
*---------------------------------*/

static bool_t 	fd_init (struct filesopen *pfd);
static void		fd_done (struct filesopen *pfd);

static void		RAM_egtbfree (void);

/*--------------------------------------------------------------------------*/
#if !defined(SHARED_forbuilding)
mySHARED void   		egtb_freemem (int i);
#endif

mySHARED struct endgamekey egkey[] = {

{0, "kqk",  MAX_KXK,  1, kxk_indextopc,  kxk_pctoindex,  NULL ,  NULL   ,NULL ,0, 0 },
{1, "krk",  MAX_KXK,  1, kxk_indextopc,  kxk_pctoindex,  NULL ,  NULL   ,NULL ,0, 0 },
{2, "kbk",  MAX_KXK,  1, kxk_indextopc,  kxk_pctoindex,  NULL ,  NULL   ,NULL ,0, 0 },
{3, "knk",  MAX_KXK,  1, kxk_indextopc,  kxk_pctoindex,  NULL ,  NULL   ,NULL ,0, 0 },
{4, "kpk",  MAX_kpk,  24,kpk_indextopc,  kpk_pctoindex,  NULL ,  NULL   ,NULL ,0, 0 },
	/* 4 pieces */	
{5, "kqkq", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },	
{6, "kqkr", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },	
{7, "kqkb", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{8, "kqkn", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },	

{9, "krkr", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{10,"krkb", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{11,"krkn", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{12,"kbkb", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{13,"kbkn", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{14,"knkn", MAX_kakb, 1, kakb_indextopc, kakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
	/**/		
{15,"kqqk", MAX_kaak, 1, kaak_indextopc, kaak_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },	
{16,"kqrk", MAX_kabk, 1, kabk_indextopc, kabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },	
{17,"kqbk", MAX_kabk, 1, kabk_indextopc, kabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{18,"kqnk", MAX_kabk, 1, kabk_indextopc, kabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },	

{19,"krrk", MAX_kaak, 1, kaak_indextopc, kaak_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },	
{20,"krbk", MAX_kabk, 1, kabk_indextopc, kabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{21,"krnk", MAX_kabk, 1, kabk_indextopc, kabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{22,"kbbk", MAX_kaak, 1, kaak_indextopc, kaak_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },	
{23,"kbnk", MAX_kabk, 1, kabk_indextopc, kabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{24,"knnk", MAX_kaak, 1, kaak_indextopc, kaak_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },	
	/**/	
	/**/
{25,"kqkp", MAX_kakp, 24,kakp_indextopc, kakp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{26,"krkp", MAX_kakp, 24,kakp_indextopc, kakp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{27,"kbkp", MAX_kakp, 24,kakp_indextopc, kakp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{28,"knkp", MAX_kakp, 24,kakp_indextopc, kakp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
	/**/
{29,"kqpk", MAX_kapk, 24,kapk_indextopc, kapk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{30,"krpk", MAX_kapk, 24,kapk_indextopc, kapk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{31,"kbpk", MAX_kapk, 24,kapk_indextopc, kapk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{32,"knpk", MAX_kapk, 24,kapk_indextopc, kapk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
	/**/	
{33,"kppk", MAX_kppk, MAX_PPINDEX ,kppk_indextopc, kppk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
	/**/
{34,"kpkp", MAX_kpkp, MAX_PpINDEX ,kpkp_indextopc, kpkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
	/**/
	/**/
	/* 5 pieces */
{ 35,"kqqqk", MAX_kaaak, 1, kaaak_indextopc, kaaak_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 36,"kqqrk", MAX_kaabk, 1, kaabk_indextopc, kaabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 37,"kqqbk", MAX_kaabk, 1, kaabk_indextopc, kaabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 38,"kqqnk", MAX_kaabk, 1, kaabk_indextopc, kaabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 39,"kqrrk", MAX_kabbk, 1, kabbk_indextopc, kabbk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 40,"kqrbk", MAX_kabck, 1, kabck_indextopc, kabck_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 41,"kqrnk", MAX_kabck, 1, kabck_indextopc, kabck_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 42,"kqbbk", MAX_kabbk, 1, kabbk_indextopc, kabbk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 43,"kqbnk", MAX_kabck, 1, kabck_indextopc, kabck_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 44,"kqnnk", MAX_kabbk, 1, kabbk_indextopc, kabbk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 45,"krrrk", MAX_kaaak, 1, kaaak_indextopc, kaaak_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 46,"krrbk", MAX_kaabk, 1, kaabk_indextopc, kaabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 47,"krrnk", MAX_kaabk, 1, kaabk_indextopc, kaabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 48,"krbbk", MAX_kabbk, 1, kabbk_indextopc, kabbk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 49,"krbnk", MAX_kabck, 1, kabck_indextopc, kabck_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 50,"krnnk", MAX_kabbk, 1, kabbk_indextopc, kabbk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 51,"kbbbk", MAX_kaaak, 1, kaaak_indextopc, kaaak_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 52,"kbbnk", MAX_kaabk, 1, kaabk_indextopc, kaabk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 53,"kbnnk", MAX_kabbk, 1, kabbk_indextopc, kabbk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 54,"knnnk", MAX_kaaak, 1, kaaak_indextopc, kaaak_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 55,"kqqkq", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 56,"kqqkr", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 57,"kqqkb", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 58,"kqqkn", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 59,"kqrkq", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 60,"kqrkr", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 61,"kqrkb", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 62,"kqrkn", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 63,"kqbkq", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 64,"kqbkr", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 65,"kqbkb", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 66,"kqbkn", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 67,"kqnkq", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 68,"kqnkr", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 69,"kqnkb", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 70,"kqnkn", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 71,"krrkq", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 72,"krrkr", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 73,"krrkb", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 74,"krrkn", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 75,"krbkq", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 76,"krbkr", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 77,"krbkb", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 78,"krbkn", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 79,"krnkq", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 80,"krnkr", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 81,"krnkb", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 82,"krnkn", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 83,"kbbkq", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 84,"kbbkr", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 85,"kbbkb", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 86,"kbbkn", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 87,"kbnkq", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 88,"kbnkr", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 89,"kbnkb", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 90,"kbnkn", MAX_kabkc, 1, kabkc_indextopc, kabkc_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 91,"knnkq", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 92,"knnkr", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 93,"knnkb", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 94,"knnkn", MAX_kaakb, 1, kaakb_indextopc, kaakb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{ 95,"kqqpk", MAX_kaapk, 24, kaapk_indextopc, kaapk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 96,"kqrpk", MAX_kabpk, 24, kabpk_indextopc, kabpk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 97,"kqbpk", MAX_kabpk, 24, kabpk_indextopc, kabpk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 98,"kqnpk", MAX_kabpk, 24, kabpk_indextopc, kabpk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{ 99,"krrpk", MAX_kaapk, 24, kaapk_indextopc, kaapk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{100,"krbpk", MAX_kabpk, 24, kabpk_indextopc, kabpk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{101,"krnpk", MAX_kabpk, 24, kabpk_indextopc, kabpk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{102,"kbbpk", MAX_kaapk, 24, kaapk_indextopc, kaapk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{103,"kbnpk", MAX_kabpk, 24, kabpk_indextopc, kabpk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{104,"knnpk", MAX_kaapk, 24, kaapk_indextopc, kaapk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{105,"kqppk", MAX_kappk, MAX_PPINDEX, kappk_indextopc, kappk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{106,"krppk", MAX_kappk, MAX_PPINDEX, kappk_indextopc, kappk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{107,"kbppk", MAX_kappk, MAX_PPINDEX, kappk_indextopc, kappk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{108,"knppk", MAX_kappk, MAX_PPINDEX, kappk_indextopc, kappk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{109,"kqpkq", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{110,"kqpkr", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{111,"kqpkb", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{112,"kqpkn", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{113,"krpkq", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{114,"krpkr", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{115,"krpkb", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{116,"krpkn", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{117,"kbpkq", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{118,"kbpkr", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{119,"kbpkb", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{120,"kbpkn", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{121,"knpkq", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{122,"knpkr", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{123,"knpkb", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{124,"knpkn", MAX_kapkb, 24, kapkb_indextopc, kapkb_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{125,"kppkq", MAX_kppka, MAX_PPINDEX, kppka_indextopc, kppka_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{126,"kppkr", MAX_kppka, MAX_PPINDEX, kppka_indextopc, kppka_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{127,"kppkb", MAX_kppka, MAX_PPINDEX, kppka_indextopc, kppka_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{128,"kppkn", MAX_kppka, MAX_PPINDEX, kppka_indextopc, kppka_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{129,"kqqkp", MAX_kaakp, 24, kaakp_indextopc, kaakp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{130,"kqrkp", MAX_kabkp, 24, kabkp_indextopc, kabkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{131,"kqbkp", MAX_kabkp, 24, kabkp_indextopc, kabkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{132,"kqnkp", MAX_kabkp, 24, kabkp_indextopc, kabkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{133,"krrkp", MAX_kaakp, 24, kaakp_indextopc, kaakp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{134,"krbkp", MAX_kabkp, 24, kabkp_indextopc, kabkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{135,"krnkp", MAX_kabkp, 24, kabkp_indextopc, kabkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{136,"kbbkp", MAX_kaakp, 24, kaakp_indextopc, kaakp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{137,"kbnkp", MAX_kabkp, 24, kabkp_indextopc, kabkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{138,"knnkp", MAX_kaakp, 24, kaakp_indextopc, kaakp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{139,"kqpkp", MAX_kapkp, MAX_PpINDEX, kapkp_indextopc, kapkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{140,"krpkp", MAX_kapkp, MAX_PpINDEX, kapkp_indextopc, kapkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{141,"kbpkp", MAX_kapkp, MAX_PpINDEX, kapkp_indextopc, kapkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{142,"knpkp", MAX_kapkp, MAX_PpINDEX, kapkp_indextopc, kapkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{143,"kppkp", MAX_kppkp, 24*MAX_PP48_INDEX, kppkp_indextopc, kppkp_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },
{144,"kpppk", MAX_kpppk, MAX_PPP48_INDEX, kpppk_indextopc, kpppk_pctoindex, NULL ,  NULL   ,NULL ,0, 0 },

{MAX_EGKEYS, NULL,  0,        1, NULL,           NULL,           NULL,   NULL   ,NULL ,0 ,0}

};

static int eg_was_open[MAX_EGKEYS];

static uint64_t Bytes_read = 0;

/****************************************************************************\
|
|
|						PATH MANAGEMENT ZONE
|
|
\****************************************************************************/

#define MAXPATHLEN tb_MAXPATHLEN
#define MAX_GTBPATHS 10

static int  			Gtbpath_end_index = 0;
static const char **	Gtbpath = NULL;

/*---------------- EXTERNAL PATH MANAGEMENT --------------------------------*/

extern const char *tbpaths_getmain (void) {	return Gtbpath[0];}

extern const char **
tbpaths_init(void)
{
	const char **newps;
	newps = (const char **) malloc (sizeof (char *));
	if (newps != NULL) {
		newps[0] = NULL;
	}
	return newps;
}

static const char **
tbpaths_add_single(const char **ps, const char *newpath)
{
	size_t counter;
	const char **newps;
	size_t i, psize;
	char *ppath;

	if (NULL == ps)
		return NULL;

	psize = strlen(newpath) + 1;
	ppath = (char *) malloc (psize * sizeof (char));
	if (NULL == ppath)
		return ps; /* failed to incorporate a new path */
	for (i = 0; i < psize; i++) ppath[i] = newpath[i];

	for (counter = 0; ps[counter] != NULL; counter++)
		; 

	/* cast to deal with const poisoning */
	newps =	(const char **) realloc ((char **)ps, sizeof(char *) * (counter+2));
	if (newps != NULL) {
		newps [counter] = ppath;
		newps [counter+1] = NULL;
	}
	return newps;
}


extern const char **
tbpaths_add(const char **ps, const char *newpath)
{
	size_t i, psize;
	char *mpath;

	if (NULL == ps)
		return NULL;

	psize = strlen(newpath) + 1;
	mpath = (char *) malloc (psize * sizeof (char));
	if (NULL == mpath) {
		return ps; /* failed to incorporate a new path */
	}
	for (i = 0; i < psize; i++) mpath[i] = newpath[i];	

	for (i = 0; i < psize; i++) {
		if(';' == mpath[i])
			mpath[i] = '\0';	
	}

	for (i = 0;;) {
		while (i < psize && mpath[i] == '\0') i++;
		if (i >= psize) break;
		ps = tbpaths_add_single (ps, &mpath[i]);
		while (i < psize && mpath[i] != '\0') i++;
	}

	free(mpath);
	return ps;
}


extern const char **
tbpaths_done(const char **ps)
{
	int counter;
	void *q;	

	if (ps != NULL) {
		for (counter = 0; ps[counter] != NULL; counter++) {
			/* cast to deal with const poisoning */
			void *p = (void *) ps[counter];
			free(p);		
		} 	
		/* cast to deal with const poisoning */
		q = (void *) ps;
		free(q);
	}
	return NULL;
}

/*---------------- PATH INITIALIZATION ROUTINES ----------------------------*/

static void path_system_reset(void) {Gtbpath_end_index = 0;}

static bool_t
path_system_init (const char **path)
{
	size_t i;
	size_t sz;
	const char *x;
	bool_t ok = TRUE;
	path_system_reset();

	if (path == NULL) {
		return FALSE;
	}

	/* calculate needed size for Gtbpath */
	i = 0;
	do {
		x = path[i++];
	} while (x != NULL);
	sz = i; /* sz includes the NULL */


	Gtbpath = (const char **) malloc (sz * sizeof(char *));

	if (Gtbpath) {
		
		ok = TRUE;
		/* point to the same strings provided */
		Gtbpath_end_index = 0;
		for (i = 0; i < sz; i++) {
			Gtbpath[i] = path[i];
			Gtbpath_end_index++;
		}

	} else {
		ok = FALSE;
	}
	return ok;

}

static void
path_system_done (void)
{
	/* before we free Gtbpath, we have to deal with the
	"const poisoning" and cast it. free() does not accept
	const pointers */
	char **	p = (char **) Gtbpath;	
	/* clean up */
	if (p != NULL)
		free(p);
	return;
}


/****************************************************************************\
 *
 *
 *						General Initialization Zone
 *
 *
 ****************************************************************************/


#ifdef WDL_PROBE
static size_t 		wdl_cache_init (size_t cache_mem);
static void 		wdl_cache_flush (void);

static void			wdl_cache_reset_counters (void);
static void			wdl_cache_done (void);

static bool_t		get_WDL_from_cache (tbkey_t key, unsigned side, index_t idx, unsigned int *out);
static bool_t		wdl_preload_cache (tbkey_t key, unsigned side, index_t idx);
#endif

#ifdef GTB_SHARE
static void 	init_bettarr (void);
#endif

static void	eg_was_open_reset(void)
{
	int i;
	for (i = 0; i < MAX_EGKEYS; i++) {
		eg_was_open[i] = 0;
	}
}

static long unsigned int eg_was_open_count(void)
{
	long int i, x;
	for (i = 0, x = 0; i < MAX_EGKEYS; i++) {
		x += eg_was_open[i];
	}
	return (long unsigned) x;
}


enum  Sizes {INISIZE = 4096};
static char ini_str[INISIZE];
static void sjoin(char *s, const char *tail, size_t max) {strncat(s, tail, max - strlen(s) - 1);}

char *
tb_init (int verbosity, int decoding_sch, const char **paths)
{
	unsigned int zi;
	int paths_ok;
	char *ret_str;
	char localstr[256];

	assert(!TB_INITIALIZED);

	if (verbosity) {
		ini_str[0] = '\0';
		ret_str = ini_str;
	} else {
		ret_str = NULL;
	}

	paths_ok = path_system_init (paths);

	if (paths_ok && verbosity) { 
		int g;
		assert(Gtbpath!=NULL);
		sjoin(ini_str,"\nGTB PATHS\n",INISIZE);
		for (g = 0; Gtbpath[g] != NULL; g++) {
			const char *p = Gtbpath[g];
			if (0 == g) {
				sprintf (localstr,"  main: %s\n", p);
			} else {
				sprintf (localstr,"    #%d: %s\n", g, p);
			}
			sjoin(ini_str,localstr,INISIZE);
		}
	}

	if (!paths_ok && verbosity) { 
		sjoin (ini_str,"\nGTB PATHS not initialized\n",INISIZE);
	}

	if (!reach_was_initialized())
		reach_init();

	attack_maps_init (); /* external initialization */

	init_indexing(0 /* no verbosity */);	

	#ifdef GTB_SHARE
	init_bettarr();
	#endif

	if (!fd_init (&fd) && verbosity) {
		sjoin (ini_str,"  File Open Memory initialization = **FAILED**\n",INISIZE);
		return ret_str;
	}
	
	GTB_scheme = decoding_sch;
	Uncompressed = GTB_scheme == 0;

	if (GTB_scheme == 0) {
		Uncompressed = TRUE;
	}

	set_decoding_scheme(GTB_scheme);

	if (verbosity) {
		sjoin (ini_str,"\nGTB initialization\n",INISIZE);
		sprintf (localstr,"  Compression  Scheme = %d\n", GTB_scheme);
		sjoin (ini_str,localstr,INISIZE);
	}

	zi = zipinfo_init();

	TB_AVAILABILITY = zi;

	if (verbosity) {
		if (0 == zi) {
			sjoin (ini_str,"  Compression Indexes = **FAILED**\n",INISIZE);
		} else {
			int n, bit;

			n = 3; bit = 1;
			if (zi&(1u<<bit)) 
				sprintf (localstr,"  Compression Indexes (%d-pc) = PASSED\n",n);
			else
				sprintf (localstr,"  Compression Indexes (%d-pc) = **FAILED**\n",n);
			sjoin (ini_str,localstr,INISIZE);
			
			n = 4; bit = 3;
			if (zi&(1u<<bit))
				sprintf (localstr,"  Compression Indexes (%d-pc) = PASSED\n",n);
			else
				sprintf (localstr,"  Compression Indexes (%d-pc) = **FAILED**\n",n);
			sjoin (ini_str,localstr,INISIZE);

			n = 5; bit = 5;
			if (zi&(1u<<bit))
				sprintf (localstr,"  Compression Indexes (%d-pc) = PASSED\n",n);
			else
				sprintf (localstr,"  Compression Indexes (%d-pc) = **FAILED**\n",n);
			sjoin (ini_str,localstr,INISIZE);
		}
		sjoin (ini_str,"\n",INISIZE);
	}

	eg_was_open_reset();
	Bytes_read = 0;

	mythread_mutex_init (&Egtb_lock);

	TB_INITIALIZED = TRUE;

	return ret_str;
}

extern unsigned int
tb_availability(void)
{
	return TB_AVAILABILITY;
}

extern bool_t
tb_is_initialized (void)
{
	return TB_INITIALIZED;
}

extern void
tb_done (void)
{
	assert(TB_INITIALIZED);
	fd_done (&fd);
	RAM_egtbfree();
	zipinfo_done();
	path_system_done();
	mythread_mutex_destroy (&Egtb_lock);
	TB_INITIALIZED = FALSE;

	/*
		HERE, I should be free() the ini_str, but in
		the current implementation, it is static
		rather than dynamic.
	*/
	return;
}

char *
tb_restart(int verbosity, int decoding_sch, const char **paths)
{
	if (tb_is_initialized()) {
		tb_done();
	}
	return tb_init(verbosity, decoding_sch, paths);
}

/* whenever the program exits should release this memory */
static void
RAM_egtbfree (void)
{
	int i;
	for (i = 0; egkey[i].str != NULL; i++) {
		egtb_freemem (i);
	}
}

/*--------------------------------------------------------------------------*/

#ifdef GTB_SHARE
static void
init_bettarr (void)
{
/*
		iDRAW  = 0, iWMATE  = 1, iBMATE  = 2, iFORBID  = 3, 
		iDRAWt = 4, iWMATEt = 5, iBMATEt = 6, iUNKNOWN = 7
 */

	int temp[] = {
	/*White*/	
	/*iDRAW   vs*/
		1, 2, 1, 1,     2, 2, 2, 2,
	/*iWMATE  vs*/	
		1, 3, 1, 1,     1, 1, 1, 1,
	/*iBMATE  vs*/
		2, 2, 4, 1,     2, 2, 2, 2,
	/*iFORBID vs*/
		2, 2, 2, 2,     2, 2, 2, 2,
	
	/*iDRAWt  vs*/
		1, 2, 1, 1,     2, 2, 1, 2,
	/*iWMATEt vs*/	
		1, 2, 1, 1,     1, 3, 1, 1,
	/*iBMATEt vs*/
		1, 2, 1, 1,     2, 2, 4, 2,
	/*iUNKNOWN  */
		1, 2, 1, 1,     1, 2, 1, 2,

	/*Black*/
	/*iDRAW   vs*/
		1, 1, 2, 1,     2, 2, 2, 2,
	/*iWMATE  vs*/	
		2, 4, 2, 1,     2, 2, 2, 2,
	/*iBMATE  vs*/
		1, 1, 3, 1,     1, 1, 1, 1,
	/*iFORBID vs*/
		2, 2, 2, 2,     2, 2, 2, 2,
	
	/*iDRAWt  vs*/
		1, 1, 2, 1,     2, 1, 2, 2,
	/*iWMATEt vs*/	
		1, 1, 2, 1,     2, 4, 2, 2,
	/*iBMATEt vs*/
		1, 1, 2, 1,     1, 1, 3, 1,
	/*iUNKNOWN  */
		1, 1, 2, 1,     1, 1, 2, 2
	};

	int i, j, k, z;
	
	/* reset */
	z = 0;
	for (i = 0; i < 2; i++)
		for (j = 0; j < 8; j++) 
			for (k = 0; k < 8; k++)	
				bettarr [i][j][k] = temp[z++];

	return;
}
#endif

/*
|
|	Own File Descriptors
|
\*---------------------------------------------------------------------------*/

static bool_t
fd_init (struct filesopen *pfd)
{
	tbkey_t *p;
    int i, allowed;

	pfd->n = 0;

	allowed = mysys_fopen_max() - 5 /*stdin,stdout,sterr,stdlog,book*/;
	if (allowed < 4)
		GTB_MAXOPEN = 4;
	if (allowed > 32)
		GTB_MAXOPEN = 32;		

	p =	(tbkey_t *) malloc(sizeof(tbkey_t)*(size_t)GTB_MAXOPEN);

	if (p != NULL) {
		for (i = 0; i < GTB_MAXOPEN; i++) {
			p[i] = -1;	
		}
		pfd->key = p;
		return TRUE;
	} else {
		return FALSE;
	}
}

static void
fd_done (struct filesopen *pfd)
{
    int i;
	tbkey_t closingkey;
	FILE *finp;

	assert(pfd != NULL);

	for (i = 0; i < pfd->n; i++) {
		closingkey = pfd->key[i];
		finp = egkey [closingkey].fd;
		fclose (finp);
		egkey[closingkey].fd = NULL;
		pfd->key[i] = -1;	
	}
	pfd->n = 0;
	free(pfd->key);
}

/****************************************************************************\
|
|
|								PROBE ZONE
|
|
\****************************************************************************/

#if !defined(SHARED_forbuilding)

/* shared with building routines */
mySHARED void 			list_sq_copy 	(const SQUARE *a, SQUARE *b);
mySHARED void 			list_pc_copy 	(const SQ_CONTENT *a, SQ_CONTENT *b);
mySHARED dtm_t 			inv_dtm 		(dtm_t x);
mySHARED bool_t 		egtb_get_id  	(SQ_CONTENT *w, SQ_CONTENT *b, tbkey_t *id);
mySHARED void 			list_sq_flipNS 	(SQUARE *s);
mySHARED dtm_t 			adjust_up 		(dtm_t dist);
mySHARED dtm_t 			bestx 			(unsigned stm, dtm_t a, dtm_t b);
mySHARED void			sortlists 		(SQUARE *ws, SQ_CONTENT *wp);

mySHARED /*@NULL@*/ 	FILE * fd_openit(tbkey_t key);

mySHARED dtm_t 			dtm_unpack 	(unsigned stm, unsigned char packed);
mySHARED void  			unpackdist 	(dtm_t d, unsigned int *res, unsigned int *ply);
mySHARED dtm_t 			packdist 	(unsigned int inf, unsigned int ply);

mySHARED bool_t			fread_entry_packed 	(FILE *dest, unsigned side, dtm_t *px);
mySHARED bool_t			fpark_entry_packed  (FILE *finp, unsigned side, index_t max, index_t idx);
#endif

/* use only with probe */
static bool_t			egtb_get_dtm 	(tbkey_t k, unsigned stm, const SQUARE *wS, const SQUARE *bS, bool_t probe_hard, dtm_t *dtm);
static void				removepiece (SQUARE *ys, SQ_CONTENT *yp, int j);
static bool_t 			egtb_filepeek (tbkey_t key, unsigned side, index_t idx, dtm_t *out_dtm);


/*prototype*/
#ifdef WDL_PROBE
static bool_t
tb_probe_wdl
			(unsigned stm, 
			 const SQUARE *inp_wSQ, 
			 const SQUARE *inp_bSQ,
			 const SQ_CONTENT *inp_wPC, 
			 const SQ_CONTENT *inp_bPC,
			 bool_t probingtype,
			 /*@out@*/ unsigned *res);
#endif

static bool_t
tb_probe_	(unsigned stm, 
			 SQUARE epsq,
			 const SQUARE *inp_wSQ, 
			 const SQUARE *inp_bSQ,
			 const SQ_CONTENT *inp_wPC, 
			 const SQ_CONTENT *inp_bPC,
			 bool_t probingtype,
			 /*@out@*/ unsigned *res, 
			 /*@out@*/ unsigned *ply);


extern bool_t
tb_probe_soft
			(unsigned stm, 
			 SQUARE epsq,
			 unsigned castles,
			 const SQUARE *inp_wSQ, 
			 const SQUARE *inp_bSQ,
			 const SQ_CONTENT *inp_wPC, 
			 const SQ_CONTENT *inp_bPC,
			 /*@out@*/ unsigned *res, 
			 /*@out@*/ unsigned *ply)
{
	if (castles != 0) 
		return FALSE;
	return tb_probe_ (stm, epsq, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, FALSE, res, ply);
} 

extern bool_t
tb_probe_hard
			(unsigned stm, 
			 SQUARE epsq,
			 unsigned castles,
			 const SQUARE *inp_wSQ, 
			 const SQUARE *inp_bSQ,
			 const SQ_CONTENT *inp_wPC, 
			 const SQ_CONTENT *inp_bPC,
			 /*@out@*/ unsigned *res, 
			 /*@out@*/ unsigned *ply)
{
	if (castles != 0) 
		return FALSE;
	return tb_probe_ (stm, epsq, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, TRUE, res, ply);
} 

extern bool_t
tb_probe_WDL_soft
			(unsigned stm, 
			 SQUARE epsq,
			 unsigned castles,
			 const SQUARE *inp_wSQ, 
			 const SQUARE *inp_bSQ,
			 const SQ_CONTENT *inp_wPC, 
			 const SQ_CONTENT *inp_bPC,
			 /*@out@*/ unsigned *res)
{
	unsigned ply_n;
	unsigned *ply = &ply_n;
	if (castles != 0) 
		return FALSE;
	if (epsq != NOSQUARE)
		return tb_probe_ (stm, epsq, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, FALSE, res, ply);

	/* probe bitbase like, assuming no en passant */
	#ifdef WDL_PROBE
	return tb_probe_wdl    (stm, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, FALSE, res);
	#else
	return tb_probe_ (stm, epsq, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, FALSE, res, ply);
	#endif	
} 

extern bool_t
tb_probe_WDL_hard
			(unsigned stm, 
			 SQUARE epsq,
			 unsigned castles,
			 const SQUARE *inp_wSQ, 
			 const SQUARE *inp_bSQ,
			 const SQ_CONTENT *inp_wPC, 
			 const SQ_CONTENT *inp_bPC,
			 /*@out@*/ unsigned *res)
{
	unsigned ply_n;
	unsigned *ply = &ply_n;
	if (castles != 0) 
		return FALSE;
	if (epsq != NOSQUARE)
		return tb_probe_ (stm, epsq, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, TRUE, res, ply);

	/* probe bitbase like, assuming no en passant */
	#ifdef WDL_PROBE
	return tb_probe_wdl    (stm, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, TRUE, res);
	#else
	return tb_probe_ (stm, epsq, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, TRUE, res, ply);
	#endif	
} 


static bool_t
tb_probe_	(unsigned stm, 
			 SQUARE epsq,
			 const SQUARE *inp_wSQ, 
			 const SQUARE *inp_bSQ,
			 const SQ_CONTENT *inp_wPC, 
			 const SQ_CONTENT *inp_bPC,
			 bool_t probingtype,
			 /*@out@*/ unsigned *res, 
			 /*@out@*/ unsigned *ply)
{
	int i = 0, j = 0;
	tbkey_t id = -1;
	dtm_t dtm;

	SQUARE 		storage_ws [MAX_LISTSIZE], storage_bs [MAX_LISTSIZE];
	SQ_CONTENT  storage_wp [MAX_LISTSIZE], storage_bp [MAX_LISTSIZE];

	SQUARE     *ws = storage_ws;
	SQUARE     *bs = storage_bs;
	SQ_CONTENT *wp = storage_wp;
	SQ_CONTENT *bp = storage_bp;

	SQUARE     *xs;
	SQUARE     *ys;
	SQ_CONTENT *xp;
	SQ_CONTENT *yp;

	SQUARE 		tmp_ws [MAX_LISTSIZE], tmp_bs [MAX_LISTSIZE];
	SQ_CONTENT  tmp_wp [MAX_LISTSIZE], tmp_bp [MAX_LISTSIZE];

	SQUARE *temps;
	bool_t straight = FALSE;
	
	SQUARE capturer_a, capturer_b, xed = NOSQUARE;
	
	unsigned int plies;
	unsigned int inf;

	bool_t  okdtm  = TRUE;
	bool_t	okcall = TRUE;

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

	assert (stm == WH || stm == BL);
	/*assert (inp_wPC[0] == KING && inp_bPC[0] == KING );*/
	assert ((epsq >> 3) == 2 || (epsq >> 3) == 5 || epsq == NOSQUARE);

	/* VALID ONLY FOR KK!! */
	if (inp_wPC[1] == NOPIECE && inp_bPC[1] == NOPIECE) {
		index_t dummy_i;
		bool_t b = kxk_pctoindex (inp_wSQ, inp_bSQ, &dummy_i);
		*res = b? iDRAW: iFORBID;
		*ply = 0;
		return TRUE;
	} 

	/* copy input */
	list_pc_copy (inp_wPC, wp);
	list_pc_copy (inp_bPC, bp);
	list_sq_copy (inp_wSQ, ws);
	list_sq_copy (inp_bSQ, bs);

	sortlists (ws, wp);
	sortlists (bs, bp);

	FOLLOW_label("EGTB_PROBE")

	if (egtb_get_id (wp, bp, &id)) {
		FOLLOW_LU("got ID",id)
		straight = TRUE;
	} else if (egtb_get_id (bp, wp, &id)) {
		FOLLOW_LU("rev ID",id)
		straight = FALSE;
		list_sq_flipNS (ws);
		list_sq_flipNS (bs);
        temps = ws;
        ws = bs;
        bs = temps;
		stm = Opp(stm);
		if (epsq != NOSQUARE) epsq ^= 070; 				/* added */
		{SQ_CONTENT *tempp = wp; wp = bp; bp = tempp;} 	/* added */
	} else {
		#if defined(DEBUG)
		printf("did not get id...\n");
		output_state (stm, ws, bs, wp, bp);		
		#endif
		unpackdist (iFORBID, res, ply);
		return FALSE;
	}

	/* store position... */
	list_pc_copy (wp, tmp_wp);
	list_pc_copy (bp, tmp_bp);
	list_sq_copy (ws, tmp_ws);
	list_sq_copy (bs, tmp_bs);

	/* x will be stm and y will be stw */
	if (stm == WH) {
        xs = ws;
        xp = wp;
        ys = bs;
        yp = bp;
    } else {
        xs = bs;
        xp = bp;
        ys = ws;
        yp = wp;
	}

	okdtm = egtb_get_dtm (id, stm, ws, bs, probingtype, &dtm);

	FOLLOW_LU("dtmok?",okdtm)
	FOLLOW_DTM("dtm", dtm)

	if (okdtm) {

		capturer_a = NOSQUARE;
		capturer_b = NOSQUARE;

		if (epsq != NOSQUARE) {
			/* captured pawn, trick: from epsquare to captured */		
			xed = epsq ^ (1<<3);

			/* find index captured (j) */
			for (j = 0; ys[j] != NOSQUARE; j++) {
				if (ys[j] == xed) break;
			}	

			/* try first possible ep capture */
			if (0 == (0x88 & (map88(xed) + 1))) 
				capturer_a = xed + 1;
			/* try second possible ep capture */
			if (0 == (0x88 & (map88(xed) - 1))) 
				capturer_b = xed - 1;

			if (ys[j] == xed) {
	
				/* find capturers (i) */
				for (i = 0; xs[i] != NOSQUARE && okcall; i++) {

					if (xp[i]==PAWN && (xs[i]==capturer_a || xs[i]==capturer_b)) {
						dtm_t epscore = iFORBID;

						/* execute capture */
						xs[i] = epsq;
						removepiece (ys, yp, j);
					
						okcall = tb_probe_ (Opp(stm), NOSQUARE, ws, bs, wp, bp, probingtype, &inf, &plies);
						
						if (okcall) {
							epscore = packdist (inf, plies); 					
							epscore = adjust_up (epscore);

							/* chooses to ep or not */
							dtm = bestx (stm, epscore, dtm);
						}
	
						/* restore position */
						list_pc_copy (tmp_wp, wp);
						list_pc_copy (tmp_bp, bp);

						list_sq_copy (tmp_ws, ws);
						list_sq_copy (tmp_bs, bs);					
					}
				}
			} 
		} /* ep */

		if (straight) {
			unpackdist (dtm, res, ply);
		} else {
			unpackdist (inv_dtm (dtm), res, ply);
		}	 
	} 

	if (!okdtm || !okcall) {
		unpackdist (iFORBID, res, ply);
	}
	
	return okdtm && okcall;
} 

#ifdef _MSC_VER
/* to silence warning for sprintf usage */
#pragma warning(disable:4996)
#endif

static bool_t
egtb_filepeek (tbkey_t key, unsigned side, index_t idx, dtm_t *out_dtm)
{
	FILE *finp;

#define USE_FD

	#if !defined(USE_FD)
	char buf[1024];
	char *filename = buf;
	#endif

	bool_t ok;
    dtm_t x=0;
	index_t maxindex  = egkey[key].maxindex;



	assert (Uncompressed);
	assert (side == WH || side == BL);
	assert (out_dtm != NULL);
	assert (idx >= 0);
	assert (key < MAX_EGKEYS);

		
	#if defined(USE_FD)
		if (NULL == (finp = egkey[key].fd) ) {
			if (NULL == (finp = fd_openit (key))) {
				return FALSE;
			}	
		}
	#else
		sprintf (buf, "%s.gtb", egkey[key].str);
		if (NULL == (finp = fopen (filename, "rb"))) {
			return FALSE;
		}	
	#endif

	ok = fpark_entry_packed (finp, side, maxindex, idx);
	ok = ok && fread_entry_packed (finp, side, &x);

	if (ok) {
		*out_dtm = x;		
	} else
		*out_dtm = iFORBID;

	#if !defined(USE_FD)
	fclose (finp);
	#endif

	return ok;
}

/* will get defined later */
static bool_t			dtm_cache_is_on (void);

static bool_t
egtb_get_dtm (tbkey_t k, unsigned stm, const SQUARE *wS, const SQUARE *bS, bool_t probe_hard_flag, dtm_t *dtm)
{
	bool_t idxavail;
	index_t idx;
	dtm_t *tab[2];
	bool_t (*pc2idx) (const SQUARE *, const SQUARE *, index_t *);

	FOLLOW_label("egtb_get_dtm --> starts")

	if (egkey[k].status == STATUS_MALLOC || egkey[k].status == STATUS_STATICRAM) {

		tab[WH] = egkey[k].egt_w;
		tab[BL] = egkey[k].egt_b;
		pc2idx  = egkey[k].pctoi;

		idxavail = pc2idx (wS, bS, &idx);

		FOLLOW_LU("indexavail (RAM)",idxavail)

		if (idxavail) {
			*dtm = tab[stm][idx];
		} else {
			*dtm = iFORBID;
		}

		return TRUE;

	} else if (egkey[k].status == STATUS_ABSENT) {

		pc2idx   = egkey[k].pctoi;
		idxavail = pc2idx (wS, bS, &idx);

		FOLLOW_LU("indexavail (HD)",idxavail)

		if (idxavail) {
			bool_t success;

			/* 
			|		LOCK 
			*-------------------------------*/
			mythread_mutex_lock (&Egtb_lock);	

			if (dtm_cache_is_on()) {

				success = get_dtm       (k, stm, idx, dtm, probe_hard_flag);

				FOLLOW_LU("get_dtm (succ)",success)
				FOLLOW_LU("get_dtm (dtm )",*dtm)

					#if defined(DEBUG)
					if (Uncompressed) {
						dtm_t 	dtm_temp;
						bool_t 	ok;
						bool_t 	success2;

						assert (decoding_scheme() == 0 && GTB_scheme == 0);

						success2 = egtb_filepeek (k, stm, idx, &dtm_temp);
						ok =  (success == success2) && (!success || *dtm == dtm_temp);
						if (!ok) {
							printf ("\nERROR\nsuccess1=%d sucess2=%d\n"
									"k=%d stm=%u idx=%d dtm_peek=%d dtm_cache=%d\n", 
									success, success2, k, stm, idx, dtm_temp, *dtm);
							fatal_error();
						}
					}
					#endif

			} else {	
				assert(Uncompressed);		
				if (probe_hard_flag && Uncompressed)
					success = egtb_filepeek (k, stm, idx, dtm);
				else
					success = FALSE;
			}

			mythread_mutex_unlock (&Egtb_lock);	
			/*------------------------------*\ 
			|		UNLOCK 
			*/
			

			if (success) {
				return TRUE;
			} else {
				if (probe_hard_flag) /* after probing hard and failing, no chance to succeed later */
					egkey[k].status = STATUS_REJECT;
				*dtm = iUNKNOWN;
				return FALSE;
			}

		} else {
			*dtm = iFORBID;
			return 	TRUE;
		}
		
	} else if (egkey[k].status == STATUS_REJECT) {

		FOLLOW_label("STATUS_REJECT")

		*dtm = iFORBID;
		return 	FALSE;
	} else {

		FOLLOW_label("STATUS_WRONG!")

		assert(0);
		*dtm = iFORBID;
		return 	FALSE;
	} 

} 

static void
removepiece (SQUARE *ys, SQ_CONTENT *yp, int j)
{
    int k;
	for (k = j; ys[k] != NOSQUARE; k++) {
		ys[k] = ys[k+1];
		yp[k] = yp[k+1];
	}
}

/* 
|
|	mySHARED by probe and build 
|
\*----------------------------------------------------*/

mySHARED /*@NULL@*/ FILE *
fd_openit (tbkey_t key)
{	
	int 			i;
	tbkey_t			closingkey;
	FILE *			finp = NULL;
	char	 		buf[4096];
	char *			filename = buf;
	int 			start; 
	int				end;
	int				pth;
	const char *	extension;

	assert (0 <= key && key < MAX_EGKEYS);
	assert (0 <= fd.n && fd.n <= GTB_MAXOPEN);

	/* test if I reach limit of files open */
	if (fd.n == GTB_MAXOPEN) {

		/* fclose the last accessed, first in the list */
		closingkey = fd.key[0];
		finp = egkey [closingkey].fd;
		assert (finp != NULL);
		fclose (finp);
		egkey[closingkey].fd = NULL;
		finp = NULL;

		for (i = 1; i < fd.n; i++) {
			fd.key[i-1] = fd.key[i]; 		
		}
		fd.key[--fd.n] = -1;
	} 

	assert (fd.n < GTB_MAXOPEN);

	/* set proper extensions to the File */
	if (Uncompressed) {
		assert (decoding_scheme() == 0 && GTB_scheme == 0);
		extension = ".gtb";
	} else {
		extension = Extension[decoding_scheme()];
	}

	/* Scan folders to find the File*/
	finp = NULL;

	start = egkey[key].pathn;
	end   = Gtbpath_end_index;

/*@@
printf ("start: %d\n",start);
printf ("===================Gtbpath[0]=%s\n",Gtbpath[0]);
*/
	for (pth = start; NULL == finp && pth < end && Gtbpath[pth] != NULL; pth++) {
		const char *path = Gtbpath[pth];
		size_t pl = strlen(path);
/*@@
printf ("path: %s\n",path);
*/
		if (pl == 0) {
				sprintf (buf, "%s%s%s", path, egkey[key].str, extension);
		} else {
			if (isfoldersep( path[pl-1] )) {
				sprintf (buf, "%s%s%s", path, egkey[key].str, extension);			
			} else {
				sprintf (buf, "%s%s%s%s", path, FOLDERSEP, egkey[key].str, extension);
			}
		}
/*printf ("try to open %s   --> ",filename);*/
		/* Finally found the file? */
		finp = fopen (filename, "rb");
/*printf ("%d\n",finp != NULL);*/
	}

	/* File was found and opened */
	if (NULL != finp) {
		fd.key [fd.n++] = key;
		egkey[key].fd = finp;
		egkey[key].pathn = pth; /* remember succesful path */
		eg_was_open[key] = 1;
		return finp;
	}

	start = 0;
	end   = egkey[key].pathn;
	for (pth = start; NULL == finp && pth < end && Gtbpath[pth] != NULL; pth++) {
		const char *path = Gtbpath[pth];
		size_t pl = strlen(path);

		if (pl == 0) {
				sprintf (buf, "%s%s%s", path, egkey[key].str, extension);
		} else {
			if (isfoldersep( path[pl-1] )) {
				sprintf (buf, "%s%s%s", path, egkey[key].str, extension);			
			} else {
				sprintf (buf, "%s%s%s%s", path, FOLDERSEP, egkey[key].str, extension);
			}
		}
/*printf ("try to open %s   --> ",filename);*/
		/* Finally found the file? */
		finp = fopen (filename, "rb");
/*printf ("%d\n",finp != NULL);*/
	}


	/* File was found and opened */
	if (NULL != finp) {
		fd.key [fd.n++] = key;
		egkey[key].fd = finp;
		egkey[key].pathn = pth; /* remember succesful path */
		eg_was_open[key] = 1;
	}

	return finp;
}

#ifdef _MSC_VER
/* to silence warning for sprintf usage */
#pragma warning(default:4996)
#endif

mySHARED void
sortlists (SQUARE *ws, SQ_CONTENT *wp)
{
	int i, j;
	SQUARE ts;
	SQ_CONTENT tp;
	/* input is sorted */
	for (i = 0; wp[i] != NOPIECE; i++) {
		for (j = (i+1); wp[j] != NOPIECE; j++) {	
			if (wp[j] > wp[i]) {
				tp = wp[i]; wp[i] = wp[j]; wp[j] = tp;		
				ts = ws[i]; ws[i] = ws[j]; ws[j] = ts;
			}			
		}	
	}
}

mySHARED void
list_sq_copy (const SQUARE *a, SQUARE *b)
{
	while (NOSQUARE != (*b++ = *a++))
		;
}

mySHARED void
list_pc_copy (const SQ_CONTENT *a, SQ_CONTENT *b)
{
	while (NOPIECE != (*b++ = *a++))
		;
}

mySHARED dtm_t
inv_dtm (dtm_t x)
{
	unsigned mat;
	assert ( (x & iUNKNBIT) == 0);

	if (x == iDRAW || x == iFORBID)
		return x;
	
	mat = (unsigned)x & 3u;
	if (mat == iWMATE)
		mat = iBMATE;
	else
		mat = iWMATE;

	x = (dtm_t) (((unsigned)x & ~3u) | mat);

	return x;
}

static const char pctoch[] = {'-','p','n','b','r','q','k'};

mySHARED bool_t
egtb_get_id (SQ_CONTENT *w, SQ_CONTENT *b, tbkey_t *id)
{

	char pcstr[2*MAX_LISTSIZE];
	SQ_CONTENT *s; 
	char *t;
	bool_t found;
	tbkey_t i;
	static tbkey_t cache_i = 0;
	
	assert (PAWN == 1 && KNIGHT == 2 && BISHOP == 3 && ROOK == 4 && QUEEN == 5 && KING == 6);

	t = pcstr; 

	s = w;
	while (NOPIECE != *s)
		*t++ = pctoch[*s++]; 		
	s = b;
	while (NOPIECE != *s)
		*t++ = pctoch[*s++]; 		

	*t = '\0';

	found = (0 == strcmp(pcstr, egkey[cache_i].str));
	if (found) {
		*id = cache_i;
		return found;		
	}
	
	for (i = 0, found = FALSE; !found && egkey[i].str != NULL; i++) {
		found = (0 == strcmp(pcstr, egkey[i].str));
	}
	if (found) {
		cache_i = *id = i - 1;
	}
	
	return found;
}

mySHARED void
list_sq_flipNS (SQUARE *s)
{
	while (*s != NOSQUARE) {
		*s ^= 070;
		s++;
	}
}

mySHARED void
unpackdist (dtm_t d, unsigned int *res, unsigned int *ply)
{
	*ply = (unsigned int)d >> PLYSHIFT;
	*res = d & INFOMASK;
}

mySHARED dtm_t
packdist (unsigned int inf, unsigned int ply)
{
	assert (inf <= INFOMASK);
	return (dtm_t) (inf | ply << PLYSHIFT);
}

mySHARED dtm_t
adjust_up (dtm_t dist)
{
	#if 0
	static const dtm_t adding[] = {	
		0, 1<<PLYSHIFT, 1<<PLYSHIFT, 0, 
		0, 1<<PLYSHIFT, 1<<PLYSHIFT, 0
	};
	dist += adding [dist&INFOMASK];
	return dist;
	#else			
	unsigned udist = (unsigned) dist;				
	switch (udist & INFOMASK) {
		case iWMATE:
		case iWMATEt:
		case iBMATE:
		case iBMATEt:
			udist += (1u << PLYSHIFT);
			break;
		default:			
			break;
	}
	return (dtm_t) udist;	
	#endif
}


mySHARED dtm_t
bestx (unsigned stm, dtm_t a, dtm_t b)
{
	unsigned int key;	
	static const unsigned int
	comparison [4] [4] = {
	 			/*draw, wmate, bmate, forbid*/
	/* draw  */ {0, 3, 0, 0},
	/* wmate */ {0, 1, 0, 0},
	/* bmate */ {3, 3, 2, 0},
	/* forbid*/ {3, 3, 3, 0}

	/* 0 = selectfirst   */
	/* 1 = selectlowest  */
	/* 2 = selecthighest */
	/* 3 = selectsecond  */
	};

	static const unsigned int xorkey [2] = {0, 3};	
	dtm_t retu[4];
	dtm_t ret = iFORBID;
	
	assert (stm == WH || stm == BL);
	assert ((a & iUNKNBIT) == 0 && (b & iUNKNBIT) == 0 );
	
	if (a == iFORBID)
		return b;
	if (b == iFORBID)
		return a;	

	retu[0] = a; /* first parameter */
	retu[1] = a; /* the lowest by default */
	retu[2] = b; /* highest by default */
	retu[3]	= b; /* second parameter */
	if (b < a) {
		retu[1] = b;
		retu[2] = a;		
	}
	
	key = comparison [a&3] [b&3] ^ xorkey[stm];
	ret = retu [key];
		
	return ret;
}


/*--------------------------------------------------------------------------*\
 |								PACKING ZONE
 *--------------------------------------------------------------------------*/

mySHARED dtm_t
dtm_unpack (unsigned stm, unsigned char packed)
{
	unsigned int info, plies, prefx, store, moves;
	dtm_t ret;
	unsigned int p = packed;

	if (iDRAW == p || iFORBID == p) {
		return (dtm_t) p;
	}
	
	info  = (unsigned int) p & 3;
	store = (unsigned int) p >> 2;

	if (WH == stm) {
		switch (info) {
			case iWMATE:		
						moves = store + 1;
						plies = moves * 2 - 1;
						prefx = info;
						break;				

			case iBMATE:
						moves = store;
						plies = moves * 2;
						prefx = info;
						break;

			case iDRAW:
						moves = store + 1 + 63;
						plies = moves * 2 - 1;
						prefx = iWMATE;						
						break;

			case iFORBID:

						moves = store + 63;
						plies = moves * 2;
						prefx = iBMATE;
						break;
			default:	
            plies = 0;
            prefx = 0;
            assert(0);
						break;

		}
		ret = (dtm_t) (prefx | (plies << 3));
	} else {
		switch (info) {
			case iBMATE:		
						moves = store + 1;
						plies = moves * 2 - 1;
						prefx = info;
						break;				

			case iWMATE:
						moves = store;
						plies = moves * 2;
						prefx = info;
						break;

			case iDRAW:

						if (store == 63) {
						/* 	exception: no position in the 5-man 
							TBs needs to store 63 for iBMATE 
							it is then used to indicate iWMATE 
							when just overflows */
							store++;

							moves = store + 63;
							plies = moves * 2;
							prefx = iWMATE;	

							break;
						}
	
						moves = store + 1 + 63;
						plies = moves * 2 - 1;
						prefx = iBMATE;						
						break;

			case iFORBID:

						moves = store + 63;
						plies = moves * 2;
						prefx = iWMATE;
						break;
			default:	
            plies = 0;
            prefx = 0;
            assert(0);
						break;

		}
		ret = (dtm_t) (prefx | (plies << 3));
	}
	return ret;	
}


/*
static bool_t fwrite_entry_packed (FILE *dest, unsigned side, dtm_t x);
*/


mySHARED bool_t
fread_entry_packed (FILE *finp, unsigned side, dtm_t *px)
{
	unsigned char p[SLOTSIZE];
	bool_t ok = (SLOTSIZE == fread (p, sizeof(unsigned char), SLOTSIZE, finp));
	if (ok) {
		*px = dtm_unpack (side, p[0]);
	}
	return ok;
}


mySHARED bool_t
fpark_entry_packed  (FILE *finp, unsigned side, index_t max, index_t idx)
{
	bool_t ok;
	index_t i;
	long int fseek_i;
	index_t sz = (index_t) sizeof(unsigned char);	

	assert (side == WH || side == BL);
	assert (finp != NULL);
	assert (idx >= 0);
	i = ((index_t)side * max + idx) * sz;
	fseek_i = (long int) i;
	assert (fseek_i >= 0);
	ok = (0 == fseek (finp, fseek_i, SEEK_SET));
	return ok;
}

/*----------------------------------------------------*\ 
|
|	shared by probe and build 
|
\*/


/*---------------------------------------------------------------------*\
|			WDL CACHE Implementation  ZONE
\*---------------------------------------------------------------------*/

#define WDL_entries_per_unit 4
#define WDL_entry_mask     3
static size_t		WDL_units_per_block = 0;

static bool_t		WDL_CACHE_INITIALIZED = FALSE;

typedef unsigned char unit_t; /* block unit */

typedef struct wdl_block 	wdl_block_t;

struct wdl_block {
	tbkey_t			key;
	unsigned		side;
	index_t 		offset;
	unit_t			*p_arr;
	wdl_block_t		*prev;
	wdl_block_t		*next;
};

struct WDL_CACHE {
	/* defined at init */
	bool_t			cached;
	size_t			max_blocks;
	size_t 			entries_per_block;
	unit_t		 *	buffer;

	/* flushables */
	wdl_block_t	*	top;
	wdl_block_t *	bot;
	size_t			n;
	wdl_block_t *	blocks; /* was entry */

	/* counters */
	uint64_t		hard;
	uint64_t		soft;
	uint64_t		hardmisses;
	uint64_t		hits;
	uint64_t		softmisses;
	uint64_t 		comparisons;
};

struct WDL_CACHE 	wdl_cache = {FALSE,0,0,NULL,
								 NULL,NULL,0,NULL,
								 0,0,0,0,0,0};


/*---------------------------------------------------------------------*\
|			DTM CACHE Implementation  ZONE
\*---------------------------------------------------------------------*/

struct dtm_block;

typedef struct dtm_block dtm_block_t;

struct dtm_block {
	tbkey_t			key;
	unsigned		side;
	index_t 		offset;
	dtm_t			*p_arr;
	dtm_block_t		*prev;
	dtm_block_t		*next;
};

struct cache_table {
	/* defined at init */
	bool_t			cached;
	size_t			max_blocks;
	size_t 			entries_per_block;
	dtm_t *			buffer;

	/* flushables */
	dtm_block_t	*	top;
	dtm_block_t *	bot;
	size_t			n;
	dtm_block_t *	entry;

	/* counters */
	uint64_t		hard;
	uint64_t		soft;
	uint64_t		hardmisses;
	uint64_t		hits;
	uint64_t		softmisses;
	unsigned long	comparisons;
};

struct cache_table 	dtm_cache = {FALSE,0,0,NULL,
								NULL,NULL,0,NULL,
								0,0,0,0,0,0};

struct general_counters {
	/* counters */
	uint64_t		hits;
	uint64_t		miss;
};

static struct general_counters Drive = {0,0};


static void 		split_index (size_t entries_per_block, index_t i, index_t *o, index_t *r);
static dtm_block_t *point_block_to_replace (void);
static bool_t 		preload_cache (tbkey_t key, unsigned side, index_t idx);
static void			movetotop (dtm_block_t *t);

/*--cache prototypes--------------------------------------------------------*/

/*- WDL --------------------------------------------------------------------*/
#ifdef WDL_PROBE
static unsigned int		wdl_extract (unit_t *uarr, index_t x);
static wdl_block_t *	wdl_point_block_to_replace (void);
static void				wdl_movetotop (wdl_block_t *t);

#if 0
static bool_t			wdl_cache_init (size_t cache_mem);
static void				wdl_cache_flush (void);
static bool_t			get_WDL (tbkey_t key, unsigned side, index_t idx, unsigned int *info_out, bool_t probe_hard_flag);
#endif

static bool_t			wdl_cache_is_on (void);
static void				wdl_cache_reset_counters (void);
static void				wdl_cache_done (void);

static wdl_block_t *	wdl_point_block_to_replace (void);
static bool_t			get_WDL_from_cache (tbkey_t key, unsigned side, index_t idx, unsigned int *out);
static void				wdl_movetotop (wdl_block_t *t);
static bool_t			wdl_preload_cache (tbkey_t key, unsigned side, index_t idx);
#endif
/*--------------------------------------------------------------------------*/
/*- DTM --------------------------------------------------------------------*/
static bool_t			dtm_cache_is_on (void);
static void				dtm_cache_reset_counters (void);
static void				dtm_cache_done (void);

static size_t			dtm_cache_init (size_t cache_mem);
static void				dtm_cache_flush (void);
/*--------------------------------------------------------------------------*/

static bool_t
dtm_cache_is_on (void)
{
	return dtm_cache.cached;
}

static void
dtm_cache_reset_counters (void)
{
	dtm_cache.hard = 0;
	dtm_cache.soft = 0;
	dtm_cache.hardmisses = 0;
	dtm_cache.hits = 0;
	dtm_cache.softmisses = 0;
	dtm_cache.comparisons = 0;
	return;
}


static size_t
dtm_cache_init (size_t cache_mem)
{
	unsigned int 	i;
	dtm_block_t 	*p;
	size_t 			entries_per_block;
	size_t 			max_blocks;
	size_t 			block_mem; 

	if (DTM_CACHE_INITIALIZED)
		dtm_cache_done();

	entries_per_block 	= 16 * 1024;  /* fixed, needed for the compression schemes */

	block_mem 			= entries_per_block * sizeof(dtm_t);

	max_blocks 			= cache_mem / block_mem;
	if (!Uncompressed && 1 > max_blocks)
		max_blocks = 1; 
	cache_mem 			= max_blocks * block_mem;


	dtm_cache_reset_counters ();

	dtm_cache.entries_per_block	= entries_per_block;
	dtm_cache.max_blocks 		= max_blocks;
	dtm_cache.cached 			= TRUE;
	dtm_cache.top 				= NULL;
	dtm_cache.bot 				= NULL;
	dtm_cache.n 				= 0;

	if (0 == cache_mem || NULL == (dtm_cache.buffer = (dtm_t *)  malloc (cache_mem))) {
		dtm_cache.cached = FALSE;
		dtm_cache.buffer = NULL;
		dtm_cache.entry = NULL;
		return 0;
	}

	if (0 == max_blocks|| NULL == (dtm_cache.entry  = (dtm_block_t *) malloc (max_blocks * sizeof(dtm_block_t)))) {
		dtm_cache.cached = FALSE;
		dtm_cache.entry = NULL;
		free (dtm_cache.buffer);
		dtm_cache.buffer = NULL;
		return 0;
	}
	
	for (i = 0; i < max_blocks; i++) {
		p = &dtm_cache.entry[i];
		p->key  	= -1;
		p->side 	= gtbNOSIDE;
		p->offset 	= gtbNOINDEX;
		p->p_arr 	= dtm_cache.buffer + i * entries_per_block;
		p->prev 	= NULL;
		p->next 	= NULL;
	}

	DTM_CACHE_INITIALIZED = TRUE;

	return cache_mem;
}


static void
dtm_cache_done (void)
{
	assert(DTM_CACHE_INITIALIZED);

	dtm_cache.cached = FALSE;
	dtm_cache.hard = 0;
	dtm_cache.soft = 0;
	dtm_cache.hardmisses = 0;
	dtm_cache.hits = 0;
	dtm_cache.softmisses = 0;
	dtm_cache.comparisons = 0;
	dtm_cache.max_blocks = 0;
	dtm_cache.entries_per_block = 0;

	dtm_cache.top = NULL;
	dtm_cache.bot = NULL;
	dtm_cache.n = 0;

	if (dtm_cache.buffer != NULL)
		free (dtm_cache.buffer);
	dtm_cache.buffer = NULL;

	if (dtm_cache.entry != NULL)
		free (dtm_cache.entry);
	dtm_cache.entry = NULL;

	DTM_CACHE_INITIALIZED = FALSE;

	return;
}

static void
dtm_cache_flush (void)
{
	unsigned int 	i;
	dtm_block_t 	*p;
	size_t entries_per_block = dtm_cache.entries_per_block;
	size_t max_blocks = dtm_cache.max_blocks;

	dtm_cache.top 				= NULL;
	dtm_cache.bot 				= NULL;
	dtm_cache.n 				= 0;
	
	for (i = 0; i < max_blocks; i++) {
		p = &dtm_cache.entry[i];
		p->key  	= -1;
		p->side 	= gtbNOSIDE;
		p->offset 	= gtbNOINDEX;
		p->p_arr 	= dtm_cache.buffer + i * entries_per_block;
		p->prev 	= NULL;
		p->next 	= NULL;
	}
	dtm_cache_reset_counters ();
	return;
}


/*---- end tbcache zone ----------------------------------------------------------------------*/

extern bool_t
tbcache_is_on (void)
{
	return dtm_cache_is_on() || wdl_cache_is_on();
}


/* STATISTICS OUTPUT */

extern void 
tbstats_get (struct TB_STATS *x)
{
	long unsigned mask = 0xfffffffflu;
	uint64_t memory_hits, total_hits;


	/*
	|	WDL CACHE
	\*---------------------------------------------------*/

	x->wdl_easy_hits[0] = (long unsigned)(wdl_cache.hits & mask);
	x->wdl_easy_hits[1] = (long unsigned)(wdl_cache.hits >> 32);

	x->wdl_hard_prob[0] = (long unsigned)(wdl_cache.hard & mask);
	x->wdl_hard_prob[1] = (long unsigned)(wdl_cache.hard >> 32);

	x->wdl_soft_prob[0] = (long unsigned)(wdl_cache.soft & mask);
	x->wdl_soft_prob[1] = (long unsigned)(wdl_cache.soft >> 32);

	x->wdl_cachesize    = WDL_cache_size;

	/* occupancy */
	x->wdl_occupancy = wdl_cache.max_blocks==0? 0:(double)100.0*(double)wdl_cache.n/(double)wdl_cache.max_blocks;

	/*
	|	DTM CACHE
	\*---------------------------------------------------*/

	x->dtm_easy_hits[0] = (long unsigned)(dtm_cache.hits & mask);
	x->dtm_easy_hits[1] = (long unsigned)(dtm_cache.hits >> 32);

	x->dtm_hard_prob[0] = (long unsigned)(dtm_cache.hard & mask);
	x->dtm_hard_prob[1] = (long unsigned)(dtm_cache.hard >> 32);

	x->dtm_soft_prob[0] = (long unsigned)(dtm_cache.soft & mask);
	x->dtm_soft_prob[1] = (long unsigned)(dtm_cache.soft >> 32);

	x->dtm_cachesize    = DTM_cache_size;

	/* occupancy */
	x->dtm_occupancy = dtm_cache.max_blocks==0? 0:(double)100.0*(double)dtm_cache.n/(double)dtm_cache.max_blocks;

	/*
	|	GENERAL
	\*---------------------------------------------------*/

	/* memory */
	memory_hits = wdl_cache.hits + dtm_cache.hits;
	x->memory_hits[0] = (long unsigned)(memory_hits & mask);
	x->memory_hits[1] = (long unsigned)(memory_hits >> 32);

	/* hard drive */
	x->drive_hits[0] = (long unsigned)(Drive.hits & mask);
	x->drive_hits[1] = (long unsigned)(Drive.hits >> 32);

	x->drive_miss[0] = (long unsigned)(Drive.miss & mask);
	x->drive_miss[1] = (long unsigned)(Drive.miss >> 32);

	x->bytes_read[0] = (long unsigned)(Bytes_read & mask);
	x->bytes_read[1] = (long unsigned)(Bytes_read >> 32);

	x->files_opened = eg_was_open_count();

	/* total */
	total_hits = memory_hits + Drive.hits;
	x->total_hits[0] = (long unsigned)(total_hits & mask);
	x->total_hits[1] = (long unsigned)(total_hits >> 32);

	/* efficiency */
	{ uint64_t denominator = memory_hits + Drive.hits + Drive.miss;
	x->memory_efficiency = 0==denominator? 0: 100.0 * (double)(memory_hits) / (double)(denominator);
	}
}


extern bool_t
tbcache_init (size_t cache_mem, int wdl_fraction)
{
	assert (wdl_fraction <= WDL_FRACTION_MAX && wdl_fraction >= 0);

	/* defensive against input */
	if (wdl_fraction > WDL_FRACTION_MAX) wdl_fraction = WDL_FRACTION_MAX;
	if (wdl_fraction <                0) wdl_fraction = 0;
	WDL_FRACTION = wdl_fraction;
	
	DTM_cache_size = (cache_mem/(size_t)WDL_FRACTION_MAX)*(size_t)(WDL_FRACTION_MAX-WDL_FRACTION);
	WDL_cache_size = (cache_mem/(size_t)WDL_FRACTION_MAX)*(size_t)     				WDL_FRACTION ;

	#ifdef WDL_PROBE
	/* returns the actual memory allocated */
	DTM_cache_size = dtm_cache_init (DTM_cache_size);
	WDL_cache_size = wdl_cache_init (WDL_cache_size);
	#else
	/* returns the actual memory allocated */
	DTM_cache_size = dtm_cache_init (DTM_cache_size);
	#endif
	tbstats_reset ();
	return TRUE;
}

extern bool_t
tbcache_restart (size_t cache_mem, int wdl_fraction)
{
	return tbcache_init (cache_mem, wdl_fraction);
}

extern void
tbcache_done (void)
{
	dtm_cache_done();
	#ifdef WDL_PROBE
	wdl_cache_done();
	#endif
	tbstats_reset ();
	return;
}

extern void
tbcache_flush (void)
{
	dtm_cache_flush();
	#ifdef WDL_PROBE
	wdl_cache_flush();
	#endif
	tbstats_reset ();
	return;
}

extern void	
tbstats_reset (void)
{
	dtm_cache_reset_counters ();
	#ifdef WDL_PROBE
	wdl_cache_reset_counters ();
	#endif
	eg_was_open_reset();
	Drive.hits = 0;
	Drive.miss = 0;
	return;
}

static dtm_block_t	*
dtm_cache_pointblock (tbkey_t key, unsigned side, index_t idx)
{
	index_t 		offset;
	index_t			remainder;
	dtm_block_t	*	p;
	dtm_block_t	*	ret;

	if (!dtm_cache_is_on())
		return NULL;

	split_index (dtm_cache.entries_per_block, idx, &offset, &remainder); 

	ret   = NULL;

	for (p = dtm_cache.top; p != NULL; p = p->prev) {

		dtm_cache.comparisons++;

		if (key == p->key && side == p->side && offset  == p->offset) {
			ret = p;
			break;
		}
	}

	FOLLOW_LU("point_to_dtm_block ok?",(ret!=NULL))

	return ret;
}

/****************************************************************************\
|
|
|			WRAPPERS for ENCODING/DECODING FUNCTIONS ZONE
|
|
\****************************************************************************/

#include "gtb-dec.h"

/*
|
|	PRE LOAD CACHE AND DEPENDENT FUNCTIONS 
|
\*--------------------------------------------------------------------------*/

struct ZIPINFO {
	index_t 	extraoffset;
	index_t 	totalblocks;
	index_t *	blockindex;
};

struct ZIPINFO Zipinfo[MAX_EGKEYS];

static index_t 	egtb_block_getnumber 		(tbkey_t key, unsigned side, index_t idx);
static index_t 	egtb_block_getsize 			(tbkey_t key, index_t idx);
static index_t 	egtb_block_getsize_zipped 	(tbkey_t key, index_t block );
static  bool_t 	egtb_block_park  			(tbkey_t key, index_t block);
static  bool_t 	egtb_block_read 			(tbkey_t key, index_t len, unsigned char *buffer); 
static  bool_t 	egtb_block_decode 			(tbkey_t key, index_t z, unsigned char *bz, index_t n, unsigned char *bp);
static  bool_t 	egtb_block_unpack 			(unsigned side, index_t n, const unsigned char *bp, dtm_t *out);
static  bool_t 	egtb_file_beready 			(tbkey_t key);
static  bool_t 	egtb_loadindexes 			(tbkey_t key);
static index_t 	egtb_block_uncompressed_to_index (tbkey_t key, index_t b);
static  bool_t 	fread32 					(FILE *f, unsigned long int *y);


static unsigned int
zipinfo_init (void)
{
	int i, start, end;
	unsigned ret;
	bool_t ok, complet[8] = {0,0,0,0,0,0,0,0};
	bool_t pa, partial[8] = {0,0,0,0,0,0,0,0};
	unsigned int z;
	int x, j;

	/* reset all values */
	for (i = 0; i < MAX_EGKEYS; i++) {
		Zipinfo[i].blockindex = NULL;
	 	Zipinfo[i].extraoffset = 0;
	 	Zipinfo[i].totalblocks = 0;
	}

	/* load all values */
	start = 0;
	end   = 5;
	x	  = 3;
	for (i = start, ok = TRUE, pa = FALSE; i < end; i++) {
		ok = NULL != fd_openit(i);
		pa = pa || ok;
		ok = ok && egtb_loadindexes (i);
	}
	complet[x] = ok;
	partial[x] = pa;

	start = 5;
	end   = 35;
	x	  = 4;
	for (i = start, ok = TRUE, pa = FALSE; i < end; i++) {
		ok = NULL != fd_openit(i);
		pa = pa || ok;
		ok = ok && egtb_loadindexes (i);
	}
	complet[x] = ok;
	partial[x] = pa;

	start = 35;
	end   = MAX_EGKEYS;
	x	  = 5;
	for (i = start, ok = TRUE, pa = FALSE; i < end; i++) {
		ok = NULL != fd_openit(i);
		pa = pa || ok;
		ok = ok && egtb_loadindexes (i);
	}
	complet[x] = ok;
	partial[x] = pa;


	for (j = 0, z = 0, x = 3; x < 8; x++) {
		if (partial[x]) z |= 1u << j; 
		j++;
		if (complet[x]) z |= 1u << j;
		j++;
	}

	ret = z;

	return ret;
}

static void
zipinfo_done (void)
{
	int i;
	bool_t ok;
	for (i = 0, ok = TRUE; ok && i < MAX_EGKEYS; i++) {
		if (Zipinfo[i].blockindex != NULL) {
			free(Zipinfo[i].blockindex);
			Zipinfo[i].blockindex = NULL;
		 	Zipinfo[i].extraoffset = 0;
		 	Zipinfo[i].totalblocks = 0;
		}
	}
	return;
}

static size_t
zipinfo_memory_allocated (void)
{
	int i;
	index_t accum_blocks = 0;
	for (i = 0; i < MAX_EGKEYS; i++) {
		if (Zipinfo[i].blockindex != NULL) {
		 	accum_blocks += Zipinfo[i].totalblocks;
		}
	}
	return (size_t)accum_blocks * sizeof(index_t);
}

extern size_t
tb_indexmemory (void)
{
	return zipinfo_memory_allocated ();
}

static bool_t
fread32 (FILE *f, unsigned long int *y)
{
	enum SIZE {SZ = 4};
	int i;
	unsigned long int x;
	unsigned char p[SZ];
	bool_t ok;

	ok = (SZ == fread (p, sizeof(unsigned char), SZ, f));

	if (ok) {
		for (x = 0, i = 0; i < SZ; i++) {
			x |= (unsigned long int)p[i] << (i*8);
		}
		*y = x;
	}
	return ok;
}

static bool_t
egtb_loadindexes (tbkey_t key)
{

	unsigned long int blocksize = 1;
	unsigned long int tailblocksize1 = 0;
	unsigned long int tailblocksize2 = 0;
    unsigned long int offset=0;
	unsigned long int dummy;
	unsigned long int i;
	unsigned long int blocks;
	unsigned long int n_idx;
	unsigned long int idx;
	index_t	*p;

	bool_t ok;

	FILE *f;

	if (Uncompressed) {
		assert (decoding_scheme() == 0 && GTB_scheme == 0);	
		return TRUE; /* no need to load indexes */
	}
	if (Zipinfo[key].blockindex != NULL)
		return TRUE; /* indexes must have been loaded already */

	if (NULL == (f = egkey[key].fd))
		return FALSE; /* file was no open */

	/* Get Reserved bytes, blocksize, offset */
	ok = (0 == fseek (f, 0, SEEK_SET)) &&
	fread32 (f, &dummy) &&	
	fread32 (f, &dummy) &&
	fread32 (f, &blocksize) &&
	fread32 (f, &dummy) &&
	fread32 (f, &tailblocksize1) &&
	fread32 (f, &dummy) &&
	fread32 (f, &tailblocksize2) &&
	fread32 (f, &dummy) &&
	fread32 (f, &offset) &&
	fread32 (f, &dummy);

	blocks = (offset - 40)/4 -1;
	n_idx = blocks + 1;

	p = NULL;

	ok = ok && NULL != (p = (index_t *)malloc (n_idx * sizeof(index_t)));

	/* Input of Indexes */
	for (i = 0; ok && i < n_idx; i++) {
		ok = fread32 (f, &idx);
		p[i] = (index_t)idx; /* reads a 32 bit int, and converts it to index_t */ assert (sizeof(index_t) >= 4);
	}

	if (ok) {
		Zipinfo[key].extraoffset = 0;	
		assert (n_idx <= MAXINDEX_T);
		Zipinfo[key].totalblocks = (index_t) n_idx; 
		Zipinfo[key].blockindex  = p;
	}	

	if (!ok && p != NULL) {
		free(p);
	}

	return ok;
}

static index_t
egtb_block_uncompressed_to_index (tbkey_t key, index_t b)
{
	index_t max;
	index_t blocks_per_side;
	index_t idx;

	max = egkey[key].maxindex;
	blocks_per_side = 1 + (max-1) / (index_t)dtm_cache.entries_per_block;

	if (b < blocks_per_side) {
		idx = 0;
	} else {
		b -= blocks_per_side;
		idx = max;
	}
	idx += b * (index_t)dtm_cache.entries_per_block;
	return idx;
}


static index_t
egtb_block_getnumber (tbkey_t key, unsigned side, index_t idx)
{
	index_t blocks_per_side;
	index_t block_in_side;
	index_t max = egkey[key].maxindex;

	blocks_per_side = 1 + (max-1) / (index_t)dtm_cache.entries_per_block;
	block_in_side   = idx         / (index_t)dtm_cache.entries_per_block;

	return (index_t)side * blocks_per_side + block_in_side; /* block */
}


static index_t 
egtb_block_getsize (tbkey_t key, index_t idx)
{
	index_t blocksz = (index_t) dtm_cache.entries_per_block;
	index_t maxindex  = egkey[key].maxindex;
	index_t block, offset, x; 

	assert (dtm_cache.entries_per_block <= MAXINDEX_T);
	assert (0 <= idx && idx < maxindex);
	assert (key < MAX_EGKEYS);

	block = idx / blocksz;
	offset = block * blocksz;

	/* 
	|	adjust block size in case that this is the last block 
	|	and is shorter than "blocksz" 
	*/
	if ( (offset + blocksz) > maxindex) 
		x = maxindex - offset; /* last block size */
	else
		x = blocksz; /* size of a normal block */
	
	return x;
}

static index_t 
egtb_block_getsize_zipped (tbkey_t key, index_t block )
{
	index_t i, j;
	assert (Zipinfo[key].blockindex != NULL);
	i = Zipinfo[key].blockindex[block];
	j = Zipinfo[key].blockindex[block+1];	
	return j - i;
}


static bool_t
egtb_file_beready (tbkey_t key)
{
	bool_t success;
	assert (key < MAX_EGKEYS);
	success = 	(NULL != egkey[key].fd) ||
				(NULL != fd_openit(key) && egtb_loadindexes (key));
	return success; 
}


static bool_t
egtb_block_park  (tbkey_t key, index_t block)
{
	index_t i;
	long fseek_i;
	assert (egkey[key].fd != NULL);

	if (Uncompressed) {
		assert (decoding_scheme() == 0 && GTB_scheme == 0);	
		i = egtb_block_uncompressed_to_index (key, block);
	} else {
		assert (Zipinfo[key].blockindex != NULL);
		i  = Zipinfo[key].blockindex[block];
		i += Zipinfo[key].extraoffset;
	}

	fseek_i = (long) i;
	assert (fseek_i >= 0);
	return 0 == fseek (egkey[key].fd, fseek_i, SEEK_SET);
}


static bool_t
egtb_block_read (tbkey_t key, index_t len, unsigned char *buffer) 
{
	assert (egkey[key].fd != NULL);
	assert (sizeof(size_t) >= sizeof(len));
	return ((size_t)len == fread (buffer, sizeof (unsigned char), (size_t)len, egkey[key].fd));	
}

tbkey_t TB_PROBE_indexing_dummy;

static bool_t
egtb_block_decode (tbkey_t key, index_t z, unsigned char *bz, index_t n, unsigned char *bp)
/* bz:buffer zipped to bp:buffer packed */
{
	size_t zz = (size_t) z;
	size_t nn = (size_t) n;
	TB_PROBE_indexing_dummy = key; /* to silence compiler */
	assert (sizeof(size_t) >= sizeof(n));
	assert (sizeof(size_t) >= sizeof(z));
	return decode (zz-1, bz+1, nn, bp);
}

static bool_t
egtb_block_unpack (unsigned side, index_t n, const unsigned char *bp, dtm_t *out)
/* bp:buffer packed to out:distance to mate buffer */
{
	index_t i;
	for (i = 0; i < n; i++) {
		*out++ = dtm_unpack (side, bp[i]);		
	}	
	return TRUE;
}

static bool_t
preload_cache (tbkey_t key, unsigned side, index_t idx)
/* output to the least used block of the cache */
{
	dtm_block_t 	*pblock;
	dtm_t 			*p;
	bool_t 			ok;

	FOLLOW_label("preload_cache starts")

	if (idx >= egkey[key].maxindex) {
		FOLLOW_LULU("Wrong index", __LINE__, idx)	
		return FALSE;
	}
	
	/* find aged blocked in cache */
	pblock = point_block_to_replace();

	if (NULL == pblock)
		return FALSE;

	p = pblock->p_arr;

	if (Uncompressed) {

		index_t block = egtb_block_getnumber (key, side, idx);
		index_t n     = egtb_block_getsize   (key, idx);

		ok =	   egtb_file_beready (key)
				&& egtb_block_park   (key, block)
				&& egtb_block_read   (key, n, Buffer_packed)
				&& egtb_block_unpack (side, n, Buffer_packed, p);	

		FOLLOW_LULU("preload_cache", __LINE__, ok)

		assert (decoding_scheme() == 0 && GTB_scheme == 0);	

		if (ok) { Bytes_read = Bytes_read + (uint64_t) n; }

	} else {

        index_t block = 0;
		index_t n = 0;
		index_t z = 0;
		
		ok =	   egtb_file_beready (key);

		FOLLOW_LULU("preload_cache", __LINE__, ok)

		if (ok) {
			block = egtb_block_getnumber (key, side, idx);
			n     = egtb_block_getsize   (key, idx);
			z     = egtb_block_getsize_zipped (key, block);				
		}

		ok =	   ok
				&& egtb_block_park   (key, block);
		FOLLOW_LULU("preload_cache", __LINE__, ok)

		ok =	   ok	
				&& egtb_block_read   (key, z, Buffer_zipped);
		FOLLOW_LULU("preload_cache", __LINE__, ok)

		ok =	   ok		
				&& egtb_block_decode (key, z, Buffer_zipped, n, Buffer_packed);
		FOLLOW_LULU("preload_cache", __LINE__, ok)

		ok =	   ok		 
				&& egtb_block_unpack (side, n, Buffer_packed, p);	
		FOLLOW_LULU("preload_cache", __LINE__, ok)

		if (ok) { Bytes_read = Bytes_read + (uint64_t) z; }
	}

	if (ok) {

		index_t 		offset;
		index_t			remainder;
		split_index (dtm_cache.entries_per_block, idx, &offset, &remainder); 

		pblock->key    = key;
		pblock->side   = side;
		pblock->offset = offset;
	} else {
		/* make it unusable */
		pblock->key    = -1;
		pblock->side   = gtbNOSIDE;
		pblock->offset = gtbNOINDEX;
	}

	FOLLOW_LU("preload_cache?", ok)

	return ok;		
}

/****************************************************************************\
|
|
|						MEMORY ALLOCATION ZONE
|
|
\****************************************************************************/


mySHARED void
egtb_freemem (int i)
{
	if (egkey[i].status == STATUS_MALLOC) {
		assert (egkey[i].egt_w != NULL);
		assert (egkey[i].egt_b != NULL);	
		free (egkey[i].egt_w);
		free (egkey[i].egt_b);	
		egkey[i].egt_w = NULL;
		egkey[i].egt_b = NULL;
	}	
	egkey[i].status = STATUS_ABSENT;	
}

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

mySHARED bool_t
get_dtm (tbkey_t key, unsigned side, index_t idx, dtm_t *out, bool_t probe_hard_flag)
{
	bool_t found;

	if (probe_hard_flag) {
		dtm_cache.hard++;
	} else {
		dtm_cache.soft++;
	}

	if (get_dtm_from_cache (key, side, idx, out)) {
		dtm_cache.hits++;
		found = TRUE;
	} else if (probe_hard_flag) {
		dtm_cache.hardmisses++;
		found = preload_cache (key, side, idx) &&
				get_dtm_from_cache (key, side, idx, out);

		if (found) {
			Drive.hits++;			
		} else {
			Drive.miss++;					
		}
			

	} else {
		dtm_cache.softmisses++;
		found = FALSE;
	}
	return found;
}


static bool_t
get_dtm_from_cache (tbkey_t key, unsigned side, index_t idx, dtm_t *out)
{
	index_t 	offset;
	index_t		remainder;
	bool_t 		found;
	dtm_block_t	*p;

	if (!dtm_cache_is_on())
		return FALSE;

	split_index (dtm_cache.entries_per_block, idx, &offset, &remainder); 

	found = NULL != (p = dtm_cache_pointblock (key, side, idx));

	if (found) {
		*out = p->p_arr[remainder];
		movetotop(p);
	}

	FOLLOW_LU("get_dtm_from_cache ok?",found)

	return found;
}


static void
split_index (size_t entries_per_block, index_t i, index_t *o, index_t *r)
{
	index_t n;
	n  = i / (index_t) entries_per_block;
	*o = n * (index_t) entries_per_block;
	*r = i - *o;
	return;
}


static dtm_block_t *
point_block_to_replace (void)
{
	dtm_block_t *p, *t, *s;

	assert (0 == dtm_cache.n || dtm_cache.top != NULL);
	assert (0 == dtm_cache.n || dtm_cache.bot != NULL);
	assert (0 == dtm_cache.n || dtm_cache.bot->prev == NULL);
	assert (0 == dtm_cache.n || dtm_cache.top->next == NULL);

	/* no cache is being used */
	if (dtm_cache.max_blocks == 0)
		return NULL;

	if (dtm_cache.n > 0 && -1 == dtm_cache.top->key) {

		/* top entry is unusable, should be the one to replace*/
		p = dtm_cache.top;

	} else
	if (dtm_cache.n == 0) {
		
		assert (NULL != dtm_cache.entry);
		p = &dtm_cache.entry[dtm_cache.n++];
		dtm_cache.top = p;
		dtm_cache.bot = p;
	
		assert (NULL != p);
		p->prev = NULL;
		p->next = NULL;

	} else
	if (dtm_cache.n < dtm_cache.max_blocks) { /* add */

		assert (NULL != dtm_cache.entry);
		s = dtm_cache.top;
		p = &dtm_cache.entry[dtm_cache.n++];
		dtm_cache.top = p;
	
		assert (NULL != p && NULL != s);
		s->next = p;
		p->prev = s;
		p->next = NULL;

	} else if (1 < dtm_cache.max_blocks) { /* replace*/ 
		
		assert (NULL != dtm_cache.bot && NULL != dtm_cache.top);
		t = dtm_cache.bot;
		s = dtm_cache.top;

		dtm_cache.bot = t->next;
		dtm_cache.top = t;

		s->next = t;
		t->prev = s;

		assert (dtm_cache.top);
		dtm_cache.top->next = NULL;

		assert (dtm_cache.bot);
		dtm_cache.bot->prev = NULL;

		p = t;

	} else {
		
		assert (1 == dtm_cache.max_blocks);
		p =	dtm_cache.top;
		assert (p == dtm_cache.bot && p == dtm_cache.entry);
	}
	
	/* make the information content unusable, it will be replaced */
	p->key    = -1;
	p->side   = gtbNOSIDE;
	p->offset = gtbNOINDEX;

	return p;
}

static void
movetotop (dtm_block_t *t)
{
	dtm_block_t *s, *nx, *pv;

	assert (t != NULL);

	if (t->next == NULL) /* at the top already */
		return;

	/* detach */
	pv = t->prev;
	nx = t->next;

	if (pv == NULL)  /* at the bottom */
		dtm_cache.bot = nx;
	else 
		pv->next = nx;

	if (nx == NULL) /* at the top */
		dtm_cache.top = pv;
	else
		nx->prev = pv;

	/* relocate */
	s = dtm_cache.top;
	assert (s != NULL);
	if (s == NULL)
		dtm_cache.bot = t;	
	else
		s->next = t;

	t->next = NULL;
	t->prev = s;
	dtm_cache.top = t;

	return;
}

/****************************************************************************\
 *
 *
 *								INDEXING ZONE
 *
 *
 ****************************************************************************/

static void
init_indexing (int verbosity)
{
	index_t a,b,c,d,e,f;

	init_flipt ();

	a = init_kkidx     () ;	
	b = init_ppidx     () ;	
	c = init_aaidx     () ;
	d = init_aaa       () ;
	e = init_pp48_idx  () ;
	f = init_ppp48_idx () ;

	if (verbosity) {
		printf ("\nGTB supporting tables, Initialization\n");
		printf ("  Max    kk idx: %8d\n", (int)a );	
		printf ("  Max    pp idx: %8d\n", (int)b );	
		printf ("  Max    aa idx: %8d\n", (int)c );
		printf ("  Max   aaa idx: %8d\n", (int)d );
		printf ("  Max  pp48 idx: %8d\n", (int)e );
		printf ("  Max ppp48 idx: %8d\n", (int)f );
	}

	if (!reach_was_initialized())
		reach_init();

	/* testing used only in development stage */

#ifdef _MSC_VER
#pragma warning(disable:4127)
#endif

	if (0) {
		list_index ();
		printf ("\nTEST indexing functions\n");

		test_kaakb ();
		test_kaabk ();
		test_kaaak ();
		test_kabbk ();
	
		test_kapkb ();
		test_kabkp ();
	
		test_kppka ();

		test_kapkp ();
		test_kabpk();
		test_kaapk ();
	
		test_kappk ();	
		test_kaakp ();
		test_kppk ();
	 	test_kppkp ();
	 	test_kpppk ();
	}	

#ifdef _MSC_VER
#pragma warning(default:4127)
#endif

	return;
}


static index_t
init_kkidx (void)
/* modifies kkidx[][], wksq[], bksq[] */
{
	index_t idx;
	SQUARE x, y, i, j;
	
	/* default is noindex */
	for (x = 0; x < 64; x++) {
		for (y = 0; y < 64; y++) {
			IDX_set_empty(kkidx [x][y]);
		}
	}

	idx = 0;
	for (x = 0; x < 64; x++) {
		for (y = 0; y < 64; y++) {
		
			/* is x,y illegal? continue */
			if (possible_attack (x, y, wK) || x == y)
				continue;
		
			/* normalize */
			/*i <-- x; j <-- y */
			norm_kkindex (x, y, &i, &j);
		
			if (IDX_is_empty(kkidx [i][j])) { /* still empty */
				kkidx [i][j] = idx;
				kkidx [x][y] = idx;
				bksq [idx] = i;
				wksq [idx] = j;			
				idx++;
			}
		}
	}
	
	assert (idx == MAX_KKINDEX);

	return idx;
}


static index_t
init_aaidx (void)
/* modifies aabase[], aaidx[][] */
{
	index_t idx;
	SQUARE x, y;
	
	/* default is noindex */
	for (x = 0; x < 64; x++) {
		for (y = 0; y < 64; y++) {
			IDX_set_empty(aaidx [x][y]);
		}
	}

	for (idx = 0; idx < MAX_AAINDEX; idx++)
		aabase [idx] = 0;

	idx = 0;
	for (x = 0; x < 64; x++) {
		for (y = x + 1; y < 64; y++) {

			assert (idx == (int)((y - x) + x * (127-x)/2 - 1) );

			if (IDX_is_empty(aaidx [x][y])) { /* still empty */
				aaidx [x] [y] = idx; 
				aaidx [y] [x] = idx;
				aabase [idx] = (unsigned char) x;
				idx++;
			} else {
				assert (aaidx [x] [y] == idx);
				assert (aabase [idx] == x);
			}


		}
	}
	
	assert (idx == MAX_AAINDEX);

	return idx;
}


static index_t
init_ppidx (void)
/* modifies ppidx[][], pp_hi24[], pp_lo48[] */
{
	index_t i, j;
	index_t idx = 0;
	SQUARE a, b;

	/* default is noindex */
	for (i = 0; i < 24; i++) {
		for (j = 0; j < 48; j++) {
			IDX_set_empty(ppidx [i][j]);
		}
	}
		
	for (idx = 0; idx < MAX_PPINDEX; idx++) {
		IDX_set_empty(pp_hi24 [idx]);
		IDX_set_empty(pp_lo48 [idx]);		
	}		
		
	idx = 0;
	for (a = H7; a >= A2; a--) {

		if ((a & 07) < 4) /* square in the queen side */
			continue;

		for (b = a - 1; b >= A2; b--) {
			
			SQUARE anchor, loosen;
	
			pp_putanchorfirst (a, b, &anchor, &loosen);
			
			if ((anchor & 07) > 3) { /* square in the king side */
				anchor = flipWE(anchor);
				loosen = flipWE(loosen);				
			}
			
			i = wsq_to_pidx24 (anchor);
			j = wsq_to_pidx48 (loosen);
			
			if (IDX_is_empty(ppidx [i] [j])) {

                ppidx [i] [j] = idx;
                assert (idx < MAX_PPINDEX);
                pp_hi24 [idx] = i;
                assert (i < 24);
                pp_lo48 [idx] =	j;
                assert (j < 48);
				idx++;
			}
			
		}	
	}
	assert (idx == MAX_PPINDEX);
	return idx;
}

static void
init_flipt (void)
{
	unsigned int i, j;
	for (i = 0; i < 64; i++) {
		for (j = 0; j < 64; j++) {
			flipt [i] [j] = flip_type (i, j);
		}		
	}
}

/*--- NORMALIZE -------*/

static void
norm_kkindex (SQUARE x, SQUARE y, /*@out@*/ SQUARE *pi, /*@out@*/ SQUARE *pj)
{
	unsigned int rowx, rowy, colx, coly;
	
	assert (x < 64);
	assert (y < 64);
	
	if (getcol(x) > 3) { 
		x = flipWE (x); /* x = x ^ 07  */
		y = flipWE (y);		
	}
	if (getrow(x) > 3)  { 
		x = flipNS (x); /* x = x ^ 070  */
		y = flipNS (y);		
	}	
	rowx = getrow(x);
	colx = getcol(x);
	if ( rowx > colx ) {
		x = flipNW_SE (x); /* x = ((x&7)<<3) | (x>>3) */
		y = flipNW_SE (y);			
	}
	rowy = getrow(y);
	coly = getcol(y);	
	if ( rowx == colx && rowy > coly) {
		x = flipNW_SE (x);
		y = flipNW_SE (y);			
	}	
	
	*pi = x;
	*pj = y;
}

static unsigned int
flip_type (SQUARE x, SQUARE y)
{
	unsigned int rowx, rowy, colx, coly;
	unsigned int ret = 0;
	
	assert (x < 64);
	assert (y < 64);
	
	
	if (getcol(x) > 3) { 
		x = flipWE (x); /* x = x ^ 07  */
		y = flipWE (y);		
		ret |= 1;
	}
	if (getrow(x) > 3)  { 
		x = flipNS (x); /* x = x ^ 070  */
		y = flipNS (y);		
		ret |= 2;		
	}	
	rowx = getrow(x);
	colx = getcol(x);
	if ( rowx > colx ) {
		x = flipNW_SE (x); /* x = ((x&7)<<3) | (x>>3) */
		y = flipNW_SE (y);	
		ret |= 4;				
	}
	rowy = getrow(y);
	coly = getcol(y);	
	if ( rowx == colx && rowy > coly) {
		x = flipNW_SE (x);
		y = flipNW_SE (y);	
		ret |= 4;					
	}	
	return ret;
}


static void
pp_putanchorfirst (SQUARE a, SQUARE b, /*@out@*/ SQUARE *out_anchor, /*@out@*/ SQUARE *out_loosen)
{
	unsigned int anchor, loosen;
			
	unsigned int row_b, row_a;
	row_b = b & 070;
	row_a = a & 070;
			
	/* default */
	anchor = a;
	loosen = b;
	if (row_b > row_a) {
		anchor = b;
		loosen = a;
	} 
	else
	if (row_b == row_a) {
		unsigned int x, col, inv, hi_a, hi_b;
		x = a;
		col = x & 07;
		inv = col ^ 07;
		x = (1u<<col) | (1u<<inv);
		x &= (x-1);	
		hi_a = x;
		
		x = b;
		col = x & 07;
		inv = col ^ 07;
		x = (1u<<col) | (1u<<inv);
		x &= (x-1);	
		hi_b = x;			
				
		if (hi_b > hi_a) {
			anchor = b;
			loosen = a;					
		}

		if (hi_b < hi_a) {
			anchor = a;
			loosen = b;					
		}

		if (hi_b == hi_a) {
			if (a < b) {
				anchor = a;
				loosen = b;	
			} else {
				anchor = b;
				loosen = a;	
			}				
		}
	}

	*out_anchor = anchor;
	*out_loosen = loosen;
	return;
}


static index_t
wsq_to_pidx24 (SQUARE pawn)
{
	unsigned int idx24;
	SQUARE sq = pawn;

	/* input can be only queen side, pawn valid */
	assert (A2 <= pawn && pawn < A8);
	assert ((pawn & 07) < 4);

	sq ^= 070; /* flipNS*/
	sq -= 8;   /* down one row*/
	idx24 = (sq+(sq&3)) >> 1; 
	assert (idx24 < 24);
	return (index_t) idx24;
}

static index_t
wsq_to_pidx48 (SQUARE pawn)
{
	unsigned int idx48;
	SQUARE sq = pawn;

	/* input can be both queen or king side, pawn valid square  */
	assert (A2 <= pawn && pawn < A8);

	sq ^= 070; /* flipNS*/
	sq -= 8;   /* down one row*/
	idx48 = sq;
	assert (idx48 < 48);
	return (index_t)idx48;
}

static SQUARE
pidx24_to_wsq (index_t a)
{
	enum  {B11100  = 7u << 2};
	unsigned int x = (unsigned int) a; 	/* x is pslice */
	assert (a < 24);

	x += x & B11100; /* get upper part and double it */
	x += 8;          /* add extra row  */
	x ^= 070;        /* flip NS */
	return (SQUARE) x;
}

static SQUARE
pidx48_to_wsq (index_t a)
{
	unsigned int x;
	assert (a < 48);
	/* x is pslice */
	x = (unsigned int)a;
	x += 8;          /* add extra row  */
	x ^= 070;        /* flip NS */
	return x;
}


static void
kxk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {BLOCK_A = 64}; 

	index_t a = i / BLOCK_A;
	index_t b = i - a * BLOCK_A;
	
	pw[0] = wksq [a];
	pb[0] = bksq [a];
	pw[1] = (SQUARE) b;
	pw[2] = NOSQUARE;
	pb[1] = NOSQUARE;
	
	assert (kxk_pctoindex (pw, pb, &a) && a == i);

	return;
}

static bool_t 
kxk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out)
{
	enum  {BLOCK_A = 64}; 
	SQUARE *p;
	SQUARE ws[32], bs[32];
	index_t ki;
	int i;
	
	unsigned int ft;
	
	ft = flip_type (inp_pb[0],inp_pw[0]);

	assert (ft < 8);


	for (i = 0; inp_pw[i] != NOSQUARE; i++) {
		ws[i] = inp_pw[i];
	}
	ws[i] = NOSQUARE;
	for (i = 0; inp_pb[i] != NOSQUARE; i++) {
		bs[i] = inp_pb[i];
	}
	bs[i] = NOSQUARE;

	if ((ft & 1) != 0) {
		for (p = ws; *p != NOSQUARE; p++) 
				*p = flipWE (*p);
		for (p = bs; *p != NOSQUARE; p++) 
				*p = flipWE (*p);
	}

	if ((ft & 2) != 0) {
		for (p = ws; *p != NOSQUARE; p++) 
				*p = flipNS (*p);
		for (p = bs; *p != NOSQUARE; p++) 
				*p = flipNS (*p);
	}

	if ((ft & 4) != 0) {
		for (p = ws; *p != NOSQUARE; p++) 
				*p = flipNW_SE (*p);
		for (p = bs; *p != NOSQUARE; p++) 
				*p = flipNW_SE (*p);
	}

	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */

	if (IDX_is_empty(ki)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + (index_t) ws[1]; 
	return TRUE;
	
}


static void
kabk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 
	index_t a, b, c, r;

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r;
	
	pw[0] = wksq [a];
	pb[0] = bksq [a];

	pw[1] = (SQUARE) b;
	pw[2] = (SQUARE) c;
	pw[3] = NOSQUARE;

	pb[1] = NOSQUARE;
	
	assert (kabk_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kabk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out)
{
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 
	SQUARE *p;
	SQUARE ws[32], bs[32];
	index_t ki;
	int i;
	
	unsigned int ft;
	
	ft = flip_type (inp_pb[0],inp_pw[0]);

	assert (ft < 8);

	for (i = 0; inp_pw[i] != NOSQUARE; i++) {
		ws[i] = inp_pw[i];
	}
	ws[i] = NOSQUARE;
	for (i = 0; inp_pb[i] != NOSQUARE; i++) {
		bs[i] = inp_pb[i];
	}
	bs[i] = NOSQUARE;

	if ((ft & 1) != 0) {
		for (p = ws; *p != NOSQUARE; p++) 
				*p = flipWE (*p);
		for (p = bs; *p != NOSQUARE; p++) 
				*p = flipWE (*p);
	}

	if ((ft & 2) != 0) {
		for (p = ws; *p != NOSQUARE; p++) 
				*p = flipNS (*p);
		for (p = bs; *p != NOSQUARE; p++) 
				*p = flipNS (*p);
	}

	if ((ft & 4) != 0) {
		for (p = ws; *p != NOSQUARE; p++) 
				*p = flipNW_SE (*p);
		for (p = bs; *p != NOSQUARE; p++) 
				*p = flipNW_SE (*p);
	}

	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */

	if (IDX_is_empty(ki)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + (index_t)ws[1] * BLOCK_B + (index_t)ws[2]; 
	return TRUE;
	
}


static void
kabkc_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t a, b, c, d, r;

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;
	d  = r;
	
	pw[0] = wksq [a];
	pb[0] = bksq [a];

	pw[1] = (SQUARE) b;
	pw[2] = (SQUARE) c;
	pw[3] = NOSQUARE;

	pb[1] = (SQUARE) d;
	pb[2] = NOSQUARE;
	
	assert (kabkc_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kabkc_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out)
{
	enum  {N_WHITE = 3, N_BLACK = 2};

	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	SQUARE ws[MAX_LISTSIZE], bs[MAX_LISTSIZE];
	index_t ki;
	int i;
	unsigned int ft;

	#if 0
		ft = flip_type (inp_pb[0], inp_pw[0]);
	#else
		ft = flipt [inp_pb[0]] [inp_pw[0]];
	#endif

	assert (ft < 8);

	for (i = 0; i < N_WHITE; i++) ws[i] = inp_pw[i]; ws[N_WHITE] = NOSQUARE;
	for (i = 0; i < N_BLACK; i++) bs[i] = inp_pb[i]; bs[N_BLACK] = NOSQUARE;	

	if ((ft & WE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipWE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipWE (bs[i]);
	}

	if ((ft & NS_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNS (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNS (bs[i]);
	}

	if ((ft & NW_SE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNW_SE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNW_SE (bs[i]);
	}


	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */

	if (IDX_is_empty(ki)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + (index_t)ws[1] * BLOCK_B + (index_t)ws[2] * BLOCK_C + (index_t)bs[1]; 
	return TRUE;
	
}

/* ABC/ ***/

extern void
kabck_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t a, b, c, d, r;

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;
	d  = r;
	
	pw[0] = wksq [a];
	pb[0] = bksq [a];

	pw[1] = (SQUARE) b;
	pw[2] = (SQUARE) c;
	pw[3] = (SQUARE) d;
	pw[4] = NOSQUARE;

	pb[1] = NOSQUARE;
	
	assert (kabck_pctoindex (pw, pb, &a) && a == i);

	return;
}


extern bool_t 
kabck_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out)
{
	enum  {N_WHITE = 4, N_BLACK = 1};
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 

	SQUARE ws[MAX_LISTSIZE], bs[MAX_LISTSIZE];
	index_t ki;
	int i;
	unsigned int ft;

	ft = flipt [inp_pb[0]] [inp_pw[0]];

	assert (ft < 8);

	for (i = 0; i < N_WHITE; i++) ws[i] = inp_pw[i]; ws[N_WHITE] = NOSQUARE;
	for (i = 0; i < N_BLACK; i++) bs[i] = inp_pb[i]; bs[N_BLACK] = NOSQUARE;	

	if ((ft & WE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipWE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipWE (bs[i]);
	}

	if ((ft & NS_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNS (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNS (bs[i]);
	}

	if ((ft & NW_SE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNW_SE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNW_SE (bs[i]);
	}


	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */

	if (IDX_is_empty(ki)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + (index_t)ws[1] * BLOCK_B + (index_t)ws[2] * BLOCK_C + (index_t)ws[3]; 
	return TRUE;
	
}


static void
kakb_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 
	index_t a, b, c, r;

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r;
	
	pw[0] = wksq [a];
	pb[0] = bksq [a];

	pw[1] = (SQUARE) b;
	pw[2] = NOSQUARE;

	pb[1] = (SQUARE) c;
	pb[2] = NOSQUARE;
	
	assert (kakb_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kakb_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out)
{
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 
	SQUARE ws[32], bs[32];
	index_t ki;
	unsigned int ft;
	
	#if 0
		ft = flip_type (inp_pb[0], inp_pw[0]);
	#else
		ft = flipt [inp_pb[0]] [inp_pw[0]];
	#endif

	assert (ft < 8);

	ws[0] = inp_pw[0];
	ws[1] = inp_pw[1];
	ws[2] = NOSQUARE;
	
	bs[0] = inp_pb[0];
	bs[1] = inp_pb[1];
	bs[2] = NOSQUARE;

	if ((ft & 1) != 0) {
		ws[0] = flipWE (ws[0]);
		ws[1] = flipWE (ws[1]);
		bs[0] = flipWE (bs[0]);
		bs[1] = flipWE (bs[1]);
	}

	if ((ft & 2) != 0) {
		ws[0] = flipNS (ws[0]);
		ws[1] = flipNS (ws[1]);
		bs[0] = flipNS (bs[0]);
		bs[1] = flipNS (bs[1]);
	}

	if ((ft & 4) != 0) {
		ws[0] = flipNW_SE (ws[0]);
		ws[1] = flipNW_SE (ws[1]);
		bs[0] = flipNW_SE (bs[0]);
		bs[1] = flipNW_SE (bs[1]);
	}

	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */

	if (IDX_is_empty(ki)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + (index_t)ws[1] * BLOCK_B + (index_t)bs[1]; 
	return TRUE;
	
}

/********************** KAAKB *************************************/

static bool_t 	test_kaakb (void);
static bool_t 	kaakb_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kaakb_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kaakb (void)
{

	enum  {MAXPC = 16+1};
	char 		str[] = "kaakb";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = NOSQUARE;
		
			pb[0] = d;
			pb[1] = e;
			pb[2] = NOSQUARE;
	
			if (kaakb_pctoindex (pw, pb, &i)) {
							kaakb_indextopc (i, px, py);		
							kaakb_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}

static void
kaakb_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {
			BLOCK_B = 64,
			BLOCK_A = BLOCK_B * MAX_AAINDEX 
		}; 
	index_t a, b, c, r, x, y;

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;

	b  = r / BLOCK_B;
	r -= b * BLOCK_B;

	c  = r;
	
	assert (i == (a * BLOCK_A + b * BLOCK_B + c));

	pw[0] = wksq [a];
	pb[0] = bksq [a];

	x = aabase [b];
	y = (b + 1) + x - (x * (127-x)/2);

	pw[1] = (SQUARE) x;
	pw[2] = (SQUARE) y;
	pw[3] = NOSQUARE;

	pb[1] = (SQUARE) c;
	pb[2] = NOSQUARE;
	
	assert (kaakb_pctoindex (pw, pb, &a) && a == i);

	return;
}

static bool_t 
kaakb_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out)
{
	enum  {N_WHITE = 3, N_BLACK = 2};
	enum  {
			BLOCK_B = 64,
			BLOCK_A = BLOCK_B * MAX_AAINDEX 
		}; 
	SQUARE ws[MAX_LISTSIZE], bs[MAX_LISTSIZE];
	index_t ki, ai;
	unsigned int ft;
	int i;

	ft = flipt [inp_pb[0]] [inp_pw[0]];

	assert (ft < 8);

	for (i = 0; i < N_WHITE; i++) ws[i] = inp_pw[i]; ws[N_WHITE] = NOSQUARE;
	for (i = 0; i < N_BLACK; i++) bs[i] = inp_pb[i]; bs[N_BLACK] = NOSQUARE;	

	if ((ft & WE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipWE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipWE (bs[i]);
	}

	if ((ft & NS_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNS (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNS (bs[i]);
	}

	if ((ft & NW_SE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNW_SE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNW_SE (bs[i]);
	}

	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */
	ai = aaidx [ws[1]] [ws[2]];

	if (IDX_is_empty(ki) || IDX_is_empty(ai)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + ai * BLOCK_B + (index_t)bs[1]; 
	return TRUE;
}

/****************** End KAAKB *************************************/

/********************** KAAB/K ************************************/

static bool_t 	test_kaabk (void);
static bool_t 	kaabk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kaabk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kaabk (void)
{

	enum  {MAXPC = 16+1};
	char 		str[] = "kaabk";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = d;
			pw[4] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = NOSQUARE;
	
			if (kaabk_pctoindex (pw, pb, &i)) {
							kaabk_indextopc (i, px, py);		
							kaabk_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}

static void
kaabk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {
			BLOCK_B = 64,
			BLOCK_A = BLOCK_B * MAX_AAINDEX 
		}; 
	index_t a, b, c, r, x, y;

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;

	b  = r / BLOCK_B;
	r -= b * BLOCK_B;

	c  = r;
	
	assert (i == (a * BLOCK_A + b * BLOCK_B + c));

	pw[0] = wksq [a];
	pb[0] = bksq [a];

	x = aabase [b];
	y = (b + 1) + x - (x * (127-x)/2);

	pw[1] = (SQUARE) x;
	pw[2] = (SQUARE) y;
	pw[3] = (SQUARE) c;
	pw[4] = NOSQUARE;

	pb[1] = NOSQUARE;
	
	assert (kaabk_pctoindex (pw, pb, &a) && a == i);

	return;
}

static bool_t 
kaabk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out)
{
	enum  {N_WHITE = 4, N_BLACK = 1};
	enum  {
			BLOCK_B = 64,
			BLOCK_A = BLOCK_B * MAX_AAINDEX 
		}; 
	SQUARE ws[MAX_LISTSIZE], bs[MAX_LISTSIZE];
	index_t ki, ai;
	unsigned int ft;
	int i;

	ft = flipt [inp_pb[0]] [inp_pw[0]];

	assert (ft < 8);

	for (i = 0; i < N_WHITE; i++) ws[i] = inp_pw[i]; ws[N_WHITE] = NOSQUARE;
	for (i = 0; i < N_BLACK; i++) bs[i] = inp_pb[i]; bs[N_BLACK] = NOSQUARE;	

	if ((ft & WE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipWE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipWE (bs[i]);
	}

	if ((ft & NS_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNS (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNS (bs[i]);
	}

	if ((ft & NW_SE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNW_SE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNW_SE (bs[i]);
	}

	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */
	ai = aaidx [ws[1]] [ws[2]];

	if (IDX_is_empty(ki) || IDX_is_empty(ai)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + ai * BLOCK_B + (index_t)ws[3]; 
	return TRUE;
}

/****************** End KAAB/K *************************************/

/********************** KABB/K ************************************/

static bool_t 	test_kabbk (void);
static bool_t 	kabbk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kabbk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kabbk (void)
{

	enum  {MAXPC = 16+1};
	char 		str[] = "kabbk";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = d;
			pw[4] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = NOSQUARE;
	
			if (kabbk_pctoindex (pw, pb, &i)) {
							kabbk_indextopc (i, px, py);		
							kabbk_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}

static void
kabbk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {
			BLOCK_B = 64,
			BLOCK_A = BLOCK_B * MAX_AAINDEX 
		}; 
	index_t a, b, c, r, x, y;

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;

	b  = r / BLOCK_B;
	r -= b * BLOCK_B;

	c  = r;
	
	assert (i == (a * BLOCK_A + b * BLOCK_B + c));

	pw[0] = wksq [a];
	pb[0] = bksq [a];

	x = aabase [b];
	y = (b + 1) + x - (x * (127-x)/2);

	pw[1] = (SQUARE) c;
	pw[2] = (SQUARE) x;
	pw[3] = (SQUARE) y;
	pw[4] = NOSQUARE;

	pb[1] = NOSQUARE;
	
	assert (kabbk_pctoindex (pw, pb, &a) && a == i);

	return;
}

static bool_t 
kabbk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, /*@out@*/ index_t *out)
{
	enum  {N_WHITE = 4, N_BLACK = 1};
	enum  {
			BLOCK_B = 64,
			BLOCK_A = BLOCK_B * MAX_AAINDEX 
		}; 
	SQUARE ws[MAX_LISTSIZE], bs[MAX_LISTSIZE];
	index_t ki, ai;
	unsigned int ft;
	int i;

	ft = flipt [inp_pb[0]] [inp_pw[0]];

	assert (ft < 8);

	for (i = 0; i < N_WHITE; i++) ws[i] = inp_pw[i]; ws[N_WHITE] = NOSQUARE;
	for (i = 0; i < N_BLACK; i++) bs[i] = inp_pb[i]; bs[N_BLACK] = NOSQUARE;	

	if ((ft & WE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipWE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipWE (bs[i]);
	}

	if ((ft & NS_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNS (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNS (bs[i]);
	}

	if ((ft & NW_SE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNW_SE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNW_SE (bs[i]);
	}

	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */
	ai = aaidx [ws[2]] [ws[3]];

	if (IDX_is_empty(ki) || IDX_is_empty(ai)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + ai * BLOCK_B + (index_t)ws[1]; 
	return TRUE;
}

/********************** End KABB/K *************************************/

/********************** init KAAA/K ************************************/

static index_t
aaa_getsubi (sq_t x, sq_t y, sq_t z);

static sq_t 		aaa_xyz [MAX_AAAINDEX] [3];
static index_t	 	aaa_base [64];

static index_t
init_aaa (void)
/* modifies aaa_base[], aaa_xyz[][] */
{
	index_t comb [64];
	index_t accum;
	index_t a;
	
	index_t idx;
	SQUARE x, y, z;

	/* getting aaa_base */	
	comb [0] = 0;	
	for (a = 1; a < 64; a++) {
		comb [a] = a * (a-1) / 2;	
	}
	
	accum = 0;
	aaa_base [0] = accum;		
	for (a = 0; a < (64-1); a++) {
		accum += comb[a];
		aaa_base [a+1] = accum;	
	}

	assert ((accum + comb[63]) == MAX_AAAINDEX);
	/* end getting aaa_base */


	/* initialize aaa_xyz [][] */
	for (idx = 0; idx < MAX_AAAINDEX; idx++) {
		IDX_set_empty (aaa_xyz[idx][0]);
		IDX_set_empty (aaa_xyz[idx][1]);				
		IDX_set_empty (aaa_xyz[idx][2]);
	}

	idx = 0;
	for (z = 0; z < 64; z++) {
		for (y = 0; y < z; y++) {
			for (x = 0; x < y; x++) {
			
				assert (idx == aaa_getsubi (x, y, z));
	
				aaa_xyz [idx] [0] = x;
				aaa_xyz [idx] [1] = y;				
				aaa_xyz [idx] [2] = z;
				
				idx++;
			}	
		}
	}
	
	assert (idx == MAX_AAAINDEX);

	return idx;
}


static index_t
aaa_getsubi (sq_t x, sq_t y, sq_t z)
/* uses aaa_base */
{
	index_t calc_idx, base;
	
	assert (x < 64 && y < 64 && z < 64);
	assert (x < y && y < z);

	base = aaa_base[z];
	calc_idx = (index_t)x + ((index_t)y - 1) * (index_t)y / 2 + base;

	return calc_idx;
}

/********************** KAAA/K ************************************/

static bool_t 	test_kaaak (void);
static bool_t 	kaaak_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kaaak_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kaaak (void)
{

	enum  {MAXPC = 16+1};
	char 		str[] = "kaaak";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = d;
			pw[4] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = NOSQUARE;
	
			if (kaaak_pctoindex (pw, pb, &i)) {
							kaaak_indextopc (i, px, py);		
							kaaak_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}

static void
kaaak_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {
			BLOCK_A = MAX_AAAINDEX 
		};
	index_t a, b, r;

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;

	b  = r;
	
	assert (i == (a * BLOCK_A + b));
	assert (b < BLOCK_A);

	pw[0] = wksq [a];
	pb[0] = bksq [a];

	pw[1] = aaa_xyz [b] [0];
	pw[2] = aaa_xyz [b] [1];
	pw[3] = aaa_xyz [b] [2];
	pw[4] = NOSQUARE;

	pb[1] = NOSQUARE;

	assert (kaaak_pctoindex (pw, pb, &a) && a == i);

	return;
}

static bool_t 
kaaak_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out)
{
	enum  {N_WHITE = 4, N_BLACK = 1};
	enum  {
			BLOCK_A = MAX_AAAINDEX 
		}; 
	SQUARE ws[MAX_LISTSIZE], bs[MAX_LISTSIZE];
	index_t ki, ai;
	unsigned int ft;
	int i;

	ft = flipt [inp_pb[0]] [inp_pw[0]];

	assert (ft < 8);

	for (i = 0; i < N_WHITE; i++) ws[i] = inp_pw[i]; ws[N_WHITE] = NOSQUARE;
	for (i = 0; i < N_BLACK; i++) bs[i] = inp_pb[i]; bs[N_BLACK] = NOSQUARE;	

	if ((ft & WE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipWE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipWE (bs[i]);
	}

	if ((ft & NS_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNS (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNS (bs[i]);
	}

	if ((ft & NW_SE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNW_SE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNW_SE (bs[i]);
	}


	{ 
		SQUARE tmp;
		if (ws[2] < ws[1]) {
            tmp = ws[1];
            ws[1] = ws[2];
            ws[2] = tmp;
		}
		if (ws[3] < ws[2]) {
            tmp = ws[2];
            ws[2] = ws[3];
            ws[3] = tmp;
		}
		if (ws[2] < ws[1]) {
            tmp = ws[1];
            ws[1] = ws[2];
            ws[2] = tmp;
		}
	}
	
	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */

/*128 == (128 & (((ws[1]^ws[2])-1) | ((ws[1]^ws[3])-1) | ((ws[2]^ws[3])-1)) */
	
	if (ws[1] == ws[2] || ws[1] == ws[3] || ws[2] == ws[3]) {
		*out = NOINDEX;
		return FALSE;
	}
	
	ai = aaa_getsubi ( ws[1], ws[2], ws[3] );	
	
	if (IDX_is_empty(ki) || IDX_is_empty(ai)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + ai; 
	return TRUE;
}

/****************** End KAAB/K *************************************/

/**********************  KAP/KB ************************************/

static bool_t 	test_kapkb (void);
static bool_t 	kapkb_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void	kapkb_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kapkb (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kapkb";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (c <= H1 || c >= A8)
				continue;
			
			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = d;
			pb[2] = NOSQUARE;
	
			if (kapkb_pctoindex (pw, pb, &i)) {
							kapkb_indextopc (i, px, py);		
							kapkb_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}

static void
kapkb_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d * BLOCK_D + e; 
	*----------------------------------------------------------*/
	enum  {B11100  = 7u << 2};
	enum  {BLOCK_A = 64*64*64*64, BLOCK_B = 64*64*64, BLOCK_C = 64*64, BLOCK_D = 64}; 
	index_t a, b, c, d, e, r;
	index_t x;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r / BLOCK_D;
	r -= d * BLOCK_D;
	e  = r;
	
	/* x is pslice */
	x = a;
	x += x & B11100; /* get upper part and double it */
	x += 8;          /* add extra row  */
	x ^= 070;        /* flip NS */

	pw[0] = (SQUARE) b;
	pb[0] = (SQUARE) c;	
	pw[1] = (SQUARE) d;
	pw[2] = (SQUARE) x;
	pw[3] = NOSQUARE;
	pb[1] = (SQUARE) e;
	pb[2] = NOSQUARE;
	
	assert (kapkb_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kapkb_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64*64*64, BLOCK_B = 64*64*64, BLOCK_C = 64*64, BLOCK_D = 64}; 	
	index_t pslice;
	SQUARE sq;
	SQUARE pawn = pw[2];
	SQUARE wa   = pw[1];
	SQUARE wk   = pw[0];
	SQUARE bk   = pb[0];
	SQUARE ba   = pb[1];
	
	assert (A2 <= pawn && pawn < A8);

	if (  !(A2 <= pawn && pawn < A8)) {
		*out = NOINDEX;
		return FALSE;
	}	
	
	if ((pawn & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		pawn = flipWE (pawn);
		wk   = flipWE (wk);		
		bk   = flipWE (bk);		
		wa   = flipWE (wa);
		ba   = flipWE (ba);		
	}

	sq = pawn;
	sq ^= 070; /* flipNS*/
	sq -= 8;   /* down one row*/
	pslice = (index_t) ((sq+(sq&3)) >> 1); 

	*out = pslice * BLOCK_A + (index_t)wk * BLOCK_B  + (index_t)bk * BLOCK_C + (index_t)wa * BLOCK_D + (index_t)ba;

	return TRUE;
}
/********************** end KAP/KB ************************************/

/*************************  KAB/KP ************************************/

static bool_t 	test_kabkp (void);
static bool_t 	kabkp_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void	kabkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kabkp (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kabkp";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (d <= H1 || d >= A8)
				continue;
			
			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = d;
			pb[2] = NOSQUARE;
	
			if (kabkp_pctoindex (pw, pb, &i)) {
							kabkp_indextopc (i, px, py);		
							kabkp_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}

static void
kabkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d * BLOCK_D + e; 
	*----------------------------------------------------------*/
	enum  {B11100  = 7u << 2};
	enum  {BLOCK_A = 64*64*64*64, BLOCK_B = 64*64*64, BLOCK_C = 64*64, BLOCK_D = 64}; 
	index_t a, b, c, d, e, r;
	index_t x;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r / BLOCK_D;
	r -= d * BLOCK_D;
	e  = r;
	
	/* x is pslice */
	x = a;
	x += x & B11100; /* get upper part and double it */
	x += 8;          /* add extra row  */
	/*x ^= 070;*/        /* do not flip NS */

	pw[0] = (SQUARE) b;
	pb[0] = (SQUARE) c;	
	pw[1] = (SQUARE) d;
	pw[2] = (SQUARE) e;
	pw[3] = NOSQUARE;
	pb[1] = (SQUARE) x;
	pb[2] = NOSQUARE;
	
	assert (kabkp_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kabkp_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64*64*64, BLOCK_B = 64*64*64, BLOCK_C = 64*64, BLOCK_D = 64}; 	
	index_t pslice;
	SQUARE sq;
	SQUARE pawn = pb[1];
	SQUARE wa   = pw[1];
	SQUARE wk   = pw[0];
	SQUARE bk   = pb[0];
	SQUARE wb   = pw[2];
	
	assert (A2 <= pawn && pawn < A8);

	if (  !(A2 <= pawn && pawn < A8)) {
		*out = NOINDEX;
		return FALSE;
	}	
	
	if ((pawn & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		pawn = flipWE (pawn);
		wk   = flipWE (wk);		
		bk   = flipWE (bk);		
		wa   = flipWE (wa);
		wb   = flipWE (wb);		
	}

	sq = pawn;
	/*sq ^= 070;*/ /* do not flipNS*/
	sq -= 8;   /* down one row*/
	pslice = (index_t) ((sq+(sq&3)) >> 1); 

	*out = pslice * BLOCK_A + (index_t)wk * BLOCK_B  + (index_t)bk * BLOCK_C + (index_t)wa * BLOCK_D + (index_t)wb;

	return TRUE;
}
/********************** end KAB/KP ************************************/


static void
kpk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c; 
	*----------------------------------------------------------*/
	enum  {B11100  = 7u << 2};
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 
	index_t a, b, c, r;
	index_t x;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r;
	
	/* x is pslice */
	x = a;
	x += x & B11100; /* get upper part and double it */
	x += 8;          /* add extra row  */
	x ^= 070;        /* flip NS */
	
	pw[1] = (SQUARE) x;
	pw[0] = (SQUARE) b;
	pb[0] = (SQUARE) c;

	pw[2] = NOSQUARE;
	pb[1] = NOSQUARE;
	
	assert (kpk_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kpk_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 	
	index_t pslice;
	SQUARE sq;
	SQUARE pawn = pw[1];
	SQUARE wk   = pw[0];
	SQUARE bk   = pb[0];

	#ifdef DEBUG
	if (  !(A2 <= pawn && pawn < A8)) {
		SQ_CONTENT wp[MAX_LISTSIZE], bp[MAX_LISTSIZE];
        bp [0] = wp[0] = KING;
        wp[1] = PAWN;
        wp[2] = bp[1] = NOPIECE;
		output_state (0, pw, pb, wp, bp);
	}	
	#endif

	assert (A2 <= pawn && pawn < A8);

	if (  !(A2 <= pawn && pawn < A8)) {
		*out = NOINDEX;
		return FALSE;
	}	
	
	if ((pawn & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		pawn = flipWE (pawn);
		wk   = flipWE (wk);		
		bk   = flipWE (bk);		
	}

	sq = pawn;
	sq ^= 070; /* flipNS*/
	sq -= 8;   /* down one row*/
	pslice = (index_t) ((sq+(sq&3)) >> 1); 

	*out = pslice * BLOCK_A + (index_t)wk * BLOCK_B  + (index_t)bk;

	return TRUE;
}


/**********************  KPP/K ************************************/

static bool_t 	test_kppk (void);
static bool_t 	kppk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kppk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kppk (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kppk";
	SQUARE 		a, b, c, d;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
			sq_t anchor1, anchor2, loosen1, loosen2;
			if (c <= H1 || c >= A8)
				continue;
			if (b <= H1 || b >= A8)
				continue;

			pp_putanchorfirst (b, c, &anchor1, &loosen1);
			pp_putanchorfirst (c, b, &anchor2, &loosen2);
			if (!(anchor1 == anchor2 && loosen1 == loosen2)) {
				printf ("Output depends on input in pp_outanchorfirst()\n input:%u, %u\n",(unsigned)b,(unsigned)c);
				fatal_error();
			} 
		}
	}


	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {


			if (c <= H1 || c >= A8)
				continue;
			if (b <= H1 || b >= A8)
				continue;

			
			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = NOSQUARE;
		
			pb[0] = d;
			pb[1] = NOSQUARE;
	
			if (kppk_pctoindex (pw, pb, &i)) {
							kppk_indextopc (i, px, py);		
							kppk_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}

static void
kppk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c; 
	*----------------------------------------------------------*/
	enum  {B11100  = 7u << 2};
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 
	index_t a, b, c, r;
	index_t m, n;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r;

	m = pp_hi24 [a];
	n = pp_lo48 [a];
	
	pw[0] = (SQUARE) b;
	pb[0] = (SQUARE) c;	
	pb[1] = NOSQUARE;	
	
	pw[1] = pidx24_to_wsq (m);
	pw[2] = pidx48_to_wsq (n);

	pw[3] = NOSQUARE;


	assert (A2 <= pw[1] && pw[1] < A8);
	assert (A2 <= pw[2] && pw[2] < A8);

#ifdef DEBUG
	if (!(kppk_pctoindex (pw, pb, &a) && a == i)) {
		pc_t wp[] = {KING, PAWN, PAWN, NOPIECE};
		pc_t bp[] = {KING, NOPIECE};		
		printf("Indexes not matching: input:%d, output:%d\n", i, a);
		print_pos (pw, pb, wp, bp);
	}
#endif

	assert (kppk_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kppk_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 	
	index_t pp_slice;
	SQUARE anchor, loosen;
	
	SQUARE wk     = pw[0];
	SQUARE pawn_a = pw[1];
	SQUARE pawn_b = pw[2];
	SQUARE bk     = pb[0];
	index_t i, j;

	#ifdef DEBUG
	if (!(A2 <= pawn_a && pawn_a < A8)) {
		printf ("\n\nsquare of pawn_a: %s\n", Square_str[pawn_a]);
		printf(" wk %s\n p1 %s\n p2 %s\n bk %s\n"
			, Square_str[wk]
			, Square_str[pawn_a]
			, Square_str[pawn_b]
			, Square_str[bk]
			);
	}
	#endif

	assert (A2 <= pawn_a && pawn_a < A8);
	assert (A2 <= pawn_b && pawn_b < A8);

	pp_putanchorfirst (pawn_a, pawn_b, &anchor, &loosen);

	if ((anchor & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		anchor = flipWE (anchor);
		loosen = flipWE (loosen);
		wk     = flipWE (wk);		
		bk     = flipWE (bk);		
	}
 
	i = wsq_to_pidx24 (anchor);
	j = wsq_to_pidx48 (loosen);

	pp_slice = ppidx [i] [j];

	if (IDX_is_empty(pp_slice)) {
		*out = NOINDEX;
		return FALSE;
	}

	assert (pp_slice < MAX_PPINDEX );
	
	*out = pp_slice * BLOCK_A + (index_t)wk * BLOCK_B  + (index_t)bk;

	return TRUE;
}
/****************** end  KPP/K ************************************/

static void
kakp_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d; 
	*----------------------------------------------------------*/
	enum  {B11100  = 7u << 2};
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t a, b, c, d, r;
	index_t x;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r;
	
	/* x is pslice */
	x = a;
	x += x & B11100; /* get upper part and double it */
	x += 8;          /* add extra row  */
/*	x ^= 070;   */     /* flip NS */

	pw[0] = (SQUARE) b;
	pb[0] = (SQUARE) c;	
	pw[1] = (SQUARE) d;
	pb[1] = (SQUARE) x;
	pw[2] = NOSQUARE;
	pb[2] = NOSQUARE;
	
	assert (kakp_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kakp_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 	
	index_t pslice;
	SQUARE sq;
	SQUARE pawn = pb[1];
	SQUARE wa   = pw[1];
	SQUARE wk   = pw[0];
	SQUARE bk   = pb[0];

	assert (A2 <= pawn && pawn < A8);

	if (  !(A2 <= pawn && pawn < A8)) {
		*out = NOINDEX;
		return FALSE;
	}	
	
	if ((pawn & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		pawn = flipWE (pawn);
		wk   = flipWE (wk);		
		bk   = flipWE (bk);		
		wa   = flipWE (wa);
	}

	sq = pawn;
	/*sq ^= 070;*/ /* flipNS*/
	sq -= 8;   /* down one row*/
	pslice = (index_t) ((sq+(sq&3)) >> 1); 

	*out = pslice * BLOCK_A + (index_t)wk * BLOCK_B  + (index_t)bk * BLOCK_C + (index_t)wa;

	return TRUE;
}


static void
kapk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d; 
	*----------------------------------------------------------*/
	enum  {B11100  = 7u << 2};
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t a, b, c, d, r;
	index_t x;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r;
	
	/* x is pslice */
	x = a;
	x += x & B11100; /* get upper part and double it */
	x += 8;          /* add extra row  */
	x ^= 070;        /* flip NS */

	pw[0] = (SQUARE) b;
	pb[0] = (SQUARE) c;	
	pw[1] = (SQUARE) d;
	pw[2] = (SQUARE) x;
	pw[3] = NOSQUARE;
	pb[1] = NOSQUARE;
	
	assert (kapk_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kapk_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 	
	index_t pslice;
	SQUARE sq;
	SQUARE pawn = pw[2];
	SQUARE wa   = pw[1];
	SQUARE wk   = pw[0];
	SQUARE bk   = pb[0];

	assert (A2 <= pawn && pawn < A8);

	if (  !(A2 <= pawn && pawn < A8)) {
		*out = NOINDEX;
		return FALSE;
	}	
	
	if ((pawn & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		pawn = flipWE (pawn);
		wk   = flipWE (wk);		
		bk   = flipWE (bk);		
		wa   = flipWE (wa);
	}

	sq = pawn;
	sq ^= 070; /* flipNS*/
	sq -= 8;   /* down one row*/
	pslice = (index_t) ((sq+(sq&3)) >> 1); 

	*out = pslice * BLOCK_A + (index_t)wk * BLOCK_B  + (index_t)bk * BLOCK_C + (index_t)wa;

	return TRUE;
}


static void
kaak_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	enum  {BLOCK_A = MAX_AAINDEX}; 
	index_t a, b, r, x, y;

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r;
	
	assert (i == (a * BLOCK_A + b));

	pw[0] = wksq [a];
	pb[0] = bksq [a];

	x = aabase [b];
	y = (b + 1) + x - (x * (127-x)/2);

	pw[1] = (SQUARE) x;
	pw[2] = (SQUARE) y;
	pw[3] = NOSQUARE;

	pb[1] = NOSQUARE;
	
	assert (kaak_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kaak_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out)
{
	enum  {N_WHITE = 3, N_BLACK = 1};
	enum  {BLOCK_A = MAX_AAINDEX}; 
	SQUARE ws[MAX_LISTSIZE], bs[MAX_LISTSIZE];
	index_t ki, ai;
	unsigned int ft;
	SQUARE i;

	ft = flipt [inp_pb[0]] [inp_pw[0]];

	assert (ft < 8);

    for (i = 0; i < N_WHITE; i++) ws[i] = inp_pw[i];
    ws[N_WHITE] = NOSQUARE;
    for (i = 0; i < N_BLACK; i++) bs[i] = inp_pb[i];
    bs[N_BLACK] = NOSQUARE;

	if ((ft & WE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipWE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipWE (bs[i]);
	}

	if ((ft & NS_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNS (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNS (bs[i]);
	}

	if ((ft & NW_SE_FLAG) != 0) {
		for (i = 0; i < N_WHITE; i++) ws[i] = flipNW_SE (ws[i]);
		for (i = 0; i < N_BLACK; i++) bs[i] = flipNW_SE (bs[i]);
	}

	ki = kkidx [bs[0]] [ws[0]]; /* kkidx [black king] [white king] */
	ai = (index_t) aaidx [ws[1]] [ws[2]];

	if (IDX_is_empty(ki) || IDX_is_empty(ai)) {
		*out = NOINDEX;
		return FALSE;
	}	
	*out = ki * BLOCK_A + ai; 
	return TRUE;
}

/**********************  KPP/KA ************************************/

static bool_t 	test_kppka (void);
static bool_t 	kppka_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kppka_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kppka (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kppka";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (c <= H1 || c >= A8)
				continue;
			if (b <= H1 || b >= A8)
				continue;

			
			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = d;
			pb[2] = NOSQUARE;
	
			if (kppka_pctoindex (pw, pb, &i)) {
							kppka_indextopc (i, px, py);		
							kppka_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}


static void
kppka_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d; 
	*----------------------------------------------------------*/

	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t a, b, c, d, r;
	index_t m, n;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r;
	
	m = pp_hi24 [a];
	n = pp_lo48 [a];
	
	pw[0] = (SQUARE) b;
	pw[1] = pidx24_to_wsq (m);
	pw[2] = pidx48_to_wsq (n);
	pw[3] = NOSQUARE;

	pb[0] = (SQUARE) c;	
	pb[1] = (SQUARE) d;
	pb[2] = NOSQUARE;	


	assert (A2 <= pw[1] && pw[1] < A8);
	assert (A2 <= pw[2] && pw[2] < A8);
	assert (kppka_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kppka_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t pp_slice;
	index_t i, j;

	SQUARE anchor, loosen;
	
	SQUARE wk     = pw[0];
	SQUARE pawn_a = pw[1];
	SQUARE pawn_b = pw[2];
	SQUARE bk     = pb[0];
	SQUARE ba	  = pb[1];	


	assert (A2 <= pawn_a && pawn_a < A8);
	assert (A2 <= pawn_b && pawn_b < A8);

	pp_putanchorfirst (pawn_a, pawn_b, &anchor, &loosen);

	if ((anchor & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		anchor = flipWE (anchor);
		loosen = flipWE (loosen);
		wk     = flipWE (wk);		
		bk     = flipWE (bk);		
		ba	   = flipWE (ba);	
	}
 
	i = wsq_to_pidx24 (anchor);
	j = wsq_to_pidx48 (loosen);

	pp_slice = ppidx [i] [j];

	if (IDX_is_empty(pp_slice)) {
		*out = NOINDEX;
		return FALSE;
	}

	assert (pp_slice < MAX_PPINDEX );
	
	*out = pp_slice * (index_t)BLOCK_A + (index_t)wk * (index_t)BLOCK_B  + (index_t)bk * (index_t)BLOCK_C + (index_t)ba;

	return TRUE;
}

/********************** end KPP/KA ************************************/

/**********************  KAPP/K ************************************/

static bool_t 	test_kappk (void);
static bool_t 	kappk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kappk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kappk (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kappk";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (c <= H1 || c >= A8)
				continue;
			if (b <= H1 || b >= A8)
				continue;

			
			pw[0] = a;
			pw[1] = d;	
			pw[2] = b;
			pw[3] = c;
			pw[4] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = NOSQUARE;
	
			if (kappk_pctoindex (pw, pb, &i)) {
							kappk_indextopc (i, px, py);		
							kappk_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}


static void
kappk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d; 
	*----------------------------------------------------------*/

	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t a, b, c, d, r;
	index_t m, n;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r;
	
	m = pp_hi24 [a];
	n = pp_lo48 [a];
	
	pw[0] = (SQUARE) b;
	pw[1] = (SQUARE) d;
	pw[2] = pidx24_to_wsq (m);
	pw[3] = pidx48_to_wsq (n);
	pw[4] = NOSQUARE;

	pb[0] = (SQUARE) c;	
	pb[1] = NOSQUARE;	


	assert (A2 <= pw[3] && pw[3] < A8);
	assert (A2 <= pw[2] && pw[2] < A8);
	assert (kappk_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kappk_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t pp_slice;
	SQUARE anchor, loosen;
	
	SQUARE wk     = pw[0];
	SQUARE wa	  = pw[1];	
	SQUARE pawn_a = pw[2];
	SQUARE pawn_b = pw[3];
	SQUARE bk     = pb[0];

	index_t i, j;

	assert (A2 <= pawn_a && pawn_a < A8);
	assert (A2 <= pawn_b && pawn_b < A8);

	pp_putanchorfirst (pawn_a, pawn_b, &anchor, &loosen);

	if ((anchor & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		anchor = flipWE (anchor);
		loosen = flipWE (loosen);
		wk     = flipWE (wk);		
		bk     = flipWE (bk);		
		wa	   = flipWE (wa);	
	}
 
	i = wsq_to_pidx24 (anchor);
	j = wsq_to_pidx48 (loosen);

	pp_slice = ppidx [i] [j];

	if (IDX_is_empty(pp_slice)) {
		*out = NOINDEX;
		return FALSE;
	}

	assert (pp_slice < MAX_PPINDEX );
	
	*out = pp_slice * (index_t)BLOCK_A + (index_t)wk * (index_t)BLOCK_B  + (index_t)bk * (index_t)BLOCK_C + (index_t)wa;

	return TRUE;
}

/********************** end KAPP/K ************************************/

/**********************  KAPP/K ************************************/

static bool_t 	test_kapkp (void);
static bool_t 	kapkp_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kapkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kapkp (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kapkp";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (c <= H1 || c >= A8)
				continue;
			if (b <= H1 || b >= A8)
				continue;

			
			pw[0] = a;
			pw[1] = d;	
			pw[2] = b;
			pw[3] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = c;
			pb[2] = NOSQUARE;
	
			if (kapkp_pctoindex (pw, pb, &i)) {
							kapkp_indextopc (i, px, py);		
							kapkp_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}


static bool_t 
kapkp_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 	
	index_t pp_slice;
	SQUARE anchor, loosen;
	
	SQUARE wk     = pw[0];
	SQUARE wa	  = pw[1];
	SQUARE pawn_a = pw[2];
	SQUARE bk     = pb[0];
	SQUARE pawn_b = pb[1];
	index_t m, n;

	assert (A2 <= pawn_a && pawn_a < A8);
	assert (A2 <= pawn_b && pawn_b < A8);
	assert (pw[3] == NOSQUARE && pb[2] == NOSQUARE);

	anchor = pawn_a;
	loosen = pawn_b;

	if ((anchor & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		anchor = flipWE (anchor);
		loosen = flipWE (loosen);
		wk     = flipWE (wk);		
		bk     = flipWE (bk);		
		wa	   = flipWE (wa);		
	}
 
	m = wsq_to_pidx24 (anchor);
	n = (index_t)loosen - 8;

	pp_slice = m * 48 + n; 

	if (IDX_is_empty(pp_slice)) {
		*out = NOINDEX;
		return FALSE;
	}

	assert (pp_slice < (64*MAX_PpINDEX) );
	
	*out = pp_slice * (index_t)BLOCK_A + (index_t)wk * (index_t)BLOCK_B  + (index_t)bk * (index_t)BLOCK_C + (index_t)wa;

	return TRUE;
}

static void
kapkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d; 
	*----------------------------------------------------------*/
	enum  {BLOCK_A = 64*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	enum  {block_m = 48};
	index_t a, b, c, d, r;
	index_t m, n;
	SQUARE sq_m, sq_n;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r;
	
	/* unpack a, which is pslice, into m and n */
	r = a;
	m  = r / block_m;
	r -= m * block_m;
	n  = r ;

	sq_m = pidx24_to_wsq (m);
	sq_n = (SQUARE) n + 8;
	
	pw[0] = (SQUARE) b;
	pb[0] = (SQUARE) c;	
	pw[1] = (SQUARE) d;
	pw[2] = sq_m;
	pb[1] = sq_n;
	pw[3] = NOSQUARE;
	pb[2] = NOSQUARE;	
	
	assert (A2 <= sq_m && sq_m < A8);
	assert (A2 <= sq_n && sq_n < A8);
	assert (kapkp_pctoindex (pw, pb, &a) && a == i);

	return;
}

/********************** end KAP/KP ************************************/

/**********************  KABP/K ************************************/

static bool_t 	test_kabpk (void);
static bool_t 	kabpk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kabpk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kabpk (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kabpk";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (d <= H1 || d >= A8)
				continue;
		
			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = d;
			pw[4] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = NOSQUARE;
	
			if (kabpk_pctoindex (pw, pb, &i)) {
							kabpk_indextopc (i, px, py);		
							kabpk_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}


static void
kabpk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d * BLOCK_D + e; 
	*----------------------------------------------------------*/
	enum  {BLOCK_A = 64*64*64*64, BLOCK_B = 64*64*64, BLOCK_C = 64*64, BLOCK_D = 64}; 
	index_t a, b, c, d, e, r;
	SQUARE x;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r / BLOCK_D;
	r -= d * BLOCK_D;
	e  = r;
	
	x = pidx24_to_wsq(a);

	pw[0] = (SQUARE) b;
	pw[1] = (SQUARE) d;
	pw[2] = (SQUARE) e;
	pw[3] = x;
	pw[4] = NOSQUARE;

	pb[0] = (SQUARE) c;	
	pb[1] = NOSQUARE;
	
	assert (kabpk_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kabpk_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64*64*64, BLOCK_B = 64*64*64, BLOCK_C = 64*64, BLOCK_D = 64}; 	
	index_t pslice;

	SQUARE wk   = pw[0];
	SQUARE wa   = pw[1];
	SQUARE wb   = pw[2];
	SQUARE pawn = pw[3];
	SQUARE bk   = pb[0];

	assert (A2 <= pawn && pawn < A8);
	
	if ((pawn & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		pawn = flipWE (pawn);
		wk   = flipWE (wk);		
		bk   = flipWE (bk);		
		wa   = flipWE (wa);
		wb   = flipWE (wb);		
	}

	pslice = wsq_to_pidx24 (pawn);

	*out = pslice * (index_t)BLOCK_A + (index_t)wk * (index_t)BLOCK_B  + (index_t)bk * (index_t)BLOCK_C + (index_t)wa * (index_t)BLOCK_D + (index_t)wb;

	return TRUE;
}

/********************** end KABP/K ************************************/

/**********************  KAAP/K ************************************/

static bool_t 	test_kaapk (void);
static bool_t 	kaapk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kaapk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kaapk (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kaapk";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (d <= H1 || d >= A8)
				continue;
		
			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = d;
			pw[4] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = NOSQUARE;
	
			if (kaapk_pctoindex (pw, pb, &i)) {
							kaapk_indextopc (i, px, py);		
							kaapk_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}


static void
kaapk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d; 
	*----------------------------------------------------------*/
	enum 	{BLOCK_C = MAX_AAINDEX
			,BLOCK_B = 64*BLOCK_C
			,BLOCK_A = 64*BLOCK_B
	}; 	
	index_t a, b, c, d, r;
	index_t x, y, z;
	
	assert (i >= 0);

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r;

	z = (index_t) pidx24_to_wsq(a);
	
	/* split d into x, y*/
	x = aabase [d];
	y = (d + 1) + x - (x * (127-x)/2);

	assert (aaidx[x][y] == aaidx[y][x]);
	assert (aaidx[x][y] == d);


	pw[0] = (SQUARE) b;
	pw[1] = (SQUARE) x;
	pw[2] = (SQUARE) y;
	pw[3] = (SQUARE) z;
	pw[4] = NOSQUARE;
	
	pb[0] = (SQUARE) c;	
	pb[1] = NOSQUARE;
	
	assert (kaapk_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kaapk_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum 	{BLOCK_C = MAX_AAINDEX
			,BLOCK_B = 64*BLOCK_C
			,BLOCK_A = 64*BLOCK_B
	}; 	
	index_t aa_combo, pslice;

	SQUARE wk   = pw[0];
	SQUARE wa   = pw[1];
	SQUARE wa2  = pw[2];
	SQUARE pawn = pw[3];
	SQUARE bk   = pb[0];

	assert (A2 <= pawn && pawn < A8);

	if ((pawn & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		pawn = flipWE (pawn);
		wk   = flipWE (wk);		
		bk   = flipWE (bk);		
		wa   = flipWE (wa);
		wa2  = flipWE (wa2);
	}

	pslice = wsq_to_pidx24 (pawn);

	aa_combo = (index_t) aaidx [wa] [wa2];

	if (IDX_is_empty(aa_combo)) {
		*out = NOINDEX;
		return FALSE;
	}	

	*out = pslice * (index_t)BLOCK_A + (index_t)wk * (index_t)BLOCK_B  + (index_t)bk * (index_t)BLOCK_C + aa_combo;

	assert (*out >= 0);

	return TRUE;
}

/********************** end KAAP/K ************************************/

/**********************  KAA/KP ************************************/

static bool_t 	test_kaakp (void);
static bool_t 	kaakp_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kaakp_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static bool_t
test_kaakp (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kaakp";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (d <= H1 || d >= A8)
				continue;
		
			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = d;
			pb[2] = NOSQUARE;
	
			if (kaakp_pctoindex (pw, pb, &i)) {
							kaakp_indextopc (i, px, py);		
							kaakp_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}


static void
kaakp_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d; 
	*----------------------------------------------------------*/
	enum 	{BLOCK_C = MAX_AAINDEX
			,BLOCK_B = 64*BLOCK_C
			,BLOCK_A = 64*BLOCK_B
	}; 	
	index_t a, b, c, d, r;
	index_t x, y, z;
	SQUARE zq;	

	assert (i >= 0);

	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r;

	zq = pidx24_to_wsq(a); 
	z  = (index_t)flipNS(zq);

	
	/* split d into x, y*/
	x = aabase [d];
	y = (d + 1) + x - (x * (127-x)/2);

	assert (aaidx[x][y] == aaidx[y][x]);
	assert (aaidx[x][y] == d);


	pw[0] = (SQUARE)b;
	pw[1] = (SQUARE)x;
	pw[2] = (SQUARE)y;
	pw[3] = NOSQUARE;
	
	pb[0] = (SQUARE)c;	
	pb[1] = (SQUARE)z;
	pb[2] = NOSQUARE;
	
	assert (kaakp_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kaakp_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum 	{BLOCK_C = MAX_AAINDEX
			,BLOCK_B = 64*BLOCK_C
			,BLOCK_A = 64*BLOCK_B
	}; 	
	index_t aa_combo, pslice;

	SQUARE wk   = pw[0];
	SQUARE wa   = pw[1];
	SQUARE wa2  = pw[2];
	SQUARE bk   = pb[0];
	SQUARE pawn = pb[1];

	assert (A2 <= pawn && pawn < A8);

	if ((pawn & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		pawn = flipWE (pawn);
		wk   = flipWE (wk);		
		bk   = flipWE (bk);		
		wa   = flipWE (wa);
		wa2  = flipWE (wa2);
	}

	pawn = flipNS(pawn);
	pslice = wsq_to_pidx24 (pawn);

	aa_combo = (index_t)aaidx [wa] [wa2];

	if (IDX_is_empty(aa_combo)) {
		*out = NOINDEX;
		return FALSE;
	}	

	*out = pslice * (index_t)BLOCK_A + (index_t)wk * (index_t)BLOCK_B  + (index_t)bk * (index_t)BLOCK_C + aa_combo;

	assert (*out >= 0);

	return TRUE;
}

/********************** end KAA/KP ************************************/

/**********************  KPP/KP ************************************/
/*
index_t 	pp48_idx[48][48];
sq_t		pp48_sq_x[MAX_PP48_INDEX];
sq_t		pp48_sq_y[MAX_PP48_INDEX]; 
*/
static bool_t 	test_kppkp (void);
static bool_t 	kppkp_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kppkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static sq_t map24_b   (sq_t s);
static sq_t unmap24_b (index_t i);

static index_t
init_pp48_idx (void)
/* modifies pp48_idx[][], pp48_sq_x[], pp48_sq_y[] */
{
	enum  {MAX_I = 48, MAX_J = 48};
	SQUARE i, j;
	index_t idx = 0;
	SQUARE a, b;

	/* default is noindex */
	for (i = 0; i < MAX_I; i++) {
		for (j = 0; j < MAX_J; j++) {
			IDX_set_empty (pp48_idx [i][j]);
		}
	}
		
	for (idx = 0; idx < MAX_PP48_INDEX; idx++) {
		pp48_sq_x [idx] = NOSQUARE;	
		pp48_sq_y [idx] = NOSQUARE;			
	}		
		
	idx = 0;
	for (a = H7; a >= A2; a--) {

		for (b = a - 1; b >= A2; b--) {

			i = flipWE( flipNS (a) ) - 8;
			j = flipWE( flipNS (b) ) - 8;
			
			if (IDX_is_empty(pp48_idx [i] [j])) {

				pp48_idx  [i][j]= idx; 	assert (idx < MAX_PP48_INDEX);
				pp48_idx  [j][i]= idx;
				pp48_sq_x [idx] = i; 	assert (i < MAX_I);
				pp48_sq_y [idx] = j; 	assert (j < MAX_J);		
				idx++;
			}
		}	
	}
	assert (idx == MAX_PP48_INDEX);
	return idx;
}



static bool_t
test_kppkp (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kppkp";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (c <= H1 || c >= A8)
				continue;
			if (b <= H1 || b >= A8)
				continue;
			if (d <= H1 || d >= A8)
				continue;
			
			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = d;
			pb[2] = NOSQUARE;
	
			if (kppkp_pctoindex (pw, pb, &i)) {
							kppkp_indextopc (i, px, py);		
							kppkp_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}


static void
kppkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d; 
	*----------------------------------------------------------*/

	enum  {BLOCK_A = MAX_PP48_INDEX*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t a, b, c, d, r;
	SQUARE m, n;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r / BLOCK_C;
	r -= c * BLOCK_C;	
	d  = r;
	
	m = pp48_sq_x [b];
	n = pp48_sq_y [b];
	
	pw[0] = (SQUARE)c;
	pw[1] = flipWE(flipNS(m+8));
	pw[2] = flipWE(flipNS(n+8));
	pw[3] = NOSQUARE;

	pb[0] = (SQUARE)d;	
	pb[1] = (SQUARE)unmap24_b (a);
	pb[2] = NOSQUARE;	


	assert (A2 <= pw[1] && pw[1] < A8);
	assert (A2 <= pw[2] && pw[2] < A8);
	assert (A2 <= pb[1] && pb[1] < A8);
	assert (kppkp_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kppkp_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = MAX_PP48_INDEX*64*64, BLOCK_B = 64*64, BLOCK_C = 64}; 
	index_t pp48_slice;	
	
	SQUARE wk     = pw[0];
	SQUARE pawn_a = pw[1];
	SQUARE pawn_b = pw[2];
	SQUARE bk     = pb[0];
	SQUARE pawn_c = pb[1];	
	SQUARE i, j, k;

	assert (A2 <= pawn_a && pawn_a < A8);
	assert (A2 <= pawn_b && pawn_b < A8);
	assert (A2 <= pawn_c && pawn_c < A8);

	if ((pawn_c & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		wk     = flipWE (wk);		
		pawn_a = flipWE (pawn_a);
		pawn_b = flipWE (pawn_b);
		bk     = flipWE (bk);		
		pawn_c = flipWE (pawn_c);	
	}
 
	i = flipWE( flipNS (pawn_a) ) - 8;
	j = flipWE( flipNS (pawn_b) ) - 8;
	k = map24_b (pawn_c); /* black pawn, so low indexes mean more advanced 0 == A2 */

	pp48_slice = pp48_idx [i] [j];

	if (IDX_is_empty(pp48_slice)) {
		*out = NOINDEX;
		return FALSE;
	}

	assert (pp48_slice < MAX_PP48_INDEX );
	
	*out = (index_t)k * (index_t)BLOCK_A + pp48_slice * (index_t)BLOCK_B + (index_t)wk * (index_t)BLOCK_C  + (index_t)bk;

	return TRUE;
}

static sq_t
map24_b (sq_t s)
{
	s -= 8;
	return ((s&3)+s)>>1;
}

static sq_t
unmap24_b (index_t i)
{
	return (sq_t) ((i&(4+8+16)) + i + 8);
}

/********************** end KPP/KP ************************************/

/**********************  KPPP/K ************************************/

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

static bool_t 	test_kpppk (void);
static bool_t 	kpppk_pctoindex (const SQUARE *inp_pw, const SQUARE *inp_pb, index_t *out);
static void		kpppk_indextopc (index_t i, SQUARE *pw, SQUARE *pb);

static index_t
init_ppp48_idx (void)
/* modifies ppp48_idx[][], ppp48_sq_x[], ppp48_sq_y[], ppp48_sq_z[] */
{
	enum  {MAX_I = 48, MAX_J = 48, MAX_K = 48};
	SQUARE i, j, k;
	index_t idx = 0;
	SQUARE a, b, c;
	int x, y, z;

	/* default is noindex */
	for (i = 0; i < MAX_I; i++) {
		for (j = 0; j < MAX_J; j++) {
			for (k = 0; k < MAX_K; k++) {
				IDX_set_empty(ppp48_idx [i][j][k]);
			}
		}
	}
		
	for (idx = 0; idx < MAX_PPP48_INDEX; idx++) {
		ppp48_sq_x [idx] = (uint8_t)NOSQUARE;	
		ppp48_sq_y [idx] = (uint8_t)NOSQUARE;	
		ppp48_sq_z [idx] = (uint8_t)NOSQUARE;		
	}		

	idx = 0;
	for (x = 0; x < 48; x++) {
		for (y = x+1; y < 48; y++) {
			for (z = y+1; z < 48; z++) {

				a = itosq [x];
				b = itosq [y];
				c = itosq [z];
		
				if (!in_queenside(b) || !in_queenside(c))			
						continue;

				i = a - 8;
				j = b - 8;
				k = c - 8;
				
				if (IDX_is_empty(ppp48_idx [i] [j] [k])) {

					ppp48_idx  [i][j][k] = idx; 	
					ppp48_idx  [i][k][j] = idx;
					ppp48_idx  [j][i][k] = idx;
					ppp48_idx  [j][k][i] = idx;
					ppp48_idx  [k][i][j] = idx;
					ppp48_idx  [k][j][i] = idx;
					ppp48_sq_x [idx] = (uint8_t) i; 	assert (i < MAX_I);
					ppp48_sq_y [idx] = (uint8_t) j; 	assert (j < MAX_J);		
					ppp48_sq_z [idx] = (uint8_t) k; 	assert (k < MAX_K);	
					idx++;
				}
			}
		}	
	}

/*	assert (idx == MAX_PPP48_INDEX);*/
	return idx;
}

static bool_t
test_kpppk (void)
{

	enum 		{MAXPC = 16+1};
	char 		str[] = "kpppk";
	SQUARE 		a, b, c, d, e;
	SQUARE 		pw[MAXPC], pb[MAXPC];
	SQUARE 		px[MAXPC], py[MAXPC];	

	index_t		i, j;
	bool_t 		err = FALSE;

	printf ("%8s ", str);

	for (a = 0; a < 64; a++) {
		for (b = 0; b < 64; b++) {
		for (c = 0; c < 64; c++) {
		for (d = 0; d < 64; d++) {
		for (e = 0; e < 64; e++) {

			if (c <= H1 || c >= A8)
				continue;
			if (b <= H1 || b >= A8)
				continue;
			if (d <= H1 || d >= A8)
				continue;
			
			pw[0] = a;
			pw[1] = b;	
			pw[2] = c;
			pw[3] = d;
			pw[4] = NOSQUARE;
		
			pb[0] = e;
			pb[1] = NOSQUARE;
	
			if (kpppk_pctoindex (pw, pb, &i)) {
							kpppk_indextopc (i, px, py);		
							kpppk_pctoindex (px, py, &j);
							if (i != j) {
								err = TRUE;
							}
							assert (i == j);	
			}
	
		}
		}
		}
		}

        if ((a&1)==0) {
            printf(".");
            fflush(stdout);
        }
	}

	if (err)		
		printf ("> %s NOT passed\n", str);	
	else
		printf ("> %s PASSED\n", str);	
	return !err;
}


static void
kpppk_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c * BLOCK_C + d; 
	*----------------------------------------------------------*/

	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 
	index_t a, b, c, r;
	SQUARE m, n, o;
	
	r  = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r;
	
	m = ppp48_sq_x [a];
	n = ppp48_sq_y [a];
	o = ppp48_sq_z [a];

	
	pw[0] = (SQUARE)b;
	pw[1] = m + 8;
	pw[2] = n + 8;
	pw[3] = o + 8;
	pw[4] = NOSQUARE;

	pb[0] = (SQUARE)c;	
	pb[1] = NOSQUARE;	


	assert (A2 <= pw[1] && pw[1] < A8);
	assert (A2 <= pw[2] && pw[2] < A8);
	assert (A2 <= pw[3] && pw[3] < A8);
	assert (kpppk_pctoindex (pw, pb, &a) && a == i);

	return;
}


static bool_t 
kpppk_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 
	index_t ppp48_slice;	
	
	SQUARE wk     = pw[0];
	SQUARE pawn_a = pw[1];
	SQUARE pawn_b = pw[2];
	SQUARE pawn_c = pw[3];	

	SQUARE bk     = pb[0];

	SQUARE i, j, k;

	assert (A2 <= pawn_a && pawn_a < A8);
	assert (A2 <= pawn_b && pawn_b < A8);
	assert (A2 <= pawn_c && pawn_c < A8);

	i = pawn_a - 8;
	j = pawn_b - 8;
	k = pawn_c - 8;

	ppp48_slice = ppp48_idx [i] [j] [k];

	if (IDX_is_empty(ppp48_slice)) { 
		wk     = flipWE (wk);		
		pawn_a = flipWE (pawn_a);
		pawn_b = flipWE (pawn_b);
		pawn_c = flipWE (pawn_c);
		bk     = flipWE (bk);		
	}

	i = pawn_a - 8;
	j = pawn_b - 8;
	k = pawn_c - 8;

	ppp48_slice = ppp48_idx [i] [j] [k];
 
	if (IDX_is_empty(ppp48_slice)) {
		*out = NOINDEX;
		return FALSE;
	}

	assert (ppp48_slice < MAX_PPP48_INDEX );
	
	*out = (index_t)ppp48_slice * BLOCK_A + (index_t)wk * BLOCK_B  + (index_t)bk;

	return TRUE;
}


/********************** end KPPP/K ************************************/


static bool_t 
kpkp_pctoindex (const SQUARE *pw, const SQUARE *pb, index_t *out)
{
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 	
	SQUARE pp_slice;	
	SQUARE anchor, loosen;
	
	SQUARE wk     = pw[0];
	SQUARE bk     = pb[0];
	SQUARE pawn_a = pw[1];
	SQUARE pawn_b = pb[1];

	SQUARE m, n;

	#ifdef DEBUG
	if (!(A2 <= pawn_a && pawn_a < A8)) {
		printf ("\n\nsquare of pawn_a: %s\n", Square_str[pawn_a]);
		printf(" wk %s\n p1 %s\n p2 %s\n bk %s\n"
			, Square_str[wk]
			, Square_str[pawn_a]
			, Square_str[pawn_b]
			, Square_str[bk]
			);
	}
	#endif

	assert (A2 <= pawn_a && pawn_a < A8);
	assert (A2 <= pawn_b && pawn_b < A8);
	assert (pw[2] == NOSQUARE && pb[2] == NOSQUARE);

	/*pp_putanchorfirst (pawn_a, pawn_b, &anchor, &loosen);*/
	anchor = pawn_a;
	loosen = pawn_b;

	if ((anchor & 07) > 3) { /* column is more than 3. e.g. = e,f,g, or h */
		anchor = flipWE (anchor);
		loosen = flipWE (loosen);
		wk     = flipWE (wk);		
		bk     = flipWE (bk);		
	}
 
	m = (SQUARE)wsq_to_pidx24 (anchor);
	n = loosen - 8;

	pp_slice = m * 48 + n; 

	if (IDX_is_empty(pp_slice)) {
		*out = NOINDEX;
		return FALSE;
	}

	assert (pp_slice < MAX_PpINDEX );
	
	*out = (index_t) (pp_slice * BLOCK_A + wk * BLOCK_B  + bk);

	return TRUE;
}


static void
kpkp_indextopc (index_t i, SQUARE *pw, SQUARE *pb)
{
	/*---------------------------------------------------------*
		inverse work to make sure that the following is valid
		index = a * BLOCK_A + b * BLOCK_B + c; 
	*----------------------------------------------------------*/
	enum  {B11100  = 7u << 2};
	enum  {BLOCK_A = 64*64, BLOCK_B = 64}; 
	enum  {block_m = 48};
	index_t a, b, c, r;
	index_t m, n;
	SQUARE sq_m, sq_n;
	
	r = i;
	a  = r / BLOCK_A;
	r -= a * BLOCK_A;
	b  = r / BLOCK_B;
	r -= b * BLOCK_B;	
	c  = r;
	
	/* unpack a, which is pslice, into m and n */
	r = a;
	m  = r / block_m;
	r -= m * block_m;
	n  = r ;

	sq_m  = pidx24_to_wsq (m);
	sq_n  = (SQUARE)n + 8;
	
	pw[0] = (SQUARE)b;
	pb[0] = (SQUARE)c;	
	pw[1] = sq_m;
	pb[1] = sq_n;
	pw[2] = NOSQUARE;
	pb[2] = NOSQUARE;	
	
	assert (A2 <= pw[1] && pw[1] < A8);
	assert (A2 <= pb[1] && pb[1] < A8);

	return;
}


/****************************************************************************\
 *
 *
 *								DEBUG ZONE 
 *
 *
 ****************************************************************************/

#if defined(DEBUG) 
static void
print_pos (const sq_t *ws, const sq_t *bs, const pc_t *wp, const pc_t *bp)
{
	int i;
	printf ("White: ");
	for (i = 0; ws[i] != NOSQUARE; i++) {
		printf ("%s%s ", P_str[wp[i]], Square_str[ws[i]]);	
	}
	printf ("\nBlack: ");
	for (i = 0; bs[i] != NOSQUARE; i++) {
		printf ("%s%s ", P_str[bp[i]], Square_str[bs[i]]);	
	}
	printf ("\n");
}
#endif

#if defined(DEBUG) || defined(FOLLOW_EGTB)
static void
output_state (unsigned stm, const SQUARE *wSQ, const SQUARE *bSQ, 
								const SQ_CONTENT *wPC, const SQ_CONTENT *bPC)
{
	int i;
	assert (stm == WH || stm == BL);
	
	printf("\n%s to move\n", stm==WH?"White":"Black");
	printf("W: ");
	for (i = 0; wSQ[i] != NOSQUARE; i++) {
		printf("%s%s ", P_str[wPC[i]], Square_str[wSQ[i]]);
	} 				
	printf("\n");
	printf("B: ");	
	for (i = 0; bSQ[i] != NOSQUARE; i++) {
		printf("%s%s ", P_str[bPC[i]], Square_str[bSQ[i]]);
	} 
	printf("\n\n");
}
#endif

static void
list_index (void)
{
	enum  {START_GTB = 0, END_GTB = (MAX_EGKEYS)};
	int i; 
	index_t accum = 0;
	printf ("\nIndex for each GTB\n");
		printf ("%3s: %7s  %7s   %7s   %7s\n" , "i", "TB", "RAM-slice", "RAM-max", "HD-cumulative");	
	for (i = START_GTB; i < END_GTB; i++) { 
		index_t indiv_k  = egkey[i].maxindex * (index_t)sizeof(dtm_t) * 2/1024;
		accum += indiv_k;
		printf ("%3d: %7s %8luk %8luk %8luM\n", i, egkey[i].str, (long unsigned)(indiv_k/egkey[i].slice_n), 
													(long unsigned)indiv_k, (long unsigned)accum/1024/2);	
	}
	printf ("\n");	
	return;
}

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

 NEW_WDL

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

/*---------------------------------------------------------------------*\
|			WDL CACHE Implementation  ZONE
\*---------------------------------------------------------------------*/

/*
|			WDL CACHE Statics
\*---------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
static unsigned int		wdl_extract (unit_t *uarr, index_t x);
static wdl_block_t *	wdl_point_block_to_replace (void);
static void				wdl_movetotop (wdl_block_t *t);

#if 0
static bool_t			wdl_cache_init (size_t cache_mem);
static void				wdl_cache_flush (void);
static bool_t			get_WDL (tbkey_t key, unsigned side, index_t idx, unsigned int *info_out, bool_t probe_hard_flag);
#endif

static bool_t			wdl_cache_is_on (void);
static void				wdl_cache_reset_counters (void);
static void				wdl_cache_done (void);

static wdl_block_t *	wdl_point_block_to_replace (void);
static bool_t			get_WDL_from_cache (tbkey_t key, unsigned side, index_t idx, unsigned int *out);
static void				wdl_movetotop (wdl_block_t *t);
static bool_t			wdl_preload_cache (tbkey_t key, unsigned side, index_t idx);

/*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------*\
|			WDL CACHE Maintainance
\*---------------------------------------------------------------------*/


static size_t
wdl_cache_init (size_t cache_mem)
{
	unsigned int 	i;
	wdl_block_t 	*p;
	size_t 			entries_per_block;
	size_t 			max_blocks;
	size_t 			block_mem;

	if (WDL_CACHE_INITIALIZED)
		wdl_cache_done();

	entries_per_block 	= 16 * 1024;  /* fixed, needed for the compression schemes */

	WDL_units_per_block	= entries_per_block / WDL_entries_per_unit;
	block_mem			= WDL_units_per_block * sizeof(unit_t);

	max_blocks 			= cache_mem / block_mem;
	cache_mem 			= max_blocks * block_mem;


	wdl_cache_reset_counters ();

	wdl_cache.entries_per_block = entries_per_block;
	wdl_cache.max_blocks 		= max_blocks;
	wdl_cache.cached 			= TRUE;
	wdl_cache.top 				= NULL;
	wdl_cache.bot 				= NULL;
	wdl_cache.n 				= 0;

	if (0 == cache_mem || NULL == (wdl_cache.buffer = (unit_t *) malloc (cache_mem))) {
		wdl_cache.cached = FALSE;
		return 0;
	}

	if (0 == max_blocks|| NULL == (wdl_cache.blocks = (wdl_block_t *) malloc (max_blocks * sizeof(wdl_block_t)))) {
		wdl_cache.cached = FALSE;
		free (wdl_cache.buffer);
		return 0;
	}
	
	for (i = 0; i < max_blocks; i++) {
		p = &wdl_cache.blocks[i];
		p->key  	= -1;
		p->side 	= gtbNOSIDE;
		p->offset 	= gtbNOINDEX;
		p->p_arr 	= wdl_cache.buffer + i * WDL_units_per_block;
		p->prev 	= NULL;
		p->next 	= NULL;
	}

	WDL_CACHE_INITIALIZED = TRUE;

	return cache_mem;
}


static void
wdl_cache_done (void)
{
	assert(WDL_CACHE_INITIALIZED);

	wdl_cache.cached = FALSE;
	wdl_cache.hard = 0;
	wdl_cache.soft = 0;
	wdl_cache.hardmisses = 0;
	wdl_cache.hits = 0;
	wdl_cache.softmisses = 0;
	wdl_cache.comparisons = 0;
	wdl_cache.max_blocks = 0;
	wdl_cache.entries_per_block = 0;

	wdl_cache.top = NULL;
	wdl_cache.bot = NULL;
	wdl_cache.n = 0;

	if (wdl_cache.buffer != NULL)
		free (wdl_cache.buffer);
	wdl_cache.buffer = NULL;

	if (wdl_cache.blocks != NULL)
		free (wdl_cache.blocks);
	wdl_cache.blocks = NULL;

	WDL_CACHE_INITIALIZED = FALSE;
	return;
}


static void
wdl_cache_flush (void)
{
	unsigned int 	i;
	wdl_block_t 	*p;
	size_t max_blocks = wdl_cache.max_blocks;

	wdl_cache.top 				= NULL;
	wdl_cache.bot 				= NULL;
	wdl_cache.n 				= 0;
	
	for (i = 0; i < max_blocks; i++) {
		p = &wdl_cache.blocks[i];
		p->key  	= -1;
		p->side 	= gtbNOSIDE;
		p->offset 	= gtbNOINDEX;
		p->p_arr 	= wdl_cache.buffer + i * WDL_units_per_block;
		p->prev 	= NULL;
		p->next 	= NULL;
	}

	wdl_cache_reset_counters  ();

	return;
}


static void
wdl_cache_reset_counters (void)
{
	wdl_cache.hard = 0;
	wdl_cache.soft = 0;
	wdl_cache.hardmisses = 0;
	wdl_cache.hits = 0;
	wdl_cache.softmisses = 0;
	wdl_cache.comparisons = 0;
	return;
}


static bool_t
wdl_cache_is_on (void)
{
	return wdl_cache.cached;
}

/****************************************************************************\
|						Replacement
\****************************************************************************/

static wdl_block_t *
wdl_point_block_to_replace (void)
{
	wdl_block_t *p, *t, *s;

	assert (0 == wdl_cache.n || wdl_cache.top != NULL);
	assert (0 == wdl_cache.n || wdl_cache.bot != NULL);
	assert (0 == wdl_cache.n || wdl_cache.bot->prev == NULL);
	assert (0 == wdl_cache.n || wdl_cache.top->next == NULL);

	if (wdl_cache.n > 0 && -1 == wdl_cache.top->key) {

		/* top blocks is unusable, should be the one to replace*/
		p = wdl_cache.top;

	} else
	if (wdl_cache.n == 0) {
		
		p = &wdl_cache.blocks[wdl_cache.n++];
		wdl_cache.top = p;
		wdl_cache.bot = p;
	
		p->prev = NULL;
		p->next = NULL;

	} else
	if (wdl_cache.n < wdl_cache.max_blocks) { /* add */

		s = wdl_cache.top;
		p = &wdl_cache.blocks[wdl_cache.n++];
		wdl_cache.top = p;
	
		s->next = p;
		p->prev = s;
		p->next = NULL;

	} else {                       /* replace*/ 
		
		t = wdl_cache.bot;
		s = wdl_cache.top;
		wdl_cache.bot = t->next;
		wdl_cache.top = t;
		
		s->next = t;
		t->prev = s;
		wdl_cache.top->next = NULL;
		wdl_cache.bot->prev = NULL;

		p = t;
	}
	
	/* make the information content unusable, it will be replaced */
	p->key    = -1;
	p->side   = gtbNOSIDE;
	p->offset = gtbNOINDEX;

	return p;
}

/****************************************************************************\
|
|						NEW PROBING ZONE
|
\****************************************************************************/

static unsigned int	wdl_extract (unit_t *uarr, index_t x);
static bool_t		get_WDL_from_cache (tbkey_t key, unsigned side, index_t idx, unsigned int *info_out);
static unsigned 	dtm2WDL(dtm_t dtm);	
static void			wdl_movetotop (wdl_block_t *t);
static bool_t		wdl_preload_cache (tbkey_t key, unsigned side, index_t idx);
static void			dtm_block_2_wdl_block(dtm_block_t *g, wdl_block_t *w, size_t n);	

static bool_t
get_WDL (tbkey_t key, unsigned side, index_t idx, unsigned int *info_out, bool_t probe_hard_flag)
{
	dtm_t dtm;
	bool_t found;

	found = get_WDL_from_cache (key, side, idx, info_out);

	if (found) {
		wdl_cache.hits++;
	} else {
		/* may probe soft */
		found = get_dtm (key, side, idx, &dtm, probe_hard_flag);
		if (found) {
			*info_out = dtm2WDL(dtm);			
			/* move cache info from dtm_cache to WDL_cache */
			if (wdl_cache_is_on())
				wdl_preload_cache (key, side, idx);
		} 
	}

	if (probe_hard_flag) {
		wdl_cache.hard++;
		if (!found) {
			wdl_cache.hardmisses++;
		}
	} else {
		wdl_cache.soft++;
		if (!found) {
			wdl_cache.softmisses++;
		}
	}

	return found;
}

static bool_t
get_WDL_from_cache (tbkey_t key, unsigned side, index_t idx, unsigned int *out)
{
	index_t 	offset;
	index_t		remainder;
	wdl_block_t	*p;
	wdl_block_t	*ret;

	if (!wdl_cache_is_on())
		return FALSE;

	split_index (wdl_cache.entries_per_block, idx, &offset, &remainder); 

	ret = NULL;
	for (p = wdl_cache.top; p != NULL; p = p->prev) {

		wdl_cache.comparisons++;

		if (key == p->key && side == p->side && offset  == p->offset) {
			ret = p;
			break;
		}
	}

	if (ret != NULL) {
		*out = wdl_extract (ret->p_arr, remainder); 
		wdl_movetotop(ret);
	}

	FOLLOW_LU("get_wdl_from_cache ok?",(ret != NULL))

	return ret != NULL;
}

static unsigned int
wdl_extract (unit_t *uarr, index_t x)
{
	index_t width = 2;
	index_t nu = x/WDL_entries_per_unit;
	index_t y  = x - (nu * WDL_entries_per_unit);
	return (uarr[nu] >> (y*width)) & WDL_entry_mask;
}

static void
wdl_movetotop (wdl_block_t *t)
{
	wdl_block_t *s, *nx, *pv;

	assert (t != NULL);

	if (t->next == NULL) /* at the top already */
		return;

	/* detach */
	pv = t->prev;
	nx = t->next;

	if (pv == NULL)  /* at the bottom */
		wdl_cache.bot = nx;
	else 
		pv->next = nx;

	if (nx == NULL) /* at the top */
		wdl_cache.top = pv;
	else
		nx->prev = pv;

	/* relocate */
	s = wdl_cache.top;
	assert (s != NULL);
	if (s == NULL)
		wdl_cache.bot = t;	
	else
		s->next = t;

	t->next = NULL;
	t->prev = s;
	wdl_cache.top = t;

	return;
}

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

static bool_t
wdl_preload_cache (tbkey_t key, unsigned side, index_t idx)
/* output to the least used block of the cache */
{
	dtm_block_t		*dtm_block;
	wdl_block_t 	*to_modify;
	bool_t 			ok;

	FOLLOW_label("wdl preload_cache starts")

	if (idx >= egkey[key].maxindex) {
		FOLLOW_LULU("Wrong index", __LINE__, idx)	
		return FALSE;
	}

	/* find fresh block in dtm cache */
	dtm_block = dtm_cache_pointblock (key, side, idx); 

	/* find aged blocked in wdl cache */
	to_modify = wdl_point_block_to_replace ();

	ok = !(NULL == dtm_block || NULL == to_modify);

	if (!ok)
		return FALSE;
	
	/* transform and move a block */
	dtm_block_2_wdl_block(dtm_block, to_modify, dtm_cache.entries_per_block);	

	if (ok) {
		index_t 		offset;
		index_t			remainder;
		split_index (wdl_cache.entries_per_block, idx, &offset, &remainder); 

		to_modify->key    = key;
		to_modify->side   = side;
		to_modify->offset = offset;
	} else {
		/* make it unusable */
		to_modify->key    = -1;
		to_modify->side   = gtbNOSIDE;
		to_modify->offset = gtbNOINDEX;
	}

	FOLLOW_LU("wdl preload_cache?", ok)

	return ok;		
}

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

static void			
dtm_block_2_wdl_block(dtm_block_t *g, wdl_block_t *w, size_t n)
{
	int width = 2;
	int shifting;
	size_t i;
	int j;
	unsigned int x ,y;
	 dtm_t *s = g->p_arr;
	unit_t *d = w->p_arr;

	for (i = 0, y = 0; i < n; i++) {
		j =  i & 3; /* modulo WDL_entries_per_unit */
		x = dtm2WDL(s[i]);
		shifting = j * width;
		y |= (x << shifting);		
		
		if (j == 3) {
			d[i/WDL_entries_per_unit] = (unit_t) y;
			y = 0;
		}
	}

	if (0 != (n & 3)) { /* not multiple of 4 */
		d[(n-1)/WDL_entries_per_unit] = (unit_t) y; /* save the rest     */
		y = 0;
	}

	return;
}	

static unsigned 	
dtm2WDL(dtm_t dtm)
{
	return (unsigned) dtm & 3;
}	


/**************************/
#ifdef WDL_PROBE

static unsigned int	inv_wdl(unsigned w);
static bool_t	egtb_get_wdl (tbkey_t k, unsigned stm, const SQUARE *wS, const SQUARE *bS, bool_t probe_hard_flag, unsigned int *wdl);

static bool_t
tb_probe_wdl
			(unsigned stm, 
			 const SQUARE *inp_wSQ, 
			 const SQUARE *inp_bSQ,
			 const SQ_CONTENT *inp_wPC, 
			 const SQ_CONTENT *inp_bPC,
			 bool_t probingtype,
			 /*@out@*/ unsigned *res)
{
	tbkey_t id = -1;
	unsigned int wdl = iUNKNOWN;

	SQUARE 		storage_ws [MAX_LISTSIZE], storage_bs [MAX_LISTSIZE];
	SQ_CONTENT  storage_wp [MAX_LISTSIZE], storage_bp [MAX_LISTSIZE];

	SQUARE     *ws = storage_ws;
	SQUARE     *bs = storage_bs;
	SQ_CONTENT *wp = storage_wp;
	SQ_CONTENT *bp = storage_bp;
	SQUARE 		tmp_ws [MAX_LISTSIZE], tmp_bs [MAX_LISTSIZE];
	SQ_CONTENT  tmp_wp [MAX_LISTSIZE], tmp_bp [MAX_LISTSIZE];

	SQUARE *temps;
	bool_t straight = FALSE;
	
	bool_t  okcall  = TRUE;
	unsigned ply_;
	unsigned *ply = &ply_;

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

	assert (stm == WH || stm == BL);

	/* VALID ONLY FOR KK!! */
	if (inp_wPC[1] == NOPIECE && inp_bPC[1] == NOPIECE) {
		index_t dummy_i;
		bool_t b = kxk_pctoindex (inp_wSQ, inp_bSQ, &dummy_i);
		*res = b? iDRAW: iFORBID;
		*ply = 0;
		return TRUE;
	} 

	/* copy input */
	list_pc_copy (inp_wPC, wp);
	list_pc_copy (inp_bPC, bp);
	list_sq_copy (inp_wSQ, ws);
	list_sq_copy (inp_bSQ, bs);

	sortlists (ws, wp);
	sortlists (bs, bp);

	FOLLOW_label("EGTB_PROBE")

	if (egtb_get_id (wp, bp, &id)) {
		FOLLOW_LU("got ID",id)
		straight = TRUE;
	} else if (egtb_get_id (bp, wp, &id)) {
		FOLLOW_LU("rev ID",id)
		straight = FALSE;
		list_sq_flipNS (ws);
		list_sq_flipNS (bs);
        temps = ws;
        ws = bs;
        bs = temps;
		stm = Opp(stm);
		/* no enpassant in this fuction, so no adjustment */
		{SQ_CONTENT *tempp = wp; wp = bp; bp = tempp;} 	/* added */
	} else {
		#if defined(DEBUG)
		printf("did not get id...\n");
		output_state (stm, ws, bs, wp, bp);		
		#endif
		unpackdist (iFORBID, res, ply);
		return FALSE;
	}

	/* store position... */
	list_pc_copy (wp, tmp_wp);
	list_pc_copy (bp, tmp_bp);
	list_sq_copy (ws, tmp_ws);
	list_sq_copy (bs, tmp_bs);

	/* x will be stm and y will be stw */
/*
	if (stm == WH) {
        xs = ws;
        xp = wp;
        ys = bs;
        yp = bp;
    } else {
        xs = bs;
        xp = bp;
        ys = ws;
        yp = wp;
	}
*/
	okcall = egtb_get_wdl (id, stm, ws, bs, probingtype, &wdl);

	FOLLOW_LU("dtmok?",okcall)
	FOLLOW_DTM("wdl", wdl)

	if (okcall) {

		/*assert(epsq == NOSQUARE); */

		if (straight) {
			*res = wdl;
		} else {
			*res = inv_wdl (wdl);
		}	 
	} else {
			unpackdist (iFORBID, res, ply);
	}

	return okcall;
} 

static unsigned int
inv_wdl(unsigned w)
{
	unsigned r = tb_UNKNOWN;
	switch (w) {
		case tb_DRAW:    r = tb_DRAW;    break;
		case tb_WMATE:   r = tb_BMATE;   break;
		case tb_BMATE:   r = tb_WMATE;   break;
		case tb_FORBID:  r = tb_FORBID;  break;
		case tb_UNKNOWN: r = tb_UNKNOWN; break;
		default:         r = tb_UNKNOWN; break;
	}
	return r;
}

static bool_t
egtb_get_wdl (tbkey_t k, unsigned stm, const SQUARE *wS, const SQUARE *bS, bool_t probe_hard_flag, unsigned int *wdl)
{
	bool_t idxavail;
	index_t idx;
	dtm_t *tab[2];
	bool_t (*pc2idx) (const SQUARE *, const SQUARE *, index_t *);

	FOLLOW_label("egtb_get_wdl --> starts")

	if (egkey[k].status == STATUS_MALLOC || egkey[k].status == STATUS_STATICRAM) {

		tab[WH] = egkey[k].egt_w;
		tab[BL] = egkey[k].egt_b;
		pc2idx  = egkey[k].pctoi;

		idxavail = pc2idx (wS, bS, &idx);

		FOLLOW_LU("indexavail (RAM)",idxavail)

		if (idxavail) {
			*wdl = dtm2WDL(tab[stm][idx]);
		} else {
			*wdl = dtm2WDL(iFORBID);
		}

		return FALSE;

	} else if (egkey[k].status == STATUS_ABSENT) {

		pc2idx   = egkey[k].pctoi;
		idxavail = pc2idx (wS, bS, &idx);

		FOLLOW_LU("indexavail (HD)",idxavail)

		if (idxavail) {
			bool_t success;

			/* 
			|		LOCK 
			*-------------------------------*/
			mythread_mutex_lock (&Egtb_lock);	

			success = get_WDL (k, stm, idx, wdl, probe_hard_flag);
			FOLLOW_LU("get_wld (succ)",success)
			FOLLOW_LU("get_wld (wdl )",*wdl)

			/* this may not be needed */
			if (!success) {		
				dtm_t dtm;
				unsigned res, ply;
				if (probe_hard_flag && Uncompressed) {
					assert(Uncompressed);
					success = egtb_filepeek (k, stm, idx, &dtm);
					unpackdist (dtm, &res, &ply);			
					*wdl = res;		
				}
				else
					success = FALSE;
			}

			mythread_mutex_unlock (&Egtb_lock);	
			/*------------------------------*\ 
			|		UNLOCK 
			*/

			if (success) {
				return TRUE;
			} else {
				if (probe_hard_flag) /* after probing hard and failing, no chance to succeed later */
					egkey[k].status = STATUS_REJECT;
				*wdl = dtm2WDL(iUNKNOWN);
				return FALSE;
			}

		} else {
			*wdl = dtm2WDL(iFORBID);
			return 	TRUE;
		}
	} else if (egkey[k].status == STATUS_REJECT) {
		FOLLOW_label("STATUS_REJECT")
		*wdl = dtm2WDL(iFORBID);
		return 	FALSE;
	} else {
		FOLLOW_label("STATUS_WRONG!")
		assert(0);
		*wdl = dtm2WDL(iFORBID);
		return 	FALSE;
	} 

} 
#endif



