base.hh 10 KB

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