basedOpinionated utility library |
git clone git://git.dimitrijedobrota.com/based.git |
Log | Files | Refs | README | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 7878c84a84832916d834b32a13cf5ff504896ef1 |
parent | 5339e33b06119b799b79910226098157f26174db |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Tue, 8 Apr 2025 13:24:43 +0200 |
Reduce functions and test
Diffstat:M | .clang-tidy | | | + |
M | include/based/algorithm.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------- |
M | test/CMakeLists.txt | | | + |
A | test/source/reduce_test.cpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 125 insertions(+), 13 deletions(-)
diff --git a/.clang-tidy b/.clang-tidy
@@ -10,6 +10,7 @@ Checks: "*,\
-llvm-header-guard,\
-llvm-include-order,\
-llvmlibc-*,\
-cppcoreguidelines-avoid-do-while,\
-cppcoreguidelines-pro-bounds-constant-array-index,\
-misc-include-cleaner,\
-misc-non-private-member-variables-in-classes,\
diff --git a/include/based/algorithm.hpp b/include/based/algorithm.hpp
@@ -166,7 +166,7 @@ Proc for_each(I f, I d, Proc proc)
// Precondition: readable_bounded_range(f, d);
while (f != d) {
proc(*f);
f = next(f);
f++;
}
return proc;
}
@@ -176,7 +176,7 @@ I find(I f, I d, const iter_value_t<I>& x)
{
// Precondition: readable_bounded_range(f, d);
while (f != d && *f != x) {
f = next(f);
f++;
}
return f;
}
@@ -186,7 +186,7 @@ I find_not(I f, I d, const iter_value_t<I>& x)
{
// Precondition: readable_bounded_range(f, d);
while (f != d && *f == x) {
f = next(f);
f++;
}
return f;
}
@@ -196,7 +196,7 @@ I find_if(I f, I d, P p)
{
// Precondition: readable_bounded_range(f, d);
while (f != d && !p(*f)) {
f = next(f);
f++;
}
return f;
}
@@ -206,7 +206,7 @@ I find_if_not(I f, I d, P p)
{
// Precondition: readable_bounded_range(f, d);
while (f != d && p(*f)) {
f = next(f);
f++;
}
return f;
}
@@ -245,9 +245,9 @@ J count(I f, I d, const iter_value_t<I>& x, J j)
// Precondition: readable_bounded_range(f, d);
while (f != d) {
if (*f == x) {
j = next(j);
j++;
}
f = next(f);
f++;
}
return j;
}
@@ -258,15 +258,16 @@ iter_dist_t<I> count(I f, I d, const iter_value_t<I>& x)
// Precondition: readable_bounded_range(f, d);
return count(f, d, x, iter_dist_t<I> {0});
}
template<ReadableIterator I, Iterator J>
J count_not(I f, I d, const iter_value_t<I>& x, J j)
{
// Precondition: readable_bounded_range(f, d);
while (f != d) {
if (*f != x) {
j = next(j);
j++;
}
f = next(f);
f++;
}
return j;
}
@@ -284,9 +285,9 @@ J count_if(I f, I d, P p, J j)
// Precondition: readable_bounded_range(f, d);
while (f != d) {
if (p(*f)) {
j = next(j);
j++;
}
f = next(f);
f++;
}
return j;
}
@@ -304,9 +305,9 @@ J count_if_not(I f, I d, P p, J j)
// Precondition: readable_bounded_range(f, d);
while (f != d) {
if (!p(*f)) {
j = next(j);
j++;
}
f = next(f);
f++;
}
return j;
}
@@ -318,4 +319,57 @@ iter_dist_t<I> count_if_not(I f, I d, P p)
return count_if_not(f, d, p, iter_dist_t<I> {0});
}
template<Iterator I, UnaryFunction<I> F, BinaryOperation<codomain_t<F, I>> Op>
auto reduce_nonempty(I f, I d, Op op, F fun)
{
assert(f != d);
// Precondition: bounded_range(f,d)
// Precondition: partially_associative(op)
auto r = fun(f);
f++;
while (f != d) {
r = op(r, fun(f));
f++;
}
return r;
}
template<Iterator I, UnaryFunction<I> F, BinaryOperation<codomain_t<F, I>> Op>
auto reduce(
I f, I d, Op op, F fun, const decltype(reduce_nonempty(f, d, op, fun))& z)
{
// Precondition: bounded_range(f,d)
// Precondition: partially_associative(op)
if (f == d) {
return z;
}
return reduce_nonempty(f, d, op, fun);
}
template<Iterator I, UnaryFunction<I> F, BinaryOperation<codomain_t<F, I>> Op>
auto reduce_nonzero(
I f, I d, Op op, F fun, const decltype(reduce_nonempty(f, d, op, fun))& z)
{
// Precondition: bounded_range(f,d)
// Precondition: partially_associative(op)
codomain_t<F, I> x;
do {
if (f == d) {
return z;
}
x = fun(f);
f++;
} while (x == z);
while (f != d) {
auto y = fun(f);
if (y != z) {
x = op(x, y);
}
f++;
}
return x;
}
} // namespace based
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
@@ -32,6 +32,7 @@ add_test(find_test)
add_test(find_if_test)
add_test(count_test)
add_test(count_if_test)
add_test(reduce_test)
# ---- List ----
add_test(list_test)
diff --git a/test/source/reduce_test.cpp b/test/source/reduce_test.cpp
@@ -0,0 +1,56 @@
#include <array>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include "based/algorithm.hpp"
struct op
{
auto operator()(const auto& a, const auto& b) { return a + b; }
};
struct fun
{
auto operator()(based::Iterator auto it) { return *it; }
};
TEST_CASE("reduce(empty)", "[algorithm/reduce]")
{
const std::array<int, 0> arr = {};
const auto count =
based::reduce(std::cbegin(arr), std::cend(arr), op {}, fun {}, 0);
REQUIRE(count == 0);
}
TEST_CASE("reduce(sequence)", "[algorithm/reduce]")
{
const std::array arr = {0, 1, 2, 3, 4, 5};
const auto count =
based::reduce(std::cbegin(arr), std::cend(arr), op {}, fun {}, 0);
REQUIRE(count == 15);
}
TEST_CASE("reduce_nonzero(empty)", "[algorithm/reduce_nonzero]")
{
const std::array<int, 0> arr = {};
const auto count =
based::reduce_nonzero(std::cbegin(arr), std::cend(arr), op {}, fun {}, 0);
REQUIRE(count == 0);
}
TEST_CASE("reduce_nonzero(sequence)", "[algorithm/reduce_nonzero]")
{
const std::array arr = {0, 1, 2, 3, 4, 5};
const auto count =
based::reduce_nonzero(std::cbegin(arr), std::cend(arr), op {}, fun {}, 0);
REQUIRE(count == 15);
}