/* EQEmu: EQEmulator Copyright (C) 2001-2026 EQEmu Development Team 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 . */ #pragma once #include #include #include #include #include /* * This is an additive lagged fibonacci generator as seen in The Art of Computer Programming, Vol. 2 * This should roughly match the implementation that EQ's client uses and be compatible with our Random class * * EQ's rand looks like it was from an example implementation that as posted on pscode.com * * You might also want to consider defining BIASED_INT_DIST as well to more closely match EQ */ namespace EQ { template class additive_lagged_fibonacci_engine { static_assert(std::is_unsigned::value, "result_type must be an unsigned integral type"); static_assert(0u < j && j < k, "0 < j < k"); static_assert(0u < w && w <= std::numeric_limits::digits, "template argument substituting w out of bounds"); public: using result_type = UIntType; static constexpr size_t word_size = w; static constexpr size_t short_lag = j; static constexpr size_t long_lag = k; static constexpr result_type default_seed = 19780503u; // default for subtract_with_carry_engine additive_lagged_fibonacci_engine() : additive_lagged_fibonacci_engine(default_seed) {} explicit additive_lagged_fibonacci_engine(result_type sd) { seed(sd); } void seed(result_type seed = default_seed) { state1 = long_lag - long_lag; state2 = long_lag - short_lag; state[0] = static_cast(seed) & ((1u << word_size) - 1); state[1] = 1; for (int i = 2; i < long_lag; ++i) state[i] = (state[i - 1] + state[i - 2]) & ((1u << word_size) - 1); return; } // TODO: seed via seed_seq static constexpr result_type min() { return 0; } static constexpr result_type max() { return ((1u << word_size) - 1) >> 6; } void discard(unsigned long long z) { for (; z != 0ULL; --z) (*this)(); } result_type operator()() { result_type rand = (state[state1] + state[state2]) & ((1u << word_size) - 1); state[state1] = rand; if (++state1 == long_lag) state1 = 0; if (++state2 == long_lag) state2 = 0; return rand >> 6; } private: result_type state1; result_type state2; result_type state[long_lag]; public: template friend bool operator==(const additive_lagged_fibonacci_engine &x, const additive_lagged_fibonacci_engine &y) { return std::equal(x.state, x.state + long_lag, y.state) && x.state1 == y.state1 && x.state2 == y.state2; } template friend bool operator!=(const additive_lagged_fibonacci_engine &x, const additive_lagged_fibonacci_engine &y) { return !(x == y); } template friend std::basic_ostream & operator<<(std::basic_istream &os, additive_lagged_fibonacci_engine &x) { using ios_base = typename std::basic_istream::ios_base; const typename ios_base::fmtflags flags = os.flags(); const CharT fill = os.fill(); const CharT space = os.widen(' '); os.flags(ios_base::dec | ios_base::fixed | ios_base::left); os.fill(space); for (size_t i = 0; i < long_lag; ++i) os << x.state[i] << space; os << x.state1 << space << x.state2; os.flags(flags); os.fill(fill); return os; } template friend std::basic_istream & operator>>(std::basic_istream &is, additive_lagged_fibonacci_engine &x) { using ios_base = typename std::basic_istream::ios_base; const typename ios_base::fmtflags flags = is.flags(); is.flags(ios_base::dec | ios_base::skipws); for (size_t i = 0; i < long_lag; ++i) is >> x.state[i]; is >> x.state1; is >> x.state2; is.flags(flags); return is; } }; using EQRand = additive_lagged_fibonacci_engine; };