small.hh 28 KB

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