array.hΒΆ

// Copyright (C) 2017, 2019, 2022 by Mark Melton
//

#pragma once
#include <algorithm>
#include <array>

namespace core::mp {

template<class T, size_t N> requires std::is_integral_v<T>
consteval uint64_t array_product(const std::array<T, N>& arr) {
    T product{1};
    for (auto element : arr)
        product *= element;
    return product;
}

template<class T, T Elem, class U, size_t N>
requires std::is_same_v<T, U>
consteval int array_find(const std::array<U, N>& arr) {
    for (auto i = 0; i < arr.size(); ++i)
        if (arr[i] == Elem)
            return i;
    return arr.size();
}

template<size_t From, size_t To, class T, size_t N>
consteval auto array_slice(const std::array<T, N>& arr) {
    std::array<T, To - From> r;
    for (auto i = 0; i < r.size(); ++i)
        r[i] = arr[i + From];
    return r;
}

template<size_t From, class T, size_t N>
requires (From < N)
consteval auto array_slice_from(const std::array<T, N>& arr) {
    std::array<T, N - From> r;
    for (auto i = 0; i < r.size(); ++i)
        r[i] = arr[i + From];
    return r;
}

template<size_t From, class T, size_t N>
requires (From >= N)
consteval auto array_slice_from(const std::array<T, N>& arr) {
    return std::array<T, 0>{};
}

template<size_t To, class T, size_t N>
requires (To <= N)
consteval auto array_slice_to(const std::array<T, N>& arr) {
    std::array<T, To> r;
    for (auto i = 0; i < r.size(); ++i)
        r[i] = arr[i];
    return r;
}

template<class T, T delim, T... Ts>
consteval auto array_split() {
    constexpr auto arr = std::array<T, sizeof...(Ts)>{Ts...};
    constexpr auto n = array_find<T, delim>(arr);
    constexpr auto avalid = n > 0, bvalid = n + 1 < arr.size();
    if constexpr (avalid and bvalid)
        return std::pair{array_slice_to<n>(arr), array_slice_from<n + 1>(arr)};
    else if constexpr (avalid and not bvalid)
        return std::pair{array_slice_to<n>(arr), std::array<T, 0>{}};
    else if constexpr (not avalid and bvalid)
        return std::pair{std::array<T, 0>{}, array_slice_from<n + 1>(arr)};
    else if constexpr (not avalid and not bvalid)
        return std::pair{std::array<T, 0>{}, std::array<T, 0>{}};
}

template<class T, size_t N, class... Us>
requires (sizeof...(Us) > 0)
consteval auto array_append(const std::array<T, N>& arr, Us... elements) {
    std::array<T, N + sizeof...(Us)> r;
    std::copy(arr.begin(), arr.end(), r.begin());

    std::array<T, sizeof...(Us)> other{T(elements)...};
    std::copy(other.begin(), other.end(), r.begin() + N);
    return r;
}

template<class T, size_t N>
requires (N > 0)
consteval auto array_append(const std::array<T, N>& arr) {
    std::array<T, N> r;
    std::copy(arr.begin(), arr.end(), r.begin());
    return r;
}

template<class T, size_t N>
requires (N == 0)
consteval auto array_append(const std::array<T, N>& arr) {
    return std::array<T, N>{};
}

template<class T, size_t N> requires (N > 0 and std::is_integral_v<T>)
consteval std::array<T, N> array_bit_or(const std::array<T, N>& arr, const auto n) {
    std::array<T, N> r = arr;
    r[0] |= n;
    return r;
}

template<size_t M, class T, size_t N>
consteval auto array_drop(const std::array<T, N>& arr) {
    std::array<T, N - M> r;
    for (auto i = 0; i < r.size(); ++i)
        r[i] = arr[i + M];
    return r;
}

template<size_t M, class T, size_t N>
requires (M == N)
consteval auto array_drop(const std::array<T, N>& arr) {
    return std::array<T, 0>{};
}

template<class T, size_t N, class F>
consteval auto array_filter(const std::array<T, N>& arr, const F&& f) {
    std::array<T, N> r{};
    size_t rdx{0};
    for (auto elem : arr)
        if (f(elem))
            r[rdx++] = elem;
    return std::pair{r, rdx};
}

template<class T, size_t N> requires (N > 0 and std::is_integral_v<T>)
consteval std::array<T, N> array_shift_left(const std::array<T, N>& arr, const int n) {
    std::array<T, N> r;
    r[0] = arr[0] << n;
    for (auto i = 1; i < N; ++i)
        r[i] = (arr[i] << n) bitor (arr[i-1] >> (sizeof(T) * CHAR_BIT - n));
    return r;
}

template<size_t M, class T, size_t N>
consteval auto array_resize(const std::array<T, N>& arr) {
    std::array<T, M> r{};
    for (auto i = 0; i < std::min(N, M); ++i)
        r[i] = arr[i];
    return r;
}

}; // core::mp