1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#pragma once


#include <cassert><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <vector><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <functional><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <queue><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <optional><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <concepts><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <ranges><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.


#include "internal/dev_env.hpp"
#include "internal/types.hpp"


namespace uni {


// Thanks to: https://qiita.com/drken/items/1b7e6e459c24a83bb7fd
template<
    class T,
    std::ranges::output_range<T> Container = std::vector<T>,
    std::strict_weak_order<T, T> Comparer = std::less<T>,
    std::strict_weak_order<T, T> RevComparer = std::greater<T>,
    std::integral Size = internal::size_t
>
struct kth_element {
    using value_type = T;
    using size_type = Size;

  protected:
    size_type _k;
    std::priority_queue<T, Container, Comparer> _small;
    std::priority_queue<T, Container, RevComparer> _large;

  public:
    kth_element(const size_type k = 0) noexcept(NO_EXCEPT) : _k(k + 1) { assert(k >= 0); }

    inline bool has() const noexcept(NO_EXCEPT) { return std::ssize(this->_small) == this->_k; }

    inline value_type value() const noexcept(NO_EXCEPT) { return this->_small.top(); }

    inline std::optional<value_type> get() const noexcept(NO_EXCEPT) {
        if(this->has()) return this->value();
        return {};
    }

    template<std::convertible_to<T> U = T>
    inline auto value_or(U&& v) const noexcept(NO_EXCEPT) {
        return this->get().value_or(std::forward<U>(v));
    }

    inline void push(const value_type& v) noexcept(NO_EXCEPT) {
        if(std::ssize(this->_small) < this->_k) {
            this->_small.push(v);
            return;
        }

        const auto kth = this->_small.top();

        if(Comparer{}(v, kth)) {
            this->_small.pop();
            this->_small.push(v);
            this->_large.push(kth);
        }
        else {
            this->_large.push(v);
        }
    }

    inline void pop() noexcept(NO_EXCEPT) {
        assert(this->has());

        this->_small.pop();

        if(this->_large.empty()) return;

        const auto v = this->_large.top();

        this->_large.pop();
        this->_small.push(v);
    }
};


} // namespace uni