← Back
Editing: slice.hpp
#ifndef PYTHONIC_TYPES_SLICE_HPP #define PYTHONIC_TYPES_SLICE_HPP #include "pythonic/include/types/slice.hpp" #include "pythonic/types/NoneType.hpp" #include "pythonic/builtins/None.hpp" #include <cassert> #include <stdexcept> #include <ostream> #include <algorithm> PYTHONIC_NS_BEGIN namespace types { struct contiguous_slice; namespace details { long roundup_divide(long a, long b) { if (b > 0) return (a + b - 1) / b; else return (a + b + 1) / b; } } normalized_slice::normalized_slice() { } normalized_slice::normalized_slice(long lower, long upper, long step) : lower(lower), upper(upper), step(step) { } normalized_slice normalized_slice:: operator*(normalized_slice const &other) const { return {lower + step * other.lower, lower + step * other.upper, step * other.step}; } normalized_slice normalized_slice:: operator*(contiguous_normalized_slice const &other) const { return {lower + step * other.lower, lower + step * other.upper, step * other.step}; } normalized_slice normalized_slice::operator*(slice const &other) const { return (*this) * other.normalize(size()); } normalized_slice normalized_slice:: operator*(contiguous_slice const &other) const { return (*this) * other.normalize(size()); } long normalized_slice::size() const { return std::max(0L, details::roundup_divide(upper - lower, step)); } long normalized_slice::get(long i) const { return lower + i * step; } slice::slice(none<long> lower, none<long> upper, none<long> step) : lower(lower), upper(upper), step(step) { } slice::slice() : lower(builtins::None), upper(builtins::None), step(builtins::None) { } slice slice::operator*(slice const &other) const { // We do not implement these because it requires to know the "end" // value of the slice which is ! possible if it is ! "step == 1" slice // TODO: We can skip these constraints if we know begin, end && step. long sstep = (step.is_none()) ? 1 : (long)step; long ostep = (other.step.is_none()) ? 1 : (long)other.step; assert(!((ostep < 0 || static_cast<long>(other.upper) < 0 || static_cast<long>(other.lower) < 0) && sstep != 1 && sstep != -1) && "not implemented"); bound<long> new_lower; if (other.lower.is_none() || (long)other.lower == 0) { if (ostep > 0) new_lower = lower; else if (sstep > 0) { if (upper.is_none() || (long)upper == 0) // 0 means the first value && ! the last value new_lower = none_type{}; else new_lower = (long)upper - 1; } else { if (upper.is_none() || (long)upper == -1) new_lower = none_type{}; else new_lower = (long)upper + 1; } } else { none<long> ref = ((long)other.lower > 0) ? lower : upper; if (ref.is_none) { if (sstep > 0) new_lower = (long)other.lower * sstep; else new_lower = (long)other.lower * sstep - 1; } else new_lower = (long)ref + (long)other.lower * sstep; } long new_step = sstep * ostep; bound<long> new_upper; if (other.upper.is_none()) { if (ostep > 0) new_upper = upper; else if (sstep > 0) { if (lower.is_none() || (long)lower == 0) new_upper = none_type{}; else new_upper = (long)lower - 1; } else { if (lower.is_none() || (long)lower == -1) // 0 means the first value && ! the last value new_upper = none_type{}; else new_upper = (long)lower + 1; } } else { none<long> ref = ((long)other.upper > 0) ? lower : upper; if (ref.is_none) { if (sstep > 0) new_upper = (long)other.upper * sstep; else new_upper = (long)other.upper * sstep - 1; } else new_upper = (long)ref + (long)other.upper * sstep; } return {new_lower, new_upper, new_step}; } /* Normalize change a[:-1] to a[:len(a)-1] to have positif index. It also check for value bigger than len(a) to fit the size of the container */ normalized_slice slice::normalize(long max_size) const { long sstep = step.is_none() ? 1 : (long)step; long normalized_upper; if (upper.is_none()) { if (sstep > 0L) normalized_upper = max_size; else normalized_upper = -1L; } else { if (upper < 0L) normalized_upper = std::max(-1L, max_size + upper); else if (upper > max_size) normalized_upper = max_size; else normalized_upper = (long)upper; } long normalized_lower; if (lower.is_none() && sstep > 0L) normalized_lower = 0L; else if (lower.is_none() && sstep < 0L) normalized_lower = max_size - 1L; else if (lower < 0L) normalized_lower = std::max(0L, max_size + lower); else if (lower > max_size) normalized_lower = max_size; else normalized_lower = (long)lower; return {normalized_lower, normalized_upper, sstep}; } /* * An assert is raised when we can't compute the size without more * informations. */ long slice::size() const { long sstep = step.is_none() ? 1 : (long)step; assert(!(upper.is_none() && lower.is_none())); long len; #define SIGN(x) (((long)x >= 0l) ? 0 : 1) if (upper.is_none()) { assert(SIGN(sstep) != SIGN(lower)); len = -(long)lower; } else if (lower.is_none()) { assert(SIGN(sstep) == SIGN((long)upper)); len = upper; } else len = upper - lower; #undef SIGN return std::max(0L, details::roundup_divide(len, sstep)); } long slice::get(long i) const { long sstep = step.is_none() ? 1 : (long)step; assert(!upper.is_none() && !lower.is_none()); return (long)lower + i * sstep; } contiguous_normalized_slice::contiguous_normalized_slice() { } contiguous_normalized_slice::contiguous_normalized_slice(long lower, long upper) : lower(lower), upper(upper) { } contiguous_normalized_slice contiguous_normalized_slice:: operator*(contiguous_normalized_slice const &other) const { return contiguous_normalized_slice(lower + other.lower, lower + other.upper); } contiguous_normalized_slice contiguous_normalized_slice:: operator*(contiguous_slice const &other) const { return (*this) * other.normalize(size()); } contiguous_normalized_slice contiguous_normalized_slice:: operator*(fast_contiguous_slice const &other) const { return (*this) * other.normalize(size()); } normalized_slice contiguous_normalized_slice:: operator*(normalized_slice const &other) const { return normalized_slice(lower + step * other.lower, lower + step * other.upper, step * other.step); } normalized_slice contiguous_normalized_slice:: operator*(slice const &other) const { return (*this) * other.normalize(size()); } long contiguous_normalized_slice::size() const { return std::max(0L, upper - lower); } inline long contiguous_normalized_slice::get(long i) const { return lower + i; } contiguous_slice::contiguous_slice(none<long> lower, none<long> upper) : lower(lower.is_none ? 0 : (long)lower), upper(upper) { } contiguous_slice contiguous_slice:: operator*(contiguous_slice const &other) const { long new_lower; if (other.lower < 0) new_lower = upper + other.lower * step; else new_lower = lower + other.lower * step; bound<long> new_upper; if (other.upper.is_none()) new_upper = upper; else if ((long)other.upper < 0) { if (upper.is_none()) new_upper = (long)other.upper * step; else new_upper = upper + (long)other.upper * step; } else new_upper = lower + (long)other.upper * step; return {new_lower, new_upper}; } slice contiguous_slice::operator*(slice const &other) const { none<long> new_lower; if (other.lower.is_none() || (long)other.lower == 0) { if (other.step > 0) new_lower = lower; else if (upper.is_none() || (long)upper == 0) // 0 means the first value && ! the last value new_lower = none_type{}; else new_lower = (long)upper - 1; } else { if ((long)other.lower > 0) new_lower = lower + (long)other.lower * step; else if (upper.is_none()) new_lower = (long)other.lower * step; else new_lower = (long)upper + (long)other.lower * step; } long new_step = other.step; bound<long> new_upper; if (other.upper.is_none()) { if (other.step > 0) new_upper = upper; else if ((long)lower == 0) new_upper = none_type{}; else new_upper = (long)lower - 1; } else { if ((long)other.upper > 0) new_upper = lower + (long)other.upper * step; else if (upper.is_none()) new_upper = (long)other.upper * step; else new_upper = (long)upper + (long)other.upper * step; } return {new_lower, new_upper, new_step}; } /* Normalize change a[:-1] to a[:len(a)-1] to have positif index. It also check for value bigger than len(a) to fit the size of the container */ contiguous_normalized_slice contiguous_slice::normalize(long max_size) const { long normalized_upper; if (upper.is_none()) normalized_upper = max_size; else if (upper < 0L) normalized_upper = std::max(-1L, max_size + upper); else if (upper > max_size) normalized_upper = max_size; else normalized_upper = (long)upper; long normalized_lower; if (lower < 0L) normalized_lower = std::max(0L, max_size + lower); else if (lower > max_size) normalized_lower = max_size; else normalized_lower = (long)lower; return contiguous_normalized_slice(normalized_lower, normalized_upper); } long contiguous_slice::size() const { long len; if (upper.is_none()) { assert(lower < 0); len = -lower; } else len = upper - lower; return std::max(0L, len); } long contiguous_slice::get(long i) const { return int(lower) + i; } fast_contiguous_slice::fast_contiguous_slice(none<long> lower, none<long> upper) : lower(lower.is_none ? 0 : (long)lower), upper(upper) { } fast_contiguous_slice fast_contiguous_slice:: operator*(fast_contiguous_slice const &other) const { long new_lower = lower + other.lower * step; bound<long> new_upper; if (other.upper.is_none()) new_upper = upper; else new_upper = lower + (long)other.upper * step; return {new_lower, new_upper}; } contiguous_slice fast_contiguous_slice:: operator*(contiguous_slice const &other) const { long new_lower; if (other.lower < 0) new_lower = upper + other.lower * step; else new_lower = lower + other.lower * step; bound<long> new_upper; if (other.upper.is_none()) new_upper = upper; else if ((long)other.upper < 0) { if (upper.is_none()) new_upper = (long)other.upper * step; else new_upper = upper + (long)other.upper * step; } else new_upper = lower + (long)other.upper * step; return {new_lower, new_upper}; } slice fast_contiguous_slice::operator*(slice const &other) const { none<long> new_lower; if (other.lower.is_none() || (long)other.lower == 0) { if (other.step > 0) new_lower = lower; else if (upper.is_none() || (long)upper == 0) // 0 means the first value && ! the last value new_lower = none_type{}; else new_lower = (long)upper - 1; } else { if ((long)other.lower > 0) new_lower = lower + (long)other.lower * step; else if (upper.is_none()) new_lower = (long)other.lower * step; else new_lower = (long)upper + (long)other.lower * step; } long new_step = other.step; bound<long> new_upper; if (other.upper.is_none()) { if (other.step > 0) new_upper = upper; else if ((long)lower == 0) new_upper = none_type{}; else new_upper = (long)lower - 1; } else { if ((long)other.upper > 0) new_upper = lower + (long)other.upper * step; else if (upper.is_none()) new_upper = (long)other.upper * step; else new_upper = (long)upper + (long)other.upper * step; } return {new_lower, new_upper, new_step}; } /* Normalize change a[:-1] to a[:len(a)-1] to have positif index. It also check for value bigger than len(a) to fit the size of the container */ contiguous_normalized_slice fast_contiguous_slice::normalize(long max_size) const { long normalized_upper; if (upper.is_none()) normalized_upper = max_size; else if (upper > max_size) normalized_upper = max_size; else normalized_upper = (long)upper; long normalized_lower; if (lower > max_size) normalized_lower = max_size; else normalized_lower = (long)lower; return {normalized_lower, normalized_upper}; } long fast_contiguous_slice::size() const { assert(!upper.is_none()); return std::max(0L, upper - lower); } slice slice::operator*(contiguous_slice const &other) const { // We do ! implement these because it requires to know the "end" // value of the slice which is ! possible if it is ! "step == 1" slice // TODO: We can skip these constraints if we know begin, end && step. assert(!((static_cast<long>(other.upper) < 0 || static_cast<long>(other.lower) < 0) && step != 1 && step != -1) && "not implemented"); bound<long> new_lower; if (other.lower == 0) new_lower = lower; else { bound<long> ref = ((long)other.lower > 0) ? lower : upper; if (ref.is_none()) { if (step > 0) new_lower = (long)other.lower * step; else new_lower = (long)other.lower * step - 1; } else new_lower = (long)ref + (long)other.lower * step; } long new_step = step; bound<long> new_upper; if (other.upper.is_none()) new_upper = upper; else { bound<long> ref = ((long)other.upper > 0) ? lower : upper; if (ref.is_none()) { if (step > 0) new_upper = (long)other.upper * step; else new_upper = (long)other.upper * step - 1; } else new_upper = (long)ref + (long)other.upper * step; } return {new_lower, new_upper, new_step}; } template <class T> std::ostream &operator<<(std::ostream &os, bound<T> const &b) { return (b.is_none() ? (os << "None") : (os << (T)b)); } template <class S> typename std::enable_if<is_slice<S>::value, std::ostream &>::type operator<<(std::ostream &os, S const &s) { return os << "slice(" << s.lower << ", " << s.upper << ", " << s.step << ")"; } } PYTHONIC_NS_END #ifdef ENABLE_PYTHON_MODULE PYTHONIC_NS_BEGIN template <class T> PyObject *to_python<types::bound<T>>::convert(types::bound<T> const &b) { if (b.is_none()) return ::to_python(types::none_type{}); else return ::to_python((T)b); } PyObject *to_python<types::contiguous_normalized_slice>::convert( types::contiguous_normalized_slice const &v) { return PySlice_New(::to_python(v.lower), ::to_python(v.upper), ::to_python(types::none_type{})); } PyObject * to_python<types::contiguous_slice>::convert(types::contiguous_slice const &v) { return PySlice_New(::to_python(v.lower), ::to_python(v.upper), ::to_python(types::none_type{})); } PyObject * to_python<types::normalized_slice>::convert(types::normalized_slice const &v) { if (v.step > 0) { return PySlice_New(::to_python(v.lower), ::to_python(v.upper), ::to_python(v.step)); } else { return PySlice_New(::to_python(v.lower), v.upper < 0 ? ::to_python(types::none_type{}) : ::to_python(v.upper), ::to_python(v.step)); } } PyObject *to_python<types::slice>::convert(types::slice const &v) { if (v.step > 0) { return PySlice_New(::to_python(v.lower), ::to_python(v.upper), ::to_python(v.step)); } else { return PySlice_New(::to_python(v.lower), v.upper < 0 ? ::to_python(types::none_type{}) : ::to_python(v.upper), ::to_python(v.step)); } } bool from_python<types::slice>::is_convertible(PyObject *obj) { return PySlice_Check(obj); } types::slice from_python<types::slice>::convert(PyObject *obj) { Py_ssize_t start, stop, step; #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 6) || \ (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 6 && \ PY_MICRO_VERSION >= 1 && !defined(PYPY_VERSION)) PySlice_Unpack(obj, &start, &stop, &step); #elif PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 1 PySlice_GetIndices((PyObject *)obj, PY_SSIZE_T_MAX, &start, &stop, &step); #else PySlice_GetIndices((PySliceObject *)obj, PY_SSIZE_T_MAX, &start, &stop, &step); #endif types::none<long> nstart, nstop, nstep; if (start != PY_SSIZE_T_MAX) nstart = start; else nstart = types::none_type{}; if (stop != PY_SSIZE_T_MAX) nstop = stop; else nstop = types::none_type{}; if (step != PY_SSIZE_T_MAX) nstep = step; else nstep = types::none_type{}; return {nstart, nstop, nstep}; } PYTHONIC_NS_END #endif #endif
Save File
Cancel