#ifndef lint
#ifdef __GNUC__
#define ATTRIBUTE(attrs) __attribute__(attrs)
#else
#define ATTRIBUTE(attrs)
#endif
static char Rcs_Id[] ATTRIBUTE((used)) =
    "$Id: rdtest.c,v 1.11 2013-01-05 01:18:52-08 geoff Exp $";
#endif

/*
 * Test the random-distribution library.  Usage:
 *
 * rdtest seed how_many distribution [params...]
 *
 * where:
 *
 *	seed	is the random seed.  If seed is zero, the package's
 *		automatic seed generator is used.
 *	how_many
 *		is how many random numbers to generate.
 *	distribution
 *		is the distribution to draw from (see below).
 *	params	are the parameters to the distribution (see below).
 *
 * Distributions supported:
 *
 *   iuniform	A uniform distribution of integers on the interval [p1, p2).
 *   uniform	A uniform distribution on the interval [p1, p2).
 *   exponential	Exponential with mean p1, default 1.
 *   erlang	p1-Erlang with mean p2.
 *   weibull	Weibull with shape parameter p1 and scale parameter p2.
 *   normal	Normal with mean p1 and standard deviation p2.
 *   lognormal	Lognormal with scale parameter p1 and shape parameter p2.
 *   triangular	Triangular on the interval (p1, p2) with mode at p3.
 *   empirical	p1 with probability p2, p3 with probability p4, ...,
 *		p(2n+1) with probability p(2n).  Actually, the
 *		"probabilities" are
 *              weights, and do not need to sum to 1.
 *   continuous_empirical
 *		p1 to p3 with probability p2, p3 to p5 with
 *		probability p4, ..., p(2n+1) to p(2n+2) with
 *		probability p(2n).  Actually, the "probabilities" are
 *		weights, and do not need to sum to 1.
 *
 * $Log: rdtest.c,v $
 * Revision 1.11  2013-01-05 01:18:52-08  geoff
 * Fix a lot of compiler warnings.
 *
 * Revision 1.10  2012-12-30 16:24:49-08  geoff
 * Use gcc attributes to suppress warnings on Rcs_Id.
 *
 * Revision 1.9  2010-12-10 03:28:19-08  geoff
 * Support the new empirical_distribution interface.
 *
 * Revision 1.8  2008-07-26 11:34:01+12  geoff
 * Fix notation for intervals in commentary.
 *
 * Revision 1.7  2007/10/26 07:21:06  geoff
 * Fix the usage message to be correct.
 *
 * Revision 1.6  2004/03/30 07:29:43  geoff
 * Document the program better, and allow the random seed to be
 * controlled for verification testing.
 *
 * Revision 1.5  2003/09/11 05:50:53  geoff
 * Include string.h to get the declaration of strcmp.
 *
 * Revision 1.4  2001/06/18 10:09:24  geoff
 * Get rid of some warning messages.
 *
 * Revision 1.3  2001/04/10 09:11:38  geoff
 * Add a tiny bit of explanatory commentary.  When testing the empirical
 * distribution, expect the parameters to give individual probabilities,
 * not running sums.  In other words, calculate the running sums for the
 * user before generating the distribution.
 *
 * Revision 1.2  2001/04/09 08:47:19  geoff
 * Add RCS ID keywords.  Give a name to a magic number I missed.
 *
 */

#include "randistrs.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int			main(int argc, char * argv[]);
static void		usage(void);

#define SEED_PARAM	1		/* Offset of seed param in argv */
#define COUNT_PARAM	2		/* Offset of count param in argv */
#define DISTR_PARAM	3		/* Offset of distr param in argv */
#define PARAM_OFFSET	4		/* Offset of params in argv */

int main(
    int			argc,		/* Argument count */
    char *		argv[])		/* Argument vector */
    {
    rd_empirical_control*
			control = NULL;	/* Control if empirical distr. */
    size_t		how_many;	/* How many numbers to generate */
    size_t		i;		/* Loop index */
    size_t		needed_params = 0;
					/* Number of params needed by distr */
    size_t		n_params;	/* Number of parameters */
    size_t		n_probs = 0;	/* Number of empirical probabilites */
    double *		params;		/* Parameters of distribution */
    double *		probs = NULL;	/* Probabilities for empirical */
    double		ran_value = 0.0;
					/* Value generated by PRNG */
    uint32_t		seed;		/* Seed for PRNG */
    double *		values = NULL;	/* Values for empirical */

    if (argc <= PARAM_OFFSET)
	usage();

    seed = atoi (argv[SEED_PARAM]);
    how_many = atoi (argv[COUNT_PARAM]);

    n_params = argc - PARAM_OFFSET;
    params = (double *) malloc (sizeof (double) * n_params);
    if (params == NULL)
	{
	(void) fprintf (stderr, "rdtest: can't malloc params\n");
	return 1;
	}
    for (i = 0;  i < n_params;  i++)
	params[i] = atof (argv[i + PARAM_OFFSET]);

    if (strcmp (argv[DISTR_PARAM], "iuniform") == 0)
	needed_params = 2;
    else if (strcmp (argv[DISTR_PARAM], "uniform") == 0)
	needed_params = 2;
    else if (strcmp (argv[DISTR_PARAM], "exponential") == 0)
	needed_params = 1;
    else if (strcmp (argv[DISTR_PARAM], "erlang") == 0)
	{
	if (n_params < 2  ||  params[0] < 1.0)
	    usage();
	needed_params = 2;
	}
    else if (strcmp (argv[DISTR_PARAM], "weibull") == 0)
	needed_params = 2;
    else if (strcmp (argv[DISTR_PARAM], "normal") == 0)
	needed_params = 2;
    else if (strcmp (argv[DISTR_PARAM], "lognormal") == 0)
	needed_params = 2;
    else if (strcmp (argv[DISTR_PARAM], "triangular") == 0)
	needed_params = 3;
    else if (strcmp (argv[DISTR_PARAM], "empirical") == 0)
	{
	if (n_params % 2 != 0  ||  n_params < 4)
	    usage();
	n_probs = n_params / 2;
	probs = (double *) malloc (sizeof (double) * n_probs);
	values = (double *) malloc (sizeof (double) * (n_probs + 1));
	if (probs == NULL  ||  values == NULL)
	    {
	    (void) fprintf (stderr, "rdtest: can't malloc probs/values\n");
	    return 1;
	    }
	for (i = 0;  i < n_probs;  i++)
	    {
	    values[i] = params[i * 2];
	    probs[i] = params[i * 2 + 1];
	    if (probs[i] < 0)
		{
		(void)fprintf(stderr, "rdtest: negative probability given\n");
		exit(2);
		}
	    }
	values[n_probs] = 0.0;		/* Just for cleanliness */
	needed_params = n_params;
	control = rd_empirical_setup(n_probs, probs, values);
	}
    else if (strcmp(argv[DISTR_PARAM], "continuous_empirical") == 0)
	{
	if (n_params % 2 == 0  ||  n_params < 5)
	    usage();
	n_probs = (n_params - 1) / 2;
	probs = (double *) malloc (sizeof (double) * n_probs);
	values = (double *) malloc (sizeof (double) * (n_probs + 1));
	if (probs == NULL  ||  values == NULL)
	    {
	    (void) fprintf (stderr, "rdtest: can't malloc probs/values\n");
	    return 1;
	    }
	for (i = 0;  i < n_probs;  i++)
	    {
	    values[i] = params[i * 2];
	    probs[i] = params[i * 2 + 1];
	    if (probs[i] < 0)
		{
		(void)fprintf(stderr, "rdtest: negative probability given\n");
		exit(2);
		}
	    }
	values[n_probs] = params[n_probs * 2];
	needed_params = n_params;
	control = rd_empirical_setup(n_probs, probs, values);
	}
    else
	usage();

    if (n_params != needed_params)
	usage();

    /*
     * Pick a seed
     */
    if (seed == 0)
	mt_goodseed();
    else
	mt_seed32(seed);

    for (i = 0;  i < how_many;  i++)
	{
	if (strcmp (argv[DISTR_PARAM], "iuniform") == 0)
	    ran_value = rd_iuniform ((int32_t)params[0], (int32_t)params[1]);
	else if (strcmp (argv[DISTR_PARAM], "uniform") == 0)
	    ran_value = rd_uniform (params[0], params[1]);
	else if (strcmp (argv[DISTR_PARAM], "exponential") == 0)
	    ran_value = rd_exponential (params[0]);
	else if (strcmp (argv[DISTR_PARAM], "erlang") == 0)
	    ran_value = rd_erlang ((int) params[0], params[1]);
	else if (strcmp (argv[DISTR_PARAM], "weibull") == 0)
	    ran_value = rd_weibull (params[0], params[1]);
	else if (strcmp (argv[DISTR_PARAM], "normal") == 0)
	    ran_value = rd_normal (params[0], params[1]);
	else if (strcmp (argv[DISTR_PARAM], "lognormal") == 0)
	    ran_value = rd_lognormal (params[0], params[1]);
	else if (strcmp (argv[DISTR_PARAM], "triangular") == 0)
	    ran_value = rd_triangular (params[0], params[1], params[2]);
	else if (strcmp (argv[DISTR_PARAM], "empirical") == 0)
	    ran_value = rd_double_empirical (control);
	else if (strcmp (argv[DISTR_PARAM], "continuous_empirical") == 0)
	    ran_value = rd_continuous_empirical (control);
	(void) printf ("%f\n", ran_value);
	}
    return 0;
    }

static void usage(void)
    {
    (void) fprintf (stderr, "Usage: rdtest seed count distribution p0 ...\n");
    (void) fprintf (stderr, "Distributions:\n");
    (void) fprintf (stderr, "\tiuniform lower upper\n");
    (void) fprintf (stderr, "\tuniform lower upper\n");
    (void) fprintf (stderr, "\texponential mean\n");
    (void) fprintf (stderr, "\terlang p mean\n");
    (void) fprintf (stderr, "\tweibull shape scale\n");
    (void) fprintf (stderr, "\tnormal mean sigma\n");
    (void) fprintf (stderr, "\tlognormal shape scale\n");
    (void) fprintf (stderr, "\ttriangular lower upper mode\n");
    (void) fprintf (stderr, "\tempirical v0 p0 v1 p1 ... vn-1 pn-1\n");
    (void) fprintf (stderr,
      "\tcontinuous_empirical v0 p0 v1 p1 ... vn-1 pn-1 vn\n");
    exit(2);
    }