競技プログラミング用の C++ ライブラリ
#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A"
// container_func.hpp の関数を愚直な実装とランダムに比較する
// 問題の入力は使わず、すべて一致したら Hello World を出力する
#include <algorithm>
#include <functional>
#include <iostream>
#include <numeric>
#include <random>
#include <set>
#include <string>
#include <vector>
#include "../../src/base/container_func.hpp"
bool ok = true;
void check(bool cond, const char* what) {
if (!cond) {
if (ok) std::cerr << "failed: " << what << std::endl;
ok = false;
}
}
int main() {
std::mt19937_64 rng(314);
auto rand_vec = [&](int n, long long lo, long long hi) {
std::vector<long long> v(n);
for (auto& x : v) x = lo + (long long)(rng() % (unsigned long long)(hi - lo + 1));
return v;
};
for (int rep = 0; rep < 2000 && ok; rep++) {
int n = rng() % 10 + 1;
std::vector<long long> v = rand_vec(n, -5, 5);
// sorted / sort (比較関数つき)
{
std::vector<long long> expect = v;
std::sort(expect.begin(), expect.end(), std::greater<long long>());
check(sorted(v, std::greater<long long>()) == expect, "sorted(c, f)");
std::vector<long long> w = v;
sort(w, std::greater<long long>());
check(w == expect, "sort(c, f)");
}
// set / multiset に対する max / min (ソート済みのコンテナは先頭と末尾)
{
std::set<long long> s(v.begin(), v.end());
std::multiset<long long> ms(v.begin(), v.end());
long long mx = *std::max_element(v.begin(), v.end());
long long mn = *std::min_element(v.begin(), v.end());
check(max(s) == mx && min(s) == mn, "max/min(set)");
check(max(ms) == mx && min(ms) == mn, "max/min(multiset)");
check(max(v) == mx && min(v) == mn, "max/min(vector)");
}
// product / product_xor
{
std::vector<long long> small = rand_vec(n, -3, 3);
long long p = 1, x = 0;
for (long long a : small) p *= a, x ^= a;
check(product(small) == p, "product(c)");
check(product_xor(small) == x, "product_xor(c)");
std::vector<long long> nonneg = rand_vec(n, 0, 1000000);
long long y = 0;
for (long long a : nonneg) y ^= a;
check(product_xor(nonneg) == y, "product_xor(c) large");
}
// maximum_subarray (空の部分列を許す版): 全区間の和の最大値と init の大きい方
{
long long init = (long long)(rng() % 7) - 3;
long long best = init;
for (int l = 0; l < n; l++) {
long long s = init;
for (int r = l; r < n; r++) {
s += v[r];
best = std::max(best, s);
}
}
check(maximum_subarray(v, init) == best, "maximum_subarray(c, init)");
// 空でない版も合わせて確かめる
long long best_nonempty = v[0];
for (int l = 0; l < n; l++) {
long long s = 0;
for (int r = l; r < n; r++) {
s += v[r];
best_nonempty = std::max(best_nonempty, s);
}
}
check(maximum_subarray(v) == best_nonempty, "maximum_subarray(c)");
}
// median: 偶数個なら中央 2 つの平均 (整数型は 0 方向に切り捨て)
{
std::vector<long long> w = v;
std::sort(w.begin(), w.end());
long long expect = n % 2 ? w[n / 2] : (w[n / 2 - 1] + w[n / 2]) / 2;
check(median(v) == expect, "median(vector<long long>)");
std::vector<double> d(v.begin(), v.end());
std::vector<double> dw = d;
std::sort(dw.begin(), dw.end());
double dexp = n % 2 ? dw[n / 2] : (dw[n / 2 - 1] + dw[n / 2]) / 2;
check(median(d) == dexp, "median(vector<double>)");
}
// index: 最初に現れる位置、なければ size
{
long long x = (long long)(rng() % 13) - 6;
long long expect = std::find(v.begin(), v.end(), x) - v.begin();
check(index(v, x) == expect, "index(c, x)");
}
// mex: 含まれない最小の非負整数
{
std::vector<int> w(n);
for (auto& a : w) a = (int)(rng() % 8) - 2;
int expect = 0;
while (std::find(w.begin(), w.end(), expect) != w.end()) expect++;
check(mex(w) == expect, "mex(c)");
}
// count / find / lower_bound / upper_bound (set はメンバ関数、vector は std:: のアルゴリズム)
{
long long x = (long long)(rng() % 13) - 6;
std::multiset<long long> ms(v.begin(), v.end());
std::vector<long long> w = v;
std::sort(w.begin(), w.end());
check(count(ms, x) == (std::size_t)std::count(v.begin(), v.end(), x), "count(multiset, x)");
check(count(v, x) == std::count(v.begin(), v.end(), x), "count(vector, x)");
check((find(ms, x) != ms.end()) == (std::find(v.begin(), v.end(), x) != v.end()), "find(multiset, x)");
check(find(v, x) == std::find(v.begin(), v.end(), x), "find(vector, x)");
check(lower_bound(ms, x) == ms.lower_bound(x) && upper_bound(ms, x) == ms.upper_bound(x), "lower/upper_bound(multiset, x)");
check(lower_bound(w, x) == std::lower_bound(w.begin(), w.end(), x), "lower_bound(vector, x)");
check(upper_bound(w, x) == std::upper_bound(w.begin(), w.end(), x), "upper_bound(vector, x)");
check(contains(v, x) == (std::find(v.begin(), v.end(), x) != v.end()), "contains(vector, x)");
}
}
// 初期化子リスト版
check(max({3, -1, 7, 2}) == 7, "max(initializer_list)");
check(min({3, -1, 7, 2}) == -1, "min(initializer_list)");
check(sum({3, -1, 7, 2}) == 11, "sum(initializer_list)");
check(product({3, -1, 7, 2}) == -42, "product(initializer_list)");
check(product_xor({3, 5, 6}) == 0, "product_xor(initializer_list)");
check(max({5}) == 5 && min({5}) == 5, "max/min(initializer_list) 1 要素");
// 境界: 1 要素、空
check(median(std::vector<int>{4}) == 4, "median 1 要素");
check(median(std::vector<int>{1, 4}) == 2, "median 2 要素");
check(mex(std::vector<int>{}) == 0, "mex 空");
check(mex(std::vector<int>{0, 1, 2}) == 3, "mex 0..n-1 をすべて含む");
check(maximum_subarray(std::vector<int>{}, 0) == 0, "maximum_subarray 空");
check(maximum_subarray(std::vector<int>{-5, -2, -9}) == -2, "maximum_subarray すべて負");
check(maximum_subarray(std::vector<int>{-5, -2, -9}, 0) == 0, "maximum_subarray すべて負、空を許す");
check(product(std::vector<int>{}) == 1 && product_xor(std::vector<int>{}) == 0, "product 空");
check(index(std::vector<int>{}, 3) == 0, "index 空");
check(sorted(std::string("bca"), std::greater<char>()) == "cba", "sorted(string, f)");
if (ok) std::cout << "Hello World" << std::endl;
}#line 1 "test/unittest/container-func.test.cpp"
#define PROBLEM "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_1_A"
// container_func.hpp の関数を愚直な実装とランダムに比較する
// 問題の入力は使わず、すべて一致したら Hello World を出力する
#include <algorithm>
#include <functional>
#include <iostream>
#include <numeric>
#include <random>
#include <set>
#include <string>
#include <vector>
#line 2 "src/base/container_func.hpp"
/**
* @file container_func.hpp
* @brief コンテナに対する関数 (sorted, max, sum など)
*/
#line 9 "src/base/container_func.hpp"
#include <cassert>
#line 11 "src/base/container_func.hpp"
#include <initializer_list>
#include <iterator>
#line 2 "src/base/traits.hpp"
/**
* @file traits.hpp
* @brief 型特性
*/
#include <cstddef>
#line 11 "src/base/traits.hpp"
#include <type_traits>
#include <utility>
namespace detail {
using std::begin, std::end;
template <class T, class = void>
struct is_range_impl : std::false_type {};
template <class T>
struct is_range_impl<T, std::void_t<decltype(begin(std::declval<T&>()), end(std::declval<T&>()))>> : std::true_type {};
} // namespace detail
template <class T>
struct is_range : detail::is_range_impl<T>::type {};
template <class T>
inline constexpr bool is_range_v = is_range<T>::value;
#define HAS_METHOD(func_name) \
namespace detail { \
template <class T, class = void> \
struct has_##func_name##_impl : std::false_type {}; \
template <class T> \
struct has_##func_name##_impl<T, std::void_t<decltype(std::declval<T>().func_name())>> \
: std::true_type {}; \
} \
template <class T> \
struct has_##func_name : detail::has_##func_name##_impl<T>::type {}; \
template <class T> \
inline constexpr bool has_##func_name##_v = has_##func_name<T>::value;
#define HAS_METHOD_ARG(func_name) \
namespace detail { \
template <class T, typename U, class = void> \
struct has_##func_name##_impl : std::false_type {}; \
template <class T, typename U> \
struct has_##func_name##_impl<T, U, std::void_t<decltype(std::declval<T>().func_name(std::declval<U>()))>> \
: std::true_type {}; \
} \
template <class T, typename U> \
struct has_##func_name : detail::has_##func_name##_impl<T, U>::type {}; \
template <class T, typename U> \
inline constexpr bool has_##func_name##_v = has_##func_name<T, U>::value;
HAS_METHOD(repr)
HAS_METHOD(type_str)
HAS_METHOD(initializer_str)
HAS_METHOD(max)
HAS_METHOD(min)
HAS_METHOD(reversed)
HAS_METHOD(sorted)
HAS_METHOD(sum)
HAS_METHOD(product)
HAS_METHOD(product_xor)
HAS_METHOD_ARG(count)
HAS_METHOD_ARG(find)
HAS_METHOD_ARG(lower_bound)
HAS_METHOD_ARG(upper_bound)
#define ENABLE_IF_T_IMPL(expr) std::enable_if_t<expr, std::nullptr_t> = nullptr
#define ENABLE_IF_T(...) ENABLE_IF_T_IMPL((__VA_ARGS__))
template <class C>
using mem_value_type = typename C::value_type;
template <class C>
using mem_difference_type = typename C::difference_type;
namespace detail {
template <class T, class = void>
struct is_sorted_container_impl : std::false_type {};
template <class T>
struct is_sorted_container_impl<std::set<T>> : std::true_type {};
template <class T>
struct is_sorted_container_impl<std::multiset<T>> : std::true_type {};
} // namespace detail
template <class T>
struct is_sorted_container : detail::is_sorted_container_impl<T>::type {};
template <class T>
inline constexpr bool is_sorted_container_v = is_sorted_container<T>::value;
#line 16 "src/base/container_func.hpp"
#define METHOD_EXPAND(func) \
template <typename T, ENABLE_IF_T(has_##func##_v<T>)> \
inline constexpr auto func(const T &t) -> decltype(t.func()) { \
return t.func(); \
}
#define METHOD_AND_FUNC_ARG_EXPAND(func) \
template <typename T, typename U, ENABLE_IF_T(has_##func##_v<T, U>)> \
inline constexpr auto func(const T &t, const U &u) \
-> decltype(t.func(u)) { \
return t.func(u); \
} \
template <typename T, typename U, ENABLE_IF_T(not has_##func##_v<T, U>)> \
inline constexpr auto func(const T &t, const U &u) \
-> decltype(std::func(t.begin(), t.end(), u)) { \
return std::func(t.begin(), t.end(), u); \
}
METHOD_EXPAND(reversed)
template <class C, ENABLE_IF_T(not has_reversed_v<C>)>
inline constexpr C reversed(C t) {
std::reverse(t.begin(), t.end());
return t;
}
METHOD_EXPAND(sorted)
template <class C, ENABLE_IF_T(not has_sorted_v<C>)>
inline constexpr C sorted(C t, bool reverse = false) {
std::sort(t.begin(), t.end());
if (reverse) std::reverse(t.begin(), t.end());
return t;
}
template <class C, class F, ENABLE_IF_T(not has_sorted_v<C> and std::is_invocable_r_v<bool, F, mem_value_type<C>, mem_value_type<C>>)>
inline constexpr C sorted(C t, F f) {
std::sort(t.begin(), t.end(), f);
return t;
}
template <class C>
inline constexpr void sort(C &t, bool reverse = false) {
std::sort(t.begin(), t.end());
if (reverse) std::reverse(t.begin(), t.end());
}
template <class C, class F, ENABLE_IF_T(std::is_invocable_r_v<bool, F, mem_value_type<C>, mem_value_type<C>>)>
inline constexpr void sort(C &t, F f) {
std::sort(t.begin(), t.end(), f);
}
template <class C, class F, ENABLE_IF_T(std::is_invocable_v<F, mem_value_type<C>>)>
inline constexpr void sort_by_key(C &t, F f) {
std::sort(t.begin(), t.end(), [&](const mem_value_type<C> &left, const mem_value_type<C> &right) {
return f(left) < f(right);
});
}
template <class C>
inline constexpr void reverse(C &t) {
std::reverse(t.begin(), t.end());
}
METHOD_EXPAND(max)
template <class C, ENABLE_IF_T(not has_max_v<C> and is_sorted_container_v<C>)>
inline constexpr mem_value_type<C> max(const C &v) {
assert(v.begin() != v.end());
return *v.rbegin();
}
template <class C, ENABLE_IF_T(not has_max_v<C> and not is_sorted_container_v<C>)>
inline constexpr mem_value_type<C> max(const C &v) {
assert(v.begin() != v.end());
return *std::max_element(v.begin(), v.end());
}
template <typename T>
inline constexpr T max(const std::initializer_list<T> &v) {
return std::max(v);
}
METHOD_EXPAND(min)
template <class C, ENABLE_IF_T(not has_min_v<C> and is_sorted_container_v<C>)>
inline constexpr mem_value_type<C> min(const C &v) {
assert(v.begin() != v.end());
return *v.begin();
}
template <class C, ENABLE_IF_T(not has_min_v<C> and not is_sorted_container_v<C>)>
inline constexpr mem_value_type<C> min(const C &v) {
assert(v.begin() != v.end());
return *std::min_element(v.begin(), v.end());
}
template <typename T>
inline constexpr T min(const std::initializer_list<T> &v) {
return std::min(v);
}
METHOD_EXPAND(sum)
template <class C, ENABLE_IF_T(not has_sum_v<C>)>
inline constexpr mem_value_type<C> sum(const C &v) {
return std::accumulate(v.begin(), v.end(), mem_value_type<C>{});
}
template <typename T>
inline constexpr T sum(const std::initializer_list<T> &v) {
return std::accumulate(v.begin(), v.end(), T{});
}
METHOD_EXPAND(product)
template <class C, ENABLE_IF_T(not has_product_v<C>)>
inline constexpr mem_value_type<C> product(const C &v) {
return std::accumulate(v.begin(), v.end(), mem_value_type<C>{1}, std::multiplies<mem_value_type<C>>());
}
template <typename T>
inline constexpr T product(const std::initializer_list<T> &v) {
return std::accumulate(v.begin(), v.end(), T{1}, std::multiplies<T>());
}
METHOD_EXPAND(product_xor)
template <class C, ENABLE_IF_T(not has_product_xor_v<C>)>
inline constexpr mem_value_type<C> product_xor(const C &v) {
return std::accumulate(v.begin(), v.end(), mem_value_type<C>{0}, std::bit_xor<mem_value_type<C>>());
}
template <typename T>
inline constexpr T product_xor(const std::initializer_list<T> &v) {
return std::accumulate(v.begin(), v.end(), T{0}, std::bit_xor<T>());
}
template <class C>
inline constexpr mem_value_type<C> maximum_subarray(const C &v) {
assert(not v.empty());
auto itr = v.begin();
mem_value_type<C> tmp = *itr++;
mem_value_type<C> res = tmp;
while (itr != v.end()) {
tmp += *itr;
if (tmp < *itr) tmp = *itr;
if (res < tmp) res = tmp;
++itr;
}
return res;
}
template <class C>
inline constexpr mem_value_type<C> maximum_subarray(const C &v, mem_value_type<C> init) {
mem_value_type<C> res = init, tmp = init;
for (const auto &a : v) {
tmp += a;
if (tmp < init) tmp = init;
if (res < tmp) res = tmp;
}
return res;
}
METHOD_AND_FUNC_ARG_EXPAND(count)
METHOD_AND_FUNC_ARG_EXPAND(find)
METHOD_AND_FUNC_ARG_EXPAND(lower_bound)
METHOD_AND_FUNC_ARG_EXPAND(upper_bound)
template <class C, typename T>
inline constexpr bool contains(const C &c, const T &t) {
return find(c, t) != c.end();
}
template <class C>
inline constexpr mem_value_type<C> gcd(const C &v) {
mem_value_type<C> init(0);
for (const auto &e : v) init = std::gcd(init, e);
return init;
}
template <class C>
inline constexpr mem_value_type<C> average(const C &v) {
assert(v.size());
return sum(v) / v.size();
}
template <class C>
inline constexpr mem_value_type<C> median(const C &v) {
assert(not v.empty());
std::vector<size_t> u(v.size());
std::iota(u.begin(), u.end(), 0);
std::sort(u.begin(), u.end(), [&](size_t a, size_t b) {
return v[a] < v[b];
});
if (v.size() & 1) {
return v[u[v.size() / 2]];
}
// C++20
// return std::midpoint(v[u[v.size() / 2]], v[u[v.size() / 2 - 1]]);
return (v[u[v.size() / 2]] + v[u[v.size() / 2 - 1]]) / 2;
}
template <class C, typename U>
inline constexpr std::ptrdiff_t index(const C &v, const U &x) {
return std::distance(v.begin(), find(v, x));
}
template <class C, ENABLE_IF_T(std::is_integral_v<mem_value_type<C>>)>
inline constexpr mem_value_type<C> mex(const C &v) {
std::vector<bool> b(v.size() + 1);
for (const auto &a : v) {
if (0 <= a and static_cast<size_t>(a) < b.size()) {
b[a] = true;
}
}
mem_value_type<C> ret = 0; // b の要素数は v より 1 多いので、下のループで必ず見つかる
for (size_t i = 0; i < b.size(); i++) {
if (not b[i]) {
ret = i;
break;
}
}
return ret;
}
template <class C>
inline constexpr mem_difference_type<C> bisect_left(const C &v, const mem_value_type<C> &x) {
return std::distance(v.begin(), lower_bound(v, x));
}
template <class C>
inline constexpr mem_difference_type<C> bisect_right(const C &v, const mem_value_type<C> &x) {
return std::distance(v.begin(), upper_bound(v, x));
}
#line 16 "test/unittest/container-func.test.cpp"
bool ok = true;
void check(bool cond, const char* what) {
if (!cond) {
if (ok) std::cerr << "failed: " << what << std::endl;
ok = false;
}
}
int main() {
std::mt19937_64 rng(314);
auto rand_vec = [&](int n, long long lo, long long hi) {
std::vector<long long> v(n);
for (auto& x : v) x = lo + (long long)(rng() % (unsigned long long)(hi - lo + 1));
return v;
};
for (int rep = 0; rep < 2000 && ok; rep++) {
int n = rng() % 10 + 1;
std::vector<long long> v = rand_vec(n, -5, 5);
// sorted / sort (比較関数つき)
{
std::vector<long long> expect = v;
std::sort(expect.begin(), expect.end(), std::greater<long long>());
check(sorted(v, std::greater<long long>()) == expect, "sorted(c, f)");
std::vector<long long> w = v;
sort(w, std::greater<long long>());
check(w == expect, "sort(c, f)");
}
// set / multiset に対する max / min (ソート済みのコンテナは先頭と末尾)
{
std::set<long long> s(v.begin(), v.end());
std::multiset<long long> ms(v.begin(), v.end());
long long mx = *std::max_element(v.begin(), v.end());
long long mn = *std::min_element(v.begin(), v.end());
check(max(s) == mx && min(s) == mn, "max/min(set)");
check(max(ms) == mx && min(ms) == mn, "max/min(multiset)");
check(max(v) == mx && min(v) == mn, "max/min(vector)");
}
// product / product_xor
{
std::vector<long long> small = rand_vec(n, -3, 3);
long long p = 1, x = 0;
for (long long a : small) p *= a, x ^= a;
check(product(small) == p, "product(c)");
check(product_xor(small) == x, "product_xor(c)");
std::vector<long long> nonneg = rand_vec(n, 0, 1000000);
long long y = 0;
for (long long a : nonneg) y ^= a;
check(product_xor(nonneg) == y, "product_xor(c) large");
}
// maximum_subarray (空の部分列を許す版): 全区間の和の最大値と init の大きい方
{
long long init = (long long)(rng() % 7) - 3;
long long best = init;
for (int l = 0; l < n; l++) {
long long s = init;
for (int r = l; r < n; r++) {
s += v[r];
best = std::max(best, s);
}
}
check(maximum_subarray(v, init) == best, "maximum_subarray(c, init)");
// 空でない版も合わせて確かめる
long long best_nonempty = v[0];
for (int l = 0; l < n; l++) {
long long s = 0;
for (int r = l; r < n; r++) {
s += v[r];
best_nonempty = std::max(best_nonempty, s);
}
}
check(maximum_subarray(v) == best_nonempty, "maximum_subarray(c)");
}
// median: 偶数個なら中央 2 つの平均 (整数型は 0 方向に切り捨て)
{
std::vector<long long> w = v;
std::sort(w.begin(), w.end());
long long expect = n % 2 ? w[n / 2] : (w[n / 2 - 1] + w[n / 2]) / 2;
check(median(v) == expect, "median(vector<long long>)");
std::vector<double> d(v.begin(), v.end());
std::vector<double> dw = d;
std::sort(dw.begin(), dw.end());
double dexp = n % 2 ? dw[n / 2] : (dw[n / 2 - 1] + dw[n / 2]) / 2;
check(median(d) == dexp, "median(vector<double>)");
}
// index: 最初に現れる位置、なければ size
{
long long x = (long long)(rng() % 13) - 6;
long long expect = std::find(v.begin(), v.end(), x) - v.begin();
check(index(v, x) == expect, "index(c, x)");
}
// mex: 含まれない最小の非負整数
{
std::vector<int> w(n);
for (auto& a : w) a = (int)(rng() % 8) - 2;
int expect = 0;
while (std::find(w.begin(), w.end(), expect) != w.end()) expect++;
check(mex(w) == expect, "mex(c)");
}
// count / find / lower_bound / upper_bound (set はメンバ関数、vector は std:: のアルゴリズム)
{
long long x = (long long)(rng() % 13) - 6;
std::multiset<long long> ms(v.begin(), v.end());
std::vector<long long> w = v;
std::sort(w.begin(), w.end());
check(count(ms, x) == (std::size_t)std::count(v.begin(), v.end(), x), "count(multiset, x)");
check(count(v, x) == std::count(v.begin(), v.end(), x), "count(vector, x)");
check((find(ms, x) != ms.end()) == (std::find(v.begin(), v.end(), x) != v.end()), "find(multiset, x)");
check(find(v, x) == std::find(v.begin(), v.end(), x), "find(vector, x)");
check(lower_bound(ms, x) == ms.lower_bound(x) && upper_bound(ms, x) == ms.upper_bound(x), "lower/upper_bound(multiset, x)");
check(lower_bound(w, x) == std::lower_bound(w.begin(), w.end(), x), "lower_bound(vector, x)");
check(upper_bound(w, x) == std::upper_bound(w.begin(), w.end(), x), "upper_bound(vector, x)");
check(contains(v, x) == (std::find(v.begin(), v.end(), x) != v.end()), "contains(vector, x)");
}
}
// 初期化子リスト版
check(max({3, -1, 7, 2}) == 7, "max(initializer_list)");
check(min({3, -1, 7, 2}) == -1, "min(initializer_list)");
check(sum({3, -1, 7, 2}) == 11, "sum(initializer_list)");
check(product({3, -1, 7, 2}) == -42, "product(initializer_list)");
check(product_xor({3, 5, 6}) == 0, "product_xor(initializer_list)");
check(max({5}) == 5 && min({5}) == 5, "max/min(initializer_list) 1 要素");
// 境界: 1 要素、空
check(median(std::vector<int>{4}) == 4, "median 1 要素");
check(median(std::vector<int>{1, 4}) == 2, "median 2 要素");
check(mex(std::vector<int>{}) == 0, "mex 空");
check(mex(std::vector<int>{0, 1, 2}) == 3, "mex 0..n-1 をすべて含む");
check(maximum_subarray(std::vector<int>{}, 0) == 0, "maximum_subarray 空");
check(maximum_subarray(std::vector<int>{-5, -2, -9}) == -2, "maximum_subarray すべて負");
check(maximum_subarray(std::vector<int>{-5, -2, -9}, 0) == 0, "maximum_subarray すべて負、空を許す");
check(product(std::vector<int>{}) == 1 && product_xor(std::vector<int>{}) == 0, "product 空");
check(index(std::vector<int>{}, 3) == 0, "index 空");
check(sorted(std::string("bca"), std::greater<char>()) == "cba", "sorted(string, f)");
if (ok) std::cout << "Hello World" << std::endl;
}