#include <stdint.h>
#include <iostream>
#include "lxm.hpp"
//#include <algorithm>
//#include <thread>
//#include <ctime>
//#include <ncurses.h>
//#include <atomic>
//#include <numeric>
//#include <math.h>
//#include <string>
//#include <unistd.h>
//#include <cstring>
//#include <stdlib.h>
uint64_t z;
uint64_t tNum = 0;
const uint64_t seed = 88172645463325252ull;
#define THREADS 1
#define MILLION 1000*1000
uint64_t x = seed + tNum * UINT64_MAX / THREADS * 0x9e3779b97f4a7c15ull;
const uint64_t million = 1000*1000;
const uint64_t billion = 1000*million;
using namespace std;
//using namespace std::chrono;
//high_resolution_clock::time_point simStartTime, updateTime, updateDiskTime, timeNow;
uint8_t bitsremaining = 64;

lxm_random::lxm_random rng(seed);
uint16_t card[417];
double Rank[17], StartRank[17];

inline void swapcards(uint8_t&  a, uint8_t&  b) {const uint8_t  c = a; a = b; b = c;}
inline void swapcards(uint16_t& a, uint16_t& b) {const uint16_t c = a; a = b; b = c;}
inline uint16_t randbits(const uint8_t bitsneeded) {
  const uint16_t bitmask[10] = {0,1,3,7,15,31,63,127,255,511};
  uint16_t result;
  if (bitsneeded==bitsremaining) {
    result = z & bitmask[bitsneeded];
    z = rng();
    bitsremaining = 64;
    return result;
  }
  if (bitsneeded>bitsremaining) { // to do: recycle leftover bits
    z = rng();
    bitsremaining = 64;
  }
  bitsremaining -= bitsneeded;
  result = z & bitmask[bitsneeded];
  z >>= bitsneeded;
  return result;
}
template<typename T> void shuffleAll(T * card, uint16_t n) {
  uint16_t b;
  while (n>256) {
    do b = randbits(9)+1; while (b>n);
    swapcards(card[n--], card[b]);
  }
  while (n>128) {
    do b = randbits(8)+1; while (b>n);
    swapcards(card[n--],card[b]);
  }
  while (n>64) {
    do b = randbits(7)+1; while (b>n);
    swapcards(card[n--],card[b]);
  }
  while (n>32) {
    do b = randbits(6)+1; while (b>n);
    swapcards(card[n--],card[b]);
  }
  while (n>16) {
    do b = randbits(5)+1; while (b>n);
    swapcards(card[n--],card[b]);
  }
  while (n>8) {
    do b = randbits(4)+1; while (b>n);
    swapcards(card[n--],card[b]);
  }
  while (n>4) {
    do b = randbits(3)+1; while (b>n);
    swapcards(card[n--],card[b]);
  }
  while (n>2) {
    do b = randbits(2)+1; while (b>n);
    swapcards(card[n--],card[b]);
  }
  swapcards(card[2],card[randbits(1)+1]);
}
#define DECKS 8
#define CARDS_IN_DECK DECKS*52
#define WASH_COUNT 36000/DECKS
#define NUM_SHUFFLES (unsigned long long)510*MILLION
uint64_t sum = 0;
uint64_t z2 = 0;
uint8_t washes   [WASH_COUNT   +1][CARDS_IN_DECK+1];
uint8_t freshDeck[CARDS_IN_DECK+1] = {0};
uint16_t freshShuffle[CARDS_IN_DECK+1] = {0};
int main() {
  lxm_random::lxm_random rng(42);
  //std::cout << rng() << std::endl;
  for (int j = 1; j <= CARDS_IN_DECK; ++j) {
    freshDeck[j] = (j%13>8) ? 10 : (j%13)+1;
    freshShuffle[j] = j;
  }
  for (int w = 1; w <=WASH_COUNT; ++w) { // Stack & Shuffle Washes
    for (int j = 1; j <= CARDS_IN_DECK; ++j) washes[w][j] = freshDeck[j];
    shuffleAll(&washes[w][0], CARDS_IN_DECK);
    // deal single pass shuffle
    if (z2<NUM_SHUFFLES) {
      for (int k = CARDS_IN_DECK; k; --k) sum += washes[w][k];
      if (++z2>=NUM_SHUFFLES) goto DONE;
    }
  }
  if (NUM_SHUFFLES==0) goto DONE;

  // deal double pass shuffles
  while (1) {
    uint16_t shuffle[CARDS_IN_DECK+1] = {0};
    for (int i = 1; i <= CARDS_IN_DECK; ++i) shuffle[i] = freshShuffle[i]; // or memcpy
    shuffleAll(&shuffle[0], CARDS_IN_DECK);
    for (int w = 1; w<=WASH_COUNT; ++w) {
      for (int k = CARDS_IN_DECK; k; --k) {sum += washes[w][shuffle[k]];}
      if (++z2>=NUM_SHUFFLES) goto DONE;
    }
  };

  DONE:
  std::cout<<std::endl<<std::endl;
  std::cout << z2 << std::endl;
  if ((double)sum / (4*DECKS*(double)z2)!=85) std::cout << "CHECKSUM ERROR!!!!!  >>> " << (double)sum / (4*DECKS*(double)z2);
}

// shuffles per minute (million)
// single deck   4115
// double deck   2090
// four decks     950
// six decks      655
// eight decks    510

// cards per minute (billion)
// single deck  210
// double deck  217
// four decks   199
// six decks    203
// eight decks  206

