////////////////////////////////////////////////////////////////////////////////
// 
// Global.hh
//
//    produced: 19/03/98 jr
// 
////////////////////////////////////////////////////////////////////////////////

#ifndef GLOBAL_HH
#define GLOBAL_HH

#include <cstdint>
#include <cmath>
#include <mutex>
#include <string>
#include <iostream>
#include <chrono>
#include <random>

namespace topcom {
  
  typedef enum { lexmin_mode, colexmax_mode } comparison_mode_type;
  
  typedef std::uint64_t      block_type;       // type for blocks of a bitstring
  typedef std::size_t        size_type;        // type for sizes of containers like memory size and numbers of objects
  typedef int                parameter_type;   // type for element labels like no or rank, *** must be signed! ***
  typedef enum {DIR_UP, DIR_DOWN, DIR_BOTH, DIR_RANDOM} direction_type;

  class CommentGrinder {
  public:
    constexpr static char COMMENTCHAR = '#';
  private:
    bool _verbose;
  public:
    inline CommentGrinder() : _verbose(true) {}
    inline CommentGrinder(bool verbose) : _verbose(verbose) {}
    inline bool verbose() const { return _verbose; }
  };
  
  inline std::istream& operator>>(std::istream& ist, CommentGrinder& commentgrinder) {
    std::string comment;
    ist >> std::ws;    
    while (ist.peek() == CommentGrinder::COMMENTCHAR) {
      
      // eat the rest of the line:
      std::getline(ist, comment);
      if (commentgrinder.verbose()) {
	std::cerr << "• comment: " << comment << std::endl;
      }
      ist >> std::ws;
    }
    return ist;
  }
  
  namespace global {

    // generic computations:

    class RandomIntegerGenerator {
    private:
      std::random_device               _random_device;
      std::mt19937                     _random_engine;
    public:
      inline RandomIntegerGenerator(long seed = std::chrono::system_clock::now().time_since_epoch().count()) :
	_random_device(),
	_random_engine(seed) {}
      inline long operator()(const long min, const long max) {
	std::uniform_int_distribution<> distribution(min, max);
	return distribution(_random_engine);
      }
    };
    
    inline size_type factorial(const parameter_type n) {
      size_type result(1);
      for (parameter_type i = 2; i < n; ++i) {
	result *= i;
      }
      return result;
    }
    
    
    // the binomial is usually used to compute a size for a cache:
    inline size_type binomial(const parameter_type n, const parameter_type k) {
      size_type kk(k);
      if (n - k < k) {
	kk = n - k;
      }
      if (k == 0) {
	return 1;
      }
      if (k == 1) {
	return n;
      }
      if (kk < 1) {
	return 1;
      }
      size_type result(1);
      for (parameter_type i = 0; i < kk; ++i) {
	result *= n - i;
	result /= i + 1;
      }
      return result;
    }
    
    // a bit-reverse implementation by Knuth from
    // https://matthewarcus.wordpress.com/2012/11/18/reversing-a-64-bit-word/
    // for 64bit bitstrings read on 2020/02/02:
    template <typename T, T m, int k>
    static inline T swapbits(T p) {
      T q = ((p>>k)^p)&m;
      return p^q^(q<<k);
    }
    
    inline block_type kbitreverse (block_type n)
    {
      static const block_type m0 = 0x5555555555555555LLU;
      static const block_type m1 = 0x0300c0303030c303LLU;
      static const block_type m2 = 0x00c0300c03f0003fLLU;
      static const block_type m3 = 0x00000ffc00003fffLLU;
      n = ((n>>1)&m0) | (n&m0)<<1;
      n = swapbits<block_type, m1, 4>(n);
      n = swapbits<block_type, m2, 8>(n);
      n = swapbits<block_type, m3, 20>(n);
      n = (n >> 34) | (n << 30);
      return n;
    }
    
#ifndef BIT_BUILTIN_FUNCTIONS
    inline int fls(uint64_t x) {
      x|=(x>>1ul);
      x|=(x>>2ul);
      x|=(x>>4ul);
      x|=(x>>8ul);
      x|=(x>>16ul);
      x|=(x>>32ul);
      x^=(x>>1ul);
      return std::log2(x);
    }
#endif
    
  }; // namespace global    
}; // namespace topcom
  
#endif

  // eof Global.hh
