/* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.

iem_dp written by IOhannes m zmoelnig, Thomas Musil, Copyright (c) IEM KUG Graz Austria 1999 - 2007 */
/* double precision library */

#include "m_pd.h"
#include "iemlib.h"
#include "iem_dp.h"


/* ------------------------  random_dp  ---------------------------- */
/* based on miller's random, which is part of pd, only with double precision */

static t_class *random_dp_class;

typedef struct _random_dp
{
  t_object x_obj;
  t_float x_max_coarse;
  t_float x_max_fine;
  unsigned int x_state;
  t_outlet  *x_out_coarse;
  t_outlet  *x_out_fine;
} t_random_dp;


static int makeseed(void)
{
  static unsigned int random_nextseed = 1489853723;
  random_nextseed = random_nextseed * 435898247 + 938284287;
  return (random_nextseed & 0x7fffffff);
}

static void *random_dp_new(t_symbol *s, int ac, t_atom *av)
{
  t_random_dp *x = (t_random_dp *)pd_new(random_dp_class);

  if((ac > 0) && (IS_A_FLOAT(av, 0)))
    x->x_max_coarse = atom_getfloatarg(0, ac, av);
  else
    x->x_max_coarse = 0.0f;
  if((ac > 1) && (IS_A_FLOAT(av, 1)))
    x->x_max_fine = atom_getfloatarg(1, ac, av);
  else
    x->x_max_fine = 0.0f;
  x->x_state = makeseed();
  floatinlet_new(&x->x_obj, &x->x_max_coarse);
  floatinlet_new(&x->x_obj, &x->x_max_fine);
  x->x_out_coarse = outlet_new(&x->x_obj, &s_float);
  x->x_out_fine = outlet_new(&x->x_obj, &s_float);
  return (x);
}

static void random_dp_bang(t_random_dp *x)
{
  int n = (int)iem_dp_calc_sum(x->x_max_coarse, x->x_max_fine);
  int nval;
  int range = (n < 1 ? 1 : n);
  unsigned int randval = x->x_state;
  t_float fine, coarse;

  x->x_state = randval = randval * 472940017 + 832416023;
  nval = ((double)range) * ((double)randval) * (1./4294967296.);
  if(nval >= range)
    nval = range-1;
  coarse = iem_dp_cast_to_float((double)nval);
  outlet_float(x->x_out_fine, iem_dp_calc_residual((double)nval, coarse));
  outlet_float(x->x_out_coarse, coarse);
}

static void random_dp_seed(t_random_dp *x, t_symbol *s, int ac, t_atom *av)
{
  t_float fine, coarse;

  if((ac > 0) && (IS_A_FLOAT(av, 0)))
    coarse = atom_getfloatarg(0, ac, av);
  else
    coarse = 0.0f;
  if((ac > 1) && (IS_A_FLOAT(av, 1)))
    fine = atom_getfloatarg(1, ac, av);
  else
    fine = 0.0f;
  x->x_state = (int)iem_dp_calc_sum(coarse, fine);
}

void random_dp_setup(void)
{
  random_dp_class = class_new(gensym("random__"), (t_newmethod)random_dp_new, 0, sizeof(t_random_dp), 0, A_GIMME, 0);
  class_addcreator((t_newmethod)random_dp_new, gensym("random''"), A_GIMME, 0);
  class_addbang(random_dp_class, random_dp_bang);
  class_addmethod(random_dp_class, (t_method)random_dp_seed, gensym("seed"), A_GIMME, 0);
}
