| 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 : /usr/lib/python3/dist-packages/pythran/pythonic/types/ |
Upload File : |
#ifndef PYTHONIC_TYPES_NUMPY_GEXPR_HPP
#define PYTHONIC_TYPES_NUMPY_GEXPR_HPP
#include "pythonic/include/types/numpy_gexpr.hpp"
#include "pythonic/builtins/ValueError.hpp"
#include "pythonic/utils/meta.hpp"
#include "pythonic/operator_/iadd.hpp"
#include "pythonic/operator_/isub.hpp"
#include "pythonic/operator_/imul.hpp"
#include "pythonic/operator_/idiv.hpp"
PYTHONIC_NS_BEGIN
namespace types
{
template <class S0, class S1>
bool slices_may_overlap(S0 const &s0, S1 const &s1)
{
if (s0.step >= 0 && s1.step >= 0)
return s0.lower > s1.lower;
else
return s0.lower < s1.lower;
}
template <class S>
bool slices_may_overlap(S const &s, long const &i)
{
return s.lower <= i && i < s.upper;
}
template <class S>
bool slices_may_overlap(long const &i, S const &s)
{
return s.lower <= i && i < s.upper;
}
template <class E0, class E1>
bool may_overlap(E0 const &, E1 const &)
{
return true;
}
template <class E0, class T0, class T1>
bool may_overlap(E0 const &, broadcast<T0, T1> const &)
{
return false;
}
template <class Arg, class E1, class... S>
typename std::enable_if<std::is_scalar<E1>::value, bool>::type
may_overlap(numpy_gexpr<Arg, S...> const &gexpr, E1 const &)
{
return false;
}
template <class Arg, class Tuple, class... S, size_t... I>
bool may_overlap_helper(numpy_gexpr<Arg, S...> const &gexpr,
Tuple const &args, utils::index_sequence<I...>)
{
bool overlaps[] = {may_overlap(gexpr, std::get<I>(args))...};
return std::any_of(std::begin(overlaps), std::end(overlaps),
[](bool b) { return b; });
}
template <class Arg, class... E, class... S>
bool may_overlap(numpy_gexpr<Arg, S...> const &gexpr,
numpy_expr<E...> const &expr)
{
return may_overlap_helper(gexpr, expr.args,
utils::make_index_sequence<sizeof...(E)-1>{});
}
template <class T, class pS, class Tp, class pSp, class E0, class E1>
bool may_gexpr_overlap(E0 const &gexpr, E1 const &expr)
{
if (!std::is_same<T, Tp>::value) {
return false;
}
if (std::tuple_size<pS>::value != std::tuple_size<pSp>::value) {
return false;
}
if (gexpr.arg.id() != expr.arg.id()) {
return false;
}
if (!slices_may_overlap(std::get<0>(gexpr.slices),
std::get<0>(expr.slices)))
return false;
return true;
}
template <class T, class pS, class Tp, class pSp, class... S, class... Sp>
bool may_overlap(numpy_gexpr<ndarray<T, pS> const &, S...> const &gexpr,
numpy_gexpr<ndarray<Tp, pSp> const &, Sp...> const &expr)
{
return may_gexpr_overlap<T, pS, Tp, pSp>(gexpr, expr);
}
template <class T, class pS, class Tp, class pSp, class... S, class... Sp>
bool may_overlap(numpy_gexpr<ndarray<T, pS> &, S...> const &gexpr,
numpy_gexpr<ndarray<Tp, pSp> &, Sp...> const &expr)
{
return may_gexpr_overlap<T, pS, Tp, pSp>(gexpr, expr);
}
template <class T, class pS, class Tp, class pSp, class... S, class... Sp>
bool may_overlap(numpy_gexpr<ndarray<T, pS> &, S...> const &gexpr,
numpy_gexpr<ndarray<Tp, pSp> const &, Sp...> const &expr)
{
return may_gexpr_overlap<T, pS, Tp, pSp>(gexpr, expr);
}
template <class T, class pS, class Tp, class pSp, class... S, class... Sp>
bool may_overlap(numpy_gexpr<ndarray<T, pS> const &, S...> const &gexpr,
numpy_gexpr<ndarray<Tp, pSp> &, Sp...> const &expr)
{
return may_gexpr_overlap<T, pS, Tp, pSp>(gexpr, expr);
}
template <class T>
T to_slice<T>::operator()(T value)
{
return value;
}
fast_contiguous_slice to_slice<none_type>::operator()(none_type)
{
return {0, 1};
}
template <class T>
T to_normalized_slice<T>::operator()(T value)
{
return value;
}
contiguous_normalized_slice to_normalized_slice<none_type>::
operator()(none_type)
{
return {0, 1};
}
/* 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))
{
return sutils::copy_new_axis<pS::value + N>(shape, is_new_axis);
}
/* helper to build an extended slice aka numpy_gexpr out of a subscript
*/
namespace details
{
template <size_t I, class S>
std::tuple<> merge_gexpr<std::tuple<>, std::tuple<>>::run(
S const &, std::tuple<> const &t0, std::tuple<> const &)
{
return t0;
}
template <class... T0>
template <size_t I, class S>
std::tuple<T0...> merge_gexpr<std::tuple<T0...>, std::tuple<>>::run(
S const &, std::tuple<T0...> const &t0, std::tuple<>)
{
return t0;
}
template <class T, size_t... Is>
constexpr long count_new_axis_helper(utils::index_sequence<Is...>)
{
return count_new_axis<typename std::tuple_element<Is, T>::type...>::value;
}
template <size_t I, class S, class T, size_t... Is>
auto normalize_all(S const &s, T const &t, utils::index_sequence<Is...>)
-> decltype(std::make_tuple(normalize(
std::get<Is>(t),
s.template shape<I + Is -
count_new_axis_helper<T>(
utils::make_index_sequence<1 + Is>())>())...))
{
return std::make_tuple(normalize(
std::get<Is>(t),
s.template shape<I + Is -
count_new_axis_helper<T>(
utils::make_index_sequence<1 + Is>())>())...);
}
template <class... T1>
template <size_t I, class S>
std::tuple<normalize_t<T1>...>
merge_gexpr<std::tuple<>, std::tuple<T1...>>::run(
S const &s, std::tuple<>, std::tuple<T1...> const &t1)
{
return normalize_all<I>(s, t1,
utils::make_index_sequence<sizeof...(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)
{
return {arg, 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...>)
{
return {arg,
to_normalized_slice<typename std::tuple_element<Is, S>::type>{}(
std::get<Is>(s))...};
}
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
{
return _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)>());
}
template <class Arg, class... S>
template <size_t... Is>
numpy_gexpr<Arg, normalize_t<S>...> make_gexpr<Arg, S...>::
operator()(Arg arg, std::tuple<S...> s, utils::index_sequence<Is...>)
{
return {arg, normalize(std::get<Is>(s), arg.template shape<Is>())...};
}
template <class Arg, class... S>
numpy_gexpr<Arg, normalize_t<S>...> make_gexpr<Arg, S...>::
operator()(Arg arg, S const &... s)
{
return operator()(arg, std::tuple<S...>(s...),
utils::make_index_sequence<sizeof...(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...))
{
return details::make_gexpr<Arg, S...>{}(std::forward<Arg>(arg), s...);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...>::numpy_gexpr()
: buffer(nullptr)
{
}
template <class Arg, class... S>
template <class Argp> // not using the default one, to make it possible to
// accept reference && non reference version of Argp
numpy_gexpr<Arg, S...>::numpy_gexpr(numpy_gexpr<Argp, S...> const &other)
: arg(other.arg), slices(other.slices), _shape(other._shape),
buffer(other.buffer), _strides(other._strides)
{
static_assert(std::is_same<typename returnable<Arg>::type,
typename returnable<Argp>::type>::value,
"this constructor is only here to adapt reference / non "
"reference type, nothing else");
assert(buffer);
}
template <class Arg, class... S>
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
numpy_gexpr<Arg, S...>::init_shape(Slice const &s, utils::int_<1>,
utils::int_<J>)
{
buffer += s.lower * arg.template strides<sizeof...(S)-1>();
sutils::assign(std::get<J>(_strides),
s.step * arg.template strides<sizeof...(S)-1>());
sutils::assign(std::get<J>(_shape),
std::get<sizeof...(S)-1>(slices).size());
}
template <class Arg, class... S>
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
numpy_gexpr<Arg, S...>::init_shape(Slice const &s, utils::int_<I>,
utils::int_<J>)
{
sutils::assign(std::get<J>(_shape),
std::get<sizeof...(S)-I>(slices).size());
buffer += s.lower * arg.template strides<sizeof...(S)-I>();
sutils::assign(std::get<J>(_strides),
s.step * arg.template strides<sizeof...(S)-I>());
init_shape(std::get<sizeof...(S)-I + 1>(slices), utils::int_<I - 1>(),
utils::int_<J + 1>());
}
template <class Arg, class... S>
template <size_t J>
void numpy_gexpr<Arg, S...>::init_shape(long cs, utils::int_<1>,
utils::int_<J>)
{
assert(cs >= 0 && "normalized");
buffer += cs * arg.template strides<sizeof...(S)-1>();
}
template <class Arg, class... S>
template <size_t I, size_t J>
void numpy_gexpr<Arg, S...>::init_shape(long cs, utils::int_<I>,
utils::int_<J>)
{
assert(cs >= 0 && "normalized");
buffer += cs * arg.template strides<sizeof...(S)-I>();
init_shape(std::get<sizeof...(S)-I + 1>(slices), utils::int_<I - 1>(),
utils::int_<J>());
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...>::numpy_gexpr(Arg const &arg,
std::tuple<S const &...> const &values)
: arg(arg), slices(values), buffer(const_cast<dtype *>(this->arg.buffer))
{
assert(buffer);
init_shape(std::get<0>(slices), utils::int_<sizeof...(S)>(),
utils::int_<0>());
sutils::copy_shape<sizeof...(S)-count_long<S...>::value,
count_long<S...>::value>(
_shape, arg,
utils::make_index_sequence<value -
(sizeof...(S)-count_long<S...>::value)>());
sutils::copy_strides<sizeof...(S)-count_long<S...>::value,
count_long<S...>::value>(
_strides, arg,
utils::make_index_sequence<value -
(sizeof...(S)-count_long<S...>::value)>());
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...>::numpy_gexpr(Arg const &arg, S const &... s)
: numpy_gexpr(arg, std::tuple<S const &...>(s...))
{
}
template <class Arg, class... S>
template <class Argp, class... Sp>
numpy_gexpr<Arg, S...>::numpy_gexpr(numpy_gexpr<Argp, Sp...> const &expr,
Arg arg)
: arg(arg), slices(tuple_pop(expr.slices)), buffer(expr.buffer)
{
assert(buffer);
sutils::copy_shape<0, 1>(_shape, expr, utils::make_index_sequence<value>());
buffer += arg.buffer - expr.arg.buffer;
sutils::copy_strides<0, 1>(_strides, expr,
utils::make_index_sequence<value>());
}
template <class Arg, class... S>
template <class G>
numpy_gexpr<Arg, S...>::numpy_gexpr(G const &expr, Arg &&arg)
: arg(std::forward<Arg>(arg)), slices(tuple_pop(expr.slices)),
buffer(expr.buffer)
{
assert(buffer);
sutils::copy_shape<0, 1>(_shape, expr, utils::make_index_sequence<value>());
buffer += (arg.buffer - expr.arg.buffer);
sutils::copy_strides<0, 1>(_strides, expr,
utils::make_index_sequence<value>());
}
template <class Arg, class... S>
template <class E>
typename std::enable_if<may_overlap_gexpr<E>::value,
numpy_gexpr<Arg, S...> &>::type
numpy_gexpr<Arg, S...>::_copy(E const &expr)
{
static_assert(value >= utils::dim_of<E>::value, "dimensions match");
/* at this point, we could not statically check that there is not an
* aliasing issue that would require an extra copy because of the vector
* assignment
* perform a fuzzy alias check dynamically!
*/
assert(buffer);
constexpr bool vectorize =
is_vectorizable &&
std::is_same<dtype, typename dtype_of<E>::type>::value &&
is_vectorizable_array<E>::value;
if (may_overlap(*this, expr)) {
return utils::broadcast_copy<
numpy_gexpr &, ndarray<typename E::dtype, typename E::shape_t>, value,
value - utils::dim_of<E>::value, vectorize>(
*this, ndarray<typename E::dtype, typename E::shape_t>(expr));
} else {
// 100% sure there's no overlap
return utils::broadcast_copy<numpy_gexpr &, E, value,
value - utils::dim_of<E>::value, vectorize>(
*this, expr);
}
}
template <class Arg, class... S>
template <class E>
typename std::enable_if<!may_overlap_gexpr<E>::value,
numpy_gexpr<Arg, S...> &>::type
numpy_gexpr<Arg, S...>::_copy(E const &expr)
{
constexpr bool vectorize =
is_vectorizable &&
std::is_same<dtype, typename dtype_of<E>::type>::value &&
is_vectorizable_array<E>::value;
static_assert(value >= utils::dim_of<E>::value, "dimensions match");
assert(buffer);
return utils::broadcast_copy<numpy_gexpr &, E, value,
value - utils::dim_of<E>::value, vectorize>(
*this, expr);
}
template <class Arg, class... S>
template <class E>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::operator=(E const &expr)
{
return _copy(expr);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::
operator=(numpy_gexpr<Arg, S...> const &expr)
{
if (buffer == nullptr) {
// arg = expr.arg;
const_cast<typename std::decay<Arg>::type &>(arg) = expr.arg;
slices = expr.slices;
assert(expr.buffer);
buffer = arg.buffer + (expr.buffer - expr.arg.buffer);
_shape = expr._shape;
_strides = expr._strides;
assert(sutils::getshape(*this) == sutils::getshape(expr) &&
"compatible sizes");
return *this;
} else {
return _copy(expr);
}
}
template <class Arg, class... S>
template <class Argp>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::
operator=(numpy_gexpr<Argp, S...> const &expr)
{
if (buffer == nullptr) {
// arg = expr.arg;
const_cast<typename std::decay<Arg>::type &>(arg) = expr.arg;
slices = expr.slices;
assert(expr.buffer);
buffer = arg.buffer + (expr.buffer - expr.arg.buffer);
_shape = expr._shape;
_strides = expr._strides;
return *this;
} else {
return _copy(expr);
}
}
template <class Arg, class... S>
template <class Op, class E>
typename std::enable_if<!may_overlap_gexpr<E>::value,
numpy_gexpr<Arg, S...> &>::type
numpy_gexpr<Arg, S...>::update_(E const &expr)
{
using BExpr =
typename std::conditional<std::is_scalar<E>::value, broadcast<E, dtype>,
E const &>::type;
BExpr bexpr = expr;
// 100% sure there's no overlap
return utils::broadcast_update < Op, numpy_gexpr &, BExpr, value,
value - (std::is_scalar<E>::value + utils::dim_of<E>::value),
is_vectorizable &&
types::is_vectorizable<typename std::remove_cv<
typename std::remove_reference<BExpr>::type>::type>::value &&
std::is_same<dtype, typename dtype_of<typename std::decay<
BExpr>::type>::type>::value >
(*this, bexpr);
}
template <class Arg, class... S>
template <class Op, class E>
typename std::enable_if<may_overlap_gexpr<E>::value,
numpy_gexpr<Arg, S...> &>::type
numpy_gexpr<Arg, S...>::update_(E const &expr)
{
using BExpr =
typename std::conditional<std::is_scalar<E>::value, broadcast<E, dtype>,
E const &>::type;
BExpr bexpr = expr;
if (may_overlap(*this, expr)) {
using NBExpr =
ndarray<typename std::remove_reference<BExpr>::type::dtype,
typename std::remove_reference<BExpr>::type::shape_t>;
return utils::broadcast_update < Op, numpy_gexpr &, NBExpr, value,
value - (std::is_scalar<E>::value + utils::dim_of<E>::value),
is_vectorizable && types::is_vectorizable<E>::value &&
std::is_same<dtype,
typename std::decay<BExpr>::type::dtype>::value >
(*this, NBExpr(bexpr));
} else {
// 100% sure there's no overlap
return utils::broadcast_update < Op, numpy_gexpr &, BExpr, value,
value - (std::is_scalar<E>::value + utils::dim_of<E>::value),
is_vectorizable && types::is_vectorizable<E>::value &&
std::is_same<dtype,
typename std::decay<BExpr>::type::dtype>::value >
(*this, bexpr);
}
}
template <class Arg, class... S>
template <class E>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::operator+=(E const &expr)
{
return update_<pythonic::operator_::functor::iadd>(expr);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::
operator+=(numpy_gexpr<Arg, S...> const &expr)
{
return update_<pythonic::operator_::functor::iadd>(expr);
}
template <class Arg, class... S>
template <class E>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::operator-=(E const &expr)
{
return update_<pythonic::operator_::functor::isub>(expr);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::
operator-=(numpy_gexpr<Arg, S...> const &expr)
{
return update_<pythonic::operator_::functor::isub>(expr);
}
template <class Arg, class... S>
template <class E>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::operator*=(E const &expr)
{
return update_<pythonic::operator_::functor::imul>(expr);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::
operator*=(numpy_gexpr<Arg, S...> const &expr)
{
return update_<pythonic::operator_::functor::imul>(expr);
}
template <class Arg, class... S>
template <class E>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::operator/=(E const &expr)
{
return update_<pythonic::operator_::functor::idiv>(expr);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::
operator/=(numpy_gexpr<Arg, S...> const &expr)
{
return update_<pythonic::operator_::functor::idiv>(expr);
}
template <class Arg, class... S>
template <class E>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::operator|=(E const &expr)
{
return update_<pythonic::operator_::functor::ior>(expr);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::
operator|=(numpy_gexpr<Arg, S...> const &expr)
{
return update_<pythonic::operator_::functor::ior>(expr);
}
template <class Arg, class... S>
template <class E>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::operator&=(E const &expr)
{
return update_<pythonic::operator_::functor::iand>(expr);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::
operator&=(numpy_gexpr<Arg, S...> const &expr)
{
return update_<pythonic::operator_::functor::iand>(expr);
}
template <class Arg, class... S>
template <class E>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::operator^=(E const &expr)
{
return update_<pythonic::operator_::functor::ixor>(expr);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...> &numpy_gexpr<Arg, S...>::
operator^=(numpy_gexpr<Arg, S...> const &expr)
{
return update_<pythonic::operator_::functor::ixor>(expr);
}
template <class Arg, class... S>
typename numpy_gexpr<Arg, S...>::const_iterator
numpy_gexpr<Arg, S...>::begin() const
{
return make_const_nditerator < is_strided || value != 1 > ()(*this, 0);
}
template <class Arg, class... S>
typename numpy_gexpr<Arg, S...>::const_iterator
numpy_gexpr<Arg, S...>::end() const
{
return make_const_nditerator < is_strided || value != 1 > ()(*this, size());
}
template <class Arg, class... S>
typename numpy_gexpr<Arg, S...>::iterator numpy_gexpr<Arg, S...>::begin()
{
return make_nditerator < is_strided || value != 1 > ()(*this, 0);
}
template <class Arg, class... S>
typename numpy_gexpr<Arg, S...>::iterator numpy_gexpr<Arg, S...>::end()
{
return make_nditerator < is_strided || value != 1 > ()(*this, size());
}
#ifdef USE_XSIMD
template <class Arg, class... S>
template <class vectorizer>
typename numpy_gexpr<Arg, S...>::simd_iterator
numpy_gexpr<Arg, S...>::vbegin(vectorizer) const
{
return {buffer};
}
template <class Arg, class... S>
template <class vectorizer>
typename numpy_gexpr<Arg, S...>::simd_iterator
numpy_gexpr<Arg, S...>::vend(vectorizer) const
{
using vector_type = typename xsimd::batch<dtype>;
static const std::size_t vector_size = vector_type::size;
return {buffer + long(size() / vector_size * vector_size)};
}
#endif
template <class Arg, class... S>
auto numpy_gexpr<Arg, S...>::operator[](long i) const
-> decltype(this->fast(i))
{
if (i < 0)
i += std::get<0>(_shape);
return fast(i);
}
template <class Arg, class... S>
auto numpy_gexpr<Arg, S...>::operator[](long i) -> decltype(this->fast(i))
{
if (i < 0)
i += std::get<0>(_shape);
return fast(i);
}
template <class Arg, class... S>
template <class... Sp>
auto numpy_gexpr<Arg, S...>::operator()(Sp const &... s) const
-> decltype(make_gexpr(*this, s...))
{
return make_gexpr(*this, s...);
}
template <class Arg, class... S>
template <class Sp>
auto numpy_gexpr<Arg, S...>::operator[](Sp const &s) const ->
typename std::enable_if<is_slice<Sp>::value,
decltype(make_gexpr(*this, (s.lower, s)))>::type
{
return make_gexpr(*this, s);
}
template <class Arg, class... S>
template <size_t M>
auto numpy_gexpr<Arg, S...>::fast(array<long, M> const &indices) const
& -> decltype(nget<M - 1>().fast(*this, indices))
{
return nget<M - 1>().fast(*this, indices);
}
template <class Arg, class... S>
template <size_t M>
auto numpy_gexpr<Arg, S...>::fast(array<long, M> const &indices) &&
-> decltype(nget<M - 1>().fast(std::move(*this), indices))
{
return nget<M - 1>().fast(std::move(*this), indices);
}
template <class Arg, class... S>
template <size_t M>
auto numpy_gexpr<Arg, S...>::operator[](array<long, M> const &indices) const
& -> decltype(nget<M - 1>()(*this, indices))
{
return nget<M - 1>()(*this, indices);
}
template <class Arg, class... S>
template <size_t M>
auto numpy_gexpr<Arg, S...>::operator[](array<long, M> const &indices) &&
-> decltype(nget<M - 1>()(std::move(*this), indices))
{
return nget<M - 1>()(std::move(*this), indices);
}
template <class Arg, class... S>
template <class F>
typename std::enable_if<
is_numexpr_arg<F>::value && std::is_same<bool, typename F::dtype>::value,
numpy_vexpr<numpy_gexpr<Arg, S...>, ndarray<long, pshape<long>>>>::type
numpy_gexpr<Arg, S...>::fast(F const &filter) const
{
long sz = filter.template shape<0>();
long *raw = (long *)malloc(sz * sizeof(long));
long n = 0;
for (long i = 0; i < sz; ++i)
if (filter.fast(i))
raw[n++] = i;
// realloc(raw, n * sizeof(long));
long shp[1] = {n};
return this->fast(
ndarray<long, pshape<long>>(raw, shp, types::ownership::owned));
}
template <class Arg, class... S>
template <class F>
typename std::enable_if<
is_numexpr_arg<F>::value && std::is_same<bool, typename F::dtype>::value,
numpy_vexpr<numpy_gexpr<Arg, S...>, ndarray<long, pshape<long>>>>::type
numpy_gexpr<Arg, S...>::
operator[](F const &filter) const
{
return fast(filter);
}
template <class Arg, class... S>
numpy_gexpr<Arg, S...>::operator bool() const
{
if (sutils::any_of(*this, [](long n) { return n != 1; }))
throw ValueError("The truth value of an array with more than one element "
"is ambiguous. Use a.any() or a.all()");
return *buffer;
}
template <class Arg, class... S>
long numpy_gexpr<Arg, S...>::flat_size() const
{
return sutils::prod(*this);
}
template <class Arg, class... S>
long numpy_gexpr<Arg, S...>::size() const
{
return std::get<0>(_shape);
}
}
PYTHONIC_NS_END
#endif