1
\$\begingroup\$

This is a follow-up question for A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++ and A recursive_count_if Function with Unwrap Level for Various Type Arbitrary Nested Iterable Implementation in C++. The function recursive_count_if is Implemented with a std::ranges::range input and a predicate. I am attempting to implement a function batch_recursive_count_if in order to handle the use cases of multiple predicates (as the example as below) via template parameter pack technique.

auto predicator1 = [](auto& i) {return i == 1; };
auto predicator2 = [](auto& i) {return i == 2; };
auto predicator3 = [](auto& i) {return i == 3; };
auto predicator4 = [](auto& i) {return i == 4; };
auto predicator5 = [](auto& i) {return i == 5; };
auto predicator6 = [](auto& i) {return i == 6; };
auto predicator7 = [](auto& i) {return i == 7; };
auto predicator8 = [](auto& i) {return i == 8; };
auto predicator9 = [](auto& i) {return i == 9; };
auto predicator10 = [](auto& i) {return i == 10; };

The API shape is like std::vector<int> counts = batch_recursive_count_if<unwrap_level>(ranges, predicate1, predicate2, predicate3, ...); which includes the extended the predicate parameters and the return type is a std::vector (to represent a sequence of output which is the result of each predicate).

The experimental implementation

//  recursive_count_if implementation (the version with unwrap_level)
template<std::size_t unwrap_level = 1, std::ranges::range T, class Pred>
auto recursive_count_if(const T& input, const Pred& predicate)
{
    if constexpr (unwrap_level > 1)
    {
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
            return recursive_count_if<unwrap_level - 1>(element, predicate);
            });
    }
    else
    {
        return std::count_if(std::ranges::cbegin(input), std::ranges::cend(input), predicate);
    }
}

//  batch_recursive_count_if implementation (the version with unwrap_level)
template<std::size_t unwrap_level = 1, std::ranges::range T, class Pred1>
constexpr auto batch_recursive_count_if(const T& input, const Pred1& predicate1)
{
    std::vector<decltype(recursive_count_if<unwrap_level>(input, predicate1))> output;
    output.push_back(recursive_count_if<unwrap_level>(input, predicate1));
    return output;
}

template<std::size_t unwrap_level = 1, std::ranges::range T, class Pred1, class... Preds>
constexpr auto batch_recursive_count_if(const T& input, const Pred1& predicate1, const Preds&... predicates)
{
    auto output1 = batch_recursive_count_if<unwrap_level>(input, predicate1);
    auto output2 = batch_recursive_count_if<unwrap_level>(input, predicates...);
    output1.insert(std::ranges::cend(output1), std::ranges::cbegin(output2), std::ranges::cend(output2));
    return output1;
}

Test cases

The test cases below include:

  • Counting int element in std::vector<std::vector<int>> which is equal to specified number.

  • Counting int element in std::deque<std::deque<int>> which is equal to specified number.

  • Counting std::string element in std::vector<std::vector<std::string>> which is equal to specified string.

  • Counting std::vector<std::vector<int>> element in std::vector<std::vector<std::vector<int>>> which is equal to specified pattern.

//  std::vector<std::vector<int>> case
std::cout << "std::vector<std::vector<int>> case:" << '\n';
std::vector<int> test_vector{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
std::vector<decltype(test_vector)> test_vector2;
test_vector2.push_back(test_vector);
test_vector2.push_back(test_vector);
test_vector2.push_back(test_vector);

// use lambda expressions to count the elements which is equal to 1 to 10.
auto predicator1 = [](auto& i) {return i == 1; };
std::cout << "#number is 1: " << recursive_count_if<2>(test_vector2, predicator1) << '\n';

auto predicator2 = [](auto& i) {return i == 2; };
auto predicator3 = [](auto& i) {return i == 3; };
auto predicator4 = [](auto& i) {return i == 4; };
auto predicator5 = [](auto& i) {return i == 5; };
auto predicator6 = [](auto& i) {return i == 6; };
auto predicator7 = [](auto& i) {return i == 7; };
auto predicator8 = [](auto& i) {return i == 8; };
auto predicator9 = [](auto& i) {return i == 9; };
auto predicator10 = [](auto& i) {return i == 10; };

auto batch_output1 = batch_recursive_count_if<2>(test_vector2, predicator1, predicator2, predicator3, predicator4, predicator5, predicator6, predicator7, predicator8, predicator9, predicator10);
std::cout << "Each element in batch_output1: " << '\n';
for (auto& element : batch_output1)
{
    std::cout << element << '\n';
}
std::cout << '\n';

// std::deque<std::deque<int>> case
std::cout << "std::deque<std::deque<int>> case:" << '\n';
std::deque<int> test_deque;
test_deque.push_back(1);
test_deque.push_back(2);
test_deque.push_back(3);

std::deque<decltype(test_deque)> test_deque2;
test_deque2.push_back(test_deque);
test_deque2.push_back(test_deque);
test_deque2.push_back(test_deque);

// use lambda expressions to count the elements which is equal to 1 to 10.
std::cout << "#number is 1: " << recursive_count_if<2>(test_deque2, predicator1) << '\n';

auto batch_output2 = batch_recursive_count_if<2>(test_deque2, predicator1, predicator2, predicator3, predicator4, predicator5, predicator6, predicator7, predicator8, predicator9, predicator10);
std::cout << "Each element in batch_output1: " << '\n';
for (auto& element : batch_output2)
{
    std::cout << element << '\n';
}
std::cout << '\n';

// std::vector<std::vector<std::string>> case
std::cout << "std::vector<std::vector<std::string>>:" << '\n';
std::vector<std::vector<std::string>> v = { {"hello"}, {"world"} };
auto is_hello = [](auto& i) { return i == "hello"; };
auto is_world = [](auto& i) { return i == "world"; };
std::cout << "recursive_count_if output of is_hello:" << recursive_count_if<2>(v, is_hello) << std::endl;
auto batch_output3 = batch_recursive_count_if<2>(v, is_hello, is_world);
std::cout << "Each element in batch_output1: " << '\n';
for (auto& element : batch_output3)
{
    std::cout << element << '\n';
}
std::cout << '\n';

//  Count specific std::vector<std::vector<int>> element in std::vector<std::vector<std::vector<int>>> case
std::cout << "Count specific std::vector<std::vector<int>> element in std::vector<std::vector<std::vector<int>>> case:" << std::endl;
std::vector<decltype(test_vector2)> test_vector3;
for (size_t i = 0; i < 3; i++)
{
    test_vector3.push_back(test_vector2);
}

auto batch_output4 = batch_recursive_count_if<1>(test_vector3,
    [test_vector2](auto& element)
    {
        if (element.size() != test_vector2.size())
        {
            return false;
        }
        return std::equal(element.begin(), element.end(), test_vector2.begin());
    },
    [test_vector2](auto& element)
    {
        auto vector_for_comparison = test_vector2;
        vector_for_comparison.push_back(std::vector<int>{12, 2, 3});
        if (element.size() != vector_for_comparison.size())
        {
            return false;
        }
        return std::equal(vector_for_comparison.begin(), vector_for_comparison.end(), vector_for_comparison.begin());
    });
for (auto& element : batch_output4)
{
    std::cout << element << '\n';
}
std::cout << '\n';

The output of the test code above:

std::vector<std::vector<int>> case:
#number is 1: 3
Each element in batch_output1:
3
3
6
6
0
0
3
3
3
3

std::deque<std::deque<int>> case:
#number is 1: 3
Each element in batch_output1:
3
3
3
0
0
0
0
0
0
0

std::vector<std::vector<std::string>>:
recursive_count_if output of is_hello:1
Each element in batch_output1:
1
1

Count specific std::vector<std::vector<int>> element in std::vector<std::vector<std::vector<int>>> case:
3
0

Full Testing Code

The full testing code:

//  A batch_recursive_count_if Function with Unwrap Level for Various Type Arbitrary Nested Iterable Implementation in C++

#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <complex>
#include <concepts>
#include <deque>
#include <execution>
#include <exception>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <mutex>
#include <numeric>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
//#define USE_BOOST_MULTIDIMENSIONAL_ARRAY
#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
#include <boost/multi_array.hpp>
#include <boost/multi_array/algorithm.hpp>
#include <boost/multi_array/base.hpp>
#include <boost/multi_array/collection_concept.hpp>
#endif

template<typename T>
concept is_back_inserterable = requires(T x)
{
    std::back_inserter(x);
};

template<typename T>
concept is_inserterable = requires(T x)
{
    std::inserter(x, std::ranges::end(x));
};

template<typename T>
concept is_minusable = requires(T x) { x - x; };

template<typename T1, typename T2>
concept is_minusable2 = requires(T1 x1, T2 x2) { x1 - x2; };

#ifdef USE_BOOST_MULTIDIMENSIONAL_ARRAY
template<typename T>
concept is_multi_array = requires(T x)
{
    x.num_dimensions();
    x.shape();
    boost::multi_array(x);
};
#endif

template<typename T1, typename T2>
concept is_std_powable = requires(T1 x1, T2 x2)
{
    std::pow(x1, x2);
};

//  recursive_depth function implementation
template<typename T>
constexpr std::size_t recursive_depth()
{
    return 0;
}

template<std::ranges::input_range Range>
constexpr std::size_t recursive_depth()
{
    return recursive_depth<std::ranges::range_value_t<Range>>() + 1;
}

//  recursive_count implementation

//  recursive_count implementation (the version with unwrap_level)
template<std::size_t unwrap_level, class T>
constexpr auto recursive_count(const T& input, const auto& target)
{
    if constexpr (unwrap_level > 0)
    {
        static_assert(unwrap_level <= recursive_depth<T>(),
            "unwrap level higher than recursion depth of input");
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [&target](auto&& element) {
            return recursive_count<unwrap_level - 1>(element, target);
            });
    }
    else
    {
        return (input == target) ? 1 : 0;
    }
}

//  recursive_count implementation (the version without unwrap_level)
template<std::ranges::input_range Range>
constexpr auto recursive_count(const Range& input, const auto& target)
{
    return recursive_count<recursive_depth<Range>()>(input, target);
}

//  recursive_count implementation (with execution policy)
template<class ExPo, std::ranges::input_range Range, typename T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target)
{
    return std::count(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), target);
}

template<class ExPo, std::ranges::input_range Range, typename T>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_count(ExPo execution_policy, const Range& input, const T& target)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [execution_policy, target](auto&& element) {
        return recursive_count(execution_policy, element, target);
        });
}

//  recursive_count_if implementation
template<class T, std::invocable<T> Pred>
constexpr std::size_t recursive_count_if(const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<std::ranges::input_range Range, class Pred>
requires (!std::invocable<Pred, Range>)
constexpr auto recursive_count_if(const Range& input, const Pred& predicate)
{
    return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
        return recursive_count_if(element, predicate);
        });
}

//  recursive_count_if implementation (with execution policy)
template<class ExPo, class T, std::invocable<T> Pred>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr std::size_t recursive_count_if(ExPo execution_policy, const T& input, const Pred& predicate)
{
    return predicate(input) ? 1 : 0;
}

template<class ExPo, std::ranges::input_range Range, class Pred>
requires ((std::is_execution_policy_v<std::remove_cvref_t<ExPo>>) && (!std::invocable<Pred, Range>))
constexpr auto recursive_count_if(ExPo execution_policy, const Range& input, const Pred& predicate)
{
    return std::transform_reduce(execution_policy, std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
        return recursive_count_if(element, predicate);
        });
}

//  recursive_count_if implementation (the version with unwrap_level)
template<std::size_t unwrap_level = 1, std::ranges::range T, class Pred>
auto recursive_count_if(const T& input, const Pred& predicate)
{
    if constexpr (unwrap_level > 1)
    {
        return std::transform_reduce(std::ranges::cbegin(input), std::ranges::cend(input), std::size_t{}, std::plus<std::size_t>(), [predicate](auto&& element) {
            return recursive_count_if<unwrap_level - 1>(element, predicate);
            });
    }
    else
    {
        return std::count_if(std::ranges::cbegin(input), std::ranges::cend(input), predicate);
    }
}

//  batch_recursive_count_if implementation (the version with unwrap_level)
template<std::size_t unwrap_level = 1, std::ranges::range T, class Pred1>
constexpr auto batch_recursive_count_if(const T& input, const Pred1& predicate1)
{
    std::vector<decltype(recursive_count_if<unwrap_level>(input, predicate1))> output;
    output.push_back(recursive_count_if<unwrap_level>(input, predicate1));
    return output;
}

template<std::size_t unwrap_level = 1, std::ranges::range T, class Pred1, class... Preds>
constexpr auto batch_recursive_count_if(const T& input, const Pred1& predicate1, const Preds&... predicates)
{
    auto output1 = batch_recursive_count_if<unwrap_level>(input, predicate1);
    auto output2 = batch_recursive_count_if<unwrap_level>(input, predicates...);
    output1.insert(std::ranges::cend(output1), std::ranges::cbegin(output2), std::ranges::cend(output2));
    return output1;
}

//  recursive_max implementation
template<std::totally_ordered T>
constexpr auto recursive_max(T number)
{
    return number;
}

template<std::ranges::range T>
constexpr auto recursive_max(const T& numbers)
{
    auto maxValue = recursive_max(numbers.at(0));
    for (auto& element : numbers)
    {
        maxValue = std::max(maxValue, recursive_max(element));
    }
    return maxValue;
}

//  recursive_print implementation
template<typename T>
constexpr void recursive_print(const T& input, const std::size_t level = 0)
{
    std::cout << std::string(level, ' ') << input << '\n';
}

template<std::ranges::input_range Range>
constexpr void recursive_print(const Range& input, const std::size_t level = 0)
{
    std::cout << std::string(level, ' ') << "Level " << level << ":" << std::endl;
    std::ranges::for_each(input, [level](auto&& element) {
        recursive_print(element, level + 1);
    });
}

//  recursive_size implementation
template<class T> requires (!std::ranges::range<T>)
constexpr auto recursive_size(const T& input)
{
    return 1;
}

template<std::ranges::range Range> requires (!(std::ranges::input_range<std::ranges::range_value_t<Range>>))
constexpr auto recursive_size(const Range& input)
{
    return std::ranges::size(input);
}

template<std::ranges::range Range> requires (std::ranges::input_range<std::ranges::range_value_t<Range>>)
constexpr auto recursive_size(const Range& input)
{
    return std::transform_reduce(std::ranges::begin(input), std::end(input), std::size_t{}, std::plus<std::size_t>(), [](auto& element) {
        return recursive_size(element);
        });
}

//  recursive_reduce implementation
//  Reference: https://codereview.stackexchange.com/a/251310/231235
template<class T, class ValueType, class Function = std::plus<ValueType>>
constexpr auto recursive_reduce(const T& input, ValueType init, const Function& f)
{
    return f(init, input);
}

template<std::ranges::range Container, class ValueType, class Function = std::plus<ValueType>>
constexpr auto recursive_reduce(const Container& input, ValueType init, const Function& f = std::plus<ValueType>())
{
    for (const auto& element : input) {
        auto result = recursive_reduce(element, ValueType{}, f);
        init = f(init, result);
    }
    return init;
}

template<typename T>
concept is_recursive_reduceable = requires(T x)
{
    recursive_reduce(x, T{});
};

template<typename T>
concept is_recursive_sizeable = requires(T x)
{
    recursive_size(x);
};

//  arithmetic_mean implementation
template<class T = double, is_recursive_sizeable Container>
constexpr auto arithmetic_mean(const Container& input)
{
    if (recursive_size(input) == 0) //  Check the case of dividing by zero exception
    {
        throw std::logic_error("Divide by zero exception"); //  Handle the case of dividing by zero exception
    }
    return (recursive_reduce(input, T{})) / (recursive_size(input));
}

//  recursive_invoke_result_t implementation
template<typename, typename>
struct recursive_invoke_result { };

template<typename T, std::invocable<T> F>
struct recursive_invoke_result<F, T> { using type = std::invoke_result_t<F, T>; };

template<typename F, template<typename...> typename Container, typename... Ts>
requires (
    !std::invocable<F, Container<Ts...>>&&
    std::ranges::input_range<Container<Ts...>>&&
    requires { typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type; })
    struct recursive_invoke_result<F, Container<Ts...>>
{
    using type = Container<typename recursive_invoke_result<F, std::ranges::range_value_t<Container<Ts...>>>::type>;
};

template<typename F, typename T>
using recursive_invoke_result_t = typename recursive_invoke_result<F, T>::type;

//  recursive_transform implementation
template <class T, std::invocable<T> F>
constexpr auto recursive_transform(const T& input, const F& f)
{
    return f(input);
}

template <
    std::ranges::input_range Range,
    class F>
    requires (!std::invocable<F, Range>)
    constexpr auto recursive_transform(const Range& input, const F& f)
{
    recursive_invoke_result_t<F, Range> output{};

    std::ranges::transform(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        [&f](auto&& element) { return recursive_transform(element, f); }
    );
    return output;
}

//  recursive_copy_if function 
template <std::ranges::input_range Range, std::invocable<std::ranges::range_value_t<Range>> UnaryPredicate>
constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
    Range output{};
    std::ranges::copy_if(std::ranges::cbegin(input), std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        unary_predicate);
    return output;
}

template <
    std::ranges::input_range Range,
    class UnaryPredicate>
    requires (!std::invocable<UnaryPredicate, std::ranges::range_value_t<Range>>)
    constexpr auto recursive_copy_if(const Range& input, const UnaryPredicate& unary_predicate)
{
    Range output{};

    std::ranges::transform(
        std::ranges::cbegin(input),
        std::ranges::cend(input),
        std::inserter(output, std::ranges::end(output)),
        [&unary_predicate](auto&& element) { return recursive_copy_if(element, unary_predicate); }
    );
    return output;
}


//  recursive_transform_reduce implementation
template<class Input, class T, class UnaryOp, class BinaryOp = std::plus<T>>
constexpr auto recursive_transform_reduce(const Input& input, T init, const UnaryOp& unary_op, const BinaryOp& binop = std::plus<T>())
{
    return binop(init, unary_op(input));
}

template<std::ranges::range Input, class T, class UnaryOp, class BinaryOp = std::plus<T>>
constexpr auto recursive_transform_reduce(const Input& input, T init, const UnaryOp& unary_op, const BinaryOp& binop = std::plus<T>())
{
    return std::transform_reduce(std::ranges::begin(input), std::end(input), init, binop, [&](auto& element) {
        return recursive_transform_reduce(element, T{}, unary_op, binop);
        });
}

//  With execution policy
template<class ExPo, class Input, class T, class UnaryOp, class BinaryOp = std::plus<T>>
//requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_transform_reduce(ExPo execution_policy, const Input& input, T init, const UnaryOp& unary_op, const BinaryOp& binop = std::plus<T>())
{
    return binop(init, unary_op(input));
}

template<class ExPo, std::ranges::range Input, class T, class UnaryOp, class BinaryOp = std::plus<T>>
requires (std::is_execution_policy_v<std::remove_cvref_t<ExPo>>)
constexpr auto recursive_transform_reduce(ExPo execution_policy, const Input& input, T init, const UnaryOp& unary_op, const BinaryOp& binop = std::plus<T>())
{
    return std::transform_reduce(execution_policy, std::ranges::begin(input), std::end(input), init, binop, [&](auto& element) {
        return recursive_transform_reduce(execution_policy, element, T{}, unary_op, binop);
        });
}


template<typename T>
concept can_calculate_variance_of = requires(const T & value)
{
    (std::pow(value, 2) - value) / std::size_t{ 1 };
};

template<typename T>
struct recursive_iter_value_t_detail
{
    using type = T;
};

template <std::ranges::range T>
struct recursive_iter_value_t_detail<T>
    : recursive_iter_value_t_detail<std::iter_value_t<T>>
{ };

template<typename T>
using recursive_iter_value_t = typename recursive_iter_value_t_detail<T>::type;

//  population_variance function implementation (with recursive_transform_reduce template function)
template<class T = double, is_recursive_sizeable Container>
requires (can_calculate_variance_of<recursive_iter_value_t<Container>>)
constexpr auto population_variance(const Container& input)
{
    if (recursive_size(input) == 0) //  Check the case of dividing by zero exception
    {
        throw std::logic_error("Divide by zero exception"); //  Handle the case of dividing by zero exception
    }
    auto mean = arithmetic_mean<T>(input);
    return recursive_transform_reduce(std::execution::par,
        input, T{}, [mean](auto& element) {
            return std::pow(element - mean, 2);
        }, std::plus<T>()) / recursive_size(input);
}

//  population_standard_deviation implementation
template<class T = double, is_recursive_sizeable Container>
requires (can_calculate_variance_of<recursive_iter_value_t<Container>>)
constexpr auto population_standard_deviation(const Container& input)
{
    if (recursive_size(input) == 0) //  Check the case of dividing by zero exception
    {
        throw std::logic_error("Divide by zero exception"); //  Handle the case of dividing by zero exception
    }
    return std::pow(population_variance(input), 0.5);
}

template<std::size_t dim, class T>
constexpr auto n_dim_vector_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_vector_generator<dim - 1>(input, times);
        std::vector<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, std::size_t times, class T>
constexpr auto n_dim_array_generator(T input)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_array_generator<dim - 1, times>(input);
        std::array<decltype(element), times> output;
        std::fill(std::ranges::begin(output), std::ranges::end(output), element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_deque_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_deque_generator<dim - 1>(input, times);
        std::deque<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, class T>
constexpr auto n_dim_list_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        auto element = n_dim_list_generator<dim - 1>(input, times);
        std::list<decltype(element)> output(times, element);
        return output;
    }
}

template<std::size_t dim, template<class...> class Container = std::vector, class T>
constexpr auto n_dim_container_generator(T input, std::size_t times)
{
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        return Container(times, n_dim_container_generator<dim - 1, Container, T>(input, times));
    }
}

void batch_recursive_count_if_test1();

int main()
{
    batch_recursive_count_if_test1();
    return 0;
}


void batch_recursive_count_if_test1()
{
    //  std::vector<std::vector<int>> case
    std::cout << "std::vector<std::vector<int>> case:" << '\n';
    std::vector<int> test_vector{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
    std::vector<decltype(test_vector)> test_vector2;
    test_vector2.push_back(test_vector);
    test_vector2.push_back(test_vector);
    test_vector2.push_back(test_vector);

    // use lambda expressions to count the elements which is equal to 1 to 10.
    auto predicator1 = [](auto& i) {return i == 1; };
    std::cout << "#number is 1: " << recursive_count_if<2>(test_vector2, predicator1) << '\n';

    auto predicator2 = [](auto& i) {return i == 2; };
    auto predicator3 = [](auto& i) {return i == 3; };
    auto predicator4 = [](auto& i) {return i == 4; };
    auto predicator5 = [](auto& i) {return i == 5; };
    auto predicator6 = [](auto& i) {return i == 6; };
    auto predicator7 = [](auto& i) {return i == 7; };
    auto predicator8 = [](auto& i) {return i == 8; };
    auto predicator9 = [](auto& i) {return i == 9; };
    auto predicator10 = [](auto& i) {return i == 10; };

    auto batch_output1 = batch_recursive_count_if<2>(test_vector2, predicator1, predicator2, predicator3, predicator4, predicator5, predicator6, predicator7, predicator8, predicator9, predicator10);
    std::cout << "Each element in batch_output1: " << '\n';
    for (auto& element : batch_output1)
    {
        std::cout << element << '\n';
    }
    std::cout << '\n';

    // std::deque<std::deque<int>> case
    std::cout << "std::deque<std::deque<int>> case:" << '\n';
    std::deque<int> test_deque;
    test_deque.push_back(1);
    test_deque.push_back(2);
    test_deque.push_back(3);

    std::deque<decltype(test_deque)> test_deque2;
    test_deque2.push_back(test_deque);
    test_deque2.push_back(test_deque);
    test_deque2.push_back(test_deque);

    // use lambda expressions to count the elements which is equal to 1 to 10.
    std::cout << "#number is 1: " << recursive_count_if<2>(test_deque2, predicator1) << '\n';

    auto batch_output2 = batch_recursive_count_if<2>(test_deque2, predicator1, predicator2, predicator3, predicator4, predicator5, predicator6, predicator7, predicator8, predicator9, predicator10);
    std::cout << "Each element in batch_output1: " << '\n';
    for (auto& element : batch_output2)
    {
        std::cout << element << '\n';
    }
    std::cout << '\n';

    // std::vector<std::vector<std::string>> case
    std::cout << "std::vector<std::vector<std::string>>:" << '\n';
    std::vector<std::vector<std::string>> v = { {"hello"}, {"world"} };
    auto is_hello = [](auto& i) { return i == "hello"; };
    auto is_world = [](auto& i) { return i == "world"; };
    std::cout << "recursive_count_if output of is_hello:" << recursive_count_if<2>(v, is_hello) << std::endl;
    auto batch_output3 = batch_recursive_count_if<2>(v, is_hello, is_world);
    std::cout << "Each element in batch_output1: " << '\n';
    for (auto& element : batch_output3)
    {
        std::cout << element << '\n';
    }
    std::cout << '\n';

    //  Count specific std::vector<std::vector<int>> element in std::vector<std::vector<std::vector<int>>> case
    std::cout << "Count specific std::vector<std::vector<int>> element in std::vector<std::vector<std::vector<int>>> case:" << std::endl;
    std::vector<decltype(test_vector2)> test_vector3;
    for (size_t i = 0; i < 3; i++)
    {
        test_vector3.push_back(test_vector2);
    }

    auto batch_output4 = batch_recursive_count_if<1>(test_vector3,
        [test_vector2](auto& element)
        {
            if (element.size() != test_vector2.size())
            {
                return false;
            }
            return std::equal(element.begin(), element.end(), test_vector2.begin());
        },
        [test_vector2](auto& element)
        {
            auto vector_for_comparison = test_vector2;
            vector_for_comparison.push_back(std::vector<int>{12, 2, 3});
            if (element.size() != vector_for_comparison.size())
            {
                return false;
            }
            return std::equal(vector_for_comparison.begin(), vector_for_comparison.end(), vector_for_comparison.begin());
        });
    for (auto& element : batch_output4)
    {
        std::cout << element << '\n';
    }
    std::cout << '\n';
    return;
}

A Godbolt link is here.

All suggestions are welcome.

The summary information:

\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.