Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lukas committed Oct 22, 2021
1 parent 2371f0a commit 4ca3ddd
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 0 deletions.
63 changes: 63 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
cmake_minimum_required(VERSION 3.12)

project(
"UnionFind"
VERSION 0.0.1
DESCRIPTION "A single header c++ library that provides a union find datastructure")


add_library(${PROJECT_NAME} INTERFACE)
# add alias so the project can be uses with add_subdirectory
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})

include(GNUInstallDirs)

# Adding the install interface generator expression makes sure that the include
# files are installed to the proper location (provided by GNUInstallDirs)
target_include_directories(
${PROJECT_NAME}
INTERFACE $<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)

target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_17)

# Tests
option(BUILD_TESTS "build tests" ON)
if (BUILD_TESTS)
include(cmake/gtest.cmake)
enable_testing()
add_subdirectory(test)
endif (BUILD_TESTS)

# locations are provided by GNUInstallDirs
install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}_Targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

include(CMakePackageConfigHelpers)
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)

configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION
${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)

install(EXPORT ${PROJECT_NAME}_Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)

install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)

install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/SI DESTINATION include)

set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")

include(CPack)
Empty file added cmake/UnionFindConfig.cmake.in
Empty file.
41 changes: 41 additions & 0 deletions cmake/gtest.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
include(ExternalProject)
include(GNUInstallDirs)

set(CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DBUILD_STATIC_LIBS=ON
-DBUILD_SHARED_LIBS=OFF
-Dgtest_build_samples=OFF
-Dgtest_build_tests=OFF
-DINSTALL_GTEST=ON
-DBUILD_GMOCK=OFF)

ExternalProject_Add(gtest-project
PREFIX deps/gtest
DOWNLOAD_NAME gtest-1.11.0.tar.gz
DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/downloads
URL https://github.com/google/googletest/archive/release-1.11.0.tar.gz
PATCH_COMMAND cmake -E make_directory <SOURCE_DIR>/win32-deps/include
CMAKE_ARGS ${CMAKE_ARGS}
# Overwtire build and install commands to force Release build on MSVC.
BUILD_COMMAND cmake --build <BINARY_DIR> --config Release
INSTALL_COMMAND cmake --build <BINARY_DIR> --config Release --target install
)


ExternalProject_Get_Property(gtest-project INSTALL_DIR)
add_library(gtest STATIC IMPORTED)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(GTEST_LIBRARY ${INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtestd${CMAKE_STATIC_LIBRARY_SUFFIX})
else()
set(GTEST_LIBRARY ${INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX})
endif ()
set(GTEST_INCLUDE_DIR ${INSTALL_DIR}/include)
file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIR}) # Must exist.
set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${GTEST_LIBRARY})
set_property(TARGET gtest PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GTEST_INCLUDE_DIR})

unset(INSTALL_DIR)
unset(CMAKE_ARGS)
91 changes: 91 additions & 0 deletions include/unionfind/unionfind.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#pragma once

#include <compare>
#include <functional>
#include <numeric>
#include <optional>
#include <vector>

namespace unionfind {

class UnionFind
{
public:
explicit UnionFind(std::size_t size) noexcept
: root_(size),
rank_(size, 1),
number_of_sets_(size)
{
std::iota(std::begin(root_),
std::end(root_),
0);
}

[[nodiscard]] auto find(std::size_t x) noexcept
-> std::optional<std::size_t>
{
if(x >= root_.size()) {
return std::nullopt;
}

if(root_[x] != x) {
return root_[x] = findUnsafe(root_[x]);
}

return x;
}


[[nodiscard]] auto findUnsafe(std::size_t x) noexcept
-> std::size_t
{
if(root_[x] != x) {
return root_[x] = findUnsafe(root_[x]);
}

return x;
}

auto merge(std::size_t x, std::size_t y) noexcept
-> void
{
const auto x_opt = find(x);
const auto y_opt = find(y);

if(!x_opt or !y_opt) {
return;
}

x = x_opt.value();
y = y_opt.value();

if(x == y) {
return;
}

number_of_sets_--;

if(rank_[x] < rank_[y]) {
root_[x] = y;
} else if(rank_[x] > rank_[y]) {
root_[y] = x;
} else {
root_[x] = y;
rank_[y]++;
}
}

[[nodiscard]] auto numberOfSets() const noexcept
-> std::size_t
{
return number_of_sets_;
}


private:
std::vector<std::size_t> root_;
std::vector<std::size_t> rank_;
std::size_t number_of_sets_;
};

} // namespace unionfind
31 changes: 31 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# needed for multithreading
find_package(Threads REQUIRED)

add_executable(unit_tests
main.cpp
tests.cpp
)

target_link_libraries(unit_tests LINK_PRIVATE
gtest
${CMAKE_THREAD_LIBS_INIT})

target_include_directories(
unit_tests PUBLIC
gtest
${NAMEDTYPE_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/../include
)

add_test(
NAME unit_tests
COMMAND
${CMAKE_BINARY_DIR}/test/unit_tests
)


add_dependencies(unit_tests gtest-project)
8 changes: 8 additions & 0 deletions test/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <gtest/gtest.h>

auto main(int argc, char **argv)
-> int
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
89 changes: 89 additions & 0 deletions test/tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//all the includes you want to use before the gtest include
#include <unionfind/unionfind.hpp>

#include <gtest/gtest.h>

TEST(UnionFindTest, UnmergedTest)
{
unionfind::UnionFind uf{5};

EXPECT_EQ(uf.findUnsafe(0), 0);
EXPECT_EQ(uf.findUnsafe(1), 1);
EXPECT_EQ(uf.findUnsafe(2), 2);
EXPECT_EQ(uf.findUnsafe(3), 3);
EXPECT_EQ(uf.findUnsafe(4), 4);

EXPECT_EQ(uf.find(0), 0);
EXPECT_EQ(uf.find(1), 1);
EXPECT_EQ(uf.find(2), 2);
EXPECT_EQ(uf.find(3), 3);
EXPECT_EQ(uf.find(4), 4);

EXPECT_EQ(uf.numberOfSets(), 5);
}

TEST(UnionFindTest, SimpleMergeTest0)
{
unionfind::UnionFind uf{5};

uf.merge(0, 4);

EXPECT_EQ(uf.find(0), 4);
EXPECT_EQ(uf.find(1), 1);
EXPECT_EQ(uf.find(2), 2);
EXPECT_EQ(uf.find(3), 3);
EXPECT_EQ(uf.find(4), 4);

EXPECT_EQ(uf.numberOfSets(), 4);
}

TEST(UnionFindTest, SimpleMergeTest2)
{
unionfind::UnionFind uf{5};

uf.merge(0, 4);
uf.merge(2, 3);

EXPECT_EQ(uf.find(0), 4);
EXPECT_EQ(uf.find(1), 1);
EXPECT_EQ(uf.find(2), 3);
EXPECT_EQ(uf.find(3), 3);
EXPECT_EQ(uf.find(4), 4);

EXPECT_EQ(uf.numberOfSets(), 3);
}

TEST(UnionFindTest, SimpleMergeTest3)
{
unionfind::UnionFind uf{5};

uf.merge(0, 4);
uf.merge(2, 3);
uf.merge(0, 1);

EXPECT_EQ(uf.find(0), 4);
EXPECT_EQ(uf.find(1), 4);
EXPECT_EQ(uf.find(2), 3);
EXPECT_EQ(uf.find(3), 3);
EXPECT_EQ(uf.find(4), 4);

EXPECT_EQ(uf.numberOfSets(), 2);
}

TEST(UnionFindTest, SimpleMergeTest4)
{
unionfind::UnionFind uf{5};

uf.merge(0, 4);
uf.merge(2, 3);
uf.merge(0, 1);
uf.merge(3, 0);

EXPECT_EQ(uf.find(0), 4);
EXPECT_EQ(uf.find(1), 4);
EXPECT_EQ(uf.find(2), 4);
EXPECT_EQ(uf.find(3), 4);
EXPECT_EQ(uf.find(4), 4);

EXPECT_EQ(uf.numberOfSets(), 1);
}

0 comments on commit 4ca3ddd

Please sign in to comment.