#ifndef XOROLIB_CUH
#define XOROLIB_CUH

#include <stdint.h>

//==================RNG================
typedef struct
{
    uint64_t lo, hi;
} Xoroshiro;

uint64_t rotl64(uint64_t x, uint8_t b)
{
    return (x << b) | (x >> (64-b));
}

void xSetSeed(Xoroshiro *xr, uint64_t value)
{
    const uint64_t XL = 0x9e3779b97f4a7c15ULL;
    const uint64_t XH = 0x6a09e667f3bcc909ULL;
    const uint64_t A = 0xbf58476d1ce4e5b9ULL;
    const uint64_t B = 0x94d049bb133111ebULL;
    uint64_t l = value ^ XH;
    uint64_t h = l + XL;
    l = (l ^ (l >> 30)) * A;
    h = (h ^ (h >> 30)) * A;
    l = (l ^ (l >> 27)) * B;
    h = (h ^ (h >> 27)) * B;
    l = l ^ (l >> 31);
    h = h ^ (h >> 31);
    xr->lo = l;
    xr->hi = h;
}

uint64_t xNextLong(Xoroshiro *xr)
{
    uint64_t l = xr->lo;
    uint64_t h = xr->hi;
    uint64_t n = rotl64(l + h, 17) + l;
    h ^= l;
    xr->lo = rotl64(l, 49) ^ h ^ (h << 21);
    xr->hi = rotl64(h, 28);
    return n;
}

//thanks @tremeschin
#define SKIP_REJECTION 0

int xNextInt(Xoroshiro *xr, uint32_t n) {
    uint64_t r = (xNextLong(xr) & 0xFFFFFFFF) * n;

    #if !SKIP_REJECTION
        if ((uint32_t)r < n) {
            while ((uint32_t)r < (~n + 1) % n) {
                r = (xNextLong(xr) & 0xFFFFFFFF) * n;
            }
        }
    #endif

    return r >> 32;
}

double xNextDouble(Xoroshiro *xr)
{
    return (xNextLong(xr) >> (64-53)) * 1.1102230246251565E-16;
}

int xNextBoolean(Xoroshiro* xr)
{
    return xNextLong(xr) & 1;
}

//=================END_RNG==================

#endif // XOROLIB_CUH
