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
484 template <class R = std::ptrdiff_t, class T>
485 inline auto drop(T&& indices)
486 {
487 if constexpr (xtl::is_integral<std::remove_cvref_t<T>>::value)
488 {
489 using slice_type = xdrop_slice<R>;
490 using container_type = typename slice_type::container_type;
491 container_type tmp = {static_cast<R>(std::forward<T>(indices))};
492 return slice_type(std::move(tmp));
493 }
494 else
495 {
496 return xdrop_slice<typename std::decay_t<T>::value_type>(std::forward<T>(indices));
497 }
498 }
499
500 template <class R = std::ptrdiff_t, class Arg0, class Arg1, class... Args>
501 inline xdrop_slice<R> drop(Arg0 i0, Arg1 i1, Args... args)
502 {
503 using slice_type = xdrop_slice<R>;
504 using container_type = typename slice_type::container_type;
505 container_type tmp = {static_cast<R>(i0), static_cast<R>(i1), static_cast<R>(args)...};
506 return slice_type(std::move(tmp));
507 }
508
509 /******************************
510 * xrange_adaptor declaration *
511 ******************************/
512
513 template <class A, class B = A, class C = A>
514 struct xrange_adaptor
515 {
516 xrange_adaptor(A start_val, B stop_val, C step)
517 : m_start(start_val)
518 , m_stop(stop_val)
519 , m_step(step)
520 {
521 }
522
523 template <class MI = A, class MA = B, class STEP = C>
524 auto get(std::size_t size) const
525 {
526 if constexpr (xtl::is_integral<MI>::value && xtl::is_integral<MA>::value && xtl::is_integral<STEP>::value)
527 {
528 return get_stepped_range(m_start, m_stop, m_step, size);
529 }
530 else if constexpr (!xtl::is_integral<MI>::value && xtl::is_integral<MA>::value && xtl::is_integral<STEP>::value)
531 {
532 return get_stepped_range(
533 m_step > 0 ? 0 : static_cast<std::ptrdiff_t>(size) - 1,
534 m_stop,
535 m_step,
536 size
537 );
538 }
539 else if constexpr (xtl::is_integral<MI>::value && !xtl::is_integral<MA>::value && xtl::is_integral<STEP>::value)
540 {
541 auto sz = static_cast<std::ptrdiff_t>(size);
542 return get_stepped_range(m_start, m_step > 0 ? sz : -(sz + 1), m_step, size);
543 }
544 else if constexpr (xtl::is_integral<MI>::value && xtl::is_integral<MA>::value && !xtl::is_integral<STEP>::value)
545 {
546 return xrange<std::ptrdiff_t>(normalize(m_start, size), normalize(m_stop, size));
547 }
548 else if constexpr (!xtl::is_integral<MI>::value && !xtl::is_integral<MA>::value && xtl::is_integral<STEP>::value)
549 {
550 std::ptrdiff_t start = m_step >= 0 ? 0 : static_cast<std::ptrdiff_t>(size) - 1;
551 std::ptrdiff_t stop = m_step >= 0 ? static_cast<std::ptrdiff_t>(size) : -1;
552 return xstepped_range<std::ptrdiff_t>(start, stop, m_step);
553 }
554 else if constexpr (xtl::is_integral<MI>::value && !xtl::is_integral<MA>::value && !xtl::is_integral<STEP>::value)
555 {
556 return xrange<std::ptrdiff_t>(normalize(m_start, size), static_cast<std::ptrdiff_t>(size));
557 }
558 else if constexpr (!xtl::is_integral<MI>::value && xtl::is_integral<MA>::value && !xtl::is_integral<STEP>::value)
559 {
560 return xrange<std::ptrdiff_t>(0, normalize(m_stop, size));
561 }
562 else if constexpr (!xtl::is_integral<MI>::value && !xtl::is_integral<MA>::value && !xtl::is_integral<STEP>::value)
563 {
564 return xall<std::ptrdiff_t>(static_cast<std::ptrdiff_t>(size));
565 }
566 }
567
568 A start() const
569 {
570 return m_start;
571 }
572
573 B stop() const
574 {
575 return m_stop;
576 }
577
578 C step() const
579 {
580 return m_step;
581 }
582
583 private:
584
585 static auto normalize(std::ptrdiff_t val, std::size_t ssize)
586 {
587 std::ptrdiff_t size = static_cast<std::ptrdiff_t>(ssize);
588 val = (val >= 0) ? val : val + size;
589 return (std::max)(std::ptrdiff_t(0), (std::min)(size, val));
590 }
591
592 static auto
593 get_stepped_range(std::ptrdiff_t start, std::ptrdiff_t stop, std::ptrdiff_t step, std::size_t ssize)
594 {
595 std::ptrdiff_t size = static_cast<std::ptrdiff_t>(ssize);
596 start = (start >= 0) ? start : start + size;
597 stop = (stop >= 0) ? stop : stop + size;
598
599 if (step > 0)
600 {
601 start = (std::max)(std::ptrdiff_t(0), (std::min)(size, start));
602 stop = (std::max)(std::ptrdiff_t(0), (std::min)(size, stop));
603 }
604 else
605 {
606 start = (std::max)(std::ptrdiff_t(-1), (std::min)(size - 1, start));
607 stop = (std::max)(std::ptrdiff_t(-1), (std::min)(size - 1, stop));
608 }
609
610 return xstepped_range<std::ptrdiff_t>(start, stop, step);
611 }
612
613 A m_start;
614 B m_stop;
615 C m_step;
616 };
617
618 /*******************************
619 * Placeholders and rangemaker *
620 *******************************/
621
622 namespace placeholders
623 {
624 // xtensor universal placeholder
625 struct xtuph
626 {
627 };
628
629 template <class... Args>
631 {
632 std::ptrdiff_t rng[3]; // = { 0, 0, 0 };
633 };
634
635 constexpr xtuph get_tuph_or_val(std::ptrdiff_t /*val*/, std::true_type)
636 {
637 return xtuph();
638 }
639
640 constexpr std::ptrdiff_t get_tuph_or_val(std::ptrdiff_t val, std::false_type)
641 {
642 return val;
643 }
644
645 template <class A, class B, class C>
646 struct rangemaker<A, B, C>
647 {
648 constexpr operator xrange_adaptor<A, B, C>()
649 {
651 {get_tuph_or_val(rng[0], std::is_same<A, xtuph>()),
652 get_tuph_or_val(rng[1], std::is_same<B, xtuph>()),
653 get_tuph_or_val(rng[2], std::is_same<C, xtuph>())}
654 );
655 }
656
657 std::ptrdiff_t rng[3]; // = { 0, 0, 0 };
658 };
659
660 template <class A, class B>
661 struct rangemaker<A, B>
662 {
664 {
666 {get_tuph_or_val(rng[0], std::is_same<A, xtuph>()),
667 get_tuph_or_val(rng[1], std::is_same<B, xtuph>()),
668 xtuph()}
669 );
670 }
671
672 std::ptrdiff_t rng[3]; // = { 0, 0, 0 };
673 };
674
675 template <class... OA>
676 constexpr auto operator|(const rangemaker<OA...>& rng, const std::ptrdiff_t& t)
677 {
678 auto nrng = rangemaker<OA..., std::ptrdiff_t>({rng.rng[0], rng.rng[1], rng.rng[2]});
679 nrng.rng[sizeof...(OA)] = t;
680 return nrng;
681 }
682
683 template <class... OA>
684 constexpr auto operator|(const rangemaker<OA...>& rng, const xt::placeholders::xtuph& /*t*/)
685 {
686 auto nrng = rangemaker<OA..., xt::placeholders::xtuph>({rng.rng[0], rng.rng[1], rng.rng[2]});
687 return nrng;
688 }
689
690 constexpr xtuph _{};
691 constexpr rangemaker<> _r = rangemaker<>({{0, 0, 0}});
692 constexpr xall_tag _a{};
693 constexpr xnewaxis_tag _n{};
694 constexpr xellipsis_tag _e{};
695 }
696
697 inline auto xnone()
698 {
699 return placeholders::xtuph();
700 }
701
702 namespace detail
703 {
704 template <class T>
705 struct cast_if_integer
706 {
707 using type = std::conditional_t<xtl::is_integral<T>::value, std::ptrdiff_t, T>;
708
709 type operator()(T t)
710 {
711 return static_cast<type>(t);
712 }
713 };
714
715 template <class T>
716 using cast_if_integer_t = typename cast_if_integer<T>::type;
717 }
718
733 template <class A, class B>
734 inline auto range(A start_val, B stop_val)
735 {
736 return xrange_adaptor<detail::cast_if_integer_t<A>, detail::cast_if_integer_t<B>, placeholders::xtuph>(
737 detail::cast_if_integer<A>{}(start_val),
738 detail::cast_if_integer<B>{}(stop_val),
740 );
741 }
742
754 template <class A, class B, class C>
755 inline auto range(A start_val, B stop_val, C step)
756 {
757 return xrange_adaptor<detail::cast_if_integer_t<A>, detail::cast_if_integer_t<B>, detail::cast_if_integer_t<C>>(
758 detail::cast_if_integer<A>{}(start_val),
759 detail::cast_if_integer<B>{}(stop_val),
760 detail::cast_if_integer<C>{}(step)
761 );
762 }
763
764 /******************************************************
765 * homogeneous get_size for integral types and slices *
766 ******************************************************/
767
768 template <class S>
769 inline std::size_t get_size(const S& slice) noexcept
770 {
771 if constexpr (is_xslice<S>::value)
772 {
773 return static_cast<std::size_t>(slice.size());
774 }
775 else
776 {
777 return 1;
778 }
779 }
780
781 /*******************************************************
782 * homogeneous step_size for integral types and slices *
783 *******************************************************/
784
785 template <class S>
786 inline std::size_t step_size(const S& slice, std::size_t idx) noexcept
787 {
788 if constexpr (is_xslice<S>::value)
789 {
790 return static_cast<std::size_t>(slice.step_size(idx));
791 }
792 else
793 {
794 return 0;
795 }
796 }
797
798 template <class S>
799 inline std::size_t step_size(const S& slice, std::size_t idx, std::size_t n) noexcept
800 {
801 if constexpr (is_xslice<S>::value)
802 {
803 return static_cast<std::size_t>(slice.step_size(idx, n));
804 }
805 else
806 {
807 return 0;
808 }
809 }
810
811 /*********************************************
812 * homogeneous value for integral and slices *
813 *********************************************/
814
815 template <class S, class I>
816 inline std::size_t value(const S& slice, I i) noexcept
817 {
818 if constexpr (is_xslice<S>::value)
819 {
820 using ST = typename S::size_type;
821 return static_cast<std::size_t>(slice(static_cast<ST>(i)));
822 }
823 else
824 {
825 return static_cast<std::size_t>(slice);
826 }
827 }
828
829 /****************************************
830 * homogeneous get_slice_implementation *
831 ****************************************/
832
833 namespace detail
834 {
835 template <class T>
836 struct slice_implementation_getter
837 {
838 template <class E, class SL>
839 inline decltype(auto) operator()(E& e, SL&& slice, std::size_t index) const
840 {
841 return get_slice(e, std::forward<SL>(slice), index, xtl::is_signed<std::decay_t<SL>>());
842 }
843
844 private:
845
846 template <class E, class SL>
847 inline decltype(auto) get_slice(E&, SL&& slice, std::size_t, std::false_type) const
848 {
849 return std::forward<SL>(slice);
850 }
851
852 template <class E, class SL>
853 inline decltype(auto) get_slice(E& e, SL&& slice, std::size_t index, std::true_type) const
854 {
855 using int_type = std::decay_t<SL>;
856 return slice < int_type(0) ? slice + static_cast<std::ptrdiff_t>(e.shape(index))
857 : std::ptrdiff_t(slice);
858 }
859 };
860
861 struct keep_drop_getter
862 {
863 template <class E, class SL>
864 inline decltype(auto) operator()(E& e, SL&& slice, std::size_t index) const
865 {
866 slice.normalize(e.shape()[index]);
867 return std::forward<SL>(slice);
868 }
869
870 template <class E, class SL>
871 inline auto operator()(E& e, const SL& slice, std::size_t index) const
872 {
873 return this->operator()(e, SL(slice), index);
874 }
875 };
876
877 template <class T>
878 struct slice_implementation_getter<xkeep_slice<T>> : keep_drop_getter
879 {
880 };
881
882 template <class T>
883 struct slice_implementation_getter<xdrop_slice<T>> : keep_drop_getter
884 {
885 };
886
887 template <>
888 struct slice_implementation_getter<xall_tag>
889 {
890 template <class E, class SL>
891 inline auto operator()(E& e, SL&&, std::size_t index) const
892 {
893 return xall<typename E::size_type>(e.shape()[index]);
894 }
895 };
896
897 template <>
898 struct slice_implementation_getter<xnewaxis_tag>
899 {
900 template <class E, class SL>
901 inline auto operator()(E&, SL&&, std::size_t) const
902 {
903 return xnewaxis<typename E::size_type>();
904 }
905 };
906
907 template <class A, class B, class C>
908 struct slice_implementation_getter<xrange_adaptor<A, B, C>>
909 {
910 template <class E, class SL>
911 inline auto operator()(E& e, SL&& adaptor, std::size_t index) const
912 {
913 return adaptor.get(e.shape()[index]);
914 }
915 };
916 }
917
918 template <class E, class SL>
919 inline auto get_slice_implementation(E& e, SL&& slice, std::size_t index)
920 {
921 detail::slice_implementation_getter<std::decay_t<SL>> getter;
922 return getter(e, std::forward<SL>(slice), index);
923 }
924
925 /******************************
926 * homogeneous get_slice_type *
927 ******************************/
928
929 namespace detail
930 {
931 template <class E, class SL>
932 struct get_slice_type_impl
933 {
934 using type = SL;
935 };
936
937 template <class E>
938 struct get_slice_type_impl<E, xall_tag>
939 {
940 using type = xall<typename E::size_type>;
941 };
942
943 template <class E>
944 struct get_slice_type_impl<E, xnewaxis_tag>
945 {
946 using type = xnewaxis<typename E::size_type>;
947 };
948
949 template <class E, class A, class B, class C>
950 struct get_slice_type_impl<E, xrange_adaptor<A, B, C>>
951 {
952 using type = decltype(xrange_adaptor<A, B, C>(A(), B(), C()).get(0));
953 };
954 }
955
956 template <class E, class SL>
957 using get_slice_type = typename detail::get_slice_type_impl<E, std::remove_reference_t<SL>>::type;
958
959 /*************************
960 * xslice implementation *
961 *************************/
962
963 template <class D>
964 inline auto xslice<D>::derived_cast() noexcept -> derived_type&
965 {
966 return *static_cast<derived_type*>(this);
967 }
968
969 template <class D>
970 inline auto xslice<D>::derived_cast() const noexcept -> const derived_type&
971 {
972 return *static_cast<const derived_type*>(this);
973 }
974
975 /*************************
976 * xrange implementation *
977 *************************/
978
979 template <class T>
980 inline xrange<T>::xrange(size_type start_val, size_type stop_val) noexcept
981 : m_start(start_val)
982 , m_size(stop_val > start_val ? stop_val - start_val : 0)
983 {
984 }
985
986 template <class T>
987 template <std::convertible_to<T> S>
988 inline xrange<T>::operator xrange<S>() const noexcept
989 {
990 xrange<S> ret;
991 ret.m_start = static_cast<S>(m_start);
992 ret.m_size = static_cast<S>(m_size);
993 return ret;
994 }
995
996 template <class T>
997 template <std::convertible_to<T> S>
998 inline xrange<S> xrange<T>::convert() const noexcept
999 {
1000 return xrange<S>(*this);
1001 }
1002
1003 template <class T>
1004 inline auto xrange<T>::operator()(size_type i) const noexcept -> size_type
1005 {
1006 return m_start + i;
1007 }
1008
1009 template <class T>
1010 inline auto xrange<T>::size() const noexcept -> size_type
1011 {
1012 return m_size;
1013 }
1014
1015 template <class T>
1016 inline auto xrange<T>::step_size() const noexcept -> size_type
1017 {
1018 return 1;
1019 }
1020
1021 template <class T>
1022 inline auto xrange<T>::step_size(std::size_t /*i*/, std::size_t n) const noexcept -> size_type
1023 {
1024 return static_cast<size_type>(n);
1025 }
1026
1027 template <class T>
1028 inline auto xrange<T>::revert_index(std::size_t i) const noexcept -> size_type
1029 {
1030 return i - m_start;
1031 }
1032
1033 template <class T>
1034 inline bool xrange<T>::contains(size_type i) const noexcept
1035 {
1036 return i >= m_start && i < m_start + m_size;
1037 }
1038
1039 template <class T>
1040 inline bool xrange<T>::operator==(const self_type& rhs) const noexcept
1041 {
1042 return (m_start == rhs.m_start) && (m_size == rhs.m_size);
1043 }
1044
1045 template <class T>
1046 inline bool xrange<T>::operator!=(const self_type& rhs) const noexcept
1047 {
1048 return !(*this == rhs);
1049 }
1050
1051 /********************************
1052 * xtepped_range implementation *
1053 ********************************/
1054
1055 template <class T>
1056 inline xstepped_range<T>::xstepped_range(size_type start_val, size_type stop_val, size_type step) noexcept
1057 : m_start(start_val)
1058 , m_size(size_type(0))
1059 , m_step(step)
1060 {
1061 if ((step > 0 && start_val >= stop_val) || (step < 0 && start_val <= stop_val))
1062 {
1063 m_size = 0;
1064 return;
1065 }
1066 size_type n = stop_val - start_val;
1067 m_size = n / step + static_cast<bool>(n % step);
1068 }
1069
1070 template <class T>
1071 template <std::convertible_to<T> S>
1072 inline xstepped_range<T>::operator xstepped_range<S>() const noexcept
1073 {
1075 ret.m_start = static_cast<S>(m_start);
1076 ret.m_size = static_cast<S>(m_size);
1077 ret.m_step = static_cast<S>(m_step);
1078 return ret;
1079 }
1080
1081 template <class T>
1082 template <std::convertible_to<T> S>
1083 inline xstepped_range<S> xstepped_range<T>::convert() const noexcept
1084 {
1085 return xstepped_range<S>(*this);
1086 }
1087
1088 template <class T>
1089 inline auto xstepped_range<T>::operator()(size_type i) const noexcept -> size_type
1090 {
1091 return m_start + i * m_step;
1092 }
1093
1094 template <class T>
1095 inline auto xstepped_range<T>::size() const noexcept -> size_type
1096 {
1097 return m_size;
1098 }
1099
1100 template <class T>
1101 inline auto xstepped_range<T>::step_size() const noexcept -> size_type
1102 {
1103 return m_step;
1104 }
1105
1106 template <class T>
1107 inline auto xstepped_range<T>::step_size(std::size_t /*i*/, std::size_t n) const noexcept -> size_type
1108 {
1109 return m_step * static_cast<size_type>(n);
1110 }
1111
1112 template <class T>
1113 inline auto xstepped_range<T>::revert_index(std::size_t i) const noexcept -> size_type
1114 {
1115 return (i - m_start) / m_step;
1116 }
1117
1118 template <class T>
1119 inline bool xstepped_range<T>::contains(size_type i) const noexcept
1120 {
1121 return i >= m_start && i < m_start + m_size * m_step && ((i - m_start) % m_step == 0);
1122 }
1123
1124 template <class T>
1125 inline bool xstepped_range<T>::operator==(const self_type& rhs) const noexcept
1126 {
1127 return (m_start == rhs.m_start) && (m_size == rhs.m_size) && (m_step == rhs.m_step);
1128 }
1129
1130 template <class T>
1131 inline bool xstepped_range<T>::operator!=(const self_type& rhs) const noexcept
1132 {
1133 return !(*this == rhs);
1134 }
1135
1136 /***********************
1137 * xall implementation *
1138 ***********************/
1139
1140 template <class T>
1141 inline xall<T>::xall(size_type size) noexcept
1142 : m_size(size)
1143 {
1144 }
1145
1146 template <class T>
1147 template <std::convertible_to<T> S>
1148 inline xall<T>::operator xall<S>() const noexcept
1149 {
1150 return xall<S>(static_cast<S>(m_size));
1151 }
1152
1153 template <class T>
1154 template <std::convertible_to<T> S>
1155 inline xall<S> xall<T>::convert() const noexcept
1156 {
1157 return xall<S>(*this);
1158 }
1159
1160 template <class T>
1161 inline auto xall<T>::operator()(size_type i) const noexcept -> size_type
1162 {
1163 return i;
1164 }
1165
1166 template <class T>
1167 inline auto xall<T>::size() const noexcept -> size_type
1168 {
1169 return m_size;
1170 }
1171
1172 template <class T>
1173 inline auto xall<T>::step_size() const noexcept -> size_type
1174 {
1175 return 1;
1176 }
1177
1178 template <class T>
1179 inline auto xall<T>::step_size(std::size_t /*i*/, std::size_t n) const noexcept -> size_type
1180 {
1181 return static_cast<size_type>(n);
1182 }
1183
1184 template <class T>
1185 inline auto xall<T>::revert_index(std::size_t i) const noexcept -> size_type
1186 {
1187 return i;
1188 }
1189
1190 template <class T>
1191 inline bool xall<T>::contains(size_type i) const noexcept
1192 {
1193 return i < m_size;
1194 }
1195
1196 template <class T>
1197 inline bool xall<T>::operator==(const self_type& rhs) const noexcept
1198 {
1199 return m_size == rhs.m_size;
1200 }
1201
1202 template <class T>
1203 inline bool xall<T>::operator!=(const self_type& rhs) const noexcept
1204 {
1205 return !(*this == rhs);
1206 }
1207
1208 /***************************
1209 * xnewaxis implementation *
1210 ***************************/
1211
1212 template <class T>
1213 template <std::convertible_to<T> S>
1214 inline xnewaxis<T>::operator xnewaxis<S>() const noexcept
1215 {
1216 return xnewaxis<S>();
1217 }
1218
1219 template <class T>
1220 template <std::convertible_to<T> S>
1221 inline xnewaxis<S> xnewaxis<T>::convert() const noexcept
1222 {
1223 return xnewaxis<S>(*this);
1224 }
1225
1226 template <class T>
1227 inline auto xnewaxis<T>::operator()(size_type) const noexcept -> size_type
1228 {
1229 return 0;
1230 }
1231
1232 template <class T>
1233 inline auto xnewaxis<T>::size() const noexcept -> size_type
1234 {
1235 return 1;
1236 }
1237
1238 template <class T>
1239 inline auto xnewaxis<T>::step_size() const noexcept -> size_type
1240 {
1241 return 0;
1242 }
1243
1244 template <class T>
1245 inline auto xnewaxis<T>::step_size(std::size_t /*i*/, std::size_t /*n*/) const noexcept -> size_type
1246 {
1247 return 0;
1248 }
1249
1250 template <class T>
1251 inline auto xnewaxis<T>::revert_index(std::size_t i) const noexcept -> size_type
1252 {
1253 return i;
1254 }
1255
1256 template <class T>
1257 inline bool xnewaxis<T>::contains(size_type i) const noexcept
1258 {
1259 return i == 0;
1260 }
1261
1262 template <class T>
1263 inline bool xnewaxis<T>::operator==(const self_type& /*rhs*/) const noexcept
1264 {
1265 return true;
1266 }
1267
1268 template <class T>
1269 inline bool xnewaxis<T>::operator!=(const self_type& /*rhs*/) const noexcept
1270 {
1271 return true;
1272 }
1273
1274 /******************************
1275 * xkeep_slice implementation *
1276 ******************************/
1277
1278 template <class T>
1279 template <class C>
1280 inline xkeep_slice<T>::xkeep_slice(C& cont)
1281 requires(!detail::is_xkeep_slice<std::decay_t<C>>::value)
1282 : m_raw_indices(cont.begin(), cont.end())
1283 {
1284 }
1285
1286 template <class T>
1287 inline xkeep_slice<T>::xkeep_slice(container_type&& cont)
1288 : m_raw_indices(std::move(cont))
1289 {
1290 }
1291
1292 template <class T>
1293 template <class S>
1294 inline xkeep_slice<T>::xkeep_slice(std::initializer_list<S> t)
1295 : m_raw_indices(t.size())
1296 {
1297 std::transform(
1298 t.begin(),
1299 t.end(),
1300 m_raw_indices.begin(),
1301 [](auto t)
1302 {
1303 return static_cast<size_type>(t);
1304 }
1305 );
1306 }
1307
1308 template <class T>
1309 template <std::convertible_to<T> S>
1310 inline xkeep_slice<T>::operator xkeep_slice<S>() const noexcept
1311 {
1312 xkeep_slice<S> ret;
1313 using us_type = typename container_type::size_type;
1314 us_type sz = static_cast<us_type>(size());
1315 ret.m_raw_indices.resize(sz);
1316 ret.m_indices.resize(sz);
1317 std::transform(
1318 m_raw_indices.cbegin(),
1319 m_raw_indices.cend(),
1320 ret.m_raw_indices.begin(),
1321 [](const T& val)
1322 {
1323 return static_cast<S>(val);
1324 }
1325 );
1326 std::transform(
1327 m_indices.cbegin(),
1328 m_indices.cend(),
1329 ret.m_indices.begin(),
1330 [](const T& val)
1331 {
1332 return static_cast<S>(val);
1333 }
1334 );
1335 return ret;
1336 }
1337
1338 template <class T>
1339 template <std::convertible_to<T> S>
1340 inline xkeep_slice<S> xkeep_slice<T>::convert() const noexcept
1341 {
1342 return xkeep_slice<S>(*this);
1343 }
1344
1345 template <class T>
1346 inline void xkeep_slice<T>::normalize(std::size_t shape)
1347 {
1348 m_indices.resize(m_raw_indices.size());
1349 std::size_t sz = m_indices.size();
1350 for (std::size_t i = 0; i < sz; ++i)
1351 {
1352 m_indices[i] = m_raw_indices[i] < 0 ? static_cast<size_type>(shape) + m_raw_indices[i]
1353 : m_raw_indices[i];
1354 }
1355 }
1356
1357 template <class T>
1358 inline auto xkeep_slice<T>::operator()(size_type i) const noexcept -> size_type
1359 {
1360 return m_indices.size() == size_type(1) ? m_indices.front() : m_indices[static_cast<std::size_t>(i)];
1361 }
1362
1363 template <class T>
1364 inline auto xkeep_slice<T>::size() const noexcept -> size_type
1365 {
1366 return static_cast<size_type>(m_raw_indices.size());
1367 }
1368
1369 template <class T>
1370 inline auto xkeep_slice<T>::step_size(std::size_t i, std::size_t n) const noexcept -> size_type
1371 {
1372 if (m_indices.size() == 1)
1373 {
1374 return 0;
1375 }
1376 if (i + n >= m_indices.size())
1377 {
1378 return m_indices.back() - m_indices[i] + 1;
1379 }
1380 else
1381 {
1382 return m_indices[i + n] - m_indices[i];
1383 }
1384 }
1385
1386 template <class T>
1387 inline auto xkeep_slice<T>::revert_index(std::size_t i) const -> size_type
1388 {
1389 auto it = std::find(m_indices.begin(), m_indices.end(), i);
1390 if (it != m_indices.end())
1391 {
1392 return std::distance(m_indices.begin(), it);
1393 }
1394 else
1395 {
1396 XTENSOR_THROW(std::runtime_error, "Index i (" + std::to_string(i) + ") not in indices of islice.");
1397 }
1398 }
1399
1400 template <class T>
1401 inline bool xkeep_slice<T>::contains(size_type i) const noexcept
1402 {
1403 return (std::find(m_indices.begin(), m_indices.end(), i) == m_indices.end()) ? false : true;
1404 }
1405
1406 template <class T>
1407 inline bool xkeep_slice<T>::operator==(const self_type& rhs) const noexcept
1408 {
1409 return m_indices == rhs.m_indices;
1410 }
1411
1412 template <class T>
1413 inline bool xkeep_slice<T>::operator!=(const self_type& rhs) const noexcept
1414 {
1415 return !(*this == rhs);
1416 }
1417
1418 /******************************
1419 * xdrop_slice implementation *
1420 ******************************/
1421
1422 template <class T>
1423 template <class C>
1424 inline xdrop_slice<T>::xdrop_slice(C& cont)
1425 requires(!detail::is_xdrop_slice<std::decay_t<C>>::value)
1426 : m_raw_indices(cont.begin(), cont.end())
1427 {
1428 }
1429
1430 template <class T>
1431 inline xdrop_slice<T>::xdrop_slice(container_type&& cont)
1432 : m_raw_indices(std::move(cont))
1433 {
1434 }
1435
1436 template <class T>
1437 template <class S>
1438 inline xdrop_slice<T>::xdrop_slice(std::initializer_list<S> t)
1439 : m_raw_indices(t.size())
1440 {
1441 std::transform(
1442 t.begin(),
1443 t.end(),
1444 m_raw_indices.begin(),
1445 [](auto t)
1446 {
1447 return static_cast<size_type>(t);
1448 }
1449 );
1450 }
1451
1452 template <class T>
1453 template <std::convertible_to<T> S>
1454 inline xdrop_slice<T>::operator xdrop_slice<S>() const noexcept
1455 {
1456 xdrop_slice<S> ret;
1457 ret.m_raw_indices.resize(m_raw_indices.size());
1458 ret.m_indices.resize(m_indices.size());
1459 std::transform(
1460 m_raw_indices.cbegin(),
1461 m_raw_indices.cend(),
1462 ret.m_raw_indices.begin(),
1463 [](const T& val)
1464 {
1465 return static_cast<S>(val);
1466 }
1467 );
1468 std::transform(
1469 m_indices.cbegin(),
1470 m_indices.cend(),
1471 ret.m_indices.begin(),
1472 [](const T& val)
1473 {
1474 return static_cast<S>(val);
1475 }
1476 );
1477 std::transform(
1478 m_inc.cbegin(),
1479 m_inc.cend(),
1480 std::inserter(ret.m_inc, ret.m_inc.begin()),
1481 [](const auto& val)
1482 {
1483 return std::make_pair(static_cast<S>(val.first), static_cast<S>(val.second));
1484 }
1485 );
1486 ret.m_size = static_cast<S>(m_size);
1487 return ret;
1488 }
1489
1490 template <class T>
1491 template <std::convertible_to<T> S>
1492 inline xdrop_slice<S> xdrop_slice<T>::convert() const noexcept
1493 {
1494 return xdrop_slice<S>(*this);
1495 }
1496
1497 template <class T>
1498 inline void xdrop_slice<T>::normalize(std::size_t shape)
1499 {
1500 m_size = static_cast<size_type>(shape - m_raw_indices.size());
1501
1502 m_indices.resize(m_raw_indices.size());
1503 std::size_t sz = m_indices.size();
1504 for (std::size_t i = 0; i < sz; ++i)
1505 {
1506 m_indices[i] = m_raw_indices[i] < 0 ? static_cast<size_type>(shape) + m_raw_indices[i]
1507 : m_raw_indices[i];
1508 }
1509 size_type cum = size_type(0);
1510 size_type prev_cum = cum;
1511 for (std::size_t i = 0; i < sz; ++i)
1512 {
1513 std::size_t ind = i;
1514 size_type d = m_indices[i];
1515 while (i + 1 < sz && m_indices[i + 1] == m_indices[i] + 1)
1516 {
1517 ++i;
1518 }
1519 cum += (static_cast<size_type>(i) - static_cast<size_type>(ind)) + 1;
1520 m_inc[d - prev_cum] = cum;
1521 prev_cum = cum;
1522 }
1523 }
1524
1525 template <class T>
1526 inline auto xdrop_slice<T>::operator()(size_type i) const noexcept -> size_type
1527 {
1528 if (m_inc.empty() || i < m_inc.begin()->first)
1529 {
1530 return i;
1531 }
1532 else
1533 {
1534 auto iter = --m_inc.upper_bound(i);
1535 return i + iter->second;
1536 }
1537 }
1538
1539 template <class T>
1540 inline auto xdrop_slice<T>::size() const noexcept -> size_type
1541 {
1542 return m_size;
1543 }
1544
1545 template <class T>
1546 inline auto xdrop_slice<T>::step_size(std::size_t i, std::size_t n) const noexcept -> size_type
1547 {
1548 if (i + n >= static_cast<std::size_t>(m_size))
1549 {
1550 return (*this)(static_cast<size_type>(m_size - 1)) - (*this)(static_cast<size_type>(i)) + 1;
1551 }
1552 else
1553 {
1554 return (*this)(static_cast<size_type>(i + n)) - (*this)(static_cast<size_type>(i));
1555 }
1556 }
1557
1558 template <class T>
1559 inline auto xdrop_slice<T>::revert_index(std::size_t i) const -> size_type
1560 {
1561 if (i < m_inc.begin()->first)
1562 {
1563 return i;
1564 }
1565 else
1566 {
1567 auto iter = --m_inc.lower_bound(i);
1568 auto check = iter->first + iter->second;
1569 if (check > i)
1570 {
1571 --iter;
1572 }
1573 return i - iter->second;
1574 }
1575 }
1576
1577 template <class T>
1578 inline bool xdrop_slice<T>::contains(size_type i) const noexcept
1579 {
1580 return (std::find(m_indices.begin(), m_indices.end(), i) == m_indices.end()) ? true : false;
1581 }
1582
1583 template <class T>
1584 inline bool xdrop_slice<T>::operator==(const self_type& rhs) const noexcept
1585 {
1586 return m_indices == rhs.m_indices;
1587 }
1588
1589 template <class T>
1590 inline bool xdrop_slice<T>::operator!=(const self_type& rhs) const noexcept
1591 {
1592 return !(*this == rhs);
1593 }
1594}
1595
1596#endif
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:734
auto all() noexcept
Returns a slice representing a full dimension, to be used as an argument of view function.
Definition xslice.hpp:221
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:485
auto keep(T &&indices)
Create a non-contigous slice from a container of indices to keep.
Definition xslice.hpp:376