1 /*
2  * tensorview.h
3  *
4  *  Created on: Dec 28, 2013
5  *      Author: evaleev
6  */
7 
8 #ifndef BTAS_TENSORVIEW_H_
9 #define BTAS_TENSORVIEW_H_
10 
11 #include <functional>
12 
13 #include <btas/storage_traits.h>
14 #include <btas/util/sequence_adaptor.h>
15 #include <btas/tensorview_iterator.h>
16 #include <btas/defaults.h>
17 #include <btas/util/functional.h>
18 #include <btas/error.h>
19 
20 namespace btas {
21 
22   enum TensorViewPolicy_ConstnessPolicy {
23     TensorViewPolicy_RuntimeConst = 1,
24     TensorViewPolicy_CompiletimeConst = 0
25   };
26 
27   /// TensorViewPolicy configures behavior of certain features of TensorView
28   /// \tparam RuntimeConst: if true, constness of data access is checked at runtime. This involves
29   ///      extra space overhead (enough to store a boolean readwrite flag). Non-const data access members
30   ///      will also check whether readwrite is set using assert (hence runtime overhead can be eliminated after
31   ///      testing. This feature is needed if you want to use a single TensorView<T,Range,Storage> type
32   ///      for mutable (non-const) and immutable (const) views. The default value is false, which requires use
33   ///      of TensorView<T,Range,const Storage>, aka TensorConstView<T.Range,Storage>, for immutable views.
34   template <TensorViewPolicy_ConstnessPolicy ConstnessPolicy = TensorViewPolicy_CompiletimeConst>
35   struct TensorViewPolicy {
36       /// true if constness tracked at runtime
37       static constexpr bool runtimeconst = (ConstnessPolicy == TensorViewPolicy_RuntimeConst);
38   };
39 
40   /// View (aka generalized slice) of a tensor
41 
42   /**
43       @tparam _T apparent element type, TensorView will present tensor elements as values of this type
44       @tparam _Range Range type
45       @tparam _Storage Storage type
46   */
47   template<typename _T,
48            class _Range = btas::DEFAULT::range,
49            class _Storage = btas::DEFAULT::storage<_T>,
50            class _Policy = btas::TensorViewPolicy<>
51            >
52   class TensorView {
53 
54     public:
55 
56       /// value type
57       typedef _T value_type;
58 
59       /// type of Range
60       typedef _Range range_type;
61 
62       /// type of index
63       typedef typename _Range::index_type index_type;
64 
65       /// type of underlying data storage
66       typedef _Storage storage_type;
67 
68       /// type of data storage reference
69       typedef std::reference_wrapper<storage_type> storageref_type;
70 
71       /// size type
72       typedef typename storage_traits<storage_type>::size_type size_type;
73 
74       /// element iterator
75       typedef TensorViewIterator<range_type, storage_type> iterator;
76 
77       /// element iterator
78       typedef TensorViewIterator<range_type, const storage_type> const_iterator;
79 
80     private:
81       struct Enabler {};
82 
83     public:
84 
85       /// destructor
~TensorView()86       ~TensorView () { }
87 
88       /// move-construct from \c range and \c storageref ; write access must be passed explicitly if \c _Policy requires
89       template<class Policy = _Policy, class = typename std::enable_if<not Policy::runtimeconst>::type>
90       explicit
91       TensorView (range_type&& range,
92                   storageref_type&& storageref,
93                   bool can_write = not _Policy::runtimeconst ? not std::is_const<storage_type>::value : false) :
range_(range)94       range_(range), storageref_(storageref), can_write_(can_write)
95       {
96       }
97 
98       /// conversion from const Tensor into TensorConstView
99       template<class _Tensor,
100                class Storage = _Storage,
101                class = typename std::enable_if<is_boxtensor<_Tensor>::value &&
102                                                std::is_const<Storage>::value>::type
103               >
TensorView(const _Tensor & x)104       TensorView (const _Tensor& x)
105       : range_ (x.range()),
106         storageref_(std::cref(x.storage())),
107         can_write_(false)
108       {
109       }
110 
111       /// conversion from const Tensor to non-const View only possible if \c Policy::runtimeconst is \c true
112       template<class _Tensor,
113                class Storage = _Storage,
114                class Policy = _Policy,
115                class = typename std::enable_if<is_boxtensor<_Tensor>::value &&
116                                                not std::is_const<Storage>::value &&
117                                                Policy::runtimeconst>::type
118               >
TensorView(const _Tensor & x)119       TensorView (const _Tensor& x)
120       : range_ (x.range()),
121         storageref_(std::ref(const_cast<storage_type&>(x.storage()))),
122         can_write_(false)
123       {
124       }
125 
126       /// conversion from non-const Tensor
127       template<class _Tensor,
128                class Storage = _Storage,
129                class = typename std::enable_if<is_boxtensor<_Tensor>::value &&
130                                                std::is_same<typename _Tensor::storage_type,Storage>::value>::type>
TensorView(_Tensor & x)131       TensorView (_Tensor& x)
132       : range_ (x.range()),
133         storageref_(std::ref(x.storage())),
134         can_write_(true)
135       {
136       }
137 
138       /// conversion from non-const TensorView
139       template<class __T,
140                class __Range,
141                class __Storage,
142                class __Policy,
143                class = typename std::enable_if<not std::is_const<__Storage>::value>::type>
TensorView(TensorView<__T,__Range,__Storage,__Policy> & x)144       TensorView (TensorView<__T,__Range,__Storage,__Policy>& x)
145       : range_ (x.range()),
146         storageref_(std::ref(x.storage())),
147         can_write_(_Policy::runtimeconst ? bool(x.can_write_) : not std::is_const<storage_type>::value)
148       {
149       }
150 
151       /// standard copy constructor
TensorView(const TensorView & x)152       TensorView (const TensorView& x) :
153         range_ (x.range_),
154         storageref_(x.storageref_),
155         can_write_(false)
156       {
157       }
158 
159       /// copy assignment
160       TensorView&
161       operator= (const TensorView& x)
162       {
163         range_ = x.range_;
164         storageref_ = x.storageref_;
165         can_write_ = x.can_write_;
166         return *this;
167       }
168 
169       /// move constructor
TensorView(TensorView && x)170       TensorView (TensorView&& x) : range_(), storageref_(x.storageref_), can_write_(x.can_write_)
171       {
172         std::swap(range_, x.range_);
173       }
174 
175       /// move assignment operator
176       TensorView&
177       operator= (TensorView&& x)
178       {
179         std::swap(range_, x.range_);
180         std::swap(storageref_, x.storageref_);
181         std::swap(can_write_, x.can_write_);
182         return *this;
183       }
184 
185       /// number of indices (tensor rank)
186       size_type
rank()187       rank () const
188       {
189         return range_.rank();
190       }
191 
192       /// \return number of elements
193       size_type
size()194       size () const
195       {
196         return range_.area();
197       }
198 
199       /// \return range object
200       const range_type&
range()201       range() const
202       {
203         return range_;
204       }
205 
206       /// \param d dimension
207       /// \return subrange for dimension \d
208       const Range1d<typename index_type::value_type>
range(size_t d)209       range(size_t d) const
210       {
211         return range_.range(d);
212       }
213 
214       /// \return range's extent object
215       typename range_type::extent_type
extent()216       extent() const
217       {
218         return range_.extent();
219       }
220 
221       /// \return extent of range along dimension \c d
222       typename range_type::extent_type::value_type
extent(size_t d)223       extent(size_t d) const
224       {
225         return range_.extent(d);
226       }
227 
228       /// \return storage object
229       const storage_type&
storage()230       storage() const
231       {
232         return storageref_.get();
233       }
234 
235       /// \return storage object
236       storage_type&
storage()237       storage()
238       {
239         assert_writable();
240         return storageref_.get();
241       }
242 
243       /// test whether TensorView is empty
244       bool
empty()245       empty() const
246       {
247         return range_.area() == 0;
248       }
249 
250       /// \return const iterator begin
251       const_iterator
begin()252       begin() const
253       {
254         return cbegin();
255       }
256 
257       /// \return begin iterator
258       iterator
begin()259       begin()
260       {
261         assert_writable();
262         return iterator(range().begin(), storage());
263       }
264 
265       /// \return const end iterator
266       const_iterator
end()267       end() const
268       {
269         return cend();
270       }
271 
272       /// \return const end iterator
273       iterator
end()274       end()
275       {
276         assert_writable();
277         return iterator(range().end(), storageref_);
278       }
279 
280       /// \return const iterator begin, even if this is not itself const
281       const_iterator
cbegin()282       cbegin() const
283       {
284         return const_iterator(range().begin(), storage());
285       }
286 
287       /// \return const iterator end, even if this is not itself const
288       const_iterator
cend()289       cend() const
290       {
291         return const_iterator(range().end(), storage());
292       }
293 
294       /// Immutable access to an element without range check.
295 
296       /// Available when \c value_type == \c storage_type::value_type
297       /// \return const reference to the element indexed by {\c first, \c rest}
298       template<typename index0, typename... _args>
299       typename std::enable_if<std::is_integral<index0>::value &&
300                                std::is_same<value_type,typename storage_type::value_type>::value,
301                               const value_type&
302                              >::type
operator()303       operator() (const index0& first, const _args&... rest) const
304       {
305         typedef typename common_signed_type<index0, typename index_type::value_type>::type ctype;
306         auto indexv = {static_cast<ctype>(first), static_cast<ctype>(rest)...};
307         index_type index = array_adaptor<index_type>::construct(indexv.size());
308         std::copy(std::begin(indexv), std::end(indexv), std::begin(index));
309         return storageref_.get()[ range_.ordinal(index) ];
310       }
311 
312       /// Immutable access to an element without range check.
313 
314       /// Available when \c value_type == \c storage_type::value_type
315       /// \return const reference to the element indexed by \c index
316       template <typename Index>
317       typename std::enable_if<is_index<Index>::value &&
318                                std::is_same<value_type,typename storage_type::value_type>::value,
319                               const value_type&
320                              >::type
operator()321       operator() (const Index& index) const
322       {
323         return storageref_.get()[range_.ordinal(index)];
324       }
325 
326       /// Mutable access to an element without range check.
327 
328       /// Available when \c value_type == \c storage_type::value_type
329       /// \return reference to the element indexed by {\c first, \c rest}
330       template<typename index0, typename... _args>
331       typename std::enable_if<std::is_integral<index0>::value &&
332                               std::is_same<value_type,typename storage_type::value_type>::value &&
333                               not std::is_const<storage_type>::value,
334                               value_type&
335                              >::type
operator()336       operator() (const index0& first, const _args&... rest)
337       {
338         assert_writable();
339         typedef typename common_signed_type<index0, typename index_type::value_type>::type ctype;
340         auto indexv = {static_cast<ctype>(first), static_cast<ctype>(rest)...};
341         index_type index = array_adaptor<index_type>::construct(indexv.size());
342         std::copy(std::begin(indexv), std::end(indexv), std::begin(index));
343         return storageref_.get()[ range_.ordinal(index) ];
344       }
345 
346       /// Mutable access to an element without range check (rank() == general)
347 
348       /// Available when \c value_type == \c storag_type::value_type
349       /// \return reference to the element indexed by \c index
350       template <typename Index>
351       typename std::enable_if<is_index<Index>::value &&
352                               std::is_same<value_type,typename storage_type::value_type>::value &&
353                               not std::is_const<storage_type>::value,
354                               value_type&
355                              >::type
operator()356       operator() (const Index& index)
357       {
358         assert_writable();
359         return storageref_.get()[range_.ordinal(index)];
360       }
361 
362       /// Immutable access to an element without range check.
363 
364       /// Available when \c value_type != \c storage_type::value_type
365       /// \return value of the element indexed by {\c first, \c rest}
366       template<typename index0, typename... _args>
367       typename std::enable_if<std::is_integral<index0>::value &&
368                               not std::is_same<value_type,typename storage_type::value_type>::value,
369                               value_type>::type
operator()370       operator() (const index0& first, const _args&... rest) const
371       {
372         typedef typename common_signed_type<index0, typename index_type::value_type>::type ctype;
373         auto indexv = {static_cast<ctype>(first), static_cast<ctype>(rest)...};
374         index_type index = array_adaptor<index_type>::construct(indexv.size());
375         std::copy(std::begin(indexv), std::end(indexv), std::begin(index));
376         return storageref_.get()[ range_.ordinal(index) ];
377       }
378 
379       /// Immutable access to an element without range check (rank() == general)
380 
381       /// Available when \c value_type != \c storage_type::value_type
382       /// \return value of the element indexed by \c index
383       template <typename Index>
384       typename std::enable_if<is_index<Index>::value &&
385                               not std::is_same<value_type,typename storage_type::value_type>::value,
386                               value_type
387                              >::type
operator()388       operator() (const Index& index) const
389       {
390         return storageref_.get()[range_.ordinal(index)];
391       }
392 
393 
394       /// \return element without range check
395       template<typename index0, typename... _args>
396       typename std::enable_if<std::is_integral<index0>::value &&
397                               std::is_same<value_type,typename storage_type::value_type>::value,
398                               const value_type&>::type
at(const index0 & first,const _args &...rest)399       at (const index0& first, const _args&... rest) const
400       {
401         typedef typename common_signed_type<index0, typename index_type::value_type>::type ctype;
402         auto indexv = {static_cast<ctype>(first), static_cast<ctype>(rest)...};
403         index_type index = array_adaptor<index_type>::construct(indexv.size());
404         std::copy(std::begin(indexv), std::end(indexv), std::begin(index));
405         assert( range_.includes(index) );
406         return storageref_.get()[ range_.ordinal(index) ];
407       }
408 
409       /// \return element without range check (rank() == general)
410       template <typename Index>
411       typename std::enable_if<is_index<Index>::value &&
412                               std::is_same<value_type,typename storage_type::value_type>::value,
413                               const value_type&>::type
at(const Index & index)414       at (const Index& index) const
415       {
416         assert( range_.includes(index) );
417         return storageref_.get()[ range_.ordinal(index) ];
418       }
419 
420       /// access element without range check
421       template<typename index0, typename... _args>
422       typename std::enable_if<std::is_integral<index0>::value &&
423                               std::is_same<value_type,typename storage_type::value_type>::value &&
424                               not std::is_const<storage_type>::value,
425                               value_type&>::type
at(const index0 & first,const _args &...rest)426       at (const index0& first, const _args&... rest)
427       {
428         assert_writable();
429         typedef typename common_signed_type<index0, typename index_type::value_type>::type ctype;
430         auto indexv = {static_cast<ctype>(first), static_cast<ctype>(rest)...};
431         index_type index = array_adaptor<index_type>::construct(indexv.size());
432         std::copy(std::begin(indexv), std::end(indexv), std::begin(index));
433         assert( range_.includes(index) );
434         return storageref_.get()[ range_.ordinal(index) ];
435       }
436 
437       /// access element without range check (rank() == general)
438       template <typename Index>
439       typename std::enable_if<is_index<Index>::value &&
440                               std::is_same<value_type,typename storage_type::value_type>::value &&
441                               not std::is_const<storage_type>::value,
442                               value_type&>::type
at(const Index & index)443       at (const Index& index)
444       {
445         assert_writable();
446         assert( range_.includes(index) );
447         return storageref_.get()[ range_.ordinal(index) ];
448       }
449 
450       /// swap this and x
451       void
swap(TensorView & x)452       swap (TensorView& x)
453       {
454         std::swap(range_, x.range_);
455         std::swap(storageref_, x.storageref_);
456         std::swap(can_write_, x.can_write_);
457       }
458 
459       //  ========== Finished Public Interface and Its Reference Implementations ==========
460 
461       //
462       //  Here come Non-Standard members (to be discussed)
463       //
464 #if 0
465       /// addition assignment
466       TensorView&
467       operator+= (const TensorView& x)
468       {
469         assert( std::equal(range_.begin(), range_.end(), x.range_.begin()) );
470         std::transform(storageref_.begin(), storageref_.end(), x.storageref_.begin(), storageref_.begin(), std::plus<value_type>());
471         return *this;
472       }
473 
474       /// addition of tensors
475       TensorView
476       operator+ (const TensorView& x) const
477       {
478         TensorView y(*this); y += x;
479         return y; /* automatically called move semantics */
480       }
481 
482       /// subtraction assignment
483       TensorView&
484       operator-= (const TensorView& x)
485       {
486         assert(
487             std::equal(range_.begin(), range_.end(), x.range_.begin()));
488         std::transform(storageref_.begin(), storageref_.end(), x.storageref_.begin(), storageref_.begin(), std::minus<value_type>());
489         return *this;
490       }
491 
492       /// subtraction of tensors
493       TensorView
494       operator- (const TensorView& x) const
495       {
496         TensorView y(*this); y -= x;
497         return y; /* automatically called move semantics */
498       }
499 
500       /// fill all elements by val
501       void
502       fill (const value_type& val)
503       {
504         std::fill(storageref_.begin(), storageref_.end(), val);
505       }
506 
507       /// generate all elements by gen()
508       template<class Generator>
509       void
510       generate (Generator gen)
511       {
512           std::generate(storageref_.begin(), storageref_.end(), gen);
513       }
514 #endif
515 
writable()516       bool writable() const {
517         return can_write_;
518       }
519 
520     private:
521 
522       range_type range_;///< range object
523       storageref_type storageref_;///< dataref
524       typedef typename std::conditional<_Policy::runtimeconst,
525                                         bool,
526                                         btas::detail::bool_type<not std::is_const<storage_type>::value>
527                                        >::type writable_type;
528       writable_type can_write_;
529 
530       /// use this in non-const members to assert writability if Policy calls for runtime const check
assert_writable()531       void assert_writable() const {
532         if (_Policy::runtimeconst)
533           BTAS_ASSERT(can_write_);
534       }
535 
536       /// construct from \c range and \c storage; pass \c can_write explicitly if needed
537       explicit TensorView (range_type&& range, storage_type& storage,
538                            bool can_write = not _Policy::runtimeconst ? not std::is_const<storage_type>::value : false) :
range_(std::move (range))539           range_(std::move(range)), storageref_(std::ref(storage)), can_write_(can_write) {
540       }
541 
542       template <typename T,
543                 typename Range,
544                 typename Storage,
545                 typename Policy>
546       friend TensorView<T,
547                  Range,
548                  typename std::conditional<std::is_same<T,typename Storage::value_type>::value,
549                                            Storage,
550                                            typename std::add_const<Storage>::type
551                                           >::type,
552                  Policy>
553       __make_view(Range&& range, Storage& storage,
554                   Policy,
555                   bool can_write);
556       template <typename T,
557                 typename Range,
558                 typename Storage,
559                 typename Policy>
560       friend TensorView<T, Range, const Storage, Policy> __make_cview(Range&& range, const Storage& storage, Policy);
561 
562       template <class __T,
563                 class __Range,
564                 class __Storage,
565                 class __Policy>
566       friend class TensorView;
567   }; // end of TensorView
568 
569   /// TensorConstView is a read-only variant of TensorView
570   template <typename _T,
571             class _Range   = btas::DEFAULT::range,
572             class _Storage = btas::DEFAULT::storage<_T>,
573             class _Policy  = btas::TensorViewPolicy<>
574            >
575   using TensorConstView = TensorView<_T, _Range, const _Storage, _Policy>;
576 
577   /// TensorRWView is a variant of TensorView with runtime write access check
578   template <typename _T,
579             class _Range   = btas::DEFAULT::range,
580             class _Storage = btas::DEFAULT::storage<_T>,
581             class _Policy  = btas::TensorViewPolicy<TensorViewPolicy_RuntimeConst>
582            >
583   using TensorRWView = TensorView<_T, _Range, typename std::remove_const<_Storage>::type, _Policy>;
584 
585 
586   /// Helper function (friendly to TensorView) that constructs a view with an explicitly-specified element type of the view. Useful if need to
587   /// view a tensor of floats as a tensor of complex floats.
588   /// \tparam T the element type of the resulting view
589   /// \tparam Range the range type
590   /// \tparam Storage the storage type
591   /// \tparam Policy the TensorViewPolicy type
592   /// \param range the range object defining the view
593   /// \param storage the storage object that will be viewed into
594   /// \return TensorView into \c storage using \c range and policy \c Policy
595   /// \attention use __make_cview if you must force a const view; this will provide const view, however, if \c storage is a const reference.
596   template <typename T,
597             typename Range,
598             typename Storage,
599             typename Policy>
600   TensorView<T,
601              Range,
602              typename std::conditional<std::is_same<T,typename Storage::value_type>::value,
603                                        Storage,
604                                        typename std::add_const<Storage>::type
605                                       >::type,
606              Policy>
607   __make_view(Range&& range, Storage& storage,
608               Policy = Policy(),
609               bool can_write = not Policy::runtimeconst
610                                ? (not std::is_const<Storage>::value && std::is_same<T,typename Storage::value_type>::value)
611                                : false)
612   {
613     typedef  TensorView<T,
614         Range,
615         typename std::conditional<std::is_same<T,typename Storage::value_type>::value,
616                                   Storage,
617                                   typename std::add_const<Storage>::type
618                                  >::type,
619         Policy> result_type;
620     return result_type(std::move(range), storage, can_write);
621   }
622 
623   /// Helper function (friendly to TensorView) that constructs a view, with an explicitly-specified element type of the view. Useful if need to
624   /// view a tensor of floats as a tensor of complex floats. \sa TensorConstView
625   /// \tparam T the element type of the resulting view
626   /// \tparam Range the range type
627   /// \tparam Storage the storage type
628   /// \param range the range object defining the view
629   /// \param storage the storage object that will be viewed into
630   /// \return TensorView into \c storage using \c range and policy \c Policy
631   template <typename T,
632             typename Range,
633             typename Storage,
634             typename Policy>
635   TensorView<T, Range, const Storage, Policy>
636   __make_cview(Range&& range, const Storage& storage, Policy = Policy())
637   {
638     return TensorView<T, Range, const Storage, Policy>(std::move(range), storage, false);
639   }
640 
641   /// Helper function that constructs TensorView.
642   /// \tparam Range the range type
643   /// \tparam Storage the storage type
644   /// \tparam Policy the TensorViewPolicy type; if the Policy requires additional runtime parameters use __make_view instead
645   /// \param range the range object defining the view
646   /// \param storage the storage object that will be viewed into
647   /// \return TensorView into \c storage using \c range, with policy \c Policy
648   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c storage is a const reference.
649   template <typename Range,
650             typename Storage,
651             typename Policy = TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
652             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
653   TensorView<typename Storage::value_type, Range, Storage, Policy>
654   make_view(const Range& range, Storage& storage, Policy = Policy())
655   {
656     return make_view<typename Storage::value_type, Range, Storage, Policy>(range, storage);
657   }
658 
659   /// Helper function that constructs TensorView.
660   /// \tparam Range the range type
661   /// \tparam Storage the storage type
662   /// \tparam Policy the TensorViewPolicy type
663   /// \param range the range object defining the view
664   /// \param storage the storage object that will be viewed into
665   /// \return TensorView into \c storage using \c range, with policy \c Policy
666   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c storage is a const reference.
667   template <typename Range,
668             typename Storage,
669             typename Policy = TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
670             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
671   TensorView<typename Storage::value_type, Range, Storage, Policy>
672   make_view(Range&& range, Storage& storage, Policy = Policy())
673   {
674     return make_view<typename Storage::value_type, Range, Storage, Policy>(range, storage);
675   }
676 
677 
678   /// Helper function that constructs TensorView, with an explicitly-specified element type of the view. Useful if need to
679   /// view a tensor of floats as a tensor of complex floats.
680   /// \tparam T the element type of the resulting view
681   /// \tparam Range the range type
682   /// \tparam Storage the storage type
683   /// \tparam Policy the TensorViewPolicy type
684   /// \param range the range object defining the view
685   /// \param storage the storage object that will be viewed into
686   /// \return TensorView into \c storage using \c range, with policy \c Policy
687   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c storage is a const reference.
688   template <typename T,
689             typename Range,
690             typename Storage,
691             typename Policy = TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
692             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
693   auto
694   make_view(const Range& range, Storage& storage, Policy = Policy()) -> decltype(__make_view<T, Range, Storage, Policy>(Range(range), storage))
695   {
696     return __make_view<T, Range, Storage, Policy>(Range(range), storage);
697   }
698 
699   /// Helper function that constructs TensorView, with an explicitly-specified element type of the view. Useful if need to
700   /// view a tensor of floats as a tensor of complex floats.
701   /// \tparam T the element type of the resulting view
702   /// \tparam Range the range type
703   /// \tparam Storage the storage type
704   /// \tparam Policy the TensorViewPolicy type
705   /// \param range the range object defining the view
706   /// \param storage the storage object that will be viewed into
707   /// \return TensorView into \c storage using \c range, with policy \c Policy
708   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c storage is a const reference.
709   template <typename T,
710             typename Range,
711             typename Storage,
712             typename Policy = TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
713             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
714   auto
715   make_view(Range&& range, Storage& storage, Policy = Policy()) -> decltype(__make_view<T, Range, Storage, Policy>(range, storage))
716   {
717     return __make_view<T, Range, Storage, Policy>(range, storage);
718   }
719 
720   /// Helper function that constructs a full TensorView of a Tensor.
721   /// \tparam Tensor the tensor type
722   /// \param tensor the Tensor object
723   /// \return TensorView, a full view of the \c tensor
724   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c tensor is a const reference.
725   /// \note Provided for completeness.
726   template <typename Tensor,
727             typename Policy = TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
728             class = typename std::enable_if<is_boxtensor<Tensor>::value>::type>
729   TensorView<typename Tensor::value_type,
730              typename Tensor::range_type,
731              typename Tensor::storage_type,
732              Policy>
733   make_view(Tensor& tensor, Policy = Policy())
734   {
735     return TensorView<typename Tensor::value_type,
736                       typename Tensor::range_type,
737                       typename Tensor::storage_type,
738                       Policy>(tensor);
739   }
740 
741   /// Helper function that constructs a full TensorView of a Tensor,
742   /// with an explicitly-specified element type of the view. Useful if need to
743   /// view a tensor of floats as a tensor of complex floats.
744   /// \tparam T the element type of the resulting view
745   /// \tparam Tensor the tensor type
746   /// \param tensor the Tensor object
747   /// \return TensorView, a full view of the \c tensor
748   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c tensor is a const reference.
749   /// \note Provided for completeness.
750   template <typename T, typename Tensor,
751             typename Policy = TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
752             class = typename std::enable_if<is_boxtensor<Tensor>::value>::type>
753   TensorView<T,
754              typename Tensor::range_type,
755              typename std::conditional<std::is_same<T,typename Tensor::storage_type::value_type>::value,
756                                        typename Tensor::storage_type,
757                                        typename std::add_const<typename Tensor::storage_type>::type
758                                       >::type,
759              Policy>
760   make_view(Tensor& tensor, Policy = Policy())
761   {
762       typedef   TensorView<T,
763           typename Tensor::range_type,
764           typename std::conditional<std::is_same<T,typename Tensor::storage_type::value_type>::value,
765                                     typename Tensor::storage_type,
766                                     typename std::add_const<typename Tensor::storage_type>::type
767                                    >::type,
768           Policy> result_type;
769     return result_type(tensor);
770   }
771 
772   /// Helper function that constructs a constant TensorView. \sa TensorConstView
773   /// \tparam Range the range type
774   /// \tparam Storage the storage type
775   /// \param range the range object defining the view
776   /// \param storage the storage object that will be viewed into
777   /// \return TensorView into \c storage using \c range
778   template <typename Range,
779             typename Storage,
780             typename Policy = btas::TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
781             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
782   TensorView<typename Storage::value_type, Range, const Storage, Policy>
783   make_cview(const Range& range, const Storage& storage, Policy = Policy())
784   {
785     return make_cview<typename Storage::value_type, Range, Storage, Policy>(range, storage);
786   }
787 
788   /// Helper function that constructs a constant TensorView, with an explicitly-specified element type of the view. Useful if need to
789   /// view a tensor of floats as a tensor of complex floats. \sa TensorConstView
790   /// \tparam T the element type of the resulting view
791   /// \tparam Range the range type
792   /// \tparam Storage the storage type
793   /// \param range the range object defining the view
794   /// \param storage the storage object that will be viewed into
795   /// \return TensorView into \c storage using \c range
796   template <typename T,
797             typename Range,
798             typename Storage,
799             typename Policy = TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
800             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
801   TensorView<T, Range, const Storage, Policy>
802   make_cview(const Range& range, const Storage& storage, Policy = Policy())
803   {
804     return __make_cview<T, Range, const Storage, Policy>(Range(range), storage);
805   }
806 
807   /// Helper function that constructs a full constant TensorView of a Tensor.
808   /// \tparam Tensor the tensor type
809   /// \param tensor the Tensor object
810   /// \return TensorView, a full view of the \c tensor
811   /// \note Provided for completeness.
812   template <typename Tensor,
813             typename Policy = TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
814             class = typename std::enable_if<is_boxtensor<Tensor>::value>::type>
815   TensorView<typename Tensor::value_type,
816              typename Tensor::range_type,
817              const typename Tensor::storage_type,
818              Policy>
make_cview(const Tensor & tensor)819   make_cview(const Tensor& tensor)
820   {
821     return TensorView<typename Tensor::value_type,
822                       typename Tensor::range_type,
823                       const typename Tensor::storage_type,
824                       Policy>(tensor);
825   }
826 
827   /// Helper function that constructs a full constant TensorView of a Tensor,
828   /// with an explicitly-specified element type of the view. Useful if need to
829   /// view a tensor of floats as a tensor of complex floats.
830   /// \tparam T the element type of the resulting view
831   /// \tparam Tensor the tensor type
832   /// \param tensor the Tensor object
833   /// \return TensorView, a full view of the \c tensor
834   /// \note Provided for completeness.
835   template <typename T, typename Tensor,
836             typename Policy = TensorViewPolicy<TensorViewPolicy_CompiletimeConst>,
837             class = typename std::enable_if<is_boxtensor<Tensor>::value>::type>
838   TensorView<T,
839              typename Tensor::range_type,
840              const typename Tensor::storage_type,
841              Policy>
make_cview(const Tensor & tensor)842   make_cview(const Tensor& tensor)
843   {
844     return TensorView<T,
845                       typename Tensor::range_type,
846                       const typename Tensor::storage_type,
847                       Policy>(tensor);
848   }
849 
850   /// Helper function that constructs writable TensorView.
851   /// \tparam Range the range type
852   /// \tparam Storage the storage type
853   /// \param range the range object defining the view
854   /// \param storage the storage object that will be viewed into
855   /// \return TensorView into \c storage using \c range
856   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c storage is a const reference.
857   template <typename Range,
858             typename Storage,
859             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
860   TensorRWView<typename Storage::value_type, Range, Storage>
861   make_rwview(const Range& range,
862               Storage& storage,
863               bool can_write = not std::is_const<Storage>::value)
864   {
865     // enforce mutability
866     can_write = can_write && (not std::is_const<Storage>::value);
867     return make_rwview(Range(range), storage, can_write);
868   }
869 
870   /// Helper function that constructs writable TensorView.
871   /// \tparam Range the range type
872   /// \tparam Storage the storage type
873   /// \param range the range object defining the view
874   /// \param storage the storage object that will be viewed into
875   /// \return TensorView into \c storage using \c range
876   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c storage is a const reference.
877   template <typename Range,
878             typename Storage,
879             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
880   TensorRWView<typename Storage::value_type, Range, Storage>
881   make_rwview(Range&& range,
882               Storage& storage,
883               bool can_write = not std::is_const<Storage>::value)
884   {
885     // enforce mutability
886     can_write = can_write && (not std::is_const<Storage>::value);
887     return make_rwview<typename Storage::value_type, Range, Storage>(std::move(range), storage, can_write);
888   }
889 
890   /// Helper function that constructs writable TensorView, with an explicitly-specified element type of the view. Useful if need to
891   /// view a tensor of floats as a tensor of complex floats.
892   /// \tparam T the element type of the resulting view
893   /// \tparam Range the range type
894   /// \tparam Storage the storage type
895   /// \param range the range object defining the view
896   /// \param storage the storage object that will be viewed into
897   /// \return TensorView into \c storage using \c range
898   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c storage is a const reference.
899   template <typename T,
900             typename Range,
901             typename Storage,
902             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
903   TensorRWView<T, Range, Storage>
904   make_rwview(const Range& range, Storage& storage,
905               bool can_write = not std::is_const<Storage>::value &&
906                                std::is_same<T,typename Storage::value_type>::value)
907   {
908     // enforce mutability
909     can_write = can_write && (not std::is_const<Storage>::value &&
910                               std::is_same<T,typename Storage::value_type>::value);
911     return make_rwview(Range(range),
912                        storage,
913                        can_write);
914   }
915 
916   /// Helper function that constructs writable TensorView, with an explicitly-specified element type of the view. Useful if need to
917   /// view a tensor of floats as a tensor of complex floats.
918   /// \tparam T the element type of the resulting view
919   /// \tparam Range the range type
920   /// \tparam Storage the storage type
921   /// \param range the range object defining the view
922   /// \param storage the storage object that will be viewed into
923   /// \return TensorView into \c storage using \c range
924   /// \attention use make_cview if you must force a const view; this will provide const view, however, if \c storage is a const reference.
925   template <typename T,
926             typename Range,
927             typename Storage,
928             class = typename std::enable_if<not std::is_reference<Range>::value>::type>
929   TensorRWView<T, Range, Storage>
930   make_rwview(Range&& range, Storage& storage,
931               bool can_write = not std::is_const<Storage>::value &&
932                                std::is_same<T,typename Storage::value_type>::value)
933   {
934     // enforce mutability
935     can_write = can_write && (not std::is_const<Storage>::value &&
936                               std::is_same<T,typename Storage::value_type>::value);
937     return __make_view<T,
938                        Range,
939                        typename std::remove_const<Storage>::type,
940                        TensorViewPolicy<TensorViewPolicy_RuntimeConst> >(std::move(range),
941                           const_cast<typename std::remove_const<Storage>::type&>(storage),
942                           TensorViewPolicy<TensorViewPolicy_RuntimeConst>(),
943                           can_write);
944   }
945 
946   /// Helper function that constructs a full writable TensorView of a Tensor.
947   /// \tparam Tensor the tensor type
948   /// \param tensor the Tensor object
949   /// \return TensorView, a full view of the \c tensor
950   /// \note Provided for completeness.
951   template <typename Tensor, class = typename std::enable_if<is_boxtensor<Tensor>::value>::type>
952   TensorRWView<typename Tensor::value_type,
953                typename Tensor::range_type,
954                typename Tensor::storage_type>
955   make_rwview(Tensor& tensor,
956               bool can_write = not std::is_const<Tensor>::value &&
957                                not std::is_const<typename Tensor::storage_type>::value)
958   {
959       // enforce mutability
960       can_write = can_write && (not std::is_const<Tensor>::value && not std::is_const<typename Tensor::storage_type>::value);
961       return make_rwview(tensor.range(), tensor.storage(), can_write);
962   }
963 
964   /// Helper function that constructs a full writable TensorView of a Tensor,
965   /// with an explicitly-specified element type of the view. Useful if need to
966   /// view a tensor of floats as a tensor of complex floats.
967   /// \tparam T the element type of the resulting view
968   /// \tparam Tensor the tensor type
969   /// \param tensor the Tensor object
970   /// \return TensorView, a full view of the \c tensor
971   /// \note Provided for completeness.
972   template <typename T, typename Tensor, class = typename std::enable_if<is_boxtensor<Tensor>::value>::type>
973   TensorRWView<T,
974                typename Tensor::range_type,
975                typename Tensor::storage_type>
976   make_rwview(Tensor& tensor,
977               bool can_write = not std::is_const<Tensor>::value &&
978                                not std::is_const<typename Tensor::storage_type>::value &&
979                                std::is_same<T,typename Tensor::storage_type::value_type>::value)
980   {
981       // enforce mutability
982       can_write = can_write &&
983                   (not std::is_const<Tensor>::value &&
984                    not std::is_const<typename Tensor::storage_type>::value &&
985                    std::is_same<T,typename Tensor::storage_type::value_type>::value);
986       return make_rwview(tensor.range(), tensor.storage(), can_write);
987   }
988 
989   template <typename _T, typename _Range, typename _Storage>
990   auto cbegin(const btas::TensorView<_T, _Range, _Storage>& x) -> decltype(x.cbegin()) {
991     return x.cbegin();
992   }
993   template <typename _T, typename _Range, typename _Storage>
994   auto cend(const btas::TensorView<_T, _Range, _Storage>& x) -> decltype(x.cbegin()) {
995     return x.cend();
996   }
997 
998   /// maps TensorView -> Range
999   template <typename _T, typename _Range, typename _Storage>
1000   auto
1001   range (const btas::TensorView<_T, _Range, _Storage>& t) -> decltype(t.range()) {
1002     return t.range();
1003   }
1004 
1005   /// maps TensorView -> Range extent
1006   template <typename _T, typename _Range, typename _Storage>
1007   auto
1008   extent (const btas::TensorView<_T, _Range, _Storage>& t) -> decltype(t.range().extent()) {
1009     return t.range().extent();
1010   }
1011 
1012   /// TensorView stream output operator
1013 
1014   /// prints TensorView in row-major form. To be implemented elsewhere using slices.
1015   /// \param os The output stream that will be used to print \c t
1016   /// \param t The TensorView to be printed
1017   /// \return A reference to the output stream
1018   template <typename _T, typename _Range, typename _Storage>
1019   std::ostream& operator<<(std::ostream& os, const btas::TensorView<_T, _Range, _Storage>& t) {
1020     os << "TensorView:\n  Range: " << t.range() << std::endl;
1021     return os;
1022   }
1023 
1024   /// TensorMap views a sequence of values as a Tensor
1025   template <typename _T,
1026             class _Range = btas::DEFAULT::range>
1027   using TensorMap = TensorView<_T, _Range, btas::infinite_sequence_adaptor<_T*>>;
1028   /// TensorConstMap const-views a sequence of values as a Tensor
1029   template <typename _T,
1030             class _Range = btas::DEFAULT::range>
1031   using TensorConstMap = TensorView<const _T, _Range, const btas::infinite_sequence_adaptor<const _T*>>;
1032 
1033   /// Helper function that constructs TensorMap.
1034   /// \tparam T the element type returned by the view
1035   /// \tparam Range the range type
1036   /// \param range the range object defining the view
1037   /// \return TensorView into \c storage using \c range
1038   /// \attention use make_cmap if you must force a const view; this will provide const view, however, if \c storage is a const reference.
1039   template <typename T,
1040             typename Range>
1041   TensorMap<T, Range>
make_map(T * data,Range && range)1042   make_map(T* data, Range&& range)
1043   {
1044     return TensorMap<T, Range>(std::move(range),
1045                                std::ref(btas::infinite_sequence_adaptor<T*>(data)));
1046   }
1047 
1048   /// Helper function that constructs TensorConstMap.
1049   /// \tparam T the element type returned by the view
1050   /// \tparam Range the range type
1051   /// \param range the range object defining the view
1052   /// \return TensorView into \c storage using \c range
1053   /// \attention use make_cmap if you must force a const view; this will provide const view, however, if \c storage is a const reference.
1054   template <typename T,
1055             typename Range>
1056   TensorConstMap<T, Range>
make_map(const T * data,Range && range)1057   make_map(const T* data, Range&& range)
1058   {
1059     return TensorConstMap<T, Range>(std::move(range),
1060                                     std::cref(btas::infinite_sequence_adaptor<const T*>(data)));
1061   }
1062 
1063   /// Helper function that constructs TensorConstMap.
1064   /// \tparam Range the range type
1065   /// \param range the range object defining the view
1066   /// \return TensorView into \c storage using \c range
1067   /// \attention use make_cmap if you must force a const view; this will provide const view, however, if \c storage is a const reference.
1068   template <typename T,
1069             typename Range>
1070   TensorConstMap<typename std::remove_const<T>::type, Range>
make_cmap(T * data,Range && range)1071   make_cmap(T* data, Range&& range)
1072   {
1073     typedef typename std::remove_const<T>::type value_type;
1074     typedef TensorConstMap<value_type, Range> result_type;
1075     return result_type(std::move(range),
1076                        std::cref(btas::infinite_sequence_adaptor<const T*>(const_cast<const T*>(data))));
1077   }
1078 
1079 } // namespace btas
1080 
1081 
1082 #endif /* TENSORVIEW_H_ */
1083