/* 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 "lcg.h"

typedef struct {
  alrand_uint32_t mult;
  alrand_uint32_t add;
  alrand_uint32_t seed;
} alrandint_lcg_state;

void alrand_lcg_destroy(alrand_state *state)
{
  if (state) {
    assert(state->type==alrand_type_linear_congruential);
    free(state);
  }
}

void alrand_lcg_seed(alrand_state *state,alrand_uint32_t seed)
{
  alrandint_lcg_state *priv;
  assert(state);
  assert(state->type==alrand_type_linear_congruential);
  priv=(alrandint_lcg_state*)&state->priv;
  priv->seed=seed;
}

alrand_uint32_t alrand_lcg_generate(alrand_state *state)
{
  alrandint_lcg_state *priv;
  assert(state);
  assert(state->type==alrand_type_linear_congruential);
  priv=(alrandint_lcg_state*)&state->priv;

  priv->seed=priv->seed*priv->mult+priv->add;
  return priv->seed;
}

alrand_state *alrand_lcg_load(alrand_uint32_t (*read)(void*),void *ptr)
{
  alrand_uint32_t mult,add,seed;

  assert(read);

  mult=read(ptr);
  add=read(ptr);
  seed=read(ptr);
  return alrand_lcg_create_custom(mult,add,seed);
}

int alrand_lcg_save(
  alrand_state *state,void (*write)(alrand_uint32_t,void*),void *ptr
)
{
  alrandint_lcg_state *priv;

  assert(write);
  assert(state);
  assert(state->type==alrand_type_linear_congruential);

  priv=(alrandint_lcg_state*)&state->priv;
  write(priv->mult,ptr);
  write(priv->add,ptr);
  write(priv->seed,ptr);

  return 0;
}

static alrand_vtable alrandint_lcg_vtable={
  &alrand_lcg_seed,
  &alrand_lcg_generate,
  &alrand_lcg_destroy,
  &alrand_lcg_save,
};

alrand_state *alrand_lcg_create_custom(
  alrand_uint32_t mult,alrand_uint32_t add,alrand_uint32_t seed
)
{
  alrandint_lcg_state *priv;
  size_t size=sizeof(alrand_state)+sizeof(alrandint_lcg_state);
  alrand_state *state=malloc(size);
  if (!state) return NULL;

  state->type=alrand_type_linear_congruential;
  state->vtable=&alrandint_lcg_vtable;

  priv=(alrandint_lcg_state*)&state->priv;
  priv->mult=mult;
  priv->add=add;

  alrand_lcg_seed(state,seed);
  return state;
}

alrand_state *alrand_lcg_create(alrand_uint32_t seed)
{
  return alrand_lcg_create_custom(50331499,29230937,seed);
}
