small.hh 27 KB

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