small.H 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. // (c) Daniel Llorens - 2013-2016
  2. // This library is free software; you can redistribute it and/or modify it under
  3. // the terms of the GNU Lesser General Public License as published by the Free
  4. // Software Foundation; either version 3 of the License, or (at your option) any
  5. // later version.
  6. #pragma once
  7. #include "ra/ply.H"
  8. #include <iterator>
  9. /// @file small.H
  10. /// @brief Arrays with fixed size.
  11. #ifdef RA_CHECK_BOUNDS
  12. #define RA_CHECK_BOUNDS_RA_SMALL RA_CHECK_BOUNDS
  13. #else
  14. #ifndef RA_CHECK_BOUNDS_RA_SMALL
  15. #define RA_CHECK_BOUNDS_RA_SMALL 1
  16. #endif
  17. #endif
  18. #if RA_CHECK_BOUNDS_RA_SMALL==0
  19. #define CHECK_BOUNDS( cond )
  20. #else
  21. #define CHECK_BOUNDS( cond ) assert( cond )
  22. #endif
  23. namespace ra {
  24. template <class sizes, class strides>
  25. struct Indexer0
  26. {
  27. static_assert(mp::len<sizes> == mp::len<strides>, "mismatched sizes & strides");
  28. template <rank_t end, rank_t k, class P>
  29. constexpr static std::enable_if_t<k==end, dim_t> index_p_(dim_t const c, P const & p)
  30. {
  31. return c;
  32. }
  33. template <rank_t end, rank_t k, class P>
  34. constexpr static std::enable_if_t<(k<end), dim_t> index_p_(dim_t const c, P const & p)
  35. {
  36. CHECK_BOUNDS(inside(p[k], mp::First_<sizes>::value));
  37. return Indexer0<mp::Drop1_<sizes>, mp::Drop1_<strides>>::template index_p_<end, k+1>(c + p[k] * mp::First_<strides>::value, p);
  38. }
  39. template <class P>
  40. constexpr static dim_t index_p(P const & p) // for Container::at().
  41. {
  42. // gcc accepts p.size(), but I also need P = std::array to work. See also below.
  43. static_assert(mp::len<sizes> >= ra_traits<P>::size_s(), "too many indices");
  44. return Indexer0<sizes, strides>::template index_p_<ra_traits<P>::size_s(), 0>(0, p);
  45. }
  46. template <class P, std::enable_if_t<ra_traits<P>::size_s()!=RANK_ANY, int> =0>
  47. constexpr static dim_t index_short(P const & p) // for ArrayIterator::at().
  48. {
  49. static_assert(mp::len<sizes> <= ra_traits<P>::size_s(), "too few indices");
  50. return Indexer0<sizes, strides>::template index_p_<mp::len<sizes>, 0>(0, p);
  51. }
  52. template <class P, std::enable_if_t<ra_traits<P>::size_s()==RANK_ANY, int> =0>
  53. constexpr static dim_t index_short(P const & p) // for ArrayIterator::at().
  54. {
  55. CHECK_BOUNDS(mp::len<sizes> <= p.size());
  56. return Indexer0<sizes, strides>::template index_p_<mp::len<sizes>, 0>(0, p);
  57. }
  58. };
  59. struct Indexer1
  60. {
  61. template <rank_t k, class Dim>
  62. constexpr static dim_t index_(Dim const & dim)
  63. {
  64. return 0;
  65. }
  66. template <rank_t k, class Dim, class Iarg0, class ... Iarg>
  67. constexpr static dim_t index_(Dim const & dim, Iarg0 const i0_, Iarg const ... i_)
  68. {
  69. CHECK_BOUNDS(inside(i0_, dim[k].size));
  70. return i0_ * dim[k].stride + index_<k+1>(dim, i_ ...);
  71. }
  72. template <class Dim, class ... Iarg>
  73. constexpr static dim_t index(Dim const & dim, Iarg const ... i_)
  74. {
  75. CHECK_BOUNDS(dim.size()>=sizeof...(Iarg) && "too many indices");
  76. return index_<0>(dim, i_ ...);
  77. }
  78. template <class Dim, class P>
  79. constexpr static dim_t index_p_(Dim const & dim, P const & p)
  80. {
  81. // use dim.data() to skip the size check.
  82. dim_t c = 0;
  83. for_each([&c](auto && d, auto && p) { CHECK_BOUNDS(inside(p, d.size)); c += d.stride*p; },
  84. ptr(dim.data()), p);
  85. return c;
  86. }
  87. template <class Dim, class P>
  88. constexpr static dim_t index_p(Dim const & dim, P const & p)
  89. {
  90. CHECK_BOUNDS(dim_t(dim.size())>=start(p).size(0) && "too many indices");
  91. return index_p_(dim, p);
  92. }
  93. // used by ra_iterator::at() for rank matching on rank<driving rank, no slicing. todo Static check.
  94. template <class Dim, class P>
  95. constexpr static dim_t index_short(rank_t framer, Dim const & dim, P const & p)
  96. {
  97. dim_t c = 0;
  98. for (rank_t k=0; k<framer; ++k) {
  99. CHECK_BOUNDS(inside(p[k], dim[k].size));
  100. c += dim[k].stride * p[k];
  101. }
  102. return c;
  103. }
  104. };
  105. // Static dimensions, stack storage. See View, Unique, Shared.
  106. template <class T, class sizes, class strides> struct SmallArray; // Used as type of Small::iterator::shape().
  107. template <class T, class sizes, class strides> struct SmallView; // Used as types of Small::iterator::at().
  108. template <class sizes_, class strides_, class ... I>
  109. struct FilterDims
  110. {
  111. using sizes = sizes_;
  112. using strides = strides_;
  113. };
  114. template <class sizes_, class strides_, class I0, class ... I>
  115. struct FilterDims<sizes_, strides_, I0, I ...>
  116. {
  117. constexpr static int s = is_beatable<I0>::skip;
  118. constexpr static int s_src = is_beatable<I0>::skip_src;
  119. using next = FilterDims<mp::Drop_<sizes_, s_src>, mp::Drop_<strides_, s_src>, I ...>;
  120. using sizes = mp::Append_<mp::Take_<sizes_, s>, typename next::sizes>;
  121. using strides = mp::Append_<mp::Take_<strides_, s>, typename next::strides>;
  122. };
  123. template <dim_t size0, dim_t stride0> inline
  124. constexpr dim_t select(dim_t i0)
  125. {
  126. CHECK_BOUNDS(inside(i0, size0));
  127. return i0*stride0;
  128. };
  129. template <dim_t size0, dim_t stride0, int n> inline
  130. constexpr dim_t select(dots_t<n> i0)
  131. {
  132. return 0;
  133. }
  134. template <class sizes, class strides> inline
  135. constexpr dim_t select_loop()
  136. {
  137. return 0;
  138. }
  139. template <class sizes, class strides, class I0, class ... I> inline
  140. constexpr dim_t select_loop(I0 i0, I ... i)
  141. {
  142. constexpr int s_src = is_beatable<I0>::skip_src;
  143. return select<mp::First_<sizes>::value, mp::First_<strides>::value>(i0)
  144. + select_loop<mp::Drop_<sizes, s_src>, mp::Drop_<strides, s_src>>(i ...);
  145. }
  146. template <class T> struct mat_uv { T uu, uv, vu, vv; };
  147. template <class T> struct mat_xy { T xx, xy, yx, yy; };
  148. template <class T> struct mat_uvz { T uu, uv, uz, vu, vv, vz, zu, zv, zz; };
  149. template <class T> struct mat_xyz { T xx, xy, xz, yx, yy, yz, zx, zy, zz; };
  150. template <class T> struct mat_xyzw { T xx, xy, xz, xw, yx, yy, yz, yw, zx, zy, zz, zw, wx, wy, wz, ww; };
  151. template <class T> struct vec_uv { T u, v; };
  152. template <class T> struct vec_xy { T x, y; };
  153. template <class T> struct vec_tp { T t, p; };
  154. template <class T> struct vec_uvz { T u, v, z; };
  155. template <class T> struct vec_xyz { T x, y, z; };
  156. template <class T> struct vec_rtp { T r, t, p; };
  157. template <class T> struct vec_xyzw { T u, v, z, w; };
  158. // TODO Should this be itself an xpr type, or do I need a ra_iterator-like obj?
  159. template <template <class, class, class> class Child_, class T, class sizes_, class strides_>
  160. struct SmallBase
  161. {
  162. using sizes = sizes_;
  163. using strides = strides_;
  164. constexpr static std::array<dim_t, mp::len<sizes_>> ssizes = mp::tuple_copy<sizes, std::array<dim_t, mp::len<sizes>> >::f();
  165. constexpr static std::array<dim_t, mp::len<strides_>> sstrides = mp::tuple_copy<strides, std::array<dim_t, mp::len<strides>> >::f();
  166. // TODO complete static check on strides.
  167. template <class TTT> struct BadDimension
  168. {
  169. using type = mp::int_t<(TTT::value<0 || TTT::value==DIM_ANY || TTT::value==DIM_BAD)>;
  170. };
  171. static_assert(mp::len<sizes> == mp::len<strides>, "bad strides");
  172. static_assert(!mp::Apply_<mp::Or, mp::Map_<BadDimension, sizes>>::value, "negative dimensions");
  173. // TODO look at Dim below; useable in the same way?
  174. using Child = Child_<T, sizes, strides>;
  175. constexpr static rank_t rank() { return mp::len<sizes>; }
  176. constexpr static rank_t rank_s() { return mp::len<sizes>; }
  177. constexpr static dim_t size() { return mp::Apply_<mp::Prod, sizes>::value; }
  178. constexpr static dim_t size_s() { return size(); }
  179. constexpr static dim_t size(int j) { return ssizes[j]; }
  180. constexpr static dim_t stride(int j) { return sstrides[j]; }
  181. constexpr T * data() { return static_cast<Child &>(*this).p; }
  182. constexpr T const * data() const { return static_cast<Child const &>(*this).p; }
  183. // Specialize for rank() integer-args -> scalar, same in ra::View in big.H.
  184. #define SUBSCRIPTS(CONST) \
  185. template <class ... I, std::enable_if_t<((0 + ... + std::is_integral<I>::value)<rank() \
  186. && (is_beatable<I>::static_p && ...)), int> = 0> \
  187. constexpr auto operator()(I ... i) CONST \
  188. { \
  189. using FD = FilterDims<sizes, strides, I ...>; \
  190. return SmallView<T CONST, typename FD::sizes, typename FD::strides> \
  191. (data()+select_loop<sizes, strides>(i ...)); \
  192. } \
  193. template <class ... I, std::enable_if_t<(0 + ... + std::is_integral<I>::value)==rank(), int> = 0> \
  194. constexpr decltype(auto) operator()(I ... i) CONST \
  195. { \
  196. return data()[select_loop<sizes, strides>(i ...)]; \
  197. } /* TODO More than one selector... */ \
  198. template <class ... I, std::enable_if_t<(!is_beatable<I>::static_p || ...), int> = 0> \
  199. constexpr auto operator()(I && ... i) CONST \
  200. { \
  201. return from(*this, std::forward<I>(i) ...); \
  202. } \
  203. /* BUG I must be fixed size, otherwise we can't make out the output type. */ \
  204. template <class I> \
  205. constexpr auto at(I const & i) CONST \
  206. { \
  207. return SmallView<T CONST, mp::Drop_<sizes, ra_traits<I>::size_s()>, mp::Drop_<strides, ra_traits<I>::size_s()>> \
  208. (data()+Indexer0<sizes, strides>::index_p(i)); \
  209. } \
  210. constexpr decltype(auto) operator[](dim_t const i) CONST \
  211. { \
  212. return (*this)(i); \
  213. }
  214. SUBSCRIPTS(const)
  215. SUBSCRIPTS(/*const*/)
  216. #undef SUBSCRIPTS
  217. // See same thing for View.
  218. #define DEF_ASSIGNOPS(OP) \
  219. template <class X> constexpr Child & operator OP(X && x) \
  220. { \
  221. /* TODO generally forbid writing to smaller rank from larger rank (small.H [ref01]) */ \
  222. constexpr rank_t xrank = std::decay_t<decltype(start(x))>::rank_s(); \
  223. static_assert(xrank<=rank() || xrank==RANK_ANY, "bad assigment"); \
  224. for_each([](T & y, T const & x) { y OP x; }, static_cast<Child &>(*this), x); \
  225. return static_cast<Child &>(*this); \
  226. }
  227. FOR_EACH(DEF_ASSIGNOPS, =, *=, +=, -=, /=)
  228. #undef DEF_ASSIGNOPS
  229. constexpr Child & operator=(std::initializer_list<T> const x)
  230. {
  231. assert(x.size()==size() && "bad initializer list"); /* static_assert */
  232. std::copy(x.begin(), x.end(), this->begin());
  233. return static_cast<Child &>(*this);
  234. }
  235. // TODO Would replace by s(ra::iota) if that could be made constexpr
  236. #define DEF_AS(CONST) \
  237. template <int ss, int oo=0> \
  238. auto as() CONST \
  239. { \
  240. static_assert(rank()>=1, "bad rank for as<>"); \
  241. static_assert(ss>=0 && oo>=0 && ss+oo<=size(), "bad size for as<>"); \
  242. return SmallView<T CONST, mp::Cons_<mp::int_t<ss>, mp::Drop1_<sizes>>, strides>(this->data()+oo*this->stride(0)); \
  243. }
  244. DEF_AS(const)
  245. DEF_AS(/* const */)
  246. #undef DEF_AS
  247. // TODO This is rank 0 only. Maybe reuse cell_iterator?
  248. template <class P>
  249. struct Iterator
  250. {
  251. P p;
  252. using value_type = typename std::iterator_traits<P>::value_type;
  253. constexpr Iterator(P p_): p(p_) {}
  254. constexpr Iterator(Iterator const & q): p(q.p) {}
  255. constexpr Iterator() = delete;
  256. // TODO See ra_traits<Small>. Why do these twice?
  257. constexpr static auto const & shape() { return ssizes; }
  258. constexpr static dim_t size(int j) { return SmallBase::size(j); }
  259. constexpr static dim_t size_s(int j) { return SmallBase::size(j); }
  260. // For xpr use; cf this stride() against SmallBase::stride().
  261. constexpr static dim_t stride(int j) { return j<rank() ? SmallBase::stride(j) : 0; }
  262. constexpr bool keep_stride(dim_t step, int z, int j) const
  263. {
  264. return step*(z<rank() ? stride(z) : 0)==(j<rank() ? stride(j) : 0);
  265. }
  266. constexpr static auto size() { return SmallBase::size(); }
  267. constexpr static auto rank() { return SmallBase::rank(); }
  268. constexpr static auto size_s() { return size(); }
  269. constexpr static auto rank_s() { return rank(); }
  270. template <class I> constexpr decltype(auto) at(I const & i_)
  271. {
  272. return p[Indexer0<sizes, strides>::index_short(i_)];
  273. }
  274. void adv(rank_t const k, dim_t const d)
  275. {
  276. p += (k<rank()) * stride(k)*d;
  277. }
  278. constexpr auto flat() { return p; }
  279. constexpr auto flat() const { return p; }
  280. #define DEF_ASSIGNOPS(OP) \
  281. template <class X> void operator OP(X && x) \
  282. { for_each([](auto && y, auto && x) { std::forward<decltype(y)>(y) OP x; }, *this, x); } \
  283. template <class X> void operator OP(Iterator const & x) \
  284. { for_each([](auto && y, auto && x) { std::forward<decltype(y)>(y) OP x; }, *this, x); }
  285. FOR_EACH(DEF_ASSIGNOPS, =, *=, +=, -=, /=)
  286. };
  287. #undef DEF_ASSIGNOPS
  288. template <rank_t c=0> constexpr auto iter() { static_assert(c==0, "not yet"); return Iterator<T *>(data()); }
  289. template <rank_t c=0> constexpr auto iter() const { static_assert(c==0, "not yet"); return Iterator<T const *>(data()); }
  290. template <class Iterator>
  291. struct general_stl_iterator
  292. {
  293. using value_type = typename Iterator::value_type;
  294. using difference_type = dim_t;
  295. using pointer = value_type *;
  296. using reference = value_type &;
  297. using const_reference = value_type const &;
  298. using iterator_category = std::forward_iterator_tag;
  299. Iterator ii;
  300. std::decay_t<decltype(ssizes)> i;
  301. general_stl_iterator(pointer const & p): ii(std::tuple_size<decltype(i)>::value==0 ? nullptr : p) { std::fill(i.begin(), i.end(), 0.); }
  302. general_stl_iterator(): ii(pointer(nullptr)) {}
  303. template <class PP> bool operator==(PP const & q) const { return ii.p==q.ii.p; }
  304. template <class PP> bool operator!=(PP const & q) const { return ii.p!=q.ii.p; }
  305. decltype(auto) operator*() const { return *(ii.p); }
  306. decltype(auto) operator*() { return *(ii.p); }
  307. template <int k=rank()-1>
  308. std::enable_if_t<(k>=0)> next_in_cube()
  309. {
  310. ++i[k];
  311. if (i[k]<mp::Ref_<sizes, k>::value) {
  312. ii.p += mp::Ref_<strides, k>::value;
  313. } else {
  314. i[k] = 0;
  315. ii.p -= mp::Ref_<strides, k>::value*(mp::Ref_<sizes, k>::value-1);
  316. next_in_cube<k-1>();
  317. return;
  318. }
  319. }
  320. template <int k> std::enable_if_t<(k<0)> next_in_cube() { ii.p = nullptr; }
  321. general_stl_iterator & operator++() { next_in_cube(); return *this; }
  322. };
  323. constexpr static bool have_default_strides = std::is_same<strides, default_strides_<sizes>>::value;
  324. template <class P> using stl_iterator = mp::If_<have_default_strides, P, general_stl_iterator<Iterator<P>>>;
  325. // TODO In C++17 begin() end() may be different types. See if we can use this to simplify end() and !=end() test.
  326. constexpr auto begin() { return stl_iterator<T *>(data()); }
  327. constexpr auto begin() const { return stl_iterator<T const *>(data()); }
  328. constexpr auto end() { return have_default_strides ? stl_iterator<T *>(data()+size()) : stl_iterator<T *>(); }
  329. constexpr auto end() const { return have_default_strides ? stl_iterator<T const *>(data()+size()) : stl_iterator<T const *>(); }
  330. // Renames.
  331. #define COMP_RENAME_C(name__, req_dim0__, req_dim1__, CONST) \
  332. operator name__<T> CONST &() CONST \
  333. { \
  334. static_assert(std::is_same<strides, default_strides_<mp::int_list<req_dim0__, req_dim1__>>>::value, "renames only on default strides"); \
  335. static_assert(size(0)==req_dim0__ && size(1)==req_dim1__, "dimension error"); \
  336. return reinterpret_cast<name__<T> CONST &>(*this); \
  337. }
  338. #define COMP_RENAME(name__, dim0__, dim1__) \
  339. COMP_RENAME_C(name__, dim0__, dim1__, /* const */) \
  340. COMP_RENAME_C(name__, dim0__, dim1__, const)
  341. COMP_RENAME(mat_xy, 2, 2)
  342. COMP_RENAME(mat_uv, 2, 2)
  343. COMP_RENAME(mat_xyz, 3, 3)
  344. COMP_RENAME(mat_uvz, 3, 3)
  345. COMP_RENAME(mat_xyzw, 4, 4)
  346. #undef COMP_RENAME
  347. #undef COMP_RENAME_C
  348. #define COMP_RENAME_C(name__, dim0__, CONST) \
  349. operator name__<T> CONST &() CONST \
  350. { \
  351. static_assert(std::is_same<strides, default_strides_<mp::int_list<dim0__>>>::value, "renames only on default strides"); \
  352. static_assert(size(0)==dim0__, "dimension error"); \
  353. return reinterpret_cast<name__<T> CONST &>(*this); \
  354. }
  355. #define COMP_RENAME(name__, dim0__) \
  356. COMP_RENAME_C(name__, dim0__, /* const */) \
  357. COMP_RENAME_C(name__, dim0__, const)
  358. COMP_RENAME(vec_xy, 2)
  359. COMP_RENAME(vec_uv, 2)
  360. COMP_RENAME(vec_tp, 2)
  361. COMP_RENAME(vec_xyz, 3)
  362. COMP_RENAME(vec_uvz, 3)
  363. COMP_RENAME(vec_rtp, 3)
  364. COMP_RENAME(vec_xyzw, 4)
  365. #undef COMP_RENAME
  366. #undef COMP_RENAME_C
  367. };
  368. // TODO apparently this won't be needed anymore in C++17 (http://en.cppreference.com/w/cpp/language/static).
  369. template <template <class, class, class> class Child_, class T, class sizes_, class strides_>
  370. constexpr std::array<dim_t, mp::len<sizes_>> SmallBase<Child_, T, sizes_, strides_>::ssizes;
  371. template <template <class, class, class> class Child_, class T, class sizes_, class strides_>
  372. constexpr std::array<dim_t, mp::len<strides_>> SmallBase<Child_, T, sizes_, strides_>::sstrides;
  373. template <class T, class sizes, class strides>
  374. struct SmallView: public SmallBase<SmallView, T, sizes, strides>
  375. {
  376. using Base = SmallBase<ra::SmallView, T, sizes, strides>; // TODO qualifier needed for clang 3.8 bug
  377. T * p;
  378. constexpr SmallView(T * p_): p(p_) {}
  379. constexpr SmallView(SmallView const & s): p(s.p) {}
  380. constexpr operator T & ()
  381. {
  382. static_assert(Base::rank()==0 || (Base::rank()==1 && Base::size()==1), "bad rank"); // rank 1 for coord types
  383. return p[0];
  384. }
  385. constexpr operator T const & () const
  386. {
  387. static_assert(Base::rank()==0 || (Base::rank()==1 && Base::size()==1), "bad rank"); // rank 1 for coord types
  388. return p[0];
  389. };
  390. using Base::operator=;
  391. // templated operator= cannot take these over. As in ra::View.
  392. SmallView & operator=(SmallView && x) { return Base::operator=(x); }
  393. SmallView & operator=(SmallView const & x) { return Base::operator=(x); }
  394. };
  395. #define DEF_SMALLARRAY \
  396. /* TODO qualifier needed for clang 3.8 bug */ \
  397. using Base = SmallBase<ra::SmallArray, T, sizes, strides>; \
  398. T p[Base::size()]; \
  399. \
  400. constexpr SmallArray(): p() {} \
  401. template <class X> \
  402. constexpr SmallArray(X && x): p() \
  403. { \
  404. static_cast<Base &>(*this) = x; \
  405. } \
  406. constexpr SmallArray(std::initializer_list<T> const x): p() /* init for constexpr */ \
  407. { \
  408. assert(x.size()==Base::size() && "bad initializer list"); /* static_assert */ \
  409. auto b = this->begin(); \
  410. for (auto const & xx: x) { *b = xx; ++b; } /* std::copy not constexpr */ \
  411. } \
  412. template <class P> /* TODO remove; only here because std::vector has it */ \
  413. constexpr SmallArray(P begin, P end) \
  414. { \
  415. std::copy(begin, end, this->begin()); \
  416. } \
  417. /* Special case is needed if ra::start() doesn't acknowledge T as scalar */ \
  418. constexpr SmallArray(T const & t) \
  419. { \
  420. std::fill(this->begin(), this->end(), t); \
  421. }
  422. template <class T, class sizes, class strides>
  423. struct SmallArray: public SmallBase<SmallArray, T, sizes, strides>
  424. {
  425. DEF_SMALLARRAY
  426. using Base::rank;
  427. using Base::size;
  428. constexpr operator SmallView<T, sizes, strides> () { return SmallView<T, sizes, strides>(p); }
  429. constexpr operator SmallView<T const, sizes, strides> const () { return SmallView<T const, sizes, strides>(p); }
  430. // BUG these make SmallArray<T, N> std::is_convertible to T even though conversion isn't possible b/c of the assert.
  431. constexpr operator T & ()
  432. {
  433. static_assert(rank()==0 || (rank()==1 && size()==1), "bad rank"); // rank 1 for coord types
  434. return p[0];
  435. };
  436. constexpr operator T const & () const
  437. {
  438. static_assert(rank()==0 || (rank()==1 && size()==1), "bad rank"); // rank 1 for coord types
  439. return p[0];
  440. };
  441. using Base::operator=;
  442. T const & back() const { static_assert(Base::rank()==1, "bad rank for back"); return (*this)[Base::size()-1]; }
  443. T & back() { static_assert(Base::rank()==1, "bad rank for back"); return (*this)[Base::size()-1]; }
  444. };
  445. // This specialization exists only to avoid -Warray-bounds on operator[] when sizes is [0].
  446. template <class T, class strides>
  447. struct SmallArray<T, mp::int_list<0>, strides>: public SmallBase<SmallArray, T, mp::int_list<0>, strides>
  448. {
  449. using sizes = mp::int_list<0>;
  450. DEF_SMALLARRAY
  451. T & operator[](dim_t const i) { assert(0 && "internal error"); }
  452. T const & operator[](dim_t const i) const { assert(0 && "internal error"); }
  453. operator SmallView<T, sizes, strides> () { return SmallView<T, sizes, strides>(p); }
  454. operator SmallView<T const, sizes, strides> const () { return SmallView<T const, sizes, strides>(p); }
  455. };
  456. #undef DEF_SMALLARRAY
  457. template <template <class, class, class> class Type_,
  458. class T, class sizes, class strides>
  459. struct ra_traits_small
  460. {
  461. using V = Type_<T, sizes, strides>;
  462. using value_type = T;
  463. using shape_type = std::decay_t<decltype(V::ssizes)>;
  464. // constexpr static auto const & shape(V const & v) { return V::ssizes; }
  465. constexpr static auto shape(V const & v) { return SmallView<ra::dim_t const, mp::int_list<V::rank_s()>, mp::int_list<1>>(V::ssizes.data()); }
  466. #define MAKE_COND (std::is_same<strides, mp::int_list<1>>::value && std::is_same<sizes, mp::Take_<sizes, 1>>::value)
  467. constexpr static V make(dim_t const n)
  468. {
  469. static_assert(MAKE_COND, "bad type for ra_traits::make");
  470. assert(n==V::size(0));
  471. return V {};
  472. }
  473. template <class TT> static V make(dim_t n, TT const & t)
  474. {
  475. static_assert(MAKE_COND, "bad type for ra_traits::make");
  476. assert(n==V::size(0));
  477. return V(t);
  478. }
  479. #undef MAKE_COND
  480. constexpr static dim_t size(V const & v) { return v.size(); }
  481. constexpr static rank_t rank(V const & v) { return V::rank(); }
  482. constexpr static dim_t size_s() { return V::size(); }
  483. constexpr static rank_t rank_s() { return V::rank(); };
  484. };
  485. template <class T, class sizes, class strides>
  486. struct ra_traits_def<SmallArray<T, sizes, strides>>: public ra_traits_small<SmallArray, T, sizes, strides> {};
  487. template <class T, class sizes, class strides>
  488. struct ra_traits_def<SmallView<T, sizes, strides>>: public ra_traits_small<SmallView, T, sizes, strides> {};
  489. template <class A, class i>
  490. struct axis_indices
  491. {
  492. template <class T> struct MatchIndex { using type = mp::int_t<(T::value==i::value)>; };
  493. using I = mp::Iota_<mp::len<A>>;
  494. using W = mp::Map_<MatchIndex, A>;
  495. using type = mp::Filter_<W, I>;
  496. static_assert(mp::len<type>> 0, "dst axis doesn't appear in transposed axes list");
  497. };
  498. template <class axes_list, class src_sizes, class src_strides>
  499. struct axes_list_indices
  500. {
  501. static_assert(mp::len<axes_list> == mp::len<src_sizes>, "bad size for transposed axes list");
  502. constexpr static int talmax = mp::Fold_<mp::Max, void, axes_list>::value;
  503. constexpr static int talmin = mp::Fold_<mp::Min, void, axes_list>::value;
  504. static_assert(talmax < mp::len<src_sizes>, "bad index in transposed axes list");
  505. static_assert(talmin >= 0, "bad index in transposed axes list");
  506. template <class dst_i> struct dst_indices
  507. {
  508. using type = typename axis_indices<axes_list, dst_i>::type;
  509. template <class i> using sizesi = mp::Ref<src_sizes, i::value>;
  510. template <class i> using stridesi = mp::Ref<src_strides, i::value>;
  511. using stride = mp::Fold_<mp::Sum, void, mp::Map_<stridesi, type>>;
  512. using size = mp::Fold_<mp::Min, void, mp::Map_<sizesi, type>>;
  513. };
  514. template <class dst_i> struct dst_size { using type = typename dst_indices<dst_i>::size; };
  515. template <class dst_i> struct dst_stride { using type = typename dst_indices<dst_i>::stride; };
  516. using dst = mp::Iota_<(talmax>=0 ? (1+talmax) : 0)>;
  517. using type = mp::Map_<dst_indices, dst>;
  518. using sizes = mp::Map_<dst_size, dst>;
  519. using strides = mp::Map_<dst_stride, dst>;
  520. };
  521. #define DEF_TRANSPOSE(CONST) \
  522. template <int ... Iarg, template <class, class, class> class Child, class T, class sizes, class strides> \
  523. inline auto transpose(SmallBase<Child, T, sizes, strides> CONST & a) \
  524. { \
  525. using ti = axes_list_indices<mp::int_list<Iarg ...>, sizes, strides>; \
  526. return SmallView<T CONST, typename ti::sizes, typename ti::strides>(a.data()); \
  527. }; \
  528. \
  529. template <template <class, class, class> class Child, class T, class sizes, class strides> \
  530. inline auto diag(SmallBase<Child, T, sizes, strides> CONST & a) \
  531. { \
  532. return transpose<0, 0>(a); \
  533. }
  534. DEF_TRANSPOSE(const)
  535. DEF_TRANSPOSE(/* */)
  536. #undef DEF_TRANSPOSE
  537. // TODO Used by ProductRule; waiting for proper generalization.
  538. template <template <class, class, class> class Child1, class T1, class sizes1, class strides1,
  539. template <class, class, class> class Child2, class T2, class sizes2, class strides2>
  540. auto cat(SmallBase<Child1, T1, sizes1, strides1> const & a1, SmallBase<Child2, T2, sizes2, strides2> const & a2)
  541. {
  542. using A1 = SmallBase<Child1, T1, sizes1, strides1>;
  543. using A2 = SmallBase<Child2, T2, sizes2, strides2>;
  544. static_assert(A1::rank()==1 && A2::rank()==1, "bad ranks for cat"); // gcc accepts a1.rank(), etc.
  545. using T = std::decay_t<decltype(a1[0])>;
  546. Small<T, A1::size()+A2::size()> val;
  547. std::copy(a1.begin(), a1.end(), val.begin());
  548. std::copy(a2.begin(), a2.end(), val.begin()+a1.size());
  549. return val;
  550. }
  551. template <template <class, class, class> class Child1, class T1, class sizes1, class strides1,
  552. class A2,
  553. std::enable_if_t<is_scalar<A2>, int> =0>
  554. auto cat(SmallBase<Child1, T1, sizes1, strides1> const & a1, A2 const & a2)
  555. {
  556. using A1 = SmallBase<Child1, T1, sizes1, strides1>;
  557. static_assert(A1::rank()==1, "bad ranks for cat");
  558. using T = std::decay_t<decltype(a1[0])>;
  559. Small<T, A1::size()+1> val;
  560. std::copy(a1.begin(), a1.end(), val.begin());
  561. val[a1.size()] = a2;
  562. return val;
  563. }
  564. template <class A1,
  565. template <class, class, class> class Child2, class T2, class sizes2, class strides2,
  566. std::enable_if_t<is_scalar<A1>, int> =0>
  567. auto cat(A1 const & a1, SmallBase<Child2, T2, sizes2, strides2> const & a2)
  568. {
  569. using A2 = SmallBase<Child2, T2, sizes2, strides2>;
  570. static_assert(A2::rank()==1, "bad ranks for cat");
  571. using T = std::decay_t<decltype(a2[0])>;
  572. Small<T, 1+A2::size()> val;
  573. val[0] = a1;
  574. std::copy(a2.begin(), a2.end(), val.begin()+1);
  575. return val;
  576. }
  577. template <class T, class I=std::make_integer_sequence<int, std::rank<T>::value>>
  578. struct builtin_array_sizes;
  579. template <class T, int ... I>
  580. struct builtin_array_sizes<T, std::integer_sequence<int, I ...> >
  581. {
  582. using type = mp::int_list<std::extent<T, I>::value ...>;
  583. };
  584. template <class T> using builtin_array_sizes_t = typename builtin_array_sizes<T>::type;
  585. // forward declared in type.H.
  586. template <class T, std::enable_if_t<is_builtin_array<T>, int>>
  587. inline constexpr auto start(T && t)
  588. {
  589. // preserve const.
  590. using A = std::remove_volatile_t<std::remove_reference_t<T>>;
  591. using E = std::remove_all_extents_t<A>;
  592. using sizes = ra::builtin_array_sizes_t<A>;
  593. return ra::SmallView<E, sizes, default_strides_<sizes>>((E *)(t)).iter();
  594. }
  595. } // namespace ra
  596. #undef CHECK_BOUNDS
  597. #undef RA_CHECK_BOUNDS_RA_SMALL