#pragma once
/* скорости рандомов:
rand > lcg_parkmiller > irand > z26_random > RPNG > DOOM_RANDOM */
#include <random>
#include "doom-rand-table.hpp"
#include "error.hpp"
#include "macro.hpp"
namespace a83 {
uint rseed = 1; ///< зерно генераторов
auto rand_generator = std::minstd_rand();
//! примитивный рандом
uint RPNG() {
  rseed = (8253729 * rseed + 2396403);
  return rseed % 32767;
}
//! указать зерно для генераторов
void set_random_seed(uint seed) {
  rseed = seed;
  if ( !rseed)
    rseed = 1;
  srand(rseed);
  rand_generator.seed(rseed);
}
//! табличный рандом Думчика
CN(uint8_t) DOOM_RANDOM() {
  static uint8_t index = rseed;
  return random_map[index++]; // idex = 8b, no use bound check
}
//! получить случайное число из таблицы рандома
template <typename T> CN(uint8_t) doom_table(T index)
  { return random_map[uint8_t(index)]; }
//! рандом из эмулятора z26
uint8_t z26_random() {
  static uint8_t r_seed = rseed;
  r_seed = 
    (((((r_seed & 0x80) >> 7)   ^
       ((r_seed & 0x20) >> 5))  ^
      (((r_seed & 0x10) >> 4)   ^
       ((r_seed & 0x08) >> 3))) ^ 1) |
        (r_seed         << 1);
  return r_seed;
}
//! быстрый хороший рандом
uint32_t lcg_parkmiller() {
  static uint32_t state = rseed;
  // Precomputed parameters for Schrage's method
  constexpr uint32_t M = 0x7fffffff;
  constexpr uint32_t A = 48271;
  constexpr uint32_t Q = M / A;    // 44488
  constexpr uint32_t R = M % A;    //  3399
  uint32_t div = state / Q;	// max: M / Q = A = 48,271
  uint32_t rem = state % Q;	// max: Q - 1     = 44,487
  int32_t s = rem * A;	// max: 44,487 * 48,271 = 2,147,431,977 = 0x7fff3629
  int32_t t = div * R;	// max: 48,271 *  3,399 =   164,073,129
  int32_t result = s - t;
  if (result < 0)
    result += M;
  return state = result;
}
int32_t irand() { return rand_generator(); }
int32_t irand(int32_t _min, int32_t _max) {
  #ifdef DEBUG
  if (_max <= _min)
    a83::error("irand: _max >= _min"); 
  #endif
  return _min + (rand_generator() % (_max - _min));
}
int32_t irand_fast() { return lcg_parkmiller(); }
//! случайное число от 0.0 до 1.0
float frand() { return float(irand()) / float(rand_generator.max()); }
float drand() { return float(irand()) / double(rand_generator.max()); }
//! равном в пределах (-1, 1)
float sfrand() { return frand() * 2.0f - 1.0f; }
//! случайное число от 0.0 до 1.0 (быстрое)
float frand_fast() {
  return RPNG() * float(1.0 / 32767.0);
}
//! равном в пределах (-1, 1) (быстрое)
float sfrand_fast() { return frand_fast() * 2.0f - 1.0f; }
} // namespace a83