/*
 * by pavle michko, february 2014
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>

int alpha = 5;
typedef unsigned long long seq;
typedef struct _ls_ {
   seq     s;
   int     n;
   int     t;
   struct _ls_ *next;
} enrls, *ls;

typedef struct _lt_ {
   int     t[64];
   int type;
   struct _lt_ *next;
} enrlt, *lt;


ls los = NULL;
lt lot = NULL;

int weight( seq x )
{ int r = 0;
  while ( x ) {
     x &= (x-1);
     r++;
  }
  return r;
}
int acorr( seq x, seq m , int L)
{ seq y;
  int i, w;
  y = x;
  for ( i = 1; i < L ; i++ ) {
     y = ( 2 * y ) % m;
     w = abs( L - 2 * weight( x ^ y ) );
     if ( w > alpha  ) return 0;
  };
  return 1;
}

int cross( seq x, seq z, seq m , int L)
{ seq y;
  int i, w, res = 0;
  y = x;
  for ( i = 0; i < L ; i++ ) {
     y = ( 2 * y ) % m;
     w = abs( L - 2 * weight( z ^ y ) );
     if ( w > res  ) res = w;
  };
  return res;
}
void aval( int t[], seq x, seq m , int L)
{ seq y;
  int i, w;
  y = x;
  for( i = 0; i < L; i++ )
      t[i] = 0;
  for ( i = 1; i < L ; i++ ) {
     y = ( 2 * y ) % m;
     w = weight( x ^ y ) ;
     t[w]++;
  };
}

void print( seq s , int L )
{
int i;
printf("\n");
for( i = 0; i < L; i++ , s >>=1)
   if ( s&1 ) printf(" -"); else printf(" +");
}

int main( int argc, char* argv[] )
{
FILE *src, *dst;
char fn[64], *dir=".";
seq s, m;
int i, L = 0,  opt, num=0, type=0, nb = 0;
ls sx, sy;
lt tx, ty;
int val, beta;
while ((opt = getopt(argc, argv, "d:L:")) != -1) {
               switch (opt) {
               case 'L':
                   L = atoi( optarg );
                   break;
               case 'd':
                   dir = strdup( optarg);
                   break;
               default: /* '?' */
                   fprintf(stderr, "Usage: %s [-directory] [-Length]\n",
                           argv[0]);
                   exit(EXIT_FAILURE);
               }
           }
if ( L == 0 ) {
   printf("\nargument L is missing");
   return 1;
}
  switch ( L % 4 ) {
   case 0 : alpha = 4; break;
   case 1 : alpha = 3; break;
   case 2 : alpha = 2; break;
   case 3 : alpha = 1; break;
  }
  m = (((seq ) 1) << L) - 1; 

sprintf( fn, "%s/opt-%d.dat", dir, L );
src = fopen( fn, "r");
if ( ! src ) {
   fprintf( stderr, "%s not found", fn);
   exit(1);
}
sprintf( fn, "%s/bp-%d.dat", dir, L );
dst = fopen( fn, "a");
while ( ! feof( src ) ) {
   if ( 1 == fscanf( src, "%Ld", &s) ) {
     sx = calloc( 1, sizeof(enrls) );
     sx->s = s;
     sx->n = num;
     tx = calloc( 1, sizeof(enrlt) );
     aval( tx->t, sx->s , m, L ) ;
     sx->next = los;
     los = sx;
     ty = lot;
     while (ty) {
        for( i = 0; i < L && ty->t[i] == tx->t[i]; i++) ;
        if ( i == L ) break;
        ty = ty->next;
     }
     if ( ! ty  ) {
        tx->type = type;
        tx->next = lot;
        lot  = tx;
        sx->t = type;
        type++;
         
     } else free(tx);
     num++;
   }
}
fclose(src); 
fprintf(dst, "\nnumber of optimal sequences : %d", num ); 
fprintf(dst, "\ntypes of autocorrelations   : %d", type );
while ( lot ) {
    fprintf(dst, "\n%3d : ", lot->type);
    for( i = 0; i < L; i++ )
	if ( lot->t[i] ) 
            fprintf(dst, " %3d [%2d]", lot->t[i], L-2*i);
    lot = lot->next;
} 
sy  = los;
beta = L;
while ( sy ) {
    sx = sy->next;
    while ( sx ) {
      val = cross( sy->s, sx->s, m, L );
      if ( val < beta ){
		beta = val;
		nb   = 0;
      }
      if ( val == beta ) nb++;
      sx=sx->next;
    }
    sy = sy -> next;
}
fprintf(dst, "\nbest intercorrelation is    : %d (%.4f)", beta , beta/sqrt(L));
fprintf(dst, "\nnumber of pairs             : %d\n", nb );
fclose(dst);
return 0;
}
