/* This file is part of the alrand library.
   alrand is Copyright by Vincent Penquerc'h <lyrian -at- kezako -dot- net>.

   The alrand library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The alrand library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the alrand Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA. */

#include <malloc.h>
#include <assert.h>
#include "mersenne.h"
#include "lcg.h"
#include "alrand.h"

#define BITS_TO_BYTES(bits) (((bits)+7)/8)


/* Globals used by the global interface generator */
static alrand_generator_type global_generator_type=alrand_type_mersenne_twister;
static alrand_state *global_state=NULL;


/* Initialization */
void alrand_init()
{
}

void alrand_exit()
{
  if (global_state) {
    alrand_destroy(global_state);
    global_state=NULL;
  }
}



/* State interface */

alrand_state *alrand_create(alrand_generator_type type,alrand_uint32_t seed)
{
  switch (type) {
    case alrand_type_mersenne_twister:
      return alrand_mersenne_twister_create(seed);
    case alrand_type_linear_congruential:
      return alrand_lcg_create(seed);
    case alrand_num_types:
      assert(0);
  }
  assert(0);
  return NULL;
}

void alrand_seed(alrand_state *state,alrand_uint32_t seed)
{
  assert(state);
  assert(state->vtable);
  assert(state->vtable->seed);
  (*state->vtable->seed)(state,seed);
}

void alrand_destroy(alrand_state *state)
{
  if (state) {
    assert(state->vtable);
    assert(state->vtable->destroy);
    (*state->vtable->destroy)(state);
  }
}

alrand_uint32_t alrand_generate(alrand_state *state)
{
  assert(state);
  assert(state->vtable);
  assert(state->vtable->generate);
  return (*state->vtable->generate)(state);
}



/* Global interface */

static void create_global_state(alrand_uint32_t seed)
{
  if (!global_state) {
    global_state=alrand_create(global_generator_type,seed);
  }
}

void alrand_srand(alrand_uint32_t seed)
{
  if (global_state) {
    alrand_destroy(global_state);
    global_state=NULL;
  }
  create_global_state(seed);
}

alrand_uint32_t alrand_rand()
{
  create_global_state(4357);
  return alrand_generate(global_state);
}

void alrand_set_global_generator_type(alrand_generator_type type)
{
  if (global_state) {
    alrand_destroy(global_state);
    global_state=NULL;
  }
  global_generator_type=type;
}

alrand_generator_type alrand_get_global_generator_type()
{
  return global_generator_type;
}



/* State persistence */

int alrand_save(
  alrand_state *state,void (*write)(alrand_uint32_t,void*),void *ptr
)
{
  assert(write);
  assert(state);
  assert(state->vtable);
  assert(state->vtable->save);
  write(state->type,ptr);
  return state->vtable->save(state,write,ptr);
}

alrand_state *alrand_load(alrand_uint32_t (*read)(void*),void *ptr)
{
  alrand_generator_type type;
  alrand_state *state=NULL;

  assert(read);

  type=(alrand_generator_type)read(ptr);
  switch (type) {
    case alrand_type_mersenne_twister:
      state=alrand_mersenne_twister_load(read,ptr);
      break;
    case alrand_type_linear_congruential:
      state=alrand_lcg_load(read,ptr);
      break;
    default:
      assert(0);
  }
  return state;
}

