/*
 * lfsr.c
 *
 */


#include <stdio.h>
#include "datatypes.h"

uint32_t 
parity(uint32_t x) {

  x ^= (x >> 16);
  x ^= (x >> 8);
  x ^= (x >> 4);
  x ^= (x >> 2);
  x ^= (x >> 1);

  return x & 1;
}


/* typedef struct { */
/*   uint32_t register[8]; */
/* } lfsr_t; */

void
compute_period(uint32_t feedback_polynomial) {
  int i;
  v32_t lfsr;
  v32_t mask;

  mask.value = feedback_polynomial;
  lfsr.value = 1;

  printf("polynomial: %s\t", v32_bit_string(mask));

  for (i=0; i < 256; i++) {
/*     printf("%s\n", v32_bit_string(lfsr)); */
    if (parity(mask.value & lfsr.value))
      lfsr.value = ((lfsr.value << 1) | 1) & 0xff;
    else
      lfsr.value = (lfsr.value << 1) & 0xff;
    
    /* now halt if we're back at the initial state */
    if (lfsr.value == 1) {
      printf("period: %d\n", i);
      break;
    }
  }
}

uint32_t poly0 = 223;


uint32_t polynomials[39] = {
31, 
47,
55,
59,
61,
79,
87,
91,
103,
107,
109,
115,
117,
121,
143,
151,
157,
167,
171,
173,
179,
181,
185,
199,
203,
205,
211,
213,
227,
229,
233, 
241,
127,
191,
223, 
239,
247,
251,
253 
};

char binary_string[32];

char *
u32_bit_string(uint32_t x, unsigned int length) {
  unsigned int mask;
  int index;
 
  mask = 1 << length;
  index = 0;
  for (; mask > 0; mask >>= 1)
    if ((x & mask) == 0)
      binary_string[index++] = '0';
    else
      binary_string[index++] = '1';

  binary_string[index++] = 0;  /* NULL terminate string */
  return binary_string;
}

extern int octet_weight[256];

unsigned int 
weight(uint32_t poly) {
  int wt = 0;

  /* note: endian-ness makes no difference */
  wt += octet_weight[poly        & 0xff]; 
  wt += octet_weight[(poly >> 8) & 0xff];
  wt += octet_weight[(poly >> 16) & 0xff];
  wt += octet_weight[(poly >> 24)];

  return wt;
}

#define MAX_PERIOD 65535

#define debug_print 0

int
period(uint32_t poly) {
  int i;
  uint32_t x;


  /* set lfsr to 1 */
  x = 1;
#if debug_print
  printf("%d:\t%s\n", 0, u32_bit_string(x,8));
#endif
  for (i=1; i < MAX_PERIOD; i++) {
    if (x & 1) 
      x = (x >> 1) ^ poly;
    else
      x = (x >> 1);

#if debug_print
    /* print for a sanity check */
    printf("%d:\t%s\n", i, u32_bit_string(x,8));
#endif

    /* check for return to original value */
    if (x == 1)
      return i;
  }
  return i;
}

/*
 * weight distribution computes the weight distribution of the
 * code generated by the polynomial poly
 */

#define MAX_LEN    8
#define MAX_WEIGHT (1 << MAX_LEN)

int A[MAX_WEIGHT+1];

void
weight_distribution2(uint32_t poly, int *A) {
  int i;
  uint32_t x;

  /* zeroize array */
  for (i=0; i < MAX_WEIGHT+1; i++)
    A[i] = 0;

  /* loop over all input sequences */
  
  
  /* set lfsr to 1 */
  x = 1;
#if debug_print
  printf("%d:\t%s\n", 0, u32_bit_string(x,8));
#endif
  for (i=1; i < MAX_PERIOD; i++) {
    if (x & 1) 
      x = (x >> 1) ^ poly;
    else
      x = (x >> 1);

#if debug_print
    /* print for a sanity check */
    printf("%d:\t%s\n", i, u32_bit_string(x,8));
#endif
    
    /* increment weight */
    wt += (x & 1);

    /* check for return to original value */
    if (x == 1)
      break;
  }

  /* set zero */
  A[0] = 0;
}


void
weight_distribution(uint32_t poly, int *A) {
  int i;
  uint32_t x;

  /* zeroize array */
  for (i=0; i < MAX_WEIGHT+1; i++)
    A[i] = 0;

  /* set lfsr to 1 */
  x = 1;
#if debug_print
  printf("%d:\t%s\n", 0, u32_bit_string(x,8));
#endif
  for (i=1; i < MAX_PERIOD; i++) {
    if (x & 1) 
      x = (x >> 1) ^ poly;
    else
      x = (x >> 1);

#if debug_print
    /* print for a sanity check */
    printf("%d:\t%s\n", i, u32_bit_string(x,8));
#endif

    /* compute weight, increment proper element */
    A[weight(x)]++;

    /* check for return to original value */
    if (x == 1)
      break;
  }

  /* set zero */
  A[0] = 0;
}




int
main () {

  int i,j;
  v32_t x;
  v32_t p;

  /* originally 0xaf */
  p.value = 0x9;

  printf("polynomial: %s\tperiod: %d\n",  
 	   u32_bit_string(p.value,8), period(p.value)); 
 
   /* compute weight distribution */
  weight_distribution(p.value, A);
  
  /* print weight distribution */
  for (i=0; i <= 8; i++) {
    printf("A[%d]: %d\n", i, A[i]);
  }
  
#if 0
  for (i=0; i < 39; i++) {
     printf("polynomial: %s\tperiod: %d\n",  
 	   u32_bit_string(polynomials[i],8), period(polynomials[i])); 
   
     /* compute weight distribution */
     weight_distribution(p.value, A);
     
     /* print weight distribution */
     for (j=0; j <= 8; j++) {
       printf("A[%d]: %d\n", j, A[j]);
     }   
  }
#endif

  { 
    int bits = 8;
    uint32_t y;
    for (y=0; y < (1 << bits); y++) {
      printf("polynomial: %s\tweight: %d\tperiod: %d\n", 
	     u32_bit_string(y,bits), weight(y), period(y));
      
      /* compute weight distribution */
      weight_distribution(y, A);
      
      /* print weight distribution */
      for (j=0; j <= 8; j++) {
	printf("A[%d]: %d\n", j, A[j]);
      }     
    }
  }

  return 0;
}