Removing code duplicate with if constexpr - c++

I have some code as follows:
Matrix<T, N> res = zeros<T, N>(dims);
if constexpr (N1 == 1) {
MatrixView<U, 2> m1_view ({1, m1.dims[0]}, m1.dataView(), {m1.dims[0], 1});
if constexpr (N2 == 1) {
MatrixView<V, 2> m2_view ({m2.dims[0], 1}, m2.dataView(), {1, 1});
// duplicate code! yuck..
res.applyFunctionWithBroadcast(m1_view, m2_view, MatmulTo<U, V, T,
std::min(std::max(N1, 2lu), N - 1),
std::min(std::max(N2, 2lu), N - 1), N - 1>);
} else {
MatrixView<V, N2> m2_view (m2);
res.applyFunctionWithBroadcast(m1_view, m2_view, MatmulTo<U, V, T,
std::min(std::max(N1, 2lu), N - 1),
std::min(std::max(N2, 2lu), N - 1), N - 1>);
}
} else {
MatrixView<U, N1> m1_view (m1);
if constexpr (N2 == 1) {
MatrixView<V, 2> m2_view ({m2.dims[0], 1}, m2.dataView(), {1, 1});
// duplicate code! yuck..
res.applyFunctionWithBroadcast(m1_view, m2_view, MatmulTo<U, V, T,
std::min(std::max(N1, 2lu), N - 1),
std::min(std::max(N2, 2lu), N - 1), N - 1>);
} else {
MatrixView<V, N2> m2_view (m2);
res.applyFunctionWithBroadcast(m1_view, m2_view, MatmulTo<U, V, T,
std::min(std::max(N1, 2lu), N - 1),
std::min(std::max(N2, 2lu), N - 1), N - 1>);
}
}
return res;
which can be simplified in languages like Python (but not in C++):
if constexpr (N1 == 1) {
MatrixView<U, 2> m1_view ({1, m1.dims[0]}, m1.dataView(), {m1.dims[0], 1});
} else {
MatrixView<U, N1> m1_view (m1);
}
if constexpr (N2 == 1) {
MatrixView<V, 2> m2_view ({m2.dims[0], 1}, m2.dataView(), {1, 1});
} else {
MatrixView<V, N2> m2_view (m2);
}
// doesn't compile
res.applyFunctionWithBroadcast(m1_view, m2_view, MatmulTo<U, V, T,
std::min(std::max(N1, 2lu), N - 1),
std::min(std::max(N2, 2lu), N - 1), N - 1>);
return res;
MatrixView is not default constructible. (It is basically a non-owning view for some Matrix, therefore allowing so will break design)
The code above is logically valid but it won't compile, so I wrote the first ugly, verbose code.
Is there any way to remove code duplication in situations like this?

you might do:
auto m1_view = [&](){
if constexpr (N1 == 1) {
return MatrixView<U, 2>({1, m1.dims[0]}, m1.dataView(), {m1.dims[0], 1});
} else {
return MatrixView<U, N1>(m1);
}
}(); // notice extra () to immediate call
auto m2_view = [&](){
if constexpr (N2 == 1) {
return MatrixView<V, 2>({m2.dims[0], 1}, m2.dataView(), {1, 1});
} else {
return MatrixView<V, N2>(m2);
}
}();
Matrix<T, N> res = zeros<T, N>(dims);
res.applyFunctionWithBroadcast(m1_view, m2_view, MatmulTo<U, V, T,
std::min(std::max(N1, 2lu), N - 1),
std::min(std::max(N2, 2lu), N - 1), N - 1>);
return res;

Related

Generate constexpr array (error: the value of 'sum' is not usable in a constant expression)

The Problem
I need to generate all possible partitions of an integer m into the sum of j elements a_k, where each a_k can be -1, 0, or 1. This is a deterministic algorithm and as such it should be able to implement it at compile time. I would like to return a std::array with all possible combinations as constexpr.
My Algorithm
Plain and simple, there are 3^j combinations in total. So we loop over all of them and check if the sum is m. The total number of valid combinations will be
\sum_{k=m}^{\lfloor (m+j)/2\rfloor}\binom{j}{k}\binom{j-k}{k-m}
Thus we can calculate the size of the array (which is j times the number above) and simply queue in all the number combinations we obtain by brute force.
My Code
Check it on Godbolt
I obtain the error
error: the value of 'sum' is not usable in a constant expression
88 | if constexpr( sum == m )
I fail to see however, how sum is not known at compile time.
How can I fix this?
#include <array>
#include <iostream>
#include <utility>
/** constexpr for loop **/
template <auto Start, auto End, auto Inc, class F>
constexpr void constexpr_for(F&& f)
{
if constexpr (Start < End)
{
f(std::integral_constant<decltype(Start), Start>());
constexpr_for<Start + Inc, End, Inc>(f);
}
}
/** constexpr binomials **/
template<std::size_t n, std::size_t k>
struct Binomial
{
constexpr static std::size_t value = (Binomial<n-1,k-1>::value + Binomial<n-1,k>::value);
};
template<>
struct Binomial<0,0>
{
constexpr static std::size_t value = 1;
};
template<std::size_t n>
struct Binomial<n,0>
{
constexpr static std::size_t value = 1;
};
template<std::size_t n>
struct Binomial<n,n>
{
constexpr static std::size_t value = 1;
};
template<std::size_t n, std::size_t k>
constexpr std::size_t binomial()
{
return Binomial<n,k>::value;
}
/** formula from the picture **/
template<std::size_t j, std::size_t m>
constexpr std::size_t n()
{
std::size_t result = 0;
constexpr_for<m, (j+m)/2+1, 1>([&result](auto k){
result += binomial<j, k>() * binomial<j-k, k-m>();
});
return result;
}
/** constexpr power function **/
template<std::size_t i, std::size_t j>
struct pow_t
{
constexpr static std::size_t value = i * pow_t<i, j-1>::value;
};
template<std::size_t i>
struct pow_t<i, 0>
{
constexpr static std::size_t value = 1;
};
template<std::size_t i, std::size_t j>
constexpr std::size_t pow()
{
return pow_t<i, j>::value;
}
/** actual function in question **/
template<std::size_t j, std::size_t m>
constexpr std::array<int, j*n<j,m>()> integer_compositions()
{
std::array<int, j*n<j,m>()> result;
std::size_t i = 0;
constexpr_for<0, pow<3, j>(), 1>([&](auto k)
{
std::array<std::size_t, j> partition;
std::size_t sum = 0;
constexpr_for<0, j, 1>([&](auto l)
{
partition[l] = -((k/static_cast<std::size_t>(pow<3,l>()))%3-1);
sum += partition[l];
});
if constexpr( sum == m ) // line 88
{
constexpr_for<0, j, 1>([&](auto l)
{
result[j*i + l] = partition[l];
});
++i;
}
});
return result;
}
int main()
{
constexpr auto list = integer_compositions<3, 1>();
return EXIT_SUCCESS;
}
You're confusing the purpose of constexpr function. A constexpr function can be executed both at runtime and as part of constant expression, that depends on how you use the function (and probably if the compiler wants to optimize things at compile time).
You don't need all these templated functions, since the whole purpose of constexpr functions is to avoid them for clarity, e.g., your binomial function can simply be:
constexpr std::size_t binomial(std::size_t n, std::size_t k) {
if (k == 0 || n == k) {
return 1;
}
else {
return binomial(n - 1, k - 1) + binomial(n - 1, k);
}
}
And you can then do:
// binomial(3, 2) is evaluated at compile-time
std::array<int, binomial(3, 2)> x;
static_assert(x.size() == 3);
This can be done for all of your functions, except the last one (integer_compositions) because the return type depends on the parameter, so you need a template here.
There are other issues in your code, for instance, you need to initialize your std::array in constexpr function, so std::array<..., ...> result{}(note the {} here).
Below is a working version of your code that does not use all of these templates (this requires C++20, but only because operator== for std::array is only constexpr since C++20):
#include <array>
#include <iostream>
/** constexpr binomials **/
constexpr std::size_t binomial(std::size_t n, std::size_t k) {
if (k == 0 || n == k) {
return 1;
}
else {
return binomial(n - 1, k - 1) + binomial(n - 1, k);
}
}
/** formula from the picture **/
constexpr std::size_t n(std::size_t j, std::size_t m)
{
std::size_t result = 0;
for (std::size_t k = m; k <= (j + m) / 2; ++k) {
result += binomial(j, k) * binomial(j - k, k - m);
}
return result;
}
/** constexpr power function **/
constexpr std::size_t pow(std::size_t a, std::size_t n) {
std::size_t r = 1;
while (n--) {
r *= a;
}
return r;
}
/** actual function in question **/
template<std::size_t j, std::size_t m>
constexpr std::array<int, j*n(j, m)> integer_compositions()
{
std::array<int, j*n(j, m)> result{};
std::size_t i = 0;
for (std::size_t k = 0; k < ::pow(3, j); ++k) {
std::array<std::size_t, j> partition{};
std::size_t sum = 0;
for (std::size_t l = 0; l < j; ++l) {
partition[l] = -((k/static_cast<std::size_t>(::pow(3, l)))%3-1);
sum += partition[l];
}
if (sum == m) // line 88
{
for (std::size_t l = 0; l < j; ++l) {
result[j*i + l] = partition[l];
}
++i;
}
}
return result;
}
int main()
{
static_assert(integer_compositions<3, 1>() == std::array<int, 18>{});
}
Note: The static assertion obviously fails because I have no clue what the 18 values are.

Error in the problem of finding the maximum in the segment tree

When compiling the program throws this error:
Unhandled exception at 0x000418B9 в laba6c++.exe: 0xC00000FD: Stack overflow (characteristic: 0x00000001, 0x00402FFC).
I think the error is due to too many recursions of the maxim function, but I don't know how to fix it
#include <iostream>
#include <cstdio>
#include <vector>
const int INF = 1000;
using namespace std;
int const n = 5;
int t[4 * n];
int a[] = { 2, 2, 2, 1, 5 };
int find_max(int a, int b) {
if (a > b) return a;
if (b > a) return b;
return a;
}
int find_min(int a, int b) {
if (a < b) return a;
if (b < a) return b;
return a;
}
void build(int a[], int v, int tl, int tr) { //а-массив с числами, v-текущая вершина, tl & tr - границы соответствующие текущей вершине
if (tl == tr)
t[v] = a[tl]; //определяем номер для листа дерева
else {
int tm = (tl + tr) / 2; // tm-середина
build(a, v * 2, tl, tm); //строим левую часть дерева
build(a, v * 2 + 1, tm + 1, tr); //строим правую часть дерева
t[v] = find_max(t[v * 2], t[v * 2 + 1]); //определяем номер текущей вершины
}
}
int maxim(int v, int tl, int tr, int l, int r) {
if (l > r)
return -INF;
if (l == tl && r == tr)
return t[v];
int tm = (tl + tr) / 2;
return find_max(maxim(v * 2, tl, tm, l, find_min(r, tm)), maxim(v * 2 + 1, tm + 1, tr, find_max(l, tm + 1), r));
}
int main() {
build(a, 1, 0, n - 1);
cout << maxim(1, 0, n - 1, 2, 5);
}

Nested static loop fails to work due to constexpr uncapturable

I have this static_loop construct that is used for type dispatching over loop unrolling.
template <std::size_t n, typename F> void static_loop(F&& f) {
static_assert(n <= 8 && "static loop size should <= 8");
if constexpr (n >= 8)
f(std::integral_constant<size_t, n - 8>());
if constexpr (n >= 7)
f(std::integral_constant<size_t, n - 7>());
if constexpr (n >= 6)
f(std::integral_constant<size_t, n - 6>());
if constexpr (n >= 5)
f(std::integral_constant<size_t, n - 5>());
if constexpr (n >= 4)
f(std::integral_constant<size_t, n - 4>());
if constexpr (n >= 3)
f(std::integral_constant<size_t, n - 3>());
if constexpr (n >= 2)
f(std::integral_constant<size_t, n - 2>());
if constexpr (n >= 1)
f(std::integral_constant<size_t, n - 1>());
}
template <typename T> constexpr size_t tupleSize(T) { return tuple_size_v<T>; }
struct A {
int a;
int b;
void run() {
auto ab = std::make_tuple(std::ref(a), std::ref(b));
static_loop<2>([&](auto i) {
std::get<i>(ab) = i;
static_loop<2>([&](auto j) { std::get<i * j>(ab) = i; });
// static_loop<2>([&, i = std::integral_constant<size_t, i>()](auto j) { std::get<i * j>(ab) = i; });
});
std::cout << a << " " << b << std::endl;
}
};
However it doesn't compile when doing nested loops. I'd assume i and j are both constexpr thus i * j is valid in std::get<>, however, compiler seems not allowing this. Is it possible to capture i as constexpr in the inner lambda?
Full example is at godbolt along with the error messages.
static_loop<2>([&](auto i) {
std::get<i>(ab) = i;
static_loop<2>([&](auto j) { std::get<i * j>(ab) }
}
The IDE correctly underlines the error for you. i * jis a multiplication of two variables, it is not a compile-time constant.
You can nest loops if you switch to template parameters rather than function arguments.
This somehow works. Not sure if it violates any standard rules.
template <std::size_t n, typename F> void static_loop(F&& f) {
static_assert(n <= 8 && "static loop size should <= 8");
if constexpr (n >= 8)
f(std::integral_constant<size_t, n - 8>());
if constexpr (n >= 7)
f(std::integral_constant<size_t, n - 7>());
if constexpr (n >= 6)
f(std::integral_constant<size_t, n - 6>());
if constexpr (n >= 5)
f(std::integral_constant<size_t, n - 5>());
if constexpr (n >= 4)
f(std::integral_constant<size_t, n - 4>());
if constexpr (n >= 3)
f(std::integral_constant<size_t, n - 3>());
if constexpr (n >= 2)
f(std::integral_constant<size_t, n - 2>());
if constexpr (n >= 1)
f(std::integral_constant<size_t, n - 1>());
}
struct A {
int a;
int b;
void run() {
auto ab = std::make_tuple(std::ref(a), std::ref(b));
static_loop<2>([&](auto i) {
std::get<i>(ab) = i;
static_loop<2>([&](auto j) { auto ii = decltype(i)(); std::get<ii * j>(ab) = ii; });
});
std::cout << a << " " << b << std::endl;
}
};
The captured value might not be constexpr but its type is somehow retained.

STATUS_ACCESS_VIOLATION at 3-way quicksort (C++)

I tried to implement the 3-way quicksort algorithm in C++, described here.
Unfortunality I get the exception STATUS_ACCESS_VIOLATION.
template<typename T, size_t SIZE>
void qsort(std::array<T, SIZE> &a, std::size_t lo, std::size_t hi) {
if (hi <= lo) {
return;
}
std::size_t lt = lo, gt = hi;
T v = a[lo];
std::size_t i = lo;
while (i <= gt) {
if (a[i] < v) {
std::swap(a[lt++], a[i++]);
} else if (a[i] > v) {
std::swap(a[i], a[gt--]);
} else {
i++;
}
}
qsort(a, lo, lt - 1);
qsort(a, gt + 1, hi);
}
template<typename T, size_t SIZE>
void quickSortThreeWay(std::array<T, SIZE> &a) {
std::size_t arraySize = sizeof(a) / sizeof(a[0]);
qsort(a, 0, arraySize - 1);
}
The array is a std::array I filled with random values. This works fine with other algorithms.
Can you help me find the problem? Thanks.
Thank you.
At this line of code:
qsort(a, lo, lt - 1);
When lo is 0, lt can be -1. Unfortunately, your signature doesn't allow negative numbers because you are using size_t, so in my test, when lo is 0, lt - 1 is 18446744073709551615. This test then fails to do what you want:
if (hi <= lo) { return }
Then it blindly goes on and crashes. You might think to allow negative numbers in your method signature instead of size_t, eg:
void qsort(std::array<T, SIZE> &a, int lo, int hi) {
std::cout << "qsort (a, " << lo << ", " << hi << ")" << std::endl;
When I run the above modified signature against an array of three strings:
std::array<std::string, 3> a;
a[0] = "hello";
a[1] = "abacus";
a[2] = "goodbye";
quickSortThreeWay (a);
I get -1 for that last parameter:
qsort (a, 0, 2)
qsort (a, 0, 1)
qsort (a, 0, -1)
qsort (a, 1, 1)
qsort (a, 3, 2)

Custom binary search in vector

Suppose I have a vector<int> myVec. Let there be n elements in it. I know that these elements are in sorted order(ascending) and also that they are unique. Let n = 10 and myVec be {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}. I'm given l and r such that 0<=l<=r<=n-1. Now i want to search an element val in the subvector that is defined by the bounds l and rsuch that
if val is found return val
if val is not found then return (if possible) a value in the subvector which is just smaller than val.
Return false(or -1 maybe) if either of the above is not possible.
In the above case if if l = 3 and r = 5. The subvector is {8, 10, 12}. If val = 8 return 8. If val = 7 return false (or -1). If val = 9 return 8.
How do I implement this. I want order comparable to binary search. Also, is it possible to use std::binary_search() present under algorithm header file.
something like this?
int search(int l, int r, int value) {
if (l > vec.Size() || r > vec.Size() || l > r) return -1;
for (int i = r; i >= l; --i) {
int v = vector[i];
if (v <= value) return v;
}
return -1;
}
or does it need to be binary?
int BinarySearch(int l, int r, int value) {
return PrivateBinarySearch(l, r, (l+r)/2, value);
}
int PrivateBinarySearch(int l, int r, int index, int value) {
if (vector[index] == value) return value;
else if (vector[index] > value) {
if (index == l) return -1;
else if (index == r) return -1;
else return PrivateBinarySearch(l, index, (index-1+l)/2, value);
}
else { // vector[index] < value
if (index == l) return vector[index];
else if (index == r) return vector[index];
else return PrivateBinarySearch(index, r, (index+1+r)/2, value);
}
Hope this helps
This should work for you and is pretty extensible and flexible:
template<typename T>
typename vector<T>::const_iterator
find_or_under (typename vector<T>::const_iterator start, typename vector<T>::const_iterator end,
const T& val)
{
auto el = std::lower_bound(start, end, val);
//if not found, propagate
if (el == end)
return el;
//if it's equal, just return the iterator
if ((*el) == val)
return el;
//if there is no value of an equal or smaller size, return the end
if (el == start)
return end;
//otherwise, return the previous element
return el-1;
}
//Functor representing the search
struct CustomSearch
{
//Create a searcher from a subrange
CustomSearch (const vector<int> &v, size_t l, size_t r)
{
start = std::lower_bound(std::begin(v), std::end(v), l);
end = find_or_under(start, std::end(v), r) + 1;
}
//Calling the searcher
//Returns this->end on not found
auto operator() (int val)
{
return find_or_under(start, end, val);
}
vector<int>::const_iterator start;
vector<int>::const_iterator end;
};
int main() {
vector<int> v = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20};
CustomSearch searcher {v, 3, 8};
cout << *searcher(6);
}
Using traditional binary search with minor modification:
#include <iostream>
#include <vector>
int search(const std::vector<int> &vec, int l, int r, int val)
{
int pivot, xl = l, xr = r, mid;
do {
/* Not exact match, check if the closest lower match is in the
* subvector. */
if (xl > xr) {
return xr >= l ? vec[xr]: -1;
}
mid = (xl + xr) / 2;
pivot = vec[mid];
if (val < pivot) {
xr = mid - 1;
} else if (val > pivot) {
xl = mid + 1;
} else if (val == pivot) {
return val;
}
} while (true);
}
int main()
{
std::vector<int> myVec(10);
myVec[0] = 2;
myVec[1] = 4;
myVec[2] = 6;
myVec[3] = 8;
myVec[4] = 10;
myVec[5] = 12;
myVec[6] = 14;
myVec[7] = 16;
myVec[8] = 18;
myVec[9] = 20;
int l = 3, r = 5;
std::cout << "search(3, 5, 8) = " << search(myVec, 3, 5, 8) << std::endl;
std::cout << "search(3, 5, 7) = " << search(myVec, 3, 5, 7) << std::endl;
std::cout << "search(3, 5, 9) = " << search(myVec, 3, 5, 9) << std::endl;
return 0;
}
enter code here