guanaqo 1.0.0-alpha.26
Utilities for scientific software
Loading...
Searching...
No Matches
lut.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file
4/// @ingroup core
5/// Compile-time lookup table generators.
6
7#include <array>
8#include <type_traits>
9#include <utility>
10
11namespace guanaqo {
12
13/// Returns a 2D array of the form:
14///
15/// ~~~
16/// {{ f(0, 0), f(0, 1), ..., f(0, C - 1) },
17/// { f(1, 0), f(1, 1), ..., f(1, C - 1) },
18/// { ..., ..., ..., ... },
19/// { f(R - 1, 0), f(R - 1, 1)}, ..., f(R - 1, C - 1) }}
20/// ~~~
21///
22/// The argument @p f should be a function (or callable) that accepts two
23/// arguments of type @ref std::integral_constant.
24///
25/// Example 1: compile-time arguments
26/// ~~~
27/// auto lut = guanaqo::make_2d_lut<int, 3, 4>(
28/// []<int R, int C>(std::integral_constant<int, R>,
29/// std::integral_constant<int, C>) {
30/// return std::pair{R, C};
31/// });
32/// for (const auto &row : lut) {
33/// for (const auto &el : row)
34/// std::cout << "(" << el.first << ", " << el.second << ")\t";
35/// std::cout << "\n";
36/// }
37/// ~~~
38/// ~~~
39/// (0, 0) (0, 1) (0, 2) (0, 3)
40/// (1, 0) (1, 1) (1, 2) (1, 3)
41/// (2, 0) (2, 1) (2, 2) (2, 3)
42/// ~~~
43///
44/// Example 2: run-time arguments
45/// ~~~
46/// auto lut = guanaqo::make_2d_lut<int, 3, 4>([](int r, int c) {
47/// return std::pair{r, c};
48/// });
49/// for (const auto &row : lut) {
50/// for (const auto &el : row)
51/// std::cout << "(" << el.first << ", " << el.second << ")\t";
52/// std::cout << "\n";
53/// }
54/// ~~~
55/// @ingroup core
56template <class I, I R, I C, class F>
57consteval auto make_2d_lut(F f) {
58 return [&]<I... Rs, I... Cs>(std::integer_sequence<I, Rs...>,
59 std::integer_sequence<I, Cs...>) {
60 auto make_row = [&]<I Rr>(std::integral_constant<I, Rr>) {
61 using elem_t = std::common_type_t<decltype(f(
62 std::integral_constant<I, Rr>(),
63 std::integral_constant<I, Cs>()))...>;
64 return std::array<elem_t, C>{
65 f(std::integral_constant<I, Rr>(),
66 std::integral_constant<I, Cs>())...,
67 };
68 };
69 using row_t = std::common_type_t<decltype(make_row(
70 std::integral_constant<I, Rs>()))...>;
71 return std::array<row_t, R>{{
72 make_row(std::integral_constant<I, Rs>())...,
73 }};
74 }(std::make_integer_sequence<I, R>(), std::make_integer_sequence<I, C>());
75}
76
77/// Returns an array of the form:
78///
79/// ~~~
80/// { f(0), f(1), ..., f(N - 1) }
81/// ~~~
82///
83/// The argument @p f should be a function (or callable) that accepts an
84/// argument of type @ref std::integral_constant.
85/// @ingroup core
86template <class I, I N, class F>
87consteval auto make_1d_lut(F f) {
88 return [&]<I... Ns>(std::integer_sequence<I, Ns...>) {
89 using elem_t =
90 std::common_type_t<decltype(f(std::integral_constant<I, Ns>()))...>;
91 return std::array<elem_t, N>{{f(std::integral_constant<I, Ns>())...}};
92 }(std::make_integer_sequence<I, N>());
93}
94
95namespace detail {
96
97template <auto... Args>
98struct lut;
99
100// Recursive case: integer range
101template <std::integral I, I N, auto... Ns>
102struct lut<N, Ns...> {
103 template <class F, class... Args>
104 static consteval auto make(F f, Args... args) {
105 return [&]<I... Is>(std::integer_sequence<I, Is...>) consteval {
106 using elem_t = std::common_type_t<decltype(lut<Ns...>::make(
107 f, args..., std::integral_constant<I, Is>{}))...>;
108 return std::array<elem_t, N>{{lut<Ns...>::make(
109 f, args..., std::integral_constant<I, Is>{})...}};
110 }(std::make_integer_sequence<I, N>());
111 }
112};
113
114// Recursive case: array
115template <class Elem, std::size_t N, std::array<Elem, N> Arr, auto... Ns>
116struct lut<Arr, Ns...> {
117 template <class F, class... Args>
118 static consteval auto make(F f, Args... args) {
119 return [&]<std::size_t... Is>(std::index_sequence<Is...>) consteval {
120 using elem_t = std::common_type_t<decltype(lut<Ns...>::make(
121 f, args..., std::integral_constant<Elem, Arr[Is]>{}))...>;
122 return std::array<elem_t, N>{{lut<Ns...>::make(
123 f, args..., std::integral_constant<Elem, Arr[Is]>{})...}};
124 }(std::make_index_sequence<N>());
125 }
126};
127
128// Base case: no ranges left → call f with all collected arguments
129template <>
130struct lut<> {
131 template <class F, class... Args>
132 static consteval auto make(F f, Args... args) {
133 return f(args...);
134 }
135};
136
137} // namespace detail
138
139/// Generalization of @ref make_1d_lut and @ref make_2d_lut. Generates an
140/// n-dimensional array of the given function @p f applied point-wise to the
141/// cartesian product of the ranges given as template parameters.
142///
143/// @tparam Ranges
144/// Integers or arrays of [structural types](https://en.cppreference.com/w/cpp/language/template_parameters.html#Constant_template_parameter):
145/// an integer N results in the half-open range [0, N), and arrays
146/// result in all array elements being passed to the given function.
147///
148/// Example:
149/// ~~~
150/// auto lut = guanaqo::make_lut<3, 4, std::array{10, 20, 30, 40, 50}>([](int i, int j, int k) {
151/// return std::tuple{i, j, k};
152/// });
153/// for (const auto &slice : lut) {
154/// for (const auto &row : slice) {
155/// for (const auto [i, j, k] : row)
156/// std::cout << "(" << i << ", " << j << ", " << k << ")\t";
157/// std::cout << "\n";
158/// }
159/// std::cout << "\n";
160/// }
161/// ~~~
162/// ~~~
163/// (0, 0, 10) (0, 0, 20) (0, 0, 30) (0, 0, 40) (0, 0, 50)
164/// (0, 1, 10) (0, 1, 20) (0, 1, 30) (0, 1, 40) (0, 1, 50)
165/// (0, 2, 10) (0, 2, 20) (0, 2, 30) (0, 2, 40) (0, 2, 50)
166/// (0, 3, 10) (0, 3, 20) (0, 3, 30) (0, 3, 40) (0, 3, 50)
167///
168/// (1, 0, 10) (1, 0, 20) (1, 0, 30) (1, 0, 40) (1, 0, 50)
169/// (1, 1, 10) (1, 1, 20) (1, 1, 30) (1, 1, 40) (1, 1, 50)
170/// (1, 2, 10) (1, 2, 20) (1, 2, 30) (1, 2, 40) (1, 2, 50)
171/// (1, 3, 10) (1, 3, 20) (1, 3, 30) (1, 3, 40) (1, 3, 50)
172///
173/// (2, 0, 10) (2, 0, 20) (2, 0, 30) (2, 0, 40) (2, 0, 50)
174/// (2, 1, 10) (2, 1, 20) (2, 1, 30) (2, 1, 40) (2, 1, 50)
175/// (2, 2, 10) (2, 2, 20) (2, 2, 30) (2, 2, 40) (2, 2, 50)
176/// (2, 3, 10) (2, 3, 20) (2, 3, 30) (2, 3, 40) (2, 3, 50)
177/// ~~~
178/// @ingroup core
179template <auto... Ranges, class F>
180consteval auto make_lut(F f) {
182}
183
184} // namespace guanaqo
consteval auto make_1d_lut(F f)
Returns an array of the form:
Definition lut.hpp:87
consteval auto make_2d_lut(F f)
Returns a 2D array of the form:
Definition lut.hpp:57
consteval auto make_lut(F f)
Generalization of make_1d_lut and make_2d_lut.
Definition lut.hpp:180
static consteval auto make(F f, Args... args)
Definition lut.hpp:118
static consteval auto make(F f, Args... args)
Definition lut.hpp:104
static consteval auto make(F f, Args... args)
Definition lut.hpp:132