small.hh 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // ra-ra - Arrays with static lengths/strides, cf big.hh.
  3. // (c) Daniel Llorens - 2013-2023
  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. namespace ra {
  11. constexpr rank_t
  12. rank_sum(rank_t a, rank_t b) { return (ANY==a || ANY==b) ? ANY : a+b; }
  13. constexpr rank_t
  14. rank_diff(rank_t a, rank_t b) { return (ANY==a || ANY==b) ? ANY : a-b; }
  15. // cr>=0 is cell rank. -cr>0 is frame rank. TODO How to say frame rank 0.
  16. constexpr rank_t
  17. rank_cell(rank_t r, rank_t cr) { return cr>=0 ? cr /* indep */ : r==ANY ? ANY /* defer */ : (r+cr); }
  18. constexpr rank_t
  19. rank_frame(rank_t r, rank_t cr) { return r==ANY ? ANY /* defer */ : cr>=0 ? (r-cr) /* indep */ : -cr; }
  20. struct Dim { dim_t len, step; };
  21. inline std::ostream &
  22. operator<<(std::ostream & o, Dim const & dim) { return (o << "[Dim " << dim.len << " " << dim.step << "]"); }
  23. constexpr bool
  24. is_c_order_dimv(auto const & dimv, bool unitstep=true)
  25. {
  26. bool steps = true;
  27. dim_t s = 1;
  28. int k = dimv.size();
  29. if (!unitstep) {
  30. while (--k>=0 && 1==dimv[k].len) {}
  31. if (k<=0) { return true; }
  32. s = dimv[k].step*dimv[k].len;
  33. }
  34. while (--k>=0) {
  35. steps = steps && (1==dimv[k].len || dimv[k].step==s);
  36. s *= dimv[k].len;
  37. }
  38. return s==0 || steps;
  39. }
  40. constexpr bool
  41. is_c_order(auto const & v, bool unitstep=true) { return is_c_order_dimv(v.dimv, unitstep); }
  42. constexpr dim_t
  43. filldim(auto & dimv, auto && shape)
  44. {
  45. map(&Dim::len, dimv) = shape;
  46. dim_t s = 1;
  47. for (int k=dimv.size(); --k>=0;) {
  48. dimv[k].step = s;
  49. RA_CHECK(dimv[k].len>=0, "Bad len[", k, "] ", dimv[k].len, ".");
  50. s *= dimv[k].len;
  51. }
  52. return s;
  53. }
  54. // FIXME parameterize Small on dimv, then simplify.
  55. template <class lens>
  56. struct default_steps_
  57. {
  58. constexpr static int rank = mp::len<lens>;
  59. constexpr static auto dimv = [] { std::array<Dim, rank> dimv; filldim(dimv, mp::tuple_values<dim_t, lens>()); return dimv; } ();
  60. using type = decltype([] { return std::apply([](auto ... k) { return mp::int_list<dimv[k].step ...> {}; }, mp::iota<rank> {}); } ());
  61. };
  62. template <class lens> using default_steps = typename default_steps_<lens>::type;
  63. constexpr dim_t
  64. shape(auto const & v, int k)
  65. {
  66. RA_CHECK(inside(k, rank(v)), "Bad axis ", k, " for rank ", rank(v), ".");
  67. return v.len(k);
  68. }
  69. template <class A>
  70. constexpr void
  71. resize(A & a, dim_t s)
  72. {
  73. if constexpr (ANY==size_s<A>()) {
  74. RA_CHECK(s>=0, "Bad resize ", s, ".");
  75. a.resize(s);
  76. } else {
  77. RA_CHECK(s==start(a).len(0) || BAD==s, "Bad resize ", s, " vs ", start(a).len(0), ".");
  78. }
  79. }
  80. // --------------------
  81. // Slicing helpers
  82. // --------------------
  83. template <int n=BAD> struct dots_t { static_assert(n>=0 || BAD==n); };
  84. template <int n=BAD> constexpr dots_t<n> dots = dots_t<n>();
  85. constexpr auto all = dots<1>;
  86. template <int n> struct insert_t { static_assert(n>=0); };
  87. template <int n=1> constexpr insert_t<n> insert = insert_t<n>();
  88. template <class I> constexpr bool is_scalar_index = ra::is_zero_or_scalar<I>;
  89. struct beatable_t
  90. {
  91. bool rt, ct; // beatable at all and statically
  92. int src, dst, add; // axes on src, dst, and dst-src
  93. };
  94. template <class I> constexpr beatable_t beatable_def
  95. = { .rt=is_scalar_index<I>, .ct=is_scalar_index<I>, .src=1, .dst=0, .add=-1 };
  96. template <int n> constexpr beatable_t beatable_def<dots_t<n>>
  97. = { .rt=true, .ct = true, .src=n, .dst=n, .add=0 };
  98. template <int n> constexpr beatable_t beatable_def<insert_t<n>>
  99. = { .rt=true, .ct = true, .src=0, .dst=n, .add=n };
  100. template <class I>
  101. struct is_constant_iota
  102. {
  103. using Ilen = std::decay_t<decltype(with_len(ic<1>, std::declval<I>()))>; // arbitrary constant len
  104. constexpr static bool value = is_constant<typename Ilen::N> && is_constant<typename Ilen::S>;
  105. };
  106. template <class I> requires (is_iota<I>) constexpr beatable_t beatable_def<I>
  107. = { .rt=(BAD!=I::nn), .ct=is_constant_iota<I>::value, .src=1, .dst=1, .add=0 };
  108. template <class I> constexpr beatable_t beatable = beatable_def<std::decay_t<I>>;
  109. template <int k=0, class V>
  110. constexpr decltype(auto)
  111. maybe_len(V && v)
  112. {
  113. if constexpr (ANY!=std::decay_t<V>::len_s(k)) {
  114. return ic<std::decay_t<V>::len_s(k)>;
  115. } else {
  116. return v.len(k);
  117. }
  118. }
  119. template <class A, class ... I> constexpr decltype(auto) from(A && a, I && ... i);
  120. template <int N, class KK=mp::iota<N>> struct unbeat;
  121. template <int N, int ... k>
  122. struct unbeat<N, mp::int_list<k ...>>
  123. {
  124. template <class V, class ... I>
  125. constexpr static decltype(auto)
  126. op(V && v, I && ... i)
  127. {
  128. return from(RA_FWD(v), with_len(maybe_len<k>(v), RA_FWD(i)) ...);
  129. }
  130. };
  131. // --------------------
  132. // Develop indices
  133. // --------------------
  134. template <rank_t k, rank_t end>
  135. constexpr dim_t
  136. indexer(auto const & q, auto && pp, auto const & ss0, dim_t c)
  137. {
  138. if constexpr (k==end) {
  139. return c;
  140. } else {
  141. auto pk = *pp;
  142. RA_CHECK(inside(pk, q.len(k)) || (BAD==q.len(k) && 0==q.step(k)));
  143. return pp.mov(ss0), indexer<k+1, end>(q, pp, ss0, c + (q.step(k) * pk));
  144. }
  145. }
  146. constexpr dim_t
  147. indexer(rank_t end, auto const & q, auto && pp, auto const & ss0)
  148. {
  149. dim_t c = 0;
  150. for (rank_t k=0; k<end; ++k, pp.mov(ss0)) {
  151. auto pk = *pp;
  152. RA_CHECK(inside(pk, q.len(k)) || (BAD==q.len(k) && 0==q.step(k)));
  153. c += q.step(k) * pk;
  154. }
  155. return c;
  156. }
  157. template <class Q, class P>
  158. constexpr dim_t
  159. longer(Q const & q, P const & pp)
  160. {
  161. decltype(auto) p = start(pp);
  162. if constexpr (ANY==rank_s<P>()) {
  163. RA_CHECK(1==rank(p), "Bad rank ", rank(p), " for subscript.");
  164. } else {
  165. static_assert(1==rank_s<P>(), "Bad rank for subscript.");
  166. }
  167. if constexpr (ANY==size_s<P>() || ANY==rank_s<Q>()) {
  168. RA_CHECK(p.len(0) >= q.rank(), "Too few indices.");
  169. } else {
  170. static_assert(size_s<P>() >= rank_s<Q>(), "Too few indices.");
  171. }
  172. if constexpr (ANY==rank_s<Q>()) {
  173. return indexer(q.rank(), q, p, p.step(0));
  174. } else {
  175. return indexer<0, rank_s<Q>()>(q, p, p.step(0), 0);
  176. }
  177. }
  178. // --------------------
  179. // Small iterator
  180. // --------------------
  181. template <class T, class lens, class steps> struct SmallView;
  182. // TODO Refactor with CellBig / STLIterator
  183. template <class T, class Dimv, rank_t spec=0>
  184. struct CellSmall
  185. {
  186. constexpr static auto dimv = Dimv::value;
  187. static_assert(spec!=ANY && spec!=BAD, "Bad cell rank.");
  188. constexpr static rank_t fullr = ssize(dimv);
  189. constexpr static rank_t cellr = rank_cell(fullr, spec);
  190. constexpr static rank_t framer = rank_frame(fullr, spec);
  191. static_assert(cellr>=0 || cellr==ANY, "Bad cell rank.");
  192. static_assert(framer>=0 || framer==ANY, "Bad frame rank.");
  193. static_assert(choose_rank(fullr, cellr)==fullr, "Bad cell rank.");
  194. // FIXME Small take dimv instead of lens/steps
  195. using clens = decltype(std::apply([](auto ... i) { return mp::int_list<dimv[i].len ...> {}; }, mp::iota<cellr, framer> {}));
  196. using csteps = decltype(std::apply([](auto ... i) { return mp::int_list<dimv[i].step ...> {}; }, mp::iota<cellr, framer> {}));
  197. using ctype = SmallView<T, clens, csteps>;
  198. using value_type = std::conditional_t<0==cellr, T, ctype>;
  199. ctype c;
  200. consteval static rank_t rank() { return framer; }
  201. #pragma GCC diagnostic push // gcc 13.2
  202. #pragma GCC diagnostic warning "-Warray-bounds"
  203. constexpr static dim_t len(int k) { return dimv[k].len; } // len(0<=k<rank) or step(0<=k)
  204. #pragma GCC diagnostic pop
  205. constexpr static dim_t len_s(int k) { return len(k); }
  206. constexpr static dim_t step(int k) { return k<rank() ? dimv[k].step : 0; }
  207. constexpr void adv(rank_t k, dim_t d) { c.cp += step(k)*d; }
  208. constexpr static bool keep_step(dim_t st, int z, int j) { return st*step(z)==step(j); }
  209. // see STLIterator for len(0)=0, etc. [ra12].
  210. constexpr CellSmall(T * p): c { p } {}
  211. RA_ASSIGNOPS_SELF(CellSmall)
  212. RA_ASSIGNOPS_DEFAULT_SET
  213. constexpr decltype(auto)
  214. at(auto const & i) const
  215. {
  216. auto d = longer(*this, i);
  217. if constexpr (0==cellr) {
  218. return c.cp[d];
  219. } else {
  220. ctype cc(c); cc.cp += d;
  221. return cc;
  222. }
  223. }
  224. constexpr decltype(auto) operator*() const requires (0==cellr) { return *(c.cp); }
  225. constexpr ctype const & operator*() const requires (0!=cellr) { return c; }
  226. constexpr auto save() const { return c.cp; }
  227. constexpr void load(decltype(c.cp) cp) { c.cp = cp; }
  228. constexpr void mov(dim_t d) { c.cp += d; }
  229. };
  230. // ---------------------
  231. // nested braces for Small initializers. Cf braces_def for in big.hh.
  232. // ---------------------
  233. template <class T, class lens>
  234. struct nested_arg { using sub = noarg; };
  235. template <class T, class lens>
  236. struct small_args { using nested = std::tuple<noarg>; };
  237. template <class T, class lens> requires (0<mp::len<lens>)
  238. struct small_args<T, lens>
  239. {
  240. using nested = mp::makelist<mp::ref<lens, 0>::value, typename nested_arg<T, lens>::sub>;
  241. };
  242. template <class T, class lens, class steps, class nested_args = small_args<T, lens>::nested>
  243. struct SmallArray;
  244. template <class T, dim_t ... lens>
  245. using Small = SmallArray<T, mp::int_list<lens ...>, default_steps<mp::int_list<lens ...>>>;
  246. template <class T, int S0, int ... S>
  247. struct nested_arg<T, mp::int_list<S0, S ...>>
  248. {
  249. using sub = std::conditional_t<0==sizeof...(S), T, Small<T, S ...>>;
  250. };
  251. // --------------------
  252. // Base for both small view & container
  253. // --------------------
  254. template <class lens_, class steps_, class ... I>
  255. struct FilterDims
  256. {
  257. using lens = lens_;
  258. using steps = steps_;
  259. };
  260. template <class lens_, class steps_, class I0, class ... I> requires (!is_iota<I0>)
  261. struct FilterDims<lens_, steps_, I0, I ...>
  262. {
  263. constexpr static bool stretch = (beatable<I0>.dst==BAD);
  264. static_assert(!stretch || ((beatable<I>.dst!=BAD) && ...), "Cannot repeat stretch index.");
  265. constexpr static int dst = stretch ? (mp::len<lens_> - (0 + ... + beatable<I>.src)) : beatable<I0>.dst;
  266. constexpr static int src = stretch ? (mp::len<lens_> - (0 + ... + beatable<I>.src)) : beatable<I0>.src;
  267. using next = FilterDims<mp::drop<lens_, src>, mp::drop<steps_, src>, I ...>;
  268. using lens = mp::append<mp::take<lens_, dst>, typename next::lens>;
  269. using steps = mp::append<mp::take<steps_, dst>, typename next::steps>;
  270. };
  271. template <class lens_, class steps_, class I0, class ... I> requires (is_iota<I0>)
  272. struct FilterDims<lens_, steps_, I0, I ...>
  273. {
  274. constexpr static int dst = beatable<I0>.dst;
  275. constexpr static int src = beatable<I0>.src;
  276. using next = FilterDims<mp::drop<lens_, src>, mp::drop<steps_, src>, I ...>;
  277. using lens = mp::append<mp::int_list<I0::nn>, typename next::lens>;
  278. using steps = mp::append<mp::int_list<(mp::ref<steps_, 0>::value * I0::gets())>, typename next::steps>;
  279. };
  280. template <class T_, class lens_, class steps_>
  281. struct SmallBase
  282. {
  283. using lens = lens_;
  284. using steps = steps_;
  285. using T = T_;
  286. using sub = typename nested_arg<T, lens>::sub;
  287. static_assert(mp::len<lens> == mp::len<steps>, "Mismatched lengths & steps.");
  288. consteval static rank_t rank() { return mp::len<lens>; }
  289. constexpr static auto dimv = std::apply([](auto ... i) { return std::array<Dim, rank()> { Dim { mp::ref<lens, i>::value, mp::ref<steps, i>::value } ... }; }, mp::iota<rank()> {});
  290. constexpr static auto theshape = mp::tuple_values<dim_t, lens>();
  291. consteval static dim_t size() { return std::apply([](auto ... s) { return (s * ... * 1); }, theshape); }
  292. constexpr static dim_t len(int k) { return dimv[k].len; }
  293. consteval static dim_t size_s() { return size(); }
  294. constexpr static dim_t len_s(int k) { return len(k); }
  295. constexpr static dim_t step(int k) { return dimv[k].step; }
  296. consteval static decltype(auto) shape() { return theshape; }
  297. // TODO check steps
  298. static_assert(std::apply([](auto ... s) { return ((0<=s) && ...); }, theshape), "Bad shape.");
  299. constexpr static bool convertible_to_scalar = (1==size()); // allowed for 1 for coord types
  300. constexpr static dim_t len0 = rank()>0 ? len(0) : 0;
  301. };
  302. // ---------------------
  303. // Small view & container
  304. // ---------------------
  305. // Strides are compile time, so we can put most members in the view type.
  306. template <class T, class lens, class steps>
  307. struct SmallView: public SmallBase<T, lens, steps>
  308. {
  309. using Base = SmallBase<T, lens, steps>;
  310. using Base::rank, Base::size, Base::convertible_to_scalar, Base::dimv;
  311. using Base::len, Base::len_s, Base::step, Base::len0;
  312. using sub = Base::sub;
  313. T * cp;
  314. template <rank_t c=0> using iterator = CellSmall<T, ic_t<dimv>, c>;
  315. using ViewConst = SmallView<T const, lens, steps>;
  316. constexpr operator ViewConst () const requires (!std::is_const_v<T>) { return ViewConst(cp); }
  317. constexpr SmallView const & view() const { return *this; }
  318. constexpr SmallView(T * cp_): cp(cp_) {}
  319. // cf RA_ASSIGNOPS_SELF [ra38] [ra34]
  320. SmallView const & operator=(SmallView const & x) const { start(*this) = x; return *this; }
  321. constexpr SmallView(SmallView const & s) = default;
  322. template <class X> requires (!std::is_same_v<std::decay_t<X>, T> && !std::is_same_v<std::decay_t<X>, sub>)
  323. constexpr SmallView const & operator=(X && x) const { start(*this) = x; return *this; }
  324. #define ASSIGNOPS(OP) \
  325. constexpr SmallView const & operator OP(auto && x) const { start(*this) OP x; return *this; }
  326. FOR_EACH(ASSIGNOPS, *=, +=, -=, /=)
  327. #undef ASSIGNOPS
  328. // needed if T isn't registered as scalar [ra44]
  329. constexpr SmallView const &
  330. operator=(T const & t) const
  331. {
  332. std::fill(cp, cp+size(), t); return *this;
  333. }
  334. // nested braces
  335. constexpr SmallView const &
  336. operator=(sub (&&x)[len0]) const requires (0<rank() && 0!=len0 && (1!=rank() || 1!=len0))
  337. {
  338. ra::iter<-1>(*this) = x; return *this;
  339. }
  340. // row-major ravel braces
  341. constexpr SmallView const &
  342. operator=(T (&&x)[size()]) const requires ((rank()>1) && (size()>1))
  343. {
  344. std::copy(std::begin(x), std::end(x), begin()); return *this;
  345. }
  346. template <int k>
  347. constexpr static dim_t
  348. select(dim_t i)
  349. {
  350. RA_CHECK(inside(i, len(k)), "Bad index in len[", k, "]=", len(k), ": ", i, ".");
  351. return step(k)*i;
  352. }
  353. template <int k>
  354. constexpr static dim_t
  355. select(is_iota auto i)
  356. {
  357. if constexpr (0==i.n) {
  358. return 0;
  359. } else if constexpr ((1==i.n ? 1 : (i.s<0 ? -i.s : i.s)*(i.n-1)+1) > len(k)) { // FIXME c++23 std::abs
  360. static_assert(always_false<k>, "Bad index.");
  361. } else {
  362. RA_CHECK(inside(i, len(k)), "Bad index in len[", k, "]=", len(k), ": iota [", i.n, " ", i.i, " ", i.s, "]");
  363. }
  364. return step(k)*i.i;
  365. }
  366. template <int k, int n>
  367. constexpr static dim_t
  368. select(dots_t<n> i)
  369. {
  370. return 0;
  371. }
  372. template <int k, class I0, class ... I>
  373. constexpr static dim_t
  374. select_loop(I0 && i0, I && ... i)
  375. {
  376. constexpr int nn = (BAD==beatable<I0>.src) ? (rank() - k - (0 + ... + beatable<I>.src)) : beatable<I0>.src;
  377. return select<k>(with_len(ic<len(k)>, RA_FWD(i0)))
  378. + select_loop<k + nn>(RA_FWD(i) ...);
  379. }
  380. template <int k>
  381. consteval static dim_t
  382. select_loop()
  383. {
  384. return 0;
  385. }
  386. template <class ... I>
  387. constexpr decltype(auto)
  388. operator()(I && ... i) const
  389. {
  390. constexpr int stretch = (0 + ... + (beatable<I>.dst==BAD));
  391. static_assert(stretch<=1, "Cannot repeat stretch index.");
  392. if constexpr ((0 + ... + is_scalar_index<I>)==rank()) {
  393. return cp[select_loop<0>(i ...)];
  394. // FIXME with_len before this, cf is_constant_iota
  395. } else if constexpr ((beatable<I>.ct && ...)) {
  396. using FD = FilterDims<lens, steps, std::decay_t<I> ...>;
  397. return SmallView<T, typename FD::lens, typename FD::steps> (cp + select_loop<0>(i ...));
  398. // TODO partial beating
  399. } else {
  400. // FIXME must forward *this so that expr can hold to it (c++23 deducing this).
  401. // We get away with not doing it on Container bc Container's view is self, but here we create new temp views on every Small::view() call. But I really just want to copy the pointer around.
  402. return unbeat<sizeof...(I)>::op(SmallView(*this), RA_FWD(i) ...);
  403. }
  404. }
  405. constexpr decltype(auto)
  406. operator[](auto && ... i) const { return (*this)(RA_FWD(i) ...); } // see above about forwarding
  407. template <class I>
  408. constexpr decltype(auto)
  409. at(I && i) const
  410. {
  411. // FIXME can't say 'frame rank 0' so -size wouldn't work.
  412. constexpr rank_t crank = rank_diff(rank(), ra::size_s<I>());
  413. static_assert(crank>=0); // to make out the output type
  414. return iter<crank>().at(RA_FWD(i));
  415. }
  416. // maybe remove if ic becomes easier to use
  417. template <int ss, int oo=0> constexpr auto as() const { return operator()(ra::iota(ra::ic<ss>, oo)); }
  418. constexpr T * data() const { return cp; }
  419. template <rank_t c=0> constexpr iterator<c> iter() const { return cp; }
  420. constexpr static bool def = is_c_order_dimv(dimv);
  421. constexpr auto begin() const { if constexpr (def) return cp; else return STLIterator(iter()); }
  422. constexpr auto end() const requires (def) { return cp+size(); }
  423. constexpr static auto end() requires (!def) { return std::default_sentinel; }
  424. constexpr T & back() const { static_assert(rank()>=1 && size()>0, "No back()."); return cp[size()-1]; }
  425. constexpr operator T & () const { static_assert(convertible_to_scalar); return cp[0]; }
  426. };
  427. #if defined (__clang__)
  428. template <class T, int N> using extvector __attribute__((ext_vector_type(N))) = T;
  429. #else
  430. template <class T, int N> using extvector __attribute__((vector_size(N*sizeof(T)))) = T;
  431. #endif
  432. template <class Z, class ... T> constexpr static bool equal_to_any = (std::is_same_v<Z, T> || ...);
  433. template <class T, size_t N>
  434. consteval size_t
  435. align_req()
  436. {
  437. if constexpr (equal_to_any<T, char, unsigned char, short, unsigned short,
  438. int, unsigned int, long, unsigned long, long long, unsigned long long,
  439. float, double>
  440. && 0<N && 0==(N & (N-1))) {
  441. return alignof(extvector<T, N>);
  442. } else {
  443. return alignof(T[N]);
  444. }
  445. }
  446. template <class T, class lens, class steps, class ... nested_args>
  447. struct
  448. #if RA_DO_OPT_SMALLVECTOR==1
  449. alignas(align_req<T, mp::apply<mp::prod, lens>::value>())
  450. #else
  451. #endif
  452. SmallArray<T, lens, steps, std::tuple<nested_args ...>>
  453. : public SmallBase<T, lens, steps>
  454. {
  455. using Base = SmallBase<T, lens, steps>;
  456. using Base::rank, Base::size, Base::convertible_to_scalar, Base::len0;
  457. using sub = Base::sub;
  458. T cp[size()]; // cf what std::array does for zero size; wish zero size just worked :-/
  459. using View = SmallView<T, lens, steps>;
  460. using ViewConst = SmallView<T const, lens, steps>;
  461. constexpr View view() { return View(cp); }
  462. constexpr ViewConst view() const { return ViewConst(cp); }
  463. // conversion to const
  464. constexpr operator View () { return View(cp); }
  465. constexpr operator ViewConst () const { return ViewConst(cp); }
  466. constexpr SmallArray() {}
  467. // needed if T isn't registered as scalar [ra44]
  468. constexpr SmallArray(T const & t)
  469. {
  470. for (auto & x: cp) { x = t; }
  471. }
  472. // nested braces FIXME p1219??
  473. constexpr SmallArray(nested_args const & ... x)
  474. requires ((0<rank() && 0!=Base::len(0) && (1!=rank() || 1!=Base::len(0))))
  475. {
  476. view() = { x ... };
  477. }
  478. // row-major ravel braces
  479. constexpr SmallArray(T const & x0, std::convertible_to<T> auto const & ... x)
  480. requires ((rank()>1) && (size()>1) && ((1+sizeof...(x))==size()))
  481. {
  482. view() = { static_cast<T>(x0), static_cast<T>(x) ... };
  483. }
  484. SmallArray & operator=(SmallArray const &) = default;
  485. // X && x makes this a better match than nested_args ... for 1 argument.
  486. template <class X> requires (!std::is_same_v<std::decay_t<X>, T> && !std::is_same_v<std::decay_t<X>, sub>)
  487. constexpr SmallArray(X && x)
  488. {
  489. view() = RA_FWD(x);
  490. }
  491. #define ASSIGNOPS(OP) \
  492. constexpr decltype(auto) operator OP(auto && x) { view() OP RA_FWD(x); return *this; }
  493. FOR_EACH(ASSIGNOPS, =, *=, +=, -=, /=)
  494. #undef ASSIGNOPS
  495. // FIXME
  496. constexpr static bool def = View::def;
  497. template <rank_t c=0> using iterator = View::template iterator<c>;
  498. template <rank_t c=0> using const_iterator = ViewConst::template iterator<c>;
  499. #define RA_CONST_OR_NOT(CONST) \
  500. constexpr T CONST & back() CONST { return view().back(); } \
  501. constexpr T CONST * data() CONST { return view().data(); } \
  502. constexpr operator T CONST & () CONST requires (convertible_to_scalar) { return view(); } \
  503. constexpr decltype(auto) operator()(auto && ... a) CONST { return view()(RA_FWD(a) ...); } \
  504. constexpr decltype(auto) operator[](auto && ... a) CONST { return view()(RA_FWD(a) ...); } \
  505. constexpr decltype(auto) at(auto && i) CONST { return view().at(RA_FWD(i)); } \
  506. template <int ss, int oo=0> constexpr decltype(auto) as() CONST { return view().template as<ss, oo>(); } \
  507. template <rank_t c=0> constexpr auto iter() CONST { return view().template iter<c>(); } \
  508. constexpr auto begin() CONST { return view().begin(); } \
  509. constexpr auto end() CONST { return view().end(); }
  510. FOR_EACH(RA_CONST_OR_NOT, /*not const*/, const)
  511. #undef RA_CONST_OR_NOT
  512. };
  513. template <class A0, class ... A> SmallArray(A0, A ...) -> Small<A0, 1+sizeof...(A)>;
  514. // FIXME tagged ravel constructor. Then we can pass any rank 1 thing not just iterator pairs.
  515. template <class A>
  516. constexpr auto
  517. ravel_from_iterators(auto && begin, auto && end)
  518. {
  519. A a;
  520. std::copy(RA_FWD(begin), RA_FWD(end), a.begin());
  521. return a;
  522. }
  523. // ---------------------
  524. // Builtin arrays.
  525. // ---------------------
  526. template <class T>
  527. constexpr auto
  528. peel(T && t)
  529. {
  530. static_assert(0 < std::extent_v<std::remove_cvref_t<T>, 0>);
  531. if constexpr (1 < std::rank_v<std::remove_cvref_t<T>>) {
  532. return peel(*std::data(RA_FWD(t)));
  533. } else {
  534. return std::data(t);
  535. }
  536. }
  537. constexpr auto
  538. start(is_builtin_array auto && t)
  539. {
  540. using A = std::remove_reference_t<decltype(t)>; // preserve const
  541. using lens = decltype(std::apply([](auto ... i) { return mp::int_list<std::extent_v<A, i> ...> {}; },
  542. mp::iota<std::rank_v<A>> {}));
  543. return SmallView<std::remove_all_extents_t<A>, lens, default_steps<lens>>(peel(t)).iter();
  544. }
  545. // --------------------
  546. // Small view ops, see View ops in big.hh.
  547. // FIXME Merge with Reframe (eg beat(reframe(a)) -> transpose(a) ?)
  548. // --------------------
  549. template <class A, class i>
  550. struct axis_indices
  551. {
  552. template <class T> using match_index = ic_t<(T::value==i::value)>;
  553. using I = mp::iota<mp::len<A>>;
  554. using type = mp::Filter_<mp::map<match_index, A>, I>;
  555. };
  556. template <class axes_list, class src_lens, class src_steps>
  557. struct axes_list_indices
  558. {
  559. static_assert(mp::len<axes_list> == mp::len<src_lens>, "Bad size for transposed axes list.");
  560. constexpr static int talmax = mp::fold<mp::max, void, axes_list>::value;
  561. constexpr static int talmin = mp::fold<mp::min, void, axes_list>::value;
  562. static_assert(talmin >= 0, "Bad index in transposed axes list.");
  563. template <class dst_i> struct dst_indices_
  564. {
  565. using type = typename axis_indices<axes_list, dst_i>::type;
  566. template <class i> using lensi = mp::ref<src_lens, i::value>;
  567. template <class i> using stepsi = mp::ref<src_steps, i::value>;
  568. using step = mp::fold<mp::sum, void, mp::map<stepsi, type>>;
  569. using len = mp::fold<mp::min, void, mp::map<lensi, type>>;
  570. };
  571. template <class dst_i> using dst_indices = typename dst_indices_<dst_i>::type;
  572. template <class dst_i> using dst_len = typename dst_indices_<dst_i>::len;
  573. template <class dst_i> using dst_step = typename dst_indices_<dst_i>::step;
  574. using dst = mp::iota<(talmax>=0 ? (1+talmax) : 0)>;
  575. using type = mp::map<dst_indices, dst>;
  576. using lens = mp::map<dst_len, dst>;
  577. using steps = mp::map<dst_step, dst>;
  578. };
  579. RA_IS_DEF(cv_smallview, (std::is_convertible_v<A, SmallView<typename A::T, typename A::lens, typename A::steps>>));
  580. template <int ... Iarg, class A> requires (cv_smallview<A>)
  581. constexpr auto
  582. transpose(A && a_)
  583. {
  584. decltype(auto) a = a_.view();
  585. using AA = typename std::decay_t<decltype(a)>;
  586. using ti = axes_list_indices<mp::int_list<Iarg ...>, typename AA::lens, typename AA::steps>;
  587. return SmallView<typename AA::T, typename ti::lens, typename ti::steps>(a.data());
  588. };
  589. template <class A> requires (cv_smallview<A>)
  590. constexpr auto
  591. diag(A && a)
  592. {
  593. return transpose<0, 0>(a);
  594. }
  595. // TODO generalize
  596. template <class A1, class A2> requires (cv_smallview<A1> || cv_smallview<A2>)
  597. constexpr auto
  598. cat(A1 && a1_, A2 && a2_)
  599. {
  600. if constexpr (cv_smallview<A1> && cv_smallview<A2>) {
  601. decltype(auto) a1 = a1_.view();
  602. decltype(auto) a2 = a2_.view();
  603. static_assert(1==a1.rank() && 1==a2.rank(), "Bad ranks for cat."); // gcc accepts a1.rank(), etc.
  604. using T = std::common_type_t<std::decay_t<decltype(a1[0])>, std::decay_t<decltype(a2[0])>>;
  605. Small<T, a1.size()+a2.size()> val;
  606. std::copy(a1.begin(), a1.end(), val.begin());
  607. std::copy(a2.begin(), a2.end(), val.begin()+a1.size());
  608. return val;
  609. } else if constexpr (cv_smallview<A1> && is_scalar<A2>) {
  610. decltype(auto) a1 = a1_.view();
  611. static_assert(1==a1.rank(), "bad ranks for cat");
  612. using T = std::common_type_t<std::decay_t<decltype(a1[0])>, A2>;
  613. Small<T, a1.size()+1> val;
  614. std::copy(a1.begin(), a1.end(), val.begin());
  615. val[a1.size()] = a2_;
  616. return val;
  617. } else if constexpr (is_scalar<A1> && cv_smallview<A2>) {
  618. decltype(auto) a2 = a2_.view();
  619. static_assert(1==a2.rank(), "bad ranks for cat");
  620. using T = std::common_type_t<A1, std::decay_t<decltype(a2[0])>>;
  621. Small<T, 1+a2.size()> val;
  622. val[0] = a1_;
  623. std::copy(a2.begin(), a2.end(), val.begin()+1);
  624. return val;
  625. } else {
  626. static_assert(always_false<A1, A2>);
  627. }
  628. }
  629. template <class super_t, class A> requires (cv_smallview<A>)
  630. constexpr auto
  631. explode(A && a_)
  632. {
  633. // result has steps in super_t, but to support general steps we'd need steps in T. FIXME?
  634. decltype(auto) a = a_.view();
  635. using AA = std::decay_t<decltype(a)>;
  636. static_assert(super_t::def);
  637. constexpr rank_t ra = ra::rank_s<AA>();
  638. constexpr rank_t rb = rank_s<super_t>();
  639. static_assert(std::is_same_v<mp::drop<typename AA::lens, ra-rb>, typename super_t::lens>);
  640. static_assert(std::is_same_v<mp::drop<typename AA::steps, ra-rb>, typename super_t::steps>);
  641. constexpr dim_t supers = ra::size_s<super_t>();
  642. using csteps = decltype(std::apply([](auto ... i)
  643. {
  644. static_assert(((i==(i/supers)*supers) && ...));
  645. return mp::int_list<(i/supers) ...> {};
  646. }, mp::take<typename AA::steps, ra-rb> {}));
  647. return SmallView<super_t, mp::take<typename AA::lens, ra-rb>, csteps>((super_t *) a.data());
  648. }
  649. } // namespace ra