// @(#)root/mathcore:$Id$
// Authors: L. Moneta    8/2015

/**********************************************************************
 *                                                                    *
 * Copyright (c) 2015 , ROOT MathLib Team                             *
 *                                                                    *
 *                                                                    *
 **********************************************************************/

// implementation file of MixMax engine
//
//
// Created by: Lorenzo Moneta  : Tue 4 Aug 2015
//
//
#ifndef ROOT_Math_MixMaxEngine_icc
#define ROOT_Math_MixMaxEngine_icc

#ifndef ROOT_Math_MixMaxEngine
#error "Do not use MixMaxEngine.icc directly. #include \"MixMaxEngine.h\" instead."
#endif // ROOT_Math_MixMaxEngine

//#include "Math/mixmax/mixmax.h"
#include <cassert>
#include "Math/Util.h"


namespace ROOT {
namespace Math {


   
   template<int N, int S>
   MixMaxEngine<N,S>::MixMaxEngine(uint64_t seed) { 
      // fRng = new mixmax::mixmax_engine<N>(); 
      // fRng->seed(seed);
      fRng = new MixMaxEngineImpl<N>(seed);
   }

   template<int N, int S>
   MixMaxEngine<N,S>::~MixMaxEngine() {
      if (fRng) delete fRng; 
   }


   // void template<int N, int S>
   //MixMaxEngine<N,S>::SeedUniqueStream(unsigned int clusterID, unsigned int machineID, unsigned int runID, unsigned int  streamID) { 
   //    seed_uniquestream(fRngState, clusterID,  machineID,  runID,   streamID);
   // }

   template<int N, int S>
   void MixMaxEngine<N,S>::SetSeed(uint64_t seed) { 
      //fRng->seed(seed);
      fRng->SetSeed(seed);
   }

   // void template<int N, int S>
   // MixMaxEngine<N,S>::SetSeed64(uint64_t seed) { 
   //    seed_spbox(fRngState, seed);
   //    iterate(fRngState);                    
   // }

   // unsigned int template<int N, int S>
   // MixMaxEngine<N,S>::GetSeed() const { 
   //    return get_next(fRngState);
   // }
         
   // generate one random number in interval ]0,1]
   // apply skipping when needed

   template<int SkipNumber>
   struct SkipFunction {
      template<class Engine>
      static void Apply (Engine * rng, int counter, int n) {
         // apply skipping
         if (counter < n) return; 
         for (int iskip = 0; iskip < SkipNumber; ++iskip)
            rng->Iterate();
      }
   };
   // specialization for SkipNumber = 0
   template<>
   struct SkipFunction<0> {
      template<class Engine>
      static void Apply (Engine *, int , int ) {
         // no operation
      }
   };
   
   template<int N, int S>
   double MixMaxEngine<N,S>::Rndm_impl()  {
      int counter = fRng->Counter();
      SkipFunction<S>::Apply(fRng, counter, N);
      //reset counter to original value
      // otherwise we would skip -1 time
      fRng->SetCounter(counter);
      return fRng->Rndm();
   }

   // generate one integer number 
   template<int N, int S>
   uint64_t MixMaxEngine<N,S>::IntRndm() {
      int counter = fRng->Counter();
      SkipFunction<S>::Apply(fRng, counter,N);
      fRng->SetCounter(counter);
      return fRng->IntRndm();
   }

   template<int N, int S>
   uint64_t MixMaxEngine<N,S>::MaxInt() { 
      //return mixmax::mixmax_engine<N>::max();
      return  2305843009213693951ULL; 
   }

   template<int N, int S>
   uint64_t MixMaxEngine<N,S>::MinInt() { 
      //return mixmax::mixmax_engine<N>::min();
      return 0; 
   }

   template<int N, int S>
   void MixMaxEngine<N,S>::RndmArray(int n, double *array){
      // Return an array of n random numbers uniformly distributed in ]0,1]
      for (int i = 0; i < n; ++i)
         array[i] = Rndm_impl();
   }

   template<int N, int S>
   void MixMaxEngine<N,S>::SetState(const std::vector<StateInt_t> & state) {
      assert(state.size() >= N);
      // for (int i = 0; i < N; ++i)
      //    fRng->S.V[i] = state[i];
      // //set counter  to fore iteration afterwards
      // fRng->S.counter = N;
      fRng->SetState(state);
      fRng->SetCounter(N); 
   }

   template<int N, int S>
   void MixMaxEngine<N,S>::GetState(std::vector<StateInt_t> & state) const {
      state.resize(N);
      fRng->GetState(state); 
   }

   template<int N, int S>
   int MixMaxEngine<N,S>::Size()  {
      return MixMaxEngineImpl<N>::Size(); 
   }

   template<int N, int S>
   int MixMaxEngine<N,S>::Counter() const {
      return fRng->Counter(); 
   }

   template<int N, int S>
   const char *MixMaxEngine<N, S>::Name() {
      static const std::string name = "MixMax" + Util::ToString(N) + (S > 0 ? "_" + Util::ToString(S) : "");
      return name.c_str();
   }
   
   } // namespace Math
} // namespace ROOT

#endif
