/*
 * Copyright 1996 by Michel D. Grimminck
 *
 * All Rights Reserved
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#include <stdio.h>
#include "const.h"
#include "var.h"
#include "functions.h"
#include <time.h>


#define DB_INDEX_FILE "endings"
#define DB_UNKNOWN 3
#define DB_ALL 100
#define CAPTURE 100
#define DB_VERIFY 200
#define QMAX 2000000

/* XXvXX log
db:0202, found=139512, time=80
db:0202, found=10415, time=245
db:0202, found=115, time=238
db:0202, found=0, time=240
*/

FILE *logfile;
int pos_count[4096];
int bytesize[4096];
int verified[4096];
int qsize[2],qmax[2];
char qname[2][20]={"/tmp/pos_queue0","/tmp/pos_queue1"};
FILE *qfile[2]={NULL,NULL};


/* 'database' points to all databases with a maximum of 8 man and 8 crowns
   or contains NULL if the database is not present.

   All databases are computed for 'white to move'. If black is to move
   the board is reversed.

*/


void init_queue_write(int q)
{
  if (q!=0 && q!=1) {printf("fatal: queue does not exist\n"); exit(1);}
  qsize[q]=0;
  qfile[q]=fopen(qname[q],"wb");
  if (qfile[q]==NULL) {printf("warning: can not open queue for writing\n"); return;}
}

void init_queue_read(int q)
{
  if (q!=0 && q!=1) {printf("fatal: queue does not exist\n"); exit(1);}
  if (qsize[q]<=0) {printf("fatal: attempting to open incomplete queue\n"); exit(1);}
  qfile[q]=fopen(qname[q],"rb");
  if (qfile[q]==NULL) {printf("fatal: can not open queue for reading\n"); exit(1);}
}

void add_to_queue(int q)
{
  int i,n;

  if (qfile[q]==NULL) return; /* queue not open (anymore) */
  if (qsize[q]>=qmax[q]) {
    printf("warning: maximal queue size reached: closing queue\n");
    fclose(qfile[q]);
    qfile[q]=NULL;
    qsize[q]=-1; /* queue incomplete: don't use it */
    return;
  }
  n=0; for(i=0;i<50;i++) if (board[map[i]]!=empty) n++;
  fputc(n,qfile[q]);
  for(i=0;i<50;i++) if (board[map[i]]!=empty) {
    fputc(i,qfile[q]);
    fputc(board[map[i]],qfile[q]);
  }
  qsize[q]++;
}

static char* read_from_queue(int q)
{
  int i,n,p;
  static char local[93];

  if (qfile[q]==NULL) {printf("fatal: attempting to read from closed queue\n"); exit(1);}
  if (feof(qfile[q])) {
    fclose(qfile[q]);
    qfile[q]=NULL;
    return(false);
  }
  
  for(i=0;i<50;i++) local[map[i]]=empty;
  n=fgetc(qfile[q]);
  for(i=0;i<n;i++) {
    p=fgetc(qfile[q]);
    local[map[p]]=fgetc(qfile[q]);
  }
  return(local);
}

void close_queue(int q)
{
  if (qfile[q]!=NULL) fclose(qfile[q]);
  qfile[q]=NULL;
}

static char *database_name(int wman,int wcrown,int bman,int bcrown)
{
  static char name[40];
  int a=0,i;

  for(i=0;i<wcrown;i++) name[a++]='X';
  for(i=0;i<wman;i++) name[a++]='O';
  name[a++]='v';
  for(i=0;i<bcrown;i++) name[a++]='X';
  for(i=0;i<bman;i++) name[a++]='O';
  name[a]='\0';
  return(name);
}

void database_piecelist(int *piecelist,int wman,int wcrown,int bman,int bcrown)
{
  int a=0,i;

  for(i=0;i<wcrown;i++) piecelist[a++]=white|crown;
  for(i=0;i<wman;i++) piecelist[a++]=white|man;
  for(i=0;i<bcrown;i++) piecelist[a++]=black|crown;
  for(i=0;i<bman;i++) piecelist[a++]=black|man;
}

void database_count(char *id,int *wman,int *wcrown,int *bman,int *bcrown)
/* returns the number of pieces for a database */
{
  int n;

  *wman=*bman=*wcrown=*bcrown=0;
  
  printf("s:%s\n",id);
  n=0;
  while(id[n]!='v') {
    if (id[n]=='X') *wcrown++;
    if (id[n]=='O') *wman++;
    n++;
  }
  while(id[n]!=0) {
    if (id[n]=='X') *bcrown++;
    if (id[n]=='O') *bman++;
    n++;
  }
  printf("s:%i %i\n",n,*wman);
}

int load_database(int wman,int wcrown,int bman,int bcrown)
{
  char db_file[100],*id;
  int i,size,nr;
  FILE *db;

  id=database_name(wman,wcrown,bman,bcrown);
  sprintf(db_file,"databases/%s.raw",id);
  
  size=db_count(wman,wcrown,bman,bcrown);
  nr=database_nr(white,wman,wcrown,bman,bcrown);
  pos_count[nr]=size;
  bytesize[nr]=(size+3)/4;
  database[nr]=(unsigned char *)malloc(sizeof(unsigned char)*bytesize[nr]);
  if (database[nr]==NULL) {
    printf("fatal: no memory for database\n");
    exit(1);
  }
  
  db=my_fopen(db_file,"rb");
  if (db==NULL) {
    printf("database %s not found\n",db_file);
    database[nr]=NULL;
    return(false);
  }
  
  printf("opening database %s:%i%i%i%i\r",db_file,wman,wcrown,bman,bcrown);
  fflush(stdout);
  for(i=0;i<bytesize[nr];i+=256) database[nr][i]=0;
  fread(database[nr],sizeof(unsigned char),bytesize[nr],db);
  fclose(db);
  printf("                                     \r");
  return(true);
}

void init_databases(void)
{
  int i;
  for(i=0;i<4096;i++) {
    database[i]=NULL;
    verified[i]=false;
  }
  init_index();
}

void read_all_databases(void)
{
  /* reads in all available databases defined in the file 'DB_INDEX_FILE'
  */
  FILE *in;
  char id[12],include;
  int wman,wcrown,bman,bcrown,i,ndb,ii,byte_size,type,n,nr;

  in=my_fopen(DB_INDEX_FILE,"r");
  if (in==NULL) {
    printf("error: database index file not found\n");
    return;
  }
  
  /* get number of databases to be loaded */
  fscanf(in,"%i\n",&ndb);
  
  /* read all selected databases */
  for(ii=0;ii<ndb;ii++) {
    fscanf(in,"%s %c",id,&include);
    if (include=='n') continue;
    n=wman=bman=wcrown=bcrown=0;
    while(id[n]!='v') {
      if (id[n]=='X') wcrown++;
      if (id[n]=='O') wman++;
      n++;
    }
    while(id[n]!=0) {
      if (id[n]=='X') bcrown++;
      if (id[n]=='O') bman++;
      n++;
    }
    load_database(wman,wcrown,bman,bcrown);
  }
  fclose(in);
}  
  
int database_nr(int color,int wman,int wcrown,int bman,int bcrown)
/* returns the number of the requested database */
{
  int nr;

  if (color==white) nr=wman+8*wcrown+64*bman+512*bcrown;
  else nr=bman+8*bcrown+64*wman+512*wcrown;

  return(nr);
}

int database_retreive_value(int color,int wman,int wcrown,int bman,int bcrown)
/* retreives the value of a database entry using 'database_address' and
   'database_linear_index. 2 bits per position
   
   Returns a value from the perspective of 'color'.
   */
{
  int index,dindex,score;
  unsigned char *db;

  /* no pieces: return LOSE */
  if (color==white) if (wman==0 && wcrown==0) return(DB_LOSE);
  if (color==black) if (bman==0 && bcrown==0) return(DB_LOSE);
  db=database[database_nr(color,wman,wcrown,bman,bcrown)];
#ifdef PARTIAL
  if (db==NULL) return(DB_UNKNOWN);
#endif
  index=database_linear_index(color);

  /* place decompression here ! */
  dindex=index/4;
  
  switch(index-4*dindex) {
  case 0:{score=(db[dindex] >>6)&3;break;}
  case 1:{score=(db[dindex] >>4)&3;break;}
  case 2:{score=(db[dindex] >>2)&3;break;}
  case 3:{score=(db[dindex]) &3;break;}
  }
  return(score);
}

int read_value(unsigned char *db,int index)
{
  int dindex;
  int score;
  dindex=index/4;
  
  switch(index-4*dindex) {
  case 0:{score=(db[dindex] >>6)&3;break;}
  case 1:{score=(db[dindex] >>4)&3;break;}
  case 2:{score=(db[dindex] >>2)&3;break;}
  case 3:{score=(db[dindex]) &3;break;}
  }
  return(score);
}

void store_value(unsigned char *db,int index,int score)
{
  int dindex;
  dindex=index/4;

  switch(index-4*dindex) {
  case 0:{
    db[dindex]=(db[dindex]&63)+(score*64);
    break;
  }
  case 1:{
    db[dindex]=(db[dindex]&207)+(score*16);
    break;
  }
  case 2:{
    db[dindex]=(db[dindex]&243)+(score*4);
    break;
  }
  case 3:{
    db[dindex]=(db[dindex]&252)+(score);
    break;
  }
  }
}

void countvalue(int wman,int wcrown,int bman,int bcrown)
/* prints all position in a database with value 'value'. */
{
  int nr,size,index;
  int piecelist[20];
  int win=0,draw=0,lose=0,unknown=0;

  nr=database_nr(white,wman,wcrown,bman,bcrown);
  size=pos_count[nr];  
 
  for(index=0;index<size;index++) {
    if (read_value(database[nr],index)==DB_WIN) win++;
    if (read_value(database[nr],index)==DB_DRAW) draw++;
    if (read_value(database[nr],index)==DB_LOSE) lose++;
    if (read_value(database[nr],index)==DB_UNKNOWN) unknown++;
  }
  printf("win %i  draw %i  lose %i  unknown/illegal %i\n",win,draw,lose,unknown);
}

int nextboard(int *list,int *type,int *constraint,int *min,int *max,int p)
{
  int try,stop=false;

  if (p<0) return(true);
  board[map[list[p]]]=empty;
  do {
    list[p]++;
  } while(board[map[list[p]]]!=empty && list[p]<max[p]);
  if (list[p]>=max[p]) {
  back:
    stop=nextboard(list,type,constraint,min,max,p-1);
    try=min[p];
    if (constraint[p]==1) try=list[p-1]+1;
    while(board[map[try]]!=empty && try<max[p]) try++;
    if (try>=max[p]) goto back;
    list[p]=try;
  }
  board[map[list[p]]]=type[p];
  return(stop);
}

int init_nextboard(int *list,int *type,int *constraint,int *min,int *max,int wman,int wcrown,int bman,int bcrown)
{
  int i,j,n;
  
  init_board();
  for(i=0;i<50;i++) board[map[i]]=empty;
  for(i=0;i<12;i++) list[i]=49;
  for(i=0;i<12;i++) constraint[i]=1;
  j=0;
  constraint[j]=0;
  for(i=0;i<bman;i++) {min[j]=0; max[j]=45; type[j++]=black|man;}
  constraint[j]=0;
  for(i=0;i<wman;i++) {min[j]=5; max[j]=50; type[j++]=white|man;}
  constraint[j]=0;
  for(i=0;i<bcrown;i++) {min[j]=0; max[j]=50; type[j++]=black|crown;}
  constraint[j]=0;
  for(i=0;i<wcrown;i++) {min[j]=0; max[j]=50; type[j++]=white|crown;}
  n=j;
  nextboard(list,type,constraint,min,max,n-1);
  return(n);
}

int determen_value(int nr,int index)
/* determens and sets, if possible, a value in database 'nr',
   returns the value
*/
{
  int score,nmoves,m,found=DB_UNKNOWN,known,best;

  best=DB_LOSE; known=true;
  nmoves=move_list(0,white);
  for(m=0;m<nmoves;m++) {
    do_move(movelist[0][m]);
    score=database_retreive_value(black,pieces[white|man],pieces[white|crown],pieces[black|man],pieces[black|crown]);
    undo_move(movelist[0][m]);
    if (score==DB_UNKNOWN) known=false;
    else if (score==DB_LOSE) {best=DB_WIN; break;}
    else if (score==DB_DRAW) best=DB_DRAW;
  }
  if (best==DB_WIN || known==true) {
    store_value(database[nr],index,best);
    found=best;
  }
  return(found);
}

int one_iteration_fromqueue(int nr,int wman,int wcrown,int bman,int bcrown,int inq,int outq,int mirror)
{
  int nmoves,m,result,index;
  int i,j,found=0,starttime;
  static char *local;
  starttime=clock();
  init_queue_write(outq);
  init_queue_read(inq);
  init_board(); 

  for(i=0;i<qsize[inq];i++) {
    local=read_from_queue(inq);
    
    /* we now have a new board position, with black to move. Undo the
       last (white) move and do a forward search. If results, store into
       the out-queue
       */
    if (mirror==0) for(j=0;j<50;j++) board[map[j]]=local[map[j]];
    else reverse_board(board,local);

    set_pieces();
      
    nmoves=reverse_move_list(1,white); /* kings only ! */
  
    for(m=0;m<nmoves;m++) {
      do_move(movelist[1][m]); /* we have a potential position, try it ! */
      set_pieces();
      index=database_linear_index(white);
      if (read_value(database[nr],index)==DB_UNKNOWN) {
	result=determen_value(nr,index);
#ifdef PARTIAL
	if (result==DB_WIN || result==DB_LOSE || result==DB_DRAW) found++;
	if (result==DB_DRAW) add_to_queue(outq);
#else
	if (result==DB_WIN || result==DB_LOSE) found++;
#endif
	if (result==DB_WIN || result==DB_LOSE) add_to_queue(outq);
      }
      undo_move(movelist[1][m]);
    }
  }
  close_queue(inq);
  close_queue(outq);
  printf("*\007"); fflush(stdout);
  fprintf(logfile,"db:%i%i%i%i, found=%i, time=%i\n",wman,wcrown,bman,bcrown,found,(clock()-starttime)/CLOCKS_PER_SEC); fflush(logfile);

  return(found);
}

int one_iteration(int nr,int wman,int wcrown,int bman,int bcrown,int mode,int outq)
/* does one iteration in creating database 'nr'. Returns the number of
   newly discovered positions */
{
  int index,i,j,n;
  int size,score,trypos;
  int found=0;
  int piecelist[40]; /* type of piece for 1 ... n */
  int list[12],type[12],constraint[12],min[12],max[12];
  int stop=false,starttime,result;
  FILE *out=NULL;

  starttime=clock();
  if (outq>=0) init_queue_write(outq);
  n=init_nextboard(list,type,constraint,min,max,wman,wcrown,bman,bcrown);
  size=pos_count[nr];
  database_piecelist(piecelist,wman,wcrown,bman,bcrown);
  set_pieces();
  do {
    index=database_linear_index(white);
    trypos=true;
    if (read_value(database[nr],index)!=DB_UNKNOWN) trypos=false;
    else {
      if (mode==CAPTURE) {set_pieces();
	if (quiet(white)==true) trypos=false;
      }
    }
    if (trypos==true) {
      result=determen_value(nr,index);
#ifdef PARTIAL
      if (result==DB_WIN || result==DB_LOSE || result==DB_DRAW) found++;
      if (result==DB_DRAW && outq>=0) add_to_queue(outq);
#else
      if (result==DB_WIN || result==DB_LOSE) found++;
#endif
      if (result==DB_WIN || result==DB_LOSE && outq>=0) add_to_queue(outq);
    }
  } while(nextboard(list,type,constraint,min,max,n-1)==false);
  printf("#\007");
  fflush(stdout);
  fprintf(logfile,"db:%i%i%i%i, found=%i, time=%i\n",wman,wcrown,bman,bcrown,found,(clock()-starttime)/CLOCKS_PER_SEC); fflush(logfile);
  if (outq>=0) close_queue(outq);
  return(found);
}

void validqueue(int *q1,int *q2)
{
  /* if all went ok, q2 contains a valid queue: use this as
     input queue
     */
  *q1=1-*q1; *q2=1-*q2;
}

void create_database(int wman,int wcrown,int bman,int bcrown,int level)
{
  int wm,wc,bm,bc;
  unsigned char *db;
  char db_file[2][100],*id;
  int nr[2],size[2],p,n,newfound,i;
  int pos,found=0,loaded=0;
  int q1,q2;
  FILE *out,*in;
  int mirror;  /* mirror is set to 1 for symmetric databases, 2 otherwise */
  
  if (level<0) return;
  /* has it been created ? */
  nr[0]=database_nr(white,wman,wcrown,bman,bcrown);
  nr[1]=database_nr(white,bman,bcrown,wman,wcrown);
  if (database[nr[0]]!=NULL) { /* database is already present */
    return;
  }
  
  /* first create all required sub-databases */
  for(wm=0;wm<=7;wm++) for(wc=0;wc<=7;wc++)
    for(bm=0;bm<=7;bm++) for(bc=0;bc<=7;bc++)
      if (wm!=wman || bm!=bman || wc!=wcrown || bc!=bcrown) 
	if ((wm+wc)>0 && (bm+bc)>0)
	  if (wm<=wman && bm<=bman && wc<=(wman+wcrown) && bc<=(bman+bcrown))
	    if ((wm+wc)<=(wman+wcrown) && (bm+bc)<=(bman+bcrown))
	      if (wc<=(wcrown+1) && bc<=(bcrown+1))
		create_database(wm,wc,bm,bc,level-1);

  /* get the database into memory ! */
  printf("\n");
  
  /* symmetric database ? */
  if (nr[0]==nr[1]) mirror=1; else mirror=2;

   /* determen size of the database(s) */
  size[0]=db_count(wman,wcrown,bman,bcrown);
  size[1]=db_count(bman,bcrown,wman,wcrown);
  for(i=0;i<mirror;i++) {
    if (i==0) id=database_name(wman,wcrown,bman,bcrown);
    else id=database_name(bman,bcrown,wman,wcrown);
    sprintf(db_file[i],"databases/%s.raw",id);
    
    /* allocate memory */
    pos_count[nr[i]]=size[i];
    bytesize[nr[i]]=(size[i]+3)/4;
    database[nr[i]]=(unsigned char *)malloc(sizeof(unsigned char)*bytesize[nr[i]]);
    if (database[nr[i]]==NULL) {
      printf("fatal: no memory for database\n");
      exit(1);
    }
    /* try loading from disk */
    in=fopen(db_file[i],"rb");
    if (in==NULL) {
      /*printf("database %s not found: creating\n",db_file[i]);*/
    } else {   
      printf("loading database %s\n",db_file[i]);
      fread(database[nr[i]],sizeof(unsigned char),bytesize[nr[i]],in);
      fclose(in);
      
      loaded++;
    }
  }
  if (loaded==mirror) return;
  if (loaded!=0) {
    printf("fatal: weird error on loading\n");
    exit(1);
  }
#ifdef PARTIAL
  if (level==0) { /* sub-database: do not create! */
    for(i=0;i<mirror;i++) {
      free(database[nr[i]]);
      database[nr[i]]=NULL;
      return;
    }
  }
#endif
  /* basic database generation routine */

  /* initialize */
  for(i=0;i<mirror;i++) {
    printf("creating %s, %i positions\n",db_file[i],size[i]);
    for(p=0;p<size[i];p++) {
      store_value(database[nr[i]],p,DB_UNKNOWN);
    }
  }

  /* initialise queue system */
  q1=0; q2=1;
  qmax[0]=0.05*size[0];
  if (qmax[0]>QMAX) qmax[0]=QMAX;
  qmax[1]=qmax[0];
  qsize[0]=qsize[1]=-1;

  if (mirror==1) {
    /* first pass: capture moves only */
    found=one_iteration(nr[0],wman,wcrown,bman,bcrown,CAPTURE,-1);
    
    /* second pass: force all positions being seen */
    found+=newfound=one_iteration(nr[0],wman,wcrown,bman,bcrown,NORMAL,q2);
    validqueue(&q1,&q2);

    while(newfound>0) {
      if (qsize[q1]<=0) found+=newfound=one_iteration(nr[0],wman,wcrown,bman,bcrown,NORMAL,q2); /* no queue available */
      else found+=newfound=one_iteration_fromqueue(nr[0],wman,wcrown,bman,bcrown,q1,q2,mirror); /* previous computation resulted in a queue */
      validqueue(&q1,&q2);
    }
  }
  else {
    /* first pass: capture moves only */
    found=one_iteration(nr[0],wman,wcrown,bman,bcrown,CAPTURE,-1);
    found+=one_iteration(nr[1],bman,bcrown,wman,wcrown,CAPTURE,-1);

    /* second pass: force all positions being seen */
    found+=one_iteration(nr[0],wman,wcrown,bman,bcrown,NORMAL,-1);
    found+=one_iteration(nr[1],bman,bcrown,wman,wcrown,NORMAL,q2);
    validqueue(&q1,&q2);

    do {
      if (qsize[q1]<=0) found+=newfound=one_iteration(nr[0],wman,wcrown,bman,bcrown,NORMAL,q2);
      else found+=newfound=one_iteration_fromqueue(nr[0],wman,wcrown,bman,bcrown,q1,q2,mirror);
      validqueue(&q1,&q2);
      if (newfound==0) break;
      if (qsize[q1]<=0) found+=newfound=one_iteration(nr[1],bman,bcrown,wman,wcrown,NORMAL,q2);
      else found+=newfound=one_iteration_fromqueue(nr[1],bman,bcrown,wman,wcrown,q1,q2,mirror);
      validqueue(&q1,&q2);
    } while(newfound!=0);
  }
  printf("a total of %i positions have been solved\n",found);
  /* wrapping up */
  countvalue(wman,wcrown,bman,bcrown);
  if (mirror==2) countvalue(bman,bcrown,wman,wcrown);

#ifndef PARTIAL
  for(i=0;i<mirror;i++) for(p=0;p<size[i];p++) {
    if (read_value(database[nr[i]],p)==DB_UNKNOWN) 
      store_value(database[nr[i]],p,DB_DRAW);
  }
#endif

  /* write databases */
  for(i=0;i<mirror;i++) {
    printf("writing file '%s'\n",db_file[i]);
    out=fopen(db_file[i],"wb");
    if (out==NULL) {
      printf("fatal: write error\n");
      exit(1);
    }
    fwrite(database[nr[i]],sizeof(unsigned char),bytesize[nr[i]],out);
    fclose(out);
  }
}

void debug(int value,int wman,int wcrown,int bman,int bcrown)
/* prints all position in a database with value 'value'. */
{
  int nr,size,index,i,j,n;
  int list[12],type[12],constraint[12],min[12],max[12];
  char *id;
  int best,known,original,score,nmoves,m;
  id=database_name(wman,wcrown,bman,bcrown);
  printf("debug %s, searching value %i\n",id,value);
  nr=database_nr(white,wman,wcrown,bman,bcrown);
  if (database[nr]==0) {
    printf("database is not present\n");
    return;
  }

  countvalue(wman,wcrown,bman,bcrown);
  n=init_nextboard(list,type,constraint,min,max,wman,wcrown,bman,bcrown);
  i=0;
  do {
    index=database_linear_index(white);
    if (value==DB_VERIFY) {
      original=read_value(database[nr],index);
      best=DB_LOSE; known=true;
      set_pieces();
      nmoves=move_list(0,white);
      for(m=0;m<nmoves;m++) {
	do_move(movelist[0][m]);
	score=database_retreive_value(black,pieces[white|man],pieces[white|crown],pieces[black|man],pieces[black|crown]);
	undo_move(movelist[0][m]);
	if (score==DB_UNKNOWN) known=false;
	if (score==DB_LOSE) {best=DB_WIN; break;}
	if (score==DB_DRAW) best=DB_DRAW;
      }
      if (known==false || best!=original) {
	printf("error found! nr:%i ply0 value:%i ply1 value:%i, index:%i\n",i,original,best,index);
	display_board();
	i++;
      }
    }
    else if (value==DB_ALL || read_value(database[nr],index)==value) {
      display_board(); printf("index: %i nr:%i  value:%i\n",index,i,read_value(database[nr],index));
      i++;
    }
  } while(nextboard(list,type,constraint,min,max,n-1)==false);
  if (value==DB_VERIFY) fprintf(logfile,"verified %s errors:%i\n",id,i);
}

void verify_all(wman,wcrown,bman,bcrown,level)
{
  int wm,wc,bm,bc,nr[2];

  if (level<0) return;
  nr[0]=database_nr(white,wman,wcrown,bman,bcrown);
  nr[1]=database_nr(white,bman,bcrown,wman,wcrown);
  if (verified[nr[0]]==true) return;

  /* first verify all sub-databases */
  for(wm=0;wm<=7;wm++) for(wc=0;wc<=7;wc++)
    for(bm=0;bm<=7;bm++) for(bc=0;bc<=7;bc++)
      if (wm!=wman || bm!=bman || wc!=wcrown || bc!=bcrown) 
	if ((wm+wc)>0 && (bm+bc)>0)
	  if (wm<=wman && bm<=bman && wc<=(wman+wcrown) && bc<=(bman+bcrown))
	    if ((wm+wc)<=(wman+wcrown) && (bm+bc)<=(bman+bcrown))
	    verify_all(wm,wc,bm,bc,level-1);
  debug(DB_VERIFY,wman,wcrown,bman,bcrown);
  if (nr[0]!=nr[1]) debug(DB_VERIFY,bman,bcrown,wman,wcrown);
  verified[nr[0]]=verified[nr[1]]=true;
}

int database_value(int color,int wman,int wcrown,int bman,int bcrown)
/* checks of board[] is in a loaded database. If it is, it returns
   one of:WIN,LOSE or UNKNOWN, seen from the white party. Inputs:
   color: player to move
   wman,wcrown,bman,bcrown: the number of these pieces on the board.
   In general these are known, so it speeds up the access.
*/
{
  int index,score,nr;
  unsigned char *db;

  if (use_db==false) return(UNKNOWN);
  db=database[nr=database_nr(color,wman,wcrown,bman,bcrown)];
  if (db==NULL) return(UNKNOWN);
  index=database_linear_index(color);
  score=read_value(db,index);
  if (score==DB_UNKNOWN) {dbfail++; return(UNKNOWN);}
  ndat++; db_usage[nr]++;
  if (color==white) {
        if (score==DB_DRAW) return(DRAW);
        if (score==DB_WIN) return(WIN);
        if (score==DB_LOSE) return(LOSE);
        }
  else {
        if (score==DB_DRAW) return(DRAW);
        if (score==DB_WIN) return(LOSE);
        if (score==DB_LOSE) return(WIN);
        }
  return(UNKNOWN); /* never get here */
}

int db_main()
{
  logfile=fopen("log","w");
  if (logfile==NULL) {
    printf("error 23\n");
    exit(1);
  }

  init_var();
  init_databases();
  create_database(2,0,2,0,10);
  create_database(3,0,1,0,10);
  verify_all(2,0,2,0,10);
  verify_all(3,0,1,0,10);
  /*verify_all(3,0,1,1,0);*/
  fclose(logfile);
  return(0);
}
