big.hh 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // ra-ra - Arrays with dynamic lengths/strides, cf small.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 "small.hh"
  10. #include <memory>
  11. #include <complex> // for view ops
  12. namespace ra {
  13. // --------------------
  14. // nested braces for Container initializers, cf small_args in small.hh. FIXME Let any expr = braces.
  15. // --------------------
  16. template <class T, rank_t rank>
  17. struct braces_def { using type = noarg; };
  18. template <class T, rank_t rank>
  19. using braces = braces_def<T, rank>::type;
  20. template <class T, rank_t rank> requires (rank==1)
  21. struct braces_def<T, rank> { using type = std::initializer_list<T>; };
  22. template <class T, rank_t rank> requires (rank>1)
  23. struct braces_def<T, rank> { using type = std::initializer_list<braces<T, rank-1>>; };
  24. template <int i, class T, rank_t rank>
  25. constexpr dim_t
  26. braces_len(braces<T, rank> const & l)
  27. {
  28. if constexpr (i>=rank) {
  29. return 0;
  30. } else if constexpr (i==0) {
  31. return l.size();
  32. } else {
  33. return braces_len<i-1, T, rank-1>(*(l.begin()));
  34. }
  35. }
  36. template <class T, rank_t rank, class S = std::array<dim_t, rank>>
  37. constexpr S
  38. braces_shape(braces<T, rank> const & l)
  39. {
  40. return std::apply([&l](auto ... i) { return S { braces_len<i.value, T, rank>(l) ... }; }, mp::iota<rank> {});
  41. }
  42. // --------------------
  43. // ViewBig
  44. // --------------------
  45. // FIXME size is immutable so that it can be kept together with rank.
  46. // TODO Parameterize on Child having .data() so that there's only one pointer.
  47. // TODO Parameterize on iterator type not on value type.
  48. // TODO Constructor checks (nonnegative lens, steps inside, etc.).
  49. template <class T, rank_t RANK>
  50. struct ViewBig
  51. {
  52. using Dimv = std::conditional_t<ANY==RANK, vector_default_init<Dim>, Small<Dim, ANY==RANK ? 0 : RANK>>;
  53. Dimv dimv;
  54. T * cp;
  55. consteval static rank_t rank() requires (RANK!=ANY) { return RANK; }
  56. constexpr rank_t rank() const requires (RANK==ANY) { return rank_t(dimv.size()); }
  57. constexpr static dim_t len_s(int k) { return ANY; }
  58. constexpr dim_t len(int k) const { return dimv[k].len; }
  59. constexpr dim_t step(int k) const { return dimv[k].step; }
  60. constexpr auto data() const { return cp; }
  61. constexpr dim_t size() const { return prod(map(&Dim::len, dimv)); }
  62. constexpr bool empty() const { return any(0==map(&Dim::len, dimv)); }
  63. constexpr ViewBig(): cp() {} // FIXME used by Container constructors
  64. constexpr ViewBig(Dimv const & dimv_, T * cp_): dimv(dimv_), cp(cp_) {} // [ra36]
  65. constexpr ViewBig(auto && s, T * cp_): cp(cp_)
  66. {
  67. ra::resize(dimv, ra::size(s)); // [ra37]
  68. if constexpr (std::is_convertible_v<value_t<decltype(s)>, Dim>) {
  69. start(dimv) = s;
  70. } else {
  71. filldim(dimv, s);
  72. }
  73. }
  74. constexpr ViewBig(std::initializer_list<dim_t> s, T * cp_): ViewBig(start(s), cp_) {}
  75. // cf RA_ASSIGNOPS_SELF [ra38] [ra34]
  76. ViewBig const & operator=(ViewBig && x) const { start(*this) = x; return *this; }
  77. ViewBig const & operator=(ViewBig const & x) const { start(*this) = x; return *this; }
  78. constexpr ViewBig(ViewBig &&) = default;
  79. constexpr ViewBig(ViewBig const &) = default;
  80. #define ASSIGNOPS(OP) \
  81. ViewBig const & operator OP (auto && x) const { start(*this) OP x; return *this; }
  82. FOR_EACH(ASSIGNOPS, =, *=, +=, -=, /=)
  83. #undef ASSIGNOPS
  84. // braces row-major ravel for rank!=1. See Container::fill1
  85. using ravel_arg = std::conditional_t<RANK==1, noarg, std::initializer_list<T>>;
  86. ViewBig const & operator=(ravel_arg const x) const
  87. {
  88. auto xsize = ssize(x);
  89. RA_CHECK(size()==xsize, "Mismatched sizes ", ViewBig::size(), " ", xsize, ".");
  90. std::ranges::copy_n(x.begin(), xsize, begin());
  91. return *this;
  92. }
  93. constexpr ViewBig const & operator=(braces<T, RANK> x) const requires (RANK!=ANY) { ra::iter<-1>(*this) = x; return *this; }
  94. #define RA_BRACES_ANY(N) \
  95. constexpr ViewBig const & operator=(braces<T, N> x) const requires (RANK==ANY) { ra::iter<-1>(*this) = x; return *this; }
  96. FOR_EACH(RA_BRACES_ANY, 2, 3, 4);
  97. #undef RA_BRACES_ANY
  98. template <rank_t c=0> constexpr auto iter() const && { return Cell<T, Dimv, ic_t<c>>(cp, std::move(dimv)); }
  99. template <rank_t c=0> constexpr auto iter() const & { return Cell<T, Dimv const &, ic_t<c>>(cp, dimv); }
  100. constexpr auto iter(rank_t c) const && { return Cell<T, Dimv, dim_t>(cp, std::move(dimv), c); }
  101. constexpr auto iter(rank_t c) const & { return Cell<T, Dimv const &, dim_t>(cp, dimv, c); }
  102. constexpr auto begin() const { return STLIterator(iter<0>()); }
  103. constexpr decltype(auto) static end() { return std::default_sentinel; }
  104. constexpr dim_t
  105. select(Dim * dim, int k, dim_t i) const
  106. {
  107. RA_CHECK(inside(i, len(k)), "Bad index ", i, " for len[", k, "]=", len(k), ".");
  108. return step(k)*i;
  109. }
  110. constexpr dim_t
  111. select(Dim * dim, int k, is_iota auto i) const
  112. {
  113. RA_CHECK(inside(i, len(k)), "Bad index iota [", i.n, " ", i.i, " ", i.s, "] for len[", k, "]=", len(k), ".");
  114. *dim = { .len = i.n, .step = step(k) * i.s };
  115. return 0==i.n ? 0 : step(k)*i.i;
  116. }
  117. template <class I0, class ... I>
  118. constexpr dim_t
  119. select_loop(Dim * dim, int k, I0 && i0, I && ... i) const
  120. {
  121. return select(dim, k, with_len(len(k), RA_FWD(i0)))
  122. + select_loop(dim + beatable<I0>.dst, k + beatable<I0>.src, RA_FWD(i) ...);
  123. }
  124. template <int n, class ... I>
  125. constexpr dim_t
  126. select_loop(Dim * dim, int k, dots_t<n> i0, I && ... i) const
  127. {
  128. int nn = (BAD==n) ? (rank() - k - (0 + ... + beatable<I>.src)) : n;
  129. for (Dim * end = dim+nn; dim!=end; ++dim, ++k) {
  130. *dim = dimv[k];
  131. }
  132. return select_loop(dim, k, RA_FWD(i) ...);
  133. }
  134. template <int n, class ... I>
  135. constexpr dim_t
  136. select_loop(Dim * dim, int k, insert_t<n> i0, I && ... i) const
  137. {
  138. for (Dim * end = dim+n; dim!=end; ++dim) {
  139. *dim = { .len = BAD, .step = 0 };
  140. }
  141. return select_loop(dim, k, RA_FWD(i) ...);
  142. }
  143. constexpr static dim_t
  144. select_loop(Dim * dim, int k) { return 0; }
  145. template <class ... I>
  146. constexpr decltype(auto)
  147. operator()(I && ... i) const
  148. {
  149. constexpr int stretch = (0 + ... + (beatable<I>.dst==BAD));
  150. static_assert(stretch<=1, "Cannot repeat stretch index.");
  151. if constexpr ((0 + ... + is_scalar_index<I>)==RANK) {
  152. return cp[select_loop(nullptr, 0, i ...)];
  153. } else if constexpr ((beatable<I>.rt && ...)) {
  154. constexpr rank_t extended = (0 + ... + beatable<I>.add);
  155. ViewBig<T, rank_sum(RANK, extended)> sub;
  156. rank_t subrank = rank()+extended;
  157. if constexpr (ANY==RANK) {
  158. sub.dimv.resize(subrank);
  159. }
  160. sub.cp = cp + select_loop(sub.dimv.data(), 0, i ...);
  161. // fill the rest of dim, skipping over beatable subscripts.
  162. for (int k = (0==stretch ? (0 + ... + beatable<I>.dst) : subrank); k<subrank; ++k) {
  163. sub.dimv[k] = dimv[k-extended];
  164. }
  165. return sub;
  166. // TODO partial beating. FIXME should forward this; see unbeat, ViewSmall::operator()
  167. } else {
  168. return unbeat<sizeof...(I)>::op(*this, RA_FWD(i) ...);
  169. }
  170. }
  171. constexpr decltype(auto)
  172. operator[](auto && ... i) const { return (*this)(RA_FWD(i) ...); }
  173. template <class I>
  174. constexpr decltype(auto)
  175. at(I && i) const
  176. {
  177. // can't say 'frame rank 0' so -size wouldn't work. What about ra::len
  178. constexpr rank_t crank = rank_diff(RANK, ra::size_s<I>());
  179. if constexpr (ANY==crank) {
  180. return iter(rank()-ra::size(i)).at(RA_FWD(i));
  181. } else {
  182. return iter<crank>().at(RA_FWD(i));
  183. }
  184. }
  185. constexpr
  186. operator T & () const
  187. {
  188. if constexpr (0!=RANK) {
  189. RA_CHECK(1==size(), "Bad conversion to scalar from shape [", ra::noshape, ra::shape(*this), "].");
  190. }
  191. return cp[0];
  192. }
  193. // FIXME necessary per [ra15], conflict with converting constructor? maybe gcc14 https://wg21.link/cwg976
  194. constexpr operator T & () { return std::as_const(*this); }
  195. // conversions from var rank to fixed rank
  196. template <rank_t R> requires (R==ANY && R!=RANK)
  197. constexpr ViewBig(ViewBig<T, R> const & x): dimv(x.dimv), cp(x.cp) {}
  198. template <rank_t R> requires (R==ANY && R!=RANK && std::is_const_v<T>)
  199. constexpr ViewBig(ViewBig<std::remove_const_t<T>, R> const & x): dimv(x.dimv), cp(x.cp) {}
  200. // conversion from fixed rank to var rank
  201. template <rank_t R> requires (R!=ANY && RANK==ANY)
  202. constexpr ViewBig(ViewBig<T, R> const & x): dimv(x.dimv.begin(), x.dimv.end()), cp(x.cp) {}
  203. template <rank_t R> requires (R!=ANY && RANK==ANY && std::is_const_v<T>)
  204. constexpr ViewBig(ViewBig<std::remove_const_t<T>, R> const & x): dimv(x.dimv.begin(), x.dimv.end()), cp(x.cp) {}
  205. // conversion to const. We rely on it for Container::view(). FIXME iffy? not constexpr, and doesn't work for Small.
  206. constexpr
  207. operator ViewBig<T const, RANK> const & () const requires (!std::is_const_v<T>)
  208. {
  209. return *reinterpret_cast<ViewBig<T const, RANK> const *>(this);
  210. }
  211. };
  212. // --------------------
  213. // Container types
  214. // --------------------
  215. template <class V>
  216. struct storage_traits
  217. {
  218. using T = V::value_type;
  219. static_assert(!std::is_same_v<std::remove_const_t<T>, bool>, "No pointers to bool in std::vector<bool>.");
  220. constexpr static auto create(dim_t n) { RA_CHECK(0<=n, "Bad size ", n, "."); return V(n); }
  221. template <class VV> constexpr static auto data(VV & v) { return v.data(); }
  222. };
  223. template <class P>
  224. struct storage_traits<std::unique_ptr<P>>
  225. {
  226. using V = std::unique_ptr<P>;
  227. using T = std::decay_t<decltype(*std::declval<V>().get())>;
  228. constexpr static auto create(dim_t n) { RA_CHECK(0<=n, "Bad size ", n, "."); return V(new T[n]); }
  229. template <class VV> constexpr static auto data(VV & v) { return v.get(); }
  230. };
  231. template <class P>
  232. struct storage_traits<std::shared_ptr<P>>
  233. {
  234. using V = std::shared_ptr<P>;
  235. using T = std::decay_t<decltype(*std::declval<V>().get())>;
  236. constexpr static auto create(dim_t n) { RA_CHECK(0<=n, "Bad size ", n, "."); return V(new T[n], std::default_delete<T[]>()); }
  237. template <class VV> constexpr static auto data(VV & v) { return v.get(); }
  238. };
  239. // FIXME avoid duplicating ViewBig::p. Avoid overhead with rank 1.
  240. template <class Store, rank_t RANK>
  241. struct Container: public ViewBig<typename storage_traits<Store>::T, RANK>
  242. {
  243. Store store;
  244. using T = typename storage_traits<Store>::T;
  245. using View = ra::ViewBig<T, RANK>;
  246. using ViewConst = ra::ViewBig<T const, RANK>;
  247. using View::size, View::rank;
  248. using shape_arg = decltype(shape(std::declval<View>().iter()));
  249. constexpr ViewConst const & view() const { return static_cast<View const &>(*this); }
  250. constexpr View & view() { return *this; }
  251. // Needed to set View::cp. FIXME Remove duplication as in SmallBase/SmallArray.
  252. Container(Container && w): store(std::move(w.store))
  253. {
  254. View::dimv = std::move(w.dimv);
  255. View::cp = storage_traits<Store>::data(store);
  256. }
  257. Container(Container const & w): store(w.store)
  258. {
  259. View::dimv = w.dimv;
  260. View::cp = storage_traits<Store>::data(store);
  261. }
  262. Container(Container & w): Container(std::as_const(w)) {}
  263. // A(shape 2 3) = A-type [1 2 3] initializes, so it doesn't behave as A(shape 2 3) = not-A-type [1 2 3] which uses View::operator=. This is used by operator>>(std::istream &, Container &). See test/ownership.cc [ra20].
  264. // TODO don't require copyable T in constructors, see fill1. That requires operator= to initialize, not update.
  265. Container & operator=(Container && w)
  266. {
  267. store = std::move(w.store);
  268. View::dimv = std::move(w.dimv);
  269. View::cp = storage_traits<Store>::data(store);
  270. return *this;
  271. }
  272. Container & operator=(Container const & w)
  273. {
  274. store = w.store;
  275. View::dimv = w.dimv;
  276. View::cp = storage_traits<Store>::data(store);
  277. return *this;
  278. }
  279. Container & operator=(Container & w) { return *this = std::as_const(w); }
  280. // const/nonconst shims over View's methods. FIXME > gcc13 ? __cpp_explicit_this_parameter
  281. #define RA_CONST_OR_NOT(CONST) \
  282. constexpr T CONST & back() CONST { RA_CHECK(1==rank() && size()>0, "Bad back()."); return store[size()-1]; } \
  283. constexpr auto data() CONST { return view().data(); } \
  284. constexpr decltype(auto) operator()(auto && ... a) CONST { return view()(RA_FWD(a) ...); } \
  285. constexpr decltype(auto) operator[](auto && ... a) CONST { return view()(RA_FWD(a) ...); } \
  286. constexpr decltype(auto) at(auto && i) CONST { return view().at(RA_FWD(i)); } \
  287. /* always compact/row-major, so STL-like iterators can be raw pointers. */ \
  288. constexpr auto begin() CONST { assert(is_c_order(view())); return view().data(); } \
  289. constexpr auto end() CONST { return view().data()+size(); } \
  290. template <rank_t c=0> constexpr auto iter() CONST { if constexpr (1==RANK && 0==c) { return ptr(data(), size()); } else { return view().template iter<c>(); } } \
  291. constexpr operator T CONST & () CONST { return view(); }
  292. FOR_EACH(RA_CONST_OR_NOT, /*not const*/, const)
  293. #undef RA_CONST_OR_NOT
  294. // non-copy assignment operators follow View, but cannot be just using'd because of constness.
  295. #define ASSIGNOPS(OP) \
  296. Container & operator OP (auto && x) { view() OP x; return *this; }
  297. FOR_EACH(ASSIGNOPS, =, *=, +=, -=, /=)
  298. #undef ASSIGNOPS
  299. using ravel_arg = std::conditional_t<RANK==1, noarg, std::initializer_list<T>>;
  300. Container & operator=(ravel_arg const x) { view() = x; return *this; }
  301. constexpr Container & operator=(braces<T, RANK> x) requires (RANK!=ANY) { view() = x; return *this; }
  302. #define RA_BRACES_ANY(N) \
  303. constexpr Container & operator=(braces<T, N> x) requires (RANK==ANY) { view() = x; return *this; }
  304. FOR_EACH(RA_BRACES_ANY, 2, 3, 4);
  305. #undef RA_BRACES_ANY
  306. template <class S> requires (1==rank_s<S>() || ANY==rank_s<S>())
  307. void
  308. init(S && s)
  309. {
  310. static_assert(!std::is_convertible_v<value_t<S>, Dim>);
  311. RA_CHECK(1==ra::rank(s), "Rank mismatch for init shape.");
  312. static_assert(ANY==RANK || ANY==size_s<S>() || RANK==size_s<S>() || BAD==size_s<S>(), "Bad shape for rank.");
  313. ra::resize(View::dimv, ra::size(s)); // [ra37]
  314. store = storage_traits<Store>::create(filldim(View::dimv, s));
  315. View::cp = storage_traits<Store>::data(store);
  316. }
  317. void init(dim_t s) { init(std::array {s}); } // scalar allowed as shape if rank is 1.
  318. // provided so that {} calls shape_arg constructor below.
  319. Container() requires (ANY==RANK): View({ Dim {0, 1} }, nullptr) {}
  320. Container() requires (ANY!=RANK && 0!=RANK): View(typename View::Dimv(Dim {0, 1}), nullptr) {}
  321. Container() requires (0==RANK): Container({}, none) {}
  322. // shape_arg overloads handle {...} arguments. Size check is at conversion (if shape_arg is Small) or init().
  323. Container(shape_arg const & s, none_t) { init(s); }
  324. Container(shape_arg const & s, auto && x): Container(s, none) { iter() = x; }
  325. Container(shape_arg const & s, braces<T, RANK> x) requires (RANK==1) : Container(s, none) { view() = x; }
  326. Container(auto && x): Container(ra::shape(x), none) { iter() = x; }
  327. Container(braces<T, RANK> x) requires (RANK!=ANY)
  328. : Container(braces_shape<T, RANK>(x), none) { view() = x; }
  329. #define RA_BRACES_ANY(N) \
  330. Container(braces<T, N> x) requires (RANK==ANY) \
  331. : Container(braces_shape<T, N>(x), none) { view() = x; }
  332. FOR_EACH(RA_BRACES_ANY, 1, 2, 3, 4)
  333. #undef RA_BRACES_ANY
  334. // FIXME requires T to be copiable, which conflicts with the semantics of view_.operator=. store(x) avoids it for Big, but doesn't work for Unique. Should construct in place like std::vector does.
  335. constexpr void
  336. fill1(auto && xbegin, dim_t xsize)
  337. {
  338. RA_CHECK(size()==xsize, "Mismatched sizes ", size(), " ", xsize, ".");
  339. std::ranges::copy_n(RA_FWD(xbegin), xsize, begin());
  340. }
  341. // shape + row-major ravel.
  342. // FIXME explicit it-is-ravel mark. Also iter<n> initializers.
  343. // FIXME regular (no need for fill1) for ANY if rank is 1.
  344. Container(shape_arg const & s, typename View::ravel_arg x)
  345. : Container(s, none) { fill1(x.begin(), x.size()); }
  346. // FIXME remove
  347. Container(shape_arg const & s, auto * p)
  348. : Container(s, none) { fill1(p, size()); } // FIXME fake check
  349. // FIXME remove
  350. Container(shape_arg const & s, auto && pbegin, dim_t psize)
  351. : Container(s, none) { fill1(RA_FWD(pbegin), psize); }
  352. // for shape arguments that doesn't convert implicitly to shape_arg
  353. Container(auto && s, none_t)
  354. { init(RA_FWD(s)); }
  355. Container(auto && s, auto && x)
  356. : Container(RA_FWD(s), none) { iter() = x; }
  357. Container(auto && s, std::initializer_list<T> x)
  358. : Container(RA_FWD(s), none) { fill1(x.begin(), x.size()); }
  359. // resize first axis or full shape. Only for some kinds of store.
  360. void resize(dim_t const s)
  361. {
  362. static_assert(ANY==RANK || 0<RANK); RA_CHECK(0<rank());
  363. View::dimv[0].len = s;
  364. store.resize(size());
  365. View::cp = store.data();
  366. }
  367. void resize(dim_t const s, T const & t)
  368. {
  369. static_assert(ANY==RANK || 0<RANK); RA_CHECK(0<rank());
  370. View::dimv[0].len = s;
  371. store.resize(size(), t);
  372. View::cp = store.data();
  373. }
  374. template <class S> requires (rank_s<S>() > 0)
  375. void resize(S const & s)
  376. {
  377. ra::resize(View::dimv, start(s).len(0)); // [ra37] FIXME is View constructor
  378. store.resize(filldim(View::dimv, s));
  379. View::cp = store.data();
  380. }
  381. // lets us move. A template + RA_FWD wouldn't work for push_back(brace-enclosed-list).
  382. void push_back(T && t)
  383. {
  384. static_assert(ANY==RANK || 1==RANK); RA_CHECK(1==rank());
  385. store.push_back(std::move(t));
  386. ++View::dimv[0].len;
  387. View::cp = store.data();
  388. }
  389. void push_back(T const & t)
  390. {
  391. static_assert(ANY==RANK || 1==RANK); RA_CHECK(1==rank());
  392. store.push_back(t);
  393. ++View::dimv[0].len;
  394. View::cp = store.data();
  395. }
  396. void emplace_back(auto && ... a)
  397. {
  398. static_assert(ANY==RANK || 1==RANK); RA_CHECK(1==rank());
  399. store.emplace_back(RA_FWD(a) ...);
  400. ++View::dimv[0].len;
  401. View::cp = store.data();
  402. }
  403. void pop_back()
  404. {
  405. static_assert(ANY==RANK || 1==RANK); RA_CHECK(1==rank());
  406. RA_CHECK(0<View::dimv[0].len, "Empty array trying to pop_back().");
  407. --View::dimv[0].len;
  408. store.pop_back();
  409. }
  410. };
  411. // rely on std::swap; else ambiguous
  412. template <class Store, rank_t RANKA, rank_t RANKB> requires (RANKA!=RANKB)
  413. void
  414. swap(Container<Store, RANKA> & a, Container<Store, RANKB> & b)
  415. {
  416. if constexpr (ANY==RANKA) {
  417. RA_CHECK(rank(a)==rank(b), "Mismatched ranks ", rank(a), " and ", rank(b), ".");
  418. decltype(b.dimv) c = a.dimv;
  419. start(a.dimv) = b.dimv;
  420. std::swap(b.dimv, c);
  421. } else if constexpr (ANY==RANKB) {
  422. RA_CHECK(rank(a)==rank(b), "Mismatched ranks ", rank(a), " and ", rank(b), ".");
  423. decltype(a.dimv) c = b.dimv;
  424. start(b.dimv) = a.dimv;
  425. std::swap(a.dimv, c);
  426. } else {
  427. static_assert(RANKA==RANKB);
  428. std::swap(a.dimv, b.dimv);
  429. }
  430. std::swap(a.store, b.store);
  431. std::swap(a.cp, b.cp);
  432. }
  433. template <class T, rank_t RANK=ANY> using Big = Container<vector_default_init<T>, RANK>;
  434. template <class T, rank_t RANK=ANY> using Unique = Container<std::unique_ptr<T []>, RANK>;
  435. template <class T, rank_t RANK=ANY> using Shared = Container<std::shared_ptr<T>, RANK>;
  436. // -------------
  437. // Used in Guile wrappers to let parameter either borrow from Guile storage or convert into new array (eg 'f32 into 'f64).
  438. // TODO Can use unique_ptr's deleter for this?
  439. // TODO Shared/Unique should maybe have constructors with unique_ptr/shared_ptr args
  440. // -------------
  441. template <rank_t RANK, class T>
  442. Shared<T, RANK>
  443. shared_borrowing(ViewBig<T, RANK> & raw)
  444. {
  445. Shared<T, RANK> a;
  446. a.dimv = raw.dimv;
  447. a.cp = raw.cp;
  448. a.store = std::shared_ptr<T>(raw.data(), [](T *) {});
  449. return a;
  450. }
  451. // --------------------
  452. // concrete (container) type from expression.
  453. // --------------------
  454. template <class E>
  455. struct concrete_type_def
  456. {
  457. using type = void;
  458. };
  459. template <class E> requires (size_s<E>()==ANY)
  460. struct concrete_type_def<E>
  461. {
  462. using type = Big<ncvalue_t<E>, rank_s<E>()>;
  463. };
  464. template <class E> requires (size_s<E>()!=ANY)
  465. struct concrete_type_def<E>
  466. {
  467. using type = decltype(std::apply([](auto ... i) { return Small<ncvalue_t<E>, E::len_s(i) ...> {}; },
  468. mp::iota<rank_s<E>()> {}));
  469. };
  470. // Scalars are their own concrete_type. Treat unregistered types as scalars.
  471. template <class E>
  472. using concrete_type = std::decay_t<
  473. std::conditional_t<(0==rank_s<E>() && !is_ra<E>) || is_scalar<E>,
  474. std::decay_t<E>,
  475. typename concrete_type_def<std::decay_t<decltype(start(std::declval<E>()))>>::type>>;
  476. template <class E>
  477. constexpr auto
  478. concrete(E && e)
  479. {
  480. // FIXME gcc 11.3 on GH workflows (?)
  481. #pragma GCC diagnostic push
  482. #pragma GCC diagnostic warning "-Wmaybe-uninitialized"
  483. return concrete_type<E>(RA_FWD(e));
  484. #pragma GCC diagnostic pop
  485. }
  486. template <class E>
  487. constexpr auto
  488. with_same_shape(E && e)
  489. {
  490. if constexpr (ANY!=size_s<concrete_type<E>>()) {
  491. return concrete_type<E>();
  492. } else {
  493. return concrete_type<E>(ra::shape(e), none);
  494. }
  495. }
  496. template <class E, class X>
  497. constexpr auto
  498. with_same_shape(E && e, X && x)
  499. {
  500. // FIXME gcc 11.3 on GH workflows (?)
  501. #pragma GCC diagnostic push
  502. #pragma GCC diagnostic warning "-Wmaybe-uninitialized"
  503. if constexpr (ANY!=size_s<concrete_type<E>>()) {
  504. return concrete_type<E>(RA_FWD(x));
  505. } else {
  506. return concrete_type<E>(ra::shape(e), RA_FWD(x));
  507. }
  508. #pragma GCC diagnostic pop
  509. }
  510. template <class E, class S, class X>
  511. constexpr auto
  512. with_shape(S && s, X && x)
  513. {
  514. if constexpr (ANY!=size_s<concrete_type<E>>()) {
  515. return concrete_type<E>(RA_FWD(x));
  516. } else {
  517. return concrete_type<E>(RA_FWD(s), RA_FWD(x));
  518. }
  519. }
  520. template <class E, class S, class X>
  521. constexpr auto
  522. with_shape(std::initializer_list<S> && s, X && x)
  523. {
  524. if constexpr (ANY!=size_s<concrete_type<E>>()) {
  525. return concrete_type<E>(RA_FWD(x));
  526. } else {
  527. return concrete_type<E>(s, RA_FWD(x));
  528. }
  529. }
  530. // --------------------
  531. // ViewBig ops
  532. // --------------------
  533. template <class T, rank_t RANK>
  534. inline ViewBig<T, RANK>
  535. reverse(ViewBig<T, RANK> const & view, int k=0)
  536. {
  537. RA_CHECK(inside(k, view.rank()), "Bad axis ", k, " for rank ", view.rank(), ".");
  538. ViewBig<T, RANK> r = view;
  539. if (auto & dim=r.dimv[k]; dim.len!=0) {
  540. r.cp += dim.step*(dim.len-1);
  541. dim.step *= -1;
  542. }
  543. return r;
  544. }
  545. // static transposed axes list, output rank is static.
  546. template <int ... Iarg, class T, rank_t RANK>
  547. inline auto
  548. transpose(ViewBig<T, RANK> const & view)
  549. {
  550. static_assert(RANK==ANY || RANK==sizeof...(Iarg), "Bad output rank.");
  551. RA_CHECK(view.rank()==sizeof...(Iarg), "Bad output rank: ", view.rank(), "should be ", (sizeof...(Iarg)), ".");
  552. constexpr static std::array<dim_t, sizeof...(Iarg)> s = { Iarg ... };
  553. constexpr rank_t dstrank = (0==ra::size(s)) ? 0 : 1 + *std::ranges::max_element(s);
  554. ViewBig<T, dstrank> r;
  555. r.cp = view.data();
  556. transpose_filldim(s, view.dimv, r.dimv);
  557. return r;
  558. }
  559. // dynamic transposed axes list, output rank is dynamic. FIXME only some S are valid here.
  560. template <class T, rank_t RANK, class S>
  561. inline ViewBig<T, ANY>
  562. transpose_(S && s, ViewBig<T, RANK> const & view)
  563. {
  564. RA_CHECK(view.rank()==ra::size(s), "Bad size for transposed axes list.");
  565. rank_t dstrank = (0==ra::size(s)) ? 0 : 1 + *std::ranges::max_element(s);
  566. ViewBig<T, ANY> r { decltype(r.dimv)(dstrank), view.data() };
  567. transpose_filldim(s, view.dimv, r.dimv);
  568. return r;
  569. }
  570. template <class T, rank_t RANK, class S>
  571. inline ViewBig<T, ANY>
  572. transpose(S && s, ViewBig<T, RANK> const & view)
  573. {
  574. return transpose_(RA_FWD(s), view);
  575. }
  576. // Need compile time values and not sizes to deduce the output rank, so initializer_list suffices.
  577. template <class T, rank_t RANK>
  578. inline ViewBig<T, ANY>
  579. transpose(std::initializer_list<ra::rank_t> s, ViewBig<T, RANK> const & view)
  580. {
  581. return transpose_(s, view);
  582. }
  583. template <class T, rank_t RANK>
  584. inline auto
  585. diag(ViewBig<T, RANK> const & view)
  586. {
  587. return transpose<0, 0>(view);
  588. }
  589. template <class T, rank_t RANK>
  590. inline ViewBig<T, 1>
  591. ravel_free(ViewBig<T, RANK> const & a)
  592. {
  593. RA_CHECK(is_c_order(a, false));
  594. int r = a.rank()-1;
  595. for (; r>=0 && a.len(r)==1; --r) {}
  596. ra::dim_t s = r<0 ? 1 : a.step(r);
  597. return ra::ViewBig<T, 1>({{ra::size(a), s}}, a.cp);
  598. }
  599. template <class T, rank_t RANK, class S>
  600. inline auto
  601. reshape_(ViewBig<T, RANK> const & a, S && sb_)
  602. {
  603. auto sb = concrete(RA_FWD(sb_));
  604. // FIXME when we need to copy, accept/return Shared
  605. dim_t la = ra::size(a);
  606. dim_t lb = 1;
  607. for (int i=0; i<ra::size(sb); ++i) {
  608. if (sb[i]==-1) {
  609. dim_t quot = lb;
  610. for (int j=i+1; j<ra::size(sb); ++j) {
  611. quot *= sb[j];
  612. RA_CHECK(quot>0, "Cannot deduce dimensions.");
  613. }
  614. auto pv = la/quot;
  615. RA_CHECK((la%quot==0 && pv>=0), "Bad placeholder.");
  616. sb[i] = pv;
  617. lb = la;
  618. break;
  619. } else {
  620. lb *= sb[i];
  621. }
  622. }
  623. auto sa = shape(a);
  624. // FIXME should be able to reshape Scalar etc.
  625. ViewBig<T, ra::size_s(sb)> b(map([](auto i) { return Dim { BAD, 0 }; }, ra::iota(ra::size(sb))), a.data());
  626. rank_t i = 0;
  627. for (; i<a.rank() && i<b.rank(); ++i) {
  628. if (sa[a.rank()-i-1]!=sb[b.rank()-i-1]) {
  629. RA_CHECK(is_c_order(a, false) && la>=lb, "Reshape with copy not implemented.");
  630. // FIXME ViewBig(SS const & s, T * p). Cf [ra37].
  631. filldim(b.dimv, sb);
  632. for (int j=0; j!=b.rank(); ++j) {
  633. b.dimv[j].step *= a.step(a.rank()-1);
  634. }
  635. return b;
  636. } else {
  637. // select
  638. b.dimv[b.rank()-i-1] = a.dimv[a.rank()-i-1];
  639. }
  640. }
  641. if (i==a.rank()) {
  642. // tile
  643. for (rank_t j=i; j<b.rank(); ++j) {
  644. b.dimv[b.rank()-j-1] = { sb[b.rank()-j-1], 0 };
  645. }
  646. }
  647. return b;
  648. }
  649. template <class T, rank_t RANK, class S>
  650. inline auto
  651. reshape(ViewBig<T, RANK> const & a, S && sb_)
  652. {
  653. return reshape_(a, RA_FWD(sb_));
  654. }
  655. // We need dimtype bc {1, ...} deduces to int and that fails to match ra::dim_t. initializer_list could handle the general case, but the result would have var rank and would override this one (?).
  656. template <class T, rank_t RANK, class dimtype, int N>
  657. inline auto
  658. reshape(ViewBig<T, RANK> const & a, dimtype const (&sb_)[N])
  659. {
  660. return reshape_(a, sb_);
  661. }
  662. // lo: lower bounds, hi: upper bounds. The stencil indices are in [0 lo+1+hi] = [-lo +hi].
  663. template <class LO, class HI, class T, rank_t N>
  664. inline ViewBig<T, rank_sum(N, N)>
  665. stencil(ViewBig<T, N> const & a, LO && lo, HI && hi)
  666. {
  667. ViewBig<T, rank_sum(N, N)> s;
  668. s.cp = a.data();
  669. ra::resize(s.dimv, 2*a.rank());
  670. RA_CHECK(every(lo>=0) && every(hi>=0), "Bad stencil bounds lo ", noshape, lo, " hi ", noshape, hi, ".");
  671. for_each([](auto & dims, auto && dima, auto && lo, auto && hi)
  672. {
  673. RA_CHECK(dima.len>=lo+hi, "Stencil is too large for array.");
  674. dims = {dima.len-lo-hi, dima.step};
  675. },
  676. ptr(s.dimv.data()), a.dimv, lo, hi);
  677. for_each([](auto & dims, auto && dima, auto && lo, auto && hi)
  678. { dims = {lo+hi+1, dima.step}; },
  679. ptr(s.dimv.data()+a.rank()), a.dimv, lo, hi);
  680. return s;
  681. }
  682. // Make last sizes of ViewBig<> be compile-time constants.
  683. template <class super_t, rank_t SUPERR, class T, rank_t RANK>
  684. inline auto
  685. explode_(ViewBig<T, RANK> const & a)
  686. {
  687. // TODO Reduce to single check, either the first or the second.
  688. static_assert(RANK>=SUPERR || RANK==ANY, "rank of a is too low");
  689. RA_CHECK(a.rank()>=SUPERR, "Rank of a ", a.rank(), " should be at least ", SUPERR, ".");
  690. ViewBig<super_t, rank_sum(RANK, -SUPERR)> b;
  691. ra::resize(b.dimv, a.rank()-SUPERR);
  692. dim_t r = 1;
  693. for (int i=0; i<SUPERR; ++i) {
  694. r *= a.len(i+b.rank());
  695. }
  696. RA_CHECK(r*sizeof(T)==sizeof(super_t), "Length of axes ", r*sizeof(T), " doesn't match type ", sizeof(super_t), ".");
  697. for (int i=0; i<b.rank(); ++i) {
  698. RA_CHECK(a.step(i) % r==0, "Step of axes ", a.step(i), " doesn't match type ", r, " on axis ", i, ".");
  699. b.dimv[i] = { .len = a.len(i), .step = a.step(i) / r };
  700. }
  701. RA_CHECK((b.rank()==0 || a.step(b.rank()-1)==r), "Super type is not compact in array.");
  702. b.cp = reinterpret_cast<super_t *>(a.data());
  703. return b;
  704. }
  705. template <class super_t, class T, rank_t RANK>
  706. inline auto
  707. explode(ViewBig<T, RANK> const & a)
  708. {
  709. return explode_<super_t, (std::is_same_v<super_t, std::complex<T>> ? 1 : rank_s<super_t>())>(a);
  710. }
  711. // FIXME Maybe namespace level generics
  712. template <class T> inline int gstep(int i) { if constexpr (is_scalar<T>) return 1; else return T::step(i); }
  713. template <class T> inline int glen(int i) { if constexpr (is_scalar<T>) return 1; else return T::len(i); }
  714. // TODO The ranks below SUBR must be compact, which is not checked.
  715. template <class sub_t, class super_t, rank_t RANK>
  716. inline auto
  717. collapse(ViewBig<super_t, RANK> const & a)
  718. {
  719. using super_v = value_t<super_t>;
  720. using sub_v = value_t<sub_t>;
  721. constexpr int subtype = sizeof(super_v)/sizeof(sub_t);
  722. constexpr int SUBR = rank_s<super_t>() - rank_s<sub_t>();
  723. ViewBig<sub_t, rank_sum(RANK, SUBR+int(subtype>1))> b;
  724. resize(b.dimv, a.rank()+SUBR+int(subtype>1));
  725. constexpr dim_t r = sizeof(super_t)/sizeof(sub_t);
  726. static_assert(sizeof(super_t)==r*sizeof(sub_t), "Cannot make axis of super_t from sub_t.");
  727. for (int i=0; i<a.rank(); ++i) {
  728. b.dimv[i] = { .len = a.len(i), .step = a.step(i) * r };
  729. }
  730. constexpr int t = sizeof(super_v)/sizeof(sub_v);
  731. constexpr int s = sizeof(sub_t)/sizeof(sub_v);
  732. static_assert(t*sizeof(sub_v)>=1, "Bad subtype.");
  733. for (int i=0; i<SUBR; ++i) {
  734. RA_CHECK(((gstep<super_t>(i)/s)*s==gstep<super_t>(i)), "Bad steps."); // TODO actually static
  735. b.dimv[a.rank()+i] = { .len = glen<super_t>(i), .step = gstep<super_t>(i) / s * t };
  736. }
  737. if (subtype>1) {
  738. b.dimv[a.rank()+SUBR] = { .len = t, .step = 1 };
  739. }
  740. b.cp = reinterpret_cast<sub_t *>(a.data());
  741. return b;
  742. }
  743. } // namespace ra