| Server IP : 217.160.0.135 / Your IP : 216.73.217.85 Web Server : Apache System : Linux www 6.18.52-i1-ampere #1203 SMP Mon Sep 14 18:29:59 CEST 2026 aarch64 User : sws1074145052 ( 1074145052) PHP Version : 8.3.32 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /lib/python3/dist-packages/pythran/pythonic/include/types/ |
Upload File : |
#ifndef PYTHONIC_INCLUDE_TYPES_NUMPY_EXPR_HPP
#define PYTHONIC_INCLUDE_TYPES_NUMPY_EXPR_HPP
#include "pythonic/include/utils/meta.hpp"
#include "pythonic/include/types/nditerator.hpp"
PYTHONIC_NS_BEGIN
namespace types
{
template <size_t I, class Args>
bool is_trivial_broadcast()
{
return std::is_same<typename std::tuple_element<
0, typename std::decay<typename std::tuple_element<
I, Args>::type>::type::shape_t>::type,
std::integral_constant<long, 1>>::value;
}
template <class... Tys>
struct count_non_integral;
template <>
struct count_non_integral<long> : std::integral_constant<long, 1> {
};
template <class T>
struct count_non_integral<T> : std::integral_constant<long, 0> {
};
template <class Ty0, class Ty1, class... Tys>
struct count_non_integral<Ty0, Ty1, Tys...>
: std::integral_constant<long,
count_non_integral<Ty0>::value +
count_non_integral<Ty1, Tys...>::value> {
};
template <class P>
struct is_perfect_stepping;
template <class... Tys>
struct is_perfect_stepping<pshape<Tys...>>
: std::integral_constant<bool, count_non_integral<Tys...>::value == 1> {
};
template <size_t value, class Args, size_t N, size_t... Is>
struct all_valid_indices;
template <size_t value, class Args, size_t... Is>
struct all_valid_indices<value, Args, 0, Is...> {
using type = utils::index_sequence<Is...>;
};
template <size_t value, class Args, size_t N, size_t... Is>
struct all_valid_indices
: std::conditional<(value <=
std::remove_reference<typename std::tuple_element<
N - 1, Args>::type>::type::value),
all_valid_indices<value, Args, N - 1, Is..., N - 1>,
all_valid_indices<value, Args, N - 1, Is...>>::type {
};
template <size_t value, class Args>
using valid_indices =
typename all_valid_indices<value, Args,
std::tuple_size<Args>::value>::type;
template <class Expr>
struct is_numexpr_arg;
template <class Expr, class... Slice>
struct numpy_gexpr;
template <class Op>
struct Dereferencer {
template <class Ts, size_t... I>
auto operator()(Ts const &iters, utils::index_sequence<I...>)
-> decltype(Op{}(*std::get<I>(iters)...))
{
return Op{}(*std::get<I>(iters)...);
}
};
template <class T>
struct step {
using type = typename T::step_type;
};
namespace details
{
template <size_t I, class Args, size_t... Is>
long init_shape_element(Args const &args, utils::index_sequence<Is...>);
}
template <class Op, class Steps, class... Iters>
struct numpy_expr_iterator
: std::iterator<std::random_access_iterator_tag,
typename std::remove_reference<decltype(std::declval<
Op>()(*std::declval<Iters>()...))>::type> {
Steps steps_;
std::tuple<Iters...> iters_;
numpy_expr_iterator(Steps steps, Iters... iters)
: steps_(steps), iters_(iters...)
{
}
numpy_expr_iterator(numpy_expr_iterator const &other)
: steps_(other.steps_), iters_(other.iters_)
{
}
numpy_expr_iterator &operator=(numpy_expr_iterator const &other)
{
iters_ = other.iters_;
return *this;
}
template <size_t... I>
auto _dereference(utils::index_sequence<I...> s) const
-> decltype(Dereferencer<Op>{}(iters_, s))
{
return Dereferencer<Op>{}(iters_, s);
}
auto operator*() const -> decltype(
this->_dereference(utils::make_index_sequence<sizeof...(Iters)>{}))
{
return _dereference(utils::make_index_sequence<sizeof...(Iters)>{});
}
template <size_t I>
bool _incr_opt(std::integral_constant<bool, true> long_step)
{
if (is_perfect_stepping<Steps>::value)
++std::get<I>(iters_);
else
std::get<I>(iters_) += std::get<I>(steps_);
return true;
}
template <size_t I>
bool _incr_opt(std::integral_constant<bool, false> long_step)
{
if (std::tuple_element<I, Steps>::type::value)
++std::get<I>(iters_);
return true;
}
template <size_t... I>
void _incr(utils::index_sequence<I...>)
{
(void)std::initializer_list<bool>{_incr_opt<I>(std::integral_constant<
bool, std::is_same<long, typename std::tuple_element<
I, Steps>::type>::value>{})...};
}
numpy_expr_iterator &operator++()
{
_incr(utils::make_index_sequence<sizeof...(Iters)>{});
return *this;
}
numpy_expr_iterator operator+(long i) const
{
numpy_expr_iterator other(*this);
return other += i;
}
template <size_t... I>
void _update(long i, utils::index_sequence<I...>)
{
(void)std::initializer_list<bool>{
(std::get<I>(iters_) += std::get<I>(steps_) * i, true)...};
}
numpy_expr_iterator &operator+=(long i)
{
_update(i, utils::make_index_sequence<sizeof...(Iters)>{});
return *this;
}
template <size_t... I>
long _difference(numpy_expr_iterator const &other,
utils::index_sequence<I...>) const
{
std::initializer_list<long> distances{(static_cast<long>(
std::get<I>(iters_) - std::get<I>(other.iters_)))...};
return *std::max_element(distances.begin(), distances.end());
}
long operator-(numpy_expr_iterator const &other) const
{
return _difference(other, utils::make_index_sequence<sizeof...(Iters)>{});
}
bool _neq(numpy_expr_iterator const &other, utils::int_<0u>) const
{
return false;
}
template <size_t I>
bool _neq(numpy_expr_iterator const &other, utils::int_<I>) const
{
return (std::get<I - 1>(steps_) &&
(std::get<I - 1>(iters_) != std::get<I - 1>(other.iters_))) ||
_neq(other, utils::int_<I - 1>{});
}
bool operator!=(numpy_expr_iterator const &other) const
{
return _neq(other, utils::int_<sizeof...(Iters)>{});
}
bool _eq(numpy_expr_iterator const &other, utils::int_<0u>) const
{
return true;
}
template <size_t I>
bool _eq(numpy_expr_iterator const &other, utils::int_<I>) const
{
return (!std::get<I - 1>(steps_) ||
(std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_))) &&
_eq(other, utils::int_<I - 1>{});
}
bool operator==(numpy_expr_iterator const &other) const
{
return _eq(other, utils::int_<sizeof...(Iters)>{});
}
bool _lt(numpy_expr_iterator const &other, utils::int_<0u>) const
{
return false;
}
template <size_t I>
bool _lt(numpy_expr_iterator const &other, utils::int_<I>) const
{
if (!std::get<I - 1>(steps_) ||
(std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_)))
return _lt(other, utils::int_<I - 1>{});
else
return std::get<I - 1>(steps_) &&
(std::get<I - 1>(iters_) < std::get<I - 1>(other.iters_));
}
bool operator<(numpy_expr_iterator const &other) const
{
return _lt(other, utils::int_<sizeof...(Iters)>{});
}
};
#ifdef USE_XSIMD
template <class E, class Op, class Steps, class SIters, class... Iters>
struct numpy_expr_simd_iterator
: std::iterator<std::random_access_iterator_tag,
typename std::remove_reference<decltype(std::declval<
Op>()(*std::declval<Iters>()...))>::type> {
Steps steps_;
std::tuple<Iters...> iters_;
SIters siters_;
numpy_expr_simd_iterator(array<long, sizeof...(Iters)> steps,
SIters const &siters, Iters... iters)
: steps_(steps), iters_(iters...), siters_(siters)
{
}
numpy_expr_simd_iterator(numpy_expr_simd_iterator const &other)
: steps_(other.steps_), iters_(other.iters_), siters_(other.siters_)
{
}
numpy_expr_simd_iterator &operator=(numpy_expr_simd_iterator const &other)
{
iters_ = other.iters_;
siters_ = other.siters_;
return *this;
}
template <size_t... I>
auto _dereference(utils::index_sequence<I...>) const
-> decltype(Op{}(*std::get<I>(iters_)...))
{
return Op{}((
(std::get<I>(steps_))
? (*std::get<I>(iters_))
: (xsimd::batch<
typename std::decay<decltype(*std::get<I>(siters_))>::type>(
*std::get<I>(siters_))))...);
}
auto operator*() const -> decltype(
this->_dereference(utils::make_index_sequence<sizeof...(Iters)>{}))
{
return _dereference(utils::make_index_sequence<sizeof...(Iters)>{});
}
template <size_t I>
bool _incr_opt(std::integral_constant<bool, true> long_step)
{
if (is_perfect_stepping<Steps>::value)
++std::get<I>(iters_);
else
std::get<I>(iters_) += std::get<I>(steps_);
return true;
}
template <size_t I>
bool _incr_opt(std::integral_constant<bool, false> long_step)
{
if (std::tuple_element<I, Steps>::type::value)
++std::get<I>(iters_);
return true;
}
template <size_t... I>
void _incr(utils::index_sequence<I...>)
{
(void)std::initializer_list<bool>{_incr_opt<I>(std::integral_constant<
bool, std::is_same<long, typename std::tuple_element<
I, Steps>::type>::value>{})...};
}
numpy_expr_simd_iterator &operator++()
{
_incr(utils::make_index_sequence<sizeof...(Iters)>{});
return *this;
}
numpy_expr_simd_iterator operator+(long i) const
{
numpy_expr_simd_iterator other(*this);
return other += i;
}
template <size_t... I>
void _update(long i, utils::index_sequence<I...>)
{
(void)std::initializer_list<bool>{
(std::get<I>(iters_) += std::get<I>(steps_) * i, true)...};
}
numpy_expr_simd_iterator &operator+=(long i)
{
_update(i, utils::make_index_sequence<sizeof...(Iters)>{});
return *this;
}
template <size_t... I>
long _difference(numpy_expr_simd_iterator const &other,
utils::index_sequence<I...>) const
{
std::initializer_list<long> distances{
(std::get<I>(iters_) - std::get<I>(other.iters_))...};
return *std::max_element(distances.begin(), distances.end());
}
long operator-(numpy_expr_simd_iterator const &other) const
{
return _difference(other, utils::make_index_sequence<sizeof...(Iters)>{});
}
bool _neq(numpy_expr_simd_iterator const &other, utils::int_<0u>) const
{
return false;
}
template <size_t I>
bool _neq(numpy_expr_simd_iterator const &other, utils::int_<I>) const
{
return (std::get<I - 1>(steps_) &&
(std::get<I - 1>(iters_) != std::get<I - 1>(other.iters_))) ||
_neq(other, utils::int_<I - 1>{});
}
bool operator!=(numpy_expr_simd_iterator const &other) const
{
return _neq(other, utils::int_<sizeof...(Iters)>{});
}
bool _eq(numpy_expr_simd_iterator const &other, utils::int_<0u>) const
{
return true;
}
template <size_t I>
bool _eq(numpy_expr_simd_iterator const &other, utils::int_<I>) const
{
return (std::get<I - 1>(steps_) &&
(std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_))) &&
_eq(other, utils::int_<I - 1>{});
}
bool operator==(numpy_expr_simd_iterator const &other) const
{
return _eq(other, utils::int_<sizeof...(Iters)>{});
}
bool _lt(numpy_expr_simd_iterator const &other, utils::int_<0u>) const
{
return false;
}
template <size_t I>
bool _lt(numpy_expr_simd_iterator const &other, utils::int_<I>) const
{
if (std::get<I - 1>(steps_) &&
(std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_)))
return _lt(other, utils::int_<I - 1>{});
else
return std::get<I - 1>(steps_) &&
(std::get<I - 1>(iters_) < std::get<I - 1>(other.iters_));
}
bool operator<(numpy_expr_simd_iterator const &other) const
{
return _lt(other, utils::int_<sizeof...(Iters)>{});
}
};
template <class E, class Op, class... Iters>
struct numpy_expr_simd_iterator_nobroadcast
: std::iterator<std::random_access_iterator_tag,
typename std::remove_reference<decltype(std::declval<
Op>()(*std::declval<Iters>()...))>::type> {
std::tuple<Iters...> iters_;
numpy_expr_simd_iterator_nobroadcast(Iters... iters) : iters_(iters...)
{
}
numpy_expr_simd_iterator_nobroadcast(
numpy_expr_simd_iterator_nobroadcast const &other)
: iters_(other.iters_)
{
}
numpy_expr_simd_iterator_nobroadcast &
operator=(numpy_expr_simd_iterator_nobroadcast const &other)
{
iters_ = other.iters_;
return *this;
}
template <size_t... I>
auto _dereference(utils::index_sequence<I...>) const
-> decltype(Op{}(*std::get<I>(iters_)...))
{
return Op{}((*std::get<I>(iters_))...);
}
auto operator*() const -> decltype(
this->_dereference(utils::make_index_sequence<sizeof...(Iters)>{}))
{
return _dereference(utils::make_index_sequence<sizeof...(Iters)>{});
}
template <size_t... I>
void _incr(utils::index_sequence<I...>)
{
(void)std::initializer_list<bool>{(++std::get<I>(iters_), true)...};
}
numpy_expr_simd_iterator_nobroadcast &operator++()
{
_incr(utils::make_index_sequence<sizeof...(Iters)>{});
return *this;
}
template <size_t... I>
long _difference(numpy_expr_simd_iterator_nobroadcast const &other,
utils::index_sequence<I...>) const
{
std::initializer_list<long> distances{
(std::get<I>(iters_) - std::get<I>(other.iters_))...};
return *std::max_element(distances.begin(), distances.end());
}
long operator-(numpy_expr_simd_iterator_nobroadcast const &other) const
{
return _difference(other, utils::make_index_sequence<sizeof...(Iters)>{});
}
numpy_expr_simd_iterator_nobroadcast operator+(long i) const
{
numpy_expr_simd_iterator_nobroadcast other(*this);
return other += i;
}
template <size_t... I>
void _update(long i, utils::index_sequence<I...>)
{
(void)std::initializer_list<bool>{(std::get<I>(iters_) += i, true)...};
}
numpy_expr_simd_iterator_nobroadcast &operator+=(long i)
{
_update(i, utils::make_index_sequence<sizeof...(Iters)>{});
return *this;
}
bool _neq(numpy_expr_simd_iterator_nobroadcast const &other,
utils::int_<0u>) const
{
return false;
}
template <size_t I>
bool _neq(numpy_expr_simd_iterator_nobroadcast const &other,
utils::int_<I>) const
{
return (std::get<I - 1>(iters_) != std::get<I - 1>(other.iters_)) ||
_neq(other, utils::int_<I - 1>{});
}
bool operator!=(numpy_expr_simd_iterator_nobroadcast const &other) const
{
return _neq(other, utils::int_<sizeof...(Iters)>{});
}
bool _eq(numpy_expr_simd_iterator_nobroadcast const &other,
utils::int_<0u>) const
{
return true;
}
template <size_t I>
bool _eq(numpy_expr_simd_iterator_nobroadcast const &other,
utils::int_<I>) const
{
return (std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_)) &&
_eq(other, utils::int_<I - 1>{});
}
bool operator==(numpy_expr_simd_iterator_nobroadcast const &other) const
{
return _eq(other, utils::int_<sizeof...(Iters)>{});
}
bool _lt(numpy_expr_simd_iterator_nobroadcast const &other,
utils::int_<0u>) const
{
return false;
}
template <size_t I>
bool _lt(numpy_expr_simd_iterator_nobroadcast const &other,
utils::int_<I>) const
{
if (std::get<I - 1>(iters_) == std::get<I - 1>(other.iters_))
return _lt(other, utils::int_<I - 1>{});
else
return std::get<I - 1>(iters_) < std::get<I - 1>(other.iters_);
}
bool operator<(numpy_expr_simd_iterator_nobroadcast const &other) const
{
return _lt(other, utils::int_<sizeof...(Iters)>{});
}
};
#endif
template <long N0, long N1>
std::integral_constant<long, N0 == N1>
make_step(std::integral_constant<long, N0>, std::integral_constant<long, N1>)
{
return {};
}
template <class T0, class T1>
long make_step(T0 n0, T1 n1)
{
return (long)n0 == (long)n1;
}
template <class S>
constexpr size_t count_none(size_t I)
{
return std::is_same<S, none_type>::value;
}
template <class S, class Sp, class... Ss>
constexpr size_t count_none(size_t I)
{
return std::is_same<S, none_type>::value +
(I == 0 ? 0 : count_none<Sp, Ss...>(I - 1));
}
template <class BT, class T>
using step_type_t = decltype(make_step(std::get<0>(std::declval<BT>()),
std::get<0>(std::declval<T>())));
constexpr size_t clamp(size_t i, size_t j)
{
return i > j ? j : i;
}
template <size_t... J, class Arg, class Shp, class... S>
auto make_subslice(utils::index_sequence<J...>, Arg const &arg,
Shp const &shp, std::tuple<S...> const &ss)
-> decltype(arg(std::get<J>(ss)...))
{
// we need to adapt_slice to take broadcasting into account
return arg(adapt_slice(std::get<J>(ss),
shp.template shape<J - count_none<S...>(J)>(),
arg.template shape<clamp(J - count_none<S...>(J),
Arg::value - 1)>())...);
}
/* Expression template for numpy expressions - binary operators
*/
template <class Op, class... Args>
struct numpy_expr {
using first_arg = typename utils::front<Args...>::type;
static const bool is_vectorizable =
utils::all_of<
std::remove_reference<Args>::type::is_vectorizable...>::value &&
utils::all_of<
std::is_same<typename std::remove_cv<typename std::remove_reference<
first_arg>::type>::type::dtype,
typename std::remove_cv<typename std::remove_reference<
Args>::type>::type::dtype>::value...>::value &&
types::is_vector_op<
Op, typename std::remove_reference<Args>::type::dtype...>::value;
static const bool is_strided =
utils::any_of<std::remove_reference<Args>::type::is_strided...>::value;
static constexpr size_t value =
utils::max_element<std::remove_reference<Args>::type::value...>::value;
using value_type = decltype(Op()(std::declval<
typename std::remove_reference<Args>::type::value_type>()...));
using dtype = decltype(Op()(
std::declval<typename std::remove_reference<Args>::type::dtype>()...));
#ifdef CYTHON_ABI
std::tuple<typename std::remove_reference<Args>::type...> args;
#else
std::tuple<Args...> args;
#endif
using shape_t = sutils::merged_shapes_t<
value, typename std::remove_reference<Args>::type::shape_t...>;
using steps_t = pshape<step_type_t<
shape_t, typename std::remove_reference<Args>::type::shape_t>...>;
static_assert(value == std::tuple_size<shape_t>::value,
"consistent shape and size");
using const_iterator = numpy_expr_iterator<
Op, steps_t,
typename std::remove_reference<Args>::type::const_iterator...>;
using iterator = numpy_expr_iterator<
Op, steps_t, typename std::remove_reference<Args>::type::iterator...>;
using const_fast_iterator = const_nditerator<numpy_expr>;
numpy_expr() = default;
numpy_expr(numpy_expr const &) = default;
numpy_expr(numpy_expr &&) = default;
#ifdef CYTHON_ABI
template <class... Argp>
numpy_expr(numpy_expr<Op, Argp...> const &other)
: args(other.args)
{
}
#endif
numpy_expr(Args const &... args);
template <size_t... I>
const_iterator _begin(utils::index_sequence<I...>) const;
const_iterator begin() const;
template <size_t... I>
const_iterator _end(utils::index_sequence<I...>) const;
const_iterator end() const;
const_fast_iterator begin(types::fast) const;
const_fast_iterator end(types::fast) const;
template <size_t... I>
iterator _begin(utils::index_sequence<I...>);
iterator begin();
template <size_t... I>
iterator _end(utils::index_sequence<I...>);
iterator end();
template <size_t... I>
auto _fast(long i, utils::index_sequence<I...>) const
-> decltype(Op()(std::get<I>(args).fast(i)...))
{
return Op()(std::get<I>(args).fast(i)...);
}
auto fast(long i) const
-> decltype(this->_fast(i,
utils::make_index_sequence<sizeof...(Args)>{}));
template <class... Indices, size_t... I>
auto _load(utils::index_sequence<I...>, Indices... indices) const
-> decltype(Op()(std::get<I>(args).load(indices...)...))
{
return Op()(std::get<I>(args).load(indices...)...);
}
template <class... Indices>
auto load(Indices... indices) const
-> decltype(this->_load(utils::make_index_sequence<sizeof...(Args)>{},
indices...))
{
return this->_load(utils::make_index_sequence<sizeof...(Args)>{},
indices...);
}
template <size_t... I>
auto _map_fast(array<long, sizeof...(I)> const &indices,
utils::index_sequence<I...>) const
-> decltype(Op()(std::get<I>(args).fast(std::get<I>(indices))...))
{
return Op()(std::get<I>(args).fast(std::get<I>(indices))...);
}
template <class... Indices>
auto map_fast(Indices... indices) const -> decltype(
this->_map_fast(array<long, sizeof...(Indices)>{{indices...}},
utils::make_index_sequence<sizeof...(Args)>{}));
public:
template <size_t I>
auto shape() const -> decltype(details::init_shape_element<I>(
args, valid_indices<value, std::tuple<Args...>>{}))
{
return details::init_shape_element<I>(
args, valid_indices<value, std::tuple<Args...>>{});
}
template <size_t... I>
bool _no_broadcast(utils::index_sequence<I...>) const;
bool no_broadcast() const;
template <size_t... I>
bool _no_broadcast_vectorize(utils::index_sequence<I...>) const;
bool no_broadcast_vectorize() const;
template <size_t... I>
bool _no_broadcast_ex(utils::index_sequence<I...>) const;
bool no_broadcast_ex() const;
#ifdef USE_XSIMD
using simd_iterator = numpy_expr_simd_iterator<
numpy_expr, Op,
pshape<step_type_t<
shape_t, typename std::remove_reference<Args>::type::shape_t>...>,
std::tuple<
typename std::remove_reference<Args>::type::const_iterator...>,
typename std::remove_reference<Args>::type::simd_iterator...>;
using simd_iterator_nobroadcast = numpy_expr_simd_iterator_nobroadcast<
numpy_expr, Op, typename std::remove_reference<
Args>::type::simd_iterator_nobroadcast...>;
template <size_t... I>
simd_iterator _vbegin(types::vectorize, utils::index_sequence<I...>) const;
simd_iterator vbegin(types::vectorize) const;
template <size_t... I>
simd_iterator _vend(types::vectorize, utils::index_sequence<I...>) const;
simd_iterator vend(types::vectorize) const;
template <size_t... I>
simd_iterator_nobroadcast _vbegin(types::vectorize_nobroadcast,
utils::index_sequence<I...>) const;
simd_iterator_nobroadcast vbegin(types::vectorize_nobroadcast) const;
template <size_t... I>
simd_iterator_nobroadcast _vend(types::vectorize_nobroadcast,
utils::index_sequence<I...>) const;
simd_iterator_nobroadcast vend(types::vectorize_nobroadcast) const;
#endif
template <size_t... I, class... S>
auto _get(utils::index_sequence<I...> is, S const &... s) const -> decltype(
Op{}(make_subslice(utils::make_index_sequence<sizeof...(S)>{},
std::get<I>(args), *this, std::make_tuple(s...))...))
{
return Op{}(make_subslice(utils::make_index_sequence<sizeof...(S)>{},
std::get<I>(args), *this,
std::make_tuple(s...))...);
}
template <class... S>
auto operator()(S const &... s) const
-> decltype(this->_get(utils::make_index_sequence<sizeof...(Args)>{},
s...));
template <class F>
typename std::enable_if<
is_numexpr_arg<F>::value &&
std::is_same<bool, typename F::dtype>::value &&
!is_pod_array<F>::value,
numpy_vexpr<numpy_expr, ndarray<long, pshape<long>>>>::type
fast(F const &filter) const;
template <class F>
typename std::enable_if<
is_numexpr_arg<F>::value &&
std::is_same<bool, typename F::dtype>::value &&
!is_pod_array<F>::value,
numpy_vexpr<numpy_expr, ndarray<long, pshape<long>>>>::type
operator[](F const &filter) const;
template <class F> // indexing through an array of indices -- a view
typename std::enable_if<is_numexpr_arg<F>::value &&
!is_array_index<F>::value &&
!std::is_same<bool, typename F::dtype>::value &&
!is_pod_array<F>::value,
numpy_vexpr<numpy_expr, F>>::type
operator[](F const &filter) const;
template <class F> // indexing through an array of indices -- a view
typename std::enable_if<is_numexpr_arg<F>::value &&
!is_array_index<F>::value &&
!std::is_same<bool, typename F::dtype>::value &&
!is_pod_array<F>::value,
numpy_vexpr<numpy_expr, F>>::type
fast(F const &filter) const;
// FIXME: this does not take into account bounds and broadcasting
auto operator[](long i) const -> decltype(this->fast(i));
template <size_t... I, class S>
auto _index(S s, utils::index_sequence<I...>) const
-> decltype(Op{}(std::get<I>(args)[s]...))
{
return Op{}(std::get<I>(args)[s]...);
}
template <class S>
auto operator[](S s) const
-> decltype((*this)
._index((s.lower, s),
utils::make_index_sequence<sizeof...(Args)>{}))
{
return _index(s, utils::make_index_sequence<sizeof...(Args)>{});
}
dtype operator[](array<long, value> const &indices) const
{
return _index(indices, utils::make_index_sequence<sizeof...(Args)>{});
}
explicit operator bool() const;
long flat_size() const;
long size() const;
};
}
template <class Op, class... Args>
struct assignable<types::numpy_expr<Op, Args...>> {
using type = types::ndarray<
typename pythonic::types::numpy_expr<Op, Args...>::dtype,
typename pythonic::types::numpy_expr<Op, Args...>::shape_t>;
};
template <class Op, class... Arg>
struct lazy<types::numpy_expr<Op, Arg...>> {
using type = types::numpy_expr<Op, typename lazy<Arg>::type...>;
};
PYTHONIC_NS_END
/* type inference stuff {*/
#include "pythonic/include/types/combined.hpp"
template <class Op, class K, class... Args>
struct __combined<pythonic::types::numpy_expr<Op, Args...>, indexable<K>> {
using type = pythonic::types::numpy_expr<Op, Args...>;
};
template <class Op, class K, class... Args>
struct __combined<indexable<K>, pythonic::types::numpy_expr<Op, Args...>> {
using type = pythonic::types::numpy_expr<Op, Args...>;
};
template <class Op, class K, class V, class... Args>
struct __combined<pythonic::types::numpy_expr<Op, Args...>,
indexable_container<K, V>> {
using type = pythonic::types::numpy_expr<Op, Args...>;
};
template <class Op, class K, class V, class... Args>
struct __combined<indexable_container<K, V>,
pythonic::types::numpy_expr<Op, Args...>> {
using type = pythonic::types::numpy_expr<Op, Args...>;
};
template <class Op, class K, class... Args>
struct __combined<container<K>, pythonic::types::numpy_expr<Op, Args...>> {
using type = pythonic::types::numpy_expr<Op, Args...>;
};
template <class Op, class K, class... Args>
struct __combined<pythonic::types::numpy_expr<Op, Args...>, container<K>> {
using type = pythonic::types::numpy_expr<Op, Args...>;
};
template <class Op, class Op2, class... Args, class... Args2>
struct __combined<pythonic::types::numpy_expr<Op, Args...>,
pythonic::types::numpy_expr<Op2, Args2...>> {
using type = pythonic::types::ndarray<
typename pythonic::types::numpy_expr<Op, Args...>::dtype,
pythonic::types::array<long,
pythonic::types::numpy_expr<Op, Args...>::value>>;
};
template <class E, class Op, class... Args>
struct __combined<pythonic::types::numpy_iexpr<E>,
pythonic::types::numpy_expr<Op, Args...>> {
using type = pythonic::types::numpy_iexpr<E>;
};
template <class E, class Op, class... Args>
struct __combined<pythonic::types::numpy_expr<Op, Args...>,
pythonic::types::numpy_iexpr<E>> {
using type = pythonic::types::numpy_iexpr<E>;
};
template <class T, class pS, class Op, class... Args>
struct __combined<pythonic::types::numpy_expr<Op, Args...>,
pythonic::types::ndarray<T, pS>> {
using type = pythonic::types::ndarray<T, pS>;
};
template <class T, class Op, class... Args>
struct __combined<pythonic::types::numpy_expr<Op, Args...>,
pythonic::types::numpy_texpr<T>> {
using type = pythonic::types::ndarray<
typename pythonic::types::numpy_expr<Op, Args...>::dtype,
pythonic::types::array<long,
pythonic::types::numpy_expr<Op, Args...>::value>>;
};
template <class T, class Op, class... Args>
struct __combined<pythonic::types::numpy_texpr<T>,
pythonic::types::numpy_expr<Op, Args...>> {
using type = pythonic::types::ndarray<
typename pythonic::types::numpy_expr<Op, Args...>::dtype,
pythonic::types::array<long,
pythonic::types::numpy_expr<Op, Args...>::value>>;
};
/*}*/
#endif