/* Correction du TD 2 */

#include <stdlib.h> /* definition de NULL */
#include <stdio.h>  /* definition de printf, putchar */
#include <string.h> /* definition de strlen */
#include <time.h>   /* definition de time */
#include <limits.h> /* definition de CHAR_BIT */
#include <unistd.h> /* definition de size_t*/

/* Exercice 1.1
   La fonction apply_key applique la cle au message et modifie celui-ci. */

void apply_key(char *message, size_t size_m, const char *key, size_t size_k){
  size_t i,j;
  for(i=0, j=0 ; i<size_m ; i++, j=(j+1)%size_k)
    message[i]^=key[j];
}

/* Generation "aleatoire" d'une cle*/
void generate_key(unsigned char *key, size_t size_k){
  size_t i;
  srand(time(NULL));
  for(i=0; i< size_k; i++)
    key[i]=(unsigned char)(rand()%1<<CHAR_BIT);
}

/* fonction qui affiche prudemment une chaine de caractere;
   si le caractere est "bizarre", elle affiche son code */
void careful_display(const char *string, size_t length){
  size_t i;
  for(i=0; i< length; i++)
    if(string[i]>31)
      putchar(string[i]);
    else
      printf("[%d]",string[i]);
  putchar('\n');
}

/* Exercice 1.2
*/

int crypt(int argc, char **argv){
  char key[12];
  size_t length;
  generate_key((unsigned char*)key,12);
  if(argc!=2){
    fprintf(stderr, "Usage: crypt message\n");
    return 1;
  }
  length=strlen(argv[1]);
  printf("Avant codage:\n%s\n",argv[1]);
  apply_key(argv[1], length, key, 12);
  printf("Après codage:\n");
  careful_display(argv[1], length);
  apply_key(argv[1], length, key, 12);
  careful_display(argv[1], length);
  return 0;
}
 
/*** Exercice 2 ****/

#define N 1000u


/*Dans les fonctions suivantes, on suppose les index valides
  La fonction get_bit retourne 0 si le i-ème bit est nul et
  un nombre non nul sinon.  
*/
int get_bit(const unsigned char *tab, int i){
  return (tab[i/8]& (1<<(i%8)));
}

void set_bit(unsigned char *tab, int i){
  tab[i/8] |= (1<<(i%8)) ;
}

/*Dans cette fonction, on teste si n est premier,
  et on remplit tab*/
int is_n_prime(int n, unsigned char *tab){
  if( get_bit( tab, n) )
    return 0;
  else{
    int i;
    for(i=n*n; i<N; i+=n)
      set_bit(tab, i);
    return 1;
  }
}

void display_primes_until_N(){
  int i;
  static unsigned char tab[N/CHAR_BIT+1];
  for(i=2; i<N; i++)
    if(is_n_prime(i, tab))
      printf("%d ",i);
  putchar('\n');
}
/*
int main(){
  display_primes_until_N();
  return 0;
}
*/
#undef N

/***  Exercice 3 ****/

void uchar_to_bin(unsigned char c){
  unsigned char mask = 1<<(CHAR_BIT-1);
  for( ; mask ; mask>>=1 )
    if(c & mask)
      putchar('1');
    else
      putchar('0');
}

void uchar_to_bin_sep(unsigned char c, int sep){
  unsigned char mask = 1<<(CHAR_BIT-1);
  int i;
  for(i=0 ; mask ; i++,mask>>=1 ){
    if(i==sep)
      putchar('|');
    if(c & mask)
      putchar('1');
    else
      putchar('0');
  }
  if(sep==8)
      putchar('|');    
}

int is_little_indian(){
  static const int t=1;
  return *((char*)&t);
}

/* le tableau sep contient les positions des '|' que l'on doit ecrire,
   dans l'ordre, avec au plus 1 '|' par octet et -1 pour marquer la fin du
   tableau */

void var_to_bin(void* v, size_t size, const int * sep){
  int s=0, a, b;
  size_t i;
  unsigned char *c=v;
  if(is_little_indian()){
    a=size-1; b=-1;
  }
  else{
    a=0; b=1;
  }
  for(i=0; i<size; i++){
    if(sep[s]>=0 && sep[s]/CHAR_BIT==i)
      uchar_to_bin_sep(c[a+b*i], sep[s++]%CHAR_BIT);
    else
      uchar_to_bin(c[a+b*i]);
  }
  putchar('\n');
}

#define SEPARATOR(NOM, TYPE)        \
int separator_##NOM(){              \
  TYPE x=1.;                        \
 int a,b,i;                         \
  if(is_little_indian()){           \
    a=sizeof(x)*CHAR_BIT-1; b=-1;   \
  }                                 \
  else{                             \
    a=0; b=1;                       \
  }                                 \
  for(i=0; !get_bit((char*)&x,a+b*i); i++); \
  for(; get_bit((char*)&x,a+b*i); i++);     \
  return i;                         \
}

SEPARATOR(float,float)
SEPARATOR(double,double)
SEPARATOR(long_double,long double)
#undef SEPARATOR

#define REAL_TO_BIN(NOM, TYPE)        \
void NOM##_to_bin(TYPE x){            \
  int t[]={1,separator_##NOM(),-1};   \
  var_to_bin(&x,sizeof(x),t);         \
}

REAL_TO_BIN(float,float)
REAL_TO_BIN(double,double)
REAL_TO_BIN(long_double,long double)

#undef REEL_TO_BIN

int main(){
  float_to_bin(1.f);
  double_to_bin(1.);
  long_double_to_bin(1.l);
  float_to_bin(.3f);
  double_to_bin(.3);
  long_double_to_bin(.3l);
  return 0;
}
