m1une's library

This documentation is automatically generated by online-judge-tools/verification-helper

View on GitHub

:warning: Enumeration Algorithms All
(algo/enumeration/all.hpp)

Overview

algo/enumeration/all.hpp includes helpers for traversing combinatorial state spaces. The public namespace is m1une::algo.

Included Headers

Header Contents
algo/enumeration/gray_code.hpp Binary-reflected Gray-code encoding, decoding, and enumeration.
algo/enumeration/segtree_range.hpp Decomposition into maximal aligned segment-tree ranges.
algo/enumeration/submask.hpp Submask and supermask enumeration helpers.
algo/enumeration/combination.hpp Fixed-size subset mask generation helpers.
algo/enumeration/permutation_lexicographical_order.hpp Rank and select permutations in lexicographical order.

Depends on

Required by

Code

#ifndef M1UNE_ALGO_ENUMERATION_ALL_HPP
#define M1UNE_ALGO_ENUMERATION_ALL_HPP 1

#include "combination.hpp"
#include "gray_code.hpp"
#include "permutation_lexicographical_order.hpp"
#include "segtree_range.hpp"
#include "submask.hpp"

#endif  // M1UNE_ALGO_ENUMERATION_ALL_HPP
#line 1 "algo/enumeration/all.hpp"



#line 1 "algo/enumeration/combination.hpp"



#include <cassert>
#include <concepts>
#include <cstdint>
#include <limits>
#include <type_traits>

namespace m1une {
namespace algo {

namespace internal {

template <std::unsigned_integral UInt>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
UInt combination_low_bits(int bit_count) {
    constexpr int digits = std::numeric_limits<UInt>::digits;
    assert(0 <= bit_count && bit_count <= digits);
    if (bit_count == digits) return ~UInt(0);
    return (UInt(1) << bit_count) - UInt(1);
}

}  // namespace internal

template <std::unsigned_integral UInt = std::uint64_t>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
UInt first_combination_mask(int bit_count, int choose) {
    constexpr int digits = std::numeric_limits<UInt>::digits;
    assert(0 <= choose && choose <= bit_count && bit_count <= digits);
    if (choose == 0) return UInt(0);
    if (choose == bit_count) return internal::combination_low_bits<UInt>(bit_count);
    return (UInt(1) << choose) - UInt(1);
}

template <std::unsigned_integral UInt>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
bool next_combination_mask(UInt& mask, int bit_count) {
    const UInt universe = internal::combination_low_bits<UInt>(bit_count);
    assert((mask & ~universe) == 0);
    if (mask == 0) return false;

    const UInt lowest = mask & (~mask + UInt(1));
    const UInt ripple = mask + lowest;
    if (ripple == 0 || (ripple & ~universe) != 0) return false;

    const UInt next = (((ripple ^ mask) >> 2) / lowest) | ripple;
    if ((next & ~universe) != 0) return false;
    mask = next;
    return true;
}

template <std::unsigned_integral UInt = std::uint64_t, class F>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
void for_each_combination_mask(int bit_count, int choose, F f) {
    constexpr int digits = std::numeric_limits<UInt>::digits;
    assert(0 <= choose && choose <= bit_count && bit_count <= digits);
    UInt mask = first_combination_mask<UInt>(bit_count, choose);
    while (true) {
        f(mask);
        if (!next_combination_mask(mask, bit_count)) break;
    }
}

}  // namespace algo
}  // namespace m1une


#line 1 "algo/enumeration/gray_code.hpp"



#line 6 "algo/enumeration/gray_code.hpp"
#include <cstddef>
#line 10 "algo/enumeration/gray_code.hpp"
#include <vector>

namespace m1une {
namespace algo {

// Converts a binary value to its binary-reflected Gray code.
template <std::unsigned_integral UInt>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
constexpr UInt gray_encode(UInt value) noexcept {
    return value ^ (value >> 1);
}

// Converts a binary-reflected Gray code to the corresponding binary value.
template <std::unsigned_integral UInt>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
constexpr UInt gray_decode(UInt code) noexcept {
    for (int shift = 1; shift < std::numeric_limits<UInt>::digits;
         shift <<= 1) {
        code ^= code >> shift;
    }
    return code;
}

// Returns all bit_count-bit binary-reflected Gray codes in traversal order.
template <std::unsigned_integral UInt = std::uint64_t>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
std::vector<UInt> gray_code_sequence(int bit_count) {
    constexpr int uint_digits = std::numeric_limits<UInt>::digits;
    constexpr int size_digits = std::numeric_limits<std::size_t>::digits;
    assert(0 <= bit_count);
    assert(bit_count <= uint_digits);
    assert(bit_count < size_digits);
    if (bit_count < 0 || uint_digits < bit_count || size_digits <= bit_count) {
        return {};
    }

    const std::size_t size = std::size_t(1) << bit_count;
    std::vector<UInt> result(size);
    for (std::size_t index = 0; index < size; ++index) {
        result[index] = gray_encode(static_cast<UInt>(index));
    }
    return result;
}

}  // namespace algo
}  // namespace m1une


#line 1 "algo/enumeration/permutation_lexicographical_order.hpp"



#line 8 "algo/enumeration/permutation_lexicographical_order.hpp"
#include <optional>
#line 10 "algo/enumeration/permutation_lexicographical_order.hpp"
#include <utility>
#line 12 "algo/enumeration/permutation_lexicographical_order.hpp"

namespace m1une {
namespace algo {

namespace internal {

struct PermutationOrderFenwick {
    std::vector<int> data;

    explicit PermutationOrderFenwick(int size) : data(size + 1) {}

    void add(int index, int value) {
        for (index++; index < int(data.size()); index += index & -index) {
            data[index] += value;
        }
    }

    int prefix_sum(int right) const {
        int result = 0;
        for (; 0 < right; right -= right & -right) result += data[right];
        return result;
    }

    int kth(int order) const {
        int index = 0;
        int accumulated = 0;
        int step = 1;
        while (step < int(data.size())) step <<= 1;
        for (; 0 < step; step >>= 1) {
            const int next = index + step;
            if (next < int(data.size()) &&
                accumulated + data[next] <= order) {
                index = next;
                accumulated += data[next];
            }
        }
        return index;
    }
};

}  // namespace internal

// Returns the zero-based lexicographical rank of a permutation of [0, n).
// Returns nullopt when the sequence is invalid or the rank does not fit in UInt.
template <
    std::unsigned_integral UInt = std::uint64_t,
    class Permutation
>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
std::optional<UInt> checked_permutation_lexicographical_rank(
    const Permutation& permutation
) {
    const int size = int(permutation.size());
    internal::PermutationOrderFenwick fenwick(size);
    for (int value = 0; value < size; value++) fenwick.add(value, 1);

    UInt rank = 0;
    constexpr UInt limit = std::numeric_limits<UInt>::max();
    for (int index = 0; index < size; index++) {
        const auto& value_reference = permutation[index];
        using Value = std::remove_cvref_t<decltype(value_reference)>;
        static_assert(std::integral<Value>);
        static_assert(!std::same_as<Value, bool>);

        if (std::cmp_less(value_reference, 0) ||
            std::cmp_greater_equal(value_reference, size)) {
            return std::nullopt;
        }
        const int value = int(value_reference);
        if (fenwick.prefix_sum(value + 1) == fenwick.prefix_sum(value)) {
            return std::nullopt;
        }

        const std::uintmax_t smaller = fenwick.prefix_sum(value);
        const std::uintmax_t remaining = size - index;
        if (smaller > std::uintmax_t(limit) ||
            std::uintmax_t(rank) >
                (std::uintmax_t(limit) - smaller) / remaining) {
            return std::nullopt;
        }
        rank = UInt(std::uintmax_t(rank) * remaining + smaller);
        fenwick.add(value, -1);
    }
    return rank;
}

// Every value must occur exactly once in [0, n), and the rank must fit in UInt.
template <
    std::unsigned_integral UInt = std::uint64_t,
    class Permutation
>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
UInt permutation_lexicographical_rank(const Permutation& permutation) {
    const std::optional<UInt> result =
        checked_permutation_lexicographical_rank<UInt>(permutation);
    assert(result.has_value());
    return result.value_or(UInt(0));
}

// Returns the permutation of [0, size) with the given zero-based rank.
// Returns nullopt when size is negative or rank is at least size factorial.
template <std::unsigned_integral UInt = std::uint64_t>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
std::optional<std::vector<int>> checked_kth_lexicographical_permutation(
    int size,
    UInt rank
) {
    if (size < 0) return std::nullopt;

    std::vector<int> lehmer_code(size);
    UInt remaining_rank = rank;
    for (int base = 1; base <= size && remaining_rank != 0; base++) {
        lehmer_code[size - base] = int(remaining_rank % UInt(base));
        remaining_rank /= UInt(base);
    }
    if (remaining_rank != 0) return std::nullopt;

    internal::PermutationOrderFenwick fenwick(size);
    for (int value = 0; value < size; value++) fenwick.add(value, 1);

    std::vector<int> permutation(size);
    for (int index = 0; index < size; index++) {
        const int value = fenwick.kth(lehmer_code[index]);
        permutation[index] = value;
        fenwick.add(value, -1);
    }
    return permutation;
}

// Rank must be less than size factorial.
template <std::unsigned_integral UInt = std::uint64_t>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
std::vector<int> kth_lexicographical_permutation(int size, UInt rank) {
    std::optional<std::vector<int>> result =
        checked_kth_lexicographical_permutation(size, rank);
    assert(result.has_value());
    return result.value_or(std::vector<int>());
}

}  // namespace algo
}  // namespace m1une


#line 1 "algo/enumeration/segtree_range.hpp"



#include <bit>
#line 10 "algo/enumeration/segtree_range.hpp"

namespace m1une {
namespace algo {

// Splits [left, right) into maximal segment-tree ranges from left to right.
template <std::integral Int>
requires(!std::same_as<std::remove_cv_t<Int>, bool>)
std::vector<std::pair<Int, Int>> split_segtree_range(Int left, Int right) {
    if constexpr (std::signed_integral<Int>) assert(Int(0) <= left);
    assert(left <= right);
    if constexpr (std::signed_integral<Int>) {
        if (left < 0) return {};
    }
    if (right < left) return {};

    using UInt = std::make_unsigned_t<Int>;
    UInt position = static_cast<UInt>(left);
    const UInt end = static_cast<UInt>(right);
    std::vector<std::pair<Int, Int>> result;
    if (position == end) return result;
    result.reserve(2 * std::bit_width(end - position));

    while (position < end) {
        UInt length = std::bit_floor(end - position);
        if (position != 0) {
            const UInt alignment = position & (~position + UInt(1));
            if (alignment < length) length = alignment;
        }
        const UInt next = position + length;
        result.emplace_back(
            static_cast<Int>(position), static_cast<Int>(next)
        );
        position = next;
    }
    return result;
}

}  // namespace algo
}  // namespace m1une


#line 1 "algo/enumeration/submask.hpp"



#line 8 "algo/enumeration/submask.hpp"

namespace m1une {
namespace algo {

namespace internal {

template <std::unsigned_integral UInt>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
UInt submask_low_bits(int bit_count) {
    constexpr int digits = std::numeric_limits<UInt>::digits;
    assert(0 <= bit_count && bit_count <= digits);
    if (bit_count == digits) return ~UInt(0);
    return (UInt(1) << bit_count) - UInt(1);
}

}  // namespace internal

template <std::unsigned_integral UInt, class F>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
void for_each_submask(UInt mask, F f) {
    UInt submask = mask;
    while (true) {
        f(submask);
        if (submask == 0) break;
        submask = (submask - 1) & mask;
    }
}

template <std::unsigned_integral UInt, class F>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
void for_each_nonzero_submask(UInt mask, F f) {
    for (UInt submask = mask; submask != 0; submask = (submask - 1) & mask) {
        f(submask);
    }
}

template <std::unsigned_integral UInt, class F>
requires(!std::same_as<std::remove_cv_t<UInt>, bool>)
void for_each_supermask(UInt mask, int bit_count, F f) {
    const UInt universe = internal::submask_low_bits<UInt>(bit_count);
    assert((mask & ~universe) == 0);
    const UInt free_bits = universe ^ mask;
    for_each_submask(free_bits, [&](UInt added_bits) {
        f(mask | added_bits);
    });
}

}  // namespace algo
}  // namespace m1une


#line 9 "algo/enumeration/all.hpp"
Back to top page