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