base.hh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // ra-ra - Before all other ra:: includes.
  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 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 "tuples.hh"
  10. #include <array>
  11. #include <ranges>
  12. #include <vector>
  13. #include <iosfwd> // for format, ss.
  14. #include <cstdint>
  15. #include <sstream>
  16. #include <version>
  17. #include <source_location>
  18. // FIMXE benchmark shows it's bad by default; probably requires optimizing also +=, etc.
  19. #ifndef RA_DO_OPT_SMALLVECTOR
  20. #define RA_DO_OPT_SMALLVECTOR 0
  21. #endif
  22. namespace ra {
  23. constexpr int VERSION = 29;
  24. constexpr int ANY = -1944444444; // only at ct, meaning tbd at rt
  25. constexpr int BAD = -1988888888; // undefined, eg dead axes
  26. using rank_t = int;
  27. using dim_t = std::ptrdiff_t;
  28. static_assert(sizeof(rank_t)>=4 && sizeof(dim_t)>=4);
  29. static_assert(sizeof(rank_t)>=sizeof(int) && sizeof(dim_t)>=sizeof(rank_t));
  30. static_assert(std::is_signed_v<rank_t> && std::is_signed_v<dim_t>);
  31. template <dim_t V> using dim_c = std::integral_constant<dim_t, V>;
  32. template <rank_t V> using rank_c = std::integral_constant<rank_t, V>;
  33. enum none_t { none }; // in constructors to mean: don't initialize
  34. struct noarg { noarg() = delete; }; // in constructors to mean: don't instantiate
  35. // forward decl, extended in ra.hh
  36. constexpr bool any(bool const x) { return x; }
  37. constexpr bool every(bool const x) { return x; }
  38. // default storage for Big - see https://stackoverflow.com/a/21028912.
  39. // allocator adaptor that interposes construct() calls to convert value initialization into default initialization.
  40. template <class T, class A=std::allocator<T>>
  41. struct default_init_allocator: public A
  42. {
  43. using a_t = std::allocator_traits<A>;
  44. using A::A;
  45. template <class U>
  46. struct rebind
  47. {
  48. using other = default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
  49. };
  50. template <class U>
  51. void construct(U * ptr) noexcept(std::is_nothrow_default_constructible<U>::value)
  52. {
  53. ::new(static_cast<void *>(ptr)) U;
  54. }
  55. template <class U, class... Args>
  56. void construct(U * ptr, Args &&... args)
  57. {
  58. a_t::construct(static_cast<A &>(*this), ptr, RA_FWD(args)...);
  59. }
  60. };
  61. template <class T> using vector_default_init = std::vector<T, default_init_allocator<T>>;
  62. // ---------------------
  63. // concepts
  64. // ---------------------
  65. template <class A>
  66. concept IteratorConcept = requires (A a, rank_t k, dim_t d, rank_t i, rank_t j)
  67. {
  68. { a.rank() } -> std::same_as<rank_t>;
  69. { std::decay_t<A>::len_s(k) } -> std::same_as<dim_t>;
  70. { a.len(k) } -> std::same_as<dim_t>;
  71. { a.adv(k, d) } -> std::same_as<void>;
  72. { a.step(k) };
  73. { a.keep(d, i, j) } -> std::same_as<bool>;
  74. { a.save() };
  75. { a.load(std::declval<decltype(a.save())>()) } -> std::same_as<void>;
  76. { a.mov(d) } -> std::same_as<void>;
  77. { *a };
  78. };
  79. template <class A>
  80. concept SliceConcept = requires (A a)
  81. {
  82. { a.rank() } -> std::same_as<rank_t>;
  83. { a.iter() } -> IteratorConcept;
  84. };
  85. // --------------
  86. // type classification / introspection
  87. // --------------
  88. // FIXME https://wg21.link/p2841r0 ?
  89. #define RA_IS_DEF(NAME, PRED) \
  90. template <class A> constexpr bool JOIN(NAME, _def) = requires { requires PRED; }; \
  91. template <class A> concept NAME = JOIN(NAME, _def)<std::decay_t< A >>;
  92. RA_IS_DEF(is_scalar, (!std::is_pointer_v<A> && std::is_scalar_v<A> || ra::is_constant<A>))
  93. template <> constexpr bool is_scalar_def<std::strong_ordering> = true;
  94. template <> constexpr bool is_scalar_def<std::weak_ordering> = true;
  95. template <> constexpr bool is_scalar_def<std::partial_ordering> = true;
  96. // template <> constexpr bool is_scalar_def<std::string_view> = true; // [ra13]
  97. RA_IS_DEF(is_iterator, IteratorConcept<A>)
  98. template <class A> concept is_ra = is_iterator<A> || SliceConcept<A>;
  99. template <class A> concept is_builtin_array = std::is_array_v<std::remove_cvref_t<A>>;
  100. RA_IS_DEF(is_fov, (!is_scalar<A> && !is_ra<A> && !is_builtin_array<A> && std::ranges::bidirectional_range<A>))
  101. template<class> constexpr bool is_std_array = false; // snowflake
  102. template<class T, std::size_t N> constexpr bool is_std_array<std::array<T, N>> = true;
  103. template <class VV> requires (!std::is_void_v<VV>)
  104. consteval rank_t
  105. rank_s()
  106. {
  107. using V = std::remove_cvref_t<VV>;
  108. if constexpr (is_builtin_array<V>) {
  109. return std::rank_v<V>;
  110. } else if constexpr (is_fov<V>) {
  111. return 1;
  112. } else if constexpr (requires { V::rank(); }) {
  113. return V::rank();
  114. } else if constexpr (requires (V v) { v.rank(); }) {
  115. return ANY;
  116. } else {
  117. return 0;
  118. }
  119. }
  120. template <class V> consteval rank_t rank_s(V const &) { return rank_s<V>(); }
  121. constexpr rank_t
  122. rank(auto const & v)
  123. {
  124. if constexpr (ANY!=rank_s(v)) {
  125. return rank_s(v);
  126. } else if constexpr (requires { v.rank(); }) {
  127. return v.rank();
  128. } else {
  129. static_assert(false, "No rank() for this type.");
  130. }
  131. }
  132. RA_IS_DEF(is_pos, 0!=rank_s<A>())
  133. template <class A> concept is_ra_pos = is_ra<A> && is_pos<A>;
  134. template <class A> concept is_zero_or_scalar = (is_ra<A> && !is_pos<A>) || is_scalar<A>;
  135. // all args rank 0 (immediate application), but at least one ra:: (don't collide with the scalar version).
  136. RA_IS_DEF(is_special, false) // rank-0 types that we don't want reduced.
  137. template <class ... A> constexpr bool toreduce = (!is_scalar<A> || ...) && ((is_zero_or_scalar<A> && !is_special<A>) && ...);
  138. template <class ... A> constexpr bool tomap = ((is_ra_pos<A> || is_special<A>) || ...) && ((is_ra<A> || is_scalar<A> || is_fov<A> || is_builtin_array<A>) && ...);
  139. template <class VV> requires (!std::is_void_v<VV>)
  140. consteval dim_t
  141. size_s()
  142. {
  143. using V = std::remove_cvref_t<VV>;
  144. constexpr rank_t rs = rank_s<V>();
  145. if constexpr (0==rs) {
  146. return 1;
  147. } else if constexpr (is_builtin_array<V>) {
  148. return std::apply([] (auto ... i) { return (std::extent_v<V, i> * ... * 1); }, mp::iota<rs> {});
  149. } else if constexpr (is_std_array<V>) {
  150. return std::tuple_size_v<V>;
  151. } else if constexpr (is_fov<V> && requires { V::size(); }) {
  152. return V::size();
  153. } else if constexpr (is_fov<V> && requires { V::extent; }) {
  154. return std::dynamic_extent==V::extent ? ANY : V::extent;
  155. } else if constexpr (is_fov<V> || rs==ANY) {
  156. return ANY;
  157. } else if constexpr (requires { V::size_s(); }) {
  158. return V::size_s();
  159. } else {
  160. dim_t s = 1;
  161. for (int i=0; i<rs; ++i) {
  162. if (dim_t ss=V::len_s(i); ss>=0) { s *= ss; } else { return ss; } // ANY or BAD
  163. }
  164. return s;
  165. }
  166. }
  167. template <class V> consteval dim_t size_s(V const &) { return size_s<V>(); }
  168. template <class V>
  169. constexpr dim_t
  170. size(V const & v)
  171. {
  172. if constexpr (ANY!=size_s(v)) {
  173. return size_s(v);
  174. } else if constexpr (is_fov<V>) {
  175. return std::ssize(v);
  176. } else if constexpr (requires { v.size(); }) {
  177. return v.size();
  178. } else {
  179. dim_t s = 1;
  180. for (rank_t k=0; k<rank(v); ++k) { s *= v.len(k); }
  181. return s;
  182. }
  183. }
  184. // Returns concrete types, value or const &. FIXME return ra:: types, but only if it's in all cases.
  185. template <class V>
  186. constexpr decltype(auto)
  187. shape(V const & v)
  188. {
  189. constexpr rank_t rs = rank_s(v);
  190. // FIXME __cpp_constexpr >= 202211L to return references to the constexpr cases
  191. if constexpr (is_builtin_array<V>) {
  192. return std::apply([] (auto ... i) { return std::array<dim_t, rs> { std::extent_v<V, i> ... }; }, mp::iota<rs> {});
  193. } else if constexpr (requires { v.shape(); }) {
  194. return v.shape();
  195. } else if constexpr (0==rs) {
  196. return std::array<dim_t, 0> {};
  197. } else if constexpr (1==rs) {
  198. return std::array<dim_t, 1> { ra::size(v) };
  199. } else if constexpr (1<rs) {
  200. return std::apply([&v](auto ... i) { return std::array<dim_t, rs> { v.len(i) ... }; }, mp::iota<rs> {});
  201. } else {
  202. static_assert(ANY==rs);
  203. return std::ranges::to<vector_default_init<dim_t>>(
  204. std::ranges::iota_view { 0, rank(v) } | std::views::transform([&v](auto k) { return v.len(k); }));
  205. }
  206. }
  207. // --------------
  208. // format FIXME std::format
  209. // --------------
  210. enum shape_t { defaultshape, withshape, noshape };
  211. struct format_t
  212. {
  213. shape_t shape = defaultshape;
  214. using pchar = char const *;
  215. pchar open = "", close = "", sep0 = " ", sepn = "\n", rep = "\n";
  216. bool align = false;
  217. };
  218. constexpr format_t jstyle = {};
  219. constexpr format_t cstyle = { .shape=noshape, .open="{", .close="}", .sep0=", ", .sepn=",\n", .rep="", .align=true};
  220. constexpr format_t lstyle = { .shape=noshape, .open="(", .close=")", .sep0=" ", .sepn="\n", .rep="", .align=true};
  221. constexpr format_t pstyle = { .shape=noshape, .open="[", .close="]", .sep0=", ", .sepn=",\n", .rep="\n", .align=true};
  222. template <class A>
  223. struct FormatArray
  224. {
  225. A const & a;
  226. format_t fmt = {};
  227. };
  228. constexpr auto
  229. format_array(auto const & a, format_t fmt = {})
  230. {
  231. return FormatArray<decltype(a)> { a, fmt };
  232. }
  233. // exclude std::string_view so it still prints as a string [ra13].
  234. template <class A> requires (is_ra<A> || (is_fov<A> && !std::is_convertible_v<A, std::string_view>))
  235. constexpr std::ostream & operator<<(std::ostream & o, A && a) { return o << format_array(a); }
  236. template <class T>
  237. constexpr std::ostream & operator<<(std::ostream & o, std::initializer_list<T> const & a) { return o << format_array(a); }
  238. struct shape_o { std::ostream & o; shape_t shape; };
  239. struct format_o { std::ostream & o; format_t fmt; };
  240. constexpr shape_o operator<<(std::ostream & o, shape_t shape) { return shape_o { o, shape }; }
  241. constexpr format_o operator<<(std::ostream & o, format_t fmt) { return format_o { o, fmt }; }
  242. constexpr std::ostream & operator<<(format_o const & m, auto const & a) { return m.o << format_array(a, m.fmt); }
  243. template <class A>
  244. constexpr std::ostream & operator<<(shape_o const & m, FormatArray<A> fa) { fa.fmt.shape=m.shape; return m.o << fa; }
  245. constexpr std::ostream & operator<<(shape_o const & m, auto const & a) { return m << format_array(a); }
  246. constexpr format_o operator<<(shape_o const & m, format_t f) { f.shape=m.shape; return m.o << f; }
  247. constexpr std::ostream &
  248. operator<<(std::ostream & o, std::source_location const & loc)
  249. {
  250. return o << loc.file_name() << ":" << loc.line() << "," << loc.column();
  251. }
  252. constexpr std::string
  253. format(auto && ... a)
  254. {
  255. if constexpr (sizeof... (a)>0) {
  256. std::ostringstream o; (o << ... << RA_FWD(a)); return o.str();
  257. } else {
  258. return "";
  259. }
  260. }
  261. constexpr std::string const &
  262. format(std::string const & s) { return s; }
  263. } // namespace ra
  264. #ifdef RA_AFTER_CHECK
  265. #error Bad header include order! Do not include ra/base.hh after other ra:: headers.
  266. #endif