/*
 * 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 "var.h"

#define INDEX int

INDEX combi_array[64][64];
INDEX single[64][64][12];

void init_single(void)
{
  int n,f,lp;
  INDEX index;

  for(n=0;n<8;n++) for(f=0;f<=50;f++) {
    index=0;
    for(lp=0;lp<f;lp++) {
      single[lp][f][n]=index;
      index+=combi_array[f-lp-1][n-1];
    }
  }
}


INDEX fac(int a)
{
  int i;
  INDEX result=1;
  if (a<2) return(1);
  for(i=2;i<=a;i++) result*=i;
  return(result);
}

INDEX combi_calc(int a,int b)
{
  int i;
  INDEX result=1;
  if (2*b<a) b=a-b;
  i=b+1;
  while(i<=a) {result*=i;i++;}
  result/=fac(a-b);
  return(result);
}

INDEX combi(int a,int b)
{
  return(combi_array[a][b]);
}

void init_index()
{
  int a,b;
  for(a=0;a<=50;a++) {
    for(b=0;b<=12 && b<=a;b++) combi_array[a][b]=combi_calc(a,b);
    for(b=a;b>=0 && b>=a-12;b--) combi_array[a][b]=combi_calc(a,b);
  }
  init_single();
}

INDEX db_count(int wman,int wcrown,int bman,int bcrown)
{
  INDEX count=0;
  
  count=combi(50-bman-wman-bcrown,wcrown)*combi(50-bman-wman,bcrown)*combi(45,wman)*combi(45,bman);
  return(count);
}

INDEX index_singletype_old(int n,int offset,int fields,int *pos)
/* pos must be a strictly increasing function, with a maximum of 'fields-1' */
{
  INDEX result=0;
  int i,j,lp;
  
  if (n==0) return(0);
  lp=pos[0]-offset;
  result=single[lp][fields][n];
  result+=index_singletype_old(n-1,pos[0]+1,fields-lp-1,pos+1);  
  return(result);
}

INDEX index_singletype(int n,int offset,int fields,int *pos)
/* pos must be a strictly increasing function, with a maximum of 'fields-1' */
{
  INDEX result=0;
  int i,lp;

  lp=offset-1;
  for(i=0;i<n;i++) {
    lp=pos[i]-lp-1;
    result+=single[lp][fields][n-i];
    fields-=lp+1; lp=pos[i];
  }
  return(result);
}

INDEX index_singletype_slow(int n,int offset,int fields,int *pos)
/* pos must be a strictly increasing function, with a maximum of 'fields-1' */
{
  INDEX result=0;
  int i,j,lp;
  
  if (n==0) return(0);
  lp=pos[0]-offset;
  for(j=0;j<lp;j++) { /* in array ! */
    result+=combi_array[fields-j-1][n-1];
  }
  result+=index_singletype(n-1,pos[0]+1,fields-lp-1,pos+1);  
  return(result);
}

void sort_list(int *a,int n)
{
  int i,j,t;
  for(i=0;i<n-1;i++) for(j=i+1;j<n;j++) if (a[i]>a[j]) {
    t=a[i];a[i]=a[j];a[j]=t;
  }
}
    
#define wc (white|crown)
#define bc (black|crown)
#define wm (white|man)
#define bm (black|man)

int database_linear_index(int color)
/* reads the 'board' array and determens the index function (not in bytes)
   method a la Wiesenekker
*/
{
  int pos[12]={0,0,0,0,0,0,0,0,0,0},val[12];
  int p,i,j,n=0,temp;
  int type[12];
  int index,jumpover;
  int np[8]={0,0,0,0,0,0,0,0},list[8][12];
  int decrease[8][12];

  int p1,p2,p3;

  if (color==white) for(p=0;p<50;p++) switch(board[map[p]]) {
  case white|man:{
    list[wm][np[wm]++]=p; break;}
  case white|crown:{
    list[wc][np[wc]++]=p;break;}
  case black|man:{
    list[bm][np[bm]++]=p;break;}
  case black|crown:{
    list[bc][np[bc]++]=p;break;}
  }
  else for(p=0;p<50;p++) switch(board[map[p]]) {
  case black|man:{
    list[wm][np[wm]++]=49-p;break;}
  case black|crown:{
    list[wc][np[wc]++]=49-p;break;}
  case white|man:{
    list[bm][np[bm]++]=49-p;break;}
  case white|crown:{
    list[bc][np[bc]++]=49-p;break;}
  }

  sort_list(list[wm],np[wm]);
  sort_list(list[bm],np[bm]);
  sort_list(list[wc],np[wc]);
  sort_list(list[bc],np[bc]);

  p3=combi_array[50-np[bm]-np[wm]-np[bc]] [np[wc]];
  p2=p3*combi_array[50-np[bm]-np[wm]] [np[bc]];
  p1=p2*combi_array[45] [np[wm]];

  /* set jumpovers white man */
  for(i=0;i<np[wm];i++) {
    jumpover=0;
    for(j=0;j<np[bm];j++) if (list[bm][j]<list[wm][i] && list[bm][j]>=5) jumpover++;
    decrease[wm][i]=jumpover;
  }
  /* set jumpovers black crown */
  for(i=0;i<np[bc];i++) {
    jumpover=0;
    for(j=0;j<np[bm];j++) if (list[bm][j]<list[bc][i]) jumpover++;
    for(j=0;j<np[wm];j++) if (list[wm][j]<list[bc][i]) jumpover++;
    decrease[bc][i]=jumpover;
  }
  /* set jumpovers white crown */
  for(i=0;i<np[wc];i++) {
    jumpover=0;
    for(j=0;j<np[bm];j++) if (list[bm][j]<list[wc][i]) jumpover++;
    for(j=0;j<np[wm];j++) if (list[wm][j]<list[wc][i]) jumpover++;
    for(j=0;j<np[bc];j++) if (list[bc][j]<list[wc][i]) jumpover++;
    decrease[wc][i]=jumpover;
  }
  /* apply decreases */
  for(i=0;i<np[bc];i++) list[bc][i]-=decrease[bc][i];
  for(i=0;i<np[wc];i++) list[wc][i]-=decrease[wc][i];
  for(i=0;i<np[wm];i++) list[wm][i]-=decrease[wm][i]+5;
  
  index=index_singletype(np[bm],0,45,list[bm])*p1;
  index+=index_singletype(np[wm],0,45,list[wm])*p2;
  index+=index_singletype(np[bc],0,50-np[bm]-np[wm],list[bc])*p3;
  index+=index_singletype(np[wc],0,50-np[bm]-np[wm]-np[bc],list[wc]);
  return(index);
}
#undef wm
#undef wc
#undef bm
#undef bc


















