From 456009cb1f7c5e9b2906fe9bfdd7d3ccd8395b34 Mon Sep 17 00:00:00 2001 From: mir Date: Thu, 16 Nov 2023 17:30:41 +0100 Subject: [PATCH] Add a sequence container in OAI Add a sequence container in OAI. Includes for_each algorithm, doxygen odcumentation, and test using ctest. --- common/utils/CMakeLists.txt | 1 + common/utils/alg/CMakeLists.txt | 6 + common/utils/alg/find.c | 49 +++++++ common/utils/alg/find.h | 58 ++++++++ common/utils/alg/foreach.c | 40 ++++++ common/utils/alg/foreach.h | 38 +++++ common/utils/ds/CMakeLists.txt | 5 + common/utils/ds/seq_arr.c | 190 +++++++++++++++++++++++++ common/utils/ds/seq_arr.h | 144 +++++++++++++++++++ common/utils/ds/tests/CMakeLists.txt | 4 + common/utils/ds/tests/test_seq_array.c | 90 ++++++++++++ 11 files changed, 625 insertions(+) create mode 100644 common/utils/alg/CMakeLists.txt create mode 100644 common/utils/alg/find.c create mode 100644 common/utils/alg/find.h create mode 100644 common/utils/alg/foreach.c create mode 100644 common/utils/alg/foreach.h create mode 100644 common/utils/ds/seq_arr.c create mode 100644 common/utils/ds/seq_arr.h create mode 100644 common/utils/ds/tests/CMakeLists.txt create mode 100644 common/utils/ds/tests/test_seq_array.c diff --git a/common/utils/CMakeLists.txt b/common/utils/CMakeLists.txt index 8d5989be38..094ca6b10d 100644 --- a/common/utils/CMakeLists.txt +++ b/common/utils/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(alg) add_subdirectory(ds) add_boolean_option(ENABLE_TELNETSRV OFF "Whether to build telnet support in modems" OFF) if(ENABLE_TELNETSRV) diff --git a/common/utils/alg/CMakeLists.txt b/common/utils/alg/CMakeLists.txt new file mode 100644 index 0000000000..0c9f9ce25c --- /dev/null +++ b/common/utils/alg/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(alg OBJECT + find.c + foreach.c +) + +target_include_directories(alg PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/common/utils/alg/find.c b/common/utils/alg/find.c new file mode 100644 index 0000000000..1aea32ab88 --- /dev/null +++ b/common/utils/alg/find.c @@ -0,0 +1,49 @@ +/* +MIT License + +Copyright (c) 2022 Mikel Irazabal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "find.h" +#include +#include +#include + +elm_arr_t find_if_arr_it(seq_arr_t* arr, void* start_it, void* end_it, void* value, bool (*f)(const void*, const void*)) +{ + assert(arr != NULL); + + while (start_it != end_it) { + if (f(value, start_it)) + return (elm_arr_t){.found = true, .it = start_it}; + start_it = seq_arr_next(arr, start_it); + } + // If I trusted the average developer I should return it=start_it, but I don't. + return (elm_arr_t){.found = false, .it = NULL}; +} + +elm_arr_t find_if_arr(seq_arr_t* arr, void* value, bool (*f)(const void*, const void*)) +{ + assert(arr != NULL); + void* start_it = seq_arr_front(arr); + void* end_it = seq_arr_end(arr); + return find_if_arr_it(arr, start_it, end_it, value, f); +} diff --git a/common/utils/alg/find.h b/common/utils/alg/find.h new file mode 100644 index 0000000000..a8acde169d --- /dev/null +++ b/common/utils/alg/find.h @@ -0,0 +1,58 @@ +/* +MIT License + +Copyright (c) 2022 Mikel Irazabal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef FIND_ALGORITHM +#define FIND_ALGORITHM + +#include +#include "../ds/seq_arr.h" + +typedef struct { + void* it; + bool found; +} elm_arr_t; + +// Sequencial containers + +/** + * @brief Find elements in an array if the predicate is true + * @param arr The sequence container + * @param value Pointer to the value that will be used by the predicate + * @param f Function representing the predicate + * @return Whether the predicate was fullfilled and the iterator to the element if true + */ +elm_arr_t find_if_arr(seq_arr_t* arr, void* value, bool (*f)(const void* value, const void* it)); + +/** + * @brief Find elements in an array in the semi-open range [start_it, end_it) + * @param arr The sequence container + * @param start_it Iterator to the first element + * @param end_it Iterator to the one past last element + * @param value Pointer to the value usied in the predicate + * @param f Function representing the predicate + * @return Whether the predicate was fullfilled and the iterator to the element if true + */ +elm_arr_t find_if_arr_it(seq_arr_t* arr, void* start_it, void* end_it, void* value, bool (*f)(const void* value, const void* it)); + +#endif diff --git a/common/utils/alg/foreach.c b/common/utils/alg/foreach.c new file mode 100644 index 0000000000..a520a47736 --- /dev/null +++ b/common/utils/alg/foreach.c @@ -0,0 +1,40 @@ +/* +MIT License + +Copyright (c) 2022 Mikel Irazabal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "foreach.h" + +#include +#include + +void for_each(seq_arr_t* arr, void* value, void (*f)(void* value, void* it)) +{ + assert(arr != NULL); + + void* it = seq_arr_front(arr); + void* end = seq_arr_end(arr); + while (it != end) { + f(value, it); + it = seq_arr_next(arr, it); + } +} diff --git a/common/utils/alg/foreach.h b/common/utils/alg/foreach.h new file mode 100644 index 0000000000..6bcf67b606 --- /dev/null +++ b/common/utils/alg/foreach.h @@ -0,0 +1,38 @@ +/* +MIT License + +Copyright (c) 2022 Mikel Irazabal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef FOR_EACH_ALGORITHM +#define FOR_EACH_ALGORITHM + +#include "../ds/seq_arr.h" + +/** + * @brief Apply function to each element in the sequence container + * @param arr The sequence container + * @param value Pointer to the value that will be used by the function applied + * @param fn_apply Function called by every element in the sequence container + */ +void for_each(seq_arr_t* arr, void* value, void (*fn_apply)(void* value, void* it)); + +#endif diff --git a/common/utils/ds/CMakeLists.txt b/common/utils/ds/CMakeLists.txt index 400d34ad06..a5ae86c52f 100644 --- a/common/utils/ds/CMakeLists.txt +++ b/common/utils/ds/CMakeLists.txt @@ -1,5 +1,10 @@ add_library(ds OBJECT byte_array.c + seq_arr.c ) target_include_directories(ds PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +if(ENABLE_TESTS) + add_subdirectory(tests) +endif() diff --git a/common/utils/ds/seq_arr.c b/common/utils/ds/seq_arr.c new file mode 100644 index 0000000000..708552a24e --- /dev/null +++ b/common/utils/ds/seq_arr.c @@ -0,0 +1,190 @@ +/* +MIT License + +Copyright (c) 2022 Mikel Irazabal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "seq_arr.h" + +#include +#include +#include +#include + +static const size_t MIN_SIZE = 8; + +static void maybe_expand(seq_arr_t* arr) +{ + if (arr->size + 1 == arr->cap) { + arr->data = realloc(arr->data, 2 * arr->cap * arr->elt_size); + assert(arr->data != NULL && "realloc failed to allocate memory"); + arr->cap *= 2; + } +} + +static void maybe_shrink(seq_arr_t* arr) +{ + const float occ = (float)arr->size / (float)arr->cap; + if (arr->size > MIN_SIZE && occ < 0.25) { + assert(arr->cap > MIN_SIZE); + seq_arr_t tmp = {.data = NULL, .size = arr->size, .elt_size = arr->elt_size, .cap = arr->cap / 2}; + tmp.data = calloc(tmp.cap, tmp.cap); + assert(tmp.data != NULL && "Memory exhausted"); + assert(arr->size <= tmp.cap); + memcpy(tmp.data, arr->data, arr->size * arr->elt_size); + free(arr->data); + memcpy(arr, &tmp, sizeof(seq_arr_t)); + } +} + +void seq_arr_init(seq_arr_t* arr, size_t elt_size) //__attribute__(malloc) +{ + assert(arr != NULL); + seq_arr_t tmp = {.data = NULL, .size = 0, .elt_size = elt_size, .cap = MIN_SIZE}; + memcpy(arr, &tmp, sizeof(seq_arr_t)); + arr->data = calloc(arr->cap, elt_size); + assert(arr->data != NULL); +} + +void seq_arr_free(seq_arr_t* arr, void (*free_func)(void*)) +{ + assert(arr != NULL); + assert(arr->data != NULL); + + if (free_func != NULL) { + void* start_it = seq_arr_front(arr); + void* end_it = seq_arr_end(arr); + while (start_it != end_it) { + free_func(start_it); + start_it = seq_arr_next(arr, start_it); + } + } + + free(arr->data); +} + +void seq_arr_push_back(seq_arr_t* arr, void* data, size_t len) +{ + assert(arr != NULL); + assert(arr->data != NULL); + // assert(data != NULL); + assert(len == arr->elt_size); + + maybe_expand(arr); + + const size_t offset = arr->size * arr->elt_size; + memcpy(&arr->data[offset], data, arr->elt_size); + arr->size += 1; +} + +void seq_arr_erase(seq_arr_t* arr, void* start_it) +{ + // start_it must be in the range of arr->data + assert(arr != NULL); + assert(start_it != NULL); + return seq_arr_erase_deep(arr, start_it, NULL); +} + +void seq_arr_erase_deep(seq_arr_t* arr, void* start_it, void (*free_func)(void* it)) +{ + // start_it must be in the range of arr->data + assert(arr != NULL); + assert(start_it != NULL); + return seq_arr_erase_it(arr, start_it, seq_arr_next(arr, start_it), free_func); +} + +void seq_arr_erase_it(seq_arr_t* arr, void* start_it, void* end_it, void (*free_func)(void* it)) +{ + // start_it && end_it must be in the range of arr->data + assert(arr != NULL); + assert(start_it != NULL); + assert(end_it != NULL); + assert(end_it >= start_it); + + if (start_it == end_it) + return; + + if (free_func != NULL) { + void* start_it = seq_arr_front(arr); + void* end_it = seq_arr_end(arr); + while (start_it != end_it) { + free_func(start_it); + start_it = seq_arr_next(arr, start_it); + } + } + + const int num_bytes_move = seq_arr_end(arr) - end_it; + assert(num_bytes_move > -1); + memmove(start_it, end_it, num_bytes_move); + + const int num_bytes_erase = end_it - start_it; + const int32_t num_elm_erase = num_bytes_erase / arr->elt_size; + assert(num_elm_erase > 0); + arr->size -= num_elm_erase; + + memset(seq_arr_end(arr), 0, num_bytes_erase); + + maybe_shrink(arr); +} + +size_t seq_arr_size(seq_arr_t const* arr) +{ + assert(arr != NULL); + return arr->size; +} + +void* seq_arr_front(seq_arr_t const* arr) +{ + assert(arr != NULL); + return arr->data; +} + +void* seq_arr_next(seq_arr_t const* arr, void const* it) +{ + assert(arr != NULL); + assert(it != NULL); + return (uint8_t*)it + arr->elt_size; +} + +void* seq_arr_end(seq_arr_t const* arr) +{ + assert(arr != NULL); + assert(arr->data != NULL); + return &arr->data[arr->size * arr->elt_size]; +} + +void* seq_arr_at(seq_arr_t const* arr, uint32_t pos) +{ + assert(arr != NULL); + assert(arr->data != NULL); + assert(pos < arr->size); + return arr->data + pos * arr->elt_size; +} + +ptrdiff_t seq_arr_dist(seq_arr_t const* arr, void const* first, void const* last) +{ + assert(arr != NULL); + assert(first != NULL); + assert(last != NULL); + const ptrdiff_t pos = (last - first) / arr->elt_size; + assert(pos > -1); + return pos; +} diff --git a/common/utils/ds/seq_arr.h b/common/utils/ds/seq_arr.h new file mode 100644 index 0000000000..2fd049fa34 --- /dev/null +++ b/common/utils/ds/seq_arr.h @@ -0,0 +1,144 @@ +/* +MIT License + +Copyright (c) 2022 Mikel Irazabal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef SEQ_CONTAINER_ARRAY +#define SEQ_CONTAINER_ARRAY + +/* + * Basic sequence container with a similar API and behaviour to C++ std::vector + */ + +#include +#include +#include +#include + +typedef struct seq_arr_s { + uint8_t* data; + size_t size; + const size_t elt_size; + size_t cap; +} seq_arr_t; + +/** + * Init a sequence container, similar to a constructor + * @brief Constructor. + * @param arr The sequence container + * @param elm_sz value returned by the sizeof operator of the type that the container will hold e.g., sizeof(int). + */ +void seq_arr_init(seq_arr_t* arr, size_t elm_sz); + +/** + * Free a sequence container, similar to a destructor + * @brief Destructor. + * @param arr The sequence container + * @param free_func Function called for every element while destructing e.g., useful to free memory of deep objects. + */ +void seq_arr_free(seq_arr_t* arr, void (*free_func)(void* it)); + +/** + * @brief Push back elements into the sequence container. Value semantic. i.e., the void* data will be shallowly copied in the + * array. + * @param arr The sequence container + * @param data Pointer to the data to be copied + * @param elm_sz Size of the element to be copied e.g., sizeof(int) + */ +void seq_arr_push_back(seq_arr_t* arr, void* data, size_t elm_sz); + +/** + * @brief Erase the element pointed by it + * @param arr The sequence container + * @param it Iterator to the element to erase + */ +void seq_arr_erase(seq_arr_t*, void* it); + +/** + * @brief Erase the element pointed by it and call the f function. Useful to free deep copies + * @param arr The sequence container + * @param it Iterator to the element to erase + * @param free_func Function to call while erasing element + */ +void seq_arr_erase_deep(seq_arr_t* arr, void* it, void (*free_func)(void* it)); + +/** + * @brief Erase the elements in the semi-open range [first,last) + * @param arr The sequence container + * @param first Iterator to the first element to erase + * @param last Iterator to the last element. Note that this element will NOT be erased + * @param f Function that will be called by every element while erasing. Useful for deep copies. Pass NULL if shallow + * erased required + */ +void seq_arr_erase_it(seq_arr_t* arr, void* first, void* last, void (*free_func)(void* it)); + +/** + * @brief Elements in the array + * @param arr The sequence container + * @return The number of elements in the sequence container + */ +size_t seq_arr_size(seq_arr_t const* arr); + +///// +// Iterators +///// + +/** + * @brief First iterator + * @param arr The sequence container + * @return The iterator to the first element in the sequence container + */ +void* seq_arr_front(seq_arr_t const* arr); + +/** + * @brief Next iterator + * @param arr The sequence container + * @param it Iterator to a valid element + * @return The iterator to the next element in the sequence container + */ +void* seq_arr_next(seq_arr_t const* arr, void const* it); + +/** + * @brief End iterator + * @param arr The sequence container + * @return The iterator to one past the last element in the sequence container + */ +void* seq_arr_end(seq_arr_t const* arr); + +/** + * @brief Returns iterator in positions pos + * @param arr The sequence container + * @param pos The position of the element in the sequence container + * @return The iterator to the element + */ +void* seq_arr_at(seq_arr_t const* arr, uint32_t pos); + +/** + * @brief Distance between iterators + * @param arr The sequence container + * @param first Iterator to first element + * @param last Iterator to last element + * @return The distance among iterators + */ +ptrdiff_t seq_arr_dist(seq_arr_t const* arr, void const* first, void const* last); + +#endif diff --git a/common/utils/ds/tests/CMakeLists.txt b/common/utils/ds/tests/CMakeLists.txt new file mode 100644 index 0000000000..a3ef512f09 --- /dev/null +++ b/common/utils/ds/tests/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(test_seq_arr test_seq_array.c) +target_link_libraries(test_seq_arr ds alg) +add_dependencies(tests test_seq_arr) +add_test(NAME test_seq_arr COMMAND test_seq_arr) # no options required diff --git a/common/utils/ds/tests/test_seq_array.c b/common/utils/ds/tests/test_seq_array.c new file mode 100644 index 0000000000..c642c883c7 --- /dev/null +++ b/common/utils/ds/tests/test_seq_array.c @@ -0,0 +1,90 @@ +#include "../seq_arr.h" +#include "../../alg/find.h" +#include "../../alg/foreach.h" +#include + +/* + Example to show seq_arr_t capabilities and usage + To compile: gcc test_seq_array.c ../seq_arr.c ../../alg/find.c ../../alg/foreach.c +*/ + +static bool eq_int(const void* value, const void* it) +{ + const int* v = (const int*)value; + const int* i = (const int*)it; + return *v == *i; +} + +static void sum_elm(void* value, void* it) +{ + int* sum = (int*)value; + int* elm = (int*)it; + *sum += *elm; +} + +static int compar(const void* m0, const void* m1) +{ + int* a = (int*)m0; + int* b = (int*)m1; + + if (*a < *b) + return -1; + + if (*a == *b) + return 0; + + return 1; +} + +int main() +{ + seq_arr_t arr = {0}; + seq_arr_init(&arr, sizeof(int)); + + // Insert data and expand + for (int i = 0; i < 100; ++i) + seq_arr_push_back(&arr, &i, sizeof(int)); + + // Check inserted data + assert(seq_arr_size(&arr) == 100); + assert(*(int*)seq_arr_front(&arr) == 0); + assert(*(int*)seq_arr_at(&arr, 25) == 25); + + // Find element in the array + int value = 50; + elm_arr_t elm = find_if_arr(&arr, &value, eq_int); + assert(elm.found == true); + // Check + assert(*(int*)elm.it == 50); + assert(seq_arr_dist(&arr, seq_arr_front(&arr), elm.it) == 50); + + // Apply predicate to all the elements + int sum = 0; + for_each(&arr, &sum, sum_elm); + assert(sum == 4950); // N*(N+1)/2 -> 99*100/2 + + // Erase found element in the array + seq_arr_erase(&arr, elm.it); + // Check + assert(seq_arr_size(&arr) == 99); + assert(*(int*)seq_arr_at(&arr, 50) == 51); + + // Erase range and force shrink + seq_arr_erase_it(&arr, seq_arr_front(&arr), seq_arr_at(&arr, 90), NULL); + assert(seq_arr_size(&arr) == 9); + assert(*(int*)seq_arr_front(&arr) == 91); + + // Recall that C functions qsort and bsearch, also work + int key = 95; + void* base = seq_arr_front(&arr); + size_t nmemb = seq_arr_size(&arr); + size_t size = sizeof(int); + + void* it = bsearch(&key, base, nmemb, size, compar); + assert(seq_arr_dist(&arr, seq_arr_front(&arr), it) == 4); + + // Free data structure + seq_arr_free(&arr, NULL); + + return EXIT_SUCCESS; +}