View on GitHub

Jules documentation

Documentation of the Jules library

Home Tutorial Modules Entities Files

Header file array/functional.hpp

#define JULES_ARRAY_FUNCTIONAL_H

#define UNPACK

#define OPERATIONS_LIST

#define BASE_NDARRAY

#define CONTIGUOUS_ARRAY

#define REF_NDARRAY

#define INDIRECT_NDARRAY

#define BINARY_EXPR_NDARRAY

#define UNARY_EXPR_NDARRAY

#define BINARY_OPERATIONS_LIST

#define BINARY_APPLY_COMBINATIONS

#define BINARY_COMBINATIONS

#define BINARY_TYPE_OPERATION

#define BINARY_APPLY_OPERATION

#define BINARY_OPERATION

#define BINARY_RIGHT_TYPE_OPERATION

#define BINARY_LEFT_TYPE_OPERATION

#define UNARY_OPERATIONS_LIST

#define UNARY_APPLY_OPERATION

#define UNARY_OPERATION

namespace jules
{
    template <typename T, std::size_t N, typename Operator>
    auto apply(const base_array<T, N>& operand, Operator&& op);
    
    template <typename T, std::size_t N>
    auto operator+(const base_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator-(const base_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator~(const base_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator!(const base_array<T, N>& operand);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const base_array<T, N>& lhs, const base_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const base_array<T, N>& lhs, const ref_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const base_array<T, N>& lhs, const ind_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename A, typename B, typename Operator>
    auto apply(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename A, typename B, typename C, typename Operator>
    auto apply(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator+(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator+(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator-(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator-(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator*(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator*(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator/(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator/(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator%(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator%(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator&(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator&(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator|(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator|(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator^(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator^(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator&&(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator&&(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator||(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator||(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator==(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator==(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator!=(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator!=(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator<(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator<(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator<=(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator<=(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator>(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator>(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const base_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const base_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const base_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const base_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator>=(const base_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator>=(const base_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(const base_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(U lhs, const base_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename Operator>
    auto apply(const contiguous_array<T, N>& operand, Operator&& op);
    
    template <typename T, std::size_t N>
    auto operator+(const contiguous_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator-(const contiguous_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator~(const contiguous_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator!(const contiguous_array<T, N>& operand);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename A, typename B, typename Operator>
    auto apply(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename A, typename B, typename C, typename Operator>
    auto apply(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator+(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator+(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator-(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator-(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator*(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator*(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator/(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator/(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator%(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator%(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator&(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator&(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator|(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator|(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator^(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator^(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator&&(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator&&(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator||(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator||(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator==(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator==(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator!=(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator!=(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator<(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator<(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator<=(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator<=(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator>(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator>(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const contiguous_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const contiguous_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const contiguous_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const contiguous_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator>=(const contiguous_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator>=(const contiguous_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(const contiguous_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(U lhs, const contiguous_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename Operator>
    auto apply(const ref_array<T, N>& operand, Operator&& op);
    
    template <typename T, std::size_t N>
    auto operator+(const ref_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator-(const ref_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator~(const ref_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator!(const ref_array<T, N>& operand);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const ref_array<T, N>& lhs, const base_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename A, typename B, typename Operator>
    auto apply(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename A, typename B, typename C, typename Operator>
    auto apply(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator+(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator+(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator-(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator-(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator*(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator*(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator/(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator/(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator%(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator%(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator&(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator&(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator|(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator|(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator^(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator^(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator&&(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator&&(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator||(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator||(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator==(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator==(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator!=(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator!=(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator<(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator<(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator<=(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator<=(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator>(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator>(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const ref_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const ref_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const ref_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const ref_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator>=(const ref_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator>=(const ref_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(const ref_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(U lhs, const ref_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename Operator>
    auto apply(const ind_array<T, N>& operand, Operator&& op);
    
    template <typename T, std::size_t N>
    auto operator+(const ind_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator-(const ind_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator~(const ind_array<T, N>& operand);
    
    template <typename T, std::size_t N>
    auto operator!(const ind_array<T, N>& operand);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const ind_array<T, N>& lhs, const base_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U, typename Operator>
    auto apply(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename A, typename B, typename Operator>
    auto apply(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename A, typename B, typename C, typename Operator>
    auto apply(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs, Operator&& op);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator+(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator+(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator+(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator-(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator-(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator-(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator*(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator*(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator*(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator/(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator/(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator/(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator%(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator%(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator%(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator&(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator&(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator|(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator|(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator|(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator^(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator^(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator^(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator&&(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator&&(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator&&(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator||(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator||(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator||(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator==(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator==(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator==(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator!=(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator!=(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator!=(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator<(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator<(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator<=(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator<=(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator<=(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator>(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator>(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(U lhs, const ind_array<T, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const ind_array<T, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const ind_array<T, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const ind_array<T, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename U>
    auto operator>=(const ind_array<T, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B>
    auto operator>=(const ind_array<T, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename T, std::size_t N, typename A, typename B, typename C>
    auto operator>=(const ind_array<T, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(const ind_array<T, N>& lhs, U rhs);
    
    template <typename T, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(U lhs, const ind_array<T, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename Operator>
    auto apply(const unary_expr_array<It, Op, N>& operand, Operator&& op);
    
    template <typename It, typename Op, std::size_t N>
    auto operator+(const unary_expr_array<It, Op, N>& operand);
    
    template <typename It, typename Op, std::size_t N>
    auto operator-(const unary_expr_array<It, Op, N>& operand);
    
    template <typename It, typename Op, std::size_t N>
    auto operator~(const unary_expr_array<It, Op, N>& operand);
    
    template <typename It, typename Op, std::size_t N>
    auto operator!(const unary_expr_array<It, Op, N>& operand);
    
    template <typename It, typename Op, std::size_t N, typename U, typename Operator>
    auto apply(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs, Operator&& op);
    
    template <typename It, typename Op, std::size_t N, typename U, typename Operator>
    auto apply(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs, Operator&& op);
    
    template <typename It, typename Op, std::size_t N, typename U, typename Operator>
    auto apply(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs, Operator&& op);
    
    template <typename It, typename Op, std::size_t N, typename U, typename Operator>
    auto apply(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs, Operator&& op);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename Operator>
    auto apply(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs, Operator&& op);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C, typename Operator>
    auto apply(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs, Operator&& op);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator+(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator+(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator+(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator+(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator+(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator+(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator-(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator-(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator-(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator-(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator-(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator-(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator*(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator*(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator*(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator*(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator*(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator*(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator/(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator/(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator/(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator/(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator/(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator/(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator%(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator%(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator%(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator%(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator%(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator%(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator&(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator&(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator&(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator&(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator&(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator&(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator|(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator|(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator|(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator|(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator|(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator|(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator^(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator^(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator^(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator^(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator^(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator^(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator&&(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator&&(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator&&(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator&&(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator&&(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator&&(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator||(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator||(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator||(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator||(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator||(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator||(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator==(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator==(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator==(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator==(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator==(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator==(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator!=(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator!=(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator!=(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator!=(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator!=(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator!=(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator<(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator<(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator<(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator<(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator<(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator<(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator<=(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator<=(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator<=(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator<=(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator<=(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator<=(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator>(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator>(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator>(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator>(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator>(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator>(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator>=(const unary_expr_array<It, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator>=(const unary_expr_array<It, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator>=(const unary_expr_array<It, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U>
    auto operator>=(const unary_expr_array<It, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B>
    auto operator>=(const unary_expr_array<It, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator>=(const unary_expr_array<It, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(const unary_expr_array<It, Op, N>& lhs, U rhs);
    
    template <typename It, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(U lhs, const unary_expr_array<It, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename Operator>
    auto apply(const binary_expr_array<LhsIt, RhsIt, Op, N>& operand, Operator&& op);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N>
    auto operator+(const binary_expr_array<LhsIt, RhsIt, Op, N>& operand);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N>
    auto operator-(const binary_expr_array<LhsIt, RhsIt, Op, N>& operand);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N>
    auto operator~(const binary_expr_array<LhsIt, RhsIt, Op, N>& operand);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N>
    auto operator!(const binary_expr_array<LhsIt, RhsIt, Op, N>& operand);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename Operator>
    auto apply(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs, Operator&& op);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename Operator>
    auto apply(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs, Operator&& op);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename Operator>
    auto apply(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs, Operator&& op);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename Operator>
    auto apply(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs, Operator&& op);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename Operator>
    auto apply(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs, Operator&& op);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C, typename Operator>
    auto apply(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs, Operator&& op);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator+(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator+(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator+(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator+(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator+(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator+(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator+(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator-(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator-(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator-(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator-(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator-(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator-(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator-(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator*(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator*(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator*(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator*(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator*(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator*(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator*(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator/(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator/(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator/(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator/(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator/(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator/(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator/(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator%(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator%(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator%(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator%(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator%(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator%(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator%(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator|(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator|(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator|(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator|(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator|(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator|(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator|(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator^(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator^(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator^(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator^(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator^(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator^(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator^(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator&&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator&&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator&&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator&&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator&&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator&&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator&&(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator||(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator||(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator||(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator||(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator||(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator||(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator||(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator==(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator==(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator==(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator==(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator==(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator==(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator==(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator!=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator!=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator!=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator!=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator!=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator!=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator!=(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator<(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator<(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator<(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator<(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator<(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator<(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator<=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator<=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator<=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator<=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator<=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator<=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator<=(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator>(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator>(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator>(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator>(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator>(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator>(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator>=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const base_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator>=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const contiguous_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator>=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ref_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U>
    auto operator>=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const ind_array<U, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B>
    auto operator>=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const unary_expr_array<A, B, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename A, typename B, typename C>
    auto operator>=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, const binary_expr_array<A, B, C, N>& rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(const binary_expr_array<LhsIt, RhsIt, Op, N>& lhs, U rhs);
    
    template <typename LhsIt, typename RhsIt, typename Op, std::size_t N, typename U, typename = meta::fallback<Array<U>>>
    auto operator>=(U lhs, const binary_expr_array<LhsIt, RhsIt, Op, N>& rhs);
}