1 // Copyright 2002 The Trustees of Indiana University.
2 
3 // Copyright 2018 Glen Joseph Fernandes
4 // (glenjofe@gmail.com)
5 
6 // Use, modification and distribution is subject to the Boost Software
7 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 
10 //  Boost.MultiArray Library
11 //  Authors: Ronald Garcia
12 //           Jeremy Siek
13 //           Andrew Lumsdaine
14 //  See http://www.boost.org/libs/multi_array for documentation.
15 
16 #ifndef BOOST_MULTI_ARRAY_RG071801_HPP
17 #define BOOST_MULTI_ARRAY_RG071801_HPP
18 
19 //
20 // multi_array.hpp - contains the multi_array class template
21 // declaration and definition
22 //
23 
24 #if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 406)
25 #  pragma GCC diagnostic push
26 #  pragma GCC diagnostic ignored "-Wshadow"
27 #endif
28 
29 #include "boost/multi_array/base.hpp"
30 #include "boost/multi_array/collection_concept.hpp"
31 #include "boost/multi_array/copy_array.hpp"
32 #include "boost/multi_array/iterator.hpp"
33 #include "boost/multi_array/subarray.hpp"
34 #include "boost/multi_array/multi_array_ref.hpp"
35 #include "boost/multi_array/algorithm.hpp"
36 #include "boost/multi_array/allocators.hpp"
37 #include "boost/core/empty_value.hpp"
38 #include "boost/array.hpp"
39 #include "boost/mpl/if.hpp"
40 #include "boost/type_traits.hpp"
41 #include <algorithm>
42 #include <cstddef>
43 #include <functional>
44 #include <numeric>
45 #include <vector>
46 
47 
48 
49 namespace boost {
50   namespace detail {
51     namespace multi_array {
52 
53       struct populate_index_ranges {
54         multi_array_types::index_range
55         // RG: underscore on extent_ to stifle strange MSVC warning.
operator ()boost::detail::multi_array::populate_index_ranges56         operator()(multi_array_types::index base,
57                    multi_array_types::size_type extent_) {
58           return multi_array_types::index_range(base,base+extent_);
59         }
60       };
61 
62 #ifdef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
63 //
64 // Compilers that don't support partial ordering may need help to
65 // disambiguate multi_array's templated constructors.  Even vc6/7 are
66 // capable of some limited SFINAE, so we take the most-general version
67 // out of the overload set with disable_multi_array_impl.
68 //
69 template <typename T, std::size_t NumDims, typename TPtr>
70 char is_multi_array_impl_help(const_multi_array_view<T,NumDims,TPtr>&);
71 template <typename T, std::size_t NumDims, typename TPtr>
72 char is_multi_array_impl_help(const_sub_array<T,NumDims,TPtr>&);
73 template <typename T, std::size_t NumDims, typename TPtr>
74 char is_multi_array_impl_help(const_multi_array_ref<T,NumDims,TPtr>&);
75 
76 char ( &is_multi_array_impl_help(...) )[2];
77 
78 template <class T>
79 struct is_multi_array_impl
80 {
81     static T x;
82     BOOST_STATIC_CONSTANT(bool, value = sizeof((is_multi_array_impl_help)(x)) == 1);
83 
84   typedef mpl::bool_<value> type;
85 };
86 
87 template <bool multi_array = false>
88 struct disable_multi_array_impl_impl
89 {
90     typedef int type;
91 };
92 
93 template <>
94 struct disable_multi_array_impl_impl<true>
95 {
96     // forming a pointer to a reference triggers SFINAE
97     typedef int& type;
98 };
99 
100 
101 template <class T>
102 struct disable_multi_array_impl :
103   disable_multi_array_impl_impl<is_multi_array_impl<T>::value>
104 { };
105 
106 
107 template <>
108 struct disable_multi_array_impl<int>
109 {
110   typedef int type;
111 };
112 
113 
114 #endif
115 
116     } //namespace multi_array
117   } // namespace detail
118 
119 template<typename T, std::size_t NumDims,
120   typename Allocator>
121 class multi_array :
122   public multi_array_ref<T,NumDims>,
123   private boost::empty_value<Allocator>
124 {
125   typedef boost::empty_value<Allocator> alloc_base;
126   typedef multi_array_ref<T,NumDims> super_type;
127 public:
128   typedef typename super_type::value_type value_type;
129   typedef typename super_type::reference reference;
130   typedef typename super_type::const_reference const_reference;
131   typedef typename super_type::iterator iterator;
132   typedef typename super_type::const_iterator const_iterator;
133   typedef typename super_type::reverse_iterator reverse_iterator;
134   typedef typename super_type::const_reverse_iterator const_reverse_iterator;
135   typedef typename super_type::element element;
136   typedef typename super_type::size_type size_type;
137   typedef typename super_type::difference_type difference_type;
138   typedef typename super_type::index index;
139   typedef typename super_type::extent_range extent_range;
140 
141 
142   template <std::size_t NDims>
143   struct const_array_view {
144     typedef boost::detail::multi_array::const_multi_array_view<T,NDims> type;
145   };
146 
147   template <std::size_t NDims>
148   struct array_view {
149     typedef boost::detail::multi_array::multi_array_view<T,NDims> type;
150   };
151 
multi_array(const Allocator & alloc=Allocator ())152   explicit multi_array(const Allocator& alloc = Allocator()) :
153     super_type((T*)initial_base_,c_storage_order(),
154                /*index_bases=*/0, /*extents=*/0),
155     alloc_base(boost::empty_init_t(),alloc) {
156     allocate_space();
157   }
158 
159   template <class ExtentList>
multi_array(ExtentList const & extents,const Allocator & alloc=Allocator (),typename mpl::if_<detail::multi_array::is_multi_array_impl<ExtentList>,int &,int>::type * =0)160   explicit multi_array(
161       ExtentList const& extents,
162       const Allocator& alloc = Allocator()
163 #ifdef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
164       , typename mpl::if_<
165       detail::multi_array::is_multi_array_impl<ExtentList>,
166       int&,int>::type* = 0
167 #endif
168       ) :
169     super_type((T*)initial_base_,extents),
170     alloc_base(boost::empty_init_t(),alloc) {
171     boost::function_requires<
172       detail::multi_array::CollectionConcept<ExtentList> >();
173     allocate_space();
174   }
175 
176 
177   template <class ExtentList>
multi_array(ExtentList const & extents,const general_storage_order<NumDims> & so)178   explicit multi_array(ExtentList const& extents,
179                        const general_storage_order<NumDims>& so) :
180     super_type((T*)initial_base_,extents,so),
181     alloc_base(boost::empty_init_t()) {
182     boost::function_requires<
183       detail::multi_array::CollectionConcept<ExtentList> >();
184     allocate_space();
185   }
186 
187   template <class ExtentList>
multi_array(ExtentList const & extents,const general_storage_order<NumDims> & so,Allocator const & alloc)188   explicit multi_array(ExtentList const& extents,
189                        const general_storage_order<NumDims>& so,
190                        Allocator const& alloc) :
191     super_type((T*)initial_base_,extents,so),
192     alloc_base(boost::empty_init_t(),alloc) {
193     boost::function_requires<
194       detail::multi_array::CollectionConcept<ExtentList> >();
195     allocate_space();
196   }
197 
198 
multi_array(const detail::multi_array::extent_gen<NumDims> & ranges,const Allocator & alloc=Allocator ())199   explicit multi_array(const detail::multi_array
200                        ::extent_gen<NumDims>& ranges,
201                        const Allocator& alloc = Allocator()) :
202     super_type((T*)initial_base_,ranges),
203     alloc_base(boost::empty_init_t(),alloc) {
204 
205     allocate_space();
206   }
207 
208 
multi_array(const detail::multi_array::extent_gen<NumDims> & ranges,const general_storage_order<NumDims> & so)209   explicit multi_array(const detail::multi_array
210                        ::extent_gen<NumDims>& ranges,
211                        const general_storage_order<NumDims>& so) :
212     super_type((T*)initial_base_,ranges,so),
213     alloc_base(boost::empty_init_t()) {
214 
215     allocate_space();
216   }
217 
218 
multi_array(const detail::multi_array::extent_gen<NumDims> & ranges,const general_storage_order<NumDims> & so,Allocator const & alloc)219   explicit multi_array(const detail::multi_array
220                        ::extent_gen<NumDims>& ranges,
221                        const general_storage_order<NumDims>& so,
222                        Allocator const& alloc) :
223     super_type((T*)initial_base_,ranges,so),
224     alloc_base(boost::empty_init_t(),alloc) {
225 
226     allocate_space();
227   }
228 
multi_array(const multi_array & rhs)229   multi_array(const multi_array& rhs) :
230   super_type(rhs),
231   alloc_base(static_cast<const alloc_base&>(rhs)) {
232     allocate_space();
233     boost::detail::multi_array::copy_n(rhs.base_,rhs.num_elements(),base_);
234   }
235 
236 
237   //
238   // A multi_array is constructible from any multi_array_ref, subarray, or
239   // array_view object.  The following constructors ensure that.
240   //
241 
242   // Due to limited support for partial template ordering,
243   // MSVC 6&7 confuse the following with the most basic ExtentList
244   // constructor.
245 #ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
246   template <typename OPtr>
multi_array(const const_multi_array_ref<T,NumDims,OPtr> & rhs,const general_storage_order<NumDims> & so=c_storage_order (),const Allocator & alloc=Allocator ())247   multi_array(const const_multi_array_ref<T,NumDims,OPtr>& rhs,
248               const general_storage_order<NumDims>& so = c_storage_order(),
249               const Allocator& alloc = Allocator())
250     : super_type(0,so,rhs.index_bases(),rhs.shape()),
251       alloc_base(boost::empty_init_t(),alloc)
252   {
253     allocate_space();
254     // Warning! storage order may change, hence the following copy technique.
255     std::copy(rhs.begin(),rhs.end(),this->begin());
256   }
257 
258   template <typename OPtr>
multi_array(const detail::multi_array::const_sub_array<T,NumDims,OPtr> & rhs,const general_storage_order<NumDims> & so=c_storage_order (),const Allocator & alloc=Allocator ())259   multi_array(const detail::multi_array::
260               const_sub_array<T,NumDims,OPtr>& rhs,
261               const general_storage_order<NumDims>& so = c_storage_order(),
262               const Allocator& alloc = Allocator())
263     : super_type(0,so,rhs.index_bases(),rhs.shape()),
264       alloc_base(boost::empty_init_t(),alloc)
265   {
266     allocate_space();
267     std::copy(rhs.begin(),rhs.end(),this->begin());
268   }
269 
270 
271   template <typename OPtr>
multi_array(const detail::multi_array::const_multi_array_view<T,NumDims,OPtr> & rhs,const general_storage_order<NumDims> & so=c_storage_order (),const Allocator & alloc=Allocator ())272   multi_array(const detail::multi_array::
273               const_multi_array_view<T,NumDims,OPtr>& rhs,
274               const general_storage_order<NumDims>& so = c_storage_order(),
275               const Allocator& alloc = Allocator())
276     : super_type(0,so,rhs.index_bases(),rhs.shape()),
277       alloc_base(boost::empty_init_t(),alloc)
278   {
279     allocate_space();
280     std::copy(rhs.begin(),rhs.end(),this->begin());
281   }
282 
283 #else // BOOST_NO_FUNCTION_TEMPLATE_ORDERING
284   // More limited support for MSVC
285 
286 
multi_array(const const_multi_array_ref<T,NumDims> & rhs,const Allocator & alloc=Allocator ())287   multi_array(const const_multi_array_ref<T,NumDims>& rhs,
288               const Allocator& alloc = Allocator())
289     : super_type(0,c_storage_order(),rhs.index_bases(),rhs.shape()),
290       alloc_base(boost::empty_init_t(),alloc)
291   {
292     allocate_space();
293     // Warning! storage order may change, hence the following copy technique.
294     std::copy(rhs.begin(),rhs.end(),this->begin());
295   }
296 
multi_array(const const_multi_array_ref<T,NumDims> & rhs,const general_storage_order<NumDims> & so,const Allocator & alloc=Allocator ())297   multi_array(const const_multi_array_ref<T,NumDims>& rhs,
298               const general_storage_order<NumDims>& so,
299               const Allocator& alloc = Allocator())
300     : super_type(0,so,rhs.index_bases(),rhs.shape()),
301       alloc_base(boost::empty_init_t(),alloc)
302   {
303     allocate_space();
304     // Warning! storage order may change, hence the following copy technique.
305     std::copy(rhs.begin(),rhs.end(),this->begin());
306   }
307 
multi_array(const detail::multi_array::const_sub_array<T,NumDims> & rhs,const Allocator & alloc=Allocator ())308   multi_array(const detail::multi_array::
309               const_sub_array<T,NumDims>& rhs,
310               const Allocator& alloc = Allocator())
311     : super_type(0,c_storage_order(),rhs.index_bases(),rhs.shape()),
312       alloc_base(boost::empty_init_t(),alloc)
313   {
314     allocate_space();
315     std::copy(rhs.begin(),rhs.end(),this->begin());
316   }
317 
multi_array(const detail::multi_array::const_sub_array<T,NumDims> & rhs,const general_storage_order<NumDims> & so,const Allocator & alloc=Allocator ())318   multi_array(const detail::multi_array::
319               const_sub_array<T,NumDims>& rhs,
320               const general_storage_order<NumDims>& so,
321               const Allocator& alloc = Allocator())
322     : super_type(0,so,rhs.index_bases(),rhs.shape()),
323       alloc_base(boost::empty_init_t(),alloc)
324   {
325     allocate_space();
326     std::copy(rhs.begin(),rhs.end(),this->begin());
327   }
328 
329 
multi_array(const detail::multi_array::const_multi_array_view<T,NumDims> & rhs,const Allocator & alloc=Allocator ())330   multi_array(const detail::multi_array::
331               const_multi_array_view<T,NumDims>& rhs,
332               const Allocator& alloc = Allocator())
333     : super_type(0,c_storage_order(),rhs.index_bases(),rhs.shape()),
334       alloc_base(boost::empty_init_t(),alloc)
335   {
336     allocate_space();
337     std::copy(rhs.begin(),rhs.end(),this->begin());
338   }
339 
multi_array(const detail::multi_array::const_multi_array_view<T,NumDims> & rhs,const general_storage_order<NumDims> & so,const Allocator & alloc=Allocator ())340   multi_array(const detail::multi_array::
341               const_multi_array_view<T,NumDims>& rhs,
342               const general_storage_order<NumDims>& so,
343               const Allocator& alloc = Allocator())
344     : super_type(0,so,rhs.index_bases(),rhs.shape()),
345       alloc_base(boost::empty_init_t(),alloc)
346   {
347     allocate_space();
348     std::copy(rhs.begin(),rhs.end(),this->begin());
349   }
350 
351 #endif // !BOOST_NO_FUNCTION_TEMPLATE_ORDERING
352 
353   // Thes constructors are necessary because of more exact template matches.
multi_array(const multi_array_ref<T,NumDims> & rhs,const Allocator & alloc=Allocator ())354   multi_array(const multi_array_ref<T,NumDims>& rhs,
355               const Allocator& alloc = Allocator())
356     : super_type(0,c_storage_order(),rhs.index_bases(),rhs.shape()),
357       alloc_base(boost::empty_init_t(),alloc)
358   {
359     allocate_space();
360     // Warning! storage order may change, hence the following copy technique.
361     std::copy(rhs.begin(),rhs.end(),this->begin());
362   }
363 
multi_array(const multi_array_ref<T,NumDims> & rhs,const general_storage_order<NumDims> & so,const Allocator & alloc=Allocator ())364   multi_array(const multi_array_ref<T,NumDims>& rhs,
365               const general_storage_order<NumDims>& so,
366               const Allocator& alloc = Allocator())
367     : super_type(0,so,rhs.index_bases(),rhs.shape()),
368       alloc_base(boost::empty_init_t(),alloc)
369   {
370     allocate_space();
371     // Warning! storage order may change, hence the following copy technique.
372     std::copy(rhs.begin(),rhs.end(),this->begin());
373   }
374 
375 
multi_array(const detail::multi_array::sub_array<T,NumDims> & rhs,const Allocator & alloc=Allocator ())376   multi_array(const detail::multi_array::
377               sub_array<T,NumDims>& rhs,
378               const Allocator& alloc = Allocator())
379     : super_type(0,c_storage_order(),rhs.index_bases(),rhs.shape()),
380       alloc_base(boost::empty_init_t(),alloc)
381   {
382     allocate_space();
383     std::copy(rhs.begin(),rhs.end(),this->begin());
384   }
385 
multi_array(const detail::multi_array::sub_array<T,NumDims> & rhs,const general_storage_order<NumDims> & so,const Allocator & alloc=Allocator ())386   multi_array(const detail::multi_array::
387               sub_array<T,NumDims>& rhs,
388               const general_storage_order<NumDims>& so,
389               const Allocator& alloc = Allocator())
390     : super_type(0,so,rhs.index_bases(),rhs.shape()),
391       alloc_base(boost::empty_init_t(),alloc)
392   {
393     allocate_space();
394     std::copy(rhs.begin(),rhs.end(),this->begin());
395   }
396 
397 
multi_array(const detail::multi_array::multi_array_view<T,NumDims> & rhs,const Allocator & alloc=Allocator ())398   multi_array(const detail::multi_array::
399               multi_array_view<T,NumDims>& rhs,
400               const Allocator& alloc = Allocator())
401     : super_type(0,c_storage_order(),rhs.index_bases(),rhs.shape()),
402       alloc_base(boost::empty_init_t(),alloc)
403   {
404     allocate_space();
405     std::copy(rhs.begin(),rhs.end(),this->begin());
406   }
407 
multi_array(const detail::multi_array::multi_array_view<T,NumDims> & rhs,const general_storage_order<NumDims> & so,const Allocator & alloc=Allocator ())408   multi_array(const detail::multi_array::
409               multi_array_view<T,NumDims>& rhs,
410               const general_storage_order<NumDims>& so,
411               const Allocator& alloc = Allocator())
412     : super_type(0,so,rhs.index_bases(),rhs.shape()),
413       alloc_base(boost::empty_init_t(),alloc)
414   {
415     allocate_space();
416     std::copy(rhs.begin(),rhs.end(),this->begin());
417   }
418 
419   // Since assignment is a deep copy, multi_array_ref
420   // contains all the necessary code.
421   template <typename ConstMultiArray>
operator =(const ConstMultiArray & other)422   multi_array& operator=(const ConstMultiArray& other) {
423     super_type::operator=(other);
424     return *this;
425   }
426 
operator =(const multi_array & other)427   multi_array& operator=(const multi_array& other) {
428     if (&other != this) {
429       super_type::operator=(other);
430     }
431     return *this;
432   }
433 
434 
435   template <typename ExtentList>
resize(const ExtentList & extents)436   multi_array& resize(const ExtentList& extents) {
437     boost::function_requires<
438       detail::multi_array::CollectionConcept<ExtentList> >();
439 
440     typedef detail::multi_array::extent_gen<NumDims> gen_type;
441     gen_type ranges;
442 
443     for (int i=0; i != NumDims; ++i) {
444       typedef typename gen_type::range range_type;
445       ranges.ranges_[i] = range_type(0,extents[i]);
446     }
447 
448     return this->resize(ranges);
449   }
450 
451 
452 
resize(const detail::multi_array::extent_gen<NumDims> & ranges)453   multi_array& resize(const detail::multi_array
454                       ::extent_gen<NumDims>& ranges) {
455 
456 
457     // build a multi_array with the specs given
458     multi_array new_array(ranges,this->storage_order(),allocator());
459 
460 
461     // build a view of tmp with the minimum extents
462 
463     // Get the minimum extents of the arrays.
464     boost::array<size_type,NumDims> min_extents;
465 
466     const size_type& (*min)(const size_type&, const size_type&) =
467       std::min;
468     std::transform(new_array.extent_list_.begin(),new_array.extent_list_.end(),
469                    this->extent_list_.begin(),
470                    min_extents.begin(),
471                    min);
472 
473 
474     // typedef boost::array<index,NumDims> index_list;
475     // Build index_gen objects to create views with the same shape
476 
477     // these need to be separate to handle non-zero index bases
478     typedef detail::multi_array::index_gen<NumDims,NumDims> index_gen;
479     index_gen old_idxes;
480     index_gen new_idxes;
481 
482     std::transform(new_array.index_base_list_.begin(),
483                    new_array.index_base_list_.end(),
484                    min_extents.begin(),new_idxes.ranges_.begin(),
485                    detail::multi_array::populate_index_ranges());
486 
487     std::transform(this->index_base_list_.begin(),
488                    this->index_base_list_.end(),
489                    min_extents.begin(),old_idxes.ranges_.begin(),
490                    detail::multi_array::populate_index_ranges());
491 
492     // Build same-shape views of the two arrays
493     typename
494       multi_array::BOOST_NESTED_TEMPLATE array_view<NumDims>::type view_old = (*this)[old_idxes];
495     typename
496       multi_array::BOOST_NESTED_TEMPLATE array_view<NumDims>::type view_new = new_array[new_idxes];
497 
498     // Set the right portion of the new array
499     view_new = view_old;
500 
501     using std::swap;
502     // Swap the internals of these arrays.
503     swap(this->super_type::base_,new_array.super_type::base_);
504     swap(this->allocator(),new_array.allocator());
505     swap(this->storage_,new_array.storage_);
506     swap(this->extent_list_,new_array.extent_list_);
507     swap(this->stride_list_,new_array.stride_list_);
508     swap(this->index_base_list_,new_array.index_base_list_);
509     swap(this->origin_offset_,new_array.origin_offset_);
510     swap(this->directional_offset_,new_array.directional_offset_);
511     swap(this->num_elements_,new_array.num_elements_);
512     swap(this->base_,new_array.base_);
513     swap(this->allocated_elements_,new_array.allocated_elements_);
514 
515     return *this;
516   }
517 
518 
~multi_array()519   ~multi_array() {
520     deallocate_space();
521   }
522 
523 private:
operator ==(const multi_array & a,const multi_array & b)524   friend inline bool operator==(const multi_array& a, const multi_array& b) {
525     return a.base() == b.base();
526   }
527 
operator !=(const multi_array & a,const multi_array & b)528   friend inline bool operator!=(const multi_array& a, const multi_array& b) {
529     return !(a == b);
530   }
531 
base() const532   const super_type& base() const {
533     return *this;
534   }
535 
allocator() const536   const Allocator& allocator() const {
537     return alloc_base::get();
538   }
539 
allocator()540   Allocator& allocator() {
541     return alloc_base::get();
542   }
543 
allocate_space()544   void allocate_space() {
545     base_ = allocator().allocate(this->num_elements());
546     this->set_base_ptr(base_);
547     allocated_elements_ = this->num_elements();
548     detail::multi_array::construct(allocator(),base_,base_+allocated_elements_);
549   }
550 
deallocate_space()551   void deallocate_space() {
552     if(base_) {
553       detail::multi_array::destroy(allocator(),base_,base_+allocated_elements_);
554       allocator().deallocate(base_,allocated_elements_);
555     }
556   }
557 
558   typedef boost::array<size_type,NumDims> size_list;
559   typedef boost::array<index,NumDims> index_list;
560 
561   T* base_;
562   size_type allocated_elements_;
563   enum {initial_base_ = 0};
564 };
565 
566 } // namespace boost
567 
568 #if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 406)
569 #  pragma GCC diagnostic pop
570 #endif
571 
572 #endif // BOOST_MULTI_ARRAY_RG071801_HPP
573