small.H 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. // -*- mode: c++; coding: utf-8 -*-
  2. /// @file small.H
  3. /// @brief Arrays with static dimensions. Compare with View, Container.
  4. // (c) Daniel Llorens - 2013-2016, 2018-2019
  5. // This library is free software; you can redistribute it and/or modify it under
  6. // the terms of the GNU Lesser General Public License as published by the Free
  7. // Software Foundation; either version 3 of the License, or (at your option) any
  8. // later version.
  9. #pragma once
  10. #include "ra/expr.H"
  11. #include "ra/mpdebug.H"
  12. #if defined(RA_CHECK_BOUNDS) && RA_CHECK_BOUNDS==0
  13. #define CHECK_BOUNDS( cond )
  14. #else
  15. #define CHECK_BOUNDS( cond ) RA_ASSERT( cond, 0 )
  16. #endif
  17. namespace ra {
  18. // --------------------
  19. // Develop indices for Small
  20. // --------------------
  21. template <class sizes, class strides>
  22. struct Indexer0
  23. {
  24. static_assert(mp::len<sizes> == mp::len<strides>, "mismatched sizes & strides");
  25. template <rank_t end, rank_t k, class P>
  26. constexpr static std::enable_if_t<k==end, dim_t> index_p_(dim_t const c, P const & p)
  27. {
  28. return c;
  29. }
  30. template <rank_t end, rank_t k, class P>
  31. constexpr static std::enable_if_t<(k<end), dim_t> index_p_(dim_t const c, P const & p)
  32. {
  33. CHECK_BOUNDS(inside(p[k], mp::first<sizes>::value));
  34. return Indexer0<mp::drop1<sizes>, mp::drop1<strides>>::template index_p_<end, k+1>(c + p[k] * mp::first<strides>::value, p);
  35. }
  36. template <class P>
  37. constexpr static dim_t index_p(P const & p) // for Container::at().
  38. {
  39. // gcc accepts p.size(), but I also need P = std::array to work. See also below.
  40. static_assert(mp::len<sizes> >= size_s<P>(), "too many indices");
  41. return index_p_<size_s<P>(), 0>(0, p);
  42. }
  43. template <class P, std::enable_if_t<size_s<P>()!=RANK_ANY, int> =0>
  44. constexpr static dim_t index_short(P const & p) // for ArrayIterator::at().
  45. {
  46. static_assert(mp::len<sizes> <= size_s<P>(), "too few indices");
  47. return index_p_<mp::len<sizes>, 0>(0, p);
  48. }
  49. template <class P, std::enable_if_t<size_s<P>()==RANK_ANY, int> =0>
  50. constexpr static dim_t index_short(P const & p) // for ArrayIterator::at().
  51. {
  52. CHECK_BOUNDS(mp::len<sizes> <= p.size());
  53. return index_p_<mp::len<sizes>, 0>(0, p);
  54. }
  55. };
  56. // --------------------
  57. // Small iterator
  58. // --------------------
  59. // TODO Refactor with cell_iterator / STLIterator for View?
  60. // V is always SmallBase<SmallView, ...>
  61. template <class V, rank_t cellr_=0>
  62. struct cell_iterator_small
  63. {
  64. constexpr static rank_t cellr_spec = cellr_;
  65. static_assert(cellr_spec!=RANK_ANY && cellr_spec!=RANK_BAD, "bad cell rank");
  66. constexpr static rank_t fullr = V::rank_s();
  67. constexpr static rank_t cellr = dependent_cell_rank(fullr, cellr_spec);
  68. constexpr static rank_t framer = dependent_frame_rank(fullr, cellr_spec);
  69. static_assert(cellr>=0 || cellr==RANK_ANY, "bad cell rank");
  70. static_assert(framer>=0 || framer==RANK_ANY, "bad frame rank");
  71. static_assert(fullr==cellr || gt_rank(fullr, cellr), "bad cell rank");
  72. constexpr static rank_t rank_s() { return framer; }
  73. constexpr static rank_t rank() { return framer; }
  74. using cell_sizes = mp::drop<typename V::sizes, framer>;
  75. using cell_strides = mp::drop<typename V::strides, framer>;
  76. using sizes = mp::take<typename V::sizes, framer>; // these are strides on atom_type * p !!
  77. using strides = mp::take<typename V::strides, framer>;
  78. using shape_type = std::array<dim_t, framer>;
  79. using atom_type = typename V::value_type; // FIXME fix ra_traits<SmallBase>
  80. using cell_type = SmallView<atom_type, cell_sizes, cell_strides>;
  81. using value_type = std::conditional_t<0==cellr, atom_type, cell_type>;
  82. using frame_type = SmallView<int, sizes, strides>; // only to compute ssizes
  83. cell_type c;
  84. constexpr cell_iterator_small(cell_iterator_small const & ci): c { ci.c.p } {}
  85. // see STLIterator for the case of s_[0]=0, etc. [ra12].
  86. constexpr cell_iterator_small(atom_type * p_): c { p_ } {}
  87. constexpr static dim_t size(int k) { CHECK_BOUNDS(inside(k, rank())); return V::size(k); }
  88. constexpr static dim_t size_s(int k) { /* CHECK_BOUNDS(inside(k, rank_s())); */ return V::size(k); }
  89. constexpr static dim_t stride(int k) { return k<rank() ? V::stride(k) : 0; }
  90. constexpr static bool keep_stride(dim_t step, int z, int j)
  91. {
  92. return step*(z<rank() ? stride(z) : 0)==(j<rank() ? stride(j) : 0);
  93. }
  94. constexpr void adv(rank_t k, dim_t d) { c.p += (k<rank()) * stride(k)*d; }
  95. constexpr auto flat() const
  96. {
  97. if constexpr (0==cellr) {
  98. return c.p;
  99. } else {
  100. return CellFlat<cell_type> { c };
  101. }
  102. }
  103. // Return type to allow either View & or View const & verb. Can't set self b/c original p isn't kept. TODO Think this over.
  104. template <class I>
  105. constexpr decltype(auto) at(I const & i_)
  106. {
  107. CHECK_BOUNDS(rank()<=dim_t(i_.size()) && "too few indices");
  108. if constexpr (0==cellr) {
  109. return c.p[Indexer0<sizes, strides>::index_short(i_)];
  110. } else {
  111. return cell_type(c.p + Indexer0<sizes, strides>::index_short(i_));
  112. }
  113. }
  114. #define DEF_ASSIGNOPS(OP) \
  115. template <class X> decltype(auto) constexpr operator OP(X && x) \
  116. { for_each([](auto && y, auto && x) { std::forward<decltype(y)>(y) OP x; }, *this, x); return *this; }
  117. FOR_EACH(DEF_ASSIGNOPS, =, *=, +=, -=, /=)
  118. #undef DEF_ASSIGNOPS
  119. };
  120. // --------------------
  121. // STLIterator for both cell_iterator_small & cell_iterator
  122. // FIXME make it work for any array iterator, as in ply_ravel, ply_index.
  123. // --------------------
  124. template <class S, class I, class P>
  125. inline void next_in_cube(rank_t const framer, S const & dim, I & i, P & p)
  126. {
  127. for (int k=framer-1; k>=0; --k) {
  128. ++i[k];
  129. if (i[k]<dim[k].size) {
  130. p += dim[k].stride;
  131. return;
  132. } else {
  133. i[k] = 0;
  134. p -= dim[k].stride*(dim[k].size-1);
  135. }
  136. }
  137. p = nullptr;
  138. }
  139. template <int k, class sizes, class strides, class I, class P> void
  140. next_in_cube(I & i, P & p)
  141. {
  142. if constexpr (k>=0) {
  143. ++i[k];
  144. if (i[k]<mp::ref<sizes, k>::value) {
  145. p += mp::ref<strides, k>::value;
  146. } else {
  147. i[k] = 0;
  148. p -= mp::ref<strides, k>::value*(mp::ref<sizes, k>::value-1);
  149. next_in_cube<k-1, sizes, strides>(i, p);
  150. }
  151. } else {
  152. p = nullptr;
  153. }
  154. }
  155. template <class Iterator>
  156. struct STLIterator
  157. {
  158. using value_type = typename Iterator::value_type;
  159. using difference_type = dim_t;
  160. using pointer = value_type *;
  161. using reference = value_type &;
  162. using iterator_category = std::forward_iterator_tag;
  163. using shape_type = typename Iterator::shape_type;
  164. Iterator ii;
  165. shape_type i;
  166. STLIterator(STLIterator const & it) = default;
  167. constexpr STLIterator & operator=(STLIterator const & it)
  168. {
  169. i = it.i;
  170. ii.Iterator::~Iterator(); // no-op except for View<RANK_ANY>. Still...
  171. new (&ii) Iterator(it.ii); // avoid ii = it.ii [ra11]
  172. return *this;
  173. }
  174. // FIXME shape_type may be std::array or std::vector, so can't get rid of ra_traits::make yet
  175. STLIterator(Iterator const & ii_): ii(ii_), i(ra::ra_traits<shape_type>::make(ii.rank(), 0))
  176. {
  177. // [ra12] Null p_ so begin()==end() for empty range. ply() uses sizes so this doesn't matter.
  178. if (ii.c.p!=nullptr && 0==ra::size(ii)) {
  179. ii.c.p = nullptr;
  180. }
  181. };
  182. template <class PP> bool operator==(PP const & j) const { return ii.c.p==j.ii.c.p; }
  183. template <class PP> bool operator!=(PP const & j) const { return ii.c.p!=j.ii.c.p; }
  184. decltype(auto) operator*() const { if constexpr (0==Iterator::cellr) return *ii.c.p; else return ii.c; }
  185. decltype(auto) operator*() { if constexpr (0==Iterator::cellr) return *ii.c.p; else return ii.c; }
  186. STLIterator & operator++()
  187. {
  188. if constexpr (0==Iterator::rank_s()) { // when rank==0, DIM_ANY check isn't enough :-/
  189. ii.c.p = nullptr;
  190. } else if constexpr (DIM_ANY != ra::size_s<Iterator>()) {
  191. next_in_cube<Iterator::rank()-1, typename Iterator::sizes, typename Iterator::strides>(i, ii.c.p);
  192. } else {
  193. next_in_cube(ii.rank(), ii.dim, i, ii.c.p);
  194. }
  195. return *this;
  196. }
  197. };
  198. template <class T> STLIterator<T> stl_iterator(T && t) { return STLIterator<T>(std::forward<T>(t)); }
  199. // --------------------
  200. // Base for both small view & container
  201. // --------------------
  202. template <class sizes_, class strides_, class ... I>
  203. struct FilterDims
  204. {
  205. using sizes = sizes_;
  206. using strides = strides_;
  207. };
  208. template <class sizes_, class strides_, class I0, class ... I>
  209. struct FilterDims<sizes_, strides_, I0, I ...>
  210. {
  211. constexpr static int s = is_beatable<I0>::skip;
  212. constexpr static int s_src = is_beatable<I0>::skip_src;
  213. using next = FilterDims<mp::drop<sizes_, s_src>, mp::drop<strides_, s_src>, I ...>;
  214. using sizes = mp::append<mp::take<sizes_, s>, typename next::sizes>;
  215. using strides = mp::append<mp::take<strides_, s>, typename next::strides>;
  216. };
  217. template <dim_t size0, dim_t stride0> inline
  218. constexpr dim_t select(dim_t i0)
  219. {
  220. CHECK_BOUNDS(inside(i0, size0));
  221. return i0*stride0;
  222. };
  223. template <dim_t size0, dim_t stride0, int n> inline
  224. constexpr dim_t select(dots_t<n> i0)
  225. {
  226. return 0;
  227. }
  228. template <class sizes, class strides> inline
  229. constexpr dim_t select_loop()
  230. {
  231. return 0;
  232. }
  233. template <class sizes, class strides, class I0, class ... I> inline
  234. constexpr dim_t select_loop(I0 i0, I ... i)
  235. {
  236. constexpr int s_src = is_beatable<I0>::skip_src;
  237. return select<mp::first<sizes>::value, mp::first<strides>::value>(i0)
  238. + select_loop<mp::drop<sizes, s_src>, mp::drop<strides, s_src>>(i ...);
  239. }
  240. template <class T> struct mat_uv { T uu, uv, vu, vv; };
  241. template <class T> struct mat_xy { T xx, xy, yx, yy; };
  242. template <class T> struct mat_uvz { T uu, uv, uz, vu, vv, vz, zu, zv, zz; };
  243. template <class T> struct mat_xyz { T xx, xy, xz, yx, yy, yz, zx, zy, zz; };
  244. template <class T> struct mat_xyzw { T xx, xy, xz, xw, yx, yy, yz, yw, zx, zy, zz, zw, wx, wy, wz, ww; };
  245. template <class T> struct vec_uv { T u, v; };
  246. template <class T> struct vec_xy { T x, y; };
  247. template <class T> struct vec_tp { T t, p; };
  248. template <class T> struct vec_uvz { T u, v, z; };
  249. template <class T> struct vec_xyz { T x, y, z; };
  250. template <class T> struct vec_rtp { T r, t, p; };
  251. template <class T> struct vec_xyzw { T u, v, z, w; };
  252. template <template <class, class, class> class Child_, class T, class sizes_, class strides_>
  253. struct SmallBase
  254. {
  255. using sizes = sizes_;
  256. using strides = strides_;
  257. using value_type = T;
  258. template <class TT> using BadDimension = mp::int_t<(TT::value<0 || TT::value==DIM_ANY || TT::value==DIM_BAD)>;
  259. static_assert(!mp::apply<mp::orb, mp::map<BadDimension, sizes>>::value, "negative dimensions");
  260. static_assert(mp::len<sizes> == mp::len<strides>, "bad strides"); // TODO full static check on strides.
  261. using Child = Child_<T, sizes, strides>;
  262. constexpr static rank_t rank() { return mp::len<sizes>; }
  263. constexpr static rank_t rank_s() { return mp::len<sizes>; }
  264. constexpr static dim_t size() { return mp::apply<mp::prod, sizes>::value; }
  265. constexpr static dim_t size_s() { return size(); }
  266. constexpr static auto ssizes = mp::tuple_values<std::array<dim_t, rank()>, sizes>();
  267. constexpr static auto sstrides = mp::tuple_values<std::array<dim_t, rank()>, strides>();
  268. constexpr static dim_t size(int k) { return ssizes[k]; }
  269. constexpr static dim_t size_s(int k) { return ssizes[k]; }
  270. constexpr static dim_t stride(int k) { return sstrides[k]; }
  271. constexpr T * data() { return static_cast<Child &>(*this).p; }
  272. constexpr T const * data() const { return static_cast<Child const &>(*this).p; }
  273. // Specialize for rank() integer-args -> scalar, same in ra::View in big.H.
  274. #define SUBSCRIPTS(CONST) \
  275. template <class ... I, std::enable_if_t<((0 + ... + std::is_integral_v<I>)<rank() \
  276. && (is_beatable<I>::static_p && ...)), int> = 0> \
  277. constexpr auto operator()(I ... i) CONST \
  278. { \
  279. using FD = FilterDims<sizes, strides, I ...>; \
  280. return SmallView<T CONST, typename FD::sizes, typename FD::strides> \
  281. (data()+select_loop<sizes, strides>(i ...)); \
  282. } \
  283. template <class ... I, std::enable_if_t<(0 + ... + std::is_integral_v<I>)==rank(), int> = 0> \
  284. constexpr decltype(auto) operator()(I ... i) CONST \
  285. { \
  286. return data()[select_loop<sizes, strides>(i ...)]; \
  287. } /* TODO More than one selector... */ \
  288. template <class ... I, std::enable_if_t<(!is_beatable<I>::static_p || ...), int> = 0> \
  289. constexpr auto operator()(I && ... i) CONST \
  290. { \
  291. return from(*this, std::forward<I>(i) ...); \
  292. } \
  293. /* BUG I must be fixed size, otherwise we can't make out the output type. */ \
  294. template <class I> \
  295. constexpr auto at(I const & i) CONST \
  296. { \
  297. return SmallView<T CONST, mp::drop<sizes, ra::size_s<I>()>, mp::drop<strides, ra::size_s<I>()>> \
  298. (data()+Indexer0<sizes, strides>::index_p(i)); \
  299. } \
  300. constexpr decltype(auto) operator[](dim_t const i) CONST \
  301. { \
  302. return (*this)(i); \
  303. }
  304. FOR_EACH(SUBSCRIPTS, /*const*/, const)
  305. #undef SUBSCRIPTS
  306. // see same thing for View.
  307. #define DEF_ASSIGNOPS(OP) \
  308. template <class X, std::enable_if_t<!mp::is_tuple_v<std::decay_t<X>>, int> =0> \
  309. constexpr Child & operator OP(X && x) \
  310. { \
  311. ra::start(static_cast<Child &>(*this)) OP x; \
  312. return static_cast<Child &>(*this); \
  313. }
  314. FOR_EACH(DEF_ASSIGNOPS, =, *=, +=, -=, /=)
  315. #undef DEF_ASSIGNOPS
  316. // braces don't match X &&
  317. constexpr Child & operator=(nested_arg<T, sizes> const & x)
  318. {
  319. ra::iter<-1>(static_cast<Child &>(*this)) = mp::from_tuple<std::array<typename nested_tuple<T, sizes>::sub, size(0)>>(x);
  320. return static_cast<Child &>(*this);
  321. }
  322. // braces row-major ravel for rank!=1
  323. constexpr Child & operator=(ravel_arg<T, sizes> const & x_)
  324. {
  325. auto x = mp::from_tuple<std::array<T, size()>>(x_);
  326. auto b = this->begin();
  327. for (auto const & xx: x) { *b = xx; ++b; } // std::copy will be constexpr in c++20
  328. return static_cast<Child &>(*this);
  329. }
  330. // TODO would replace by s(ra::iota) if that could be made constexpr
  331. #define DEF_AS(CONST) \
  332. template <int ss, int oo=0> \
  333. constexpr auto as() CONST \
  334. { \
  335. static_assert(rank()>=1, "bad rank for as<>"); \
  336. static_assert(ss>=0 && oo>=0 && ss+oo<=size(), "bad size for as<>"); \
  337. return SmallView<T CONST, mp::cons<mp::int_t<ss>, mp::drop1<sizes>>, strides>(this->data()+oo*this->stride(0)); \
  338. }
  339. FOR_EACH(DEF_AS, /* const */, const)
  340. #undef DEF_AS
  341. template <rank_t c=0> using iterator = ra::cell_iterator_small<SmallBase<SmallView, T, sizes, strides>, c>;
  342. template <rank_t c=0> using const_iterator = ra::cell_iterator_small<SmallBase<SmallView, T const, sizes, strides>, c>;
  343. template <rank_t c=0> constexpr iterator<c> iter() { return data(); }
  344. template <rank_t c=0> constexpr const_iterator<c> iter() const { return data(); }
  345. // FIXME see if we need to extend this for cellr!=0.
  346. // template <class P> using STLIterator = std::conditional_t<have_default_strides, P, STLIterator<Iterator<P>>>;
  347. constexpr static bool have_default_strides = std::is_same_v<strides, default_strides<sizes>>;
  348. template <class I, class P> using pick_STLIterator = std::conditional_t<have_default_strides, P, ra::STLIterator<I>>;
  349. using STLIterator = pick_STLIterator<iterator<0>, T *>;
  350. using STLConstIterator = pick_STLIterator<const_iterator<0>, T const *>;
  351. // TODO In C++17 begin() end() may be different types. See if we can use this to simplify end() and !=end() test.
  352. // TODO With default strides I can just return p. Make sure to test before changing this.
  353. constexpr STLIterator begin() { if constexpr (have_default_strides) return data(); else return iter(); }
  354. constexpr STLConstIterator begin() const { if constexpr (have_default_strides) return data(); else return iter(); }
  355. constexpr STLIterator end() { if constexpr (have_default_strides) return data()+size(); else return iterator<0>(nullptr); }
  356. constexpr STLConstIterator end() const { if constexpr (have_default_strides) return data()+size(); else return const_iterator<0>(nullptr); }
  357. // allowing rank 1 for coord types
  358. constexpr static bool convertible_to_scalar = size()==1; // rank()==0 || (rank()==1 && size()==1);
  359. // Renames.
  360. #define COMP_RENAME_C(name__, req_dim0__, req_dim1__, CONST) \
  361. operator name__<T> CONST &() CONST \
  362. { \
  363. static_assert(std::is_same_v<strides, default_strides<mp::int_list<req_dim0__, req_dim1__>>>, \
  364. "renames only on default strides"); \
  365. static_assert(size(0)==req_dim0__ && size(1)==req_dim1__, "dimension error"); \
  366. return reinterpret_cast<name__<T> CONST &>(*this); \
  367. }
  368. #define COMP_RENAME(name__, dim0__, dim1__) \
  369. COMP_RENAME_C(name__, dim0__, dim1__, /* const */) \
  370. COMP_RENAME_C(name__, dim0__, dim1__, const)
  371. COMP_RENAME(mat_xy, 2, 2)
  372. COMP_RENAME(mat_uv, 2, 2)
  373. COMP_RENAME(mat_xyz, 3, 3)
  374. COMP_RENAME(mat_uvz, 3, 3)
  375. COMP_RENAME(mat_xyzw, 4, 4)
  376. #undef COMP_RENAME
  377. #undef COMP_RENAME_C
  378. #define COMP_RENAME_C(name__, dim0__, CONST) \
  379. operator name__<T> CONST &() CONST \
  380. { \
  381. static_assert(std::is_same_v<strides, default_strides<mp::int_list<dim0__>>>, \
  382. "renames only on default strides"); \
  383. static_assert(size(0)==dim0__, "dimension error"); \
  384. return reinterpret_cast<name__<T> CONST &>(*this); \
  385. }
  386. #define COMP_RENAME(name__, dim0__) \
  387. COMP_RENAME_C(name__, dim0__, /* const */) \
  388. COMP_RENAME_C(name__, dim0__, const)
  389. COMP_RENAME(vec_xy, 2)
  390. COMP_RENAME(vec_uv, 2)
  391. COMP_RENAME(vec_tp, 2)
  392. COMP_RENAME(vec_xyz, 3)
  393. COMP_RENAME(vec_uvz, 3)
  394. COMP_RENAME(vec_rtp, 3)
  395. COMP_RENAME(vec_xyzw, 4)
  396. #undef COMP_RENAME
  397. #undef COMP_RENAME_C
  398. };
  399. // ---------------------
  400. // Small view & container
  401. // ---------------------
  402. // Strides are compile time, so we can put most members in the view type.
  403. template <class T, class sizes, class strides>
  404. struct SmallView: public SmallBase<SmallView, T, sizes, strides>
  405. {
  406. using Base = SmallBase<SmallView, T, sizes, strides>;
  407. using Base::rank, Base::size, Base::operator=;
  408. T * p;
  409. constexpr SmallView(T * p_): p(p_) {}
  410. constexpr SmallView(SmallView const & s): p(s.p) {}
  411. constexpr operator T & () { static_assert(Base::convertible_to_scalar); return p[0]; }
  412. constexpr operator T const & () const { static_assert(Base::convertible_to_scalar); return p[0]; };
  413. };
  414. template <class T, class sizes, class strides, class ... nested_args, class ... ravel_args>
  415. struct SmallArray<T, sizes, strides, std::tuple<nested_args ...>, std::tuple<ravel_args ...>>
  416. : public SmallBase<SmallArray, T, sizes, strides>
  417. {
  418. using Base = SmallBase<SmallArray, T, sizes, strides>;
  419. using Base::rank, Base::size;
  420. T p[Base::size()];
  421. constexpr SmallArray(): p() {}
  422. // braces don't match (X &&)
  423. constexpr SmallArray(nested_args const & ... x): p()
  424. {
  425. static_cast<Base &>(*this) = nested_arg<T, sizes> { x ... };
  426. }
  427. // braces row-major ravel for rank!=1
  428. constexpr SmallArray(ravel_args const & ... x): p()
  429. {
  430. static_cast<Base &>(*this) = ravel_arg<T, sizes> { x ... };
  431. }
  432. constexpr SmallArray(T const & t): p() // needed if T isn't registered as scalar [ra44]
  433. {
  434. for (auto & x: p) { x = t; } // std::fill will be constexpr in c++20
  435. }
  436. // X && x makes this a better match than nested_args ... for 1 argument.
  437. template <class X, std::enable_if_t<!std::is_same_v<T, std::decay_t<X>>
  438. && !mp::is_tuple_v<std::decay_t<X>>, int> =0>
  439. constexpr SmallArray(X && x): p()
  440. {
  441. static_cast<Base &>(*this) = x;
  442. }
  443. constexpr operator SmallView<T, sizes, strides> () { return SmallView<T, sizes, strides>(p); }
  444. constexpr operator SmallView<T const, sizes, strides> const () { return SmallView<T const, sizes, strides>(p); }
  445. // BUG these make SmallArray<T, N> std::is_convertible to T even though conversion isn't possible b/c of the assert.
  446. #define DEF_SCALARS(CONST) \
  447. constexpr operator T CONST & () CONST { static_assert(Base::convertible_to_scalar); return p[0]; } \
  448. T CONST & back() CONST \
  449. { \
  450. static_assert(Base::rank()==1, "bad rank for back"); \
  451. static_assert(Base::size()>0, "bad size for back"); \
  452. return (*this)[Base::size()-1]; \
  453. }
  454. FOR_EACH(DEF_SCALARS, /* const */, const)
  455. #undef DEF_SCALARS
  456. };
  457. // FIXME unfortunately necessary. Try to remove the need, also of (S, begin, end) in Container, once the nested_tuple constructors work.
  458. template <class A, class I, class J>
  459. A ravel_from_iterators(I && begin, J && end)
  460. {
  461. A a;
  462. std::copy(std::forward<I>(begin), std::forward<J>(end), a.begin());
  463. return a;
  464. }
  465. // FIXME Type_ seems superfluous
  466. template <template <class, class, class> class Type_,
  467. class T, class sizes, class strides>
  468. struct ra_traits_small
  469. {
  470. using V = Type_<T, sizes, strides>;
  471. using value_type = T;
  472. // constexpr static auto const & shape(V const & v) { return V::ssizes; }
  473. 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()); }
  474. #define MAKE_COND (std::is_same_v<strides, mp::int_list<1>> && std::is_same_v<sizes, mp::take<sizes, 1>>)
  475. constexpr static V make(dim_t const n)
  476. {
  477. static_assert(MAKE_COND, "bad type for ra_traits::make");
  478. CHECK_BOUNDS(n==V::size(0));
  479. return V {};
  480. }
  481. template <class TT> static V make(dim_t n, TT const & t)
  482. {
  483. static_assert(MAKE_COND, "bad type for ra_traits::make");
  484. CHECK_BOUNDS(n==V::size(0));
  485. return V(t);
  486. }
  487. #undef MAKE_COND
  488. constexpr static dim_t size(V const & v) { return v.size(); }
  489. constexpr static rank_t rank(V const & v) { return V::rank(); }
  490. constexpr static dim_t size_s() { return V::size(); }
  491. constexpr static rank_t rank_s() { return V::rank(); };
  492. };
  493. template <class T, class sizes, class strides>
  494. struct ra_traits_def<SmallArray<T, sizes, strides>>: public ra_traits_small<SmallArray, T, sizes, strides> {};
  495. template <class T, class sizes, class strides>
  496. struct ra_traits_def<SmallView<T, sizes, strides>>: public ra_traits_small<SmallView, T, sizes, strides> {};
  497. // ---------------------
  498. // Support for builtin arrays
  499. // ---------------------
  500. template <class T, class I=mp::iota<std::rank_v<T>>>
  501. struct builtin_array_sizes;
  502. template <class T, int ... I>
  503. struct builtin_array_sizes<T, mp::int_list<I ...>>
  504. {
  505. using type = mp::int_list<std::extent_v<T, I> ...>;
  506. };
  507. template <class T> using builtin_array_sizes_t = typename builtin_array_sizes<T>::type;
  508. template <class T>
  509. struct builtin_array_types
  510. {
  511. // do preserve const.
  512. using A = std::remove_volatile_t<std::remove_reference_t<T>>;
  513. using E = std::remove_all_extents_t<A>;
  514. using sizes = builtin_array_sizes_t<A>;
  515. using view = SmallView<E, sizes>;
  516. };
  517. // forward declared in type.H.
  518. template <class T, std::enable_if_t<is_builtin_array<T>, int>>
  519. inline constexpr auto start(T && t)
  520. {
  521. using Z = builtin_array_types<T>;
  522. return typename Z::view((typename Z::E *)(t)).iter();
  523. }
  524. template <class T>
  525. struct ra_traits_def<T, std::enable_if_t<is_builtin_array<T>>>
  526. {
  527. using S = typename builtin_array_types<T>::view;
  528. // FIXME messy start()
  529. constexpr static decltype(auto) shape(T const & t) { return ra::shape(start(t)); }
  530. constexpr static dim_t size(T const & t) { return ra::size_s<S>(); }
  531. constexpr static dim_t size_s() { return ra::size_s<S>(); }
  532. constexpr static rank_t rank(T const & t) { return S::rank(); }
  533. constexpr static rank_t rank_s() { return S::rank_s(); }
  534. };
  535. // --------------------
  536. // Small ops; cf view-ops.H. FIXME maybe there, or separate file.
  537. // --------------------
  538. template <class A, class i>
  539. struct axis_indices
  540. {
  541. template <class T> using match_index = mp::int_t<(T::value==i::value)>;
  542. using I = mp::iota<mp::len<A>>;
  543. using type = mp::Filter_<mp::map<match_index, A>, I>;
  544. static_assert(mp::len<type>> 0, "dst axis doesn't appear in transposed axes list");
  545. };
  546. template <class axes_list, class src_sizes, class src_strides>
  547. struct axes_list_indices
  548. {
  549. static_assert(mp::len<axes_list> == mp::len<src_sizes>, "bad size for transposed axes list");
  550. constexpr static int talmax = mp::fold<mp::max, void, axes_list>::value;
  551. constexpr static int talmin = mp::fold<mp::min, void, axes_list>::value;
  552. static_assert(talmax < mp::len<src_sizes>, "bad index in transposed axes list");
  553. static_assert(talmin >= 0, "bad index in transposed axes list");
  554. template <class dst_i> struct dst_indices_
  555. {
  556. using type = typename axis_indices<axes_list, dst_i>::type;
  557. template <class i> using sizesi = mp::ref<src_sizes, i::value>;
  558. template <class i> using stridesi = mp::ref<src_strides, i::value>;
  559. using stride = mp::fold<mp::sum, void, mp::map<stridesi, type>>;
  560. using size = mp::fold<mp::min, void, mp::map<sizesi, type>>;
  561. };
  562. template <class dst_i> using dst_indices = typename dst_indices_<dst_i>::type;
  563. template <class dst_i> using dst_size = typename dst_indices_<dst_i>::size;
  564. template <class dst_i> using dst_stride = typename dst_indices_<dst_i>::stride;
  565. using dst = mp::iota<(talmax>=0 ? (1+talmax) : 0)>;
  566. using type = mp::map<dst_indices, dst>;
  567. using sizes = mp::map<dst_size, dst>;
  568. using strides = mp::map<dst_stride, dst>;
  569. };
  570. #define DEF_TRANSPOSE(CONST) \
  571. template <int ... Iarg, template <class, class, class> class Child, class T, class sizes, class strides> \
  572. inline auto transpose(SmallBase<Child, T, sizes, strides> CONST & a) \
  573. { \
  574. using ti = axes_list_indices<mp::int_list<Iarg ...>, sizes, strides>; \
  575. return SmallView<T CONST, typename ti::sizes, typename ti::strides>(a.data()); \
  576. }; \
  577. \
  578. template <template <class, class, class> class Child, class T, class sizes, class strides> \
  579. inline auto diag(SmallBase<Child, T, sizes, strides> CONST & a) \
  580. { \
  581. return transpose<0, 0>(a); \
  582. }
  583. FOR_EACH(DEF_TRANSPOSE, /* const */, const)
  584. #undef DEF_TRANSPOSE
  585. // TODO Used by ProductRule; waiting for proper generalization.
  586. template <template <class, class, class> class Child1, class T1, class sizes1, class strides1,
  587. template <class, class, class> class Child2, class T2, class sizes2, class strides2>
  588. auto cat(SmallBase<Child1, T1, sizes1, strides1> const & a1, SmallBase<Child2, T2, sizes2, strides2> const & a2)
  589. {
  590. using A1 = SmallBase<Child1, T1, sizes1, strides1>;
  591. using A2 = SmallBase<Child2, T2, sizes2, strides2>;
  592. static_assert(A1::rank()==1 && A2::rank()==1, "bad ranks for cat"); // gcc accepts a1.rank(), etc.
  593. using T = std::decay_t<decltype(a1[0])>;
  594. Small<T, A1::size()+A2::size()> val;
  595. std::copy(a1.begin(), a1.end(), val.begin());
  596. std::copy(a2.begin(), a2.end(), val.begin()+a1.size());
  597. return val;
  598. }
  599. template <template <class, class, class> class Child1, class T1, class sizes1, class strides1,
  600. class A2,
  601. std::enable_if_t<is_scalar<A2>, int> =0>
  602. auto cat(SmallBase<Child1, T1, sizes1, strides1> const & a1, A2 const & a2)
  603. {
  604. using A1 = SmallBase<Child1, T1, sizes1, strides1>;
  605. static_assert(A1::rank()==1, "bad ranks for cat");
  606. using T = std::decay_t<decltype(a1[0])>;
  607. Small<T, A1::size()+1> val;
  608. std::copy(a1.begin(), a1.end(), val.begin());
  609. val[a1.size()] = a2;
  610. return val;
  611. }
  612. template <class A1,
  613. template <class, class, class> class Child2, class T2, class sizes2, class strides2,
  614. std::enable_if_t<is_scalar<A1>, int> =0>
  615. auto cat(A1 const & a1, SmallBase<Child2, T2, sizes2, strides2> const & a2)
  616. {
  617. using A2 = SmallBase<Child2, T2, sizes2, strides2>;
  618. static_assert(A2::rank()==1, "bad ranks for cat");
  619. using T = std::decay_t<decltype(a2[0])>;
  620. Small<T, 1+A2::size()> val;
  621. val[0] = a1;
  622. std::copy(a2.begin(), a2.end(), val.begin()+1);
  623. return val;
  624. }
  625. // FIXME should be local (constexpr lambda + mp::apply?)
  626. template <int s> struct explode_divop
  627. {
  628. template <class T> struct op_
  629. {
  630. static_assert((T::value/s)*s==T::value);
  631. using type = mp::int_t<T::value / s>;
  632. };
  633. template <class T> using op = typename op_<T>::type;
  634. };
  635. // See view-ops.H:explode, collapse. FIXME support real->complex, etc.
  636. template <class super_t,
  637. template <class, class, class> class Child, class T, class sizes, class strides>
  638. auto explode(SmallBase<Child, T, sizes, strides> & a)
  639. {
  640. using ta = SmallBase<Child, T, sizes, strides>;
  641. // the returned type has strides in super_t, but to support general strides we'd need strides in T. Maybe FIXME?
  642. static_assert(super_t::have_default_strides);
  643. constexpr rank_t ra = ta::rank_s();
  644. constexpr rank_t rb = super_t::rank_s();
  645. static_assert(std::is_same_v<mp::drop<sizes, ra-rb>, typename super_t::sizes>);
  646. static_assert(std::is_same_v<mp::drop<strides, ra-rb>, typename super_t::strides>);
  647. using cstrides = mp::map<explode_divop<ra::size_s<super_t>()>::template op, mp::take<strides, ra-rb>>;
  648. return SmallView<super_t, mp::take<sizes, ra-rb>, cstrides>((super_t *) a.data());
  649. }
  650. } // namespace ra
  651. #undef CHECK_BOUNDS