commit
ff8c729ef7
@ -0,0 +1,4 @@
|
|||||||
|
obj/
|
||||||
|
vgcore.*
|
||||||
|
*-release
|
||||||
|
*-debug
|
@ -0,0 +1,88 @@
|
|||||||
|
# Generic C and C++ Makefile project template
|
||||||
|
# Contains targets for `release', `debug', and `clean'.
|
||||||
|
|
||||||
|
PROJECT=rng
|
||||||
|
AUTHOR=Avril (Flanchan) <flanchan@cumallover.me>
|
||||||
|
|
||||||
|
SRC_C = $(wildcard src/*.c)
|
||||||
|
SRC_CXX = $(wildcard src/*.cpp)
|
||||||
|
|
||||||
|
INCLUDE=include
|
||||||
|
|
||||||
|
COMMON_FLAGS= -W -Wall -pedantic -fno-strict-aliasing $(addprefix -I,$(INCLUDE))
|
||||||
|
|
||||||
|
OPT_FLAGS?= -march=native -fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \
|
||||||
|
-floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block \
|
||||||
|
-fno-stack-check
|
||||||
|
|
||||||
|
CXX_OPT_FLAGS?= $(OPT_FLAGS) -felide-constructors
|
||||||
|
|
||||||
|
CFLAGS += $(COMMON_FLAGS) --std=gnu17
|
||||||
|
CXXFLAGS += $(COMMON_FLAGS) --std=gnu++20
|
||||||
|
LDFLAGS +=
|
||||||
|
|
||||||
|
STRIP=strip
|
||||||
|
|
||||||
|
RELEASE_COMMON_FLAGS+= -Werror
|
||||||
|
DEBUG_COMMON_FLAGS+= -fanalyzer
|
||||||
|
|
||||||
|
ifneq ($(TARGET_SPEC_FLAGS),no)
|
||||||
|
RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS)
|
||||||
|
RELEASE_CXXFLAGS?= -O3 -flto $(CXX_OPT_FLAGS)
|
||||||
|
RELEASE_LDFLAGS?= -Wl,-O3 -Wl,-flto
|
||||||
|
|
||||||
|
DEBUG_CFLAGS?= -Og -g
|
||||||
|
DEBUG_CXXFLAGS?=-O0 -g
|
||||||
|
DEBUG_LDFLAGS?=
|
||||||
|
endif
|
||||||
|
|
||||||
|
DEBUG_CFLAGS+=-DDEBUG $(DEBUG_COMMON_FLAGS)
|
||||||
|
DEBUG_CXXFLAGS+=-DDEBUG $(DEBUG_COMMON_FLAGS)
|
||||||
|
|
||||||
|
RELEASE_CFLAGS+=-DRELEASE $(RELEASE_COMMON_FLAGS)
|
||||||
|
RELEASE_CXXFLAGS+=-DRELEASE $(RELEASE_COMMON_FLAGS)
|
||||||
|
|
||||||
|
# Objects
|
||||||
|
|
||||||
|
OBJ_C = $(addprefix obj/c/,$(SRC_C:.c=.o))
|
||||||
|
OBJ_CXX = $(addprefix obj/cxx/,$(SRC_CXX:.cpp=.o))
|
||||||
|
OBJ = $(OBJ_C) $(OBJ_CXX)
|
||||||
|
|
||||||
|
# Phonies
|
||||||
|
|
||||||
|
.PHONY: release
|
||||||
|
release: | dirs $(PROJECT)-release
|
||||||
|
|
||||||
|
.PHONY: debug
|
||||||
|
debug: | dirs $(PROJECT)-debug
|
||||||
|
|
||||||
|
# Targets
|
||||||
|
|
||||||
|
dirs:
|
||||||
|
@mkdir -p obj/c{,xx}/src
|
||||||
|
|
||||||
|
obj/c/%.o: %.c
|
||||||
|
$(CC) -c $< $(CFLAGS) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
obj/cxx/%.o: %.cpp
|
||||||
|
$(CXX) -c $< $(CXXFLAGS) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
$(PROJECT)-release: CFLAGS+= $(RELEASE_CFLAGS)
|
||||||
|
$(PROJECT)-release: CXXFLAGS += $(RELEASE_CXXFLAGS)
|
||||||
|
$(PROJECT)-release: LDFLAGS += $(RELEASE_LDFLAGS)
|
||||||
|
$(PROJECT)-release: $(OBJ)
|
||||||
|
$(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS)
|
||||||
|
$(STRIP) $@
|
||||||
|
|
||||||
|
$(PROJECT)-debug: CFLAGS+= $(DEBUG_CFLAGS)
|
||||||
|
$(PROJECT)-debug: CXXFLAGS += $(DEBUG_CXXFLAGS)
|
||||||
|
$(PROJECT)-debug: LDFLAGS += $(DEBUG_LDFLAGS)
|
||||||
|
$(PROJECT)-debug: $(OBJ)
|
||||||
|
$(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
clean-rebuild:
|
||||||
|
rm -rf obj
|
||||||
|
|
||||||
|
clean: clean-rebuild
|
||||||
|
rm -f $(PROJECT)-{release,debug,pgo}
|
||||||
|
|
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef _INTS_H
|
||||||
|
#define _INTS_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
DEFINT(8);
|
||||||
|
DEFINT(16);
|
||||||
|
DEFINT(32);
|
||||||
|
DEFINT(64);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#else
|
||||||
|
#include <stdbool.h>
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
|
typedef signed __int128 i128;
|
||||||
|
typedef unsigned __int128 u128;
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
typedef float f32;
|
||||||
|
typedef double f64;
|
||||||
|
|
||||||
|
typedef size_t usize;
|
||||||
|
typedef ssize_t isize;
|
||||||
|
|
||||||
|
typedef bool u1;
|
||||||
|
typedef bool i1;
|
||||||
|
|
||||||
|
typedef uintptr_t ptr_t;
|
||||||
|
|
||||||
|
#undef DEFINT
|
||||||
|
#undef DEF
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define restrict __restrict__
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _INTS_H */
|
@ -0,0 +1,186 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define CTOR_COPY(name) name(const name& copy)
|
||||||
|
#define CTOR_MOVE(name) name(name&& move)
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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 :/)
|
||||||
|
|
||||||
|
constexpr inline Random(){}
|
||||||
|
constexpr inline virtual ~Random(){}
|
||||||
|
|
||||||
|
inline f64 next_f64() { return sample(); }
|
||||||
|
inline f32 next_f32() { return (f32)sample(); }
|
||||||
|
|
||||||
|
virtual bool next_bool();
|
||||||
|
|
||||||
|
#define NDEF(t, M) inline t next_ ## t() { return next_ ## t(M); }
|
||||||
|
#define NDEFF(n) NDEF(i ## n, INT ## n ## _MAX) NDEF(u ## n, UINT ## n ## _MAX)
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
_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(); //TODO: An iterator that yields `next<T>()` forever.
|
||||||
|
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;
|
||||||
|
|
||||||
|
inline f64 sample()
|
||||||
|
{
|
||||||
|
auto s = _sample();
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (s < 0 || s > 1) throw InvalidRandomSample{ s };
|
||||||
|
#endif
|
||||||
|
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
|
@ -0,0 +1,19 @@
|
|||||||
|
#include <rng.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
void r(Random& r)
|
||||||
|
{
|
||||||
|
u8 bytes[32];
|
||||||
|
r.next_bytes(bytes);
|
||||||
|
u32 n = r.next<u32>();
|
||||||
|
f64 f = r.next<f64>();
|
||||||
|
f = r.next_f64();
|
||||||
|
std::array<char, 10> nn = r.next<std::array<char, 10>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
#include <rng.h>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static inline constexpr T _scale(f64 sample, T max)
|
||||||
|
{
|
||||||
|
return (T)(sample * (f64)max);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Random::next_bool() { return sample() < 0.5; }
|
||||||
|
#define NEXT(T) T Random::next_ ## T(T max) { return _scale<T >(sample(), max); }
|
||||||
|
#define NEXTT(n) NEXT(i ## n) NEXT(u ## n)
|
||||||
|
|
||||||
|
#define N_INTS \
|
||||||
|
NEXTT(8) \
|
||||||
|
NEXTT(16) \
|
||||||
|
NEXTT(32) \
|
||||||
|
NEXTT(64)
|
||||||
|
N_INTS
|
||||||
|
|
||||||
|
#undef NEXTT
|
||||||
|
#undef NEXT
|
||||||
|
|
||||||
|
// Inefficient sample() based impl of `bytes()`
|
||||||
|
void Random::next_bytes(u8* ptr, usize n)
|
||||||
|
{
|
||||||
|
while( n --> 0 )
|
||||||
|
*ptr++ = next_u8();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-vertorised impl of _v methods for base
|
||||||
|
void Random::next_v64(u64* p, usize n)
|
||||||
|
{
|
||||||
|
return next_bytes(reinterpret_cast<u8*>(p), n * sizeof(u64));
|
||||||
|
}
|
||||||
|
void Random::next_v32(u32* p, usize n)
|
||||||
|
{
|
||||||
|
return next_bytes(reinterpret_cast<u8*>(p), n * sizeof(u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array getters
|
||||||
|
#define NEXT(T) void Random::next_ ## T(T* a, usize n) { while( n --> 0 ) *a++ = next_ ## T(); }
|
||||||
|
#define NEXTT(n) NEXT(i ## n) NEXT(u ## n)
|
||||||
|
|
||||||
|
N_INTS
|
||||||
|
|
||||||
|
#undef NEXTT
|
||||||
|
#undef NEXT
|
Loading…
Reference in new issue