/*
 *  randomdev.h
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef RANDOMDEV_H
#define RANDOMDEV_H

#include <cassert>
#include "randomgen.h"
#include "dictdatum.h"

/**
 * @defgroup RandomDeviateGenerators Random Deviate Generators.
 * @ingroup RandomNumbers
 *
 * Random deviate generators (RDGs) produce random numbers with
 * various distributions on the basis of [0,1) uniformly distributed
 * numbers.  Discrete and continuous distributions are available.
 * For a complete list of available deviates, please use the following
 * sli command
 *
 * @verbatim
 * SLI ] rdevdict info
 * --------------------------------------------------
 * Name                     Type                Value
 * --------------------------------------------------
 * binomial                 rdvfacttype         <rdvfacttype>
 * poisson                  rdvfacttype         <rdvfacttype>
 * normal                   rdvfacttype         <rdvfacttype>
 * exponential              rdvfacttype         <rdvfacttype>
 * gamma                    rdvfacttype         <rdvfacttype>
 * uniformint               rdvfacttype         <rdvfacttype>
 * --------------------------------------------------
 * @endverbatim
 *
 * @note 
 * RDGs that are to be used in multithreaded code, must be called
 * with the thread-RNG as argument whenever a number is drawn, to
 * ensure thread-consistent number generation.
 *
 * @note
 * All RDGs provide double numbers, generators for discrete 
 * distributions may provide unsigned longs as well (eg, Poisson).
 * This can be checked with the has_uldev().
 *
 * @note
 * Here is a code example for the use of the Poisson generator:
 *
 * @code
 * #include "poisson_randomdev.h"
 * class poisson_generator : 
 *   public Node, 
 *   protected Device
 * {
 * ...
 * private: 
 *   librandom::PoissonRandomDev poisson_dev_;  //!< random deviate generator
 * ... 
 *  };
 * @endcode
 * @code
 * nest::poisson_generator::poisson_generator()
 * :...
 *   poisson_dev_(0.0),
 *  ...
 * {
 *   calibrate(network()->get_resolution());
 * }
 * void nest::poisson_generator::calibrate(Time const & dt)
 * {
 *  poisson_dev_.set_lambda(dt.get_ms() * rate_*1e-3);
 * }
 *
 * void nest::poisson_generator::update(thread thrd, Time const & T)
 * {
 *   ...
 *   librandom::RngPtr rng=Node::network()->get_rng(thrd);
 *   ...
 *     ulong_t n_spikes = poisson_dev_.uldev(rng);
 *   ...
 * }
 * @endcode
 *
 * Note that the RNG used by the deviate generator must be obtained
 * via get_rng(thrd) on each call to update, to get the proper generator
 * for the present thread.
 *
 */

namespace librandom {

  class RandomDev;
  
  /**
   * Common lock-pointer type for Random deviate generators
   *
   * A safe pointer that should be used instead of RandomDev*
   * in user code to manage random number generators.
   */
  typedef lockPTR<RandomDev> RdvPtr;



/**
 * Abstract base class for access to non-uniform random deviate
 * generators.
 * @see RandomGen
 * @ingroup RandomDeviate
 * HEP 2002-07-09, 2004-06-28
 */

  class RandomDev {

  public:

    /**
     * Construct with (single-threaded) or without (multithreaded)
     * RNG.
     */  
    RandomDev(RngPtr rng = RngPtr(0)) : rng_(rng) {}

    //! ensure proper clean-up
    virtual ~RandomDev() {};

    /**
     * Operator delivering doubles
     *
     * All random deviates must deliver doubles
     *
     * @note
     * Operator varieties with and without RngPtr as argument
     * are implemented explicitly instead of via default value
     * of the argument to force all derived classes to implement
     * both varieties.
     */
    virtual double operator()(void) = 0;   //!< single-threaded
    virtual double operator()(RngPtr) const = 0; //!< multi-threaded

    /**
     * integer valued functions for discrete distributions
     */
    virtual unsigned long uldev(void);
    virtual unsigned long uldev(RngPtr) const;

    /**
     * true if RDG implements uldev function
     */
    virtual bool has_uldev() const { return false; }

    //! set RNG
    void set_rng(RngPtr rng) { rng_ = rng; }

    /**
     * set distribution parameters from SLI interface
     *
     * @note
     * RDGs with parameters will usually implement functions
     * to manipulate the parameters directly, without use
     * of a SLI dictionary.  This function is meant only to
     * provide access from the SLI interface.
     */
    virtual void set_status(const DictionaryDatum&) =0; 

    /**
     * get distribution parameters from SLI interface
     *
     * @note
     * RDGs with parameters will usually implement functions
     * to manipulate the parameters directly, without use
     * of a SLI dictionary.  This function is meant only to
     * provide access from the SLI interface.
     */
    virtual void get_status(DictionaryDatum&) const =0; 

  protected:
    RngPtr rng_;  //!< store underlying RNG
 
  };

  /**
   * Generic factory class for RandomDev.
   */
  class GenericRandomDevFactory {
  public:
    virtual ~GenericRandomDevFactory() {}
    virtual RdvPtr create(RngPtr rng) const =0;
  };

  /**
   * Factory class for generating objects of type RandomDev
   */

  template <typename DevType>
  class RandomDevFactory : public GenericRandomDevFactory {

  public:

    //! create deviate generator given uniform number generator
    RdvPtr create(RngPtr rng) const
    {
      return RdvPtr(new DevType(rng));
    }
    
  };
  

}

#endif