Compare commits
No commits in common. 'master' and 'lib' have entirely different histories.
@ -1,168 +1,215 @@
|
||||
// C api for rngxx
|
||||
#ifndef _RNGXX_H
|
||||
#define _RNGXX_H
|
||||
|
||||
#ifndef _RNGXX_IMPL
|
||||
#define _RNGXX_COMMON_MINIMAL
|
||||
#endif
|
||||
#include "rngxx/internal/common.h"
|
||||
#ifndef _RNGXX_IMPL
|
||||
#undef _RNGXX_COMMON_MINIMAL
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#ifndef _RNGXX_IMPL_ONLY_TYPES
|
||||
#warning "C++ compilers might not like this header file. Please use the C++ interface for C++ TUs"
|
||||
#endif
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// -- C API types -- (C++ compat)
|
||||
typedef struct Random rng_t;
|
||||
|
||||
enum rng_kind {
|
||||
RNG_KIND_CRAND,
|
||||
RNG_KIND_SM64,
|
||||
};
|
||||
|
||||
enum rng_next_flag {
|
||||
RNG_TYQ_SIGNED = 0, // Binary with `unsigned`.
|
||||
RNG_TYQ_UNSIGNED = 1 << 0,
|
||||
|
||||
RNG_TYQ_CHAOS = 1 << 1,
|
||||
RNG_TYQ_ARRAY = 1 << 2,
|
||||
}; // 3 bits
|
||||
|
||||
typedef int rng_bool_t;
|
||||
|
||||
enum rng_next_type {
|
||||
RNG_TY_BLOB = 0,
|
||||
|
||||
RNG_TY_BOOL, // Expects type `rng_bool_t`.
|
||||
|
||||
RNG_TY_INT8,
|
||||
RNG_TY_INT16,
|
||||
RNG_TY_INT32,
|
||||
RNG_TY_INT64,
|
||||
|
||||
RNG_TY_F32,
|
||||
RNG_TY_F64,
|
||||
}; // 4 bits
|
||||
|
||||
struct rng_next_opt {
|
||||
union {
|
||||
// Bounded types
|
||||
struct {
|
||||
const void* restrict pmin;
|
||||
const void* restrict pmax;
|
||||
} range;
|
||||
// Blob types
|
||||
size_t len;
|
||||
} bound;
|
||||
|
||||
struct {
|
||||
enum rng_next_type type : 4;
|
||||
enum rng_next_flag mod : 3;
|
||||
uint8_t _unused0 : 1;
|
||||
//first byte
|
||||
|
||||
uint32_t _padding : 24;
|
||||
} ty; // 32 bits :/
|
||||
union {
|
||||
uint32_t array_size;
|
||||
} ex; // 32 bits. fills hole
|
||||
};
|
||||
|
||||
typedef __typeof( ((struct rng_next_opt*)NULL)->ty ) rng_next_tyq; // Qualified type
|
||||
|
||||
typedef rng_t* (*rng_ctor_fn)(const void*);
|
||||
|
||||
typedef struct rng_dyn_ctor* rng_dyn_ctor_ref;
|
||||
#pragma once
|
||||
|
||||
// -- // --
|
||||
#ifndef _RNGXX_IMPL_ONLY_TYPES
|
||||
#include <bit>
|
||||
#include <stdexcept>
|
||||
#include <array>
|
||||
|
||||
_Static_assert(sizeof(rng_next_tyq) == sizeof(uint32_t));
|
||||
_Static_assert(sizeof( ((struct rng_next_opt*)NULL)->ex ) == sizeof(uint32_t));
|
||||
#include <climits>
|
||||
|
||||
// -- C API functions -- (C++ NO compat)
|
||||
#include "rngxx/common.h"
|
||||
|
||||
int rng_raw(rng_t* engine, void* restrict output, struct rng_next_opt opt);
|
||||
void rng_next_bin(rng_t* engine, unsigned char* restrict output, size_t n);
|
||||
#define CTOR_COPY(name) name(const name& copy)
|
||||
#define CTOR_MOVE(name) name(name&& move)
|
||||
|
||||
void* rng_next_ty(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags);
|
||||
void* rng_next_tya(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, uint32_t sz);
|
||||
void* rng_next_tyab(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, uint32_t sz, const void* pmin, const void* pmax);
|
||||
void* rng_next_tyb(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, const void* pmin, const void* pmax);
|
||||
namespace _rng__util {
|
||||
struct divp
|
||||
{
|
||||
inline consteval divp(usize n, usize o)
|
||||
: d(n / o),
|
||||
r(n % o){}
|
||||
const usize d,r;
|
||||
|
||||
rng_dyn_ctor_ref rng_ctor_ref(const char* name);
|
||||
rng_ctor_fn rng_ctor(const char* name) __attribute__((deprecated("many dynamic ctors will have state that cannot be expressed through a raw function pointer, and so this method will not work for them. use `rng_ctor_ref` and `rng_ctor_call` instead where possible, unless you know that the dynamic ctor is a bare function pointer."))); // Get a dynamic ctor that is a raw funciton pointer. If the ctor for this name is not a raw function pointer (if it has state), then NULL is returned.
|
||||
rng_t* rng_ctor_call(rng_dyn_ctor_ref ctor, const void* seed);
|
||||
inline consteval bool exact() const { return d && !r; }
|
||||
inline consteval bool none() const { return !d; }
|
||||
|
||||
rng_t* rng_new_named(const char* name, const void* seed);
|
||||
rng_t* rng_new(enum rng_kind kind, const u64 seed[static restrict 1]);
|
||||
void rng_free(rng_t* rng);
|
||||
|
||||
#ifdef _RNGXX_INLINE_ONLY
|
||||
#define _rng__always_inline __attribute__((gnu_inline)) inline extern
|
||||
#else
|
||||
#define _rng__always_inline __attribute__((gnu_inline)) inline static
|
||||
#endif
|
||||
|
||||
#define _NEXT_TYPED_(name, T, N, S) \
|
||||
_rng__always_inline T rng_next_ ## name (rng_t* engine, const T* min, const T* max) \
|
||||
{ \
|
||||
T output; \
|
||||
rng_next_tyb(engine, &output, RNG_TY_ ## N, RNG_TYQ_ ## S, min, max); \
|
||||
return output; \
|
||||
} \
|
||||
_rng__always_inline T* rng_nexta_ ## name(uint32_t len; rng_t* engine, T output[restrict len], uint32_t len, const T* min, const T* max) \
|
||||
{ \
|
||||
return rng_next_tyab(engine, output, RNG_TY_ ## N, RNG_TYQ_ ## S, len, min, max); \
|
||||
} \
|
||||
_rng__always_inline T* rng_nextam_ ## name(rng_t* engine, uint32_t len, const T* min, const T* max) \
|
||||
{ \
|
||||
return rng_next_tyab(engine, NULL, RNG_TY_ ## N, RNG_TYQ_ ## S, len, min, max); \
|
||||
template<typename T>
|
||||
inline static consteval divp type(usize bytes) { return divp(bytes, sizeof(T)); }
|
||||
};
|
||||
}
|
||||
|
||||
#define _NEXT_TYPED(T, N, S) _NEXT_TYPED_(T, T, N, S)
|
||||
|
||||
#define NEXT_TYPED_INT(n) _NEXT_TYPED(i ## n, INT ## n, SIGNED) \
|
||||
_NEXT_TYPED(u ## n, INT ## n, UNSIGNED)
|
||||
|
||||
NEXT_TYPED_INT(8)
|
||||
NEXT_TYPED_INT(16)
|
||||
NEXT_TYPED_INT(32)
|
||||
NEXT_TYPED_INT(64)
|
||||
|
||||
_NEXT_TYPED(f32, F32, SIGNED)
|
||||
_NEXT_TYPED(f64, F64, SIGNED)
|
||||
|
||||
_rng__always_inline rng_bool_t rng_next_bool(rng_t* engine)
|
||||
{
|
||||
rng_bool_t output;
|
||||
rng_next_tyb(engine, &output, RNG_TY_BOOL, 0, NULL, NULL);
|
||||
return output & 1;
|
||||
}
|
||||
_rng__always_inline rng_bool_t* rng_nexta_bool(uint32_t len; rng_t* engine, rng_bool_t output[restrict len], uint32_t len)
|
||||
{
|
||||
return rng_next_tyab(engine, output, RNG_TY_BOOL, 0, len, NULL, NULL);
|
||||
}
|
||||
_rng__always_inline rng_bool_t* rng_nextam_bool(rng_t* engine, uint32_t len)
|
||||
struct InvalidRandomSample final : public std::exception {
|
||||
inline explicit InvalidRandomSample(f64 s) : std::exception(), value(s){}
|
||||
inline CTOR_COPY(InvalidRandomSample): std::exception(copy), value(copy.value){}
|
||||
|
||||
const f64 value;
|
||||
};
|
||||
struct ObjectMoved final : public std::exception{};
|
||||
|
||||
/// Interface for a simple random number generator
|
||||
///
|
||||
/// # Must override
|
||||
/// f64 _sample() // A representation of a random range. The value must be between `0..=1`
|
||||
///
|
||||
/// # Should override
|
||||
/// void next_bytes(u8* ptr, usize n); // Random bytes. The default implementation falls back to _sample() for each byte. This is very inefficient.
|
||||
///
|
||||
/// void next_v32(u32* ptr, usize n); // Vectorised random bytes (4 bytes ptr iteration.) If 32-bit vectorised outputs are possible for your implementation, you should override this (falls back to next_bytes(ptr, n)) NOTE: `n` is the number of `u32`s `ptr` points to, **not** the number of bytes.
|
||||
/// void next_v64(u64* ptr, usize n); // Same as above, but for 8 byte iterations (64 bits.)
|
||||
struct Random
|
||||
{
|
||||
return rng_next_tyab(engine, NULL, RNG_TY_BOOL, 0, len, NULL, NULL);
|
||||
}
|
||||
|
||||
#undef NEXT_TYPED_INT
|
||||
#undef _NEXT_TYPED
|
||||
#undef _NEXT_TYPED_
|
||||
template<typename T>
|
||||
struct iterator { //TODO: Implement this in another file (has to be header because of template :/)
|
||||
friend class Random;
|
||||
|
||||
#undef _rng__always_inline
|
||||
//TODO: Make this work with foreach(), and STL iterator APIs somehow
|
||||
|
||||
// -- // --
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _RNGXX_H */
|
||||
inline CTOR_COPY(iterator<T>) : rng(copy.rng){}
|
||||
inline CTOR_MOVE(iterator<T>) : rng(move.rng) {
|
||||
*const_cast<Random**>(&move.rng) = nullptr;
|
||||
}
|
||||
|
||||
inline virtual ~iterator(){}
|
||||
|
||||
protected:
|
||||
virtual T _sample() { if (rng) return rng->next<T>(); else throw ObjectMoved(); }
|
||||
virtual inline void _init() {}
|
||||
private:
|
||||
inline explicit iterator(Random& rng) : rng(&rng){ _init(); }
|
||||
|
||||
Random* const rng;
|
||||
};
|
||||
public:
|
||||
|
||||
inline Random(){}
|
||||
inline virtual ~Random(){}
|
||||
|
||||
inline f64 next_f64() { return sample(); }
|
||||
inline f32 next_f32() { return (f32)sample(); }
|
||||
|
||||
virtual bool next_bool();
|
||||
|
||||
#define NDEF(t) inline virtual t next_ ## t() { return next_ ## t(_max_ ## t()); }
|
||||
#define NDEFF(n) NDEF(i ## n) NDEF(u ## n)
|
||||
NDEFF(8)
|
||||
NDEFF(16)
|
||||
NDEFF(32)
|
||||
NDEFF(64)
|
||||
#undef NDEFF
|
||||
#undef NDEF
|
||||
virtual void next_u8(u8* a, usize n);
|
||||
virtual void next_i8(i8* a, usize n);
|
||||
|
||||
virtual void next_u16(u16* a, usize n);
|
||||
virtual void next_i16(i16* a, usize n);
|
||||
|
||||
virtual void next_u32(u32* a, usize n);
|
||||
virtual void next_i32(i32* a, usize n);
|
||||
|
||||
virtual void next_u64(u64* a, usize n);
|
||||
virtual void next_i64(i64* a, usize n);
|
||||
|
||||
virtual i8 next_i8(i8 max);
|
||||
i8 next_i8(i8 min, i8 max);
|
||||
|
||||
virtual u8 next_u8(u8 max);
|
||||
u8 next_u8(u8 min, u8 max);
|
||||
|
||||
virtual u16 next_u16(u16 max);
|
||||
u16 next_u16(u16 min, u16 max);
|
||||
|
||||
virtual i16 next_i16(i16 max);
|
||||
i16 next_i16(i16 min, i16 max);
|
||||
|
||||
virtual i32 next_i32(i32 max);
|
||||
i32 next_i32(i32 min, i32 max);
|
||||
|
||||
virtual i64 next_i64(i64 max);
|
||||
i64 next_i64(i64 min, i64 max);
|
||||
|
||||
virtual u32 next_u32(u32 max);
|
||||
u32 next_u32(u32 min, u32 max);
|
||||
|
||||
virtual u64 next_u64(u64 max);
|
||||
u64 next_u64(u64 min, u64 max);
|
||||
|
||||
virtual inline f32 next_f32(f32 max) { return next_f32() * max; }
|
||||
inline f32 next_f32(f32 min, f32 max) { return min + next_f32(max-min); }
|
||||
|
||||
virtual inline f64 next_f64(f64 max) { return next_f64() * max; }
|
||||
inline f64 next_f64(f64 min, f64 max) { return min + next_f64(max-min); }
|
||||
|
||||
virtual void next_bytes(u8* bytes, usize n);
|
||||
|
||||
template<usize N>
|
||||
inline void next_bytes(u8 (&a)[N]) {
|
||||
_next_bytes<N>(a);
|
||||
}
|
||||
template<usize N>
|
||||
inline void next_bytes(std::array<u8, N>& ar)
|
||||
{
|
||||
// XXX: this doesn't seem to work (aliasing issues?)
|
||||
_next_bytes<N>(&ar[0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T next() {
|
||||
std::array<u8, sizeof(T)> arr;
|
||||
|
||||
next_bytes(arr);
|
||||
|
||||
return std::bit_cast<T>(arr);
|
||||
}
|
||||
template<typename T>
|
||||
inline iterator<T> iter() { return iterator(*this); } //TODO: An iterator that yields `next<T>()` forever.
|
||||
protected:
|
||||
//TODO: Should we have _min_* functions too? or just continue to use 0 as the lower bound for next_*(..max)? I think use 0...
|
||||
#define MAX(T, M) constexpr inline virtual T _max_ ## T() const { return M; }
|
||||
#define MAXX(n) MAX(u ## n, UINT ## n ## _MAX) MAX(i ## n, INT ## n ## _MAX)
|
||||
MAXX(8)
|
||||
MAXX(16)
|
||||
MAXX(32)
|
||||
MAXX(64)
|
||||
#undef MAXX
|
||||
#undef MAX
|
||||
//constexpr inline virtual i8 _max() const { return 100; } // use limits.h stuff instead.
|
||||
|
||||
// Vectorised versions of `next_bytes()`. These will fall back to that if they are not overriden, but if the implementation has a more efficient way of generating 4/8 bytes of random data it should override these.
|
||||
//
|
||||
// These should produce entirely random (not bounded or weighted) results, not confined to the integer types they take.
|
||||
virtual void next_v64(u64* p, usize n);
|
||||
virtual void next_v32(u32* p, usize n);
|
||||
|
||||
// Main sample function. Must return between 0..=1
|
||||
// If nothing else is overrided, this value is used for everything else.
|
||||
// It is recommended to override `next_bytes()` too however.
|
||||
virtual f64 _sample() = 0;
|
||||
|
||||
inline f64 sample()
|
||||
{
|
||||
auto s = _sample();
|
||||
if (UNLIKELY(s < 0 || s > 1)) throw InvalidRandomSample{ s };
|
||||
return s;
|
||||
}
|
||||
private:
|
||||
template<usize N>
|
||||
inline void _next_bytes(u8* a)
|
||||
{
|
||||
using namespace _rng__util;
|
||||
constexpr const auto rem64 = divp::type<u64>(N);
|
||||
u8* ptr = a;
|
||||
if constexpr(rem64.d) {
|
||||
next_v64(reinterpret_cast<u64*>(ptr), rem64.d); ptr+= rem64.d * sizeof(u64);
|
||||
}
|
||||
constexpr const auto rem32 = divp::type<u32>(rem64.r);
|
||||
if constexpr(rem32.d) {
|
||||
next_v32(reinterpret_cast<u32*>(ptr), rem32.d); ptr+=rem32.d * sizeof(u32);
|
||||
}
|
||||
if constexpr(rem32.r) {
|
||||
next_bytes(ptr, rem32.r);
|
||||
}
|
||||
}
|
||||
};
|
||||
#define DEFT(T) template<> inline T Random::next< T >() { return next_ ## T(); }
|
||||
#define DEFTT(n) DEFT(i ## n) DEFT(u ## n)
|
||||
DEFT(bool)
|
||||
DEFTT(8)
|
||||
DEFTT(16)
|
||||
DEFTT(32)
|
||||
DEFTT(64)
|
||||
//DEFTT(128)
|
||||
#undef DEFTT
|
||||
#undef DEFT
|
||||
|
||||
#undef CTOR_COPY
|
||||
#undef CTOR_MOVE
|
||||
|
@ -1,253 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <stdexcept>
|
||||
#include <array>
|
||||
|
||||
#include <climits>
|
||||
|
||||
#ifndef _RNGXX_IMPL
|
||||
#define _RNGXX_COMMON_MINIMAL
|
||||
#endif
|
||||
#include "rngxx/internal/common.h"
|
||||
#ifndef _RNGXX_IMPL
|
||||
#undef _RNGXX_COMMON_MINIMAL
|
||||
#endif
|
||||
|
||||
#define RNGXX_DCTOR_NAME(T) _rng__init_for_ ## T
|
||||
|
||||
#define CTOR_COPY(name) name(const name& copy)
|
||||
#define CTOR_MOVE(name) name(name&& move)
|
||||
|
||||
// ugly hack to get around the absolutely retarded name lookup restrictions when overriding
|
||||
#define RNG_OVERRIDE(ty, nm, rest) using Random::nm; ty nm rest override
|
||||
|
||||
namespace _rng__util {
|
||||
struct divp
|
||||
{
|
||||
inline consteval divp(usize n, usize o)
|
||||
: d(n / o),
|
||||
r(n % o){}
|
||||
const usize d,r;
|
||||
|
||||
inline consteval bool exact() const { return d && !r; }
|
||||
inline consteval bool none() const { return !d; }
|
||||
|
||||
template<typename T>
|
||||
inline static consteval divp type(usize bytes) { return divp(bytes, sizeof(T)); }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
struct InvalidRandomSample final : public std::exception {
|
||||
inline explicit InvalidRandomSample(f64 s) : std::exception(), value(s){}
|
||||
inline CTOR_COPY(InvalidRandomSample): std::exception(copy), value(copy.value){}
|
||||
|
||||
const f64 value;
|
||||
};
|
||||
struct ObjectMoved final : public std::exception{};
|
||||
|
||||
/// Interface for a simple random number generator
|
||||
///
|
||||
/// # Must override
|
||||
/// f64 _sample() // A representation of a random range. The value must be between `0..=1`
|
||||
///
|
||||
/// # Should override
|
||||
/// void next_bytes(u8* ptr, usize n); // Random bytes. The default implementation falls back to _sample() for each byte. This is very inefficient.
|
||||
///
|
||||
/// void next_v32(u32* ptr, usize n); // Vectorised random bytes (4 bytes ptr iteration.) If 32-bit vectorised outputs are possible for your implementation, you should override this (falls back to next_bytes(ptr, n)) NOTE: `n` is the number of `u32`s `ptr` points to, **not** the number of bytes.
|
||||
/// void next_v64(u64* ptr, usize n); // Same as above, but for 8 byte iterations (64 bits.)
|
||||
struct Random
|
||||
{
|
||||
template<typename T>
|
||||
struct iterator { //TODO: Implement this in another file (has to be header because of template :/)
|
||||
//XXX: C++ iterators are absolute jank. This is far harder than it should be.
|
||||
friend class Random;
|
||||
|
||||
//TODO: Make this work with foreach(), and STL iterator APIs somehow
|
||||
|
||||
inline CTOR_COPY(iterator<T>) : rng(copy.rng){}
|
||||
inline CTOR_MOVE(iterator<T>) : rng(move.rng) {
|
||||
*const_cast<Random**>(&move.rng) = nullptr;
|
||||
}
|
||||
|
||||
inline virtual ~iterator(){}
|
||||
|
||||
inline T next() { return _sample(); }
|
||||
|
||||
inline iterator& operator++() { return *this; }
|
||||
inline iterator& operator++(int) { return *this; }
|
||||
|
||||
inline T operator*() { return next(); }
|
||||
|
||||
friend auto operator<=>(const iterator<T>&, const iterator<T>&) = default;
|
||||
|
||||
protected:
|
||||
virtual T _sample() { if (LIKELY(rng)) return rng->next<T>(); else throw ObjectMoved(); }
|
||||
virtual inline void _init() {}
|
||||
private:
|
||||
inline explicit iterator(Random& rng) : rng(&rng){ _init(); }
|
||||
|
||||
Random* const rng;
|
||||
};
|
||||
public:
|
||||
|
||||
inline Random(){}
|
||||
inline virtual ~Random(){}
|
||||
|
||||
inline f64 next_f64() { return sample(); }
|
||||
inline f32 next_f32() { return (f32)sample(); }
|
||||
|
||||
virtual bool next_bool();
|
||||
|
||||
#define NDEF(t) inline virtual t next_ ## t() { return next_ ## t(_max_ ## t()); }
|
||||
#define NDEFF(n) NDEF(i ## n) NDEF(u ## n)
|
||||
NDEFF(8)
|
||||
NDEFF(16)
|
||||
NDEFF(32)
|
||||
NDEFF(64)
|
||||
#undef NDEFF
|
||||
#undef NDEF
|
||||
virtual void next_u8(u8* a, usize n);
|
||||
virtual void next_i8(i8* a, usize n);
|
||||
|
||||
virtual void next_u16(u16* a, usize n);
|
||||
virtual void next_i16(i16* a, usize n);
|
||||
|
||||
virtual void next_u32(u32* a, usize n);
|
||||
virtual void next_i32(i32* a, usize n);
|
||||
|
||||
virtual void next_u64(u64* a, usize n);
|
||||
virtual void next_i64(i64* a, usize n);
|
||||
|
||||
virtual i8 next_i8(i8 max);
|
||||
i8 next_i8(i8 min, i8 max);
|
||||
|
||||
virtual u8 next_u8(u8 max);
|
||||
u8 next_u8(u8 min, u8 max);
|
||||
|
||||
virtual u16 next_u16(u16 max);
|
||||
u16 next_u16(u16 min, u16 max);
|
||||
|
||||
virtual i16 next_i16(i16 max);
|
||||
i16 next_i16(i16 min, i16 max);
|
||||
|
||||
virtual i32 next_i32(i32 max);
|
||||
i32 next_i32(i32 min, i32 max);
|
||||
|
||||
virtual i64 next_i64(i64 max);
|
||||
i64 next_i64(i64 min, i64 max);
|
||||
|
||||
virtual u32 next_u32(u32 max);
|
||||
u32 next_u32(u32 min, u32 max);
|
||||
|
||||
virtual u64 next_u64(u64 max);
|
||||
u64 next_u64(u64 min, u64 max);
|
||||
|
||||
virtual inline f32 next_f32(f32 max) { return next_f32() * max; }
|
||||
inline f32 next_f32(f32 min, f32 max) { return min + next_f32(max-min); }
|
||||
|
||||
virtual inline f64 next_f64(f64 max) { return next_f64() * max; }
|
||||
inline f64 next_f64(f64 min, f64 max) { return min + next_f64(max-min); }
|
||||
|
||||
virtual void next_bytes(u8* bytes, usize n);
|
||||
|
||||
template<usize N>
|
||||
inline void next_bytes(u8 (&a)[N]) {
|
||||
_next_bytes<N>(a);
|
||||
}
|
||||
template<usize N>
|
||||
inline void next_bytes(std::array<u8, N>& ar)
|
||||
{
|
||||
// XXX: this doesn't seem to work (aliasing issues?)
|
||||
_next_bytes<N>(&ar[0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T next() {
|
||||
std::array<u8, sizeof(T)> arr;
|
||||
|
||||
next_bytes(arr);
|
||||
|
||||
return std::bit_cast<T>(arr);
|
||||
}
|
||||
template<typename T>
|
||||
inline T next(T min, T max);
|
||||
template<typename T>
|
||||
inline T next(T max);
|
||||
|
||||
template<typename T>
|
||||
inline iterator<T> iter() { return iterator<T>(*this); } //TODO: An iterator that yields `next<T>()` forever.
|
||||
|
||||
template<typename T>
|
||||
constexpr inline T max_for() const;
|
||||
protected:
|
||||
//TODO: Should we have _min_* functions too? or just continue to use 0 as the lower bound for next_*(..max)? I think use 0...
|
||||
#define MAX(T, M) constexpr inline virtual T _max_ ## T() const { return M; }
|
||||
#define MAXX(n) MAX(u ## n, UINT ## n ## _MAX) MAX(i ## n, INT ## n ## _MAX)
|
||||
MAXX(8)
|
||||
MAXX(16)
|
||||
MAXX(32)
|
||||
MAXX(64)
|
||||
#undef MAXX
|
||||
#undef MAX
|
||||
protected:
|
||||
//constexpr inline virtual i8 _max() const { return 100; } // use limits.h stuff instead.
|
||||
|
||||
// Vectorised versions of `next_bytes()`. These will fall back to that if they are not overriden, but if the implementation has a more efficient way of generating 4/8 bytes of random data it should override these.
|
||||
//
|
||||
// These should produce entirely random (not bounded or weighted) results, not confined to the integer types they take.
|
||||
virtual void next_v64(u64* p, usize n);
|
||||
virtual void next_v32(u32* p, usize n);
|
||||
|
||||
// Main sample function. Must return between 0..=1
|
||||
// If nothing else is overrided, this value is used for everything else.
|
||||
// It is recommended to override `next_bytes()` too however.
|
||||
virtual f64 _sample() = 0;
|
||||
|
||||
f64 sample();
|
||||
|
||||
private:
|
||||
template<usize N>
|
||||
inline void _next_bytes(u8* a)
|
||||
{
|
||||
using namespace _rng__util;
|
||||
constexpr const auto rem64 = divp::type<u64>(N);
|
||||
u8* ptr = a;
|
||||
if constexpr(rem64.d) {
|
||||
next_v64(reinterpret_cast<u64*>(ptr), rem64.d); ptr+= rem64.d * sizeof(u64);
|
||||
}
|
||||
constexpr const auto rem32 = divp::type<u32>(rem64.r);
|
||||
if constexpr(rem32.d) {
|
||||
next_v32(reinterpret_cast<u32*>(ptr), rem32.d); ptr+=rem32.d * sizeof(u32);
|
||||
}
|
||||
if constexpr(rem32.r) {
|
||||
next_bytes(ptr, rem32.r);
|
||||
}
|
||||
}
|
||||
};
|
||||
#define DEFTT(n) DEFT(i ## n) DEFT(u ## n)
|
||||
#define DEFT(T) template<> inline T Random::max_for< T >() const { return _max_ ## T(); }
|
||||
#define DEF \
|
||||
DEFTT(8) \
|
||||
DEFTT(16) \
|
||||
DEFTT(32) \
|
||||
DEFTT(64) \
|
||||
//DEFTT(128)
|
||||
DEF
|
||||
#undef DEFT
|
||||
#define DEFT(T) template<> inline T Random::next< T >() { return next_ ## T(); } \
|
||||
template<> inline T Random::next< T >(T min, T max) { return next_ ## T(min, max); } \
|
||||
template<> inline T Random::next< T >(T max) { return next_ ## T(max); }
|
||||
template<> inline bool Random::next<bool>() { return next_bool(); }
|
||||
//template<> inline f64 Random::next<f64>() { return next_f64(); }
|
||||
//template<> inline f32 Random::next<f32>() { return next_f32(); }
|
||||
DEFT(f32)
|
||||
DEFT(f64)
|
||||
DEF
|
||||
#undef DEF
|
||||
#undef DEFTT
|
||||
#undef DEFT
|
||||
|
||||
#undef CTOR_COPY
|
||||
#undef CTOR_MOVE
|
@ -1,14 +0,0 @@
|
||||
#ifndef _RNGXX_DCTOR_HACK_H
|
||||
#define _RNGXX_DCTOR_HACK_H
|
||||
|
||||
#include <rngxx.hpp>
|
||||
#include <rngxx/crand.h>
|
||||
#include <rngxx/sm64.h>
|
||||
|
||||
#include <init.hpp>
|
||||
// For some reason, dctors defined in some files do not work??!
|
||||
|
||||
RNGXX_APPLY_CTOR(rng, sm64, u64);
|
||||
RNGXX_APPLY_CTOR(rng, crand, u64);
|
||||
|
||||
#endif /* _RNGXX_DCTOR_HACK_H */
|
@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common.h"
|
||||
#include <rngxx.hpp>
|
||||
|
||||
namespace rng::init
|
||||
{
|
||||
using ctor_func = std::function<Random* (const void*)>;
|
||||
struct DCLookupFailed final : std::exception{ inline DCLookupFailed(const std::string_view& n) : std::exception(), name(std::move(n)){} std::string name; };
|
||||
|
||||
const ctor_func& apply_raw(std::string_view name, ctor_func fn) noexcept;
|
||||
|
||||
template<typename T, typename _Ptr = void>
|
||||
inline auto apply(std::string_view name, auto&& lam) noexcept
|
||||
{
|
||||
return apply_raw(name, [lam](const void* seed) -> Random* {
|
||||
return static_cast<Random*>(lam(reinterpret_cast<const _Ptr*>(seed)));
|
||||
});
|
||||
}
|
||||
|
||||
ctor_func& get(std::string_view name);
|
||||
}
|
||||
|
||||
// To add to dynamic CTOR map
|
||||
#define RNGXX_APPLY_CTOR(ns, T, TSeed) __attribute__((used, retain))\
|
||||
const static auto& RNGXX_DCTOR_NAME(T) = rng::init::apply<ns::T, TSeed>(#T, [](const TSeed* ptr) { return new ns::T(*ptr); })
|
||||
|
||||
//const extern init::ctor_func& RNGXX_DCTOR_NAME(sm64);
|
@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
namespace rng
|
||||
{
|
||||
template<typename T>
|
||||
struct seed_gen
|
||||
{
|
||||
virtual T generate_seed() = 0;
|
||||
|
||||
inline virtual ~seed_gen(){}
|
||||
};
|
||||
|
||||
namespace seed
|
||||
{
|
||||
struct splitmix64 : public virtual seed_gen<u64>
|
||||
{
|
||||
inline splitmix64(const splitmix64&) = default;
|
||||
explicit inline splitmix64(seed_gen<u64>& from) : m_state(from.generate_seed()){}
|
||||
splitmix64(u64 i);
|
||||
|
||||
u64 generate_seed() override;
|
||||
f64 generate_f64();
|
||||
|
||||
inline virtual ~splitmix64() override{}
|
||||
|
||||
static u64 oneshot(u64& state);
|
||||
inline static u64 oneshot(const u64& state) { u64 s = state; return oneshot(s); }
|
||||
static f64 oneshotf(u64& state);
|
||||
inline static f64 oneshotf(const u64& state) { u64 s = state; return oneshotf(s); }
|
||||
static splitmix64 random();
|
||||
private:
|
||||
u64 m_state;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <rngxx.hpp>
|
||||
|
||||
#include "internal/seed.h"
|
||||
|
||||
namespace rng
|
||||
{
|
||||
struct sm64 final : public Random
|
||||
{
|
||||
inline sm64() : m_generator(seed::splitmix64::random()){}
|
||||
inline sm64(const seed::splitmix64& g) : m_generator(g){}
|
||||
explicit inline sm64(seed_gen<u64>& generator) : m_generator(generator){}
|
||||
inline sm64(u64 seed) : Random(), m_generator(seed){}
|
||||
|
||||
RNG_OVERRIDE(inline u64, next_u64, ()) { return m_generator.generate_seed(); }
|
||||
protected:
|
||||
inline f64 _sample() override { return m_generator.generate_f64(); }
|
||||
private:
|
||||
seed::splitmix64 m_generator;
|
||||
};
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
// Contains the work-doing code for the C API interface, which should be exported to the C API shim in the header "capi-bridge.h"
|
||||
#include <cstdio>
|
||||
|
||||
#include <rngxx.hpp>
|
||||
#include <rngxx/crand.h>
|
||||
#include <rngxx/sm64.h>
|
||||
|
||||
#include <init.hpp>
|
||||
|
||||
#include "capi-bridge.h"
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static inline consteval bool is_same_any() {
|
||||
return (std::is_same_v<T, Args> || ...);
|
||||
}
|
||||
|
||||
namespace bridge _export(internal)
|
||||
{
|
||||
template<typename T>
|
||||
inline T next_typed(Random& engine, const void* minp = nullptr, const void* maxp = nullptr)
|
||||
{
|
||||
if constexpr(!is_same_any<T, bool>()) {
|
||||
if(minp && maxp) return engine.next(*reinterpret_cast<const T*>(minp), *reinterpret_cast<const T*>(maxp));
|
||||
else if(maxp) return engine.next(*reinterpret_cast<const T*>(maxp));
|
||||
else {
|
||||
if constexpr(!is_same_any<T, f32, f64>())
|
||||
if(minp)return engine.next(*reinterpret_cast<const T*>(minp), engine.max_for<T>());
|
||||
}
|
||||
}
|
||||
return engine.next<T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void rep_ptr(void* vptr, size_t n, auto lambda)
|
||||
{
|
||||
T* ptr = reinterpret_cast<T*>(vptr);
|
||||
if(!n) lambda(ptr);
|
||||
else while( n --> 0 ) lambda(ptr++);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void next_typed_into(Random& engine, void* restrict output, const void* minp = nullptr, const void* maxp = nullptr)
|
||||
{
|
||||
*reinterpret_cast<T* restrict>(output) = next_typed<T>(engine, minp, maxp);
|
||||
}
|
||||
template<typename T>
|
||||
inline void next_typed_into_n(Random& engine, void* restrict output, size_t rep = 0, const void* minp = nullptr, const void* maxp = nullptr)
|
||||
{
|
||||
if(!rep) return next_typed_into<T>(engine, output, minp, maxp);
|
||||
T* restrict ro = reinterpret_cast<T*>(output);
|
||||
while( rep --> 0 )
|
||||
next_typed_into<T>(engine, reinterpret_cast<void*>(ro++), minp, maxp);
|
||||
}
|
||||
|
||||
inline void nblob(Random& engine, unsigned char* restrict output, size_t es, size_t l)
|
||||
{
|
||||
size_t len = es * l; // full length (bytes)
|
||||
|
||||
engine.next_bytes(output, len);
|
||||
}
|
||||
inline void nblob(Random& engine, unsigned char* restrict output, size_t len) { nblob(engine, output, 1, len); }
|
||||
|
||||
constexpr inline size_t ty_alignof(rng_next_type ty)
|
||||
{
|
||||
switch(ty)
|
||||
{
|
||||
case RNG_TY_BOOL: return alignof(rng_bool_t);
|
||||
|
||||
case RNG_TY_INT8: return alignof(i8);
|
||||
case RNG_TY_INT16: return alignof(i16);
|
||||
case RNG_TY_INT32: return alignof(i32);
|
||||
case RNG_TY_INT64: return alignof(i64);
|
||||
case RNG_TY_F32: return alignof(f32);
|
||||
case RNG_TY_F64: return alignof(f64);
|
||||
|
||||
case RNG_TY_BLOB: return 0;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
constexpr inline size_t ty_sizeof(rng_next_type ty)
|
||||
{
|
||||
switch(ty)
|
||||
{
|
||||
case RNG_TY_BOOL: return sizeof(rng_bool_t);
|
||||
|
||||
case RNG_TY_INT8: return sizeof(i8);
|
||||
case RNG_TY_INT16: return sizeof(i16);
|
||||
case RNG_TY_INT32: return sizeof(i32);
|
||||
case RNG_TY_INT64: return sizeof(i64);
|
||||
case RNG_TY_F32: return sizeof(f32);
|
||||
case RNG_TY_F64: return sizeof(f64);
|
||||
|
||||
case RNG_TY_BLOB: return 0;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline bool ty_signed(rng_next_flag f) { return ! (f & RNG_TYQ_UNSIGNED); }
|
||||
constexpr inline bool ty_signed(const rng_next_tyq& qt) { return ty_signed(qt.mod); }
|
||||
|
||||
constexpr inline bool ty_hasflag(rng_next_flag hay, rng_next_flag nee) { return !!(hay & nee); }
|
||||
constexpr inline bool ty_hasflag(const rng_next_tyq& q, rng_next_flag nee) { return ty_hasflag(q.mod, nee); }
|
||||
|
||||
template<typename S, typename U>
|
||||
inline auto next_fun_of(const rng_next_tyq& ty)
|
||||
{
|
||||
if(ty_signed(ty)) return &next_typed_into_n<S>; //ty.hasflag(ty, RNG_TYQ_ARRAY) ? &next_typed_into_n<S> : &next_typed_into<S>;
|
||||
else return &next_typed_into_n<U>; //ty.hasflag(ty, RNG_TYQ_ARRAY) ? &next_typed_into_n<U> : &next_typed_into<U>;
|
||||
}
|
||||
|
||||
auto dc_lookup(const char* name)
|
||||
{
|
||||
namespace init = rng::init;
|
||||
try {
|
||||
auto& ctor = init::get(name);
|
||||
return &ctor;
|
||||
} catch(init::DCLookupFailed& f) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Lookup failed for `%s' (%s)\n", name, f.name.c_str());
|
||||
#else
|
||||
(void)f;
|
||||
#endif
|
||||
return reinterpret_cast<std::remove_reference_t<decltype(init::get(""))>*>( 0 ); //auto deduction fails here?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Internal bridge members
|
||||
_export(internal)
|
||||
rng_t* RNG_IMPL(mkdriver)(rng_kind kind, const u64* restrict seed)
|
||||
{
|
||||
switch(kind)
|
||||
{
|
||||
case RNG_KIND_CRAND:
|
||||
return new rng::crand(seed[0]);
|
||||
case RNG_KIND_SM64:
|
||||
return new rng::sm64(seed[0]);
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
_export(internal)
|
||||
int RNG_IMPL(mnext)(rng_t* engine, void* restrict output, const rng_next_opt* restrict opt)
|
||||
{
|
||||
if(opt->ty.mod & RNG_TYQ_CHAOS) {
|
||||
bridge::nblob(*engine, reinterpret_cast<unsigned char*>(output), bridge::ty_sizeof(opt->ty.type));
|
||||
return 1;
|
||||
}
|
||||
|
||||
const size_t array = bridge::ty_hasflag(opt->ty, RNG_TYQ_ARRAY)
|
||||
? static_cast<size_t>(opt->ex.array_size)
|
||||
: 0;
|
||||
switch(opt->ty.type)
|
||||
{
|
||||
case RNG_TY_BLOB: bridge::nblob(*engine, reinterpret_cast<unsigned char*>(output), opt->bound.len); break;
|
||||
|
||||
#define INTTY(bits) case RNG_TY_INT ## bits: \
|
||||
bridge::next_fun_of<i ## bits, u ## bits>(opt->ty)(*engine, output, array, opt->bound.range.pmin, opt->bound.range.pmax); break
|
||||
INTTY(8);
|
||||
INTTY(16);
|
||||
INTTY(32);
|
||||
INTTY(64);
|
||||
#undef INTTY
|
||||
case RNG_TY_F32:
|
||||
bridge::next_typed_into_n<f32>(*engine, output, array, opt->bound.range.pmin, opt->bound.range.pmax);
|
||||
break;
|
||||
case RNG_TY_F64:
|
||||
bridge::next_typed_into_n<f64>(*engine, output, array, opt->bound.range.pmin, opt->bound.range.pmax);
|
||||
break;
|
||||
case RNG_TY_BOOL:
|
||||
bridge::rep_ptr<rng_bool_t>(output, array, [&](rng_bool_t* restrict output) {
|
||||
*output = bridge::next_typed<bool>(*engine) ? 1 : 0;
|
||||
});
|
||||
break;
|
||||
default: return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
_export(internal)
|
||||
int RNG_IMPL(m_allocsz_for)(enum rng_next_type ty, size_t* restrict size, size_t* restrict align)
|
||||
{
|
||||
int ok = 0;
|
||||
if(size) ok |= ((*size = bridge::ty_sizeof(ty)) > 0) ? 1 : 0;
|
||||
if(align) ok |= ((*align = bridge::ty_alignof(ty)) > 0) ? 2 : 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Direct C interface members
|
||||
void rng_free(rng_t* rng)
|
||||
{
|
||||
delete rng;
|
||||
}
|
||||
|
||||
|
||||
_export(internal)
|
||||
rng_t* RNG_IMPL(dynctor_trycall)(const char* name, const void* seed)
|
||||
{
|
||||
auto cptr = bridge::dc_lookup(name);
|
||||
if(LIKELY(cptr)) return (*cptr)(seed);
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
_export(internal)
|
||||
rng_t* RNG_IMPL(dynctor_callref)(struct rng_dyn_ctor* ptr, const void* seed)
|
||||
{
|
||||
auto& ctor = *reinterpret_cast<decltype(bridge::dc_lookup(""))>(ptr);
|
||||
return ctor(seed);
|
||||
}
|
||||
|
||||
_export(internal)
|
||||
rng_dyn_ctor* RNG_IMPL(dynctor_refer)(const char* name)
|
||||
{
|
||||
auto ctor = bridge::dc_lookup(name);
|
||||
return reinterpret_cast<rng_dyn_ctor*>(ctor);
|
||||
}
|
||||
|
||||
rng_ctor_fn rng_ctor(const char* name)
|
||||
{
|
||||
auto cptr = bridge::dc_lookup(name);
|
||||
if(LIKELY(cptr)) {
|
||||
auto& ctor = *cptr;
|
||||
if(ctor) {
|
||||
rng_ctor_fn* fun = ctor.target<rng_ctor_fn>();
|
||||
if(fun) return *fun;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
// Bridges the working TU `capi-bridge.cpp`, which handles interfacing with rngxx's C++ API, and the C API shim TU defined in `capi.c`
|
||||
// This file must be useable by both C and C++ compilers, and everything here must have C linkage
|
||||
//
|
||||
// Other headers shouldn't be included here. The only ones included are included for the types they define
|
||||
#ifndef _RNG_CAPI_BRIDGE_H
|
||||
#define _RNG_CAPI_BRIDGE_H
|
||||
|
||||
#define _RNGXX_IMPL_ONLY_TYPES
|
||||
#include <rngxx.h>
|
||||
#undef _RNGXX_IMPL_ONLY_TYPES
|
||||
#include <common.h>
|
||||
|
||||
#define RNG_IMPL(name) _rng__internal_ ## name
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define _ro_p(n) __attribute__((access (read_only, n)))
|
||||
|
||||
rng_t* RNG_IMPL(mkdriver)(enum rng_kind kind, const u64* restrict seed) _export(internal);
|
||||
int RNG_IMPL(mnext)(rng_t* engine, void* restrict output, const struct rng_next_opt* restrict opt) _ro_p(3) _export(internal);
|
||||
|
||||
int RNG_IMPL(m_allocsz_for)(enum rng_next_type ty, size_t* restrict size, size_t* restrict align) _export(internal);
|
||||
|
||||
rng_t* RNG_IMPL(dynctor_trycall)(const char* name, const void* seed) _export(internal);
|
||||
struct rng_dyn_ctor* RNG_IMPL(dynctor_refer)(const char* name) _export(internal);
|
||||
rng_t* RNG_IMPL(dynctor_callref)(struct rng_dyn_ctor* ptr, const void* seed) _export(internal);
|
||||
|
||||
#undef _ro_p
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _RNG_CAPI_BRIDGE_H */
|
@ -1,122 +0,0 @@
|
||||
// Contains the library exported C API functions.
|
||||
// Actual work may be done in `capi-bridge.cpp`, `capi-bridge.h` bridges this shim TU with that working one
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <common.h>
|
||||
#include <rngxx.h>
|
||||
|
||||
#include "capi-bridge.h"
|
||||
|
||||
#define assert_not_null(expr, ...) ({ __auto_type _nn__expr = (expr); \
|
||||
if(UNLIKELY(_nn__expr == NULL)) { fprintf(stderr, "fatal (unexpected null pointer): " __VA_ARGS__); abort(); } \
|
||||
_nn__expr; })
|
||||
|
||||
rng_t* rng_new(enum rng_kind kind, const u64 seed[static restrict 1])
|
||||
{
|
||||
return assert_not_null(RNG_IMPL(mkdriver) (kind, &seed[0]), "invalid kind %d", (int)kind);
|
||||
}
|
||||
// void rng_free() - direct bridge
|
||||
|
||||
int rng_raw(rng_t* engine, void* restrict output, struct rng_next_opt opt)
|
||||
{
|
||||
return RNG_IMPL(mnext)(engine, output, &opt);
|
||||
}
|
||||
|
||||
void rng_next_bin(rng_t* engine, unsigned char* restrict output, size_t n)
|
||||
{
|
||||
RNG_IMPL(mnext)(engine, (void* restrict)output, &(struct rng_next_opt){
|
||||
.bound.len = n,
|
||||
.ty.type = RNG_TY_BLOB,
|
||||
});
|
||||
}
|
||||
|
||||
static void* m_alloc_for(enum rng_next_type ty)
|
||||
{
|
||||
size_t s, a;
|
||||
switch( RNG_IMPL(m_allocsz_for)(ty, &s, &a) )
|
||||
{
|
||||
case 2: // size 0, align nonzero
|
||||
case 0: break;
|
||||
case 1: return malloc(s);
|
||||
case 3: return aligned_alloc(a, s);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* m_alloc_array(enum rng_next_type ty, size_t ars)
|
||||
{
|
||||
size_t s, a;
|
||||
switch( RNG_IMPL(m_allocsz_for)(ty, &s, &a) )
|
||||
{
|
||||
case 2: // size 0, align nonzero
|
||||
case 0: break;
|
||||
case 1: return calloc(ars, s);
|
||||
case 3: return aligned_alloc(a, s * ars);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* rng_next_tyab(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, uint32_t sz, const void* pmin, const void* pmax)
|
||||
{
|
||||
if(!sz) return output;
|
||||
if(! (output = output ?: m_alloc_array(ty, (size_t)sz)) ) return NULL;
|
||||
|
||||
RNG_IMPL(mnext)(engine, output, &(struct rng_next_opt){
|
||||
.bound.range = { pmin, pmax },
|
||||
.ty = {
|
||||
.type = ty,
|
||||
.mod = flags | (sz > 1 ? RNG_TYQ_ARRAY : 0 ),
|
||||
},
|
||||
.ex = {
|
||||
.array_size = sz,
|
||||
},
|
||||
});
|
||||
return output;
|
||||
|
||||
}
|
||||
void* rng_next_tyb(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, const void* pmin, const void* pmax)
|
||||
{
|
||||
if(! ( output = output ?: m_alloc_for(ty)) ) return NULL;
|
||||
|
||||
RNG_IMPL(mnext)(engine, output, &(struct rng_next_opt){
|
||||
.bound.range = {
|
||||
.pmin = pmin,
|
||||
.pmax = pmax,
|
||||
},
|
||||
.ty = {
|
||||
.type = ty,
|
||||
.mod = flags,
|
||||
},
|
||||
.ex = {0},
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void* rng_next_ty(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags)
|
||||
{
|
||||
return rng_next_tyb(engine, output, ty, flags, NULL, NULL);
|
||||
}
|
||||
|
||||
void* rng_next_tya(rng_t* engine, void* restrict output, enum rng_next_type ty, enum rng_next_flag flags, uint32_t sz)
|
||||
{
|
||||
if(!sz) return output;
|
||||
return rng_next_tyab(engine, output, ty, flags, sz, NULL, NULL);
|
||||
}
|
||||
|
||||
rng_t* rng_new_named(const char* name, const void* seed)
|
||||
{
|
||||
return RNG_IMPL(dynctor_trycall)(name, seed);
|
||||
}
|
||||
|
||||
rng_dyn_ctor_ref rng_ctor_ref(const char* name)
|
||||
{
|
||||
return RNG_IMPL(dynctor_refer)(name);
|
||||
}
|
||||
|
||||
rng_t* rng_ctor_call(rng_dyn_ctor_ref ctor, const void* seed)
|
||||
{
|
||||
return RNG_IMPL(dynctor_callref)(ctor, seed);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
#include <init.hpp>
|
||||
|
||||
using ctor_map = std::unordered_map<std::string_view, rng::init::ctor_func>;
|
||||
|
||||
static ctor_map* INIT_MAP=nullptr;
|
||||
|
||||
namespace rng::init
|
||||
{
|
||||
const ctor_func& apply_raw(std::string_view name, ctor_func fn) noexcept
|
||||
{
|
||||
if(!INIT_MAP) INIT_MAP = new ctor_map();
|
||||
|
||||
auto& map = *INIT_MAP;
|
||||
map[name] = std::move(fn);
|
||||
return map[name];
|
||||
}
|
||||
|
||||
ctor_func& get(std::string_view name)
|
||||
{
|
||||
if(UNLIKELY(!INIT_MAP)) throw DCLookupFailed ( name );
|
||||
auto& map = *INIT_MAP;
|
||||
if(map.contains(name)) return map[name];
|
||||
else throw DCLookupFailed ( name );
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
__attribute__((destructor))
|
||||
static void _rng_ctor__fini() noexcept{
|
||||
if(LIKELY(INIT_MAP)) {
|
||||
delete INIT_MAP;
|
||||
INIT_MAP = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
../include/rngxx/internal
|
@ -0,0 +1 @@
|
||||
../../include/rngxx/common.h
|
@ -1,14 +0,0 @@
|
||||
#include <bit>
|
||||
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
|
||||
#include <rngxx.hpp>
|
||||
#include <mem.h>
|
||||
#include <init.hpp>
|
||||
|
||||
#include <rngxx/crand.h>
|
||||
#include <rngxx/sm64.h>
|
||||
#include "crand.h"
|
||||
|
||||
//TODO: Why does dynctors defined in this TU not function properly? Is it because there's nothing else in here?
|
@ -1,31 +0,0 @@
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
|
||||
#include <seed.h>
|
||||
|
||||
#include <common.h>
|
||||
|
||||
static u64 _sm64_next(u64& x)
|
||||
{
|
||||
u64 z = (x += 0x9e3779b97f4a7c15);
|
||||
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
|
||||
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
|
||||
return z ^ (z >> 31);
|
||||
}
|
||||
|
||||
namespace rng::seed
|
||||
{
|
||||
splitmix64::splitmix64(u64 u) : m_state(u){}
|
||||
|
||||
u64 splitmix64::generate_seed() { return _sm64_next(m_state); }
|
||||
f64 splitmix64::generate_f64() { return generate_seed() / pow(2.0, 64); }
|
||||
|
||||
u64 splitmix64::oneshot(u64& state) { return _sm64_next(state); }
|
||||
f64 splitmix64::oneshotf(u64& state) { return _sm64_next(state) / pow(2.0, 64); }
|
||||
|
||||
splitmix64 splitmix64::random()
|
||||
{
|
||||
splitmix64 r (oneshot(time(NULL)));
|
||||
return r;
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#define _RNGXX_INLINE_ONLY
|
||||
#include <rngxx.h>
|
||||
|
||||
|
||||
static int next(rng_t* rng, const int* min, const int* max)
|
||||
{
|
||||
int output;
|
||||
if(rng_next_tyb(rng, &output, RNG_TY_INT32, RNG_TYQ_SIGNED, min, max)) return output;
|
||||
|
||||
fprintf(stderr, "next<int>() failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#define TREF(x) ( (const __typeof(x)[]){ (x) } )
|
||||
|
||||
const struct { const char* const name; const enum rng_kind static_kind; } DEFAULT_ALG = { "crand", RNG_KIND_CRAND };
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
(void)argc;
|
||||
const char* alg_name = argv[1] ?: DEFAULT_ALG.name;
|
||||
const enum rng_kind* alg_dev = ((int)DEFAULT_ALG.static_kind) == -1 ? NULL : &DEFAULT_ALG.static_kind;
|
||||
|
||||
printf("constructing for %s...\n", alg_name);
|
||||
rng_dyn_ctor_ref rcref = rng_ctor_ref(alg_name);
|
||||
rng_ctor_fn rctor = rng_ctor(alg_name);
|
||||
|
||||
rng_t* engine = NULL;
|
||||
if(rctor) {
|
||||
printf("constructing direct\n");
|
||||
engine = rctor(TREF((u64)time(NULL)));
|
||||
} else {
|
||||
printf("constructing through ref\n");
|
||||
engine = rng_ctor_call(rcref, TREF((u64)time(NULL)));
|
||||
}
|
||||
if(!engine) {
|
||||
printf("fallback to `rng_new_named()`\n");
|
||||
engine = rng_new_named(alg_name, TREF((u64)time(NULL))); //rng_new(rng_kind_crand, (const u64[]){ time(null) });
|
||||
}
|
||||
if(!engine && alg_dev) {
|
||||
printf("fallback to static init of default arg (%d)\n", *alg_dev);
|
||||
engine = rng_new(*alg_dev, (const u64[]){ time(NULL) });
|
||||
}
|
||||
if(!engine) {
|
||||
fprintf(stderr, "failed to create engine\n");
|
||||
return 1;
|
||||
}
|
||||
printf("%d %d %d\n", next(engine, NULL, TREF(100)), next(engine, TREF(10), TREF(20)), next(engine, NULL, NULL));
|
||||
printf("%ld\n", rng_next_i64(engine, NULL, NULL));
|
||||
printf("%lf\n", rng_next_f64(engine, TREF(1.0), TREF(100.0)));
|
||||
printf("%d\n", rng_next_bool(engine));
|
||||
|
||||
|
||||
rng_bool_t* array = rng_nextam_bool(engine, 100);
|
||||
if(!array) {
|
||||
fprintf(stderr, "failed to allocate for array\n");
|
||||
return 1;
|
||||
}
|
||||
for(int i=0;i<100;i++)
|
||||
printf("%d ", array[i]);
|
||||
printf("\n"); free(array);
|
||||
|
||||
int ints[100];
|
||||
if(!rng_nexta_i32(engine, ints, 100, NULL, TREF(100))) {
|
||||
fprintf(stderr, "failed to compute int array\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(size_t i=0;i<sizeof(ints)/sizeof(ints[0]);i++)
|
||||
printf("%d ", ints[i]);
|
||||
printf("\n");
|
||||
|
||||
rng_free(engine);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#include <fmt/format.h>
|
||||
#include <rngxx.hpp>
|
||||
#include <rngxx/crand.h>
|
||||
|
||||
void iter(Random& rng)
|
||||
{
|
||||
for(auto&& i : rng.iter<int>())
|
||||
fmt::print("{} ", i);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void cpp_test()
|
||||
{
|
||||
auto random = new rng::crand(100);
|
||||
iter(random);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue