bootstrap.hh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // ra-ra - Before all other ra:: includes.
  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 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 <ranges>
  10. #include "tuples.hh"
  11. // ---------------------
  12. // Default #defines.
  13. // ---------------------
  14. // Since these are tested with #if, define them at the very top so accidental nodef isn't mistaken for 0.
  15. // benchmark shows it's bad by default; probably requires optimizing also +=, etc.
  16. #ifndef RA_DO_OPT_SMALLVECTOR
  17. #define RA_DO_OPT_SMALLVECTOR 0
  18. #endif
  19. // no real downside to this.
  20. #ifndef RA_DO_OPT_IOTA
  21. #define RA_DO_OPT_IOTA 1
  22. #endif
  23. namespace ra {
  24. constexpr int VERSION = 20;
  25. using rank_t = int;
  26. using dim_t = std::ptrdiff_t;
  27. static_assert(sizeof(rank_t)>=4 && sizeof(dim_t)>=4);
  28. static_assert(std::is_signed_v<rank_t> && std::is_signed_v<dim_t>);
  29. template <dim_t V> using dim_c = std::integral_constant<dim_t, V>;
  30. template <rank_t V> using rank_c = std::integral_constant<rank_t, V>;
  31. // Negative numbers are used in some places as 'frame rank' in contrast to 'cell rank', so these numbers limit the rank that ra:: can handle besides the range of rank_t.
  32. constexpr dim_t DIM_ANY = -1944444444; // only on ct values: not known at ct, but maybe at rt
  33. constexpr rank_t RANK_ANY = -1944444444;
  34. constexpr dim_t DIM_BAD = -1988888888; // undefined
  35. constexpr rank_t RANK_BAD = -1988888888;
  36. constexpr dim_t
  37. dim_prod(dim_t a, dim_t b)
  38. {
  39. return (a==DIM_ANY) ? DIM_ANY : ((b==DIM_ANY) ? DIM_ANY : a*b);
  40. }
  41. constexpr rank_t
  42. rank_sum(rank_t a, rank_t b)
  43. {
  44. return (a==RANK_ANY) ? RANK_ANY : ((b==RANK_ANY) ? RANK_ANY : a+b);
  45. }
  46. constexpr rank_t
  47. rank_diff(rank_t a, rank_t b)
  48. {
  49. return (a==RANK_ANY) ? RANK_ANY : ((b==RANK_ANY) ? RANK_ANY : a-b);
  50. }
  51. constexpr bool
  52. inside(dim_t i, dim_t b)
  53. {
  54. return i>=0 && i<b;
  55. }
  56. constexpr bool
  57. inside(dim_t i, dim_t a, dim_t b)
  58. {
  59. return i>=a && i<b;
  60. }
  61. // ---------------------
  62. // concepts (WIP) - not sure i want duck typing, tbr.
  63. // ---------------------
  64. template <class P, class S>
  65. concept FlatConcept = requires (P p, S d)
  66. {
  67. { *p };
  68. { p += d };
  69. };
  70. template <class A>
  71. concept IteratorConcept = requires (A a, rank_t k, dim_t d, rank_t i, rank_t j)
  72. {
  73. // FIXME we still allow ply(&) in some places. Cf also test/types.cc.
  74. { std::decay_t<A>::rank_s() } -> std::convertible_to<rank_t>;
  75. { a.rank() } -> std::convertible_to<rank_t>;
  76. { std::decay_t<A>::len_s(k) } -> std::convertible_to<dim_t>;
  77. { a.len(k) } -> std::same_as<dim_t>;
  78. { a.adv(k, d) } -> std::same_as<void>;
  79. { a.step(k) };
  80. { a.keep_step(d, i, j) } -> std::same_as<bool>;
  81. { a.flat() } -> FlatConcept<decltype(a.step(k))>;
  82. };
  83. template <class A>
  84. concept SliceConcept = requires (A a)
  85. {
  86. { A::rank_s() } -> std::convertible_to<rank_t>;
  87. { a.rank() } -> std::convertible_to<rank_t>;
  88. { a.iter() } -> IteratorConcept;
  89. };
  90. // ---------------------
  91. // other types, forward decl
  92. // ---------------------
  93. enum none_t { none }; // used in array constructors to mean ‘don't initialize’
  94. struct no_arg {}; // used in array constructors to mean ‘don't instantiate’
  95. template <class C> struct Scalar; // for type predicates
  96. template <class V> struct ra_traits_def;
  97. template <class S> struct default_steps_ {};
  98. template <class tend> struct default_steps_<std::tuple<tend>> { using type = mp::int_list<1>; };
  99. template <> struct default_steps_<std::tuple<>> { using type = mp::int_list<>; };
  100. template <class t0, class t1, class ... ti>
  101. struct default_steps_<std::tuple<t0, t1, ti ...>>
  102. {
  103. using rest = typename default_steps_<std::tuple<t1, ti ...>>::type;
  104. static int const step0 = t1::value * mp::first<rest>::value;
  105. using type = mp::cons<mp::int_c<step0>, rest>;
  106. };
  107. template <class S> using default_steps = typename default_steps_<S>::type;
  108. template <int n> struct dots_t
  109. {
  110. static_assert(n>=0);
  111. constexpr static rank_t rank_s() { return n; }
  112. };
  113. template <int n> constexpr dots_t<n> dots = dots_t<n>();
  114. constexpr auto all = dots<1>;
  115. template <int n> struct insert_t
  116. {
  117. static_assert(n>=0);
  118. constexpr static rank_t rank_s() { return n; }
  119. };
  120. template <int n=1> constexpr insert_t<n> insert = insert_t<n>();
  121. // Common to View / SmallBase. TODO Shouldn't it work on ... foreign vectors? arbitrary exprs?
  122. template <int cell_rank, class A> constexpr auto
  123. iter(A && a) { return std::forward<A>(a).template iter<cell_rank>(); }
  124. // Used in big.hh (selectors, etc).
  125. template <class A, class ... I> constexpr auto from(A && a, I && ... i);
  126. // Extended in operators.hh. TODO All users be int, then this take int.
  127. constexpr bool any(bool const x) { return x; }
  128. constexpr bool every(bool const x) { return x; }
  129. constexpr bool odd(unsigned int N) { return N & 1; }
  130. // ---------------------
  131. // nested braces for Small initializers
  132. // ---------------------
  133. // This logically belongs in ra/small.hh, but it's here so that shape() can return ra:: types.
  134. // The general SmallArray has 4 constructors,
  135. // 1. The empty constructor.
  136. // 2. The scalar constructor. This is needed when T isn't registered as ra::scalar, which isn't required purely for container use.
  137. // 3. The ravel constructor.
  138. // 4. The nested constructor.
  139. // When SmallArray has rank 1, or the first dimension is empty, or the shape is [1] or [], several of the constructors above become ambiguous. We solve this by defining the constructor arguments to variants of no_arg.
  140. template <class T, class lens>
  141. struct nested_tuple;
  142. // ambiguity with empty constructor and scalar constructor.
  143. // if len(0) is 0, then prefer the empty constructor.
  144. // if len(0) is 1...
  145. template <class lens> constexpr bool no_nested = (mp::first<lens>::value<1);
  146. template <> constexpr bool no_nested<mp::nil> = true;
  147. template <> constexpr bool no_nested<mp::int_list<1>> = true;
  148. template <class T, class lens>
  149. using nested_arg = std::conditional_t<no_nested<lens>,
  150. std::tuple<no_arg>, // match the template for SmallArray.
  151. typename nested_tuple<T, lens>::list>;
  152. // ambiguity with scalar constructors (for rank 0) and nested_tuple (for rank 1).
  153. template <class lens> constexpr bool no_ravel = ((mp::len<lens> <=1) || (mp::apply<mp::prod, lens>::value <= 1));
  154. template <class T, class lens>
  155. using ravel_arg = std::conditional_t<no_ravel<lens>,
  156. std::tuple<no_arg, no_arg>, // match the template for SmallArray.
  157. mp::makelist<mp::apply<mp::prod, lens>::value, T>>;
  158. template <class T, class lens, class steps = default_steps<lens>> struct SmallView; // for CellSmall
  159. template <class T, class lens, class steps = default_steps<lens>,
  160. class nested_arg_ = nested_arg<T, lens>, class ravel_arg_ = ravel_arg<T, lens>>
  161. struct SmallArray;
  162. template <class T, dim_t ... lens> using Small = SmallArray<T, mp::int_list<lens ...>>;
  163. template <class T>
  164. struct nested_tuple<T, mp::nil>
  165. {
  166. using sub = no_arg;
  167. using list = std::tuple<no_arg>; // match the template for SmallArray.
  168. };
  169. template <class T, int S0>
  170. struct nested_tuple<T, mp::int_list<S0>>
  171. {
  172. using sub = T;
  173. using list = mp::makelist<S0, T>;
  174. };
  175. template <class T, int S0, int S1, int ... S>
  176. struct nested_tuple<T, mp::int_list<S0, S1, S ...>>
  177. {
  178. using sub = Small<T, S1, S ...>;
  179. using list = mp::makelist<S0, sub>;
  180. };
  181. } // namespace ra
  182. #include <array>
  183. #include <ranges>
  184. #include <cstdint>
  185. namespace ra {
  186. // --------------
  187. // type classification
  188. // --------------
  189. // FIXME https://wg21.link/p2841r0 ?
  190. #define RA_IS_DEF(NAME, PRED) \
  191. template <class A> constexpr bool JOIN(NAME, _def) = requires { requires PRED; }; \
  192. template <class A> constexpr bool NAME = JOIN(NAME, _def)< std::decay_t< A >>;
  193. // ra_traits are for foreign types only. FIXME Not sure this is the interface I want.
  194. RA_IS_DEF(is_scalar, (!std::is_pointer_v<A> && std::is_scalar_v<A>))
  195. template <> constexpr bool is_scalar_def<std::strong_ordering> = true;
  196. template <> constexpr bool is_scalar_def<std::weak_ordering> = true;
  197. template <> constexpr bool is_scalar_def<std::partial_ordering> = true;
  198. // template <> constexpr bool is_scalar_def<std::string_view> = true; // [ra13]
  199. template <class T> requires (is_scalar<T>)
  200. struct ra_traits_def<T>
  201. {
  202. using V = T;
  203. constexpr static auto shape(V const & v) { return std::array<dim_t, 0> {}; }
  204. constexpr static dim_t size(V const & v) { return 1; }
  205. constexpr static dim_t size_s() { return 1; }
  206. constexpr static rank_t rank(V const & v) { return 0; }
  207. constexpr static rank_t rank_s() { return 0; }
  208. };
  209. // TODO make things is_iterator explicitly, as with is_scalar, and not by poking in the insides.
  210. RA_IS_DEF(is_iterator, IteratorConcept<A>)
  211. RA_IS_DEF(is_iterator_pos_rank, IteratorConcept<A> && A::rank_s()!=0)
  212. RA_IS_DEF(is_slice, SliceConcept<A>)
  213. RA_IS_DEF(is_slice_pos_rank, SliceConcept<A> && A::rank_s()!=0)
  214. template <class A> constexpr bool is_ra = is_iterator<A> || is_slice<A>;
  215. template <class A> constexpr bool is_ra_pos_rank = is_iterator_pos_rank<A> || is_slice_pos_rank<A>; // internal only FIXME
  216. template <class A> constexpr bool is_ra_zero_rank = is_ra<A> && !is_ra_pos_rank<A>;
  217. template <class A> constexpr bool is_zero_or_scalar = is_ra_zero_rank<A> || is_scalar<A>;
  218. // ra_traits defined in small.hh.
  219. template <class A> constexpr bool is_builtin_array = std::is_array_v<std::remove_cvref_t<A>>;
  220. // std::string is std::ranges::range, but if we have it as is_scalar, we can't have it as is_foreign_vector.
  221. RA_IS_DEF(is_foreign_vector, (!is_scalar<A> && !is_ra<A> && !is_builtin_array<A> && std::ranges::random_access_range<A>))
  222. // not using decay_t bc of builtin arrays.
  223. template <class A> using ra_traits = ra_traits_def<std::remove_cvref_t<A>>;
  224. // FIXME should be able to use std::span(V).extent (maybe p2325r3?) [ra2]
  225. template <class V>
  226. requires (is_foreign_vector<V> && requires { std::tuple_size<V>::value; })
  227. struct ra_traits_def<V>
  228. {
  229. constexpr static dim_t N = std::tuple_size_v<V>;
  230. constexpr static auto shape(V const & v) { return std::array<dim_t, 1> { N }; }
  231. constexpr static dim_t size(V const & v) { return N; }
  232. constexpr static dim_t size_s() { return N; }
  233. constexpr static rank_t rank(V const & v) { return 1; }
  234. constexpr static rank_t rank_s() { return 1; };
  235. };
  236. template <class V>
  237. requires (is_foreign_vector<V> && !(requires { std::tuple_size<V>::value; }))
  238. struct ra_traits_def<V>
  239. {
  240. // FIXME unqualified ssize fails on std::ranges::iota_view - looks iffy
  241. constexpr static auto shape(V const & v) { return std::array<dim_t, 1> { std::ssize(v) }; }
  242. constexpr static dim_t size(V const & v) { return std::ssize(v); }
  243. constexpr static dim_t size_s() { return DIM_ANY; }
  244. constexpr static rank_t rank(V const & v) { return 1; }
  245. constexpr static rank_t rank_s() { return 1; }
  246. };
  247. template <class T>
  248. struct ra_traits_def<std::initializer_list<T>>
  249. {
  250. using V = std::initializer_list<T>;
  251. constexpr static auto shape(V const & v) { return std::array<dim_t, 1> { ssize(v) }; }
  252. constexpr static dim_t size(V const & v) { return ssize(v); }
  253. constexpr static dim_t size_s() { return DIM_ANY; }
  254. constexpr static rank_t rank(V const & v) { return 1; }
  255. constexpr static rank_t rank_s() { return 1; }
  256. };
  257. template <class ... A> constexpr bool ra_pos_and_any = (is_ra_pos_rank<A> || ...) && ((is_ra<A> || is_scalar<A> || is_foreign_vector<A> || is_builtin_array<A>) && ...);
  258. // all args have rank 0 (so immediate application), but at least one is ra:: (don't collide with the scalar version).
  259. template <class ... A> constexpr bool ra_zero = !(is_scalar<A> && ...) && (is_zero_or_scalar<A> && ...);
  260. } // namespace ra
  261. // --------------
  262. // array output formatting
  263. // --------------
  264. #include <iterator>
  265. #include <iosfwd>
  266. #include <sstream>
  267. #include <version>
  268. #include <source_location>
  269. namespace ra {
  270. constexpr char const * esc_bold = "\x1b[01m";
  271. constexpr char const * esc_unbold = "\x1b[0m";
  272. constexpr char const * esc_invert = "\x1b[07m";
  273. constexpr char const * esc_underline = "\x1b[04m";
  274. constexpr char const * esc_red = "\x1b[31m";
  275. constexpr char const * esc_green = "\x1b[32m";
  276. constexpr char const * esc_cyan = "\x1b[36m";
  277. constexpr char const * esc_yellow = "\x1b[33m";
  278. constexpr char const * esc_blue = "\x1b[34m";
  279. constexpr char const * esc_white = "\x1b[97m"; // an AIXTERM sequence
  280. constexpr char const * esc_plain = "\x1b[39m";
  281. constexpr char const * esc_reset = "\x1b[39m\x1b[0m"; // plain + unbold
  282. constexpr char const * esc_pink = "\x1b[38;5;225m";
  283. enum print_shape_t { defaultshape, withshape, noshape };
  284. template <class A>
  285. struct FormatArray
  286. {
  287. A const & a;
  288. print_shape_t shape;
  289. char const * sep0;
  290. char const * sep1;
  291. char const * sep2;
  292. };
  293. template <class A>
  294. constexpr FormatArray<A>
  295. format_array(A const & a, char const * sep0=" ", char const * sep1="\n", char const * sep2="\n")
  296. {
  297. return FormatArray<A> { a, defaultshape, sep0, sep1, sep2 };
  298. }
  299. struct shape_manip_t
  300. {
  301. std::ostream & o;
  302. print_shape_t shape;
  303. };
  304. constexpr shape_manip_t
  305. operator<<(std::ostream & o, print_shape_t shape)
  306. {
  307. return shape_manip_t { o, shape };
  308. }
  309. // is_foreign_vector is included bc std::vector or std::array may be used as the type of shape().
  310. // Excluding std::string_view allows it to be is_foreign_vector and still print as a string [ra13].
  311. template <class A> requires (is_ra<A> || (is_foreign_vector<A> && !std::is_convertible_v<A, std::string_view>))
  312. constexpr std::ostream &
  313. operator<<(std::ostream & o, A && a)
  314. {
  315. return o << format_array(a);
  316. }
  317. // initializer_list cannot match A && above.
  318. template <class T>
  319. constexpr std::ostream &
  320. operator<<(std::ostream & o, std::initializer_list<T> const & a)
  321. {
  322. return o << format_array(a);
  323. }
  324. template <class A>
  325. constexpr std::ostream &
  326. operator<<(shape_manip_t const & sm, A const & a)
  327. {
  328. FormatArray<A> fa = format_array(a);
  329. fa.shape = sm.shape;
  330. return sm.o << fa;
  331. }
  332. template <class A>
  333. constexpr std::ostream &
  334. operator<<(shape_manip_t const & sm, FormatArray<A> fa)
  335. {
  336. fa.shape = sm.shape;
  337. return sm.o << fa;
  338. }
  339. inline std::ostream &
  340. operator<<(std::ostream & o, std::source_location const & loc)
  341. {
  342. o << loc.file_name() << ":" << loc.line() << "," << loc.column();
  343. return o;
  344. }
  345. template <class ... A>
  346. constexpr std::string
  347. format(A && ... a)
  348. {
  349. if constexpr (sizeof ... (A)>0) {
  350. std::ostringstream o; (o << ... << std::forward<A>(a)); return o.str();
  351. } else {
  352. return "";
  353. }
  354. }
  355. constexpr std::string const &
  356. format(std::string const & s) { return s; }
  357. } // namespace ra
  358. #ifdef RA_AFTER_CHECK
  359. #error Bad header include order! Do not include ra/bootstrap.hh after ra/ra.hh.
  360. #endif