xtensor
Loading...
Searching...
No Matches
xslice.hpp
1/***************************************************************************
2 * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht *
3 * Copyright (c) QuantStack *
4 * *
5 * Distributed under the terms of the BSD 3-Clause License. *
6 * *
7 * The full license is in the file LICENSE, distributed with this software. *
8 ****************************************************************************/
9
10#ifndef XTENSOR_SLICE_HPP
11#define XTENSOR_SLICE_HPP
12
13#include <cstddef>
14#include <map>
15#include <type_traits>
16#include <utility>
17
18#include <xtl/xtype_traits.hpp>
19
20#include "../containers/xstorage.hpp"
21#include "../core/xtensor_config.hpp"
22#include "../utils/xutils.hpp"
23
24namespace xt
25{
26
27 /**********************
28 * xslice declaration *
29 **********************/
30
31 template <class D>
32 class xslice
33 {
34 public:
35
36 using derived_type = D;
37
38 derived_type& derived_cast() noexcept;
39 const derived_type& derived_cast() const noexcept;
40
41 protected:
42
43 xslice() = default;
44 ~xslice() = default;
45
46 xslice(const xslice&) = default;
47 xslice& operator=(const xslice&) = default;
48
49 xslice(xslice&&) = default;
50 xslice& operator=(xslice&&) = default;
51 };
52
53 template <class S>
54 using is_xslice = std::is_base_of<xslice<S>, S>;
55
56 template <class... E>
57 using has_xslice = std::disjunction<is_xslice<E>...>;
58
59 /**************
60 * slice tags *
61 **************/
62
63#define DEFINE_TAG_CONVERSION(NAME) \
64 template <class T> \
65 constexpr NAME convert() const noexcept \
66 { \
67 return NAME(); \
68 }
69
70 struct xall_tag
71 {
72 DEFINE_TAG_CONVERSION(xall_tag)
73 };
74
76 {
77 DEFINE_TAG_CONVERSION(xnewaxis_tag)
78 };
79
81 {
82 DEFINE_TAG_CONVERSION(xellipsis_tag)
83 };
84
85#undef DEFINE_TAG_CONVERSION
86
87 /**********************
88 * xrange declaration *
89 **********************/
90
91 template <class T>
92 class xrange : public xslice<xrange<T>>
93 {
94 public:
95
96 using size_type = T;
97 using self_type = xrange<T>;
98
99 xrange() = default;
100 xrange(size_type start_val, size_type stop_val) noexcept;
101
102 template <std::convertible_to<T> S>
103 operator xrange<S>() const noexcept;
104
105 // Same as implicit conversion operator but more convenient to call
106 // from a variant visitor
107 template <std::convertible_to<T> S>
108 xrange<S> convert() const noexcept;
109
110 size_type operator()(size_type i) const noexcept;
111
112 size_type size() const noexcept;
113 size_type step_size() const noexcept;
114 size_type step_size(std::size_t i, std::size_t n = 1) const noexcept;
115 size_type revert_index(std::size_t i) const noexcept;
116
117 bool contains(size_type i) const noexcept;
118
119 bool operator==(const self_type& rhs) const noexcept;
120 bool operator!=(const self_type& rhs) const noexcept;
121
122 private:
123
124 size_type m_start;
125 size_type m_size;
126
127 template <class S>
128 friend class xrange;
129 };
130
131 /******************************
132 * xstepped_range declaration *
133 ******************************/
134
135 template <class T>
136 class xstepped_range : public xslice<xstepped_range<T>>
137 {
138 public:
139
140 using size_type = T;
141 using self_type = xstepped_range<T>;
142
143 xstepped_range() = default;
144 xstepped_range(size_type start_val, size_type stop_val, size_type step) noexcept;
145
146 template <std::convertible_to<T> S>
147 operator xstepped_range<S>() const noexcept;
148
149 // Same as implicit conversion operator but more convenient to call
150 // from a variant visitor
151 template <std::convertible_to<T> S>
152 xstepped_range<S> convert() const noexcept;
153
154 size_type operator()(size_type i) const noexcept;
155
156 size_type size() const noexcept;
157 size_type step_size() const noexcept;
158 size_type step_size(std::size_t i, std::size_t n = 1) const noexcept;
159 size_type revert_index(std::size_t i) const noexcept;
160
161 bool contains(size_type i) const noexcept;
162
163 bool operator==(const self_type& rhs) const noexcept;
164 bool operator!=(const self_type& rhs) const noexcept;
165
166 private:
167
168 size_type m_start;
169 size_type m_size;
170 size_type m_step;
171
172 template <class S>
173 friend class xstepped_range;
174 };
175
176 /********************
177 * xall declaration *
178 ********************/
179
180 template <class T>
181 class xall : public xslice<xall<T>>
182 {
183 public:
184
185 using size_type = T;
186 using self_type = xall<T>;
187
188 xall() = default;
189 explicit xall(size_type size) noexcept;
190
191 template <std::convertible_to<T> S>
192 operator xall<S>() const noexcept;
193
194 // Same as implicit conversion operator but more convenient to call
195 // from a variant visitor
196 template <std::convertible_to<T> S>
197 xall<S> convert() const noexcept;
198
199 size_type operator()(size_type i) const noexcept;
200
201 size_type size() const noexcept;
202 size_type step_size() const noexcept;
203 size_type step_size(std::size_t i, std::size_t n = 1) const noexcept;
204 size_type revert_index(std::size_t i) const noexcept;
205
206 bool contains(size_type i) const noexcept;
207
208 bool operator==(const self_type& rhs) const noexcept;
209 bool operator!=(const self_type& rhs) const noexcept;
210
211 private:
212
213 size_type m_size;
214 };
215
221 inline auto all() noexcept
222 {
223 return xall_tag();
224 }
225
242 inline auto ellipsis() noexcept
243 {
244 return xellipsis_tag();
245 }
246
247 /************************
248 * xnewaxis declaration *
249 ************************/
250
251 template <class T>
252 class xnewaxis : public xslice<xnewaxis<T>>
253 {
254 public:
255
256 using size_type = T;
257 using self_type = xnewaxis<T>;
258
259 xnewaxis() = default;
260
261 template <std::convertible_to<T> S>
262 operator xnewaxis<S>() const noexcept;
263
264 // Same as implicit conversion operator but more convenient to call
265 // from a variant visitor
266 template <std::convertible_to<T> S>
267 xnewaxis<S> convert() const noexcept;
268
269 size_type operator()(size_type i) const noexcept;
270
271 size_type size() const noexcept;
272 size_type step_size() const noexcept;
273 size_type step_size(std::size_t i, std::size_t n = 1) const noexcept;
274 size_type revert_index(std::size_t i) const noexcept;
275
276 bool contains(size_type i) const noexcept;
277
278 bool operator==(const self_type& rhs) const noexcept;
279 bool operator!=(const self_type& rhs) const noexcept;
280 };
281
287 inline auto newaxis() noexcept
288 {
289 return xnewaxis_tag();
290 }
291
292 /***************************
293 * xkeep_slice declaration *
294 ***************************/
295
296 template <class T>
297 class xkeep_slice;
298
299 namespace detail
300 {
301 template <class T>
302 struct is_xkeep_slice : std::false_type
303 {
304 };
305
306 template <class T>
307 struct is_xkeep_slice<xkeep_slice<T>> : std::true_type
308 {
309 };
310 }
311
312 template <class T>
313 class xkeep_slice : public xslice<xkeep_slice<T>>
314 {
315 public:
316
317 using container_type = svector<T>;
318 using size_type = typename container_type::value_type;
319 using self_type = xkeep_slice<T>;
320
321 template <class C>
322 explicit xkeep_slice(C& cont)
323 requires(!detail::is_xkeep_slice<std::decay_t<C>>::value);
324 explicit xkeep_slice(container_type&& cont);
325
326 template <class S>
327 xkeep_slice(std::initializer_list<S> t);
328
329 template <std::convertible_to<T> S>
330 operator xkeep_slice<S>() const noexcept;
331
332 // Same as implicit conversion operator but more convenient to call
333 // from a variant visitor
334 template <std::convertible_to<T> S>
335 xkeep_slice<S> convert() const noexcept;
336
337 size_type operator()(size_type i) const noexcept;
338 size_type size() const noexcept;
339
340 void normalize(std::size_t s);
341
342 size_type step_size(std::size_t i, std::size_t n = 1) const noexcept;
343 size_type revert_index(std::size_t i) const;
344
345 bool contains(size_type i) const noexcept;
346
347 bool operator==(const self_type& rhs) const noexcept;
348 bool operator!=(const self_type& rhs) const noexcept;
349
350 private:
351
352 xkeep_slice() = default;
353
354 container_type m_indices;
355 container_type m_raw_indices;
356
357 template <class S>
358 friend class xkeep_slice;
359 };
360
375 template <class R = std::ptrdiff_t, class T>
376 inline auto keep(T&& indices)
377 {
378 if constexpr (xtl::is_integral<std::decay_t<T>>::value)
379 {
380 using slice_type = xkeep_slice<R>;
381 using container_type = typename slice_type::container_type;
382 container_type tmp = {static_cast<R>(std::forward<T>(indices))};
383 return slice_type(std::move(tmp));
384 }
385 else
386 {
387 return xkeep_slice<typename std::decay_t<T>::value_type>(std::forward<T>(indices));
388 }
389 }
390
391 template <class R = std::ptrdiff_t, class Arg0, class Arg1, class... Args>
392 inline xkeep_slice<R> keep(Arg0 i0, Arg1 i1, Args... args)
393 {
394 using slice_type = xkeep_slice<R>;
395 using container_type = typename slice_type::container_type;
396 container_type tmp = {static_cast<R>(i0), static_cast<R>(i1), static_cast<R>(args)...};
397 return slice_type(std::move(tmp));
398 }
399
400 /***************************
401 * xdrop_slice declaration *
402 ***************************/
403
404 template <class T>
405 class xdrop_slice;
406
407 namespace detail
408 {
409 template <class T>
410 struct is_xdrop_slice : std::false_type
411 {
412 };
413
414 template <class T>
415 struct is_xdrop_slice<xdrop_slice<T>> : std::true_type
416 {
417 };
418 }
419
420 template <class T>
421 class xdrop_slice : public xslice<xdrop_slice<T>>
422 {
423 public:
424
425 using container_type = svector<T>;
426 using size_type = typename container_type::value_type;
427 using self_type = xdrop_slice<T>;
428
429 template <class C>
430 explicit xdrop_slice(C& cont)
431 requires(!detail::is_xdrop_slice<std::decay_t<C>>::value);
432 explicit xdrop_slice(container_type&& cont);
433
434 template <class S>
435 xdrop_slice(std::initializer_list<S> t);
436
437 template <std::convertible_to<T> S>
438 operator xdrop_slice<S>() const noexcept;
439
440 // Same as implicit conversion operator but more convenient to call
441 // from a variant visitor
442 template <std::convertible_to<T> S>
443 xdrop_slice<S> convert() const noexcept;
444
445 size_type operator()(size_type i) const noexcept;
446 size_type size() const noexcept;
447
448 void normalize(std::size_t s);
449
450 size_type step_size(std::size_t i, std::size_t n = 1) const noexcept;
451 size_type revert_index(std::size_t i) const;
452
453 bool contains(size_type i) const noexcept;
454
455 bool operator==(const self_type& rhs) const noexcept;
456 bool operator!=(const self_type& rhs) const noexcept;
457
458 private:
459
460 xdrop_slice() = default;
461
462 container_type m_indices;
463 container_type m_raw_indices;
464 std::map<size_type, size_type> m_inc;
465 size_type m_size;
466
467 template <class S>
468 friend class xdrop_slice;
469 };
470
487 template <class R = std::ptrdiff_t, class T>
488 inline auto drop(T&& indices)
489 {
490 if constexpr (xtl::is_integral<std::remove_cvref_t<T>>::value)
491 {
492 using slice_type = xdrop_slice<R>;
493 using container_type = typename slice_type::container_type;
494 container_type tmp = {static_cast<R>(std::forward<T>(indices))};
495 return slice_type(std::move(tmp));
496 }
497 else
498 {
499 return xdrop_slice<typename std::decay_t<T>::value_type>(std::forward<T>(indices));
500 }
501 }
502
503 template <class R = std::ptrdiff_t, class Arg0, class Arg1, class... Args>
504 inline xdrop_slice<R> drop(Arg0 i0, Arg1 i1, Args... args)
505 {
506 using slice_type = xdrop_slice<R>;
507 using container_type = typename slice_type::container_type;
508 container_type tmp = {static_cast<R>(i0), static_cast<R>(i1), static_cast<R>(args)...};
509 return slice_type(std::move(tmp));
510 }
511
512 /******************************
513 * xrange_adaptor declaration *
514 ******************************/
515
516 template <class A, class B = A, class C = A>
517 struct xrange_adaptor
518 {
519 xrange_adaptor(A start_val, B stop_val, C step)
520 : m_start(start_val)
521 , m_stop(stop_val)
522 , m_step(step)
523 {
524 }
525
526 template <class MI = A, class MA = B, class STEP = C>
527 auto get(std::size_t size) const
528 {
529 if constexpr (xtl::is_integral<MI>::value && xtl::is_integral<MA>::value && xtl::is_integral<STEP>::value)
530 {
531 return get_stepped_range(m_start, m_stop, m_step, size);
532 }
533 else if constexpr (!xtl::is_integral<MI>::value && xtl::is_integral<MA>::value && xtl::is_integral<STEP>::value)
534 {
535 return get_stepped_range(
536 m_step > 0 ? 0 : static_cast<std::ptrdiff_t>(size) - 1,
537 m_stop,
538 m_step,
539 size
540 );
541 }
542 else if constexpr (xtl::is_integral<MI>::value && !xtl::is_integral<MA>::value && xtl::is_integral<STEP>::value)
543 {
544 auto sz = static_cast<std::ptrdiff_t>(size);
545 return get_stepped_range(m_start, m_step > 0 ? sz : -(sz + 1), m_step, size);
546 }
547 else if constexpr (xtl::is_integral<MI>::value && xtl::is_integral<MA>::value && !xtl::is_integral<STEP>::value)
548 {
549 return xrange<std::ptrdiff_t>(normalize(m_start, size), normalize(m_stop, size));
550 }
551 else if constexpr (!xtl::is_integral<MI>::value && !xtl::is_integral<MA>::value && xtl::is_integral<STEP>::value)
552 {
553 std::ptrdiff_t start = m_step >= 0 ? 0 : static_cast<std::ptrdiff_t>(size) - 1;
554 std::ptrdiff_t stop = m_step >= 0 ? static_cast<std::ptrdiff_t>(size) : -1;
555 return xstepped_range<std::ptrdiff_t>(start, stop, m_step);
556 }
557 else if constexpr (xtl::is_integral<MI>::value && !xtl::is_integral<MA>::value && !xtl::is_integral<STEP>::value)
558 {
559 return xrange<std::ptrdiff_t>(normalize(m_start, size), static_cast<std::ptrdiff_t>(size));
560 }
561 else if constexpr (!xtl::is_integral<MI>::value && xtl::is_integral<MA>::value && !xtl::is_integral<STEP>::value)
562 {
563 return xrange<std::ptrdiff_t>(0, normalize(m_stop, size));
564 }
565 else if constexpr (!xtl::is_integral<MI>::value && !xtl::is_integral<MA>::value && !xtl::is_integral<STEP>::value)
566 {
567 return xall<std::ptrdiff_t>(static_cast<std::ptrdiff_t>(size));
568 }
569 }
570
571 A start() const
572 {
573 return m_start;
574 }
575
576 B stop() const
577 {
578 return m_stop;
579 }
580
581 C step() const
582 {
583 return m_step;
584 }
585
586 private:
587
588 static auto normalize(std::ptrdiff_t val, std::size_t ssize)
589 {
590 std::ptrdiff_t size = static_cast<std::ptrdiff_t>(ssize);
591 val = (val >= 0) ? val : val + size;
592 return (std::max)(std::ptrdiff_t(0), (std::min)(size, val));
593 }
594
595 static auto
596 get_stepped_range(std::ptrdiff_t start, std::ptrdiff_t stop, std::ptrdiff_t step, std::size_t ssize)
597 {
598 std::ptrdiff_t size = static_cast<std::ptrdiff_t>(ssize);
599 start = (start >= 0) ? start : start + size;
600 stop = (stop >= 0) ? stop : stop + size;
601
602 if (step > 0)
603 {
604 start = (std::max)(std::ptrdiff_t(0), (std::min)(size, start));
605 stop = (std::max)(std::ptrdiff_t(0), (std::min)(size, stop));
606 }
607 else
608 {
609 start = (std::max)(std::ptrdiff_t(-1), (std::min)(size - 1, start));
610 stop = (std::max)(std::ptrdiff_t(-1), (std::min)(size - 1, stop));
611 }
612
613 return xstepped_range<std::ptrdiff_t>(start, stop, step);
614 }
615
616 A m_start;
617 B m_stop;
618 C m_step;
619 };
620
621 /*******************************
622 * Placeholders and rangemaker *
623 *******************************/
624
625 namespace placeholders
626 {
627 // xtensor universal placeholder
628 struct xtuph
629 {
630 };
631
632 template <class... Args>
634 {
635 std::ptrdiff_t rng[3]; // = { 0, 0, 0 };
636 };
637
638 constexpr xtuph get_tuph_or_val(std::ptrdiff_t /*val*/, std::true_type)
639 {
640 return xtuph();
641 }
642
643 constexpr std::ptrdiff_t get_tuph_or_val(std::ptrdiff_t val, std::false_type)
644 {
645 return val;
646 }
647
648 template <class A, class B, class C>
649 struct rangemaker<A, B, C>
650 {
651 constexpr operator xrange_adaptor<A, B, C>()
652 {
654 {get_tuph_or_val(rng[0], std::is_same<A, xtuph>()),
655 get_tuph_or_val(rng[1], std::is_same<B, xtuph>()),
656 get_tuph_or_val(rng[2], std::is_same<C, xtuph>())}
657 );
658 }
659
660 std::ptrdiff_t rng[3]; // = { 0, 0, 0 };
661 };
662
663 template <class A, class B>
664 struct rangemaker<A, B>
665 {
667 {
669 {get_tuph_or_val(rng[0], std::is_same<A, xtuph>()),
670 get_tuph_or_val(rng[1], std::is_same<B, xtuph>()),
671 xtuph()}
672 );
673 }
674
675 std::ptrdiff_t rng[3]; // = { 0, 0, 0 };
676 };
677
678 template <class... OA>
679 constexpr auto operator|(const rangemaker<OA...>& rng, const std::ptrdiff_t& t)
680 {
681 auto nrng = rangemaker<OA..., std::ptrdiff_t>({rng.rng[0], rng.rng[1], rng.rng[2]});
682 nrng.rng[sizeof...(OA)] = t;
683 return nrng;
684 }
685
686 template <class... OA>
687 constexpr auto operator|(const rangemaker<OA...>& rng, const xt::placeholders::xtuph& /*t*/)
688 {
689 auto nrng = rangemaker<OA..., xt::placeholders::xtuph>({rng.rng[0], rng.rng[1], rng.rng[2]});
690 return nrng;
691 }
692
693 constexpr xtuph _{};
694 constexpr rangemaker<> _r = rangemaker<>({{0, 0, 0}});
695 constexpr xall_tag _a{};
696 constexpr xnewaxis_tag _n{};
697 constexpr xellipsis_tag _e{};
698 }
699
700 inline auto xnone()
701 {
702 return placeholders::xtuph();
703 }
704
705 namespace detail
706 {
707 template <class T>
708 struct cast_if_integer
709 {
710 using type = std::conditional_t<xtl::is_integral<T>::value, std::ptrdiff_t, T>;
711
712 type operator()(T t)
713 {
714 return static_cast<type>(t);
715 }
716 };
717
718 template <class T>
719 using cast_if_integer_t = typename cast_if_integer<T>::type;
720 }
721
736 template <class A, class B>
737 inline auto range(A start_val, B stop_val)
738 {
739 return xrange_adaptor<detail::cast_if_integer_t<A>, detail::cast_if_integer_t<B>, placeholders::xtuph>(
740 detail::cast_if_integer<A>{}(start_val),
741 detail::cast_if_integer<B>{}(stop_val),
743 );
744 }
745
757 template <class A, class B, class C>
758 inline auto range(A start_val, B stop_val, C step)
759 {
760 return xrange_adaptor<detail::cast_if_integer_t<A>, detail::cast_if_integer_t<B>, detail::cast_if_integer_t<C>>(
761 detail::cast_if_integer<A>{}(start_val),
762 detail::cast_if_integer<B>{}(stop_val),
763 detail::cast_if_integer<C>{}(step)
764 );
765 }
766
767 /******************************************************
768 * homogeneous get_size for integral types and slices *
769 ******************************************************/
770
771 template <class S>
772 inline std::size_t get_size(const S& slice) noexcept
773 {
774 if constexpr (is_xslice<S>::value)
775 {
776 return static_cast<std::size_t>(slice.size());
777 }
778 else
779 {
780 return 1;
781 }
782 }
783
784 /*******************************************************
785 * homogeneous step_size for integral types and slices *
786 *******************************************************/
787
788 template <class S>
789 inline std::size_t step_size(const S& slice, std::size_t idx) noexcept
790 {
791 if constexpr (is_xslice<S>::value)
792 {
793 return static_cast<std::size_t>(slice.step_size(idx));
794 }
795 else
796 {
797 return 0;
798 }
799 }
800
801 template <class S>
802 inline std::size_t step_size(const S& slice, std::size_t idx, std::size_t n) noexcept
803 {
804 if constexpr (is_xslice<S>::value)
805 {
806 return static_cast<std::size_t>(slice.step_size(idx, n));
807 }
808 else
809 {
810 return 0;
811 }
812 }
813
814 /*********************************************
815 * homogeneous value for integral and slices *
816 *********************************************/
817
818 template <class S, class I>
819 inline std::size_t value(const S& slice, I i) noexcept
820 {
821 if constexpr (is_xslice<S>::value)
822 {
823 using ST = typename S::size_type;
824 return static_cast<std::size_t>(slice(static_cast<ST>(i)));
825 }
826 else
827 {
828 return static_cast<std::size_t>(slice);
829 }
830 }
831
832 /****************************************
833 * homogeneous get_slice_implementation *
834 ****************************************/
835
836 namespace detail
837 {
838 template <class T>
839 struct slice_implementation_getter
840 {
841 template <class E, class SL>
842 inline decltype(auto) operator()(E& e, SL&& slice, std::size_t index) const
843 {
844 return get_slice(e, std::forward<SL>(slice), index, xtl::is_signed<std::decay_t<SL>>());
845 }
846
847 private:
848
849 template <class E, class SL>
850 inline decltype(auto) get_slice(E&, SL&& slice, std::size_t, std::false_type) const
851 {
852 return std::forward<SL>(slice);
853 }
854
855 template <class E, class SL>
856 inline decltype(auto) get_slice(E& e, SL&& slice, std::size_t index, std::true_type) const
857 {
858 using int_type = std::decay_t<SL>;
859 return slice < int_type(0) ? slice + static_cast<std::ptrdiff_t>(e.shape(index))
860 : std::ptrdiff_t(slice);
861 }
862 };
863
864 struct keep_drop_getter
865 {
866 template <class E, class SL>
867 inline decltype(auto) operator()(E& e, SL&& slice, std::size_t index) const
868 {
869 slice.normalize(e.shape()[index]);
870 return std::forward<SL>(slice);
871 }
872
873 template <class E, class SL>
874 inline auto operator()(E& e, const SL& slice, std::size_t index) const
875 {
876 return this->operator()(e, SL(slice), index);
877 }
878 };
879
880 template <class T>
881 struct slice_implementation_getter<xkeep_slice<T>> : keep_drop_getter
882 {
883 };
884
885 template <class T>
886 struct slice_implementation_getter<xdrop_slice<T>> : keep_drop_getter
887 {
888 };
889
890 template <>
891 struct slice_implementation_getter<xall_tag>
892 {
893 template <class E, class SL>
894 inline auto operator()(E& e, SL&&, std::size_t index) const
895 {
896 return xall<typename E::size_type>(e.shape()[index]);
897 }
898 };
899
900 template <>
901 struct slice_implementation_getter<xnewaxis_tag>
902 {
903 template <class E, class SL>
904 inline auto operator()(E&, SL&&, std::size_t) const
905 {
906 return xnewaxis<typename E::size_type>();
907 }
908 };
909
910 template <class A, class B, class C>
911 struct slice_implementation_getter<xrange_adaptor<A, B, C>>
912 {
913 template <class E, class SL>
914 inline auto operator()(E& e, SL&& adaptor, std::size_t index) const
915 {
916 return adaptor.get(e.shape()[index]);
917 }
918 };
919 }
920
921 template <class E, class SL>
922 inline auto get_slice_implementation(E& e, SL&& slice, std::size_t index)
923 {
924 detail::slice_implementation_getter<std::decay_t<SL>> getter;
925 return getter(e, std::forward<SL>(slice), index);
926 }
927
928 /******************************
929 * homogeneous get_slice_type *
930 ******************************/
931
932 namespace detail
933 {
934 template <class E, class SL>
935 struct get_slice_type_impl
936 {
937 using type = SL;
938 };
939
940 template <class E>
941 struct get_slice_type_impl<E, xall_tag>
942 {
943 using type = xall<typename E::size_type>;
944 };
945
946 template <class E>
947 struct get_slice_type_impl<E, xnewaxis_tag>
948 {
949 using type = xnewaxis<typename E::size_type>;
950 };
951
952 template <class E, class A, class B, class C>
953 struct get_slice_type_impl<E, xrange_adaptor<A, B, C>>
954 {
955 using type = decltype(xrange_adaptor<A, B, C>(A(), B(), C()).get(0));
956 };
957 }
958
959 template <class E, class SL>
960 using get_slice_type = typename detail::get_slice_type_impl<E, std::remove_reference_t<SL>>::type;
961
962 /*************************
963 * xslice implementation *
964 *************************/
965
966 template <class D>
967 inline auto xslice<D>::derived_cast() noexcept -> derived_type&
968 {
969 return *static_cast<derived_type*>(this);
970 }
971
972 template <class D>
973 inline auto xslice<D>::derived_cast() const noexcept -> const derived_type&
974 {
975 return *static_cast<const derived_type*>(this);
976 }
977
978 /*************************
979 * xrange implementation *
980 *************************/
981
982 template <class T>
983 inline xrange<T>::xrange(size_type start_val, size_type stop_val) noexcept
984 : m_start(start_val)
985 , m_size(stop_val > start_val ? stop_val - start_val : 0)
986 {
987 }
988
989 template <class T>
990 template <std::convertible_to<T> S>
991 inline xrange<T>::operator xrange<S>() const noexcept
992 {
993 xrange<S> ret;
994 ret.m_start = static_cast<S>(m_start);
995 ret.m_size = static_cast<S>(m_size);
996 return ret;
997 }
998
999 template <class T>
1000 template <std::convertible_to<T> S>
1001 inline xrange<S> xrange<T>::convert() const noexcept
1002 {
1003 return xrange<S>(*this);
1004 }
1005
1006 template <class T>
1007 inline auto xrange<T>::operator()(size_type i) const noexcept -> size_type
1008 {
1009 return m_start + i;
1010 }
1011
1012 template <class T>
1013 inline auto xrange<T>::size() const noexcept -> size_type
1014 {
1015 return m_size;
1016 }
1017
1018 template <class T>
1019 inline auto xrange<T>::step_size() const noexcept -> size_type
1020 {
1021 return 1;
1022 }
1023
1024 template <class T>
1025 inline auto xrange<T>::step_size(std::size_t /*i*/, std::size_t n) const noexcept -> size_type
1026 {
1027 return static_cast<size_type>(n);
1028 }
1029
1030 template <class T>
1031 inline auto xrange<T>::revert_index(std::size_t i) const noexcept -> size_type
1032 {
1033 return i - m_start;
1034 }
1035
1036 template <class T>
1037 inline bool xrange<T>::contains(size_type i) const noexcept
1038 {
1039 return i >= m_start && i < m_start + m_size;
1040 }
1041
1042 template <class T>
1043 inline bool xrange<T>::operator==(const self_type& rhs) const noexcept
1044 {
1045 return (m_start == rhs.m_start) && (m_size == rhs.m_size);
1046 }
1047
1048 template <class T>
1049 inline bool xrange<T>::operator!=(const self_type& rhs) const noexcept
1050 {
1051 return !(*this == rhs);
1052 }
1053
1054 /********************************
1055 * xtepped_range implementation *
1056 ********************************/
1057
1058 template <class T>
1059 inline xstepped_range<T>::xstepped_range(size_type start_val, size_type stop_val, size_type step) noexcept
1060 : m_start(start_val)
1061 , m_size(size_type(0))
1062 , m_step(step)
1063 {
1064 if ((step > 0 && start_val >= stop_val) || (step < 0 && start_val <= stop_val))
1065 {
1066 m_size = 0;
1067 return;
1068 }
1069 size_type n = stop_val - start_val;
1070 m_size = n / step + static_cast<bool>(n % step);
1071 }
1072
1073 template <class T>
1074 template <std::convertible_to<T> S>
1075 inline xstepped_range<T>::operator xstepped_range<S>() const noexcept
1076 {
1078 ret.m_start = static_cast<S>(m_start);
1079 ret.m_size = static_cast<S>(m_size);
1080 ret.m_step = static_cast<S>(m_step);
1081 return ret;
1082 }
1083
1084 template <class T>
1085 template <std::convertible_to<T> S>
1086 inline xstepped_range<S> xstepped_range<T>::convert() const noexcept
1087 {
1088 return xstepped_range<S>(*this);
1089 }
1090
1091 template <class T>
1092 inline auto xstepped_range<T>::operator()(size_type i) const noexcept -> size_type
1093 {
1094 return m_start + i * m_step;
1095 }
1096
1097 template <class T>
1098 inline auto xstepped_range<T>::size() const noexcept -> size_type
1099 {
1100 return m_size;
1101 }
1102
1103 template <class T>
1104 inline auto xstepped_range<T>::step_size() const noexcept -> size_type
1105 {
1106 return m_step;
1107 }
1108
1109 template <class T>
1110 inline auto xstepped_range<T>::step_size(std::size_t /*i*/, std::size_t n) const noexcept -> size_type
1111 {
1112 return m_step * static_cast<size_type>(n);
1113 }
1114
1115 template <class T>
1116 inline auto xstepped_range<T>::revert_index(std::size_t i) const noexcept -> size_type
1117 {
1118 return (i - m_start) / m_step;
1119 }
1120
1121 template <class T>
1122 inline bool xstepped_range<T>::contains(size_type i) const noexcept
1123 {
1124 return i >= m_start && i < m_start + m_size * m_step && ((i - m_start) % m_step == 0);
1125 }
1126
1127 template <class T>
1128 inline bool xstepped_range<T>::operator==(const self_type& rhs) const noexcept
1129 {
1130 return (m_start == rhs.m_start) && (m_size == rhs.m_size) && (m_step == rhs.m_step);
1131 }
1132
1133 template <class T>
1134 inline bool xstepped_range<T>::operator!=(const self_type& rhs) const noexcept
1135 {
1136 return !(*this == rhs);
1137 }
1138
1139 /***********************
1140 * xall implementation *
1141 ***********************/
1142
1143 template <class T>
1144 inline xall<T>::xall(size_type size) noexcept
1145 : m_size(size)
1146 {
1147 }
1148
1149 template <class T>
1150 template <std::convertible_to<T> S>
1151 inline xall<T>::operator xall<S>() const noexcept
1152 {
1153 return xall<S>(static_cast<S>(m_size));
1154 }
1155
1156 template <class T>
1157 template <std::convertible_to<T> S>
1158 inline xall<S> xall<T>::convert() const noexcept
1159 {
1160 return xall<S>(*this);
1161 }
1162
1163 template <class T>
1164 inline auto xall<T>::operator()(size_type i) const noexcept -> size_type
1165 {
1166 return i;
1167 }
1168
1169 template <class T>
1170 inline auto xall<T>::size() const noexcept -> size_type
1171 {
1172 return m_size;
1173 }
1174
1175 template <class T>
1176 inline auto xall<T>::step_size() const noexcept -> size_type
1177 {
1178 return 1;
1179 }
1180
1181 template <class T>
1182 inline auto xall<T>::step_size(std::size_t /*i*/, std::size_t n) const noexcept -> size_type
1183 {
1184 return static_cast<size_type>(n);
1185 }
1186
1187 template <class T>
1188 inline auto xall<T>::revert_index(std::size_t i) const noexcept -> size_type
1189 {
1190 return i;
1191 }
1192
1193 template <class T>
1194 inline bool xall<T>::contains(size_type i) const noexcept
1195 {
1196 return i < m_size;
1197 }
1198
1199 template <class T>
1200 inline bool xall<T>::operator==(const self_type& rhs) const noexcept
1201 {
1202 return m_size == rhs.m_size;
1203 }
1204
1205 template <class T>
1206 inline bool xall<T>::operator!=(const self_type& rhs) const noexcept
1207 {
1208 return !(*this == rhs);
1209 }
1210
1211 /***************************
1212 * xnewaxis implementation *
1213 ***************************/
1214
1215 template <class T>
1216 template <std::convertible_to<T> S>
1217 inline xnewaxis<T>::operator xnewaxis<S>() const noexcept
1218 {
1219 return xnewaxis<S>();
1220 }
1221
1222 template <class T>
1223 template <std::convertible_to<T> S>
1224 inline xnewaxis<S> xnewaxis<T>::convert() const noexcept
1225 {
1226 return xnewaxis<S>(*this);
1227 }
1228
1229 template <class T>
1230 inline auto xnewaxis<T>::operator()(size_type) const noexcept -> size_type
1231 {
1232 return 0;
1233 }
1234
1235 template <class T>
1236 inline auto xnewaxis<T>::size() const noexcept -> size_type
1237 {
1238 return 1;
1239 }
1240
1241 template <class T>
1242 inline auto xnewaxis<T>::step_size() const noexcept -> size_type
1243 {
1244 return 0;
1245 }
1246
1247 template <class T>
1248 inline auto xnewaxis<T>::step_size(std::size_t /*i*/, std::size_t /*n*/) const noexcept -> size_type
1249 {
1250 return 0;
1251 }
1252
1253 template <class T>
1254 inline auto xnewaxis<T>::revert_index(std::size_t i) const noexcept -> size_type
1255 {
1256 return i;
1257 }
1258
1259 template <class T>
1260 inline bool xnewaxis<T>::contains(size_type i) const noexcept
1261 {
1262 return i == 0;
1263 }
1264
1265 template <class T>
1266 inline bool xnewaxis<T>::operator==(const self_type& /*rhs*/) const noexcept
1267 {
1268 return true;
1269 }
1270
1271 template <class T>
1272 inline bool xnewaxis<T>::operator!=(const self_type& /*rhs*/) const noexcept
1273 {
1274 return true;
1275 }
1276
1277 /******************************
1278 * xkeep_slice implementation *
1279 ******************************/
1280
1281 template <class T>
1282 template <class C>
1283 inline xkeep_slice<T>::xkeep_slice(C& cont)
1284 requires(!detail::is_xkeep_slice<std::decay_t<C>>::value)
1285 : m_raw_indices(cont.begin(), cont.end())
1286 {
1287 }
1288
1289 template <class T>
1290 inline xkeep_slice<T>::xkeep_slice(container_type&& cont)
1291 : m_raw_indices(std::move(cont))
1292 {
1293 }
1294
1295 template <class T>
1296 template <class S>
1297 inline xkeep_slice<T>::xkeep_slice(std::initializer_list<S> t)
1298 : m_raw_indices(t.size())
1299 {
1300 std::transform(
1301 t.begin(),
1302 t.end(),
1303 m_raw_indices.begin(),
1304 [](auto t)
1305 {
1306 return static_cast<size_type>(t);
1307 }
1308 );
1309 }
1310
1311 template <class T>
1312 template <std::convertible_to<T> S>
1313 inline xkeep_slice<T>::operator xkeep_slice<S>() const noexcept
1314 {
1315 xkeep_slice<S> ret;
1316 using us_type = typename container_type::size_type;
1317 us_type sz = static_cast<us_type>(size());
1318 ret.m_raw_indices.resize(sz);
1319 ret.m_indices.resize(sz);
1320 std::transform(
1321 m_raw_indices.cbegin(),
1322 m_raw_indices.cend(),
1323 ret.m_raw_indices.begin(),
1324 [](const T& val)
1325 {
1326 return static_cast<S>(val);
1327 }
1328 );
1329 std::transform(
1330 m_indices.cbegin(),
1331 m_indices.cend(),
1332 ret.m_indices.begin(),
1333 [](const T& val)
1334 {
1335 return static_cast<S>(val);
1336 }
1337 );
1338 return ret;
1339 }
1340
1341 template <class T>
1342 template <std::convertible_to<T> S>
1343 inline xkeep_slice<S> xkeep_slice<T>::convert() const noexcept
1344 {
1345 return xkeep_slice<S>(*this);
1346 }
1347
1348 template <class T>
1349 inline void xkeep_slice<T>::normalize(std::size_t shape)
1350 {
1351 m_indices.resize(m_raw_indices.size());
1352 std::size_t sz = m_indices.size();
1353 for (std::size_t i = 0; i < sz; ++i)
1354 {
1355 m_indices[i] = m_raw_indices[i] < 0 ? static_cast<size_type>(shape) + m_raw_indices[i]
1356 : m_raw_indices[i];
1357 }
1358 }
1359
1360 template <class T>
1361 inline auto xkeep_slice<T>::operator()(size_type i) const noexcept -> size_type
1362 {
1363 return m_indices.size() == size_type(1) ? m_indices.front() : m_indices[static_cast<std::size_t>(i)];
1364 }
1365
1366 template <class T>
1367 inline auto xkeep_slice<T>::size() const noexcept -> size_type
1368 {
1369 return static_cast<size_type>(m_raw_indices.size());
1370 }
1371
1372 template <class T>
1373 inline auto xkeep_slice<T>::step_size(std::size_t i, std::size_t n) const noexcept -> size_type
1374 {
1375 if (m_indices.size() == 1)
1376 {
1377 return 0;
1378 }
1379 if (i + n >= m_indices.size())
1380 {
1381 return m_indices.back() - m_indices[i] + 1;
1382 }
1383 else
1384 {
1385 return m_indices[i + n] - m_indices[i];
1386 }
1387 }
1388
1389 template <class T>
1390 inline auto xkeep_slice<T>::revert_index(std::size_t i) const -> size_type
1391 {
1392 auto it = std::find(m_indices.begin(), m_indices.end(), i);
1393 if (it != m_indices.end())
1394 {
1395 return std::distance(m_indices.begin(), it);
1396 }
1397 else
1398 {
1399 XTENSOR_THROW(std::runtime_error, "Index i (" + std::to_string(i) + ") not in indices of islice.");
1400 }
1401 }
1402
1403 template <class T>
1404 inline bool xkeep_slice<T>::contains(size_type i) const noexcept
1405 {
1406 return (std::find(m_indices.begin(), m_indices.end(), i) == m_indices.end()) ? false : true;
1407 }
1408
1409 template <class T>
1410 inline bool xkeep_slice<T>::operator==(const self_type& rhs) const noexcept
1411 {
1412 return m_indices == rhs.m_indices;
1413 }
1414
1415 template <class T>
1416 inline bool xkeep_slice<T>::operator!=(const self_type& rhs) const noexcept
1417 {
1418 return !(*this == rhs);
1419 }
1420
1421 /******************************
1422 * xdrop_slice implementation *
1423 ******************************/
1424
1425 template <class T>
1426 template <class C>
1427 inline xdrop_slice<T>::xdrop_slice(C& cont)
1428 requires(!detail::is_xdrop_slice<std::decay_t<C>>::value)
1429 : m_raw_indices(cont.begin(), cont.end())
1430 {
1431 }
1432
1433 template <class T>
1434 inline xdrop_slice<T>::xdrop_slice(container_type&& cont)
1435 : m_raw_indices(std::move(cont))
1436 {
1437 }
1438
1439 template <class T>
1440 template <class S>
1441 inline xdrop_slice<T>::xdrop_slice(std::initializer_list<S> t)
1442 : m_raw_indices(t.size())
1443 {
1444 std::transform(
1445 t.begin(),
1446 t.end(),
1447 m_raw_indices.begin(),
1448 [](auto t)
1449 {
1450 return static_cast<size_type>(t);
1451 }
1452 );
1453 }
1454
1455 template <class T>
1456 template <std::convertible_to<T> S>
1457 inline xdrop_slice<T>::operator xdrop_slice<S>() const noexcept
1458 {
1459 xdrop_slice<S> ret;
1460 ret.m_raw_indices.resize(m_raw_indices.size());
1461 ret.m_indices.resize(m_indices.size());
1462 std::transform(
1463 m_raw_indices.cbegin(),
1464 m_raw_indices.cend(),
1465 ret.m_raw_indices.begin(),
1466 [](const T& val)
1467 {
1468 return static_cast<S>(val);
1469 }
1470 );
1471 std::transform(
1472 m_indices.cbegin(),
1473 m_indices.cend(),
1474 ret.m_indices.begin(),
1475 [](const T& val)
1476 {
1477 return static_cast<S>(val);
1478 }
1479 );
1480 std::transform(
1481 m_inc.cbegin(),
1482 m_inc.cend(),
1483 std::inserter(ret.m_inc, ret.m_inc.begin()),
1484 [](const auto& val)
1485 {
1486 return std::make_pair(static_cast<S>(val.first), static_cast<S>(val.second));
1487 }
1488 );
1489 ret.m_size = static_cast<S>(m_size);
1490 return ret;
1491 }
1492
1493 template <class T>
1494 template <std::convertible_to<T> S>
1495 inline xdrop_slice<S> xdrop_slice<T>::convert() const noexcept
1496 {
1497 return xdrop_slice<S>(*this);
1498 }
1499
1500 template <class T>
1501 inline void xdrop_slice<T>::normalize(std::size_t shape)
1502 {
1503 m_size = static_cast<size_type>(shape - m_raw_indices.size());
1504
1505 m_indices.resize(m_raw_indices.size());
1506 std::size_t sz = m_indices.size();
1507 for (std::size_t i = 0; i < sz; ++i)
1508 {
1509 m_indices[i] = m_raw_indices[i] < 0 ? static_cast<size_type>(shape) + m_raw_indices[i]
1510 : m_raw_indices[i];
1511 }
1512 m_inc.clear();
1513 size_type cum = size_type(0);
1514 size_type prev_cum = cum;
1515 for (std::size_t i = 0; i < sz; ++i)
1516 {
1517 std::size_t ind = i;
1518 size_type d = m_indices[i];
1519 while (i + 1 < sz && m_indices[i + 1] == m_indices[i] + 1)
1520 {
1521 ++i;
1522 }
1523 cum += (static_cast<size_type>(i) - static_cast<size_type>(ind)) + 1;
1524 m_inc[d - prev_cum] = cum;
1525 prev_cum = cum;
1526 }
1527 }
1528
1529 template <class T>
1530 inline auto xdrop_slice<T>::operator()(size_type i) const noexcept -> size_type
1531 {
1532 if (m_inc.empty() || i < m_inc.begin()->first)
1533 {
1534 return i;
1535 }
1536 else
1537 {
1538 auto iter = --m_inc.upper_bound(i);
1539 return i + iter->second;
1540 }
1541 }
1542
1543 template <class T>
1544 inline auto xdrop_slice<T>::size() const noexcept -> size_type
1545 {
1546 return m_size;
1547 }
1548
1549 template <class T>
1550 inline auto xdrop_slice<T>::step_size(std::size_t i, std::size_t n) const noexcept -> size_type
1551 {
1552 if (i + n >= static_cast<std::size_t>(m_size))
1553 {
1554 return (*this)(static_cast<size_type>(m_size - 1)) - (*this)(static_cast<size_type>(i)) + 1;
1555 }
1556 else
1557 {
1558 return (*this)(static_cast<size_type>(i + n)) - (*this)(static_cast<size_type>(i));
1559 }
1560 }
1561
1562 template <class T>
1563 inline auto xdrop_slice<T>::revert_index(std::size_t i) const -> size_type
1564 {
1565 if (i < m_inc.begin()->first)
1566 {
1567 return i;
1568 }
1569 else
1570 {
1571 auto iter = --m_inc.lower_bound(i);
1572 auto check = iter->first + iter->second;
1573 if (check > i)
1574 {
1575 --iter;
1576 }
1577 return i - iter->second;
1578 }
1579 }
1580
1581 template <class T>
1582 inline bool xdrop_slice<T>::contains(size_type i) const noexcept
1583 {
1584 return (std::find(m_indices.begin(), m_indices.end(), i) == m_indices.end()) ? true : false;
1585 }
1586
1587 template <class T>
1588 inline bool xdrop_slice<T>::operator==(const self_type& rhs) const noexcept
1589 {
1590 return m_indices == rhs.m_indices;
1591 }
1592
1593 template <class T>
1594 inline bool xdrop_slice<T>::operator!=(const self_type& rhs) const noexcept
1595 {
1596 return !(*this == rhs);
1597 }
1598}
1599
1600#endif
bool all(E &&e)
Any.
standard mathematical functions for xexpressions
auto range(A start_val, B stop_val)
Select a range from start_val to stop_val (excluded).
Definition xslice.hpp:737
auto newaxis() noexcept
Returns a slice representing a new axis of length one, to be used as an argument of view function.
Definition xslice.hpp:287
auto ellipsis() noexcept
Returns a slice representing all remaining dimensions, and selecting all in these dimensions.
Definition xslice.hpp:242
auto drop(T &&indices)
Create a non-contigous slice from a container of indices to drop.
Definition xslice.hpp:488
auto keep(T &&indices)
Create a non-contigous slice from a container of indices to keep.
Definition xslice.hpp:376