Program Listing for File bpqueue.hpp

Return to documentation for file (ckpttncpp/bpqueue.hpp)

#pragma once

#include "dllist.hpp" // import dllink
#include <cassert>
#include <gsl/span>
// #include <type_traits>
#include <vector>

// Forward declaration for begin() end()
template <typename _Tp, typename Int>
class bpq_iterator;

template <typename _Tp, typename Int = int32_t,
    typename _Sequence = std::vector<dllink<std::pair<_Tp, std::make_unsigned_t<Int>>>>>
//          class Allocator = typename std::allocator<dllink<std::pair<_Tp,
//          Int>> > >
class bpqueue
{
    using UInt = std::make_unsigned_t<Int>;

    friend bpq_iterator<_Tp, Int>;
    using Item = dllink<std::pair<_Tp, UInt>>;

    static_assert(std::is_same_v<Item, typename _Sequence::value_type>,
        "value_type must be the same as the underlying container");

  public:
    using value_type = typename _Sequence::value_type;
    using reference = typename _Sequence::reference;
    using const_reference = typename _Sequence::const_reference;
    using size_type = typename _Sequence::size_type;
    using container_type = _Sequence;

  private:
    Item sentinel {};

    _Sequence bucket;
    UInt max {};
    Int offset;
    UInt high;

    // using alloc_t = decltype(bucket.get_allocator());

  public:
    constexpr bpqueue(Int a, Int b)
        : bucket(static_cast<UInt>(b - a) + 2U)
        , offset(a - 1)
        , high(static_cast<UInt>(b - offset))
    {
        assert(a <= b);
        static_assert(
            std::is_integral<Int>::value, "bucket's key must be an integer");
        bucket[0].append(this->sentinel); // sentinel
    }

    bpqueue(const bpqueue&) = delete; // don't copy
    ~bpqueue() = default;
    constexpr auto operator=(const bpqueue&)
        -> bpqueue& = delete; // don't assign
    constexpr bpqueue(bpqueue&&) noexcept = default;
    constexpr auto operator=(bpqueue&&) noexcept
        -> bpqueue& = default; // don't assign

    [[nodiscard]] constexpr auto is_empty() const noexcept -> bool
    {
        return this->max == 0U;
    }

    constexpr auto set_key(Item& it, Int gain) noexcept -> void
    {
        it.data.second = static_cast<UInt>(gain - this->offset);
    }

    [[nodiscard]] constexpr auto get_max() const noexcept -> Int
    {
        return this->offset + Int(this->max);
    }

    constexpr auto clear() noexcept -> void
    {
        while (this->max > 0)
        {
            this->bucket[this->max].clear();
            this->max -= 1;
        }
    }

    constexpr auto append_direct(Item& it) noexcept -> void
    {
        assert(static_cast<Int>(it.data.second) > this->offset);
        this->append(it, Int(it.data.second));
    }

    constexpr auto append(Item& it, Int k) noexcept -> void
    {
        assert(k > this->offset);
        it.data.second = UInt(k - this->offset);
        if (this->max < it.data.second)
        {
            this->max = it.data.second;
        }
        this->bucket[it.data.second].append(it);
    }

    // constexpr auto appendfrom(gsl::span<Item> nodes) noexcept -> void
    // {
    //     for (auto& it : nodes)
    //     {
    //         it.data.second -= this->offset;
    //         assert(it.data.second > 0);
    //         this->bucket[it.data.second].append(it);
    //     }
    //     this->max = this->high;
    //     while (this->bucket[this->max].is_empty())
    //     {
    //         this->max -= 1;
    //     }
    // }

    constexpr auto popleft() noexcept -> Item&
    {
        auto& res = this->bucket[this->max].popleft();
        while (this->bucket[this->max].is_empty())
        {
            this->max -= 1;
        }
        return res;
    }

    constexpr auto decrease_key(Item& it, UInt delta) noexcept -> void
    {
        // this->bucket[it.data.second].detach(it)
        it.detach();
        it.data.second -= delta;
        assert(it.data.second > 0);
        assert(it.data.second <= this->high);
        this->bucket[it.data.second].append(it); // FIFO
        if (this->max < it.data.second)
        {
            this->max = it.data.second;
            return;
        }
        while (this->bucket[this->max].is_empty())
        {
            this->max -= 1;
        }
    }

    constexpr auto increase_key(Item& it, UInt delta) noexcept -> void
    {
        // this->bucket[it.data.second].detach(it)
        it.detach();
        it.data.second += delta;
        assert(it.data.second > 0);
        assert(it.data.second <= this->high);
        this->bucket[it.data.second].appendleft(it); // LIFO
        if (this->max < it.data.second)
        {
            this->max = it.data.second;
        }
    }

    constexpr auto modify_key(Item& it, Int delta) noexcept -> void
    {
        if (it.is_locked())
        {
            return;
        }
        if (delta > 0)
        {
            this->increase_key(it, UInt(delta));
        }
        else if (delta < 0)
        {
            this->decrease_key(it, UInt(-delta));
        }
    }

    constexpr auto detach(Item& it) noexcept -> void
    {
        // this->bucket[it.data.second].detach(it)
        it.detach();
        while (this->bucket[this->max].is_empty())
        {
            this->max -= 1;
        }
    }

    constexpr auto begin() -> bpq_iterator<_Tp, Int>;

    constexpr auto end() -> bpq_iterator<_Tp, Int>;

    // constexpr auto& items()
    // {
    //     return *this;
    // }

    // constexpr const auto& items() const
    // {
    //     return *this;
    // }

    // using coro_t = boost::coroutines2::coroutine<dllink<T>&>;
    // using pull_t = typename coro_t::pull_type;

    // /**
    //  * @brief item generator
    //  *
    //  * @return pull_t
    //  */
    // auto items() -> pull_t
    // {
    //     auto func = [&](typename coro_t::push_type& yield) {
    //         auto curkey = this->max;
    //         while (curkey > 0)
    //         {
    //             for (const auto& item : this->bucket[curkey].items())
    //             {
    //                 yield(item);
    //             }
    //             curkey -= 1;
    //         }
    //     };
    //     return pull_t(func);
    // }
};

template <typename _Tp, typename Int = int32_t>
class bpq_iterator
{
    using UInt = std::make_unsigned_t<Int>;

    // using value_type = _Tp;
    // using key_type = Int;
    using Item = dllink<std::pair<_Tp, UInt>>;

  private:
    bpqueue<_Tp, Int>& bpq;
    UInt curkey;
    dll_iterator<std::pair<_Tp, UInt>>
        curitem;
    constexpr auto curlist() -> Item&
    {
        return this->bpq.bucket[this->curkey];
    }

  public:
    constexpr bpq_iterator(bpqueue<_Tp, Int>& bpq, UInt curkey)
        : bpq {bpq}
        , curkey {curkey}
        , curitem {bpq.bucket[curkey].begin()}
    {
    }

    constexpr auto operator++() -> bpq_iterator&
    {
        ++this->curitem;
        while (this->curitem == this->curlist().end())
        {
            do
            {
                this->curkey -= 1;
            } while (this->curlist().is_empty());
            this->curitem = this->curlist().begin();
        }
        return *this;
    }

    constexpr auto operator*() -> Item&
    {
        return *this->curitem;
    }

    friend constexpr auto operator==(
        const bpq_iterator& lhs, const bpq_iterator& rhs) -> bool
    {
        return lhs.curitem == rhs.curitem;
    }

    friend constexpr auto operator!=(
        const bpq_iterator& lhs, const bpq_iterator& rhs) -> bool
    {
        return !(lhs == rhs);
    }
};

template <typename _Tp, typename Int, class _Sequence>
inline constexpr auto bpqueue<_Tp, Int, _Sequence>::begin()
    -> bpq_iterator<_Tp, Int>
{
    return {*this, this->max};
}

template <typename _Tp, typename Int, class _Sequence>
inline constexpr auto bpqueue<_Tp, Int, _Sequence>::end()
    -> bpq_iterator<_Tp, Int>
{
    return {*this, 0};
}