| Server IP : 217.160.0.135 / Your IP : 216.73.217.117 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_GEXPR_HPP
#define PYTHONIC_INCLUDE_TYPES_NUMPY_GEXPR_HPP
#include "pythonic/include/utils/meta.hpp"
#include "pythonic/include/utils/array_helper.hpp"
PYTHONIC_NS_BEGIN
namespace types
{
/* helper to count new axis
*/
template <class... T>
struct count_new_axis;
template <>
struct count_new_axis<> {
static constexpr size_t value = 0;
};
template <>
struct count_new_axis<types::none_type> {
static constexpr size_t value = 1;
};
template <class T>
struct count_new_axis<T> {
static constexpr size_t value = 0;
};
template <class T0, class... T>
struct count_new_axis<T0, T...> {
static constexpr size_t value =
count_new_axis<T0>::value + count_new_axis<T...>::value;
};
/* helper to turn a new axis into a slice
*/
template <class T>
struct to_slice {
using type = T;
static constexpr bool is_new_axis = false;
T operator()(T value);
};
template <>
struct to_slice<none_type> {
using type = fast_contiguous_slice;
static constexpr bool is_new_axis = true;
fast_contiguous_slice operator()(none_type);
};
template <class T>
struct to_normalized_slice {
using type = T;
T operator()(T value);
};
template <>
struct to_normalized_slice<none_type> {
using type = contiguous_normalized_slice;
contiguous_normalized_slice operator()(none_type);
};
/* helper to build a new shape out of a shape and a slice with new axis
*/
template <size_t N, class pS, class IsNewAxis>
auto make_reshape(pS const &shape, IsNewAxis is_new_axis)
-> decltype(sutils::copy_new_axis<pS::value + N>(shape, is_new_axis));
/* helper to build an extended slice aka numpy_gexpr out of a subscript
*/
template <size_t C>
struct extended_slice {
template <class E, class... S>
auto operator()(E &&expr, S const &... s)
-> decltype(std::forward<E>(expr).reshape(make_reshape<C>(
expr,
std::tuple<
std::integral_constant<bool, to_slice<S>::is_new_axis>...>()))(
to_slice<S>{}(s)...))
{
return std::forward<E>(expr).reshape(make_reshape<C>(
expr,
std::tuple<
std::integral_constant<bool, to_slice<S>::is_new_axis>...>()))(
to_slice<S>{}(s)...);
}
};
template <>
struct extended_slice<0> {
template <class E, class... S>
auto operator()(E &&expr, long const &s0, S const &... s) ->
typename std::enable_if<
utils::all_of<std::is_integral<S>::value...>::value,
decltype(std::forward<E>(expr)[types::make_tuple(s0, s...)])>::type
{
return std::forward<E>(expr)[types::make_tuple(s0, s...)];
}
template <class E, class... S>
auto operator()(E &&expr, long const &s0, S const &... s) ->
typename std::enable_if<
!utils::all_of<std::is_integral<S>::value...>::value,
decltype(std::forward<E>(expr)[s0](s...))>::type
{
return std::forward<E>(expr)[s0](s...);
}
template <class E, class... S, size_t... Is>
numpy_gexpr<typename std::decay<E>::type, normalize_t<S>...>
fwd(E &&expr, std::tuple<S...> const &s, utils::index_sequence<Is...>)
{
return {std::forward<E>(expr),
std::get<Is>(s).normalize(expr.template shape<Is>())...};
}
template <class E, class Sp, class... S>
typename std::enable_if<
is_slice<Sp>::value,
numpy_gexpr<E, normalize_t<Sp>, normalize_t<S>...>>::type
operator()(E &&expr, Sp const &s0, S const &... s)
{
return make_gexpr(std::forward<E>(expr), s0, s...);
}
template <class E, class F, class... S>
typename std::enable_if<
!is_slice<F>::value,
numpy_gexpr<ndarray<typename std::decay<E>::type::dtype,
array<long, std::decay<E>::type::value>>,
contiguous_normalized_slice, normalize_t<S>...>>::type
operator()(E &&expr, F const &s0, S const &... s)
{
return numpy_vexpr<ndarray<typename std::decay<E>::type::dtype,
array<long, std::decay<E>::type::value>>,
F>{std::forward<E>(expr), s0}(
fast_contiguous_slice(none_type{}, none_type{}), s...);
}
};
/* Meta-Function to count the number of slices in a type list
*/
template <class... Types>
struct count_long;
template <>
struct count_long<long> {
static constexpr size_t value = 1;
};
template <>
struct count_long<normalized_slice> {
static constexpr size_t value = 0;
};
template <>
struct count_long<contiguous_normalized_slice> {
static constexpr size_t value = 0;
};
template <class T, class... Types>
struct count_long<T, Types...> {
static constexpr size_t value =
count_long<T>::value + count_long<Types...>::value;
};
template <>
struct count_long<> {
static constexpr size_t value = 0;
};
/* helper to get the type of the nth element of an array
*/
template <class T, size_t N>
struct nth_value_type {
using type = typename nth_value_type<typename T::value_type, N - 1>::type;
};
template <class T>
struct nth_value_type<T, 0> {
using type = T;
};
/* helper that yields true if the first slice of a pack is a contiguous
* slice
*/
template <class... S>
struct is_contiguous {
static const bool value = false;
};
template <class... S>
struct is_contiguous<contiguous_normalized_slice, S...> {
static const bool value = true;
};
/* numpy_gexpr factory
*
* replaces the constructor, in order to properly merge gexpr composition
*into a single gexpr
*/
namespace details
{
template <class T, class Ts, size_t... Is>
std::tuple<T, typename std::tuple_element<Is, Ts>::type...>
tuple_push_head(T const &val, Ts const &vals, utils::index_sequence<Is...>)
{
return std::tuple<T, typename std::tuple_element<Is, Ts>::type...>{
val, std::get<Is>(vals)...};
}
template <class T, class Ts>
auto tuple_push_head(T const &val, Ts const &vals)
-> decltype(tuple_push_head(
val, vals,
utils::make_index_sequence<std::tuple_size<Ts>::value>()))
{
return tuple_push_head(
val, vals, utils::make_index_sequence<std::tuple_size<Ts>::value>());
}
// this struct is specialized for every type combination && takes care of
// the slice merge
template <class T, class Tp>
struct merge_gexpr;
template <>
struct merge_gexpr<std::tuple<>, std::tuple<>> {
template <size_t I, class S>
std::tuple<> run(S const &, std::tuple<> const &t0, std::tuple<> const &);
};
template <class... T0>
struct merge_gexpr<std::tuple<T0...>, std::tuple<>> {
template <size_t I, class S>
std::tuple<T0...> run(S const &, std::tuple<T0...> const &t0,
std::tuple<>);
static_assert(
utils::all_of<std::is_same<T0, normalize_t<T0>>::value...>::value,
"all slices are normalized");
};
template <class... T1>
struct merge_gexpr<std::tuple<>, std::tuple<T1...>> {
template <size_t I, class S>
std::tuple<normalize_t<T1>...> run(S const &, std::tuple<>,
std::tuple<T1...> const &t1);
};
template <class S0, class... T0, class S1, class... T1>
struct merge_gexpr<std::tuple<S0, T0...>, std::tuple<S1, T1...>> {
template <size_t I, class S>
auto run(S const &s, std::tuple<S0, T0...> const &t0,
std::tuple<S1, T1...> const &t1)
-> decltype(tuple_push_head(
std::get<0>(t0) * std::get<0>(t1),
merge_gexpr<std::tuple<T0...>, std::tuple<T1...>>{}
.template run<I + 1>(s, tuple_tail(t0), tuple_tail(t1))))
{
return tuple_push_head(
std::get<0>(t0) * std::get<0>(t1),
merge_gexpr<std::tuple<T0...>, std::tuple<T1...>>{}
.template run<I + 1>(s, tuple_tail(t0), tuple_tail(t1)));
}
static_assert(
std::is_same<decltype(std::declval<S0>() * std::declval<S1>()),
normalize_t<decltype(std::declval<S0>() *
std::declval<S1>())>>::value,
"all slices are normalized");
};
template <class S0, class... T0, class... T1>
struct merge_gexpr<std::tuple<S0, T0...>, std::tuple<none_type, T1...>> {
template <size_t I, class S>
auto run(S const &s, std::tuple<S0, T0...> const &t0,
std::tuple<none_type, T1...> const &t1)
-> decltype(tuple_push_head(
std::get<0>(t1),
merge_gexpr<std::tuple<S0, T0...>, std::tuple<T1...>>{}
.template run<I + 1>(s, t0, tuple_tail(t1))))
{
return tuple_push_head(
std::get<0>(t1),
merge_gexpr<std::tuple<S0, T0...>, std::tuple<T1...>>{}
.template run<I + 1>(s, t0, tuple_tail(t1)));
}
};
template <class... T0, class S1, class... T1>
struct merge_gexpr<std::tuple<long, T0...>, std::tuple<S1, T1...>> {
template <size_t I, class S>
auto run(S const &s, std::tuple<long, T0...> const &t0,
std::tuple<S1, T1...> const &t1)
-> decltype(tuple_push_head(
std::get<0>(t0),
merge_gexpr<std::tuple<T0...>, std::tuple<S1, T1...>>{}
.template run<I>(s, tuple_tail(t0), t1)))
{
return tuple_push_head(
std::get<0>(t0),
merge_gexpr<std::tuple<T0...>, std::tuple<S1, T1...>>{}
.template run<I>(s, tuple_tail(t0), t1));
}
};
template <class... T0, class... T1>
struct merge_gexpr<std::tuple<long, T0...>, std::tuple<none_type, T1...>> {
template <size_t I, class S>
auto run(S const &s, std::tuple<long, T0...> const &t0,
std::tuple<none_type, T1...> const &t1)
-> decltype(tuple_push_head(
std::get<0>(t0),
merge_gexpr<std::tuple<T0...>, std::tuple<none_type, T1...>>{}
.template run<I>(s, tuple_tail(t0), t1)))
{
return tuple_push_head(
std::get<0>(t0),
merge_gexpr<std::tuple<T0...>, std::tuple<none_type, T1...>>{}
.template run<I>(s, tuple_tail(t0), t1));
}
};
template <class S0, class... T0, class... T1>
struct merge_gexpr<std::tuple<S0, T0...>, std::tuple<long, T1...>> {
template <size_t I, class S>
auto run(S const &s, std::tuple<S0, T0...> const &t0,
std::tuple<long, T1...> const &t1)
-> decltype(tuple_push_head(
std::get<0>(t1) * std::get<0>(t0).step + std::get<0>(t0).lower,
merge_gexpr<std::tuple<T0...>, std::tuple<T1...>>{}
.template run<I + 1>(s, tuple_tail(t0), tuple_tail(t1))))
{
return tuple_push_head(
std::get<0>(t1) * std::get<0>(t0).step + std::get<0>(t0).lower,
merge_gexpr<std::tuple<T0...>, std::tuple<T1...>>{}
.template run<I + 1>(s, tuple_tail(t0), tuple_tail(t1)));
}
};
template <class... T0, class... T1>
struct merge_gexpr<std::tuple<long, T0...>, std::tuple<long, T1...>> {
template <size_t I, class S>
auto run(S const &s, std::tuple<long, T0...> const &t0,
std::tuple<long, T1...> const &t1)
-> decltype(tuple_push_head(
std::get<0>(t0),
merge_gexpr<std::tuple<T0...>, std::tuple<long, T1...>>{}
.template run<I>(s, tuple_tail(t0), t1)))
{
return tuple_push_head(
std::get<0>(t0),
merge_gexpr<std::tuple<T0...>, std::tuple<long, T1...>>{}
.template run<I>(s, tuple_tail(t0), t1));
}
};
template <class Arg, class... Sp>
typename std::enable_if<count_new_axis<Sp...>::value == 0,
numpy_gexpr<Arg, Sp...>>::type
_make_gexpr(Arg arg, std::tuple<Sp...> const &t);
template <class Arg, class S, size_t... Is>
numpy_gexpr<Arg, typename to_normalized_slice<
typename std::tuple_element<Is, S>::type>::type...>
_make_gexpr_helper(Arg arg, S const &s, utils::index_sequence<Is...>);
template <class Arg, class... Sp>
auto _make_gexpr(Arg arg, std::tuple<Sp...> const &s) ->
typename std::enable_if<
count_new_axis<Sp...>::value != 0,
decltype(_make_gexpr_helper(
arg.reshape(make_reshape<count_new_axis<Sp...>::value>(
arg, std::tuple<std::integral_constant<
bool, to_slice<Sp>::is_new_axis>...>())),
s, utils::make_index_sequence<sizeof...(Sp)>()))>::type;
template <class Arg, class... S>
struct make_gexpr {
template <size_t... Is>
numpy_gexpr<Arg, normalize_t<S>...>
operator()(Arg arg, std::tuple<S...>, utils::index_sequence<Is...>);
numpy_gexpr<Arg, normalize_t<S>...> operator()(Arg arg, S const &... s);
};
// this specialization is in charge of merging gexpr
template <class Arg, class... S, class... Sp>
struct make_gexpr<numpy_gexpr<Arg, S...> const &, Sp...> {
auto operator()(numpy_gexpr<Arg, S...> const &arg, Sp const &... s)
-> decltype(
_make_gexpr(std::declval<Arg>(),
merge_gexpr<std::tuple<S...>, std::tuple<Sp...>>{}
.template run<0>(arg, std::tuple<S...>(),
std::tuple<Sp...>())))
{
return _make_gexpr(
arg.arg,
merge_gexpr<std::tuple<S...>, std::tuple<Sp...>>{}.template run<0>(
arg, arg.slices, std::make_tuple(s...)));
}
};
}
template <class Arg, class... S>
auto make_gexpr(Arg &&arg, S const &... s)
-> decltype(details::make_gexpr<Arg, S...>{}(std::forward<Arg>(arg),
s...));
/* type-based compile time overlapping detection: detect if a type may
*overlap with another
* the goal is to detect whether the following operation
*
* a[...] = b
*
* requires a copy.
*
* It requires a copy if b = a[...], as in
*
* a[1:] = a[:-1]
*
* because this is *!* equivalent to for i in range(0, n-1): a[i+1] = a[i]
*
* to avoid the copy, we rely on the lhs type
*/
template <class E>
struct may_overlap_gexpr : std::integral_constant<bool, !is_dtype<E>::value> {
};
template <class T0, class T1>
struct may_overlap_gexpr<broadcast<T0, T1>> : std::false_type {
};
template <class E>
struct may_overlap_gexpr<broadcasted<E>> : std::false_type {
};
template <class E>
struct may_overlap_gexpr<E &> : may_overlap_gexpr<E> {
};
template <class E>
struct may_overlap_gexpr<E const &> : may_overlap_gexpr<E> {
};
template <class T, class pS>
struct may_overlap_gexpr<ndarray<T, pS>> : std::false_type {
};
template <class E>
struct may_overlap_gexpr<numpy_iexpr<E>> : may_overlap_gexpr<E> {
};
template <class E>
struct may_overlap_gexpr<numpy_texpr<E>> : may_overlap_gexpr<E> {
};
template <class E>
struct may_overlap_gexpr<list<E>>
: std::integral_constant<bool, !is_dtype<E>::value> {
};
template <class E, size_t N, class V>
struct may_overlap_gexpr<array_base<E, N, V>> : may_overlap_gexpr<E> {
};
template <class Op, class... Args>
struct may_overlap_gexpr<numpy_expr<Op, Args...>>
: utils::any_of<may_overlap_gexpr<Args>::value...> {
};
template <class OpS, class pS, class... S>
struct gexpr_shape;
template <class... Tys, class... oTys>
struct gexpr_shape<pshape<Tys...>, pshape<oTys...>> {
using type = pshape<Tys..., oTys...>;
};
template <class... Tys>
struct gexpr_shape<pshape<Tys...>, array<long, 0>> {
using type = pshape<Tys...>;
};
template <class... Tys, size_t N>
struct gexpr_shape<pshape<Tys...>, array<long, N>>
: gexpr_shape<pshape<Tys..., long>, array<long, N - 1>> {
};
template <class... Tys, class... oTys, class... S>
struct gexpr_shape<pshape<Tys...>,
pshape<std::integral_constant<long, 1>, oTys...>,
contiguous_normalized_slice, S...>
: gexpr_shape<pshape<Tys..., std::integral_constant<long, 1>>,
pshape<oTys...>, S...> {
};
template <class... Tys, class... oTys, class... S>
struct gexpr_shape<pshape<Tys...>,
pshape<std::integral_constant<long, 1>, oTys...>,
normalized_slice, S...>
: gexpr_shape<pshape<Tys..., std::integral_constant<long, 1>>,
pshape<oTys...>, S...> {
};
template <class... Tys, class oT, class... oTys, class... S>
struct gexpr_shape<pshape<Tys...>, pshape<oT, oTys...>, long, S...>
: gexpr_shape<pshape<Tys...>, pshape<oTys...>, S...> {
};
template <class... Tys, class oT, class... oTys, class cS, class... S>
struct gexpr_shape<pshape<Tys...>, pshape<oT, oTys...>, cS, S...>
: gexpr_shape<pshape<Tys..., long>, pshape<oTys...>, S...> {
};
template <class... Tys, size_t N, class... S>
struct gexpr_shape<pshape<Tys...>, array<long, N>, long, S...>
: gexpr_shape<pshape<Tys...>, array<long, N - 1>, S...> {
};
template <class... Tys, size_t N, class cS, class... S>
struct gexpr_shape<pshape<Tys...>, array<long, N>, cS, S...>
: gexpr_shape<pshape<Tys..., long>, array<long, N - 1>, S...> {
};
template <class pS, class... S>
using gexpr_shape_t = typename gexpr_shape<pshape<>, pS, S...>::type;
/* Expression template for numpy expressions - extended slicing operators
*/
template <class Arg, class... S>
struct numpy_gexpr {
static_assert(
utils::all_of<std::is_same<S, normalize_t<S>>::value...>::value,
"all slices are normalized");
static_assert(
utils::all_of<(std::is_same<S, long>::value ||
std::is_same<S, contiguous_normalized_slice>::value ||
std::is_same<S, normalized_slice>::value)...>::value,
"all slices are valid");
static_assert(std::decay<Arg>::type::value >= sizeof...(S),
"slicing respects array shape");
// numpy_gexpr is a wrapper for extended sliced array around a numpy
// expression.
// It contains compacted sorted slices value in lower, step && upper is
// the same as shape.
// indices for long index are store in the indices array.
// position for slice and long value in the extended slice can be found
// through the S... template
// && compacted values as we know that first S is a slice.
static_assert(
utils::all_of<
std::is_same<S, typename std::decay<S>::type>::value...>::value,
"no modifiers on slices");
using dtype = typename std::remove_reference<Arg>::type::dtype;
static constexpr size_t value =
std::remove_reference<Arg>::type::value - count_long<S...>::value;
// It is not possible to vectorize everything. We only vectorize if the
// last dimension is contiguous, which happens if
// 1. Arg is an ndarray (this is too strict)
// 2. the size of the gexpr is lower than the dim of arg, || it's the
// same, but the last slice is contiguous
static const bool is_vectorizable =
std::remove_reference<Arg>::type::is_vectorizable &&
(sizeof...(S) < std::remove_reference<Arg>::type::value ||
std::is_same<contiguous_normalized_slice,
typename std::tuple_element<
sizeof...(S)-1, std::tuple<S...>>::type>::value);
static const bool is_strided =
std::remove_reference<Arg>::type::is_strided ||
(((sizeof...(S)-count_long<S...>::value) == value) &&
!std::is_same<contiguous_normalized_slice,
typename std::tuple_element<
sizeof...(S)-1, std::tuple<S...>>::type>::value);
using value_type = typename std::decay<decltype(
numpy_iexpr_helper<value>::get(std::declval<numpy_gexpr>(), 1))>::type;
using iterator =
typename std::conditional<is_strided || value != 1,
nditerator<numpy_gexpr>, dtype *>::type;
using const_iterator =
typename std::conditional<is_strided || value != 1,
const_nditerator<numpy_gexpr>,
dtype const *>::type;
typename std::remove_cv<Arg>::type arg;
std::tuple<S...> slices;
using shape_t =
gexpr_shape_t<typename std::remove_reference<Arg>::type::shape_t, S...>;
shape_t _shape;
dtype *buffer;
array<long, value> _strides;
template <size_t I>
auto shape() const -> decltype(std::get<I>(_shape))
{
return std::get<I>(_shape);
}
template <size_t I>
auto strides() const -> decltype(std::get<I>(_strides))
{
return std::get<I>(_strides);
}
numpy_gexpr();
numpy_gexpr(numpy_gexpr const &) = default;
numpy_gexpr(numpy_gexpr &&) = default;
template <class Argp> // ! using the default one, to make it possible to
// accept reference && non reference version of
// Argp
numpy_gexpr(numpy_gexpr<Argp, S...> const &other);
template <size_t J, class Slice>
typename std::enable_if<
std::is_same<Slice, normalized_slice>::value ||
std::is_same<Slice, contiguous_normalized_slice>::value,
void>::type
init_shape(Slice const &s, utils::int_<1>, utils::int_<J>);
template <size_t I, size_t J, class Slice>
typename std::enable_if<
std::is_same<Slice, normalized_slice>::value ||
std::is_same<Slice, contiguous_normalized_slice>::value,
void>::type
init_shape(Slice const &s, utils::int_<I>, utils::int_<J>);
template <size_t J>
void init_shape(long cs, utils::int_<1>, utils::int_<J>);
template <size_t I, size_t J>
void init_shape(long cs, utils::int_<I>, utils::int_<J>);
// private because we must use the make_gexpr factory to create a gexpr
private:
template <class _Arg, class... _other_classes>
friend struct details::make_gexpr;
friend struct array_base_slicer;
template <class _Arg, class... _other_classes>
friend
typename std::enable_if<count_new_axis<_other_classes...>::value == 0,
numpy_gexpr<_Arg, _other_classes...>>::type
details::_make_gexpr(_Arg arg, std::tuple<_other_classes...> const &t);
template <class _Arg, class _other_classes, size_t... Is>
friend numpy_gexpr<_Arg,
typename to_normalized_slice<typename std::tuple_element<
Is, _other_classes>::type>::type...>
details::_make_gexpr_helper(_Arg arg, _other_classes const &s,
utils::index_sequence<Is...>);
template <size_t C>
friend struct extended_slice;
#ifdef ENABLE_PYTHON_MODULE
template <typename T>
friend struct pythonic::from_python;
#endif
// When we create a new numpy_gexpr, we deduce step, lower && shape from
// slices
// && indices from long value.
// Also, last shape information are set from origin array like in :
// >>> a = numpy.arange(2*3*4).reshape(2,3,4)
// >>> a[:, 1]
// the last dimension (4) is missing from slice information
// Finally, if origin expression was already sliced, lower bound && step
// have to
// be increased
numpy_gexpr(Arg const &arg, std::tuple<S const &...> const &values);
numpy_gexpr(Arg const &arg, S const &... s);
public:
template <class Argp, class... Sp>
numpy_gexpr(numpy_gexpr<Argp, Sp...> const &expr, Arg arg);
template <class G>
numpy_gexpr(G const &expr, Arg &&arg);
template <class pS>
ndarray<dtype, pS> reshape(pS const &shape) const
{
return copy().reshape(shape);
}
template <class E>
typename std::enable_if<may_overlap_gexpr<E>::value, numpy_gexpr &>::type
_copy(E const &expr);
template <class E>
typename std::enable_if<!may_overlap_gexpr<E>::value, numpy_gexpr &>::type
_copy(E const &expr);
template <class E>
numpy_gexpr &operator=(E const &expr);
numpy_gexpr &operator=(numpy_gexpr const &expr);
template <class Argp>
numpy_gexpr &operator=(numpy_gexpr<Argp, S...> const &expr);
template <class Op, class E>
typename std::enable_if<may_overlap_gexpr<E>::value, numpy_gexpr &>::type
update_(E const &expr);
template <class Op, class E>
typename std::enable_if<!may_overlap_gexpr<E>::value, numpy_gexpr &>::type
update_(E const &expr);
template <class E>
numpy_gexpr &operator+=(E const &expr);
numpy_gexpr &operator+=(numpy_gexpr const &expr);
template <class E>
numpy_gexpr &operator-=(E const &expr);
numpy_gexpr &operator-=(numpy_gexpr const &expr);
template <class E>
numpy_gexpr &operator*=(E const &expr);
numpy_gexpr &operator*=(numpy_gexpr const &expr);
template <class E>
numpy_gexpr &operator/=(E const &expr);
numpy_gexpr &operator/=(numpy_gexpr const &expr);
template <class E>
numpy_gexpr &operator|=(E const &expr);
numpy_gexpr &operator|=(numpy_gexpr const &expr);
template <class E>
numpy_gexpr &operator&=(E const &expr);
numpy_gexpr &operator&=(numpy_gexpr const &expr);
template <class E>
numpy_gexpr &operator^=(E const &expr);
numpy_gexpr &operator^=(numpy_gexpr const &expr);
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
auto fast(long i) const
& -> decltype(numpy_iexpr_helper<value>::get(*this, i))
{
return numpy_iexpr_helper<value>::get(*this, i);
}
auto fast(long i) & -> decltype(numpy_iexpr_helper<value>::get(*this, i))
{
return numpy_iexpr_helper<value>::get(*this, i);
}
template <class E, class... Indices>
void store(E elt, Indices... indices)
{
static_assert(is_dtype<E>::value, "valid store");
*(buffer + noffset<value>{}(*this, array<long, value>{{indices...}})) =
static_cast<E>(elt);
}
template <class... Indices>
dtype load(Indices... indices) const
{
return *(buffer +
noffset<value>{}(*this, array<long, value>{{indices...}}));
}
template <class Op, class E, class... Indices>
void update(E elt, Indices... indices) const
{
static_assert(is_dtype<E>::value, "valid store");
Op{}(
*(buffer + noffset<value>{}(*this, array<long, value>{{indices...}})),
static_cast<E>(elt));
}
#ifdef USE_XSIMD
using simd_iterator = const_simd_nditerator<numpy_gexpr>;
using simd_iterator_nobroadcast = simd_iterator;
template <class vectorizer>
simd_iterator vbegin(vectorizer) const;
template <class vectorizer>
simd_iterator vend(vectorizer) const;
#endif
template <class... Sp>
auto operator()(Sp const &... s) const -> decltype(make_gexpr(*this, s...));
template <class Sp>
auto operator[](Sp const &s) const -> typename std::enable_if<
is_slice<Sp>::value, decltype(make_gexpr(*this, (s.lower, s)))>::type;
template <size_t M>
auto fast(array<long, M> const &indices) const
& -> decltype(nget<M - 1>().fast(*this, indices));
template <size_t M>
auto fast(array<long, M> const &indices) &&
-> decltype(nget<M - 1>().fast(std::move(*this), indices));
template <size_t M>
auto operator[](array<long, M> const &indices) const
& -> decltype(nget<M - 1>()(*this, indices));
template <size_t M>
auto operator[](array<long, M> const &indices) &&
-> decltype(nget<M - 1>()(std::move(*this), indices));
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,
numpy_vexpr<numpy_gexpr, F>>::type
operator[](F const &filter) const
{
return {*this, filter};
}
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,
numpy_vexpr<numpy_gexpr, F>>::type
fast(F const &filter) const
{
return {*this, filter};
}
template <class F>
typename std::enable_if<
is_numexpr_arg<F>::value &&
std::is_same<bool, typename F::dtype>::value,
numpy_vexpr<numpy_gexpr, 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,
numpy_vexpr<numpy_gexpr, ndarray<long, pshape<long>>>>::type
operator[](F const &filter) const;
auto operator[](long i) const -> decltype(this->fast(i));
auto operator[](long i) -> decltype(this->fast(i));
// template <class... Sp>
// auto operator()(long i, Sp const &... s) const
// -> decltype((*this)[i](s...));
explicit operator bool() const;
long flat_size() const;
long size() const;
ndarray<dtype, shape_t> copy() const
{
return {*this};
}
};
}
template <class E, class... S>
struct assignable_noescape<types::numpy_gexpr<E, S...>> {
using type = types::numpy_gexpr<E, S...>;
};
template <class T, class pS, class... S>
struct assignable<types::numpy_gexpr<types::ndarray<T, pS> const &, S...>> {
using type = types::numpy_gexpr<types::ndarray<T, pS>, S...>;
};
template <class T, class pS, class... S>
struct assignable<types::numpy_gexpr<types::ndarray<T, pS> &, S...>> {
using type = types::numpy_gexpr<types::ndarray<T, pS>, S...>;
};
template <class Arg, class... S>
struct assignable<types::numpy_gexpr<Arg, S...>> {
using type = types::numpy_gexpr<typename assignable<Arg>::type, S...>;
};
template <class Arg, class... S>
struct lazy<types::numpy_gexpr<Arg, S...>>
: assignable<types::numpy_gexpr<Arg, S...>> {
};
PYTHONIC_NS_END
/* type inference stuff {*/
#include "pythonic/include/types/combined.hpp"
template <class Arg, class... S>
struct __combined<pythonic::types::numpy_gexpr<Arg, S...>,
pythonic::types::numpy_gexpr<Arg, S...>> {
using type = pythonic::types::numpy_gexpr<Arg, S...>;
};
template <class Arg, class... S, class Argp, class... Sp>
struct __combined<pythonic::types::numpy_gexpr<Arg, S...>,
pythonic::types::numpy_gexpr<Argp, Sp...>> {
using t0 = pythonic::types::numpy_gexpr<Arg, S...>;
using t1 = pythonic::types::numpy_gexpr<Argp, Sp...>;
using type =
pythonic::types::ndarray <
typename __combined<typename t0::dtype, typename t1::dtype>::type,
pythonic::types::array < long,
t0::value < t1::value ? t1::value : t0::value >>
;
};
template <class Arg, class... S, class O>
struct __combined<pythonic::types::numpy_gexpr<Arg, S...>, O> {
using type = pythonic::types::numpy_gexpr<Arg, S...>;
};
template <class Arg, class... S, class T>
struct __combined<pythonic::types::list<T>,
pythonic::types::numpy_gexpr<Arg, S...>> {
using type = pythonic::types::list<typename __combined<
typename pythonic::types::numpy_gexpr<Arg, S...>::value_type, T>::type>;
};
template <class Arg, class... S, class T>
struct __combined<pythonic::types::numpy_gexpr<Arg, S...>,
pythonic::types::list<T>> {
using type = pythonic::types::list<typename __combined<
typename pythonic::types::numpy_gexpr<Arg, S...>::value_type, T>::type>;
};
template <class Arg, class... S>
struct __combined<pythonic::types::numpy_gexpr<Arg, S...>,
pythonic::types::none_type> {
using type = pythonic::types::none<pythonic::types::numpy_gexpr<Arg, S...>>;
};
template <class Arg, class... S, class O>
struct __combined<O, pythonic::types::numpy_gexpr<Arg, S...>> {
using type = pythonic::types::numpy_gexpr<Arg, S...>;
};
template <class Arg, class... S>
struct __combined<pythonic::types::none_type,
pythonic::types::numpy_gexpr<Arg, S...>> {
using type = pythonic::types::none<pythonic::types::numpy_gexpr<Arg, S...>>;
};
/* combined are sorted such that the assigned type comes first */
template <class Arg, class... S, class T, class pS>
struct __combined<pythonic::types::numpy_gexpr<Arg, S...>,
pythonic::types::ndarray<T, pS>> {
using type = pythonic::types::ndarray<T, pS>;
};
template <class Arg, class... S, class T, class pS>
struct __combined<pythonic::types::ndarray<T, pS>,
pythonic::types::numpy_gexpr<Arg, S...>> {
using type = pythonic::types::ndarray<T, pS>;
};
#endif