//==================================================================================================
// S t a n d a r d                                                                        Interface
// R a n d o m _ n u m b e r
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 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 Library General Public License for more details (http://www.gnu.org).
//
// This file is just an adaptation of MT19937 coded by Takuji Nishimura, considering the
// suggestions by Topher Cooper and Marc Rieffel in July-August 1997. It is originally a GNU
// Library General Public License with the following copyright:
//
//    "Copyright (c) 1997 Makoto Matsumoto and Takuji Nishimura.
//    Any feedback is very welcome. For any question, comments,
//    see http://www.math.keio.ac.jp/matumoto/emt.html or e-mail
//    matumoto@math.keio.ac.jp."

/*DESCRIPTION*/
/* This module provides a random number generator (integer and real). It is just an adaptation of
   MT19937 coded by Takuji Nishimura, considering the suggestions by Topher Cooper and Marc Rieffel
   in July-August 1997. It is originally a GNU Library General Public License with the following
   copyright.<BR><BR>
   <BLOCKQUOTE><I>Copyright (c) 1997 Makoto Matsumoto and Takuji Nishimura.<BR>
   Any feedback is very welcome. For any question, comments,<BR>
   see <A HREF="http://www.math.keio.ac.jp/matumoto/emt.html" TARGET="new_window">
   http://www.math.keio.ac.jp/matumoto/emt.html</A> or e-mail<BR>
   matumoto@math.keio.ac.jp.</I></BLOCKQUOTE> */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "standard/random_number.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guStandardRandomNumber
#define guStandardRandomNumber

// Headers //---------------------------------------------------------------------------------------
#include <bpp/standard/maths.hpp> /*INCLUDE*/

namespace bpp {

// Importation/Exportation //-----------------------------------------------------------------------
#ifdef STANDARD_DLL
 #define dll_export DLL_EXPORT
#else
 #define dll_export DLL_IMPORT
#endif

// Namespaces //------------------------------------------------------------------------------------
#define public_area  standardRandomNumber
#define private_area standardRandomNumber_private

namespace public_area  { /*NAMESPACE*/ using namespace standardMaths; }
namespace private_area { using namespace public_area; }

extern_module_name;

// Initialization //--------------------------------------------------------------------------------
#define iniStandardRandomNumber
has_initializer;

// Macrocommands //---------------------------------------------------------------------------------

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area  { class clRandomGenerator; }
namespace private_area {}

// Functions Interface //---------------------------------------------------------------------------
namespace public_area {
 inline tyCardinal randomCardinal(tyCardinal);
 inline tyReal     randomReal(void);
}

namespace private_area { testing_mode ( function void test(void); ) }

// Errors //----------------------------------------------------------------------------------------
namespace public_area {
 /*ERROR*/ extern_error erRandomSeedParameterMissing;
 /* The <CODE>random_seed</CODE> parameter is missing in the initialization file. */
}

// Constants & Variables //-------------------------------------------------------------------------
extern_dynamic_constant(private,clRandomGenerator,goRandomGenerator,?);
extern_static_constant(private,clMutex,goMutex,?);

// R a n d o m G e n e r a t o r  Interface //------------------------------------------------------
namespace public_area {
 /*CLASS clRandomGenerator */ /* Represents a random number generator. */
 class clRandomGenerator {
  //-------------------------------------------------------------------------------------------Types
  /*TYPE clRandomGenerator */ /* Type of the seed of the random generator. */
  public_property typedef unsigned long tySeed;
  //-----------------------------------------------------------------------------------------Private
  private_property constructor clRandomGenerator(const clRandomGenerator &);
  private_property const clRandomGenerator operator = (const clRandomGenerator &);

  private_property tyCardinal mag01[2];
  private_property tyCardinal mt[624];
  private_property tyInteger  mti;
  //------------------------------------------------------------------------------------------Public
  /*ATTRIBUTE clRandomGenerator */ /* Seed of the random generator. */
  read_only_attribute(tySeed,atSeed,seed);

  public_property constructor clRandomGenerator(tySeed);
  public_property destructor clRandomGenerator(void);

  public_property tyCardinal nextCardinal(void);
  public_property tyReal     nextCeilingReal(void);
  public_property tyReal     nextFloorReal(void);
  public_property tyReal     nextReal(void);
 };
}

// R a n d o m G e n e r a t o r  Inline //---------------------------------------------------------
namespace public_area {
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clRandomGenerator */ /* Destructs the random generator. */
 inline clRandomGenerator::~clRandomGenerator(void) {}
 //----------------------------------------------------------------------------------NextCeilingReal
 /*METHOD clRandomGenerator */
 /* Returns the next real number in the random sequence. The value is generated between
    zero (excluded) and one (included). */
 inline tyReal clRandomGenerator::nextCeilingReal(void)
 { return (1.0-(tyReal)nextCardinal()/(tyReal)4294967296.0); }
 //------------------------------------------------------------------------------------NextFloorReal
 /*METHOD clRandomGenerator */
 /* Returns the next real number in the random sequence. The value is generated between
    zero (included) and one (excluded). */
 inline tyReal clRandomGenerator::nextFloorReal(void)
 { return ((tyReal)nextCardinal()/(tyReal)4294967296.0); }
 //-----------------------------------------------------------------------------------------NextReal
 /*METHOD clRandomGenerator */
 /* Returns the next real number in the random sequence. The value is generated between
    zero (included) and one (included). */
 inline tyReal clRandomGenerator::nextReal(void)
 { return ((tyReal)nextCardinal()/(tyReal)4294967295.0); }
}

// Functions Inline //------------------------------------------------------------------------------
namespace public_area {
 //-----------------------------------------------------------------------------------RandomCardinal
 /*FUNCTION*/
 /* Returns a random integer number within a given range (<CODE>0</CODE> to
    <CODE>range-1</CODE>). */
 inline tyCardinal randomCardinal(tyCardinal agRange) {
  tyCardinal lcNumber;

  if (agRange==0) send_inline_error(erZeroDivision,"randomCardinal");
  safe_yes ( private_area::goMutex.lock(); )
  lcNumber=private_area::goRandomGenerator->nextCardinal();
  safe_yes ( private_area::goMutex.release(); )
  return (lcNumber%agRange);
 }
 //---------------------------------------------------------------------------------------RandomReal
 /*FUNCTION*/ /* Returns a random real number. */
 inline tyReal randomReal(void) {
  tyReal lcNumber;

  safe_yes ( private_area::goMutex.lock(); )
  lcNumber=private_area::goRandomGenerator->nextReal();
  safe_yes ( private_area::goMutex.release(); )
  return (lcNumber);
 }
}

namespace private_area {}

// End //-------------------------------------------------------------------------------------------
}
#undef dll_export
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// S t a n d a r d                                                                   Implementation
// R a n d o m _ n u m b e r
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 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 Library General Public License for more details (http://www.gnu.org).
//
// This file is just an adaptation of MT19937 coded by Takuji Nishimura, considering the
// suggestions by Topher Cooper and Marc Rieffel in July-August 1997. It is originally a GNU
// Library General Public License with the following copyright:
//
//    "Copyright (c) 1997 Makoto Matsumoto and Takuji Nishimura.
//    Any feedback is very welcome. For any question, comments,
//    see http://www.math.keio.ac.jp/matumoto/emt.html or e-mail
//    matumoto@math.keio.ac.jp."

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "standard/random_number.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define STANDARD_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/standard/random_number.hpp> /*INTERFACE*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  standardRandomNumber
#define private_area standardRandomNumber_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Standard/Random_number");

// Initialization //--------------------------------------------------------------------------------
#undef iniStandardRandomNumber
static_constant(private_area::clInitializer,goInitializer);

// Errors //----------------------------------------------------------------------------------------
namespace public_area { static_error erRandomSeedParameterMissing; }

// Constants & Variables //-------------------------------------------------------------------------
dynamic_constant(clRandomGenerator,goRandomGenerator);
static_constant(clMutex,goMutex);

// Static Members //--------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Implementation //----------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// R a n d o m G e n e r a t o r  Implementation //-------------------------------------------------
namespace public_area {
 #define N                    624
 #define M                    397
 #define MATRIX_A             0x9908B0DF
 #define UPPER_MASK           0x80000000
 #define LOWER_MASK           0x7FFFFFFF
 #define TEMPERING_MASK_B     0x9D2C5680
 #define TEMPERING_MASK_C     0xEFC60000
 #define TEMPERING_SHIFT_U(y) (y>>11)
 #define TEMPERING_SHIFT_S(y) (y<<7)
 #define TEMPERING_SHIFT_T(y) (y<<15)
 #define TEMPERING_SHIFT_L(y) (y>>18)
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clRandomGenerator */
 /* Builds a new random generator with a given seed. If the seed is zero, the seed is built from
    the internal clock of the computer. */
 property clRandomGenerator::clRandomGenerator(tySeed agSeed) {
  mag01[0]=0x0;
  mag01[1]=MATRIX_A;
  if (agSeed==0) agSeed=(tySeed)time(nil);
  atSeed=agSeed;
  mt[0]=agSeed&0xFFFFFFFF;
  for (mti=1;mti<N;mti++) mt[mti]=(69069*mt[mti-1])&0xFFFFFFFF;
 }
 //-------------------------------------------------------------------------------------NextCardinal
 /*METHOD clRandomGenerator */ /* Returns the next integer number in the random sequence. */
 property tyCardinal clRandomGenerator::nextCardinal(void) {
  tyInteger  kk;
  tyCardinal y;

  if (mti>=N) {
   for (kk=0;kk<N-M;kk++) {
    y=(mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
    mt[kk]=mt[kk+M]^(y>>1)^mag01[y&0x1];
   }

   for (;kk<N-1;kk++) {
    y=(mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
    mt[kk]=mt[kk+(M-N)]^(y>>1)^mag01[y&0x1];
   }

   y=(mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
   mt[N-1]=mt[M-1]^(y>>1)^mag01[y&0x1];
   mti=0;
  }

  y=mt[mti++];
  y^=TEMPERING_SHIFT_U(y);
  y^=TEMPERING_SHIFT_S(y)&TEMPERING_MASK_B;
  y^=TEMPERING_SHIFT_T(y)&TEMPERING_MASK_C;
  y^=TEMPERING_SHIFT_L(y);

  return (y);
 }
}

// I n i t i a l i z e r  Implementation //---------------------------------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------------Start
 property void clInitializer::start(void) {
  if (atCounter++ == 0) {
   try {
    #include <bpp/modules.hpp> /*NEED*/
    registerStop(this);
    environment::informInitialization(goModuleName);

    method_name("initializer::constructor");

    erRandomSeedParameterMissing.create("The 'random_seed' parameter is missing.");

    if (environment::parameters().count(clString("random_seed"))==0)
     send_error(erRandomSeedParameterMissing);

    goRandomGenerator = new_object(clRandomGenerator(cardinal(
                                   environment::parameter(clString("random_seed")).data())));

    bpp::low::string::toString(environment_private::goRandomSeed,
                               tyInteger(goRandomGenerator->seed()));
   }

   initializer_catch;
  }
 }
 //---------------------------------------------------------------------------------------------Stop
 property void clInitializer::stop(void) {
  try {
   environment::informTermination(goModuleName);

   delete_object(goRandomGenerator);
  }

  initializer_catch;
 }
}

// End //-------------------------------------------------------------------------------------------
}