/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2006
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    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. See the GNU
    General Public License for more details.
  
    You should have received a copy of the GNU General Public License
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/

/* midnightcode-random.c :: Midnight Code random library		*/


#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

#include "preferences.h"
#include "midnightcode-random.h"



unsigned int
midnightcode_random_randmax(unsigned int max) {
  unsigned int ret_i;


  if(max == 0xFFFFFFFF || max == 0) {
    ret_i = rand();
  } else {
    ret_i = (rand() % max) + 1;
  }

  return ret_i;
}



unsigned int
midnightcode_random_seed(int seed_opt) {
  unsigned int seeder;
  unsigned int seeds;
  unsigned int seed;


  seed = 0;

  if(seed_opt)
    midnightcode_random_init(0);

  seeder = 0;
  while(!seeder)
    seeder = midnightcode_random_randmax(0xFFFFFFFF);
  seeder = midnightcode_random_randmax(64);

  for(seeds = 0; seeds < seeder; seeds++)
    seed = midnightcode_random_randmax(0xFFFFFFFF);

  return seed;
}



int
midnightcode_random_ent_complex(unsigned int * table, int len,
                                unsigned int seed) {
  unsigned int random;
  unsigned int result;
  int offset;


  if(len < 1)
    return -1;

  random = seed;
  result = 0;

                   /* Copyright (C) 2004 Eric Uner */
  for(offset = 0; offset < len; offset++) {  
    if(random == 0 || random == 1)
      random = seed;
    random = (9973 * ~random) + ((result) % 701);
    result = (random >> 24) % 9;
    table[offset] = result;
  }

  return offset;
}



/* NOTE: Entropy = 0.999306 bits per bit  */
unsigned int
midnightcode_random_gen_basic(unsigned int * buffer, int len,
                                unsigned int useed) {
  unsigned int offset;
  unsigned int seeder;
  unsigned int seeds;
  unsigned int seed;


  if(len < 1)
    return -1;
  if(useed < 1)
    useed = 11;

  seed = 0;
  for(offset = 0; (int)offset < len; offset++) {  
    seeder = midnightcode_random_randmax(useed);
    for(seeds = 0; seeds < seeder; seeds++)
      seed = midnightcode_random_randmax(0xFFFFFFFF);
    buffer[offset] = seed;
  }

  /* TODO: Add SHA1 checksum for FIPS recommended random output */
  return offset;
}



/* NOTE: Entropy = 0.948022 bits per bit  */
unsigned int
midnightcode_random_gen_complex(unsigned int * buffer, int len,
                                unsigned int useed) {
  unsigned int * table;
  int table_max;
  unsigned int seed;
  unsigned int offset;
  unsigned int random;
  unsigned int pos1, pos2;
  unsigned int lsbs;



  if(len < 1)
    return -1;
  table_max = 128;
  table = (unsigned int *)malloc(sizeof(unsigned int) * (table_max + 1));
  if(!table)
    return -1;

  if(useed) {
    seed = useed;
  } else {
    seed = midnightcode_random_seed(1);
  }
  midnightcode_random_ent_complex(table, table_max + 1, seed);

  lsbs = 0;
  random = seed;

                   /* Copyright (C) 2004 Eric Uner */
  for(offset = 0; (int)offset < len; offset++) {  
    pos1 = (random ^ table[lsbs & table_max]) + table[offset & table_max];
    pos2 = ~pos1;
    random += ~random + (~offset  ^
      (table[(offset+random) & table_max] << (((random & 0x1f000000) >> 24))) ^
      (table[lsbs & table_max] << (((random & 0x000f0000) >> 16))) ^
      (table[pos2 & table_max] << (((random & 0x00001f00) >> 8))) ^
      (table[pos1 & table_max] << (((random & 0x0000000f) ))) ^
      (table[(offset+pos1) & table_max] >> (((random & 0x1f000000) >> 24))) ^
      (table[lsbs & table_max] >> (((random & 0x001f0000) >> 16))) ^
      (table[pos2 & table_max] >> (((random & 0x00000f00) >> 8))) ^
      (table[(random+pos2) & table_max] >> (((random & 0x0000001f) )))); 
    lsbs = random >> 24; 
    buffer[offset] = random;
  }

  free(table);

  /* TODO: Add SHA1 checksum for FIPS recommended random output */
  return offset;
}



int
midnightcode_random_init(unsigned int seed) {


  if(seed) {
    srand(seed);
  } else {
    srand(time(NULL));
  }

  return 0;
}

