/*
 *  Open BEAGLE
 *  Copyright (C) 2001-2004 by Christian Gagne and Marc Parizeau
 *
 *  This 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.
 *
 *  This 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 this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Contact:
 *  Laboratoire de Vision et Systemes Numeriques
 *  Departement de genie electrique et de genie informatique
 *  Universite Laval, Quebec, Canada, G1K 7P4
 *  http://vision.gel.ulaval.ca
 *
 */

/*
 *  Part of this file include code from file MersenneTwister.h, licensed under
 *  a BSD license. This code is available on-line at
 *  http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html
 *
 *  Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
 *  Copyright (C) 2000 - 2003, Richard J. Wagner
 *  All rights reserved. 
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 *    3. The names of its contributors may not be used to endorse or promote
 *       products derived from this software without specific prior written
 *       permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
 
/*!
 *  \file   beagle/RandomizerMersenne.hpp
 *  \brief  Definition of the class RandomizerMersenne.
 *  \author Makoto Matsumoto
 *  \author Takuji Nishimura
 *  \author Christian Gagne
 *  \author Marc Parizeau
 *  $Revision: 1.4 $
 *  $Date: 2004/04/25 23:12:21 $
 */

#ifndef Beagle_RandomizerMersenne_hpp
#define Beagle_RandomizerMersenne_hpp

#include <ctime>

#include "beagle/config.hpp"
#include "beagle/macros.hpp"
#include "beagle/Randomizer.hpp"  


namespace Beagle {

/*!
 *  \class RandomizerMersenne beagle/RandomizerMersenne.hpp "beagle/RandomizerMersenne.hpp"
 *  \brief Random number generator based on the Mersenne Twister from Matsumoto and Nishimura.
 *  \ingroup ECF
 *  \ingroup Sys
 *
 *  Reference:
 *  M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
 *  Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
 *  Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
 *
 */
class RandomizerMersenne : public Randomizer {

public:

  //! Length of state vector
  enum { N = 624 };

  //! RandomizerMersenne allocator type.
  typedef AllocatorT<RandomizerMersenne,Randomizer::Alloc>
          Alloc;
  //! RandomizerMersenne handle type.
  typedef PointerT<RandomizerMersenne,Randomizer::Handle>
          Handle;
  //! RandomizerMersenne bag type.
  typedef ContainerT<RandomizerMersenne,Randomizer::Bag>
          Bag;

           RandomizerMersenne();
  virtual ~RandomizerMersenne() { }

  virtual void          initRandomizer(unsigned long inState);
  virtual void          read(XMLNode::Handle& inNode);
  virtual unsigned long rollInteger(unsigned long inLower=0, unsigned long inUpper=ULONG_MAX);
  virtual double        rollUniform(double inLower=0.0, double inUpper=1.0);
  virtual void          write(XMLStreamer& ioStreamer) const;

protected:
  enum { M = 397 };            //!< Period parameter.

  unsigned long hash(unsigned int inTime, unsigned int inClock);
  unsigned long rollInt();
  void          reload();

  /*!
   *  \brief Return value of highest bit of the number.
   *  \param inNumber Number to get high bit.
   *  \return Number representing the high bit.
   */
  inline unsigned long hiBit(unsigned long inNumber) const
  {
    return inNumber & 0x80000000UL;
  }

  /*!
   *  \brief Return value of lowest bit of a number.
   *  \param inNumber Number to get low bit.
   *  \return Number representing the low bit.
   */
  inline unsigned long loBit(unsigned long inNumber) const
  {
    return inNumber & 0x00000001UL;
  }

  /*!
   *  \brief Return all low bits of a number (all bits except the highest).
   *  \param inNumber Number to get the low bits.
   *  \return Number representing the low bits.
   */
  inline unsigned long loBits(unsigned long inNumber) const
  {
    return inNumber & 0x7fffffffUL;
  }

  /*!
   *  \brief Mix highest bit of number 1 with the lowest bits of number 2.
   *  \param inNumber1 First number to mix.
   *  \param inNumber2 Second number to mix
   *  \return Resulting mixed number.
   */
  inline unsigned long mixBits(unsigned long inNumber1, unsigned long inNumber2) const
  {
    return hiBit(inNumber1) | loBits(inNumber2);
  }

   /*!
   *  \brief Twist three numbers.
   *  \param inM First number to twist.
   *  \param inS0 Second number to twist.
   *  \param inS1 Third number to twist.
   *  \return Resulting twisted number.
   */
  inline unsigned long twist(unsigned long inM, unsigned long inS0, unsigned long inS1) const
  {
    return inM ^ (mixBits(inS0,inS1)>>1) ^ (-loBit(inS1) & 0x9908b0dfUL);
  }

  unsigned long  mValidator;   //!< Valid if state parameter has changed.
  unsigned long  mStateMT[N];  //!< Internal state.
  unsigned long *mNextMT;      //!< Next value to get from state.
  int            mLeftMT;      //!< Number of values left before reload needed.

};

}

#endif // Beagle_RandomizerMersenne_hpp
