IsaacRandom.h

// IsaacRandom.h

#pragma once
#ifndef Ceda_cxUtils_IsaacRandom_H
#define Ceda_cxUtils_IsaacRandom_H

/*
ISAAC: a fast cryptographic random number generator

ISAAC (Indirection, Shift, Accumulate, Add, and Count) generates 32-bit random numbers. Averaged 
out, it requires 18.75 machine cycles to generate each 32-bit value. Cycles are guaranteed to be 
at least 2^40 values long, and they are 2^8295 values long on average. The results are uniformly
distributed, unbiased, and unpredictable unless you know the seed.
*/

#include "BasicTypeSizes.h"
#include "cxUtils.h"
#include <mutex>
#include <type_traits>
#include <limits>

namespace ceda
{

// Recommended: 8 for crypto, 4 for simulations
const int RANDSIZL = 8;  

// Context of random number generator
struct randctx
{
    uint32 randcnt;
    uint32 randrsl[1 << RANDSIZL];
    uint32 randmem[1 << RANDSIZL];
    uint32 randa;
    uint32 randb;
    uint32 randc;
};

struct cxUtils_API IsaacSeed
{
    IsaacSeed() : seed1(0), seed2(0), seed3(0), seed4(0) {}
    
    int32 seed1;
    int32 seed2;
    int32 seed3;
    int32 seed4;
    void SeedFromClock();
};

class cxUtils_API IsaacRandomNumberGen
{
public:
    explicit IsaacRandomNumberGen(bool seedFromClock = false);
    IsaacRandomNumberGen(int32 seed1, int32 seed2, int32 seed3, int32 seed4);
    IsaacRandomNumberGen(const IsaacSeed& seed);
    
    void SeedFromClock();

    void Init(int32 seed1=0, int32 seed2=0, int32 seed3=0, int32 seed4=0);
    void Init(const IsaacSeed& seed);
    
    // Measured generation rate:  20 million per sec on 2GHz Pentium IV
    int32 operator()();

    // Generate random number in range [x1,x2).  Interval must be non-empty.
    // Measured generation rate:  11 million per sec on 2GHz Pentium IV
    int32 GetUniformDistInteger(int32 x1,int32 x2);

    int64 GetUniformDistInteger(int64 x1,int64 x2);

    template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
    T GetUniformDistInteger(T x1, T x2)
    {
        if constexpr (std::numeric_limits<T>::digits <= 32)
        {
            return static_cast<T>(GetUniformDistInteger(static_cast<int32>(x1),static_cast<int32>(x2)));
        }
        else
        {
            return static_cast<T>(GetUniformDistInteger(static_cast<int64>(x1),static_cast<int64>(x2)));
        }
    }

    ssize_t GetUniformDistInteger_ssize_t(ssize_t x1, ssize_t x2)
    {
        return GetUniformDistInteger(x1,x2);
    }
    
    //double GetUniformDistDouble(double x1,double x2);
    
    // Use this random number generator to create a random seed value.
    void SetRandomSeed(IsaacSeed& seed);

private:
    randctx ctx;    
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// ThreadSafeRandomNumberGen

class ThreadSafeRandomNumberGen
{
public:
    int32 GetUniformDistInteger(int32 x1,int32 x2)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        return isaac_.GetUniformDistInteger(x1,x2);
    }

    int64 GetUniformDistInteger(int64 x1,int64 x2)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        return isaac_.GetUniformDistInteger(x1,x2);
    }

    template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
    T GetUniformDistInteger(T x1, T x2)
    {
        if constexpr (std::numeric_limits<T>::digits <= 32)
        {
            return static_cast<T>(GetUniformDistInteger(static_cast<int32>(x1),static_cast<int32>(x2)));
        }
        else
        {
            return static_cast<T>(GetUniformDistInteger(static_cast<int64>(x1),static_cast<int64>(x2)));
        }
    }
    
    ssize_t GetUniformDistInteger_ssize_t(ssize_t x1, ssize_t x2)
    {
        return GetUniformDistInteger(x1,x2);
    }

private:
    IsaacRandomNumberGen isaac_;
    mutable std::mutex mutex_;
};

} // namespace ceda

#endif // include guard