| // Copyright 2017 The Abseil Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #ifndef ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_ | |
| #define ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_ | |
| #include <algorithm> | |
| #include <cmath> | |
| #include <ios> | |
| #include <istream> | |
| #include <limits> | |
| #include <ostream> | |
| #include "absl/base/config.h" | |
| #include "absl/base/macros.h" | |
| #include "absl/random/internal/iostream_state_saver.h" | |
| #include "absl/random/internal/traits.h" | |
| #include "absl/random/uniform_int_distribution.h" | |
| namespace absl { | |
| ABSL_NAMESPACE_BEGIN | |
| // log_uniform_int_distribution: | |
| // | |
| // Returns a random variate R in range [min, max] such that | |
| // floor(log(R-min, base)) is uniformly distributed. | |
| // We ensure uniformity by discretization using the | |
| // boundary sets [0, 1, base, base * base, ... min(base*n, max)] | |
| // | |
| template <typename IntType = int> | |
| class log_uniform_int_distribution { | |
| private: | |
| using unsigned_type = | |
| typename random_internal::make_unsigned_bits<IntType>::type; | |
| public: | |
| using result_type = IntType; | |
| class param_type { | |
| public: | |
| using distribution_type = log_uniform_int_distribution; | |
| explicit param_type( | |
| result_type min = 0, | |
| result_type max = (std::numeric_limits<result_type>::max)(), | |
| result_type base = 2) | |
| : min_(min), | |
| max_(max), | |
| base_(base), | |
| range_(static_cast<unsigned_type>(max_) - | |
| static_cast<unsigned_type>(min_)), | |
| log_range_(0) { | |
| ABSL_HARDENING_ASSERT(max_ >= min_); | |
| ABSL_HARDENING_ASSERT(base_ > 1); | |
| if (base_ == 2) { | |
| // Determine where the first set bit is on range(), giving a log2(range) | |
| // value which can be used to construct bounds. | |
| log_range_ = (std::min)(random_internal::BitWidth(range()), | |
| std::numeric_limits<unsigned_type>::digits); | |
| } else if (base_ > 2) { | |
| // An out-of-contract base_ (<= 1) skips this branch entirely so that | |
| // no floating-point undefined behavior is reached. | |
| // | |
| // NOTE: Computing the logN(x) introduces error from 2 sources: | |
| // 1. Conversion of int to double loses precision for values >= | |
| // 2^53, which may cause some log() computations to operate on | |
| // different values. | |
| // 2. The error introduced by the division will cause the result | |
| // to differ from the expected value. | |
| // | |
| // Thus a result which should equal K may equal K +/- epsilon, | |
| // which can eliminate some values depending on where the bounds fall. | |
| const double inv_log_base = 1.0 / std::log(static_cast<double>(base_)); | |
| const double log_range = std::log(static_cast<double>(range()) + 0.5); | |
| log_range_ = static_cast<int>(std::ceil(inv_log_base * log_range)); | |
| } | |
| } | |
| result_type(min)() const { return min_; } | |
| result_type(max)() const { return max_; } | |
| result_type base() const { return base_; } | |
| friend bool operator==(const param_type& a, const param_type& b) { | |
| return a.min_ == b.min_ && a.max_ == b.max_ && a.base_ == b.base_; | |
| } | |
| friend bool operator!=(const param_type& a, const param_type& b) { | |
| return !(a == b); | |
| } | |
| private: | |
| friend class log_uniform_int_distribution; | |
| int log_range() const { return log_range_; } | |
| unsigned_type range() const { return range_; } | |
| result_type min_; | |
| result_type max_; | |
| result_type base_; | |
| unsigned_type range_; // max - min | |
| int log_range_; // ceil(logN(range_)) | |
| static_assert(random_internal::IsIntegral<IntType>::value, | |
| "Class-template absl::log_uniform_int_distribution<> must be " | |
| "parameterized using an integral type."); | |
| }; | |
| log_uniform_int_distribution() : log_uniform_int_distribution(0) {} | |
| explicit log_uniform_int_distribution( | |
| result_type min, | |
| result_type max = (std::numeric_limits<result_type>::max)(), | |
| result_type base = 2) | |
| : param_(min, max, base) {} | |
| explicit log_uniform_int_distribution(const param_type& p) : param_(p) {} | |
| void reset() {} | |
| // generating functions | |
| template <typename URBG> | |
| result_type operator()(URBG& g) { // NOLINT(runtime/references) | |
| return (*this)(g, param_); | |
| } | |
| template <typename URBG> | |
| result_type operator()(URBG& g, // NOLINT(runtime/references) | |
| const param_type& p) { | |
| return static_cast<result_type>((p.min)() + Generate(g, p)); | |
| } | |
| result_type(min)() const { return (param_.min)(); } | |
| result_type(max)() const { return (param_.max)(); } | |
| result_type base() const { return param_.base(); } | |
| param_type param() const { return param_; } | |
| void param(const param_type& p) { param_ = p; } | |
| friend bool operator==(const log_uniform_int_distribution& a, | |
| const log_uniform_int_distribution& b) { | |
| return a.param_ == b.param_; | |
| } | |
| friend bool operator!=(const log_uniform_int_distribution& a, | |
| const log_uniform_int_distribution& b) { | |
| return a.param_ != b.param_; | |
| } | |
| private: | |
| // Returns a log-uniform variate in the range [0, p.range()]. The caller | |
| // should add min() to shift the result to the correct range. | |
| template <typename URNG> | |
| unsigned_type Generate(URNG& g, // NOLINT(runtime/references) | |
| const param_type& p); | |
| param_type param_; | |
| }; | |
| template <typename IntType> | |
| template <typename URBG> | |
| typename log_uniform_int_distribution<IntType>::unsigned_type | |
| log_uniform_int_distribution<IntType>::Generate( | |
| URBG& g, // NOLINT(runtime/references) | |
| const param_type& p) { | |
| // sample e over [0, log_range]. Map the results of e to this: | |
| // 0 => 0 | |
| // 1 => [1, b-1] | |
| // 2 => [b, (b^2)-1] | |
| // n => [b^(n-1)..(b^n)-1] | |
| const int e = absl::uniform_int_distribution<int>(0, p.log_range())(g); | |
| if (e == 0) { | |
| return 0; | |
| } | |
| const int d = e - 1; | |
| unsigned_type base_e, top_e; | |
| if (p.base() == 2) { | |
| base_e = static_cast<unsigned_type>(1) << d; | |
| top_e = (e >= std::numeric_limits<unsigned_type>::digits) | |
| ? (std::numeric_limits<unsigned_type>::max)() | |
| : (static_cast<unsigned_type>(1) << e) - 1; | |
| } else { | |
| const double r = std::pow(static_cast<double>(p.base()), d); | |
| const double s = (r * static_cast<double>(p.base())) - 1.0; | |
| base_e = | |
| (r > static_cast<double>((std::numeric_limits<unsigned_type>::max)())) | |
| ? (std::numeric_limits<unsigned_type>::max)() | |
| : static_cast<unsigned_type>(r); | |
| top_e = | |
| (s > static_cast<double>((std::numeric_limits<unsigned_type>::max)())) | |
| ? (std::numeric_limits<unsigned_type>::max)() | |
| : static_cast<unsigned_type>(s); | |
| } | |
| const unsigned_type lo = (base_e >= p.range()) ? p.range() : base_e; | |
| const unsigned_type hi = (top_e >= p.range()) ? p.range() : top_e; | |
| // choose uniformly over [lo, hi] | |
| return absl::uniform_int_distribution<result_type>( | |
| static_cast<result_type>(lo), static_cast<result_type>(hi))(g); | |
| } | |
| template <typename CharT, typename Traits, typename IntType> | |
| std::basic_ostream<CharT, Traits>& operator<<( | |
| std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) | |
| const log_uniform_int_distribution<IntType>& x) { | |
| using stream_type = | |
| typename random_internal::stream_format_type<IntType>::type; | |
| auto saver = random_internal::make_ostream_state_saver(os); | |
| os << static_cast<stream_type>((x.min)()) << os.fill() | |
| << static_cast<stream_type>((x.max)()) << os.fill() | |
| << static_cast<stream_type>(x.base()); | |
| return os; | |
| } | |
| template <typename CharT, typename Traits, typename IntType> | |
| std::basic_istream<CharT, Traits>& operator>>( | |
| std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) | |
| log_uniform_int_distribution<IntType>& x) { // NOLINT(runtime/references) | |
| using param_type = typename log_uniform_int_distribution<IntType>::param_type; | |
| using result_type = | |
| typename log_uniform_int_distribution<IntType>::result_type; | |
| using stream_type = | |
| typename random_internal::stream_format_type<IntType>::type; | |
| stream_type min; | |
| stream_type max; | |
| stream_type base; | |
| auto saver = random_internal::make_istream_state_saver(is); | |
| is >> min >> max >> base; | |
| if (!is.fail()) { | |
| const result_type min_val = static_cast<result_type>(min); | |
| const result_type max_val = static_cast<result_type>(max); | |
| const result_type base_val = static_cast<result_type>(base); | |
| if (max_val < min_val || base_val <= 1) { | |
| // The input violates the param_type preconditions; signal failure by | |
| // setting the failbit instead of constructing an invalid param_type. | |
| is.setstate(is.rdstate() | std::ios_base::failbit); | |
| } else { | |
| x.param(param_type(min_val, max_val, base_val)); | |
| } | |
| } | |
| return is; | |
| } | |
| ABSL_NAMESPACE_END | |
| } // namespace absl | |
| #endif // ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_ |