/* examin stuf in data-base format */

#include <sys/types.h>
#include <sys/file.h>
#include <curses.h>

#include "exam.h"
#define FIT_MODE
#include "ev_fit.h"

/* #define SUN_ENDIAN			/* controls endian-ess of database file */

#define MIN_FREQ  0.2			/* 5 occuraces are considered significant */
/* #define MIN_FREQ  2.0   		/* 5 occuraces are considered significant */

/* #define KEEP_PV			/* for test purposes only */
/* #define T_DEBUG			/* print search tree */
/* #define TE_DEBUG			/* add t_EVAL debugging code */
/* #define V_TRACE			/* dump eval's to "q" */
/* #define TF_DEBUG			/* table fit debug */

#define SCALEF 4096			/* coefficient scale factor (256 * 16) */

#ifndef sun
int rint(x)				/* mips doesn't have rint() */
    double x;
{
    register int    i;
    if (x < 0.0)
	i = x - 0.5;
    else
	i = x + 0.5;
    return i;
}
#else
double rint();
#endif

/******************** interface definitions to eval.c ********************/

extern struct fit_param *All_params[];	/* parameters		*/
extern struct fit_param *pc_params[8];	/* pice value parameters */
extern struct fit_param *fit_PV0,	/* pointer to pawn-values */
			*fit_PV1;	/* Special feature for 'PawnPlace'-table fit */
extern struct fit_param *fit_PPA;	/* pointer to PawnAdvance constant dump */

int n_param;				/* number of parameters */
int fit_const;				/* constant term to eval */

int sunboard[64];			/* actual board at current root */
int sun_cst;				/* castle status at current root */
extern int *fit_castle_st;		/* signals castle status to eval */
int FIT_cast = -1;			/* if != -1: lst move was castle, holds type */

int rook_ti[8];				/* rook table indeces */
int wpawn_ti[8];			/* w-pawn table indeces */
int bpawn_ti[8];			/* b-pawn table indeces */
int wpp_ti[8];				/* w-pawn promote table indeces */
int bpp_ti[8];				/* b-pawn promote table indeces */

int piece_ti[32];			/* piece/placement table indeces */
int n_ppt;				/* # of entries used in pp-tab */

int sq[64];				/* dummy, to keep eval.c happy */


/******************** chess stuff ********************/

int backup[64];			/* scratch */
int back_cst;				/* copy of castle status (used during move enumeration) */

int new_pos[64];			/* saved board after played move */
int new_cst;				/* copy of castle status (used during move enumeration) */

int Ex, E0, En;				/* scores at root, move 0, move n */

int stm;				/* side to move: 0=W 1=B */

int capture;				/* most recent capture (or EMPTY) */

int init_pos[64] = {
    WROOK, WKNIGHT, WBISHOP,  WQUEEN,   WKING, WBISHOP, WKNIGHT,   WROOK, 
    WPAWN,   WPAWN,   WPAWN,   WPAWN,   WPAWN,   WPAWN,   WPAWN,   WPAWN,
    EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,
    EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,
    EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,
    EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,   EMPTY,
    BPAWN,   BPAWN,   BPAWN,   BPAWN,   BPAWN,   BPAWN,   BPAWN,   BPAWN,
    BROOK, BKNIGHT, BBISHOP,  BQUEEN,   BKING, BBISHOP, BKNIGHT,   BROOK 
};

char pct[32] = {
    '*', 'p', 'n', 'b', 'r', 'q', 'k', '?',
    '*', 'P', 'N', 'B', 'R', 'Q', 'K', '?',
    '-', 'p', 'n', 'b', 'r', 'q', 'k', '?',
    '-', 'P', 'N', 'B', 'R', 'Q', 'K', '?'
};


/******************** File-I/O stuff ********************/

int dbf;				/* data base file */
char *dbf_nm;				/* name of db */
long gm_strt[MAX_GAMES];		/* seek table to start of games */
int cur_gm = 0, max_gm = MAX_GAMES;	/* current, max game in db */

int gm_type;				/* game type: 0=drawn, 1=white wins, 2=white loses */
char *gty[3] = {"drawn)", "white won)", "white lost)"};

int st_ind[128];			/* sub-tree indicees */
unsigned short *mlist;			/* move list (storage area) */
int mlist_max = 0;			/* size of move list */
int ml_cnt, ml_max;			/* current, max. # of moves in list */
int mvs_cnt;				/* real move count */

#ifndef SUN_ENDIAN
#define SWAPP_BYTES(x,y) swapp_bytes(x,y)
#else
#define SWAPP_BYTES(x,y)		/* don't do anything */
#endif

/******************** Fit related sruff ********************/

int conf_def = 0;			/* 1: a configuration is defined */

int i_skip;				/* initial skip */
int efunc;				/* error function */
char *ef[5] = {"LOG", "SQR", "LIN", "STP", "INV"};
int wfunc;				/* multiple error weighting */
int from, to;				/* range of games to use */
int a_weight;				/* weight of (Ex-E0)^2 term in % */
int min_g;				/* pull - offset */
int n_on, n_off;			/* of parameter in fit */
int n_kill;				/* number of positions skipped due to excessive evaluation (more 1/2 pawn difference) */

int vari_c;				/* variance controll */
int n_cor = 0;				/* times a compensation was applied */
double acc_cor = 0.0;			/* accumulated corrective force */

int n_err;				/* Number of bad moves evaluated better than the good move */

struct tab_descr *tbl_ptr = 0;		/* pointer to table under construction or 0 */
struct fit_param *par_ptr = 0;		/* associated parameter */

int hist[MAX_NMVS];			/* error histogram */
int hist_sa[MAX_PREGS][MAX_NMVS];	/* error histogram save area */
int psa[MAX_PREGS][MAX_NPRM];		/* parameter set storage area */

double A[MAX_NPRM][MAX_NPRM];		/* accumulator */
double X[MAX_NPRM];			/* result vector */
double D[MAX_NPRM];			/* const. part */

double A_wght, B_wght;			/* wights */

double A_err, B_err;			/* error sum */
int tot_pos, ok_pos;			/* total and correctly evaluated positions */
int tot_mvs, ok_mvs;			/* total and correctly evaluated moves */

/******************** Routine to swapp bytes in database for non-sun systems */

void swapp_bytes(unsigned short *usp, int n)
{
	unsigned short t;
	while (n-- > 0) {
		t = *usp;
		t = ((t >> 8) & 0x00ff) | ((t << 8) & 0xff00);
		*usp = t;
		usp++;
	}
}


/******************** Here we go ********************/

main (argc, argv)
    int	argc;
    char *argv[];
{
    char            t, *get_cmd();
    int		    i;
    struct fit_param *get_tbl();

    initialize(argc, argv);

    game(0);
    E0 = Ex = EVAL();
    disp_info();
    disp_board(backup);
    disp_param();
    refresh();

    while ('q' != (t = getch())) {	/* command loop */
	err(0, 0, 0, 0, 0);	/* clear pending messages */
	switch (t) {
#ifdef TE_DEBUG
	case 'X':
		test();
		err("test done", 0, 0, 0, 0);
		break;
#endif

	case 't':		/* table fit */
	    par_ptr = get_tbl();
	    if (par_ptr) {
		tbl_ptr = par_ptr->tp;
		if(!conf_def)
		    get_config();
		do_T_fit();
		disp_fit("T-");
		refresh();
	    } else {
		tbl_ptr = 0;
	    }
	    
	    break;

	case ' ':		/* next possible move */
	    if (!next()) {
		Ex = E0;
		if (!Next()) {
		    game(cur_gm + 1);
		    Ex = EVAL();
		} else
		    next();
	    }
	    if (ml_cnt == 1)
		E0 = EVAL();
	    else
		En = EVAL();
	    disp_info();
	    disp_board(backup);
	    disp_param();
	    refresh();
	    break;

	case 'n':		/* next played move */
	    Ex = E0;
	    if (!ml_cnt && mvs_cnt == 1)
		next();
	    else if (!Next()) {
		game(cur_gm + 1);
		E0 = Ex = EVAL();
	    } else {
		next();
		Ex = E0;
		E0 = EVAL();
	    }
	    disp_info();
	    disp_board(backup);
	    disp_param();
	    refresh();
	    break;

	case 'N':		/* next game */
	    game(cur_gm + 1);
	    E0 = Ex = EVAL();
	    disp_info();
	    disp_board(backup);
	    disp_param();
	    refresh();
	    break;

	case 'g':		/* goto specific game */
	    if (1 != sscanf(get_cmd("game #= "), "%d", &i) || i < 0)
		err("Invalid game number", 0, 0, 0, 0);
	    else {
		game(i);
		E0 = Ex = EVAL();
		disp_info();
		disp_board(backup);
		disp_param();
		refresh();
	    }
	    break;

	case 'c':
	    get_config();
	    err(0, 0, 0, 0, 0);
	    refresh();
	    break;

	case 'f':
	    if(!conf_def)
		get_config();
	    do_fit();
	    disp_fit("P-");
	    refresh();
	    break;

	case 's':
	    p_save(get_number(0, MAX_PREGS - 1, 0, "Save parameters to M[0-9]:"));
	    err(0, 0, 0, 0, 0);
	    refresh();
	    break;

	case 'r':
	    p_restore(get_number(0, MAX_PREGS - 1, 0, "Restore parameters from M[0-9]:"));
	    p_update();
	    err(0, 0, 0, 0, 0);
	    refresh();
	    break;

	case 'S':
	    p_Save(get_cmd("Save parameters to file:"), get_cmd("Optional Memo:"), !strcmp("y", get_cmd("All Tabs? [y/n]:")));
	    break;

	case 'R':
	    p_Restore(get_cmd("Restore parameters from file:"));
	    p_update();
	    refresh();
	    break;

	case 'e':
	    p_edit();
	    break;
	    
	case 'w':
	    p_write(get_cmd("Output file name:"));
	    break;

	case 'F':
	    if(!conf_def)
		get_config();
	    do_long_fit(get_number(1, MAX_PREGS - 1, MAX_PREGS - 1, "Number of iterations:"), get_cmd("Log file prefix:"));
	    disp_fit("P-");
	    refresh();
	    break;

	case 'T':
	    if(!conf_def)
		get_config();
	    if(!tbl_ptr) {
		par_ptr = get_tbl();
		if (!par_ptr)
		    break;
		tbl_ptr = par_ptr->tp;
	    }
	    do_long_T_fit(get_number(1, MAX_PREGS - 1, MAX_PREGS - 1, "Number of iterations:"), get_cmd("Log file prefix:"));
	    disp_fit("T-");
	    refresh();
	    break;

	case 'd':		    /* dump data for 3d plot */
	    par_ptr = get_tbl();
	    if (par_ptr) {
		tbl_ptr = par_ptr->tp;
		d_dump(get_cmd("Data dump file name:"));
		tbl_ptr = 0;
	    }
	    break;

	default:
	    err("Available commands are: [ deFfgnNrRstTSqw]", 0, 0, 0, 0);
	    break;
	}
    }

    quit(0);
}

char *sh_int(i)			    /* short hand integer notation */
    register int i;
{
    static char buf[4][5];
    static int cnt = 0;
    static char pf[3] = {'K', 'M', 'G'};
    register int j = 0;
    register char *s;

    s = &(buf[cnt++ & 3][0]);
    if(i<0) {
	s[0] = '-';
	i = -i;
    } else
	s[0] = ' ';

    if (i > 999999999) {
	j = 3;
	i = (i + 50000000) / 100000000;
    } else if (i > 999999) {
	j = 2;
	i = (i + 50000) / 100000;
    } else if (i > 999) {		    /* 1000 ... 999999 */
	j = 1;
	i = (i + 50) / 100;
    }

    if (j) {
	if (i < 100) {
	    s[1] = '0' + i / 10;
	    s[2] = pf[j - 1];
	    s[3] = '0' + i % 10;
	} else if (i < 1000){
	    i = (i + 5) / 10;
	    s[1] = '0' + i / 10;
	    s[2] = '0' + i % 10;
	    s[3] = pf[j - 1];
	} else {
	    i = (i + 50) / 100;
	    s[1] = pf[j];
	    s[2] = '0' + i / 10;
	    s[3] = '0' + i % 10;
	}
	s[4] = 0;
    } else 
	sprintf (s, "%4d", (s[0] == '-') ? -i : i);

    return s;
}

char *m_dec(m, brd, ms)
    unsigned short m;
    register int *brd;
    register int ms;
/*
 * note: brd: board before move (normally sunboard)
 *       ms:  move-side: normally sunborad
 */
{
    register int to = (m & TO) >> 4, from = (m & FROM) >> 10, i;

    static char *cst[4] = {"o-o", "o-o-o", "O-O", "O-O-O"}, s[12];

    switch (m & PIECE) {
	case KING:
	    if (to == from) 	    /* castle move */
		return cst[to];
	case PAWN:
	case KNIGHT:
	case BISHOP:
	case ROOK:
	case QUEEN:
	    s[0] = pct[(m & PIECE) + 8*ms];
	    s[1] = 'a' + (from & 7);
	    s[2] = '1' + (from >> 3);
	    s[3] = 'a' + (to & 7);
	    s[4] = '1' + (to >> 3);
	    if (m & CAPTURE) {
		if (brd[to] == EMPTY) {
		    fprintf(stderr, "m_dec: nothing to capture ??\n");
		    s[5] = '?';
		} else if ((brd[to] >> 3) & 1 == ms) {
		    fprintf(stderr, "m_dec: capture own piece ??\n");
		    s[5] = '!';
		} else
		    s[5] = pct[brd[to]];
		s[6] = 0;
	    } else
		s[5] = 0;
	    break;

	case P_PAWN:
	    i = to >> 3;
	    to = (to & 7) + (((ms ^ 1) * 7) << 3);
	    s[0] = pct[1 + 8*ms];
	    s[1] = 'a' + (from & 7);
	    s[2] = '1' + (from >> 3);
	    s[3] = 'a' + (to & 7);
	    s[4] = '1' + (to >> 3);
	    if (m & CAPTURE) {
		if (brd[to] == EMPTY) {
		    fprintf(stderr, "m_dec: nothing to capture ??\n");
		    s[5] = '?';
		} else if ((brd[to] >> 3) & 1 == ms) {
		    fprintf(stderr, "m_dec: capture own piece ??\n");
		    s[5] = '!';
		} else
		    s[5] = pct[brd[to]];
		s[6] = '(';
		s[7] = pct[i + 8*ms];
		s[8] = ')';
		s[9] = 0;
	    } else {
		s[5] = '(';
		s[6] = pct[i + 8*ms];
		s[7] = ')';
		s[8] = 0;
	    }
	    break;

	case EP_PAWN:
	    s[0] = pct[1 + 8*ms];
	    s[1] = 'a' + (from & 7);
	    s[2] = '1' + (from >> 3);
	    s[3] = 'a' + (to & 7);
	    s[4] = '1' + (to >> 3);

	    if (m & CAPTURE) {
		i = (ms) ? to + 8 : to - 8;
		if (brd[i] == EMPTY) {
		    fprintf(stderr, "m_dec: nothing to capture ??\n");
		    s[5] = '?';
		} else if ((brd[i] >> 3) & 1 == ms) {
		    fprintf(stderr, "m_dec: capture own piece ??\n");
		    s[5] = '!';
		} else
		    s[5] = pct[brd[i]];
		s[6] = '(';
		s[7] = 'E';
		s[8] = 'P';
		s[9] = ')';
		s[10] = 0;
	    } else {
		fprintf(stderr, "m_dec: EP not capture ??\n");
		s[5] = '?';
		s[6] = '(';
		s[7] = 'E';
		s[8] = 'P';
		s[9] = ')';
		s[10] = 0;
	    }
	    break;

	default:
	    return "shit";
    }

    return s;
}

disp_board(b)
    int *b;
{
    register int    i, j, x, y;

    for (i = 8, y = D_BPOS_Y; i--; y++) {
	for (j = 0, x = D_BPOS_X + 3; j < 8; j++, x += 2) {
	    move(y, x);
	    addch(pct[b[i * 8 + j] + 16 * ((i ^ j) & 1)]);
	}
    }
}

b_update(m, r_stm)		    /* apply move m to board */
    register int r_stm;
    unsigned short m;
{
    register int to = (m & TO) >> 4, from = (m & FROM) >> 10, i;
    register int *bp = backup;

    FIT_cast = -1;

    switch (m & PIECE) {
	case KING:
	    if (to == from) { 	    /* castle move */
		FIT_cast = to;
		i = r_stm * 56 + 4;
		bp[i] = EMPTY;
		if (to & 1) {	    /* long */
		    bp[i - 2] = KING + r_stm*8;
		    bp[i - 4] = EMPTY;
		    bp[i - 1] = ROOK + r_stm*8;
		} else {	    /* short */
		    bp[i + 2] = KING + r_stm*8;
		    bp[i + 3] = EMPTY;
		    bp[i + 1] = ROOK + r_stm*8;
		}
		break;
	    }
	case PAWN:
	case KNIGHT:
	case BISHOP:
	case ROOK:
	case QUEEN:
	    capture = bp[to];
	    bp[to] = bp[from];
	    bp[from] = EMPTY;
	    break;

	case P_PAWN:
	    i = (to & 7);
	    if (!r_stm) i += 0x38;
	    capture = bp[i];
	    bp[i] = (to >> 3) + r_stm*8;
	    bp[from] = EMPTY;
	    break;

	case EP_PAWN:
	    bp[to] = bp[from];
	    bp[from] = EMPTY;
	    if (r_stm) {
		capture = WPAWN;
		bp[to + 8] = EMPTY;
	    } else{
		capture = BPAWN;
		bp[to - 8] = EMPTY;
	    }
	    break;
    }

					    /* update castle status */
    if (bp[0] != WROOK || bp[4] != WKING) *fit_castle_st &= ~WLCastle;
    if (bp[7] != WROOK || bp[4] != WKING) *fit_castle_st &= ~WSCastle;
    if (bp[56] != BROOK || bp[60] != BKING) *fit_castle_st &= ~BLCastle;
    if (bp[63] != BROOK || bp[60] != BKING) *fit_castle_st &= ~BSCastle;
}

EVAL()			/* update table indecess */
{
    register int i, j, *p;
    register struct fit_param **ft;
    int wp,bp,lwp,lbp,rwp,rbp,wrcnt,brcnt,neblked,noblked;

    for (i = 64, p = &(backup[63]), j = 0; i--; p--)	/* update pp-tab */
	if (*p != EMPTY)		/* found a piece */
	    piece_ti[j++] = i;
    n_ppt = j;

    lwp = lbp = 0;
    wp = bp = 0;
    for (i = 6; i > 0; --i) {
	wp = (wp << 1) + ((backup[8 * i] == WPAWN) ? 1 : 0);
    }
    for (i = 1; i < 7; ++i) {
	bp = (bp << 1) + ((backup[8 * i] == BPAWN) ? 1 : 0);
    }

    for (i = 0; i < 8; ++i) {
	rwp = rbp = 0;
	if (i != 7)
	    for (j = 6; j > 0; --j) {
		rwp = (rwp << 1) + ((backup[8 * j + i + 1] == WPAWN) ? 1 : 0);
	    }
	if (i != 7)
	    for (j = 1; j < 7; ++j) {
		rbp = (rbp << 1) + ((backup[8 * j + i + 1] == BPAWN) ? 1 : 0);
	    }
	wrcnt = brcnt = 0;
	for (j = 0; j < 8; ++j) {
	    wrcnt += (backup[8 * j + i] == WROOK) ? 1 : 0;
	    brcnt += (backup[8 * j + i] == BROOK) ? 1 : 0;
	}
	wrcnt &= 3;
	brcnt &= 3;

	wpawn_ti[i] = (i << 10) + ((bp == 0) ? 0x200 : 0) + (((lwp | rwp) & 0xf) << 5) + (wp & 0x1f);
	bpawn_ti[i] = (i << 10) + ((wp == 0) ? 0x200 : 0) + (((lbp | rbp) & 0xf) << 5) + (bp & 0x1f);
	wpp_ti[i] = (i << 10) + (((lbp | bp | rbp) & 0x1f) << 5) + (wp & 0x1f);
	bpp_ti[i] = (i << 10) + (((lwp | wp | rwp) & 0x1f) << 5) + (bp & 0x1f);

	if (((wp & 1) && (bp & 0x10)) || ((wp & 4) && (bp & 4)) || ((wp & 0x10) && (bp & 1))) {
	    neblked = 0;
	} else {
	    neblked = 0x400;
	}
	if (((wp & 2) && (bp & 8)) || ((wp & 8) && (bp & 2))) {
	    noblked = 0;
	} else {
	    noblked = 0x200;
	}

	rook_ti[i] = neblked + noblked + (bp ? 0 : 0x100) + (wp ? 0 : 0x80) + (i << 4) + (brcnt << 2) + wrcnt;
	lwp = wp;
	lbp = bp;
	wp = rwp;
	bp = rbp;
    }

    for(ft = All_params; *ft; ft++)
	(*ft)->coef = 0;
    fit_const = 0;

    eval();

    for(ft = All_params, i = fit_const; *ft; ft++) {
	j = (*ft)->coef * (*ft)->init;
	i += j /* unscale(j) */;
    }

    return i;
}

disp_param()
{
    register int    i, k;
    register struct fit_param *p;
    char            buf[40];

    for (k = (n_param + 3) / 4, i = 0; i < k; i++) {
	p = All_params[i];
	if (p->coef != p->last) {
	    move(D_PPOS_Y + i, D_PPOS_X + 24);
	    if (p->coef) {
	        sprintf(buf, "%4s=%4s", sh_int(p->coef), sh_int(unscale(p->init * p->coef)));
	        addstr(buf);
	    } else
		addstr("   0=     ");
	    p->last = p->coef;
	}

	p = All_params[i + k];
	if (p && p->coef != p->last) {
	    move(D_PPOS_Y + i, D_PPOS_X + 59);
	    if (p->coef) {
	        sprintf(buf, "%4s=%4s", sh_int(p->coef), sh_int(unscale(p->init * p->coef)));
	        addstr(buf);
	    } else
		addstr("   0=     ");
	    p->last = p->coef;
	}

	p = All_params[i + 2 * k];
	if (p && p->coef != p->last) {
	    move(D_PPOS_Y + i, D_PPOS_X + 94);
	    if (p->coef) {
	        sprintf(buf, "%4s=%4s", sh_int(p->coef), sh_int(unscale(p->init * p->coef)));
	        addstr(buf);
	    } else
		addstr("   0=     ");
	    p->last = p->coef;
	}

	p = All_params[i + 3 * k];
	if (p && p->coef != p->last) {
	    move(D_PPOS_Y + i, D_PPOS_X + 129);
	    if (p->coef) {
	        sprintf(buf, "%4s=%4s", sh_int(p->coef), sh_int(unscale(p->init * p->coef)));
	        addstr(buf);
	    } else
		addstr("   0=     ");
	    p->last = p->coef;
	}
    }
}

disp_info()		    /* display general info */
{
    char            *m_dec(), buf[150];

    sprintf(buf, "File: %s  game: %-3d (%-12s  move-#: %-2d  side-to-move: %c  move: %-10s (%2d/%-2d)",
	    dbf_nm, cur_gm, gty[gm_type], mvs_cnt, (stm) ? 'B' : 'W', ml_cnt ? m_dec(mlist[ml_cnt], sunboard, stm) : ".....",
	    ml_cnt, ml_max);
    move(0, 0);
    addstr(buf);

    if (!ml_cnt) {
	sprintf(buf, "%-6d", unscale(Ex));
	move(D_SPOS_Y, D_SPOS_X + 4);
	addstr(buf);
	sprintf(buf, "      ");
	move(D_SPOS_Y + 1, D_SPOS_X + 4);
	addstr(buf);
	move(D_SPOS_Y + 2, D_SPOS_X + 1);
	addstr("  =      ");
    } else if (ml_cnt == 1) {
	sprintf(buf, "%-6d", unscale(Ex));
	move(D_SPOS_Y, D_SPOS_X + 4);
	addstr(buf);
	sprintf(buf, "%-6d", unscale(E0));
	move(D_SPOS_Y + 1, D_SPOS_X + 4);
	addstr(buf);
	move(D_SPOS_Y + 2, D_SPOS_X + 1);
	addstr("  =      ");
    } else {
	sprintf(buf, "%-2d=%-6d", ml_cnt - 1, unscale(En));
	move(D_SPOS_Y + 2, D_SPOS_X + 1);
	addstr(buf);
    }
}

initialize(argc, argv)
    int	argc;
    char *argv[];
{
    register int    i, k, y;
    char buf[40], err[80];

    if (argc != 2 || 0 > (dbf = open(argv[1], O_RDONLY))) {
	fprintf(stderr, "exam <db-file>\n");
	exit(1);
    }
    dbf_nm = argv[1];
    initscr();
    cbreak();
    noecho();

    if (LINES < 40 || COLS < 140) {
	sprintf(err, "Window too small: Lines %d < 40 | Columns %d < 140",
	        LINES, COLS);
	quit(err);
    }

    mlist_max = 2000;
    mlist = (unsigned short *) malloc (mlist_max * sizeof(unsigned short));

    for(i = 1; i < MAX_GAMES; i++)
	gm_strt[i] = -1;
    gm_strt[0] = 0;

    for (i = 8, y = D_BPOS_Y; i--; y++) {
	move(y, D_BPOS_X);
	addch('1' + i);
    }
    move(D_BPOS_Y + 9, D_BPOS_X + 3);
    addstr("a b c d e f g h");

    move(D_SPOS_Y, D_SPOS_X);
    addstr("Ex =");
    move(D_SPOS_Y + 1, D_SPOS_X);
    addstr("E0 =");
    move(D_SPOS_Y + 2, D_SPOS_X);
    addstr("E1 =");

    for (n_param = 0; All_params[n_param]; n_param++);
    if (n_param > MAX_NPRM)
	quit("Increase 'MAX_NPRM'");

    for (i = 0; i < 8; i++)	/* turn of material values by default */
	if (pc_params[i])
	    pc_params[i]->on = 0;

    for (k = (n_param + 3) / 4, i = 0; i < k; i++) {
	sprintf(buf, "%-19s%4s%c%4s=%4s", All_params[i]->name, sh_int(All_params[i]->init), All_params[i]->on ? '*' : '!',
		sh_int(All_params[i]->coef), sh_int(unscale(All_params[i]->init * All_params[i]->coef)));
	move(D_PPOS_Y + i, D_PPOS_X);
	addstr(buf);

	if (All_params[i + k]) {
	    sprintf(buf, "%-19s%4s%c%4s=%4s", All_params[i + k]->name, sh_int(All_params[i + k]->init), All_params[i + k]->on ? '*' : '!',
		    sh_int(All_params[i + k]->coef), sh_int(unscale(All_params[i + k]->init * All_params[i + k]->coef)));
	    move(D_PPOS_Y + i, D_PPOS_X + 35);
	    addstr(buf);
	}
	if (All_params[i + 2 * k]) {
	    sprintf(buf, "%-19s%4s%c%4s=%4s", All_params[i + 2 * k]->name, sh_int(All_params[i + 2 * k]->init), All_params[i + k * 2]->on ? '*' : '!',
		    sh_int(All_params[i + 2 * k]->coef), sh_int(unscale(All_params[i + 2 * k]->init * All_params[i + 2 * k]->coef)));
	    move(D_PPOS_Y + i, D_PPOS_X + 70);
	    addstr(buf);
	}
	if (All_params[i + 3 * k]) {
	    sprintf(buf, "%-19s%4s%c%4s=%4s", All_params[i + 3 * k]->name, sh_int(All_params[i + 3 * k]->init), All_params[i + k * 3]->on ? '*' : '!',
		    sh_int(All_params[i + 3 * k]->coef), sh_int(unscale(All_params[i + 3 * k]->init * All_params[i + 3 * k]->coef)));
	    move(D_PPOS_Y + i, D_PPOS_X + 105);
	    addstr(buf);
	}
    }
}

p_update ()				/* change the init-part of the parameter display */
{
    register int i, k;
    char buf[10];
    
    for (k = (n_param + 3) / 4, i = 0; i < k; i++) {
	sprintf(buf, "%4s%c", sh_int(All_params[i]->init),  All_params[i]->on ? '*' : '!');
	move(D_PPOS_Y + i, D_PPOS_X + 19);
	addstr(buf);

	if (All_params[i + k]) {
	    sprintf(buf, "%4s%c", sh_int(All_params[i + k]->init), All_params[i + k]->on ? '*' : '!');
	    move(D_PPOS_Y + i, D_PPOS_X + 54);
	    addstr(buf);
	}

	if (All_params[i + 2 * k]) {
	    sprintf(buf, "%4s%c", sh_int(All_params[i + 2 * k]->init), All_params[i + k * 2]->on ? '*' : '!');
	    move(D_PPOS_Y + i, D_PPOS_X + 89);
	    addstr(buf);
	}

	if (All_params[i + 3 * k]) {
	    sprintf(buf, "%4s%c", sh_int(All_params[i + 3 * k]->init), All_params[i + k * 3]->on ? '*' : '!');
	    move(D_PPOS_Y + i, D_PPOS_X + 124);
	    addstr(buf);
	}
    }
}

quit(s)					/* exit (if s!=0: this is an error exit) */
    char *s;
{
    clear();
    refresh();
    endwin();
    if (s)
	fprintf(stderr, "Error: %s\n", s);
    close(dbf);

    exit(0 != s);
}

err(s, i1, i2, i3, i4)			/* issue (or clear an error message) */
    char *s;
    int i1, i2, i3, i4;
{
    char            buf[200];
    move(D_EPOS_Y, D_EPOS_X);
    if (!s)
	clrtoeol();
    else {
	sprintf(buf, s, i1, i2, i3, i4);
	addstr(buf);
	clrtoeol();
	refresh();		/* display it immediately (clear can wait) */
    }
}

char *get_cmd(s)		/* prompt for command string */
    char *s;
{
    static int i = 0;
    static char     buf[4][80];

    move(D_EPOS_Y, D_EPOS_X);
    clrtoeol();
    move(D_EPOS_Y, D_EPOS_X);
    addstr(s);
    refresh();

    echo();
    i = (i + 1) % 4;
    getstr(&(buf[i][0]));
    noecho();

    return &(buf[i][0]);
}

game(n)					/* go to start of game <n>, reset all state to start of game, don't do anything else */
    int n;
{
    char t;
    register long i, j, depth, mli;
    int dc[MAX_DEPTH];
    
    if (n >= max_gm) {
	err("only %d games in file", max_gm, 0, 0, 0);
	n = max_gm - 1;
    }

    if (gm_strt[n] >= 0) {		/* Known start ? */
	lseek(dbf, gm_strt[n], L_SET);
	cur_gm = n;
    } else {
	if (n <= cur_gm)
	    quit("Access logic error");

	i = cur_gm;			/* skip forward as fast as possible */
	while (i < n && gm_strt[i + 1] >= 0)
	    i++;
	if (i != cur_gm) {
	    lseek(dbf, gm_strt[i], L_SET);
	    cur_gm = i;
	}

	j = 1;
	while (cur_gm < n) {	    /* scan forward */

	    if (1 != read(dbf, &t, sizeof(char))) { /* hit EOF */
		max_gm = cur_gm + 1;
		n = cur_gm;
		lseek(dbf, gm_strt[n], L_SET);
		err("only %d games in file", max_gm, 0, 0, 0);
		break;
	    }

	    if(t <= 0) {	    /* start of a game */
		if (!j) {	    /* != current */
		    gm_strt[++cur_gm] = lseek(dbf, 0l, L_INCR) - 1;
		    if (cur_gm == n) {
			lseek(dbf, gm_strt[cur_gm], L_SET);
			gm_type = -t;
			break;
		    }
		}
		t = N_OPEN;
	    }
	    j = 0;

	    depth = 0;
	    dc[0] = t;
	    do {
		if (t && 0 > lseek(dbf, t * sizeof(unsigned short), L_INCR))
		    quit ("game: Premature EOF");

		if ( 1 != read(dbf, &t, sizeof(char)))
		    quit ("game: Premature EOF");

		dc[depth]--;
		if (t) {
		    if(++depth >= MAX_DEPTH)
			quit("Tree depth (MAX_DEPTH) exceeded");
		    if(t < 0)
			quit("Database format error");
		    dc[depth] = t;
		} else {
		    while (depth >=0 && dc[depth] <= 0)
			depth--;
		}

	    } while (depth >= 0);
	}
    }

    lseek(dbf, 1l, L_INCR);
    t = N_OPEN;
    depth = 0;
    mli = 1;
    dc[0] = t;
    mlist[0] = t;
    j = 0;

    do {
	if ((mli + t + 1) >= mlist_max) {	/* need more space */
	    mlist_max += mlist_max / 2;
	    mlist = (unsigned short *) realloc(mlist, mlist_max * sizeof(unsigned short));
	    if (!mlist)
		quit("Out of memory");
	}

	i = t * sizeof(unsigned short);
	if (i && i != read(dbf, &(mlist[mli]), i))
	    quit("game: read error (Premature EOF)");
	SWAPP_BYTES(&(mlist[mli]), (int) t);

	mli += t;

	if (1 != read(dbf, &t, sizeof(char)))
	    quit("game: read error (Premature EOF)");

	if (depth == 0)
	    st_ind[j++] = mli;			/* remember where each subtree starts */

	mlist[mli++] = t;

	dc[depth]--;
	if (t) {
	    if(++depth >= MAX_DEPTH)
		quit("Tree depth (MAX_DEPTH) exceeded");
	    if(t < 0)
		quit("game: database format error");
	    dc[depth] = t;
	} else {
	    while (depth >=0 && dc[depth] <= 0)
		depth--;
	}

    } while (depth >= 0);
    
    ml_cnt = 0;
    ml_max = N_OPEN;
    for (i = 64; i--; backup[i] = sunboard[i] = init_pos[i]);
    *fit_castle_st = back_cst = sun_cst = WSCastle | WLCastle | BSCastle | BLCastle;
    stm = 0;
    mvs_cnt = 1;
    capture = EMPTY;
    FIT_cast = -1;
}

next()	    /* apply next possible move or return 0 */
{
    register int i = 64, *sb = backup, *p;
    
    if (ml_cnt >= ml_max)
	return 0;

    if (ml_cnt) {
	for (p = sunboard; i--; *sb++ = *p++);
	*fit_castle_st = sun_cst;
	b_update(mlist[++ml_cnt], stm);
	back_cst = *fit_castle_st;
    } else {
	b_update(mlist[1], stm);
	for (p = new_pos; i--; *p++ = *sb++);
	new_cst = back_cst = *fit_castle_st;
	ml_cnt = 1;
    }

    return 1;
}

Next()	   /* read next move tree and reset board to the root position of that tree */
	   /* note: file pointer must be positioned at first byte of tree */
{
    register int j, *np = new_pos, *bp = sunboard, *sb = backup, depth, mli;
    register long i;
    int dc[MAX_DEPTH];
    char t;
    
    if (!ml_cnt)		/* need at least one move */
	next();

    if (1 != read(dbf, &t, sizeof(char))) {
	max_gm = cur_gm + 1;
	return 0;		/* end of game/file */
    }

    if (t <= 0) {
	i = lseek(dbf, -1l, L_INCR);
	if (cur_gm + 1 < max_gm || gm_strt[cur_gm + 1] < 0)
	    gm_strt[cur_gm + 1] = i;
	return 0;		/* end of game */
    }

    depth = 0;
    mli = 1;
    dc[0] = t;
    mlist[0] = t;
    j = 0;
    
    do {
	if ((mli + t + 1) >= mlist_max) {	/* need more space */
	    mlist_max += mlist_max / 2;
	    mlist = (unsigned short *) realloc(mlist, mlist_max * sizeof(unsigned short));
	    if (!mlist)
		quit("Out of memory");
	}

	i = t * sizeof(unsigned short);
	if (i && i != read(dbf, &(mlist[mli]), i))
	    quit("Next: read error (Premature EOF)");
	SWAPP_BYTES(&(mlist[mli]), t);

	mli += t;

	if (1 != read(dbf, &t, sizeof(char)))
	    quit("Next: read error (Premature EOF)");

	if (depth == 0)
	    st_ind[j++] = mli;			/* remember where each sub tree starts */

	mlist[mli++] = t;

	dc[depth]--;
	if (t) {
	    if(++depth >= MAX_DEPTH)
		quit("Tree depth (MAX_DEPTH) exceeded");
	    if(t < 0)
		quit("Database format error");
	    dc[depth] = t;
	} else {
	    while (depth >=0 && dc[depth] <= 0)
		depth--;
	}

    } while (depth >= 0);

    ml_cnt = 0;
    ml_max = mlist[0];

    for (i = 64; i--;) {
	j = *np++;
	*sb++ = j;
	*bp++ = j;
    }
    j = new_cst;
    back_cst = j;
    *fit_castle_st = j;
    mvs_cnt += stm;
    stm ^= 1;

    return 1;
}

get_number(min, max, dflt, prompt)
    int min, max, dflt;
    char *prompt;
{
    register char *s;
    int i;

    do {
	s = get_cmd(prompt);
	if (!s[0]) {
	    i = dflt;
	    break;
	}
    } while (1 != sscanf(s, "%d", &i) || i < min || i > max);

    return i;
}

get_config()
{
    conf_def = 1;			 /* got a configuration */
    
    from = get_number(0, max_gm - 1, 0, "Start with game #:");
    to = get_number(from, max_gm - 1, max_gm - 1, "End with game #:");
    a_weight = get_number(0, 100, 5, "A-measure weight [%]:");
    i_skip = get_number(0, 100, 5, "# of initial moves to be skiped:");
    
    efunc = get_number(0, 4, 4, "Error weight function [0=log, 1=sqrt, 2=linear, 3=step, 4=inverse]:");
    wfunc = get_number(-1000, 1000, 100, "Multiple error weighting [<0: deemphasize,  0: even,  >0: emphasize]:");
    min_g = get_number(-1000, 1000, 50, "Force factor [100 = 1 pawn, >0 improves]:");
    vari_c = get_number(-1000, 1000, 0, "Variance compensation [0 = none, 100 = neutral]:");
}

static int C[MAX_NMVS][MAX_NPRM];

do_fit()
{
    struct fit_param *on[MAX_NPRM], *off[MAX_NPRM];
    int	Cx[MAX_NPRM], C0[MAX_NPRM];
    int Kx, K0, K[MAX_NMVS], E[MAX_NMVS];	/* constant part to the ev. funct */
    int n_mvs;
    register int i, j, k, *p;
    register struct fit_param **f;

#ifdef V_TRACE
    int tfd, T[2];
    if (0 > (tfd = open("q", O_WRONLY | O_CREAT, 0644)))
	quit("failed to open trace file");
#endif

    for(i=0, j=0, k=0; All_params[i]; i++) {	/* get participating parameters */
	All_params[i]->used = 0;
	if (All_params[i]->on)
	    on[j++] = All_params[i];
	else
	    off[k++] = All_params[i];
    }
    n_on = j;
    n_off = k;

    init_math();		/* reset arrays, etc */

    for(i = from; i <= to; i++) {
	if (i >= max_gm) {	/* hit end of game file: update upper limit */
	    to = max_gm - 1;
	    break;
	}
	game(i);
	err("*** Busy on game %d ***", i, 0, 0, 0);

	do{			/* loop through game */

	    if (mvs_cnt <= i_skip)			    /* skip initial moves */
		continue;

	    Ex = EVAL();

	    for(j = n_on, p = Cx, f = on; j--; f++) (*f)->used |= 0 != (*p++ = (*f)->coef);
	    for(j = n_off, f = off, Kx = fit_const; j--; f++) Kx += (*f)->coef * (*f)->init;

	    next();

	    E0 = t_EVAL();

	    for(j = n_on, p = C0, f = on; j--; f++) (*f)->used |= 0 != (*p++ = (*f)->coef);
	    for(j = n_off, f = off, K0 = fit_const; j--; f++) K0 += (*f)->coef * (*f)->init;

	    j = Ex - E0;
	    if (j < 0)
		j = -j;
	    if (j <= SCALEF * 64)
	        do_A_term(Cx, Kx, C0, K0);	/* only if values are within 1/2 pawn */

	    n_err = 0;
	    n_mvs = 0;

	    while(next()) {
		E[n_mvs] = k = t_EVAL();

		j = E0 - k;
		if (j < 0)
		    j = -j;
		if (j > SCALEF * 64) {		/* value is off by more than 1/2 pawn: skip */
		    n_kill++;
		    continue;
		}

#ifdef V_TRACE
		T[0] = E0;
		T[1] = (!stm) ? k : 2*E0 - k;
		write(tfd, T, 2 * sizeof(int));
#endif
		if (stm) n_err += E0 >= k;	/* black */
		else     n_err += E0 <= k;	/* white */
		
		for(j = n_on, p = &(C[n_mvs][0]), f = on; j--; f++) (*f)->used |= 0 != (*p++ = (*f)->coef);
		for(j = n_off, f = off, k = fit_const; j--; f++) k += (*f)->coef * (*f)->init;
		K[n_mvs] =k;
		n_mvs++;
	    }

	    for (j = n_mvs; j--;)
		do_B_term(C0, K0, &(C[j][0]), K[j], E[j], n_err);

	    if (n_mvs) {			/* skip trivial cases */
		tot_pos += n_mvs;		/* statistics */
		ok_pos += n_mvs - n_err;
		tot_mvs++;
		ok_mvs += !n_err;
		if(n_err >= MAX_NMVS) ++hist[MAX_NMVS - 1];
		else                  ++hist[n_err];
	    }

	} while (Next());

    }

#ifdef V_TRACE
    close(tfd);
#endif

    do_math(on);			/* solve the system... */
}

init_math()				/* initialize arrays */
{
    register int    i, j;

    for (i = n_on; i--;) {
	for (j = n_on; j--;)
	    A[i][j] = 0.0;
	D[i] = 0.0;
    }

    for(i = 0; i < MAX_NMVS; i++)
	hist[i] = 0;

    A_wght = 0.01 * a_weight;
    B_wght = 1.0 - A_wght;
    tot_pos = 0;
    tot_mvs = 0;
    ok_pos = 0;
    ok_mvs = 0;
    A_err = 0.0;
    B_err = 0.0;
    n_kill = 0;

    n_cor = (n_cor + 50) / 100;		/* carry over 1% of the previous average (to improve precision at start) */
    acc_cor *= 0.01;
}

do_math(on)			/* solve the system */
    struct fit_param *on[];
{
    register int    i;
    double sqrt();

    A_err = sqrt(A_err / tot_mvs) * 3.0518e-5;
    B_err = sqrt(B_err / tot_pos) * 3.0518e-5;

    if (lin_equ(n_on, A, X, D)) {
	err("Sorry, singular system: some parameters were never used - no fit possible", 0, 0, 0, 0);
	for (i = 0; i < n_on; i++)
	    on[i]->on = on[i]->used;	/* turn off all zero's */
	return;
    } else
	err("Done!", 0, 0, 0, 0);

    for (i = 0; i < n_on; i++)
	on[i]->init = rint(X[i]);
}

disp_fit (s)			/* show results */
    char *s;
{
    char            buf[120];

    sprintf(buf, "%sfit: games=%d-%d  force=%d  EF=%s  ME=%d  A-weight=%d%%  move-skip=%d  VC=%d", s,
		from, to, min_g, ef[efunc], wfunc, a_weight, i_skip, vari_c);
    move(D_FPOS_Y, D_FPOS_X);
    addstr(buf);
    clrtoeol();

    sprintf(buf, "Old set:  Positions ok= %d/%d (%.0f%%)  Moves ok= %d/%d (%.0f%%)",
	    ok_pos, tot_pos, 100.0 * ok_pos / tot_pos, ok_mvs, tot_mvs, 100.0 * ok_mvs / tot_mvs);
    move(D_FPOS_Y + 1, D_FPOS_X);
    addstr(buf);
    clrtoeol();

    sprintf(buf, "A-error= %10.4g  B-error=%10.4g [100 = 1pawn/move]  out-of-bound positions=%d", A_err, B_err, n_kill);
    move(D_FPOS_Y + 2, D_FPOS_X + 10);
    addstr(buf);
    clrtoeol();

    p_update();
}

p_save(i)
    int i;
{
    register int j;

    for(j = n_param; j--;)
	psa[i][j] = All_params[j]->init;

    for(j = MAX_NMVS; j--;)
	hist_sa[i][j] = hist[j];
}

p_Save(s, m, tbl)
    char *s, *m;
    int tbl;
{
    register int i, j, i1, r1, i2, r2;
    int i3, r3, i4, r4;
    FILE *ofd;
    struct tab_descr *tptr;
    register short *dp;

    if(!(ofd = fopen(s, "w"))) {
	err("Failed to open file", 0, 0, 0, 0);
	return;
    }

    fprintf(ofd, "@M %s\n", m);
    if(conf_def)
	fprintf(ofd, "@C %d %d %d %d %d %d %d %d\n", from, to, a_weight, min_g, efunc, wfunc, i_skip, vari_c);
	
    for(j = n_param; j--;) {
	fprintf(ofd, "%s %s %d\n", All_params[j]->name, All_params[j]->on ? "on" : "off", All_params[j]->init);
	if (All_params[j]->table && (tbl || par_ptr == All_params[j])) {
	    tptr = All_params[j]->tp;
	    dp = tptr->data;
	    fprintf(ofd, "@T %d %d %d %d %d\n", tptr->dim, r1 = tptr->rng[0], r2 = tptr->rng[1], r3 = tptr->rng[2], r4 = tptr->rng[3]);
	    for(i4 = 0; i4 < r4; i4++)
		for(i3 = 0; i3 < r3; i3++)
		    for(i2 = 0; i2 < r2; i2++) {
			i = i2 * r1 + i3 * r2 *r1 + i4 * r3 * r2 * r1;
			fprintf(ofd, "@D");
			for(i1 = 0; i1 < r1; i1++)
			    fprintf(ofd, " %d", dp[i1 + i]);
			fprintf(ofd, "\n");
		    }
	}
    }

    fclose(ofd);
    
    err(0, 0, 0, 0, 0);
    refresh();
}

p_restore(i)
    int i;
{
    register int j;

    for(j = n_param; j--;)
	All_params[j]->init = psa[i][j];
}

p_Restore(s)
    char *s;
{
    register int i, j, i1, i2, i3, i4;
    int d, r1, r2, r3, r4;
    FILE *ifd;
    char buf[200], pname[200], pon[200];
    int pi, e = 0;
    struct tab_descr *tptr;
    register short *dp;

    if(!(ifd = fopen(s, "r"))) {
	err("Failed to open file", 0, 0, 0, 0);
	return 1;
    }

    while (fgets(buf, 200, ifd)) {
	if(buf[0] == '@') {
	    if (buf[1] == 'M')
		err(&buf[2], 0, 0, 0, 0);
	    else if (buf[1] == 'C') {
		if(8 == sscanf(&buf[2], "%d%d%d%d%d%d%d%d", &from, &to, &a_weight, &min_g, &efunc, &wfunc, &i_skip, &vari_c))
		    conf_def = 1;
		else {
		    err("Invalid configuration", 0, 0, 0, 0);
		    conf_def = 0;
		    e = 1;
		}
	    } else if (buf[1] == 'T') {		    /* read a table */
		if (i >= n_param || !(All_params[i]->table)) {
		    err ("Error: Not a table", 0, 0, 0, 0);
		} else {
		    tptr = All_params[i]->tp;
		    if (5 != sscanf(&(buf[2]), "%d%d%d%d%d", &d, &r1, &r2, &r3, &r4) || d != tptr->dim ||
			r1 != tptr->rng[0] || r2 != tptr->rng[1] || r3 != tptr->rng[2] || r4 != tptr->rng[3]) {
			err("Error: Table params are different", 0, 0, 0, 0);
			continue;
		    }
		    dp = tptr->data;
		    for(i4 = 0; i4 < r4; i4++)
			for(i3 = 0; i3 < r3; i3++)
			    for(i2 = 0; i2 < r2; i2++) {
				j = i2 * r1 + i3 * r2 *r1 + i4 * r3 * r2 * r1;
				if(1 != fscanf(ifd, "%s", buf) || strcmp("@D", buf)) {
				    err ("Error: missing data", 0, 0, 0, 0);
				    goto e_exit;
				}
				for(i1 = 0; i1 < r1; i1++) {
				    if(1 != fscanf(ifd, "%d", &d)) {
					err("Error: missing data", 0, 0, 0, 0);
					goto e_exit;
				    }
				    dp[i1 + j] = d;
				}
			    }

		    e_exit:
			fgets(buf, 200, ifd);		/* there is an outstanding '/n' */
		}
	    }
	} else {
	    if(3 != sscanf(buf, "%s%s%d", pname, pon, &pi))
		err("Invalid format or missing field", 0, 0, 0, 0);
	    else {
		for(i = 0; i < n_param; i++)
		    if (!strcmp(All_params[i]->name, pname))
			break;
		if (i >= n_param){
		    e = 1;
		    err("Unknown parameter", 0, 0, 0, 0);
		} else {
		    All_params[i]->init = pi;
		    All_params[i]->on = !strcmp(pon, "on");
		}
	    }
	}
    }

    fclose(ifd);
    return e;
}

lin_equ (n, A, X, D)
/******************************************************************\
* 								   *
*  Linear equation solver (Gaussian elimiation with pivot search)  *
* 								   *
*  solves    SUM(i=0..n)  A[j][i] * X[i] = D[j]  		   *
* 								   *
\******************************************************************/
    int n;
    double (*A)[MAX_NPRM], *X, *D;
{
    register int i, j, k;
    register double  t, p;
    double  fabs ();

    for (i = 0; i < n; ++i) {
	k = i;
	p = fabs (A[k][i]);
	for (j = i + 1; j < n; ++j) {/* find pivot		 */
	    if ((t = fabs (A[j][i])) > p) {
		k = j;
		p = t;
	    }
	}

	if (p < 1.0e-12)	/* check for singularity	 */
	    return 1;		/* no solution			 */

	if (k != i) {
	    for (j = i; j < n; ++j) {
		t = A[k][j];
		A[k][j] = A[i][j];
		A[i][j] = t;
	    }
	    t = D[k];
	    D[k] = D[i];
	    D[i] = t;
	}

	for (j = i + 1; j < n; ++j) {/* elimination		 */
	    p = A[j][i] / A[i][i];
	    for (k = i + 1; k < n; ++k)
		A[j][k] -= p * A[i][k];
	    D[j] -= p * D[i];
	}
    }

    for (i = n - 1; i >= 0; --i) {/* roll back			 */
	p = D[i];
	for (j = i + 1; j < n; ++j)
	    p -= X[j] * A[i][j];
	X[i] = p / A[i][i];
    }

    return 0;			/* successfull run */
}


add_vec(iv, y, w)
    int y, *iv;
    double w;
{
    register int i, j, x, t;
    register int *p1, *p2;
    register double *fp, rw;

    for (i = 0, p1 = iv; i < n_on; i++) {
	if (!(x = *p1++))
	    continue;

	rw = w * x;
	D[i] += y * rw;

	for (j = n_on, p2 = iv, fp = &(A[i][0]); j--; fp++) {
	    if (!(t = *p2++))
		continue;
	    *fp += t * rw;
	}
    }
}

do_A_term(Cx, Kx, C0, K0)			/* process the (Ex-E0)^2 part */
    int Kx, K0, *C0, *Cx;
{    
    int t[MAX_NPRM];
    register int i, *p1, *p2, *p3;
    register double e;

    e = Ex - E0;
    A_err += e * e;

    for (i = n_on, p1 = Cx, p2 = C0, p3 = t; i--;)
	*p3++ = *p1++ - *p2++;

    add_vec (t, K0 - Kx, A_wght);		/* keep Ex and E0 similar term */
/*  add_vec (t, (K0-E0) - (Kx-Ex), A_wght);	/* inertia term: keep things as they are */
}

do_B_term(C0, K0, Cn, Kn, E_n, ne)		/* process the (E0 - En - gn)^2 part */
    int *C0, K0, *Cn, Kn, E_n, ne;
{
    
    int t[MAX_NPRM];
    register int i, g, *p1, *p2, *p3;
    register double e, w = 1.0;
    double log(), sqrt();

#ifndef OLD_ME
    if (ne) {					/* there are ne > 0 moves ahead of the GM one */
	w = wfunc * 0.01 / ne;
    }
#endif


    g = E_n - E0;				/* assume no change desired */

    /*
     * increasing g will cause a force towards  E0 < En
     * decreasing g will cause a force towards  E0 > En
     *
     */


    if ((stm  && E_n < E0) ||		    /* black to move */
        (!stm && E_n > E0)   ) {	    /* white to move */

	e = (stm) ? E0 - E_n : E_n - E0;
	B_err += e * e;

	/* "i" is the correcting force as a function of the absolute error "e" */
        /* i > 0 will cause the E0 be evaluated "better" than En */

	/* Normalization: a min_g of 100 will cause a force of 1 pawn for 1 pawn difference in e */

	switch (efunc) {
	    case 0:
		i = rint(min_g * (1.0 + 31.42 * log(1.0 + e)));	    /* log */
		break;

	    case 1:
		i = rint(min_g * 1.810 * sqrt(1.0 + e));	    /* Square root */
		break;

	    case 2:
		i = rint(min_g * 0.01 * (1.0 + e));		    /* linear */
		break;

	    case 4:						    /* inverse */
		i = rint(min_g * 327.68 * 50000.0 /(50000.0 + e));
		break;

	    default:
		i = rint(min_g * 327.68);			    /* Step function */
		break;
	}

	if (stm) g += i;
	else     g -= i;

	acc_cor += i;

#ifdef OLD_ME
	if (wfunc > 0)				/* emphazize multiple errors */
	    w += 0.01 * wfunc * (ne - 1);
	else if (wfunc < 0)			/* de-emphazize multiple errors */
	    w += 0.01 * wfunc * (ne - 1.0) / ne;
#endif

    } else {
	
	n_cor++;
	i = rint(vari_c * 0.01 * acc_cor / n_cor);

	if (stm) g += i;
	else     g -= i;
    }

    for (i = n_on, p1 = C0, p2 = Cn, p3 = t; i--;)
	*p3++ = *p1++ - *p2++;

    add_vec (t, (Kn - K0) - g, B_wght * w);		/* make E0 better than En term */
/*  add_vec (t, (Kn - K0) - (E_n - E0), B_wght * w);	/* innertia term: keep things as they are */
}

p_edit()			/* edit or change parameters */
{
    register int i, k;
    int j;
    char t, buf[160], *p;
    
    i = 0;

    err("p-edit cmds: <=frst  >=lst  b=bckw  f=forw  A-Z=find match  e=exit   to change: +=on  -=off  0-9=new value  *=all on  !=all off",
	 0, 0, 0, 0);
    t = getch();

    do {
	if (!t) {
	    sprintf(buf, "%s (%s) = %d : ", All_params[i]->name, All_params[i]->on ? "on" : "off", All_params[i]->init);
	    err(buf, 0, 0, 0, 0);
	    t = getch();
	}
	switch (t) {
	    case '+':
		All_params[i]->on = 1;
		t = 0;
		break;

	    case '-':
		All_params[i]->on = 0;
		t = 0;
		break;

	    case '*':
		for(j = 0; j < n_param; j++)
		    All_params[j]->on = 1;
		p_update();
		t = 0;
		break;

	    case '!':
		for(j = 0; j < n_param; j++)
		    All_params[j]->on = 0;
		p_update();
		t = 0;
		break;

	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
		sprintf(buf, "%s (%s) = %d <- %c", All_params[i]->name, All_params[i]->on ? "on" : "off", All_params[i]->init, t);
		p = get_cmd(buf);
		buf[0] = t;
		strcpy(&buf[1], p);
		if (1 == sscanf(buf, "%d", &j)) {
		    All_params[i]->init = j;
		};
		t = 0;
		break;

	    case 'e':
		err(0, 0, 0, 0, 0);
		refresh();
		return;

	    case '<':
		i = 0;
		t = 0;
		break;

	    case '>':
		i = n_param - 1;
		t = 0;
		break;

	    case 'f':
		i++;
		if (i >= n_param)
		    i = n_param - 1;
		t = 0;
		break;

	    case 'b':
		i--;
		if (i < 0)
		    i = 0;
		t = 0;
		break;

	    default:
		sprintf(buf, "Find: %c", t);
		p = get_cmd(buf);
		buf[0] = t;
		strcpy(&buf[1], p);
		k = strlen(buf);
		for(j = 0; j < n_param; j++)
		    if (!strncmp(buf, All_params[j]->name, k))
			break;
		t = 0;
		if(j < n_param)
		    i = j;
		else {
		    err("No Match", 0, 0, 0, 0);
		    t = getch();
		}
		break;
	}

	k = (n_param + 3) / 4;
	sprintf(buf, "%4d%c", All_params[i]->init,  All_params[i]->on ? '*' : '!');
	move(D_PPOS_Y + i % k, D_PPOS_X + 19 + (i / k) * 35);
	addstr(buf);

    } while(1);
}

p_write(p)			/* write coefficients to a file */
    char *p;
{
    FILE *ofd, *fopen();
    register int i, j;

    if (!(ofd = fopen(p, "w"))) {
	err ("Failed to open file", 0, 0, 0, 0);
    } else {
	for (i = 0; i < n_param; i++) {
	    if (All_params[i]->table) {
		fprintf(ofd, "decl_%dtab (%s, %d", All_params[i]->tp->dim, All_params[i]->name, All_params[i]->init);
		for (j = 0; j < All_params[i]->tp->dim; j++)
		    fprintf(ofd, ", %d", All_params[i]->tp->rng[j]);
		for (j = 0; j < All_params[i]->tp->dim; j++)
		    fprintf(ofd, ", %d", All_params[i]->tp->deg[j]);
		fprintf(ofd, ") = {\n\t");
		tbl_lst (ofd, All_params[i]->tp->data, All_params[i]->tp->dim, All_params[i]->tp->rng);
		fprintf(ofd, "};\n");
	    } else {
		fprintf(ofd, "decl_param (%s, %d);\n", All_params[i]->name, All_params[i]->init);
	    }
	}
	    
	fclose(ofd);
	err(0, 0, 0, 0, 0);
	refresh();
    }
}

tbl_lst (fp, sp, dim, rng)
    FILE *fp;
    short *sp;
    int dim, rng[];
{
    register int i, j, k;
    
    if (dim <= 1) {
	fprintf(fp, "%d", *sp++);
	for (i = 1; i < rng[0]; i++)
	    fprintf(fp, ", %d", *sp++);
    } else {
	for (k = 1, i = dim - 1; i--; k *= rng[i]);
	for (i = 0; i < rng[dim - 1]; i++) {
	    if (i)
		fprintf(fp, ",\n\t");
	    for (j = 1; j < dim; j++)
		fprintf(fp, "\t");
	    fprintf(fp, "{");
	    tbl_lst(fp, sp + i * k, dim - 1, rng);
	    fprintf(fp, "}");
	}
    }
}

do_long_fit(n, fn)			/* Fit iteration */
    int n;
    char *fn;
{
    char buf[200], fname[100];
    int i, j, POK[MAX_PREGS], MOK[MAX_PREGS], OOBP[MAX_PREGS];
    double A[MAX_PREGS], B[MAX_PREGS];
    FILE *ofd;

    par_ptr = 0;
    tbl_ptr = 0;

    sprintf(fname, "%s_p0.par", fn);
    p_Save(fname, "Initial configuration", 0);
    p_save(0);

    for (i = 0; i < n; i++) {
	do_fit();
	sprintf(fname, "%s_p%d.par", fn, i + 1);
	sprintf(buf, "pass%d: POK=%d/%d (%.0f%%) MOK=%d/%d (%.0f%%) Aerr=%10.4g Berr=%10.4g OOB-POS=%d", i + 1,
		ok_pos, tot_pos, 100.0 * ok_pos / tot_pos, ok_mvs, tot_mvs, 100.0 * ok_mvs / tot_mvs, A_err, B_err, n_kill);
	p_Save(fname, buf, 0);
	p_save(i + 1);
	POK[i] = ok_pos;
	MOK[i] = ok_mvs;
	OOBP[i] = n_kill;
	A[i] = A_err;
	B[i] = B_err;
    }

    sprintf(fname, "%s.stat", fn);
    if (!(ofd = fopen(fname, "w"))) {
	err("Failed to open statistics file", 0, 0, 0, 0);
	return;
    }
    fprintf(ofd, "Summary for a fit %d pass fit run:\n", n);
    fprintf(ofd, "    games=%d-%d  force=%d  EF=%s  ME=%d  A-weight=%d%%  move-skip=%d VC=%d\n",
	from, to, min_g, ef[efunc], wfunc, a_weight, i_skip, vari_c);
    fprintf(ofd, "    OK Posistions out of %d:", tot_pos);
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %d (%.0f%%)", POK[i], 100.0 * POK[i] / tot_pos);
    fprintf(ofd, "\n    OK Moves out of %d:", tot_mvs);
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %d (%.0f%%)", MOK[i], 100.0 * MOK[i] / tot_mvs);
    fprintf(ofd, "\n    A-Error:");
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %10.4g", A[i]);
    fprintf(ofd, "\n    B-Error:");
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %10.4g", B[i]);
    fprintf(ofd, "\n    OOB-POS:");
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %d", OOBP[i]);
    fprintf(ofd, "\nError histograms:\n");

    for(i = 0; i < MAX_NMVS; i++) {
	for(j = 1; j <= n; j++)
	    if(hist_sa[j][i])
		break;
	if (j <= n) {
	    fprintf(ofd, "%3d :", i);
	    for(j = 1; j <= n; j++)
		fprintf(ofd, " %4d", hist_sa[j][i]);
	    fprintf(ofd, "\n");
	}
    }

    fprintf(ofd, "\n");
    for (i = 0; i < n_param; i++) {
	fprintf(ofd, "%s %d", All_params[i]->name, psa[0][i]);
	if (All_params[i]->on) {
	    for (j = 1; j <= n; j++)
		fprintf(ofd, " %d", psa[j][i]);
	}
	fprintf(ofd, "\n");
    }

    fclose(ofd);
}

do_long_T_fit(n, fn)			/* Fit iteration */
    int n;
    char *fn;
{
    char buf[200], fname[100];
    int i, j, POK[MAX_PREGS], MOK[MAX_PREGS], OOBP[MAX_PREGS];
    double A[MAX_PREGS], B[MAX_PREGS];
    FILE *ofd;

    sprintf(fname, "%s_p0.par", fn);
    p_Save(fname, "Initial configuration", 0);
    p_save(0);

    for (i = 0; i < n; i++) {
	do_T_fit();
	sprintf(fname, "%s_p%d.par", fn, i + 1);
	sprintf(buf, "pass%d: POK=%d/%d (%.0f%%) MOK=%d/%d (%.0f%%) Aerr=%10.4g Berr=%10.4g OOB-POS=%d", i + 1,
		ok_pos, tot_pos, 100.0 * ok_pos / tot_pos, ok_mvs, tot_mvs, 100.0 * ok_mvs / tot_mvs, A_err, B_err, n_kill);
	p_Save(fname, buf, 0);
	p_save(i + 1);
	POK[i] = ok_pos;
	MOK[i] = ok_mvs;
	OOBP[i] = n_kill;
	A[i] = A_err;
	B[i] = B_err;
    }

    sprintf(fname, "%s.h", fn);
    p_write(fname);

    sprintf(fname, "%s.stat", fn);
    if (!(ofd = fopen(fname, "w"))) {
	err("Failed to open statistics file", 0, 0, 0, 0);
	return;
    }
    fprintf(ofd, "Summary for a Table-fit run for '%s', %d passes:\n", par_ptr->name, n);
    fprintf(ofd, "    games=%d-%d  force=%d  EF=%s  ME=%d  A-weight=%d%%  move-skip=%d VC=%d\n",
	from, to, min_g, ef[efunc], wfunc, a_weight, i_skip, vari_c);
    fprintf(ofd, "    OK Posistions out of %d:", tot_pos);
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %d (%.0f%%)", POK[i], 100.0 * POK[i] / tot_pos);
    fprintf(ofd, "\n    OK Moves out of %d:", tot_mvs);
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %d (%.0f%%)", MOK[i], 100.0 * MOK[i] / tot_mvs);
    fprintf(ofd, "\n    A-Error:");
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %10.4g", A[i]);
    fprintf(ofd, "\n    B-Error:");
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %10.4g", B[i]);
    fprintf(ofd, "\n    OOB-POS:");
    for (i = 0; i < n; i++)
	fprintf(ofd, "  %d", OOBP[i]);
    fprintf(ofd, "\nError histograms:\n");

    for(i = 0; i < MAX_NMVS; i++) {
	for(j = 1; j <= n; j++)
	    if(hist_sa[j][i])
		break;
	if (j <= n) {
	    fprintf(ofd, "%3d :", i);
	    for(j = 1; j <= n; j++)
		fprintf(ofd, " %4d", hist_sa[j][i]);
	    fprintf(ofd, "\n");
	}
    }

    fprintf(ofd, "\n");
    for (i = 0; i < n_param; i++) {
	fprintf(ofd, "%s %d", All_params[i]->name, psa[0][i]);
	if (All_params[i] == par_ptr) {
	    for (j = 1; j <= n; j++)
		fprintf(ofd, " %d", psa[j][i]);
	}
	fprintf(ofd, "\n");
    }

    fclose(ofd);
}

#ifdef KEEP_PV
static int pv_depth[MAX_DEPTH];
static char pv[MAX_DEPTH][20];
#endif

static int ml_cind;	    /* consumer index ... */

#ifdef T_DEBUG
static FILE *qtst = 0;
#endif

t_EVAL()		    /* complete min/max evaluation of current position */
			    /* is is assumed that "next" was used to set-up back[0] and backup[0] */
{
    int px[MAX_NPRM];
    register int i, j, *p1;
    register struct fit_param **pp;

#ifdef KEEP_PV
    strcpy(&(pv[0][0]), m_dec(mlist[ml_cnt], sunboard, stm));
#endif

#ifdef T_DEBUG
    fprintf(qtst, ">> %s (%d/%d):\n", m_dec(mlist[ml_cnt], sunboard, stm), ml_cnt,  ml_max);
#endif

    ml_cind = st_ind[ml_cnt - 1];
    j = t_eval(0, stm, px);

    for(i = n_param, pp = All_params, p1 = px; i--; (*pp++)->coef = *p1++);

#ifdef T_DEBUG
    fprintf(qtst, "<< %s = %d\n", m_dec(mlist[ml_cnt], sunboard, stm), unscale(j));
#endif

    return j;
}

t_eval (depth, min, par)
    int depth, min, *par;
{
    int board[64], px[MAX_NPRM], cst, EV, ev, i, j;
    register int k, m, *p1, *p2;
    register struct fit_param **pp;

    if (!(j = mlist[ml_cind++])) {	/* # of moves on this level */
	k = EVAL();
	for(m = n_param, pp = All_params, p1 = par; m--; *p1++ = (*pp++)->coef);

#ifdef KEEP_PV
	pv_depth[depth] = depth;
#endif

#ifdef T_DEBUG
	for(m = 0; m <= depth; m++)
	    fprintf(qtst, "    ");
	fprintf(qtst, "TV = %d\n", unscale(k));
#endif

	return k;		/* leave node: just return value */
    }

    cst = back_cst;		/* save position */
    for (m = 64, p1 = board, p2 = backup; m--; *p1++ = *p2++);

    depth++;
    min ^= 1;

    for (i = 0, k = ml_cind, ml_cind += j; i < j; i++, k++) {
	b_update(mlist[k], min);
	back_cst = *fit_castle_st;

	ev = t_eval(depth, min, px);
	if (!i || (min && ev < EV) || (!min && ev > EV)) {
	    EV = ev;
	    for(m = n_param, p2 = px, p1 = par; m--; *p1++ = *p2++);
#ifdef KEEP_PV
	    strcpy(&(pv[depth][0]), m_dec(mlist[k], board, min));
	    pv_depth[depth - 1]	= pv_depth[depth];
#endif
	}

#ifdef T_DEBUG
	for(m = 0; m <= depth; m++)
	    fprintf(qtst, "    ");
	fprintf(qtst, "%s = %d (%d)\n", m_dec(mlist[k], board, min), unscale(ev), unscale(EV));
#endif

	back_cst = cst;
	*fit_castle_st = cst;
	for (m = 64, p1 = board, p2 = backup; m--; *p2++ = *p1++);
    }

    return EV;
}



#ifdef TE_DEBUG

test ()
{
    FILE *ftst;
    register int i, j, k;

    ftst = fopen("q", "w");
#ifdef T_DEBUG
    qtst = fopen("qq", "w");
#endif
    for (i = 0; i <= 1; i++) {
	game(i);
	fprintf(ftst, ">>> game %d:\n", i);
	j = 0;
	do {
#ifdef T_DEBUG
	    fprintf(qtst, "---------- game=%d move=%d side=%d\n", i, mvs_cnt, stm);
#endif
	    next();
	    k = t_EVAL(); 
/*	    k = T_EVAL(); */
	    fprintf(ftst, "%-2d: %4d", ++j, unscale(k));
	    for (k = 0; k <= pv_depth[0]; k++)
		fprintf(ftst, " %s", &(pv[k][0]));
	    fprintf(ftst, "\n    ");
	    while(next()) {
		k = t_EVAL();
		fprintf(ftst, "%s=%d ", m_dec(mlist[ml_cnt], sunboard, stm), unscale(k));
	    }
	    fprintf(ftst, "\n");
	    
	} while (Next());
    }

    fclose(ftst);
#ifdef T_DEBUG
    fclose(qtst);
#endif
}
#endif


/*====================================================================================================*/
/*				Table fitting code follows					      */
/*====================================================================================================*/

static float FPx[MAX_NPRM], FP0[MAX_NPRM], FP[MAX_NMVS][MAX_NPRM];
static float *tbl_fpv;

static int *use_cnt = 0;

#ifdef TF_DEBUG
FILE *qtst;
#endif

init_T_math()				/* initialize arrays */
{
    register int    i, j;

    if (use_cnt)
	free(use_cnt);

    i = tbl_ptr->rng[0] * tbl_ptr->rng[1] * tbl_ptr->rng[2] * tbl_ptr->rng[3];

    use_cnt = (int *) malloc (sizeof(int) * i);
    for(j = 0; j < i; j++)
	use_cnt[j] = 0;

    for (i = n_on; i--;) {
	for (j = n_on; j--;)
	    A[i][j] = 0.0;
	D[i] = 0.0;
    }

    for(i = 0; i < MAX_NMVS; i++)
	hist[i] = 0;

    A_wght = 0.01 * a_weight;
    B_wght = 1.0 - A_wght;
    tot_pos = 0;
    tot_mvs = 0;
    ok_pos = 0;
    ok_mvs = 0;
    A_err = 0.0;
    B_err = 0.0;
    n_kill = 0;

    n_cor = (n_cor + 50) / 100;		/* carry over 1% of the previous average (to improve precision at start) */
    acc_cor *= 0.01;
}

tbl_1upd (tp, x1, s)
    struct tab_descr *tp;
    int x1, s;
{
    register int i;
    register float *p = tbl_fpv, rx1, X1 = x1;

    ++use_cnt[x1];

    if (s > 0) {
	for (i = tp->deg[0] + 1, rx1 = 1.0; i--; rx1 *= X1)
	    *p++ += rx1;
    } else {
	for (i = tp->deg[0] + 1, rx1 = 1.0; i--; rx1 *= X1)
	    *p++ -= rx1;
    }
#ifdef TF1_DEBUG
fprintf(qtst, "upd: x1=%d s=%d tbl_fpv=", x1, s);
for (i = tp->n_p, p=tbl_fpv; i--; p++)fprintf(qtst, " %.0lf", (double) *p);
fprintf(qtst, "\n");
#endif
}

tbl_2upd (tp, x1, x2, s)
    register struct tab_descr *tp;
    int x1, x2, s;
{
    register int i, j;
    register float *p = tbl_fpv, rx1, X1 = x1, rx2, X2 = x2;

    ++use_cnt[tp->rng[0] * x2 + x1];

    if (s > 0) {
	for (i = tp->deg[1] + 1, rx2 = 1.0; i--; rx2 *= X2)
	    for (j = tp->deg[0] + 1, rx1 = rx2; j--; rx1 *= X1)
		*p++ += rx1;
    } else {
	for (i = tp->deg[1] + 1, rx2 = 1.0; i--; rx2 *= X2)
	    for (j = tp->deg[0] + 1, rx1 = rx2; j--; rx1 *= X1)
		*p++ -= rx1;
    }

#ifdef TF_DEBUG
fprintf(qtst, "upd: x1=%d x2=%d s=%d tbl_fpv=", x1, x2, s);
for (i = tp->n_p, p=tbl_fpv; i--; p++)fprintf(qtst, " %.0lf", (double) *p);
fprintf(qtst, "\n");
#endif
}

tbl_3upd (tp, x1, x2, x3, s)
    register struct tab_descr *tp;
    int x1, x2, x3, s;
{
    register int i, I, j, J, k;
    register float *p = tbl_fpv, rx1, X1 = x1, rx2, X2 = x2, rx3, X3 = x3;

    ++use_cnt[(tp->rng[1] * x3 + x2) * tp->rng[0] + x1];

    I = tp->deg[1] + 1;
    J = tp->deg[0];

    if (s > 0) {
	for (k = tp->deg[2] + 1, rx3 = 1.0; k--; rx3 *= X3)
	    for (i = I, rx2 = rx3; i--; rx2 *= X2) {
		*p++ += (rx1 = rx2);
		for (j = J; j--;)
		    *p++ += (rx1 *= X1);

	    }
    } else {
	for (k = tp->deg[2] + 1, rx3 = 1.0; k--; rx3 *= X3)
	    for (i = I, rx2 = rx3; i--; rx2 *= X2) {
		*p++ -= (rx1 = rx2);
		for (j = J; j--;)
		    *p++ -= (rx1 *= X1);

	    }
    }
}

tbl_4upd (tp, x1, x2, x3, x4, s)
    register struct tab_descr *tp;
    int x1, x2, x3, x4, s;
{
    register int i, I, j, J, k, l;
    register float *p = tbl_fpv, rx1, X1 = x1, rx2, X2 = x2, rx3, X3 = x3, rx4, X4 = x4;

    ++use_cnt[((tp->rng[2] * x4 + x3) * tp->rng[1] + x2) * tp->rng[0] + x1];

    I = tp->deg[1] + 1;
    J = tp->deg[0];

    if (s > 0) {
	for (l = tp->deg[3] + 1, rx4 = 1.0; l--; rx4 *= X4)
	    for (k = tp->deg[2] + 1, rx3 = rx4; k--; rx3 *= X3)
		for (i = I, rx2 = rx3; i--; rx2 *= X2) {
		    *p++ += (rx1 = rx2);
		    for (j = J; j--;)
			*p++ += (rx1 *= X1);

		}
    } else {
	for (l = tp->deg[3] + 1, rx4 = 1.0; l--; rx4 *= X4)
	    for (k = tp->deg[2] + 1, rx3 = rx4; k--; rx3 *= X3)
		for (i = I, rx2 = rx3; i--; rx2 *= X2) {
		    *p++ -= (rx1 = rx2);
		    for (j = J; j--;)
			*p++ -= (rx1 *= X1);

		}
    }
}

do_T_fit()			/* do a table fit */
{
    int n_mvs, E[MAX_NMVS], Kx, K0, K[MAX_NMVS];
    register int i, j, k;
    register float *fp;

#ifdef V_TRACE
    int tfd, T[2];
    if (0 > (tfd = open("q", O_WRONLY | O_CREAT, 0644)))
	quit("failed to open trace file");
#endif
#ifdef TF_DEBUG
qtst=fopen("q", "w");
#endif

    par_ptr->tab_on = 1;
    n_on = tbl_ptr->n_p;
    init_T_math();		/* reset arrays, etc */

    for(i = from; i <= to; i++) {
	if (i >= max_gm) {	/* hit end of game file: update upper limit */
	    to = max_gm - 1;
	    break;
	}
	game(i);
	err("*** Busy on game %d ***", i, 0, 0, 0);

	do{			/* loop through game */

	    if (mvs_cnt <= i_skip)			    /* skip initial moves */
		continue;

	    
	    for (j = n_on, tbl_fpv = fp = FPx; j--; *fp++ = 0.0);
	    
	    Ex = EVAL();
	    Kx = Ex - par_ptr->coef * par_ptr->init;

	    next();

	    for (j = n_on, tbl_fpv = fp = FP0; j--; *fp++ = 0.0);

	    E0 = T_EVAL(&K0);

	    j = Ex - E0;
	    if (j < 0)
		j = -j;
	    if (j <= SCALEF * 64)
	        do_AT_term(FPx, Kx, FP0, K0);	    /* only if values are within 1/2 pawn */

	    n_err = 0;
	    n_mvs = 0;

	    while(next()) {
		for (j = n_on, tbl_fpv = fp = &(FP[n_mvs][0]); j--; *fp++ = 0.0);

		E[n_mvs] = k = T_EVAL(&(K[n_mvs]));

		j = E0 - k;
		if (j < 0)
		    j = -j;
		if (j > SCALEF * 64) {		/* value is off by more than 1/2 pawn: skip */
		    n_kill++;
		    continue;
		}

#ifdef V_TRACE
		T[0] = E0;
		T[1] = (!stm) ? k : 2*E0 - k;
		write(tfd, T, 2 * sizeof(int));
#endif
		if (stm) n_err += E0 >= k;	/* black */
		else     n_err += E0 <= k;	/* white */

		n_mvs++;
	    }

	    for (j = n_mvs; j--;)
		do_BT_term(FP0, K0, &(FP[j][0]), K[j], E[j], n_err);

	    if (n_mvs) {			/* skip trivial cases */
		tot_pos += n_mvs;		/* statistics */
		ok_pos += n_mvs - n_err;
		tot_mvs++;
		ok_mvs += !n_err;
		if(n_err >= MAX_NMVS) ++hist[MAX_NMVS - 1];
		else                  ++hist[n_err];
	    }

	} while (Next());

    }

#ifdef V_TRACE
    close(tfd);
#endif
    par_ptr->tab_on = 0;

    do_T_math();			    /* solve the system... */
#ifdef TF_DEBUG
fclose(qtst);
#endif
}


T_EVAL(kx)		    /* complete min/max evaluation of current position */
    int *kx;			    /* is is assumed that "next" was used to set-up back[0] and backup[0] */
{
    float px[MAX_NPRM];
    register float *pp, *p1;
    register int i, j;

    for(i = n_on, p1 = px; i--; *p1++ = 0.0);
    pp = tbl_fpv;
    tbl_fpv = px;

#ifdef KEEP_PV
    strcpy(&(pv[0][0]), m_dec(mlist[ml_cnt], sunboard, stm));
#endif

#ifdef T_DEBUG
    fprintf(qtst, ">> %s (%d/%d):\n", m_dec(mlist[ml_cnt], sunboard, stm), ml_cnt,  ml_max);
#endif

    ml_cind = st_ind[ml_cnt - 1];
    j = T_eval(0, stm, kx);

    for(i = n_on, tbl_fpv = pp, p1 = px; i--; *pp++ = *p1++);

#ifdef T_DEBUG
    fprintf(qtst, "<< %s = %d\n", m_dec(mlist[ml_cnt], sunboard, stm), unscale(j));
#endif

    return j;
}

T_eval (depth, min, kx)
    int depth, min, *kx;
{
    int board[64], cst, EV, ev, i, j, l;
    float px[MAX_NPRM], *P;
    register float *fp1, *fp2;
    register int k, m, *p1, *p2;

    if (!(j = mlist[ml_cind++])) {	/* # of moves on this level */
	k = EVAL();
	*kx = k - par_ptr->coef * par_ptr->init;

#ifdef KEEP_PV
	pv_depth[depth] = depth;
#endif

#ifdef TX_DEBUG
	for(m = 0; m <= depth; m++)
	    fprintf(qtst, "    ");
	fprintf(qtst, "TV = %d\n", unscale(k));
#endif

	return k;		/* leave node: just return value */
    }

    P = tbl_fpv;
    tbl_fpv = px;

    cst = back_cst;		/* save position */
    for (m = 64, p1 = board, p2 = backup; m--; *p1++ = *p2++);

    depth++;
    min ^= 1;

    for (i = 0, k = ml_cind, ml_cind += j; i < j; i++, k++) {
	b_update(mlist[k], min);
	back_cst = *fit_castle_st;

	for(m = n_on, fp2 = px; m--; *fp2++ = 0.0);

	ev = T_eval(depth, min, &l);
	if (!i || (min && ev < EV) || (!min && ev > EV)) {
	    EV = ev;
	    *kx = l;

	    for(m = n_on, fp1 = P, fp2 = px; m--; *fp1++ = *fp2++);

#ifdef KEEP_PV
	    strcpy(&(pv[depth][0]), m_dec(mlist[k], board, min));
	    pv_depth[depth - 1]	= pv_depth[depth];
#endif
	}

#ifdef T_DEBUG
	for(m = 0; m <= depth; m++)
	    fprintf(qtst, "    ");
	fprintf(qtst, "%s = %d (%d)\n", m_dec(mlist[k], board, min), unscale(ev), unscale(EV));
#endif

	back_cst = cst;
	*fit_castle_st = cst;
	for (m = 64, p1 = board, p2 = backup; m--; *p2++ = *p1++);
    }

    tbl_fpv = P;

    return EV;
}

do_AT_term(fpx, Kx, fp0, K0)			/* process the (Ex-E0)^2 part */
    float *fpx, *fp0;
{    
    float t[MAX_NPRM];
    register int i;
    register float *p1, *p2, *p3;
    register double e;

    e = Ex - E0;
    A_err += e * e;

    for (i = n_on, p1 = fpx, p2 = fp0, p3 = t; i--;)
	*p3++ = *p1++ - *p2++;

    add_fvec (t, K0 - Kx, A_wght);		/* keep Ex and E0 similar term */
/*  add_fvec (t, (K0-E0) - (Kx-Ex), A_wght);	/* inertia term: keep things as they are */
}

do_BT_term(fp0, K0, fpn, Kn, E_n, ne)		/* process the (E0 - En - gn)^2 part */
    int E_n, ne, K0, Kn;
    float *fp0, *fpn;
{
    
    float t[MAX_NPRM];
    register int i, g;
    register float *p1, *p2, *p3;
    register double e, w = 1.0;
    double log(), sqrt();

#ifndef OLD_ME
    if (ne) {					/* there are ne > 0 moves ahead of the GM one */
	w = wfunc * 0.01 / ne;
    }
#endif


    g = E_n - E0;				/* assume no change desired */

    /*
     * increasing g will cause a force towards  E0 < En
     * decreasing g will cause a force towards  E0 > En
     *
     */


    if ((stm  && E_n < E0) ||		    /* black to move */
        (!stm && E_n > E0)   ) {	    /* white to move */

	e = (stm) ? E0 - E_n : E_n - E0;
	B_err += e * e;

	/* "i" is the correcting force as a function of the absolute error "e" */
        /* i > 0 will cause the E0 be evaluated "better" than En */

	/* Normalization: a min_g of 100 will cause a force of 1 pawn for 1 pawn difference in e */

	switch (efunc) {
	    case 0:
		i = rint(min_g * (1.0 + 31.42 * log(1.0 + e)));	    /* log */
		break;

	    case 1:
		i = rint(min_g * 1.810 * sqrt(1.0 + e));	    /* Square root */
		break;

	    case 2:
		i = rint(min_g * 0.01 * (1.0 + e));		    /* linear */
		break;

	    case 4:						    /* inverse */
		i = rint(min_g * 327.68 * 50000.0 /(50000.0 + e));
		break;

	    default:
		i = rint(min_g * 327.68);			    /* Step function */
		break;
	}

	if (stm) g += i;
	else     g -= i;

	acc_cor += i;

    } else {
	
	n_cor++;
	i = rint(vari_c * 0.01 * acc_cor / n_cor);

	if (stm) g += i;
	else     g -= i;
    }

    for (i = n_on, p1 = fp0, p2 = fpn, p3 = t; i--;)
	*p3++ = *p1++ - *p2++;

    add_fvec (t, (Kn - K0) - g, B_wght * w);		/* make E0 better than En term */
/*  add_fvec (t, (Kn - K0) - (E_n - E0), B_wght * w);	/* make E0 better than En term */
}

add_fvec(iv, y, w)
    int y;
    float *iv;
    double w;
{
    register int i, j;
    register float x, t, *p1, *p2;
    register double *fp, rw;
#ifdef TF_DEBUG
fprintf(qtst, "add_v y=%d w=%.2f iv=", y, w);
for (i = tbl_ptr->n_p, p1=iv; i--; p1++)fprintf(qtst, " %.0f", *p1);
fprintf(qtst, "\n");
#endif
    for (i = 0, p1 = iv; i < n_on; i++) {
	if (!(x = *p1++))
	    continue;

	rw = w * x;
	D[i] += y * rw;

	for (j = n_on, p2 = iv, fp = &(A[i][0]); j--; fp++) {
	    if (!(t = *p2++))
		continue;
	    *fp += t * rw;
	}
    }
}

do_T_math()				    /* solve the system */
{
    register float rx1, X1, rx2, X2;
    float rx3, X3, rx4, X4, *tp, *fp;
    register int i, j, k, l;
    double sqrt(), *p;
    register double t, clp, sf;
    int ts, x1, x2, x3, x4;
    short *sp;

    static int max_tmp = 0;
    static float *f_tmp = 0, *sma;

    A_err = sqrt(A_err / tot_mvs) * 3.0518e-5;
    B_err = sqrt(B_err / tot_pos) * 3.0518e-5;

    if (lin_equ(n_on, A, X, D)) {
	err("Sorry, singular system: some parameters were never used - no fit possible", 0, 0, 0, 0);
	return;
    } else
	err("Computing new table", 0, 0, 0, 0);
#ifdef TF_DEBUG
fprintf(qtst, "math: n_on=%d X=", n_on);
for (i = tbl_ptr->n_p, p=X; i--; p++)fprintf(qtst, " %.4g", *p);
fprintf(qtst, "\n");
#endif
    /* need to eval polyn., normalize it, compute table, adjust param */

    ts = tbl_ptr->rng[0] * tbl_ptr->rng[1] * tbl_ptr->rng[2] * tbl_ptr->rng[3];

    clp = fit_PV0->init * 205.0;			/* clamping set to +/- 0.8 pawn: calm down some
							   of the wild values that high order polynomial
							   produce when they are extrapolating to ranges
							   for which no data points were present */

    if (ts > max_tmp) {
	if (f_tmp) {
	    free(sma);
	    free(f_tmp);
	}

	f_tmp = (float *) malloc (sizeof(float) * ts);
	sma = (float *) malloc (sizeof(float) * ts);
	max_tmp = ts;
    }

    tp = f_tmp;

    if (!strcmp(par_ptr->name, "PawnPlace")) {		/* special feature for "PawnPlace": move const and m-linear
							   terms to 'PawnValue0' and 'PawnValue1' */
	i = (tbl_ptr->deg[0] + 1) * (tbl_ptr->deg[1] + 1);	/* index to linear material term */

	fit_PV0->init += rint(X[0] / 256.0);
	X[0] = 0.0;
	fit_PV1->init += rint(X[i] / 256.0);
	X[i] = 0.0;
    }

    if (!strcmp(par_ptr->name, "PPAdvance")) {
	fit_PPA->init += rint(X[0] / 256.0);
	X[0] = 0.0;
    }

    get_sa (sma);		/* get significance array */
    smooth_sa (sma);
    clip_sa (sma);
    fp = sma;

    sp = tbl_ptr->data;
    sf = par_ptr->init;
    
    for (x4 = 0; x4 < tbl_ptr->rng[3]; x4++) {
	X4 = x4;
	for (x3 = 0; x3 < tbl_ptr->rng[2]; x3++) {
	    X3 = x3;
	    for (x2 = 0; x2 < tbl_ptr->rng[1]; x2++) {
		X2 = x2;
		for (x1 = 0; x1 < tbl_ptr->rng[0]; x1++) {
		    X1 = x1;

		    p = X;
		    t = 0.0;

		    for (l = tbl_ptr->deg[3] + 1, rx4 = 1.0; l--; rx4 *= X4)
			for (k = tbl_ptr->deg[2] + 1, rx3 = rx4; k--; rx3 *= X3)
			    for (i = tbl_ptr->deg[1] + 1, rx2 = rx3; i--; rx2 *= X2)
				for (j = tbl_ptr->deg[0] + 1, rx1 = rx2; j--; rx1 *= X1)
				    t += *p++ * rx1;

		    t = *fp * t + (1.0 - *fp) * sf * *sp++;
		    fp++;

		    if      (t >  clp) t =  clp;	/* clamp */
		    else if (t < -clp) t = -clp;
		    
		    *tp++ = t;
		}
	    }
	}
    }

    rx1 = f_tmp[0];	/* min */
    rx2 = rx1;		/* max */

    for (tp = f_tmp + 1, i = ts - 1; i--; tp++) {   /* find min/max */
	if (rx1 > *tp)
	    rx1 = *tp;
	if (rx2 < *tp)
	    rx2 = *tp;
    }
    if (rx1 < 0)
	rx1 = -rx1;
    if (rx2 < 0)
	rx2 = -rx2;
    if (rx1 < rx2)
	rx1 = rx2;

    t = 256.0 / rx1;	/* compute scale factor */

    for (i = ts, sp = tbl_ptr->data, tp = f_tmp; i--;)	/*  update table */
	*sp++ = rint(t * *tp++);

    par_ptr->init = rint(1.0 / t);

    err("Finally done !", 0, 0, 0, 0);
}

struct fit_param *get_tbl()		    /* select a table to work on */
{
    char buf[200], *p;
    register int i;

    for(i = 0; i < n_param; i++)
	if (All_params[i]->table) {
	    sprintf(buf, "'%s' [y/n]:", All_params[i]->name);
	    p = get_cmd(buf);
	    if (p[0] == 'y' || p[0] == 'Y') {
		if(All_params[i]->tp->n_p > MAX_NPRM) {
		    err("Increase 'MAX_NPRM' to %d and recompile", All_params[i]->tp->n_p, 0, 0, 0);
		    return 0;
		}
		return All_params[i];
	    }
	}

    return 0;
}

d_dump(s)			/* dump tables to file */
    char *s;
{
    char buf[80];
    FILE *fp;
    register int l, K, k, J, j, I, i;
    register short *sp;

    switch(tbl_ptr->dim) {

	case 3:
	    I = tbl_ptr->rng[2];
	    J = tbl_ptr->rng[1];
	    K = tbl_ptr->rng[0];
	    if (!strcmp("PawnPlace", par_ptr->name) ||
		!strcmp("PPAdvance", par_ptr->name)   ) {	/* special case: mirror centrality term */
		for(i = I * J * K, sp = tbl_ptr->data, j = *sp; i--; sp++) {
		    if (j > *sp)
			j = *sp;
		}
		l = 0;
		if (j < 0) l = -j;
		for(i = 0; i < I; i++) {
		    sprintf(buf, "%s_%d.dat", s, i);
		    if (!(fp = fopen(buf, "w"))) {
			err("Failed to open file", 0, 0, 0, 0);
			break;
		    }
		    for(j = J; j--;) {
			for(k = 0; k < K; k++)
			    fprintf(fp, "%d ", l + tbl_ptr->data[k + j * K + i * J * K]);
			for(; k--;)
			    fprintf(fp, "%d ", l + tbl_ptr->data[k + j * K + i * J * K]);
			fprintf(fp, "\n");
		    }
		    fclose(fp);
		}
	    } else {
		for(i = 0; i < I; i++) {
		    sprintf(buf, "%s_%d.dat", s, i);
		    if (!(fp = fopen(buf, "w"))) {
			err("Failed to open file", 0, 0, 0, 0);
			break;
		    }
		    for(j = 0; j < J; j++) {
			for(k = 0; k < K; k++)
			    fprintf(fp, "%d ", tbl_ptr->data[k + j * K + i * J]);
			fprintf(fp, "\n");
		    }
		    fclose(fp);
		}
	    }
	    break;
	    
	default:
	    err("This table format is not yet supported", 0, 0, 0, 0);
	    break;
    }
}

get_sa (fp)
    register float *fp;
/*
 * copy the use-array to the significance array pointed to by <fp>
 *
 * average over unity-volume (+/- 1 in each dimension)
 *
 */
{
    int x4, x3, x2;
    int y4, y3, y2;
    int X4, X3, X2;
    register int n, s, *U = use_cnt, x1, y1, X1, t;

    X4 = tbl_ptr->rng[3];
    X3 = tbl_ptr->rng[2];
    X2 = tbl_ptr->rng[1];
    X1 = tbl_ptr->rng[0];

    for (x4 = 0; x4 < X4; x4++) {
	for (x3 = 0; x3 < X3; x3++) {
	    for (x2 = 0; x2 < X2; x2++) {
		for (x1 = 0; x1 < X1; x1++) {

		    s = 0;
		    n = 0;

		    for (y4 = (x4 > 0) ? (x4 - 1) : 0; y4 < X4 && y4 <= (x4 + 1); y4++) {
			for (y3 = (x3 > 0) ? (x3 - 1) : 0; y3 < X3 && y3 <= (x3 + 1); y3++) {

			    t = X2 * (y3 + X3 * y4);

			    for (y2 = (x2 > 0) ? (x2 - 1) : 0; y2 < X2 && y2 <= (x2 + 1); y2++) {
				for (y1 = (x1 > 0) ? (x1 - 1) : 0; y1 < X1 && y1 <= (x1 + 1); y1++) {
				    n++;
				    s += U[y1 + X1 * (y2 + t)];
				}
			    }
			}
		    }

		    *fp++ = MIN_FREQ * ((float) s / (float) n);
		}
	    }
	}
    }
}

clip_sa (fp)			/* limit significance to 1 */
    register float *fp;
{
    register int i = tbl_ptr->rng[0] * tbl_ptr->rng[1] * tbl_ptr->rng[2] * tbl_ptr->rng[3];
    for (; i--; fp++)
	if (*fp > 1.0)
	    *fp = 1.0;
}

smooth_sa (fp)			/* optional place to add further convolution of the significance array */
    float *fp;
{
    
}
