From 07bf5221e52c60b7ee98819ad73b2dcb83c861b9 Mon Sep 17 00:00:00 2001 From: James Roy Date: Sun, 27 Jul 2025 10:29:23 +0800 Subject: [PATCH 1/2] sys: Add Disjoint-set data structure Add a set of Disjoint-set functions (`find`, `union`) to handle queries between sets. Signed-off-by: James Roy --- doc/releases/release-notes-4.3.rst | 8 ++++ include/zephyr/sys/set.h | 72 ++++++++++++++++++++++++++++++ lib/utils/set.c | 39 ++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 include/zephyr/sys/set.h create mode 100644 lib/utils/set.c diff --git a/doc/releases/release-notes-4.3.rst b/doc/releases/release-notes-4.3.rst index 21b6da36da7c8..03214c71e8a45 100644 --- a/doc/releases/release-notes-4.3.rst +++ b/doc/releases/release-notes-4.3.rst @@ -65,6 +65,14 @@ New APIs and options .. zephyr-keep-sorted-start re(^\* \w) + +* Utilities + + * :c:struct:`sys_set_node` + * :c:func:`sys_set_makeset` + * :c:func:`sys_set_find` + * :c:func:`sys_set_union` + .. zephyr-keep-sorted-stop New Boards diff --git a/include/zephyr/sys/set.h b/include/zephyr/sys/set.h new file mode 100644 index 0000000000000..43f89987e5372 --- /dev/null +++ b/include/zephyr/sys/set.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 James Roy + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup union_find_apis Disjoint-set + * @ingroup datastructure_apis + * + * @brief Disjoint-set implementation + * + * This implements a disjoint-set (union-find) that guarantees + * O(alpha(n)) runtime for all operations. The algorithms and + * naming are conventional per existing academic and didactic + * implementations, c.f.: + * + * https://en.wikipedia.org/wiki/Disjoint-set_data_structure + * + * @{ + */ + +#ifndef ZEPHYR_INCLUDE_SYS_SET_H_ +#define ZEPHYR_INCLUDE_SYS_SET_H_ + +#include + +/** + * @brief Disjoint-set node structure + */ +struct sys_set_node { + /** @cond INTERNAL_HIDDEN */ + struct sys_set_node *parent; + uint16_t rank; + /** @endcond */ +}; + +/** + * @brief Initialize a disjoint-set. + * + * @param node Pointer to a sys_set_node structure. + */ +static inline void sys_set_makeset(struct sys_set_node *node) +{ + node->parent = node; + node->rank = 0; +} + +/** + * @brief Find the root of the disjoint-set. + * + * @param node Pointer to a sys_set_node structure. + * @return Pointer to the root sys_set_node of the set containing the @p node. + */ +struct sys_set_node *sys_set_find(struct sys_set_node *node); + +/** + * @brief Merge two nodes into the same disjoint-set. + * + * This function attaches the node with the smaller rank to the one with the + * larger rank. That say, if @p node1 has a smaller rank than @p node2, it will + * be linked to @p node2. + * + * @param node1 Pointer to a sys_set_node structure. + * @param node2 Pointer to a sys_set_node structure. + */ +void sys_set_union(struct sys_set_node *node1, struct sys_set_node *node2); + +/** @} */ + +#endif /* ZEPHYR_INCLUDE_SYS_SET_H_ */ diff --git a/lib/utils/set.c b/lib/utils/set.c new file mode 100644 index 0000000000000..9cf2b4ef1b7f9 --- /dev/null +++ b/lib/utils/set.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 James Roy + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +struct sys_set_node *sys_set_find(struct sys_set_node *node) +{ + struct sys_set_node *parent; + + while (node != node->parent) { + parent = node->parent; + node->parent = parent->parent; + node = parent; + } + + return node; +} + +void sys_set_union(struct sys_set_node *node1, struct sys_set_node *node2) +{ + struct sys_set_node *root1 = sys_set_find(node1); + struct sys_set_node *root2 = sys_set_find(node2); + + if (root1 == root2) { + return; + } + + if (root1->rank < root2->rank) { + root1->parent = root2; + } else if (root1->rank > root2->rank) { + root2->parent = root1; + } else { + root2->parent = root1; + root1->rank++; + } +} From e6827c16cfccd519bf4f1760a07fb13b954e3da3 Mon Sep 17 00:00:00 2001 From: James Roy Date: Sun, 27 Jul 2025 10:30:23 +0800 Subject: [PATCH 2/2] tests: sys: Add unit test for Disjoint-set Add unit tests for functions (`sys_set_makeset`, `sys_set_union` and `sys_set_find`). Signed-off-by: James Roy --- tests/lib/data_structure/set/CMakeLists.txt | 8 +++++ tests/lib/data_structure/set/prj.conf | 3 ++ tests/lib/data_structure/set/src/main.c | 37 +++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 tests/lib/data_structure/set/CMakeLists.txt create mode 100644 tests/lib/data_structure/set/prj.conf create mode 100644 tests/lib/data_structure/set/src/main.c diff --git a/tests/lib/data_structure/set/CMakeLists.txt b/tests/lib/data_structure/set/CMakeLists.txt new file mode 100644 index 0000000000000..e436a632ba047 --- /dev/null +++ b/tests/lib/data_structure/set/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(timespec_util) + +FILE(GLOB app_sources src/main.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/lib/data_structure/set/prj.conf b/tests/lib/data_structure/set/prj.conf new file mode 100644 index 0000000000000..496f4f8932a63 --- /dev/null +++ b/tests/lib/data_structure/set/prj.conf @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ZTEST=y diff --git a/tests/lib/data_structure/set/src/main.c b/tests/lib/data_structure/set/src/main.c new file mode 100644 index 0000000000000..0417779c54252 --- /dev/null +++ b/tests/lib/data_structure/set/src/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 James Roy + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +struct user_data { + int n; + struct sys_set_node node; +} data_list[5] = {{.n = 1}, {.n = 3}, {.n = 2}, {.n = 5}, {.n = 4}}; + +ZTEST(set, test_find_and_union) +{ + sys_set_makeset(&data_list[0].node, 5); + sys_set_makeset(&data_list[1].node, 4); + sys_set_makeset(&data_list[2].node, 3); + sys_set_makeset(&data_list[3].node, 2); + sys_set_makeset(&data_list[4].node, 1); + + sys_set_union(&data_list[0].node, &data_list[1].node); + sys_set_union(&data_list[1].node, &data_list[2].node); + sys_set_union(&data_list[2].node, &data_list[3].node); + sys_set_union(&data_list[3].node, &data_list[4].node); + + struct sys_set_node *root_node = sys_set_find(&data_list[4].node); + struct user_data *root_data = CONTAINER_OF(root_node, struct user_data, node); + + zassert_equal(root_data->n, data_list[0].n, "Root set data n are not equal"); + zassert_equal(root_data->node.rank, root_node->rank, "Root set are not equal"); +} + +ZTEST_SUITE(set, NULL, NULL, NULL, NULL, NULL);