/* @file psRandom.h
 * @brief Random Number Generators
 *
 * This file will hold the prototypes for procedures which allocate, free,
 * and evaluate random number Generators.
 *
 * @author GLG, MHPCC
 *
 * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
 * @date $Date: 2007-11-16 00:41:07 $
 *
 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
 */

#ifndef PS_RANDOM_H
#define PS_RANDOM_H

/// @addtogroup MathOps Mathematical Operations
/// @{

#include <stdio.h>
#include <stdbool.h>
#include <float.h>
#include <math.h>

#include "psVector.h"
#include "psScalar.h"
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

/** Enumeration containing a flag for psRandom types.  */
typedef enum {
    PS_RANDOM_TAUS                     ///< A maximally equidistributed combined Tausworthe generator.
} psRandomType;

/** Data structure for psRandom.
 *  Contains information on the psRandom type and GNU Scientific Library random number generator.
 */
typedef struct {
    psRandomType type;                 ///< The type of RNG
    gsl_rng *gsl;                      ///< The RNG itself
} psRandom;

/// Get a seed from the system.
///
/// Tries /dev/random first, and then the system clock
psU64 p_psRandomGetSystemSeed(bool log  ///< Print a log message about the choice of seed?
    );

/** Allocates a psRandom struct.
 *
 *  @return psRandom*:    A new psRandom structure.
 */
psRandom *psRandomAlloc(
    psRandomType type,                 ///< The type of RNG
    unsigned long seed                 ///< Known value with which to seed the RNG
) PS_ATTR_MALLOC;

/** Resets an existing psRandom struct.
 *
 *  @return void
 */
void psRandomReset(
    psRandom *rand,                    ///< Existing psRandom struct to reset
    unsigned long seed                 ///< Known value with which to seed the RNG
);

/** Random number generator based on a uniform distribution on [0,1).
 *  Uses gsl_rng_uniform.
 *
 *  @return double:     Random number.
 */
double psRandomUniform(
    const psRandom *r                  ///< psRandom struct for RNG
);

/** Random number generator based on a Gaussian deviate, N(0,1).
 *  Uses gsl_ran_gaussian.
 *
 *  @return double:     Random number.
 */
double psRandomGaussian(
    const psRandom *r                  ///< psRandom struct for RNG
);

/** Random number generator based on a Gaussian deviate, N(0,1).
 *  Uses gsl_ran_gaussian.
 *
 *  XXX: I created this since the above psLib spec for p_psRandomGaussian
 *  had no argument for sigma.  Verify that with IfA.
 *
 *  @return double:     Random number.
 */
double p_psRandomGaussian(
    const psRandom *r,                  ///< psRandom struct for RNG
    double sigma
);

/** Random number generator based on a Poisson distribution with the given mean.
 *  Uses gsl_ran_poisson.
 *
 *  @return double:     Random number.
 */
double psRandomPoisson(
    const psRandom *r,                 ///< psRandom struct for RNG
    double mean                         ///< Mean value
);

#define PS_ASSERT_RANDOM_NON_NULL(NAME, RVAL) \
if (!(NAME) || !(NAME)->gsl) { \
    psError(PS_ERR_UNEXPECTED_NULL, true, "Error: Random number generator %s is NULL", #NAME); \
    return RVAL; \
}

/// @}
#endif // #ifndef PS_RANDOM_H
