#define  VERSION     "version 20260417"
#define  TIME_STAMP  "10:18 UTC+2"
#include <stdio.h>
#include "no3in_functions.h"
#define CHA2U(zei)  ( (zei)<' '||(zei)>=127 ? xX : char_to_val[zei-' '] )
const char  *_symclass_[NO_SYM+1]= { SYMMETRY_ABBREV };  // last is null-pointer
const char  char_to_val[]= { xX,68,xX,62,63,64,65,xX,69,70,78,79,88,81,89,82,\
                             0,1,2,3,4,5,6,7,8,9,86,87,73,77,74,67,66,10,11,\
                             12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,\
                             28,29,30,31,32,33,34,35,71,xX,72,84,85,xX,36,37,\
                             38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,\
                             54,55,56,57,58,59,60,61,75,80,76,83,xX } ;


inline const char *version()
{
   return  VERSION "_" TIME_STAMP;
}


inline const char *alphabet()
{
   return  OLDPOS NEWPOS;
}


int  set_displaystring(char *string, unsigned no)
{
   if (no >= 16)
      return -1;
   else
      display_str[no]= string;
   return 0;
}


inline const char *symclass(const unsigned i)
{
   return  _symclass_[i < NO_SYM ? i : NO_SYM];
}


inline int char2val(const unsigned char z)
{
   return  z < ' ' || z >= 127 ? xX : char_to_val[z-' '] ; 
}


const unsigned symmetries(unsigned n, const unsigned grid[MAX_N][MAX_N])
{
unsigned  h, i, j, k, ii, jj,
          code = VERT | HORI | ROTA | LEFT | RIGH | ANTI | MAIN | IDEN ;
#ifdef  NEW_LEXICO_NORMALIZE
   if (lexicon_n != n)
      std_lexi_order(n,1);
   else
      reset_coord(n);
   for (h=0;h<n*n;h++)
   {  next_coord(&i,&j);
      ii= n-1-i;
      jj= n-1-j;
      if (grid[i][j])
      {
         if ((code&VERT) && !grid[ii][j])   code &= ~VERT;
         if ((code&HORI) && !grid[i][jj])   code &= ~HORI;
         if ((code&ROTA) && !grid[ii][jj])  code &= ~ROTA;
         if ((code&LEFT) && !grid[j][ii])   code &= ~LEFT;
         if ((code&RIGH) && !grid[jj][i])   code &= ~RIGH;
         if ((code&ANTI) && !grid[jj][ii])  code &= ~ANTI;
         if ((code&MAIN) && !grid[j][i])    code &= ~MAIN;
         if (code==IDEN)  return  IDEN;
      }
      else
      {
         if ((code&VERT) && grid[ii][j])   return  0;
         if ((code&HORI) && grid[i][jj])   return  0;
         if ((code&ROTA) && grid[ii][jj])  return  0;
         if ((code&LEFT) && grid[j][ii])   return  0;
         if ((code&RIGH) && grid[jj][i])   return  0;
         if ((code&ANTI) && grid[jj][ii])  return  0;
         if ((code&MAIN) && grid[j][i])    return  0;
      }
   }
#else
   for (k=1+(n&1);k<=n;k+=2)
   {  int  r, s;
      i = j = n/2;
      i -= k/2;
      r = -(k/2);
      s = 0;
      if (!(n&1))
      {  i--;  j--;
         if (!r)  s = 1;  // s=k/2;
      }
      ii = n-1-i;
      jj = n-1-j;
      for (h=4*k;h;h--)
      {  if (grid[i][j])
         {
            if ((code&VERT) && !grid[ii][j])   code &= ~VERT;
            if ((code&HORI) && !grid[i][jj])   code &= ~HORI;
            if ((code&ROTA) && !grid[ii][jj])  code &= ~ROTA;
            if ((code&LEFT) && !grid[j][ii])   code &= ~LEFT;
            if ((code&RIGH) && !grid[jj][i])   code &= ~RIGH;
            if ((code&ANTI) && !grid[jj][ii])  code &= ~ANTI;
            if ((code&MAIN) && !grid[j][i])    code &= ~MAIN;
            if (code==IDEN)  return  IDEN;
         }
         else
         {
            if ((code&VERT) && grid[ii][j])   return  0;
            if ((code&HORI) && grid[i][jj])   return  0;
            if ((code&ROTA) && grid[ii][jj])  return  0;
            if ((code&LEFT) && grid[j][ii])   return  0;
            if ((code&RIGH) && grid[jj][i])   return  0;
            if ((code&ANTI) && grid[jj][ii])  return  0;
            if ((code&MAIN) && grid[j][i])    return  0;
         }
         if (r>0)       {  j++;  r--;  jj--;  if (!r)  s = -k;  }
         else if (r<0)  {  j--;  r++;  jj++;  if (!r)  s = k;  }
         else if (s>0)  {  i++;  s--;  ii--;  if (!s)  r = k;  }
         else if (s<0)  {  i--;  s++;  ii++;  if (!s)  r = -k;  }
      }
   }
#endif
   return  code;
}


int normalize(unsigned n, unsigned x[2*MAX_N], unsigned y[2*MAX_N])
{
unsigned  i, j, h, k, anz, grid[MAX_N][MAX_N];
   if (n > MAX_N)
      return  -1;
   for (k=0;k<2*n;k++)
      if (x[k] == ~0 || y[k] == ~0)
         break;
   anz= k;
   for (h=0;h<8;h++)
   {  for (i=0;i<n;i++)
      for (j=0;j<n;j++)
         grid[i][j]= 0;
      for (k=0;k<anz;k++)
         grid[ y[k] ][ x[k] ]= 1;
      anz= k;
      if (symmetries(n,grid))
         break;
      for (k=0;k<anz;k++)
      {  // either quarter rotation or (each 4th time) a diagonal reflection
         i= x[k];
         x[k]= y[k];
         y[k]= (h&3)==3 ? i : n-1-i;
      }
   }
   return h;
}


int encode(unsigned n, const unsigned x[2*MAX_N], const unsigned y[2*MAX_N],
           char buffer[2*MAX_N+4], unsigned lz)
{  unsigned  h, i, j, k, anz, grid[MAX_N][MAX_N];
   if (lz >= NO_SYM)
      lz=  NO_SYM-1;
   if (n > MAX_N)
      return  -1;
   for (k=0;k<2*n;k++)
      if (x[k] == ~0 || y[k] == ~0)
         break;
   anz= k;
// if (anz&1)
//    return  -4;
   for(i=0;i<n;i++)
      for(j=0;j<n;j++)
         grid[i][j]= 0;
   for(k=0;k<anz;k++)
      grid[y[k]][x[k]]= 1;
   buffer[0]= SYMM[lz];
   for(k=1,i=0;i<n;i++)
   {  for(h=j=0;j<n;j++)
         if (grid[i][j])
         {  h++;
            buffer[k++]= OLDPOS NEWPOS [j];
         }
      if (h > 2)
         return  -6;
      while (h++ < 2)
         buffer[k++]= IGN;
   }
   buffer[k] ='\0';
   return  0;
}


int decode(const char buffer[2*MAX_N+4], unsigned x[2*MAX_N],
           unsigned y[2*MAX_N], unsigned *leadin)
{  unsigned  h, i, j, n, c[MAX_N];
   for (i=0;buffer[i]>' '&&i<2*MAX_N+4;i++);
   for (h=0;SYMM[h];h++)
      if (buffer[0]==SYMM[h])
         break;
   *leadin= ((i&1) && SYMM[h]);
   i-= *leadin;
   if (i > 2*MAX_N)
      return  -1;
   if (i&1)
      return  -4;
   n= i/2;
   for (h=0;h<MAX_N;h++)
      c[h]= 0;
   for (j=h=0;h<i;h++)
   {  y[j]= h/2;
      if (buffer[h+ *leadin] == IGN)
         continue;
      x[j]= (*leadin ? CHA2U(buffer[h+ *leadin]) : Topos(buffer[h+ *leadin]) );
      if (x[h] == xX)
         return  -2;
      if (x[j] >= n)
         return  -3;
      c[ x[j] ]++;
      j++;
   }
   for (;j<i;j++)
      x[j]= y[j]= ~0;
   for (h=0;h<n;h++)
      if (c[h] > 2)
         return  -6;
   return  n;
}


int check_validity(unsigned n, const unsigned x[2*MAX_N],
                   const unsigned y[2*MAX_N])
{
unsigned  h, i, j, k, anz;
   for (k=0;k<2*n;k++)
      if (x[k] == ~0 || y[k] == ~0)
         break;
   anz= k;
   h= 0;
   for(i=0;i<anz;i++)
      for(j=i+1;j<anz;j++)
         for(k=j+1;k<anz;k++)
            h += (x[j]-x[i])*(y[k]-y[i]) == (x[k]-x[i])*(y[j]-y[i]);
   return h;
}


int  check_code_plausible(const char buffer[2*MAX_N+4])
{  unsigned  h, anz, leadin, c[MAX_N];
   for (h=0;buffer[h];h++)
      if (buffer[h] <= ' ')
         break;
   leadin= h&1;
   anz = h-leadin;
   for (h=0;h<MAX_N;h++)
      c[h]= 0;
   for (h=0;h<anz;h++)
      if (buffer[h+leadin] != IGN)
         c[ leadin ? CHA2U(buffer[h+leadin]) : Topos(buffer[h+leadin]) ]++;
   for (h=0;h<anz/2;h++)
      if (c[h] > 2)
         return  -2;
   for (;h<MAX_N;h++)
      if (c[h])
         return  -1;
   return  anz/2;
}


const unsigned symmetry_index(unsigned n, const unsigned x[2*MAX_N],
                              const unsigned y[2*MAX_N], char *str)
{
   int  h, i, j, k, a, b, c;
   unsigned  anz, sym, sym_all, flag;
   unsigned  grid[MAX_N][MAX_N];
   for (k=0;k<2*n;k++)
      if (x[k] == ~0 || y[k] == ~0)
         break;
   anz= k;
   for(i=0;i<n;i++)
      for(j=0;j<n;j++)
         grid[i][j]= 0;
   for(k=0;k<anz;k++)
      grid[x[k]][y[k]] = 1;
   sym_all = 255;
   for(k=0;k<anz;k++)
   {  sym= 0;
      a = x[k];
      b = y[k];
      for(h=0;h<4;h++)
      {  sym <<=1;
         c = n-1-a;
         sym += grid[c][b];
         sym<<=1;
         sym += grid[b][c];
         a = b,   b = c;
      }
      if ((n&1) && (x[k]==y[k] || x[k] == n-1-y[k]))
         sym |= 68;
      sym_all &= sym ;
   }
   flag= 9;
   if (sym_all ==  1)  flag= 0;
   if (sym_all ==  3)  flag= 2;
   if (sym_all ==  9)  flag= 3;
   if (sym_all == 17)  flag= 1;
   if (sym_all == 33)  flag= 2;
   if (sym_all == 51)  flag= 5;
   if (sym_all == 85)  flag= n&1?8:4;
   if (sym_all ==119)  flag= 5;
   if (sym_all ==129)  flag= 3;
   if (sym_all ==153)  flag= 6;
   if (sym_all ==197)  flag= 3;   // new
   if (sym_all ==255)  flag= 7;
   if (str)
   {  switch(flag)
      {  case 0: h=4; break;
         case 1: h=7; break;
         case 2: h= (sym_all==3?5:8); break;
         case 3: h= (sym_all==9?6:12); break;
         case 4: h=11; break;
         case 5: h=9; break;
         case 6: h=13; break;
         case 7: h=14; break;
         case 8: h=10; break;
         default: h=15; break;
      }
      sprintf(str,"%s",display_str[h]);
   }
   if (flag==9)
      fprintf(stderr,"error in symmetry_index:  unknown sym_all=%u\n",sym_all);
   return  flag;
}


int print_config(unsigned anz, const char buffer[2*MAX_N+4], unsigned no,
                  char FREE, char MARK, FILE *fp)
{
unsigned  i, j, h, k, leadin;
char  line[2*MAX_N+2];
   leadin= anz&1;
   i= anz-leadin;
   if (i > 2*MAX_N)
      return  -1;
   for (h=j=0;j<i;j++)
      h += (buffer[j+leadin] == IGN);
#ifdef  SIMPLE
   fprintf(fp,"%s%2d\n",display_str[2],i/2);
#else
   fprintf(fp,"%3u. %s",no,display_str[0]);
   fprintf(fp," (%s%u) ",display_str[2],i/2);
   for (j=0;j<NO_SYM-1;j++)
      if (buffer[0]==SYMM[j])
         break;
   if (j != NO_SYM-1)
      fprintf(fp,"     %s  %s",display_str[1],symclass(j));
   if (h)
      fprintf(fp,"    %s%2d",display_str[3],h);
   fprintf(fp,"\n");
#endif
   for (j=0;j<i;j++)
      line[j] = ((j&1) ? ' ' : FREE);
   line[j] = '\n';
   line[j+1] = '\0';
   h= k= 0;
   for (j=0;j<i;j++)
   {  if (buffer[j+leadin] != IGN)
      {  h = (leadin ? CHA2U(buffer[j+leadin]) : Topos(buffer[j+leadin]) );
         line[h+h] = MARK;
      }
      j++;
      if (buffer[j+leadin] != IGN)
      {  k = ( leadin ? CHA2U(buffer[j+leadin]) : Topos(buffer[j+leadin]) );
         line[k+k] = MARK;
      }
      fprintf(fp," %s",line);  /* must start with SPACE for compatibility */
      line[h+h] = FREE;
      line[k+k] = FREE;
   }
   return 0;
}


int  print_grid(unsigned n, const unsigned x[2*MAX_N], const unsigned
                 y[2*MAX_N], unsigned no, char FREE, char MARK, FILE *fp)
{
unsigned  h, i, j, k, anz, grid[MAX_N][MAX_N];
char  line[2*MAX_N+2];
   if (n > MAX_N)
      return -1;
   for (k=0;k<2*n;k++)
      if (x[k] == ~0 || y[k] == ~0)
         break;
   anz= k;
   h= 2*n-anz;
   for (i=0;i<n;i++)
   for (j=0;j<n;j++)
      grid[i][j]= 0;
   for (k=0;k<anz;k++)
      grid[ y[k] ][ x[k] ]= 1;

#ifdef  SIMPLE
   fprintf(fp,"%s%2d\n",display_str[2],n);
#else
   fprintf(fp,"%3u. %s",no,display_str[0]);
   fprintf(fp," (%s%u) ",display_str[2],n);
   j= symmetry_index(anz,x,y,(char*)0);
   if (j < NO_SYM-1)
      fprintf(fp,"     %s  %s",display_str[1],symclass(j));
   if (h)
      fprintf(fp,"%s%2d",display_str[3],h);
   fprintf(fp,"\n");
#endif
   for (j=0;j<anz;j++)
      line[j] = ((j&1) ? ' ' : FREE);
   line[j] = '\n';
   line[j+1] = '\0';
   for (i=0;i<n;i++)
   {  for (j=0;j<n;j++)
         if (grid[i][j])
            line[j+j] = MARK;
      fprintf(fp," %s",line);  /* must start with SPACE for compatibility */
      for (j=0;j<n;j++)
         if (grid[i][j])
            line[j+j] = FREE;
   }
   return 0;
}


int read_grid(FILE *fp, unsigned x[2*MAX_N], unsigned y[2*MAX_N], char FREE, char MARK)
{
unsigned  i, j, anz;
char  line[2*MAX_N+2];
   anz= 0;
   for (i=0; fgets(line,2*MAX_N+2,fp) ;i++)
   {
      for (j=0;line[j]&&line[j]!='\n';j++)
      {
         if (j&1)
         {  if (line[j] == MARK)
               x[anz]=i,  y[anz]=j,  anz++;
            else if (line[j] != FREE)
               return -i-1;
         }
         else if (line[j] != ' ')
            return -i-1;
      }
      if (line[j])
         return -i-1;
   }
   return  anz;
}


const unsigned  get_next_coded_config(FILE *fp, char buffer[2*MAX_N+4])
{
#ifdef  NEW_READ
int  i, j;
   while ((j=fgetc(fp))<=' ');
   if (j < 0 || j == EOF)
      return  0;
   buffer[i=0]= j;
   while ((j=fgetc(fp))>' ' && i<2*MAX_N)
      buffer[++i]= j;
   buffer[++i]= '\0';
   return  i;
#else
#define STR1(x)  #x
#define STR(x)  STR1(x)
unsigned  i, j;

// if (1 != fscanf(fp,"%" STR(2*MAX_N+4) "s",buffer))
   if (!fgets(buffer,2*MAX_N+4,fp))
      return  0;
   for (i=0;buffer[i] && buffer[i]<=' ';i++);
   if (i)
      for (j=i;(buffer[i-j]=buffer[i])>' ';i++);
   else
      for (j=i;buffer[i]>' ';i++);
   buffer[i-j]='\0';
   return  i-j;
#undef  STR
#undef  STR1
#endif
}


void  reset_coord(unsigned n)
{
   next_coord_index= 0;
   coord_indices= n*n;
}


int  std_lexi_order(unsigned n, unsigned no)
{
unsigned  h, k, i, j, m;
   if (n >  MAX_N)
      return  -1;
   lexicon_n = n;
// k= (n+(n&1))/2;
// k= (k*(k+1))/2;
   if (no >= 2)
      return -2;
   switch (no) 
   {  case 0:  i= j= 0;
               for (k=0;i<n;k++,j=(++j==n?i++,0:j))
               {  lexicon[k].i = i;
                  lexicon[k].j = j;
               }
               break; 
      case 1:  k= 0;
               for (m=1+(n&1);m<=n;m+=2)
               {  int  r, s;
                  i= j= n/2;
                  i -= m/2;
                  r= -(m/2);
                  s= 0;
                  if (!(n&1))
                  {  i--;  j--;
                     if (!r)  s= 1; // s= m/2;
                  }
                  for (h=4*m;h;h--)
                  {  lexicon[k].i= i;
                     lexicon[k].j= j;
                     k++;
                     if (r>0)
                     {  j++;  r--;  if (!r)  s= -m;  }
                     else if (r<0)
                     {  j--;  r++;  if (!r)  s= m;  }
                     else if (s>0)
                     {  i++;  s--;  if (!s)  r= m;  }
                     else if (s<0)
                     {  i--;  s++;  if (!s)  r= -m;  }
                  }
               }
               break; 
   }
   reset_coord(n);
   return  k; 
}


int  set_lexi_order(unsigned n, const int i[MAX_LEX], const int j[MAX_LEX])
{
unsigned  h, k, m;
   if (n >  MAX_N)
      return  -1;
   lexicon_n = n;
   k= n*n;
   for (h=0;h<k;h++)
   {  if (i[k] < 0 || i[k] > MAX_N/2 || j[k] < 0 || j[k] > MAX_N/2 || i[k] > j[k])
         return  -2-2*h;
      lexicon[k].i = i[k];
      lexicon[k].j = j[k];
      for (m=0;m<h;m++)
         if (lexicon[m].i == i[k]  &&  lexicon[m].j == j[k])
            return  -1-2-2*h;
   }
   reset_coord(n);
   return  k; 
}


int  get_lexi_order(unsigned *n, int i[MAX_LEX], int j[MAX_LEX])
{
unsigned  h, k;
   *n= lexicon_n;
   k= *n * *n;
   for (h=0;h<k;h++)
   {  i[k]= lexicon[k].i;
      j[k]= lexicon[k].j;
   }
   return  k; 
}


int  next_coord(int *i, int *j)
{
   if (next_coord_index >= coord_indices)
      return  -1;
   *i= lexicon[next_coord_index].i;
   *j= lexicon[next_coord_index].j;
   next_coord_index++;
   return 0;
}
