From 7364613cb2a955211e3974e39f04e81e35dbb71f Mon Sep 17 00:00:00 2001 From: serge-sans-paille Date: Sun, 25 Aug 2024 08:31:10 +0200 Subject: [PATCH] array module support: no longer use an alias for array type Finally fix #2226 --- pythran/pythonic/array/array.hpp | 2 +- pythran/pythonic/array/array/frombytes.hpp | 2 +- pythran/pythonic/array/array/fromfile.hpp | 2 +- .../pythonic/include/__dispatch__/tolist.hpp | 20 +- pythran/pythonic/include/types/array.hpp | 645 ++++++++++++++- .../include/types/numpy_op_helper.hpp | 6 + .../include/utils/nested_container.hpp | 24 + .../pythonic/include/utils/numpy_traits.hpp | 19 +- pythran/pythonic/types/array.hpp | 749 ++++++++++++++++++ pythran/tests/test_array.py | 182 ++++- 10 files changed, 1603 insertions(+), 48 deletions(-) diff --git a/pythran/pythonic/array/array.hpp b/pythran/pythonic/array/array.hpp index 688f18ab4..6edf43770 100644 --- a/pythran/pythonic/array/array.hpp +++ b/pythran/pythonic/array/array.hpp @@ -17,7 +17,7 @@ namespace array types::array::type> array(std::integral_constant) { - return types::empty_list{}; + return {0}; } template diff --git a/pythran/pythonic/array/array/frombytes.hpp b/pythran/pythonic/array/array/frombytes.hpp index f9798bac0..a42701531 100644 --- a/pythran/pythonic/array/array/frombytes.hpp +++ b/pythran/pythonic/array/array/frombytes.hpp @@ -18,7 +18,7 @@ namespace array { long size = seq.size(); seq.resize(size + s.size() / sizeof(T)); - memcpy(&*seq.begin() + size, s.c_str(), s.size()); + memcpy(seq.data() + size, s.c_str(), s.size()); return {}; } diff --git a/pythran/pythonic/array/array/fromfile.hpp b/pythran/pythonic/array/array/fromfile.hpp index 63551528e..1c4151d9a 100644 --- a/pythran/pythonic/array/array/fromfile.hpp +++ b/pythran/pythonic/array/array/fromfile.hpp @@ -17,7 +17,7 @@ namespace array { long p = seq.size(); seq.resize(p + n); - f.read_as(n, &*seq.begin() + p); + f.read_as(n, seq.data() + p); return {}; } diff --git a/pythran/pythonic/include/__dispatch__/tolist.hpp b/pythran/pythonic/include/__dispatch__/tolist.hpp index 613a37def..199ee3fc9 100644 --- a/pythran/pythonic/include/__dispatch__/tolist.hpp +++ b/pythran/pythonic/include/__dispatch__/tolist.hpp @@ -14,12 +14,28 @@ namespace __dispatch__ return numpy::ndarray::tolist(any); } + template + types::list< + typename std::conditional::value, long, double>::type> + tolist(types::sliced_array &&a) + { + return {a.begin(), a.end()}; + } + + template + types::list< + typename std::conditional::value, long, double>::type> + tolist(types::sliced_array const &a) + { + return {a.begin(), a.end()}; + } + template types::list< typename std::conditional::value, long, double>::type> tolist(types::array &&a) { - return a; + return {a.begin(), a.end()}; } template @@ -27,7 +43,7 @@ namespace __dispatch__ typename std::conditional::value, long, double>::type> tolist(types::array const &a) { - return a; + return {a.begin(), a.end()}; } DEFINE_FUNCTOR(pythonic::__dispatch__, tolist); diff --git a/pythran/pythonic/include/types/array.hpp b/pythran/pythonic/include/types/array.hpp index 0f2981709..d4e6c1e06 100644 --- a/pythran/pythonic/include/types/array.hpp +++ b/pythran/pythonic/include/types/array.hpp @@ -1,17 +1,660 @@ #ifndef PYTHONIC_INCLUDE_TYPES_ARRAY_HPP #define PYTHONIC_INCLUDE_TYPES_ARRAY_HPP +#include "pythonic/include/types/assignable.hpp" +#include "pythonic/include/types/empty_iterator.hpp" #include "pythonic/include/types/list.hpp" +#include "pythonic/include/types/nditerator.hpp" +#include "pythonic/include/types/slice.hpp" +#include "pythonic/include/types/tuple.hpp" +#include "pythonic/include/types/vectorizable_type.hpp" +#include "pythonic/include/utils/allocate.hpp" +#include "pythonic/include/utils/int_.hpp" +#include "pythonic/include/utils/reserve.hpp" +#include "pythonic/include/utils/shared_ref.hpp" + +#include +#include +#include +#include +#include PYTHONIC_NS_BEGIN namespace types { template - using array = list; + using container = std::vector>; + + /* forward declaration */ + template + class array; + template + class sliced_array; + template + struct ndarray; + template + struct pshape; + + template + struct array_reference { + typename E::data_type &data; + array_reference(typename E::data_type &data) : data(data) + { + } + array_reference &operator=(typename E::value_type value) + { + data = value; + return *this; + } + array_reference &operator=(array_reference value) + { + data = value.data; + return *this; + } + + operator typename E::value_type() const + { + return data; + } + + friend void swap(array_reference self, array_reference other) + { + std::swap(self.data, other.data); + } + }; + + template + struct array_iterator + : std::iterator { + E data; + long index; + array_iterator(E data, long index) : data(data), index(index) + { + } + + array_reference operator*() + { + return data.fast(index); + } + auto operator*() const -> decltype(data.fast(index)) + { + return data.fast(index); + } + array_iterator &operator++() + { + ++index; + return *this; + } + array_iterator &operator--() + { + --index; + return *this; + } + array_iterator &operator+=(long i) + { + index += i; + return *this; + } + array_iterator &operator-=(long i) + { + index -= i; + return *this; + } + array_iterator operator+(long i) const + { + array_iterator res(*this); + res += i; + return res; + } + array_iterator operator-(long i) const + { + array_iterator res(*this); + res -= i; + return res; + } + long operator-(array_iterator const &other) const + { + return index - other.index; + } + bool operator!=(array_iterator const &other) const + { + return index != other.index; + } + bool operator==(array_iterator const &other) const + { + return index == other.index; + } + bool operator<(array_iterator const &other) const + { + return index < other.index; + } + array_iterator &operator=(array_iterator const &other) + { + index = other.index; + return *this; + } + }; + + /* array view */ + template + class sliced_array + { + + // data holder + typedef + typename std::remove_cv::type>::type + _type; + typedef container<_type> container_type; + utils::shared_ref _data; + + template + friend class array; + + typename S::normalized_type slicing; + + public: + // types + typedef T data_type; + typedef typename std::conditional::value, long, + double>::type value_type; + typedef array_reference reference; + typedef value_type const_reference; + typedef array_iterator iterator; + typedef array_iterator const_iterator; + typedef typename container_type::size_type size_type; + typedef typename container_type::difference_type difference_type; + typedef typename container_type::allocator_type allocator_type; + typedef typename container_type::pointer pointer; + typedef typename container_type::const_pointer const_pointer; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + // minimal ndarray interface + typedef data_type dtype; + static const size_t value = 1; + static const bool is_vectorizable = + types::is_vectorizable_dtype::value && + !std::is_same::value; + static const bool is_flat = std::is_same::value; + static const bool is_strided = std::is_same::value; + + using shape_t = types::array_tuple; + template + auto shape() const -> decltype(details::extract_shape(*this, + utils::int_{})) + { + return details::extract_shape(*this, utils::int_{}); + } + + // constructor + sliced_array(); + sliced_array(sliced_array const &s); + sliced_array(array const &other, S const &s); + template + sliced_array(utils::shared_ref const &other, Sn const &s); + + // assignment + sliced_array &operator=(array const &); + sliced_array &operator=(sliced_array const &); + array operator+(array const &) const; + template + array operator+(array_base const &) const; + template + array::type> + operator+(sliced_array const &) const; + + // iterators + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + reverse_iterator rbegin() + { + return {end()}; + } + const_reverse_iterator rbegin() const + { + return {end()}; + } + reverse_iterator rend() + { + return {begin()}; + } + const_reverse_iterator rend() const + { + return {begin()}; + } + + // size + long size() const; + explicit operator bool() const; + + // accessors + const_reference fast(long i) const; + reference fast(long i); + const_reference operator[](long i) const; + reference operator[](long i); + template + typename std::enable_if< + is_slice::value, + sliced_array() * std::declval())>>::type + operator[](Sp s) const; + + template + dtype load(long index0, long index1, Indices... indices) const + { + return fast(index0).load(index1, indices...); + } + + dtype load(long index) const + { + return fast(index); + } + // comparison + template + bool operator==(array const &other) const; + +#ifdef USE_XSIMD + using simd_iterator = const_simd_nditerator; + using simd_iterator_nobroadcast = simd_iterator; + template + simd_iterator vbegin(vectorizer) const; + template + simd_iterator vend(vectorizer) const; +#endif + + // other operations + template + bool contains(V const &v) const; + intptr_t id() const; + + intptr_t baseid() const + { + return reinterpret_cast(&(*_data)); + } + + long count(T const &x) const; + template + friend std::ostream &operator<<(std::ostream &os, + sliced_array const &v); + }; + + /* array */ + template + class array + { + + static const size_t DEFAULT_CAPACITY = 16; + + // data holder + typedef + typename std::remove_cv::type>::type + _type; + typedef container<_type> container_type; + utils::shared_ref _data; + + template + friend class sliced_array; + + template + friend class array; + + public: + // types + typedef T data_type; + typedef typename std::conditional::value, long, + double>::type value_type; + typedef array_reference reference; + typedef value_type const_reference; + typedef array_iterator iterator; + typedef array_iterator const_iterator; + typedef typename container_type::size_type size_type; + typedef typename container_type::difference_type difference_type; + typedef typename container_type::allocator_type allocator_type; + typedef typename container_type::pointer pointer; + typedef typename container_type::const_pointer const_pointer; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + // minimal ndarray interface + typedef data_type dtype; + static const size_t value = 1; + static const bool is_vectorizable = types::is_vectorizable::value; + static const bool is_flat = true; + static const bool is_strided = false; + + // constructors + array(); + template + array(InputIterator start, InputIterator stop); + array(size_type sz); + array(array &&other); + array(array const &other); + template + array(list const &other) : array(other.begin(), other.end()) + { + } + template + array(static_list const &other) : array(other.begin(), other.end()) + { + } + template + array(array const &other); + template + array(sliced_array const &other); + array &operator=(array &&other); + template + array &operator=(array const &other); + array &operator=(array const &other); + template + array &operator=(array_base const &); + template + array &operator=(sliced_array const &other); + + template + array & + operator=(ndarray> const &); // implemented in ndarray.hpp + + template + array &operator+=(sliced_array const &other); + template + array operator+(sliced_array const &other) const; + template + array operator+(array_base const &other) const; + + // io + template + friend std::ostream &operator<<(std::ostream &os, array const &v); + + // comparison + template + bool operator==(array const &other) const; + template + bool operator!=(array const &other) const; + + // iterators + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + reverse_iterator rbegin() + { + return {end()}; + } + const_reverse_iterator rbegin() const + { + return {end()}; + } + reverse_iterator rend() + { + return {begin()}; + } + const_reverse_iterator rend() const + { + return {begin()}; + } + + // comparison + bool operator<(array const &other) const; + bool operator<=(array const &other) const; + bool operator>(array const &other) const; + bool operator>=(array const &other) const; + +// element access +#ifdef USE_XSIMD + using simd_iterator = const_simd_nditerator; + using simd_iterator_nobroadcast = simd_iterator; + template + simd_iterator vbegin(vectorizer) const; + template + simd_iterator vend(vectorizer) const; +#endif + reference fast(long n); + reference operator[](long n); + + const_reference fast(long n) const; + const_reference operator[](long n) const; + + template + typename std::enable_if::value, sliced_array>::type + operator[](Sp const &s) const; + + template + dtype load(long index0, long index1, Indices... indices) const + { + return fast(index0).load(index1, indices...); + } + + dtype load(long index) const + { + return fast(index); + } + + dtype *data() + { + return _data->data(); + } + const dtype *data() const + { + return _data->data(); + } + + // modifiers + template + void push_back(Tp &&x); + template + void insert(long i, Tp &&x); + + void reserve(size_t n); + void resize(size_t n); + void erase(size_t n); + + T pop(long x = -1); + void clear(); + + // TODO: have to raise a valueError + none_type remove(T const &x); + + // Misc + // TODO: have to raise a valueError + long index(T const &x) const; + + // array interface + explicit operator bool() const; + + template + array::type> operator+(array const &s) const; + + template + array() + + std::declval::value_type>())> + operator+(sliced_array const &s) const; + + array operator*(long t) const; + array const &operator*=(long t); + + template + array &operator+=(F const &s); + + long size() const; + template + long _flat_size(E const &e, utils::int_<1>) const; + template + long _flat_size(E const &e, utils::int_) const; + long flat_size() const; + + template + bool contains(V const &v) const; + intptr_t id() const; + + long count(T const &x) const; + using shape_t = array_tuple; + template + long shape() const + { + if (I == 0) + return size(); + else + return details::extract_shape(*this, utils::int_{}); + } + + template + operator array_base() const + { + assert(size() == N && "consistent size"); + array_base res; + std::copy(begin(), end(), res.begin()); + return res; + } + }; } // namespace types +namespace utils +{ + /** + * Reserve enough space to save all values generated from f. + * + * We use a dummy arguments (p) to reserve only when f have a + * const_iterator type. + */ + template + void reserve(types::array &l, From const &f, + typename From::const_iterator *p = nullptr); +} // namespace utils + +template +struct assignable> { + typedef types::array::type> type; +}; + +template +struct assignable> { + typedef types::array::type> type; +}; + +template +struct assignable> { + typedef typename E::value_type type; +}; + PYTHONIC_NS_END +/* overload std::get */ +namespace std +{ + template + typename pythonic::types::array::reference + get(pythonic::types::array &t); + + template + typename pythonic::types::array::const_reference + get(pythonic::types::array const &t); + + template + typename pythonic::types::array::value_type + get(pythonic::types::array &&t); + + template + typename pythonic::types::sliced_array::reference + get(pythonic::types::sliced_array &t); + + template + typename pythonic::types::sliced_array::const_reference + get(pythonic::types::sliced_array const &t); + + template + typename pythonic::types::sliced_array::value_type + get(pythonic::types::sliced_array &&t); + + template + struct tuple_element> { + typedef typename pythonic::types::array::value_type type; + }; + template + struct tuple_element> { + typedef typename pythonic::types::sliced_array::value_type type; + }; +} // namespace std + +/* type inference stuff {*/ +#include "pythonic/include/types/combined.hpp" + +template +struct __combined, pythonic::types::array> { + typedef pythonic::types::array::type> type; +}; + +template +struct __combined, container> { + typedef pythonic::types::array::type> type; +}; + +template +struct __combined, pythonic::types::array> { + typedef pythonic::types::array type; +}; + +template +struct __combined, indexable> { + typedef pythonic::types::array type; +}; + +template +struct __combined, pythonic::types::array> { + typedef pythonic::types::array::type> type; +}; + +template +struct __combined, indexable_container> { + typedef pythonic::types::array::type> type; +}; + +template +struct __combined, pythonic::types::array> { + typedef pythonic::types::array::type> type; +}; + +template +struct __combined, + pythonic::types::array> { + typedef pythonic::types::array::type> type; +}; +template +struct __combined, + pythonic::types::sliced_array> { + typedef pythonic::types::array::type> type; +}; + +template +struct __combined, + pythonic::types::array> { + typedef pythonic::types::array::type> type; +}; +template +struct __combined, + pythonic::types::array_base> { + typedef pythonic::types::array::type> type; +}; + +/* } */ + +#ifdef ENABLE_PYTHON_MODULE + +PYTHONIC_NS_BEGIN + +template +struct to_python> { + static PyObject *convert(types::array const &v); +}; +template +struct to_python> { + static PyObject *convert(types::sliced_array const &v); +}; + +PYTHONIC_NS_END + +#endif + #endif diff --git a/pythran/pythonic/include/types/numpy_op_helper.hpp b/pythran/pythonic/include/types/numpy_op_helper.hpp index 034a9a082..d61e0b875 100644 --- a/pythran/pythonic/include/types/numpy_op_helper.hpp +++ b/pythran/pythonic/include/types/numpy_op_helper.hpp @@ -63,6 +63,12 @@ namespace types template struct any_numop_arg> : std::false_type { }; + template + struct any_numop_arg> : std::false_type { + }; + template + struct any_numop_arg> : std::false_type { + }; template struct any_numop_arg> : std::false_type { }; diff --git a/pythran/pythonic/include/utils/nested_container.hpp b/pythran/pythonic/include/utils/nested_container.hpp index 3f8e8dd14..e53703a1c 100644 --- a/pythran/pythonic/include/utils/nested_container.hpp +++ b/pythran/pythonic/include/utils/nested_container.hpp @@ -12,6 +12,10 @@ namespace types class sliced_list; template class list; + template + class sliced_array; + template + class array; template struct array_base; template @@ -51,6 +55,16 @@ namespace utils static const int value = 1 + nested_container_depth::value; }; + template + struct nested_container_depth> { + static const int value = 1; + }; + + template + struct nested_container_depth> { + static const int value = 1; + }; + template struct nested_container_depth> { static const int value = 1 + nested_container_depth::value; @@ -120,6 +134,16 @@ namespace utils using type = typename nested_container_value_type::type; }; + template + struct nested_container_value_type> { + using type = T; + }; + + template + struct nested_container_value_type> { + using type = T; + }; + template struct nested_container_value_type> { using type = typename nested_container_value_type::type; diff --git a/pythran/pythonic/include/utils/numpy_traits.hpp b/pythran/pythonic/include/utils/numpy_traits.hpp index 06a10ac79..112ff7a94 100644 --- a/pythran/pythonic/include/utils/numpy_traits.hpp +++ b/pythran/pythonic/include/utils/numpy_traits.hpp @@ -36,6 +36,12 @@ namespace types struct empty_list; + template + class array; + + template + class sliced_array; + template struct array_base; @@ -130,9 +136,7 @@ namespace types }; template - struct is_numexpr_arg> { - static constexpr bool value = - is_numexpr_arg::value || is_dtype::value; + struct is_numexpr_arg> : is_numexpr_arg> { }; template <> @@ -140,6 +144,15 @@ namespace types static constexpr bool value = true; }; + template + struct is_numexpr_arg> : is_numexpr_arg> { + }; + + template + struct is_numexpr_arg> + : is_numexpr_arg> { + }; + template struct is_numexpr_arg> { static constexpr bool value = diff --git a/pythran/pythonic/types/array.hpp b/pythran/pythonic/types/array.hpp index 061d00eec..e7b396679 100644 --- a/pythran/pythonic/types/array.hpp +++ b/pythran/pythonic/types/array.hpp @@ -2,5 +2,754 @@ #define PYTHONIC_TYPES_ARRAY_HPP #include "pythonic/include/types/array.hpp" +#include "pythonic/types/nditerator.hpp" + +#include "pythonic/builtins/len.hpp" +#include "pythonic/types/bool.hpp" +#include "pythonic/types/slice.hpp" +#include "pythonic/types/tuple.hpp" +#include "pythonic/utils/allocate.hpp" +#include "pythonic/utils/reserve.hpp" +#include "pythonic/utils/shared_ref.hpp" + +#include "pythonic/builtins/NotImplementedError.hpp" + +#include +#include + +PYTHONIC_NS_BEGIN + +namespace types +{ + + /// Sliced array + + // Constructors + template + sliced_array::sliced_array() : _data(utils::no_memory()) + { + } + template + sliced_array::sliced_array(sliced_array const &s) + : _data(s._data), slicing(s.slicing) + { + } + template + sliced_array::sliced_array(array const &other, S const &s) + : _data(other._data), slicing(s.normalize(other.size())) + { + } + template + template + sliced_array::sliced_array( + utils::shared_ref const &other, Sn const &s) + : _data(other), slicing(s) + { + } + + // iterators + template + typename sliced_array::iterator sliced_array::begin() + { + return {*this, 0}; + } + template + typename sliced_array::const_iterator sliced_array::begin() const + { + return {*this, 0}; + } + template + typename sliced_array::iterator sliced_array::end() + { + return {*this, size()}; + } + template + typename sliced_array::const_iterator sliced_array::end() const + { + return {*this, size()}; + } + + // size + template + long sliced_array::size() const + { + return slicing.size(); + } + template + sliced_array::operator bool() const + { + return slicing.size(); + } + + // accessor + template + typename sliced_array::const_reference + sliced_array::fast(long i) const + { + assert(0 <= i && i < size()); + auto const index = slicing.get(i); + assert(0 <= index && index < (long)_data->size()); + return (*_data)[index]; + } + template + typename sliced_array::reference sliced_array::fast(long i) + { + assert(0 <= i && i < size()); + auto const index = slicing.get(i); + assert(0 <= index && index < (long)_data->size()); + return (*_data)[index]; + } + template + typename sliced_array::const_reference + sliced_array::operator[](long i) const + { + assert(i < size()); + auto const index = slicing.get(i); + assert(0 <= index && index < (long)_data->size()); + return (*_data)[index]; + } + template + typename sliced_array::reference sliced_array::operator[](long i) + { + assert(i < size()); + auto const index = slicing.get(i); + assert(0 <= index && index < (long)_data->size()); + return (*_data)[index]; + } + + template + template + typename std::enable_if< + is_slice::value, + sliced_array() * std::declval())>>::type + sliced_array::operator[](Sp s) const + { + return {_data, slicing * s.normalize(this->size())}; + } + + // io + template + std::ostream &operator<<(std::ostream &os, sliced_array const &v) + { + os << '['; + auto iter = v.begin(); + if (iter != v.end()) { + while (iter + 1 != v.end()) { + os << *iter << ", "; + ++iter; + } + os << *iter; + } + return os << ']'; + } + + // comparison + template + template + bool sliced_array::operator==(array const &other) const + { + if (size() != other.size()) + return false; + return std::equal(begin(), end(), other.begin()); + } + template + inline sliced_array & + sliced_array::operator=(sliced_array const &s) + { + if (slicing.step == 1) { + // inserting before erasing in case of self-copy + auto insert_pt = _data->begin() + slicing.lower; + _data->insert(insert_pt, s.begin(), s.end()); + auto erase_pt = _data->begin() + s.size(); + _data->erase(erase_pt + slicing.lower, erase_pt + slicing.upper); + } else + assert(!"not implemented yet"); + return *this; + } + template + sliced_array &sliced_array::operator=(array const &seq) + { + if (slicing.step == 1) { + // inserting before erasing in case of self-copy + auto insert_pt = _data->begin() + slicing.lower; + _data->insert(insert_pt, seq.begin(), seq.end()); + auto erase_pt = _data->begin() + seq.size(); + _data->erase(erase_pt + slicing.lower, erase_pt + slicing.upper); + } else + assert(!"not implemented yet"); + return *this; + } + template + array sliced_array::operator+(array const &s) const + { + array out(size() + s.size()); + std::copy(s.begin(), s.end(), std::copy(begin(), end(), out.begin())); + return out; + } + template + template + array sliced_array::operator+(array_base const &s) const + { + array out(size() + s.size()); + std::copy(s.begin(), s.end(), std::copy(begin(), end(), out.begin())); + return out; + } + template + template + array::type> + sliced_array::operator+(sliced_array const &s) const + { + array::type> out(size() + s.size()); + std::copy(s.begin(), s.end(), std::copy(begin(), end(), out.begin())); + return out; + } + template + array operator*(N n, array const &l) + { + return l * n; + } +#ifdef USE_XSIMD + template + template + typename sliced_array::simd_iterator + sliced_array::vbegin(vectorizer) const + { + return {_data->data() + slicing.lower}; + } + + template + template + typename sliced_array::simd_iterator + sliced_array::vend(vectorizer) const + { + using vector_type = typename xsimd::batch; + static const std::size_t vector_size = vector_type::size; + return {_data->data() + slicing.lower + + long(size() / vector_size * vector_size)}; + } + +#endif + + // other operations + template + template + bool sliced_array::contains(V const &v) const + { + return std::find(_data->begin(), _data->end(), v) != _data->end(); + } + template + intptr_t sliced_array::id() const + { + // sharing is not implemented for sliced array + return reinterpret_cast(this); + } + + template + long sliced_array::count(T const &x) const + { + return std::count(begin(), end(), x); + } + + /// List + + // constructors + template + array::array() : _data(utils::no_memory()) + { + } + template + template + array::array(InputIterator start, InputIterator stop) : _data() + { + if (std::is_same< + typename std::iterator_traits::iterator_category, + std::random_access_iterator_tag>::value) + _data->reserve(std::distance(start, stop)); + else + _data->reserve(DEFAULT_CAPACITY); + std::copy(start, stop, std::back_inserter(*_data)); + } + template + array::array(size_type sz) : _data(sz) + { + } + template + array::array(array &&other) : _data(std::move(other._data)) + { + } + template + array::array(array const &other) : _data(other._data) + { + } + template + template + array::array(array const &other) : _data(other.size()) + { + std::copy(other.begin(), other.end(), begin()); + } + template + template + array::array(sliced_array const &other) + : _data(other.begin(), other.end()) + { + } + + // operators + template + array &array::operator=(array &&other) + { + _data = std::move(other._data); + return *this; + } + template + template + array &array::operator=(array const &other) + { + _data = utils::shared_ref{other.size()}; + std::copy(other.begin(), other.end(), begin()); + return *this; + } + template + array &array::operator=(array const &other) + { + _data = other._data; + return *this; + } + template + template + array &array::operator=(array_base const &other) + { + _data = utils::shared_ref(other.begin(), other.end()); + return *this; + } + template + template + array &array::operator=(sliced_array const &other) + { + if (other._data == _data) { + auto it = std::copy(other.begin(), other.end(), _data->begin()); + _data->resize(it - _data->begin()); + } else + _data = utils::shared_ref(other.begin(), other.end()); + return *this; + } + + template + template + array &array::operator+=(sliced_array const &other) + { + _data->resize(size() + other.size()); + std::copy(other.begin(), other.end(), _data->begin()); + return *this; + } + + template + template + array array::operator+(sliced_array const &other) const + { + array new_array(begin(), end()); + new_array.reserve(size() + other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(new_array)); + return new_array; + } + + template + template + array array::operator+(array_base const &other) const + { + array new_array(begin(), end()); + new_array.reserve(size() + other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(new_array)); + return new_array; + } + + // io + template + std::ostream &operator<<(std::ostream &os, array const &v) + { + os << '['; + auto iter = v.begin(); + if (iter != v.end()) { + while (iter + 1 != v.end()) + os << *iter++ << ", "; + os << *iter; + } + return os << ']'; + } + + // comparison + template + template + bool array::operator==(array const &other) const + { + if (size() != other.size()) + return false; + return std::equal(begin(), end(), other.begin()); + } + template + template + bool array::operator!=(array const &other) const + { + return !operator==(other); + } + + // iterators + template + typename array::iterator array::begin() + { + return {*this, 0}; + } + template + typename array::const_iterator array::begin() const + { + return {*this, 0}; + } + template + typename array::iterator array::end() + { + return {*this, size()}; + } + template + typename array::const_iterator array::end() const + { + return {*this, size()}; + } + + // comparison + template + bool array::operator<(array const &other) const + { + return std::lexicographical_compare(begin(), end(), other.begin(), + other.end()); + } + template + bool array::operator>(array const &other) const + { + return std::lexicographical_compare(other.begin(), other.end(), begin(), + end()); + } + template + bool array::operator<=(array const &other) const + { + return !(*this > other); + } + template + bool array::operator>=(array const &other) const + { + return !(*this < other); + } + +// element access +#ifdef USE_XSIMD + template + template + typename array::simd_iterator array::vbegin(vectorizer) const + { + return {_data->data()}; + } + + template + template + typename array::simd_iterator array::vend(vectorizer) const + { + using vector_type = typename xsimd::batch; + static const std::size_t vector_size = vector_type::size; + return {_data->data() + long(size() / vector_size * vector_size)}; + } + +#endif + template + typename array::reference array::fast(long n) + { + return (*_data)[n]; + } + template + typename array::reference array::operator[](long n) + { + if (n < 0) + n += size(); + assert(0 <= n && n < size()); + return fast(n); + } + template + typename array::const_reference array::fast(long n) const + { + assert(n < size()); + return (*_data)[n]; + } + template + typename array::const_reference array::operator[](long n) const + { + if (n < 0) + n += size(); + assert(0 <= n && n < size()); + return fast(n); + } + + template + template + typename std::enable_if::value, sliced_array>::type + array::operator[](Sp const &s) const + { + return {*this, s}; + } + + // modifiers + template + template + void array::push_back(Tp &&x) + { + // FIXME: clang-3.4 doesn't support emplace_back for vector of bool + _data->push_back(std::forward(x)); + } + template + template + void array::insert(long i, Tp &&x) + { + if (i == size()) + _data->emplace_back(std::forward(x)); + else + _data->insert(_data->begin() + i, std::forward(x)); + } + template + void array::reserve(size_t n) + { + if (n > _data->capacity()) + _data->reserve((n / 2) * 3); + } + template + void array::resize(size_t n) + { + _data->resize(n); + } + template + void array::erase(size_t n) + { + _data->erase(_data->begin() + n); + } + template + T array::pop(long x) + { + long sz = size(); + x = x % sz; + if (x < 0) + x += sz; + T res = fast(x); + erase(x); + return res; + } + template + void array::clear() + { + _data->clear(); + } + + // TODO: have to raise a valueError + template + none_type array::remove(T const &x) + { + erase(index(x)); + return {}; + } + + // Misc + template + long array::index(T const &x) const + { + return std::find(begin(), end(), x) - begin(); + } + + // array interface + template + array::operator bool() const + { + return !_data->empty(); + } + + template + template + array::type> + array::operator+(array const &s) const + { + array::type> clone(size() + s.size()); + std::copy(s.begin(), s.end(), std::copy(begin(), end(), clone.begin())); + return clone; + } + + template + template + array() + + std::declval::value_type>())> + array::operator+(sliced_array const &s) const + { + array() + + std::declval::value_type>())> + clone(size() + len(s)); + std::copy(s.begin(), s.end(), std::copy(begin(), end(), clone.begin())); + return clone; + } + + template + array array::operator*(long n) const + { + if (size() == 1) { + return array(fast(0), single_value{}, n); + } else { + array r(size() * n); + auto start = r.begin(); + while (start != r.end()) + start = std::copy(this->begin(), this->end(), start); + return r; + } + } + template + array const &array::operator*=(long n) + { + if (size() == 1) { + resize(n); + std::fill(begin() + 1, end(), fast(0)); + } else { + auto const initial_size = size(); + resize(n * initial_size); + // FIXME: could use less calls to std::copy + auto tgt = begin() + initial_size; + for (long i = 1; i < n; ++i) + tgt = std::copy(begin(), begin() + initial_size, tgt); + } + return *this; + } + + template + template + array &array::operator+=(F const &s) + { + reserve(size() + s.size()); + std::copy(s.begin(), s.end(), std::back_inserter(*this)); + return *this; + } + + template + long array::size() const + { + return _data->size(); + } + template + template + long array::_flat_size(E const &e, utils::int_<1>) const + { + return std::distance(e.begin(), e.end()); + } + template + template + long array::_flat_size(E const &e, utils::int_) const + { + return std::distance(e.begin(), e.end()) * + _flat_size(e[0], utils::int_{}); + } + template + long array::flat_size() const + { + return _flat_size(*this, utils::int_{}); + } + + template + template + bool array::contains(V const &v) const + { + return std::find(_data->begin(), _data->end(), v) != _data->end(); + } + template + intptr_t array::id() const + { + return reinterpret_cast(&(*_data)); + } + + template + long array::count(T const &x) const + { + return std::count(begin(), end(), x); + } + +} // namespace types + +namespace utils +{ + + template + void reserve(types::array &l, From const &f, + typename From::const_iterator *) + { + l.reserve(builtins::len(f)); + } +} // namespace utils +PYTHONIC_NS_END + +/* overload std::get */ +namespace std +{ + template + typename pythonic::types::array::reference + get(pythonic::types::array &t) + { + return t[I]; + } + + template + typename pythonic::types::array::const_reference + get(pythonic::types::array const &t) + { + return t[I]; + } + + template + typename pythonic::types::array::value_type + get(pythonic::types::array &&t) + { + return std::move(t)[I]; + } + + template + typename pythonic::types::sliced_array::reference + get(pythonic::types::sliced_array &t) + { + return t[I]; + } + + template + typename pythonic::types::sliced_array::const_reference + get(pythonic::types::sliced_array const &t) + { + return t[I]; + } + + template + typename pythonic::types::sliced_array::value_type + get(pythonic::types::sliced_array &&t) + { + return std::move(t)[I]; + } +} // namespace std + +#ifdef ENABLE_PYTHON_MODULE + +PYTHONIC_NS_BEGIN + +template +PyObject *to_python>::convert(types::array const &v) +{ + throw types::NotImplementedError( + "Pythran cannot efficiently convert array::array values"); +} +template +PyObject *to_python>::convert( + types::sliced_array const &v) +{ + throw types::NotImplementedError( + "Pythran cannot efficiently convert array::array values"); +} + +PYTHONIC_NS_END + +#endif #endif diff --git a/pythran/tests/test_array.py b/pythran/tests/test_array.py index a63e4c234..1e493612c 100644 --- a/pythran/tests/test_array.py +++ b/pythran/tests/test_array.py @@ -5,85 +5,189 @@ import os import sys + class TestArray(TestEnv): - @unittest.skipIf(sys.implementation.name == 'pypy', "non supported upstream") + @unittest.skipIf(sys.implementation.name == "pypy", "non supported upstream") def test_typecodes(self): - self.run_test("def typecodes_(i): import array; return array.typecodes[i]", - 2, typecodes_=[int]) + self.run_test( + "def typecodes_(i): import array; return array.typecodes[i]", + 2, + typecodes_=[int], + ) def test_array_empty(self): - self.run_test("def array_empty_(): import array; return len(array.array('b'))", - array_empty_=[]) + self.run_test( + "def array_empty_(): import array; return len(array.array('b'))", + array_empty_=[], + ) def test_array_seq(self): - self.run_test("def array_seq_(): import array; return len(array.array('h', [1, 2, 3]))", - array_seq_=[]) + self.run_test( + "def array_seq_(): import array; return len(array.array('h', [1, 2, 3]))", + array_seq_=[], + ) def test_array_seq_tuple(self): - self.run_test("def array_seq_tuple_(): import array; return len(array.array('h', (8, 9, 10)))", - array_seq_tuple_=[]) + self.run_test( + "def array_seq_tuple_(): import array; return len(array.array('h', (8, 9, 10)))", + array_seq_tuple_=[], + ) def test_array_tolist(self): - self.run_test("def array_tolist_(n): import array; return array.array('h', [n]).tolist()", - 2, array_tolist_=[int]) + self.run_test( + "def array_tolist_(n): import array; return array.array('h', [n]).tolist()", + 2, + array_tolist_=[int], + ) def test_array_append(self): - self.run_test("def array_append_(n): import array; x = array.array('h', [n]); x.append(1); return x.tolist()", - 2, array_append_=[int]) + self.run_test( + "def array_append_(n): import array; x = array.array('h', [n]); x.append(1); return x.tolist()", + 2, + array_append_=[int], + ) def test_array_buffer_info(self): - self.run_test("def array_buffer_info_(n): import array; x = array.array('h', [n]); return x.buffer_info()[1]", - 2, array_buffer_info_=[int]) + self.run_test( + "def array_buffer_info_(n): import array; x = array.array('h', [n]); return x.buffer_info()[1]", + 2, + array_buffer_info_=[int], + ) def test_array_byteswap(self): - self.run_test("def array_byteswap_(n): import array; x = array.array('H', [n]); x.byteswap(); return x.tolist()", - 2, array_byteswap_=[int]) + self.run_test( + "def array_byteswap_(n): import array; x = array.array('H', [n]); x.byteswap(); return x.tolist()", + 2, + array_byteswap_=[int], + ) def test_array_count(self): - self.run_test("def array_count_(n): import array; x = array.array('H', [n, n]); return x.count(1), x.count(2)", - 2, array_count_=[int]) + self.run_test( + "def array_count_(n): import array; x = array.array('H', [n, n]); return x.count(1), x.count(2)", + 2, + array_count_=[int], + ) def test_array_extend(self): - self.run_test("def array_extend_(n): import array; x = array.array('h', [n]); x.extend((1, 1)); return x.tolist()", - 2, array_extend_=[int]) + self.run_test( + "def array_extend_(n): import array; x = array.array('h', [n]); x.extend((1, 1)); return x.tolist()", + 2, + array_extend_=[int], + ) def test_array_fromfile(self): filename = mkstemp()[1] with open(filename, "w") as fd: - fd.write("12345678"*100) - self.run_test("def array_fromfile_(s): import array; x = array.array('h'); x.fromfile(open(s, 'rb'), 8); return x.tolist()", - filename, array_fromfile_=[str]) + fd.write("12345678" * 100) + self.run_test( + "def array_fromfile_(s): import array; x = array.array('h'); x.fromfile(open(s, 'rb'), 8); return x.tolist()", + filename, + array_fromfile_=[str], + ) os.remove(filename) def test_array_fromlist(self): - self.run_test("def array_fromlist_(f): import array; x = array.array('f'); x.fromlist([f]); return x.tolist()", - 3., array_fromlist_=[float]) + self.run_test( + "def array_fromlist_(f): import array; x = array.array('f'); x.fromlist([f]); return x.tolist()", + 3.0, + array_fromlist_=[float], + ) def test_array_frombytes(self): filename = mkstemp()[1] with open(filename, "w") as fd: - fd.write("12345678"*10) - self.run_test("def array_frombytes_(s): import array; x = array.array('i'); x.frombytes(open(s, 'rb').read()); return x.tolist()", - filename, array_frombytes_=[str]) + fd.write("12345678" * 10) + self.run_test( + "def array_frombytes_(s): import array; x = array.array('i'); x.frombytes(open(s, 'rb').read()); return x.tolist()", + filename, + array_frombytes_=[str], + ) os.remove(filename) def test_array_index(self): - self.run_test("def array_index_(f): import array; x = array.array('I',[1,2,3]); return x.index(f)", - 3, array_index_=[int]) + self.run_test( + "def array_index_(f): import array; x = array.array('I',[1,2,3]); return x.index(f)", + 3, + array_index_=[int], + ) def test_array_insert(self): - self.run_test("def array_insert_(f): import array; x = array.array('I',[1,2,3]); x.insert(0, f); x.insert(-1, f); return x.tolist()", - 3, array_insert_=[int]) + self.run_test( + "def array_insert_(f): import array; x = array.array('I',[1,2,3]); x.insert(0, f); x.insert(-1, f); return x.tolist()", + 3, + array_insert_=[int], + ) def test_array_pop(self): - self.run_test("def array_pop_(f): import array; x = array.array('I',[f,2,3]); x.pop(); return int(x.pop(0))", - 3, array_pop_=[int]) + self.run_test( + "def array_pop_(f): import array; x = array.array('I',[f,2,3]); x.pop(); return int(x.pop(0))", + 3, + array_pop_=[int], + ) def test_array_remove(self): - self.run_test("def array_remove_(f): import array; x = array.array('I',[f,2,3]); x.remove(2); return x.tolist()", - 3, array_remove_=[int]) + self.run_test( + "def array_remove_(f): import array; x = array.array('I',[f,2,3]); x.remove(2); return x.tolist()", + 3, + array_remove_=[int], + ) def test_array_reverse(self): - self.run_test("def array_reverse_(f): import array; x = array.array('I',[f,2,3]); x.reverse(); return x.tolist()", - 3, array_reverse_=[int]) + self.run_test( + "def array_reverse_(f): import array; x = array.array('I',[f,2,3]); x.reverse(); return x.tolist()", + 3, + array_reverse_=[int], + ) + + def test_array_get(self): + self.run_test( + "def array_get_(f): import array; x = array.array('I',[f,2,3]); return x[0], x[f]", + 1, + array_get_=[int], + ) + + def test_array_set(self): + self.run_test( + "def array_set_(f): import array; x = array.array('I',[f,2,3]); x[2] = 4; x[f] = 9; return x.tolist()", + 1, + array_set_=[int], + ) + + def test_array_iter(self): + self.run_test( + "def array_iter_(f): import array; x = array.array('I',[f,2,3]); return next(iter(x))", + 1, + array_iter_=[int], + ) + + def test_array_len(self): + self.run_test( + "def array_len_(f): import array; x = array.array('I',[f,2,3]); return len(x)", + 1, + array_len_=[int], + ) + + def test_array_loop(self): + self.run_test( + "def array_loop_(f): import array; x = " + "array.array('I',[f,2,3]); return [2 * i for i in x]", + 1, + array_loop_=[int], + ) + + def test_array_add(self): + self.run_test( + "def array_add_(f): import array; x = " + "array.array('I',[f,2,3]); return (x + x).tolist()", + 1, + array_add_=[int], + ) + + def test_array_slice(self): + self.run_test( + "def array_slice_(f): import array; x = array.array('I',[f,2,3]); " + "return x[1:].tolist(), x[:-1][0]", + 1, + array_slice_=[int], + )