From b50cb3c9547a63a81c815385e5b289846b9e17ca Mon Sep 17 00:00:00 2001 From: Tobias Hermann Date: Mon, 15 Apr 2024 18:23:18 +0200 Subject: [PATCH] Add div_pos_int_ceil and split_evenly (#299) --- .../curry_instances.autogenerated_defines | 1 + .../fplus/fwd_instances.autogenerated_defines | 2 ++ include/fplus/numeric.hpp | 11 +++++++ include/fplus/split.hpp | 18 +++++++++++ include_all_in_one/include/fplus/fplus.hpp | 32 +++++++++++++++++++ test/numeric_test.cpp | 8 +++++ test/split_test.cpp | 3 ++ 7 files changed, 75 insertions(+) diff --git a/include/fplus/curry_instances.autogenerated_defines b/include/fplus/curry_instances.autogenerated_defines index 27595026..21750540 100644 --- a/include/fplus/curry_instances.autogenerated_defines +++ b/include/fplus/curry_instances.autogenerated_defines @@ -394,6 +394,7 @@ fplus_curry_define_fn_2(insert_at_idx) fplus_curry_define_fn_1(partition) fplus_curry_define_fn_1(split_at_idxs) fplus_curry_define_fn_1(split_every) +fplus_curry_define_fn_1(split_evenly) fplus_curry_define_fn_2(split_by_token) fplus_curry_define_fn_1(run_length_encode_by) fplus_curry_define_fn_0(run_length_encode) diff --git a/include/fplus/fwd_instances.autogenerated_defines b/include/fplus/fwd_instances.autogenerated_defines index 05e1a4bb..635d9c73 100644 --- a/include/fplus/fwd_instances.autogenerated_defines +++ b/include/fplus/fwd_instances.autogenerated_defines @@ -394,6 +394,7 @@ fplus_fwd_define_fn_2(insert_at_idx) fplus_fwd_define_fn_1(partition) fplus_fwd_define_fn_1(split_at_idxs) fplus_fwd_define_fn_1(split_every) +fplus_fwd_define_fn_1(split_evenly) fplus_fwd_define_fn_2(split_by_token) fplus_fwd_define_fn_1(run_length_encode_by) fplus_fwd_define_fn_0(run_length_encode) @@ -693,6 +694,7 @@ fplus_fwd_flip_define_fn_1(split_at_idx) fplus_fwd_flip_define_fn_1(partition) fplus_fwd_flip_define_fn_1(split_at_idxs) fplus_fwd_flip_define_fn_1(split_every) +fplus_fwd_flip_define_fn_1(split_evenly) fplus_fwd_flip_define_fn_1(run_length_encode_by) fplus_fwd_flip_define_fn_1(span) fplus_fwd_flip_define_fn_1(aperture) diff --git a/include/fplus/numeric.hpp b/include/fplus/numeric.hpp index 0c3b6bbf..b82c7040 100644 --- a/include/fplus/numeric.hpp +++ b/include/fplus/numeric.hpp @@ -833,6 +833,17 @@ std::function divide_by(const X& x) }; } +// API search type: div_pos_int_ceil : (a, a) -> a +// Positive integer division, but rounding up instead of down. +// div_pos_int_ceil(5, 3) == 2 +template +static auto div_pos_int_ceil(X numerator, X denominator) +{ + static_assert(std::is_integral::value, "type must be integral"); + static_assert(!std::is_signed::value, "type must be unsigned"); + return numerator / denominator + (numerator % denominator != 0); +} + // API search type: histogram_using_intervals : ([(a, a)], [a]) -> [((a, a), Int)] // fwd bind count: 1 // Generate a histogram of a sequence with given bins. diff --git a/include/fplus/split.hpp b/include/fplus/split.hpp index f1ae816a..772814e9 100644 --- a/include/fplus/split.hpp +++ b/include/fplus/split.hpp @@ -492,6 +492,24 @@ ContainerOut split_every(std::size_t n, const ContainerIn& xs) xs); } +// API search type: split_evenly : (Int, [a]) -> [[a]] +// fwd bind count: 1 +// Split a sequence into n similarly-sized chunks. +// split_evenly(2, [0,1,2,3,4]) == [[0,1,2],[3,4]] +template > +ContainerOut split_evenly(std::size_t n, const ContainerIn& xs) +{ + const std::size_t every_n = div_pos_int_ceil(size_of_cont(xs), n); + return split_at_idxs< + std::vector, + ContainerIn, + ContainerOut>( + numbers_step( + every_n, size_of_cont(xs), every_n), + xs); +} + // API search type: split_by_token : ([a], Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every segment matching a token. diff --git a/include_all_in_one/include/fplus/fplus.hpp b/include_all_in_one/include/fplus/fplus.hpp index 75b05a6c..e00cb68a 100644 --- a/include_all_in_one/include/fplus/fplus.hpp +++ b/include_all_in_one/include/fplus/fplus.hpp @@ -8351,6 +8351,17 @@ std::function divide_by(const X& x) }; } +// API search type: div_pos_int_ceil : (a, a) -> a +// Positive integer division, but rounding up instead of down. +// div_pos_int_ceil(5, 3) == 2 +template +static auto div_pos_int_ceil(X numerator, X denominator) +{ + static_assert(std::is_integral::value, "type must be integral"); + static_assert(!std::is_signed::value, "type must be unsigned"); + return numerator / denominator + (numerator % denominator != 0); +} + // API search type: histogram_using_intervals : ([(a, a)], [a]) -> [((a, a), Int)] // fwd bind count: 1 // Generate a histogram of a sequence with given bins. @@ -10762,6 +10773,24 @@ ContainerOut split_every(std::size_t n, const ContainerIn& xs) xs); } +// API search type: split_evenly : (Int, [a]) -> [[a]] +// fwd bind count: 1 +// Split a sequence into n similarly-sized chunks. +// split_evenly(2, [0,1,2,3,4]) == [[0,1,2],[3,4]] +template > +ContainerOut split_evenly(std::size_t n, const ContainerIn& xs) +{ + const std::size_t every_n = div_pos_int_ceil(size_of_cont(xs), n); + return split_at_idxs< + std::vector, + ContainerIn, + ContainerOut>( + numbers_step( + every_n, size_of_cont(xs), every_n), + xs); +} + // API search type: split_by_token : ([a], Bool, [a]) -> [[a]] // fwd bind count: 2 // Split a sequence at every segment matching a token. @@ -15094,6 +15123,7 @@ fplus_curry_define_fn_2(insert_at_idx) fplus_curry_define_fn_1(partition) fplus_curry_define_fn_1(split_at_idxs) fplus_curry_define_fn_1(split_every) +fplus_curry_define_fn_1(split_evenly) fplus_curry_define_fn_2(split_by_token) fplus_curry_define_fn_1(run_length_encode_by) fplus_curry_define_fn_0(run_length_encode) @@ -15702,6 +15732,7 @@ fplus_fwd_define_fn_2(insert_at_idx) fplus_fwd_define_fn_1(partition) fplus_fwd_define_fn_1(split_at_idxs) fplus_fwd_define_fn_1(split_every) +fplus_fwd_define_fn_1(split_evenly) fplus_fwd_define_fn_2(split_by_token) fplus_fwd_define_fn_1(run_length_encode_by) fplus_fwd_define_fn_0(run_length_encode) @@ -16001,6 +16032,7 @@ fplus_fwd_flip_define_fn_1(split_at_idx) fplus_fwd_flip_define_fn_1(partition) fplus_fwd_flip_define_fn_1(split_at_idxs) fplus_fwd_flip_define_fn_1(split_every) +fplus_fwd_flip_define_fn_1(split_evenly) fplus_fwd_flip_define_fn_1(run_length_encode_by) fplus_fwd_flip_define_fn_1(span) fplus_fwd_flip_define_fn_1(aperture) diff --git a/test/numeric_test.cpp b/test/numeric_test.cpp index 5147a130..db2da7e5 100644 --- a/test/numeric_test.cpp +++ b/test/numeric_test.cpp @@ -286,6 +286,14 @@ TEST_CASE("numeric_test - ceil_to_int_mult") REQUIRE_EQ(ceil_to_int_mult(1, 1), 1); } +TEST_CASE("numeric_test - div_pos_int_ceil") +{ + using namespace fplus; + const std::uint64_t numerator = 5; + const std::uint64_t denominator = 3; + REQUIRE_EQ(div_pos_int_ceil(numerator, denominator), 2); +} + TEST_CASE("numeric_test - reference_interval") { using namespace fplus; diff --git a/test/split_test.cpp b/test/split_test.cpp index 19cd24d7..29983782 100644 --- a/test/split_test.cpp +++ b/test/split_test.cpp @@ -91,6 +91,9 @@ TEST_CASE("split_test - split functions") REQUIRE_EQ(split_at_idx(2, xs), std::make_pair(IntVector({ 1, 2 }), IntVector({ 2, 3, 2 }))); REQUIRE_EQ(partition(is_even_int, xs), std::make_pair(IntVector({ 2, 2, 2 }), IntVector({ 1, 3 }))); REQUIRE_EQ(split_every(2, xs), IntVectors({ { 1, 2 }, { 2, 3 }, { 2 } })); + REQUIRE_EQ(split_evenly(1, xs), IntVectors({ { 1, 2, 2, 3, 2 } })); + REQUIRE_EQ(split_evenly(2, xs), IntVectors({ { 1, 2, 2 }, { 3, 2 } })); + REQUIRE_EQ(split_evenly(3, xs), IntVectors({ { 1, 2 }, { 2, 3 }, { 2 } })); auto splittedAt1And3 = split_at_idxs(IdxVector({ 1, 3 }), xs); IntVectors splittedAt1And3Dest = { IntVector({ 1 }), IntVector({ 2, 2 }), IntVector({ 3, 2 }) }; REQUIRE_EQ(splittedAt1And3, splittedAt1And3Dest);