Program Listing for File array.hpp¶
↰ Return to documentation for file (include/ripple/container/array.hpp
)
#ifndef RIPPLE_CONTAINER_ARRAY_HPP
#define RIPPLE_CONTAINER_ARRAY_HPP
#include "array_traits.hpp"
#include <ripple/algorithm/unrolled_for.hpp>
namespace ripple {
template <typename Impl>
struct Array {
static constexpr size_t elements = array_traits_t<Impl>::size;
public:
ripple_all constexpr decltype(auto) operator[](size_t i) noexcept {
return impl()->operator[](i);
}
ripple_all constexpr decltype(auto)
operator[](size_t i) const noexcept {
return impl()->operator[](i);
}
ripple_all constexpr auto size() const noexcept -> size_t {
return array_traits_t<Impl>::size;
}
/*==--- [comparison operators] -------------------------------------------==*/
template <typename ImplOther>
ripple_all constexpr auto
operator==(const Array<ImplOther>& other) noexcept -> bool {
assert_size_match<ImplOther>();
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) != other[i]) {
return false;
}
}
return true;
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator==(T val) const noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) != val) {
return false;
}
}
return true;
}
template <typename ImplOther>
ripple_all constexpr auto
operator!=(const Array<ImplOther>& other) noexcept -> bool {
return !(*this == other);
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator!=(T val) const noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) != val) {
return true;
}
}
return false;
}
template <typename ImplOther>
ripple_all constexpr auto
operator<=(const Array<ImplOther>& other) noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) > other[i]) {
return false;
}
}
return true;
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator<=(T val) const noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) > val) {
return false;
}
}
return true;
}
template <typename ImplOther>
ripple_all constexpr auto
operator>=(const Array<ImplOther>& other) noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) < other[i]) {
return false;
}
}
return true;
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator>=(T val) const noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) < val) {
return false;
}
}
return true;
}
template <typename ImplOther>
ripple_all constexpr auto
operator<(const Array<ImplOther>& other) const noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) >= other[i]) {
return false;
}
}
return true;
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator<(T val) const noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) >= val) {
return false;
}
}
return true;
}
template <typename ImplOther>
ripple_all constexpr auto
operator>(const Array<ImplOther>& a) const noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) <= a[i]) {
return false;
}
}
return true;
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator>(T val) const noexcept -> bool {
for (size_t i = 0; i < elements; ++i) {
if (impl()->operator[](i) <= val) {
return false;
}
}
return true;
}
/*==--- [operator {+,+=} overloads] --------------------------------------==*/
template <typename ImplOther>
ripple_all constexpr auto
operator+=(const Array<ImplOther>& other) noexcept -> Impl& {
assert_size_match<ImplOther>();
unrolled_for_bounded<elements>(
[&](auto i) { impl()->operator[](i) += other[i]; });
return *impl();
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator+=(T val) noexcept -> Impl& {
using Value = typename array_traits_t<Impl>::Value;
unrolled_for_bounded<elements>(
[&](auto i) { impl()->operator[](i) += static_cast<Value>(val); });
return *impl();
}
template <typename ImplOther, typename R = array_impl_t<Impl, ImplOther>>
ripple_all constexpr auto
operator+(const Array<ImplOther>& other) const noexcept -> R {
R result;
unrolled_for_bounded<elements>(
[&](auto i) { result[i] = impl()->operator[](i) + other[i]; });
return result;
}
template <
typename T,
typename R = array_impl_t<Impl, Impl>,
array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator+(T val) const noexcept -> R {
R result;
using Value = typename array_traits_t<Impl>::Value;
unrolled_for_bounded<elements>([&](auto i) {
result[i] = impl()->operator[](i) + static_cast<Value>(val);
});
return result;
}
/*==--- [operator {-,-=} overloads] --------------------------------------==*/
template <typename ImplOther>
ripple_all constexpr auto
operator-=(const Array<ImplOther>& other) noexcept -> Impl& {
assert_size_match<ImplOther>();
unrolled_for_bounded<elements>(
[&](auto i) { impl()->operator[](i) -= other[i]; });
return *impl();
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator-=(T val) noexcept -> Impl& {
using Value = typename array_traits_t<Impl>::Value;
unrolled_for_bounded<elements>(
[&](auto i) { impl()->operator[](i) -= static_cast<Value>(val); });
return *impl();
}
template <typename ImplOther, typename R = array_impl_t<Impl, ImplOther>>
ripple_all constexpr auto
operator-(const Array<ImplOther>& other) const noexcept -> R {
R result;
unrolled_for_bounded<elements>(
[&](auto i) { result[i] = impl()->operator[](i) - other[i]; });
return result;
}
template <
typename T,
typename R = array_impl_t<Impl, Impl>,
array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator-(T val) const noexcept -> R {
R result;
using Value = typename array_traits_t<Impl>::Value;
unrolled_for_bounded<elements>([&](auto i) {
result[i] = impl()->operator[](i) - static_cast<Value>(val);
});
return result;
}
/*==--- [operator {*,*=} overloads] --------------------------------------==*/
template <typename ImplOther>
ripple_all constexpr auto
operator*=(const Array<ImplOther>& other) noexcept -> Impl& {
assert_size_match<ImplOther>();
unrolled_for_bounded<elements>(
[&](auto i) { impl()->operator[](i) *= other[i]; });
return *impl();
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator*=(T val) noexcept -> Impl& {
using Value = typename array_traits_t<Impl>::Value;
unrolled_for_bounded<elements>(
[&](auto i) { impl()->operator[](i) *= static_cast<Value>(val); });
return *impl();
}
template <typename ImplOther, typename R = array_impl_t<Impl, ImplOther>>
ripple_all constexpr auto
operator*(const Array<ImplOther>& other) const noexcept -> R {
R result;
unrolled_for_bounded<elements>(
[&](auto i) { result[i] = impl()->operator[](i) * other[i]; });
return result;
}
template <
typename T,
typename R = array_impl_t<Impl, Impl>,
array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator*(T val) const noexcept -> R {
R result;
using Value = typename array_traits_t<Impl>::Value;
unrolled_for_bounded<elements>([&](auto i) {
result[i] = impl()->operator[](i) * static_cast<Value>(val);
});
return result;
}
/*==--- [operator {/,/=} overloads] --------------------------------------==*/
template <typename ImplOther>
ripple_all constexpr auto
operator/=(const Array<ImplOther>& other) noexcept -> Impl& {
assert_size_match<ImplOther>();
unrolled_for_bounded<elements>(
[&](auto i) { impl()->operator[](i) /= other[i]; });
return *impl();
}
template <typename T, array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator/=(T val) noexcept -> Impl& {
using Value = typename array_traits_t<Impl>::Value;
unrolled_for_bounded<elements>(
[&](auto i) { impl()->operator[](i) /= static_cast<Value>(val); });
return *impl();
}
template <typename ImplOther, typename R = array_impl_t<Impl, ImplOther>>
ripple_all constexpr auto
operator/(const Array<ImplOther>& other) const noexcept -> R {
R result;
unrolled_for_bounded<elements>(
[&](auto i) { result[i] = impl()->operator[](i) / other[i]; });
return result;
}
template <
typename T,
typename R = array_impl_t<Impl, Impl>,
array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto operator/(T val) const noexcept -> R {
R result;
using Value = typename array_traits_t<Impl>::Value;
unrolled_for_bounded<elements>([&](auto i) {
result[i] = impl()->operator[](i) / static_cast<Value>(val);
});
return result;
}
private:
ripple_all constexpr auto impl() noexcept -> Impl* {
return static_cast<Impl*>(this);
}
ripple_all constexpr auto impl() const noexcept -> const Impl* {
return static_cast<const Impl*>(this);
}
template <typename ImplOther>
ripple_all constexpr auto assert_size_match() const noexcept -> void {
constexpr size_t size_other = array_traits_t<ImplOther>::size;
static_assert(
size_other == elements, "Arrays have different number of elements");
}
};
/*==--- [operator overloads] -----------------------------------------------==*/
template <
typename T,
typename Impl,
typename R = array_impl_t<Impl, Impl>,
array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto
operator+(T val, const Array<Impl>& a) noexcept -> R {
using Value = typename array_traits_t<Impl>::Value;
using Type = std::decay_t<T>;
static_assert(
std::is_same_v<Type, Value> || std::is_convertible_v<Type, Value>,
"Cannot perform operations on an array with a type which is not the "
"value type, or convertible to the value type!");
R result;
unrolled_for_bounded<array_traits_t<Impl>::size>(
[&](auto i) { result[i] = static_cast<Value>(val) + a[i]; });
return result;
}
template <
typename T,
typename Impl,
typename R = array_impl_t<Impl, Impl>,
array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto
operator-(T val, const Array<Impl>& a) noexcept -> R {
using Value = typename array_traits_t<Impl>::Value;
using Type = std::decay_t<T>;
static_assert(
std::is_same_v<Type, Value> || std::is_convertible_v<Type, Value>,
"Cannot perform operations on an array with a type which is not the "
"value type, or convertible to the value type!");
R result;
unrolled_for_bounded<array_traits_t<Impl>::size>(
[&](auto i) { result[i] = static_cast<Value>(val) - a[i]; });
return result;
}
template <
typename T,
typename Impl,
typename R = array_impl_t<Impl, Impl>,
array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto
operator*(T val, const Array<Impl>& a) noexcept -> R {
using Value = typename array_traits_t<Impl>::Value;
using Type = std::decay_t<T>;
static_assert(
std::is_same_v<Type, Value> || std::is_convertible_v<Type, Value>,
"Cannot perform operations on an array with a type which is not the "
"value type, or convertible to the value type!");
R result;
unrolled_for_bounded<array_traits_t<Impl>::size>(
[&](auto i) { result[i] = static_cast<Value>(val) * a[i]; });
return result;
}
template <
typename T,
typename Impl,
typename R = array_impl_t<Impl, Impl>,
array_value_enable_t<T, Impl> = 0>
ripple_all constexpr auto
operator/(T val, const Array<Impl>& a) noexcept -> R {
using Value = typename array_traits_t<Impl>::Value;
using Type = std::decay_t<T>;
static_assert(
std::is_same_v<Type, Value> || std::is_convertible_v<Type, Value>,
"Cannot perform operations on an array with a type which is not the "
"value type, or convertible to the value type!");
R result;
unrolled_for_bounded<array_traits_t<Impl>::size>(
[&](auto i) { result[i] = static_cast<Value>(val) / a[i]; });
return result;
}
} // namespace ripple
#endif // RIPPLE_CONTAINER_ARRAY_HPP