/* *****************************************************************************
* kblib is a general utility library for C++14 and C++17, intended to provide
* performant high-level abstractions and more expressive ways to do simple
* things.
*
* Copyright (c) 2021 killerbee
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* ****************************************************************************/
/**
* @file
* @brief Provides utilities to correctly and expressively use C++11's random
* number generation library, without requiring a PhD.
*
* @author killerbee
* @date 2019-2021
* @copyright GNU General Public Licence v3.0
*/
#ifndef RANDOM_H
#define RANDOM_H
#include "algorithm.h"
#include "iterators.h"
#include "memory.h"
#include "simple.h"
#include "stats.h"
#include "tdecl.h"
#include
#include
#include
#if KBLIB_DEBUG_SEED_SEQ
# include
#endif
namespace KBLIB_NS {
/**
* @brief Given a categorical distribution cats, selects one category
*
* @deprecated std::discrete_distribution provides the same functionality, with
* a worse name. Because it exists, there is no reason to use this function.
*
* @param cats A sequence of category weights
* @param r A -compatible RandomGenerator
* @todo Refactor to remove the ugly unreachable stuff.
*/
template
KBLIB_NODISCARD
[[deprecated("Use std::discrete_distribution instead")]] constexpr auto
chooseCategorical(Array&& cats, RandomGenerator& r)
-> decltype(cats.size()) {
std::uniform_real_distribution uniform(
0.0, kblib::accumulate(cats.begin(), cats.end(), 0.0));
freqtype choose = uniform(r);
for (decltype(cats.size()) stop = 0; stop != cats.size(); ++stop) {
choose -= cats[stop];
if (choose <= 0) {
return stop;
}
}
#ifdef __has_builtin
# if __has_builtin(__builtin_unreachable)
__builtin_unreachable();
# else
return cats.size() - 1;
# endif
#else
return cats.size() - 1;
#endif
}
class KBLIB_NODISCARD trivial_seed_seq {
public:
using result_type = std::uint32_t;
trivial_seed_seq() = default;
template
trivial_seed_seq(InputIt begin, InputIt end)
: data(begin, end) {}
template
trivial_seed_seq(std::initializer_list il)
: trivial_seed_seq(il.begin(), il.end()) {}
template
trivial_seed_seq(Source gen, std::size_t count)
: data(count) {
kblib::generate_n(data.begin(), count, std::ref(gen));
}
// Work around std::linear_congruential_engine without overpulling from the
// random source
template
trivial_seed_seq(Source gen, std::size_t count, std::size_t discard)
: data(count + discard, 0x8b8b8b8bu) {
kblib::generate_n(data.begin() + kblib::to_signed(discard), count,
std::ref(gen));
}
template
auto generate(RandomAccessIt begin, RandomAccessIt end) const -> void {
auto o_size = end - begin;
auto d_size = to_signed(data.size());
#if KBLIB_DEBUG_SEED_SEQ
if (o_size < d_size) {
std::clog << "trivial_seed_seq: overseeded for output size of "
<< o_size << ", have data size " << d_size << '\n';
}
#endif
if (data.empty()) {
std::fill(begin, end, 0x8b8b8b8bu); // copied from std::seed_seq
} else {
#if KBLIB_DEBUG_SEED_SEQ
if (o_size > d_size) {
std::clog << "trivial_seed_seq: unexpectedly wrapping, output size "
<< o_size << " greater than data size " << d_size << '\n';
}
#endif
auto dpos = begin;
do {
auto blk_size
= saturating_cast(std::min(d_size, o_size));
dpos = std::copy_n(data.begin(), blk_size, dpos);
} while ((o_size -= d_size) > 0);
}
return;
}
KBLIB_NODISCARD auto size() const noexcept -> std::size_t {
return data.size();
}
template
auto param(OutputIt dest) const -> void {
std::copy(data.begin(), data.end(), dest);
return;
}
private:
std::vector data;
};
template
struct state_size;
template
struct state_size>
: std::integral_constant {};
template
struct state_size>
: std::integral_constant {
// std::linear_congruential_engine discards 3 seed words due to hardcoded
// hack for std::seed_seq, so we work around that with this trait
KBLIB_CONSTANT_M std::size_t seed_discard = 3;
};
template
struct state_size>
: std::integral_constant {};
template
struct state_size>
: state_size {};
template
struct state_size>
: state_size {};
template
struct state_size> : state_size {};
template
constexpr std::size_t state_size_v = state_size::value;
static_assert(state_size_v == std::mt19937::state_size, "");
static_assert(state_size_v == std::mt19937_64::state_size * 2,
"");
template
constexpr std::size_t seed_discard_v = 0;
template
constexpr std::size_t seed_discard_v<
T, void_t::seed_discard)>> = state_size::
seed_discard;
static_assert(seed_discard_v == 3,
"linear_congruential_engines discard 3 words of seed data");
template
KBLIB_NODISCARD auto seeded(Source&& s) -> Gen {
auto seed
= trivial_seed_seq(std::ref(s), state_size_v, seed_discard_v);
return Gen{seed};
}
template
KBLIB_NODISCARD auto seeded() -> Gen {
return seeded(std::random_device{});
}
template
class KBLIB_NODISCARD transform_engine : URBG {
private:
using E = URBG;
static_assert(std::is_default_constructible::value, "");
KBLIB_NODISCARD constexpr auto engine() -> E& {
return static_cast(*this);
}
KBLIB_NODISCARD constexpr auto engine() const -> const E& {
return static_cast(*this);
}
public:
using result_type = typename Transform::result_type;
constexpr transform_engine() = default;
constexpr transform_engine(const transform_engine&) noexcept(
std::is_nothrow_copy_constructible::value)
= default;
constexpr transform_engine(transform_engine&&) noexcept(
std::is_nothrow_move_constructible::value)
= default;
constexpr transform_engine(result_type s)
: E(s) {}
template ::value>>
constexpr transform_engine(SSeq& s)
: E(s) {}
KBLIB_NODISCARD auto operator=(const transform_engine&)
-> transform_engine& = delete;
KBLIB_NODISCARD auto operator=(transform_engine&&)
-> transform_engine& = delete;
~transform_engine() = default;
KBLIB_NODISCARD constexpr auto operator()() noexcept -> result_type {
return Transform{}(engine()());
}
// using E::seed;
using E::discard;
KBLIB_NODISCARD static constexpr auto min() noexcept -> result_type {
return Transform::min(URBG::min(), URBG::max());
}
KBLIB_NODISCARD static constexpr auto max() noexcept -> result_type {
return Transform::max(URBG::min(), URBG::max());
}
KBLIB_NODISCARD friend constexpr auto operator==(
const transform_engine& lhs, const transform_engine& rhs) noexcept
-> bool {
return lhs.engine() == rhs.engine();
}
KBLIB_NODISCARD friend constexpr auto operator!=(
const transform_engine& lhs, const transform_engine& rhs) noexcept
-> bool {
return ! (lhs == rhs);
}
friend constexpr auto operator<<(std::ostream& os, const transform_engine& e)
-> std::ostream& {
return os << e.engine();
}
friend constexpr auto operator>>(std::istream& is, transform_engine& e)
-> std::istream& {
return is >> e.engine();
}
};
template
struct state_size> : state_size {};
template
struct shift_mask {
using result_type = UIntType;
template
KBLIB_NODISCARD static constexpr auto g(UIntInput in) noexcept -> UIntType {
return static_cast(in >> shift) & mask;
}
KBLIB_NODISCARD constexpr auto operator()(UIntType in) const noexcept
-> UIntType {
return g(in);
}
KBLIB_NODISCARD static constexpr auto min(UIntType min,
KBLIB_UNUSED UIntType max) noexcept
-> UIntType {
return g(min);
}
KBLIB_NODISCARD static constexpr auto max(KBLIB_UNUSED UIntType min,
UIntType max) noexcept
-> UIntType {
return g(max);
}
};
template
KBLIB_NODISCARD constexpr auto ipow2(UIntType b) noexcept -> UIntType {
if (b == std::numeric_limits::digits) {
return 0u;
} else {
return UIntType{1} << b;
}
}
inline namespace lcgs {
// shortcut alias for common case of m = 2^b
template
using lcg_p2 = std::linear_congruential_engine;
inline namespace common_lcgs {
using rand48
= transform_engine,
shift_mask>;
using java_rand = rand48;
using glibc_rand0
= transform_engine,
shift_mask>;
using ansic_rand
= transform_engine,
shift_mask>;
using knuth_lcg = std::linear_congruential_engine<
uint64_t, 6364136223846793005U, 1442695040888963407U,
0U>; /* Knuth's preferred 64-bit LCG */
} // namespace common_lcgs
inline namespace best_lcgs {
// mcg = multiplicative congruential generator
// note that for an mcg to work effectively, the seed must be coprime to m
// in this case, that means seeds must be odd
// lcgs do not have this restriction.
using lcg32 = lcg_p2;
using mcg32 = lcg_p2;
using lcg48 = lcg_p2;
using mcg48 = lcg_p2;
using lcg64 = lcg_p2;
using mcg64 = lcg_p2;
} // namespace best_lcgs
} // namespace lcgs
} // namespace KBLIB_NS
#endif // RANDOM_H