21#include "absl/log/check.h"
22#include "absl/numeric/int128.h"
23#include "absl/status/status.h"
24#include "absl/status/statusor.h"
25#include "absl/strings/str_format.h"
26#include "absl/time/clock.h"
27#include "absl/time/time.h"
35template <
typename Int>
36Int InternalChoose(Int n, Int k) {
44 for (Int i = 2;
i <= k; ++
i) {
61std::vector<Int> LastNThatDoesNotOverflowForAllK(
62 bool overflows_intermediate_computation) {
63 absl::Time start_time = absl::Now();
77 const int bound_digits = std::numeric_limits<Int>::digits +
78 (overflows_intermediate_computation ? 0 : 1);
79 std::vector<Int> result = {
80 std::numeric_limits<Int>::max(),
81 std::numeric_limits<Int>::max(),
83 ? Int{1} << (bound_digits / 2)
85 0.5 + std::pow(2.0, 0.5 * std::numeric_limits<Int>::digits)),
89 for (Int k = 3;; ++k) {
90 const double max_log_comb =
91 overflows_intermediate_computation
92 ? std::numeric_limits<Int>::digits * std::log(2) - std::log(k)
93 : std::numeric_limits<Int>::digits * std::log(2);
100 2 + 6 * std::pow(2.0, std::numeric_limits<Int>::digits / 3 + 1))),
101 [k, max_log_comb](Int n) {
104 if (result.back() < 2 * k) {
110 if constexpr (std::numeric_limits<Int>::digits == 63) {
111 DCHECK_EQ(result.size(),
112 overflows_intermediate_computation
116 VLOG(1) <<
"LastNThatDoesNotOverflowForAllK(): " << absl::Now() - start_time;
120template <
typename Int>
121bool NChooseKIntermediateComputationOverflowsInt(Int n, Int k) {
123 static const auto*
const result =
124 new std::vector<Int>(LastNThatDoesNotOverflowForAllK<Int>(
126 return k < result->size() ? n > (*result)[k] :
true;
129template <
typename Int>
130bool NChooseKResultOverflowsInt(Int n, Int k) {
132 static const auto*
const result =
133 new std::vector<Int>(LastNThatDoesNotOverflowForAllK<Int>(
135 return k < result->size() ? n > (*result)[k] :
true;
142absl::StatusOr<int64_t>
NChooseK(int64_t n, int64_t k) {
144 return absl::InvalidArgumentError(absl::StrFormat(
"n is negative (%d)", n));
147 return absl::InvalidArgumentError(absl::StrFormat(
"k is negative (%d)", k));
153 if (k == 0)
return 1;
154 if (n < std::numeric_limits<uint32_t>::max() &&
155 !NChooseKIntermediateComputationOverflowsInt<uint32_t>(n, k)) {
156 return static_cast<int64_t
>(InternalChoose<uint32_t>(n, k));
158 if (!NChooseKIntermediateComputationOverflowsInt<int64_t>(n, k)) {
159 return InternalChoose<uint64_t>(n, k);
161 if (NChooseKResultOverflowsInt<int64_t>(n, k)) {
162 return absl::InvalidArgumentError(
163 absl::StrFormat(
"(%d choose %d) overflows int64", n, k));
165 return static_cast<int64_t
>(InternalChoose<absl::uint128>(n, k));
static double LogCombinations(int n, int k)
In SWIG mode, we don't want anything besides these top-level includes.
absl::StatusOr< int64_t > NChooseK(int64_t n, int64_t k)
Point BinarySearch(Point x_true, Point x_false, std::function< bool(Point)> f)