xtensor
 
Loading...
Searching...
No Matches
xstrides.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_STRIDES_HPP
11#define XTENSOR_STRIDES_HPP
12
13#include <cstddef>
14#include <functional>
15#include <limits>
16#include <numeric>
17
18#include <xtl/xsequence.hpp>
19
20#include "../core/xshape.hpp"
21#include "../core/xtensor_config.hpp"
22#include "../core/xtensor_forward.hpp"
23#include "../utils/xexception.hpp"
24
25namespace xt
26{
27
28 template <class shape_type>
29 std::size_t compute_size(const shape_type& shape) noexcept;
30
34
35 /***************
36 * data offset *
37 ***************/
38
39 template <class offset_type, class S>
40 offset_type data_offset(const S& strides) noexcept;
41
66 template <class offset_type, class S, class Arg, class... Args>
67 offset_type data_offset(const S& strides, Arg arg, Args... args) noexcept;
68
69 template <class offset_type, layout_type L = layout_type::dynamic, class S, class... Args>
70 offset_type unchecked_data_offset(const S& strides, Args... args) noexcept;
71
72 template <class offset_type, class S, class It>
73 offset_type element_offset(const S& strides, It first, It last) noexcept;
74
75 /*******************
76 * strides builder *
77 *******************/
78
88 template <layout_type L = layout_type::dynamic, class shape_type, class strides_type>
89 std::size_t compute_strides(const shape_type& shape, layout_type l, strides_type& strides);
90
91 template <layout_type L = layout_type::dynamic, class shape_type, class strides_type, class backstrides_type>
92 std::size_t
93 compute_strides(const shape_type& shape, layout_type l, strides_type& strides, backstrides_type& backstrides);
94
95 template <class shape_type, class strides_type>
96 void adapt_strides(const shape_type& shape, strides_type& strides) noexcept;
97
98 template <class shape_type, class strides_type, class backstrides_type>
99 void adapt_strides(const shape_type& shape, strides_type& strides, backstrides_type& backstrides) noexcept;
100
101 /*****************
102 * unravel_index *
103 *****************/
104
105 template <class S>
106 S unravel_from_strides(typename S::value_type index, const S& strides, layout_type l = layout_type::row_major);
107
108 template <class S>
109 get_strides_t<S>
110 unravel_index(typename S::value_type index, const S& shape, layout_type l = layout_type::row_major);
111
112 template <class S, class T>
113 std::vector<get_strides_t<S>>
114 unravel_indices(const T& indices, const S& shape, layout_type l = layout_type::row_major);
115
116 /***********************
117 * broadcast functions *
118 ***********************/
119
120 template <class S, class size_type>
121 S uninitialized_shape(size_type size);
122
123 template <class S1, class S2>
124 bool broadcast_shape(const S1& input, S2& output);
125
126 template <class S1, class S2>
127 bool broadcastable(const S1& s1, S2& s2);
128
129 /*************************
130 * check strides overlap *
131 *************************/
132
133 template <layout_type L>
135
136 /**********************************
137 * check bounds, without throwing *
138 **********************************/
139
148 template <class S, class... Args>
149 bool in_bounds(const S& shape, Args&... args);
150
151 /********************************
152 * apply periodicity to indices *
153 *******************************/
154
163 template <class S, class... Args>
164 void normalize_periodic(const S& shape, Args&... args);
165
166 /********************************************
167 * utility functions for strided containers *
168 ********************************************/
169
170 template <class C, class It, class size_type>
171 It strided_data_end(const C& c, It begin, layout_type l, size_type offset)
172 {
173 using difference_type = typename std::iterator_traits<It>::difference_type;
174 if (c.size() == 0 || std::find(c.shape().cbegin(), c.shape().cend(), size_type(0)) != c.shape().cend())
175 {
176 return begin;
177 }
178 if (c.dimension() == 0)
179 {
180 ++begin;
181 }
182 else
183 {
184 for (std::size_t i = 0; i != c.dimension(); ++i)
185 {
186 begin += c.strides()[i] * difference_type(c.shape()[i] - 1);
187 }
188 if (l == layout_type::row_major)
189 {
190 begin += c.strides().back();
191 }
192 else
193 {
194 if (offset == 0)
195 {
196 begin += c.strides().front();
197 }
198 }
199 }
200 return begin;
201 }
202
203 /***********
204 * strides *
205 ***********/
206
207 namespace detail
208 {
209 template <class return_type, class S, class T, class D>
210 inline return_type compute_stride_impl(layout_type layout, const S& shape, T axis, D default_stride)
211 {
212 if (layout == layout_type::row_major)
213 {
214 return std::accumulate(
215 shape.cbegin() + axis + 1,
216 shape.cend(),
217 static_cast<return_type>(1),
218 std::multiplies<return_type>()
219 );
220 }
221 if (layout == layout_type::column_major)
222 {
223 return std::accumulate(
224 shape.cbegin(),
225 shape.cbegin() + axis,
226 static_cast<return_type>(1),
227 std::multiplies<return_type>()
228 );
229 }
230 return default_stride;
231 }
232 }
233
238 enum class stride_type
239 {
241 normal = 1,
242 bytes = 2,
243 };
244
253 template <class E>
254 inline auto strides(const E& e, stride_type type = stride_type::normal) noexcept
255 {
256 using strides_type = typename E::strides_type;
257 using return_type = typename strides_type::value_type;
258 strides_type ret = e.strides();
259 auto shape = e.shape();
260
261 if (type == stride_type::internal)
262 {
263 return ret;
264 }
265
266 for (std::size_t i = 0; i < ret.size(); ++i)
267 {
268 if (shape[i] == 1)
269 {
270 ret[i] = detail::compute_stride_impl<return_type>(e.layout(), shape, i, ret[i]);
271 }
272 }
273
274 if (type == stride_type::bytes)
275 {
276 return_type f = static_cast<return_type>(sizeof(typename E::value_type));
277 std::for_each(
278 ret.begin(),
279 ret.end(),
280 [f](auto& c)
281 {
282 c *= f;
283 }
284 );
285 }
286
287 return ret;
288 }
289
299 template <class E>
300 inline auto strides(const E& e, std::size_t axis, stride_type type = stride_type::normal) noexcept
301 {
302 using strides_type = typename E::strides_type;
303 using return_type = typename strides_type::value_type;
304
305 return_type ret = e.strides()[axis];
306
307 if (type == stride_type::internal)
308 {
309 return ret;
310 }
311
312 if (ret == 0)
313 {
314 if (e.shape(axis) == 1)
315 {
316 ret = detail::compute_stride_impl<return_type>(e.layout(), e.shape(), axis, ret);
317 }
318 }
319
320 if (type == stride_type::bytes)
321 {
322 return_type f = static_cast<return_type>(sizeof(typename E::value_type));
323 ret *= f;
324 }
325
326 return ret;
327 }
328
329 /******************
330 * Implementation *
331 ******************/
332
333 namespace detail
334 {
335 template <class shape_type>
336 inline std::size_t compute_size_impl(const shape_type& shape, std::true_type /* is signed */)
337 {
338 using size_type = std::decay_t<typename shape_type::value_type>;
339 return static_cast<std::size_t>(std::abs(
340 std::accumulate(shape.cbegin(), shape.cend(), size_type(1), std::multiplies<size_type>())
341 ));
342 }
343
344 template <class shape_type>
345 inline std::size_t compute_size_impl(const shape_type& shape, std::false_type /* is not signed */)
346 {
347 using size_type = std::decay_t<typename shape_type::value_type>;
348 return static_cast<std::size_t>(
349 std::accumulate(shape.cbegin(), shape.cend(), size_type(1), std::multiplies<size_type>())
350 );
351 }
352 }
353
354 template <class shape_type>
355 inline std::size_t compute_size(const shape_type& shape) noexcept
356 {
357 return detail::compute_size_impl(
358 shape,
359 xtl::is_signed<std::decay_t<typename std::decay_t<shape_type>::value_type>>()
360 );
361 }
362
363 namespace detail
364 {
365
366 template <std::size_t dim, class S>
367 inline auto raw_data_offset(const S&) noexcept
368 {
369 using strides_value_type = std::decay_t<decltype(std::declval<S>()[0])>;
370 return strides_value_type(0);
371 }
372
373 template <std::size_t dim, class S>
374 inline auto raw_data_offset(const S&, missing_type) noexcept
375 {
376 using strides_value_type = std::decay_t<decltype(std::declval<S>()[0])>;
377 return strides_value_type(0);
378 }
379
380 template <std::size_t dim, class S, class Arg, class... Args>
381 inline auto raw_data_offset(const S& strides, Arg arg, Args... args) noexcept
382 {
383 return static_cast<std::ptrdiff_t>(arg) * strides[dim] + raw_data_offset<dim + 1>(strides, args...);
384 }
385
386 template <layout_type L, std::ptrdiff_t static_dim>
387 struct layout_data_offset
388 {
389 template <std::size_t dim, class S, class Arg, class... Args>
390 inline static auto run(const S& strides, Arg arg, Args... args) noexcept
391 {
392 return raw_data_offset<dim>(strides, arg, args...);
393 }
394 };
395
396 template <std::ptrdiff_t static_dim>
397 struct layout_data_offset<layout_type::row_major, static_dim>
398 {
399 using self_type = layout_data_offset<layout_type::row_major, static_dim>;
400
401 template <std::size_t dim, class S, class Arg>
402 inline static auto run(const S& strides, Arg arg) noexcept
403 {
404 if (std::ptrdiff_t(dim) + 1 == static_dim)
405 {
406 return arg;
407 }
408 else
409 {
410 return arg * strides[dim];
411 }
412 }
413
414 template <std::size_t dim, class S, class Arg, class... Args>
415 inline static auto run(const S& strides, Arg arg, Args... args) noexcept
416 {
417 return arg * strides[dim] + self_type::template run<dim + 1>(strides, args...);
418 }
419 };
420
421 template <std::ptrdiff_t static_dim>
422 struct layout_data_offset<layout_type::column_major, static_dim>
423 {
424 using self_type = layout_data_offset<layout_type::column_major, static_dim>;
425
426 template <std::size_t dim, class S, class Arg>
427 inline static auto run(const S& strides, Arg arg) noexcept
428 {
429 if (dim == 0)
430 {
431 return arg;
432 }
433 else
434 {
435 return arg * strides[dim];
436 }
437 }
438
439 template <std::size_t dim, class S, class Arg, class... Args>
440 inline static auto run(const S& strides, Arg arg, Args... args) noexcept
441 {
442 if (dim == 0)
443 {
444 return arg + self_type::template run<dim + 1>(strides, args...);
445 }
446 else
447 {
448 return arg * strides[dim] + self_type::template run<dim + 1>(strides, args...);
449 }
450 }
451 };
452 }
453
454 template <class offset_type, class S>
455 inline offset_type data_offset(const S&) noexcept
456 {
457 return offset_type(0);
458 }
459
460 template <class offset_type, class S, class Arg, class... Args>
461 inline offset_type data_offset(const S& strides, Arg arg, Args... args) noexcept
462 {
463 constexpr std::size_t nargs = sizeof...(Args) + 1;
464 if (nargs == strides.size())
465 {
466 // Correct number of arguments: iterate
467 return static_cast<offset_type>(detail::raw_data_offset<0>(strides, arg, args...));
468 }
469 else if (nargs > strides.size())
470 {
471 // Too many arguments: drop the first
472 return data_offset<offset_type, S>(strides, args...);
473 }
474 else if (detail::last_type_is_missing<Args...>)
475 {
476 // Too few arguments & last argument xt::missing: postfix index with zeros
477 return static_cast<offset_type>(detail::raw_data_offset<0>(strides, arg, args...));
478 }
479 else
480 {
481 // Too few arguments: right to left scalar product
482 auto view = strides.cend() - nargs;
483 return static_cast<offset_type>(detail::raw_data_offset<0>(view, arg, args...));
484 }
485 }
486
487 template <class offset_type, layout_type L, class S, class... Args>
488 inline offset_type unchecked_data_offset(const S& strides, Args... args) noexcept
489 {
490 return static_cast<offset_type>(
491 detail::layout_data_offset<L, static_dimension<S>::value>::template run<0>(strides.cbegin(), args...)
492 );
493 }
494
495 template <class offset_type, class S, class It>
496 inline offset_type element_offset(const S& strides, It first, It last) noexcept
497 {
498 using difference_type = typename std::iterator_traits<It>::difference_type;
499 auto size = static_cast<difference_type>(
500 (std::min)(static_cast<typename S::size_type>(std::distance(first, last)), strides.size())
501 );
502 return std::inner_product(last - size, last, strides.cend() - size, offset_type(0));
503 }
504
505 namespace detail
506 {
507 template <class shape_type, class strides_type, class bs_ptr>
508 inline void adapt_strides(
509 const shape_type& shape,
510 strides_type& strides,
511 bs_ptr backstrides,
512 typename strides_type::size_type i
513 ) noexcept
514 {
515 if (shape[i] == 1)
516 {
517 strides[i] = 0;
518 }
519 (*backstrides)[i] = strides[i] * std::ptrdiff_t(shape[i] - 1);
520 }
521
522 template <class shape_type, class strides_type>
523 inline void adapt_strides(
524 const shape_type& shape,
525 strides_type& strides,
526 std::nullptr_t,
527 typename strides_type::size_type i
528 ) noexcept
529 {
530 if (shape[i] == 1)
531 {
532 strides[i] = 0;
533 }
534 }
535
536 template <layout_type L, class shape_type, class strides_type, class bs_ptr>
537 inline std::size_t
538 compute_strides(const shape_type& shape, layout_type l, strides_type& strides, bs_ptr bs)
539 {
540 using strides_value_type = typename std::decay_t<strides_type>::value_type;
541 strides_value_type data_size = 1;
542
543#if defined(_MSC_VER) && (1931 <= _MSC_VER)
544 // Workaround MSVC compiler optimization bug, xtensor#2568
545 if (0 == shape.size())
546 {
547 return static_cast<std::size_t>(data_size);
548 }
549#endif
550
552 {
553 for (std::size_t i = shape.size(); i != 0; --i)
554 {
555 strides[i - 1] = data_size;
556 data_size = strides[i - 1] * static_cast<strides_value_type>(shape[i - 1]);
557 adapt_strides(shape, strides, bs, i - 1);
558 }
559 }
560 else
561 {
562 for (std::size_t i = 0; i < shape.size(); ++i)
563 {
564 strides[i] = data_size;
565 data_size = strides[i] * static_cast<strides_value_type>(shape[i]);
566 adapt_strides(shape, strides, bs, i);
567 }
568 }
569 return static_cast<std::size_t>(data_size);
570 }
571 }
572
573 template <layout_type L, class shape_type, class strides_type>
574 inline std::size_t compute_strides(const shape_type& shape, layout_type l, strides_type& strides)
575 {
576 return detail::compute_strides<L>(shape, l, strides, nullptr);
577 }
578
579 template <layout_type L, class shape_type, class strides_type, class backstrides_type>
580 inline std::size_t
581 compute_strides(const shape_type& shape, layout_type l, strides_type& strides, backstrides_type& backstrides)
582 {
583 return detail::compute_strides<L>(shape, l, strides, &backstrides);
584 }
585
586 template <class T1, class T2>
587 inline bool
588 stride_match_condition(const T1& stride, const T2& shape, const T1& data_size, bool zero_strides)
589 {
590 return (shape == T2(1) && stride == T1(0) && zero_strides) || (stride == data_size);
591 }
592
593 // zero_strides should be true when strides are set to 0 if the corresponding dimensions are 1
594 template <class shape_type, class strides_type>
595 inline bool
596 do_strides_match(const shape_type& shape, const strides_type& strides, layout_type l, bool zero_strides)
597 {
598 using value_type = typename strides_type::value_type;
599 value_type data_size = 1;
600 if (l == layout_type::row_major)
601 {
602 for (std::size_t i = strides.size(); i != 0; --i)
603 {
604 if (!stride_match_condition(strides[i - 1], shape[i - 1], data_size, zero_strides))
605 {
606 return false;
607 }
608 data_size *= static_cast<value_type>(shape[i - 1]);
609 }
610 return true;
611 }
612 else if (l == layout_type::column_major)
613 {
614 for (std::size_t i = 0; i < strides.size(); ++i)
615 {
616 if (!stride_match_condition(strides[i], shape[i], data_size, zero_strides))
617 {
618 return false;
619 }
620 data_size *= static_cast<value_type>(shape[i]);
621 }
622 return true;
623 }
624 else
625 {
626 return false;
627 }
628 }
629
630 template <class shape_type, class strides_type>
631 inline void adapt_strides(const shape_type& shape, strides_type& strides) noexcept
632 {
633 for (typename shape_type::size_type i = 0; i < shape.size(); ++i)
634 {
635 detail::adapt_strides(shape, strides, nullptr, i);
636 }
637 }
638
639 template <class shape_type, class strides_type, class backstrides_type>
640 inline void
641 adapt_strides(const shape_type& shape, strides_type& strides, backstrides_type& backstrides) noexcept
642 {
643 for (typename shape_type::size_type i = 0; i < shape.size(); ++i)
644 {
645 detail::adapt_strides(shape, strides, &backstrides, i);
646 }
647 }
648
649 namespace detail
650 {
651 template <class S>
652 inline S unravel_noexcept(typename S::value_type idx, const S& strides, layout_type l) noexcept
653 {
654 using value_type = typename S::value_type;
655 using size_type = typename S::size_type;
656 S result = xtl::make_sequence<S>(strides.size(), 0);
657 if (l == layout_type::row_major)
658 {
659 for (size_type i = 0; i < strides.size(); ++i)
660 {
661 value_type str = strides[i];
662 value_type quot = str != 0 ? idx / str : 0;
663 idx = str != 0 ? idx % str : idx;
664 result[i] = quot;
665 }
666 }
667 else
668 {
669 for (size_type i = strides.size(); i != 0; --i)
670 {
671 value_type str = strides[i - 1];
672 value_type quot = str != 0 ? idx / str : 0;
673 idx = str != 0 ? idx % str : idx;
674 result[i - 1] = quot;
675 }
676 }
677 return result;
678 }
679 }
680
681 template <class S>
682 inline S unravel_from_strides(typename S::value_type index, const S& strides, layout_type l)
683 {
685 {
686 XTENSOR_THROW(std::runtime_error, "unravel_index: dynamic layout not supported");
687 }
688 return detail::unravel_noexcept(index, strides, l);
689 }
690
691 template <class S, class T>
692 inline get_value_type_t<T> ravel_from_strides(const T& index, const S& strides)
693 {
694 return element_offset<get_value_type_t<T>>(strides, index.begin(), index.end());
695 }
696
697 template <class S>
698 inline get_strides_t<S> unravel_index(typename S::value_type index, const S& shape, layout_type l)
699 {
700 using strides_type = get_strides_t<S>;
701 using strides_value_type = typename strides_type::value_type;
702 strides_type strides = xtl::make_sequence<strides_type>(shape.size(), 0);
703 compute_strides(shape, l, strides);
704 return unravel_from_strides(static_cast<strides_value_type>(index), strides, l);
705 }
706
707 template <class S, class T>
708 inline std::vector<get_strides_t<S>> unravel_indices(const T& idx, const S& shape, layout_type l)
709 {
710 using strides_type = get_strides_t<S>;
711 using strides_value_type = typename strides_type::value_type;
712 strides_type strides = xtl::make_sequence<strides_type>(shape.size(), 0);
713 compute_strides(shape, l, strides);
714 std::vector<get_strides_t<S>> out(idx.size());
715 auto out_iter = out.begin();
716 auto idx_iter = idx.begin();
717 for (; out_iter != out.end(); ++out_iter, ++idx_iter)
718 {
719 *out_iter = unravel_from_strides(static_cast<strides_value_type>(*idx_iter), strides, l);
720 }
721 return out;
722 }
723
724 template <class S, class T>
725 inline get_value_type_t<T> ravel_index(const T& index, const S& shape, layout_type l)
726 {
727 using strides_type = get_strides_t<S>;
728 strides_type strides = xtl::make_sequence<strides_type>(shape.size(), 0);
729 compute_strides(shape, l, strides);
730 return ravel_from_strides(index, strides);
731 }
732
733 template <class S, class stype>
734 inline S uninitialized_shape(stype size)
735 {
736 using value_type = typename S::value_type;
737 using size_type = typename S::size_type;
738 return xtl::make_sequence<S>(static_cast<size_type>(size), std::numeric_limits<value_type>::max());
739 }
740
741 template <class S1, class S2>
742 inline bool broadcast_shape(const S1& input, S2& output)
743 {
744 bool trivial_broadcast = (input.size() == output.size());
745 // Indices are faster than reverse iterators
746 using value_type = typename S2::value_type;
747 auto output_index = output.size();
748 auto input_index = input.size();
749
750 if (output_index < input_index)
751 {
752 throw_broadcast_error(output, input);
753 }
754 for (; input_index != 0; --input_index, --output_index)
755 {
756 // First case: output = (MAX, MAX, ...., MAX)
757 // output is a new shape that has not been through
758 // the broadcast process yet; broadcast is trivial
759 if (output[output_index - 1] == std::numeric_limits<value_type>::max())
760 {
761 output[output_index - 1] = static_cast<value_type>(input[input_index - 1]);
762 }
763 // Second case: output has been initialized to 1. Broadcast is trivial
764 // only if input is 1 to.
765 else if (output[output_index - 1] == 1)
766 {
767 output[output_index - 1] = static_cast<value_type>(input[input_index - 1]);
768 trivial_broadcast = trivial_broadcast && (input[input_index - 1] == 1);
769 }
770 // Third case: output has been initialized to something different from 1.
771 // if input is 1, then the broadcast is not trivial
772 else if (input[input_index - 1] == 1)
773 {
774 trivial_broadcast = false;
775 }
776 // Last case: input and output must have the same value, else
777 // shape are not compatible and an exception is thrown
778 else if (static_cast<value_type>(input[input_index - 1]) != output[output_index - 1])
779 {
780 throw_broadcast_error(output, input);
781 }
782 }
783 return trivial_broadcast;
784 }
785
786 template <class S1, class S2>
787 inline bool broadcastable(const S1& src_shape, const S2& dst_shape)
788 {
789 auto src_iter = src_shape.crbegin();
790 auto dst_iter = dst_shape.crbegin();
791 bool res = dst_shape.size() >= src_shape.size();
792 for (; src_iter != src_shape.crend() && res; ++src_iter, ++dst_iter)
793 {
794 res = (static_cast<std::size_t>(*src_iter) == static_cast<std::size_t>(*dst_iter))
795 || (*src_iter == 1);
796 }
797 return res;
798 }
799
800 template <>
802 {
803 template <class S1, class S2>
804 static std::size_t get(const S1& s1, const S2& s2)
805 {
806 using value_type = typename S1::value_type;
807 // Indices are faster than reverse iterators
808 auto s1_index = s1.size();
809 auto s2_index = s2.size();
810
811 for (; s2_index != 0; --s1_index, --s2_index)
812 {
813 if (static_cast<value_type>(s1[s1_index - 1]) != static_cast<value_type>(s2[s2_index - 1]))
814 {
815 break;
816 }
817 }
818 return s1_index;
819 }
820 };
821
822 template <>
824 {
825 template <class S1, class S2>
826 static std::size_t get(const S1& s1, const S2& s2)
827 {
828 // Indices are faster than reverse iterators
829 using size_type = typename S1::size_type;
830 using value_type = typename S1::value_type;
831 size_type index = 0;
832
833 // This check is necessary as column major "broadcasting" is still
834 // performed in a row major fashion
835 if (s1.size() != s2.size())
836 {
837 return 0;
838 }
839
840 auto size = s2.size();
841
842 for (; index < size; ++index)
843 {
844 if (static_cast<value_type>(s1[index]) != static_cast<value_type>(s2[index]))
845 {
846 break;
847 }
848 }
849 return index;
850 }
851 };
852
853 namespace detail
854 {
855 template <class S, std::size_t dim>
856 inline bool check_in_bounds_impl(const S&)
857 {
858 return true;
859 }
860
861 template <class S, std::size_t dim>
862 inline bool check_in_bounds_impl(const S&, missing_type)
863 {
864 return true;
865 }
866
867 template <class S, std::size_t dim, class T, class... Args>
868 inline bool check_in_bounds_impl(const S& shape, T& arg, Args&... args)
869 {
870 if (sizeof...(Args) + 1 > shape.size())
871 {
872 return check_in_bounds_impl<S, dim>(shape, args...);
873 }
874 else
875 {
876 return arg >= T(0) && arg < static_cast<T>(shape[dim])
877 && check_in_bounds_impl<S, dim + 1>(shape, args...);
878 }
879 }
880 }
881
882 template <class S, class... Args>
883 inline bool check_in_bounds(const S& shape, Args&... args)
884 {
885 return detail::check_in_bounds_impl<S, 0>(shape, args...);
886 }
887
888 namespace detail
889 {
890 template <class S, std::size_t dim>
891 inline void normalize_periodic_impl(const S&)
892 {
893 }
894
895 template <class S, std::size_t dim>
896 inline void normalize_periodic_impl(const S&, missing_type)
897 {
898 }
899
900 template <class S, std::size_t dim, class T, class... Args>
901 inline void normalize_periodic_impl(const S& shape, T& arg, Args&... args)
902 {
903 if (sizeof...(Args) + 1 > shape.size())
904 {
905 normalize_periodic_impl<S, dim>(shape, args...);
906 }
907 else
908 {
909 T n = static_cast<T>(shape[dim]);
910 arg = (n + (arg % n)) % n;
911 normalize_periodic_impl<S, dim + 1>(shape, args...);
912 }
913 }
914 }
915
916 template <class S, class... Args>
917 inline void normalize_periodic(const S& shape, Args&... args)
918 {
919 check_dimension(shape, args...);
920 detail::normalize_periodic_impl<S, 0>(shape, args...);
921 }
922}
923
924#endif
auto arg(E &&e) noexcept
Calculates the phase angle (in radians) elementwise for the complex numbers in e.
Definition xcomplex.hpp:221
std::size_t compute_strides(const shape_type &shape, layout_type l, strides_type &strides)
Compute the strides given the shape and the layout of an array.
Definition xstrides.hpp:574
auto strides(const E &e, stride_type type=stride_type::normal) noexcept
Get strides of an object.
Definition xstrides.hpp:254
stride_type
Choose stride type.
Definition xstrides.hpp:239
void normalize_periodic(const S &shape, Args &... args)
Normalise an index of a periodic array.
Definition xstrides.hpp:917
@ bytes
Normal stride in bytes.
Definition xstrides.hpp:242
@ internal
As used internally (with stride(axis) == 0 if shape(axis) == 1)
Definition xstrides.hpp:240
@ normal
Normal stride corresponding to storage.
Definition xstrides.hpp:241
standard mathematical functions for xexpressions
bool in_bounds(const S &shape, Args &... args)
Check if the index is within the bounds of the array.
layout_type
Definition xlayout.hpp:24
auto view(E &&e, S &&... slices)
Constructs and returns a view on the specified xexpression.
Definition xview.hpp:1824