From ff8c729ef7be7e87e28e61ec17d40cffa1cc8778 Mon Sep 17 00:00:00 2001 From: Avril Date: Wed, 29 Sep 2021 21:10:22 +0100 Subject: [PATCH] Initial commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added `Random` interface. Fortune for cpprng's current commit: Blessing − 吉 --- .gitignore | 4 + Makefile | 88 ++++++++++++++++++++++ include/common.h | 50 +++++++++++++ include/rng.h | 186 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 19 +++++ src/rng.cpp | 48 ++++++++++++ 6 files changed, 395 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 include/common.h create mode 100644 include/rng.h create mode 100644 src/main.cpp create mode 100644 src/rng.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46cdddc --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +obj/ +vgcore.* +*-release +*-debug diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3ed44ee --- /dev/null +++ b/Makefile @@ -0,0 +1,88 @@ +# Generic C and C++ Makefile project template +# Contains targets for `release', `debug', and `clean'. + +PROJECT=rng +AUTHOR=Avril (Flanchan) + +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} + diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..a78773e --- /dev/null +++ b/include/common.h @@ -0,0 +1,50 @@ +#ifndef _INTS_H +#define _INTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + + +#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 +#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 */ diff --git a/include/rng.h b/include/rng.h new file mode 100644 index 0000000..1736a42 --- /dev/null +++ b/include/rng.h @@ -0,0 +1,186 @@ +#pragma once + +#include +#include +#include + +#include + +#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 + 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 + 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 + inline void next_bytes(u8 (&a)[N]) { + _next_bytes(a); + } + template + inline void next_bytes(std::array& ar) + { + _next_bytes(&ar[0]); + } + + template + inline T next() { + std::array arr; + + next_bytes(arr); + + return std::bit_cast(arr); + } + template + inline iterator iter(); //TODO: An iterator that yields `next()` 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 + inline void _next_bytes(u8* a) + { + using namespace _rng__util; + constexpr const auto rem64 = divp::type(N); + u8* ptr = a; + if constexpr(rem64.d) { + next_v64(reinterpret_cast(ptr), rem64.d); ptr+= rem64.d * sizeof(u64); + } + constexpr const auto rem32 = divp::type(rem64.r); + if constexpr(rem32.d) { + next_v32(reinterpret_cast(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 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..6026bc0 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,19 @@ +#include + +#include + +void r(Random& r) +{ + u8 bytes[32]; + r.next_bytes(bytes); + u32 n = r.next(); + f64 f = r.next(); + f = r.next_f64(); + std::array nn = r.next>(); +} + +int main() +{ + + return 0; +} diff --git a/src/rng.cpp b/src/rng.cpp new file mode 100644 index 0000000..7e523ee --- /dev/null +++ b/src/rng.cpp @@ -0,0 +1,48 @@ + +#include + +template +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(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(p), n * sizeof(u64)); +} +void Random::next_v32(u32* p, usize n) +{ + return next_bytes(reinterpret_cast(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