Compare commits

..

No commits in common. 'master' and 'lib' have entirely different histories.
master ... lib

2
.gitignore vendored

@ -2,7 +2,5 @@ obj/
vgcore.*
*-release
*-debug
*-test
*.so*
*.a
rngrx

@ -1,24 +1,23 @@
# rngxx - Basic, easy to use C++ p-random number generation
# pipec - Piping IPC
PROJECT=rngxx
AUTHOR=Avril (Flanchan) <flanchan@cumallover.me>
VERSION_MAJOR=1
VERSION_MINOR=1
VERSION_MAJOR=0
VERSION_MINOR=0
VERSION=$(VERSION_MAJOR).$(VERSION_MINOR)
ifeq ($(PREFIX),)
PREFIX := /usr/local
endif
SRC_C = $(wildcard src/*.c) $(wildcard src/rng/*.c)
SRC_CXX = $(wildcard src/*.cpp) $(wildcard src/rng/*.cpp)
SRC_C = $(wildcard src/*.c)
SRC_CXX = $(wildcard src/*.cpp)
INCLUDE=include
INCLUDE_INTERNAL=src/internal
COMMON_FLAGS= -W -Wall -Wextra -Wstrict-aliasing -fno-strict-aliasing "-D_VERSION=$(VERSION)" $(addprefix -I,$(INCLUDE)) $(addprefix -I,$(INCLUDE_INTERNAL))
COMMON_FLAGS+=-D_RNGXX_IMPL
TARGET_CPU?=native
OPT_FLAGS?= $(addprefix -march=,$(TARGET_CPU)) -fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \
@ -28,15 +27,14 @@ OPT_FLAGS?= $(addprefix -march=,$(TARGET_CPU)) -fgraphite -fopenmp -floop-parall
CXX_OPT_FLAGS?= $(OPT_FLAGS)
# Static analyzer currently generates false positives for C++, enable it only for C
CFLAGS += $(COMMON_FLAGS) --std=gnu17
#-fanalyzer
CFLAGS += $(COMMON_FLAGS) --std=gnu17 -fanalyzer
CXXFLAGS += $(COMMON_FLAGS) --std=gnu++20 -felide-constructors
LDFLAGS +=
STRIP=strip
RELEASE_COMMON_FLAGS+= -Werror
DEBUG_COMMON_FLAGS+=
DEBUG_COMMON_FLAGS+= -fanalyzer
ifneq ($(TARGET_SPEC_FLAGS),no)
RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS)
@ -155,17 +153,12 @@ install:
install -m 644 $(wildcard $(INCLUDE)/*.*) $(DESTDIR)$(PREFIX)/include/
install -d $(DESTDIR)$(PREFIX)/include/$(PROJECT)/
install -m 644 $(wildcard $(INCLUDE)/$(PROJECT)/*.*) $(DESTDIR)$(PREFIX)/include/$(PROJECT)/
install -d $(DESTDIR)$(PREFIX)/include/$(PROJECT)/internal
install -m 644 $(wildcard $(INCLUDE)/$(PROJECT)/internal/*.*) $(DESTDIR)$(PREFIX)/include/$(PROJECT)/internal
uninstall:
-rm $(DESTDIR)$(PREFIX)/lib/lib$(PROJECT).{a,so{,.*}}
cd $(INCLUDE) && find . -type f | xargs -I {} rm "$(DESTDIR)$(PREFIX)/include/{}"
-rmdir $(DESTDIR)$(PREFIX)/include/$(PROJECT)/internal
-rmdir $(DESTDIR)$(PREFIX)/include/$(PROJECT)
$(PROJECT)-test: LDFLAGS+= -lfmt -lstdc++
$(PROJECT)-test: CFLAGS+= -Og -g
$(PROJECT)-test: lib$(PROJECT)-debug.a
$(CC) $(CFLAGS) src/test/*.c -o $@ -l:$< $(LDFLAGS)
$(PROJECT)-test: lib$(PROJECT).so
$(CXX) $(CXXFLAGS) src/test/*.cpp -o $@ -l:$< -lfmt -Wl,-flto -Wl,-O3 $(LDFLAGS)
-valgrind ./$@

@ -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

@ -7,10 +7,6 @@ extern "C" {
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
#ifndef _RNGXX_COMMON_MINIMAL
#include <stdlib.h>
#define IFUNC_NAME(name, ver) _impl__ ## name ## __ ## ver
@ -36,8 +32,6 @@ extern "C" {
#define _rng_internal v_internal
#endif
#define DEF(s, n) typedef s ## int ## n ## _t s ## n
#define DEFINT(n) typedef uint ## n ## _t u ## n; \
typedef int ## n ## _t i ## n
@ -47,7 +41,6 @@ DEFINT(16);
DEFINT(32);
DEFINT(64);
#ifdef __cplusplus
#else
#include <stdbool.h>
@ -72,9 +65,7 @@ typedef uintptr_t ptr_t;
#undef DEF
#ifdef __cplusplus
#ifndef _RNGXX_COMMON_MINIMAL
#define restrict __restrict__
#endif
}
#endif

@ -1,10 +1,9 @@
#pragma once
#include "../common.h"
#include <rngxx.hpp>
#include "internal/mem.h"
#include "internal/common.h"
#include <rng.h>
#include "../mem.h"
namespace rng
{
@ -18,14 +17,14 @@ namespace rng
crand(u64 seed);
inline ~crand() override{}
RNG_OVERRIDE(inline i64, next_i64, ()) { return _sample_int(); }
RNG_OVERRIDE(u64, next_u64,());
RNG_OVERRIDE(i32, next_i32,());
RNG_OVERRIDE(u32, next_u32,());
inline i64 next_i64() override { return _sample_int(); }
u64 next_u64() override;
i32 next_i32() override;
u32 next_u32() override;
RNG_OVERRIDE(void, next_bytes, (u8* p, usize n));
void next_bytes(u8* p, usize n) override;
protected:
inline constexpr i64 _max_i64() const override { return RANGE_MAX; }
inline constexpr u64 _max_u64() const override { return (u64)RANGE_MAX; }
@ -41,5 +40,4 @@ namespace rng
explicit crand(_opaque* raw);
i64 _sample_int();
};
}

@ -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,5 +1,5 @@
#include <rngxx.hpp>
#include <rngxx.h>
#include <range.h>
constexpr const static util::range<f64> SAMPLE_RANGE { 0.0, 1.0 };
@ -30,15 +30,6 @@ N_INTS
#undef NEXTT
#undef NEXT
f64 Random::sample()
{
auto s = _sample();
if (UNLIKELY(s < 0 || s > 1)) throw InvalidRandomSample{ s };
return s;
}
// Inefficient sample() based impl of `bytes()`
void Random::next_bytes(u8* ptr, usize n)
{
@ -75,6 +66,3 @@ N_INTS
#undef NEXTT
#undef NEXT
// ---
//TODO: Why does this have to be in a specific TU to work?
#include "../internal/dctor.h"

@ -33,7 +33,7 @@ _Static_assert( sizeof(uint48_t) == (sizeof(uint16_t) * 3), "bad uint48 (ushort[
_Static_assert( sizeof(((struct jr_state*)NULL)->st) == sizeof(uint64_t), "bad uint64 (union st)");
inline static unsigned short* IFUNC_DEF(_jr_st_resolv, (jr_xsub_t* restrict state)
__attribute__((const, nonnull, returns_nonnull, access(none, 1))));
__attribute__((const, nonnull, returns_nonnull)));
inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, low) (jr_xsub_t* restrict state)
{
@ -43,18 +43,6 @@ inline static unsigned short* IFUNC_IMPL(_jr_st_resolv, high) (jr_xsub_t* restri
{
return state->xsubi+1;
}
inline static __attribute__((artificial, always_inline, gnu_inline, const)) int _resv_is_high()
{
// This being `static const` initialised makes this function be seen as a proper constant expression. Nothing is leaked on to the stack of the caller and the function is replaced with a single `lea`.
static const struct jr_state chk = {
.st.xsubl = JR_MAX,
};
return chk.st._xsub
? 1
: 0;
}
__attribute__((const))
inline static unsigned short* IFUNC_RESOLVER(_jr_st_resolv) (jr_xsub_t* restrict state)
{
@ -63,31 +51,13 @@ inline static unsigned short* IFUNC_RESOLVER(_jr_st_resolv) (jr_xsub_t* restrict
// The ifunc `_jr_st_resolv()` is essentially (almost) the same as a symbol-aliased constexpr function.
// Ain't that neat?
#ifdef _RNGXX_JR_RESOLV_IFUNC_OLD_STACKDYN_CHECK
// The old, dynamic stack alloc of the struct, instead of putting it in global r/o data
struct jr_state chk = {0};
chk.st.xsubl = JR_MAX;
#else
// The new, static const alloc of the struct, puts the value in at compile-time. Better optimisation opportunities.
// NOTE: Both stratergies make the ifunc resolve its target at compile-time, there is no difference in this ifunc resolver between the two. However, in `_resv_is_high()`, the old (this) stratergy prevents it being recognised as a constant-expression and stack-allocates the useless memory in the function's (macro-expanded) caller. Causing a spill of useless instructions that the new method changes to one `lea'.
static const struct jr_state chk = {
.st.xsubl = JR_MAX,
};
#endif
chk.st.xsubh = JR_MAX;
return chk.st._xsub
? & IFUNC_NAME(_jr_st_resolv, high)
: & IFUNC_NAME(_jr_st_resolv, low);
}
#ifndef _RNGXX_JR_RESOLV_RUNTIME
#define _jr_st_resolv_f (_resv_is_high() \
? & IFUNC_NAME(_jr_st_resolv, high) \
: & IFUNC_NAME(_jr_st_resolv, low))
//unsigned short* (*const _jr_st_resolv_fp)(jr_xsub_t* restrict state) = _jr_st_resolv_f; // "not a constant expr ://
#define _jr_st_resolv(st) _jr_st_resolv_f (st)
#else
#define _jr_st_resolv_f _jr_st_resolv
#endif
_fspec(readonly) internal long _jr_lastl(const struct jr_state* restrict state)
{

@ -5,10 +5,10 @@
#include <climits>
#include <cmath>
#include <rngxx.hpp>
#include <rng.h>
#include <mem.h>
#include <rngxx/crand.h>
#include <rng/crand.h>
#include "crand.h"
// Note: the output of drand48 is uniform between INT32_MIN..=INT32_MAX (inclusive)

@ -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…
Cancel
Save