From 38ff610db099a0111d26fbd5c664ab11f8a5aa64 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 16 Aug 2024 13:27:48 +0100 Subject: [PATCH 01/64] Testing Added and Flattening message buffer --- CMakeLists.txt | 2 +- parallel/parallel_src/CMakeLists.txt | 10 +++ .../parallel_contraction.cpp | 40 ++++++++--- .../parallel_contraction.h | 2 + parallel/parallel_src/tests/CMakeLists.txt | 20 ++++++ .../tests/catch_mpi/catch_mpi_runner.cpp | 21 ++++++ .../parallel_contraction_test.cpp | 69 +++++++++++++++++++ 7 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 parallel/parallel_src/tests/CMakeLists.txt create mode 100644 parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp create mode 100644 parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fa7c7a4..d6dcb248 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ if(CCACHE_PROGRAM) endif() project(KaHIP C CXX) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index 4043b93f..54225e30 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -112,3 +112,13 @@ target_compile_definitions(parhip_interface_static PRIVATE "-DGRAPH_GENERATOR_MP target_include_directories(parhip_interface_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_link_libraries(parhip_interface_static PRIVATE libmodified_kahip_interface) install(TARGETS parhip_interface_static DESTINATION lib) + + +# Temporary Testing +set(ENABLE_TESTING ON) +if (ENABLE_TESTING) + find_package(Catch2 REQUIRED CONFIG) + enable_testing() + message("Building Tests") + add_subdirectory(tests) +endif () diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 83cf67b9..58bf70b1 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -56,6 +56,28 @@ void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicat update_ghost_nodes_weights( communicator, Q ); } +// Flattens Message Buffer so that MPI_AlltoAllv can be used +auto flatten_messages( std::vector> const& messages ) -> std::tuple< std::vector, std::vector, std::vector> { + std::vector flattened_vector; + std::vector offsets; + std::vector lengths; + + int current_offset = 0; + + for (auto const& subvector : messages) { + offsets.push_back(current_offset); + lengths.push_back(static_cast(subvector.size())); + + flattened_vector.insert(flattened_vector.end(), subvector.begin(), subvector.end()); + + current_offset += subvector.size(); + } + + return std::make_tuple(flattened_vector, offsets, lengths); + +} + + void parallel_contraction::compute_label_mapping( MPI_Comm communicator, parallel_graph_access & G, NodeID & global_num_distinct_ids, std::unordered_map< NodeID, NodeID > & label_mapping ) { @@ -63,19 +85,21 @@ void parallel_contraction::compute_label_mapping( MPI_Comm communicator, paralle MPI_Comm_rank( communicator, &rank); MPI_Comm_size( communicator, &size); - NodeID divisor = ceil( G.number_of_global_nodes()/ (double)size); + NodeID divisor = ceil( G.number_of_global_nodes()/ static_cast(size)); helpers helper; + m_messages.clear(); m_messages.resize(size); std::vector< std::unordered_map< NodeID, bool > > filter; filter.resize(size); + forall_local_nodes(G, node) { PEID peID = G.getNodeLabel(node) / divisor; filter[ peID ][G.getNodeLabel(node)] = true; } endfor - for( PEID peID = 0; peID < (PEID) size; peID++) { + for( PEID peID = 0; peID < size; peID++) { std::unordered_map< NodeID, bool >::iterator it; for( it = filter[peID].begin(); it != filter[peID].end(); it++) { m_messages[peID].push_back(it->first); @@ -85,14 +109,14 @@ void parallel_contraction::compute_label_mapping( MPI_Comm communicator, paralle // now flood the network for( PEID peID = 0; peID < size; peID++) { if( peID != rank ) { - if( m_messages[peID].size() == 0 ){ + if( m_messages[peID].empty() ){ m_messages[peID].push_back(std::numeric_limits::max()); } - MPI_Request rq; - MPI_Isend( &m_messages[peID][0], - m_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, + MPI_Request rq; + MPI_Isend( &m_messages[peID][0], + m_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, peID, peID+4*size, communicator, &rq); } } @@ -118,7 +142,7 @@ void parallel_contraction::compute_label_mapping( MPI_Comm communicator, paralle std::vector incmessage; incmessage.resize(message_length); MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+4*size, communicator, &rst); + MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+4*size, communicator, &rst); counter++; PEID peID = st.MPI_SOURCE; diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h index 9d5da7ac..8c85c6b9 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h @@ -46,5 +46,7 @@ class parallel_contraction { std::vector< std::vector< NodeID > > m_send_buffers; // buffers to send messages }; +auto flatten_messages(std::vector> const& messages ) -> std::tuple< std::vector, std::vector, std::vector>; + #endif /* end of include guard: PARALLEL_CONTRACTION_64O127GD */ diff --git a/parallel/parallel_src/tests/CMakeLists.txt b/parallel/parallel_src/tests/CMakeLists.txt new file mode 100644 index 00000000..d467f82b --- /dev/null +++ b/parallel/parallel_src/tests/CMakeLists.txt @@ -0,0 +1,20 @@ +# Automatically enable catch2 to generate ctest targets +list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) +include(CTest) +include(Catch) + +# MPI Version of Catch Test Runner +add_library(catch_mpi_runner catch_mpi/catch_mpi_runner.cpp) +target_link_libraries(catch_mpi_runner PUBLIC MPI::MPI_CXX) +target_link_libraries(catch_mpi_runner PUBLIC Catch2::Catch2) + +# Parallel contraction +add_executable(parallel_contraction_test parallel_contraction/parallel_contraction_test.cpp) +target_link_libraries(parallel_contraction_test PRIVATE Catch2::Catch2WithMain libparallel) +target_link_libraries(parallel_contraction_test PRIVATE libmodified_kahip_interface) + +catch_discover_tests( + parallel_contraction_test + TEST_PREFIX "unit-" + OUTPUT_DIR . + OUTPUT_PREFIX "unit-") diff --git a/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp b/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp new file mode 100644 index 00000000..ee352ea1 --- /dev/null +++ b/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp @@ -0,0 +1,21 @@ +// +// Created by Erich Essmann on 16/08/2024. +// +#pragma once +#include +#include + +int main(int argc, char* argv[]) { + Catch::Session session; // There must be exactly one instance + // writing to session.configData() here sets defaults + // this is the preferred way to set them + int returnCode = session.applyCommandLine(argc, argv); + if (returnCode != 0) // Indicates a command line error + return returnCode; + // global setup... + MPI_Init(&argc, &argv); + int result = session.run(); + // global clean-up... + MPI_Finalize(); + return result; +} \ No newline at end of file diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp new file mode 100644 index 00000000..3a8c36f5 --- /dev/null +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -0,0 +1,69 @@ +// +// Created by Erich Essmann on 16/08/2024. +// +#include +#include +#include +#include + +#include "parallel_contraction_projection/parallel_contraction.h" + +TEST_CASE("flattening vector of messages", "[unit][mpi]") { + SECTION("Empty Vector") { + std::vector > m_empty{}; + auto [flattened, offsets, lengths] = flatten_messages(m_empty); + REQUIRE(flattened.empty()); + REQUIRE(offsets.empty()); + REQUIRE(lengths.empty()); + } + SECTION("Simple Vector") { + std::vector > m_simple{{1, 2, 3, 4}}; + auto [flattened, offsets, lengths] = flatten_messages(m_simple); + + // Testing sizes + REQUIRE(flattened.size() == 4); + REQUIRE(offsets.size() == 1); + REQUIRE(lengths.size() == 1); + + // Testing content + REQUIRE(flattened == m_simple.at(0)); + } + + SECTION("Complex Vector") { + std::vector > data = { + {1, 2, 3}, + {}, + {4, 5}, + {6, 7, 8, 9} + }; + + auto [flattened, offsets, lengths] = flatten_messages(data); + + // Testing sizes + REQUIRE(flattened.size() == 9); + REQUIRE(offsets.size() == 4); + REQUIRE(lengths.size() == 4); + + // Creating Subspans + auto s0 = std::span(flattened); + auto s1 = s0.subspan(offsets[0], lengths[0]) | std::ranges::to >(); + auto s2 = s0.subspan(offsets[1], lengths[1]) | std::ranges::to >(); + auto s3 = s0.subspan(offsets[2], lengths[2]) | std::ranges::to >(); + auto s4 = s0.subspan(offsets[3], lengths[3]) | std::ranges::to >(); + + REQUIRE(s1 == data[0]); + REQUIRE(s2 == data[1]); + REQUIRE(s3 == data[2]); + REQUIRE(s4 == data[3]); + } + + SECTION("Sliced Vector") { + std::vector orig = {1, 2, 3, 1, 2, 3, 3, 3, 1, 2, 3}; + auto fun = std::ranges::less{}; + auto sliced = orig | std::views::chunk_by(fun) | std::ranges::to > >(); + + auto [flattened, offsets, lengths] = flatten_messages(sliced); + REQUIRE(flattened.size() == orig.size()); + REQUIRE(orig == flattened); + } +} From f1c5ea4b3b2b5d99664d55e3908d2fd08b918df4 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 16 Aug 2024 16:55:47 +0100 Subject: [PATCH 02/64] MPI AlltoAll used for communicating node_ids --- .../lib/communication/mpi_tools.h | 132 +++++++++--- .../parallel_contraction.cpp | 192 ++++++++++++++++-- .../parallel_contraction.h | 9 +- parallel/parallel_src/tests/CMakeLists.txt | 2 +- .../parallel_contraction_mpi_test.cpp | 3 + .../parallel_contraction_test.cpp | 10 +- 6 files changed, 296 insertions(+), 52 deletions(-) create mode 100644 parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 53330def..71a9d52f 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -6,7 +6,6 @@ *****************************************************************************/ - #ifndef MPI_TOOLS_HMESDXF2 #define MPI_TOOLS_HMESDXF2 @@ -15,38 +14,119 @@ class mpi_tools { public: - mpi_tools(); - virtual ~mpi_tools(); - - void collect_and_write_labels( MPI_Comm communicator, PPartitionConfig & config, - parallel_graph_access & G); + mpi_tools(); - void collect_parallel_graph_to_local_graph( MPI_Comm communicator, - PPartitionConfig & config, - parallel_graph_access & G, - complete_graph_access & Q); + virtual ~mpi_tools(); - // G is input (only on ROOT) - // G is output (on every other PE) - void distribute_local_graph( MPI_Comm communicator, PPartitionConfig & config, complete_graph_access & G); + void collect_and_write_labels(MPI_Comm communicator, PPartitionConfig &config, + parallel_graph_access &G); - // alltoallv that can send more than int-count elements - void alltoallv( void * sendbuf, - ULONG sendcounts[], ULONG displs[], - const MPI_Datatype & sendtype, void * recvbuf, - ULONG recvcounts[], ULONG rdispls[], - const MPI_Datatype & recvtype ) { - alltoallv( sendbuf, sendcounts, displs, sendtype, recvbuf, recvcounts, rdispls, recvtype, MPI_COMM_WORLD); - }; + void collect_parallel_graph_to_local_graph(MPI_Comm communicator, + PPartitionConfig &config, + parallel_graph_access &G, + complete_graph_access &Q); - void alltoallv( void * sendbuf, - ULONG sendcounts[], ULONG displs[], - const MPI_Datatype & sendtype, void * recvbuf, - ULONG recvcounts[], ULONG rdispls[], - const MPI_Datatype & recvtype, MPI_Comm communicator ); + // G is input (only on ROOT) + // G is output (on every other PE) + void distribute_local_graph(MPI_Comm communicator, PPartitionConfig &config, complete_graph_access &G); + // alltoallv that can send more than int-count elements + void alltoallv(void *sendbuf, + ULONG sendcounts[], ULONG displs[], + const MPI_Datatype &sendtype, void *recvbuf, + ULONG recvcounts[], ULONG rdispls[], + const MPI_Datatype &recvtype) { + alltoallv(sendbuf, sendcounts, displs, sendtype, recvbuf, recvcounts, rdispls, recvtype, MPI_COMM_WORLD); + }; + void alltoallv(void *sendbuf, + ULONG sendcounts[], ULONG displs[], + const MPI_Datatype &sendtype, void *recvbuf, + ULONG recvcounts[], ULONG rdispls[], + const MPI_Datatype &recvtype, MPI_Comm communicator); }; +namespace collective_tools { + template + concept Container = requires(ContainerType a, const ContainerType b) + { + requires std::regular; + requires std::swappable; + requires std::destructible; + requires std::same_as; + requires std::same_as; + requires std::forward_iterator; + requires std::forward_iterator; + requires std::signed_integral; + requires std::same_as::difference_type>; + requires std::same_as::difference_type>; + { a.begin() } -> std::same_as; + { a.end() } -> std::same_as; + { b.begin() } -> std::same_as; + { b.end() } -> std::same_as; + { a.cbegin() } -> std::same_as; + { a.cend() } -> std::same_as; + { a.size() } -> std::same_as; + { a.max_size() } -> std::same_as; + { a.empty() } -> std::same_as; + }; + + template + struct mpi_packed_message { + std::vector packed_message; + std::vector offsets; + std::vector lengths; + }; + + // Packs message buffer so that MPI_AlltoAllv can be used + template + requires Container + auto pack_messages( + Input const& messages) -> mpi_packed_message { + using inner = typename Input::value_type; + using element_type = typename inner::value_type; + + std::vector flattened_vector{}; + std::vector offsets{}; + std::vector lengths{}; + + int current_offset = 0; + + for (auto const &subvector: messages) { + offsets.push_back(current_offset); + lengths.push_back(static_cast(subvector.size())); + + flattened_vector.insert(flattened_vector.end(), subvector.begin(), subvector.end()); + + current_offset += static_cast(subvector.size()); + } + + return mpi_packed_message{flattened_vector, offsets, lengths}; + } + + // Unpacks received message buffer received from alltoall + template + auto unpack_messages(mpi_packed_message const& packed_message) -> std::vector> { + auto const& [recv_buf, recv_displs, recv_counts] = packed_message; + int num_ranks = static_cast(recv_counts.size()); + std::vector > result(num_ranks); + + for (int i = 0; i < num_ranks; ++i) { + int const count = recv_counts.at(i); + int const offset = recv_displs.at(i); + + // Extract the subvector corresponding to rank `i` + std::vector subvector(recv_buf.begin() + offset, recv_buf.begin() + offset + count); + + // Store it in the result vector at index `i` + result[i] = std::move(subvector); + } + + return result; + } +} + #endif /* end of include guard: MPI_TOOLS_HMESDXF2 */ diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 58bf70b1..ac45aba5 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -6,16 +6,21 @@ *****************************************************************************/ #include "parallel_contraction.h" + +#include + #include "data_structure/hashed_graph.h" #include "tools/helpers.h" -parallel_contraction::parallel_contraction() { - -} +#define CONTRACTION true -parallel_contraction::~parallel_contraction() { - -} +#ifndef CONTRACTION +#define CONTRACTION false +#endif + +parallel_contraction::parallel_contraction() = default; + +parallel_contraction::~parallel_contraction() = default; void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G, @@ -56,29 +61,164 @@ void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicat update_ghost_nodes_weights( communicator, Q ); } -// Flattens Message Buffer so that MPI_AlltoAllv can be used -auto flatten_messages( std::vector> const& messages ) -> std::tuple< std::vector, std::vector, std::vector> { - std::vector flattened_vector; - std::vector offsets; - std::vector lengths; +// MPI AlltoAll based implementation +void parallel_contraction::compute_label_mapping_collective( MPI_Comm communicator, parallel_graph_access & G, + NodeID & global_num_distinct_ids, + std::unordered_map< NodeID, NodeID > & label_mapping ) { + PEID rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); - int current_offset = 0; + NodeID divisor = ceil( G.number_of_global_nodes()/ static_cast(size)); + + helpers helper; + m_messages.clear(); + m_messages.resize(size); - for (auto const& subvector : messages) { - offsets.push_back(current_offset); - lengths.push_back(static_cast(subvector.size())); + std::vector< std::unordered_map< NodeID, bool > > filter; + filter.resize(size); - flattened_vector.insert(flattened_vector.end(), subvector.begin(), subvector.end()); + forall_local_nodes(G, node) { + PEID peID = G.getNodeLabel(node) / divisor; + filter[ peID ][G.getNodeLabel(node)] = true; + } endfor - current_offset += subvector.size(); + for( PEID peID = 0; peID < size; peID++) { + std::unordered_map< NodeID, bool >::iterator it; + for( it = filter[peID].begin(); it != filter[peID].end(); it++) { + m_messages[peID].push_back(it->first); + } } - return std::make_tuple(flattened_vector, offsets, lengths); + // Packing messages into vector and computing offsets and lengths for the sub messages + auto [send_packed_messages, send_offsets, send_lengths] = collective_tools::pack_messages(m_messages); -} + // Preparing receive buffers for the node ids, offsets, and lengths + int total_message_size = 0; + int local_message_size = static_cast(send_packed_messages.size()); + MPI_Allreduce(&local_message_size, &total_message_size, 1, MPI_INT, MPI_SUM, communicator); + + auto recv_packed_messages = std::vector(total_message_size); + auto recv_offsets = std::vector(size); + auto recv_lengths = std::vector(size); + + auto mpi_error = MPI_Alltoallv(send_packed_messages.data(), send_lengths.data(), send_offsets.data(), MPI_UNSIGNED_LONG_LONG, recv_packed_messages.data(), recv_lengths.data(), recv_offsets.data(), MPI_UNSIGNED_LONG_LONG, communicator); + if(mpi_error != MPI_SUCCESS) { + MPI_Abort(communicator, mpi_error); + } + + auto local_labels_byPE = collective_tools::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); + + std::vector< std::vector< NodeID > > inc_messages; + inc_messages.resize(size); + + PEID counter = 0; + + std::vector< NodeID > local_labels; + for( PEID peID = 0; peID < size; peID++) { + for( ULONG i = 0; i < local_labels_byPE[peID].size(); i++) { + local_labels.push_back(local_labels_byPE[peID][i]); + } + } + + + // filter duplicates locally + helper.filter_duplicates( local_labels, + [](const NodeID & lhs, const NodeID & rhs) -> bool { + return (lhs < rhs); + }, + [](const NodeID & lhs, const NodeID & rhs) -> bool { + return (lhs == rhs); + }); + //afterwards they are sorted! + + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%Labels are unique on all PEs%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // now counting + + NodeID local_num_labels = local_labels.size(); + NodeID prefix_sum = 0; + + MPI_Scan(&local_num_labels, &prefix_sum, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + + global_num_distinct_ids = prefix_sum; + // Broadcast global number of ids + MPI_Bcast(&global_num_distinct_ids, 1, MPI_UNSIGNED_LONG_LONG, size-1, communicator); + + NodeID num_smaller_ids = prefix_sum - local_num_labels; + + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%Now Build the mapping and send information back to PEs%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + // build the mapping locally + std::unordered_map< NodeID, NodeID > label_mapping_to_cnode; + NodeID cur_id = num_smaller_ids; + for( ULONG i = 0; i < local_labels.size(); i++) { + label_mapping_to_cnode[local_labels[i]] = cur_id++; + } + + // now send the processes the mapping back + //std::vector< std::vector< NodeID > > m_out_messages; + m_out_messages.resize(size); + + for( PEID peID = 0; peID < (PEID)size; peID++) { + if( peID == rank ) continue; + if( inc_messages[peID][0] == std::numeric_limits::max()) { + m_out_messages[peID].push_back(std::numeric_limits::max()); + continue; + } -void parallel_contraction::compute_label_mapping( MPI_Comm communicator, parallel_graph_access & G, + for( ULONG i = 0; i < inc_messages[peID].size(); i++) { + m_out_messages[peID].push_back( label_mapping_to_cnode[ inc_messages[peID][i] ] ); + } + } + + for( PEID peID = 0; peID < size; peID++) { + if( peID != rank ) { + MPI_Request rq; + MPI_Isend( &m_out_messages[peID][0], + m_out_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, + peID, peID+5*size, communicator, &rq); + } + } + + // first the local labels + for( ULONG i = 0; i < m_messages[rank].size(); i++) { + label_mapping[ m_messages[rank][i] ] = label_mapping_to_cnode[m_messages[rank][i]]; + } + + counter = 0; + while( counter < size - 1) { + // wait for incomming message of an adjacent processor + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, rank+5*size, communicator, &st); + + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector incmessage; incmessage.resize(message_length); + + MPI_Status rst; + MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+5*size, communicator, &rst); + counter++; + + // now integrate the changes + if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do + + PEID peID = st.MPI_SOURCE; + for( int i = 0; i < message_length; i++) { + label_mapping[ m_messages[peID][i] ] = incmessage[i]; + } + } +} + +// Original Implementation +void parallel_contraction::compute_label_mapping_original( MPI_Comm communicator, parallel_graph_access & G, NodeID & global_num_distinct_ids, std::unordered_map< NodeID, NodeID > & label_mapping ) { PEID rank, size; @@ -262,6 +402,18 @@ void parallel_contraction::compute_label_mapping( MPI_Comm communicator, paralle } +// Testing Switch +void parallel_contraction::compute_label_mapping( MPI_Comm communicator, parallel_graph_access & G, + NodeID & global_num_distinct_ids, + std::unordered_map< NodeID, NodeID > & label_mapping ) { + if constexpr (CONTRACTION == true) { + parallel_contraction::compute_label_mapping_collective(communicator, G, global_num_distinct_ids, label_mapping); + } else { + parallel_contraction::compute_label_mapping_original(communicator, G, global_num_distinct_ids, label_mapping); + } + +}; + void parallel_contraction::get_nodes_to_cnodes_ghost_nodes( MPI_Comm communicator, parallel_graph_access & G ) { PEID rank, size; MPI_Comm_rank( communicator, &rank); diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h index 8c85c6b9..acb502a5 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h @@ -21,6 +21,13 @@ class parallel_contraction { parallel_graph_access & G, parallel_graph_access & Q); private: + // Temporary Duplicated function + void compute_label_mapping_collective( MPI_Comm communicator, parallel_graph_access & G, + NodeID & global_num_distinct_ids, + std::unordered_map< NodeID, NodeID > & label_mapping); + void compute_label_mapping_original( MPI_Comm communicator, parallel_graph_access & G, + NodeID & global_num_distinct_ids, + std::unordered_map< NodeID, NodeID > & label_mapping); // compute mapping of labels id into contiguous intervall [0, ...., num_lables) void compute_label_mapping( MPI_Comm communicator, parallel_graph_access & G, NodeID & global_num_distinct_ids, @@ -46,7 +53,7 @@ class parallel_contraction { std::vector< std::vector< NodeID > > m_send_buffers; // buffers to send messages }; -auto flatten_messages(std::vector> const& messages ) -> std::tuple< std::vector, std::vector, std::vector>; +auto flatten_messages(std::vector> const& messages ) -> std::tuple< std::vector, std::vector, std::vector>; #endif /* end of include guard: PARALLEL_CONTRACTION_64O127GD */ diff --git a/parallel/parallel_src/tests/CMakeLists.txt b/parallel/parallel_src/tests/CMakeLists.txt index d467f82b..f729557a 100644 --- a/parallel/parallel_src/tests/CMakeLists.txt +++ b/parallel/parallel_src/tests/CMakeLists.txt @@ -17,4 +17,4 @@ catch_discover_tests( parallel_contraction_test TEST_PREFIX "unit-" OUTPUT_DIR . - OUTPUT_PREFIX "unit-") + OUTPUT_PREFIX "unit-") \ No newline at end of file diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp new file mode 100644 index 00000000..91b7a475 --- /dev/null +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -0,0 +1,3 @@ +// +// Created by Erich Essmann on 16/08/2024. +// diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index 3a8c36f5..32712635 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -8,17 +8,19 @@ #include "parallel_contraction_projection/parallel_contraction.h" +#include + TEST_CASE("flattening vector of messages", "[unit][mpi]") { SECTION("Empty Vector") { std::vector > m_empty{}; - auto [flattened, offsets, lengths] = flatten_messages(m_empty); + auto [flattened, offsets, lengths] = collective_tools::pack_messages(m_empty); REQUIRE(flattened.empty()); REQUIRE(offsets.empty()); REQUIRE(lengths.empty()); } SECTION("Simple Vector") { std::vector > m_simple{{1, 2, 3, 4}}; - auto [flattened, offsets, lengths] = flatten_messages(m_simple); + auto [flattened, offsets, lengths] = collective_tools::pack_messages(m_simple); // Testing sizes REQUIRE(flattened.size() == 4); @@ -37,7 +39,7 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { {6, 7, 8, 9} }; - auto [flattened, offsets, lengths] = flatten_messages(data); + auto [flattened, offsets, lengths] = collective_tools::pack_messages(data); // Testing sizes REQUIRE(flattened.size() == 9); @@ -62,7 +64,7 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { auto fun = std::ranges::less{}; auto sliced = orig | std::views::chunk_by(fun) | std::ranges::to > >(); - auto [flattened, offsets, lengths] = flatten_messages(sliced); + auto [flattened, offsets, lengths] = collective_tools::pack_messages(sliced); REQUIRE(flattened.size() == orig.size()); REQUIRE(orig == flattened); } From 99a09902aa47f2376d937f1a64ae3669feaa210b Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 19 Aug 2024 12:47:53 +0100 Subject: [PATCH 03/64] Initial implementation --- .../lib/communication/mpi_tools.h | 38 ++++- .../parallel_contraction.cpp | 145 +++++++----------- parallel/parallel_src/tests/CMakeLists.txt | 18 ++- .../tests/catch_mpi/catch_mpi_runner.cpp | 1 - .../parallel_contraction_mpi_test.cpp | 14 ++ .../parallel_contraction_test.cpp | 8 +- 6 files changed, 125 insertions(+), 99 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 71a9d52f..25f59710 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -46,7 +46,7 @@ class mpi_tools { const MPI_Datatype &recvtype, MPI_Comm communicator); }; -namespace collective_tools { +namespace mpi_collective_tools { template concept Container = requires(ContainerType a, const ContainerType b) { @@ -84,7 +84,7 @@ namespace collective_tools { template requires Container auto pack_messages( - Input const& messages) -> mpi_packed_message { + Input const &messages) -> mpi_packed_message { using inner = typename Input::value_type; using element_type = typename inner::value_type; @@ -108,8 +108,8 @@ namespace collective_tools { // Unpacks received message buffer received from alltoall template - auto unpack_messages(mpi_packed_message const& packed_message) -> std::vector> { - auto const& [recv_buf, recv_displs, recv_counts] = packed_message; + auto unpack_messages(mpi_packed_message const &packed_message) -> std::vector > { + auto const &[recv_buf, recv_displs, recv_counts] = packed_message; int num_ranks = static_cast(recv_counts.size()); std::vector > result(num_ranks); @@ -126,6 +126,36 @@ namespace collective_tools { return result; } + + template + requires Container + auto all_to_all(Input const &sends, MPI_Comm communicator) { + using inner = typename Input::value_type; + using element_type = typename inner::value_type; + + PEID rank, size; + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); + + // Packing messages into vector and computing offsets and lengths for the sub messages + auto [send_packed_messages, send_offsets, send_lengths] = mpi_collective_tools::pack_messages(sends); + + // Preparing receive buffers for the node ids, offsets, and lengths + int total_message_size = 0; + const int local_message_size = static_cast(send_packed_messages.size()); + MPI_Allreduce(&local_message_size, &total_message_size, 1, MPI_INT, MPI_SUM, communicator); + + auto recv_packed_messages = std::vector(total_message_size); + auto recv_offsets = std::vector(size); + auto recv_lengths = std::vector(size); + + auto mpi_error = MPI_Alltoallv(send_packed_messages.data(), send_lengths.data(), send_offsets.data(), + MPI_UNSIGNED_LONG_LONG, recv_packed_messages.data(), recv_lengths.data(), + recv_offsets.data(), MPI_UNSIGNED_LONG_LONG, + communicator); + + return mpi_collective_tools::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); + } } diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index ac45aba5..26f619e4 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -62,74 +62,54 @@ void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicat } // MPI AlltoAll based implementation -void parallel_contraction::compute_label_mapping_collective( MPI_Comm communicator, parallel_graph_access & G, - NodeID & global_num_distinct_ids, - std::unordered_map< NodeID, NodeID > & label_mapping ) { +void parallel_contraction::compute_label_mapping_collective(MPI_Comm communicator, parallel_graph_access &G, + NodeID &global_num_distinct_ids, + std::unordered_map &label_mapping) { PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); - NodeID divisor = ceil( G.number_of_global_nodes()/ static_cast(size)); + NodeID divisor = ceil(G.number_of_global_nodes() / static_cast(size)); helpers helper; m_messages.clear(); m_messages.resize(size); - std::vector< std::unordered_map< NodeID, bool > > filter; + std::vector > filter; filter.resize(size); - forall_local_nodes(G, node) { - PEID peID = G.getNodeLabel(node) / divisor; - filter[ peID ][G.getNodeLabel(node)] = true; - } endfor - - for( PEID peID = 0; peID < size; peID++) { - std::unordered_map< NodeID, bool >::iterator it; - for( it = filter[peID].begin(); it != filter[peID].end(); it++) { - m_messages[peID].push_back(it->first); + forall_local_nodes(G, node) + { + PEID peID = G.getNodeLabel(node) / divisor; + filter.at(peID).at(G.getNodeLabel(node)) = true; } - } - - // Packing messages into vector and computing offsets and lengths for the sub messages - auto [send_packed_messages, send_offsets, send_lengths] = collective_tools::pack_messages(m_messages); + endfor - // Preparing receive buffers for the node ids, offsets, and lengths - int total_message_size = 0; - int local_message_size = static_cast(send_packed_messages.size()); - MPI_Allreduce(&local_message_size, &total_message_size, 1, MPI_INT, MPI_SUM, communicator); - - auto recv_packed_messages = std::vector(total_message_size); - auto recv_offsets = std::vector(size); - auto recv_lengths = std::vector(size); - - auto mpi_error = MPI_Alltoallv(send_packed_messages.data(), send_lengths.data(), send_offsets.data(), MPI_UNSIGNED_LONG_LONG, recv_packed_messages.data(), recv_lengths.data(), recv_offsets.data(), MPI_UNSIGNED_LONG_LONG, communicator); - if(mpi_error != MPI_SUCCESS) { - MPI_Abort(communicator, mpi_error); + for (PEID peID = 0; peID < size; peID++) { + std::unordered_map::iterator it; + for (it = filter.at(peID).begin(); it != filter.at(peID).end(); ++it) { + m_messages.at(peID).push_back(it->first); + } } - auto local_labels_byPE = collective_tools::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); + auto const local_labels_byPE = mpi_collective_tools::all_to_all(m_messages, communicator); - std::vector< std::vector< NodeID > > inc_messages; - inc_messages.resize(size); - - PEID counter = 0; - - std::vector< NodeID > local_labels; - for( PEID peID = 0; peID < size; peID++) { - for( ULONG i = 0; i < local_labels_byPE[peID].size(); i++) { - local_labels.push_back(local_labels_byPE[peID][i]); + std::vector local_labels; + for (PEID peID = 0; peID < size; peID++) { + for (ULONG i = 0; i < local_labels_byPE.at(peID).size(); i++) { + local_labels.push_back(local_labels_byPE.at(peID).at(i)); } } // filter duplicates locally - helper.filter_duplicates( local_labels, - [](const NodeID & lhs, const NodeID & rhs) -> bool { - return (lhs < rhs); - }, - [](const NodeID & lhs, const NodeID & rhs) -> bool { - return (lhs == rhs); - }); + helper.filter_duplicates(local_labels, + [](const NodeID &lhs, const NodeID &rhs) -> bool { + return (lhs < rhs); + }, + [](const NodeID &lhs, const NodeID &rhs) -> bool { + return (lhs == rhs); + }); //afterwards they are sorted! // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -138,14 +118,14 @@ void parallel_contraction::compute_label_mapping_collective( MPI_Comm communicat // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // now counting - NodeID local_num_labels = local_labels.size(); - NodeID prefix_sum = 0; + NodeID local_num_labels = local_labels.size(); + NodeID prefix_sum = 0; MPI_Scan(&local_num_labels, &prefix_sum, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); global_num_distinct_ids = prefix_sum; // Broadcast global number of ids - MPI_Bcast(&global_num_distinct_ids, 1, MPI_UNSIGNED_LONG_LONG, size-1, communicator); + MPI_Bcast(&global_num_distinct_ids, 1, MPI_UNSIGNED_LONG_LONG, size - 1, communicator); NodeID num_smaller_ids = prefix_sum - local_num_labels; @@ -155,64 +135,51 @@ void parallel_contraction::compute_label_mapping_collective( MPI_Comm communicat // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // build the mapping locally - std::unordered_map< NodeID, NodeID > label_mapping_to_cnode; + std::unordered_map label_mapping_to_cnode; NodeID cur_id = num_smaller_ids; - for( ULONG i = 0; i < local_labels.size(); i++) { - label_mapping_to_cnode[local_labels[i]] = cur_id++; + for (ULONG i = 0; i < local_labels.size(); i++) { + label_mapping_to_cnode.at(local_labels.at(i)) = cur_id++; } // now send the processes the mapping back //std::vector< std::vector< NodeID > > m_out_messages; + m_out_messages.clear(); m_out_messages.resize(size); - for( PEID peID = 0; peID < (PEID)size; peID++) { - if( peID == rank ) continue; + for (PEID peID = 0; peID < size; peID++) { + if (peID == rank) continue; - if( inc_messages[peID][0] == std::numeric_limits::max()) { - m_out_messages[peID].push_back(std::numeric_limits::max()); + if (local_labels_byPE.at(peID).empty()) { + m_out_messages.at(peID) = {}; continue; } - for( ULONG i = 0; i < inc_messages[peID].size(); i++) { - m_out_messages[peID].push_back( label_mapping_to_cnode[ inc_messages[peID][i] ] ); + for (ULONG i = 0; i < local_labels_byPE[peID].size(); i++) { + m_out_messages.at(peID).push_back(label_mapping_to_cnode.at(local_labels_byPE.at(peID).at(i))); } } - for( PEID peID = 0; peID < size; peID++) { - if( peID != rank ) { + for (PEID peID = 0; peID < size; peID++) { + if (peID != rank) { MPI_Request rq; - MPI_Isend( &m_out_messages[peID][0], - m_out_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID+5*size, communicator, &rq); + MPI_Isend(&m_out_messages[peID][0], + m_out_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, + peID, peID + 5 * size, communicator, &rq); } } - // first the local labels - for( ULONG i = 0; i < m_messages[rank].size(); i++) { - label_mapping[ m_messages[rank][i] ] = label_mapping_to_cnode[m_messages[rank][i]]; - } - - counter = 0; - while( counter < size - 1) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+5*size, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector incmessage; incmessage.resize(message_length); + auto recv_mapping = mpi_collective_tools::all_to_all(m_out_messages, communicator); - MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+5*size, communicator, &rst); - counter++; - // now integrate the changes - if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do + // first the local labels + for (ULONG i = 0; i < m_messages.at(rank).size(); i++) { + label_mapping[m_messages.at(rank).at(i)] = label_mapping_to_cnode.at(m_messages.at(rank).at(i)); + } - PEID peID = st.MPI_SOURCE; - for( int i = 0; i < message_length; i++) { - label_mapping[ m_messages[peID][i] ] = incmessage[i]; + for (PEID peID = 0; peID < size; peID++) { + for (ULONG i = 0; i < recv_mapping.at(peID).size(); i++) { + label_mapping[m_messages.at(peID).at(i)] = recv_mapping.at(peID).at(i); } } } diff --git a/parallel/parallel_src/tests/CMakeLists.txt b/parallel/parallel_src/tests/CMakeLists.txt index f729557a..0fe4e3a6 100644 --- a/parallel/parallel_src/tests/CMakeLists.txt +++ b/parallel/parallel_src/tests/CMakeLists.txt @@ -17,4 +17,20 @@ catch_discover_tests( parallel_contraction_test TEST_PREFIX "unit-" OUTPUT_DIR . - OUTPUT_PREFIX "unit-") \ No newline at end of file + OUTPUT_PREFIX "unit-") + + +# Parallel contraction +add_executable(parallel_contraction_mpi_test parallel_contraction/parallel_contraction_mpi_test.cpp) +target_link_libraries(parallel_contraction_mpi_test PRIVATE catch_mpi_runner) +target_link_libraries(parallel_contraction_mpi_test PRIVATE libparallel) +target_link_libraries(parallel_contraction_mpi_test PRIVATE libmodified_kahip_interface) + +catch_discover_tests( + parallel_contraction_mpi_test + TEST_PREFIX "unit-" + PROPERTIES crosscompiling_emulator "mpirun -n 4" + REPORTER xml + OUTPUT_DIR . + OUTPUT_PREFIX "unit-" + OUTPUT_SUFFIX .xml) diff --git a/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp b/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp index ee352ea1..b2cb9e4c 100644 --- a/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp +++ b/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp @@ -1,7 +1,6 @@ // // Created by Erich Essmann on 16/08/2024. // -#pragma once #include #include diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index 91b7a475..4308fa25 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -1,3 +1,17 @@ // // Created by Erich Essmann on 16/08/2024. // +#include +#include +#include +#include +#include + + +TEST_CASE("all to all vector of vectos", "[unit][mpi]") { + SECTION("emtpy case") { + const std::vector> v_empty{{1}}; + auto vec = mpi_collective_tools::all_to_all(v_empty, MPI_COMM_WORLD); + REQUIRE(vec.empty()); + } +} \ No newline at end of file diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index 32712635..5f78518a 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -13,14 +13,14 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { SECTION("Empty Vector") { std::vector > m_empty{}; - auto [flattened, offsets, lengths] = collective_tools::pack_messages(m_empty); + auto [flattened, offsets, lengths] = mpi_collective_tools::pack_messages(m_empty); REQUIRE(flattened.empty()); REQUIRE(offsets.empty()); REQUIRE(lengths.empty()); } SECTION("Simple Vector") { std::vector > m_simple{{1, 2, 3, 4}}; - auto [flattened, offsets, lengths] = collective_tools::pack_messages(m_simple); + auto [flattened, offsets, lengths] = mpi_collective_tools::pack_messages(m_simple); // Testing sizes REQUIRE(flattened.size() == 4); @@ -39,7 +39,7 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { {6, 7, 8, 9} }; - auto [flattened, offsets, lengths] = collective_tools::pack_messages(data); + auto [flattened, offsets, lengths] = mpi_collective_tools::pack_messages(data); // Testing sizes REQUIRE(flattened.size() == 9); @@ -64,7 +64,7 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { auto fun = std::ranges::less{}; auto sliced = orig | std::views::chunk_by(fun) | std::ranges::to > >(); - auto [flattened, offsets, lengths] = collective_tools::pack_messages(sliced); + auto [flattened, offsets, lengths] = mpi_collective_tools::pack_messages(sliced); REQUIRE(flattened.size() == orig.size()); REQUIRE(orig == flattened); } From b2a497a97c9bd3e608879ca465292bbc5dc02fc4 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 2 Sep 2024 14:03:42 +0100 Subject: [PATCH 04/64] Fixed Memory leak --- .../lib/communication/mpi_tools.h | 74 +++++++++++-------- parallel/parallel_src/tests/CMakeLists.txt | 4 +- .../parallel_contraction_mpi_test.cpp | 14 +++- .../parallel_contraction_test.cpp | 39 ++++++++++ 4 files changed, 95 insertions(+), 36 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 25f59710..93183c40 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -9,6 +9,11 @@ #ifndef MPI_TOOLS_HMESDXF2 #define MPI_TOOLS_HMESDXF2 +#include +#include +#include +#include + #include "data_structure/parallel_graph_access.h" #include "partition_config.h" @@ -83,45 +88,45 @@ namespace mpi_collective_tools { // Packs message buffer so that MPI_AlltoAllv can be used template requires Container - auto pack_messages( - Input const &messages) -> mpi_packed_message { + auto pack_messages(Input const &messages) + -> mpi_packed_message { using inner = typename Input::value_type; using element_type = typename inner::value_type; - std::vector flattened_vector{}; - std::vector offsets{}; - std::vector lengths{}; - - int current_offset = 0; - - for (auto const &subvector: messages) { - offsets.push_back(current_offset); - lengths.push_back(static_cast(subvector.size())); + // Flattening the vector of vectors using join_view + auto const flattened_view = messages | std::ranges::views::join; + std::vector flattened_vector; + // converting view into vector + for (auto &&elem: flattened_view) { + flattened_vector.push_back(static_cast(elem)); + } - flattened_vector.insert(flattened_vector.end(), subvector.begin(), subvector.end()); + // Calculating lengths using transform view + std::vector lengths; + lengths.reserve(messages.size()); + std::ranges::transform(messages, std::back_inserter(lengths), + [](auto const &elem) { return static_cast(elem.size()); }); - current_offset += static_cast(subvector.size()); - } + // Calculating offsets using exclusive_scan + std::vector offsets(messages.size()); + std::exclusive_scan(lengths.begin(), lengths.end(), offsets.begin(), 0); return mpi_packed_message{flattened_vector, offsets, lengths}; } - // Unpacks received message buffer received from alltoall template - auto unpack_messages(mpi_packed_message const &packed_message) -> std::vector > { + auto unpack_messages(mpi_packed_message const &packed_message) + -> std::vector > { auto const &[recv_buf, recv_displs, recv_counts] = packed_message; + auto const recv_span = std::span(recv_buf); int num_ranks = static_cast(recv_counts.size()); - std::vector > result(num_ranks); - for (int i = 0; i < num_ranks; ++i) { - int const count = recv_counts.at(i); - int const offset = recv_displs.at(i); - - // Extract the subvector corresponding to rank `i` - std::vector subvector(recv_buf.begin() + offset, recv_buf.begin() + offset + count); + std::vector > result; + result.reserve(num_ranks); - // Store it in the result vector at index `i` - result[i] = std::move(subvector); + for (int i = 0; i < num_ranks; ++i) { + auto const subspan = recv_span.subspan(recv_displs.at(i), recv_counts.at(i)); + result.emplace_back(subspan.begin(), subspan.end()); } return result; @@ -140,20 +145,25 @@ namespace mpi_collective_tools { // Packing messages into vector and computing offsets and lengths for the sub messages auto [send_packed_messages, send_offsets, send_lengths] = mpi_collective_tools::pack_messages(sends); + if (send_offsets.size() != send_lengths.size()) { + throw(std::runtime_error("MPI_collective_tools::pack_messages()")); + } else if ((send_offsets.size() != size) || (send_lengths.size() != size)) { + throw(std::runtime_error("MPI_collective_tools::pack_messages()")); + } + // Preparing receive buffers for the node ids, offsets, and lengths int total_message_size = 0; const int local_message_size = static_cast(send_packed_messages.size()); MPI_Allreduce(&local_message_size, &total_message_size, 1, MPI_INT, MPI_SUM, communicator); - auto recv_packed_messages = std::vector(total_message_size); - auto recv_offsets = std::vector(size); - auto recv_lengths = std::vector(size); + auto recv_packed_messages = std::vector(total_message_size, 0); + auto recv_offsets = std::vector(size, 0); + auto recv_lengths = std::vector(size, 0); auto mpi_error = MPI_Alltoallv(send_packed_messages.data(), send_lengths.data(), send_offsets.data(), - MPI_UNSIGNED_LONG_LONG, recv_packed_messages.data(), recv_lengths.data(), - recv_offsets.data(), MPI_UNSIGNED_LONG_LONG, - communicator); - + MPI_UNSIGNED_LONG_LONG, recv_packed_messages.data(), recv_lengths.data(), + recv_offsets.data(), MPI_UNSIGNED_LONG_LONG, + communicator); return mpi_collective_tools::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); } } diff --git a/parallel/parallel_src/tests/CMakeLists.txt b/parallel/parallel_src/tests/CMakeLists.txt index 0fe4e3a6..886db697 100644 --- a/parallel/parallel_src/tests/CMakeLists.txt +++ b/parallel/parallel_src/tests/CMakeLists.txt @@ -1,4 +1,6 @@ # Automatically enable catch2 to generate ctest targets +find_package(fmt REQUIRED CONFIG) + list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) include(CTest) include(Catch) @@ -24,7 +26,7 @@ catch_discover_tests( add_executable(parallel_contraction_mpi_test parallel_contraction/parallel_contraction_mpi_test.cpp) target_link_libraries(parallel_contraction_mpi_test PRIVATE catch_mpi_runner) target_link_libraries(parallel_contraction_mpi_test PRIVATE libparallel) -target_link_libraries(parallel_contraction_mpi_test PRIVATE libmodified_kahip_interface) +target_link_libraries(parallel_contraction_mpi_test PRIVATE libmodified_kahip_interface fmt::fmt) catch_discover_tests( parallel_contraction_mpi_test diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index 4308fa25..98766447 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -6,12 +6,20 @@ #include #include #include +#include +#include -TEST_CASE("all to all vector of vectos", "[unit][mpi]") { +TEST_CASE("all to all vector of vectors", "[unit][mpi]") { SECTION("emtpy case") { - const std::vector> v_empty{{1}}; + PEID rank, size; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + const std::vector> v_empty{{1,1,1,1},{2,2,2,2},{3,3,3,3},{4,4,4,4}}; auto vec = mpi_collective_tools::all_to_all(v_empty, MPI_COMM_WORLD); - REQUIRE(vec.empty()); + MPI_Barrier(MPI_COMM_WORLD); + fmt::println("rank: {} -> {}", rank, vec); + REQUIRE(v_empty == vec); } } \ No newline at end of file diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index 5f78518a..76b78174 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "parallel_contraction_projection/parallel_contraction.h" @@ -17,6 +18,12 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { REQUIRE(flattened.empty()); REQUIRE(offsets.empty()); REQUIRE(lengths.empty()); + + std::vector > m_empty2{{}}; + auto [flattened2, offsets2, lengths2] = mpi_collective_tools::pack_messages(m_empty2); + REQUIRE(flattened2.empty()); + REQUIRE(offsets2.size() == 1); + REQUIRE(lengths2.size() == 1); } SECTION("Simple Vector") { std::vector > m_simple{{1, 2, 3, 4}}; @@ -69,3 +76,35 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { REQUIRE(orig == flattened); } } + +TEST_CASE("Packing and Unpacking for messages", "[unit][mpi]") { + SECTION("Empty Vector") { + constexpr std::vector > m_empty{}; + auto const packed = mpi_collective_tools::pack_messages(m_empty); + auto const unpacked = mpi_collective_tools::unpack_messages(packed); + + REQUIRE(m_empty == unpacked); + } + + SECTION("Message of an empty Vector") { + std::vector > const m_empty{{}}; + auto const packed = mpi_collective_tools::pack_messages(m_empty); + auto const unpacked = mpi_collective_tools::unpack_messages(packed); + + REQUIRE(m_empty == unpacked); + } + + SECTION("Complex Message") { + std::vector > data = { + {1, 2, 3}, + {}, + {4, 5}, + {},{}, + {6, 7, 8, 9} + }; + auto const packed = mpi_collective_tools::pack_messages(data); + auto const unpacked = mpi_collective_tools::unpack_messages(packed); + REQUIRE(data == unpacked); + + } +} From 18599ba6ebd93c287e612fd76715540b18eee9df Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 3 Sep 2024 11:02:28 +0100 Subject: [PATCH 05/64] Possible fix --- .../lib/communication/mpi_tools.h | 9 ++++--- .../lib/communication/mpi_types.h | 26 +++++++++++++++++++ .../parallel_contraction_mpi_test.cpp | 8 ++++-- 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 parallel/parallel_src/lib/communication/mpi_types.h diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 93183c40..cd59c1e0 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -16,6 +16,7 @@ #include "data_structure/parallel_graph_access.h" #include "partition_config.h" +#include "mpi_types.h" class mpi_tools { public: @@ -152,11 +153,11 @@ namespace mpi_collective_tools { } // Preparing receive buffers for the node ids, offsets, and lengths - int total_message_size = 0; - const int local_message_size = static_cast(send_packed_messages.size()); - MPI_Allreduce(&local_message_size, &total_message_size, 1, MPI_INT, MPI_SUM, communicator); + std::vector num_recv_from_rank(send_lengths.size(),0); // number of messeages from each rank + MPI_Alltoall(send_lengths.data(), size, MPI_INT, num_recv_from_rank.data(), size, MPI_INT, communicator); + auto const recv_buff_size = std::reduce(send_packed_messages.begin(), send_packed_messages.end(), 0); - auto recv_packed_messages = std::vector(total_message_size, 0); + auto recv_packed_messages = std::vector(recv_buff_size, 0); auto recv_offsets = std::vector(size, 0); auto recv_lengths = std::vector(size, 0); diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h new file mode 100644 index 00000000..aad34e9b --- /dev/null +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -0,0 +1,26 @@ +// +// Created by Erich Essmann on 03/09/2024. +// + +#ifndef MPI_TYPES_H +#define MPI_TYPES_H +#include +#include +namespace mpi { + namespace detail { + enum class mpi_data_kinds { + base, + composite, + none + }; + + template + constexpr auto get_data_kind() -> mpi_data_kinds { + if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } + } + } +} + +#endif //MPI_TYPES_H diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index 98766447..2146ed1c 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -6,9 +6,12 @@ #include #include #include + #include #include +#include "parallel_contraction_projection/parallel_contraction.h" + TEST_CASE("all to all vector of vectors", "[unit][mpi]") { SECTION("emtpy case") { @@ -16,10 +19,11 @@ TEST_CASE("all to all vector of vectors", "[unit][mpi]") { MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); - const std::vector> v_empty{{1,1,1,1},{2,2,2,2},{3,3,3,3},{4,4,4,4}}; + const std::vector> v_empty{{1},{2,2},{3,3,3},{4,4,4,4}}; + auto [send_buff, offsets, counts] = mpi_collective_tools::pack_messages(v_empty); auto vec = mpi_collective_tools::all_to_all(v_empty, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); fmt::println("rank: {} -> {}", rank, vec); - REQUIRE(v_empty == vec); + REQUIRE(v_empty.size() == vec.size()); } } \ No newline at end of file From 6d0d468815d9a510f09587d71099dc8624494dd5 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 3 Sep 2024 12:36:02 +0100 Subject: [PATCH 06/64] MPI_Alltoallv is silly function --- parallel/parallel_src/lib/communication/mpi_tools.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index cd59c1e0..dcd85471 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -154,12 +154,15 @@ namespace mpi_collective_tools { // Preparing receive buffers for the node ids, offsets, and lengths std::vector num_recv_from_rank(send_lengths.size(),0); // number of messeages from each rank - MPI_Alltoall(send_lengths.data(), size, MPI_INT, num_recv_from_rank.data(), size, MPI_INT, communicator); - auto const recv_buff_size = std::reduce(send_packed_messages.begin(), send_packed_messages.end(), 0); + MPI_Alltoall(send_lengths.data(), 1, MPI_INT, num_recv_from_rank.data(), 1, MPI_INT, communicator); + auto const recv_buff_size = std::reduce(num_recv_from_rank.begin(), num_recv_from_rank.end(), 0); auto recv_packed_messages = std::vector(recv_buff_size, 0); auto recv_offsets = std::vector(size, 0); - auto recv_lengths = std::vector(size, 0); + auto const recv_lengths = num_recv_from_rank; + + // Calculating recv offsets + std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), recv_offsets.begin(), 0); auto mpi_error = MPI_Alltoallv(send_packed_messages.data(), send_lengths.data(), send_offsets.data(), MPI_UNSIGNED_LONG_LONG, recv_packed_messages.data(), recv_lengths.data(), From fcc254c3fdd828c09ea2ac0b87245204a5fb95dd Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 3 Sep 2024 13:25:21 +0100 Subject: [PATCH 07/64] Removed at() --- .../parallel_contraction.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 26f619e4..c05e4013 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -78,12 +78,10 @@ void parallel_contraction::compute_label_mapping_collective(MPI_Comm communicato std::vector > filter; filter.resize(size); - forall_local_nodes(G, node) - { - PEID peID = G.getNodeLabel(node) / divisor; - filter.at(peID).at(G.getNodeLabel(node)) = true; - } - endfor + forall_local_nodes(G, node) { + PEID peID = G.getNodeLabel(node) / divisor; + filter[ peID ][G.getNodeLabel(node)] = true; + } endfor for (PEID peID = 0; peID < size; peID++) { std::unordered_map::iterator it; From 358852d9c02b84f966deae59aaf287ada5a6b316 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 3 Sep 2024 13:48:16 +0100 Subject: [PATCH 08/64] Memory leak found and fixed --- .../parallel_contraction.cpp | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index c05e4013..9b5e7432 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -133,10 +133,10 @@ void parallel_contraction::compute_label_mapping_collective(MPI_Comm communicato // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // build the mapping locally - std::unordered_map label_mapping_to_cnode; + std::unordered_map< NodeID, NodeID > label_mapping_to_cnode; NodeID cur_id = num_smaller_ids; - for (ULONG i = 0; i < local_labels.size(); i++) { - label_mapping_to_cnode.at(local_labels.at(i)) = cur_id++; + for( ULONG i = 0; i < local_labels.size(); i++) { + label_mapping_to_cnode[local_labels[i]] = cur_id++; } // now send the processes the mapping back @@ -157,19 +157,8 @@ void parallel_contraction::compute_label_mapping_collective(MPI_Comm communicato } } - for (PEID peID = 0; peID < size; peID++) { - if (peID != rank) { - MPI_Request rq; - MPI_Isend(&m_out_messages[peID][0], - m_out_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID + 5 * size, communicator, &rq); - } - } - auto recv_mapping = mpi_collective_tools::all_to_all(m_out_messages, communicator); - - + // first the local labels for (ULONG i = 0; i < m_messages.at(rank).size(); i++) { label_mapping[m_messages.at(rank).at(i)] = label_mapping_to_cnode.at(m_messages.at(rank).at(i)); From 289ac3a2cdddda8ad9674bdc03c8cf316b0d3ad6 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 3 Sep 2024 17:53:12 +0100 Subject: [PATCH 09/64] parhip works --- .../lib/communication/mpi_tools.h | 5 +- .../lib/communication/mpi_types.h | 52 ++++++++++++++++++- .../parallel_contraction.cpp | 2 +- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index dcd85471..337b2dfd 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -164,10 +164,13 @@ namespace mpi_collective_tools { // Calculating recv offsets std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), recv_offsets.begin(), 0); - auto mpi_error = MPI_Alltoallv(send_packed_messages.data(), send_lengths.data(), send_offsets.data(), + auto const mpi_error = MPI_Alltoallv(send_packed_messages.data(), send_lengths.data(), send_offsets.data(), MPI_UNSIGNED_LONG_LONG, recv_packed_messages.data(), recv_lengths.data(), recv_offsets.data(), MPI_UNSIGNED_LONG_LONG, communicator); + if (mpi_error != MPI_SUCCESS) { + throw(std::runtime_error("MPI_collective_tools::all_to_all()")); + } return mpi_collective_tools::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); } } diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index aad34e9b..8b82aafd 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -4,7 +4,9 @@ #ifndef MPI_TYPES_H #define MPI_TYPES_H +#include #include + #include namespace mpi { namespace detail { @@ -16,8 +18,56 @@ namespace mpi { template constexpr auto get_data_kind() -> mpi_data_kinds { - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else { + return mpi_data_kinds::none; } } } diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 9b5e7432..befebfe4 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -158,7 +158,7 @@ void parallel_contraction::compute_label_mapping_collective(MPI_Comm communicato } auto recv_mapping = mpi_collective_tools::all_to_all(m_out_messages, communicator); - + // first the local labels for (ULONG i = 0; i < m_messages.at(rank).size(); i++) { label_mapping[m_messages.at(rank).at(i)] = label_mapping_to_cnode.at(m_messages.at(rank).at(i)); From 4ca1871cd52c43724fd9c0a9e374bed1394f9d84 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Wed, 4 Sep 2024 14:51:41 +0100 Subject: [PATCH 10/64] MPI to C data mapping --- .clang-format | 0 .../lib/communication/mpi_types.h | 241 +++++++++++++----- 2 files changed, 178 insertions(+), 63 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..e69de29b diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 8b82aafd..e085dee0 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -4,73 +4,188 @@ #ifndef MPI_TYPES_H #define MPI_TYPES_H -#include +#include #include #include + namespace mpi { - namespace detail { - enum class mpi_data_kinds { - base, - composite, - none - }; +namespace details { +enum class mpi_data_kinds { none, base, composite }; + +template +constexpr auto get_data_kind() -> mpi_data_kinds { + if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else if constexpr (std::is_same_v) { + return mpi_data_kinds::base; + } else { + return mpi_data_kinds::none; + } +} +} // namespace detail + +template +concept mpi_native_datatype = requires { + details::get_data_kind() == details::mpi_data_kinds::base; +}; + +template +concept mpi_composite_datatype = requires { + details::get_data_kind() == details::mpi_data_kinds::composite; +}; + - template - constexpr auto get_data_kind() -> mpi_data_kinds { - if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else { - return mpi_data_kinds::none; - } - } - } +template +constexpr auto get_mpi_native_datatype() -> MPI_Datatype { + if constexpr (std::is_same_v) { + return MPI_CHAR; + } else if constexpr (std::is_same_v) { + return MPI_WCHAR; + } else if constexpr (std::is_same_v) { + return MPI_SHORT; + } else if constexpr (std::is_same_v) { + return MPI_INT; + } else if constexpr (std::is_same_v) { + return MPI_LONG; + } else if constexpr (std::is_same_v) { + return MPI_SIGNED_CHAR; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED_CHAR; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED_SHORT; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED_LONG; + } else if constexpr (std::is_same_v) { + return MPI_FLOAT; + } else if constexpr (std::is_same_v) { + return MPI_DOUBLE; + } else if constexpr (std::is_same_v) { + return MPI_LONG_DOUBLE; + } else if constexpr (std::is_same_v) { + return MPI_CXX_BOOL; + } else if constexpr (std::is_same_v) { + return MPI_INT8_T; + } else if constexpr (std::is_same_v) { + return MPI_INT16_T; + } else if constexpr (std::is_same_v) { + return MPI_INT32_T; + } else if constexpr (std::is_same_v) { + return MPI_INT64_T; + } else if constexpr (std::is_same_v) { + return MPI_UINT8_T; + } else if constexpr (std::is_same_v) { + return MPI_UINT16_T; + } else if constexpr (std::is_same_v) { + return MPI_UINT32_T; + } else if constexpr (std::is_same_v) { + return MPI_UINT64_T; + } else if constexpr (std::is_same_v) { + return MPI_LONG_LONG_INT; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED_LONG_LONG; + } else { + return MPI_DATATYPE_NULL; + }; } -#endif //MPI_TYPES_H +template +constexpr auto get_mpi_composite_datatype() -> MPI_Datatype; + +template +concept mpi_datatype = mpi_native_datatype || mpi_composite_datatype; + +template +concept container = requires(ContainerType a, ContainerType const b) { + requires std::regular; + requires std::swappable; + requires std::destructible; + requires std::same_as; + requires std::same_as; + requires std::forward_iterator; + requires std::forward_iterator; + requires std::signed_integral; + requires std::same_as::difference_type>; + requires std::same_as< + typename ContainerType::difference_type, + typename std::iterator_traits< + typename ContainerType::const_iterator>::difference_type>; + { a.begin() } -> std::same_as; + { a.end() } -> std::same_as; + { b.begin() } -> std::same_as; + { b.end() } -> std::same_as; + { a.cbegin() } -> std::same_as; + { a.cend() } -> std::same_as; + { a.size() } -> std::same_as; + { a.max_size() } -> std::same_as; + { a.empty() } -> std::same_as; +}; + +template +constexpr auto get_mpi_datatype() -> MPI_Datatype { + if constexpr (details::get_data_kind() == details::mpi_data_kinds::base) { + return get_mpi_native_datatype(); + } else if constexpr (details::get_data_kind() == details::mpi_data_kinds::composite) { + return get_mpi_composite_datatype(); // Point of customization for user-defined types + } else { + return MPI_PACKED; // Fallback to serialization of object + } + +}; + +template +concept mpi_container = container && + mpi_datatype; +} // namespace mpi + +#endif // MPI_TYPES_H From f1401d7a6b3d9f5550c9632e0d66c3f6386ffa0c Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Wed, 4 Sep 2024 15:11:22 +0100 Subject: [PATCH 11/64] mpi_pack fixed --- .../lib/communication/mpi_tools.h | 43 ++++--------------- .../parallel_contraction.cpp | 4 +- .../parallel_contraction_mpi_test.cpp | 5 +-- .../parallel_contraction_test.cpp | 22 +++++----- 4 files changed, 24 insertions(+), 50 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 337b2dfd..394290fe 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -52,32 +52,7 @@ class mpi_tools { const MPI_Datatype &recvtype, MPI_Comm communicator); }; -namespace mpi_collective_tools { - template - concept Container = requires(ContainerType a, const ContainerType b) - { - requires std::regular; - requires std::swappable; - requires std::destructible; - requires std::same_as; - requires std::same_as; - requires std::forward_iterator; - requires std::forward_iterator; - requires std::signed_integral; - requires std::same_as::difference_type>; - requires std::same_as::difference_type>; - { a.begin() } -> std::same_as; - { a.end() } -> std::same_as; - { b.begin() } -> std::same_as; - { b.end() } -> std::same_as; - { a.cbegin() } -> std::same_as; - { a.cend() } -> std::same_as; - { a.size() } -> std::same_as; - { a.max_size() } -> std::same_as; - { a.empty() } -> std::same_as; - }; +namespace mpi { template struct mpi_packed_message { @@ -87,8 +62,8 @@ namespace mpi_collective_tools { }; // Packs message buffer so that MPI_AlltoAllv can be used - template - requires Container + template + requires container auto pack_messages(Input const &messages) -> mpi_packed_message { using inner = typename Input::value_type; @@ -133,8 +108,8 @@ namespace mpi_collective_tools { return result; } - template - requires Container + template + requires container auto all_to_all(Input const &sends, MPI_Comm communicator) { using inner = typename Input::value_type; using element_type = typename inner::value_type; @@ -144,7 +119,7 @@ namespace mpi_collective_tools { MPI_Comm_size(communicator, &size); // Packing messages into vector and computing offsets and lengths for the sub messages - auto [send_packed_messages, send_offsets, send_lengths] = mpi_collective_tools::pack_messages(sends); + auto [send_packed_messages, send_offsets, send_lengths] = mpi::pack_messages(sends); if (send_offsets.size() != send_lengths.size()) { throw(std::runtime_error("MPI_collective_tools::pack_messages()")); @@ -165,13 +140,13 @@ namespace mpi_collective_tools { std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), recv_offsets.begin(), 0); auto const mpi_error = MPI_Alltoallv(send_packed_messages.data(), send_lengths.data(), send_offsets.data(), - MPI_UNSIGNED_LONG_LONG, recv_packed_messages.data(), recv_lengths.data(), - recv_offsets.data(), MPI_UNSIGNED_LONG_LONG, + get_mpi_datatype(), recv_packed_messages.data(), recv_lengths.data(), + recv_offsets.data(), get_mpi_datatype(), communicator); if (mpi_error != MPI_SUCCESS) { throw(std::runtime_error("MPI_collective_tools::all_to_all()")); } - return mpi_collective_tools::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); + return mpi::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); } } diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index befebfe4..8125aecc 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -90,7 +90,7 @@ void parallel_contraction::compute_label_mapping_collective(MPI_Comm communicato } } - auto const local_labels_byPE = mpi_collective_tools::all_to_all(m_messages, communicator); + auto const local_labels_byPE = mpi::all_to_all(m_messages, communicator); std::vector local_labels; for (PEID peID = 0; peID < size; peID++) { @@ -157,7 +157,7 @@ void parallel_contraction::compute_label_mapping_collective(MPI_Comm communicato } } - auto recv_mapping = mpi_collective_tools::all_to_all(m_out_messages, communicator); + auto recv_mapping = mpi::all_to_all(m_out_messages, communicator); // first the local labels for (ULONG i = 0; i < m_messages.at(rank).size(); i++) { diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index 2146ed1c..48c92ae8 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -19,9 +19,8 @@ TEST_CASE("all to all vector of vectors", "[unit][mpi]") { MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); - const std::vector> v_empty{{1},{2,2},{3,3,3},{4,4,4,4}}; - auto [send_buff, offsets, counts] = mpi_collective_tools::pack_messages(v_empty); - auto vec = mpi_collective_tools::all_to_all(v_empty, MPI_COMM_WORLD); + const std::vector> v_empty{{},{1},{2,2},{3,3,3},{4,4,4,4}}; + auto vec = mpi::all_to_all(v_empty, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); fmt::println("rank: {} -> {}", rank, vec); REQUIRE(v_empty.size() == vec.size()); diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index 76b78174..3c25b7a3 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -14,20 +14,20 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { SECTION("Empty Vector") { std::vector > m_empty{}; - auto [flattened, offsets, lengths] = mpi_collective_tools::pack_messages(m_empty); + auto [flattened, offsets, lengths] = mpi::pack_messages(m_empty); REQUIRE(flattened.empty()); REQUIRE(offsets.empty()); REQUIRE(lengths.empty()); std::vector > m_empty2{{}}; - auto [flattened2, offsets2, lengths2] = mpi_collective_tools::pack_messages(m_empty2); + auto [flattened2, offsets2, lengths2] = mpi::pack_messages(m_empty2); REQUIRE(flattened2.empty()); REQUIRE(offsets2.size() == 1); REQUIRE(lengths2.size() == 1); } SECTION("Simple Vector") { std::vector > m_simple{{1, 2, 3, 4}}; - auto [flattened, offsets, lengths] = mpi_collective_tools::pack_messages(m_simple); + auto [flattened, offsets, lengths] = mpi::pack_messages(m_simple); // Testing sizes REQUIRE(flattened.size() == 4); @@ -46,7 +46,7 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { {6, 7, 8, 9} }; - auto [flattened, offsets, lengths] = mpi_collective_tools::pack_messages(data); + auto [flattened, offsets, lengths] = mpi::pack_messages(data); // Testing sizes REQUIRE(flattened.size() == 9); @@ -71,7 +71,7 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { auto fun = std::ranges::less{}; auto sliced = orig | std::views::chunk_by(fun) | std::ranges::to > >(); - auto [flattened, offsets, lengths] = mpi_collective_tools::pack_messages(sliced); + auto [flattened, offsets, lengths] = mpi::pack_messages(sliced); REQUIRE(flattened.size() == orig.size()); REQUIRE(orig == flattened); } @@ -80,16 +80,16 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { TEST_CASE("Packing and Unpacking for messages", "[unit][mpi]") { SECTION("Empty Vector") { constexpr std::vector > m_empty{}; - auto const packed = mpi_collective_tools::pack_messages(m_empty); - auto const unpacked = mpi_collective_tools::unpack_messages(packed); + auto const packed = mpi::pack_messages(m_empty); + auto const unpacked = mpi::unpack_messages(packed); REQUIRE(m_empty == unpacked); } SECTION("Message of an empty Vector") { std::vector > const m_empty{{}}; - auto const packed = mpi_collective_tools::pack_messages(m_empty); - auto const unpacked = mpi_collective_tools::unpack_messages(packed); + auto const packed = mpi::pack_messages(m_empty); + auto const unpacked = mpi::unpack_messages(packed); REQUIRE(m_empty == unpacked); } @@ -102,8 +102,8 @@ TEST_CASE("Packing and Unpacking for messages", "[unit][mpi]") { {},{}, {6, 7, 8, 9} }; - auto const packed = mpi_collective_tools::pack_messages(data); - auto const unpacked = mpi_collective_tools::unpack_messages(packed); + auto const packed = mpi::pack_messages(data); + auto const unpacked = mpi::unpack_messages(packed); REQUIRE(data == unpacked); } From 04ef612fb916dd9a02ba29a37218c4fe833f992f Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Wed, 4 Sep 2024 15:16:56 +0100 Subject: [PATCH 12/64] Unneeded constructors and destructors removed --- parallel/parallel_src/lib/communication/mpi_tools.cpp | 9 --------- parallel/parallel_src/lib/communication/mpi_tools.h | 4 ---- 2 files changed, 13 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.cpp b/parallel/parallel_src/lib/communication/mpi_tools.cpp index e6a5646b..80864a60 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.cpp +++ b/parallel/parallel_src/lib/communication/mpi_tools.cpp @@ -12,15 +12,6 @@ #include "io/parallel_graph_io.h" #include "mpi_tools.h" -mpi_tools::mpi_tools() { - -} - -mpi_tools::~mpi_tools() { - - -} - // currently this method is for debugging purposses only // later on this may be a parallel io routine void mpi_tools::collect_and_write_labels( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G) { diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 394290fe..53bf8743 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -20,10 +20,6 @@ class mpi_tools { public: - mpi_tools(); - - virtual ~mpi_tools(); - void collect_and_write_labels(MPI_Comm communicator, PPartitionConfig &config, parallel_graph_access &G); From e10b73441f502e48e7e0fc7a888a84a2de73f630 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 6 Sep 2024 17:32:38 +0100 Subject: [PATCH 13/64] Update parallel_contraction.cpp --- .../parallel_contraction_projection/parallel_contraction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 8125aecc..e46acad8 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -12,7 +12,7 @@ #include "data_structure/hashed_graph.h" #include "tools/helpers.h" -#define CONTRACTION true +#define CONTRACTION false #ifndef CONTRACTION #define CONTRACTION false From b5c9655c7ef9ffe23affdd5f0574715727b6b804 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 17 Sep 2024 16:04:40 +0100 Subject: [PATCH 14/64] Cray doesn't have spans --- .gitignore | 2 ++ CMakeLists.txt | 2 +- .../lib/communication/mpi_tools.h | 6 +++--- .../parallel_contraction_test.cpp | 20 +++++-------------- 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index f8240950..304755b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .cache/ build/ deploy/ +.idea +**/.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index d6dcb248..dd6bbd8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ if(CCACHE_PROGRAM) endif() project(KaHIP C CXX) -set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 53bf8743..15f08aaf 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -90,14 +90,14 @@ namespace mpi { auto unpack_messages(mpi_packed_message const &packed_message) -> std::vector > { auto const &[recv_buf, recv_displs, recv_counts] = packed_message; - auto const recv_span = std::span(recv_buf); int num_ranks = static_cast(recv_counts.size()); std::vector > result; result.reserve(num_ranks); for (int i = 0; i < num_ranks; ++i) { - auto const subspan = recv_span.subspan(recv_displs.at(i), recv_counts.at(i)); + std::vector subspan{}; + subspan.insert(subspan.begin(), recv_buf.begin() + recv_displs[i], recv_buf.begin() + recv_displs[i] + recv_counts[i]); result.emplace_back(subspan.begin(), subspan.end()); } @@ -124,7 +124,7 @@ namespace mpi { } // Preparing receive buffers for the node ids, offsets, and lengths - std::vector num_recv_from_rank(send_lengths.size(),0); // number of messeages from each rank + std::vector num_recv_from_rank(send_lengths.size(),0); // number of messages from each rank MPI_Alltoall(send_lengths.data(), 1, MPI_INT, num_recv_from_rank.data(), 1, MPI_INT, communicator); auto const recv_buff_size = std::reduce(num_recv_from_rank.begin(), num_recv_from_rank.end(), 0); diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index 3c25b7a3..ffda0de9 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -54,27 +54,17 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { REQUIRE(lengths.size() == 4); // Creating Subspans - auto s0 = std::span(flattened); - auto s1 = s0.subspan(offsets[0], lengths[0]) | std::ranges::to >(); - auto s2 = s0.subspan(offsets[1], lengths[1]) | std::ranges::to >(); - auto s3 = s0.subspan(offsets[2], lengths[2]) | std::ranges::to >(); - auto s4 = s0.subspan(offsets[3], lengths[3]) | std::ranges::to >(); + std::vector s1, s2, s3, s4; + s1.insert(s1.begin(), flattened.begin() + offsets[0], flattened.begin() + offsets[0] + lengths[0]); + s2.insert(s2.begin(), flattened.begin() + offsets[1], flattened.begin() + offsets[1] + lengths[1]); + s3.insert(s3.begin(), flattened.begin() + offsets[2], flattened.begin() + offsets[2] + lengths[2]); + s4.insert(s4.begin(), flattened.begin() + offsets[3], flattened.begin() + offsets[3] + lengths[3]); REQUIRE(s1 == data[0]); REQUIRE(s2 == data[1]); REQUIRE(s3 == data[2]); REQUIRE(s4 == data[3]); } - - SECTION("Sliced Vector") { - std::vector orig = {1, 2, 3, 1, 2, 3, 3, 3, 1, 2, 3}; - auto fun = std::ranges::less{}; - auto sliced = orig | std::views::chunk_by(fun) | std::ranges::to > >(); - - auto [flattened, offsets, lengths] = mpi::pack_messages(sliced); - REQUIRE(flattened.size() == orig.size()); - REQUIRE(orig == flattened); - } } TEST_CASE("Packing and Unpacking for messages", "[unit][mpi]") { From 100bce20fcf82e918eab67906b1e4af44245d8ea Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 17 Sep 2024 16:15:38 +0100 Subject: [PATCH 15/64] Cray cannot deduce template types --- parallel/parallel_src/lib/communication/mpi_tools.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 15f08aaf..da08fdda 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -83,7 +83,7 @@ namespace mpi { std::vector offsets(messages.size()); std::exclusive_scan(lengths.begin(), lengths.end(), offsets.begin(), 0); - return mpi_packed_message{flattened_vector, offsets, lengths}; + return mpi_packed_message{flattened_vector, offsets, lengths}; } template From e0bf6eef78db0af15386c560c651fc9d66d2382b Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 17 Sep 2024 16:54:43 +0100 Subject: [PATCH 16/64] Testing off by default --- parallel/parallel_src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index 54225e30..de51ccce 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -115,7 +115,7 @@ install(TARGETS parhip_interface_static DESTINATION lib) # Temporary Testing -set(ENABLE_TESTING ON) +set(ENABLE_TESTING OFF) if (ENABLE_TESTING) find_package(Catch2 REQUIRED CONFIG) enable_testing() From 446adf1b92a7f6f26c6971d28de7c1d64da3d0b6 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 17 Sep 2024 22:58:15 +0100 Subject: [PATCH 17/64] Update parallel_contraction.cpp --- .../parallel_contraction.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index e46acad8..b7db74bf 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -12,11 +12,7 @@ #include "data_structure/hashed_graph.h" #include "tools/helpers.h" -#define CONTRACTION false - -#ifndef CONTRACTION -#define CONTRACTION false -#endif +#define CONTRACTION true parallel_contraction::parallel_contraction() = default; From 10aca3c6af7a09e58bdb6a5d87f5dcda3f26f67f Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 20 Sep 2024 12:42:17 +0100 Subject: [PATCH 18/64] Enable testing, add {cinttypes}, and update CRAY-C MPI handling This commit enables testing by setting ENABLE_TESTING to ON in CMakeLists.txt. It includes the header in mpi_types.h and updates the handling of CRAY-C compilers by adding conditional compilation checks. Additionally, it refines type casting and constexpr definitions for improved code clarity and performance. --- parallel/parallel_src/CMakeLists.txt | 2 +- .../lib/communication/mpi_tools.cpp | 4 +-- .../lib/communication/mpi_tools.h | 28 +++++++++++++------ .../lib/communication/mpi_types.h | 1 + parallel/parallel_src/lib/definitions.h | 2 +- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index de51ccce..54225e30 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -115,7 +115,7 @@ install(TARGETS parhip_interface_static DESTINATION lib) # Temporary Testing -set(ENABLE_TESTING OFF) +set(ENABLE_TESTING ON) if (ENABLE_TESTING) find_package(Catch2 REQUIRED CONFIG) enable_testing() diff --git a/parallel/parallel_src/lib/communication/mpi_tools.cpp b/parallel/parallel_src/lib/communication/mpi_tools.cpp index 80864a60..218bdaef 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.cpp +++ b/parallel/parallel_src/lib/communication/mpi_tools.cpp @@ -168,7 +168,7 @@ void mpi_tools::distribute_local_graph( MPI_Comm communicator, PPartitionConfig ULONG number_of_edges = 0; std::vector< int > buffer(2,0); - if(rank == (int)ROOT) { + if(rank == static_cast(ROOT)) { buffer[0] = G.number_of_global_nodes(); buffer[1] = G.number_of_global_edges(); } @@ -182,7 +182,7 @@ void mpi_tools::distribute_local_graph( MPI_Comm communicator, PPartitionConfig int* vwgt; int* adjwgt; - if( rank == (int)ROOT) { + if( rank == static_cast(ROOT)) { xadj = G.UNSAFE_metis_style_xadj_array(); adjncy = G.UNSAFE_metis_style_adjncy_array(); diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index da08fdda..4869e8a2 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -9,6 +9,10 @@ #ifndef MPI_TOOLS_HMESDXF2 #define MPI_TOOLS_HMESDXF2 +#ifndef _CRAYC +#define _CRAYC 0 +#endif + #include #include #include @@ -65,9 +69,9 @@ namespace mpi { using inner = typename Input::value_type; using element_type = typename inner::value_type; - // Flattening the vector of vectors using join_view + // Flattening the container of containers using join_view auto const flattened_view = messages | std::ranges::views::join; - std::vector flattened_vector; + std::vector flattened_vector{}; // converting view into vector for (auto &&elem: flattened_view) { flattened_vector.push_back(static_cast(elem)); @@ -94,19 +98,27 @@ namespace mpi { std::vector > result; result.reserve(num_ranks); - - for (int i = 0; i < num_ranks; ++i) { - std::vector subspan{}; - subspan.insert(subspan.begin(), recv_buf.begin() + recv_displs[i], recv_buf.begin() + recv_displs[i] + recv_counts[i]); - result.emplace_back(subspan.begin(), subspan.end()); + if constexpr (_CRAYC) { + for (int i = 0; i < num_ranks; ++i) { + std::vector subspan{}; + subspan.insert(subspan.begin(), recv_buf.begin() + recv_displs[i], recv_buf.begin() + recv_displs[i] + recv_counts[i]); + result.emplace_back(subspan.begin(), subspan.end()); + } + } else { + auto const recv_span = std::span(recv_buf); + for (int i = 0; i < num_ranks; ++i) { + auto const subspan = recv_span.subspan(recv_displs.at(i), recv_counts.at(i)); + result.emplace_back(subspan.begin(), subspan.end()); + } } + return result; } template requires container - auto all_to_all(Input const &sends, MPI_Comm communicator) { + auto all_to_all(Input const &sends, MPI_Comm communicator) -> std::vector > { using inner = typename Input::value_type; using element_type = typename inner::value_type; diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index e085dee0..2e4076c8 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -4,6 +4,7 @@ #ifndef MPI_TYPES_H #define MPI_TYPES_H +#include #include #include diff --git a/parallel/parallel_src/lib/definitions.h b/parallel/parallel_src/lib/definitions.h index 142e843d..98c1d716 100644 --- a/parallel/parallel_src/lib/definitions.h +++ b/parallel/parallel_src/lib/definitions.h @@ -37,7 +37,7 @@ typedef unsigned long long NodeWeight; typedef unsigned long long EdgeWeight; typedef int PEID; -const PEID ROOT = 0; +constexpr PEID ROOT = 0; typedef enum { PERMUTATION_QUALITY_NONE, From 1e57ba882b4f1c76b2e7b4311fb7eae2b12efc0c Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 20 Sep 2024 13:04:47 +0100 Subject: [PATCH 19/64] Clean up formatting and adding documentation --- .clang-format | 3 + .gitignore | 1 + .../lib/communication/mpi_tools.h | 331 +++++++++++------- .../lib/communication/mpi_types.h | 120 +++---- .../parallel_contraction.cpp | 220 ++++++------ 5 files changed, 381 insertions(+), 294 deletions(-) diff --git a/.clang-format b/.clang-format index e69de29b..ff5635c8 100644 --- a/.clang-format +++ b/.clang-format @@ -0,0 +1,3 @@ +BasedOnStyle: Chromium +UseTab: ForContinuationAndIndentation +TabWidth: 2 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 304755b7..a239015e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build/ deploy/ .idea **/.DS_Store +cmake-build-dev diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 4869e8a2..08294aa6 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -5,7 +5,6 @@ * Christian Schulz *****************************************************************************/ - #ifndef MPI_TOOLS_HMESDXF2 #define MPI_TOOLS_HMESDXF2 @@ -15,148 +14,224 @@ #include #include -#include #include +#include #include "data_structure/parallel_graph_access.h" -#include "partition_config.h" #include "mpi_types.h" +#include "partition_config.h" class mpi_tools { public: - void collect_and_write_labels(MPI_Comm communicator, PPartitionConfig &config, - parallel_graph_access &G); - - void collect_parallel_graph_to_local_graph(MPI_Comm communicator, - PPartitionConfig &config, - parallel_graph_access &G, - complete_graph_access &Q); - - // G is input (only on ROOT) - // G is output (on every other PE) - void distribute_local_graph(MPI_Comm communicator, PPartitionConfig &config, complete_graph_access &G); - - // alltoallv that can send more than int-count elements - void alltoallv(void *sendbuf, - ULONG sendcounts[], ULONG displs[], - const MPI_Datatype &sendtype, void *recvbuf, - ULONG recvcounts[], ULONG rdispls[], - const MPI_Datatype &recvtype) { - alltoallv(sendbuf, sendcounts, displs, sendtype, recvbuf, recvcounts, rdispls, recvtype, MPI_COMM_WORLD); - }; - - void alltoallv(void *sendbuf, - ULONG sendcounts[], ULONG displs[], - const MPI_Datatype &sendtype, void *recvbuf, - ULONG recvcounts[], ULONG rdispls[], - const MPI_Datatype &recvtype, MPI_Comm communicator); + void collect_and_write_labels(MPI_Comm communicator, PPartitionConfig &config, + parallel_graph_access &G); + + void collect_parallel_graph_to_local_graph(MPI_Comm communicator, + PPartitionConfig &config, + parallel_graph_access &G, + complete_graph_access &Q); + + // G is input (only on ROOT) + // G is output (on every other PE) + void distribute_local_graph(MPI_Comm communicator, PPartitionConfig &config, + complete_graph_access &G); + + // alltoallv that can send more than int-count elements + void alltoallv(void *sendbuf, ULONG sendcounts[], ULONG displs[], + const MPI_Datatype &sendtype, void *recvbuf, + ULONG recvcounts[], ULONG rdispls[], + const MPI_Datatype &recvtype) { + alltoallv(sendbuf, sendcounts, displs, sendtype, recvbuf, recvcounts, + rdispls, recvtype, MPI_COMM_WORLD); + }; + + void alltoallv(void *sendbuf, ULONG sendcounts[], ULONG displs[], + const MPI_Datatype &sendtype, void *recvbuf, + ULONG recvcounts[], ULONG rdispls[], + const MPI_Datatype &recvtype, MPI_Comm communicator); }; namespace mpi { - template - struct mpi_packed_message { - std::vector packed_message; - std::vector offsets; - std::vector lengths; - }; - - // Packs message buffer so that MPI_AlltoAllv can be used - template - requires container - auto pack_messages(Input const &messages) - -> mpi_packed_message { - using inner = typename Input::value_type; - using element_type = typename inner::value_type; - - // Flattening the container of containers using join_view - auto const flattened_view = messages | std::ranges::views::join; - std::vector flattened_vector{}; - // converting view into vector - for (auto &&elem: flattened_view) { - flattened_vector.push_back(static_cast(elem)); - } - - // Calculating lengths using transform view - std::vector lengths; - lengths.reserve(messages.size()); - std::ranges::transform(messages, std::back_inserter(lengths), - [](auto const &elem) { return static_cast(elem.size()); }); - - // Calculating offsets using exclusive_scan - std::vector offsets(messages.size()); - std::exclusive_scan(lengths.begin(), lengths.end(), offsets.begin(), 0); - - return mpi_packed_message{flattened_vector, offsets, lengths}; - } +template struct mpi_packed_message { + std::vector packed_message; + std::vector offsets; + std::vector lengths; +}; - template - auto unpack_messages(mpi_packed_message const &packed_message) - -> std::vector > { - auto const &[recv_buf, recv_displs, recv_counts] = packed_message; - int num_ranks = static_cast(recv_counts.size()); - - std::vector > result; - result.reserve(num_ranks); - if constexpr (_CRAYC) { - for (int i = 0; i < num_ranks; ++i) { - std::vector subspan{}; - subspan.insert(subspan.begin(), recv_buf.begin() + recv_displs[i], recv_buf.begin() + recv_displs[i] + recv_counts[i]); - result.emplace_back(subspan.begin(), subspan.end()); - } - } else { - auto const recv_span = std::span(recv_buf); - for (int i = 0; i < num_ranks; ++i) { - auto const subspan = recv_span.subspan(recv_displs.at(i), recv_counts.at(i)); - result.emplace_back(subspan.begin(), subspan.end()); - } - } - - - return result; - } +/** + * Packs a container of containers into a single flat vector suitable for + * MPI communication. + * + * This function takes a nested container (e.g., + * `std::vector>`) and flattens it into a single + * `std::vector`. It also calculates the lengths and offsets necessary to + * reconstruct the original nested container structure. + * + * Transformations: + * - Flattens the nested container using `std::ranges::views::join`. + * - Converts the flattened view into a `std::vector`. + * - Calculates the lengths of the inner containers. + * - Calculates the offsets for each inner container using exclusive_scan. + * + * Requirements: + * - `Input` must be a container of containers. + * - The inner containers must support the `value_type` type alias. + * + * @param messages The input container of containers to be packed. + * @return A `mpi_packed_message` object containing: + * - `flattened_vector`: A single vector containing all elements of + * the nested containers. + * - `offsets`: A vector of offsets for where each inner container + * starts in the flattened vector. + * - `lengths`: A vector of lengths for each inner container in the + * original nested container. + */ +template + requires container +auto pack_messages(Input const &messages) + -> mpi_packed_message { + using inner = typename Input::value_type; + using element_type = typename inner::value_type; + + // Flattening the container of containers using join_view + auto const flattened_view = messages | std::ranges::views::join; + std::vector flattened_vector{}; + // converting view into vector + for (auto &&elem : flattened_view) { + flattened_vector.push_back(static_cast(elem)); + } + + // Calculating lengths using transform view + std::vector lengths; + lengths.reserve(messages.size()); + std::ranges::transform( + messages, std::back_inserter(lengths), + [](auto const &elem) { return static_cast(elem.size()); }); + + // Calculating offsets using exclusive_scan + std::vector offsets(messages.size()); + std::exclusive_scan(lengths.begin(), lengths.end(), offsets.begin(), 0); + + return mpi_packed_message{flattened_vector, offsets, lengths}; +} - template - requires container - auto all_to_all(Input const &sends, MPI_Comm communicator) -> std::vector > { - using inner = typename Input::value_type; - using element_type = typename inner::value_type; - - PEID rank, size; - MPI_Comm_rank(communicator, &rank); - MPI_Comm_size(communicator, &size); - - // Packing messages into vector and computing offsets and lengths for the sub messages - auto [send_packed_messages, send_offsets, send_lengths] = mpi::pack_messages(sends); - - if (send_offsets.size() != send_lengths.size()) { - throw(std::runtime_error("MPI_collective_tools::pack_messages()")); - } else if ((send_offsets.size() != size) || (send_lengths.size() != size)) { - throw(std::runtime_error("MPI_collective_tools::pack_messages()")); - } - - // Preparing receive buffers for the node ids, offsets, and lengths - std::vector num_recv_from_rank(send_lengths.size(),0); // number of messages from each rank - MPI_Alltoall(send_lengths.data(), 1, MPI_INT, num_recv_from_rank.data(), 1, MPI_INT, communicator); - auto const recv_buff_size = std::reduce(num_recv_from_rank.begin(), num_recv_from_rank.end(), 0); - - auto recv_packed_messages = std::vector(recv_buff_size, 0); - auto recv_offsets = std::vector(size, 0); - auto const recv_lengths = num_recv_from_rank; - - // Calculating recv offsets - std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), recv_offsets.begin(), 0); - - auto const mpi_error = MPI_Alltoallv(send_packed_messages.data(), send_lengths.data(), send_offsets.data(), - get_mpi_datatype(), recv_packed_messages.data(), recv_lengths.data(), - recv_offsets.data(), get_mpi_datatype(), - communicator); - if (mpi_error != MPI_SUCCESS) { - throw(std::runtime_error("MPI_collective_tools::all_to_all()")); - } - return mpi::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); +/** + * Unpacks a flat MPI packed message into a nested vector. + * + * This function transforms a packed MPI message, represented as an + * `mpi_packed_message`, back into a vector of vectors. + * The original structure of the nested container is reconstructed. + * + * The function deconstructs the `mpi_packed_message` into its constituent + * parts: + * - A flat buffer containing all elements. + * - Displacements indicating the start of each inner vector within the + * flat buffer. + * - Counts indicating the lengths of each inner vector. + * + * It handles two different approaches depending on whether the `_CRAYC` + * macro is defined: + * - If `_CRAYC` is defined, it uses vector insertion. + * - Otherwise, it uses `std::span` to subspan elements. + * + * @tparam Elem Type of elements contained in the message. + * @param packed_message The packed message to be unpacked. + * @return A nested vector of elements, reconstructing the original + * structure. + */ +template +auto unpack_messages(mpi_packed_message const &packed_message) + -> std::vector> { + auto const &[recv_buf, recv_displs, recv_counts] = packed_message; + int num_ranks = static_cast(recv_counts.size()); + + std::vector> result; + result.reserve(num_ranks); + if constexpr (_CRAYC) { + for (int i = 0; i < num_ranks; ++i) { + std::vector subvec{}; + subvec.insert(subvec.begin(), recv_buf.begin() + recv_displs[i], + recv_buf.begin() + recv_displs[i] + recv_counts[i]); + result.emplace_back(subvec.begin(), subvec.end()); } + } else { + auto const recv_span = std::span(recv_buf); + for (int i = 0; i < num_ranks; ++i) { + auto const subspan = + recv_span.subspan(recv_displs.at(i), recv_counts.at(i)); + result.emplace_back(subspan.begin(), subspan.end()); + } + } + + return result; } +/** + * @brief Performs an MPI all-to-all communication operation, distributing + * data from all processes to all processes. + * + * This function packs messages from the input data structure, performs an + * MPI all-to-all communication, and then unpacks the received messages. + * + * @param sends A structure containing the data to be sent from each + * process. + * @param communicator The MPI communicator used for the all-to-all + * operation. + * @return A vector of vectors, where each inner vector contains the data + * received by a process from other processes. + * @throws std::runtime_error if there's an inconsistency in the send + * offsets/lengths or if the MPI operation fails. + */ +template + requires container +auto all_to_all(Input const &sends, MPI_Comm communicator) + -> std::vector> { + using inner = typename Input::value_type; + using element_type = typename inner::value_type; + + PEID rank, size; + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); + + // Packing messages into vector and computing offsets and lengths for the sub + // messages + auto [send_packed_messages, send_offsets, send_lengths] = + mpi::pack_messages(sends); + + if (send_offsets.size() != send_lengths.size()) { + throw(std::runtime_error("MPI_collective_tools::pack_messages()")); + } else if ((send_offsets.size() != size) || (send_lengths.size() != size)) { + throw(std::runtime_error("MPI_collective_tools::pack_messages()")); + } + + // Preparing receive buffers for the node ids, offsets, and lengths + std::vector num_recv_from_rank(send_lengths.size(), + 0); // number of messages from each rank + MPI_Alltoall(send_lengths.data(), 1, MPI_INT, num_recv_from_rank.data(), 1, + MPI_INT, communicator); + auto const recv_buff_size = + std::reduce(num_recv_from_rank.begin(), num_recv_from_rank.end(), 0); + + auto recv_packed_messages = std::vector(recv_buff_size, 0); + auto recv_offsets = std::vector(size, 0); + auto const recv_lengths = num_recv_from_rank; + + // Calculating recv offsets + std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), + recv_offsets.begin(), 0); + + auto const mpi_error = MPI_Alltoallv( + send_packed_messages.data(), send_lengths.data(), send_offsets.data(), + get_mpi_datatype(), recv_packed_messages.data(), + recv_lengths.data(), recv_offsets.data(), + get_mpi_datatype(), communicator); + if (mpi_error != MPI_SUCCESS) { + throw(std::runtime_error("MPI_collective_tools::all_to_all()")); + } + return mpi::unpack_messages( + {recv_packed_messages, recv_offsets, recv_lengths}); +} +} // namespace mpi #endif /* end of include guard: MPI_TOOLS_HMESDXF2 */ diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 2e4076c8..9b702ffa 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -68,7 +68,7 @@ constexpr auto get_data_kind() -> mpi_data_kinds { return mpi_data_kinds::none; } } -} // namespace detail +} // namespace details template concept mpi_native_datatype = requires { @@ -80,67 +80,67 @@ concept mpi_composite_datatype = requires { details::get_data_kind() == details::mpi_data_kinds::composite; }; - template constexpr auto get_mpi_native_datatype() -> MPI_Datatype { - if constexpr (std::is_same_v) { - return MPI_CHAR; - } else if constexpr (std::is_same_v) { - return MPI_WCHAR; - } else if constexpr (std::is_same_v) { - return MPI_SHORT; - } else if constexpr (std::is_same_v) { - return MPI_INT; - } else if constexpr (std::is_same_v) { - return MPI_LONG; - } else if constexpr (std::is_same_v) { - return MPI_SIGNED_CHAR; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED_CHAR; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED_SHORT; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED_LONG; - } else if constexpr (std::is_same_v) { - return MPI_FLOAT; - } else if constexpr (std::is_same_v) { - return MPI_DOUBLE; - } else if constexpr (std::is_same_v) { - return MPI_LONG_DOUBLE; - } else if constexpr (std::is_same_v) { - return MPI_CXX_BOOL; - } else if constexpr (std::is_same_v) { - return MPI_INT8_T; - } else if constexpr (std::is_same_v) { - return MPI_INT16_T; - } else if constexpr (std::is_same_v) { - return MPI_INT32_T; - } else if constexpr (std::is_same_v) { - return MPI_INT64_T; - } else if constexpr (std::is_same_v) { - return MPI_UINT8_T; - } else if constexpr (std::is_same_v) { - return MPI_UINT16_T; - } else if constexpr (std::is_same_v) { - return MPI_UINT32_T; - } else if constexpr (std::is_same_v) { - return MPI_UINT64_T; - } else if constexpr (std::is_same_v) { - return MPI_LONG_LONG_INT; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED_LONG_LONG; - } else { - return MPI_DATATYPE_NULL; - }; + if constexpr (std::is_same_v) { + return MPI_CHAR; + } else if constexpr (std::is_same_v) { + return MPI_WCHAR; + } else if constexpr (std::is_same_v) { + return MPI_SHORT; + } else if constexpr (std::is_same_v) { + return MPI_INT; + } else if constexpr (std::is_same_v) { + return MPI_LONG; + } else if constexpr (std::is_same_v) { + return MPI_SIGNED_CHAR; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED_CHAR; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED_SHORT; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED_LONG; + } else if constexpr (std::is_same_v) { + return MPI_FLOAT; + } else if constexpr (std::is_same_v) { + return MPI_DOUBLE; + } else if constexpr (std::is_same_v) { + return MPI_LONG_DOUBLE; + } else if constexpr (std::is_same_v) { + return MPI_CXX_BOOL; + } else if constexpr (std::is_same_v) { + return MPI_INT8_T; + } else if constexpr (std::is_same_v) { + return MPI_INT16_T; + } else if constexpr (std::is_same_v) { + return MPI_INT32_T; + } else if constexpr (std::is_same_v) { + return MPI_INT64_T; + } else if constexpr (std::is_same_v) { + return MPI_UINT8_T; + } else if constexpr (std::is_same_v) { + return MPI_UINT16_T; + } else if constexpr (std::is_same_v) { + return MPI_UINT32_T; + } else if constexpr (std::is_same_v) { + return MPI_UINT64_T; + } else if constexpr (std::is_same_v) { + return MPI_LONG_LONG_INT; + } else if constexpr (std::is_same_v) { + return MPI_UNSIGNED_LONG_LONG; + } else { + return MPI_DATATYPE_NULL; + }; } template constexpr auto get_mpi_composite_datatype() -> MPI_Datatype; template -concept mpi_datatype = mpi_native_datatype || mpi_composite_datatype; +concept mpi_datatype = + mpi_native_datatype || mpi_composite_datatype; template concept container = requires(ContainerType a, ContainerType const b) { @@ -174,14 +174,16 @@ concept container = requires(ContainerType a, ContainerType const b) { template constexpr auto get_mpi_datatype() -> MPI_Datatype { - if constexpr (details::get_data_kind() == details::mpi_data_kinds::base) { + if constexpr (details::get_data_kind() == + details::mpi_data_kinds::base) { return get_mpi_native_datatype(); - } else if constexpr (details::get_data_kind() == details::mpi_data_kinds::composite) { - return get_mpi_composite_datatype(); // Point of customization for user-defined types + } else if constexpr (details::get_data_kind() == + details::mpi_data_kinds::composite) { + return get_mpi_composite_datatype(); // Point of customization + // for user-defined types } else { - return MPI_PACKED; // Fallback to serialization of object + return MPI_PACKED; // Fallback to serialization of object } - }; template diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index b7db74bf..6c0f9c36 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -58,113 +58,119 @@ void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicat } // MPI AlltoAll based implementation -void parallel_contraction::compute_label_mapping_collective(MPI_Comm communicator, parallel_graph_access &G, - NodeID &global_num_distinct_ids, - std::unordered_map &label_mapping) { - PEID rank, size; - MPI_Comm_rank(communicator, &rank); - MPI_Comm_size(communicator, &size); - - NodeID divisor = ceil(G.number_of_global_nodes() / static_cast(size)); - - helpers helper; - m_messages.clear(); - m_messages.resize(size); - - std::vector > filter; - filter.resize(size); - - forall_local_nodes(G, node) { - PEID peID = G.getNodeLabel(node) / divisor; - filter[ peID ][G.getNodeLabel(node)] = true; - } endfor - - for (PEID peID = 0; peID < size; peID++) { - std::unordered_map::iterator it; - for (it = filter.at(peID).begin(); it != filter.at(peID).end(); ++it) { - m_messages.at(peID).push_back(it->first); - } - } - - auto const local_labels_byPE = mpi::all_to_all(m_messages, communicator); - - std::vector local_labels; - for (PEID peID = 0; peID < size; peID++) { - for (ULONG i = 0; i < local_labels_byPE.at(peID).size(); i++) { - local_labels.push_back(local_labels_byPE.at(peID).at(i)); - } - } - - - // filter duplicates locally - helper.filter_duplicates(local_labels, - [](const NodeID &lhs, const NodeID &rhs) -> bool { - return (lhs < rhs); - }, - [](const NodeID &lhs, const NodeID &rhs) -> bool { - return (lhs == rhs); - }); - //afterwards they are sorted! - - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%Labels are unique on all PEs%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // now counting - - NodeID local_num_labels = local_labels.size(); - NodeID prefix_sum = 0; - - MPI_Scan(&local_num_labels, &prefix_sum, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - - global_num_distinct_ids = prefix_sum; - // Broadcast global number of ids - MPI_Bcast(&global_num_distinct_ids, 1, MPI_UNSIGNED_LONG_LONG, size - 1, communicator); - - NodeID num_smaller_ids = prefix_sum - local_num_labels; - - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%Now Build the mapping and send information back to PEs%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - // build the mapping locally - std::unordered_map< NodeID, NodeID > label_mapping_to_cnode; - NodeID cur_id = num_smaller_ids; - for( ULONG i = 0; i < local_labels.size(); i++) { - label_mapping_to_cnode[local_labels[i]] = cur_id++; - } - - // now send the processes the mapping back - //std::vector< std::vector< NodeID > > m_out_messages; - m_out_messages.clear(); - m_out_messages.resize(size); - - for (PEID peID = 0; peID < size; peID++) { - if (peID == rank) continue; - - if (local_labels_byPE.at(peID).empty()) { - m_out_messages.at(peID) = {}; - continue; - } - - for (ULONG i = 0; i < local_labels_byPE[peID].size(); i++) { - m_out_messages.at(peID).push_back(label_mapping_to_cnode.at(local_labels_byPE.at(peID).at(i))); - } - } - - auto recv_mapping = mpi::all_to_all(m_out_messages, communicator); - - // first the local labels - for (ULONG i = 0; i < m_messages.at(rank).size(); i++) { - label_mapping[m_messages.at(rank).at(i)] = label_mapping_to_cnode.at(m_messages.at(rank).at(i)); - } - - for (PEID peID = 0; peID < size; peID++) { - for (ULONG i = 0; i < recv_mapping.at(peID).size(); i++) { - label_mapping[m_messages.at(peID).at(i)] = recv_mapping.at(peID).at(i); - } - } +void parallel_contraction::compute_label_mapping_collective( + MPI_Comm communicator, + parallel_graph_access& G, + NodeID& global_num_distinct_ids, + std::unordered_map& label_mapping) { + PEID rank, size; + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); + + NodeID divisor = ceil(G.number_of_global_nodes() / static_cast(size)); + + helpers helper; + m_messages.clear(); + m_messages.resize(size); + + std::vector > filter; + filter.resize(size); + + forall_local_nodes(G, node) { + PEID peID = G.getNodeLabel(node) / divisor; + filter[peID][G.getNodeLabel(node)] = true; + } + endfor + + for (PEID peID = 0; peID < size; peID++) { + std::unordered_map::iterator it; + for (it = filter.at(peID).begin(); it != filter.at(peID).end(); ++it) { + m_messages.at(peID).push_back(it->first); + } + } + + auto const local_labels_byPE = mpi::all_to_all(m_messages, communicator); + + std::vector local_labels; + for (PEID peID = 0; peID < size; peID++) { + for (ULONG i = 0; i < local_labels_byPE.at(peID).size(); i++) { + local_labels.push_back(local_labels_byPE.at(peID).at(i)); + } + } + + // filter duplicates locally + helper.filter_duplicates( + local_labels, + [](const NodeID& lhs, const NodeID& rhs) -> bool { return (lhs < rhs); }, + [](const NodeID& lhs, const NodeID& rhs) -> bool { + return (lhs == rhs); + }); + // afterward they are sorted! + + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%Labels are unique on all PEs%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // now counting + + NodeID local_num_labels = local_labels.size(); + NodeID prefix_sum = 0; + + MPI_Scan(&local_num_labels, &prefix_sum, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, + communicator); + + global_num_distinct_ids = prefix_sum; + // Broadcast global number of ids + MPI_Bcast(&global_num_distinct_ids, 1, MPI_UNSIGNED_LONG_LONG, size - 1, + communicator); + + NodeID num_smaller_ids = prefix_sum - local_num_labels; + + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%Now Build the mapping and send information back to PEs%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + // build the mapping locally + std::unordered_map label_mapping_to_cnode; + NodeID cur_id = num_smaller_ids; + for (ULONG i = 0; i < local_labels.size(); i++) { + label_mapping_to_cnode[local_labels[i]] = cur_id++; + } + + // now send the processes the mapping back + // std::vector< std::vector< NodeID > > m_out_messages; + m_out_messages.clear(); + m_out_messages.resize(size); + + for (PEID peID = 0; peID < size; peID++) { + if (peID == rank) + continue; + + if (local_labels_byPE.at(peID).empty()) { + m_out_messages.at(peID) = {}; + continue; + } + + for (ULONG i = 0; i < local_labels_byPE[peID].size(); i++) { + m_out_messages.at(peID).push_back( + label_mapping_to_cnode.at(local_labels_byPE.at(peID).at(i))); + } + } + + auto recv_mapping = mpi::all_to_all(m_out_messages, communicator); + + // first the local labels + for (ULONG i = 0; i < m_messages.at(rank).size(); i++) { + label_mapping[m_messages.at(rank).at(i)] = + label_mapping_to_cnode.at(m_messages.at(rank).at(i)); + } + + for (PEID peID = 0; peID < size; peID++) { + for (ULONG i = 0; i < recv_mapping.at(peID).size(); i++) { + label_mapping[m_messages.at(peID).at(i)] = recv_mapping.at(peID).at(i); + } + } } // Original Implementation From 7d6d3a05676c673aa0b8eb2a11505bf82e92b62c Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 23 Sep 2024 14:56:08 +0100 Subject: [PATCH 20/64] Removed original implementation --- .../lib/communication/mpi_tools.h | 2 + .../parallel_contraction.cpp | 207 +----------------- 2 files changed, 6 insertions(+), 203 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 08294aa6..bf9eb52f 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -149,6 +149,7 @@ auto unpack_messages(mpi_packed_message const &packed_message) std::vector> result; result.reserve(num_ranks); if constexpr (_CRAYC) { + puts("Cray compiler detected"); for (int i = 0; i < num_ranks; ++i) { std::vector subvec{}; subvec.insert(subvec.begin(), recv_buf.begin() + recv_displs[i], @@ -157,6 +158,7 @@ auto unpack_messages(mpi_packed_message const &packed_message) } } else { auto const recv_span = std::span(recv_buf); + puts("Not using Cray compiler"); for (int i = 0; i < num_ranks; ++i) { auto const subspan = recv_span.subspan(recv_displs.at(i), recv_counts.at(i)); diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 6c0f9c36..9fd56742 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -12,8 +12,6 @@ #include "data_structure/hashed_graph.h" #include "tools/helpers.h" -#define CONTRACTION true - parallel_contraction::parallel_contraction() = default; parallel_contraction::~parallel_contraction() = default; @@ -58,7 +56,7 @@ void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicat } // MPI AlltoAll based implementation -void parallel_contraction::compute_label_mapping_collective( +void parallel_contraction::compute_label_mapping( MPI_Comm communicator, parallel_graph_access& G, NodeID& global_num_distinct_ids, @@ -79,10 +77,9 @@ void parallel_contraction::compute_label_mapping_collective( forall_local_nodes(G, node) { PEID peID = G.getNodeLabel(node) / divisor; filter[peID][G.getNodeLabel(node)] = true; - } - endfor + } endfor - for (PEID peID = 0; peID < size; peID++) { + for (PEID peID = 0; peID < size; peID++) { std::unordered_map::iterator it; for (it = filter.at(peID).begin(); it != filter.at(peID).end(); ++it) { m_messages.at(peID).push_back(it->first); @@ -173,203 +170,6 @@ void parallel_contraction::compute_label_mapping_collective( } } -// Original Implementation -void parallel_contraction::compute_label_mapping_original( MPI_Comm communicator, parallel_graph_access & G, - NodeID & global_num_distinct_ids, - std::unordered_map< NodeID, NodeID > & label_mapping ) { - PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - NodeID divisor = ceil( G.number_of_global_nodes()/ static_cast(size)); - - helpers helper; - m_messages.clear(); - m_messages.resize(size); - - std::vector< std::unordered_map< NodeID, bool > > filter; - filter.resize(size); - - forall_local_nodes(G, node) { - PEID peID = G.getNodeLabel(node) / divisor; - filter[ peID ][G.getNodeLabel(node)] = true; - } endfor - - for( PEID peID = 0; peID < size; peID++) { - std::unordered_map< NodeID, bool >::iterator it; - for( it = filter[peID].begin(); it != filter[peID].end(); it++) { - m_messages[peID].push_back(it->first); - } - } - - // now flood the network - for( PEID peID = 0; peID < size; peID++) { - if( peID != rank ) { - if( m_messages[peID].empty() ){ - m_messages[peID].push_back(std::numeric_limits::max()); - } - - MPI_Request rq; - MPI_Isend( &m_messages[peID][0], - m_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID+4*size, communicator, &rq); - } - } - std::vector< std::vector< NodeID > > local_labels_byPE; - local_labels_byPE.resize(size); - - for( ULONG i = 0; i < m_messages[rank].size(); i++) { - local_labels_byPE[rank].push_back(m_messages[rank][i]); - } - - - std::vector< std::vector< NodeID > > inc_messages; - inc_messages.resize(size); - - PEID counter = 0; - while( counter < size - 1) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+4*size, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector incmessage; incmessage.resize(message_length); - - MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+4*size, communicator, &rst); - counter++; - - PEID peID = st.MPI_SOURCE; - for( int i = 0; i < message_length; i++) { - inc_messages[peID].push_back(incmessage[i]); - } // store those because we need to send them their mapping back - - // now integrate the changes - if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do - - for( int i = 0; i < message_length; i++) { - local_labels_byPE[peID].push_back(incmessage[i]); - } - } - - std::vector< NodeID > local_labels; - for( PEID peID = 0; peID < size; peID++) { - for( ULONG i = 0; i < local_labels_byPE[peID].size(); i++) { - local_labels.push_back(local_labels_byPE[peID][i]); - } - } - - - // filter duplicates locally - helper.filter_duplicates( local_labels, - [](const NodeID & lhs, const NodeID & rhs) -> bool { - return (lhs < rhs); - }, - [](const NodeID & lhs, const NodeID & rhs) -> bool { - return (lhs == rhs); - }); - //afterwards they are sorted! - - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%Labels are unique on all PEs%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // now counting - - NodeID local_num_labels = local_labels.size(); - NodeID prefix_sum = 0; - - MPI_Scan(&local_num_labels, &prefix_sum, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - - global_num_distinct_ids = prefix_sum; - // Broadcast global number of ids - MPI_Bcast(&global_num_distinct_ids, 1, MPI_UNSIGNED_LONG_LONG, size-1, communicator); - - NodeID num_smaller_ids = prefix_sum - local_num_labels; - - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%Now Build the mapping and send information back to PEs%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - // build the mapping locally - std::unordered_map< NodeID, NodeID > label_mapping_to_cnode; - NodeID cur_id = num_smaller_ids; - for( ULONG i = 0; i < local_labels.size(); i++) { - label_mapping_to_cnode[local_labels[i]] = cur_id++; - } - - // now send the processes the mapping back - //std::vector< std::vector< NodeID > > m_out_messages; - m_out_messages.resize(size); - - for( PEID peID = 0; peID < (PEID)size; peID++) { - if( peID == rank ) continue; - - if( inc_messages[peID][0] == std::numeric_limits::max()) { - m_out_messages[peID].push_back(std::numeric_limits::max()); - continue; - } - - for( ULONG i = 0; i < inc_messages[peID].size(); i++) { - m_out_messages[peID].push_back( label_mapping_to_cnode[ inc_messages[peID][i] ] ); - } - } - - for( PEID peID = 0; peID < size; peID++) { - if( peID != rank ) { - MPI_Request rq; - MPI_Isend( &m_out_messages[peID][0], - m_out_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID+5*size, communicator, &rq); - } - } - - // first the local labels - for( ULONG i = 0; i < m_messages[rank].size(); i++) { - label_mapping[ m_messages[rank][i] ] = label_mapping_to_cnode[m_messages[rank][i]]; - } - - counter = 0; - while( counter < size - 1) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+5*size, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector incmessage; incmessage.resize(message_length); - - MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+5*size, communicator, &rst); - counter++; - - // now integrate the changes - if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do - - PEID peID = st.MPI_SOURCE; - for( int i = 0; i < message_length; i++) { - label_mapping[ m_messages[peID][i] ] = incmessage[i]; - } - } -} - - -// Testing Switch -void parallel_contraction::compute_label_mapping( MPI_Comm communicator, parallel_graph_access & G, - NodeID & global_num_distinct_ids, - std::unordered_map< NodeID, NodeID > & label_mapping ) { - if constexpr (CONTRACTION == true) { - parallel_contraction::compute_label_mapping_collective(communicator, G, global_num_distinct_ids, label_mapping); - } else { - parallel_contraction::compute_label_mapping_original(communicator, G, global_num_distinct_ids, label_mapping); - } - -}; - void parallel_contraction::get_nodes_to_cnodes_ghost_nodes( MPI_Comm communicator, parallel_graph_access & G ) { PEID rank, size; MPI_Comm_rank( communicator, &rank); @@ -486,6 +286,7 @@ void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( MPI NodeID divisor = ceil( number_of_cnodes/(double)size); //std::vector< std::vector< NodeID > > messages; + m_messages.clear(); m_messages.resize(size); //build messages From 03631d402e7ec35ee12a3cf4335b44d2c880d26c Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 23 Sep 2024 14:56:38 +0100 Subject: [PATCH 21/64] Fixed CMake so that flags work correctly --- parallel/parallel_src/CMakeLists.txt | 36 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index 54225e30..2ef0d2b7 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -35,7 +35,7 @@ set(LIBPARALLEL_SOURCE_FILES lib/tools/random_functions.cpp lib/tools/distributed_quality_metrics.cpp extern/argtable3-3.2.2/argtable3.c) -add_library(libparallel OBJECT ${LIBPARALLEL_SOURCE_FILES}) +add_library(libparallel ${LIBPARALLEL_SOURCE_FILES}) target_include_directories(libparallel PUBLIC $) set(LIBGRAPH2BGF_SOURCE_FILES @@ -44,7 +44,7 @@ set(LIBGRAPH2BGF_SOURCE_FILES lib/data_structure/balance_management.cpp lib/data_structure/balance_management_refinement.cpp lib/data_structure/balance_management_coarsening.cpp) -add_library(libgraph2bgf OBJECT ${LIBGRAPH2BGF_SOURCE_FILES}) +add_library(libgraph2bgf ${LIBGRAPH2BGF_SOURCE_FILES}) set(LIBEDGELIST_SOURCE_FILES lib/data_structure/parallel_graph_access.cpp @@ -53,38 +53,44 @@ set(LIBEDGELIST_SOURCE_FILES lib/data_structure/balance_management_refinement.cpp lib/data_structure/balance_management_coarsening.cpp extern/argtable3-3.2.2/argtable3.c) -add_library(libedgelist OBJECT ${LIBEDGELIST_SOURCE_FILES}) +add_library(libedgelist STATIC ${LIBEDGELIST_SOURCE_FILES}) set(LIBDSPAC_SOURCE_FILES lib/dspac/dspac.cpp lib/dspac/edge_balanced_graph_io.cpp) -add_library(libdspac OBJECT ${LIBDSPAC_SOURCE_FILES}) +add_library(libdspac STATIC ${LIBDSPAC_SOURCE_FILES}) -add_executable(parhip app/parhip.cpp $) +add_executable(parhip app/parhip.cpp) target_compile_definitions(parhip PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_link_libraries(parhip PRIVATE libmodified_kahip_interface) +target_link_libraries(parhip PRIVATE libparallel) install(TARGETS parhip DESTINATION bin) -add_executable(toolbox app/toolbox.cpp $) +add_executable(toolbox app/toolbox.cpp) target_compile_definitions(toolbox PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DTOOLBOX") target_link_libraries(toolbox PRIVATE libmodified_kahip_interface) +target_link_libraries(toolbox PRIVATE libparallel) install(TARGETS toolbox DESTINATION bin) -add_executable(graph2binary app/graph2binary.cpp $) +add_executable(graph2binary app/graph2binary.cpp) target_compile_definitions(graph2binary PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION -DGRAPH2DGF") +target_link_libraries(graph2binary PRIVATE libgraph2bgf) install(TARGETS graph2binary DESTINATION bin) -add_executable(graph2binary_external app/graph2binary_external.cpp $) +add_executable(graph2binary_external app/graph2binary_external.cpp) target_compile_definitions(graph2binary_external PRIVATE "-DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION -DGRAPH2DGF") +target_link_libraries(graph2binary_external PRIVATE libgraph2bgf) install(TARGETS graph2binary_external DESTINATION bin) -add_executable(readbgf app/readbgf.cpp $) +add_executable(readbgf app/readbgf.cpp) target_compile_definitions(readbgf PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") +target_link_libraries(readbgf PRIVATE libgraph2bgf) install(TARGETS readbgf DESTINATION bin) -add_executable(edge_list_to_metis_graph app/edge_list_to_metis_graph.cpp $) +add_executable(edge_list_to_metis_graph app/edge_list_to_metis_graph.cpp) target_compile_definitions(edge_list_to_metis_graph PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DKRONECKER_GENERATOR_PROGRAM") target_link_libraries(edge_list_to_metis_graph PRIVATE libmodified_kahip_interface) +target_link_libraries(edge_list_to_metis_graph PRIVATE libedgelist) install(TARGETS edge_list_to_metis_graph DESTINATION bin) #add_executable(friendster_list_to_metis_graph app/friendster_list_to_metis_graph.cpp $) @@ -92,25 +98,29 @@ install(TARGETS edge_list_to_metis_graph DESTINATION bin) #target_link_libraries(edge_list_to_metis_graph PRIVATE libmodified_kahip_interface) #install(TARGETS friendster_list_to_metis_graph DESTINATION bin) -add_executable(dspac app/dspac.cpp $ $) +add_executable(dspac app/dspac.cpp) target_compile_definitions(dspac PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_link_libraries(dspac PRIVATE libmodified_kahip_interface) +target_link_libraries(dspac PRIVATE libparallel) +target_link_libraries(dspac PRIVATE libdspac) install(TARGETS dspac DESTINATION bin) -add_library(parhip_interface SHARED interface/parhip_interface.cpp $) +add_library(parhip_interface SHARED interface/parhip_interface.cpp) target_compile_definitions(parhip_interface PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_include_directories(parhip_interface PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_link_libraries(parhip_interface PRIVATE libmodified_kahip_interface) +target_link_libraries(parhip_interface PRIVATE libparallel) set_target_properties(parhip_interface PROPERTIES PUBLIC_HEADER interface/parhip_interface.h) install(TARGETS parhip_interface LIBRARY DESTINATION lib PUBLIC_HEADER DESTINATION include ) -add_library(parhip_interface_static interface/parhip_interface.cpp $) +add_library(parhip_interface_static interface/parhip_interface.cpp) target_compile_definitions(parhip_interface_static PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_include_directories(parhip_interface_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_link_libraries(parhip_interface_static PRIVATE libmodified_kahip_interface) +target_link_libraries(parhip_interface_static PRIVATE libparallel) install(TARGETS parhip_interface_static DESTINATION lib) From 9d168b85090981e3a7ce2829824ec09015c6ae2c Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 23 Sep 2024 15:05:31 +0100 Subject: [PATCH 22/64] Removed unused mpi_tools::alltoallv --- .../lib/communication/mpi_tools.cpp | 41 ------------------- .../lib/communication/mpi_tools.h | 21 ++-------- 2 files changed, 3 insertions(+), 59 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.cpp b/parallel/parallel_src/lib/communication/mpi_tools.cpp index 218bdaef..6496c1a7 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.cpp +++ b/parallel/parallel_src/lib/communication/mpi_tools.cpp @@ -208,44 +208,3 @@ void mpi_tools::distribute_local_graph( MPI_Comm communicator, PPartitionConfig delete[] adjwgt; } -void mpi_tools::alltoallv( void * sendbuf, - ULONG sendcounts[], ULONG displs[], - const MPI_Datatype & sendtype, void * recvbuf, - ULONG recvcounts[], ULONG rdispls[], - const MPI_Datatype & recvtype, MPI_Comm communicator ) { - - int rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - bool no_special_case = true; - for( int i = 0; i < size && no_special_case; i++) { - if( sendcounts[i] > std::numeric_limits< int >::max()) no_special_case = false; - if( recvcounts[i] > std::numeric_limits< int >::max()) no_special_case = false; - } - if( displs[size] > std::numeric_limits< int >::max()) no_special_case = false; - if( rdispls[size] > std::numeric_limits< int >::max()) no_special_case = false; - - if( no_special_case ) { - int sbktsize[size]; - int rbktsize[size]; - int sdispl[size+1]; - int rdispl[size+1]; - - for( int i = 0; i < size; i++) { - sbktsize[i] = sendcounts[i]; - rbktsize[i] = recvcounts[i]; - } - - for( int i = 0; i <= size; i++) { - sdispl[i] = displs[i]; - rdispl[i] = rdispls[i]; - } - - MPI_Alltoallv(sendbuf, sbktsize, sdispl, MPI_UNSIGNED_LONG_LONG, - recvbuf, rbktsize, rdispl, MPI_UNSIGNED_LONG_LONG, communicator); - } else { - if( rank == ROOT ) { std::cout << "special case all to all with counts > sizeof(int)! not tested yet!" << std::endl; exit(0);} - } -} - diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index bf9eb52f..d618a352 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -36,19 +36,6 @@ class mpi_tools { void distribute_local_graph(MPI_Comm communicator, PPartitionConfig &config, complete_graph_access &G); - // alltoallv that can send more than int-count elements - void alltoallv(void *sendbuf, ULONG sendcounts[], ULONG displs[], - const MPI_Datatype &sendtype, void *recvbuf, - ULONG recvcounts[], ULONG rdispls[], - const MPI_Datatype &recvtype) { - alltoallv(sendbuf, sendcounts, displs, sendtype, recvbuf, recvcounts, - rdispls, recvtype, MPI_COMM_WORLD); - }; - - void alltoallv(void *sendbuf, ULONG sendcounts[], ULONG displs[], - const MPI_Datatype &sendtype, void *recvbuf, - ULONG recvcounts[], ULONG rdispls[], - const MPI_Datatype &recvtype, MPI_Comm communicator); }; namespace mpi { @@ -149,7 +136,6 @@ auto unpack_messages(mpi_packed_message const &packed_message) std::vector> result; result.reserve(num_ranks); if constexpr (_CRAYC) { - puts("Cray compiler detected"); for (int i = 0; i < num_ranks; ++i) { std::vector subvec{}; subvec.insert(subvec.begin(), recv_buf.begin() + recv_displs[i], @@ -158,7 +144,6 @@ auto unpack_messages(mpi_packed_message const &packed_message) } } else { auto const recv_span = std::span(recv_buf); - puts("Not using Cray compiler"); for (int i = 0; i < num_ranks; ++i) { auto const subspan = recv_span.subspan(recv_displs.at(i), recv_counts.at(i)); @@ -202,9 +187,9 @@ auto all_to_all(Input const &sends, MPI_Comm communicator) mpi::pack_messages(sends); if (send_offsets.size() != send_lengths.size()) { - throw(std::runtime_error("MPI_collective_tools::pack_messages()")); + throw(std::runtime_error("mpi::pack_messages(): send_offsets.size() != send_lengths.size()")); } else if ((send_offsets.size() != size) || (send_lengths.size() != size)) { - throw(std::runtime_error("MPI_collective_tools::pack_messages()")); + throw(std::runtime_error("mpi::pack_messages(): send_offsets.size() != mpi size")); } // Preparing receive buffers for the node ids, offsets, and lengths @@ -229,7 +214,7 @@ auto all_to_all(Input const &sends, MPI_Comm communicator) recv_lengths.data(), recv_offsets.data(), get_mpi_datatype(), communicator); if (mpi_error != MPI_SUCCESS) { - throw(std::runtime_error("MPI_collective_tools::all_to_all()")); + throw(std::runtime_error("mpi::all_to_all()")); } return mpi::unpack_messages( {recv_packed_messages, recv_offsets, recv_lengths}); From 42664b858f0643012c8f07475eae60e6124ece3a Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 23 Sep 2024 17:06:07 +0100 Subject: [PATCH 23/64] Removing Duplicated function from class header --- .../lib/communication/mpi_tools.h | 2 +- .../data_structure/parallel_graph_access.h | 3 +- .../parallel_contraction.cpp | 13 ++-- .../parallel_contraction.h | 77 +++++++++---------- .../parallel_projection.cpp | 13 ++-- 5 files changed, 51 insertions(+), 57 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index d618a352..c5f32b1e 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -196,7 +196,7 @@ auto all_to_all(Input const &sends, MPI_Comm communicator) std::vector num_recv_from_rank(send_lengths.size(), 0); // number of messages from each rank MPI_Alltoall(send_lengths.data(), 1, MPI_INT, num_recv_from_rank.data(), 1, - MPI_INT, communicator); + MPI_INT, communicator); // Check send count number auto const recv_buff_size = std::reduce(num_recv_from_rank.begin(), num_recv_from_rank.end(), 0); diff --git a/parallel/parallel_src/lib/data_structure/parallel_graph_access.h b/parallel/parallel_src/lib/data_structure/parallel_graph_access.h index 7e73ffa9..77053bd6 100644 --- a/parallel/parallel_src/lib/data_structure/parallel_graph_access.h +++ b/parallel/parallel_src/lib/data_structure/parallel_graph_access.h @@ -24,6 +24,7 @@ struct Node { EdgeID firstEdge; }; + struct NodeData { NodeID label; PartitionID block; // a given partition of the graph (for v-cycles) @@ -89,7 +90,7 @@ class ghost_node_communication { }; - virtual ~ghost_node_communication() {}; + ~ghost_node_communication() = default; inline void setGraphReference( parallel_graph_access * G ) { diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 9fd56742..1e5fb299 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -12,10 +12,6 @@ #include "data_structure/hashed_graph.h" #include "tools/helpers.h" -parallel_contraction::parallel_contraction() = default; - -parallel_contraction::~parallel_contraction() = default; - void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G, parallel_graph_access & Q) { @@ -79,7 +75,7 @@ void parallel_contraction::compute_label_mapping( filter[peID][G.getNodeLabel(node)] = true; } endfor - for (PEID peID = 0; peID < size; peID++) { + for (PEID peID = 0; peID < size; peID++) { std::unordered_map::iterator it; for (it = filter.at(peID).begin(); it != filter.at(peID).end(); ++it) { m_messages.at(peID).push_back(it->first); @@ -176,6 +172,7 @@ void parallel_contraction::get_nodes_to_cnodes_ghost_nodes( MPI_Comm communicato MPI_Comm_size( communicator, &size); std::vector< bool > PE_packed( size, false ); + m_send_buffers.clear(); m_send_buffers.resize( size ); forall_local_nodes(G, node) { @@ -314,9 +311,9 @@ void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( MPI } MPI_Request rq; - MPI_Isend( &m_messages[peID][0], - m_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, + MPI_Isend( &m_messages[peID][0], + m_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, peID, peID+7*size, communicator, &rq); } } diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h index acb502a5..371a4f51 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h @@ -13,47 +13,42 @@ #include "partition_config.h" class parallel_contraction { -public: - parallel_contraction(); - virtual ~parallel_contraction(); - - void contract_to_distributed_quotient( MPI_Comm communicator, PPartitionConfig & config, - parallel_graph_access & G, - parallel_graph_access & Q); -private: - // Temporary Duplicated function - void compute_label_mapping_collective( MPI_Comm communicator, parallel_graph_access & G, - NodeID & global_num_distinct_ids, - std::unordered_map< NodeID, NodeID > & label_mapping); - void compute_label_mapping_original( MPI_Comm communicator, parallel_graph_access & G, - NodeID & global_num_distinct_ids, - std::unordered_map< NodeID, NodeID > & label_mapping); - // compute mapping of labels id into contiguous intervall [0, ...., num_lables) - void compute_label_mapping( MPI_Comm communicator, parallel_graph_access & G, - NodeID & global_num_distinct_ids, - std::unordered_map< NodeID, NodeID > & label_mapping); - - void get_nodes_to_cnodes_ghost_nodes( MPI_Comm communicator, parallel_graph_access & G ); - - void build_quotient_graph_locally( parallel_graph_access & G, - NodeID number_of_distinct_labels, - hashed_graph & hG, - std::unordered_map< NodeID, NodeWeight > & node_weights); - - void redistribute_hased_graph_and_build_graph_locally( MPI_Comm communicator, hashed_graph & hG, - std::unordered_map< NodeID, NodeWeight > & node_weights, - NodeID number_of_cnodes, - parallel_graph_access & Q); - - void update_ghost_nodes_weights( MPI_Comm communicator, parallel_graph_access & G ); - - // some send buffers - std::vector< std::vector< NodeID > > m_messages; - std::vector< std::vector< NodeID > > m_out_messages; - std::vector< std::vector< NodeID > > m_send_buffers; // buffers to send messages + public: + void contract_to_distributed_quotient(MPI_Comm communicator, + PPartitionConfig& config, + parallel_graph_access& G, + parallel_graph_access& Q); + + private: + // compute mapping of labels id into contiguous intervall [0,...,num_lables) + void compute_label_mapping(MPI_Comm communicator, + parallel_graph_access& G, + NodeID& global_num_distinct_ids, + std::unordered_map& label_mapping); + + void get_nodes_to_cnodes_ghost_nodes(MPI_Comm communicator, + parallel_graph_access& G); + + void build_quotient_graph_locally( + parallel_graph_access& G, + NodeID number_of_distinct_labels, + hashed_graph& hG, + std::unordered_map& node_weights); + + void redistribute_hased_graph_and_build_graph_locally( + MPI_Comm communicator, + hashed_graph& hG, + std::unordered_map& node_weights, + NodeID number_of_cnodes, + parallel_graph_access& Q); + + void update_ghost_nodes_weights(MPI_Comm communicator, + parallel_graph_access& G); + + // some send buffers + std::vector> m_messages; + std::vector> m_out_messages; + std::vector> m_send_buffers; // buffers to send messages }; -auto flatten_messages(std::vector> const& messages ) -> std::tuple< std::vector, std::vector, std::vector>; - - #endif /* end of include guard: PARALLEL_CONTRACTION_64O127GD */ diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp index 70032512..45fe27ae 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp @@ -66,15 +66,15 @@ void parallel_projection::parallel_project( MPI_Comm communicator, parallel_grap // wait for incomming message of an adjacent processor MPI_Status st; MPI_Probe(MPI_ANY_SOURCE, rank+size, communicator, &st); - + int message_length; MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); std::vector incmessage; incmessage.resize(message_length); MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+size, communicator, &rst); + MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+size, communicator, &rst); counter++; - + //----- size of messeges PEID peID = st.MPI_SOURCE; // now integrate the changes if( incmessage[0] == std::numeric_limits< NodeID >::max()) { @@ -89,17 +89,18 @@ void parallel_projection::parallel_project( MPI_Comm communicator, parallel_grap } + for( int i = 0; i < message_length; i++) { NodeID cnode = coarser.getLocalID(incmessage[i]); out_messages[peID].push_back(coarser.getNodeLabel(cnode)); } - + // -- Handling messages and building response MPI_Request rq; MPI_Isend( &out_messages[peID][0], out_messages[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, peID+2*size, communicator, &rq); - + // -- Sending responses } counter = 0; @@ -115,7 +116,7 @@ void parallel_projection::parallel_project( MPI_Comm communicator, parallel_grap MPI_Status rst; MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, tag, communicator, &rst); counter++; - + // -- get sizes and revcs // now integrate the changes if( incmessage[0] == std::numeric_limits< NodeID >::max()) { continue; // nothing to do From f403eca0a2d66b2bdfca1a326c5d6d2a7c2a8422 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Wed, 25 Sep 2024 17:44:36 +0100 Subject: [PATCH 24/64] Custom MPI Datatypes --- .../lib/communication/mpi_tools.cpp | 108 +++--- .../lib/communication/mpi_tools.h | 196 ++++------ .../lib/communication/mpi_types.h | 357 ++++++++++-------- .../parallel_contraction.cpp | 19 +- .../parallel_contraction.h | 40 ++ .../parallel_contraction_mpi_test.cpp | 45 ++- .../parallel_contraction_test.cpp | 165 ++++---- 7 files changed, 484 insertions(+), 446 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.cpp b/parallel/parallel_src/lib/communication/mpi_tools.cpp index 6496c1a7..8352797e 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.cpp +++ b/parallel/parallel_src/lib/communication/mpi_tools.cpp @@ -157,54 +157,64 @@ void mpi_tools::collect_parallel_graph_to_local_graph( MPI_Comm communicator, PP -void mpi_tools::distribute_local_graph( MPI_Comm communicator, PPartitionConfig & config, - complete_graph_access & G) { - - int rank; - MPI_Comm_rank( communicator, &rank); - - //first B-Cast number of nodes and number of edges - ULONG number_of_nodes = 0; - ULONG number_of_edges = 0; - - std::vector< int > buffer(2,0); - if(rank == static_cast(ROOT)) { - buffer[0] = G.number_of_global_nodes(); - buffer[1] = G.number_of_global_edges(); - } - MPI_Bcast(&buffer[0], 2, MPI_INT, ROOT, communicator); - - number_of_nodes = buffer[0]; - number_of_edges = buffer[1]; - - int* xadj; - int* adjncy; - int* vwgt; - int* adjwgt; - - if( rank == static_cast(ROOT)) { - xadj = G.UNSAFE_metis_style_xadj_array(); - adjncy = G.UNSAFE_metis_style_adjncy_array(); - - vwgt = G.UNSAFE_metis_style_vwgt_array(); - adjwgt = G.UNSAFE_metis_style_adjwgt_array(); - } else { - xadj = new int[number_of_nodes+1]; - adjncy = new int[number_of_edges]; - - vwgt = new int[number_of_nodes]; - adjwgt = new int[number_of_edges]; - } - MPI_Bcast(xadj, number_of_nodes+1, MPI_INT, ROOT, communicator); - MPI_Bcast(adjncy, number_of_edges, MPI_INT, ROOT, communicator); - MPI_Bcast(vwgt, number_of_nodes, MPI_INT, ROOT, communicator); - MPI_Bcast(adjwgt, number_of_edges, MPI_INT, ROOT, communicator); - - G.build_from_metis_weighted( number_of_nodes, xadj, adjncy, vwgt, adjwgt); - - delete[] xadj; - delete[] adjncy; - delete[] vwgt; - delete[] adjwgt; +void mpi_tools::distribute_local_graph(MPI_Comm communicator, + PPartitionConfig& config, + complete_graph_access& G) { + int rank; + MPI_Comm_rank(communicator, &rank); + + // first B-Cast number of nodes and number of edges + ULONG number_of_nodes = 0; + ULONG number_of_edges = 0; + + std::vector buffer(2, 0); + if (rank == static_cast(ROOT)) { + buffer[0] = G.number_of_global_nodes(); + buffer[1] = G.number_of_global_edges(); + } + MPI_Bcast(&buffer[0], 2, MPI_INT, ROOT, communicator); + + number_of_nodes = buffer[0]; + number_of_edges = buffer[1]; + + int* xadj; + int* adjncy; + int* vwgt; + int* adjwgt; + + if (rank == static_cast(ROOT)) { + xadj = G.UNSAFE_metis_style_xadj_array(); + adjncy = G.UNSAFE_metis_style_adjncy_array(); + + vwgt = G.UNSAFE_metis_style_vwgt_array(); + adjwgt = G.UNSAFE_metis_style_adjwgt_array(); + } else { + xadj = new int[number_of_nodes + 1]; + adjncy = new int[number_of_edges]; + + vwgt = new int[number_of_nodes]; + adjwgt = new int[number_of_edges]; + } + MPI_Bcast(xadj, number_of_nodes + 1, MPI_INT, ROOT, communicator); + MPI_Bcast(adjncy, number_of_edges, MPI_INT, ROOT, communicator); + MPI_Bcast(vwgt, number_of_nodes, MPI_INT, ROOT, communicator); + MPI_Bcast(adjwgt, number_of_edges, MPI_INT, ROOT, communicator); + + G.build_from_metis_weighted(number_of_nodes, xadj, adjncy, vwgt, adjwgt); + + delete[] xadj; + delete[] adjncy; + delete[] vwgt; + delete[] adjwgt; +} +auto mpi::exchange_num_messages(std::vector const& num_sent_per_rank, + MPI_Comm communicator) -> std::vector { + PEID size; + MPI_Comm_size(communicator, &size); + // Preparing receive buffers for the lengths + std::vector num_recv_from_rank(size, 0); + MPI_Alltoall(num_sent_per_rank.data(), 1, MPI_INT, num_recv_from_rank.data(), + 1, MPI_INT, communicator); + return num_recv_from_rank; } diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index c5f32b1e..40b0afa2 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -46,110 +46,57 @@ template struct mpi_packed_message { std::vector lengths; }; -/** - * Packs a container of containers into a single flat vector suitable for - * MPI communication. - * - * This function takes a nested container (e.g., - * `std::vector>`) and flattens it into a single - * `std::vector`. It also calculates the lengths and offsets necessary to - * reconstruct the original nested container structure. - * - * Transformations: - * - Flattens the nested container using `std::ranges::views::join`. - * - Converts the flattened view into a `std::vector`. - * - Calculates the lengths of the inner containers. - * - Calculates the offsets for each inner container using exclusive_scan. - * - * Requirements: - * - `Input` must be a container of containers. - * - The inner containers must support the `value_type` type alias. - * - * @param messages The input container of containers to be packed. - * @return A `mpi_packed_message` object containing: - * - `flattened_vector`: A single vector containing all elements of - * the nested containers. - * - `offsets`: A vector of offsets for where each inner container - * starts in the flattened vector. - * - `lengths`: A vector of lengths for each inner container in the - * original nested container. - */ -template - requires container -auto pack_messages(Input const &messages) - -> mpi_packed_message { - using inner = typename Input::value_type; - using element_type = typename inner::value_type; - - // Flattening the container of containers using join_view - auto const flattened_view = messages | std::ranges::views::join; - std::vector flattened_vector{}; - // converting view into vector - for (auto &&elem : flattened_view) { - flattened_vector.push_back(static_cast(elem)); - } +auto exchange_num_messages(std::vector const& num_sent_per_rank, + MPI_Comm communicator) -> std::vector; + + +template + requires std::ranges::forward_range> +auto pack_messages(const Input& messages) + -> mpi_packed_message>> { + using InnerRange = std::ranges::range_value_t; + using ElementType = std::ranges::range_value_t; - // Calculating lengths using transform view + // Flattening the container of containers using views::join + auto flattened_view = messages | std::ranges::views::join; + std::vector flattened_vector{flattened_view.begin(), flattened_view.end()}; + + // Calculating lengths of the inner ranges std::vector lengths; - lengths.reserve(messages.size()); - std::ranges::transform( - messages, std::back_inserter(lengths), - [](auto const &elem) { return static_cast(elem.size()); }); + lengths.reserve(std::ranges::distance(messages)); + for (const auto& inner : messages) { + lengths.push_back(static_cast(std::ranges::distance(inner))); + } // Calculating offsets using exclusive_scan - std::vector offsets(messages.size()); + std::vector offsets(lengths.size()); std::exclusive_scan(lengths.begin(), lengths.end(), offsets.begin(), 0); - return mpi_packed_message{flattened_vector, offsets, lengths}; + return mpi_packed_message{flattened_vector, offsets, lengths}; } -/** - * Unpacks a flat MPI packed message into a nested vector. - * - * This function transforms a packed MPI message, represented as an - * `mpi_packed_message`, back into a vector of vectors. - * The original structure of the nested container is reconstructed. - * - * The function deconstructs the `mpi_packed_message` into its constituent - * parts: - * - A flat buffer containing all elements. - * - Displacements indicating the start of each inner vector within the - * flat buffer. - * - Counts indicating the lengths of each inner vector. - * - * It handles two different approaches depending on whether the `_CRAYC` - * macro is defined: - * - If `_CRAYC` is defined, it uses vector insertion. - * - Otherwise, it uses `std::span` to subspan elements. - * - * @tparam Elem Type of elements contained in the message. - * @param packed_message The packed message to be unpacked. - * @return A nested vector of elements, reconstructing the original - * structure. - */ + template -auto unpack_messages(mpi_packed_message const &packed_message) +auto unpack_messages(const mpi_packed_message& packed_message) -> std::vector> { - auto const &[recv_buf, recv_displs, recv_counts] = packed_message; - int num_ranks = static_cast(recv_counts.size()); + const auto& [recv_buf, recv_displs, recv_counts] = packed_message; + std::size_t num_ranks = recv_counts.size(); + + // Ensure recv_displs and recv_counts have the same size + assert(recv_displs.size() == num_ranks); std::vector> result; result.reserve(num_ranks); - if constexpr (_CRAYC) { - for (int i = 0; i < num_ranks; ++i) { - std::vector subvec{}; - subvec.insert(subvec.begin(), recv_buf.begin() + recv_displs[i], - recv_buf.begin() + recv_displs[i] + recv_counts[i]); - result.emplace_back(subvec.begin(), subvec.end()); - } - } else { - auto const recv_span = std::span(recv_buf); - for (int i = 0; i < num_ranks; ++i) { - auto const subspan = - recv_span.subspan(recv_displs.at(i), recv_counts.at(i)); - result.emplace_back(subspan.begin(), subspan.end()); - } - } + + // Use std::transform to construct the sub-vectors + std::transform( + recv_displs.begin(), recv_displs.end(), recv_counts.begin(), + std::back_inserter(result), + [&](int displ, int count) { + auto const start = recv_buf.begin() + displ; + auto const end = start + count; + return std::vector(start, end); + }); return result; } @@ -170,54 +117,57 @@ auto unpack_messages(mpi_packed_message const &packed_message) * @throws std::runtime_error if there's an inconsistency in the send * offsets/lengths or if the MPI operation fails. */ -template - requires container -auto all_to_all(Input const &sends, MPI_Comm communicator) - -> std::vector> { - using inner = typename Input::value_type; - using element_type = typename inner::value_type; +template + requires std::ranges::forward_range> +auto all_to_all(const Input& sends, MPI_Comm communicator) + -> std::vector>>> { + using InnerRange = std::ranges::range_value_t; + using ElementType = std::ranges::range_value_t; PEID rank, size; MPI_Comm_rank(communicator, &rank); MPI_Comm_size(communicator, &size); - // Packing messages into vector and computing offsets and lengths for the sub - // messages - auto [send_packed_messages, send_offsets, send_lengths] = - mpi::pack_messages(sends); + // Packing messages + auto [send_packed_messages, send_offsets, send_lengths] = mpi::pack_messages(sends); if (send_offsets.size() != send_lengths.size()) { - throw(std::runtime_error("mpi::pack_messages(): send_offsets.size() != send_lengths.size()")); - } else if ((send_offsets.size() != size) || (send_lengths.size() != size)) { - throw(std::runtime_error("mpi::pack_messages(): send_offsets.size() != mpi size")); + throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + std::to_string(send_offsets.size()) + + ") != send_lengths.size() (" + std::to_string(send_lengths.size()) + ")"); + } else if ((send_offsets.size() != static_cast(size)) || (send_lengths.size() != static_cast(size))) { + throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + std::to_string(send_offsets.size()) + + ") != mpi size (" + std::to_string(size) + ")"); } - // Preparing receive buffers for the node ids, offsets, and lengths - std::vector num_recv_from_rank(send_lengths.size(), - 0); // number of messages from each rank - MPI_Alltoall(send_lengths.data(), 1, MPI_INT, num_recv_from_rank.data(), 1, - MPI_INT, communicator); // Check send count number - auto const recv_buff_size = - std::reduce(num_recv_from_rank.begin(), num_recv_from_rank.end(), 0); + // Exchanging message sizes + auto const recv_lengths = exchange_num_messages(send_lengths, communicator); - auto recv_packed_messages = std::vector(recv_buff_size, 0); - auto recv_offsets = std::vector(size, 0); - auto const recv_lengths = num_recv_from_rank; + // Calculating total receive buffer size + auto const recv_buff_size = std::accumulate(recv_lengths.begin(), recv_lengths.end(), 0); - // Calculating recv offsets - std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), - recv_offsets.begin(), 0); + // Preparing receive buffers + std::vector recv_packed_messages(recv_buff_size); + std::vector recv_offsets(size, 0); - auto const mpi_error = MPI_Alltoallv( + // Calculating receive offsets + std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), recv_offsets.begin(), 0); + + // Performing MPI communication + auto mpi_error = MPI_Alltoallv( send_packed_messages.data(), send_lengths.data(), send_offsets.data(), - get_mpi_datatype(), recv_packed_messages.data(), + get_mpi_datatype(), recv_packed_messages.data(), recv_lengths.data(), recv_offsets.data(), - get_mpi_datatype(), communicator); + get_mpi_datatype(), communicator); + if (mpi_error != MPI_SUCCESS) { - throw(std::runtime_error("mpi::all_to_all()")); + char error_string[MPI_MAX_ERROR_STRING]; + int length_of_error_string; + MPI_Error_string(mpi_error, error_string, &length_of_error_string); + throw std::runtime_error(std::string("mpi::all_to_all() failed with error: ") + error_string); } - return mpi::unpack_messages( - {recv_packed_messages, recv_offsets, recv_lengths}); + + // Unpacking messages + return mpi::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); } } // namespace mpi diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 9b702ffa..7a752194 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -2,193 +2,216 @@ // Created by Erich Essmann on 03/09/2024. // -#ifndef MPI_TYPES_H -#define MPI_TYPES_H -#include +// mpi_datatype_trait.h +#pragma once + +#include #include +#include +#include #include -#include namespace mpi { namespace details { +// Define an enum to represent the kind of MPI data enum class mpi_data_kinds { none, base, composite }; +// Trait to determine the MPI data kind template -constexpr auto get_data_kind() -> mpi_data_kinds { - if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else if constexpr (std::is_same_v) { - return mpi_data_kinds::base; - } else { - return mpi_data_kinds::none; - } -} -} // namespace details +struct mpi_data_kind_trait { + static constexpr mpi_data_kinds kind = mpi_data_kinds::none; +}; + +// Macro to specialize mpi_data_kind_trait for unique base types +#define MPI_BASE_TYPE_KIND(type) \ + template <> \ + struct mpi_data_kind_trait { \ + static constexpr mpi_data_kinds kind = mpi_data_kinds::base; \ + }; + +// Specializations for unique base types +MPI_BASE_TYPE_KIND(char) +MPI_BASE_TYPE_KIND(wchar_t) +MPI_BASE_TYPE_KIND(float) +MPI_BASE_TYPE_KIND(double) +MPI_BASE_TYPE_KIND(long double) +MPI_BASE_TYPE_KIND(bool) +MPI_BASE_TYPE_KIND(int8_t) +MPI_BASE_TYPE_KIND(int16_t) +MPI_BASE_TYPE_KIND(int32_t) +MPI_BASE_TYPE_KIND(int64_t) +MPI_BASE_TYPE_KIND(uint8_t) +MPI_BASE_TYPE_KIND(uint16_t) +MPI_BASE_TYPE_KIND(uint32_t) +MPI_BASE_TYPE_KIND(uint64_t) +#undef MPI_BASE_TYPE_KIND +} // namespace details +// Concept to check if a type is a native MPI datatype template -concept mpi_native_datatype = requires { - details::get_data_kind() == details::mpi_data_kinds::base; -}; +concept mpi_native_datatype = (details::mpi_data_kind_trait::kind == + details::mpi_data_kinds::base); +namespace details { +// Trait to get the MPI_Datatype for a given DataType template -concept mpi_composite_datatype = requires { - details::get_data_kind() == details::mpi_data_kinds::composite; -}; +struct mpi_datatype_trait; -template -constexpr auto get_mpi_native_datatype() -> MPI_Datatype { - if constexpr (std::is_same_v) { - return MPI_CHAR; - } else if constexpr (std::is_same_v) { - return MPI_WCHAR; - } else if constexpr (std::is_same_v) { - return MPI_SHORT; - } else if constexpr (std::is_same_v) { - return MPI_INT; - } else if constexpr (std::is_same_v) { - return MPI_LONG; - } else if constexpr (std::is_same_v) { - return MPI_SIGNED_CHAR; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED_CHAR; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED_SHORT; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED_LONG; - } else if constexpr (std::is_same_v) { - return MPI_FLOAT; - } else if constexpr (std::is_same_v) { - return MPI_DOUBLE; - } else if constexpr (std::is_same_v) { - return MPI_LONG_DOUBLE; - } else if constexpr (std::is_same_v) { - return MPI_CXX_BOOL; - } else if constexpr (std::is_same_v) { - return MPI_INT8_T; - } else if constexpr (std::is_same_v) { - return MPI_INT16_T; - } else if constexpr (std::is_same_v) { - return MPI_INT32_T; - } else if constexpr (std::is_same_v) { - return MPI_INT64_T; - } else if constexpr (std::is_same_v) { - return MPI_UINT8_T; - } else if constexpr (std::is_same_v) { - return MPI_UINT16_T; - } else if constexpr (std::is_same_v) { - return MPI_UINT32_T; - } else if constexpr (std::is_same_v) { - return MPI_UINT64_T; - } else if constexpr (std::is_same_v) { - return MPI_LONG_LONG_INT; - } else if constexpr (std::is_same_v) { - return MPI_UNSIGNED_LONG_LONG; - } else { - return MPI_DATATYPE_NULL; - }; -} +// Macro to specialize mpi_datatype_trait for unique base types +#define MPI_DATATYPE_TRAIT(type, mpi_type_const) \ + template <> \ + struct mpi_datatype_trait { \ + static MPI_Datatype get_mpi_type() { \ + return mpi_type_const; \ + } \ + }; + +// Map unique base types to MPI_Datatypes +MPI_DATATYPE_TRAIT(char, MPI_CHAR) +MPI_DATATYPE_TRAIT(wchar_t, MPI_WCHAR) +MPI_DATATYPE_TRAIT(float, MPI_FLOAT) +MPI_DATATYPE_TRAIT(double, MPI_DOUBLE) +MPI_DATATYPE_TRAIT(long double, MPI_LONG_DOUBLE) +MPI_DATATYPE_TRAIT(bool, MPI_CXX_BOOL) +MPI_DATATYPE_TRAIT(int8_t, MPI_INT8_T) +MPI_DATATYPE_TRAIT(int16_t, MPI_INT16_T) +MPI_DATATYPE_TRAIT(int32_t, MPI_INT32_T) +MPI_DATATYPE_TRAIT(int64_t, MPI_INT64_T) +MPI_DATATYPE_TRAIT(uint8_t, MPI_UINT8_T) +MPI_DATATYPE_TRAIT(uint16_t, MPI_UINT16_T) +MPI_DATATYPE_TRAIT(uint32_t, MPI_UINT32_T) +MPI_DATATYPE_TRAIT(uint64_t, MPI_UINT64_T) -template -constexpr auto get_mpi_composite_datatype() -> MPI_Datatype; +#undef MPI_DATATYPE_TRAIT +} // namespace details + +// Function to get the MPI_Datatype for a given DataType +// Concept to check if a type is an MPI composite datatype (for completeness) +template +concept mpi_composite_datatype = + (details::mpi_data_kind_trait::kind == + details::mpi_data_kinds::composite); + +// mpi_datatype concept combines native and composite datatypes template concept mpi_datatype = - mpi_native_datatype || mpi_composite_datatype; - -template -concept container = requires(ContainerType a, ContainerType const b) { - requires std::regular; - requires std::swappable; - requires std::destructible; - requires std::same_as; - requires std::same_as; - requires std::forward_iterator; - requires std::forward_iterator; - requires std::signed_integral; - requires std::same_as::difference_type>; - requires std::same_as< - typename ContainerType::difference_type, - typename std::iterator_traits< - typename ContainerType::const_iterator>::difference_type>; - { a.begin() } -> std::same_as; - { a.end() } -> std::same_as; - { b.begin() } -> std::same_as; - { b.end() } -> std::same_as; - { a.cbegin() } -> std::same_as; - { a.cend() } -> std::same_as; - { a.size() } -> std::same_as; - { a.max_size() } -> std::same_as; - { a.empty() } -> std::same_as; + mpi_native_datatype || mpi_composite_datatype; + +template +auto get_mpi_datatype() -> MPI_Datatype { + if constexpr (mpi_datatype) { + return details::mpi_datatype_trait::get_mpi_type(); + } else if constexpr (std::is_same_v) { + // int8_t may be the same as signed char + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // uint8_t may be the same as unsigned char + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // int16_t may be the same as short + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // uint16_t may be the same as unsigned short + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // int32_t may be the same as int or long + if constexpr (sizeof(DataType) == sizeof(int32_t)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(DataType) == sizeof(int64_t)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported integer type"); + } + } else if constexpr (std::is_same_v) { + // uint32_t may be the same as unsigned int or unsigned long + if constexpr (sizeof(DataType) == sizeof(uint32_t)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(DataType) == sizeof(uint64_t)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported unsigned integer type"); + } + } else if constexpr (std::is_same_v) { + // int64_t may be the same as long or long long + if constexpr (sizeof(DataType) == sizeof(int32_t)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(DataType) == sizeof(int64_t)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported integer type"); + } + } else if constexpr (std::is_same_v) { + // uint64_t may be the same as unsigned long or unsigned long long + if constexpr (sizeof(DataType) == sizeof(uint32_t)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(DataType) == sizeof(uint64_t)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported unsigned integer type"); + } + } else if constexpr (std::is_same_v) { + // int64_t may be the same as long or long long + if constexpr (sizeof(DataType) == sizeof(int64_t)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported integer type"); + } + } else if constexpr (std::is_same_v) { + // uint64_t may be the same as unsigned long or unsigned long long + if constexpr (sizeof(DataType) == sizeof(uint64_t)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported unsigned integer type"); + } + } else { + static_assert(sizeof(DataType) == 0, + "Unsupported data type for MPI communication"); + return MPI_DATATYPE_NULL; // This line will never be reached due to + // static_assert + } + return MPI_DATATYPE_NULL; +} + +} // namespace mpi + + +struct MyType { + int a; + double b; + + friend bool operator==(const MyType& lhs, const MyType& rhs) { + return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b); + } + friend bool operator!=(const MyType& lhs, const MyType& rhs) { + return !(lhs == rhs); + } }; -template -constexpr auto get_mpi_datatype() -> MPI_Datatype { - if constexpr (details::get_data_kind() == - details::mpi_data_kinds::base) { - return get_mpi_native_datatype(); - } else if constexpr (details::get_data_kind() == - details::mpi_data_kinds::composite) { - return get_mpi_composite_datatype(); // Point of customization - // for user-defined types - } else { - return MPI_PACKED; // Fallback to serialization of object - } +// Specialize mpi_data_kind_trait for MyType +namespace mpi::details { +template <> +struct mpi_data_kind_trait { + static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; }; +template <> +struct mpi_datatype_trait { + static MPI_Datatype get_mpi_type() { + static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; + if (mpi_type == MPI_DATATYPE_NULL) { + int block_lengths[2] = {1, 1}; + MPI_Datatype types[2] = {MPI_INT, MPI_DOUBLE}; + MPI_Aint offsets[2]; -template -concept mpi_container = container && - mpi_datatype; -} // namespace mpi + offsets[0] = offsetof(MyType, a); + offsets[1] = offsetof(MyType, b); + + MPI_Type_create_struct(2, block_lengths, offsets, types, &mpi_type); + MPI_Type_commit(&mpi_type); + } + return mpi_type; + } +}; -#endif // MPI_TYPES_H +} // namespace mpi::details diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 1e5fb299..10145ab7 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -6,9 +6,6 @@ *****************************************************************************/ #include "parallel_contraction.h" - -#include - #include "data_structure/hashed_graph.h" #include "tools/helpers.h" @@ -40,12 +37,9 @@ void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicat MPI_Barrier(communicator); - m_messages.resize(0); - std::vector< std::vector< NodeID > >(m_messages).swap(m_messages); - m_out_messages.resize(0); - std::vector< std::vector< NodeID > >(m_out_messages).swap(m_out_messages); - m_send_buffers.resize(0); - std::vector< std::vector< NodeID > >(m_send_buffers).swap(m_send_buffers); + m_messages.clear(); + m_out_messages.clear(); + m_send_buffers.clear(); redistribute_hased_graph_and_build_graph_locally( communicator, hG, node_weights, number_of_distinct_labels, Q ); update_ghost_nodes_weights( communicator, Q ); @@ -282,22 +276,24 @@ void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( MPI NodeID divisor = ceil( number_of_cnodes/(double)size); - //std::vector< std::vector< NodeID > > messages; + std::vector< std::vector< contraction::bundled_edge > > messages(size); m_messages.clear(); m_messages.resize(size); //build messages hashed_graph::iterator it; - for( it = hG.begin(); it != hG.end(); it++) { + for(it = hG.begin(); it != hG.end(); it++) { data_hashed_edge & e = it->second; hashed_edge he = it->first; PEID peID = he.source / divisor; + messages[peID].emplace_back(he.source, he.target, e.weight); m_messages[ peID ].push_back( he.source ); m_messages[ peID ].push_back( he.target ); m_messages[ peID ].push_back( e.weight ); peID = he.target / divisor; + messages[peID].emplace_back(he.target, he.source, e.weight); m_messages[ peID ].push_back( he.target ); m_messages[ peID ].push_back( he.source ); m_messages[ peID ].push_back( e.weight ); @@ -320,6 +316,7 @@ void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( MPI // build the local part of the graph // + auto const local_msg_edges_byPE = mpi::all_to_all(messages, communicator); std::vector< std::vector< NodeID > > local_msg_byPE; local_msg_byPE.resize(size); diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h index 371a4f51..9fdd63ab 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h @@ -11,6 +11,7 @@ #include "data_structure/hashed_graph.h" #include "data_structure/parallel_graph_access.h" #include "partition_config.h" +#include "communication/mpi_tools.h" class parallel_contraction { public: @@ -51,4 +52,43 @@ class parallel_contraction { std::vector> m_send_buffers; // buffers to send messages }; +// Comm types +namespace contraction { +struct bundled_edge { + NodeID source; + NodeID target; + NodeWeight weight; +}; +} // namespace contraction + +// Specialize mpi_data_kind_trait for bundled_edge +namespace mpi::details { +template <> +struct mpi_data_kind_trait { + static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; +}; + +template <> +struct mpi_datatype_trait { + static MPI_Datatype get_mpi_type() { + static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; + if (mpi_type == MPI_DATATYPE_NULL) { + int block_lengths[3] = {1, 1, 1}; + MPI_Datatype types[3] = { + get_mpi_datatype(), + get_mpi_datatype(), + get_mpi_datatype()}; + MPI_Aint offsets[3]; + + offsets[0] = offsetof(contraction::bundled_edge, source); + offsets[1] = offsetof(contraction::bundled_edge, target); + offsets[2] = offsetof(contraction::bundled_edge, weight); + + MPI_Type_create_struct(3, block_lengths, offsets, types, &mpi_type); + MPI_Type_commit(&mpi_type); + } + return mpi_type; + } +}; +} // namespace mpi::details #endif /* end of include guard: PARALLEL_CONTRACTION_64O127GD */ diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index 48c92ae8..6ea36ce5 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -1,28 +1,49 @@ // // Created by Erich Essmann on 16/08/2024. // +#include #include +#include #include #include -#include -#include #include #include #include "parallel_contraction_projection/parallel_contraction.h" +template <> +class fmt::formatter { +public: + constexpr auto parse (format_parse_context& ctx) { return ctx.begin(); } + template + constexpr auto format (MyType const& foo, Context& ctx) const { + return format_to(ctx.out(), "({},{})", foo.a, foo.b); + } +}; TEST_CASE("all to all vector of vectors", "[unit][mpi]") { - SECTION("emtpy case") { - PEID rank, size; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &size); + SECTION("empty cases") { + PEID rank, size; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + const std::vector> v_empty{{{1,2}}, {{1,2}}, {{1,2}}, {{1,2}}, {{1,2}}}; + auto vec = mpi::all_to_all(v_empty, MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); + fmt::println("rank: {} -> {}", rank, vec); + REQUIRE(v_empty == vec); + } + SECTION("complex case") { + PEID rank, size; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); - const std::vector> v_empty{{},{1},{2,2},{3,3,3},{4,4,4,4}}; - auto vec = mpi::all_to_all(v_empty, MPI_COMM_WORLD); - MPI_Barrier(MPI_COMM_WORLD); - fmt::println("rank: {} -> {}", rank, vec); - REQUIRE(v_empty.size() == vec.size()); - } + const std::vector> v_empty{ + {}, {1}, {2, 2}, {3, 3, 3}, {4, 4, 4, 4}}; + auto vec = mpi::all_to_all(v_empty, MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); + fmt::println("rank: {} -> {}", rank, vec); + REQUIRE(v_empty.size() == vec.size()); + } } \ No newline at end of file diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index ffda0de9..68fd942f 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -2,99 +2,96 @@ // Created by Erich Essmann on 16/08/2024. // #include -#include -#include #include +#include #include +#include #include "parallel_contraction_projection/parallel_contraction.h" #include TEST_CASE("flattening vector of messages", "[unit][mpi]") { - SECTION("Empty Vector") { - std::vector > m_empty{}; - auto [flattened, offsets, lengths] = mpi::pack_messages(m_empty); - REQUIRE(flattened.empty()); - REQUIRE(offsets.empty()); - REQUIRE(lengths.empty()); - - std::vector > m_empty2{{}}; - auto [flattened2, offsets2, lengths2] = mpi::pack_messages(m_empty2); - REQUIRE(flattened2.empty()); - REQUIRE(offsets2.size() == 1); - REQUIRE(lengths2.size() == 1); - } - SECTION("Simple Vector") { - std::vector > m_simple{{1, 2, 3, 4}}; - auto [flattened, offsets, lengths] = mpi::pack_messages(m_simple); - - // Testing sizes - REQUIRE(flattened.size() == 4); - REQUIRE(offsets.size() == 1); - REQUIRE(lengths.size() == 1); - - // Testing content - REQUIRE(flattened == m_simple.at(0)); - } - - SECTION("Complex Vector") { - std::vector > data = { - {1, 2, 3}, - {}, - {4, 5}, - {6, 7, 8, 9} - }; - - auto [flattened, offsets, lengths] = mpi::pack_messages(data); - - // Testing sizes - REQUIRE(flattened.size() == 9); - REQUIRE(offsets.size() == 4); - REQUIRE(lengths.size() == 4); - - // Creating Subspans - std::vector s1, s2, s3, s4; - s1.insert(s1.begin(), flattened.begin() + offsets[0], flattened.begin() + offsets[0] + lengths[0]); - s2.insert(s2.begin(), flattened.begin() + offsets[1], flattened.begin() + offsets[1] + lengths[1]); - s3.insert(s3.begin(), flattened.begin() + offsets[2], flattened.begin() + offsets[2] + lengths[2]); - s4.insert(s4.begin(), flattened.begin() + offsets[3], flattened.begin() + offsets[3] + lengths[3]); - - REQUIRE(s1 == data[0]); - REQUIRE(s2 == data[1]); - REQUIRE(s3 == data[2]); - REQUIRE(s4 == data[3]); - } + SECTION("Empty Vector") { + std::vector > m_empty{}; + auto [flattened, offsets, lengths] = mpi::pack_messages(m_empty); + REQUIRE(flattened.empty()); + REQUIRE(offsets.empty()); + REQUIRE(lengths.empty()); + + std::vector > m_empty2{{}}; + auto [flattened2, offsets2, lengths2] = mpi::pack_messages(m_empty2); + REQUIRE(flattened2.empty()); + REQUIRE(offsets2.size() == 1); + REQUIRE(lengths2.size() == 1); + } + SECTION("Simple Vector") { + std::vector > m_simple{{1, 2, 3, 4}}; + auto [flattened, offsets, lengths] = mpi::pack_messages(m_simple); + + // Testing sizes + REQUIRE(flattened.size() == 4); + REQUIRE(offsets.size() == 1); + REQUIRE(lengths.size() == 1); + + // Testing content + REQUIRE(flattened == m_simple.at(0)); + } + + SECTION("Complex Vector") { + std::vector > data = { + {1, 2, 3}, {}, {4, 5}, {6, 7, 8, 9}, {}}; + + auto [flattened, offsets, lengths] = mpi::pack_messages(data); + + // Testing sizes + REQUIRE(flattened.size() == 9); + REQUIRE(offsets.size() == 5); + REQUIRE(lengths.size() == 5); + + // Creating Subspans + std::vector s1, s2, s3, s4, s5; + s1.insert(s1.begin(), flattened.begin() + offsets[0], + flattened.begin() + offsets[0] + lengths[0]); + s2.insert(s2.begin(), flattened.begin() + offsets[1], + flattened.begin() + offsets[1] + lengths[1]); + s3.insert(s3.begin(), flattened.begin() + offsets[2], + flattened.begin() + offsets[2] + lengths[2]); + s4.insert(s4.begin(), flattened.begin() + offsets[3], + flattened.begin() + offsets[3] + lengths[3]); + s5.insert(s5.begin(), flattened.begin() + offsets[4], + flattened.begin() + offsets[4] + lengths[4]); + + REQUIRE(s1 == data[0]); + REQUIRE(s2 == data[1]); + REQUIRE(s3 == data[2]); + REQUIRE(s4 == data[3]); + REQUIRE(s5 == data[4]); + } } TEST_CASE("Packing and Unpacking for messages", "[unit][mpi]") { - SECTION("Empty Vector") { - constexpr std::vector > m_empty{}; - auto const packed = mpi::pack_messages(m_empty); - auto const unpacked = mpi::unpack_messages(packed); - - REQUIRE(m_empty == unpacked); - } - - SECTION("Message of an empty Vector") { - std::vector > const m_empty{{}}; - auto const packed = mpi::pack_messages(m_empty); - auto const unpacked = mpi::unpack_messages(packed); - - REQUIRE(m_empty == unpacked); - } - - SECTION("Complex Message") { - std::vector > data = { - {1, 2, 3}, - {}, - {4, 5}, - {},{}, - {6, 7, 8, 9} - }; - auto const packed = mpi::pack_messages(data); - auto const unpacked = mpi::unpack_messages(packed); - REQUIRE(data == unpacked); - - } + SECTION("Empty Vector") { + constexpr std::vector > m_empty{}; + auto const packed = mpi::pack_messages(m_empty); + auto const unpacked = mpi::unpack_messages(packed); + + REQUIRE(m_empty == unpacked); + } + + SECTION("Message of an empty Vector") { + std::vector > const m_empty{{}}; + auto const packed = mpi::pack_messages(m_empty); + auto const unpacked = mpi::unpack_messages(packed); + + REQUIRE(m_empty == unpacked); + } + + SECTION("Complex Message") { + std::vector > data = {{1, 2, 3}, {}, {4, 5}, + {}, {}, {6, 7, 8, 9}}; + auto const packed = mpi::pack_messages(data); + auto const unpacked = mpi::unpack_messages(packed); + REQUIRE(data == unpacked); + } } From 34973f333429cb13b8eeb4e00b34ffcfe4560c67 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 26 Sep 2024 14:03:06 +0100 Subject: [PATCH 25/64] hashed graph updated --- .../parallel_contraction.cpp | 336 +++++++----------- .../parallel_contraction.h | 34 ++ 2 files changed, 160 insertions(+), 210 deletions(-) diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 10145ab7..f7a77012 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -264,229 +264,145 @@ void parallel_contraction::build_quotient_graph_locally( parallel_graph_access & } endfor } +void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( + MPI_Comm communicator, + hashed_graph& hG, + std::unordered_map& node_weights, + NodeID number_of_cnodes, + parallel_graph_access& Q) { + PEID rank, size; + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); + NodeID divisor = ceil(number_of_cnodes / (double)size); -void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( MPI_Comm communicator, hashed_graph & hG, - std::unordered_map< NodeID, NodeWeight > & node_weights, - NodeID number_of_cnodes, - parallel_graph_access & Q ) { - PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - NodeID divisor = ceil( number_of_cnodes/(double)size); - - std::vector< std::vector< contraction::bundled_edge > > messages(size); - m_messages.clear(); - m_messages.resize(size); - - //build messages - hashed_graph::iterator it; - for(it = hG.begin(); it != hG.end(); it++) { - data_hashed_edge & e = it->second; - hashed_edge he = it->first; - - PEID peID = he.source / divisor; - messages[peID].emplace_back(he.source, he.target, e.weight); - m_messages[ peID ].push_back( he.source ); - m_messages[ peID ].push_back( he.target ); - m_messages[ peID ].push_back( e.weight ); - - peID = he.target / divisor; - messages[peID].emplace_back(he.target, he.source, e.weight); - m_messages[ peID ].push_back( he.target ); - m_messages[ peID ].push_back( he.source ); - m_messages[ peID ].push_back( e.weight ); - } - - // now flood the network - for( PEID peID = 0; peID < size; peID++) { - if( peID != rank ) { - if( m_messages[peID].size() == 0 ){ - m_messages[peID].push_back(std::numeric_limits::max()); - } - - MPI_Request rq; - MPI_Isend( &m_messages[peID][0], - m_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID+7*size, communicator, &rq); - } - } - - // build the local part of the graph - // - auto const local_msg_edges_byPE = mpi::all_to_all(messages, communicator); - std::vector< std::vector< NodeID > > local_msg_byPE; - local_msg_byPE.resize(size); - - - if( m_messages[ rank ].size() != 0 ) { - local_msg_byPE[rank] = m_messages[rank]; - } - - PEID counter = 0; - while( counter < size - 1) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+7*size, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector incmessage; incmessage.resize(message_length); - - MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+7*size, communicator, &rst); - counter++; - - // now integrate the changes - if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do - - - PEID peID = rst.MPI_SOURCE; - local_msg_byPE[peID] = incmessage; - } - - hashed_graph local_graph; - for( PEID peID = 0; peID < size; peID++) { - if(local_msg_byPE[peID].size() > 0) { - for( ULONG i = 0; i < local_msg_byPE[peID].size()-2; i+=3) { - hashed_edge he; - he.k = number_of_cnodes; - he.source = local_msg_byPE[peID][i]; - he.target = local_msg_byPE[peID][i+1]; - - local_graph[he].weight += local_msg_byPE[peID][i+2]; - }} - } - - - ULONG from = rank * ceil(number_of_cnodes / (double)size); - ULONG to = (rank+1) * ceil(number_of_cnodes / (double)size) - 1; - // handle the case where we dont have local edges - from = std::min(from, number_of_cnodes); - to = std::min(to, number_of_cnodes - 1); - ULONG local_num_cnodes = to - from + 1; - - std::vector < std::vector< std::pair > > sorted_graph; - sorted_graph.resize( local_num_cnodes ); - - EdgeID edge_counter = 0; - for( it = local_graph.begin(); it != local_graph.end(); it++) { - data_hashed_edge & e = it->second; - hashed_edge he = it->first; - - if( from <= he.target && he.target <= to) { - std::pair< NodeID, NodeWeight > edge; - edge.first = he.target; - edge.second = e.weight/4; - - std::pair< NodeID, NodeWeight > e_bar; - e_bar.first = he.source; - e_bar.second = e.weight/4; - - sorted_graph[ he.target - from ].push_back( e_bar); - sorted_graph[ he.source - from ].push_back( edge ); - edge_counter+=2; - } else { - std::pair< NodeID, NodeWeight > edge; - edge.first = he.target; - edge.second = e.weight/2; - sorted_graph[ he.source - from ].push_back( edge ); - edge_counter++; - } - } - - ULONG global_edges = 0; - MPI_Allreduce(&edge_counter, &global_edges, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - - Q.start_construction(local_num_cnodes, edge_counter, number_of_cnodes, global_edges); - Q.set_range(from, to); + std::vector> messages(size); + m_messages.clear(); + m_messages.resize(size); - std::vector< NodeID > vertex_dist( size+1, 0 ); - for( PEID peID = 0; peID <= size; peID++) { - vertex_dist[peID] = std::min(number_of_cnodes, (NodeID) (peID * ceil(number_of_cnodes / (double)size))); // from positions - } - //vertex_dist[size] = std::min(to, number_of_cnodes - 1); - Q.set_range_array(vertex_dist); - - for (NodeID i = 0; i < local_num_cnodes; ++i) { - NodeID node = Q.new_node(); - NodeID globalID = from+node; - Q.setNodeWeight(node, 0); - Q.setNodeLabel(node, globalID); - - for( EdgeID e = 0; e < sorted_graph[node].size(); e++) { - NodeID target = sorted_graph[node][e].first; - EdgeID e_bar = Q.new_edge(node, target); - Q.setEdgeWeight(e_bar, sorted_graph[node][e].second); - } - } + // build messages + hashed_graph::iterator it; + for (it = hG.begin(); it != hG.end(); it++) { + data_hashed_edge& e = it->second; + hashed_edge he = it->first; + PEID peID = he.source / divisor; + messages[peID].emplace_back(he.source, he.target, e.weight); + peID = he.target / divisor; + messages[peID].emplace_back(he.target, he.source, e.weight); + } - Q.finish_construction(); + // build the local part of the graph + // + auto const local_msg_byPE = mpi::all_to_all(messages, communicator); - for( PEID peID = 0; peID < size; peID++) { - m_messages[peID].clear(); - } - //now distribute the node weights - //pack messages - std::unordered_map< NodeID, NodeWeight >::iterator wit; - for( wit = node_weights.begin(); wit != node_weights.end(); wit++) { - NodeID node = wit->first; - NodeWeight weight = wit->second; - PEID peID = node / divisor; - - m_messages[ peID ].push_back( node ); - m_messages[ peID ].push_back( weight ); - } + hashed_graph local_graph; + for (PEID peID = 0; peID < size; peID++) { + if (!local_msg_byPE[peID].empty()) { + for (auto packed_edge : local_msg_byPE[peID]) { + hashed_edge he; + he.k = number_of_cnodes; + he.source = packed_edge.source; + he.target = packed_edge.target; + + local_graph[he].weight += packed_edge.weight; + } + } + } - for( PEID peID = 0; peID < size; peID++) { - if( peID != rank ) { - if( m_messages[peID].size() == 0 ){ - m_messages[peID].push_back(std::numeric_limits::max()); - } + ULONG from = rank * ceil(number_of_cnodes / (double)size); + ULONG to = (rank + 1) * ceil(number_of_cnodes / (double)size) - 1; + // handle the case where we dont have local edges + from = std::min(from, number_of_cnodes); + to = std::min(to, number_of_cnodes - 1); + ULONG local_num_cnodes = to - from + 1; + + std::vector>> sorted_graph; + sorted_graph.resize(local_num_cnodes); + + EdgeID edge_counter = 0; + for (it = local_graph.begin(); it != local_graph.end(); it++) { + data_hashed_edge& e = it->second; + hashed_edge he = it->first; + + if (from <= he.target && he.target <= to) { + std::pair edge; + edge.first = he.target; + edge.second = e.weight / 4; + + std::pair e_bar; + e_bar.first = he.source; + e_bar.second = e.weight / 4; + + sorted_graph[he.target - from].push_back(e_bar); + sorted_graph[he.source - from].push_back(edge); + edge_counter += 2; + } else { + std::pair edge; + edge.first = he.target; + edge.second = e.weight / 2; + sorted_graph[he.source - from].push_back(edge); + edge_counter++; + } + } - MPI_Request rq; - MPI_Isend( &m_messages[peID][0], - m_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID+8*size, communicator, &rq); - } - } + ULONG global_edges = 0; + MPI_Allreduce(&edge_counter, &global_edges, 1, MPI_UNSIGNED_LONG_LONG, + MPI_SUM, communicator); - if( m_messages[ rank ].size() != 0 ) { - for( ULONG i = 0; i < m_messages[rank].size()-1; i+=2) { - NodeID globalID = m_messages[rank][i]; - NodeID node = globalID - from; - NodeWeight weight = m_messages[rank][i+1]; - Q.setNodeWeight( node , Q.getNodeWeight(node) + weight); - } - } + Q.start_construction(local_num_cnodes, edge_counter, number_of_cnodes, + global_edges); + Q.set_range(from, to); - counter = 0; - while( counter < size - 1) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+8*size, communicator, &st); + std::vector vertex_dist(size + 1, 0); + for (PEID peID = 0; peID <= size; peID++) { + vertex_dist[peID] = std::min( + number_of_cnodes, + (NodeID)(peID * + ceil(number_of_cnodes / (double)size))); // from positions + } + // vertex_dist[size] = std::min(to, number_of_cnodes - 1); + Q.set_range_array(vertex_dist); + + for (NodeID i = 0; i < local_num_cnodes; ++i) { + NodeID node = Q.new_node(); + NodeID globalID = from + node; + Q.setNodeWeight(node, 0); + Q.setNodeLabel(node, globalID); + + for (EdgeID e = 0; e < sorted_graph[node].size(); e++) { + NodeID target = sorted_graph[node][e].first; + EdgeID e_bar = Q.new_edge(node, target); + Q.setEdgeWeight(e_bar, sorted_graph[node][e].second); + } + } - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector incmessage; incmessage.resize(message_length); + Q.finish_construction(); - MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+8*size, communicator, &rst); - counter++; + for (PEID peID = 0; peID < size; peID++) { + m_messages[peID].clear(); + } + // now distribute the node weights + // pack messages + std::vector> weight_messages( + size); + std::unordered_map::iterator wit; + for (wit = node_weights.begin(); wit != node_weights.end(); wit++) { + NodeID node = wit->first; + NodeWeight weight = wit->second; + PEID peID = node / divisor; + weight_messages[peID].emplace_back(node, weight); + } - // now integrate the changes - if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do + auto const node_weights_byPE = mpi::all_to_all(weight_messages, communicator); - for( ULONG i = 0; i < incmessage.size()-1; i+=2) { - NodeID globalID = incmessage[i]; - NodeWeight weight = incmessage[i+1]; - NodeID node = globalID - from; - Q.setNodeWeight( node , Q.getNodeWeight(node) + weight); - } - } + for (auto& message_byPE : node_weights_byPE) { + if (message_byPE.empty()) { + for (auto& [globalID, weight] : message_byPE) { + NodeID node = globalID - from; + Q.setNodeWeight(node, Q.getNodeWeight(node) + weight); + } + } + } } diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h index 9fdd63ab..96ad1e95 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h @@ -59,6 +59,11 @@ struct bundled_edge { NodeID target; NodeWeight weight; }; + +struct bundled_node_weight { + NodeID node; + NodeWeight weight; +}; } // namespace contraction // Specialize mpi_data_kind_trait for bundled_edge @@ -91,4 +96,33 @@ struct mpi_datatype_trait { } }; } // namespace mpi::details + +// Specialize mpi_data_kind_trait for node_weight +namespace mpi::details { +template <> +struct mpi_data_kind_trait { + static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; +}; + +template <> +struct mpi_datatype_trait { + static MPI_Datatype get_mpi_type() { + static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; + if (mpi_type == MPI_DATATYPE_NULL) { + int block_lengths[2] = { 1, 1}; + MPI_Datatype types[2] = { + get_mpi_datatype(), + get_mpi_datatype()}; + MPI_Aint offsets[3]; + + offsets[0] = offsetof(contraction::bundled_node_weight, node); + offsets[1] = offsetof(contraction::bundled_node_weight, weight); + + MPI_Type_create_struct(3, block_lengths, offsets, types, &mpi_type); + MPI_Type_commit(&mpi_type); + } + return mpi_type; + } +}; +} // namespace mpi::details #endif /* end of include guard: PARALLEL_CONTRACTION_64O127GD */ From 1e1fccc114db2e1d331461a1b5a3cc3f0903d249 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 26 Sep 2024 14:43:10 +0100 Subject: [PATCH 26/64] Update CMakeLists.txt --- parallel/parallel_src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index 2ef0d2b7..57819e56 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -125,7 +125,7 @@ install(TARGETS parhip_interface_static DESTINATION lib) # Temporary Testing -set(ENABLE_TESTING ON) +set(ENABLE_TESTING OFF) if (ENABLE_TESTING) find_package(Catch2 REQUIRED CONFIG) enable_testing() From b3c2a5d19e170c3f706b3adb1b1140d9caa4dce3 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 26 Sep 2024 15:20:33 +0100 Subject: [PATCH 27/64] WIP on mpi-collective --- parallel/parallel_src/lib/communication/mpi_tools.h | 2 +- .../parallel_contraction.cpp | 2 +- .../parallel_projection.cpp | 10 ++++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 40b0afa2..854f1e70 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -92,7 +92,7 @@ auto unpack_messages(const mpi_packed_message& packed_message) std::transform( recv_displs.begin(), recv_displs.end(), recv_counts.begin(), std::back_inserter(result), - [&](int displ, int count) { + [&recv_buf](int displ, int count) { auto const start = recv_buf.begin() + displ; auto const end = start + count; return std::vector(start, end); diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index f7a77012..6d246b49 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -166,7 +166,7 @@ void parallel_contraction::get_nodes_to_cnodes_ghost_nodes( MPI_Comm communicato MPI_Comm_size( communicator, &size); std::vector< bool > PE_packed( size, false ); - m_send_buffers.clear(); + m_send_buffers.clear(); m_send_buffers.resize( size ); forall_local_nodes(G, node) { diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp index 45fe27ae..317182a6 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp @@ -7,6 +7,8 @@ #include "parallel_projection.h" +#include "communication/mpi_tools.h" + parallel_projection::parallel_projection() { } @@ -58,8 +60,12 @@ void parallel_projection::parallel_project( MPI_Comm communicator, parallel_grap } } - std::vector< std::vector< NodeID > > out_messages; - out_messages.resize(size); + std::vector< std::vector< NodeID > > out_messages(size); + auto const inc_mess_byPE = mpi::all_to_all( m_messages, communicator); + + for(auto const& incmessage : inc_mess_byPE) { + + } PEID counter = 0; while( counter < size - 1) { From 4e4347b8dc3f1cb726ad2274074427068d7b5790 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 26 Sep 2024 16:06:29 +0100 Subject: [PATCH 28/64] Isolated OpenMP --- CMakeLists.txt | 33 ++++++++++++++-------------- parallel/parallel_src/CMakeLists.txt | 13 +++++++++-- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd6bbd8e..d55b2e55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,6 @@ endif() find_package(OpenMP) if(OpenMP_CXX_FOUND) message(STATUS "OpenMP support detected") - add_definitions(${OpenMP_CXX_FLAGS}) else() message(WARNING "OpenMP not available, activating workaround") add_library(OpenMP::OpenMP_CXX IMPORTED INTERFACE) @@ -249,7 +248,7 @@ add_library(libnodeordering OBJECT ${NODE_ORDERING_SOURCE_FILES}) # generate targets for each binary add_executable(kaffpa app/kaffpa.cpp $ $) target_compile_definitions(kaffpa PRIVATE "-DMODE_KAFFPA") -target_link_libraries(kaffpa ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(kaffpa PRIVATE OpenMP::OpenMP_CXX) install(TARGETS kaffpa DESTINATION bin) if (USE_TCMALLOC) find_library(TCMALLOC_LIB tcmalloc) @@ -263,7 +262,7 @@ endif () add_executable(global_multisection app/global_multisection.cpp $ $) target_compile_definitions(global_multisection PRIVATE "-DMODE_KAFFPA -DMODE_GLOBALMS") -target_link_libraries(global_multisection ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(global_multisection PRIVATE OpenMP::OpenMP_CXX) install(TARGETS global_multisection DESTINATION bin) if (USE_TCMALLOC) find_library(TCMALLOC_LIB tcmalloc) @@ -277,33 +276,33 @@ endif () add_executable(evaluator app/evaluator.cpp $ $) target_compile_definitions(evaluator PRIVATE "-DMODE_EVALUATOR") -target_link_libraries(evaluator ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(evaluator PRIVATE OpenMP::OpenMP_CXX) install(TARGETS evaluator DESTINATION bin) add_executable(edge_evaluator app/edge_evaluator.cpp $ $) target_compile_definitions(edge_evaluator PRIVATE "-DMODE_EVALUATOR") -target_link_libraries(edge_evaluator ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(edge_evaluator PRIVATE OpenMP::OpenMP_CXX) install(TARGETS edge_evaluator DESTINATION bin) add_executable(node_separator app/node_separator_ml.cpp $ $) target_compile_definitions(node_separator PRIVATE "-DMODE_NODESEP") -target_link_libraries(node_separator ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(node_separator PRIVATE OpenMP::OpenMP_CXX) install(TARGETS node_separator DESTINATION bin) add_executable(label_propagation app/label_propagation.cpp $ $) target_compile_definitions(label_propagation PRIVATE "-DMODE_LABELPROPAGATION") -target_link_libraries(label_propagation ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(label_propagation PRIVATE OpenMP::OpenMP_CXX) install(TARGETS label_propagation DESTINATION bin) add_executable(partition_to_vertex_separator app/partition_to_vertex_separator.cpp $ $) target_compile_definitions(partition_to_vertex_separator PRIVATE "-DMODE_PARTITIONTOVERTEXSEPARATOR") -target_link_libraries(partition_to_vertex_separator ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(partition_to_vertex_separator PRIVATE OpenMP::OpenMP_CXX) install(TARGETS partition_to_vertex_separator DESTINATION bin) add_executable(interface_test misc/example_library_call/interface_test.cpp interface/kaHIP_interface.cpp $ $ $ $) target_include_directories(interface_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(interface_test PRIVATE "-DMODE_KAFFPA") -target_link_libraries(interface_test ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(interface_test PRIVATE OpenMP::OpenMP_CXX) if(LIB_METIS) target_link_libraries(interface_test ${LIB_METIS} ${LIB_GK}) endif() @@ -313,13 +312,13 @@ if(NOT NOMPI) add_executable(kaffpaE app/kaffpaE.cpp $ $ $) target_compile_definitions(kaffpaE PRIVATE "-DMODE_KAFFPAE") target_include_directories(kaffpaE PUBLIC ${MPI_CXX_INCLUDE_PATH}) - target_link_libraries(kaffpaE ${MPI_CXX_LIBRARIES} ${OpenMP_CXX_LIBRARIES} OpenMP::OpenMP_CXX ) + target_link_libraries(kaffpaE PRIVATE MPI::MPI_CXX OpenMP::OpenMP_CXX ) install(TARGETS kaffpaE DESTINATION bin) endif() add_executable(graphchecker app/graphchecker.cpp $ $) target_compile_definitions(graphchecker PRIVATE "-DMODE_GRAPHCHECKER") -target_link_libraries(graphchecker ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(graphchecker PRIVATE OpenMP::OpenMP_CXX) install(TARGETS graphchecker DESTINATION bin) add_executable(edge_partitioning app/spac.cpp $ $ $) @@ -329,13 +328,13 @@ install(TARGETS edge_partitioning DESTINATION bin) add_executable(node_ordering app/node_ordering.cpp $ $) target_compile_definitions(node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING") -target_link_libraries(node_ordering ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(node_ordering PRIVATE OpenMP::OpenMP_CXX) install(TARGETS node_ordering DESTINATION bin) if(LIB_METIS) add_executable(fast_node_ordering app/fast_node_ordering.cpp $ $) target_compile_definitions(fast_node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING -DFASTORDERING") - target_link_libraries(fast_node_ordering ${OpenMP_CXX_LIBRARIES} ${LIB_METIS} ${LIB_GK}) + target_link_libraries(fast_node_ordering PRIVATE OpenMP::OpenMP_CXX ${LIB_METIS} ${LIB_GK}) install(TARGETS fast_node_ordering DESTINATION bin) endif() @@ -347,7 +346,7 @@ add_library(kahip SHARED interface/kaHIP_interface.cpp $) target_include_directories(kahip PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(kahip PRIVATE "-DMODE_KAFFPA") -target_link_libraries(kahip PUBLIC ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(kahip PRIVATE OpenMP::OpenMP_CXX) if(LIB_METIS) target_link_libraries(kahip PUBLIC ${LIB_METIS} ${LIB_GK}) endif() @@ -360,7 +359,7 @@ add_library(kahip_static interface/kaHIP_interface.cpp $) target_include_directories(kahip_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(kahip_static PRIVATE "-DMODE_KAFFPA") -target_link_libraries(kahip_static PUBLIC ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(kahip_static PRIVATE OpenMP::OpenMP_CXX) set_target_properties(kahip_static PROPERTIES PUBLIC_HEADER interface/kaHIP_interface.h) if(LIB_METIS) @@ -385,12 +384,12 @@ if(USE_ILP) add_executable(ilp_improve app/ilp_improve.cpp $ $) target_include_directories(ilp_improve PUBLIC ${GUROBI_INCLUDE_DIR}) target_compile_definitions(ilp_improve PRIVATE "-DMODE_ILPIMPROVE") - target_link_libraries(ilp_improve ${OpenMP_CXX_LIBRARIES} ${GUROBI_LIBRARIES}) + target_link_libraries(ilp_improve PRIVATE OpenMP::OpenMP_CXX ${GUROBI_LIBRARIES}) add_executable(ilp_exact app/ilp_exact.cpp $ $) target_include_directories(ilp_exact PUBLIC ${GUROBI_INCLUDE_DIR}) target_compile_definitions(ilp_exact PRIVATE "-DMODE_ILPIMPROVE -DMODE_ILPEXACT") - target_link_libraries(ilp_exact ${OpenMP_CXX_LIBRARIES} ${GUROBI_LIBRARIES}) + target_link_libraries(ilp_exact PRIVATE OpenMP::OpenMP_CXX ${GUROBI_LIBRARIES}) endif() diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index 57819e56..94789ae3 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -12,8 +12,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) -include_directories(${MPI_CXX_INCLUDE_PATH}) -link_libraries(OpenMP::OpenMP_CXX MPI::MPI_CXX) + set(LIBPARALLEL_SOURCE_FILES lib/data_structure/parallel_graph_access.cpp @@ -37,6 +36,7 @@ set(LIBPARALLEL_SOURCE_FILES extern/argtable3-3.2.2/argtable3.c) add_library(libparallel ${LIBPARALLEL_SOURCE_FILES}) target_include_directories(libparallel PUBLIC $) +target_link_libraries(libparallel PUBLIC MPI::MPI_CXX) set(LIBGRAPH2BGF_SOURCE_FILES lib/data_structure/parallel_graph_access.cpp @@ -45,6 +45,8 @@ set(LIBGRAPH2BGF_SOURCE_FILES lib/data_structure/balance_management_refinement.cpp lib/data_structure/balance_management_coarsening.cpp) add_library(libgraph2bgf ${LIBGRAPH2BGF_SOURCE_FILES}) +target_link_libraries(libgraph2bgf PUBLIC MPI::MPI_CXX) + set(LIBEDGELIST_SOURCE_FILES lib/data_structure/parallel_graph_access.cpp @@ -54,27 +56,34 @@ set(LIBEDGELIST_SOURCE_FILES lib/data_structure/balance_management_coarsening.cpp extern/argtable3-3.2.2/argtable3.c) add_library(libedgelist STATIC ${LIBEDGELIST_SOURCE_FILES}) +target_link_libraries(libedgelist PUBLIC MPI::MPI_CXX) + set(LIBDSPAC_SOURCE_FILES lib/dspac/dspac.cpp lib/dspac/edge_balanced_graph_io.cpp) add_library(libdspac STATIC ${LIBDSPAC_SOURCE_FILES}) +target_link_libraries(libdspac PUBLIC MPI::MPI_CXX) + add_executable(parhip app/parhip.cpp) target_compile_definitions(parhip PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_link_libraries(parhip PRIVATE libmodified_kahip_interface) target_link_libraries(parhip PRIVATE libparallel) +target_link_libraries(parhip PRIVATE MPI::MPI_CXX) install(TARGETS parhip DESTINATION bin) add_executable(toolbox app/toolbox.cpp) target_compile_definitions(toolbox PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DTOOLBOX") target_link_libraries(toolbox PRIVATE libmodified_kahip_interface) target_link_libraries(toolbox PRIVATE libparallel) +target_link_libraries(toolbox PRIVATE MPI::MPI_CXX) install(TARGETS toolbox DESTINATION bin) add_executable(graph2binary app/graph2binary.cpp) target_compile_definitions(graph2binary PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION -DGRAPH2DGF") target_link_libraries(graph2binary PRIVATE libgraph2bgf) +target_link_libraries(graph2binary PRIVATE MPI::MPI_CXX) install(TARGETS graph2binary DESTINATION bin) add_executable(graph2binary_external app/graph2binary_external.cpp) From 7d9b6899c800be78353e493c4832a14688347117 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 27 Sep 2024 11:40:43 +0100 Subject: [PATCH 29/64] Custom type fix --- .../parallel_contraction.h | 4 ++-- .../parallel_contraction_mpi_test.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h index 96ad1e95..b2ec542c 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h @@ -113,12 +113,12 @@ struct mpi_datatype_trait { MPI_Datatype types[2] = { get_mpi_datatype(), get_mpi_datatype()}; - MPI_Aint offsets[3]; + MPI_Aint offsets[2]; offsets[0] = offsetof(contraction::bundled_node_weight, node); offsets[1] = offsetof(contraction::bundled_node_weight, weight); - MPI_Type_create_struct(3, block_lengths, offsets, types, &mpi_type); + MPI_Type_create_struct(2, block_lengths, offsets, types, &mpi_type); MPI_Type_commit(&mpi_type); } return mpi_type; diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index 6ea36ce5..ec416786 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -46,4 +46,20 @@ TEST_CASE("all to all vector of vectors", "[unit][mpi]") { fmt::println("rank: {} -> {}", rank, vec); REQUIRE(v_empty.size() == vec.size()); } + + SECTION("custom types") { + PEID rank, size; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + const std::vector> empty_edges{ + {{}}, {{}}, {{}}, {{}}, {{}}}; + const std::vector> empty_weights{ + {{}}, {{}}, {{}}, {{}}, {{}}}; + auto vec_1 = mpi::all_to_all(empty_edges, MPI_COMM_WORLD); + auto vec_2 = mpi::all_to_all(empty_weights, MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); + REQUIRE(empty_edges.size() == vec_1.size()); + REQUIRE(empty_weights.size() == vec_2.size()); + } } \ No newline at end of file From 0434215d6c809f844ec7b4cb2f11000c8b32b857 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 30 Sep 2024 13:03:06 +0100 Subject: [PATCH 30/64] Added reflection and custom datatypes fixed --- parallel/parallel_src/CMakeLists.txt | 22 +- .../parallel_src/extern/cista/.clang-format | 47 + .../.github/actions/install-llvm/action.yml | 59 + .../extern/cista/.github/workflows/linux.yml | 155 + .../cista/.github/workflows/windows.yml | 45 + parallel/parallel_src/extern/cista/.gitignore | 7 + .../extern/cista/CMake/cistaConfig.cmake.in | 6 + .../parallel_src/extern/cista/CMakeLists.txt | 209 + parallel/parallel_src/extern/cista/LICENSE | 19 + parallel/parallel_src/extern/cista/README.md | 175 + .../extern/cista/fuzz/bitset_verification.cc | 250 + .../extern/cista/fuzz/bitvec_verification.cc | 248 + .../parallel_src/extern/cista/fuzz/graph.cc | 116 + .../cista/fuzz/hash_map_verification.cc | 56 + .../extern/cista/fuzz/hash_set.cc | 97 + .../cista/fuzz/multimap_verification.cc | 159 + .../cista/include/cista/aligned_alloc.h | 48 + .../cista/include/cista/aligned_allocator.h | 68 + .../extern/cista/include/cista/allocator.h | 24 + .../extern/cista/include/cista/bit_counting.h | 120 + .../extern/cista/include/cista/buffer.h | 71 + .../extern/cista/include/cista/chunk.h | 21 + .../cista/include/cista/cista_exception.h | 13 + .../extern/cista/include/cista/containers.h | 21 + .../cista/include/cista/containers/array.h | 18 + .../cista/include/cista/containers/bitset.h | 328 + .../cista/include/cista/containers/bitvec.h | 487 ++ .../cista/include/cista/containers/cstring.h | 462 ++ .../include/cista/containers/flat_matrix.h | 142 + .../include/cista/containers/fws_multimap.h | 217 + .../cista/include/cista/containers/hash_map.h | 40 + .../cista/include/cista/containers/hash_set.h | 28 + .../include/cista/containers/hash_storage.h | 677 ++ .../cista/include/cista/containers/mmap_vec.h | 183 + .../cista/containers/mutable_fws_multimap.h | 658 ++ .../cista/include/cista/containers/nvec.h | 590 ++ .../include/cista/containers/offset_ptr.h | 204 + .../cista/include/cista/containers/optional.h | 83 + .../cista/include/cista/containers/paged.h | 153 + .../include/cista/containers/paged_vecvec.h | 342 + .../cista/include/cista/containers/pair.h | 31 + .../cista/include/cista/containers/ptr.h | 31 + .../cista/include/cista/containers/string.h | 617 ++ .../cista/include/cista/containers/tuple.h | 308 + .../include/cista/containers/unique_ptr.h | 107 + .../cista/include/cista/containers/variant.h | 448 ++ .../cista/include/cista/containers/vector.h | 558 ++ .../cista/include/cista/containers/vecvec.h | 396 + .../extern/cista/include/cista/decay.h | 25 + .../cista/include/cista/endian/conversion.h | 83 + .../cista/include/cista/endian/detection.h | 39 + .../extern/cista/include/cista/equal_to.h | 83 + .../extern/cista/include/cista/exception.h | 14 + .../extern/cista/include/cista/hash.h | 151 + .../extern/cista/include/cista/hashing.h | 186 + .../extern/cista/include/cista/indexed.h | 32 + .../extern/cista/include/cista/is_iterable.h | 42 + .../cista/include/cista/memory_holder.h | 42 + .../extern/cista/include/cista/mmap.h | 217 + .../extern/cista/include/cista/mode.h | 40 + .../cista/include/cista/next_power_of_2.h | 24 + .../extern/cista/include/cista/offset_t.h | 15 + .../cista/include/cista/reflection/arity.h | 54 + .../include/cista/reflection/comparable.h | 59 + .../include/cista/reflection/for_each_field.h | 42 + .../include/cista/reflection/member_index.h | 25 + .../include/cista/reflection/printable.h | 63 + .../cista/include/cista/reflection/to_tuple.h | 529 ++ .../cista/include/cista/serialization.h | 1186 +++ .../cista/include/cista/serialized_size.h | 16 + .../extern/cista/include/cista/strong.h | 207 + .../extern/cista/include/cista/targets/buf.h | 78 + .../extern/cista/include/cista/targets/file.h | 326 + .../cista/type_hash/static_type_hash.h | 269 + .../cista/include/cista/type_hash/type_hash.h | 189 + .../cista/include/cista/type_hash/type_name.h | 69 + .../extern/cista/include/cista/type_traits.h | 25 + .../extern/cista/include/cista/unused_param.h | 3 + .../extern/cista/include/cista/verify.h | 16 + parallel/parallel_src/extern/cista/logo.svg | 5 + .../cista/test/anonymous_namespace_test.cc | 22 + .../extern/cista/test/array_test.cc | 115 + .../extern/cista/test/bit_counting_test.cc | 58 + .../extern/cista/test/bitset_test.cc | 115 + .../extern/cista/test/bitvec_test.cc | 183 + .../extern/cista/test/cista_members_test.cc | 106 + .../extern/cista/test/comparable_test.cc | 37 + .../copy_from_potentially_analigned_test.cc | 34 + .../cista/test/cstring_serialize_test.cc | 205 + .../extern/cista/test/cstring_test.cc | 85 + .../extern/cista/test/custom_struct_test.cc | 42 + .../extern/cista/test/decay_test.cc | 61 + .../extern/cista/test/downward_compat_test.cc | 78 + .../extern/cista/test/file_test.cc | 52 + .../extern/cista/test/for_each_field_test.cc | 58 + .../extern/cista/test/fws_map_test.cc | 91 + .../extern/cista/test/hash_set_test.cc | 255 + .../extern/cista/test/hash_test.cc | 9 + .../extern/cista/test/hashing_test.cc | 104 + .../cista/test/inherited_derived_test.cc | 44 + .../extern/cista/test/leak_check.cc | 15 + .../extern/cista/test/member_index_test.cc | 16 + .../extern/cista/test/mesh_test.cc | 79 + .../cista/test/mutable_multimap_test.cc | 387 + .../extern/cista/test/nvec_test.cc | 102 + .../test/offset_graph_indexed_vector_test.cc | 230 + .../test/offset_graph_static_version_test.cc | 233 + .../extern/cista/test/offset_graph_test.cc | 250 + .../cista/test/offset_ptr_vector_test.cc | 96 + .../extern/cista/test/offset_security_test.cc | 169 + .../extern/cista/test/offset_void_ptr_test.cc | 16 + .../extern/cista/test/pointer_test.cc | 28 + .../extern/cista/test/printable_test.cc | 31 + .../extern/cista/test/raw_graph_test.cc | 163 + .../extern/cista/test/raw_security_test.cc | 169 + .../extern/cista/test/single_member_struct.cc | 16 + .../extern/cista/test/std_vector_test.cc | 93 + .../extern/cista/test/string_test.cc | 443 ++ .../cista/test/struct_serialize_test.cc | 34 + .../extern/cista/test/to_tuple_test.cc | 35 + .../extern/cista/test/tuple_test.cc | 272 + .../extern/cista/test/type_hash_test.cc | 61 + .../extern/cista/test/type_name_test.cc | 32 + .../extern/cista/test/union_derive_test.cc | 127 + .../extern/cista/test/variant_test.cc | 255 + .../extern/cista/test/vector_copy_test.cc | 20 + .../extern/cista/test/vector_test.cc | 325 + .../extern/cista/test/vecvec_test.cc | 251 + .../extern/cista/tools/doctest/CMakeLists.txt | 5 + .../extern/cista/tools/doctest/doctest.cc | 3 + .../extern/cista/tools/doctest/doctest.h | 7077 +++++++++++++++++ .../tools/pretty-printers/gdb/cista_bitset.py | 29 + .../pretty-printers/gdb/cista_hash_storage.py | 142 + .../pretty-printers/gdb/cista_pointer.py | 19 + .../tools/pretty-printers/gdb/cista_string.py | 36 + .../tools/pretty-printers/gdb/cista_tuple.py | 51 + .../pretty-printers/gdb/cista_variant.py | 27 + .../tools/pretty-printers/gdb/cista_vector.py | 108 + .../pretty-printers/gdb/offset_pointer.py | 24 + .../tools/to_tuple_generator/CMakeLists.txt | 3 + .../to_tuple_generator/to_tuple_generator.cc | 132 + .../extern/cista/tools/uniter/CMakeLists.txt | 4 + .../extern/cista/tools/uniter/uniter.cc | 65 + .../extern/cista/tools/wyhash/CMakeLists.txt | 3 + .../extern/cista/tools/wyhash/wyhash.h | 121 + .../extern/cista/tools/xxh3/CMakeLists.txt | 3 + .../extern/cista/tools/xxh3/xxh3.h | 1632 ++++ .../extern/cista/tools/xxh3/xxhash.c | 1110 +++ .../extern/cista/tools/xxh3/xxhash.h | 587 ++ parallel/parallel_src/extern/nameof.hpp | 1254 +++ .../lib/communication/mpi_types.h | 107 +- parallel/parallel_src/tests/CMakeLists.txt | 6 +- .../tests/catch_mpi/catch_mpi_runner.cpp | 1 + .../parallel_contraction_mpi_test.cpp | 40 +- .../parallel_contraction_test.cpp | 62 +- 155 files changed, 31619 insertions(+), 52 deletions(-) create mode 100644 parallel/parallel_src/extern/cista/.clang-format create mode 100644 parallel/parallel_src/extern/cista/.github/actions/install-llvm/action.yml create mode 100644 parallel/parallel_src/extern/cista/.github/workflows/linux.yml create mode 100644 parallel/parallel_src/extern/cista/.github/workflows/windows.yml create mode 100644 parallel/parallel_src/extern/cista/.gitignore create mode 100644 parallel/parallel_src/extern/cista/CMake/cistaConfig.cmake.in create mode 100755 parallel/parallel_src/extern/cista/CMakeLists.txt create mode 100644 parallel/parallel_src/extern/cista/LICENSE create mode 100644 parallel/parallel_src/extern/cista/README.md create mode 100755 parallel/parallel_src/extern/cista/fuzz/bitset_verification.cc create mode 100644 parallel/parallel_src/extern/cista/fuzz/bitvec_verification.cc create mode 100755 parallel/parallel_src/extern/cista/fuzz/graph.cc create mode 100755 parallel/parallel_src/extern/cista/fuzz/hash_map_verification.cc create mode 100755 parallel/parallel_src/extern/cista/fuzz/hash_set.cc create mode 100644 parallel/parallel_src/extern/cista/fuzz/multimap_verification.cc create mode 100644 parallel/parallel_src/extern/cista/include/cista/aligned_alloc.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/aligned_allocator.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/allocator.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/bit_counting.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/buffer.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/chunk.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/cista_exception.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/array.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/bitset.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/bitvec.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/cstring.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/flat_matrix.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/fws_multimap.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/hash_map.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/hash_set.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/hash_storage.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/mmap_vec.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/mutable_fws_multimap.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/nvec.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/offset_ptr.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/optional.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/paged.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/paged_vecvec.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/pair.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/ptr.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/string.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/tuple.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/unique_ptr.h create mode 100755 parallel/parallel_src/extern/cista/include/cista/containers/variant.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/vector.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/containers/vecvec.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/decay.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/endian/conversion.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/endian/detection.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/equal_to.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/exception.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/hash.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/hashing.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/indexed.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/is_iterable.h create mode 100755 parallel/parallel_src/extern/cista/include/cista/memory_holder.h create mode 100755 parallel/parallel_src/extern/cista/include/cista/mmap.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/mode.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/next_power_of_2.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/offset_t.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/reflection/arity.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/reflection/comparable.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/reflection/for_each_field.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/reflection/member_index.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/reflection/printable.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/reflection/to_tuple.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/serialization.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/serialized_size.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/strong.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/targets/buf.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/targets/file.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/type_hash/static_type_hash.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/type_hash/type_hash.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/type_hash/type_name.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/type_traits.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/unused_param.h create mode 100644 parallel/parallel_src/extern/cista/include/cista/verify.h create mode 100644 parallel/parallel_src/extern/cista/logo.svg create mode 100644 parallel/parallel_src/extern/cista/test/anonymous_namespace_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/array_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/bit_counting_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/bitset_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/bitvec_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/cista_members_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/comparable_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/copy_from_potentially_analigned_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/cstring_serialize_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/cstring_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/custom_struct_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/decay_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/downward_compat_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/file_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/for_each_field_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/fws_map_test.cc create mode 100755 parallel/parallel_src/extern/cista/test/hash_set_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/hash_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/hashing_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/inherited_derived_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/leak_check.cc create mode 100644 parallel/parallel_src/extern/cista/test/member_index_test.cc create mode 100755 parallel/parallel_src/extern/cista/test/mesh_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/mutable_multimap_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/nvec_test.cc create mode 100755 parallel/parallel_src/extern/cista/test/offset_graph_indexed_vector_test.cc create mode 100755 parallel/parallel_src/extern/cista/test/offset_graph_static_version_test.cc create mode 100755 parallel/parallel_src/extern/cista/test/offset_graph_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/offset_ptr_vector_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/offset_security_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/offset_void_ptr_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/pointer_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/printable_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/raw_graph_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/raw_security_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/single_member_struct.cc create mode 100755 parallel/parallel_src/extern/cista/test/std_vector_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/string_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/struct_serialize_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/to_tuple_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/tuple_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/type_hash_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/type_name_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/union_derive_test.cc create mode 100755 parallel/parallel_src/extern/cista/test/variant_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/vector_copy_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/vector_test.cc create mode 100644 parallel/parallel_src/extern/cista/test/vecvec_test.cc create mode 100644 parallel/parallel_src/extern/cista/tools/doctest/CMakeLists.txt create mode 100755 parallel/parallel_src/extern/cista/tools/doctest/doctest.cc create mode 100644 parallel/parallel_src/extern/cista/tools/doctest/doctest.h create mode 100644 parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_bitset.py create mode 100644 parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_hash_storage.py create mode 100644 parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_pointer.py create mode 100644 parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_string.py create mode 100644 parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_tuple.py create mode 100644 parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_variant.py create mode 100644 parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_vector.py create mode 100644 parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/offset_pointer.py create mode 100644 parallel/parallel_src/extern/cista/tools/to_tuple_generator/CMakeLists.txt create mode 100644 parallel/parallel_src/extern/cista/tools/to_tuple_generator/to_tuple_generator.cc create mode 100644 parallel/parallel_src/extern/cista/tools/uniter/CMakeLists.txt create mode 100644 parallel/parallel_src/extern/cista/tools/uniter/uniter.cc create mode 100755 parallel/parallel_src/extern/cista/tools/wyhash/CMakeLists.txt create mode 100644 parallel/parallel_src/extern/cista/tools/wyhash/wyhash.h create mode 100755 parallel/parallel_src/extern/cista/tools/xxh3/CMakeLists.txt create mode 100755 parallel/parallel_src/extern/cista/tools/xxh3/xxh3.h create mode 100755 parallel/parallel_src/extern/cista/tools/xxh3/xxhash.c create mode 100755 parallel/parallel_src/extern/cista/tools/xxh3/xxhash.h create mode 100644 parallel/parallel_src/extern/nameof.hpp diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index 94789ae3..5e27f960 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -6,6 +6,8 @@ if(DETERMINISTIC_PARHIP) add_definitions("-DDETERMINISTIC_PARHIP") endif() +add_subdirectory(extern/cista) + include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/app) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) @@ -34,9 +36,9 @@ set(LIBPARALLEL_SOURCE_FILES lib/tools/random_functions.cpp lib/tools/distributed_quality_metrics.cpp extern/argtable3-3.2.2/argtable3.c) -add_library(libparallel ${LIBPARALLEL_SOURCE_FILES}) -target_include_directories(libparallel PUBLIC $) -target_link_libraries(libparallel PUBLIC MPI::MPI_CXX) +add_library(parallel ${LIBPARALLEL_SOURCE_FILES}) +target_include_directories(parallel PUBLIC $) +target_link_libraries(parallel PUBLIC MPI::MPI_CXX cista::cista) set(LIBGRAPH2BGF_SOURCE_FILES lib/data_structure/parallel_graph_access.cpp @@ -44,7 +46,7 @@ set(LIBGRAPH2BGF_SOURCE_FILES lib/data_structure/balance_management.cpp lib/data_structure/balance_management_refinement.cpp lib/data_structure/balance_management_coarsening.cpp) -add_library(libgraph2bgf ${LIBGRAPH2BGF_SOURCE_FILES}) +add_library(libgraph2bgf OBJECT ${LIBGRAPH2BGF_SOURCE_FILES}) target_link_libraries(libgraph2bgf PUBLIC MPI::MPI_CXX) @@ -69,14 +71,14 @@ target_link_libraries(libdspac PUBLIC MPI::MPI_CXX) add_executable(parhip app/parhip.cpp) target_compile_definitions(parhip PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_link_libraries(parhip PRIVATE libmodified_kahip_interface) -target_link_libraries(parhip PRIVATE libparallel) +target_link_libraries(parhip PRIVATE parallel) target_link_libraries(parhip PRIVATE MPI::MPI_CXX) install(TARGETS parhip DESTINATION bin) add_executable(toolbox app/toolbox.cpp) target_compile_definitions(toolbox PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DTOOLBOX") target_link_libraries(toolbox PRIVATE libmodified_kahip_interface) -target_link_libraries(toolbox PRIVATE libparallel) +target_link_libraries(toolbox PRIVATE parallel) target_link_libraries(toolbox PRIVATE MPI::MPI_CXX) install(TARGETS toolbox DESTINATION bin) @@ -110,7 +112,7 @@ install(TARGETS edge_list_to_metis_graph DESTINATION bin) add_executable(dspac app/dspac.cpp) target_compile_definitions(dspac PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_link_libraries(dspac PRIVATE libmodified_kahip_interface) -target_link_libraries(dspac PRIVATE libparallel) +target_link_libraries(dspac PRIVATE parallel) target_link_libraries(dspac PRIVATE libdspac) install(TARGETS dspac DESTINATION bin) @@ -118,7 +120,7 @@ add_library(parhip_interface SHARED interface/parhip_interface.cpp) target_compile_definitions(parhip_interface PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_include_directories(parhip_interface PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_link_libraries(parhip_interface PRIVATE libmodified_kahip_interface) -target_link_libraries(parhip_interface PRIVATE libparallel) +target_link_libraries(parhip_interface PRIVATE parallel) set_target_properties(parhip_interface PROPERTIES PUBLIC_HEADER interface/parhip_interface.h) install(TARGETS parhip_interface LIBRARY DESTINATION lib @@ -129,12 +131,12 @@ add_library(parhip_interface_static interface/parhip_interface.cpp) target_compile_definitions(parhip_interface_static PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_include_directories(parhip_interface_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_link_libraries(parhip_interface_static PRIVATE libmodified_kahip_interface) -target_link_libraries(parhip_interface_static PRIVATE libparallel) +target_link_libraries(parhip_interface_static PRIVATE parallel) install(TARGETS parhip_interface_static DESTINATION lib) # Temporary Testing -set(ENABLE_TESTING OFF) +set(ENABLE_TESTING ON) if (ENABLE_TESTING) find_package(Catch2 REQUIRED CONFIG) enable_testing() diff --git a/parallel/parallel_src/extern/cista/.clang-format b/parallel/parallel_src/extern/cista/.clang-format new file mode 100644 index 00000000..635e0104 --- /dev/null +++ b/parallel/parallel_src/extern/cista/.clang-format @@ -0,0 +1,47 @@ +BasedOnStyle: Google +ColumnLimit: 80 +IndentWidth: 2 +Language: Cpp +DerivePointerAlignment: false +PointerAlignment: Left +AccessModifierOffset: -2 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +AlignTrailingComments: false +KeepEmptyLinesAtTheStartOfBlocks: true +AllowShortCaseLabelsOnASingleLine: true +AlwaysBreakTemplateDeclarations: true +SpacesBeforeTrailingComments: 2 +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^' + Priority: 1 + - Regex: '^ + clang-17 + clang-tools-17 + clang-format-17 + clang-tidy-17 + libc++-17-dev + libc++abi-17-dev + libclang-common-17-dev + libclang-rt-17-dev + libunwind-17-dev +runs: + using: "composite" + steps: + - run: | + sudo apt-get install -qq --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + software-properties-common + wget -nv -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + source /etc/os-release + sudo add-apt-repository "deb http://apt.llvm.org/${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-${{ inputs.version }} main" + sudo apt-get update + shell: bash + env: + DEBIAN_FRONTEND: noninteractive + + # workaround for package conflicts + - run: | + sudo apt-get purge --auto-remove \ + llvm \ + llvm-14 \ + python3-lldb-14 \ + libc++-dev \ + libc++1-14 \ + libc++abi-dev \ + libc++abi1-14 \ + libc++abi-14-dev \ + libunwind-14 \ + libunwind-14-dev + shell: bash + env: + DEBIAN_FRONTEND: noninteractive + + - run: | + sudo apt-get install -y --no-install-recommends \ + ${{ inputs.packages }} + shell: bash + env: + DEBIAN_FRONTEND: noninteractive \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/.github/workflows/linux.yml b/parallel/parallel_src/extern/cista/.github/workflows/linux.yml new file mode 100644 index 00000000..689df99f --- /dev/null +++ b/parallel/parallel_src/extern/cista/.github/workflows/linux.yml @@ -0,0 +1,155 @@ +name: Linux Build + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + release: + types: + - published + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + formatting: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install clang-format + uses: ./.github/actions/install-llvm + with: + packages: clang-format-17 + + - name: Format files + run: find include test -type f -a \( -name "*.cc" -o -name "*.h" \) -print0 | xargs -0 clang-format-17 -i + + - name: Check for differences + run: | + git status --porcelain + git status --porcelain | xargs -I {} -0 test -z \"{}\" + build: + runs-on: ubuntu-latest + strategy: + matrix: + config: + - name: GCC 12 Release + cxx: g++-12 + cc: gcc-12 + mode: Release + - name: GCC 12 Debug + cxx: g++-12 + cc: gcc-12 + mode: Debug + valgrind: true + - name: Clang 17 Release + cxx: clang++-17 + cc: clang-17 + mode: Release + cxxflags: -stdlib=libc++ + ldflags: -lc++abi + - name: Clang 17 Debug + cxx: clang++-17 + cc: clang-17 + mode: Debug + fuzz: true + - key: Clang 17 Sanitizer + cxx: clang++-17 + cc: clang-17 + mode: Debug + cflags: -fsanitize=address,undefined -fno-omit-frame-pointer + cxxflags: -fsanitize=address,undefined -fno-omit-frame-pointer + + env: + UBSAN_OPTIONS: halt_on_error=1:abort_on_error=1 + steps: + - uses: actions/checkout@v4 + + - name: Install Ninja + env: + DEBIAN_FRONTEND: noninteractive + run: sudo apt-get install -y --no-install-recommends ninja-build + + # ==== INSTALL ==== + - name: Install Clang + uses: ./.github/actions/install-llvm + with: + packages: libstdc++-12-dev libc++-17-dev libc++abi-17-dev clang-tidy-17 libunwind-17-dev llvm-17 libfuzzer-17-dev llvm-17-dev libclang-rt-17-dev + + # ==== BUILD ==== + - name: CMake + run: | + cmake \ + -G Ninja -S . -B build \ + -DCMAKE_C_COMPILER=${{ matrix.config.cc }} \ + -DCMAKE_CXX_COMPILER=${{ matrix.config.cxx }} \ + -DCMAKE_C_FLAGS="${{ matrix.config.cflags }}" \ + -DCMAKE_CXX_FLAGS="${{ matrix.config.cxxflags }}" \ + -DCMAKE_CXX_LINKER_FLAGS=${{ matrix.config.ldflags }}" \ + -DCMAKE_CXX_EXE_LINKER_FLAGS="${{ matrix.config.ldflags }} \ + -DCMAKE_BUILD_TYPE=${{ matrix.config.mode }} \ + -DCISTA_ZERO_OUT=${{ matrix.config.mode == 'Debug' && matrix.config.cc == 'gcc-12' }} + - name: Build + run: cmake --build build --target cista-test cista-test-single-header + + # ==== TESTS ==== + - name: Run Tests + run: ./build/cista-test + - name: Run Single Header Tests + run: ./build/cista-test-single-header + + # ==== VALGRIND ==== + - name: Install Valgrind + if: matrix.config.valgrind + env: + DEBIAN_FRONTEND: noninteractive + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends valgrind + - name: Run Single Header Tests Valgrind + if: matrix.config.valgrind + run: valgrind --error-exitcode=1 --show-reachable=yes --leak-check=full ./build/cista-test + - name: Run Single Header Tests Tests Valgrind + if: matrix.config.valgrind + run: valgrind --error-exitcode=1 --show-reachable=yes --leak-check=full ./build/cista-test-single-header + + # ==== FUZZ ==== + - name: Fuzzing + if: matrix.config.fuzz + run: | + cmake --build build --target \ + cista-fuzz-bitset_verification \ + cista-fuzz-bitvec_verification \ + cista-fuzz-graph \ + cista-fuzz-hash_map_verification \ + cista-fuzz-hash_set \ + cista-fuzz-multimap_verification + ./build/cista-fuzz-hash_set -max_total_time=120 + ./build/cista-fuzz-hash_map_verification -max_total_time=120 + ./build/cista-fuzz-bitset_verification -max_total_time=120 + ./build/cista-fuzz-bitvec_verification -max_total_time=120 + ./build/cista-fuzz-graph -max_total_time=120 + ./build/cista-fuzz-multimap_verification -max_total_time=120 + + # ==== DISTRIBUTION ==== + - name: Upload Distribution + if: matrix.config.mode == 'Release' && matrix.config.cc == 'gcc-12' + uses: actions/upload-artifact@v4 + with: + name: cista.h + path: build/cista.h + + # ==== RELEASE ==== + - name: Upload Release + if: github.event.action == 'published' && matrix.config.mode == 'Release' && matrix.config.cc == 'gcc-12' + uses: actions/upload-release-asset@v1.0.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./build/cista.h + asset_name: cista.h + asset_content_type: text/plain diff --git a/parallel/parallel_src/extern/cista/.github/workflows/windows.yml b/parallel/parallel_src/extern/cista/.github/workflows/windows.yml new file mode 100644 index 00000000..f15da223 --- /dev/null +++ b/parallel/parallel_src/extern/cista/.github/workflows/windows.yml @@ -0,0 +1,45 @@ +name: Windows Build + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + release: + types: + - published + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: windows-latest + + strategy: + matrix: + mode: [ Debug, Release ] + arch: [ amd64, x86 ] + + env: + CXX: cl.exe + CC: cl.exe + + steps: + - uses: actions/checkout@v4 + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Install ninja + run: choco install ninja + + - name: Build + run: | + cmake -GNinja -S . -B build -DCMAKE_BUILD_TYPE=${{ matrix.mode }} + cmake --build build --target cista-test cista-test-single-header + + # ==== TESTS ==== + - name: Run Tests + run: .\build\cista-test.exe + - name: Run Single Header Tests + run: .\build\cista-test-single-header.exe diff --git a/parallel/parallel_src/extern/cista/.gitignore b/parallel/parallel_src/extern/cista/.gitignore new file mode 100644 index 00000000..ab7a7c4e --- /dev/null +++ b/parallel/parallel_src/extern/cista/.gitignore @@ -0,0 +1,7 @@ +/*build* +/.vscode +/.idea +/out +/.vs +/CMakeSettings.json +*.bin diff --git a/parallel/parallel_src/extern/cista/CMake/cistaConfig.cmake.in b/parallel/parallel_src/extern/cista/CMake/cistaConfig.cmake.in new file mode 100644 index 00000000..25a54232 --- /dev/null +++ b/parallel/parallel_src/extern/cista/CMake/cistaConfig.cmake.in @@ -0,0 +1,6 @@ +@PACKAGE_INIT@ + +if(NOT TARGET cista::cista) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + include(${CMAKE_CURRENT_LIST_DIR}/cistaTargets.cmake) +endif() diff --git a/parallel/parallel_src/extern/cista/CMakeLists.txt b/parallel/parallel_src/extern/cista/CMakeLists.txt new file mode 100755 index 00000000..11aa026f --- /dev/null +++ b/parallel/parallel_src/extern/cista/CMakeLists.txt @@ -0,0 +1,209 @@ +cmake_minimum_required(VERSION 3.8) + +if (NOT DEFINED PROJECT_NAME AND CISTA_HASH STREQUAL "FNV1A") + set(CISTA_INSTALL ON) +endif() + +project(cista LANGUAGES CXX VERSION 0.7) + +include(GNUInstallDirs) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + set(cista-compile-flags + -Wno-unknown-warning-option + -Wno-global-constructors + -Wno-exit-time-destructors + -fno-strict-aliasing + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-newline-eof + -Wno-missing-prototypes + -Wno-padded + -Wno-double-promotion + -Wno-undef + -Wno-undefined-reinterpret-cast + -Wno-float-conversion + -Wno-gnu-zero-variadic-macro-arguments + -Wno-unknown-pragmas + -Wno-documentation-unknown-command + -Wno-reserved-identifier + -Wno-weak-vtables + -Wno-unneeded-member-function + -Wno-unused-member-function + -Wno-unsafe-buffer-usage + -Wno-deprecated-declarations + -Wno-ctad-maybe-unsupported + -Wno-self-assign-overloaded + -Werror + ) +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(cista-compile-flags -Wall -Wextra) +endif() + +option(CISTA_ZERO_OUT "zero out fresh memory for valgrind" OFF) +option(CISTA_COVERAGE "generate coverage report" OFF) +option(CISTA_GENERATE_TO_TUPLE "generate include/cista/reflection/to_tuple.h" OFF) +option(CISTA_USE_MIMALLOC "compile with mimalloc support" OFF) +set(CISTA_HASH "FNV1A" CACHE STRING "Options: FNV1A XXH3 WYHASH WYHASH_FASTEST") + +add_library(cista INTERFACE) +if (CISTA_HASH STREQUAL "XXH3") + add_subdirectory(tools/xxh3) + target_link_libraries(cista INTERFACE xxh3) +elseif(CISTA_HASH STREQUAL "WYHASH" OR CISTA_HASH STREQUAL "WYHASH_FASTEST") + add_subdirectory(tools/wyhash) + target_link_libraries(cista INTERFACE wyhash) +endif() +target_compile_definitions(cista INTERFACE CISTA_${CISTA_HASH}=1) +if (CISTA_ZERO_OUT) + target_compile_definitions(cista INTERFACE CISTA_ZERO_OUT=1) +endif() +if (CISTA_USE_MIMALLOC) + target_compile_definitions(cista INTERFACE CISTA_USE_MIMALLOC=1) +endif() +target_include_directories(cista SYSTEM INTERFACE + $ + $) +target_compile_features(cista INTERFACE cxx_std_17) + +if (${CISTA_GENERATE_TO_TUPLE}) + add_subdirectory(tools/to_tuple_generator EXCLUDE_FROM_ALL) + add_custom_target(generate_to_tuple + COMMAND to_tuple_generator + 64 # max number of supported member fields + > ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/to_tuple.h + ) + add_dependencies(cista generate_to_tuple) +endif() + +add_subdirectory(tools/doctest EXCLUDE_FROM_ALL) + +file(GLOB_RECURSE cista-include-files include/*.h*) + +add_subdirectory(tools/uniter EXCLUDE_FROM_ALL) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/cista.h + COMMAND uniter + ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/serialization.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/comparable.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/printable.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/member_index.h + > ${CMAKE_CURRENT_BINARY_DIR}/cista.h + DEPENDS ${cista-include-files} +) + +file(GLOB_RECURSE cista-test-files test/*.cc) +add_executable(cista-test-single-header EXCLUDE_FROM_ALL ${cista-test-files} ${CMAKE_CURRENT_BINARY_DIR}/cista.h) +target_link_libraries(cista-test-single-header cista-doctest) +target_include_directories(cista-test-single-header PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +target_compile_options(cista-test-single-header PRIVATE ${cista-compile-flags}) +target_compile_definitions(cista-test-single-header PRIVATE SINGLE_HEADER) +target_compile_features(cista-test-single-header PRIVATE cxx_std_17) +if (CISTA_ZERO_OUT) + target_compile_definitions(cista-test-single-header PRIVATE CISTA_ZERO_OUT=1) +endif() + +add_executable(cista-test EXCLUDE_FROM_ALL ${cista-test-files}) +target_compile_options(cista-test PRIVATE ${cista-compile-flags}) +target_link_libraries(cista-test cista-doctest cista) +if(CISTA_COVERAGE) + target_compile_options(cista-test PRIVATE -fprofile-arcs -ftest-coverage) + set_target_properties(cista-test PROPERTIES LINK_FLAGS --coverage) +endif() + +add_custom_target(cista-coverage + rm -rf *.info && + find . -name "*.gcda" -delete && + ./cista-test && + lcov --directory . --capture --output-file cov.info && + lcov -r cov.info "*usr/include/*" -o cov.info && + lcov -r cov.info "*doctest*" -o cov.info && + lcov -r cov.info "*test/*.cc" -o cov.info && + lcov -r cov.info "*v1*" -o cov.info +) +add_dependencies(cista-coverage cista-test) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + message(STATUS "Cista fuzzing enabled") + set(cista-fuzz-targets "") + file(GLOB_RECURSE fuzz-files fuzz/*.cc) + foreach(fuzz-file ${fuzz-files}) + get_filename_component(test-name ${fuzz-file} NAME_WE) + + add_executable(cista-fuzz-${test-name}-main EXCLUDE_FROM_ALL ${fuzz-file}) + target_link_libraries(cista-fuzz-${test-name}-main cista -fsanitize=address,undefined) + target_compile_options(cista-fuzz-${test-name}-main PRIVATE -g -O0) + target_compile_definitions(cista-fuzz-${test-name}-main PRIVATE MAIN) + + add_executable(cista-fuzz-${test-name} EXCLUDE_FROM_ALL ${fuzz-file}) + target_link_libraries(cista-fuzz-${test-name} cista -fsanitize=address,undefined,fuzzer) + target_compile_options(cista-fuzz-${test-name} PRIVATE -g -O0 -fsanitize=address,fuzzer) + + add_executable(cista-fuzz-${test-name}-seed EXCLUDE_FROM_ALL ${fuzz-file}) + target_link_libraries(cista-fuzz-${test-name}-seed cista) + target_compile_definitions(cista-fuzz-${test-name}-seed PRIVATE GENERATE_SEED) + + add_custom_target(cista-fuzz-${test-name}-run + DEPENDS + cista-fuzz-${test-name} + cista-fuzz-${test-name}-seed + COMMAND + mkdir -p fuzz-${test-name}-corpus && + ./cista-fuzz-${test-name}-seed ./fuzz-${test-name}-corpus/seed.bin && + ./cista-fuzz-${test-name} ./fuzz-${test-name}-corpus -max_total_time=120 + ) + + list(APPEND cista-fuzz-targets cista-fuzz-${test-name}-run) + endforeach() + + add_custom_target(cista-fuzz) + add_dependencies(cista-fuzz ${cista-fuzz-targets}) +endif() + +add_library(cista::cista ALIAS cista) + +# Export targets when not used via `add_subdirectory` +if (CISTA_INSTALL) + include(CMakePackageConfigHelpers) + set(CISTA_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/cista") + + configure_package_config_file( + ${CMAKE_CURRENT_LIST_DIR}/CMake/cistaConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/cistaConfig.cmake + INSTALL_DESTINATION ${CISTA_CMAKE_CONFIG_DESTINATION} + ) + + install( + TARGETS cista + EXPORT cistaTargets + DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + + install( + EXPORT cistaTargets + NAMESPACE cista:: + DESTINATION ${CISTA_CMAKE_CONFIG_DESTINATION} + ) + + install( + DIRECTORY "include/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + ) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/cistaConfigVersion.cmake" + COMPATIBILITY SameMajorVersion + ) + + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/cistaConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cistaConfigVersion.cmake" + DESTINATION ${CISTA_CMAKE_CONFIG_DESTINATION} + ) +endif() diff --git a/parallel/parallel_src/extern/cista/LICENSE b/parallel/parallel_src/extern/cista/LICENSE new file mode 100644 index 00000000..495d1410 --- /dev/null +++ b/parallel/parallel_src/extern/cista/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018-2021 Felix Gündling + +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. \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/README.md b/parallel/parallel_src/extern/cista/README.md new file mode 100644 index 00000000..485907ca --- /dev/null +++ b/parallel/parallel_src/extern/cista/README.md @@ -0,0 +1,175 @@ +

+ +

+ + + + + + + + + + + + +

+ +# Simple C++ Serialization & Reflection. + +Cista++ is a simple, open source (MIT license) C++17 compatible way of (de-)serializing C++ data structures. + +*Single header - no dependencies. No macros. No source code generation.* + + - Raw performance - use your native structs. Supports modification/resizing of deserialized data! + - Supports complex and cyclic data structures including cyclic references, recursive data structures, etc. + - Save 50% memory: serialize directly to the filesystem if needed, no intermediate buffer required. + - Fuzzing-checked though continuous fuzzing using LLVMs LibFuzzer. + - Comes with a serializable high-performance hash map and hash set implementation based on [Google's Swiss Table](https://abseil.io/blog/20180927-swisstables). + - Reduce boilerplate code: automatic derivation of hash and equality functions. + - Built-in optional automatic data structure versioning through recursive type hashing. + - Optional check sum to prevent deserialization of corrupt data. + - Compatible with Clang, GCC, and MSVC + +The underlying reflection mechanism can be used in [other ways](https://cista.rocks/#reflection), too! + +**Examples:** + +Download the [latest release](https://github.com/felixguendling/cista/releases/latest/download/cista.h) and try it out. + +Simple example writing to a buffer: + +```cpp +namespace data = cista::raw; +struct my_struct { // Define your struct. + int a_{0}; + struct inner { + data::string b_; + } j; +}; + +std::vector buf; +{ // Serialize. + my_struct obj{1, {data::string{"test"}}}; + buf = cista::serialize(obj); +} + +// Deserialize. +auto deserialized = cista::deserialize(buf); +assert(deserialized->j.b_ == data::string{"test"}); +``` + +Advanced example writing a hash map to a memory mapped file: + +```cpp +namespace data = cista::offset; +constexpr auto const MODE = // opt. versioning + check sum + cista::mode::WITH_VERSION | cista::mode::WITH_INTEGRITY; + +struct pos { int x, y; }; +using pos_map = // Automatic deduction of hash & equality + data::hash_map, + data::hash_set>; + +{ // Serialize. + auto positions = + pos_map{{{{1, 2}, {3, 4}}, {"hello", "cista"}}, + {{{5, 6}, {7, 8}}, {"hello", "world"}}}; + cista::buf mmap{cista::mmap{"data"}}; + cista::serialize(mmap, positions); +} + +// Deserialize. +auto b = cista::mmap("data", cista::mmap::protection::READ); +auto positions = cista::deserialize(b); +``` + +Advanced example showing support for non-aggregate types like derived classes or classes with custom constructors: + +```cpp +namespace data = cista::offset; +constexpr auto MODE = cista::mode::WITH_VERSION; + +struct parent { + parent() = default; + explicit parent(int a) : x_{a}, y_{a} {} + auto cista_members() { return std::tie(x_, y_); } + int x_, y_; +}; +struct child : parent { + child() = default; + explicit child(int a) : parent{a}, z_{a} {} + auto cista_members() { + return std::tie(*static_cast(this), z_); + } + int z_; +}; + +/* + * Automatically defaulted for you: + * - de/serialization + * - hashing (use child in hash containers) + * - equality comparison + * - data structure version ("type hash") + */ +using t = data::hash_map; + +// ... usage, serialization as in the previous examples +``` + +# Benchmarks + +Have a look at the [benchmark repository](https://github.com/felixguendling/cpp-serialization-benchmark) for more details. + +| Library | Serialize | Deserialize | Fast Deserialize | Traverse | Deserialize & Traverse | Size | +| :--- | ---: | ---: | ---: | ---: | ---: | ---: | +| [Cap’n Proto](https://capnproto.org/capnp-tool.html) | 105 ms | **0.002 ms** | **0.0 ms** | 356 ms | 353 ms | 50.5M | +| [cereal](https://uscilab.github.io/cereal/index.html) | 239 ms | 197.000 ms | - | 125 ms | 322 ms | 37.8M | +| [Cista++](https://cista.rocks/) `offset` | **72 ms** | 0.053 ms | **0.0 ms** | 132 ms | **132 ms** | **25.3M** | +| [Cista++](https://cista.rocks/) `raw` | 3555 ms | 68.900 ms | 21.5 ms | **112 ms** | **133 ms** | 176.4M | +| [Flatbuffers](https://google.github.io/flatbuffers/) | 2349 ms | 15.400 ms | **0.0 ms** | 136 ms | **133 ms** | 63.0M | + + +# Use Cases + +Reader and writer should have the same pointer width. Loading data on systems with a different byte order (endianess) is supported. +Examples: + + - Asset loading for all kinds of applications (i.e. game assets, GIS data, large graphs, etc.) + - Transferring data over network + - shared memory applications + +Currently, only C++17 software can read/write data. +But it should be possible to generate accessors +for other programming languages, too. + +# Alternatives + +If you need to be compatible with other programming languages +or require protocol evolution (downward compatibility) +you should look for another solution: + + - [Protocol Buffers](https://developers.google.com/protocol-buffers/) + - [Cap’n Proto](https://capnproto.org/) + - [Flatbuffers](https://google.github.io/flatbuffers/) + - [cereal](https://uscilab.github.io/cereal/) + - [Boost Serialization](https://www.boost.org/doc/libs/1_68_0/libs/serialization/doc/index.html) + - [MessagePack](https://msgpack.org/) + - [tser](https://github.com/KonanM/tser) + - [... many more](https://en.wikipedia.org/wiki/Comparison_of_data_serialization_formats) + +# Documentation + +* [Installation and Usage](https://github.com/felixguendling/cista/wiki/Installation-and-Usage) +* [Serialization Reference](https://github.com/felixguendling/cista/wiki/Serialization-Reference) +* [Custom (De-)Serialization Functions](https://github.com/felixguendling/cista/wiki/Custom-(De-)Serialization-Functions) +* [Data Structure Versioning](https://github.com/felixguendling/cista/wiki/Data-Structure-Versioning) +* [Hash Containers](https://github.com/felixguendling/cista/wiki/Hash-Containers) + * [Hashing Framework](https://github.com/felixguendling/cista/wiki/Hashing-Framework) + * [Equality Framework](https://github.com/felixguendling/cista/wiki/Equality-Framework) + * [Benchmark](https://github.com/felixguendling/cista/wiki/Hash-Map-Benchmark) +* [Security](https://github.com/felixguendling/cista/wiki/Security) + +# Contribute + +Feel free to contribute (bug reports, pull requests, etc.)! diff --git a/parallel/parallel_src/extern/cista/fuzz/bitset_verification.cc b/parallel/parallel_src/extern/cista/fuzz/bitset_verification.cc new file mode 100755 index 00000000..eff6cb75 --- /dev/null +++ b/parallel/parallel_src/extern/cista/fuzz/bitset_verification.cc @@ -0,0 +1,250 @@ +#include +#include +#include +#include + +#include "cista/containers/bitset.h" + +enum op { SHIFT_LEFT, SHIFT_RIGHT, XOR, OR, AND, NEGATE, NUM_OPS }; +char const* op_strings[] = {"SHIFT_LEFT", "SHIFT_RIGHT", "XOR", "OR", + "AND", "NEGATE", "NUM_OPS"}; + +template +bool operator<(std::bitset const& x, + std::bitset const& y) { + for (size_t i = BitSetSize - 1; i != 0; --i) { + if (x[i] ^ y[i]) return y[i]; + } + if (x[0] ^ y[0]) return y[0]; + return false; +} + +template +bool operator>(std::bitset const& x, + std::bitset const& y) { + return y < x; +} + +template +bool operator<=(std::bitset const& x, + std::bitset const& y) { + return !(x > y); +} + +template +bool operator>=(std::bitset const& x, + std::bitset const& y) { + return !(x < y); +} + +template +struct test_set { + template + void init(Fn&& gen) { + for (auto i = 0; i < ref1.size(); ++i) { + auto const val = gen(); + ref1.set(i, val); + uut1.set(i, val); + } + for (auto i = 0; i < ref1.size(); ++i) { + auto const val = gen(); + uut2.set(i, val); + ref2.set(i, val); + } + } + + void apply_op(op const o, size_t const num) { + auto const ref1_before = ref1; + auto const ref2_before = ref2; + auto const uut1_before = uut1; + auto const uut2_before = uut2; + + switch (o) { + case SHIFT_LEFT: + ref1 <<= num; + uut1 <<= num; + break; + + case SHIFT_RIGHT: + ref1 >>= num; + uut1 >>= num; + break; + + case XOR: + ref1 = ref1 ^ ref2; + uut1 = uut1 ^ uut2; + break; + + case OR: + ref1 |= ref2; + uut1 |= uut2; + break; + + case AND: + ref1 &= ref2; + uut1 &= uut2; + break; + + case NEGATE: + ref1 = ~ref1; + ref2 = ~ref2; + uut1 = ~uut1; + uut2 = ~uut2; + break; + + default: abort(); + } + + auto const print = [&](std::string_view msg) { + std::cerr << msg << ": OP=" << op_strings[o] << " NUM=" << num << "\n" + << " ref1_before: " << ref1_before << "\n" + << " uut1_before: " << uut1_before << "\n" + << " ref2_before: " << ref2_before << "\n" + << " uut2_before: " << uut2_before << "\n" + << " ref1: " << ref1 << "\n" + << " uut1: " << uut1 << "\n" + << " ref2: " << ref2 << "\n" + << " uut2: " << uut2 << "\n"; + }; + + if ((ref1 < ref2) != (uut1 < uut2)) { + std::cerr << "uut1 < uut2 => " << (uut1 < uut2) << "\n" + << "ref1 < ref2 => " << (ref1 < ref2) << "\n"; + print("fail on <"); + abort(); + } + + if ((ref1 <= ref2) != (uut1 <= uut2)) { + std::cerr << "uut1 <= uut2 => " << (uut1 <= uut2) << "\n" + << "ref1 <= ref2 => " << (ref1 <= ref2) << "\n"; + print("fail on <="); + abort(); + } + + if ((ref1 > ref2) != (uut1 > uut2)) { + std::cerr << "uut1 > uut2 => " << (uut1 > uut2) << "\n" + << "ref1 > ref2 => " << (ref1 > ref2) << "\n"; + print("fail on >"); + abort(); + } + + if ((ref1 >= ref2) != (uut1 >= uut2)) { + std::cerr << "uut1 >= uut2 => " << (uut1 >= uut2) << "\n" + << "ref1 >= ref2 => " << (ref1 >= ref2) << "\n"; + print("fail on >="); + abort(); + } + + if ((ref1 == ref2) != (uut1 == uut2)) { + std::cerr << "uut1 == uut2 => " << (uut1 == uut2) << "\n" + << "ref1 == ref2 => " << (ref1 == ref2) << "\n"; + print("fail on =="); + abort(); + } + + if ((ref1 != ref2) != (uut1 != uut2)) { + std::cerr << "uut1 != uut2 => " << (uut1 != uut2) << "\n" + << "ref1 != ref2 => " << (ref1 != ref2) << "\n"; + print("fail on !="); + abort(); + } + + if (uut1.any() != ref1.any()) { + std::cerr << "uut1.any() => " << uut1.any() << "\n" + << "ref1.any() => " << ref1.any() << "\n"; + print("fail on any 1"); + abort(); + } + + if (uut2.any() != ref2.any()) { + std::cerr << "uut2.any() => " << uut2.any() << "\n" + << "ref2.any() => " << ref2.any() << "\n"; + print("fail on any 2"); + abort(); + } + + if (uut1.none() != ref1.none()) { + std::cerr << "uut1.none() => " << uut1.none() << "\n" + << "ref1.none() => " << ref1.none() << "\n"; + print("fail on none 1"); + abort(); + } + + if (uut2.none() != ref2.none()) { + std::cerr << "uut2.none() => " << uut2.none() << "\n" + << "ref2.none() => " << ref2.none() << "\n"; + print("fail on none 2"); + abort(); + } + + if (uut1.to_string() != ref1.to_string()) { + print("fail on 1"); + abort(); + } + + if (uut2.to_string() != ref2.to_string()) { + print("fail on 2"); + abort(); + } + } + + std::bitset ref1, ref2; + cista::bitset uut1, uut2; +}; + +#if defined(GENERATE_SEED) +int main() {} +#else +#if defined(MAIN) +int main(int argc, char const** argv) { + if (argc != 2) { + std::cout << "usage: " << argv[0] << " INPUT\n"; + return 1; + } + + auto in = std::ifstream{}; + in.exceptions(std::ios::failbit | std::ios::badbit); + in.open(argv[1], std::ios_base::binary); + auto str = std::string{}; + + in.seekg(0, std::ios::end); + str.reserve(in.tellg()); + in.seekg(0, std::ios::beg); + + str.assign((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + auto data = reinterpret_cast(str.data()); + auto const size = str.size(); +#else +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) { +#endif + auto test_sets = + std::make_tuple(test_set<22>{}, test_set<77>{}, test_set<222>{}, + test_set<64>{}, test_set<128>{}, test_set<192>{}); + + auto const end = data + size; + + auto gen = [&, bit = 0U]() mutable { + if (data >= end) { + return false; + } + auto const boolean = ((data[0] >> bit) & 1) != 0U; + ++bit; + if (bit == 8U) { + ++data; + bit = 0U; + } + return boolean; + }; + + std::apply([&](auto&... t) { (t.init(gen), ...); }, test_sets); + + for (; data != end; ++data) { + auto const o = static_cast(data[0U] % NUM_OPS); + auto const num = (data[0U] & 0xF0) % 100; + std::apply([&](auto&... t) { (t.apply_op(o, num), ...); }, test_sets); + } + + return 0; +} +#endif diff --git a/parallel/parallel_src/extern/cista/fuzz/bitvec_verification.cc b/parallel/parallel_src/extern/cista/fuzz/bitvec_verification.cc new file mode 100644 index 00000000..2fa6134a --- /dev/null +++ b/parallel/parallel_src/extern/cista/fuzz/bitvec_verification.cc @@ -0,0 +1,248 @@ +#include +#include +#include +#include +#include + +#include "cista/containers/bitvec.h" + +enum op { RESIZE, SET, NUM_OPS }; +char const* op_strings[] = {"RESIZE", "SET", "NUM_OPS"}; + +std::ostream& operator<<(std::ostream& out, std::vector const& v) { + for (auto i = 0U; i != v.size(); ++i) { + auto const j = v.size() - i - 1U; + out << (v[j] ? '1' : '0'); + } + return out; +} + +std::string to_string(std::vector const& v) { + auto ss = std::stringstream{}; + ss << v; + return ss.str(); +} + +bool any(std::vector const& v) { + return std::any_of(begin(v), end(v), [](bool const b) { return b; }); +} + +template +bool bitvec_lt(T const& x, T const& y) { + assert(x.size() == y.size()); + if (x.size() == 0U) { + return false; + } + for (std::size_t i = 0U; i != x.size(); ++i) { + auto const j = static_cast(x.size() - i - 1U); + if (x[i] ^ y[i]) { + return y[i]; + } + } + if (x[0] ^ y[0]) { + return y[0]; + } + return false; +} + +struct test_set { + template + void init(Fn&& gen) { + for (auto i = 0; i < ref1.size(); ++i) { + auto const val = gen(); + ref1[i] = val; + uut1.set(i, val); + } + for (auto i = 0; i < ref1.size(); ++i) { + auto const val = gen(); + uut2.set(i, val); + ref2[i] = val; + } + + auto const print = [&](std::string_view msg) { + std::cerr << msg << ": INIT\n" + << " ref1: " << ref1 << "\n" + << " uut1: " << uut1 << "\n" + << " ref2: " << ref2 << "\n" + << " uut2: " << uut2 << "\n"; + }; + + if (uut1.str() != to_string(ref1)) { + print("fail on init"); + abort(); + } + + if (uut2.str() != to_string(ref2)) { + print("fail on 2"); + abort(); + } + } + + void apply_op(op const o, std::size_t const num, bool const value) { + auto const ref1_before = ref1; + auto const ref2_before = ref2; + auto const uut1_before = uut1; + auto const uut2_before = uut2; + + switch (o) { + case RESIZE: + ref1.resize(num); + ref2.resize(num); + uut1.resize(num); + uut2.resize(num); + break; + + case SET: { + if (!ref1.empty()) { + auto const index = + std::min({ref1.size() - 1U, ref2.size() - 1U, num}); + ref1[index] = value; + ref2[index] = value; + uut1.set(index, value); + uut2.set(index, value); + } + break; + } + + default: abort(); + } + + auto const print = [&](std::string_view msg) { + std::cerr << msg << ": OP=" << op_strings[o] << " NUM=" << num << "\n" + << " ref1_before: " << ref1_before << "\n" + << " uut1_before: " << uut1_before << "\n" + << " ref2_before: " << ref2_before << "\n" + << " uut2_before: " << uut2_before << "\n" + << " ref1: " << ref1 << "\n" + << " uut1: " << uut1 << "\n" + << " ref2: " << ref2 << "\n" + << " uut2: " << uut2 << "\n"; + }; + + if (bitvec_lt(ref1, ref2) != bitvec_lt(uut1, uut2)) { + std::cerr << "uut1 < uut2 => " << bitvec_lt(uut1, uut2) << "\n" + << "ref1 < ref2 => " << bitvec_lt(ref1, ref2) << "\n"; + print("fail on <"); + abort(); + } + + if ((ref1 == ref2) != (uut1 == uut2)) { + std::cerr << "uut1 == uut2 => " << (uut1 == uut2) << "\n" + << "ref1 == ref2 => " << (ref1 == ref2) << "\n"; + print("fail on =="); + abort(); + } + + if ((ref1 != ref2) != (uut1 != uut2)) { + std::cerr << "uut1 != uut2 => " << (uut1 != uut2) << "\n" + << "ref1 != ref2 => " << (ref1 != ref2) << "\n"; + print("fail on !="); + abort(); + } + + if (uut1.any() != any(ref1)) { + std::cerr << "uut1.any() => " << uut1.any() << "\n" + << "ref1.any() => " << any(ref1) << "\n"; + print("fail on any 1"); + abort(); + } + + if (uut2.any() != any(ref2)) { + std::cerr << "uut2.any() => " << uut2.any() << "\n" + << "ref2.any() => " << any(ref2) << "\n"; + print("fail on any 2"); + abort(); + } + + if (uut1.none() != !any(ref1)) { + std::cerr << "uut1.none() => " << uut1.none() << "\n" + << "ref1.none() => " << !any(ref1) << "\n"; + print("fail on none 1"); + abort(); + } + + if (uut2.none() != !any(ref2)) { + std::cerr << "uut2.none() => " << uut2.none() << "\n" + << "ref2.none() => " << !any(ref2) << "\n"; + print("fail on none 2"); + abort(); + } + + if (uut1.str() != to_string(ref1)) { + print("fail on 1"); + abort(); + } + + if (uut2.str() != to_string(ref2)) { + print("fail on 2"); + abort(); + } + } + + void resize(std::size_t const size) { + ref1.resize(size); + ref2.resize(size); + uut1.resize(size); + uut2.resize(size); + } + + std::vector ref1, ref2; + cista::raw::bitvec uut1, uut2; +}; + +#if defined(GENERATE_SEED) +int main() {} +#else +#if defined(MAIN) +int main(int argc, char const** argv) { + if (argc != 2) { + std::cout << "usage: " << argv[0] << " INPUT\n"; + return 1; + } + + auto in = std::ifstream{}; + in.exceptions(std::ios::failbit | std::ios::badbit); + in.open(argv[1], std::ios_base::binary); + auto str = std::string{}; + + in.seekg(0, std::ios::end); + str.reserve(in.tellg()); + in.seekg(0, std::ios::beg); + + str.assign((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + auto data = reinterpret_cast(str.data()); + auto const size = str.size(); +#else +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) { +#endif + auto uut = test_set{}; + + auto const end = data + size; + + auto gen = [&, bit = 0U]() mutable { + if (data >= end) { + return false; + } + auto const boolean = ((data[0] >> bit) & 1) != 0U; + ++bit; + if (bit == 8U) { + ++data; + bit = 0U; + } + return boolean; + }; + + uut.resize(1440); + uut.init(gen); + + for (; data != end; ++data) { + auto const o = static_cast(data[0U] % NUM_OPS); + auto const num = (data[0U] & 0xF0) % 1440; + auto const value = (data[0U] & 0xF7) == 0; + uut.apply_op(o, num, value); + } + + return 0; +} +#endif diff --git a/parallel/parallel_src/extern/cista/fuzz/graph.cc b/parallel/parallel_src/extern/cista/fuzz/graph.cc new file mode 100755 index 00000000..ff070af6 --- /dev/null +++ b/parallel/parallel_src/extern/cista/fuzz/graph.cc @@ -0,0 +1,116 @@ +#include +#include + +#include "cista/serialization.h" +#include "cista/targets/file.h" + +namespace data = cista::raw; + +struct node; + +using node_id_t = uint32_t; + +struct edge { + data::ptr from_; + data::ptr to_; +}; + +struct node { + void add_edge(edge* e) { edges_.emplace_back(e); } + node_id_t id() const { return id_; } + + node_id_t id_{0}; + node_id_t fill_{0}; + data::vector> edges_; + data::string name_; +}; + +struct graph { + node* make_node(data::string name) { + return nodes_ + .emplace_back(data::make_unique( + node{next_node_id_++, 0, data::vector>{0u}, + std::move(name)})) + .get(); + } + + edge* make_edge(node_id_t const from, node_id_t const to) { + return edges_ + .emplace_back( + data::make_unique(edge{nodes_[from].get(), nodes_[to].get()})) + .get(); + } + + data::vector> nodes_; + data::vector> edges_; + node_id_t next_node_id_{0}; + node_id_t fill_{0}; +}; + +inline std::set bfs(node const* entry) { + if (entry == nullptr) { + return {}; + } + + std::queue q; + std::set visited; + + q.emplace(entry); + + while (!q.empty()) { + auto const next = q.front(); + q.pop(); + + if (visited.find(next) != end(visited)) { + continue; + } + + visited.emplace(next); + + for (auto const& e : next->edges_) { + if (e != nullptr && e->to_ != nullptr) { + q.emplace(e->to_); + } + } + } + + return visited; +} + +#if defined(GENERATE_SEED) +int main(int argc, char** argv) { + if (argc != 2) { + printf("usage: %s [seed file]\n", argv[0]); + return 0; + } + + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::file f{argv[1], "w+"}; + cista::serialize(f, g); +} +#else +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) { + try { + cista::buffer b{reinterpret_cast(data), size}; + auto const g = cista::deserialize(b); + if (!g->nodes_.empty() && g->nodes_[0].get() != nullptr) { + bfs(g->nodes_[0].get()).size(); + } + } catch (std::exception const&) { + } + return 0; +} +#endif \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/fuzz/hash_map_verification.cc b/parallel/parallel_src/extern/cista/fuzz/hash_map_verification.cc new file mode 100755 index 00000000..106e692b --- /dev/null +++ b/parallel/parallel_src/extern/cista/fuzz/hash_map_verification.cc @@ -0,0 +1,56 @@ +#include +#include + +#include + +#include "cista/containers/hash_map.h" + +#if defined(GENERATE_SEED) +int main() {} +#else +/** + * Check hash_map + * + * Input bytes: + * 0-3: key + * 4-7: value + * 9: operation => less or equal 128 insert, + * => greater 128 delete + */ +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) { + std::unordered_map ref; + cista::raw::hash_map uut; + + for (auto bytes_to_process = size; bytes_to_process > 8; + bytes_to_process -= 9, data += 9) { + auto key = 0; + auto value = 0; + auto const insert = data[8] <= 128 ? true : false; + + std::memcpy(&key, data, sizeof(key)); + std::memcpy(&value, data + sizeof(key), sizeof(value)); + + if (insert) { + ref.emplace(key, value); + uut.emplace(key, value); + } else { + ref.erase(key); + uut.erase(key); + } + } + + for (auto const& [key, value] : ref) { + if (uut.find(key) == end(uut)) { + abort(); + } + } + + for (auto const& [key, value] : uut) { + if (ref.find(key) == end(ref)) { + abort(); + } + } + + return 0; +} +#endif diff --git a/parallel/parallel_src/extern/cista/fuzz/hash_set.cc b/parallel/parallel_src/extern/cista/fuzz/hash_set.cc new file mode 100755 index 00000000..1a1ab2be --- /dev/null +++ b/parallel/parallel_src/extern/cista/fuzz/hash_set.cc @@ -0,0 +1,97 @@ +#include +#include +#include + +#include "cista/serialization.h" +#include "cista/targets/file.h" + +namespace data = cista::raw; + +struct hash { + size_t operator()(data::vector const& v) { + auto hash = cista::BASE_HASH; + for (auto const& s : v) { + hash = cista::hash(s, hash); + } + return hash; + } +}; + +struct eq { + bool operator()(data::vector const& a, + data::vector const& b) { + if (a.size() != b.size()) { + return false; + } + for (auto ia = a.begin(), ib = b.begin(); ia != a.end(); ++ia, ++ib) { + if (*ia != *ib) { + return false; + } + } + return true; + } +}; + +using serialize_me_t = data::hash_set, hash, eq>; + +#if defined(GENERATE_SEED) +int main(int argc, char** argv) { + if (argc != 2) { + printf("usage: %s [seed file]\n", argv[0]); + return 0; + } + + auto const make_e1 = []() { + data::vector e1; + e1.emplace_back("short"); + e1.emplace_back("long long long long long long long", + data::string::owning_t{}); + return e1; + }; + + auto const make_e2 = []() { + data::vector e2; + e2.emplace_back("hello"); + e2.emplace_back("world"); + e2.emplace_back("yeah!"); + return e2; + }; + + auto const make_e3 = []() { + data::vector e3; + e3.emplace_back("This"); + e3.emplace_back("is"); + e3.emplace_back("Sparta"); + e3.emplace_back("!!!"); + return e3; + }; + + serialize_me_t s; + s.emplace(make_e1()); + s.emplace(make_e2()); + s.emplace(make_e3()); + + cista::file f{argv[1], "w+"}; + cista::serialize(f, s); +} +#else +static bool __attribute__((used)) found; +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) { + try { + cista::buffer b{reinterpret_cast(data), size}; + for (auto const& el : + *cista::deserialize(b)) { + if (auto const it = std::find_if( + begin(el), end(el), + [&](auto&& el) { + return el == "The quick brown fox jumps over the lazy dog"; + }); + it != end(el)) { + found = true; + } + } + } catch (std::exception const&) { + } + return 0; +} +#endif \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/fuzz/multimap_verification.cc b/parallel/parallel_src/extern/cista/fuzz/multimap_verification.cc new file mode 100644 index 00000000..fe33bbe4 --- /dev/null +++ b/parallel/parallel_src/extern/cista/fuzz/multimap_verification.cc @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include + +#include "cista/containers/mutable_fws_multimap.h" +#include "cista/mmap.h" + +using my_key = cista::strong; + +/* +struct my_entry { + my_entry() : i_{static_cast(malloc(sizeof(int)))} { + cista::verify(i_ != nullptr, "bad malloc"); + *i_ = 0; + } + + my_entry(my_entry const& e) : i_{static_cast(malloc(sizeof(int)))} { + cista::verify(i_ != nullptr, "bad malloc"); + if (e.i_ != nullptr) { + *i_ = *e.i_; + } + } + + my_entry(my_entry&& e) noexcept : i_{e.i_} { e.i_ = nullptr; } + + my_entry& operator=(my_entry const& e) { + if (&e == this) { + return *this; + } + if (i_ == nullptr) { + i_ = static_cast(malloc(sizeof(int))); + } + cista::verify(i_ != nullptr, "bad malloc"); + *i_ = *e.i_; + return *this; + } + + my_entry& operator=(my_entry&& e) noexcept { + std::free(i_); + i_ = e.i_; + e.i_ = nullptr; + return *this; + } + + constexpr bool operator==(my_entry const& o) const { + return i_ == nullptr && o.i_ == nullptr || + (i_ != nullptr && o.i_ != nullptr && *i_ == *o.i_); + } + + friend std::ostream& operator<<(std::ostream& out, my_entry const& e) { + if (e.i_ == nullptr) { + return out << "nullptr"; + } else { + return out << e.i_; + } + } + + ~my_entry() { std::free(i_); } + + int* i_{nullptr}; +}; +*/ + +using my_entry = int; + +int run(std::uint8_t const* data, size_t const size) { + cista::offset::mutable_fws_multimap uut; + std::multimap ref; + + my_key key; + my_entry value; + constexpr auto const block_size = 1 + sizeof(key.v_) + sizeof(int); + for (auto bytes_to_process = size; bytes_to_process > block_size; + bytes_to_process -= block_size, data += block_size) { + auto const insert = data[8] <= 128 ? true : false; + + std::memcpy(&key.v_, data, sizeof(key.v_)); + std::memcpy(&value, data + sizeof(key.v_), sizeof(int)); + + if (key > 1'000'000) { + continue; + } + + if (insert) { + std::cerr << "insert key=" << key << ", value=" << value << "\n"; + ref.emplace(key, value); + uut.get_or_create(key).emplace_back(value); + } else { + std::cerr << "erase key=" << key << "\n"; + ref.erase(key); + uut.erase(key); + } + } + + auto const print_range = [](auto const i1, auto const i2, auto&& fn) { + std::cerr << "{"; + for (auto it = i1; it != i2; ++it) { + std::cerr << fn(*it) << ", "; + } + std::cerr << "}" << std::endl; + }; + + for (auto const& bucket : uut) { + using std::begin; + using std::end; + auto const key = bucket.index(); + auto const ref_range = ref.equal_range(my_key{key}); + auto const equal = std::equal( + begin(bucket), end(bucket), ref_range.first, ref_range.second, + [](my_entry const& a, std::pair const& b) { + return a == b.second; + }); + + if (!equal) { + std::cerr << "uut:\n"; + print_range(begin(bucket), end(bucket), [](auto&& x) { return x; }); + + std::cerr << "ref:\n"; + print_range(ref_range.first, ref_range.second, + [](auto&& x) { return x.second; }); + abort(); + } + } + + for (auto const& [k, v] : ref) { + auto const bucket = uut.at(k); + auto const found = std::find(begin(bucket), end(bucket), v) != end(bucket); + if (!found) { + abort(); + } + } + + return 0; +} + +#if defined(GENERATE_SEED) +int main() {} +#elif defined(MAIN) +int main(int ac, char** av) { + if (ac < 2) { + std::cout << "usage: " << av[0] << " [crash-file]\n"; + return 0; + } + + if (!std::filesystem::is_regular_file(av[1])) { + std::cout << "file " << av[1] << " does not exist\n"; + return 0; + } + + cista::mmap m{av[1], cista::mmap::protection::READ}; + return run(m.data(), m.size()); +} +#else +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) { + return run(data, size); +} +#endif diff --git a/parallel/parallel_src/extern/cista/include/cista/aligned_alloc.h b/parallel/parallel_src/extern/cista/include/cista/aligned_alloc.h new file mode 100644 index 00000000..b8b451d6 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/aligned_alloc.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include "cista/next_power_of_2.h" + +namespace cista { + +template +T1 to_next_multiple(T1 const n, T2 const multiple) noexcept { + auto const r = n % multiple; + return r == 0 ? n : n + multiple - r; +} + +} // namespace cista + +#if CISTA_USE_MIMALLOC + +#include "mimalloc.h" +#define CISTA_ALIGNED_ALLOC(alignment, size) \ + (mi_malloc_aligned( \ + cista::to_next_multiple((size), cista::next_power_of_two((alignment))), \ + cista::next_power_of_two((alignment)))) +#define CISTA_ALIGNED_FREE(alignment, ptr) \ + (mi_free_aligned((ptr), cista::next_power_of_two((alignment)))) + +#elif defined(_MSC_VER) + +#define CISTA_ALIGNED_ALLOC(alignment, size) \ + (_aligned_malloc((size), cista::next_power_of_two((alignment)))) +#define CISTA_ALIGNED_FREE(alignment, ptr) (_aligned_free((ptr))) + +#elif defined(_LIBCPP_HAS_C11_FEATURES) || defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) + +#include +#define CISTA_ALIGNED_ALLOC(alignment, size) \ + (std::aligned_alloc( \ + cista::next_power_of_two((alignment)), \ + cista::to_next_multiple((size), cista::next_power_of_two((alignment))))) +#define CISTA_ALIGNED_FREE(alignment, ptr) std::free((ptr)) + +#else + +#include +#define CISTA_ALIGNED_ALLOC(alignment, size) (std::malloc((size))) +#define CISTA_ALIGNED_FREE(alignment, ptr) (std::free((ptr))) + +#endif diff --git a/parallel/parallel_src/extern/cista/include/cista/aligned_allocator.h b/parallel/parallel_src/extern/cista/include/cista/aligned_allocator.h new file mode 100644 index 00000000..84cf3471 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/aligned_allocator.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include + +#include "cista/aligned_alloc.h" +#include "cista/exception.h" + +namespace cista { + +template +struct aligned_allocator { + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + using pointer = T*; + using const_pointer = const T*; + + using reference = T&; + using const_reference = const T&; + + aligned_allocator() noexcept = default; + + template + aligned_allocator(const aligned_allocator&) noexcept {} + + ~aligned_allocator() noexcept = default; + + pointer adress(reference r) const noexcept { return &r; } + + const_pointer adress(const_reference r) const noexcept { return &r; } + + pointer allocate(size_type const n) const { + auto const ptr = + static_cast(CISTA_ALIGNED_ALLOC(N, n * sizeof(value_type))); + if (ptr == nullptr) { + throw_exception(std::bad_alloc{}); + } + return ptr; + } + + void deallocate(pointer p, size_type) const { CISTA_ALIGNED_FREE(N, p); } + + void construct(pointer p, const value_type& wert) const { + new (p) value_type(wert); + } + + void destroy(pointer p) const { p->~value_type(); } + + size_type max_size() const noexcept { + return size_type(~0U) / sizeof(value_type); + } + + template + struct rebind { + using other = aligned_allocator; + }; + + bool operator!=(aligned_allocator const&) const noexcept { + return false; + } + bool operator==(aligned_allocator const&) const noexcept { + return true; + } +}; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/allocator.h b/parallel/parallel_src/extern/cista/include/cista/allocator.h new file mode 100644 index 00000000..99a6e38d --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/allocator.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace cista { + +template typename Ptr> +class allocator : public std::allocator { +public: + using size_type = std::size_t; + using pointer = Ptr; + using const_pointer = Ptr; + + template + struct rebind { + using other = allocator; + }; + + using std::allocator::allocator; + using std::allocator::allocate; + using std::allocator::deallocate; +}; + +} // namespace cista \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/bit_counting.h b/parallel/parallel_src/extern/cista/include/cista/bit_counting.h new file mode 100644 index 00000000..2a220ed9 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/bit_counting.h @@ -0,0 +1,120 @@ +#pragma once + +#if defined(_MSC_VER) +#include +#if defined(_M_X64) +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_BitScanForward64) +#endif +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) +#endif + +#include +#include + +namespace cista { + +template +inline constexpr unsigned constexpr_trailing_zeros(T t) { + auto const is_bit_set = [&](unsigned const i) { + return ((t >> i) & T{1U}) == T{1U}; + }; + for (auto i = 0U; i != sizeof(T) * 8U; ++i) { + if (is_bit_set(i)) { + return i; + } + } + return 0U; +} + +template +constexpr unsigned trailing_zeros(T t) noexcept { + static_assert(sizeof(T) == 8U || sizeof(T) == 4U, "not supported"); + + if (t == 0U) { + return sizeof(T) == 8U ? 64U : 32U; + } + + if constexpr (sizeof(T) == 8U) { // 64bit +#if defined(_MSC_VER) && defined(_M_X64) + unsigned long index = 0U; + _BitScanForward64(&index, t); + return index; +#elif defined(_MSC_VER) + unsigned long index = 0U; + if (static_cast(t) == 0) { + _BitScanForward(&index, t >> 32U); + return index + 32U; + } + _BitScanForward(&index, static_cast(t)); + return index; +#else + return static_cast(__builtin_ctzll(t)); +#endif + } else if constexpr (sizeof(T) == 4U) { // 32bit +#if defined(_MSC_VER) + unsigned long index = 0U; + _BitScanForward(&index, t); + return index; +#else + return static_cast(__builtin_ctz(t)); +#endif + } +} + +template +constexpr unsigned leading_zeros(T t) noexcept { + static_assert(sizeof(T) == 8U || sizeof(T) == 4U, "not supported"); + + if (t == 0U) { + return sizeof(T) == 8U ? 64U : 32U; + } + + if constexpr (sizeof(T) == 8U) { // 64bit +#if defined(_MSC_VER) && defined(_M_X64) + unsigned long index = 0U; + if (_BitScanReverse64(&index, t)) { + return 63U - index; + } + return 64U; +#elif defined(_MSC_VER) + unsigned long index = 0U; + if ((t >> 32U) && _BitScanReverse(&index, t >> 32U)) { + return 31U - index; + } + if (_BitScanReverse(&index, static_cast(t))) { + return 63U - index; + } + return 64U; +#else + return static_cast(__builtin_clzll(t)); +#endif + } else if constexpr (sizeof(T) == 4U) { // 32bit +#if defined(_MSC_VER) + unsigned long index = 0; + if (_BitScanReverse(&index, t)) { + return 31U - index; + } + return 32U; +#else + return static_cast(__builtin_clz(t)); +#endif + } +} + +inline std::size_t popcount(std::uint64_t const b) noexcept { +#if defined(_MSC_VER) && defined(_M_X64) + return __popcnt64(b); +#elif defined(_MSC_VER) + return static_cast( + __popcnt(static_cast(b)) + + __popcnt(static_cast(b >> 32U))); +#elif defined(__INTEL_COMPILER) + return static_cast(_mm_popcnt_u64(b)); +#else + return static_cast(__builtin_popcountll(b)); +#endif +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/buffer.h b/parallel/parallel_src/extern/cista/include/cista/buffer.h new file mode 100644 index 00000000..4b26e527 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/buffer.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +#include "cista/verify.h" + +namespace cista { + +struct buffer final { + constexpr buffer() noexcept : buf_(nullptr), size_(0U) {} + + explicit buffer(std::size_t const size) + : buf_(std::malloc(size)), size_(size) { + verify(buf_ != nullptr, "buffer initialization failed"); + } + + explicit buffer(char const* str) : buffer(std::strlen(str)) { + std::memcpy(buf_, str, size_); + } + + buffer(char const* str, std::size_t size) : buffer(size) { + std::memcpy(buf_, str, size_); + } + + ~buffer() { + std::free(buf_); + buf_ = nullptr; + } + + buffer(buffer const&) = delete; + buffer& operator=(buffer const&) = delete; + + buffer(buffer&& o) noexcept : buf_(o.buf_), size_(o.size_) { o.reset(); } + + buffer& operator=(buffer&& o) noexcept { + buf_ = o.buf_; + size_ = o.size_; + o.reset(); + return *this; + } + + std::size_t size() const noexcept { return size_; } + + std::uint8_t* data() noexcept { return static_cast(buf_); } + std::uint8_t const* data() const noexcept { + return static_cast(buf_); + } + + std::uint8_t* begin() noexcept { return data(); } + std::uint8_t* end() noexcept { return data() + size_; } + + std::uint8_t const* begin() const noexcept { return data(); } + std::uint8_t const* end() const noexcept { return data() + size_; } + + std::uint8_t& operator[](std::size_t const i) noexcept { return data()[i]; } + std::uint8_t const& operator[](std::size_t const i) const noexcept { + return data()[i]; + } + + void reset() noexcept { + buf_ = nullptr; + size_ = 0U; + } + + void* buf_; + std::size_t size_; +}; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/chunk.h b/parallel/parallel_src/extern/cista/include/cista/chunk.h new file mode 100644 index 00000000..17e89fa4 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/chunk.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace cista { + +template +void chunk(unsigned const chunk_size, std::size_t const total, Fn fn) { + std::size_t offset = 0U; + std::size_t remaining = total; + while (remaining != 0U) { + auto const curr_chunk_size = static_cast( + std::min(remaining, static_cast(chunk_size))); + fn(offset, curr_chunk_size); + offset += curr_chunk_size; + remaining -= curr_chunk_size; + } +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/cista_exception.h b/parallel/parallel_src/extern/cista/include/cista/cista_exception.h new file mode 100644 index 00000000..72a2461a --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/cista_exception.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "cista/exception.h" + +namespace cista { + +struct cista_exception : public std::runtime_error { + using std::runtime_error::runtime_error; +}; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers.h b/parallel/parallel_src/extern/cista/include/cista/containers.h new file mode 100644 index 00000000..bbc9e478 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers.h @@ -0,0 +1,21 @@ +#pragma once + +#include "cista/containers/array.h" +#include "cista/containers/bitset.h" +#include "cista/containers/bitvec.h" +#include "cista/containers/cstring.h" +#include "cista/containers/fws_multimap.h" +#include "cista/containers/hash_map.h" +#include "cista/containers/hash_set.h" +#include "cista/containers/mmap_vec.h" +#include "cista/containers/mutable_fws_multimap.h" +#include "cista/containers/nvec.h" +#include "cista/containers/optional.h" +#include "cista/containers/paged.h" +#include "cista/containers/paged_vecvec.h" +#include "cista/containers/string.h" +#include "cista/containers/tuple.h" +#include "cista/containers/unique_ptr.h" +#include "cista/containers/variant.h" +#include "cista/containers/vector.h" +#include "cista/containers/vecvec.h" \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/array.h b/parallel/parallel_src/extern/cista/include/cista/containers/array.h new file mode 100644 index 00000000..ac5df5ff --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/array.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace cista { + +template +using array = std::array; + +namespace raw { +using cista::array; +} // namespace raw + +namespace offset { +using cista::array; +} // namespace offset + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/bitset.h b/parallel/parallel_src/extern/cista/include/cista/containers/bitset.h new file mode 100644 index 00000000..bc2332f5 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/bitset.h @@ -0,0 +1,328 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cista/bit_counting.h" +#include "cista/containers/array.h" + +namespace cista { + +template +struct bitset { + using block_t = std::uint64_t; + static constexpr auto const bits_per_block = sizeof(block_t) * 8U; + static constexpr auto const num_blocks = + Size / bits_per_block + (Size % bits_per_block == 0U ? 0U : 1U); + + constexpr bitset() noexcept = default; + constexpr bitset(std::string_view s) noexcept { set(s); } + static constexpr bitset max() { + bitset ret; + for (auto& b : ret.blocks_) { + b = std::numeric_limits::max(); + } + return ret; + } + + auto cista_members() noexcept { return std::tie(blocks_); } + + constexpr void set(std::string_view s) noexcept { + for (std::size_t i = 0U; i != std::min(Size, s.size()); ++i) { + set(i, s[s.size() - i - 1U] != '0'); + } + } + + constexpr void set(std::size_t const i, bool const val = true) noexcept { + assert((i / bits_per_block) < num_blocks); + auto& block = blocks_[i / bits_per_block]; + auto const bit = i % bits_per_block; + auto const mask = block_t{1U} << bit; + if (val) { + block |= mask; + } else { + block &= (~block_t{0U} ^ mask); + } + } + + void reset() noexcept { blocks_ = {}; } + + bool operator[](std::size_t const i) const noexcept { return test(i); } + + std::size_t count() const noexcept { + std::size_t sum = 0U; + for (std::size_t i = 0U; i != num_blocks - 1U; ++i) { + sum += popcount(blocks_[i]); + } + return sum + popcount(sanitized_last_block()); + } + + constexpr bool test(std::size_t const i) const noexcept { + if (i >= Size) { + return false; + } + auto const block = blocks_[i / bits_per_block]; + auto const bit = (i % bits_per_block); + return (block & (block_t{1U} << bit)) != 0U; + } + + std::size_t size() const noexcept { return Size; } + + bool any() const noexcept { + for (std::size_t i = 0U; i != num_blocks - 1U; ++i) { + if (blocks_[i] != 0U) { + return true; + } + } + return sanitized_last_block() != 0U; + } + + bool none() const noexcept { return !any(); } + + block_t sanitized_last_block() const noexcept { + if constexpr ((Size % bits_per_block) != 0U) { + return blocks_[num_blocks - 1U] & + ~((~block_t{0U}) << (Size % bits_per_block)); + } else { + return blocks_[num_blocks - 1U]; + } + } + + template + void for_each_set_bit(Fn&& f) const { + auto const check_block = [&](std::size_t const i, block_t const block) { + if (block != 0U) { + for (auto bit = std::size_t{0U}; bit != bits_per_block; ++bit) { + if ((block & (block_t{1U} << bit)) != 0U) { + f(std::size_t{i * bits_per_block + bit}); + } + } + } + }; + for (auto i = std::size_t{0U}; i != blocks_.size() - 1; ++i) { + check_block(i, blocks_[i]); + } + check_block(blocks_.size() - 1, sanitized_last_block()); + } + + std::string to_string() const { + std::string s{}; + s.resize(Size); + for (std::size_t i = 0U; i != Size; ++i) { + s[i] = test(Size - i - 1U) ? '1' : '0'; + } + return s; + } + + friend bool operator==(bitset const& a, bitset const& b) noexcept { + for (std::size_t i = 0U; i != num_blocks - 1U; ++i) { + if (a.blocks_[i] != b.blocks_[i]) { + return false; + } + } + return a.sanitized_last_block() == b.sanitized_last_block(); + } + + friend bool operator<(bitset const& a, bitset const& b) noexcept { + auto const a_last = a.sanitized_last_block(); + auto const b_last = b.sanitized_last_block(); + if (a_last < b_last) { + return true; + } + if (b_last < a_last) { + return false; + } + + for (int i = num_blocks - 2; i != -1; --i) { + auto const x = a.blocks_[i]; + auto const y = b.blocks_[i]; + if (x < y) { + return true; + } + if (y < x) { + return false; + } + } + + return false; + } + friend bool operator!=(bitset const& a, bitset const& b) noexcept { + return !(a == b); + } + + friend bool operator>(bitset const& a, bitset const& b) noexcept { + return b < a; + } + + friend bool operator<=(bitset const& a, bitset const& b) noexcept { + return !(a > b); + } + + friend bool operator>=(bitset const& a, bitset const& b) noexcept { + return !(a < b); + } + + bitset& operator&=(bitset const& o) noexcept { + for (auto i = 0U; i < num_blocks; ++i) { + blocks_[i] &= o.blocks_[i]; + } + return *this; + } + + bitset& operator|=(bitset const& o) noexcept { + for (auto i = 0U; i < num_blocks; ++i) { + blocks_[i] |= o.blocks_[i]; + } + return *this; + } + + bitset& operator^=(bitset const& o) noexcept { + for (auto i = 0U; i < num_blocks; ++i) { + blocks_[i] ^= o.blocks_[i]; + } + return *this; + } + + bitset operator~() const noexcept { + auto copy = *this; + for (auto& b : copy.blocks_) { + b = ~b; + } + return copy; + } + + friend bitset operator&(bitset const& lhs, bitset const& rhs) noexcept { + auto copy = lhs; + copy &= rhs; + return copy; + } + + friend bitset operator|(bitset const& lhs, bitset const& rhs) noexcept { + auto copy = lhs; + copy |= rhs; + return copy; + } + + friend bitset operator^(bitset const& lhs, bitset const& rhs) noexcept { + auto copy = lhs; + copy ^= rhs; + return copy; + } + + bitset& operator>>=(std::size_t const shift) noexcept { + if (shift >= Size) { + reset(); + return *this; + } + + if constexpr ((Size % bits_per_block) != 0U) { + blocks_[num_blocks - 1U] = sanitized_last_block(); + } + + if constexpr (num_blocks == 1U) { + blocks_[0U] >>= shift; + return *this; + } else { + if (shift == 0U) { + return *this; + } + + auto const shift_blocks = shift / bits_per_block; + auto const shift_bits = shift % bits_per_block; + auto const border = num_blocks - shift_blocks - 1U; + + if (shift_bits == 0U) { + for (std::size_t i = 0U; i <= border; ++i) { + blocks_[i] = blocks_[i + shift_blocks]; + } + } else { + for (std::size_t i = 0U; i < border; ++i) { + blocks_[i] = + (blocks_[i + shift_blocks] >> shift_bits) | + (blocks_[i + shift_blocks + 1] << (bits_per_block - shift_bits)); + } + blocks_[border] = (blocks_[num_blocks - 1] >> shift_bits); + } + + for (auto i = border + 1U; i != num_blocks; ++i) { + blocks_[i] = 0U; + } + + return *this; + } + } + + bitset& operator<<=(std::size_t const shift) noexcept { + if (shift >= Size) { + reset(); + return *this; + } + + if constexpr (num_blocks == 1U) { + blocks_[0U] <<= shift; + return *this; + } else { + if (shift == 0U) { + return *this; + } + + auto const shift_blocks = shift / bits_per_block; + auto const shift_bits = shift % bits_per_block; + + if (shift_bits == 0U) { + for (auto i = std::size_t{num_blocks - 1}; i >= shift_blocks; --i) { + blocks_[i] = blocks_[i - shift_blocks]; + } + } else { + for (auto i = std::size_t{num_blocks - 1}; i != shift_blocks; --i) { + blocks_[i] = + (blocks_[i - shift_blocks] << shift_bits) | + (blocks_[i - shift_blocks - 1U] >> (bits_per_block - shift_bits)); + } + blocks_[shift_blocks] = blocks_[0U] << shift_bits; + } + + for (auto i = 0U; i != shift_blocks; ++i) { + blocks_[i] = 0U; + } + + return *this; + } + } + + bitset operator>>(std::size_t const i) const noexcept { + auto copy = *this; + copy >>= i; + return copy; + } + + bitset operator<<(std::size_t const i) const noexcept { + auto copy = *this; + copy <<= i; + return copy; + } + + friend std::ostream& operator<<(std::ostream& out, bitset const& b) { + return out << b.to_string(); + } + + cista::array blocks_{}; +}; + +} // namespace cista + +#if __has_include("fmt/ostream.h") + +#include "fmt/ostream.h" + +template +struct fmt::formatter> : ostream_formatter {}; + +#endif diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/bitvec.h b/parallel/parallel_src/extern/cista/include/cista/containers/bitvec.h new file mode 100644 index 00000000..529bbb85 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/bitvec.h @@ -0,0 +1,487 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cista/bit_counting.h" +#include "cista/containers/vector.h" +#include "cista/strong.h" + +namespace cista { + +template +struct basic_bitvec { + using block_t = typename Vec::value_type; + using size_type = typename Vec::size_type; + static constexpr auto const bits_per_block = + static_cast(sizeof(block_t) * 8); + + constexpr basic_bitvec() noexcept {} + basic_bitvec(std::string_view s) { set(s); } + basic_bitvec(size_type const size) { resize(size); } + constexpr basic_bitvec(Vec&& v) + : size_{v.size() * bits_per_block}, // inaccurate for loading mmap vector + blocks_{std::move(v)} {} + static constexpr basic_bitvec max(std::size_t const size) { + basic_bitvec ret; + ret.resize(size); + for (auto& b : ret.blocks_) { + b = std::numeric_limits::max(); + } + return ret; + } + + auto cista_members() noexcept { return std::tie(blocks_); } + + static constexpr size_type num_blocks(size_type num_bits) { + return static_cast(num_bits / bits_per_block + + (num_bits % bits_per_block == 0 ? 0 : 1)); + } + + void zero_out() { + for (auto& b : blocks_) { + b = 0U; + } + } + + void resize(size_type const new_size) { + if (new_size == size_) { + return; + } + + if (!empty() && (size_ % bits_per_block) != 0U) { + blocks_[blocks_.size() - 1] &= + ~((~block_t{0}) << (size_ % bits_per_block)); + } + blocks_.resize(num_blocks(new_size)); + size_ = new_size; + } + + void set(std::string_view s) { + assert(std::all_of(begin(s), end(s), + [](char const c) { return c == '0' || c == '1'; })); + resize(s.size()); + for (auto i = std::size_t{0U}; + i != std::min(static_cast(size_), s.size()); ++i) { + set(i, s[s.size() - i - 1] != '0'); + } + } + + constexpr void set(Key const i, bool const val = true) noexcept { + assert(i < size_); + assert((to_idx(i) / bits_per_block) < blocks_.size()); + auto& block = blocks_[static_cast(to_idx(i)) / bits_per_block]; + auto const bit = to_idx(i) % bits_per_block; + if (val) { + block |= (block_t{1U} << bit); + } else { + block &= (~block_t{0U} ^ (block_t{1U} << bit)); + } + } + +#if __cpp_lib_atomic_ref + constexpr void atomic_set( + Key const i, bool const val = true, + std::memory_order succ = std::memory_order_seq_cst, + std::memory_order fail = std::memory_order_seq_cst) noexcept { + assert(i < size_); + assert((to_idx(i) / bits_per_block) < blocks_.size()); + auto const block = std::atomic_ref{ + blocks_[static_cast(to_idx(i)) / bits_per_block]}; + auto const bit = to_idx(i) % bits_per_block; + + auto const update_block = [&](block_t const b) -> block_t { + if (val) { + return block | (block_t{1U} << bit); + } else { + return block & (~block_t{0U} ^ (block_t{1U} << bit)); + } + }; + + auto expected = block.load(); + while (std::atomic_compare_exchange_weak( + &block, &expected, update_block(expected), succ, fail)) { + } + } +#endif + + void reset() noexcept { blocks_ = {}; } + + bool operator[](Key const i) const noexcept { return test(i); } + + std::size_t count() const noexcept { + if (empty()) { + return 0; + } + auto sum = std::size_t{0U}; + for (auto i = size_type{0U}; i != blocks_.size() - 1; ++i) { + sum += popcount(blocks_[i]); + } + return sum + popcount(sanitized_last_block()); + } + + constexpr bool test(Key const i) const noexcept { + if (i >= size_) { + return false; + } + assert((i / bits_per_block) < blocks_.size()); + auto const block = + blocks_[static_cast(to_idx(i)) / bits_per_block]; + auto const bit = (to_idx(i) % bits_per_block); + return (block & (block_t{1U} << bit)) != 0U; + } + + template + void for_each_set_bit(Fn&& f) const { + if (empty()) { + return; + } + auto const check_block = [&](size_type const i, block_t const block) { + if (block != 0U) { + for (auto bit = size_type{0U}; bit != bits_per_block; ++bit) { + if ((block & (block_t{1U} << bit)) != 0U) { + f(Key{i * bits_per_block + bit}); + } + } + } + }; + for (auto i = size_type{0U}; i != blocks_.size() - 1; ++i) { + check_block(i, blocks_[i]); + } + check_block(blocks_.size() - 1, sanitized_last_block()); + } + + std::optional next_set_bit(size_type const i) const { + if (i >= size()) { + return std::nullopt; + } + + auto const first_block_idx = i / bits_per_block; + auto const first_block = blocks_[first_block_idx]; + if (first_block != 0U) { + auto const first_bit = i % bits_per_block; + auto const n = std::min(size(), bits_per_block); + for (auto bit = first_bit; bit != n; ++bit) { + if ((first_block & (block_t{1U} << bit)) != 0U) { + return Key{first_block_idx * bits_per_block + bit}; + } + } + } + + if (first_block_idx + 1U == blocks_.size()) { + return std::nullopt; + } + + auto const check_block = [&](size_type const block_idx, + block_t const block) -> std::optional { + if (block != 0U) { + for (auto bit = size_type{0U}; bit != bits_per_block; ++bit) { + if ((block & (block_t{1U} << bit)) != 0U) { + return Key{block_idx * bits_per_block + bit}; + } + } + } + return std::nullopt; + }; + + for (auto block_idx = first_block_idx + 1U; block_idx != blocks_.size() - 1; + ++block_idx) { + if (auto const set_bit_idx = check_block(block_idx, blocks_[block_idx]); + set_bit_idx.has_value()) { + return set_bit_idx; + } + } + + if (auto const set_bit_idx = + check_block(blocks_.size() - 1, sanitized_last_block()); + set_bit_idx.has_value()) { + return set_bit_idx; + } + + return std::nullopt; + } + + std::optional get_next(std::atomic_size_t& next) const { + while (true) { + auto expected = next.load(); + auto idx = next_set_bit(Key{static_cast>(expected)}); + if (!idx.has_value()) { + return std::nullopt; + } + if (next.compare_exchange_weak(expected, *idx + 1U)) { + return idx; + } + } + } + + size_type size() const noexcept { return size_; } + bool empty() const noexcept { return size() == 0U; } + + bool any() const noexcept { + if (empty()) { + return false; + } + for (auto i = size_type{0U}; i != blocks_.size() - 1; ++i) { + if (blocks_[i] != 0U) { + return true; + } + } + return sanitized_last_block() != 0U; + } + + bool none() const noexcept { return !any(); } + + block_t sanitized_last_block() const noexcept { + if ((size_ % bits_per_block) != 0) { + return blocks_[blocks_.size() - 1] & + ~((~block_t{0}) << (size_ % bits_per_block)); + } else { + return blocks_[blocks_.size() - 1]; + } + } + + std::string str() const { + auto s = std::string{}; + s.resize(size_); + for (auto i = 0U; i != size_; ++i) { + s[i] = test(size_ - i - 1) ? '1' : '0'; + } + return s; + } + + friend bool operator==(basic_bitvec const& a, + basic_bitvec const& b) noexcept { + if (a.size() != b.size()) { + return false; + } + + if (a.empty() && b.empty()) { + return true; + } + + for (auto i = size_type{0U}; i != a.blocks_.size() - 1; ++i) { + if (a.blocks_[i] != b.blocks_[i]) { + return false; + } + } + return a.sanitized_last_block() == b.sanitized_last_block(); + } + + friend bool operator<(basic_bitvec const& a, basic_bitvec const& b) noexcept { + assert(a.size() == b.size()); + if (a.empty() && b.empty()) { + return false; + } + + auto const a_last = a.sanitized_last_block(); + auto const b_last = b.sanitized_last_block(); + if (a_last < b_last) { + return true; + } else if (b_last < a_last) { + return false; + } + + for (int i = a.blocks_.size() - 2; i != -1; --i) { + if (a.blocks_[i] < b.blocks_[i]) { + return true; + } else if (b.blocks_[i] < a.blocks_[i]) { + return false; + } + } + + return false; + } + friend bool operator!=(basic_bitvec const& a, + basic_bitvec const& b) noexcept { + return !(a == b); + } + + friend bool operator>(basic_bitvec const& a, basic_bitvec const& b) noexcept { + return !(a.empty() && b.empty()) && b < a; + } + + friend bool operator<=(basic_bitvec const& a, + basic_bitvec const& b) noexcept { + return (a.empty() && b.empty()) || !(a > b); + } + + friend bool operator>=(basic_bitvec const& a, + basic_bitvec const& b) noexcept { + return (a.empty() && b.empty()) || !(a < b); + } + + basic_bitvec& operator&=(basic_bitvec const& o) noexcept { + assert(size() == o.size()); + + for (auto i = 0U; i < blocks_.size(); ++i) { + blocks_[i] &= o.blocks_[i]; + } + return *this; + } + + basic_bitvec& operator|=(basic_bitvec const& o) noexcept { + assert(size() == o.size()); + + for (auto i = 0U; i < blocks_.size(); ++i) { + blocks_[i] |= o.blocks_[i]; + } + return *this; + } + + basic_bitvec& operator^=(basic_bitvec const& o) noexcept { + assert(size() == o.size()); + + for (auto i = 0U; i < blocks_.size(); ++i) { + blocks_[i] ^= o.blocks_[i]; + } + return *this; + } + + basic_bitvec operator~() const noexcept { + auto copy = *this; + for (auto& b : copy.blocks_) { + b = ~b; + } + return copy; + } + + friend basic_bitvec operator&(basic_bitvec const& lhs, + basic_bitvec const& rhs) noexcept { + auto copy = lhs; + copy &= rhs; + return copy; + } + + friend basic_bitvec operator|(basic_bitvec const& lhs, + basic_bitvec const& rhs) noexcept { + auto copy = lhs; + copy |= rhs; + return copy; + } + + friend basic_bitvec operator^(basic_bitvec const& lhs, + basic_bitvec const& rhs) noexcept { + auto copy = lhs; + copy ^= rhs; + return copy; + } + + basic_bitvec& operator>>=(std::size_t const shift) noexcept { + if (shift >= size_) { + reset(); + return *this; + } + + if ((size_ % bits_per_block) != 0) { + blocks_[blocks_.size() - 1] = sanitized_last_block(); + } + + if (blocks_.size() == 1U) { + blocks_[0] >>= shift; + return *this; + } else { + if (shift == 0U) { + return *this; + } + + auto const shift_blocks = shift / bits_per_block; + auto const shift_bits = shift % bits_per_block; + auto const border = blocks_.size() - shift_blocks - 1U; + + if (shift_bits == 0U) { + for (auto i = std::size_t{0U}; i <= border; ++i) { + blocks_[i] = blocks_[i + shift_blocks]; + } + } else { + for (auto i = std::size_t{0U}; i < border; ++i) { + blocks_[i] = + (blocks_[i + shift_blocks] >> shift_bits) | + (blocks_[i + shift_blocks + 1] << (bits_per_block - shift_bits)); + } + blocks_[border] = (blocks_[blocks_.size() - 1] >> shift_bits); + } + + for (auto i = border + 1; i != blocks_.size(); ++i) { + blocks_[i] = 0U; + } + + return *this; + } + } + + basic_bitvec& operator<<=(std::size_t const shift) noexcept { + if (shift >= size_) { + reset(); + return *this; + } + + if (blocks_.size() == 1U) { + blocks_[0] <<= shift; + return *this; + } else { + if (shift == 0U) { + return *this; + } + + auto const shift_blocks = shift / bits_per_block; + auto const shift_bits = shift % bits_per_block; + + if (shift_bits == 0U) { + for (auto i = size_type{blocks_.size() - 1}; i >= shift_blocks; --i) { + blocks_[i] = blocks_[i - shift_blocks]; + } + } else { + for (auto i = size_type{blocks_.size() - 1}; i != shift_blocks; --i) { + blocks_[i] = + (blocks_[i - shift_blocks] << shift_bits) | + (blocks_[i - shift_blocks - 1] >> (bits_per_block - shift_bits)); + } + blocks_[shift_blocks] = blocks_[0] << shift_bits; + } + + for (auto i = 0U; i != shift_blocks; ++i) { + blocks_[i] = 0U; + } + + return *this; + } + } + + basic_bitvec operator>>(std::size_t const i) const noexcept { + auto copy = *this; + copy >>= i; + return copy; + } + + basic_bitvec operator<<(std::size_t const i) const noexcept { + auto copy = *this; + copy <<= i; + return copy; + } + + friend std::ostream& operator<<(std::ostream& out, basic_bitvec const& b) { + return out << b.str(); + } + + size_type size_{0U}; + Vec blocks_; +}; + +namespace offset { +using bitvec = basic_bitvec>; +} + +namespace raw { +using bitvec = basic_bitvec>; +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/cstring.h b/parallel/parallel_src/extern/cista/include/cista/containers/cstring.h new file mode 100644 index 00000000..7cca3e59 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/cstring.h @@ -0,0 +1,462 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include "cista/containers/ptr.h" +#include "cista/exception.h" +#include "cista/type_traits.h" + +namespace cista { + +// This class is a generic string container that stores an extra \0 byte post +// the last byte of the valid data. This makes sure the pointer returned by +// data() can be passed as a C-string. +// +// The content stored within this container can contain binary data, that is, +// any number of \0 bytes is permitted within [data(), data() + size()). +template +struct generic_cstring { + using msize_t = std::uint32_t; + using value_type = char; + + static msize_t mstrlen(char const* s) noexcept { + return static_cast(std::strlen(s)); + } + + static constexpr struct owning_t { + } owning{}; + static constexpr struct non_owning_t { + } non_owning{}; + + constexpr generic_cstring() noexcept {} + ~generic_cstring() noexcept { reset(); } + + generic_cstring(std::string_view s, owning_t const) { set_owning(s); } + generic_cstring(std::string_view s, non_owning_t const) { set_non_owning(s); } + generic_cstring(std::string const& s, owning_t const) { set_owning(s); } + generic_cstring(std::string const& s, non_owning_t const) { + set_non_owning(s); + } + generic_cstring(char const* s, owning_t const) { set_owning(s); } + generic_cstring(char const* s, non_owning_t const) { set_non_owning(s); } + + char* begin() noexcept { return data(); } + char* end() noexcept { return data() + size(); } + char const* begin() const noexcept { return data(); } + char const* end() const noexcept { return data() + size(); } + + friend char const* begin(generic_cstring const& s) { return s.begin(); } + friend char* begin(generic_cstring& s) { return s.begin(); } + friend char const* end(generic_cstring const& s) { return s.end(); } + friend char* end(generic_cstring& s) { return s.end(); } + + bool is_short() const noexcept { return s_.remaining_ >= 0; } + + bool is_owning() const { return is_short() || h_.self_allocated_; } + + void reset() noexcept { + if (!is_short() && h_.self_allocated_) { + std::free(data()); + } + s_ = stack{}; + } + + void set_owning(std::string const& s) { + set_owning(s.data(), static_cast(s.size())); + } + + void set_owning(std::string_view s) { + set_owning(s.data(), static_cast(s.size())); + } + + void set_owning(char const* str) { + assert(str != nullptr); + set_owning(str, mstrlen(str)); + } + + static constexpr msize_t short_length_limit = 15U; + + void set_owning(char const* str, msize_t const len) { + assert(str != nullptr || len == 0U); + reset(); + if (len == 0U) { + return; + } + s_.remaining_ = static_cast( + std::max(static_cast(short_length_limit - len), -1)); + if (is_short()) { + std::memcpy(s_.s_, str, len); + } else { + h_ = heap(len, owning); + std::memcpy(data(), str, len); + } + } + + void set_non_owning(std::string const& v) { + set_non_owning(v.data(), static_cast(v.size())); + } + + void set_non_owning(std::string_view v) { + set_non_owning(v.data(), static_cast(v.size())); + } + + void set_non_owning(char const* str) { + set_non_owning(str, str != nullptr ? mstrlen(str) : 0); + } + + void set_non_owning(char const* str, msize_t const len) { + assert(str != nullptr || len == 0U); + reset(); + h_ = heap(str, len, non_owning); + } + + void move_from(generic_cstring&& s) noexcept { + std::memcpy(static_cast(this), &s, sizeof(*this)); + if constexpr (std::is_pointer_v) { + std::memset(static_cast(&s), 0, sizeof(*this)); + } else if (!s.is_short()) { + h_.ptr_ = s.h_.ptr_; + s.s_ = stack{}; + } + } + + void copy_from(generic_cstring const& s) { + reset(); + if (s.is_short()) { + std::memcpy(static_cast(this), &s, sizeof(s)); + } else if (s.h_.self_allocated_) { + set_owning(s.data(), s.size()); + } else { + set_non_owning(s.data(), s.size()); + } + } + + bool empty() const noexcept { return size() == 0U; } + std::string_view view() const noexcept { return {data(), size()}; } + std::string str() const { return {data(), size()}; } + + operator std::string_view() const { return view(); } + + char& operator[](std::size_t const i) noexcept { return data()[i]; } + char const& operator[](std::size_t const i) const noexcept { + return data()[i]; + } + + friend std::ostream& operator<<(std::ostream& out, generic_cstring const& s) { + return out << s.view(); + } + + friend bool operator==(generic_cstring const& a, + generic_cstring const& b) noexcept { + return a.view() == b.view(); + } + + friend bool operator!=(generic_cstring const& a, + generic_cstring const& b) noexcept { + return a.view() != b.view(); + } + + friend bool operator<(generic_cstring const& a, + generic_cstring const& b) noexcept { + return a.view() < b.view(); + } + + friend bool operator>(generic_cstring const& a, + generic_cstring const& b) noexcept { + return a.view() > b.view(); + } + + friend bool operator<=(generic_cstring const& a, + generic_cstring const& b) noexcept { + return a.view() <= b.view(); + } + + friend bool operator>=(generic_cstring const& a, + generic_cstring const& b) noexcept { + return a.view() >= b.view(); + } + + friend bool operator==(generic_cstring const& a, + std::string_view b) noexcept { + return a.view() == b; + } + + friend bool operator!=(generic_cstring const& a, + std::string_view b) noexcept { + return a.view() != b; + } + + friend bool operator<(generic_cstring const& a, std::string_view b) noexcept { + return a.view() < b; + } + + friend bool operator>(generic_cstring const& a, std::string_view b) noexcept { + return a.view() > b; + } + + friend bool operator<=(generic_cstring const& a, + std::string_view b) noexcept { + return a.view() <= b; + } + + friend bool operator>=(generic_cstring const& a, + std::string_view b) noexcept { + return a.view() >= b; + } + + friend bool operator==(std::string_view a, + generic_cstring const& b) noexcept { + return a == b.view(); + } + + friend bool operator!=(std::string_view a, + generic_cstring const& b) noexcept { + return a != b.view(); + } + + friend bool operator<(std::string_view a, generic_cstring const& b) noexcept { + return a < b.view(); + } + + friend bool operator>(std::string_view a, generic_cstring const& b) noexcept { + return a > b.view(); + } + + friend bool operator<=(std::string_view a, + generic_cstring const& b) noexcept { + return a <= b.view(); + } + + friend bool operator>=(std::string_view a, + generic_cstring const& b) noexcept { + return a >= b.view(); + } + + friend bool operator==(generic_cstring const& a, char const* b) noexcept { + return a.view() == std::string_view{b}; + } + + friend bool operator!=(generic_cstring const& a, char const* b) noexcept { + return a.view() != std::string_view{b}; + } + + friend bool operator<(generic_cstring const& a, char const* b) noexcept { + return a.view() < std::string_view{b}; + } + + friend bool operator>(generic_cstring const& a, char const* b) noexcept { + return a.view() > std::string_view{b}; + } + + friend bool operator<=(generic_cstring const& a, char const* b) noexcept { + return a.view() <= std::string_view{b}; + } + + friend bool operator>=(generic_cstring const& a, char const* b) noexcept { + return a.view() >= std::string_view{b}; + } + + friend bool operator==(char const* a, generic_cstring const& b) noexcept { + return std::string_view{a} == b.view(); + } + + friend bool operator!=(char const* a, generic_cstring const& b) noexcept { + return std::string_view{a} != b.view(); + } + + friend bool operator<(char const* a, generic_cstring const& b) noexcept { + return std::string_view{a} < b.view(); + } + + friend bool operator>(char const* a, generic_cstring const& b) noexcept { + return std::string_view{a} > b.view(); + } + + friend bool operator<=(char const* a, generic_cstring const& b) noexcept { + return std::string_view{a} <= b.view(); + } + + friend bool operator>=(char const* a, generic_cstring const& b) noexcept { + return std::string_view{a} >= b.view(); + } + + char const* internal_data() const noexcept { + if constexpr (std::is_pointer_v) { + return is_short() ? s_.s_ : h_.ptr_; + } else { + return is_short() ? s_.s_ : h_.ptr_.get(); + } + } + + char* data() noexcept { return const_cast(internal_data()); } + char const* data() const noexcept { return internal_data(); } + + msize_t size() const noexcept { return is_short() ? s_.size() : h_.size(); } + + struct heap { + Ptr ptr_{nullptr}; + std::uint32_t size_{0}; + bool self_allocated_{false}; + char __fill__[sizeof(uintptr_t) == 8 ? 2 : 6]{0}; + int8_t minus_one_{-1}; // The offset of this field needs to match the + // offset of stack::remaining_ below. + + heap() = default; + heap(msize_t len, owning_t) { + char* mem = static_cast(std::malloc(len + 1)); + if (mem == nullptr) { + throw_exception(std::bad_alloc{}); + } + mem[len] = '\0'; + ptr_ = mem; + size_ = len; + self_allocated_ = true; + } + heap(Ptr ptr, msize_t len, non_owning_t) { + ptr_ = ptr; + size_ = len; + } + + msize_t size() const { return size_; } + }; + + struct stack { + char s_[short_length_limit]{0}; + int8_t remaining_{ + short_length_limit}; // The remaining capacity the inline buffer still + // has. A negative value indicates the buffer is + // not inline. In case the inline buffer is fully + // occupied, this field also serves as a null + // terminator. + + msize_t size() const { + assert(remaining_ >= 0); + return short_length_limit - static_cast(remaining_); + } + }; + + union { + heap h_; + stack s_{}; + }; +}; + +template +struct basic_cstring : public generic_cstring { + using base = generic_cstring; + + using base::base; + using base::operator std::string_view; + + friend std::ostream& operator<<(std::ostream& out, basic_cstring const& s) { + return out << s.view(); + } + + explicit operator std::string() const { return {base::data(), base::size()}; } + + basic_cstring(std::string_view s) : base{s, base::owning} {} + basic_cstring(std::string const& s) : base{s, base::owning} {} + basic_cstring(char const* s) : base{s, base::owning} {} + basic_cstring(char const* s, typename base::msize_t const len) + : base{s, len, base::owning} {} + + basic_cstring(basic_cstring const& o) : base{o.view(), base::owning} {} + basic_cstring(basic_cstring&& o) { base::move_from(std::move(o)); } + + basic_cstring& operator=(basic_cstring const& o) { + base::set_owning(o.data(), o.size()); + return *this; + } + + basic_cstring& operator=(basic_cstring&& o) { + base::move_from(std::move(o)); + return *this; + } + + basic_cstring& operator=(char const* s) { + base::set_owning(s); + return *this; + } + basic_cstring& operator=(std::string const& s) { + base::set_owning(s); + return *this; + } + basic_cstring& operator=(std::string_view s) { + base::set_owning(s); + return *this; + } +}; + +template +struct basic_cstring_view : public generic_cstring { + using base = generic_cstring; + + using base::base; + using base::operator std::string_view; + + friend std::ostream& operator<<(std::ostream& out, + basic_cstring_view const& s) { + return out << s.view(); + } + + basic_cstring_view(std::string_view s) : base{s, base::non_owning} {} + basic_cstring_view(std::string const& s) : base{s, base::non_owning} {} + basic_cstring_view(char const* s) : base{s, base::non_owning} {} + basic_cstring_view(char const* s, typename base::msize_t const len) + : base{s, len, base::non_owning} {} + + basic_cstring_view(basic_cstring_view const& o) { + base::set_non_owning(o.data(), o.size()); + } + basic_cstring_view(basic_cstring_view&& o) { + base::set_non_owning(o.data(), o.size()); + } + basic_cstring_view& operator=(basic_cstring_view const& o) { + base::set_non_owning(o.data(), o.size()); + return *this; + } + basic_cstring_view& operator=(basic_cstring_view&& o) { + base::set_non_owning(o.data(), o.size()); + return *this; + } + + basic_cstring_view& operator=(char const* s) { + base::set_non_owning(s); + return *this; + } + basic_cstring_view& operator=(std::string_view s) { + base::set_non_owning(s); + return *this; + } + basic_cstring_view& operator=(std::string const& s) { + base::set_non_owning(s); + return *this; + } +}; + +template +struct is_string_helper> : std::true_type {}; + +template +struct is_string_helper> : std::true_type {}; + +template +struct is_string_helper> : std::true_type {}; + +namespace raw { +using generic_cstring = generic_cstring>; +using cstring = basic_cstring>; +} // namespace raw + +namespace offset { +using generic_cstring = generic_cstring>; +using cstring = basic_cstring>; +} // namespace offset + +} // namespace cista \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/flat_matrix.h b/parallel/parallel_src/extern/cista/include/cista/containers/flat_matrix.h new file mode 100644 index 00000000..411a0621 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/flat_matrix.h @@ -0,0 +1,142 @@ +#pragma once + +#include + +#include "cista/containers/vector.h" + +namespace cista { + +template +struct base_flat_matrix { + using value_type = typename Vector::value_type; + using size_type = typename Vector::size_type; + + struct row { + row(base_flat_matrix& matrix, size_type const i) : matrix_(matrix), i_(i) {} + + using iterator = typename Vector::iterator; + using const_iterator = typename Vector::const_iterator; + + const_iterator begin() const { + return std::next(matrix_.entries_, matrix_.n_columns_ * i_); + } + const_iterator end() const { + return std::next(matrix_.entries_, matrix_.n_columns_ * (i_ + 1)); + } + iterator begin() { + return std::next(matrix_.entries_, matrix_.n_columns_ * i_); + } + iterator end() { + return std::next(matrix_.entries_, matrix_.n_columns_ * (i_ + 1)); + } + friend const_iterator begin(row const& r) { return r.begin(); } + friend const_iterator end(row const& r) { return r.end(); } + friend iterator begin(row& r) { return r.begin(); } + friend iterator end(row& r) { return r.end(); } + + value_type& operator[](size_type const j) { + assert(j < matrix_.n_columns_); + auto const pos = matrix_.n_columns_ * i_ + j; + return matrix_.entries_[pos]; + } + + base_flat_matrix& matrix_; + size_type i_; + }; + + struct const_row { + const_row(base_flat_matrix const& matrix, size_type const i) + : matrix_(matrix), i_(i) {} + + using iterator = typename Vector::const_iterator; + + iterator begin() const { + return std::next(matrix_.entries_, matrix_.n_columns_ * i_); + } + iterator end() const { + return std::next(matrix_.entries_, matrix_.n_columns_ * (i_ + 1)); + } + friend iterator begin(const_row const& r) { return r.begin(); } + friend iterator end(const_row const& r) { return r.end(); } + + value_type const& operator[](size_type const j) const { + assert(j < matrix_.n_columns_); + auto const pos = matrix_.n_columns_ * i_ + j; + return matrix_.entries_[pos]; + } + + base_flat_matrix const& matrix_; + size_type i_; + }; + + row operator[](size_type i) { + assert(i < n_rows_); + return {*this, i}; + } + const_row operator[](size_type i) const { + assert(i < n_rows_); + return {*this, i}; + } + + value_type& operator()(size_type const i, size_type const j) { + assert(i < n_rows_ && j < n_columns_); + return entries_[n_columns_ * i + j]; + } + + row at(size_type const i) { + verify(i < n_rows_, "matrix::at: index out of range"); + return {*this, i}; + } + + const_row at(size_type const i) const { + verify(i < n_rows_, "matrix::at: index out of range"); + return {*this, i}; + } + + void resize(size_type const n_rows, size_t const n_columns) { + n_rows_ = n_rows; + n_columns_ = n_columns; + entries_.resize(n_rows * n_columns); + } + + void reset(value_type const& t) { + std::fill(begin(entries_), end(entries_), t); + } + + size_type n_rows_{0U}, n_columns_{0U}; + Vector entries_; +}; + +namespace offset { + +template +using flat_matrix = base_flat_matrix>; + +template +inline flat_matrix make_flat_matrix(std::uint32_t const n_rows, + std::uint32_t const n_columns, + T const& init = T{}) { + auto v = vector{}; + v.resize(n_rows * n_columns, init); + return {n_rows, n_columns, std::move(v)}; +} + +} // namespace offset + +namespace raw { + +template +using flat_matrix = base_flat_matrix>; + +template +inline flat_matrix make_flat_matrix(std::uint32_t const n_rows, + std::uint32_t const n_columns, + T const& init = T{}) { + auto v = vector{}; + v.resize(n_rows * n_columns, init); + return {n_rows, n_columns, std::move(v)}; +} + +} // namespace raw + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/fws_multimap.h b/parallel/parallel_src/extern/cista/include/cista/containers/fws_multimap.h new file mode 100644 index 00000000..566e1087 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/fws_multimap.h @@ -0,0 +1,217 @@ +#pragma once + +#include +#include +#include + +#include "cista/containers/vector.h" +#include "cista/strong.h" + +namespace cista { + +template +struct fws_multimap_entry { + using iterator = typename DataVec::const_iterator; + using index_t = typename IndexVec::value_type; + using value_t = typename DataVec::value_type; + + static_assert(!std::is_same_v, bool>, + "bool not supported"); + + fws_multimap_entry(DataVec const& data, IndexVec const& index, + index_t const key) + : data_{data}, index_start_{index[key]}, index_end_{index[key + 1]} {} + + fws_multimap_entry(DataVec const& data, index_t const start_index, + index_t const end_index) + : data_{data}, index_start_{start_index}, index_end_{end_index} {} + + iterator begin() const { return data_.begin() + index_start_; } + iterator end() const { return data_.begin() + index_end_; } + + iterator cbegin() const { return begin(); } + iterator cend() const { return end(); } + + friend iterator begin(fws_multimap_entry const& e) { return e.begin(); } + friend iterator end(fws_multimap_entry const& e) { return e.end(); } + + value_t const& operator[](index_t const index) const { + return data_[data_index(index)]; + } + + index_t data_index(index_t const index) const noexcept { + assert(index_start_ + index < data_.size()); + return index_start_ + index; + } + + std::size_t size() const noexcept { return index_end_ - index_start_; } + bool empty() const noexcept { return size() == 0U; } + + DataVec const& data_; + index_t const index_start_; + index_t const index_end_; +}; + +template +struct fws_multimap_iterator { + using iterator_category = std::random_access_iterator_tag; + using value_type = EntryType; + using difference_type = int; + using pointer = value_type*; + using reference = value_type&; + using index_t = typename MapType::index_t; + + fws_multimap_iterator(MapType const& map, index_t const index) + : map_{map}, index_{index} {} + + value_type operator*() const { return {map_[index_]}; } + + fws_multimap_iterator& operator+=(int const n) { + index_ += n; + return *this; + } + + fws_multimap_iterator& operator-=(int const n) { + index_ -= n; + return *this; + } + + fws_multimap_iterator& operator++() { + ++index_; + return *this; + } + + fws_multimap_iterator& operator--() { + --index_; + return *this; + } + + fws_multimap_iterator operator+(int const n) const { + return {map_, index_ + static_cast(n)}; + } + + fws_multimap_iterator operator-(int const n) const { + return {map_, index_ + static_cast(n)}; + } + + int operator-(fws_multimap_iterator const& o) const { + return index_ - o.index_; + } + + value_type& operator[](int const n) const { return {map_, index_ + n}; } + + bool operator<(fws_multimap_iterator const& o) const { + return index_ < o.index_; + } + + bool operator>(fws_multimap_iterator const& o) const { + return index_ > o.index_; + } + + bool operator<=(fws_multimap_iterator const& o) const { + return index_ <= o.index_; + } + + bool operator>=(fws_multimap_iterator const& o) const { + return index_ >= o.index_; + } + + bool operator==(fws_multimap_iterator const& o) const { + return &map_ == &o.map_ && index_ == o.index_; + } + + bool operator!=(fws_multimap_iterator const& o) const { + return !(*this == o); + } + +protected: + MapType const& map_; + index_t index_; +}; + +template +struct fws_multimap { + using value_t = typename DataVec::value_type; + using index_t = typename IndexVec::value_type; + using entry_t = fws_multimap_entry; + using iterator = fws_multimap_iterator; + + template + struct is_unsigned_strong { + static constexpr auto const value = + std::is_unsigned_v; + }; + + static_assert( + std::disjunction_v< + std::is_unsigned, + std::conjunction, is_unsigned_strong>>, + "index has to be unsigned"); + + void push_back(value_t const& val) { + assert(!complete_); + data_.push_back(val); + } + + template + void emplace_back(Args&&... args) { + data_.emplace_back(std::forward(args)...); + } + + index_t current_key() const { return static_cast(index_.size()); } + + void finish_key() { + assert(!complete_); + index_.push_back(current_start_); + current_start_ = static_cast(data_.size()); + } + + void finish_map() { + assert(!complete_); + index_.push_back(static_cast(data_.size())); + complete_ = true; + } + + void reserve_index(index_t size) { + index_.reserve(static_cast(size) + 1); + } + + entry_t operator[](index_t const index) const { + assert(index < index_.size() - 1); + return {data_, index_, index}; + } + + iterator begin() const { return {*this, 0}; } + iterator end() const { return {*this, index_.size() - 1}; } + + iterator cbegin() const { return begin(); } + iterator cend() const { return end(); } + + friend iterator begin(fws_multimap const& e) { return e.begin(); } + friend iterator end(fws_multimap const& e) { return e.end(); } + + std::size_t index_size() const { return index_.size(); } + std::size_t data_size() const { return data_.size(); } + bool finished() const { return complete_; } + + DataVec data_; + IndexVec index_; + index_t current_start_{0}; + bool complete_{false}; +}; + +namespace offset { + +template +using fws_multimap = fws_multimap, vector>; + +} // namespace offset + +namespace raw { + +template +using fws_multimap = fws_multimap, vector>; + +} // namespace raw + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/hash_map.h b/parallel/parallel_src/extern/cista/include/cista/containers/hash_map.h new file mode 100644 index 00000000..da4ff51c --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/hash_map.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "cista/containers/hash_storage.h" +#include "cista/containers/pair.h" +#include "cista/equal_to.h" +#include "cista/hashing.h" + +namespace cista { + +struct get_first { + template + auto&& operator()(T&& t) noexcept { + return t.first; + } +}; + +struct get_second { + template + auto&& operator()(T&& t) noexcept { + return t.second; + } +}; + +namespace raw { +template , + typename Eq = equal_to> +using hash_map = + hash_storage, ptr, get_first, get_second, Hash, Eq>; +} // namespace raw + +namespace offset { +template , + typename Eq = equal_to> +using hash_map = + hash_storage, ptr, get_first, get_second, Hash, Eq>; +} // namespace offset + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/hash_set.h b/parallel/parallel_src/extern/cista/include/cista/containers/hash_set.h new file mode 100644 index 00000000..0db70def --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/hash_set.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "cista/containers/hash_storage.h" +#include "cista/equal_to.h" +#include "cista/hashing.h" + +namespace cista { + +struct identity { + template + decltype(auto) operator()(T&& t) const noexcept { + return std::forward(t); + } +}; + +namespace raw { +template , typename Eq = equal_to> +using hash_set = hash_storage; +} // namespace raw + +namespace offset { +template , typename Eq = equal_to> +using hash_set = hash_storage; +} // namespace offset + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/hash_storage.h b/parallel/parallel_src/extern/cista/include/cista/containers/hash_storage.h new file mode 100644 index 00000000..753c6750 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/hash_storage.h @@ -0,0 +1,677 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "cista/aligned_alloc.h" +#include "cista/bit_counting.h" +#include "cista/containers/ptr.h" +#include "cista/decay.h" +#include "cista/exception.h" +#include "cista/hash.h" + +namespace cista { + +// This class is a generic hash-based container. +// It can be used e.g. as hash set or hash map. +// - hash map: `T` = `std::pair`, GetKey = `return entry.first;` +// - hash set: `T` = `T`, GetKey = `return entry;` (identity) +// +// It is based on the idea of swiss tables: +// https://abseil.io/blog/20180927-swisstables +// +// Original implementation: +// https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h +// +// Missing features of this implemenation compared to the original: +// - SSE to speedup the lookup in the ctrl data structure +// - sanitizer support (Sanitizer[Un]PoisonMemoryRegion) +// - overloads (conveniance as well to reduce copying) in the interface +// - allocator support +template typename Ptr, typename GetKey, + typename GetValue, typename Hash, typename Eq> +struct hash_storage { + using entry_t = T; + using difference_type = ptrdiff_t; + using size_type = hash_t; + using key_type = + decay_t().operator()(std::declval()))>; + using mapped_type = + decay_t().operator()(std::declval()))>; + using group_t = std::uint64_t; + using h2_t = std::uint8_t; + static constexpr size_type const WIDTH = 8U; + static constexpr std::size_t const ALIGNMENT = alignof(T); + + template + hash_t compute_hash(Key const& k) { + if constexpr (std::is_same_v, key_type>) { + return static_cast(Hash{}(k)); + } else { + return static_cast(Hash::template create()(k)); + } + } + + enum ctrl_t : int8_t { + EMPTY = -128, // 10000000 + DELETED = -2, // 11111110 + END = -1 // 11111111 + }; + + struct find_info { + size_type offset_{}, probe_length_{}; + }; + + struct probe_seq { + constexpr probe_seq(size_type const hash, size_type const mask) noexcept + : mask_{mask}, offset_{hash & mask_} {} + size_type offset(size_type const i) const noexcept { + return (offset_ + i) & mask_; + } + void next() noexcept { + index_ += WIDTH; + offset_ += index_; + offset_ &= mask_; + } + size_type mask_, offset_, index_{0U}; + }; + + struct bit_mask { + static constexpr auto const SHIFT = 3U; + + constexpr explicit bit_mask(group_t const mask) noexcept : mask_{mask} {} + + bit_mask& operator++() noexcept { + mask_ &= (mask_ - 1U); + return *this; + } + + size_type operator*() const noexcept { return trailing_zeros(); } + + explicit operator bool() const noexcept { return mask_ != 0U; } + + bit_mask begin() const noexcept { return *this; } + bit_mask end() const noexcept { return bit_mask{0}; } + + size_type trailing_zeros() const noexcept { + return ::cista::trailing_zeros(mask_) >> SHIFT; + } + + size_type leading_zeros() const noexcept { + constexpr int total_significant_bits = 8 << SHIFT; + constexpr int extra_bits = sizeof(group_t) * 8 - total_significant_bits; + return ::cista::leading_zeros(mask_ << extra_bits) >> SHIFT; + } + + friend bool operator!=(bit_mask const& a, bit_mask const& b) noexcept { + return a.mask_ != b.mask_; + } + + group_t mask_; + }; + + struct group { + static constexpr auto MSBS = 0x8080808080808080ULL; + static constexpr auto LSBS = 0x0101010101010101ULL; + static constexpr auto GAPS = 0x00FEFEFEFEFEFEFEULL; + + explicit group(ctrl_t const* pos) noexcept { + std::memcpy(&ctrl_, pos, WIDTH); +#if defined(CISTA_BIG_ENDIAN) + ctrl_ = endian_swap(ctrl_); +#endif + } + bit_mask match(h2_t const hash) const noexcept { + auto const x = ctrl_ ^ (LSBS * hash); + return bit_mask{(x - LSBS) & ~x & MSBS}; + } + bit_mask match_empty() const noexcept { + return bit_mask{(ctrl_ & (~ctrl_ << 6U)) & MSBS}; + } + bit_mask match_empty_or_deleted() const noexcept { + return bit_mask{(ctrl_ & (~ctrl_ << 7U)) & MSBS}; + } + std::size_t count_leading_empty_or_deleted() const noexcept { + return (trailing_zeros(((~ctrl_ & (ctrl_ >> 7U)) | GAPS) + 1U) + 7U) >> + 3U; + } + group_t ctrl_; + }; + + struct iterator { + using iterator_category = std::forward_iterator_tag; + using value_type = hash_storage::entry_t; + using reference = hash_storage::entry_t&; + using pointer = hash_storage::entry_t*; + using difference_type = ptrdiff_t; + + constexpr iterator() noexcept = default; + + reference operator*() const noexcept { return *entry_; } + pointer operator->() const noexcept { return entry_; } + iterator& operator++() noexcept { + ++ctrl_; + ++entry_; + skip_empty_or_deleted(); + return *this; + } + iterator operator++(int) noexcept { + auto tmp = *this; + ++*this; + return tmp; + } + + friend bool operator==(iterator const& a, iterator const& b) noexcept { + return a.ctrl_ == b.ctrl_; + } + friend bool operator!=(iterator const& a, iterator const& b) noexcept { + return !(a == b); + } + + constexpr iterator(ctrl_t* const ctrl) noexcept : ctrl_(ctrl) {} + constexpr iterator(ctrl_t* const ctrl, T* const entry) noexcept + : ctrl_(ctrl), entry_(entry) {} + + void skip_empty_or_deleted() noexcept { + while (is_empty_or_deleted(*ctrl_)) { + auto const shift = group{ctrl_}.count_leading_empty_or_deleted(); + ctrl_ += shift; + entry_ += shift; + } + } + + ctrl_t* ctrl_{nullptr}; + T* entry_{nullptr}; + }; + + struct const_iterator { + using iterator_category = std::forward_iterator_tag; + using value_type = hash_storage::entry_t; + using reference = hash_storage::entry_t const&; + using pointer = hash_storage::entry_t const*; + using difference_type = ptrdiff_t; + + constexpr const_iterator() noexcept = default; + const_iterator(iterator i) noexcept : inner_(std::move(i)) {} + + reference operator*() const noexcept { return *inner_; } + pointer operator->() const noexcept { return inner_.operator->(); } + + const_iterator& operator++() noexcept { + ++inner_; + return *this; + } + const_iterator operator++(int) noexcept { return inner_++; } + + friend bool operator==(const const_iterator& a, + const const_iterator& b) noexcept { + return a.inner_ == b.inner_; + } + friend bool operator!=(const const_iterator& a, + const const_iterator& b) noexcept { + return !(a == b); + } + + const_iterator(ctrl_t const* ctrl, T const* entry) noexcept + : inner_(const_cast(ctrl), const_cast(entry)) {} + + iterator inner_; + }; + + static ctrl_t* empty_group() noexcept { + alignas(16) static constexpr ctrl_t empty_group[] = { + END, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY}; + return const_cast(empty_group); + } + + static constexpr bool is_empty(ctrl_t const c) noexcept { return c == EMPTY; } + static constexpr bool is_full(ctrl_t const c) noexcept { return c >= 0; } + static constexpr bool is_deleted(ctrl_t const c) noexcept { + return c == DELETED; + } + static constexpr bool is_empty_or_deleted(ctrl_t const c) noexcept { + return c < END; + } + + static constexpr std::size_t normalize_capacity(size_type const n) noexcept { + return n == 0U ? 1U : ~size_type{} >> leading_zeros(n); + } + + static constexpr size_type h1(size_type const hash) noexcept { + return (hash >> 7U) ^ 16777619U; + } + + static constexpr h2_t h2(size_type const hash) noexcept { + return hash & 0x7FU; + } + + static constexpr size_type capacity_to_growth( + size_type const capacity) noexcept { + return (capacity == 7U) ? 6U : capacity - (capacity / 8U); + } + + constexpr hash_storage() = default; + + hash_storage(std::initializer_list init) { + insert(init.begin(), init.end()); + } + + hash_storage(hash_storage&& other) noexcept + : entries_{other.entries_}, + ctrl_{other.ctrl_}, + size_{other.size_}, + capacity_{other.capacity_}, + growth_left_{other.growth_left_}, + self_allocated_{other.self_allocated_} { + other.reset(); + } + + hash_storage(hash_storage const& other) { + if (other.size() != 0U) { + for (const auto& v : other) { + emplace(v); + } + } + } + + hash_storage& operator=(hash_storage&& other) noexcept { + if (&other == this) { + return *this; + } + entries_ = other.entries_; + ctrl_ = other.ctrl_; + size_ = other.size_; + capacity_ = other.capacity_; + growth_left_ = other.growth_left_; + self_allocated_ = other.self_allocated_; + other.reset(); + return *this; + } + + hash_storage& operator=(hash_storage const& other) { + if (&other == this) { + return *this; + } + clear(); + if (other.size() == 0U) { + return *this; + } + for (const auto& v : other) { + emplace(v); + } + return *this; + } + + ~hash_storage() { clear(); } + + void set_empty_key(key_type const&) noexcept {} + void set_deleted_key(key_type const&) noexcept {} + + // --- operator[] + template + mapped_type& bracket_operator_impl(Key&& key) { + auto const res = find_or_prepare_insert(std::forward(key)); + if (res.second) { + new (entries_ + res.first) T{static_cast(key), mapped_type{}}; + } + return GetValue{}(entries_[res.first]); + } + + template + mapped_type& operator[](Key&& key) { + return bracket_operator_impl(std::forward(key)); + } + + mapped_type& operator[](key_type const& key) { + return bracket_operator_impl(key); + } + + // --- get() + template + std::optional get_impl(Key&& key) { + if (auto it = find(std::forward(key)); it != end()) { + return GetValue{}(*it); + } else { + return std::nullopt; + } + } + + template + std::optional get(Key&& key) const { + return const_cast(this)->get_impl(std::forward(key)); + } + + // --- at() + template + mapped_type& at_impl(Key&& key) { + auto const it = find(std::forward(key)); + if (it == end()) { + throw_exception(std::out_of_range{"hash_storage::at() key not found"}); + } + return GetValue{}(*it); + } + + mapped_type& at(key_type const& key) { return at_impl(key); } + + mapped_type const& at(key_type const& key) const { + return const_cast(this)->at(key); + } + + template + mapped_type& at(Key&& key) { + return at_impl(std::forward(key)); + } + + template + mapped_type const& at(Key&& key) const { + return const_cast(this)->at(std::forward(key)); + } + + // --- find() + template + iterator find_impl(Key&& key) { + auto const hash = compute_hash(key); + for (auto seq = probe_seq{h1(hash), capacity_}; true; seq.next()) { + group g{ctrl_ + seq.offset_}; + for (auto const i : g.match(h2(hash))) { + if (Eq{}(GetKey()(entries_[seq.offset(i)]), key)) { + return iterator_at(seq.offset(i)); + } + } + if (g.match_empty()) { + return end(); + } + } + } + + template + const_iterator find(Key&& key) const { + return const_cast(this)->find_impl(std::forward(key)); + } + + template + iterator find(Key&& key) { + return find_impl(std::forward(key)); + } + + const_iterator find(key_type const& key) const noexcept { + return const_cast(this)->find_impl(key); + } + + iterator find(key_type const& key) noexcept { return find_impl(key); } + + template + void insert(InputIt first, InputIt last) { + for (; first != last; ++first) { + emplace(*first); + } + } + + // --- erase() + template + std::size_t erase_impl(Key&& key) { + auto it = find(std::forward(key)); + if (it == end()) { + return 0U; + } + erase(it); + return 1U; + } + + std::size_t erase(key_type const& k) { return erase_impl(k); } + + template + std::size_t erase(Key&& key) { + return erase_impl(std::forward(key)); + } + + void erase(iterator const it) noexcept { + it.entry_->~T(); + erase_meta_only(it); + } + + std::pair insert(T const& entry) { return emplace(entry); } + + template + std::pair emplace(Args&&... args) { + auto entry = T{std::forward(args)...}; + auto res = find_or_prepare_insert(GetKey()(entry)); + if (res.second) { + new (entries_ + res.first) T{std::move(entry)}; + } + return {iterator_at(res.first), res.second}; + } + + iterator begin() noexcept { + auto it = iterator_at(0U); + if (ctrl_ != nullptr) { + it.skip_empty_or_deleted(); + } + return it; + } + iterator end() noexcept { return {ctrl_ + capacity_}; } + + const_iterator begin() const noexcept { + return const_cast(this)->begin(); + } + const_iterator end() const noexcept { + return const_cast(this)->end(); + } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + friend iterator begin(hash_storage& h) noexcept { return h.begin(); } + friend const_iterator begin(hash_storage const& h) noexcept { + return h.begin(); + } + friend const_iterator cbegin(hash_storage const& h) noexcept { + return h.begin(); + } + friend iterator end(hash_storage& h) noexcept { return h.end(); } + friend const_iterator end(hash_storage const& h) noexcept { return h.end(); } + friend const_iterator cend(hash_storage const& h) noexcept { return h.end(); } + + bool empty() const noexcept { return size() == 0U; } + size_type size() const noexcept { return size_; } + size_type capacity() const noexcept { return capacity_; } + size_type max_size() const noexcept { + return std::numeric_limits::max(); + } + + bool is_free(int index) const noexcept { + auto const index_before = (index - WIDTH) & capacity_; + + auto const empty_after = group{ctrl_ + index}.match_empty(); + auto const empty_before = group{ctrl_ + index_before}.match_empty(); + + return empty_before && empty_after && + (empty_after.trailing_zeros() + empty_before.leading_zeros()) < + WIDTH; + } + + bool was_never_full(std::size_t const index) const noexcept { + auto const index_before = (index - WIDTH) & capacity_; + + auto const empty_after = group{ctrl_ + index}.match_empty(); + auto const empty_before = group{ctrl_ + index_before}.match_empty(); + + return empty_before && empty_after && + (empty_after.trailing_zeros() + empty_before.leading_zeros()) < + WIDTH; + } + + void erase_meta_only(const_iterator it) noexcept { + --size_; + auto const index = static_cast(it.inner_.ctrl_ - ctrl_); + auto const wnf = was_never_full(index); + set_ctrl(index, static_cast(wnf ? EMPTY : DELETED)); + growth_left_ += wnf; + } + + void clear() { + if (capacity_ == 0U) { + return; + } + + for (size_type i = 0U; i != capacity_; ++i) { + if (is_full(ctrl_[i])) { + entries_[i].~T(); + } + } + + if (self_allocated_) { + CISTA_ALIGNED_FREE(ALIGNMENT, entries_); + } + + partial_reset(); + } + + template + std::pair find_or_prepare_insert(Key&& key) { + auto const hash = compute_hash(key); + for (auto seq = probe_seq{h1(hash), capacity_}; true; seq.next()) { + group g{ctrl_ + seq.offset_}; + for (auto const i : g.match(h2(hash))) { + if (Eq{}(GetKey()(entries_[seq.offset(i)]), key)) { + return {seq.offset(i), false}; + } + } + if (g.match_empty()) { + break; + } + } + return {prepare_insert(hash), true}; + } + + find_info find_first_non_full(size_type const hash) const noexcept { + for (auto seq = probe_seq{h1(hash), capacity_}; true; seq.next()) { + auto const mask = group{ctrl_ + seq.offset_}.match_empty_or_deleted(); + if (mask) { + return {seq.offset(*mask), seq.index_}; + } + } + } + + size_type prepare_insert(size_type const hash) { + auto target = find_first_non_full(hash); + if (growth_left_ == 0U && !is_deleted(ctrl_[target.offset_])) { + rehash_and_grow_if_necessary(); + target = find_first_non_full(hash); + } + ++size_; + growth_left_ -= (is_empty(ctrl_[target.offset_]) ? 1U : 0U); + set_ctrl(target.offset_, h2(hash)); + return target.offset_; + } + + void set_ctrl(size_type const i, h2_t const c) noexcept { + ctrl_[i] = static_cast(c); + ctrl_[((i - WIDTH) & capacity_) + 1U + ((WIDTH - 1U) & capacity_)] = + static_cast(c); + } + + void rehash_and_grow_if_necessary() { + resize(capacity_ == 0U ? 1U : capacity_ * 2U + 1U); + } + + void reset_growth_left() noexcept { + growth_left_ = capacity_to_growth(capacity_) - size_; + } + + void reset_ctrl() noexcept { + std::memset(ctrl_, EMPTY, static_cast(capacity_ + WIDTH + 1U)); + ctrl_[capacity_] = END; + } + + void initialize_entries() { + self_allocated_ = true; + auto const size = static_cast( + capacity_ * sizeof(T) + (capacity_ + 1U + WIDTH) * sizeof(ctrl_t)); + entries_ = reinterpret_cast( + CISTA_ALIGNED_ALLOC(ALIGNMENT, static_cast(size))); + if (entries_ == nullptr) { + throw_exception(std::bad_alloc{}); + } +#if defined(CISTA_ZERO_OUT) + std::memset(entries_, 0, size); +#endif + ctrl_ = reinterpret_cast( + reinterpret_cast(ptr_cast(entries_)) + + capacity_ * sizeof(T)); + reset_ctrl(); + reset_growth_left(); + } + + void resize(size_type const new_capacity) { + auto const old_ctrl = ctrl_; + auto const old_entries = entries_; + auto const old_capacity = capacity_; + auto const old_self_allocated = self_allocated_; + + capacity_ = new_capacity; + initialize_entries(); + + for (size_type i = 0U; i != old_capacity; ++i) { + if (is_full(old_ctrl[i])) { + auto const hash = compute_hash(GetKey()(old_entries[i])); + auto const target = find_first_non_full(hash); + auto const new_index = target.offset_; + set_ctrl(new_index, h2(hash)); + new (entries_ + new_index) T{std::move(old_entries[i])}; + old_entries[i].~T(); + } + } + + if (old_capacity != 0U && old_self_allocated) { + CISTA_ALIGNED_FREE(ALIGNMENT, old_entries); + } + } + + void partial_reset() noexcept { + entries_ = nullptr; + ctrl_ = empty_group(); + size_ = 0U; + capacity_ = 0U; + growth_left_ = 0U; + } + + void reset() noexcept { + partial_reset(); + self_allocated_ = false; + } + + void rehash() { resize(capacity_); } + + iterator iterator_at(size_type const i) noexcept { + return {ctrl_ + i, entries_ + i}; + } + const_iterator iterator_at(size_type const i) const noexcept { + return {ctrl_ + i, entries_ + i}; + } + + bool operator==(hash_storage const& b) const noexcept { + if (size() != b.size()) { + return false; + } + for (auto const& el : *this) { + auto const it = b.find(GetKey()(el)); + if (it == b.end() || GetValue()(el) != GetValue()(*it)) { + return false; + } + } + return true; + } + + Ptr entries_{nullptr}; + Ptr ctrl_{empty_group()}; + size_type size_{0U}, capacity_{0U}, growth_left_{0U}; + bool self_allocated_{false}; +}; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/mmap_vec.h b/parallel/parallel_src/extern/cista/include/cista/containers/mmap_vec.h new file mode 100644 index 00000000..6e206201 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/mmap_vec.h @@ -0,0 +1,183 @@ +#pragma once + +#include + +#include "cista/mmap.h" +#include "cista/strong.h" + +namespace cista { + +template +struct basic_mmap_vec { + using size_type = base_t; + using difference_type = std::ptrdiff_t; + using access_type = Key; + using reference = T&; + using const_reference = T const&; + using pointer = T*; + using const_pointer = T*; + using value_type = T; + using iterator = T*; + using const_iterator = T const*; + + static_assert(std::is_trivially_copyable_v); + + explicit basic_mmap_vec(cista::mmap mmap) + : mmap_{std::move(mmap)}, + used_size_{static_cast(mmap_.size() / sizeof(T))} {} + + void push_back(T const& t) { + ++used_size_; + mmap_.resize(sizeof(T) * used_size_); + (*this)[Key{used_size_ - 1U}] = t; + } + + template + T& emplace_back(Args&&... el) { + reserve(used_size_ + 1U); + new (data() + used_size_) T{std::forward(el)...}; + T* ptr = data() + used_size_; + ++used_size_; + return *ptr; + } + + size_type size() const { return used_size_; } + + T const* data() const noexcept { return begin(); } + T* data() noexcept { return begin(); } + T const* begin() const noexcept { + return reinterpret_cast(mmap_.data()); + } + T const* end() const noexcept { return begin() + used_size_; } // NOLINT + T const* cbegin() const noexcept { return begin(); } + T const* cend() const noexcept { return begin() + used_size_; } // NOLINT + T* begin() noexcept { return reinterpret_cast(mmap_.data()); } + T* end() noexcept { return begin() + used_size_; } // NOLINT + + friend T const* begin(basic_mmap_vec const& a) noexcept { return a.begin(); } + friend T const* end(basic_mmap_vec const& a) noexcept { return a.end(); } + + friend T* begin(basic_mmap_vec& a) noexcept { return a.begin(); } + friend T* end(basic_mmap_vec& a) noexcept { return a.end(); } + + bool empty() const noexcept { return size() == 0U; } + + T const& operator[](access_type const index) const noexcept { + assert(index < used_size_); + return begin()[to_idx(index)]; + } + + T& operator[](access_type const index) noexcept { + assert(used_size_); + return begin()[to_idx(index)]; + } + + void reserve(size_type const size) { mmap_.resize(size * sizeof(T)); } + + void resize(size_type const size) { + mmap_.resize(size * sizeof(T)); + for (auto i = used_size_; i < size; ++i) { + new (data() + i) T{}; + } + used_size_ = size; + } + + template + void set(It begin_it, It end_it) { + using diff_t = + std::common_type_t::difference_type, + size_type>; + auto const range_size = std::distance(begin_it, end_it); + verify(range_size >= 0 && + static_cast(range_size) <= + static_cast(std::numeric_limits::max()), + "cista::vector::set: invalid range"); + + reserve(static_cast(range_size)); + + auto copy_source = begin_it; + auto copy_target = data(); + for (; copy_source != end_it; ++copy_source, ++copy_target) { + new (copy_target) T{std::forward(*copy_source)}; + } + + used_size_ = static_cast(range_size); + } + + template + T* insert(T* it, Arg&& el) { + auto const old_offset = std::distance(begin(), it); + auto const old_size = used_size_; + + reserve(used_size_ + 1); + new (data() + used_size_) T{std::forward(el)}; + ++used_size_; + + return std::rotate(begin() + old_offset, begin() + old_size, end()); + } + + template + T* insert(T* pos, InputIt first, InputIt last, std::input_iterator_tag) { + auto const old_offset = std::distance(begin(), pos); + auto const old_size = used_size_; + + for (; !(first == last); ++first) { + reserve(used_size_ + 1); + new (data() + used_size_) T{std::forward(*first)}; + ++used_size_; + } + + return std::rotate(begin() + old_offset, begin() + old_size, end()); + } + + template + T* insert(T* pos, FwdIt first, FwdIt last, std::forward_iterator_tag) { + if (empty()) { + set(first, last); + return begin(); + } + + auto const pos_idx = pos - begin(); + auto const new_count = static_cast(std::distance(first, last)); + reserve(used_size_ + new_count); + pos = begin() + pos_idx; + + for (auto src_last = end() - 1, dest_last = end() + new_count - 1; + !(src_last == pos - 1); --src_last, --dest_last) { + if (dest_last >= end()) { + new (dest_last) T(std::move(*src_last)); + } else { + *dest_last = std::move(*src_last); + } + } + + for (auto insert_ptr = pos; !(first == last); ++first, ++insert_ptr) { + if (insert_ptr >= end()) { + new (insert_ptr) T(std::forward(*first)); + } else { + *insert_ptr = std::forward(*first); + } + } + + used_size_ += new_count; + + return pos; + } + + template + T* insert(T* pos, It first, It last) { + return insert(pos, first, last, + typename std::iterator_traits::iterator_category()); + } + + cista::mmap mmap_; + size_type used_size_{0U}; +}; + +template +using mmap_vec = basic_mmap_vec; + +template +using mmap_vec_map = basic_mmap_vec; + +} // namespace cista \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/mutable_fws_multimap.h b/parallel/parallel_src/extern/cista/include/cista/containers/mutable_fws_multimap.h new file mode 100644 index 00000000..1713ce05 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/mutable_fws_multimap.h @@ -0,0 +1,658 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cista/bit_counting.h" +#include "cista/containers/array.h" +#include "cista/containers/vector.h" +#include "cista/exception.h" +#include "cista/next_power_of_2.h" +#include "cista/verify.h" + +namespace cista { + +template typename Vec, + std::size_t Log2MaxEntriesPerBucket = 20U> +struct dynamic_fws_multimap_base { + using value_type = T; + using size_type = base_t; + using access_t = SizeType; + using data_vec_t = Vec; + static constexpr auto const MAX_ENTRIES_PER_BUCKET = + static_cast(1ULL << Log2MaxEntriesPerBucket); + + struct index_type { + size_type begin_{}; + size_type size_{}; + size_type capacity_{}; + }; + using index_vec_t = Vec; + + template + struct bucket { + friend dynamic_fws_multimap_base; + + using value_type = T; + using iterator = typename data_vec_t::iterator; + using const_iterator = typename data_vec_t::const_iterator; + + template > + // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) + bucket(bucket const& b) : multimap_{b.multimap_}, index_{b.index_} {} + + size_type index() const noexcept { return index_; } + size_t size() const noexcept { return get_index().size_; } + size_type capacity() const noexcept { return get_index().capacity_; } + bool empty() const noexcept { return size() == 0; } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wclass-conversion" +#endif + template > + operator bucket() { + return bucket{multimap_, index_}; + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + iterator begin() { return mutable_mm().data_.begin() + get_index().begin_; } + + const_iterator begin() const { + return multimap_.data_.begin() + get_index().begin_; + } + + iterator end() { + auto const& index = get_index(); + return std::next(mutable_mm().data_.begin(), index.begin_ + index.size_); + } + + const_iterator end() const { + auto const& index = get_index(); + return std::next(multimap_.data_.begin(), index.begin_ + index.size_); + } + + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + friend iterator begin(bucket& b) { return b.begin(); } + friend const_iterator begin(bucket const& b) { return b.begin(); } + friend iterator end(bucket& b) { return b.end(); } + friend const_iterator end(bucket const& b) { return b.end(); } + + value_type& operator[](size_type index) { + return mutable_mm().data_[data_index(index)]; + } + + value_type const& operator[](size_type index) const { + return multimap_.data_[data_index(index)]; + } + + value_type& at(size_type const index) { + return mutable_mm().data_[get_and_check_data_index(index)]; + } + + value_type const& at(size_type const index) const { + return multimap_.data_[get_and_check_data_index(index)]; + } + + value_type& front() { return (*this)[0U]; } + value_type const& front() const { return (*this)[0U]; } + + value_type& back() { + assert(!empty()); + return (*this)[static_cast(size() - 1U)]; + } + + value_type const& back() const { + assert(!empty()); + return (*this)[static_cast(size() - 1U)]; + } + + size_type data_index(size_type const index) const { + assert(index < get_index().size_); + return get_index().begin_ + index; + } + + size_type bucket_index(const_iterator it) const { + if (it < begin() || it >= end()) { + throw_exception(std::out_of_range{ + "dynamic_fws_multimap::bucket::bucket_index() out of range"}); + } + return std::distance(begin(), it); + } + + template > + size_type push_back(value_type const& val) { + return mutable_mm().push_back_entry(index_, val); + } + + template , + typename... Args> + size_type emplace_back(Args&&... args) { + return mutable_mm().emplace_back_entry(index_, + std::forward(args)...); + } + + template > + iterator insert(iterator it, value_type const& val) { + auto insert_it = prepare_insert(it); + *insert_it = val; + return insert_it; + } + + template > + iterator insert(iterator it, value_type&& val) { + auto insert_it = prepare_insert(it); + *insert_it = std::move(val); + return insert_it; + } + + template > + void reserve(size_type const new_size) { + if (new_size > capacity()) { + mutable_mm().grow_bucket(index_, get_index(), new_size); + } + } + + template > + void resize(size_type const new_size, + value_type const init = value_type{}) { + auto const old_size = size(); + reserve(new_size); + auto& index = get_index(); + auto& data = mutable_mm().data_; + if (new_size < old_size) { + for (auto i = new_size; i < old_size; ++i) { + data[index.begin_ + i].~T(); + } + mutable_mm().element_count_ -= old_size - new_size; + } else if (new_size > old_size) { + for (auto i = old_size; i < new_size; ++i) { + data[static_cast(index.begin_ + i)] = init; + } + mutable_mm().element_count_ += new_size - old_size; + } + index.size_ = new_size; + } + + template > + void pop_back() { + if (!empty()) { + resize(static_cast(size() - 1U)); + } + } + + template > + void clear() { + auto& index = get_index(); + auto& data = mutable_mm().data_; + for (auto i = index.begin_; i < index.begin_ + index.size_; ++i) { + data[i].~T(); + } + mutable_mm().element_count_ -= index.size_; + index.size_ = 0U; + } + + template > + iterator erase(iterator pos) { + auto last = std::prev(end()); + while (pos < last) { + std::swap(*pos, *std::next(pos)); + pos = std::next(pos); + } + (*pos).~T(); + --get_index().size_; + --mutable_mm().element_count_; + return end(); + } + + template > + iterator erase(iterator first, iterator last) { + if (first != last) { + auto const new_end = std::move(last, end(), first); + for (auto it = new_end; it != end(); it = std::next(it)) { + (*it).~T(); + } + auto const count = std::distance(new_end, end()); + get_index().size_ -= count; + mutable_mm().element_count_ -= count; + } + return end(); + } + + protected: + bucket(dynamic_fws_multimap_base const& multimap, size_type const index) + : multimap_(multimap), index_(index) {} + + index_type& get_index() { return mutable_mm().index_[index_]; } + index_type const& get_index() const { return multimap_.index_[index_]; } + + size_type get_and_check_data_index(size_type index) const { + auto const& idx = get_index(); + if (index >= idx.size_) { + throw_exception(std::out_of_range{ + "dynamic_fws_multimap::bucket::at() out of range"}); + } + return idx.begin_ + index; + } + + template > + iterator prepare_insert(bucket::iterator it) { + auto const pos = std::distance(begin(), it); + auto& index = get_index(); + reserve(index.size_ + 1U); + it = std::next(begin(), pos); + std::move_backward(it, end(), std::next(end())); + ++index.size_; + ++mutable_mm().element_count_; + return it; + } + + dynamic_fws_multimap_base& mutable_mm() noexcept { + return const_cast(multimap_); // NOLINT + } + + dynamic_fws_multimap_base const& multimap_; + size_type index_; + }; + + using mutable_bucket = bucket; + using const_bucket = bucket; + + template + struct bucket_iterator { + friend dynamic_fws_multimap_base; + using iterator_category = std::random_access_iterator_tag; + using value_type = bucket; + using difference_type = int; + using pointer = value_type; + using reference = value_type; + + template > + // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) + bucket_iterator(bucket_iterator const& it) + : multimap_{it.multimap_}, index_{it.index_} {} + + value_type operator*() const { + return const_cast(multimap_) // NOLINT + .at(access_t{index_}); + } + + value_type operator->() const { return multimap_.at(access_t{index_}); } + + template > + value_type operator->() { + return const_cast(multimap_) // NOLINT + .at(access_t{index_}); + } + + bucket_iterator& operator+=(difference_type n) { + index_ += n; + return *this; + } + + bucket_iterator& operator-=(difference_type n) { + index_ -= n; + return *this; + } + + bucket_iterator& operator++() { + ++index_; + return *this; + } + + bucket_iterator operator++(int) { + auto old = *this; + ++(*this); + return old; + } + + bucket_iterator& operator--() { + ++index_; + return *this; + } + + bucket_iterator operator--(int) { + auto old = *this; + --(*this); + return old; + } + + bucket_iterator operator+(difference_type n) const { + return {multimap_, index_ + n}; + } + + bucket_iterator operator-(difference_type n) const { + return {multimap_, index_ - n}; + } + + difference_type operator-(bucket_iterator const& rhs) const { + return static_cast(index_) - + static_cast(rhs.index_); + } + + value_type operator[](difference_type n) const { + return multimap_.at(access_t{index_ + n}); + } + + template > + value_type operator[](difference_type const n) { + return const_cast(multimap_) // NOLINT + .at(access_t{index_ + n}); + } + + bool operator<(bucket_iterator const& rhs) const { + return index_ < rhs.index_; + } + bool operator<=(bucket_iterator const& rhs) const { + return index_ <= rhs.index_; + } + bool operator>(bucket_iterator const& rhs) const { + return index_ > rhs.index_; + } + bool operator>=(bucket_iterator const& rhs) const { + return index_ >= rhs.index_; + } + + bool operator==(bucket_iterator const& rhs) const { + return index_ == rhs.index_ && &multimap_ == &rhs.multimap_; + } + + bool operator!=(bucket_iterator const& rhs) const { + return index_ != rhs.index_ || &multimap_ != &rhs.multimap_; + } + + protected: + bucket_iterator(dynamic_fws_multimap_base const& multimap, + size_type const index) + : multimap_{multimap}, index_{index} {} + + dynamic_fws_multimap_base const& multimap_; + size_type index_; + }; + + using iterator = bucket_iterator; + using const_iterator = bucket_iterator; + + mutable_bucket operator[](access_t index) { + if (index >= index_.size()) { + index_.resize(to_idx(index) + 1U); + } + return {*this, to_idx(index)}; + } + + const_bucket operator[](access_t const index) const { + assert(index < index_.size()); + return {*this, to_idx(index)}; + } + + mutable_bucket at(access_t const index) { + if (index >= index_.size()) { + throw_exception( + std::out_of_range{"dynamic_fws_multimap::at() out of range"}); + } + return {*this, to_idx(index)}; + } + + const_bucket at(access_t const index) const { + if (index >= index_.size()) { + throw_exception( + std::out_of_range{"dynamic_fws_multimap::at() out of range"}); + } + return {*this, to_idx(index)}; + } + + mutable_bucket front() { return (*this)[access_t{0U}]; } + const_bucket front() const { return (*this)[access_t{0U}]; } + + mutable_bucket back() { return (*this)[access_t{size() - 1U}]; } + const_bucket back() const { return (*this)[access_t{size() - 1U}]; } + + mutable_bucket emplace_back() { return (*this)[access_t{size()}]; } + + mutable_bucket get_or_create(access_t const index) { + verify(index != std::numeric_limits::max(), + "mutable_fws_multimap::get_or_create: type bound"); + if (to_idx(index) + 1U >= index_.size()) { + index_.resize(to_idx(index + 1U)); + } + return {*this, to_idx(index)}; + } + + void erase(access_t const i) { + if (to_idx(i) < index_.size()) { + release_bucket(index_[to_idx(i)]); + } + } + + size_type size() const noexcept { return index_.size(); } + size_type data_size() const noexcept { return data_.size(); } + size_type element_count() const noexcept { return element_count_; } + [[nodiscard]] bool empty() const noexcept { return size() == 0; } + + std::size_t allocated_size() const noexcept { + auto size = index_.allocated_size_ * sizeof(index_type) + + data_.allocated_size_ * sizeof(value_type); + for (auto const& v : free_buckets_) { + size += v.allocated_size_ * sizeof(index_type); + } + return size; + } + + constexpr size_type max_entries_per_bucket() const noexcept { + return MAX_ENTRIES_PER_BUCKET; + } + + constexpr size_type max_entries_per_bucket_log2() const noexcept { + return Log2MaxEntriesPerBucket; + } + + iterator begin() { return {*this, size_type{0U}}; } + const_iterator begin() const { return {*this, size_type{0U}}; } + iterator end() { + return iterator{*this, static_cast(index_.size())}; + } + const_iterator end() const { + return const_iterator{*this, static_cast(index_.size())}; + } + + friend iterator begin(dynamic_fws_multimap_base& m) { return m.begin(); } + friend const_iterator begin(dynamic_fws_multimap_base const& m) { + return m.begin(); + } + + friend iterator end(dynamic_fws_multimap_base& m) { return m.end(); } + friend const_iterator end(dynamic_fws_multimap_base const& m) { + return m.end(); + } + + data_vec_t& data() noexcept { return data_; } + data_vec_t const& data() const noexcept { return data_; } + + void reserve(size_type index, size_type data) { + index_.reserve(index); + data_.reserve(data); + } + + void clear() { + index_.clear(); + data_.clear(); + for (auto& e : free_buckets_) { + e.clear(); + } + element_count_ = 0U; + } + + size_type insert_new_entry(size_type const i) { + auto const map_index = to_idx(i); + assert(map_index < index_.size()); + auto& idx = index_[map_index]; + if (idx.size_ == idx.capacity_) { + grow_bucket(map_index, idx); + } + auto const data_index = idx.begin_ + idx.size_; + ++idx.size_; + assert(idx.size_ <= idx.capacity_); + return data_index; + } + + void grow_bucket(size_type const map_index, index_type& idx) { + grow_bucket(to_idx(map_index), idx, idx.capacity_ + 1U); + } + + void grow_bucket(size_type const map_index, index_type& idx, + size_type const requested_capacity) { + /* Currently, only trivially copyable types are supported. + * Changing this would require to do custom memory management. */ + static_assert(std::is_trivially_copyable_v); + + assert(requested_capacity > 0U); + auto const new_capacity = + size_type{cista::next_power_of_two(to_idx(requested_capacity))}; + auto const new_order = get_order(new_capacity); + + verify(new_order <= Log2MaxEntriesPerBucket, + "dynamic_fws_multimap: too many entries in a bucket"); + + auto old_bucket = idx; + + auto free_bucket = get_free_bucket(new_order); + if (free_bucket) { + // reuse free bucket + if (old_bucket.capacity_ != 0U) { + move_entries(map_index, old_bucket.begin_, free_bucket->begin_, + idx.size_); + release_bucket(old_bucket); + } + idx.begin_ = free_bucket->begin_; + idx.capacity_ = free_bucket->capacity_; + } else { + if (idx.begin_ + idx.capacity_ == data_.size()) { + // last bucket -> resize data vector + auto const additional_capacity = new_capacity - idx.capacity_; + data_.resize(data_.size() + additional_capacity); + idx.capacity_ = new_capacity; + } else { + // allocate new bucket at the end + auto const new_begin = data_.size(); + data_.resize(data_.size() + new_capacity); + move_entries(map_index, idx.begin_, new_begin, idx.size_); + idx.begin_ = new_begin; + idx.capacity_ = new_capacity; + release_bucket(old_bucket); + } + } + } + + std::optional get_free_bucket(size_type const requested_order) { + assert(requested_order <= Log2MaxEntriesPerBucket); + + auto const pop = [](index_vec_t& vec) -> std::optional { + if (!vec.empty()) { + auto it = std::prev(vec.end()); + auto const entry = *it; + vec.erase(it); + return entry; + } + return {}; + }; + + return pop(free_buckets_[to_idx(requested_order)]); // NOLINT + } + + void release_bucket(index_type& bucket) { + if (bucket.capacity_ != 0U) { + auto const order = get_order(bucket.capacity_); + assert(order <= Log2MaxEntriesPerBucket); + bucket.size_ = size_type{0U}; + free_buckets_[to_idx(order)].push_back(index_type{bucket}); // NOLINT + bucket.capacity_ = size_type{0U}; + } + } + + void move_entries(size_type const /* map_index */, + size_type const old_data_index, + size_type const new_data_index, size_type const count) { + if (count == 0U) { + return; + } + auto old_data = &data_[old_data_index]; + auto new_data = &data_[new_data_index]; + if constexpr (std::is_trivially_copyable_v) { + std::memcpy(new_data, old_data, to_idx(count) * sizeof(value_type)); + } else { + for (auto i = static_cast(0); i < count; + ++i, ++old_data, ++new_data) { + *new_data = value_type(std::move(*old_data)); + old_data->~T(); + } + } + } + + size_type push_back_entry(size_type const map_index, value_type const& val) { + auto const data_index = insert_new_entry(map_index); + data_[data_index] = val; + ++element_count_; + return data_index; + } + + template + size_type emplace_back_entry(size_type const i, Args&&... args) { + auto const map_index = to_idx(i); + auto const data_index = insert_new_entry(map_index); + data_[data_index] = value_type{std::forward(args)...}; + ++element_count_; + return data_index; + } + + static size_type get_order(size_type const size) { + return size_type{cista::trailing_zeros(to_idx(size))}; + } + + index_vec_t index_; + data_vec_t data_; + array free_buckets_; + size_type element_count_{}; +}; + +namespace offset { + +template +struct mutable_multimap_helper { + template + using vec = vector; + using type = dynamic_fws_multimap_base; +}; + +template +using mutable_fws_multimap = + typename mutable_multimap_helper::type; + +} // namespace offset + +namespace raw { + +template +struct mutable_multimap_helper { + template + using vec = vector; + using type = dynamic_fws_multimap_base; +}; + +template +using mutable_fws_multimap = + typename mutable_multimap_helper::type; + +} // namespace raw + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/nvec.h b/parallel/parallel_src/extern/cista/include/cista/containers/nvec.h new file mode 100644 index 00000000..82aa930b --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/nvec.h @@ -0,0 +1,590 @@ +#pragma once + +#include +#include + +#include "cista/containers/array.h" +#include "cista/containers/vector.h" +#include "cista/strong.h" +#include "cista/verify.h" + +namespace cista { +template +struct const_bucket final { + using size_type = SizeType; + using index_value_type = typename IndexVec::value_type; + using data_value_type = typename DataVec::value_type; + + using value_type = data_value_type; + using iterator = typename DataVec::const_iterator; + using const_iterator = typename DataVec::const_iterator; + using reference = typename DataVec::reference; + using const_reference = typename DataVec::const_reference; + + using iterator_category = std::random_access_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = std::add_pointer_t; + + const_bucket(DataVec const* data, IndexVec const* index, + index_value_type const i) + : data_{data}, index_{index}, i_{to_idx(i)} {} + + friend data_value_type* data(const_bucket b) { return &b[0]; } + friend index_value_type size(const_bucket b) { return b.size(); } + + template , + typename = std::enable_if_t>> + std::string_view view() const { + return std::string_view{begin(), size()}; + } + + value_type const& front() const { + assert(!empty()); + return operator[](0); + } + + value_type const& back() const { + assert(!empty()); + return operator[](size() - 1U); + } + + bool empty() const { return begin() == end(); } + + value_type const& at(std::size_t const i) const { + verify(i < size(), "bucket::at: index out of range"); + return *(begin() + i); + } + + value_type const& operator[](std::size_t const i) const { + assert(is_inside_bucket(i)); + return data()[index()[i_] + i]; + } + + const_bucket operator*() const { return *this; } + + std::size_t size() const { return bucket_end_idx() - bucket_begin_idx(); } + + const_iterator begin() const { return data().begin() + bucket_begin_idx(); } + const_iterator end() const { return data().begin() + bucket_end_idx(); } + + friend const_iterator begin(const_bucket const& b) { return b.begin(); } + friend const_iterator end(const_bucket const& b) { return b.end(); } + + friend bool operator==(const_bucket const& a, const_bucket const& b) { + assert(a.data_ == b.data_); + assert(a.index_ == b.index_); + return a.i_ == b.i_; + } + friend bool operator!=(const_bucket const& a, const_bucket const& b) { + assert(a.data_ == b.data_); + assert(a.index_ == b.index_); + return a.i_ != b.i_; + } + const_bucket& operator++() { + ++i_; + return *this; + } + const_bucket& operator--() { + --i_; + return *this; + } + const_bucket operator*() { return *this; } + const_bucket& operator+=(difference_type const n) { + i_ += n; + return *this; + } + const_bucket& operator-=(difference_type const n) { + i_ -= n; + return *this; + } + const_bucket operator+(difference_type const n) const { + auto tmp = *this; + tmp += n; + return tmp; + } + const_bucket operator-(difference_type const n) const { + auto tmp = *this; + tmp -= n; + return tmp; + } + friend difference_type operator-(const_bucket const& a, + const_bucket const& b) { + assert(a.data_ == b.data_); + assert(a.index_ == b.index_); + return a.i_ - b.i_; + } + +private: + DataVec const& data() const { return *data_; } + IndexVec const& index() const { return *index_; } + + std::size_t bucket_begin_idx() const { return to_idx(index()[i_]); } + std::size_t bucket_end_idx() const { return to_idx(index()[i_ + 1U]); } + bool is_inside_bucket(std::size_t const i) const { + return bucket_begin_idx() + i < bucket_end_idx(); + } + + DataVec const* data_; + IndexVec const* index_; + size_type i_; +}; + +template +struct bucket final { + using size_type = SizeType; + using index_value_type = typename IndexVec::value_type; + using data_value_type = typename DataVec::value_type; + + using value_type = data_value_type; + using iterator = typename DataVec::iterator; + using const_iterator = typename DataVec::iterator; + using reference = typename DataVec::reference; + using const_reference = typename DataVec::const_reference; + + using iterator_category = std::random_access_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = std::add_pointer_t; + + bucket(DataVec* data, IndexVec* index, index_value_type const i) + : data_{data}, index_{index}, i_{to_idx(i)} {} + + friend data_value_type* data(bucket b) { return &b[0]; } + friend index_value_type size(bucket b) { return b.size(); } + + template , + typename = std::enable_if_t>> + std::string_view view() const { + return std::string_view{begin(), size()}; + } + + value_type& front() { + assert(!empty()); + return operator[](0); + } + + value_type& back() { + assert(!empty()); + return operator[](size() - 1U); + } + + bool empty() const { return begin() == end(); } + + value_type& operator[](std::size_t const i) { + assert(is_inside_bucket(i)); + return data()[to_idx(index()[i_] + i)]; + } + + value_type const& operator[](std::size_t const i) const { + assert(is_inside_bucket(i)); + return data()[to_idx(index()[i_] + i)]; + } + + value_type const& at(std::size_t const i) const { + verify(i < size(), "bucket::at: index out of range"); + return *(begin() + i); + } + + value_type& at(std::size_t const i) { + verify(i < size(), "bucket::at: index out of range"); + return *(begin() + i); + } + + reference operator*() const { return *this; } + + operator const_bucket() const { + return {data_, index_, i_}; + } + + index_value_type size() const { + return bucket_end_idx() - bucket_begin_idx(); + } + iterator begin() { return data().begin() + bucket_begin_idx(); } + iterator end() { return data().begin() + bucket_end_idx(); } + const_iterator begin() const { return data().begin() + bucket_begin_idx(); } + const_iterator end() const { return data().begin() + bucket_end_idx(); } + friend iterator begin(bucket const& b) { return b.begin(); } + friend iterator end(bucket const& b) { return b.end(); } + friend iterator begin(bucket& b) { return b.begin(); } + friend iterator end(bucket& b) { return b.end(); } + + friend bool operator==(bucket const& a, bucket const& b) { + assert(a.data_ == b.data_); + assert(a.index_ == b.index_); + return a.i_ == b.i_; + } + friend bool operator!=(bucket const& a, bucket const& b) { + assert(a.data_ == b.data_); + assert(a.index_ == b.index_); + return a.i_ != b.i_; + } + bucket& operator++() { + ++i_; + return *this; + } + bucket& operator--() { + --i_; + return *this; + } + bucket operator*() { return *this; } + bucket& operator+=(difference_type const n) { + i_ += n; + return *this; + } + bucket& operator-=(difference_type const n) { + i_ -= n; + return *this; + } + bucket operator+(difference_type const n) const { + auto tmp = *this; + tmp += n; + return tmp; + } + bucket operator-(difference_type const n) const { + auto tmp = *this; + tmp -= n; + return tmp; + } + friend difference_type operator-(bucket const& a, bucket const& b) { + assert(a.data_ == b.data_); + assert(a.index_ == b.index_); + return a.i_ - b.i_; + } + +private: + DataVec& data() const { return *data_; } + IndexVec& index() const { return *index_; } + + index_value_type bucket_begin_idx() const { return to_idx(index()[i_]); } + index_value_type bucket_end_idx() const { return to_idx(index()[i_ + 1U]); } + bool is_inside_bucket(std::size_t const i) const { + return bucket_begin_idx() + i < bucket_end_idx(); + } + + size_type i_; + DataVec* data_; + IndexVec* index_; +}; + +template +struct const_meta_bucket { + using index_value_type = typename IndexVec::value_type; + + using const_iterator = std::conditional_t< + Depth == 1U, const_bucket, + const_meta_bucket>; + using iterator = const_iterator; + using difference_type = std::ptrdiff_t; + using value_type = const_iterator; + using pointer = void; + using reference = const_meta_bucket; + using const_reference = const_meta_bucket; + using iterator_category = std::random_access_iterator_tag; + using size_type = SizeType; + + const_meta_bucket(DataVec const* data, IndexVec const* index, + index_value_type const i) + : data_{data}, index_{index}, i_{i} {} + + index_value_type size() const { return index()[i_ + 1U] - index()[i_]; } + + iterator begin() const { return {data_, index_ - 1U, index()[i_]}; } + const_iterator end() const { return {data_, index_ - 1U, index()[i_ + 1U]}; } + + friend iterator begin(const_meta_bucket const& b) { return b.begin(); } + friend iterator end(const_meta_bucket const& b) { return b.end(); } + + reference operator*() const { return *this; } + + iterator operator[](size_type const i) { return begin() + i; } + + const_meta_bucket& operator++() { + ++i_; + return *this; + } + const_meta_bucket& operator--() { + --i_; + return *this; + } + const_meta_bucket& operator+=(difference_type const n) { + i_ += n; + return *this; + } + const_meta_bucket& operator-=(difference_type const n) { + i_ -= n; + return *this; + } + const_meta_bucket operator+(difference_type const n) const { + auto tmp = *this; + tmp += n; + return tmp; + } + const_meta_bucket operator-(difference_type const n) const { + auto tmp = *this; + tmp -= n; + return tmp; + } + + friend bool operator==(const_meta_bucket const& a, + const_meta_bucket const& b) { + return a.i_ == b.i_; + } + + friend bool operator!=(const_meta_bucket const& a, + const_meta_bucket const& b) { + return !(a == b); + } + +private: + IndexVec const& index() const { return *index_; } + DataVec const& data() const { return *data_; } + + DataVec const* data_; + IndexVec const* index_; + index_value_type i_; +}; + +template +struct meta_bucket { + using index_value_type = typename IndexVec::value_type; + + using iterator = + std::conditional_t, + meta_bucket>; + using const_iterator = std::conditional_t< + Depth == 1U, const_bucket, + const_meta_bucket>; + + using value_type = iterator; + using iterator_category = std::random_access_iterator_tag; + using reference = meta_bucket; + using const_reference = const_meta_bucket; + using difference_type = std::ptrdiff_t; + using size_type = SizeType; + + meta_bucket(DataVec* data, IndexVec* index, index_value_type const i) + : data_{data}, index_{index}, i_{i} {} + + index_value_type size() const { return index()[i_ + 1U] - index()[i_]; } + + iterator begin() { return {data_, index_ - 1U, index()[i_]}; } + iterator end() { return {data_, index_ - 1U, index()[i_ + 1U]}; } + + const_iterator begin() const { return {data_, index_ - 1U, index()[i_]}; } + const_iterator end() const { return {data_, index_ - 1U, index()[i_ + 1U]}; } + + friend iterator begin(meta_bucket& b) { return b.begin(); } + friend iterator end(meta_bucket& b) { return b.end(); } + + friend const_iterator begin(meta_bucket const& b) { return b.begin(); } + friend const_iterator end(meta_bucket const& b) { return b.end(); } + + const_reference operator*() const { return {data_, index_, i_}; } + reference operator*() { return *this; } + + iterator operator[](size_type const i) { return begin() + i; } + const_iterator operator[](size_type const i) const { return begin() + i; } + + operator const_meta_bucket() const { + return {data_, index_, i_}; + } + + meta_bucket& operator++() { + ++i_; + return *this; + } + + meta_bucket& operator--() { + --i_; + return *this; + } + meta_bucket& operator+=(difference_type const n) { + i_ += n; + return *this; + } + meta_bucket& operator-=(difference_type const n) { + i_ -= n; + return *this; + } + meta_bucket operator+(difference_type const n) const { + auto tmp = *this; + tmp += n; + return tmp; + } + meta_bucket operator-(difference_type const n) const { + auto tmp = *this; + tmp -= n; + return tmp; + } + + friend bool operator==(meta_bucket const& a, meta_bucket const& b) { + return a.i_ == b.i_; + } + + friend bool operator!=(meta_bucket const& a, meta_bucket const& b) { + return !(a == b); + } + +private: + IndexVec& index() const { return *index_; } + DataVec& data() const { return *data_; } + + DataVec* data_; + IndexVec* index_; + index_value_type i_; +}; + +template +struct basic_nvec { + using data_vec_t = DataVec; + using index_vec_t = IndexVec; + using size_type = SizeType; + using data_value_type = typename DataVec::value_type; + using index_value_type = typename IndexVec::value_type; + + using bucket_t = bucket; + using const_bucket_t = const_bucket; + + using iterator_category = std::random_access_iterator_tag; + using difference_type = std::ptrdiff_t; + using iterator = meta_bucket; + using const_iterator = const_meta_bucket; + using reference = iterator; + using const_reference = const_iterator; + using value_type = iterator; + + iterator begin() { return {&data_, &index_.back(), 0U}; } + iterator end() { return {&data_, &index_.back(), size()}; } + + const_iterator begin() const { return {&data_, &index_.back(), 0U}; } + const_iterator end() const { return {&data_, &index_.back(), size()}; } + + iterator front() { return begin(); } + iterator back() { return begin() + size() - 1; } + + const_iterator front() const { return begin(); } + const_iterator back() const { return begin() + size() - 1; } + + template + void emplace_back(Container&& bucket) { + if (index_[0].size() == 0U) { + for (auto& i : index_) { + i.push_back(0U); + } + } + add(bucket); + } + + iterator operator[](Key const k) { return begin() + to_idx(k); } + const_iterator operator[](Key const k) const { return begin() + to_idx(k); } + + size_type size() const { + return index_[N - 1].size() == 0U ? 0U : (index_[N - 1].size() - 1U); + } + + template + size_type size(Key const first, Indices... rest) const { + constexpr auto const I = sizeof...(Indices); + verify(to_idx(first) < index_[I].size(), "nvec::at: index out of range"); + if (sizeof...(Indices) == 0U) { + return get_size(first); + } else { + return get_size(index_[I][first], rest...); + } + } + + template + bucket_t at(Key const first, Indices... rest) { + constexpr auto const I = sizeof...(Indices); + static_assert(I == N - 1); + verify(to_idx(first) < index_[I].size(), "nvec::at: index out of range"); + return get_bucket(index_[I][to_idx(first)], rest...); + } + + template + const_bucket_t at(Key const first, Indices... rest) const { + constexpr auto const I = sizeof...(Indices); + static_assert(I == N - 1); + verify(to_idx(first) < index_[I].size(), "nvec::at: index out of range"); + return get_bucket(index_[I][to_idx(first)], rest...); + } + + auto cista_members() noexcept { return std::tie(index_, data_); } + + template + bucket_t get_bucket(index_value_type const bucket_start, + index_value_type const i, Rest... rest) { + return get_bucket(index_[sizeof...(Rest)][bucket_start + i], + rest...); + } + + bucket_t get_bucket(index_value_type const bucket_start, + index_value_type const i) { + return {&data_, &index_[0], bucket_start + i}; + } + + template + const_bucket_t get_bucket(index_value_type const bucket_start, + index_value_type const i, Rest... rest) const { + return get_bucket(index_[sizeof...(Rest)][bucket_start + i], + rest...); + } + + const_bucket_t get_bucket(index_value_type const bucket_start, + index_value_type const i) const { + return {&data_, &index_[0], bucket_start + i}; + } + + template + void add(Container&& c) { + if constexpr (L == 0) { + index_[0].push_back(static_cast(data_.size() + c.size())); + data_.insert(std::end(data_), std::make_move_iterator(std::begin(c)), + std::make_move_iterator(std::end(c))); + } else { + index_[L].push_back( + static_cast(index_[L - 1].size() + c.size() - 1U)); + for (auto&& x : c) { + add(x); + } + } + } + + template + size_type get_size(index_value_type const i, index_value_type const j, + Rest... rest) const { + if constexpr (sizeof...(Rest) == 0U) { + return index_[L][i + j + 1] - index_[L][i + j]; + } else { + return get_size(index_[L][i + j], rest...); + } + } + + template + size_type get_size(index_value_type const i) const { + return index_[L][i + 1] - index_[L][i]; + } + + array index_; + DataVec data_; +}; + +namespace offset { + +template +using nvec = basic_nvec, vector>, N, SizeType>; + +} // namespace offset + +namespace raw { + +template +using nvec = basic_nvec, vector>, N, SizeType>; + +} // namespace raw + +} // namespace cista \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/offset_ptr.h b/parallel/parallel_src/extern/cista/include/cista/containers/offset_ptr.h new file mode 100644 index 00000000..a8e699f7 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/offset_ptr.h @@ -0,0 +1,204 @@ +#pragma once + +#if defined(__has_include) && (_MSVC_LANG >= 202002L || __cplusplus >= 202002L) +#if __has_include() +#include +#endif +#endif +#include +#include + +#include "cista/offset_t.h" +#include "cista/strong.h" + +namespace cista { + +#if __cpp_lib_bit_cast +inline offset_t to_offset(void const* ptr) noexcept { + return std::bit_cast(ptr); +} +#else +inline offset_t to_offset(void const* ptr) noexcept { + offset_t r; + std::memcpy(&r, &ptr, sizeof(ptr)); + return r; +} +#endif + +template +struct offset_ptr { + constexpr offset_ptr() noexcept = default; + constexpr offset_ptr(std::nullptr_t) noexcept : offset_{NULLPTR_OFFSET} {} + offset_ptr(T const* p) noexcept : offset_{ptr_to_offset(p)} {} + + offset_ptr& operator=(T const* p) noexcept { + offset_ = ptr_to_offset(p); + return *this; + } + offset_ptr& operator=(std::nullptr_t) noexcept { + offset_ = NULLPTR_OFFSET; + return *this; + } + + offset_ptr(offset_ptr const& o) noexcept : offset_{ptr_to_offset(o.get())} {} + offset_ptr(offset_ptr&& o) noexcept : offset_{ptr_to_offset(o.get())} {} + offset_ptr& operator=(offset_ptr const& o) noexcept { + offset_ = ptr_to_offset(o.get()); + return *this; + } + offset_ptr& operator=(offset_ptr&& o) noexcept { + offset_ = ptr_to_offset(o.get()); + return *this; + } + + ~offset_ptr() noexcept = default; + + offset_t ptr_to_offset(T const* p) const noexcept { + return p == nullptr ? NULLPTR_OFFSET + : static_cast(to_offset(p) - to_offset(this)); + } + + explicit operator bool() const noexcept { return offset_ != NULLPTR_OFFSET; } + explicit operator void*() const noexcept { return get(); } + explicit operator void const*() const noexcept { return get(); } + operator T*() const noexcept { return get(); } + T& operator*() const noexcept { return *get(); } + T* operator->() const noexcept { return get(); } + T& operator[](std::size_t const i) const noexcept { return get()[i]; } + + T* get() const noexcept { + auto const ptr = + offset_ == NULLPTR_OFFSET + ? nullptr + : reinterpret_cast(reinterpret_cast(this) + offset_); + return ptr; + } + + template + T* operator+(Int const i) const noexcept { + return get() + i; + } + + template + T* operator-(Int const i) const noexcept { + return get() - i; + } + + template + constexpr T* operator+(strong const& s) { + return get() + s.v_; + } + + template + constexpr T* operator-(strong const& s) { + return get() - s.v_; + } + + offset_ptr& operator++() noexcept { + offset_ = ptr_to_offset(get() + 1); + return *this; + } + + offset_ptr& operator--() noexcept { + offset_ = ptr_to_offset(get() - 1); + return *this; + } + + offset_ptr operator++(int) const noexcept { return offset_ptr{get() + 1}; } + offset_ptr operator--(int) const noexcept { return offset_ptr{get() - 1}; } + + offset_t offset_{NULLPTR_OFFSET}; +}; + +template +struct offset_ptr>> { + constexpr offset_ptr() noexcept = default; + constexpr offset_ptr(std::nullptr_t) noexcept : offset_{NULLPTR_OFFSET} {} + offset_ptr(T const* p) noexcept : offset_{ptr_to_offset(p)} {} + + offset_ptr& operator=(T const* p) noexcept { + offset_ = ptr_to_offset(p); + return *this; + } + offset_ptr& operator=(std::nullptr_t) noexcept { + offset_ = NULLPTR_OFFSET; + return *this; + } + + offset_ptr(offset_ptr const& o) noexcept : offset_{ptr_to_offset(o.get())} {} + offset_ptr(offset_ptr&& o) noexcept : offset_{ptr_to_offset(o.get())} {} + offset_ptr& operator=(offset_ptr const& o) noexcept { + offset_ = ptr_to_offset(o.get()); + return *this; + } + offset_ptr& operator=(offset_ptr&& o) noexcept { + offset_ = ptr_to_offset(o.get()); + return *this; + } + + offset_t ptr_to_offset(T const* p) const noexcept { + return p == nullptr ? NULLPTR_OFFSET + : static_cast(to_offset(p) - to_offset(this)); + } + + operator bool() const noexcept { return offset_ != NULLPTR_OFFSET; } + explicit operator void*() const noexcept { return get(); } + explicit operator void const*() const noexcept { return get(); } + T* get() const noexcept { + auto const ptr = + offset_ == NULLPTR_OFFSET + ? nullptr + : reinterpret_cast(reinterpret_cast(this) + offset_); + return ptr; + } + + friend bool operator==(std::nullptr_t, offset_ptr const& o) noexcept { + return o.offset_ == NULLPTR_OFFSET; + } + friend bool operator==(offset_ptr const& o, std::nullptr_t) noexcept { + return o.offset_ == NULLPTR_OFFSET; + } + friend bool operator!=(std::nullptr_t, offset_ptr const& o) noexcept { + return o.offset_ != NULLPTR_OFFSET; + } + friend bool operator!=(offset_ptr const& o, std::nullptr_t) noexcept { + return o.offset_ != NULLPTR_OFFSET; + } + + offset_t offset_{NULLPTR_OFFSET}; +}; + +template +struct is_pointer_helper : std::false_type {}; + +template +struct is_pointer_helper : std::true_type {}; + +template +struct is_pointer_helper> : std::true_type {}; + +template +constexpr bool is_pointer_v = is_pointer_helper>::value; + +template +struct remove_pointer_helper { + using type = T; +}; + +template +struct remove_pointer_helper { + using type = T; +}; + +template +struct remove_pointer_helper> { + using type = T; +}; + +template +struct remove_pointer : remove_pointer_helper> {}; + +template +using remove_pointer_t = typename remove_pointer::type; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/optional.h b/parallel/parallel_src/extern/cista/include/cista/containers/optional.h new file mode 100644 index 00000000..a9d5df51 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/optional.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include + +#include "cista/exception.h" + +namespace cista { + +template +struct optional { + optional() = default; + + template + optional(Ts&&... t) noexcept(std::is_nothrow_constructible_v) { + new (&storage_[0]) T{std::forward(t)...}; + valid_ = true; + } + + template + optional(std::nullopt_t) noexcept {} + + optional(optional const& other) { copy(other); } + optional& operator=(optional const& other) { return copy(other); } + + optional(optional&& other) noexcept { move(std::forward(other)); } + optional& operator=(optional&& other) noexcept { + return move(std::forward(other)); + } + + optional& copy(optional const& other) { + if (other.has_value()) { + new (&storage_[0]) T{other.value()}; + } + + valid_ = other.has_value(); + + return *this; + } + + optional& move(optional&& other) { + if (other.has_value()) { + new (&storage_[0]) T(std::move(other.value())); + } + + valid_ = other.has_value(); + + return *this; + } + + T const& value() const { + if (!valid_) { + throw_exception(std::bad_optional_access{}); + } + return *reinterpret_cast(&storage_[0]); + } + + T& value() { + if (!valid_) { + throw_exception(std::bad_optional_access{}); + } + return *reinterpret_cast(&storage_[0]); + } + + bool has_value() const noexcept { return valid_; } + operator bool() const noexcept { return valid_; } + + T* operator->() noexcept { return reinterpret_cast(&storage_[0]); } + T const* operator->() const noexcept { + return reinterpret_cast(&storage_[0]); + } + + T& operator*() noexcept { return *reinterpret_cast(&storage_[0]); } + T const& operator*() const noexcept { + return *reinterpret_cast(&storage_[0]); + } + + bool valid_{false}; + alignas(std::alignment_of_v) unsigned char storage_[sizeof(T)]; +}; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/paged.h b/parallel/parallel_src/extern/cista/include/cista/containers/paged.h new file mode 100644 index 00000000..f7dea3e5 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/paged.h @@ -0,0 +1,153 @@ +#pragma once + +#include +#include +#include + +#include "cista/bit_counting.h" +#include "cista/containers/array.h" +#include "cista/next_power_of_2.h" +#include "cista/verify.h" + +namespace cista { + +template +struct page { + bool valid() const { return capacity_ != 0U; } + PageSizeType size() const noexcept { return size_; } + + PageSizeType size_{0U}; + PageSizeType capacity_{0U}; + SizeType start_{0U}; +}; + +template ) / + sizeof(typename DataVec::value_type))), + PageSizeType MaxPageSize = 4096> +struct paged { + using value_type = typename DataVec::value_type; + using iterator = typename DataVec::iterator; + using const_iterator = typename DataVec::const_iterator; + using reference = typename DataVec::reference; + using const_reference = typename DataVec::const_reference; + using size_type = SizeType; + using page_size_type = PageSizeType; + using page_t = page; + + static_assert(sizeof(value_type) * MinPageSize >= sizeof(page_t)); + static_assert(std::is_trivially_copyable_v); + + static constexpr std::size_t free_list_index(size_type const capacity) { + return static_cast(constexpr_trailing_zeros(capacity) - + constexpr_trailing_zeros(MinPageSize)); + } + + static constexpr auto const free_list_size = + free_list_index(MaxPageSize) + 1U; + + page_t resize_page(page_t const& p, PageSizeType const size) { + if (size <= p.capacity_) { + return {size, p.capacity_, p.start_}; + } else { + auto const new_page = create_page(size); + copy(new_page, p); + free_page(p); + return new_page; + } + } + + page_t create_page(PageSizeType const size) { + auto const capacity = next_power_of_two(std::max(MinPageSize, size)); + auto const i = free_list_index(capacity); + verify(i < free_list_.size(), "paged::create_page: size > max capacity"); + if (!free_list_[i].empty()) { + auto const start = free_list_[i].pop(*this); + return {size, capacity, start}; + } else { + auto const start = data_.size(); + data_.resize(data_.size() + capacity); + return {size, capacity, start}; + } + } + + void free_page(page_t const& p) { + if (!p.valid()) { + return; + } + auto const i = free_list_index(p.capacity_); + verify(i < free_list_.size(), "paged::free_page: size > max capacity"); + free_list_[i].push(*this, p.start_); + } + + template + T read(size_type const offset) { + static_assert(std::is_trivially_copyable_v); + auto x = T{}; + std::memcpy(&x, &data_[offset], sizeof(x)); + return x; + } + + template + void write(size_type const offset, T const& x) { + static_assert(std::is_trivially_copyable_v); + std::memcpy(&data_[offset], &x, sizeof(T)); + } + + value_type* data(page_t const& p) { return &data_[p.start_]; } + value_type const* data(page_t const& p) const { return &data_[p.start_]; } + + value_type* begin(page_t const& p) { return data(p); } + value_type const* begin(page_t const& p) const { return data(p); } + + value_type* end(page_t& p) const { return begin(p) + p.size(); } + value_type const* end(page_t const& p) const { return begin() + p.size; } + + void copy(page_t const& to, page_t const& from) { + std::memcpy(data(to), data(from), from.size() * sizeof(value_type)); + } + + template + void copy(page_t const& to, ItA begin, ItB end) { + auto const n = static_cast(std::distance(begin, end)); + if (n != 0U) { + std::memcpy(data(to), &*begin, n * sizeof(value_type)); + } + } + + void clear() { + data_.clear(); + for (auto& n : free_list_) { + n.next_ = std::numeric_limits::max(); + } + } + + struct node { + bool empty() const { + return next_ == std::numeric_limits::max(); + } + + void push(paged& m, size_type const start) { + m.write(start, next_); + next_ = start; + } + + size_type pop(paged& m) { + verify(!empty(), "paged: invalid read access to empty free list entry"); + auto const next_start = m.read(next_); + auto start = next_; + next_ = next_start; + return start; + } + + size_type next_{std::numeric_limits::max()}; + }; + + DataVec data_; + array free_list_{}; +}; + +} // namespace cista \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/paged_vecvec.h b/parallel/parallel_src/extern/cista/include/cista/containers/paged_vecvec.h new file mode 100644 index 00000000..33dd4309 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/paged_vecvec.h @@ -0,0 +1,342 @@ +#pragma once + +#include "cista/strong.h" + +namespace cista { + +template +struct paged_vecvec { + using index_t = Index; + using data_t = Paged; + + using page_t = typename Paged::page_t; + using size_type = typename Paged::size_type; + using data_value_type = typename Paged::value_type; + + struct const_bucket final { + using size_type = typename Paged::size_type; + using data_value_type = typename Paged::value_type; + + using value_type = data_value_type; + using iterator = typename Paged::const_iterator; + using const_iterator = typename Paged::const_iterator; + using reference = typename Paged::const_reference; + using const_reference = typename Paged::const_reference; + + using iterator_category = std::random_access_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = std::add_pointer_t; + + const_bucket(paged_vecvec const* pv, Key const i) : pv_{pv}, i_{i} {} + + template , + typename = std::enable_if_t>> + std::string_view view() const { + return std::string_view{begin(), size()}; + } + + const_iterator begin() const { return pv_->data(i_); } + const_iterator end() const { return pv_->data(i_) + size(); } + friend const_iterator begin(const_bucket const& b) { return b.begin(); } + friend const_iterator end(const_bucket const& b) { return b.end(); } + + value_type const& operator[](std::size_t const i) const { + assert(i < size()); + return *(begin() + i); + } + + value_type const& at(std::size_t const i) const { + verify(i < size(), "paged_vecvec: const_bucket::at: index out of range"); + return *(begin() + i); + } + + value_type& at(std::size_t const i) { + verify(i < size(), "paged_vecvec: const_bucket::at: index out of range"); + return *(begin() + i); + } + + value_type const& front() const { + assert(!empty()); + return (*this)[0]; + } + + value_type const& back() const { + assert(!empty()); + return (*this)[size() - 1U]; + } + + reference operator*() const { return *this; } + + size_type size() const { return pv_->page(i_).size_; } + bool empty() const { return size() == 0U; } + + friend bool operator==(const_bucket const& a, const_bucket const& b) { + assert(a.pv_ == b.pv_); + return a.i_ == b.i_; + } + + friend bool operator!=(const_bucket const& a, const_bucket const& b) { + assert(a.pv_ == b.pv_); + return a.i_ != b.i_; + } + + const_bucket& operator++() { + ++i_; + return *this; + } + const_bucket& operator--() { + --i_; + return *this; + } + const_bucket operator*() { return *this; } + const_bucket& operator+=(difference_type const n) { + i_ += n; + return *this; + } + const_bucket& operator-=(difference_type const n) { + i_ -= n; + return *this; + } + const_bucket operator+(difference_type const n) const { + auto tmp = *this; + tmp += n; + return tmp; + } + const_bucket operator-(difference_type const n) const { + auto tmp = *this; + tmp -= n; + return tmp; + } + friend difference_type operator-(const_bucket const& a, + const_bucket const& b) { + assert(a.pv_ == b.pv_); + return a.i_ - b.i_; + } + + private: + paged_vecvec const* pv_; + Key i_; + }; + + struct bucket final { + using size_type = typename Paged::size_type; + using index_value_type = typename Paged::page_t; + using data_value_type = typename Paged::value_type; + + using value_type = data_value_type; + using iterator = typename Paged::iterator; + using const_iterator = typename Paged::iterator; + using reference = typename Paged::reference; + using const_reference = typename Paged::const_reference; + + using iterator_category = std::random_access_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = std::add_pointer_t; + + bucket(paged_vecvec* pv, Key const i) : pv_{pv}, i_{i} {} + + value_type& front() { + assert(!empty()); + return (*this)[0]; + } + + value_type& back() { + assert(!empty()); + return (*this)[size() - 1U]; + } + + value_type const& front() const { + assert(!empty()); + return (*this)[0]; + } + + value_type const& back() const { + assert(!empty()); + return (*this)[size() - 1U]; + } + + void push_back(data_value_type const& x) { + auto& p = pv_->page(i_); + p = pv_->paged_.resize_page(p, p.size_ + 1U); + (*this)[size() - 1U] = x; + } + + template , + typename = std::enable_if_t>> + std::string_view view() const { + return std::string_view{begin(), static_cast(size())}; + } + + iterator begin() { return pv_->data(i_); } + iterator end() { return pv_->data(i_) + size(); } + const_iterator begin() const { return pv_->data(i_); } + const_iterator end() const { return pv_->data(i_) + size(); } + friend iterator begin(bucket const& b) { return b.begin(); } + friend iterator end(bucket const& b) { return b.end(); } + friend iterator begin(bucket& b) { return b.begin(); } + friend iterator end(bucket& b) { return b.end(); } + + value_type& operator[](std::size_t const i) { + assert(i < size()); + return *(begin() + i); + } + + value_type const& operator[](std::size_t const i) const { + assert(i < size()); + return *(begin() + i); + } + + value_type const& at(std::size_t const i) const { + verify(i < size(), "bucket::at: index out of range"); + return *(begin() + i); + } + + value_type& at(std::size_t const i) { + verify(i < size(), "bucket::at: index out of range"); + return *(begin() + i); + } + + reference operator*() const { return *this; } + + operator const_bucket() const { return {pv_, i_}; } + + size_type size() const { return pv_->page(i_).size_; } + bool empty() const { return size() == 0U; } + + friend bool operator==(bucket const& a, bucket const& b) { + assert(a.pv_ == b.pv_); + return a.i_ == b.i_; + } + + friend bool operator!=(bucket const& a, bucket const& b) { + assert(a.pv_ == b.pv_); + return a.i_ != b.i_; + } + + bucket& operator++() { + ++i_; + return *this; + } + bucket& operator--() { + --i_; + return *this; + } + bucket operator*() { return *this; } + bucket& operator+=(difference_type const n) { + i_ += n; + return *this; + } + bucket& operator-=(difference_type const n) { + i_ -= n; + return *this; + } + bucket operator+(difference_type const n) const { + auto tmp = *this; + tmp += n; + return tmp; + } + bucket operator-(difference_type const n) const { + auto tmp = *this; + tmp -= n; + return tmp; + } + friend difference_type operator-(bucket const& a, bucket const& b) { + assert(a.pv_ == b.pv_); + return a.i_ - b.i_; + } + + private: + page_t page() { return pv_->page(i_); } + pointer data() { pv_->data(i_); } + + paged_vecvec* pv_; + Key i_; + }; + + using value_type = bucket; + using iterator = bucket; + using const_iterator = bucket; + + bucket operator[](Key const i) { return {this, i}; } + const_bucket operator[](Key const i) const { return {this, i}; } + + page_t& page(Key const i) { return idx_[to_idx(i)]; } + page_t const& page(Key const i) const { return idx_[to_idx(i)]; } + + data_value_type const* data(Key const i) const { + return data(idx_[to_idx(i)]); + } + data_value_type* data(Key const i) { return data(idx_[to_idx(i)]); } + data_value_type const* data(page_t const& p) const { return paged_.data(p); } + data_value_type* data(page_t const& p) { return paged_.data(p); } + + const_bucket at(Key const i) const { + verify(to_idx(i) < idx_.size(), "paged_vecvec::at: index out of range"); + return operator[](i); + } + + bucket at(Key const i) { + verify(to_idx(i) < idx_.size(), "paged_vecvec::at: index out of range"); + return operator[](i); + } + + bucket front() { return at(Key{0}); } + bucket back() { return at(Key{size() - 1}); } + + const_bucket front() const { return at(Key{0}); } + const_bucket back() const { return at(Key{size() - 1}); } + + size_type size() const { return idx_.size(); } + bool empty() const { return idx_.empty(); } + + bucket begin() { return front(); } + bucket end() { return operator[](Key{size()}); } + const_bucket begin() const { return front(); } + const_bucket end() const { return operator[](Key{size()}); } + + friend bucket begin(paged_vecvec& m) { return m.begin(); } + friend bucket end(paged_vecvec& m) { return m.end(); } + friend const_bucket begin(paged_vecvec const& m) { return m.begin(); } + friend const_bucket end(paged_vecvec const& m) { return m.end(); } + + template ().begin()), data_value_type>>> + void emplace_back(Container&& bucket) { + auto p = paged_.create_page( + static_cast(bucket.size())); + paged_.copy(p, std::begin(bucket), std::end(bucket)); + idx_.emplace_back(p); + } + + template + std::enable_if_t, data_value_type>> + emplace_back(std::initializer_list&& x) { + emplace_back(x); + } + + void emplace_back_empty() { idx_.emplace_back(paged_.create_page(0U)); } + + template >> + void emplace_back(char const* s) { + return emplace_back(std::string_view{s}); + } + + void resize(size_type const size) { + for (auto i = size; i < idx_.size(); ++i) { + paged_.free_page(idx_[i]); + } + idx_.resize(size); + } + + void clear() { + paged_.clear(); + idx_.clear(); + } + + Paged paged_; + Index idx_; +}; + +} // namespace cista \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/pair.h b/parallel/parallel_src/extern/cista/include/cista/containers/pair.h new file mode 100644 index 00000000..fdd8e188 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/pair.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "cista/decay.h" +#include "cista/reflection/comparable.h" + +namespace cista { + +template +struct pair { + CISTA_COMPARABLE() + using first_type = T1; + using second_type = T2; + auto cista_members() { return std::tie(first, second); } + T1 first{}; + T2 second{}; +}; + +template +pair(T1&&, T2&&) -> pair, decay_t>; + +namespace raw { +using cista::pair; +} // namespace raw + +namespace offset { +using cista::pair; +} // namespace offset + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/ptr.h b/parallel/parallel_src/extern/cista/include/cista/containers/ptr.h new file mode 100644 index 00000000..a07d1d8c --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/ptr.h @@ -0,0 +1,31 @@ +#pragma once + +#include "cista/containers/offset_ptr.h" + +namespace cista { + +namespace raw { + +template +using ptr = T*; + +} // namespace raw + +namespace offset { + +template +using ptr = cista::offset_ptr; + +} // namespace offset + +template +T* ptr_cast(raw::ptr const p) noexcept { + return p; +} + +template +T* ptr_cast(offset::ptr const p) noexcept { + return p.get(); +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/string.h b/parallel/parallel_src/extern/cista/include/cista/containers/string.h new file mode 100644 index 00000000..5f0350eb --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/string.h @@ -0,0 +1,617 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include "cista/containers/ptr.h" +#include "cista/exception.h" +#include "cista/type_traits.h" + +namespace cista { + +template +struct generic_string { + using CharT = typename std::remove_const_t>; + using msize_t = std::uint32_t; + using value_type = CharT; + + static msize_t mstrlen(CharT const* s) noexcept { + return static_cast(std::char_traits::length(s)); + } + + static constexpr struct owning_t { + } owning{}; + static constexpr struct non_owning_t { + } non_owning{}; + + constexpr generic_string() noexcept {} + ~generic_string() noexcept { reset(); } + + generic_string(std::basic_string_view s, owning_t const) { + set_owning(s); + } + generic_string(std::basic_string_view s, non_owning_t const) { + set_non_owning(s); + } + generic_string(std::basic_string const& s, owning_t const) { + set_owning(s); + } + generic_string(std::basic_string const& s, non_owning_t const) { + set_non_owning(s); + } + generic_string(CharT const* s, owning_t const) { set_owning(s, mstrlen(s)); } + generic_string(CharT const* s, non_owning_t const) { set_non_owning(s); } + generic_string(CharT const* s, msize_t const len, owning_t const) { + set_owning(s, len); + } + generic_string(CharT const* s, msize_t const len, non_owning_t const) { + set_non_owning(s, len); + } + + generic_string(generic_string&& o) { move_from(std::move(o)); } + generic_string(generic_string const& o) { copy_from(o); } + + generic_string& operator=(generic_string&& o) { + move_from(std::move(o)); + return *this; + } + generic_string& operator=(generic_string const& o) { + copy_from(o); + return *this; + } + + CharT* begin() noexcept { return data(); } + CharT* end() noexcept { return data() + size(); } + CharT const* begin() const noexcept { return data(); } + CharT const* end() const noexcept { return data() + size(); } + + friend CharT const* begin(generic_string const& s) { return s.begin(); } + friend CharT* begin(generic_string& s) { return s.begin(); } + friend CharT const* end(generic_string const& s) { return s.end(); } + friend CharT* end(generic_string& s) { return s.end(); } + + bool is_short() const noexcept { return s_.is_short_; } + + void reset() noexcept { + if (!h_.is_short_ && h_.ptr_ != nullptr && h_.self_allocated_) { + std::free(data()); + } + h_ = heap{}; + } + + void set_owning(std::basic_string const& s) { + set_owning(s.data(), static_cast(s.size())); + } + + void set_owning(std::basic_string_view s) { + set_owning(s.data(), static_cast(s.size())); + } + + void set_owning(CharT const* str) { set_owning(str, mstrlen(str)); } + + static constexpr msize_t short_length_limit = 15U / sizeof(CharT); + + void set_owning(CharT const* str, msize_t const len) { + reset(); + if (str == nullptr || len == 0U) { + return; + } + s_.is_short_ = (len <= short_length_limit); + if (s_.is_short_) { + std::memcpy(s_.s_, str, len * sizeof(CharT)); + for (auto i = len; i < short_length_limit; ++i) { + s_.s_[i] = 0; + } + } else { + h_.ptr_ = static_cast(std::malloc(len * sizeof(CharT))); + if (h_.ptr_ == nullptr) { + throw_exception(std::bad_alloc{}); + } + h_.size_ = len; + h_.self_allocated_ = true; + std::memcpy(data(), str, len * sizeof(CharT)); + } + } + + void set_non_owning(std::basic_string const& v) { + set_non_owning(v.data(), static_cast(v.size())); + } + + void set_non_owning(std::basic_string_view v) { + set_non_owning(v.data(), static_cast(v.size())); + } + + void set_non_owning(CharT const* str) { set_non_owning(str, mstrlen(str)); } + + void set_non_owning(CharT const* str, msize_t const len) { + reset(); + if (str == nullptr || len == 0U) { + return; + } + + if (len <= short_length_limit) { + return set_owning(str, len); + } + + h_.is_short_ = false; + h_.self_allocated_ = false; + h_.ptr_ = str; + h_.size_ = len; + } + + void move_from(generic_string&& s) noexcept { + if (&s == this) { + return; + } + reset(); + std::memcpy(static_cast(this), &s, sizeof(*this)); + if constexpr (std::is_pointer_v) { + std::memset(static_cast(&s), 0, sizeof(*this)); + } else { + if (!s.is_short()) { + h_.ptr_ = s.h_.ptr_; + s.h_.ptr_ = nullptr; + s.h_.size_ = 0U; + } + } + } + + void copy_from(generic_string const& s) { + if (&s == this) { + return; + } + reset(); + if (s.is_short()) { + std::memcpy(static_cast(this), &s, sizeof(s)); + } else if (s.h_.self_allocated_) { + set_owning(s.data(), s.size()); + } else { + set_non_owning(s.data(), s.size()); + } + } + + bool empty() const noexcept { return size() == 0U; } + std::basic_string_view view() const noexcept { + return {data(), size()}; + } + std::basic_string str() const { return {data(), size()}; } + + operator std::basic_string_view() const { return view(); } + + CharT& operator[](std::size_t const i) noexcept { return data()[i]; } + CharT const& operator[](std::size_t const i) const noexcept { + return data()[i]; + } + + friend std::basic_ostream& operator<<(std::basic_ostream& out, + generic_string const& s) { + return out << s.view(); + } + + friend bool operator==(generic_string const& a, + generic_string const& b) noexcept { + return a.view() == b.view(); + } + + friend bool operator!=(generic_string const& a, + generic_string const& b) noexcept { + return a.view() != b.view(); + } + + friend bool operator<(generic_string const& a, + generic_string const& b) noexcept { + return a.view() < b.view(); + } + + friend bool operator>(generic_string const& a, + generic_string const& b) noexcept { + return a.view() > b.view(); + } + + friend bool operator<=(generic_string const& a, + generic_string const& b) noexcept { + return a.view() <= b.view(); + } + + friend bool operator>=(generic_string const& a, + generic_string const& b) noexcept { + return a.view() >= b.view(); + } + + friend bool operator==(generic_string const& a, + std::basic_string_view b) noexcept { + return a.view() == b; + } + + friend bool operator!=(generic_string const& a, + std::basic_string_view b) noexcept { + return a.view() != b; + } + + friend bool operator<(generic_string const& a, + std::basic_string_view b) noexcept { + return a.view() < b; + } + + friend bool operator>(generic_string const& a, + std::basic_string_view b) noexcept { + return a.view() > b; + } + + friend bool operator<=(generic_string const& a, + std::basic_string_view b) noexcept { + return a.view() <= b; + } + + friend bool operator>=(generic_string const& a, + std::basic_string_view b) noexcept { + return a.view() >= b; + } + + friend bool operator==(std::basic_string_view a, + generic_string const& b) noexcept { + return a == b.view(); + } + + friend bool operator!=(std::basic_string_view a, + generic_string const& b) noexcept { + return a != b.view(); + } + + friend bool operator<(std::basic_string_view a, + generic_string const& b) noexcept { + return a < b.view(); + } + + friend bool operator>(std::basic_string_view a, + generic_string const& b) noexcept { + return a > b.view(); + } + + friend bool operator<=(std::basic_string_view a, + generic_string const& b) noexcept { + return a <= b.view(); + } + + friend bool operator>=(std::basic_string_view a, + generic_string const& b) noexcept { + return a >= b.view(); + } + + friend bool operator==(generic_string const& a, CharT const* b) noexcept { + return a.view() == std::basic_string_view{b}; + } + + friend bool operator!=(generic_string const& a, CharT const* b) noexcept { + return a.view() != std::basic_string_view{b}; + } + + friend bool operator<(generic_string const& a, CharT const* b) noexcept { + return a.view() < std::basic_string_view{b}; + } + + friend bool operator>(generic_string const& a, CharT const* b) noexcept { + return a.view() > std::basic_string_view{b}; + } + + friend bool operator<=(generic_string const& a, CharT const* b) noexcept { + return a.view() <= std::basic_string_view{b}; + } + + friend bool operator>=(generic_string const& a, CharT const* b) noexcept { + return a.view() >= std::basic_string_view{b}; + } + + friend bool operator==(CharT const* a, generic_string const& b) noexcept { + return std::basic_string_view{a} == b.view(); + } + + friend bool operator!=(CharT const* a, generic_string const& b) noexcept { + return std::basic_string_view{a} != b.view(); + } + + friend bool operator<(CharT const* a, generic_string const& b) noexcept { + return std::basic_string_view{a} < b.view(); + } + + friend bool operator>(CharT const* a, generic_string const& b) noexcept { + return std::basic_string_view{a} > b.view(); + } + + friend bool operator<=(CharT const* a, generic_string const& b) noexcept { + return std::basic_string_view{a} <= b.view(); + } + + friend bool operator>=(CharT const* a, generic_string const& b) noexcept { + return std::basic_string_view{a} >= b.view(); + } + + CharT const* internal_data() const noexcept { + if constexpr (std::is_pointer_v) { + return is_short() ? s_.s_ : h_.ptr_; + } else { + return is_short() ? s_.s_ : h_.ptr_.get(); + } + } + + CharT* data() noexcept { return const_cast(internal_data()); } + CharT const* data() const noexcept { return internal_data(); } + + msize_t size() const noexcept { + if (is_short()) { + auto const pos = + std::char_traits::find(s_.s_, short_length_limit, CharT(0)); + return (pos != nullptr) ? static_cast(pos - s_.s_) + : short_length_limit; + } + return h_.size_; + } + + generic_string& erase(msize_t const pos, msize_t const n) { + if (!is_short() && !h_.self_allocated_) { + set_owning(view()); + } + auto const size_before = size(); + std::memmove(data() + pos, data() + pos + n, + (size_before - (pos + n)) * sizeof(CharT)); + std::memset(data() + size_before - n, 0U, n * sizeof(CharT)); + if (!is_short()) { + h_.size_ = size_before - n; + } + return *this; + } + + constexpr bool starts_with(generic_string const& s) const noexcept { + return starts_with(s.data(), static_cast(s.size())); + } + constexpr bool starts_with( + std::basic_string_view const& sv) const noexcept { + return starts_with(sv.data(), static_cast(sv.size())); + } + constexpr bool starts_with(CharT const* s) const noexcept { + return starts_with(s, mstrlen(s)); + } + constexpr bool starts_with(CharT const* s, msize_t size_s) const noexcept { + if (size_s > size()) { + return false; + } + if (size_s == 0) { + return true; + } + if (empty()) { + return false; + } + return !std::memcmp(s, data(), size_s * sizeof(CharT)); + } + constexpr bool starts_with(CharT ch) const noexcept { + if (empty()) { + return false; + } + return data()[0] == ch; + } + + constexpr bool ends_with(generic_string const& s) const noexcept { + return ends_with(s.data(), static_cast(s.size())); + } + constexpr bool ends_with( + std::basic_string_view const& sv) const noexcept { + return ends_with(sv.data(), static_cast(sv.size())); + } + constexpr bool ends_with(CharT const* s) const noexcept { + return ends_with(s, mstrlen(s)); + } + constexpr bool ends_with(CharT const* s, msize_t size_s) const noexcept { + if (size_s > size()) { + return false; + } + if (size_s == 0) { + return true; + } + if (empty()) { + return false; + } + return !std::memcmp(s, data() + size() - size_s, size_s * sizeof(CharT)); + } + constexpr bool ends_with(CharT ch) const noexcept { + if (size() == 0) { + return false; + } + return data()[size() - 1] == ch; + } + + struct heap { + bool is_short_{false}; + bool self_allocated_{false}; + std::uint16_t __fill__{0}; + std::uint32_t size_{0}; + Ptr ptr_{nullptr}; + }; + + struct stack { + union { + bool is_short_{true}; + CharT __fill__; + }; + CharT s_[short_length_limit]{0}; + }; + + union { + heap h_{}; + stack s_; + }; +}; + +template +struct basic_string : public generic_string { + using base = generic_string; + using CharT = typename base::CharT; + + using base::base; + using base::operator std::basic_string_view; + + friend std::basic_ostream& operator<<(std::basic_ostream& out, + basic_string const& s) { + return out << s.view(); + } + + explicit operator std::basic_string() const { + return {base::data(), base::size()}; + } + + basic_string(std::basic_string_view s) : base{s, base::owning} {} + basic_string(std::basic_string const& s) : base{s, base::owning} {} + basic_string(CharT const* s) : base{s, base::owning} {} + basic_string(CharT const* s, typename base::msize_t const len) + : base{s, len, base::owning} {} + + basic_string(basic_string const& o) : base{o.view(), base::owning} {} + basic_string(basic_string&& o) { base::move_from(std::move(o)); } + + basic_string& operator=(basic_string const& o) { + if (&o == this) { + return *this; + } + base::set_owning(o.data(), o.size()); + return *this; + } + + basic_string& operator=(basic_string&& o) { + base::move_from(std::move(o)); + return *this; + } + + basic_string& operator=(CharT const* s) { + base::set_owning(s); + return *this; + } + basic_string& operator=(std::basic_string const& s) { + base::set_owning(s); + return *this; + } + basic_string& operator=(std::basic_string_view s) { + base::set_owning(s); + return *this; + } +}; + +template +struct basic_string_view : public generic_string { + using base = generic_string; + using CharT = typename base::CharT; + + using base::base; + using base::operator std::basic_string_view; + + friend std::basic_ostream& operator<<(std::basic_ostream& out, + basic_string_view const& s) { + return out << s.view(); + } + + basic_string_view(std::basic_string_view s) + : base{s, base::non_owning} {} + basic_string_view(std::basic_string const& s) + : base{s, base::non_owning} {} + basic_string_view(CharT const* s) : base{s, base::non_owning} {} + basic_string_view(CharT const* s, typename base::msize_t const len) + : base{s, len, base::non_owning} {} + + basic_string_view(basic_string_view const& o) { + base::set_non_owning(o.data(), o.size()); + } + basic_string_view(basic_string_view&& o) { + base::set_non_owning(o.data(), o.size()); + } + basic_string_view& operator=(basic_string_view const& o) { + base::set_non_owning(o.data(), o.size()); + return *this; + } + basic_string_view& operator=(basic_string_view&& o) { + base::set_non_owning(o.data(), o.size()); + return *this; + } + + basic_string_view& operator=(CharT const* s) { + base::set_non_owning(s); + return *this; + } + basic_string_view& operator=(std::basic_string_view s) { + base::set_non_owning(s); + return *this; + } + basic_string_view& operator=(std::basic_string const& s) { + base::set_non_owning(s); + return *this; + } +}; + +template +struct is_string_helper> : std::true_type {}; + +template +struct is_string_helper> : std::true_type {}; + +template +struct is_string_helper> : std::true_type {}; + +namespace raw { +using generic_string = generic_string>; +using generic_u16string = cista::generic_string>; +using generic_u32string = cista::generic_string>; + +using string = basic_string>; +using u16string = basic_string>; +using u32string = basic_string>; + +using string_view = basic_string_view>; +using u16string_view = basic_string_view>; +using u32string_view = basic_string_view>; + +#ifdef __cpp_char8_t +using generic_u8string = cista::generic_string>; +using u8string = basic_string>; +using u8string_view = basic_string_view>; +#endif +} // namespace raw + +namespace offset { +using generic_string = generic_string>; +using generic_u16string = cista::generic_string>; +using generic_u32string = cista::generic_string>; + +using string = basic_string>; +using u16string = basic_string>; +using u32string = basic_string>; + +using string_view = basic_string_view>; +using u16string_view = basic_string_view>; +using u32string_view = basic_string_view>; + +#ifdef __cpp_char8_t +using generic_u8string = cista::generic_string>; +using u8string = basic_string>; +using u8string_view = basic_string_view>; +#endif +} // namespace offset + +template +auto format_as(cista::basic_string const& s) { + return s.view(); +} + +} // namespace cista + +#if __has_include("fmt/ranges.h") + +#include "fmt/ranges.h" + +namespace fmt { +template +struct range_format_kind, Char, void> + : std::false_type {}; +} // namespace fmt + +#endif \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/tuple.h b/parallel/parallel_src/extern/cista/include/cista/containers/tuple.h new file mode 100644 index 00000000..12ee6db6 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/tuple.h @@ -0,0 +1,308 @@ +#pragma once + +#include +#include +#include + +#include "cista/decay.h" + +namespace cista { + +template +struct tuple; + +template +struct tuple_element; + +template +struct tuple_element> : tuple_element> { +}; + +template +struct tuple_element<0U, tuple> { + using type = T; +}; + +template +struct tuple_element : tuple_element {}; + +template +struct tuple_element<0U, T, Ts...> { + using type = T; +}; + +template +using tuple_element_t = typename tuple_element::type; + +template +constexpr std::size_t max_align_of() { + if constexpr (sizeof...(Ts) == 0U) { + return alignof(T); + } else { + return std::max(alignof(T), max_align_of()); + } +} + +template +constexpr std::size_t get_offset(std::size_t const current_idx, + std::size_t current_offset = 0U) { + if (auto const misalign = current_offset % alignof(T); misalign != 0U) { + current_offset += (alignof(T) - misalign) % alignof(T); + } + + if (current_idx == 0U) { + return current_offset; + } + + current_offset += sizeof(T); + + if constexpr (sizeof...(Ts) == 0U) { + return current_idx == 1 ? current_offset + sizeof(T) : current_offset; + } else { + return get_offset(current_idx - 1U, current_offset); + } +} + +template +constexpr std::size_t get_total_size() { + return get_offset(sizeof...(Ts) + 1U); +} + +template +constexpr auto get_arg(T&& arg, Ts&&... args) { + if constexpr (I == 0U) { + return std::forward(arg); + } else { + return get_arg(std::forward(args)...); + } +} + +template +auto& get(tuple& t) { + using return_t = tuple_element_t; + return *std::launder(reinterpret_cast(t.template get_ptr())); +} + +template +auto const& get(tuple const& t) { + using return_t = tuple_element_t; + return *std::launder( + reinterpret_cast(t.template get_ptr())); +} + +template +auto& get(tuple&& t) { + using return_t = tuple_element_t; + return *std::launder(reinterpret_cast(t.template get_ptr())); +} + +template +struct tuple { + template + using seq_t = std::index_sequence; + static constexpr auto Indices = std::make_index_sequence{}; + + tuple() { default_construct(Indices); } + + tuple(tuple const& o) { copy_construct(o, Indices); } + + tuple& operator=(tuple const& o) { + if (&o != this) { + copy_assign(o, Indices); + } + + return *this; + } + + tuple(tuple&& o) noexcept { move_construct(o, Indices); } + + tuple& operator=(tuple&& o) noexcept { + move_assign(o, Indices); + return *this; + } + + tuple(Ts&&... args) { // NOLINT + move_construct_from_args(Indices, std::forward(args)...); + } + + ~tuple() { destruct(Indices); } + + template + constexpr void move_construct_from_args(seq_t, Ts&&... args) { + (new (get_ptr()) + tuple_element_t(get_arg(std::forward(args)...)), + ...); + } + + template + constexpr void default_construct(seq_t) { + ((new (get_ptr()) tuple_element_t{}), ...); + } + + template + constexpr void copy_construct(From const& from, seq_t) { + (new (get_ptr()) tuple_element_t(get(from)), ...); + } + + template + constexpr void copy_assign(From const& from, seq_t) { + ((get(*this) = get(from)), ...); + } + + template + constexpr void move_construct(From&& from, seq_t) { + (new (get_ptr()) tuple_element_t(std::move(get(from))), + ...); + } + + template + constexpr void move_assign(From&& from, seq_t) { + ((get(*this) = std::move(get(from))), ...); + } + + template + constexpr void destruct(T& t) { + static_assert(!std::is_array_v); + t.~T(); + } + + template + constexpr void destruct(seq_t) { + (destruct(get(*this)), ...); + } + + template + constexpr auto get_ptr() { + return reinterpret_cast(&mem_) + get_offset(I); + } + + template + constexpr auto get_ptr() const { + return reinterpret_cast(&mem_) + get_offset(I); + } + + std::aligned_storage_t(), max_align_of()> mem_; +}; + +template +tuple(Head&& first, Tail&&... tail) -> tuple; + +template +struct is_tuple : std::false_type {}; + +template +struct is_tuple> : std::true_type {}; + +template +inline constexpr auto is_tuple_v = is_tuple::value; + +template +struct tuple_size; + +template +struct tuple_size> + : public std::integral_constant {}; + +template +constexpr std::size_t tuple_size_v = tuple_size>::value; + +template +constexpr decltype(auto) apply_impl(std::index_sequence, F&& f, + Tuple&& t) { + return std::invoke(std::forward(f), get(std::forward(t))...); +} + +template >>> +constexpr decltype(auto) apply(F&& f, Tuple&& t) { + return apply_impl(std::make_index_sequence>{}, + std::forward(f), std::forward(t)); +} + +template +constexpr decltype(auto) apply_impl(std::index_sequence, F&& f, + Tuple const& a, Tuple const& b) { + return (std::invoke(std::forward(f), get(std::forward(a)), + get(std::forward(b))), + ...); +} + +template +constexpr decltype(auto) apply(F&& f, Tuple const& a, Tuple const& b) { + return apply_impl( + std::make_index_sequence>>{}, + std::forward(f), std::forward(a), std::forward(b)); +} + +template +constexpr decltype(auto) eq(std::index_sequence, T1 const& a, + T2 const& b) { + return ((get(a) == get(b)) && ...); +} + +template +std::enable_if_t> && is_tuple_v>, bool> +operator==(T1&& a, T2&& b) { + return eq( + std::make_index_sequence>>{}, a, + b); +} + +template +std::enable_if_t>, bool> operator!=(Tuple const& a, + Tuple const& b) { + return !(a == b); +} + +template +bool lt(Tuple const& a, Tuple const& b) { + if constexpr (Index == tuple_size_v) { + return false; + } else { + if (get(a) < get(b)) { + return true; + } + if (get(b) < get(a)) { + return false; + } + return lt(a, b); + } +} + +template +std::enable_if_t>, bool> operator<(Tuple const& a, + Tuple const& b) { + return lt(a, b); +} + +template +std::enable_if_t>, bool> operator<=(Tuple const& a, + Tuple const& b) { + return !(b < a); +} + +template +std::enable_if_t>, bool> operator>(Tuple const& a, + Tuple const& b) { + return b < a; +} + +template +std::enable_if_t>, bool> operator>=(Tuple const& a, + Tuple const& b) { + return !(a < b); +} + +} // namespace cista + +namespace std { + +template +struct tuple_size> + : cista::tuple_size> {}; + +template +struct tuple_element> + : cista::tuple_element> {}; + +} // namespace std diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/unique_ptr.h b/parallel/parallel_src/extern/cista/include/cista/containers/unique_ptr.h new file mode 100644 index 00000000..c490a6b7 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/unique_ptr.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include + +#include + +#include "cista/containers/ptr.h" + +namespace cista { + +template +struct basic_unique_ptr { + using value_t = T; + + basic_unique_ptr() = default; + + explicit basic_unique_ptr(T* el, bool take_ownership = true) + : el_{el}, self_allocated_{take_ownership} {} + + basic_unique_ptr(basic_unique_ptr const&) = delete; + basic_unique_ptr& operator=(basic_unique_ptr const&) = delete; + + basic_unique_ptr(basic_unique_ptr&& o) noexcept + : el_{o.el_}, self_allocated_{o.self_allocated_} { + o.el_ = nullptr; + o.self_allocated_ = false; + } + + basic_unique_ptr& operator=(basic_unique_ptr&& o) noexcept { + reset(); + el_ = o.el_; + self_allocated_ = o.self_allocated_; + o.el_ = nullptr; + o.self_allocated_ = false; + return *this; + } + + basic_unique_ptr(std::nullptr_t) noexcept {} + basic_unique_ptr& operator=(std::nullptr_t) { + reset(); + return *this; + } + + ~basic_unique_ptr() { reset(); } + + void reset() { + if (self_allocated_ && el_ != nullptr) { + delete el_; + el_ = nullptr; + self_allocated_ = false; + } + } + + explicit operator bool() const noexcept { return el_ != nullptr; } + + friend bool operator==(basic_unique_ptr const& a, std::nullptr_t) noexcept { + return a.el_ == nullptr; + } + friend bool operator==(std::nullptr_t, basic_unique_ptr const& a) noexcept { + return a.el_ == nullptr; + } + friend bool operator!=(basic_unique_ptr const& a, std::nullptr_t) noexcept { + return a.el_ != nullptr; + } + friend bool operator!=(std::nullptr_t, basic_unique_ptr const& a) noexcept { + return a.el_ != nullptr; + } + + T* get() const noexcept { return el_; } + T* operator->() noexcept { return el_; } + T& operator*() noexcept { return *el_; } + T const& operator*() const noexcept { return *el_; } + T const* operator->() const noexcept { return el_; } + + Ptr el_{nullptr}; + bool self_allocated_{false}; + std::uint8_t __fill_0__{0U}; + std::uint16_t __fill_1__{0U}; + std::uint32_t __fill_2__{0U}; +}; + +namespace raw { + +template +using unique_ptr = basic_unique_ptr>; + +template +unique_ptr make_unique(Args&&... args) { + return unique_ptr{new T{std::forward(args)...}, true}; +} + +} // namespace raw + +namespace offset { + +template +using unique_ptr = basic_unique_ptr>; + +template +unique_ptr make_unique(Args&&... args) { + return unique_ptr{new T{std::forward(args)...}, true}; +} + +} // namespace offset + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/variant.h b/parallel/parallel_src/extern/cista/include/cista/containers/variant.h new file mode 100755 index 00000000..b035975d --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/variant.h @@ -0,0 +1,448 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "cista/exception.h" +#include "cista/hashing.h" + +namespace cista { + +template +struct bytes_to_integer_type {}; + +template +struct bytes_to_integer_type> { + using type = std::uint8_t; +}; + +template +struct bytes_to_integer_type> { + using type = std::uint16_t; +}; + +template +constexpr std::size_t bytes() noexcept { + return (sizeof...(T) > std::numeric_limits::max()) ? 2U : 1U; +} + +template +using variant_index_t = typename bytes_to_integer_type()>::type; + +constexpr auto const TYPE_NOT_FOUND = std::numeric_limits::max(); + +template +constexpr std::size_t index_of_type() noexcept { + constexpr std::array matches = { + {std::is_same, std::decay_t>::value...}}; + for (std::size_t i = 0U; i < sizeof...(T); ++i) { + if (matches[i]) { + return i; + } + } + return TYPE_NOT_FOUND; +} + +template +struct type_at_index; + +template +struct type_at_index<0, First, Rest...> { + using type = First; +}; + +template +struct type_at_index { + using type = typename type_at_index::type; +}; + +template +using type_at_index_t = typename type_at_index::type; + +template +struct variant { + using index_t = variant_index_t; + static constexpr auto NO_VALUE = std::numeric_limits::max(); + + constexpr variant() noexcept : idx_{NO_VALUE} {} + + template , T...>() != TYPE_NOT_FOUND>> + constexpr variant(Arg&& arg) + : idx_{static_cast(index_of_type())} { +#if defined(CISTA_ZERO_OUT) + std::memset(&storage_, 0, sizeof(storage_)); +#endif + using Type = std::decay_t; + new (&storage_) Type(std::forward(arg)); + } + + variant(variant const& o) : idx_{o.idx_} { + o.apply([this](auto&& el) { + using Type = std::decay_t; + new (&storage_) Type(std::forward(el)); + }); + } + variant(variant&& o) : idx_{o.idx_} { + o.apply([this](auto&& el) { + using Type = std::decay_t; + new (&storage_) Type(std::move(el)); + }); + } + + variant& operator=(variant const& o) { + return o.apply([&](auto&& el) -> variant& { return *this = el; }); + } + variant& operator=(variant&& o) { + return o.apply( + [&](auto&& el) -> variant& { return *this = std::move(el); }); + } + + template , T...>() != TYPE_NOT_FOUND>> + variant& operator=(Arg&& arg) { + if (index_of_type() == idx_) { + apply([&](auto&& el) { + if constexpr (std::is_same_v, Arg>) { + el = std::move(arg); + } + }); + return *this; + } + destruct(); + idx_ = static_cast(index_of_type()); + new (&storage_) std::decay_t{std::forward(arg)}; + return *this; + } +#if _MSVC_LANG >= 202002L || __cplusplus >= 202002L + constexpr +#endif + ~variant() { + destruct(); + } + + constexpr bool valid() const noexcept { return index() != NO_VALUE; } + + constexpr operator bool() const noexcept { return valid(); } + + friend bool operator==(variant const& a, variant const& b) noexcept { + return a.idx_ == b.idx_ + ? apply([](auto&& u, auto&& v) -> bool { return u == v; }, + a.idx_, a, b) + : false; + } + + friend bool operator!=(variant const& a, variant const& b) noexcept { + return a.idx_ == b.idx_ + ? apply([](auto&& u, auto&& v) -> bool { return u != v; }, + a.idx_, a, b) + : true; + } + + friend bool operator<(variant const& a, variant const& b) noexcept { + return a.idx_ == b.idx_ + ? apply([](auto&& u, auto&& v) -> bool { return u < v; }, a.idx_, + a, b) + : a.idx_ < b.idx_; + } + + friend bool operator>(variant const& a, variant const& b) noexcept { + return a.idx_ == b.idx_ + ? apply([](auto&& u, auto&& v) -> bool { return u > v; }, a.idx_, + a, b) + : a.idx_ > b.idx_; + } + + friend bool operator<=(variant const& a, variant const& b) noexcept { + return a.idx_ == b.idx_ + ? apply([](auto&& u, auto&& v) -> bool { return u <= v; }, + a.idx_, a, b) + : a.idx_ <= b.idx_; + } + + friend bool operator>=(variant const& a, variant const& b) noexcept { + return a.idx_ == b.idx_ + ? apply([](auto&& u, auto&& v) -> bool { return u >= v; }, + a.idx_, a, b) + : a.idx_ >= b.idx_; + } + + template + Arg& emplace(CtorArgs&&... ctor_args) { + static_assert(index_of_type() != TYPE_NOT_FOUND); + destruct(); + idx_ = static_cast(index_of_type()); + return *(new (&storage_) + std::decay_t{std::forward(ctor_args)...}); + } + + template + type_at_index_t& emplace(CtorArgs&&... ctor_args) { + static_assert(I < sizeof...(T)); + destruct(); + idx_ = I; + return *(new (&storage_) std::decay_t>{ + std::forward(ctor_args)...}); + } + + constexpr std::size_t index() const noexcept { return idx_; } + + void swap(variant& o) { + if (idx_ == o.idx_) { + apply( + [](auto&& a, auto&& b) { + using std::swap; + swap(a, b); + }, + idx_, *this, o); + } else { + variant tmp{std::move(o)}; + o = std::move(*this); + *this = std::move(tmp); + } + } + + constexpr void destruct() { + if (idx_ != NO_VALUE) { + apply([](auto&& el) { + using el_type = std::decay_t; + el.~el_type(); + }); + } + } + + template + auto apply(F&& f) -> decltype(f(std::declval&>())) { + if (idx_ == NO_VALUE) { + throw_exception(std::runtime_error{"variant::apply: no value"}); + } + return apply(std::forward(f), idx_, *this); + } + + template + auto apply(F&& f) const + -> decltype(f(std::declval>())) { + if (idx_ == NO_VALUE) { + throw_exception(std::runtime_error{"variant::apply: no value"}); + } + return apply(std::forward(f), idx_, *this); + } + +#ifdef _MSC_VER + static __declspec(noreturn) void noret() {} +#endif + + template + static auto apply(F&& f, index_t const idx, Vs&&... vs) + -> decltype(f((vs, std::declval&>())...)) { + switch (idx) { + case B + 0U: + if constexpr (B + 0U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 1U: + if constexpr (B + 1U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 2U: + if constexpr (B + 2U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 3U: + if constexpr (B + 3U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 4U: + if constexpr (B + 4U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 5U: + if constexpr (B + 5U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 6U: + if constexpr (B + 6U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 7U: + if constexpr (B + 7U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 8U: + if constexpr (B + 8U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 9U: + if constexpr (B + 9U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 10U: + if constexpr (B + 10U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 11U: + if constexpr (B + 11U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 12U: + if constexpr (B + 12U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 13U: + if constexpr (B + 13U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 14U: + if constexpr (B + 14U < sizeof...(T)) { + return f(vs.template as>()...); + } + [[fallthrough]]; + case B + 15U: + if constexpr (B + 15U < sizeof...(T)) { + return f(vs.template as>()...); + } + break; + } + + if constexpr (B + 15U < sizeof...(T)) { + return apply(std::forward(f), std::forward(vs)...); + } + +#ifndef _MSC_VER + __builtin_unreachable(); +#else + noret(); +#endif + } + + template + AsType& as() noexcept { + return *reinterpret_cast(&storage_); + } + + template + AsType const& as() const noexcept { + return *reinterpret_cast(&storage_); + } + + hash_t hash() const noexcept { + return apply([&](auto&& val) { + auto const idx = index(); + auto h = hashing{}(idx, BASE_HASH); + h = hashing{}(val, h); + return h; + }); + } + + index_t idx_{NO_VALUE}; + std::aligned_union_t<0, T...> storage_{}; +}; + +template +bool holds_alternative(variant const& v) noexcept { + return v.idx_ == index_of_type, Ts...>(); +} + +template +constexpr cista::type_at_index_t const& get( + cista::variant const& v) noexcept { + return v.template as>(); +} + +template +constexpr cista::type_at_index_t& get( + cista::variant& v) noexcept { + return v.template as>(); +} + +template +constexpr T const& get(cista::variant const& v) noexcept { + static_assert(cista::index_of_type() != cista::TYPE_NOT_FOUND); + return v.template as(); +} +template +constexpr T& get(cista::variant& v) noexcept { + static_assert(cista::index_of_type() != cista::TYPE_NOT_FOUND); + return v.template as(); +} + +template +constexpr std::add_pointer_t get_if(cista::variant& v) noexcept { + static_assert(cista::index_of_type() != cista::TYPE_NOT_FOUND); + return v.idx_ == cista::index_of_type() ? &v.template as() + : nullptr; +} + +template +constexpr std::add_pointer_t const> get_if( + cista::variant const& v) noexcept { + static_assert(I < sizeof...(Ts)); + return v.idx_ == I ? &v.template as>() + : nullptr; +} + +template +constexpr std::add_pointer_t> get_if( + cista::variant& v) noexcept { + static_assert(I < sizeof...(Ts)); + return v.idx_ == I ? &v.template as>() + : nullptr; +} + +template +constexpr std::add_pointer_t get_if( + cista::variant const& v) noexcept { + static_assert(cista::index_of_type() != cista::TYPE_NOT_FOUND); + return v.idx_ == cista::index_of_type() ? &v.template as() + : nullptr; +} + +template +struct variant_size; + +template +struct variant_size> + : std::integral_constant {}; + +template +constexpr std::size_t variant_size_v = variant_size::value; + +namespace raw { +using cista::variant; +} // namespace raw + +namespace offset { +using cista::variant; +} // namespace offset + +} // namespace cista + +namespace std { + +template +constexpr auto visit(Visitor&& vis, cista::variant&& v) { + v.apply(vis); +} + +using cista::get; + +} // namespace std diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/vector.h b/parallel/parallel_src/extern/cista/include/cista/containers/vector.h new file mode 100644 index 00000000..6722baaf --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/vector.h @@ -0,0 +1,558 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cista/allocator.h" +#include "cista/containers/ptr.h" +#include "cista/exception.h" +#include "cista/is_iterable.h" +#include "cista/next_power_of_2.h" +#include "cista/strong.h" +#include "cista/unused_param.h" +#include "cista/verify.h" + +namespace cista { + +template typename Ptr, + bool IndexPointers = false, typename TemplateSizeType = std::uint32_t, + class Allocator = allocator> +struct basic_vector { + using size_type = base_t; + using difference_type = std::ptrdiff_t; + using access_type = TemplateSizeType; + using reference = T&; + using const_reference = T const&; + using pointer = Ptr; + using const_pointer = Ptr; + using value_type = T; + using iterator = T*; + using const_iterator = T const*; + using allocator_type = Allocator; + + explicit basic_vector(allocator_type const&) noexcept {} + basic_vector() noexcept = default; + + explicit basic_vector(size_type const size, T init = T{}, + Allocator const& alloc = Allocator{}) { + CISTA_UNUSED_PARAM(alloc) + resize(size, std::move(init)); + } + + basic_vector(std::initializer_list init, + Allocator const& alloc = Allocator{}) { + CISTA_UNUSED_PARAM(alloc) + set(init.begin(), init.end()); + } + + template + basic_vector(It begin_it, It end_it) { + set(begin_it, end_it); + } + + basic_vector(basic_vector&& o, Allocator const& alloc = Allocator{}) noexcept + : el_(o.el_), + used_size_(o.used_size_), + allocated_size_(o.allocated_size_), + self_allocated_(o.self_allocated_) { + CISTA_UNUSED_PARAM(alloc) + o.reset(); + } + + basic_vector(basic_vector const& o, Allocator const& alloc = Allocator{}) { + CISTA_UNUSED_PARAM(alloc) + set(o); + } + + basic_vector& operator=(basic_vector&& arr) noexcept { + deallocate(); + + el_ = arr.el_; + used_size_ = arr.used_size_; + self_allocated_ = arr.self_allocated_; + allocated_size_ = arr.allocated_size_; + + arr.reset(); + return *this; + } + + basic_vector& operator=(basic_vector const& arr) { + if (&arr != this) { + set(arr); + } + return *this; + } + + ~basic_vector() { deallocate(); } + + void deallocate() { + if (!self_allocated_ || el_ == nullptr) { + return; + } + + for (auto& el : *this) { + el.~T(); + } + + std::free(el_); // NOLINT + reset(); + } + + allocator_type get_allocator() const noexcept { return {}; } + + T const* data() const noexcept { return begin(); } + T* data() noexcept { return begin(); } + T const* begin() const noexcept { return el_; } + T const* end() const noexcept { return el_ + used_size_; } // NOLINT + T const* cbegin() const noexcept { return el_; } + T const* cend() const noexcept { return el_ + used_size_; } // NOLINT + T* begin() noexcept { return el_; } + T* end() noexcept { return el_ + used_size_; } // NOLINT + + std::reverse_iterator rbegin() const { + return std::reverse_iterator(el_ + size()); // NOLINT + } + std::reverse_iterator rend() const { + return std::reverse_iterator(el_); + } + std::reverse_iterator rbegin() { + return std::reverse_iterator(el_ + size()); // NOLINT + } + std::reverse_iterator rend() { return std::reverse_iterator(el_); } + + friend T const* begin(basic_vector const& a) noexcept { return a.begin(); } + friend T const* end(basic_vector const& a) noexcept { return a.end(); } + + friend T* begin(basic_vector& a) noexcept { return a.begin(); } + friend T* end(basic_vector& a) noexcept { return a.end(); } + + T const& operator[](access_type const index) const noexcept { + assert(el_ != nullptr && index < used_size_); + return el_[to_idx(index)]; + } + T& operator[](access_type const index) noexcept { + assert(el_ != nullptr && index < used_size_); + return el_[to_idx(index)]; + } + + T& at(access_type const index) { + if (index >= used_size_) { + throw_exception(std::out_of_range{"vector::at(): invalid index"}); + } + return (*this)[index]; + } + + T const& at(access_type const index) const { + return const_cast(this)->at(index); + } + + T const& back() const noexcept { return ptr_cast(el_)[used_size_ - 1]; } + T& back() noexcept { return ptr_cast(el_)[used_size_ - 1]; } + + T& front() noexcept { return ptr_cast(el_)[0]; } + T const& front() const noexcept { return ptr_cast(el_)[0]; } + + size_type size() const noexcept { return used_size_; } + bool empty() const noexcept { return size() == 0U; } + + template + void set(It begin_it, It end_it) { + auto const range_size = std::distance(begin_it, end_it); + verify( + range_size >= 0 && range_size <= std::numeric_limits::max(), + "cista::vector::set: invalid range"); + + reserve(static_cast(range_size)); + + auto copy_source = begin_it; + auto copy_target = el_; + for (; copy_source != end_it; ++copy_source, ++copy_target) { + new (copy_target) T{std::forward(*copy_source)}; + } + + used_size_ = static_cast(range_size); + } + + void set(basic_vector const& arr) { + if constexpr (std::is_trivially_copyable_v) { + if (arr.used_size_ != 0U) { + reserve(arr.used_size_); + std::memcpy(data(), arr.data(), arr.used_size_ * sizeof(T)); + } + used_size_ = arr.used_size_; + } else { + set(std::begin(arr), std::end(arr)); + } + } + + friend std::ostream& operator<<(std::ostream& out, basic_vector const& v) { + out << "[\n "; + auto first = true; + for (auto const& e : v) { + if (!first) { + out << ",\n "; + } + out << e; + first = false; + } + return out << "\n]"; + } + + template + T* insert(T* it, Arg&& el) { + auto const old_offset = std::distance(begin(), it); + auto const old_size = used_size_; + + reserve(used_size_ + 1); + new (el_ + used_size_) T{std::forward(el)}; + ++used_size_; + + return std::rotate(begin() + old_offset, begin() + old_size, end()); + } + + template + T* insert(T* pos, InputIt first, InputIt last, std::input_iterator_tag) { + auto const old_offset = std::distance(begin(), pos); + auto const old_size = used_size_; + + for (; !(first == last); ++first) { + reserve(used_size_ + 1); + new (el_ + used_size_) T{std::forward(*first)}; + ++used_size_; + } + + return std::rotate(begin() + old_offset, begin() + old_size, end()); + } + + template + T* insert(T* pos, FwdIt first, FwdIt last, std::forward_iterator_tag) { + if (empty()) { + set(first, last); + return begin(); + } + + auto const pos_idx = pos - begin(); + auto const new_count = static_cast(std::distance(first, last)); + reserve(used_size_ + new_count); + pos = begin() + pos_idx; + + for (auto src_last = end() - 1, dest_last = end() + new_count - 1; + !(src_last == pos - 1); --src_last, --dest_last) { + if (dest_last >= end()) { + new (dest_last) T(std::move(*src_last)); + } else { + *dest_last = std::move(*src_last); + } + } + + for (auto insert_ptr = pos; !(first == last); ++first, ++insert_ptr) { + if (insert_ptr >= end()) { + new (insert_ptr) T(std::forward(*first)); + } else { + *insert_ptr = std::forward(*first); + } + } + + used_size_ += new_count; + + return pos; + } + + template + T* insert(T* pos, It first, It last) { + return insert(pos, first, last, + typename std::iterator_traits::iterator_category()); + } + + void push_back(T const& el) { + reserve(used_size_ + 1U); + new (el_ + used_size_) T(el); + ++used_size_; + } + + template + T& emplace_back(Args&&... el) { + reserve(used_size_ + 1U); + new (el_ + used_size_) T{std::forward(el)...}; + T* ptr = el_ + used_size_; + ++used_size_; + return *ptr; + } + + void resize(size_type const size, T init = T{}) { + reserve(size); + for (auto i = used_size_; i < size; ++i) { + new (el_ + i) T{init}; + } + used_size_ = size; + } + + void pop_back() noexcept(noexcept(std::declval().~T())) { + --used_size_; + el_[used_size_].~T(); + } + + void clear() { + for (auto& el : *this) { + el.~T(); + } + used_size_ = 0; + } + + void reserve(size_type new_size) { + new_size = std::max(allocated_size_, new_size); + + if (allocated_size_ >= new_size) { + return; + } + + auto next_size = next_power_of_two(new_size); + auto num_bytes = static_cast(next_size) * sizeof(T); + auto mem_buf = static_cast(std::malloc(num_bytes)); // NOLINT + if (mem_buf == nullptr) { + throw_exception(std::bad_alloc()); + } + + if (size() != 0) { + try { + auto move_target = mem_buf; + for (auto& el : *this) { + new (move_target++) T(std::move(el)); + } + + for (auto& el : *this) { + el.~T(); + } + } catch (...) { + assert(0); + } + } + + auto free_me = el_; + el_ = mem_buf; + if (self_allocated_) { + std::free(free_me); // NOLINT + } + + self_allocated_ = true; + allocated_size_ = next_size; + } + + T* erase(T* pos) { + auto const r = pos; + T* last = end() - 1; + while (pos < last) { + std::swap(*pos, *(pos + 1)); + pos = pos + 1; + } + pos->~T(); + --used_size_; + return r; + } + + T* erase(T* first, T* last) { + if (first != last) { + auto const new_end = std::move(last, end(), first); + for (auto it = new_end; it != end(); ++it) { + it->~T(); + } + used_size_ -= static_cast(std::distance(new_end, end())); + } + return end(); + } + + bool contains(T const* el) const noexcept { + return el >= begin() && el < end(); + } + + std::size_t index_of(T const* el) const noexcept { + assert(contains(el)); + return std::distance(begin(), el); + } + + friend bool operator==(basic_vector const& a, + basic_vector const& b) noexcept { + return std::equal(a.begin(), a.end(), b.begin(), b.end()); + } + friend bool operator!=(basic_vector const& a, + basic_vector const& b) noexcept { + return !(a == b); + } + friend bool operator<(basic_vector const& a, basic_vector const& b) { + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + friend bool operator>(basic_vector const& a, basic_vector const& b) noexcept { + return b < a; + } + friend bool operator<=(basic_vector const& a, + basic_vector const& b) noexcept { + return !(a > b); + } + friend bool operator>=(basic_vector const& a, + basic_vector const& b) noexcept { + return !(a < b); + } + + void reset() noexcept { + el_ = nullptr; + used_size_ = {}; + allocated_size_ = {}; + self_allocated_ = false; + } + + Ptr el_{nullptr}; + size_type used_size_{0U}; + size_type allocated_size_{0U}; + bool self_allocated_{false}; + std::uint8_t __fill_0__{0U}; + std::uint16_t __fill_1__{0U}; + std::uint32_t __fill_2__{0U}; +}; + +namespace raw { + +template +using vector = basic_vector; + +template +using indexed_vector = basic_vector; + +template +using vector_map = basic_vector; + +template +auto to_vec(It s, It e, UnaryOperation&& op) + -> vector> { + vector> v; + v.reserve(static_cast(std::distance(s, e))); + std::transform(s, e, std::back_inserter(v), op); + return v; +} + +template +auto to_vec(Container const& c, UnaryOperation&& op) + -> vector { + vector v; + v.reserve(static_cast( + std::distance(std::begin(c), std::end(c)))); + std::transform(std::begin(c), std::end(c), std::back_inserter(v), op); + return v; +} + +template +auto to_vec(Container const& c) -> vector> { + vector> v; + v.reserve(static_cast( + std::distance(std::begin(c), std::end(c)))); + std::copy(std::begin(c), std::end(c), std::back_inserter(v)); + return v; +} +template +auto to_indexed_vec(It s, It e, UnaryOperation&& op) + -> indexed_vector> { + indexed_vector> v; + v.reserve(static_cast(std::distance(s, e))); + std::transform(s, e, std::back_inserter(v), op); + return v; +} + +template +auto to_indexed_vec(Container const& c, UnaryOperation&& op) + -> indexed_vector> { + indexed_vector> v; + v.reserve(static_cast( + std::distance(std::begin(c), std::end(c)))); + std::transform(std::begin(c), std::end(c), std::back_inserter(v), op); + return v; +} + +template +auto to_indexed_vec(Container const& c) + -> indexed_vector> { + indexed_vector> v; + v.reserve(static_cast( + std::distance(std::begin(c), std::end(c)))); + std::copy(std::begin(c), std::end(c), std::back_inserter(v)); + return v; +} + +} // namespace raw + +namespace offset { + +template +using vector = basic_vector; + +template +using indexed_vector = basic_vector; + +template +using vector_map = basic_vector; + +template +auto to_vec(It s, It e, UnaryOperation&& op) + -> vector> { + vector> v; + v.reserve(static_cast(std::distance(s, e))); + std::transform(s, e, std::back_inserter(v), op); + return v; +} + +template +auto to_vec(Container&& c, UnaryOperation&& op) + -> vector { + vector v; + v.reserve(static_cast(c.size())); + std::transform(std::begin(c), std::end(c), std::back_inserter(v), op); + return v; +} + +template +auto to_vec(Container&& c) -> vector> { + vector> v; + v.reserve(static_cast( + std::distance(std::begin(c), std::end(c)))); + std::copy(std::begin(c), std::end(c), std::back_inserter(v)); + return v; +} +template +auto to_indexed_vec(It s, It e, UnaryOperation&& op) + -> indexed_vector> { + indexed_vector> v; + v.reserve(static_cast(std::distance(s, e))); + std::transform(s, e, std::back_inserter(v), op); + return v; +} + +template +auto to_indexed_vec(Container const& c, UnaryOperation&& op) + -> indexed_vector> { + indexed_vector> v; + v.reserve(static_cast( + std::distance(std::begin(c), std::end(c)))); + std::transform(std::begin(c), std::end(c), std::back_inserter(v), op); + return v; +} + +template +auto to_indexed_vec(Container const& c) + -> indexed_vector> { + indexed_vector> v; + v.reserve(static_cast( + std::distance(std::begin(c), std::end(c)))); + std::copy(std::begin(c), std::end(c), std::back_inserter(v)); + return v; +} + +} // namespace offset + +#undef CISTA_TO_VEC + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/containers/vecvec.h b/parallel/parallel_src/extern/cista/include/cista/containers/vecvec.h new file mode 100644 index 00000000..dc4a9b80 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/containers/vecvec.h @@ -0,0 +1,396 @@ +#pragma once + +#include +#include +#include + +#include "cista/containers/vector.h" +#include "cista/verify.h" + +namespace cista { + +template +struct basic_vecvec { + using data_value_type = typename DataVec::value_type; + using index_value_type = typename IndexVec::value_type; + + struct bucket final { + using value_type = data_value_type; + using iterator = typename DataVec::iterator; + using const_iterator = typename DataVec::iterator; + + using iterator_category = std::random_access_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = std::add_pointer_t; + using reference = bucket; + + bucket(basic_vecvec* map, index_value_type const i) + : map_{map}, i_{to_idx(i)} {} + + friend data_value_type* data(bucket b) { return &b[0]; } + friend index_value_type size(bucket b) { return b.size(); } + + data_value_type const* data() const { return empty() ? nullptr : &front(); } + + template , + typename Traits = std::char_traits> + std::basic_string_view view() const { + return std::basic_string_view{begin(), size()}; + } + + value_type& front() { + assert(!empty()); + return operator[](0); + } + + value_type& back() { + assert(!empty()); + return operator[](size() - 1U); + } + + value_type const& front() const { + assert(!empty()); + return operator[](0); + } + + value_type const& back() const { + assert(!empty()); + return operator[](size() - 1U); + } + + bool empty() const { return begin() == end(); } + + template + void push_back(Args&& args) { + map_->data_.insert(std::next(std::begin(map_->data_), bucket_end_idx()), + std::forward(args)); + for (auto i = i_ + 1; i != map_->bucket_starts_.size(); ++i) { + ++map_->bucket_starts_[i]; + } + } + + value_type& operator[](std::size_t const i) { + assert(is_inside_bucket(i)); + return map_->data_[to_idx(map_->bucket_starts_[i_] + i)]; + } + + value_type const& operator[](std::size_t const i) const { + assert(is_inside_bucket(i)); + return map_->data_[to_idx(map_->bucket_starts_[i_] + i)]; + } + + value_type const& at(std::size_t const i) const { + verify(i < size(), "bucket::at: index out of range"); + return *(begin() + i); + } + + value_type& at(std::size_t const i) { + verify(i < size(), "bucket::at: index out of range"); + return *(begin() + i); + } + + std::size_t size() const { return bucket_end_idx() - bucket_begin_idx(); } + iterator begin() { return map_->data_.begin() + bucket_begin_idx(); } + iterator end() { return map_->data_.begin() + bucket_end_idx(); } + const_iterator begin() const { + return map_->data_.begin() + bucket_begin_idx(); + } + const_iterator end() const { + return map_->data_.begin() + bucket_end_idx(); + } + friend iterator begin(bucket const& b) { return b.begin(); } + friend iterator end(bucket const& b) { return b.end(); } + friend iterator begin(bucket& b) { return b.begin(); } + friend iterator end(bucket& b) { return b.end(); } + + friend bool operator==(bucket const& a, bucket const& b) { + assert(a.map_ == b.map_); + return a.i_ == b.i_; + } + friend bool operator!=(bucket const& a, bucket const& b) { + assert(a.map_ == b.map_); + return a.i_ != b.i_; + } + bucket& operator++() { + ++i_; + return *this; + } + bucket& operator--() { + --i_; + return *this; + } + bucket operator*() const { return *this; } + bucket& operator+=(difference_type const n) { + i_ += n; + return *this; + } + bucket& operator-=(difference_type const n) { + i_ -= n; + return *this; + } + bucket operator+(difference_type const n) const { + auto tmp = *this; + tmp += n; + return tmp; + } + bucket operator-(difference_type const n) const { + auto tmp = *this; + tmp -= n; + return tmp; + } + friend difference_type operator-(bucket const& a, bucket const& b) { + assert(a.map_ == b.map_); + return a.i_ - b.i_; + } + + private: + index_value_type bucket_begin_idx() const { + return to_idx(map_->bucket_starts_[i_]); + } + index_value_type bucket_end_idx() const { + return to_idx(map_->bucket_starts_[i_ + 1U]); + } + bool is_inside_bucket(std::size_t const i) const { + return bucket_begin_idx() + i < bucket_end_idx(); + } + + basic_vecvec* map_; + index_value_type i_; + }; + + struct const_bucket final { + using value_type = data_value_type; + using iterator = typename DataVec::const_iterator; + using const_iterator = typename DataVec::const_iterator; + + using iterator_category = std::random_access_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference; + + const_bucket(basic_vecvec const* map, index_value_type const i) + : map_{map}, i_{to_idx(i)} {} + + friend data_value_type const* data(const_bucket b) { return b.data(); } + friend index_value_type size(const_bucket b) { return b.size(); } + + data_value_type const* data() const { return empty() ? nullptr : &front(); } + + template , + typename = std::enable_if_t>> + std::string_view view() const { + return std::string_view{begin(), size()}; + } + + value_type const& front() const { + assert(!empty()); + return operator[](0); + } + + value_type const& back() const { + assert(!empty()); + return operator[](size() - 1U); + } + + bool empty() const { return begin() == end(); } + + value_type const& at(std::size_t const i) const { + verify(i < size(), "bucket::at: index out of range"); + return *(begin() + i); + } + + value_type const& operator[](std::size_t const i) const { + assert(is_inside_bucket(i)); + return map_->data_[map_->bucket_starts_[i_] + i]; + } + + index_value_type size() const { + return bucket_end_idx() - bucket_begin_idx(); + } + const_iterator begin() const { + return map_->data_.begin() + bucket_begin_idx(); + } + const_iterator end() const { + return map_->data_.begin() + bucket_end_idx(); + } + friend const_iterator begin(const_bucket const& b) { return b.begin(); } + friend const_iterator end(const_bucket const& b) { return b.end(); } + + std::reverse_iterator rbegin() const { + return std::reverse_iterator{begin() + size()}; + } + std::reverse_iterator rend() const { + return std::reverse_iterator{begin()}; + } + + friend bool operator==(const_bucket const& a, const_bucket const& b) { + assert(a.map_ == b.map_); + return a.i_ == b.i_; + } + friend bool operator!=(const_bucket const& a, const_bucket const& b) { + assert(a.map_ == b.map_); + return a.i_ != b.i_; + } + const_bucket& operator++() { + ++i_; + return *this; + } + const_bucket& operator--() { + --i_; + return *this; + } + const_bucket operator*() const { return *this; } + const_bucket& operator+=(difference_type const n) { + i_ += n; + return *this; + } + const_bucket& operator-=(difference_type const n) { + i_ -= n; + return *this; + } + const_bucket operator+(difference_type const n) const { + auto tmp = *this; + tmp += n; + return tmp; + } + const_bucket operator-(difference_type const n) const { + auto tmp = *this; + tmp -= n; + return tmp; + } + friend difference_type operator-(const_bucket const& a, + const_bucket const& b) { + assert(a.map_ == b.map_); + return a.i_ - b.i_; + } + + private: + std::size_t bucket_begin_idx() const { + return to_idx(map_->bucket_starts_[i_]); + } + std::size_t bucket_end_idx() const { + return to_idx(map_->bucket_starts_[i_ + 1]); + } + bool is_inside_bucket(std::size_t const i) const { + return bucket_begin_idx() + i < bucket_end_idx(); + } + + std::size_t i_; + basic_vecvec const* map_; + }; + + using value_type = bucket; + using iterator = bucket; + using const_iterator = const_bucket; + + bucket operator[](Key const i) { return {this, to_idx(i)}; } + const_bucket operator[](Key const i) const { return {this, to_idx(i)}; } + + const_bucket at(Key const i) const { + verify(to_idx(i) < bucket_starts_.size(), + "basic_vecvec::at: index out of range"); + return {this, to_idx(i)}; + } + + bucket at(Key const i) { + verify(to_idx(i) < bucket_starts_.size(), + "basic_vecvec::at: index out of range"); + return {this, to_idx(i)}; + } + + bucket front() { return at(Key{0}); } + bucket back() { return at(Key{size() - 1}); } + + const_bucket front() const { return at(Key{0}); } + const_bucket back() const { return at(Key{size() - 1}); } + + index_value_type size() const { + return empty() ? 0U : bucket_starts_.size() - 1; + } + bool empty() const { return bucket_starts_.empty(); } + + void clear() { + bucket_starts_.clear(); + data_.clear(); + } + + template ().begin()), data_value_type>>> + void emplace_back(Container&& bucket) { + if (bucket_starts_.empty()) { + bucket_starts_.emplace_back(index_value_type{0U}); + } + bucket_starts_.emplace_back( + static_cast(data_.size() + bucket.size())); + data_.insert(std::end(data_), // + std::make_move_iterator(std::begin(bucket)), + std::make_move_iterator(std::end(bucket))); + } + + bucket add_back_sized(std::size_t const bucket_size) { + if (bucket_starts_.empty()) { + bucket_starts_.emplace_back(index_value_type{0U}); + } + data_.resize(data_.size() + bucket_size); + bucket_starts_.emplace_back(static_cast(data_.size())); + return at(Key{size() - 1U}); + } + + template + std::enable_if_t, data_value_type>> + emplace_back(std::initializer_list&& x) { + if (bucket_starts_.empty()) { + bucket_starts_.emplace_back(index_value_type{0U}); + } + bucket_starts_.emplace_back( + static_cast(data_.size() + x.size())); + data_.insert(std::end(data_), // + std::make_move_iterator(std::begin(x)), + std::make_move_iterator(std::end(x))); + } + + template >> + void emplace_back(char const* s) { + return emplace_back(std::string_view{s}); + } + + void resize(std::size_t const new_size) { + auto const old_size = bucket_starts_.size(); + bucket_starts_.resize( + static_cast(new_size + 1U)); + for (auto i = old_size; i < new_size + 1U; ++i) { + bucket_starts_[i] = data_.size(); + } + } + + bucket begin() { return bucket{this, 0U}; } + bucket end() { return bucket{this, size()}; } + const_bucket begin() const { return const_bucket{this, 0U}; } + const_bucket end() const { return const_bucket{this, size()}; } + + friend bucket begin(basic_vecvec& m) { return m.begin(); } + friend bucket end(basic_vecvec& m) { return m.end(); } + friend const_bucket begin(basic_vecvec const& m) { return m.begin(); } + friend const_bucket end(basic_vecvec const& m) { return m.end(); } + + DataVec data_; + IndexVec bucket_starts_; +}; + +namespace offset { + +template > +using vecvec = basic_vecvec, vector>; + +} // namespace offset + +namespace raw { + +template > +using vecvec = basic_vecvec, vector>; + +} // namespace raw + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/decay.h b/parallel/parallel_src/extern/cista/include/cista/decay.h new file mode 100644 index 00000000..2d10d338 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/decay.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace cista { + +namespace detail { + +template +struct decay { + using type = std::remove_cv_t>; +}; + +template +struct decay> { + using type = std::remove_cv_t>; +}; + +} // namespace detail + +template +using decay_t = typename detail::decay>::type; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/endian/conversion.h b/parallel/parallel_src/extern/cista/include/cista/endian/conversion.h new file mode 100644 index 00000000..d446605e --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/endian/conversion.h @@ -0,0 +1,83 @@ +#pragma once + +#include "cista/endian/detection.h" +#include "cista/mode.h" + +// Based on +// https://github.com/google/flatbuffers/blob/master/include/flatbuffers/base.h + +#if defined(_MSC_VER) +#define CISTA_BYTESWAP_16 _byteswap_ushort +#define CISTA_BYTESWAP_32 _byteswap_ulong +#define CISTA_BYTESWAP_64 _byteswap_uint64 +#else +#define CISTA_BYTESWAP_16 __builtin_bswap16 +#define CISTA_BYTESWAP_32 __builtin_bswap32 +#define CISTA_BYTESWAP_64 __builtin_bswap64 +#endif + +namespace cista { + +template +constexpr T endian_swap(T const t) noexcept { + static_assert(sizeof(T) == 1U || sizeof(T) == 2U || sizeof(T) == 4U || + sizeof(T) == 8U); + + if constexpr (sizeof(T) == 1U) { + return t; + } else if constexpr (sizeof(T) == 2U) { + union { + T t; + std::uint16_t i; + } u{t}; + u.i = CISTA_BYTESWAP_16(u.i); + return u.t; + } else if constexpr (sizeof(T) == 4U) { + union { + T t; + std::uint32_t i; + } u{t}; + u.i = CISTA_BYTESWAP_32(u.i); + return u.t; + } else if constexpr (sizeof(T) == 8U) { + union { + T t; + std::uint64_t i; + } u{t}; + u.i = CISTA_BYTESWAP_64(u.i); + return u.t; + } +} + +template +constexpr bool endian_conversion_necessary() noexcept { + if constexpr ((Mode & mode::SERIALIZE_BIG_ENDIAN) == + mode::SERIALIZE_BIG_ENDIAN) { +#if defined(CISTA_BIG_ENDIAN) + return false; +#else + return true; +#endif + } else { +#if defined(CISTA_LITTLE_ENDIAN) + return false; +#else + return true; +#endif + } +} + +template +constexpr T convert_endian(T t) noexcept { + if constexpr (endian_conversion_necessary()) { + return endian_swap(t); + } else { + return t; + } +} + +} // namespace cista + +#undef CISTA_BYTESWAP_16 +#undef CISTA_BYTESWAP_32 +#undef CISTA_BYTESWAP_64 diff --git a/parallel/parallel_src/extern/cista/include/cista/endian/detection.h b/parallel/parallel_src/extern/cista/include/cista/endian/detection.h new file mode 100644 index 00000000..86fca211 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/endian/detection.h @@ -0,0 +1,39 @@ +#pragma once + +// Based on: +// https://stackoverflow.com/a/32210953 (MSVC) +// https://stackoverflow.com/a/27054190 (GCC/Clang) + +#if !defined(CISTA_BIG_ENDIAN) && !defined(CISTA_LITTLE_ENDIAN) + +#if defined(__APPLE__) +#include +#elif defined(__GNUC__) +#include +#elif defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif + +#if defined(REG_DWORD) && REG_DWORD == REG_DWORD_BIG_ENDIAN || \ + defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ + defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || \ + defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || \ + defined(__MIBSEB__) +#define CISTA_BIG_ENDIAN +#elif defined(REG_DWORD) && REG_DWORD == REG_DWORD_LITTLE_ENDIAN || \ + defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(__MIPSEL) || defined(__MIPSEL__) +#define CISTA_LITTLE_ENDIAN +#else +#error "architecture: unknown byte order" +#endif + +#endif diff --git a/parallel/parallel_src/extern/cista/include/cista/equal_to.h b/parallel/parallel_src/extern/cista/include/cista/equal_to.h new file mode 100644 index 00000000..b8210ef8 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/equal_to.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +#include "cista/containers/pair.h" +#include "cista/decay.h" +#include "cista/is_iterable.h" +#include "cista/reflection/to_tuple.h" + +namespace cista { + +namespace detail { + +template +constexpr bool tuple_equal_impl(F&& is_equal, Tuple&& a, Tuple&& b, + std::index_sequence) { + return (is_equal(std::get(std::forward(a)), + std::get(std::forward(b))) && + ...); +} + +} // namespace detail + +template +constexpr decltype(auto) tuple_equal(F&& is_equal, Tuple&& a, Tuple&& b) { + return detail::tuple_equal_impl( + std::forward(is_equal), std::forward(a), std::forward(b), + std::make_index_sequence< + std::tuple_size_v>>{}); +} + +template +struct is_eq_comparable : std::false_type {}; + +template +struct is_eq_comparable< + A, B, std::void_t() == std::declval())>> + : std::true_type {}; + +template +constexpr bool is_eq_comparable_v = is_eq_comparable::value; + +template +struct equal_to; + +template +struct equal_to { + template + constexpr bool operator()(T const& a, T1 const& b) const { + using Type = decay_t; + using Type1 = decay_t; + if constexpr (is_iterable_v && is_iterable_v) { + using std::begin; + using std::end; + auto const eq = std::equal( + begin(a), end(a), begin(b), end(b), + [](auto&& x, auto&& y) { return equal_to{}(x, y); }); + return eq; + } else if constexpr (to_tuple_works_v && to_tuple_works_v) { + return tuple_equal( + [](auto&& x, auto&& y) { return equal_to{}(x, y); }, + to_tuple(a), to_tuple(b)); + } else if constexpr (is_eq_comparable_v) { + return a == b; + } else { + static_assert(is_iterable_v || is_eq_comparable_v || + to_tuple_works_v, + "Implement custom equality"); + } + return false; + } +}; + +template +struct equal_to> { + template + constexpr bool operator()(pair const& a, T1 const& b) const { + return a.first == b.first && a.second == b.second; + } +}; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/exception.h b/parallel/parallel_src/extern/cista/include/cista/exception.h new file mode 100644 index 00000000..22256e0c --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/exception.h @@ -0,0 +1,14 @@ +#pragma once + +namespace cista { + +template +void throw_exception(E&& e) { +#if !defined(__cpp_exceptions) || __cpp_exceptions < 199711L + abort(); +#else + throw e; +#endif +} + +} // namespace cista \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/hash.h b/parallel/parallel_src/extern/cista/include/cista/hash.h new file mode 100644 index 00000000..04b647e2 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/hash.h @@ -0,0 +1,151 @@ +#pragma once + +#include +#include + +namespace cista { + +#if defined(CISTA_XXH3) + +#include "xxh3.h" + +using hash_t = XXH64_hash_t; + +constexpr auto const BASE_HASH = 0ULL; + +template +constexpr hash_t hash_combine(hash_t h, Args... val) { + auto xxh3 = [&](auto const& arg) { + h = XXH3_64bits_withSeed(&arg, sizeof(arg), h); + }; + ((xxh3(val)), ...); + return h; +} + +inline hash_t hash(std::string_view s, hash_t h = BASE_HASH) { + return XXH3_64bits_withSeed(s.data(), s.size(), h); +} + +template +constexpr hash_t hash(const char (&str)[N], hash_t const h = BASE_HASH) { + return XXH3_64bits_withSeed(str, N - 1, h); +} + +template +constexpr std::uint64_t hash(T const& buf, hash_t const h = BASE_HASH) { + return buf.size() == 0 ? h : XXH3_64bits_withSeed(&buf[0], buf.size(), h); +} + +#elif defined(CISTA_WYHASH) + +#include "wyhash.h" + +using hash_t = std::uint64_t; + +constexpr auto const BASE_HASH = 34432ULL; + +template +constexpr hash_t hash_combine(hash_t h, Args... val) { + auto wy = [&](auto const& arg) { + h = wyhash::wyhash(&arg, sizeof(arg), h, wyhash::_wyp); + }; + ((wy(val)), ...); + return h; +} + +inline hash_t hash(std::string_view s, hash_t h = BASE_HASH) { + return wyhash::wyhash(s.data(), s.size(), h, wyhash::_wyp); +} + +template +constexpr hash_t hash(const char (&str)[N], + hash_t const h = BASE_HASH) noexcept { + return wyhash::wyhash(str, N - 1, h, wyhash::_wyp); +} + +template +constexpr std::uint64_t hash(T const& buf, + hash_t const h = BASE_HASH) noexcept { + return buf.size() == 0 ? h + : wyhash::wyhash(&buf[0], buf.size(), h, wyhash::_wyp); +} + +#elif defined(CISTA_WYHASH_FASTEST) + +#include "wyhash.h" + +using hash_t = std::uint64_t; + +constexpr auto const BASE_HASH = 123ULL; + +template +constexpr hash_t hash_combine(hash_t h, Args... val) { + auto fh = [&](auto const& arg) { + h = wyhash::FastestHash(&arg, sizeof(arg), h); + }; + ((fh(val)), ...); + return h; +} + +inline hash_t hash(std::string_view s, hash_t h = BASE_HASH) { + return wyhash::FastestHash(s.data(), s.size(), h); +} + +template +constexpr hash_t hash(const char (&str)[N], + hash_t const h = BASE_HASH) noexcept { + return wyhash::FastestHash(str, N - 1U, h); +} + +template +constexpr std::uint64_t hash(T const& buf, + hash_t const h = BASE_HASH) noexcept { + return buf.size() == 0U ? h : wyhash::FastestHash(&buf[0U], buf.size(), h); +} + +#else // defined(CISTA_FNV1A) + +// Algorithm: 64bit FNV-1a +// Source: http://www.isthe.com/chongo/tech/comp/fnv/ + +using hash_t = std::uint64_t; + +constexpr auto const BASE_HASH = 14695981039346656037ULL; + +template +constexpr hash_t hash_combine(hash_t h, Args... val) noexcept { + constexpr hash_t fnv_prime = 1099511628211ULL; + auto fnv = [&](auto arg) noexcept { + h = (h ^ static_cast(arg)) * fnv_prime; + }; + ((fnv(val)), ...); + return h; +} + +constexpr hash_t hash(std::string_view s, hash_t h = BASE_HASH) noexcept { + auto const ptr = s.data(); + for (std::size_t i = 0U; i < s.size(); ++i) { + h = hash_combine(h, static_cast(ptr[i])); + } + return h; +} + +template +constexpr hash_t hash(const char (&str)[N], + hash_t const h = BASE_HASH) noexcept { + return hash(std::string_view{str, N - 1U}, h); +} + +template +constexpr std::uint64_t hash(T const& buf, + hash_t const h = BASE_HASH) noexcept { + return buf.size() == 0U + ? h + : hash(std::string_view{reinterpret_cast(&buf[0U]), + buf.size()}, + h); +} + +#endif + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/hashing.h b/parallel/parallel_src/extern/cista/include/cista/hashing.h new file mode 100644 index 00000000..99b18197 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/hashing.h @@ -0,0 +1,186 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "cista/containers/cstring.h" +#include "cista/containers/offset_ptr.h" +#include "cista/containers/pair.h" +#include "cista/containers/string.h" +#include "cista/decay.h" +#include "cista/hash.h" +#include "cista/is_iterable.h" +#include "cista/reflection/for_each_field.h" +#include "cista/type_traits.h" + +namespace cista { + +namespace detail { + +template +struct has_hash : std::false_type {}; + +template +struct has_hash().hash())>> + : std::true_type {}; + +template +struct has_std_hash : std::false_type {}; + +template +struct has_std_hash< + T, std::void_t>()(std::declval()))>> + : std::true_type {}; + +} // namespace detail + +template +inline constexpr bool has_hash_v = detail::has_hash::value; + +template +inline constexpr bool has_std_hash_v = detail::has_std_hash::value; + +template +struct is_hash_equivalent_helper : std::false_type {}; + +template +constexpr bool is_hash_equivalent_v = + is_hash_equivalent_helper, std::remove_cv_t>::value; + +template +constexpr bool is_string_like_v = + is_string_v> || is_char_array_v || + std::is_same_v || + std::is_same_v, std::string> || + std::is_same_v, std::string_view>; + +template +constexpr bool is_ptr_same = is_pointer_v && is_pointer_v; + +template +struct hashing; + +template +struct hashing { + template + static constexpr bool is_hash_equivalent() noexcept { + using DecayA = decay_t; + using DecayB = decay_t; + return is_hash_equivalent_v || + std::is_same_v || + (is_string_like_v && is_string_like_v) || + std::is_convertible_v || is_ptr_same; + } + + template + static constexpr hashing create() noexcept { + static_assert(is_hash_equivalent(), "Incompatible types"); + return hashing{}; + } + + constexpr hash_t operator()(T const& el, + hash_t const seed = BASE_HASH) const { + using Type = decay_t; + if constexpr (has_hash_v) { + return hash_combine(el.hash(), seed); + } else if constexpr (is_pointer_v) { + return hash_combine(seed, reinterpret_cast(ptr_cast(el))); + } else if constexpr (is_char_array_v) { + return hash(std::string_view{el, sizeof(el) - 1U}, seed); + } else if constexpr (is_string_like_v) { + using std::begin; + using std::end; + return el.size() == 0U + ? seed + : hash(std::string_view{&(*begin(el)), el.size()}, seed); + } else if constexpr (std::is_scalar_v) { + return hash_combine(seed, el); + } else if constexpr (has_std_hash_v) { + return hash_combine(std::hash()(el), seed); + } else if constexpr (is_iterable_v) { + auto h = seed; + for (auto const& v : el) { + h = hashing>()(v, h); + } + return h; + } else if constexpr (to_tuple_works_v) { + auto h = seed; + for_each_field(el, [&h](auto&& f) { + h = hashing>{}(f, h); + }); + return h; + } else if constexpr (is_strong_v) { + return hashing{}(el.v_, seed); + } else { + static_assert(has_hash_v || std::is_scalar_v || + has_std_hash_v || is_iterable_v || + to_tuple_works_v || is_strong_v, + "Implement hash"); + } + } +}; + +template +struct hashing> { + hash_t operator()(std::chrono::duration const& el, + hash_t const seed = BASE_HASH) { + return hashing{}(el.count(), seed); + } +}; + +template +struct hashing> { + constexpr hash_t operator()(std::pair const& el, + hash_t const seed = BASE_HASH) { + std::size_t h = seed; + h = hashing{}(el.first, h); + h = hashing{}(el.second, h); + return h; + } +}; + +template +struct hashing> { + constexpr hash_t operator()(pair const& el, + hash_t const seed = BASE_HASH) { + std::size_t h = seed; + h = hashing{}(el.first, h); + h = hashing{}(el.second, h); + return h; + } +}; + +template +struct hashing> { + constexpr hash_t operator()(std::tuple const& el, + hash_t const seed = BASE_HASH) { + hash_t h = seed; + std::apply( + [&h](auto&&... args) { + ((h = hashing{}(args, h)), ...); + }, + el); + return h; + } +}; + +template <> +struct hashing { + hash_t operator()(char const* el, hash_t const seed = BASE_HASH) { + return hash(std::string_view{el}, seed); + } +}; + +template +hash_t build_hash(Args const&... args) { + hash_t h = BASE_HASH; + ((h = hashing{}(args, h)), ...); + return h; +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/indexed.h b/parallel/parallel_src/extern/cista/include/cista/indexed.h new file mode 100644 index 00000000..abd393cc --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/indexed.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace cista { + +template +struct indexed : public T { + using value_type = T; + using T::T; + using T::operator=; +}; + +template +struct is_indexed_helper : std::false_type {}; + +template +struct is_indexed_helper> : std::true_type {}; + +template +constexpr bool is_indexed_v = is_indexed_helper>::value; + +} // namespace cista + +#if __has_include("fmt/ostream.h") + +#include "fmt/ostream.h" + +template +struct fmt::formatter> : ostream_formatter {}; + +#endif diff --git a/parallel/parallel_src/extern/cista/include/cista/is_iterable.h b/parallel/parallel_src/extern/cista/include/cista/is_iterable.h new file mode 100644 index 00000000..89461d41 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/is_iterable.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +#include "cista/decay.h" + +namespace cista { +namespace detail { + +using std::begin; +using std::end; + +template +struct is_iterable : std::false_type {}; + +template +struct is_iterable())), + decltype(end(std::declval()))>> + : std::true_type {}; + +template +struct it_value { + using type = void; +}; + +template +struct it_value::value>> { + using type = decay_t()))>; +}; + +} // namespace detail + +using detail::is_iterable; + +template +constexpr bool is_iterable_v = is_iterable::value; + +template +using it_value_t = typename detail::it_value::type; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/memory_holder.h b/parallel/parallel_src/extern/cista/include/cista/memory_holder.h new file mode 100755 index 00000000..470b7e55 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/memory_holder.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "cista/buffer.h" +#include "cista/containers/unique_ptr.h" +#include "cista/mmap.h" +#include "cista/targets/buf.h" + +namespace cista { + +using memory_holder = std::variant, buffer, byte_buf>; + +template +struct wrapped { + wrapped() = default; + wrapped(memory_holder mem, T* el) : mem_{std::move(mem)}, el_{el} { + el_.self_allocated_ = false; + } + explicit wrapped(raw::unique_ptr el) : el_{std::move(el)} {} + + friend bool operator==(wrapped const& x, std::nullptr_t) { + return x.el_ == nullptr; + } + + T* get() const noexcept { return el_.get(); } + T* operator->() noexcept { return el_.get(); } + T const* operator->() const noexcept { return el_.get(); } + T& operator*() noexcept { return *el_; } + T const& operator*() const noexcept { return *el_; } + + memory_holder mem_; + raw::unique_ptr el_; +}; + +template +wrapped(memory_holder, T*) -> wrapped; + +template +wrapped(memory_holder, raw::unique_ptr) -> wrapped; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/mmap.h b/parallel/parallel_src/extern/cista/include/cista/mmap.h new file mode 100755 index 00000000..c6b40d24 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/mmap.h @@ -0,0 +1,217 @@ +#pragma once + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#else +#include +#include +#include +#endif + +#include "cista/next_power_of_2.h" +#include "cista/targets/file.h" + +namespace cista { + +struct mmap { + static constexpr auto const OFFSET = 0ULL; + static constexpr auto const ENTIRE_FILE = + std::numeric_limits::max(); + enum class protection { READ, WRITE, MODIFY }; + + mmap() = default; + + explicit mmap(char const* path, protection const prot = protection::WRITE) + : f_{path, prot == protection::MODIFY + ? "r+" + : (prot == protection::READ ? "r" : "w+")}, + prot_{prot}, + size_{f_.size()}, + used_size_{f_.size()}, + addr_{size_ == 0U ? nullptr : map()} {} + + ~mmap() { + if (addr_ != nullptr) { + sync(); + size_ = used_size_; + unmap(); + if (size_ != f_.size()) { + resize_file(); + } + } + } + + mmap(mmap const&) = delete; + mmap& operator=(mmap const&) = delete; + + mmap(mmap&& o) + : f_{std::move(o.f_)}, + prot_{o.prot_}, + size_{o.size_}, + used_size_{o.used_size_}, + addr_{o.addr_} { +#ifdef _WIN32 + file_mapping_ = o.file_mapping_; +#endif + o.addr_ = nullptr; + } + + mmap& operator=(mmap&& o) { + f_ = std::move(o.f_); + prot_ = o.prot_; + size_ = o.size_; + used_size_ = o.used_size_; + addr_ = o.addr_; +#ifdef _WIN32 + file_mapping_ = o.file_mapping_; +#endif + o.addr_ = nullptr; + return *this; + } + + void sync() { + if ((prot_ == protection::WRITE || prot_ == protection::MODIFY) && + addr_ != nullptr) { +#ifdef _WIN32 + verify(::FlushViewOfFile(addr_, size_) != 0, "flush error"); + verify(::FlushFileBuffers(f_.f_) != 0, "flush error"); +#else + verify(::msync(addr_, size_, MS_SYNC) == 0, "sync error"); +#endif + } + } + + void resize(std::size_t const new_size) { + verify(prot_ == protection::WRITE || prot_ == protection::MODIFY, + "read-only not resizable"); + if (size_ < new_size) { + resize_map(next_power_of_two(new_size)); + } + used_size_ = new_size; + } + + void reserve(std::size_t const new_size) { + verify(prot_ == protection::WRITE || prot_ == protection::MODIFY, + "read-only not resizable"); + if (size_ < new_size) { + resize_map(next_power_of_two(new_size)); + } + } + + std::size_t size() const noexcept { return used_size_; } + + std::string_view view() const noexcept { + return {static_cast(addr_), size()}; + } + std::uint8_t* data() noexcept { return static_cast(addr_); } + std::uint8_t const* data() const noexcept { + return static_cast(addr_); + } + + std::uint8_t* begin() noexcept { return data(); } + std::uint8_t* end() noexcept { return data() + used_size_; } + std::uint8_t const* begin() const noexcept { return data(); } + std::uint8_t const* end() const noexcept { return data() + used_size_; } + + std::uint8_t& operator[](std::size_t const i) noexcept { return data()[i]; } + std::uint8_t const& operator[](std::size_t const i) const noexcept { + return data()[i]; + } + +private: + void unmap() { +#ifdef _WIN32 + if (addr_ != nullptr) { + verify(::UnmapViewOfFile(addr_), "unmap error"); + addr_ = nullptr; + + verify(::CloseHandle(file_mapping_), "close file mapping error"); + file_mapping_ = nullptr; + } +#else + if (addr_ != nullptr) { + ::munmap(addr_, size_); + addr_ = nullptr; + } +#endif + } + + void* map() { +#ifdef _WIN32 + auto const size_low = static_cast(size_); +#ifdef _WIN64 + auto const size_high = static_cast(size_ >> 32U); +#else + auto const size_high = static_cast(0U); +#endif + const auto fm = ::CreateFileMapping( + f_.f_, 0, prot_ == protection::READ ? PAGE_READONLY : PAGE_READWRITE, + size_high, size_low, 0); + verify(fm != NULL, "file mapping error"); + file_mapping_ = fm; + + auto const addr = ::MapViewOfFile( + fm, prot_ == protection::READ ? FILE_MAP_READ : FILE_MAP_WRITE, OFFSET, + OFFSET, size_); + verify(addr != nullptr, "map error"); + + return addr; +#else + auto const addr = + ::mmap(nullptr, size_, + prot_ == protection::READ ? PROT_READ : PROT_READ | PROT_WRITE, + MAP_SHARED, f_.fd(), OFFSET); + verify(addr != MAP_FAILED, "map error"); + return addr; +#endif + } + + void resize_file() { + if (prot_ == protection::READ) { + return; + } + +#ifdef _WIN32 + LARGE_INTEGER Size = {0}; + verify(::GetFileSizeEx(f_.f_, &Size), "resize: get file size error"); + + LARGE_INTEGER Distance = {0}; + Distance.QuadPart = size_ - Size.QuadPart; + verify(::SetFilePointerEx(f_.f_, Distance, nullptr, FILE_END), + "resize error"); + verify(::SetEndOfFile(f_.f_), "resize set eof error"); +#else + verify(::ftruncate(f_.fd(), static_cast(size_)) == 0, + "resize error"); +#endif + } + + void resize_map(std::size_t const new_size) { + if (prot_ == protection::READ) { + return; + } + + unmap(); + size_ = new_size; + resize_file(); + addr_ = map(); + } + + file f_; + protection prot_; + std::size_t size_; + std::size_t used_size_; + void* addr_; +#ifdef _WIN32 + HANDLE file_mapping_; +#endif +}; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/mode.h b/parallel/parallel_src/extern/cista/include/cista/mode.h new file mode 100644 index 00000000..0e94ea25 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/mode.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace cista { + +enum class mode { + NONE = 0U, + UNCHECKED = 1U << 0U, + WITH_VERSION = 1U << 1U, + WITH_INTEGRITY = 1U << 2U, + SERIALIZE_BIG_ENDIAN = 1U << 3U, + DEEP_CHECK = 1U << 4U, + CAST = 1U << 5U, + WITH_STATIC_VERSION = 1U << 6U, + SKIP_INTEGRITY = 1U << 7U, + SKIP_VERSION = 1U << 8U, + _CONST = 1U << 29U, + _PHASE_II = 1U << 30U +}; + +constexpr mode operator|(mode const& a, mode const& b) noexcept { + return mode{static_cast>(a) | + static_cast>(b)}; +} + +constexpr mode operator&(mode const& a, mode const& b) noexcept { + return mode{static_cast>(a) & + static_cast>(b)}; +} + +constexpr bool is_mode_enabled(mode const in, mode const flag) noexcept { + return (in & flag) == flag; +} + +constexpr bool is_mode_disabled(mode const in, mode const flag) noexcept { + return (in & flag) == mode::NONE; +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/next_power_of_2.h b/parallel/parallel_src/extern/cista/include/cista/next_power_of_2.h new file mode 100644 index 00000000..37333195 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/next_power_of_2.h @@ -0,0 +1,24 @@ +#pragma once + +namespace cista { + +template +constexpr TemplateSizeType next_power_of_two(TemplateSizeType n) noexcept { + --n; + n |= n >> 1U; + n |= n >> 2U; + n |= n >> 4U; + if constexpr (sizeof(TemplateSizeType) > 1U) { + n |= n >> 8U; + } + if constexpr (sizeof(TemplateSizeType) > 2U) { + n |= n >> 16U; + } + if constexpr (sizeof(TemplateSizeType) > 4U) { + n |= n >> 32U; + } + ++n; + return n; +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/offset_t.h b/parallel/parallel_src/extern/cista/include/cista/offset_t.h new file mode 100644 index 00000000..d0781bd6 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/offset_t.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#define PRI_O PRIdPTR + +namespace cista { + +using offset_t = intptr_t; + +constexpr auto const NULLPTR_OFFSET = std::numeric_limits::min(); +constexpr auto const DANGLING = std::numeric_limits::min() + 1U; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/reflection/arity.h b/parallel/parallel_src/extern/cista/include/cista/reflection/arity.h new file mode 100644 index 00000000..5763af1d --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/reflection/arity.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "cista/decay.h" + +// Credits: Implementation by Anatoliy V. Tomilov (@tomilov), +// based on gist by Rafal T. Janik (@ChemiaAion) +// +// Resources: +// https://playfulprogramming.blogspot.com/2016/12/serializing-structs-with-c17-structured.html +// https://codereview.stackexchange.com/questions/142804/get-n-th-data-member-of-a-struct +// https://stackoverflow.com/questions/39768517/structured-bindings-width +// https://stackoverflow.com/questions/35463646/arity-of-aggregate-in-logarithmic-time +// https://stackoverflow.com/questions/38393302/returning-variadic-aggregates-struct-and-syntax-for-c17-variadic-template-c + +namespace cista { + +namespace detail { + +struct instance { + template + operator Type() const; +}; + +template , + typename = void> +struct arity_impl : IndexSequence {}; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +template +struct arity_impl, + std::void_t(Indices), std::declval())..., + std::declval()})>> + : arity_impl> {}; + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +} // namespace detail + +template +constexpr std::size_t arity() noexcept { + return detail::arity_impl>().size(); +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/reflection/comparable.h b/parallel/parallel_src/extern/cista/include/cista/reflection/comparable.h new file mode 100644 index 00000000..baa89164 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/reflection/comparable.h @@ -0,0 +1,59 @@ +#pragma once + +#include "cista/reflection/to_tuple.h" + +#define CISTA_COMPARABLE() \ + template \ + bool operator==(T&& b) const { \ + return cista::to_tuple(*this) == cista::to_tuple(b); \ + } \ + \ + template \ + bool operator!=(T&& b) const { \ + return cista::to_tuple(*this) != cista::to_tuple(b); \ + } \ + \ + template \ + bool operator<(T&& b) const { \ + return cista::to_tuple(*this) < cista::to_tuple(b); \ + } \ + \ + template \ + bool operator<=(T&& b) const { \ + return cista::to_tuple(*this) <= cista::to_tuple(b); \ + } \ + \ + template \ + bool operator>(T&& b) const { \ + return cista::to_tuple(*this) > cista::to_tuple(b); \ + } \ + \ + template \ + bool operator>=(T&& b) const { \ + return cista::to_tuple(*this) >= cista::to_tuple(b); \ + } + +#define CISTA_FRIEND_COMPARABLE(class_name) \ + friend bool operator==(class_name const& a, class_name const& b) { \ + return cista::to_tuple(a) == cista::to_tuple(b); \ + } \ + \ + friend bool operator!=(class_name const& a, class_name const& b) { \ + return cista::to_tuple(a) != cista::to_tuple(b); \ + } \ + \ + friend bool operator<(class_name const& a, class_name const& b) { \ + return cista::to_tuple(a) < cista::to_tuple(b); \ + } \ + \ + friend bool operator<=(class_name const& a, class_name const& b) { \ + return cista::to_tuple(a) <= cista::to_tuple(b); \ + } \ + \ + friend bool operator>(class_name const& a, class_name const& b) { \ + return cista::to_tuple(a) > cista::to_tuple(b); \ + } \ + \ + friend bool operator>=(class_name const& a, class_name const& b) { \ + return cista::to_tuple(a) >= cista::to_tuple(b); \ + } diff --git a/parallel/parallel_src/extern/cista/include/cista/reflection/for_each_field.h b/parallel/parallel_src/extern/cista/include/cista/reflection/for_each_field.h new file mode 100644 index 00000000..8b0da86d --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/reflection/for_each_field.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +#include "cista/reflection/to_tuple.h" + +namespace cista { + +template +void for_each_ptr_field(T& t, Fn&& fn) { + if constexpr (std::is_pointer_v) { + if (t != nullptr) { + for_each_ptr_field(*t, std::forward(fn)); + } + } else if constexpr (std::is_scalar_v) { + fn(t); + } else { + std::apply([&](auto&&... args) { (fn(args), ...); }, to_ptr_tuple(t)); + } +} + +template +void for_each_field(T& t, Fn&& fn) { + if constexpr (std::is_pointer_v) { + if (t != nullptr) { + for_each_field(*t, std::forward(fn)); + } + } else if constexpr (std::is_scalar_v) { + fn(t); + } else { + std::apply([&](auto&&... args) { (fn(args), ...); }, to_tuple(t)); + } +} + +template +void for_each_field(Fn&& fn) { + T t{}; + for_each_field(t, std::forward(fn)); +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/reflection/member_index.h b/parallel/parallel_src/extern/cista/include/cista/reflection/member_index.h new file mode 100644 index 00000000..ebb29198 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/reflection/member_index.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cista/reflection/for_each_field.h" + +#include +#include + +namespace cista { + +template +std::size_t member_index(MemberType T::*const member_ptr) { + auto i = 0U, field_index = std::numeric_limits::max(); + T t{}; + cista::for_each_field(t, [&](auto&& m) { + if constexpr (std::is_same_v) { + if (&m == &(t.*member_ptr)) { + field_index = i; + } + } + ++i; + }); + return field_index; +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/reflection/printable.h b/parallel/parallel_src/extern/cista/include/cista/reflection/printable.h new file mode 100644 index 00000000..f107a4c9 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/reflection/printable.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +#ifndef CISTA_PRINTABLE_NO_VEC +#include +#endif + +#include "cista/decay.h" +#include "cista/reflection/for_each_field.h" + +#ifndef CISTA_PRINTABLE_NO_VEC +template +std::ostream& operator<<(std::ostream& out, std::vector const& v) { + out << "[\n "; + auto first = true; + for (auto const& e : v) { + if (!first) { + out << ",\n "; + } + using Type = cista::decay_t; + if constexpr (std::is_enum_v) { + out << static_cast>(e); + } else { + out << e; + } + first = false; + } + return out << "\n]"; +} +#endif + +template +constexpr std::array to_str_array(T... args) { + return {args...}; +} + +#define CISTA_PRINTABLE(class_name, ...) \ + friend std::ostream& operator<<(std::ostream& out, class_name const& o) { \ + constexpr auto const names = to_str_array(__VA_ARGS__); \ + bool first = true; \ + out << '{'; \ + std::size_t i = 0U; \ + ::cista::for_each_field(o, [&](auto&& f) { \ + using Type = ::cista::decay_t; \ + if (!first) { \ + out << ", "; \ + } else { \ + first = false; \ + } \ + if (i < names.size()) { \ + out << names[i] << '='; \ + } \ + if constexpr (std::is_enum_v) { \ + out << static_cast>(f); \ + } else { \ + out << f; \ + } \ + ++i; \ + }); \ + return out << '}'; \ + } diff --git a/parallel/parallel_src/extern/cista/include/cista/reflection/to_tuple.h b/parallel/parallel_src/extern/cista/include/cista/reflection/to_tuple.h new file mode 100644 index 00000000..78f7ed3e --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/reflection/to_tuple.h @@ -0,0 +1,529 @@ + +#pragma once + +#include +#include + +#include "cista/reflection/arity.h" + +namespace cista { + +namespace detail { + +template +struct has_cista_members : std::false_type {}; + +template +struct has_cista_members< + T, std::void_t>().cista_members())>> + : std::true_type {}; + +template +inline constexpr auto const has_cista_members_v = has_cista_members::value; + +template +constexpr auto add_const_helper(std::tuple&& t, + std::index_sequence) { + return std::make_tuple(std::cref(std::get(t))...); +} + +template +constexpr auto add_const(T&& t) { + return add_const_helper( + std::forward(t), + std::make_index_sequence>>()); +} + +template +auto to_ptrs_helper(std::tuple&& t, std::index_sequence) { + return std::make_tuple(&std::get(t)...); +} + +template +auto to_ptrs(T&& t) { + return to_ptrs_helper( + std::forward(t), + std::make_index_sequence>>()); +} + +} // namespace detail + +template +inline constexpr auto to_tuple_works_v = + detail::has_cista_members_v || + (std::is_aggregate_v && +#if !defined(_MSC_VER) || defined(NDEBUG) + std::is_standard_layout_v && +#endif + !std::is_polymorphic_v && !std::is_union_v); + +template && std::is_const_v, + void*> = nullptr> +constexpr auto to_tuple(T& t) { + return detail::add_const( + const_cast>>(t) + .cista_members()); +} + +template && + !std::is_const_v, + void*> = nullptr> +constexpr auto to_tuple(T&& t) { + return t.cista_members(); +} + +template , void*> = nullptr> +auto to_tuple(T& t) { + constexpr auto const a = arity(); + static_assert(a <= 64U, "Max. supported members: 64"); + if constexpr (a == 0U) { + return std::tie(); + } else if constexpr (a == 1U) { + auto& [p1] = t; + return std::tie(p1); + } else if constexpr (a == 2U) { + auto& [p1, p2] = t; + return std::tie(p1, p2); + } else if constexpr (a == 3U) { + auto& [p1, p2, p3] = t; + return std::tie(p1, p2, p3); + } else if constexpr (a == 4U) { + auto& [p1, p2, p3, p4] = t; + return std::tie(p1, p2, p3, p4); + } else if constexpr (a == 5U) { + auto& [p1, p2, p3, p4, p5] = t; + return std::tie(p1, p2, p3, p4, p5); + } else if constexpr (a == 6U) { + auto& [p1, p2, p3, p4, p5, p6] = t; + return std::tie(p1, p2, p3, p4, p5, p6); + } else if constexpr (a == 7U) { + auto& [p1, p2, p3, p4, p5, p6, p7] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7); + } else if constexpr (a == 8U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8); + } else if constexpr (a == 9U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9); + } else if constexpr (a == 10U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); + } else if constexpr (a == 11U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); + } else if constexpr (a == 12U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12); + } else if constexpr (a == 13U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); + } else if constexpr (a == 14U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, + p14); + } else if constexpr (a == 15U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15] = + t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15); + } else if constexpr (a == 16U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16); + } else if constexpr (a == 17U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17); + } else if constexpr (a == 18U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18); + } else if constexpr (a == 19U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19); + } else if constexpr (a == 20U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20); + } else if constexpr (a == 21U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21); + } else if constexpr (a == 22U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22); + } else if constexpr (a == 23U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23); + } else if constexpr (a == 24U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24); + } else if constexpr (a == 25U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25); + } else if constexpr (a == 26U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26); + } else if constexpr (a == 27U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27); + } else if constexpr (a == 28U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28); + } else if constexpr (a == 29U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, + p29] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29); + } else if constexpr (a == 30U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30); + } else if constexpr (a == 31U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31); + } else if constexpr (a == 32U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32); + } else if constexpr (a == 33U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33); + } else if constexpr (a == 34U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34); + } else if constexpr (a == 35U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35); + } else if constexpr (a == 36U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36); + } else if constexpr (a == 37U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37); + } else if constexpr (a == 38U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38); + } else if constexpr (a == 39U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39); + } else if constexpr (a == 40U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40); + } else if constexpr (a == 41U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41); + } else if constexpr (a == 42U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42); + } else if constexpr (a == 43U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, + p43] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43); + } else if constexpr (a == 44U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44); + } else if constexpr (a == 45U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45); + } else if constexpr (a == 46U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46); + } else if constexpr (a == 47U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47); + } else if constexpr (a == 48U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48); + } else if constexpr (a == 49U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49); + } else if constexpr (a == 50U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50); + } else if constexpr (a == 51U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51); + } else if constexpr (a == 52U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52); + } else if constexpr (a == 53U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53); + } else if constexpr (a == 54U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54); + } else if constexpr (a == 55U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55); + } else if constexpr (a == 56U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55, p56); + } else if constexpr (a == 57U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, + p57] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55, p56, p57); + } else if constexpr (a == 58U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, + p58] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55, p56, p57, p58); + } else if constexpr (a == 59U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, + p58, p59] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55, p56, p57, p58, p59); + } else if constexpr (a == 60U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, + p58, p59, p60] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55, p56, p57, p58, p59, p60); + } else if constexpr (a == 61U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, + p58, p59, p60, p61] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61); + } else if constexpr (a == 62U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, + p58, p59, p60, p61, p62] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62); + } else if constexpr (a == 63U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, + p58, p59, p60, p61, p62, p63] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62, + p63); + } else if constexpr (a == 64U) { + auto& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, + p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, + p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, + p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, + p58, p59, p60, p61, p62, p63, p64] = t; + return std::tie(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, + p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, + p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, + p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, + p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62, + p63, p64); + } +} + +template +auto to_ptr_tuple(T&& t) { + return detail::to_ptrs(to_tuple(std::forward(t))); +} +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/serialization.h b/parallel/parallel_src/extern/cista/include/cista/serialization.h new file mode 100644 index 00000000..95a5b7f9 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/serialization.h @@ -0,0 +1,1186 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "cista/aligned_alloc.h" +#include "cista/containers.h" +#include "cista/decay.h" +#include "cista/endian/conversion.h" +#include "cista/hash.h" +#include "cista/mode.h" +#include "cista/offset_t.h" +#include "cista/reflection/for_each_field.h" +#include "cista/serialized_size.h" +#include "cista/strong.h" +#include "cista/targets/buf.h" +#include "cista/targets/file.h" +#include "cista/type_hash/static_type_hash.h" +#include "cista/type_hash/type_hash.h" +#include "cista/unused_param.h" +#include "cista/verify.h" + +#ifndef cista_member_offset +#define cista_member_offset(Type, Member) \ + ([]() { \ + if constexpr (std::is_standard_layout_v) { \ + return static_cast<::cista::offset_t>(offsetof(Type, Member)); \ + } else { \ + return ::cista::member_offset(null(), &Type::Member); \ + } \ + }()) +#endif + +namespace cista { + +template +cista::offset_t member_offset(T const* t, Member const* m) { + static_assert(std::is_trivially_copyable_v); + return (reinterpret_cast(m) - + reinterpret_cast(t)); +} + +template +offset_t member_offset(T const* t, Member T::*m) { + static_assert(std::is_trivially_copyable_v); + return (reinterpret_cast(&(t->*m)) - + reinterpret_cast(t)); +} + +// ============================================================================= +// SERIALIZE +// ----------------------------------------------------------------------------- +struct pending_offset { + void const* origin_ptr_; + offset_t pos_; +}; + +struct vector_range { + bool contains(void const* begin, void const* ptr) const noexcept { + auto const ptr_int = reinterpret_cast(ptr); + auto const from = reinterpret_cast(begin); + auto const to = + reinterpret_cast(begin) + static_cast(size_); + return ptr_int >= from && ptr_int < to; + } + + offset_t offset_of(void const* begin, void const* ptr) const noexcept { + return start_ + reinterpret_cast(ptr) - + reinterpret_cast(begin); + } + + offset_t start_; + std::size_t size_; +}; + +template +struct serialization_context { + static constexpr auto const MODE = Mode; + + explicit serialization_context(Target& t) : t_{t} {} + + static bool compare(std::pair const& a, + std::pair const& b) noexcept { + return a.first < b.first; + } + + offset_t write(void const* ptr, std::size_t const size, + std::size_t const alignment = 0) { + return t_.write(ptr, size, alignment); + } + + template + void write(offset_t const pos, T const& val) { + t_.write(static_cast(pos), val); + } + + template + bool resolve_pointer(offset_ptr const& ptr, offset_t const pos, + bool const add_pending = true) { + return resolve_pointer(ptr.get(), pos, add_pending); + } + + template + bool resolve_pointer(Ptr ptr, offset_t const pos, + bool const add_pending = true) { + if (std::is_same_v>, void> && add_pending) { + write(pos, convert_endian(NULLPTR_OFFSET)); + return true; + } + if (ptr == nullptr) { + write(pos, convert_endian(NULLPTR_OFFSET)); + return true; + } + if (auto const it = offsets_.find(ptr_cast(ptr)); it != end(offsets_)) { + write(pos, convert_endian(it->second - pos)); + return true; + } + if (auto const offset = resolve_vector_range_ptr(ptr); offset.has_value()) { + write(pos, convert_endian(*offset - pos)); + return true; + } + if (add_pending) { + write(pos, convert_endian(NULLPTR_OFFSET)); + pending_.emplace_back(pending_offset{ptr_cast(ptr), pos}); + return true; + } + return false; + } + + template + std::optional resolve_vector_range_ptr(Ptr ptr) { + if (vector_ranges_.empty()) { + return std::nullopt; + } + auto const vec_it = vector_ranges_.upper_bound(ptr); + if (vec_it == begin(vector_ranges_)) { + return std::nullopt; + } + auto const pred = std::prev(vec_it); + return pred->second.contains(pred->first, ptr) + ? std::make_optional(pred->second.offset_of(pred->first, ptr)) + : std::nullopt; + } + + std::uint64_t checksum(offset_t const from) const noexcept { + return t_.checksum(from); + } + + cista::raw::hash_map offsets_; + std::map vector_ranges_; + std::vector pending_; + Target& t_; +}; + +template +void serialize(Ctx& c, T const* origin, offset_t const pos) { + using Type = decay_t; + if constexpr (std::is_union_v) { + static_assert(std::is_standard_layout_v && + std::is_trivially_copyable_v); + } else if constexpr (is_pointer_v) { + c.resolve_pointer(*origin, pos); + } else if constexpr (is_indexed_v) { + c.offsets_.emplace(origin, pos); + serialize(c, static_cast(origin), pos); + } else if constexpr (!std::is_scalar_v) { + static_assert(to_tuple_works_v, "Please implement custom serializer"); + for_each_ptr_field(*origin, [&](auto& member) { + auto const member_offset = + static_cast(reinterpret_cast(member) - + reinterpret_cast(origin)); + serialize(c, member, pos + member_offset); + }); + } else if constexpr (std::numeric_limits::is_integer) { + c.write(pos, convert_endian(*origin)); + } else { + CISTA_UNUSED_PARAM(origin) + CISTA_UNUSED_PARAM(pos) + } +} + +template typename Ptr, + bool Indexed, typename TemplateSizeType> +void serialize(Ctx& c, + basic_vector const* origin, + offset_t const pos) { + using Type = basic_vector; + + auto const size = serialized_size() * origin->used_size_; + auto const start = origin->empty() + ? NULLPTR_OFFSET + : c.write(static_cast(origin->el_), size, + std::alignment_of_v); + + c.write(pos + cista_member_offset(Type, el_), + convert_endian( + start == NULLPTR_OFFSET + ? start + : start - cista_member_offset(Type, el_) - pos)); + c.write(pos + cista_member_offset(Type, allocated_size_), + convert_endian(origin->used_size_)); + c.write(pos + cista_member_offset(Type, used_size_), + convert_endian(origin->used_size_)); + c.write(pos + cista_member_offset(Type, self_allocated_), false); + + if constexpr (Indexed) { + if (origin->el_ != nullptr) { + c.vector_ranges_.emplace(origin->el_, vector_range{start, size}); + } + } + + if (origin->el_ != nullptr) { + auto i = 0U; + for (auto it = start; it != start + static_cast(size); + it += serialized_size()) { + serialize(c, static_cast(origin->el_ + i++), it); + } + } +} + +template +void serialize(Ctx& c, generic_string const* origin, offset_t const pos) { + using Type = generic_string; + auto str_convert_endian = [](Ctx& ctx, offset_t const start, + typename Type::CharT const* str, + offset_t const size) -> void { + if constexpr (sizeof(typename Type::CharT) > 1) { + for (offset_t i = 0; i < size; ++i) { + ctx.write( + start + i * static_cast(sizeof(typename Type::CharT)), + convert_endian(str[i])); + } + } + }; + + if (origin->is_short()) { + str_convert_endian(c, pos + cista_member_offset(Type, s_.s_), origin->s_.s_, + static_cast(Type::short_length_limit)); + return; + } + + auto const start = + (origin->h_.ptr_ == nullptr) + ? NULLPTR_OFFSET + : c.write(origin->data(), + origin->size() * sizeof(typename Type::CharT)); + if (start != NULLPTR_OFFSET) { + str_convert_endian(c, start, origin->data(), + static_cast(origin->size())); + } + c.write(pos + cista_member_offset(Type, h_.ptr_), + convert_endian( + start == NULLPTR_OFFSET + ? start + : start - cista_member_offset(Type, h_.ptr_) - pos)); + c.write(pos + cista_member_offset(Type, h_.size_), + convert_endian(origin->h_.size_)); + c.write(pos + cista_member_offset(Type, h_.self_allocated_), false); +} + +template typename Vec, std::size_t Log2MaxEntriesPerBucket> +void serialize(Ctx& c, + dynamic_fws_multimap_base const* origin, + offset_t const pos) { + using Type = + dynamic_fws_multimap_base; + serialize(c, &origin->index_, pos + cista_member_offset(Type, index_)); + serialize(c, &origin->data_, pos + cista_member_offset(Type, data_)); + serialize(c, &origin->free_buckets_, + pos + cista_member_offset(Type, free_buckets_)); + serialize(c, &origin->element_count_, + pos + cista_member_offset(Type, element_count_)); +} + +template +void serialize(Ctx& c, generic_cstring const* origin, offset_t const pos) { + using Type = generic_cstring; + + if (origin->is_short()) { + return; + } + + const auto* data = origin->data(); + auto size = origin->size(); + std::string buf; + if (!origin->is_owning()) { + buf = origin->str(); + data = buf.data(); + size = buf.size(); + } + auto capacity = size + 1; + + auto const start = c.write(data, capacity); + c.write(pos + cista_member_offset(Type, h_.ptr_), + convert_endian(start - cista_member_offset(Type, h_.ptr_) - + pos)); + c.write(pos + cista_member_offset(Type, h_.size_), + convert_endian(origin->h_.size_)); + c.write(pos + cista_member_offset(Type, h_.self_allocated_), false); + c.write(pos + cista_member_offset(Type, h_.minus_one_), + static_cast(-1)); +} + +template +void serialize(Ctx& c, basic_string const* origin, offset_t const pos) { + serialize(c, static_cast const*>(origin), pos); +} + +template +void serialize(Ctx& c, basic_string_view const* origin, + offset_t const pos) { + serialize(c, static_cast const*>(origin), pos); +} + +template +void serialize(Ctx& c, basic_cstring const* origin, offset_t const pos) { + serialize(c, static_cast const*>(origin), pos); +} + +template +void serialize(Ctx& c, basic_unique_ptr const* origin, + offset_t const pos) { + using Type = basic_unique_ptr; + + auto const start = + origin->el_ == nullptr + ? NULLPTR_OFFSET + : c.write(origin->el_, serialized_size(), std::alignment_of_v); + + c.write(pos + cista_member_offset(Type, el_), + convert_endian( + start == NULLPTR_OFFSET + ? start + : start - cista_member_offset(Type, el_) - pos)); + c.write(pos + cista_member_offset(Type, self_allocated_), false); + + if (origin->el_ != nullptr) { + c.offsets_[origin->el_] = start; + serialize(c, ptr_cast(origin->el_), start); + } +} + +template typename Ptr, + typename GetKey, typename GetValue, typename Hash, typename Eq> +void serialize(Ctx& c, + hash_storage const* origin, + offset_t const pos) { + using Type = hash_storage; + + auto const start = + origin->entries_ == nullptr + ? NULLPTR_OFFSET + : c.write(origin->entries_, + static_cast( + origin->capacity_ * serialized_size() + + (origin->capacity_ + 1 + Type::WIDTH) * + sizeof(typename Type::ctrl_t)), + std::alignment_of_v); + auto const ctrl_start = + start == NULLPTR_OFFSET + ? c.write(Type::empty_group(), 16U * sizeof(typename Type::ctrl_t), + std::alignment_of_v) + : start + + static_cast(origin->capacity_ * serialized_size()); + + c.write(pos + cista_member_offset(Type, entries_), + convert_endian( + start == NULLPTR_OFFSET + ? start + : start - cista_member_offset(Type, entries_) - pos)); + c.write(pos + cista_member_offset(Type, ctrl_), + convert_endian( + ctrl_start == NULLPTR_OFFSET + ? ctrl_start + : ctrl_start - cista_member_offset(Type, ctrl_) - pos)); + + c.write(pos + cista_member_offset(Type, self_allocated_), false); + + c.write(pos + cista_member_offset(Type, size_), + convert_endian(origin->size_)); + c.write(pos + cista_member_offset(Type, capacity_), + convert_endian(origin->capacity_)); + c.write(pos + cista_member_offset(Type, growth_left_), + convert_endian(origin->growth_left_)); + + if (origin->entries_ != nullptr) { + auto i = 0u; + for (auto it = start; + it != start + static_cast(origin->capacity_ * + serialized_size()); + it += serialized_size(), ++i) { + if (Type::is_full(origin->ctrl_[i])) { + serialize(c, static_cast(origin->entries_ + i), it); + } + } + } +} + +template +void serialize(Ctx& c, std::chrono::duration const* origin, + offset_t const pos) { + static_assert(sizeof(origin->count() == sizeof(*origin))); + c.write(pos, convert_endian(origin->count())); +} + +template +void serialize(Ctx& c, std::chrono::time_point const* origin, + offset_t const pos) { + static_assert(sizeof(origin->time_since_epoch().count()) == sizeof(*origin)); + c.write(pos, convert_endian(origin->time_since_epoch().count())); +} + +template +void serialize(Ctx& c, bitset const* origin, offset_t const pos) { + serialize(c, &origin->blocks_, pos); +} + +template +void serialize(Ctx& c, array const* origin, offset_t const pos) { + auto const size = + static_cast(serialized_size() * origin->size()); + auto i = 0U; + for (auto it = pos; it != pos + size; it += serialized_size()) { + serialize(c, &(*origin)[i++], it); + } +} + +template +void serialize(Ctx& c, pair const* origin, offset_t const pos) { + using Type = decay_t; + serialize(c, &origin->first, pos + cista_member_offset(Type, first)); + serialize(c, &origin->second, pos + cista_member_offset(Type, second)); +} + +template +void serialize(Ctx& c, std::pair const* origin, offset_t const pos) { + using Type = decay_t; + serialize(c, &origin->first, pos + cista_member_offset(Type, first)); + serialize(c, &origin->second, pos + cista_member_offset(Type, second)); +} + +template +void serialize(Ctx& c, variant const* origin, offset_t const pos) { + using Type = decay_t; + c.write(pos + cista_member_offset(Type, idx_), + convert_endian(origin->idx_)); + auto const offset = cista_member_offset(Type, storage_); + origin->apply([&](auto&& t) { serialize(c, &t, pos + offset); }); +} + +template +void serialize(Ctx& c, optional const* origin, offset_t const pos) { + using Type = decay_t; + + if (origin->valid_) { + serialize(c, &origin->value(), pos + cista_member_offset(Type, storage_)); + } +} + +template +void serialize(Ctx& c, tuple const* origin, + cista::offset_t const offset) { + ::cista::apply( + [&](auto&&... args) { + (serialize(c, &args, + offset + (reinterpret_cast(&args) - + reinterpret_cast(origin))), + ...); + }, + *origin); +} + +template +void serialize(Ctx& c, strong const* origin, + cista::offset_t const offset) { + serialize(c, &origin->v_, offset); +} + +constexpr offset_t integrity_start(mode const m) noexcept { + offset_t start = 0; + if (is_mode_enabled(m, mode::WITH_VERSION) || + is_mode_enabled(m, mode::WITH_STATIC_VERSION) || + is_mode_enabled(m, mode::SKIP_VERSION)) { + start += sizeof(std::uint64_t); + } + return start; +} + +constexpr offset_t data_start(mode const m) noexcept { + auto start = integrity_start(m); + if (is_mode_enabled(m, mode::WITH_INTEGRITY) || + is_mode_enabled(m, mode::SKIP_INTEGRITY)) { + start += sizeof(std::uint64_t); + } + return start; +} + +template +void serialize(Target& t, T& value) { + serialization_context c{t}; + + if constexpr (is_mode_enabled(Mode, mode::WITH_VERSION) || + is_mode_enabled(Mode, mode::WITH_STATIC_VERSION)) { + static_assert(is_mode_enabled(Mode, mode::WITH_VERSION) ^ + is_mode_enabled(Mode, mode::WITH_STATIC_VERSION), + "WITH_VERSION cannot be combined with WITH_STATIC_VERSION"); + + if constexpr (is_mode_enabled(Mode, mode::WITH_VERSION)) { + auto const h = convert_endian(type_hash>()); + c.write(&h, sizeof(h)); + } else { + constexpr auto const type_hash = static_type_hash>(); + auto const h = convert_endian(type_hash); + c.write(&h, sizeof(h)); + } + } + + auto integrity_offset = offset_t{0}; + if constexpr (is_mode_enabled(Mode, mode::WITH_INTEGRITY)) { + auto const h = hash_t{}; + integrity_offset = c.write(&h, sizeof(h)); + } + + serialize(c, &value, + c.write(&value, serialized_size(), + std::alignment_of_v>)); + + for (auto& p : c.pending_) { + if (!c.resolve_pointer(p.origin_ptr_, p.pos_, false)) { + printf("warning: dangling pointer at %" PRI_O " (origin=%p)\n", p.pos_, + p.origin_ptr_); + } + } + + if constexpr (is_mode_enabled(Mode, mode::WITH_INTEGRITY)) { + auto const csum = + c.checksum(integrity_offset + static_cast(sizeof(hash_t))); + c.write(integrity_offset, convert_endian(csum)); + } +} + +template +byte_buf serialize(T& el) { + auto b = buf{}; + serialize(b, el); + return std::move(b.buf_); +} + +// ============================================================================= +// DESERIALIZE +// ----------------------------------------------------------------------------- +template +Arg checked_addition(Arg a1, Args... aN) { + using Type = decay_t; + + auto add_if_ok = [&](Arg const x) { + if (x == 0) { + return; + } + if (((x < 0) && (a1 < std::numeric_limits::min() - x)) || + ((x > 0) && (a1 > std::numeric_limits::max() - x))) { + throw_exception(std::overflow_error("addition overflow")); + } + a1 = a1 + x; + }; + (add_if_ok(aN), ...); + return a1; +} + +template +Arg checked_multiplication(Arg a1, Args... aN) { + using Type = decay_t; + auto multiply_if_ok = [&](auto x) { + if (a1 != 0 && ((std::numeric_limits::max() / a1) < x)) { + throw_exception(std::overflow_error("addition overflow")); + } + a1 = a1 * x; + }; + (multiply_if_ok(aN), ...); + return a1; +} + +template +struct deserialization_context { + static constexpr auto const MODE = Mode; + + deserialization_context(std::uint8_t const* from, std::uint8_t const* to) + : from_{reinterpret_cast(from)}, + to_{reinterpret_cast(to)} {} + + template + void convert_endian(T& el) const { + if constexpr (endian_conversion_necessary()) { + el = ::cista::convert_endian(el); + } + } + + template + void deserialize_ptr(Ptr** ptr) const { + auto const offset = + reinterpret_cast(::cista::convert_endian(*ptr)); + static_assert(is_mode_disabled(MODE, mode::_CONST), + "raw pointer deserialize is not const"); + *ptr = offset == NULLPTR_OFFSET + ? nullptr + : reinterpret_cast( + checked_addition(reinterpret_cast(ptr), offset)); + } + + template + constexpr static std::size_t type_size() noexcept { + using Type = decay_t; + if constexpr (std::is_same_v) { + return 0U; + } else { + return sizeof(Type); + } + } + + template + void check_ptr(offset_ptr const& el, + std::size_t const size = type_size()) const { + if (el != nullptr) { + checked_addition(el.offset_, reinterpret_cast(&el)); + check_ptr(el.get(), size); + } + } + + template + void check_ptr(T* el, std::size_t const size = type_size()) const { + if constexpr ((MODE & mode::UNCHECKED) == mode::UNCHECKED) { + return; + } + + if (el == nullptr || to_ == 0U) { + return; + } + + auto const pos = reinterpret_cast(el); + verify(pos >= from_, "underflow"); + verify(checked_addition(pos, static_cast(size)) <= to_, + "overflow"); + verify( + size < static_cast(std::numeric_limits::max()), + "size out of bounds"); + + if constexpr (!std::is_same_v, void>) { + verify((pos & static_cast(std::alignment_of>() - + 1U)) == 0U, + "ptr alignment"); + } + } + + static void check_bool(bool const& b) { + auto const val = *reinterpret_cast(&b); + verify(val <= 1U, "valid bool"); + } + + void require(bool condition, char const* msg) const { + if constexpr ((MODE & mode::UNCHECKED) == mode::UNCHECKED) { + return; + } + + verify(condition, msg); + } + + intptr_t from_, to_; +}; + +template +struct deep_check_context : public deserialization_context { + using parent = deserialization_context; + + using parent::parent; + + template + bool add_checked(T const* v) const { + return checked_.emplace(type_hash(), static_cast(v)).second; + } + + std::set> mutable checked_; +}; + +template +void check(std::uint8_t const* const from, std::uint8_t const* const to) { + verify(to - from > data_start(Mode), "invalid range"); + + if constexpr ((Mode & mode::WITH_VERSION) == mode::WITH_VERSION) { + verify(convert_endian(*reinterpret_cast(from)) == + type_hash(), + "invalid version"); + } else if constexpr ((Mode & mode::WITH_STATIC_VERSION) == + mode::WITH_STATIC_VERSION) { + verify(convert_endian(*reinterpret_cast(from)) == + static_type_hash(), + "invalid static version"); + } + + if constexpr ((Mode & mode::WITH_INTEGRITY) == mode::WITH_INTEGRITY) { + verify(convert_endian(*reinterpret_cast( + from + integrity_start(Mode))) == + hash(std::string_view{ + reinterpret_cast(from + data_start(Mode)), + static_cast(to - from - data_start(Mode))}), + "invalid checksum"); + } +} + +// --- GENERIC --- +template +void convert_endian_and_ptr(Ctx const& c, T* el) { + using Type = decay_t; + if constexpr (std::is_pointer_v) { + c.deserialize_ptr(el); + } else if constexpr (std::numeric_limits::is_integer) { + c.convert_endian(*el); + } else { + CISTA_UNUSED_PARAM(c) + CISTA_UNUSED_PARAM(el) + } +} + +template +void check_state(Ctx const& c, T* el) { + using Type = decay_t; + if constexpr (std::is_pointer_v && !std::is_same_v) { + c.check_ptr(*el); + } else { + CISTA_UNUSED_PARAM(c) + CISTA_UNUSED_PARAM(el) + } +} + +template +void recurse(Ctx& c, T* el, Fn&& fn) { + using Type = decay_t; + if constexpr (is_indexed_v) { + fn(static_cast(el)); + } else if constexpr (to_tuple_works_v) { + for_each_ptr_field(*el, [&](auto& f) { fn(f); }); + } else if constexpr (is_mode_enabled(Ctx::MODE, mode::_PHASE_II) && + std::is_pointer_v) { + if (*el != nullptr && c.add_checked(el)) { + fn(*el); + } + } else { + CISTA_UNUSED_PARAM(c) + CISTA_UNUSED_PARAM(el) + CISTA_UNUSED_PARAM(fn) + } +} + +template +void deserialize(Ctx const& c, T* el) { + c.check_ptr(el); + if constexpr (is_mode_disabled(Ctx::MODE, mode::_PHASE_II)) { + convert_endian_and_ptr(c, el); + } + if constexpr (is_mode_disabled(Ctx::MODE, mode::UNCHECKED)) { + check_state(c, el); + } + recurse(c, el, [&](auto* entry) { deserialize(c, entry); }); +} + +// --- PAIR --- +template +void recurse(Ctx&, pair* el, Fn&& fn) { + fn(&el->first); + fn(&el->second); +} + +template +void recurse(Ctx&, std::pair* el, Fn&& fn) { + fn(&el->first); + fn(&el->second); +} + +// --- OFFSET_PTR --- +template +void convert_endian_and_ptr(Ctx const& c, offset_ptr* el) { + c.convert_endian(el->offset_); +} + +template +void check_state(Ctx const& c, offset_ptr* el) { + c.check_ptr(*el); +} + +template +void recurse(Ctx& c, offset_ptr* el, Fn&& fn) { + if constexpr (is_mode_enabled(Ctx::MODE, mode::_PHASE_II)) { + if (*el != nullptr && c.add_checked(el)) { + fn(static_cast(*el)); + } + } else { + CISTA_UNUSED_PARAM(c) + CISTA_UNUSED_PARAM(el) + CISTA_UNUSED_PARAM(fn) + } +} + +// --- VECTOR --- +template typename Ptr, + bool Indexed, typename TemplateSizeType> +void convert_endian_and_ptr( + Ctx const& c, basic_vector* el) { + deserialize(c, &el->el_); + c.convert_endian(el->allocated_size_); + c.convert_endian(el->used_size_); +} + +template typename Ptr, + bool Indexed, typename TemplateSizeType> +void check_state(Ctx const& c, + basic_vector* el) { + c.check_ptr(el->el_, + checked_multiplication( + static_cast(el->allocated_size_), sizeof(T))); + c.check_bool(el->self_allocated_); + c.require(!el->self_allocated_, "vec self-allocated"); + c.require(el->allocated_size_ == el->used_size_, "vec size mismatch"); + c.require((el->size() == 0U) == (el->el_ == nullptr), "vec size=0 <=> ptr=0"); +} + +template typename Ptr, + bool Indexed, typename TemplateSizeType, typename Fn> +void recurse(Ctx&, basic_vector* el, + Fn&& fn) { + for (auto& m : *el) { // NOLINT(clang-analyzer-core.NullDereference) + fn(&m); + } +} + +// --- STRING --- +template +void convert_endian_and_ptr(Ctx const& c, generic_string* el) { + if (*reinterpret_cast(&el->s_.is_short_) == 0U) { + deserialize(c, &el->h_.ptr_); + c.convert_endian(el->h_.size_); + } +} + +template +void check_state(Ctx const& c, generic_string* el) { + c.check_bool(el->s_.is_short_); + if (!el->is_short()) { + c.check_ptr(el->h_.ptr_, + el->h_.size_ * sizeof(typename generic_string::CharT)); + c.check_bool(el->h_.self_allocated_); + c.require(!el->h_.self_allocated_, "string self-allocated"); + c.require((el->h_.size_ == 0) == (el->h_.ptr_ == nullptr), + "str size=0 <=> ptr=0"); + } +} + +template +void recurse(Ctx&, generic_string* el, Fn&& fn) { + using Type = generic_string; + if constexpr (sizeof(typename Type::CharT) > 1) { + typename Type::CharT* s = el->data(); + std::size_t const size = static_cast(el->size()); + for (std::size_t i = 0; i < size; ++i) { + fn(&s[i]); + } + } +} + +template +void convert_endian_and_ptr(Ctx const& c, basic_string* el) { + convert_endian_and_ptr(c, static_cast*>(el)); +} + +template +void check_state(Ctx const& c, basic_string* el) { + check_state(c, static_cast*>(el)); +} + +template +void recurse(Ctx& c, basic_string* el, Fn&& fn) { + recurse(c, static_cast*>(el), fn); +} + +template +void convert_endian_and_ptr(Ctx const& c, basic_string_view* el) { + convert_endian_and_ptr(c, static_cast*>(el)); +} + +template +void check_state(Ctx const& c, basic_string_view* el) { + check_state(c, static_cast*>(el)); +} + +template +void recurse(Ctx& c, basic_string_view* el, Fn&& fn) { + recurse(c, static_cast*>(el), fn); +} + +// --- UNIQUE_PTR --- +template +void convert_endian_and_ptr(Ctx const& c, basic_unique_ptr* el) { + deserialize(c, &el->el_); +} + +template +void check_state(Ctx const& c, basic_unique_ptr* el) { + c.check_bool(el->self_allocated_); + c.require(!el->self_allocated_, "unique_ptr self-allocated"); +} + +template +void recurse(Ctx&, basic_unique_ptr* el, Fn&& fn) { + if (el->el_ != nullptr) { + fn(static_cast(el->el_)); + } +} + +// --- MUTABLE_FWS_MULTIMAP --- +template typename Vec, std::size_t Log2MaxEntriesPerBucket, + typename Fn> +void recurse( + Ctx&, + dynamic_fws_multimap_base* el, + Fn&& fn) { + fn(&el->index_); + fn(&el->data_); + fn(&el->free_buckets_); + fn(&el->element_count_); +} + +// --- HASH_STORAGE --- +template typename Ptr, + typename GetKey, typename GetValue, typename Hash, typename Eq> +void convert_endian_and_ptr( + Ctx const& c, hash_storage* el) { + deserialize(c, &el->entries_); + deserialize(c, &el->ctrl_); + c.convert_endian(el->size_); + c.convert_endian(el->capacity_); + c.convert_endian(el->growth_left_); +} + +template typename Ptr, + typename GetKey, typename GetValue, typename Hash, typename Eq> +void check_state(Ctx const& c, + hash_storage* el) { + using Type = decay_t>; + c.require(el->ctrl_ != nullptr, "hash storage: ctrl must be set"); + c.check_ptr( + el->entries_, + checked_addition( + checked_multiplication( + el->capacity_, static_cast(sizeof(T))), + checked_addition(el->capacity_, 1U, Type::WIDTH))); + c.check_ptr(el->ctrl_, checked_addition(el->capacity_, 1U, Type::WIDTH)); + c.require( + el->entries_ == nullptr || + reinterpret_cast(ptr_cast(el->ctrl_)) == + reinterpret_cast(ptr_cast(el->entries_)) + + checked_multiplication( + static_cast(el->capacity_), sizeof(T)), + "hash storage: entries!=null -> ctrl = entries+capacity"); + c.require( + (el->entries_ == nullptr) == (el->capacity_ == 0U && el->size_ == 0U), + "hash storage: entries=null <=> size=capacity=0"); + + c.check_bool(el->self_allocated_); + c.require(!el->self_allocated_, "hash storage: self-allocated"); + + c.require(el->ctrl_[el->capacity_] == Type::END, + "hash storage: end ctrl byte"); + c.require(std::all_of(ptr_cast(el->ctrl_), + ptr_cast(el->ctrl_) + el->capacity_ + 1U + Type::WIDTH, + [](typename Type::ctrl_t const ctrl) { + return Type::is_empty(ctrl) || + Type::is_deleted(ctrl) || + Type::is_full(ctrl) || + ctrl == Type::ctrl_t::END; + }), + "hash storage: ctrl bytes must be empty or deleted or full"); + + using st_t = typename Type::size_type; + auto [total_empty, total_full, total_deleted, total_growth_left] = + std::accumulate( + ptr_cast(el->ctrl_), ptr_cast(el->ctrl_) + el->capacity_, + std::tuple{st_t{0U}, st_t{0U}, st_t{0U}, st_t{0}}, + [&](std::tuple const acc, + typename Type::ctrl_t const& ctrl) { + auto const [empty, full, deleted, growth_left] = acc; + return std::tuple{ + Type::is_empty(ctrl) ? empty + 1 : empty, + Type::is_full(ctrl) ? full + 1 : full, + Type::is_deleted(ctrl) ? deleted + 1 : deleted, + (Type::is_empty(ctrl) && el->was_never_full(static_cast( + &ctrl - el->ctrl_)) + ? growth_left + 1 + : growth_left)}; + }); + + c.require(el->size_ == total_full, "hash storage: size"); + c.require(total_empty + total_full + total_deleted == el->capacity_, + "hash storage: empty + full + deleted = capacity"); + c.require(std::min(Type::capacity_to_growth(el->capacity_) - el->size_, + total_growth_left) <= el->growth_left_, + "hash storage: growth left"); +} + +template typename Ptr, + typename GetKey, typename GetValue, typename Hash, typename Eq, + typename Fn> +void recurse(Ctx&, hash_storage* el, + Fn&& fn) { + for (auto& m : *el) { + fn(&m); + } +} + +// --- BITSET --- +template +void recurse(Ctx&, bitset* el, Fn&& fn) { + fn(&el->blocks_); +} + +// --- ARRAY --- +template +void recurse(Ctx&, array* el, Fn&& fn) { + for (auto& m : *el) { + fn(&m); + } +} + +// --- VARIANT --- +template +void convert_endian_and_ptr(Ctx const& c, variant* el) { + c.convert_endian(el->idx_); +} + +template +void recurse(Ctx&, variant* el, Fn&& fn) { + el->apply([&](auto&& t) { fn(&t); }); +} + +template +void check_state(Ctx const& c, variant* el) { + c.require(el->index() < sizeof...(T), "variant index"); +} + +// --- OPTIONAL --- +template +void check_state(Ctx const& c, optional* el) { + c.check_bool(el->valid_); +} + +template +void recurse(Ctx&, optional* el, Fn&& fn) { + if (el->valid_) { + fn(&(el->value())); + } +} + +// --- TUPLE --- +template +void recurse(Ctx const& c, tuple* el) { + apply([&](auto&&... args) { (deserialize(c, &args), ...); }, *el); +} + +// --- TIMEPOINT --- +template +void convert_endian_and_ptr(Ctx const& c, + std::chrono::time_point* el) { + c.convert_endian(*reinterpret_cast(el)); +} + +// --- DURATION --- +template +void convert_endian_and_ptr(Ctx const& c, + std::chrono::duration* el) { + c.convert_endian(*reinterpret_cast(el)); +} + +template +T* deserialize(std::uint8_t* from, std::uint8_t* to = nullptr) { + if constexpr (is_mode_enabled(Mode, mode::CAST)) { + CISTA_UNUSED_PARAM(to) + return reinterpret_cast(from); + } else { + check(from, to); + auto const el = reinterpret_cast(from + data_start(Mode)); + + deserialization_context c{from, to}; + deserialize(c, el); + + if constexpr ((Mode & mode::DEEP_CHECK) == mode::DEEP_CHECK) { + deep_check_context c1{from, to}; + deserialize(c1, el); + } + + return el; + } +} + +template +T const* deserialize(std::uint8_t const* from, + std::uint8_t const* to = nullptr) { + static_assert(!endian_conversion_necessary(), "cannot be const"); + return deserialize(const_cast(from), + const_cast(to)); +} + +template +T const* deserialize(CharT const* from, CharT const* to = nullptr) { + static_assert(sizeof(CharT) == 1U, "byte size entries"); + return deserialize(reinterpret_cast(from), + reinterpret_cast(to)); +} + +template +T* deserialize(CharT* from, CharT* to = nullptr) { + static_assert(sizeof(CharT) == 1U, "byte size entries"); + return deserialize(reinterpret_cast(from), + reinterpret_cast(to)); +} + +template +T const* deserialize(std::string_view c) { + return deserialize(&c[0], &c[0] + c.size()); +} + +template +auto deserialize(Container& c) { + return deserialize(&c[0], &c[0] + c.size()); +} + +template +T* unchecked_deserialize(std::uint8_t* from, std::uint8_t* to = nullptr) { + return deserialize(from, to); +} + +template +T* unchecked_deserialize(Container& c) { + return unchecked_deserialize(&c[0], &c[0] + c.size()); +} + +template +T copy_from_potentially_unaligned(std::string_view buf) { + struct aligned { + explicit aligned(std::string_view buf) + : mem_{static_cast( + CISTA_ALIGNED_ALLOC(sizeof(max_align_t), buf.size()))} { + verify(mem_ != nullptr, "failed to allocate aligned memory"); + std::memcpy(mem_, buf.data(), buf.size()); + } + ~aligned() { CISTA_ALIGNED_FREE(sizeof(max_align_t), mem_); } + std::uint8_t* mem_; + }; + + auto const is_already_aligned = + (reinterpret_cast(buf.data()) % sizeof(max_align_t)) == + 0U; + if (is_already_aligned) { + return *deserialize(buf); + } + auto copy = aligned{buf}; + return *deserialize(copy.mem_, copy.mem_ + buf.size()); +} + +namespace raw { +using cista::deserialize; +using cista::unchecked_deserialize; +} // namespace raw + +namespace offset { +using cista::deserialize; +using cista::unchecked_deserialize; +} // namespace offset + +} // namespace cista + +#undef cista_member_offset diff --git a/parallel/parallel_src/extern/cista/include/cista/serialized_size.h b/parallel/parallel_src/extern/cista/include/cista/serialized_size.h new file mode 100644 index 00000000..1f95ae91 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/serialized_size.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "cista/decay.h" + +namespace cista { + +template +static constexpr std::size_t serialized_size( + void* const param = nullptr) noexcept { + static_cast(param); + return sizeof(decay_t); +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/strong.h b/parallel/parallel_src/extern/cista/include/cista/strong.h new file mode 100644 index 00000000..b2bfbe8d --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/strong.h @@ -0,0 +1,207 @@ +#pragma once + +#include +#include +#include +#include + +namespace cista { + +template +struct strong { + using value_t = T; + + constexpr strong() = default; + + explicit constexpr strong(T const& v) noexcept( + std::is_nothrow_copy_constructible_v) + : v_{v} {} + explicit constexpr strong(T&& v) noexcept( + std::is_nothrow_move_constructible_v) + : v_{std::move(v)} {} + + template +#if _MSVC_LANG >= 202002L || __cplusplus >= 202002L + requires std::is_integral_v> && + std::is_integral_v> +#endif + explicit constexpr strong(X&& x) : v_{static_cast(x)} { + } + + constexpr strong(strong&& o) noexcept( + std::is_nothrow_move_constructible_v) = default; + constexpr strong& operator=(strong&& o) noexcept( + std::is_nothrow_move_constructible_v) = default; + + constexpr strong(strong const& o) = default; + constexpr strong& operator=(strong const& o) = default; + + static constexpr strong invalid() { + return strong{std::numeric_limits::max()}; + } + + constexpr strong& operator++() { + ++v_; + return *this; + } + + constexpr strong operator++(int) { + auto cpy = *this; + ++v_; + return cpy; + } + + constexpr strong& operator--() { + --v_; + return *this; + } + + constexpr const strong operator--(int) { + auto cpy = *this; + --v_; + return cpy; + } + + constexpr strong operator+(strong const& s) const { + return strong{static_cast(v_ + s.v_)}; + } + constexpr strong operator-(strong const& s) const { + return strong{static_cast(v_ - s.v_)}; + } + constexpr strong operator*(strong const& s) const { + return strong{static_cast(v_ * s.v_)}; + } + constexpr strong operator/(strong const& s) const { + return strong{static_cast(v_ / s.v_)}; + } + constexpr strong operator+(T const& i) const { + return strong{static_cast(v_ + i)}; + } + constexpr strong operator-(T const& i) const { + return strong{static_cast(v_ - i)}; + } + constexpr strong operator*(T const& i) const { + return strong{static_cast(v_ * i)}; + } + constexpr strong operator/(T const& i) const { + return strong{static_cast(v_ / i)}; + } + + constexpr strong& operator+=(T const& i) { + v_ += i; + return *this; + } + constexpr strong& operator-=(T const& i) { + v_ -= i; + return *this; + } + + constexpr strong operator>>(T const& i) const { + return strong{static_cast(v_ >> i)}; + } + constexpr strong operator<<(T const& i) const { + return strong{static_cast(v_ << i)}; + } + constexpr strong operator>>(strong const& o) const { return v_ >> o.v_; } + constexpr strong operator<<(strong const& o) const { return v_ << o.v_; } + + constexpr strong& operator|=(strong const& o) { + v_ |= o.v_; + return *this; + } + constexpr strong& operator&=(strong const& o) { + v_ &= o.v_; + return *this; + } + + constexpr bool operator==(strong const& o) const { return v_ == o.v_; } + constexpr bool operator!=(strong const& o) const { return v_ != o.v_; } + constexpr bool operator<=(strong const& o) const { return v_ <= o.v_; } + constexpr bool operator>=(strong const& o) const { return v_ >= o.v_; } + constexpr bool operator<(strong const& o) const { return v_ < o.v_; } + constexpr bool operator>(strong const& o) const { return v_ > o.v_; } + + constexpr bool operator==(T const& o) const { return v_ == o; } + constexpr bool operator!=(T const& o) const { return v_ != o; } + constexpr bool operator<=(T const& o) const { return v_ <= o; } + constexpr bool operator>=(T const& o) const { return v_ >= o; } + constexpr bool operator<(T const& o) const { return v_ < o; } + constexpr bool operator>(T const& o) const { return v_ > o; } + + explicit operator T const&() const& noexcept { return v_; } + + friend std::ostream& operator<<(std::ostream& o, strong const& t) { + return o << t.v_; + } + + T v_; +}; + +template +struct is_strong : std::false_type {}; + +template +struct is_strong> : std::true_type {}; + +template +constexpr auto const is_strong_v = is_strong::value; + +template +inline constexpr typename strong::value_t to_idx( + strong const& s) { + return s.v_; +} + +template +struct base_type { + using type = T; +}; + +template +struct base_type> { + using type = T; +}; + +template +using base_t = typename base_type::type; + +template +T to_idx(T const& t) { + return t; +} + +} // namespace cista + +#include + +namespace std { + +template +class numeric_limits> { +public: + static constexpr cista::strong min() noexcept { + return cista::strong{std::numeric_limits::min()}; + } + static constexpr cista::strong max() noexcept { + return cista::strong{std::numeric_limits::max()}; + } + static constexpr bool is_integer = std::is_integral_v; +}; + +template +struct hash> { + size_t operator()(cista::strong const& t) const { + return hash{}(t.v_); + } +}; + +} // namespace std + +#if __has_include("fmt/ostream.h") + +#include "fmt/ostream.h" + +template +struct fmt::formatter> : ostream_formatter {}; + +#endif diff --git a/parallel/parallel_src/extern/cista/include/cista/targets/buf.h b/parallel/parallel_src/extern/cista/include/cista/targets/buf.h new file mode 100644 index 00000000..3a37a899 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/targets/buf.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include + +#include "cista/hash.h" +#include "cista/offset_t.h" +#include "cista/serialized_size.h" +#include "cista/verify.h" + +namespace cista { + +using byte_buf = std::vector; + +template +struct buf { + buf() = default; + explicit buf(Buf&& buf) : buf_{std::forward(buf)} {} + + std::uint8_t* addr(offset_t const offset) noexcept { + return (&buf_[0U]) + offset; + } + std::uint8_t* base() noexcept { return &buf_[0U]; } + + std::uint64_t checksum(offset_t const start = 0U) const noexcept { + return hash(std::string_view{ + reinterpret_cast(&buf_[static_cast(start)]), + buf_.size() - static_cast(start)}); + } + + template + void write(std::size_t const pos, T const& val) { + verify(buf_.size() >= pos + serialized_size(), "out of bounds write"); + std::memcpy(&buf_[pos], &val, serialized_size()); + } + + offset_t write(void const* ptr, std::size_t const num_bytes, + std::size_t alignment = 0U) { + auto start = static_cast(size()); + if (alignment > 1U && buf_.size() != 0U) { + auto unaligned_ptr = static_cast(addr(start)); + auto space = std::numeric_limits::max(); + auto const aligned_ptr = + std::align(alignment, num_bytes, unaligned_ptr, space); + auto const new_offset = static_cast( + aligned_ptr ? static_cast(aligned_ptr) - base() : 0U); + auto const adjustment = static_cast(new_offset - start); + start += adjustment; + } + + auto const space_left = + static_cast(buf_.size()) - static_cast(start); + if (space_left < static_cast(num_bytes)) { + auto const missing = static_cast( + static_cast(num_bytes) - space_left); + buf_.resize(buf_.size() + missing); + } + std::memcpy(addr(start), ptr, num_bytes); + + return start; + } + + std::uint8_t& operator[](std::size_t const i) noexcept { return buf_[i]; } + std::uint8_t const& operator[](std::size_t const i) const noexcept { + return buf_[i]; + } + std::size_t size() const noexcept { return buf_.size(); } + void reset() { buf_.resize(0U); } + + Buf buf_; +}; + +template +buf(Buf&&) -> buf; + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/targets/file.h b/parallel/parallel_src/extern/cista/include/cista/targets/file.h new file mode 100644 index 00000000..90319044 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/targets/file.h @@ -0,0 +1,326 @@ +#pragma once + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#endif + +#include +#include + +#include "cista/buffer.h" +#include "cista/chunk.h" +#include "cista/hash.h" +#include "cista/offset_t.h" +#include "cista/serialized_size.h" +#include "cista/targets/file.h" +#include "cista/verify.h" + +#ifdef _WIN32 +namespace cista { + +inline std::string last_error_str() { + auto const err = ::GetLastError(); + if (err == 0) { + return "no error"; + } + + struct buf { + ~buf() { + if (b_ != nullptr) { + LocalFree(b_); + b_ = nullptr; + } + } + LPSTR b_ = nullptr; + } b; + auto const size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), b.b_, 0, + nullptr); + + return size == 0 ? std::to_string(err) : std::string{b.b_, size}; +} + +inline HANDLE open_file(char const* path, char const* mode) { + bool read = std::strcmp(mode, "r") == 0; + bool write = std::strcmp(mode, "w+") == 0 || std::strcmp(mode, "r+") == 0; + + verify(read || write, "open file mode not supported"); + + DWORD access = read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE; + DWORD create_mode = read ? OPEN_EXISTING : CREATE_ALWAYS; + + auto const f = CreateFileA(path, access, FILE_SHARE_READ, nullptr, + create_mode, FILE_ATTRIBUTE_NORMAL, nullptr); + if (f == INVALID_HANDLE_VALUE) { + throw_exception(std::runtime_error{std::string{"cannot open path="} + path + + ", mode=" + mode + ", message=\"" + + last_error_str() + "\""}); + } + return f; +} + +struct file { + file() = default; + + file(char const* path, char const* mode) + : f_(open_file(path, mode)), size_{size()} {} + + ~file() { + if (f_ != nullptr) { + CloseHandle(f_); + } + f_ = nullptr; + } + + file(file const&) = delete; + file& operator=(file const&) = delete; + + file(file&& o) : f_{o.f_}, size_{o.size_} { + o.f_ = nullptr; + o.size_ = 0U; + } + + file& operator=(file&& o) { + f_ = o.f_; + size_ = o.size_; + o.f_ = nullptr; + o.size_ = 0U; + return *this; + } + + std::size_t size() const { + if (f_ == nullptr) { + return 0U; + } + LARGE_INTEGER filesize; + verify(GetFileSizeEx(f_, &filesize), "file size error"); + return static_cast(filesize.QuadPart); + } + + buffer content() const { + constexpr auto block_size = 8192u; + std::size_t const file_size = size(); + + auto b = buffer(file_size); + + chunk(block_size, size(), [&](std::size_t const from, unsigned block_size) { + OVERLAPPED overlapped = {0}; + overlapped.Offset = static_cast(from); +#ifdef _WIN64 + overlapped.OffsetHigh = static_cast(from >> 32u); +#endif + ReadFile(f_, b.data() + from, static_cast(block_size), nullptr, + &overlapped); + }); + + return b; + } + + std::uint64_t checksum(offset_t const start = 0) const { + constexpr auto const block_size = 512U * 1024U; // 512kB + auto c = BASE_HASH; + char buf[block_size]; + chunk(block_size, size_ - static_cast(start), + [&](auto const from, auto const size) { + OVERLAPPED overlapped = {0}; + overlapped.Offset = static_cast(start + from); +#ifdef _WIN64 + overlapped.OffsetHigh = static_cast((start + from) >> 32U); +#endif + DWORD bytes_read = {0}; + verify(ReadFile(f_, buf, static_cast(size), &bytes_read, + &overlapped), + "checksum read error"); + verify(bytes_read == size, "checksum read error bytes read"); + c = hash(std::string_view{buf, size}, c); + }); + return c; + } + + template + void write(std::size_t const pos, T const& val) { + OVERLAPPED overlapped = {0}; + overlapped.Offset = static_cast(pos); +#ifdef _WIN64 + overlapped.OffsetHigh = pos >> 32u; +#endif + DWORD bytes_written = {0}; + verify(WriteFile(f_, &val, sizeof(T), &bytes_written, &overlapped), + "write(pos, val) write error"); + verify(bytes_written == sizeof(T), + "write(pos, val) write error bytes written"); + } + + offset_t write(void const* ptr, std::size_t const size, + std::size_t alignment) { + auto curr_offset = size_; + if (alignment != 0 && alignment != 1) { + auto unaligned_ptr = reinterpret_cast(size_); + auto space = std::numeric_limits::max(); + auto const aligned_ptr = + std::align(alignment, size, unaligned_ptr, space); + curr_offset = aligned_ptr ? reinterpret_cast(aligned_ptr) + : curr_offset; + } + + std::uint8_t const buf[16U] = {0U}; + auto const num_padding_bytes = static_cast(curr_offset - size_); + if (num_padding_bytes != 0U) { + verify(num_padding_bytes < 16U, "invalid padding size"); + OVERLAPPED overlapped = {0}; + overlapped.Offset = static_cast(size_); +#ifdef _WIN64 + overlapped.OffsetHigh = static_cast(size_ >> 32u); +#endif + DWORD bytes_written = {0}; + verify(WriteFile(f_, buf, num_padding_bytes, &bytes_written, &overlapped), + "write padding error"); + verify(bytes_written == num_padding_bytes, + "write padding error bytes written"); + size_ = curr_offset; + } + + constexpr auto block_size = 8192u; + chunk(block_size, size, [&](std::size_t const from, unsigned block_size) { + OVERLAPPED overlapped = {0}; + overlapped.Offset = 0xFFFFFFFF; + overlapped.OffsetHigh = 0xFFFFFFFF; + DWORD bytes_written = {0}; + verify(WriteFile(f_, reinterpret_cast(ptr) + from, + block_size, &bytes_written, &overlapped), + "write error"); + verify(bytes_written == block_size, "write error bytes written"); + }); + + auto const offset = size_; + size_ += size; + + return offset; + } + + HANDLE f_{nullptr}; + std::size_t size_{0U}; +}; +} // namespace cista +#else + +#include + +#include + +namespace cista { + +struct file { + file() = default; + + file(char const* path, char const* mode) + : f_{std::fopen(path, mode)}, size_{size()} { + verify(f_ != nullptr, "unable to open file"); + } + + ~file() { + if (f_ != nullptr) { + std::fclose(f_); + } + f_ = nullptr; + } + + file(file const&) = delete; + file& operator=(file const&) = delete; + + file(file&& o) : f_{o.f_}, size_{o.size_} { + o.f_ = nullptr; + o.size_ = 0U; + } + + file& operator=(file&& o) { + f_ = o.f_; + size_ = o.size_; + o.f_ = nullptr; + o.size_ = 0U; + return *this; + } + + int fd() const { + auto const fd = fileno(f_); + verify(fd != -1, "invalid fd"); + return fd; + } + + std::size_t size() const { + if (f_ == nullptr) { + return 0U; + } + struct stat s; + verify(fstat(fd(), &s) != -1, "fstat error"); + return static_cast(s.st_size); + } + + buffer content() { + auto b = buffer(size()); + verify(std::fread(b.data(), 1U, b.size(), f_) == b.size(), "read error"); + return b; + } + + std::uint64_t checksum(offset_t const start = 0) const { + constexpr auto const block_size = + static_cast(512U * 1024U); // 512kB + verify(size_ >= static_cast(start), "invalid checksum offset"); + verify(!std::fseek(f_, static_cast(start), SEEK_SET), "fseek error"); + auto c = BASE_HASH; + char buf[block_size]; + chunk(block_size, size_ - static_cast(start), + [&](auto const, auto const s) { + verify(std::fread(buf, 1U, s, f_) == s, "invalid read"); + c = hash(std::string_view{buf, s}, c); + }); + return c; + } + + template + void write(std::size_t const pos, T const& val) { + verify(!std::fseek(f_, static_cast(pos), SEEK_SET), "seek error"); + verify(std::fwrite(reinterpret_cast(&val), 1U, + serialized_size(), f_) == serialized_size(), + "write error"); + } + + offset_t write(void const* ptr, std::size_t const size, + std::size_t alignment) { + auto curr_offset = size_; + auto seek_offset = long{0}; + auto seek_whence = int{SEEK_END}; + if (alignment > 1U) { + auto unaligned_ptr = reinterpret_cast(size_); + auto space = std::numeric_limits::max(); + auto const aligned_ptr = + std::align(alignment, size, unaligned_ptr, space); + if (aligned_ptr != nullptr) { + curr_offset = reinterpret_cast(aligned_ptr); + } + seek_offset = static_cast(curr_offset); + seek_whence = SEEK_SET; + } + verify(!std::fseek(f_, seek_offset, seek_whence), "seek error"); + verify(std::fwrite(ptr, 1U, size, f_) == size, "write error"); + size_ = curr_offset + size; + return static_cast(curr_offset); + } + + FILE* f_{nullptr}; + std::size_t size_{0U}; +}; + +} // namespace cista + +#endif diff --git a/parallel/parallel_src/extern/cista/include/cista/type_hash/static_type_hash.h b/parallel/parallel_src/extern/cista/include/cista/type_hash/static_type_hash.h new file mode 100644 index 00000000..d45a32d7 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/type_hash/static_type_hash.h @@ -0,0 +1,269 @@ +#pragma once + +#include "cista/containers.h" +#include "cista/decay.h" +#include "cista/hash.h" +#include "cista/indexed.h" +#include "cista/reflection/to_tuple.h" +#include "cista/type_hash/type_name.h" + +namespace cista { + +template +constexpr hash_t static_type2str_hash() noexcept { + return hash_combine(hash(type_str>()), sizeof(T)); +} + +template +constexpr auto to_member_type_list() { + auto t = T{}; + return cista::to_tuple(t); +} + +template +using tuple_representation_t = decltype(to_member_type_list()); + +template +struct count_map { + using key_type = K; + using mapped_type = V; + struct value_type { + key_type first; + mapped_type second; + }; + using const_iterator = typename std::array::const_iterator; + + constexpr count_map() = default; + + constexpr std::pair add(key_type k) noexcept { + auto const it = find(k); + if (it == end()) { + arr_[size_] = value_type{k, static_cast(size_)}; + auto const index = size_; + ++size_; + return {static_cast(index), true}; + } else { + return {it->second, false}; + } + } + + constexpr const_iterator find(key_type k) const noexcept { + for (auto it = begin(); it != end(); ++it) { + if (it->first == k) { + return it; + } + } + return end(); + } + + constexpr const_iterator begin() const noexcept { return std::begin(arr_); } + constexpr const_iterator end() const noexcept { + return std::next( + begin(), + static_cast< + typename std::iterator_traits::difference_type>( + size_)); + } + + std::size_t size_{0U}; + std::array arr_{}; +}; + +template +struct hash_data { + constexpr hash_data combine(hash_t const h) const noexcept { + hash_data r; + r.done_ = done_; + r.h_ = hash_combine(h_, h); + return r; + } + count_map done_; + hash_t h_{BASE_HASH}; +}; + +template +constexpr T* null() noexcept { + return static_cast(nullptr); +} + +template +constexpr hash_data static_type_hash(T const*, + hash_data) noexcept; + +template +constexpr auto hash_tuple_element(hash_data const h) noexcept { + using element_type = std::decay_t>; + return static_type_hash(null(), h); +} + +template +constexpr auto hash_tuple(Tuple const*, hash_data h, + std::index_sequence) noexcept { + ((h = hash_tuple_element(h)), ...); + return h; +} + +template +constexpr hash_data static_type_hash( + T const*, hash_data h) noexcept { + using Type = decay_t; + + auto const base_hash = static_type2str_hash(); + auto const [ordering, inserted] = h.done_.add(base_hash); + if (!inserted) { + return h.combine(ordering); + } + + if constexpr (is_pointer_v) { + using PointeeType = remove_pointer_t; + if constexpr (std::is_same_v, void>) { + return h.combine(hash("void*")); + } else { + h = h.combine(hash("pointer")); + return static_type_hash(static_cast*>(nullptr), h); + } + } else if constexpr (std::is_integral_v) { + return h.combine(hash("i")).combine(sizeof(Type)); + } else if constexpr (std::is_scalar_v) { + return h.combine(static_type2str_hash()); + } else { + static_assert(to_tuple_works_v, "Please implement custom type hash."); + using tuple_t = tuple_representation_t; + return hash_tuple( + null(), h.combine(hash("struct")), + std::make_index_sequence>()); + } +} + +template +constexpr auto static_type_hash(std::chrono::duration const*, + hash_data h) noexcept { + h = h.combine(hash("duration")); + h = static_type_hash(null(), h); + h = static_type_hash(null(), h); + return h; +} + +template +constexpr auto static_type_hash(std::pair const*, + hash_data h) noexcept { + h = h.combine(hash("pair")); + h = static_type_hash(null(), h); + h = static_type_hash(null(), h); + return h; +} + +template +constexpr auto static_type_hash(std::chrono::time_point const*, + hash_data h) noexcept { + h = h.combine(hash("timepoint")); + h = static_type_hash(null(), h); + h = static_type_hash(null(), h); + return h; +} + +template +constexpr auto static_type_hash(array const*, + hash_data h) noexcept { + h = static_type_hash(null(), h); + return h.combine(hash("array")).combine(Size); +} + +template typename Ptr, bool Indexed, + typename TemplateSizeType, std::size_t NMaxTypes> +constexpr auto static_type_hash( + basic_vector const*, + hash_data h) noexcept { + h = h.combine(hash("vector")); + return static_type_hash(null(), h); +} + +template +constexpr auto static_type_hash(basic_unique_ptr const*, + hash_data h) noexcept { + h = h.combine(hash("unique_ptr")); + return static_type_hash(null(), h); +} + +template typename Ptr, typename GetKey, + typename GetValue, typename Hash, typename Eq, std::size_t NMaxTypes> +constexpr auto static_type_hash( + hash_storage const*, + hash_data h) noexcept { + h = h.combine(hash("hash_storage")); + return static_type_hash(null(), h); +} + +template +constexpr auto static_type_hash(variant const*, + hash_data h) noexcept { + h = h.combine(hash("variant")); + ((h = static_type_hash(null(), h)), ...); + return h; +} + +template +constexpr auto static_type_hash(tuple const*, + hash_data h) noexcept { + h = h.combine(hash("tuple")); + ((h = static_type_hash(null(), h)), ...); + return h; +} + +template +constexpr auto static_type_hash(generic_string const*, + hash_data h) noexcept { + return h.combine(hash("string")); +} + +template +constexpr auto static_type_hash(basic_string const*, + hash_data h) noexcept { + return h.combine(hash("string")); +} + +template +constexpr auto static_type_hash(basic_string_view const*, + hash_data h) noexcept { + return h.combine(hash("string")); +} + +template +constexpr auto static_type_hash(indexed const*, + hash_data h) noexcept { + return static_type_hash(null(), h); +} + +template +constexpr auto static_type_hash(strong const*, + hash_data h) noexcept { + h = static_type_hash(null(), h); + return h; +} + +template +constexpr auto static_type_hash(optional const*, + hash_data h) noexcept { + h = h.combine(hash("optional")); + h = static_type_hash(null(), h); + return h; +} + +template typename Vec, + std::size_t Log2MaxEntriesPerBucket, std::size_t NMaxTypes> +constexpr auto static_type_hash( + dynamic_fws_multimap_base const*, + hash_data h) noexcept { + h = h.combine(hash("dynamic_fws_multimap")); + h = static_type_hash(null>(), h); + h = static_type_hash(null>(), h); + h = h.combine(Log2MaxEntriesPerBucket); + return h; +} + +template +constexpr hash_t static_type_hash() noexcept { + return static_type_hash(null(), hash_data{}).h_; +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/type_hash/type_hash.h b/parallel/parallel_src/extern/cista/include/cista/type_hash/type_hash.h new file mode 100644 index 00000000..886026b5 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/type_hash/type_hash.h @@ -0,0 +1,189 @@ +#pragma once + +#include +#include + +#include "cista/containers.h" +#include "cista/decay.h" +#include "cista/hash.h" +#include "cista/indexed.h" +#include "cista/reflection/for_each_field.h" +#include "cista/type_hash/type_name.h" + +namespace cista { + +template +hash_t type2str_hash() noexcept { + return hash_combine(hash(canonical_type_str>()), sizeof(T)); +} + +template +hash_t type_hash(T const&, hash_t, std::map&) noexcept; + +template +hash_t type_hash(std::chrono::duration const&, hash_t h, + std::map& done) noexcept { + h = hash_combine(h, hash("duration")); + h = type_hash(Rep{}, h, done); + h = hash_combine(hash(canonical_type_str()), h); + return h; +} + +template +hash_t type_hash(std::chrono::time_point const&, hash_t h, + std::map& done) noexcept { + h = hash_combine(h, hash("timepoint")); + h = type_hash(Duration{}, h, done); + h = hash_combine(hash(canonical_type_str()), h); + return h; +} + +template +hash_t type_hash(array const&, hash_t h, + std::map& done) noexcept { + h = hash_combine(h, hash("array")); + h = hash_combine(h, Size); + return type_hash(T{}, h, done); +} + +template +hash_t type_hash(T const& el, hash_t h, + std::map& done) noexcept { + using Type = decay_t; + + auto const base_hash = type2str_hash(); + auto [it, inserted] = + done.try_emplace(base_hash, static_cast(done.size())); + if (!inserted) { + return hash_combine(h, it->second); + } + + if constexpr (is_pointer_v) { + using PointeeType = remove_pointer_t; + if constexpr (std::is_same_v) { + return hash_combine(h, hash("void*")); + } else { + return type_hash(remove_pointer_t{}, + hash_combine(h, hash("pointer")), done); + } + } else if constexpr (std::is_integral_v) { + return hash_combine(h, hash("i"), sizeof(Type)); + } else if constexpr (std::is_scalar_v) { + return hash_combine(h, type2str_hash()); + } else { + static_assert(to_tuple_works_v, "Please implement custom type hash."); + h = hash_combine(h, hash("struct")); + for_each_field(el, [&](auto const& member) noexcept { + h = type_hash(member, h, done); + }); + return h; + } +} + +template +hash_t type_hash(pair const&, hash_t h, + std::map& done) noexcept { + h = type_hash(A{}, h, done); + h = type_hash(B{}, h, done); + return hash_combine(h, hash("pair")); +} + +template typename Ptr, bool Indexed, + typename TemplateSizeType> +hash_t type_hash(basic_vector const&, + hash_t h, std::map& done) noexcept { + h = hash_combine(h, hash("vector")); + return type_hash(T{}, h, done); +} + +template +hash_t type_hash(basic_unique_ptr const&, hash_t h, + std::map& done) noexcept { + h = hash_combine(h, hash("unique_ptr")); + return type_hash(T{}, h, done); +} + +template typename Ptr, typename GetKey, + typename GetValue, typename Hash, typename Eq> +hash_t type_hash(hash_storage const&, + hash_t h, std::map& done) noexcept { + h = hash_combine(h, hash("hash_storage")); + return type_hash(T{}, h, done); +} + +template +hash_t type_hash(variant const&, hash_t h, + std::map& done) noexcept { + h = hash_combine(h, hash("variant")); + ((h = type_hash(T{}, h, done)), ...); + return h; +} + +template +hash_t type_hash(tuple const&, hash_t h, + std::map& done) noexcept { + h = hash_combine(h, hash("tuple")); + ((h = type_hash(T{}, h, done)), ...); + return h; +} + +template +hash_t type_hash(generic_string const&, hash_t h, + std::map&) noexcept { + return hash_combine(h, hash("string")); +} + +template +hash_t type_hash(basic_string const&, hash_t h, + std::map&) noexcept { + return hash_combine(h, hash("string")); +} + +template +hash_t type_hash(basic_string_view const&, hash_t h, + std::map&) noexcept { + return hash_combine(h, hash("string")); +} + +template +hash_t type_hash(indexed const&, hash_t h, + std::map& done) noexcept { + return type_hash(T{}, h, done); +} + +template +hash_t type_hash(strong const&, hash_t h, + std::map& done) noexcept { + h = hash_combine(h, hash("strong")); + h = type_hash(T{}, h, done); + h = hash_combine(hash(canonical_type_str()), h); + return h; +} + +template +hash_t type_hash(optional const&, hash_t h, + std::map& done) noexcept { + h = hash_combine(h, hash("optional")); + h = type_hash(T{}, h, done); + return h; +} + +template typename Vec, + std::size_t Log2MaxEntriesPerBucket> +hash_t type_hash( + dynamic_fws_multimap_base const&, + hash_t h, std::map& done) noexcept { + h = hash_combine(h, hash("dynamic_fws_multimap")); + h = type_hash(Vec{}, h, done); + h = type_hash(Vec{}, h, done); + h = hash_combine(Log2MaxEntriesPerBucket, h); + return h; +} + +template +hash_t type_hash() { + auto done = std::map{}; + return type_hash(T{}, BASE_HASH, done); +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/include/cista/type_hash/type_name.h b/parallel/parallel_src/extern/cista/include/cista/type_hash/type_name.h new file mode 100644 index 00000000..6b27bb6f --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/type_hash/type_name.h @@ -0,0 +1,69 @@ +#pragma once + +// Credits: Manu Sánchez (@Manu343726) +// https://github.com/Manu343726/ctti/blob/master/include/ctti/detail/pretty_function.hpp + +#include +#include + +namespace cista { + +#if defined(_MSC_VER) +#define CISTA_SIG __FUNCSIG__ +#elif defined(__clang__) || defined(__GNUC__) +#define CISTA_SIG __PRETTY_FUNCTION__ +#else +#error unsupported compiler +#endif + +inline void remove_all(std::string& s, std::string_view substr) { + auto pos = std::size_t{}; + while ((pos = s.find(substr, pos)) != std::string::npos) { + s.erase(pos, substr.length()); + } +} + +inline void canonicalize_type_name(std::string& s) { + remove_all(s, "{anonymous}::"); // GCC + remove_all(s, "(anonymous namespace)::"); // Clang + remove_all(s, "`anonymous-namespace'::"); // MSVC + remove_all(s, "struct"); // MSVC "struct my_struct" vs "my_struct" + remove_all(s, "const"); // MSVC "char const*"" vs "const char*" + remove_all(s, " "); // MSVC +} + +template +constexpr std::string_view type_str() { +#if defined(__clang__) + constexpr std::string_view prefix = + "std::string_view cista::type_str() [T = "; + constexpr std::string_view suffix = "]"; +#elif defined(_MSC_VER) + constexpr std::string_view prefix = + "class std::basic_string_view > " + "__cdecl cista::type_str<"; + constexpr std::string_view suffix = ">(void)"; +#else + constexpr std::string_view prefix = + "constexpr std::string_view cista::type_str() [with T = "; + constexpr std::string_view suffix = + "; std::string_view = std::basic_string_view]"; +#endif + + auto sig = std::string_view{CISTA_SIG}; + sig.remove_prefix(prefix.size()); + sig.remove_suffix(suffix.size()); + return sig; +} + +template +std::string canonical_type_str() { + auto const base = type_str(); + std::string s{base.data(), base.length()}; + canonicalize_type_name(s); + return s; +} + +} // namespace cista + +#undef CISTA_SIG diff --git a/parallel/parallel_src/extern/cista/include/cista/type_traits.h b/parallel/parallel_src/extern/cista/include/cista/type_traits.h new file mode 100644 index 00000000..aa7bcf3f --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/type_traits.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace cista { + +template +struct is_char_array_helper : std::false_type {}; + +template +struct is_char_array_helper : std::true_type {}; + +template +struct is_char_array_helper : std::true_type {}; + +template +constexpr bool is_char_array_v = is_char_array_helper::value; + +template +struct is_string_helper : std::false_type {}; + +template +constexpr bool is_string_v = is_string_helper>::value; + +} // namespace cista \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/include/cista/unused_param.h b/parallel/parallel_src/extern/cista/include/cista/unused_param.h new file mode 100644 index 00000000..a18b62a6 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/unused_param.h @@ -0,0 +1,3 @@ +#pragma once + +#define CISTA_UNUSED_PARAM(param) static_cast(param); diff --git a/parallel/parallel_src/extern/cista/include/cista/verify.h b/parallel/parallel_src/extern/cista/include/cista/verify.h new file mode 100644 index 00000000..328d03d9 --- /dev/null +++ b/parallel/parallel_src/extern/cista/include/cista/verify.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "cista/cista_exception.h" +#include "cista/exception.h" + +namespace cista { + +inline void verify(bool const condition, char const* msg) { + if (!condition) { + throw_exception(cista_exception{msg}); + } +} + +} // namespace cista diff --git a/parallel/parallel_src/extern/cista/logo.svg b/parallel/parallel_src/extern/cista/logo.svg new file mode 100644 index 00000000..e4a841ec --- /dev/null +++ b/parallel/parallel_src/extern/cista/logo.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/anonymous_namespace_test.cc b/parallel/parallel_src/extern/cista/test/anonymous_namespace_test.cc new file mode 100644 index 00000000..ac026494 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/anonymous_namespace_test.cc @@ -0,0 +1,22 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/type_hash/type_name.h" +#endif + +namespace { +namespace { +struct type_name {}; +} // namespace +} // namespace + +struct abc { + struct def {}; +}; + +TEST_CASE("canonicalize typename special cases") { + CHECK(cista::canonical_type_str() == "type_name"); + CHECK(cista::canonical_type_str() == "abc::def"); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/array_test.cc b/parallel/parallel_src/extern/cista/test/array_test.cc new file mode 100644 index 00000000..9ca7b903 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/array_test.cc @@ -0,0 +1,115 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::offset; +using namespace cista; + +static unsigned ctor{0}, cpy_ctor{0}, mov_ctor{0}, cpy_assign{0}, mov_assign{0}, + dtor{0}; + +void reset() { + ctor = 0; + cpy_ctor = 0; + mov_ctor = 0; + cpy_assign = 0; + mov_assign = 0; + dtor = 0; +} + +struct array_test_struct { + array_test_struct(int i) : i_{i} { ++ctor; } + array_test_struct() { ++ctor; } + array_test_struct(array_test_struct const& o) : i_{o.i_} { ++cpy_ctor; } + array_test_struct(array_test_struct&& o) : i_{o.i_} { ++mov_ctor; } + array_test_struct& operator=(array_test_struct const& o) { + i_ = o.i_; + ++cpy_assign; + return *this; + } + array_test_struct& operator=(array_test_struct&& o) { + i_ = o.i_; + ++mov_assign; + return *this; + } + ~array_test_struct() { ++dtor; } + int i_{0}; +}; + +TEST_CASE("array test move reverse") { + reset(); + + { + data::array arr1, arr2; + arr1[0].i_ = 1; + arr1[1].i_ = 2; + + std::copy(std::make_move_iterator(arr1.rbegin()), + std::make_move_iterator(arr1.rend()), std::begin(arr2)); + + CHECK(arr2.at(0).i_ == 2); + CHECK(arr2[1].i_ == 1); + } + + CHECK(ctor == 4); + CHECK(cpy_ctor == 0); + CHECK(mov_ctor == 0); + CHECK(cpy_assign == 0); + CHECK(mov_assign == 2); + CHECK(dtor == 4); +} + +TEST_CASE("array test copy forward") { + reset(); + + { + data::array arr1, arr2; + arr1[0].i_ = 1; + arr1[1].i_ = 2; + + std::copy(std::begin(arr1), std::end(arr1), std::begin(arr2)); + + CHECK(arr2.at(0).i_ == 1); + CHECK(arr2[1].i_ == 2); + } + + CHECK(ctor == 4); + CHECK(cpy_ctor == 0); + CHECK(mov_ctor == 0); + CHECK(cpy_assign == 2); + CHECK(mov_assign == 0); + CHECK(dtor == 4); +} + +TEST_CASE("array serialize test") { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::array d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::array{}}}; + obj.j_.d_[0] = 1; + obj.j_.d_[1] = 2; + obj.j_.d_[2] = 3; + obj.j_.d_[3] = 4; + buf = cista::serialize(obj); + } // EOL obj + + auto const deserialize = + cista::deserialize(buf); + CHECK(deserialize->j_.d_[0] == 1); + CHECK(deserialize->j_.d_[1] == 2); + CHECK(deserialize->j_.d_[2] == 3); + CHECK(deserialize->j_.d_[3] == 4); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/bit_counting_test.cc b/parallel/parallel_src/extern/cista/test/bit_counting_test.cc new file mode 100644 index 00000000..58cab76a --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/bit_counting_test.cc @@ -0,0 +1,58 @@ +#include "doctest.h" + +#include + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/bit_counting.h" +#endif + +TEST_CASE("count 32bit 0") { + uint64_t t = 0; + CHECK(cista::trailing_zeros(t) == 64); + CHECK(cista::leading_zeros(t) == 64); +} + +TEST_CASE("count 64bit 1") { + uint64_t t = 0'1; + CHECK(cista::trailing_zeros(t) == 0); + CHECK(cista::leading_zeros(t) == 63); +} + +TEST_CASE("count 64bit higher") { + uint64_t t = std::numeric_limits::max(); + t += 1; + CHECK(cista::trailing_zeros(t) == 32); + CHECK(cista::leading_zeros(t) == 31); +} + +TEST_CASE("count 64bit any") { + uint64_t t = 7ULL << 30ULL; + CHECK(cista::trailing_zeros(t) == 30); + CHECK(cista::leading_zeros(t) == 31); +} + +TEST_CASE("count 32bit 0") { + uint32_t t = 0; + CHECK(cista::trailing_zeros(t) == 32); + CHECK(cista::leading_zeros(t) == 32); +} + +TEST_CASE("count 32bit 1") { + uint32_t t = 0'1; + CHECK(cista::trailing_zeros(t) == 0); + CHECK(cista::leading_zeros(t) == 31); +} + +TEST_CASE("count 32bit highest") { + uint32_t t = 1U << 31U; + CHECK(cista::trailing_zeros(t) == 31); + CHECK(cista::leading_zeros(t) == 0); +} + +TEST_CASE("count 32bit any") { + uint32_t t = 7U << 30U; + CHECK(cista::trailing_zeros(t) == 30); + CHECK(cista::leading_zeros(t) == 0); +} diff --git a/parallel/parallel_src/extern/cista/test/bitset_test.cc b/parallel/parallel_src/extern/cista/test/bitset_test.cc new file mode 100644 index 00000000..573788e7 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/bitset_test.cc @@ -0,0 +1,115 @@ +#include "doctest.h" + +#include + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/bitset.h" +#include "cista/serialization.h" +#endif + +TEST_CASE("bitset") { + constexpr auto const size = 136; + + auto s = std::string{}; + for (int i = 0U; i != size; ++i) { + s.push_back(i % 3 == 0U ? '1' : '0'); + } + + auto std_bits = std::bitset{&s[0], s.size()}; + auto cista_bits = cista::bitset{std::string_view{s}}; + + CHECK(std_bits.size() == cista_bits.size()); + + for (auto i = 0U; i != size; ++i) { + CHECK(std_bits.test(size - i - 1U) == (i % 3U == 0U)); + CHECK(cista_bits.test(size - i - 1U) == (i % 3U == 0U)); + } + CHECK(std_bits.to_string() == cista_bits.to_string()); + auto const mod_3_std = std_bits; + auto const mod_3_cista = cista_bits; + + for (auto i = 0U; i != size; ++i) { + cista_bits.set(i, i % 5 == 0); + std_bits.set(i, i % 5 == 0); + } + + for (auto i = 0U; i != size; ++i) { + CHECK(std_bits.test(i) == (i % 5 == 0U)); + CHECK(cista_bits.test(i) == (i % 5 == 0U)); + CHECK(std_bits[i] == (i % 5 == 0U)); + CHECK(cista_bits[i] == (i % 5 == 0U)); + } + CHECK(std_bits.to_string() == cista_bits.to_string()); + auto const mod_5_std = std_bits; + auto const mod_5_cista = cista_bits; + + CHECK((~mod_3_std).to_string() == (~mod_3_cista).to_string()); + CHECK((~mod_3_std).count() == (~mod_3_cista).count()); + + auto mod_15_cista = mod_5_cista; + mod_15_cista &= mod_3_cista; + auto mod_15_std = mod_5_std; + mod_15_std &= mod_3_std; + CHECK(mod_15_cista.to_string() == mod_15_std.to_string()); + CHECK(mod_15_cista.count() == mod_15_std.count()); + + auto mod_3_or_5_cista = mod_5_cista; + mod_3_or_5_cista |= mod_3_cista; + auto mod_3_or_5_std = mod_5_std; + mod_3_or_5_std |= mod_3_std; + CHECK(mod_3_or_5_cista.to_string() == mod_3_or_5_std.to_string()); + CHECK(mod_3_or_5_cista.count() == mod_3_or_5_std.count()); + + auto x = mod_3_or_5_cista; + mod_3_or_5_cista ^= mod_15_cista; + auto y = mod_3_or_5_std; + mod_3_or_5_std ^= mod_15_std; + CHECK(x.to_string() == y.to_string()); + + mod_3_or_5_cista >>= 67U; + mod_3_or_5_std >>= 67U; + CHECK(mod_3_or_5_cista.to_string() == mod_3_or_5_std.to_string()); + CHECK(mod_3_or_5_cista.count() == mod_3_or_5_std.count()); + + mod_3_or_5_cista <<= 67U; + mod_3_or_5_std <<= 67U; + CHECK(mod_3_or_5_cista.to_string() == mod_3_or_5_std.to_string()); + CHECK(mod_3_or_5_cista.count() == mod_3_or_5_std.count()); +} + +TEST_CASE("shift right") { + auto const z = + "000000000000000000000000000000000000000000000000000000000000000000000000" + "00000"; + auto a = cista::bitset<77>{std::string_view{z}}; + auto b = std::bitset<77>{std::string{z}}; + a >>= 48; + b >>= 48; + CHECK(a.to_string() == b.to_string()); + CHECK(a.count() == b.count()); +} + +TEST_CASE("serialize") { + constexpr auto const mode = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION; + + constexpr auto const size = 512; + using bitfield = cista::bitset; + + cista::byte_buf buf; + + auto s = std::string{}; + for (int i = 0U; i != size; ++i) { + s.push_back(i % 5 == 0U ? '1' : '0'); + } + + { + auto obj = cista::bitset{std::string_view{s}}; + buf = cista::serialize(obj); + } // EOL obj + + auto const& deserialized = *cista::unchecked_deserialize(buf); + CHECK(deserialized == bitfield{std::string_view(s)}); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/bitvec_test.cc b/parallel/parallel_src/extern/cista/test/bitvec_test.cc new file mode 100644 index 00000000..220466cb --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/bitvec_test.cc @@ -0,0 +1,183 @@ +#include "doctest.h" + +#include +#include +#include + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/bitvec.h" +#endif + +void set(std::vector& v, std::string_view s) { + v.resize(s.size()); + for (auto i = 0U; i != s.size(); ++i) { + auto const j = s.size() - i - 1; + v[j] = s[i] != '0'; + } +} + +void set(cista::raw::bitvec& v, std::string_view s) { + v.resize(static_cast(s.size())); + for (auto i = 0U; i != s.size(); ++i) { + auto const j = static_cast(s.size() - i - 1); + v.set(j, s[i] != '0'); + } +} + +std::ostream& operator<<(std::ostream& out, std::vector const& v) { + for (auto i = 0U; i != v.size(); ++i) { + auto const j = v.size() - i - 1U; + out << (v[j] ? '1' : '0'); + } + return out; +} + +std::string to_string(std::vector const& v) { + auto ss = std::stringstream{}; + ss << v; + return ss.str(); +} + +template +bool bitvec_lt(T const& x, T const& y) { + assert(x.size() == y.size()); + for (auto i = x.size() - 1; i != 0; --i) { + if (x[i] ^ y[i]) return y[i]; + } + if (!x.empty() && x[0] ^ y[0]) return y[0]; + return false; +} + +TEST_CASE("bitvec bignum") { + auto uut = + cista::basic_bitvec>{}; + uut.resize(8355212022ULL); + uut.set(8355212021ULL, true); + uut.for_each_set_bit([](auto const i) { CHECK_EQ(i, 8355212021ULL); }); +} + +TEST_CASE("bitvec less than") { + auto const str_1 = + R"(000000000101001001010010010100100101001001010010010100100101001001010010010100100101001001010010111111111111111111111111111111111111111111111111)"; + auto const str_2 = + R"(000010010000100100001001000010010000100100001001000010010101001001010010010100100101001001010010010100100101001001010010010100100101001011111111)"; + + auto uut1 = cista::raw::bitvec{}; + auto uut2 = cista::raw::bitvec{}; + auto ref1 = std::vector{}; + auto ref2 = std::vector{}; + + set(uut1, str_1); + set(uut2, str_2); + set(ref1, str_1); + set(ref2, str_2); + + auto count = 0U; + uut2.for_each_set_bit([&](auto const /* */) { ++count; }); + CHECK_EQ(count, uut2.count()); + + CHECK(uut1.str() == std::string_view{str_1}); + CHECK(uut2.str() == std::string_view{str_2}); + CHECK(to_string(ref1) == std::string_view{str_1}); + CHECK(to_string(ref2) == std::string_view{str_2}); + + auto const ref_lt = bitvec_lt(ref1, ref2); + auto const uut_lt = bitvec_lt(uut1, uut2); + CHECK(ref_lt == uut_lt); +} + +unsigned long get_random_number() { // period 2^96-1 + static std::uint64_t x = 123456789, y = 362436069, z = 521288629; + + unsigned long t; + x ^= x << 16; + x ^= x >> 5; + x ^= x << 1; + + t = x; + x = y; + y = z; + z = t ^ x ^ y; + + return z; +} + +#if __has_cpp_attribute(__cpp_lib_atomic_ref) +TEST_CASE("bitvec atomic set") { + constexpr auto const kBits = 100'000U; + constexpr auto const kWorkers = 100U; + + auto start = std::atomic_bool{}; + auto b = cista::raw::bitvec{}; + b.resize(kBits); + auto workers = std::vector(kWorkers); + for (auto i = 0U; i != kWorkers; ++i) { + workers[i] = std::thread{[&, i]() { + while (!start) { + // wait for synchronized start + } + + for (auto j = i; j < kBits; j += kBits / kWorkers) { + b.atomic_set(j); + } + }}; + } + + start.store(true); + + for (auto& w : workers) { + w.join(); + } + + CHECK(b.count() == kBits); +} +#endif + +TEST_CASE("bitvec parallel") { + constexpr auto const kBits = 1'000'000U; + constexpr auto const kWorkers = 100U; + + auto b = cista::raw::bitvec{}; + b.resize(kBits); + + auto bits = std::vector{}; + bits.resize(b.size() * 0.2); + std::generate(begin(bits), end(bits), [&]() { + auto x = static_cast(get_random_number() % b.size()); + b.set(x, true); + return x; + }); + std::sort(begin(bits), end(bits)); + bits.erase(std::unique(begin(bits), end(bits)), end(bits)); + + auto next = std::atomic_size_t{0U}; + auto workers = std::vector(kWorkers); + auto collected_bits = std::vector>(kWorkers); + for (auto i = 0U; i != kWorkers; ++i) { + workers[i] = std::thread{[&, i]() { + auto next_bit = std::optional{}; + do { + next_bit = b.get_next(next); + if (next_bit.has_value()) { + collected_bits[i].push_back(*next_bit); + } + } while (next_bit.has_value()); + }}; + } + + for (auto& w : workers) { + w.join(); + } + + auto check = std::vector{}; + for (auto& x : collected_bits) { + check.insert(end(check), begin(x), end(x)); + } + std::sort(begin(check), end(check)); + check.erase(std::unique(begin(check), end(check)), end(check)); + + CHECK_EQ(bits, check); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/cista_members_test.cc b/parallel/parallel_src/extern/cista/test/cista_members_test.cc new file mode 100644 index 00000000..61e2950a --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/cista_members_test.cc @@ -0,0 +1,106 @@ +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/reflection/for_each_field.h" +#include "cista/reflection/printable.h" +#include "cista/serialization.h" +#endif + +TEST_CASE("cista_members to tuple") { + struct x {}; + struct a : public x { + auto cista_members() noexcept { return std::tie(a_, b_, c_); } + int a_{1}, b_{2}, c_{3}; + }; + static_assert(cista::detail::has_cista_members_v); + auto const obj = a{}; + CHECK(std::get<0>(cista::to_tuple(obj)) == 1); + CHECK(std::get<1>(cista::to_tuple(obj)) == 2); + CHECK(std::get<2>(cista::to_tuple(obj)) == 3); + static_assert( + std::is_same_v(cista::to_tuple(obj))), int const&>); + static_assert(std::is_same_v< + std::decay_t(cista::to_ptr_tuple(obj)))>, + int const*>); + + auto obj1 = a{}; + std::get<2>(cista::to_tuple(obj1)) = 77; + static_assert( + std::is_same_v(cista::to_tuple(obj1))), int&>); + static_assert(std::is_same_v< + std::decay_t(cista::to_ptr_tuple(obj1)))>, + int*>); + CHECK(obj1.c_ == 77); +} + +namespace cista_members_printable { + +struct x {}; +struct a : public x { + CISTA_PRINTABLE(a) + auto cista_members() noexcept { return std::tie(a_, b_, c_); } + int a_{1}, b_{2}, c_{3}; +}; +struct b : public x { + CISTA_PRINTABLE(b, "a", "b", "c") + auto cista_members() noexcept { return std::tie(a_, b_, c_); } + int a_{1}, b_{2}, c_{3}; +}; + +TEST_CASE("cista_members printable") { + std::stringstream ss; + ss << a{}; + CHECK(ss.str() == "{1, 2, 3}"); +} + +TEST_CASE("cista_members printable names") { + std::stringstream ss; + ss << b{}; + CHECK(ss.str() == "{a=1, b=2, c=3}"); +} + +struct parent { + parent() = default; + explicit parent(double u) : u_{u} {} + auto cista_members() noexcept { return std::tie(u_, v_, w_); } + double u_{1}, v_{2}, w_{3}; +}; +struct serialize_me : public parent { + static int s_; + auto cista_members() noexcept { return std::tie(u_, v_, w_, a_, j_); } + int a_{0}; + struct inner : public parent { + auto cista_members() noexcept { return std::tie(u_, v_, w_, b_, c_, d_); } + int b_{0}; + int c_{0}; + cista::raw::string d_; + } j_; +}; + +TEST_CASE("cista_members serialization") { + cista::byte_buf buf; + + { + serialize_me obj; + obj.a_ = 1; + obj.j_.b_ = 2; + obj.j_.c_ = 3; + obj.j_.d_ = cista::raw::string{"testtes"}; + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me)); + + auto const serialized = + cista::deserialize(&buf[0], &buf[0] + buf.size()); + CHECK(serialized->a_ == 1); + CHECK(serialized->j_.b_ == 2); + CHECK(serialized->j_.c_ == 3); + CHECK(serialized->j_.d_ == cista::raw::string{"testtes"}); +} + +} // namespace cista_members_printable diff --git a/parallel/parallel_src/extern/cista/test/comparable_test.cc b/parallel/parallel_src/extern/cista/test/comparable_test.cc new file mode 100644 index 00000000..268058e8 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/comparable_test.cc @@ -0,0 +1,37 @@ +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/reflection/comparable.h" +#endif + +struct comparable_a { + CISTA_FRIEND_COMPARABLE(comparable_a) + int i_ = 1; + int j_ = 2; + double d_ = 100.0; + std::string s_ = "hello world"; +}; + +TEST_CASE("comparable") { + comparable_a inst1, inst2; + + CHECK(inst1 == inst2); + CHECK(!(inst1 != inst2)); + CHECK(inst1 <= inst2); + CHECK(inst1 >= inst2); + CHECK(!(inst1 < inst2)); + CHECK(!(inst1 > inst2)); + + inst1.j_ = 1; + + CHECK(!(inst1 == inst2)); + CHECK(inst1 != inst2); + CHECK(inst1 <= inst2); + CHECK(!(inst1 >= inst2)); + CHECK(inst1 < inst2); + CHECK(!(inst1 > inst2)); +} diff --git a/parallel/parallel_src/extern/cista/test/copy_from_potentially_analigned_test.cc b/parallel/parallel_src/extern/cista/test/copy_from_potentially_analigned_test.cc new file mode 100644 index 00000000..597ced5f --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/copy_from_potentially_analigned_test.cc @@ -0,0 +1,34 @@ +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +TEST_CASE("copy from aligned") { + max_align_t t; + static_assert(sizeof(uint16_t) < sizeof(max_align_t)); + + auto const mem = reinterpret_cast(&t); + *mem = 155; + + auto const buf = + std::string_view{reinterpret_cast(mem), sizeof(uint16_t)}; + CHECK(155 == cista::copy_from_potentially_unaligned(buf)); +} + +TEST_CASE("copy from unaligned") { + max_align_t t; + static_assert(sizeof(uint16_t) < sizeof(max_align_t)); + + auto const value = uint16_t{155}; + auto const storage = reinterpret_cast(&t) + 1; + std::memcpy(storage, &value, sizeof(value)); + + auto const buf = + std::string_view{reinterpret_cast(storage), sizeof(value)}; + CHECK(value == cista::copy_from_potentially_unaligned(buf)); +} diff --git a/parallel/parallel_src/extern/cista/test/cstring_serialize_test.cc b/parallel/parallel_src/extern/cista/test/cstring_serialize_test.cc new file mode 100644 index 00000000..ac4e0f1c --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/cstring_serialize_test.cc @@ -0,0 +1,205 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +constexpr auto const LONG_STR = "aaahelloworldtestaaa"; +constexpr auto const SHORT_STR = "ahelloworldtest"; + +TEST_CASE("cstring serialization long_str") { + cista::raw::string s = LONG_STR; + + cista::byte_buf buf = cista::serialize(s); + auto const serialized = + cista::deserialize(&buf[0], &buf[0] + buf.size()); + CHECK(*serialized == std::string_view{LONG_STR}); +} + +TEST_CASE("cstring serialization short_str") { + cista::raw::string s = SHORT_STR; + + cista::byte_buf buf = cista::serialize(s); + CHECK(buf.size() == sizeof(cista::raw::string)); + + auto const serialized = + cista::deserialize(&buf[0], &buf[0] + buf.size()); + CHECK(*serialized == std::string_view{SHORT_STR}); +} + +using cista::raw::u16string; +using cista::raw::u16string_view; + +const auto constexpr U16STR_SHORT = u"0123"; +const auto constexpr U16STR_SHORT_CORNER_CASE = u"0123456"; +const auto constexpr U16STR_LONG_CORNER_CASE = u"01234567"; +const auto constexpr U16STR_LONG = u"0123456789ABCDEF\xD83D\xDCBB"; + +TEST_CASE("u16string serialization short") { + u16string s = U16STR_SHORT; + + cista::byte_buf buf = cista::serialize(s); + CHECK(buf.size() == sizeof(u16string)); + + auto const serialized = cista::deserialize(buf); + CHECK(*serialized == U16STR_SHORT); +} + +TEST_CASE("u16string serialization long") { + u16string s = U16STR_LONG; + + cista::byte_buf buf = cista::serialize(s); + CHECK(buf.size() == sizeof(u16string) + s.size() * sizeof(char16_t)); + + auto const serialized = cista::deserialize(buf); + CHECK(*serialized == U16STR_LONG); +} + +TEST_CASE("u16string serialization with additional checks") { + u16string s_s = U16STR_SHORT_CORNER_CASE, s_l = U16STR_LONG_CORNER_CASE; + constexpr auto mode = cista::mode::WITH_VERSION | + cista::mode::WITH_INTEGRITY | cista::mode::DEEP_CHECK; + + cista::byte_buf buf_s = cista::serialize(s_s), + buf_l = cista::serialize(s_l); + u16string *serialized_s, *serialized_l; + + CHECK_NOTHROW(serialized_s = cista::deserialize(buf_s)); + CHECK(*serialized_s == U16STR_SHORT_CORNER_CASE); + CHECK_NOTHROW(serialized_l = cista::deserialize(buf_l)); + CHECK(*serialized_l == U16STR_LONG_CORNER_CASE); +} + +TEST_CASE("u16string serialization endian short") { + cista::byte_buf str_le = {0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, + 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00}; + cista::byte_buf str_be = {0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, + 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36}; + u16string s = U16STR_SHORT_CORNER_CASE; + + cista::byte_buf buf_le = cista::serialize(s); + cista::byte_buf buf_be = + cista::serialize(s); + + CHECK(std::equal(buf_le.begin() + offsetof(u16string, s_.s_), buf_le.end(), + str_le.begin())); + CHECK(std::equal(buf_be.begin() + offsetof(u16string, s_.s_), buf_be.end(), + str_be.begin())); + + auto const serialized_be = + cista::deserialize(buf_be); + + CHECK(*serialized_be == U16STR_SHORT_CORNER_CASE); +} + +TEST_CASE("u16string serialization endian long") { + cista::byte_buf str_le = {0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, + 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00}; + cista::byte_buf str_be = {0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, + 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37}; + u16string s = U16STR_LONG_CORNER_CASE; + + cista::byte_buf buf_le = cista::serialize(s); + cista::byte_buf buf_be = + cista::serialize(s); + + CHECK(std::equal(buf_le.begin() + sizeof(u16string), buf_le.end(), + str_le.begin())); + CHECK(std::equal(buf_be.begin() + sizeof(u16string), buf_be.end(), + str_be.begin())); + + auto const serialized_be = + cista::deserialize(buf_be); + + CHECK(*serialized_be == U16STR_LONG_CORNER_CASE); +} + +using cista::raw::u32string; +using cista::raw::u32string_view; + +const auto constexpr U32STR_SHORT = U"01"; +const auto constexpr U32STR_SHORT_CORNER_CASE = U"012"; +const auto constexpr U32STR_LONG_CORNER_CASE = U"0123"; +const auto constexpr U32STR_LONG = U"0123456789ABCDEF\U0001F4BB"; + +TEST_CASE("u32string serialization short") { + u32string s = U32STR_SHORT; + + cista::byte_buf buf = cista::serialize(s); + CHECK(buf.size() == sizeof(u32string)); + + auto const serialized = cista::deserialize(buf); + CHECK(*serialized == U32STR_SHORT); +} + +TEST_CASE("u32string serialization long") { + u32string s = U32STR_LONG; + + cista::byte_buf buf = cista::serialize(s); + CHECK(buf.size() == sizeof(u32string) + s.size() * sizeof(char32_t)); + + auto const serialized = + cista::deserialize(&buf[0], &buf[0] + buf.size()); + CHECK(*serialized == U32STR_LONG); +} + +TEST_CASE("u32string serialization with additional checks") { + u32string s_s = U32STR_SHORT_CORNER_CASE, s_l = U32STR_LONG_CORNER_CASE; + constexpr auto mode = cista::mode::WITH_VERSION | + cista::mode::WITH_INTEGRITY | cista::mode::DEEP_CHECK; + + cista::byte_buf buf_s = cista::serialize(s_s), + buf_l = cista::serialize(s_l); + u32string *serialized_s, *serialized_l; + + CHECK_NOTHROW(serialized_s = cista::deserialize(buf_s)); + CHECK(*serialized_s == U32STR_SHORT_CORNER_CASE); + CHECK_NOTHROW(serialized_l = cista::deserialize(buf_l)); + CHECK(*serialized_l == U32STR_LONG_CORNER_CASE); +} + +TEST_CASE("u32string serialization endian short") { + cista::byte_buf str_le = {0x30, 0x00, 0x00, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x32, 0x00, 0x00, 0x00}; + cista::byte_buf str_be = {0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x00, 0x31, 0x00, 0x00, 0x00, 0x32}; + u32string s = U32STR_SHORT_CORNER_CASE; + + cista::byte_buf buf_le = cista::serialize(s); + cista::byte_buf buf_be = + cista::serialize(s); + + CHECK(std::equal(buf_le.begin() + offsetof(u32string, s_.s_), buf_le.end(), + str_le.begin())); + CHECK(std::equal(buf_be.begin() + offsetof(u32string, s_.s_), buf_be.end(), + str_be.begin())); + + auto const serialized_be = + cista::deserialize(buf_be); + + CHECK(*serialized_be == U32STR_SHORT_CORNER_CASE); +} + +TEST_CASE("u32string serialization endian long") { + cista::byte_buf str_le = {0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00}; + cista::byte_buf str_be = {0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, + 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x33}; + u32string s = U32STR_LONG_CORNER_CASE; + + cista::byte_buf buf_le = cista::serialize(s); + cista::byte_buf buf_be = + cista::serialize(s); + + CHECK(std::equal(buf_le.begin() + sizeof(u32string), buf_le.end(), + str_le.begin())); + CHECK(std::equal(buf_be.begin() + sizeof(u32string), buf_be.end(), + str_be.begin())); + + auto const serialized_be = + cista::deserialize(buf_be); + + CHECK(*serialized_be == U32STR_LONG_CORNER_CASE); +} diff --git a/parallel/parallel_src/extern/cista/test/cstring_test.cc b/parallel/parallel_src/extern/cista/test/cstring_test.cc new file mode 100644 index 00000000..fed12cd8 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/cstring_test.cc @@ -0,0 +1,85 @@ +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/cstring.h" +#include "cista/hash.h" +#endif + +using cista::raw::cstring; + +constexpr auto const CORNER_CASE_SHORT_14 = "01234567891234"; +constexpr auto const CORNER_CASE_SHORT_15 = "012345678912345"; +constexpr auto const CORNER_CASE_LONG_16 = "0123456789123456"; +constexpr auto const LONG_STR = "hello world hello world"; +constexpr auto const SHORT_STR = "hello world"; + +TEST_CASE("cstring init") { + auto s = cstring{}; + CHECK(s.is_short()); + CHECK(s.size() == 0); + CHECK(s.data() != nullptr); +} + +TEST_CASE("cstring long short corner 14") { + auto s = cstring{CORNER_CASE_SHORT_14, cstring::owning}; + CHECK(s.is_short()); + CHECK(s.size() == std::strlen(CORNER_CASE_SHORT_14)); + CHECK(s.view() == CORNER_CASE_SHORT_14); +} + +TEST_CASE("cstring long short corner 15") { + auto s = cstring{CORNER_CASE_SHORT_15, cstring::owning}; + CHECK(s.is_short()); + CHECK(s.size() == std::strlen(CORNER_CASE_SHORT_15)); + CHECK(s.view() == CORNER_CASE_SHORT_15); +} + +TEST_CASE("cstring long short corner 16") { + auto s = cstring{CORNER_CASE_LONG_16, cstring::owning}; + CHECK(!s.is_short()); + CHECK(s.size() == std::strlen(CORNER_CASE_LONG_16)); + CHECK(s.view() == CORNER_CASE_LONG_16); +} + +TEST_CASE("cstring long short") { + auto s = cstring{SHORT_STR, cstring::owning}; + CHECK(s.view() == SHORT_STR); + CHECK(s.is_short()); + + s.set_owning(CORNER_CASE_LONG_16); + CHECK(!s.is_short()); + CHECK(s.view() == CORNER_CASE_LONG_16); + + s.set_owning(LONG_STR); + CHECK(!s.is_short()); + CHECK(s.view() == LONG_STR); +} + +TEST_CASE("cstring dealloc long to short") { + cstring s = "one two"; + CHECK(s.size() == std::strlen("one two")); + CHECK(s.is_short()); + s.set_non_owning(""); +} + +TEST_CASE("cstring copy assign and copy construct") { + auto s0 = cstring{LONG_STR, cstring::owning}; + auto s1 = cstring{s0}; + CHECK(s0 == s1); + CHECK(s1.view() == LONG_STR); + + cstring s2; + s2 = s0; + CHECK(s0 == s2); + CHECK(s2.view() == LONG_STR); +} + +TEST_CASE("cstring hash") { + auto str = cstring{""}; + auto h = cista::hash(str, cista::BASE_HASH); + CHECK(cista::BASE_HASH == h); +} diff --git a/parallel/parallel_src/extern/cista/test/custom_struct_test.cc b/parallel/parallel_src/extern/cista/test/custom_struct_test.cc new file mode 100644 index 00000000..55660158 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/custom_struct_test.cc @@ -0,0 +1,42 @@ +#include "doctest.h" + +#include + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +using namespace cista; +using namespace cista::raw; + +struct serialize_me { + uint32_t v1_ : 10; + uint32_t v2_ : 10; + uint32_t v3_ : 10; + uint32_t v4_ : 1; + uint32_t v5_ : 1; +}; + +template +void serialize(Ctx&, serialize_me const*, offset_t const) {} + +template +void deserialize(Ctx const&, serialize_me*) {} + +TEST_CASE("custom struct test") { + byte_buf buf; + + { + serialize_me obj{1, 2, 3, 0, 1}; + buf = serialize(obj); + } // EOL obj + + auto const serialized = unchecked_deserialize(buf); + CHECK((1 == serialized->v1_)); + CHECK((2 == serialized->v2_)); + CHECK((3 == serialized->v3_)); + CHECK((0 == serialized->v4_)); + CHECK((1 == serialized->v5_)); +} diff --git a/parallel/parallel_src/extern/cista/test/decay_test.cc b/parallel/parallel_src/extern/cista/test/decay_test.cc new file mode 100644 index 00000000..e1b0107b --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/decay_test.cc @@ -0,0 +1,61 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/decay.h" +#include "cista/reflection/for_each_field.h" +#endif + +using cista::decay_t; + +static_assert(std::is_same_v>, int>); +static_assert(std::is_same_v>, int>); +static_assert( + std::is_same_v>, int>); +static_assert( + std::is_same_v&>, int>); +static_assert(std::is_same_v, int>); +static_assert(std::is_same_v, int>); + +void test(int const& i) { + static_assert(std::is_same_v, int>); +} + +TEST_CASE("reference wrapper field") { + int i; + + struct a { + explicit a(int& i) : i_{i} {} + std::reference_wrapper i_; + } instance{i}; + + cista::for_each_field(instance, [](auto&& f) { f.get() = 7; }); + + CHECK(i == 7); +} + +template +auto decay_type(T& type) { + using DecayedType = decay_t; + return DecayedType(type); +} + +TEST_CASE("decay") { + struct a { + int number = 42; + } instance; + + auto& simple = instance; + auto wrapper = std::reference_wrapper(instance); + + auto result1 = decay_type(instance); + auto result2 = decay_type(simple); + auto result3 = decay_type(wrapper); + + instance.number = 0; + + CHECK(result1.number == 42); + CHECK(result2.number == 42); + CHECK(result3.number == 42); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/downward_compat_test.cc b/parallel/parallel_src/extern/cista/test/downward_compat_test.cc new file mode 100644 index 00000000..18e1ac49 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/downward_compat_test.cc @@ -0,0 +1,78 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::raw; + +struct v1 { + data::string s_; +}; + +struct v2 { + data::string s1_, s2_; + double arr_; +}; + +struct data_v1 { + int version_{1}; + data::vector> values_; +}; + +struct data_v2 { + int version_{2}; + data::vector> values_; +}; + +struct version_detection { + int version_{0}; +}; + +TEST_CASE("downward compatibility test") { + for (auto const i : {1, 2}) { + std::vector buf; + switch (i) { + case 1: { + data_v1 values; + values.values_.emplace_back( + data::make_unique(v1{data::string{"A"}})); + values.values_.emplace_back( + data::make_unique(v1{data::string{"B"}})); + values.values_.emplace_back( + data::make_unique(v1{data::string{"C"}})); + buf = cista::serialize(values); + break; + } + + case 2: { + data_v2 values; + values.values_.emplace_back(data::make_unique( + v2{data::string{"A"}, data::string{"1"}, .0})); + values.values_.emplace_back(data::make_unique( + v2{data::string{"B"}, data::string{"2"}, .1})); + values.values_.emplace_back(data::make_unique( + v2{data::string{"C"}, data::string{"3"}, .2})); + buf = cista::serialize(values); + break; + } + + default:; + } + + if (cista::unchecked_deserialize(buf)->version_ == 1) { + auto const v1 = cista::unchecked_deserialize(buf); + CHECK(v1->values_[0]->s_ == "A"); + CHECK(v1->values_[1]->s_ == "B"); + CHECK(v1->values_[2]->s_ == "C"); + } else if (cista::unchecked_deserialize(buf)->version_ == + 2) { + auto const v2 = cista::unchecked_deserialize(buf); + CHECK(v2->values_[0]->s1_ == "A"); + CHECK(v2->values_[1]->s1_ == "B"); + CHECK(v2->values_[2]->s1_ == "C"); + } + } +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/file_test.cc b/parallel/parallel_src/extern/cista/test/file_test.cc new file mode 100644 index 00000000..50e039ef --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/file_test.cc @@ -0,0 +1,52 @@ +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/reflection/for_each_field.h" +#include "cista/targets/file.h" +#endif + +using namespace cista; + +TEST_CASE("file") { + struct test { + int a_ = 1; + int b_ = 2; + int c_ = 3; + } t; + + struct test1 { + uint64_t number_ = 77; + } t1; + + auto start = cista::offset_t{}; + { + file f{"test.bin", "w+"}; + start = f.write(&t, sizeof(t), std::alignment_of_v); + for_each_field(t, [start, i = 11, &t, &f](auto&& m) mutable { + f.write( + static_cast(start + static_cast( + reinterpret_cast(&m) - + reinterpret_cast(&t))), + i++); + }); + + start = f.write(&t1, sizeof(t1), std::alignment_of_v); + } + + int i; + std::ifstream f{"test.bin", std::ios_base::binary}; + for (int j = 0; j < 3; ++j) { + f.read(reinterpret_cast(&i), sizeof(i)); + CHECK(i == 11 + j); + } + + uint64_t number; + f.seekg(start); + f.read(reinterpret_cast(&number), sizeof(number)); + CHECK(number == 77); +} diff --git a/parallel/parallel_src/extern/cista/test/for_each_field_test.cc b/parallel/parallel_src/extern/cista/test/for_each_field_test.cc new file mode 100644 index 00000000..882ce795 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/for_each_field_test.cc @@ -0,0 +1,58 @@ +#include "doctest.h" + +#include +#include +#include + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/reflection/for_each_field.h" +#endif + +struct a { + static constexpr auto const id = 77; + int i_ = 1; + int j_ = 2; + double d_ = 100.0; + std::string s_ = "hello world"; +}; + +struct ignore {}; + +template +std::vector encode(T&& m) { + using Type = std::decay_t; + std::vector fields = {std::to_string(Type::id)}; + std::stringstream ss; + cista::for_each_field(m, [&](auto&& f) { + if constexpr (!std::is_same_v, ignore>) { + ss << f; + fields.emplace_back(ss.str()); + ss.str(""); + } + }); + return fields; +} + +TEST_CASE("for_each_field") { + a instance; + cista::for_each_field(instance, [](auto&& m) { m = {}; }); + CHECK(instance.i_ == 0); + CHECK(instance.j_ == 0); + CHECK(instance.d_ == 0.0); + CHECK(instance.s_ == ""); + CHECK(std::vector({"77", "0", "0", "0", ""}) == + encode(instance)); +} + +struct current_time_req { + static constexpr auto const id = 49; + int version_ = 1; + ignore s_; +}; + +TEST_CASE("for_each_field^_1") { + current_time_req a; + CHECK(std::vector({"49", "1"}) == encode(a)); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/fws_map_test.cc b/parallel/parallel_src/extern/cista/test/fws_map_test.cc new file mode 100644 index 00000000..7bb47101 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/fws_map_test.cc @@ -0,0 +1,91 @@ +#include "doctest.h" + +#include +#include +#include +#include + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/fws_multimap.h" +#endif + +using cista::offset::fws_multimap; + +template +using entry_t = typename cista::offset::fws_multimap::entry_t; + +template +void check_result(Ref const& ref, Result const& result) { + using value_t = typename Ref::value_type; + + if (ref.size() != result.size() && result.size() < 10) { + std::cout << "Invalid result:\n Expected: "; + std::copy(begin(ref), end(ref), + std::ostream_iterator(std::cout, " ")); + std::cout << "\n Result: "; + std::copy(begin(result), end(result), + std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + } + CHECK(ref.size() == result.size()); + for (auto i = 0U; i < ref.size(); ++i) { + CHECK(ref[i] == result[i]); + } +} + +TEST_CASE("fws_multimap_test, multimap_simple") { + fws_multimap m; + + m.push_back(1); + m.push_back(2); + m.finish_key(); + + m.push_back(3); + m.finish_key(); + + m.push_back(4); + m.push_back(5); + m.push_back(6); + m.finish_key(); + + m.finish_map(); + + CHECK(3 + 1 == m.index_size()); + check_result(cista::offset::vector({1, 2}), m[0]); + check_result(cista::offset::vector({3}), m[1]); + check_result(cista::offset::vector({4, 5, 6}), m[2]); + + check_result(cista::offset::vector({1, 2}), *begin(m)); + check_result(cista::offset::vector({3}), *(begin(m) + 1)); + check_result(cista::offset::vector({4, 5, 6}), *(begin(m) + 2)); + CHECK(end(m) == begin(m) + 3); +} + +TEST_CASE("fws_multimap_test, multimap_empty_key") { + fws_multimap m; + + m.push_back(1); + m.push_back(2); + m.finish_key(); + + m.finish_key(); + + m.push_back(4); + m.push_back(5); + m.push_back(6); + m.finish_key(); + + m.finish_map(); + + CHECK(3 + 1 == m.index_size()); + check_result(cista::offset::vector({1, 2}), m[0]); + check_result(cista::offset::vector({}), m[1]); + check_result(cista::offset::vector({4, 5, 6}), m[2]); + + check_result(cista::offset::vector({1, 2}), *begin(m)); + check_result(cista::offset::vector({}), *(begin(m) + 1)); + check_result(cista::offset::vector({4, 5, 6}), *(begin(m) + 2)); + CHECK(end(m) == begin(m) + 3); +} diff --git a/parallel/parallel_src/extern/cista/test/hash_set_test.cc b/parallel/parallel_src/extern/cista/test/hash_set_test.cc new file mode 100755 index 00000000..7638579b --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/hash_set_test.cc @@ -0,0 +1,255 @@ +#define DOCTEST_CONFIG_NO_EXCEPTIONS +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/hash_map.h" +#include "cista/containers/hash_set.h" +#include "cista/hash.h" +#include "cista/serialization.h" +#endif + +TEST_CASE("hash_set test delete even") { + auto const max = 250; + cista::raw::hash_set uut; + for (auto j = 0; j < 10; ++j) { + for (auto i = 0; i < max; ++i) { + auto const res = uut.emplace(i); + CHECK(res.second); + CHECK(*res.first == i); + CHECK(uut.find(i) != uut.end()); + } + CHECK(uut.size() == max); + for (auto i = 0; i < max; ++i) { + auto const res = uut.emplace(i); + CHECK(!res.second); + CHECK(*res.first == i); + if (i % 2 == 0) { + uut.erase(i); + } + } + CHECK(uut.size() == max / 2); + for (auto it = begin(uut); it != end(uut); ++it) { + CHECK(*it % 2 != 0); + CHECK(*uut.find(*it) == *it); + uut.erase(it); + } + CHECK(uut.empty()); + } +} + +TEST_CASE("hash_set test delete half") { + auto const max = 250; + cista::raw::hash_set uut; + for (auto j = 0; j < 10; ++j) { + for (auto i = 0; i < max; ++i) { + auto const res = uut.emplace(i); + CHECK(res.second); + CHECK(*res.first == i); + CHECK(uut.find(i) != uut.end()); + } + CHECK(uut.size() == max); + for (auto i = 0; i < max; ++i) { + auto const res = uut.emplace(i); + CHECK(!res.second); + CHECK(*res.first == i); + if (i >= max / 2) { + uut.erase(i); + } + } + CHECK(uut.size() == max / 2); + for (auto it = begin(uut); it != end(uut); ++it) { + CHECK(*it < max / 2); + CHECK(*uut.find(*it) == *it); + uut.erase(it); + } + CHECK(uut.empty()); + } +} + +TEST_CASE("hash_map test") { + auto const max = 250; + cista::raw::hash_map uut; + for (auto i = 0; i < max; ++i) { + auto const res = uut.emplace(i, i + 1); + CHECK(res.second); + CHECK(res.first->first == i); + CHECK(res.first->second == i + 1); + CHECK(uut.find(i) != uut.end()); + } + for (auto i = 0; i < max; ++i) { + auto const res = uut.emplace(i, i + 1); + CHECK(!res.second); + CHECK(res.first->first == i); + CHECK(res.first->second == i + 1); + if (i % 2 == 0) { + uut.erase(i); + } + } + for (auto it = begin(uut); it != end(uut); ++it) { + CHECK(it->first % 2 != 0); + CHECK(uut.find(it->first)->first == it->first); + CHECK(uut.find(it->first)->second == it->second); + uut.erase(it); + } + CHECK(uut.empty()); +} + +TEST_CASE("iterate empty hash_set test") { + using namespace cista::raw; + hash_set> v; + for (auto& e : v) { + (void)e; + CHECK(false); + } +} + +TEST_CASE("serialize hash_set test") { + using namespace cista; + using namespace cista::raw; + + auto const make_e1 = []() { + vector e1; + e1.emplace_back("short"); + e1.emplace_back("long long long long long long long"); + return e1; + }; + + auto const make_e2 = []() { + vector e2; + e2.emplace_back("hello"); + e2.emplace_back("world"); + e2.emplace_back("yeah!"); + return e2; + }; + + auto const make_e3 = []() { + vector e3; + e3.emplace_back("This"); + e3.emplace_back("is"); + e3.emplace_back("Sparta"); + e3.emplace_back("!!!"); + return e3; + }; + + using serialize_me_t = hash_set>; + + byte_buf buf; + + { + serialize_me_t s; + s.emplace(make_e1()); + s.emplace(make_e2()); + s.emplace(make_e3()); + buf = serialize(s); + } // EOL s + + auto const deserialized = deserialize(buf); + + CHECK(deserialized->size() == 3U); + + CHECK(deserialized->find(make_e1()) != deserialized->end()); + CHECK(deserialized->find(make_e2()) != deserialized->end()); + CHECK(deserialized->find(make_e3()) != deserialized->end()); + + CHECK(*deserialized->find(make_e1()) == make_e1()); + CHECK(*deserialized->find(make_e2()) == make_e2()); + CHECK(*deserialized->find(make_e3()) == make_e3()); +} + +TEST_CASE("hash_set self-assignment") { + using namespace cista::raw; + + hash_set s{"hash", "set", "self", "assignment"}; + s = s; + + CHECK(s.size() == 4); + CHECK(s.find("hash") != s.end()); + CHECK(s.find("set") != s.end()); + CHECK(s.find("self") != s.end()); + CHECK(s.find("assignment") != s.end()); +} + +TEST_CASE("hash_map self-assignment") { + using namespace cista::raw; + + hash_map s{ + {"hash", 1}, {"map", 2}, {"self", 3}, {"assignment", 128}}; + s = s; + + CHECK(s.size() == 4); + CHECK(s.find("hash") != s.end()); + CHECK(s.find("map") != s.end()); + CHECK(s.find("self") != s.end()); + CHECK(s.find("assignment") != s.end()); + + CHECK(s["hash"] == 1); + CHECK(s["map"] == 2); + CHECK(s["self"] == 3); + CHECK(s["assignment"] == 128); +} + +#ifndef _MSC_VER // MSVC compiler bug :/ +TEST_CASE("string view get") { + using namespace cista::raw; + + hash_map s; + + s[{"0"}] = 0; // Calls std::string constructor with key_t const& overload + s[string{"1"}] = 1; // cista::string to std::string_view to std::string + s["2"] = 2; // Calls std::string constructor on insertion + + // operator[] + CHECK(s[{"0"}] == 0); + CHECK(s[string{"0"}] == 0); + CHECK(s["0"] == 0); + + CHECK(s[{"1"}] == 1); + CHECK(s[string{"1"}] == 1); + CHECK(s["1"] == 1); + + CHECK(s[{"2"}] == 2); + CHECK(s[string{"2"}] == 2); + CHECK(s["2"] == 2); + + // .at() + CHECK(s.at({"0"}) == 0); + CHECK(s.at(string{"0"}) == 0); + CHECK(s.at("0") == 0); + + CHECK(s.at({"1"}) == 1); + CHECK(s.at(string{"1"}) == 1); + CHECK(s.at("1") == 1); + + CHECK(s.at({"2"}) == 2); + CHECK(s.at(string{"2"}) == 2); + CHECK(s.at("2") == 2); + + // .find() + CHECK(s.find({"0"})->second == 0); + CHECK(s.find(string{"0"})->second == 0); + CHECK(s.find("0")->second == 0); + + CHECK(s.find({"1"})->second == 1); + CHECK(s.find(string{"1"})->second == 1); + CHECK(s.find("1")->second == 1); + + CHECK(s.find({"2"})->second == 2); + CHECK(s.find(string{"2"})->second == 2); + CHECK(s.find("2")->second == 2); + + // .erase() + CHECK(s.erase({"0"}) == 1); + CHECK(s.erase(string{"0"}) == 0); + CHECK(s.erase("0") == 0); + + CHECK(s.erase({"1"}) == 1); + CHECK(s.erase(string{"1"}) == 0); + CHECK(s.erase("1") == 0); + + CHECK(s.erase({"2"}) == 1); + CHECK(s.erase(string{"2"}) == 0); + CHECK(s.erase("2") == 0); +} +#endif \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/hash_test.cc b/parallel/parallel_src/extern/cista/test/hash_test.cc new file mode 100644 index 00000000..c26fdc47 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/hash_test.cc @@ -0,0 +1,9 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/type_hash/type_name.h" +#endif + +TEST_CASE("type_hash") { CHECK(cista::type_str() == "int"); } \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/hashing_test.cc b/parallel/parallel_src/extern/cista/test/hashing_test.cc new file mode 100644 index 00000000..7bd8b08b --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/hashing_test.cc @@ -0,0 +1,104 @@ +#include +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/hash_map.h" +#include "cista/containers/string.h" +#include "cista/containers/vector.h" +#include "cista/reflection/comparable.h" +#endif + +namespace data = cista::raw; + +struct hash_override { + cista::hash_t hash() const { return i_; } + unsigned i_; +}; + +struct std_hash_key { + explicit std_hash_key(unsigned i) : i_{i} {} + unsigned i_; +}; + +struct key { + int i_; + data::string s_; +}; + +namespace std { +template <> +class hash { +public: + size_t operator()(std_hash_key const& s) const { return s.i_; } +}; +} // namespace std + +struct my_type { + CISTA_COMPARABLE() + int code_{}; + std::string text_; + std::string type_; +}; + +TEST_CASE("hashing std::string member") { + my_type k{3, std::string{"4321"}, std::string{"1234"}}; + CHECK(cista::hashing{}(k) == + cista::hash(std::string{"1234"}, + cista::hash(std::string{"4321"}, + cista::hash_combine(cista::BASE_HASH, 3)))); +} + +TEST_CASE("hash() override") { + hash_override k{7}; + CHECK(cista::hashing{}(k) == + cista::hash_combine(cista::BASE_HASH, 7)); +} + +TEST_CASE("automatic hash validation") { + key k{3U, data::string{"1234"}}; + CHECK(cista::hashing{}(k) == + cista::hash(data::string{"1234"}, + cista::hash_combine(cista::BASE_HASH, 3U))); +} + +TEST_CASE("automatic hashing and equality check") { + data::hash_map, int> m{ + {data::vector{{-2, std::to_string(-2)}, + {-1, std::to_string(-1)}, + {0, std::to_string(0)}}, + 1}, + {data::vector{{-1, std::to_string(-1)}, + {0, std::to_string(0)}, + {1, std::to_string(1)}}, + 2}}; + + for (auto i = 0; i <= 100; ++i) { + m.emplace(data::vector{{i, std::to_string(i)}, + {i + 1, std::to_string(i + 1)}, + {i + 2, std::to_string(i + 2)}}, + i + 3); + } + + CHECK(m.size() == 103); + CHECK(m[{{100, std::to_string(100)}, + {101, std::to_string(101)}, + {102, std::to_string(102)}}] == 103); + CHECK(m.at({{100, std::to_string(100)}, + {101, std::to_string(101)}, + {102, std::to_string(102)}}) == 103); + for (auto const& [k, v] : m) { + CHECK(k.size() == 3U); + CHECK(k.at(0).i_ == k.at(0).i_); + CHECK(k.at(0).i_ == std::stoi(k.at(0).s_.str())); + CHECK(k.at(1).i_ == k.at(1).i_); + CHECK(k.at(1).i_ == std::stoi(k.at(1).s_.str())); + CHECK(k.at(2).i_ == k.at(2).i_); + CHECK(k.at(2).i_ == std::stoi(k.at(2).s_.str())); + CHECK(k.at(0).i_ == v - 3); + } +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/inherited_derived_test.cc b/parallel/parallel_src/extern/cista/test/inherited_derived_test.cc new file mode 100644 index 00000000..bfc710b2 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/inherited_derived_test.cc @@ -0,0 +1,44 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/hash_map.h" +#include "cista/containers/string.h" +#include "cista/containers/vector.h" +#include "cista/reflection/comparable.h" +#include "cista/serialization.h" +#endif + +TEST_CASE("inherited derived") { + namespace data = cista::offset; + constexpr auto MODE = cista::mode::WITH_VERSION; + + struct parent { + parent() = default; + explicit parent(int a) : x_{a}, y_{a} {} + auto cista_members() { return std::tie(x_, y_); } + int x_, y_; + }; + struct child : parent { + child() = default; + explicit child(int a) : parent{a}, z_{a} {} + auto cista_members() { return std::tie(*static_cast(this), z_); } + int z_; + }; + + /* + * Note: hashing, equality comparison, and type hash + * will be automatically generated for you! + */ + using t = data::hash_map; + + cista::byte_buf b; + { // Serialize. + auto x = t{{child{1}, 2}, {child{3}, 4}}; + b = cista::serialize(x); + } + + // Deserialize. + cista::deserialize(b); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/leak_check.cc b/parallel/parallel_src/extern/cista/test/leak_check.cc new file mode 100644 index 00000000..5d1a4fc1 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/leak_check.cc @@ -0,0 +1,15 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/hash_map.h" +#include "cista/containers/string.h" +#endif + +namespace data = cista::offset; + +TEST_CASE("leak_check") { + data::hash_map m{{"longlonglonglonglonglonglong", 0}, + {"short", 0}}; +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/member_index_test.cc b/parallel/parallel_src/extern/cista/test/member_index_test.cc new file mode 100644 index 00000000..ed6a7267 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/member_index_test.cc @@ -0,0 +1,16 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/reflection/member_index.h" +#endif + +TEST_CASE("member_index") { + struct test { + int a, b, c; + }; + CHECK(0 == cista::member_index(&test::a)); + CHECK(1 == cista::member_index(&test::b)); + CHECK(2 == cista::member_index(&test::c)); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/mesh_test.cc b/parallel/parallel_src/extern/cista/test/mesh_test.cc new file mode 100755 index 00000000..1d43ec48 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/mesh_test.cc @@ -0,0 +1,79 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/mmap.h" +#include "cista/serialization.h" +#endif + +using namespace cista; +namespace data = cista::offset; + +struct vec3 { + float x, y, z; +}; + +struct mat4 { + data::array d; +}; + +struct Vertex { + vec3 position; + vec3 normal; +}; + +struct Mesh { + data::vector transforms; + data::vector vertices; +}; +using Meshes = data::vector; + +struct Staging { + int stepIndex; + data::vector distances; + data::vector translations; + data::vector rotations; +}; +using Stagings = data::vector; + +struct Group { + Meshes meshes; + Stagings stagings; +}; + +TEST_CASE("mesh test") { + constexpr auto const FILENAME = "group.bin"; + + std::remove(FILENAME); + + { + Group g; + Mesh m; + m.transforms.emplace_back(mat4{0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.0, + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}); + g.meshes.emplace_back(m); + + cista::buf mmap{cista::mmap{FILENAME}}; + cista::serialize(mmap, g); + } // EOL graph + + auto b = cista::file(FILENAME, "r").content(); + auto const g = cista::deserialize(b); + CHECK(g->meshes[0].transforms[0].d[0] == 0.0); + CHECK(g->meshes[0].transforms[0].d[1] == 1.0); + CHECK(g->meshes[0].transforms[0].d[2] == 2.0); + CHECK(g->meshes[0].transforms[0].d[3] == 3.0); + CHECK(g->meshes[0].transforms[0].d[4] == 4.0); + CHECK(g->meshes[0].transforms[0].d[5] == 5.0); + CHECK(g->meshes[0].transforms[0].d[6] == 6.0); + CHECK(g->meshes[0].transforms[0].d[7] == 7.0); + CHECK(g->meshes[0].transforms[0].d[8] == 0.0); + CHECK(g->meshes[0].transforms[0].d[9] == 1.0); + CHECK(g->meshes[0].transforms[0].d[10] == 2.0); + CHECK(g->meshes[0].transforms[0].d[11] == 3.0); + CHECK(g->meshes[0].transforms[0].d[12] == 4.0); + CHECK(g->meshes[0].transforms[0].d[13] == 5.0); + CHECK(g->meshes[0].transforms[0].d[14] == 6.0); + CHECK(g->meshes[0].transforms[0].d[15] == 7.0); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/mutable_multimap_test.cc b/parallel/parallel_src/extern/cista/test/mutable_multimap_test.cc new file mode 100644 index 00000000..aa964c90 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/mutable_multimap_test.cc @@ -0,0 +1,387 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/mutable_fws_multimap.h" +#include "cista/reflection/comparable.h" +#include "cista/verify.h" +#endif + +template +using mutable_fws_multimap = cista::offset::mutable_fws_multimap; + +template +using strong_key = cista::strong; + +template +using weak_key = T; + +namespace utl { + +template +void erase(Container& c, Element elem) { + c.erase(std::remove(begin(c), end(c), elem), end(c)); +} + +template +void erase(Container&& c, Element elem) { + using std::begin; + using std::end; + c.erase(std::remove(begin(c), end(c), elem), end(c)); +} + +template +void erase_if(Container&& c, Predicate&& pred) { + using std::begin; + using std::end; + c.erase(std::remove_if(begin(c), end(c), std::forward(pred)), + end(c)); +} + +} // namespace utl + +struct test_edge { + CISTA_COMPARABLE() + + std::uint32_t from_{}; + std::uint32_t to_{}; + std::uint32_t weight_{}; +}; + +template +bool ElementsAreArray(T1 const& t1, std::initializer_list t2) { + CHECK(t1.size() == t2.size()); + auto it1 = begin(t1); + auto it2 = begin(t2); + auto end1 = end(t1); + auto end2 = end(t2); + for (; it1 != end1 && it2 != end2; ++it1, ++it2) { + CHECK(*it1 == *it2); + } + return true; +} + +std::ostream& operator<<(std::ostream& out, test_edge const& e) { + return out << "{from=" << e.from_ << ", to=" << e.to_ + << ", weight=" << e.weight_ << "}"; +} + +template +mutable_fws_multimap build_test_map_1() { + mutable_fws_multimap mm; + + mm[KeyType{0}].push_back(4); + mm[KeyType{0}].push_back(8); + + mm[KeyType{1}].push_back(15); + mm[KeyType{1}].push_back(16); + mm[KeyType{1}].push_back(23); + mm[KeyType{1}].push_back(42); + + mm[KeyType{2}].push_back(100); + mm[KeyType{2}].push_back(200); + mm[KeyType{2}].push_back(250); + mm[KeyType{2}].push_back(300); + mm[KeyType{2}].push_back(350); + mm[KeyType{2}].push_back(400); + + return mm; +} + +TEST_CASE_TEMPLATE("mutable_fws_multimap_test, int_1", KeyType, + strong_key, weak_key) { + mutable_fws_multimap mm; + + REQUIRE(0 == mm.element_count()); + REQUIRE(0 == mm.size()); + + mm[KeyType{0}].push_back(42); + REQUIRE(1 == mm.element_count()); + REQUIRE(1 == mm.size()); + CHECK(ElementsAreArray(mm[KeyType{0}], {42})); + CHECK(1 == mm[KeyType{0}].size()); + + mm[KeyType{1}].push_back(4); + REQUIRE(2 == mm.element_count()); + REQUIRE(2 == mm.size()); + CHECK(ElementsAreArray(mm[KeyType{0}], {42})); + CHECK(1 == mm[KeyType{0}].size()); + CHECK(ElementsAreArray(mm[KeyType{1}], {4})); + CHECK(1 == mm[KeyType{1}].size()); + + mm[KeyType{1}].push_back(8); + REQUIRE(3 == mm.element_count()); + REQUIRE(2 == mm.size()); + CHECK(ElementsAreArray(mm[KeyType{0}], {42})); + CHECK(1 == mm[KeyType{0}].size()); + CHECK(ElementsAreArray(mm[KeyType{1}], {4, 8})); + CHECK(2 == mm[KeyType{1}].size()); + + mm[KeyType{1}].emplace_back(15); + REQUIRE(4 == mm.element_count()); + REQUIRE(2 == mm.size()); + CHECK(ElementsAreArray(mm[KeyType{0}], {42})); + CHECK(1 == mm[KeyType{0}].size()); + CHECK(ElementsAreArray(mm[KeyType{1}], {4, 8, 15})); + CHECK(3 == mm[KeyType{1}].size()); + + mm[KeyType{1}].push_back(16); + REQUIRE(5 == mm.element_count()); + REQUIRE(2 == mm.size()); + CHECK(ElementsAreArray(mm[KeyType{0}], {42})); + CHECK(1 == mm[KeyType{0}].size()); + CHECK(ElementsAreArray(mm[KeyType{1}], {4, 8, 15, 16})); + CHECK(4 == mm[KeyType{1}].size()); + + mm[KeyType{0}].push_back(23); + REQUIRE(6 == mm.element_count()); + REQUIRE(2 == mm.size()); + CHECK(ElementsAreArray(mm[KeyType{0}], {42, 23})); + CHECK(2 == mm[KeyType{0}].size()); + CHECK(ElementsAreArray(mm[KeyType{1}], {4, 8, 15, 16})); + CHECK(4 == mm[KeyType{1}].size()); +} + +TEST_CASE("mutable_fws_multimap_test, graph_1") { + mutable_fws_multimap mm; + + mm[0].emplace_back(0U, 1U, 10U); + mm[1].emplace_back(1U, 2U, 20U); + mm[1].emplace_back(1U, 3U, 30U); + mm[3].emplace_back(3U, 0U, 50U); + mm[2].emplace_back(2U, 3U, 5U); + + REQUIRE(4 == mm.size()); + CHECK(5 == mm.element_count()); + + CHECK(ElementsAreArray(mm[0], {test_edge{0U, 1U, 10U}})); + CHECK(ElementsAreArray(mm[1], + {test_edge{1U, 2U, 20U}, test_edge{1U, 3U, 30U}})); + CHECK(ElementsAreArray(mm[2], {test_edge{2U, 3U, 5U}})); + CHECK(ElementsAreArray(mm[3], {test_edge{3U, 0U, 50U}})); +} + +TEST_CASE("mutable_fws_multimap_test, int_2") { + auto const mm = build_test_map_1(); + + REQUIRE(3 == mm.size()); + CHECK(12 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8})); + CHECK(ElementsAreArray(mm[1], {15, 16, 23, 42})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350, 400})); + + CHECK(ElementsAreArray(mm.front(), {4, 8})); + CHECK(ElementsAreArray(mm.back(), {100, 200, 250, 300, 350, 400})); + CHECK(15 == mm[1].front()); + CHECK(42 == mm[1].back()); +} + +TEST_CASE_TEMPLATE("mutable_fws_multimap_test, int_insert_1", KeyType, + strong_key, weak_key) { + auto mm = build_test_map_1(); + + mm[KeyType{1}].insert(std::next(mm[KeyType{1}].begin(), 2), 20); + + REQUIRE(3 == mm.size()); + CHECK(13 == mm.element_count()); + CHECK(ElementsAreArray(mm[KeyType{0}], {4, 8})); + CHECK(ElementsAreArray(mm[KeyType{1}], {15, 16, 20, 23, 42})); + CHECK(ElementsAreArray(mm[KeyType{2}], {100, 200, 250, 300, 350, 400})); +} + +TEST_CASE("mutable_fws_multimap_test, int_insert_2") { + auto mm = build_test_map_1(); + + auto const val = 20; + mm[1].insert(std::next(mm[1].begin(), 2), val); + + REQUIRE(3 == mm.size()); + CHECK(13 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8})); + CHECK(ElementsAreArray(mm[1], {15, 16, 20, 23, 42})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350, 400})); +} + +TEST_CASE_TEMPLATE("mutable_fws_multimap_test, int_erase_1", KeyType, + weak_key, strong_key) { + auto mm = build_test_map_1(); + + utl::erase(mm[KeyType{1}], 16); + + REQUIRE(3 == mm.size()); + CHECK(11 == mm.element_count()); + CHECK(ElementsAreArray(mm[KeyType{0}], {4, 8})); + CHECK(ElementsAreArray(mm[KeyType{1}], {15, 23, 42})); + CHECK(ElementsAreArray(mm[KeyType{2}], {100, 200, 250, 300, 350, 400})); + + utl::erase(mm[KeyType{2}], 100); + + REQUIRE(3 == mm.size()); + CHECK(10 == mm.element_count()); + CHECK(ElementsAreArray(mm[KeyType{0}], {4, 8})); + CHECK(ElementsAreArray(mm[KeyType{1}], {15, 23, 42})); + CHECK(ElementsAreArray(mm[KeyType{2}], {200, 250, 300, 350, 400})); + + utl::erase(mm[KeyType{2}], 400); + + REQUIRE(3 == mm.size()); + CHECK(9 == mm.element_count()); + CHECK(ElementsAreArray(mm[KeyType{0}], {4, 8})); + CHECK(ElementsAreArray(mm[KeyType{1}], {15, 23, 42})); + CHECK(ElementsAreArray(mm[KeyType{2}], {200, 250, 300, 350})); + + utl::erase(mm[KeyType{2}], 250); + + REQUIRE(3 == mm.size()); + CHECK(8 == mm.element_count()); + CHECK(ElementsAreArray(mm[KeyType{0}], {4, 8})); + CHECK(ElementsAreArray(mm[KeyType{1}], {15, 23, 42})); + CHECK(ElementsAreArray(mm[KeyType{2}], {200, 300, 350})); + + utl::erase(mm[KeyType{1}], 404); + + REQUIRE(3 == mm.size()); + CHECK(8 == mm.element_count()); + CHECK(ElementsAreArray(mm[KeyType{0}], {4, 8})); + CHECK(ElementsAreArray(mm[KeyType{1}], {15, 23, 42})); + CHECK(ElementsAreArray(mm[KeyType{2}], {200, 300, 350})); +} + +TEST_CASE("mutable_fws_multimap_test, int_erase_2") { + auto mm = build_test_map_1(); + + utl::erase_if(mm[2], [](int e) { return e % 100 == 0; }); + + REQUIRE(3 == mm.size()); + CHECK(8 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8})); + CHECK(ElementsAreArray(mm[1], {15, 16, 23, 42})); + CHECK(ElementsAreArray(mm[2], {250, 350})); +} + +TEST_CASE("mutable_fws_multimap_test, int_resize_1") { + auto mm = build_test_map_1(); + + mm[0].resize(4); + + REQUIRE(3 == mm.size()); + CHECK(14 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8, 0, 0})); + CHECK(ElementsAreArray(mm[1], {15, 16, 23, 42})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350, 400})); + + mm[1].resize(3); + + REQUIRE(3 == mm.size()); + CHECK(13 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8, 0, 0})); + CHECK(ElementsAreArray(mm[1], {15, 16, 23})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350, 400})); + + mm[1].resize(6, 123); + + REQUIRE(3 == mm.size()); + CHECK(16 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8, 0, 0})); + CHECK(ElementsAreArray(mm[1], {15, 16, 23, 123, 123, 123})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350, 400})); +} + +TEST_CASE("mutable_fws_multimap_test, pop_back_1") { + auto mm = build_test_map_1(); + + mm[2].pop_back(); + + REQUIRE(3 == mm.size()); + CHECK(11 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8})); + CHECK(ElementsAreArray(mm[1], {15, 16, 23, 42})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350})); + + mm[1].pop_back(); + + REQUIRE(3 == mm.size()); + CHECK(10 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8})); + CHECK(ElementsAreArray(mm[1], {15, 16, 23})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350})); + + mm[0].pop_back(); + + REQUIRE(3 == mm.size()); + CHECK(9 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4})); + CHECK(ElementsAreArray(mm[1], {15, 16, 23})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350})); + + mm[0].pop_back(); + + REQUIRE(3 == mm.size()); + CHECK(8 == mm.element_count()); + CHECK(mm[0].empty()); + CHECK(ElementsAreArray(mm[1], {15, 16, 23})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350})); +} + +TEST_CASE("mutable_fws_multimap_test, clear_1") { + auto mm = build_test_map_1(); + + mm[0].clear(); + + REQUIRE(3 == mm.size()); + CHECK(10 == mm.element_count()); + CHECK(mm[0].empty()); + CHECK(ElementsAreArray(mm[1], {15, 16, 23, 42})); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350, 400})); +} + +TEST_CASE("mutable_fws_multimap_test, clear_2") { + auto mm = build_test_map_1(); + + mm[1].clear(); + + REQUIRE(3 == mm.size()); + CHECK(8 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8})); + CHECK(mm[1].empty()); + CHECK(ElementsAreArray(mm[2], {100, 200, 250, 300, 350, 400})); +} + +TEST_CASE("mutable_fws_multimap_test, clear_3") { + auto mm = build_test_map_1(); + + mm[2].clear(); + + REQUIRE(3 == mm.size()); + CHECK(6 == mm.element_count()); + CHECK(ElementsAreArray(mm[0], {4, 8})); + CHECK(ElementsAreArray(mm[1], {15, 16, 23, 42})); + CHECK(mm[2].empty()); +} + +template +T get_order_loop(T const size) { + for (auto order = T{0U}, value = T{1}; order < static_cast(sizeof(T) * 8); + ++order, value = value << 1) { + if (value == size) { + return order; + } + } + cista::verify(false, "mutable_fws_multimap::get_order: not found for"); + return T{}; +} + +TEST_CASE("mutable_fws_multimap_test, get_order_loop") { + for (std::uint16_t i = 0U; i < 16; ++i) { + CHECK(get_order_loop(static_cast(1U) << i) == i); + } + + for (std::uint32_t i = 0U; i < 32; ++i) { + CHECK(get_order_loop(1U << i) == i); + } + + for (std::uint64_t i = 0ULL; i < 64; ++i) { + CHECK(get_order_loop(1ULL << i) == i); + } +} diff --git a/parallel/parallel_src/extern/cista/test/nvec_test.cc b/parallel/parallel_src/extern/cista/test/nvec_test.cc new file mode 100644 index 00000000..b1d1074d --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/nvec_test.cc @@ -0,0 +1,102 @@ +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/nvec.h" +#include "cista/serialization.h" +#include "cista/targets/buf.h" +#endif + +TEST_CASE("nvec test") { + constexpr auto const kMode = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION; + + struct transfer { + bool operator==(transfer const& o) const { return o.i_ == i_; } + int i_; + }; + using container_t = cista::offset::nvec; + + cista::byte_buf buf; + { + container_t v; + + v.emplace_back(std::vector>{ + {transfer{1}, transfer{2}}, {transfer{3}, transfer{4}, transfer{5}}}); + v.emplace_back(std::vector>{ + {transfer{6}, transfer{7}}, + {transfer{8}, transfer{9}, transfer{10}}, + {transfer{11}}}); + v.emplace_back(std::vector>{ + {transfer{12}, transfer{13}}, + {transfer{14}, transfer{15}, transfer{16}}, + {transfer{17}}}); + buf = cista::serialize(v); + } + + auto const& v = *cista::deserialize(buf); + + auto const all = std::vector{ + transfer{1}, transfer{2}, transfer{3}, transfer{4}, transfer{5}, + transfer{6}, transfer{7}, transfer{8}, transfer{9}, transfer{10}, + transfer{11}, transfer{12}, transfer{13}, transfer{14}, transfer{15}, + transfer{16}, transfer{17}}; + auto l = 0U; + for (auto i = 0U; i != v.size(); ++i) { + for (auto j = 0U; j != v.size(i); ++j) { + for (auto k = 0U; k != v.size(i, j); ++k) { + CHECK_EQ(all[l++], v.at(i, j).at(k)); + } + } + } + + l = 0U; + for (auto const a : v) { + for (auto const b : a) { + for (auto const c : b) { + CHECK_EQ((all[l++]), c); + } + } + } + + { + auto const bucket = v.at(2U, 1U); + auto const entries = {transfer{14}, transfer{15}, transfer{16}}; + CHECK(std::equal(begin(bucket), end(bucket), begin(entries), end(entries))); + } + + { + auto const bucket = v.at(2U, 2U); + auto const entries = {transfer{17}}; + CHECK(std::equal(begin(bucket), end(bucket), begin(entries), end(entries))); + } + + for (auto const& t : v.at(1U, 2U)) { + CHECK_EQ(t.i_, 11); + } + CHECK_EQ(2U, v.size(0U)); + CHECK_EQ(3U, v.size(1U)); + CHECK_EQ(2U, v.size(0U, 0U)); + CHECK_EQ(3U, v.size(0U, 1U)); + CHECK_EQ(2U, v.size(1U, 0U)); + CHECK_EQ(3U, v.size(1U, 1U)); + CHECK_EQ(1U, v.size(1U, 2U)); + CHECK_EQ(2U, v.size(2U, 0U)); + CHECK_EQ(3U, v.size(2U, 1U)); + CHECK_EQ(1U, v.size(2U, 2U)); + + CHECK_EQ(2U, v[0U].size()); + CHECK_EQ(3U, v[1U].size()); + CHECK_EQ(2U, v[0U][0U].size()); + CHECK_EQ(3U, v[0U][1U].size()); + CHECK_EQ(2U, v[1U][0U].size()); + CHECK_EQ(3U, v[1U][1U].size()); + CHECK_EQ(1U, v[1U][2U].size()); + CHECK_EQ(2U, v[2U][0U].size()); + CHECK_EQ(3U, v[2U][1U].size()); + CHECK_EQ(1U, v[2U][2U].size()); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/offset_graph_indexed_vector_test.cc b/parallel/parallel_src/extern/cista/test/offset_graph_indexed_vector_test.cc new file mode 100755 index 00000000..c823b87e --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/offset_graph_indexed_vector_test.cc @@ -0,0 +1,230 @@ +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/mmap.h" +#include "cista/serialization.h" +#endif + +namespace data = cista::offset; + +constexpr auto const CHECKSUM_INTEGRITY_AND_VERSION = + sizeof(void*) == 4 ? 6178049379267258110ULL : 16480686862354987723ULL; +constexpr auto const CHECKSUM_BIG_ENDIAN = + sizeof(void*) == 4 ? 3740667825761940361ULL : 6763878759702099468ULL; + +namespace graph_indexed_vec_ns::offset { + +struct node; + +using node_id_t = uint32_t; + +struct edge { + data::ptr from_; + data::ptr to_; +}; + +struct node { + void add_edge(edge* e) { edges_.emplace_back(e); } + node_id_t id() const { return id_; } + + node_id_t id_{0}; + node_id_t fill_{0}; + data::vector> edges_; + cista::indexed name_; +}; + +struct graph { + static graph make_graph() { + graph g; + + g.edges_.resize(3); + g.nodes_.resize(3); + g.node_names_.resize(3); + + g.nodes_[0].id_ = 0; + g.nodes_[1].id_ = 1; + g.nodes_[2].id_ = 2; + g.nodes_[0].name_ = data::string{"NODE A"}; + g.nodes_[1].name_ = data::string{"NODE B"}; + g.nodes_[2].name_ = data::string{"NODE C"}; + g.nodes_[0].edges_ = {&g.edges_[0]}; + g.nodes_[1].edges_ = {&g.edges_[1]}; + g.nodes_[2].edges_ = {&g.edges_[2]}; + + g.node_names_[0] = &g.nodes_[0].name_; + g.node_names_[1] = &g.nodes_[1].name_; + g.node_names_[2] = &g.nodes_[2].name_; + + g.edges_[0].from_ = &g.nodes_[0]; + g.edges_[0].to_ = &g.nodes_[1]; + g.edges_[1].from_ = &g.nodes_[1]; + g.edges_[1].to_ = &g.nodes_[2]; + g.edges_[2].from_ = &g.nodes_[2]; + g.edges_[2].to_ = &g.nodes_[0]; + + return g; + } + + data::indexed_vector nodes_; + data::indexed_vector edges_; + data::vector> node_names_; +}; + +} // namespace graph_indexed_vec_ns::offset + +using namespace graph_indexed_vec_ns::offset; + +inline std::set bfs(node const* entry) { + std::queue q; + std::set visited; + + q.emplace(entry); + + while (!q.empty()) { + auto const next = q.front(); + q.pop(); + + if (visited.find(next) != end(visited)) { + continue; + } + + visited.emplace(next); + + for (auto const& e : next->edges_) { + q.emplace(e->to_); + } + } + + return visited; +} + +TEST_CASE("graph offset indexed vec serialize file") { + constexpr auto const FILENAME = "offset_graph.bin"; + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION; + + std::remove(FILENAME); + + { + graph g = graph::make_graph(); + + cista::file f{FILENAME, "w+"}; + cista::serialize(f, g); + + CHECK(f.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + } // EOL graph + + { + cista::file f{FILENAME, "r"}; + CHECK(f.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + } + + auto b = cista::file(FILENAME, "r").content(); + CHECK(cista::hash(b) == CHECKSUM_INTEGRITY_AND_VERSION); + + auto const g = cista::deserialize(b); + auto const visited = bfs(&g->nodes_[0]); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); + CHECK(*g->node_names_[0] == "NODE A"); + CHECK(*g->node_names_[1] == "NODE B"); + CHECK(*g->node_names_[2] == "NODE C"); +} + +TEST_CASE("graph offset indexed vec serialize buf") { + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION; + + cista::byte_buf buf; + { + graph g = graph::make_graph(); + + cista::buf b; + cista::serialize(b, g); + + CHECK(b.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + + buf = std::move(b.buf_); + } // EOL graph + + CHECK(cista::hash(buf) == CHECKSUM_INTEGRITY_AND_VERSION); + + auto const g = cista::deserialize(buf); + auto const visited = bfs(&g->nodes_[0]); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); + CHECK(*g->node_names_[0] == "NODE A"); + CHECK(*g->node_names_[1] == "NODE B"); + CHECK(*g->node_names_[2] == "NODE C"); +} + +TEST_CASE("graph offset indexed vec serialize mmap file") { + constexpr auto const FILENAME = "offset_graph_mmap.bin"; + constexpr auto const MODE = cista::mode::WITH_INTEGRITY | + cista::mode::WITH_VERSION | + cista::mode::DEEP_CHECK; + + std::remove(FILENAME); + + { + graph g = graph::make_graph(); + + cista::buf mmap{cista::mmap{FILENAME}}; + mmap.buf_.reserve(512); + cista::serialize(mmap, g); + CHECK(mmap.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + } // EOL graph + +#if defined(CISTA_LITTLE_ENDIAN) + auto b = cista::mmap(FILENAME, cista::mmap::protection::READ); +#else + auto b = cista::file(FILENAME, "r").content(); +#endif + auto const g = cista::deserialize(b); + auto const visited = bfs(&g->nodes_[0]); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); + CHECK(*g->node_names_[0] == data::string{"NODE A"}); + CHECK(*g->node_names_[1] == data::string{"NODE B"}); + CHECK(*g->node_names_[2] == data::string{"NODE C"}); +} + +TEST_CASE("graph offset indexed vec serialize endian test") { + constexpr auto const FILENAME = "offset_graph_big_endian.bin"; + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION | + cista::mode::SERIALIZE_BIG_ENDIAN | cista::mode::DEEP_CHECK; + + std::remove(FILENAME); + + { + graph g = graph::make_graph(); + + cista::buf mmap{cista::mmap{FILENAME}}; + mmap.buf_.reserve(512); + cista::serialize(mmap, g); + CHECK(mmap.checksum() == CHECKSUM_BIG_ENDIAN); + } // EOL graph + + auto b = cista::file(FILENAME, "r").content(); + auto const g = cista::deserialize(b); + auto const visited = bfs(&g->nodes_[0]); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); + CHECK(*g->node_names_[0] == "NODE A"); + CHECK(*g->node_names_[1] == "NODE B"); + CHECK(*g->node_names_[2] == "NODE C"); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/offset_graph_static_version_test.cc b/parallel/parallel_src/extern/cista/test/offset_graph_static_version_test.cc new file mode 100755 index 00000000..9cc9b554 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/offset_graph_static_version_test.cc @@ -0,0 +1,233 @@ +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/mmap.h" +#include "cista/serialization.h" +#endif + +namespace data = cista::offset; + +namespace graphns::offset { + +struct node; + +using node_id_t = uint32_t; + +struct edge { + data::ptr from_; + data::ptr to_; +}; + +struct node { + void add_edge(edge* e) { edges_.emplace_back(e); } + node_id_t id() const { return id_; } + + node_id_t id_{0}; + node_id_t fill_{0}; + data::vector> edges_; + data::string name_; +}; + +struct graph { + node* make_node(data::string name) { + return nodes_ + .emplace_back(data::make_unique( + node{next_node_id_++, 0, data::vector>(0u), + std::move(name)})) + .get(); + } + + edge* make_edge(node_id_t const from, node_id_t const to) { + return edges_ + .emplace_back( + data::make_unique(edge{nodes_[from].get(), nodes_[to].get()})) + .get(); + } + + data::vector> nodes_; + data::vector> edges_; + node_id_t next_node_id_{0}; + node_id_t fill_{0}; +}; + +} // namespace graphns::offset + +using namespace graphns::offset; + +inline std::set bfs(node const* entry) { + std::queue q; + std::set visited; + + q.emplace(entry); + + while (!q.empty()) { + auto const next = q.front(); + q.pop(); + + if (visited.find(next) != end(visited)) { + continue; + } + + visited.emplace(next); + + for (auto const& e : next->edges_) { + q.emplace(e->to_); + } + } + + return visited; +} + +TEST_CASE("graph offset serialize file - static version") { + constexpr auto const FILENAME = "offset_graph.bin"; + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_STATIC_VERSION; + + std::remove(FILENAME); + + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::file f{FILENAME, "w+"}; + cista::serialize(f, g); + } // EOL graph + + { cista::file f{FILENAME, "r"}; } + + auto b = cista::file(FILENAME, "r").content(); + + auto const g = cista::deserialize(b); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} + +TEST_CASE("graph offset serialize buf - static version") { + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_STATIC_VERSION; + + cista::byte_buf buf; + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::buf b; + cista::serialize(b, g); + + buf = std::move(b.buf_); + } // EOL graph + + auto const g = cista::deserialize(buf); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} + +TEST_CASE("graph offset serialize mmap file - static version") { + constexpr auto const FILENAME = "offset_graph_mmap.bin"; + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_STATIC_VERSION; + + std::remove(FILENAME); + + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::buf mmap{cista::mmap{FILENAME}}; + mmap.buf_.reserve(512); + cista::serialize(mmap, g); + } // EOL graph + +#if defined(CISTA_LITTLE_ENDIAN) + auto b = cista::mmap(FILENAME, cista::mmap::protection::READ); +#else + auto b = cista::file(FILENAME, "r").content(); +#endif + auto const g = cista::deserialize(b); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} + +TEST_CASE("graph offset serialize endian test - static version") { + constexpr auto const FILENAME = "offset_graph_big_endian.bin"; + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_STATIC_VERSION | + cista::mode::SERIALIZE_BIG_ENDIAN | cista::mode::DEEP_CHECK; + + std::remove(FILENAME); + + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::buf mmap{cista::mmap{FILENAME}}; + mmap.buf_.reserve(512); + cista::serialize(mmap, g); + } // EOL graph + + auto b = cista::file(FILENAME, "r").content(); + auto const g = cista::deserialize(b); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} diff --git a/parallel/parallel_src/extern/cista/test/offset_graph_test.cc b/parallel/parallel_src/extern/cista/test/offset_graph_test.cc new file mode 100755 index 00000000..df8a1ef9 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/offset_graph_test.cc @@ -0,0 +1,250 @@ +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/mmap.h" +#include "cista/serialization.h" +#endif + +namespace data = cista::offset; + +constexpr auto const CHECKSUM_INTEGRITY_AND_VERSION = + sizeof(void*) == 4 ? 13555853665375988614ULL : 10074751584048131577ULL; +constexpr auto const CHECKSUM_BIG_ENDIAN = + sizeof(void*) == 4 ? 13950808542881900738ULL : 14201103843034132153ULL; + +namespace graphns::offset { + +struct node; + +using node_id_t = uint32_t; + +struct edge { + data::ptr from_; + data::ptr to_; +}; + +struct node { + void add_edge(edge* e) { edges_.emplace_back(e); } + node_id_t id() const { return id_; } + + node_id_t id_{0}; + node_id_t fill_{0}; + data::vector> edges_; + data::string name_; +}; + +struct graph { + node* make_node(data::string name) { + return nodes_ + .emplace_back(data::make_unique( + node{next_node_id_++, 0, data::vector>(0u), + std::move(name)})) + .get(); + } + + edge* make_edge(node_id_t const from, node_id_t const to) { + return edges_ + .emplace_back( + data::make_unique(edge{nodes_[from].get(), nodes_[to].get()})) + .get(); + } + + data::vector> nodes_; + data::vector> edges_; + node_id_t next_node_id_{0}; + node_id_t fill_{0}; +}; + +} // namespace graphns::offset + +using namespace graphns::offset; + +inline std::set bfs(node const* entry) { + std::queue q; + std::set visited; + + q.emplace(entry); + + while (!q.empty()) { + auto const next = q.front(); + q.pop(); + + if (visited.find(next) != end(visited)) { + continue; + } + + visited.emplace(next); + + for (auto const& e : next->edges_) { + q.emplace(e->to_); + } + } + + return visited; +} + +TEST_CASE("graph offset serialize file") { + constexpr auto const FILENAME = "offset_graph.bin"; + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION; + + std::remove(FILENAME); + + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::file f{FILENAME, "w+"}; + cista::serialize(f, g); + + CHECK(f.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + } // EOL graph + + { + cista::file f{FILENAME, "r"}; + CHECK(f.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + } + + auto b = cista::file(FILENAME, "r").content(); + CHECK(cista::hash(b) == CHECKSUM_INTEGRITY_AND_VERSION); + + auto const g = cista::deserialize(b); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} + +TEST_CASE("graph offset serialize buf") { + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION; + + cista::byte_buf buf; + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::buf b; + cista::serialize(b, g); + + CHECK(b.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + + buf = std::move(b.buf_); + } // EOL graph + + CHECK(cista::hash(buf) == CHECKSUM_INTEGRITY_AND_VERSION); + + auto const g = cista::deserialize(buf); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} + +TEST_CASE("graph offset serialize mmap file") { + constexpr auto const FILENAME = "offset_graph_mmap.bin"; + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION; + + std::remove(FILENAME); + + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::buf mmap{cista::mmap{FILENAME}}; + mmap.buf_.reserve(512); + cista::serialize(mmap, g); + CHECK(mmap.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + } // EOL graph + +#if defined(CISTA_LITTLE_ENDIAN) + auto b = cista::mmap(FILENAME, cista::mmap::protection::READ); +#else + auto b = cista::file(FILENAME, "r").content(); +#endif + auto const g = cista::deserialize(b); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} + +TEST_CASE("graph offset serialize endian test") { + constexpr auto const FILENAME = "offset_graph_big_endian.bin"; + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION | + cista::mode::SERIALIZE_BIG_ENDIAN | cista::mode::DEEP_CHECK; + + std::remove(FILENAME); + + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::buf mmap{cista::mmap{FILENAME}}; + mmap.buf_.reserve(512); + cista::serialize(mmap, g); + CHECK(mmap.checksum() == CHECKSUM_BIG_ENDIAN); + } // EOL graph + + auto b = cista::file(FILENAME, "r").content(); + auto const g = cista::deserialize(b); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/offset_ptr_vector_test.cc b/parallel/parallel_src/extern/cista/test/offset_ptr_vector_test.cc new file mode 100644 index 00000000..2c058e45 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/offset_ptr_vector_test.cc @@ -0,0 +1,96 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::offset; + +TEST_CASE("offset vector serialize") { + cista::byte_buf buf; + { + data::vector vec; + vec.push_back(1); + vec.push_back(2); + vec.push_back(3); + vec.push_back(4); + + int32_t j = 1; + for (auto const i : vec) { + CHECK(i == j++); + } + + buf = serialize(vec); + } + + auto const vec = cista::deserialize>(buf); + int32_t j = 1; + for (auto const i : *vec) { + CHECK(i == j++); + } +} + +TEST_CASE("offset string serialize") { + constexpr auto const s = "The quick brown fox jumps over the lazy dog"; + + cista::byte_buf buf; + { + data::string str{s}; + buf = serialize(str); + } + + auto const str = cista::deserialize(buf); + CHECK(*str == s); +} + +TEST_CASE("offset unique_ptr serialize") { + cista::byte_buf buf; + { + auto const ptr = data::make_unique(33); + buf = serialize(ptr); + } + + auto const ptr = cista::deserialize>(buf); + CHECK(**ptr == 33); +} + +TEST_CASE("offset_ptr serialize") { + struct serialize_me { + data::unique_ptr i_{data::make_unique(77)}; + data::ptr raw_{i_.get()}; + }; + + cista::byte_buf buf; + + { + serialize_me obj; + buf = cista::serialize(obj); + } // EOL obj + + auto const deserialized = cista::deserialize(buf); + CHECK(deserialized->raw_.get() == deserialized->i_.get()); + CHECK(*deserialized->raw_ == 77); + CHECK(*deserialized->i_.get() == 77); +} + +TEST_CASE("offset_ptr serialize pending") { + struct serialize_me { + data::ptr raw_{nullptr}; + data::unique_ptr i_{data::make_unique(77)}; + }; + + cista::byte_buf buf; + + { + serialize_me obj; + obj.raw_ = obj.i_.get(); + buf = cista::serialize(obj); + } // EOL obj + + auto const serialized = cista::deserialize(buf); + CHECK(serialized->raw_.get() == serialized->i_.get()); + CHECK(*serialized->raw_ == 77); + CHECK(*serialized->i_.get() == 77); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/offset_security_test.cc b/parallel/parallel_src/extern/cista/test/offset_security_test.cc new file mode 100644 index 00000000..8142a21c --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/offset_security_test.cc @@ -0,0 +1,169 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::offset; + +namespace { +inline void test_sec_value_overflow() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::string d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::string{"testtes"}}}; + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me)); + buf.resize(buf.size() - 1); + + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_string_overflow() { + auto constexpr const long_str = "The quick brown fox jumps over the lazy dog"; + + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::string d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::string{long_str}}}; + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me) + std::strlen(long_str)); + buf.resize(buf.size() - 1); + + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_vector_overflow() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::vector d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::vector{}}}; + obj.j_.d_.push_back(1); + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me) + sizeof(int)); + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); + + { + serialize_me obj{1, {2, 3, data::vector{}}}; + buf = cista::serialize(obj); + } + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_unique_ptr_overflow_unset() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::unique_ptr d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::unique_ptr{}}}; + buf = cista::serialize(obj); + } + + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_unique_ptr_overflow_set() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::unique_ptr d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::unique_ptr{}}}; + obj.j_.d_ = data::make_unique(3); + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me) + sizeof(int)); + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_array_overflow() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::array d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, {}}}; + obj.j_.d_[0] = 1; + obj.j_.d_[1] = 2; + obj.j_.d_[2] = 3; + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me)); + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); +} +} // namespace + +TEST_CASE("sec offset test value overflow") { test_sec_value_overflow(); } +TEST_CASE("sec offset test string overflow") { test_sec_string_overflow(); } +TEST_CASE("sec offset test vector overflow") { test_sec_vector_overflow(); } +TEST_CASE("sec offset test unique ptr overflow unset") { + test_sec_unique_ptr_overflow_unset(); +} +TEST_CASE("sec offset test unique ptr overflow set") { + test_sec_unique_ptr_overflow_set(); +} +TEST_CASE("sec offset test array overflow") { test_sec_array_overflow(); } \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/offset_void_ptr_test.cc b/parallel/parallel_src/extern/cista/test/offset_void_ptr_test.cc new file mode 100644 index 00000000..fc8474af --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/offset_void_ptr_test.cc @@ -0,0 +1,16 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/offset_ptr.h" +#endif + +TEST_CASE("offset ptr to void test") { + auto const a = 77; + auto const ptr = cista::offset_ptr{&a}; + auto const null = cista::offset_ptr{nullptr}; + CHECK(*reinterpret_cast(ptr.get()) == 77); + CHECK(!static_cast(null)); + CHECK(static_cast(ptr)); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/pointer_test.cc b/parallel/parallel_src/extern/cista/test/pointer_test.cc new file mode 100644 index 00000000..dad57587 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/pointer_test.cc @@ -0,0 +1,28 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::raw; + +TEST_CASE("pointer serialization") { + struct serialize_me { + data::unique_ptr i_{data::make_unique(77)}; + data::ptr raw_{i_.get()}; + }; + + cista::byte_buf buf; + + { + serialize_me obj; + buf = cista::serialize(obj); + } // EOL obj + + auto const deserialized = cista::deserialize(buf); + CHECK(deserialized->raw_ == deserialized->i_.get()); + CHECK(*deserialized->raw_ == 77); + CHECK(*deserialized->i_.get() == 77); +} diff --git a/parallel/parallel_src/extern/cista/test/printable_test.cc b/parallel/parallel_src/extern/cista/test/printable_test.cc new file mode 100644 index 00000000..dda6eaa1 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/printable_test.cc @@ -0,0 +1,31 @@ +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/reflection/printable.h" +#endif + +namespace { + +enum class Color : int { RED, BLUE, GREEN }; + +struct a { + CISTA_PRINTABLE(a) + int i_ = 1; + int j_ = 2; + double d_ = 100.0; + std::string s_ = "hello world"; + Color c_ = Color::BLUE; +}; + +} // namespace + +TEST_CASE("printable") { + a instance; + std::stringstream ss; + ss << instance; + CHECK(ss.str() == "{1, 2, 100, hello world, 1}"); +} diff --git a/parallel/parallel_src/extern/cista/test/raw_graph_test.cc b/parallel/parallel_src/extern/cista/test/raw_graph_test.cc new file mode 100644 index 00000000..beb01a0d --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/raw_graph_test.cc @@ -0,0 +1,163 @@ +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::raw; + +constexpr auto const CHECKSUM_INTEGRITY_AND_VERSION = + sizeof(void*) == 4 ? 13555853665375988614ULL : 10074751584048131577ULL; + +namespace graphns::raw { + +struct node; + +using node_id_t = uint32_t; + +struct edge { + data::ptr from_; + data::ptr to_; +}; + +struct node { + void add_edge(edge* e) { edges_.emplace_back(e); } + node_id_t id() const { return id_; } + + node_id_t id_{0}; + node_id_t fill_{0}; + data::vector> edges_; + data::string name_; +}; + +struct graph { + node* make_node(data::string name) { + return nodes_ + .emplace_back(data::make_unique( + node{next_node_id_++, 0, data::vector>(0u), + std::move(name)})) + .get(); + } + + edge* make_edge(node_id_t const from, node_id_t const to) { + return edges_ + .emplace_back( + data::make_unique(edge{nodes_[from].get(), nodes_[to].get()})) + .get(); + } + + data::vector> nodes_; + data::vector> edges_; + node_id_t next_node_id_{0}; + node_id_t fill_{0}; +}; + +} // namespace graphns::raw + +using namespace graphns::raw; + +inline std::set bfs(node const* entry) { + std::queue q; + std::set visited; + + q.emplace(entry); + + while (!q.empty()) { + auto const next = q.front(); + q.pop(); + + if (visited.find(next) != end(visited)) { + continue; + } + + visited.emplace(next); + + for (auto const& e : next->edges_) { + q.emplace(e->to_); + } + } + + return visited; +} + +TEST_CASE("graph raw serialize file") { + constexpr auto const FILENAME = "raw_graph.bin"; + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION; + + std::remove(FILENAME); + + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::file f{FILENAME, "w+"}; + cista::serialize(f, g); + + CHECK(f.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + } // EOL graph + + auto b = cista::file(FILENAME, "r").content(); + CHECK(cista::hash(b) == CHECKSUM_INTEGRITY_AND_VERSION); + + auto const g = cista::deserialize(b); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} + +TEST_CASE("graph raw serialize buf") { + constexpr auto const MODE = + cista::mode::WITH_INTEGRITY | cista::mode::WITH_VERSION; + + cista::byte_buf buf; + { + graph g; + + auto const n1 = g.make_node(data::string{"NODE A"}); + auto const n2 = g.make_node(data::string{"NODE B"}); + auto const n3 = g.make_node(data::string{"NODE C"}); + + auto const e1 = g.make_edge(n1->id(), n2->id()); + auto const e2 = g.make_edge(n2->id(), n3->id()); + auto const e3 = g.make_edge(n3->id(), n1->id()); + + n1->add_edge(e1); + n2->add_edge(e2); + n3->add_edge(e3); + + cista::buf b; + cista::serialize(b, g); + + CHECK(b.checksum() == CHECKSUM_INTEGRITY_AND_VERSION); + + buf = std::move(b.buf_); + } // EOL graph + + CHECK(cista::hash(buf) == CHECKSUM_INTEGRITY_AND_VERSION); + + auto const g = cista::deserialize(buf); + auto const visited = bfs(g->nodes_[0].get()); + unsigned i = 0; + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE A"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE B"}); + CHECK((*std::next(begin(visited), i++))->name_ == data::string{"NODE C"}); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/raw_security_test.cc b/parallel/parallel_src/extern/cista/test/raw_security_test.cc new file mode 100644 index 00000000..a4a3ac79 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/raw_security_test.cc @@ -0,0 +1,169 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::raw; + +namespace { +inline void test_sec_value_overflow() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::string d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::string{"testtes"}}}; + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me)); + buf.resize(buf.size() - 1); + + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_string_overflow() { + auto constexpr const long_str = "The quick brown fox jumps over the lazy dog"; + + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::string d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::string{long_str}}}; + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me) + std::strlen(long_str)); + buf.resize(buf.size() - 1); + + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_vector_overflow() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::vector d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::vector{}}}; + obj.j_.d_.push_back(1); + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me) + sizeof(int)); + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); + + { + serialize_me obj{1, {2, 3, data::vector{}}}; + buf = cista::serialize(obj); + } + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_unique_ptr_overflow_unset() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::unique_ptr d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::unique_ptr{}}}; + buf = cista::serialize(obj); + } + + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_unique_ptr_overflow_set() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::unique_ptr d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, data::unique_ptr{}}}; + obj.j_.d_ = data::make_unique(3); + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me) + sizeof(int)); + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); +} + +inline void test_sec_array_overflow() { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + data::array d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, {}}}; + obj.j_.d_[0] = 1; + obj.j_.d_[1] = 2; + obj.j_.d_[2] = 3; + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me)); + buf.resize(buf.size() - 1); + CHECK_THROWS(cista::deserialize(buf)); +} +} // namespace + +TEST_CASE("sec raw test value overflow") { test_sec_value_overflow(); } +TEST_CASE("sec raw test string overflow") { test_sec_string_overflow(); } +TEST_CASE("sec raw test vector overflow") { test_sec_vector_overflow(); } +TEST_CASE("sec raw test unique ptr overflow unset") { + test_sec_unique_ptr_overflow_unset(); +} +TEST_CASE("sec raw test unique ptr overflow set") { + test_sec_unique_ptr_overflow_set(); +} +TEST_CASE("sec raw test array overflow") { test_sec_array_overflow(); } \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/single_member_struct.cc b/parallel/parallel_src/extern/cista/test/single_member_struct.cc new file mode 100644 index 00000000..52f0fc8d --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/single_member_struct.cc @@ -0,0 +1,16 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/reflection/for_each_field.h" +#endif + +struct single_member_struct { + int i_{7}; +}; + +TEST_CASE("single member struct test") { + single_member_struct s; + cista::for_each_field(s, [](auto&& f) { CHECK(f == 7); }); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/std_vector_test.cc b/parallel/parallel_src/extern/cista/test/std_vector_test.cc new file mode 100755 index 00000000..eea6d9ce --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/std_vector_test.cc @@ -0,0 +1,93 @@ +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::raw; + +namespace std { + +template +inline void serialize(Ctx& c, std::vector const* origin, + cista::offset_t const offset) { + static_assert(sizeof(std::vector) >= + sizeof(cista::NULLPTR_OFFSET) + sizeof(uint64_t)); + + auto const start = origin->empty() + ? cista::NULLPTR_OFFSET + : c.write(origin->data(), origin->size() * sizeof(T), + std::alignment_of_v); + + c.write(offset, static_cast(origin->size())); + c.write(offset + static_cast(sizeof(uint64_t)), start); + + for (auto i = 0u; i < origin->size(); ++i) { + cista::serialize( + c, &(*origin)[i], + start + static_cast(i * cista::serialized_size())); + } +} + +template +inline void deserialize(Ctx const& c, std::vector* el) { + auto const size = *reinterpret_cast(el); + auto const data = reinterpret_cast( + c.from_ + *reinterpret_cast( + reinterpret_cast(el) + sizeof(uint64_t))); + + for (auto it = data; it != data + size; ++it) { + cista::deserialize(c, it); + } + + auto& vec = *new (static_cast(el)) std::vector(); + + if (size != 0 && data != nullptr) { + vec.insert(begin(vec), std::make_move_iterator(data), + std::make_move_iterator(data + size)); + } +} + +template +cista::hash_t type_hash(std::vector const&, cista::hash_t h) { + h = cista::hash_combine(h, cista::type_hash>()); + return cista::type_hash(T{}, h); +} + +} // namespace std + +TEST_CASE("vector test") { + using serialize_me = std::vector>>; + + cista::byte_buf buf; + + { + serialize_me obj(3); + obj[0].emplace_back(std::vector({1, 2, 3})); + obj[0].emplace_back(std::vector({2, 3, 4})); + obj[0].emplace_back(std::vector({3, 4, 5})); + obj[1].emplace_back(std::vector({4, 5, 6})); + obj[1].emplace_back(std::vector({5, 6, 7})); + obj[2].emplace_back(std::vector({6, 7, 8})); + buf = serialize(obj); + } // EOL obj + + auto const& serialized = *cista::unchecked_deserialize(buf); + CHECK(serialized[0][0][0] == 1); + CHECK(serialized[0][1][0] == 2); + CHECK(serialized[0][2][0] == 3); + CHECK(serialized[1][0][0] == 4); + CHECK(serialized[1][1][0] == 5); + CHECK(serialized[2][0][0] == 6); + + for (auto& v1 : serialized) { + for (auto& v2 : v1) { + v2.~vector(); + } + } + serialized.~vector(); +} diff --git a/parallel/parallel_src/extern/cista/test/string_test.cc b/parallel/parallel_src/extern/cista/test/string_test.cc new file mode 100644 index 00000000..5f9cfb05 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/string_test.cc @@ -0,0 +1,443 @@ +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/string.h" +#include "cista/hash.h" +#endif + +using cista::raw::string; + +constexpr auto const CORNER_CASE_SHORT_14 = "01234567891234"; +constexpr auto const CORNER_CASE_SHORT_15 = "012345678912345"; +constexpr auto const CORNER_CASE_LONG_16 = "0123456789123456"; +constexpr auto const LONG_STR = "hello world hello world"; +constexpr auto const SHORT_STR = "hello world"; + +TEST_CASE("self assignment") { + // case short string + cista::raw::string test_short{"test_short"}; + std::string output_short_pre{test_short}; + test_short = test_short; + std::string output_short_post{test_short}; + // test_short is empty now + CHECK(output_short_pre == output_short_post); + + // case long string + cista::raw::string test_long{"test_long_12345678901234567890123456789012"}; + std::string output_long_pre{test_long}; + test_long = test_long; + std::string output_long_post{test_long}; + // test_long is filled with 0x01 now + CHECK(output_long_pre == output_long_post); +} + +TEST_CASE("string init") { + auto s = string{}; + CHECK(!s.is_short()); + CHECK(s.size() == 0); + CHECK(s.data() == nullptr); +} + +TEST_CASE("string long short corner 14") { + auto s = string{CORNER_CASE_SHORT_14, string::owning}; + CHECK(s.is_short()); + CHECK(s.size() == std::strlen(CORNER_CASE_SHORT_14)); + CHECK(s.view() == CORNER_CASE_SHORT_14); +} + +TEST_CASE("string erase") { + auto uut = std::vector>{}; + auto ref = std::vector{}; + for (auto const s : + {CORNER_CASE_SHORT_14, CORNER_CASE_SHORT_15, CORNER_CASE_LONG_16}) { + auto x = cista::generic_string{ + s, cista::generic_string::non_owning}; + x.erase(3, 7); + uut.emplace_back(std::move(x)); + + auto y = std::string{s}; + y.erase(3, 7); + ref.emplace_back(std::move(y)); + } + + for (auto i = 0U; i != ref.size(); ++i) { + CHECK(ref[i] == uut[i].view()); + } +} + +TEST_CASE("string long short corner 15") { + auto s = string{CORNER_CASE_SHORT_15, string::owning}; + CHECK(s.is_short()); + CHECK(s.size() == std::strlen(CORNER_CASE_SHORT_15)); + CHECK(s.view() == CORNER_CASE_SHORT_15); +} + +TEST_CASE("string long short corner 16") { + auto s = string{CORNER_CASE_LONG_16, string::owning}; + CHECK(!s.is_short()); + CHECK(s.size() == std::strlen(CORNER_CASE_LONG_16)); + CHECK(s.view() == CORNER_CASE_LONG_16); +} + +TEST_CASE("string long short") { + auto s = string{SHORT_STR, string::owning}; + CHECK(s.view() == SHORT_STR); + CHECK(s.is_short()); + + s.set_owning(CORNER_CASE_LONG_16); + CHECK(!s.is_short()); + CHECK(s.view() == CORNER_CASE_LONG_16); + + s.set_owning(LONG_STR); + CHECK(!s.is_short()); + CHECK(s.view() == LONG_STR); +} + +TEST_CASE("string dealloc long to short") { + string s = "one two"; + CHECK(s.size() == std::strlen("one two")); + CHECK(s.is_short()); + s.set_non_owning(""); +} + +TEST_CASE("string copy assign and copy construct") { + auto s0 = string{LONG_STR, string::owning}; + auto s1 = string{s0}; + CHECK(s0 == s1); + CHECK(s1.view() == LONG_STR); + + string s2; + s2 = s0; + CHECK(s0 == s2); + CHECK(s2.view() == LONG_STR); +} + +TEST_CASE("string hash") { + auto str = string{""}; + auto h = cista::hash(str, cista::BASE_HASH); + CHECK(cista::BASE_HASH == h); +} + +TEST_CASE("string starts_with") { + string s = "abacaba"; + + CHECK(s.starts_with("abac") == true); + CHECK(s.starts_with("abacaba") == true); + CHECK(s.starts_with("abacaba_") == false); + CHECK(s.starts_with("a") == true); + CHECK(s.starts_with("") == true); + CHECK(s.starts_with("abad") == false); + + CHECK(s.starts_with(string{"abac"}) == true); + CHECK(s.starts_with(string{"abacaba"}) == true); + CHECK(s.starts_with(string{"abacaba_"}) == false); + CHECK(s.starts_with(string{"a"}) == true); + CHECK(s.starts_with(string{""}) == true); + CHECK(s.starts_with(string{"abad"}) == false); + + CHECK(s.starts_with(std::string{"abac"}) == true); + CHECK(s.starts_with(std::string{"abacaba"}) == true); + CHECK(s.starts_with(std::string{"abacaba_"}) == false); + CHECK(s.starts_with(std::string{"a"}) == true); + CHECK(s.starts_with(std::string{""}) == true); + CHECK(s.starts_with(std::string{"\0", 1}) == false); + CHECK(s.starts_with(std::string{"abad"}) == false); + + CHECK(s.starts_with(std::string_view{"abac"}) == true); + CHECK(s.starts_with(std::string_view{"abacaba"}) == true); + CHECK(s.starts_with(std::string_view{"abacaba_"}) == false); + CHECK(s.starts_with(std::string_view{"a"}) == true); + CHECK(s.starts_with(std::string_view{""}) == true); + CHECK(s.starts_with(std::string_view{"\0", 1}) == false); + CHECK(s.starts_with(std::string_view{"abad"}) == false); + + CHECK(s.starts_with('a') == true); + CHECK(s.starts_with('b') == false); + CHECK(s.starts_with('\0') == false); +} + +TEST_CASE("string ends_with") { + string s = "abacaba"; + + CHECK(s.ends_with("caba") == true); + CHECK(s.ends_with("abacaba") == true); + CHECK(s.ends_with("abacaba_") == false); + CHECK(s.ends_with("a") == true); + CHECK(s.ends_with("") == true); + CHECK(s.ends_with("daba") == false); + + CHECK(s.ends_with(string{"caba"}) == true); + CHECK(s.ends_with(string{"abacaba"}) == true); + CHECK(s.ends_with(string{"abacaba_"}) == false); + CHECK(s.ends_with(string{"a"}) == true); + CHECK(s.ends_with(string{""}) == true); + CHECK(s.ends_with(string{"daba"}) == false); + + CHECK(s.ends_with(std::string{"caba"}) == true); + CHECK(s.ends_with(std::string{"abacaba"}) == true); + CHECK(s.ends_with(std::string{"abacaba_"}) == false); + CHECK(s.ends_with(std::string{"a"}) == true); + CHECK(s.ends_with(std::string{""}) == true); + CHECK(s.ends_with(std::string{"\0", 1}) == false); + CHECK(s.ends_with(std::string{"daba"}) == false); + + CHECK(s.ends_with(std::string_view{"caba"}) == true); + CHECK(s.ends_with(std::string_view{"abacaba"}) == true); + CHECK(s.ends_with(std::string_view{"abacaba_"}) == false); + CHECK(s.ends_with(std::string_view{"a"}) == true); + CHECK(s.ends_with(std::string_view{""}) == true); + CHECK(s.ends_with(std::string_view{"\0", 1}) == false); + CHECK(s.ends_with(std::string_view{"daba"}) == false); + + CHECK(s.ends_with('a') == true); + CHECK(s.ends_with('b') == false); + CHECK(s.ends_with('\0') == false); +} + +using cista::raw::u16string; +using cista::raw::u16string_view; + +const auto constexpr U16STR_SHORT = u"0123"; +const auto constexpr U16STR_SHORT_CORNER_CASE = u"0123456"; +const auto constexpr U16STR_LONG_CORNER_CASE = u"01234567"; +const auto constexpr U16STR_LONG = u"0123456789ABCDEF\xD83D\xDCBB"; + +TEST_CASE("u16string long short") { + u16string s[] = {u"", U16STR_SHORT, U16STR_SHORT_CORNER_CASE, + U16STR_LONG_CORNER_CASE, U16STR_LONG}; + + CHECK(!s[0].is_short()); + CHECK(s[0].size() == 0); + CHECK(s[0].data() == nullptr); + + CHECK(s[1].is_short()); + CHECK(s[1].size() == 4); + CHECK(s[1].view() == U16STR_SHORT); + + CHECK(s[2].is_short()); + CHECK(s[2].size() == 7); + CHECK(s[2].view() == U16STR_SHORT_CORNER_CASE); + + CHECK(!s[3].is_short()); + CHECK(s[3].size() == 8); + CHECK(s[3].view() == U16STR_LONG_CORNER_CASE); + + CHECK(!s[4].is_short()); + CHECK(s[4].size() == 18); + CHECK(s[4].view() == U16STR_LONG); +} + +TEST_CASE("u16string copy") { + u16string s, s_s{U16STR_SHORT}, s_l{U16STR_LONG}; + u16string_view sv_s{U16STR_SHORT}, sv_l{U16STR_LONG}; + + s = U16STR_SHORT; + CHECK(s.view() == U16STR_SHORT); + + s = U16STR_LONG; + CHECK(s.view() == U16STR_LONG); + + s = s_s; + CHECK(s == s_s); + CHECK(s.view() == U16STR_SHORT); + + s = s_l; + CHECK(s == s_l); + CHECK(s.view() == U16STR_LONG); + + s = sv_s; + CHECK(s == sv_s); + CHECK(s.view() == U16STR_SHORT); + + s = sv_l; + CHECK(s == sv_l); + CHECK(s.view() == U16STR_LONG); +} + +TEST_CASE("u16string erase") { + u16string s[] = {U16STR_SHORT, U16STR_SHORT_CORNER_CASE, + U16STR_LONG_CORNER_CASE, U16STR_LONG}; + std::u16string ref[] = {U16STR_SHORT, U16STR_SHORT_CORNER_CASE, + U16STR_LONG_CORNER_CASE, U16STR_LONG}; + + for (u16string::msize_t i = 0; i < 4; ++i) { + s[i].erase(1, i + 2); + ref[i].erase(1, i + 2); + CHECK(s[i] == ref[i]); + } + + for (u16string::msize_t i = 0; i < 4; ++i) { + s[i].erase(0, s[i].size()); + CHECK(s[i].empty()); + } +} + +TEST_CASE("u16string starts_with") { + u16string s[] = {U16STR_SHORT, U16STR_SHORT_CORNER_CASE, + U16STR_LONG_CORNER_CASE, U16STR_LONG}; + + for (auto i = 1; i < 4; ++i) { + CHECK(s[i].starts_with(s[i - 1])); + } + for (auto i = 1; i < 4; ++i) { + CHECK(!s[i - 1].starts_with(s[i])); + } + for (auto i = 0; i < 4; ++i) { + CHECK(s[i].starts_with(u'0')); + CHECK(!s[i].starts_with(u'A')); + } +} + +TEST_CASE("u16string ends_with") { + u16string s[] = {U16STR_SHORT, U16STR_SHORT_CORNER_CASE, + U16STR_LONG_CORNER_CASE, U16STR_LONG}; + + CHECK(s[0].ends_with(U16STR_SHORT)); + CHECK(s[0].ends_with(u"123")); + CHECK(s[0].ends_with(u'3')); + CHECK(!s[0].ends_with(u"01234")); + CHECK(!s[0].ends_with(u'A')); + + CHECK(s[1].ends_with(U16STR_SHORT_CORNER_CASE)); + CHECK(s[1].ends_with(u"456")); + CHECK(s[1].ends_with(u'6')); + CHECK(!s[1].ends_with(u"01234567")); + CHECK(!s[1].ends_with(u'A')); + + CHECK(s[2].ends_with(U16STR_LONG_CORNER_CASE)); + CHECK(s[2].ends_with(u"567")); + CHECK(s[2].ends_with(u'7')); + CHECK(!s[2].ends_with(u"012345677")); + CHECK(!s[2].ends_with(u'A')); + + CHECK(s[3].ends_with(U16STR_LONG)); + CHECK(s[3].ends_with(u"F\xD83D\xDCBB")); + CHECK(s[3].ends_with(u'\xDCBB')); + CHECK(!s[3].ends_with(u'A')); +} + +using cista::raw::u32string; +using cista::raw::u32string_view; + +const auto constexpr U32STR_SHORT = U"01"; +const auto constexpr U32STR_SHORT_CORNER_CASE = U"012"; +const auto constexpr U32STR_LONG_CORNER_CASE = U"0123"; +const auto constexpr U32STR_LONG = U"0123456789ABCDEF\U0001F4BB"; + +TEST_CASE("u32string long short") { + u32string s[] = {U"", U32STR_SHORT, U32STR_SHORT_CORNER_CASE, + U32STR_LONG_CORNER_CASE, U32STR_LONG}; + + CHECK(!s[0].is_short()); + CHECK(s[0].size() == 0); + CHECK(s[0].data() == nullptr); + + CHECK(s[1].is_short()); + CHECK(s[1].size() == 2); + CHECK(s[1].view() == U32STR_SHORT); + + CHECK(s[2].is_short()); + CHECK(s[2].size() == 3); + CHECK(s[2].view() == U32STR_SHORT_CORNER_CASE); + + CHECK(!s[3].is_short()); + CHECK(s[3].size() == 4); + CHECK(s[3].view() == U32STR_LONG_CORNER_CASE); + + CHECK(!s[4].is_short()); + CHECK(s[4].size() == 17); + CHECK(s[4].view() == U32STR_LONG); +} + +TEST_CASE("u32string copy") { + u32string s, s_s{U32STR_SHORT}, s_l{U32STR_LONG}; + u32string_view sv_s{U32STR_SHORT}, sv_l{U32STR_LONG}; + + s = U32STR_SHORT; + CHECK(s.view() == U32STR_SHORT); + + s = U32STR_LONG; + CHECK(s.view() == U32STR_LONG); + + s = s_s; + CHECK(s == s_s); + CHECK(s.view() == U32STR_SHORT); + + s = s_l; + CHECK(s == s_l); + CHECK(s.view() == U32STR_LONG); + + s = sv_s; + CHECK(s == sv_s); + CHECK(s.view() == U32STR_SHORT); + + s = sv_l; + CHECK(s == sv_l); + CHECK(s.view() == U32STR_LONG); +} + +TEST_CASE("u32string erase") { + u32string s[] = {U32STR_SHORT, U32STR_SHORT_CORNER_CASE, + U32STR_LONG_CORNER_CASE, U32STR_LONG}; + std::u32string ref[] = {U32STR_SHORT, U32STR_SHORT_CORNER_CASE, + U32STR_LONG_CORNER_CASE, U32STR_LONG}; + + for (u32string::msize_t i = 0; i < 4; ++i) { + s[i].erase(1, i + 1); + ref[i].erase(1, i + 1); + CHECK(s[i] == ref[i]); + } + + for (u32string::msize_t i = 0; i < 4; ++i) { + s[i].erase(0, s[i].size()); + CHECK(s[i].empty()); + } +} + +TEST_CASE("u32string starts_with") { + u32string s[] = {U32STR_SHORT, U32STR_SHORT_CORNER_CASE, + U32STR_LONG_CORNER_CASE, U32STR_LONG}; + + for (auto i = 1; i < 4; ++i) { + CHECK(s[i].starts_with(s[i - 1])); + } + for (auto i = 1; i < 4; ++i) { + CHECK(!s[i - 1].starts_with(s[i])); + } + for (auto i = 0; i < 4; ++i) { + CHECK(s[i].starts_with(U'0')); + CHECK(!s[i].starts_with(U'A')); + } +} + +TEST_CASE("u32string ends_with") { + u32string s[] = {U32STR_SHORT, U32STR_SHORT_CORNER_CASE, + U32STR_LONG_CORNER_CASE, U32STR_LONG}; + + CHECK(s[0].ends_with(U32STR_SHORT)); + CHECK(s[0].ends_with(U'1')); + CHECK(!s[0].ends_with(U"012")); + CHECK(!s[0].ends_with(U'A')); + + CHECK(s[1].ends_with(U32STR_SHORT_CORNER_CASE)); + CHECK(s[1].ends_with(U"12")); + CHECK(s[1].ends_with(U'2')); + CHECK(!s[1].ends_with(U"0123")); + CHECK(!s[1].ends_with(U'A')); + + CHECK(s[2].ends_with(U32STR_LONG_CORNER_CASE)); + CHECK(s[2].ends_with(U"123")); + CHECK(s[2].ends_with(U'3')); + CHECK(!s[2].ends_with(U"01234")); + CHECK(!s[2].ends_with(U'A')); + + CHECK(s[3].ends_with(U32STR_LONG)); + CHECK(s[3].ends_with(U"EF\U0001F4BB")); + CHECK(s[3].ends_with(U'\U0001F4BB')); + CHECK(!s[3].ends_with(U'A')); +} diff --git a/parallel/parallel_src/extern/cista/test/struct_serialize_test.cc b/parallel/parallel_src/extern/cista/test/struct_serialize_test.cc new file mode 100644 index 00000000..d2b2abd4 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/struct_serialize_test.cc @@ -0,0 +1,34 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +TEST_CASE("struct serialization") { + struct serialize_me { + int a_{0}; + struct inner { + int b_{0}; + int c_{0}; + cista::raw::string d_; + } j_; + }; + + cista::byte_buf buf; + + { + serialize_me obj{1, {2, 3, cista::raw::string{"testtes"}}}; + buf = cista::serialize(obj); + } // EOL obj + + CHECK(buf.size() == sizeof(serialize_me)); + + auto const serialized = + cista::deserialize(&buf[0], &buf[0] + buf.size()); + CHECK(serialized->a_ == 1); + CHECK(serialized->j_.b_ == 2); + CHECK(serialized->j_.c_ == 3); + CHECK(serialized->j_.d_ == cista::raw::string{"testtes"}); +} diff --git a/parallel/parallel_src/extern/cista/test/to_tuple_test.cc b/parallel/parallel_src/extern/cista/test/to_tuple_test.cc new file mode 100644 index 00000000..55d7b0e1 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/to_tuple_test.cc @@ -0,0 +1,35 @@ +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/reflection/to_tuple.h" +#endif + +struct a { + int i_ = 1; + int j_ = 2; + double d_ = 100.0; + std::string s_ = "hello world"; +}; + +TEST_CASE("to_tuple") { + a instance; + + CHECK(instance.i_ == 1); + CHECK(instance.j_ == 2); + CHECK(instance.d_ == 100.0); + CHECK(instance.s_ == "hello world"); + + std::get<0>(cista::to_tuple(instance)) = 5; + std::get<1>(cista::to_tuple(instance)) = 7; + std::get<2>(cista::to_tuple(instance)) = 2.0; + std::get<3>(cista::to_tuple(instance)) = "yeah"; + + CHECK(instance.i_ == 5); + CHECK(instance.j_ == 7); + CHECK(instance.d_ == 2.0); + CHECK(instance.s_ == "yeah"); +} diff --git a/parallel/parallel_src/extern/cista/test/tuple_test.cc b/parallel/parallel_src/extern/cista/test/tuple_test.cc new file mode 100644 index 00000000..94b20d85 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/tuple_test.cc @@ -0,0 +1,272 @@ +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::offset; + +TEST_SUITE("tuple") { + TEST_CASE("tuple get") { + auto t = cista::tuple{1, .1, 'a'}; + static_assert(alignof(decltype(t)) == 8); + + cista::get<2>(t) = 'B'; + CHECK(cista::get<0>(t) == 1); + CHECK(cista::get<1>(t) == .1); + CHECK(cista::get<2>(t) == 'B'); + CHECK(cista::tuple_size_v == 3U); + cista::apply([](auto&... f) { ((f = {}), ...); }, t); + CHECK(cista::get<0>(t) == 0); + CHECK(cista::get<1>(t) == 0); + CHECK(cista::get<2>(t) == '\0'); + } + + TEST_CASE("tuple get with same types") { + auto t = cista::tuple{1, 42, 1336, 11}; + + cista::get<2>(t) += 1; + + CHECK(cista::get<0>(t) == 1); + CHECK(cista::get<1>(t) == 42); + CHECK(cista::get<2>(t) == 1337); + CHECK(cista::get<3>(t) == 11); + CHECK(cista::tuple_size_v == 4U); + cista::apply([](auto&... f) { ((f = {}), ...); }, t); + CHECK(cista::get<0>(t) == 0); + CHECK(cista::get<1>(t) == 0); + CHECK(cista::get<2>(t) == 0); + CHECK(cista::get<3>(t) == 0); + } + + TEST_CASE("tuple structured bindings") { + auto t = cista::tuple{1, 42, 1336, 11}; + auto& [t0, t1, t2, t3] = t; + t2 += 1; + CHECK(t0 == 1); + CHECK(t1 == 42); + CHECK(t2 == 1337); + CHECK(t3 == 11); + } + + TEST_CASE("tuple comparison") { + auto a = cista::tuple{2, 'a'}; + auto b = cista::tuple{1, 'b'}; + CHECK(!(a == b)); + CHECK(a != b); + CHECK(b < a); + CHECK(b <= a); + CHECK(a > b); + CHECK(!(b >= a)); + } + + TEST_CASE("tuple_element_t") { // NOLINT + static_assert( + std::is_same_v< + uint32_t, cista::tuple_element_t< + 0, cista::tuple>>); + static_assert( + std::is_same_v< + uint64_t, cista::tuple_element_t< + 1, cista::tuple>>); + static_assert( + std::is_same_v< + float, cista::tuple_element_t< + 2, cista::tuple>>); + static_assert( + std::is_same_v< + double, cista::tuple_element_t< + 3, cista::tuple>>); + } + + TEST_CASE("tuple - simple get") { // NOLINT + cista::tuple t = {42, 1082342034, 0.42F, + 0.001337}; + + CHECK(cista::get<0>(t) == 42); + CHECK(cista::get<1>(t) == 1082342034); + CHECK(cista::get<2>(t) == 0.42F); + CHECK(cista::get<3>(t) == 0.001337); + } + + TEST_CASE("tuple - simple get max") { // NOLINT + cista::tuple t2 = { + std::numeric_limits::max(), + std::numeric_limits::max(), std::numeric_limits::max(), + std::numeric_limits::max()}; + + CHECK(cista::get<0>(t2) == std::numeric_limits::max()); + CHECK(cista::get<1>(t2) == std::numeric_limits::max()); + CHECK(cista::get<2>(t2) == std::numeric_limits::max()); + CHECK(cista::get<3>(t2) == std::numeric_limits::max()); + } + + TEST_CASE("tuple - container get with preceding type") { // NOLINT + cista::tuple> t = {100, + {0.1F, 0.2F, 10.11F}}; + + auto& vec = cista::get<1>(t); + CHECK(vec.front() == 0.1F); + CHECK(vec[1] == 0.2F); + CHECK(vec.back() == 10.11F); + + CHECK(cista::get<0>(t) == 100); + } + + TEST_CASE("tuple - container get with succeeding type") { // NOLINT + cista::tuple, std::size_t> t = {{0.1F, 0.2F, 10.11F}, + 100}; + + auto& vec = cista::get<0>(t); + CHECK(vec.front() == 0.1F); + CHECK(vec[1] == 0.2F); + CHECK(vec.back() == 10.11F); + CHECK(cista::get<1>(t) == 100); + } + + TEST_CASE("tuple - container get prec and suc type") { // NOLINT + cista::tuple, std::size_t> t = { + 10, {0.1F, 0.2F, 10.11F}, 1000}; + + CHECK(cista::get<0>(t) == 10); + CHECK(cista::get<1>(t).front() == 0.1F); + CHECK(cista::get<1>(t)[1] == 0.2F); + CHECK(cista::get<1>(t).back() == 10.11F); + CHECK(cista::get<2>(t) == 1000); + } + + TEST_CASE("tuple - serialize primitives") { // NOLINT + using serialize_me = cista::tuple; + static_assert(alignof(serialize_me) == 8); + + std::vector buf; + { + serialize_me t = {42, 1082342034, 0.42F, 0.001337}; + buf = cista::serialize(t); + } + + serialize_me d_t = *cista::deserialize(buf); + + cista::get<0>(d_t) += 1; + + CHECK(cista::get<0>(d_t) == 43); + CHECK(cista::get<1>(d_t) == 1082342034); + CHECK(cista::get<2>(d_t) == 0.42F); + CHECK(cista::get<3>(d_t) == 0.001337); + } + + TEST_CASE("tuple - serialize container") { // NOLINT + std::vector buf; + + using serialize_me = + cista::tuple, std::size_t>; + + { + serialize_me t = {10, {0.1F, 0.2F, 10.11F}, 1000}; + buf = cista::serialize(t); + } + auto d_t = *cista::deserialize(buf); + + CHECK(cista::get<0>(d_t) == 10); + CHECK(cista::get<1>(d_t).front() == 0.1F); + CHECK(cista::get<1>(d_t)[1] == 0.2F); + CHECK(cista::get<1>(d_t).back() == 10.11F); + CHECK(cista::get<2>(d_t) == 1000); + } + + TEST_CASE("serialize struct with tuple") { // NOLINT + struct s { + cista::tuple, int> t_; + }; + + std::vector buf; + + { + s s; + s.t_ = {10, {1, 2, 3}, 1000}; + + buf = cista::serialize(s); + } + + auto d_s = *cista::deserialize(buf); + + cista::get<1>(d_s.t_).push_back(4); + + CHECK(cista::get<0>(d_s.t_) == 10); + CHECK(cista::get<1>(d_s.t_).front() == 1); + CHECK(cista::get<1>(d_s.t_)[1] == 2); + CHECK(cista::get<1>(d_s.t_)[2] == 3); + CHECK(cista::get<1>(d_s.t_).back() == 4); + CHECK(cista::get<2>(d_s.t_) == 1000); + } + + TEST_CASE("tuple serialize") { + namespace data = cista::offset; + using serialize_me = cista::tuple< + data::vector>>, + int>; + constexpr auto const MODE = cista::mode::WITH_VERSION; + + cista::byte_buf buf; + { + serialize_me obj; + std::get<0>(obj).emplace_back( + cista::tuple{data::string{"hello"}, + data::hash_set{ + data::string{"1"}, + data::string{"this is a very very very long string"}, + data::string{"3"}}}); + std::get<0>(obj).emplace_back( + cista::tuple{data::string{"world"}, + data::hash_set{ + data::string{"4"}, + data::string{"this is a very very very long string"}, + data::string{"6"}}}); + std::get<1>(obj) = 55; + buf = cista::serialize(obj); + } // EOL obj + + auto const& serialized = + *cista::unchecked_deserialize(buf); + CHECK( + (std::get<0>(serialized).at(0) == + cista::tuple{data::string{"hello"}, + data::hash_set{ + data::string{"1"}, + data::string{"this is a very very very long string"}, + data::string{"3"}}})); + CHECK( + (std::get<0>(serialized).at(1) == + cista::tuple{data::string{"world"}, + data::hash_set{ + data::string{"4"}, + data::string{"this is a very very very long string"}, + data::string{"6"}}})); + CHECK((std::get<1>(serialized) == 55)); + } + +#ifndef _WIN32 + template + void check_size() { + CHECK(sizeof(std::tuple) == sizeof(cista::tuple)); + } + + TEST_CASE("size check") { + using pixbuf = std::array; + enum class error_code { success, fail }; + + check_size(); + check_size(); + check_size(); + check_size(); + check_size(); + check_size(); + check_size(); + } +#endif +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/type_hash_test.cc b/parallel/parallel_src/extern/cista/test/type_hash_test.cc new file mode 100644 index 00000000..2ca9ddad --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/type_hash_test.cc @@ -0,0 +1,61 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/unique_ptr.h" +#include "cista/type_hash/type_hash.h" +#endif + +namespace hash_test { +struct s1 { + int i; + struct { + int j; + } k; +}; + +struct s2 { + struct { + int i; + } j; + int k; +}; +} // namespace hash_test + +namespace data = cista::offset; + +namespace rec_hash_test { + +struct B; +struct C; + +struct A { + data::unique_ptr m1_; + B const* m2_; +}; + +struct B { + B const* m1_; + A const* m2_; + C const* m3_; +}; + +struct C { + A const* m1_; +}; + +}; // namespace rec_hash_test + +TEST_CASE("hash int struct != int") { + CHECK(cista::type_hash() != cista::type_hash()); +} + +TEST_CASE("hash test struct field order") { + CHECK(2060989771674233508ULL == cista::type_hash()); + CHECK(8501584041991634637ULL == cista::type_hash()); +} + +TEST_CASE("recursive type hash does work") { + CHECK(6208585983120472160ULL == cista::type_hash()); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/type_name_test.cc b/parallel/parallel_src/extern/cista/test/type_name_test.cc new file mode 100644 index 00000000..7c27e535 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/type_name_test.cc @@ -0,0 +1,32 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/hash.h" +#include "cista/type_hash/type_name.h" +#endif + +#ifdef _MSC_VER +TEST_CASE("canonicalize type name test") { + std::string msvc = + R"(struct cista::basic_vector >,struct cista::offset_ptr > >,unsigned int>)"; + + std::string clang = + R"(cista::basic_vector >, cista::offset_ptr > >, unsigned int>)"; + + std::string gcc = + R"(cista::basic_vector >, cista::offset_ptr > >, unsigned int>)"; + + cista::canonicalize_type_name(msvc); + cista::canonicalize_type_name(clang); + cista::canonicalize_type_name(gcc); + + CHECK(clang == gcc); + CHECK(clang == msvc); + + CHECK(13128622470683179033ULL == cista::hash(msvc)); + CHECK(13128622470683179033ULL == cista::hash(clang)); + CHECK(13128622470683179033ULL == cista::hash(gcc)); +} +#endif \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/union_derive_test.cc b/parallel/parallel_src/extern/cista/test/union_derive_test.cc new file mode 100644 index 00000000..8c669cac --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/union_derive_test.cc @@ -0,0 +1,127 @@ +#include "doctest.h" + +#include + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/serialization.h" +#endif + +namespace data = cista::offset; + +TEST_CASE("union_test") { + union union_type { + int32_t a_; + float b_; + }; + static_assert(!cista::to_tuple_works_v); + + cista::byte_buf buf; + + { + union_type obj; + obj.b_ = 33.4F; + buf = cista::serialize(obj); + } // EOL obj + + auto const deserialize = cista::deserialize(buf); + CHECK(deserialize->b_ == 33.4F); +} + +struct Base {}; +struct Derive : Base { + int field; +}; + +template +void serialize(Ctx&, Derive const*, cista::offset_t const) {} + +template +void deserialize(Ctx const&, Derive*) {} + +TEST_CASE("derive_test") { + cista::byte_buf buf; + + { + Derive obj; + obj.field = 33; + buf = cista::serialize(obj); + } // EOL obj + + auto const deserialize = cista::deserialize(buf); + CHECK(deserialize->field == 33); +} + +union union_type { + union_type() : type_{type_t::NONE} {} + ~union_type() { + switch (type_) { + case type_t::MAP: a_.~a(); break; + case type_t::VEC: b_.~b(); break; + case type_t::NONE: break; + } + } + + friend std::ostream& operator<<(std::ostream& out, union_type const& u) { + switch (u.type_) { + case union_type::type_t::NONE: out << "{empty}"; break; + case union_type::type_t::MAP: + for (auto const& [k, v] : u.a_.map_) { + out << k << ", " << v << "\n"; + } + break; + case union_type::type_t::VEC: + for (auto const& [k, v] : u.b_.vec_) { + out << k << ", " << v << "\n"; + } + break; + } + return out; + } + + enum class type_t : int8_t { NONE, MAP, VEC } type_; + struct a { + int8_t type_; + data::hash_map map_; + } a_; + struct b { + int8_t type_; + data::vector> vec_; + } b_; +}; + +template +void serialize(Ctx& c, union_type const* el, cista::offset_t const pos) { + switch (el->type_) { + case union_type::type_t::MAP: serialize(c, &el->a_, pos); break; + case union_type::type_t::VEC: serialize(c, &el->b_, pos); break; + case union_type::type_t::NONE: break; + } +} + +template +void deserialize(Ctx const& c, union_type* el) { + switch (el->type_) { + case union_type::type_t::MAP: deserialize(c, &el->a_); break; + case union_type::type_t::VEC: deserialize(c, &el->b_); break; + case union_type::type_t::NONE: break; + } +} + +TEST_CASE("complex union") { + cista::byte_buf buf; + { + union_type obj; + obj.type_ = union_type::type_t::MAP; + obj.a_.map_ = {{1, 2}, {3, 4}}; + buf = cista::serialize(obj); + } + + auto const u = cista::deserialize(buf); + + std::stringstream ss; + ss << *u; + auto const check = ss.str() == "1, 2\n3, 4\n" || ss.str() == "3, 4\n1, 2\n"; + CHECK(check); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/variant_test.cc b/parallel/parallel_src/extern/cista/test/variant_test.cc new file mode 100755 index 00000000..570503b9 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/variant_test.cc @@ -0,0 +1,255 @@ +#include "doctest.h" + +#include + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/variant.h" +#include "cista/mmap.h" +#include "cista/serialization.h" +#include "cista/targets/file.h" +#endif + +namespace data = cista::offset; + +namespace variant_test { +enum entry_idx_t { + A_DEFAULT_CTOR, + A_COPY_CTOR, + A_MOVE_CTOR, + A_MOVE_ASSIGN, + A_COPY_ASSIGN, + A_DTOR, + B_DEFAULT_CTOR, + B_COPY_CTOR, + B_MOVE_CTOR, + B_MOVE_ASSIGN, + B_COPY_ASSIGN, + B_DTOR, + NUM_ENTRIES +}; +static std::array entries; + +struct a { + a() { ++entries[A_DEFAULT_CTOR]; } + a(a const&) { ++entries[A_COPY_CTOR]; } + a(a&&) { ++entries[A_MOVE_CTOR]; } + a& operator=(a&&) { + ++entries[A_MOVE_ASSIGN]; + return *this; + } + a& operator=(a const&) { + ++entries[A_COPY_ASSIGN]; + return *this; + } + ~a() { ++entries[A_DTOR]; } +}; + +struct b { + b() { ++entries[B_DEFAULT_CTOR]; } + b(b const&) { ++entries[B_COPY_CTOR]; } + b(b&&) { ++entries[B_MOVE_CTOR]; } + b& operator=(b&&) { + ++entries[B_MOVE_ASSIGN]; + return *this; + } + b& operator=(b const&) { + ++entries[B_COPY_ASSIGN]; + return *this; + } + ~b() { ++entries[B_DTOR]; } +}; + +} // namespace variant_test + +using namespace variant_test; + +TEST_CASE("variant basic methods") { + data::variant v{a{}}, u{b{}}; + + CHECK(entries == std::array{1, 0, 1, 0, 0, 1, // + 1, 0, 1, 0, 0, 1}); + entries = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + v.apply( + [](auto&& e) { CHECK(std::is_same_v, a>); }); + CHECK(entries == std::array{0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0}); + v = b{}; + CHECK(entries == std::array{0, 0, 0, 0, 0, 1, // + 1, 0, 1, 0, 0, 1}); + entries = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + v.apply( + [](auto&& e) { CHECK(std::is_same_v, b>); }); + CHECK(entries == std::array{0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0}); + + v = a{}; + CHECK(entries == std::array{1, 0, 1, 0, 0, 1, // + 0, 0, 0, 0, 0, 1}); + entries = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + v.apply( + [](auto&& e) { CHECK(std::is_same_v, a>); }); + CHECK(entries == std::array{0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0}); + + v = u; + v.apply( + [](auto&& e) { CHECK(std::is_same_v, b>); }); + CHECK(entries == std::array{0, 0, 0, 0, 0, 1, // + 0, 1, 0, 0, 0, 0}); + entries = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + v = a{}; + u = b{}; + entries = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + v.swap(u); + CHECK(entries == std::array{0, 0, 1, 0, 0, 1, // + 0, 0, 2, 0, 0, 2}); + + v = a{}; + u = a{}; + entries = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + v.swap(u); + CHECK(entries == std::array{0, 0, 1, 2, 0, 1, // + 0, 0, 0, 0, 0, 0}); +} + +TEST_CASE("variant comparison") { + data::variant v{1}, u{std::string{"hello"}}; + static_assert(cista::variant_size_v == 2); + + CHECK(v.index() == 0); + CHECK(u.index() == 1); + CHECK(cista::holds_alternative(v)); + CHECK(cista::holds_alternative(u)); + + CHECK(u > v); + CHECK(v < u); + CHECK(u >= v); + CHECK(v <= u); + CHECK(v != u); + CHECK(!(v == u)); + + u = 0; + CHECK(u < v); + CHECK(v > u); + CHECK(v >= u); + CHECK(u <= v); + CHECK(v != u); + CHECK(!(v == u)); + + CHECK(10 == (v.emplace(0) = 10)); + CHECK(20 == (v.emplace<0>(0) = 20)); + + v = 0; + CHECK(!(u < v)); + CHECK(!(v > u)); + CHECK(v >= u); + CHECK(u <= v); + CHECK(v == u); + CHECK(!(v != u)); +} + +TEST_CASE("variant get") { + data::variant v{1}, u{std::string{"hello"}}; + + CHECK(cista::get_if<0>(u) == nullptr); + CHECK(cista::get_if<1>(v) == nullptr); + CHECK(*cista::get_if<0>(v) == 1); + CHECK(*cista::get_if<1>(u) == std::string{"hello"}); + + v.swap(u); + + CHECK(std::get(u) == 1); + CHECK(std::get(v) == "hello"); + CHECK(std::get<0>(u) == 1); + CHECK(std::get<1>(v) == "hello"); +} + +TEST_CASE("variant get const") { + data::variant const v{1}, u{std::string{"hello"}}; + + CHECK(cista::get_if<0>(u) == nullptr); + CHECK(cista::get_if<1>(v) == nullptr); + CHECK(*cista::get_if<0>(v) == 1); + CHECK(*cista::get_if<1>(u) == std::string{"hello"}); + + CHECK(std::get(v) == 1); + CHECK(std::get(u) == "hello"); + CHECK(std::get<0>(v) == 1); + CHECK(std::get<1>(u) == "hello"); +} + +TEST_CASE("variant serialization") { + namespace data = cista::offset; + constexpr auto const MODE = // opt. versioning + check sum + cista::mode::WITH_VERSION | cista::mode::WITH_INTEGRITY; + + struct pos { + int x, y; + }; + using property = data::variant; + using pos_map = // Automatic deduction of hash & equality + data::hash_map, data::hash_set>; + + { // Serialize. + auto positions = pos_map{ + {{{1, 2}, {3, 4}}, + {property{data::string{"hello"}}, property{std::int64_t{123}}}}, + {{{5, 6}, {7, 8}}, + {property{std::int64_t{456}}, property{data::string{"world"}}}}}; + cista::buf mmap{cista::mmap{"data"}}; + cista::serialize(mmap, positions); + } + + // Deserialize. + auto b = cista::file("data", "r").content(); + auto positions = cista::deserialize(b); + + // Check. + CHECK(positions->size() == 2); + auto const one = positions->find(pos_map::key_type{{1, 2}, {3, 4}}); + auto const two = positions->find(pos_map::key_type{{5, 6}, {7, 8}}); + CHECK(one != positions->end()); + CHECK(two != positions->end()); + CHECK(one->second.find(property{data::string{"hello"}}) != end(one->second)); + CHECK(two->second.find(property{std::int64_t{456}}) != end(two->second)); +} + +TEST_CASE("variant get_if") { + namespace CISTA = cista::offset; + struct Test; + + using Variant = + CISTA::variant, CISTA::unique_ptr>; + + struct Test : public CISTA::vector {}; + + auto t1 = Variant{true}; + auto const result = cista::get_if(t1); + REQUIRE(result != nullptr); + CHECK(*result == true); + + auto const t2 = Variant{CISTA::string{"hello test test test test"}}; + auto const str = cista::get_if(t2); + REQUIRE(str != nullptr); + CHECK(*str == "hello test test test test"); +} + +TEST_CASE("std visit") { + std::visit( + [](auto&& x) { + if constexpr (std::is_same_v>) { + CHECK(true); + } else { + CHECK(false); + } + }, + cista::variant{1}); +} diff --git a/parallel/parallel_src/extern/cista/test/vector_copy_test.cc b/parallel/parallel_src/extern/cista/test/vector_copy_test.cc new file mode 100644 index 00000000..58973058 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/vector_copy_test.cc @@ -0,0 +1,20 @@ +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/vector.h" +#endif + +TEST_CASE("to_tuple") { + namespace data = cista::offset; + data::vector a; + a.emplace_back(1u); + a.emplace_back(2u); + a.emplace_back(3u); + + data::vector b; + b = a; + + CHECK(a == b); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/vector_test.cc b/parallel/parallel_src/extern/cista/test/vector_test.cc new file mode 100644 index 00000000..ccf6bfc4 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/vector_test.cc @@ -0,0 +1,325 @@ +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/vector.h" +#include "cista/equal_to.h" +#include "cista/is_iterable.h" +#include "cista/reflection/printable.h" +#include "cista/serialization.h" +#include "cista/type_hash/type_name.h" +#include "cista/verify.h" +#endif + +TEST_CASE("insert begin test") { + using cista::raw::vector; + + auto v = vector{1, 2, 3}; + v.insert(begin(v), 0); + + CHECK(v == vector{0, 1, 2, 3}); +} + +TEST_CASE("insert end test") { + using cista::raw::vector; + + auto v = vector{1, 2, 3}; + v.insert(end(v), 4); + + CHECK(v == vector{1, 2, 3, 4}); +} + +TEST_CASE("insert middle test") { + using cista::raw::vector; + + auto v = vector{1, 2, 3}; + v.insert(begin(v) + 1, 4); + + CHECK(v == vector{1, 4, 2, 3}); +} + +TEST_CASE("range insert begin test") { + using cista::raw::vector; + + auto v = vector{1, 2, 3}; + auto const w = vector{8, 9}; + v.insert(begin(v), begin(w), end(w)); + + CHECK(v == vector{8, 9, 1, 2, 3}); +} + +TEST_CASE("range insert end test") { + using cista::raw::vector; + + auto v = vector{1, 2, 3}; + auto const w = vector{8, 9}; + v.insert(end(v), begin(w), end(w)); + + CHECK(v == vector{1, 2, 3, 8, 9}); +} + +TEST_CASE("range insert middle test") { + using cista::raw::vector; + + auto v = vector{1, 2, 3}; + auto const w = vector{8, 9}; + v.insert(begin(v) + 1, begin(w), end(w)); + + CHECK(v == vector{1, 8, 9, 2, 3}); +} + +TEST_CASE("range insert nothing") { + using cista::raw::vector; + + auto v = vector{1, 2, 3}; + auto const w = vector{}; + v.insert(begin(v) + 1, begin(w), end(w)); + + CHECK(v == vector{1, 2, 3}); +} + +struct move_only_int { + explicit move_only_int(int i) : i_{i} {} + move_only_int(move_only_int const&) = delete; + move_only_int(move_only_int&& o) = default; + move_only_int& operator=(move_only_int const&) = delete; + move_only_int& operator=(move_only_int&& o) = default; + ~move_only_int() = default; + + friend bool operator==(move_only_int const& lhs, move_only_int const& rhs) { + return lhs.i_ == rhs.i_; + } + + friend std::ostream& operator<<(std::ostream& out, move_only_int const& e) { + out << e.i_; + return out; + } + + int i_; +}; + +cista::raw::vector make_move_only_int_vector( + std::initializer_list init) { + cista::raw::vector v; + for (auto i : init) { + v.emplace_back(i); + } + return v; +} + +TEST_CASE("range insert middle move only test") { + auto v = make_move_only_int_vector({1, 2, 3}); + auto w = make_move_only_int_vector({8, 9}); + + v.insert(begin(v) + 1, std::make_move_iterator(begin(w)), + std::make_move_iterator(end(w))); + + CHECK(v == make_move_only_int_vector({1, 8, 9, 2, 3})); +} + +struct input_iterator { + using iterator_category = std::input_iterator_tag; + using value_type = move_only_int; + using difference_type = int; + using pointer = move_only_int*; + using reference = move_only_int&; + + explicit input_iterator(int i) : i_{i}, consumed_{std::make_shared(i)} {} + + input_iterator& operator++() { + cista::verify(i_ == *consumed_, "input_iterator: iterate a stale copy"); + *consumed_ = ++i_; + return *this; + } + + move_only_int operator*() const { + cista::verify(i_ == *consumed_, "input_iterator: dereference a stale copy"); + return move_only_int{i_}; + } + + friend bool operator==(input_iterator const& lhs, input_iterator const& rhs) { + return lhs.i_ == rhs.i_; + } + + int i_; + std::shared_ptr consumed_; +}; + +TEST_CASE("input iterator works") { + input_iterator a{42}; + auto cpy = a; + ++a; + REQUIRE_THROWS(++cpy); + REQUIRE_THROWS(*cpy); +} + +TEST_CASE("range insert input_iterator begin test") { + auto v = make_move_only_int_vector({1, 2, 3}); + v.insert(begin(v), input_iterator{8}, input_iterator{10}); + + CHECK(v == make_move_only_int_vector({8, 9, 1, 2, 3})); +} + +TEST_CASE("range insert input_iterator end test") { + auto v = make_move_only_int_vector({1, 2, 3}); + v.insert(end(v), input_iterator{8}, input_iterator{10}); + + CHECK(v == make_move_only_int_vector({1, 2, 3, 8, 9})); +} + +TEST_CASE("range insert input_iterator middle test") { + auto v = make_move_only_int_vector({1, 2, 3}); + v.insert(begin(v) + 1, input_iterator{8}, input_iterator{10}); + + CHECK(v == make_move_only_int_vector({1, 8, 9, 2, 3})); +} + +TEST_CASE("range insert input_iterator nothing") { + auto v = make_move_only_int_vector({1, 2, 3}); + v.insert(begin(v) + 1, input_iterator{0}, input_iterator{0}); + + CHECK(v == make_move_only_int_vector({1, 2, 3})); +} + +TEST_CASE("erase duplicates") { + using cista::raw::vector; + + auto v = vector{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9}; + std::sort(begin(v), end(v)); + v.erase(std::unique(begin(v), end(v)), end(v)); + + CHECK(v == vector{1, 2, 3, 4, 5, 6, 9}); +} + +TEST_CASE("iterable comparison") { + std::vector std_v{1, 2, 3}; + cista::raw::vector cista_v{1, 2, 3}; + std::set std_s{1, 2, 3}; + CHECK(cista::equal_to{}(std_v, cista_v)); + CHECK(cista::equal_to{}(std_s, cista_v)); +} + +TEST_CASE("to_vec") { + namespace data = cista::raw; + + cista::buf> buf; + for (auto i = 0U; i != 3; ++i) { // Serialize. + buf.reset(); + + auto const v = std::vector({1.0, 2.0}); + auto o = data::to_vec(v); + CHECK(o == data::to_vec(v, [](auto&& e) { return e; })); + CHECK(o == data::to_vec(begin(v), end(v), [](auto&& e) { return e; })); + cista::serialize(buf, o); + } + + // Deserialize. + std::string s{reinterpret_cast(&buf.buf_[0]), buf.buf_.size()}; + auto const& deserialized = + *cista::deserialize>(&s[0], &s[0] + s.size()); + REQUIRE(deserialized.size() == 2); + CHECK(deserialized[0] == 1.0); + CHECK(deserialized[1] == 2.0); +} + +TEST_CASE("to_indexed_vec") { + namespace data = cista::raw; + + std::vector buf; + { // Serialize. + auto const v = std::vector({1.0, 2.0}); + auto o = data::to_indexed_vec(v); + CHECK(o == data::to_indexed_vec(v, [](auto&& e) { return e; })); + CHECK(o == + data::to_indexed_vec(begin(v), end(v), [](auto&& e) { return e; })); + buf = cista::serialize(o); + } + + // Deserialize. + auto const& deserialized = + *cista::deserialize>(buf); + REQUIRE(deserialized.size() == 2); + CHECK(deserialized[0] == 1.0); + CHECK(deserialized[1] == 2.0); +} + +TEST_CASE("complex_type_insert") { + namespace data = cista::raw; + + auto v = data::vector>{ + {"abc", "def", "longlonglonglonglonglonglonglonglonglonglongstring"}, + {"uvw", "xyz", "longlonglonglonglonglonglonglonglonglonglongstring"}}; + auto u = v; + for (int i = 0; i < 10; ++i) { + auto const v_copy = v; + v.insert(begin(v), begin(u), end(u)); + u.insert(begin(u), begin(v_copy), end(v_copy)); + } + REQUIRE(u.size() == v.size()); + REQUIRE(u.size() == 2048); +} + +TEST_CASE("erase then serialize") { + using cista::raw::vector; + + std::vector buf; + { + auto v = vector{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9}; + v.erase(std::remove_if(begin(v), end(v), [](auto&& i) { return i > 5; }), + end(v)); + + buf = cista::serialize(v); + } + + auto deserialized = cista::deserialize>(buf); + + CHECK(*deserialized == vector{3, 1, 4, 1, 5, 2, 5, 3, 5}); +} + +TEST_CASE("erase until empty then serialize") { + using cista::raw::vector; + + std::vector buf; + + { + auto v = vector{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 9}; + v.erase(std::remove_if(begin(v), end(v), [](auto&& i) { return i != 10; }), + end(v)); + + buf = cista::serialize(v); + } + + auto deserialized = cista::deserialize>(buf); + + CHECK(*deserialized == vector{}); +} + +TEST_CASE("erase loop") { + using cista::raw::vector; + + vector uut{1, 2, 3}; + std::vector ref{1, 2, 3}; + + auto const verify_equality = [&]() { + REQUIRE(ref.size() == uut.size()); + for (auto i = 0U; i != ref.size(); ++i) { + CHECK(ref[i] == uut[i]); + } + }; + + auto uut_it = uut.begin(); + auto ref_it = ref.begin(); + while (!ref.empty()) { + uut_it = uut.erase(uut_it); + ref_it = ref.erase(ref_it); + if (ref_it != end(ref)) { + REQUIRE(uut_it != end(uut)); + CHECK(*ref_it == *uut_it); + } + verify_equality(); + } +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/test/vecvec_test.cc b/parallel/parallel_src/extern/cista/test/vecvec_test.cc new file mode 100644 index 00000000..7c3ac0b3 --- /dev/null +++ b/parallel/parallel_src/extern/cista/test/vecvec_test.cc @@ -0,0 +1,251 @@ +#include +#include +#include + +#include "doctest.h" + +#ifdef SINGLE_HEADER +#include "cista.h" +#else +#include "cista/containers/mmap_vec.h" +#include "cista/containers/paged.h" +#include "cista/containers/paged_vecvec.h" +#include "cista/containers/vecvec.h" +#include "cista/strong.h" +#endif + +TEST_CASE("vecvec insert begin test") { + using key = cista::strong; + struct value { + double lat_, lng_; + }; + using data = cista::raw::vecvec; + + data d; + CHECK(d.size() == 0U); + CHECK(d.empty()); + + d.emplace_back( + std::initializer_list{value{1.0, 1.0}, value{2.0, 2.0}}); + CHECK(d.size() == 1); + CHECK(d[key{0}].size() == 2); +} + +TEST_CASE("vecvec string test") { + using key = cista::strong; + using data = cista::raw::vecvec; + + data d; + d.emplace_back("hello"); + CHECK(d[key{0U}].view() == "hello"); +} + +TEST_CASE("vecvec sort bucket test") { + using key = cista::strong; + using data = cista::raw::vecvec; + + data d; + d.emplace_back("hello"); + + std::sort(begin(d.at(key{0})), end(d.at(key{0}))); + + CHECK_EQ("ehllo", d.at(key{0}).view()); +} + +TEST_CASE("vecvec bucket emplace_back test") { + using key = cista::strong; + using data = cista::raw::vecvec; + + data d; + d.emplace_back("hello"); + d.emplace_back("world"); + d.emplace_back("test"); + + d[key{0}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("world", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{1}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("worldx", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{2}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("worldx", d[key{1}].view()); + CHECK_EQ("testx", d[key{2}].view()); +} + +TEST_CASE("vecvec resize test") { + using key = cista::strong; + using data = cista::raw::vecvec; + + data d; + d.emplace_back("hello"); + d.emplace_back("world"); + d.emplace_back("test"); + + d.resize(5); + + auto const expected = std::array{ + std::string_view{"hello"}, std::string_view{"world"}, + std::string_view{"test"}, std::string_view{""}, std::string_view{""}}; + for (auto i = 0U; i != d.size(); ++i) { + CHECK((d[key{i}].view() == expected[i])); + } + + d.resize(1); + auto const expected_1 = + std::array{std::string_view{"hello"}}; + for (auto i = 0U; i != d.size(); ++i) { + CHECK((d[key{i}].view() == expected_1[i])); + } +} + +TEST_CASE("vecvec mmap") { + using key = cista::strong; + using idx_t = cista::mmap_vec>; + using data_t = cista::mmap_vec; + + auto idx = idx_t{cista::mmap{std::tmpnam(nullptr)}}; + auto data = data_t{cista::mmap{std::tmpnam(nullptr)}}; + auto d = + cista::basic_vecvec{std::move(data), std::move(idx)}; + + d.emplace_back("hello"); + d.emplace_back("world"); + d.emplace_back("test"); + + CHECK_EQ("hello", d[key{0}].view()); + CHECK_EQ("world", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{0}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("world", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{1}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("worldx", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{2}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("worldx", d[key{1}].view()); + CHECK_EQ("testx", d[key{2}].view()); +} + +TEST_CASE("paged_vecvec mmap") { + struct x { + char x_{'x'}; + }; + + auto t = cista::mmap_vec{cista::mmap(std::tmpnam(nullptr))}; + t.push_back(x{'y'}); + CHECK_EQ(1, t.size()); + t.resize(5); + CHECK_EQ(5, t.size()); + + auto first = true; + for (auto const& x : t) { + if (first) { + CHECK_EQ(x.x_, 'y'); + first = false; + } else { + CHECK_EQ(x.x_, 'x'); + } + } + + using key = cista::strong; + using data_t = cista::paged>; + using idx_t = cista::mmap_vec; + + auto idx = idx_t{cista::mmap{std::tmpnam(nullptr)}}; + auto data = data_t{cista::mmap_vec{cista::mmap{std::tmpnam(nullptr)}}}; + auto d = + cista::paged_vecvec{std::move(data), std::move(idx)}; + + d.emplace_back("hello"); + d.emplace_back("world"); + d.emplace_back("test"); + + CHECK_EQ("hello", d[key{0}].view()); + CHECK_EQ("world", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{0}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("world", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{1}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("worldx", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{2}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("worldx", d[key{1}].view()); + CHECK_EQ("testx", d[key{2}].view()); +} + +TEST_CASE("paged_vecvec vector") { + using key = cista::strong; + using data_t = cista::paged>; + using idx_t = cista::mmap_vec; + + auto idx = idx_t{cista::mmap{std::tmpnam(nullptr)}}; + auto data = data_t{}; + auto d = + cista::paged_vecvec{std::move(data), std::move(idx)}; + + d.resize(3U); + CHECK_EQ(3, d.size()); + + d.emplace_back("hello"); + d.emplace_back("world"); + d.emplace_back("test"); + + CHECK_EQ(6, d.size()); + + CHECK_EQ("hello", d[key{3}].view()); + CHECK_EQ("world", d[key{4}].view()); + CHECK_EQ("test", d[key{5}].view()); + + d[key{3}].push_back('x'); + CHECK_EQ("hellox", d[key{3}].view()); + CHECK_EQ("world", d[key{4}].view()); + CHECK_EQ("test", d[key{5}].view()); + + d[key{4}].push_back('x'); + CHECK_EQ("hellox", d[key{3}].view()); + CHECK_EQ("worldx", d[key{4}].view()); + CHECK_EQ("test", d[key{5}].view()); + + d[key{5}].push_back('x'); + CHECK_EQ("hellox", d[key{3}].view()); + CHECK_EQ("worldx", d[key{4}].view()); + CHECK_EQ("testx", d[key{5}].view()); + + d.resize(0); + d.emplace_back("hello"); + d.emplace_back("world"); + d.emplace_back("test"); + + d[key{0}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("world", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{1}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("worldx", d[key{1}].view()); + CHECK_EQ("test", d[key{2}].view()); + + d[key{2}].push_back('x'); + CHECK_EQ("hellox", d[key{0}].view()); + CHECK_EQ("worldx", d[key{1}].view()); + CHECK_EQ("testx", d[key{2}].view()); +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/doctest/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/doctest/CMakeLists.txt new file mode 100644 index 00000000..0bc78c74 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/doctest/CMakeLists.txt @@ -0,0 +1,5 @@ +project(doctest) + +add_library(cista-doctest STATIC doctest.cc) +target_include_directories(cista-doctest SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_features(cista-doctest PUBLIC cxx_std_11) \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/doctest/doctest.cc b/parallel/parallel_src/extern/cista/tools/doctest/doctest.cc new file mode 100755 index 00000000..987bf627 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/doctest/doctest.cc @@ -0,0 +1,3 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#define DOCTEST_CONFIG_NO_EXCEPTIONS +#include "doctest.h" \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/doctest/doctest.h b/parallel/parallel_src/extern/cista/tools/doctest/doctest.h new file mode 100644 index 00000000..d81f6232 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/doctest/doctest.h @@ -0,0 +1,7077 @@ +// ====================================================================== lgtm [cpp/missing-header-guard] +// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! == +// ====================================================================== +// +// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD +// +// Copyright (c) 2016-2023 Viktor Kirilov +// +// Distributed under the MIT Software License +// See accompanying file LICENSE.txt or copy at +// https://opensource.org/licenses/MIT +// +// The documentation can be found at the library's page: +// https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md +// +// ================================================================================================= +// ================================================================================================= +// ================================================================================================= +// +// The library is heavily influenced by Catch - https://github.com/catchorg/Catch2 +// which uses the Boost Software License - Version 1.0 +// see here - https://github.com/catchorg/Catch2/blob/master/LICENSE.txt +// +// The concept of subcases (sections in Catch) and expression decomposition are from there. +// Some parts of the code are taken directly: +// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<> +// - the Approx() helper class for floating point comparison +// - colors in the console +// - breaking into a debugger +// - signal / SEH handling +// - timer +// - XmlWriter class - thanks to Phil Nash for allowing the direct reuse (AKA copy/paste) +// +// The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest +// which uses the Boost Software License - Version 1.0 +// see here - https://github.com/martinmoene/lest/blob/master/LICENSE.txt +// +// ================================================================================================= +// ================================================================================================= +// ================================================================================================= + +#ifndef DOCTEST_LIBRARY_INCLUDED +#define DOCTEST_LIBRARY_INCLUDED + +// ================================================================================================= +// == VERSION ====================================================================================== +// ================================================================================================= + +#define DOCTEST_VERSION_MAJOR 2 +#define DOCTEST_VERSION_MINOR 4 +#define DOCTEST_VERSION_PATCH 11 + +// util we need here +#define DOCTEST_TOSTR_IMPL(x) #x +#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x) + +#define DOCTEST_VERSION_STR \ + DOCTEST_TOSTR(DOCTEST_VERSION_MAJOR) "." \ + DOCTEST_TOSTR(DOCTEST_VERSION_MINOR) "." \ + DOCTEST_TOSTR(DOCTEST_VERSION_PATCH) + +#define DOCTEST_VERSION \ + (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) + +// ================================================================================================= +// == COMPILER VERSION ============================================================================= +// ================================================================================================= + +// ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect + +#ifdef _MSC_VER +#define DOCTEST_CPLUSPLUS _MSVC_LANG +#else +#define DOCTEST_CPLUSPLUS __cplusplus +#endif + +#define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH)) + +// GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl... +#if defined(_MSC_VER) && defined(_MSC_FULL_VER) +#if _MSC_VER == _MSC_FULL_VER / 10000 +#define DOCTEST_MSVC DOCTEST_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000) +#else // MSVC +#define DOCTEST_MSVC \ + DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000) +#endif // MSVC +#endif // MSVC +#if defined(__clang__) && defined(__clang_minor__) && defined(__clang_patchlevel__) +#define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__) +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \ + !defined(__INTEL_COMPILER) +#define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#endif // GCC +#if defined(__INTEL_COMPILER) +#define DOCTEST_ICC DOCTEST_COMPILER(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif // ICC + +#ifndef DOCTEST_MSVC +#define DOCTEST_MSVC 0 +#endif // DOCTEST_MSVC +#ifndef DOCTEST_CLANG +#define DOCTEST_CLANG 0 +#endif // DOCTEST_CLANG +#ifndef DOCTEST_GCC +#define DOCTEST_GCC 0 +#endif // DOCTEST_GCC +#ifndef DOCTEST_ICC +#define DOCTEST_ICC 0 +#endif // DOCTEST_ICC + +// ================================================================================================= +// == COMPILER WARNINGS HELPERS ==================================================================== +// ================================================================================================= + +#if DOCTEST_CLANG && !DOCTEST_ICC +#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) +#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push") +#define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w) +#define DOCTEST_CLANG_SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop") +#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \ + DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING(w) +#else // DOCTEST_CLANG +#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH +#define DOCTEST_CLANG_SUPPRESS_WARNING(w) +#define DOCTEST_CLANG_SUPPRESS_WARNING_POP +#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) +#endif // DOCTEST_CLANG + +#if DOCTEST_GCC +#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) +#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push") +#define DOCTEST_GCC_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(GCC diagnostic ignored w) +#define DOCTEST_GCC_SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop") +#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \ + DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING(w) +#else // DOCTEST_GCC +#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH +#define DOCTEST_GCC_SUPPRESS_WARNING(w) +#define DOCTEST_GCC_SUPPRESS_WARNING_POP +#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) +#endif // DOCTEST_GCC + +#if DOCTEST_MSVC +#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push)) +#define DOCTEST_MSVC_SUPPRESS_WARNING(w) __pragma(warning(disable : w)) +#define DOCTEST_MSVC_SUPPRESS_WARNING_POP __pragma(warning(pop)) +#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \ + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(w) +#else // DOCTEST_MSVC +#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH +#define DOCTEST_MSVC_SUPPRESS_WARNING(w) +#define DOCTEST_MSVC_SUPPRESS_WARNING_POP +#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) +#endif // DOCTEST_MSVC + +// ================================================================================================= +// == COMPILER WARNINGS ============================================================================ +// ================================================================================================= + +// both the header and the implementation suppress all of these, +// so it only makes sense to aggregate them like so +#define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \ + DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \ + DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \ + DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \ + DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \ + DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \ + DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \ + DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \ + \ + DOCTEST_GCC_SUPPRESS_WARNING_PUSH \ + DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") \ + DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") \ + DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") \ + DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \ + DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \ + DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \ + DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \ + DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \ + \ + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ + /* these 4 also disabled globally via cmake: */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \ + /* common ones */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5264) /* 'variable-name': 'const' variable is not used */ \ + /* static analysis */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */ + +#define DOCTEST_SUPPRESS_COMMON_WARNINGS_POP \ + DOCTEST_CLANG_SUPPRESS_WARNING_POP \ + DOCTEST_GCC_SUPPRESS_WARNING_POP \ + DOCTEST_MSVC_SUPPRESS_WARNING_POP + +DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH + +DOCTEST_CLANG_SUPPRESS_WARNING_PUSH +DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated") + +DOCTEST_GCC_SUPPRESS_WARNING_PUSH +DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy") +DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor") +DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo") + +DOCTEST_MSVC_SUPPRESS_WARNING_PUSH +DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted + +#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \ + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ + DOCTEST_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */ \ + DOCTEST_MSVC_SUPPRESS_WARNING(5262) /* implicit fall-through */ + +#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP + +// ================================================================================================= +// == FEATURE DETECTION ============================================================================ +// ================================================================================================= + +// general compiler feature support table: https://en.cppreference.com/w/cpp/compiler_support +// MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx +// GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html +// MSVC version table: +// https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering +// MSVC++ 14.3 (17) _MSC_VER == 1930 (Visual Studio 2022) +// MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019) +// MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017) +// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) +// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) +// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) +// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) +// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) +// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) + +// Universal Windows Platform support +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#define DOCTEST_CONFIG_NO_WINDOWS_SEH +#endif // WINAPI_FAMILY +#if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH) +#define DOCTEST_CONFIG_WINDOWS_SEH +#endif // MSVC +#if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH) +#undef DOCTEST_CONFIG_WINDOWS_SEH +#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH + +#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \ + !defined(__EMSCRIPTEN__) && !defined(__wasi__) +#define DOCTEST_CONFIG_POSIX_SIGNALS +#endif // _WIN32 +#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS) +#undef DOCTEST_CONFIG_POSIX_SIGNALS +#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS +#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) \ + || defined(__wasi__) +#define DOCTEST_CONFIG_NO_EXCEPTIONS +#endif // no exceptions +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + +#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS +#define DOCTEST_CONFIG_NO_EXCEPTIONS +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS + +#if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS) +#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS + +#ifdef __wasi__ +#define DOCTEST_CONFIG_NO_MULTITHREADING +#endif + +#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT) +#define DOCTEST_CONFIG_IMPLEMENT +#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN + +#if defined(_WIN32) || defined(__CYGWIN__) +#if DOCTEST_MSVC +#define DOCTEST_SYMBOL_EXPORT __declspec(dllexport) +#define DOCTEST_SYMBOL_IMPORT __declspec(dllimport) +#else // MSVC +#define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport)) +#define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport)) +#endif // MSVC +#else // _WIN32 +#define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default"))) +#define DOCTEST_SYMBOL_IMPORT +#endif // _WIN32 + +#ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL +#ifdef DOCTEST_CONFIG_IMPLEMENT +#define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT +#else // DOCTEST_CONFIG_IMPLEMENT +#define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT +#endif // DOCTEST_CONFIG_IMPLEMENT +#else // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL +#define DOCTEST_INTERFACE +#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL + +// needed for extern template instantiations +// see https://github.com/fmtlib/fmt/issues/2228 +#if DOCTEST_MSVC +#define DOCTEST_INTERFACE_DECL +#define DOCTEST_INTERFACE_DEF DOCTEST_INTERFACE +#else // DOCTEST_MSVC +#define DOCTEST_INTERFACE_DECL DOCTEST_INTERFACE +#define DOCTEST_INTERFACE_DEF +#endif // DOCTEST_MSVC + +#define DOCTEST_EMPTY + +#if DOCTEST_MSVC +#define DOCTEST_NOINLINE __declspec(noinline) +#define DOCTEST_UNUSED +#define DOCTEST_ALIGNMENT(x) +#elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0) +#define DOCTEST_NOINLINE +#define DOCTEST_UNUSED +#define DOCTEST_ALIGNMENT(x) +#else +#define DOCTEST_NOINLINE __attribute__((noinline)) +#define DOCTEST_UNUSED __attribute__((unused)) +#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x))) +#endif + +#ifdef DOCTEST_CONFIG_NO_CONTRADICTING_INLINE +#define DOCTEST_INLINE_NOINLINE inline +#else +#define DOCTEST_INLINE_NOINLINE inline DOCTEST_NOINLINE +#endif + +#ifndef DOCTEST_NORETURN +#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) +#define DOCTEST_NORETURN +#else // DOCTEST_MSVC +#define DOCTEST_NORETURN [[noreturn]] +#endif // DOCTEST_MSVC +#endif // DOCTEST_NORETURN + +#ifndef DOCTEST_NOEXCEPT +#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) +#define DOCTEST_NOEXCEPT +#else // DOCTEST_MSVC +#define DOCTEST_NOEXCEPT noexcept +#endif // DOCTEST_MSVC +#endif // DOCTEST_NOEXCEPT + +#ifndef DOCTEST_CONSTEXPR +#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) +#define DOCTEST_CONSTEXPR const +#define DOCTEST_CONSTEXPR_FUNC inline +#else // DOCTEST_MSVC +#define DOCTEST_CONSTEXPR constexpr +#define DOCTEST_CONSTEXPR_FUNC constexpr +#endif // DOCTEST_MSVC +#endif // DOCTEST_CONSTEXPR + +#ifndef DOCTEST_NO_SANITIZE_INTEGER +#if DOCTEST_CLANG >= DOCTEST_COMPILER(3, 7, 0) +#define DOCTEST_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer"))) +#else +#define DOCTEST_NO_SANITIZE_INTEGER +#endif +#endif // DOCTEST_NO_SANITIZE_INTEGER + +// ================================================================================================= +// == FEATURE DETECTION END ======================================================================== +// ================================================================================================= + +#define DOCTEST_DECLARE_INTERFACE(name) \ + virtual ~name(); \ + name() = default; \ + name(const name&) = delete; \ + name(name&&) = delete; \ + name& operator=(const name&) = delete; \ + name& operator=(name&&) = delete; + +#define DOCTEST_DEFINE_INTERFACE(name) \ + name::~name() = default; + +// internal macros for string concatenation and anonymous variable name generation +#define DOCTEST_CAT_IMPL(s1, s2) s1##s2 +#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2) +#ifdef __COUNTER__ // not standard and may be missing for some compilers +#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__) +#else // __COUNTER__ +#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) +#endif // __COUNTER__ + +#ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE +#define DOCTEST_REF_WRAP(x) x& +#else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE +#define DOCTEST_REF_WRAP(x) x +#endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE + +// not using __APPLE__ because... this is how Catch does it +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED +#define DOCTEST_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define DOCTEST_PLATFORM_IPHONE +#elif defined(_WIN32) +#define DOCTEST_PLATFORM_WINDOWS +#elif defined(__wasi__) +#define DOCTEST_PLATFORM_WASI +#else // DOCTEST_PLATFORM +#define DOCTEST_PLATFORM_LINUX +#endif // DOCTEST_PLATFORM + +namespace doctest { namespace detail { + static DOCTEST_CONSTEXPR int consume(const int*, int) noexcept { return 0; } +}} + +#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \ + static const int var = doctest::detail::consume(&var, __VA_ARGS__); \ + DOCTEST_CLANG_SUPPRESS_WARNING_POP + +#ifndef DOCTEST_BREAK_INTO_DEBUGGER +// should probably take a look at https://github.com/scottt/debugbreak +#ifdef DOCTEST_PLATFORM_LINUX +#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) +// Break at the location of the failing check if possible +#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler) +#else +#include +#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP) +#endif +#elif defined(DOCTEST_PLATFORM_MAC) +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) +#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler) +#elif defined(__ppc__) || defined(__ppc64__) +// https://www.cocoawithlove.com/2008/03/break-into-debugger.html +#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n": : : "memory","r0","r3","r4") // NOLINT(hicpp-no-assembler) +#else +#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT(hicpp-no-assembler) +#endif +#elif DOCTEST_MSVC +#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() +#elif defined(__MINGW32__) +DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wredundant-decls") +extern "C" __declspec(dllimport) void __stdcall DebugBreak(); +DOCTEST_GCC_SUPPRESS_WARNING_POP +#define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() +#else // linux +#define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast(0)) +#endif // linux +#endif // DOCTEST_BREAK_INTO_DEBUGGER + +// this is kept here for backwards compatibility since the config option was changed +#ifdef DOCTEST_CONFIG_USE_IOSFWD +#ifndef DOCTEST_CONFIG_USE_STD_HEADERS +#define DOCTEST_CONFIG_USE_STD_HEADERS +#endif +#endif // DOCTEST_CONFIG_USE_IOSFWD + +// for clang - always include ciso646 (which drags some std stuff) because +// we want to check if we are using libc++ with the _LIBCPP_VERSION macro in +// which case we don't want to forward declare stuff from std - for reference: +// https://github.com/doctest/doctest/issues/126 +// https://github.com/doctest/doctest/issues/356 +#if DOCTEST_CLANG +#include +#endif // clang + +#ifdef _LIBCPP_VERSION +#ifndef DOCTEST_CONFIG_USE_STD_HEADERS +#define DOCTEST_CONFIG_USE_STD_HEADERS +#endif +#endif // _LIBCPP_VERSION + +#ifdef DOCTEST_CONFIG_USE_STD_HEADERS +#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN +#include +#include +#include +DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END +#else // DOCTEST_CONFIG_USE_STD_HEADERS + +#include +#include +#include + +#endif // DOCTEST_CONFIG_USE_STD_HEADERS + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +#include +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + +namespace doctest { + +using std::size_t; + +DOCTEST_INTERFACE extern bool is_running_in_test; + +#ifndef DOCTEST_CONFIG_STRING_SIZE_TYPE +#define DOCTEST_CONFIG_STRING_SIZE_TYPE unsigned +#endif + +// A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length +// of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for: +// - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128) +// - if small - capacity left before going on the heap - using the lowest 5 bits +// - if small - 2 bits are left unused - the second and third highest ones +// - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator) +// and the "is small" bit remains "0" ("as well as the capacity left") so its OK +// Idea taken from this lecture about the string implementation of facebook/folly - fbstring +// https://www.youtube.com/watch?v=kPR8h4-qZdk +// TODO: +// - optimizations - like not deleting memory unnecessarily in operator= and etc. +// - resize/reserve/clear +// - replace +// - back/front +// - iterator stuff +// - find & friends +// - push_back/pop_back +// - assign/insert/erase +// - relational operators as free functions - taking const char* as one of the params +class DOCTEST_INTERFACE String +{ +public: + using size_type = DOCTEST_CONFIG_STRING_SIZE_TYPE; + +private: + static DOCTEST_CONSTEXPR size_type len = 24; //!OCLINT avoid private static members + static DOCTEST_CONSTEXPR size_type last = len - 1; //!OCLINT avoid private static members + + struct view // len should be more than sizeof(view) - because of the final byte for flags + { + char* ptr; + size_type size; + size_type capacity; + }; + + union + { + char buf[len]; // NOLINT(*-avoid-c-arrays) + view data; + }; + + char* allocate(size_type sz); + + bool isOnStack() const noexcept { return (buf[last] & 128) == 0; } + void setOnHeap() noexcept; + void setLast(size_type in = last) noexcept; + void setSize(size_type sz) noexcept; + + void copy(const String& other); + +public: + static DOCTEST_CONSTEXPR size_type npos = static_cast(-1); + + String() noexcept; + ~String(); + + // cppcheck-suppress noExplicitConstructor + String(const char* in); + String(const char* in, size_type in_size); + + String(std::istream& in, size_type in_size); + + String(const String& other); + String& operator=(const String& other); + + String& operator+=(const String& other); + + String(String&& other) noexcept; + String& operator=(String&& other) noexcept; + + char operator[](size_type i) const; + char& operator[](size_type i); + + // the only functions I'm willing to leave in the interface - available for inlining + const char* c_str() const { return const_cast(this)->c_str(); } // NOLINT + char* c_str() { + if (isOnStack()) { + return reinterpret_cast(buf); + } + return data.ptr; + } + + size_type size() const; + size_type capacity() const; + + String substr(size_type pos, size_type cnt = npos) &&; + String substr(size_type pos, size_type cnt = npos) const &; + + size_type find(char ch, size_type pos = 0) const; + size_type rfind(char ch, size_type pos = npos) const; + + int compare(const char* other, bool no_case = false) const; + int compare(const String& other, bool no_case = false) const; + +friend DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); +}; + +DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs); + +DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs); + +class DOCTEST_INTERFACE Contains { +public: + explicit Contains(const String& string); + + bool checkWith(const String& other) const; + + String string; +}; + +DOCTEST_INTERFACE String toString(const Contains& in); + +DOCTEST_INTERFACE bool operator==(const String& lhs, const Contains& rhs); +DOCTEST_INTERFACE bool operator==(const Contains& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator!=(const String& lhs, const Contains& rhs); +DOCTEST_INTERFACE bool operator!=(const Contains& lhs, const String& rhs); + +namespace Color { + enum Enum + { + None = 0, + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White + }; + + DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, Color::Enum code); +} // namespace Color + +namespace assertType { + enum Enum + { + // macro traits + + is_warn = 1, + is_check = 2 * is_warn, + is_require = 2 * is_check, + + is_normal = 2 * is_require, + is_throws = 2 * is_normal, + is_throws_as = 2 * is_throws, + is_throws_with = 2 * is_throws_as, + is_nothrow = 2 * is_throws_with, + + is_false = 2 * is_nothrow, + is_unary = 2 * is_false, // not checked anywhere - used just to distinguish the types + + is_eq = 2 * is_unary, + is_ne = 2 * is_eq, + + is_lt = 2 * is_ne, + is_gt = 2 * is_lt, + + is_ge = 2 * is_gt, + is_le = 2 * is_ge, + + // macro types + + DT_WARN = is_normal | is_warn, + DT_CHECK = is_normal | is_check, + DT_REQUIRE = is_normal | is_require, + + DT_WARN_FALSE = is_normal | is_false | is_warn, + DT_CHECK_FALSE = is_normal | is_false | is_check, + DT_REQUIRE_FALSE = is_normal | is_false | is_require, + + DT_WARN_THROWS = is_throws | is_warn, + DT_CHECK_THROWS = is_throws | is_check, + DT_REQUIRE_THROWS = is_throws | is_require, + + DT_WARN_THROWS_AS = is_throws_as | is_warn, + DT_CHECK_THROWS_AS = is_throws_as | is_check, + DT_REQUIRE_THROWS_AS = is_throws_as | is_require, + + DT_WARN_THROWS_WITH = is_throws_with | is_warn, + DT_CHECK_THROWS_WITH = is_throws_with | is_check, + DT_REQUIRE_THROWS_WITH = is_throws_with | is_require, + + DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn, + DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check, + DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require, + + DT_WARN_NOTHROW = is_nothrow | is_warn, + DT_CHECK_NOTHROW = is_nothrow | is_check, + DT_REQUIRE_NOTHROW = is_nothrow | is_require, + + DT_WARN_EQ = is_normal | is_eq | is_warn, + DT_CHECK_EQ = is_normal | is_eq | is_check, + DT_REQUIRE_EQ = is_normal | is_eq | is_require, + + DT_WARN_NE = is_normal | is_ne | is_warn, + DT_CHECK_NE = is_normal | is_ne | is_check, + DT_REQUIRE_NE = is_normal | is_ne | is_require, + + DT_WARN_GT = is_normal | is_gt | is_warn, + DT_CHECK_GT = is_normal | is_gt | is_check, + DT_REQUIRE_GT = is_normal | is_gt | is_require, + + DT_WARN_LT = is_normal | is_lt | is_warn, + DT_CHECK_LT = is_normal | is_lt | is_check, + DT_REQUIRE_LT = is_normal | is_lt | is_require, + + DT_WARN_GE = is_normal | is_ge | is_warn, + DT_CHECK_GE = is_normal | is_ge | is_check, + DT_REQUIRE_GE = is_normal | is_ge | is_require, + + DT_WARN_LE = is_normal | is_le | is_warn, + DT_CHECK_LE = is_normal | is_le | is_check, + DT_REQUIRE_LE = is_normal | is_le | is_require, + + DT_WARN_UNARY = is_normal | is_unary | is_warn, + DT_CHECK_UNARY = is_normal | is_unary | is_check, + DT_REQUIRE_UNARY = is_normal | is_unary | is_require, + + DT_WARN_UNARY_FALSE = is_normal | is_false | is_unary | is_warn, + DT_CHECK_UNARY_FALSE = is_normal | is_false | is_unary | is_check, + DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require, + }; +} // namespace assertType + +DOCTEST_INTERFACE const char* assertString(assertType::Enum at); +DOCTEST_INTERFACE const char* failureString(assertType::Enum at); +DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file); + +struct DOCTEST_INTERFACE TestCaseData +{ + String m_file; // the file in which the test was registered (using String - see #350) + unsigned m_line; // the line where the test was registered + const char* m_name; // name of the test case + const char* m_test_suite; // the test suite in which the test was added + const char* m_description; + bool m_skip; + bool m_no_breaks; + bool m_no_output; + bool m_may_fail; + bool m_should_fail; + int m_expected_failures; + double m_timeout; +}; + +struct DOCTEST_INTERFACE AssertData +{ + // common - for all asserts + const TestCaseData* m_test_case; + assertType::Enum m_at; + const char* m_file; + int m_line; + const char* m_expr; + bool m_failed; + + // exception-related - for all asserts + bool m_threw; + String m_exception; + + // for normal asserts + String m_decomp; + + // for specific exception-related asserts + bool m_threw_as; + const char* m_exception_type; + + class DOCTEST_INTERFACE StringContains { + private: + Contains content; + bool isContains; + + public: + StringContains(const String& str) : content(str), isContains(false) { } + StringContains(Contains cntn) : content(static_cast(cntn)), isContains(true) { } + + bool check(const String& str) { return isContains ? (content == str) : (content.string == str); } + + operator const String&() const { return content.string; } + + const char* c_str() const { return content.string.c_str(); } + } m_exception_string; + + AssertData(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type, const StringContains& exception_string); +}; + +struct DOCTEST_INTERFACE MessageData +{ + String m_string; + const char* m_file; + int m_line; + assertType::Enum m_severity; +}; + +struct DOCTEST_INTERFACE SubcaseSignature +{ + String m_name; + const char* m_file; + int m_line; + + bool operator==(const SubcaseSignature& other) const; + bool operator<(const SubcaseSignature& other) const; +}; + +struct DOCTEST_INTERFACE IContextScope +{ + DOCTEST_DECLARE_INTERFACE(IContextScope) + virtual void stringify(std::ostream*) const = 0; +}; + +namespace detail { + struct DOCTEST_INTERFACE TestCase; +} // namespace detail + +struct ContextOptions //!OCLINT too many fields +{ + std::ostream* cout = nullptr; // stdout stream + String binary_name; // the test binary name + + const detail::TestCase* currentTest = nullptr; + + // == parameters from the command line + String out; // output filename + String order_by; // how tests should be ordered + unsigned rand_seed; // the seed for rand ordering + + unsigned first; // the first (matching) test to be executed + unsigned last; // the last (matching) test to be executed + + int abort_after; // stop tests after this many failed assertions + int subcase_filter_levels; // apply the subcase filters for the first N levels + + bool success; // include successful assertions in output + bool case_sensitive; // if filtering should be case sensitive + bool exit; // if the program should be exited after the tests are ran/whatever + bool duration; // print the time duration of each test case + bool minimal; // minimal console output (only test failures) + bool quiet; // no console output + bool no_throw; // to skip exceptions-related assertion macros + bool no_exitcode; // if the framework should return 0 as the exitcode + bool no_run; // to not run the tests at all (can be done with an "*" exclude) + bool no_intro; // to not print the intro of the framework + bool no_version; // to not print the version of the framework + bool no_colors; // if output to the console should be colorized + bool force_colors; // forces the use of colors even when a tty cannot be detected + bool no_breaks; // to not break into the debugger + bool no_skip; // don't skip test cases which are marked to be skipped + bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): + bool no_path_in_filenames; // if the path to files should be removed from the output + bool no_line_numbers; // if source code line numbers should be omitted from the output + bool no_debug_output; // no output in the debug console when a debugger is attached + bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! + bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!! + + bool help; // to print the help + bool version; // to print the version + bool count; // if only the count of matching tests is to be retrieved + bool list_test_cases; // to list all tests matching the filters + bool list_test_suites; // to list all suites matching the filters + bool list_reporters; // lists all registered reporters +}; + +namespace detail { + namespace types { +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + using namespace std; +#else + template + struct enable_if { }; + + template + struct enable_if { using type = T; }; + + struct true_type { static DOCTEST_CONSTEXPR bool value = true; }; + struct false_type { static DOCTEST_CONSTEXPR bool value = false; }; + + template struct remove_reference { using type = T; }; + template struct remove_reference { using type = T; }; + template struct remove_reference { using type = T; }; + + template struct is_rvalue_reference : false_type { }; + template struct is_rvalue_reference : true_type { }; + + template struct remove_const { using type = T; }; + template struct remove_const { using type = T; }; + + // Compiler intrinsics + template struct is_enum { static DOCTEST_CONSTEXPR bool value = __is_enum(T); }; + template struct underlying_type { using type = __underlying_type(T); }; + + template struct is_pointer : false_type { }; + template struct is_pointer : true_type { }; + + template struct is_array : false_type { }; + // NOLINTNEXTLINE(*-avoid-c-arrays) + template struct is_array : true_type { }; +#endif + } + + // + template + T&& declval(); + + template + DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference::type& t) DOCTEST_NOEXCEPT { + return static_cast(t); + } + + template + DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference::type&& t) DOCTEST_NOEXCEPT { + return static_cast(t); + } + + template + struct deferred_false : types::false_type { }; + +// MSVS 2015 :( +#if !DOCTEST_CLANG && defined(_MSC_VER) && _MSC_VER <= 1900 + template + struct has_global_insertion_operator : types::false_type { }; + + template + struct has_global_insertion_operator(), declval()), void())> : types::true_type { }; + + template + struct has_insertion_operator { static DOCTEST_CONSTEXPR bool value = has_global_insertion_operator::value; }; + + template + struct insert_hack; + + template + struct insert_hack { + static void insert(std::ostream& os, const T& t) { ::operator<<(os, t); } + }; + + template + struct insert_hack { + static void insert(std::ostream& os, const T& t) { operator<<(os, t); } + }; + + template + using insert_hack_t = insert_hack::value>; +#else + template + struct has_insertion_operator : types::false_type { }; +#endif + + template + struct has_insertion_operator(), declval()), void())> : types::true_type { }; + + template + struct should_stringify_as_underlying_type { + static DOCTEST_CONSTEXPR bool value = detail::types::is_enum::value && !doctest::detail::has_insertion_operator::value; + }; + + DOCTEST_INTERFACE std::ostream* tlssPush(); + DOCTEST_INTERFACE String tlssPop(); + + template + struct StringMakerBase { + template + static String convert(const DOCTEST_REF_WRAP(T)) { +#ifdef DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES + static_assert(deferred_false::value, "No stringification detected for type T. See string conversion manual"); +#endif + return "{?}"; + } + }; + + template + struct filldata; + + template + void filloss(std::ostream* stream, const T& in) { + filldata::fill(stream, in); + } + + template + void filloss(std::ostream* stream, const T (&in)[N]) { // NOLINT(*-avoid-c-arrays) + // T[N], T(&)[N], T(&&)[N] have same behaviour. + // Hence remove reference. + filloss::type>(stream, in); + } + + template + String toStream(const T& in) { + std::ostream* stream = tlssPush(); + filloss(stream, in); + return tlssPop(); + } + + template <> + struct StringMakerBase { + template + static String convert(const DOCTEST_REF_WRAP(T) in) { + return toStream(in); + } + }; +} // namespace detail + +template +struct StringMaker : public detail::StringMakerBase< + detail::has_insertion_operator::value || detail::types::is_pointer::value || detail::types::is_array::value> +{}; + +#ifndef DOCTEST_STRINGIFY +#ifdef DOCTEST_CONFIG_DOUBLE_STRINGIFY +#define DOCTEST_STRINGIFY(...) toString(toString(__VA_ARGS__)) +#else +#define DOCTEST_STRINGIFY(...) toString(__VA_ARGS__) +#endif +#endif + +template +String toString() { +#if DOCTEST_CLANG == 0 && DOCTEST_GCC == 0 && DOCTEST_ICC == 0 + String ret = __FUNCSIG__; // class doctest::String __cdecl doctest::toString(void) + String::size_type beginPos = ret.find('<'); + return ret.substr(beginPos + 1, ret.size() - beginPos - static_cast(sizeof(">(void)"))); +#else + String ret = __PRETTY_FUNCTION__; // doctest::String toString() [with T = TYPE] + String::size_type begin = ret.find('=') + 2; + return ret.substr(begin, ret.size() - begin - 1); +#endif +} + +template ::value, bool>::type = true> +String toString(const DOCTEST_REF_WRAP(T) value) { + return StringMaker::convert(value); +} + +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +DOCTEST_INTERFACE String toString(const char* in); +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + +#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 +DOCTEST_INTERFACE String toString(const std::string& in); +#endif // VS 2019 + +DOCTEST_INTERFACE String toString(String in); + +DOCTEST_INTERFACE String toString(std::nullptr_t); + +DOCTEST_INTERFACE String toString(bool in); + +DOCTEST_INTERFACE String toString(float in); +DOCTEST_INTERFACE String toString(double in); +DOCTEST_INTERFACE String toString(double long in); + +DOCTEST_INTERFACE String toString(char in); +DOCTEST_INTERFACE String toString(char signed in); +DOCTEST_INTERFACE String toString(char unsigned in); +DOCTEST_INTERFACE String toString(short in); +DOCTEST_INTERFACE String toString(short unsigned in); +DOCTEST_INTERFACE String toString(signed in); +DOCTEST_INTERFACE String toString(unsigned in); +DOCTEST_INTERFACE String toString(long in); +DOCTEST_INTERFACE String toString(long unsigned in); +DOCTEST_INTERFACE String toString(long long in); +DOCTEST_INTERFACE String toString(long long unsigned in); + +template ::value, bool>::type = true> +String toString(const DOCTEST_REF_WRAP(T) value) { + using UT = typename detail::types::underlying_type::type; + return (DOCTEST_STRINGIFY(static_cast(value))); +} + +namespace detail { + template + struct filldata + { + static void fill(std::ostream* stream, const T& in) { +#if defined(_MSC_VER) && _MSC_VER <= 1900 + insert_hack_t::insert(*stream, in); +#else + operator<<(*stream, in); +#endif + } + }; + +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866) +// NOLINTBEGIN(*-avoid-c-arrays) + template + struct filldata { + static void fill(std::ostream* stream, const T(&in)[N]) { + *stream << "["; + for (size_t i = 0; i < N; i++) { + if (i != 0) { *stream << ", "; } + *stream << (DOCTEST_STRINGIFY(in[i])); + } + *stream << "]"; + } + }; +// NOLINTEND(*-avoid-c-arrays) +DOCTEST_MSVC_SUPPRESS_WARNING_POP + + // Specialized since we don't want the terminating null byte! +// NOLINTBEGIN(*-avoid-c-arrays) + template + struct filldata { + static void fill(std::ostream* stream, const char (&in)[N]) { + *stream << String(in, in[N - 1] ? N : N - 1); + } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) + }; +// NOLINTEND(*-avoid-c-arrays) + + template <> + struct filldata { + static void fill(std::ostream* stream, const void* in); + }; + + template + struct filldata { +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4180) + static void fill(std::ostream* stream, const T* in) { +DOCTEST_MSVC_SUPPRESS_WARNING_POP +DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wmicrosoft-cast") + filldata::fill(stream, +#if DOCTEST_GCC == 0 || DOCTEST_GCC >= DOCTEST_COMPILER(4, 9, 0) + reinterpret_cast(in) +#else + *reinterpret_cast(&in) +#endif + ); +DOCTEST_CLANG_SUPPRESS_WARNING_POP + } + }; +} + +struct DOCTEST_INTERFACE Approx +{ + Approx(double value); + + Approx operator()(double value) const; + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + template + explicit Approx(const T& value, + typename detail::types::enable_if::value>::type* = + static_cast(nullptr)) { + *this = static_cast(value); + } +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + Approx& epsilon(double newEpsilon); + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + template + typename std::enable_if::value, Approx&>::type epsilon( + const T& newEpsilon) { + m_epsilon = static_cast(newEpsilon); + return *this; + } +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + Approx& scale(double newScale); + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + template + typename std::enable_if::value, Approx&>::type scale( + const T& newScale) { + m_scale = static_cast(newScale); + return *this; + } +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + // clang-format off + DOCTEST_INTERFACE friend bool operator==(double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator==(const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator!=(double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator!=(const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator<=(double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator<=(const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator>=(double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator>=(const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator< (double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator< (const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs); + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +#define DOCTEST_APPROX_PREFIX \ + template friend typename std::enable_if::value, bool>::type + + DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(static_cast(lhs), rhs); } + DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); } + DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); } + DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); } + DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return static_cast(lhs) < rhs.m_value || lhs == rhs; } + DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast(rhs) || lhs == rhs; } + DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return static_cast(lhs) > rhs.m_value || lhs == rhs; } + DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast(rhs) || lhs == rhs; } + DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return static_cast(lhs) < rhs.m_value && lhs != rhs; } + DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast(rhs) && lhs != rhs; } + DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return static_cast(lhs) > rhs.m_value && lhs != rhs; } + DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast(rhs) && lhs != rhs; } +#undef DOCTEST_APPROX_PREFIX +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + // clang-format on + + double m_epsilon; + double m_scale; + double m_value; +}; + +DOCTEST_INTERFACE String toString(const Approx& in); + +DOCTEST_INTERFACE const ContextOptions* getContextOptions(); + +template +struct DOCTEST_INTERFACE_DECL IsNaN +{ + F value; bool flipped; + IsNaN(F f, bool flip = false) : value(f), flipped(flip) { } + IsNaN operator!() const { return { value, !flipped }; } + operator bool() const; +}; +#ifndef __MINGW32__ +extern template struct DOCTEST_INTERFACE_DECL IsNaN; +extern template struct DOCTEST_INTERFACE_DECL IsNaN; +extern template struct DOCTEST_INTERFACE_DECL IsNaN; +#endif +DOCTEST_INTERFACE String toString(IsNaN in); +DOCTEST_INTERFACE String toString(IsNaN in); +DOCTEST_INTERFACE String toString(IsNaN in); + +#ifndef DOCTEST_CONFIG_DISABLE + +namespace detail { + // clang-format off +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + template struct decay_array { using type = T; }; + template struct decay_array { using type = T*; }; + template struct decay_array { using type = T*; }; + + template struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 1; }; + template<> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 0; }; + template<> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 0; }; + + template struct can_use_op : public not_char_pointer::type> {}; +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + // clang-format on + + struct DOCTEST_INTERFACE TestFailureException + { + }; + + DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + DOCTEST_NORETURN +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + DOCTEST_INTERFACE void throwException(); + + struct DOCTEST_INTERFACE Subcase + { + SubcaseSignature m_signature; + bool m_entered = false; + + Subcase(const String& name, const char* file, int line); + Subcase(const Subcase&) = delete; + Subcase(Subcase&&) = delete; + Subcase& operator=(const Subcase&) = delete; + Subcase& operator=(Subcase&&) = delete; + ~Subcase(); + + operator bool() const; + + private: + bool checkFilters(); + }; + + template + String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, + const DOCTEST_REF_WRAP(R) rhs) { + return (DOCTEST_STRINGIFY(lhs)) + op + (DOCTEST_STRINGIFY(rhs)); + } + +#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) +DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison") +#endif + +// This will check if there is any way it could find a operator like member or friend and uses it. +// If not it doesn't find the operator or if the operator at global scope is defined after +// this template, the template won't be instantiated due to SFINAE. Once the template is not +// instantiated it can look for global operator using normal conversions. +#ifdef __NVCC__ +#define SFINAE_OP(ret,op) ret +#else +#define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval() op doctest::detail::declval()),ret{}) +#endif + +#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \ + template \ + DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \ + bool res = op_macro(doctest::detail::forward(lhs), doctest::detail::forward(rhs)); \ + if(m_at & assertType::is_false) \ + res = !res; \ + if(!res || doctest::getContextOptions()->success) \ + return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ + return Result(res); \ + } + + // more checks could be added - like in Catch: + // https://github.com/catchorg/Catch2/pull/1480/files + // https://github.com/catchorg/Catch2/pull/1481/files +#define DOCTEST_FORBIT_EXPRESSION(rt, op) \ + template \ + rt& operator op(const R&) { \ + static_assert(deferred_false::value, \ + "Expression Too Complex Please Rewrite As Binary Comparison!"); \ + return *this; \ + } + + struct DOCTEST_INTERFACE Result // NOLINT(*-member-init) + { + bool m_passed; + String m_decomp; + + Result() = default; // TODO: Why do we need this? (To remove NOLINT) + Result(bool passed, const String& decomposition = String()); + + // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence + DOCTEST_FORBIT_EXPRESSION(Result, &) + DOCTEST_FORBIT_EXPRESSION(Result, ^) + DOCTEST_FORBIT_EXPRESSION(Result, |) + DOCTEST_FORBIT_EXPRESSION(Result, &&) + DOCTEST_FORBIT_EXPRESSION(Result, ||) + DOCTEST_FORBIT_EXPRESSION(Result, ==) + DOCTEST_FORBIT_EXPRESSION(Result, !=) + DOCTEST_FORBIT_EXPRESSION(Result, <) + DOCTEST_FORBIT_EXPRESSION(Result, >) + DOCTEST_FORBIT_EXPRESSION(Result, <=) + DOCTEST_FORBIT_EXPRESSION(Result, >=) + DOCTEST_FORBIT_EXPRESSION(Result, =) + DOCTEST_FORBIT_EXPRESSION(Result, +=) + DOCTEST_FORBIT_EXPRESSION(Result, -=) + DOCTEST_FORBIT_EXPRESSION(Result, *=) + DOCTEST_FORBIT_EXPRESSION(Result, /=) + DOCTEST_FORBIT_EXPRESSION(Result, %=) + DOCTEST_FORBIT_EXPRESSION(Result, <<=) + DOCTEST_FORBIT_EXPRESSION(Result, >>=) + DOCTEST_FORBIT_EXPRESSION(Result, &=) + DOCTEST_FORBIT_EXPRESSION(Result, ^=) + DOCTEST_FORBIT_EXPRESSION(Result, |=) + }; + +#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + + DOCTEST_CLANG_SUPPRESS_WARNING_PUSH + DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-compare") + //DOCTEST_CLANG_SUPPRESS_WARNING("-Wdouble-promotion") + //DOCTEST_CLANG_SUPPRESS_WARNING("-Wconversion") + //DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-equal") + + DOCTEST_GCC_SUPPRESS_WARNING_PUSH + DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") + DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-compare") + //DOCTEST_GCC_SUPPRESS_WARNING("-Wdouble-promotion") + //DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") + //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal") + + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH + // https://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389 + DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch + DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch + DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch + //DOCTEST_MSVC_SUPPRESS_WARNING(4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation + +#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + + // clang-format off +#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_COMPARISON_RETURN_TYPE bool +#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_COMPARISON_RETURN_TYPE typename types::enable_if::value || can_use_op::value, bool>::type + inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } + inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } + inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } + inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); } + inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); } + inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); } +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + // clang-format on + +#define DOCTEST_RELATIONAL_OP(name, op) \ + template \ + DOCTEST_COMPARISON_RETURN_TYPE name(const DOCTEST_REF_WRAP(L) lhs, \ + const DOCTEST_REF_WRAP(R) rhs) { \ + return lhs op rhs; \ + } + + DOCTEST_RELATIONAL_OP(eq, ==) + DOCTEST_RELATIONAL_OP(ne, !=) + DOCTEST_RELATIONAL_OP(lt, <) + DOCTEST_RELATIONAL_OP(gt, >) + DOCTEST_RELATIONAL_OP(le, <=) + DOCTEST_RELATIONAL_OP(ge, >=) + +#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_CMP_EQ(l, r) l == r +#define DOCTEST_CMP_NE(l, r) l != r +#define DOCTEST_CMP_GT(l, r) l > r +#define DOCTEST_CMP_LT(l, r) l < r +#define DOCTEST_CMP_GE(l, r) l >= r +#define DOCTEST_CMP_LE(l, r) l <= r +#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_CMP_EQ(l, r) eq(l, r) +#define DOCTEST_CMP_NE(l, r) ne(l, r) +#define DOCTEST_CMP_GT(l, r) gt(l, r) +#define DOCTEST_CMP_LT(l, r) lt(l, r) +#define DOCTEST_CMP_GE(l, r) ge(l, r) +#define DOCTEST_CMP_LE(l, r) le(l, r) +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + + template + // cppcheck-suppress copyCtorAndEqOperator + struct Expression_lhs + { + L lhs; + assertType::Enum m_at; + + explicit Expression_lhs(L&& in, assertType::Enum at) + : lhs(static_cast(in)) + , m_at(at) {} + + DOCTEST_NOINLINE operator Result() { +// this is needed only for MSVC 2015 +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool + bool res = static_cast(lhs); +DOCTEST_MSVC_SUPPRESS_WARNING_POP + if(m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional + res = !res; + } + + if(!res || getContextOptions()->success) { + return { res, (DOCTEST_STRINGIFY(lhs)) }; + } + return { res }; + } + + /* This is required for user-defined conversions from Expression_lhs to L */ + operator L() const { return lhs; } + + // clang-format off + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>, " > ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<, " < ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>=, " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional + // clang-format on + + // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &&) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ||) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, =) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, +=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, -=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, *=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, /=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, %=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |=) + // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the + // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression... + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>) + }; + +#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_MSVC_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + +#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + +#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0) +DOCTEST_CLANG_SUPPRESS_WARNING_POP +#endif + + struct DOCTEST_INTERFACE ExpressionDecomposer + { + assertType::Enum m_at; + + ExpressionDecomposer(assertType::Enum at); + + // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table) + // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now... + // https://github.com/catchorg/Catch2/issues/870 + // https://github.com/catchorg/Catch2/issues/565 + template + Expression_lhs operator<<(L&& operand) { + return Expression_lhs(static_cast(operand), m_at); + } + + template ::value,void >::type* = nullptr> + Expression_lhs operator<<(const L &operand) { + return Expression_lhs(operand, m_at); + } + }; + + struct DOCTEST_INTERFACE TestSuite + { + const char* m_test_suite = nullptr; + const char* m_description = nullptr; + bool m_skip = false; + bool m_no_breaks = false; + bool m_no_output = false; + bool m_may_fail = false; + bool m_should_fail = false; + int m_expected_failures = 0; + double m_timeout = 0; + + TestSuite& operator*(const char* in); + + template + TestSuite& operator*(const T& in) { + in.fill(*this); + return *this; + } + }; + + using funcType = void (*)(); + + struct DOCTEST_INTERFACE TestCase : public TestCaseData + { + funcType m_test; // a function pointer to the test case + + String m_type; // for templated test cases - gets appended to the real name + int m_template_id; // an ID used to distinguish between the different versions of a templated test case + String m_full_name; // contains the name (only for templated test cases!) + the template type + + TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, + const String& type = String(), int template_id = -1); + + TestCase(const TestCase& other); + TestCase(TestCase&&) = delete; + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function + TestCase& operator=(const TestCase& other); + DOCTEST_MSVC_SUPPRESS_WARNING_POP + + TestCase& operator=(TestCase&&) = delete; + + TestCase& operator*(const char* in); + + template + TestCase& operator*(const T& in) { + in.fill(*this); + return *this; + } + + bool operator<(const TestCase& other) const; + + ~TestCase() = default; + }; + + // forward declarations of functions used by the macros + DOCTEST_INTERFACE int regTest(const TestCase& tc); + DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); + DOCTEST_INTERFACE bool isDebuggerActive(); + + template + int instantiationHelper(const T&) { return 0; } + + namespace binaryAssertComparison { + enum Enum + { + eq = 0, + ne, + gt, + lt, + ge, + le + }; + } // namespace binaryAssertComparison + + // clang-format off + template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R) ) const { return false; } }; + +#define DOCTEST_BINARY_RELATIONAL_OP(n, op) \ + template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } }; + // clang-format on + + DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq) + DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne) + DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt) + DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt) + DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge) + DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le) + + struct DOCTEST_INTERFACE ResultBuilder : public AssertData + { + ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type = "", const String& exception_string = ""); + + ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type, const Contains& exception_string); + + void setResult(const Result& res); + + template + DOCTEST_NOINLINE bool binary_assert(const DOCTEST_REF_WRAP(L) lhs, + const DOCTEST_REF_WRAP(R) rhs) { + m_failed = !RelationalComparator()(lhs, rhs); + if (m_failed || getContextOptions()->success) { + m_decomp = stringifyBinaryExpr(lhs, ", ", rhs); + } + return !m_failed; + } + + template + DOCTEST_NOINLINE bool unary_assert(const DOCTEST_REF_WRAP(L) val) { + m_failed = !val; + + if (m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional + m_failed = !m_failed; + } + + if (m_failed || getContextOptions()->success) { + m_decomp = (DOCTEST_STRINGIFY(val)); + } + + return !m_failed; + } + + void translateException(); + + bool log(); + void react() const; + }; + + namespace assertAction { + enum Enum + { + nothing = 0, + dbgbreak = 1, + shouldthrow = 2 + }; + } // namespace assertAction + + DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad); + + DOCTEST_INTERFACE bool decomp_assert(assertType::Enum at, const char* file, int line, + const char* expr, const Result& result); + +#define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \ + do { \ + if(!is_running_in_test) { \ + if(failed) { \ + ResultBuilder rb(at, file, line, expr); \ + rb.m_failed = failed; \ + rb.m_decomp = decomp; \ + failed_out_of_a_testing_context(rb); \ + if(isDebuggerActive() && !getContextOptions()->no_breaks) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ + if(checkIfShouldThrow(at)) \ + throwException(); \ + } \ + return !failed; \ + } \ + } while(false) + +#define DOCTEST_ASSERT_IN_TESTS(decomp) \ + ResultBuilder rb(at, file, line, expr); \ + rb.m_failed = failed; \ + if(rb.m_failed || getContextOptions()->success) \ + rb.m_decomp = decomp; \ + if(rb.log()) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ + if(rb.m_failed && checkIfShouldThrow(at)) \ + throwException() + + template + DOCTEST_NOINLINE bool binary_assert(assertType::Enum at, const char* file, int line, + const char* expr, const DOCTEST_REF_WRAP(L) lhs, + const DOCTEST_REF_WRAP(R) rhs) { + bool failed = !RelationalComparator()(lhs, rhs); + + // ################################################################################### + // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT + // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED + // ################################################################################### + DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); + DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); + return !failed; + } + + template + DOCTEST_NOINLINE bool unary_assert(assertType::Enum at, const char* file, int line, + const char* expr, const DOCTEST_REF_WRAP(L) val) { + bool failed = !val; + + if(at & assertType::is_false) //!OCLINT bitwise operator in conditional + failed = !failed; + + // ################################################################################### + // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT + // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED + // ################################################################################### + DOCTEST_ASSERT_OUT_OF_TESTS((DOCTEST_STRINGIFY(val))); + DOCTEST_ASSERT_IN_TESTS((DOCTEST_STRINGIFY(val))); + return !failed; + } + + struct DOCTEST_INTERFACE IExceptionTranslator + { + DOCTEST_DECLARE_INTERFACE(IExceptionTranslator) + virtual bool translate(String&) const = 0; + }; + + template + class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class + { + public: + explicit ExceptionTranslator(String (*translateFunction)(T)) + : m_translateFunction(translateFunction) {} + + bool translate(String& res) const override { +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + try { + throw; // lgtm [cpp/rethrow-no-exception] + // cppcheck-suppress catchExceptionByValue + } catch(const T& ex) { + res = m_translateFunction(ex); //!OCLINT parameter reassignment + return true; + } catch(...) {} //!OCLINT - empty catch statement +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + static_cast(res); // to silence -Wunused-parameter + return false; + } + + private: + String (*m_translateFunction)(T); + }; + + DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); + + // ContextScope base class used to allow implementing methods of ContextScope + // that don't depend on the template parameter in doctest.cpp. + struct DOCTEST_INTERFACE ContextScopeBase : public IContextScope { + ContextScopeBase(const ContextScopeBase&) = delete; + + ContextScopeBase& operator=(const ContextScopeBase&) = delete; + ContextScopeBase& operator=(ContextScopeBase&&) = delete; + + ~ContextScopeBase() override = default; + + protected: + ContextScopeBase(); + ContextScopeBase(ContextScopeBase&& other) noexcept; + + void destroy(); + bool need_to_destroy{true}; + }; + + template class ContextScope : public ContextScopeBase + { + L lambda_; + + public: + explicit ContextScope(const L &lambda) : lambda_(lambda) {} + explicit ContextScope(L&& lambda) : lambda_(static_cast(lambda)) { } + + ContextScope(const ContextScope&) = delete; + ContextScope(ContextScope&&) noexcept = default; + + ContextScope& operator=(const ContextScope&) = delete; + ContextScope& operator=(ContextScope&&) = delete; + + void stringify(std::ostream* s) const override { lambda_(s); } + + ~ContextScope() override { + if (need_to_destroy) { + destroy(); + } + } + }; + + struct DOCTEST_INTERFACE MessageBuilder : public MessageData + { + std::ostream* m_stream; + bool logged = false; + + MessageBuilder(const char* file, int line, assertType::Enum severity); + + MessageBuilder(const MessageBuilder&) = delete; + MessageBuilder(MessageBuilder&&) = delete; + + MessageBuilder& operator=(const MessageBuilder&) = delete; + MessageBuilder& operator=(MessageBuilder&&) = delete; + + ~MessageBuilder(); + + // the preferred way of chaining parameters for stringification +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866) + template + MessageBuilder& operator,(const T& in) { + *m_stream << (DOCTEST_STRINGIFY(in)); + return *this; + } +DOCTEST_MSVC_SUPPRESS_WARNING_POP + + // kept here just for backwards-compatibility - the comma operator should be preferred now + template + MessageBuilder& operator<<(const T& in) { return this->operator,(in); } + + // the `,` operator has the lowest operator precedence - if `<<` is used by the user then + // the `,` operator will be called last which is not what we want and thus the `*` operator + // is used first (has higher operator precedence compared to `<<`) so that we guarantee that + // an operator of the MessageBuilder class is called first before the rest of the parameters + template + MessageBuilder& operator*(const T& in) { return this->operator,(in); } + + bool log(); + void react(); + }; + + template + ContextScope MakeContextScope(const L &lambda) { + return ContextScope(lambda); + } +} // namespace detail + +#define DOCTEST_DEFINE_DECORATOR(name, type, def) \ + struct name \ + { \ + type data; \ + name(type in = def) \ + : data(in) {} \ + void fill(detail::TestCase& state) const { state.DOCTEST_CAT(m_, name) = data; } \ + void fill(detail::TestSuite& state) const { state.DOCTEST_CAT(m_, name) = data; } \ + } + +DOCTEST_DEFINE_DECORATOR(test_suite, const char*, ""); +DOCTEST_DEFINE_DECORATOR(description, const char*, ""); +DOCTEST_DEFINE_DECORATOR(skip, bool, true); +DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true); +DOCTEST_DEFINE_DECORATOR(no_output, bool, true); +DOCTEST_DEFINE_DECORATOR(timeout, double, 0); +DOCTEST_DEFINE_DECORATOR(may_fail, bool, true); +DOCTEST_DEFINE_DECORATOR(should_fail, bool, true); +DOCTEST_DEFINE_DECORATOR(expected_failures, int, 0); + +template +int registerExceptionTranslator(String (*translateFunction)(T)) { + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") + static detail::ExceptionTranslator exceptionTranslator(translateFunction); + DOCTEST_CLANG_SUPPRESS_WARNING_POP + detail::registerExceptionTranslatorImpl(&exceptionTranslator); + return 0; +} + +} // namespace doctest + +// in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro +// introduces an anonymous namespace in which getCurrentTestSuite gets overridden +namespace doctest_detail_test_suite_ns { +DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite(); +} // namespace doctest_detail_test_suite_ns + +namespace doctest { +#else // DOCTEST_CONFIG_DISABLE +template +int registerExceptionTranslator(String (*)(T)) { + return 0; +} +#endif // DOCTEST_CONFIG_DISABLE + +namespace detail { + using assert_handler = void (*)(const AssertData&); + struct ContextState; +} // namespace detail + +class DOCTEST_INTERFACE Context +{ + detail::ContextState* p; + + void parseArgs(int argc, const char* const* argv, bool withDefaults = false); + +public: + explicit Context(int argc = 0, const char* const* argv = nullptr); + + Context(const Context&) = delete; + Context(Context&&) = delete; + + Context& operator=(const Context&) = delete; + Context& operator=(Context&&) = delete; + + ~Context(); // NOLINT(performance-trivially-destructible) + + void applyCommandLine(int argc, const char* const* argv); + + void addFilter(const char* filter, const char* value); + void clearFilters(); + void setOption(const char* option, bool value); + void setOption(const char* option, int value); + void setOption(const char* option, const char* value); + + bool shouldExit(); + + void setAsDefaultForAssertsOutOfTestCases(); + + void setAssertHandler(detail::assert_handler ah); + + void setCout(std::ostream* out); + + int run(); +}; + +namespace TestCaseFailureReason { + enum Enum + { + None = 0, + AssertFailure = 1, // an assertion has failed in the test case + Exception = 2, // test case threw an exception + Crash = 4, // a crash... + TooManyFailedAsserts = 8, // the abort-after option + Timeout = 16, // see the timeout decorator + ShouldHaveFailedButDidnt = 32, // see the should_fail decorator + ShouldHaveFailedAndDid = 64, // see the should_fail decorator + DidntFailExactlyNumTimes = 128, // see the expected_failures decorator + FailedExactlyNumTimes = 256, // see the expected_failures decorator + CouldHaveFailedAndDid = 512 // see the may_fail decorator + }; +} // namespace TestCaseFailureReason + +struct DOCTEST_INTERFACE CurrentTestCaseStats +{ + int numAssertsCurrentTest; + int numAssertsFailedCurrentTest; + double seconds; + int failure_flags; // use TestCaseFailureReason::Enum + bool testCaseSuccess; +}; + +struct DOCTEST_INTERFACE TestCaseException +{ + String error_string; + bool is_crash; +}; + +struct DOCTEST_INTERFACE TestRunStats +{ + unsigned numTestCases; + unsigned numTestCasesPassingFilters; + unsigned numTestSuitesPassingFilters; + unsigned numTestCasesFailed; + int numAsserts; + int numAssertsFailed; +}; + +struct QueryData +{ + const TestRunStats* run_stats = nullptr; + const TestCaseData** data = nullptr; + unsigned num_data = 0; +}; + +struct DOCTEST_INTERFACE IReporter +{ + // The constructor has to accept "const ContextOptions&" as a single argument + // which has most of the options for the run + a pointer to the stdout stream + // Reporter(const ContextOptions& in) + + // called when a query should be reported (listing test cases, printing the version, etc.) + virtual void report_query(const QueryData&) = 0; + + // called when the whole test run starts + virtual void test_run_start() = 0; + // called when the whole test run ends (caching a pointer to the input doesn't make sense here) + virtual void test_run_end(const TestRunStats&) = 0; + + // called when a test case is started (safe to cache a pointer to the input) + virtual void test_case_start(const TestCaseData&) = 0; + // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input) + virtual void test_case_reenter(const TestCaseData&) = 0; + // called when a test case has ended + virtual void test_case_end(const CurrentTestCaseStats&) = 0; + + // called when an exception is thrown from the test case (or it crashes) + virtual void test_case_exception(const TestCaseException&) = 0; + + // called whenever a subcase is entered (don't cache pointers to the input) + virtual void subcase_start(const SubcaseSignature&) = 0; + // called whenever a subcase is exited (don't cache pointers to the input) + virtual void subcase_end() = 0; + + // called for each assert (don't cache pointers to the input) + virtual void log_assert(const AssertData&) = 0; + // called for each message (don't cache pointers to the input) + virtual void log_message(const MessageData&) = 0; + + // called when a test case is skipped either because it doesn't pass the filters, has a skip decorator + // or isn't in the execution range (between first and last) (safe to cache a pointer to the input) + virtual void test_case_skipped(const TestCaseData&) = 0; + + DOCTEST_DECLARE_INTERFACE(IReporter) + + // can obtain all currently active contexts and stringify them if one wishes to do so + static int get_num_active_contexts(); + static const IContextScope* const* get_active_contexts(); + + // can iterate through contexts which have been stringified automatically in their destructors when an exception has been thrown + static int get_num_stringified_contexts(); + static const String* get_stringified_contexts(); +}; + +namespace detail { + using reporterCreatorFunc = IReporter* (*)(const ContextOptions&); + + DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter); + + template + IReporter* reporterCreator(const ContextOptions& o) { + return new Reporter(o); + } +} // namespace detail + +template +int registerReporter(const char* name, int priority, bool isReporter) { + detail::registerReporterImpl(name, priority, detail::reporterCreator, isReporter); + return 0; +} +} // namespace doctest + +#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES +#define DOCTEST_FUNC_EMPTY [] { return false; }() +#else +#define DOCTEST_FUNC_EMPTY (void)0 +#endif + +// if registering is not disabled +#ifndef DOCTEST_CONFIG_DISABLE + +#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES +#define DOCTEST_FUNC_SCOPE_BEGIN [&] +#define DOCTEST_FUNC_SCOPE_END () +#define DOCTEST_FUNC_SCOPE_RET(v) return v +#else +#define DOCTEST_FUNC_SCOPE_BEGIN do +#define DOCTEST_FUNC_SCOPE_END while(false) +#define DOCTEST_FUNC_SCOPE_RET(v) (void)0 +#endif + +// common code in asserts - for convenience +#define DOCTEST_ASSERT_LOG_REACT_RETURN(b) \ + if(b.log()) DOCTEST_BREAK_INTO_DEBUGGER(); \ + b.react(); \ + DOCTEST_FUNC_SCOPE_RET(!b.m_failed) + +#ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS +#define DOCTEST_WRAP_IN_TRY(x) x; +#else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS +#define DOCTEST_WRAP_IN_TRY(x) \ + try { \ + x; \ + } catch(...) { DOCTEST_RB.translateException(); } +#endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS + +#ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS +#define DOCTEST_CAST_TO_VOID(...) \ + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \ + static_cast(__VA_ARGS__); \ + DOCTEST_GCC_SUPPRESS_WARNING_POP +#else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS +#define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__; +#endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS + +// registers the test by initializing a dummy var with a function +#define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \ + global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT */ \ + doctest::detail::regTest( \ + doctest::detail::TestCase( \ + f, __FILE__, __LINE__, \ + doctest_detail_test_suite_ns::getCurrentTestSuite()) * \ + decorators)) + +#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \ + namespace { /* NOLINT */ \ + struct der : public base \ + { \ + void f(); \ + }; \ + static DOCTEST_INLINE_NOINLINE void func() { \ + der v; \ + v.f(); \ + } \ + DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \ + } \ + DOCTEST_INLINE_NOINLINE void der::f() // NOLINT(misc-definitions-in-headers) + +#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \ + static void f(); \ + DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, f, decorators) \ + static void f() + +#define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \ + static doctest::detail::funcType proxy() { return f; } \ + DOCTEST_REGISTER_FUNCTION(inline, proxy(), decorators) \ + static void f() + +// for registering tests +#define DOCTEST_TEST_CASE(decorators) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) + +// for registering tests in classes - requires C++17 for inline variables! +#if DOCTEST_CPLUSPLUS >= 201703L +#define DOCTEST_TEST_CASE_CLASS(decorators) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \ + DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \ + decorators) +#else // DOCTEST_TEST_CASE_CLASS +#define DOCTEST_TEST_CASE_CLASS(...) \ + TEST_CASES_CAN_BE_REGISTERED_IN_CLASSES_ONLY_IN_CPP17_MODE_OR_WITH_VS_2017_OR_NEWER +#endif // DOCTEST_TEST_CASE_CLASS + +// for registering tests with a fixture +#define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \ + DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), c, \ + DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) + +// for converting types to strings without the header and demangling +#define DOCTEST_TYPE_TO_STRING_AS(str, ...) \ + namespace doctest { \ + template <> \ + inline String toString<__VA_ARGS__>() { \ + return str; \ + } \ + } \ + static_assert(true, "") + +#define DOCTEST_TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING_AS(#__VA_ARGS__, __VA_ARGS__) + +#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \ + template \ + static void func(); \ + namespace { /* NOLINT */ \ + template \ + struct iter; \ + template \ + struct iter> \ + { \ + iter(const char* file, unsigned line, int index) { \ + doctest::detail::regTest(doctest::detail::TestCase(func, file, line, \ + doctest_detail_test_suite_ns::getCurrentTestSuite(), \ + doctest::toString(), \ + int(line) * 1000 + index) \ + * dec); \ + iter>(file, line, index + 1); \ + } \ + }; \ + template <> \ + struct iter> \ + { \ + iter(const char*, unsigned, int) {} \ + }; \ + } \ + template \ + static void func() + +#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \ + DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \ + DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)) + +#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY), /* NOLINT(cert-err58-cpp, fuchsia-statically-constructed-objects) */ \ + doctest::detail::instantiationHelper( \ + DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0))) + +#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ + DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \ + static_assert(true, "") + +#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ + DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) \ + static_assert(true, "") + +#define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \ + DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \ + DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(anon, anon, std::tuple<__VA_ARGS__>) \ + template \ + static void anon() + +#define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \ + DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) + +// for subcases +#define DOCTEST_SUBCASE(name) \ + if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \ + doctest::detail::Subcase(name, __FILE__, __LINE__)) + +// for grouping tests in test suites by using code blocks +#define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \ + namespace ns_name { namespace doctest_detail_test_suite_ns { \ + static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() noexcept { \ + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \ + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \ + static doctest::detail::TestSuite data{}; \ + static bool inited = false; \ + DOCTEST_MSVC_SUPPRESS_WARNING_POP \ + DOCTEST_CLANG_SUPPRESS_WARNING_POP \ + DOCTEST_GCC_SUPPRESS_WARNING_POP \ + if(!inited) { \ + data* decorators; \ + inited = true; \ + } \ + return data; \ + } \ + } \ + } \ + namespace ns_name + +#define DOCTEST_TEST_SUITE(decorators) \ + DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(DOCTEST_ANON_SUITE_)) + +// for starting a testsuite block +#define DOCTEST_TEST_SUITE_BEGIN(decorators) \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \ + doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators)) \ + static_assert(true, "") + +// for ending a testsuite block +#define DOCTEST_TEST_SUITE_END \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \ + doctest::detail::setTestSuite(doctest::detail::TestSuite() * "")) \ + using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int + +// for registering exception translators +#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \ + inline doctest::String translatorName(signature); \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), /* NOLINT(cert-err58-cpp) */ \ + doctest::registerExceptionTranslator(translatorName)) \ + doctest::String translatorName(signature) + +#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ + DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \ + signature) + +// for registering reporters +#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \ + doctest::registerReporter(name, priority, true)) \ + static_assert(true, "") + +// for registering listeners +#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \ + doctest::registerReporter(name, priority, false)) \ + static_assert(true, "") + +// clang-format off +// for logging - disabling formatting because it's important to have these on 2 separate lines - see PR #557 +#define DOCTEST_INFO(...) \ + DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_), \ + DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_OTHER_), \ + __VA_ARGS__) +// clang-format on + +#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \ + auto DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \ + [&](std::ostream* s_name) { \ + doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \ + mb_name.m_stream = s_name; \ + mb_name * __VA_ARGS__; \ + }) + +#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x) + +#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \ + DOCTEST_FUNC_SCOPE_BEGIN { \ + doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \ + mb * __VA_ARGS__; \ + if(mb.log()) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ + mb.react(); \ + } DOCTEST_FUNC_SCOPE_END + +// clang-format off +#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) +#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) +#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__) +// clang-format on + +#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__) +#define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__) +#define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__) + +#define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility. + +#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ + /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \ + doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ + DOCTEST_WRAP_IN_TRY(DOCTEST_RB.setResult( \ + doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ + << __VA_ARGS__)) /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \ + DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB) \ + DOCTEST_CLANG_SUPPRESS_WARNING_POP + +#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ + DOCTEST_FUNC_SCOPE_BEGIN { \ + DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \ + } DOCTEST_FUNC_SCOPE_END // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) + +#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \ + DOCTEST_FUNC_SCOPE_BEGIN { \ + doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ + DOCTEST_WRAP_IN_TRY( \ + DOCTEST_RB.binary_assert( \ + __VA_ARGS__)) \ + DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ + } DOCTEST_FUNC_SCOPE_END + +#define DOCTEST_UNARY_ASSERT(assert_type, ...) \ + DOCTEST_FUNC_SCOPE_BEGIN { \ + doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ + DOCTEST_WRAP_IN_TRY(DOCTEST_RB.unary_assert(__VA_ARGS__)) \ + DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ + } DOCTEST_FUNC_SCOPE_END + +#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +// necessary for _MESSAGE +#define DOCTEST_ASSERT_IMPLEMENT_2 DOCTEST_ASSERT_IMPLEMENT_1 + +#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ + doctest::detail::decomp_assert( \ + doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, \ + doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ + << __VA_ARGS__) DOCTEST_CLANG_SUPPRESS_WARNING_POP + +#define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \ + doctest::detail::binary_assert( \ + doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__) + +#define DOCTEST_UNARY_ASSERT(assert_type, ...) \ + doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \ + #__VA_ARGS__, __VA_ARGS__) + +#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN, __VA_ARGS__) +#define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK, __VA_ARGS__) +#define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE, __VA_ARGS__) +#define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN_FALSE, __VA_ARGS__) +#define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK_FALSE, __VA_ARGS__) +#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__) + +// clang-format off +#define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } DOCTEST_FUNC_SCOPE_END +// clang-format on + +#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__) +#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__) +#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__) +#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__) +#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__) +#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__) +#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__) +#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__) +#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__) +#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__) +#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__) +#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__) +#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__) +#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__) +#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__) +#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__) +#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__) +#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__) + +#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__) +#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__) +#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__) +#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__) +#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__) +#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__) + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + +#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \ + DOCTEST_FUNC_SCOPE_BEGIN { \ + if(!doctest::getContextOptions()->no_throw) { \ + doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #expr, #__VA_ARGS__, message); \ + try { \ + DOCTEST_CAST_TO_VOID(expr) \ + } catch(const typename doctest::detail::types::remove_const< \ + typename doctest::detail::types::remove_reference<__VA_ARGS__>::type>::type&) {\ + DOCTEST_RB.translateException(); \ + DOCTEST_RB.m_threw_as = true; \ + } catch(...) { DOCTEST_RB.translateException(); } \ + DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ + } else { /* NOLINT(*-else-after-return) */ \ + DOCTEST_FUNC_SCOPE_RET(false); \ + } \ + } DOCTEST_FUNC_SCOPE_END + +#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \ + DOCTEST_FUNC_SCOPE_BEGIN { \ + if(!doctest::getContextOptions()->no_throw) { \ + doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, expr_str, "", __VA_ARGS__); \ + try { \ + DOCTEST_CAST_TO_VOID(expr) \ + } catch(...) { DOCTEST_RB.translateException(); } \ + DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ + } else { /* NOLINT(*-else-after-return) */ \ + DOCTEST_FUNC_SCOPE_RET(false); \ + } \ + } DOCTEST_FUNC_SCOPE_END + +#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \ + DOCTEST_FUNC_SCOPE_BEGIN { \ + doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ + try { \ + DOCTEST_CAST_TO_VOID(__VA_ARGS__) \ + } catch(...) { DOCTEST_RB.translateException(); } \ + DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ + } DOCTEST_FUNC_SCOPE_END + +// clang-format off +#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "") +#define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "") +#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "") + +#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__) +#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__) +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__) + +#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__) +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__) +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__) + +#define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__) +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__) +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__) + +#define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__) +#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__) +#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__) + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END +// clang-format on + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + +// ================================================================================================= +// == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! == +// == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! == +// ================================================================================================= +#else // DOCTEST_CONFIG_DISABLE + +#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ + namespace /* NOLINT */ { \ + template \ + struct der : public base \ + { void f(); }; \ + } \ + template \ + inline void der::f() + +#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \ + template \ + static inline void f() + +// for registering tests +#define DOCTEST_TEST_CASE(name) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) + +// for registering tests in classes +#define DOCTEST_TEST_CASE_CLASS(name) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) + +// for registering tests with a fixture +#define DOCTEST_TEST_CASE_FIXTURE(x, name) \ + DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), x, \ + DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name) + +// for converting types to strings without the header and demangling +#define DOCTEST_TYPE_TO_STRING_AS(str, ...) static_assert(true, "") +#define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "") + +// for typed tests +#define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \ + template \ + inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() + +#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \ + template \ + inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() + +#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) static_assert(true, "") +#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) static_assert(true, "") + +// for subcases +#define DOCTEST_SUBCASE(name) + +// for a testsuite block +#define DOCTEST_TEST_SUITE(name) namespace // NOLINT + +// for starting a testsuite block +#define DOCTEST_TEST_SUITE_BEGIN(name) static_assert(true, "") + +// for ending a testsuite block +#define DOCTEST_TEST_SUITE_END using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int + +#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ + template \ + static inline doctest::String DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_)(signature) + +#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) +#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) + +#define DOCTEST_INFO(...) (static_cast(0)) +#define DOCTEST_CAPTURE(x) (static_cast(0)) +#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast(0)) +#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast(0)) +#define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast(0)) +#define DOCTEST_MESSAGE(...) (static_cast(0)) +#define DOCTEST_FAIL_CHECK(...) (static_cast(0)) +#define DOCTEST_FAIL(...) (static_cast(0)) + +#if defined(DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED) \ + && defined(DOCTEST_CONFIG_ASSERTS_RETURN_VALUES) + +#define DOCTEST_WARN(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_CHECK(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_REQUIRE(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_WARN_FALSE(...) [&] { return !(__VA_ARGS__); }() +#define DOCTEST_CHECK_FALSE(...) [&] { return !(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_FALSE(...) [&] { return !(__VA_ARGS__); }() + +#define DOCTEST_WARN_MESSAGE(cond, ...) [&] { return cond; }() +#define DOCTEST_CHECK_MESSAGE(cond, ...) [&] { return cond; }() +#define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] { return cond; }() +#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() +#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() +#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() + +namespace doctest { +namespace detail { +#define DOCTEST_RELATIONAL_OP(name, op) \ + template \ + bool name(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs op rhs; } + + DOCTEST_RELATIONAL_OP(eq, ==) + DOCTEST_RELATIONAL_OP(ne, !=) + DOCTEST_RELATIONAL_OP(lt, <) + DOCTEST_RELATIONAL_OP(gt, >) + DOCTEST_RELATIONAL_OP(le, <=) + DOCTEST_RELATIONAL_OP(ge, >=) +} // namespace detail +} // namespace doctest + +#define DOCTEST_WARN_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() +#define DOCTEST_CHECK_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() +#define DOCTEST_WARN_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() +#define DOCTEST_CHECK_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() +#define DOCTEST_WARN_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() +#define DOCTEST_CHECK_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() +#define DOCTEST_WARN_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() +#define DOCTEST_CHECK_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() +#define DOCTEST_WARN_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() +#define DOCTEST_CHECK_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() +#define DOCTEST_WARN_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() +#define DOCTEST_CHECK_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() +#define DOCTEST_WARN_UNARY(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_CHECK_UNARY(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_REQUIRE_UNARY(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() +#define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + +#define DOCTEST_WARN_THROWS_WITH(expr, with, ...) [] { static_assert(false, "Exception translation is not available when doctest is disabled."); return false; }() +#define DOCTEST_CHECK_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_REQUIRE_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) + +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) + +#define DOCTEST_WARN_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_CHECK_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_REQUIRE_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_WARN_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_CHECK_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_WARN_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() +#define DOCTEST_CHECK_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() +#define DOCTEST_REQUIRE_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + +#else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED + +#define DOCTEST_WARN(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_FALSE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_FALSE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_FUNC_EMPTY + +#define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY + +#define DOCTEST_WARN_EQ(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_EQ(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_EQ(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_NE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_NE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_NE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_GT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_GT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_GT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_LT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_LT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_LT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_GE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_GE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_GE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_LE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_LE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_LE(...) DOCTEST_FUNC_EMPTY + +#define DOCTEST_WARN_UNARY(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_UNARY(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + +#define DOCTEST_WARN_THROWS(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_NOTHROW(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_FUNC_EMPTY + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + +#endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED + +#endif // DOCTEST_CONFIG_DISABLE + +#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS + +#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS +#define DOCTEST_EXCEPTION_EMPTY_FUNC DOCTEST_FUNC_EMPTY +#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS +#define DOCTEST_EXCEPTION_EMPTY_FUNC [] { static_assert(false, "Exceptions are disabled! " \ + "Use DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS if you want to compile with exceptions disabled."); return false; }() + +#undef DOCTEST_REQUIRE +#undef DOCTEST_REQUIRE_FALSE +#undef DOCTEST_REQUIRE_MESSAGE +#undef DOCTEST_REQUIRE_FALSE_MESSAGE +#undef DOCTEST_REQUIRE_EQ +#undef DOCTEST_REQUIRE_NE +#undef DOCTEST_REQUIRE_GT +#undef DOCTEST_REQUIRE_LT +#undef DOCTEST_REQUIRE_GE +#undef DOCTEST_REQUIRE_LE +#undef DOCTEST_REQUIRE_UNARY +#undef DOCTEST_REQUIRE_UNARY_FALSE + +#define DOCTEST_REQUIRE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_FALSE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_EQ DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_NE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_GT DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_LT DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_GE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_LE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_UNARY DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_UNARY_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS + +#define DOCTEST_WARN_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + +// clang-format off +// KEPT FOR BACKWARDS COMPATIBILITY - FORWARDING TO THE RIGHT MACROS +#define DOCTEST_FAST_WARN_EQ DOCTEST_WARN_EQ +#define DOCTEST_FAST_CHECK_EQ DOCTEST_CHECK_EQ +#define DOCTEST_FAST_REQUIRE_EQ DOCTEST_REQUIRE_EQ +#define DOCTEST_FAST_WARN_NE DOCTEST_WARN_NE +#define DOCTEST_FAST_CHECK_NE DOCTEST_CHECK_NE +#define DOCTEST_FAST_REQUIRE_NE DOCTEST_REQUIRE_NE +#define DOCTEST_FAST_WARN_GT DOCTEST_WARN_GT +#define DOCTEST_FAST_CHECK_GT DOCTEST_CHECK_GT +#define DOCTEST_FAST_REQUIRE_GT DOCTEST_REQUIRE_GT +#define DOCTEST_FAST_WARN_LT DOCTEST_WARN_LT +#define DOCTEST_FAST_CHECK_LT DOCTEST_CHECK_LT +#define DOCTEST_FAST_REQUIRE_LT DOCTEST_REQUIRE_LT +#define DOCTEST_FAST_WARN_GE DOCTEST_WARN_GE +#define DOCTEST_FAST_CHECK_GE DOCTEST_CHECK_GE +#define DOCTEST_FAST_REQUIRE_GE DOCTEST_REQUIRE_GE +#define DOCTEST_FAST_WARN_LE DOCTEST_WARN_LE +#define DOCTEST_FAST_CHECK_LE DOCTEST_CHECK_LE +#define DOCTEST_FAST_REQUIRE_LE DOCTEST_REQUIRE_LE + +#define DOCTEST_FAST_WARN_UNARY DOCTEST_WARN_UNARY +#define DOCTEST_FAST_CHECK_UNARY DOCTEST_CHECK_UNARY +#define DOCTEST_FAST_REQUIRE_UNARY DOCTEST_REQUIRE_UNARY +#define DOCTEST_FAST_WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE +#define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE +#define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE + +#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__) +// clang-format on + +// BDD style macros +// clang-format off +#define DOCTEST_SCENARIO(name) DOCTEST_TEST_CASE(" Scenario: " name) +#define DOCTEST_SCENARIO_CLASS(name) DOCTEST_TEST_CASE_CLASS(" Scenario: " name) +#define DOCTEST_SCENARIO_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(" Scenario: " name, T, __VA_ARGS__) +#define DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(" Scenario: " name, T, id) + +#define DOCTEST_GIVEN(name) DOCTEST_SUBCASE(" Given: " name) +#define DOCTEST_WHEN(name) DOCTEST_SUBCASE(" When: " name) +#define DOCTEST_AND_WHEN(name) DOCTEST_SUBCASE("And when: " name) +#define DOCTEST_THEN(name) DOCTEST_SUBCASE(" Then: " name) +#define DOCTEST_AND_THEN(name) DOCTEST_SUBCASE(" And: " name) +// clang-format on + +// == SHORT VERSIONS OF THE MACROS +#ifndef DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES + +#define TEST_CASE(name) DOCTEST_TEST_CASE(name) +#define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name) +#define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name) +#define TYPE_TO_STRING_AS(str, ...) DOCTEST_TYPE_TO_STRING_AS(str, __VA_ARGS__) +#define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__) +#define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__) +#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id) +#define TEST_CASE_TEMPLATE_INVOKE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__) +#define TEST_CASE_TEMPLATE_APPLY(id, ...) DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, __VA_ARGS__) +#define SUBCASE(name) DOCTEST_SUBCASE(name) +#define TEST_SUITE(decorators) DOCTEST_TEST_SUITE(decorators) +#define TEST_SUITE_BEGIN(name) DOCTEST_TEST_SUITE_BEGIN(name) +#define TEST_SUITE_END DOCTEST_TEST_SUITE_END +#define REGISTER_EXCEPTION_TRANSLATOR(signature) DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) +#define REGISTER_REPORTER(name, priority, reporter) DOCTEST_REGISTER_REPORTER(name, priority, reporter) +#define REGISTER_LISTENER(name, priority, reporter) DOCTEST_REGISTER_LISTENER(name, priority, reporter) +#define INFO(...) DOCTEST_INFO(__VA_ARGS__) +#define CAPTURE(x) DOCTEST_CAPTURE(x) +#define ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_MESSAGE_AT(file, line, __VA_ARGS__) +#define ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_FAIL_CHECK_AT(file, line, __VA_ARGS__) +#define ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_FAIL_AT(file, line, __VA_ARGS__) +#define MESSAGE(...) DOCTEST_MESSAGE(__VA_ARGS__) +#define FAIL_CHECK(...) DOCTEST_FAIL_CHECK(__VA_ARGS__) +#define FAIL(...) DOCTEST_FAIL(__VA_ARGS__) +#define TO_LVALUE(...) DOCTEST_TO_LVALUE(__VA_ARGS__) + +#define WARN(...) DOCTEST_WARN(__VA_ARGS__) +#define WARN_FALSE(...) DOCTEST_WARN_FALSE(__VA_ARGS__) +#define WARN_THROWS(...) DOCTEST_WARN_THROWS(__VA_ARGS__) +#define WARN_THROWS_AS(expr, ...) DOCTEST_WARN_THROWS_AS(expr, __VA_ARGS__) +#define WARN_THROWS_WITH(expr, ...) DOCTEST_WARN_THROWS_WITH(expr, __VA_ARGS__) +#define WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_WARN_THROWS_WITH_AS(expr, with, __VA_ARGS__) +#define WARN_NOTHROW(...) DOCTEST_WARN_NOTHROW(__VA_ARGS__) +#define CHECK(...) DOCTEST_CHECK(__VA_ARGS__) +#define CHECK_FALSE(...) DOCTEST_CHECK_FALSE(__VA_ARGS__) +#define CHECK_THROWS(...) DOCTEST_CHECK_THROWS(__VA_ARGS__) +#define CHECK_THROWS_AS(expr, ...) DOCTEST_CHECK_THROWS_AS(expr, __VA_ARGS__) +#define CHECK_THROWS_WITH(expr, ...) DOCTEST_CHECK_THROWS_WITH(expr, __VA_ARGS__) +#define CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_AS(expr, with, __VA_ARGS__) +#define CHECK_NOTHROW(...) DOCTEST_CHECK_NOTHROW(__VA_ARGS__) +#define REQUIRE(...) DOCTEST_REQUIRE(__VA_ARGS__) +#define REQUIRE_FALSE(...) DOCTEST_REQUIRE_FALSE(__VA_ARGS__) +#define REQUIRE_THROWS(...) DOCTEST_REQUIRE_THROWS(__VA_ARGS__) +#define REQUIRE_THROWS_AS(expr, ...) DOCTEST_REQUIRE_THROWS_AS(expr, __VA_ARGS__) +#define REQUIRE_THROWS_WITH(expr, ...) DOCTEST_REQUIRE_THROWS_WITH(expr, __VA_ARGS__) +#define REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, __VA_ARGS__) +#define REQUIRE_NOTHROW(...) DOCTEST_REQUIRE_NOTHROW(__VA_ARGS__) + +#define WARN_MESSAGE(cond, ...) DOCTEST_WARN_MESSAGE(cond, __VA_ARGS__) +#define WARN_FALSE_MESSAGE(cond, ...) DOCTEST_WARN_FALSE_MESSAGE(cond, __VA_ARGS__) +#define WARN_THROWS_MESSAGE(expr, ...) DOCTEST_WARN_THROWS_MESSAGE(expr, __VA_ARGS__) +#define WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) +#define WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) +#define WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) +#define WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_WARN_NOTHROW_MESSAGE(expr, __VA_ARGS__) +#define CHECK_MESSAGE(cond, ...) DOCTEST_CHECK_MESSAGE(cond, __VA_ARGS__) +#define CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_CHECK_FALSE_MESSAGE(cond, __VA_ARGS__) +#define CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_CHECK_THROWS_MESSAGE(expr, __VA_ARGS__) +#define CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) +#define CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) +#define CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) +#define CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_CHECK_NOTHROW_MESSAGE(expr, __VA_ARGS__) +#define REQUIRE_MESSAGE(cond, ...) DOCTEST_REQUIRE_MESSAGE(cond, __VA_ARGS__) +#define REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_REQUIRE_FALSE_MESSAGE(cond, __VA_ARGS__) +#define REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_REQUIRE_THROWS_MESSAGE(expr, __VA_ARGS__) +#define REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) +#define REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) +#define REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) +#define REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, __VA_ARGS__) + +#define SCENARIO(name) DOCTEST_SCENARIO(name) +#define SCENARIO_CLASS(name) DOCTEST_SCENARIO_CLASS(name) +#define SCENARIO_TEMPLATE(name, T, ...) DOCTEST_SCENARIO_TEMPLATE(name, T, __VA_ARGS__) +#define SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) +#define GIVEN(name) DOCTEST_GIVEN(name) +#define WHEN(name) DOCTEST_WHEN(name) +#define AND_WHEN(name) DOCTEST_AND_WHEN(name) +#define THEN(name) DOCTEST_THEN(name) +#define AND_THEN(name) DOCTEST_AND_THEN(name) + +#define WARN_EQ(...) DOCTEST_WARN_EQ(__VA_ARGS__) +#define CHECK_EQ(...) DOCTEST_CHECK_EQ(__VA_ARGS__) +#define REQUIRE_EQ(...) DOCTEST_REQUIRE_EQ(__VA_ARGS__) +#define WARN_NE(...) DOCTEST_WARN_NE(__VA_ARGS__) +#define CHECK_NE(...) DOCTEST_CHECK_NE(__VA_ARGS__) +#define REQUIRE_NE(...) DOCTEST_REQUIRE_NE(__VA_ARGS__) +#define WARN_GT(...) DOCTEST_WARN_GT(__VA_ARGS__) +#define CHECK_GT(...) DOCTEST_CHECK_GT(__VA_ARGS__) +#define REQUIRE_GT(...) DOCTEST_REQUIRE_GT(__VA_ARGS__) +#define WARN_LT(...) DOCTEST_WARN_LT(__VA_ARGS__) +#define CHECK_LT(...) DOCTEST_CHECK_LT(__VA_ARGS__) +#define REQUIRE_LT(...) DOCTEST_REQUIRE_LT(__VA_ARGS__) +#define WARN_GE(...) DOCTEST_WARN_GE(__VA_ARGS__) +#define CHECK_GE(...) DOCTEST_CHECK_GE(__VA_ARGS__) +#define REQUIRE_GE(...) DOCTEST_REQUIRE_GE(__VA_ARGS__) +#define WARN_LE(...) DOCTEST_WARN_LE(__VA_ARGS__) +#define CHECK_LE(...) DOCTEST_CHECK_LE(__VA_ARGS__) +#define REQUIRE_LE(...) DOCTEST_REQUIRE_LE(__VA_ARGS__) +#define WARN_UNARY(...) DOCTEST_WARN_UNARY(__VA_ARGS__) +#define CHECK_UNARY(...) DOCTEST_CHECK_UNARY(__VA_ARGS__) +#define REQUIRE_UNARY(...) DOCTEST_REQUIRE_UNARY(__VA_ARGS__) +#define WARN_UNARY_FALSE(...) DOCTEST_WARN_UNARY_FALSE(__VA_ARGS__) +#define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__) +#define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__) + +// KEPT FOR BACKWARDS COMPATIBILITY +#define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__) +#define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__) +#define FAST_REQUIRE_EQ(...) DOCTEST_FAST_REQUIRE_EQ(__VA_ARGS__) +#define FAST_WARN_NE(...) DOCTEST_FAST_WARN_NE(__VA_ARGS__) +#define FAST_CHECK_NE(...) DOCTEST_FAST_CHECK_NE(__VA_ARGS__) +#define FAST_REQUIRE_NE(...) DOCTEST_FAST_REQUIRE_NE(__VA_ARGS__) +#define FAST_WARN_GT(...) DOCTEST_FAST_WARN_GT(__VA_ARGS__) +#define FAST_CHECK_GT(...) DOCTEST_FAST_CHECK_GT(__VA_ARGS__) +#define FAST_REQUIRE_GT(...) DOCTEST_FAST_REQUIRE_GT(__VA_ARGS__) +#define FAST_WARN_LT(...) DOCTEST_FAST_WARN_LT(__VA_ARGS__) +#define FAST_CHECK_LT(...) DOCTEST_FAST_CHECK_LT(__VA_ARGS__) +#define FAST_REQUIRE_LT(...) DOCTEST_FAST_REQUIRE_LT(__VA_ARGS__) +#define FAST_WARN_GE(...) DOCTEST_FAST_WARN_GE(__VA_ARGS__) +#define FAST_CHECK_GE(...) DOCTEST_FAST_CHECK_GE(__VA_ARGS__) +#define FAST_REQUIRE_GE(...) DOCTEST_FAST_REQUIRE_GE(__VA_ARGS__) +#define FAST_WARN_LE(...) DOCTEST_FAST_WARN_LE(__VA_ARGS__) +#define FAST_CHECK_LE(...) DOCTEST_FAST_CHECK_LE(__VA_ARGS__) +#define FAST_REQUIRE_LE(...) DOCTEST_FAST_REQUIRE_LE(__VA_ARGS__) + +#define FAST_WARN_UNARY(...) DOCTEST_FAST_WARN_UNARY(__VA_ARGS__) +#define FAST_CHECK_UNARY(...) DOCTEST_FAST_CHECK_UNARY(__VA_ARGS__) +#define FAST_REQUIRE_UNARY(...) DOCTEST_FAST_REQUIRE_UNARY(__VA_ARGS__) +#define FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_WARN_UNARY_FALSE(__VA_ARGS__) +#define FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_CHECK_UNARY_FALSE(__VA_ARGS__) +#define FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_REQUIRE_UNARY_FALSE(__VA_ARGS__) + +#define TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, __VA_ARGS__) + +#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES + +#ifndef DOCTEST_CONFIG_DISABLE + +// this is here to clear the 'current test suite' for the current translation unit - at the top +DOCTEST_TEST_SUITE_END(); + +#endif // DOCTEST_CONFIG_DISABLE + +DOCTEST_CLANG_SUPPRESS_WARNING_POP +DOCTEST_MSVC_SUPPRESS_WARNING_POP +DOCTEST_GCC_SUPPRESS_WARNING_POP + +DOCTEST_SUPPRESS_COMMON_WARNINGS_POP + +#endif // DOCTEST_LIBRARY_INCLUDED + +#ifndef DOCTEST_SINGLE_HEADER +#define DOCTEST_SINGLE_HEADER +#endif // DOCTEST_SINGLE_HEADER + +#if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER) + +#ifndef DOCTEST_SINGLE_HEADER +#include "doctest_fwd.h" +#endif // DOCTEST_SINGLE_HEADER + +DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros") + +#ifndef DOCTEST_LIBRARY_IMPLEMENTATION +#define DOCTEST_LIBRARY_IMPLEMENTATION + +DOCTEST_CLANG_SUPPRESS_WARNING_POP + +DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH + +DOCTEST_CLANG_SUPPRESS_WARNING_PUSH +DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path") + +DOCTEST_GCC_SUPPRESS_WARNING_PUSH +DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") +DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") +DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers") +DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces") +DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch") +DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum") +DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default") +DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations") +DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast") +DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function") +DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance") +DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute") + +DOCTEST_MSVC_SUPPRESS_WARNING_PUSH +DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data +DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled +DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified +DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal +DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch +DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C +DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning) +DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed + +DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN + +// required includes - will go only in one translation unit! +#include +#include +#include +// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37 +#ifdef __BORLANDC__ +#include +#endif // __BORLANDC__ +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM +#include +#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM +#include +#include +#include +#ifndef DOCTEST_CONFIG_NO_MULTITHREADING +#include +#include +#define DOCTEST_DECLARE_MUTEX(name) std::mutex name; +#define DOCTEST_DECLARE_STATIC_MUTEX(name) static DOCTEST_DECLARE_MUTEX(name) +#define DOCTEST_LOCK_MUTEX(name) std::lock_guard DOCTEST_ANONYMOUS(DOCTEST_ANON_LOCK_)(name); +#else // DOCTEST_CONFIG_NO_MULTITHREADING +#define DOCTEST_DECLARE_MUTEX(name) +#define DOCTEST_DECLARE_STATIC_MUTEX(name) +#define DOCTEST_LOCK_MUTEX(name) +#endif // DOCTEST_CONFIG_NO_MULTITHREADING +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DOCTEST_PLATFORM_MAC +#include +#include +#include +#endif // DOCTEST_PLATFORM_MAC + +#ifdef DOCTEST_PLATFORM_WINDOWS + +// defines for a leaner windows.h +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#define DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN +#endif // WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX +#define DOCTEST_UNDEF_NOMINMAX +#endif // NOMINMAX + +// not sure what AfxWin.h is for - here I do what Catch does +#ifdef __AFXDLL +#include +#else +#include +#endif +#include + +#else // DOCTEST_PLATFORM_WINDOWS + +#include +#include + +#endif // DOCTEST_PLATFORM_WINDOWS + +// this is a fix for https://github.com/doctest/doctest/issues/348 +// https://mail.gnome.org/archives/xml/2012-January/msg00000.html +#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO) +#define STDOUT_FILENO fileno(stdout) +#endif // HAVE_UNISTD_H + +DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END + +// counts the number of elements in a C array +#define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0])) + +#ifdef DOCTEST_CONFIG_DISABLE +#define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_disabled +#else // DOCTEST_CONFIG_DISABLE +#define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_not_disabled +#endif // DOCTEST_CONFIG_DISABLE + +#ifndef DOCTEST_CONFIG_OPTIONS_PREFIX +#define DOCTEST_CONFIG_OPTIONS_PREFIX "dt-" +#endif + +#ifndef DOCTEST_THREAD_LOCAL +#if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) +#define DOCTEST_THREAD_LOCAL +#else // DOCTEST_MSVC +#define DOCTEST_THREAD_LOCAL thread_local +#endif // DOCTEST_MSVC +#endif // DOCTEST_THREAD_LOCAL + +#ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES +#define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32 +#endif + +#ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE +#define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64 +#endif + +#ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS +#define DOCTEST_OPTIONS_PREFIX_DISPLAY DOCTEST_CONFIG_OPTIONS_PREFIX +#else +#define DOCTEST_OPTIONS_PREFIX_DISPLAY "" +#endif + +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS +#endif + +#ifndef DOCTEST_CDECL +#define DOCTEST_CDECL __cdecl +#endif + +namespace doctest { + +bool is_running_in_test = false; + +namespace { + using namespace detail; + + template + DOCTEST_NORETURN void throw_exception(Ex const& e) { +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + throw e; +#else // DOCTEST_CONFIG_NO_EXCEPTIONS +#ifdef DOCTEST_CONFIG_HANDLE_EXCEPTION + DOCTEST_CONFIG_HANDLE_EXCEPTION(e); +#else // DOCTEST_CONFIG_HANDLE_EXCEPTION +#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + std::cerr << "doctest will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; +#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM +#endif // DOCTEST_CONFIG_HANDLE_EXCEPTION + std::terminate(); +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + } + +#ifndef DOCTEST_INTERNAL_ERROR +#define DOCTEST_INTERNAL_ERROR(msg) \ + throw_exception(std::logic_error( \ + __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) +#endif // DOCTEST_INTERNAL_ERROR + + // case insensitive strcmp + int stricmp(const char* a, const char* b) { + for(;; a++, b++) { + const int d = tolower(*a) - tolower(*b); + if(d != 0 || !*a) + return d; + } + } + + struct Endianness + { + enum Arch + { + Big, + Little + }; + + static Arch which() { + int x = 1; + // casting any data pointer to char* is allowed + auto ptr = reinterpret_cast(&x); + if(*ptr) + return Little; + return Big; + } + }; +} // namespace + +namespace detail { + DOCTEST_THREAD_LOCAL class + { + std::vector stack; + std::stringstream ss; + + public: + std::ostream* push() { + stack.push_back(ss.tellp()); + return &ss; + } + + String pop() { + if (stack.empty()) + DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!"); + + std::streampos pos = stack.back(); + stack.pop_back(); + unsigned sz = static_cast(ss.tellp() - pos); + ss.rdbuf()->pubseekpos(pos, std::ios::in | std::ios::out); + return String(ss, sz); + } + } g_oss; + + std::ostream* tlssPush() { + return g_oss.push(); + } + + String tlssPop() { + return g_oss.pop(); + } + +#ifndef DOCTEST_CONFIG_DISABLE + +namespace timer_large_integer +{ + +#if defined(DOCTEST_PLATFORM_WINDOWS) + using type = ULONGLONG; +#else // DOCTEST_PLATFORM_WINDOWS + using type = std::uint64_t; +#endif // DOCTEST_PLATFORM_WINDOWS +} + +using ticks_t = timer_large_integer::type; + +#ifdef DOCTEST_CONFIG_GETCURRENTTICKS + ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); } +#elif defined(DOCTEST_PLATFORM_WINDOWS) + ticks_t getCurrentTicks() { + static LARGE_INTEGER hz = { {0} }, hzo = { {0} }; + if(!hz.QuadPart) { + QueryPerformanceFrequency(&hz); + QueryPerformanceCounter(&hzo); + } + LARGE_INTEGER t; + QueryPerformanceCounter(&t); + return ((t.QuadPart - hzo.QuadPart) * LONGLONG(1000000)) / hz.QuadPart; + } +#else // DOCTEST_PLATFORM_WINDOWS + ticks_t getCurrentTicks() { + timeval t; + gettimeofday(&t, nullptr); + return static_cast(t.tv_sec) * 1000000 + static_cast(t.tv_usec); + } +#endif // DOCTEST_PLATFORM_WINDOWS + + struct Timer + { + void start() { m_ticks = getCurrentTicks(); } + unsigned int getElapsedMicroseconds() const { + return static_cast(getCurrentTicks() - m_ticks); + } + //unsigned int getElapsedMilliseconds() const { + // return static_cast(getElapsedMicroseconds() / 1000); + //} + double getElapsedSeconds() const { return static_cast(getCurrentTicks() - m_ticks) / 1000000.0; } + + private: + ticks_t m_ticks = 0; + }; + +#ifdef DOCTEST_CONFIG_NO_MULTITHREADING + template + using Atomic = T; +#else // DOCTEST_CONFIG_NO_MULTITHREADING + template + using Atomic = std::atomic; +#endif // DOCTEST_CONFIG_NO_MULTITHREADING + +#if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING) + template + using MultiLaneAtomic = Atomic; +#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS + // Provides a multilane implementation of an atomic variable that supports add, sub, load, + // store. Instead of using a single atomic variable, this splits up into multiple ones, + // each sitting on a separate cache line. The goal is to provide a speedup when most + // operations are modifying. It achieves this with two properties: + // + // * Multiple atomics are used, so chance of congestion from the same atomic is reduced. + // * Each atomic sits on a separate cache line, so false sharing is reduced. + // + // The disadvantage is that there is a small overhead due to the use of TLS, and load/store + // is slower because all atomics have to be accessed. + template + class MultiLaneAtomic + { + struct CacheLineAlignedAtomic + { + Atomic atomic{}; + char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic)]; + }; + CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES]; + + static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE, + "guarantee one atomic takes exactly one cache line"); + + public: + T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; } + + T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); } + + T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { + return myAtomic().fetch_add(arg, order); + } + + T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { + return myAtomic().fetch_sub(arg, order); + } + + operator T() const DOCTEST_NOEXCEPT { return load(); } + + T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT { + auto result = T(); + for(auto const& c : m_atomics) { + result += c.atomic.load(order); + } + return result; + } + + T operator=(T desired) DOCTEST_NOEXCEPT { // lgtm [cpp/assignment-does-not-return-this] + store(desired); + return desired; + } + + void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { + // first value becomes desired", all others become 0. + for(auto& c : m_atomics) { + c.atomic.store(desired, order); + desired = {}; + } + } + + private: + // Each thread has a different atomic that it operates on. If more than NumLanes threads + // use this, some will use the same atomic. So performance will degrade a bit, but still + // everything will work. + // + // The logic here is a bit tricky. The call should be as fast as possible, so that there + // is minimal to no overhead in determining the correct atomic for the current thread. + // + // 1. A global static counter laneCounter counts continuously up. + // 2. Each successive thread will use modulo operation of that counter so it gets an atomic + // assigned in a round-robin fashion. + // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with + // little overhead. + Atomic& myAtomic() DOCTEST_NOEXCEPT { + static Atomic laneCounter; + DOCTEST_THREAD_LOCAL size_t tlsLaneIdx = + laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES; + + return m_atomics[tlsLaneIdx].atomic; + } + }; +#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS + + // this holds both parameters from the command line and runtime data for tests + struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats + { + MultiLaneAtomic numAssertsCurrentTest_atomic; + MultiLaneAtomic numAssertsFailedCurrentTest_atomic; + + std::vector> filters = decltype(filters)(9); // 9 different filters + + std::vector reporters_currently_used; + + assert_handler ah = nullptr; + + Timer timer; + + std::vector stringifiedContexts; // logging from INFO() due to an exception + + // stuff for subcases + bool reachedLeaf; + std::vector subcaseStack; + std::vector nextSubcaseStack; + std::unordered_set fullyTraversedSubcases; + size_t currentSubcaseDepth; + Atomic shouldLogCurrentException; + + void resetRunData() { + numTestCases = 0; + numTestCasesPassingFilters = 0; + numTestSuitesPassingFilters = 0; + numTestCasesFailed = 0; + numAsserts = 0; + numAssertsFailed = 0; + numAssertsCurrentTest = 0; + numAssertsFailedCurrentTest = 0; + } + + void finalizeTestCaseData() { + seconds = timer.getElapsedSeconds(); + + // update the non-atomic counters + numAsserts += numAssertsCurrentTest_atomic; + numAssertsFailed += numAssertsFailedCurrentTest_atomic; + numAssertsCurrentTest = numAssertsCurrentTest_atomic; + numAssertsFailedCurrentTest = numAssertsFailedCurrentTest_atomic; + + if(numAssertsFailedCurrentTest) + failure_flags |= TestCaseFailureReason::AssertFailure; + + if(Approx(currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 && + Approx(seconds).epsilon(DBL_EPSILON) > currentTest->m_timeout) + failure_flags |= TestCaseFailureReason::Timeout; + + if(currentTest->m_should_fail) { + if(failure_flags) { + failure_flags |= TestCaseFailureReason::ShouldHaveFailedAndDid; + } else { + failure_flags |= TestCaseFailureReason::ShouldHaveFailedButDidnt; + } + } else if(failure_flags && currentTest->m_may_fail) { + failure_flags |= TestCaseFailureReason::CouldHaveFailedAndDid; + } else if(currentTest->m_expected_failures > 0) { + if(numAssertsFailedCurrentTest == currentTest->m_expected_failures) { + failure_flags |= TestCaseFailureReason::FailedExactlyNumTimes; + } else { + failure_flags |= TestCaseFailureReason::DidntFailExactlyNumTimes; + } + } + + bool ok_to_fail = (TestCaseFailureReason::ShouldHaveFailedAndDid & failure_flags) || + (TestCaseFailureReason::CouldHaveFailedAndDid & failure_flags) || + (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags); + + // if any subcase has failed - the whole test case has failed + testCaseSuccess = !(failure_flags && !ok_to_fail); + if(!testCaseSuccess) + numTestCasesFailed++; + } + }; + + ContextState* g_cs = nullptr; + + // used to avoid locks for the debug output + // TODO: figure out if this is indeed necessary/correct - seems like either there still + // could be a race or that there wouldn't be a race even if using the context directly + DOCTEST_THREAD_LOCAL bool g_no_colors; + +#endif // DOCTEST_CONFIG_DISABLE +} // namespace detail + +char* String::allocate(size_type sz) { + if (sz <= last) { + buf[sz] = '\0'; + setLast(last - sz); + return buf; + } else { + setOnHeap(); + data.size = sz; + data.capacity = data.size + 1; + data.ptr = new char[data.capacity]; + data.ptr[sz] = '\0'; + return data.ptr; + } +} + +void String::setOnHeap() noexcept { *reinterpret_cast(&buf[last]) = 128; } +void String::setLast(size_type in) noexcept { buf[last] = char(in); } +void String::setSize(size_type sz) noexcept { + if (isOnStack()) { buf[sz] = '\0'; setLast(last - sz); } + else { data.ptr[sz] = '\0'; data.size = sz; } +} + +void String::copy(const String& other) { + if(other.isOnStack()) { + memcpy(buf, other.buf, len); + } else { + memcpy(allocate(other.data.size), other.data.ptr, other.data.size); + } +} + +String::String() noexcept { + buf[0] = '\0'; + setLast(); +} + +String::~String() { + if(!isOnStack()) + delete[] data.ptr; +} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) + +String::String(const char* in) + : String(in, strlen(in)) {} + +String::String(const char* in, size_type in_size) { + memcpy(allocate(in_size), in, in_size); +} + +String::String(std::istream& in, size_type in_size) { + in.read(allocate(in_size), in_size); +} + +String::String(const String& other) { copy(other); } + +String& String::operator=(const String& other) { + if(this != &other) { + if(!isOnStack()) + delete[] data.ptr; + + copy(other); + } + + return *this; +} + +String& String::operator+=(const String& other) { + const size_type my_old_size = size(); + const size_type other_size = other.size(); + const size_type total_size = my_old_size + other_size; + if(isOnStack()) { + if(total_size < len) { + // append to the current stack space + memcpy(buf + my_old_size, other.c_str(), other_size + 1); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + setLast(last - total_size); + } else { + // alloc new chunk + char* temp = new char[total_size + 1]; + // copy current data to new location before writing in the union + memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed + // update data in union + setOnHeap(); + data.size = total_size; + data.capacity = data.size + 1; + data.ptr = temp; + // transfer the rest of the data + memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); + } + } else { + if(data.capacity > total_size) { + // append to the current heap block + data.size = total_size; + memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); + } else { + // resize + data.capacity *= 2; + if(data.capacity <= total_size) + data.capacity = total_size + 1; + // alloc new chunk + char* temp = new char[data.capacity]; + // copy current data to new location before releasing it + memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed + // release old chunk + delete[] data.ptr; + // update the rest of the union members + data.size = total_size; + data.ptr = temp; + // transfer the rest of the data + memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); + } + } + + return *this; +} + +String::String(String&& other) noexcept { + memcpy(buf, other.buf, len); + other.buf[0] = '\0'; + other.setLast(); +} + +String& String::operator=(String&& other) noexcept { + if(this != &other) { + if(!isOnStack()) + delete[] data.ptr; + memcpy(buf, other.buf, len); + other.buf[0] = '\0'; + other.setLast(); + } + return *this; +} + +char String::operator[](size_type i) const { + return const_cast(this)->operator[](i); +} + +char& String::operator[](size_type i) { + if(isOnStack()) + return reinterpret_cast(buf)[i]; + return data.ptr[i]; +} + +DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") +String::size_type String::size() const { + if(isOnStack()) + return last - (size_type(buf[last]) & 31); // using "last" would work only if "len" is 32 + return data.size; +} +DOCTEST_GCC_SUPPRESS_WARNING_POP + +String::size_type String::capacity() const { + if(isOnStack()) + return len; + return data.capacity; +} + +String String::substr(size_type pos, size_type cnt) && { + cnt = std::min(cnt, size() - 1 - pos); + char* cptr = c_str(); + memmove(cptr, cptr + pos, cnt); + setSize(cnt); + return std::move(*this); +} + +String String::substr(size_type pos, size_type cnt) const & { + cnt = std::min(cnt, size() - 1 - pos); + return String{ c_str() + pos, cnt }; +} + +String::size_type String::find(char ch, size_type pos) const { + const char* begin = c_str(); + const char* end = begin + size(); + const char* it = begin + pos; + for (; it < end && *it != ch; it++); + if (it < end) { return static_cast(it - begin); } + else { return npos; } +} + +String::size_type String::rfind(char ch, size_type pos) const { + const char* begin = c_str(); + const char* it = begin + std::min(pos, size() - 1); + for (; it >= begin && *it != ch; it--); + if (it >= begin) { return static_cast(it - begin); } + else { return npos; } +} + +int String::compare(const char* other, bool no_case) const { + if(no_case) + return doctest::stricmp(c_str(), other); + return std::strcmp(c_str(), other); +} + +int String::compare(const String& other, bool no_case) const { + return compare(other.c_str(), no_case); +} + +String operator+(const String& lhs, const String& rhs) { return String(lhs) += rhs; } + +bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } +bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } +bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } +bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } +bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; } +bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; } + +std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); } + +Contains::Contains(const String& str) : string(str) { } + +bool Contains::checkWith(const String& other) const { + return strstr(other.c_str(), string.c_str()) != nullptr; +} + +String toString(const Contains& in) { + return "Contains( " + in.string + " )"; +} + +bool operator==(const String& lhs, const Contains& rhs) { return rhs.checkWith(lhs); } +bool operator==(const Contains& lhs, const String& rhs) { return lhs.checkWith(rhs); } +bool operator!=(const String& lhs, const Contains& rhs) { return !rhs.checkWith(lhs); } +bool operator!=(const Contains& lhs, const String& rhs) { return !lhs.checkWith(rhs); } + +namespace { + void color_to_stream(std::ostream&, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;) +} // namespace + +namespace Color { + std::ostream& operator<<(std::ostream& s, Color::Enum code) { + color_to_stream(s, code); + return s; + } +} // namespace Color + +// clang-format off +const char* assertString(assertType::Enum at) { + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitly handled + #define DOCTEST_GENERATE_ASSERT_TYPE_CASE(assert_type) case assertType::DT_ ## assert_type: return #assert_type + #define DOCTEST_GENERATE_ASSERT_TYPE_CASES(assert_type) \ + DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN_ ## assert_type); \ + DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK_ ## assert_type); \ + DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE_ ## assert_type) + switch(at) { + DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN); + DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK); + DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(FALSE); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_AS); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH_AS); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOTHROW); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(EQ); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(NE); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(GT); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(LT); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(GE); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(LE); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY_FALSE); + + default: DOCTEST_INTERNAL_ERROR("Tried stringifying invalid assert type!"); + } + DOCTEST_MSVC_SUPPRESS_WARNING_POP +} +// clang-format on + +const char* failureString(assertType::Enum at) { + if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional + return "WARNING"; + if(at & assertType::is_check) //!OCLINT bitwise operator in conditional + return "ERROR"; + if(at & assertType::is_require) //!OCLINT bitwise operator in conditional + return "FATAL ERROR"; + return ""; +} + +DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") +DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") +// depending on the current options this will remove the path of filenames +const char* skipPathFromFilename(const char* file) { +#ifndef DOCTEST_CONFIG_DISABLE + if(getContextOptions()->no_path_in_filenames) { + auto back = std::strrchr(file, '\\'); + auto forward = std::strrchr(file, '/'); + if(back || forward) { + if(back > forward) + forward = back; + return forward + 1; + } + } +#endif // DOCTEST_CONFIG_DISABLE + return file; +} +DOCTEST_CLANG_SUPPRESS_WARNING_POP +DOCTEST_GCC_SUPPRESS_WARNING_POP + +bool SubcaseSignature::operator==(const SubcaseSignature& other) const { + return m_line == other.m_line + && std::strcmp(m_file, other.m_file) == 0 + && m_name == other.m_name; +} + +bool SubcaseSignature::operator<(const SubcaseSignature& other) const { + if(m_line != other.m_line) + return m_line < other.m_line; + if(std::strcmp(m_file, other.m_file) != 0) + return std::strcmp(m_file, other.m_file) < 0; + return m_name.compare(other.m_name) < 0; +} + +DOCTEST_DEFINE_INTERFACE(IContextScope) + +namespace detail { + void filldata::fill(std::ostream* stream, const void* in) { + if (in) { *stream << in; } + else { *stream << "nullptr"; } + } + + template + String toStreamLit(T t) { + std::ostream* os = tlssPush(); + os->operator<<(t); + return tlssPop(); + } +} + +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + +#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 +String toString(const std::string& in) { return in.c_str(); } +#endif // VS 2019 + +String toString(String in) { return in; } + +String toString(std::nullptr_t) { return "nullptr"; } + +String toString(bool in) { return in ? "true" : "false"; } + +String toString(float in) { return toStreamLit(in); } +String toString(double in) { return toStreamLit(in); } +String toString(double long in) { return toStreamLit(in); } + +String toString(char in) { return toStreamLit(static_cast(in)); } +String toString(char signed in) { return toStreamLit(static_cast(in)); } +String toString(char unsigned in) { return toStreamLit(static_cast(in)); } +String toString(short in) { return toStreamLit(in); } +String toString(short unsigned in) { return toStreamLit(in); } +String toString(signed in) { return toStreamLit(in); } +String toString(unsigned in) { return toStreamLit(in); } +String toString(long in) { return toStreamLit(in); } +String toString(long unsigned in) { return toStreamLit(in); } +String toString(long long in) { return toStreamLit(in); } +String toString(long long unsigned in) { return toStreamLit(in); } + +Approx::Approx(double value) + : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100) + , m_scale(1.0) + , m_value(value) {} + +Approx Approx::operator()(double value) const { + Approx approx(value); + approx.epsilon(m_epsilon); + approx.scale(m_scale); + return approx; +} + +Approx& Approx::epsilon(double newEpsilon) { + m_epsilon = newEpsilon; + return *this; +} +Approx& Approx::scale(double newScale) { + m_scale = newScale; + return *this; +} + +bool operator==(double lhs, const Approx& rhs) { + // Thanks to Richard Harris for his help refining this formula + return std::fabs(lhs - rhs.m_value) < + rhs.m_epsilon * (rhs.m_scale + std::max(std::fabs(lhs), std::fabs(rhs.m_value))); +} +bool operator==(const Approx& lhs, double rhs) { return operator==(rhs, lhs); } +bool operator!=(double lhs, const Approx& rhs) { return !operator==(lhs, rhs); } +bool operator!=(const Approx& lhs, double rhs) { return !operator==(rhs, lhs); } +bool operator<=(double lhs, const Approx& rhs) { return lhs < rhs.m_value || lhs == rhs; } +bool operator<=(const Approx& lhs, double rhs) { return lhs.m_value < rhs || lhs == rhs; } +bool operator>=(double lhs, const Approx& rhs) { return lhs > rhs.m_value || lhs == rhs; } +bool operator>=(const Approx& lhs, double rhs) { return lhs.m_value > rhs || lhs == rhs; } +bool operator<(double lhs, const Approx& rhs) { return lhs < rhs.m_value && lhs != rhs; } +bool operator<(const Approx& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; } +bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs != rhs; } +bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } + +String toString(const Approx& in) { + return "Approx( " + doctest::toString(in.m_value) + " )"; +} +const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } + +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738) +template +IsNaN::operator bool() const { + return std::isnan(value) ^ flipped; +} +DOCTEST_MSVC_SUPPRESS_WARNING_POP +template struct DOCTEST_INTERFACE_DEF IsNaN; +template struct DOCTEST_INTERFACE_DEF IsNaN; +template struct DOCTEST_INTERFACE_DEF IsNaN; +template +String toString(IsNaN in) { return String(in.flipped ? "! " : "") + "IsNaN( " + doctest::toString(in.value) + " )"; } +String toString(IsNaN in) { return toString(in); } +String toString(IsNaN in) { return toString(in); } +String toString(IsNaN in) { return toString(in); } + +} // namespace doctest + +#ifdef DOCTEST_CONFIG_DISABLE +namespace doctest { +Context::Context(int, const char* const*) {} +Context::~Context() = default; +void Context::applyCommandLine(int, const char* const*) {} +void Context::addFilter(const char*, const char*) {} +void Context::clearFilters() {} +void Context::setOption(const char*, bool) {} +void Context::setOption(const char*, int) {} +void Context::setOption(const char*, const char*) {} +bool Context::shouldExit() { return false; } +void Context::setAsDefaultForAssertsOutOfTestCases() {} +void Context::setAssertHandler(detail::assert_handler) {} +void Context::setCout(std::ostream*) {} +int Context::run() { return 0; } + +int IReporter::get_num_active_contexts() { return 0; } +const IContextScope* const* IReporter::get_active_contexts() { return nullptr; } +int IReporter::get_num_stringified_contexts() { return 0; } +const String* IReporter::get_stringified_contexts() { return nullptr; } + +int registerReporter(const char*, int, IReporter*) { return 0; } + +} // namespace doctest +#else // DOCTEST_CONFIG_DISABLE + +#if !defined(DOCTEST_CONFIG_COLORS_NONE) +#if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI) +#ifdef DOCTEST_PLATFORM_WINDOWS +#define DOCTEST_CONFIG_COLORS_WINDOWS +#else // linux +#define DOCTEST_CONFIG_COLORS_ANSI +#endif // platform +#endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI +#endif // DOCTEST_CONFIG_COLORS_NONE + +namespace doctest_detail_test_suite_ns { +// holds the current test suite +doctest::detail::TestSuite& getCurrentTestSuite() { + static doctest::detail::TestSuite data{}; + return data; +} +} // namespace doctest_detail_test_suite_ns + +namespace doctest { +namespace { + // the int (priority) is part of the key for automatic sorting - sadly one can register a + // reporter with a duplicate name and a different priority but hopefully that won't happen often :| + using reporterMap = std::map, reporterCreatorFunc>; + + reporterMap& getReporters() { + static reporterMap data; + return data; + } + reporterMap& getListeners() { + static reporterMap data; + return data; + } +} // namespace +namespace detail { +#define DOCTEST_ITERATE_THROUGH_REPORTERS(function, ...) \ + for(auto& curr_rep : g_cs->reporters_currently_used) \ + curr_rep->function(__VA_ARGS__) + + bool checkIfShouldThrow(assertType::Enum at) { + if(at & assertType::is_require) //!OCLINT bitwise operator in conditional + return true; + + if((at & assertType::is_check) //!OCLINT bitwise operator in conditional + && getContextOptions()->abort_after > 0 && + (g_cs->numAssertsFailed + g_cs->numAssertsFailedCurrentTest_atomic) >= + getContextOptions()->abort_after) + return true; + + return false; + } + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + DOCTEST_NORETURN void throwException() { + g_cs->shouldLogCurrentException = false; + throw TestFailureException(); // NOLINT(hicpp-exception-baseclass) + } +#else // DOCTEST_CONFIG_NO_EXCEPTIONS + void throwException() {} +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS +} // namespace detail + +namespace { + using namespace detail; + // matching of a string against a wildcard mask (case sensitivity configurable) taken from + // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing + int wildcmp(const char* str, const char* wild, bool caseSensitive) { + const char* cp = str; + const char* mp = wild; + + while((*str) && (*wild != '*')) { + if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && + (*wild != '?')) { + return 0; + } + wild++; + str++; + } + + while(*str) { + if(*wild == '*') { + if(!*++wild) { + return 1; + } + mp = wild; + cp = str + 1; + } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) || + (*wild == '?')) { + wild++; + str++; + } else { + wild = mp; //!OCLINT parameter reassignment + str = cp++; //!OCLINT parameter reassignment + } + } + + while(*wild == '*') { + wild++; + } + return !*wild; + } + + // checks if the name matches any of the filters (and can be configured what to do when empty) + bool matchesAny(const char* name, const std::vector& filters, bool matchEmpty, + bool caseSensitive) { + if (filters.empty() && matchEmpty) + return true; + for (auto& curr : filters) + if (wildcmp(name, curr.c_str(), caseSensitive)) + return true; + return false; + } + + DOCTEST_NO_SANITIZE_INTEGER + unsigned long long hash(unsigned long long a, unsigned long long b) { + return (a << 5) + b; + } + + // C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html + DOCTEST_NO_SANITIZE_INTEGER + unsigned long long hash(const char* str) { + unsigned long long hash = 5381; + char c; + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; // hash * 33 + c + return hash; + } + + unsigned long long hash(const SubcaseSignature& sig) { + return hash(hash(hash(sig.m_file), hash(sig.m_name.c_str())), sig.m_line); + } + + unsigned long long hash(const std::vector& sigs, size_t count) { + unsigned long long running = 0; + auto end = sigs.begin() + count; + for (auto it = sigs.begin(); it != end; it++) { + running = hash(running, hash(*it)); + } + return running; + } + + unsigned long long hash(const std::vector& sigs) { + unsigned long long running = 0; + for (const SubcaseSignature& sig : sigs) { + running = hash(running, hash(sig)); + } + return running; + } +} // namespace +namespace detail { + bool Subcase::checkFilters() { + if (g_cs->subcaseStack.size() < size_t(g_cs->subcase_filter_levels)) { + if (!matchesAny(m_signature.m_name.c_str(), g_cs->filters[6], true, g_cs->case_sensitive)) + return true; + if (matchesAny(m_signature.m_name.c_str(), g_cs->filters[7], false, g_cs->case_sensitive)) + return true; + } + return false; + } + + Subcase::Subcase(const String& name, const char* file, int line) + : m_signature({name, file, line}) { + if (!g_cs->reachedLeaf) { + if (g_cs->nextSubcaseStack.size() <= g_cs->subcaseStack.size() + || g_cs->nextSubcaseStack[g_cs->subcaseStack.size()] == m_signature) { + // Going down. + if (checkFilters()) { return; } + + g_cs->subcaseStack.push_back(m_signature); + g_cs->currentSubcaseDepth++; + m_entered = true; + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } + } else { + if (g_cs->subcaseStack[g_cs->currentSubcaseDepth] == m_signature) { + // This subcase is reentered via control flow. + g_cs->currentSubcaseDepth++; + m_entered = true; + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } else if (g_cs->nextSubcaseStack.size() <= g_cs->currentSubcaseDepth + && g_cs->fullyTraversedSubcases.find(hash(hash(g_cs->subcaseStack, g_cs->currentSubcaseDepth), hash(m_signature))) + == g_cs->fullyTraversedSubcases.end()) { + if (checkFilters()) { return; } + // This subcase is part of the one to be executed next. + g_cs->nextSubcaseStack.clear(); + g_cs->nextSubcaseStack.insert(g_cs->nextSubcaseStack.end(), + g_cs->subcaseStack.begin(), g_cs->subcaseStack.begin() + g_cs->currentSubcaseDepth); + g_cs->nextSubcaseStack.push_back(m_signature); + } + } + } + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + + Subcase::~Subcase() { + if (m_entered) { + g_cs->currentSubcaseDepth--; + + if (!g_cs->reachedLeaf) { + // Leaf. + g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); + g_cs->nextSubcaseStack.clear(); + g_cs->reachedLeaf = true; + } else if (g_cs->nextSubcaseStack.empty()) { + // All children are finished. + g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); + } + +#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) + if(std::uncaught_exceptions() > 0 +#else + if(std::uncaught_exception() +#endif + && g_cs->shouldLogCurrentException) { + DOCTEST_ITERATE_THROUGH_REPORTERS( + test_case_exception, {"exception thrown in subcase - will translate later " + "when the whole test case has been exited (cannot " + "translate while there is an active exception)", + false}); + g_cs->shouldLogCurrentException = false; + } + + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); + } + } + + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + DOCTEST_MSVC_SUPPRESS_WARNING_POP + + Subcase::operator bool() const { return m_entered; } + + Result::Result(bool passed, const String& decomposition) + : m_passed(passed) + , m_decomp(decomposition) {} + + ExpressionDecomposer::ExpressionDecomposer(assertType::Enum at) + : m_at(at) {} + + TestSuite& TestSuite::operator*(const char* in) { + m_test_suite = in; + return *this; + } + + TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, + const String& type, int template_id) { + m_file = file; + m_line = line; + m_name = nullptr; // will be later overridden in operator* + m_test_suite = test_suite.m_test_suite; + m_description = test_suite.m_description; + m_skip = test_suite.m_skip; + m_no_breaks = test_suite.m_no_breaks; + m_no_output = test_suite.m_no_output; + m_may_fail = test_suite.m_may_fail; + m_should_fail = test_suite.m_should_fail; + m_expected_failures = test_suite.m_expected_failures; + m_timeout = test_suite.m_timeout; + + m_test = test; + m_type = type; + m_template_id = template_id; + } + + TestCase::TestCase(const TestCase& other) + : TestCaseData() { + *this = other; + } + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function + TestCase& TestCase::operator=(const TestCase& other) { + TestCaseData::operator=(other); + m_test = other.m_test; + m_type = other.m_type; + m_template_id = other.m_template_id; + m_full_name = other.m_full_name; + + if(m_template_id != -1) + m_name = m_full_name.c_str(); + return *this; + } + DOCTEST_MSVC_SUPPRESS_WARNING_POP + + TestCase& TestCase::operator*(const char* in) { + m_name = in; + // make a new name with an appended type for templated test case + if(m_template_id != -1) { + m_full_name = String(m_name) + "<" + m_type + ">"; + // redirect the name to point to the newly constructed full name + m_name = m_full_name.c_str(); + } + return *this; + } + + bool TestCase::operator<(const TestCase& other) const { + // this will be used only to differentiate between test cases - not relevant for sorting + if(m_line != other.m_line) + return m_line < other.m_line; + const int name_cmp = strcmp(m_name, other.m_name); + if(name_cmp != 0) + return name_cmp < 0; + const int file_cmp = m_file.compare(other.m_file); + if(file_cmp != 0) + return file_cmp < 0; + return m_template_id < other.m_template_id; + } + + // all the registered tests + std::set& getRegisteredTests() { + static std::set data; + return data; + } +} // namespace detail +namespace { + using namespace detail; + // for sorting tests by file/line + bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) { + // this is needed because MSVC gives different case for drive letters + // for __FILE__ when evaluated in a header and a source file + const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC)); + if(res != 0) + return res < 0; + if(lhs->m_line != rhs->m_line) + return lhs->m_line < rhs->m_line; + return lhs->m_template_id < rhs->m_template_id; + } + + // for sorting tests by suite/file/line + bool suiteOrderComparator(const TestCase* lhs, const TestCase* rhs) { + const int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite); + if(res != 0) + return res < 0; + return fileOrderComparator(lhs, rhs); + } + + // for sorting tests by name/suite/file/line + bool nameOrderComparator(const TestCase* lhs, const TestCase* rhs) { + const int res = std::strcmp(lhs->m_name, rhs->m_name); + if(res != 0) + return res < 0; + return suiteOrderComparator(lhs, rhs); + } + + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + void color_to_stream(std::ostream& s, Color::Enum code) { + static_cast(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS + static_cast(code); // for DOCTEST_CONFIG_COLORS_NONE +#ifdef DOCTEST_CONFIG_COLORS_ANSI + if(g_no_colors || + (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) + return; + + auto col = ""; + // clang-format off + switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement + case Color::Red: col = "[0;31m"; break; + case Color::Green: col = "[0;32m"; break; + case Color::Blue: col = "[0;34m"; break; + case Color::Cyan: col = "[0;36m"; break; + case Color::Yellow: col = "[0;33m"; break; + case Color::Grey: col = "[1;30m"; break; + case Color::LightGrey: col = "[0;37m"; break; + case Color::BrightRed: col = "[1;31m"; break; + case Color::BrightGreen: col = "[1;32m"; break; + case Color::BrightWhite: col = "[1;37m"; break; + case Color::Bright: // invalid + case Color::None: + case Color::White: + default: col = "[0m"; + } + // clang-format on + s << "\033" << col; +#endif // DOCTEST_CONFIG_COLORS_ANSI + +#ifdef DOCTEST_CONFIG_COLORS_WINDOWS + if(g_no_colors || + (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false)) + return; + + static struct ConsoleHelper { + HANDLE stdoutHandle; + WORD origFgAttrs; + WORD origBgAttrs; + + ConsoleHelper() { + stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo); + origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | + BACKGROUND_BLUE | BACKGROUND_INTENSITY); + origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | + FOREGROUND_BLUE | FOREGROUND_INTENSITY); + } + } ch; + +#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs) + + // clang-format off + switch (code) { + case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; + case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break; + case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break; + case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break; + case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break; + case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break; + case Color::Grey: DOCTEST_SET_ATTR(0); break; + case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break; + case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break; + case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break; + case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; + case Color::None: + case Color::Bright: // invalid + default: DOCTEST_SET_ATTR(ch.origFgAttrs); + } + // clang-format on +#endif // DOCTEST_CONFIG_COLORS_WINDOWS + } + DOCTEST_CLANG_SUPPRESS_WARNING_POP + + std::vector& getExceptionTranslators() { + static std::vector data; + return data; + } + + String translateActiveException() { +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + String res; + auto& translators = getExceptionTranslators(); + for(auto& curr : translators) + if(curr->translate(res)) + return res; + // clang-format off + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcatch-value") + try { + throw; + } catch(std::exception& ex) { + return ex.what(); + } catch(std::string& msg) { + return msg.c_str(); + } catch(const char* msg) { + return msg; + } catch(...) { + return "unknown exception"; + } + DOCTEST_GCC_SUPPRESS_WARNING_POP +// clang-format on +#else // DOCTEST_CONFIG_NO_EXCEPTIONS + return ""; +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + } +} // namespace + +namespace detail { + // used by the macros for registering tests + int regTest(const TestCase& tc) { + getRegisteredTests().insert(tc); + return 0; + } + + // sets the current test suite + int setTestSuite(const TestSuite& ts) { + doctest_detail_test_suite_ns::getCurrentTestSuite() = ts; + return 0; + } + +#ifdef DOCTEST_IS_DEBUGGER_ACTIVE + bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); } +#else // DOCTEST_IS_DEBUGGER_ACTIVE +#ifdef DOCTEST_PLATFORM_LINUX + class ErrnoGuard { + public: + ErrnoGuard() : m_oldErrno(errno) {} + ~ErrnoGuard() { errno = m_oldErrno; } + private: + int m_oldErrno; + }; + // See the comments in Catch2 for the reasoning behind this implementation: + // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102 + bool isDebuggerActive() { + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for(std::string line; std::getline(in, line);) { + static const int PREFIX_LEN = 11; + if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + return false; + } +#elif defined(DOCTEST_PLATFORM_MAC) + // The following function is taken directly from the following technical note: + // https://developer.apple.com/library/archive/qa/qa1361/_index.html + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive() { + int mib[4]; + kinfo_proc info; + size_t size; + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + info.kp_proc.p_flag = 0; + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + // Call sysctl. + size = sizeof(info); + if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { + std::cerr << "\nCall to sysctl failed - unable to determine if debugger is active **\n"; + return false; + } + // We're being debugged if the P_TRACED flag is set. + return ((info.kp_proc.p_flag & P_TRACED) != 0); + } +#elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__) + bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } +#else + bool isDebuggerActive() { return false; } +#endif // Platform +#endif // DOCTEST_IS_DEBUGGER_ACTIVE + + void registerExceptionTranslatorImpl(const IExceptionTranslator* et) { + if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) == + getExceptionTranslators().end()) + getExceptionTranslators().push_back(et); + } + + DOCTEST_THREAD_LOCAL std::vector g_infoContexts; // for logging with INFO() + + ContextScopeBase::ContextScopeBase() { + g_infoContexts.push_back(this); + } + + ContextScopeBase::ContextScopeBase(ContextScopeBase&& other) noexcept { + if (other.need_to_destroy) { + other.destroy(); + } + other.need_to_destroy = false; + g_infoContexts.push_back(this); + } + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + + // destroy cannot be inlined into the destructor because that would mean calling stringify after + // ContextScope has been destroyed (base class destructors run after derived class destructors). + // Instead, ContextScope calls this method directly from its destructor. + void ContextScopeBase::destroy() { +#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) + if(std::uncaught_exceptions() > 0) { +#else + if(std::uncaught_exception()) { +#endif + std::ostringstream s; + this->stringify(&s); + g_cs->stringifiedContexts.push_back(s.str().c_str()); + } + g_infoContexts.pop_back(); + } + + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + DOCTEST_MSVC_SUPPRESS_WARNING_POP +} // namespace detail +namespace { + using namespace detail; + +#if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) + struct FatalConditionHandler + { + static void reset() {} + static void allocateAltStackMem() {} + static void freeAltStackMem() {} + }; +#else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH + + void reportFatal(const std::string&); + +#ifdef DOCTEST_PLATFORM_WINDOWS + + struct SignalDefs + { + DWORD id; + const char* name; + }; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { + {static_cast(EXCEPTION_ILLEGAL_INSTRUCTION), + "SIGILL - Illegal instruction signal"}, + {static_cast(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"}, + {static_cast(EXCEPTION_ACCESS_VIOLATION), + "SIGSEGV - Segmentation violation signal"}, + {static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"}, + }; + + struct FatalConditionHandler + { + static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) { + // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the + // console just once no matter how many threads have crashed. + DOCTEST_DECLARE_STATIC_MUTEX(mutex) + static bool execute = true; + { + DOCTEST_LOCK_MUTEX(mutex) + if(execute) { + bool reported = false; + for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { + if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reportFatal(signalDefs[i].name); + reported = true; + break; + } + } + if(reported == false) + reportFatal("Unhandled SEH exception caught"); + if(isDebuggerActive() && !g_cs->no_breaks) + DOCTEST_BREAK_INTO_DEBUGGER(); + } + execute = false; + } + std::exit(EXIT_FAILURE); + } + + static void allocateAltStackMem() {} + static void freeAltStackMem() {} + + FatalConditionHandler() { + isSet = true; + // 32k seems enough for doctest to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + // Register an unhandled exception filter + previousTop = SetUnhandledExceptionFilter(handleException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + + // On Windows uncaught exceptions from another thread, exceptions from + // destructors, or calls to std::terminate are not a SEH exception + + // The terminal handler gets called when: + // - std::terminate is called FROM THE TEST RUNNER THREAD + // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD + original_terminate_handler = std::get_terminate(); + std::set_terminate([]() DOCTEST_NOEXCEPT { + reportFatal("Terminate handler called"); + if(isDebuggerActive() && !g_cs->no_breaks) + DOCTEST_BREAK_INTO_DEBUGGER(); + std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well + }); + + // SIGABRT is raised when: + // - std::terminate is called FROM A DIFFERENT THREAD + // - an exception is thrown from a destructor FROM A DIFFERENT THREAD + // - an uncaught exception is thrown FROM A DIFFERENT THREAD + prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT { + if(signal == SIGABRT) { + reportFatal("SIGABRT - Abort (abnormal termination) signal"); + if(isDebuggerActive() && !g_cs->no_breaks) + DOCTEST_BREAK_INTO_DEBUGGER(); + std::exit(EXIT_FAILURE); + } + }); + + // The following settings are taken from google test, and more + // specifically from UnitTest::Run() inside of gtest.cc + + // the user does not want to see pop-up dialogs about crashes + prev_error_mode_1 = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + // This forces the abort message to go to stderr in all circumstances. + prev_error_mode_2 = _set_error_mode(_OUT_TO_STDERR); + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program - we want to disable that. + prev_abort_behavior = _set_abort_behavior(0x0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + // In debug mode, the Windows CRT can crash with an assertion over invalid + // input (e.g. passing an invalid file descriptor). The default handling + // for these assertions is to pop up a dialog and wait for user input. + // Instead ask the CRT to dump such assertions to stderr non-interactively. + prev_report_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + prev_report_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + } + + static void reset() { + if(isSet) { + // Unregister handler and restore the old guarantee + SetUnhandledExceptionFilter(previousTop); + SetThreadStackGuarantee(&guaranteeSize); + std::set_terminate(original_terminate_handler); + std::signal(SIGABRT, prev_sigabrt_handler); + SetErrorMode(prev_error_mode_1); + _set_error_mode(prev_error_mode_2); + _set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + static_cast(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode)); + static_cast(_CrtSetReportFile(_CRT_ASSERT, prev_report_file)); + isSet = false; + } + } + + ~FatalConditionHandler() { reset(); } + + private: + static UINT prev_error_mode_1; + static int prev_error_mode_2; + static unsigned int prev_abort_behavior; + static int prev_report_mode; + static _HFILE prev_report_file; + static void (DOCTEST_CDECL *prev_sigabrt_handler)(int); + static std::terminate_handler original_terminate_handler; + static bool isSet; + static ULONG guaranteeSize; + static LPTOP_LEVEL_EXCEPTION_FILTER previousTop; + }; + + UINT FatalConditionHandler::prev_error_mode_1; + int FatalConditionHandler::prev_error_mode_2; + unsigned int FatalConditionHandler::prev_abort_behavior; + int FatalConditionHandler::prev_report_mode; + _HFILE FatalConditionHandler::prev_report_file; + void (DOCTEST_CDECL *FatalConditionHandler::prev_sigabrt_handler)(int); + std::terminate_handler FatalConditionHandler::original_terminate_handler; + bool FatalConditionHandler::isSet = false; + ULONG FatalConditionHandler::guaranteeSize = 0; + LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr; + +#else // DOCTEST_PLATFORM_WINDOWS + + struct SignalDefs + { + int id; + const char* name; + }; + SignalDefs signalDefs[] = {{SIGINT, "SIGINT - Terminal interrupt signal"}, + {SIGILL, "SIGILL - Illegal instruction signal"}, + {SIGFPE, "SIGFPE - Floating point error signal"}, + {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, + {SIGTERM, "SIGTERM - Termination request signal"}, + {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; + + struct FatalConditionHandler + { + static bool isSet; + static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)]; + static stack_t oldSigStack; + static size_t altStackSize; + static char* altStackMem; + + static void handleSignal(int sig) { + const char* name = ""; + for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { + SignalDefs& def = signalDefs[i]; + if(sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise(sig); + } + + static void allocateAltStackMem() { + altStackMem = new char[altStackSize]; + } + + static void freeAltStackMem() { + delete[] altStackMem; + } + + FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = altStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = {}; + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + ~FatalConditionHandler() { reset(); } + static void reset() { + if(isSet) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + }; + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + size_t FatalConditionHandler::altStackSize = 4 * SIGSTKSZ; + char* FatalConditionHandler::altStackMem = nullptr; + +#endif // DOCTEST_PLATFORM_WINDOWS +#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH + +} // namespace + +namespace { + using namespace detail; + +#ifdef DOCTEST_PLATFORM_WINDOWS +#define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text) +#else + // TODO: integration with XCode and other IDEs +#define DOCTEST_OUTPUT_DEBUG_STRING(text) +#endif // Platform + + void addAssert(assertType::Enum at) { + if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional + g_cs->numAssertsCurrentTest_atomic++; + } + + void addFailedAssert(assertType::Enum at) { + if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional + g_cs->numAssertsFailedCurrentTest_atomic++; + } + +#if defined(DOCTEST_CONFIG_POSIX_SIGNALS) || defined(DOCTEST_CONFIG_WINDOWS_SEH) + void reportFatal(const std::string& message) { + g_cs->failure_flags |= TestCaseFailureReason::Crash; + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true}); + + while (g_cs->subcaseStack.size()) { + g_cs->subcaseStack.pop_back(); + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); + } + + g_cs->finalizeTestCaseData(); + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); + } +#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH +} // namespace + +AssertData::AssertData(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type, const StringContains& exception_string) + : m_test_case(g_cs->currentTest), m_at(at), m_file(file), m_line(line), m_expr(expr), + m_failed(true), m_threw(false), m_threw_as(false), m_exception_type(exception_type), + m_exception_string(exception_string) { +#if DOCTEST_MSVC + if (m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC + ++m_expr; +#endif // MSVC +} + +namespace detail { + ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type, const String& exception_string) + : AssertData(at, file, line, expr, exception_type, exception_string) { } + + ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type, const Contains& exception_string) + : AssertData(at, file, line, expr, exception_type, exception_string) { } + + void ResultBuilder::setResult(const Result& res) { + m_decomp = res.m_decomp; + m_failed = !res.m_passed; + } + + void ResultBuilder::translateException() { + m_threw = true; + m_exception = translateActiveException(); + } + + bool ResultBuilder::log() { + if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional + m_failed = !m_threw; + } else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT + m_failed = !m_threw_as || !m_exception_string.check(m_exception); + } else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional + m_failed = !m_threw_as; + } else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional + m_failed = !m_exception_string.check(m_exception); + } else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional + m_failed = m_threw; + } + + if(m_exception.size()) + m_exception = "\"" + m_exception + "\""; + + if(is_running_in_test) { + addAssert(m_at); + DOCTEST_ITERATE_THROUGH_REPORTERS(log_assert, *this); + + if(m_failed) + addFailedAssert(m_at); + } else if(m_failed) { + failed_out_of_a_testing_context(*this); + } + + return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks && + (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger + } + + void ResultBuilder::react() const { + if(m_failed && checkIfShouldThrow(m_at)) + throwException(); + } + + void failed_out_of_a_testing_context(const AssertData& ad) { + if(g_cs->ah) + g_cs->ah(ad); + else + std::abort(); + } + + bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, + const Result& result) { + bool failed = !result.m_passed; + + // ################################################################################### + // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT + // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED + // ################################################################################### + DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp); + DOCTEST_ASSERT_IN_TESTS(result.m_decomp); + return !failed; + } + + MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) { + m_stream = tlssPush(); + m_file = file; + m_line = line; + m_severity = severity; + } + + MessageBuilder::~MessageBuilder() { + if (!logged) + tlssPop(); + } + + DOCTEST_DEFINE_INTERFACE(IExceptionTranslator) + + bool MessageBuilder::log() { + if (!logged) { + m_string = tlssPop(); + logged = true; + } + + DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this); + + const bool isWarn = m_severity & assertType::is_warn; + + // warn is just a message in this context so we don't treat it as an assert + if(!isWarn) { + addAssert(m_severity); + addFailedAssert(m_severity); + } + + return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn && + (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger + } + + void MessageBuilder::react() { + if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional + throwException(); + } +} // namespace detail +namespace { + using namespace detail; + + // clang-format off + +// ================================================================================================= +// The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp +// This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. +// ================================================================================================= + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); + + ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT; + ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + }; + +#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + XmlWriter( std::ostream& os = std::cout ); +#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + XmlWriter( std::ostream& os ); +#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, const char* attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + std::stringstream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ); + + //XmlWriter& writeComment( std::string const& text ); + + //void writeStylesheetRef( std::string const& url ); + + //XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + void writeDeclaration(); + + private: + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +// ================================================================================================= +// The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp +// This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. +// ================================================================================================= + +using uchar = unsigned char; + +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + os.flags(f); + } + +} // anonymous namespace + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: https://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + uchar c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: https://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } + + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } + + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + uchar nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + ( value < 0x800 && encBytes > 2) || // removed "0x80 <= value &&" because redundant + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } + + + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + // writeDeclaration(); // called explicitly by the reporters that use the writer class - see issue #627 + } + + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, const char* attribute ) { + if( !name.empty() && attribute && attribute[0] != '\0' ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + //XmlWriter& XmlWriter::writeComment( std::string const& text ) { + // ensureTagClosed(); + // m_os << m_indent << ""; + // m_needsNewline = true; + // return *this; + //} + + //void XmlWriter::writeStylesheetRef( std::string const& url ) { + // m_os << "\n"; + //} + + //XmlWriter& XmlWriter::writeBlankLine() { + // ensureTagClosed(); + // m_os << '\n'; + // return *this; + //} + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } + +// ================================================================================================= +// End of copy-pasted code from Catch +// ================================================================================================= + + // clang-format on + + struct XmlReporter : public IReporter + { + XmlWriter xml; + DOCTEST_DECLARE_MUTEX(mutex) + + // caching pointers/references to objects of these types - safe to do + const ContextOptions& opt; + const TestCaseData* tc = nullptr; + + XmlReporter(const ContextOptions& co) + : xml(*co.cout) + , opt(co) {} + + void log_contexts() { + int num_contexts = get_num_active_contexts(); + if(num_contexts) { + auto contexts = get_active_contexts(); + std::stringstream ss; + for(int i = 0; i < num_contexts; ++i) { + contexts[i]->stringify(&ss); + xml.scopedElement("Info").writeText(ss.str()); + ss.str(""); + } + } + } + + unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } + + void test_case_start_impl(const TestCaseData& in) { + bool open_ts_tag = false; + if(tc != nullptr) { // we have already opened a test suite + if(std::strcmp(tc->m_test_suite, in.m_test_suite) != 0) { + xml.endElement(); + open_ts_tag = true; + } + } + else { + open_ts_tag = true; // first test case ==> first test suite + } + + if(open_ts_tag) { + xml.startElement("TestSuite"); + xml.writeAttribute("name", in.m_test_suite); + } + + tc = ∈ + xml.startElement("TestCase") + .writeAttribute("name", in.m_name) + .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str())) + .writeAttribute("line", line(in.m_line)) + .writeAttribute("description", in.m_description); + + if(Approx(in.m_timeout) != 0) + xml.writeAttribute("timeout", in.m_timeout); + if(in.m_may_fail) + xml.writeAttribute("may_fail", true); + if(in.m_should_fail) + xml.writeAttribute("should_fail", true); + } + + // ========================================================================================= + // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE + // ========================================================================================= + + void report_query(const QueryData& in) override { + test_run_start(); + if(opt.list_reporters) { + for(auto& curr : getListeners()) + xml.scopedElement("Listener") + .writeAttribute("priority", curr.first.first) + .writeAttribute("name", curr.first.second); + for(auto& curr : getReporters()) + xml.scopedElement("Reporter") + .writeAttribute("priority", curr.first.first) + .writeAttribute("name", curr.first.second); + } else if(opt.count || opt.list_test_cases) { + for(unsigned i = 0; i < in.num_data; ++i) { + xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name) + .writeAttribute("testsuite", in.data[i]->m_test_suite) + .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str())) + .writeAttribute("line", line(in.data[i]->m_line)) + .writeAttribute("skipped", in.data[i]->m_skip); + } + xml.scopedElement("OverallResultsTestCases") + .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); + } else if(opt.list_test_suites) { + for(unsigned i = 0; i < in.num_data; ++i) + xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]->m_test_suite); + xml.scopedElement("OverallResultsTestCases") + .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); + xml.scopedElement("OverallResultsTestSuites") + .writeAttribute("unskipped", in.run_stats->numTestSuitesPassingFilters); + } + xml.endElement(); + } + + void test_run_start() override { + xml.writeDeclaration(); + + // remove .exe extension - mainly to have the same output on UNIX and Windows + std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); +#ifdef DOCTEST_PLATFORM_WINDOWS + if(binary_name.rfind(".exe") != std::string::npos) + binary_name = binary_name.substr(0, binary_name.length() - 4); +#endif // DOCTEST_PLATFORM_WINDOWS + + xml.startElement("doctest").writeAttribute("binary", binary_name); + if(opt.no_version == false) + xml.writeAttribute("version", DOCTEST_VERSION_STR); + + // only the consequential ones (TODO: filters) + xml.scopedElement("Options") + .writeAttribute("order_by", opt.order_by.c_str()) + .writeAttribute("rand_seed", opt.rand_seed) + .writeAttribute("first", opt.first) + .writeAttribute("last", opt.last) + .writeAttribute("abort_after", opt.abort_after) + .writeAttribute("subcase_filter_levels", opt.subcase_filter_levels) + .writeAttribute("case_sensitive", opt.case_sensitive) + .writeAttribute("no_throw", opt.no_throw) + .writeAttribute("no_skip", opt.no_skip); + } + + void test_run_end(const TestRunStats& p) override { + if(tc) // the TestSuite tag - only if there has been at least 1 test case + xml.endElement(); + + xml.scopedElement("OverallResultsAsserts") + .writeAttribute("successes", p.numAsserts - p.numAssertsFailed) + .writeAttribute("failures", p.numAssertsFailed); + + xml.startElement("OverallResultsTestCases") + .writeAttribute("successes", + p.numTestCasesPassingFilters - p.numTestCasesFailed) + .writeAttribute("failures", p.numTestCasesFailed); + if(opt.no_skipped_summary == false) + xml.writeAttribute("skipped", p.numTestCases - p.numTestCasesPassingFilters); + xml.endElement(); + + xml.endElement(); + } + + void test_case_start(const TestCaseData& in) override { + test_case_start_impl(in); + xml.ensureTagClosed(); + } + + void test_case_reenter(const TestCaseData&) override {} + + void test_case_end(const CurrentTestCaseStats& st) override { + xml.startElement("OverallResultsAsserts") + .writeAttribute("successes", + st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest) + .writeAttribute("failures", st.numAssertsFailedCurrentTest) + .writeAttribute("test_case_success", st.testCaseSuccess); + if(opt.duration) + xml.writeAttribute("duration", st.seconds); + if(tc->m_expected_failures) + xml.writeAttribute("expected_failures", tc->m_expected_failures); + xml.endElement(); + + xml.endElement(); + } + + void test_case_exception(const TestCaseException& e) override { + DOCTEST_LOCK_MUTEX(mutex) + + xml.scopedElement("Exception") + .writeAttribute("crash", e.is_crash) + .writeText(e.error_string.c_str()); + } + + void subcase_start(const SubcaseSignature& in) override { + xml.startElement("SubCase") + .writeAttribute("name", in.m_name) + .writeAttribute("filename", skipPathFromFilename(in.m_file)) + .writeAttribute("line", line(in.m_line)); + xml.ensureTagClosed(); + } + + void subcase_end() override { xml.endElement(); } + + void log_assert(const AssertData& rb) override { + if(!rb.m_failed && !opt.success) + return; + + DOCTEST_LOCK_MUTEX(mutex) + + xml.startElement("Expression") + .writeAttribute("success", !rb.m_failed) + .writeAttribute("type", assertString(rb.m_at)) + .writeAttribute("filename", skipPathFromFilename(rb.m_file)) + .writeAttribute("line", line(rb.m_line)); + + xml.scopedElement("Original").writeText(rb.m_expr); + + if(rb.m_threw) + xml.scopedElement("Exception").writeText(rb.m_exception.c_str()); + + if(rb.m_at & assertType::is_throws_as) + xml.scopedElement("ExpectedException").writeText(rb.m_exception_type); + if(rb.m_at & assertType::is_throws_with) + xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string.c_str()); + if((rb.m_at & assertType::is_normal) && !rb.m_threw) + xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str()); + + log_contexts(); + + xml.endElement(); + } + + void log_message(const MessageData& mb) override { + DOCTEST_LOCK_MUTEX(mutex) + + xml.startElement("Message") + .writeAttribute("type", failureString(mb.m_severity)) + .writeAttribute("filename", skipPathFromFilename(mb.m_file)) + .writeAttribute("line", line(mb.m_line)); + + xml.scopedElement("Text").writeText(mb.m_string.c_str()); + + log_contexts(); + + xml.endElement(); + } + + void test_case_skipped(const TestCaseData& in) override { + if(opt.no_skipped_summary == false) { + test_case_start_impl(in); + xml.writeAttribute("skipped", "true"); + xml.endElement(); + } + } + }; + + DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter); + + void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) { + if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) == + 0) //!OCLINT bitwise operator in conditional + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " + << Color::None; + + if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional + s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; + } else if((rb.m_at & assertType::is_throws_as) && + (rb.m_at & assertType::is_throws_with)) { //!OCLINT + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" + << rb.m_exception_string.c_str() + << "\", " << rb.m_exception_type << " ) " << Color::None; + if(rb.m_threw) { + if(!rb.m_failed) { + s << "threw as expected!\n"; + } else { + s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n"; + } + } else { + s << "did NOT throw at all!\n"; + } + } else if(rb.m_at & + assertType::is_throws_as) { //!OCLINT bitwise operator in conditional + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " + << rb.m_exception_type << " ) " << Color::None + << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" : + "threw a DIFFERENT exception: ") : + "did NOT throw at all!") + << Color::Cyan << rb.m_exception << "\n"; + } else if(rb.m_at & + assertType::is_throws_with) { //!OCLINT bitwise operator in conditional + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" + << rb.m_exception_string.c_str() + << "\" ) " << Color::None + << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : + "threw a DIFFERENT exception: ") : + "did NOT throw at all!") + << Color::Cyan << rb.m_exception << "\n"; + } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional + s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan + << rb.m_exception << "\n"; + } else { + s << (rb.m_threw ? "THREW exception: " : + (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); + if(rb.m_threw) + s << rb.m_exception << "\n"; + else + s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; + } + } + + // TODO: + // - log_message() + // - respond to queries + // - honor remaining options + // - more attributes in tags + struct JUnitReporter : public IReporter + { + XmlWriter xml; + DOCTEST_DECLARE_MUTEX(mutex) + Timer timer; + std::vector deepestSubcaseStackNames; + + struct JUnitTestCaseData + { + static std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + + std::tm timeInfo; +#ifdef DOCTEST_PLATFORM_WINDOWS + gmtime_s(&timeInfo, &rawtime); +#else // DOCTEST_PLATFORM_WINDOWS + gmtime_r(&rawtime, &timeInfo); +#endif // DOCTEST_PLATFORM_WINDOWS + + char timeStamp[timeStampSize]; + const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; + + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); + return std::string(timeStamp); + } + + struct JUnitTestMessage + { + JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details) + : message(_message), type(_type), details(_details) {} + + JUnitTestMessage(const std::string& _message, const std::string& _details) + : message(_message), type(), details(_details) {} + + std::string message, type, details; + }; + + struct JUnitTestCase + { + JUnitTestCase(const std::string& _classname, const std::string& _name) + : classname(_classname), name(_name), time(0), failures() {} + + std::string classname, name; + double time; + std::vector failures, errors; + }; + + void add(const std::string& classname, const std::string& name) { + testcases.emplace_back(classname, name); + } + + void appendSubcaseNamesToLastTestcase(std::vector nameStack) { + for(auto& curr: nameStack) + if(curr.size()) + testcases.back().name += std::string("/") + curr.c_str(); + } + + void addTime(double time) { + if(time < 1e-4) + time = 0; + testcases.back().time = time; + totalSeconds += time; + } + + void addFailure(const std::string& message, const std::string& type, const std::string& details) { + testcases.back().failures.emplace_back(message, type, details); + ++totalFailures; + } + + void addError(const std::string& message, const std::string& details) { + testcases.back().errors.emplace_back(message, details); + ++totalErrors; + } + + std::vector testcases; + double totalSeconds = 0; + int totalErrors = 0, totalFailures = 0; + }; + + JUnitTestCaseData testCaseData; + + // caching pointers/references to objects of these types - safe to do + const ContextOptions& opt; + const TestCaseData* tc = nullptr; + + JUnitReporter(const ContextOptions& co) + : xml(*co.cout) + , opt(co) {} + + unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } + + // ========================================================================================= + // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE + // ========================================================================================= + + void report_query(const QueryData&) override { + xml.writeDeclaration(); + } + + void test_run_start() override { + xml.writeDeclaration(); + } + + void test_run_end(const TestRunStats& p) override { + // remove .exe extension - mainly to have the same output on UNIX and Windows + std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); +#ifdef DOCTEST_PLATFORM_WINDOWS + if(binary_name.rfind(".exe") != std::string::npos) + binary_name = binary_name.substr(0, binary_name.length() - 4); +#endif // DOCTEST_PLATFORM_WINDOWS + xml.startElement("testsuites"); + xml.startElement("testsuite").writeAttribute("name", binary_name) + .writeAttribute("errors", testCaseData.totalErrors) + .writeAttribute("failures", testCaseData.totalFailures) + .writeAttribute("tests", p.numAsserts); + if(opt.no_time_in_output == false) { + xml.writeAttribute("time", testCaseData.totalSeconds); + xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp()); + } + if(opt.no_version == false) + xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR); + + for(const auto& testCase : testCaseData.testcases) { + xml.startElement("testcase") + .writeAttribute("classname", testCase.classname) + .writeAttribute("name", testCase.name); + if(opt.no_time_in_output == false) + xml.writeAttribute("time", testCase.time); + // This is not ideal, but it should be enough to mimic gtest's junit output. + xml.writeAttribute("status", "run"); + + for(const auto& failure : testCase.failures) { + xml.scopedElement("failure") + .writeAttribute("message", failure.message) + .writeAttribute("type", failure.type) + .writeText(failure.details, false); + } + + for(const auto& error : testCase.errors) { + xml.scopedElement("error") + .writeAttribute("message", error.message) + .writeText(error.details); + } + + xml.endElement(); + } + xml.endElement(); + xml.endElement(); + } + + void test_case_start(const TestCaseData& in) override { + testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); + timer.start(); + } + + void test_case_reenter(const TestCaseData& in) override { + testCaseData.addTime(timer.getElapsedSeconds()); + testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); + deepestSubcaseStackNames.clear(); + + timer.start(); + testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); + } + + void test_case_end(const CurrentTestCaseStats&) override { + testCaseData.addTime(timer.getElapsedSeconds()); + testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); + deepestSubcaseStackNames.clear(); + } + + void test_case_exception(const TestCaseException& e) override { + DOCTEST_LOCK_MUTEX(mutex) + testCaseData.addError("exception", e.error_string.c_str()); + } + + void subcase_start(const SubcaseSignature& in) override { + deepestSubcaseStackNames.push_back(in.m_name); + } + + void subcase_end() override {} + + void log_assert(const AssertData& rb) override { + if(!rb.m_failed) // report only failures & ignore the `success` option + return; + + DOCTEST_LOCK_MUTEX(mutex) + + std::ostringstream os; + os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(") + << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; + + fulltext_log_assert_to_stream(os, rb); + log_contexts(os); + testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str()); + } + + void log_message(const MessageData& mb) override { + if(mb.m_severity & assertType::is_warn) // report only failures + return; + + DOCTEST_LOCK_MUTEX(mutex) + + std::ostringstream os; + os << skipPathFromFilename(mb.m_file) << (opt.gnu_file_line ? ":" : "(") + << line(mb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; + + os << mb.m_string.c_str() << "\n"; + log_contexts(os); + + testCaseData.addFailure(mb.m_string.c_str(), + mb.m_severity & assertType::is_check ? "FAIL_CHECK" : "FAIL", os.str()); + } + + void test_case_skipped(const TestCaseData&) override {} + + void log_contexts(std::ostringstream& s) { + int num_contexts = get_num_active_contexts(); + if(num_contexts) { + auto contexts = get_active_contexts(); + + s << " logged: "; + for(int i = 0; i < num_contexts; ++i) { + s << (i == 0 ? "" : " "); + contexts[i]->stringify(&s); + s << std::endl; + } + } + } + }; + + DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter); + + struct Whitespace + { + int nrSpaces; + explicit Whitespace(int nr) + : nrSpaces(nr) {} + }; + + std::ostream& operator<<(std::ostream& out, const Whitespace& ws) { + if(ws.nrSpaces != 0) + out << std::setw(ws.nrSpaces) << ' '; + return out; + } + + struct ConsoleReporter : public IReporter + { + std::ostream& s; + bool hasLoggedCurrentTestStart; + std::vector subcasesStack; + size_t currentSubcaseLevel; + DOCTEST_DECLARE_MUTEX(mutex) + + // caching pointers/references to objects of these types - safe to do + const ContextOptions& opt; + const TestCaseData* tc; + + ConsoleReporter(const ContextOptions& co) + : s(*co.cout) + , opt(co) {} + + ConsoleReporter(const ContextOptions& co, std::ostream& ostr) + : s(ostr) + , opt(co) {} + + // ========================================================================================= + // WHAT FOLLOWS ARE HELPERS USED BY THE OVERRIDES OF THE VIRTUAL METHODS OF THE INTERFACE + // ========================================================================================= + + void separator_to_stream() { + s << Color::Yellow + << "===============================================================================" + "\n"; + } + + const char* getSuccessOrFailString(bool success, assertType::Enum at, + const char* success_str) { + if(success) + return success_str; + return failureString(at); + } + + Color::Enum getSuccessOrFailColor(bool success, assertType::Enum at) { + return success ? Color::BrightGreen : + (at & assertType::is_warn) ? Color::Yellow : Color::Red; + } + + void successOrFailColoredStringToStream(bool success, assertType::Enum at, + const char* success_str = "SUCCESS") { + s << getSuccessOrFailColor(success, at) + << getSuccessOrFailString(success, at, success_str) << ": "; + } + + void log_contexts() { + int num_contexts = get_num_active_contexts(); + if(num_contexts) { + auto contexts = get_active_contexts(); + + s << Color::None << " logged: "; + for(int i = 0; i < num_contexts; ++i) { + s << (i == 0 ? "" : " "); + contexts[i]->stringify(&s); + s << "\n"; + } + } + + s << "\n"; + } + + // this was requested to be made virtual so users could override it + virtual void file_line_to_stream(const char* file, int line, + const char* tail = "") { + s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(") + << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option + << (opt.gnu_file_line ? ":" : "):") << tail; + } + + void logTestStart() { + if(hasLoggedCurrentTestStart) + return; + + separator_to_stream(); + file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n"); + if(tc->m_description) + s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n"; + if(tc->m_test_suite && tc->m_test_suite[0] != '\0') + s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n"; + if(strncmp(tc->m_name, " Scenario:", 11) != 0) + s << Color::Yellow << "TEST CASE: "; + s << Color::None << tc->m_name << "\n"; + + for(size_t i = 0; i < currentSubcaseLevel; ++i) { + if(subcasesStack[i].m_name[0] != '\0') + s << " " << subcasesStack[i].m_name << "\n"; + } + + if(currentSubcaseLevel != subcasesStack.size()) { + s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None; + for(size_t i = 0; i < subcasesStack.size(); ++i) { + if(subcasesStack[i].m_name[0] != '\0') + s << " " << subcasesStack[i].m_name << "\n"; + } + } + + s << "\n"; + + hasLoggedCurrentTestStart = true; + } + + void printVersion() { + if(opt.no_version == false) + s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \"" + << DOCTEST_VERSION_STR << "\"\n"; + } + + void printIntro() { + if(opt.no_intro == false) { + printVersion(); + s << Color::Cyan << "[doctest] " << Color::None + << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n"; + } + } + + void printHelp() { + int sizePrefixDisplay = static_cast(strlen(DOCTEST_OPTIONS_PREFIX_DISPLAY)); + printVersion(); + // clang-format off + s << Color::Cyan << "[doctest]\n" << Color::None; + s << Color::Cyan << "[doctest] " << Color::None; + s << "boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n"; + s << Color::Cyan << "[doctest] " << Color::None; + s << "filter values: \"str1,str2,str3\" (comma separated strings)\n"; + s << Color::Cyan << "[doctest]\n" << Color::None; + s << Color::Cyan << "[doctest] " << Color::None; + s << "filters use wildcards for matching strings\n"; + s << Color::Cyan << "[doctest] " << Color::None; + s << "something passes a filter if any of the strings in a filter matches\n"; +#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + s << Color::Cyan << "[doctest]\n" << Color::None; + s << Color::Cyan << "[doctest] " << Color::None; + s << "ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"" DOCTEST_CONFIG_OPTIONS_PREFIX "\" PREFIX!!!\n"; +#endif + s << Color::Cyan << "[doctest]\n" << Color::None; + s << Color::Cyan << "[doctest] " << Color::None; + s << "Query flags - the program quits after them. Available:\n\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "?, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "help, -" DOCTEST_OPTIONS_PREFIX_DISPLAY "h " + << Whitespace(sizePrefixDisplay*0) << "prints this message\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "v, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "version " + << Whitespace(sizePrefixDisplay*1) << "prints the version\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "c, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "count " + << Whitespace(sizePrefixDisplay*1) << "prints the number of matching tests\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ltc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-cases " + << Whitespace(sizePrefixDisplay*1) << "lists all matching tests by name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-suites " + << Whitespace(sizePrefixDisplay*1) << "lists all matching test suites\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-reporters " + << Whitespace(sizePrefixDisplay*1) << "lists all registered reporters\n\n"; + // ================================================================================== << 79 + s << Color::Cyan << "[doctest] " << Color::None; + s << "The available / options/filters are:\n\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case= " + << Whitespace(sizePrefixDisplay*1) << "filters tests by their name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case-exclude= " + << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file= " + << Whitespace(sizePrefixDisplay*1) << "filters tests by their file\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sfe, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file-exclude= " + << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their file\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite= " + << Whitespace(sizePrefixDisplay*1) << "filters tests by their test suite\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tse, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite-exclude= " + << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their test suite\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase= " + << Whitespace(sizePrefixDisplay*1) << "filters subcases by their name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-exclude= " + << Whitespace(sizePrefixDisplay*1) << "filters OUT subcases by their name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "r, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "reporters= " + << Whitespace(sizePrefixDisplay*1) << "reporters to use (console is default)\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "o, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "out= " + << Whitespace(sizePrefixDisplay*1) << "output filename\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by= " + << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n"; + s << Whitespace(sizePrefixDisplay*3) << " - [file/suite/name/rand/none]\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed= " + << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first= " + << Whitespace(sizePrefixDisplay*1) << "the first test passing the filters to\n"; + s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "l, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "last= " + << Whitespace(sizePrefixDisplay*1) << "the last test passing the filters to\n"; + s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "aa, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "abort-after= " + << Whitespace(sizePrefixDisplay*1) << "stop after failed assertions\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "scfl,--" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-filter-levels= " + << Whitespace(sizePrefixDisplay*1) << "apply filters for the first levels\n"; + s << Color::Cyan << "\n[doctest] " << Color::None; + s << "Bool options - can be used like flags and true is assumed. Available:\n\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "s, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "success= " + << Whitespace(sizePrefixDisplay*1) << "include successful assertions in output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "cs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "case-sensitive= " + << Whitespace(sizePrefixDisplay*1) << "filters being treated as case sensitive\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "e, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "exit= " + << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration= " + << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal= " + << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet= " + << Whitespace(sizePrefixDisplay*1) << "no console output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw= " + << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode= " + << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run= " + << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro= " + << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version= " + << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors= " + << Whitespace(sizePrefixDisplay*1) << "disables colors in output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "fc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "force-colors= " + << Whitespace(sizePrefixDisplay*1) << "use colors even when not in a tty\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nb, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-breaks= " + << Whitespace(sizePrefixDisplay*1) << "disables breakpoints in debuggers\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ns, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-skip= " + << Whitespace(sizePrefixDisplay*1) << "don't skip test cases marked as skip\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "gfl, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "gnu-file-line= " + << Whitespace(sizePrefixDisplay*1) << ":n: vs (n): for line numbers in output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "npf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-path-filenames= " + << Whitespace(sizePrefixDisplay*1) << "only filenames and no paths in output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nln, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-line-numbers= " + << Whitespace(sizePrefixDisplay*1) << "0 instead of real line numbers in output\n"; + // ================================================================================== << 79 + // clang-format on + + s << Color::Cyan << "\n[doctest] " << Color::None; + s << "for more information visit the project documentation\n\n"; + } + + void printRegisteredReporters() { + printVersion(); + auto printReporters = [this] (const reporterMap& reporters, const char* type) { + if(reporters.size()) { + s << Color::Cyan << "[doctest] " << Color::None << "listing all registered " << type << "\n"; + for(auto& curr : reporters) + s << "priority: " << std::setw(5) << curr.first.first + << " name: " << curr.first.second << "\n"; + } + }; + printReporters(getListeners(), "listeners"); + printReporters(getReporters(), "reporters"); + } + + // ========================================================================================= + // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE + // ========================================================================================= + + void report_query(const QueryData& in) override { + if(opt.version) { + printVersion(); + } else if(opt.help) { + printHelp(); + } else if(opt.list_reporters) { + printRegisteredReporters(); + } else if(opt.count || opt.list_test_cases) { + if(opt.list_test_cases) { + s << Color::Cyan << "[doctest] " << Color::None + << "listing all test case names\n"; + separator_to_stream(); + } + + for(unsigned i = 0; i < in.num_data; ++i) + s << Color::None << in.data[i]->m_name << "\n"; + + separator_to_stream(); + + s << Color::Cyan << "[doctest] " << Color::None + << "unskipped test cases passing the current filters: " + << g_cs->numTestCasesPassingFilters << "\n"; + + } else if(opt.list_test_suites) { + s << Color::Cyan << "[doctest] " << Color::None << "listing all test suites\n"; + separator_to_stream(); + + for(unsigned i = 0; i < in.num_data; ++i) + s << Color::None << in.data[i]->m_test_suite << "\n"; + + separator_to_stream(); + + s << Color::Cyan << "[doctest] " << Color::None + << "unskipped test cases passing the current filters: " + << g_cs->numTestCasesPassingFilters << "\n"; + s << Color::Cyan << "[doctest] " << Color::None + << "test suites with unskipped test cases passing the current filters: " + << g_cs->numTestSuitesPassingFilters << "\n"; + } + } + + void test_run_start() override { + if(!opt.minimal) + printIntro(); + } + + void test_run_end(const TestRunStats& p) override { + if(opt.minimal && p.numTestCasesFailed == 0) + return; + + separator_to_stream(); + s << std::dec; + + auto totwidth = int(std::ceil(log10(static_cast(std::max(p.numTestCasesPassingFilters, static_cast(p.numAsserts))) + 1))); + auto passwidth = int(std::ceil(log10(static_cast(std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast(p.numAsserts - p.numAssertsFailed))) + 1))); + auto failwidth = int(std::ceil(log10(static_cast(std::max(p.numTestCasesFailed, static_cast(p.numAssertsFailed))) + 1))); + const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0; + s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth) + << p.numTestCasesPassingFilters << " | " + << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None : + Color::Green) + << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" + << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None) + << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |"; + if(opt.no_skipped_summary == false) { + const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters; + s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped + << " skipped" << Color::None; + } + s << "\n"; + s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth) + << p.numAsserts << " | " + << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green) + << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None + << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth) + << p.numAssertsFailed << " failed" << Color::None << " |\n"; + s << Color::Cyan << "[doctest] " << Color::None + << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green) + << ((p.numTestCasesFailed > 0) ? "FAILURE!" : "SUCCESS!") << Color::None << std::endl; + } + + void test_case_start(const TestCaseData& in) override { + hasLoggedCurrentTestStart = false; + tc = ∈ + subcasesStack.clear(); + currentSubcaseLevel = 0; + } + + void test_case_reenter(const TestCaseData&) override { + subcasesStack.clear(); + } + + void test_case_end(const CurrentTestCaseStats& st) override { + if(tc->m_no_output) + return; + + // log the preamble of the test case only if there is something + // else to print - something other than that an assert has failed + if(opt.duration || + (st.failure_flags && st.failure_flags != static_cast(TestCaseFailureReason::AssertFailure))) + logTestStart(); + + if(opt.duration) + s << Color::None << std::setprecision(6) << std::fixed << st.seconds + << " s: " << tc->m_name << "\n"; + + if(st.failure_flags & TestCaseFailureReason::Timeout) + s << Color::Red << "Test case exceeded time limit of " << std::setprecision(6) + << std::fixed << tc->m_timeout << "!\n"; + + if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedButDidnt) { + s << Color::Red << "Should have failed but didn't! Marking it as failed!\n"; + } else if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedAndDid) { + s << Color::Yellow << "Failed as expected so marking it as not failed\n"; + } else if(st.failure_flags & TestCaseFailureReason::CouldHaveFailedAndDid) { + s << Color::Yellow << "Allowed to fail so marking it as not failed\n"; + } else if(st.failure_flags & TestCaseFailureReason::DidntFailExactlyNumTimes) { + s << Color::Red << "Didn't fail exactly " << tc->m_expected_failures + << " times so marking it as failed!\n"; + } else if(st.failure_flags & TestCaseFailureReason::FailedExactlyNumTimes) { + s << Color::Yellow << "Failed exactly " << tc->m_expected_failures + << " times as expected so marking it as not failed!\n"; + } + if(st.failure_flags & TestCaseFailureReason::TooManyFailedAsserts) { + s << Color::Red << "Aborting - too many failed asserts!\n"; + } + s << Color::None; // lgtm [cpp/useless-expression] + } + + void test_case_exception(const TestCaseException& e) override { + DOCTEST_LOCK_MUTEX(mutex) + if(tc->m_no_output) + return; + + logTestStart(); + + file_line_to_stream(tc->m_file.c_str(), tc->m_line, " "); + successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require : + assertType::is_check); + s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ") + << Color::Cyan << e.error_string << "\n"; + + int num_stringified_contexts = get_num_stringified_contexts(); + if(num_stringified_contexts) { + auto stringified_contexts = get_stringified_contexts(); + s << Color::None << " logged: "; + for(int i = num_stringified_contexts; i > 0; --i) { + s << (i == num_stringified_contexts ? "" : " ") + << stringified_contexts[i - 1] << "\n"; + } + } + s << "\n" << Color::None; + } + + void subcase_start(const SubcaseSignature& subc) override { + subcasesStack.push_back(subc); + ++currentSubcaseLevel; + hasLoggedCurrentTestStart = false; + } + + void subcase_end() override { + --currentSubcaseLevel; + hasLoggedCurrentTestStart = false; + } + + void log_assert(const AssertData& rb) override { + if((!rb.m_failed && !opt.success) || tc->m_no_output) + return; + + DOCTEST_LOCK_MUTEX(mutex) + + logTestStart(); + + file_line_to_stream(rb.m_file, rb.m_line, " "); + successOrFailColoredStringToStream(!rb.m_failed, rb.m_at); + + fulltext_log_assert_to_stream(s, rb); + + log_contexts(); + } + + void log_message(const MessageData& mb) override { + if(tc->m_no_output) + return; + + DOCTEST_LOCK_MUTEX(mutex) + + logTestStart(); + + file_line_to_stream(mb.m_file, mb.m_line, " "); + s << getSuccessOrFailColor(false, mb.m_severity) + << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity, + "MESSAGE") << ": "; + s << Color::None << mb.m_string << "\n"; + log_contexts(); + } + + void test_case_skipped(const TestCaseData&) override {} + }; + + DOCTEST_REGISTER_REPORTER("console", 0, ConsoleReporter); + +#ifdef DOCTEST_PLATFORM_WINDOWS + struct DebugOutputWindowReporter : public ConsoleReporter + { + DOCTEST_THREAD_LOCAL static std::ostringstream oss; + + DebugOutputWindowReporter(const ContextOptions& co) + : ConsoleReporter(co, oss) {} + +#define DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(func, type, arg) \ + void func(type arg) override { \ + bool with_col = g_no_colors; \ + g_no_colors = false; \ + ConsoleReporter::func(arg); \ + if(oss.tellp() != std::streampos{}) { \ + DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ + oss.str(""); \ + } \ + g_no_colors = with_col; \ + } + + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_end, const TestRunStats&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_start, const TestCaseData&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_reenter, const TestCaseData&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_exception, const TestCaseException&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_start, const SubcaseSignature&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_end, DOCTEST_EMPTY, DOCTEST_EMPTY) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_assert, const AssertData&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_message, const MessageData&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_skipped, const TestCaseData&, in) + }; + + DOCTEST_THREAD_LOCAL std::ostringstream DebugOutputWindowReporter::oss; +#endif // DOCTEST_PLATFORM_WINDOWS + + // the implementation of parseOption() + bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String* value) { + // going from the end to the beginning and stopping on the first occurrence from the end + for(int i = argc; i > 0; --i) { + auto index = i - 1; + auto temp = std::strstr(argv[index], pattern); + if(temp && (value || strlen(temp) == strlen(pattern))) { //!OCLINT prefer early exits and continue + // eliminate matches in which the chars before the option are not '-' + bool noBadCharsFound = true; + auto curr = argv[index]; + while(curr != temp) { + if(*curr++ != '-') { + noBadCharsFound = false; + break; + } + } + if(noBadCharsFound && argv[index][0] == '-') { + if(value) { + // parsing the value of an option + temp += strlen(pattern); + const unsigned len = strlen(temp); + if(len) { + *value = temp; + return true; + } + } else { + // just a flag - no value + return true; + } + } + } + } + return false; + } + + // parses an option and returns the string after the '=' character + bool parseOption(int argc, const char* const* argv, const char* pattern, String* value = nullptr, + const String& defaultVal = String()) { + if(value) + *value = defaultVal; +#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + // offset (normally 3 for "dt-") to skip prefix + if(parseOptionImpl(argc, argv, pattern + strlen(DOCTEST_CONFIG_OPTIONS_PREFIX), value)) + return true; +#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + return parseOptionImpl(argc, argv, pattern, value); + } + + // locates a flag on the command line + bool parseFlag(int argc, const char* const* argv, const char* pattern) { + return parseOption(argc, argv, pattern); + } + + // parses a comma separated list of words after a pattern in one of the arguments in argv + bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern, + std::vector& res) { + String filtersString; + if(parseOption(argc, argv, pattern, &filtersString)) { + // tokenize with "," as a separator, unless escaped with backslash + std::ostringstream s; + auto flush = [&s, &res]() { + auto string = s.str(); + if(string.size() > 0) { + res.push_back(string.c_str()); + } + s.str(""); + }; + + bool seenBackslash = false; + const char* current = filtersString.c_str(); + const char* end = current + strlen(current); + while(current != end) { + char character = *current++; + if(seenBackslash) { + seenBackslash = false; + if(character == ',' || character == '\\') { + s.put(character); + continue; + } + s.put('\\'); + } + if(character == '\\') { + seenBackslash = true; + } else if(character == ',') { + flush(); + } else { + s.put(character); + } + } + + if(seenBackslash) { + s.put('\\'); + } + flush(); + return true; + } + return false; + } + + enum optionType + { + option_bool, + option_int + }; + + // parses an int/bool option from the command line + bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type, + int& res) { + String parsedValue; + if(!parseOption(argc, argv, pattern, &parsedValue)) + return false; + + if(type) { + // integer + // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse... + int theInt = std::atoi(parsedValue.c_str()); + if (theInt != 0) { + res = theInt; //!OCLINT parameter reassignment + return true; + } + } else { + // boolean + const char positive[][5] = { "1", "true", "on", "yes" }; // 5 - strlen("true") + 1 + const char negative[][6] = { "0", "false", "off", "no" }; // 6 - strlen("false") + 1 + + // if the value matches any of the positive/negative possibilities + for (unsigned i = 0; i < 4; i++) { + if (parsedValue.compare(positive[i], true) == 0) { + res = 1; //!OCLINT parameter reassignment + return true; + } + if (parsedValue.compare(negative[i], true) == 0) { + res = 0; //!OCLINT parameter reassignment + return true; + } + } + } + return false; + } +} // namespace + +Context::Context(int argc, const char* const* argv) + : p(new detail::ContextState) { + parseArgs(argc, argv, true); + if(argc) + p->binary_name = argv[0]; +} + +Context::~Context() { + if(g_cs == p) + g_cs = nullptr; + delete p; +} + +void Context::applyCommandLine(int argc, const char* const* argv) { + parseArgs(argc, argv); + if(argc) + p->binary_name = argv[0]; +} + +// parses args +void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { + using namespace detail; + + // clang-format off + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file=", p->filters[0]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sf=", p->filters[0]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file-exclude=",p->filters[1]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sfe=", p->filters[1]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite=", p->filters[2]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ts=", p->filters[2]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite-exclude=", p->filters[3]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tse=", p->filters[3]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case=", p->filters[4]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tc=", p->filters[4]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case-exclude=", p->filters[5]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tce=", p->filters[5]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase=", p->filters[6]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sc=", p->filters[6]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase-exclude=", p->filters[7]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sce=", p->filters[7]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "reporters=", p->filters[8]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "r=", p->filters[8]); + // clang-format on + + int intRes = 0; + String strRes; + +#define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ + if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \ + parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \ + p->var = static_cast(intRes); \ + else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \ + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \ + p->var = true; \ + else if(withDefaults) \ + p->var = default + +#define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \ + if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_int, intRes) || \ + parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_int, intRes)) \ + p->var = intRes; \ + else if(withDefaults) \ + p->var = default + +#define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \ + if(parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", &strRes, default) || \ + parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", &strRes, default) || \ + withDefaults) \ + p->var = strRes + + // clang-format off + DOCTEST_PARSE_STR_OPTION("out", "o", out, ""); + DOCTEST_PARSE_STR_OPTION("order-by", "ob", order_by, "file"); + DOCTEST_PARSE_INT_OPTION("rand-seed", "rs", rand_seed, 0); + + DOCTEST_PARSE_INT_OPTION("first", "f", first, 0); + DOCTEST_PARSE_INT_OPTION("last", "l", last, UINT_MAX); + + DOCTEST_PARSE_INT_OPTION("abort-after", "aa", abort_after, 0); + DOCTEST_PARSE_INT_OPTION("subcase-filter-levels", "scfl", subcase_filter_levels, INT_MAX); + + DOCTEST_PARSE_AS_BOOL_OR_FLAG("success", "s", success, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("minimal", "m", minimal, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("quiet", "q", quiet, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-intro", "ni", no_intro, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-breaks", "nb", no_breaks, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skip", "ns", no_skip, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC)); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false); + // clang-format on + + if(withDefaults) { + p->help = false; + p->version = false; + p->count = false; + p->list_test_cases = false; + p->list_test_suites = false; + p->list_reporters = false; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "help") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "h") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "?")) { + p->help = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "version") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "v")) { + p->version = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "count") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "c")) { + p->count = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-cases") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ltc")) { + p->list_test_cases = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-suites") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lts")) { + p->list_test_suites = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-reporters") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lr")) { + p->list_reporters = true; + p->exit = true; + } +} + +// allows the user to add procedurally to the filters from the command line +void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); } + +// allows the user to clear all filters from the command line +void Context::clearFilters() { + for(auto& curr : p->filters) + curr.clear(); +} + +// allows the user to override procedurally the bool options from the command line +void Context::setOption(const char* option, bool value) { + setOption(option, value ? "true" : "false"); +} + +// allows the user to override procedurally the int options from the command line +void Context::setOption(const char* option, int value) { + setOption(option, toString(value).c_str()); +} + +// allows the user to override procedurally the string options from the command line +void Context::setOption(const char* option, const char* value) { + auto argv = String("-") + option + "=" + value; + auto lvalue = argv.c_str(); + parseArgs(1, &lvalue); +} + +// users should query this in their main() and exit the program if true +bool Context::shouldExit() { return p->exit; } + +void Context::setAsDefaultForAssertsOutOfTestCases() { g_cs = p; } + +void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; } + +void Context::setCout(std::ostream* out) { p->cout = out; } + +static class DiscardOStream : public std::ostream +{ +private: + class : public std::streambuf + { + private: + // allowing some buffering decreases the amount of calls to overflow + char buf[1024]; + + protected: + std::streamsize xsputn(const char_type*, std::streamsize count) override { return count; } + + int_type overflow(int_type ch) override { + setp(std::begin(buf), std::end(buf)); + return traits_type::not_eof(ch); + } + } discardBuf; + +public: + DiscardOStream() + : std::ostream(&discardBuf) {} +} discardOut; + +// the main function that does all the filtering and test running +int Context::run() { + using namespace detail; + + // save the old context state in case such was setup - for using asserts out of a testing context + auto old_cs = g_cs; + // this is the current contest + g_cs = p; + is_running_in_test = true; + + g_no_colors = p->no_colors; + p->resetRunData(); + + std::fstream fstr; + if(p->cout == nullptr) { + if(p->quiet) { + p->cout = &discardOut; + } else if(p->out.size()) { + // to a file if specified + fstr.open(p->out.c_str(), std::fstream::out); + p->cout = &fstr; + } else { +#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + // stdout by default + p->cout = &std::cout; +#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + return EXIT_FAILURE; +#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + } + } + + FatalConditionHandler::allocateAltStackMem(); + + auto cleanup_and_return = [&]() { + FatalConditionHandler::freeAltStackMem(); + + if(fstr.is_open()) + fstr.close(); + + // restore context + g_cs = old_cs; + is_running_in_test = false; + + // we have to free the reporters which were allocated when the run started + for(auto& curr : p->reporters_currently_used) + delete curr; + p->reporters_currently_used.clear(); + + if(p->numTestCasesFailed && !p->no_exitcode) + return EXIT_FAILURE; + return EXIT_SUCCESS; + }; + + // setup default reporter if none is given through the command line + if(p->filters[8].empty()) + p->filters[8].push_back("console"); + + // check to see if any of the registered reporters has been selected + for(auto& curr : getReporters()) { + if(matchesAny(curr.first.second.c_str(), p->filters[8], false, p->case_sensitive)) + p->reporters_currently_used.push_back(curr.second(*g_cs)); + } + + // TODO: check if there is nothing in reporters_currently_used + + // prepend all listeners + for(auto& curr : getListeners()) + p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs)); + +#ifdef DOCTEST_PLATFORM_WINDOWS + if(isDebuggerActive() && p->no_debug_output == false) + p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs)); +#endif // DOCTEST_PLATFORM_WINDOWS + + // handle version, help and no_run + if(p->no_run || p->version || p->help || p->list_reporters) { + DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, QueryData()); + + return cleanup_and_return(); + } + + std::vector testArray; + for(auto& curr : getRegisteredTests()) + testArray.push_back(&curr); + p->numTestCases = testArray.size(); + + // sort the collected records + if(!testArray.empty()) { + if(p->order_by.compare("file", true) == 0) { + std::sort(testArray.begin(), testArray.end(), fileOrderComparator); + } else if(p->order_by.compare("suite", true) == 0) { + std::sort(testArray.begin(), testArray.end(), suiteOrderComparator); + } else if(p->order_by.compare("name", true) == 0) { + std::sort(testArray.begin(), testArray.end(), nameOrderComparator); + } else if(p->order_by.compare("rand", true) == 0) { + std::srand(p->rand_seed); + + // random_shuffle implementation + const auto first = &testArray[0]; + for(size_t i = testArray.size() - 1; i > 0; --i) { + int idxToSwap = std::rand() % (i + 1); + + const auto temp = first[i]; + + first[i] = first[idxToSwap]; + first[idxToSwap] = temp; + } + } else if(p->order_by.compare("none", true) == 0) { + // means no sorting - beneficial for death tests which call into the executable + // with a specific test case in mind - we don't want to slow down the startup times + } + } + + std::set testSuitesPassingFilt; + + bool query_mode = p->count || p->list_test_cases || p->list_test_suites; + std::vector queryResults; + + if(!query_mode) + DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_start, DOCTEST_EMPTY); + + // invoke the registered functions if they match the filter criteria (or just count them) + for(auto& curr : testArray) { + const auto& tc = *curr; + + bool skip_me = false; + if(tc.m_skip && !p->no_skip) + skip_me = true; + + if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive)) + skip_me = true; + if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive)) + skip_me = true; + if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive)) + skip_me = true; + if(matchesAny(tc.m_test_suite, p->filters[3], false, p->case_sensitive)) + skip_me = true; + if(!matchesAny(tc.m_name, p->filters[4], true, p->case_sensitive)) + skip_me = true; + if(matchesAny(tc.m_name, p->filters[5], false, p->case_sensitive)) + skip_me = true; + + if(!skip_me) + p->numTestCasesPassingFilters++; + + // skip the test if it is not in the execution range + if((p->last < p->numTestCasesPassingFilters && p->first <= p->last) || + (p->first > p->numTestCasesPassingFilters)) + skip_me = true; + + if(skip_me) { + if(!query_mode) + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_skipped, tc); + continue; + } + + // do not execute the test if we are to only count the number of filter passing tests + if(p->count) + continue; + + // print the name of the test and don't execute it + if(p->list_test_cases) { + queryResults.push_back(&tc); + continue; + } + + // print the name of the test suite if not done already and don't execute it + if(p->list_test_suites) { + if((testSuitesPassingFilt.count(tc.m_test_suite) == 0) && tc.m_test_suite[0] != '\0') { + queryResults.push_back(&tc); + testSuitesPassingFilt.insert(tc.m_test_suite); + p->numTestSuitesPassingFilters++; + } + continue; + } + + // execute the test if it passes all the filtering + { + p->currentTest = &tc; + + p->failure_flags = TestCaseFailureReason::None; + p->seconds = 0; + + // reset atomic counters + p->numAssertsFailedCurrentTest_atomic = 0; + p->numAssertsCurrentTest_atomic = 0; + + p->fullyTraversedSubcases.clear(); + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc); + + p->timer.start(); + + bool run_test = true; + + do { + // reset some of the fields for subcases (except for the set of fully passed ones) + p->reachedLeaf = false; + // May not be empty if previous subcase exited via exception. + p->subcaseStack.clear(); + p->currentSubcaseDepth = 0; + + p->shouldLogCurrentException = true; + + // reset stuff for logging with INFO() + p->stringifiedContexts.clear(); + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + try { +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS +// MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method) +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable + FatalConditionHandler fatalConditionHandler; // Handle signals + // execute the test + tc.m_test(); + fatalConditionHandler.reset(); +DOCTEST_MSVC_SUPPRESS_WARNING_POP +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + } catch(const TestFailureException&) { + p->failure_flags |= TestCaseFailureReason::AssertFailure; + } catch(...) { + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, + {translateActiveException(), false}); + p->failure_flags |= TestCaseFailureReason::Exception; + } +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + + // exit this loop if enough assertions have failed - even if there are more subcases + if(p->abort_after > 0 && + p->numAssertsFailed + p->numAssertsFailedCurrentTest_atomic >= p->abort_after) { + run_test = false; + p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts; + } + + if(!p->nextSubcaseStack.empty() && run_test) + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc); + if(p->nextSubcaseStack.empty()) + run_test = false; + } while(run_test); + + p->finalizeTestCaseData(); + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); + + p->currentTest = nullptr; + + // stop executing tests if enough assertions have failed + if(p->abort_after > 0 && p->numAssertsFailed >= p->abort_after) + break; + } + } + + if(!query_mode) { + DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); + } else { + QueryData qdata; + qdata.run_stats = g_cs; + qdata.data = queryResults.data(); + qdata.num_data = unsigned(queryResults.size()); + DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata); + } + + return cleanup_and_return(); +} + +DOCTEST_DEFINE_INTERFACE(IReporter) + +int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); } +const IContextScope* const* IReporter::get_active_contexts() { + return get_num_active_contexts() ? &detail::g_infoContexts[0] : nullptr; +} + +int IReporter::get_num_stringified_contexts() { return detail::g_cs->stringifiedContexts.size(); } +const String* IReporter::get_stringified_contexts() { + return get_num_stringified_contexts() ? &detail::g_cs->stringifiedContexts[0] : nullptr; +} + +namespace detail { + void registerReporterImpl(const char* name, int priority, reporterCreatorFunc c, bool isReporter) { + if(isReporter) + getReporters().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); + else + getListeners().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); + } +} // namespace detail + +} // namespace doctest + +#endif // DOCTEST_CONFIG_DISABLE + +#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) // 'function' : must be 'attribute' - see issue #182 +int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } +DOCTEST_MSVC_SUPPRESS_WARNING_POP +#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN + +DOCTEST_CLANG_SUPPRESS_WARNING_POP +DOCTEST_MSVC_SUPPRESS_WARNING_POP +DOCTEST_GCC_SUPPRESS_WARNING_POP + +DOCTEST_SUPPRESS_COMMON_WARNINGS_POP + +#endif // DOCTEST_LIBRARY_IMPLEMENTATION +#endif // DOCTEST_CONFIG_IMPLEMENT + +#ifdef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN +#undef WIN32_LEAN_AND_MEAN +#undef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN +#endif // DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN + +#ifdef DOCTEST_UNDEF_NOMINMAX +#undef NOMINMAX +#undef DOCTEST_UNDEF_NOMINMAX +#endif // DOCTEST_UNDEF_NOMINMAX \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_bitset.py b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_bitset.py new file mode 100644 index 00000000..357a5e65 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_bitset.py @@ -0,0 +1,29 @@ +import re + + +class CistaBitsetPrinter(object): + """Print a cista::bitset.""" + + def __init__(self, val): + self.val = val + self.n_words = int(self.val["num_blocks"]) + self.bits_per_word = int(self.val["bits_per_block"]) + self.values = [int(self.val["blocks_"]["el_"][index]) for index in range(self.n_words)] + + def to_string(self): + s = "" + for word_index in range(self.n_words): + s += format(self.values[word_index], '064b') + return s + + def str(self): + return self.to_string() + + +def cista_bitset(val): + regex = re.compile("cista::bitset") + if regex.match(str(val.type.strip_typedefs().unqualified())): + return CistaBitsetPrinter(val) + + +gdb.pretty_printers.append(cista_bitset) diff --git a/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_hash_storage.py b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_hash_storage.py new file mode 100644 index 00000000..586d8138 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_hash_storage.py @@ -0,0 +1,142 @@ +import re +import gdb.xmethod + +def is_cista_hash_storage(gdb_type): + type_str = str(gdb_type.strip_typedefs().unqualified()) + return type_str.startswith("cista::hash_storage") and not type_str.endswith("::ctrl_t") + +class CistaHashStorage: + def __init__(self, val): + self.val = val + self.entries = self.val['entries_'] + self.capacity = self.val['capacity_'] + self.ctrl = self.val['ctrl_'] + + regex = re.compile("cista::offset_ptr") + if regex.match(str(self.entries.type.strip_typedefs())): + self.entries = OffsetPointer(self.entries) + self.ctrl = OffsetPointer(self.ctrl) + + def is_valid_entry(self, idx): + return (self.ctrl + idx).dereference() & int('0b10000000', 2) == 0 + + def __len__(self): + return int(self.val['size_']) + + def __getitem__(self, key): + for i in range(self.val['capacity_']): + if not self.is_valid_entry(i): + continue + + entry = (self.entries + i).dereference() + if str(entry['first']) == str(key): + return entry['second'] + +class CistaHashStoragePrinter: + def __init__(self, val): + self.val = CistaHashStorage(val) + + def children(self): + current_idx = 0 + for i in range(self.val.capacity): + if self.val.is_valid_entry(i): + yield '[' + str(current_idx) + ']', (self.val.entries + i).dereference() + current_idx += 1 + + def to_string(self): + return str(self.val) + +def my_pp_func(val): + if is_cista_hash_storage(val.type): + return CistaHashStoragePrinter(val) + +### XMethod cista::vector::operator[] + +class CistaHashStorageWorker_operator_brackets(gdb.xmethod.XMethodWorker): + def __init__(self, class_type): + self.class_type = class_type + + def get_arg_types(self): + return self.class_type.template_argument(0) + + def get_result_type(self, obj): + return obj.type.strip_typedefs().template_argument(1) + + def __call__(self, this, key): + hash_storage = CistaHashStorage(this.dereference()) + return hash_storage[key] + +class CistaHashStorageWorker_operator_brackets_char_ptr(gdb.xmethod.XMethodWorker): + def __init__(self, class_type): + self.class_type = class_type + + def get_arg_types(self): + return gdb.lookup_type('const char* const') + + def get_result_type(self, obj): + return obj.type.strip_typedefs().template_argument(1) + + def __call__(self, this, key): + hash_storage = CistaHashStorage(this.dereference()) + key = key.cast(gdb.lookup_type("const char* const")) + return hash_storage[str(key).split()[1]] + +class CistaHashStorage_operator_brackets(gdb.xmethod.XMethod): + def __init__(self): + gdb.xmethod.XMethod.__init__(self, 'operator[]') + + def get_worker(self, method_name, class_type): + worker = [] + if method_name == 'operator[]': + worker.append(CistaHashStorageWorker_operator_brackets(class_type)) + + temp_arg = class_type.template_argument(0).name + is_string = temp_arg.startswith("std::__cxx11::basic_string") \ + or temp_arg.startswith("cista::basic_string") + if method_name == 'operator[]' and is_string: + worker.append(CistaHashStorageWorker_operator_brackets_char_ptr(class_type)) + + return worker + +### XMethod cista::vector::size + +class CistaHashStorageWorker_size(gdb.xmethod.XMethodWorker): + def get_arg_types(self): + return None + + def get_result_type(self): + return gdb.lookup_type('unsigned long int') + + def __call__(self, this): + hash_storage = CistaHashStorage(this.dereference()) + return len(hash_storage) + +class CistaHashStorage_size(gdb.xmethod.XMethod): + def __init__(self): + gdb.xmethod.XMethod.__init__(self, 'size') + + def get_worker(self, method_name, _): + if method_name == 'size': + return [CistaHashStorageWorker_size()] + +class CistaHashStorageMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): + gdb.xmethod.XMethodMatcher.__init__(self, 'CistaHashStorageMatcher') + # List of methods 'managed' by this matcher + self.methods = [CistaHashStorage_operator_brackets(), CistaHashStorage_size()] + + def match(self, class_type, method_name): + if not is_cista_hash_storage(class_type): + return None + + workers = [] + for method in self.methods: + if method.enabled: + worker = method.get_worker(method_name, class_type.template_argument(0)) + if worker: + workers.extend(worker) + + return workers + +gdb.pretty_printers.append(my_pp_func) +gdb.xmethod.register_xmethod_matcher(None, CistaHashStorageMatcher()) diff --git a/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_pointer.py b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_pointer.py new file mode 100644 index 00000000..b9ab9003 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_pointer.py @@ -0,0 +1,19 @@ +import re + +class CistaOffsetPointerPrinter: + def __init__(self, val): + self.val = OffsetPointer(val) + + def children(self): + yield "*", self.val.dereference() + + def to_string(self): + return str(self.val) + +def my_pp_func(val): + regex = re.compile("cista::offset_ptr") + if regex.match(str(val.type.strip_typedefs().unqualified())): + return CistaOffsetPointerPrinter(val) + +gdb.pretty_printers.append(my_pp_func) + diff --git a/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_string.py b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_string.py new file mode 100644 index 00000000..e8d377df --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_string.py @@ -0,0 +1,36 @@ +import re + +class CistaString: + def __init__(self, val): + self.val = val + self.GDB_CHAR_PTR = gdb.lookup_type("char*") + + def is_short(self): + return self.val['s_']['is_short_'] + + def data(self): + return self.val['s_']['s_'] if self.is_short() else self.val['h_']['ptr_'] + + def str(self): + if self.is_short(): + return self.data().address.cast(self.GDB_CHAR_PTR) + + if is_offset_ptr(self.data().type): + return OffsetPointer(self.data()).as_raw_ptr() + + return self.data() + +class CistaStringPrinter: + def __init__(self, val): + self.val = CistaString(val) + + def to_string(self): + return self.val.str() + +def my_pp_func(val): + regex = re.compile("cista::basic_string") + if regex.match(str(val.type.strip_typedefs().unqualified())): + return CistaStringPrinter(val) + +gdb.pretty_printers.append(my_pp_func) + diff --git a/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_tuple.py b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_tuple.py new file mode 100644 index 00000000..c4c9a81e --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_tuple.py @@ -0,0 +1,51 @@ +import re + +def get_offset(gdb_type, idx): + current_offset = 0 + + for i in range(0, idx): + current_offset += gdb_type.template_argument(i).sizeof + + next_type = gdb_type.template_argument(i + 1) + align_of = next_type.alignof + + misalign = current_offset % align_of + if misalign != 0: + current_offset += (align_of - misalign) % align_of + + return current_offset + +class CistaTuplePrinter: + def __init__(self, val): + self.val = val + + def this_as_char_ptr(self): + char_ptr = gdb.lookup_type("char*") + return self.val.address.reinterpret_cast(char_ptr) + + def children(self): + current_idx = 0 + + while True: + try: + template_type = self.val.type.template_argument(current_idx) + except: + return + + member_address = self.this_as_char_ptr() + get_offset(self.val.type, current_idx) + casted_member = member_address.reinterpret_cast(template_type.pointer()).dereference() + + yield '[' + str(current_idx) + ']', casted_member + + current_idx += 1 + + def to_string(self): + return str(self.val) + +def my_pp_func(val): + regex = re.compile("cista::tuple") + if regex.match(str(val.type.strip_typedefs().unqualified())): + return CistaTuplePrinter(val) + +gdb.pretty_printers.append(my_pp_func) + diff --git a/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_variant.py b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_variant.py new file mode 100644 index 00000000..a2ef7888 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_variant.py @@ -0,0 +1,27 @@ +import re + +class CistaVariantPrinter: + def __init__(self, val): + self.val = val + + def get_actual_type(self): + return self.val.type.template_argument(self.val['idx_']) + + def children(self): + for field in self.val.type.fields(): + if (field.name == "storage_"): + yield field.name, self.val[field.name].cast(self.get_actual_type()) + else: + yield field.name, self.val[field.name].cast(field.type) + + def to_string(self): + return str(self.val) + + +def my_pp_func(val): + regex = re.compile("cista::variant") + if regex.match(str(val.type.strip_typedefs().unqualified())): + return CistaVariantPrinter(val) + +gdb.pretty_printers.append(my_pp_func) + diff --git a/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_vector.py b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_vector.py new file mode 100644 index 00000000..558af04b --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/cista_vector.py @@ -0,0 +1,108 @@ +import re +import gdb.xmethod + +def is_cista_vector(gdb_type): + return str(gdb_type.strip_typedefs().unqualified()).startswith("cista::basic_vector") + +def is_raw_vector(gdb_type): + return not str(gdb_type.strip_typedefs().template_argument(1)).startswith("cista::offset_ptr") + +class CistaVector: + def __init__(self, val): + self.val = val + self.size = val['used_size_'] + self.el = val['el_'] if is_raw_vector(val.type) else OffsetPointer(val['el_']) + + def __len__(self): + return self.size + + def __getitem__(self, idx): + return (self.el + idx).dereference() + + def at(self, idx): + if (self.size < idx): + print("Accessing vector out of bounds") + return None + + return self[idx] + +class CistaVectorPrinter: + def __init__(self, val): + self.val = CistaVector(val) + + def children(self): + for idx in range(len(self.val)): + yield '[' + str(idx) + ']', self.val[idx] + + def to_string(self): + return str(self.val) + +def my_pp_func(val): + if not is_cista_vector(val.type): + return + + return CistaVectorPrinter(val) + +### XMethod cista::vector::at + +class CistaVectorWorker_at(gdb.xmethod.XMethodWorker): + def get_arg_types(self): + return gdb.lookup_type('unsigned long int') + + def get_result_type(self, obj): + return obj.type.strip_typedefs().template_argument(0) + + def __call__(self, this, idx): + vec = CistaVector(this.dereference()) + return vec.at(idx) + +class CistaVector_at(gdb.xmethod.XMethod): + def __init__(self): + gdb.xmethod.XMethod.__init__(self, 'at') + + def get_worker(self, method_name): + if method_name == 'at': + return CistaVectorWorker_at() + +### XMethod cista::vector::operator[] + +class CistaVectorWorker_operator_brackets(gdb.xmethod.XMethodWorker): + def get_arg_types(self): + return gdb.lookup_type('unsigned long int') + + def get_result_type(self, obj): + return obj.type.strip_typedefs().template_argument(0) + + def __call__(self, this, idx): + vec = CistaVector(this.dereference()) + return vec[idx] + +class CistaVector_operator_brackets(gdb.xmethod.XMethod): + def __init__(self): + gdb.xmethod.XMethod.__init__(self, 'operator[]') + + def get_worker(self, method_name): + if method_name == 'operator[]': + return CistaVectorWorker_operator_brackets() + +class CistaVectorMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): + gdb.xmethod.XMethodMatcher.__init__(self, 'CistaVectorMatcher') + # List of methods 'managed' by this matcher + self.methods = [CistaVector_at(), CistaVector_operator_brackets()] + + def match(self, class_type, method_name): + if not is_cista_vector(class_type): + return None + + workers = [] + for method in self.methods: + if method.enabled: + worker = method.get_worker(method_name) + if worker: + workers.append(worker) + + return workers + +gdb.pretty_printers.append(my_pp_func) +gdb.xmethod.register_xmethod_matcher(None, CistaVectorMatcher()) diff --git a/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/offset_pointer.py b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/offset_pointer.py new file mode 100644 index 00000000..a9aa7851 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/pretty-printers/gdb/offset_pointer.py @@ -0,0 +1,24 @@ +class OffsetPointer: + def __init__(self, val): + self.val = val + self.offset = val['offset_'] + self.pointer_type = val.type.template_argument(0).pointer() + + def this_as_intptr_t(self): + intptr_t = gdb.lookup_type("intptr_t") + return self.val.address.reinterpret_cast(intptr_t) + + def as_raw_ptr(self): + return (self.this_as_intptr_t() + self.offset).reinterpret_cast(self.pointer_type) + + def add(self, offset): + return self.as_raw_ptr() + offset + + def __add__(self, o): + return self.add(o) + + def dereference(self): + return self.as_raw_ptr().dereference() + +def is_offset_ptr(type): + return str(type.strip_typedefs().unqualified()).startswith("cista::offset_ptr") \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/to_tuple_generator/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/to_tuple_generator/CMakeLists.txt new file mode 100644 index 00000000..e784232a --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/to_tuple_generator/CMakeLists.txt @@ -0,0 +1,3 @@ +project(to_tuple_generator) + +add_executable(to_tuple_generator to_tuple_generator.cc) \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/to_tuple_generator/to_tuple_generator.cc b/parallel/parallel_src/extern/cista/tools/to_tuple_generator/to_tuple_generator.cc new file mode 100644 index 00000000..7179cf6a --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/to_tuple_generator/to_tuple_generator.cc @@ -0,0 +1,132 @@ +#include +#include +#include + +std::string var_list(unsigned num, bool address_of) { + std::stringstream ss; + for (int i = 0; i < num; ++i) { + if (address_of) { + ss << "&"; + } + ss << "p" << (i + 1); + if (i != num - 1) { + ss << ", "; + } + } + return ss.str(); +} + +int main(int argc, char** argv) { + if (argc != 2) { + std::cout << "usage: " << argv[0] << " [max members]\n"; + return 1; + } + + auto const max_members = std::stoi(argv[1]); + + std::cout << R"( +#pragma once + +#include +#include + +#include "cista/reflection/arity.h" + +namespace cista { + +namespace detail { + +template +struct has_cista_members : std::false_type {}; + +template +struct has_cista_members< + T, + std::void_t>().cista_members())>> + : std::true_type {}; + +template +inline constexpr auto const has_cista_members_v = has_cista_members::value; + +template +constexpr inline auto add_const_helper(std::tuple&& t, + std::index_sequence) { + return std::make_tuple(std::cref(std::get(t))...); +} + +template +constexpr inline auto add_const(T&& t) { + return add_const_helper( + std::forward(t), + std::make_index_sequence>>()); +} + +template +auto to_ptrs_helper(std::tuple&& t, std::index_sequence) { + return std::make_tuple(&std::get(t)...); +} + +template +auto to_ptrs(T&& t) { + return to_ptrs_helper( + std::forward(t), + std::make_index_sequence>>()); +} + +} // namespace detail + +template +inline constexpr auto to_tuple_works_v = + detail::has_cista_members_v || + (std::is_aggregate_v && +#if !defined(_MSC_VER) || defined(NDEBUG) + std::is_standard_layout_v < T>&& +#endif + !std::is_polymorphic_v); + +template && std::is_const_v, + void*> = nullptr> +constexpr inline auto to_tuple(T& t) { + return detail::add_const( + const_cast>>(t) + .cista_members()); +} + +template && !std::is_const_v, + void*> = nullptr> +constexpr inline auto to_tuple(T&& t) { + return t.cista_members(); +} + +template , void*> = nullptr> +inline auto to_tuple(T& t) { + constexpr auto const a = arity(); + static_assert(a <= )" + << max_members << R"(, "Max. supported members: )" << max_members + << R"(");)" + << R"( + if constexpr (a == 0) { + return std::tie(); + })"; + + for (auto i = 1U; i <= max_members; ++i) { + std::cout << R"( else if constexpr (a == )" << i << R"(U) { + auto& [)" << var_list(i, false) + << R"(] = t; + return std::tie()" + << var_list(i, false) << R"(); + })"; + } + std::cout << "\n}\n"; + + std::cout << R"( +template +inline auto to_ptr_tuple(T&& t) { + return detail::to_ptrs(to_tuple(std::forward(t))); +} +} // namespace cista +)"; +} diff --git a/parallel/parallel_src/extern/cista/tools/uniter/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/uniter/CMakeLists.txt new file mode 100644 index 00000000..d740e5b7 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/uniter/CMakeLists.txt @@ -0,0 +1,4 @@ +project(uniter) + +add_executable(uniter uniter.cc) +target_compile_features(uniter PRIVATE cxx_std_17) \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/uniter/uniter.cc b/parallel/parallel_src/extern/cista/tools/uniter/uniter.cc new file mode 100644 index 00000000..dcf05d5e --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/uniter/uniter.cc @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#define starts_with(s, start) (s.substr(0, std::strlen(start)) == start) + +void write_file(std::string const& include_path, std::string const& path, + std::set& included) { + try { + std::ifstream f{path.c_str()}; + f.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + std::string line; + while (!f.eof() && f.peek() != EOF && std::getline(f, line)) { + constexpr auto const include_start = R"(#include ")"; + auto const include_start_len = std::strlen(include_start); + if (starts_with(line, include_start)) { + auto const include_file = + line.substr(include_start_len, line.size() - include_start_len - 1); + auto const path = include_path + "/" + include_file; + ; + if (included.insert(path).second) { + try { + write_file(include_path, path, included); + } catch (std::exception const& e) { + std::cout << "// " << include_file << ": " << e.what() << "\n"; + } + } + } else if (starts_with(line, "#pragma once")) { + // ignore + } else { + std::cout << line << "\n"; + } + } + } catch (...) { + throw; + } +} + +int main(int argc, char const** argv) { + if (argc < 4) { + printf("usage: %s license include_path file0, [file1, [file2, ...]]\n", argv[0]); + return 1; + } + + auto const license_path = std::string{argv[1]}; + auto const include_path = std::string{argv[2]}; + + std::cout << "/*\n"; + std::ifstream f{license_path.c_str()}; + f.exceptions(std::ifstream::failbit | std::ifstream::badbit); + std::string line; + while (!f.eof() && f.peek() != EOF && std::getline(f, line)) { + std::cout << line << "\n"; + } + std::cout << "*/\n\n"; + + std::cout << "#pragma once\n\n"; + std::set included; + for (int i = 3; i < argc; ++i) { + write_file(include_path, argv[i], included); + } +} \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/wyhash/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/wyhash/CMakeLists.txt new file mode 100755 index 00000000..6e06e438 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/wyhash/CMakeLists.txt @@ -0,0 +1,3 @@ +project(wyhash) +add_library(wyhash INTERFACE) +target_include_directories(wyhash SYSTEM INTERFACE .) \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/wyhash/wyhash.h b/parallel/parallel_src/extern/cista/tools/wyhash/wyhash.h new file mode 100644 index 00000000..1e8436f8 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/wyhash/wyhash.h @@ -0,0 +1,121 @@ +// Author: Wang Yi +#ifndef wyhash_version_alpha +#define wyhash_version_alpha +#include +#include +#if defined(_MSC_VER) && defined(_M_X64) +#include +#pragma intrinsic(_umul128) +#endif +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) +#define _likely_(x) __builtin_expect(x, 1) +#define _unlikely_(x) __builtin_expect(x, 0) +#else +#define _likely_(x) (x) +#define _unlikely_(x) (x) +#endif +namespace wyhash { +const uint64_t _wyp[2] = { 0xa0761d6478bd642full, 0xe7037ed1a0b428dbull}; +static inline uint64_t _wyrotr(uint64_t v, unsigned k) { return (v >> k) | (v << (64 - k));} +static inline uint64_t _wymum(uint64_t A, uint64_t B) { +#ifdef UNOFFICIAL_WYHASH_32BIT + uint64_t hh = (A >> 32) * (B >> 32), hl = (A >> 32) * (unsigned)B, lh = (unsigned)A * (B >> 32), ll = (uint64_t)(unsigned)A * (unsigned)B; + return _wyrotr(hl, 32) ^ _wyrotr(lh, 32) ^ hh ^ ll; +#else +#ifdef __SIZEOF_INT128__ + __uint128_t r = A; r *= B; return (r >> 64) ^ r; +#elif defined(_MSC_VER) && defined(_M_X64) + A = _umul128(A, B, &B); return A ^ B; +#else + uint64_t ha = A >> 32, hb = B >> 32, la = (uint32_t)A, lb = (uint32_t)B, hi, lo; + uint64_t rh = ha * hb, rm0 = ha * lb, rm1 = hb * la, rl = la * lb, t = rl + (rm0 << 32), c = t < rl; + lo = t + (rm1 << 32); c += lo < t; hi = rh + (rm0 >> 32) + (rm1 >> 32) + c; + return hi ^ lo; +#endif +#endif +} +static inline uint64_t _wymix(uint64_t A, uint64_t B) { +#ifdef UNOFFICIAL_WYHASH_CONDOM + return (A ^ B) ^ _wymum(A, B); +#else + return _wymum(A, B); +#endif +} +static inline uint64_t wyhash64(uint64_t A, uint64_t B) { return _wymum(_wymum(A ^ *_wyp, B ^ _wyp[1]), *_wyp);} +static inline uint64_t wyrand(uint64_t *seed) { *seed += *_wyp; return _wymum(*seed ^ _wyp[1], *seed);} +static inline double wy2u01(uint64_t r) { const double _wynorm = 1.0 / (1ull << 52); return (r >> 12) * _wynorm;} +static inline double wy2gau(uint64_t r) { const double _wynorm = 1.0 / (1ull << 20); return ((r & 0x1fffff) + ((r >> 21) & 0x1fffff) + ((r >> 42) & 0x1fffff)) * _wynorm - 3.0;} +#ifndef WYHASH_LITTLE_ENDIAN +#if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define WYHASH_LITTLE_ENDIAN 1 +#elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define WYHASH_LITTLE_ENDIAN 0 +#endif +#endif +#if (WYHASH_LITTLE_ENDIAN) +static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v;} +static inline uint64_t _wyr4(const uint8_t *p) { unsigned v; memcpy(&v, p, 4); return v;} +#else +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) +static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return __builtin_bswap64(v);} +static inline uint64_t _wyr4(const uint8_t *p) { unsigned v; memcpy(&v, p, 4); return __builtin_bswap32(v);} +#elif defined(_MSC_VER) +static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return _byteswap_uint64(v);} +static inline uint64_t _wyr4(const uint8_t *p) { unsigned v; memcpy(&v, p, 4); return _byteswap_ulong(v);} +#endif +#endif +static inline uint64_t _wyr3(const uint8_t *p, unsigned k) { return (((uint64_t)p[0]) << 16) | (((uint64_t)p[k >> 1]) << 8) | p[k - 1];} +static inline uint64_t FastestHash(const void *key, size_t len, uint64_t seed) { + const uint8_t *p = (const uint8_t *)key; + return _likely_(len >= 4) ? (_wyr4(p) + _wyr4(p + len - 4)) * (_wyr4(p + (len >> 1) - 2) ^ seed) : (_likely_(len) ? _wyr3(p, len) * (*_wyp ^ seed) : seed); +} +static inline uint64_t _wyhash(const void *key, uint64_t len, uint64_t seed, const uint64_t secret[2]) { + const uint8_t *p = (const uint8_t *)key; uint64_t i = len; seed ^= secret[1]; + if (_likely_(i <= 64)) { + finalization: + if(_likely_(i<=8)) { + if(_likely_(i>=4)) return _wymix(_wyr4(p) ^ *secret, _wyr4(p + i - 4) ^ seed); + else if (_likely_(i)) return _wymix(_wyr3(p, i) ^ *secret, seed); + else return _wymum(*secret, seed); + } + else if(_likely_(i <= 16)) return _wymix(_wyr8(p) ^ *secret, _wyr8(p + i - 8) ^ seed); + else{ seed = _wymix(_wyr8(p) ^ *secret, _wyr8(p + 8) ^ seed); i -= 16; p += 16; goto finalization;} + } + uint64_t see1 = _wyrotr(seed,16), see2 = _wyrotr(seed,32), see3 = _wyrotr(seed,48); + for (; i > 64; i -= 64, p += 64) { + seed = _wymix(_wyr8(p) ^ *secret, _wyr8(p + 8) ^ seed); + see1 = _wymix(_wyr8(p + 16) ^ *secret, _wyr8(p + 24) ^ see1); + see2 = _wymix(_wyr8(p + 32) ^ *secret, _wyr8(p + 40) ^ see2); + see3 = _wymix(_wyr8(p + 48) ^ *secret, _wyr8(p + 56) ^ see3); + } + seed ^= see1 ^ see2 ^ see3; + goto finalization; +} +static inline uint64_t wyhash(const void *key, uint64_t len, uint64_t seed, const uint64_t secret[2]) { return _wymum(_wyhash(key, len, seed, secret) ^ len, *secret);} +static inline void make_secret(uint64_t seed, uint64_t secret[2]) { + uint8_t c[] = {15, 23, 27, 29, 30, 39, 43, 45, 46, 51, 53, 54, 57, 58, 60, 71, 75, 77, 78, 83, 85, 86, 89, 90, 92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120, 135, 139, 141, 142, 147, 149, 150, 153, 154, 156, 163, 165, 166, 169, 170, 172, 177, 178, 180, 184, 195, 197, 198, 201, 202, 204, 209, 210, 212, 216, 225, 226, 228, 232, 240 }; + for (size_t i = 0; i < 2; i++) { + uint8_t ok; + do { + ok = 1; secret[i] = 0; + for (size_t j = 0; j < 64; j += 8) secret[i] |= ((uint64_t)c[wyrand(&seed) % sizeof(c)]) << j; + if (secret[i] % 2 == 0) { ok = 0; continue; } + for (size_t j = 0; j < i; j++) +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) + if (__builtin_popcountll(secret[i] ^ secret[j]) != 32) { + ok = 0; + break; + } +#elif defined(_MSC_VER) + if (_mm_popcnt_u64(secret[i] ^ secret[j]) != 32) { + ok = 0; + break; + } +#endif + if (!ok||secret[i]%2==0) continue; + for (uint64_t j = 3; j < 0x100000000ull; j += 2) if (secret[i] % j == 0) { ok = 0; break;} + } while (!ok); + } +} +#endif +} // namespace wyhash \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/xxh3/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/xxh3/CMakeLists.txt new file mode 100755 index 00000000..78a57a75 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/xxh3/CMakeLists.txt @@ -0,0 +1,3 @@ +project(xxh3) +add_library(xxh3 INTERFACE) +target_include_directories(xxh3 SYSTEM INTERFACE .) \ No newline at end of file diff --git a/parallel/parallel_src/extern/cista/tools/xxh3/xxh3.h b/parallel/parallel_src/extern/cista/tools/xxh3/xxh3.h new file mode 100755 index 00000000..a3a81bcd --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/xxh3/xxh3.h @@ -0,0 +1,1632 @@ +/* + xxHash - Extremely Fast Hash algorithm + Development source file for `xxh3` + Copyright (C) 2019-present, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Note : + This file is separated for development purposes. + It will be integrated into `xxhash.c` when development phase is complete. +*/ + +#ifndef XXH3_H +#define XXH3_H + + +/* === Dependencies === */ + +#undef XXH_INLINE_ALL /* in case it's already defined */ +#define XXH_INLINE_ALL +#include "xxhash.h" + + +/* === Compiler specifics === */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ +# define XXH_RESTRICT restrict +#else +/* note : it might be useful to define __restrict or __restrict__ for some C++ compilers */ +# define XXH_RESTRICT /* disable */ +#endif + +#if defined(__GNUC__) +# if defined(__AVX2__) +# include +# elif defined(__SSE2__) +# include +# elif defined(__ARM_NEON__) || defined(__ARM_NEON) +# define inline __inline__ /* clang bug */ +# include +# undef inline +# endif +#elif defined(_MSC_VER) +# include +#endif + +/* + * Sanity check. + * + * XXH3 only requires these features to be efficient: + * + * - Usable unaligned access + * - A 32-bit or 64-bit ALU + * - If 32-bit, a decent ADC instruction + * - A 32 or 64-bit multiply with a 64-bit result + * + * Almost all 32-bit and 64-bit targets meet this, except for Thumb-1, the + * classic 16-bit only subset of ARM's instruction set. + * + * First of all, Thumb-1 lacks support for the UMULL instruction which + * performs the important long multiply. This means numerous __aeabi_lmul + * calls. + * + * Second of all, the 8 functional registers are just not enough. + * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need + * Lo registers, and this shuffling results in thousands more MOVs than A32. + * + * A32 and T32 don't have this limitation. They can access all 14 registers, + * do a 32->64 multiply with UMULL, and the flexible operand is helpful too. + * + * If compiling Thumb-1 for a target which supports ARM instructions, we + * will give a warning. + * + * Usually, if this happens, it is because of an accident and you probably + * need to specify -march, as you probably meant to compileh for a newer + * architecture. + */ +#if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM) +# warning "XXH3 is highly inefficient without ARM or Thumb-2." +#endif + +/* ========================================== + * Vectorization detection + * ========================================== */ +#define XXH_SCALAR 0 +#define XXH_SSE2 1 +#define XXH_AVX2 2 +#define XXH_NEON 3 +#define XXH_VSX 4 + +#ifndef XXH_VECTOR /* can be defined on command line */ +# if defined(__AVX2__) +# define XXH_VECTOR XXH_AVX2 +# elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) +# define XXH_VECTOR XXH_SSE2 +# elif defined(__GNUC__) /* msvc support maybe later */ \ + && (defined(__ARM_NEON__) || defined(__ARM_NEON)) \ + && (defined(__LITTLE_ENDIAN__) /* We only support little endian NEON */ \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +# define XXH_VECTOR XXH_NEON +# elif defined(__PPC64__) && defined(__POWER8_VECTOR__) && defined(__GNUC__) +# define XXH_VECTOR XXH_VSX +# else +# define XXH_VECTOR XXH_SCALAR +# endif +#endif + +/* control alignment of accumulator, + * for compatibility with fast vector loads */ +#ifndef XXH_ACC_ALIGN +# if XXH_VECTOR == 0 /* scalar */ +# define XXH_ACC_ALIGN 8 +# elif XXH_VECTOR == 1 /* sse2 */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == 2 /* avx2 */ +# define XXH_ACC_ALIGN 32 +# elif XXH_VECTOR == 3 /* neon */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == 4 /* vsx */ +# define XXH_ACC_ALIGN 16 +# endif +#endif + +/* xxh_u64 XXH_mult32to64(xxh_u32 a, xxh_u64 b) { return (xxh_u64)a * (xxh_u64)b; } */ +#if defined(_MSC_VER) && defined(_M_IX86) +# include +# define XXH_mult32to64(x, y) __emulu(x, y) +#else +# define XXH_mult32to64(x, y) ((xxh_u64)((x) & 0xFFFFFFFF) * (xxh_u64)((y) & 0xFFFFFFFF)) +#endif + +/* VSX stuff. It's a lot because VSX support is mediocre across compilers and + * there is a lot of mischief with endianness. */ +#if XXH_VECTOR == XXH_VSX +# include +# undef vector +typedef __vector unsigned long long U64x2; +typedef __vector unsigned char U8x16; +typedef __vector unsigned U32x4; + +#ifndef XXH_VSX_BE +# if defined(__BIG_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# define XXH_VSX_BE 1 +# elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__ +# warning "-maltivec=be is not recommended. Please use native endianness." +# define XXH_VSX_BE 1 +# else +# define XXH_VSX_BE 0 +# endif +#endif + +/* We need some helpers for big endian mode. */ +#if XXH_VSX_BE +/* A wrapper for POWER9's vec_revb. */ +# ifdef __POWER9_VECTOR__ +# define XXH_vec_revb vec_revb +# else +XXH_FORCE_INLINE U64x2 XXH_vec_revb(U64x2 val) +{ + U8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 }; + return vec_perm(val, val, vByteSwap); +} +# endif + +/* Power8 Crypto gives us vpermxor which is very handy for + * PPC64EB. + * + * U8x16 vpermxor(U8x16 a, U8x16 b, U8x16 mask) + * { + * U8x16 ret; + * for (int i = 0; i < 16; i++) { + * ret[i] = a[mask[i] & 0xF] ^ b[mask[i] >> 4]; + * } + * return ret; + * } + * + * Because both of the main loops load the key, swap, and xor it with input, + * we can combine the key swap into this instruction. + */ +# ifdef vec_permxor +# define XXH_vec_permxor vec_permxor +# else +# define XXH_vec_permxor __builtin_crypto_vpermxor +# endif +#endif /* XXH_VSX_BE */ +/* + * Because we reinterpret the multiply, there are endian memes: vec_mulo actually becomes + * vec_mule. + * + * Additionally, the intrinsic wasn't added until GCC 8, despite existing for a while. + * Clang has an easy way to control this, we can just use the builtin which doesn't swap. + * GCC needs inline assembly. */ +#if __has_builtin(__builtin_altivec_vmuleuw) +# define XXH_vec_mulo __builtin_altivec_vmulouw +# define XXH_vec_mule __builtin_altivec_vmuleuw +#else +/* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */ +XXH_FORCE_INLINE U64x2 XXH_vec_mulo(U32x4 a, U32x4 b) { + U64x2 result; + __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); + return result; +} +XXH_FORCE_INLINE U64x2 XXH_vec_mule(U32x4 a, U32x4 b) { + U64x2 result; + __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); + return result; +} +#endif /* __has_builtin(__builtin_altivec_vmuleuw) */ +#endif /* XXH_VECTOR == XXH_VSX */ + +/* prefetch + * can be disabled, by declaring XXH_NO_PREFETCH build macro */ +#if defined(XXH_NO_PREFETCH) +# define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ +#else +# if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */ +# include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ +# define XXH_PREFETCH(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) +# elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) +# define XXH_PREFETCH(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) +# else +# define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ +# endif +#endif /* XXH_NO_PREFETCH */ + + +/* ========================================== + * XXH3 default settings + * ========================================== */ + +#define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */ + +#if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN) +# error "default keyset is not large enough" +#endif + +XXH_ALIGN(64) static const xxh_u8 kSecret[XXH_SECRET_DEFAULT_SIZE] = { + 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, + 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, + 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, + 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, + 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, + 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, + 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, + 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, + + 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, + 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, + 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, + 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, +}; + +/* + * GCC for x86 has a tendency to use SSE in this loop. While it + * successfully avoids swapping (as MUL overwrites EAX and EDX), it + * slows it down because instead of free register swap shifts, it + * must use pshufd and punpckl/hd. + * + * To prevent this, we use this attribute to shut off SSE. + */ +#if defined(__GNUC__) && !defined(__clang__) && defined(__i386__) +__attribute__((__target__("no-sse"))) +#endif +static XXH128_hash_t +XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) +{ + /* + * GCC/Clang __uint128_t method. + * + * On most 64-bit targets, GCC and Clang define a __uint128_t type. + * This is usually the best way as it usually uses a native long 64-bit + * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64. + * + * Usually. + * + * Despite being a 32-bit platform, Clang (and emscripten) define this + * type despite not having the arithmetic for it. This results in a + * laggy compiler builtin call which calculates a full 128-bit multiply. + * In that case it is best to use the portable one. + * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 + */ +#if defined(__GNUC__) && !defined(__wasm__) \ + && defined(__SIZEOF_INT128__) \ + || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) + + __uint128_t product = (__uint128_t)lhs * (__uint128_t)rhs; + XXH128_hash_t const r128 = { (xxh_u64)(product), (xxh_u64)(product >> 64) }; + return r128; + + /* + * MSVC for x64's _umul128 method. + * + * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct); + * + * This compiles to single operand MUL on x64. + */ +#elif defined(_M_X64) || defined(_M_IA64) + +#ifndef _MSC_VER +# pragma intrinsic(_umul128) +#endif + xxh_u64 product_high; + xxh_u64 const product_low = _umul128(lhs, rhs, &product_high); + XXH128_hash_t const r128 = { product_low, product_high }; + return r128; + +#else + /* + * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. + * + * This is a fast and simple grade school multiply, which is shown + * below with base 10 arithmetic instead of base 0x100000000. + * + * 9 3 // D2 lhs = 93 + * x 7 5 // D2 rhs = 75 + * ---------- + * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) + * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) + * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) + * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) + * --------- + * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 + * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 + * --------- + * 6 9 7 5 + * + * The reasons for adding the products like this are: + * 1. It avoids manual carry tracking. Just like how + * (9 * 9) + 9 + 9 = 99, the same applies with this for + * UINT64_MAX. This avoids a lot of complexity. + * + * 2. It hints for, and on Clang, compiles to, the powerful UMAAL + * instruction available in ARMv6+ A32/T32, which is shown below: + * + * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm) + * { + * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm; + * *RdLo = (xxh_u32)(product & 0xFFFFFFFF); + * *RdHi = (xxh_u32)(product >> 32); + * } + * + * This instruction was designed for efficient long multiplication, + * and allows this to be calculated in only 4 instructions which + * is comparable to some 64-bit ALUs. + * + * 3. It isn't terrible on other platforms. Usually this will be + * a couple of 32-bit ADD/ADCs. + */ + + /* First calculate all of the cross products. */ + xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); + xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); + xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); + xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); + + /* Now add the products together. These will never overflow. */ + xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; + xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; + xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); + + XXH128_hash_t r128 = { lower, upper }; + return r128; +#endif +} + +/* + * We want to keep the attribute here because a target switch + * disables inlining. + * + * Does a 64-bit to 128-bit multiply, then XOR folds it. + * The reason for the separate function is to prevent passing + * too many structs around by value. This will hopefully inline + * the multiply, but we don't force it. + */ +#if defined(__GNUC__) && !defined(__clang__) && defined(__i386__) +__attribute__((__target__("no-sse"))) +#endif +static xxh_u64 +XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) +{ + XXH128_hash_t product = XXH_mult64to128(lhs, rhs); + return product.low64 ^ product.high64; +} + + +static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) +{ + h64 ^= h64 >> 37; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +/* ========================================== + * Short keys + * ========================================== */ + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + { xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combined = ((xxh_u32)c1) | (((xxh_u32)c2) << 8) | (((xxh_u32)c3) << 16) | (((xxh_u32)len) << 24); + xxh_u64 const keyed = (xxh_u64)combined ^ (XXH_readLE32(secret) + seed); + xxh_u64 const mixed = keyed * PRIME64_1; + return XXH3_avalanche(mixed); + } +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len <= 8); + { xxh_u32 const input_lo = XXH_readLE32(input); + xxh_u32 const input_hi = XXH_readLE32(input + len - 4); + xxh_u64 const input_64 = input_lo | ((xxh_u64)input_hi << 32); + xxh_u64 const keyed = input_64 ^ (XXH_readLE64(secret) + seed); + xxh_u64 const mix64 = len + ((keyed ^ (keyed >> 51)) * PRIME32_1); + return XXH3_avalanche((mix64 ^ (mix64 >> 47)) * PRIME64_2); + } +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(9 <= len && len <= 16); + { xxh_u64 const input_lo = XXH_readLE64(input) ^ (XXH_readLE64(secret) + seed); + xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ (XXH_readLE64(secret + 8) - seed); + xxh_u64 const acc = len + (input_lo + input_hi) + XXH3_mul128_fold64(input_lo, input_hi); + return XXH3_avalanche(acc); + } +} + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(len <= 16); + { if (len > 8) return XXH3_len_9to16_64b(input, len, secret, seed); + if (len >= 4) return XXH3_len_4to8_64b(input, len, secret, seed); + if (len) return XXH3_len_1to3_64b(input, len, secret, seed); + return 0; + } +} + + +/* === Long Keys === */ + +#define STRIPE_LEN 64 +#define XXH_SECRET_CONSUME_RATE 8 /* nb of secret bytes consumed at each accumulation */ +#define ACC_NB (STRIPE_LEN / sizeof(xxh_u64)) + +typedef enum { XXH3_acc_64bits, XXH3_acc_128bits } XXH3_accWidth_e; + +XXH_FORCE_INLINE void +XXH3_accumulate_512( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret, + XXH3_accWidth_e accWidth) +{ +#if (XXH_VECTOR == XXH_AVX2) + + XXH_ASSERT((((size_t)acc) & 31) == 0); + { XXH_ALIGN(32) __m256i* const xacc = (__m256i *) acc; + const __m256i* const xinput = (const __m256i *) input; /* not really aligned, just for ptr arithmetic, and because _mm256_loadu_si256() requires this type */ + const __m256i* const xsecret = (const __m256i *) secret; /* not really aligned, just for ptr arithmetic, and because _mm256_loadu_si256() requires this type */ + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(__m256i); i++) { + __m256i const data_vec = _mm256_loadu_si256 (xinput+i); + __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); + __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); /* uint32 dk[8] = {d0+k0, d1+k1, d2+k2, d3+k3, ...} */ + __m256i const product = _mm256_mul_epu32 (data_key, _mm256_shuffle_epi32 (data_key, 0x31)); /* uint64 mul[4] = {dk0*dk1, dk2*dk3, ...} */ + if (accWidth == XXH3_acc_128bits) { + __m256i const data_swap = _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2)); + __m256i const sum = _mm256_add_epi64(xacc[i], data_swap); + xacc[i] = _mm256_add_epi64(product, sum); + } else { /* XXH3_acc_64bits */ + __m256i const sum = _mm256_add_epi64(xacc[i], data_vec); + xacc[i] = _mm256_add_epi64(product, sum); + } + } } + +#elif (XXH_VECTOR == XXH_SSE2) + + XXH_ASSERT((((size_t)acc) & 15) == 0); + { XXH_ALIGN(16) __m128i* const xacc = (__m128i *) acc; + const __m128i* const xinput = (const __m128i *) input; /* not really aligned, just for ptr arithmetic, and because _mm_loadu_si128() requires this type */ + const __m128i* const xsecret = (const __m128i *) secret; /* not really aligned, just for ptr arithmetic, and because _mm_loadu_si128() requires this type */ + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(__m128i); i++) { + __m128i const data_vec = _mm_loadu_si128 (xinput+i); + __m128i const key_vec = _mm_loadu_si128 (xsecret+i); + __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); /* uint32 dk[8] = {d0+k0, d1+k1, d2+k2, d3+k3, ...} */ + __m128i const product = _mm_mul_epu32 (data_key, _mm_shuffle_epi32 (data_key, 0x31)); /* uint64 mul[4] = {dk0*dk1, dk2*dk3, ...} */ + if (accWidth == XXH3_acc_128bits) { + __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2)); + __m128i const sum = _mm_add_epi64(xacc[i], data_swap); + xacc[i] = _mm_add_epi64(product, sum); + } else { /* XXH3_acc_64bits */ + __m128i const sum = _mm_add_epi64(xacc[i], data_vec); + xacc[i] = _mm_add_epi64(product, sum); + } + } } + +#elif (XXH_VECTOR == XXH_NEON) + + XXH_ASSERT((((size_t)acc) & 15) == 0); + { + XXH_ALIGN(16) uint64x2_t* const xacc = (uint64x2_t *) acc; + /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */ + uint8_t const* const xinput = (const uint8_t *) input; + uint8_t const* const xsecret = (const uint8_t *) secret; + + size_t i; + for (i=0; i < STRIPE_LEN / sizeof(uint64x2_t); i++) { +#if !defined(__aarch64__) && !defined(__arm64__) && defined(__GNUC__) /* ARM32-specific hack */ + /* vzip on ARMv7 Clang generates a lot of vmovs (technically vorrs) without this. + * vzip on 32-bit ARM NEON will overwrite the original register, and I think that Clang + * assumes I don't want to destroy it and tries to make a copy. This slows down the code + * a lot. + * aarch64 not only uses an entirely different syntax, but it requires three + * instructions... + * ext v1.16B, v0.16B, #8 // select high bits because aarch64 can't address them directly + * zip1 v3.2s, v0.2s, v1.2s // first zip + * zip2 v2.2s, v0.2s, v1.2s // second zip + * ...to do what ARM does in one: + * vzip.32 d0, d1 // Interleave high and low bits and overwrite. */ + + /* data_vec = xsecret[i]; */ + uint8x16_t const data_vec = vld1q_u8(xinput + (i * 16)); + /* key_vec = xsecret[i]; */ + uint8x16_t const key_vec = vld1q_u8(xsecret + (i * 16)); + /* data_key = data_vec ^ key_vec; */ + uint32x4_t data_key; + + if (accWidth == XXH3_acc_64bits) { + /* Add first to prevent register swaps */ + /* xacc[i] += data_vec; */ + xacc[i] = vaddq_u64 (xacc[i], vreinterpretq_u64_u8(data_vec)); + } else { /* XXH3_acc_128bits */ + /* xacc[i] += swap(data_vec); */ + /* can probably be optimized better */ + uint64x2_t const data64 = vreinterpretq_u64_u8(data_vec); + uint64x2_t const swapped= vextq_u64(data64, data64, 1); + xacc[i] = vaddq_u64 (xacc[i], swapped); + } + + data_key = vreinterpretq_u32_u8(veorq_u8(data_vec, key_vec)); + + /* Here's the magic. We use the quirkiness of vzip to shuffle data_key in place. + * shuffle: data_key[0, 1, 2, 3] = data_key[0, 2, 1, 3] */ + __asm__("vzip.32 %e0, %f0" : "+w" (data_key)); + /* xacc[i] += (uint64x2_t) data_key[0, 1] * (uint64x2_t) data_key[2, 3]; */ + xacc[i] = vmlal_u32(xacc[i], vget_low_u32(data_key), vget_high_u32(data_key)); + +#else + /* On aarch64, vshrn/vmovn seems to be equivalent to, if not faster than, the vzip method. */ + + /* data_vec = xsecret[i]; */ + uint8x16_t const data_vec = vld1q_u8(xinput + (i * 16)); + /* key_vec = xsecret[i]; */ + uint8x16_t const key_vec = vld1q_u8(xsecret + (i * 16)); + /* data_key = data_vec ^ key_vec; */ + uint64x2_t const data_key = vreinterpretq_u64_u8(veorq_u8(data_vec, key_vec)); + /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF); */ + uint32x2_t const data_key_lo = vmovn_u64 (data_key); + /* data_key_hi = (uint32x2_t) (data_key >> 32); */ + uint32x2_t const data_key_hi = vshrn_n_u64 (data_key, 32); + if (accWidth == XXH3_acc_64bits) { + /* xacc[i] += data_vec; */ + xacc[i] = vaddq_u64 (xacc[i], vreinterpretq_u64_u8(data_vec)); + } else { /* XXH3_acc_128bits */ + /* xacc[i] += swap(data_vec); */ + uint64x2_t const data64 = vreinterpretq_u64_u8(data_vec); + uint64x2_t const swapped= vextq_u64(data64, data64, 1); + xacc[i] = vaddq_u64 (xacc[i], swapped); + } + /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */ + xacc[i] = vmlal_u32 (xacc[i], data_key_lo, data_key_hi); + +#endif + } + } + +#elif (XXH_VECTOR == XXH_VSX) + U64x2* const xacc = (U64x2*) acc; /* presumed aligned */ + U64x2 const* const xinput = (U64x2 const*) input; /* no alignment restriction */ + U64x2 const* const xsecret = (U64x2 const*) secret; /* no alignment restriction */ + U64x2 const v32 = { 32, 32 }; +#if XXH_VSX_BE + U8x16 const vXorSwap = { 0x07, 0x16, 0x25, 0x34, 0x43, 0x52, 0x61, 0x70, + 0x8F, 0x9E, 0xAD, 0xBC, 0xCB, 0xDA, 0xE9, 0xF8 }; +#endif + size_t i; + for (i = 0; i < STRIPE_LEN / sizeof(U64x2); i++) { + /* data_vec = xinput[i]; */ + /* key_vec = xsecret[i]; */ +#if XXH_VSX_BE + /* byteswap */ + U64x2 const data_vec = XXH_vec_revb(vec_vsx_ld(0, xinput + i)); + U64x2 const key_raw = vec_vsx_ld(0, xsecret + i); + /* See comment above. data_key = data_vec ^ swap(xsecret[i]); */ + U64x2 const data_key = (U64x2)XXH_vec_permxor((U8x16)data_vec, (U8x16)key_raw, vXorSwap); +#else + U64x2 const data_vec = vec_vsx_ld(0, xinput + i); + U64x2 const key_vec = vec_vsx_ld(0, xsecret + i); + U64x2 const data_key = data_vec ^ key_vec; +#endif + /* shuffled = (data_key << 32) | (data_key >> 32); */ + U32x4 const shuffled = (U32x4)vec_rl(data_key, v32); + /* product = ((U64x2)data_key & 0xFFFFFFFF) * ((U64x2)shuffled & 0xFFFFFFFF); */ + U64x2 const product = XXH_vec_mulo((U32x4)data_key, shuffled); + xacc[i] += product; + + if (accWidth == XXH3_acc_64bits) { + xacc[i] += data_vec; + } else { /* XXH3_acc_128bits */ + /* swap high and low halves */ + U64x2 const data_swapped = vec_xxpermdi(data_vec, data_vec, 2); + xacc[i] += data_swapped; + } + } + +#else /* scalar variant of Accumulator - universal */ + + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned on 32-bytes boundaries, little hint for the auto-vectorizer */ + const xxh_u8* const xinput = (const xxh_u8*) input; /* no alignment restriction */ + const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ + size_t i; + XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); + for (i=0; i < ACC_NB; i++) { + xxh_u64 const data_val = XXH_readLE64(xinput + 8*i); + xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i*8); + + if (accWidth == XXH3_acc_64bits) { + xacc[i] += data_val; + } else { + xacc[i ^ 1] += data_val; /* swap adjacent lanes */ + } + xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); + } +#endif +} + +XXH_FORCE_INLINE void +XXH3_scrambleAcc(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ +#if (XXH_VECTOR == XXH_AVX2) + + XXH_ASSERT((((size_t)acc) & 31) == 0); + { XXH_ALIGN(32) __m256i* const xacc = (__m256i*) acc; + const __m256i* const xsecret = (const __m256i *) secret; /* not really aligned, just for ptr arithmetic, and because _mm256_loadu_si256() requires this argument type */ + const __m256i prime32 = _mm256_set1_epi32((int)PRIME32_1); + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(__m256i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m256i const acc_vec = xacc[i]; + __m256i const shifted = _mm256_srli_epi64 (acc_vec, 47); + __m256i const data_vec = _mm256_xor_si256 (acc_vec, shifted); + /* xacc[i] ^= xsecret; */ + __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); + __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); + + /* xacc[i] *= PRIME32_1; */ + __m256i const data_key_hi = _mm256_shuffle_epi32 (data_key, 0x31); + __m256i const prod_lo = _mm256_mul_epu32 (data_key, prime32); + __m256i const prod_hi = _mm256_mul_epu32 (data_key_hi, prime32); + xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32)); + } + } + +#elif (XXH_VECTOR == XXH_SSE2) + + XXH_ASSERT((((size_t)acc) & 15) == 0); + { XXH_ALIGN(16) __m128i* const xacc = (__m128i*) acc; + const __m128i* const xsecret = (const __m128i *) secret; /* not really aligned, just for ptr arithmetic, and because _mm_loadu_si128() requires this argument type */ + const __m128i prime32 = _mm_set1_epi32((int)PRIME32_1); + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(__m128i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m128i const acc_vec = xacc[i]; + __m128i const shifted = _mm_srli_epi64 (acc_vec, 47); + __m128i const data_vec = _mm_xor_si128 (acc_vec, shifted); + /* xacc[i] ^= xsecret; */ + __m128i const key_vec = _mm_loadu_si128 (xsecret+i); + __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); + + /* xacc[i] *= PRIME32_1; */ + __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, 0x31); + __m128i const prod_lo = _mm_mul_epu32 (data_key, prime32); + __m128i const prod_hi = _mm_mul_epu32 (data_key_hi, prime32); + xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)); + } + } + +#elif (XXH_VECTOR == XXH_NEON) + + XXH_ASSERT((((size_t)acc) & 15) == 0); + + { uint64x2_t* const xacc = (uint64x2_t*) acc; + uint8_t const* const xsecret = (uint8_t const*) secret; + uint32x2_t const prime = vdup_n_u32 (PRIME32_1); + + size_t i; + for (i=0; i < STRIPE_LEN/sizeof(uint64x2_t); i++) { + /* data_vec = xacc[i] ^ (xacc[i] >> 47); */ + uint64x2_t const acc_vec = xacc[i]; + uint64x2_t const shifted = vshrq_n_u64 (acc_vec, 47); + uint64x2_t const data_vec = veorq_u64 (acc_vec, shifted); + + /* key_vec = xsecret[i]; */ + uint32x4_t const key_vec = vreinterpretq_u32_u8(vld1q_u8(xsecret + (i * 16))); + /* data_key = data_vec ^ key_vec; */ + uint32x4_t const data_key = veorq_u32 (vreinterpretq_u32_u64(data_vec), key_vec); + /* shuffled = { data_key[0, 2], data_key[1, 3] }; */ + uint32x2x2_t const shuffled = vzip_u32 (vget_low_u32(data_key), vget_high_u32(data_key)); + + /* data_key *= PRIME32_1 */ + + /* prod_hi = (data_key >> 32) * PRIME32_1; */ + uint64x2_t const prod_hi = vmull_u32 (shuffled.val[1], prime); + /* xacc[i] = prod_hi << 32; */ + xacc[i] = vshlq_n_u64(prod_hi, 32); + /* xacc[i] += (prod_hi & 0xFFFFFFFF) * PRIME32_1; */ + xacc[i] = vmlal_u32(xacc[i], shuffled.val[0], prime); + } } + +#elif (XXH_VECTOR == XXH_VSX) + + U64x2* const xacc = (U64x2*) acc; + const U64x2* const xsecret = (const U64x2*) secret; + /* constants */ + U64x2 const v32 = { 32, 32 }; + U64x2 const v47 = { 47, 47 }; + U32x4 const prime = { PRIME32_1, PRIME32_1, PRIME32_1, PRIME32_1 }; + size_t i; +#if XXH_VSX_BE + /* endian swap */ + U8x16 const vXorSwap = { 0x07, 0x16, 0x25, 0x34, 0x43, 0x52, 0x61, 0x70, + 0x8F, 0x9E, 0xAD, 0xBC, 0xCB, 0xDA, 0xE9, 0xF8 }; +#endif + for (i = 0; i < STRIPE_LEN / sizeof(U64x2); i++) { + U64x2 const acc_vec = xacc[i]; + U64x2 const data_vec = acc_vec ^ (acc_vec >> v47); + /* key_vec = xsecret[i]; */ +#if XXH_VSX_BE + /* swap bytes words */ + U64x2 const key_raw = vec_vsx_ld(0, xsecret + i); + U64x2 const data_key = (U64x2)XXH_vec_permxor((U8x16)data_vec, (U8x16)key_raw, vXorSwap); +#else + U64x2 const key_vec = vec_vsx_ld(0, xsecret + i); + U64x2 const data_key = data_vec ^ key_vec; +#endif + + /* data_key *= PRIME32_1 */ + + /* prod_lo = ((U64x2)data_key & 0xFFFFFFFF) * ((U64x2)prime & 0xFFFFFFFF); */ + U64x2 const prod_even = XXH_vec_mule((U32x4)data_key, prime); + /* prod_hi = ((U64x2)data_key >> 32) * ((U64x2)prime >> 32); */ + U64x2 const prod_odd = XXH_vec_mulo((U32x4)data_key, prime); + xacc[i] = prod_odd + (prod_even << v32); + } + +#else /* scalar variant of Scrambler - universal */ + + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned on 32-bytes boundaries, little hint for the auto-vectorizer */ + const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ + size_t i; + XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0); + for (i=0; i < ACC_NB; i++) { + xxh_u64 const key64 = XXH_readLE64(xsecret + 8*i); + xxh_u64 acc64 = xacc[i]; + acc64 ^= acc64 >> 47; + acc64 ^= key64; + acc64 *= PRIME32_1; + xacc[i] = acc64; + } + +#endif +} + +#define XXH_PREFETCH_DIST 384 + +/* assumption : nbStripes will not overflow secret size */ +XXH_FORCE_INLINE void +XXH3_accumulate( xxh_u64* XXH_RESTRICT acc, + const xxh_u8* XXH_RESTRICT input, + const xxh_u8* XXH_RESTRICT secret, + size_t nbStripes, + XXH3_accWidth_e accWidth) +{ + size_t n; + for (n = 0; n < nbStripes; n++ ) { + const xxh_u8* const in = input + n*STRIPE_LEN; + XXH_PREFETCH(in + XXH_PREFETCH_DIST); + XXH3_accumulate_512(acc, + in, + secret + n*XXH_SECRET_CONSUME_RATE, + accWidth); + } +} + +/* note : clang auto-vectorizes well in SS2 mode _if_ this function is `static`, + * and doesn't auto-vectorize it at all if it is `FORCE_INLINE`. + * However, it auto-vectorizes better AVX2 if it is `FORCE_INLINE` + * Pretty much every other modes and compilers prefer `FORCE_INLINE`. + */ + +#if defined(__clang__) && (XXH_VECTOR==0) && !defined(__AVX2__) && !defined(__arm__) && !defined(__thumb__) +static void +#else +XXH_FORCE_INLINE void +#endif +XXH3_hashLong_internal_loop( xxh_u64* XXH_RESTRICT acc, + const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH3_accWidth_e accWidth) +{ + size_t const nb_rounds = (secretSize - STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; + size_t const block_len = STRIPE_LEN * nb_rounds; + size_t const nb_blocks = len / block_len; + + size_t n; + + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + + for (n = 0; n < nb_blocks; n++) { + XXH3_accumulate(acc, input + n*block_len, secret, nb_rounds, accWidth); + XXH3_scrambleAcc(acc, secret + secretSize - STRIPE_LEN); + } + + /* last partial block */ + XXH_ASSERT(len > STRIPE_LEN); + { size_t const nbStripes = (len - (block_len * nb_blocks)) / STRIPE_LEN; + XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); + XXH3_accumulate(acc, input + nb_blocks*block_len, secret, nbStripes, accWidth); + + /* last stripe */ + if (len & (STRIPE_LEN - 1)) { + const xxh_u8* const p = input + len - STRIPE_LEN; +#define XXH_SECRET_LASTACC_START 7 /* do not align on 8, so that secret is different from scrambler */ + XXH3_accumulate_512(acc, p, secret + secretSize - STRIPE_LEN - XXH_SECRET_LASTACC_START, accWidth); + } } +} + +XXH_FORCE_INLINE xxh_u64 +XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret) +{ + return XXH3_mul128_fold64( + acc[0] ^ XXH_readLE64(secret), + acc[1] ^ XXH_readLE64(secret+8) ); +} + +static XXH64_hash_t +XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start) +{ + xxh_u64 result64 = start; + + result64 += XXH3_mix2Accs(acc+0, secret + 0); + result64 += XXH3_mix2Accs(acc+2, secret + 16); + result64 += XXH3_mix2Accs(acc+4, secret + 32); + result64 += XXH3_mix2Accs(acc+6, secret + 48); + + return XXH3_avalanche(result64); +} + +#define XXH3_INIT_ACC { PRIME32_3, PRIME64_1, PRIME64_2, PRIME64_3, \ + PRIME64_4, PRIME32_2, PRIME64_5, PRIME32_1 }; + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_hashLong_internal(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize) +{ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[ACC_NB] = XXH3_INIT_ACC; + + XXH3_hashLong_internal_loop(acc, input, len, secret, secretSize, XXH3_acc_64bits); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); +#define XXH_SECRET_MERGEACCS_START 11 /* do not align on 8, so that secret is different from accumulator */ + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * PRIME64_1); +} + + +XXH_NO_INLINE XXH64_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_64b_defaultSecret(const xxh_u8* XXH_RESTRICT input, size_t len) +{ + return XXH3_hashLong_internal(input, len, kSecret, sizeof(kSecret)); +} + +XXH_NO_INLINE XXH64_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_64b_withSecret(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize) +{ + return XXH3_hashLong_internal(input, len, secret, secretSize); +} + + +XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) +{ + if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); + memcpy(dst, &v64, sizeof(v64)); +} + +/* XXH3_initCustomSecret() : + * destination `customSecret` is presumed allocated and same size as `kSecret`. + */ +XXH_FORCE_INLINE void XXH3_initCustomSecret(xxh_u8* customSecret, xxh_u64 seed64) +{ + int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; + int i; + + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); + + for (i=0; i < nbRounds; i++) { + XXH_writeLE64(customSecret + 16*i, XXH_readLE64(kSecret + 16*i) + seed64); + XXH_writeLE64(customSecret + 16*i + 8, XXH_readLE64(kSecret + 16*i + 8) - seed64); + } +} + + +/* XXH3_hashLong_64b_withSeed() : + * Generate a custom key, + * based on alteration of default kSecret with the seed, + * and then use this key for long mode hashing. + * This operation is decently fast but nonetheless costs a little bit of time. + * Try to avoid it whenever possible (typically when seed==0). + */ +XXH_NO_INLINE XXH64_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_64b_withSeed(const xxh_u8* input, size_t len, XXH64_hash_t seed) +{ + XXH_ALIGN(8) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + if (seed==0) return XXH3_hashLong_64b_defaultSecret(input, len); + XXH3_initCustomSecret(secret, seed); + return XXH3_hashLong_internal(input, len, secret, sizeof(secret)); +} + + +XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, + const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64) +{ + xxh_u64 const input_lo = XXH_readLE64(input); + xxh_u64 const input_hi = XXH_readLE64(input+8); + return XXH3_mul128_fold64( + input_lo ^ (XXH_readLE64(secret) + seed64), + input_hi ^ (XXH_readLE64(secret+8) - seed64) ); +} + + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { xxh_u64 acc = len * PRIME64_1; + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc += XXH3_mix16B(input+48, secret+96, seed); + acc += XXH3_mix16B(input+len-64, secret+112, seed); + } + acc += XXH3_mix16B(input+32, secret+64, seed); + acc += XXH3_mix16B(input+len-48, secret+80, seed); + } + acc += XXH3_mix16B(input+16, secret+32, seed); + acc += XXH3_mix16B(input+len-32, secret+48, seed); + } + acc += XXH3_mix16B(input+0, secret+0, seed); + acc += XXH3_mix16B(input+len-16, secret+16, seed); + + return XXH3_avalanche(acc); + } +} + +#define XXH3_MIDSIZE_MAX 240 + +XXH_NO_INLINE XXH64_hash_t +XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + + #define XXH3_MIDSIZE_STARTOFFSET 3 + #define XXH3_MIDSIZE_LASTOFFSET 17 + + { xxh_u64 acc = len * PRIME64_1; + int const nbRounds = (int)len / 16; + int i; + for (i=0; i<8; i++) { + acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed); + } + acc = XXH3_avalanche(acc); + XXH_ASSERT(nbRounds >= 8); + for (i=8 ; i < nbRounds; i++) { + acc += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); + } + /* last bytes */ + acc += XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); + return XXH3_avalanche(acc); + } +} + +/* === Public entry point === */ + +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* input, size_t len) +{ + if (len <= 16) return XXH3_len_0to16_64b((const xxh_u8*)input, len, kSecret, 0); + if (len <= 128) return XXH3_len_17to128_64b((const xxh_u8*)input, len, kSecret, sizeof(kSecret), 0); + if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_64b((const xxh_u8*)input, len, kSecret, sizeof(kSecret), 0); + return XXH3_hashLong_64b_defaultSecret((const xxh_u8*)input, len); +} + +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + /* if an action must be taken should `secret` conditions not be respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash */ + if (len <= 16) return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, 0); + if (len <= 128) return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, 0); + if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, 0); + return XXH3_hashLong_64b_withSecret((const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize); +} + +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) +{ + if (len <= 16) return XXH3_len_0to16_64b((const xxh_u8*)input, len, kSecret, seed); + if (len <= 128) return XXH3_len_17to128_64b((const xxh_u8*)input, len, kSecret, sizeof(kSecret), seed); + if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_64b((const xxh_u8*)input, len, kSecret, sizeof(kSecret), seed); + return XXH3_hashLong_64b_withSeed((const xxh_u8*)input, len, seed); +} + +/* === XXH3 streaming === */ + +XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) +{ + return (XXH3_state_t*)XXH_malloc(sizeof(XXH3_state_t)); +} + +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void +XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state) +{ + memcpy(dst_state, src_state, sizeof(*dst_state)); +} + +static void +XXH3_64bits_reset_internal(XXH3_state_t* statePtr, + XXH64_hash_t seed, + const xxh_u8* secret, size_t secretSize) +{ + XXH_ASSERT(statePtr != NULL); + memset(statePtr, 0, sizeof(*statePtr)); + statePtr->acc[0] = PRIME32_3; + statePtr->acc[1] = PRIME64_1; + statePtr->acc[2] = PRIME64_2; + statePtr->acc[3] = PRIME64_3; + statePtr->acc[4] = PRIME64_4; + statePtr->acc[5] = PRIME32_2; + statePtr->acc[6] = PRIME64_5; + statePtr->acc[7] = PRIME32_1; + statePtr->seed = seed; + XXH_ASSERT(secret != NULL); + statePtr->secret = secret; + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + statePtr->secretLimit = (XXH32_hash_t)(secretSize - STRIPE_LEN); + statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset(XXH3_state_t* statePtr) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_64bits_reset_internal(statePtr, 0, kSecret, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_64bits_reset_internal(statePtr, 0, (const xxh_u8*)secret, secretSize); + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_64bits_reset_internal(statePtr, seed, kSecret, XXH_SECRET_DEFAULT_SIZE); + XXH3_initCustomSecret(statePtr->customSecret, seed); + statePtr->secret = statePtr->customSecret; + return XXH_OK; +} + +XXH_FORCE_INLINE void +XXH3_consumeStripes( xxh_u64* acc, + XXH32_hash_t* nbStripesSoFarPtr, XXH32_hash_t nbStripesPerBlock, + const xxh_u8* input, size_t totalStripes, + const xxh_u8* secret, size_t secretLimit, + XXH3_accWidth_e accWidth) +{ + XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock); + if (nbStripesPerBlock - *nbStripesSoFarPtr <= totalStripes) { + /* need a scrambling operation */ + size_t const nbStripes = nbStripesPerBlock - *nbStripesSoFarPtr; + XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, accWidth); + XXH3_scrambleAcc(acc, secret + secretLimit); + XXH3_accumulate(acc, input + nbStripes * STRIPE_LEN, secret, totalStripes - nbStripes, accWidth); + *nbStripesSoFarPtr = (XXH32_hash_t)(totalStripes - nbStripes); + } else { + XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, totalStripes, accWidth); + *nbStripesSoFarPtr += (XXH32_hash_t)totalStripes; + } +} + +XXH_FORCE_INLINE XXH_errorcode +XXH3_update(XXH3_state_t* state, const xxh_u8* input, size_t len, XXH3_accWidth_e accWidth) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const xxh_u8* const bEnd = input + len; + + state->totalLen += len; + + if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { /* fill in tmp buffer */ + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t)len; + return XXH_OK; + } + /* input now > XXH3_INTERNALBUFFER_SIZE */ + + #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / STRIPE_LEN) + XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % STRIPE_LEN == 0); /* clean multiple */ + + if (state->bufferedSize) { /* some input within internal buffer: fill then consume it */ + size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; + XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); + input += loadSize; + XXH3_consumeStripes(state->acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, XXH3_INTERNALBUFFER_STRIPES, + state->secret, state->secretLimit, + accWidth); + state->bufferedSize = 0; + } + + /* consume input by full buffer quantities */ + if (input+XXH3_INTERNALBUFFER_SIZE <= bEnd) { + const xxh_u8* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE; + do { + XXH3_consumeStripes(state->acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + input, XXH3_INTERNALBUFFER_STRIPES, + state->secret, state->secretLimit, + accWidth); + input += XXH3_INTERNALBUFFER_SIZE; + } while (input<=limit); + } + + if (input < bEnd) { /* some remaining input input : buffer it */ + XXH_memcpy(state->buffer, input, (size_t)(bEnd-input)); + state->bufferedSize = (XXH32_hash_t)(bEnd-input); + } + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len) +{ + return XXH3_update(state, (const xxh_u8*)input, len, XXH3_acc_64bits); +} + + +XXH_FORCE_INLINE void +XXH3_digest_long (XXH64_hash_t* acc, const XXH3_state_t* state, XXH3_accWidth_e accWidth) +{ + memcpy(acc, state->acc, sizeof(state->acc)); /* digest locally, state remains unaltered, and can continue ingesting more input afterwards */ + if (state->bufferedSize >= STRIPE_LEN) { + size_t const totalNbStripes = state->bufferedSize / STRIPE_LEN; + XXH32_hash_t nbStripesSoFar = state->nbStripesSoFar; + XXH3_consumeStripes(acc, + &nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, totalNbStripes, + state->secret, state->secretLimit, + accWidth); + if (state->bufferedSize % STRIPE_LEN) { /* one last partial stripe */ + XXH3_accumulate_512(acc, + state->buffer + state->bufferedSize - STRIPE_LEN, + state->secret + state->secretLimit - XXH_SECRET_LASTACC_START, + accWidth); + } + } else { /* bufferedSize < STRIPE_LEN */ + if (state->bufferedSize) { /* one last stripe */ + xxh_u8 lastStripe[STRIPE_LEN]; + size_t const catchupSize = STRIPE_LEN - state->bufferedSize; + memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); + memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); + XXH3_accumulate_512(acc, + lastStripe, + state->secret + state->secretLimit - XXH_SECRET_LASTACC_START, + accWidth); + } } +} + +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state) +{ + if (state->totalLen > XXH3_MIDSIZE_MAX) { + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[ACC_NB]; + XXH3_digest_long(acc, state, XXH3_acc_64bits); + return XXH3_mergeAccs(acc, state->secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)state->totalLen * PRIME64_1); + } + /* len <= XXH3_MIDSIZE_MAX : short code */ + if (state->seed) + return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); + return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), state->secret, state->secretLimit + STRIPE_LEN); +} + +/* ========================================== + * XXH3 128 bits (=> XXH128) + * ========================================== */ + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + { xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combinedl = ((xxh_u32)c1) + (((xxh_u32)c2) << 8) + (((xxh_u32)c3) << 16) + (((xxh_u32)len) << 24); + xxh_u32 const combinedh = XXH_swap32(combinedl); + xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ (XXH_readLE32(secret) + seed); + xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ (XXH_readLE32(secret+4) - seed); + xxh_u64 const mixedl = keyed_lo * PRIME64_1; + xxh_u64 const mixedh = keyed_hi * PRIME64_5; + XXH128_hash_t const h128 = { XXH3_avalanche(mixedl) /*low64*/, XXH3_avalanche(mixedh) /*high64*/ }; + return h128; + } +} + + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len <= 8); + { xxh_u32 const input_lo = XXH_readLE32(input); + xxh_u32 const input_hi = XXH_readLE32(input + len - 4); + xxh_u64 const input_64_lo = input_lo + ((xxh_u64)input_hi << 32); + xxh_u64 const input_64_hi = XXH_swap64(input_64_lo); + xxh_u64 const keyed_lo = input_64_lo ^ (XXH_readLE64(secret) + seed); + xxh_u64 const keyed_hi = input_64_hi ^ (XXH_readLE64(secret + 8) - seed); + xxh_u64 const mix64l1 = len + ((keyed_lo ^ (keyed_lo >> 51)) * PRIME32_1); + xxh_u64 const mix64l2 = (mix64l1 ^ (mix64l1 >> 47)) * PRIME64_2; + xxh_u64 const mix64h1 = ((keyed_hi ^ (keyed_hi >> 47)) * PRIME64_1) - len; + xxh_u64 const mix64h2 = (mix64h1 ^ (mix64h1 >> 43)) * PRIME64_4; + { XXH128_hash_t const h128 = { XXH3_avalanche(mix64l2) /*low64*/, XXH3_avalanche(mix64h2) /*high64*/ }; + return h128; + } } +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(9 <= len && len <= 16); + { xxh_u64 const input_lo = XXH_readLE64(input) ^ (XXH_readLE64(secret) + seed); + xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ (XXH_readLE64(secret+8) - seed); + XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi, PRIME64_1); + xxh_u64 const lenContrib = XXH_mult32to64(len, PRIME32_5); + m128.low64 += lenContrib; + m128.high64 += input_hi * PRIME64_1; + m128.low64 ^= (m128.high64 >> 32); + { XXH128_hash_t h128 = XXH_mult64to128(m128.low64, PRIME64_2); + h128.high64 += m128.high64 * PRIME64_2; + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = XXH3_avalanche(h128.high64); + return h128; + } } +} + +/* Assumption : `secret` size is >= 16 + * Note : it should be >= XXH3_SECRET_SIZE_MIN anyway */ +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(len <= 16); + { if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); + if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); + if (len) return XXH3_len_1to3_128b(input, len, secret, seed); + { XXH128_hash_t const h128 = { 0, 0 }; + return h128; + } } +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_hashLong_128b_internal(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize) +{ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[ACC_NB] = XXH3_INIT_ACC; + + XXH3_hashLong_internal_loop(acc, input, len, secret, secretSize, XXH3_acc_128bits); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + { xxh_u64 const low64 = XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * PRIME64_1); + xxh_u64 const high64 = XXH3_mergeAccs(acc, secret + secretSize - sizeof(acc) - XXH_SECRET_MERGEACCS_START, ~((xxh_u64)len * PRIME64_2)); + XXH128_hash_t const h128 = { low64, high64 }; + return h128; + } +} + +XXH_NO_INLINE XXH128_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_128b_defaultSecret(const xxh_u8* input, size_t len) +{ + return XXH3_hashLong_128b_internal(input, len, kSecret, sizeof(kSecret)); +} + +XXH_NO_INLINE XXH128_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_128b_withSecret(const xxh_u8* input, size_t len, + const xxh_u8* secret, size_t secretSize) +{ + return XXH3_hashLong_128b_internal(input, len, secret, secretSize); +} + +XXH_NO_INLINE XXH128_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */ +XXH3_hashLong_128b_withSeed(const xxh_u8* input, size_t len, XXH64_hash_t seed) +{ + XXH_ALIGN(8) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + if (seed == 0) return XXH3_hashLong_128b_defaultSecret(input, len); + XXH3_initCustomSecret(secret, seed); + return XXH3_hashLong_128b_internal(input, len, secret, sizeof(secret)); +} + + +XXH_FORCE_INLINE XXH128_hash_t +XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2, const xxh_u8* secret, XXH64_hash_t seed) +{ + acc.low64 += XXH3_mix16B (input_1, secret+0, seed); + acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); + acc.high64 += XXH3_mix16B (input_2, secret+16, seed); + acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); + return acc; +} + +XXH_NO_INLINE XXH128_hash_t +XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + + { XXH128_hash_t acc; + int const nbRounds = (int)len / 32; + int i; + acc.low64 = len * PRIME64_1; + acc.high64 = 0; + for (i=0; i<4; i++) { + acc = XXH128_mix32B(acc, input+(32*i), input+(32*i)+16, secret+(32*i), seed); + } + acc.low64 = XXH3_avalanche(acc.low64); + acc.high64 = XXH3_avalanche(acc.high64); + XXH_ASSERT(nbRounds >= 4); + for (i=4 ; i < nbRounds; i++) { + acc = XXH128_mix32B(acc, input+(32*i), input+(32*i)+16, secret+XXH3_MIDSIZE_STARTOFFSET+(32*(i-4)), seed); + } + /* last bytes */ + acc = XXH128_mix32B(acc, input + len - 16, input + len - 32, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, 0ULL - seed); + + { xxh_u64 const low64 = acc.low64 + acc.high64; + xxh_u64 const high64 = (acc.low64 * PRIME64_1) + (acc.high64 * PRIME64_4) + ((len - seed) * PRIME64_2); + XXH128_hash_t const h128 = { XXH3_avalanche(low64), (XXH64_hash_t)0 - XXH3_avalanche(high64) }; + return h128; + } + } +} + + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { XXH128_hash_t acc; + acc.low64 = len * PRIME64_1; + acc.high64 = 0; + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed); + } + acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed); + } + acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed); + } + acc = XXH128_mix32B(acc, input, input+len-16, secret, seed); + { xxh_u64 const low64 = acc.low64 + acc.high64; + xxh_u64 const high64 = (acc.low64 * PRIME64_1) + (acc.high64 * PRIME64_4) + ((len - seed) * PRIME64_2); + XXH128_hash_t const h128 = { XXH3_avalanche(low64), (XXH64_hash_t)0 - XXH3_avalanche(high64) }; + return h128; + } + } +} + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* input, size_t len) +{ + if (len <= 16) return XXH3_len_0to16_128b((const xxh_u8*)input, len, kSecret, 0); + if (len <= 128) return XXH3_len_17to128_128b((const xxh_u8*)input, len, kSecret, sizeof(kSecret), 0); + if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_128b((const xxh_u8*)input, len, kSecret, sizeof(kSecret), 0); + return XXH3_hashLong_128b_defaultSecret((const xxh_u8*)input, len); +} + +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + /* if an action must be taken should `secret` conditions not be respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash */ + if (len <= 16) return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, 0); + if (len <= 128) return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, 0); + if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, 0); + return XXH3_hashLong_128b_withSecret((const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize); +} + +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSeed(const void* input, size_t len, XXH64_hash_t seed) +{ + if (len <= 16) return XXH3_len_0to16_128b((const xxh_u8*)input, len, kSecret, seed); + if (len <= 128) return XXH3_len_17to128_128b((const xxh_u8*)input, len, kSecret, sizeof(kSecret), seed); + if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_128b((const xxh_u8*)input, len, kSecret, sizeof(kSecret), seed); + return XXH3_hashLong_128b_withSeed((const xxh_u8*)input, len, seed); +} + +XXH_PUBLIC_API XXH128_hash_t +XXH128(const void* input, size_t len, XXH64_hash_t seed) +{ + return XXH3_128bits_withSeed(input, len, seed); +} + + +/* === XXH3 128-bit streaming === */ + +/* all the functions are actually the same as for 64-bit streaming variant, + just the reset one is different (different initial acc values for 0,5,6,7), + and near the end of the digest function */ + +static void +XXH3_128bits_reset_internal(XXH3_state_t* statePtr, + XXH64_hash_t seed, + const xxh_u8* secret, size_t secretSize) +{ + XXH3_64bits_reset_internal(statePtr, seed, secret, secretSize); +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset(XXH3_state_t* statePtr) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_128bits_reset_internal(statePtr, 0, kSecret, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_128bits_reset_internal(statePtr, 0, (const xxh_u8*)secret, secretSize); + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_128bits_reset_internal(statePtr, seed, kSecret, XXH_SECRET_DEFAULT_SIZE); + XXH3_initCustomSecret(statePtr->customSecret, seed); + statePtr->secret = statePtr->customSecret; + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_update(XXH3_state_t* state, const void* input, size_t len) +{ + return XXH3_update(state, (const xxh_u8*)input, len, XXH3_acc_128bits); +} + +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state) +{ + if (state->totalLen > XXH3_MIDSIZE_MAX) { + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[ACC_NB]; + XXH3_digest_long(acc, state, XXH3_acc_128bits); + XXH_ASSERT(state->secretLimit + STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + { xxh_u64 const low64 = XXH3_mergeAccs(acc, state->secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)state->totalLen * PRIME64_1); + xxh_u64 const high64 = XXH3_mergeAccs(acc, state->secret + state->secretLimit + STRIPE_LEN - sizeof(acc) - XXH_SECRET_MERGEACCS_START, ~((xxh_u64)state->totalLen * PRIME64_2)); + XXH128_hash_t const h128 = { low64, high64 }; + return h128; + } + } + /* len <= XXH3_MIDSIZE_MAX : short code */ + if (state->seed) + return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); + return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), state->secret, state->secretLimit + STRIPE_LEN); +} + +/* 128-bit utility functions */ + +#include /* memcmp */ + +/* return : 1 is equal, 0 if different */ +XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) +{ + /* note : XXH128_hash_t is compact, it has no padding byte */ + return !(memcmp(&h1, &h2, sizeof(h1))); +} + +/* This prototype is compatible with stdlib's qsort(). + * return : >0 if *h128_1 > *h128_2 + * <0 if *h128_1 < *h128_2 + * =0 if *h128_1 == *h128_2 */ +XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) +{ + XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1; + XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2; + int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64); + /* note : bets that, in most cases, hash values are different */ + if (hcmp) return hcmp; + return (h1.low64 > h2.low64) - (h2.low64 > h1.low64); +} + + +/*====== Canonical representation ======*/ +XXH_PUBLIC_API void +XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) { + hash.high64 = XXH_swap64(hash.high64); + hash.low64 = XXH_swap64(hash.low64); + } + memcpy(dst, &hash.high64, sizeof(hash.high64)); + memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); +} + +XXH_PUBLIC_API XXH128_hash_t +XXH128_hashFromCanonical(const XXH128_canonical_t* src) +{ + XXH128_hash_t h; + h.high64 = XXH_readBE64(src); + h.low64 = XXH_readBE64(src->digest + 8); + return h; +} + + + +#endif /* XXH3_H */ diff --git a/parallel/parallel_src/extern/cista/tools/xxh3/xxhash.c b/parallel/parallel_src/extern/cista/tools/xxh3/xxhash.c new file mode 100755 index 00000000..3f49b7d1 --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/xxh3/xxhash.c @@ -0,0 +1,1110 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* You can contact the author at : +* - xxHash homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* since xxhash.c can be included (via XXH_INLINE_ALL), + * it's good practice to protect it with guard + * in case of multiples inclusions */ +#ifndef XXHASH_C_01393879 +#define XXHASH_C_01393879 + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. + * It can generate buggy code on targets which do not support unaligned memory accesses. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See http://stackoverflow.com/a/32095106/646947 for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if !defined(__clang__) && defined(__GNUC__) && defined(__ARM_FEATURE_UNALIGNED) && defined(__ARM_ARCH) && (__ARM_ARCH == 6) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif !defined(__clang__) && ((defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ + (defined(__GNUC__) && (defined(__ARM_ARCH) && __ARM_ARCH >= 7))) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/*!XXH_ACCEPT_NULL_INPUT_POINTER : + * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault. + * When this macro is enabled, xxHash actively checks input for null pointer. + * It it is, result for null input pointers is the same as a null-length input. + */ +#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ +# define XXH_ACCEPT_NULL_INPUT_POINTER 0 +#endif + +/*!XXH_FORCE_ALIGN_CHECK : + * This is a minor performance trick, only useful with lots of very small keys. + * It means : check for aligned/unaligned input. + * The check costs one initial branch per hash; + * set it to 0 when the input is guaranteed to be aligned, + * or when alignment doesn't matter for performance. + */ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + +/*!XXH_REROLL: + * Whether to reroll XXH32_finalize, and XXH64_finalize, + * instead of using an unrolled jump table/if statement loop. + * + * This is automatically defined on -Os/-Oz on GCC and Clang. */ +#ifndef XXH_REROLL +# if defined(__OPTIMIZE_SIZE__) +# define XXH_REROLL 1 +# else +# define XXH_REROLL 0 +# endif +#endif + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#include /* ULLONG_MAX */ + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define XXH_FORCE_INLINE static __forceinline +# define XXH_NO_INLINE static __declspec(noinline) +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define XXH_FORCE_INLINE static inline __attribute__((always_inline)) +# define XXH_NO_INLINE static __attribute__((noinline)) +# else +# define XXH_FORCE_INLINE static inline +# define XXH_NO_INLINE static +# endif +# else +# define XXH_FORCE_INLINE static +# define XXH_NO_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + + +/* ************************************* +* Debug +***************************************/ +/* DEBUGLEVEL is expected to be defined externally, + * typically through compiler command line. + * Value must be a number. */ +#ifndef DEBUGLEVEL +# define DEBUGLEVEL 0 +#endif + +#if (DEBUGLEVEL>=1) +# include /* note : can still be disabled with NDEBUG */ +# define XXH_ASSERT(c) assert(c) +#else +# define XXH_ASSERT(c) ((void)0) +#endif + +/* note : use after variable declarations */ +#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } + + +/* ************************************* +* Basic Types +***************************************/ +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t xxh_u8; +#else + typedef unsigned char xxh_u8; +#endif +typedef XXH32_hash_t xxh_u32; + + +/* === Memory access === */ + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { xxh_u32 u32; } __attribute__((packed)) unalign; +static xxh_u32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static xxh_u32 XXH_read32(const void* memPtr) +{ + xxh_u32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* === Endianess === */ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN +# if defined(_WIN32) /* Windows is always little endian */ \ + || defined(__LITTLE_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +# define XXH_CPU_LITTLE_ENDIAN 1 +# elif defined(__BIG_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# define XXH_CPU_LITTLE_ENDIAN 0 +# else +static int XXH_isLittleEndian(void) +{ + const union { xxh_u32 u; xxh_u8 c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} +# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() +# endif +#endif + + + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#if !defined(NO_CLANG_BUILTIN) && __has_builtin(__builtin_rotateleft32) && __has_builtin(__builtin_rotateleft64) +# define XXH_rotl32 __builtin_rotateleft32 +# define XXH_rotl64 __builtin_rotateleft64 +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#elif defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +# define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static xxh_u32 XXH_swap32 (xxh_u32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); +} + +static xxh_u32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + +XXH_FORCE_INLINE xxh_u32 +XXH_readLE32_align(const void* ptr, XXH_alignment align) +{ + if (align==XXH_unaligned) { + return XXH_readLE32(ptr); + } else { + return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr); + } +} + + +/* ************************************* +* Misc +***************************************/ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +static const xxh_u32 PRIME32_1 = 0x9E3779B1U; /* 0b10011110001101110111100110110001 */ +static const xxh_u32 PRIME32_2 = 0x85EBCA77U; /* 0b10000101111010111100101001110111 */ +static const xxh_u32 PRIME32_3 = 0xC2B2AE3DU; /* 0b11000010101100101010111000111101 */ +static const xxh_u32 PRIME32_4 = 0x27D4EB2FU; /* 0b00100111110101001110101100101111 */ +static const xxh_u32 PRIME32_5 = 0x165667B1U; /* 0b00010110010101100110011110110001 */ + +static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) +{ + acc += input * PRIME32_2; + acc = XXH_rotl32(acc, 13); + acc *= PRIME32_1; +#if defined(__GNUC__) && defined(__SSE4_1__) && !defined(XXH_ENABLE_AUTOVECTORIZE) + /* UGLY HACK: + * This inline assembly hack forces acc into a normal register. This is the + * only thing that prevents GCC and Clang from autovectorizing the XXH32 loop + * (pragmas and attributes don't work for some resason) without globally + * disabling SSE4.1. + * + * The reason we want to avoid vectorization is because despite working on + * 4 integers at a time, there are multiple factors slowing XXH32 down on + * SSE4: + * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on newer chips!) + * making it slightly slower to multiply four integers at once compared to four + * integers independently. Even when pmulld was fastest, Sandy/Ivy Bridge, it is + * still not worth it to go into SSE just to multiply unless doing a long operation. + * + * - Four instructions are required to rotate, + * movqda tmp, v // not required with VEX encoding + * pslld tmp, 13 // tmp <<= 13 + * psrld v, 19 // x >>= 19 + * por v, tmp // x |= tmp + * compared to one for scalar: + * roll v, 13 // reliably fast across the board + * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason + * + * - Instruction level parallelism is actually more beneficial here because the + * SIMD actually serializes this operation: While v1 is rotating, v2 can load data, + * while v3 can multiply. SSE forces them to operate together. + * + * How this hack works: + * __asm__("" // Declare an assembly block but don't declare any instructions + * : // However, as an Input/Output Operand, + * "+r" // constrain a read/write operand (+) as a general purpose register (r). + * (acc) // and set acc as the operand + * ); + * + * Because of the 'r', the compiler has promised that seed will be in a + * general purpose register and the '+' says that it will be 'read/write', + * so it has to assume it has changed. It is like volatile without all the + * loads and stores. + * + * Since the argument has to be in a normal register (not an SSE register), + * each time XXH32_round is called, it is impossible to vectorize. */ + __asm__("" : "+r" (acc)); +#endif + return acc; +} + +/* mix all bits */ +static xxh_u32 XXH32_avalanche(xxh_u32 h32) +{ + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + return(h32); +} + +#define XXH_get32bits(p) XXH_readLE32_align(p, align) + +static xxh_u32 +XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align) +{ +#define PROCESS1 \ + h32 += (*ptr++) * PRIME32_5; \ + h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; + +#define PROCESS4 \ + h32 += XXH_get32bits(ptr) * PRIME32_3; \ + ptr+=4; \ + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + + /* Compact rerolled version */ + if (XXH_REROLL) { + len &= 15; + while (len >= 4) { + PROCESS4; + len -= 4; + } + while (len > 0) { + PROCESS1; + --len; + } + return XXH32_avalanche(h32); + } else { + switch(len&15) /* or switch(bEnd - p) */ { + case 12: PROCESS4; + /* fallthrough */ + case 8: PROCESS4; + /* fallthrough */ + case 4: PROCESS4; + return XXH32_avalanche(h32); + + case 13: PROCESS4; + /* fallthrough */ + case 9: PROCESS4; + /* fallthrough */ + case 5: PROCESS4; + PROCESS1; + return XXH32_avalanche(h32); + + case 14: PROCESS4; + /* fallthrough */ + case 10: PROCESS4; + /* fallthrough */ + case 6: PROCESS4; + PROCESS1; + PROCESS1; + return XXH32_avalanche(h32); + + case 15: PROCESS4; + /* fallthrough */ + case 11: PROCESS4; + /* fallthrough */ + case 7: PROCESS4; + /* fallthrough */ + case 3: PROCESS1; + /* fallthrough */ + case 2: PROCESS1; + /* fallthrough */ + case 1: PROCESS1; + /* fallthrough */ + case 0: return XXH32_avalanche(h32); + } + XXH_ASSERT(0); + return h32; /* reaching this point is deemed impossible */ + } +} + +XXH_FORCE_INLINE xxh_u32 +XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align) +{ + const xxh_u8* bEnd = input + len; + xxh_u32 h32; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (input==NULL) { + len=0; + bEnd=input=(const xxh_u8*)(size_t)16; + } +#endif + + if (len>=16) { + const xxh_u8* const limit = bEnd - 15; + xxh_u32 v1 = seed + PRIME32_1 + PRIME32_2; + xxh_u32 v2 = seed + PRIME32_2; + xxh_u32 v3 = seed + 0; + xxh_u32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4; + v2 = XXH32_round(v2, XXH_get32bits(input)); input += 4; + v3 = XXH32_round(v3, XXH_get32bits(input)); input += 4; + v4 = XXH32_round(v4, XXH_get32bits(input)); input += 4; + } while (input < limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (xxh_u32)len; + + return XXH32_finalize(h32, input, len&15, align); +} + + +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, (const xxh_u8*)input, len); + return XXH32_digest(&state); + +#else + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); + } } + + return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + + +XXH_PUBLIC_API XXH_errorcode +XXH32_update(XXH32_state_t* state, const void* input, size_t len) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const xxh_u8* p = (const xxh_u8*)input; + const xxh_u8* const bEnd = p + len; + + state->total_len_32 += (XXH32_hash_t)len; + state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16)); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, len); + state->memsize += (XXH32_hash_t)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const xxh_u32* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const xxh_u8* const limit = bEnd - 16; + xxh_u32 v1 = state->v1; + xxh_u32 v2 = state->v2; + xxh_u32 v3 = state->v3; + xxh_u32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* state) +{ + xxh_u32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + + XXH_rotl32(state->v2, 7) + + XXH_rotl32(state->v3, 12) + + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, remaining comparable across different systems. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +typedef XXH64_hash_t xxh_u64; + + +/*! XXH_REROLL_XXH64: + * Whether to reroll the XXH64_finalize() loop. + * + * Just like XXH32, we can unroll the XXH64_finalize() loop. This can be a performance gain + * on 64-bit hosts, as only one jump is required. + * + * However, on 32-bit hosts, because arithmetic needs to be done with two 32-bit registers, + * and 64-bit arithmetic needs to be simulated, it isn't beneficial to unroll. The code becomes + * ridiculously large (the largest function in the binary on i386!), and rerolling it saves + * anywhere from 3kB to 20kB. It is also slightly faster because it fits into cache better + * and is more likely to be inlined by the compiler. + * + * If XXH_REROLL is defined, this is ignored and the loop is always rerolled. */ +#ifndef XXH_REROLL_XXH64 +# if (defined(__ILP32__) || defined(_ILP32)) /* ILP32 is often defined on 32-bit GCC family */ \ + || !(defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) /* x86-64 */ \ + || defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__) /* aarch64 */ \ + || defined(__PPC64__) || defined(__PPC64LE__) || defined(__ppc64__) || defined(__powerpc64__) /* ppc64 */ \ + || defined(__mips64__) || defined(__mips64)) /* mips64 */ \ + || (!defined(SIZE_MAX) || SIZE_MAX < ULLONG_MAX) /* check limits */ +# define XXH_REROLL_XXH64 1 +# else +# define XXH_REROLL_XXH64 0 +# endif +#endif /* !defined(XXH_REROLL_XXH64) */ + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static xxh_u64 XXH_read64(const void* memPtr) { return *(const xxh_u64*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) unalign64; +static xxh_u64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static xxh_u64 XXH_read64(const void* memPtr) +{ + xxh_u64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static xxh_u64 XXH_swap64 (xxh_u64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + +XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); +} + +static xxh_u64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + +XXH_FORCE_INLINE xxh_u64 +XXH_readLE64_align(const void* ptr, XXH_alignment align) +{ + if (align==XXH_unaligned) + return XXH_readLE64(ptr); + else + return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr); +} + + +/*====== xxh64 ======*/ + +static const xxh_u64 PRIME64_1 = 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 */ +static const xxh_u64 PRIME64_2 = 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 */ +static const xxh_u64 PRIME64_3 = 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 */ +static const xxh_u64 PRIME64_4 = 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 */ +static const xxh_u64 PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 */ + +static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +static xxh_u64 XXH64_avalanche(xxh_u64 h64) +{ + h64 ^= h64 >> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +#define XXH_get64bits(p) XXH_readLE64_align(p, align) + +static xxh_u64 +XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align) +{ +#define PROCESS1_64 \ + h64 ^= (*ptr++) * PRIME64_5; \ + h64 = XXH_rotl64(h64, 11) * PRIME64_1; + +#define PROCESS4_64 \ + h64 ^= (xxh_u64)(XXH_get32bits(ptr)) * PRIME64_1; \ + ptr+=4; \ + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + +#define PROCESS8_64 { \ + xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); \ + ptr+=8; \ + h64 ^= k1; \ + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \ +} + + /* Rerolled version for 32-bit targets is faster and much smaller. */ + if (XXH_REROLL || XXH_REROLL_XXH64) { + len &= 31; + while (len >= 8) { + PROCESS8_64; + len -= 8; + } + if (len >= 4) { + PROCESS4_64; + len -= 4; + } + while (len > 0) { + PROCESS1_64; + --len; + } + return XXH64_avalanche(h64); + } else { + switch(len & 31) { + case 24: PROCESS8_64; + /* fallthrough */ + case 16: PROCESS8_64; + /* fallthrough */ + case 8: PROCESS8_64; + return XXH64_avalanche(h64); + + case 28: PROCESS8_64; + /* fallthrough */ + case 20: PROCESS8_64; + /* fallthrough */ + case 12: PROCESS8_64; + /* fallthrough */ + case 4: PROCESS4_64; + return XXH64_avalanche(h64); + + case 25: PROCESS8_64; + /* fallthrough */ + case 17: PROCESS8_64; + /* fallthrough */ + case 9: PROCESS8_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 29: PROCESS8_64; + /* fallthrough */ + case 21: PROCESS8_64; + /* fallthrough */ + case 13: PROCESS8_64; + /* fallthrough */ + case 5: PROCESS4_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 26: PROCESS8_64; + /* fallthrough */ + case 18: PROCESS8_64; + /* fallthrough */ + case 10: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 30: PROCESS8_64; + /* fallthrough */ + case 22: PROCESS8_64; + /* fallthrough */ + case 14: PROCESS8_64; + /* fallthrough */ + case 6: PROCESS4_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 27: PROCESS8_64; + /* fallthrough */ + case 19: PROCESS8_64; + /* fallthrough */ + case 11: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 31: PROCESS8_64; + /* fallthrough */ + case 23: PROCESS8_64; + /* fallthrough */ + case 15: PROCESS8_64; + /* fallthrough */ + case 7: PROCESS4_64; + /* fallthrough */ + case 3: PROCESS1_64; + /* fallthrough */ + case 2: PROCESS1_64; + /* fallthrough */ + case 1: PROCESS1_64; + /* fallthrough */ + case 0: return XXH64_avalanche(h64); + } + } + /* impossible to reach */ + XXH_ASSERT(0); + return 0; /* unreachable, but some compilers complain without it */ +} + +XXH_FORCE_INLINE xxh_u64 +XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align) +{ + const xxh_u8* bEnd = input + len; + xxh_u64 h64; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (input==NULL) { + len=0; + bEnd=input=(const xxh_u8*)(size_t)32; + } +#endif + + if (len>=32) { + const xxh_u8* const limit = bEnd - 32; + xxh_u64 v1 = seed + PRIME64_1 + PRIME64_2; + xxh_u64 v2 = seed + PRIME64_2; + xxh_u64 v3 = seed + 0; + xxh_u64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(input)); input+=8; + v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8; + v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8; + v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8; + } while (input<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (xxh_u64) len; + + return XXH64_finalize(h64, input, len, align); +} + + +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t len, XXH64_hash_t seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, (const xxh_u8*)input, len); + return XXH64_digest(&state); + +#else + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); + } } + + return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); + +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, XXH64_hash_t seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + /* do not write into reserved64, might be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved64)); + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode +XXH64_update (XXH64_state_t* state, const void* input, size_t len) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const xxh_u8* p = (const xxh_u8*)input; + const xxh_u8* const bEnd = p + len; + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, len); + state->memsize += (xxh_u32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const xxh_u8* const limit = bEnd - 32; + xxh_u64 v1 = state->v1; + xxh_u64 v2 = state->v2; + xxh_u64 v3 = state->v3; + xxh_u64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* state) +{ + xxh_u64 h64; + + if (state->total_len >= 32) { + xxh_u64 const v1 = state->v1; + xxh_u64 const v2 = state->v2; + xxh_u64 const v3 = state->v3; + xxh_u64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 /*seed*/ + PRIME64_5; + } + + h64 += (xxh_u64) state->total_len; + + return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + + + +/* ********************************************************************* +* XXH3 +* New generation hash designed for speed on small keys and vectorization +************************************************************************ */ + +#include "xxh3.h" + + +#endif /* XXH_NO_LONG_LONG */ + +#endif /* XXHASH_C_01393879 */ diff --git a/parallel/parallel_src/extern/cista/tools/xxh3/xxhash.h b/parallel/parallel_src/extern/cista/tools/xxh3/xxhash.h new file mode 100755 index 00000000..db4350cc --- /dev/null +++ b/parallel/parallel_src/extern/cista/tools/xxh3/xxhash.h @@ -0,0 +1,587 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s † 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Note †: other CRC32 implementations can be over 40x faster than SMHasher's: +http://fastcompression.blogspot.com/2019/03/presenting-xxh3.html?showComment=1552696407071#c3490092340461170735 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** + * API modifier + ******************************/ +/** XXH_INLINE_ALL (and XXH_PRIVATE_API) + * This build macro includes xxhash functions in `static` mode + * in order to inline them, and remove their symbol from the public list. + * Inlining offers great performance improvement on small keys, + * and dramatic ones when length is expressed as a compile-time constant. + * See https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html . + * Methodology : + * #define XXH_INLINE_ALL + * #include "xxhash.h" + * `xxhash.c` is automatically included. + * It's not useful to compile and link it as a separate object. + */ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif +#else +# if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) +# ifdef XXH_EXPORT +# define XXH_PUBLIC_API __declspec(dllexport) +# elif XXH_IMPORT +# define XXH_PUBLIC_API __declspec(dllimport) +# endif +# else +# define XXH_PUBLIC_API /* do nothing */ +# endif +#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ + +/*! XXH_NAMESPACE, aka Namespace Emulation : + * + * If you want to include _and expose_ xxHash functions from within your own library, + * but also want to avoid symbol collisions with other libraries which may also include xxHash, + * + * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library + * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + * + * Note that no change is required within the calling program as long as it includes `xxhash.h` : + * regular symbol name will be automatically translated by this header. + */ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 7 +#define XXH_VERSION_RELEASE 2 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint32_t XXH32_hash_t; +#else +# include +# if UINT_MAX == 0xFFFFFFFFUL + typedef unsigned int XXH32_hash_t; +# else +# if ULONG_MAX == 0xFFFFFFFFUL + typedef unsigned long XXH32_hash_t; +# else +# error "unsupported platform : need a 32-bit type" +# endif +# endif +#endif + +/*! XXH32() : + Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); + +/*====== Streaming ======*/ + +/* + * Streaming functions generate the xxHash value from an incrememtal input. + * This method is slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * XXH state must first be allocated, using XXH*_createState() . + * + * Start a new hash by initializing state with a seed, using XXH*_reset(). + * + * Then, feed the hash state by calling XXH*_update() as many times as necessary. + * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using XXH*_digest(). + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a digest, + * and generate some new hash values later on, by invoking again XXH*_digest(). + * + * When done, release the state, using XXH*_freeState(). + */ + +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/*====== Canonical representation ======*/ + +/* Default return values from XXH functions are basic unsigned 32 and 64 bits. + * This the simplest and fastest format for further post-processing. + * However, this leaves open the question of what is the order of bytes, + * since little and big endian conventions will write the same number differently. + * + * The canonical representation settles this issue, + * by mandating big-endian convention, + * aka, the same convention as human-readable numbers (large digits first). + * When writing hash values to storage, sending them over a network, or printing them, + * it's highly recommended to use the canonical representation, + * to ensure portability across a wider range of systems, present and future. + * + * The following functions allow transformation of hash values into and from canonical format. + */ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t XXH64_hash_t; +#else + /* the following type must have a width of 64-bit */ + typedef unsigned long long XXH64_hash_t; +#endif + +/*! XXH64() : + Calculate the 64-bit hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, XXH64_hash_t seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, XXH64_hash_t seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); + + +#endif /* XXH_NO_LONG_LONG */ + + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains declarations which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + These declarations should only be used with static linking. + Never use them in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only present to allow + * static allocation of XXH state, on stack or in a struct for example. + * Never **ever** use members directly. */ + +struct XXH32_state_s { + XXH32_hash_t total_len_32; + XXH32_hash_t large_len; + XXH32_hash_t v1; + XXH32_hash_t v2; + XXH32_hash_t v3; + XXH32_hash_t v4; + XXH32_hash_t mem32[4]; + XXH32_hash_t memsize; + XXH32_hash_t reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +#ifndef XXH_NO_LONG_LONG /* remove 64-bit support */ +struct XXH64_state_s { + XXH64_hash_t total_len; + XXH64_hash_t v1; + XXH64_hash_t v2; + XXH64_hash_t v3; + XXH64_hash_t v4; + XXH64_hash_t mem64[4]; + XXH32_hash_t memsize; + XXH32_hash_t reserved32; /* required for padding anyway */ + XXH64_hash_t reserved64; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +#endif /* XXH_NO_LONG_LONG */ + + +/*-********************************************************************** +* XXH3 +* New experimental hash +************************************************************************/ +#ifndef XXH_NO_LONG_LONG + + +/* ============================================ + * XXH3 is a new hash algorithm, + * featuring improved speed performance for both small and large inputs. + * See full speed analysis at : http://fastcompression.blogspot.com/2019/03/presenting-xxh3.html + * In general, expect XXH3 to run about ~2x faster on large inputs, + * and >3x faster on small ones, though exact differences depend on platform. + * + * The algorithm is portable, will generate the same hash on all platforms. + * It benefits greatly from vectorization units, but does not require it. + * + * XXH3 offers 2 variants, _64bits and _128bits. + * When only 64 bits are needed, prefer calling the _64bits variant : + * it reduces the amount of mixing, resulting in faster speed on small inputs. + * It's also generally simpler to manipulate a scalar return type than a struct. + * + * The XXH3 algorithm is still considered experimental. + * Produced results can still change between versions. + * Results produced by v0.7.x are not comparable with results from v0.7.y . + * It's nonetheless possible to use XXH3 for ephemeral data (local sessions), + * but avoid storing values in long-term storage for later reads. + * + * The API supports one-shot hashing, streaming mode, and custom secrets. + * + * There are still a number of opened questions that community can influence during the experimental period. + * I'm trying to list a few of them below, though don't consider this list as complete. + * + * - 128-bits output type : currently defined as a structure of two 64-bits fields. + * That's because 128-bit values do not exist in C standard. + * Note that it means that, at byte level, result is not identical depending on endianess. + * However, at field level, they are identical on all platforms. + * The canonical representation solves the issue of identical byte-level representation across platforms, + * which is necessary for serialization. + * Q1 : Would there be a better representation for a 128-bit hash result ? + * Q2 : Are the names of the inner 64-bit fields important ? Should they be changed ? + * + * - Prototype XXH128() : XXH128() uses the same arguments as XXH64(), for consistency. + * It means it maps to XXH3_128bits_withSeed(). + * This variant is slightly slower than XXH3_128bits(), + * because the seed is now part of the algorithm, and can't be simplified. + * Is that a good idea ? + * + * - Seed type for XXH128() : currently, it's a single 64-bit value, like the 64-bit variant. + * It could be argued that it's more logical to offer a 128-bit seed input parameter for a 128-bit hash. + * But 128-bit seed is more difficult to use, since it requires to pass a structure instead of a scalar value. + * Such a variant could either replace current one, or become an additional one. + * Farmhash, for example, offers both variants (the 128-bits seed variant is called `doubleSeed`). + * Follow up question : if both 64-bit and 128-bit seeds are allowed, which variant should be called XXH128 ? + * + * - Result for len==0 : Currently, the result of hashing a zero-length input is always `0`. + * It seems okay as a return value when using "default" secret and seed. + * But is it still fine to return `0` when secret or seed are non-default ? + * Are there use cases which could depend on generating a different hash result for zero-length input when the secret is different ? + * + * - Consistency (1) : Streaming XXH128 uses an XXH3 state, which is the same state as XXH3_64bits(). + * It means a 128bit streaming loop must invoke the following symbols : + * XXH3_createState(), XXH3_128bits_reset(), XXH3_128bits_update() (loop), XXH3_128bits_digest(), XXH3_freeState(). + * Is that consistent enough ? + * + * - Consistency (2) : The canonical representation of `XXH3_64bits` is provided by existing functions + * XXH64_canonicalFromHash(), and reverse operation XXH64_hashFromCanonical(). + * As a mirror, canonical functions for XXH128_hash_t results generated by `XXH3_128bits` + * are XXH128_canonicalFromHash() and XXH128_hashFromCanonical(). + * Which means, `XXH3` doesn't appear in the names, because canonical functions operate on a type, + * independently of which algorithm was used to generate that type. + * Is that consistent enough ? + */ + +#ifdef XXH_NAMESPACE +# define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) +# define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) +# define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) + +# define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState) +# define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState) +# define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState) + +# define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) +# define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) +# define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) +# define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) +# define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) +#endif + +/* XXH3_64bits() : + * default 64-bit variant, using default secret and default seed of 0. + * It's the fastest variant. */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len); + +/* XXH3_64bits_withSecret() : + * It's possible to provide any blob of bytes as a "secret" to generate the hash. + * This makes it more difficult for an external actor to prepare an intentional collision. + * The secret *must* be large enough (>= XXH3_SECRET_SIZE_MIN). + * It should consist of random bytes. + * Avoid repeating same character, or sequences of bytes, + * and especially avoid swathes of \0. + * Failure to respect these conditions will result in a poor quality hash. + */ +#define XXH3_SECRET_SIZE_MIN 136 +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); + +/* XXH3_64bits_withSeed() : + * This variant generates on the fly a custom secret, + * based on the default secret, altered using the `seed` value. + * While this operation is decently fast, note that it's not completely free. + * note : seed==0 produces same results as XXH3_64bits() */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); + + +/* streaming 64-bit */ + +#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11+ */ +# include +# define XXH_ALIGN(n) alignas(n) +#elif defined(__GNUC__) +# define XXH_ALIGN(n) __attribute__ ((aligned(n))) +#elif defined(_MSC_VER) +# define XXH_ALIGN(n) __declspec(align(n)) +#else +# define XXH_ALIGN(n) /* disabled */ +#endif + +typedef struct XXH3_state_s XXH3_state_t; + +#define XXH3_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */ +#define XXH3_INTERNALBUFFER_SIZE 256 +struct XXH3_state_s { + XXH_ALIGN(64) XXH64_hash_t acc[8]; + XXH_ALIGN(64) unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]; /* used to store a custom secret generated from the seed. Makes state larger. Design might change */ + XXH_ALIGN(64) unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]; + XXH32_hash_t bufferedSize; + XXH32_hash_t nbStripesPerBlock; + XXH32_hash_t nbStripesSoFar; + XXH32_hash_t secretLimit; + XXH32_hash_t reserved32; + XXH32_hash_t reserved32_2; + XXH64_hash_t totalLen; + XXH64_hash_t seed; + XXH64_hash_t reserved64; + const unsigned char* secret; /* note : there is some padding after, due to alignment on 64 bytes */ +}; /* typedef'd to XXH3_state_t */ + +/* Streaming requires state maintenance. + * This operation costs memory and cpu. + * As a consequence, streaming is slower than one-shot hashing. + * For better performance, prefer using one-shot functions whenever possible. */ + +XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr); +XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state); + + +/* XXH3_64bits_reset() : + * initialize with default parameters. + * result will be equivalent to `XXH3_64bits()`. */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr); +/* XXH3_64bits_reset_withSeed() : + * generate a custom secret from `seed`, and store it into state. + * digest will be equivalent to `XXH3_64bits_withSeed()`. */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); +/* XXH3_64bits_reset_withSecret() : + * `secret` is referenced, and must outlive the hash streaming session. + * secretSize must be >= XXH3_SECRET_SIZE_MIN. + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); + +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr); + + +/* 128-bit */ + +#ifdef XXH_NAMESPACE +# define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) +# define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) +# define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) +# define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret) + +# define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset) +# define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed) +# define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret) +# define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update) +# define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest) + +# define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual) +# define XXH128_cmp XXH_NAME2(XXH_NAMESPACE, XXH128_cmp) +# define XXH128_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash) +# define XXH128_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical) +#endif + +typedef struct { + XXH64_hash_t low64; + XXH64_hash_t high64; +} XXH128_hash_t; + +XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed); +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len); +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); /* == XXH128() */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize); + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr); +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed); +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize); + +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH3_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* statePtr); + + +/* Note : for better performance, following functions can be inlined, + * using XXH_INLINE_ALL */ + +/* return : 1 is equal, 0 if different */ +XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); + +/* This comparator is compatible with stdlib's qsort(). + * return : >0 if *h128_1 > *h128_2 + * <0 if *h128_1 < *h128_2 + * =0 if *h128_1 == *h128_2 */ +XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2); + + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[16]; } XXH128_canonical_t; +XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); +XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src); + + +#endif /* XXH_NO_LONG_LONG */ + + +/*-********************************************************************** +* XXH_INLINE_ALL +************************************************************************/ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + + + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ diff --git a/parallel/parallel_src/extern/nameof.hpp b/parallel/parallel_src/extern/nameof.hpp new file mode 100644 index 00000000..3154847b --- /dev/null +++ b/parallel/parallel_src/extern/nameof.hpp @@ -0,0 +1,1254 @@ +// _ _ __ _____ +// | \ | | / _| / ____|_ _ +// | \| | __ _ _ __ ___ ___ ___ | |_ | | _| |_ _| |_ +// | . ` |/ _` | '_ ` _ \ / _ \/ _ \| _| | | |_ _|_ _| +// | |\ | (_| | | | | | | __/ (_) | | | |____|_| |_| +// |_| \_|\__,_|_| |_| |_|\___|\___/|_| \_____| +// https://github.com/Neargye/nameof +// version 0.10.4 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2016 - 2024 Daniil Goncharov . +// +// 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 NEARGYE_NAMEOF_HPP +#define NEARGYE_NAMEOF_HPP + +#define NAMEOF_VERSION_MAJOR 0 +#define NAMEOF_VERSION_MINOR 10 +#define NAMEOF_VERSION_PATCH 4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(NAMEOF_USING_ALIAS_STRING) +# include +#endif +#if !defined(NAMEOF_USING_ALIAS_STRING_VIEW) +# include +#endif + +#if __has_include() +# include +# include +#endif + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunknown-warning-option" +# pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstringop-overflow" // Missing terminating nul 'enum_name_v'. +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 26495) // Variable 'cstring::chars_' is uninitialized. +# pragma warning(disable : 28020) // Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. +# pragma warning(disable : 26451) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call. +# pragma warning(disable : 4514) // Unreferenced inline function has been removed. +#endif + +// Checks nameof_type compiler compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && _MSC_VER >= 1910 +# undef NAMEOF_TYPE_SUPPORTED +# define NAMEOF_TYPE_SUPPORTED 1 +#endif + +// Checks nameof_type_rtti compiler compatibility. +#if defined(__clang__) +# if __has_feature(cxx_rtti) +# undef NAMEOF_TYPE_RTTI_SUPPORTED +# define NAMEOF_TYPE_RTTI_SUPPORTED 1 +# endif +#elif defined(__GNUC__) +# if defined(__GXX_RTTI) +# undef NAMEOF_TYPE_RTTI_SUPPORTED +# define NAMEOF_TYPE_RTTI_SUPPORTED 1 +# endif +#elif defined(_MSC_VER) +# if defined(_CPPRTTI) +# undef NAMEOF_TYPE_RTTI_SUPPORTED +# define NAMEOF_TYPE_RTTI_SUPPORTED 1 +# endif +#endif + +// Checks nameof_member compiler compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L +# undef NAMEOF_MEMBER_SUPPORTED +# define NAMEOF_MEMBER_SUPPORTED 1 +#endif + +// Checks nameof_pointer compiler compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L +# undef NAMEOF_POINTER_SUPPORTED +# define NAMEOF_POINTER_SUPPORTED 1 +#endif + +// Checks nameof_enum compiler compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 +# undef NAMEOF_ENUM_SUPPORTED +# define NAMEOF_ENUM_SUPPORTED 1 +#endif + +// Checks nameof_enum compiler aliases compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1920 +# undef NAMEOF_ENUM_SUPPORTED_ALIASES +# define NAMEOF_ENUM_SUPPORTED_ALIASES 1 +#endif + +// Enum value must be greater or equals than NAMEOF_ENUM_RANGE_MIN. By default NAMEOF_ENUM_RANGE_MIN = -128. +// If need another min range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN. +#if !defined(NAMEOF_ENUM_RANGE_MIN) +# define NAMEOF_ENUM_RANGE_MIN -128 +#endif + +// Enum value must be less or equals than NAMEOF_ENUM_RANGE_MAX. By default NAMEOF_ENUM_RANGE_MAX = 128. +// If need another max range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MAX. +#if !defined(NAMEOF_ENUM_RANGE_MAX) +# define NAMEOF_ENUM_RANGE_MAX 128 +#endif + +namespace nameof { + +// If need another string_view type, define the macro NAMEOF_USING_ALIAS_STRING_VIEW. +#if defined(NAMEOF_USING_ALIAS_STRING_VIEW) +NAMEOF_USING_ALIAS_STRING_VIEW +#else +using std::string_view; +#endif + +// If need another string type, define the macro NAMEOF_USING_ALIAS_STRING. +#if defined(NAMEOF_USING_ALIAS_STRING) +NAMEOF_USING_ALIAS_STRING +#else +using std::string; +#endif + +namespace customize { + +// Enum value must be in range [NAMEOF_ENUM_RANGE_MIN, NAMEOF_ENUM_RANGE_MAX]. By default NAMEOF_ENUM_RANGE_MIN = -128, NAMEOF_ENUM_RANGE_MAX = 128. +// If you need another range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN and NAMEOF_ENUM_RANGE_MAX. +// If you need another range for specific enum type, add specialization enum_range for necessary enum type. +template +struct enum_range { + static_assert(std::is_enum_v, "nameof::customize::enum_range requires enum type."); + inline static constexpr int min = NAMEOF_ENUM_RANGE_MIN; + inline static constexpr int max = NAMEOF_ENUM_RANGE_MAX; + static_assert(max > min, "nameof::customize::enum_range requires max > min."); +}; + +static_assert(NAMEOF_ENUM_RANGE_MIN <= 0, "NAMEOF_ENUM_RANGE_MIN must be less or equals than 0."); +static_assert(NAMEOF_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "NAMEOF_ENUM_RANGE_MIN must be greater than INT16_MIN."); + +static_assert(NAMEOF_ENUM_RANGE_MAX > 0, "NAMEOF_ENUM_RANGE_MAX must be greater than 0."); +static_assert(NAMEOF_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "NAMEOF_ENUM_RANGE_MAX must be less than INT16_MAX."); + +static_assert(NAMEOF_ENUM_RANGE_MAX > NAMEOF_ENUM_RANGE_MIN, "NAMEOF_ENUM_RANGE_MAX must be greater than NAMEOF_ENUM_RANGE_MIN."); + +// If you need custom names for enum, add specialization enum_name for necessary enum type. +template +constexpr string_view enum_name(E) noexcept { + static_assert(std::is_enum_v, "nameof::customize::enum_name requires enum type."); + return {}; +} + +// If you need custom name for type, add specialization type_name for necessary type. +template +constexpr string_view type_name() noexcept { + return {}; +} + +// If you need custom name for member, add specialization member_name for necessary type. +template +constexpr string_view member_name() noexcept { + return {}; +} + +// If you need custom name for a pointer, add specialization pointer_name for necessary type. +template +constexpr string_view pointer_name() noexcept { + return {}; +} + +} // namespace nameof::customize + +template +class [[nodiscard]] cstring { + public: + using value_type = const char; + using size_type = std::uint16_t; + using difference_type = std::ptrdiff_t; + using pointer = const char*; + using const_pointer = const char*; + using reference = const char&; + using const_reference = const char&; + + using iterator = const char*; + using const_iterator = const char*; + + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr explicit cstring(string_view str) noexcept : cstring{str, std::make_integer_sequence{}} { + assert(str.size() > 0 && str.size() == N); + } + + constexpr cstring() = delete; + + constexpr cstring(const cstring&) = default; + + constexpr cstring(cstring&&) = default; + + ~cstring() = default; + + cstring& operator=(const cstring&) = default; + + cstring& operator=(cstring&&) = default; + + [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; } + + [[nodiscard]] constexpr size_type size() const noexcept { return N; } + + [[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); } + + [[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); } + + [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); } + + [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } + + [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return end(); } + + [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return begin(); } + + [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } + + [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); } + + [[nodiscard]] constexpr const_reference operator[](size_type i) const noexcept { return assert(i < size()), chars_[i]; } + + [[nodiscard]] constexpr const_reference front() const noexcept { return chars_[0]; } + + [[nodiscard]] constexpr const_reference back() const noexcept { return chars_[N]; } + + [[nodiscard]] constexpr size_type length() const noexcept { return size(); } + + [[nodiscard]] constexpr bool empty() const noexcept { return false; } + + [[nodiscard]] constexpr int compare(string_view str) const noexcept { return string_view{data(), size()}.compare(str); } + + [[nodiscard]] constexpr const char* c_str() const noexcept { return data(); } + + [[nodiscard]] string str() const { return {begin(), end()}; } + + [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; } + + [[nodiscard]] constexpr explicit operator const_pointer() const noexcept { return data(); } + + [[nodiscard]] explicit operator string() const { return {begin(), end()}; } + + private: + template + constexpr cstring(string_view str, std::integer_sequence) noexcept : chars_{str[I]..., '\0'} {} + + char chars_[static_cast(N) + 1]; +}; + +template <> +class [[nodiscard]] cstring<0> { + public: + using value_type = const char; + using size_type = std::uint16_t; + using difference_type = std::ptrdiff_t; + using pointer = const char*; + using const_pointer = const char*; + using reference = const char&; + using const_reference = const char&; + + using iterator = const char*; + using const_iterator = const char*; + + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr explicit cstring(string_view) noexcept {} + + constexpr cstring() = default; + + constexpr cstring(const cstring&) = default; + + constexpr cstring(cstring&&) = default; + + ~cstring() = default; + + cstring& operator=(const cstring&) = default; + + cstring& operator=(cstring&&) = default; + + [[nodiscard]] constexpr const_pointer data() const noexcept { return nullptr; } + + [[nodiscard]] constexpr size_type size() const noexcept { return 0; } + + [[nodiscard]] constexpr const_iterator begin() const noexcept { return nullptr; } + + [[nodiscard]] constexpr const_iterator end() const noexcept { return nullptr; } + + [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return nullptr; } + + [[nodiscard]] constexpr const_iterator cend() const noexcept { return nullptr; } + + [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return {}; } + + [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return {}; } + + [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return {}; } + + [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return {}; } + + [[nodiscard]] constexpr size_type length() const noexcept { return 0; } + + [[nodiscard]] constexpr bool empty() const noexcept { return true; } + + [[nodiscard]] constexpr int compare(string_view str) const noexcept { return string_view{}.compare(str); } + + [[nodiscard]] constexpr const char* c_str() const noexcept { return nullptr; } + + [[nodiscard]] string str() const { return {}; } + + [[nodiscard]] constexpr operator string_view() const noexcept { return {}; } + + [[nodiscard]] constexpr explicit operator const_pointer() const noexcept { return nullptr; } + + [[nodiscard]] explicit operator string() const { return {}; } +}; + +template +[[nodiscard]] constexpr bool operator==(const cstring& lhs, string_view rhs) noexcept { + return lhs.compare(rhs) == 0; +} + +template +[[nodiscard]] constexpr bool operator==(string_view lhs, const cstring& rhs) noexcept { + return lhs.compare(rhs) == 0; +} + +template +[[nodiscard]] constexpr bool operator!=(const cstring& lhs, string_view rhs) noexcept { + return lhs.compare(rhs) != 0; +} + +template +[[nodiscard]] constexpr bool operator!=(string_view lhs, const cstring& rhs) noexcept { + return lhs.compare(rhs) != 0; +} + +template +[[nodiscard]] constexpr bool operator>(const cstring& lhs, string_view rhs) noexcept { + return lhs.compare(rhs) > 0; +} + +template +[[nodiscard]] constexpr bool operator>(string_view lhs, const cstring& rhs) noexcept { + return lhs.compare(rhs) > 0; +} + +template +[[nodiscard]] constexpr bool operator>=(const cstring& lhs, string_view rhs) noexcept { + return lhs.compare(rhs) >= 0; +} + +template +[[nodiscard]] constexpr bool operator>=(string_view lhs, const cstring& rhs) noexcept { + return lhs.compare(rhs) >= 0; +} + +template +[[nodiscard]] constexpr bool operator<(const cstring& lhs, string_view rhs) noexcept { + return lhs.compare(rhs) < 0; +} + +template +[[nodiscard]] constexpr bool operator<(string_view lhs, const cstring& rhs) noexcept { + return lhs.compare(rhs) < 0; +} + +template +[[nodiscard]] constexpr bool operator<=(const cstring& lhs, string_view rhs) noexcept { + return lhs.compare(rhs) <= 0; +} + +template +[[nodiscard]] constexpr bool operator<=(string_view lhs, const cstring& rhs) noexcept { + return lhs.compare(rhs) <= 0; +} + +template +std::basic_ostream& operator<<(std::basic_ostream& os, const cstring& srt) { + for (const auto c : srt) { + os.put(c); + } + return os; +} + +namespace detail { + +constexpr string_view pretty_name(string_view name, bool remove_suffix = true) noexcept { + if (name.size() >= 1 && (name[0] == '"' || name[0] == '\'')) { + return {}; // Narrow multibyte string literal. + } else if (name.size() >= 2 && name[0] == 'R' && (name[1] == '"' || name[1] == '\'')) { + return {}; // Raw string literal. + } else if (name.size() >= 2 && name[0] == 'L' && (name[1] == '"' || name[1] == '\'')) { + return {}; // Wide string literal. + } else if (name.size() >= 2 && name[0] == 'U' && (name[1] == '"' || name[1] == '\'')) { + return {}; // UTF-32 encoded string literal. + } else if (name.size() >= 2 && name[0] == 'u' && (name[1] == '"' || name[1] == '\'')) { + return {}; // UTF-16 encoded string literal. + } else if (name.size() >= 3 && name[0] == 'u' && name[1] == '8' && (name[2] == '"' || name[2] == '\'')) { + return {}; // UTF-8 encoded string literal. + } else if (name.size() >= 1 && (name[0] >= '0' && name[0] <= '9')) { + return {}; // Invalid name. + } + + for (std::size_t i = name.size(), h = 0, s = 0; i > 0; --i) { + if (name[i - 1] == ')') { + ++h; + ++s; + continue; + } else if (name[i - 1] == '(') { + --h; + ++s; + continue; + } + + if (h == 0) { + name.remove_suffix(s); + break; + } else { + ++s; + continue; + } + } + + std::size_t s = 0; + for (std::size_t i = name.size(), h = 0; i > 0; --i) { + if (name[i - 1] == '>') { + ++h; + ++s; + continue; + } else if (name[i - 1] == '<') { + --h; + ++s; + continue; + } + + if (h == 0) { + break; + } else { + ++s; + continue; + } + } + + for (std::size_t i = name.size() - s; i > 0; --i) { + if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || + (name[i - 1] >= 'a' && name[i - 1] <= 'z') || + (name[i - 1] >= 'A' && name[i - 1] <= 'Z') || + (name[i - 1] == '_'))) { + name.remove_prefix(i); + break; + } + } + if (remove_suffix) { + name.remove_suffix(s); + } + + if (name.size() > 0 && ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z') || + (name[0] == '_'))) { + return name; + } + + return {}; // Invalid name. +} + +#if defined(__cpp_lib_array_constexpr) && __cpp_lib_array_constexpr >= 201603L +# define NAMEOF_ARRAY_CONSTEXPR 1 +#else +template +constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) noexcept { + return {{a[I]...}}; +} +#endif + +template +constexpr bool cmp_less(L lhs, R rhs) noexcept { + static_assert(std::is_integral_v && std::is_integral_v, "nameof::detail::cmp_less requires integral type."); + + if constexpr (std::is_signed_v == std::is_signed_v) { + // If same signedness (both signed or both unsigned). + return lhs < rhs; + } else if constexpr (std::is_same_v) { // bool special case + return static_cast(lhs) < rhs; + } else if constexpr (std::is_same_v) { // bool special case + return lhs < static_cast(rhs); + } else if constexpr (std::is_signed_v) { + // If 'right' is negative, then result is 'false', otherwise cast & compare. + return rhs > 0 && lhs < static_cast>(rhs); + } else { + // If 'left' is negative, then result is 'true', otherwise cast & compare. + return lhs < 0 || static_cast>(lhs) < rhs; + } +} + +template +constexpr I log2(I value) noexcept { + static_assert(std::is_integral_v, "nameof::detail::log2 requires integral type."); + + if constexpr (std::is_same_v) { // bool special case + return assert(false), value; + } else { + auto ret = I{0}; + for (; value > I{1}; value >>= I{1}, ++ret) {} + + return ret; + } +} + +template +struct nameof_enum_supported +#if defined(NAMEOF_ENUM_SUPPORTED) && NAMEOF_ENUM_SUPPORTED || defined(NAMEOF_ENUM_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template +using enable_if_enum_t = std::enable_if_t>, R>; + +template +inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; + +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "nameof::detail::n requires enum type."); + + if constexpr (nameof_enum_supported::value) { +#if defined(__clang__) || defined(__GNUC__) + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); +#elif defined(_MSC_VER) + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); +#else + constexpr auto name = string_view{}; +#endif + return name; + } else { + return string_view{}; + } +} + +template +constexpr auto enum_name() noexcept { + [[maybe_unused]] constexpr auto custom_name = customize::enum_name(V); + + if constexpr (custom_name.empty()) { + constexpr auto name = n(); + return cstring{name}; + } else { + return cstring{custom_name}; + } +} + +template +inline constexpr auto enum_name_v = enum_name(); + +template +constexpr bool is_valid() noexcept { +#if defined(__clang__) && __clang_major__ >= 16 + // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 + constexpr E v = __builtin_bit_cast(E, V); +#else + constexpr E v = static_cast(V); +#endif + [[maybe_unused]] constexpr auto custom_name = customize::enum_name(v); + if constexpr (custom_name.empty()) { + return n().size() != 0; + } else { + return custom_name.size() != 0; + } +} + +template > +constexpr U ualue(std::size_t i) noexcept { + if constexpr (std::is_same_v) { // bool special case + static_assert(O == 0, "nameof::detail::ualue requires valid offset."); + + return static_cast(i); + } else if constexpr (IsFlags) { + return static_cast(U{1} << static_cast(static_cast(i) + O)); + } else { + return static_cast(static_cast(i) + O); + } +} + +template > +constexpr E value(std::size_t i) noexcept { + return static_cast(ualue(i)); +} + +template > +constexpr int reflected_min() noexcept { + if constexpr (IsFlags) { + return 0; + } else { + constexpr auto lhs = customize::enum_range::min; + constexpr auto rhs = (std::numeric_limits::min)(); + + if constexpr (cmp_less(rhs, lhs)) { + return lhs; + } else { + return rhs; + } + } +} + +template > +constexpr int reflected_max() noexcept { + if constexpr (IsFlags) { + return std::numeric_limits::digits - 1; + } else { + constexpr auto lhs = customize::enum_range::max; + constexpr auto rhs = (std::numeric_limits::max)(); + + if constexpr (cmp_less(lhs, rhs)) { + return lhs; + } else { + return rhs; + } + } +} + +#define NAMEOF_FOR_EACH_256(T) \ + T( 0)T( 1)T( 2)T( 3)T( 4)T( 5)T( 6)T( 7)T( 8)T( 9)T( 10)T( 11)T( 12)T( 13)T( 14)T( 15)T( 16)T( 17)T( 18)T( 19)T( 20)T( 21)T( 22)T( 23)T( 24)T( 25)T( 26)T( 27)T( 28)T( 29)T( 30)T( 31) \ + T( 32)T( 33)T( 34)T( 35)T( 36)T( 37)T( 38)T( 39)T( 40)T( 41)T( 42)T( 43)T( 44)T( 45)T( 46)T( 47)T( 48)T( 49)T( 50)T( 51)T( 52)T( 53)T( 54)T( 55)T( 56)T( 57)T( 58)T( 59)T( 60)T( 61)T( 62)T( 63) \ + T( 64)T( 65)T( 66)T( 67)T( 68)T( 69)T( 70)T( 71)T( 72)T( 73)T( 74)T( 75)T( 76)T( 77)T( 78)T( 79)T( 80)T( 81)T( 82)T( 83)T( 84)T( 85)T( 86)T( 87)T( 88)T( 89)T( 90)T( 91)T( 92)T( 93)T( 94)T( 95) \ + T( 96)T( 97)T( 98)T( 99)T(100)T(101)T(102)T(103)T(104)T(105)T(106)T(107)T(108)T(109)T(110)T(111)T(112)T(113)T(114)T(115)T(116)T(117)T(118)T(119)T(120)T(121)T(122)T(123)T(124)T(125)T(126)T(127) \ + T(128)T(129)T(130)T(131)T(132)T(133)T(134)T(135)T(136)T(137)T(138)T(139)T(140)T(141)T(142)T(143)T(144)T(145)T(146)T(147)T(148)T(149)T(150)T(151)T(152)T(153)T(154)T(155)T(156)T(157)T(158)T(159) \ + T(160)T(161)T(162)T(163)T(164)T(165)T(166)T(167)T(168)T(169)T(170)T(171)T(172)T(173)T(174)T(175)T(176)T(177)T(178)T(179)T(180)T(181)T(182)T(183)T(184)T(185)T(186)T(187)T(188)T(189)T(190)T(191) \ + T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \ + T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255) + +template +constexpr void valid_count(bool* valid, std::size_t& count) noexcept { +#define NAMEOF_ENUM_V(O) \ + if constexpr ((I + O) < Size) { \ + if constexpr (is_valid(I + O)>()) { \ + valid[I + O] = true; \ + ++count; \ + } \ + } + + NAMEOF_FOR_EACH_256(NAMEOF_ENUM_V) + + if constexpr ((I + 256) < Size) { + valid_count(valid, count); + } +#undef NAMEOF_ENUM_V +} + +template +struct valid_count_t { + std::size_t count = 0; + bool valid[N] = {}; +}; + +template +constexpr auto valid_count() noexcept { + valid_count_t vc; + valid_count(vc.valid, vc.count); + return vc; +} + +template +constexpr auto values() noexcept { + constexpr auto vc = valid_count(); + + if constexpr (vc.count > 0) { +#if defined(NAMEOF_ARRAY_CONSTEXPR) + std::array values = {}; +#else + E values[vc.count] = {}; +#endif + for (std::size_t i = 0, v = 0; v < vc.count; ++i) { + if (vc.valid[i]) { + values[v++] = value(i); + } + } +#if defined(NAMEOF_ARRAY_CONSTEXPR) + return values; +#else + return to_array(values, std::make_index_sequence{}); +#endif + } else { + return std::array{}; + } +} + +template > +constexpr auto values() noexcept { + constexpr auto min = reflected_min(); + constexpr auto max = reflected_max(); + constexpr auto range_size = max - min + 1; + static_assert(range_size > 0, "nameof::enum_range requires valid size."); + static_assert(range_size < (std::numeric_limits::max)(), "nameof::enum_range requires valid size."); + + return values(); +} + +template +inline constexpr auto values_v = values(); + +template +inline constexpr auto count_v = values_v.size(); + +template > +inline constexpr auto min_v = (count_v > 0) ? static_cast(values_v.front()) : U{0}; + +template > +inline constexpr auto max_v = (count_v > 0) ? static_cast(values_v.back()) : U{0}; + +template +constexpr auto names(std::index_sequence) noexcept { + constexpr auto names = std::array{{enum_name_v[I]>...}}; + return names; +} + +template +inline constexpr auto names_v = names(std::make_index_sequence>{}); + +template > +constexpr bool is_sparse() noexcept { + if constexpr (count_v == 0) { + return false; + } else if constexpr (std::is_same_v) { // bool special case + return false; + } else { + constexpr auto max = IsFlags ? log2(max_v) : max_v; + constexpr auto min = IsFlags ? log2(min_v) : min_v; + constexpr auto range_size = max - min + 1; + + return range_size != count_v; + } +} + +template +inline constexpr bool is_sparse_v = is_sparse(); + +template > +constexpr E enum_value(std::size_t i) noexcept { + if constexpr (is_sparse_v) { + return values_v[i]; + } else { + constexpr auto min = IsFlags ? log2(min_v) : min_v; + + return value(i); + } +} + +template +struct nameof_type_supported +#if defined(NAMEOF_TYPE_SUPPORTED) && NAMEOF_TYPE_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template +struct nameof_type_rtti_supported +#if defined(NAMEOF_TYPE_RTTI_SUPPORTED) && NAMEOF_TYPE_RTTI_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template +struct nameof_member_supported +#if defined(NAMEOF_MEMBER_SUPPORTED) && NAMEOF_MEMBER_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +template +struct nameof_pointer_supported +#if defined(NAMEOF_POINTER_SUPPORTED) && NAMEOF_POINTER_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +template +struct identity { + using type = T; +}; +#else +template +using identity = T; +#endif + +template +using remove_cvref_t = std::remove_cv_t>; + +template +using enable_if_has_short_name_t = std::enable_if_t && !std::is_pointer_v, R>; + +template +constexpr auto n() noexcept { +#if defined(_MSC_VER) && !defined(__clang__) + [[maybe_unused]] constexpr auto custom_name = customize::type_name(); +#else + [[maybe_unused]] constexpr auto custom_name = customize::type_name(); +#endif + + if constexpr (custom_name.empty() && nameof_type_supported::value) { +#if defined(__clang__) + constexpr string_view name{__PRETTY_FUNCTION__ + 31, sizeof(__PRETTY_FUNCTION__) - 34}; +#elif defined(__GNUC__) + constexpr string_view name{__PRETTY_FUNCTION__ + 46, sizeof(__PRETTY_FUNCTION__) - 49}; +#elif defined(_MSC_VER) + constexpr string_view name{__FUNCSIG__ + 63, sizeof(__FUNCSIG__) - 81 - (__FUNCSIG__[sizeof(__FUNCSIG__) - 19] == ' ' ? 1 : 0)}; +#else + constexpr auto name = string_view{}; +#endif + return cstring{name}; + } else { + return cstring{custom_name}; + } +} + +template +inline constexpr auto type_name_v = n(); + +#if __has_include() +template +string nameof_type_rtti(const char* tn) { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); + const auto name = string{dmg}; + free(dmg); + assert(!name.empty() && "Type does not have a name."); + + return name; +} + +template +string nameof_full_type_rtti(const char* tn) { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); + auto name = string{dmg}; + free(dmg); + assert(!name.empty() && "Type does not have a name."); + if constexpr (std::is_const_v>) { + name = string{"const "}.append(name); + } + if constexpr (std::is_volatile_v>) { + name = string{"volatile "}.append(name); + } + if constexpr (std::is_lvalue_reference_v) { + name.append(1, '&'); + } + if constexpr (std::is_rvalue_reference_v) { + name.append("&&"); + } + + return name; +} + +template = 0> +string nameof_short_type_rtti(const char* tn) { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); + const auto pname = pretty_name(dmg); + const auto name = string{pname.data(), pname.size()}; + free(dmg); + assert(!name.empty() && "Type does not have a short name."); + + return name; +} +#else +template +string nameof_type_rtti(const char* tn) { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto name = string_view{tn}; + assert(!name.empty() && "Type does not have a name."); + return {name.data(), name.size()}; +} + +template +string nameof_full_type_rtti(const char* tn) { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + auto name = string{tn}; + assert(!name.empty() && "Type does not have a name."); + if constexpr (std::is_const_v>) { + name = string{"const "}.append(name); + } + if constexpr (std::is_volatile_v>) { + name = string{"volatile "}.append(name); + } + if constexpr (std::is_lvalue_reference_v) { + name.append(1, '&'); + } + if constexpr (std::is_rvalue_reference_v) { + name.append("&&"); + } + return name; +} + +template = 0> +string nameof_short_type_rtti(const char* tn) { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto name = pretty_name(tn); + assert(!name.empty() && "Type does not have a short name."); + return {name.data(), name.size()}; +} +#endif + +template +constexpr auto n() noexcept { + [[maybe_unused]] constexpr auto custom_name = customize::member_name(); + + if constexpr (custom_name.empty() && nameof_member_supported::value) { +#if defined(__clang__) || defined(__GNUC__) + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); +#elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L + constexpr auto name = pretty_name({__FUNCSIG__, + sizeof(__FUNCSIG__) - 18 + std::is_member_function_pointer_v}); +#else + constexpr auto name = string_view{}; +#endif + return cstring{name}; + } else { + return cstring{custom_name}; + } +} + +#if defined(__clang__) || defined(__GNUC__) +template +inline constexpr auto member_name_v = n(); +#elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L +template +From get_base_type(Type From::*); + +template +extern T nonexist_object; + +template +struct Store { + T v; +}; + +template +Store(T) -> Store; + +template +consteval auto get_member_name() noexcept { + if constexpr (std::is_member_function_pointer_v) { + return n(); + } else { + constexpr bool is_defined = sizeof(decltype(get_base_type(V))) != 0; + static_assert(is_defined, "nameof::nameof_member member name can use only if the struct is already fully defined. Please use NAMEOF macro, or separate definition and declaration."); + if constexpr (is_defined) { + return n.*V)}>(); + } else { + return ""; + } + } +} + +template +inline constexpr auto member_name_v = get_member_name(); +#else +template +inline constexpr auto member_name_v = cstring<0>{}; +#endif + +template +struct is_same : std::false_type {}; + +template +struct is_same : std::true_type {}; + +template +constexpr bool is_nullptr_v = is_same>(nullptr)>::value; + +template +constexpr auto p() noexcept { + [[maybe_unused]] constexpr auto custom_name = customize::pointer_name().empty() && is_nullptr_v ? "nullptr" : customize::pointer_name(); + + if constexpr (custom_name.empty() && nameof_pointer_supported::value) { +#if defined(__clang__) + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); +#elif defined(__GNUC__) + constexpr bool has_parenthesis = __PRETTY_FUNCTION__[sizeof(__PRETTY_FUNCTION__) - 3] == ')'; + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2 - has_parenthesis}); +#elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); +#else + constexpr auto name = string_view{}; +#endif + return cstring{name}; + } else { + return cstring{custom_name}; + } +} + +template +inline constexpr auto pointer_name_v = p(); + +} // namespace nameof::detail + +// Checks is nameof_type supported compiler. +inline constexpr bool is_nameof_type_supported = detail::nameof_type_supported::value; + +// Checks is nameof_type_rtti supported compiler. +inline constexpr bool is_nameof_type_rtti_supported = detail::nameof_type_rtti_supported::value; + +// Checks is nameof_member supported compiler. +inline constexpr bool is_nameof_member_supported = detail::nameof_member_supported::value; + +// Checks is nameof_pointer supported compiler. +inline constexpr bool is_nameof_pointer_supported = detail::nameof_pointer_supported::value; + +// Checks is nameof_enum supported compiler. +inline constexpr bool is_nameof_enum_supported = detail::nameof_enum_supported::value; + +// Obtains name of enum variable. +template +[[nodiscard]] constexpr auto nameof_enum(E value) noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + using U = std::underlying_type_t; + static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + static_assert(detail::count_v > 0, "nameof::nameof_enum requires enum implementation and valid max and min."); + + if constexpr (detail::is_sparse_v) { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::enum_value(i) == value) { + return detail::names_v[i]; + } + } + } else { + const auto v = static_cast(value); + if (v >= detail::min_v && v <= detail::max_v) { + return detail::names_v[static_cast(v - detail::min_v)]; + } + } + return {}; // Value out of range. +} + +// Obtains name of enum variable or default value if enum variable out of range. +template +[[nodiscard]] auto nameof_enum_or(E value, string_view default_value) -> detail::enable_if_enum_t { + using D = std::decay_t; + static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum_or unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + static_assert(detail::count_v > 0, "nameof::nameof_enum_or requires enum implementation and valid max and min."); + + if (auto v = nameof_enum(value); !v.empty()) { + return string{v.data(), v.size()}; + } + return string{default_value.data(), default_value.size()}; +} + +// Obtains name of enum-flags variable. +template +[[nodiscard]] auto nameof_enum_flag(E value, char sep = '|') -> detail::enable_if_enum_t { + using D = std::decay_t; + using U = std::underlying_type_t; + static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum_flag unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + static_assert(detail::count_v > 0, "nameof::nameof_enum_flag requires enum-flags implementation."); + + string name; + auto check_value = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = static_cast(detail::enum_value(i)); (static_cast(value) & v) != 0) { + if (const auto n = detail::names_v[i]; !n.empty()) { + check_value |= v; + if (!name.empty()) { + name.append(1, sep); + } + name.append(n.data(), n.size()); + } else { + return {}; // Value out of range. + } + } + } + + if (check_value != 0 && check_value == static_cast(value)) { + return name; + } + return {}; // Invalid value or out of range. +} + +// Obtains name of static storage enum variable. +// This version is much lighter on the compile times and is not restricted to the enum_range limitation. +template = 0> +[[nodiscard]] constexpr auto nameof_enum() noexcept { + using D = std::decay_t; + static_assert(std::is_enum_v, "nameof::nameof_enum requires member enum type."); + static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + return detail::enum_name_v; +} + +// Obtains name of type, reference and cv-qualifiers are ignored. +template +[[nodiscard]] constexpr string_view nameof_type() noexcept { + using U = detail::identity>; + static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + return detail::type_name_v; +} + +// Obtains full name of type, with reference and cv-qualifiers. +template +[[nodiscard]] constexpr string_view nameof_full_type() noexcept { + using U = detail::identity; + static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + return detail::type_name_v; +} + +// Obtains short name of type. +template = 0> +[[nodiscard]] constexpr auto nameof_short_type() noexcept { + using U = detail::identity>; + static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + static_assert(!std::is_array_v && !std::is_pointer_v, "nameof::nameof_member requires non array and non pointer type."); + constexpr string_view name = detail::pretty_name(detail::type_name_v); + static_assert(!name.empty(), "Type does not have a short name."); + return cstring{name}; +} + +// Obtains name of member. +template , int> = 0> +[[nodiscard]] constexpr auto nameof_member() noexcept { + using U = decltype(V); + static_assert(std::is_member_pointer_v, "nameof::nameof_member requires member pointer type."); + static_assert(detail::nameof_member_supported::value, "nameof::nameof_member unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + return detail::member_name_v; +} + +// Obtains name of a function, a global or class static variable. +template , int> = 0> +[[nodiscard]] constexpr auto nameof_pointer() noexcept { + using U = decltype(V); + static_assert(std::is_pointer_v, "nameof::nameof_pointer requires pointer type."); + static_assert(detail::nameof_pointer_supported::value, "nameof::nameof_pointer unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + return detail::pointer_name_v; +} + +} // namespace nameof + +// Obtains name of variable, function, macro. +#define NAMEOF(...) []() constexpr noexcept { \ + ::std::void_t(); \ + constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \ + static_assert(!_name.empty(), "Expression does not have a name."); \ + constexpr auto _size = _name.size(); \ + constexpr auto _nameof = ::nameof::cstring<_size>{_name}; \ + return _nameof; }() + +// Obtains full name of variable, function, macro. +#define NAMEOF_FULL(...) []() constexpr noexcept { \ + ::std::void_t(); \ + constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__, false); \ + static_assert(!_name.empty(), "Expression does not have a name."); \ + constexpr auto _size = _name.size(); \ + constexpr auto _nameof_full = ::nameof::cstring<_size>{_name}; \ + return _nameof_full; }() + +// Obtains raw name of variable, function, macro. +#define NAMEOF_RAW(...) []() constexpr noexcept { \ + ::std::void_t(); \ + constexpr auto _name = ::nameof::string_view{#__VA_ARGS__}; \ + static_assert(!_name.empty(), "Expression does not have a name."); \ + constexpr auto _size = _name.size(); \ + constexpr auto _nameof_raw = ::nameof::cstring<_size>{_name}; \ + return _nameof_raw; }() + +// Obtains name of enum variable. +#define NAMEOF_ENUM(...) ::nameof::nameof_enum<::std::decay_t>(__VA_ARGS__) + +// Obtains name of enum variable or default value if enum variable out of range. +#define NAMEOF_ENUM_OR(...) ::nameof::nameof_enum_or(__VA_ARGS__) + +// Obtains name of static storage enum variable. +// This version is much lighter on the compile times and is not restricted to the enum_range limitation. +#define NAMEOF_ENUM_CONST(...) ::nameof::nameof_enum<__VA_ARGS__>() + +// Obtains name of enum-flags variable. +#define NAMEOF_ENUM_FLAG(...) ::nameof::nameof_enum_flag<::std::decay_t>(__VA_ARGS__) + +// Obtains type name, reference and cv-qualifiers are ignored. +#define NAMEOF_TYPE(...) ::nameof::nameof_type<__VA_ARGS__>() + +// Obtains full type name, with reference and cv-qualifiers. +#define NAMEOF_FULL_TYPE(...) ::nameof::nameof_full_type<__VA_ARGS__>() + +// Obtains short type name. +#define NAMEOF_SHORT_TYPE(...) ::nameof::nameof_short_type<__VA_ARGS__>() + +// Obtains type name of expression, reference and cv-qualifiers are ignored. +#define NAMEOF_TYPE_EXPR(...) ::nameof::nameof_type() + +// Obtains full type name of expression, with reference and cv-qualifiers. +#define NAMEOF_FULL_TYPE_EXPR(...) ::nameof::nameof_full_type() + +// Obtains short type name of expression. +#define NAMEOF_SHORT_TYPE_EXPR(...) ::nameof::nameof_short_type() + +// Obtains type name, with reference and cv-qualifiers, using RTTI. +#define NAMEOF_TYPE_RTTI(...) ::nameof::detail::nameof_type_rtti<::std::void_t>(typeid(__VA_ARGS__).name()) + +// Obtains full type name, using RTTI. +#define NAMEOF_FULL_TYPE_RTTI(...) ::nameof::detail::nameof_full_type_rtti(typeid(__VA_ARGS__).name()) + +// Obtains short type name, using RTTI. +#define NAMEOF_SHORT_TYPE_RTTI(...) ::nameof::detail::nameof_short_type_rtti(typeid(__VA_ARGS__).name()) + +// Obtains name of member. +#define NAMEOF_MEMBER(...) ::nameof::nameof_member<__VA_ARGS__>() + +// Obtains name of a function, a global or class static variable. +#define NAMEOF_POINTER(...) ::nameof::nameof_pointer<__VA_ARGS__>() + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // NEARGYE_NAMEOF_HPP diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 7a752194..6f119c0e 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -7,10 +7,16 @@ #include #include +#include #include #include #include +#include +#include +#include + + namespace mpi { namespace details { @@ -23,10 +29,17 @@ struct mpi_data_kind_trait { static constexpr mpi_data_kinds kind = mpi_data_kinds::none; }; +// Detect aggregate types and set kind to composite +template +requires std::is_aggregate_v +struct mpi_data_kind_trait { + static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; +}; + // Macro to specialize mpi_data_kind_trait for unique base types -#define MPI_BASE_TYPE_KIND(type) \ - template <> \ - struct mpi_data_kind_trait { \ +#define MPI_BASE_TYPE_KIND(type) \ + template <> \ + struct mpi_data_kind_trait { \ static constexpr mpi_data_kinds kind = mpi_data_kinds::base; \ }; @@ -45,6 +58,9 @@ MPI_BASE_TYPE_KIND(uint8_t) MPI_BASE_TYPE_KIND(uint16_t) MPI_BASE_TYPE_KIND(uint32_t) MPI_BASE_TYPE_KIND(uint64_t) +MPI_BASE_TYPE_KIND(long) +MPI_BASE_TYPE_KIND(unsigned long) +MPI_BASE_TYPE_KIND(std::complex) #undef MPI_BASE_TYPE_KIND } // namespace details @@ -60,11 +76,11 @@ struct mpi_datatype_trait; // Macro to specialize mpi_datatype_trait for unique base types #define MPI_DATATYPE_TRAIT(type, mpi_type_const) \ - template <> \ - struct mpi_datatype_trait { \ - static MPI_Datatype get_mpi_type() { \ - return mpi_type_const; \ - } \ + template <> \ + struct mpi_datatype_trait { \ + static MPI_Datatype get_mpi_type() { \ + return mpi_type_const; \ + } \ }; // Map unique base types to MPI_Datatypes @@ -82,6 +98,10 @@ MPI_DATATYPE_TRAIT(uint8_t, MPI_UINT8_T) MPI_DATATYPE_TRAIT(uint16_t, MPI_UINT16_T) MPI_DATATYPE_TRAIT(uint32_t, MPI_UINT32_T) MPI_DATATYPE_TRAIT(uint64_t, MPI_UINT64_T) +MPI_DATATYPE_TRAIT(long, MPI_LONG) +MPI_DATATYPE_TRAIT(unsigned long, MPI_UNSIGNED_LONG) +MPI_DATATYPE_TRAIT(std::complex, MPI_CXX_DOUBLE_COMPLEX) + #undef MPI_DATATYPE_TRAIT @@ -176,42 +196,57 @@ auto get_mpi_datatype() -> MPI_Datatype { } // namespace mpi +namespace mpi::details { +// Handle composite types using reflection +template +requires std::is_aggregate_v +struct mpi_datatype_trait{ + static MPI_Datatype get_mpi_type() { + static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; + if (mpi_type == MPI_DATATYPE_NULL) { + constexpr size_t num_fields = cista::arity(); + std::vector block_lengths(num_fields, 1); + auto types = std::vector{}; + auto offsets = std::vector{}; + + DataType const tmp{}; + + cista::for_each_field(tmp, [&types, &offsets, &tmp](auto&& field) { + using field_type = std::decay_t; + types.push_back(mpi::get_mpi_datatype()); + auto offset = reinterpret_cast(&field) - reinterpret_cast(&tmp); + offsets.push_back(offset); + }); + + // Create the MPI datatype + if(num_fields != 0) {} + int mpi_error = MPI_Type_create_struct( + static_cast(num_fields), block_lengths.data(), offsets.data(), + types.data(), &mpi_type); + if (mpi_error != MPI_SUCCESS) { + throw std::runtime_error("MPI_Type_create_struct() failed"); + } + + mpi_error = MPI_Type_commit(&mpi_type); + if (mpi_error != MPI_SUCCESS) { + throw std::runtime_error("MPI_Type_commit() failed"); + } + } + return mpi_type; + } +}; +} + struct MyType { int a; double b; + std::complex c; friend bool operator==(const MyType& lhs, const MyType& rhs) { - return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b); + return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c); } friend bool operator!=(const MyType& lhs, const MyType& rhs) { return !(lhs == rhs); } }; - -// Specialize mpi_data_kind_trait for MyType -namespace mpi::details { -template <> -struct mpi_data_kind_trait { - static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; -}; -template <> -struct mpi_datatype_trait { - static MPI_Datatype get_mpi_type() { - static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; - if (mpi_type == MPI_DATATYPE_NULL) { - int block_lengths[2] = {1, 1}; - MPI_Datatype types[2] = {MPI_INT, MPI_DOUBLE}; - MPI_Aint offsets[2]; - - offsets[0] = offsetof(MyType, a); - offsets[1] = offsetof(MyType, b); - - MPI_Type_create_struct(2, block_lengths, offsets, types, &mpi_type); - MPI_Type_commit(&mpi_type); - } - return mpi_type; - } -}; - -} // namespace mpi::details diff --git a/parallel/parallel_src/tests/CMakeLists.txt b/parallel/parallel_src/tests/CMakeLists.txt index 886db697..f5f0e87e 100644 --- a/parallel/parallel_src/tests/CMakeLists.txt +++ b/parallel/parallel_src/tests/CMakeLists.txt @@ -12,8 +12,8 @@ target_link_libraries(catch_mpi_runner PUBLIC Catch2::Catch2) # Parallel contraction add_executable(parallel_contraction_test parallel_contraction/parallel_contraction_test.cpp) -target_link_libraries(parallel_contraction_test PRIVATE Catch2::Catch2WithMain libparallel) -target_link_libraries(parallel_contraction_test PRIVATE libmodified_kahip_interface) +target_link_libraries(parallel_contraction_test PRIVATE Catch2::Catch2WithMain parallel) +target_link_libraries(parallel_contraction_test PRIVATE libmodified_kahip_interface fmt::fmt) catch_discover_tests( parallel_contraction_test @@ -25,7 +25,7 @@ catch_discover_tests( # Parallel contraction add_executable(parallel_contraction_mpi_test parallel_contraction/parallel_contraction_mpi_test.cpp) target_link_libraries(parallel_contraction_mpi_test PRIVATE catch_mpi_runner) -target_link_libraries(parallel_contraction_mpi_test PRIVATE libparallel) +target_link_libraries(parallel_contraction_mpi_test PRIVATE parallel) target_link_libraries(parallel_contraction_mpi_test PRIVATE libmodified_kahip_interface fmt::fmt) catch_discover_tests( diff --git a/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp b/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp index b2cb9e4c..de0d82ba 100644 --- a/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp +++ b/parallel/parallel_src/tests/catch_mpi/catch_mpi_runner.cpp @@ -13,6 +13,7 @@ int main(int argc, char* argv[]) { return returnCode; // global setup... MPI_Init(&argc, &argv); + MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN); int result = session.run(); // global clean-up... MPI_Finalize(); diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index ec416786..f48dd8f1 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include "parallel_contraction_projection/parallel_contraction.h" @@ -18,17 +19,36 @@ class fmt::formatter { constexpr auto parse (format_parse_context& ctx) { return ctx.begin(); } template constexpr auto format (MyType const& foo, Context& ctx) const { - return format_to(ctx.out(), "({},{})", foo.a, foo.b); + return format_to(ctx.out(), "({},{},{}+i{})", foo.a, foo.b, foo.c.real(), foo.c.imag()); } }; +struct MyTestType{ + int a; + float b; + char c; + double d; + long double e; + long long f; + +}; + +struct SuperStruct { + int a; + MyTestType b; +}; + +struct MetaStruct { + SuperStruct s; +}; + TEST_CASE("all to all vector of vectors", "[unit][mpi]") { SECTION("empty cases") { PEID rank, size; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); - const std::vector> v_empty{{{1,2}}, {{1,2}}, {{1,2}}, {{1,2}}, {{1,2}}}; + const std::vector> v_empty{{{1,2,{3, -3}}}, {{1,2,{3, -3}}}, {{1,2,{3, -3}}}, {{1,2,{3, -3}}}, {{1,2,{3, -3}}}}; auto vec = mpi::all_to_all(v_empty, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); fmt::println("rank: {} -> {}", rank, vec); @@ -56,10 +76,26 @@ TEST_CASE("all to all vector of vectors", "[unit][mpi]") { {{}}, {{}}, {{}}, {{}}, {{}}}; const std::vector> empty_weights{ {{}}, {{}}, {{}}, {{}}, {{}}}; + const std::vector> empty_meta{ + {{}}, {{}}, {{}}, {{}}, {{}}}; auto vec_1 = mpi::all_to_all(empty_edges, MPI_COMM_WORLD); auto vec_2 = mpi::all_to_all(empty_weights, MPI_COMM_WORLD); + auto vec_3 = mpi::all_to_all(empty_meta, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); REQUIRE(empty_edges.size() == vec_1.size()); REQUIRE(empty_weights.size() == vec_2.size()); + REQUIRE(empty_meta.size() == vec_3.size()); + } +} + + + +TEST_CASE("MPI Custom Datatype mapping", "[unit][mpi]") { + SECTION("Aggrigate MPI data kinds") { + STATIC_REQUIRE(mpi::mpi_composite_datatype); + STATIC_REQUIRE(mpi::mpi_composite_datatype); + } + SECTION("Aggrigate MPI data type") { + REQUIRE(mpi::get_mpi_datatype() != MPI_DATATYPE_NULL); } } \ No newline at end of file diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index 68fd942f..496700be 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -1,15 +1,18 @@ // // Created by Erich Essmann on 16/08/2024. // +#include #include #include #include #include #include +#include +#include #include "parallel_contraction_projection/parallel_contraction.h" -#include +#include "communication/mpi_tools.h" TEST_CASE("flattening vector of messages", "[unit][mpi]") { SECTION("Empty Vector") { @@ -95,3 +98,60 @@ TEST_CASE("Packing and Unpacking for messages", "[unit][mpi]") { REQUIRE(data == unpacked); } } + +using mpi_native_types = std::tuple; +static auto const mpi_datatypes = + std::array{MPI_CHAR, MPI_WCHAR, MPI_FLOAT, MPI_DOUBLE, + MPI_LONG_DOUBLE, MPI_CXX_BOOL, MPI_INT8_T, MPI_INT16_T, + MPI_INT32_T, MPI_INT64_T, MPI_UINT8_T, MPI_UINT16_T, + MPI_UINT32_T, MPI_UINT64_T, MPI_LONG, MPI_UNSIGNED_LONG}; + +struct MyTestType{ + int a; + float b; + char c; + double d; + long double e; + long long f; + +}; +TEMPLATE_LIST_TEST_CASE("MPI Native Datatype mapping", + "[unit][mpi]", + mpi_native_types) { + SECTION("Native MPI data kinds") { + STATIC_REQUIRE(mpi::mpi_native_datatype); + } + SECTION("Native MPI data type") { + auto matches = + std::ranges::views::transform(mpi_datatypes, [](auto datatype) -> bool { + return (datatype == mpi::get_mpi_datatype()); + }); + auto any_match = std::accumulate(std::begin(matches), std::end(matches), + false, std::logical_or()); + REQUIRE(any_match == true); + } +} From 776ac9b59731050d7f7a905bde45035ee1b2f087 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 30 Sep 2024 14:57:13 +0100 Subject: [PATCH 31/64] Clean up --- .../lib/communication/mpi_tools.h | 237 +++++++++--------- .../lib/communication/mpi_types.h | 42 ++-- 2 files changed, 142 insertions(+), 137 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 854f1e70..4a8426a8 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -22,83 +22,82 @@ #include "partition_config.h" class mpi_tools { -public: - void collect_and_write_labels(MPI_Comm communicator, PPartitionConfig &config, - parallel_graph_access &G); - - void collect_parallel_graph_to_local_graph(MPI_Comm communicator, - PPartitionConfig &config, - parallel_graph_access &G, - complete_graph_access &Q); - - // G is input (only on ROOT) - // G is output (on every other PE) - void distribute_local_graph(MPI_Comm communicator, PPartitionConfig &config, - complete_graph_access &G); - + public: + void collect_and_write_labels(MPI_Comm communicator, + PPartitionConfig& config, + parallel_graph_access& G); + + void collect_parallel_graph_to_local_graph(MPI_Comm communicator, + PPartitionConfig& config, + parallel_graph_access& G, + complete_graph_access& Q); + + // G is input (only on ROOT) + // G is output (on every other PE) + void distribute_local_graph(MPI_Comm communicator, + PPartitionConfig& config, + complete_graph_access& G); }; namespace mpi { -template struct mpi_packed_message { - std::vector packed_message; - std::vector offsets; - std::vector lengths; +template +struct mpi_packed_message { + std::vector packed_message; + std::vector offsets; + std::vector lengths; }; auto exchange_num_messages(std::vector const& num_sent_per_rank, MPI_Comm communicator) -> std::vector; - template - requires std::ranges::forward_range> -auto pack_messages(const Input& messages) - -> mpi_packed_message>> { - using InnerRange = std::ranges::range_value_t; - using ElementType = std::ranges::range_value_t; - - // Flattening the container of containers using views::join - auto flattened_view = messages | std::ranges::views::join; - std::vector flattened_vector{flattened_view.begin(), flattened_view.end()}; - - // Calculating lengths of the inner ranges - std::vector lengths; - lengths.reserve(std::ranges::distance(messages)); - for (const auto& inner : messages) { - lengths.push_back(static_cast(std::ranges::distance(inner))); - } - - // Calculating offsets using exclusive_scan - std::vector offsets(lengths.size()); - std::exclusive_scan(lengths.begin(), lengths.end(), offsets.begin(), 0); - - return mpi_packed_message{flattened_vector, offsets, lengths}; + requires std::ranges::forward_range> +auto pack_messages(const Input& messages) -> mpi_packed_message< + std::ranges::range_value_t>> { + using InnerRange = std::ranges::range_value_t; + using ElementType = std::ranges::range_value_t; + + // Flattening the container of containers using views::join + auto flattened_view = messages | std::ranges::views::join; + std::vector flattened_vector{flattened_view.begin(), + flattened_view.end()}; + + // Calculating lengths of the inner ranges + std::vector lengths; + lengths.reserve(std::ranges::distance(messages)); + for (const auto& inner : messages) { + lengths.push_back(static_cast(std::ranges::distance(inner))); + } + + // Calculating offsets using exclusive_scan + std::vector offsets(lengths.size()); + std::exclusive_scan(lengths.begin(), lengths.end(), offsets.begin(), 0); + + return mpi_packed_message{flattened_vector, offsets, lengths}; } - template auto unpack_messages(const mpi_packed_message& packed_message) - -> std::vector> { - const auto& [recv_buf, recv_displs, recv_counts] = packed_message; - std::size_t num_ranks = recv_counts.size(); - - // Ensure recv_displs and recv_counts have the same size - assert(recv_displs.size() == num_ranks); - - std::vector> result; - result.reserve(num_ranks); - - // Use std::transform to construct the sub-vectors - std::transform( - recv_displs.begin(), recv_displs.end(), recv_counts.begin(), - std::back_inserter(result), - [&recv_buf](int displ, int count) { - auto const start = recv_buf.begin() + displ; - auto const end = start + count; - return std::vector(start, end); - }); - - return result; + -> std::vector> { + const auto& [recv_buf, recv_displs, recv_counts] = packed_message; + std::size_t num_ranks = recv_counts.size(); + + // Ensure recv_displs and recv_counts have the same size + assert(recv_displs.size() == num_ranks); + + std::vector> result; + result.reserve(num_ranks); + + // Use std::transform to construct the sub-vectors + std::transform(recv_displs.begin(), recv_displs.end(), recv_counts.begin(), + std::back_inserter(result), [&recv_buf](int displ, int count) { + auto const start = recv_buf.begin() + displ; + auto const end = start + count; + return std::vector(start, end); + }); + + return result; } /** @@ -118,57 +117,69 @@ auto unpack_messages(const mpi_packed_message& packed_message) * offsets/lengths or if the MPI operation fails. */ template - requires std::ranges::forward_range> + requires std::ranges::forward_range> && + mpi::mpi_datatype< + std::ranges::range_value_t>> auto all_to_all(const Input& sends, MPI_Comm communicator) - -> std::vector>>> { - using InnerRange = std::ranges::range_value_t; - using ElementType = std::ranges::range_value_t; - - PEID rank, size; - MPI_Comm_rank(communicator, &rank); - MPI_Comm_size(communicator, &size); - - // Packing messages - auto [send_packed_messages, send_offsets, send_lengths] = mpi::pack_messages(sends); - - if (send_offsets.size() != send_lengths.size()) { - throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + std::to_string(send_offsets.size()) + - ") != send_lengths.size() (" + std::to_string(send_lengths.size()) + ")"); - } else if ((send_offsets.size() != static_cast(size)) || (send_lengths.size() != static_cast(size))) { - throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + std::to_string(send_offsets.size()) + - ") != mpi size (" + std::to_string(size) + ")"); - } - - // Exchanging message sizes - auto const recv_lengths = exchange_num_messages(send_lengths, communicator); - - // Calculating total receive buffer size - auto const recv_buff_size = std::accumulate(recv_lengths.begin(), recv_lengths.end(), 0); - - // Preparing receive buffers - std::vector recv_packed_messages(recv_buff_size); - std::vector recv_offsets(size, 0); - - // Calculating receive offsets - std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), recv_offsets.begin(), 0); - - // Performing MPI communication - auto mpi_error = MPI_Alltoallv( - send_packed_messages.data(), send_lengths.data(), send_offsets.data(), - get_mpi_datatype(), recv_packed_messages.data(), - recv_lengths.data(), recv_offsets.data(), - get_mpi_datatype(), communicator); - - if (mpi_error != MPI_SUCCESS) { - char error_string[MPI_MAX_ERROR_STRING]; - int length_of_error_string; - MPI_Error_string(mpi_error, error_string, &length_of_error_string); - throw std::runtime_error(std::string("mpi::all_to_all() failed with error: ") + error_string); - } - - // Unpacking messages - return mpi::unpack_messages({recv_packed_messages, recv_offsets, recv_lengths}); + -> std::vector>>> { + using InnerRange = std::ranges::range_value_t; + using ElementType = std::ranges::range_value_t; + + PEID rank, size; + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); + + // Packing messages + auto [send_packed_messages, send_offsets, send_lengths] = + mpi::pack_messages(sends); + + if (send_offsets.size() != send_lengths.size()) { + throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + + std::to_string(send_offsets.size()) + + ") != send_lengths.size() (" + + std::to_string(send_lengths.size()) + ")"); + } else if ((send_offsets.size() != static_cast(size)) || + (send_lengths.size() != static_cast(size))) { + throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + + std::to_string(send_offsets.size()) + + ") != mpi size (" + std::to_string(size) + ")"); + } + + // Exchanging message sizes + auto const recv_lengths = exchange_num_messages(send_lengths, communicator); + + // Calculating total receive buffer size + auto const recv_buff_size = + std::accumulate(recv_lengths.begin(), recv_lengths.end(), 0); + + // Preparing receive buffers + std::vector recv_packed_messages(recv_buff_size); + std::vector recv_offsets(size, 0); + + // Calculating receive offsets + std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), + recv_offsets.begin(), 0); + + // Performing MPI communication + auto mpi_error = MPI_Alltoallv( + send_packed_messages.data(), send_lengths.data(), send_offsets.data(), + get_mpi_datatype(), recv_packed_messages.data(), + recv_lengths.data(), recv_offsets.data(), get_mpi_datatype(), + communicator); + + if (mpi_error != MPI_SUCCESS) { + char error_string[MPI_MAX_ERROR_STRING]; + int length_of_error_string; + MPI_Error_string(mpi_error, error_string, &length_of_error_string); + throw std::runtime_error( + std::string("mpi::all_to_all() failed with error: ") + error_string); + } + + // Unpacking messages + return mpi::unpack_messages( + {recv_packed_messages, recv_offsets, recv_lengths}); } -} // namespace mpi +} // namespace mpi #endif /* end of include guard: MPI_TOOLS_HMESDXF2 */ diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 6f119c0e..2aeaceba 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -6,8 +6,8 @@ #pragma once #include -#include #include +#include #include #include #include @@ -16,8 +16,6 @@ #include #include - - namespace mpi { namespace details { // Define an enum to represent the kind of MPI data @@ -31,15 +29,15 @@ struct mpi_data_kind_trait { // Detect aggregate types and set kind to composite template -requires std::is_aggregate_v + requires std::is_aggregate_v struct mpi_data_kind_trait { static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; }; // Macro to specialize mpi_data_kind_trait for unique base types -#define MPI_BASE_TYPE_KIND(type) \ - template <> \ - struct mpi_data_kind_trait { \ +#define MPI_BASE_TYPE_KIND(type) \ + template <> \ + struct mpi_data_kind_trait { \ static constexpr mpi_data_kinds kind = mpi_data_kinds::base; \ }; @@ -76,11 +74,11 @@ struct mpi_datatype_trait; // Macro to specialize mpi_datatype_trait for unique base types #define MPI_DATATYPE_TRAIT(type, mpi_type_const) \ - template <> \ - struct mpi_datatype_trait { \ - static MPI_Datatype get_mpi_type() { \ - return mpi_type_const; \ - } \ + template <> \ + struct mpi_datatype_trait { \ + static MPI_Datatype get_mpi_type() { \ + return mpi_type_const; \ + } \ }; // Map unique base types to MPI_Datatypes @@ -102,7 +100,6 @@ MPI_DATATYPE_TRAIT(long, MPI_LONG) MPI_DATATYPE_TRAIT(unsigned long, MPI_UNSIGNED_LONG) MPI_DATATYPE_TRAIT(std::complex, MPI_CXX_DOUBLE_COMPLEX) - #undef MPI_DATATYPE_TRAIT } // namespace details @@ -120,7 +117,7 @@ concept mpi_datatype = mpi_native_datatype || mpi_composite_datatype; template -auto get_mpi_datatype() -> MPI_Datatype { +constexpr auto get_mpi_datatype() -> MPI_Datatype { if constexpr (mpi_datatype) { return details::mpi_datatype_trait::get_mpi_type(); } else if constexpr (std::is_same_v) { @@ -196,11 +193,10 @@ auto get_mpi_datatype() -> MPI_Datatype { } // namespace mpi -namespace mpi::details { // Handle composite types using reflection template -requires std::is_aggregate_v -struct mpi_datatype_trait{ + requires std::is_aggregate_v +struct mpi::details::mpi_datatype_trait { static MPI_Datatype get_mpi_type() { static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; if (mpi_type == MPI_DATATYPE_NULL) { @@ -214,15 +210,15 @@ struct mpi_datatype_trait{ cista::for_each_field(tmp, [&types, &offsets, &tmp](auto&& field) { using field_type = std::decay_t; types.push_back(mpi::get_mpi_datatype()); - auto offset = reinterpret_cast(&field) - reinterpret_cast(&tmp); + auto offset = reinterpret_cast(&field) - + reinterpret_cast(&tmp); offsets.push_back(offset); }); // Create the MPI datatype - if(num_fields != 0) {} int mpi_error = MPI_Type_create_struct( - static_cast(num_fields), block_lengths.data(), offsets.data(), - types.data(), &mpi_type); + static_cast(num_fields), block_lengths.data(), offsets.data(), + types.data(), &mpi_type); if (mpi_error != MPI_SUCCESS) { throw std::runtime_error("MPI_Type_create_struct() failed"); } @@ -234,9 +230,7 @@ struct mpi_datatype_trait{ } return mpi_type; } -}; -} - +}; // namespace mpi::details struct MyType { int a; From 84935f7c782e1fabd0571cc59e18d5d61a052d3f Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 30 Sep 2024 15:15:21 +0100 Subject: [PATCH 32/64] All_to_all return type fixed --- .../lib/communication/mpi_tools.h | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 4a8426a8..87b8c67a 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -100,6 +100,17 @@ auto unpack_messages(const mpi_packed_message& packed_message) return result; } +template +concept mpi_nested_range = requires { + std::ranges::forward_range; + std::ranges::forward_range>; + mpi_datatype>>; +}; + +template +using mpi_alltoall_t = std::vector>>>; + /** * @brief Performs an MPI all-to-all communication operation, distributing * data from all processes to all processes. @@ -116,13 +127,9 @@ auto unpack_messages(const mpi_packed_message& packed_message) * @throws std::runtime_error if there's an inconsistency in the send * offsets/lengths or if the MPI operation fails. */ -template - requires std::ranges::forward_range> && - mpi::mpi_datatype< - std::ranges::range_value_t>> -auto all_to_all(const Input& sends, MPI_Comm communicator) - -> std::vector>>> { +template +auto all_to_all(Input const& sends, MPI_Comm communicator) + -> mpi_alltoall_t { using InnerRange = std::ranges::range_value_t; using ElementType = std::ranges::range_value_t; From 0784ac6c8ea68f3e0af514fc3061d319ec72a573 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 30 Sep 2024 16:42:45 +0100 Subject: [PATCH 33/64] Fixed MPI types --- parallel/modified_kahip/CMakeLists.txt | 3 +- parallel/parallel_src/CMakeLists.txt | 2 +- .../lib/communication/mpi_types.h | 160 ++++++++---------- .../parallel_contraction_test.cpp | 21 ++- 4 files changed, 85 insertions(+), 101 deletions(-) diff --git a/parallel/modified_kahip/CMakeLists.txt b/parallel/modified_kahip/CMakeLists.txt index 7795ddcc..2f445567 100644 --- a/parallel/modified_kahip/CMakeLists.txt +++ b/parallel/modified_kahip/CMakeLists.txt @@ -75,6 +75,7 @@ set(LIBMODIFIED_KAFFPA_PARALLEL_SOURCE_FILES lib/tools/mpi_tools.cpp) add_library(libmodified_kaffpa_async OBJECT ${LIBMODIFIED_KAFFPA_PARALLEL_SOURCE_FILES}) -add_library(libmodified_kahip_interface STATIC interface/kaHIP_interface.cpp $ $) +add_library(libmodified_kahip_interface STATIC interface/kaHIP_interface.cpp) +target_link_libraries(libmodified_kahip_interface PRIVATE libmodified_kaffpa libmodified_kaffpa_async) target_link_libraries(libmodified_kahip_interface PRIVATE OpenMP::OpenMP_CXX) target_include_directories(libmodified_kahip_interface PUBLIC interface) diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index 5e27f960..e32818c9 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -37,7 +37,7 @@ set(LIBPARALLEL_SOURCE_FILES lib/tools/distributed_quality_metrics.cpp extern/argtable3-3.2.2/argtable3.c) add_library(parallel ${LIBPARALLEL_SOURCE_FILES}) -target_include_directories(parallel PUBLIC $) +target_link_libraries(parallel PUBLIC libmodified_kahip_interface) target_link_libraries(parallel PUBLIC MPI::MPI_CXX cista::cista) set(LIBGRAPH2BGF_SOURCE_FILES diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 2aeaceba..b8a00972 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -44,20 +44,20 @@ struct mpi_data_kind_trait { // Specializations for unique base types MPI_BASE_TYPE_KIND(char) MPI_BASE_TYPE_KIND(wchar_t) +MPI_BASE_TYPE_KIND(signed char) +MPI_BASE_TYPE_KIND(unsigned char) +MPI_BASE_TYPE_KIND(short) +MPI_BASE_TYPE_KIND(unsigned short) +MPI_BASE_TYPE_KIND(int) +MPI_BASE_TYPE_KIND(unsigned int) +MPI_BASE_TYPE_KIND(long) +MPI_BASE_TYPE_KIND(unsigned long) +MPI_BASE_TYPE_KIND(long long) +MPI_BASE_TYPE_KIND(unsigned long long) MPI_BASE_TYPE_KIND(float) MPI_BASE_TYPE_KIND(double) MPI_BASE_TYPE_KIND(long double) MPI_BASE_TYPE_KIND(bool) -MPI_BASE_TYPE_KIND(int8_t) -MPI_BASE_TYPE_KIND(int16_t) -MPI_BASE_TYPE_KIND(int32_t) -MPI_BASE_TYPE_KIND(int64_t) -MPI_BASE_TYPE_KIND(uint8_t) -MPI_BASE_TYPE_KIND(uint16_t) -MPI_BASE_TYPE_KIND(uint32_t) -MPI_BASE_TYPE_KIND(uint64_t) -MPI_BASE_TYPE_KIND(long) -MPI_BASE_TYPE_KIND(unsigned long) MPI_BASE_TYPE_KIND(std::complex) #undef MPI_BASE_TYPE_KIND @@ -84,20 +84,20 @@ struct mpi_datatype_trait; // Map unique base types to MPI_Datatypes MPI_DATATYPE_TRAIT(char, MPI_CHAR) MPI_DATATYPE_TRAIT(wchar_t, MPI_WCHAR) +MPI_DATATYPE_TRAIT(signed char, MPI_SIGNED_CHAR) +MPI_DATATYPE_TRAIT(unsigned char, MPI_UNSIGNED_CHAR) +MPI_DATATYPE_TRAIT(short, MPI_SHORT) +MPI_DATATYPE_TRAIT(unsigned short, MPI_UNSIGNED_SHORT) +MPI_DATATYPE_TRAIT(int, MPI_INT) +MPI_DATATYPE_TRAIT(unsigned int, MPI_UNSIGNED) +MPI_DATATYPE_TRAIT(long, MPI_LONG) +MPI_DATATYPE_TRAIT(unsigned long, MPI_UNSIGNED_LONG) +MPI_DATATYPE_TRAIT(long long, MPI_LONG_LONG_INT) +MPI_DATATYPE_TRAIT(unsigned long long, MPI_UNSIGNED_LONG_LONG) MPI_DATATYPE_TRAIT(float, MPI_FLOAT) MPI_DATATYPE_TRAIT(double, MPI_DOUBLE) MPI_DATATYPE_TRAIT(long double, MPI_LONG_DOUBLE) MPI_DATATYPE_TRAIT(bool, MPI_CXX_BOOL) -MPI_DATATYPE_TRAIT(int8_t, MPI_INT8_T) -MPI_DATATYPE_TRAIT(int16_t, MPI_INT16_T) -MPI_DATATYPE_TRAIT(int32_t, MPI_INT32_T) -MPI_DATATYPE_TRAIT(int64_t, MPI_INT64_T) -MPI_DATATYPE_TRAIT(uint8_t, MPI_UINT8_T) -MPI_DATATYPE_TRAIT(uint16_t, MPI_UINT16_T) -MPI_DATATYPE_TRAIT(uint32_t, MPI_UINT32_T) -MPI_DATATYPE_TRAIT(uint64_t, MPI_UINT64_T) -MPI_DATATYPE_TRAIT(long, MPI_LONG) -MPI_DATATYPE_TRAIT(unsigned long, MPI_UNSIGNED_LONG) MPI_DATATYPE_TRAIT(std::complex, MPI_CXX_DOUBLE_COMPLEX) #undef MPI_DATATYPE_TRAIT @@ -120,74 +120,58 @@ template constexpr auto get_mpi_datatype() -> MPI_Datatype { if constexpr (mpi_datatype) { return details::mpi_datatype_trait::get_mpi_type(); - } else if constexpr (std::is_same_v) { - // int8_t may be the same as signed char - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // uint8_t may be the same as unsigned char - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // int16_t may be the same as short - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // uint16_t may be the same as unsigned short - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // int32_t may be the same as int or long - if constexpr (sizeof(DataType) == sizeof(int32_t)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(DataType) == sizeof(int64_t)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported integer type"); - } - } else if constexpr (std::is_same_v) { - // uint32_t may be the same as unsigned int or unsigned long - if constexpr (sizeof(DataType) == sizeof(uint32_t)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(DataType) == sizeof(uint64_t)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported unsigned integer type"); - } - } else if constexpr (std::is_same_v) { - // int64_t may be the same as long or long long - if constexpr (sizeof(DataType) == sizeof(int32_t)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(DataType) == sizeof(int64_t)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported integer type"); - } - } else if constexpr (std::is_same_v) { - // uint64_t may be the same as unsigned long or unsigned long long - if constexpr (sizeof(DataType) == sizeof(uint32_t)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(DataType) == sizeof(uint64_t)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported unsigned integer type"); - } - } else if constexpr (std::is_same_v) { - // int64_t may be the same as long or long long - if constexpr (sizeof(DataType) == sizeof(int64_t)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported integer type"); - } - } else if constexpr (std::is_same_v) { - // uint64_t may be the same as unsigned long or unsigned long long - if constexpr (sizeof(DataType) == sizeof(uint64_t)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported unsigned integer type"); - } - } else { - static_assert(sizeof(DataType) == 0, - "Unsupported data type for MPI communication"); - return MPI_DATATYPE_NULL; // This line will never be reached due to - // static_assert - } + } else if constexpr (std::is_same_v) { + // int8_t may be the same as signed char + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // uint8_t may be the same as unsigned char + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // int16_t may be the same as short + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // uint16_t may be the same as unsigned short + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // int32_t may be the same as int or long + if constexpr (sizeof(int32_t) == sizeof(int)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(int32_t) == sizeof(long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported 32-bit integer type"); + } + } else if constexpr (std::is_same_v) { + // uint32_t may be the same as unsigned int or unsigned long + if constexpr (sizeof(uint32_t) == sizeof(unsigned int)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(uint32_t) == sizeof(unsigned long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported 32-bit unsigned integer type"); + } + } else if constexpr (std::is_same_v) { + // int64_t may be the same as long or long long + if constexpr (sizeof(int64_t) == sizeof(long)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(int64_t) == sizeof(long long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported 64-bit integer type"); + } + } else if constexpr (std::is_same_v) { + // uint64_t may be the same as unsigned long or unsigned long long + if constexpr (sizeof(uint64_t) == sizeof(unsigned long)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(uint64_t) == sizeof(unsigned long long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported 64-bit unsigned integer type"); + } + } else { + static_assert(sizeof(DataType) == 0, "Unsupported data type for MPI communication"); + return MPI_DATATYPE_NULL; // This line will never be reached due to static_assert + } return MPI_DATATYPE_NULL; } diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index 496700be..6a9c5b13 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -2,13 +2,13 @@ // Created by Erich Essmann on 16/08/2024. // #include +#include +#include #include #include #include #include #include -#include -#include #include "parallel_contraction_projection/parallel_contraction.h" @@ -124,20 +124,19 @@ using mpi_native_types = std::tuple; -static auto const mpi_datatypes = - std::array{MPI_CHAR, MPI_WCHAR, MPI_FLOAT, MPI_DOUBLE, - MPI_LONG_DOUBLE, MPI_CXX_BOOL, MPI_INT8_T, MPI_INT16_T, - MPI_INT32_T, MPI_INT64_T, MPI_UINT8_T, MPI_UINT16_T, - MPI_UINT32_T, MPI_UINT64_T, MPI_LONG, MPI_UNSIGNED_LONG}; - -struct MyTestType{ - int a; +static auto const mpi_datatypes = std::array{ + MPI_CHAR, MPI_WCHAR, MPI_SIGNED_CHAR, MPI_UNSIGNED_CHAR, + MPI_SHORT, MPI_UNSIGNED_SHORT, MPI_INT, MPI_UNSIGNED, + MPI_LONG, MPI_UNSIGNED_LONG, MPI_LONG_LONG_INT, MPI_UNSIGNED_LONG_LONG, + MPI_FLOAT, MPI_DOUBLE, MPI_LONG_DOUBLE, MPI_CXX_BOOL}; + +struct MyTestType { + int a; float b; char c; double d; long double e; long long f; - }; TEMPLATE_LIST_TEST_CASE("MPI Native Datatype mapping", "[unit][mpi]", From bca00bf1a14e3fa74609699dbc07b50203c7dd8a Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 30 Sep 2024 17:12:42 +0100 Subject: [PATCH 34/64] Cleaning up types --- parallel/parallel_src/lib/communication/mpi_types.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index b8a00972..76aaf403 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -182,7 +182,6 @@ template requires std::is_aggregate_v struct mpi::details::mpi_datatype_trait { static MPI_Datatype get_mpi_type() { - static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; if (mpi_type == MPI_DATATYPE_NULL) { constexpr size_t num_fields = cista::arity(); std::vector block_lengths(num_fields, 1); @@ -214,6 +213,11 @@ struct mpi::details::mpi_datatype_trait { } return mpi_type; } + private: + inline static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; + ~mpi_datatype_trait() { + MPI_Type_free(&mpi_type); + } }; // namespace mpi::details struct MyType { From afbbc03cf7b2a4af372f5f3765b5f3284d0fcb7c Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Wed, 2 Oct 2024 18:03:36 +0100 Subject: [PATCH 35/64] MPI type cleanup working --- .../lib/communication/mpi_types.h | 246 +++++++++++------- 1 file changed, 157 insertions(+), 89 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 76aaf403..7c0603da 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -27,6 +27,63 @@ struct mpi_data_kind_trait { static constexpr mpi_data_kinds kind = mpi_data_kinds::none; }; +// Mutex for thread safety; needed for the cleanup function +inline std::mutex mpi_type_mutex; + +// Function to get the list of custom MPI datatypes +inline std::vector& get_custom_mpi_types() { + static std::vector custom_types; + return custom_types; +} + +// Declare the keyval for the attribute +inline int mpi_type_cleanup_keyval = MPI_KEYVAL_INVALID; + +// Cleanup function called during MPI_Finalize +inline int mpi_type_cleanup(MPI_Comm comm, + int keyval, + void* attribute_val, + void* extra_state) { + std::lock_guard lock(mpi_type_mutex); + + auto& custom_types = get_custom_mpi_types(); + for (MPI_Datatype& datatype : custom_types) { + if (datatype != MPI_DATATYPE_NULL) { + MPI_Type_free(&datatype); + datatype = MPI_DATATYPE_NULL; + } + } + custom_types.clear(); + + // Free the keyval + MPI_Comm_free_keyval(&mpi_type_cleanup_keyval); + mpi_type_cleanup_keyval = MPI_KEYVAL_INVALID; + + return MPI_SUCCESS; +} + +// Initialize the attribute and set the cleanup function +inline void initialize_mpi_type_cleanup() { + // Use a static function-local variable to ensure initialization occurs only + // once + static bool initialized = []() { + int mpi_error = MPI_Comm_create_keyval(MPI_NULL_COPY_FN, mpi_type_cleanup, &mpi_type_cleanup_keyval, nullptr); + if (mpi_error != MPI_SUCCESS) { + throw std::runtime_error("MPI_Comm_create_keyval() failed"); + } + + // Set an attribute on MPI_COMM_SELF + mpi_error = + MPI_Comm_set_attr(MPI_COMM_SELF, mpi_type_cleanup_keyval, nullptr); + if (mpi_error != MPI_SUCCESS) { + throw std::runtime_error("MPI_Comm_set_attr() failed"); + } + + return true; + }(); + (void)initialized; // Suppress unused variable warning +} + // Detect aggregate types and set kind to composite template requires std::is_aggregate_v @@ -35,9 +92,9 @@ struct mpi_data_kind_trait { }; // Macro to specialize mpi_data_kind_trait for unique base types -#define MPI_BASE_TYPE_KIND(type) \ - template <> \ - struct mpi_data_kind_trait { \ +#define MPI_BASE_TYPE_KIND(type) \ + template <> \ + struct mpi_data_kind_trait { \ static constexpr mpi_data_kinds kind = mpi_data_kinds::base; \ }; @@ -61,24 +118,18 @@ MPI_BASE_TYPE_KIND(bool) MPI_BASE_TYPE_KIND(std::complex) #undef MPI_BASE_TYPE_KIND -} // namespace details -// Concept to check if a type is a native MPI datatype -template -concept mpi_native_datatype = (details::mpi_data_kind_trait::kind == - details::mpi_data_kinds::base); -namespace details { // Trait to get the MPI_Datatype for a given DataType template struct mpi_datatype_trait; // Macro to specialize mpi_datatype_trait for unique base types -#define MPI_DATATYPE_TRAIT(type, mpi_type_const) \ - template <> \ - struct mpi_datatype_trait { \ - static MPI_Datatype get_mpi_type() { \ - return mpi_type_const; \ - } \ +#define MPI_DATATYPE_TRAIT(type, mpi_type_const) \ + template <> \ + struct mpi_datatype_trait { \ + static MPI_Datatype get_mpi_type() { \ + return mpi_type_const; \ + } \ }; // Map unique base types to MPI_Datatypes @@ -104,8 +155,11 @@ MPI_DATATYPE_TRAIT(std::complex, MPI_CXX_DOUBLE_COMPLEX) } // namespace details -// Function to get the MPI_Datatype for a given DataType -// Concept to check if a type is an MPI composite datatype (for completeness) +// Concept to check if a type is a native MPI datatype +template +concept mpi_native_datatype = (details::mpi_data_kind_trait::kind == + details::mpi_data_kinds::base); + template concept mpi_composite_datatype = (details::mpi_data_kind_trait::kind == @@ -117,76 +171,82 @@ concept mpi_datatype = mpi_native_datatype || mpi_composite_datatype; template -constexpr auto get_mpi_datatype() -> MPI_Datatype { - if constexpr (mpi_datatype) { +auto get_mpi_datatype() -> MPI_Datatype { + if constexpr (mpi_native_datatype) { + return details::mpi_datatype_trait::get_mpi_type(); + } else if constexpr (mpi_composite_datatype) { return details::mpi_datatype_trait::get_mpi_type(); - } else if constexpr (std::is_same_v) { - // int8_t may be the same as signed char - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // uint8_t may be the same as unsigned char - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // int16_t may be the same as short - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // uint16_t may be the same as unsigned short - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // int32_t may be the same as int or long - if constexpr (sizeof(int32_t) == sizeof(int)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(int32_t) == sizeof(long)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported 32-bit integer type"); - } - } else if constexpr (std::is_same_v) { - // uint32_t may be the same as unsigned int or unsigned long - if constexpr (sizeof(uint32_t) == sizeof(unsigned int)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(uint32_t) == sizeof(unsigned long)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported 32-bit unsigned integer type"); - } - } else if constexpr (std::is_same_v) { - // int64_t may be the same as long or long long - if constexpr (sizeof(int64_t) == sizeof(long)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(int64_t) == sizeof(long long)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported 64-bit integer type"); - } - } else if constexpr (std::is_same_v) { - // uint64_t may be the same as unsigned long or unsigned long long - if constexpr (sizeof(uint64_t) == sizeof(unsigned long)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(uint64_t) == sizeof(unsigned long long)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported 64-bit unsigned integer type"); - } - } else { - static_assert(sizeof(DataType) == 0, "Unsupported data type for MPI communication"); - return MPI_DATATYPE_NULL; // This line will never be reached due to static_assert - } - return MPI_DATATYPE_NULL; + } else if constexpr (std::is_same_v) { + // int8_t may be the same as signed char + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // uint8_t may be the same as unsigned char + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // int16_t may be the same as short + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // uint16_t may be the same as unsigned short + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // int32_t may be the same as int or long + if constexpr (sizeof(int32_t) == sizeof(int)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(int32_t) == sizeof(long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported 32-bit integer type"); + } + } else if constexpr (std::is_same_v) { + // uint32_t may be the same as unsigned int or unsigned long + if constexpr (sizeof(uint32_t) == sizeof(unsigned int)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(uint32_t) == sizeof(unsigned long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, + "Unsupported 32-bit unsigned integer type"); + } + } else if constexpr (std::is_same_v) { + // int64_t may be the same as long or long long + if constexpr (sizeof(int64_t) == sizeof(long)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(int64_t) == sizeof(long long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported 64-bit integer type"); + } + } else if constexpr (std::is_same_v) { + // uint64_t may be the same as unsigned long or unsigned long long + if constexpr (sizeof(uint64_t) == sizeof(unsigned long)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(uint64_t) == sizeof(unsigned long long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, + "Unsupported 64-bit unsigned integer type"); + } + } else { + static_assert(sizeof(DataType) == 0, + "Unsupported data type for MPI communication"); + return MPI_DATATYPE_NULL; // This line will never be reached due to + // static_assert + } } -} // namespace mpi - // Handle composite types using reflection template - requires std::is_aggregate_v + requires mpi_composite_datatype struct mpi::details::mpi_datatype_trait { static MPI_Datatype get_mpi_type() { - if (mpi_type == MPI_DATATYPE_NULL) { + // Function-local static variable initialized via lambda + static MPI_Datatype mpi_type = []() -> MPI_Datatype { + MPI_Datatype mpi_type = MPI_DATATYPE_NULL; + constexpr size_t num_fields = cista::arity(); std::vector block_lengths(num_fields, 1); - auto types = std::vector{}; - auto offsets = std::vector{}; + std::vector types; + std::vector offsets; DataType const tmp{}; @@ -195,7 +255,7 @@ struct mpi::details::mpi_datatype_trait { types.push_back(mpi::get_mpi_datatype()); auto offset = reinterpret_cast(&field) - reinterpret_cast(&tmp); - offsets.push_back(offset); + offsets.push_back(static_cast(offset)); }); // Create the MPI datatype @@ -210,19 +270,27 @@ struct mpi::details::mpi_datatype_trait { if (mpi_error != MPI_SUCCESS) { throw std::runtime_error("MPI_Type_commit() failed"); } - } + + // Register the datatype and initialize cleanup + { + std::lock_guard lock(mpi::details::mpi_type_mutex); + mpi::details::get_custom_mpi_types().push_back(mpi_type); + mpi::details::initialize_mpi_type_cleanup(); + } + + return mpi_type; + }(); + return mpi_type; } - private: - inline static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; - ~mpi_datatype_trait() { - MPI_Type_free(&mpi_type); - } -}; // namespace mpi::details +}; +} // namespace mpi + +// Example struct struct MyType { - int a; - double b; + int a{}; + double b{}; std::complex c; friend bool operator==(const MyType& lhs, const MyType& rhs) { @@ -231,4 +299,4 @@ struct MyType { friend bool operator!=(const MyType& lhs, const MyType& rhs) { return !(lhs == rhs); } -}; +}; \ No newline at end of file From e3b52632b73cc4a88b13003d745295e81db4ab89 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 3 Oct 2024 12:13:33 +0100 Subject: [PATCH 36/64] Tidy up of mutex locks --- .../parallel_src/lib/communication/mpi_types.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 7c0603da..6b72fc7d 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -37,14 +37,14 @@ inline std::vector& get_custom_mpi_types() { } // Declare the keyval for the attribute -inline int mpi_type_cleanup_keyval = MPI_KEYVAL_INVALID; +inline constinit int mpi_type_cleanup_keyval = MPI_KEYVAL_INVALID; // Cleanup function called during MPI_Finalize inline int mpi_type_cleanup(MPI_Comm comm, int keyval, void* attribute_val, void* extra_state) { - std::lock_guard lock(mpi_type_mutex); + std::scoped_lock lock{mpi_type_mutex}; auto& custom_types = get_custom_mpi_types(); for (MPI_Datatype& datatype : custom_types) { @@ -157,13 +157,11 @@ MPI_DATATYPE_TRAIT(std::complex, MPI_CXX_DOUBLE_COMPLEX) // Concept to check if a type is a native MPI datatype template -concept mpi_native_datatype = (details::mpi_data_kind_trait::kind == - details::mpi_data_kinds::base); +concept mpi_native_datatype = (details::mpi_data_kind_trait::kind == details::mpi_data_kinds::base); template concept mpi_composite_datatype = - (details::mpi_data_kind_trait::kind == - details::mpi_data_kinds::composite); + (details::mpi_data_kind_trait::kind == details::mpi_data_kinds::composite); // mpi_datatype concept combines native and composite datatypes template @@ -255,7 +253,7 @@ struct mpi::details::mpi_datatype_trait { types.push_back(mpi::get_mpi_datatype()); auto offset = reinterpret_cast(&field) - reinterpret_cast(&tmp); - offsets.push_back(static_cast(offset)); + offsets.push_back(offset); }); // Create the MPI datatype @@ -273,7 +271,7 @@ struct mpi::details::mpi_datatype_trait { // Register the datatype and initialize cleanup { - std::lock_guard lock(mpi::details::mpi_type_mutex); + std::scoped_lock lock{mpi::details::mpi_type_mutex}; mpi::details::get_custom_mpi_types().push_back(mpi_type); mpi::details::initialize_mpi_type_cleanup(); } From 2b57658b30675ee3e24ab36f1d36006cec410c8c Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 3 Oct 2024 13:08:46 +0100 Subject: [PATCH 37/64] Added LTO --- CMakeLists.txt | 10 +++ .../parallel_src/extern/cista/CMakeLists.txt | 72 ------------------- 2 files changed, 10 insertions(+), 72 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d55b2e55..006d6dca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,16 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +include(CheckIPOSupported) +check_ipo_supported(RESULT supported OUTPUT error) + +if( supported ) + message(STATUS "IPO / LTO enabled") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +else() + message(STATUS "IPO / LTO not supported: <${error}>") +endif() + # if no build mode is specified build in release mode if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "Release") diff --git a/parallel/parallel_src/extern/cista/CMakeLists.txt b/parallel/parallel_src/extern/cista/CMakeLists.txt index 11aa026f..a2af5217 100755 --- a/parallel/parallel_src/extern/cista/CMakeLists.txt +++ b/parallel/parallel_src/extern/cista/CMakeLists.txt @@ -78,11 +78,8 @@ if (${CISTA_GENERATE_TO_TUPLE}) add_dependencies(cista generate_to_tuple) endif() -add_subdirectory(tools/doctest EXCLUDE_FROM_ALL) - file(GLOB_RECURSE cista-include-files include/*.h*) -add_subdirectory(tools/uniter EXCLUDE_FROM_ALL) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/cista.h COMMAND uniter @@ -96,75 +93,6 @@ add_custom_command( DEPENDS ${cista-include-files} ) -file(GLOB_RECURSE cista-test-files test/*.cc) -add_executable(cista-test-single-header EXCLUDE_FROM_ALL ${cista-test-files} ${CMAKE_CURRENT_BINARY_DIR}/cista.h) -target_link_libraries(cista-test-single-header cista-doctest) -target_include_directories(cista-test-single-header PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -target_compile_options(cista-test-single-header PRIVATE ${cista-compile-flags}) -target_compile_definitions(cista-test-single-header PRIVATE SINGLE_HEADER) -target_compile_features(cista-test-single-header PRIVATE cxx_std_17) -if (CISTA_ZERO_OUT) - target_compile_definitions(cista-test-single-header PRIVATE CISTA_ZERO_OUT=1) -endif() - -add_executable(cista-test EXCLUDE_FROM_ALL ${cista-test-files}) -target_compile_options(cista-test PRIVATE ${cista-compile-flags}) -target_link_libraries(cista-test cista-doctest cista) -if(CISTA_COVERAGE) - target_compile_options(cista-test PRIVATE -fprofile-arcs -ftest-coverage) - set_target_properties(cista-test PROPERTIES LINK_FLAGS --coverage) -endif() - -add_custom_target(cista-coverage - rm -rf *.info && - find . -name "*.gcda" -delete && - ./cista-test && - lcov --directory . --capture --output-file cov.info && - lcov -r cov.info "*usr/include/*" -o cov.info && - lcov -r cov.info "*doctest*" -o cov.info && - lcov -r cov.info "*test/*.cc" -o cov.info && - lcov -r cov.info "*v1*" -o cov.info -) -add_dependencies(cista-coverage cista-test) - -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR - "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") - message(STATUS "Cista fuzzing enabled") - set(cista-fuzz-targets "") - file(GLOB_RECURSE fuzz-files fuzz/*.cc) - foreach(fuzz-file ${fuzz-files}) - get_filename_component(test-name ${fuzz-file} NAME_WE) - - add_executable(cista-fuzz-${test-name}-main EXCLUDE_FROM_ALL ${fuzz-file}) - target_link_libraries(cista-fuzz-${test-name}-main cista -fsanitize=address,undefined) - target_compile_options(cista-fuzz-${test-name}-main PRIVATE -g -O0) - target_compile_definitions(cista-fuzz-${test-name}-main PRIVATE MAIN) - - add_executable(cista-fuzz-${test-name} EXCLUDE_FROM_ALL ${fuzz-file}) - target_link_libraries(cista-fuzz-${test-name} cista -fsanitize=address,undefined,fuzzer) - target_compile_options(cista-fuzz-${test-name} PRIVATE -g -O0 -fsanitize=address,fuzzer) - - add_executable(cista-fuzz-${test-name}-seed EXCLUDE_FROM_ALL ${fuzz-file}) - target_link_libraries(cista-fuzz-${test-name}-seed cista) - target_compile_definitions(cista-fuzz-${test-name}-seed PRIVATE GENERATE_SEED) - - add_custom_target(cista-fuzz-${test-name}-run - DEPENDS - cista-fuzz-${test-name} - cista-fuzz-${test-name}-seed - COMMAND - mkdir -p fuzz-${test-name}-corpus && - ./cista-fuzz-${test-name}-seed ./fuzz-${test-name}-corpus/seed.bin && - ./cista-fuzz-${test-name} ./fuzz-${test-name}-corpus -max_total_time=120 - ) - - list(APPEND cista-fuzz-targets cista-fuzz-${test-name}-run) - endforeach() - - add_custom_target(cista-fuzz) - add_dependencies(cista-fuzz ${cista-fuzz-targets}) -endif() - add_library(cista::cista ALIAS cista) # Export targets when not used via `add_subdirectory` From 2781b493114810e61f0c4b38d9b442c8f0e5ff47 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 3 Oct 2024 14:59:07 +0100 Subject: [PATCH 38/64] Fixed include --- parallel/parallel_src/lib/communication/mpi_types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 6b72fc7d..aeb53c77 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include From 85e6336957dbc9eefccb8b745054d9105094e0cf Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 3 Oct 2024 15:39:42 +0100 Subject: [PATCH 39/64] Fixed tests --- .../parallel_src/lib/communication/mpi_types.h | 2 +- .../parallel_contraction_mpi_test.cpp | 15 ++------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index aeb53c77..ef17a067 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -291,7 +291,7 @@ struct mpi::details::mpi_datatype_trait { struct MyType { int a{}; double b{}; - std::complex c; + double c{}; friend bool operator==(const MyType& lhs, const MyType& rhs) { return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c); diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index f48dd8f1..711e1ac2 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -1,27 +1,17 @@ // // Created by Erich Essmann on 16/08/2024. // -#include #include #include #include #include - #include #include #include +#include "communication/mpi_tools.h" #include "parallel_contraction_projection/parallel_contraction.h" -template <> -class fmt::formatter { -public: - constexpr auto parse (format_parse_context& ctx) { return ctx.begin(); } - template - constexpr auto format (MyType const& foo, Context& ctx) const { - return format_to(ctx.out(), "({},{},{}+i{})", foo.a, foo.b, foo.c.real(), foo.c.imag()); - } -}; struct MyTestType{ int a; @@ -48,10 +38,9 @@ TEST_CASE("all to all vector of vectors", "[unit][mpi]") { MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); - const std::vector> v_empty{{{1,2,{3, -3}}}, {{1,2,{3, -3}}}, {{1,2,{3, -3}}}, {{1,2,{3, -3}}}, {{1,2,{3, -3}}}}; + const std::vector> v_empty{{{1,2,3}}, {{1,2,3}}, {{1,2,3}}, {{1,2,3}}, {{1,2,3}}}; auto vec = mpi::all_to_all(v_empty, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); - fmt::println("rank: {} -> {}", rank, vec); REQUIRE(v_empty == vec); } SECTION("complex case") { From 02e2d714c104706d465f0fecb360178fa4ade9e7 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 3 Oct 2024 15:59:01 +0100 Subject: [PATCH 40/64] Fixing concept warnings --- parallel/parallel_src/extern/nameof.hpp | 1254 ----------------- .../lib/communication/mpi_tools.h | 8 +- .../lib/communication/mpi_types.h | 16 +- 3 files changed, 15 insertions(+), 1263 deletions(-) delete mode 100644 parallel/parallel_src/extern/nameof.hpp diff --git a/parallel/parallel_src/extern/nameof.hpp b/parallel/parallel_src/extern/nameof.hpp deleted file mode 100644 index 3154847b..00000000 --- a/parallel/parallel_src/extern/nameof.hpp +++ /dev/null @@ -1,1254 +0,0 @@ -// _ _ __ _____ -// | \ | | / _| / ____|_ _ -// | \| | __ _ _ __ ___ ___ ___ | |_ | | _| |_ _| |_ -// | . ` |/ _` | '_ ` _ \ / _ \/ _ \| _| | | |_ _|_ _| -// | |\ | (_| | | | | | | __/ (_) | | | |____|_| |_| -// |_| \_|\__,_|_| |_| |_|\___|\___/|_| \_____| -// https://github.com/Neargye/nameof -// version 0.10.4 -// -// Licensed under the MIT License . -// SPDX-License-Identifier: MIT -// Copyright (c) 2016 - 2024 Daniil Goncharov . -// -// 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 NEARGYE_NAMEOF_HPP -#define NEARGYE_NAMEOF_HPP - -#define NAMEOF_VERSION_MAJOR 0 -#define NAMEOF_VERSION_MINOR 10 -#define NAMEOF_VERSION_PATCH 4 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(NAMEOF_USING_ALIAS_STRING) -# include -#endif -#if !defined(NAMEOF_USING_ALIAS_STRING_VIEW) -# include -#endif - -#if __has_include() -# include -# include -#endif - -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunknown-warning-option" -# pragma clang diagnostic ignored "-Wenum-constexpr-conversion" -#elif defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wstringop-overflow" // Missing terminating nul 'enum_name_v'. -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 26495) // Variable 'cstring::chars_' is uninitialized. -# pragma warning(disable : 28020) // Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. -# pragma warning(disable : 26451) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call. -# pragma warning(disable : 4514) // Unreferenced inline function has been removed. -#endif - -// Checks nameof_type compiler compatibility. -#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && _MSC_VER >= 1910 -# undef NAMEOF_TYPE_SUPPORTED -# define NAMEOF_TYPE_SUPPORTED 1 -#endif - -// Checks nameof_type_rtti compiler compatibility. -#if defined(__clang__) -# if __has_feature(cxx_rtti) -# undef NAMEOF_TYPE_RTTI_SUPPORTED -# define NAMEOF_TYPE_RTTI_SUPPORTED 1 -# endif -#elif defined(__GNUC__) -# if defined(__GXX_RTTI) -# undef NAMEOF_TYPE_RTTI_SUPPORTED -# define NAMEOF_TYPE_RTTI_SUPPORTED 1 -# endif -#elif defined(_MSC_VER) -# if defined(_CPPRTTI) -# undef NAMEOF_TYPE_RTTI_SUPPORTED -# define NAMEOF_TYPE_RTTI_SUPPORTED 1 -# endif -#endif - -// Checks nameof_member compiler compatibility. -#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L -# undef NAMEOF_MEMBER_SUPPORTED -# define NAMEOF_MEMBER_SUPPORTED 1 -#endif - -// Checks nameof_pointer compiler compatibility. -#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L -# undef NAMEOF_POINTER_SUPPORTED -# define NAMEOF_POINTER_SUPPORTED 1 -#endif - -// Checks nameof_enum compiler compatibility. -#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 -# undef NAMEOF_ENUM_SUPPORTED -# define NAMEOF_ENUM_SUPPORTED 1 -#endif - -// Checks nameof_enum compiler aliases compatibility. -#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1920 -# undef NAMEOF_ENUM_SUPPORTED_ALIASES -# define NAMEOF_ENUM_SUPPORTED_ALIASES 1 -#endif - -// Enum value must be greater or equals than NAMEOF_ENUM_RANGE_MIN. By default NAMEOF_ENUM_RANGE_MIN = -128. -// If need another min range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN. -#if !defined(NAMEOF_ENUM_RANGE_MIN) -# define NAMEOF_ENUM_RANGE_MIN -128 -#endif - -// Enum value must be less or equals than NAMEOF_ENUM_RANGE_MAX. By default NAMEOF_ENUM_RANGE_MAX = 128. -// If need another max range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MAX. -#if !defined(NAMEOF_ENUM_RANGE_MAX) -# define NAMEOF_ENUM_RANGE_MAX 128 -#endif - -namespace nameof { - -// If need another string_view type, define the macro NAMEOF_USING_ALIAS_STRING_VIEW. -#if defined(NAMEOF_USING_ALIAS_STRING_VIEW) -NAMEOF_USING_ALIAS_STRING_VIEW -#else -using std::string_view; -#endif - -// If need another string type, define the macro NAMEOF_USING_ALIAS_STRING. -#if defined(NAMEOF_USING_ALIAS_STRING) -NAMEOF_USING_ALIAS_STRING -#else -using std::string; -#endif - -namespace customize { - -// Enum value must be in range [NAMEOF_ENUM_RANGE_MIN, NAMEOF_ENUM_RANGE_MAX]. By default NAMEOF_ENUM_RANGE_MIN = -128, NAMEOF_ENUM_RANGE_MAX = 128. -// If you need another range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN and NAMEOF_ENUM_RANGE_MAX. -// If you need another range for specific enum type, add specialization enum_range for necessary enum type. -template -struct enum_range { - static_assert(std::is_enum_v, "nameof::customize::enum_range requires enum type."); - inline static constexpr int min = NAMEOF_ENUM_RANGE_MIN; - inline static constexpr int max = NAMEOF_ENUM_RANGE_MAX; - static_assert(max > min, "nameof::customize::enum_range requires max > min."); -}; - -static_assert(NAMEOF_ENUM_RANGE_MIN <= 0, "NAMEOF_ENUM_RANGE_MIN must be less or equals than 0."); -static_assert(NAMEOF_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "NAMEOF_ENUM_RANGE_MIN must be greater than INT16_MIN."); - -static_assert(NAMEOF_ENUM_RANGE_MAX > 0, "NAMEOF_ENUM_RANGE_MAX must be greater than 0."); -static_assert(NAMEOF_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "NAMEOF_ENUM_RANGE_MAX must be less than INT16_MAX."); - -static_assert(NAMEOF_ENUM_RANGE_MAX > NAMEOF_ENUM_RANGE_MIN, "NAMEOF_ENUM_RANGE_MAX must be greater than NAMEOF_ENUM_RANGE_MIN."); - -// If you need custom names for enum, add specialization enum_name for necessary enum type. -template -constexpr string_view enum_name(E) noexcept { - static_assert(std::is_enum_v, "nameof::customize::enum_name requires enum type."); - return {}; -} - -// If you need custom name for type, add specialization type_name for necessary type. -template -constexpr string_view type_name() noexcept { - return {}; -} - -// If you need custom name for member, add specialization member_name for necessary type. -template -constexpr string_view member_name() noexcept { - return {}; -} - -// If you need custom name for a pointer, add specialization pointer_name for necessary type. -template -constexpr string_view pointer_name() noexcept { - return {}; -} - -} // namespace nameof::customize - -template -class [[nodiscard]] cstring { - public: - using value_type = const char; - using size_type = std::uint16_t; - using difference_type = std::ptrdiff_t; - using pointer = const char*; - using const_pointer = const char*; - using reference = const char&; - using const_reference = const char&; - - using iterator = const char*; - using const_iterator = const char*; - - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - constexpr explicit cstring(string_view str) noexcept : cstring{str, std::make_integer_sequence{}} { - assert(str.size() > 0 && str.size() == N); - } - - constexpr cstring() = delete; - - constexpr cstring(const cstring&) = default; - - constexpr cstring(cstring&&) = default; - - ~cstring() = default; - - cstring& operator=(const cstring&) = default; - - cstring& operator=(cstring&&) = default; - - [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; } - - [[nodiscard]] constexpr size_type size() const noexcept { return N; } - - [[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); } - - [[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); } - - [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); } - - [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } - - [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return end(); } - - [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return begin(); } - - [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } - - [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); } - - [[nodiscard]] constexpr const_reference operator[](size_type i) const noexcept { return assert(i < size()), chars_[i]; } - - [[nodiscard]] constexpr const_reference front() const noexcept { return chars_[0]; } - - [[nodiscard]] constexpr const_reference back() const noexcept { return chars_[N]; } - - [[nodiscard]] constexpr size_type length() const noexcept { return size(); } - - [[nodiscard]] constexpr bool empty() const noexcept { return false; } - - [[nodiscard]] constexpr int compare(string_view str) const noexcept { return string_view{data(), size()}.compare(str); } - - [[nodiscard]] constexpr const char* c_str() const noexcept { return data(); } - - [[nodiscard]] string str() const { return {begin(), end()}; } - - [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; } - - [[nodiscard]] constexpr explicit operator const_pointer() const noexcept { return data(); } - - [[nodiscard]] explicit operator string() const { return {begin(), end()}; } - - private: - template - constexpr cstring(string_view str, std::integer_sequence) noexcept : chars_{str[I]..., '\0'} {} - - char chars_[static_cast(N) + 1]; -}; - -template <> -class [[nodiscard]] cstring<0> { - public: - using value_type = const char; - using size_type = std::uint16_t; - using difference_type = std::ptrdiff_t; - using pointer = const char*; - using const_pointer = const char*; - using reference = const char&; - using const_reference = const char&; - - using iterator = const char*; - using const_iterator = const char*; - - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - constexpr explicit cstring(string_view) noexcept {} - - constexpr cstring() = default; - - constexpr cstring(const cstring&) = default; - - constexpr cstring(cstring&&) = default; - - ~cstring() = default; - - cstring& operator=(const cstring&) = default; - - cstring& operator=(cstring&&) = default; - - [[nodiscard]] constexpr const_pointer data() const noexcept { return nullptr; } - - [[nodiscard]] constexpr size_type size() const noexcept { return 0; } - - [[nodiscard]] constexpr const_iterator begin() const noexcept { return nullptr; } - - [[nodiscard]] constexpr const_iterator end() const noexcept { return nullptr; } - - [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return nullptr; } - - [[nodiscard]] constexpr const_iterator cend() const noexcept { return nullptr; } - - [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return {}; } - - [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return {}; } - - [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return {}; } - - [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return {}; } - - [[nodiscard]] constexpr size_type length() const noexcept { return 0; } - - [[nodiscard]] constexpr bool empty() const noexcept { return true; } - - [[nodiscard]] constexpr int compare(string_view str) const noexcept { return string_view{}.compare(str); } - - [[nodiscard]] constexpr const char* c_str() const noexcept { return nullptr; } - - [[nodiscard]] string str() const { return {}; } - - [[nodiscard]] constexpr operator string_view() const noexcept { return {}; } - - [[nodiscard]] constexpr explicit operator const_pointer() const noexcept { return nullptr; } - - [[nodiscard]] explicit operator string() const { return {}; } -}; - -template -[[nodiscard]] constexpr bool operator==(const cstring& lhs, string_view rhs) noexcept { - return lhs.compare(rhs) == 0; -} - -template -[[nodiscard]] constexpr bool operator==(string_view lhs, const cstring& rhs) noexcept { - return lhs.compare(rhs) == 0; -} - -template -[[nodiscard]] constexpr bool operator!=(const cstring& lhs, string_view rhs) noexcept { - return lhs.compare(rhs) != 0; -} - -template -[[nodiscard]] constexpr bool operator!=(string_view lhs, const cstring& rhs) noexcept { - return lhs.compare(rhs) != 0; -} - -template -[[nodiscard]] constexpr bool operator>(const cstring& lhs, string_view rhs) noexcept { - return lhs.compare(rhs) > 0; -} - -template -[[nodiscard]] constexpr bool operator>(string_view lhs, const cstring& rhs) noexcept { - return lhs.compare(rhs) > 0; -} - -template -[[nodiscard]] constexpr bool operator>=(const cstring& lhs, string_view rhs) noexcept { - return lhs.compare(rhs) >= 0; -} - -template -[[nodiscard]] constexpr bool operator>=(string_view lhs, const cstring& rhs) noexcept { - return lhs.compare(rhs) >= 0; -} - -template -[[nodiscard]] constexpr bool operator<(const cstring& lhs, string_view rhs) noexcept { - return lhs.compare(rhs) < 0; -} - -template -[[nodiscard]] constexpr bool operator<(string_view lhs, const cstring& rhs) noexcept { - return lhs.compare(rhs) < 0; -} - -template -[[nodiscard]] constexpr bool operator<=(const cstring& lhs, string_view rhs) noexcept { - return lhs.compare(rhs) <= 0; -} - -template -[[nodiscard]] constexpr bool operator<=(string_view lhs, const cstring& rhs) noexcept { - return lhs.compare(rhs) <= 0; -} - -template -std::basic_ostream& operator<<(std::basic_ostream& os, const cstring& srt) { - for (const auto c : srt) { - os.put(c); - } - return os; -} - -namespace detail { - -constexpr string_view pretty_name(string_view name, bool remove_suffix = true) noexcept { - if (name.size() >= 1 && (name[0] == '"' || name[0] == '\'')) { - return {}; // Narrow multibyte string literal. - } else if (name.size() >= 2 && name[0] == 'R' && (name[1] == '"' || name[1] == '\'')) { - return {}; // Raw string literal. - } else if (name.size() >= 2 && name[0] == 'L' && (name[1] == '"' || name[1] == '\'')) { - return {}; // Wide string literal. - } else if (name.size() >= 2 && name[0] == 'U' && (name[1] == '"' || name[1] == '\'')) { - return {}; // UTF-32 encoded string literal. - } else if (name.size() >= 2 && name[0] == 'u' && (name[1] == '"' || name[1] == '\'')) { - return {}; // UTF-16 encoded string literal. - } else if (name.size() >= 3 && name[0] == 'u' && name[1] == '8' && (name[2] == '"' || name[2] == '\'')) { - return {}; // UTF-8 encoded string literal. - } else if (name.size() >= 1 && (name[0] >= '0' && name[0] <= '9')) { - return {}; // Invalid name. - } - - for (std::size_t i = name.size(), h = 0, s = 0; i > 0; --i) { - if (name[i - 1] == ')') { - ++h; - ++s; - continue; - } else if (name[i - 1] == '(') { - --h; - ++s; - continue; - } - - if (h == 0) { - name.remove_suffix(s); - break; - } else { - ++s; - continue; - } - } - - std::size_t s = 0; - for (std::size_t i = name.size(), h = 0; i > 0; --i) { - if (name[i - 1] == '>') { - ++h; - ++s; - continue; - } else if (name[i - 1] == '<') { - --h; - ++s; - continue; - } - - if (h == 0) { - break; - } else { - ++s; - continue; - } - } - - for (std::size_t i = name.size() - s; i > 0; --i) { - if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || - (name[i - 1] >= 'a' && name[i - 1] <= 'z') || - (name[i - 1] >= 'A' && name[i - 1] <= 'Z') || - (name[i - 1] == '_'))) { - name.remove_prefix(i); - break; - } - } - if (remove_suffix) { - name.remove_suffix(s); - } - - if (name.size() > 0 && ((name[0] >= 'a' && name[0] <= 'z') || - (name[0] >= 'A' && name[0] <= 'Z') || - (name[0] == '_'))) { - return name; - } - - return {}; // Invalid name. -} - -#if defined(__cpp_lib_array_constexpr) && __cpp_lib_array_constexpr >= 201603L -# define NAMEOF_ARRAY_CONSTEXPR 1 -#else -template -constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) noexcept { - return {{a[I]...}}; -} -#endif - -template -constexpr bool cmp_less(L lhs, R rhs) noexcept { - static_assert(std::is_integral_v && std::is_integral_v, "nameof::detail::cmp_less requires integral type."); - - if constexpr (std::is_signed_v == std::is_signed_v) { - // If same signedness (both signed or both unsigned). - return lhs < rhs; - } else if constexpr (std::is_same_v) { // bool special case - return static_cast(lhs) < rhs; - } else if constexpr (std::is_same_v) { // bool special case - return lhs < static_cast(rhs); - } else if constexpr (std::is_signed_v) { - // If 'right' is negative, then result is 'false', otherwise cast & compare. - return rhs > 0 && lhs < static_cast>(rhs); - } else { - // If 'left' is negative, then result is 'true', otherwise cast & compare. - return lhs < 0 || static_cast>(lhs) < rhs; - } -} - -template -constexpr I log2(I value) noexcept { - static_assert(std::is_integral_v, "nameof::detail::log2 requires integral type."); - - if constexpr (std::is_same_v) { // bool special case - return assert(false), value; - } else { - auto ret = I{0}; - for (; value > I{1}; value >>= I{1}, ++ret) {} - - return ret; - } -} - -template -struct nameof_enum_supported -#if defined(NAMEOF_ENUM_SUPPORTED) && NAMEOF_ENUM_SUPPORTED || defined(NAMEOF_ENUM_NO_CHECK_SUPPORT) - : std::true_type {}; -#else - : std::false_type {}; -#endif - -template -using enable_if_enum_t = std::enable_if_t>, R>; - -template -inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; - -template -constexpr auto n() noexcept { - static_assert(is_enum_v, "nameof::detail::n requires enum type."); - - if constexpr (nameof_enum_supported::value) { -#if defined(__clang__) || defined(__GNUC__) - constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); -#elif defined(_MSC_VER) - constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); -#else - constexpr auto name = string_view{}; -#endif - return name; - } else { - return string_view{}; - } -} - -template -constexpr auto enum_name() noexcept { - [[maybe_unused]] constexpr auto custom_name = customize::enum_name(V); - - if constexpr (custom_name.empty()) { - constexpr auto name = n(); - return cstring{name}; - } else { - return cstring{custom_name}; - } -} - -template -inline constexpr auto enum_name_v = enum_name(); - -template -constexpr bool is_valid() noexcept { -#if defined(__clang__) && __clang_major__ >= 16 - // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 - constexpr E v = __builtin_bit_cast(E, V); -#else - constexpr E v = static_cast(V); -#endif - [[maybe_unused]] constexpr auto custom_name = customize::enum_name(v); - if constexpr (custom_name.empty()) { - return n().size() != 0; - } else { - return custom_name.size() != 0; - } -} - -template > -constexpr U ualue(std::size_t i) noexcept { - if constexpr (std::is_same_v) { // bool special case - static_assert(O == 0, "nameof::detail::ualue requires valid offset."); - - return static_cast(i); - } else if constexpr (IsFlags) { - return static_cast(U{1} << static_cast(static_cast(i) + O)); - } else { - return static_cast(static_cast(i) + O); - } -} - -template > -constexpr E value(std::size_t i) noexcept { - return static_cast(ualue(i)); -} - -template > -constexpr int reflected_min() noexcept { - if constexpr (IsFlags) { - return 0; - } else { - constexpr auto lhs = customize::enum_range::min; - constexpr auto rhs = (std::numeric_limits::min)(); - - if constexpr (cmp_less(rhs, lhs)) { - return lhs; - } else { - return rhs; - } - } -} - -template > -constexpr int reflected_max() noexcept { - if constexpr (IsFlags) { - return std::numeric_limits::digits - 1; - } else { - constexpr auto lhs = customize::enum_range::max; - constexpr auto rhs = (std::numeric_limits::max)(); - - if constexpr (cmp_less(lhs, rhs)) { - return lhs; - } else { - return rhs; - } - } -} - -#define NAMEOF_FOR_EACH_256(T) \ - T( 0)T( 1)T( 2)T( 3)T( 4)T( 5)T( 6)T( 7)T( 8)T( 9)T( 10)T( 11)T( 12)T( 13)T( 14)T( 15)T( 16)T( 17)T( 18)T( 19)T( 20)T( 21)T( 22)T( 23)T( 24)T( 25)T( 26)T( 27)T( 28)T( 29)T( 30)T( 31) \ - T( 32)T( 33)T( 34)T( 35)T( 36)T( 37)T( 38)T( 39)T( 40)T( 41)T( 42)T( 43)T( 44)T( 45)T( 46)T( 47)T( 48)T( 49)T( 50)T( 51)T( 52)T( 53)T( 54)T( 55)T( 56)T( 57)T( 58)T( 59)T( 60)T( 61)T( 62)T( 63) \ - T( 64)T( 65)T( 66)T( 67)T( 68)T( 69)T( 70)T( 71)T( 72)T( 73)T( 74)T( 75)T( 76)T( 77)T( 78)T( 79)T( 80)T( 81)T( 82)T( 83)T( 84)T( 85)T( 86)T( 87)T( 88)T( 89)T( 90)T( 91)T( 92)T( 93)T( 94)T( 95) \ - T( 96)T( 97)T( 98)T( 99)T(100)T(101)T(102)T(103)T(104)T(105)T(106)T(107)T(108)T(109)T(110)T(111)T(112)T(113)T(114)T(115)T(116)T(117)T(118)T(119)T(120)T(121)T(122)T(123)T(124)T(125)T(126)T(127) \ - T(128)T(129)T(130)T(131)T(132)T(133)T(134)T(135)T(136)T(137)T(138)T(139)T(140)T(141)T(142)T(143)T(144)T(145)T(146)T(147)T(148)T(149)T(150)T(151)T(152)T(153)T(154)T(155)T(156)T(157)T(158)T(159) \ - T(160)T(161)T(162)T(163)T(164)T(165)T(166)T(167)T(168)T(169)T(170)T(171)T(172)T(173)T(174)T(175)T(176)T(177)T(178)T(179)T(180)T(181)T(182)T(183)T(184)T(185)T(186)T(187)T(188)T(189)T(190)T(191) \ - T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \ - T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255) - -template -constexpr void valid_count(bool* valid, std::size_t& count) noexcept { -#define NAMEOF_ENUM_V(O) \ - if constexpr ((I + O) < Size) { \ - if constexpr (is_valid(I + O)>()) { \ - valid[I + O] = true; \ - ++count; \ - } \ - } - - NAMEOF_FOR_EACH_256(NAMEOF_ENUM_V) - - if constexpr ((I + 256) < Size) { - valid_count(valid, count); - } -#undef NAMEOF_ENUM_V -} - -template -struct valid_count_t { - std::size_t count = 0; - bool valid[N] = {}; -}; - -template -constexpr auto valid_count() noexcept { - valid_count_t vc; - valid_count(vc.valid, vc.count); - return vc; -} - -template -constexpr auto values() noexcept { - constexpr auto vc = valid_count(); - - if constexpr (vc.count > 0) { -#if defined(NAMEOF_ARRAY_CONSTEXPR) - std::array values = {}; -#else - E values[vc.count] = {}; -#endif - for (std::size_t i = 0, v = 0; v < vc.count; ++i) { - if (vc.valid[i]) { - values[v++] = value(i); - } - } -#if defined(NAMEOF_ARRAY_CONSTEXPR) - return values; -#else - return to_array(values, std::make_index_sequence{}); -#endif - } else { - return std::array{}; - } -} - -template > -constexpr auto values() noexcept { - constexpr auto min = reflected_min(); - constexpr auto max = reflected_max(); - constexpr auto range_size = max - min + 1; - static_assert(range_size > 0, "nameof::enum_range requires valid size."); - static_assert(range_size < (std::numeric_limits::max)(), "nameof::enum_range requires valid size."); - - return values(); -} - -template -inline constexpr auto values_v = values(); - -template -inline constexpr auto count_v = values_v.size(); - -template > -inline constexpr auto min_v = (count_v > 0) ? static_cast(values_v.front()) : U{0}; - -template > -inline constexpr auto max_v = (count_v > 0) ? static_cast(values_v.back()) : U{0}; - -template -constexpr auto names(std::index_sequence) noexcept { - constexpr auto names = std::array{{enum_name_v[I]>...}}; - return names; -} - -template -inline constexpr auto names_v = names(std::make_index_sequence>{}); - -template > -constexpr bool is_sparse() noexcept { - if constexpr (count_v == 0) { - return false; - } else if constexpr (std::is_same_v) { // bool special case - return false; - } else { - constexpr auto max = IsFlags ? log2(max_v) : max_v; - constexpr auto min = IsFlags ? log2(min_v) : min_v; - constexpr auto range_size = max - min + 1; - - return range_size != count_v; - } -} - -template -inline constexpr bool is_sparse_v = is_sparse(); - -template > -constexpr E enum_value(std::size_t i) noexcept { - if constexpr (is_sparse_v) { - return values_v[i]; - } else { - constexpr auto min = IsFlags ? log2(min_v) : min_v; - - return value(i); - } -} - -template -struct nameof_type_supported -#if defined(NAMEOF_TYPE_SUPPORTED) && NAMEOF_TYPE_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) - : std::true_type {}; -#else - : std::false_type {}; -#endif - -template -struct nameof_type_rtti_supported -#if defined(NAMEOF_TYPE_RTTI_SUPPORTED) && NAMEOF_TYPE_RTTI_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) - : std::true_type {}; -#else - : std::false_type {}; -#endif - -template -struct nameof_member_supported -#if defined(NAMEOF_MEMBER_SUPPORTED) && NAMEOF_MEMBER_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) - : std::true_type {}; -#else - : std::false_type {}; -#endif - -template -struct nameof_pointer_supported -#if defined(NAMEOF_POINTER_SUPPORTED) && NAMEOF_POINTER_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) - : std::true_type {}; -#else - : std::false_type {}; -#endif - -#if defined(_MSC_VER) && !defined(__clang__) -template -struct identity { - using type = T; -}; -#else -template -using identity = T; -#endif - -template -using remove_cvref_t = std::remove_cv_t>; - -template -using enable_if_has_short_name_t = std::enable_if_t && !std::is_pointer_v, R>; - -template -constexpr auto n() noexcept { -#if defined(_MSC_VER) && !defined(__clang__) - [[maybe_unused]] constexpr auto custom_name = customize::type_name(); -#else - [[maybe_unused]] constexpr auto custom_name = customize::type_name(); -#endif - - if constexpr (custom_name.empty() && nameof_type_supported::value) { -#if defined(__clang__) - constexpr string_view name{__PRETTY_FUNCTION__ + 31, sizeof(__PRETTY_FUNCTION__) - 34}; -#elif defined(__GNUC__) - constexpr string_view name{__PRETTY_FUNCTION__ + 46, sizeof(__PRETTY_FUNCTION__) - 49}; -#elif defined(_MSC_VER) - constexpr string_view name{__FUNCSIG__ + 63, sizeof(__FUNCSIG__) - 81 - (__FUNCSIG__[sizeof(__FUNCSIG__) - 19] == ' ' ? 1 : 0)}; -#else - constexpr auto name = string_view{}; -#endif - return cstring{name}; - } else { - return cstring{custom_name}; - } -} - -template -inline constexpr auto type_name_v = n(); - -#if __has_include() -template -string nameof_type_rtti(const char* tn) { - static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); - const auto name = string{dmg}; - free(dmg); - assert(!name.empty() && "Type does not have a name."); - - return name; -} - -template -string nameof_full_type_rtti(const char* tn) { - static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); - auto name = string{dmg}; - free(dmg); - assert(!name.empty() && "Type does not have a name."); - if constexpr (std::is_const_v>) { - name = string{"const "}.append(name); - } - if constexpr (std::is_volatile_v>) { - name = string{"volatile "}.append(name); - } - if constexpr (std::is_lvalue_reference_v) { - name.append(1, '&'); - } - if constexpr (std::is_rvalue_reference_v) { - name.append("&&"); - } - - return name; -} - -template = 0> -string nameof_short_type_rtti(const char* tn) { - static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); - const auto pname = pretty_name(dmg); - const auto name = string{pname.data(), pname.size()}; - free(dmg); - assert(!name.empty() && "Type does not have a short name."); - - return name; -} -#else -template -string nameof_type_rtti(const char* tn) { - static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - const auto name = string_view{tn}; - assert(!name.empty() && "Type does not have a name."); - return {name.data(), name.size()}; -} - -template -string nameof_full_type_rtti(const char* tn) { - static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - auto name = string{tn}; - assert(!name.empty() && "Type does not have a name."); - if constexpr (std::is_const_v>) { - name = string{"const "}.append(name); - } - if constexpr (std::is_volatile_v>) { - name = string{"volatile "}.append(name); - } - if constexpr (std::is_lvalue_reference_v) { - name.append(1, '&'); - } - if constexpr (std::is_rvalue_reference_v) { - name.append("&&"); - } - return name; -} - -template = 0> -string nameof_short_type_rtti(const char* tn) { - static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - const auto name = pretty_name(tn); - assert(!name.empty() && "Type does not have a short name."); - return {name.data(), name.size()}; -} -#endif - -template -constexpr auto n() noexcept { - [[maybe_unused]] constexpr auto custom_name = customize::member_name(); - - if constexpr (custom_name.empty() && nameof_member_supported::value) { -#if defined(__clang__) || defined(__GNUC__) - constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); -#elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L - constexpr auto name = pretty_name({__FUNCSIG__, - sizeof(__FUNCSIG__) - 18 + std::is_member_function_pointer_v}); -#else - constexpr auto name = string_view{}; -#endif - return cstring{name}; - } else { - return cstring{custom_name}; - } -} - -#if defined(__clang__) || defined(__GNUC__) -template -inline constexpr auto member_name_v = n(); -#elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L -template -From get_base_type(Type From::*); - -template -extern T nonexist_object; - -template -struct Store { - T v; -}; - -template -Store(T) -> Store; - -template -consteval auto get_member_name() noexcept { - if constexpr (std::is_member_function_pointer_v) { - return n(); - } else { - constexpr bool is_defined = sizeof(decltype(get_base_type(V))) != 0; - static_assert(is_defined, "nameof::nameof_member member name can use only if the struct is already fully defined. Please use NAMEOF macro, or separate definition and declaration."); - if constexpr (is_defined) { - return n.*V)}>(); - } else { - return ""; - } - } -} - -template -inline constexpr auto member_name_v = get_member_name(); -#else -template -inline constexpr auto member_name_v = cstring<0>{}; -#endif - -template -struct is_same : std::false_type {}; - -template -struct is_same : std::true_type {}; - -template -constexpr bool is_nullptr_v = is_same>(nullptr)>::value; - -template -constexpr auto p() noexcept { - [[maybe_unused]] constexpr auto custom_name = customize::pointer_name().empty() && is_nullptr_v ? "nullptr" : customize::pointer_name(); - - if constexpr (custom_name.empty() && nameof_pointer_supported::value) { -#if defined(__clang__) - constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); -#elif defined(__GNUC__) - constexpr bool has_parenthesis = __PRETTY_FUNCTION__[sizeof(__PRETTY_FUNCTION__) - 3] == ')'; - constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2 - has_parenthesis}); -#elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L - constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); -#else - constexpr auto name = string_view{}; -#endif - return cstring{name}; - } else { - return cstring{custom_name}; - } -} - -template -inline constexpr auto pointer_name_v = p(); - -} // namespace nameof::detail - -// Checks is nameof_type supported compiler. -inline constexpr bool is_nameof_type_supported = detail::nameof_type_supported::value; - -// Checks is nameof_type_rtti supported compiler. -inline constexpr bool is_nameof_type_rtti_supported = detail::nameof_type_rtti_supported::value; - -// Checks is nameof_member supported compiler. -inline constexpr bool is_nameof_member_supported = detail::nameof_member_supported::value; - -// Checks is nameof_pointer supported compiler. -inline constexpr bool is_nameof_pointer_supported = detail::nameof_pointer_supported::value; - -// Checks is nameof_enum supported compiler. -inline constexpr bool is_nameof_enum_supported = detail::nameof_enum_supported::value; - -// Obtains name of enum variable. -template -[[nodiscard]] constexpr auto nameof_enum(E value) noexcept -> detail::enable_if_enum_t { - using D = std::decay_t; - using U = std::underlying_type_t; - static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - static_assert(detail::count_v > 0, "nameof::nameof_enum requires enum implementation and valid max and min."); - - if constexpr (detail::is_sparse_v) { - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (detail::enum_value(i) == value) { - return detail::names_v[i]; - } - } - } else { - const auto v = static_cast(value); - if (v >= detail::min_v && v <= detail::max_v) { - return detail::names_v[static_cast(v - detail::min_v)]; - } - } - return {}; // Value out of range. -} - -// Obtains name of enum variable or default value if enum variable out of range. -template -[[nodiscard]] auto nameof_enum_or(E value, string_view default_value) -> detail::enable_if_enum_t { - using D = std::decay_t; - static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum_or unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - static_assert(detail::count_v > 0, "nameof::nameof_enum_or requires enum implementation and valid max and min."); - - if (auto v = nameof_enum(value); !v.empty()) { - return string{v.data(), v.size()}; - } - return string{default_value.data(), default_value.size()}; -} - -// Obtains name of enum-flags variable. -template -[[nodiscard]] auto nameof_enum_flag(E value, char sep = '|') -> detail::enable_if_enum_t { - using D = std::decay_t; - using U = std::underlying_type_t; - static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum_flag unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - static_assert(detail::count_v > 0, "nameof::nameof_enum_flag requires enum-flags implementation."); - - string name; - auto check_value = U{0}; - for (std::size_t i = 0; i < detail::count_v; ++i) { - if (const auto v = static_cast(detail::enum_value(i)); (static_cast(value) & v) != 0) { - if (const auto n = detail::names_v[i]; !n.empty()) { - check_value |= v; - if (!name.empty()) { - name.append(1, sep); - } - name.append(n.data(), n.size()); - } else { - return {}; // Value out of range. - } - } - } - - if (check_value != 0 && check_value == static_cast(value)) { - return name; - } - return {}; // Invalid value or out of range. -} - -// Obtains name of static storage enum variable. -// This version is much lighter on the compile times and is not restricted to the enum_range limitation. -template = 0> -[[nodiscard]] constexpr auto nameof_enum() noexcept { - using D = std::decay_t; - static_assert(std::is_enum_v, "nameof::nameof_enum requires member enum type."); - static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - return detail::enum_name_v; -} - -// Obtains name of type, reference and cv-qualifiers are ignored. -template -[[nodiscard]] constexpr string_view nameof_type() noexcept { - using U = detail::identity>; - static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - return detail::type_name_v; -} - -// Obtains full name of type, with reference and cv-qualifiers. -template -[[nodiscard]] constexpr string_view nameof_full_type() noexcept { - using U = detail::identity; - static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - return detail::type_name_v; -} - -// Obtains short name of type. -template = 0> -[[nodiscard]] constexpr auto nameof_short_type() noexcept { - using U = detail::identity>; - static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - static_assert(!std::is_array_v && !std::is_pointer_v, "nameof::nameof_member requires non array and non pointer type."); - constexpr string_view name = detail::pretty_name(detail::type_name_v); - static_assert(!name.empty(), "Type does not have a short name."); - return cstring{name}; -} - -// Obtains name of member. -template , int> = 0> -[[nodiscard]] constexpr auto nameof_member() noexcept { - using U = decltype(V); - static_assert(std::is_member_pointer_v, "nameof::nameof_member requires member pointer type."); - static_assert(detail::nameof_member_supported::value, "nameof::nameof_member unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - return detail::member_name_v; -} - -// Obtains name of a function, a global or class static variable. -template , int> = 0> -[[nodiscard]] constexpr auto nameof_pointer() noexcept { - using U = decltype(V); - static_assert(std::is_pointer_v, "nameof::nameof_pointer requires pointer type."); - static_assert(detail::nameof_pointer_supported::value, "nameof::nameof_pointer unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - return detail::pointer_name_v; -} - -} // namespace nameof - -// Obtains name of variable, function, macro. -#define NAMEOF(...) []() constexpr noexcept { \ - ::std::void_t(); \ - constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \ - static_assert(!_name.empty(), "Expression does not have a name."); \ - constexpr auto _size = _name.size(); \ - constexpr auto _nameof = ::nameof::cstring<_size>{_name}; \ - return _nameof; }() - -// Obtains full name of variable, function, macro. -#define NAMEOF_FULL(...) []() constexpr noexcept { \ - ::std::void_t(); \ - constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__, false); \ - static_assert(!_name.empty(), "Expression does not have a name."); \ - constexpr auto _size = _name.size(); \ - constexpr auto _nameof_full = ::nameof::cstring<_size>{_name}; \ - return _nameof_full; }() - -// Obtains raw name of variable, function, macro. -#define NAMEOF_RAW(...) []() constexpr noexcept { \ - ::std::void_t(); \ - constexpr auto _name = ::nameof::string_view{#__VA_ARGS__}; \ - static_assert(!_name.empty(), "Expression does not have a name."); \ - constexpr auto _size = _name.size(); \ - constexpr auto _nameof_raw = ::nameof::cstring<_size>{_name}; \ - return _nameof_raw; }() - -// Obtains name of enum variable. -#define NAMEOF_ENUM(...) ::nameof::nameof_enum<::std::decay_t>(__VA_ARGS__) - -// Obtains name of enum variable or default value if enum variable out of range. -#define NAMEOF_ENUM_OR(...) ::nameof::nameof_enum_or(__VA_ARGS__) - -// Obtains name of static storage enum variable. -// This version is much lighter on the compile times and is not restricted to the enum_range limitation. -#define NAMEOF_ENUM_CONST(...) ::nameof::nameof_enum<__VA_ARGS__>() - -// Obtains name of enum-flags variable. -#define NAMEOF_ENUM_FLAG(...) ::nameof::nameof_enum_flag<::std::decay_t>(__VA_ARGS__) - -// Obtains type name, reference and cv-qualifiers are ignored. -#define NAMEOF_TYPE(...) ::nameof::nameof_type<__VA_ARGS__>() - -// Obtains full type name, with reference and cv-qualifiers. -#define NAMEOF_FULL_TYPE(...) ::nameof::nameof_full_type<__VA_ARGS__>() - -// Obtains short type name. -#define NAMEOF_SHORT_TYPE(...) ::nameof::nameof_short_type<__VA_ARGS__>() - -// Obtains type name of expression, reference and cv-qualifiers are ignored. -#define NAMEOF_TYPE_EXPR(...) ::nameof::nameof_type() - -// Obtains full type name of expression, with reference and cv-qualifiers. -#define NAMEOF_FULL_TYPE_EXPR(...) ::nameof::nameof_full_type() - -// Obtains short type name of expression. -#define NAMEOF_SHORT_TYPE_EXPR(...) ::nameof::nameof_short_type() - -// Obtains type name, with reference and cv-qualifiers, using RTTI. -#define NAMEOF_TYPE_RTTI(...) ::nameof::detail::nameof_type_rtti<::std::void_t>(typeid(__VA_ARGS__).name()) - -// Obtains full type name, using RTTI. -#define NAMEOF_FULL_TYPE_RTTI(...) ::nameof::detail::nameof_full_type_rtti(typeid(__VA_ARGS__).name()) - -// Obtains short type name, using RTTI. -#define NAMEOF_SHORT_TYPE_RTTI(...) ::nameof::detail::nameof_short_type_rtti(typeid(__VA_ARGS__).name()) - -// Obtains name of member. -#define NAMEOF_MEMBER(...) ::nameof::nameof_member<__VA_ARGS__>() - -// Obtains name of a function, a global or class static variable. -#define NAMEOF_POINTER(...) ::nameof::nameof_pointer<__VA_ARGS__>() - -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) -# pragma GCC diagnostic pop -#elif defined(_MSC_VER) -# pragma warning(pop) -#endif - -#endif // NEARGYE_NAMEOF_HPP diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 87b8c67a..362afea0 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -101,10 +101,10 @@ auto unpack_messages(const mpi_packed_message& packed_message) } template -concept mpi_nested_range = requires { - std::ranges::forward_range; - std::ranges::forward_range>; - mpi_datatype>>; +concept mpi_nested_range = requires(Input) { + requires std::ranges::forward_range; + requires std::ranges::forward_range>; + requires mpi_datatype>>; }; template diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index ef17a067..bc95bfb1 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -159,16 +159,22 @@ MPI_DATATYPE_TRAIT(std::complex, MPI_CXX_DOUBLE_COMPLEX) // Concept to check if a type is a native MPI datatype template -concept mpi_native_datatype = (details::mpi_data_kind_trait::kind == details::mpi_data_kinds::base); +concept mpi_native_datatype = requires(DataType) { + requires details::mpi_data_kind_trait::kind == details::mpi_data_kinds::base; +}; template -concept mpi_composite_datatype = - (details::mpi_data_kind_trait::kind == details::mpi_data_kinds::composite); +concept mpi_composite_datatype = requires(DataType) { + requires details::mpi_data_kind_trait::kind == details::mpi_data_kinds::composite; +}; + // mpi_datatype concept combines native and composite datatypes template -concept mpi_datatype = - mpi_native_datatype || mpi_composite_datatype; +concept mpi_datatype = requires(DataType) { + requires mpi_native_datatype || mpi_composite_datatype; +}; + template auto get_mpi_datatype() -> MPI_Datatype { From 005ebc800a1f942597906837350b6e20f8cc0657 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 3 Oct 2024 16:02:10 +0100 Subject: [PATCH 41/64] Fixed handle leak --- .../parallel_contraction.h | 60 ------------------- 1 file changed, 60 deletions(-) diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h index b2ec542c..9499cfd0 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h @@ -65,64 +65,4 @@ struct bundled_node_weight { NodeWeight weight; }; } // namespace contraction - -// Specialize mpi_data_kind_trait for bundled_edge -namespace mpi::details { -template <> -struct mpi_data_kind_trait { - static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; -}; - -template <> -struct mpi_datatype_trait { - static MPI_Datatype get_mpi_type() { - static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; - if (mpi_type == MPI_DATATYPE_NULL) { - int block_lengths[3] = {1, 1, 1}; - MPI_Datatype types[3] = { - get_mpi_datatype(), - get_mpi_datatype(), - get_mpi_datatype()}; - MPI_Aint offsets[3]; - - offsets[0] = offsetof(contraction::bundled_edge, source); - offsets[1] = offsetof(contraction::bundled_edge, target); - offsets[2] = offsetof(contraction::bundled_edge, weight); - - MPI_Type_create_struct(3, block_lengths, offsets, types, &mpi_type); - MPI_Type_commit(&mpi_type); - } - return mpi_type; - } -}; -} // namespace mpi::details - -// Specialize mpi_data_kind_trait for node_weight -namespace mpi::details { -template <> -struct mpi_data_kind_trait { - static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; -}; - -template <> -struct mpi_datatype_trait { - static MPI_Datatype get_mpi_type() { - static MPI_Datatype mpi_type = MPI_DATATYPE_NULL; - if (mpi_type == MPI_DATATYPE_NULL) { - int block_lengths[2] = { 1, 1}; - MPI_Datatype types[2] = { - get_mpi_datatype(), - get_mpi_datatype()}; - MPI_Aint offsets[2]; - - offsets[0] = offsetof(contraction::bundled_node_weight, node); - offsets[1] = offsetof(contraction::bundled_node_weight, weight); - - MPI_Type_create_struct(2, block_lengths, offsets, types, &mpi_type); - MPI_Type_commit(&mpi_type); - } - return mpi_type; - } -}; -} // namespace mpi::details #endif /* end of include guard: PARALLEL_CONTRACTION_64O127GD */ From 75ca671eab9a98ed2fd23c808567d7865abc55d4 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 4 Oct 2024 12:00:08 +0100 Subject: [PATCH 42/64] Add Dev container for test --- .devcontainer/Dockerfile | 18 ++++++ .devcontainer/devcontainer.json | 27 +++++++++ .devcontainer/reinstall-cmake.sh | 59 +++++++++++++++++++ .../lib/communication/mpi_types.h | 2 +- .../parallel_contraction.cpp | 2 +- .../parallel_contraction_mpi_test.cpp | 2 +- 6 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/reinstall-cmake.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..4f05b806 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/devcontainers/cpp:1-ubuntu-24.04 + +ARG REINSTALL_CMAKE_VERSION_FROM_SOURCE="none" + +# Optionally install the cmake for vcpkg +COPY ./reinstall-cmake.sh /tmp/ + +RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \ + chmod +x /tmp/reinstall-cmake.sh && /tmp/reinstall-cmake.sh ${REINSTALL_CMAKE_VERSION_FROM_SOURCE}; \ + fi \ + && rm -f /tmp/reinstall-cmake.sh + +# [Optional] Uncomment this section to install additional vcpkg ports. +# RUN su vscode -c "${VCPKG_ROOT}/vcpkg install " + +# [Optional] Uncomment this section to install additional packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install openmpi-bin libopenmpi-dev catch2 gfortran libfmt-dev diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..e04d2a74 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,27 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/cpp +{ + "name": "C++", + "build": { + "dockerfile": "Dockerfile" + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "gcc -v", + + // Configure tool-specific properties. + "customizations" : { + "jetbrains" : { + "backend" : "CLion" + } + }, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.devcontainer/reinstall-cmake.sh b/.devcontainer/reinstall-cmake.sh new file mode 100644 index 00000000..408b81d2 --- /dev/null +++ b/.devcontainer/reinstall-cmake.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +set -e + +CMAKE_VERSION=${1:-"none"} + +if [ "${CMAKE_VERSION}" = "none" ]; then + echo "No CMake version specified, skipping CMake reinstallation" + exit 0 +fi + +# Cleanup temporary directory and associated files when exiting the script. +cleanup() { + EXIT_CODE=$? + set +e + if [[ -n "${TMP_DIR}" ]]; then + echo "Executing cleanup of tmp files" + rm -Rf "${TMP_DIR}" + fi + exit $EXIT_CODE +} +trap cleanup EXIT + + +echo "Installing CMake..." +apt-get -y purge --auto-remove cmake +mkdir -p /opt/cmake + +architecture=$(dpkg --print-architecture) +case "${architecture}" in + arm64) + ARCH=aarch64 ;; + amd64) + ARCH=x86_64 ;; + *) + echo "Unsupported architecture ${architecture}." + exit 1 + ;; +esac + +CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh" +CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt" +TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX) + +echo "${TMP_DIR}" +cd "${TMP_DIR}" + +curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O +curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O + +sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}" +sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license + +ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake +ln -s /opt/cmake/bin/ctest /usr/local/bin/ctest diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index bc95bfb1..c4fd36a7 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -69,7 +69,7 @@ inline void initialize_mpi_type_cleanup() { // Use a static function-local variable to ensure initialization occurs only // once static bool initialized = []() { - int mpi_error = MPI_Comm_create_keyval(MPI_NULL_COPY_FN, mpi_type_cleanup, &mpi_type_cleanup_keyval, nullptr); + int mpi_error = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, mpi_type_cleanup, &mpi_type_cleanup_keyval, nullptr); if (mpi_error != MPI_SUCCESS) { throw std::runtime_error("MPI_Comm_create_keyval() failed"); } diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 6d246b49..0bdde805 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -299,7 +299,7 @@ void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( for (PEID peID = 0; peID < size; peID++) { if (!local_msg_byPE[peID].empty()) { for (auto packed_edge : local_msg_byPE[peID]) { - hashed_edge he; + hashed_edge he{}; he.k = number_of_cnodes; he.source = packed_edge.source; he.target = packed_edge.target; diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index 711e1ac2..79ac7d6f 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -52,7 +52,7 @@ TEST_CASE("all to all vector of vectors", "[unit][mpi]") { {}, {1}, {2, 2}, {3, 3, 3}, {4, 4, 4, 4}}; auto vec = mpi::all_to_all(v_empty, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); - fmt::println("rank: {} -> {}", rank, vec); + fmt::print("rank: {} -> {}\n", rank, vec); REQUIRE(v_empty.size() == vec.size()); } From 622df104df52c36e293a9659790f24c1fc977bdb Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 4 Oct 2024 17:31:38 +0100 Subject: [PATCH 43/64] Fixing ODR issues --- .devcontainer/Dockerfile | 4 +- CMakeLists.txt | 225 +- cmake/Cache.cmake | 33 + cmake/CompilerWarnings.cmake | 116 + cmake/InterproceduralOptimization.cmake | 9 + cmake/KaHIPSettings.cmake | 57 + cmake/Sanitizers.cmake | 104 + cmake/StaticAnalyzers.cmake | 112 + cmake/Utilities.cmake | 139 + extern/argtable3-3.2.2/CMakeLists.txt | 2 + lib/{ => version}/version.h | 0 parallel/parallel_src/CMakeLists.txt | 17 +- .../extern/argtable3-3.2.2/LICENSE | 167 - .../extern/argtable3-3.2.2/README.md | 399 -- .../extern/argtable3-3.2.2/argtable3.c | 6021 ----------------- .../extern/argtable3-3.2.2/argtable3.h | 273 - .../argtable3-3.2.2/examples/CMakeLists.txt | 54 - .../extern/argtable3-3.2.2/examples/echo.c | 128 - .../extern/argtable3-3.2.2/examples/ls.c | 326 - .../argtable3-3.2.2/examples/multisyntax.c | 239 - .../extern/argtable3-3.2.2/examples/mv.c | 179 - .../extern/argtable3-3.2.2/examples/myprog.c | 147 - .../argtable3-3.2.2/examples/myprog_C89.c | 165 - .../argtable3-3.2.2/examples/testargtable3.c | 58 - .../extern/argtable3-3.2.2/examples/uname.c | 137 - .../argtable3-3.2.2/tests/CMakeLists.txt | 104 - .../extern/argtable3-3.2.2/tests/CuTest.c | 326 - .../extern/argtable3-3.2.2/tests/CuTest.h | 104 - .../argtable3-3.2.2/tests/argtable3_private.h | 240 - .../extern/argtable3-3.2.2/tests/testall.c | 80 - .../extern/argtable3-3.2.2/tests/testargcmd.c | 93 - .../argtable3-3.2.2/tests/testargdate.c | 377 -- .../extern/argtable3-3.2.2/tests/testargdbl.c | 451 -- .../argtable3-3.2.2/tests/testargdstr.c | 123 - .../argtable3-3.2.2/tests/testargfile.c | 809 --- .../argtable3-3.2.2/tests/testarghashtable.c | 276 - .../extern/argtable3-3.2.2/tests/testargint.c | 2018 ------ .../extern/argtable3-3.2.2/tests/testarglit.c | 538 -- .../extern/argtable3-3.2.2/tests/testargrex.c | 299 - .../extern/argtable3-3.2.2/tests/testargstr.c | 531 -- parallel/parallel_src/tests/CMakeLists.txt | 5 +- 41 files changed, 711 insertions(+), 14774 deletions(-) create mode 100644 cmake/Cache.cmake create mode 100644 cmake/CompilerWarnings.cmake create mode 100644 cmake/InterproceduralOptimization.cmake create mode 100644 cmake/KaHIPSettings.cmake create mode 100644 cmake/Sanitizers.cmake create mode 100644 cmake/StaticAnalyzers.cmake create mode 100644 cmake/Utilities.cmake create mode 100644 extern/argtable3-3.2.2/CMakeLists.txt rename lib/{ => version}/version.h (100%) delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/LICENSE delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/README.md delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/argtable3.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/argtable3.h delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/examples/CMakeLists.txt delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/examples/echo.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/examples/ls.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/examples/multisyntax.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/examples/mv.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/examples/myprog.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/examples/myprog_C89.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/examples/testargtable3.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/examples/uname.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/CMakeLists.txt delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/CuTest.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/CuTest.h delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/argtable3_private.h delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testall.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testargcmd.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdate.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdbl.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdstr.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testargfile.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testarghashtable.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testargint.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testarglit.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testargrex.c delete mode 100644 parallel/parallel_src/extern/argtable3-3.2.2/tests/testargstr.c diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4f05b806..2fb5aec6 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -11,8 +11,8 @@ RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \ && rm -f /tmp/reinstall-cmake.sh # [Optional] Uncomment this section to install additional vcpkg ports. -# RUN su vscode -c "${VCPKG_ROOT}/vcpkg install " +RUN su vscode -c "export VCPKG_FORCE_SYSTEM_BINARIES=arm && ${VCPKG_ROOT}/vcpkg install catch2 fmt metis" # [Optional] Uncomment this section to install additional packages. RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install openmpi-bin libopenmpi-dev catch2 gfortran libfmt-dev + && apt-get -y install gfortran openmpi-bin libopenmpi-dev ninja-build clang-tidy cppcheck iwyu diff --git a/CMakeLists.txt b/CMakeLists.txt index 006d6dca..61fd0177 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,11 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.25..28) include(CheckCXXCompilerFlag) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -find_program(CCACHE_PROGRAM ccache) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +project(KaHIP + LANGUAGES C CXX) -if(CCACHE_PROGRAM) - message(STATUS "Using compiler cache") - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}") -endif() -project(KaHIP C CXX) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +include(KaHIPSettings) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -18,17 +13,6 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -include(CheckIPOSupported) -check_ipo_supported(RESULT supported OUTPUT error) - -if( supported ) - message(STATUS "IPO / LTO enabled") - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) -else() - message(STATUS "IPO / LTO not supported: <${error}>") -endif() - # if no build mode is specified build in release mode if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "Release") @@ -38,36 +22,36 @@ endif() option(NONATIVEOPTIMIZATIONS "Disable --march=native optimizations" OFF) # tweak compiler flags -CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) -if(COMPILER_SUPPORTS_FUNROLL_LOOPS) - add_definitions(-funroll-loops) -endif() -CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) -if(COMPILER_SUPPORTS_FNOSTACKLIMITS) - add_definitions(-fno-stack-limit) -endif() -CHECK_CXX_COMPILER_FLAG(-Wall COMPILER_SUPPORTS_WALL) -if(COMPILER_SUPPORTS_WALL) - add_definitions(-Wall) -endif() -CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) -if(COMPILER_SUPPORTS_MARCH_NATIVE) - if( NOT NONATIVEOPTIMIZATIONS ) - add_definitions(-march=native) - endif() -endif() -CHECK_CXX_COMPILER_FLAG(-fpermissive COMPILER_SUPPORTS_FPERMISSIVE) -if(COMPILER_SUPPORTS_FPERMISSIVE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") -endif() -CHECK_CXX_COMPILER_FLAG(-Wno-unused-result COMPILER_SUPPORTS_UNUSED) -if(COMPILER_SUPPORTS_UNUSED) - add_definitions(-Wno-unused-result) -endif() -CHECK_CXX_COMPILER_FLAG(-Wno-sign-compare COMPILER_SUPPORTS_NOSIGNCOMP) -if(COMPILER_SUPPORTS_NOSIGNCOMP) - add_definitions(-Wno-sign-compare) -endif() +#CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) +#if(COMPILER_SUPPORTS_FUNROLL_LOOPS) +# add_definitions(-funroll-loops) +#endif() +#CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) +#if(COMPILER_SUPPORTS_FNOSTACKLIMITS) +# add_definitions(-fno-stack-limit) +#endif() +#CHECK_CXX_COMPILER_FLAG(-Wall COMPILER_SUPPORTS_WALL) +#if(COMPILER_SUPPORTS_WALL) +# add_definitions(-Wall) +#endif() +#CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) +#if(COMPILER_SUPPORTS_MARCH_NATIVE) +# if( NOT NONATIVEOPTIMIZATIONS ) +# add_definitions(-march=native) +# endif() +#endif() +#CHECK_CXX_COMPILER_FLAG(-fpermissive COMPILER_SUPPORTS_FPERMISSIVE) +#if(COMPILER_SUPPORTS_FPERMISSIVE) +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") +#endif() +#CHECK_CXX_COMPILER_FLAG(-Wno-unused-result COMPILER_SUPPORTS_UNUSED) +#if(COMPILER_SUPPORTS_UNUSED) +# add_definitions(-Wno-unused-result) +#endif() +#CHECK_CXX_COMPILER_FLAG(-Wno-sign-compare COMPILER_SUPPORTS_NOSIGNCOMP) +#if(COMPILER_SUPPORTS_NOSIGNCOMP) +# add_definitions(-Wno-sign-compare) +#endif() # Check dependencies @@ -80,13 +64,13 @@ else() set_property(TARGET OpenMP::OpenMP_CXX PROPERTY INTERFACE_COMPILE_OPTIONS "") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/misc) endif() -find_library(LIB_METIS metis) -if(LIB_METIS) +find_package(metis CONFIG) +if(metis_FOUND) message(STATUS "Metis support detected") - find_library(LIB_GK GKlib) - if (NOT LIB_GK) + find_library(GKlib CONFIG) + if (NOT GKlib_FOUND) message(STATUS "Metis requires GKlib, but GKlib was not found") - set(LIB_METIS "NOTFOUND") + set(metis_FOUND OFF) else() add_definitions("-DUSEMETIS") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") @@ -146,15 +130,21 @@ option(USE_TCMALLOC "if available, link against tcmalloc" OFF) # ILP improver option(USE_ILP "build local ILP improver - introduces dependency on Gurobi" OFF) +# Argtable3 +add_subdirectory(extern/argtable3-3.2.2) + +add_library(kahip_headers INTERFACE) +target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/app) +target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib) +target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io) +target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) +target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) +target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/app) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/extern/argtable3-3.2.2) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/io) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) +add_library(kahip_version INTERFACE) +target_include_directories(kahip_version INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/version) +target_link_libraries(kahip_headers INTERFACE kahip_version) set(LIBKAFFPA_SOURCE_FILES lib/data_structure/graph_hierarchy.cpp @@ -217,9 +207,9 @@ set(LIBKAFFPA_SOURCE_FILES lib/partition/uncoarsening/refinement/node_separators/localized_fm_ns_local_search.cpp lib/algorithms/cycle_search.cpp lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp - lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp - extern/argtable3-3.2.2/argtable3.c) + lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp) add_library(libkaffpa OBJECT ${LIBKAFFPA_SOURCE_FILES}) +target_link_libraries(libkaffpa PUBLIC kahip_headers argtable3) if(NOT NOMPI) set(LIBKAFFPA_PARALLEL_SOURCE_FILES @@ -231,7 +221,7 @@ if(NOT NOMPI) lib/tools/graph_communication.cpp lib/tools/mpi_tools.cpp) add_library(libkaffpa_parallel OBJECT ${LIBKAFFPA_PARALLEL_SOURCE_FILES}) - target_include_directories(libkaffpa_parallel PUBLIC ${MPI_CXX_INCLUDE_PATH}) + target_link_libraries(libkaffpa_parallel PRIVATE kahip_headers MPI::MPI_CXX) endif() set(LIBMAPPING_SOURCE_FILES @@ -244,9 +234,11 @@ set(LIBMAPPING_SOURCE_FILES lib/mapping/mapping_algorithms.cpp lib/mapping/construct_mapping.cpp) add_library(libmapping OBJECT ${LIBMAPPING_SOURCE_FILES}) +target_link_libraries(libmapping PRIVATE kahip_headers) set(LIBSPAC_SOURCE_FILES lib/spac/spac.cpp) add_library(libspac OBJECT ${LIBSPAC_SOURCE_FILES}) +target_link_libraries(libspac PRIVATE kahip_headers) set(NODE_ORDERING_SOURCE_FILES lib/node_ordering/min_degree_ordering.cpp @@ -254,11 +246,15 @@ set(NODE_ORDERING_SOURCE_FILES lib/node_ordering/ordering_tools.cpp lib/node_ordering/reductions.cpp) add_library(libnodeordering OBJECT ${NODE_ORDERING_SOURCE_FILES}) +target_link_libraries(libnodeordering PRIVATE kahip_headers) + # generate targets for each binary -add_executable(kaffpa app/kaffpa.cpp $ $) +add_executable(kaffpa app/kaffpa.cpp) +target_link_libraries(kaffpa PRIVATE libkaffpa libmapping) target_compile_definitions(kaffpa PRIVATE "-DMODE_KAFFPA") target_link_libraries(kaffpa PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(kaffpa PRIVATE kahip_headers) install(TARGETS kaffpa DESTINATION bin) if (USE_TCMALLOC) find_library(TCMALLOC_LIB tcmalloc) @@ -270,9 +266,11 @@ if (USE_TCMALLOC) endif () endif () -add_executable(global_multisection app/global_multisection.cpp $ $) +add_executable(global_multisection app/global_multisection.cpp) +target_link_libraries(global_multisection PRIVATE libkaffpa libmapping) target_compile_definitions(global_multisection PRIVATE "-DMODE_KAFFPA -DMODE_GLOBALMS") target_link_libraries(global_multisection PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(global_multisection PRIVATE kahip_headers) install(TARGETS global_multisection DESTINATION bin) if (USE_TCMALLOC) find_library(TCMALLOC_LIB tcmalloc) @@ -284,96 +282,115 @@ if (USE_TCMALLOC) endif () endif () -add_executable(evaluator app/evaluator.cpp $ $) +add_executable(evaluator app/evaluator.cpp) +target_link_libraries(evaluator PRIVATE libkaffpa libmapping) target_compile_definitions(evaluator PRIVATE "-DMODE_EVALUATOR") target_link_libraries(evaluator PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(evaluator PRIVATE kahip_headers) install(TARGETS evaluator DESTINATION bin) -add_executable(edge_evaluator app/edge_evaluator.cpp $ $) +add_executable(edge_evaluator app/edge_evaluator.cpp) +target_link_libraries(evaluator PRIVATE libkaffpa libmapping) target_compile_definitions(edge_evaluator PRIVATE "-DMODE_EVALUATOR") target_link_libraries(edge_evaluator PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(edge_evaluator PRIVATE kahip_headers) +target_link_libraries(edge_evaluator PRIVATE argtable3) install(TARGETS edge_evaluator DESTINATION bin) -add_executable(node_separator app/node_separator_ml.cpp $ $) +add_executable(node_separator app/node_separator_ml.cpp) +target_link_libraries(node_separator PRIVATE libkaffpa libmapping) target_compile_definitions(node_separator PRIVATE "-DMODE_NODESEP") target_link_libraries(node_separator PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(node_separator PRIVATE kahip_headers) install(TARGETS node_separator DESTINATION bin) -add_executable(label_propagation app/label_propagation.cpp $ $) +add_executable(label_propagation app/label_propagation.cpp) +target_link_libraries(label_propagation PRIVATE libkaffpa libmapping) target_compile_definitions(label_propagation PRIVATE "-DMODE_LABELPROPAGATION") target_link_libraries(label_propagation PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(label_propagation PRIVATE kahip_headers) install(TARGETS label_propagation DESTINATION bin) -add_executable(partition_to_vertex_separator app/partition_to_vertex_separator.cpp $ $) +add_executable(partition_to_vertex_separator app/partition_to_vertex_separator.cpp) +target_link_libraries(partition_to_vertex_separator PRIVATE libkaffpa libmapping) target_compile_definitions(partition_to_vertex_separator PRIVATE "-DMODE_PARTITIONTOVERTEXSEPARATOR") target_link_libraries(partition_to_vertex_separator PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(partition_to_vertex_separator PRIVATE kahip_headers) install(TARGETS partition_to_vertex_separator DESTINATION bin) -add_executable(interface_test misc/example_library_call/interface_test.cpp interface/kaHIP_interface.cpp $ $ $ $) +add_executable(interface_test misc/example_library_call/interface_test.cpp interface/kaHIP_interface.cpp) +target_link_libraries(interface_test PRIVATE libkaffpa libmapping libnodeordering libspac) target_include_directories(interface_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(interface_test PRIVATE "-DMODE_KAFFPA") target_link_libraries(interface_test PRIVATE OpenMP::OpenMP_CXX) -if(LIB_METIS) - target_link_libraries(interface_test ${LIB_METIS} ${LIB_GK}) +target_link_libraries(interface_test PRIVATE kahip_headers) +if(metis_FOUND) + target_link_libraries(interface_test PUBLIC metis GKlib) endif() install(TARGETS interface_test DESTINATION bin) if(NOT NOMPI) - add_executable(kaffpaE app/kaffpaE.cpp $ $ $) + add_executable(kaffpaE app/kaffpaE.cpp) + target_link_libraries(kaffpaE PRIVATE libkaffpa libmapping libkaffpa_parallel) target_compile_definitions(kaffpaE PRIVATE "-DMODE_KAFFPAE") - target_include_directories(kaffpaE PUBLIC ${MPI_CXX_INCLUDE_PATH}) - target_link_libraries(kaffpaE PRIVATE MPI::MPI_CXX OpenMP::OpenMP_CXX ) + target_link_libraries(kaffpaE PRIVATE MPI::MPI_CXX OpenMP::OpenMP_CXX) + target_link_libraries(kaffpaE PRIVATE kahip_headers) install(TARGETS kaffpaE DESTINATION bin) endif() -add_executable(graphchecker app/graphchecker.cpp $ $) +add_executable(graphchecker app/graphchecker.cpp) +target_link_libraries(graphchecker PRIVATE libkaffpa libmapping) target_compile_definitions(graphchecker PRIVATE "-DMODE_GRAPHCHECKER") target_link_libraries(graphchecker PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(graphchecker PRIVATE kahip_headers) install(TARGETS graphchecker DESTINATION bin) -add_executable(edge_partitioning app/spac.cpp $ $ $) +add_executable(edge_partitioning app/spac.cpp) +target_link_libraries(edge_partitioning PRIVATE libkaffpa libmapping libspac) target_compile_definitions(edge_partitioning PRIVATE "-DMODE_KAFFPA") -target_link_libraries(edge_partitioning ${OpenMP_CXX_LIBRARIES}) +target_link_libraries(edge_partitioning PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(edge_partitioning PRIVATE kahip_headers) install(TARGETS edge_partitioning DESTINATION bin) -add_executable(node_ordering app/node_ordering.cpp $ $) +add_executable(node_ordering app/node_ordering.cpp) +target_link_libraries(node_ordering PRIVATE libkaffpa libnodeordering) target_compile_definitions(node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING") target_link_libraries(node_ordering PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(node_ordering PRIVATE kahip_headers) install(TARGETS node_ordering DESTINATION bin) -if(LIB_METIS) - add_executable(fast_node_ordering app/fast_node_ordering.cpp $ $) +if(metis_FOUND) + add_executable(fast_node_ordering app/fast_node_ordering.cpp) + target_link_libraries(fast_node_ordering PRIVATE libkaffpa libmapping) target_compile_definitions(fast_node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING -DFASTORDERING") - target_link_libraries(fast_node_ordering PRIVATE OpenMP::OpenMP_CXX ${LIB_METIS} ${LIB_GK}) + target_link_libraries(fast_node_ordering PRIVATE OpenMP::OpenMP_CXX metis GKlib) + target_link_libraries(fast_node_ordering PRIVATE kahip_headers) install(TARGETS fast_node_ordering DESTINATION bin) endif() # Shared interface library -add_library(kahip SHARED interface/kaHIP_interface.cpp $ - $ - $ - $) +add_library(kahip SHARED interface/kaHIP_interface.cpp) +target_link_libraries(kahip PRIVATE libkaffpa libmapping libnodeordering libspac) target_include_directories(kahip PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(kahip PRIVATE "-DMODE_KAFFPA") target_link_libraries(kahip PRIVATE OpenMP::OpenMP_CXX) -if(LIB_METIS) - target_link_libraries(kahip PUBLIC ${LIB_METIS} ${LIB_GK}) +target_link_libraries(kahip PRIVATE kahip_headers) +if(metis_FOUND) + target_link_libraries(kahip PRIVATE metis GKlib) endif() install(TARGETS kahip DESTINATION lib) # Static interface library -add_library(kahip_static interface/kaHIP_interface.cpp $ - $ - $ - $) +add_library(kahip_static interface/kaHIP_interface.cpp) +target_link_libraries(kahip_static PRIVATE libkaffpa libmapping libnodeordering libspac) target_include_directories(kahip_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(kahip_static PRIVATE "-DMODE_KAFFPA") target_link_libraries(kahip_static PRIVATE OpenMP::OpenMP_CXX) set_target_properties(kahip_static PROPERTIES PUBLIC_HEADER interface/kaHIP_interface.h) - -if(LIB_METIS) - target_link_libraries(kahip_static PUBLIC ${LIB_METIS} ${LIB_GK}) +target_link_libraries(kahip_static PRIVATE kahip_headers) +if(metis_FOUND) + target_link_libraries(kahip_static PRIVATE metis GKlib) endif() install(TARGETS kahip_static @@ -391,12 +408,14 @@ endif() if(USE_ILP) find_package(Gurobi REQUIRED) MESSAGE("Using Gurobi for ILP solver in ilp_improve") - add_executable(ilp_improve app/ilp_improve.cpp $ $) + add_executable(ilp_improve app/ilp_improve.cpp) + target_link_libraries(ilp_improve PRIVATE libkaffpa libmapping) target_include_directories(ilp_improve PUBLIC ${GUROBI_INCLUDE_DIR}) target_compile_definitions(ilp_improve PRIVATE "-DMODE_ILPIMPROVE") target_link_libraries(ilp_improve PRIVATE OpenMP::OpenMP_CXX ${GUROBI_LIBRARIES}) - add_executable(ilp_exact app/ilp_exact.cpp $ $) + add_executable(ilp_exact app/ilp_exact.cpp) + target_link_libraries(ilp_exact PRIVATE libkaffpa libmapping) target_include_directories(ilp_exact PUBLIC ${GUROBI_INCLUDE_DIR}) target_compile_definitions(ilp_exact PRIVATE "-DMODE_ILPIMPROVE -DMODE_ILPEXACT") target_link_libraries(ilp_exact PRIVATE OpenMP::OpenMP_CXX ${GUROBI_LIBRARIES}) @@ -420,4 +439,4 @@ if (BUILDPYTHONMODULE) target_link_libraries(kahip_python_binding PUBLIC kahip_static) set_target_properties(kahip_python_binding PROPERTIES OUTPUT_NAME "kahip") -endif () +endif () \ No newline at end of file diff --git a/cmake/Cache.cmake b/cmake/Cache.cmake new file mode 100644 index 00000000..b73e7694 --- /dev/null +++ b/cmake/Cache.cmake @@ -0,0 +1,33 @@ +# Enable cache if available +function(kahip_enable_cache) + set(CACHE_OPTION + "ccache" + CACHE STRING "Compiler cache to be used") + set(CACHE_OPTION_VALUES "ccache" "sccache") + set_property(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES}) + list( + FIND + CACHE_OPTION_VALUES + ${CACHE_OPTION} + CACHE_OPTION_INDEX) + + if(${CACHE_OPTION_INDEX} EQUAL -1) + message( + STATUS + "Using custom compiler cache system: '${CACHE_OPTION}', explicitly supported entries are ${CACHE_OPTION_VALUES}" + ) + endif() + + find_program(CACHE_BINARY NAMES ${CACHE_OPTION_VALUES}) + if(CACHE_BINARY) + message(STATUS "${CACHE_BINARY} found and enabled") + set(CMAKE_CXX_COMPILER_LAUNCHER + ${CACHE_BINARY} + CACHE FILEPATH "CXX compiler cache used") + set(CMAKE_C_COMPILER_LAUNCHER + ${CACHE_BINARY} + CACHE FILEPATH "C compiler cache used") + else() + message(WARNING "${CACHE_OPTION} is enabled but was not found. Not using it") + endif() +endfunction() diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake new file mode 100644 index 00000000..5911ff16 --- /dev/null +++ b/cmake/CompilerWarnings.cmake @@ -0,0 +1,116 @@ +# from here: +# +# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md + +function( + kahip_set_project_warnings + project_name + WARNINGS_AS_ERRORS + MSVC_WARNINGS + CLANG_WARNINGS + GCC_WARNINGS + CUDA_WARNINGS) + if("${MSVC_WARNINGS}" STREQUAL "") + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not + # be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside + # the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + endif() + + if("${CLANG_WARNINGS}" STREQUAL "") + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps + # catch hard to track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output (ie printf) + -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation + ) + endif() + + if("${GCC_WARNINGS}" STREQUAL "") + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were probably wanted + -Wuseless-cast # warn if you perform a cast to the same type + -Wsuggest-override # warn if an overridden member function is not marked 'override' or 'final' + ) + endif() + + if("${CUDA_WARNINGS}" STREQUAL "") + set(CUDA_WARNINGS + -Wall + -Wextra + -Wunused + -Wconversion + -Wshadow + # TODO add more Cuda warnings + ) + endif() + + if(WARNINGS_AS_ERRORS) + message(TRACE "Warnings are treated as errors") + list(APPEND CLANG_WARNINGS -Werror) + list(APPEND GCC_WARNINGS -Werror) + list(APPEND MSVC_WARNINGS /WX) + endif() + + if(MSVC) + set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS}) + else() + message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") + # TODO support Intel compiler + endif() + + # use the same warning flags for C + set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}") + + set(PROJECT_WARNINGS_CUDA "${CUDA_WARNINGS}") + + target_compile_options( + ${project_name} + INTERFACE # C++ warnings + $<$:${PROJECT_WARNINGS_CXX}> + # C warnings + $<$:${PROJECT_WARNINGS_C}> + # Cuda warnings + $<$:${PROJECT_WARNINGS_CUDA}>) +endfunction() diff --git a/cmake/InterproceduralOptimization.cmake b/cmake/InterproceduralOptimization.cmake new file mode 100644 index 00000000..c8713ac2 --- /dev/null +++ b/cmake/InterproceduralOptimization.cmake @@ -0,0 +1,9 @@ +macro(kahip_enable_ipo) + include(CheckIPOSupported) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) + else() + message(SEND_ERROR "IPO is not supported: ${output}") + endif() +endmacro() diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake new file mode 100644 index 00000000..23072a2e --- /dev/null +++ b/cmake/KaHIPSettings.cmake @@ -0,0 +1,57 @@ +include(${CMAKE_SOURCE_DIR}/cmake/Cache.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/CompilerWarnings.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/InterproceduralOptimization.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/Sanitizers.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/StaticAnalyzers.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) + +kahip_supports_sanitizers() + +option(kahip_ENABLE_IPO "Enable IPO/LTO" OFF) +option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) +option(kahip_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) +option(kahip_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) +option(kahip_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" ${SUPPORTS_UBSAN}) +option(kahip_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) +option(kahip_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) +option(kahip_ENABLE_CLANG_TIDY "Enable clang-tidy" OFF) +option(kahip_ENABLE_CPPCHECK "Enable cpp-check analysis" OFF) +option(kahip_ENABLE_IWYU "Enable `include_what_you_use`" OFF) +option(kahip_ENABLE_CACHE "Enable ccache" OFF) + + +if(kahip_ENABLE_IPO) + kahip_enable_ipo() +endif() + +add_library(kahip_warnings INTERFACE) +add_library(kahip_options INTERFACE) + +kahip_set_project_warnings( + kahip_warnings + ${kahip_WARNINGS_AS_ERRORS} + "" + "" + "" + "") + +kahip_enable_sanitizers( + kahip_options + ${kahip_ENABLE_SANITIZER_ADDRESS} + ${kahip_ENABLE_SANITIZER_LEAK} + ${kahip_ENABLE_SANITIZER_UNDEFINED} + ${kahip_ENABLE_SANITIZER_THREAD} + ${kahip_ENABLE_SANITIZER_MEMORY}) + +if(kahip_ENABLE_CLANG_TIDY) + kahip_enable_clang_tidy(kahip_options ${kahip_WARNINGS_AS_ERRORS}) +endif() + +if(kahip_ENABLE_CPPCHECK) + kahip_enable_cppcheck(${kahip_WARNINGS_AS_ERRORS} "" # override cppcheck options + ) +endif() + +if(kahip_ENABLE_IWYU) + kahip_enable_include_what_you_use() +endif() \ No newline at end of file diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake new file mode 100644 index 00000000..fc52a113 --- /dev/null +++ b/cmake/Sanitizers.cmake @@ -0,0 +1,104 @@ +macro(kahip_supports_sanitizers) + if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND NOT WIN32) + set(SUPPORTS_UBSAN ON) + else() + set(SUPPORTS_UBSAN OFF) + endif() + + if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND WIN32) + set(SUPPORTS_ASAN OFF) + else() + set(SUPPORTS_ASAN ON) + endif() +endmacro() + + +function( + kahip_enable_sanitizers project_name + ENABLE_SANITIZER_ADDRESS + ENABLE_SANITIZER_LEAK + ENABLE_SANITIZER_UNDEFINED_BEHAVIOR + ENABLE_SANITIZER_THREAD + ENABLE_SANITIZER_MEMORY) + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(SANITIZERS "") + + if(${ENABLE_SANITIZER_ADDRESS}) + list(APPEND SANITIZERS "address") + endif() + + if(${ENABLE_SANITIZER_LEAK}) + list(APPEND SANITIZERS "leak") + endif() + + if(${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR}) + list(APPEND SANITIZERS "undefined") + endif() + + if(${ENABLE_SANITIZER_THREAD}) + if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) + message(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled") + else() + list(APPEND SANITIZERS "thread") + endif() + endif() + + if(${ENABLE_SANITIZER_MEMORY} AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + message( + WARNING + "Memory sanitizer requires all the code (including libc++) to be MSan-instrumented otherwise it reports false positives" + ) + if("address" IN_LIST SANITIZERS + OR "thread" IN_LIST SANITIZERS + OR "leak" IN_LIST SANITIZERS) + message(WARNING "Memory sanitizer does not work with Address, Thread or Leak sanitizer enabled") + else() + list(APPEND SANITIZERS "memory") + endif() + endif() + elseif(MSVC) + if(${ENABLE_SANITIZER_ADDRESS}) + list(APPEND SANITIZERS "address") + endif() + if(${ENABLE_SANITIZER_LEAK} + OR ${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR} + OR ${ENABLE_SANITIZER_THREAD} + OR ${ENABLE_SANITIZER_MEMORY}) + message(WARNING "MSVC only supports address sanitizer") + endif() + endif() + + list( + JOIN + SANITIZERS + "," + LIST_OF_SANITIZERS) + + if(LIST_OF_SANITIZERS) + if(NOT + "${LIST_OF_SANITIZERS}" + STREQUAL + "") + if(NOT MSVC) + target_compile_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) + target_link_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) + else() + string(FIND "$ENV{PATH}" "$ENV{VSINSTALLDIR}" index_of_vs_install_dir) + if("${index_of_vs_install_dir}" STREQUAL "-1") + message( + SEND_ERROR + "Using MSVC sanitizers requires setting the MSVC environment before building the project. Please manually open the MSVC command prompt and rebuild the project." + ) + endif() + target_compile_options(${project_name} INTERFACE /fsanitize=${LIST_OF_SANITIZERS} /Zi /INCREMENTAL:NO) + target_compile_definitions(${project_name} INTERFACE _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION) + target_link_options(${project_name} INTERFACE /INCREMENTAL:NO) + endif() + endif() + endif() + +endfunction() + + + diff --git a/cmake/StaticAnalyzers.cmake b/cmake/StaticAnalyzers.cmake new file mode 100644 index 00000000..54650cc5 --- /dev/null +++ b/cmake/StaticAnalyzers.cmake @@ -0,0 +1,112 @@ +macro(kahip_enable_cppcheck WARNINGS_AS_ERRORS CPPCHECK_OPTIONS) + find_program(CPPCHECK cppcheck) + if(CPPCHECK) + + if(CMAKE_GENERATOR MATCHES ".*Visual Studio.*") + set(CPPCHECK_TEMPLATE "vs") + else() + set(CPPCHECK_TEMPLATE "gcc") + endif() + + if("${CPPCHECK_OPTIONS}" STREQUAL "") + # Enable all warnings that are actionable by the user of this toolset + # style should enable the other 3, but we'll be explicit just in case + set(SUPPRESS_DIR "*:${CMAKE_CURRENT_BINARY_DIR}/_deps/*.h") + message(STATUS "CPPCHECK_OPTIONS suppress: ${SUPPRESS_DIR}") + set(CMAKE_CXX_CPPCHECK + ${CPPCHECK} + --template=${CPPCHECK_TEMPLATE} + --enable=style,performance,warning,portability + --inline-suppr + # We cannot act on a bug/missing feature of cppcheck + --suppress=cppcheckError + --suppress=internalAstError + # if a file does not have an internalAstError, we get an unmatchedSuppression error + --suppress=unmatchedSuppression + # noisy and incorrect sometimes + --suppress=passedByValue + # ignores code that cppcheck thinks is invalid C++ + --suppress=syntaxError + --suppress=preprocessorErrorDirective + --inconclusive + --suppress=${SUPPRESS_DIR}) + else() + # if the user provides a CPPCHECK_OPTIONS with a template specified, it will override this template + set(CMAKE_CXX_CPPCHECK ${CPPCHECK} --template=${CPPCHECK_TEMPLATE} ${CPPCHECK_OPTIONS}) + endif() + + if(NOT + "${CMAKE_CXX_STANDARD}" + STREQUAL + "") + set(CMAKE_CXX_CPPCHECK ${CMAKE_CXX_CPPCHECK} --std=c++${CMAKE_CXX_STANDARD}) + endif() + if(${WARNINGS_AS_ERRORS}) + list(APPEND CMAKE_CXX_CPPCHECK --error-exitcode=2) + endif() + else() + message(${WARNING_MESSAGE} "cppcheck requested but executable not found") + endif() +endmacro() + +macro(kahip_enable_clang_tidy target WARNINGS_AS_ERRORS) + + find_program(CLANGTIDY clang-tidy) + if(CLANGTIDY) + if(NOT + CMAKE_CXX_COMPILER_ID + MATCHES + ".*Clang") + + get_target_property(TARGET_PCH ${target} INTERFACE_PRECOMPILE_HEADERS) + + if("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND") + get_target_property(TARGET_PCH ${target} PRECOMPILE_HEADERS) + endif() + + if(NOT ("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND")) + message( + SEND_ERROR + "clang-tidy cannot be enabled with non-clang compiler and PCH, clang-tidy fails to handle gcc's PCH file") + endif() + endif() + + # construct the clang-tidy command line + set(CLANG_TIDY_OPTIONS + ${CLANGTIDY} + -extra-arg=-Wno-unknown-warning-option + -extra-arg=-Wno-ignored-optimization-argument + -extra-arg=-Wno-unused-command-line-argument + -p) + # set standard + if(NOT + "${CMAKE_CXX_STANDARD}" + STREQUAL + "") + if("${CLANG_TIDY_OPTIONS_DRIVER_MODE}" STREQUAL "cl") + set(CLANG_TIDY_OPTIONS ${CLANG_TIDY_OPTIONS} -extra-arg=/std:c++${CMAKE_CXX_STANDARD}) + else() + set(CLANG_TIDY_OPTIONS ${CLANG_TIDY_OPTIONS} -extra-arg=-std=c++${CMAKE_CXX_STANDARD}) + endif() + endif() + + # set warnings as errors + if(${WARNINGS_AS_ERRORS}) + list(APPEND CLANG_TIDY_OPTIONS -warnings-as-errors=*) + endif() + + message("Also setting clang-tidy globally") + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_OPTIONS}) + else() + message(${WARNING_MESSAGE} "clang-tidy requested but executable not found") + endif() +endmacro() + +macro(kahip_enable_include_what_you_use) + find_program(INCLUDE_WHAT_YOU_USE include-what-you-use) + if(INCLUDE_WHAT_YOU_USE) + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE}) + else() + message(${WARNING_MESSAGE} "include-what-you-use requested but executable not found") + endif() +endmacro() diff --git a/cmake/Utilities.cmake b/cmake/Utilities.cmake new file mode 100644 index 00000000..e278f518 --- /dev/null +++ b/cmake/Utilities.cmake @@ -0,0 +1,139 @@ +# find a substring from a string by a given prefix such as VCVARSALL_ENV_START +function( + find_substring_by_prefix + output + prefix + input) + # find the prefix + string(FIND "${input}" "${prefix}" prefix_index) + if("${prefix_index}" STREQUAL "-1") + message(SEND_ERROR "Could not find ${prefix} in ${input}") + endif() + # find the start index + string(LENGTH "${prefix}" prefix_length) + math(EXPR start_index "${prefix_index} + ${prefix_length}") + + string( + SUBSTRING "${input}" + "${start_index}" + "-1" + _output) + set("${output}" + "${_output}" + PARENT_SCOPE) +endfunction() + +# A function to set environment variables of CMake from the output of `cmd /c set` +function(set_env_from_string env_string) + # replace ; in paths with __sep__ so we can split on ; + string( + REGEX + REPLACE ";" + "__sep__" + env_string_sep_added + "${env_string}") + + # the variables are separated by \r?\n + string( + REGEX + REPLACE "\r?\n" + ";" + env_list + "${env_string_sep_added}") + + foreach(env_var ${env_list}) + # split by = + string( + REGEX + REPLACE "=" + ";" + env_parts + "${env_var}") + + list(LENGTH env_parts env_parts_length) + if("${env_parts_length}" EQUAL "2") + # get the variable name and value + list( + GET + env_parts + 0 + env_name) + list( + GET + env_parts + 1 + env_value) + + # recover ; in paths + string( + REGEX + REPLACE "__sep__" + ";" + env_value + "${env_value}") + + # set env_name to env_value + set(ENV{${env_name}} "${env_value}") + + # update cmake program path + if("${env_name}" EQUAL "PATH") + list(APPEND CMAKE_PROGRAM_PATH ${env_value}) + endif() + endif() + endforeach() +endfunction() + +function(get_all_targets var) + set(targets) + get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR}) + set(${var} + ${targets} + PARENT_SCOPE) +endfunction() + +function(get_all_installable_targets var) + set(targets) + get_all_targets(targets) + foreach(_target ${targets}) + get_target_property(_target_type ${_target} TYPE) + if(NOT + ${_target_type} + MATCHES + ".*LIBRARY|EXECUTABLE") + list(REMOVE_ITEM targets ${_target}) + endif() + endforeach() + set(${var} + ${targets} + PARENT_SCOPE) +endfunction() + +macro(get_all_targets_recursive targets dir) + get_property( + subdirectories + DIRECTORY ${dir} + PROPERTY SUBDIRECTORIES) + foreach(subdir ${subdirectories}) + get_all_targets_recursive(${targets} ${subdir}) + endforeach() + + get_property( + current_targets + DIRECTORY ${dir} + PROPERTY BUILDSYSTEM_TARGETS) + list(APPEND ${targets} ${current_targets}) +endmacro() + +function(is_verbose var) + if("CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "VERBOSE" + OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "DEBUG" + OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "TRACE") + set(${var} + ON + PARENT_SCOPE) + else() + set(${var} + OFF + PARENT_SCOPE) + endif() +endfunction() diff --git a/extern/argtable3-3.2.2/CMakeLists.txt b/extern/argtable3-3.2.2/CMakeLists.txt new file mode 100644 index 00000000..d3ace5fa --- /dev/null +++ b/extern/argtable3-3.2.2/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(argtable3 argtable3.c) +target_include_directories(argtable3 SYSTEM PUBLIC ${CMAKE_SOURCE_DIR}/extern/argtable3-3.2.2/) \ No newline at end of file diff --git a/lib/version.h b/lib/version/version.h similarity index 100% rename from lib/version.h rename to lib/version/version.h diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index e32818c9..57be5b1f 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -34,10 +34,11 @@ set(LIBPARALLEL_SOURCE_FILES lib/io/parallel_graph_io.cpp lib/io/parallel_vector_io.cpp lib/tools/random_functions.cpp - lib/tools/distributed_quality_metrics.cpp - extern/argtable3-3.2.2/argtable3.c) + lib/tools/distributed_quality_metrics.cpp) add_library(parallel ${LIBPARALLEL_SOURCE_FILES}) target_link_libraries(parallel PUBLIC libmodified_kahip_interface) +target_link_libraries(parallel PUBLIC argtable3) +target_link_libraries(parallel PUBLIC kahip_version) target_link_libraries(parallel PUBLIC MPI::MPI_CXX cista::cista) set(LIBGRAPH2BGF_SOURCE_FILES @@ -55,10 +56,9 @@ set(LIBEDGELIST_SOURCE_FILES lib/io/parallel_graph_io.cpp lib/data_structure/balance_management.cpp lib/data_structure/balance_management_refinement.cpp - lib/data_structure/balance_management_coarsening.cpp - extern/argtable3-3.2.2/argtable3.c) + lib/data_structure/balance_management_coarsening.cpp) add_library(libedgelist STATIC ${LIBEDGELIST_SOURCE_FILES}) -target_link_libraries(libedgelist PUBLIC MPI::MPI_CXX) +target_link_libraries(libedgelist PUBLIC MPI::MPI_CXX argtable3) set(LIBDSPAC_SOURCE_FILES @@ -73,6 +73,7 @@ target_compile_definitions(parhip PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DIST target_link_libraries(parhip PRIVATE libmodified_kahip_interface) target_link_libraries(parhip PRIVATE parallel) target_link_libraries(parhip PRIVATE MPI::MPI_CXX) +target_link_libraries(parhip PRIVATE argtable3) install(TARGETS parhip DESTINATION bin) add_executable(toolbox app/toolbox.cpp) @@ -85,23 +86,27 @@ install(TARGETS toolbox DESTINATION bin) add_executable(graph2binary app/graph2binary.cpp) target_compile_definitions(graph2binary PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION -DGRAPH2DGF") target_link_libraries(graph2binary PRIVATE libgraph2bgf) +target_link_libraries(graph2binary PRIVATE kahip_version) target_link_libraries(graph2binary PRIVATE MPI::MPI_CXX) install(TARGETS graph2binary DESTINATION bin) add_executable(graph2binary_external app/graph2binary_external.cpp) target_compile_definitions(graph2binary_external PRIVATE "-DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION -DGRAPH2DGF") target_link_libraries(graph2binary_external PRIVATE libgraph2bgf) +target_link_libraries(graph2binary_external PRIVATE kahip_version) install(TARGETS graph2binary_external DESTINATION bin) add_executable(readbgf app/readbgf.cpp) target_compile_definitions(readbgf PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_link_libraries(readbgf PRIVATE libgraph2bgf) +target_link_libraries(readbgf PRIVATE kahip_version) install(TARGETS readbgf DESTINATION bin) add_executable(edge_list_to_metis_graph app/edge_list_to_metis_graph.cpp) target_compile_definitions(edge_list_to_metis_graph PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DKRONECKER_GENERATOR_PROGRAM") target_link_libraries(edge_list_to_metis_graph PRIVATE libmodified_kahip_interface) target_link_libraries(edge_list_to_metis_graph PRIVATE libedgelist) +target_link_libraries(edge_list_to_metis_graph PRIVATE kahip_version) install(TARGETS edge_list_to_metis_graph DESTINATION bin) #add_executable(friendster_list_to_metis_graph app/friendster_list_to_metis_graph.cpp $) @@ -142,4 +147,4 @@ if (ENABLE_TESTING) enable_testing() message("Building Tests") add_subdirectory(tests) -endif () +endif () \ No newline at end of file diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/LICENSE b/parallel/parallel_src/extern/argtable3-3.2.2/LICENSE deleted file mode 100644 index 72cbcbc2..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/LICENSE +++ /dev/null @@ -1,167 +0,0 @@ -Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of STEWART HEITMANN nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -FreeBSD getopt library -====================== - -Copyright (c) 2000 The NetBSD Foundation, Inc. -All rights reserved. - -This code is derived from software contributed to The NetBSD Foundation -by Dieter Baron and Thomas Klausner. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - -Tcl library -=========== - -This software is copyrighted by the Regents of the University of -California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState -Corporation and other parties. The following terms apply to all files -associated with the software unless explicitly disclaimed in -individual files. - -The authors hereby grant permission to use, copy, modify, distribute, -and license this software and its documentation for any purpose, provided -that existing copyright notices are retained in all copies and that this -notice is included verbatim in any distributions. No written agreement, -license, or royalty fee is required for any of the authorized uses. -Modifications to this software may be copyrighted by their authors -and need not follow the licensing terms described here, provided that -the new terms are clearly indicated on the first page of each file where -they apply. - -IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY -FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY -DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE -IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE -NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR -MODIFICATIONS. - -GOVERNMENT USE: If you are acquiring this software on behalf of the -U.S. government, the Government shall have only "Restricted Rights" -in the software and related documentation as defined in the Federal -Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you -are acquiring the software on behalf of the Department of Defense, the -software shall be classified as "Commercial Computer Software" and the -Government shall have only "Restricted Rights" as defined in Clause -252.227-7014 (b) (3) of DFARs. Notwithstanding the foregoing, the -authors grant the U.S. Government and others acting in its behalf -permission to use and distribute the software in accordance with the -terms specified in this license. - - -C Hash Table library -==================== - -Copyright (c) 2002, Christopher Clark -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -* Neither the name of the original author; nor the names of any contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -The Better String library -========================= - -Copyright (c) 2014, Paul Hsieh -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of bstrlib nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/README.md b/parallel/parallel_src/extern/argtable3-3.2.2/README.md deleted file mode 100644 index 31376c2f..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/README.md +++ /dev/null @@ -1,399 +0,0 @@ -[![Build Status](https://travis-ci.org/argtable/argtable3.svg?branch=master)](https://travis-ci.org/argtable/argtable3) -[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) - - -Introduction of Argtable3 -========================= - -**Argtable3** is an open source ANSI C library that parses GNU-style -command-line options with the `getopt` library. It simplifies command-line -parsing by defining a declarative-style API that you can use to specify what -your command-line syntax looks like. Argtable3 will automatically generate -consistent error handling logic and textual descriptions of the command line -syntax, which are essential but tedious to implement for a robust CLI program. - - -Quick Start ------------ - -You can embed the amalgamation source files in your projects, add Argtable3 as a -dependency in the vcpkg manifest, install Argtable3 as a system-wide CMake -package, or build the library from release archives. - -### Embed Amalgamation Source Files - -> We no longer provide the amalgamation source files (`argtable3.c` and -> `argtable3.h`) in the repository. You can get the amalgamation distribution -> either from the release page (`argtable--amalgamation.(zip|tar.gz)`), -> or generate the distribution yourself by using the generator under the `tools` -> directory: -> -> 1. Navigate to the `tools` directory. -> 2. Run `./build dist`, which will generate the distribution under the `/dist` -> directory. - -Add `argtable3.c` and `argtable3.h` from the amalgamation distribution to your -projects. This is the simplest and recommended way to use Argtable3: it not only -removes the hassle of building the library, but also allows compilers to do -better inter-procedure optimization. - - -### Install for a Single Project with vcpkg Manifest - -[vcpkg](https://vcpkg.io) is an open source C/C++ package manager based on -CMake, and it supports certain stable releases of Argtable3. To add the library -to your CMake project, it's recommended to add vcpkg as a submodule to your -project repo and use it to manage project dependencies. All libraries installed -in this way can only be consumed by the project and won't impact other projects -in the system. - -If your project is under `D:/projects/demo` and the vcpkg submodule is under -`D:/projects/demo/deps/vcpkg`, first you need to add Argtable3 to the manifest, -`D:/projects/demo/vcpkg.json`: -``` -{ - "name": "demo", - "version": "0.0.1", - "dependencies": [ - { - "name": "argtable3", - "version>=": "3.2.1" - } - ], - "builtin-baseline": "92b42c4c680defe94f1665a847d04ded890f372e" -} -``` - -To add Argtable3 to your CMake scripts, you need to integrate the local vcpkg to -CMake by setting the `CMAKE_TOOLCHAIN_FILE` variable. You also need to link to -the static VC runtime (`/MT` or `/MTd`) if you want to use the static library -version of Argtable3: -``` -cmake_minimum_required(VERSION 3.18) - -set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/deps/vcpkg/scripts/buildsystems/vcpkg.cmake - CACHE STRING "Vcpkg toolchain file") - -project(versionstest) - -add_executable(main main.cpp) - -find_package(Argtable3 CONFIG REQUIRED) -target_link_libraries(main PRIVATE argtable3::argtable3) - -if(VCPKG_TARGET_TRIPLET STREQUAL "x64-windows-static") - set_property(TARGET main PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -endif() -``` - -Now you can run `cmake` to install Argtable3, configure and generate build -scripts, and build the project: -``` -$ mkdir build -$ cd build -$ cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -$ cmake --build . -``` - -### Install for All Projects with vcpkg - -If you want to make Argtable3 available for all projects in the system, you can -clone vcpkg to any directory and install packages there. Assuming vcpkg has been -cloned in `D:/dev/vcpkg` and the directory has been added to `PATH`, you can -install the static library version of Argtable3 in `D:/dev/vcpkg/installed`: -``` -$ vcpkg install argtable3:x64-windows-static -``` - -Since each developer may clone vcpkg in a different place, it may not be -appropriate to specify the `CMAKE_TOOLCHAIN_FILE` variable in `CMakeLists.txt`. -Therefore, you should remove setting the `CMAKE_TOOLCHAIN_FILE` variable in the -`CMakeLists.txt` example above, and set the variable in the command line: -``` -$ mkdir build -$ cd build -$ cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_TOOLCHAIN_FILE=D:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake -$ cmake --build . -``` - - -### Build from Release Archives or Source - -If none of the methods above suits your needs, or if you want to help developing -Argtable3, you can always build from archives on the release page or from the -repository. - -* If you use GCC (Linux, MacOSX, MinGW, Cygwin), run: - - ``` - $ mkdir build - $ cd build - $ cmake -DCMAKE_BUILD_TYPE=Debug .. - $ make - $ make test - ``` - - Makefile-based generators in CMake only support one configuration at a time, - so you need to specify `CMAKE_BUILD_TYPE` to `Debug`, `Release`, `MinSizeRel`, - or `RelWithDebInfo`. To build multiple configurations, you need to create a - build directory for each configuraiton. - - Since v3.2.1, CMake scripts will check `BUILD_SHARED_LIBS` and build either - the static library or the dynamic library at a time. `BUILD_SHARED_LIBS` is - `OFF` by default, so if you want to build the dynamic library, you have to set - `BUILD_SHARED_LIBS` to `ON` explicitly: - - ``` - $ cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON .. - ``` - - To cleanup, run `make clean` or remove the build directory: - - ``` - $ rm -rf build - ``` - -* If you use Microsoft Visual C++ compiler, run: - - ``` - $ mkdir build - $ cd build - $ cmake -G "Visual Studio 15 2017 Win64" .. - $ cmake --build . --config Debug - $ ctest -C Debug - ``` - - You can also use Visual Studio 2017 IDE to open the generated solution. To - cleanup, just remove the `build` directory. - - -To build a tagged version, go to the project root directory, and use the -`Makefile` in the project root folder to check out the specified version: - - ``` - $ make taglist - Available TAGs: - v3.1.1.432a160 - $ make co TAG=v3.1.1.432a160 - $ cd .tags/v3.1.1.432a160 - $ mkdir build - $ cd build - $ cmake .. - $ make - $ make test - ``` - -You will find the shared library (or Windows DLL), static library, and the -amalgamation distribution under the build directory. - - -Documentation -------------- - -To learn how to use the Argtable3 API, you can see the documentation on the web -site, study examples in the `examples` directory, or even check the unit tests -in the `tests` directory. - -To build a local copy of the documentation, you need to install the following -tools: - -* [Sphinx](https://www.sphinx-doc.org): A documentation generator based on the - reStructuredText markup format. -* [Read the Docs Sphinx Theme](https://sphinx-rtd-theme.readthedocs.io): A - Sphinx theme designed to look modern and be mobile-friendly. -* [Breathe](https://breathe.readthedocs.io): A bridge between the Sphinx and - Doxygen documentation systems. -* [Doxygen](http://www.doxygen.nl/): A documentation generator for C/C++ - sources. - -Go to the `docs` directory and run the `doxygen` command to generate Doxygen XML -output, which will be saved in the `docs/xml` directory: - -``` -$ doxygen -``` - -Run the `make` batch script and you will see the documentation in the -`docs/_build/html` directory. - -``` -$ make html -``` - - -Unit Tests ----------- - -Argtable3 is a BSD-licensed open source library, so you can modify the library -anyway you want. However, before committing your code to your own repository or -the Argtable3 official repository, please make sure your changes won't cause any -compiler warning and can pass the unit tests included in the distribution. - -To build and test each configuration (`Debug`, `Release`, `MinSizeRel`, -`RelWithDebInfo`), you can run CMake and CTest on all supported platforms: - -``` -$ mkdir build_debug && cd build_debug -$ cmake -DCMAKE_BUILD_TYPE=Debug .. -$ cmake --build . --config Debug -$ ctest -C Debug - -$ cd .. && mkdir build_release && cd build_release -$ cmake -DCMAKE_BUILD_TYPE=Release .. -$ cmake --build . --config Release -$ ctest -C Release - -$ cd .. && mkdir build_minsizerel && cd build_minsizerel -$ cmake -DCMAKE_BUILD_TYPE=MinSizeRel .. -$ cmake --build . --config MinSizeRel -$ ctest -C MinSizeRel - -$ cd .. && mkdir build_relwithdebinfo && cd build_relwithdebinfo -$ cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. -$ cmake --build . --config RelWithDebInfo -$ ctest -C RelWithDebInfo -``` - -If you see the following screenshot, you know that some unit tests are broken: - -``` -$ make test -Running tests... -Test project ~/Projects/argtable3/build-gcc-release - Start 1: test_shared -1/4 Test #1: test_shared ......................***Failed 0.07 sec - Start 2: test_static -2/4 Test #2: test_static ......................***Failed 0.13 sec - Start 3: test_src -3/4 Test #3: test_src .........................***Failed 0.13 sec - Start 4: test_amalgamation -4/4 Test #4: test_amalgamation ................***Failed 0.14 sec - -0% tests passed, 4 tests failed out of 4 - -Total Test time (real) = 0.48 sec - -The following tests FAILED: - 1 - test_shared (Failed) - 2 - test_static (Failed) - 3 - test_src (Failed) - 4 - test_amalgamation (Failed) -Errors while running CTest -make: *** [Makefile:97: test] Error 8 -``` - -To understand which unit tests are broken, you need to run the failed test -programs (based on CuTest) directly: - -``` -$ ./tests/test_shared -....................................................................................... -...................................................................F............. - -There was 1 failure: -1) test_argdstr_basic_001: ~/Projects/argtable3/tests/testargdstr.c:51: assert failed - -!!!FAILURES!!! -Runs: 168 Passes: 167 Fails: 1 -``` - - -Memory Issue Detection with ASan and Valgrind ---------------------------------------------- - -In order to prevent common memory issues in C, such as memory leak and buffer -overflow, we should use [ASan -(AddressSanitizer)](https://en.wikipedia.org/wiki/AddressSanitizer) and -[Valgrind](https://en.wikipedia.org/wiki/Valgrind) to detect as many -memory-related problems as possible before committing our code. - -To use ASan, we need to add `-fsanitize=address` to the `CFLAGS` variable when -we run `cmake` to build the **Debug** version. We should use the Debug version -because CMake will add `-g` to `CFLAGS` and prevent optimizing the code, so we -can see accurate file names and line numbers in ASan error messages. After -building the code, set the `CTEST_OUTPUT_ON_FAILURE` variable to `1` to output -error messages when we run unit tests: - -``` -$ mkdir build -$ cd build -$ CFLAGS="-fsanitize=address" cmake -DCMAKE_BUILD_TYPE=Debug .. -$ make -$ CTEST_OUTPUT_ON_FAILURE=1 make test -Running tests... -Test project /home/tomghuang/Projects/argtable3/build - Start 1: test_shared -1/4 Test #1: test_shared ...................... Passed 3.45 sec - Start 2: test_static -2/4 Test #2: test_static ...................... Passed 3.31 sec - Start 3: test_src -3/4 Test #3: test_src ......................... Passed 3.06 sec - Start 4: test_amalgamation -4/4 Test #4: test_amalgamation ................ Passed 3.29 sec - -100% tests passed, 0 tests failed out of 4 - -Total Test time (real) = 13.12 sec -``` - -To use Valgrind, just use `valgrind` to run the unit test programs: - -``` -$ valgrind ./tests/test_src -==23290== Memcheck, a memory error detector -==23290== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. -==23290== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info -==23290== Command: ./test_src -==23290== -...................................................................................... -.................................................................................. - -OK (168 tests) - -==23290== -==23290== HEAP SUMMARY: -==23290== in use at exit: 0 bytes in 0 blocks -==23290== total heap usage: 102,085 allocs, 102,085 frees, 5,589,475 bytes allocated -==23290== -==23290== All heap blocks were freed -- no leaks are possible -==23290== -==23290== For counts of detected and suppressed errors, rerun with: -v -==23290== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) -``` - - -Authors -------- - -Argtable is Copyright (C) 1998-2001,2003-2011 Stewart Heitmann. -Parts are Copyright (C) 1989-1994, 1996-1999, 2001, 2003 - Free Software Foundation, Inc. - -Argtable was written by Stewart Heitmann - -Argtable is now maintained by Tom G. Huang -The project homepage of argtable 3.x is http://www.argtable.org -The project homepage of argtable 2.x is http://argtable.sourceforge.net/ - -Here is a list of contributors who have helped to improve argtable: - -- **Nina Clemson**: Editing the original argtable-1.0 documentation. -- **Livio Bertacco**: For bug fixes and the argtable-2.x Visual C++ Makefiles. -- **Justin Dearing**: For bug fixes and Windows DLL support, plus code support for the Open Watcom compiler and help with the Mac OS X configuration. -- **Asa Packer**: Contributing bug fixes and upgrades to the Visual C++ Makefiles. -- **Danilo Cicerone**: For the Italian translation of "Introduction to Argtable-2x" on http://www.digitazero.org. -- **Uli Fouquet**: For configuration patches and documentation related to cross-compiling argtable from Unix to Windows, as well as providing the arg_print_glossary_gnu function. -- **Shachar Shemesh**: For Debian package integration and kick-starting the migration to automake/autoconf. -- **Jasper Lievisse Adriaanse**: Maintaining the argtable package in OpenBSD ports. -- **Ulrich Mohr**: For bug fixes relating to Texas Instrument DSP platforms. -- **John Vickers**: For bug fixes relating to Solaris/Motorola platforms. -- **Steve O'Neil**: For bug fixes relating to Solaris/Motorola platforms. -- **Lori A. Pritchett-Sheats**: Fixing a makefile bug relating to "make dist". -- **Paolo Bormida**: For instructions on building argtable with date and regex support on Windows. -- **Michel Valin**: For bug fixes relating to the configure scripts on IBM AIX platforms and instructions on compiling the example code under AIX. -- **Steve Christensen**: Providing prebuilt packages for SPARC/Solaris and x86/Solaris platforms on www.sunfreeware.com. -- **Jess Portnoy**: Reworking the rpm package and integrating argtable into Fedora Linux. -- **Michael Brown**: Incorporating support for pkg-config into the autoconf scripts. -- **Alexander Lindert**: For extensions to the parser to support hex, octal and binary integer formats as well as KB/MB/GB suffixes. -- **Rob Zaborowski**: Providing build configuration files for CMake. -- **Moczik Gabor**: For bug fixes relating to the parsing of filepaths and filename extensions. diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/argtable3.c b/parallel/parallel_src/extern/argtable3-3.2.2/argtable3.c deleted file mode 100644 index dcc8e769..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/argtable3.c +++ /dev/null @@ -1,6021 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#define ARG_AMALGAMATION - -/******************************************************************************* - * argtable3_private: Declares private types, constants, and interfaces - * - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#ifndef ARG_UTILS_H -#define ARG_UTILS_H - -#include - -#define ARG_ENABLE_TRACE 0 -#define ARG_ENABLE_LOG 1 - -#ifdef __cplusplus -extern "C" { -#endif - -enum { ARG_ERR_MINCOUNT = 1, ARG_ERR_MAXCOUNT, ARG_ERR_BADINT, ARG_ERR_OVERFLOW, ARG_ERR_BADDOUBLE, ARG_ERR_BADDATE, ARG_ERR_REGNOMATCH }; - -typedef void(arg_panicfn)(const char* fmt, ...); - -#if defined(_MSC_VER) -#define ARG_TRACE(x) \ - __pragma(warning(push)) __pragma(warning(disable : 4127)) do { \ - if (ARG_ENABLE_TRACE) \ - dbg_printf x; \ - } \ - while (0) \ - __pragma(warning(pop)) - -#define ARG_LOG(x) \ - __pragma(warning(push)) __pragma(warning(disable : 4127)) do { \ - if (ARG_ENABLE_LOG) \ - dbg_printf x; \ - } \ - while (0) \ - __pragma(warning(pop)) -#else -#define ARG_TRACE(x) \ - do { \ - if (ARG_ENABLE_TRACE) \ - dbg_printf x; \ - } while (0) - -#define ARG_LOG(x) \ - do { \ - if (ARG_ENABLE_LOG) \ - dbg_printf x; \ - } while (0) -#endif - -/* - * Rename a few generic names to unique names. - * They can be a problem for the platforms like NuttX, where - * the namespace is flat for everything including apps and libraries. - */ -#define xmalloc argtable3_xmalloc -#define xcalloc argtable3_xcalloc -#define xrealloc argtable3_xrealloc -#define xfree argtable3_xfree - -extern void dbg_printf(const char* fmt, ...); -extern void arg_set_panic(arg_panicfn* proc); -extern void* xmalloc(size_t size); -extern void* xcalloc(size_t count, size_t size); -extern void* xrealloc(void* ptr, size_t size); -extern void xfree(void* ptr); - -struct arg_hashtable_entry { - void *k, *v; - unsigned int h; - struct arg_hashtable_entry* next; -}; - -typedef struct arg_hashtable { - unsigned int tablelength; - struct arg_hashtable_entry** table; - unsigned int entrycount; - unsigned int loadlimit; - unsigned int primeindex; - unsigned int (*hashfn)(const void* k); - int (*eqfn)(const void* k1, const void* k2); -} arg_hashtable_t; - -/** - * @brief Create a hash table. - * - * @param minsize minimum initial size of hash table - * @param hashfn function for hashing keys - * @param eqfn function for determining key equality - * @return newly created hash table or NULL on failure - */ -arg_hashtable_t* arg_hashtable_create(unsigned int minsize, unsigned int (*hashfn)(const void*), int (*eqfn)(const void*, const void*)); - -/** - * @brief This function will cause the table to expand if the insertion would take - * the ratio of entries to table size over the maximum load factor. - * - * This function does not check for repeated insertions with a duplicate key. - * The value returned when using a duplicate key is undefined -- when - * the hash table changes size, the order of retrieval of duplicate key - * entries is reversed. - * If in doubt, remove before insert. - * - * @param h the hash table to insert into - * @param k the key - hash table claims ownership and will free on removal - * @param v the value - does not claim ownership - * @return non-zero for successful insertion - */ -void arg_hashtable_insert(arg_hashtable_t* h, void* k, void* v); - -#define ARG_DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ - int fnname(arg_hashtable_t* h, keytype* k, valuetype* v) { return arg_hashtable_insert(h, k, v); } - -/** - * @brief Search the specified key in the hash table. - * - * @param h the hash table to search - * @param k the key to search for - does not claim ownership - * @return the value associated with the key, or NULL if none found - */ -void* arg_hashtable_search(arg_hashtable_t* h, const void* k); - -#define ARG_DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ - valuetype* fnname(arg_hashtable_t* h, keytype* k) { return (valuetype*)(arg_hashtable_search(h, k)); } - -/** - * @brief Remove the specified key from the hash table. - * - * @param h the hash table to remove the item from - * @param k the key to search for - does not claim ownership - */ -void arg_hashtable_remove(arg_hashtable_t* h, const void* k); - -#define ARG_DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ - void fnname(arg_hashtable_t* h, keytype* k) { arg_hashtable_remove(h, k); } - -/** - * @brief Return the number of keys in the hash table. - * - * @param h the hash table - * @return the number of items stored in the hash table - */ -unsigned int arg_hashtable_count(arg_hashtable_t* h); - -/** - * @brief Change the value associated with the key. - * - * function to change the value associated with a key, where there already - * exists a value bound to the key in the hash table. - * Source due to Holger Schemel. - * - * @name hashtable_change - * @param h the hash table - * @param key - * @param value - */ -int arg_hashtable_change(arg_hashtable_t* h, void* k, void* v); - -/** - * @brief Free the hash table and the memory allocated for each key-value pair. - * - * @param h the hash table - * @param free_values whether to call 'free' on the remaining values - */ -void arg_hashtable_destroy(arg_hashtable_t* h, int free_values); - -typedef struct arg_hashtable_itr { - arg_hashtable_t* h; - struct arg_hashtable_entry* e; - struct arg_hashtable_entry* parent; - unsigned int index; -} arg_hashtable_itr_t; - -arg_hashtable_itr_t* arg_hashtable_itr_create(arg_hashtable_t* h); - -void arg_hashtable_itr_destroy(arg_hashtable_itr_t* itr); - -/** - * @brief Return the value of the (key,value) pair at the current position. - */ -extern void* arg_hashtable_itr_key(arg_hashtable_itr_t* i); - -/** - * @brief Return the value of the (key,value) pair at the current position. - */ -extern void* arg_hashtable_itr_value(arg_hashtable_itr_t* i); - -/** - * @brief Advance the iterator to the next element. Returns zero if advanced to end of table. - */ -int arg_hashtable_itr_advance(arg_hashtable_itr_t* itr); - -/** - * @brief Remove current element and advance the iterator to the next element. - */ -int arg_hashtable_itr_remove(arg_hashtable_itr_t* itr); - -/** - * @brief Search and overwrite the supplied iterator, to point to the entry matching the supplied key. - * - * @return Zero if not found. - */ -int arg_hashtable_itr_search(arg_hashtable_itr_t* itr, arg_hashtable_t* h, void* k); - -#define ARG_DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ - int fnname(arg_hashtable_itr_t* i, arg_hashtable_t* h, keytype* k) { return (arg_hashtable_iterator_search(i, h, k)); } - -#ifdef __cplusplus -} -#endif - -#endif -/******************************************************************************* - * arg_utils: Implements memory, panic, and other utility functions - * - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include -#include -#include -#include - -static void panic(const char* fmt, ...); -static arg_panicfn* s_panic = panic; - -void dbg_printf(const char* fmt, ...) { - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); -} - -static void panic(const char* fmt, ...) { - va_list args; - char* s; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4996) -#endif - s = getenv("EF_DUMPCORE"); -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - - if (s != NULL && *s != '\0') { - abort(); - } else { - exit(EXIT_FAILURE); - } -} - -void arg_set_panic(arg_panicfn* proc) { - s_panic = proc; -} - -void* xmalloc(size_t size) { - void* ret = malloc(size); - if (!ret) { - s_panic("Out of memory!\n"); - } - return ret; -} - -void* xcalloc(size_t count, size_t size) { - size_t allocated_count = count && size ? count : 1; - size_t allocated_size = count && size ? size : 1; - void* ret = calloc(allocated_count, allocated_size); - if (!ret) { - s_panic("Out of memory!\n"); - } - return ret; -} - -void* xrealloc(void* ptr, size_t size) { - size_t allocated_size = size ? size : 1; - void* ret = realloc(ptr, allocated_size); - if (!ret) { - s_panic("Out of memory!\n"); - } - return ret; -} - -void xfree(void* ptr) { - free(ptr); -} - -static void merge(void* data, int esize, int i, int j, int k, arg_comparefn* comparefn) { - char* a = (char*)data; - char* m; - int ipos, jpos, mpos; - - /* Initialize the counters used in merging. */ - ipos = i; - jpos = j + 1; - mpos = 0; - - /* Allocate storage for the merged elements. */ - m = (char*)xmalloc((size_t)(esize * ((k - i) + 1))); - - /* Continue while either division has elements to merge. */ - while (ipos <= j || jpos <= k) { - if (ipos > j) { - /* The left division has no more elements to merge. */ - while (jpos <= k) { - memcpy(&m[mpos * esize], &a[jpos * esize], (size_t)esize); - jpos++; - mpos++; - } - - continue; - } else if (jpos > k) { - /* The right division has no more elements to merge. */ - while (ipos <= j) { - memcpy(&m[mpos * esize], &a[ipos * esize], (size_t)esize); - ipos++; - mpos++; - } - - continue; - } - - /* Append the next ordered element to the merged elements. */ - if (comparefn(&a[ipos * esize], &a[jpos * esize]) < 0) { - memcpy(&m[mpos * esize], &a[ipos * esize], (size_t)esize); - ipos++; - mpos++; - } else { - memcpy(&m[mpos * esize], &a[jpos * esize], (size_t)esize); - jpos++; - mpos++; - } - } - - /* Prepare to pass back the merged data. */ - memcpy(&a[i * esize], m, (size_t)(esize * ((k - i) + 1))); - xfree(m); -} - -void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn) { - int j; - - /* Stop the recursion when no more divisions can be made. */ - if (i < k) { - /* Determine where to divide the elements. */ - j = (int)(((i + k - 1)) / 2); - - /* Recursively sort the two divisions. */ - arg_mgsort(data, size, esize, i, j, comparefn); - arg_mgsort(data, size, esize, j + 1, k, comparefn); - merge(data, esize, i, j, k, comparefn); - } -} -/******************************************************************************* - * arg_hashtable: Implements the hash table utilities - * - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include -#include -#include -#include - -/* - * This hash table module is adapted from the C hash table implementation by - * Christopher Clark. Here is the copyright notice from the library: - * - * Copyright (c) 2002, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Credit for primes table: Aaron Krowne - * http://br.endernet.org/~akrowne/ - * http://planetmath.org/encyclopedia/GoodHashTablePrimes.html - */ -static const unsigned int primes[] = {53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, - 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, - 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741}; -const unsigned int prime_table_length = sizeof(primes) / sizeof(primes[0]); -const float max_load_factor = (float)0.65; - -static unsigned int enhanced_hash(arg_hashtable_t* h, const void* k) { - /* - * Aim to protect against poor hash functions by adding logic here. - * The logic is taken from Java 1.4 hash table source. - */ - unsigned int i = h->hashfn(k); - i += ~(i << 9); - i ^= ((i >> 14) | (i << 18)); /* >>> */ - i += (i << 4); - i ^= ((i >> 10) | (i << 22)); /* >>> */ - return i; -} - -static unsigned int index_for(unsigned int tablelength, unsigned int hashvalue) { - return (hashvalue % tablelength); -} - -arg_hashtable_t* arg_hashtable_create(unsigned int minsize, unsigned int (*hashfn)(const void*), int (*eqfn)(const void*, const void*)) { - arg_hashtable_t* h; - unsigned int pindex; - unsigned int size = primes[0]; - - /* Check requested hash table isn't too large */ - if (minsize > (1u << 30)) - return NULL; - - /* - * Enforce size as prime. The reason is to avoid clustering of values - * into a small number of buckets (yes, distribution). A more even - * distributed hash table will perform more consistently. - */ - for (pindex = 0; pindex < prime_table_length; pindex++) { - if (primes[pindex] > minsize) { - size = primes[pindex]; - break; - } - } - - h = (arg_hashtable_t*)xmalloc(sizeof(arg_hashtable_t)); - h->table = (struct arg_hashtable_entry**)xmalloc(sizeof(struct arg_hashtable_entry*) * size); - memset(h->table, 0, size * sizeof(struct arg_hashtable_entry*)); - h->tablelength = size; - h->primeindex = pindex; - h->entrycount = 0; - h->hashfn = hashfn; - h->eqfn = eqfn; - h->loadlimit = (unsigned int)ceil(size * (double)max_load_factor); - return h; -} - -static int arg_hashtable_expand(arg_hashtable_t* h) { - /* Double the size of the table to accommodate more entries */ - struct arg_hashtable_entry** newtable; - struct arg_hashtable_entry* e; - unsigned int newsize; - unsigned int i; - unsigned int index; - - /* Check we're not hitting max capacity */ - if (h->primeindex == (prime_table_length - 1)) - return 0; - newsize = primes[++(h->primeindex)]; - - newtable = (struct arg_hashtable_entry**)xmalloc(sizeof(struct arg_hashtable_entry*) * newsize); - memset(newtable, 0, newsize * sizeof(struct arg_hashtable_entry*)); - /* - * This algorithm is not 'stable': it reverses the list - * when it transfers entries between the tables - */ - for (i = 0; i < h->tablelength; i++) { - while (NULL != (e = h->table[i])) { - h->table[i] = e->next; - index = index_for(newsize, e->h); - e->next = newtable[index]; - newtable[index] = e; - } - } - - xfree(h->table); - h->table = newtable; - h->tablelength = newsize; - h->loadlimit = (unsigned int)ceil(newsize * (double)max_load_factor); - return -1; -} - -unsigned int arg_hashtable_count(arg_hashtable_t* h) { - return h->entrycount; -} - -void arg_hashtable_insert(arg_hashtable_t* h, void* k, void* v) { - /* This method allows duplicate keys - but they shouldn't be used */ - unsigned int index; - struct arg_hashtable_entry* e; - if ((h->entrycount + 1) > h->loadlimit) { - /* - * Ignore the return value. If expand fails, we should - * still try cramming just this value into the existing table - * -- we may not have memory for a larger table, but one more - * element may be ok. Next time we insert, we'll try expanding again. - */ - arg_hashtable_expand(h); - } - e = (struct arg_hashtable_entry*)xmalloc(sizeof(struct arg_hashtable_entry)); - e->h = enhanced_hash(h, k); - index = index_for(h->tablelength, e->h); - e->k = k; - e->v = v; - e->next = h->table[index]; - h->table[index] = e; - h->entrycount++; -} - -void* arg_hashtable_search(arg_hashtable_t* h, const void* k) { - struct arg_hashtable_entry* e; - unsigned int hashvalue; - unsigned int index; - - hashvalue = enhanced_hash(h, k); - index = index_for(h->tablelength, hashvalue); - e = h->table[index]; - while (e != NULL) { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) - return e->v; - e = e->next; - } - return NULL; -} - -void arg_hashtable_remove(arg_hashtable_t* h, const void* k) { - /* - * TODO: consider compacting the table when the load factor drops enough, - * or provide a 'compact' method. - */ - - struct arg_hashtable_entry* e; - struct arg_hashtable_entry** pE; - unsigned int hashvalue; - unsigned int index; - - hashvalue = enhanced_hash(h, k); - index = index_for(h->tablelength, hashvalue); - pE = &(h->table[index]); - e = *pE; - while (NULL != e) { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { - *pE = e->next; - h->entrycount--; - xfree(e->k); - xfree(e->v); - xfree(e); - return; - } - pE = &(e->next); - e = e->next; - } -} - -void arg_hashtable_destroy(arg_hashtable_t* h, int free_values) { - unsigned int i; - struct arg_hashtable_entry *e, *f; - struct arg_hashtable_entry** table = h->table; - if (free_values) { - for (i = 0; i < h->tablelength; i++) { - e = table[i]; - while (NULL != e) { - f = e; - e = e->next; - xfree(f->k); - xfree(f->v); - xfree(f); - } - } - } else { - for (i = 0; i < h->tablelength; i++) { - e = table[i]; - while (NULL != e) { - f = e; - e = e->next; - xfree(f->k); - xfree(f); - } - } - } - xfree(h->table); - xfree(h); -} - -arg_hashtable_itr_t* arg_hashtable_itr_create(arg_hashtable_t* h) { - unsigned int i; - unsigned int tablelength; - - arg_hashtable_itr_t* itr = (arg_hashtable_itr_t*)xmalloc(sizeof(arg_hashtable_itr_t)); - itr->h = h; - itr->e = NULL; - itr->parent = NULL; - tablelength = h->tablelength; - itr->index = tablelength; - if (0 == h->entrycount) - return itr; - - for (i = 0; i < tablelength; i++) { - if (h->table[i] != NULL) { - itr->e = h->table[i]; - itr->index = i; - break; - } - } - return itr; -} - -void arg_hashtable_itr_destroy(arg_hashtable_itr_t* itr) { - xfree(itr); -} - -void* arg_hashtable_itr_key(arg_hashtable_itr_t* i) { - return i->e->k; -} - -void* arg_hashtable_itr_value(arg_hashtable_itr_t* i) { - return i->e->v; -} - -int arg_hashtable_itr_advance(arg_hashtable_itr_t* itr) { - unsigned int j; - unsigned int tablelength; - struct arg_hashtable_entry** table; - struct arg_hashtable_entry* next; - - if (itr->e == NULL) - return 0; /* stupidity check */ - - next = itr->e->next; - if (NULL != next) { - itr->parent = itr->e; - itr->e = next; - return -1; - } - - tablelength = itr->h->tablelength; - itr->parent = NULL; - if (tablelength <= (j = ++(itr->index))) { - itr->e = NULL; - return 0; - } - - table = itr->h->table; - while (NULL == (next = table[j])) { - if (++j >= tablelength) { - itr->index = tablelength; - itr->e = NULL; - return 0; - } - } - - itr->index = j; - itr->e = next; - return -1; -} - -int arg_hashtable_itr_remove(arg_hashtable_itr_t* itr) { - struct arg_hashtable_entry* remember_e; - struct arg_hashtable_entry* remember_parent; - int ret; - - /* Do the removal */ - if ((itr->parent) == NULL) { - /* element is head of a chain */ - itr->h->table[itr->index] = itr->e->next; - } else { - /* element is mid-chain */ - itr->parent->next = itr->e->next; - } - /* itr->e is now outside the hashtable */ - remember_e = itr->e; - itr->h->entrycount--; - xfree(remember_e->k); - xfree(remember_e->v); - - /* Advance the iterator, correcting the parent */ - remember_parent = itr->parent; - ret = arg_hashtable_itr_advance(itr); - if (itr->parent == remember_e) { - itr->parent = remember_parent; - } - xfree(remember_e); - return ret; -} - -int arg_hashtable_itr_search(arg_hashtable_itr_t* itr, arg_hashtable_t* h, void* k) { - struct arg_hashtable_entry* e; - struct arg_hashtable_entry* parent; - unsigned int hashvalue; - unsigned int index; - - hashvalue = enhanced_hash(h, k); - index = index_for(h->tablelength, hashvalue); - - e = h->table[index]; - parent = NULL; - while (e != NULL) { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { - itr->index = index; - itr->e = e; - itr->parent = parent; - itr->h = h; - return -1; - } - parent = e; - e = e->next; - } - return 0; -} - -int arg_hashtable_change(arg_hashtable_t* h, void* k, void* v) { - struct arg_hashtable_entry* e; - unsigned int hashvalue; - unsigned int index; - - hashvalue = enhanced_hash(h, k); - index = index_for(h->tablelength, hashvalue); - e = h->table[index]; - while (e != NULL) { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { - xfree(e->v); - e->v = v; - return -1; - } - e = e->next; - } - return 0; -} -/******************************************************************************* - * arg_dstr: Implements the dynamic string utilities - * - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include -#include -#include - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4996) -#endif - -#define START_VSNBUFF 16 - -/* - * This dynamic string module is adapted from TclResult.c in the Tcl library. - * Here is the copyright notice from the library: - * - * This software is copyrighted by the Regents of the University of - * California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState - * Corporation and other parties. The following terms apply to all files - * associated with the software unless explicitly disclaimed in - * individual files. - * - * The authors hereby grant permission to use, copy, modify, distribute, - * and license this software and its documentation for any purpose, provided - * that existing copyright notices are retained in all copies and that this - * notice is included verbatim in any distributions. No written agreement, - * license, or royalty fee is required for any of the authorized uses. - * Modifications to this software may be copyrighted by their authors - * and need not follow the licensing terms described here, provided that - * the new terms are clearly indicated on the first page of each file where - * they apply. - * - * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY - * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY - * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE - * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE - * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR - * MODIFICATIONS. - * - * GOVERNMENT USE: If you are acquiring this software on behalf of the - * U.S. government, the Government shall have only "Restricted Rights" - * in the software and related documentation as defined in the Federal - * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you - * are acquiring the software on behalf of the Department of Defense, the - * software shall be classified as "Commercial Computer Software" and the - * Government shall have only "Restricted Rights" as defined in Clause - * 252.227-7014 (b) (3) of DFARs. Notwithstanding the foregoing, the - * authors grant the U.S. Government and others acting in its behalf - * permission to use and distribute the software in accordance with the - * terms specified in this license. - */ - -typedef struct _internal_arg_dstr { - char* data; - arg_dstr_freefn* free_proc; - char sbuf[ARG_DSTR_SIZE + 1]; - char* append_data; - int append_data_size; - int append_used; -} _internal_arg_dstr_t; - -static void setup_append_buf(arg_dstr_t res, int newSpace); - -arg_dstr_t arg_dstr_create(void) { - _internal_arg_dstr_t* h = (_internal_arg_dstr_t*)xmalloc(sizeof(_internal_arg_dstr_t)); - memset(h, 0, sizeof(_internal_arg_dstr_t)); - h->sbuf[0] = 0; - h->data = h->sbuf; - h->free_proc = ARG_DSTR_STATIC; - return h; -} - -void arg_dstr_destroy(arg_dstr_t ds) { - if (ds == NULL) - return; - - arg_dstr_reset(ds); - xfree(ds); - return; -} - -void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc) { - int length; - register arg_dstr_freefn* old_free_proc = ds->free_proc; - char* old_result = ds->data; - - if (str == NULL) { - ds->sbuf[0] = 0; - ds->data = ds->sbuf; - ds->free_proc = ARG_DSTR_STATIC; - } else if (free_proc == ARG_DSTR_VOLATILE) { - length = (int)strlen(str); - if (length > ARG_DSTR_SIZE) { - ds->data = (char*)xmalloc((unsigned)length + 1); - ds->free_proc = ARG_DSTR_DYNAMIC; - } else { - ds->data = ds->sbuf; - ds->free_proc = ARG_DSTR_STATIC; - } - strcpy(ds->data, str); - } else { - ds->data = str; - ds->free_proc = free_proc; - } - - /* - * If the old result was dynamically-allocated, free it up. Do it here, - * rather than at the beginning, in case the new result value was part of - * the old result value. - */ - - if ((old_free_proc != 0) && (old_result != ds->data)) { - if (old_free_proc == ARG_DSTR_DYNAMIC) { - xfree(old_result); - } else { - (*old_free_proc)(old_result); - } - } - - if ((ds->append_data != NULL) && (ds->append_data_size > 0)) { - xfree(ds->append_data); - ds->append_data = NULL; - ds->append_data_size = 0; - } -} - -char* arg_dstr_cstr(arg_dstr_t ds) /* Interpreter whose result to return. */ -{ - return ds->data; -} - -void arg_dstr_cat(arg_dstr_t ds, const char* str) { - setup_append_buf(ds, (int)strlen(str) + 1); - memcpy(ds->data + strlen(ds->data), str, strlen(str)); -} - -void arg_dstr_catc(arg_dstr_t ds, char c) { - setup_append_buf(ds, 2); - memcpy(ds->data + strlen(ds->data), &c, 1); -} - -/* - * The logic of the `arg_dstr_catf` function is adapted from the `bformat` - * function in The Better String Library by Paul Hsieh. Here is the copyright - * notice from the library: - * - * Copyright (c) 2014, Paul Hsieh - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of bstrlib nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...) { - va_list arglist; - char* buff; - int n, r; - size_t slen; - - if (fmt == NULL) - return; - - /* Since the length is not determinable beforehand, a search is - performed using the truncating "vsnprintf" call (to avoid buffer - overflows) on increasing potential sizes for the output result. */ - - if ((n = (int)(2 * strlen(fmt))) < START_VSNBUFF) - n = START_VSNBUFF; - - buff = (char*)xmalloc((size_t)(n + 2)); - memset(buff, 0, (size_t)(n + 2)); - - for (;;) { - va_start(arglist, fmt); - r = vsnprintf(buff, (size_t)(n + 1), fmt, arglist); - va_end(arglist); - - slen = strlen(buff); - if (slen < (size_t)n) - break; - - if (r > n) - n = r; - else - n += n; - - xfree(buff); - buff = (char*)xmalloc((size_t)(n + 2)); - memset(buff, 0, (size_t)(n + 2)); - } - - arg_dstr_cat(ds, buff); - xfree(buff); -} - -static void setup_append_buf(arg_dstr_t ds, int new_space) { - int total_space; - - /* - * Make the append buffer larger, if that's necessary, then copy the - * data into the append buffer and make the append buffer the official - * data. - */ - if (ds->data != ds->append_data) { - /* - * If the buffer is too big, then free it up so we go back to a - * smaller buffer. This avoids tying up memory forever after a large - * operation. - */ - if (ds->append_data_size > 500) { - xfree(ds->append_data); - ds->append_data = NULL; - ds->append_data_size = 0; - } - ds->append_used = (int)strlen(ds->data); - } else if (ds->data[ds->append_used] != 0) { - /* - * Most likely someone has modified a result created by - * arg_dstr_cat et al. so that it has a different size. Just - * recompute the size. - */ - ds->append_used = (int)strlen(ds->data); - } - - total_space = new_space + ds->append_used; - if (total_space >= ds->append_data_size) { - char* newbuf; - - if (total_space < 100) { - total_space = 200; - } else { - total_space *= 2; - } - newbuf = (char*)xmalloc((unsigned)total_space); - memset(newbuf, 0, (size_t)total_space); - strcpy(newbuf, ds->data); - if (ds->append_data != NULL) { - xfree(ds->append_data); - } - ds->append_data = newbuf; - ds->append_data_size = total_space; - } else if (ds->data != ds->append_data) { - strcpy(ds->append_data, ds->data); - } - - arg_dstr_free(ds); - ds->data = ds->append_data; -} - -void arg_dstr_free(arg_dstr_t ds) { - if (ds->free_proc != NULL) { - if (ds->free_proc == ARG_DSTR_DYNAMIC) { - xfree(ds->data); - } else { - (*ds->free_proc)(ds->data); - } - ds->free_proc = NULL; - } -} - -void arg_dstr_reset(arg_dstr_t ds) { - arg_dstr_free(ds); - if ((ds->append_data != NULL) && (ds->append_data_size > 0)) { - xfree(ds->append_data); - ds->append_data = NULL; - ds->append_data_size = 0; - } - - ds->data = ds->sbuf; - ds->sbuf[0] = 0; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif -/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ -/* $FreeBSD$ */ - -/*- - * SPDX-License-Identifier: BSD-2-Clause-NetBSD - * - * Copyright (c) 2000 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Dieter Baron and Thomas Klausner. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#if ARG_REPLACE_GETOPT == 1 - -#ifndef _GETOPT_H_ -#define _GETOPT_H_ - -/* - * GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension. - * getopt() is declared here too for GNU programs. - */ -#define no_argument 0 -#define required_argument 1 -#define optional_argument 2 - -struct option { - /* name of long option */ - const char *name; - /* - * one of no_argument, required_argument, and optional_argument: - * whether option takes an argument - */ - int has_arg; - /* if not NULL, set *flag to val when option found */ - int *flag; - /* if flag not NULL, value to set *flag to; else return value */ - int val; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -int getopt_long(int, char * const *, const char *, - const struct option *, int *); -int getopt_long_only(int, char * const *, const char *, - const struct option *, int *); -#ifndef _GETOPT_DECLARED -#define _GETOPT_DECLARED -int getopt(int, char * const [], const char *); - -extern char *optarg; /* getopt(3) external variables */ -extern int optind, opterr, optopt; -#endif -#ifndef _OPTRESET_DECLARED -#define _OPTRESET_DECLARED -extern int optreset; /* getopt(3) external variable */ -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* !_GETOPT_H_ */ - -#endif /* ARG_REPLACE_GETOPT == 1 */ -/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */ -/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ - -/* - * Copyright (c) 2002 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Sponsored in part by the Defense Advanced Research Projects - * Agency (DARPA) and Air Force Research Laboratory, Air Force - * Materiel Command, USAF, under agreement number F39502-99-1-0512. - */ -/*- - * Copyright (c) 2000 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Dieter Baron and Thomas Klausner. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "argtable3.h" - -#if ARG_REPLACE_GETOPT == 1 - -#ifndef ARG_AMALGAMATION -#include "arg_getopt.h" -#endif - -#include -#include -#include - -#define GNU_COMPATIBLE /* Be more compatible, configure's use us! */ - -int opterr = 1; /* if error message should be printed */ -int optind = 1; /* index into parent argv vector */ -int optopt = '?'; /* character checked for validity */ -int optreset; /* reset getopt */ -char *optarg; /* argument associated with option */ - -#define PRINT_ERROR ((opterr) && (*options != ':')) - -#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ -#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ -#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ - -/* return values */ -#define BADCH (int)'?' -#define BADARG ((*options == ':') ? (int)':' : (int)'?') -#define INORDER (int)1 - -#define EMSG "" - -#ifdef GNU_COMPATIBLE -#define NO_PREFIX (-1) -#define D_PREFIX 0 -#define DD_PREFIX 1 -#define W_PREFIX 2 -#endif - -static int getopt_internal(int, char * const *, const char *, - const struct option *, int *, int); -static int parse_long_options(char * const *, const char *, - const struct option *, int *, int, int); -static int gcd(int, int); -static void permute_args(int, int, int, char * const *); - -static char *place = EMSG; /* option letter processing */ - -/* XXX: set optreset to 1 rather than these two */ -static int nonopt_start = -1; /* first non option argument (for permute) */ -static int nonopt_end = -1; /* first option after non options (for permute) */ - -/* Error messages */ -static const char recargchar[] = "option requires an argument -- %c"; -static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */ -#ifdef GNU_COMPATIBLE -static int dash_prefix = NO_PREFIX; -static const char gnuoptchar[] = "invalid option -- %c"; - -static const char recargstring[] = "option `%s%s' requires an argument"; -static const char ambig[] = "option `%s%.*s' is ambiguous"; -static const char noarg[] = "option `%s%.*s' doesn't allow an argument"; -static const char illoptstring[] = "unrecognized option `%s%s'"; -#else -static const char recargstring[] = "option requires an argument -- %s"; -static const char ambig[] = "ambiguous option -- %.*s"; -static const char noarg[] = "option doesn't take an argument -- %.*s"; -static const char illoptstring[] = "unknown option -- %s"; -#endif - -#ifdef _WIN32 - -/* - * Windows needs warnx(). We change the definition though: - * 1. (another) global is defined, opterrmsg, which holds the error message - * 2. errors are always printed out on stderr w/o the program name - * Note that opterrmsg always gets set no matter what opterr is set to. The - * error message will not be printed if opterr is 0 as usual. - */ - -#include -#include - -#define MAX_OPTERRMSG_SIZE 128 - -extern char opterrmsg[MAX_OPTERRMSG_SIZE]; -char opterrmsg[MAX_OPTERRMSG_SIZE]; /* buffer for the last error message */ - -static void warnx(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - - /* - * Make sure opterrmsg is always zero-terminated despite the _vsnprintf() - * implementation specifics and manually suppress the warning. - */ - memset(opterrmsg, 0, sizeof(opterrmsg)); - if (fmt != NULL) -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - _vsnprintf_s(opterrmsg, sizeof(opterrmsg), sizeof(opterrmsg) - 1, fmt, ap); -#else - _vsnprintf(opterrmsg, sizeof(opterrmsg) - 1, fmt, ap); -#endif - - va_end(ap); - -#ifdef _MSC_VER -#pragma warning(suppress : 6053) -#endif - fprintf(stderr, "%s\n", opterrmsg); -} - -#else -#include -#endif /*_WIN32*/ -/* - * Compute the greatest common divisor of a and b. - */ -static int -gcd(int a, int b) -{ - int c; - - c = a % b; - while (c != 0) { - a = b; - b = c; - c = a % b; - } - - return (b); -} - -/* - * Exchange the block from nonopt_start to nonopt_end with the block - * from nonopt_end to opt_end (keeping the same order of arguments - * in each block). - */ -static void -permute_args(int panonopt_start, int panonopt_end, int opt_end, - char * const *nargv) -{ - int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; - char *swap; - - /* - * compute lengths of blocks and number and size of cycles - */ - nnonopts = panonopt_end - panonopt_start; - nopts = opt_end - panonopt_end; - ncycle = gcd(nnonopts, nopts); - cyclelen = (opt_end - panonopt_start) / ncycle; - - for (i = 0; i < ncycle; i++) { - cstart = panonopt_end+i; - pos = cstart; - for (j = 0; j < cyclelen; j++) { - if (pos >= panonopt_end) - pos -= nnonopts; - else - pos += nopts; - swap = nargv[pos]; - /* LINTED const cast */ - ((char **) nargv)[pos] = nargv[cstart]; - /* LINTED const cast */ - ((char **)nargv)[cstart] = swap; - } - } -} - -/* - * parse_long_options -- - * Parse long options in argc/argv argument vector. - * Returns -1 if short_too is set and the option does not match long_options. - */ -static int -parse_long_options(char * const *nargv, const char *options, - const struct option *long_options, int *idx, int short_too, int flags) -{ - char *current_argv, *has_equal; -#ifdef GNU_COMPATIBLE - char *current_dash; -#endif - size_t current_argv_len; - int i, match, exact_match, second_partial_match; - - current_argv = place; -#ifdef GNU_COMPATIBLE - switch (dash_prefix) { - case D_PREFIX: - current_dash = "-"; - break; - case DD_PREFIX: - current_dash = "--"; - break; - case W_PREFIX: - current_dash = "-W "; - break; - default: - current_dash = ""; - break; - } -#endif - match = -1; - exact_match = 0; - second_partial_match = 0; - - optind++; - - if ((has_equal = strchr(current_argv, '=')) != NULL) { - /* argument found (--option=arg) */ - current_argv_len = (size_t)(has_equal - current_argv); - has_equal++; - } else - current_argv_len = strlen(current_argv); - - for (i = 0; long_options[i].name; i++) { - /* find matching long option */ - if (strncmp(current_argv, long_options[i].name, - current_argv_len)) - continue; - - if (strlen(long_options[i].name) == current_argv_len) { - /* exact match */ - match = i; - exact_match = 1; - break; - } - /* - * If this is a known short option, don't allow - * a partial match of a single character. - */ - if (short_too && current_argv_len == 1) - continue; - - if (match == -1) /* first partial match */ - match = i; - else if ((flags & FLAG_LONGONLY) || - long_options[i].has_arg != - long_options[match].has_arg || - long_options[i].flag != long_options[match].flag || - long_options[i].val != long_options[match].val) - second_partial_match = 1; - } - if (!exact_match && second_partial_match) { - /* ambiguous abbreviation */ - if (PRINT_ERROR) - warnx(ambig, -#ifdef GNU_COMPATIBLE - current_dash, -#endif - (int)current_argv_len, - current_argv); - optopt = 0; - return (BADCH); - } - if (match != -1) { /* option found */ - if (long_options[match].has_arg == no_argument - && has_equal) { - if (PRINT_ERROR) - warnx(noarg, -#ifdef GNU_COMPATIBLE - current_dash, -#endif - (int)current_argv_len, - current_argv); - /* - * XXX: GNU sets optopt to val regardless of flag - */ - if (long_options[match].flag == NULL) - optopt = long_options[match].val; - else - optopt = 0; -#ifdef GNU_COMPATIBLE - return (BADCH); -#else - return (BADARG); -#endif - } - if (long_options[match].has_arg == required_argument || - long_options[match].has_arg == optional_argument) { - if (has_equal) - optarg = has_equal; - else if (long_options[match].has_arg == - required_argument) { - /* - * optional argument doesn't use next nargv - */ - optarg = nargv[optind++]; - } - } - if ((long_options[match].has_arg == required_argument) - && (optarg == NULL)) { - /* - * Missing argument; leading ':' indicates no error - * should be generated. - */ - if (PRINT_ERROR) - warnx(recargstring, -#ifdef GNU_COMPATIBLE - current_dash, -#endif - current_argv); - /* - * XXX: GNU sets optopt to val regardless of flag - */ - if (long_options[match].flag == NULL) - optopt = long_options[match].val; - else - optopt = 0; - --optind; - return (BADARG); - } - } else { /* unknown option */ - if (short_too) { - --optind; - return (-1); - } - if (PRINT_ERROR) - warnx(illoptstring, -#ifdef GNU_COMPATIBLE - current_dash, -#endif - current_argv); - optopt = 0; - return (BADCH); - } - if (idx) - *idx = match; - if (long_options[match].flag) { - *long_options[match].flag = long_options[match].val; - return (0); - } else - return (long_options[match].val); -} - -/* - * getopt_internal -- - * Parse argc/argv argument vector. Called by user level routines. - */ -static int -getopt_internal(int nargc, char * const *nargv, const char *options, - const struct option *long_options, int *idx, int flags) -{ - char *oli; /* option letter list index */ - int optchar, short_too; - static int posixly_correct = -1; - - if (options == NULL) - return (-1); - - /* - * XXX Some GNU programs (like cvs) set optind to 0 instead of - * XXX using optreset. Work around this braindamage. - */ - if (optind == 0) - optind = optreset = 1; - - /* - * Disable GNU extensions if POSIXLY_CORRECT is set or options - * string begins with a '+'. - */ - if (posixly_correct == -1 || optreset) { -#if defined(_WIN32) && ((defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__))) - size_t requiredSize; - getenv_s(&requiredSize, NULL, 0, "POSIXLY_CORRECT"); - posixly_correct = requiredSize != 0; -#else - posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); -#endif - } - - if (*options == '-') - flags |= FLAG_ALLARGS; - else if (posixly_correct || *options == '+') - flags &= ~FLAG_PERMUTE; - if (*options == '+' || *options == '-') - options++; - - optarg = NULL; - if (optreset) - nonopt_start = nonopt_end = -1; -start: - if (optreset || !*place) { /* update scanning pointer */ - optreset = 0; - if (optind >= nargc) { /* end of argument vector */ - place = EMSG; - if (nonopt_end != -1) { - /* do permutation, if we have to */ - permute_args(nonopt_start, nonopt_end, - optind, nargv); - optind -= nonopt_end - nonopt_start; - } - else if (nonopt_start != -1) { - /* - * If we skipped non-options, set optind - * to the first of them. - */ - optind = nonopt_start; - } - nonopt_start = nonopt_end = -1; - return (-1); - } - if (*(place = nargv[optind]) != '-' || -#ifdef GNU_COMPATIBLE - place[1] == '\0') { -#else - (place[1] == '\0' && strchr(options, '-') == NULL)) { -#endif - place = EMSG; /* found non-option */ - if (flags & FLAG_ALLARGS) { - /* - * GNU extension: - * return non-option as argument to option 1 - */ - optarg = nargv[optind++]; - return (INORDER); - } - if (!(flags & FLAG_PERMUTE)) { - /* - * If no permutation wanted, stop parsing - * at first non-option. - */ - return (-1); - } - /* do permutation */ - if (nonopt_start == -1) - nonopt_start = optind; - else if (nonopt_end != -1) { - permute_args(nonopt_start, nonopt_end, - optind, nargv); - nonopt_start = optind - - (nonopt_end - nonopt_start); - nonopt_end = -1; - } - optind++; - /* process next argument */ - goto start; - } - if (nonopt_start != -1 && nonopt_end == -1) - nonopt_end = optind; - - /* - * If we have "-" do nothing, if "--" we are done. - */ - if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { - optind++; - place = EMSG; - /* - * We found an option (--), so if we skipped - * non-options, we have to permute. - */ - if (nonopt_end != -1) { - permute_args(nonopt_start, nonopt_end, - optind, nargv); - optind -= nonopt_end - nonopt_start; - } - nonopt_start = nonopt_end = -1; - return (-1); - } - } - - /* - * Check long options if: - * 1) we were passed some - * 2) the arg is not just "-" - * 3) either the arg starts with -- we are getopt_long_only() - */ - if (long_options != NULL && place != nargv[optind] && - (*place == '-' || (flags & FLAG_LONGONLY))) { - short_too = 0; -#ifdef GNU_COMPATIBLE - dash_prefix = D_PREFIX; -#endif - if (*place == '-') { - place++; /* --foo long option */ - if (*place == '\0') - return (BADARG); /* malformed option */ -#ifdef GNU_COMPATIBLE - dash_prefix = DD_PREFIX; -#endif - } else if (*place != ':' && strchr(options, *place) != NULL) - short_too = 1; /* could be short option too */ - - optchar = parse_long_options(nargv, options, long_options, - idx, short_too, flags); - if (optchar != -1) { - place = EMSG; - return (optchar); - } - } - - if ((optchar = (int)*place++) == (int)':' || - (optchar == (int)'-' && *place != '\0') || - (oli = strchr(options, optchar)) == NULL) { - /* - * If the user specified "-" and '-' isn't listed in - * options, return -1 (non-option) as per POSIX. - * Otherwise, it is an unknown option character (or ':'). - */ - if (optchar == (int)'-' && *place == '\0') - return (-1); - if (!*place) - ++optind; -#ifdef GNU_COMPATIBLE - if (PRINT_ERROR) - warnx(posixly_correct ? illoptchar : gnuoptchar, - optchar); -#else - if (PRINT_ERROR) - warnx(illoptchar, optchar); -#endif - optopt = optchar; - return (BADCH); - } - if (long_options != NULL && optchar == 'W' && oli[1] == ';') { - /* -W long-option */ - if (*place) /* no space */ - /* NOTHING */; - else if (++optind >= nargc) { /* no arg */ - place = EMSG; - if (PRINT_ERROR) - warnx(recargchar, optchar); - optopt = optchar; - return (BADARG); - } else /* white space */ - place = nargv[optind]; -#ifdef GNU_COMPATIBLE - dash_prefix = W_PREFIX; -#endif - optchar = parse_long_options(nargv, options, long_options, - idx, 0, flags); - place = EMSG; - return (optchar); - } - if (*++oli != ':') { /* doesn't take argument */ - if (!*place) - ++optind; - } else { /* takes (optional) argument */ - optarg = NULL; - if (*place) /* no white space */ - optarg = place; - else if (oli[1] != ':') { /* arg not optional */ - if (++optind >= nargc) { /* no arg */ - place = EMSG; - if (PRINT_ERROR) - warnx(recargchar, optchar); - optopt = optchar; - return (BADARG); - } else - optarg = nargv[optind]; - } - place = EMSG; - ++optind; - } - /* dump back option letter */ - return (optchar); -} - -/* - * getopt -- - * Parse argc/argv argument vector. - * - * [eventually this will replace the BSD getopt] - */ -int -getopt(int nargc, char * const *nargv, const char *options) -{ - - /* - * We don't pass FLAG_PERMUTE to getopt_internal() since - * the BSD getopt(3) (unlike GNU) has never done this. - * - * Furthermore, since many privileged programs call getopt() - * before dropping privileges it makes sense to keep things - * as simple (and bug-free) as possible. - */ - return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); -} - -/* - * getopt_long -- - * Parse argc/argv argument vector. - */ -int -getopt_long(int nargc, char * const *nargv, const char *options, - const struct option *long_options, int *idx) -{ - - return (getopt_internal(nargc, nargv, options, long_options, idx, - FLAG_PERMUTE)); -} - -/* - * getopt_long_only -- - * Parse argc/argv argument vector. - */ -int -getopt_long_only(int nargc, char * const *nargv, const char *options, - const struct option *long_options, int *idx) -{ - - return (getopt_internal(nargc, nargv, options, long_options, idx, - FLAG_PERMUTE|FLAG_LONGONLY)); -} - -#endif /* ARG_REPLACE_GETOPT == 1 */ -/******************************************************************************* - * arg_date: Implements the date command-line option - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include -#include - -char* arg_strptime(const char* buf, const char* fmt, struct tm* tm); - -static void arg_date_resetfn(struct arg_date* parent) { - ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); - parent->count = 0; -} - -static int arg_date_scanfn(struct arg_date* parent, const char* argval) { - int errorcode = 0; - - if (parent->count == parent->hdr.maxcount) { - errorcode = ARG_ERR_MAXCOUNT; - } else if (!argval) { - /* no argument value was given, leave parent->tmval[] unaltered but still count it */ - parent->count++; - } else { - const char* pend; - struct tm tm = parent->tmval[parent->count]; - - /* parse the given argument value, store result in parent->tmval[] */ - pend = arg_strptime(argval, parent->format, &tm); - if (pend && pend[0] == '\0') - parent->tmval[parent->count++] = tm; - else - errorcode = ARG_ERR_BADDATE; - } - - ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static int arg_date_checkfn(struct arg_date* parent) { - int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; - - ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static void arg_date_errorfn(struct arg_date* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { - const char* shortopts = parent->hdr.shortopts; - const char* longopts = parent->hdr.longopts; - const char* datatype = parent->hdr.datatype; - - /* make argval NULL safe */ - argval = argval ? argval : ""; - - arg_dstr_catf(ds, "%s: ", progname); - switch (errorcode) { - case ARG_ERR_MINCOUNT: - arg_dstr_cat(ds, "missing option "); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - break; - - case ARG_ERR_MAXCOUNT: - arg_dstr_cat(ds, "excess option "); - arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); - break; - - case ARG_ERR_BADDATE: { - struct tm tm; - char buff[200]; - - arg_dstr_catf(ds, "illegal timestamp format \"%s\"\n", argval); - memset(&tm, 0, sizeof(tm)); - arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm); - strftime(buff, sizeof(buff), parent->format, &tm); - arg_dstr_catf(ds, "correct format is \"%s\"\n", buff); - break; - } - } -} - -struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) { - return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary); -} - -struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) { - return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary); -} - -struct arg_date* -arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary) { - size_t nbytes; - struct arg_date* result; - - /* foolproof things by ensuring maxcount is not less than mincount */ - maxcount = (maxcount < mincount) ? mincount : maxcount; - - /* default time format is the national date format for the locale */ - if (!format) - format = "%x"; - - nbytes = sizeof(struct arg_date) /* storage for struct arg_date */ - + (size_t)maxcount * sizeof(struct tm); /* storage for tmval[maxcount] array */ - - /* allocate storage for the arg_date struct + tmval[] array. */ - /* we use calloc because we want the tmval[] array zero filled. */ - result = (struct arg_date*)xcalloc(1, nbytes); - - /* init the arg_hdr struct */ - result->hdr.flag = ARG_HASVALUE; - result->hdr.shortopts = shortopts; - result->hdr.longopts = longopts; - result->hdr.datatype = datatype ? datatype : format; - result->hdr.glossary = glossary; - result->hdr.mincount = mincount; - result->hdr.maxcount = maxcount; - result->hdr.parent = result; - result->hdr.resetfn = (arg_resetfn*)arg_date_resetfn; - result->hdr.scanfn = (arg_scanfn*)arg_date_scanfn; - result->hdr.checkfn = (arg_checkfn*)arg_date_checkfn; - result->hdr.errorfn = (arg_errorfn*)arg_date_errorfn; - - /* store the tmval[maxcount] array immediately after the arg_date struct */ - result->tmval = (struct tm*)(result + 1); - - /* init the remaining arg_date member variables */ - result->count = 0; - result->format = format; - - ARG_TRACE(("arg_daten() returns %p\n", result)); - return result; -} - -/*- - * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code was contributed to The NetBSD Foundation by Klaus Klein. - * Heavily optimised by David Laight - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -/* - * We do not implement alternate representations. However, we always - * check whether a given modifier is allowed for a certain conversion. - */ -#define ALT_E 0x01 -#define ALT_O 0x02 -#define LEGAL_ALT(x) \ - { \ - if (alt_format & ~(x)) \ - return (0); \ - } -#define TM_YEAR_BASE (1900) - -static int conv_num(const char**, int*, int, int); - -static const char* day[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; - -static const char* abday[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - -static const char* mon[12] = {"January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December"}; - -static const char* abmon[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - -static const char* am_pm[2] = {"AM", "PM"}; - -static int arg_strcasecmp(const char* s1, const char* s2) { - const unsigned char* us1 = (const unsigned char*)s1; - const unsigned char* us2 = (const unsigned char*)s2; - while (tolower(*us1) == tolower(*us2++)) - if (*us1++ == '\0') - return 0; - - return tolower(*us1) - tolower(*--us2); -} - -static int arg_strncasecmp(const char* s1, const char* s2, size_t n) { - if (n != 0) { - const unsigned char* us1 = (const unsigned char*)s1; - const unsigned char* us2 = (const unsigned char*)s2; - do { - if (tolower(*us1) != tolower(*us2++)) - return tolower(*us1) - tolower(*--us2); - - if (*us1++ == '\0') - break; - } while (--n != 0); - } - - return 0; -} - -char* arg_strptime(const char* buf, const char* fmt, struct tm* tm) { - char c; - const char* bp; - size_t len = 0; - int alt_format, i, split_year = 0; - - bp = buf; - - while ((c = *fmt) != '\0') { - /* Clear `alternate' modifier prior to new conversion. */ - alt_format = 0; - - /* Eat up white-space. */ - if (isspace(c)) { - while (isspace((int)(*bp))) - bp++; - - fmt++; - continue; - } - - if ((c = *fmt++) != '%') - goto literal; - - again: - switch (c = *fmt++) { - case '%': /* "%%" is converted to "%". */ - literal: - if (c != *bp++) - return (0); - break; - - /* - * "Alternative" modifiers. Just set the appropriate flag - * and start over again. - */ - case 'E': /* "%E?" alternative conversion modifier. */ - LEGAL_ALT(0); - alt_format |= ALT_E; - goto again; - - case 'O': /* "%O?" alternative conversion modifier. */ - LEGAL_ALT(0); - alt_format |= ALT_O; - goto again; - - /* - * "Complex" conversion rules, implemented through recursion. - */ - case 'c': /* Date and time, using the locale's format. */ - LEGAL_ALT(ALT_E); - bp = arg_strptime(bp, "%x %X", tm); - if (!bp) - return (0); - break; - - case 'D': /* The date as "%m/%d/%y". */ - LEGAL_ALT(0); - bp = arg_strptime(bp, "%m/%d/%y", tm); - if (!bp) - return (0); - break; - - case 'R': /* The time as "%H:%M". */ - LEGAL_ALT(0); - bp = arg_strptime(bp, "%H:%M", tm); - if (!bp) - return (0); - break; - - case 'r': /* The time in 12-hour clock representation. */ - LEGAL_ALT(0); - bp = arg_strptime(bp, "%I:%M:%S %p", tm); - if (!bp) - return (0); - break; - - case 'T': /* The time as "%H:%M:%S". */ - LEGAL_ALT(0); - bp = arg_strptime(bp, "%H:%M:%S", tm); - if (!bp) - return (0); - break; - - case 'X': /* The time, using the locale's format. */ - LEGAL_ALT(ALT_E); - bp = arg_strptime(bp, "%H:%M:%S", tm); - if (!bp) - return (0); - break; - - case 'x': /* The date, using the locale's format. */ - LEGAL_ALT(ALT_E); - bp = arg_strptime(bp, "%m/%d/%y", tm); - if (!bp) - return (0); - break; - - /* - * "Elementary" conversion rules. - */ - case 'A': /* The day of week, using the locale's form. */ - case 'a': - LEGAL_ALT(0); - for (i = 0; i < 7; i++) { - /* Full name. */ - len = strlen(day[i]); - if (arg_strncasecmp(day[i], bp, len) == 0) - break; - - /* Abbreviated name. */ - len = strlen(abday[i]); - if (arg_strncasecmp(abday[i], bp, len) == 0) - break; - } - - /* Nothing matched. */ - if (i == 7) - return (0); - - tm->tm_wday = i; - bp += len; - break; - - case 'B': /* The month, using the locale's form. */ - case 'b': - case 'h': - LEGAL_ALT(0); - for (i = 0; i < 12; i++) { - /* Full name. */ - len = strlen(mon[i]); - if (arg_strncasecmp(mon[i], bp, len) == 0) - break; - - /* Abbreviated name. */ - len = strlen(abmon[i]); - if (arg_strncasecmp(abmon[i], bp, len) == 0) - break; - } - - /* Nothing matched. */ - if (i == 12) - return (0); - - tm->tm_mon = i; - bp += len; - break; - - case 'C': /* The century number. */ - LEGAL_ALT(ALT_E); - if (!(conv_num(&bp, &i, 0, 99))) - return (0); - - if (split_year) { - tm->tm_year = (tm->tm_year % 100) + (i * 100); - } else { - tm->tm_year = i * 100; - split_year = 1; - } - break; - - case 'd': /* The day of month. */ - case 'e': - LEGAL_ALT(ALT_O); - if (!(conv_num(&bp, &tm->tm_mday, 1, 31))) - return (0); - break; - - case 'k': /* The hour (24-hour clock representation). */ - LEGAL_ALT(0); - /* FALLTHROUGH */ - case 'H': - LEGAL_ALT(ALT_O); - if (!(conv_num(&bp, &tm->tm_hour, 0, 23))) - return (0); - break; - - case 'l': /* The hour (12-hour clock representation). */ - LEGAL_ALT(0); - /* FALLTHROUGH */ - case 'I': - LEGAL_ALT(ALT_O); - if (!(conv_num(&bp, &tm->tm_hour, 1, 12))) - return (0); - if (tm->tm_hour == 12) - tm->tm_hour = 0; - break; - - case 'j': /* The day of year. */ - LEGAL_ALT(0); - if (!(conv_num(&bp, &i, 1, 366))) - return (0); - tm->tm_yday = i - 1; - break; - - case 'M': /* The minute. */ - LEGAL_ALT(ALT_O); - if (!(conv_num(&bp, &tm->tm_min, 0, 59))) - return (0); - break; - - case 'm': /* The month. */ - LEGAL_ALT(ALT_O); - if (!(conv_num(&bp, &i, 1, 12))) - return (0); - tm->tm_mon = i - 1; - break; - - case 'p': /* The locale's equivalent of AM/PM. */ - LEGAL_ALT(0); - /* AM? */ - if (arg_strcasecmp(am_pm[0], bp) == 0) { - if (tm->tm_hour > 11) - return (0); - - bp += strlen(am_pm[0]); - break; - } - /* PM? */ - else if (arg_strcasecmp(am_pm[1], bp) == 0) { - if (tm->tm_hour > 11) - return (0); - - tm->tm_hour += 12; - bp += strlen(am_pm[1]); - break; - } - - /* Nothing matched. */ - return (0); - - case 'S': /* The seconds. */ - LEGAL_ALT(ALT_O); - if (!(conv_num(&bp, &tm->tm_sec, 0, 61))) - return (0); - break; - - case 'U': /* The week of year, beginning on sunday. */ - case 'W': /* The week of year, beginning on monday. */ - LEGAL_ALT(ALT_O); - /* - * XXX This is bogus, as we can not assume any valid - * information present in the tm structure at this - * point to calculate a real value, so just check the - * range for now. - */ - if (!(conv_num(&bp, &i, 0, 53))) - return (0); - break; - - case 'w': /* The day of week, beginning on sunday. */ - LEGAL_ALT(ALT_O); - if (!(conv_num(&bp, &tm->tm_wday, 0, 6))) - return (0); - break; - - case 'Y': /* The year. */ - LEGAL_ALT(ALT_E); - if (!(conv_num(&bp, &i, 0, 9999))) - return (0); - - tm->tm_year = i - TM_YEAR_BASE; - break; - - case 'y': /* The year within 100 years of the epoch. */ - LEGAL_ALT(ALT_E | ALT_O); - if (!(conv_num(&bp, &i, 0, 99))) - return (0); - - if (split_year) { - tm->tm_year = ((tm->tm_year / 100) * 100) + i; - break; - } - split_year = 1; - if (i <= 68) - tm->tm_year = i + 2000 - TM_YEAR_BASE; - else - tm->tm_year = i + 1900 - TM_YEAR_BASE; - break; - - /* - * Miscellaneous conversions. - */ - case 'n': /* Any kind of white-space. */ - case 't': - LEGAL_ALT(0); - while (isspace((int)(*bp))) - bp++; - break; - - default: /* Unknown/unsupported conversion. */ - return (0); - } - } - - /* LINTED functional specification */ - return ((char*)bp); -} - -static int conv_num(const char** buf, int* dest, int llim, int ulim) { - int result = 0; - - /* The limit also determines the number of valid digits. */ - int rulim = ulim; - - if (**buf < '0' || **buf > '9') - return (0); - - do { - result *= 10; - result += *(*buf)++ - '0'; - rulim /= 10; - } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); - - if (result < llim || result > ulim) - return (0); - - *dest = result; - return (1); -} -/******************************************************************************* - * arg_dbl: Implements the double command-line option - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include - -static void arg_dbl_resetfn(struct arg_dbl* parent) { - ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); - parent->count = 0; -} - -static int arg_dbl_scanfn(struct arg_dbl* parent, const char* argval) { - int errorcode = 0; - - if (parent->count == parent->hdr.maxcount) { - /* maximum number of arguments exceeded */ - errorcode = ARG_ERR_MAXCOUNT; - } else if (!argval) { - /* a valid argument with no argument value was given. */ - /* This happens when an optional argument value was invoked. */ - /* leave parent argument value unaltered but still count the argument. */ - parent->count++; - } else { - double val; - char* end; - - /* extract double from argval into val */ - val = strtod(argval, &end); - - /* if success then store result in parent->dval[] array otherwise return error*/ - if (*end == 0) - parent->dval[parent->count++] = val; - else - errorcode = ARG_ERR_BADDOUBLE; - } - - ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static int arg_dbl_checkfn(struct arg_dbl* parent) { - int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; - - ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static void arg_dbl_errorfn(struct arg_dbl* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { - const char* shortopts = parent->hdr.shortopts; - const char* longopts = parent->hdr.longopts; - const char* datatype = parent->hdr.datatype; - - /* make argval NULL safe */ - argval = argval ? argval : ""; - - arg_dstr_catf(ds, "%s: ", progname); - switch (errorcode) { - case ARG_ERR_MINCOUNT: - arg_dstr_cat(ds, "missing option "); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - break; - - case ARG_ERR_MAXCOUNT: - arg_dstr_cat(ds, "excess option "); - arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); - break; - - case ARG_ERR_BADDOUBLE: - arg_dstr_catf(ds, "invalid argument \"%s\" to option ", argval); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - break; - } -} - -struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { - return arg_dbln(shortopts, longopts, datatype, 0, 1, glossary); -} - -struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { - return arg_dbln(shortopts, longopts, datatype, 1, 1, glossary); -} - -struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { - size_t nbytes; - struct arg_dbl* result; - size_t addr; - size_t rem; - - /* foolproof things by ensuring maxcount is not less than mincount */ - maxcount = (maxcount < mincount) ? mincount : maxcount; - - nbytes = sizeof(struct arg_dbl) /* storage for struct arg_dbl */ - + (size_t)(maxcount + 1) * sizeof(double); /* storage for dval[maxcount] array plus one extra for padding to memory boundary */ - - result = (struct arg_dbl*)xmalloc(nbytes); - - /* init the arg_hdr struct */ - result->hdr.flag = ARG_HASVALUE; - result->hdr.shortopts = shortopts; - result->hdr.longopts = longopts; - result->hdr.datatype = datatype ? datatype : ""; - result->hdr.glossary = glossary; - result->hdr.mincount = mincount; - result->hdr.maxcount = maxcount; - result->hdr.parent = result; - result->hdr.resetfn = (arg_resetfn*)arg_dbl_resetfn; - result->hdr.scanfn = (arg_scanfn*)arg_dbl_scanfn; - result->hdr.checkfn = (arg_checkfn*)arg_dbl_checkfn; - result->hdr.errorfn = (arg_errorfn*)arg_dbl_errorfn; - - /* Store the dval[maxcount] array on the first double boundary that - * immediately follows the arg_dbl struct. We do the memory alignment - * purely for SPARC and Motorola systems. They require floats and - * doubles to be aligned on natural boundaries. - */ - addr = (size_t)(result + 1); - rem = addr % sizeof(double); - result->dval = (double*)(addr + sizeof(double) - rem); - ARG_TRACE(("addr=%p, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem)); - - result->count = 0; - - ARG_TRACE(("arg_dbln() returns %p\n", result)); - return result; -} -/******************************************************************************* - * arg_end: Implements the error handling utilities - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include - -static void arg_end_resetfn(struct arg_end* parent) { - ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); - parent->count = 0; -} - -static void arg_end_errorfn(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname) { - /* suppress unreferenced formal parameter warning */ - (void)parent; - - progname = progname ? progname : ""; - argval = argval ? argval : ""; - - arg_dstr_catf(ds, "%s: ", progname); - switch (error) { - case ARG_ELIMIT: - arg_dstr_cat(ds, "too many errors to display"); - break; - case ARG_EMALLOC: - arg_dstr_cat(ds, "insufficient memory"); - break; - case ARG_ENOMATCH: - arg_dstr_catf(ds, "unexpected argument \"%s\"", argval); - break; - case ARG_EMISSARG: - arg_dstr_catf(ds, "option \"%s\" requires an argument", argval); - break; - case ARG_ELONGOPT: - arg_dstr_catf(ds, "invalid option \"%s\"", argval); - break; - default: - arg_dstr_catf(ds, "invalid option \"-%c\"", error); - break; - } - - arg_dstr_cat(ds, "\n"); -} - -struct arg_end* arg_end(int maxcount) { - size_t nbytes; - struct arg_end* result; - - nbytes = sizeof(struct arg_end) + (size_t)maxcount * sizeof(int) /* storage for int error[maxcount] array*/ - + (size_t)maxcount * sizeof(void*) /* storage for void* parent[maxcount] array */ - + (size_t)maxcount * sizeof(char*); /* storage for char* argval[maxcount] array */ - - result = (struct arg_end*)xmalloc(nbytes); - - /* init the arg_hdr struct */ - result->hdr.flag = ARG_TERMINATOR; - result->hdr.shortopts = NULL; - result->hdr.longopts = NULL; - result->hdr.datatype = NULL; - result->hdr.glossary = NULL; - result->hdr.mincount = 1; - result->hdr.maxcount = maxcount; - result->hdr.parent = result; - result->hdr.resetfn = (arg_resetfn*)arg_end_resetfn; - result->hdr.scanfn = NULL; - result->hdr.checkfn = NULL; - result->hdr.errorfn = (arg_errorfn*)arg_end_errorfn; - - /* store error[maxcount] array immediately after struct arg_end */ - result->error = (int*)(result + 1); - - /* store parent[maxcount] array immediately after error[] array */ - result->parent = (void**)(result->error + maxcount); - - /* store argval[maxcount] array immediately after parent[] array */ - result->argval = (const char**)(result->parent + maxcount); - - ARG_TRACE(("arg_end(%d) returns %p\n", maxcount, result)); - return result; -} - -void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname) { - int i; - ARG_TRACE(("arg_errors()\n")); - for (i = 0; i < end->count; i++) { - struct arg_hdr* errorparent = (struct arg_hdr*)(end->parent[i]); - if (errorparent->errorfn) - errorparent->errorfn(end->parent[i], ds, end->error[i], end->argval[i], progname); - } -} - -void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname) { - arg_dstr_t ds = arg_dstr_create(); - arg_print_errors_ds(ds, end, progname); - fputs(arg_dstr_cstr(ds), fp); - arg_dstr_destroy(ds); -} -/******************************************************************************* - * arg_file: Implements the file command-line option - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include -#include - -#ifdef WIN32 -#define FILESEPARATOR1 '\\' -#define FILESEPARATOR2 '/' -#else -#define FILESEPARATOR1 '/' -#define FILESEPARATOR2 '/' -#endif - -static void arg_file_resetfn(struct arg_file* parent) { - ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); - parent->count = 0; -} - -/* Returns ptr to the base filename within *filename */ -static const char* arg_basename(const char* filename) { - const char *result = NULL, *result1, *result2; - - /* Find the last occurrence of eother file separator character. */ - /* Two alternative file separator chars are supported as legal */ - /* file separators but not both together in the same filename. */ - result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL); - result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL); - - if (result2) - result = result2 + 1; /* using FILESEPARATOR2 (the alternative file separator) */ - - if (result1) - result = result1 + 1; /* using FILESEPARATOR1 (the preferred file separator) */ - - if (!result) - result = filename; /* neither file separator was found so basename is the whole filename */ - - /* special cases of "." and ".." are not considered basenames */ - if (result && (strcmp(".", result) == 0 || strcmp("..", result) == 0)) - result = filename + strlen(filename); - - return result; -} - -/* Returns ptr to the file extension within *basename */ -static const char* arg_extension(const char* basename) { - /* find the last occurrence of '.' in basename */ - const char* result = (basename ? strrchr(basename, '.') : NULL); - - /* if no '.' was found then return pointer to end of basename */ - if (basename && !result) - result = basename + strlen(basename); - - /* special case: basenames with a single leading dot (eg ".foo") are not considered as true extensions */ - if (basename && result == basename) - result = basename + strlen(basename); - - /* special case: empty extensions (eg "foo.","foo..") are not considered as true extensions */ - if (basename && result && strlen(result) == 1) - result = basename + strlen(basename); - - return result; -} - -static int arg_file_scanfn(struct arg_file* parent, const char* argval) { - int errorcode = 0; - - if (parent->count == parent->hdr.maxcount) { - /* maximum number of arguments exceeded */ - errorcode = ARG_ERR_MAXCOUNT; - } else if (!argval) { - /* a valid argument with no argument value was given. */ - /* This happens when an optional argument value was invoked. */ - /* leave parent arguiment value unaltered but still count the argument. */ - parent->count++; - } else { - parent->filename[parent->count] = argval; - parent->basename[parent->count] = arg_basename(argval); - parent->extension[parent->count] = - arg_extension(parent->basename[parent->count]); /* only seek extensions within the basename (not the file path)*/ - parent->count++; - } - - ARG_TRACE(("%s4:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static int arg_file_checkfn(struct arg_file* parent) { - int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; - - ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static void arg_file_errorfn(struct arg_file* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { - const char* shortopts = parent->hdr.shortopts; - const char* longopts = parent->hdr.longopts; - const char* datatype = parent->hdr.datatype; - - /* make argval NULL safe */ - argval = argval ? argval : ""; - - arg_dstr_catf(ds, "%s: ", progname); - switch (errorcode) { - case ARG_ERR_MINCOUNT: - arg_dstr_cat(ds, "missing option "); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - break; - - case ARG_ERR_MAXCOUNT: - arg_dstr_cat(ds, "excess option "); - arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); - break; - - default: - arg_dstr_catf(ds, "unknown error at \"%s\"\n", argval); - } -} - -struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { - return arg_filen(shortopts, longopts, datatype, 0, 1, glossary); -} - -struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { - return arg_filen(shortopts, longopts, datatype, 1, 1, glossary); -} - -struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { - size_t nbytes; - struct arg_file* result; - int i; - - /* foolproof things by ensuring maxcount is not less than mincount */ - maxcount = (maxcount < mincount) ? mincount : maxcount; - - nbytes = sizeof(struct arg_file) /* storage for struct arg_file */ - + sizeof(char*) * (size_t)maxcount /* storage for filename[maxcount] array */ - + sizeof(char*) * (size_t)maxcount /* storage for basename[maxcount] array */ - + sizeof(char*) * (size_t)maxcount; /* storage for extension[maxcount] array */ - - result = (struct arg_file*)xmalloc(nbytes); - - /* init the arg_hdr struct */ - result->hdr.flag = ARG_HASVALUE; - result->hdr.shortopts = shortopts; - result->hdr.longopts = longopts; - result->hdr.glossary = glossary; - result->hdr.datatype = datatype ? datatype : ""; - result->hdr.mincount = mincount; - result->hdr.maxcount = maxcount; - result->hdr.parent = result; - result->hdr.resetfn = (arg_resetfn*)arg_file_resetfn; - result->hdr.scanfn = (arg_scanfn*)arg_file_scanfn; - result->hdr.checkfn = (arg_checkfn*)arg_file_checkfn; - result->hdr.errorfn = (arg_errorfn*)arg_file_errorfn; - - /* store the filename,basename,extension arrays immediately after the arg_file struct */ - result->filename = (const char**)(result + 1); - result->basename = result->filename + maxcount; - result->extension = result->basename + maxcount; - result->count = 0; - - /* foolproof the string pointers by initialising them with empty strings */ - for (i = 0; i < maxcount; i++) { - result->filename[i] = ""; - result->basename[i] = ""; - result->extension[i] = ""; - } - - ARG_TRACE(("arg_filen() returns %p\n", result)); - return result; -} -/******************************************************************************* - * arg_int: Implements the int command-line option - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include -#include -#include - -static void arg_int_resetfn(struct arg_int* parent) { - ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); - parent->count = 0; -} - -/* strtol0x() is like strtol() except that the numeric string is */ -/* expected to be prefixed by "0X" where X is a user supplied char. */ -/* The string may optionally be prefixed by white space and + or - */ -/* as in +0X123 or -0X123. */ -/* Once the prefix has been scanned, the remainder of the numeric */ -/* string is converted using strtol() with the given base. */ -/* eg: to parse hex str="-0X12324", specify X='X' and base=16. */ -/* eg: to parse oct str="+0o12324", specify X='O' and base=8. */ -/* eg: to parse bin str="-0B01010", specify X='B' and base=2. */ -/* Failure of conversion is indicated by result where *endptr==str. */ -static long int strtol0X(const char* str, const char** endptr, char X, int base) { - long int val; /* stores result */ - int s = 1; /* sign is +1 or -1 */ - const char* ptr = str; /* ptr to current position in str */ - - /* skip leading whitespace */ - while (isspace((int)(*ptr))) - ptr++; - /* printf("1) %s\n",ptr); */ - - /* scan optional sign character */ - switch (*ptr) { - case '+': - ptr++; - s = 1; - break; - case '-': - ptr++; - s = -1; - break; - default: - s = 1; - break; - } - /* printf("2) %s\n",ptr); */ - - /* '0X' prefix */ - if ((*ptr++) != '0') { - /* printf("failed to detect '0'\n"); */ - *endptr = str; - return 0; - } - /* printf("3) %s\n",ptr); */ - if (toupper(*ptr++) != toupper(X)) { - /* printf("failed to detect '%c'\n",X); */ - *endptr = str; - return 0; - } - /* printf("4) %s\n",ptr); */ - - /* attempt conversion on remainder of string using strtol() */ - val = strtol(ptr, (char**)endptr, base); - if (*endptr == ptr) { - /* conversion failed */ - *endptr = str; - return 0; - } - - /* success */ - return s * val; -} - -/* Returns 1 if str matches suffix (case insensitive). */ -/* Str may contain trailing whitespace, but nothing else. */ -static int detectsuffix(const char* str, const char* suffix) { - /* scan pairwise through strings until mismatch detected */ - while (toupper(*str) == toupper(*suffix)) { - /* printf("'%c' '%c'\n", *str, *suffix); */ - - /* return 1 (success) if match persists until the string terminator */ - if (*str == '\0') - return 1; - - /* next chars */ - str++; - suffix++; - } - /* printf("'%c' '%c' mismatch\n", *str, *suffix); */ - - /* return 0 (fail) if the matching did not consume the entire suffix */ - if (*suffix != 0) - return 0; /* failed to consume entire suffix */ - - /* skip any remaining whitespace in str */ - while (isspace((int)(*str))) - str++; - - /* return 1 (success) if we have reached end of str else return 0 (fail) */ - return (*str == '\0') ? 1 : 0; -} - -static int arg_int_scanfn(struct arg_int* parent, const char* argval) { - int errorcode = 0; - - if (parent->count == parent->hdr.maxcount) { - /* maximum number of arguments exceeded */ - errorcode = ARG_ERR_MAXCOUNT; - } else if (!argval) { - /* a valid argument with no argument value was given. */ - /* This happens when an optional argument value was invoked. */ - /* leave parent arguiment value unaltered but still count the argument. */ - parent->count++; - } else { - long int val; - const char* end; - - /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */ - val = strtol0X(argval, &end, 'X', 16); - if (end == argval) { - /* hex failed, attempt octal conversion (eg +0o123) */ - val = strtol0X(argval, &end, 'O', 8); - if (end == argval) { - /* octal failed, attempt binary conversion (eg +0B101) */ - val = strtol0X(argval, &end, 'B', 2); - if (end == argval) { - /* binary failed, attempt decimal conversion with no prefix (eg 1234) */ - val = strtol(argval, (char**)&end, 10); - if (end == argval) { - /* all supported number formats failed */ - return ARG_ERR_BADINT; - } - } - } - } - - /* Safety check for integer overflow. WARNING: this check */ - /* achieves nothing on machines where size(int)==size(long). */ - if (val > INT_MAX || val < INT_MIN) - errorcode = ARG_ERR_OVERFLOW; - - /* Detect any suffixes (KB,MB,GB) and multiply argument value appropriately. */ - /* We need to be mindful of integer overflows when using such big numbers. */ - if (detectsuffix(end, "KB")) /* kilobytes */ - { - if (val > (INT_MAX / 1024) || val < (INT_MIN / 1024)) - errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */ - else - val *= 1024; /* 1KB = 1024 */ - } else if (detectsuffix(end, "MB")) /* megabytes */ - { - if (val > (INT_MAX / 1048576) || val < (INT_MIN / 1048576)) - errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */ - else - val *= 1048576; /* 1MB = 1024*1024 */ - } else if (detectsuffix(end, "GB")) /* gigabytes */ - { - if (val > (INT_MAX / 1073741824) || val < (INT_MIN / 1073741824)) - errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */ - else - val *= 1073741824; /* 1GB = 1024*1024*1024 */ - } else if (!detectsuffix(end, "")) - errorcode = ARG_ERR_BADINT; /* invalid suffix detected */ - - /* if success then store result in parent->ival[] array */ - if (errorcode == 0) - parent->ival[parent->count++] = (int)val; - } - - /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */ - return errorcode; -} - -static int arg_int_checkfn(struct arg_int* parent) { - int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; - /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ - return errorcode; -} - -static void arg_int_errorfn(struct arg_int* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { - const char* shortopts = parent->hdr.shortopts; - const char* longopts = parent->hdr.longopts; - const char* datatype = parent->hdr.datatype; - - /* make argval NULL safe */ - argval = argval ? argval : ""; - - arg_dstr_catf(ds, "%s: ", progname); - switch (errorcode) { - case ARG_ERR_MINCOUNT: - arg_dstr_cat(ds, "missing option "); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - break; - - case ARG_ERR_MAXCOUNT: - arg_dstr_cat(ds, "excess option "); - arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); - break; - - case ARG_ERR_BADINT: - arg_dstr_catf(ds, "invalid argument \"%s\" to option ", argval); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - break; - - case ARG_ERR_OVERFLOW: - arg_dstr_cat(ds, "integer overflow at option "); - arg_print_option_ds(ds, shortopts, longopts, datatype, " "); - arg_dstr_catf(ds, "(%s is too large)\n", argval); - break; - } -} - -struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { - return arg_intn(shortopts, longopts, datatype, 0, 1, glossary); -} - -struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { - return arg_intn(shortopts, longopts, datatype, 1, 1, glossary); -} - -struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { - size_t nbytes; - struct arg_int* result; - - /* foolproof things by ensuring maxcount is not less than mincount */ - maxcount = (maxcount < mincount) ? mincount : maxcount; - - nbytes = sizeof(struct arg_int) /* storage for struct arg_int */ - + (size_t)maxcount * sizeof(int); /* storage for ival[maxcount] array */ - - result = (struct arg_int*)xmalloc(nbytes); - - /* init the arg_hdr struct */ - result->hdr.flag = ARG_HASVALUE; - result->hdr.shortopts = shortopts; - result->hdr.longopts = longopts; - result->hdr.datatype = datatype ? datatype : ""; - result->hdr.glossary = glossary; - result->hdr.mincount = mincount; - result->hdr.maxcount = maxcount; - result->hdr.parent = result; - result->hdr.resetfn = (arg_resetfn*)arg_int_resetfn; - result->hdr.scanfn = (arg_scanfn*)arg_int_scanfn; - result->hdr.checkfn = (arg_checkfn*)arg_int_checkfn; - result->hdr.errorfn = (arg_errorfn*)arg_int_errorfn; - - /* store the ival[maxcount] array immediately after the arg_int struct */ - result->ival = (int*)(result + 1); - result->count = 0; - - ARG_TRACE(("arg_intn() returns %p\n", result)); - return result; -} -/******************************************************************************* - * arg_lit: Implements the literature command-line option - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include - -static void arg_lit_resetfn(struct arg_lit* parent) { - ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); - parent->count = 0; -} - -static int arg_lit_scanfn(struct arg_lit* parent, const char* argval) { - int errorcode = 0; - if (parent->count < parent->hdr.maxcount) - parent->count++; - else - errorcode = ARG_ERR_MAXCOUNT; - - ARG_TRACE(("%s:scanfn(%p,%s) returns %d\n", __FILE__, parent, argval, errorcode)); - return errorcode; -} - -static int arg_lit_checkfn(struct arg_lit* parent) { - int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; - ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static void arg_lit_errorfn(struct arg_lit* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { - const char* shortopts = parent->hdr.shortopts; - const char* longopts = parent->hdr.longopts; - const char* datatype = parent->hdr.datatype; - - switch (errorcode) { - case ARG_ERR_MINCOUNT: - arg_dstr_catf(ds, "%s: missing option ", progname); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - arg_dstr_cat(ds, "\n"); - break; - - case ARG_ERR_MAXCOUNT: - arg_dstr_catf(ds, "%s: extraneous option ", progname); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - break; - } - - ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, ds, errorcode, argval, progname)); -} - -struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary) { - return arg_litn(shortopts, longopts, 0, 1, glossary); -} - -struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary) { - return arg_litn(shortopts, longopts, 1, 1, glossary); -} - -struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary) { - struct arg_lit* result; - - /* foolproof things by ensuring maxcount is not less than mincount */ - maxcount = (maxcount < mincount) ? mincount : maxcount; - - result = (struct arg_lit*)xmalloc(sizeof(struct arg_lit)); - - /* init the arg_hdr struct */ - result->hdr.flag = 0; - result->hdr.shortopts = shortopts; - result->hdr.longopts = longopts; - result->hdr.datatype = NULL; - result->hdr.glossary = glossary; - result->hdr.mincount = mincount; - result->hdr.maxcount = maxcount; - result->hdr.parent = result; - result->hdr.resetfn = (arg_resetfn*)arg_lit_resetfn; - result->hdr.scanfn = (arg_scanfn*)arg_lit_scanfn; - result->hdr.checkfn = (arg_checkfn*)arg_lit_checkfn; - result->hdr.errorfn = (arg_errorfn*)arg_lit_errorfn; - - /* init local variables */ - result->count = 0; - - ARG_TRACE(("arg_litn() returns %p\n", result)); - return result; -} -/******************************************************************************* - * arg_rem: Implements the rem command-line option - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include - -struct arg_rem* arg_rem(const char* datatype, const char* glossary) { - struct arg_rem* result = (struct arg_rem*)xmalloc(sizeof(struct arg_rem)); - - result->hdr.flag = 0; - result->hdr.shortopts = NULL; - result->hdr.longopts = NULL; - result->hdr.datatype = datatype; - result->hdr.glossary = glossary; - result->hdr.mincount = 1; - result->hdr.maxcount = 1; - result->hdr.parent = result; - result->hdr.resetfn = NULL; - result->hdr.scanfn = NULL; - result->hdr.checkfn = NULL; - result->hdr.errorfn = NULL; - - ARG_TRACE(("arg_rem() returns %p\n", result)); - return result; -} -/******************************************************************************* - * arg_rex: Implements the regex command-line option - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include -#include - -#ifndef _TREX_H_ -#define _TREX_H_ - -/* - * This module uses the T-Rex regular expression library to implement the regex - * logic. Here is the copyright notice of the library: - * - * Copyright (C) 2003-2006 Alberto Demichelis - * - * This software is provided 'as-is', without any express - * or implied warranty. In no event will the authors be held - * liable for any damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for - * any purpose, including commercial applications, and to alter - * it and redistribute it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; - * you must not claim that you wrote the original software. - * If you use this software in a product, an acknowledgment - * in the product documentation would be appreciated but - * is not required. - * - * 2. Altered source versions must be plainly marked as such, - * and must not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any - * source distribution. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#define TRexChar char -#define MAX_CHAR 0xFF -#define _TREXC(c) (c) -#define trex_strlen strlen -#define trex_printf printf - -#ifndef TREX_API -#define TREX_API extern -#endif - -#define TRex_True 1 -#define TRex_False 0 - -#define TREX_ICASE ARG_REX_ICASE - -typedef unsigned int TRexBool; -typedef struct TRex TRex; - -typedef struct { - const TRexChar* begin; - int len; -} TRexMatch; - -#if defined(__clang__) -TREX_API TRex* trex_compile(const TRexChar* pattern, const TRexChar** error, int flags) __attribute__((optnone)); -#elif defined(__GNUC__) -TREX_API TRex* trex_compile(const TRexChar* pattern, const TRexChar** error, int flags) __attribute__((optimize(0))); -#else -TREX_API TRex* trex_compile(const TRexChar* pattern, const TRexChar** error, int flags); -#endif -TREX_API void trex_free(TRex* exp); -TREX_API TRexBool trex_match(TRex* exp, const TRexChar* text); -TREX_API TRexBool trex_search(TRex* exp, const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end); -TREX_API TRexBool -trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end); -TREX_API int trex_getsubexpcount(TRex* exp); -TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch* subexp); - -#ifdef __cplusplus -} -#endif - -#endif - -struct privhdr { - const char* pattern; - int flags; -}; - -static void arg_rex_resetfn(struct arg_rex* parent) { - ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); - parent->count = 0; -} - -static int arg_rex_scanfn(struct arg_rex* parent, const char* argval) { - int errorcode = 0; - const TRexChar* error = NULL; - TRex* rex = NULL; - TRexBool is_match = TRex_False; - - if (parent->count == parent->hdr.maxcount) { - /* maximum number of arguments exceeded */ - errorcode = ARG_ERR_MAXCOUNT; - } else if (!argval) { - /* a valid argument with no argument value was given. */ - /* This happens when an optional argument value was invoked. */ - /* leave parent argument value unaltered but still count the argument. */ - parent->count++; - } else { - struct privhdr* priv = (struct privhdr*)parent->hdr.priv; - - /* test the current argument value for a match with the regular expression */ - /* if a match is detected, record the argument value in the arg_rex struct */ - - rex = trex_compile(priv->pattern, &error, priv->flags); - is_match = trex_match(rex, argval); - if (!is_match) - errorcode = ARG_ERR_REGNOMATCH; - else - parent->sval[parent->count++] = argval; - - trex_free(rex); - } - - ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static int arg_rex_checkfn(struct arg_rex* parent) { - int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; -#if 0 - struct privhdr *priv = (struct privhdr*)parent->hdr.priv; - - /* free the regex "program" we constructed in resetfn */ - regfree(&(priv->regex)); - - /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ -#endif - return errorcode; -} - -static void arg_rex_errorfn(struct arg_rex* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { - const char* shortopts = parent->hdr.shortopts; - const char* longopts = parent->hdr.longopts; - const char* datatype = parent->hdr.datatype; - - /* make argval NULL safe */ - argval = argval ? argval : ""; - - arg_dstr_catf(ds, "%s: ", progname); - switch (errorcode) { - case ARG_ERR_MINCOUNT: - arg_dstr_cat(ds, "missing option "); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - break; - - case ARG_ERR_MAXCOUNT: - arg_dstr_cat(ds, "excess option "); - arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); - break; - - case ARG_ERR_REGNOMATCH: - arg_dstr_cat(ds, "illegal value "); - arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); - break; - - default: { - #if 0 - char errbuff[256]; - regerror(errorcode, NULL, errbuff, sizeof(errbuff)); - printf("%s\n", errbuff); - #endif - } break; - } -} - -struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary) { - return arg_rexn(shortopts, longopts, pattern, datatype, 0, 1, flags, glossary); -} - -struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary) { - return arg_rexn(shortopts, longopts, pattern, datatype, 1, 1, flags, glossary); -} - -struct arg_rex* arg_rexn(const char* shortopts, - const char* longopts, - const char* pattern, - const char* datatype, - int mincount, - int maxcount, - int flags, - const char* glossary) { - size_t nbytes; - struct arg_rex* result; - struct privhdr* priv; - int i; - const TRexChar* error = NULL; - TRex* rex = NULL; - - if (!pattern) { - printf("argtable: ERROR - illegal regular expression pattern \"(NULL)\"\n"); - printf("argtable: Bad argument table.\n"); - return NULL; - } - - /* foolproof things by ensuring maxcount is not less than mincount */ - maxcount = (maxcount < mincount) ? mincount : maxcount; - - nbytes = sizeof(struct arg_rex) /* storage for struct arg_rex */ - + sizeof(struct privhdr) /* storage for private arg_rex data */ - + (size_t)maxcount * sizeof(char*); /* storage for sval[maxcount] array */ - - /* init the arg_hdr struct */ - result = (struct arg_rex*)xmalloc(nbytes); - result->hdr.flag = ARG_HASVALUE; - result->hdr.shortopts = shortopts; - result->hdr.longopts = longopts; - result->hdr.datatype = datatype ? datatype : pattern; - result->hdr.glossary = glossary; - result->hdr.mincount = mincount; - result->hdr.maxcount = maxcount; - result->hdr.parent = result; - result->hdr.resetfn = (arg_resetfn*)arg_rex_resetfn; - result->hdr.scanfn = (arg_scanfn*)arg_rex_scanfn; - result->hdr.checkfn = (arg_checkfn*)arg_rex_checkfn; - result->hdr.errorfn = (arg_errorfn*)arg_rex_errorfn; - - /* store the arg_rex_priv struct immediately after the arg_rex struct */ - result->hdr.priv = result + 1; - priv = (struct privhdr*)(result->hdr.priv); - priv->pattern = pattern; - priv->flags = flags; - - /* store the sval[maxcount] array immediately after the arg_rex_priv struct */ - result->sval = (const char**)(priv + 1); - result->count = 0; - - /* foolproof the string pointers by initializing them to reference empty strings */ - for (i = 0; i < maxcount; i++) - result->sval[i] = ""; - - /* here we construct and destroy a regex representation of the regular - * expression for no other reason than to force any regex errors to be - * trapped now rather than later. If we don't, then errors may go undetected - * until an argument is actually parsed. - */ - - rex = trex_compile(priv->pattern, &error, priv->flags); - if (rex == NULL) { - ARG_LOG(("argtable: %s \"%s\"\n", error ? error : _TREXC("undefined"), priv->pattern)); - ARG_LOG(("argtable: Bad argument table.\n")); - } - - trex_free(rex); - - ARG_TRACE(("arg_rexn() returns %p\n", result)); - return result; -} - -/* see copyright notice in trex.h */ -#include -#include -#include -#include - -#ifdef _UINCODE -#define scisprint iswprint -#define scstrlen wcslen -#define scprintf wprintf -#define _SC(x) L(x) -#else -#define scisprint isprint -#define scstrlen strlen -#define scprintf printf -#define _SC(x) (x) -#endif - -#ifdef ARG_REX_DEBUG -#include - -static const TRexChar* g_nnames[] = {_SC("NONE"), _SC("OP_GREEDY"), _SC("OP_OR"), _SC("OP_EXPR"), _SC("OP_NOCAPEXPR"), - _SC("OP_DOT"), _SC("OP_CLASS"), _SC("OP_CCLASS"), _SC("OP_NCLASS"), _SC("OP_RANGE"), - _SC("OP_CHAR"), _SC("OP_EOL"), _SC("OP_BOL"), _SC("OP_WB")}; - -#endif -#define OP_GREEDY (MAX_CHAR + 1) /* * + ? {n} */ -#define OP_OR (MAX_CHAR + 2) -#define OP_EXPR (MAX_CHAR + 3) /* parentesis () */ -#define OP_NOCAPEXPR (MAX_CHAR + 4) /* parentesis (?:) */ -#define OP_DOT (MAX_CHAR + 5) -#define OP_CLASS (MAX_CHAR + 6) -#define OP_CCLASS (MAX_CHAR + 7) -#define OP_NCLASS (MAX_CHAR + 8) /* negates class the [^ */ -#define OP_RANGE (MAX_CHAR + 9) -#define OP_CHAR (MAX_CHAR + 10) -#define OP_EOL (MAX_CHAR + 11) -#define OP_BOL (MAX_CHAR + 12) -#define OP_WB (MAX_CHAR + 13) - -#define TREX_SYMBOL_ANY_CHAR ('.') -#define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+') -#define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*') -#define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?') -#define TREX_SYMBOL_BRANCH ('|') -#define TREX_SYMBOL_END_OF_STRING ('$') -#define TREX_SYMBOL_BEGINNING_OF_STRING ('^') -#define TREX_SYMBOL_ESCAPE_CHAR ('\\') - -typedef int TRexNodeType; - -typedef struct tagTRexNode { - TRexNodeType type; - int left; - int right; - int next; -} TRexNode; - -struct TRex { - const TRexChar* _eol; - const TRexChar* _bol; - const TRexChar* _p; - int _first; - int _op; - TRexNode* _nodes; - int _nallocated; - int _nsize; - int _nsubexpr; - TRexMatch* _matches; - int _currsubexp; - void* _jmpbuf; - const TRexChar** _error; - int _flags; -}; - -static int trex_list(TRex* exp); - -static int trex_newnode(TRex* exp, TRexNodeType type) { - TRexNode n; - int newid; - n.type = type; - n.next = n.right = n.left = -1; - if (type == OP_EXPR) - n.right = exp->_nsubexpr++; - if (exp->_nallocated < (exp->_nsize + 1)) { - exp->_nallocated *= 2; - exp->_nodes = (TRexNode*)xrealloc(exp->_nodes, (size_t)exp->_nallocated * sizeof(TRexNode)); - } - exp->_nodes[exp->_nsize++] = n; - newid = exp->_nsize - 1; - return (int)newid; -} - -static void trex_error(TRex* exp, const TRexChar* error) { - if (exp->_error) - *exp->_error = error; - longjmp(*((jmp_buf*)exp->_jmpbuf), -1); -} - -static void trex_expect(TRex* exp, int n) { - if ((*exp->_p) != n) - trex_error(exp, _SC("expected paren")); - exp->_p++; -} - -static TRexChar trex_escapechar(TRex* exp) { - if (*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) { - exp->_p++; - switch (*exp->_p) { - case 'v': - exp->_p++; - return '\v'; - case 'n': - exp->_p++; - return '\n'; - case 't': - exp->_p++; - return '\t'; - case 'r': - exp->_p++; - return '\r'; - case 'f': - exp->_p++; - return '\f'; - default: - return (*exp->_p++); - } - } else if (!scisprint((int)(*exp->_p))) - trex_error(exp, _SC("letter expected")); - return (*exp->_p++); -} - -static int trex_charclass(TRex* exp, int classid) { - int n = trex_newnode(exp, OP_CCLASS); - exp->_nodes[n].left = classid; - return n; -} - -static int trex_charnode(TRex* exp, TRexBool isclass) { - TRexChar t; - if (*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) { - exp->_p++; - switch (*exp->_p) { - case 'n': - exp->_p++; - return trex_newnode(exp, '\n'); - case 't': - exp->_p++; - return trex_newnode(exp, '\t'); - case 'r': - exp->_p++; - return trex_newnode(exp, '\r'); - case 'f': - exp->_p++; - return trex_newnode(exp, '\f'); - case 'v': - exp->_p++; - return trex_newnode(exp, '\v'); - case 'a': - case 'A': - case 'w': - case 'W': - case 's': - case 'S': - case 'd': - case 'D': - case 'x': - case 'X': - case 'c': - case 'C': - case 'p': - case 'P': - case 'l': - case 'u': { - t = *exp->_p; - exp->_p++; - return trex_charclass(exp, t); - } - case 'b': - case 'B': - if (!isclass) { - int node = trex_newnode(exp, OP_WB); - exp->_nodes[node].left = *exp->_p; - exp->_p++; - return node; - } - /* fall through */ - default: - t = *exp->_p; - exp->_p++; - return trex_newnode(exp, t); - } - } else if (!scisprint((int)(*exp->_p))) { - trex_error(exp, _SC("letter expected")); - } - t = *exp->_p; - exp->_p++; - return trex_newnode(exp, t); -} -static int trex_class(TRex* exp) { - int ret = -1; - int first = -1, chain; - if (*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) { - ret = trex_newnode(exp, OP_NCLASS); - exp->_p++; - } else - ret = trex_newnode(exp, OP_CLASS); - - if (*exp->_p == ']') - trex_error(exp, _SC("empty class")); - chain = ret; - while (*exp->_p != ']' && exp->_p != exp->_eol) { - if (*exp->_p == '-' && first != -1) { - int r, t; - if (*exp->_p++ == ']') - trex_error(exp, _SC("unfinished range")); - r = trex_newnode(exp, OP_RANGE); - if (first > *exp->_p) - trex_error(exp, _SC("invalid range")); - if (exp->_nodes[first].type == OP_CCLASS) - trex_error(exp, _SC("cannot use character classes in ranges")); - exp->_nodes[r].left = exp->_nodes[first].type; - t = trex_escapechar(exp); - exp->_nodes[r].right = t; - exp->_nodes[chain].next = r; - chain = r; - first = -1; - } else { - if (first != -1) { - int c = first; - exp->_nodes[chain].next = c; - chain = c; - first = trex_charnode(exp, TRex_True); - } else { - first = trex_charnode(exp, TRex_True); - } - } - } - if (first != -1) { - int c = first; - exp->_nodes[chain].next = c; - chain = c; - first = -1; - } - /* hack? */ - exp->_nodes[ret].left = exp->_nodes[ret].next; - exp->_nodes[ret].next = -1; - return ret; -} - -static int trex_parsenumber(TRex* exp) { - int ret = *exp->_p - '0'; - int positions = 10; - exp->_p++; - while (isdigit((int)(*exp->_p))) { - ret = ret * 10 + (*exp->_p++ - '0'); - if (positions == 1000000000) - trex_error(exp, _SC("overflow in numeric constant")); - positions *= 10; - }; - return ret; -} - -static int trex_element(TRex* exp) { - int ret = -1; - switch (*exp->_p) { - case '(': { - int expr, newn; - exp->_p++; - - if (*exp->_p == '?') { - exp->_p++; - trex_expect(exp, ':'); - expr = trex_newnode(exp, OP_NOCAPEXPR); - } else - expr = trex_newnode(exp, OP_EXPR); - newn = trex_list(exp); - exp->_nodes[expr].left = newn; - ret = expr; - trex_expect(exp, ')'); - } break; - case '[': - exp->_p++; - ret = trex_class(exp); - trex_expect(exp, ']'); - break; - case TREX_SYMBOL_END_OF_STRING: - exp->_p++; - ret = trex_newnode(exp, OP_EOL); - break; - case TREX_SYMBOL_ANY_CHAR: - exp->_p++; - ret = trex_newnode(exp, OP_DOT); - break; - default: - ret = trex_charnode(exp, TRex_False); - break; - } - - { - TRexBool isgreedy = TRex_False; - unsigned short p0 = 0, p1 = 0; - switch (*exp->_p) { - case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: - p0 = 0; - p1 = 0xFFFF; - exp->_p++; - isgreedy = TRex_True; - break; - case TREX_SYMBOL_GREEDY_ONE_OR_MORE: - p0 = 1; - p1 = 0xFFFF; - exp->_p++; - isgreedy = TRex_True; - break; - case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: - p0 = 0; - p1 = 1; - exp->_p++; - isgreedy = TRex_True; - break; - case '{': - exp->_p++; - if (!isdigit((int)(*exp->_p))) - trex_error(exp, _SC("number expected")); - p0 = (unsigned short)trex_parsenumber(exp); - /*******************************/ - switch (*exp->_p) { - case '}': - p1 = p0; - exp->_p++; - break; - case ',': - exp->_p++; - p1 = 0xFFFF; - if (isdigit((int)(*exp->_p))) { - p1 = (unsigned short)trex_parsenumber(exp); - } - trex_expect(exp, '}'); - break; - default: - trex_error(exp, _SC(", or } expected")); - } - /*******************************/ - isgreedy = TRex_True; - break; - } - if (isgreedy) { - int nnode = trex_newnode(exp, OP_GREEDY); - exp->_nodes[nnode].left = ret; - exp->_nodes[nnode].right = ((p0) << 16) | p1; - ret = nnode; - } - } - if ((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && - (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) { - int nnode = trex_element(exp); - exp->_nodes[ret].next = nnode; - } - - return ret; -} - -static int trex_list(TRex* exp) { - int ret = -1, e; - if (*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) { - exp->_p++; - ret = trex_newnode(exp, OP_BOL); - } - e = trex_element(exp); - if (ret != -1) { - exp->_nodes[ret].next = e; - } else - ret = e; - - if (*exp->_p == TREX_SYMBOL_BRANCH) { - int temp, tright; - exp->_p++; - temp = trex_newnode(exp, OP_OR); - exp->_nodes[temp].left = ret; - tright = trex_list(exp); - exp->_nodes[temp].right = tright; - ret = temp; - } - return ret; -} - -static TRexBool trex_matchcclass(int cclass, TRexChar c) { - switch (cclass) { - case 'a': - return isalpha(c) ? TRex_True : TRex_False; - case 'A': - return !isalpha(c) ? TRex_True : TRex_False; - case 'w': - return (isalnum(c) || c == '_') ? TRex_True : TRex_False; - case 'W': - return (!isalnum(c) && c != '_') ? TRex_True : TRex_False; - case 's': - return isspace(c) ? TRex_True : TRex_False; - case 'S': - return !isspace(c) ? TRex_True : TRex_False; - case 'd': - return isdigit(c) ? TRex_True : TRex_False; - case 'D': - return !isdigit(c) ? TRex_True : TRex_False; - case 'x': - return isxdigit(c) ? TRex_True : TRex_False; - case 'X': - return !isxdigit(c) ? TRex_True : TRex_False; - case 'c': - return iscntrl(c) ? TRex_True : TRex_False; - case 'C': - return !iscntrl(c) ? TRex_True : TRex_False; - case 'p': - return ispunct(c) ? TRex_True : TRex_False; - case 'P': - return !ispunct(c) ? TRex_True : TRex_False; - case 'l': - return islower(c) ? TRex_True : TRex_False; - case 'u': - return isupper(c) ? TRex_True : TRex_False; - } - return TRex_False; /*cannot happen*/ -} - -static TRexBool trex_matchclass(TRex* exp, TRexNode* node, TRexChar c) { - do { - switch (node->type) { - case OP_RANGE: - if (exp->_flags & TREX_ICASE) { - if (c >= toupper(node->left) && c <= toupper(node->right)) - return TRex_True; - if (c >= tolower(node->left) && c <= tolower(node->right)) - return TRex_True; - } else { - if (c >= node->left && c <= node->right) - return TRex_True; - } - break; - case OP_CCLASS: - if (trex_matchcclass(node->left, c)) - return TRex_True; - break; - default: - if (exp->_flags & TREX_ICASE) { - if (c == tolower(node->type) || c == toupper(node->type)) - return TRex_True; - } else { - if (c == node->type) - return TRex_True; - } - } - } while ((node->next != -1) && ((node = &exp->_nodes[node->next]) != NULL)); - return TRex_False; -} - -static const TRexChar* trex_matchnode(TRex* exp, TRexNode* node, const TRexChar* str, TRexNode* next) { - TRexNodeType type = node->type; - switch (type) { - case OP_GREEDY: { - /* TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL; */ - TRexNode* greedystop = NULL; - int p0 = (node->right >> 16) & 0x0000FFFF, p1 = node->right & 0x0000FFFF, nmaches = 0; - const TRexChar *s = str, *good = str; - - if (node->next != -1) { - greedystop = &exp->_nodes[node->next]; - } else { - greedystop = next; - } - - while ((nmaches == 0xFFFF || nmaches < p1)) { - const TRexChar* stop; - if ((s = trex_matchnode(exp, &exp->_nodes[node->left], s, greedystop)) == NULL) - break; - nmaches++; - good = s; - if (greedystop) { - /* checks that 0 matches satisfy the expression(if so skips) */ - /* if not would always stop(for instance if is a '?') */ - if (greedystop->type != OP_GREEDY || (greedystop->type == OP_GREEDY && ((greedystop->right >> 16) & 0x0000FFFF) != 0)) { - TRexNode* gnext = NULL; - if (greedystop->next != -1) { - gnext = &exp->_nodes[greedystop->next]; - } else if (next && next->next != -1) { - gnext = &exp->_nodes[next->next]; - } - stop = trex_matchnode(exp, greedystop, s, gnext); - if (stop) { - /* if satisfied stop it */ - if (p0 == p1 && p0 == nmaches) - break; - else if (nmaches >= p0 && p1 == 0xFFFF) - break; - else if (nmaches >= p0 && nmaches <= p1) - break; - } - } - } - - if (s >= exp->_eol) - break; - } - if (p0 == p1 && p0 == nmaches) - return good; - else if (nmaches >= p0 && p1 == 0xFFFF) - return good; - else if (nmaches >= p0 && nmaches <= p1) - return good; - return NULL; - } - case OP_OR: { - const TRexChar* asd = str; - TRexNode* temp = &exp->_nodes[node->left]; - while ((asd = trex_matchnode(exp, temp, asd, NULL)) != NULL) { - if (temp->next != -1) - temp = &exp->_nodes[temp->next]; - else - return asd; - } - asd = str; - temp = &exp->_nodes[node->right]; - while ((asd = trex_matchnode(exp, temp, asd, NULL)) != NULL) { - if (temp->next != -1) - temp = &exp->_nodes[temp->next]; - else - return asd; - } - return NULL; - break; - } - case OP_EXPR: - case OP_NOCAPEXPR: { - TRexNode* n = &exp->_nodes[node->left]; - const TRexChar* cur = str; - int capture = -1; - if (node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) { - capture = exp->_currsubexp; - exp->_matches[capture].begin = cur; - exp->_currsubexp++; - } - - do { - TRexNode* subnext = NULL; - if (n->next != -1) { - subnext = &exp->_nodes[n->next]; - } else { - subnext = next; - } - if ((cur = trex_matchnode(exp, n, cur, subnext)) == NULL) { - if (capture != -1) { - exp->_matches[capture].begin = 0; - exp->_matches[capture].len = 0; - } - return NULL; - } - } while ((n->next != -1) && ((n = &exp->_nodes[n->next]) != NULL)); - - if (capture != -1) - exp->_matches[capture].len = (int)(cur - exp->_matches[capture].begin); - return cur; - } - case OP_WB: - if ((str == exp->_bol && !isspace((int)(*str))) || (str == exp->_eol && !isspace((int)(*(str - 1)))) || (!isspace((int)(*str)) && isspace((int)(*(str + 1)))) || - (isspace((int)(*str)) && !isspace((int)(*(str + 1))))) { - return (node->left == 'b') ? str : NULL; - } - return (node->left == 'b') ? NULL : str; - case OP_BOL: - if (str == exp->_bol) - return str; - return NULL; - case OP_EOL: - if (str == exp->_eol) - return str; - return NULL; - case OP_DOT: { - str++; - } - return str; - case OP_NCLASS: - case OP_CLASS: - if (trex_matchclass(exp, &exp->_nodes[node->left], *str) ? (type == OP_CLASS ? TRex_True : TRex_False) - : (type == OP_NCLASS ? TRex_True : TRex_False)) { - str++; - return str; - } - return NULL; - case OP_CCLASS: - if (trex_matchcclass(node->left, *str)) { - str++; - return str; - } - return NULL; - default: /* char */ - if (exp->_flags & TREX_ICASE) { - if (*str != tolower(node->type) && *str != toupper(node->type)) - return NULL; - } else { - if (*str != node->type) - return NULL; - } - str++; - return str; - } -} - -/* public api */ -TRex* trex_compile(const TRexChar* pattern, const TRexChar** error, int flags) { - TRex* exp = (TRex*)xmalloc(sizeof(TRex)); - exp->_eol = exp->_bol = NULL; - exp->_p = pattern; - exp->_nallocated = (int)(scstrlen(pattern) * sizeof(TRexChar)); - exp->_nodes = (TRexNode*)xmalloc((size_t)exp->_nallocated * sizeof(TRexNode)); - exp->_nsize = 0; - exp->_matches = 0; - exp->_nsubexpr = 0; - exp->_first = trex_newnode(exp, OP_EXPR); - exp->_error = error; - exp->_jmpbuf = xmalloc(sizeof(jmp_buf)); - exp->_flags = flags; - if (setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) { - int res = trex_list(exp); - exp->_nodes[exp->_first].left = res; - if (*exp->_p != '\0') - trex_error(exp, _SC("unexpected character")); -#ifdef ARG_REX_DEBUG - { - int nsize, i; - nsize = exp->_nsize; - scprintf(_SC("\n")); - for (i = 0; i < nsize; i++) { - if (exp->_nodes[i].type > MAX_CHAR) - scprintf(_SC("[%02d] %10s "), i, g_nnames[exp->_nodes[i].type - MAX_CHAR]); - else - scprintf(_SC("[%02d] %10c "), i, exp->_nodes[i].type); - scprintf(_SC("left %02d right %02d next %02d\n"), exp->_nodes[i].left, exp->_nodes[i].right, exp->_nodes[i].next); - } - scprintf(_SC("\n")); - } -#endif - exp->_matches = (TRexMatch*)xmalloc((size_t)exp->_nsubexpr * sizeof(TRexMatch)); - memset(exp->_matches, 0, (size_t)exp->_nsubexpr * sizeof(TRexMatch)); - } else { - trex_free(exp); - return NULL; - } - return exp; -} - -void trex_free(TRex* exp) { - if (exp) { - xfree(exp->_nodes); - xfree(exp->_jmpbuf); - xfree(exp->_matches); - xfree(exp); - } -} - -TRexBool trex_match(TRex* exp, const TRexChar* text) { - const TRexChar* res = NULL; - exp->_bol = text; - exp->_eol = text + scstrlen(text); - exp->_currsubexp = 0; - res = trex_matchnode(exp, exp->_nodes, text, NULL); - if (res == NULL || res != exp->_eol) - return TRex_False; - return TRex_True; -} - -TRexBool trex_searchrange(TRex* exp, const TRexChar* text_begin, const TRexChar* text_end, const TRexChar** out_begin, const TRexChar** out_end) { - const TRexChar* cur = NULL; - int node = exp->_first; - if (text_begin >= text_end) - return TRex_False; - exp->_bol = text_begin; - exp->_eol = text_end; - do { - cur = text_begin; - while (node != -1) { - exp->_currsubexp = 0; - cur = trex_matchnode(exp, &exp->_nodes[node], cur, NULL); - if (!cur) - break; - node = exp->_nodes[node].next; - } - text_begin++; - } while (cur == NULL && text_begin != text_end); - - if (cur == NULL) - return TRex_False; - - --text_begin; - - if (out_begin) - *out_begin = text_begin; - if (out_end) - *out_end = cur; - return TRex_True; -} - -TRexBool trex_search(TRex* exp, const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end) { - return trex_searchrange(exp, text, text + scstrlen(text), out_begin, out_end); -} - -int trex_getsubexpcount(TRex* exp) { - return exp->_nsubexpr; -} - -TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch* subexp) { - if (n < 0 || n >= exp->_nsubexpr) - return TRex_False; - *subexp = exp->_matches[n]; - return TRex_True; -} -/******************************************************************************* - * arg_str: Implements the str command-line option - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include - -static void arg_str_resetfn(struct arg_str* parent) { - int i; - - ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); - for (i = 0; i < parent->count; i++) { - parent->sval[i] = ""; - } - parent->count = 0; -} - -static int arg_str_scanfn(struct arg_str* parent, const char* argval) { - int errorcode = 0; - - if (parent->count == parent->hdr.maxcount) { - /* maximum number of arguments exceeded */ - errorcode = ARG_ERR_MAXCOUNT; - } else if (!argval) { - /* a valid argument with no argument value was given. */ - /* This happens when an optional argument value was invoked. */ - /* leave parent argument value unaltered but still count the argument. */ - parent->count++; - } else { - parent->sval[parent->count++] = argval; - } - - ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static int arg_str_checkfn(struct arg_str* parent) { - int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; - - ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); - return errorcode; -} - -static void arg_str_errorfn(struct arg_str* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { - const char* shortopts = parent->hdr.shortopts; - const char* longopts = parent->hdr.longopts; - const char* datatype = parent->hdr.datatype; - - /* make argval NULL safe */ - argval = argval ? argval : ""; - - arg_dstr_catf(ds, "%s: ", progname); - switch (errorcode) { - case ARG_ERR_MINCOUNT: - arg_dstr_cat(ds, "missing option "); - arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); - break; - - case ARG_ERR_MAXCOUNT: - arg_dstr_cat(ds, "excess option "); - arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); - break; - } -} - -struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { - return arg_strn(shortopts, longopts, datatype, 0, 1, glossary); -} - -struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { - return arg_strn(shortopts, longopts, datatype, 1, 1, glossary); -} - -struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { - size_t nbytes; - struct arg_str* result; - int i; - - /* should not allow this stupid error */ - /* we should return an error code warning this logic error */ - /* foolproof things by ensuring maxcount is not less than mincount */ - maxcount = (maxcount < mincount) ? mincount : maxcount; - - nbytes = sizeof(struct arg_str) /* storage for struct arg_str */ - + (size_t)maxcount * sizeof(char*); /* storage for sval[maxcount] array */ - - result = (struct arg_str*)xmalloc(nbytes); - - /* init the arg_hdr struct */ - result->hdr.flag = ARG_HASVALUE; - result->hdr.shortopts = shortopts; - result->hdr.longopts = longopts; - result->hdr.datatype = datatype ? datatype : ""; - result->hdr.glossary = glossary; - result->hdr.mincount = mincount; - result->hdr.maxcount = maxcount; - result->hdr.parent = result; - result->hdr.resetfn = (arg_resetfn*)arg_str_resetfn; - result->hdr.scanfn = (arg_scanfn*)arg_str_scanfn; - result->hdr.checkfn = (arg_checkfn*)arg_str_checkfn; - result->hdr.errorfn = (arg_errorfn*)arg_str_errorfn; - - /* store the sval[maxcount] array immediately after the arg_str struct */ - result->sval = (const char**)(result + 1); - result->count = 0; - - /* foolproof the string pointers by initializing them to reference empty strings */ - for (i = 0; i < maxcount; i++) - result->sval[i] = ""; - - ARG_TRACE(("arg_strn() returns %p\n", result)); - return result; -} -/******************************************************************************* - * arg_cmd: Provides the sub-command mechanism - * - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#endif - -#include -#include -#include - -#define MAX_MODULE_VERSION_SIZE 128 - -static arg_hashtable_t* s_hashtable = NULL; -static char* s_module_name = NULL; -static int s_mod_ver_major = 0; -static int s_mod_ver_minor = 0; -static int s_mod_ver_patch = 0; -static char* s_mod_ver_tag = NULL; -static char* s_mod_ver = NULL; - -void arg_set_module_name(const char* name) { - size_t slen; - - xfree(s_module_name); - slen = strlen(name); - s_module_name = (char*)xmalloc(slen + 1); - memset(s_module_name, 0, slen + 1); - -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - strncpy_s(s_module_name, slen + 1, name, slen); -#else - memcpy(s_module_name, name, slen); -#endif -} - -void arg_set_module_version(int major, int minor, int patch, const char* tag) { - size_t slen_tag, slen_ds; - arg_dstr_t ds; - - s_mod_ver_major = major; - s_mod_ver_minor = minor; - s_mod_ver_patch = patch; - - xfree(s_mod_ver_tag); - slen_tag = strlen(tag); - s_mod_ver_tag = (char*)xmalloc(slen_tag + 1); - memset(s_mod_ver_tag, 0, slen_tag + 1); - -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - strncpy_s(s_mod_ver_tag, slen_tag + 1, tag, slen_tag); -#else - memcpy(s_mod_ver_tag, tag, slen_tag); -#endif - - ds = arg_dstr_create(); - arg_dstr_catf(ds, "%d.", s_mod_ver_major); - arg_dstr_catf(ds, "%d.", s_mod_ver_minor); - arg_dstr_catf(ds, "%d.", s_mod_ver_patch); - arg_dstr_cat(ds, s_mod_ver_tag); - - xfree(s_mod_ver); - slen_ds = strlen(arg_dstr_cstr(ds)); - s_mod_ver = (char*)xmalloc(slen_ds + 1); - memset(s_mod_ver, 0, slen_ds + 1); - -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - strncpy_s(s_mod_ver, slen_ds + 1, arg_dstr_cstr(ds), slen_ds); -#else - memcpy(s_mod_ver, arg_dstr_cstr(ds), slen_ds); -#endif - - arg_dstr_destroy(ds); -} - -static unsigned int hash_key(const void* key) { - const char* str = (const char*)key; - int c; - unsigned int hash = 5381; - - while ((c = *str++) != 0) - hash = ((hash << 5) + hash) + (unsigned int)c; /* hash * 33 + c */ - - return hash; -} - -static int equal_keys(const void* key1, const void* key2) { - char* k1 = (char*)key1; - char* k2 = (char*)key2; - return (0 == strcmp(k1, k2)); -} - -void arg_cmd_init(void) { - s_hashtable = arg_hashtable_create(32, hash_key, equal_keys); -} - -void arg_cmd_uninit(void) { - arg_hashtable_destroy(s_hashtable, 1); -} - -void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description) { - arg_cmd_info_t* cmd_info; - size_t slen_name; - void* k; - - assert(strlen(name) < ARG_CMD_NAME_LEN); - assert(strlen(description) < ARG_CMD_DESCRIPTION_LEN); - - /* Check if the command already exists. */ - /* If the command exists, replace the existing command. */ - /* If the command doesn't exist, insert the command. */ - cmd_info = (arg_cmd_info_t*)arg_hashtable_search(s_hashtable, name); - if (cmd_info) { - arg_hashtable_remove(s_hashtable, name); - cmd_info = NULL; - } - - cmd_info = (arg_cmd_info_t*)xmalloc(sizeof(arg_cmd_info_t)); - memset(cmd_info, 0, sizeof(arg_cmd_info_t)); - -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - strncpy_s(cmd_info->name, ARG_CMD_NAME_LEN, name, strlen(name)); - strncpy_s(cmd_info->description, ARG_CMD_DESCRIPTION_LEN, description, strlen(description)); -#else - memcpy(cmd_info->name, name, strlen(name)); - memcpy(cmd_info->description, description, strlen(description)); -#endif - - cmd_info->proc = proc; - - slen_name = strlen(name); - k = xmalloc(slen_name + 1); - memset(k, 0, slen_name + 1); - -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - strncpy_s((char*)k, slen_name + 1, name, slen_name); -#else - memcpy((char*)k, name, slen_name); -#endif - - arg_hashtable_insert(s_hashtable, k, cmd_info); -} - -void arg_cmd_unregister(const char* name) { - arg_hashtable_remove(s_hashtable, name); -} - -int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res) { - arg_cmd_info_t* cmd_info = arg_cmd_info(name); - - assert(cmd_info != NULL); - assert(cmd_info->proc != NULL); - - return cmd_info->proc(argc, argv, res); -} - -arg_cmd_info_t* arg_cmd_info(const char* name) { - return (arg_cmd_info_t*)arg_hashtable_search(s_hashtable, name); -} - -unsigned int arg_cmd_count(void) { - return arg_hashtable_count(s_hashtable); -} - -arg_cmd_itr_t arg_cmd_itr_create(void) { - return (arg_cmd_itr_t)arg_hashtable_itr_create(s_hashtable); -} - -int arg_cmd_itr_advance(arg_cmd_itr_t itr) { - return arg_hashtable_itr_advance((arg_hashtable_itr_t*)itr); -} - -char* arg_cmd_itr_key(arg_cmd_itr_t itr) { - return (char*)arg_hashtable_itr_key((arg_hashtable_itr_t*)itr); -} - -arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr) { - return (arg_cmd_info_t*)arg_hashtable_itr_value((arg_hashtable_itr_t*)itr); -} - -void arg_cmd_itr_destroy(arg_cmd_itr_t itr) { - arg_hashtable_itr_destroy((arg_hashtable_itr_t*)itr); -} - -int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k) { - return arg_hashtable_itr_search((arg_hashtable_itr_t*)itr, s_hashtable, k); -} - -static const char* module_name(void) { - if (s_module_name == NULL || strlen(s_module_name) == 0) - return ""; - - return s_module_name; -} - -static const char* module_version(void) { - if (s_mod_ver == NULL || strlen(s_mod_ver) == 0) - return "0.0.0.0"; - - return s_mod_ver; -} - -void arg_make_get_help_msg(arg_dstr_t res) { - arg_dstr_catf(res, "%s v%s\n", module_name(), module_version()); - arg_dstr_catf(res, "Please type '%s help' to get more information.\n", module_name()); -} - -void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable) { - arg_cmd_info_t* cmd_info = (arg_cmd_info_t*)arg_hashtable_search(s_hashtable, cmd_name); - if (cmd_info) { - arg_dstr_catf(ds, "%s: %s\n", cmd_name, cmd_info->description); - } - - arg_dstr_cat(ds, "Usage:\n"); - arg_dstr_catf(ds, " %s", module_name()); - - arg_print_syntaxv_ds(ds, argtable, "\n \nAvailable options:\n"); - arg_print_glossary_ds(ds, argtable, " %-23s %s\n"); - - arg_dstr_cat(ds, "\n"); -} - -void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end) { - arg_print_errors_ds(ds, end, module_name()); - arg_dstr_cat(ds, "Usage: \n"); - arg_dstr_catf(ds, " %s", module_name()); - arg_print_syntaxv_ds(ds, argtable, "\n"); - arg_dstr_cat(ds, "\n"); -} - -int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode) { - /* help handling - * note: '-h|--help' takes precedence over error reporting - */ - if (help > 0) { - arg_make_help_msg(ds, name, argtable); - *exitcode = EXIT_SUCCESS; - return 1; - } - - /* syntax error handling */ - if (nerrors > 0) { - arg_make_syntax_err_msg(ds, argtable, end); - *exitcode = EXIT_FAILURE; - return 1; - } - - return 0; -} -/******************************************************************************* - * argtable3: Implements the main interfaces of the library - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#ifndef ARG_AMALGAMATION -#include "argtable3_private.h" -#if ARG_REPLACE_GETOPT == 1 -#include "arg_getopt.h" -#else -#include -#endif -#else -#if ARG_REPLACE_GETOPT == 0 -#include -#endif -#endif - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#undef WIN32_LEAN_AND_MEAN -#endif - -#include -#include -#include -#include -#include - -static void arg_register_error(struct arg_end* end, void* parent, int error, const char* argval) { - /* printf("arg_register_error(%p,%p,%d,%s)\n",end,parent,error,argval); */ - if (end->count < end->hdr.maxcount) { - end->error[end->count] = error; - end->parent[end->count] = parent; - end->argval[end->count] = argval; - end->count++; - } else { - end->error[end->hdr.maxcount - 1] = ARG_ELIMIT; - end->parent[end->hdr.maxcount - 1] = end; - end->argval[end->hdr.maxcount - 1] = NULL; - } -} - -/* - * Return index of first table entry with a matching short option - * or -1 if no match was found. - */ -static int find_shortoption(struct arg_hdr** table, char shortopt) { - int tabindex; - for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - if (table[tabindex]->shortopts && strchr(table[tabindex]->shortopts, shortopt)) - return tabindex; - } - return -1; -} - -struct longoptions { - int getoptval; - int noptions; - struct option* options; -}; - -#if 0 -static -void dump_longoptions(struct longoptions * longoptions) -{ - int i; - printf("getoptval = %d\n", longoptions->getoptval); - printf("noptions = %d\n", longoptions->noptions); - for (i = 0; i < longoptions->noptions; i++) - { - printf("options[%d].name = \"%s\"\n", - i, - longoptions->options[i].name); - printf("options[%d].has_arg = %d\n", i, longoptions->options[i].has_arg); - printf("options[%d].flag = %p\n", i, longoptions->options[i].flag); - printf("options[%d].val = %d\n", i, longoptions->options[i].val); - } -} -#endif - -static struct longoptions* alloc_longoptions(struct arg_hdr** table) { - struct longoptions* result; - size_t nbytes; - int noptions = 1; - size_t longoptlen = 0; - int tabindex; - int option_index = 0; - char* store; - - /* - * Determine the total number of option structs required - * by counting the number of comma separated long options - * in all table entries and return the count in noptions. - * note: noptions starts at 1 not 0 because we getoptlong - * requires a NULL option entry to terminate the option array. - * While we are at it, count the number of chars required - * to store private copies of all the longoption strings - * and return that count in logoptlen. - */ - tabindex = 0; - do { - const char* longopts = table[tabindex]->longopts; - longoptlen += (longopts ? strlen(longopts) : 0) + 1; - while (longopts) { - noptions++; - longopts = strchr(longopts + 1, ','); - } - } while (!(table[tabindex++]->flag & ARG_TERMINATOR)); - /*printf("%d long options consuming %d chars in total\n",noptions,longoptlen);*/ - - /* allocate storage for return data structure as: */ - /* (struct longoptions) + (struct options)[noptions] + char[longoptlen] */ - nbytes = sizeof(struct longoptions) + sizeof(struct option) * (size_t)noptions + longoptlen; - result = (struct longoptions*)xmalloc(nbytes); - - result->getoptval = 0; - result->noptions = noptions; - result->options = (struct option*)(result + 1); - store = (char*)(result->options + noptions); - - for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - const char* longopts = table[tabindex]->longopts; - - while (longopts && *longopts) { - char* storestart = store; - - /* copy progressive longopt strings into the store */ - while (*longopts != 0 && *longopts != ',') - *store++ = *longopts++; - *store++ = 0; - if (*longopts == ',') - longopts++; - /*fprintf(stderr,"storestart=\"%s\"\n",storestart);*/ - - result->options[option_index].name = storestart; - result->options[option_index].flag = &(result->getoptval); - result->options[option_index].val = tabindex; - if (table[tabindex]->flag & ARG_HASOPTVALUE) - result->options[option_index].has_arg = 2; - else if (table[tabindex]->flag & ARG_HASVALUE) - result->options[option_index].has_arg = 1; - else - result->options[option_index].has_arg = 0; - - option_index++; - } - } - /* terminate the options array with a zero-filled entry */ - result->options[option_index].name = 0; - result->options[option_index].has_arg = 0; - result->options[option_index].flag = 0; - result->options[option_index].val = 0; - - /*dump_longoptions(result);*/ - return result; -} - -static char* alloc_shortoptions(struct arg_hdr** table) { - char* result; - size_t len = 2; - int tabindex; - char* res; - - /* determine the total number of option chars required */ - for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - struct arg_hdr* hdr = table[tabindex]; - len += 3 * (hdr->shortopts ? strlen(hdr->shortopts) : 0); - } - - result = xmalloc(len); - - res = result; - - /* add a leading ':' so getopt return codes distinguish */ - /* unrecognised option and options missing argument values */ - *res++ = ':'; - - for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - struct arg_hdr* hdr = table[tabindex]; - const char* shortopts = hdr->shortopts; - while (shortopts && *shortopts) { - *res++ = *shortopts++; - if (hdr->flag & ARG_HASVALUE) - *res++ = ':'; - if (hdr->flag & ARG_HASOPTVALUE) - *res++ = ':'; - } - } - /* null terminate the string */ - *res = 0; - - /*printf("alloc_shortoptions() returns \"%s\"\n",(result?result:"NULL"));*/ - return result; -} - -/* return index of the table terminator entry */ -static int arg_endindex(struct arg_hdr** table) { - int tabindex = 0; - while (!(table[tabindex]->flag & ARG_TERMINATOR)) - tabindex++; - return tabindex; -} - -static void arg_parse_tagged(int argc, char** argv, struct arg_hdr** table, struct arg_end* endtable) { - struct longoptions* longoptions; - char* shortoptions; - int copt; - - /*printf("arg_parse_tagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/ - - /* allocate short and long option arrays for the given opttable[]. */ - /* if the allocs fail then put an error msg in the last table entry. */ - longoptions = alloc_longoptions(table); - shortoptions = alloc_shortoptions(table); - - /*dump_longoptions(longoptions);*/ - - /* reset getopts internal option-index to zero, and disable error reporting */ - optind = 0; - opterr = 0; - - /* fetch and process args using getopt_long */ -#ifdef ARG_LONG_ONLY - while ((copt = getopt_long_only(argc, argv, shortoptions, longoptions->options, NULL)) != -1) { -#else - while ((copt = getopt_long(argc, argv, shortoptions, longoptions->options, NULL)) != -1) { -#endif - /* - printf("optarg='%s'\n",optarg); - printf("optind=%d\n",optind); - printf("copt=%c\n",(char)copt); - printf("optopt=%c (%d)\n",optopt, (int)(optopt)); - */ - switch (copt) { - case 0: { - int tabindex = longoptions->getoptval; - void* parent = table[tabindex]->parent; - /*printf("long option detected from argtable[%d]\n", tabindex);*/ - if (optarg && optarg[0] == 0 && (table[tabindex]->flag & ARG_HASVALUE)) { - /* printf(": long option %s requires an argument\n",argv[optind-1]); */ - arg_register_error(endtable, endtable, ARG_EMISSARG, argv[optind - 1]); - /* continue to scan the (empty) argument value to enforce argument count checking */ - } - if (table[tabindex]->scanfn) { - int errorcode = table[tabindex]->scanfn(parent, optarg); - if (errorcode != 0) - arg_register_error(endtable, parent, errorcode, optarg); - } - } break; - - case '?': - /* - * getopt_long() found an unrecognised short option. - * if it was a short option its value is in optopt - * if it was a long option then optopt=0 - */ - switch (optopt) { - case 0: - /*printf("?0 unrecognised long option %s\n",argv[optind-1]);*/ - arg_register_error(endtable, endtable, ARG_ELONGOPT, argv[optind - 1]); - break; - default: - /*printf("?* unrecognised short option '%c'\n",optopt);*/ - arg_register_error(endtable, endtable, optopt, NULL); - break; - } - break; - - case ':': - /* - * getopt_long() found an option with its argument missing. - */ - /*printf(": option %s requires an argument\n",argv[optind-1]); */ - arg_register_error(endtable, endtable, ARG_EMISSARG, argv[optind - 1]); - break; - - default: { - /* getopt_long() found a valid short option */ - int tabindex = find_shortoption(table, (char)copt); - /*printf("short option detected from argtable[%d]\n", tabindex);*/ - if (tabindex == -1) { - /* should never get here - but handle it just in case */ - /*printf("unrecognised short option %d\n",copt);*/ - arg_register_error(endtable, endtable, copt, NULL); - } else { - if (table[tabindex]->scanfn) { - void* parent = table[tabindex]->parent; - int errorcode = table[tabindex]->scanfn(parent, optarg); - if (errorcode != 0) - arg_register_error(endtable, parent, errorcode, optarg); - } - } - break; - } - } - } - - xfree(shortoptions); - xfree(longoptions); -} - -static void arg_parse_untagged(int argc, char** argv, struct arg_hdr** table, struct arg_end* endtable) { - int tabindex = 0; - int errorlast = 0; - const char* optarglast = NULL; - void* parentlast = NULL; - - /*printf("arg_parse_untagged(%d,%p,%p,%p)\n",argc,argv,table,endtable);*/ - while (!(table[tabindex]->flag & ARG_TERMINATOR)) { - void* parent; - int errorcode; - - /* if we have exhausted our argv[optind] entries then we have finished */ - if (optind >= argc) { - /*printf("arg_parse_untagged(): argv[] exhausted\n");*/ - return; - } - - /* skip table entries with non-null long or short options (they are not untagged entries) */ - if (table[tabindex]->longopts || table[tabindex]->shortopts) { - /*printf("arg_parse_untagged(): skipping argtable[%d] (tagged argument)\n",tabindex);*/ - tabindex++; - continue; - } - - /* skip table entries with NULL scanfn */ - if (!(table[tabindex]->scanfn)) { - /*printf("arg_parse_untagged(): skipping argtable[%d] (NULL scanfn)\n",tabindex);*/ - tabindex++; - continue; - } - - /* attempt to scan the current argv[optind] with the current */ - /* table[tabindex] entry. If it succeeds then keep it, otherwise */ - /* try again with the next table[] entry. */ - parent = table[tabindex]->parent; - errorcode = table[tabindex]->scanfn(parent, argv[optind]); - if (errorcode == 0) { - /* success, move onto next argv[optind] but stay with same table[tabindex] */ - /*printf("arg_parse_untagged(): argtable[%d] successfully matched\n",tabindex);*/ - optind++; - - /* clear the last tentative error */ - errorlast = 0; - } else { - /* failure, try same argv[optind] with next table[tabindex] entry */ - /*printf("arg_parse_untagged(): argtable[%d] failed match\n",tabindex);*/ - tabindex++; - - /* remember this as a tentative error we may wish to reinstate later */ - errorlast = errorcode; - optarglast = argv[optind]; - parentlast = parent; - } - } - - /* if a tenative error still remains at this point then register it as a proper error */ - if (errorlast) { - arg_register_error(endtable, parentlast, errorlast, optarglast); - optind++; - } - - /* only get here when not all argv[] entries were consumed */ - /* register an error for each unused argv[] entry */ - while (optind < argc) { - /*printf("arg_parse_untagged(): argv[%d]=\"%s\" not consumed\n",optind,argv[optind]);*/ - arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind++]); - } - - return; -} - -static void arg_parse_check(struct arg_hdr** table, struct arg_end* endtable) { - int tabindex = 0; - /* printf("arg_parse_check()\n"); */ - do { - if (table[tabindex]->checkfn) { - void* parent = table[tabindex]->parent; - int errorcode = table[tabindex]->checkfn(parent); - if (errorcode != 0) - arg_register_error(endtable, parent, errorcode, NULL); - } - } while (!(table[tabindex++]->flag & ARG_TERMINATOR)); -} - -static void arg_reset(void** argtable) { - struct arg_hdr** table = (struct arg_hdr**)argtable; - int tabindex = 0; - /*printf("arg_reset(%p)\n",argtable);*/ - do { - if (table[tabindex]->resetfn) - table[tabindex]->resetfn(table[tabindex]->parent); - } while (!(table[tabindex++]->flag & ARG_TERMINATOR)); -} - -int arg_parse(int argc, char** argv, void** argtable) { - struct arg_hdr** table = (struct arg_hdr**)argtable; - struct arg_end* endtable; - int endindex; - char** argvcopy = NULL; - int i; - - /*printf("arg_parse(%d,%p,%p)\n",argc,argv,argtable);*/ - - /* reset any argtable data from previous invocations */ - arg_reset(argtable); - - /* locate the first end-of-table marker within the array */ - endindex = arg_endindex(table); - endtable = (struct arg_end*)table[endindex]; - - /* Special case of argc==0. This can occur on Texas Instruments DSP. */ - /* Failure to trap this case results in an unwanted NULL result from */ - /* the malloc for argvcopy (next code block). */ - if (argc == 0) { - /* We must still perform post-parse checks despite the absence of command line arguments */ - arg_parse_check(table, endtable); - - /* Now we are finished */ - return endtable->count; - } - - argvcopy = (char**)xmalloc(sizeof(char*) * (size_t)(argc + 1)); - - /* - Fill in the local copy of argv[]. We need a local copy - because getopt rearranges argv[] which adversely affects - susbsequent parsing attempts. - */ - for (i = 0; i < argc; i++) - argvcopy[i] = argv[i]; - - argvcopy[argc] = NULL; - - /* parse the command line (local copy) for tagged options */ - arg_parse_tagged(argc, argvcopy, table, endtable); - - /* parse the command line (local copy) for untagged options */ - arg_parse_untagged(argc, argvcopy, table, endtable); - - /* if no errors so far then perform post-parse checks otherwise dont bother */ - if (endtable->count == 0) - arg_parse_check(table, endtable); - - /* release the local copt of argv[] */ - xfree(argvcopy); - - return endtable->count; -} - -/* - * Concatenate contents of src[] string onto *pdest[] string. - * The *pdest pointer is altered to point to the end of the - * target string and *pndest is decremented by the same number - * of chars. - * Does not append more than *pndest chars into *pdest[] - * so as to prevent buffer overruns. - * Its something like strncat() but more efficient for repeated - * calls on the same destination string. - * Example of use: - * char dest[30] = "good" - * size_t ndest = sizeof(dest); - * char *pdest = dest; - * arg_char(&pdest,"bye ",&ndest); - * arg_char(&pdest,"cruel ",&ndest); - * arg_char(&pdest,"world!",&ndest); - * Results in: - * dest[] == "goodbye cruel world!" - * ndest == 10 - */ -static void arg_cat(char** pdest, const char* src, size_t* pndest) { - char* dest = *pdest; - char* end = dest + *pndest; - - /*locate null terminator of dest string */ - while (dest < end-1 && *dest != 0) - dest++; - - /* concat src string to dest string */ - while (dest < end-1 && *src != 0) - *dest++ = *src++; - - /* null terminate dest string */ - *dest = 0; - - /* update *pdest and *pndest */ - *pndest = (size_t)(end - dest); - *pdest = dest; -} - -static void arg_cat_option(char* dest, size_t ndest, const char* shortopts, const char* longopts, const char* datatype, int optvalue) { - if (shortopts) { - char option[3]; - - /* note: option array[] is initialiazed dynamically here to satisfy */ - /* a deficiency in the watcom compiler wrt static array initializers. */ - option[0] = '-'; - option[1] = shortopts[0]; - option[2] = 0; - - arg_cat(&dest, option, &ndest); - if (datatype) { - arg_cat(&dest, " ", &ndest); - if (optvalue) { - arg_cat(&dest, "[", &ndest); - arg_cat(&dest, datatype, &ndest); - arg_cat(&dest, "]", &ndest); - } else - arg_cat(&dest, datatype, &ndest); - } - } else if (longopts) { - size_t ncspn; - - /* add "--" tag prefix */ - arg_cat(&dest, "--", &ndest); - - /* add comma separated option tag */ - ncspn = strcspn(longopts, ","); -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - strncat_s(dest, ndest, longopts, (ncspn < ndest) ? ncspn : ndest); -#else - strncat(dest, longopts, (ncspn < ndest) ? ncspn : ndest); -#endif - - if (datatype) { - arg_cat(&dest, "=", &ndest); - if (optvalue) { - arg_cat(&dest, "[", &ndest); - arg_cat(&dest, datatype, &ndest); - arg_cat(&dest, "]", &ndest); - } else - arg_cat(&dest, datatype, &ndest); - } - } else if (datatype) { - if (optvalue) { - arg_cat(&dest, "[", &ndest); - arg_cat(&dest, datatype, &ndest); - arg_cat(&dest, "]", &ndest); - } else - arg_cat(&dest, datatype, &ndest); - } -} - -static void arg_cat_optionv(char* dest, size_t ndest, const char* shortopts, const char* longopts, const char* datatype, int optvalue, const char* separator) { - separator = separator ? separator : ""; - - if (shortopts) { - const char* c = shortopts; - while (*c) { - /* "-a|-b|-c" */ - char shortopt[3]; - - /* note: shortopt array[] is initialiazed dynamically here to satisfy */ - /* a deficiency in the watcom compiler wrt static array initializers. */ - shortopt[0] = '-'; - shortopt[1] = *c; - shortopt[2] = 0; - - arg_cat(&dest, shortopt, &ndest); - if (*++c) - arg_cat(&dest, separator, &ndest); - } - } - - /* put separator between long opts and short opts */ - if (shortopts && longopts) - arg_cat(&dest, separator, &ndest); - - if (longopts) { - const char* c = longopts; - while (*c) { - size_t ncspn; - - /* add "--" tag prefix */ - arg_cat(&dest, "--", &ndest); - - /* add comma separated option tag */ - ncspn = strcspn(c, ","); -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - strncat_s(dest, ndest, c, (ncspn < ndest) ? ncspn : ndest); -#else - strncat(dest, c, (ncspn < ndest) ? ncspn : ndest); -#endif - c += ncspn; - - /* add given separator in place of comma */ - if (*c == ',') { - arg_cat(&dest, separator, &ndest); - c++; - } - } - } - - if (datatype) { - if (longopts) - arg_cat(&dest, "=", &ndest); - else if (shortopts) - arg_cat(&dest, " ", &ndest); - - if (optvalue) { - arg_cat(&dest, "[", &ndest); - arg_cat(&dest, datatype, &ndest); - arg_cat(&dest, "]", &ndest); - } else - arg_cat(&dest, datatype, &ndest); - } -} - -void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix) { - char syntax[200] = ""; - suffix = suffix ? suffix : ""; - - /* there is no way of passing the proper optvalue for optional argument values here, so we must ignore it */ - arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, 0, "|"); - - arg_dstr_cat(ds, syntax); - arg_dstr_cat(ds, (char*)suffix); -} - -/* this function should be deprecated because it doesn't consider optional argument values (ARG_HASOPTVALUE) */ -void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix) { - arg_dstr_t ds = arg_dstr_create(); - arg_print_option_ds(ds, shortopts, longopts, datatype, suffix); - fputs(arg_dstr_cstr(ds), fp); - arg_dstr_destroy(ds); -} - -/* - * Print a GNU style [OPTION] string in which all short options that - * do not take argument values are presented in abbreviated form, as - * in: -xvfsd, or -xvf[sd], or [-xvsfd] - */ -static void arg_print_gnuswitch_ds(arg_dstr_t ds, struct arg_hdr** table) { - int tabindex; - const char* format1 = " -%c"; - const char* format2 = " [-%c"; - const char* suffix = ""; - - /* print all mandatory switches that are without argument values */ - for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - /* skip optional options */ - if (table[tabindex]->mincount < 1) - continue; - - /* skip non-short options */ - if (table[tabindex]->shortopts == NULL) - continue; - - /* skip options that take argument values */ - if (table[tabindex]->flag & ARG_HASVALUE) - continue; - - /* print the short option (only the first short option char, ignore multiple choices)*/ - arg_dstr_catf(ds, format1, table[tabindex]->shortopts[0]); - format1 = "%c"; - format2 = "[%c"; - } - - /* print all optional switches that are without argument values */ - for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - /* skip mandatory args */ - if (table[tabindex]->mincount > 0) - continue; - - /* skip args without short options */ - if (table[tabindex]->shortopts == NULL) - continue; - - /* skip args with values */ - if (table[tabindex]->flag & ARG_HASVALUE) - continue; - - /* print first short option */ - arg_dstr_catf(ds, format2, table[tabindex]->shortopts[0]); - format2 = "%c"; - suffix = "]"; - } - - arg_dstr_catf(ds, "%s", suffix); -} - -void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix) { - struct arg_hdr** table = (struct arg_hdr**)argtable; - int i, tabindex; - - /* print GNU style [OPTION] string */ - arg_print_gnuswitch_ds(ds, table); - - /* print remaining options in abbreviated style */ - for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - char syntax[200] = ""; - const char *shortopts, *longopts, *datatype; - - /* skip short options without arg values (they were printed by arg_print_gnu_switch) */ - if (table[tabindex]->shortopts && !(table[tabindex]->flag & ARG_HASVALUE)) - continue; - - shortopts = table[tabindex]->shortopts; - longopts = table[tabindex]->longopts; - datatype = table[tabindex]->datatype; - arg_cat_option(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE); - - if (strlen(syntax) > 0) { - /* print mandatory instances of this option */ - for (i = 0; i < table[tabindex]->mincount; i++) { - arg_dstr_cat(ds, " "); - arg_dstr_cat(ds, syntax); - } - - /* print optional instances enclosed in "[..]" */ - switch (table[tabindex]->maxcount - table[tabindex]->mincount) { - case 0: - break; - case 1: - arg_dstr_cat(ds, " ["); - arg_dstr_cat(ds, syntax); - arg_dstr_cat(ds, "]"); - break; - case 2: - arg_dstr_cat(ds, " ["); - arg_dstr_cat(ds, syntax); - arg_dstr_cat(ds, "]"); - arg_dstr_cat(ds, " ["); - arg_dstr_cat(ds, syntax); - arg_dstr_cat(ds, "]"); - break; - default: - arg_dstr_cat(ds, " ["); - arg_dstr_cat(ds, syntax); - arg_dstr_cat(ds, "]..."); - break; - } - } - } - - if (suffix) { - arg_dstr_cat(ds, (char*)suffix); - } -} - -void arg_print_syntax(FILE* fp, void** argtable, const char* suffix) { - arg_dstr_t ds = arg_dstr_create(); - arg_print_syntax_ds(ds, argtable, suffix); - fputs(arg_dstr_cstr(ds), fp); - arg_dstr_destroy(ds); -} - -void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix) { - struct arg_hdr** table = (struct arg_hdr**)argtable; - int i, tabindex; - - /* print remaining options in abbreviated style */ - for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - char syntax[200] = ""; - const char *shortopts, *longopts, *datatype; - - shortopts = table[tabindex]->shortopts; - longopts = table[tabindex]->longopts; - datatype = table[tabindex]->datatype; - arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, "|"); - - /* print mandatory options */ - for (i = 0; i < table[tabindex]->mincount; i++) { - arg_dstr_cat(ds, " "); - arg_dstr_cat(ds, syntax); - } - - /* print optional args enclosed in "[..]" */ - switch (table[tabindex]->maxcount - table[tabindex]->mincount) { - case 0: - break; - case 1: - arg_dstr_cat(ds, " ["); - arg_dstr_cat(ds, syntax); - arg_dstr_cat(ds, "]"); - break; - case 2: - arg_dstr_cat(ds, " ["); - arg_dstr_cat(ds, syntax); - arg_dstr_cat(ds, "]"); - arg_dstr_cat(ds, " ["); - arg_dstr_cat(ds, syntax); - arg_dstr_cat(ds, "]"); - break; - default: - arg_dstr_cat(ds, " ["); - arg_dstr_cat(ds, syntax); - arg_dstr_cat(ds, "]..."); - break; - } - } - - if (suffix) { - arg_dstr_cat(ds, (char*)suffix); - } -} - -void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix) { - arg_dstr_t ds = arg_dstr_create(); - arg_print_syntaxv_ds(ds, argtable, suffix); - fputs(arg_dstr_cstr(ds), fp); - arg_dstr_destroy(ds); -} - -void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format) { - struct arg_hdr** table = (struct arg_hdr**)argtable; - int tabindex; - - format = format ? format : " %-20s %s\n"; - for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - if (table[tabindex]->glossary) { - char syntax[200] = ""; - const char* shortopts = table[tabindex]->shortopts; - const char* longopts = table[tabindex]->longopts; - const char* datatype = table[tabindex]->datatype; - const char* glossary = table[tabindex]->glossary; - arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, ", "); - arg_dstr_catf(ds, format, syntax, glossary); - } - } -} - -void arg_print_glossary(FILE* fp, void** argtable, const char* format) { - arg_dstr_t ds = arg_dstr_create(); - arg_print_glossary_ds(ds, argtable, format); - fputs(arg_dstr_cstr(ds), fp); - arg_dstr_destroy(ds); -} - -/** - * Print a piece of text formatted, which means in a column with a - * left and a right margin. The lines are wrapped at whitspaces next - * to right margin. The function does not indent the first line, but - * only the following ones. - * - * Example: - * arg_print_formatted( fp, 0, 5, "Some text that doesn't fit." ) - * will result in the following output: - * - * Some - * text - * that - * doesn' - * t fit. - * - * Too long lines will be wrapped in the middle of a word. - * - * arg_print_formatted( fp, 2, 7, "Some text that doesn't fit." ) - * will result in the following output: - * - * Some - * text - * that - * doesn' - * t fit. - * - * As you see, the first line is not indented. This enables output of - * lines, which start in a line where output already happened. - * - * Author: Uli Fouquet - */ -static void arg_print_formatted_ds(arg_dstr_t ds, const unsigned lmargin, const unsigned rmargin, const char* text) { - const unsigned int textlen = (unsigned int)strlen(text); - unsigned int line_start = 0; - unsigned int line_end = textlen; - const unsigned int colwidth = (rmargin - lmargin) + 1; - - assert(strlen(text) < UINT_MAX); - - /* Someone doesn't like us... */ - if (line_end < line_start) { - arg_dstr_catf(ds, "%s\n", text); - } - - while (line_end > line_start) { - /* Eat leading white spaces. This is essential because while - wrapping lines, there will often be a whitespace at beginning - of line. Preserve newlines */ - while (isspace((int)(*(text + line_start))) && *(text + line_start) != '\n') { - line_start++; - } - - /* Find last whitespace, that fits into line */ - if (line_end - line_start > colwidth) { - line_end = line_start + colwidth; - - while ((line_end > line_start) && !isspace((int)(*(text + line_end)))) { - line_end--; - } - - /* If no whitespace could be found, eg. the text is one long word, break the word */ - if (line_end == line_start) { - /* Set line_end to previous value */ - line_end = line_start + colwidth; - } else { - /* Consume trailing spaces, except newlines */ - while ((line_end > line_start) && isspace((int)(*(text + line_end))) && *(text + line_start) != '\n') { - line_end--; - } - - /* Restore the last non-space character */ - line_end++; - } - } - - /* Output line of text */ - while (line_start < line_end) { - char c = *(text + line_start); - - /* If character is newline stop printing, skip this character, as a newline will be printed below. */ - if (c == '\n') { - line_start++; - break; - } - - arg_dstr_catc(ds, c); - line_start++; - } - arg_dstr_cat(ds, "\n"); - - /* Initialize another line */ - if (line_end < textlen) { - unsigned i; - - for (i = 0; i < lmargin; i++) { - arg_dstr_cat(ds, " "); - } - - line_end = textlen; - } - } /* lines of text */ -} - -/** - * Prints the glossary in strict GNU format. - * Differences to arg_print_glossary() are: - * - wraps lines after 80 chars - * - indents lines without shortops - * - does not accept formatstrings - * - * Contributed by Uli Fouquet - */ -void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable) { - struct arg_hdr** table = (struct arg_hdr**)argtable; - int tabindex; - - for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - if (table[tabindex]->glossary) { - char syntax[200] = ""; - const char* shortopts = table[tabindex]->shortopts; - const char* longopts = table[tabindex]->longopts; - const char* datatype = table[tabindex]->datatype; - const char* glossary = table[tabindex]->glossary; - - if (!shortopts && longopts) { - /* Indent trailing line by 4 spaces... */ - memset(syntax, ' ', 4); - *(syntax + 4) = '\0'; - } - - arg_cat_optionv(syntax, sizeof(syntax) - 1, shortopts, longopts, datatype, table[tabindex]->flag & ARG_HASOPTVALUE, ", "); - - /* If syntax fits not into column, print glossary in new line... */ - if (strlen(syntax) > 25) { - arg_dstr_catf(ds, " %-25s %s\n", syntax, ""); - *syntax = '\0'; - } - - arg_dstr_catf(ds, " %-25s ", syntax); - arg_print_formatted_ds(ds, 28, 79, glossary); - } - } /* for each table entry */ - - arg_dstr_cat(ds, "\n"); -} - -void arg_print_glossary_gnu(FILE* fp, void** argtable) { - arg_dstr_t ds = arg_dstr_create(); - arg_print_glossary_gnu_ds(ds, argtable); - fputs(arg_dstr_cstr(ds), fp); - arg_dstr_destroy(ds); -} - -/** - * Checks the argtable[] array for NULL entries and returns 1 - * if any are found, zero otherwise. - */ -int arg_nullcheck(void** argtable) { - struct arg_hdr** table = (struct arg_hdr**)argtable; - int tabindex; - /*printf("arg_nullcheck(%p)\n",argtable);*/ - - if (!table) - return 1; - - tabindex = 0; - do { - /*printf("argtable[%d]=%p\n",tabindex,argtable[tabindex]);*/ - if (!table[tabindex]) - return 1; - } while (!(table[tabindex++]->flag & ARG_TERMINATOR)); - - return 0; -} - -/* - * arg_free() is deprecated in favour of arg_freetable() due to a flaw in its design. - * The flaw results in memory leak in the (very rare) case that an intermediate - * entry in the argtable array failed its memory allocation while others following - * that entry were still allocated ok. Those subsequent allocations will not be - * deallocated by arg_free(). - * Despite the unlikeliness of the problem occurring, and the even unlikelier event - * that it has any deliterious effect, it is fixed regardless by replacing arg_free() - * with the newer arg_freetable() function. - * We still keep arg_free() for backwards compatibility. - */ -void arg_free(void** argtable) { - struct arg_hdr** table = (struct arg_hdr**)argtable; - int tabindex = 0; - int flag; - /*printf("arg_free(%p)\n",argtable);*/ - do { - /* - if we encounter a NULL entry then somewhat incorrectly we presume - we have come to the end of the array. It isnt strictly true because - an intermediate entry could be NULL with other non-NULL entries to follow. - The subsequent argtable entries would then not be freed as they should. - */ - if (table[tabindex] == NULL) - break; - - flag = table[tabindex]->flag; - xfree(table[tabindex]); - table[tabindex++] = NULL; - - } while (!(flag & ARG_TERMINATOR)); -} - -/* frees each non-NULL element of argtable[], where n is the size of the number of entries in the array */ -void arg_freetable(void** argtable, size_t n) { - struct arg_hdr** table = (struct arg_hdr**)argtable; - size_t tabindex = 0; - /*printf("arg_freetable(%p)\n",argtable);*/ - for (tabindex = 0; tabindex < n; tabindex++) { - if (table[tabindex] == NULL) - continue; - - xfree(table[tabindex]); - table[tabindex] = NULL; - }; -} - -#ifdef _WIN32 -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { - return TRUE; - UNREFERENCED_PARAMETER(hinstDLL); - UNREFERENCED_PARAMETER(fdwReason); - UNREFERENCED_PARAMETER(lpvReserved); -} -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/argtable3.h b/parallel/parallel_src/extern/argtable3-3.2.2/argtable3.h deleted file mode 100644 index a4e6bdbd..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/argtable3.h +++ /dev/null @@ -1,273 +0,0 @@ -/******************************************************************************* - * argtable3: Declares the main interfaces of the library - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#ifndef ARGTABLE3 -#define ARGTABLE3 - -#include /* FILE */ -#include /* struct tm */ - -#ifdef __cplusplus -extern "C" { -#endif - -#define ARG_REX_ICASE 1 -#define ARG_DSTR_SIZE 200 -#define ARG_CMD_NAME_LEN 100 -#define ARG_CMD_DESCRIPTION_LEN 256 - -#ifndef ARG_REPLACE_GETOPT -#define ARG_REPLACE_GETOPT 1 /* use the embedded getopt as the system getopt(3) */ -#endif /* ARG_REPLACE_GETOPT */ - -/* bit masks for arg_hdr.flag */ -enum { ARG_TERMINATOR = 0x1, ARG_HASVALUE = 0x2, ARG_HASOPTVALUE = 0x4 }; - -#if defined(_WIN32) - #if defined(argtable3_EXPORTS) - #define ARG_EXTERN __declspec(dllexport) - #elif defined(argtable3_IMPORTS) - #define ARG_EXTERN __declspec(dllimport) - #else - #define ARG_EXTERN - #endif -#else - #define ARG_EXTERN -#endif - -typedef struct _internal_arg_dstr* arg_dstr_t; -typedef void* arg_cmd_itr_t; - -typedef void(arg_resetfn)(void* parent); -typedef int(arg_scanfn)(void* parent, const char* argval); -typedef int(arg_checkfn)(void* parent); -typedef void(arg_errorfn)(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname); -typedef void(arg_dstr_freefn)(char* buf); -typedef int(arg_cmdfn)(int argc, char* argv[], arg_dstr_t res); -typedef int(arg_comparefn)(const void* k1, const void* k2); - -/* - * The arg_hdr struct defines properties that are common to all arg_xxx structs. - * The argtable library requires each arg_xxx struct to have an arg_hdr - * struct as its first data member. - * The argtable library functions then use this data to identify the - * properties of the command line option, such as its option tags, - * datatype string, and glossary strings, and so on. - * Moreover, the arg_hdr struct contains pointers to custom functions that - * are provided by each arg_xxx struct which perform the tasks of parsing - * that particular arg_xxx arguments, performing post-parse checks, and - * reporting errors. - * These functions are private to the individual arg_xxx source code - * and are the pointer to them are initiliased by that arg_xxx struct's - * constructor function. The user could alter them after construction - * if desired, but the original intention is for them to be set by the - * constructor and left unaltered. - */ -typedef struct arg_hdr { - char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */ - const char* shortopts; /* String defining the short options */ - const char* longopts; /* String defiing the long options */ - const char* datatype; /* Description of the argument data type */ - const char* glossary; /* Description of the option as shown by arg_print_glossary function */ - int mincount; /* Minimum number of occurences of this option accepted */ - int maxcount; /* Maximum number of occurences if this option accepted */ - void* parent; /* Pointer to parent arg_xxx struct */ - arg_resetfn* resetfn; /* Pointer to parent arg_xxx reset function */ - arg_scanfn* scanfn; /* Pointer to parent arg_xxx scan function */ - arg_checkfn* checkfn; /* Pointer to parent arg_xxx check function */ - arg_errorfn* errorfn; /* Pointer to parent arg_xxx error function */ - void* priv; /* Pointer to private header data for use by arg_xxx functions */ -} arg_hdr_t; - -typedef struct arg_rem { - struct arg_hdr hdr; /* The mandatory argtable header struct */ -} arg_rem_t; - -typedef struct arg_lit { - struct arg_hdr hdr; /* The mandatory argtable header struct */ - int count; /* Number of matching command line args */ -} arg_lit_t; - -typedef struct arg_int { - struct arg_hdr hdr; /* The mandatory argtable header struct */ - int count; /* Number of matching command line args */ - int* ival; /* Array of parsed argument values */ -} arg_int_t; - -typedef struct arg_dbl { - struct arg_hdr hdr; /* The mandatory argtable header struct */ - int count; /* Number of matching command line args */ - double* dval; /* Array of parsed argument values */ -} arg_dbl_t; - -typedef struct arg_str { - struct arg_hdr hdr; /* The mandatory argtable header struct */ - int count; /* Number of matching command line args */ - const char** sval; /* Array of parsed argument values */ -} arg_str_t; - -typedef struct arg_rex { - struct arg_hdr hdr; /* The mandatory argtable header struct */ - int count; /* Number of matching command line args */ - const char** sval; /* Array of parsed argument values */ -} arg_rex_t; - -typedef struct arg_file { - struct arg_hdr hdr; /* The mandatory argtable header struct */ - int count; /* Number of matching command line args*/ - const char** filename; /* Array of parsed filenames (eg: /home/foo.bar) */ - const char** basename; /* Array of parsed basenames (eg: foo.bar) */ - const char** extension; /* Array of parsed extensions (eg: .bar) */ -} arg_file_t; - -typedef struct arg_date { - struct arg_hdr hdr; /* The mandatory argtable header struct */ - const char* format; /* strptime format string used to parse the date */ - int count; /* Number of matching command line args */ - struct tm* tmval; /* Array of parsed time values */ -} arg_date_t; - -enum { ARG_ELIMIT = 1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG }; -typedef struct arg_end { - struct arg_hdr hdr; /* The mandatory argtable header struct */ - int count; /* Number of errors encountered */ - int* error; /* Array of error codes */ - void** parent; /* Array of pointers to offending arg_xxx struct */ - const char** argval; /* Array of pointers to offending argv[] string */ -} arg_end_t; - -typedef struct arg_cmd_info { - char name[ARG_CMD_NAME_LEN]; - char description[ARG_CMD_DESCRIPTION_LEN]; - arg_cmdfn* proc; -} arg_cmd_info_t; - -/**** arg_xxx constructor functions *********************************/ - -ARG_EXTERN struct arg_rem* arg_rem(const char* datatype, const char* glossary); - -ARG_EXTERN struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary); -ARG_EXTERN struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary); -ARG_EXTERN struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary); - -ARG_EXTERN struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); - -ARG_EXTERN struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); - -ARG_EXTERN struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); - -ARG_EXTERN struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary); -ARG_EXTERN struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary); -ARG_EXTERN struct arg_rex* arg_rexn(const char* shortopts, - const char* longopts, - const char* pattern, - const char* datatype, - int mincount, - int maxcount, - int flags, - const char* glossary); - -ARG_EXTERN struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); - -ARG_EXTERN struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary); -ARG_EXTERN struct arg_date* arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary); - -ARG_EXTERN struct arg_end* arg_end(int maxcount); - -#define ARG_DSTR_STATIC ((arg_dstr_freefn*)0) -#define ARG_DSTR_VOLATILE ((arg_dstr_freefn*)1) -#define ARG_DSTR_DYNAMIC ((arg_dstr_freefn*)3) - -/**** other functions *******************************************/ -ARG_EXTERN int arg_nullcheck(void** argtable); -ARG_EXTERN int arg_parse(int argc, char** argv, void** argtable); -ARG_EXTERN void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix); -ARG_EXTERN void arg_print_syntax(FILE* fp, void** argtable, const char* suffix); -ARG_EXTERN void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix); -ARG_EXTERN void arg_print_glossary(FILE* fp, void** argtable, const char* format); -ARG_EXTERN void arg_print_glossary_gnu(FILE* fp, void** argtable); -ARG_EXTERN void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname); -ARG_EXTERN void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix); -ARG_EXTERN void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix); -ARG_EXTERN void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix); -ARG_EXTERN void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format); -ARG_EXTERN void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable); -ARG_EXTERN void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname); -ARG_EXTERN void arg_freetable(void** argtable, size_t n); - -ARG_EXTERN arg_dstr_t arg_dstr_create(void); -ARG_EXTERN void arg_dstr_destroy(arg_dstr_t ds); -ARG_EXTERN void arg_dstr_reset(arg_dstr_t ds); -ARG_EXTERN void arg_dstr_free(arg_dstr_t ds); -ARG_EXTERN void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc); -ARG_EXTERN void arg_dstr_cat(arg_dstr_t ds, const char* str); -ARG_EXTERN void arg_dstr_catc(arg_dstr_t ds, char c); -ARG_EXTERN void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...); -ARG_EXTERN char* arg_dstr_cstr(arg_dstr_t ds); - -ARG_EXTERN void arg_cmd_init(void); -ARG_EXTERN void arg_cmd_uninit(void); -ARG_EXTERN void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description); -ARG_EXTERN void arg_cmd_unregister(const char* name); -ARG_EXTERN int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res); -ARG_EXTERN unsigned int arg_cmd_count(void); -ARG_EXTERN arg_cmd_info_t* arg_cmd_info(const char* name); -ARG_EXTERN arg_cmd_itr_t arg_cmd_itr_create(void); -ARG_EXTERN void arg_cmd_itr_destroy(arg_cmd_itr_t itr); -ARG_EXTERN int arg_cmd_itr_advance(arg_cmd_itr_t itr); -ARG_EXTERN char* arg_cmd_itr_key(arg_cmd_itr_t itr); -ARG_EXTERN arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr); -ARG_EXTERN int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k); -ARG_EXTERN void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn); -ARG_EXTERN void arg_make_get_help_msg(arg_dstr_t res); -ARG_EXTERN void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable); -ARG_EXTERN void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end); -ARG_EXTERN int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode); -ARG_EXTERN void arg_set_module_name(const char* name); -ARG_EXTERN void arg_set_module_version(int major, int minor, int patch, const char* tag); - -/**** deprecated functions, for back-compatibility only ********/ -ARG_EXTERN void arg_free(void** argtable); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/examples/CMakeLists.txt b/parallel/parallel_src/extern/argtable3-3.2.2/examples/CMakeLists.txt deleted file mode 100644 index 67e22218..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/examples/CMakeLists.txt +++ /dev/null @@ -1,54 +0,0 @@ -################################################################################ -# This file is part of the argtable3 library. -# -# Copyright (C) 2016-2021 Tom G. Huang -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of STEWART HEITMANN nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, -# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -################################################################################ - -if(ARGTABLE3_ENABLE_ARG_REX_DEBUG) - add_definitions(-DARG_REX_DEBUG) -endif() - -if(NOT ARGTABLE3_REPLACE_GETOPT) - add_definitions(-DARG_REPLACE_GETOPT=0) -endif() - -if(ARGTABLE3_LONG_ONLY) - add_definitions(-DARG_LONG_ONLY) -endif() - -file(GLOB EXAMPLES_SOURCES RELATIVE ${PROJECT_SOURCE_DIR}/examples *.c) - -if(UNIX) - set(ARGTABLE3_EXTRA_LIBS m) -endif() - -foreach(examples_src ${EXAMPLES_SOURCES}) - string(REPLACE ".c" "" examplename ${examples_src}) - add_executable(${examplename} ${PROJECT_SOURCE_DIR}/examples/${examples_src}) - target_include_directories(${examplename} PRIVATE ${PROJECT_SOURCE_DIR}/src) - target_link_libraries(${examplename} argtable3 ${ARGTABLE3_EXTRA_LIBS}) -endforeach() diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/examples/echo.c b/parallel/parallel_src/extern/argtable3-3.2.2/examples/echo.c deleted file mode 100644 index fb13f3b4..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/examples/echo.c +++ /dev/null @@ -1,128 +0,0 @@ -/******************************************************************************* - * Example source code for using the argtable3 library to implement: - * - * echo [-neE] [--help] [--version] [STRING]... - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -/* Here we only approximate the echo functionality */ -void mymain(int n, int e, int E, const char** strings, int nstrings) - { - int j; - - printf("option -n = %s\n", ((n)?"YES":"NO")); - printf("option -e = %s\n", ((e)?"YES":"NO")); - printf("option -E = %s\n", ((E)?"YES":"NO")); - for (j=0; js"); - struct arg_lit *help = arg_lit0(NULL,"help", "print this help and exit"); - struct arg_lit *vers = arg_lit0(NULL,"version", "print version information and exit"); - struct arg_str *strs = arg_strn(NULL,NULL,"STRING",0,argc+2,NULL); - struct arg_end *end = arg_end(20); - void* argtable[] = {n,e,E,help,vers,strs,end}; - const char* progname = "echo"; - int exitcode=0; - int nerrors; - - /* verify the argtable[] entries were allocated sucessfully */ - if (arg_nullcheck(argtable) != 0) - { - /* NULL entries were detected, some allocations must have failed */ - printf("%s: insufficient memory\n",progname); - exitcode=1; - goto exit; - } - - /* Parse the command line as defined by argtable[] */ - nerrors = arg_parse(argc,argv,argtable); - - /* special case: '--help' takes precedence over error reporting */ - if (help->count > 0) - { - printf("Usage: %s", progname); - arg_print_syntax(stdout,argtable,"\n"); - printf("Echo the STRINGs to standard output.\n\n"); - arg_print_glossary(stdout,argtable," %-10s %s\n"); - printf("\nWithout -E, the following sequences are recognized and interpolated:\n\n" - " \\NNN the character whose ASCII code is NNN (octal)\n" - " \\\\ backslash\n" - " \\a alert (BEL)\n" - " \\b backspace\n" - " \\c suppress trailing newline\n" - " \\f form feed\n" - " \\n new line\n" - " \\r carriage return\n" - " \\t horizontal tab\n" - " \\v vertical tab\n\n" - "Report bugs to .\n"); - exitcode=0; - goto exit; - } - - /* special case: '--version' takes precedence error reporting */ - if (vers->count > 0) - { - printf("'%s' example program for the \"argtable\" command line argument parser.\n",progname); - printf("September 2003, Stewart Heitmann\n"); - exitcode=0; - goto exit; - } - - /* If the parser returned any errors then display them and exit */ - if (nerrors > 0) - { - /* Display the error details contained in the arg_end struct.*/ - arg_print_errors(stdout,end,progname); - printf("Try '%s --help' for more information.\n",progname); - exitcode=1; - goto exit; - } - - /* Command line parsing is complete, do the main processing */ - mymain(n->count, e->count, E->count, strs->sval, strs->count); - - exit: - /* deallocate each non-null entry in argtable[] */ - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - - return exitcode; - } diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/examples/ls.c b/parallel/parallel_src/extern/argtable3-3.2.2/examples/ls.c deleted file mode 100644 index c7317a88..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/examples/ls.c +++ /dev/null @@ -1,326 +0,0 @@ -/******************************************************************************* - * Example source code for using the argtable3 library to implement: - * - * ls [-aAbBcCdDfFgGhHiklLmnNopqQrRsStuUvxX1] [--author] - * [--block-size=SIZE] [--color=[WHEN]] [--format=WORD] [--full-time] - * [--si] [--dereference-command-line-symlink-to-dir] [--indicator-style=WORD] - * [-I PATTERN] [--show-control-chars] [--quoting-style=WORD] [--sort=WORD] - * [--time=WORD] [--time-style=STYLE] [-T COLS] [-w COLS] [--help] - * [--version] [FILE]... - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -/* These variables hold the values parsed from the comand line by arg_parse() */ -struct arg_lit *a, *A, *author, *b, *B, *c, *C, *d, *D, *f, *F, *fulltime; -struct arg_lit *g, *G, *h, *H, *si, *deref, *i, *k, *l, *L, *m, *n, *N, *o, *p; -struct arg_lit *q, *shcont, *Q, *r, *R, *s, *S, *t, *u, *U, *v, *x, *X, *one; -struct arg_lit *help, *version; -struct arg_int *blocksize, *T, *w; -struct arg_str *color, *format, *indic, *I, *Qstyle, *sort, *Time, *timesty; -struct arg_file *files; -struct arg_end *end; - -/* Here we simply echo the command line option values as a demonstration. */ -/* In a real program, this is where we would perform the main processing. */ -int mymain(void) - { - int j; - - if (a->count > 0) - printf("a=YES\n"); - if (A->count > 0) - printf("A=YES\n"); - if (author->count > 0) - printf("author=YES\n"); - if (b->count > 0) - printf("b=YES\n"); - if (blocksize->count > 0) - printf("blocksize=%d\n",blocksize->count); - if (B->count > 0) - printf("B=YES\n"); - if (c->count > 0) - printf("c=YES\n"); - if (C->count > 0) - printf("C=YES\n"); - if (color->count > 0) - printf("color=%s\n",color->sval[0]); - if (d->count > 0) - printf("d=YES\n"); - if (D->count > 0) - printf("D=YES\n"); - if (f->count > 0) - printf("f=YES\n"); - if (F->count > 0) - printf("F=YES\n"); - if (format->count > 0) - printf("format=%s\n",format->sval[0]); - if (fulltime->count > 0) - printf("fulltime=YES\n"); - if (g->count > 0) - printf("g=YES\n"); - if (G->count > 0) - printf("G=YES\n"); - if (h->count > 0) - printf("h=YES\n"); - if (si->count > 0) - printf("si=YES\n"); - if (H->count > 0) - printf("H=YES\n"); - if (deref->count > 0) - printf("deref=YES\n"); - if (indic->count > 0) - printf("indic=%s\n",indic->sval[0]); - if (i->count > 0) - printf("i=YES\n"); - if (I->count > 0) - printf("I=%s\n",I->sval[0]); - if (k->count > 0) - printf("k=YES\n"); - if (l->count > 0) - printf("l=YES\n"); - if (L->count > 0) - printf("L=YES\n"); - if (m->count > 0) - printf("m=YES\n"); - if (n->count > 0) - printf("n=YES\n"); - if (N->count > 0) - printf("N=YES\n"); - if (o->count > 0) - printf("o=YES\n"); - if (p->count > 0) - printf("p=YES\n"); - if (q->count > 0) - printf("q=YES\n"); - if (shcont->count > 0) - printf("shcont=YES\n"); - if (Q->count > 0) - printf("Q=YES\n"); - if (Qstyle->count > 0) - printf("Qstyle=%s\n",Qstyle->sval[0]); - if (r->count > 0) - printf("r=YES\n"); - if (R->count > 0) - printf("R=YES\n"); - if (s->count > 0) - printf("s=YES\n"); - if (S->count > 0) - printf("S=YES\n"); - if (sort->count > 0) - printf("sort=%s\n",sort->sval[0]); - if (Time->count > 0) - printf("time=%s\n",Time->sval[0]); - if (timesty->count > 0) - printf("timesty=%s\n",timesty->sval[0]); - if (t->count > 0) - printf("t=YES\n"); - if (T->count > 0) - printf("T=%d\n",T->ival[0]); - if (u->count > 0) - printf("u=YES\n"); - if (U->count > 0) - printf("U=YES\n"); - if (v->count > 0) - printf("v=YES\n"); - if (w->count > 0) - printf("w=%d\n",w->ival[0]); - if (x->count > 0) - printf("x=YES\n"); - if (X->count > 0) - printf("X=YES\n"); - if (one->count > 0) - printf("1=YES\n"); - - /* print the filenames */ - for (j=0; jcount; j++) - printf("filename[%d] = \"%s\"\n", j, files->filename[j]); - - return 0; - } - - -int main(int argc, char **argv) - { - /* The argtable[] entries define the command line options */ - void *argtable[] = { - a = arg_lit0("a", "all", "do not hide entries starting with ."), - A = arg_lit0("A", "almost-all", "do not list implied . and .."), - author = arg_lit0(NULL,"author", "print the author of each file"), - b = arg_lit0("b", "escape", "print octal escapes for nongraphic characters"), - blocksize = arg_int0(NULL,"block-size","SIZE", "use SIZE-byte blocks"), - B = arg_lit0("B", "ignore-backups", "do not list implied entries ending with ~"), - c = arg_lit0("c", NULL, "with -lt: sort by, and show, ctime (time of last"), - arg_rem(NULL, " modification of file status information)"), - arg_rem(NULL, " with -l: show ctime and sort by name"), - arg_rem(NULL, " otherwise: sort by ctime"), - C = arg_lit0("C", NULL, "list entries by columns"), - color = arg_str0(NULL,"color","WHEN", "control whether color is used to distinguish file"), - arg_rem(NULL, " types. WHEN may be `never', `always', or `auto'"), - d = arg_lit0("d", "directory", "list directory entries instead of contents,"), - arg_rem(NULL, " and do not dereference symbolic links"), - D = arg_lit0("D", "dired", "generate output designed for Emacs' dired mode"), - f = arg_lit0("f", NULL, "do not sort, enable -aU, disable -lst"), - F = arg_lit0("F", "classify", "append indicator (one of */=@|) to entries"), - format = arg_str0(NULL,"format","WORD", "across -x, commas -m, horizontal -x, long -l,"), - arg_rem (NULL, " single-column -1, verbose -l, vertical -C"), - fulltime = arg_lit0(NULL,"full-time", "like -l --time-style=full-iso"), - g = arg_lit0("g", NULL, "like -l, but do not list owner"), - G = arg_lit0("G", "no-group", "inhibit display of group information"), - h = arg_lit0("h", "human-readable", "print sizes in human readable format (e.g., 1K 234M 2G)"), - si = arg_lit0(NULL,"si", "likewise, but use powers of 1000 not 1024"), - H = arg_lit0("H", "dereference-command-line","follow symbolic links listed on the command line"), - deref = arg_lit0(NULL,"dereference-command-line-symlink-to-dir","follow each command line symbolic link"), - arg_rem(NULL, " that points to a directory"), - indic = arg_str0(NULL,"indicator-style","WORD","append indicator with style WORD to entry names:"), - arg_rem (NULL, " none (default), classify (-F), file-type (-p)"), - i = arg_lit0("i", "inode", "print index number of each file"), - I = arg_str0("I", "ignore","PATTERN", "do not list implied entries matching shell PATTERN"), - k = arg_lit0("k", NULL, "like --block-size=1K"), - l = arg_lit0("l", NULL, "use a long listing format"), - L = arg_lit0("L", "dereference", "when showing file information for a symbolic"), - arg_rem (NULL, " link, show information for the file the link"), - arg_rem (NULL, " references rather than for the link itself"), - m = arg_lit0("m", NULL, "fill width with a comma separated list of entries"), - n = arg_lit0("n", "numeric-uid-gid", "like -l, but list numeric UIDs and GIDs"), - N = arg_lit0("N", "literal", "print raw entry names (don't treat e.g. control"), - arg_rem (NULL, " characters specially)"), - o = arg_lit0("o", NULL, "like -l, but do not list group information"), - p = arg_lit0("p", "file-type", "append indicator (one of /=@|) to entries"), - q = arg_lit0("q", "hide-control-chars", "print ? instead of non graphic characters"), - shcont = arg_lit0(NULL,"show-control-chars", "show non graphic characters as-is (default"), - arg_rem (NULL, "unless program is `ls' and output is a terminal)"), - Q = arg_lit0("Q", "quote-name", "enclose entry names in double quotes"), - Qstyle = arg_str0(NULL,"quoting-style","WORD","use quoting style WORD for entry names:"), - arg_rem (NULL, " literal, locale, shell, shell-always, c, escape"), - r = arg_lit0("r", "reverse", "reverse order while sorting"), - R = arg_lit0("R", "recursive", "list subdirectories recursively"), - s = arg_lit0("s", "size", "print size of each file, in blocks"), - S = arg_lit0("S", NULL, "sort by file size"), - sort = arg_str0(NULL,"sort","WORD", "extension -X, none -U, size -S, time -t, version -v,"), - arg_rem (NULL, "status -c, time -t, atime -u, access -u, use -u"), - Time = arg_str0(NULL,"time","WORD", "show time as WORD instead of modification time:"), - arg_rem (NULL, " atime, access, use, ctime or status; use"), - arg_rem (NULL, " specified time as sort key if --sort=time"), - timesty = arg_str0(NULL, "time-style","STYLE", "show times using style STYLE:"), - arg_rem (NULL, " full-iso, long-iso, iso, locale, +FORMAT"), - arg_rem (NULL, "FORMAT is interpreted like `date'; if FORMAT is"), - arg_rem (NULL, "FORMAT1FORMAT2, FORMAT1 applies to"), - arg_rem (NULL, "non-recent files and FORMAT2 to recent files;"), - arg_rem (NULL, "if STYLE is prefixed with `posix-', STYLE"), - arg_rem (NULL, "takes effect only outside the POSIX locale"), - t = arg_lit0("t", NULL, "sort by modification time"), - T = arg_int0("T", "tabsize", "COLS", "assume tab stops at each COLS instead of 8"), - u = arg_lit0("u", NULL, "with -lt: sort by, and show, access time"), - arg_rem (NULL, " with -l: show access time and sort by name"), - arg_rem (NULL, " otherwise: sort by access time"), - U = arg_lit0("U", NULL, "do not sort; list entries in directory order"), - v = arg_lit0("v", NULL, "sort by version"), - w = arg_int0("w", "width", "COLS", "assume screen width instead of current value"), - x = arg_lit0("x", NULL, "list entries by lines instead of by columns"), - X = arg_lit0("X", NULL, "sort alphabetically by entry extension"), - one = arg_lit0("1", NULL, "list one file per line"), - help = arg_lit0(NULL,"help", "display this help and exit"), - version = arg_lit0(NULL,"version", "display version information and exit"), - files = arg_filen(NULL, NULL, "FILE", 0, argc+2, NULL), - end = arg_end(20), - }; - const char *progname = "ls"; - int exitcode=0; - int nerrors; - - /* verify the argtable[] entries were allocated sucessfully */ - if (arg_nullcheck(argtable) != 0) - { - /* NULL entries were detected, some allocations must have failed */ - printf("%s: insufficient memory\n",progname); - exitcode=1; - goto exit; - } - - /* allow optional argument values for --color */ - /* and set the default value to "always" */ - color->hdr.flag |= ARG_HASOPTVALUE; - color->sval[0] = "always"; - - /* Parse the command line as defined by argtable[] */ - nerrors = arg_parse(argc,argv,argtable); - - /* special case: '--help' takes precedence over error reporting */ - if (help->count > 0) - { - printf("Usage: %s", progname); - arg_print_syntax(stdout,argtable,"\n"); - printf("List information about the FILE(s) (the current directory by default).\n"); - printf("Sort entries alphabetically if none of -cftuSUX nor --sort.\n\n"); - arg_print_glossary(stdout,argtable," %-25s %s\n"); - printf("\nSIZE may be (or may be an integer optionally followed by) one of following:\n" - "kB 1000, K 1024, MB 1,000,000, M 1,048,576, and so on for G, T, P, E, Z, Y.\n\n" - "By default, color is not used to distinguish types of files. That is\n" - "equivalent to using --color=none. Using the --color option without the\n" - "optional WHEN argument is equivalent to using --color=always. With\n" - "--color=auto, color codes are output only if standard output is connected\n" - "to a terminal (tty).\n\n" - "Report bugs to .\n"); - exitcode=0; - goto exit; - } - - /* special case: '--version' takes precedence error reporting */ - if (version->count > 0) - { - printf("'%s' example program for the \"argtable\" command line argument parser.\n",progname); - printf("September 2003, Stewart Heitmann\n"); - exitcode=0; - goto exit; - } - - /* If the parser returned any errors then display them and exit */ - if (nerrors > 0) - { - /* Display the error details contained in the arg_end struct.*/ - arg_print_errors(stdout,end,progname); - printf("Try '%s --help' for more information.\n",progname); - exitcode=1; - goto exit; - } - - /* Command line parsing is complete, do the main processing */ - exitcode = mymain(); - -exit: - /* deallocate each non-null entry in argtable[] */ - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - - return exitcode; - } - - diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/examples/multisyntax.c b/parallel/parallel_src/extern/argtable3-3.2.2/examples/multisyntax.c deleted file mode 100644 index 45b2cd29..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/examples/multisyntax.c +++ /dev/null @@ -1,239 +0,0 @@ -/******************************************************************************* - * Example source code for using the argtable3 library to implement - * a multi-syntax command line argument program - * - * usage 1: multisyntax [-nvR] insert []... [-o ] - * usage 2: multisyntax [-nv] remove - * usage 3: multisyntax [-v] search [-o ] - * usage 4: multisyntax [--help] [--version] - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -#define REG_EXTENDED 1 -#define REG_ICASE (REG_EXTENDED << 1) - -/* mymain1 implements the actions for syntax 1 */ -int mymain1(int n, int v, int R, const char *outfile, - const char **infiles, int ninfiles) - { - int i; - printf("syntax 1 matched OK:\n"); - printf("n=%d\n", n); - printf("v=%d\n", v); - printf("R=%d\n", R); - printf("outfile=\"%s\"\n", outfile); - for (i=0; i [file]... -o */ - struct arg_rex *cmd1 = arg_rex1(NULL, NULL, "insert", NULL, REG_ICASE, NULL); - struct arg_lit *noact1 = arg_lit0("n", NULL, "take no action"); - struct arg_lit *verbose1 = arg_lit0("v", "verbose", "verbose messages"); - struct arg_lit *recurse1 = arg_lit0("R", NULL, "recurse through subdirectories"); - struct arg_file *infiles1 = arg_filen(NULL, NULL, NULL, 1,argc+2, "input file(s)"); - struct arg_file *outfile1 = arg_file0("o", NULL, "", "output file (default is \"-\")"); - struct arg_end *end1 = arg_end(20); - void* argtable1[] = {cmd1,noact1,verbose1,recurse1,infiles1,outfile1,end1}; - int nerrors1; - - /* SYNTAX 2: remove [-nv] */ - struct arg_rex *cmd2 = arg_rex1(NULL, NULL, "remove", NULL, REG_ICASE, NULL); - struct arg_lit *noact2 = arg_lit0("n", NULL, NULL); - struct arg_lit *verbose2 = arg_lit0("v", "verbose", NULL); - struct arg_file *infiles2 = arg_file1(NULL, NULL, NULL, NULL); - struct arg_end *end2 = arg_end(20); - void* argtable2[] = {cmd2,noact2,verbose2,infiles2,end2}; - int nerrors2; - - /* SYNTAX 3: search [-v] [-o ] [--help] [--version] */ - struct arg_rex *cmd3 = arg_rex1(NULL, NULL, "search", NULL, REG_ICASE, NULL); - struct arg_lit *verbose3 = arg_lit0("v", "verbose", NULL); - struct arg_str *pattern3 = arg_str1(NULL, NULL, "", "search string"); - struct arg_file *outfile3 = arg_file0("o", NULL, "", NULL); - struct arg_end *end3 = arg_end(20); - void* argtable3[] = {cmd3,verbose3,pattern3,outfile3,end3}; - int nerrors3; - - /* SYNTAX 4: [-help] [-version] */ - struct arg_lit *help4 = arg_lit0(NULL,"help", "print this help and exit"); - struct arg_lit *version4 = arg_lit0(NULL,"version", "print version information and exit"); - struct arg_end *end4 = arg_end(20); - void* argtable4[] = {help4,version4,end4}; - int nerrors4; - - const char* progname = "multisyntax"; - int exitcode=0; - - /* verify all argtable[] entries were allocated sucessfully */ - if (arg_nullcheck(argtable1)!=0 || - arg_nullcheck(argtable2)!=0 || - arg_nullcheck(argtable3)!=0 || - arg_nullcheck(argtable4)!=0 ) - { - /* NULL entries were detected, some allocations must have failed */ - printf("%s: insufficient memory\n",progname); - exitcode=1; - goto exit; - } - - /* set any command line default values prior to parsing */ - outfile1->filename[0]="-"; - outfile3->filename[0]="-"; - - /* Above we defined a separate argtable for each possible command line syntax */ - /* and here we parse each one in turn to see if any of them are successful */ - nerrors1 = arg_parse(argc,argv,argtable1); - nerrors2 = arg_parse(argc,argv,argtable2); - nerrors3 = arg_parse(argc,argv,argtable3); - nerrors4 = arg_parse(argc,argv,argtable4); - - /* Execute the appropriate main routine for the matching command line syntax */ - /* In this example program our alternate command line syntaxes are mutually */ - /* exclusive, so we know in advance that only one of them can be successful. */ - if (nerrors1==0) - exitcode = mymain1(noact1->count, verbose1->count, recurse1->count, - outfile1->filename[0], infiles1->filename, infiles1->count); - else if (nerrors2==0) - exitcode = mymain2(noact2->count, verbose2->count, infiles2->filename[0]); - else if (nerrors3==0) - exitcode = mymain3(verbose3->count, pattern3->sval[0], outfile3->filename[0]); - else if (nerrors4==0) - exitcode = mymain4(help4->count, version4->count, progname, - argtable1, argtable2, argtable3, argtable4); - else - { - /* We get here if the command line matched none of the possible syntaxes */ - if (cmd1->count > 0) - { - /* here the cmd1 argument was correct, so presume syntax 1 was intended target */ - arg_print_errors(stdout,end1,progname); - printf("usage: %s ", progname); - arg_print_syntax(stdout,argtable1,"\n"); - } - else if (cmd2->count > 0) - { - /* here the cmd2 argument was correct, so presume syntax 2 was intended target */ - arg_print_errors(stdout,end2,progname); - printf("usage: %s ", progname); - arg_print_syntax(stdout,argtable2,"\n"); - } - else if (cmd3->count > 0) - { - /* here the cmd3 argument was correct, so presume syntax 3 was intended target */ - arg_print_errors(stdout,end3,progname); - printf("usage: %s ", progname); - arg_print_syntax(stdout,argtable3,"\n"); - } - else - { - /* no correct cmd literals were given, so we cant presume which syntax was intended */ - printf("%s: missing command.\n",progname); - printf("usage 1: %s ", progname); arg_print_syntax(stdout,argtable1,"\n"); - printf("usage 2: %s ", progname); arg_print_syntax(stdout,argtable2,"\n"); - printf("usage 3: %s ", progname); arg_print_syntax(stdout,argtable3,"\n"); - printf("usage 4: %s", progname); arg_print_syntax(stdout,argtable4,"\n"); - } - } - -exit: - /* deallocate each non-null entry in each argtable */ - arg_freetable(argtable1,sizeof(argtable1)/sizeof(argtable1[0])); - arg_freetable(argtable2,sizeof(argtable2)/sizeof(argtable2[0])); - arg_freetable(argtable3,sizeof(argtable3)/sizeof(argtable3[0])); - arg_freetable(argtable4,sizeof(argtable4)/sizeof(argtable4[0])); - - return exitcode; - } diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/examples/mv.c b/parallel/parallel_src/extern/argtable3-3.2.2/examples/mv.c deleted file mode 100644 index 85048223..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/examples/mv.c +++ /dev/null @@ -1,179 +0,0 @@ -/******************************************************************************* - * Example source code for using the argtable3 library to implement: - * - * mv [-bfiuv] [--backup=[CONTROL]] [--reply={yes,no,query}] - * [--strip-trailing-slashes] [-S SUFFIX] [--target-directory=DIRECTORY] - * [--help] [--version] SOURCE [SOURCE]... DEST|DIRECTORY - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -int mymain(const char *backup_control, - int backup, - int force, - int interactive, - const char *reply, - int strip_trailing_slashes, - const char *suffix, - const char *targetdir, - int update, - int verbose, - const char **files, - int nfiles) - { - int j; - - /* if verbose option was given then display all option settings */ - if (verbose) - { - printf("backup = %s\n", ((backup)?"YES":"NO")); - printf("backup CONTROL = %s\n", backup_control); - printf("force = %s\n", ((force)?"YES":"NO")); - printf("interactive mode = %s\n", ((interactive)?"YES":"NO")); - printf("reply = %s\n", reply); - printf("strip-trailing-slashes = %s\n", ((strip_trailing_slashes)?"YES":"NO")); - printf("suffix = %s\n", suffix); - printf("target-directory = %s\n", targetdir); - printf("update = %s\n", ((update)?"YES":"NO")); - printf("verbose = %s\n", ((verbose)?"YES":"NO")); - } - - /* print the source filenames */ - for (j=0; jsval[0] = "existing"; /* --backup={none,off,numbered,t,existing,nil,simple,never} */ - suffix->sval[0] = "~"; /* --suffix=~ */ - reply->sval[0] = "query"; /* --reply={yes,no,query} */ - targetd->sval[0] = NULL; - - /* Parse the command line as defined by argtable[] */ - nerrors = arg_parse(argc,argv,argtable); - - /* special case: '--help' takes precedence over error reporting */ - if (help->count > 0) - { - printf("Usage: %s", progname); - arg_print_syntax(stdout,argtable,"\n"); - printf("Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\n"); - arg_print_glossary(stdout,argtable," %-30s %s\n"); - printf("\nThe backup suffix is \"~\", unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n" - "The version control method may be selected via the --backup option or through\n" - "the VERSION_CONTROL environment variable. Here are the values:\n\n" - " none, off never make backups (even if --backup is given)\n" - " numbered, t make numbered backups\n" - " existing, nil numbered if numbered backups exist, simple otherwise\n" - " simple, never always make simple backups\n\n" - "Report bugs to .\n"); - exitcode=0; - goto exit; - } - - /* special case: '--version' takes precedence error reporting */ - if (version->count > 0) - { - printf("'%s' example program for the \"argtable\" command line argument parser.\n",progname); - printf("September 2003, Stewart Heitmann\n"); - exitcode=0; - goto exit; - } - - /* If the parser returned any errors then display them and exit */ - if (nerrors > 0) - { - /* Display the error details contained in the arg_end struct.*/ - arg_print_errors(stdout,end,progname); - printf("Try '%s --help' for more information.\n",progname); - exitcode=1; - goto exit; - } - - /* Command line parsing is complete, do the main processing */ - exitcode = mymain(backupc->sval[0], - backup->count, - force->count, - interact->count, - reply->sval[0], - strpslsh->count, - suffix->sval[0], - targetd->sval[0], - update->count, - verbose->count, - files->filename, - files->count); - -exit: - /* deallocate each non-null entry in argtable[] */ - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - - return exitcode; - } diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/examples/myprog.c b/parallel/parallel_src/extern/argtable3-3.2.2/examples/myprog.c deleted file mode 100644 index b705cf44..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/examples/myprog.c +++ /dev/null @@ -1,147 +0,0 @@ -/******************************************************************************* - * Example source code for using the argtable3 library to implement: - * - * myprog [-lRv] [-k ] [-D MACRO]... [-o ] [--help] - * [--version] []... - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include - -int mymain(int l, int R, int k, - const char **defines, int ndefines, - const char *outfile, - int v, - const char **infiles, int ninfiles) - { - int i; - - if (l>0) printf("list files (-l)\n"); - if (R>0) printf("recurse through directories (-R)\n"); - if (v>0) printf("verbose is enabled (-v)\n"); - printf("scalar k=%d\n",k); - printf("output is \"%s\"\n", outfile); - - for (i=0; i", "output file (default is \"-\")"); - struct arg_lit *verbose = arg_lit0("v","verbose,debug", "verbose messages"); - struct arg_lit *help = arg_lit0(NULL,"help", "print this help and exit"); - struct arg_lit *version = arg_lit0(NULL,"version", "print version information and exit"); - struct arg_file *infiles = arg_filen(NULL,NULL,NULL,1,argc+2, "input file(s)"); - struct arg_end *end = arg_end(20); - void* argtable[] = {list,recurse,repeat,defines,outfile,verbose,help,version,infiles,end}; - const char* progname = "myprog"; - int nerrors; - int exitcode=0; - - /* verify the argtable[] entries were allocated sucessfully */ - if (arg_nullcheck(argtable) != 0) - { - /* NULL entries were detected, some allocations must have failed */ - printf("%s: insufficient memory\n",progname); - exitcode=1; - goto exit; - } - - /* set any command line default values prior to parsing */ - repeat->ival[0]=3; - outfile->filename[0]="-"; - - /* Parse the command line as defined by argtable[] */ - nerrors = arg_parse(argc,argv,argtable); - - /* special case: '--help' takes precedence over error reporting */ - if (help->count > 0) - { - printf("Usage: %s", progname); - arg_print_syntax(stdout,argtable,"\n"); - printf("This program demonstrates the use of the argtable2 library\n"); - printf("for parsing command line arguments. Argtable accepts integers\n"); - printf("in decimal (123), hexadecimal (0xff), octal (0o123) and binary\n"); - printf("(0b101101) formats. Suffixes KB, MB and GB are also accepted.\n"); - arg_print_glossary(stdout,argtable," %-25s %s\n"); - exitcode=0; - goto exit; - } - - /* special case: '--version' takes precedence error reporting */ - if (version->count > 0) - { - printf("'%s' example program for the \"argtable\" command line argument parser.\n",progname); - printf("September 2003, Stewart Heitmann\n"); - exitcode=0; - goto exit; - } - - /* If the parser returned any errors then display them and exit */ - if (nerrors > 0) - { - /* Display the error details contained in the arg_end struct.*/ - arg_print_errors(stdout,end,progname); - printf("Try '%s --help' for more information.\n",progname); - exitcode=1; - goto exit; - } - - /* special case: uname with no command line options induces brief help */ - if (argc==1) - { - printf("Try '%s --help' for more information.\n",progname); - exitcode=0; - goto exit; - } - - /* normal case: take the command line options at face value */ - exitcode = mymain(list->count, recurse->count, repeat->ival[0], - defines->sval, defines->count, - outfile->filename[0], verbose->count, - infiles->filename, infiles->count); - - exit: - /* deallocate each non-null entry in argtable[] */ - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - - return exitcode; - } diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/examples/myprog_C89.c b/parallel/parallel_src/extern/argtable3-3.2.2/examples/myprog_C89.c deleted file mode 100644 index 21d03883..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/examples/myprog_C89.c +++ /dev/null @@ -1,165 +0,0 @@ -/******************************************************************************* - * This example source code is an alternate version of myprog.c - * that adheres to ansi C89 standards rather than ansi C99. - * The only difference being that C89 does not permit the argtable array - * to be statically initialized with the contents of variables set at - * runtime whereas C99 does. - * Hence we cannot declare and initialize the argtable array in one declaration - * as - * void* argtable[] = {list, recurse, repeat, defines, outfile, verbose, - * help, version, infiles, end}; - * Instead, we must declare - * void* argtable[10]; - * and initialize the contents of the array separately. - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -int mymain(int l, int R, int k, - const char **defines, int ndefines, - const char *outfile, - int v, - const char **infiles, int ninfiles) - { - int i; - - if (l>0) printf("list files (-l)\n"); - if (R>0) printf("recurse through directories (-R)\n"); - if (v>0) printf("verbose is enabled (-v)\n"); - printf("scalar k=%d\n",k); - printf("output is \"%s\"\n", outfile); - - for (i=0; i", "output file (default is \"-\")"); - struct arg_lit *verbose = arg_lit0("v","verbose,debug", "verbose messages"); - struct arg_lit *help = arg_lit0(NULL,"help", "print this help and exit"); - struct arg_lit *version = arg_lit0(NULL,"version", "print version information and exit"); - struct arg_file *infiles = arg_filen(NULL,NULL,NULL,1,argc+2, "input file(s)"); - struct arg_end *end = arg_end(20); - void* argtable[10]; - const char* progname = "myprog_C89"; - int nerrors; - int exitcode=0; - - /* initialize the argtable array with ptrs to the arg_xxx structures constructed above */ - argtable[0] = list; - argtable[1] = recurse; - argtable[2] = repeat; - argtable[3] = defines; - argtable[4] = outfile; - argtable[5] = verbose; - argtable[6] = help; - argtable[7] = version; - argtable[8] = infiles; - argtable[9] = end; - - /* verify the argtable[] entries were allocated sucessfully */ - if (arg_nullcheck(argtable) != 0) - { - /* NULL entries were detected, some allocations must have failed */ - printf("%s: insufficient memory\n",progname); - exitcode=1; - goto exit; - } - - /* set any command line default values prior to parsing */ - repeat->ival[0]=3; - outfile->filename[0]="-"; - - /* Parse the command line as defined by argtable[] */ - nerrors = arg_parse(argc,argv,argtable); - - /* special case: '--help' takes precedence over error reporting */ - if (help->count > 0) - { - printf("Usage: %s", progname); - arg_print_syntax(stdout,argtable,"\n"); - printf("This program demonstrates the use of the argtable2 library\n"); - printf("for parsing command line arguments.\n"); - arg_print_glossary(stdout,argtable," %-25s %s\n"); - exitcode=0; - goto exit; - } - - /* special case: '--version' takes precedence error reporting */ - if (version->count > 0) - { - printf("'%s' example program for the \"argtable\" command line argument parser.\n",progname); - printf("September 2003, Stewart Heitmann\n"); - exitcode=0; - goto exit; - } - - /* If the parser returned any errors then display them and exit */ - if (nerrors > 0) - { - /* Display the error details contained in the arg_end struct.*/ - arg_print_errors(stdout,end,progname); - printf("Try '%s --help' for more information.\n",progname); - exitcode=1; - goto exit; - } - - /* special case: uname with no command line options induces brief help */ - if (argc==1) - { - printf("Try '%s --help' for more information.\n",progname); - exitcode=0; - goto exit; - } - - /* normal case: take the command line options at face value */ - exitcode = mymain(list->count, recurse->count, repeat->ival[0], - defines->sval, defines->count, - outfile->filename[0], verbose->count, - infiles->filename, infiles->count); - - exit: - /* deallocate each non-null entry in argtable[] */ - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - - return exitcode; - } diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/examples/testargtable3.c b/parallel/parallel_src/extern/argtable3-3.2.2/examples/testargtable3.c deleted file mode 100644 index 91bef679..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/examples/testargtable3.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "argtable3.h" - -/* global arg_xxx structs */ -struct arg_lit *a, *b, *c, *verb, *help, *version; -struct arg_int *scal; -struct arg_file *o, *file; -struct arg_end *end; - -int main(int argc, char *argv[]) -{ - /* the global arg_xxx structs are initialised within the argtable */ - void *argtable[] = { - help = arg_lit0(NULL, "help", "display this help and exit"), - version = arg_lit0(NULL, "version", "display version info and exit"), - a = arg_lit0("a", NULL,"the -a option"), - b = arg_lit0("b", NULL, "the -b option"), - c = arg_lit0("c", NULL, "the -c option"), - scal = arg_int0(NULL, "scalar", "", "foo value"), - verb = arg_lit0("v", "verbose", "verbose output"), - o = arg_file0("o", NULL, "myfile", "output file"), - file = arg_filen(NULL, NULL, "", 1, 100, "input files"), - end = arg_end(20), - }; - - int exitcode = 0; - char progname[] = "testargtable2.exe"; - - int nerrors; - nerrors = arg_parse(argc,argv,argtable); - - /* special case: '--help' takes precedence over error reporting */ - if (help->count > 0) - { - printf("Usage: %s", progname); - arg_print_syntax(stdout, argtable, "\n"); - printf("List information about the FILE(s) " - "(the current directory by default).\n\n"); - arg_print_glossary(stdout, argtable, " %-25s %s\n"); - exitcode = 0; - goto exit; - } - - /* If the parser returned any errors then display them and exit */ - if (nerrors > 0) - { - /* Display the error details contained in the arg_end struct.*/ - arg_print_errors(stdout, end, progname); - printf("Try '%s --help' for more information.\n", progname); - exitcode = 1; - goto exit; - } - -exit: - /* deallocate each non-null entry in argtable[] */ - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); - return exitcode; -} - diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/examples/uname.c b/parallel/parallel_src/extern/argtable3-3.2.2/examples/uname.c deleted file mode 100644 index e8e3f9a6..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/examples/uname.c +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************************************* - * Example source code for using the argtable3 library to implement: - * - * uname [-asnrvmpio] [--help] [--version] - * - * This file is part of the argtable3 library. - * - * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include "argtable3.h" - -/* Here we simulate the uname functionality */ -int mymain(int kname, int nname, int krel, int kver, int mach, int proc, int hard, int opsys) - { - if (kname) printf("Linux "); - if (nname) printf("localhost.localdomain "); - if (krel) printf("2.4.19-16 "); - if (kver) printf("#1 Fri Sep 20 18:15:05 CEST 2002 "); - if (mach) printf("i686 "); - if (proc) printf("Intel "); - if (hard) printf("unknown "); - if (opsys) printf("GNU/Linux "); - printf("\n"); - return 0; - } - - -int main(int argc, char **argv) - { - const char* progname = "uname"; - struct arg_lit *all = arg_lit0("a", "all", "print all information, in the following order:"); - struct arg_lit *kname = arg_lit0("s", "kernel-name", "print the kernel name"); - struct arg_lit *nname = arg_lit0("n", "nodename", "print the node name"); - struct arg_lit *krel = arg_lit0("r", "kernel-release", "print the kernel release"); - struct arg_lit *kver = arg_lit0("v", "kernel-version", "print the kernel version"); - struct arg_lit *mach = arg_lit0("m", "machine", "print the machine hardware name"); - struct arg_lit *proc = arg_lit0("p", "processor", "print the processor type"); - struct arg_lit *hard = arg_lit0("i", "hardware-platform","print the hardware platform"); - struct arg_lit *opsys = arg_lit0("o", "operating-system", "print the operating system"); - struct arg_lit *help = arg_lit0(NULL,"help", "print this help and exit"); - struct arg_lit *vers = arg_lit0(NULL,"version", "print version information and exit"); - struct arg_end *end = arg_end(20); - void* argtable[] = {all,kname,nname,krel,kver,mach,proc,hard,opsys,help,vers,end}; - int nerrors; - int exitcode=0; - - /* verify the argtable[] entries were allocated sucessfully */ - if (arg_nullcheck(argtable) != 0) - { - /* NULL entries were detected, some allocations must have failed */ - printf("%s: insufficient memory\n",progname); - exitcode=1; - goto exit; - } - - /* Parse the command line as defined by argtable[] */ - nerrors = arg_parse(argc,argv,argtable); - - /* special case: '--help' takes precedence over error reporting */ - if (help->count > 0) - { - printf("Usage: %s", progname); - arg_print_syntax(stdout,argtable,"\n"); - printf("Print certain system information. With no options, same as -s.\n\n"); - arg_print_glossary(stdout,argtable," %-25s %s\n"); - printf("\nReport bugs to .\n"); - exitcode=0; - goto exit; - } - - /* special case: '--version' takes precedence error reporting */ - if (vers->count > 0) - { - printf("'%s' example program for the \"argtable\" command line argument parser.\n",progname); - printf("September 2003, Stewart Heitmann\n"); - exitcode=0; - goto exit; - } - - /* If the parser returned any errors then display them and exit */ - if (nerrors > 0) - { - /* Display the error details contained in the arg_end struct.*/ - arg_print_errors(stdout,end,progname); - printf("Try '%s --help' for more information.\n",progname); - exitcode=1; - goto exit; - } - - /* special case: uname with no command line options is equivalent to "uname -s" */ - if (argc==1) - { - exitcode = mymain(0,1,0,0,0,0,0,0); - goto exit; - } - - /* special case: "uname -a" is equivalent to "uname -snrvmpi" */ - if (all->count>0) - { - exitcode = mymain(1,1,1,1,1,1,1,1); - goto exit; - } - - /* normal case: take the command line options at face value */ - exitcode = mymain(kname->count, nname->count, krel->count, kver->count, mach->count, proc->count, hard->count, opsys->count); - - exit: - /* deallocate each non-null entry in argtable[] */ - arg_freetable(argtable,sizeof(argtable)/sizeof(argtable[0])); - - return exitcode; - } diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/CMakeLists.txt b/parallel/parallel_src/extern/argtable3-3.2.2/tests/CMakeLists.txt deleted file mode 100644 index 97c6b593..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/CMakeLists.txt +++ /dev/null @@ -1,104 +0,0 @@ -################################################################################ -# This file is part of the argtable3 library. -# -# Copyright (C) 2016-2021 Tom G. Huang -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of STEWART HEITMANN nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, -# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -################################################################################ - -if(ARGTABLE3_ENABLE_ARG_REX_DEBUG) - add_definitions(-DARG_REX_DEBUG) -endif() - -if(NOT ARGTABLE3_REPLACE_GETOPT) - add_definitions(-DARG_REPLACE_GETOPT=0) -endif() - -if(ARGTABLE3_LONG_ONLY) - add_definitions(-DARG_LONG_ONLY) -endif() - -set(TEST_PUBLIC_SRC_FILES - testall.c - testarglit.c - testargstr.c - testargint.c - testargdate.c - testargdbl.c - testargfile.c - testargrex.c - testargdstr.c - testargcmd.c - CuTest.c -) - -set(TEST_SRC_FILES - ${TEST_PUBLIC_SRC_FILES} - testarghashtable.c -) - -if(UNIX) - set(ARGTABLE3_EXTRA_LIBS m) -endif() - -if(BUILD_SHARED_LIBS) - add_executable(test_shared ${TEST_PUBLIC_SRC_FILES}) - target_compile_definitions(test_shared PRIVATE -DARGTABLE3_TEST_PUBLIC_ONLY) - target_include_directories(test_shared PRIVATE ${PROJECT_SOURCE_DIR}/src) - target_link_libraries(test_shared argtable3 ${ARGTABLE3_EXTRA_LIBS}) - add_custom_command(TARGET test_shared POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "$" - "$" - ) - - add_test(NAME test_shared COMMAND "$") -else() - add_executable(test_static ${TEST_SRC_FILES}) - target_include_directories(test_static PRIVATE ${PROJECT_SOURCE_DIR}/src) - target_link_libraries(test_static argtable3 ${ARGTABLE3_EXTRA_LIBS}) - - add_test(NAME test_static COMMAND "$") -endif() - -add_executable(test_src ${TEST_SRC_FILES} ${ARGTABLE3_SRC_FILES}) -target_include_directories(test_src PRIVATE ${PROJECT_SOURCE_DIR}/src) -target_link_libraries(test_src ${ARGTABLE3_EXTRA_LIBS}) - -add_custom_command(OUTPUT ${ARGTABLE3_AMALGAMATION_SRC_FILE} - COMMAND "${PROJECT_SOURCE_DIR}/tools/build" dist - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tools" -) - -add_executable(test_amalgamation ${TEST_SRC_FILES} ${ARGTABLE3_AMALGAMATION_SRC_FILE}) -target_include_directories(test_amalgamation PRIVATE ${PROJECT_SOURCE_DIR}/src) -target_link_libraries(test_amalgamation ${ARGTABLE3_EXTRA_LIBS}) -add_custom_command(TARGET test_amalgamation PRE_BUILD - COMMAND "${PROJECT_SOURCE_DIR}/tools/build" dist - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tools" -) - -add_test(NAME test_src COMMAND "$") -add_test(NAME test_amalgamation COMMAND "$") diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/CuTest.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/CuTest.c deleted file mode 100644 index 7aec35b5..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/CuTest.c +++ /dev/null @@ -1,326 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "CuTest.h" - -/*-------------------------------------------------------------------------* - * CuStr - *-------------------------------------------------------------------------*/ - -char* CuStrAlloc(size_t size) { - char* newStr = (char*)malloc(sizeof(char) * (size)); - return newStr; -} - -char* CuStrCopy(const char* old) { - size_t len = strlen(old); - char* newStr = CuStrAlloc(len + 1); -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - strcpy_s(newStr, len + 1, old); -#else - strcpy(newStr, old); -#endif - - return newStr; -} - -/*-------------------------------------------------------------------------* - * CuString - *-------------------------------------------------------------------------*/ - -void CuStringInit(CuString* str) { - str->length = 0; - str->size = STRING_MAX; - str->buffer = (char*)malloc(sizeof(char) * str->size); - str->buffer[0] = '\0'; -} - -CuString* CuStringNew(void) { - CuString* str = (CuString*)malloc(sizeof(CuString)); - str->length = 0; - str->size = STRING_MAX; - str->buffer = (char*)malloc(sizeof(char) * str->size); - str->buffer[0] = '\0'; - return str; -} - -void CuStringDelete(CuString* str) { - if (!str) - return; - free(str->buffer); - free(str); -} - -void CuStringResize(CuString* str, size_t newSize) { - str->buffer = (char*)realloc(str->buffer, sizeof(char) * newSize); - str->size = newSize; -} - -void CuStringAppend(CuString* str, const char* text) { - size_t length; - - if (text == NULL) { - text = "NULL"; - } - - length = strlen(text); - if (str->length + length + 1 >= str->size) - CuStringResize(str, str->length + length + 1 + STRING_INC); - str->length += length; -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - strcat_s(str->buffer, str->size, text); -#else - strcat(str->buffer, text); -#endif -} - -void CuStringAppendChar(CuString* str, char ch) { - char text[2]; - text[0] = ch; - text[1] = '\0'; - CuStringAppend(str, text); -} - -void CuStringAppendFormat(CuString* str, const char* format, ...) { - va_list argp; - char buf[HUGE_STRING_LEN]; - va_start(argp, format); -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - vsprintf_s(buf, sizeof(buf), format, argp); -#else - vsprintf(buf, format, argp); -#endif - - va_end(argp); - CuStringAppend(str, buf); -} - -void CuStringInsert(CuString* str, const char* text, int pos) { - size_t length = strlen(text); - if ((size_t)pos > str->length) - pos = (int)str->length; - if (str->length + length + 1 >= str->size) - CuStringResize(str, str->length + length + 1 + STRING_INC); - memmove(str->buffer + pos + length, str->buffer + pos, (str->length - pos) + 1); - str->length += length; - memcpy(str->buffer + pos, text, length); -} - -/*-------------------------------------------------------------------------* - * CuTest - *-------------------------------------------------------------------------*/ - -void CuTestInit(CuTest* t, const char* name, TestFunction function) { - t->name = CuStrCopy(name); - t->failed = 0; - t->ran = 0; - t->message = NULL; - t->function = function; - t->jumpBuf = NULL; -} - -CuTest* CuTestNew(const char* name, TestFunction function) { - CuTest* tc = CU_ALLOC(CuTest); - CuTestInit(tc, name, function); - return tc; -} - -void CuTestDelete(CuTest* t) { - if (!t) - return; - free(t->name); - free(t); -} - -void CuTestRun(CuTest* tc) { - jmp_buf buf; - tc->jumpBuf = &buf; - if (setjmp(buf) == 0) { - tc->ran = 1; - (tc->function)(tc); - } - tc->jumpBuf = 0; -} - -static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string) { - char buf[HUGE_STRING_LEN]; - -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - sprintf_s(buf, sizeof(buf), "%s:%d: ", file, line); -#else - sprintf(buf, "%s:%d: ", file, line); -#endif - CuStringInsert(string, buf, 0); - - tc->failed = 1; - tc->message = string->buffer; - if (tc->jumpBuf != 0) - longjmp(*(tc->jumpBuf), 0); -} - -void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message) { - CuString string; - - CuStringInit(&string); - if (message2 != NULL) { - CuStringAppend(&string, message2); - CuStringAppend(&string, ": "); - } - CuStringAppend(&string, message); - CuFailInternal(tc, file, line, &string); -} - -void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition) { - if (condition) - return; - CuFail_Line(tc, file, line, NULL, message); -} - -void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, const char* expected, const char* actual) { - CuString string; - if ((expected == NULL && actual == NULL) || (expected != NULL && actual != NULL && strcmp(expected, actual) == 0)) { - return; - } - - CuStringInit(&string); - if (message != NULL) { - CuStringAppend(&string, message); - CuStringAppend(&string, ": "); - } - CuStringAppend(&string, "expected <"); - CuStringAppend(&string, expected); - CuStringAppend(&string, "> but was <"); - CuStringAppend(&string, actual); - CuStringAppend(&string, ">"); - CuFailInternal(tc, file, line, &string); -} - -void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, int expected, int actual) { - char buf[STRING_MAX]; - if (expected == actual) - return; -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - sprintf_s(buf, sizeof(buf), "expected <%d> but was <%d>", expected, actual); -#else - sprintf(buf, "expected <%d> but was <%d>", expected, actual); -#endif - CuFail_Line(tc, file, line, message, buf); -} - -void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, double expected, double actual, double delta) { - char buf[STRING_MAX]; - if (fabs(expected - actual) <= delta) - return; -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - sprintf_s(buf, sizeof(buf), "expected <%f> but was <%f>", expected, actual); -#else - sprintf(buf, "expected <%f> but was <%f>", expected, actual); -#endif - CuFail_Line(tc, file, line, message, buf); -} - -void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, void* expected, void* actual) { - char buf[STRING_MAX]; - if (expected == actual) - return; -#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) - sprintf_s(buf, sizeof(buf), "expected pointer <0x%p> but was <0x%p>", expected, actual); -#else - sprintf(buf, "expected pointer <0x%p> but was <0x%p>", expected, actual); -#endif - CuFail_Line(tc, file, line, message, buf); -} - -/*-------------------------------------------------------------------------* - * CuSuite - *-------------------------------------------------------------------------*/ - -void CuSuiteInit(CuSuite* testSuite) { - testSuite->count = 0; - testSuite->failCount = 0; - memset(testSuite->list, 0, sizeof(testSuite->list)); -} - -CuSuite* CuSuiteNew(void) { - CuSuite* testSuite = CU_ALLOC(CuSuite); - CuSuiteInit(testSuite); - return testSuite; -} - -void CuSuiteDelete(CuSuite* testSuite) { - unsigned int n; - for (n = 0; n < MAX_TEST_CASES; n++) { - if (testSuite->list[n]) { - CuTestDelete(testSuite->list[n]); - } - } - free(testSuite); -} - -void CuSuiteAdd(CuSuite* testSuite, CuTest* testCase) { - assert(testSuite->count < MAX_TEST_CASES); - testSuite->list[testSuite->count] = testCase; - testSuite->count++; -} - -void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2) { - int i; - for (i = 0; i < testSuite2->count; ++i) { - CuTest* testCase = testSuite2->list[i]; - CuSuiteAdd(testSuite, testCase); - } - free(testSuite2); -} - -void CuSuiteRun(CuSuite* testSuite) { - int i; - for (i = 0; i < testSuite->count; ++i) { - CuTest* testCase = testSuite->list[i]; - CuTestRun(testCase); - if (testCase->failed) { - testSuite->failCount += 1; - } - } -} - -void CuSuiteSummary(CuSuite* testSuite, CuString* summary) { - int i; - for (i = 0; i < testSuite->count; ++i) { - CuTest* testCase = testSuite->list[i]; - CuStringAppend(summary, testCase->failed ? "F" : "."); - } - CuStringAppend(summary, "\n\n"); -} - -void CuSuiteDetails(CuSuite* testSuite, CuString* details) { - int i; - int failCount = 0; - - if (testSuite->failCount == 0) { - int passCount = testSuite->count - testSuite->failCount; - const char* testWord = passCount == 1 ? "test" : "tests"; - CuStringAppendFormat(details, "OK (%d %s)\n", passCount, testWord); - } else { - if (testSuite->failCount == 1) - CuStringAppend(details, "There was 1 failure:\n"); - else - CuStringAppendFormat(details, "There were %d failures:\n", testSuite->failCount); - - for (i = 0; i < testSuite->count; ++i) { - CuTest* testCase = testSuite->list[i]; - if (testCase->failed) { - failCount++; - CuStringAppendFormat(details, "%d) %s: %s\n", failCount, testCase->name, testCase->message); - } - } - CuStringAppend(details, "\n!!!FAILURES!!!\n"); - - CuStringAppendFormat(details, "Runs: %d ", testSuite->count); - CuStringAppendFormat(details, "Passes: %d ", testSuite->count - testSuite->failCount); - CuStringAppendFormat(details, "Fails: %d\n", testSuite->failCount); - } -} diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/CuTest.h b/parallel/parallel_src/extern/argtable3-3.2.2/tests/CuTest.h deleted file mode 100644 index 7c2cc6c3..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/CuTest.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef CU_TEST_H -#define CU_TEST_H - -#include -#include - -#define CUTEST_VERSION "CuTest 1.5" - -/* CuString */ - -char* CuStrAlloc(size_t size); -char* CuStrCopy(const char* old); - -#define CU_ALLOC(TYPE) ((TYPE*)malloc(sizeof(TYPE))) - -#define HUGE_STRING_LEN 8192 -#define STRING_MAX 256 -#define STRING_INC 256 - -typedef struct { - size_t length; - size_t size; - char* buffer; -} CuString; - -void CuStringInit(CuString* str); -CuString* CuStringNew(void); -void CuStringRead(CuString* str, const char* path); -void CuStringAppend(CuString* str, const char* text); -void CuStringAppendChar(CuString* str, char ch); -void CuStringAppendFormat(CuString* str, const char* format, ...); -void CuStringInsert(CuString* str, const char* text, int pos); -void CuStringResize(CuString* str, size_t newSize); -void CuStringDelete(CuString* str); - -/* CuTest */ - -typedef struct CuTest CuTest; - -typedef void (*TestFunction)(CuTest*); - -struct CuTest { - char* name; - TestFunction function; - int failed; - int ran; - const char* message; - jmp_buf* jumpBuf; -}; - -void CuTestInit(CuTest* t, const char* name, TestFunction function); -CuTest* CuTestNew(const char* name, TestFunction function); -void CuTestRun(CuTest* tc); -void CuTestDelete(CuTest* t); - -/* Internal versions of assert functions -- use the public versions */ -void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message); -void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition); -void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, const char* expected, const char* actual); -void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, int expected, int actual); -void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, double expected, double actual, double delta); -void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, void* expected, void* actual); - -/* public assert functions */ - -#define CuFail(tc, ms) CuFail_Line((tc), __FILE__, __LINE__, NULL, (ms)) -#define CuAssert(tc, ms, cond) CuAssert_Line((tc), __FILE__, __LINE__, (ms), (cond)) -#define CuAssertTrue(tc, cond) CuAssert_Line((tc), __FILE__, __LINE__, "assert failed", (cond)) - -#define CuAssertStrEquals(tc, ex, ac) CuAssertStrEquals_LineMsg((tc), __FILE__, __LINE__, NULL, (ex), (ac)) -#define CuAssertStrEquals_Msg(tc, ms, ex, ac) CuAssertStrEquals_LineMsg((tc), __FILE__, __LINE__, (ms), (ex), (ac)) -#define CuAssertIntEquals(tc, ex, ac) CuAssertIntEquals_LineMsg((tc), __FILE__, __LINE__, NULL, (ex), (ac)) -#define CuAssertIntEquals_Msg(tc, ms, ex, ac) CuAssertIntEquals_LineMsg((tc), __FILE__, __LINE__, (ms), (ex), (ac)) -#define CuAssertDblEquals(tc, ex, ac, dl) CuAssertDblEquals_LineMsg((tc), __FILE__, __LINE__, NULL, (ex), (ac), (dl)) -#define CuAssertDblEquals_Msg(tc, ms, ex, ac, dl) CuAssertDblEquals_LineMsg((tc), __FILE__, __LINE__, (ms), (ex), (ac), (dl)) -#define CuAssertPtrEquals(tc, ex, ac) CuAssertPtrEquals_LineMsg((tc), __FILE__, __LINE__, NULL, (ex), (ac)) -#define CuAssertPtrEquals_Msg(tc, ms, ex, ac) CuAssertPtrEquals_LineMsg((tc), __FILE__, __LINE__, (ms), (ex), (ac)) - -#define CuAssertPtrNotNull(tc, p) CuAssert_Line((tc), __FILE__, __LINE__, "null pointer unexpected", (p != NULL)) -#define CuAssertPtrNotNullMsg(tc, msg, p) CuAssert_Line((tc), __FILE__, __LINE__, (msg), (p != NULL)) - -/* CuSuite */ - -#define MAX_TEST_CASES 1024 - -#define SUITE_ADD_TEST(SUITE, TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST)) - -typedef struct { - int count; - CuTest* list[MAX_TEST_CASES]; - int failCount; - -} CuSuite; - -void CuSuiteInit(CuSuite* testSuite); -CuSuite* CuSuiteNew(void); -void CuSuiteDelete(CuSuite* testSuite); -void CuSuiteAdd(CuSuite* testSuite, CuTest* testCase); -void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2); -void CuSuiteRun(CuSuite* testSuite); -void CuSuiteSummary(CuSuite* testSuite, CuString* summary); -void CuSuiteDetails(CuSuite* testSuite, CuString* details); - -#endif /* CU_TEST_H */ diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/argtable3_private.h b/parallel/parallel_src/extern/argtable3-3.2.2/tests/argtable3_private.h deleted file mode 100644 index c174fa4b..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/argtable3_private.h +++ /dev/null @@ -1,240 +0,0 @@ -/******************************************************************************* - * argtable3_private: Declares private types, constants, and interfaces - * - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#ifndef ARG_UTILS_H -#define ARG_UTILS_H - -#include - -#define ARG_ENABLE_TRACE 0 -#define ARG_ENABLE_LOG 1 - -#ifdef __cplusplus -extern "C" { -#endif - -enum { ARG_ERR_MINCOUNT = 1, ARG_ERR_MAXCOUNT, ARG_ERR_BADINT, ARG_ERR_OVERFLOW, ARG_ERR_BADDOUBLE, ARG_ERR_BADDATE, ARG_ERR_REGNOMATCH }; - -typedef void(arg_panicfn)(const char* fmt, ...); - -#if defined(_MSC_VER) -#define ARG_TRACE(x) \ - __pragma(warning(push)) __pragma(warning(disable : 4127)) do { \ - if (ARG_ENABLE_TRACE) \ - dbg_printf x; \ - } \ - while (0) \ - __pragma(warning(pop)) - -#define ARG_LOG(x) \ - __pragma(warning(push)) __pragma(warning(disable : 4127)) do { \ - if (ARG_ENABLE_LOG) \ - dbg_printf x; \ - } \ - while (0) \ - __pragma(warning(pop)) -#else -#define ARG_TRACE(x) \ - do { \ - if (ARG_ENABLE_TRACE) \ - dbg_printf x; \ - } while (0) - -#define ARG_LOG(x) \ - do { \ - if (ARG_ENABLE_LOG) \ - dbg_printf x; \ - } while (0) -#endif - -/* - * Rename a few generic names to unique names. - * They can be a problem for the platforms like NuttX, where - * the namespace is flat for everything including apps and libraries. - */ -#define xmalloc argtable3_xmalloc -#define xcalloc argtable3_xcalloc -#define xrealloc argtable3_xrealloc -#define xfree argtable3_xfree - -extern void dbg_printf(const char* fmt, ...); -extern void arg_set_panic(arg_panicfn* proc); -extern void* xmalloc(size_t size); -extern void* xcalloc(size_t count, size_t size); -extern void* xrealloc(void* ptr, size_t size); -extern void xfree(void* ptr); - -struct arg_hashtable_entry { - void *k, *v; - unsigned int h; - struct arg_hashtable_entry* next; -}; - -typedef struct arg_hashtable { - unsigned int tablelength; - struct arg_hashtable_entry** table; - unsigned int entrycount; - unsigned int loadlimit; - unsigned int primeindex; - unsigned int (*hashfn)(const void* k); - int (*eqfn)(const void* k1, const void* k2); -} arg_hashtable_t; - -/** - * @brief Create a hash table. - * - * @param minsize minimum initial size of hash table - * @param hashfn function for hashing keys - * @param eqfn function for determining key equality - * @return newly created hash table or NULL on failure - */ -arg_hashtable_t* arg_hashtable_create(unsigned int minsize, unsigned int (*hashfn)(const void*), int (*eqfn)(const void*, const void*)); - -/** - * @brief This function will cause the table to expand if the insertion would take - * the ratio of entries to table size over the maximum load factor. - * - * This function does not check for repeated insertions with a duplicate key. - * The value returned when using a duplicate key is undefined -- when - * the hash table changes size, the order of retrieval of duplicate key - * entries is reversed. - * If in doubt, remove before insert. - * - * @param h the hash table to insert into - * @param k the key - hash table claims ownership and will free on removal - * @param v the value - does not claim ownership - * @return non-zero for successful insertion - */ -void arg_hashtable_insert(arg_hashtable_t* h, void* k, void* v); - -#define ARG_DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ - int fnname(arg_hashtable_t* h, keytype* k, valuetype* v) { return arg_hashtable_insert(h, k, v); } - -/** - * @brief Search the specified key in the hash table. - * - * @param h the hash table to search - * @param k the key to search for - does not claim ownership - * @return the value associated with the key, or NULL if none found - */ -void* arg_hashtable_search(arg_hashtable_t* h, const void* k); - -#define ARG_DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ - valuetype* fnname(arg_hashtable_t* h, keytype* k) { return (valuetype*)(arg_hashtable_search(h, k)); } - -/** - * @brief Remove the specified key from the hash table. - * - * @param h the hash table to remove the item from - * @param k the key to search for - does not claim ownership - */ -void arg_hashtable_remove(arg_hashtable_t* h, const void* k); - -#define ARG_DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ - void fnname(arg_hashtable_t* h, keytype* k) { arg_hashtable_remove(h, k); } - -/** - * @brief Return the number of keys in the hash table. - * - * @param h the hash table - * @return the number of items stored in the hash table - */ -unsigned int arg_hashtable_count(arg_hashtable_t* h); - -/** - * @brief Change the value associated with the key. - * - * function to change the value associated with a key, where there already - * exists a value bound to the key in the hash table. - * Source due to Holger Schemel. - * - * @name hashtable_change - * @param h the hash table - * @param key - * @param value - */ -int arg_hashtable_change(arg_hashtable_t* h, void* k, void* v); - -/** - * @brief Free the hash table and the memory allocated for each key-value pair. - * - * @param h the hash table - * @param free_values whether to call 'free' on the remaining values - */ -void arg_hashtable_destroy(arg_hashtable_t* h, int free_values); - -typedef struct arg_hashtable_itr { - arg_hashtable_t* h; - struct arg_hashtable_entry* e; - struct arg_hashtable_entry* parent; - unsigned int index; -} arg_hashtable_itr_t; - -arg_hashtable_itr_t* arg_hashtable_itr_create(arg_hashtable_t* h); - -void arg_hashtable_itr_destroy(arg_hashtable_itr_t* itr); - -/** - * @brief Return the value of the (key,value) pair at the current position. - */ -extern void* arg_hashtable_itr_key(arg_hashtable_itr_t* i); - -/** - * @brief Return the value of the (key,value) pair at the current position. - */ -extern void* arg_hashtable_itr_value(arg_hashtable_itr_t* i); - -/** - * @brief Advance the iterator to the next element. Returns zero if advanced to end of table. - */ -int arg_hashtable_itr_advance(arg_hashtable_itr_t* itr); - -/** - * @brief Remove current element and advance the iterator to the next element. - */ -int arg_hashtable_itr_remove(arg_hashtable_itr_t* itr); - -/** - * @brief Search and overwrite the supplied iterator, to point to the entry matching the supplied key. - * - * @return Zero if not found. - */ -int arg_hashtable_itr_search(arg_hashtable_itr_t* itr, arg_hashtable_t* h, void* k); - -#define ARG_DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ - int fnname(arg_hashtable_itr_t* i, arg_hashtable_t* h, keytype* k) { return (arg_hashtable_iterator_search(i, h, k)); } - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testall.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testall.c deleted file mode 100644 index b0f3d853..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testall.c +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include - -#include "CuTest.h" - -CuSuite* get_arglit_testsuite(); -CuSuite* get_argstr_testsuite(); -CuSuite* get_argint_testsuite(); -CuSuite* get_argdate_testsuite(); -CuSuite* get_argdbl_testsuite(); -CuSuite* get_argfile_testsuite(); -CuSuite* get_argrex_testsuite(); -CuSuite* get_argdstr_testsuite(); -CuSuite* get_argcmd_testsuite(); - -#ifndef ARGTABLE3_TEST_PUBLIC_ONLY -CuSuite* get_arghashtable_testsuite(); -#endif - -int RunAllTests(void) { - CuString* output = CuStringNew(); - CuSuite* suite = CuSuiteNew(); - - CuSuiteAddSuite(suite, get_arglit_testsuite()); - CuSuiteAddSuite(suite, get_argstr_testsuite()); - CuSuiteAddSuite(suite, get_argint_testsuite()); - CuSuiteAddSuite(suite, get_argdate_testsuite()); - CuSuiteAddSuite(suite, get_argdbl_testsuite()); - CuSuiteAddSuite(suite, get_argfile_testsuite()); - CuSuiteAddSuite(suite, get_argrex_testsuite()); - CuSuiteAddSuite(suite, get_argdstr_testsuite()); - CuSuiteAddSuite(suite, get_argcmd_testsuite()); -#ifndef ARGTABLE3_TEST_PUBLIC_ONLY - CuSuiteAddSuite(suite, get_arghashtable_testsuite()); -#endif - - CuSuiteRun(suite); - CuSuiteSummary(suite, output); - CuSuiteDetails(suite, output); - printf("%s\n", output->buffer); - CuStringDelete(output); - - int failCount = suite->failCount; - CuSuiteDelete(suite); - - return failCount; -} - -int main(void) { - return RunAllTests(); -} diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargcmd.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargcmd.c deleted file mode 100644 index 36dd275e..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargcmd.c +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include -#include - -#include - -#include "CuTest.h" -#include "argtable3.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#endif - -int cmd1_proc(int argc, char* argv[], arg_dstr_t res) { - if (argc == 0) { - arg_dstr_catf(res, "cmd1 fail"); - return 1; - } - - arg_dstr_catf(res, "%d %s", argc, argv[0]); - return 0; -} - -void test_argcmd_basic_001(CuTest* tc) { - arg_cmd_init(); - CuAssertIntEquals(tc, 0, arg_cmd_count()); - - arg_cmd_register("cmd1", cmd1_proc, "description of cmd1"); - CuAssertIntEquals(tc, 1, arg_cmd_count()); - - char* argv[] = { - "cmd1", - "-o", - "file1", - }; - int argc = 3; - CuAssertTrue(tc, strcmp(argv[0], "cmd1") == 0); - CuAssertTrue(tc, strcmp(argv[1], "-o") == 0); - CuAssertTrue(tc, strcmp(argv[2], "file1") == 0); - - arg_dstr_t res = arg_dstr_create(); - int err = arg_cmd_dispatch("cmd1", argc, argv, res); - CuAssertIntEquals(tc, 0, err); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(res), "3 cmd1") == 0); - - arg_dstr_reset(res); - err = arg_cmd_dispatch("cmd1", 0, NULL, res); - CuAssertIntEquals(tc, 1, err); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(res), "cmd1 fail") == 0); - - arg_dstr_destroy(res); - arg_cmd_uninit(); -} - -CuSuite* get_argcmd_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_argcmd_basic_001); - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdate.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdate.c deleted file mode 100644 index e8f49c8e..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdate.c +++ /dev/null @@ -1,377 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include -#include - -#include "CuTest.h" -#include "argtable3.h" - -/* - printf("tm_sec = %d\n", c->tmval->tm_sec); - printf("tm_min = %d\n", c->tmval->tm_min); - printf("tm_hour = %d\n", c->tmval->tm_hour); - printf("tm_mday = %d\n", c->tmval->tm_mday); - printf("tm_mon = %d\n", c->tmval->tm_mon); - printf("tm_year = %d\n", c->tmval->tm_year); - printf("tm_wday = %d\n", c->tmval->tm_wday); - printf("tm_yday = %d\n", c->tmval->tm_yday); - printf("tm_isdst = %d\n", c->tmval->tm_isdst); - -*/ - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#endif - -void test_argdate_basic_001(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", "23:59", "--date", "12/31/04", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->tmval->tm_sec, 0); - CuAssertIntEquals(tc, a->tmval->tm_min, 59); - CuAssertIntEquals(tc, a->tmval->tm_hour, 23); - CuAssertIntEquals(tc, a->tmval->tm_mday, 0); - CuAssertIntEquals(tc, a->tmval->tm_mon, 0); - CuAssertIntEquals(tc, a->tmval->tm_year, 0); - CuAssertIntEquals(tc, a->tmval->tm_wday, 0); - CuAssertIntEquals(tc, a->tmval->tm_yday, 0); - CuAssertIntEquals(tc, a->tmval->tm_isdst, 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertIntEquals(tc, c->tmval->tm_sec, 0); - CuAssertIntEquals(tc, c->tmval->tm_min, 0); - CuAssertIntEquals(tc, c->tmval->tm_hour, 0); - CuAssertIntEquals(tc, c->tmval->tm_mday, 31); - CuAssertIntEquals(tc, c->tmval->tm_mon, 11); - CuAssertIntEquals(tc, c->tmval->tm_year, 104); - CuAssertIntEquals(tc, c->tmval->tm_wday, 0); - CuAssertIntEquals(tc, c->tmval->tm_yday, 0); - CuAssertIntEquals(tc, c->tmval->tm_isdst, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdate_basic_002(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", "--date", "12/31/04", "20:15", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->tmval->tm_sec, 0); - CuAssertIntEquals(tc, a->tmval->tm_min, 15); - CuAssertIntEquals(tc, a->tmval->tm_hour, 20); - CuAssertIntEquals(tc, a->tmval->tm_mday, 0); - CuAssertIntEquals(tc, a->tmval->tm_mon, 0); - CuAssertIntEquals(tc, a->tmval->tm_year, 0); - CuAssertIntEquals(tc, a->tmval->tm_wday, 0); - CuAssertIntEquals(tc, a->tmval->tm_yday, 0); - CuAssertIntEquals(tc, a->tmval->tm_isdst, 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertIntEquals(tc, c->tmval->tm_sec, 0); - CuAssertIntEquals(tc, c->tmval->tm_min, 0); - CuAssertIntEquals(tc, c->tmval->tm_hour, 0); - CuAssertIntEquals(tc, c->tmval->tm_mday, 31); - CuAssertIntEquals(tc, c->tmval->tm_mon, 11); - CuAssertIntEquals(tc, c->tmval->tm_year, 104); - CuAssertIntEquals(tc, c->tmval->tm_wday, 0); - CuAssertIntEquals(tc, c->tmval->tm_yday, 0); - CuAssertIntEquals(tc, c->tmval->tm_isdst, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdate_basic_003(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", "--date", "12/31/04", "20:15", "--date", "06/07/84", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->tmval->tm_sec, 0); - CuAssertIntEquals(tc, a->tmval->tm_min, 15); - CuAssertIntEquals(tc, a->tmval->tm_hour, 20); - CuAssertIntEquals(tc, a->tmval->tm_mday, 0); - CuAssertIntEquals(tc, a->tmval->tm_mon, 0); - CuAssertIntEquals(tc, a->tmval->tm_year, 0); - CuAssertIntEquals(tc, a->tmval->tm_wday, 0); - CuAssertIntEquals(tc, a->tmval->tm_yday, 0); - CuAssertIntEquals(tc, a->tmval->tm_isdst, 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 2); - CuAssertIntEquals(tc, c->tmval->tm_sec, 0); - CuAssertIntEquals(tc, c->tmval->tm_min, 0); - CuAssertIntEquals(tc, c->tmval->tm_hour, 0); - CuAssertIntEquals(tc, c->tmval->tm_mday, 31); - CuAssertIntEquals(tc, c->tmval->tm_mon, 11); - CuAssertIntEquals(tc, c->tmval->tm_year, 104); - CuAssertIntEquals(tc, c->tmval->tm_wday, 0); - CuAssertIntEquals(tc, c->tmval->tm_yday, 0); - CuAssertIntEquals(tc, c->tmval->tm_isdst, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_sec, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_min, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_hour, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_mday, 7); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_mon, 5); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_year, 84); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_wday, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_yday, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_isdst, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdate_basic_004(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", "--date", "12/31/04", "20:15", "-b", "1982-11-28", "--date", "06/07/84", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->tmval->tm_sec, 0); - CuAssertIntEquals(tc, a->tmval->tm_min, 15); - CuAssertIntEquals(tc, a->tmval->tm_hour, 20); - CuAssertIntEquals(tc, a->tmval->tm_mday, 0); - CuAssertIntEquals(tc, a->tmval->tm_mon, 0); - CuAssertIntEquals(tc, a->tmval->tm_year, 0); - CuAssertIntEquals(tc, a->tmval->tm_wday, 0); - CuAssertIntEquals(tc, a->tmval->tm_yday, 0); - CuAssertIntEquals(tc, a->tmval->tm_isdst, 0); - CuAssertTrue(tc, b->count == 1); - CuAssertIntEquals(tc, b->tmval->tm_sec, 0); - CuAssertIntEquals(tc, b->tmval->tm_min, 0); - CuAssertIntEquals(tc, b->tmval->tm_hour, 0); - CuAssertIntEquals(tc, b->tmval->tm_mday, 28); - CuAssertIntEquals(tc, b->tmval->tm_mon, 10); - CuAssertIntEquals(tc, b->tmval->tm_year, 82); - CuAssertIntEquals(tc, b->tmval->tm_wday, 0); - CuAssertIntEquals(tc, b->tmval->tm_yday, 0); - CuAssertIntEquals(tc, b->tmval->tm_isdst, 0); - CuAssertTrue(tc, c->count == 2); - CuAssertIntEquals(tc, c->tmval->tm_sec, 0); - CuAssertIntEquals(tc, c->tmval->tm_min, 0); - CuAssertIntEquals(tc, c->tmval->tm_hour, 0); - CuAssertIntEquals(tc, c->tmval->tm_mday, 31); - CuAssertIntEquals(tc, c->tmval->tm_mon, 11); - CuAssertIntEquals(tc, c->tmval->tm_year, 104); - CuAssertIntEquals(tc, c->tmval->tm_wday, 0); - CuAssertIntEquals(tc, c->tmval->tm_yday, 0); - CuAssertIntEquals(tc, c->tmval->tm_isdst, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_sec, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_min, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_hour, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_mday, 7); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_mon, 5); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_year, 84); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_wday, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_yday, 0); - CuAssertIntEquals(tc, (c->tmval + 1)->tm_isdst, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdate_basic_005(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 2); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdate_basic_006(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", "25:59", "--date", "12/31/04", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdate_basic_007(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", "23:59", "--date", "12/32/04", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdate_basic_008(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", "23:59", "--date", "12/31/04", "22:58", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdate_basic_009(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", "--date", "12/31/04", "20:15", "--date", "26/07/84", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdate_basic_010(CuTest* tc) { - struct arg_date* a = arg_date1(NULL, NULL, "%H:%M", NULL, "time 23:59"); - struct arg_date* b = arg_date0("b", NULL, "%Y-%m-%d", NULL, "date YYYY-MM-DD"); - struct arg_date* c = arg_daten(NULL, "date", "%D", NULL, 1, 2, "MM/DD/YY"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, end}; - int nerrors; - - char* argv[] = {"program", "-b", "1982-11-28", "-b", "1976-11-11", "--date", "12/07/84", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -CuSuite* get_argdate_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_argdate_basic_001); - SUITE_ADD_TEST(suite, test_argdate_basic_002); - SUITE_ADD_TEST(suite, test_argdate_basic_003); - SUITE_ADD_TEST(suite, test_argdate_basic_004); - SUITE_ADD_TEST(suite, test_argdate_basic_005); - SUITE_ADD_TEST(suite, test_argdate_basic_006); - SUITE_ADD_TEST(suite, test_argdate_basic_007); - SUITE_ADD_TEST(suite, test_argdate_basic_008); - SUITE_ADD_TEST(suite, test_argdate_basic_009); - SUITE_ADD_TEST(suite, test_argdate_basic_010); - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdbl.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdbl.c deleted file mode 100644 index 4cb0f7dd..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdbl.c +++ /dev/null @@ -1,451 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include -#include - -#include "CuTest.h" -#include "argtable3.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#endif - -void test_argdbl_basic_001(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "0", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 1); - CuAssertDblEquals(tc, a->dval[0], 0, DBL_EPSILON); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_002(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1.234", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 1); - CuAssertDblEquals(tc, a->dval[0], 1.234, DBL_EPSILON); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_003(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1.8", "2.3", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 1); - CuAssertDblEquals(tc, a->dval[0], 1.8, DBL_EPSILON); - CuAssertTrue(tc, b->count == 1); - CuAssertDblEquals(tc, b->dval[0], 2.3, DBL_EPSILON); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_004(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "5", "7", "9", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 1); - CuAssertDblEquals(tc, a->dval[0], 5, DBL_EPSILON); - CuAssertTrue(tc, b->count == 1); - CuAssertDblEquals(tc, b->dval[0], 7, DBL_EPSILON); - CuAssertTrue(tc, c->count == 1); - CuAssertDblEquals(tc, c->dval[0], 9, DBL_EPSILON); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_005(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1.9998", "-d", "13e-1", "-D", "17e-1", "--delta", "36e-1", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 1); - CuAssertDblEquals(tc, a->dval[0], 1.9998, DBL_EPSILON); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 3); - CuAssertDblEquals(tc, d->dval[0], 13e-1, DBL_EPSILON); - CuAssertDblEquals(tc, d->dval[1], 17e-1, DBL_EPSILON); - CuAssertDblEquals(tc, d->dval[2], 36e-1, DBL_EPSILON); - CuAssertTrue(tc, e->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_006(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1.2", "2.3", "4.5", "--eps", "8.3456789", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 1); - CuAssertDblEquals(tc, a->dval[0], 1.2, DBL_EPSILON); - CuAssertTrue(tc, b->count == 1); - CuAssertDblEquals(tc, b->dval[0], 2.3, DBL_EPSILON); - CuAssertTrue(tc, c->count == 1); - CuAssertDblEquals(tc, c->dval[0], 4.5, DBL_EPSILON); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 1); - CuAssertDblEquals(tc, e->dval[0], 8.3456789, DBL_EPSILON); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_007(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1.2", "2.3", "4.5", "--eqn", "8.3456789", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 1); - CuAssertDblEquals(tc, a->dval[0], 1.2, DBL_EPSILON); - CuAssertTrue(tc, b->count == 1); - CuAssertDblEquals(tc, b->dval[0], 2.3, DBL_EPSILON); - CuAssertTrue(tc, c->count == 1); - CuAssertDblEquals(tc, c->dval[0], 4.5, DBL_EPSILON); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 1); - CuAssertDblEquals(tc, e->dval[0], 8.3456789, DBL_EPSILON); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_008(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1.2", "2.3", "4.5", "--eqn", "8.345", "-D", "0.234", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 1); - CuAssertDblEquals(tc, a->dval[0], 1.2, DBL_EPSILON); - CuAssertTrue(tc, b->count == 1); - CuAssertDblEquals(tc, b->dval[0], 2.3, DBL_EPSILON); - CuAssertTrue(tc, c->count == 1); - CuAssertDblEquals(tc, c->dval[0], 4.5, DBL_EPSILON); - CuAssertTrue(tc, d->count == 1); - CuAssertDblEquals(tc, d->dval[0], 0.234, DBL_EPSILON); - CuAssertTrue(tc, e->count == 1); - CuAssertDblEquals(tc, e->dval[0], 8.345, DBL_EPSILON); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_009(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1", "2", "3", "4", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_010(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1", "2", "3", "4", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_011(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1", "2", "3", "-d1", "-d2", "-d3", "-d4", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_012(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1", "2", "3", "--eps", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_013(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "1", "2", "3", "--eps", "3", "--eqn", "6", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_014(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "hello", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argdbl_basic_015(CuTest* tc) { - struct arg_dbl* a = arg_dbl1(NULL, NULL, "a", "a is "); - struct arg_dbl* b = arg_dbl0(NULL, NULL, "b", "b is "); - struct arg_dbl* c = arg_dbl0(NULL, NULL, "c", "c is "); - struct arg_dbl* d = arg_dbln("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_dbl* e = arg_dbl0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, end}; - int nerrors; - - char* argv[] = {"program", "4", "hello", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -CuSuite* get_argdbl_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_argdbl_basic_001); - SUITE_ADD_TEST(suite, test_argdbl_basic_002); - SUITE_ADD_TEST(suite, test_argdbl_basic_003); - SUITE_ADD_TEST(suite, test_argdbl_basic_004); - SUITE_ADD_TEST(suite, test_argdbl_basic_005); - SUITE_ADD_TEST(suite, test_argdbl_basic_006); - SUITE_ADD_TEST(suite, test_argdbl_basic_007); - SUITE_ADD_TEST(suite, test_argdbl_basic_008); - SUITE_ADD_TEST(suite, test_argdbl_basic_009); - SUITE_ADD_TEST(suite, test_argdbl_basic_010); - SUITE_ADD_TEST(suite, test_argdbl_basic_011); - SUITE_ADD_TEST(suite, test_argdbl_basic_012); - SUITE_ADD_TEST(suite, test_argdbl_basic_013); - SUITE_ADD_TEST(suite, test_argdbl_basic_014); - SUITE_ADD_TEST(suite, test_argdbl_basic_015); - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdstr.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdstr.c deleted file mode 100644 index 54c79870..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargdstr.c +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include -#include - -#include - -#include "CuTest.h" -#include "argtable3.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#endif - -void test_argdstr_basic_001(CuTest* tc) { - arg_dstr_t ds = arg_dstr_create(); - - arg_dstr_set(ds, "hello ", ARG_DSTR_VOLATILE); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(ds), "hello ") == 0); - - arg_dstr_cat(ds, "world"); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(ds), "hello world") == 0); - - arg_dstr_destroy(ds); -} - -void test_argdstr_basic_002(CuTest* tc) { - arg_dstr_t ds = arg_dstr_create(); - - arg_dstr_set(ds, "hello world", ARG_DSTR_VOLATILE); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(ds), "hello world") == 0); - - arg_dstr_reset(ds); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(ds), "") == 0); - - arg_dstr_set(ds, "good", ARG_DSTR_VOLATILE); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(ds), "good") == 0); - - arg_dstr_destroy(ds); -} - -void test_argdstr_basic_003(CuTest* tc) { - arg_dstr_t ds = arg_dstr_create(); - arg_dstr_cat(ds, "hello world"); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(ds), "hello world") == 0); - - arg_dstr_destroy(ds); -} - -void test_argdstr_basic_004(CuTest* tc) { - arg_dstr_t ds = arg_dstr_create(); - arg_dstr_catf(ds, "%s %d", "hello world", 1); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(ds), "hello world 1") == 0); - - arg_dstr_destroy(ds); -} - -void test_argdstr_basic_005(CuTest* tc) { - arg_dstr_t ds = arg_dstr_create(); - arg_dstr_catf(ds, "%d.", 1); - arg_dstr_catf(ds, "%d.", 2); - arg_dstr_catf(ds, "%d.", 3); - arg_dstr_cat(ds, "456"); - CuAssertTrue(tc, strcmp(arg_dstr_cstr(ds), "1.2.3.456") == 0); - - arg_dstr_destroy(ds); -} - -void test_argdstr_basic_006(CuTest* tc) { - int i; - - arg_dstr_t ds = arg_dstr_create(); - for (i = 0; i < 100000; i++) { - arg_dstr_catf(ds, "%s", "1234567890"); - } - CuAssertTrue(tc, strlen(arg_dstr_cstr(ds)) == 1000000); - - arg_dstr_destroy(ds); -} - -CuSuite* get_argdstr_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_argdstr_basic_001); - SUITE_ADD_TEST(suite, test_argdstr_basic_002); - SUITE_ADD_TEST(suite, test_argdstr_basic_003); - SUITE_ADD_TEST(suite, test_argdstr_basic_004); - SUITE_ADD_TEST(suite, test_argdstr_basic_005); - SUITE_ADD_TEST(suite, test_argdstr_basic_006); - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargfile.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargfile.c deleted file mode 100644 index ad457e4f..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargfile.c +++ /dev/null @@ -1,809 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include - -#include "CuTest.h" -#include "argtable3.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#endif - -void test_argfile_basic_001(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "foo.bar", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "foo.bar"); - CuAssertStrEquals(tc, a->basename[0], "foo.bar"); - CuAssertStrEquals(tc, a->extension[0], ".bar"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_002(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/foo.bar", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/foo.bar"); - CuAssertStrEquals(tc, a->basename[0], "foo.bar"); - CuAssertStrEquals(tc, a->extension[0], ".bar"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_003(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "./foo.bar", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "./foo.bar"); - CuAssertStrEquals(tc, a->basename[0], "foo.bar"); - CuAssertStrEquals(tc, a->extension[0], ".bar"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_004(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "././foo.bar", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "././foo.bar"); - CuAssertStrEquals(tc, a->basename[0], "foo.bar"); - CuAssertStrEquals(tc, a->extension[0], ".bar"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_005(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "./././foo.bar", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "./././foo.bar"); - CuAssertStrEquals(tc, a->basename[0], "foo.bar"); - CuAssertStrEquals(tc, a->extension[0], ".bar"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_006(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "../foo.bar", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "../foo.bar"); - CuAssertStrEquals(tc, a->basename[0], "foo.bar"); - CuAssertStrEquals(tc, a->extension[0], ".bar"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_007(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "../../foo.bar", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "../../foo.bar"); - CuAssertStrEquals(tc, a->basename[0], "foo.bar"); - CuAssertStrEquals(tc, a->extension[0], ".bar"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_008(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "foo"); - CuAssertStrEquals(tc, a->basename[0], "foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_009(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/foo"); - CuAssertStrEquals(tc, a->basename[0], "foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_010(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "./foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "./foo"); - CuAssertStrEquals(tc, a->basename[0], "foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_011(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "././foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "././foo"); - CuAssertStrEquals(tc, a->basename[0], "foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_012(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "./././foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "./././foo"); - CuAssertStrEquals(tc, a->basename[0], "foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_013(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "../foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "../foo"); - CuAssertStrEquals(tc, a->basename[0], "foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_014(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "../../foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "../../foo"); - CuAssertStrEquals(tc, a->basename[0], "foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_015(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", ".foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], ".foo"); - CuAssertStrEquals(tc, a->basename[0], ".foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_016(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/.foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/.foo"); - CuAssertStrEquals(tc, a->basename[0], ".foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_017(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "./.foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "./.foo"); - CuAssertStrEquals(tc, a->basename[0], ".foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_018(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "../.foo", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "../.foo"); - CuAssertStrEquals(tc, a->basename[0], ".foo"); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_019(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "foo.", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "foo."); - CuAssertStrEquals(tc, a->basename[0], "foo."); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_020(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/foo.", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/foo."); - CuAssertStrEquals(tc, a->basename[0], "foo."); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_021(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "./foo.", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "./foo."); - CuAssertStrEquals(tc, a->basename[0], "foo."); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_022(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "../foo.", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "../foo."); - CuAssertStrEquals(tc, a->basename[0], "foo."); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_023(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/.foo.", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/.foo."); - CuAssertStrEquals(tc, a->basename[0], ".foo."); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_024(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/.foo.c", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/.foo.c"); - CuAssertStrEquals(tc, a->basename[0], ".foo.c"); - CuAssertStrEquals(tc, a->extension[0], ".c"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_025(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/.foo..b.c", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/.foo..b.c"); - CuAssertStrEquals(tc, a->basename[0], ".foo..b.c"); - CuAssertStrEquals(tc, a->extension[0], ".c"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_026(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/"); - CuAssertStrEquals(tc, a->basename[0], ""); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_027(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", ".", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "."); - CuAssertStrEquals(tc, a->basename[0], ""); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_028(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "..", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], ".."); - CuAssertStrEquals(tc, a->basename[0], ""); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_029(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/.", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/."); - CuAssertStrEquals(tc, a->basename[0], ""); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_030(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "/..", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "/.."); - CuAssertStrEquals(tc, a->basename[0], ""); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_031(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "./", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "./"); - CuAssertStrEquals(tc, a->basename[0], ""); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_032(CuTest* tc) { - struct arg_file* a = arg_file1(NULL, NULL, "", "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "../", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "../"); - CuAssertStrEquals(tc, a->basename[0], ""); - CuAssertStrEquals(tc, a->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -#ifdef WIN32 -void test_argfile_basic_033(CuTest* tc) { - struct arg_file* a = arg_filen(NULL, NULL, "", 0, 3, "filename to test"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, end}; - int nerrors; - - char* argv[] = {"program", "C:\\test folder\\", "C:\\test folder2", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 2); - CuAssertStrEquals(tc, a->filename[0], "C:\\test folder\\"); - CuAssertStrEquals(tc, a->basename[0], ""); - CuAssertStrEquals(tc, a->extension[0], ""); - CuAssertStrEquals(tc, a->filename[1], "C:\\test folder2"); - CuAssertStrEquals(tc, a->basename[1], "test folder2"); - CuAssertStrEquals(tc, a->extension[1], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argfile_basic_034(CuTest* tc) { - struct arg_file* a = arg_filen(NULL, NULL, "", 1, 1, "path a"); - struct arg_file* b = arg_filen(NULL, NULL, "", 1, 1, "path b"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, end}; - int nerrors; - - char* argv[] = {"program", "C:\\test folder\\", "C:\\test folder2", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->filename[0], "C:\\test folder\\"); - CuAssertStrEquals(tc, a->basename[0], ""); - CuAssertStrEquals(tc, a->extension[0], ""); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->filename[0], "C:\\test folder2"); - CuAssertStrEquals(tc, b->basename[0], "test folder2"); - CuAssertStrEquals(tc, b->extension[0], ""); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} -#endif /* #ifdef WIN32 */ - -CuSuite* get_argfile_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_argfile_basic_001); - SUITE_ADD_TEST(suite, test_argfile_basic_002); - SUITE_ADD_TEST(suite, test_argfile_basic_003); - SUITE_ADD_TEST(suite, test_argfile_basic_004); - SUITE_ADD_TEST(suite, test_argfile_basic_005); - SUITE_ADD_TEST(suite, test_argfile_basic_006); - SUITE_ADD_TEST(suite, test_argfile_basic_007); - SUITE_ADD_TEST(suite, test_argfile_basic_008); - SUITE_ADD_TEST(suite, test_argfile_basic_009); - SUITE_ADD_TEST(suite, test_argfile_basic_010); - SUITE_ADD_TEST(suite, test_argfile_basic_011); - SUITE_ADD_TEST(suite, test_argfile_basic_012); - SUITE_ADD_TEST(suite, test_argfile_basic_013); - SUITE_ADD_TEST(suite, test_argfile_basic_014); - SUITE_ADD_TEST(suite, test_argfile_basic_015); - SUITE_ADD_TEST(suite, test_argfile_basic_016); - SUITE_ADD_TEST(suite, test_argfile_basic_017); - SUITE_ADD_TEST(suite, test_argfile_basic_018); - SUITE_ADD_TEST(suite, test_argfile_basic_019); - SUITE_ADD_TEST(suite, test_argfile_basic_020); - SUITE_ADD_TEST(suite, test_argfile_basic_021); - SUITE_ADD_TEST(suite, test_argfile_basic_022); - SUITE_ADD_TEST(suite, test_argfile_basic_023); - SUITE_ADD_TEST(suite, test_argfile_basic_024); - SUITE_ADD_TEST(suite, test_argfile_basic_025); - SUITE_ADD_TEST(suite, test_argfile_basic_026); - SUITE_ADD_TEST(suite, test_argfile_basic_027); - SUITE_ADD_TEST(suite, test_argfile_basic_028); - SUITE_ADD_TEST(suite, test_argfile_basic_029); - SUITE_ADD_TEST(suite, test_argfile_basic_030); - SUITE_ADD_TEST(suite, test_argfile_basic_031); - SUITE_ADD_TEST(suite, test_argfile_basic_032); -#ifdef WIN32 - SUITE_ADD_TEST(suite, test_argfile_basic_033); - SUITE_ADD_TEST(suite, test_argfile_basic_034); -#endif /* #ifdef WIN32 */ - - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testarghashtable.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testarghashtable.c deleted file mode 100644 index 36e4a7d5..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testarghashtable.c +++ /dev/null @@ -1,276 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include -#include - -#include - -#include "CuTest.h" -#include "argtable3_private.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#pragma warning(disable : 4996) -#endif - -static unsigned int hash_key(const void* key) { - char* str = (char*)key; - int c; - unsigned int hash = 5381; - - while ((c = *str++) != 0) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - - return hash; -} - -static int equal_keys(const void* key1, const void* key2) { - char* k1 = (char*)key1; - char* k2 = (char*)key2; - return (0 == strcmp(k1, k2)); -} - -void test_arghashtable_basic_001(CuTest* tc) { - arg_hashtable_t* h = arg_hashtable_create(32, hash_key, equal_keys); - CuAssertTrue(tc, h != 0); - CuAssertIntEquals(tc, arg_hashtable_count(h), 0); - - arg_hashtable_destroy(h, 1); -} - -void test_arghashtable_basic_002(CuTest* tc) { - arg_hashtable_t* h = arg_hashtable_create(32, hash_key, equal_keys); - CuAssertTrue(tc, h != 0); - CuAssertIntEquals(tc, arg_hashtable_count(h), 0); - - char* key_1 = "k1"; - char* k_1 = (char*)malloc(strlen(key_1) + 1); - memset(k_1, 0, strlen(key_1) + 1); - strncpy(k_1, key_1, strlen(key_1)); - - char* value_1 = "v1"; - char* v_1 = (char*)malloc(strlen(value_1) + 1); - memset(v_1, 0, strlen(value_1) + 1); - strncpy(v_1, value_1, strlen(value_1)); - - arg_hashtable_insert(h, k_1, v_1); - CuAssertIntEquals(tc, 1, arg_hashtable_count(h)); - - arg_hashtable_itr_t* itr = arg_hashtable_itr_create(h); - CuAssertTrue(tc, itr != 0); - CuAssertPtrEquals(tc, k_1, arg_hashtable_itr_key(itr)); - CuAssertTrue(tc, strcmp((char*)arg_hashtable_itr_key(itr), key_1) == 0); - CuAssertPtrEquals(tc, v_1, arg_hashtable_itr_value(itr)); - CuAssertTrue(tc, strcmp((char*)arg_hashtable_itr_value(itr), value_1) == 0); - - arg_hashtable_itr_destroy(itr); - arg_hashtable_destroy(h, 1); -} - -void test_arghashtable_basic_003(CuTest* tc) { - arg_hashtable_t* h = arg_hashtable_create(32, hash_key, equal_keys); - CuAssertTrue(tc, h != 0); - CuAssertIntEquals(tc, arg_hashtable_count(h), 0); - - char* key_1 = "k1"; - char* k_1 = (char*)malloc(strlen(key_1) + 1); - memset(k_1, 0, strlen(key_1) + 1); - strncpy(k_1, key_1, strlen(key_1)); - - char* value_1 = "v1"; - char* v_1 = (char*)malloc(strlen(value_1) + 1); - memset(v_1, 0, strlen(value_1) + 1); - strncpy(v_1, value_1, strlen(value_1)); - - arg_hashtable_insert(h, k_1, v_1); - CuAssertIntEquals(tc, 1, arg_hashtable_count(h)); - - char* key_2 = "k2"; - char* k_2 = (char*)malloc(strlen(key_2) + 1); - memset(k_2, 0, strlen(key_2) + 1); - strncpy(k_2, key_2, strlen(key_2)); - - char* value_2 = "v2"; - char* v_2 = (char*)malloc(strlen(value_2) + 1); - memset(v_2, 0, strlen(value_2) + 1); - strncpy(v_2, value_2, strlen(value_2)); - - arg_hashtable_insert(h, k_2, v_2); - CuAssertIntEquals(tc, 2, arg_hashtable_count(h)); - - arg_hashtable_itr_t* itr = arg_hashtable_itr_create(h); - CuAssertTrue(tc, itr != 0); - - int ret = arg_hashtable_itr_advance(itr); - CuAssertTrue(tc, ret != 0); - - ret = arg_hashtable_itr_advance(itr); - CuAssertTrue(tc, ret == 0); - - arg_hashtable_itr_destroy(itr); - arg_hashtable_destroy(h, 1); -} - -void test_arghashtable_basic_004(CuTest* tc) { - arg_hashtable_t* h = arg_hashtable_create(32, hash_key, equal_keys); - CuAssertTrue(tc, h != 0); - CuAssertIntEquals(tc, arg_hashtable_count(h), 0); - - char* key_1 = "k1"; - char* k_1 = (char*)malloc(strlen(key_1) + 1); - memset(k_1, 0, strlen(key_1) + 1); - strncpy(k_1, key_1, strlen(key_1)); - - char* value_1 = "v1"; - char* v_1 = (char*)malloc(strlen(value_1) + 1); - memset(v_1, 0, strlen(value_1) + 1); - strncpy(v_1, value_1, strlen(value_1)); - - arg_hashtable_insert(h, k_1, v_1); - CuAssertTrue(tc, h != 0); - CuAssertIntEquals(tc, 1, arg_hashtable_count(h)); - - arg_hashtable_itr_t* itr = arg_hashtable_itr_create(h); - int ret = arg_hashtable_itr_remove(itr); - CuAssertTrue(tc, ret == 0); - CuAssertIntEquals(tc, 0, arg_hashtable_count(h)); - - arg_hashtable_itr_destroy(itr); - arg_hashtable_destroy(h, 1); -} - -void test_arghashtable_basic_005(CuTest* tc) { - arg_hashtable_t* h = arg_hashtable_create(3, hash_key, equal_keys); - CuAssertTrue(tc, h != 0); - CuAssertIntEquals(tc, arg_hashtable_count(h), 0); - - char* key_1 = "k1"; - char* k_1 = (char*)malloc(strlen(key_1) + 1); - memset(k_1, 0, strlen(key_1) + 1); - strncpy(k_1, key_1, strlen(key_1)); - - char* value_1 = "v1"; - char* v_1 = (char*)malloc(strlen(value_1) + 1); - memset(v_1, 0, strlen(value_1) + 1); - strncpy(v_1, value_1, strlen(value_1)); - - arg_hashtable_insert(h, k_1, v_1); - CuAssertTrue(tc, h != 0); - CuAssertIntEquals(tc, 1, arg_hashtable_count(h)); - - arg_hashtable_remove(h, k_1); - CuAssertIntEquals(tc, 0, arg_hashtable_count(h)); - - arg_hashtable_destroy(h, 1); -} - -void test_arghashtable_basic_006(CuTest* tc) { - arg_hashtable_t* h = arg_hashtable_create(32, hash_key, equal_keys); - CuAssertTrue(tc, h != 0); - CuAssertIntEquals(tc, arg_hashtable_count(h), 0); - - char* key_1 = "k1"; - char* k_1 = (char*)malloc(strlen(key_1) + 1); - memset(k_1, 0, strlen(key_1) + 1); - strncpy(k_1, key_1, strlen(key_1)); - - char* value_1 = "v1"; - char* v_1 = (char*)malloc(strlen(value_1) + 1); - memset(v_1, 0, strlen(value_1) + 1); - strncpy(v_1, value_1, strlen(value_1)); - - arg_hashtable_insert(h, k_1, v_1); - CuAssertTrue(tc, arg_hashtable_count(h) == 1); - - char* vv = (char*)arg_hashtable_search(h, k_1); - CuAssertTrue(tc, strcmp(vv, v_1) == 0); - - arg_hashtable_destroy(h, 1); -} - -void test_arghashtable_basic_007(CuTest* tc) { - arg_hashtable_t* h = arg_hashtable_create(32, hash_key, equal_keys); - CuAssertTrue(tc, h != 0); - CuAssertIntEquals(tc, arg_hashtable_count(h), 0); - - char* key_1 = "k1"; - char* k_1 = (char*)malloc(strlen(key_1) + 1); - memset(k_1, 0, strlen(key_1) + 1); - strncpy(k_1, key_1, strlen(key_1)); - - char* value_1 = "v1"; - char* v_1 = (char*)malloc(strlen(value_1) + 1); - memset(v_1, 0, strlen(value_1) + 1); - strncpy(v_1, value_1, strlen(value_1)); - - arg_hashtable_insert(h, k_1, v_1); - CuAssertIntEquals(tc, 1, arg_hashtable_count(h)); - - char* key_2 = "k2"; - char* k_2 = (char*)malloc(strlen(key_2) + 1); - memset(k_2, 0, strlen(key_2) + 1); - strncpy(k_2, key_2, strlen(key_2)); - - char* value_2 = "v2"; - char* v_2 = (char*)malloc(strlen(value_2) + 1); - memset(v_2, 0, strlen(value_2) + 1); - strncpy(v_2, value_2, strlen(value_2)); - - arg_hashtable_insert(h, k_2, v_2); - CuAssertIntEquals(tc, 2, arg_hashtable_count(h)); - - arg_hashtable_itr_t itr; - int ret = arg_hashtable_itr_search(&itr, h, k_1); - CuAssertTrue(tc, ret != 0); - CuAssertPtrEquals(tc, k_1, arg_hashtable_itr_key(&itr)); - CuAssertPtrEquals(tc, v_1, arg_hashtable_itr_value(&itr)); - CuAssertTrue(tc, strcmp((char*)arg_hashtable_itr_key(&itr), k_1) == 0); - CuAssertTrue(tc, strcmp((char*)arg_hashtable_itr_value(&itr), v_1) == 0); - - arg_hashtable_destroy(h, 1); -} - -CuSuite* get_arghashtable_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_arghashtable_basic_001); - SUITE_ADD_TEST(suite, test_arghashtable_basic_002); - SUITE_ADD_TEST(suite, test_arghashtable_basic_003); - SUITE_ADD_TEST(suite, test_arghashtable_basic_004); - SUITE_ADD_TEST(suite, test_arghashtable_basic_005); - SUITE_ADD_TEST(suite, test_arghashtable_basic_006); - SUITE_ADD_TEST(suite, test_arghashtable_basic_007); - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargint.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargint.c deleted file mode 100644 index 9bbac4e4..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargint.c +++ /dev/null @@ -1,2018 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include - -#include "CuTest.h" -#include "argtable3.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#endif - -void test_argint_basic_001(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - - char* argv[] = {"program", "0", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->ival[0], 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 0); - CuAssertTrue(tc, f->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_002(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - - char* argv[] = {"program", "1", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 0); - CuAssertTrue(tc, f->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_003(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - - char* argv[] = {"program", "1", "2", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertTrue(tc, b->count == 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 0); - CuAssertTrue(tc, f->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_004(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - - char* argv[] = {"program", "5", "7", "9", "-d", "-21", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->ival[0], 5); - CuAssertTrue(tc, b->count == 1); - CuAssertIntEquals(tc, b->ival[0], 7); - CuAssertTrue(tc, c->count == 1); - CuAssertIntEquals(tc, c->ival[0], 9); - CuAssertTrue(tc, d->count == 1); - CuAssertIntEquals(tc, d->ival[0], -21); - CuAssertTrue(tc, e->count == 0); - CuAssertTrue(tc, f->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_005(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - - char* argv[] = {"program", "-d", "1", "-D2", "--delta", "3", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 3); - CuAssertIntEquals(tc, d->ival[0], 1); - CuAssertIntEquals(tc, d->ival[1], 2); - CuAssertIntEquals(tc, d->ival[2], 3); - CuAssertTrue(tc, e->count == 0); - CuAssertTrue(tc, f->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_006(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - - char* argv[] = {"program", "1", "2", "4", "--eps", "-7", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertTrue(tc, b->count == 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertTrue(tc, c->count == 1); - CuAssertIntEquals(tc, c->ival[0], 4); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 1); - CuAssertIntEquals(tc, e->ival[0], -7); - CuAssertTrue(tc, f->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_007(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - - char* argv[] = {"program", "1", "2", "4", "--eqn", "-7", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertTrue(tc, b->count == 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertTrue(tc, c->count == 1); - CuAssertIntEquals(tc, c->ival[0], 4); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 1); - CuAssertIntEquals(tc, e->ival[0], -7); - CuAssertTrue(tc, f->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_008(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - - char* argv[] = {"program", "1", "2", "3", "-D4", "--eps", "-10", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertTrue(tc, b->count == 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertTrue(tc, c->count == 1); - CuAssertIntEquals(tc, c->ival[0], 3); - CuAssertTrue(tc, d->count == 1); - CuAssertIntEquals(tc, d->ival[0], 4); - CuAssertTrue(tc, e->count == 1); - CuAssertIntEquals(tc, e->ival[0], -10); - CuAssertTrue(tc, f->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_009(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1", "-f", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, e->count == 0); - CuAssertTrue(tc, f->count == 1); - CuAssertIntEquals(tc, f->ival[0], -1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_010(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "-f", "1", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 1); - CuAssertIntEquals(tc, f->ival[0], -1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_011(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "-f", "2", "--filler", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 2); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 2); - CuAssertIntEquals(tc, f->ival[0], -1); - CuAssertIntEquals(tc, f->ival[1], -1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_012(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "-f", "1", "--filler=2", "-f", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 3); - CuAssertIntEquals(tc, f->ival[0], -1); - CuAssertIntEquals(tc, f->ival[1], 2); - CuAssertIntEquals(tc, f->ival[0], -1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_013(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0x0", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 0x0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_014(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0x0", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 0x0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_015(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0x10", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 0x10); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_016(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0x10", "0x32", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 0x10); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 0x32); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_017(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0x5", "0xA", "0xF", "-d", "-0x1E", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 0x5); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 0xA); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 0xF); - CuAssertIntEquals(tc, d->count, 1); - CuAssertIntEquals(tc, d->ival[0], -0x1E); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_018(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "-d", "0xab", "-D0x09", "--delta", "0x02e", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 3); - CuAssertIntEquals(tc, d->ival[0], 0xab); - CuAssertIntEquals(tc, d->ival[1], 0x09); - CuAssertIntEquals(tc, d->ival[2], 0x02e); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_019(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0o0", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_020(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0o10", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 010); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_021(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0o67", "0O23", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 067); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 023); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_022(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0o5", "0O0", "0x1", "-d", "-0o6", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 05); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 0); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 0x1); - CuAssertIntEquals(tc, d->count, 1); - CuAssertIntEquals(tc, d->ival[0], -06); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_023(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "-d", "0o012", "-D0o0777", "--delta", "0o56", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 3); - CuAssertIntEquals(tc, d->ival[0], 012); - CuAssertIntEquals(tc, d->ival[1], 0777); - CuAssertIntEquals(tc, d->ival[2], 056); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_024(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0B0", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_025(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0B0", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_026(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0b10", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 2); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_027(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0B10110", "0b111001", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 22); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 57); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_028(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0B10110", "0b111001", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 22); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 57); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_029(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0b101001", "0b101", "0b00101010101", "-d", "0B110000011", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 41); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 5); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 341); - CuAssertIntEquals(tc, d->count, 1); - CuAssertIntEquals(tc, d->ival[0], 387); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_030(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "-d", "0b101", "-D0B11", "--delta", "0b11011", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 3); - CuAssertIntEquals(tc, d->ival[0], 5); - CuAssertIntEquals(tc, d->ival[1], 3); - CuAssertIntEquals(tc, d->ival[2], 27); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_031(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "11", "0x11", "0o11", "-D0b11", "--eps", "-0o50", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 11); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 0x11); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 011); - CuAssertIntEquals(tc, d->count, 1); - CuAssertIntEquals(tc, d->ival[0], 3); - CuAssertIntEquals(tc, e->count, 1); - CuAssertIntEquals(tc, e->ival[0], -050); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_032(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1KB", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1024); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_033(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1MB", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1024 * 1024); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_034(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1GB", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1024 * 1024 * 1024); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_035(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0x5KB", "0xAMB", "0x1GB", "-d", "-0x40A01400", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 0); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 0x5 * 1024); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 0xA * 1024 * 1024); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 0x1 * 1024 * 1024 * 1024); - CuAssertIntEquals(tc, d->count, 1); - CuAssertIntEquals(tc, d->ival[0], -0x40A01400); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_036(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_037(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1", "2", "3", "4", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 3); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_038(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1", "2", "3", "-d1", "-d2", "-d3", "-d4", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 3); - CuAssertIntEquals(tc, d->count, 3); - CuAssertIntEquals(tc, d->ival[0], 1); - CuAssertIntEquals(tc, d->ival[1], 2); - CuAssertIntEquals(tc, d->ival[2], 3); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_039(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1", "2", "3", "-d1", "-d2", "-d3", "-d", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 3); - CuAssertIntEquals(tc, d->count, 3); - CuAssertIntEquals(tc, d->ival[0], 1); - CuAssertIntEquals(tc, d->ival[1], 2); - CuAssertIntEquals(tc, d->ival[2], 3); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_040(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1", "2", "3", "-d1", "-d2", "-d", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 3); - CuAssertIntEquals(tc, d->count, 2); - CuAssertIntEquals(tc, d->ival[0], 1); - CuAssertIntEquals(tc, d->ival[1], 2); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_041(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1", "2", "3", "-d1", "-d", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 3); - CuAssertIntEquals(tc, d->count, 1); - CuAssertIntEquals(tc, d->ival[0], 1); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_042(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1", "2", "3", "-d", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 3); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_043(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1", "2", "3", "--eps", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_044(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1", "2", "3", "--eps", "3", "--eqn", "6", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 1); - CuAssertIntEquals(tc, b->count, 1); - CuAssertIntEquals(tc, b->ival[0], 2); - CuAssertIntEquals(tc, c->count, 1); - CuAssertIntEquals(tc, c->ival[0], 3); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 1); - CuAssertIntEquals(tc, e->ival[0], 3); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_045(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "hello", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_046(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1.234", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 0); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_047(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "4", "hello", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 4); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_048(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "5", "1.234", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - CuAssertIntEquals(tc, a->count, 1); - CuAssertIntEquals(tc, a->ival[0], 5); - CuAssertIntEquals(tc, b->count, 0); - CuAssertIntEquals(tc, c->count, 0); - CuAssertIntEquals(tc, d->count, 0); - CuAssertIntEquals(tc, e->count, 0); - CuAssertIntEquals(tc, f->count, 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_049(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "-f", "2", "--filler=", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 2); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_050(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0x0g", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_051(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0o08", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_052(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "0b02", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_053(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1000GB", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argint_basic_054(CuTest* tc) { - struct arg_int* a = arg_int1(NULL, NULL, "a", "a is "); - struct arg_int* b = arg_int0(NULL, NULL, "b", "b is "); - struct arg_int* c = arg_int0(NULL, NULL, "c", "c is "); - struct arg_int* d = arg_intn("dD", "delta", "", 0, 3, "d can occur 0..3 times"); - struct arg_int* e = arg_int0(NULL, "eps,eqn", "", "eps is optional"); - struct arg_int* f = arg_intn("fF", "filler", "", 0, 3, "f can occur 0..3 times"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, e, f, end}; - int nerrors; - int i; - - char* argv[] = {"program", "1GBH", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - /* allow missing argument values for the f argument, and set defaults to -1 */ - f->hdr.flag |= ARG_HASOPTVALUE; - for (i = 0; i < f->hdr.maxcount; i++) - f->ival[i] = -1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertIntEquals(tc, nerrors, 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -CuSuite* get_argint_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_argint_basic_001); - SUITE_ADD_TEST(suite, test_argint_basic_002); - SUITE_ADD_TEST(suite, test_argint_basic_003); - SUITE_ADD_TEST(suite, test_argint_basic_004); - SUITE_ADD_TEST(suite, test_argint_basic_005); - SUITE_ADD_TEST(suite, test_argint_basic_006); - SUITE_ADD_TEST(suite, test_argint_basic_007); - SUITE_ADD_TEST(suite, test_argint_basic_008); - SUITE_ADD_TEST(suite, test_argint_basic_009); - SUITE_ADD_TEST(suite, test_argint_basic_010); - SUITE_ADD_TEST(suite, test_argint_basic_011); - SUITE_ADD_TEST(suite, test_argint_basic_012); - SUITE_ADD_TEST(suite, test_argint_basic_013); - SUITE_ADD_TEST(suite, test_argint_basic_014); - SUITE_ADD_TEST(suite, test_argint_basic_015); - SUITE_ADD_TEST(suite, test_argint_basic_016); - SUITE_ADD_TEST(suite, test_argint_basic_017); - SUITE_ADD_TEST(suite, test_argint_basic_018); - SUITE_ADD_TEST(suite, test_argint_basic_019); - SUITE_ADD_TEST(suite, test_argint_basic_020); - SUITE_ADD_TEST(suite, test_argint_basic_021); - SUITE_ADD_TEST(suite, test_argint_basic_022); - SUITE_ADD_TEST(suite, test_argint_basic_023); - SUITE_ADD_TEST(suite, test_argint_basic_024); - SUITE_ADD_TEST(suite, test_argint_basic_025); - SUITE_ADD_TEST(suite, test_argint_basic_026); - SUITE_ADD_TEST(suite, test_argint_basic_027); - SUITE_ADD_TEST(suite, test_argint_basic_028); - SUITE_ADD_TEST(suite, test_argint_basic_029); - SUITE_ADD_TEST(suite, test_argint_basic_030); - SUITE_ADD_TEST(suite, test_argint_basic_031); - SUITE_ADD_TEST(suite, test_argint_basic_032); - SUITE_ADD_TEST(suite, test_argint_basic_033); - SUITE_ADD_TEST(suite, test_argint_basic_034); - SUITE_ADD_TEST(suite, test_argint_basic_035); - SUITE_ADD_TEST(suite, test_argint_basic_036); - SUITE_ADD_TEST(suite, test_argint_basic_037); - SUITE_ADD_TEST(suite, test_argint_basic_038); - SUITE_ADD_TEST(suite, test_argint_basic_039); - SUITE_ADD_TEST(suite, test_argint_basic_040); - SUITE_ADD_TEST(suite, test_argint_basic_041); - SUITE_ADD_TEST(suite, test_argint_basic_042); - SUITE_ADD_TEST(suite, test_argint_basic_043); - SUITE_ADD_TEST(suite, test_argint_basic_044); - SUITE_ADD_TEST(suite, test_argint_basic_045); - SUITE_ADD_TEST(suite, test_argint_basic_046); - SUITE_ADD_TEST(suite, test_argint_basic_047); - SUITE_ADD_TEST(suite, test_argint_basic_048); - SUITE_ADD_TEST(suite, test_argint_basic_049); - SUITE_ADD_TEST(suite, test_argint_basic_050); - SUITE_ADD_TEST(suite, test_argint_basic_051); - SUITE_ADD_TEST(suite, test_argint_basic_052); - SUITE_ADD_TEST(suite, test_argint_basic_053); - SUITE_ADD_TEST(suite, test_argint_basic_054); - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testarglit.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testarglit.c deleted file mode 100644 index 15ed6f1f..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testarglit.c +++ /dev/null @@ -1,538 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include - -#include "CuTest.h" -#include "argtable3.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#endif - -void test_arglit_basic_001(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "--help", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 2); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, help->count == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_002(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-cDd", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 2); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_003(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-cdDd", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 3); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_004(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-CDd", "--delta", "--delta", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 4); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_005(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "--delta", "-cD", "-b", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 2); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_006(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-D", "-B", "--delta", "-C", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 2); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_007(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-D", "-B", "--delta", "-C", "--hello", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertTrue(tc, b->count == 1); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 2); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_008(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-D", "-B", "--delta", "-C", "--world", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertTrue(tc, b->count == 1); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 2); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_009(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-c", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 0); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_010(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-D", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 2); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 1); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_011(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-CD", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 1); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_012(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-Dd", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 2); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_013(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-cddddd", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 4); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_014(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-ccddd", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 3); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_015(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-C", "-d", "-D", "--delta", "-b", "-B", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 3); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_016(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-C", "-d", "-D", "--delta", "--hello", "--world", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 1); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 3); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_arglit_basic_017(CuTest* tc) { - struct arg_lit* a = arg_lit0(NULL, "hello,world", "either --hello or --world or none"); - struct arg_lit* b = arg_lit0("bB", NULL, "either -b or -B or none"); - struct arg_lit* c = arg_lit1("cC", NULL, "either -c or -C"); - struct arg_lit* d = arg_litn("dD", "delta", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_lit* help = arg_lit0(NULL, "help", "print this help and exit"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, help, end}; - int nerrors; - - char* argv[] = {"program", "-C", "-d", "-D", "--delta", "--hello", "X", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 1); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertTrue(tc, d->count == 3); - CuAssertTrue(tc, help->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -CuSuite* get_arglit_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_arglit_basic_001); - SUITE_ADD_TEST(suite, test_arglit_basic_002); - SUITE_ADD_TEST(suite, test_arglit_basic_003); - SUITE_ADD_TEST(suite, test_arglit_basic_004); - SUITE_ADD_TEST(suite, test_arglit_basic_005); - SUITE_ADD_TEST(suite, test_arglit_basic_006); - SUITE_ADD_TEST(suite, test_arglit_basic_007); - SUITE_ADD_TEST(suite, test_arglit_basic_008); - SUITE_ADD_TEST(suite, test_arglit_basic_009); - SUITE_ADD_TEST(suite, test_arglit_basic_010); - SUITE_ADD_TEST(suite, test_arglit_basic_011); - SUITE_ADD_TEST(suite, test_arglit_basic_012); - SUITE_ADD_TEST(suite, test_arglit_basic_013); - SUITE_ADD_TEST(suite, test_arglit_basic_014); - SUITE_ADD_TEST(suite, test_arglit_basic_015); - SUITE_ADD_TEST(suite, test_arglit_basic_016); - SUITE_ADD_TEST(suite, test_arglit_basic_017); - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargrex.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargrex.c deleted file mode 100644 index d3c3f7dd..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargrex.c +++ /dev/null @@ -1,299 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include - -#include "CuTest.h" -#include "argtable3.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#endif - -void test_argrex_basic_001(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--beta", "world", "goodbye", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "world"); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "goodbye"); - CuAssertTrue(tc, d->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argrex_basic_002(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "GoodBye", "--beta", "World", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "World"); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "GoodBye"); - CuAssertTrue(tc, d->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argrex_basic_003(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--beta", "world", "GOODBYE", "GoodBye", "gOoDbyE", "Anything", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "world"); - CuAssertTrue(tc, c->count == 3); - CuAssertStrEquals(tc, c->sval[0], "GOODBYE"); - CuAssertStrEquals(tc, c->sval[1], "GoodBye"); - CuAssertStrEquals(tc, c->sval[2], "gOoDbyE"); - CuAssertTrue(tc, d->count == 1); - CuAssertStrEquals(tc, d->sval[0], "Anything"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argrex_basic_004(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--beta", "world", "GOODBYE", "AnyHow", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "world"); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "GOODBYE"); - CuAssertTrue(tc, d->count == 1); - CuAssertStrEquals(tc, d->sval[0], "AnyHow"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argrex_basic_005(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-a", "hello", "--beta", "world", "GOODBYE", "AnyHow", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 0); - - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->sval[0], "hello"); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "world"); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "GOODBYE"); - CuAssertTrue(tc, d->count == 1); - CuAssertStrEquals(tc, d->sval[0], "AnyHow"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argrex_basic_006(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--beta", "WORLD", "goodbye", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argrex_basic_007(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--beta", "World", "goodby", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argrex_basic_008(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--beta", "world", "GoodBye", "Goodbye", "gOoDbyE", "Anything", "goodbye", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argrex_basic_009(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--beta", "world", "GoodBye", "Goodbye", "gOoDbyE", "Anything", "Anytime", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argrex_basic_010(CuTest* tc) { - struct arg_rex* a = arg_rex0("a", NULL, "hello", NULL, 0, "blah blah"); - struct arg_rex* b = arg_rex1(NULL, "beta", "[Ww]orld", NULL, 0, "blah blah"); - struct arg_rex* c = arg_rexn(NULL, NULL, "goodbye", NULL, 1, 5, ARG_REX_ICASE, "blah blah"); - struct arg_rex* d = arg_rex0(NULL, NULL, "any.*", NULL, ARG_REX_ICASE, "blah blah"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--beta", "world", "GoodBye", "Goodbye", "Anything", "-a", "Hello", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - CuAssertTrue(tc, nerrors == 1); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -CuSuite* get_argrex_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_argrex_basic_001); - SUITE_ADD_TEST(suite, test_argrex_basic_002); - SUITE_ADD_TEST(suite, test_argrex_basic_003); - SUITE_ADD_TEST(suite, test_argrex_basic_004); - SUITE_ADD_TEST(suite, test_argrex_basic_005); - SUITE_ADD_TEST(suite, test_argrex_basic_006); - SUITE_ADD_TEST(suite, test_argrex_basic_007); - SUITE_ADD_TEST(suite, test_argrex_basic_008); - SUITE_ADD_TEST(suite, test_argrex_basic_009); - SUITE_ADD_TEST(suite, test_argrex_basic_010); - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargstr.c b/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargstr.c deleted file mode 100644 index fccae725..00000000 --- a/parallel/parallel_src/extern/argtable3-3.2.2/tests/testargstr.c +++ /dev/null @@ -1,531 +0,0 @@ -/******************************************************************************* - * This file is part of the argtable3 library. - * - * Copyright (C) 2013-2019 Tom G. Huang - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of STEWART HEITMANN nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ******************************************************************************/ - -#include - -#include "CuTest.h" -#include "argtable3.h" - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4204) -#endif - -void test_argstr_basic_001(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--hello=string1", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 2); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->sval[0], "string1"); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_002(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-cstring1", "-Dstring2", "-dstring3", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "string1"); - CuAssertTrue(tc, d->count == 2); - CuAssertStrEquals(tc, d->sval[0], "string2"); - CuAssertStrEquals(tc, d->sval[1], "string3"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_003(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Cstring1", "--delta=string2", "--delta=string3", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "string1"); - CuAssertTrue(tc, d->count == 2); - CuAssertStrEquals(tc, d->sval[0], "string2"); - CuAssertStrEquals(tc, d->sval[1], "string3"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_004(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "--delta=string1", "-cstring2", "-Dstring3", "-bstring4", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "string4"); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "string2"); - CuAssertTrue(tc, d->count == 2); - CuAssertStrEquals(tc, d->sval[0], "string1"); - CuAssertStrEquals(tc, d->sval[1], "string3"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_005(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Dstring1", "-Bstring2", "--delta=string3", "-Cstring4", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "string2"); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "string4"); - CuAssertTrue(tc, d->count == 2); - CuAssertStrEquals(tc, d->sval[0], "string1"); - CuAssertStrEquals(tc, d->sval[1], "string3"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_006(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Dstring1", "-Bstring2", "--delta=string3", "-Cstring4", "--hello=string5", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->sval[0], "string5"); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "string2"); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "string4"); - CuAssertTrue(tc, d->count == 2); - CuAssertStrEquals(tc, d->sval[0], "string1"); - CuAssertStrEquals(tc, d->sval[1], "string3"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_007(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Dstring1", "-Bstring2", "--delta=string3", "-Cstring4", "--world=string5", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - if (nerrors > 0) - arg_print_errors(stdout, end, argv[0]); - - CuAssertTrue(tc, nerrors == 0); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->sval[0], "string5"); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "string2"); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "string4"); - CuAssertTrue(tc, d->count == 2); - CuAssertStrEquals(tc, d->sval[0], "string1"); - CuAssertStrEquals(tc, d->sval[1], "string3"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_008(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-cstring1", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "string1"); - CuAssertTrue(tc, d->count == 0); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_009(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Dstring1", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 2); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 1); - CuAssertStrEquals(tc, d->sval[0], "string1"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_010(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Cstring1", "-Dstring2", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "string1"); - CuAssertTrue(tc, d->count == 1); - CuAssertStrEquals(tc, d->sval[0], "string2"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_011(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Dstring1", "-dstring2", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 0); - CuAssertTrue(tc, d->count == 2); - CuAssertStrEquals(tc, d->sval[0], "string1"); - CuAssertStrEquals(tc, d->sval[1], "string2"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_012(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-cs1", "-ds2", "-ds3", "-ds4", "-ds5", "-ds6", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "s1"); - CuAssertTrue(tc, d->count == 4); - CuAssertStrEquals(tc, d->sval[0], "s2"); - CuAssertStrEquals(tc, d->sval[1], "s3"); - CuAssertStrEquals(tc, d->sval[2], "s4"); - CuAssertStrEquals(tc, d->sval[3], "s5"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_013(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-cs1", "-cs2", "-ds3", "-ds4", "-ds5", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "s1"); - CuAssertTrue(tc, d->count == 3); - CuAssertStrEquals(tc, d->sval[0], "s3"); - CuAssertStrEquals(tc, d->sval[1], "s4"); - CuAssertStrEquals(tc, d->sval[2], "s5"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_014(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Cs1", "-ds2", "-Ds3", "--delta=s4", "-bs5", "-Bs6", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 0); - CuAssertTrue(tc, b->count == 1); - CuAssertStrEquals(tc, b->sval[0], "s5"); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "s1"); - CuAssertTrue(tc, d->count == 3); - CuAssertStrEquals(tc, d->sval[0], "s2"); - CuAssertStrEquals(tc, d->sval[1], "s3"); - CuAssertStrEquals(tc, d->sval[2], "s4"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_015(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Cs1", "-ds2", "-Ds3", "--delta=s4", "--hello=s5", "--world=s6", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->sval[0], "s5"); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "s1"); - CuAssertTrue(tc, d->count == 3); - CuAssertStrEquals(tc, d->sval[0], "s2"); - CuAssertStrEquals(tc, d->sval[1], "s3"); - CuAssertStrEquals(tc, d->sval[2], "s4"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -void test_argstr_basic_016(CuTest* tc) { - struct arg_str* a = arg_str0(NULL, "hello,world", "STRVAL", "either --hello or --world or none"); - struct arg_str* b = arg_str0("bB", NULL, "STRVAL", "either -b or -B or none"); - struct arg_str* c = arg_str1("cC", NULL, "STRVAL", "either -c or -C"); - struct arg_str* d = arg_strn("dD", "delta", "STRVAL", 2, 4, "-d|-D|--delta 2..4 occurences"); - struct arg_end* end = arg_end(20); - void* argtable[] = {a, b, c, d, end}; - int nerrors; - - char* argv[] = {"program", "-Cs1", "-ds2", "-Ds3", "--delta=s4", "--hello=s5", "X", NULL}; - int argc = sizeof(argv) / sizeof(char*) - 1; - - CuAssertTrue(tc, arg_nullcheck(argtable) == 0); - - nerrors = arg_parse(argc, argv, argtable); - - CuAssertTrue(tc, nerrors == 1); - CuAssertTrue(tc, a->count == 1); - CuAssertStrEquals(tc, a->sval[0], "s5"); - CuAssertTrue(tc, b->count == 0); - CuAssertTrue(tc, c->count == 1); - CuAssertStrEquals(tc, c->sval[0], "s1"); - CuAssertTrue(tc, d->count == 3); - CuAssertStrEquals(tc, d->sval[0], "s2"); - CuAssertStrEquals(tc, d->sval[1], "s3"); - CuAssertStrEquals(tc, d->sval[2], "s4"); - - arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0])); -} - -CuSuite* get_argstr_testsuite() { - CuSuite* suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_argstr_basic_001); - SUITE_ADD_TEST(suite, test_argstr_basic_002); - SUITE_ADD_TEST(suite, test_argstr_basic_003); - SUITE_ADD_TEST(suite, test_argstr_basic_004); - SUITE_ADD_TEST(suite, test_argstr_basic_005); - SUITE_ADD_TEST(suite, test_argstr_basic_006); - SUITE_ADD_TEST(suite, test_argstr_basic_007); - SUITE_ADD_TEST(suite, test_argstr_basic_008); - SUITE_ADD_TEST(suite, test_argstr_basic_009); - SUITE_ADD_TEST(suite, test_argstr_basic_010); - SUITE_ADD_TEST(suite, test_argstr_basic_011); - SUITE_ADD_TEST(suite, test_argstr_basic_012); - SUITE_ADD_TEST(suite, test_argstr_basic_013); - SUITE_ADD_TEST(suite, test_argstr_basic_014); - SUITE_ADD_TEST(suite, test_argstr_basic_015); - SUITE_ADD_TEST(suite, test_argstr_basic_016); - return suite; -} - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif diff --git a/parallel/parallel_src/tests/CMakeLists.txt b/parallel/parallel_src/tests/CMakeLists.txt index f5f0e87e..104b54a2 100644 --- a/parallel/parallel_src/tests/CMakeLists.txt +++ b/parallel/parallel_src/tests/CMakeLists.txt @@ -31,8 +31,11 @@ target_link_libraries(parallel_contraction_mpi_test PRIVATE libmodified_kahip_in catch_discover_tests( parallel_contraction_mpi_test TEST_PREFIX "unit-" - PROPERTIES crosscompiling_emulator "mpirun -n 4" + PROPERTIES crosscompiling_emulator "mpirun -np 5" REPORTER xml OUTPUT_DIR . OUTPUT_PREFIX "unit-" OUTPUT_SUFFIX .xml) + +target_link_libraries(parallel_contraction_test PRIVATE kahip_options kahip_warnings) +target_link_libraries(parallel_contraction_mpi_test PRIVATE kahip_options kahip_warnings) \ No newline at end of file From e694145b14b2fa1a279202c9e7bf3a49661b5f7e Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 4 Oct 2024 17:50:40 +0100 Subject: [PATCH 44/64] Linking fixed --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61fd0177..0f66a981 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,7 +290,7 @@ target_link_libraries(evaluator PRIVATE kahip_headers) install(TARGETS evaluator DESTINATION bin) add_executable(edge_evaluator app/edge_evaluator.cpp) -target_link_libraries(evaluator PRIVATE libkaffpa libmapping) +target_link_libraries(edge_evaluator PRIVATE libkaffpa libmapping) target_compile_definitions(edge_evaluator PRIVATE "-DMODE_EVALUATOR") target_link_libraries(edge_evaluator PRIVATE OpenMP::OpenMP_CXX) target_link_libraries(edge_evaluator PRIVATE kahip_headers) @@ -361,7 +361,7 @@ install(TARGETS node_ordering DESTINATION bin) if(metis_FOUND) add_executable(fast_node_ordering app/fast_node_ordering.cpp) - target_link_libraries(fast_node_ordering PRIVATE libkaffpa libmapping) + target_link_libraries(fast_node_ordering PRIVATE libkaffpa libmapping libnodeordering) target_compile_definitions(fast_node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING -DFASTORDERING") target_link_libraries(fast_node_ordering PRIVATE OpenMP::OpenMP_CXX metis GKlib) target_link_libraries(fast_node_ordering PRIVATE kahip_headers) From 6ab1d15bf579dc17a0990877de82cdb105ecc467 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 7 Oct 2024 10:16:52 +0100 Subject: [PATCH 45/64] Sanitisers --- .devcontainer/Dockerfile | 7 ++++++- parallel/modified_kahip/CMakeLists.txt | 2 ++ parallel/parallel_src/CMakeLists.txt | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2fb5aec6..e0c881a6 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -10,9 +10,14 @@ RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \ fi \ && rm -f /tmp/reinstall-cmake.sh +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install ubuntu-dbgsym-keyring \ + && printf "deb http://ddebs.ubuntu.com noble main restricted universe multiverse\ndeb http://ddebs.ubuntu.com noble-updates main restricted universe multiverse\ndeb http://ddebs.ubuntu.com noble-proposed main restricted universe multiverse" | \ + tee -a /etc/apt/sources.list.d/ddebs.list + # [Optional] Uncomment this section to install additional vcpkg ports. RUN su vscode -c "export VCPKG_FORCE_SYSTEM_BINARIES=arm && ${VCPKG_ROOT}/vcpkg install catch2 fmt metis" # [Optional] Uncomment this section to install additional packages. RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install gfortran openmpi-bin libopenmpi-dev ninja-build clang-tidy cppcheck iwyu + && apt-get -y install gfortran openmpi-bin openmpi-bin-dbgsym libopenmpi-dev libopenmpi3t64 ninja-build clang-tidy cppcheck iwyu diff --git a/parallel/modified_kahip/CMakeLists.txt b/parallel/modified_kahip/CMakeLists.txt index 2f445567..5d75d8bb 100644 --- a/parallel/modified_kahip/CMakeLists.txt +++ b/parallel/modified_kahip/CMakeLists.txt @@ -66,6 +66,8 @@ set(LIBMODIFIED_KAFFPA_SOURCE_FILES lib/parallel_mh/galinier_combine/construct_partition.cpp lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp) add_library(libmodified_kaffpa OBJECT ${LIBMODIFIED_KAFFPA_SOURCE_FILES}) +target_link_libraries(libmodified_kaffpa PUBLIC kahip_options kahip_warnings) + set(LIBMODIFIED_KAFFPA_PARALLEL_SOURCE_FILES lib/parallel_mh/parallel_mh_async.cpp diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index 57be5b1f..aeea9d5b 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -36,6 +36,7 @@ set(LIBPARALLEL_SOURCE_FILES lib/tools/random_functions.cpp lib/tools/distributed_quality_metrics.cpp) add_library(parallel ${LIBPARALLEL_SOURCE_FILES}) +target_link_libraries(parallel PUBLIC kahip_options kahip_warnings) target_link_libraries(parallel PUBLIC libmodified_kahip_interface) target_link_libraries(parallel PUBLIC argtable3) target_link_libraries(parallel PUBLIC kahip_version) @@ -70,7 +71,7 @@ target_link_libraries(libdspac PUBLIC MPI::MPI_CXX) add_executable(parhip app/parhip.cpp) target_compile_definitions(parhip PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") -target_link_libraries(parhip PRIVATE libmodified_kahip_interface) +target_link_libraries(parhip PRIVATE libmodified_kahip_interface kahip_options kahip_warnings) target_link_libraries(parhip PRIVATE parallel) target_link_libraries(parhip PRIVATE MPI::MPI_CXX) target_link_libraries(parhip PRIVATE argtable3) From 338844f6b3f9c924c25d35f697160b6d4abcfc93 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 7 Oct 2024 11:35:40 +0100 Subject: [PATCH 46/64] Fixing formating and clang-tidy --- .clang-format | 8 +- .clang-tidy | 30 ++ cmake/KaHIPSettings.cmake | 17 +- .../lib/communication/mpi_tools.h | 245 ++++----- .../lib/communication/mpi_types.h | 358 ++++++------- .../parallel_contraction.cpp | 500 +++++++++--------- .../parallel_contraction.h | 68 +-- 7 files changed, 635 insertions(+), 591 deletions(-) create mode 100644 .clang-tidy diff --git a/.clang-format b/.clang-format index ff5635c8..1dbeecde 100644 --- a/.clang-format +++ b/.clang-format @@ -1,3 +1,7 @@ BasedOnStyle: Chromium -UseTab: ForContinuationAndIndentation -TabWidth: 2 \ No newline at end of file +TabWidth: 2 +IndentWidth: 2 +SortIncludes: CaseInsensitive +Language: Cpp +Standard: c++20 +QualifierAlignment: Right diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..81fa7aa2 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,30 @@ +--- +Checks: "*, + -abseil-*, + -altera-*, + -android-*, + -fuchsia-*, + -google-*, + -llvm*, + -modernize-use-trailing-return-type, + -zircon-*, + -readability-else-after-return, + -readability-static-accessed-through-instance, + -readability-avoid-const-params-in-decls, + -cppcoreguidelines-non-private-member-variables-in-classes, + -misc-non-private-member-variables-in-classes, +" +WarningsAsErrors: '' +HeaderFilterRegex: '' +FormatStyle: none + +CheckOptions: + - key: readability-identifier-length.IgnoredVariableNames + value: 'x|y|z|i|j|k|G|Q' + - key: readability-identifier-length.IgnoredParameterNames + value: 'x|y|z|i|j|k|G|Q' + + + + + diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index 23072a2e..dd9ae590 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -9,6 +9,7 @@ kahip_supports_sanitizers() option(kahip_ENABLE_IPO "Enable IPO/LTO" OFF) option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) +option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) option(kahip_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) option(kahip_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) option(kahip_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" ${SUPPORTS_UBSAN}) @@ -35,13 +36,15 @@ kahip_set_project_warnings( "" "") -kahip_enable_sanitizers( - kahip_options - ${kahip_ENABLE_SANITIZER_ADDRESS} - ${kahip_ENABLE_SANITIZER_LEAK} - ${kahip_ENABLE_SANITIZER_UNDEFINED} - ${kahip_ENABLE_SANITIZER_THREAD} - ${kahip_ENABLE_SANITIZER_MEMORY}) +if (kahip_ENABLE_SANITIZERS) + kahip_enable_sanitizers( + kahip_options + ${kahip_ENABLE_SANITIZER_ADDRESS} + ${kahip_ENABLE_SANITIZER_LEAK} + ${kahip_ENABLE_SANITIZER_UNDEFINED} + ${kahip_ENABLE_SANITIZER_THREAD} + ${kahip_ENABLE_SANITIZER_MEMORY}) +endif () if(kahip_ENABLE_CLANG_TIDY) kahip_enable_clang_tidy(kahip_options ${kahip_WARNINGS_AS_ERRORS}) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 362afea0..e0e0a43a 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -8,14 +8,13 @@ #ifndef MPI_TOOLS_HMESDXF2 #define MPI_TOOLS_HMESDXF2 -#ifndef _CRAYC -#define _CRAYC 0 -#endif - #include +#include #include #include #include +#include +#include #include "data_structure/parallel_graph_access.h" #include "mpi_types.h" @@ -23,93 +22,94 @@ class mpi_tools { public: - void collect_and_write_labels(MPI_Comm communicator, - PPartitionConfig& config, - parallel_graph_access& G); - - void collect_parallel_graph_to_local_graph(MPI_Comm communicator, - PPartitionConfig& config, - parallel_graph_access& G, - complete_graph_access& Q); - - // G is input (only on ROOT) - // G is output (on every other PE) - void distribute_local_graph(MPI_Comm communicator, - PPartitionConfig& config, - complete_graph_access& G); + void collect_and_write_labels(MPI_Comm communicator, + PPartitionConfig& config, + parallel_graph_access& G); + + void collect_parallel_graph_to_local_graph(MPI_Comm communicator, + PPartitionConfig& config, + parallel_graph_access& G, + complete_graph_access& Q); + + // G is input (only on ROOT) + // G is output (on every other PE) + void distribute_local_graph(MPI_Comm communicator, + PPartitionConfig& config, + complete_graph_access& G); }; namespace mpi { template struct mpi_packed_message { - std::vector packed_message; - std::vector offsets; - std::vector lengths; + std::vector packed_message; + std::vector offsets; + std::vector lengths; }; auto exchange_num_messages(std::vector const& num_sent_per_rank, - MPI_Comm communicator) -> std::vector; + MPI_Comm communicator) -> std::vector; template - requires std::ranges::forward_range> + requires std::ranges::forward_range> auto pack_messages(const Input& messages) -> mpi_packed_message< - std::ranges::range_value_t>> { - using InnerRange = std::ranges::range_value_t; - using ElementType = std::ranges::range_value_t; - - // Flattening the container of containers using views::join - auto flattened_view = messages | std::ranges::views::join; - std::vector flattened_vector{flattened_view.begin(), - flattened_view.end()}; - - // Calculating lengths of the inner ranges - std::vector lengths; - lengths.reserve(std::ranges::distance(messages)); - for (const auto& inner : messages) { - lengths.push_back(static_cast(std::ranges::distance(inner))); - } - - // Calculating offsets using exclusive_scan - std::vector offsets(lengths.size()); - std::exclusive_scan(lengths.begin(), lengths.end(), offsets.begin(), 0); - - return mpi_packed_message{flattened_vector, offsets, lengths}; + std::ranges::range_value_t>> { + using InnerRange = std::ranges::range_value_t; + using ElementType = std::ranges::range_value_t; + + // Flattening the container of containers using views::join + auto flattened_view = messages | std::ranges::views::join; + std::vector flattened_vector{flattened_view.begin(), + flattened_view.end()}; + + // Calculating lengths of the inner ranges + std::vector lengths; + lengths.reserve(std::ranges::distance(messages)); + for (const auto& inner : messages) { + lengths.push_back(static_cast(std::ranges::distance(inner))); + } + + // Calculating offsets using exclusive_scan + std::vector offsets(lengths.size()); + std::exclusive_scan(lengths.begin(), lengths.end(), offsets.begin(), 0); + + return mpi_packed_message{flattened_vector, offsets, lengths}; } template auto unpack_messages(const mpi_packed_message& packed_message) - -> std::vector> { - const auto& [recv_buf, recv_displs, recv_counts] = packed_message; - std::size_t num_ranks = recv_counts.size(); + -> std::vector> { + const auto& [recv_buf, recv_displs, recv_counts] = packed_message; + std::size_t num_ranks = recv_counts.size(); - // Ensure recv_displs and recv_counts have the same size - assert(recv_displs.size() == num_ranks); + // Ensure recv_displs and recv_counts have the same size + assert(recv_displs.size() == num_ranks); - std::vector> result; - result.reserve(num_ranks); + std::vector> result; + result.reserve(num_ranks); - // Use std::transform to construct the sub-vectors - std::transform(recv_displs.begin(), recv_displs.end(), recv_counts.begin(), - std::back_inserter(result), [&recv_buf](int displ, int count) { - auto const start = recv_buf.begin() + displ; - auto const end = start + count; - return std::vector(start, end); - }); + // Use std::transform to construct the sub-vectors + std::transform(recv_displs.begin(), recv_displs.end(), recv_counts.begin(), + std::back_inserter(result), [&recv_buf](int displ, int count) { + auto const start = recv_buf.begin() + displ; + auto const end = start + count; + return std::vector(start, end); + }); - return result; + return result; } template concept mpi_nested_range = requires(Input) { - requires std::ranges::forward_range; - requires std::ranges::forward_range>; - requires mpi_datatype>>; + requires std::ranges::forward_range; + requires std::ranges::forward_range>; + requires mpi_datatype< + std::ranges::range_value_t>>; }; template -using mpi_alltoall_t = std::vector>>>; +using mpi_alltoall_t = std::vector< + std::vector>>>; /** * @brief Performs an MPI all-to-all communication operation, distributing @@ -129,63 +129,66 @@ using mpi_alltoall_t = std::vector auto all_to_all(Input const& sends, MPI_Comm communicator) - -> mpi_alltoall_t { - using InnerRange = std::ranges::range_value_t; - using ElementType = std::ranges::range_value_t; - - PEID rank, size; - MPI_Comm_rank(communicator, &rank); - MPI_Comm_size(communicator, &size); - - // Packing messages - auto [send_packed_messages, send_offsets, send_lengths] = - mpi::pack_messages(sends); - - if (send_offsets.size() != send_lengths.size()) { - throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + - std::to_string(send_offsets.size()) + - ") != send_lengths.size() (" + - std::to_string(send_lengths.size()) + ")"); - } else if ((send_offsets.size() != static_cast(size)) || - (send_lengths.size() != static_cast(size))) { - throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + - std::to_string(send_offsets.size()) + - ") != mpi size (" + std::to_string(size) + ")"); - } - - // Exchanging message sizes - auto const recv_lengths = exchange_num_messages(send_lengths, communicator); - - // Calculating total receive buffer size - auto const recv_buff_size = - std::accumulate(recv_lengths.begin(), recv_lengths.end(), 0); - - // Preparing receive buffers - std::vector recv_packed_messages(recv_buff_size); - std::vector recv_offsets(size, 0); - - // Calculating receive offsets - std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), - recv_offsets.begin(), 0); - - // Performing MPI communication - auto mpi_error = MPI_Alltoallv( - send_packed_messages.data(), send_lengths.data(), send_offsets.data(), - get_mpi_datatype(), recv_packed_messages.data(), - recv_lengths.data(), recv_offsets.data(), get_mpi_datatype(), - communicator); - - if (mpi_error != MPI_SUCCESS) { - char error_string[MPI_MAX_ERROR_STRING]; - int length_of_error_string; - MPI_Error_string(mpi_error, error_string, &length_of_error_string); - throw std::runtime_error( - std::string("mpi::all_to_all() failed with error: ") + error_string); - } - - // Unpacking messages - return mpi::unpack_messages( - {recv_packed_messages, recv_offsets, recv_lengths}); + -> mpi_alltoall_t { + using InnerRange = std::ranges::range_value_t; + using ElementType = std::ranges::range_value_t; + + PEID rank{}; + PEID size{}; + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); + + // Packing messages + auto [send_packed_messages, send_offsets, send_lengths] = + mpi::pack_messages(sends); + + if (send_offsets.size() != send_lengths.size()) { + throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + + std::to_string(send_offsets.size()) + + ") != send_lengths.size() (" + + std::to_string(send_lengths.size()) + ")"); + } else if ((send_offsets.size() != static_cast(size)) || + (send_lengths.size() != static_cast(size))) { + throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + + std::to_string(send_offsets.size()) + + ") != mpi size (" + std::to_string(size) + ")"); + } + + // Exchanging message sizes + auto const recv_lengths = exchange_num_messages(send_lengths, communicator); + + // Calculating total receive buffer size + auto const recv_buff_size = + std::accumulate(recv_lengths.begin(), recv_lengths.end(), 0); + + // Preparing receive buffers + std::vector recv_packed_messages(recv_buff_size); + std::vector recv_offsets(size, 0); + + // Calculating receive offsets + std::exclusive_scan(recv_lengths.begin(), recv_lengths.end(), + recv_offsets.begin(), 0); + + // Performing MPI communication + auto mpi_error = MPI_Alltoallv( + send_packed_messages.data(), send_lengths.data(), send_offsets.data(), + get_mpi_datatype(), recv_packed_messages.data(), + recv_lengths.data(), recv_offsets.data(), get_mpi_datatype(), + communicator); + + if (mpi_error != MPI_SUCCESS) { + std::array error_string{}; + int length_of_error_string = 0; + MPI_Error_string(mpi_error, error_string.data(), &length_of_error_string); + auto mpi_error_message = + std::string_view(error_string.data(), length_of_error_string); + throw std::runtime_error(std::format( + "mpi::all_to_all() failed with error: {}", mpi_error_message)); + } + + // Unpacking messages + return mpi::unpack_messages( + {recv_packed_messages, recv_offsets, recv_lengths}); } } // namespace mpi diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index c4fd36a7..471e6969 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -10,9 +10,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -21,12 +21,12 @@ namespace mpi { namespace details { // Define an enum to represent the kind of MPI data -enum class mpi_data_kinds { none, base, composite }; +enum class mpi_data_kinds : uint8_t { none, base, composite }; // Trait to determine the MPI data kind template struct mpi_data_kind_trait { - static constexpr mpi_data_kinds kind = mpi_data_kinds::none; + static constexpr mpi_data_kinds kind = mpi_data_kinds::none; }; // Mutex for thread safety; needed for the cleanup function @@ -34,71 +34,73 @@ inline std::mutex mpi_type_mutex; // Function to get the list of custom MPI datatypes inline std::vector& get_custom_mpi_types() { - static std::vector custom_types; - return custom_types; + static std::vector custom_types; + return custom_types; } // Declare the keyval for the attribute inline constinit int mpi_type_cleanup_keyval = MPI_KEYVAL_INVALID; // Cleanup function called during MPI_Finalize -inline int mpi_type_cleanup(MPI_Comm comm, - int keyval, - void* attribute_val, - void* extra_state) { - std::scoped_lock lock{mpi_type_mutex}; - - auto& custom_types = get_custom_mpi_types(); - for (MPI_Datatype& datatype : custom_types) { - if (datatype != MPI_DATATYPE_NULL) { - MPI_Type_free(&datatype); - datatype = MPI_DATATYPE_NULL; - } - } - custom_types.clear(); - - // Free the keyval - MPI_Comm_free_keyval(&mpi_type_cleanup_keyval); - mpi_type_cleanup_keyval = MPI_KEYVAL_INVALID; - - return MPI_SUCCESS; +inline int mpi_type_cleanup(MPI_Comm /*comm*/, + int /*keyval*/, + void* /*attribute_val*/, + void* /*extra_state*/) { + std::scoped_lock const lock{mpi_type_mutex}; + + auto& custom_types = get_custom_mpi_types(); + for (MPI_Datatype& datatype : custom_types) { + if (datatype != MPI_DATATYPE_NULL) { + MPI_Type_free(&datatype); + datatype = MPI_DATATYPE_NULL; + } + } + custom_types.clear(); + + // Free the keyval + MPI_Comm_free_keyval(&mpi_type_cleanup_keyval); + mpi_type_cleanup_keyval = MPI_KEYVAL_INVALID; + + return MPI_SUCCESS; } // Initialize the attribute and set the cleanup function inline void initialize_mpi_type_cleanup() { - // Use a static function-local variable to ensure initialization occurs only - // once - static bool initialized = []() { - int mpi_error = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, mpi_type_cleanup, &mpi_type_cleanup_keyval, nullptr); - if (mpi_error != MPI_SUCCESS) { - throw std::runtime_error("MPI_Comm_create_keyval() failed"); - } - - // Set an attribute on MPI_COMM_SELF - mpi_error = - MPI_Comm_set_attr(MPI_COMM_SELF, mpi_type_cleanup_keyval, nullptr); - if (mpi_error != MPI_SUCCESS) { - throw std::runtime_error("MPI_Comm_set_attr() failed"); - } - - return true; - }(); - (void)initialized; // Suppress unused variable warning + // Use a static function-local variable to ensure initialization occurs only + // once + static bool const initialized = []() { + int mpi_error = + MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, mpi_type_cleanup, + &mpi_type_cleanup_keyval, nullptr); + if (mpi_error != MPI_SUCCESS) { + throw std::runtime_error("MPI_Comm_create_keyval() failed"); + } + + // Set an attribute on MPI_COMM_WORLD + mpi_error = + MPI_Comm_set_attr(MPI_COMM_WORLD, mpi_type_cleanup_keyval, nullptr); + if (mpi_error != MPI_SUCCESS) { + throw std::runtime_error("MPI_Comm_set_attr() failed"); + } + + return true; + }(); + (void)initialized; // Suppress unused variable warning } // Detect aggregate types and set kind to composite template - requires std::is_aggregate_v + requires std::is_aggregate_v struct mpi_data_kind_trait { - static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; + static constexpr mpi_data_kinds kind = mpi_data_kinds::composite; }; // Macro to specialize mpi_data_kind_trait for unique base types -#define MPI_BASE_TYPE_KIND(type) \ - template <> \ - struct mpi_data_kind_trait { \ - static constexpr mpi_data_kinds kind = mpi_data_kinds::base; \ - }; +#define MPI_BASE_TYPE_KIND(type) \ + template <> \ + struct mpi_data_kind_trait { \ + static constexpr mpi_data_kinds kind = mpi_data_kinds::base; \ + }; // Specializations for unique base types MPI_BASE_TYPE_KIND(char) @@ -126,13 +128,13 @@ template struct mpi_datatype_trait; // Macro to specialize mpi_datatype_trait for unique base types -#define MPI_DATATYPE_TRAIT(type, mpi_type_const) \ - template <> \ - struct mpi_datatype_trait { \ - static MPI_Datatype get_mpi_type() { \ - return mpi_type_const; \ - } \ - }; +#define MPI_DATATYPE_TRAIT(type, mpi_type_const) \ + template <> \ + struct mpi_datatype_trait { \ + static MPI_Datatype get_mpi_type() { \ + return mpi_type_const; \ + } \ + }; // Map unique base types to MPI_Datatypes MPI_DATATYPE_TRAIT(char, MPI_CHAR) @@ -160,149 +162,149 @@ MPI_DATATYPE_TRAIT(std::complex, MPI_CXX_DOUBLE_COMPLEX) // Concept to check if a type is a native MPI datatype template concept mpi_native_datatype = requires(DataType) { - requires details::mpi_data_kind_trait::kind == details::mpi_data_kinds::base; + requires details::mpi_data_kind_trait::kind == + details::mpi_data_kinds::base; }; template concept mpi_composite_datatype = requires(DataType) { - requires details::mpi_data_kind_trait::kind == details::mpi_data_kinds::composite; + requires details::mpi_data_kind_trait::kind == + details::mpi_data_kinds::composite; }; - // mpi_datatype concept combines native and composite datatypes template concept mpi_datatype = requires(DataType) { - requires mpi_native_datatype || mpi_composite_datatype; + requires mpi_native_datatype || mpi_composite_datatype; }; - template auto get_mpi_datatype() -> MPI_Datatype { - if constexpr (mpi_native_datatype) { - return details::mpi_datatype_trait::get_mpi_type(); - } else if constexpr (mpi_composite_datatype) { - return details::mpi_datatype_trait::get_mpi_type(); - } else if constexpr (std::is_same_v) { - // int8_t may be the same as signed char - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // uint8_t may be the same as unsigned char - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // int16_t may be the same as short - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // uint16_t may be the same as unsigned short - return get_mpi_datatype(); - } else if constexpr (std::is_same_v) { - // int32_t may be the same as int or long - if constexpr (sizeof(int32_t) == sizeof(int)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(int32_t) == sizeof(long)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported 32-bit integer type"); - } - } else if constexpr (std::is_same_v) { - // uint32_t may be the same as unsigned int or unsigned long - if constexpr (sizeof(uint32_t) == sizeof(unsigned int)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(uint32_t) == sizeof(unsigned long)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, - "Unsupported 32-bit unsigned integer type"); - } - } else if constexpr (std::is_same_v) { - // int64_t may be the same as long or long long - if constexpr (sizeof(int64_t) == sizeof(long)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(int64_t) == sizeof(long long)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, "Unsupported 64-bit integer type"); - } - } else if constexpr (std::is_same_v) { - // uint64_t may be the same as unsigned long or unsigned long long - if constexpr (sizeof(uint64_t) == sizeof(unsigned long)) { - return get_mpi_datatype(); - } else if constexpr (sizeof(uint64_t) == sizeof(unsigned long long)) { - return get_mpi_datatype(); - } else { - static_assert(sizeof(DataType) == 0, - "Unsupported 64-bit unsigned integer type"); - } - } else { - static_assert(sizeof(DataType) == 0, - "Unsupported data type for MPI communication"); - return MPI_DATATYPE_NULL; // This line will never be reached due to - // static_assert - } + if constexpr (mpi_native_datatype) { + return details::mpi_datatype_trait::get_mpi_type(); + } else if constexpr (mpi_composite_datatype) { + return details::mpi_datatype_trait::get_mpi_type(); + } else if constexpr (std::is_same_v) { + // int8_t may be the same as signed char + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // uint8_t may be the same as unsigned char + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // int16_t may be the same as short + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // uint16_t may be the same as unsigned short + return get_mpi_datatype(); + } else if constexpr (std::is_same_v) { + // int32_t may be the same as int or long + if constexpr (sizeof(int32_t) == sizeof(int)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(int32_t) == sizeof(long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported 32-bit integer type"); + } + } else if constexpr (std::is_same_v) { + // uint32_t may be the same as unsigned int or unsigned long + if constexpr (sizeof(uint32_t) == sizeof(unsigned int)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(uint32_t) == sizeof(unsigned long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, + "Unsupported 32-bit unsigned integer type"); + } + } else if constexpr (std::is_same_v) { + // int64_t may be the same as long or long long + if constexpr (sizeof(int64_t) == sizeof(long)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(int64_t) == sizeof(long long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, "Unsupported 64-bit integer type"); + } + } else if constexpr (std::is_same_v) { + // uint64_t may be the same as unsigned long or unsigned long long + if constexpr (sizeof(uint64_t) == sizeof(unsigned long)) { + return get_mpi_datatype(); + } else if constexpr (sizeof(uint64_t) == sizeof(unsigned long long)) { + return get_mpi_datatype(); + } else { + static_assert(sizeof(DataType) == 0, + "Unsupported 64-bit unsigned integer type"); + } + } else { + static_assert(sizeof(DataType) == 0, + "Unsupported data type for MPI communication"); + return MPI_DATATYPE_NULL; // This line will never be reached due to + // static_assert + } } // Handle composite types using reflection template - requires mpi_composite_datatype + requires mpi_composite_datatype struct mpi::details::mpi_datatype_trait { - static MPI_Datatype get_mpi_type() { - // Function-local static variable initialized via lambda - static MPI_Datatype mpi_type = []() -> MPI_Datatype { - MPI_Datatype mpi_type = MPI_DATATYPE_NULL; - - constexpr size_t num_fields = cista::arity(); - std::vector block_lengths(num_fields, 1); - std::vector types; - std::vector offsets; - - DataType const tmp{}; - - cista::for_each_field(tmp, [&types, &offsets, &tmp](auto&& field) { - using field_type = std::decay_t; - types.push_back(mpi::get_mpi_datatype()); - auto offset = reinterpret_cast(&field) - - reinterpret_cast(&tmp); - offsets.push_back(offset); - }); - - // Create the MPI datatype - int mpi_error = MPI_Type_create_struct( - static_cast(num_fields), block_lengths.data(), offsets.data(), - types.data(), &mpi_type); - if (mpi_error != MPI_SUCCESS) { - throw std::runtime_error("MPI_Type_create_struct() failed"); - } - - mpi_error = MPI_Type_commit(&mpi_type); - if (mpi_error != MPI_SUCCESS) { - throw std::runtime_error("MPI_Type_commit() failed"); - } - - // Register the datatype and initialize cleanup - { - std::scoped_lock lock{mpi::details::mpi_type_mutex}; - mpi::details::get_custom_mpi_types().push_back(mpi_type); - mpi::details::initialize_mpi_type_cleanup(); - } - - return mpi_type; - }(); - - return mpi_type; - } + static MPI_Datatype get_mpi_type() { + // Function-local static variable initialized via lambda + static MPI_Datatype mpi_type = []() -> MPI_Datatype { + MPI_Datatype mpi_type = MPI_DATATYPE_NULL; + + constexpr size_t num_fields = cista::arity(); + std::vector block_lengths(num_fields, 1); + std::vector types; + std::vector offsets; + + DataType const tmp{}; + + cista::for_each_field(tmp, [&types, &offsets, &tmp](auto&& field) { + using field_type = std::decay_t; + types.push_back(mpi::get_mpi_datatype()); + auto offset = reinterpret_cast(&field) - + reinterpret_cast(&tmp); + offsets.push_back(offset); + }); + + // Create the MPI datatype + int mpi_error = MPI_Type_create_struct( + static_cast(num_fields), block_lengths.data(), offsets.data(), + types.data(), &mpi_type); + if (mpi_error != MPI_SUCCESS) { + throw std::runtime_error("MPI_Type_create_struct() failed"); + } + + mpi_error = MPI_Type_commit(&mpi_type); + if (mpi_error != MPI_SUCCESS) { + throw std::runtime_error("MPI_Type_commit() failed"); + } + + // Register the datatype and initialize cleanup + { + std::scoped_lock const lock{mpi::details::mpi_type_mutex}; + mpi::details::get_custom_mpi_types().push_back(mpi_type); + mpi::details::initialize_mpi_type_cleanup(); + } + + return mpi_type; + }(); + + return mpi_type; + } }; } // namespace mpi // Example struct struct MyType { - int a{}; - double b{}; - double c{}; - - friend bool operator==(const MyType& lhs, const MyType& rhs) { - return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c); - } - friend bool operator!=(const MyType& lhs, const MyType& rhs) { - return !(lhs == rhs); - } + int a{}; + double b{}; + double c{}; + + friend bool operator==(const MyType& lhs, const MyType& rhs) { + return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c); + } + friend bool operator!=(const MyType& lhs, const MyType& rhs) { + return !(lhs == rhs); + } }; \ No newline at end of file diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 0bdde805..710c0ecb 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -47,117 +47,118 @@ void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicat // MPI AlltoAll based implementation void parallel_contraction::compute_label_mapping( - MPI_Comm communicator, - parallel_graph_access& G, - NodeID& global_num_distinct_ids, - std::unordered_map& label_mapping) { - PEID rank, size; - MPI_Comm_rank(communicator, &rank); - MPI_Comm_size(communicator, &size); - - NodeID divisor = ceil(G.number_of_global_nodes() / static_cast(size)); - - helpers helper; - m_messages.clear(); - m_messages.resize(size); - - std::vector > filter; - filter.resize(size); - - forall_local_nodes(G, node) { - PEID peID = G.getNodeLabel(node) / divisor; - filter[peID][G.getNodeLabel(node)] = true; - } endfor - - for (PEID peID = 0; peID < size; peID++) { - std::unordered_map::iterator it; - for (it = filter.at(peID).begin(); it != filter.at(peID).end(); ++it) { - m_messages.at(peID).push_back(it->first); - } - } - - auto const local_labels_byPE = mpi::all_to_all(m_messages, communicator); - - std::vector local_labels; - for (PEID peID = 0; peID < size; peID++) { - for (ULONG i = 0; i < local_labels_byPE.at(peID).size(); i++) { - local_labels.push_back(local_labels_byPE.at(peID).at(i)); - } - } - - // filter duplicates locally - helper.filter_duplicates( - local_labels, - [](const NodeID& lhs, const NodeID& rhs) -> bool { return (lhs < rhs); }, - [](const NodeID& lhs, const NodeID& rhs) -> bool { - return (lhs == rhs); - }); - // afterward they are sorted! - - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%Labels are unique on all PEs%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // now counting - - NodeID local_num_labels = local_labels.size(); - NodeID prefix_sum = 0; - - MPI_Scan(&local_num_labels, &prefix_sum, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, - communicator); - - global_num_distinct_ids = prefix_sum; - // Broadcast global number of ids - MPI_Bcast(&global_num_distinct_ids, 1, MPI_UNSIGNED_LONG_LONG, size - 1, - communicator); - - NodeID num_smaller_ids = prefix_sum - local_num_labels; - - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%Now Build the mapping and send information back to PEs%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - // build the mapping locally - std::unordered_map label_mapping_to_cnode; - NodeID cur_id = num_smaller_ids; - for (ULONG i = 0; i < local_labels.size(); i++) { - label_mapping_to_cnode[local_labels[i]] = cur_id++; - } - - // now send the processes the mapping back - // std::vector< std::vector< NodeID > > m_out_messages; - m_out_messages.clear(); - m_out_messages.resize(size); - - for (PEID peID = 0; peID < size; peID++) { - if (peID == rank) - continue; - - if (local_labels_byPE.at(peID).empty()) { - m_out_messages.at(peID) = {}; - continue; - } - - for (ULONG i = 0; i < local_labels_byPE[peID].size(); i++) { - m_out_messages.at(peID).push_back( - label_mapping_to_cnode.at(local_labels_byPE.at(peID).at(i))); - } - } - - auto recv_mapping = mpi::all_to_all(m_out_messages, communicator); - - // first the local labels - for (ULONG i = 0; i < m_messages.at(rank).size(); i++) { - label_mapping[m_messages.at(rank).at(i)] = - label_mapping_to_cnode.at(m_messages.at(rank).at(i)); - } - - for (PEID peID = 0; peID < size; peID++) { - for (ULONG i = 0; i < recv_mapping.at(peID).size(); i++) { - label_mapping[m_messages.at(peID).at(i)] = recv_mapping.at(peID).at(i); - } - } + MPI_Comm communicator, + parallel_graph_access& G, + NodeID& global_num_distinct_ids, + std::unordered_map& label_mapping) { + PEID rank, size; + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); + + NodeID divisor = ceil(G.number_of_global_nodes() / static_cast(size)); + + helpers helper; + m_messages.clear(); + m_messages.resize(size); + + std::vector> filter; + filter.resize(size); + + forall_local_nodes(G, node) { + PEID peID = G.getNodeLabel(node) / divisor; + filter[peID][G.getNodeLabel(node)] = true; + } + endfor + + for (PEID peID = 0; peID < size; peID++) { + std::unordered_map::iterator it; + for (it = filter.at(peID).begin(); it != filter.at(peID).end(); ++it) { + m_messages.at(peID).push_back(it->first); + } + } + + auto const local_labels_byPE = mpi::all_to_all(m_messages, communicator); + + std::vector local_labels; + for (PEID peID = 0; peID < size; peID++) { + for (ULONG i = 0; i < local_labels_byPE.at(peID).size(); i++) { + local_labels.push_back(local_labels_byPE.at(peID).at(i)); + } + } + + // filter duplicates locally + helper.filter_duplicates( + local_labels, + [](NodeID const& lhs, NodeID const& rhs) -> bool { return (lhs < rhs); }, + [](NodeID const& lhs, NodeID const& rhs) -> bool { + return (lhs == rhs); + }); + // afterward they are sorted! + + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%Labels are unique on all PEs%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // now counting + + NodeID local_num_labels = local_labels.size(); + NodeID prefix_sum = 0; + + MPI_Scan(&local_num_labels, &prefix_sum, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, + communicator); + + global_num_distinct_ids = prefix_sum; + // Broadcast global number of ids + MPI_Bcast(&global_num_distinct_ids, 1, MPI_UNSIGNED_LONG_LONG, size - 1, + communicator); + + NodeID num_smaller_ids = prefix_sum - local_num_labels; + + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%Now Build the mapping and send information back to PEs%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + // build the mapping locally + std::unordered_map label_mapping_to_cnode; + NodeID cur_id = num_smaller_ids; + for (ULONG i = 0; i < local_labels.size(); i++) { + label_mapping_to_cnode[local_labels[i]] = cur_id++; + } + + // now send the processes the mapping back + // std::vector< std::vector< NodeID > > m_out_messages; + m_out_messages.clear(); + m_out_messages.resize(size); + + for (PEID peID = 0; peID < size; peID++) { + if (peID == rank) + continue; + + if (local_labels_byPE.at(peID).empty()) { + m_out_messages.at(peID) = {}; + continue; + } + + for (ULONG i = 0; i < local_labels_byPE[peID].size(); i++) { + m_out_messages.at(peID).push_back( + label_mapping_to_cnode.at(local_labels_byPE.at(peID).at(i))); + } + } + + auto recv_mapping = mpi::all_to_all(m_out_messages, communicator); + + // first the local labels + for (ULONG i = 0; i < m_messages.at(rank).size(); i++) { + label_mapping[m_messages.at(rank).at(i)] = + label_mapping_to_cnode.at(m_messages.at(rank).at(i)); + } + + for (PEID peID = 0; peID < size; peID++) { + for (ULONG i = 0; i < recv_mapping.at(peID).size(); i++) { + label_mapping[m_messages.at(peID).at(i)] = recv_mapping.at(peID).at(i); + } + } } void parallel_contraction::get_nodes_to_cnodes_ghost_nodes( MPI_Comm communicator, parallel_graph_access & G ) { @@ -265,144 +266,145 @@ void parallel_contraction::build_quotient_graph_locally( parallel_graph_access & } void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( - MPI_Comm communicator, - hashed_graph& hG, - std::unordered_map& node_weights, - NodeID number_of_cnodes, - parallel_graph_access& Q) { - PEID rank, size; - MPI_Comm_rank(communicator, &rank); - MPI_Comm_size(communicator, &size); - - NodeID divisor = ceil(number_of_cnodes / (double)size); - - std::vector> messages(size); - m_messages.clear(); - m_messages.resize(size); - - // build messages - hashed_graph::iterator it; - for (it = hG.begin(); it != hG.end(); it++) { - data_hashed_edge& e = it->second; - hashed_edge he = it->first; - PEID peID = he.source / divisor; - messages[peID].emplace_back(he.source, he.target, e.weight); - peID = he.target / divisor; - messages[peID].emplace_back(he.target, he.source, e.weight); - } - - // build the local part of the graph - // - auto const local_msg_byPE = mpi::all_to_all(messages, communicator); - - hashed_graph local_graph; - for (PEID peID = 0; peID < size; peID++) { - if (!local_msg_byPE[peID].empty()) { - for (auto packed_edge : local_msg_byPE[peID]) { - hashed_edge he{}; - he.k = number_of_cnodes; - he.source = packed_edge.source; - he.target = packed_edge.target; - - local_graph[he].weight += packed_edge.weight; - } - } - } - - ULONG from = rank * ceil(number_of_cnodes / (double)size); - ULONG to = (rank + 1) * ceil(number_of_cnodes / (double)size) - 1; - // handle the case where we dont have local edges - from = std::min(from, number_of_cnodes); - to = std::min(to, number_of_cnodes - 1); - ULONG local_num_cnodes = to - from + 1; - - std::vector>> sorted_graph; - sorted_graph.resize(local_num_cnodes); - - EdgeID edge_counter = 0; - for (it = local_graph.begin(); it != local_graph.end(); it++) { - data_hashed_edge& e = it->second; - hashed_edge he = it->first; - - if (from <= he.target && he.target <= to) { - std::pair edge; - edge.first = he.target; - edge.second = e.weight / 4; - - std::pair e_bar; - e_bar.first = he.source; - e_bar.second = e.weight / 4; - - sorted_graph[he.target - from].push_back(e_bar); - sorted_graph[he.source - from].push_back(edge); - edge_counter += 2; - } else { - std::pair edge; - edge.first = he.target; - edge.second = e.weight / 2; - sorted_graph[he.source - from].push_back(edge); - edge_counter++; - } - } - - ULONG global_edges = 0; - MPI_Allreduce(&edge_counter, &global_edges, 1, MPI_UNSIGNED_LONG_LONG, - MPI_SUM, communicator); - - Q.start_construction(local_num_cnodes, edge_counter, number_of_cnodes, - global_edges); - Q.set_range(from, to); - - std::vector vertex_dist(size + 1, 0); - for (PEID peID = 0; peID <= size; peID++) { - vertex_dist[peID] = std::min( - number_of_cnodes, - (NodeID)(peID * - ceil(number_of_cnodes / (double)size))); // from positions - } - // vertex_dist[size] = std::min(to, number_of_cnodes - 1); - Q.set_range_array(vertex_dist); - - for (NodeID i = 0; i < local_num_cnodes; ++i) { - NodeID node = Q.new_node(); - NodeID globalID = from + node; - Q.setNodeWeight(node, 0); - Q.setNodeLabel(node, globalID); - - for (EdgeID e = 0; e < sorted_graph[node].size(); e++) { - NodeID target = sorted_graph[node][e].first; - EdgeID e_bar = Q.new_edge(node, target); - Q.setEdgeWeight(e_bar, sorted_graph[node][e].second); - } - } - - Q.finish_construction(); - - for (PEID peID = 0; peID < size; peID++) { - m_messages[peID].clear(); - } - // now distribute the node weights - // pack messages - std::vector> weight_messages( - size); - std::unordered_map::iterator wit; - for (wit = node_weights.begin(); wit != node_weights.end(); wit++) { - NodeID node = wit->first; - NodeWeight weight = wit->second; - PEID peID = node / divisor; - weight_messages[peID].emplace_back(node, weight); - } - - auto const node_weights_byPE = mpi::all_to_all(weight_messages, communicator); - - for (auto& message_byPE : node_weights_byPE) { - if (message_byPE.empty()) { - for (auto& [globalID, weight] : message_byPE) { - NodeID node = globalID - from; - Q.setNodeWeight(node, Q.getNodeWeight(node) + weight); - } - } - } + MPI_Comm communicator, + hashed_graph& hG, + std::unordered_map& node_weights, + NodeID number_of_cnodes, + parallel_graph_access& Q) { + PEID rank = 0; + PEID size = 0; + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); + + NodeID const divisor = ceil(number_of_cnodes / static_cast(size)); + + std::vector> messages(size); + m_messages.clear(); + m_messages.resize(size); + + // build messages + hashed_graph::iterator it; + for (it = hG.begin(); it != hG.end(); it++) { + data_hashed_edge& e = it->second; + hashed_edge he = it->first; + PEID peID = he.source / divisor; + messages[peID].emplace_back(he.source, he.target, e.weight); + peID = he.target / divisor; + messages[peID].emplace_back(he.target, he.source, e.weight); + } + + // build the local part of the graph + // + auto const local_msg_byPE = mpi::all_to_all(messages, communicator); + + hashed_graph local_graph; + for (PEID peID = 0; peID < size; peID++) { + if (!local_msg_byPE[peID].empty()) { + for (auto packed_edge : local_msg_byPE[peID]) { + hashed_edge he{}; + he.k = number_of_cnodes; + he.source = packed_edge.source; + he.target = packed_edge.target; + + local_graph[he].weight += packed_edge.weight; + } + } + } + + ULONG from = rank * ceil(number_of_cnodes / (double)size); + ULONG to = (rank + 1) * ceil(number_of_cnodes / (double)size) - 1; + // handle the case where we dont have local edges + from = std::min(from, number_of_cnodes); + to = std::min(to, number_of_cnodes - 1); + ULONG local_num_cnodes = to - from + 1; + + std::vector>> sorted_graph; + sorted_graph.resize(local_num_cnodes); + + EdgeID edge_counter = 0; + for (it = local_graph.begin(); it != local_graph.end(); it++) { + data_hashed_edge& e = it->second; + hashed_edge he = it->first; + + if (from <= he.target && he.target <= to) { + std::pair edge; + edge.first = he.target; + edge.second = e.weight / 4; + + std::pair e_bar; + e_bar.first = he.source; + e_bar.second = e.weight / 4; + + sorted_graph[he.target - from].push_back(e_bar); + sorted_graph[he.source - from].push_back(edge); + edge_counter += 2; + } else { + std::pair edge; + edge.first = he.target; + edge.second = e.weight / 2; + sorted_graph[he.source - from].push_back(edge); + edge_counter++; + } + } + + ULONG global_edges = 0; + MPI_Allreduce(&edge_counter, &global_edges, 1, MPI_UNSIGNED_LONG_LONG, + MPI_SUM, communicator); + + Q.start_construction(local_num_cnodes, edge_counter, number_of_cnodes, + global_edges); + Q.set_range(from, to); + + std::vector vertex_dist(size + 1, 0); + for (PEID peID = 0; peID <= size; peID++) { + vertex_dist[peID] = std::min( + number_of_cnodes, + (NodeID)(peID * + ceil(number_of_cnodes / (double)size))); // from positions + } + // vertex_dist[size] = std::min(to, number_of_cnodes - 1); + Q.set_range_array(vertex_dist); + + for (NodeID i = 0; i < local_num_cnodes; ++i) { + NodeID node = Q.new_node(); + NodeID globalID = from + node; + Q.setNodeWeight(node, 0); + Q.setNodeLabel(node, globalID); + + for (EdgeID e = 0; e < sorted_graph[node].size(); e++) { + NodeID target = sorted_graph[node][e].first; + EdgeID e_bar = Q.new_edge(node, target); + Q.setEdgeWeight(e_bar, sorted_graph[node][e].second); + } + } + + Q.finish_construction(); + + for (PEID peID = 0; peID < size; peID++) { + m_messages[peID].clear(); + } + // now distribute the node weights + // pack messages + std::vector> weight_messages( + size); + std::unordered_map::iterator wit; + for (wit = node_weights.begin(); wit != node_weights.end(); wit++) { + NodeID node = wit->first; + NodeWeight weight = wit->second; + PEID peID = node / divisor; + weight_messages[peID].emplace_back(node, weight); + } + + auto const node_weights_byPE = mpi::all_to_all(weight_messages, communicator); + + for (auto& message_byPE : node_weights_byPE) { + if (message_byPE.empty()) { + for (auto& [globalID, weight] : message_byPE) { + NodeID node = globalID - from; + Q.setNodeWeight(node, Q.getNodeWeight(node) + weight); + } + } + } } diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h index 9499cfd0..ac151890 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h @@ -8,61 +8,61 @@ #ifndef PARALLEL_CONTRACTION_64O127GD #define PARALLEL_CONTRACTION_64O127GD +#include "communication/mpi_tools.h" #include "data_structure/hashed_graph.h" #include "data_structure/parallel_graph_access.h" #include "partition_config.h" -#include "communication/mpi_tools.h" class parallel_contraction { public: - void contract_to_distributed_quotient(MPI_Comm communicator, - PPartitionConfig& config, - parallel_graph_access& G, - parallel_graph_access& Q); + void contract_to_distributed_quotient(MPI_Comm communicator, + PPartitionConfig& config, + parallel_graph_access& G, + parallel_graph_access& Q); private: - // compute mapping of labels id into contiguous intervall [0,...,num_lables) - void compute_label_mapping(MPI_Comm communicator, - parallel_graph_access& G, - NodeID& global_num_distinct_ids, - std::unordered_map& label_mapping); + // compute mapping of labels id into contiguous intervall [0,...,num_lables) + void compute_label_mapping(MPI_Comm communicator, + parallel_graph_access& G, + NodeID& global_num_distinct_ids, + std::unordered_map& label_mapping); - void get_nodes_to_cnodes_ghost_nodes(MPI_Comm communicator, - parallel_graph_access& G); + void get_nodes_to_cnodes_ghost_nodes(MPI_Comm communicator, + parallel_graph_access& G); - void build_quotient_graph_locally( - parallel_graph_access& G, - NodeID number_of_distinct_labels, - hashed_graph& hG, - std::unordered_map& node_weights); + void build_quotient_graph_locally( + parallel_graph_access& G, + NodeID number_of_distinct_labels, + hashed_graph& hG, + std::unordered_map& node_weights); - void redistribute_hased_graph_and_build_graph_locally( - MPI_Comm communicator, - hashed_graph& hG, - std::unordered_map& node_weights, - NodeID number_of_cnodes, - parallel_graph_access& Q); + void redistribute_hased_graph_and_build_graph_locally( + MPI_Comm communicator, + hashed_graph& hG, + std::unordered_map& node_weights, + NodeID number_of_cnodes, + parallel_graph_access& Q); - void update_ghost_nodes_weights(MPI_Comm communicator, - parallel_graph_access& G); + void update_ghost_nodes_weights(MPI_Comm communicator, + parallel_graph_access& G); - // some send buffers - std::vector> m_messages; - std::vector> m_out_messages; - std::vector> m_send_buffers; // buffers to send messages + // some send buffers + std::vector> m_messages; + std::vector> m_out_messages; + std::vector> m_send_buffers; // buffers to send messages }; // Comm types namespace contraction { struct bundled_edge { - NodeID source; - NodeID target; - NodeWeight weight; + NodeID source; + NodeID target; + NodeWeight weight; }; struct bundled_node_weight { - NodeID node; - NodeWeight weight; + NodeID node; + NodeWeight weight; }; } // namespace contraction #endif /* end of include guard: PARALLEL_CONTRACTION_64O127GD */ From edd22a56a153c1d50540136d5e964b29c008dd43 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Mon, 7 Oct 2024 15:49:47 +0100 Subject: [PATCH 47/64] Cleaning up --- CMakeLists.txt | 28 +++++------ cmake/CompilerWarnings.cmake | 3 +- cmake/KaHIPSettings.cmake | 2 +- lib/tools/random_functions.h | 4 +- parallel/modified_kahip/CMakeLists.txt | 23 +++++---- .../modified_kahip/lib/tools/mpi_tools.cpp | 47 ------------------- parallel/modified_kahip/lib/tools/mpi_tools.h | 21 --------- .../lib/tools/random_functions.h | 4 +- parallel/parallel_src/CMakeLists.txt | 25 +++++----- .../lib/communication/mpi_types.h | 12 ++--- .../parallel_src/lib/tools/random_functions.h | 4 +- parallel/parallel_src/tests/CMakeLists.txt | 6 ++- 12 files changed, 61 insertions(+), 118 deletions(-) delete mode 100644 parallel/modified_kahip/lib/tools/mpi_tools.cpp delete mode 100644 parallel/modified_kahip/lib/tools/mpi_tools.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f66a981..8f05ef58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,24 +22,24 @@ endif() option(NONATIVEOPTIMIZATIONS "Disable --march=native optimizations" OFF) # tweak compiler flags -#CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) -#if(COMPILER_SUPPORTS_FUNROLL_LOOPS) -# add_definitions(-funroll-loops) -#endif() -#CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) -#if(COMPILER_SUPPORTS_FNOSTACKLIMITS) -# add_definitions(-fno-stack-limit) -#endif() +CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) +if(COMPILER_SUPPORTS_FUNROLL_LOOPS) + add_definitions(-funroll-loops) +endif() +CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) +if(COMPILER_SUPPORTS_FNOSTACKLIMITS) + add_definitions(-fno-stack-limit) +endif() #CHECK_CXX_COMPILER_FLAG(-Wall COMPILER_SUPPORTS_WALL) #if(COMPILER_SUPPORTS_WALL) # add_definitions(-Wall) #endif() -#CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) -#if(COMPILER_SUPPORTS_MARCH_NATIVE) -# if( NOT NONATIVEOPTIMIZATIONS ) -# add_definitions(-march=native) -# endif() -#endif() +CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) +if(COMPILER_SUPPORTS_MARCH_NATIVE) + if( NOT NONATIVEOPTIMIZATIONS ) + add_definitions(-march=native) + endif() +endif() #CHECK_CXX_COMPILER_FLAG(-fpermissive COMPILER_SUPPORTS_FPERMISSIVE) #if(COMPILER_SUPPORTS_FPERMISSIVE) # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index 5911ff16..0fc0c2e0 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -51,11 +51,12 @@ function( -Woverloaded-virtual # warn if you overload (not override) a virtual function -Wpedantic # warn if non-standard C++ is used -Wconversion # warn on type conversions that may lose data - -Wsign-conversion # warn on sign conversions + -Wno-sign-conversion # warn on sign conversions -Wnull-dereference # warn if a null dereference is detected -Wdouble-promotion # warn if float is implicit promoted to double -Wformat=2 # warn on security issues around functions that format output (ie printf) -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation + -Wno-float-conversion ) endif() diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index dd9ae590..948f8e4a 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -7,7 +7,7 @@ include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) kahip_supports_sanitizers() -option(kahip_ENABLE_IPO "Enable IPO/LTO" OFF) +option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) option(kahip_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) diff --git a/lib/tools/random_functions.h b/lib/tools/random_functions.h index e88ec948..466816d5 100644 --- a/lib/tools/random_functions.h +++ b/lib/tools/random_functions.h @@ -150,8 +150,8 @@ class random_functions { } static bool nextBool() { - std::uniform_int_distribution A(0,1); - return (bool) A(m_mt); + std::bernoulli_distribution bernoulli(0.5); + return bernoulli(m_mt); } diff --git a/parallel/modified_kahip/CMakeLists.txt b/parallel/modified_kahip/CMakeLists.txt index 5d75d8bb..b60c5ebe 100644 --- a/parallel/modified_kahip/CMakeLists.txt +++ b/parallel/modified_kahip/CMakeLists.txt @@ -1,9 +1,9 @@ -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) -include_directories(${MPI_CXX_INCLUDE_PATH}) +add_library(modified_kahip_headers INTERFACE) +target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib) +target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) +target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) +target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io) +target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) set(LIBMODIFIED_KAFFPA_SOURCE_FILES lib/data_structure/graph_hierarchy.cpp @@ -66,18 +66,23 @@ set(LIBMODIFIED_KAFFPA_SOURCE_FILES lib/parallel_mh/galinier_combine/construct_partition.cpp lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp) add_library(libmodified_kaffpa OBJECT ${LIBMODIFIED_KAFFPA_SOURCE_FILES}) -target_link_libraries(libmodified_kaffpa PUBLIC kahip_options kahip_warnings) +target_link_libraries(libmodified_kaffpa PRIVATE kahip_options kahip_warnings) +target_link_libraries(libmodified_kaffpa PRIVATE modified_kahip_headers) +target_link_libraries(libmodified_kaffpa PRIVATE MPI::MPI_CXX) set(LIBMODIFIED_KAFFPA_PARALLEL_SOURCE_FILES lib/parallel_mh/parallel_mh_async.cpp lib/parallel_mh/population.cpp lib/parallel_mh/exchange/exchanger.cpp - lib/tools/graph_communication.cpp - lib/tools/mpi_tools.cpp) + lib/tools/graph_communication.cpp) add_library(libmodified_kaffpa_async OBJECT ${LIBMODIFIED_KAFFPA_PARALLEL_SOURCE_FILES}) +target_link_libraries(libmodified_kaffpa_async PRIVATE modified_kahip_headers) +target_link_libraries(libmodified_kaffpa_async PRIVATE MPI::MPI_CXX) add_library(libmodified_kahip_interface STATIC interface/kaHIP_interface.cpp) target_link_libraries(libmodified_kahip_interface PRIVATE libmodified_kaffpa libmodified_kaffpa_async) target_link_libraries(libmodified_kahip_interface PRIVATE OpenMP::OpenMP_CXX) +target_link_libraries(libmodified_kahip_interface PRIVATE MPI::MPI_CXX) +target_link_libraries(libmodified_kahip_interface PRIVATE modified_kahip_headers) target_include_directories(libmodified_kahip_interface PUBLIC interface) diff --git a/parallel/modified_kahip/lib/tools/mpi_tools.cpp b/parallel/modified_kahip/lib/tools/mpi_tools.cpp deleted file mode 100644 index 556d104f..00000000 --- a/parallel/modified_kahip/lib/tools/mpi_tools.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/****************************************************************************** - * mpi_tools.cpp - * * - * Source of KaHIP -- Karlsruhe High Quality Partitioning. - * Christian Schulz - *****************************************************************************/ - -#include -#include - -#include "mpi_tools.h" - -mpi_tools::mpi_tools() { - -} - -mpi_tools::~mpi_tools() { - - -} - -//void mpi_tools::non_active_wait_for_root() { - //int rank, size; - //MPI_Comm_rank( MPI_COMM_WORLD, &rank); - //MPI_Comm_size( MPI_COMM_WORLD, &size); - - //int MASTER = 0; - - //if(rank == MASTER) { - ////wake up call - //bool wakeup = true; - //for( int to = 1; to < size; to++) { - //MPI_Send(&wakeup, 1, MPI_BOOL, to, 0, MPI_COMM_WORLD); - //} - //} else { - ////non-busy waiting: - //bool stop = false; - //do { - //usleep(5000); - //stop = MPI::COMM_WORLD.Iprobe(MASTER,0); - //} while(!stop); - - //bool wakeup = true; - //MPI::COMM_WORLD.Recv(&wakeup, 1, MPI::BOOL, MASTER, 0); - //} -//} - diff --git a/parallel/modified_kahip/lib/tools/mpi_tools.h b/parallel/modified_kahip/lib/tools/mpi_tools.h deleted file mode 100644 index 9ddb9867..00000000 --- a/parallel/modified_kahip/lib/tools/mpi_tools.h +++ /dev/null @@ -1,21 +0,0 @@ -/****************************************************************************** - * mpi_tools.h - * * - * Source of KaHIP -- Karlsruhe High Quality Partitioning. - * Christian Schulz - *****************************************************************************/ - -#ifndef MPI_TOOLS_HMESDXF2 -#define MPI_TOOLS_HMESDXF2 - - -class mpi_tools { -public: - mpi_tools(); - virtual ~mpi_tools(); - - //static void non_active_wait_for_root(); -}; - - -#endif /* end of include guard: MPI_TOOLS_HMESDXF2 */ diff --git a/parallel/modified_kahip/lib/tools/random_functions.h b/parallel/modified_kahip/lib/tools/random_functions.h index 542a869c..b44d1a79 100644 --- a/parallel/modified_kahip/lib/tools/random_functions.h +++ b/parallel/modified_kahip/lib/tools/random_functions.h @@ -131,8 +131,8 @@ class random_functions { } static bool nextBool() { - std::uniform_int_distribution A(0,1); - return (bool) A(m_mt); + std::bernoulli_distribution bernoulli(0.5); + return bernoulli(m_mt); } diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index aeea9d5b..e3019e9a 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -8,12 +8,13 @@ endif() add_subdirectory(extern/cista) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/app) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io) -include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) +add_library(parhip_headers INTERFACE) +target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/app) +target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib) +target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) +target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) +target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io) +target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) set(LIBPARALLEL_SOURCE_FILES @@ -36,7 +37,8 @@ set(LIBPARALLEL_SOURCE_FILES lib/tools/random_functions.cpp lib/tools/distributed_quality_metrics.cpp) add_library(parallel ${LIBPARALLEL_SOURCE_FILES}) -target_link_libraries(parallel PUBLIC kahip_options kahip_warnings) +target_link_libraries(parallel PRIVATE kahip_options kahip_warnings) +target_link_libraries(parallel PUBLIC parhip_headers) target_link_libraries(parallel PUBLIC libmodified_kahip_interface) target_link_libraries(parallel PUBLIC argtable3) target_link_libraries(parallel PUBLIC kahip_version) @@ -50,6 +52,7 @@ set(LIBGRAPH2BGF_SOURCE_FILES lib/data_structure/balance_management_coarsening.cpp) add_library(libgraph2bgf OBJECT ${LIBGRAPH2BGF_SOURCE_FILES}) target_link_libraries(libgraph2bgf PUBLIC MPI::MPI_CXX) +target_link_libraries(libgraph2bgf PUBLIC parhip_headers) set(LIBEDGELIST_SOURCE_FILES @@ -60,6 +63,7 @@ set(LIBEDGELIST_SOURCE_FILES lib/data_structure/balance_management_coarsening.cpp) add_library(libedgelist STATIC ${LIBEDGELIST_SOURCE_FILES}) target_link_libraries(libedgelist PUBLIC MPI::MPI_CXX argtable3) +target_link_libraries(libedgelist PUBLIC parhip_headers) set(LIBDSPAC_SOURCE_FILES @@ -67,11 +71,12 @@ set(LIBDSPAC_SOURCE_FILES lib/dspac/edge_balanced_graph_io.cpp) add_library(libdspac STATIC ${LIBDSPAC_SOURCE_FILES}) target_link_libraries(libdspac PUBLIC MPI::MPI_CXX) +target_link_libraries(libdspac PUBLIC parhip_headers) add_executable(parhip app/parhip.cpp) target_compile_definitions(parhip PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") -target_link_libraries(parhip PRIVATE libmodified_kahip_interface kahip_options kahip_warnings) +target_link_libraries(parhip PRIVATE kahip_options kahip_warnings) target_link_libraries(parhip PRIVATE parallel) target_link_libraries(parhip PRIVATE MPI::MPI_CXX) target_link_libraries(parhip PRIVATE argtable3) @@ -79,7 +84,6 @@ install(TARGETS parhip DESTINATION bin) add_executable(toolbox app/toolbox.cpp) target_compile_definitions(toolbox PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DTOOLBOX") -target_link_libraries(toolbox PRIVATE libmodified_kahip_interface) target_link_libraries(toolbox PRIVATE parallel) target_link_libraries(toolbox PRIVATE MPI::MPI_CXX) install(TARGETS toolbox DESTINATION bin) @@ -117,7 +121,6 @@ install(TARGETS edge_list_to_metis_graph DESTINATION bin) add_executable(dspac app/dspac.cpp) target_compile_definitions(dspac PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") -target_link_libraries(dspac PRIVATE libmodified_kahip_interface) target_link_libraries(dspac PRIVATE parallel) target_link_libraries(dspac PRIVATE libdspac) install(TARGETS dspac DESTINATION bin) @@ -125,7 +128,6 @@ install(TARGETS dspac DESTINATION bin) add_library(parhip_interface SHARED interface/parhip_interface.cpp) target_compile_definitions(parhip_interface PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_include_directories(parhip_interface PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) -target_link_libraries(parhip_interface PRIVATE libmodified_kahip_interface) target_link_libraries(parhip_interface PRIVATE parallel) set_target_properties(parhip_interface PROPERTIES PUBLIC_HEADER interface/parhip_interface.h) install(TARGETS parhip_interface @@ -136,7 +138,6 @@ install(TARGETS parhip_interface add_library(parhip_interface_static interface/parhip_interface.cpp) target_compile_definitions(parhip_interface_static PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") target_include_directories(parhip_interface_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) -target_link_libraries(parhip_interface_static PRIVATE libmodified_kahip_interface) target_link_libraries(parhip_interface_static PRIVATE parallel) install(TARGETS parhip_interface_static DESTINATION lib) diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 471e6969..8e864654 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -248,8 +248,8 @@ template struct mpi::details::mpi_datatype_trait { static MPI_Datatype get_mpi_type() { // Function-local static variable initialized via lambda - static MPI_Datatype mpi_type = []() -> MPI_Datatype { - MPI_Datatype mpi_type = MPI_DATATYPE_NULL; + static MPI_Datatype const mpi_type = []() -> MPI_Datatype { + MPI_Datatype mpi_type_val = MPI_DATATYPE_NULL; constexpr size_t num_fields = cista::arity(); std::vector block_lengths(num_fields, 1); @@ -269,12 +269,12 @@ struct mpi::details::mpi_datatype_trait { // Create the MPI datatype int mpi_error = MPI_Type_create_struct( static_cast(num_fields), block_lengths.data(), offsets.data(), - types.data(), &mpi_type); + types.data(), &mpi_type_val); if (mpi_error != MPI_SUCCESS) { throw std::runtime_error("MPI_Type_create_struct() failed"); } - mpi_error = MPI_Type_commit(&mpi_type); + mpi_error = MPI_Type_commit(&mpi_type_val); if (mpi_error != MPI_SUCCESS) { throw std::runtime_error("MPI_Type_commit() failed"); } @@ -282,11 +282,11 @@ struct mpi::details::mpi_datatype_trait { // Register the datatype and initialize cleanup { std::scoped_lock const lock{mpi::details::mpi_type_mutex}; - mpi::details::get_custom_mpi_types().push_back(mpi_type); + mpi::details::get_custom_mpi_types().push_back(mpi_type_val); mpi::details::initialize_mpi_type_cleanup(); } - return mpi_type; + return mpi_type_val; }(); return mpi_type; diff --git a/parallel/parallel_src/lib/tools/random_functions.h b/parallel/parallel_src/lib/tools/random_functions.h index 31d1c820..30168add 100644 --- a/parallel/parallel_src/lib/tools/random_functions.h +++ b/parallel/parallel_src/lib/tools/random_functions.h @@ -131,8 +131,8 @@ class random_functions { } static bool nextBool() { - std::uniform_int_distribution A(0,1); - return (bool) A(m_mt); + std::bernoulli_distribution bernoulli(0.5); + return bernoulli(m_mt); } //including lb and rb diff --git a/parallel/parallel_src/tests/CMakeLists.txt b/parallel/parallel_src/tests/CMakeLists.txt index 104b54a2..8502419d 100644 --- a/parallel/parallel_src/tests/CMakeLists.txt +++ b/parallel/parallel_src/tests/CMakeLists.txt @@ -1,5 +1,9 @@ # Automatically enable catch2 to generate ctest targets find_package(fmt REQUIRED CONFIG) +include(ProcessorCount) +ProcessorCount(N) + +set(mpi_runner $,"","mpirun;-np;${N}">) list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) include(CTest) @@ -31,7 +35,7 @@ target_link_libraries(parallel_contraction_mpi_test PRIVATE libmodified_kahip_in catch_discover_tests( parallel_contraction_mpi_test TEST_PREFIX "unit-" - PROPERTIES crosscompiling_emulator "mpirun -np 5" + PROPERTIES CROSSCOMPILING_EMULATOR "${mpi_runner}" REPORTER xml OUTPUT_DIR . OUTPUT_PREFIX "unit-" From 0f0afe0ebea96abd4872855f395835099be337fe Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Wed, 9 Oct 2024 10:56:34 +0100 Subject: [PATCH 48/64] Compiles on Archer --- parallel/parallel_src/lib/communication/mpi_tools.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index e0e0a43a..1efa5ef7 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -9,12 +9,12 @@ #define MPI_TOOLS_HMESDXF2 #include -#include #include #include #include #include #include +#include #include "data_structure/parallel_graph_access.h" #include "mpi_types.h" @@ -182,8 +182,9 @@ auto all_to_all(Input const& sends, MPI_Comm communicator) MPI_Error_string(mpi_error, error_string.data(), &length_of_error_string); auto mpi_error_message = std::string_view(error_string.data(), length_of_error_string); - throw std::runtime_error(std::format( - "mpi::all_to_all() failed with error: {}", mpi_error_message)); + std::stringstream errmeg{}; + errmeg << "mpi::all_to_all() failed with error: " << mpi_error_message; + throw std::runtime_error(errmeg.str()); } // Unpacking messages From 668838f8270acb74f981b02ab5f2c6aa8c9e3987 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 11 Oct 2024 13:30:15 +0100 Subject: [PATCH 49/64] Namespace added To use LTO, we need to prevent symbols from leaking out --- parallel/modified_kahip/app/configuration.h | 48 +- .../interface/kaHIP_interface.cpp | 516 ++++----- .../lib/algorithms/cycle_search.cpp | 777 ++++++------- .../lib/algorithms/cycle_search.h | 44 +- .../strongly_connected_components.cpp | 98 +- .../strongly_connected_components.h | 26 +- .../lib/algorithms/topological_sort.cpp | 55 +- .../lib/algorithms/topological_sort.h | 18 +- .../lib/data_structure/graph_access.h | 332 +++--- .../lib/data_structure/graph_hierarchy.cpp | 107 +- .../lib/data_structure/graph_hierarchy.h | 46 +- .../lib/data_structure/matrix/matrix.h | 14 +- .../lib/data_structure/matrix/normal_matrix.h | 4 +- .../priority_queues/bucket_pq.h | 106 +- .../priority_queues/maxNodeHeap.h | 172 +-- .../priority_queue_interface.h | 46 +- .../lib/data_structure/union_find.h | 80 +- parallel/modified_kahip/lib/definitions.h | 79 +- parallel/modified_kahip/lib/io/graph_io.cpp | 316 ++--- parallel/modified_kahip/lib/io/graph_io.h | 38 +- .../lib/parallel_mh/diversifyer.h | 4 +- .../lib/parallel_mh/exchange/exchanger.cpp | 468 ++++---- .../lib/parallel_mh/exchange/exchanger.h | 44 +- .../galinier_combine/construct_partition.cpp | 245 ++-- .../galinier_combine/construct_partition.h | 14 +- .../galinier_combine/gal_combine.cpp | 157 +-- .../galinier_combine/gal_combine.h | 10 +- .../lib/parallel_mh/parallel_mh_async.cpp | 522 ++++----- .../lib/parallel_mh/parallel_mh_async.h | 54 +- .../lib/parallel_mh/population.cpp | 658 +++++------ .../lib/parallel_mh/population.h | 100 +- .../coarsening/clustering/node_ordering.cpp | 4 +- .../coarsening/clustering/node_ordering.h | 18 +- .../size_constraint_label_propagation.cpp | 306 ++--- .../size_constraint_label_propagation.h | 144 +-- .../lib/partition/coarsening/coarsening.cpp | 128 +-- .../lib/partition/coarsening/coarsening.h | 10 +- .../coarsening/coarsening_configurator.h | 37 +- .../lib/partition/coarsening/contraction.cpp | 320 +++--- .../lib/partition/coarsening/contraction.h | 78 +- .../coarsening/edge_rating/edge_ratings.cpp | 263 ++--- .../coarsening/edge_rating/edge_ratings.h | 24 +- .../coarsening/matching/compare_degrees.h | 6 +- .../coarsening/matching/gpa/compare_rating.h | 20 +- .../coarsening/matching/gpa/gpa_matching.cpp | 585 +++++----- .../coarsening/matching/gpa/gpa_matching.h | 92 +- .../coarsening/matching/gpa/path.cpp | 4 +- .../partition/coarsening/matching/gpa/path.h | 60 +- .../coarsening/matching/gpa/path_set.cpp | 18 +- .../coarsening/matching/gpa/path_set.h | 116 +- .../coarsening/matching/matching.cpp | 10 +- .../partition/coarsening/matching/matching.h | 24 +- .../coarsening/matching/random_matching.cpp | 169 +-- .../coarsening/matching/random_matching.h | 22 +- .../coarsening/stop_rules/stop_rules.h | 96 +- .../lib/partition/graph_partitioner.cpp | 390 +++---- .../lib/partition/graph_partitioner.h | 26 +- .../initial_partitioning/bipartition.cpp | 471 ++++---- .../initial_partitioning/bipartition.h | 52 +- .../initial_partition_bipartition.cpp | 86 +- .../initial_partition_bipartition.h | 24 +- .../initial_partitioner.cpp | 3 +- .../initial_partitioner.h | 32 +- .../initial_partitioning.cpp | 182 +-- .../initial_partitioning.h | 12 +- .../initial_refinement/initial_refinement.cpp | 51 +- .../initial_refinement/initial_refinement.h | 14 +- .../lib/partition/partition_config.h | 378 +++--- .../cycle_improvements/advanced_models.cpp | 288 ++--- .../cycle_improvements/advanced_models.h | 140 +-- .../cycle_improvements/augmented_Qgraph.cpp | 4 +- .../cycle_improvements/augmented_Qgraph.h | 14 +- .../augmented_Qgraph_fabric.cpp | 978 ++++++++-------- .../augmented_Qgraph_fabric.h | 238 ++-- .../cycle_improvements/cycle_definitions.h | 4 +- .../cycle_improvements/cycle_refinement.cpp | 313 ++--- .../cycle_improvements/cycle_refinement.h | 38 +- .../cycle_improvements/greedy_neg_cycle.cpp | 4 +- .../cycle_improvements/greedy_neg_cycle.h | 4 +- .../cycle_improvements/problem_factory.cpp | 4 +- .../cycle_improvements/problem_factory.h | 48 +- .../kway_graph_refinement.cpp | 108 +- .../kway_graph_refinement.h | 36 +- .../kway_graph_refinement_commons.cpp | 65 +- .../kway_graph_refinement_commons.h | 58 +- .../kway_graph_refinement_core.cpp | 306 ++--- .../kway_graph_refinement_core.h | 118 +- .../kway_graph_refinement/kway_stop_rule.h | 4 +- .../multitry_kway_fm.cpp | 270 ++--- .../kway_graph_refinement/multitry_kway_fm.h | 52 +- .../label_propagation_refinement.cpp | 263 ++--- .../label_propagation_refinement.h | 14 +- .../refinement/mixed_refinement.cpp | 74 +- .../refinement/mixed_refinement.h | 14 +- .../partition_accept_rule.h | 186 +-- .../queue_selection_strategie.h | 299 ++--- .../2way_fm_refinement/search_stop_rule.h | 28 +- .../2way_fm_refinement/two_way_fm.cpp | 674 +++++------ .../2way_fm_refinement/two_way_fm.h | 159 ++- .../vertex_moved_hashtable.h | 24 +- .../boundary_lookup.h | 19 +- .../complete_boundary.cpp | 165 +-- .../complete_boundary.h | 353 +++--- .../flow_refinement/boundary_bfs.cpp | 96 +- .../flow_refinement/boundary_bfs.h | 24 +- .../edge_cut_flow_solver.cpp | 481 ++++---- .../edge_cut_flow_solver.h | 89 +- .../flow_solving_kernel/flow_macros.h | 2 - .../flow_solving_kernel/flow_solver.cpp | 1013 ++++++++--------- .../flow_solving_kernel/flow_solver.h | 90 +- .../flow_solving_kernel/timer.cpp | 4 +- .../flow_solving_kernel/timer.h | 4 +- .../flow_solving_kernel/types.h | 14 +- .../most_balanced_minimum_cuts.cpp | 260 ++--- .../most_balanced_minimum_cuts.h | 44 +- .../two_way_flow_refinement.cpp | 442 +++---- .../flow_refinement/two_way_flow_refinement.h | 80 +- .../partial_boundary.cpp | 4 +- .../partial_boundary.h | 30 +- .../quotient_graph_refinement.cpp | 424 +++---- .../quotient_graph_refinement.h | 50 +- .../active_block_quotient_graph_scheduler.cpp | 20 +- .../active_block_quotient_graph_scheduler.h | 44 +- .../quotient_graph_scheduling.cpp | 4 +- .../quotient_graph_scheduling.h | 30 +- .../simple_quotient_graph_scheduler.cpp | 22 +- .../simple_quotient_graph_scheduler.h | 6 +- .../two_way_refinement.h | 30 +- .../uncoarsening/refinement/refinement.cpp | 4 +- .../uncoarsening/refinement/refinement.h | 15 +- .../tabu_search/tabu_bucket_queue.h | 129 +-- .../refinement/tabu_search/tabu_moves_queue.h | 26 +- .../refinement/tabu_search/tabu_search.cpp | 415 +++---- .../refinement/tabu_search/tabu_search.h | 84 +- .../separator/vertex_separator_algorithm.cpp | 229 ++-- .../separator/vertex_separator_algorithm.h | 36 +- .../vertex_separator_flow_solver.cpp | 352 +++--- .../separator/vertex_separator_flow_solver.h | 60 +- .../partition/uncoarsening/uncoarsening.cpp | 159 ++- .../lib/partition/uncoarsening/uncoarsening.h | 12 +- .../partition/w_cycles/wcycle_partitioner.cpp | 295 ++--- .../partition/w_cycles/wcycle_partitioner.h | 30 +- .../lib/tools/graph_communication.cpp | 79 +- .../lib/tools/graph_communication.h | 9 +- .../lib/tools/graph_extractor.cpp | 294 ++--- .../lib/tools/graph_extractor.h | 52 +- .../lib/tools/graph_partition_assertions.h | 38 +- parallel/modified_kahip/lib/tools/misc.cpp | 61 +- parallel/modified_kahip/lib/tools/misc.h | 10 +- .../lib/tools/partition_snapshooter.cpp | 76 +- .../lib/tools/partition_snapshooter.h | 34 +- .../lib/tools/quality_metrics.cpp | 308 ++--- .../lib/tools/quality_metrics.h | 26 +- .../lib/tools/random_functions.cpp | 3 +- .../lib/tools/random_functions.h | 255 ++--- parallel/modified_kahip/lib/tools/timer.h | 52 +- .../data_structure/parallel_graph_access.h | 27 +- 157 files changed, 10670 insertions(+), 10646 deletions(-) diff --git a/parallel/modified_kahip/app/configuration.h b/parallel/modified_kahip/app/configuration.h index 152e3da9..b90929b6 100644 --- a/parallel/modified_kahip/app/configuration.h +++ b/parallel/modified_kahip/app/configuration.h @@ -15,18 +15,20 @@ class configuration { configuration() {} ; virtual ~configuration() {}; - void strong( PartitionConfig & config ); - void eco( PartitionConfig & config ); - void fast( PartitionConfig & config ); - void standard( PartitionConfig & config ); - void standardsnw( PartitionConfig & config ); - - void fastsocial( PartitionConfig & config ); - void ecosocial( PartitionConfig & config ); - void strongsocial( PartitionConfig & config ); + void strong(kahip::modified::PartitionConfig & config ); + void eco(kahip::modified::PartitionConfig & config ); + void fast(kahip::modified::PartitionConfig & config ); + void standard(kahip::modified::PartitionConfig & config ); + void standardsnw(kahip::modified::PartitionConfig & config ); + + void fastsocial(kahip::modified::PartitionConfig & config ); + void ecosocial(kahip::modified::PartitionConfig & config ); + void strongsocial(kahip::modified::PartitionConfig & config ); }; -inline void configuration::strong( PartitionConfig & partition_config ) { +inline void configuration::strong( + kahip::modified::PartitionConfig & partition_config ) { + using namespace kahip::modified; standard(partition_config); partition_config.matching_type = MATCHING_GPA; partition_config.permutation_quality = PERMUTATION_QUALITY_GOOD; @@ -63,7 +65,10 @@ inline void configuration::strong( PartitionConfig & partition_config ) { } -inline void configuration::eco( PartitionConfig & partition_config ) { +inline void configuration::eco( + kahip::modified::PartitionConfig & partition_config ) { + using namespace kahip::modified; + standard(partition_config); partition_config.eco = true; partition_config.aggressive_random_levels = std::max(2, (int)(7 - log2(partition_config.k))); @@ -88,8 +93,10 @@ inline void configuration::eco( PartitionConfig & partition_config ) { partition_config.initial_partitioning_repetitions = 16; } -inline void configuration::fast( PartitionConfig & partition_config ) { +inline void configuration::fast( + kahip::modified::PartitionConfig & partition_config ) { standard(partition_config); + using namespace kahip::modified; partition_config.fast = true; if(partition_config.k > 8) { @@ -116,7 +123,9 @@ inline void configuration::fast( PartitionConfig & partition_config ) { } -inline void configuration::standard( PartitionConfig & partition_config ) { +inline void configuration::standard( + kahip::modified::PartitionConfig & partition_config ) { + using namespace kahip::modified; partition_config.seed = 0; partition_config.fast = false; partition_config.eco = false; @@ -275,7 +284,9 @@ inline void configuration::standard( PartitionConfig & partition_config ) { } -inline void configuration::standardsnw( PartitionConfig & partition_config ) { +inline void configuration::standardsnw( + kahip::modified::PartitionConfig & partition_config ) { + using namespace kahip::modified; partition_config.matching_type = CLUSTER_COARSENING; partition_config.stop_rule = STOP_RULE_MULTIPLE_K; partition_config.num_vert_stop_factor = 5000; @@ -301,7 +312,8 @@ inline void configuration::standardsnw( PartitionConfig & partition_config ) { } -inline void configuration::fastsocial( PartitionConfig & partition_config ) { +inline void configuration::fastsocial( + kahip::modified::PartitionConfig & partition_config ) { eco(partition_config); standardsnw(partition_config); partition_config.label_propagation_refinement = true; @@ -309,7 +321,8 @@ inline void configuration::fastsocial( PartitionConfig & partition_config ) { partition_config.balance_factor = 0; } -inline void configuration::ecosocial( PartitionConfig & partition_config ) { +inline void configuration::ecosocial( + kahip::modified::PartitionConfig & partition_config ) { eco(partition_config); standardsnw(partition_config); partition_config.label_propagation_refinement = false; @@ -320,7 +333,8 @@ inline void configuration::ecosocial( PartitionConfig & partition_config ) { partition_config.cluster_coarsening_during_ip = true; } -inline void configuration::strongsocial( PartitionConfig & partition_config ) { +inline void configuration::strongsocial( + kahip::modified::PartitionConfig & partition_config ) { strong(partition_config); standardsnw(partition_config); diff --git a/parallel/modified_kahip/interface/kaHIP_interface.cpp b/parallel/modified_kahip/interface/kaHIP_interface.cpp index ada8f038..3f8065d0 100644 --- a/parallel/modified_kahip/interface/kaHIP_interface.cpp +++ b/parallel/modified_kahip/interface/kaHIP_interface.cpp @@ -21,308 +21,308 @@ #include "../app/configuration.h" using namespace std; - -void internal_build_graph( PartitionConfig & partition_config, +namespace kahip::modified { +void internal_build_graph( PartitionConfig & partition_config, int* n, int* vwgt, int* xadj, int* adjcwgt, int* adjncy, graph_access & G) { - G.build_from_metis(*n, xadj, adjncy); - G.set_partition_count(partition_config.k); - - srand(partition_config.seed); - random_functions::setSeed(partition_config.seed); - - if(vwgt != NULL) { - forall_nodes(G, node) { - G.setNodeWeight(node, vwgt[node]); - } endfor - } - - if(adjcwgt != NULL) { - forall_edges(G, e) { - G.setEdgeWeight(e, adjcwgt[e]); - } endfor - } - - partition_config.largest_graph_weight = 0; - forall_nodes(G, node) { - partition_config.largest_graph_weight += G.getNodeWeight(node); - } endfor - - double epsilon = partition_config.imbalance/100; - partition_config.upper_bound_partition = ceil((1+epsilon)*partition_config.largest_graph_weight/(double)partition_config.k); - partition_config.graph_allready_partitioned = false; + G.build_from_metis(*n, xadj, adjncy); + G.set_partition_count(partition_config.k); + + srand(partition_config.seed); + random_functions::setSeed(partition_config.seed); + + if(vwgt != NULL) { + forall_nodes(G, node) { + G.setNodeWeight(node, vwgt[node]); + } endfor +} + if(adjcwgt != NULL) { + forall_edges(G, e) { + G.setEdgeWeight(e, adjcwgt[e]); + } endfor } + partition_config.largest_graph_weight = 0; + forall_nodes(G, node) { + partition_config.largest_graph_weight += G.getNodeWeight(node); + } endfor + + double epsilon = partition_config.imbalance/100; + partition_config.upper_bound_partition = ceil((1+epsilon)*partition_config.largest_graph_weight/(double)partition_config.k); + partition_config.graph_allready_partitioned = false; -void internal_kaffpa_call(PartitionConfig & partition_config, - bool suppress_output, - int* n, - int* vwgt, - int* xadj, - int* adjcwgt, - int* adjncy, - int* nparts, - double* imbalance, - int* edgecut, +} + + +void internal_kaffpa_call(PartitionConfig & partition_config, + bool suppress_output, + int* n, + int* vwgt, + int* xadj, + int* adjcwgt, + int* adjncy, + int* nparts, + double* imbalance, + int* edgecut, int* part) { - streambuf* backup = cout.rdbuf(); - ofstream ofs; - ofs.open("/dev/null"); - if(suppress_output) { - cout.rdbuf(ofs.rdbuf()); - } + streambuf* backup = cout.rdbuf(); + ofstream ofs; + ofs.open("/dev/null"); + if(suppress_output) { + cout.rdbuf(ofs.rdbuf()); + } + + partition_config.imbalance = 100*(*imbalance); + graph_access G; + internal_build_graph( partition_config, n, vwgt, xadj, adjcwgt, adjncy, G); - partition_config.imbalance = 100*(*imbalance); - graph_access G; - internal_build_graph( partition_config, n, vwgt, xadj, adjcwgt, adjncy, G); - - timer t; - graph_partitioner partitioner; - partitioner.perform_partitioning(partition_config, G); - std::cout << "partioning took " << t.elapsed() << std::endl; + timer t; + graph_partitioner partitioner; + partitioner.perform_partitioning(partition_config, G); + std::cout << "partioning took " << t.elapsed() << std::endl; - forall_nodes(G, node) { - part[node] = G.getPartitionIndex(node); - } endfor + forall_nodes(G, node) { + part[node] = G.getPartitionIndex(node); + } endfor - quality_metrics qm; - *edgecut = qm.edge_cut(G); + quality_metrics qm; + *edgecut = qm.edge_cut(G); - ofs.close(); - cout.rdbuf(backup); + ofs.close(); + cout.rdbuf(backup); } -void kaffpa(int* n, - int* vwgt, - int* xadj, - int* adjcwgt, - int* adjncy, - int* nparts, - double* imbalance, - bool suppress_output, +void kaffpa(int* n, + int* vwgt, + int* xadj, + int* adjcwgt, + int* adjncy, + int* nparts, + double* imbalance, + bool suppress_output, int seed, int mode, - int* edgecut, + int* edgecut, int* part) { - configuration cfg; - PartitionConfig partition_config; - partition_config.k = *nparts; - - switch( mode ) { - case FAST: - cfg.fast(partition_config); - break; - case ECO: - cfg.eco(partition_config); - break; - case STRONG: - cfg.strong(partition_config); - break; - case FASTSOCIAL: - cfg.fastsocial(partition_config); - break; - case ECOSOCIAL: - cfg.ecosocial(partition_config); - break; - case STRONGSOCIAL: - cfg.strongsocial(partition_config); - break; - default: - - cfg.eco(partition_config); - break; - } - - partition_config.seed = seed; - internal_kaffpa_call(partition_config, suppress_output, n, vwgt, xadj, adjcwgt, adjncy, nparts, imbalance, edgecut, part); + configuration cfg; + PartitionConfig partition_config; + partition_config.k = *nparts; + + switch( mode ) { + case FAST: + cfg.fast(partition_config); + break; + case ECO: + cfg.eco(partition_config); + break; + case STRONG: + cfg.strong(partition_config); + break; + case FASTSOCIAL: + cfg.fastsocial(partition_config); + break; + case ECOSOCIAL: + cfg.ecosocial(partition_config); + break; + case STRONGSOCIAL: + cfg.strongsocial(partition_config); + break; + default: + + cfg.eco(partition_config); + break; + } + + partition_config.seed = seed; + internal_kaffpa_call(partition_config, suppress_output, n, vwgt, xadj, adjcwgt, adjncy, nparts, imbalance, edgecut, part); } -void internal_nodeseparator_call(PartitionConfig & partition_config, - bool suppress_output, - int* n, - int* vwgt, - int* xadj, - int* adjcwgt, - int* adjncy, - int* nparts, - double* imbalance, - int* num_nodeseparator_vertices, +void internal_nodeseparator_call(PartitionConfig & partition_config, + bool suppress_output, + int* n, + int* vwgt, + int* xadj, + int* adjcwgt, + int* adjncy, + int* nparts, + double* imbalance, + int* num_nodeseparator_vertices, int** separator) { - //first perform std partitioning using KaFFPa - streambuf* backup = cout.rdbuf(); - ofstream ofs; - ofs.open("/dev/null"); - if(suppress_output) { - cout.rdbuf(ofs.rdbuf()); - } - - partition_config.imbalance = 100*(*imbalance); - graph_access G; - internal_build_graph( partition_config, n, vwgt, xadj, adjcwgt, adjncy, G); - - - graph_partitioner partitioner; - partitioner.perform_partitioning(partition_config, G); - - // now compute a node separator from the partition of the graph - complete_boundary boundary(&G); - boundary.build(); - - vertex_separator_algorithm vsa; - std::vector internal_separator; - vsa.compute_vertex_separator(partition_config, G, boundary, internal_separator); - - // copy to output variables - *num_nodeseparator_vertices = internal_separator.size(); - *separator = new int[*num_nodeseparator_vertices]; - for( unsigned int i = 0; i < internal_separator.size(); i++) { - (*separator)[i] = internal_separator[i]; - } - - ofs.close(); - cout.rdbuf(backup); + //first perform std partitioning using KaFFPa + streambuf* backup = cout.rdbuf(); + ofstream ofs; + ofs.open("/dev/null"); + if(suppress_output) { + cout.rdbuf(ofs.rdbuf()); + } + + partition_config.imbalance = 100*(*imbalance); + graph_access G; + internal_build_graph( partition_config, n, vwgt, xadj, adjcwgt, adjncy, G); + + + graph_partitioner partitioner; + partitioner.perform_partitioning(partition_config, G); + + // now compute a node separator from the partition of the graph + complete_boundary boundary(&G); + boundary.build(); + + vertex_separator_algorithm vsa; + std::vector internal_separator; + vsa.compute_vertex_separator(partition_config, G, boundary, internal_separator); + + // copy to output variables + *num_nodeseparator_vertices = internal_separator.size(); + *separator = new int[*num_nodeseparator_vertices]; + for( unsigned int i = 0; i < internal_separator.size(); i++) { + (*separator)[i] = internal_separator[i]; + } + + ofs.close(); + cout.rdbuf(backup); } -void node_separator(int* n, - int* vwgt, - int* xadj, - int* adjcwgt, - int* adjncy, - int* nparts, - double* imbalance, - bool suppress_output, +void node_separator(int* n, + int* vwgt, + int* xadj, + int* adjcwgt, + int* adjncy, + int* nparts, + double* imbalance, + bool suppress_output, int seed, int mode, - int* num_separator_vertices, + int* num_separator_vertices, int** separator) { - configuration cfg; - PartitionConfig partition_config; - partition_config.k = *nparts; - - switch( mode ) { - case FAST: - cfg.fast(partition_config); - break; - case ECO: - cfg.eco(partition_config); - break; - case STRONG: - cfg.strong(partition_config); - break; - case FASTSOCIAL: - cfg.fastsocial(partition_config); - break; - case ECOSOCIAL: - cfg.ecosocial(partition_config); - break; - case STRONGSOCIAL: - cfg.strongsocial(partition_config); - break; - default: - cfg.eco(partition_config); - break; - } - partition_config.seed = seed; - - internal_nodeseparator_call(partition_config, suppress_output, n, vwgt, xadj, adjcwgt, adjncy, nparts, imbalance, num_separator_vertices, separator); + configuration cfg; + PartitionConfig partition_config; + partition_config.k = *nparts; + + switch( mode ) { + case FAST: + cfg.fast(partition_config); + break; + case ECO: + cfg.eco(partition_config); + break; + case STRONG: + cfg.strong(partition_config); + break; + case FASTSOCIAL: + cfg.fastsocial(partition_config); + break; + case ECOSOCIAL: + cfg.ecosocial(partition_config); + break; + case STRONGSOCIAL: + cfg.strongsocial(partition_config); + break; + default: + cfg.eco(partition_config); + break; + } + partition_config.seed = seed; + + internal_nodeseparator_call(partition_config, suppress_output, n, vwgt, xadj, adjcwgt, adjncy, nparts, imbalance, num_separator_vertices, separator); } -void kaffpaE(int* n, - int* vwgt, - int* xadj, - int* adjcwgt, - int* adjncy, - int* nparts, - double* imbalance, - bool suppress_output, - bool graph_partitioned, +void kaffpaE(int* n, + int* vwgt, + int* xadj, + int* adjcwgt, + int* adjncy, + int* nparts, + double* imbalance, + bool suppress_output, + bool graph_partitioned, int time_limit, int seed, int mode, // 0 == strong, 1 == eco, 2 == fast - MPI_Comm communicator, - int* edgecut, + MPI_Comm communicator, + int* edgecut, double* balance, int* part) { - configuration cfg; - PartitionConfig partition_config; - partition_config.k = *nparts; - cfg.standard(partition_config); - - switch( mode ) { - case FAST: - cfg.fast(partition_config); - break; - case ECO: - cfg.eco(partition_config); - break; - case STRONG: - cfg.strong(partition_config); - break; - case FASTSOCIAL: - cfg.fastsocial(partition_config); - break; - case ULTRAFASTSOCIAL: - cfg.fastsocial(partition_config); - partition_config.ultra_fast_kaffpaE_interfacecall = true; - break; - case ECOSOCIAL: - cfg.ecosocial(partition_config); - break; - case STRONGSOCIAL: - cfg.strongsocial(partition_config); - break; - default: - cfg.eco(partition_config); - break; - } - - partition_config.seed = seed; - partition_config.k = *nparts; - partition_config.imbalance = 100*(*imbalance); - partition_config.time_limit = time_limit; - partition_config.kabapE = false; - - graph_access G; - internal_build_graph( partition_config, n, vwgt, xadj, adjcwgt, adjncy, G); - - partition_config.kway_adaptive_limits_beta = log(partition_config.largest_graph_weight); - - if(graph_partitioned) { - forall_nodes(G, node) { - G.setPartitionIndex(node, part[node]); - } endfor - } - - partition_config.graph_allready_partitioned = graph_partitioned; - partition_config.no_new_initial_partitioning = graph_partitioned; - - parallel_mh_async mh(communicator); - mh.perform_partitioning(partition_config, G); - - forall_nodes(G, node) { - part[node] = G.getPartitionIndex(node); - } endfor - - quality_metrics qm; - *edgecut = qm.edge_cut(G); - *balance = qm.balance(G); - - //ofs.close(); - //cout.rdbuf(backup); + configuration cfg; + PartitionConfig partition_config; + partition_config.k = *nparts; + cfg.standard(partition_config); + + switch( mode ) { + case FAST: + cfg.fast(partition_config); + break; + case ECO: + cfg.eco(partition_config); + break; + case STRONG: + cfg.strong(partition_config); + break; + case FASTSOCIAL: + cfg.fastsocial(partition_config); + break; + case ULTRAFASTSOCIAL: + cfg.fastsocial(partition_config); + partition_config.ultra_fast_kaffpaE_interfacecall = true; + break; + case ECOSOCIAL: + cfg.ecosocial(partition_config); + break; + case STRONGSOCIAL: + cfg.strongsocial(partition_config); + break; + default: + cfg.eco(partition_config); + break; + } + + partition_config.seed = seed; + partition_config.k = *nparts; + partition_config.imbalance = 100*(*imbalance); + partition_config.time_limit = time_limit; + partition_config.kabapE = false; + + graph_access G; + internal_build_graph( partition_config, n, vwgt, xadj, adjcwgt, adjncy, G); + + partition_config.kway_adaptive_limits_beta = log(partition_config.largest_graph_weight); + + if(graph_partitioned) { + forall_nodes(G, node) { + G.setPartitionIndex(node, part[node]); + } endfor } + partition_config.graph_allready_partitioned = graph_partitioned; + partition_config.no_new_initial_partitioning = graph_partitioned; + + parallel_mh_async mh(communicator); + mh.perform_partitioning(partition_config, G); + + forall_nodes(G, node) { + part[node] = G.getPartitionIndex(node); + } endfor + + quality_metrics qm; + *edgecut = qm.edge_cut(G); + *balance = qm.balance(G); + + //ofs.close(); + //cout.rdbuf(backup); +} +} diff --git a/parallel/modified_kahip/lib/algorithms/cycle_search.cpp b/parallel/modified_kahip/lib/algorithms/cycle_search.cpp index 0b74229b..1e6e3c43 100644 --- a/parallel/modified_kahip/lib/algorithms/cycle_search.cpp +++ b/parallel/modified_kahip/lib/algorithms/cycle_search.cpp @@ -11,7 +11,7 @@ #include "cycle_search.h" #include "random_functions.h" #include "timer.h" - +namespace kahip::modified { double cycle_search::total_time = 0; cycle_search::cycle_search() { @@ -23,416 +23,417 @@ cycle_search::~cycle_search() { } void cycle_search::find_random_cycle(graph_access & G, std::vector & cycle) { - //first perform a bfs starting from a random node and build the parent array - std::deque* bfsqueue = new std::deque; - NodeID v = random_functions::nextInt(0, G.number_of_nodes()-1); - bfsqueue->push_back(v); - - std::vector touched(G.number_of_nodes(),false); - std::vector is_leaf(G.number_of_nodes(),false); - std::vector parent(G.number_of_nodes(),0); - std::vector leafes; - touched[v] = true; - parent[v] = v; - - while(!bfsqueue->empty()) { - NodeID source = bfsqueue->front(); - bfsqueue->pop_front(); - - bool is_leaf = true; - forall_out_edges(G, e, source) { - NodeID target = G.getEdgeTarget(e); - if(!touched[target]) { - is_leaf = false; - touched[target] = true; - parent[target] = source; - bfsqueue->push_back(target); - } - } endfor - - if(is_leaf) - leafes.push_back(source); - - } - - std::vector sources(G.number_of_edges(), 0); - std::vector targets(G.number_of_edges(), 0); - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - sources[e] = node; - targets[e] = target; - } endfor - } endfor - - - //now find two random leafes - NodeID v_1, v_2; - unsigned r_idx = random_functions::nextInt(0, G.number_of_edges()-1); - while(true) { - NodeID source = sources[r_idx]; - NodeID target = targets[r_idx]; - if( parent[source] != target && parent[target] != source) { - //found a non-tree edge - v_1 = source; - v_2 = target; - break; - } - - r_idx = random_functions::nextInt(0, G.number_of_edges()-1); - } - - // NodeID now climb up the parent array step wise left and right - std::vector lhs_path, rhs_path; - lhs_path.push_back(v_1); - rhs_path.push_back(v_2); - - std::vector touched_nodes(G.number_of_nodes(),false); - std::vector index(G.number_of_nodes(),0); - index[v_1] = 0; - index[v_2] = 0; - touched_nodes[v_1] = true; - touched_nodes[v_2] = true; - - NodeID cur_lhs = v_1, cur_rhs = v_2; - NodeID counter = 0; - bool break_lhs = false; - while(true) { - counter++; - if(cur_lhs != parent[cur_lhs]) { - if(touched_nodes[parent[cur_lhs]] == true) { - break_lhs = true; - lhs_path.push_back(parent[cur_lhs]); - break; - } else { - cur_lhs = parent[cur_lhs]; - touched_nodes[cur_lhs] = true; - lhs_path.push_back(cur_lhs); - index[cur_lhs] = counter; - } - } - if(cur_rhs != parent[cur_rhs]) { - if(touched_nodes[parent[cur_rhs]] == true) { - rhs_path.push_back(parent[cur_rhs]); - break; - } else { - cur_rhs = parent[cur_rhs]; - touched_nodes[cur_rhs] = true; - rhs_path.push_back(cur_rhs); - index[cur_rhs] = counter; - } - } - - } - - if(break_lhs) { - for( unsigned i = 0; i < lhs_path.size(); i++) { - cycle.push_back(lhs_path[i]); - } - - NodeID connecting_vertice = cycle[cycle.size()-1]; - for( int i = index[connecting_vertice]-1; i >= 0; i--) { - cycle.push_back(rhs_path[i]); - } - } else { - for( unsigned i = 0; i < rhs_path.size(); i++) { - cycle.push_back(rhs_path[i]); - } - - NodeID connecting_vertice = cycle[cycle.size()-1]; - for( int i = index[connecting_vertice]-1; i >= 0; i--) { - cycle.push_back(lhs_path[i]); - } - } - - cycle.push_back(cycle[0]); + //first perform a bfs starting from a random node and build the parent array + std::deque* bfsqueue = new std::deque; + NodeID v = random_functions::nextInt(0, G.number_of_nodes()-1); + bfsqueue->push_back(v); + + std::vector touched(G.number_of_nodes(),false); + std::vector is_leaf(G.number_of_nodes(),false); + std::vector parent(G.number_of_nodes(),0); + std::vector leafes; + touched[v] = true; + parent[v] = v; + + while(!bfsqueue->empty()) { + NodeID source = bfsqueue->front(); + bfsqueue->pop_front(); + + bool is_leaf = true; + forall_out_edges(G, e, source) { + NodeID target = G.getEdgeTarget(e); + if(!touched[target]) { + is_leaf = false; + touched[target] = true; + parent[target] = source; + bfsqueue->push_back(target); + } + } endfor + + if(is_leaf) + leafes.push_back(source); + + } + + std::vector sources(G.number_of_edges(), 0); + std::vector targets(G.number_of_edges(), 0); + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + sources[e] = node; + targets[e] = target; + } endfor + } endfor + + + //now find two random leafes + NodeID v_1, v_2; + unsigned r_idx = random_functions::nextInt(0, G.number_of_edges()-1); + while(true) { + NodeID source = sources[r_idx]; + NodeID target = targets[r_idx]; + if( parent[source] != target && parent[target] != source) { + //found a non-tree edge + v_1 = source; + v_2 = target; + break; + } + + r_idx = random_functions::nextInt(0, G.number_of_edges()-1); + } + + // NodeID now climb up the parent array step wise left and right + std::vector lhs_path, rhs_path; + lhs_path.push_back(v_1); + rhs_path.push_back(v_2); + + std::vector touched_nodes(G.number_of_nodes(),false); + std::vector index(G.number_of_nodes(),0); + index[v_1] = 0; + index[v_2] = 0; + touched_nodes[v_1] = true; + touched_nodes[v_2] = true; + + NodeID cur_lhs = v_1, cur_rhs = v_2; + NodeID counter = 0; + bool break_lhs = false; + while(true) { + counter++; + if(cur_lhs != parent[cur_lhs]) { + if(touched_nodes[parent[cur_lhs]] == true) { + break_lhs = true; + lhs_path.push_back(parent[cur_lhs]); + break; + } else { + cur_lhs = parent[cur_lhs]; + touched_nodes[cur_lhs] = true; + lhs_path.push_back(cur_lhs); + index[cur_lhs] = counter; + } + } + if(cur_rhs != parent[cur_rhs]) { + if(touched_nodes[parent[cur_rhs]] == true) { + rhs_path.push_back(parent[cur_rhs]); + break; + } else { + cur_rhs = parent[cur_rhs]; + touched_nodes[cur_rhs] = true; + rhs_path.push_back(cur_rhs); + index[cur_rhs] = counter; + } + } + + } + + if(break_lhs) { + for( unsigned i = 0; i < lhs_path.size(); i++) { + cycle.push_back(lhs_path[i]); + } + + NodeID connecting_vertice = cycle[cycle.size()-1]; + for( int i = index[connecting_vertice]-1; i >= 0; i--) { + cycle.push_back(rhs_path[i]); + } + } else { + for( unsigned i = 0; i < rhs_path.size(); i++) { + cycle.push_back(rhs_path[i]); + } + + NodeID connecting_vertice = cycle[cycle.size()-1]; + for( int i = index[connecting_vertice]-1; i >= 0; i--) { + cycle.push_back(lhs_path[i]); + } + } + + cycle.push_back(cycle[0]); } -bool cycle_search::find_shortest_path(graph_access & G, - NodeID & start, - NodeID & dest, +bool cycle_search::find_shortest_path(graph_access & G, + NodeID & start, + NodeID & dest, std::vector & cycle) { - std::vector distance(G.number_of_nodes(), std::numeric_limits::max()/2); - std::vector parent(G.number_of_nodes(), std::numeric_limits::max()); - - bool negative_cycle_detected = negative_cycle_detection(G, start, distance, parent, cycle); - - if( !negative_cycle_detected) { - //if there is no negative cycle then we should return a shortest path from s to t - cycle.clear(); - cycle.push_back(dest); - NodeID cur = dest; - while(cur != start) { - cur = parent[cur]; - cycle.push_back(cur); - } - std::reverse(cycle.begin(), cycle.end()); - } - return negative_cycle_detected; + std::vector distance(G.number_of_nodes(), std::numeric_limits::max()/2); + std::vector parent(G.number_of_nodes(), std::numeric_limits::max()); + + bool negative_cycle_detected = negative_cycle_detection(G, start, distance, parent, cycle); + + if( !negative_cycle_detected) { + //if there is no negative cycle then we should return a shortest path from s to t + cycle.clear(); + cycle.push_back(dest); + NodeID cur = dest; + while(cur != start) { + cur = parent[cur]; + cycle.push_back(cur); + } + std::reverse(cycle.begin(), cycle.end()); + } + return negative_cycle_detected; } bool cycle_search::find_negative_cycle(graph_access & G, NodeID & start, std::vector & cycle) { - //simplest bellman ford algorithm + //simplest bellman ford algorithm - std::vector distance(G.number_of_nodes(), std::numeric_limits::max()/2); - std::vector parent(G.number_of_nodes(), std::numeric_limits::max()); + std::vector distance(G.number_of_nodes(), std::numeric_limits::max()/2); + std::vector parent(G.number_of_nodes(), std::numeric_limits::max()); - return negative_cycle_detection(G, start, distance, parent, cycle); + return negative_cycle_detection(G, start, distance, parent, cycle); } -int cycle_search::bellman_ford_with_subtree_disassembly_and_updates(graph_access & G, - NodeID & start, - std::vector & distance, - std::vector & parent, +int cycle_search::bellman_ford_with_subtree_disassembly_and_updates(graph_access & G, + NodeID & start, + std::vector & distance, + std::vector & parent, std::vector & cycle) { - // Goldberg spc-1.2 similar implementation using our data structures - int NULL_NODE = -1; - distance[start] = 0; - std::queue L; - - //doubly linked list of shortest path tree in preorder - short OUT_OF_QUEUE = 0; - short INACTIVE = 1; - short ACTIVE = 2; - short IN_QUEUE = 2; - - std::vector before(G.number_of_nodes(), NULL_NODE); - std::vector after(G.number_of_nodes()); - std::vector degree(G.number_of_nodes()); - std::vector status(G.number_of_nodes(), OUT_OF_QUEUE); - - L.push(start); - - after[start] = start; - before[start] = start; - degree[start] = -1; - status[start] = IN_QUEUE; - - while( ! L.empty() ) { - NodeID v = L.front(); - L.pop(); - - short current_status = status[v]; - status[v] = OUT_OF_QUEUE; - - if( current_status == INACTIVE ) continue; - - forall_out_edges(G, e, v) { - NodeID w = G.getEdgeTarget(e); - int delta = distance[w] - distance[v] - G.getEdgeWeight(e); - if(delta > 0) { - // dissassemble subtree - // in this case we disassemble the subtree looking for v - int new_distance = distance[w] - delta; - - int x = before[w]; - int y = w; - if( x != NULL_NODE) { - // in this case w is allready in the tree and we remove it / disassemble the subtree - for( int total_degree = 0; total_degree >= 0; y = after[y]) { - //disassemble the subtree - //w <-> .... <-> y <-> ... - if( y == (int) v ) { - parent[w] = v; - return w; // since parent[w] = v - - } else{ - distance[y] = distance[y] - delta; - before[y] = NULL_NODE; - total_degree += degree[y]; - - if( status[y] == ACTIVE ) { - status[y] = INACTIVE; - } - } - } - - // since we removed w from the shortest path tree - degree[parent[w]]--; - - // the old subtree - // x <-> [w <-> subtree]_is cut <-> y - // y is the vertex after the subtree of w - // afterwards: x <-> y - after[x] = y; - before[y] = x; - } - distance[w] = new_distance; - parent[w] = v; - - } - - // negative cycle is not found - // =================================================== - // take care of the rest of the shortest path Tree T - // =================================================== - if( before[w] == NULL_NODE && parent[w] == v) { - // in this case w was not in the shortest path tree - // so we integrate it - degree[v] ++; - degree[w] = -1; - - NodeID after_v = after[v]; - - // integrate w into the tree - after[v] = w; - before[w] = v; - after[w] = after_v; - before[after_v] = w; - // we now have v <-> w <-> after_v in the preorder tree representation - - // handle the queue - if( status[w] == OUT_OF_QUEUE ) { - L.push(w); - status[w] = IN_QUEUE; - } else { - status[w] = ACTIVE; - } - - - } - } endfor + // Goldberg spc-1.2 similar implementation using our data structures + int NULL_NODE = -1; + distance[start] = 0; + std::queue L; + + //doubly linked list of shortest path tree in preorder + short OUT_OF_QUEUE = 0; + short INACTIVE = 1; + short ACTIVE = 2; + short IN_QUEUE = 2; + + std::vector before(G.number_of_nodes(), NULL_NODE); + std::vector after(G.number_of_nodes()); + std::vector degree(G.number_of_nodes()); + std::vector status(G.number_of_nodes(), OUT_OF_QUEUE); + + L.push(start); + + after[start] = start; + before[start] = start; + degree[start] = -1; + status[start] = IN_QUEUE; + + while( ! L.empty() ) { + NodeID v = L.front(); + L.pop(); + + short current_status = status[v]; + status[v] = OUT_OF_QUEUE; + + if( current_status == INACTIVE ) continue; + + forall_out_edges(G, e, v) { + NodeID w = G.getEdgeTarget(e); + int delta = distance[w] - distance[v] - G.getEdgeWeight(e); + if(delta > 0) { + // dissassemble subtree + // in this case we disassemble the subtree looking for v + int new_distance = distance[w] - delta; + + int x = before[w]; + int y = w; + if( x != NULL_NODE) { + // in this case w is allready in the tree and we remove it / disassemble the subtree + for( int total_degree = 0; total_degree >= 0; y = after[y]) { + //disassemble the subtree + //w <-> .... <-> y <-> ... + if( y == (int) v ) { + parent[w] = v; + return w; // since parent[w] = v + + } else{ + distance[y] = distance[y] - delta; + before[y] = NULL_NODE; + total_degree += degree[y]; + + if( status[y] == ACTIVE ) { + status[y] = INACTIVE; + } + } + } + + // since we removed w from the shortest path tree + degree[parent[w]]--; + + // the old subtree + // x <-> [w <-> subtree]_is cut <-> y + // y is the vertex after the subtree of w + // afterwards: x <-> y + after[x] = y; + before[y] = x; } - return NULL_NODE; + distance[w] = new_distance; + parent[w] = v; + + } + + // negative cycle is not found + // =================================================== + // take care of the rest of the shortest path Tree T + // =================================================== + if( before[w] == NULL_NODE && parent[w] == v) { + // in this case w was not in the shortest path tree + // so we integrate it + degree[v] ++; + degree[w] = -1; + + NodeID after_v = after[v]; + + // integrate w into the tree + after[v] = w; + before[w] = v; + after[w] = after_v; + before[after_v] = w; + // we now have v <-> w <-> after_v in the preorder tree representation + + // handle the queue + if( status[w] == OUT_OF_QUEUE ) { + L.push(w); + status[w] = IN_QUEUE; + } else { + status[w] = ACTIVE; + } + + + } + } endfor +} + return NULL_NODE; } -bool cycle_search::negative_cycle_detection(graph_access & G, - NodeID & start, - std::vector & distance, - std::vector & parent, +bool cycle_search::negative_cycle_detection(graph_access & G, + NodeID & start, + std::vector & distance, + std::vector & parent, std::vector & cycle) { - timer timeR; - - int w = bellman_ford_with_subtree_disassembly_and_updates(G, start, distance, parent, cycle); - - if(w >= 0) { // found a cycle - // the edge yielding the cycle was (t,w) - NodeID t = parent[w]; - NodeID u = t; - - std::vector seen(G.number_of_nodes(), false); //use hashing? - seen[u] = true; - NodeID predecessor = parent[u]; - NodeID start_vertex; - - while( true ) { - if( seen[predecessor] ) { - start_vertex = predecessor; - break; - } - seen[predecessor] = true; - predecessor = parent[predecessor]; - } - - cycle.push_back(start_vertex); - predecessor = parent[start_vertex]; - while( predecessor != start_vertex) { - cycle.push_back(predecessor); - predecessor = parent[predecessor]; - } - cycle.push_back(start_vertex); - std::reverse(cycle.begin(), cycle.end()); - - total_time += timeR.elapsed(); - return true; - - } - - total_time += timeR.elapsed(); - return false; + timer timeR; + + int w = bellman_ford_with_subtree_disassembly_and_updates(G, start, distance, parent, cycle); + + if(w >= 0) { // found a cycle + // the edge yielding the cycle was (t,w) + NodeID t = parent[w]; + NodeID u = t; + + std::vector seen(G.number_of_nodes(), false); //use hashing? + seen[u] = true; + NodeID predecessor = parent[u]; + NodeID start_vertex; + + while( true ) { + if( seen[predecessor] ) { + start_vertex = predecessor; + break; + } + seen[predecessor] = true; + predecessor = parent[predecessor]; + } + + cycle.push_back(start_vertex); + predecessor = parent[start_vertex]; + while( predecessor != start_vertex) { + cycle.push_back(predecessor); + predecessor = parent[predecessor]; + } + cycle.push_back(start_vertex); + std::reverse(cycle.begin(), cycle.end()); + + total_time += timeR.elapsed(); + return true; + + } + + total_time += timeR.elapsed(); + return false; } -//preconditition: no negative cycles +//preconditition: no negative cycles bool cycle_search::find_zero_weight_cycle(graph_access & G, NodeID & start, std::vector & cycle) { - std::vector distance(G.number_of_nodes(), std::numeric_limits::max()/2); - std::vector parent(G.number_of_nodes(), std::numeric_limits::max()); - bool negative_weight_cycle = negative_cycle_detection(G, start, distance, parent, cycle); - if(!negative_weight_cycle) { - //now we try to return a random directed zero weight gain cycle - //therefore we use W(e) = d(u) + w(e) - d(v) - //and keep edges with weight 0 - graph_access W; - W.start_construction(G.number_of_nodes(), G.number_of_edges()); - - forall_nodes(G, node) { - NodeID shadow_node = W.new_node(); - W.setNodeWeight(shadow_node, G.getNodeWeight(node)); - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - EdgeWeight modified_edge_weight = G.getEdgeWeight(e) + distance[node] - distance[target]; - ASSERT_GEQ(modified_edge_weight, 0); - if(modified_edge_weight == 0) { - EdgeID shadow_edge = W.new_edge(shadow_node, target); - W.setEdgeWeight(shadow_edge, 0); - } - } endfor - } endfor - W.finish_construction(); - - strongly_connected_components scc; - std::vector comp_num(W.number_of_nodes()); - scc.strong_components(W, comp_num); - - //first check wether there are components with more then one vertex - std::vector comp_count(W.number_of_nodes(), 0); - forall_nodes(W, node) { - comp_count[comp_num[node]]++; - } endfor - - std::vector candidates; - forall_nodes(W, node) { - if(comp_count[comp_num[node]] > 1) { - candidates.push_back(node); - } - } endfor - - if(candidates.size() == 0) {return false;} - - //now pick a random start vertex - NodeID start_vertex_idx = random_functions::nextInt(0, candidates.size()-1); - NodeID start_vertex = candidates[start_vertex_idx]; - std::vector seen(W.number_of_nodes(), false); - std::vector list; - - NodeID successor = start_vertex; - NodeID comp_of_sv = comp_num[start_vertex]; - do { - - seen[successor] = true; - list.push_back(successor); - - std::vector same_comp_neighbors; - forall_out_edges(W, e, successor) { - NodeID target = W.getEdgeTarget(e); - if(comp_num[target] == (int)comp_of_sv) { - same_comp_neighbors.push_back(target); - } - } endfor - NodeID succ_id = random_functions::nextInt(0, same_comp_neighbors.size()-1); - - successor = same_comp_neighbors[succ_id]; - } while(seen[successor] == false); - - NodeID start_idx = 0; - for( unsigned i = 0; i < list.size(); i++) { - if(list[i] == successor) { - start_idx = i; - break; - } - } - - for( unsigned i = start_idx; i < list.size(); i++) { - cycle.push_back(list[i]); - } - cycle.push_back(successor); - - return true; + std::vector distance(G.number_of_nodes(), std::numeric_limits::max()/2); + std::vector parent(G.number_of_nodes(), std::numeric_limits::max()); + bool negative_weight_cycle = negative_cycle_detection(G, start, distance, parent, cycle); + if(!negative_weight_cycle) { + //now we try to return a random directed zero weight gain cycle + //therefore we use W(e) = d(u) + w(e) - d(v) + //and keep edges with weight 0 + graph_access W; + W.start_construction(G.number_of_nodes(), G.number_of_edges()); + + forall_nodes(G, node) { + NodeID shadow_node = W.new_node(); + W.setNodeWeight(shadow_node, G.getNodeWeight(node)); + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + EdgeWeight modified_edge_weight = G.getEdgeWeight(e) + distance[node] - distance[target]; + ASSERT_GEQ(modified_edge_weight, 0); + if(modified_edge_weight == 0) { + EdgeID shadow_edge = W.new_edge(shadow_node, target); + W.setEdgeWeight(shadow_edge, 0); + } + } endfor +} endfor +W.finish_construction(); + + strongly_connected_components scc; + std::vector comp_num(W.number_of_nodes()); + scc.strong_components(W, comp_num); + + //first check wether there are components with more then one vertex + std::vector comp_count(W.number_of_nodes(), 0); + forall_nodes(W, node) { + comp_count[comp_num[node]]++; + } endfor + + std::vector candidates; + forall_nodes(W, node) { + if(comp_count[comp_num[node]] > 1) { + candidates.push_back(node); + } + } endfor + + if(candidates.size() == 0) {return false;} + + //now pick a random start vertex + NodeID start_vertex_idx = random_functions::nextInt(0, candidates.size()-1); + NodeID start_vertex = candidates[start_vertex_idx]; + std::vector seen(W.number_of_nodes(), false); + std::vector list; + + NodeID successor = start_vertex; + NodeID comp_of_sv = comp_num[start_vertex]; + do { + + seen[successor] = true; + list.push_back(successor); + + std::vector same_comp_neighbors; + forall_out_edges(W, e, successor) { + NodeID target = W.getEdgeTarget(e); + if(comp_num[target] == (int)comp_of_sv) { + same_comp_neighbors.push_back(target); } - return false; + } endfor + NodeID succ_id = random_functions::nextInt(0, same_comp_neighbors.size()-1); + + successor = same_comp_neighbors[succ_id]; + } while(seen[successor] == false); + + NodeID start_idx = 0; + for( unsigned i = 0; i < list.size(); i++) { + if(list[i] == successor) { + start_idx = i; + break; + } + } + + for( unsigned i = start_idx; i < list.size(); i++) { + cycle.push_back(list[i]); + } + cycle.push_back(successor); + + return true; + } + return false; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/algorithms/cycle_search.h b/parallel/modified_kahip/lib/algorithms/cycle_search.h index 87af2359..d8bfe095 100644 --- a/parallel/modified_kahip/lib/algorithms/cycle_search.h +++ b/parallel/modified_kahip/lib/algorithms/cycle_search.h @@ -9,36 +9,36 @@ #define CYCLE_SEARCH_IO23844C #include "data_structure/graph_access.h" - +namespace kahip::modified { class cycle_search { public: - cycle_search(); - virtual ~cycle_search(); + cycle_search(); + virtual ~cycle_search(); + + void find_random_cycle(graph_access & G, std::vector & cycle); - void find_random_cycle(graph_access & G, std::vector & cycle); + //returns true if a negative cycle was found, else false + bool find_negative_cycle(graph_access & G, NodeID & start, std::vector & cycle); - //returns true if a negative cycle was found, else false - bool find_negative_cycle(graph_access & G, NodeID & start, std::vector & cycle); - - bool find_zero_weight_cycle(graph_access & G, NodeID & start, std::vector & cycle); + bool find_zero_weight_cycle(graph_access & G, NodeID & start, std::vector & cycle); - bool find_shortest_path(graph_access & G, NodeID & start, NodeID & dest, std::vector & cycle); + bool find_shortest_path(graph_access & G, NodeID & start, NodeID & dest, std::vector & cycle); - static double total_time; + static double total_time; private: - bool negative_cycle_detection(graph_access & G, - NodeID & start, - std::vector & distance, - std::vector & parent, - std::vector & cycle); - - int bellman_ford_with_subtree_disassembly_and_updates(graph_access & G, - NodeID & start, - std::vector & distance, - std::vector & parent, - std::vector & cycle); + bool negative_cycle_detection(graph_access & G, + NodeID & start, + std::vector & distance, + std::vector & parent, + std::vector & cycle); + + int bellman_ford_with_subtree_disassembly_and_updates(graph_access & G, + NodeID & start, + std::vector & distance, + std::vector & parent, + std::vector & cycle); }; - +} #endif /* end of include guard: CYCLE_SEARCH_IO23844C */ diff --git a/parallel/modified_kahip/lib/algorithms/strongly_connected_components.cpp b/parallel/modified_kahip/lib/algorithms/strongly_connected_components.cpp index 0f995dca..e23c43c2 100644 --- a/parallel/modified_kahip/lib/algorithms/strongly_connected_components.cpp +++ b/parallel/modified_kahip/lib/algorithms/strongly_connected_components.cpp @@ -9,7 +9,7 @@ #include #include "strongly_connected_components.h" - +namespace kahip::modified { strongly_connected_components::strongly_connected_components() { } @@ -20,62 +20,62 @@ strongly_connected_components::~strongly_connected_components() { int strongly_connected_components::strong_components( graph_access & G, std::vector & comp_num) { - std::stack unfinished; - std::stack roots; + std::stack unfinished; + std::stack roots; - std::vector dfsnum(G.number_of_nodes(), -1); - m_dfscount = 0; - m_comp_count = 0; + std::vector dfsnum(G.number_of_nodes(), -1); + m_dfscount = 0; + m_comp_count = 0; - forall_nodes(G, node) { - comp_num[node] = -1; - } endfor + forall_nodes(G, node) { + comp_num[node] = -1; + } endfor - forall_nodes(G, node) { - if(dfsnum[node] == -1) { - scc_dfs(node, G, dfsnum, comp_num, unfinished, roots); - } - } endfor - return m_comp_count; + forall_nodes(G, node) { + if(dfsnum[node] == -1) { + scc_dfs(node, G, dfsnum, comp_num, unfinished, roots); + } + } endfor + return m_comp_count; } -void strongly_connected_components::scc_dfs(NodeID node, graph_access & G, - std::vector & dfsnum, +void strongly_connected_components::scc_dfs(NodeID node, graph_access & G, + std::vector & dfsnum, std::vector & comp_num, - std::stack & unfinished, - std::stack & roots){ - dfsnum[node] = m_dfscount++; - - //make node a tentative scc of its own - unfinished.push(node); - roots.push(node); - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - //explore edge (node, target) - if(dfsnum[target] == -1) { - scc_dfs(target, G, dfsnum, comp_num, unfinished, roots); - } else if( comp_num[target] == -1) { - //merge scc's - while( dfsnum[roots.top()] > dfsnum[target] ) roots.pop(); - } - - } endfor - - //return from call of node node - NodeID w; - if(node == roots.top()) { - do { - w = unfinished.top(); - unfinished.pop(); - comp_num[w] = m_comp_count; - } while( w != node ); - m_comp_count++; - roots.pop(); - } + std::stack & unfinished, + std::stack & roots){ + dfsnum[node] = m_dfscount++; + + //make node a tentative scc of its own + unfinished.push(node); + roots.push(node); + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + //explore edge (node, target) + if(dfsnum[target] == -1) { + scc_dfs(target, G, dfsnum, comp_num, unfinished, roots); + } else if( comp_num[target] == -1) { + //merge scc's + while( dfsnum[roots.top()] > dfsnum[target] ) roots.pop(); + } + + } endfor + + //return from call of node node + NodeID w; + if(node == roots.top()) { + do { + w = unfinished.top(); + unfinished.pop(); + comp_num[w] = m_comp_count; + } while( w != node ); + m_comp_count++; + roots.pop(); + } } - +} diff --git a/parallel/modified_kahip/lib/algorithms/strongly_connected_components.h b/parallel/modified_kahip/lib/algorithms/strongly_connected_components.h index 0350dd6d..21328f0a 100644 --- a/parallel/modified_kahip/lib/algorithms/strongly_connected_components.h +++ b/parallel/modified_kahip/lib/algorithms/strongly_connected_components.h @@ -13,23 +13,23 @@ #include "data_structure/graph_access.h" #include "definitions.h" - +namespace kahip::modified { class strongly_connected_components { public: - strongly_connected_components(); - virtual ~strongly_connected_components(); + strongly_connected_components(); + virtual ~strongly_connected_components(); + + int strong_components( graph_access & G, std::vector & comp_num); - int strong_components( graph_access & G, std::vector & comp_num); - - void scc_dfs(NodeID node, graph_access & G, - std::vector & dfsnum, - std::vector & comp_num, - std::stack & unfinished, - std::stack & roots); + void scc_dfs(NodeID node, graph_access & G, + std::vector & dfsnum, + std::vector & comp_num, + std::stack & unfinished, + std::stack & roots); private: - int m_dfscount; - int m_comp_count; + int m_dfscount; + int m_comp_count; }; - +} #endif /* end of include guard: STRONGLY_CONNECTED_COMPONENTS_7ZJ8233R */ diff --git a/parallel/modified_kahip/lib/algorithms/topological_sort.cpp b/parallel/modified_kahip/lib/algorithms/topological_sort.cpp index 7a602c72..7ee4d163 100644 --- a/parallel/modified_kahip/lib/algorithms/topological_sort.cpp +++ b/parallel/modified_kahip/lib/algorithms/topological_sort.cpp @@ -9,7 +9,7 @@ #include "random_functions.h" #include "topological_sort.h" - +namespace kahip::modified { topological_sort::topological_sort() { } @@ -19,37 +19,38 @@ topological_sort::~topological_sort() { } void topological_sort::sort( graph_access & G, std::vector & sorted_sequence) { - std::vector dfsnum(G.number_of_nodes(), -1); - int dfscount = 0; + std::vector dfsnum(G.number_of_nodes(), -1); + int dfscount = 0; - std::vector nodes(G.number_of_nodes()); - random_functions::permutate_vector_good(nodes, true); + std::vector nodes(G.number_of_nodes()); + random_functions::permutate_vector_good(nodes, true); - forall_nodes(G, node) { - NodeID curNode = nodes[node]; - if(dfsnum[curNode] == -1) { - sort_dfs(curNode, G, dfsnum, dfscount, sorted_sequence); - } - } endfor + forall_nodes(G, node) { + NodeID curNode = nodes[node]; + if(dfsnum[curNode] == -1) { + sort_dfs(curNode, G, dfsnum, dfscount, sorted_sequence); + } + } endfor - std::reverse(sorted_sequence.begin(), sorted_sequence.end()); + std::reverse(sorted_sequence.begin(), sorted_sequence.end()); } -void topological_sort::sort_dfs(NodeID node, graph_access & G, - std::vector & dfsnum, +void topological_sort::sort_dfs(NodeID node, graph_access & G, + std::vector & dfsnum, int & dfscount, - std::vector & sorted_sequence){ - - dfsnum[node] = dfscount++; - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - //explore edge (node, target) - if(dfsnum[target] == -1) { - sort_dfs(target, G, dfsnum, dfscount, sorted_sequence); - } - } endfor - - //return from call of node node - sorted_sequence.push_back(node); + std::vector & sorted_sequence){ + + dfsnum[node] = dfscount++; + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + //explore edge (node, target) + if(dfsnum[target] == -1) { + sort_dfs(target, G, dfsnum, dfscount, sorted_sequence); + } + } endfor + + //return from call of node node + sorted_sequence.push_back(node); } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/algorithms/topological_sort.h b/parallel/modified_kahip/lib/algorithms/topological_sort.h index 56701add..af0352a5 100644 --- a/parallel/modified_kahip/lib/algorithms/topological_sort.h +++ b/parallel/modified_kahip/lib/algorithms/topological_sort.h @@ -13,19 +13,19 @@ #include "data_structure/graph_access.h" #include "definitions.h" - +namespace kahip::modified { class topological_sort { public: - topological_sort(); - virtual ~topological_sort(); + topological_sort(); + virtual ~topological_sort(); - void sort( graph_access & SG, std::vector & sorted_sequence); + void sort( graph_access & SG, std::vector & sorted_sequence); - void sort_dfs(NodeID node, graph_access & G, - std::vector & dfsnum, - int & dfscount, - std::vector & sorted_sequence); + void sort_dfs(NodeID node, graph_access & G, + std::vector & dfsnum, + int & dfscount, + std::vector & sorted_sequence); }; - +} #endif /* end of include guard: TOPOLOGICAL_SORT_GB9FC2CZ */ diff --git a/parallel/modified_kahip/lib/data_structure/graph_access.h b/parallel/modified_kahip/lib/data_structure/graph_access.h index d7cfa473..151eba29 100644 --- a/parallel/modified_kahip/lib/data_structure/graph_access.h +++ b/parallel/modified_kahip/lib/data_structure/graph_access.h @@ -14,128 +14,128 @@ #include #include "definitions.h" - +namespace kahip::modified { struct Node { - EdgeID firstEdge; - NodeWeight weight; + EdgeID firstEdge; + NodeWeight weight; }; struct Edge { - NodeID target; - EdgeWeight weight; + NodeID target; + EdgeWeight weight; }; struct refinementNode { - PartitionID partitionIndex; - //Count queueIndex; + PartitionID partitionIndex; + //Count queueIndex; }; struct coarseningEdge { - EdgeRatingType rating; + EdgeRatingType rating; }; class graph_access; //construction etc. is encapsulated in basicGraph / access to properties etc. is encapsulated in graph_access class basicGraph { - friend class graph_access; + friend class graph_access; public: - basicGraph() : m_building_graph(false) { - } + basicGraph() : m_building_graph(false) { + } private: - //methods only to be used by friend class - EdgeID number_of_edges() { - return m_edges.size(); - } - - NodeID number_of_nodes() { - return m_nodes.size()-1; - } - - inline EdgeID get_first_edge(const NodeID & node) { - return m_nodes[node].firstEdge; - } - - inline EdgeID get_first_invalid_edge(const NodeID & node) { - return m_nodes[node+1].firstEdge; - } - - // construction of the graph - void start_construction(NodeID n, EdgeID m) { - m_building_graph = true; - node = 0; - e = 0; - m_last_source = -1; - - //resizes property arrays - m_nodes.resize(n+1); - m_refinement_node_props.resize(n+1); - m_edges.resize(m); - m_coarsening_edge_props.resize(m); - - m_nodes[node].firstEdge = e; - } - - EdgeID new_edge(NodeID source, NodeID target) { - ASSERT_TRUE(m_building_graph); - ASSERT_TRUE(e < m_edges.size()); - - m_edges[e].target = target; - EdgeID e_bar = e; - ++e; - - ASSERT_TRUE(source+1 < m_nodes.size()); - m_nodes[source+1].firstEdge = e; - - //fill isolated sources at the end - if ((NodeID)(m_last_source+1) < source) { - for (NodeID i = source; i>(NodeID)(m_last_source+1); i--) { - m_nodes[i].firstEdge = m_nodes[m_last_source+1].firstEdge; - } + //methods only to be used by friend class + EdgeID number_of_edges() { + return m_edges.size(); + } + + NodeID number_of_nodes() { + return m_nodes.size()-1; + } + + inline EdgeID get_first_edge(const NodeID & node) { + return m_nodes[node].firstEdge; + } + + inline EdgeID get_first_invalid_edge(const NodeID & node) { + return m_nodes[node+1].firstEdge; + } + + // construction of the graph + void start_construction(NodeID n, EdgeID m) { + m_building_graph = true; + node = 0; + e = 0; + m_last_source = -1; + + //resizes property arrays + m_nodes.resize(n+1); + m_refinement_node_props.resize(n+1); + m_edges.resize(m); + m_coarsening_edge_props.resize(m); + + m_nodes[node].firstEdge = e; } - m_last_source = source; - return e_bar; - } - - NodeID new_node() { - ASSERT_TRUE(m_building_graph); - return node++; - } - - void finish_construction() { - // inert dummy node - m_nodes.resize(node+1); - m_refinement_node_props.resize(node+1); - - m_edges.resize(e); - m_coarsening_edge_props.resize(e); - - m_building_graph = false; - - //fill isolated sources at the end - if ((unsigned int)(m_last_source) != node-1) { - //in that case at least the last node was an isolated node - for (NodeID i = node; i>(unsigned int)(m_last_source+1); i--) { - m_nodes[i].firstEdge = m_nodes[m_last_source+1].firstEdge; + + EdgeID new_edge(NodeID source, NodeID target) { + ASSERT_TRUE(m_building_graph); + ASSERT_TRUE(e < m_edges.size()); + + m_edges[e].target = target; + EdgeID e_bar = e; + ++e; + + ASSERT_TRUE(source+1 < m_nodes.size()); + m_nodes[source+1].firstEdge = e; + + //fill isolated sources at the end + if ((NodeID)(m_last_source+1) < source) { + for (NodeID i = source; i>(NodeID)(m_last_source+1); i--) { + m_nodes[i].firstEdge = m_nodes[m_last_source+1].firstEdge; + } } + m_last_source = source; + return e_bar; } - } - - // %%%%%%%%%%%%%%%%%%% DATA %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // split properties for coarsening and uncoarsening - std::vector m_nodes; - std::vector m_edges; - - std::vector m_refinement_node_props; - std::vector m_coarsening_edge_props; - - // construction properties - bool m_building_graph; - int m_last_source; - NodeID node; //current node that is constructed - EdgeID e; //current edge that is constructed + + NodeID new_node() { + ASSERT_TRUE(m_building_graph); + return node++; + } + + void finish_construction() { + // inert dummy node + m_nodes.resize(node+1); + m_refinement_node_props.resize(node+1); + + m_edges.resize(e); + m_coarsening_edge_props.resize(e); + + m_building_graph = false; + + //fill isolated sources at the end + if ((unsigned int)(m_last_source) != node-1) { + //in that case at least the last node was an isolated node + for (NodeID i = node; i>(unsigned int)(m_last_source+1); i--) { + m_nodes[i].firstEdge = m_nodes[m_last_source+1].firstEdge; + } + } + } + + // %%%%%%%%%%%%%%%%%%% DATA %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // split properties for coarsening and uncoarsening + std::vector m_nodes; + std::vector m_edges; + + std::vector m_refinement_node_props; + std::vector m_coarsening_edge_props; + + // construction properties + bool m_building_graph; + int m_last_source; + NodeID node; //current node that is constructed + EdgeID e; //current edge that is constructed }; //makros - graph access @@ -150,73 +150,73 @@ class complete_boundary; class graph_access { friend class complete_boundary; - public: - graph_access() { m_max_degree_computed = false; m_max_degree = 0; graphref = new basicGraph();} - virtual ~graph_access(){ delete graphref; }; +public: + graph_access() { m_max_degree_computed = false; m_max_degree = 0; graphref = new basicGraph();} + virtual ~graph_access(){ delete graphref; }; - /* ============================================================= */ - /* build methods */ - /* ============================================================= */ - void start_construction(NodeID nodes, EdgeID edges); - NodeID new_node(); - EdgeID new_edge(NodeID source, NodeID target); - void finish_construction(); + /* ============================================================= */ + /* build methods */ + /* ============================================================= */ + void start_construction(NodeID nodes, EdgeID edges); + NodeID new_node(); + EdgeID new_edge(NodeID source, NodeID target); + void finish_construction(); - /* ============================================================= */ - /* graph access methods */ - /* ============================================================= */ - NodeID number_of_nodes(); - EdgeID number_of_edges(); + /* ============================================================= */ + /* graph access methods */ + /* ============================================================= */ + NodeID number_of_nodes(); + EdgeID number_of_edges(); - EdgeID get_first_edge(NodeID node); - EdgeID get_first_invalid_edge(NodeID node); + EdgeID get_first_edge(NodeID node); + EdgeID get_first_invalid_edge(NodeID node); - PartitionID get_partition_count(); - void set_partition_count(PartitionID count); + PartitionID get_partition_count(); + void set_partition_count(PartitionID count); - PartitionID getPartitionIndex(NodeID node); - void setPartitionIndex(NodeID node, PartitionID id); + PartitionID getPartitionIndex(NodeID node); + void setPartitionIndex(NodeID node, PartitionID id); - PartitionID getSecondPartitionIndex(NodeID node); - void setSecondPartitionIndex(NodeID node, PartitionID id); + PartitionID getSecondPartitionIndex(NodeID node); + void setSecondPartitionIndex(NodeID node, PartitionID id); - //to be called if combine in meta heuristic is used - void resizeSecondPartitionIndex(unsigned no_nodes); + //to be called if combine in meta heuristic is used + void resizeSecondPartitionIndex(unsigned no_nodes); - NodeWeight getNodeWeight(NodeID node); - void setNodeWeight(NodeID node, NodeWeight weight); + NodeWeight getNodeWeight(NodeID node); + void setNodeWeight(NodeID node, NodeWeight weight); - EdgeWeight getNodeDegree(NodeID node); - EdgeWeight getWeightedNodeDegree(NodeID node); - EdgeWeight getMaxDegree(); + EdgeWeight getNodeDegree(NodeID node); + EdgeWeight getWeightedNodeDegree(NodeID node); + EdgeWeight getMaxDegree(); - EdgeWeight getEdgeWeight(EdgeID edge); - void setEdgeWeight(EdgeID edge, EdgeWeight weight); + EdgeWeight getEdgeWeight(EdgeID edge); + void setEdgeWeight(EdgeID edge, EdgeWeight weight); - NodeID getEdgeTarget(EdgeID edge); + NodeID getEdgeTarget(EdgeID edge); - EdgeRatingType getEdgeRating(EdgeID edge); - void setEdgeRating(EdgeID edge, EdgeRatingType rating); + EdgeRatingType getEdgeRating(EdgeID edge); + void setEdgeRating(EdgeID edge, EdgeRatingType rating); - int* UNSAFE_metis_style_xadj_array(); - int* UNSAFE_metis_style_adjncy_array(); + int* UNSAFE_metis_style_xadj_array(); + int* UNSAFE_metis_style_adjncy_array(); - int* UNSAFE_metis_style_vwgt_array(); - int* UNSAFE_metis_style_adjwgt_array(); + int* UNSAFE_metis_style_vwgt_array(); + int* UNSAFE_metis_style_adjwgt_array(); - int build_from_metis(int n, int* xadj, int* adjncy); - int build_from_metis_weighted(int n, int* xadj, int* adjncy, int * vwgt, int* adjwgt); + int build_from_metis(int n, int* xadj, int* adjncy); + int build_from_metis_weighted(int n, int* xadj, int* adjncy, int * vwgt, int* adjwgt); - //void set_node_queue_index(NodeID node, Count queue_index); - //Count get_node_queue_index(NodeID node); + //void set_node_queue_index(NodeID node, Count queue_index); + //Count get_node_queue_index(NodeID node); - void copy(graph_access & Gcopy); - private: - basicGraph * graphref; - bool m_max_degree_computed; - unsigned int m_partition_count; - EdgeWeight m_max_degree; - std::vector m_second_partition_index; + void copy(graph_access & Gcopy); +private: + basicGraph * graphref; + bool m_max_degree_computed; + unsigned int m_partition_count; + EdgeWeight m_max_degree; + std::vector m_second_partition_index; }; /* graph build methods */ @@ -300,49 +300,49 @@ inline void graph_access::setPartitionIndex(NodeID node, PartitionID id) { inline NodeWeight graph_access::getNodeWeight(NodeID node){ #ifdef NDEBUG - return graphref->m_nodes[node].weight; + return graphref->m_nodes[node].weight; #else - return graphref->m_nodes.at(node).weight; + return graphref->m_nodes.at(node).weight; #endif } inline void graph_access::setNodeWeight(NodeID node, NodeWeight weight){ #ifdef NDEBUG - graphref->m_nodes[node].weight = weight; + graphref->m_nodes[node].weight = weight; #else - graphref->m_nodes.at(node).weight = weight; + graphref->m_nodes.at(node).weight = weight; #endif } inline EdgeWeight graph_access::getEdgeWeight(EdgeID edge){ #ifdef NDEBUG - return graphref->m_edges[edge].weight; + return graphref->m_edges[edge].weight; #else - return graphref->m_edges.at(edge).weight; + return graphref->m_edges.at(edge).weight; #endif } inline void graph_access::setEdgeWeight(EdgeID edge, EdgeWeight weight){ #ifdef NDEBUG - graphref->m_edges[edge].weight = weight; + graphref->m_edges[edge].weight = weight; #else - graphref->m_edges.at(edge).weight = weight; + graphref->m_edges.at(edge).weight = weight; #endif } inline NodeID graph_access::getEdgeTarget(EdgeID edge){ #ifdef NDEBUG - return graphref->m_edges[edge].target; + return graphref->m_edges[edge].target; #else - return graphref->m_edges.at(edge).target; + return graphref->m_edges.at(edge).target; #endif } inline EdgeRatingType graph_access::getEdgeRating(EdgeID edge) { #ifdef NDEBUG - return graphref->m_coarsening_edge_props[edge].rating; + return graphref->m_coarsening_edge_props[edge].rating; #else - return graphref->m_coarsening_edge_props.at(edge).rating; + return graphref->m_coarsening_edge_props.at(edge).rating; #endif } @@ -359,10 +359,10 @@ inline EdgeWeight graph_access::getNodeDegree(NodeID node) { } inline EdgeWeight graph_access::getWeightedNodeDegree(NodeID node) { - EdgeWeight degree = 0; - for( unsigned e = graphref->m_nodes[node].firstEdge; e < graphref->m_nodes[node+1].firstEdge; ++e) { - degree += getEdgeWeight(e); - } + EdgeWeight degree = 0; + for( unsigned e = graphref->m_nodes[node].firstEdge; e < graphref->m_nodes[node+1].firstEdge; ++e) { + degree += getEdgeWeight(e); + } return degree; } @@ -496,5 +496,5 @@ inline void graph_access::copy(graph_access & G_bar) { G_bar.finish_construction(); } - +} #endif /* end of include guard: GRAPH_ACCESS_EFRXO4X2 */ diff --git a/parallel/modified_kahip/lib/data_structure/graph_hierarchy.cpp b/parallel/modified_kahip/lib/data_structure/graph_hierarchy.cpp index cde29410..9c1f903d 100644 --- a/parallel/modified_kahip/lib/data_structure/graph_hierarchy.cpp +++ b/parallel/modified_kahip/lib/data_structure/graph_hierarchy.cpp @@ -6,83 +6,84 @@ *****************************************************************************/ #include "graph_hierarchy.h" - -graph_hierarchy::graph_hierarchy() : m_current_coarser_graph(NULL), +namespace kahip::modified { +graph_hierarchy::graph_hierarchy() : m_current_coarser_graph(NULL), m_current_coarse_mapping(NULL){ } graph_hierarchy::~graph_hierarchy() { - for( unsigned i = 0; i < m_to_delete_mappings.size(); i++) { - if(m_to_delete_mappings[i] != NULL) - delete m_to_delete_mappings[i]; - } - - for( unsigned i = 0; i+1 < m_to_delete_hierachies.size(); i++) { - if(m_to_delete_hierachies[i] != NULL) - delete m_to_delete_hierachies[i]; - } + for( unsigned i = 0; i < m_to_delete_mappings.size(); i++) { + if(m_to_delete_mappings[i] != NULL) + delete m_to_delete_mappings[i]; + } + + for( unsigned i = 0; i+1 < m_to_delete_hierachies.size(); i++) { + if(m_to_delete_hierachies[i] != NULL) + delete m_to_delete_hierachies[i]; + } } void graph_hierarchy::push_back(graph_access * G, CoarseMapping * coarse_mapping) { - m_the_graph_hierarchy.push(G); - m_the_mappings.push(coarse_mapping); - m_to_delete_mappings.push_back(coarse_mapping); - m_coarsest_graph = G; + m_the_graph_hierarchy.push(G); + m_the_mappings.push(coarse_mapping); + m_to_delete_mappings.push_back(coarse_mapping); + m_coarsest_graph = G; } graph_access* graph_hierarchy::pop_finer_and_project() { - graph_access* finer = pop_coarsest(); - - CoarseMapping* coarse_mapping = m_the_mappings.top(); // mapps finer to coarser nodes - m_the_mappings.pop(); - - if(finer == m_coarsest_graph) { - m_current_coarser_graph = finer; - finer = pop_coarsest(); - finer->set_partition_count(m_current_coarser_graph->get_partition_count()); - - coarse_mapping = m_the_mappings.top(); - m_the_mappings.pop(); - } - - ASSERT_EQ(m_the_graph_hierarchy.size(), m_the_mappings.size()); - - //perform projection - graph_access& fRef = *finer; - graph_access& cRef = *m_current_coarser_graph; - forall_nodes(fRef, n) { - NodeID coarser_node = (*coarse_mapping)[n]; - PartitionID coarser_partition_id = cRef.getPartitionIndex(coarser_node); - fRef.setPartitionIndex(n, coarser_partition_id); - } endfor - - m_current_coarse_mapping = coarse_mapping; - finer->set_partition_count(m_current_coarser_graph->get_partition_count()); - m_current_coarser_graph = finer; - - return finer; + graph_access* finer = pop_coarsest(); + + CoarseMapping* coarse_mapping = m_the_mappings.top(); // mapps finer to coarser nodes + m_the_mappings.pop(); + + if(finer == m_coarsest_graph) { + m_current_coarser_graph = finer; + finer = pop_coarsest(); + finer->set_partition_count(m_current_coarser_graph->get_partition_count()); + + coarse_mapping = m_the_mappings.top(); + m_the_mappings.pop(); + } + + ASSERT_EQ(m_the_graph_hierarchy.size(), m_the_mappings.size()); + + //perform projection + graph_access& fRef = *finer; + graph_access& cRef = *m_current_coarser_graph; + forall_nodes(fRef, n) { + NodeID coarser_node = (*coarse_mapping)[n]; + PartitionID coarser_partition_id = cRef.getPartitionIndex(coarser_node); + fRef.setPartitionIndex(n, coarser_partition_id); + } endfor + + m_current_coarse_mapping = coarse_mapping; + finer->set_partition_count(m_current_coarser_graph->get_partition_count()); + m_current_coarser_graph = finer; + + return finer; } CoarseMapping * graph_hierarchy::get_mapping_of_current_finer() { - return m_current_coarse_mapping; + return m_current_coarse_mapping; } graph_access* graph_hierarchy::get_coarsest( ) { - return m_coarsest_graph; + return m_coarsest_graph; } graph_access* graph_hierarchy::pop_coarsest( ) { - graph_access* current_coarsest = m_the_graph_hierarchy.top(); - m_the_graph_hierarchy.pop(); - return current_coarsest; + graph_access* current_coarsest = m_the_graph_hierarchy.top(); + m_the_graph_hierarchy.pop(); + return current_coarsest; } bool graph_hierarchy::isEmpty( ) { - ASSERT_EQ(m_the_graph_hierarchy.size(), m_the_mappings.size()); - return m_the_graph_hierarchy.empty(); + ASSERT_EQ(m_the_graph_hierarchy.size(), m_the_mappings.size()); + return m_the_graph_hierarchy.empty(); } unsigned int graph_hierarchy::size() { - return m_the_graph_hierarchy.size(); + return m_the_graph_hierarchy.size(); } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/data_structure/graph_hierarchy.h b/parallel/modified_kahip/lib/data_structure/graph_hierarchy.h index d1195eb8..2a04d493 100644 --- a/parallel/modified_kahip/lib/data_structure/graph_hierarchy.h +++ b/parallel/modified_kahip/lib/data_structure/graph_hierarchy.h @@ -11,32 +11,32 @@ #include #include "graph_access.h" - +namespace kahip::modified { class graph_hierarchy { public: - graph_hierarchy( ); - virtual ~graph_hierarchy(); - - void push_back(graph_access * G, CoarseMapping * coarse_mapping); - - graph_access * pop_finer_and_project(); - graph_access * get_coarsest(); - CoarseMapping * get_mapping_of_current_finer(); - - bool isEmpty(); - unsigned int size(); + graph_hierarchy( ); + virtual ~graph_hierarchy(); + + void push_back(graph_access * G, CoarseMapping * coarse_mapping); + + graph_access * pop_finer_and_project(); + graph_access * get_coarsest(); + CoarseMapping * get_mapping_of_current_finer(); + + bool isEmpty(); + unsigned int size(); private: - //private functions - graph_access * pop_coarsest(); - - std::stack m_the_graph_hierarchy; - std::stack m_the_mappings; - std::vector m_to_delete_mappings; - std::vector m_to_delete_hierachies; - graph_access * m_current_coarser_graph; - graph_access * m_coarsest_graph; - CoarseMapping * m_current_coarse_mapping; + //private functions + graph_access * pop_coarsest(); + + std::stack m_the_graph_hierarchy; + std::stack m_the_mappings; + std::vector m_to_delete_mappings; + std::vector m_to_delete_hierachies; + graph_access * m_current_coarser_graph; + graph_access * m_coarsest_graph; + CoarseMapping * m_current_coarse_mapping; }; - +} #endif /* end of include guard: GRAPH_HIERACHY_UMHG74CO */ diff --git a/parallel/modified_kahip/lib/data_structure/matrix/matrix.h b/parallel/modified_kahip/lib/data_structure/matrix/matrix.h index bee2fd81..85a2771f 100644 --- a/parallel/modified_kahip/lib/data_structure/matrix/matrix.h +++ b/parallel/modified_kahip/lib/data_structure/matrix/matrix.h @@ -7,16 +7,16 @@ #ifndef MATRIX_BHHZ9T7P #define MATRIX_BHHZ9T7P - +namespace kahip::modified { class matrix { public: - matrix(unsigned int dim_x, unsigned int dim_y) {}; - matrix() {}; - virtual ~matrix() {}; + matrix(unsigned int dim_x, unsigned int dim_y) {}; + matrix() {}; + virtual ~matrix() {}; - virtual int get_xy(unsigned int x, unsigned int y) = 0; - virtual void set_xy(unsigned int x, unsigned int y, int value) = 0; + virtual int get_xy(unsigned int x, unsigned int y) = 0; + virtual void set_xy(unsigned int x, unsigned int y, int value) = 0; }; - +} #endif /* end of include guard: MATRIX_BHHZ9T7P */ diff --git a/parallel/modified_kahip/lib/data_structure/matrix/normal_matrix.h b/parallel/modified_kahip/lib/data_structure/matrix/normal_matrix.h index 6c9b4fba..6e536ff2 100644 --- a/parallel/modified_kahip/lib/data_structure/matrix/normal_matrix.h +++ b/parallel/modified_kahip/lib/data_structure/matrix/normal_matrix.h @@ -9,7 +9,7 @@ #define NORMAL_MATRIX_DAUJ4JMM #include "matrix.h" - +namespace kahip::modified { class normal_matrix : public matrix { public: normal_matrix(unsigned int dim_x, unsigned int dim_y, int lazy_init_val = 0) : m_dim_x (dim_x), @@ -42,6 +42,6 @@ class normal_matrix : public matrix { unsigned int m_dim_x, m_dim_y; int m_lazy_init_val; }; - +} #endif /* end of include guard: NORMAL_MATRIX_DAUJ4JMM */ diff --git a/parallel/modified_kahip/lib/data_structure/priority_queues/bucket_pq.h b/parallel/modified_kahip/lib/data_structure/priority_queues/bucket_pq.h index ffe0dd6a..5a2d3513 100644 --- a/parallel/modified_kahip/lib/data_structure/priority_queues/bucket_pq.h +++ b/parallel/modified_kahip/lib/data_structure/priority_queues/bucket_pq.h @@ -12,36 +12,36 @@ #include #include "priority_queue_interface.h" - +namespace kahip::modified { class bucket_pq : public priority_queue_interface { - public: - bucket_pq( const EdgeWeight & gain_span ); - - virtual ~bucket_pq() {}; - - NodeID size(); - void insert(NodeID id, Gain gain); - bool empty(); - - Gain maxValue(); - NodeID maxElement(); - NodeID deleteMax(); - - void decreaseKey(NodeID node, Gain newGain); - void increaseKey(NodeID node, Gain newGain); - - void changeKey(NodeID element, Gain newKey); - Gain getKey(NodeID element); - void deleteNode(NodeID node); - - bool contains(NodeID node); - private: - NodeID m_elements; - EdgeWeight m_gain_span; - unsigned m_max_idx; //points to the non-empty bucket with the largest gain - - std::unordered_map > m_queue_index; - std::vector< std::vector > m_buckets; +public: + bucket_pq( const EdgeWeight & gain_span ); + + virtual ~bucket_pq() {}; + + NodeID size(); + void insert(NodeID id, Gain gain); + bool empty(); + + Gain maxValue(); + NodeID maxElement(); + NodeID deleteMax(); + + void decreaseKey(NodeID node, Gain newGain); + void increaseKey(NodeID node, Gain newGain); + + void changeKey(NodeID element, Gain newKey); + Gain getKey(NodeID element); + void deleteNode(NodeID node); + + bool contains(NodeID node); +private: + NodeID m_elements; + EdgeWeight m_gain_span; + unsigned m_max_idx; //points to the non-empty bucket with the largest gain + + std::unordered_map > m_queue_index; + std::vector< std::vector > m_buckets; }; inline bucket_pq::bucket_pq( const EdgeWeight & gain_span_input ) { @@ -53,16 +53,16 @@ inline bucket_pq::bucket_pq( const EdgeWeight & gain_span_input ) { } inline NodeID bucket_pq::size() { - return m_elements; + return m_elements; } inline void bucket_pq::insert(NodeID node, Gain gain) { unsigned address = gain + m_gain_span; if(address > m_max_idx) { - m_max_idx = address; + m_max_idx = address; } - - m_buckets[address].push_back( node ); + + m_buckets[address].push_back( node ); m_queue_index[node].first = m_buckets[address].size() - 1; //store position m_queue_index[node].second = gain; @@ -70,34 +70,34 @@ inline void bucket_pq::insert(NodeID node, Gain gain) { } inline bool bucket_pq::empty( ) { - return m_elements == 0; + return m_elements == 0; } inline Gain bucket_pq::maxValue( ) { - return m_max_idx - m_gain_span; + return m_max_idx - m_gain_span; } inline NodeID bucket_pq::maxElement( ) { - return m_buckets[m_max_idx].back(); + return m_buckets[m_max_idx].back(); } inline NodeID bucket_pq::deleteMax() { - NodeID node = m_buckets[m_max_idx].back(); - m_buckets[m_max_idx].pop_back(); - m_queue_index.erase(node); - - if( m_buckets[m_max_idx].size() == 0 ) { - //update max_idx - while( m_max_idx != 0 ) { - m_max_idx--; - if(m_buckets[m_max_idx].size() > 0) { - break; - } - } - } - - m_elements--; - return node; + NodeID node = m_buckets[m_max_idx].back(); + m_buckets[m_max_idx].pop_back(); + m_queue_index.erase(node); + + if( m_buckets[m_max_idx].size() == 0 ) { + //update max_idx + while( m_max_idx != 0 ) { + m_max_idx--; + if(m_buckets[m_max_idx].size() > 0) { + break; + } + } + } + + m_elements--; + return node; } inline void bucket_pq::decreaseKey(NodeID node, Gain new_gain) { @@ -150,6 +150,6 @@ inline void bucket_pq::deleteNode(NodeID node) { inline bool bucket_pq::contains(NodeID node) { return m_queue_index.find(node) != m_queue_index.end(); } - +} #endif /* end of include guard: BUCKET_PQ_EM8YJPA9 */ diff --git a/parallel/modified_kahip/lib/data_structure/priority_queues/maxNodeHeap.h b/parallel/modified_kahip/lib/data_structure/priority_queues/maxNodeHeap.h index b72b90ae..3c9b7b45 100644 --- a/parallel/modified_kahip/lib/data_structure/priority_queues/maxNodeHeap.h +++ b/parallel/modified_kahip/lib/data_structure/priority_queues/maxNodeHeap.h @@ -14,82 +14,82 @@ #include #include "data_structure/priority_queues/priority_queue_interface.h" - +namespace kahip::modified { typedef int Key; template < typename Data > class QElement { - public: - QElement( Data data, Key key, int index ) : m_data(data), m_key (key), m_index(index) {}; - virtual ~QElement() {}; +public: + QElement( Data data, Key key, int index ) : m_data(data), m_key (key), m_index(index) {}; + virtual ~QElement() {}; - Data & get_data() { - return m_data; - } + Data & get_data() { + return m_data; + } - void set_data(Data & data) { - m_data = data; - } + void set_data(Data & data) { + m_data = data; + } - Key get_key() { - return m_key; - } + Key get_key() { + return m_key; + } - void set_key(Key key) { - m_key = key; - } + void set_key(Key key) { + m_key = key; + } - int get_index() { - return m_index; - } + int get_index() { + return m_index; + } - void set_index(int index) { - m_index = index; - } + void set_index(int index) { + m_index = index; + } - private: - Data m_data; - Key m_key; - int m_index; // the index of the element in the heap +private: + Data m_data; + Key m_key; + int m_index; // the index of the element in the heap }; class maxNodeHeap : public priority_queue_interface { - public: - - struct Data { - NodeID node; - Data( NodeID node ) : node(node) {}; - }; - - typedef QElement PQElement; - - maxNodeHeap() {}; - virtual ~maxNodeHeap() {}; - - NodeID size(); - bool empty(); - - bool contains(NodeID node); - void insert(NodeID id, Gain gain); - - NodeID deleteMax(); - void deleteNode(NodeID node); - NodeID maxElement(); - Gain maxValue(); - - void decreaseKey(NodeID node, Gain gain); - void increaseKey(NodeID node, Gain gain); - void changeKey(NodeID node, Gain gain); - Gain getKey(NodeID node); - - private: - std::vector< PQElement > m_elements; // elements that contain the data - std::unordered_map m_element_index; // stores index of the node in the m_elements array - std::vector< std::pair > m_heap; // key and index in elements (pointer) - - void siftUp( int pos ); - void siftDown( int pos ); +public: + + struct Data { + NodeID node; + Data( NodeID node ) : node(node) {}; + }; + + typedef QElement PQElement; + + maxNodeHeap() {}; + virtual ~maxNodeHeap() {}; + + NodeID size(); + bool empty(); + + bool contains(NodeID node); + void insert(NodeID id, Gain gain); + + NodeID deleteMax(); + void deleteNode(NodeID node); + NodeID maxElement(); + Gain maxValue(); + + void decreaseKey(NodeID node, Gain gain); + void increaseKey(NodeID node, Gain gain); + void changeKey(NodeID node, Gain gain); + Gain getKey(NodeID node); + +private: + std::vector< PQElement > m_elements; // elements that contain the data + std::unordered_map m_element_index; // stores index of the node in the m_elements array + std::vector< std::pair > m_heap; // key and index in elements (pointer) + + void siftUp( int pos ); + void siftDown( int pos ); }; @@ -128,7 +128,7 @@ inline void maxNodeHeap::siftDown( int pos ) { siftDown(swap_pos); return; - } + } } else if ( lhsChild < (int)m_heap.size()) { if( m_heap[pos].first < m_heap[lhsChild].first) { @@ -149,31 +149,31 @@ inline void maxNodeHeap::siftDown( int pos ) { } inline void maxNodeHeap::siftUp( int pos ) { - if( pos > 0 ) { - int parentPos = (int)(pos-1)/2; - if( m_heap[parentPos].first < m_heap[pos].first) { - //heap condition not fulfulled - std::swap(m_heap[parentPos], m_heap[pos]); + if( pos > 0 ) { + int parentPos = (int)(pos-1)/2; + if( m_heap[parentPos].first < m_heap[pos].first) { + //heap condition not fulfulled + std::swap(m_heap[parentPos], m_heap[pos]); - int element_pos = m_heap[pos].second; - m_elements[element_pos].set_index(pos); + int element_pos = m_heap[pos].second; + m_elements[element_pos].set_index(pos); - // update the heap index in the element - element_pos = m_heap[parentPos].second; - m_elements[element_pos].set_index(parentPos); + // update the heap index in the element + element_pos = m_heap[parentPos].second; + m_elements[element_pos].set_index(parentPos); - siftUp( parentPos ); - } + siftUp( parentPos ); + } - } + } } inline NodeID maxNodeHeap::size() { - return m_heap.size(); + return m_heap.size(); } inline bool maxNodeHeap::empty( ) { - return m_heap.empty(); + return m_heap.empty(); } inline void maxNodeHeap::insert(NodeID node, Gain gain) { @@ -185,11 +185,11 @@ inline void maxNodeHeap::insert(NodeID node, Gain gain) { m_heap.push_back( std::pair< Key, int>(gain, element_index) ); m_element_index[node] = element_index; siftUp( heap_size ); - } + } } inline void maxNodeHeap::deleteNode(NodeID node) { - int element_index = m_element_index[node]; + int element_index = m_element_index[node]; int heap_index = m_elements[element_index].get_index(); m_element_index.erase(node); @@ -244,13 +244,13 @@ inline NodeID maxNodeHeap::deleteMax() { } return node; - } + } return -1; } inline void maxNodeHeap::changeKey(NodeID node, Gain gain) { - Gain old_gain = m_heap[m_elements[m_element_index[node]].get_index()].first; + Gain old_gain = m_heap[m_elements[m_element_index[node]].get_index()].first; if( old_gain > gain ) { decreaseKey(node, gain); } else if ( old_gain < gain ) { @@ -260,7 +260,7 @@ inline void maxNodeHeap::changeKey(NodeID node, Gain gain) { inline void maxNodeHeap::decreaseKey(NodeID node, Gain gain) { ASSERT_TRUE(m_element_index.find(node) != m_element_index.end()); - int queue_idx = m_element_index[node]; + int queue_idx = m_element_index[node]; int heap_idx = m_elements[queue_idx].get_index(); m_elements[queue_idx].set_key(gain); m_heap[heap_idx].first = gain; @@ -269,7 +269,7 @@ inline void maxNodeHeap::decreaseKey(NodeID node, Gain gain) { inline void maxNodeHeap::increaseKey(NodeID node, Gain gain) { ASSERT_TRUE(m_element_index.find(node) != m_element_index.end()); - int queue_idx = m_element_index[node]; + int queue_idx = m_element_index[node]; int heap_idx = m_elements[queue_idx].get_index(); m_elements[queue_idx].set_key(gain); m_heap[heap_idx].first = gain; @@ -277,12 +277,12 @@ inline void maxNodeHeap::increaseKey(NodeID node, Gain gain) { } inline Gain maxNodeHeap::getKey(NodeID node) { - return m_heap[m_elements[m_element_index[node]].get_index()].first; + return m_heap[m_elements[m_element_index[node]].get_index()].first; }; inline bool maxNodeHeap::contains(NodeID node) { - return m_element_index.find(node) != m_element_index.end(); + return m_element_index.find(node) != m_element_index.end(); +} } - #endif diff --git a/parallel/modified_kahip/lib/data_structure/priority_queues/priority_queue_interface.h b/parallel/modified_kahip/lib/data_structure/priority_queues/priority_queue_interface.h index 63cb8fc7..40cfa282 100644 --- a/parallel/modified_kahip/lib/data_structure/priority_queues/priority_queue_interface.h +++ b/parallel/modified_kahip/lib/data_structure/priority_queues/priority_queue_interface.h @@ -9,32 +9,32 @@ #define PRIORITY_QUEUE_INTERFACE_20ZSYG7R #include "definitions.h" - +namespace kahip::modified { class priority_queue_interface { - public: - priority_queue_interface( ) {}; - virtual ~priority_queue_interface() {}; - - /* returns the size of the priority queue */ - virtual NodeID size() = 0; - virtual bool empty() = 0 ; - - virtual void insert(NodeID id, Gain gain) = 0; - - virtual Gain maxValue() = 0; - virtual NodeID maxElement() = 0; - virtual NodeID deleteMax() = 0; - - virtual void decreaseKey(NodeID node, Gain newGain) = 0; - virtual void increaseKey(NodeID node, Gain newKey) = 0; - - virtual void changeKey(NodeID element, Gain newKey) = 0; - virtual Gain getKey(NodeID element) = 0; - virtual void deleteNode(NodeID node) = 0; - virtual bool contains(NodeID node) = 0; +public: + priority_queue_interface( ) {}; + virtual ~priority_queue_interface() {}; + + /* returns the size of the priority queue */ + virtual NodeID size() = 0; + virtual bool empty() = 0 ; + + virtual void insert(NodeID id, Gain gain) = 0; + + virtual Gain maxValue() = 0; + virtual NodeID maxElement() = 0; + virtual NodeID deleteMax() = 0; + + virtual void decreaseKey(NodeID node, Gain newGain) = 0; + virtual void increaseKey(NodeID node, Gain newKey) = 0; + + virtual void changeKey(NodeID element, Gain newKey) = 0; + virtual Gain getKey(NodeID element) = 0; + virtual void deleteNode(NodeID node) = 0; + virtual bool contains(NodeID node) = 0; }; typedef priority_queue_interface refinement_pq; - +} #endif /* end of include guard: PRIORITY_QUEUE_INTERFACE_20ZSYG7R */ diff --git a/parallel/modified_kahip/lib/data_structure/union_find.h b/parallel/modified_kahip/lib/data_structure/union_find.h index 8650f43d..998f2d59 100644 --- a/parallel/modified_kahip/lib/data_structure/union_find.h +++ b/parallel/modified_kahip/lib/data_structure/union_find.h @@ -9,56 +9,56 @@ #define UNION_FIND_H #include - +namespace kahip::modified { // A simple Union-Find datastructure implementation. // This is sometimes also caled "disjoint sets datastructure. class union_find { - public: - union_find(unsigned n) : m_parent(n), m_rank(n), m_n(n) { - for( unsigned i = 0; i < m_parent.size(); i++) { - m_parent[i] = i; - m_rank[i] = 0; - } - }; - inline void Union(unsigned lhs, unsigned rhs) - { - int set_lhs = Find(lhs); - int set_rhs = Find(rhs); - if( set_lhs != set_rhs ) { - if( m_rank[set_lhs] < m_rank[set_rhs]) { - m_parent[set_lhs] = set_rhs; - } else { - m_parent[set_rhs] = set_lhs; - if( m_rank[set_lhs] == m_rank[set_rhs] ) m_rank[set_lhs]++; - } - --m_n; +public: + union_find(unsigned n) : m_parent(n), m_rank(n), m_n(n) { + for( unsigned i = 0; i < m_parent.size(); i++) { + m_parent[i] = i; + m_rank[i] = 0; + } + }; + inline void Union(unsigned lhs, unsigned rhs) + { + int set_lhs = Find(lhs); + int set_rhs = Find(rhs); + if( set_lhs != set_rhs ) { + if( m_rank[set_lhs] < m_rank[set_rhs]) { + m_parent[set_lhs] = set_rhs; + } else { + m_parent[set_rhs] = set_lhs; + if( m_rank[set_lhs] == m_rank[set_rhs] ) m_rank[set_lhs]++; } - }; + --m_n; + } + }; - inline unsigned Find(unsigned element) - { - if( m_parent[element] != element ) { - unsigned retValue = Find( m_parent[element] ); - m_parent[element] = retValue; // path compression - return retValue; - } - return element; - }; + inline unsigned Find(unsigned element) + { + if( m_parent[element] != element ) { + unsigned retValue = Find( m_parent[element] ); + m_parent[element] = retValue; // path compression + return retValue; + } + return element; + }; - // Returns: - // The total number of sets. - inline unsigned n() const - { return m_n; }; + // Returns: + // The total number of sets. + inline unsigned n() const + { return m_n; }; - private: - std::vector< unsigned > m_parent; - std::vector< unsigned > m_rank; +private: + std::vector< unsigned > m_parent; + std::vector< unsigned > m_rank; - // Number of elements in UF data structure. - unsigned m_n; + // Number of elements in UF data structure. + unsigned m_n; }; - +} #endif // ifndef UNION_FIND_H diff --git a/parallel/modified_kahip/lib/definitions.h b/parallel/modified_kahip/lib/definitions.h index 84ead6b5..a4ed893c 100644 --- a/parallel/modified_kahip/lib/definitions.h +++ b/parallel/modified_kahip/lib/definitions.h @@ -15,12 +15,12 @@ #include "limits.h" #include "macros_assertions.h" #include "stdio.h" - +namespace kahip::modified { // allows us to disable most of the output during partitioning #ifdef KAFFPAOUTPUT - #define PRINT(x) x +#define PRINT(x) x #else - #define PRINT(x) do {} while (false); +#define PRINT(x) do {} while (false); #endif /********************************************** @@ -49,94 +49,93 @@ const int ROOT = 0; //for the gpa algorithm struct edge_source_pair { - EdgeID e; - NodeID source; + EdgeID e; + NodeID source; }; struct source_target_pair { - NodeID source; - NodeID target; + NodeID source; + NodeID target; }; //matching array has size (no_of_nodes), so for entry in this table we get the matched neighbor -typedef std::vector CoarseMapping; -typedef std::vector Matching; -typedef std::vector NodePermutationMap; +using CoarseMapping = std::vector; +using Matching = std::vector; +using NodePermutationMap = std::vector; typedef double ImbalanceType; //Coarsening typedef enum { - EXPANSIONSTAR, - EXPANSIONSTAR2, - WEIGHT, - PSEUDOGEOM, - EXPANSIONSTAR2ALGDIST, + EXPANSIONSTAR, + EXPANSIONSTAR2, + WEIGHT, + PSEUDOGEOM, + EXPANSIONSTAR2ALGDIST, } EdgeRating; typedef enum { - PERMUTATION_QUALITY_NONE, - PERMUTATION_QUALITY_FAST, + PERMUTATION_QUALITY_NONE, + PERMUTATION_QUALITY_FAST, PERMUTATION_QUALITY_GOOD } PermutationQuality; typedef enum { - MATCHING_RANDOM, - MATCHING_GPA, + MATCHING_RANDOM, + MATCHING_GPA, MATCHING_RANDOM_GPA, - CLUSTER_COARSENING + CLUSTER_COARSENING } MatchingType; typedef enum { - INITIAL_PARTITIONING_RECPARTITION, + INITIAL_PARTITIONING_RECPARTITION, INITIAL_PARTITIONING_BIPARTITION } InitialPartitioningType; typedef enum { - REFINEMENT_SCHEDULING_FAST, - REFINEMENT_SCHEDULING_ACTIVE_BLOCKS, + REFINEMENT_SCHEDULING_FAST, + REFINEMENT_SCHEDULING_ACTIVE_BLOCKS, REFINEMENT_SCHEDULING_ACTIVE_BLOCKS_REF_KWAY } RefinementSchedulingAlgorithm; typedef enum { - REFINEMENT_TYPE_FM, - REFINEMENT_TYPE_FM_FLOW, + REFINEMENT_TYPE_FM, + REFINEMENT_TYPE_FM_FLOW, REFINEMENT_TYPE_FLOW } RefinementType; typedef enum { - STOP_RULE_SIMPLE, - STOP_RULE_MULTIPLE_K, - STOP_RULE_STRONG + STOP_RULE_SIMPLE, + STOP_RULE_MULTIPLE_K, + STOP_RULE_STRONG } StopRule; typedef enum { - BIPARTITION_BFS, + BIPARTITION_BFS, BIPARTITION_FM } BipartitionAlgorithm ; typedef enum { - KWAY_SIMPLE_STOP_RULE, + KWAY_SIMPLE_STOP_RULE, KWAY_ADAPTIVE_STOP_RULE } KWayStopRule; typedef enum { - COIN_RNDTIE, - COIN_DIFFTIE, - NOCOIN_RNDTIE, - NOCOIN_DIFFTIE + COIN_RNDTIE, + COIN_DIFFTIE, + NOCOIN_RNDTIE, + NOCOIN_DIFFTIE } MLSRule; typedef enum { - CYCLE_REFINEMENT_ALGORITHM_PLAYFIELD, - CYCLE_REFINEMENT_ALGORITHM_ULTRA_MODEL, + CYCLE_REFINEMENT_ALGORITHM_PLAYFIELD, + CYCLE_REFINEMENT_ALGORITHM_ULTRA_MODEL, CYCLE_REFINEMENT_ALGORITHM_ULTRA_MODEL_PLUS } CycleRefinementAlgorithm; typedef enum { - RANDOM_NODEORDERING, - DEGREE_NODEORDERING + RANDOM_NODEORDERING, + DEGREE_NODEORDERING } NodeOrderingType; - - +} #endif diff --git a/parallel/modified_kahip/lib/io/graph_io.cpp b/parallel/modified_kahip/lib/io/graph_io.cpp index 06542f34..db7a7037 100644 --- a/parallel/modified_kahip/lib/io/graph_io.cpp +++ b/parallel/modified_kahip/lib/io/graph_io.cpp @@ -7,7 +7,7 @@ #include #include "graph_io.h" - +namespace kahip::modified { graph_io::graph_io() { } @@ -17,177 +17,177 @@ graph_io::~graph_io() { } int graph_io::writeGraphWeighted(graph_access & G, std::string filename) { - std::ofstream f(filename.c_str()); - f << G.number_of_nodes() << " " << G.number_of_edges()/2 << " 11" << std::endl; - - forall_nodes(G, node) { - f << G.getNodeWeight(node) ; - forall_out_edges(G, e, node) { - f << " " << (G.getEdgeTarget(e)+1) << " " << G.getEdgeWeight(e) ; - } endfor - f << std::endl; - } endfor - - f.close(); - return 0; + std::ofstream f(filename.c_str()); + f << G.number_of_nodes() << " " << G.number_of_edges()/2 << " 11" << std::endl; + + forall_nodes(G, node) { + f << G.getNodeWeight(node) ; + forall_out_edges(G, e, node) { + f << " " << (G.getEdgeTarget(e)+1) << " " << G.getEdgeWeight(e) ; + } endfor + f << std::endl; + } endfor + + f.close(); + return 0; } int graph_io::writeGraph(graph_access & G, std::string filename) { - std::ofstream f(filename.c_str()); - f << G.number_of_nodes() << " " << G.number_of_edges()/2 << std::endl; - - forall_nodes(G, node) { - f << node << " "; - forall_out_edges(G, e, node) { - f << G.getEdgeTarget(e) << " " ; - } endfor - f << std::endl; - } endfor - - f.close(); - return 0; + std::ofstream f(filename.c_str()); + f << G.number_of_nodes() << " " << G.number_of_edges()/2 << std::endl; + + forall_nodes(G, node) { + f << node << " "; + forall_out_edges(G, e, node) { + f << G.getEdgeTarget(e) << " " ; + } endfor + f << std::endl; + } endfor + + f.close(); + return 0; } int graph_io::readPartition(graph_access & G, std::string filename) { - std::string line; - - // open file for reading - std::ifstream in(filename.c_str()); - if (!in) { - std::cerr << "Error opening file" << filename << std::endl; - return 1; - } - - PartitionID max = 0; - forall_nodes(G, node) { - // fetch current line - std::getline(in, line); - if (line[0] == '%') { //Comment - node--; - continue; - } - - // in this line we find the block of Node node - G.setPartitionIndex(node, (PartitionID) atol(line.c_str())); - - if(G.getPartitionIndex(node) > max) - max = G.getPartitionIndex(node); - } endfor - - G.set_partition_count(max+1); - in.close(); - - return 0; + std::string line; + + // open file for reading + std::ifstream in(filename.c_str()); + if (!in) { + std::cerr << "Error opening file" << filename << std::endl; + return 1; + } + + PartitionID max = 0; + forall_nodes(G, node) { + // fetch current line + std::getline(in, line); + if (line[0] == '%') { //Comment + node--; + continue; + } + + // in this line we find the block of Node node + G.setPartitionIndex(node, (PartitionID) atol(line.c_str())); + + if(G.getPartitionIndex(node) > max) + max = G.getPartitionIndex(node); + } endfor + + G.set_partition_count(max+1); + in.close(); + + return 0; } int graph_io::readGraphWeighted(graph_access & G, std::string filename) { - std::string line; - - // open file for reading - std::ifstream in(filename.c_str()); - if (!in) { - std::cerr << "Error opening " << filename << std::endl; - return 1; - } - - long nmbNodes; - long nmbEdges; - - std::getline(in,line); - //skip comments - while( line[0] == '%' ) { - std::getline(in, line); - } - - int ew = 0; - std::stringstream ss(line); - ss >> nmbNodes; - ss >> nmbEdges; - ss >> ew; - - if( 2*nmbEdges > std::numeric_limits::max() || nmbNodes > std::numeric_limits::max()) { - std::cout << "The graph is too large. Currently only 32bit supported!" << std::endl; - exit(0); - } - - bool read_ew = false; - bool read_nw = false; - - if(ew == 1) { - read_ew = true; - } else if (ew == 11) { - read_ew = true; - read_nw = true; - } else if (ew == 10) { - read_nw = true; - } - nmbEdges *= 2; //since we have forward and backward edges - - NodeID node_counter = 0; - EdgeID edge_counter = 0; - - G.start_construction(nmbNodes, nmbEdges); - - while( std::getline(in, line)) { - - if (line[0] == '%') { // a comment in the file - continue; - } - - NodeID node = G.new_node(); node_counter++; - G.setPartitionIndex(node, 0); - - std::stringstream ss(line); - - NodeWeight weight = 1; - if( read_nw ) { - ss >> weight; - } - G.setNodeWeight(node, weight); - - NodeID target; - while( ss >> target ) { - EdgeWeight edge_weight = 1; - if( read_ew ) { - ss >> edge_weight; - } - edge_counter++; - EdgeID e = G.new_edge(node, target-1); - G.setEdgeWeight(e, edge_weight); - } - - if(in.eof()) { - break; - } - } - - if( edge_counter != (EdgeID)nmbEdges ) { - std::cout << "number of specified edges mismatch" << std::endl; - std::cout << edge_counter << " " << nmbEdges << std::endl; - exit(0); - } - - if( node_counter != (NodeID)nmbNodes) { - std::cout << "number of specified nodes mismatch" << std::endl; - std::cout << node_counter << " " << nmbNodes << std::endl; - exit(0); - } - - - G.finish_construction(); - return 0; + std::string line; + + // open file for reading + std::ifstream in(filename.c_str()); + if (!in) { + std::cerr << "Error opening " << filename << std::endl; + return 1; + } + + long nmbNodes; + long nmbEdges; + + std::getline(in,line); + //skip comments + while( line[0] == '%' ) { + std::getline(in, line); + } + + int ew = 0; + std::stringstream ss(line); + ss >> nmbNodes; + ss >> nmbEdges; + ss >> ew; + + if( 2*nmbEdges > std::numeric_limits::max() || nmbNodes > std::numeric_limits::max()) { + std::cout << "The graph is too large. Currently only 32bit supported!" << std::endl; + exit(0); + } + + bool read_ew = false; + bool read_nw = false; + + if(ew == 1) { + read_ew = true; + } else if (ew == 11) { + read_ew = true; + read_nw = true; + } else if (ew == 10) { + read_nw = true; + } + nmbEdges *= 2; //since we have forward and backward edges + + NodeID node_counter = 0; + EdgeID edge_counter = 0; + + G.start_construction(nmbNodes, nmbEdges); + + while( std::getline(in, line)) { + + if (line[0] == '%') { // a comment in the file + continue; + } + + NodeID node = G.new_node(); node_counter++; + G.setPartitionIndex(node, 0); + + std::stringstream ss(line); + + NodeWeight weight = 1; + if( read_nw ) { + ss >> weight; + } + G.setNodeWeight(node, weight); + + NodeID target; + while( ss >> target ) { + EdgeWeight edge_weight = 1; + if( read_ew ) { + ss >> edge_weight; + } + edge_counter++; + EdgeID e = G.new_edge(node, target-1); + G.setEdgeWeight(e, edge_weight); + } + + if(in.eof()) { + break; + } + } + + if( edge_counter != (EdgeID)nmbEdges ) { + std::cout << "number of specified edges mismatch" << std::endl; + std::cout << edge_counter << " " << nmbEdges << std::endl; + exit(0); + } + + if( node_counter != (NodeID)nmbNodes) { + std::cout << "number of specified nodes mismatch" << std::endl; + std::cout << node_counter << " " << nmbNodes << std::endl; + exit(0); + } + + + G.finish_construction(); + return 0; } void graph_io::writePartition(graph_access & G, std::string filename) { - std::ofstream f(filename.c_str()); - std::cout << "writing partition to " << filename << " ... " << std::endl; + std::ofstream f(filename.c_str()); + std::cout << "writing partition to " << filename << " ... " << std::endl; - forall_nodes(G, node) { - f << G.getPartitionIndex(node) << std::endl; - } endfor + forall_nodes(G, node) { + f << G.getPartitionIndex(node) << std::endl; + } endfor - f.close(); + f.close(); +} } - diff --git a/parallel/modified_kahip/lib/io/graph_io.h b/parallel/modified_kahip/lib/io/graph_io.h index 45c452d0..92aeff13 100644 --- a/parallel/modified_kahip/lib/io/graph_io.h +++ b/parallel/modified_kahip/lib/io/graph_io.h @@ -18,32 +18,32 @@ #include "definitions.h" #include "data_structure/graph_access.h" - +namespace kahip::modified { class graph_io { - public: - graph_io(); - virtual ~graph_io () ; +public: + graph_io(); + virtual ~graph_io () ; - static - int readGraphWeighted(graph_access & G, std::string filename); + static + int readGraphWeighted(graph_access & G, std::string filename); - static - int writeGraphWeighted(graph_access & G, std::string filename); + static + int writeGraphWeighted(graph_access & G, std::string filename); - static - int writeGraph(graph_access & G, std::string filename); + static + int writeGraph(graph_access & G, std::string filename); - static - int readPartition(graph_access& G, std::string filename); + static + int readPartition(graph_access& G, std::string filename); - static - void writePartition(graph_access& G, std::string filename); + static + void writePartition(graph_access& G, std::string filename); - template - static void writeVector(std::vector & vec, std::string filename); + template + static void writeVector(std::vector & vec, std::string filename); - template - static void readVector(std::vector & vec, std::string filename); + template + static void readVector(std::vector & vec, std::string filename); }; @@ -84,5 +84,5 @@ void graph_io::readVector(std::vector & vec, std::string filename) { in.close(); } - +} #endif /*GRAPHIO_H_*/ diff --git a/parallel/modified_kahip/lib/parallel_mh/diversifyer.h b/parallel/modified_kahip/lib/parallel_mh/diversifyer.h index e0667cdd..b7e85e03 100644 --- a/parallel/modified_kahip/lib/parallel_mh/diversifyer.h +++ b/parallel/modified_kahip/lib/parallel_mh/diversifyer.h @@ -9,7 +9,7 @@ #define DIVERSIFYER_AZQIF42R #include "random_functions.h" - +namespace kahip::modified { class diversifyer { public: diversifyer() {} ; @@ -29,6 +29,6 @@ class diversifyer { config.kaba_unsucc_iterations = random_functions::nextInt(1, 10); } }; - +} #endif /* end of include guard: DIVERSIFYER_AZQIF42R */ diff --git a/parallel/modified_kahip/lib/parallel_mh/exchange/exchanger.cpp b/parallel/modified_kahip/lib/parallel_mh/exchange/exchanger.cpp index 423733c3..c810a79b 100644 --- a/parallel/modified_kahip/lib/parallel_mh/exchange/exchanger.cpp +++ b/parallel/modified_kahip/lib/parallel_mh/exchange/exchanger.cpp @@ -10,285 +10,285 @@ #include "exchanger.h" #include "tools/quality_metrics.h" #include "tools/random_functions.h" - +namespace kahip::modified { exchanger::exchanger(MPI_Comm communicator) { - m_prev_best_objective = std::numeric_limits::max(); + m_prev_best_objective = std::numeric_limits::max(); - m_communicator = communicator; + m_communicator = communicator; - int rank, comm_size; - MPI_Comm_rank( m_communicator, &rank); - MPI_Comm_size( m_communicator, &comm_size); + int rank, comm_size; + MPI_Comm_rank( m_communicator, &rank); + MPI_Comm_size( m_communicator, &comm_size); - m_cur_num_pushes = 0; - if(comm_size > 2) m_max_num_pushes = ceil(log2(comm_size)); - else m_max_num_pushes = 1; + m_cur_num_pushes = 0; + if(comm_size > 2) m_max_num_pushes = ceil(log2(comm_size)); + else m_max_num_pushes = 1; - std::cout << "max num pushes " << m_max_num_pushes << std::endl; + std::cout << "max num pushes " << m_max_num_pushes << std::endl; - m_allready_send_to.resize(comm_size); + m_allready_send_to.resize(comm_size); - for( unsigned i = 0; i < m_allready_send_to.size(); i++) { - m_allready_send_to[i] = false; - } + for( unsigned i = 0; i < m_allready_send_to.size(); i++) { + m_allready_send_to[i] = false; + } - m_allready_send_to[rank] = true; + m_allready_send_to[rank] = true; } exchanger::~exchanger() { - MPI_Barrier( m_communicator ); - int rank; - MPI_Comm_rank( m_communicator, &rank); - - int flag; MPI_Status st; - MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, m_communicator, &flag, &st); - - while(flag) { - int message_length; - MPI_Get_count(&st, MPI_INT, &message_length); - - int* partition_map = new int[message_length]; - MPI_Status rst; - MPI_Recv( partition_map, message_length, MPI_INT, st.MPI_SOURCE, rank, m_communicator, &rst); - - delete[] partition_map; - MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, m_communicator, &flag, &st); - } + MPI_Barrier( m_communicator ); + int rank; + MPI_Comm_rank( m_communicator, &rank); + + int flag; MPI_Status st; + MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, m_communicator, &flag, &st); + + while(flag) { + int message_length; + MPI_Get_count(&st, MPI_INT, &message_length); + + int* partition_map = new int[message_length]; + MPI_Status rst; + MPI_Recv( partition_map, message_length, MPI_INT, st.MPI_SOURCE, rank, m_communicator, &rst); + + delete[] partition_map; + MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, m_communicator, &flag, &st); + } + + MPI_Barrier( m_communicator ); + for( unsigned i = 0; i < m_request_pointers.size(); i++) { + MPI_Cancel( m_request_pointers[i] ); + } + + for( unsigned i = 0; i < m_request_pointers.size(); i++) { + MPI_Status st; + MPI_Wait( m_request_pointers[i], & st ); + delete[] m_partition_map_buffers[i]; + delete m_request_pointers[i]; + } - MPI_Barrier( m_communicator ); - for( unsigned i = 0; i < m_request_pointers.size(); i++) { - MPI_Cancel( m_request_pointers[i] ); - } - - for( unsigned i = 0; i < m_request_pointers.size(); i++) { - MPI_Status st; - MPI_Wait( m_request_pointers[i], & st ); - delete[] m_partition_map_buffers[i]; - delete m_request_pointers[i]; - } - } void exchanger::diversify_population( PartitionConfig & config, graph_access & G, population & island, bool replace ) { - - int rank, comm_size; - MPI_Comm_rank( m_communicator, &rank); - MPI_Comm_size( m_communicator, &comm_size); - - std::vector permutation(comm_size, 0); - - if( rank == ROOT ) { - random_functions::circular_permutation(permutation); - } - MPI_Bcast(&permutation[0], comm_size, MPI_INT, ROOT, m_communicator); + int rank, comm_size; + MPI_Comm_rank( m_communicator, &rank); + MPI_Comm_size( m_communicator, &comm_size); - int from = 0; - int to = permutation[rank]; - for( unsigned i = 0; i < permutation.size(); i++) { - if( permutation[i] == (unsigned)rank ) { - from = (int)i; - break; - } - } + std::vector permutation(comm_size, 0); - Individuum in; - Individuum out; + if( rank == ROOT ) { + random_functions::circular_permutation(permutation); + } - if(config.mh_diversify_best) { - island.get_best_individuum(in); - } else { - island.get_random_individuum(in); - } - exchange_individum( config, G, from, rank, to, in, out); + MPI_Bcast(&permutation[0], comm_size, MPI_INT, ROOT, m_communicator); - if( replace ) { - island.replace( in, out ); - } else { - island.insert( G, out ); - } + int from = 0; + int to = permutation[rank]; + for( unsigned i = 0; i < permutation.size(); i++) { + if( permutation[i] == (unsigned)rank ) { + from = (int)i; + break; + } + } + + Individuum in; + Individuum out; + + if(config.mh_diversify_best) { + island.get_best_individuum(in); + } else { + island.get_random_individuum(in); + } + exchange_individum( config, G, from, rank, to, in, out); + + if( replace ) { + island.replace( in, out ); + } else { + island.insert( G, out ); + } } void exchanger::quick_start( PartitionConfig & config, graph_access & G, population & island ) { - int comm_size; - MPI_Comm_size( m_communicator, &comm_size); - - unsigned no_of_individuals = ceil(config.mh_pool_size / (double)comm_size) - 1; + int comm_size; + MPI_Comm_size( m_communicator, &comm_size); - std::cout << "creating " << no_of_individuals << std::endl; + unsigned no_of_individuals = ceil(config.mh_pool_size / (double)comm_size) - 1; - for(unsigned i = 0; i < no_of_individuals; i++) { - PartitionConfig copy = config; - copy.combine = false; - copy.graph_allready_partitioned = false; + std::cout << "creating " << no_of_individuals << std::endl; - Individuum ind; - island.createIndividuum(config, G, ind, true); - island.insert(G, ind); - } + for(unsigned i = 0; i < no_of_individuals; i++) { + PartitionConfig copy = config; + copy.combine = false; + copy.graph_allready_partitioned = false; - int reps = config.mh_pool_size - no_of_individuals; - if(reps < 0) reps = 0; + Individuum ind; + island.createIndividuum(config, G, ind, true); + island.insert(G, ind); + } - PartitionConfig div_config = config; - div_config.mh_diversify_best = false; - for( unsigned i = 0; i < (unsigned) reps; i++) { - diversify_population( div_config , G, island, false); - } + int reps = config.mh_pool_size - no_of_individuals; + if(reps < 0) reps = 0; + + PartitionConfig div_config = config; + div_config.mh_diversify_best = false; + for( unsigned i = 0; i < (unsigned) reps; i++) { + diversify_population( div_config , G, island, false); + } } -void exchanger::exchange_individum( const PartitionConfig & config, graph_access & G, - int & from, int & rank, int & to, +void exchanger::exchange_individum( const PartitionConfig & config, graph_access & G, + int & from, int & rank, int & to, Individuum & in, Individuum & out) { - //recv. edge cut, partition_map, cut_edges from "from" - //send in to "to" - - int* partition_map = new int[G.number_of_nodes()]; - out.partition_map = partition_map; - out.cut_edges = new std::vector(); - - MPI_Status st; - MPI_Sendrecv( in.partition_map , G.number_of_nodes(), MPI_INT, to, 0, - out.partition_map, G.number_of_nodes(), MPI_INT, from, 0, m_communicator, &st); - - //recompute cut edges and edge cut locally - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(partition_map[node] != partition_map[target]) { - out.cut_edges->push_back(e); - } - } endfor - } endfor - - out.objective = m_qm.objective(config, G, partition_map); + //recv. edge cut, partition_map, cut_edges from "from" + //send in to "to" + + int* partition_map = new int[G.number_of_nodes()]; + out.partition_map = partition_map; + out.cut_edges = new std::vector(); + + MPI_Status st; + MPI_Sendrecv( in.partition_map , G.number_of_nodes(), MPI_INT, to, 0, + out.partition_map, G.number_of_nodes(), MPI_INT, from, 0, m_communicator, &st); + + //recompute cut edges and edge cut locally + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(partition_map[node] != partition_map[target]) { + out.cut_edges->push_back(e); + } + } endfor +} endfor + +out.objective = m_qm.objective(config, G, partition_map); } //extended push protocol -- see paper for details void exchanger::push_best( PartitionConfig & config, graph_access & G, population & island ) { - int rank, size; - MPI_Comm_rank( m_communicator, &rank); - MPI_Comm_size( m_communicator, &size); - - Individuum best_ind; - island.get_best_individuum(best_ind); - - if( best_ind.objective < m_prev_best_objective) { - m_prev_best_objective = best_ind.objective; - for( unsigned i = 0; i < m_allready_send_to.size(); i++) { - m_allready_send_to[i] = false; - } - - m_allready_send_to[rank] = true; - m_cur_num_pushes = 0; - - std::cout << "rank " << rank - << ": pool improved *************************************** " - << best_ind.objective << std::endl; - } - - bool something_todo = false; - for( unsigned i = 0; i < m_allready_send_to.size(); i++) { - if(!m_allready_send_to[i]) { - something_todo = true; - break; - } - } + int rank, size; + MPI_Comm_rank( m_communicator, &rank); + MPI_Comm_size( m_communicator, &size); + + Individuum best_ind; + island.get_best_individuum(best_ind); + + if( best_ind.objective < m_prev_best_objective) { + m_prev_best_objective = best_ind.objective; + for( unsigned i = 0; i < m_allready_send_to.size(); i++) { + m_allready_send_to[i] = false; + } + + m_allready_send_to[rank] = true; + m_cur_num_pushes = 0; + + std::cout << "rank " << rank + << ": pool improved *************************************** " + << best_ind.objective << std::endl; + } + + bool something_todo = false; + for( unsigned i = 0; i < m_allready_send_to.size(); i++) { + if(!m_allready_send_to[i]) { + something_todo = true; + break; + } + } + + if( m_cur_num_pushes > m_max_num_pushes ) { + something_todo = false; + } + + if(something_todo) { + int* partition_map = new int[G.number_of_nodes()]; + forall_nodes(G, node) { + partition_map[node] = G.getPartitionIndex(node); + } endfor + + int target = rank; + while( target == rank && m_allready_send_to[target]) target = random_functions::nextInt(0, size-1); + + //MPI::Request* request = new MPI::Request; + //*request = MPI::COMM_WORLD.Isend( partition_map, G.number_of_nodes(), MPI_INT, target, target); + MPI_Request* rq = new MPI_Request; + MPI_Isend( partition_map, G.number_of_nodes(), MPI_INT, target, target, m_communicator, rq); + + + m_cur_num_pushes++; + + m_request_pointers.push_back( rq ); + m_partition_map_buffers.push_back( partition_map ); + + m_allready_send_to[target] = true; + } + + for( unsigned i = 0; i < m_request_pointers.size(); i++) { + int finished = 0; + MPI_Status st; + MPI_Test( m_request_pointers[i], &finished, &st); + + if(finished) { + std::swap(m_request_pointers[i], m_request_pointers[m_request_pointers.size()-1]); + std::swap(m_partition_map_buffers[i], m_partition_map_buffers[m_request_pointers.size()-1]); + + delete[] m_partition_map_buffers[m_partition_map_buffers.size() - 1]; + delete m_request_pointers[m_request_pointers.size() - 1]; + + m_partition_map_buffers.pop_back(); + m_request_pointers.pop_back(); + } + } +} - if( m_cur_num_pushes > m_max_num_pushes ) { - something_todo = false; +void exchanger::recv_incoming( PartitionConfig & config, graph_access & G, population & island ) { + int rank; + MPI_Comm_rank( m_communicator, &rank); + + int flag; MPI_Status st; + MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, m_communicator, &flag, &st); + + while(flag) { + Individuum out; + int* partition_map = new int[G.number_of_nodes()]; + out.partition_map = partition_map; + out.cut_edges = new std::vector(); + + MPI_Status rst; + MPI_Recv( out.partition_map, G.number_of_nodes(), MPI_INT, st.MPI_SOURCE, rank, m_communicator, &rst); + + //recompute cut edges and edge cut locally + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(partition_map[node] != partition_map[target]) { + out.cut_edges->push_back(e); } + } endfor +} endfor - if(something_todo) { - int* partition_map = new int[G.number_of_nodes()]; - forall_nodes(G, node) { - partition_map[node] = G.getPartitionIndex(node); - } endfor - - int target = rank; - while( target == rank && m_allready_send_to[target]) target = random_functions::nextInt(0, size-1); +out.objective = m_qm.objective(config, G, partition_map); + island.insert( G, out ); - //MPI::Request* request = new MPI::Request; - //*request = MPI::COMM_WORLD.Isend( partition_map, G.number_of_nodes(), MPI_INT, target, target); - MPI_Request* rq = new MPI_Request; - MPI_Isend( partition_map, G.number_of_nodes(), MPI_INT, target, target, m_communicator, rq); - + if( (unsigned)out.objective < (unsigned)m_prev_best_objective) { + m_prev_best_objective = out.objective; + std::cout << "rank " << rank + << ": pool improved (inc) **************************************** " + << out.objective << std::endl; - m_cur_num_pushes++; + for( unsigned i = 0; i < m_allready_send_to.size(); i++) { + m_allready_send_to[i] = false; + } - m_request_pointers.push_back( rq ); - m_partition_map_buffers.push_back( partition_map ); + m_allready_send_to[rank] = true; + m_cur_num_pushes = 0; + } - m_allready_send_to[target] = true; - } - - for( unsigned i = 0; i < m_request_pointers.size(); i++) { - int finished = 0; - MPI_Status st; - MPI_Test( m_request_pointers[i], &finished, &st); - - if(finished) { - std::swap(m_request_pointers[i], m_request_pointers[m_request_pointers.size()-1]); - std::swap(m_partition_map_buffers[i], m_partition_map_buffers[m_request_pointers.size()-1]); + m_allready_send_to[st.MPI_SOURCE] = true; // we dont need to send it back - saves us P * 1 messages of length n - delete[] m_partition_map_buffers[m_partition_map_buffers.size() - 1]; - delete m_request_pointers[m_request_pointers.size() - 1]; - - m_partition_map_buffers.pop_back(); - m_request_pointers.pop_back(); - } - } + MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, m_communicator, &flag, &st); + } } - -void exchanger::recv_incoming( PartitionConfig & config, graph_access & G, population & island ) { - int rank; - MPI_Comm_rank( m_communicator, &rank); - - int flag; MPI_Status st; - MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, m_communicator, &flag, &st); - - while(flag) { - Individuum out; - int* partition_map = new int[G.number_of_nodes()]; - out.partition_map = partition_map; - out.cut_edges = new std::vector(); - - MPI_Status rst; - MPI_Recv( out.partition_map, G.number_of_nodes(), MPI_INT, st.MPI_SOURCE, rank, m_communicator, &rst); - - //recompute cut edges and edge cut locally - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(partition_map[node] != partition_map[target]) { - out.cut_edges->push_back(e); - } - } endfor - } endfor - - out.objective = m_qm.objective(config, G, partition_map); - island.insert( G, out ); - - if( (unsigned)out.objective < (unsigned)m_prev_best_objective) { - m_prev_best_objective = out.objective; - std::cout << "rank " << rank - << ": pool improved (inc) **************************************** " - << out.objective << std::endl; - - for( unsigned i = 0; i < m_allready_send_to.size(); i++) { - m_allready_send_to[i] = false; - } - - m_allready_send_to[rank] = true; - m_cur_num_pushes = 0; - } - - m_allready_send_to[st.MPI_SOURCE] = true; // we dont need to send it back - saves us P * 1 messages of length n - - MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, m_communicator, &flag, &st); - } } - diff --git a/parallel/modified_kahip/lib/parallel_mh/exchange/exchanger.h b/parallel/modified_kahip/lib/parallel_mh/exchange/exchanger.h index 202fad79..f625850a 100644 --- a/parallel/modified_kahip/lib/parallel_mh/exchange/exchanger.h +++ b/parallel/modified_kahip/lib/parallel_mh/exchange/exchanger.h @@ -14,38 +14,38 @@ #include "parallel_mh/population.h" #include "partition_config.h" #include "tools/quality_metrics.h" - +namespace kahip::modified { class exchanger { public: - exchanger( MPI_Comm communicator ); - virtual ~exchanger(); + exchanger( MPI_Comm communicator ); + virtual ~exchanger(); - void diversify_population( PartitionConfig & config, graph_access & G, population & island, bool replace ); - void quick_start( PartitionConfig & config, graph_access & G, population & island ); - void push_best( PartitionConfig & config, graph_access & G, population & island ); - void recv_incoming( PartitionConfig & config, graph_access & G, population & island ); + void diversify_population( PartitionConfig & config, graph_access & G, population & island, bool replace ); + void quick_start( PartitionConfig & config, graph_access & G, population & island ); + void push_best( PartitionConfig & config, graph_access & G, population & island ); + void recv_incoming( PartitionConfig & config, graph_access & G, population & island ); private: - void exchange_individum(const PartitionConfig & config, - graph_access & G, - int & from, - int & rank, - int & to, - Individuum & in, Individuum & out); + void exchange_individum(const PartitionConfig & config, + graph_access & G, + int & from, + int & rank, + int & to, + Individuum & in, Individuum & out); - std::vector< int* > m_partition_map_buffers; - std::vector< MPI_Request* > m_request_pointers; - std::vector m_allready_send_to; + std::vector< int* > m_partition_map_buffers; + std::vector< MPI_Request* > m_request_pointers; + std::vector m_allready_send_to; - int m_prev_best_objective; - int m_max_num_pushes; - int m_cur_num_pushes; + int m_prev_best_objective; + int m_max_num_pushes; + int m_cur_num_pushes; - MPI_Comm m_communicator; + MPI_Comm m_communicator; - quality_metrics m_qm; + quality_metrics m_qm; }; - +} #endif /* end of include guard: EXCHANGER_YPB6QKNL */ diff --git a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.cpp b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.cpp index 8b117cab..2ba9f2bf 100644 --- a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.cpp +++ b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.cpp @@ -15,7 +15,7 @@ #include "uncoarsening/refinement/refinement.h" #include "uncoarsening/refinement/tabu_search/tabu_search.h" - +namespace kahip::modified { construct_partition::construct_partition() { } @@ -25,128 +25,129 @@ construct_partition::~construct_partition() { } void construct_partition::construct_starting_from_partition( PartitionConfig & config, graph_access & G) { - std::vector< std::queue< NodeID > > queues(config.k); - std::vector< NodeID > unassigned_vertices; - std::vector< std::vector< NodeID > > blocks(config.k); - - forall_nodes(G, node) { - if( G.getPartitionIndex(node) == config.k ) { - unassigned_vertices.push_back(node); - } else { - blocks[G.getPartitionIndex(node)].push_back(node); - } - } endfor - - - //shuffle the blocks - std::vector< NodeWeight > block_weights(config.k, 0); - for( unsigned block = 0; block < config.k; block++) { - random_functions::permutate_vector_good(blocks[block], false); - block_weights[block] = blocks[block].size(); - } - - for( unsigned block = 0; block < config.k; block++) { - for( unsigned j = 0; j < blocks[block].size(); j++) { - NodeID node = blocks[block][j]; - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(G.getPartitionIndex(target) == config.k) {//unassigned - queues[block].push(target); - } - } endfor - } - } - - unsigned no_unassigned = unassigned_vertices.size(); - while(no_unassigned > 0) { - PartitionID block = 0; - NodeWeight min_weight = G.number_of_nodes(); - for( unsigned cur_block = 0; cur_block < config.k; cur_block++) { - if( block_weights[cur_block] < min_weight) { - block = cur_block; - min_weight = block_weights[cur_block]; - } - } - - if( queues[block].size() != 0) { - NodeID front_node = queues[block].front(); - queues[block].pop(); - if(G.getPartitionIndex(front_node) == config.k) { //safe to assign - G.setPartitionIndex(front_node, block); - block_weights[block]++; - no_unassigned--; - forall_out_edges(G, e, front_node) { - NodeID target = G.getEdgeTarget(e); - if(G.getPartitionIndex(target) == config.k) { - queues[block].push(target); - } - } endfor - } - } else { - if( unassigned_vertices.size() > 0) { - NodeID node = unassigned_vertices[0]; - do { - unsigned idx = random_functions::nextInt(0, unassigned_vertices.size()-1); - node = unassigned_vertices[idx]; - if( G.getPartitionIndex(node) != config.k ) { - std::swap(unassigned_vertices[idx], - unassigned_vertices[unassigned_vertices.size() -1]); - - unassigned_vertices.pop_back(); - } else { - queues[block].push(node); - break; - } - } while( unassigned_vertices.size() != 0 ); - } - } - - - } + std::vector< std::queue< NodeID > > queues(config.k); + std::vector< NodeID > unassigned_vertices; + std::vector< std::vector< NodeID > > blocks(config.k); + + forall_nodes(G, node) { + if( G.getPartitionIndex(node) == config.k ) { + unassigned_vertices.push_back(node); + } else { + blocks[G.getPartitionIndex(node)].push_back(node); + } + } endfor + + + //shuffle the blocks + std::vector< NodeWeight > block_weights(config.k, 0); + for( unsigned block = 0; block < config.k; block++) { + random_functions::permutate_vector_good(blocks[block], false); + block_weights[block] = blocks[block].size(); + } + + for( unsigned block = 0; block < config.k; block++) { + for( unsigned j = 0; j < blocks[block].size(); j++) { + NodeID node = blocks[block][j]; + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(G.getPartitionIndex(target) == config.k) {//unassigned + queues[block].push(target); + } + } endfor + } + } + + unsigned no_unassigned = unassigned_vertices.size(); + while(no_unassigned > 0) { + PartitionID block = 0; + NodeWeight min_weight = G.number_of_nodes(); + for( unsigned cur_block = 0; cur_block < config.k; cur_block++) { + if( block_weights[cur_block] < min_weight) { + block = cur_block; + min_weight = block_weights[cur_block]; + } + } + + if( queues[block].size() != 0) { + NodeID front_node = queues[block].front(); + queues[block].pop(); + if(G.getPartitionIndex(front_node) == config.k) { //safe to assign + G.setPartitionIndex(front_node, block); + block_weights[block]++; + no_unassigned--; + forall_out_edges(G, e, front_node) { + NodeID target = G.getEdgeTarget(e); + if(G.getPartitionIndex(target) == config.k) { + queues[block].push(target); + } + } endfor + } + } else { + if( unassigned_vertices.size() > 0) { + NodeID node = unassigned_vertices[0]; + do { + unsigned idx = random_functions::nextInt(0, unassigned_vertices.size()-1); + node = unassigned_vertices[idx]; + if( G.getPartitionIndex(node) != config.k ) { + std::swap(unassigned_vertices[idx], + unassigned_vertices[unassigned_vertices.size() -1]); + + unassigned_vertices.pop_back(); + } else { + queues[block].push(node); + break; + } + } while( unassigned_vertices.size() != 0 ); + } + } + + + } } void construct_partition::createIndividuum( PartitionConfig & config, graph_access & G, Individuum & ind, bool output) { - std::cout << "creating individuum " << std::endl; - forall_nodes(G, node) { - G.setPartitionIndex(node, config.k); - } endfor - - for( unsigned block = 0; block < config.k; block++) { - NodeID node = 0; - do { - node = random_functions::nextInt(0, G.number_of_nodes() - 1); - } while( G.getPartitionIndex(node) != config.k ); - - G.setPartitionIndex(node, block); - } - - construct_starting_from_partition( config, G); - - complete_boundary boundary(&G); - boundary.build(); - - tabu_search ts; - PartitionConfig copy = config; - copy.maxIter = G.number_of_nodes(); - - ts.perform_refinement( copy, G, boundary); - - int* partition_map = new int[G.number_of_nodes()]; - forall_nodes(G, node) { - partition_map[node] = G.getPartitionIndex(node); - } endfor - - quality_metrics qm; - ind.objective = qm.objective(config, G, partition_map); - ind.partition_map = partition_map; - ind.cut_edges = new std::vector(); - - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(partition_map[node] != partition_map[target]) { - ind.cut_edges->push_back(e); - } - } endfor - } endfor + std::cout << "creating individuum " << std::endl; + forall_nodes(G, node) { + G.setPartitionIndex(node, config.k); + } endfor + + for( unsigned block = 0; block < config.k; block++) { + NodeID node = 0; + do { + node = random_functions::nextInt(0, G.number_of_nodes() - 1); + } while( G.getPartitionIndex(node) != config.k ); + + G.setPartitionIndex(node, block); + } + + construct_starting_from_partition( config, G); + + complete_boundary boundary(&G); + boundary.build(); + + tabu_search ts; + PartitionConfig copy = config; + copy.maxIter = G.number_of_nodes(); + + ts.perform_refinement( copy, G, boundary); + + int* partition_map = new int[G.number_of_nodes()]; + forall_nodes(G, node) { + partition_map[node] = G.getPartitionIndex(node); + } endfor + + quality_metrics qm; + ind.objective = qm.objective(config, G, partition_map); + ind.partition_map = partition_map; + ind.cut_edges = new std::vector(); + + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(partition_map[node] != partition_map[target]) { + ind.cut_edges->push_back(e); + } + } endfor +} endfor } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.h b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.h index 6a464d4a..a56b281d 100644 --- a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.h +++ b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.h @@ -11,16 +11,16 @@ #include "data_structure/graph_access.h" #include "parallel_mh/population.h" #include "partition_config.h" - +namespace kahip::modified { class construct_partition { public: - construct_partition(); - virtual ~construct_partition(); + construct_partition(); + virtual ~construct_partition(); - void construct_starting_from_partition( PartitionConfig & config, graph_access & G); - void createIndividuum( PartitionConfig & config, graph_access & G, - Individuum & ind, bool output); + void construct_starting_from_partition( PartitionConfig & config, graph_access & G); + void createIndividuum( PartitionConfig & config, graph_access & G, + Individuum & ind, bool output); }; - +} #endif /* end of include guard: CONSTRUCT_PARTITION_E86DQF5S */ diff --git a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.cpp b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.cpp index 5fb015be..d60461bc 100644 --- a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.cpp +++ b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.cpp @@ -17,7 +17,7 @@ #include "uncoarsening/refinement/mixed_refinement.h" #include "uncoarsening/refinement/refinement.h" #include "uncoarsening/refinement/tabu_search/tabu_search.h" - +namespace kahip::modified { gal_combine::gal_combine() { } @@ -32,83 +32,84 @@ gal_combine::~gal_combine() { // extend to partition // apply our refinements and tabu search void gal_combine::perform_gal_combine( PartitionConfig & config, graph_access & G) { - //first greedily compute a matching of the partitions - std::vector< std::unordered_map > counters(config.k); - forall_nodes(G, node) { - //boundary_pair bp; - if(counters[G.getPartitionIndex(node)].find(G.getSecondPartitionIndex(node)) != counters[G.getPartitionIndex(node)].end()) { - counters[G.getPartitionIndex(node)][G.getSecondPartitionIndex(node)] += 1; - } else { - counters[G.getPartitionIndex(node)][G.getSecondPartitionIndex(node)] = 1; - } - } endfor - - std::vector< PartitionID > permutation(config.k); - for( unsigned i = 0; i < permutation.size(); i++) { - permutation[i] = i; - } - - random_functions::permutate_vector_good_small(permutation); - std::vector rhs_matched(config.k, false); - std::vector bipartite_matching(config.k); - for( unsigned i = 0; i < permutation.size(); i++) { - PartitionID cur_partition = permutation[i]; - PartitionID best_unassigned = config.k; - NodeWeight best_value = 0; - - for( std::unordered_map::iterator it = counters[cur_partition].begin(); - it != counters[cur_partition].end(); ++it) { - if( rhs_matched[it->first] == false && it->second > best_value ) { - best_unassigned = it->first; - best_value = it->second; - } - } - - bipartite_matching[cur_partition] = best_unassigned; - if( best_unassigned != config.k ) { - rhs_matched[best_unassigned] = true; - } - } - - std::vector blocked_vertices(G.number_of_nodes(), false); - forall_nodes(G, node) { - if( bipartite_matching[G.getPartitionIndex(node)] == G.getSecondPartitionIndex(node) ){ - blocked_vertices[node] = true; - } else { - // we will reassign this vertex since the partitions do not agree on it - G.setPartitionIndex(node, config.k); - } - } endfor - - construct_partition cp; - cp.construct_starting_from_partition( config, G ); - - refinement* refine = new mixed_refinement(); - - double real_epsilon = config.imbalance/100.0; - double epsilon = random_functions::nextDouble(real_epsilon+0.005,real_epsilon+config.kabaE_internal_bal); - PartitionConfig copy = config; - copy.upper_bound_partition = (1+epsilon)*ceil(config.largest_graph_weight/(double)config.k); - - complete_boundary boundary(&G); - boundary.build(); - - tabu_search ts; - ts.perform_refinement( copy, G, boundary); - - //now obtain the quotient graph - complete_boundary boundary2(&G); - boundary2.build(); - - copy = config; - copy.upper_bound_partition = (1+epsilon)*ceil(config.largest_graph_weight/(double)config.k); - - refine->perform_refinement( copy, G, boundary2); - - copy = config; - cycle_refinement cr; - cr.perform_refinement(config, G, boundary2); - delete refine; + //first greedily compute a matching of the partitions + std::vector< std::unordered_map > counters(config.k); + forall_nodes(G, node) { + //boundary_pair bp; + if(counters[G.getPartitionIndex(node)].find(G.getSecondPartitionIndex(node)) != counters[G.getPartitionIndex(node)].end()) { + counters[G.getPartitionIndex(node)][G.getSecondPartitionIndex(node)] += 1; + } else { + counters[G.getPartitionIndex(node)][G.getSecondPartitionIndex(node)] = 1; + } + } endfor + + std::vector< PartitionID > permutation(config.k); + for( unsigned i = 0; i < permutation.size(); i++) { + permutation[i] = i; + } + + random_functions::permutate_vector_good_small(permutation); + std::vector rhs_matched(config.k, false); + std::vector bipartite_matching(config.k); + for( unsigned i = 0; i < permutation.size(); i++) { + PartitionID cur_partition = permutation[i]; + PartitionID best_unassigned = config.k; + NodeWeight best_value = 0; + + for( std::unordered_map::iterator it = counters[cur_partition].begin(); + it != counters[cur_partition].end(); ++it) { + if( rhs_matched[it->first] == false && it->second > best_value ) { + best_unassigned = it->first; + best_value = it->second; + } + } + + bipartite_matching[cur_partition] = best_unassigned; + if( best_unassigned != config.k ) { + rhs_matched[best_unassigned] = true; + } + } + + std::vector blocked_vertices(G.number_of_nodes(), false); + forall_nodes(G, node) { + if( bipartite_matching[G.getPartitionIndex(node)] == G.getSecondPartitionIndex(node) ){ + blocked_vertices[node] = true; + } else { + // we will reassign this vertex since the partitions do not agree on it + G.setPartitionIndex(node, config.k); + } + } endfor + + construct_partition cp; + cp.construct_starting_from_partition( config, G ); + + refinement* refine = new mixed_refinement(); + + double real_epsilon = config.imbalance/100.0; + double epsilon = random_functions::nextDouble(real_epsilon+0.005,real_epsilon+config.kabaE_internal_bal); + PartitionConfig copy = config; + copy.upper_bound_partition = (1+epsilon)*ceil(config.largest_graph_weight/(double)config.k); + + complete_boundary boundary(&G); + boundary.build(); + + tabu_search ts; + ts.perform_refinement( copy, G, boundary); + + //now obtain the quotient graph + complete_boundary boundary2(&G); + boundary2.build(); + + copy = config; + copy.upper_bound_partition = (1+epsilon)*ceil(config.largest_graph_weight/(double)config.k); + + refine->perform_refinement( copy, G, boundary2); + + copy = config; + cycle_refinement cr; + cr.perform_refinement(config, G, boundary2); + delete refine; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.h b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.h index 58d0005f..ff6006ce 100644 --- a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.h +++ b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.h @@ -10,14 +10,14 @@ #include "partition_config.h" #include "data_structure/graph_access.h" - +namespace kahip::modified { class gal_combine { public: - gal_combine(); - virtual ~gal_combine(); + gal_combine(); + virtual ~gal_combine(); - void perform_gal_combine( PartitionConfig & config, graph_access & G); + void perform_gal_combine( PartitionConfig & config, graph_access & G); }; - +} #endif /* end of include guard: GAL_COMBINE_XDMU5YB7 */ diff --git a/parallel/modified_kahip/lib/parallel_mh/parallel_mh_async.cpp b/parallel/modified_kahip/lib/parallel_mh/parallel_mh_async.cpp index 9b34817b..e5d19463 100644 --- a/parallel/modified_kahip/lib/parallel_mh/parallel_mh_async.cpp +++ b/parallel/modified_kahip/lib/parallel_mh/parallel_mh_async.cpp @@ -20,310 +20,310 @@ #include "parallel_mh_async.h" #include "quality_metrics.h" #include "random_functions.h" - +namespace kahip::modified { parallel_mh_async::parallel_mh_async(): MASTER(0), m_time_limit(0) { - m_communicator = MPI_COMM_WORLD; - m_best_global_objective = std::numeric_limits::max(); - m_best_cycle_objective = std::numeric_limits::max(); - m_rounds = 0; - m_termination = false; - MPI_Comm_rank( m_communicator, &m_rank); - MPI_Comm_size( m_communicator, &m_size); + m_communicator = MPI_COMM_WORLD; + m_best_global_objective = std::numeric_limits::max(); + m_best_cycle_objective = std::numeric_limits::max(); + m_rounds = 0; + m_termination = false; + MPI_Comm_rank( m_communicator, &m_rank); + MPI_Comm_size( m_communicator, &m_size); } parallel_mh_async::parallel_mh_async(MPI_Comm communicator) : MASTER(0), m_time_limit(0) { - m_best_global_objective = std::numeric_limits::max(); - m_best_cycle_objective = std::numeric_limits::max(); - m_rounds = 0; - m_termination = false; - m_communicator = communicator; - MPI_Comm_rank( m_communicator, &m_rank); - MPI_Comm_size( m_communicator, &m_size); - + m_best_global_objective = std::numeric_limits::max(); + m_best_cycle_objective = std::numeric_limits::max(); + m_rounds = 0; + m_termination = false; + m_communicator = communicator; + MPI_Comm_rank( m_communicator, &m_rank); + MPI_Comm_size( m_communicator, &m_size); + } parallel_mh_async::~parallel_mh_async() { - delete[] m_best_global_map; + delete[] m_best_global_map; } void parallel_mh_async::perform_partitioning(const PartitionConfig & partition_config, graph_access & G) { - m_time_limit = partition_config.time_limit; - m_island = new population(m_communicator, partition_config); - m_best_global_map = new PartitionID[G.number_of_nodes()]; - - srand(partition_config.seed*m_size+m_rank); - random_functions::setSeed(partition_config.seed*m_size+m_rank); - - PartitionConfig ini_working_config = partition_config; - initialize( ini_working_config, G); - - m_t.restart(); - if( !partition_config.ultra_fast_kaffpaE_interfacecall ) { - exchanger ex(m_communicator); - do { - PartitionConfig working_config = partition_config; - - working_config.graph_allready_partitioned = false; - if(!partition_config.strong) - working_config.no_new_initial_partitioning = false; - - working_config.mh_pool_size = ini_working_config.mh_pool_size; - if(m_rounds == 0 && working_config.mh_enable_quickstart) { - ex.quick_start( working_config, G, *m_island ); - } - - perform_local_partitioning( working_config, G ); - if(m_rank == ROOT) { - std::cout << "t left " << (m_time_limit - m_t.elapsed()) << std::endl; - } - - //push and recv - if( m_t.elapsed() <= m_time_limit && m_size > 1) { - unsigned messages = ceil(log(m_size)); - for( unsigned i = 0; i < messages; i++) { - ex.push_best( working_config, G, *m_island ); - ex.recv_incoming( working_config, G, *m_island ); - } - } - - m_rounds++; - } while( m_t.elapsed() <= m_time_limit ); + m_time_limit = partition_config.time_limit; + m_island = new population(m_communicator, partition_config); + m_best_global_map = new PartitionID[G.number_of_nodes()]; + + srand(partition_config.seed*m_size+m_rank); + random_functions::setSeed(partition_config.seed*m_size+m_rank); + + PartitionConfig ini_working_config = partition_config; + initialize( ini_working_config, G); + + m_t.restart(); + if( !partition_config.ultra_fast_kaffpaE_interfacecall ) { + exchanger ex(m_communicator); + do { + PartitionConfig working_config = partition_config; + + working_config.graph_allready_partitioned = false; + if(!partition_config.strong) + working_config.no_new_initial_partitioning = false; + + working_config.mh_pool_size = ini_working_config.mh_pool_size; + if(m_rounds == 0 && working_config.mh_enable_quickstart) { + ex.quick_start( working_config, G, *m_island ); + } + + perform_local_partitioning( working_config, G ); + if(m_rank == ROOT) { + std::cout << "t left " << (m_time_limit - m_t.elapsed()) << std::endl; + } + + //push and recv + if( m_t.elapsed() <= m_time_limit && m_size > 1) { + unsigned messages = ceil(log(m_size)); + for( unsigned i = 0; i < messages; i++) { + ex.push_best( working_config, G, *m_island ); + ex.recv_incoming( working_config, G, *m_island ); } + } - collect_best_partitioning(G, partition_config); - m_island->print(); + m_rounds++; + } while( m_t.elapsed() <= m_time_limit ); + } - //print logfile (for convergence plots) - if( partition_config.mh_print_log ) { - std::stringstream filename_stream; - filename_stream << "log_"<< partition_config.graph_filename << - "_m_rank_" << m_rank << - "_file_" << - "_seed_" << partition_config.seed << - "_k_" << partition_config.k; + collect_best_partitioning(G, partition_config); + m_island->print(); - std::string filename(filename_stream.str()); - m_island->write_log(filename); - } + //print logfile (for convergence plots) + if( partition_config.mh_print_log ) { + std::stringstream filename_stream; + filename_stream << "log_"<< partition_config.graph_filename << + "_m_rank_" << m_rank << + "_file_" << + "_seed_" << partition_config.seed << + "_k_" << partition_config.k; + + std::string filename(filename_stream.str()); + m_island->write_log(filename); + } - delete m_island; + delete m_island; } void parallel_mh_async::initialize(PartitionConfig & working_config, graph_access & G) { - // each PE performs a partitioning - // estimate the runtime of a partitioner call - // calculate the poolsize and async Bcast the poolsize. - // recv. has to be sync - Individuum first_one; - m_t.restart(); - if( !working_config.mh_easy_construction) { - m_island->createIndividuum( working_config, G, first_one, true); - } else { - construct_partition cp; - cp.createIndividuum( working_config, G, first_one, true); - std::cout << "created with objective " << first_one.objective << std::endl; - } + // each PE performs a partitioning + // estimate the runtime of a partitioner call + // calculate the poolsize and async Bcast the poolsize. + // recv. has to be sync + Individuum first_one; + m_t.restart(); + if( !working_config.mh_easy_construction) { + m_island->createIndividuum( working_config, G, first_one, true); + } else { + construct_partition cp; + cp.createIndividuum( working_config, G, first_one, true); + std::cout << "created with objective " << first_one.objective << std::endl; + } + + double time_spend = m_t.elapsed(); + m_island->insert(G, first_one); + + if( working_config.ultra_fast_kaffpaE_interfacecall ) { + working_config.mh_pool_size = 1; + m_island->set_pool_size(1); + return; + } + + + //compute S and Bcast + int population_size = 1; + double fraction = working_config.mh_initial_population_fraction; + int POPSIZE_TAG = 10; + + if( m_rank == ROOT ) { + double fraction_to_spend_for_IP = (double)m_time_limit / fraction; + population_size = ceil(fraction_to_spend_for_IP / time_spend); + + for( int target = 1; target < m_size; target++) { + MPI_Request rq; + MPI_Isend(&population_size, 1, MPI_INT, target, POPSIZE_TAG, m_communicator, &rq); + } + } else { + MPI_Status rst; + MPI_Recv(&population_size, 1, MPI_INT, ROOT, POPSIZE_TAG, m_communicator, &rst); + } + + population_size = std::max(3, population_size); + if(working_config.mh_easy_construction) { + population_size = std::min(50, population_size); + } else { + population_size = std::min(100, population_size); + } + std::cout << "poolsize = " << population_size << std::endl; + + //set S + m_island->set_pool_size(population_size); + working_config.mh_pool_size = population_size; - double time_spend = m_t.elapsed(); - m_island->insert(G, first_one); +} - if( working_config.ultra_fast_kaffpaE_interfacecall ) { - working_config.mh_pool_size = 1; - m_island->set_pool_size(1); - return; - } +EdgeWeight parallel_mh_async::collect_best_partitioning(graph_access & G, const PartitionConfig & config) { + //perform partitioning locally + EdgeWeight min_objective = 0; + m_island->apply_fittest(G, min_objective); + int best_local_objective = min_objective; + int best_local_objective_m = min_objective; + int best_global_objective = 0; - //compute S and Bcast - int population_size = 1; - double fraction = working_config.mh_initial_population_fraction; - int POPSIZE_TAG = 10; + PartitionID* best_local_map = new PartitionID[G.number_of_nodes()]; + std::vector< NodeWeight > block_sizes(G.get_partition_count(),0); - if( m_rank == ROOT ) { - double fraction_to_spend_for_IP = (double)m_time_limit / fraction; - population_size = ceil(fraction_to_spend_for_IP / time_spend); - - for( int target = 1; target < m_size; target++) { - MPI_Request rq; - MPI_Isend(&population_size, 1, MPI_INT, target, POPSIZE_TAG, m_communicator, &rq); - } - } else { - MPI_Status rst; - MPI_Recv(&population_size, 1, MPI_INT, ROOT, POPSIZE_TAG, m_communicator, &rst); - } + forall_nodes(G, node) { + best_local_map[node] = G.getPartitionIndex(node); + block_sizes[G.getPartitionIndex(node)]++; + } endfor - population_size = std::max(3, population_size); - if(working_config.mh_easy_construction) { - population_size = std::min(50, population_size); - } else { - population_size = std::min(100, population_size); - } - std::cout << "poolsize = " << population_size << std::endl; +NodeWeight max_domain_weight = 0; + for( unsigned i = 0; i < G.get_partition_count(); i++) { + if( block_sizes[i] > max_domain_weight ) { + max_domain_weight = block_sizes[i]; + } + } - //set S - m_island->set_pool_size(population_size); - working_config.mh_pool_size = population_size; + if( max_domain_weight > config.upper_bound_partition ) { + best_local_objective_m = std::numeric_limits< int >::max(); + } -} + MPI_Allreduce(&best_local_objective_m, &best_global_objective, 1, MPI_INT, MPI_MIN, m_communicator); -EdgeWeight parallel_mh_async::collect_best_partitioning(graph_access & G, const PartitionConfig & config) { - //perform partitioning locally - EdgeWeight min_objective = 0; - m_island->apply_fittest(G, min_objective); - - int best_local_objective = min_objective; - int best_local_objective_m = min_objective; - int best_global_objective = 0; - - PartitionID* best_local_map = new PartitionID[G.number_of_nodes()]; - std::vector< NodeWeight > block_sizes(G.get_partition_count(),0); - - forall_nodes(G, node) { - best_local_map[node] = G.getPartitionIndex(node); - block_sizes[G.getPartitionIndex(node)]++; - } endfor - - NodeWeight max_domain_weight = 0; - for( unsigned i = 0; i < G.get_partition_count(); i++) { - if( block_sizes[i] > max_domain_weight ) { - max_domain_weight = block_sizes[i]; - } - } - - if( max_domain_weight > config.upper_bound_partition ) { - best_local_objective_m = std::numeric_limits< int >::max(); - } + if( best_global_objective == std::numeric_limits< int >::max()) { + //no partition is feasible + MPI_Allreduce(&best_local_objective, &best_global_objective, 1, MPI_INT, MPI_MIN, m_communicator); + } - MPI_Allreduce(&best_local_objective_m, &best_global_objective, 1, MPI_INT, MPI_MIN, m_communicator); - - if( best_global_objective == std::numeric_limits< int >::max()) { - //no partition is feasible - MPI_Allreduce(&best_local_objective, &best_global_objective, 1, MPI_INT, MPI_MIN, m_communicator); - } - - int my_domain_weight = best_local_objective == best_global_objective ? + int my_domain_weight = best_local_objective == best_global_objective ? max_domain_weight : std::numeric_limits::max(); - int best_domain_weight = max_domain_weight; - - MPI_Allreduce(&my_domain_weight, &best_domain_weight, 1, MPI_INT, MPI_MIN, m_communicator); + int best_domain_weight = max_domain_weight; + + MPI_Allreduce(&my_domain_weight, &best_domain_weight, 1, MPI_INT, MPI_MIN, m_communicator); - // now we know what the best objective is ... find the best balance - int bcaster = best_local_objective == best_global_objective - && my_domain_weight == best_domain_weight ? m_rank : std::numeric_limits::max(); - int g_bcaster = 0; + // now we know what the best objective is ... find the best balance + int bcaster = best_local_objective == best_global_objective + && my_domain_weight == best_domain_weight ? m_rank : std::numeric_limits::max(); + int g_bcaster = 0; - MPI_Allreduce(&bcaster, &g_bcaster, 1, MPI_INT, MPI_MIN, m_communicator); - MPI_Bcast(best_local_map, G.number_of_nodes(), MPI_INT, g_bcaster, m_communicator); + MPI_Allreduce(&bcaster, &g_bcaster, 1, MPI_INT, MPI_MIN, m_communicator); + MPI_Bcast(best_local_map, G.number_of_nodes(), MPI_INT, g_bcaster, m_communicator); - forall_nodes(G, node) { - G.setPartitionIndex(node, best_local_map[node]); - } endfor + forall_nodes(G, node) { + G.setPartitionIndex(node, best_local_map[node]); + } endfor - delete[] best_local_map; + delete[] best_local_map; - return best_global_objective; + return best_global_objective; } EdgeWeight parallel_mh_async::perform_local_partitioning(PartitionConfig & working_config, graph_access & G) { - quality_metrics qm; - unsigned local_repetitions = working_config.local_partitioning_repetitions; + quality_metrics qm; + unsigned local_repetitions = working_config.local_partitioning_repetitions; + + if( working_config.mh_diversify ) { + diversifyer div; + div.diversify(working_config); + } + + //start a new round + for( unsigned i = 0; i < local_repetitions; i++) { + if( working_config.mh_no_mh ) { + Individuum first_ind; + + if( !working_config.mh_easy_construction) { + m_island->createIndividuum(working_config, G, first_ind, true); + m_island->insert(G, first_ind); + } else { + construct_partition cp; + cp.createIndividuum( working_config, G, first_ind, true); + + m_island->insert(G, first_ind); + std::cout << "created with objective " << first_ind.objective << std::endl; + } + } else { + if( m_island->is_full() && !working_config.mh_disable_combine) { + + int decision = random_functions::nextInt(0,9); + Individuum output; + + if(decision < working_config.mh_flip_coin) { + m_island->mutate_random(working_config, G, output); + m_island->insert(G, output); + } else { - if( working_config.mh_diversify ) { - diversifyer div; - div.diversify(working_config); + int combine_decision = random_functions::nextInt(0,5); + if(combine_decision <= 4) { + Individuum first_rnd; + Individuum second_rnd; + if(working_config.mh_enable_tournament_selection) { + m_island->get_two_individuals_tournament(first_rnd, second_rnd); + } else { + m_island->get_two_random_individuals(first_rnd, second_rnd); + } + + m_island->combine(working_config, G, first_rnd, second_rnd, output); + + int coin = 0; + + if( working_config.mh_enable_gal_combine ) { + coin = random_functions::nextInt(0,100); + } + if( coin == 23 ) { + if( first_rnd.objective > second_rnd.objective) { + m_island->replace(first_rnd, output); + } else { + m_island->replace(second_rnd, output); + } + } else { + m_island->insert(G, output); + } + } else if( combine_decision == 5 ) { + if(!working_config.mh_disable_cross_combine) { + Individuum selected; + m_island->get_one_individual_tournament(selected); + m_island->combine_cross(working_config, G, selected, output); + m_island->insert(G, output); + } + } } - //start a new round - for( unsigned i = 0; i < local_repetitions; i++) { - if( working_config.mh_no_mh ) { - Individuum first_ind; - - if( !working_config.mh_easy_construction) { - m_island->createIndividuum(working_config, G, first_ind, true); - m_island->insert(G, first_ind); - } else { - construct_partition cp; - cp.createIndividuum( working_config, G, first_ind, true); - - m_island->insert(G, first_ind); - std::cout << "created with objective " << first_ind.objective << std::endl; - } - } else { - if( m_island->is_full() && !working_config.mh_disable_combine) { - - int decision = random_functions::nextInt(0,9); - Individuum output; - - if(decision < working_config.mh_flip_coin) { - m_island->mutate_random(working_config, G, output); - m_island->insert(G, output); - } else { - - int combine_decision = random_functions::nextInt(0,5); - if(combine_decision <= 4) { - Individuum first_rnd; - Individuum second_rnd; - if(working_config.mh_enable_tournament_selection) { - m_island->get_two_individuals_tournament(first_rnd, second_rnd); - } else { - m_island->get_two_random_individuals(first_rnd, second_rnd); - } - - m_island->combine(working_config, G, first_rnd, second_rnd, output); - - int coin = 0; - - if( working_config.mh_enable_gal_combine ) { - coin = random_functions::nextInt(0,100); - } - if( coin == 23 ) { - if( first_rnd.objective > second_rnd.objective) { - m_island->replace(first_rnd, output); - } else { - m_island->replace(second_rnd, output); - } - } else { - m_island->insert(G, output); - } - } else if( combine_decision == 5 ) { - if(!working_config.mh_disable_cross_combine) { - Individuum selected; - m_island->get_one_individual_tournament(selected); - m_island->combine_cross(working_config, G, selected, output); - m_island->insert(G, output); - } - } - } - - } else { - Individuum first_ind; - if(m_island->is_full()) { - m_island->mutate_random(working_config, G, first_ind); - } else { - if( !working_config.mh_easy_construction) { - m_island->createIndividuum(working_config, G, first_ind, true); - } else { - construct_partition cp; - cp.createIndividuum( working_config, G, first_ind, true); - std::cout << "created with objective " << first_ind.objective << std::endl; - } - } - m_island->insert(G, first_ind); - } - } - - //try to combine to random inidividuals from pool - if( m_t.elapsed() > m_time_limit ) { - break; - } - + } else { + Individuum first_ind; + if(m_island->is_full()) { + m_island->mutate_random(working_config, G, first_ind); + } else { + if( !working_config.mh_easy_construction) { + m_island->createIndividuum(working_config, G, first_ind, true); + } else { + construct_partition cp; + cp.createIndividuum( working_config, G, first_ind, true); + std::cout << "created with objective " << first_ind.objective << std::endl; + } } + m_island->insert(G, first_ind); + } + } - EdgeWeight min_objective = 0; - m_island->apply_fittest(G, min_objective); + //try to combine to random inidividuals from pool + if( m_t.elapsed() > m_time_limit ) { + break; + } - return min_objective; -} + } + EdgeWeight min_objective = 0; + m_island->apply_fittest(G, min_objective); + + return min_objective; +} +} diff --git a/parallel/modified_kahip/lib/parallel_mh/parallel_mh_async.h b/parallel/modified_kahip/lib/parallel_mh/parallel_mh_async.h index e54c97f2..906fd06b 100644 --- a/parallel/modified_kahip/lib/parallel_mh/parallel_mh_async.h +++ b/parallel/modified_kahip/lib/parallel_mh/parallel_mh_async.h @@ -13,38 +13,38 @@ #include "partition_config.h" #include "population.h" #include "timer.h" - +namespace kahip::modified { class parallel_mh_async { public: - parallel_mh_async(); - parallel_mh_async(MPI_Comm communicator); - virtual ~parallel_mh_async(); + parallel_mh_async(); + parallel_mh_async(MPI_Comm communicator); + virtual ~parallel_mh_async(); - void perform_partitioning(const PartitionConfig & graph_partitioner_config, graph_access & G); - void initialize(PartitionConfig & graph_partitioner_config, graph_access & G); - EdgeWeight perform_local_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); - EdgeWeight collect_best_partitioning(graph_access & G, const PartitionConfig & config); - void perform_cycle_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); + void perform_partitioning(const PartitionConfig & graph_partitioner_config, graph_access & G); + void initialize(PartitionConfig & graph_partitioner_config, graph_access & G); + EdgeWeight perform_local_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); + EdgeWeight collect_best_partitioning(graph_access & G, const PartitionConfig & config); + void perform_cycle_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); private: - //misc - const unsigned MASTER; - timer m_t; - int m_rank; - int m_size; - double m_time_limit; - bool m_termination; - unsigned m_rounds; - - //the best cut found so far - PartitionID* m_best_global_map; - int m_best_global_objective; - int m_best_cycle_objective; - - //island - population* m_island; - MPI_Comm m_communicator; + //misc + const unsigned MASTER; + timer m_t; + int m_rank; + int m_size; + double m_time_limit; + bool m_termination; + unsigned m_rounds; + + //the best cut found so far + PartitionID* m_best_global_map; + int m_best_global_objective; + int m_best_cycle_objective; + + //island + population* m_island; + MPI_Comm m_communicator; }; - +} #endif /* end of include guard: PARALLEL_MH_ASYNC_HF106Y0G */ diff --git a/parallel/modified_kahip/lib/parallel_mh/population.cpp b/parallel/modified_kahip/lib/parallel_mh/population.cpp index 00ac9be0..7b01f660 100644 --- a/parallel/modified_kahip/lib/parallel_mh/population.cpp +++ b/parallel/modified_kahip/lib/parallel_mh/population.cpp @@ -20,419 +20,419 @@ #include "random_functions.h" #include "timer.h" #include "uncoarsening/refinement/cycle_improvements/cycle_refinement.h" - +namespace kahip::modified { population::population( MPI_Comm communicator, const PartitionConfig & partition_config ) { - m_population_size = partition_config.mh_pool_size; - m_no_partition_calls = 0; - m_num_NCs = partition_config.mh_num_ncs_to_compute; - m_num_NCs_computed = 0; - m_num_ENCs = 0; - m_time_stamp = 0; - m_communicator = communicator; - m_global_timer.restart(); + m_population_size = partition_config.mh_pool_size; + m_no_partition_calls = 0; + m_num_NCs = partition_config.mh_num_ncs_to_compute; + m_num_NCs_computed = 0; + m_num_ENCs = 0; + m_time_stamp = 0; + m_communicator = communicator; + m_global_timer.restart(); } population::~population() { - for( unsigned i = 0; i < m_internal_population.size(); i++) { - delete[] (m_internal_population[i].partition_map); - delete m_internal_population[i].cut_edges; - } + for( unsigned i = 0; i < m_internal_population.size(); i++) { + delete[] (m_internal_population[i].partition_map); + delete m_internal_population[i].cut_edges; + } } void population::set_pool_size(int size) { - m_population_size = size; + m_population_size = size; } -void population::createIndividuum(const PartitionConfig & config, - graph_access & G, - Individuum & ind, bool output) { - - PartitionConfig copy = config; - graph_partitioner partitioner; - quality_metrics qm; - - std::ofstream ofs; - std::streambuf* backup = std::cout.rdbuf(); - ofs.open("/dev/null"); - std::cout.rdbuf(ofs.rdbuf()); - - timer t; t.restart(); - - if(config.buffoon) { // graph is weighted -> no negative cycle detection yet - partitioner.perform_partitioning(copy, G); - ofs.close(); - std::cout.rdbuf(backup); - } else { - if(config.kabapE) { - double real_epsilon = config.imbalance/100.0; - double lb = real_epsilon+0.005; - double ub = real_epsilon+config.kabaE_internal_bal; - double epsilon = random_functions::nextDouble(lb,ub); - copy.upper_bound_partition = (1+epsilon)*ceil(config.largest_graph_weight/(double)config.k); - - partitioner.perform_partitioning(copy, G); - - ofs.close(); - std::cout.rdbuf(backup); - - complete_boundary boundary(&G); - boundary.build(); - - copy = config; - - diversifyer df; - df.diversify_kaba(copy); - - cycle_refinement cr; - cr.perform_refinement(copy, G, boundary); - } else { - partitioner.perform_partitioning(copy, G); - ofs.close(); - std::cout.rdbuf(backup); - } - } - - int* partition_map = new int[G.number_of_nodes()]; - - forall_nodes(G, node) { - partition_map[node] = G.getPartitionIndex(node); - } endfor +void population::createIndividuum(const PartitionConfig & config, + graph_access & G, + Individuum & ind, bool output) { + + PartitionConfig copy = config; + graph_partitioner partitioner; + quality_metrics qm; + + std::ofstream ofs; + std::streambuf* backup = std::cout.rdbuf(); + ofs.open("/dev/null"); + std::cout.rdbuf(ofs.rdbuf()); + + timer t; t.restart(); + + if(config.buffoon) { // graph is weighted -> no negative cycle detection yet + partitioner.perform_partitioning(copy, G); + ofs.close(); + std::cout.rdbuf(backup); + } else { + if(config.kabapE) { + double real_epsilon = config.imbalance/100.0; + double lb = real_epsilon+0.005; + double ub = real_epsilon+config.kabaE_internal_bal; + double epsilon = random_functions::nextDouble(lb,ub); + copy.upper_bound_partition = (1+epsilon)*ceil(config.largest_graph_weight/(double)config.k); + + partitioner.perform_partitioning(copy, G); + + ofs.close(); + std::cout.rdbuf(backup); + + complete_boundary boundary(&G); + boundary.build(); + + copy = config; + + diversifyer df; + df.diversify_kaba(copy); + + cycle_refinement cr; + cr.perform_refinement(copy, G, boundary); + } else { + partitioner.perform_partitioning(copy, G); + ofs.close(); + std::cout.rdbuf(backup); + } + } + + int* partition_map = new int[G.number_of_nodes()]; + + forall_nodes(G, node) { + partition_map[node] = G.getPartitionIndex(node); + } endfor + + ind.objective = qm.objective(config, G, partition_map); + ind.partition_map = partition_map; + ind.cut_edges = new std::vector(); + + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(partition_map[node] != partition_map[target]) { + ind.cut_edges->push_back(e); + } + } endfor +} endfor + +if(output) { + m_filebuffer_string << m_global_timer.elapsed() << " " << ind.cut_edges->size()/2 << std::endl; + m_time_stamp++; +} +} - ind.objective = qm.objective(config, G, partition_map); - ind.partition_map = partition_map; - ind.cut_edges = new std::vector(); +void population::insert(graph_access & G, Individuum & ind) { - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(partition_map[node] != partition_map[target]) { - ind.cut_edges->push_back(e); - } - } endfor - } endfor + m_no_partition_calls++; + if(m_internal_population.size() < m_population_size) { + m_internal_population.push_back(ind); + } else { + EdgeWeight worst_objective = 0; + for( unsigned i = 0; i < m_internal_population.size(); i++) { + if(m_internal_population[i].objective > worst_objective) { + worst_objective = m_internal_population[i].objective; + } + } + if(ind.objective > worst_objective ) { + delete[] (ind.partition_map); + delete ind.cut_edges; + return; // do nothing + } + //else measure similarity + unsigned max_similarity = std::numeric_limits::max(); + unsigned max_similarity_idx = 0; + for( unsigned i = 0; i < m_internal_population.size(); i++) { + if(m_internal_population[i].objective >= ind.objective) { + //now measure + int diff_size = m_internal_population[i].cut_edges->size() + ind.cut_edges->size(); + std::vector output_diff(diff_size,std::numeric_limits::max()); + + set_symmetric_difference(m_internal_population[i].cut_edges->begin(), + m_internal_population[i].cut_edges->end(), + ind.cut_edges->begin(), + ind.cut_edges->end(), + output_diff.begin()); + + unsigned similarity = 0; + for( unsigned j = 0; j < output_diff.size(); j++) { + if(output_diff[j] < std::numeric_limits::max()) { + similarity++; + } else { + break; + } + } - if(output) { - m_filebuffer_string << m_global_timer.elapsed() << " " << ind.cut_edges->size()/2 << std::endl; - m_time_stamp++; + if( similarity < max_similarity) { + max_similarity = similarity; + max_similarity_idx = i; } -} + } + } -void population::insert(graph_access & G, Individuum & ind) { + delete[] (m_internal_population[max_similarity_idx].partition_map); + delete m_internal_population[max_similarity_idx].cut_edges; - m_no_partition_calls++; - if(m_internal_population.size() < m_population_size) { - m_internal_population.push_back(ind); - } else { - EdgeWeight worst_objective = 0; - for( unsigned i = 0; i < m_internal_population.size(); i++) { - if(m_internal_population[i].objective > worst_objective) { - worst_objective = m_internal_population[i].objective; - } - } - if(ind.objective > worst_objective ) { - delete[] (ind.partition_map); - delete ind.cut_edges; - return; // do nothing - } - //else measure similarity - unsigned max_similarity = std::numeric_limits::max(); - unsigned max_similarity_idx = 0; - for( unsigned i = 0; i < m_internal_population.size(); i++) { - if(m_internal_population[i].objective >= ind.objective) { - //now measure - int diff_size = m_internal_population[i].cut_edges->size() + ind.cut_edges->size(); - std::vector output_diff(diff_size,std::numeric_limits::max()); - - set_symmetric_difference(m_internal_population[i].cut_edges->begin(), - m_internal_population[i].cut_edges->end(), - ind.cut_edges->begin(), - ind.cut_edges->end(), - output_diff.begin()); - - unsigned similarity = 0; - for( unsigned j = 0; j < output_diff.size(); j++) { - if(output_diff[j] < std::numeric_limits::max()) { - similarity++; - } else { - break; - } - } - - if( similarity < max_similarity) { - max_similarity = similarity; - max_similarity_idx = i; - } - } - } - - delete[] (m_internal_population[max_similarity_idx].partition_map); - delete m_internal_population[max_similarity_idx].cut_edges; - - m_internal_population[max_similarity_idx] = ind; - } + m_internal_population[max_similarity_idx] = ind; + } } void population::replace(Individuum & in, Individuum & out) { - //first find it: - for( unsigned i = 0; i < m_internal_population.size(); i++) { - if(m_internal_population[i].partition_map == in.partition_map) { - //found it - delete[] (m_internal_population[i].partition_map); - delete m_internal_population[i].cut_edges; - - m_internal_population[i] = out; - break; - } - } + //first find it: + for( unsigned i = 0; i < m_internal_population.size(); i++) { + if(m_internal_population[i].partition_map == in.partition_map) { + //found it + delete[] (m_internal_population[i].partition_map); + delete m_internal_population[i].cut_edges; + + m_internal_population[i] = out; + break; + } + } } -void population::combine(const PartitionConfig & partition_config, - graph_access & G, - Individuum & first_ind, - Individuum & second_ind, +void population::combine(const PartitionConfig & partition_config, + graph_access & G, + Individuum & first_ind, + Individuum & second_ind, Individuum & output_ind) { - PartitionConfig config = partition_config; - G.resizeSecondPartitionIndex(G.number_of_nodes()); - if( first_ind.objective < second_ind.objective ) { - forall_nodes(G, node) { - G.setPartitionIndex(node, first_ind.partition_map[node]); - G.setSecondPartitionIndex(node, second_ind.partition_map[node]); - - } endfor - } else { - forall_nodes(G, node) { - G.setPartitionIndex(node, second_ind.partition_map[node]); - G.setSecondPartitionIndex(node, first_ind.partition_map[node]); - } endfor - } + PartitionConfig config = partition_config; + G.resizeSecondPartitionIndex(G.number_of_nodes()); + if( first_ind.objective < second_ind.objective ) { + forall_nodes(G, node) { + G.setPartitionIndex(node, first_ind.partition_map[node]); + G.setSecondPartitionIndex(node, second_ind.partition_map[node]); + + } endfor +} else { + forall_nodes(G, node) { + G.setPartitionIndex(node, second_ind.partition_map[node]); + G.setSecondPartitionIndex(node, first_ind.partition_map[node]); + } endfor +} - config.combine = true; - config.graph_allready_partitioned = true; - config.no_new_initial_partitioning = true; - - bool coin = false; - if( partition_config.mh_enable_gal_combine ) { - coin = random_functions::nextBool(); - } - - if( coin ) { - gal_combine combine_operator; - combine_operator.perform_gal_combine( config, G); - int* partition_map = new int[G.number_of_nodes()]; - - forall_nodes(G, node) { - partition_map[node] = G.getPartitionIndex(node); - } endfor - - quality_metrics qm; - output_ind.objective = qm.objective(config, G, partition_map); - output_ind.partition_map = partition_map; - output_ind.cut_edges = new std::vector(); - - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(partition_map[node] != partition_map[target]) { - output_ind.cut_edges->push_back(e); - } - } endfor - } endfor - } else { - createIndividuum(config, G, output_ind, true); - } - std::cout << "objective mh " << output_ind.objective << std::endl; + config.combine = true; + config.graph_allready_partitioned = true; + config.no_new_initial_partitioning = true; + + bool coin = false; + if( partition_config.mh_enable_gal_combine ) { + coin = random_functions::nextBool(); + } + + if( coin ) { + gal_combine combine_operator; + combine_operator.perform_gal_combine( config, G); + int* partition_map = new int[G.number_of_nodes()]; + + forall_nodes(G, node) { + partition_map[node] = G.getPartitionIndex(node); + } endfor + + quality_metrics qm; + output_ind.objective = qm.objective(config, G, partition_map); + output_ind.partition_map = partition_map; + output_ind.cut_edges = new std::vector(); + + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(partition_map[node] != partition_map[target]) { + output_ind.cut_edges->push_back(e); + } + } endfor + } endfor + } else { + createIndividuum(config, G, output_ind, true); + } + std::cout << "objective mh " << output_ind.objective << std::endl; } -void population::combine_cross(const PartitionConfig & partition_config, - graph_access & G, - Individuum & first_ind, +void population::combine_cross(const PartitionConfig & partition_config, + graph_access & G, + Individuum & first_ind, Individuum & output_ind) { - PartitionConfig config = partition_config; - G.resizeSecondPartitionIndex(G.number_of_nodes()); + PartitionConfig config = partition_config; + G.resizeSecondPartitionIndex(G.number_of_nodes()); - int lowerbound = config.k / 4; - lowerbound = std::max(2, lowerbound); - int kfactor = random_functions::nextInt(lowerbound,4*config.k); - kfactor = std::min( kfactor, (int)G.number_of_nodes()); + int lowerbound = config.k / 4; + lowerbound = std::max(2, lowerbound); + int kfactor = random_functions::nextInt(lowerbound,4*config.k); + kfactor = std::min( kfactor, (int)G.number_of_nodes()); - if( config.mh_cross_combine_original_k ) { - MPI_Bcast(&kfactor, 1, MPI_INT, 0, m_communicator); - } + if( config.mh_cross_combine_original_k ) { + MPI_Bcast(&kfactor, 1, MPI_INT, 0, m_communicator); + } - unsigned larger_imbalance = random_functions::nextInt(config.epsilon,25); - double epsilon = larger_imbalance/100.0; + unsigned larger_imbalance = random_functions::nextInt(config.epsilon,25); + double epsilon = larger_imbalance/100.0; - - PartitionConfig cross_config = config; - cross_config.k = kfactor; - cross_config.kaffpa_perfectly_balanced_refinement = false; - cross_config.upper_bound_partition = (1+epsilon)*ceil(partition_config.largest_graph_weight/(double)cross_config.k); - cross_config.refinement_scheduling_algorithm = REFINEMENT_SCHEDULING_ACTIVE_BLOCKS; - cross_config.combine = false; - cross_config.graph_allready_partitioned = false; - std::ofstream ofs; - std::streambuf* backup = std::cout.rdbuf(); - ofs.open("/dev/null"); - std::cout.rdbuf(ofs.rdbuf()); + PartitionConfig cross_config = config; + cross_config.k = kfactor; + cross_config.kaffpa_perfectly_balanced_refinement = false; + cross_config.upper_bound_partition = (1+epsilon)*ceil(partition_config.largest_graph_weight/(double)cross_config.k); + cross_config.refinement_scheduling_algorithm = REFINEMENT_SCHEDULING_ACTIVE_BLOCKS; + cross_config.combine = false; + cross_config.graph_allready_partitioned = false; - graph_partitioner partitioner; - partitioner.perform_partitioning(cross_config, G); + std::ofstream ofs; + std::streambuf* backup = std::cout.rdbuf(); + ofs.open("/dev/null"); + std::cout.rdbuf(ofs.rdbuf()); - ofs.close(); - std::cout.rdbuf(backup); + graph_partitioner partitioner; + partitioner.perform_partitioning(cross_config, G); - forall_nodes(G, node) { - G.setSecondPartitionIndex(node, G.getPartitionIndex(node)); - G.setPartitionIndex(node, first_ind.partition_map[node]); - } endfor + ofs.close(); + std::cout.rdbuf(backup); - config.combine = true; - config.graph_allready_partitioned = true; - config.no_new_initial_partitioning = true; + forall_nodes(G, node) { + G.setSecondPartitionIndex(node, G.getPartitionIndex(node)); + G.setPartitionIndex(node, first_ind.partition_map[node]); + } endfor - createIndividuum(config, G, output_ind, true); - std::cout << "objective cross " << output_ind.objective - << " k " << kfactor - << " imbal " << larger_imbalance - << " impro " << (first_ind.objective - output_ind.objective) << std::endl; + config.combine = true; + config.graph_allready_partitioned = true; + config.no_new_initial_partitioning = true; + + createIndividuum(config, G, output_ind, true); + std::cout << "objective cross " << output_ind.objective + << " k " << kfactor + << " imbal " << larger_imbalance + << " impro " << (first_ind.objective - output_ind.objective) << std::endl; } void population::mutate_random( const PartitionConfig & partition_config, graph_access & G, Individuum & first_ind) { - int number = random_functions::nextInt(0,5); + int number = random_functions::nextInt(0,5); - PartitionConfig config = partition_config; - config.combine = false; - config.graph_allready_partitioned = true; - get_random_individuum(first_ind); + PartitionConfig config = partition_config; + config.combine = false; + config.graph_allready_partitioned = true; + get_random_individuum(first_ind); - if(number < 5) { - forall_nodes(G, node) { - G.setPartitionIndex(node, first_ind.partition_map[node]); - } endfor + if(number < 5) { + forall_nodes(G, node) { + G.setPartitionIndex(node, first_ind.partition_map[node]); + } endfor - config.no_new_initial_partitioning = true; - createIndividuum( config, G, first_ind, true); + config.no_new_initial_partitioning = true; + createIndividuum( config, G, first_ind, true); - } else { - forall_nodes(G, node) { - G.setPartitionIndex(node, first_ind.partition_map[node]); - } endfor + } else { + forall_nodes(G, node) { + G.setPartitionIndex(node, first_ind.partition_map[node]); + } endfor - config.graph_allready_partitioned = false; - createIndividuum( config, G, first_ind, true); - } + config.graph_allready_partitioned = false; + createIndividuum( config, G, first_ind, true); + } } void population::extinction( ) { - for( unsigned i = 0; i < m_internal_population.size(); i++) { - delete[] m_internal_population[i].partition_map; - delete m_internal_population[i].cut_edges; - } + for( unsigned i = 0; i < m_internal_population.size(); i++) { + delete[] m_internal_population[i].partition_map; + delete m_internal_population[i].cut_edges; + } - m_internal_population.clear(); - m_internal_population.resize(0); + m_internal_population.clear(); + m_internal_population.resize(0); } void population::get_two_random_individuals(Individuum & first, Individuum & second) { - int first_idx = random_functions::nextInt(0, m_internal_population.size()-1); - first = m_internal_population[first_idx]; + int first_idx = random_functions::nextInt(0, m_internal_population.size()-1); + first = m_internal_population[first_idx]; - int second_idx = random_functions::nextInt(0, m_internal_population.size()-1); - while( first_idx == second_idx ) { - second_idx = random_functions::nextInt(0, m_internal_population.size()-1); - } + int second_idx = random_functions::nextInt(0, m_internal_population.size()-1); + while( first_idx == second_idx ) { + second_idx = random_functions::nextInt(0, m_internal_population.size()-1); + } - second = m_internal_population[second_idx]; + second = m_internal_population[second_idx]; } void population::get_one_individual_tournament(Individuum & first) { - Individuum one, two; - get_two_random_individuals(one, two); - first = one.objective < two.objective ? one : two; + Individuum one, two; + get_two_random_individuals(one, two); + first = one.objective < two.objective ? one : two; } void population::get_two_individuals_tournament(Individuum & first, Individuum & second) { - Individuum one, two; - get_two_random_individuals(one, two); - first = one.objective < two.objective? one : two; + Individuum one, two; + get_two_random_individuals(one, two); + first = one.objective < two.objective? one : two; - get_two_random_individuals(one, two); - second = one.objective < two.objective ? one : two; + get_two_random_individuals(one, two); + second = one.objective < two.objective ? one : two; - if( first.objective == second.objective) { - second = one.objective >= two.objective? one : two; - } + if( first.objective == second.objective) { + second = one.objective >= two.objective? one : two; + } } void population::get_random_individuum(Individuum & ind) { - int idx = random_functions::nextInt(0, m_internal_population.size()-1); - ind = m_internal_population[idx]; + int idx = random_functions::nextInt(0, m_internal_population.size()-1); + ind = m_internal_population[idx]; } void population::get_best_individuum(Individuum & ind) { - EdgeWeight min_objective = std::numeric_limits::max(); - unsigned idx = 0; - - for( unsigned i = 0; i < m_internal_population.size(); i++) { - if((EdgeWeight)m_internal_population[i].objective < min_objective) { - min_objective = m_internal_population[i].objective; - idx = i; - } - } + EdgeWeight min_objective = std::numeric_limits::max(); + unsigned idx = 0; + + for( unsigned i = 0; i < m_internal_population.size(); i++) { + if((EdgeWeight)m_internal_population[i].objective < min_objective) { + min_objective = m_internal_population[i].objective; + idx = i; + } + } - ind = m_internal_population[idx]; + ind = m_internal_population[idx]; } bool population::is_full() { - return m_internal_population.size() == m_population_size; + return m_internal_population.size() == m_population_size; } void population::apply_fittest( graph_access & G, EdgeWeight & objective ) { - EdgeWeight min_objective = std::numeric_limits::max(); - double best_balance = std::numeric_limits::max(); - unsigned idx = 0; - - quality_metrics qm; - for( unsigned i = 0; i < m_internal_population.size(); i++) { - forall_nodes(G, node) { - G.setPartitionIndex(node, m_internal_population[i].partition_map[node]); - } endfor - double cur_balance = qm.balance(G); - if((EdgeWeight)m_internal_population[i].objective < min_objective - || ((EdgeWeight)m_internal_population[i].objective == min_objective && cur_balance < best_balance)) { - min_objective = m_internal_population[i].objective; - idx = i; - best_balance = cur_balance; - } - } + EdgeWeight min_objective = std::numeric_limits::max(); + double best_balance = std::numeric_limits::max(); + unsigned idx = 0; + + quality_metrics qm; + for( unsigned i = 0; i < m_internal_population.size(); i++) { + forall_nodes(G, node) { + G.setPartitionIndex(node, m_internal_population[i].partition_map[node]); + } endfor + double cur_balance = qm.balance(G); + if((EdgeWeight)m_internal_population[i].objective < min_objective +|| ((EdgeWeight)m_internal_population[i].objective == min_objective && cur_balance < best_balance)) { + min_objective = m_internal_population[i].objective; + idx = i; + best_balance = cur_balance; +} + } - forall_nodes(G, node) { - G.setPartitionIndex(node, m_internal_population[idx].partition_map[node]); - } endfor + forall_nodes(G, node) { + G.setPartitionIndex(node, m_internal_population[idx].partition_map[node]); + } endfor - objective = min_objective; + objective = min_objective; } void population::print() { - int rank; - MPI_Comm_rank( m_communicator, &rank); - - std::cout << "rank " << rank << " fingerprint "; + int rank; + MPI_Comm_rank( m_communicator, &rank); - for( unsigned i = 0; i < m_internal_population.size(); i++) { - std::cout << m_internal_population[i].objective << " "; - } + std::cout << "rank " << rank << " fingerprint "; - std::cout << std::endl; + for( unsigned i = 0; i < m_internal_population.size(); i++) { + std::cout << m_internal_population[i].objective << " "; + } + + std::cout << std::endl; } void population::write_log(std::string & filename) { - std::ofstream f(filename.c_str()); - f << m_filebuffer_string.str(); - f.close(); + std::ofstream f(filename.c_str()); + f << m_filebuffer_string.str(); + f.close(); +} } - diff --git a/parallel/modified_kahip/lib/parallel_mh/population.h b/parallel/modified_kahip/lib/parallel_mh/population.h index 37e27db9..7d313aee 100644 --- a/parallel/modified_kahip/lib/parallel_mh/population.h +++ b/parallel/modified_kahip/lib/parallel_mh/population.h @@ -14,7 +14,7 @@ #include "data_structure/graph_access.h" #include "partition_config.h" #include "timer.h" - +namespace kahip::modified { struct Individuum { int* partition_map; EdgeWeight objective; @@ -26,77 +26,77 @@ struct ENC { }; class population { - public: - population( MPI_Comm comm, const PartitionConfig & config ); - virtual ~population(); +public: + population( MPI_Comm comm, const PartitionConfig & config ); + virtual ~population(); - void createIndividuum(const PartitionConfig & config, - graph_access & G, - Individuum & ind, - bool output); + void createIndividuum(const PartitionConfig & config, + graph_access & G, + Individuum & ind, + bool output); - void combine(const PartitionConfig & config, - graph_access & G, - Individuum & first_ind, - Individuum & second_ind, - Individuum & output_ind); + void combine(const PartitionConfig & config, + graph_access & G, + Individuum & first_ind, + Individuum & second_ind, + Individuum & output_ind); - void combine_cross(const PartitionConfig & partition_config, - graph_access & G, - Individuum & first_ind, - Individuum & output_ind); + void combine_cross(const PartitionConfig & partition_config, + graph_access & G, + Individuum & first_ind, + Individuum & output_ind); - void mutate_random(const PartitionConfig & partition_config, - graph_access & G, - Individuum & first_ind); + void mutate_random(const PartitionConfig & partition_config, + graph_access & G, + Individuum & first_ind); - void insert(graph_access & G, Individuum & ind); + void insert(graph_access & G, Individuum & ind); - void set_pool_size(int size); + void set_pool_size(int size); - void extinction(); + void extinction(); - void get_two_random_individuals(Individuum & first, Individuum & second); - - void get_one_individual_tournament(Individuum & first); + void get_two_random_individuals(Individuum & first, Individuum & second); - void get_two_individuals_tournament(Individuum & first, Individuum & second); + void get_one_individual_tournament(Individuum & first); - void replace(Individuum & in, Individuum & out); + void get_two_individuals_tournament(Individuum & first, Individuum & second); - void get_random_individuum(Individuum & ind); + void replace(Individuum & in, Individuum & out); - void get_best_individuum(Individuum & ind); + void get_random_individuum(Individuum & ind); - bool is_full(); + void get_best_individuum(Individuum & ind); - void apply_fittest( graph_access & G, EdgeWeight & objective); + bool is_full(); - unsigned size() { return m_internal_population.size(); } - - void print(); + void apply_fittest( graph_access & G, EdgeWeight & objective); - void write_log(std::string & filename); + unsigned size() { return m_internal_population.size(); } + void print(); - private: + void write_log(std::string & filename); - unsigned m_no_partition_calls; - unsigned m_population_size; - std::vector m_internal_population; - std::vector< std::vector< unsigned int > > m_vertex_ENCs; - std::vector< ENC > m_ENCs; - int m_num_NCs; - int m_num_NCs_computed; - int m_num_ENCs; - int m_time_stamp; +private: - MPI_Comm m_communicator; + unsigned m_no_partition_calls; + unsigned m_population_size; + std::vector m_internal_population; + std::vector< std::vector< unsigned int > > m_vertex_ENCs; + std::vector< ENC > m_ENCs; - std::stringstream m_filebuffer_string; - timer m_global_timer; -}; + int m_num_NCs; + int m_num_NCs_computed; + int m_num_ENCs; + int m_time_stamp; + MPI_Comm m_communicator; + + std::stringstream m_filebuffer_string; + timer m_global_timer; +}; +} #endif /* end of include guard: POPULATION_AEFH46G6 */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/clustering/node_ordering.cpp b/parallel/modified_kahip/lib/partition/coarsening/clustering/node_ordering.cpp index 5c0c6ce2..9af009f3 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/clustering/node_ordering.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/clustering/node_ordering.cpp @@ -7,7 +7,7 @@ #include "node_ordering.h" - +namespace kahip::modified { node_ordering::node_ordering() { } @@ -15,4 +15,4 @@ node_ordering::node_ordering() { node_ordering::~node_ordering() { } - +} diff --git a/parallel/modified_kahip/lib/partition/coarsening/clustering/node_ordering.h b/parallel/modified_kahip/lib/partition/coarsening/clustering/node_ordering.h index b87d7d2a..7d989ed6 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/clustering/node_ordering.h +++ b/parallel/modified_kahip/lib/partition/coarsening/clustering/node_ordering.h @@ -14,7 +14,7 @@ #include "partition_config.h" #include "data_structure/graph_access.h" #include "tools/random_functions.h" - +namespace kahip::modified { class node_ordering { public: node_ordering(); @@ -28,25 +28,25 @@ class node_ordering { switch( config.node_ordering ) { case RANDOM_NODEORDERING: order_nodes_random(config, G, ordered_nodes); - break; + break; case DEGREE_NODEORDERING: order_nodes_degree(config, G, ordered_nodes); - break; - } + break; + } } - void order_nodes_random(const PartitionConfig & config, graph_access & G, std::vector< NodeID > & ordered_nodes) { + void order_nodes_random(const PartitionConfig & config, graph_access & G, std::vector< NodeID > & ordered_nodes) { random_functions::permutate_vector_fast(ordered_nodes, false); } - void order_nodes_degree(const PartitionConfig & config, graph_access & G, std::vector< NodeID > & ordered_nodes) { - std::sort( ordered_nodes.begin(), ordered_nodes.end(), + void order_nodes_degree(const PartitionConfig & config, graph_access & G, std::vector< NodeID > & ordered_nodes) { + std::sort( ordered_nodes.begin(), ordered_nodes.end(), [&]( const NodeID & lhs, const NodeID & rhs) -> bool { return (G.getNodeDegree(lhs) < G.getNodeDegree(rhs)); }); } - }; - +}; +} #endif /* end of include guard: NODE_ORDERING_HM1YMLB1 */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/clustering/size_constraint_label_propagation.cpp b/parallel/modified_kahip/lib/partition/coarsening/clustering/size_constraint_label_propagation.cpp index b38195b6..a7c3000b 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/clustering/size_constraint_label_propagation.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/clustering/size_constraint_label_propagation.cpp @@ -20,7 +20,7 @@ #include "io/graph_io.h" #include "size_constraint_label_propagation.h" - +namespace kahip::modified { size_constraint_label_propagation::size_constraint_label_propagation() { } @@ -35,202 +35,202 @@ void size_constraint_label_propagation::match(const PartitionConfig & partition_ CoarseMapping & coarse_mapping, NodeID & no_of_coarse_vertices, NodePermutationMap & permutation) { - permutation.resize(G.number_of_nodes()); - coarse_mapping.resize(G.number_of_nodes()); - no_of_coarse_vertices = 0; - - if ( partition_config.ensemble_clusterings ) { - ensemble_clusterings(partition_config, G, _matching, coarse_mapping, no_of_coarse_vertices, permutation); - } else { - match_internal(partition_config, G, _matching, coarse_mapping, no_of_coarse_vertices, permutation); - } + permutation.resize(G.number_of_nodes()); + coarse_mapping.resize(G.number_of_nodes()); + no_of_coarse_vertices = 0; + + if ( partition_config.ensemble_clusterings ) { + ensemble_clusterings(partition_config, G, _matching, coarse_mapping, no_of_coarse_vertices, permutation); + } else { + match_internal(partition_config, G, _matching, coarse_mapping, no_of_coarse_vertices, permutation); + } } -void size_constraint_label_propagation::match_internal(const PartitionConfig & partition_config, - graph_access & G, - Matching & _matching, - CoarseMapping & coarse_mapping, +void size_constraint_label_propagation::match_internal(const PartitionConfig & partition_config, + graph_access & G, + Matching & _matching, + CoarseMapping & coarse_mapping, NodeID & no_of_coarse_vertices, NodePermutationMap & permutation) { - std::vector cluster_id(G.number_of_nodes()); - NodeWeight block_upperbound = ceil(partition_config.upper_bound_partition/(double)partition_config.cluster_coarsening_factor); + std::vector cluster_id(G.number_of_nodes()); + NodeWeight block_upperbound = ceil(partition_config.upper_bound_partition/(double)partition_config.cluster_coarsening_factor); - label_propagation( partition_config, G, block_upperbound, cluster_id, no_of_coarse_vertices); - create_coarsemapping( partition_config, G, cluster_id, coarse_mapping); + label_propagation( partition_config, G, block_upperbound, cluster_id, no_of_coarse_vertices); + create_coarsemapping( partition_config, G, cluster_id, coarse_mapping); } -void size_constraint_label_propagation::ensemble_two_clusterings( graph_access & G, - std::vector & lhs, - std::vector & rhs, +void size_constraint_label_propagation::ensemble_two_clusterings( graph_access & G, + std::vector & lhs, + std::vector & rhs, std::vector< NodeID > & output, NodeID & no_of_coarse_vertices) { - hash_ensemble new_mapping; - no_of_coarse_vertices = 0; - for( NodeID node = 0; node < lhs.size(); node++) { - ensemble_pair cur_pair; - cur_pair.lhs = lhs[node]; - cur_pair.rhs = rhs[node]; - cur_pair.n = G.number_of_nodes(); + hash_ensemble new_mapping; + no_of_coarse_vertices = 0; + for( NodeID node = 0; node < lhs.size(); node++) { + ensemble_pair cur_pair; + cur_pair.lhs = lhs[node]; + cur_pair.rhs = rhs[node]; + cur_pair.n = G.number_of_nodes(); - if(new_mapping.find(cur_pair) == new_mapping.end() ) { - new_mapping[cur_pair].mapping = no_of_coarse_vertices; - no_of_coarse_vertices++; - } + if(new_mapping.find(cur_pair) == new_mapping.end() ) { + new_mapping[cur_pair].mapping = no_of_coarse_vertices; + no_of_coarse_vertices++; + } - output[node] = new_mapping[cur_pair].mapping; - } + output[node] = new_mapping[cur_pair].mapping; + } - no_of_coarse_vertices = new_mapping.size(); + no_of_coarse_vertices = new_mapping.size(); } -void size_constraint_label_propagation::ensemble_clusterings(const PartitionConfig & partition_config, - graph_access & G, - Matching & _matching, - CoarseMapping & coarse_mapping, +void size_constraint_label_propagation::ensemble_clusterings(const PartitionConfig & partition_config, + graph_access & G, + Matching & _matching, + CoarseMapping & coarse_mapping, NodeID & no_of_coarse_vertices, NodePermutationMap & permutation) { - int runs = partition_config.number_of_clusterings; - std::vector< NodeID > cur_cluster(G.number_of_nodes(), 0); - std::vector< NodeID > ensemble_cluster(G.number_of_nodes(),0); - - std::cerr << "G.number_of_nodes " << G.number_of_nodes() << std::endl; - int new_cf = partition_config.cluster_coarsening_factor; - for( int i = 0; i < runs; i++) { - PartitionConfig config = partition_config; - - std::cerr << "config " << config.k << std::endl; - config.cluster_coarsening_factor = new_cf; - - NodeID cur_no_blocks = 0; - label_propagation(config, G, cur_cluster, cur_no_blocks); - - if( i != 0 ) { - ensemble_two_clusterings(G, cur_cluster, ensemble_cluster, ensemble_cluster, no_of_coarse_vertices); - } else { - forall_nodes(G, node) { - ensemble_cluster[node] = cur_cluster[node]; - } endfor - - no_of_coarse_vertices = cur_no_blocks; - } - new_cf = random_functions::nextInt(10, 30); - } + int runs = partition_config.number_of_clusterings; + std::vector< NodeID > cur_cluster(G.number_of_nodes(), 0); + std::vector< NodeID > ensemble_cluster(G.number_of_nodes(),0); + + std::cerr << "G.number_of_nodes " << G.number_of_nodes() << std::endl; + int new_cf = partition_config.cluster_coarsening_factor; + for( int i = 0; i < runs; i++) { + PartitionConfig config = partition_config; + + std::cerr << "config " << config.k << std::endl; + config.cluster_coarsening_factor = new_cf; - create_coarsemapping( partition_config, G, ensemble_cluster, coarse_mapping); + NodeID cur_no_blocks = 0; + label_propagation(config, G, cur_cluster, cur_no_blocks); + + if( i != 0 ) { + ensemble_two_clusterings(G, cur_cluster, ensemble_cluster, ensemble_cluster, no_of_coarse_vertices); + } else { + forall_nodes(G, node) { + ensemble_cluster[node] = cur_cluster[node]; + } endfor + + no_of_coarse_vertices = cur_no_blocks; + } + new_cf = random_functions::nextInt(10, 30); + } + + create_coarsemapping( partition_config, G, ensemble_cluster, coarse_mapping); } -void size_constraint_label_propagation::label_propagation(const PartitionConfig & partition_config, - graph_access & G, - std::vector & cluster_id, +void size_constraint_label_propagation::label_propagation(const PartitionConfig & partition_config, + graph_access & G, + std::vector & cluster_id, NodeID & no_of_blocks ) { - NodeWeight block_upperbound = ceil(partition_config.upper_bound_partition/(double)partition_config.cluster_coarsening_factor); + NodeWeight block_upperbound = ceil(partition_config.upper_bound_partition/(double)partition_config.cluster_coarsening_factor); - label_propagation( partition_config, G, block_upperbound, cluster_id, no_of_blocks); + label_propagation( partition_config, G, block_upperbound, cluster_id, no_of_blocks); } -void size_constraint_label_propagation::label_propagation(const PartitionConfig & partition_config, - graph_access & G, +void size_constraint_label_propagation::label_propagation(const PartitionConfig & partition_config, + graph_access & G, const NodeWeight & block_upperbound, - std::vector & cluster_id, + std::vector & cluster_id, NodeID & no_of_blocks) { - // in this case the _matching paramter is not used - // coarse_mappng stores cluster id and the mapping (it is identical) - std::vector hash_map(G.number_of_nodes(),0); - std::vector permutation(G.number_of_nodes()); - std::vector cluster_sizes(G.number_of_nodes()); - cluster_id.resize(G.number_of_nodes()); - - forall_nodes(G, node) { - cluster_sizes[node] = G.getNodeWeight(node); - cluster_id[node] = node; - } endfor - - node_ordering n_ordering; - n_ordering.order_nodes(partition_config, G, permutation); - - for( int j = 0; j < partition_config.label_iterations; j++) { - unsigned int change_counter = 0; - forall_nodes(G, i) { - NodeID node = permutation[i]; - //now move the node to the cluster that is most common in the neighborhood - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - hash_map[cluster_id[target]]+=G.getEdgeWeight(e); - } endfor - - //second sweep for finding max and resetting array - PartitionID max_block = cluster_id[node]; - PartitionID my_block = cluster_id[node]; - - PartitionID max_value = 0; - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID cur_block = cluster_id[target]; - PartitionID cur_value = hash_map[cur_block]; - if((cur_value > max_value || (cur_value == max_value && random_functions::nextBool())) - && (cluster_sizes[cur_block] + G.getNodeWeight(node) < block_upperbound || cur_block == my_block) - && (!partition_config.graph_allready_partitioned || G.getPartitionIndex(node) == G.getPartitionIndex(target)) - && (!partition_config.combine || G.getSecondPartitionIndex(node) == G.getSecondPartitionIndex(target))) - { - max_value = cur_value; - max_block = cur_block; - } - - hash_map[cur_block] = 0; - } endfor - - cluster_sizes[cluster_id[node]] -= G.getNodeWeight(node); - cluster_sizes[max_block] += G.getNodeWeight(node); - change_counter += (cluster_id[node] != max_block); - cluster_id[node] = max_block; - } endfor + // in this case the _matching paramter is not used + // coarse_mappng stores cluster id and the mapping (it is identical) + std::vector hash_map(G.number_of_nodes(),0); + std::vector permutation(G.number_of_nodes()); + std::vector cluster_sizes(G.number_of_nodes()); + cluster_id.resize(G.number_of_nodes()); + + forall_nodes(G, node) { + cluster_sizes[node] = G.getNodeWeight(node); + cluster_id[node] = node; + } endfor + + node_ordering n_ordering; + n_ordering.order_nodes(partition_config, G, permutation); + + for( int j = 0; j < partition_config.label_iterations; j++) { + unsigned int change_counter = 0; + forall_nodes(G, i) { + NodeID node = permutation[i]; + //now move the node to the cluster that is most common in the neighborhood + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + hash_map[cluster_id[target]]+=G.getEdgeWeight(e); + } endfor + + //second sweep for finding max and resetting array + PartitionID max_block = cluster_id[node]; + PartitionID my_block = cluster_id[node]; + + PartitionID max_value = 0; + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID cur_block = cluster_id[target]; + PartitionID cur_value = hash_map[cur_block]; + if((cur_value > max_value || (cur_value == max_value && random_functions::nextBool())) + && (cluster_sizes[cur_block] + G.getNodeWeight(node) < block_upperbound || cur_block == my_block) + && (!partition_config.graph_allready_partitioned || G.getPartitionIndex(node) == G.getPartitionIndex(target)) + && (!partition_config.combine || G.getSecondPartitionIndex(node) == G.getSecondPartitionIndex(target))) + { + max_value = cur_value; + max_block = cur_block; } - remap_cluster_ids( partition_config, G, cluster_id, no_of_blocks); + hash_map[cur_block] = 0; + } endfor + + cluster_sizes[cluster_id[node]] -= G.getNodeWeight(node); + cluster_sizes[max_block] += G.getNodeWeight(node); + change_counter += (cluster_id[node] != max_block); + cluster_id[node] = max_block; + } endfor +} + + remap_cluster_ids( partition_config, G, cluster_id, no_of_blocks); } -void size_constraint_label_propagation::create_coarsemapping(const PartitionConfig & partition_config, +void size_constraint_label_propagation::create_coarsemapping(const PartitionConfig & partition_config, graph_access & G, std::vector & cluster_id, CoarseMapping & coarse_mapping) { - forall_nodes(G, node) { - coarse_mapping[node] = cluster_id[node]; - } endfor + forall_nodes(G, node) { + coarse_mapping[node] = cluster_id[node]; + } endfor } -void size_constraint_label_propagation::remap_cluster_ids(const PartitionConfig & partition_config, +void size_constraint_label_propagation::remap_cluster_ids(const PartitionConfig & partition_config, graph_access & G, std::vector & cluster_id, NodeID & no_of_coarse_vertices, bool apply_to_graph) { - PartitionID cur_no_clusters = 0; - std::unordered_map remap; - forall_nodes(G, node) { - PartitionID cur_cluster = cluster_id[node]; - //check wether we already had that - if( remap.find( cur_cluster ) == remap.end() ) { - remap[cur_cluster] = cur_no_clusters++; - } - - cluster_id[node] = remap[cur_cluster]; - } endfor - - if( apply_to_graph ) { - forall_nodes(G, node) { - G.setPartitionIndex(node, cluster_id[node]); - } endfor - G.set_partition_count(cur_no_clusters); - } - - no_of_coarse_vertices = cur_no_clusters; + PartitionID cur_no_clusters = 0; + std::unordered_map remap; + forall_nodes(G, node) { + PartitionID cur_cluster = cluster_id[node]; + //check wether we already had that + if( remap.find( cur_cluster ) == remap.end() ) { + remap[cur_cluster] = cur_no_clusters++; + } + + cluster_id[node] = remap[cur_cluster]; + } endfor + + if( apply_to_graph ) { + forall_nodes(G, node) { + G.setPartitionIndex(node, cluster_id[node]); + } endfor + G.set_partition_count(cur_no_clusters); + } + + no_of_coarse_vertices = cur_no_clusters; +} } - diff --git a/parallel/modified_kahip/lib/partition/coarsening/clustering/size_constraint_label_propagation.h b/parallel/modified_kahip/lib/partition/coarsening/clustering/size_constraint_label_propagation.h index afea2d79..0e46df8a 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/clustering/size_constraint_label_propagation.h +++ b/parallel/modified_kahip/lib/partition/coarsening/clustering/size_constraint_label_propagation.h @@ -10,96 +10,96 @@ #include #include "../matching/matching.h" - +namespace kahip::modified { struct ensemble_pair { - PartitionID n; // number of nodes in the graph - PartitionID lhs; - PartitionID rhs; + PartitionID n; // number of nodes in the graph + PartitionID lhs; + PartitionID rhs; }; struct compare_ensemble_pair { - bool operator()(const ensemble_pair pair_a, const ensemble_pair pair_b) const { - bool eq = (pair_a.lhs == pair_b.lhs && pair_a.rhs == pair_b.rhs); - return eq; - } + bool operator()(const ensemble_pair pair_a, const ensemble_pair pair_b) const { + bool eq = (pair_a.lhs == pair_b.lhs && pair_a.rhs == pair_b.rhs); + return eq; + } }; struct hash_ensemble_pair{ - size_t operator()(const ensemble_pair pair) const { - return pair.lhs*pair.n + pair.rhs; - } + size_t operator()(const ensemble_pair pair) const { + return pair.lhs*pair.n + pair.rhs; + } }; struct data_ensemble_pair { - NodeID mapping; + NodeID mapping; - data_ensemble_pair() { - mapping = 0; - } + data_ensemble_pair() { + mapping = 0; + } }; -typedef std::unordered_map hash_ensemble; class size_constraint_label_propagation : public matching { - public: - size_constraint_label_propagation(); - virtual ~size_constraint_label_propagation(); - - void match(const PartitionConfig & config, - graph_access & G, - Matching & _matching, - CoarseMapping & coarse_mapping, - NodeID & no_of_coarse_vertices, - NodePermutationMap & permutation); - - - void ensemble_clusterings(const PartitionConfig & config, - graph_access & G, - Matching & _matching, - CoarseMapping & coarse_mapping, - NodeID & no_of_coarse_vertices, - NodePermutationMap & permutation); - - void ensemble_two_clusterings( graph_access & G, - std::vector & lhs, - std::vector & rhs, - std::vector< NodeID > & output, - NodeID & no_of_coarse_vertices); - - void match_internal(const PartitionConfig & config, - graph_access & G, - Matching & _matching, - CoarseMapping & coarse_mapping, - NodeID & no_of_coarse_vertices, - NodePermutationMap & permutation); - - void remap_cluster_ids(const PartitionConfig & partition_config, - graph_access & G, - std::vector & cluster_id, - NodeID & no_of_coarse_vertices, - bool apply_to_graph = false); - - void create_coarsemapping(const PartitionConfig & partition_config, - graph_access & G, - std::vector & cluster_id, - CoarseMapping & coarse_mapping); - - void label_propagation(const PartitionConfig & partition_config, - graph_access & G, - const NodeWeight & block_upperbound, - std::vector & cluster_id, // output paramter - NodeID & number_of_blocks); // output parameter - - void label_propagation(const PartitionConfig & partition_config, - graph_access & G, - std::vector & cluster_id, - NodeID & number_of_blocks ); +public: + size_constraint_label_propagation(); + virtual ~size_constraint_label_propagation(); + + void match(const PartitionConfig & config, + graph_access & G, + Matching & _matching, + CoarseMapping & coarse_mapping, + NodeID & no_of_coarse_vertices, + NodePermutationMap & permutation); + + + void ensemble_clusterings(const PartitionConfig & config, + graph_access & G, + Matching & _matching, + CoarseMapping & coarse_mapping, + NodeID & no_of_coarse_vertices, + NodePermutationMap & permutation); + + void ensemble_two_clusterings( graph_access & G, + std::vector & lhs, + std::vector & rhs, + std::vector< NodeID > & output, + NodeID & no_of_coarse_vertices); + + void match_internal(const PartitionConfig & config, + graph_access & G, + Matching & _matching, + CoarseMapping & coarse_mapping, + NodeID & no_of_coarse_vertices, + NodePermutationMap & permutation); + + void remap_cluster_ids(const PartitionConfig & partition_config, + graph_access & G, + std::vector & cluster_id, + NodeID & no_of_coarse_vertices, + bool apply_to_graph = false); + + void create_coarsemapping(const PartitionConfig & partition_config, + graph_access & G, + std::vector & cluster_id, + CoarseMapping & coarse_mapping); + + void label_propagation(const PartitionConfig & partition_config, + graph_access & G, + const NodeWeight & block_upperbound, + std::vector & cluster_id, // output paramter + NodeID & number_of_blocks); // output parameter + + void label_propagation(const PartitionConfig & partition_config, + graph_access & G, + std::vector & cluster_id, + NodeID & number_of_blocks ); }; - +} #endif /* end of include guard: SIZE_CONSTRAINT_LABEL_PROPAGATION_7SVLBKKT */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/coarsening.cpp b/parallel/modified_kahip/lib/partition/coarsening/coarsening.cpp index fc0d8d91..2b3e8777 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/coarsening.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/coarsening.cpp @@ -18,7 +18,7 @@ #include "matching/gpa/gpa_matching.h" #include "matching/random_matching.h" #include "stop_rules/stop_rules.h" - +namespace kahip::modified { coarsening::coarsening() { } @@ -29,68 +29,68 @@ coarsening::~coarsening() { void coarsening::perform_coarsening(const PartitionConfig & partition_config, graph_access & G, graph_hierarchy & hierarchy) { - NodeID no_of_coarser_vertices = G.number_of_nodes(); - NodeID no_of_finer_vertices = G.number_of_nodes(); - - edge_ratings rating(partition_config); - CoarseMapping* coarse_mapping = NULL; - - graph_access* finer = &G; - matching* edge_matcher = NULL; - contraction* contracter = new contraction(); - PartitionConfig copy_of_partition_config = partition_config; - - stop_rule* coarsening_stop_rule = NULL; - if(partition_config.stop_rule == STOP_RULE_SIMPLE) { - coarsening_stop_rule = new simple_stop_rule(copy_of_partition_config, G.number_of_nodes()); - } else if(partition_config.stop_rule == STOP_RULE_MULTIPLE_K) { - coarsening_stop_rule = new multiple_k_stop_rule(copy_of_partition_config, G.number_of_nodes()); - } else { - coarsening_stop_rule = new strong_stop_rule(copy_of_partition_config, G.number_of_nodes()); - } - - coarsening_configurator coarsening_config; - - unsigned int level = 0; - bool contraction_stop = false; - do { - graph_access* coarser = new graph_access(); - coarse_mapping = new CoarseMapping(); - Matching edge_matching; - NodePermutationMap permutation; - - coarsening_config.configure_coarsening(copy_of_partition_config, &edge_matcher, level); - rating.rate(*finer, level); - - edge_matcher->match(copy_of_partition_config, *finer, edge_matching, - *coarse_mapping, no_of_coarser_vertices, permutation); - - delete edge_matcher; - - if(partition_config.graph_allready_partitioned) { - contracter->contract_partitioned(copy_of_partition_config, *finer, *coarser, edge_matching, - *coarse_mapping, no_of_coarser_vertices, permutation); - } else { - contracter->contract(copy_of_partition_config, *finer, *coarser, edge_matching, - *coarse_mapping, no_of_coarser_vertices, permutation); - } - - hierarchy.push_back(finer, coarse_mapping); - contraction_stop = coarsening_stop_rule->stop(no_of_finer_vertices, no_of_coarser_vertices); - - no_of_finer_vertices = no_of_coarser_vertices; - PRINT(std::cout << "no of coarser vertices " << no_of_coarser_vertices - << " and no of edges " << coarser->number_of_edges() << std::endl;) - - finer = coarser; - - level++; - } while( contraction_stop ); - - hierarchy.push_back(finer, NULL); // append the last created level - - delete contracter; - delete coarsening_stop_rule; -} + NodeID no_of_coarser_vertices = G.number_of_nodes(); + NodeID no_of_finer_vertices = G.number_of_nodes(); + + edge_ratings rating(partition_config); + CoarseMapping* coarse_mapping = NULL; + + graph_access* finer = &G; + matching* edge_matcher = NULL; + contraction* contracter = new contraction(); + PartitionConfig copy_of_partition_config = partition_config; + + stop_rule* coarsening_stop_rule = NULL; + if(partition_config.stop_rule == STOP_RULE_SIMPLE) { + coarsening_stop_rule = new simple_stop_rule(copy_of_partition_config, G.number_of_nodes()); + } else if(partition_config.stop_rule == STOP_RULE_MULTIPLE_K) { + coarsening_stop_rule = new multiple_k_stop_rule(copy_of_partition_config, G.number_of_nodes()); + } else { + coarsening_stop_rule = new strong_stop_rule(copy_of_partition_config, G.number_of_nodes()); + } + + coarsening_configurator coarsening_config; + + unsigned int level = 0; + bool contraction_stop = false; + do { + graph_access* coarser = new graph_access(); + coarse_mapping = new CoarseMapping(); + Matching edge_matching; + NodePermutationMap permutation; + + coarsening_config.configure_coarsening(copy_of_partition_config, &edge_matcher, level); + rating.rate(*finer, level); + + edge_matcher->match(copy_of_partition_config, *finer, edge_matching, + *coarse_mapping, no_of_coarser_vertices, permutation); + delete edge_matcher; + + if(partition_config.graph_allready_partitioned) { + contracter->contract_partitioned(copy_of_partition_config, *finer, *coarser, edge_matching, + *coarse_mapping, no_of_coarser_vertices, permutation); + } else { + contracter->contract(copy_of_partition_config, *finer, *coarser, edge_matching, + *coarse_mapping, no_of_coarser_vertices, permutation); + } + + hierarchy.push_back(finer, coarse_mapping); + contraction_stop = coarsening_stop_rule->stop(no_of_finer_vertices, no_of_coarser_vertices); + + no_of_finer_vertices = no_of_coarser_vertices; + PRINT(std::cout << "no of coarser vertices " << no_of_coarser_vertices + << " and no of edges " << coarser->number_of_edges() << std::endl;) + + finer = coarser; + + level++; + } while( contraction_stop ); + + hierarchy.push_back(finer, NULL); // append the last created level + + delete contracter; + delete coarsening_stop_rule; +} +} diff --git a/parallel/modified_kahip/lib/partition/coarsening/coarsening.h b/parallel/modified_kahip/lib/partition/coarsening/coarsening.h index a6f98a53..2a133bb8 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/coarsening.h +++ b/parallel/modified_kahip/lib/partition/coarsening/coarsening.h @@ -11,13 +11,13 @@ #include "data_structure/graph_access.h" #include "data_structure/graph_hierarchy.h" #include "partition_config.h" - +namespace kahip::modified { class coarsening { public: - coarsening (); - virtual ~coarsening (); + coarsening (); + virtual ~coarsening (); - void perform_coarsening(const PartitionConfig & config, graph_access & G, graph_hierarchy & hierarchy); + void perform_coarsening(const PartitionConfig & config, graph_access & G, graph_hierarchy & hierarchy); }; - +} #endif /* end of include guard: COARSENING_UU97ZBTR */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/coarsening_configurator.h b/parallel/modified_kahip/lib/partition/coarsening/coarsening_configurator.h index e8d7b309..d803efd0 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/coarsening_configurator.h +++ b/parallel/modified_kahip/lib/partition/coarsening/coarsening_configurator.h @@ -16,47 +16,48 @@ #include "matching/random_matching.h" #include "clustering/size_constraint_label_propagation.h" #include "stop_rules/stop_rules.h" - +namespace kahip::modified { class coarsening_configurator { - public: - coarsening_configurator( ) {}; - virtual ~coarsening_configurator() {}; +public: + coarsening_configurator( ) {}; + virtual ~coarsening_configurator() {}; - void configure_coarsening(const PartitionConfig & partition_config, - matching** edge_matcher, - unsigned level); + void configure_coarsening(const PartitionConfig & partition_config, + matching** edge_matcher, + unsigned level); }; -inline void coarsening_configurator::configure_coarsening( const PartitionConfig & partition_config, - matching** edge_matcher, +inline void coarsening_configurator::configure_coarsening( const PartitionConfig & partition_config, + matching** edge_matcher, unsigned level) { switch(partition_config.matching_type) { - case MATCHING_RANDOM: + case MATCHING_RANDOM: *edge_matcher = new random_matching(); - break; + break; case MATCHING_GPA: *edge_matcher = new gpa_matching(); - PRINT(std::cout << "gpa matching" << std::endl;) - break; + PRINT(std::cout << "gpa matching" << std::endl;) + break; case MATCHING_RANDOM_GPA: PRINT(std::cout << "random gpa matching" << std::endl;) *edge_matcher = new gpa_matching(); - break; - case CLUSTER_COARSENING: + break; + case CLUSTER_COARSENING: PRINT(std::cout << "cluster_coarsening" << std::endl;) *edge_matcher = new size_constraint_label_propagation(); - break; + break; } - if( partition_config.matching_type == MATCHING_RANDOM_GPA + if( partition_config.matching_type == MATCHING_RANDOM_GPA && level < partition_config.aggressive_random_levels) { delete *edge_matcher; PRINT(std::cout << "random matching" << std::endl;) *edge_matcher = new random_matching(); - } + } +} } #endif /* end of include guard: COARSENING_CONFIGURATOR_8UJ78WYS */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/contraction.cpp b/parallel/modified_kahip/lib/partition/coarsening/contraction.cpp index 75a1b2bb..e72dc103 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/contraction.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/contraction.cpp @@ -8,7 +8,7 @@ #include "contraction.h" #include "../uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h" #include "macros_assertions.h" - +namespace kahip::modified { contraction::contraction() { } @@ -26,187 +26,187 @@ void contraction::contract(const PartitionConfig & partition_config, const NodeID & no_of_coarse_vertices, const NodePermutationMap & permutation) const { - if(partition_config.matching_type == CLUSTER_COARSENING) { - return contract_clustering(partition_config, G, coarser, edge_matching, coarse_mapping, no_of_coarse_vertices, permutation); - } - - if(partition_config.combine) { - coarser.resizeSecondPartitionIndex(no_of_coarse_vertices); - } - - std::vector new_edge_targets(G.number_of_edges()); - forall_edges(G, e) { - new_edge_targets[e] = coarse_mapping[G.getEdgeTarget(e)]; - } endfor - - std::vector edge_positions(no_of_coarse_vertices, UNDEFINED_EDGE); - - //we dont know the number of edges jet, so we use the old number for - //construction of the coarser graph and then resize the field according - //to the number of edges we really got - coarser.start_construction(no_of_coarse_vertices, G.number_of_edges()); - - NodeID cur_no_vertices = 0; - - forall_nodes(G, n) { - NodeID node = permutation[n]; - //we look only at the coarser nodes - if(coarse_mapping[node] != cur_no_vertices) - continue; - - NodeID coarseNode = coarser.new_node(); - coarser.setNodeWeight(coarseNode, G.getNodeWeight(node)); - - if(partition_config.combine) { - coarser.setSecondPartitionIndex(coarseNode, G.getSecondPartitionIndex(node)); - } - - // do something with all outgoing edges (in auxillary graph) - forall_out_edges(G, e, node) { - visit_edge(G, coarser, edge_positions, coarseNode, e, new_edge_targets); - } endfor - - //this node was really matched - NodeID matched_neighbor = edge_matching[node]; - if(node != matched_neighbor) { - //update weight of coarser node - NodeWeight new_coarse_weight = G.getNodeWeight(node) + G.getNodeWeight(matched_neighbor); - coarser.setNodeWeight(coarseNode, new_coarse_weight); - - forall_out_edges(G, e, matched_neighbor) { - visit_edge(G, coarser, edge_positions, coarseNode, e, new_edge_targets); - } endfor - } - forall_out_edges(coarser, e, coarseNode) { - edge_positions[coarser.getEdgeTarget(e)] = UNDEFINED_EDGE; - } endfor - - cur_no_vertices++; - } endfor - - ASSERT_RANGE_EQ(edge_positions, 0, edge_positions.size(), UNDEFINED_EDGE); - ASSERT_EQ(no_of_coarse_vertices, cur_no_vertices); - - //this also resizes the edge fields ... - coarser.finish_construction(); + if(partition_config.matching_type == CLUSTER_COARSENING) { + return contract_clustering(partition_config, G, coarser, edge_matching, coarse_mapping, no_of_coarse_vertices, permutation); + } + + if(partition_config.combine) { + coarser.resizeSecondPartitionIndex(no_of_coarse_vertices); + } + + std::vector new_edge_targets(G.number_of_edges()); + forall_edges(G, e) { + new_edge_targets[e] = coarse_mapping[G.getEdgeTarget(e)]; + } endfor + + std::vector edge_positions(no_of_coarse_vertices, UNDEFINED_EDGE); + + //we dont know the number of edges jet, so we use the old number for + //construction of the coarser graph and then resize the field according + //to the number of edges we really got + coarser.start_construction(no_of_coarse_vertices, G.number_of_edges()); + + NodeID cur_no_vertices = 0; + + forall_nodes(G, n) { + NodeID node = permutation[n]; + //we look only at the coarser nodes + if(coarse_mapping[node] != cur_no_vertices) + continue; + + NodeID coarseNode = coarser.new_node(); + coarser.setNodeWeight(coarseNode, G.getNodeWeight(node)); + + if(partition_config.combine) { + coarser.setSecondPartitionIndex(coarseNode, G.getSecondPartitionIndex(node)); + } + + // do something with all outgoing edges (in auxillary graph) + forall_out_edges(G, e, node) { + visit_edge(G, coarser, edge_positions, coarseNode, e, new_edge_targets); + } endfor + + //this node was really matched + NodeID matched_neighbor = edge_matching[node]; + if(node != matched_neighbor) { + //update weight of coarser node + NodeWeight new_coarse_weight = G.getNodeWeight(node) + G.getNodeWeight(matched_neighbor); + coarser.setNodeWeight(coarseNode, new_coarse_weight); + + forall_out_edges(G, e, matched_neighbor) { + visit_edge(G, coarser, edge_positions, coarseNode, e, new_edge_targets); + } endfor +} + forall_out_edges(coarser, e, coarseNode) { + edge_positions[coarser.getEdgeTarget(e)] = UNDEFINED_EDGE; + } endfor + + cur_no_vertices++; + } endfor + + ASSERT_RANGE_EQ(edge_positions, 0, edge_positions.size(), UNDEFINED_EDGE); + ASSERT_EQ(no_of_coarse_vertices, cur_no_vertices); + + //this also resizes the edge fields ... + coarser.finish_construction(); } -void contraction::contract_clustering(const PartitionConfig & partition_config, - graph_access & G, - graph_access & coarser, +void contraction::contract_clustering(const PartitionConfig & partition_config, + graph_access & G, + graph_access & coarser, const Matching & edge_matching, const CoarseMapping & coarse_mapping, const NodeID & no_of_coarse_vertices, const NodePermutationMap & permutation) const { - if(partition_config.combine) { - coarser.resizeSecondPartitionIndex(no_of_coarse_vertices); - } + if(partition_config.combine) { + coarser.resizeSecondPartitionIndex(no_of_coarse_vertices); + } - //save partition map -- important if the graph is allready partitioned - std::vector< int > partition_map(G.number_of_nodes()); - int k = G.get_partition_count(); - forall_nodes(G, node) { - partition_map[node] = G.getPartitionIndex(node); - G.setPartitionIndex(node, coarse_mapping[node]); - } endfor + //save partition map -- important if the graph is allready partitioned + std::vector< int > partition_map(G.number_of_nodes()); + int k = G.get_partition_count(); + forall_nodes(G, node) { + partition_map[node] = G.getPartitionIndex(node); + G.setPartitionIndex(node, coarse_mapping[node]); + } endfor - G.set_partition_count(no_of_coarse_vertices); + G.set_partition_count(no_of_coarse_vertices); - complete_boundary bnd(&G); - bnd.build(); - bnd.getUnderlyingQuotientGraph(coarser); + complete_boundary bnd(&G); + bnd.build(); + bnd.getUnderlyingQuotientGraph(coarser); - G.set_partition_count(k); - forall_nodes(G, node) { - G.setPartitionIndex(node, partition_map[node]); - coarser.setPartitionIndex(coarse_mapping[node], G.getPartitionIndex(node)); + G.set_partition_count(k); + forall_nodes(G, node) { + G.setPartitionIndex(node, partition_map[node]); + coarser.setPartitionIndex(coarse_mapping[node], G.getPartitionIndex(node)); - if(partition_config.combine) { - coarser.setSecondPartitionIndex(coarse_mapping[node], G.getSecondPartitionIndex(node)); - } + if(partition_config.combine) { + coarser.setSecondPartitionIndex(coarse_mapping[node], G.getSecondPartitionIndex(node)); + } - } endfor + } endfor } -// for documentation see technical reports of christian schulz -void contraction::contract_partitioned(const PartitionConfig & partition_config, - graph_access & G, - graph_access & coarser, +// for documentation see technical reports of christian schulz +void contraction::contract_partitioned(const PartitionConfig & partition_config, + graph_access & G, + graph_access & coarser, const Matching & edge_matching, const CoarseMapping & coarse_mapping, const NodeID & no_of_coarse_vertices, const NodePermutationMap & permutation) const { - - if(partition_config.matching_type == CLUSTER_COARSENING) { - return contract_clustering(partition_config, G, coarser, edge_matching, coarse_mapping, no_of_coarse_vertices, permutation); - } - - - std::vector new_edge_targets(G.number_of_edges()); - forall_edges(G, e) { - new_edge_targets[e] = coarse_mapping[G.getEdgeTarget(e)]; - } endfor - - std::vector edge_positions(no_of_coarse_vertices, UNDEFINED_EDGE); - - //we dont know the number of edges jet, so we use the old number for - //construction of the coarser graph and then resize the field according - //to the number of edges we really got - coarser.set_partition_count(G.get_partition_count()); - coarser.start_construction(no_of_coarse_vertices, G.number_of_edges()); - - if(partition_config.combine) { - coarser.resizeSecondPartitionIndex(no_of_coarse_vertices); - } - - NodeID cur_no_vertices = 0; - - PRINT(std::cout << "contracting a partitioned graph" << std::endl;) - forall_nodes(G, n) { - NodeID node = permutation[n]; - //we look only at the coarser nodes - if(coarse_mapping[node] != cur_no_vertices) - continue; - - NodeID coarseNode = coarser.new_node(); - coarser.setNodeWeight(coarseNode, G.getNodeWeight(node)); - coarser.setPartitionIndex(coarseNode, G.getPartitionIndex(node)); - - if(partition_config.combine) { - coarser.setSecondPartitionIndex(coarseNode, G.getSecondPartitionIndex(node)); - } - // do something with all outgoing edges (in auxillary graph) - forall_out_edges(G, e, node) { - visit_edge(G, coarser, edge_positions, coarseNode, e, new_edge_targets); - } endfor - - //this node was really matched - NodeID matched_neighbor = edge_matching[node]; - if(node != matched_neighbor) { - //update weight of coarser node - NodeWeight new_coarse_weight = G.getNodeWeight(node) + G.getNodeWeight(matched_neighbor); - coarser.setNodeWeight(coarseNode, new_coarse_weight); - - forall_out_edges(G, e, matched_neighbor) { - visit_edge(G, coarser, edge_positions, coarseNode, e, new_edge_targets); - } endfor - } - forall_out_edges(coarser, e, coarseNode) { - edge_positions[coarser.getEdgeTarget(e)] = UNDEFINED_EDGE; - } endfor - - cur_no_vertices++; - } endfor - - ASSERT_RANGE_EQ(edge_positions, 0, edge_positions.size(), UNDEFINED_EDGE); - ASSERT_EQ(no_of_coarse_vertices, cur_no_vertices); - - //this also resizes the edge fields ... - coarser.finish_construction(); + + if(partition_config.matching_type == CLUSTER_COARSENING) { + return contract_clustering(partition_config, G, coarser, edge_matching, coarse_mapping, no_of_coarse_vertices, permutation); + } + + + std::vector new_edge_targets(G.number_of_edges()); + forall_edges(G, e) { + new_edge_targets[e] = coarse_mapping[G.getEdgeTarget(e)]; + } endfor + + std::vector edge_positions(no_of_coarse_vertices, UNDEFINED_EDGE); + + //we dont know the number of edges jet, so we use the old number for + //construction of the coarser graph and then resize the field according + //to the number of edges we really got + coarser.set_partition_count(G.get_partition_count()); + coarser.start_construction(no_of_coarse_vertices, G.number_of_edges()); + + if(partition_config.combine) { + coarser.resizeSecondPartitionIndex(no_of_coarse_vertices); + } + + NodeID cur_no_vertices = 0; + + PRINT(std::cout << "contracting a partitioned graph" << std::endl;) + forall_nodes(G, n) { + NodeID node = permutation[n]; + //we look only at the coarser nodes + if(coarse_mapping[node] != cur_no_vertices) + continue; + + NodeID coarseNode = coarser.new_node(); + coarser.setNodeWeight(coarseNode, G.getNodeWeight(node)); + coarser.setPartitionIndex(coarseNode, G.getPartitionIndex(node)); + + if(partition_config.combine) { + coarser.setSecondPartitionIndex(coarseNode, G.getSecondPartitionIndex(node)); + } + // do something with all outgoing edges (in auxillary graph) + forall_out_edges(G, e, node) { + visit_edge(G, coarser, edge_positions, coarseNode, e, new_edge_targets); + } endfor + + //this node was really matched + NodeID matched_neighbor = edge_matching[node]; + if(node != matched_neighbor) { + //update weight of coarser node + NodeWeight new_coarse_weight = G.getNodeWeight(node) + G.getNodeWeight(matched_neighbor); + coarser.setNodeWeight(coarseNode, new_coarse_weight); + + forall_out_edges(G, e, matched_neighbor) { + visit_edge(G, coarser, edge_positions, coarseNode, e, new_edge_targets); + } endfor } + forall_out_edges(coarser, e, coarseNode) { + edge_positions[coarser.getEdgeTarget(e)] = UNDEFINED_EDGE; + } endfor + + cur_no_vertices++; + } endfor + + ASSERT_RANGE_EQ(edge_positions, 0, edge_positions.size(), UNDEFINED_EDGE); + ASSERT_EQ(no_of_coarse_vertices, cur_no_vertices); + //this also resizes the edge fields ... + coarser.finish_construction(); +} +} diff --git a/parallel/modified_kahip/lib/partition/coarsening/contraction.h b/parallel/modified_kahip/lib/partition/coarsening/contraction.h index 39b8732f..e9003cb9 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/contraction.h +++ b/parallel/modified_kahip/lib/partition/coarsening/contraction.h @@ -12,47 +12,47 @@ #include "data_structure/graph_access.h" #include "matching/matching.h" #include "partition_config.h" - +namespace kahip::modified { typedef NodeID Regions; class contraction { - public: - contraction(); - virtual ~contraction(); - - void contract(const PartitionConfig & partition_config, - graph_access & finer, - graph_access & coarser, - const Matching & edge_matching, - const CoarseMapping & coarse_mapping, - const NodeID & no_of_coarse_vertices, - const NodePermutationMap & permutation) const; - - void contract_clustering(const PartitionConfig & partition_config, - graph_access & finer, - graph_access & coarser, - const Matching & edge_matching, - const CoarseMapping & coarse_mapping, - const NodeID & no_of_coarse_vertices, - const NodePermutationMap & permutation) const; - - - void contract_partitioned(const PartitionConfig & partition_config, - graph_access & G, - graph_access & coarser, - const Matching & edge_matching, - const CoarseMapping & coarse_mapping, - const NodeID & no_of_coarse_vertices, - const NodePermutationMap & permutation) const; - - private: - // visits an edge in G (and auxillary graph) and updates/creates and edge in coarser graph - void visit_edge(graph_access & G, - graph_access & coarser, - std::vector & edge_positions, - const NodeID coarseNode, - const EdgeID e, - const std::vector & new_edge_targets) const; +public: + contraction(); + virtual ~contraction(); + + void contract(const PartitionConfig & partition_config, + graph_access & finer, + graph_access & coarser, + const Matching & edge_matching, + const CoarseMapping & coarse_mapping, + const NodeID & no_of_coarse_vertices, + const NodePermutationMap & permutation) const; + + void contract_clustering(const PartitionConfig & partition_config, + graph_access & finer, + graph_access & coarser, + const Matching & edge_matching, + const CoarseMapping & coarse_mapping, + const NodeID & no_of_coarse_vertices, + const NodePermutationMap & permutation) const; + + + void contract_partitioned(const PartitionConfig & partition_config, + graph_access & G, + graph_access & coarser, + const Matching & edge_matching, + const CoarseMapping & coarse_mapping, + const NodeID & no_of_coarse_vertices, + const NodePermutationMap & permutation) const; + +private: + // visits an edge in G (and auxillary graph) and updates/creates and edge in coarser graph + void visit_edge(graph_access & G, + graph_access & coarser, + std::vector & edge_positions, + const NodeID coarseNode, + const EdgeID e, + const std::vector & new_edge_targets) const; }; @@ -80,7 +80,7 @@ inline void contraction::visit_edge(graph_access & G, coarser.setEdgeWeight(edge_pos, new_edge_weight); } } - +} #endif /* end of include guard: CONTRACTION_VIXZ9K0F */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/edge_rating/edge_ratings.cpp b/parallel/modified_kahip/lib/partition/coarsening/edge_rating/edge_ratings.cpp index c03b7fa5..1f3b4cb3 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/edge_rating/edge_ratings.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/edge_rating/edge_ratings.cpp @@ -10,7 +10,7 @@ #include "edge_ratings.h" #include "partition_config.h" #include "random_functions.h" - +namespace kahip::modified { edge_ratings::edge_ratings(const PartitionConfig & _partition_config) : partition_config(_partition_config){ } @@ -20,168 +20,169 @@ edge_ratings::~edge_ratings() { } void edge_ratings::rate(graph_access & G, unsigned level) { - //rate the edges - if(level == 0 && partition_config.first_level_random_matching) { - return; - } else if(partition_config.matching_type == MATCHING_RANDOM_GPA && level < partition_config.aggressive_random_levels) { - return; - } - if(level == 0 && partition_config.rate_first_level_inner_outer && - partition_config.edge_rating != EXPANSIONSTAR2ALGDIST ) { - - rate_inner_outer(G); - - } else if(partition_config.matching_type != MATCHING_RANDOM) { - switch(partition_config.edge_rating) { - case EXPANSIONSTAR: - rate_expansion_star(G); - break; - case PSEUDOGEOM: - rate_pseudogeom(G); - break; - case EXPANSIONSTAR2: - rate_expansion_star_2(G); - break; - case EXPANSIONSTAR2ALGDIST: - rate_expansion_star_2_algdist(G); - break; - case WEIGHT: - break; - } - } + //rate the edges + if(level == 0 && partition_config.first_level_random_matching) { + return; + } else if(partition_config.matching_type == MATCHING_RANDOM_GPA && level < partition_config.aggressive_random_levels) { + return; + } + if(level == 0 && partition_config.rate_first_level_inner_outer && + partition_config.edge_rating != EXPANSIONSTAR2ALGDIST ) { + + rate_inner_outer(G); + + } else if(partition_config.matching_type != MATCHING_RANDOM) { + switch(partition_config.edge_rating) { + case EXPANSIONSTAR: + rate_expansion_star(G); + break; + case PSEUDOGEOM: + rate_pseudogeom(G); + break; + case EXPANSIONSTAR2: + rate_expansion_star_2(G); + break; + case EXPANSIONSTAR2ALGDIST: + rate_expansion_star_2_algdist(G); + break; + case WEIGHT: + break; + } + } } //simd implementation is possible void edge_ratings::compute_algdist(graph_access & G, std::vector & dist) { - for( unsigned R = 0; R < 3; R++) { - std::vector prev(G.number_of_nodes(), 0); - forall_nodes(G, node) { - prev[node] = random_functions::nextDouble(-0.5,0.5); - } endfor - - std::vector next(G.number_of_nodes(), 0); - float w = 0.5; - - for( unsigned k = 0; k < 7; k++) { - forall_nodes(G, node) { - next[node] = 0; - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - next[node] += prev[target] * G.getEdgeWeight(e); - } endfor - - float wdegree = G.getWeightedNodeDegree(node); - if(wdegree > 0) { - next[node] /= (float)wdegree; - - } - } endfor - - forall_nodes(G, node) { - prev[node] = (1-w)*prev[node] + w*next[node]; - } endfor - - } - - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - //dist[e] = max(dist[e],fabs(prev[node] - prev[target])); - dist[e] += fabs(prev[node] - prev[target]) / 7.0; - } endfor - } endfor + for( unsigned R = 0; R < 3; R++) { + std::vector prev(G.number_of_nodes(), 0); + forall_nodes(G, node) { + prev[node] = random_functions::nextDouble(-0.5,0.5); + } endfor + + std::vector next(G.number_of_nodes(), 0); + float w = 0.5; + + for( unsigned k = 0; k < 7; k++) { + forall_nodes(G, node) { + next[node] = 0; + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + next[node] += prev[target] * G.getEdgeWeight(e); + } endfor + + float wdegree = G.getWeightedNodeDegree(node); + if(wdegree > 0) { + next[node] /= (float)wdegree; + } + } endfor - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - dist[e] += 0.0001; - } endfor - } endfor + forall_nodes(G, node) { + prev[node] = (1-w)*prev[node] + w*next[node]; + } endfor + +} + + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + //dist[e] = max(dist[e],fabs(prev[node] - prev[target])); + dist[e] += fabs(prev[node] - prev[target]) / 7.0; + } endfor +} endfor +} + + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + dist[e] += 0.0001; + } endfor +} endfor } void edge_ratings::rate_expansion_star_2_algdist(graph_access & G) { - std::vector dist(G.number_of_edges(), 0); - compute_algdist(G, dist); + std::vector dist(G.number_of_edges(), 0); + compute_algdist(G, dist); - forall_nodes(G,n) { - NodeWeight sourceWeight = G.getNodeWeight(n); - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); - NodeWeight targetWeight = G.getNodeWeight(targetNode); - EdgeWeight edgeWeight = G.getEdgeWeight(e); + forall_nodes(G,n) { + NodeWeight sourceWeight = G.getNodeWeight(n); + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); + NodeWeight targetWeight = G.getNodeWeight(targetNode); + EdgeWeight edgeWeight = G.getEdgeWeight(e); - EdgeRatingType rating = 1.0*edgeWeight*edgeWeight / (targetWeight*sourceWeight*dist[e]); - G.setEdgeRating(e, rating); - } endfor - } endfor + EdgeRatingType rating = 1.0*edgeWeight*edgeWeight / (targetWeight*sourceWeight*dist[e]); + G.setEdgeRating(e, rating); + } endfor +} endfor } void edge_ratings::rate_expansion_star_2(graph_access & G) { - forall_nodes(G,n) { - NodeWeight sourceWeight = G.getNodeWeight(n); - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); - NodeWeight targetWeight = G.getNodeWeight(targetNode); - EdgeWeight edgeWeight = G.getEdgeWeight(e); - - EdgeRatingType rating = 1.0*edgeWeight*edgeWeight / (targetWeight*sourceWeight); - G.setEdgeRating(e, rating); - } endfor - } endfor + forall_nodes(G,n) { + NodeWeight sourceWeight = G.getNodeWeight(n); + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); + NodeWeight targetWeight = G.getNodeWeight(targetNode); + EdgeWeight edgeWeight = G.getEdgeWeight(e); + + EdgeRatingType rating = 1.0*edgeWeight*edgeWeight / (targetWeight*sourceWeight); + G.setEdgeRating(e, rating); + } endfor +} endfor } void edge_ratings::rate_inner_outer(graph_access & G) { - forall_nodes(G,n) { + forall_nodes(G,n) { #ifndef WALSHAWMH - EdgeWeight sourceDegree = G.getWeightedNodeDegree(n); + EdgeWeight sourceDegree = G.getWeightedNodeDegree(n); #else - EdgeWeight sourceDegree = G.getNodeDegree(n); + EdgeWeight sourceDegree = G.getNodeDegree(n); #endif - if(sourceDegree == 0) continue; + if(sourceDegree == 0) continue; - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); #ifndef WALSHAWMH - EdgeWeight targetDegree = G.getWeightedNodeDegree(targetNode); + EdgeWeight targetDegree = G.getWeightedNodeDegree(targetNode); #else - EdgeWeight targetDegree = G.getNodeDegree(targetNode); + EdgeWeight targetDegree = G.getNodeDegree(targetNode); #endif - EdgeWeight edgeWeight = G.getEdgeWeight(e); - EdgeRatingType rating = 1.0*edgeWeight/(sourceDegree+targetDegree - edgeWeight); - G.setEdgeRating(e, rating); - } endfor - } endfor + EdgeWeight edgeWeight = G.getEdgeWeight(e); + EdgeRatingType rating = 1.0*edgeWeight/(sourceDegree+targetDegree - edgeWeight); + G.setEdgeRating(e, rating); + } endfor +} endfor } void edge_ratings::rate_expansion_star(graph_access & G) { - forall_nodes(G,n) { - NodeWeight sourceWeight = G.getNodeWeight(n); - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); - NodeWeight targetWeight = G.getNodeWeight(targetNode); - EdgeWeight edgeWeight = G.getEdgeWeight(e); - - EdgeRatingType rating = 1.0 * edgeWeight / (targetWeight*sourceWeight); - G.setEdgeRating(e, rating); - } endfor - } endfor + forall_nodes(G,n) { + NodeWeight sourceWeight = G.getNodeWeight(n); + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); + NodeWeight targetWeight = G.getNodeWeight(targetNode); + EdgeWeight edgeWeight = G.getEdgeWeight(e); + + EdgeRatingType rating = 1.0 * edgeWeight / (targetWeight*sourceWeight); + G.setEdgeRating(e, rating); + } endfor +} endfor } void edge_ratings::rate_pseudogeom(graph_access & G) { - forall_nodes(G,n) { - NodeWeight sourceWeight = G.getNodeWeight(n); - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); - NodeWeight targetWeight = G.getNodeWeight(targetNode); - EdgeWeight edgeWeight = G.getEdgeWeight(e); - double random_term = random_functions::nextDouble(0.6,1.0); - EdgeRatingType rating = random_term * edgeWeight * (1.0/(double)sqrt((double)targetWeight) + 1.0/(double)sqrt((double)sourceWeight)); - G.setEdgeRating(e, rating); - } endfor - } endfor + forall_nodes(G,n) { + NodeWeight sourceWeight = G.getNodeWeight(n); + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); + NodeWeight targetWeight = G.getNodeWeight(targetNode); + EdgeWeight edgeWeight = G.getEdgeWeight(e); + double random_term = random_functions::nextDouble(0.6,1.0); + EdgeRatingType rating = random_term * edgeWeight * (1.0/(double)sqrt((double)targetWeight) + 1.0/(double)sqrt((double)sourceWeight)); + G.setEdgeRating(e, rating); + } endfor +} endfor } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/coarsening/edge_rating/edge_ratings.h b/parallel/modified_kahip/lib/partition/coarsening/edge_rating/edge_ratings.h index 1e6bff74..78b9b857 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/edge_rating/edge_ratings.h +++ b/parallel/modified_kahip/lib/partition/coarsening/edge_rating/edge_ratings.h @@ -10,22 +10,22 @@ #include "data_structure/graph_access.h" #include "partition_config.h" - +namespace kahip::modified { class edge_ratings { public: - edge_ratings(const PartitionConfig & partition_config); - virtual ~edge_ratings(); + edge_ratings(const PartitionConfig & partition_config); + virtual ~edge_ratings(); - void rate(graph_access & G, unsigned level); - void rate_expansion_star_2(graph_access & G); - void rate_expansion_star(graph_access & G); - void rate_expansion_star_2_algdist(graph_access & G); - void rate_inner_outer(graph_access & G); - void rate_pseudogeom(graph_access & G); - void compute_algdist(graph_access & G, std::vector & dist); + void rate(graph_access & G, unsigned level); + void rate_expansion_star_2(graph_access & G); + void rate_expansion_star(graph_access & G); + void rate_expansion_star_2_algdist(graph_access & G); + void rate_inner_outer(graph_access & G); + void rate_pseudogeom(graph_access & G); + void compute_algdist(graph_access & G, std::vector & dist); private: - const PartitionConfig & partition_config; + const PartitionConfig & partition_config; }; - +} #endif /* end of include guard: EDGE_RATING_FUNCTIONS_FUCW7H6Y */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/compare_degrees.h b/parallel/modified_kahip/lib/partition/coarsening/matching/compare_degrees.h index 69b59ca8..f66b7fba 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/compare_degrees.h +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/compare_degrees.h @@ -7,6 +7,10 @@ #ifndef COMPARE_DEGREES_750FUZ7Z #define COMPARE_DEGREES_750FUZ7Z +#include + +#include "definitions.h" +namespace kahip::modified { class compare_degrees : public std::binary_function { public: @@ -20,6 +24,6 @@ class compare_degrees : public std::binary_function * m_node_degrees; }; - +} #endif /* end of include guard: COMPARE_DEGREES_750FUZ7Z */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/compare_rating.h b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/compare_rating.h index 835b193f..800f60ce 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/compare_rating.h +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/compare_rating.h @@ -10,19 +10,19 @@ #include "data_structure/graph_access.h" #include "definitions.h" - +namespace kahip::modified { class compare_rating { - public: - compare_rating(graph_access * pG) : G(pG) {}; - virtual ~compare_rating() {}; +public: + compare_rating(graph_access * pG) : G(pG) {}; + virtual ~compare_rating() {}; - bool operator() (const EdgeRatingType left, const EdgeRatingType right ) { - return G->getEdgeRating(left) > G->getEdgeRating(right); - } + bool operator() (const EdgeRatingType left, const EdgeRatingType right ) { + return G->getEdgeRating(left) > G->getEdgeRating(right); + } - private: - graph_access * G; +private: + graph_access * G; }; - +} #endif /* end of include guard: COMPARE_RATING_750FUZ7Z */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/gpa_matching.cpp b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/gpa_matching.cpp index 2c95552b..dc203099 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/gpa_matching.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/gpa_matching.cpp @@ -12,7 +12,7 @@ #include "gpa_matching.h" #include "macros_assertions.h" #include "random_functions.h" - +namespace kahip::modified { gpa_matching::gpa_matching() { } @@ -28,332 +28,333 @@ void gpa_matching::match(const PartitionConfig & partition_config, CoarseMapping & coarse_mapping, NodeID & no_of_coarse_vertices, NodePermutationMap & permutation) { - PRINT(std::cout<< "matching using gpa" << std::endl;) - permutation.resize(G.number_of_nodes()); - edge_matching.resize(G.number_of_nodes()); - coarse_mapping.resize(G.number_of_nodes()); - - std::vector edge_permutation; - edge_permutation.reserve(G.number_of_edges()); - std::vector sources(G.number_of_edges()); - - init(G, partition_config, permutation, edge_matching, edge_permutation, sources); - - //permutation of the edges for random tie breaking - if(partition_config.edge_rating_tiebreaking) { - PartitionConfig gpa_perm_config = partition_config; - gpa_perm_config.permutation_quality = PERMUTATION_QUALITY_GOOD; - random_functions::permutate_entries(gpa_perm_config, edge_permutation, false); + PRINT(std::cout<< "matching using gpa" << std::endl;) + permutation.resize(G.number_of_nodes()); + edge_matching.resize(G.number_of_nodes()); + coarse_mapping.resize(G.number_of_nodes()); + + std::vector edge_permutation; + edge_permutation.reserve(G.number_of_edges()); + std::vector sources(G.number_of_edges()); + + init(G, partition_config, permutation, edge_matching, edge_permutation, sources); + + //permutation of the edges for random tie breaking + if(partition_config.edge_rating_tiebreaking) { + PartitionConfig gpa_perm_config = partition_config; + gpa_perm_config.permutation_quality = PERMUTATION_QUALITY_GOOD; + random_functions::permutate_entries(gpa_perm_config, edge_permutation, false); + } + + compare_rating cmp(&G); + std::sort(edge_permutation.begin(), edge_permutation.end(), cmp); + + path_set pathset(&G, &partition_config); + + //grow the paths + forall_edges(G, e) { + EdgeID curEdge = edge_permutation[e]; + NodeID source = sources[curEdge]; + NodeID target = G.getEdgeTarget(curEdge); + if(target < source) continue; // get rid of double edges + + if(G.getEdgeRating(curEdge) == 0.0) { + continue; + } + + //max vertex weight constraint + if(G.getNodeWeight(source) + G.getNodeWeight(target) > partition_config.max_vertex_weight) { + continue; + } + + if( partition_config.combine ) { + if(G.getSecondPartitionIndex(source) != G.getSecondPartitionIndex(target) ) { + std::cout << "b" << std::endl; + continue; + } + } + + pathset.add_if_applicable(source, curEdge); + } endfor + + extract_paths_apply_matching(G, sources, edge_matching, pathset); + + // all matched pairs are now in edge_matching + // now construct the coarsemapping + no_of_coarse_vertices = 0; + if(!partition_config.graph_allready_partitioned) { + forall_nodes(G, n) { + if(partition_config.combine) { + if(G.getSecondPartitionIndex(n) != G.getSecondPartitionIndex(edge_matching[n])) { + // v cycle... they shouldnt be contraced + edge_matching[n] = n; } - - compare_rating cmp(&G); - std::sort(edge_permutation.begin(), edge_permutation.end(), cmp); - - path_set pathset(&G, &partition_config); - - //grow the paths - forall_edges(G, e) { - EdgeID curEdge = edge_permutation[e]; - NodeID source = sources[curEdge]; - NodeID target = G.getEdgeTarget(curEdge); - if(target < source) continue; // get rid of double edges - - if(G.getEdgeRating(curEdge) == 0.0) { - continue; - } - - //max vertex weight constraint - if(G.getNodeWeight(source) + G.getNodeWeight(target) > partition_config.max_vertex_weight) { - continue; - } - - if( partition_config.combine ) { - if(G.getSecondPartitionIndex(source) != G.getSecondPartitionIndex(target) ) { - std::cout << "b" << std::endl; - continue; - } - } - - pathset.add_if_applicable(source, curEdge); - } endfor - - extract_paths_apply_matching(G, sources, edge_matching, pathset); - - // all matched pairs are now in edge_matching - // now construct the coarsemapping - no_of_coarse_vertices = 0; - if(!partition_config.graph_allready_partitioned) { - forall_nodes(G, n) { - if(partition_config.combine) { - if(G.getSecondPartitionIndex(n) != G.getSecondPartitionIndex(edge_matching[n])) { - // v cycle... they shouldnt be contraced - edge_matching[n] = n; - } - } - - if( n < edge_matching[n]) { - coarse_mapping[n] = no_of_coarse_vertices; - coarse_mapping[edge_matching[n]] = no_of_coarse_vertices; - no_of_coarse_vertices++; - } else if(n == edge_matching[n]) { - coarse_mapping[n] = no_of_coarse_vertices; - no_of_coarse_vertices++; - } - - } endfor - } else { - forall_nodes(G, n) { - if(G.getPartitionIndex(n) != G.getPartitionIndex(edge_matching[n])) { - // v cycle... they shouldnt be contraced - edge_matching[n] = n; - } - - if(partition_config.combine) { - if(G.getSecondPartitionIndex(n) != G.getSecondPartitionIndex(edge_matching[n])) { - // v cycle... they shouldnt be contraced - edge_matching[n] = n; - } - } - - - if( n < edge_matching[n]) { - coarse_mapping[n] = no_of_coarse_vertices; - coarse_mapping[edge_matching[n]] = no_of_coarse_vertices; - no_of_coarse_vertices++; - } else if(n == edge_matching[n]) { - coarse_mapping[n] = no_of_coarse_vertices; - no_of_coarse_vertices++; - } - - } endfor - } + } + + if( n < edge_matching[n]) { + coarse_mapping[n] = no_of_coarse_vertices; + coarse_mapping[edge_matching[n]] = no_of_coarse_vertices; + no_of_coarse_vertices++; + } else if(n == edge_matching[n]) { + coarse_mapping[n] = no_of_coarse_vertices; + no_of_coarse_vertices++; + } + + } endfor +} else { + forall_nodes(G, n) { + if(G.getPartitionIndex(n) != G.getPartitionIndex(edge_matching[n])) { + // v cycle... they shouldnt be contraced + edge_matching[n] = n; + } + + if(partition_config.combine) { + if(G.getSecondPartitionIndex(n) != G.getSecondPartitionIndex(edge_matching[n])) { + // v cycle... they shouldnt be contraced + edge_matching[n] = n; + } + } + + + if( n < edge_matching[n]) { + coarse_mapping[n] = no_of_coarse_vertices; + coarse_mapping[edge_matching[n]] = no_of_coarse_vertices; + no_of_coarse_vertices++; + } else if(n == edge_matching[n]) { + coarse_mapping[n] = no_of_coarse_vertices; + no_of_coarse_vertices++; + } + + } endfor +} } -void gpa_matching::init(graph_access & G, - const PartitionConfig & partition_config, - NodePermutationMap & permutation, - Matching & edge_matching, - std::vector & edge_permutation, +void gpa_matching::init(graph_access & G, + const PartitionConfig & partition_config, + NodePermutationMap & permutation, + Matching & edge_matching, + std::vector & edge_permutation, std::vector & sources) { - forall_nodes(G, n) { - permutation[n] = n; - edge_matching[n] = n; + forall_nodes(G, n) { + permutation[n] = n; + edge_matching[n] = n; - forall_out_edges(G, e, n) { - sources[e] = n; - edge_permutation.push_back(e); + forall_out_edges(G, e, n) { + sources[e] = n; + edge_permutation.push_back(e); - if(partition_config.edge_rating == WEIGHT) { - // in that case we need to copy it - G.setEdgeRating(e, G.getEdgeWeight(e)); - } + if(partition_config.edge_rating == WEIGHT) { + // in that case we need to copy it + G.setEdgeRating(e, G.getEdgeWeight(e)); + } - } endfor - } endfor + } endfor +} endfor } -void gpa_matching::extract_paths_apply_matching(graph_access & G, +void gpa_matching::extract_paths_apply_matching(graph_access & G, std::vector & sources, - Matching & edge_matching, + Matching & edge_matching, path_set & pathset) { - // extract the paths in the path set into lists of edges. - // then, apply the dynamic programming max weight function to them. Apply - // the matched edges. - EdgeRatingType matching_rating, second_matching_rating; - - forall_nodes(G, n) { - const path & p = pathset.get_path(n); - - if(not p.is_active()) { - continue; - } - if(p.get_tail() != n) { - continue; - } - if(p.get_length() == 0) { - continue; - } - - if(p.get_head() == p.get_tail()) { - // ******************************** - // handling cycles - // ******************************** - std::vector a_matching, a_second_matching; - std::deque unpacked_cycle; - unpack_path(p, pathset, unpacked_cycle); - - EdgeID first = unpacked_cycle.front(); - unpacked_cycle.pop_front(); - - maximum_weight_matching(G, - unpacked_cycle, - a_matching, - matching_rating); - - unpacked_cycle.push_front(first); - EdgeID last = unpacked_cycle.back(); - unpacked_cycle.pop_back(); - - maximum_weight_matching(G, - unpacked_cycle, - a_second_matching, - second_matching_rating); - - unpacked_cycle.push_back(last); - - if(matching_rating > second_matching_rating) { - //apply first matching - apply_matching(G, a_matching, sources, edge_matching); - } else { - //apply second matching - apply_matching(G, a_second_matching, sources, edge_matching); - } - } else { - // ******************************** - // handling paths - // ******************************** - std::vector a_matching; - std::vector unpacked_path; - - if(p.get_length() == 1) { - //match them directly - EdgeID e = 0; - if(pathset.next_vertex(p.get_tail()) == p.get_head()) { - e = pathset.edge_to_next(p.get_tail()); - } else { - e = pathset.edge_to_prev(p.get_tail()); - ASSERT_TRUE( pathset.prev_vertex(p.get_tail()) == p.get_head() ); - } - - NodeID source = sources[e]; - NodeID target = G.getEdgeTarget(e); - - edge_matching[source] = target; - edge_matching[target] = source; - - continue; - } - unpack_path(p, pathset, unpacked_path); - //dump_unpacked_path(G, unpacked_path, sources); - - EdgeRatingType final_rating = 0; - maximum_weight_matching(G, unpacked_path, a_matching, final_rating); - - //apply matched edges - apply_matching(G, a_matching, sources, edge_matching); - } - } endfor + // extract the paths in the path set into lists of edges. + // then, apply the dynamic programming max weight function to them. Apply + // the matched edges. + EdgeRatingType matching_rating, second_matching_rating; + + forall_nodes(G, n) { + const path & p = pathset.get_path(n); + + if(not p.is_active()) { + continue; + } + if(p.get_tail() != n) { + continue; + } + if(p.get_length() == 0) { + continue; + } + + if(p.get_head() == p.get_tail()) { + // ******************************** + // handling cycles + // ******************************** + std::vector a_matching, a_second_matching; + std::deque unpacked_cycle; + unpack_path(p, pathset, unpacked_cycle); + + EdgeID first = unpacked_cycle.front(); + unpacked_cycle.pop_front(); + + maximum_weight_matching(G, + unpacked_cycle, + a_matching, + matching_rating); + + unpacked_cycle.push_front(first); + EdgeID last = unpacked_cycle.back(); + unpacked_cycle.pop_back(); + + maximum_weight_matching(G, + unpacked_cycle, + a_second_matching, + second_matching_rating); + + unpacked_cycle.push_back(last); + + if(matching_rating > second_matching_rating) { + //apply first matching + apply_matching(G, a_matching, sources, edge_matching); + } else { + //apply second matching + apply_matching(G, a_second_matching, sources, edge_matching); + } + } else { + // ******************************** + // handling paths + // ******************************** + std::vector a_matching; + std::vector unpacked_path; + + if(p.get_length() == 1) { + //match them directly + EdgeID e = 0; + if(pathset.next_vertex(p.get_tail()) == p.get_head()) { + e = pathset.edge_to_next(p.get_tail()); + } else { + e = pathset.edge_to_prev(p.get_tail()); + ASSERT_TRUE( pathset.prev_vertex(p.get_tail()) == p.get_head() ); + } + + NodeID source = sources[e]; + NodeID target = G.getEdgeTarget(e); + + edge_matching[source] = target; + edge_matching[target] = source; + + continue; + } + unpack_path(p, pathset, unpacked_path); + //dump_unpacked_path(G, unpacked_path, sources); + + EdgeRatingType final_rating = 0; + maximum_weight_matching(G, unpacked_path, a_matching, final_rating); + + //apply matched edges + apply_matching(G, a_matching, sources, edge_matching); + } + } endfor } void gpa_matching::apply_matching(graph_access & G, - std::vector & matched_edges, + std::vector & matched_edges, std::vector & sources, Matching & edge_matching) { - //apply matched edges - for( unsigned i = 0; i < matched_edges.size(); i++) { - EdgeID e = matched_edges[i]; - NodeID source = sources[e]; - NodeID target = G.getEdgeTarget(e); + //apply matched edges + for( unsigned i = 0; i < matched_edges.size(); i++) { + EdgeID e = matched_edges[i]; + NodeID source = sources[e]; + NodeID target = G.getEdgeTarget(e); - edge_matching[source] = target; - edge_matching[target] = source; - } + edge_matching[source] = target; + edge_matching[target] = source; + } } -template -void gpa_matching::unpack_path(const path & p, - const path_set & pathset, +template +void gpa_matching::unpack_path(const path & p, + const path_set & pathset, VectorOrDeque & unpacked_path ) { - NodeID head = p.get_head(); - NodeID prev = p.get_tail(); - NodeID next; - NodeID current = prev; - - if(prev == head) { - //special case: the given path is a cycle - current = pathset.next_vertex(prev); - unpacked_path.push_back(pathset.edge_to_next(prev)); - } - - while(current != head) { - if(pathset.next_vertex(current) == prev) { - next = pathset.prev_vertex(current); - unpacked_path.push_back(pathset.edge_to_prev(current)); - } else { - next = pathset.next_vertex(current); - unpacked_path.push_back(pathset.edge_to_next(current)); - } - prev = current; - current = next; - } + NodeID head = p.get_head(); + NodeID prev = p.get_tail(); + NodeID next; + NodeID current = prev; + + if(prev == head) { + //special case: the given path is a cycle + current = pathset.next_vertex(prev); + unpacked_path.push_back(pathset.edge_to_next(prev)); + } + + while(current != head) { + if(pathset.next_vertex(current) == prev) { + next = pathset.prev_vertex(current); + unpacked_path.push_back(pathset.edge_to_prev(current)); + } else { + next = pathset.next_vertex(current); + unpacked_path.push_back(pathset.edge_to_next(current)); + } + prev = current; + current = next; + } } -template +template void gpa_matching::maximum_weight_matching( graph_access & G, - VectorOrDeque & unpacked_path, + VectorOrDeque & unpacked_path, std::vector & matched_edges, EdgeRatingType & final_rating) { - unsigned k = unpacked_path.size(); - if( k == 1 ) { - matched_edges.push_back(unpacked_path[0]); - return; - } - - std::vector ratings(k, 0.0); - std::vector< bool > decision(k, false); - - ratings[0] = G.getEdgeRating(unpacked_path[0]); - ratings[1] = G.getEdgeRating(unpacked_path[1]); - - decision[0] = true; - if(ratings[0] < ratings[1]) { - decision[1] = true; - } - //build up the decision vector - for( EdgeID i = 2; i < k; i++) { - ASSERT_TRUE(unpacked_path[i] < G.number_of_edges()); - EdgeRatingType curRating = G.getEdgeRating(unpacked_path[i]); - if( curRating + ratings[i-2] > ratings[i-1] ) { - decision[i] = true; - ratings[i] = curRating + ratings[i-2]; - } else { - decision[i] = false; - ratings[i] = ratings[i-1]; - } - } - - if(decision[k-1]) { - final_rating = ratings[k-1]; - } else { - final_rating = ratings[k-2]; - } - //construct optimal solution - for(int i = k-1; i >= 0;) { - if(decision[i]) { - matched_edges.push_back(unpacked_path[i]); - i-=2; - } else { - i-=1; - } - } -} + unsigned k = unpacked_path.size(); + if( k == 1 ) { + matched_edges.push_back(unpacked_path[0]); + return; + } + + std::vector ratings(k, 0.0); + std::vector< bool > decision(k, false); + + ratings[0] = G.getEdgeRating(unpacked_path[0]); + ratings[1] = G.getEdgeRating(unpacked_path[1]); + + decision[0] = true; + if(ratings[0] < ratings[1]) { + decision[1] = true; + } + //build up the decision vector + for( EdgeID i = 2; i < k; i++) { + ASSERT_TRUE(unpacked_path[i] < G.number_of_edges()); + EdgeRatingType curRating = G.getEdgeRating(unpacked_path[i]); + if( curRating + ratings[i-2] > ratings[i-1] ) { + decision[i] = true; + ratings[i] = curRating + ratings[i-2]; + } else { + decision[i] = false; + ratings[i] = ratings[i-1]; + } + } + + if(decision[k-1]) { + final_rating = ratings[k-1]; + } else { + final_rating = ratings[k-2]; + } + //construct optimal solution + for(int i = k-1; i >= 0;) { + if(decision[i]) { + matched_edges.push_back(unpacked_path[i]); + i-=2; + } else { + i-=1; + } + } +} -template +template void gpa_matching::dump_unpacked_path( graph_access & G, VectorOrDeque & unpacked_path, std::vector& sources) { - //dump the path - for( unsigned i = 0; i < unpacked_path.size(); i++) { - EdgeID e = unpacked_path[i]; - std::cout << "(" << sources[e] << " " << G.getEdgeTarget(e) << ") "; - } - std::cout << std::endl; + //dump the path + for( unsigned i = 0; i < unpacked_path.size(); i++) { + EdgeID e = unpacked_path[i]; + std::cout << "(" << sources[e] << " " << G.getEdgeTarget(e) << ") "; + } + std::cout << std::endl; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/gpa_matching.h b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/gpa_matching.h index 7182c348..87c9e759 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/gpa_matching.h +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/gpa_matching.h @@ -11,53 +11,53 @@ #include "coarsening/matching/matching.h" #include "path.h" #include "path_set.h" - +namespace kahip::modified { class gpa_matching : public matching{ - public: - gpa_matching( ); - virtual ~gpa_matching(); - - void match(const PartitionConfig & config, - graph_access & G, - Matching & _matching, - CoarseMapping & coarse_mapping, - NodeID & no_of_coarse_vertices, - NodePermutationMap & permutation); - private: - void init(graph_access & G, - const PartitionConfig & partition_config, - NodePermutationMap & permutation, - Matching & edge_matching, - std::vector & edge_permutation, - std::vector & sources); - - void extract_paths_apply_matching( graph_access & G, - std::vector & sources, - Matching & edge_matching, - path_set & pathset); - - template - void unpack_path(const path & the_path, - const path_set & pathset, - VectorOrDeque & a_path); - - template - void maximum_weight_matching( graph_access & G, - VectorOrDeque & unpacked_path, - std::vector & matched_edges, - EdgeRatingType & final_rating); - - void apply_matching( graph_access & G, - std::vector & matched_edges, - std::vector & sources, - Matching & edge_matching); - - - template - void dump_unpacked_path( graph_access & G, - VectorOrDeque & unpacked_path, - std::vector& sources); +public: + gpa_matching( ); + virtual ~gpa_matching(); + + void match(const PartitionConfig & config, + graph_access & G, + Matching & _matching, + CoarseMapping & coarse_mapping, + NodeID & no_of_coarse_vertices, + NodePermutationMap & permutation); +private: + void init(graph_access & G, + const PartitionConfig & partition_config, + NodePermutationMap & permutation, + Matching & edge_matching, + std::vector & edge_permutation, + std::vector & sources); + + void extract_paths_apply_matching( graph_access & G, + std::vector & sources, + Matching & edge_matching, + path_set & pathset); + + template + void unpack_path(const path & the_path, + const path_set & pathset, + VectorOrDeque & a_path); + + template + void maximum_weight_matching( graph_access & G, + VectorOrDeque & unpacked_path, + std::vector & matched_edges, + EdgeRatingType & final_rating); + + void apply_matching( graph_access & G, + std::vector & matched_edges, + std::vector & sources, + Matching & edge_matching); + + + template + void dump_unpacked_path( graph_access & G, + VectorOrDeque & unpacked_path, + std::vector& sources); }; - +} #endif /* end of include guard: GPA_MATCHING_NXLQ0SIT */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path.cpp b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path.cpp index 9952605d..79ff28f4 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "path.h" - +namespace kahip::modified { path::path() : head(UNDEFINED_NODE), tail(UNDEFINED_NODE), length(0), active(false) { } @@ -18,4 +18,4 @@ path::path(const NodeID & v) : head(v), tail(v), length(0), active(true) { path::~path() { } - +} diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path.h b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path.h index 7a7b530f..6ad27831 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path.h +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path.h @@ -9,45 +9,45 @@ #define PATH_X5LQS3DT #include "definitions.h" - +namespace kahip::modified { class path { - public: - path( ); - path( const NodeID & v ); - virtual ~path(); +public: + path( ); + path( const NodeID & v ); + virtual ~path(); + + void init(const NodeID & v); + + NodeID get_tail() const; + void set_tail(const NodeID & id); - void init(const NodeID & v); + NodeID get_head() const; + void set_head(const NodeID & id); - NodeID get_tail() const; - void set_tail(const NodeID & id); + void set_length(const EdgeID & length); + EdgeID get_length() const; - NodeID get_head() const; - void set_head(const NodeID & id); + //returns wether the given node is an endpoint of the path + bool is_endpoint(const NodeID & id) const; - void set_length(const EdgeID & length); - EdgeID get_length() const; + //returns wether the path is a cycle or not. + bool is_cycle() const; - //returns wether the given node is an endpoint of the path - bool is_endpoint(const NodeID & id) const; - - //returns wether the path is a cycle or not. - bool is_cycle() const; + bool is_active() const; + void set_active(const bool active); - bool is_active() const; - void set_active(const bool active); +private: + //Last vertex of the path. Cycles have head == tail + NodeID head; - private: - //Last vertex of the path. Cycles have head == tail - NodeID head; + //First vertex of the path. Cycles have head == tail + NodeID tail; - //First vertex of the path. Cycles have head == tail - NodeID tail; + //Number of edges in the graph + EdgeID length; - //Number of edges in the graph - EdgeID length; - - // True iff the parth is still in use. False iff it has been removed. - bool active; + // True iff the parth is still in use. False iff it has been removed. + bool active; }; @@ -97,6 +97,6 @@ inline bool path::is_active() const { inline void path::set_active(const bool act) { active = act; } - +} #endif /* end of include guard: PATH_X5LQS3DT */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path_set.cpp b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path_set.cpp index 13bef2e9..0c04aa23 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path_set.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path_set.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "path_set.h" - +namespace kahip::modified { path_set::path_set( graph_access * G_, const PartitionConfig * config_ ): pG(G_), config(config_), m_no_of_paths(pG->number_of_nodes()), m_vertex_to_path(m_no_of_paths), @@ -16,17 +16,17 @@ path_set::path_set( graph_access * G_, const PartitionConfig * config_ ): pG(G_) m_next_edge(m_no_of_paths, UNDEFINED_EDGE), m_prev_edge(m_no_of_paths, UNDEFINED_EDGE) { - graph_access & G = *pG; - forall_nodes(G, node) { - m_paths[node].init(node); - m_vertex_to_path[node] = node; - m_next[node] = node; - m_prev[node] = node; - } endfor + graph_access & G = *pG; + forall_nodes(G, node) { + m_paths[node].init(node); + m_vertex_to_path[node] = node; + m_next[node] = node; + m_prev[node] = node; + } endfor } path_set::~path_set() { } - +} diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path_set.h b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path_set.h index 05591372..524faee3 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path_set.h +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/gpa/path_set.h @@ -12,75 +12,75 @@ #include "macros_assertions.h" #include "partition_config.h" #include "path.h" - +namespace kahip::modified { class path_set { - public: +public: - path_set( graph_access * G, const PartitionConfig * config ); - virtual ~path_set(); + path_set( graph_access * G, const PartitionConfig * config ); + virtual ~path_set(); - //returns the path that v lies on iff v is an endpoint - const path& get_path(const NodeID & v) const; + //returns the path that v lies on iff v is an endpoint + const path& get_path(const NodeID & v) const; - //returns the number of paths in the set - PathID path_count() const; + //returns the number of paths in the set + PathID path_count() const; - // add the edge with given id to the path set if it is applicable - // returns true iff the edge was applicable - bool add_if_applicable(const NodeID & source, const EdgeID & e); + // add the edge with given id to the path set if it is applicable + // returns true iff the edge was applicable + bool add_if_applicable(const NodeID & source, const EdgeID & e); - //********** - //Navigation - //********** + //********** + //Navigation + //********** - //returns the if of vertex next to v on the path - NodeID next_vertex( const NodeID & v ) const; + //returns the if of vertex next to v on the path + NodeID next_vertex( const NodeID & v ) const; - //returns the if of vertex previous to v on the path - NodeID prev_vertex( const NodeID & v ) const; + //returns the if of vertex previous to v on the path + NodeID prev_vertex( const NodeID & v ) const; - //returns the id of the edge to the next vertex on the path - EdgeID edge_to_next(const NodeID & v) const; + //returns the id of the edge to the next vertex on the path + EdgeID edge_to_next(const NodeID & v) const; - //returns the id of the edge to the previous vertex on the path - EdgeID edge_to_prev(const NodeID & v) const; - private: - graph_access * pG; + //returns the id of the edge to the previous vertex on the path + EdgeID edge_to_prev(const NodeID & v) const; +private: + graph_access * pG; - const PartitionConfig * config; + const PartitionConfig * config; - // Number of Paths - PathID m_no_of_paths; + // Number of Paths + PathID m_no_of_paths; - // for every vertex v, vertex_to_path[v] is the id of the path - std::vector m_vertex_to_path; + // for every vertex v, vertex_to_path[v] is the id of the path + std::vector m_vertex_to_path; - // for every path id p, paths[p] is the path for this id - std::vector m_paths; + // for every path id p, paths[p] is the path for this id + std::vector m_paths; - // for every vertex v, next[v] is the id of the vertex that is next on its path. - // for the head v of a path, next[v] == v - std::vector m_next; + // for every vertex v, next[v] is the id of the vertex that is next on its path. + // for the head v of a path, next[v] == v + std::vector m_next; - // for every vertex v, prev[v] is the id of the vertex that is previouson its path. - // for the tail v of a path, prev[v] == v - std::vector m_prev; + // for every vertex v, prev[v] is the id of the vertex that is previouson its path. + // for the tail v of a path, prev[v] == v + std::vector m_prev; - // for every vertex v, next_edge[v] is the id of the vertex that is used to - // connect the vertex v to the next vertex in the path. - // if next[v] == v the next_edge[v] = UNDEFINED_EDGE - std::vector m_next_edge; + // for every vertex v, next_edge[v] is the id of the vertex that is used to + // connect the vertex v to the next vertex in the path. + // if next[v] == v the next_edge[v] = UNDEFINED_EDGE + std::vector m_next_edge; - // for every vertex v, prev_edge[v] is the id of the vertex that is used to - // connect the vertex v to the previous vertex in the path. - // if prev[v] == v the prev_edge[v] = UNDEFINED_EDGE - std::vector m_prev_edge; + // for every vertex v, prev_edge[v] is the id of the vertex that is used to + // connect the vertex v to the previous vertex in the path. + // if prev[v] == v the prev_edge[v] = UNDEFINED_EDGE + std::vector m_prev_edge; - inline bool is_endpoint(const NodeID & v) const { - return (m_next[v] == v or m_prev[v] == v); - } + inline bool is_endpoint(const NodeID & v) const { + return (m_next[v] == v or m_prev[v] == v); + } }; @@ -96,11 +96,11 @@ inline PathID path_set::path_count() const { inline NodeID path_set::next_vertex( const NodeID & v ) const { return m_next[v]; -} +} inline NodeID path_set::prev_vertex( const NodeID & v ) const { return m_prev[v]; -} +} inline EdgeID path_set::edge_to_next(const NodeID & v) const { return m_next_edge[v]; @@ -119,17 +119,17 @@ inline bool path_set::add_if_applicable(const NodeID & source, const EdgeID & e) // in this case we only grow paths inside blocks if(G.getPartitionIndex(source) != G.getPartitionIndex(target)) return false; - + if(config->combine) { if(G.getSecondPartitionIndex(source) != G.getSecondPartitionIndex(target)) { return false; } } - + } - PathID sourcePathID = m_vertex_to_path[source]; - PathID targetPathID = m_vertex_to_path[target]; + PathID sourcePathID = m_vertex_to_path[source]; + PathID targetPathID = m_vertex_to_path[target]; ASSERT_NEQ(source, target); @@ -143,8 +143,8 @@ inline bool path_set::add_if_applicable(const NodeID & source, const EdgeID & e) ASSERT_TRUE(source_path.is_active()); ASSERT_TRUE(target_path.is_active()); - - if(source_path.is_cycle() or target_path.is_cycle()) { + + if(source_path.is_cycle() or target_path.is_cycle()) { // if one of the paths is a cycle then it is not applicable return false; } @@ -223,7 +223,7 @@ inline bool path_set::add_if_applicable(const NodeID & source, const EdgeID & e) return true; } return false; -} - +} +} #endif /* end of include guard: PATH_SET_80E9CQT1 */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/matching.cpp b/parallel/modified_kahip/lib/partition/coarsening/matching/matching.cpp index 1290a04b..3aae63f2 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/matching.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/matching.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "matching.h" - +namespace kahip::modified { matching::matching() { } @@ -16,9 +16,9 @@ matching::~matching() { } void matching::print_matching(FILE * out, Matching & edge_matching) { - for (NodeID n = 0; n < edge_matching.size(); n++) { - fprintf(out, "%d:%d\n", n, edge_matching[n]); - } + for (NodeID n = 0; n < edge_matching.size(); n++) { + fprintf(out, "%d:%d\n", n, edge_matching[n]); + } +} } - diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/matching.h b/parallel/modified_kahip/lib/partition/coarsening/matching/matching.h index a8e6f57c..694536cd 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/matching.h +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/matching.h @@ -10,20 +10,20 @@ #include "data_structure/graph_access.h" #include "partition_config.h" - +namespace kahip::modified { class matching { - public: - matching(); - virtual ~matching(); +public: + matching(); + virtual ~matching(); - virtual void match(const PartitionConfig & partition_config, - graph_access & G, - Matching & _matching, - CoarseMapping & mapping, - NodeID & no_of_coarse_vertices, - NodePermutationMap & permutation) = 0; + virtual void match(const PartitionConfig & partition_config, + graph_access & G, + Matching & _matching, + CoarseMapping & mapping, + NodeID & no_of_coarse_vertices, + NodePermutationMap & permutation) = 0; - void print_matching(FILE * out, Matching & edge_matching); + void print_matching(FILE * out, Matching & edge_matching); }; - +} #endif /* end of include guard: MATCHING_QL4RUO3D */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/random_matching.cpp b/parallel/modified_kahip/lib/partition/coarsening/matching/random_matching.cpp index 3ec73ffe..5788f718 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/random_matching.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/random_matching.cpp @@ -8,7 +8,7 @@ #include "macros_assertions.h" #include "random_functions.h" #include "random_matching.h" - +namespace kahip::modified { random_matching::random_matching() { } @@ -25,92 +25,93 @@ void random_matching::match(const PartitionConfig & partition_config, NodePermutationMap & permutation) { - permutation.resize(G.number_of_nodes()); - edge_matching.resize(G.number_of_nodes()); - coarse_mapping.resize(G.number_of_nodes()); - - no_of_coarse_vertices = 0; + permutation.resize(G.number_of_nodes()); + edge_matching.resize(G.number_of_nodes()); + coarse_mapping.resize(G.number_of_nodes()); + + no_of_coarse_vertices = 0; + + if(!(partition_config.matching_type == MATCHING_RANDOM_GPA)) { + random_functions::permutate_entries(partition_config, permutation, true); + } else { + for( unsigned int i = 0; i < permutation.size(); i++) { + permutation[i] = i; + } + } + + forall_nodes(G, n) { + edge_matching[n] = n; + } endfor + + if(partition_config.graph_allready_partitioned) { //in this case edges between partitions arent matched + forall_nodes(G, n) { + NodeID curNode = permutation[n]; + NodeWeight curNodeWeight = G.getNodeWeight(curNode); + + if(edge_matching[curNode] == curNode) { + //match with a random neighbor + int matchingPartner = curNode; + forall_out_edges(G, e, curNode) { + NodeID target = G.getEdgeTarget(e); + NodeWeight coarser_weight = G.getNodeWeight(target) + curNodeWeight; + + if(edge_matching[target] == target + && coarser_weight <= partition_config.max_vertex_weight) { + if(G.getPartitionIndex(curNode) != G.getPartitionIndex(target)) + continue; + + if(partition_config.combine) { + if(G.getSecondPartitionIndex(curNode) != G.getSecondPartitionIndex(target)) + continue; + } + + matchingPartner = target; + ASSERT_NEQ(curNode, target); + break; + } + } endfor - if(!(partition_config.matching_type == MATCHING_RANDOM_GPA)) { - random_functions::permutate_entries(partition_config, permutation, true); - } else { - for( unsigned int i = 0; i < permutation.size(); i++) { - permutation[i] = i; - } + coarse_mapping[matchingPartner] = no_of_coarse_vertices; + coarse_mapping[curNode] = no_of_coarse_vertices; + + edge_matching[matchingPartner] = curNode; + edge_matching[curNode] = matchingPartner; + + no_of_coarse_vertices++; + } + } endfor +} else { + //copy n paste from the first if clause but this time all edges are matchable + forall_nodes(G, n) { + NodeID curNode = permutation[n]; + NodeWeight curNodeWeight = G.getNodeWeight(curNode); + + if(edge_matching[curNode] == curNode) { + //match with a random neighbor + int matchingPartner = curNode; + forall_out_edges(G, e, curNode) { + NodeID target = G.getEdgeTarget(e); + NodeWeight coarser_weight = G.getNodeWeight(target) + curNodeWeight; + + if(edge_matching[target] == target + && coarser_weight <= partition_config.max_vertex_weight) { + matchingPartner = target; + ASSERT_NEQ(curNode, target); + break; } + } endfor - forall_nodes(G, n) { - edge_matching[n] = n; - } endfor + coarse_mapping[matchingPartner] = no_of_coarse_vertices; + coarse_mapping[curNode] = no_of_coarse_vertices; - if(partition_config.graph_allready_partitioned) { //in this case edges between partitions arent matched - forall_nodes(G, n) { - NodeID curNode = permutation[n]; - NodeWeight curNodeWeight = G.getNodeWeight(curNode); - - if(edge_matching[curNode] == curNode) { - //match with a random neighbor - int matchingPartner = curNode; - forall_out_edges(G, e, curNode) { - NodeID target = G.getEdgeTarget(e); - NodeWeight coarser_weight = G.getNodeWeight(target) + curNodeWeight; - - if(edge_matching[target] == target - && coarser_weight <= partition_config.max_vertex_weight) { - if(G.getPartitionIndex(curNode) != G.getPartitionIndex(target)) - continue; - - if(partition_config.combine) { - if(G.getSecondPartitionIndex(curNode) != G.getSecondPartitionIndex(target)) - continue; - } - - matchingPartner = target; - ASSERT_NEQ(curNode, target); - break; - } - } endfor - - coarse_mapping[matchingPartner] = no_of_coarse_vertices; - coarse_mapping[curNode] = no_of_coarse_vertices; - - edge_matching[matchingPartner] = curNode; - edge_matching[curNode] = matchingPartner; - - no_of_coarse_vertices++; - } - } endfor - } else { - //copy n paste from the first if clause but this time all edges are matchable - forall_nodes(G, n) { - NodeID curNode = permutation[n]; - NodeWeight curNodeWeight = G.getNodeWeight(curNode); - - if(edge_matching[curNode] == curNode) { - //match with a random neighbor - int matchingPartner = curNode; - forall_out_edges(G, e, curNode) { - NodeID target = G.getEdgeTarget(e); - NodeWeight coarser_weight = G.getNodeWeight(target) + curNodeWeight; - - if(edge_matching[target] == target - && coarser_weight <= partition_config.max_vertex_weight) { - matchingPartner = target; - ASSERT_NEQ(curNode, target); - break; - } - } endfor - - coarse_mapping[matchingPartner] = no_of_coarse_vertices; - coarse_mapping[curNode] = no_of_coarse_vertices; - - edge_matching[matchingPartner] = curNode; - edge_matching[curNode] = matchingPartner; - - no_of_coarse_vertices++; - } - } endfor + edge_matching[matchingPartner] = curNode; + edge_matching[curNode] = matchingPartner; - } - PRINT(std::cout << "log>" << "no of coarse nodes: " << no_of_coarse_vertices << std::endl;) + no_of_coarse_vertices++; + } + } endfor + +} + PRINT(std::cout << "log>" << "no of coarse nodes: " << no_of_coarse_vertices << std::endl;) } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/coarsening/matching/random_matching.h b/parallel/modified_kahip/lib/partition/coarsening/matching/random_matching.h index 5e7a2b6c..e04c4785 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/matching/random_matching.h +++ b/parallel/modified_kahip/lib/partition/coarsening/matching/random_matching.h @@ -10,18 +10,18 @@ #define RANDOM_MATCHING_D5YDSMDW #include "matching.h" - +namespace kahip::modified { class random_matching : public matching { - public: - random_matching(); - virtual ~random_matching(); +public: + random_matching(); + virtual ~random_matching(); - void match(const PartitionConfig & config, - graph_access & G, - Matching & _matching, - CoarseMapping & coarse_mapping, - NodeID & no_of_coarse_vertices, - NodePermutationMap & permutation); + void match(const PartitionConfig & config, + graph_access & G, + Matching & _matching, + CoarseMapping & coarse_mapping, + NodeID & no_of_coarse_vertices, + NodePermutationMap & permutation); }; - +} #endif /* end of include guard: RANDOM_MATCHING_D5YDSMDW */ diff --git a/parallel/modified_kahip/lib/partition/coarsening/stop_rules/stop_rules.h b/parallel/modified_kahip/lib/partition/coarsening/stop_rules/stop_rules.h index 72d9b615..0c2a0a1b 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/stop_rules/stop_rules.h +++ b/parallel/modified_kahip/lib/partition/coarsening/stop_rules/stop_rules.h @@ -8,33 +8,33 @@ #ifndef STOP_RULES_SZ45JQS6 #define STOP_RULES_SZ45JQS6 -#include +#include #include "partition_config.h" - +namespace kahip::modified { class stop_rule { - public: - stop_rule() {}; - virtual ~stop_rule() {}; - virtual bool stop( NodeID number_of_finer_vertices, NodeID number_of_coarser_vertices ) = 0; +public: + stop_rule() {}; + virtual ~stop_rule() {}; + virtual bool stop( NodeID number_of_finer_vertices, NodeID number_of_coarser_vertices ) = 0; }; class simple_stop_rule : public stop_rule { - public: - simple_stop_rule(PartitionConfig & config, NodeID number_of_nodes) { - double x = 60; - num_stop = std::max(number_of_nodes/(2.0*x*config.k), 60.0*config.k); - if(config.disable_max_vertex_weight_constraint) { - config.max_vertex_weight = config.upper_bound_partition; - } else { - config.max_vertex_weight = (NodeWeight)(1.5*config.largest_graph_weight/num_stop); - } - }; - virtual ~simple_stop_rule() {}; - bool stop( NodeID number_of_finer_vertices, NodeID number_of_coarser_vertices ); - - private: - NodeID num_stop; +public: + simple_stop_rule(PartitionConfig & config, NodeID number_of_nodes) { + double x = 60; + num_stop = std::max(number_of_nodes/(2.0*x*config.k), 60.0*config.k); + if(config.disable_max_vertex_weight_constraint) { + config.max_vertex_weight = config.upper_bound_partition; + } else { + config.max_vertex_weight = (NodeWeight)(1.5*config.largest_graph_weight/num_stop); + } + }; + virtual ~simple_stop_rule() {}; + bool stop( NodeID number_of_finer_vertices, NodeID number_of_coarser_vertices ); + +private: + NodeID num_stop; }; inline bool simple_stop_rule::stop(NodeID no_of_finer_vertices, NodeID no_of_coarser_vertices ) { @@ -42,16 +42,16 @@ inline bool simple_stop_rule::stop(NodeID no_of_finer_vertices, NodeID no_of_coa return contraction_rate >= 1.1 && no_of_coarser_vertices >= num_stop; } class strong_stop_rule : public stop_rule { - public: - strong_stop_rule(PartitionConfig & config, NodeID number_of_nodes) { - num_stop = config.k; - config.max_vertex_weight = config.upper_bound_partition; - }; - virtual ~strong_stop_rule() {}; - bool stop( NodeID number_of_finer_vertices, NodeID number_of_coarser_vertices ); - - private: - NodeID num_stop; +public: + strong_stop_rule(PartitionConfig & config, NodeID number_of_nodes) { + num_stop = config.k; + config.max_vertex_weight = config.upper_bound_partition; + }; + virtual ~strong_stop_rule() {}; + bool stop( NodeID number_of_finer_vertices, NodeID number_of_coarser_vertices ); + +private: + NodeID num_stop; }; inline bool strong_stop_rule::stop(NodeID no_of_finer_vertices, NodeID no_of_coarser_vertices ) { @@ -60,34 +60,34 @@ inline bool strong_stop_rule::stop(NodeID no_of_finer_vertices, NodeID no_of_coa } class multiple_k_stop_rule : public stop_rule { - public: - multiple_k_stop_rule (PartitionConfig & config, NodeID number_of_nodes) { - num_stop = config.num_vert_stop_factor*config.k; +public: + multiple_k_stop_rule (PartitionConfig & config, NodeID number_of_nodes) { + num_stop = config.num_vert_stop_factor*config.k; - if(config.disable_max_vertex_weight_constraint) { - config.max_vertex_weight = config.upper_bound_partition; + if(config.disable_max_vertex_weight_constraint) { + config.max_vertex_weight = config.upper_bound_partition; + } else { + if(config.initial_partitioning) { + //if we perform initial partitioning we relax this constraint + config.max_vertex_weight = 1.5*((double)config.largest_graph_weight)/(2*config.num_vert_stop_factor); } else { - if(config.initial_partitioning) { - //if we perform initial partitioning we relax this constraint - config.max_vertex_weight = 1.5*((double)config.largest_graph_weight)/(2*config.num_vert_stop_factor); - } else { - config.max_vertex_weight = (NodeWeight)(1.5*config.largest_graph_weight/num_stop); - } + config.max_vertex_weight = (NodeWeight)(1.5*config.largest_graph_weight/num_stop); } + } - }; - virtual ~multiple_k_stop_rule () {}; - bool stop( NodeID number_of_finer_vertices, NodeID number_of_coarser_vertices ); + }; + virtual ~multiple_k_stop_rule () {}; + bool stop( NodeID number_of_finer_vertices, NodeID number_of_coarser_vertices ); - private: - NodeID num_stop; +private: + NodeID num_stop; }; inline bool multiple_k_stop_rule::stop(NodeID no_of_finer_vertices, NodeID no_of_coarser_vertices ) { double contraction_rate = 1.0 * no_of_finer_vertices / (double)no_of_coarser_vertices; return contraction_rate >= 1.1 && no_of_coarser_vertices >= num_stop; } - +} diff --git a/parallel/modified_kahip/lib/partition/graph_partitioner.cpp b/parallel/modified_kahip/lib/partition/graph_partitioner.cpp index 104dfe74..5775c68d 100644 --- a/parallel/modified_kahip/lib/partition/graph_partitioner.cpp +++ b/parallel/modified_kahip/lib/partition/graph_partitioner.cpp @@ -14,7 +14,7 @@ #include "uncoarsening/uncoarsening.h" #include "uncoarsening/refinement/mixed_refinement.h" #include "w_cycles/wcycle_partitioner.h" - +namespace kahip::modified { graph_partitioner::graph_partitioner() { } @@ -24,208 +24,208 @@ graph_partitioner::~graph_partitioner() { } void graph_partitioner::perform_recursive_partitioning(PartitionConfig & config, graph_access & G) { - m_global_k = config.k; - m_global_upper_bound = config.upper_bound_partition; - m_rnd_bal = random_functions::nextDouble(1,2); - perform_recursive_partitioning_internal(config, G, 0, config.k-1); + m_global_k = config.k; + m_global_upper_bound = config.upper_bound_partition; + m_rnd_bal = random_functions::nextDouble(1,2); + perform_recursive_partitioning_internal(config, G, 0, config.k-1); } -void graph_partitioner::perform_recursive_partitioning_internal(PartitionConfig & config, - graph_access & G, - PartitionID lb, +void graph_partitioner::perform_recursive_partitioning_internal(PartitionConfig & config, + graph_access & G, + PartitionID lb, PartitionID ub) { - G.set_partition_count(2); - - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // configuration of bipartitioning - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - PartitionConfig bipart_config = config; - bipart_config.k = 2; - bipart_config.stop_rule = STOP_RULE_MULTIPLE_K; - bipart_config.num_vert_stop_factor = 100; - double epsilon = 0; - bipart_config.rebalance = false; - bipart_config.softrebalance = true; - - if(config.k < 64) { - epsilon = m_rnd_bal/100.0; - bipart_config.rebalance = false; - bipart_config.softrebalance = false; - } else { - epsilon = 1/100.0; - } - if(m_global_k == 2) { - epsilon = 3.0/100.0; - } - - - bipart_config.upper_bound_partition = ceil((1+epsilon)*config.largest_graph_weight/(double)bipart_config.k); - bipart_config.corner_refinement_enabled = false; - bipart_config.quotient_graph_refinement_disabled = false; - bipart_config.refinement_scheduling_algorithm = REFINEMENT_SCHEDULING_ACTIVE_BLOCKS; - bipart_config.kway_adaptive_limits_beta = log(G.number_of_nodes()); - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // end configuration - // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - NodeID new_ub_lhs = floor((lb+ub)/2); - NodeID new_lb_rhs = floor((lb+ub)/2+1); - NodeID num_blocks_lhs = new_ub_lhs - lb + 1; - NodeID num_blocks_rhs = ub - new_lb_rhs + 1; - - if(config.k % 2 != 0) { - //otherwise the block weights have to be - bipart_config.target_weights.clear(); - bipart_config.target_weights.push_back((1+epsilon)*num_blocks_lhs/(double)(num_blocks_lhs+num_blocks_rhs)*config.largest_graph_weight); - bipart_config.target_weights.push_back((1+epsilon)*num_blocks_rhs/(double)(num_blocks_lhs+num_blocks_rhs)*config.largest_graph_weight); - bipart_config.initial_bipartitioning = true; - bipart_config.refinement_type = REFINEMENT_TYPE_FM; // flows not supported for odd block weights - } else { - - bipart_config.target_weights.clear(); - bipart_config.target_weights.push_back(bipart_config.upper_bound_partition); - bipart_config.target_weights.push_back(bipart_config.upper_bound_partition); - bipart_config.initial_bipartitioning = false; - } - - bipart_config.grow_target = ceil(num_blocks_lhs/(double)(num_blocks_lhs+num_blocks_rhs)*config.largest_graph_weight); - - perform_partitioning(bipart_config, G); - - if( config.k > 2 ) { - graph_extractor extractor; - - graph_access extracted_block_lhs; - graph_access extracted_block_rhs; - std::vector mapping_extracted_to_G_lhs; // map the new nodes to the nodes in the old graph G - std::vector mapping_extracted_to_G_rhs; // map the new nodes to the nodes in the old graph G - - NodeWeight weight_lhs_block = 0; - NodeWeight weight_rhs_block = 0; - - extractor.extract_two_blocks(G, extracted_block_lhs, - extracted_block_rhs, - mapping_extracted_to_G_lhs, - mapping_extracted_to_G_rhs, - weight_lhs_block, weight_rhs_block); - - PartitionConfig rec_config = config; - if(num_blocks_lhs > 1) { - rec_config.k = num_blocks_lhs; - - rec_config.largest_graph_weight = weight_lhs_block; - perform_recursive_partitioning_internal( rec_config, extracted_block_lhs, lb, new_ub_lhs); - - //apply partition - forall_nodes(extracted_block_lhs, node) { - G.setPartitionIndex(mapping_extracted_to_G_lhs[node], extracted_block_lhs.getPartitionIndex(node)); - } endfor - - } else { - //apply partition - forall_nodes(extracted_block_lhs, node) { - G.setPartitionIndex(mapping_extracted_to_G_lhs[node], lb); - } endfor - } - - if(num_blocks_rhs > 1) { - rec_config.k = num_blocks_rhs; - rec_config.largest_graph_weight = weight_rhs_block; - perform_recursive_partitioning_internal( rec_config, extracted_block_rhs, new_lb_rhs, ub); - - forall_nodes(extracted_block_rhs, node) { - G.setPartitionIndex(mapping_extracted_to_G_rhs[node], extracted_block_rhs.getPartitionIndex(node)); - } endfor - - } else { - //apply partition - forall_nodes(extracted_block_rhs, node) { - G.setPartitionIndex(mapping_extracted_to_G_rhs[node], ub); - } endfor - } - - } else { - forall_nodes(G, node) { - if(G.getPartitionIndex(node) == 0) { - G.setPartitionIndex(node, lb); - } else { - G.setPartitionIndex(node, ub); - } - } endfor - } - - G.set_partition_count(config.k); + G.set_partition_count(2); + + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // configuration of bipartitioning + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + PartitionConfig bipart_config = config; + bipart_config.k = 2; + bipart_config.stop_rule = STOP_RULE_MULTIPLE_K; + bipart_config.num_vert_stop_factor = 100; + double epsilon = 0; + bipart_config.rebalance = false; + bipart_config.softrebalance = true; + + if(config.k < 64) { + epsilon = m_rnd_bal/100.0; + bipart_config.rebalance = false; + bipart_config.softrebalance = false; + } else { + epsilon = 1/100.0; + } + if(m_global_k == 2) { + epsilon = 3.0/100.0; + } + + + bipart_config.upper_bound_partition = ceil((1+epsilon)*config.largest_graph_weight/(double)bipart_config.k); + bipart_config.corner_refinement_enabled = false; + bipart_config.quotient_graph_refinement_disabled = false; + bipart_config.refinement_scheduling_algorithm = REFINEMENT_SCHEDULING_ACTIVE_BLOCKS; + bipart_config.kway_adaptive_limits_beta = log(G.number_of_nodes()); + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // end configuration + // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + NodeID new_ub_lhs = floor((lb+ub)/2); + NodeID new_lb_rhs = floor((lb+ub)/2+1); + NodeID num_blocks_lhs = new_ub_lhs - lb + 1; + NodeID num_blocks_rhs = ub - new_lb_rhs + 1; + + if(config.k % 2 != 0) { + //otherwise the block weights have to be + bipart_config.target_weights.clear(); + bipart_config.target_weights.push_back((1+epsilon)*num_blocks_lhs/(double)(num_blocks_lhs+num_blocks_rhs)*config.largest_graph_weight); + bipart_config.target_weights.push_back((1+epsilon)*num_blocks_rhs/(double)(num_blocks_lhs+num_blocks_rhs)*config.largest_graph_weight); + bipart_config.initial_bipartitioning = true; + bipart_config.refinement_type = REFINEMENT_TYPE_FM; // flows not supported for odd block weights + } else { + + bipart_config.target_weights.clear(); + bipart_config.target_weights.push_back(bipart_config.upper_bound_partition); + bipart_config.target_weights.push_back(bipart_config.upper_bound_partition); + bipart_config.initial_bipartitioning = false; + } + + bipart_config.grow_target = ceil(num_blocks_lhs/(double)(num_blocks_lhs+num_blocks_rhs)*config.largest_graph_weight); + + perform_partitioning(bipart_config, G); + + if( config.k > 2 ) { + graph_extractor extractor; + + graph_access extracted_block_lhs; + graph_access extracted_block_rhs; + std::vector mapping_extracted_to_G_lhs; // map the new nodes to the nodes in the old graph G + std::vector mapping_extracted_to_G_rhs; // map the new nodes to the nodes in the old graph G + + NodeWeight weight_lhs_block = 0; + NodeWeight weight_rhs_block = 0; + + extractor.extract_two_blocks(G, extracted_block_lhs, + extracted_block_rhs, + mapping_extracted_to_G_lhs, + mapping_extracted_to_G_rhs, + weight_lhs_block, weight_rhs_block); + + PartitionConfig rec_config = config; + if(num_blocks_lhs > 1) { + rec_config.k = num_blocks_lhs; + + rec_config.largest_graph_weight = weight_lhs_block; + perform_recursive_partitioning_internal( rec_config, extracted_block_lhs, lb, new_ub_lhs); + + //apply partition + forall_nodes(extracted_block_lhs, node) { + G.setPartitionIndex(mapping_extracted_to_G_lhs[node], extracted_block_lhs.getPartitionIndex(node)); + } endfor + +} else { + //apply partition + forall_nodes(extracted_block_lhs, node) { + G.setPartitionIndex(mapping_extracted_to_G_lhs[node], lb); + } endfor +} + + if(num_blocks_rhs > 1) { + rec_config.k = num_blocks_rhs; + rec_config.largest_graph_weight = weight_rhs_block; + perform_recursive_partitioning_internal( rec_config, extracted_block_rhs, new_lb_rhs, ub); + + forall_nodes(extracted_block_rhs, node) { + G.setPartitionIndex(mapping_extracted_to_G_rhs[node], extracted_block_rhs.getPartitionIndex(node)); + } endfor + +} else { + //apply partition + forall_nodes(extracted_block_rhs, node) { + G.setPartitionIndex(mapping_extracted_to_G_rhs[node], ub); + } endfor +} + + } else { + forall_nodes(G, node) { + if(G.getPartitionIndex(node) == 0) { + G.setPartitionIndex(node, lb); + } else { + G.setPartitionIndex(node, ub); + } + } endfor +} + + G.set_partition_count(config.k); } void graph_partitioner::single_run( PartitionConfig & config, graph_access & G) { - for( unsigned i = 1; i <= config.global_cycle_iterations; i++) { - PRINT(std::cout << "vcycle " << i << " of " << config.global_cycle_iterations << std::endl;) - if(config.use_wcycles || config.use_fullmultigrid) { - wcycle_partitioner w_partitioner; - w_partitioner.perform_partitioning(config, G); - } else { - coarsening coarsen; - initial_partitioning init_part; - uncoarsening uncoarsen; - - graph_hierarchy hierarchy; - - coarsen.perform_coarsening(config, G, hierarchy); - init_part.perform_initial_partitioning(config, hierarchy); - uncoarsen.perform_uncoarsening(config, hierarchy); - } - config.graph_allready_partitioned = true; - config.balance_factor = 0; - } + for( unsigned i = 1; i <= config.global_cycle_iterations; i++) { + PRINT(std::cout << "vcycle " << i << " of " << config.global_cycle_iterations << std::endl;) + if(config.use_wcycles || config.use_fullmultigrid) { + wcycle_partitioner w_partitioner; + w_partitioner.perform_partitioning(config, G); + } else { + coarsening coarsen; + initial_partitioning init_part; + uncoarsening uncoarsen; + + graph_hierarchy hierarchy; + + coarsen.perform_coarsening(config, G, hierarchy); + init_part.perform_initial_partitioning(config, hierarchy); + uncoarsen.perform_uncoarsening(config, hierarchy); + } + config.graph_allready_partitioned = true; + config.balance_factor = 0; + } } void graph_partitioner::perform_partitioning( PartitionConfig & config, graph_access & G) { - if(config.only_first_level) { - if( !config.graph_allready_partitioned) { - initial_partitioning init_part; - init_part.perform_initial_partitioning(config, G); - } - - if( !config.mh_no_mh ) { - complete_boundary boundary(&G); - boundary.build(); - refinement* refine = new mixed_refinement(); - refine->perform_refinement(config, G, boundary); - delete refine; - } - - return; - } - - if( config.repetitions == 1 ) { - single_run(config,G); - } else { - quality_metrics qm; - // currently only for ecosocial - EdgeWeight best_cut = std::numeric_limits< EdgeWeight >::max(); - std::vector< PartitionID > best_map = std::vector< PartitionID >(G.number_of_nodes()); - for( int i = 0; i < config.repetitions; i++) { - forall_nodes(G, node) { - G.setPartitionIndex(node,0); - } endfor - PartitionConfig working_config = config; - single_run(working_config, G); - - EdgeWeight cur_cut = qm.edge_cut(G); - if( cur_cut < best_cut ) { - forall_nodes(G, node) { - best_map[node] = G.getPartitionIndex(node); - } endfor - - best_cut = cur_cut; - } - } - - forall_nodes(G, node) { - G.setPartitionIndex(node, best_map[node]); - } endfor - - } -} + if(config.only_first_level) { + if( !config.graph_allready_partitioned) { + initial_partitioning init_part; + init_part.perform_initial_partitioning(config, G); + } + + if( !config.mh_no_mh ) { + complete_boundary boundary(&G); + boundary.build(); + refinement* refine = new mixed_refinement(); + refine->perform_refinement(config, G, boundary); + delete refine; + } + + return; + } + + if( config.repetitions == 1 ) { + single_run(config,G); + } else { + quality_metrics qm; + // currently only for ecosocial + EdgeWeight best_cut = std::numeric_limits< EdgeWeight >::max(); + std::vector< PartitionID > best_map = std::vector< PartitionID >(G.number_of_nodes()); + for( int i = 0; i < config.repetitions; i++) { + forall_nodes(G, node) { + G.setPartitionIndex(node,0); + } endfor + PartitionConfig working_config = config; + single_run(working_config, G); + + EdgeWeight cur_cut = qm.edge_cut(G); + if( cur_cut < best_cut ) { + forall_nodes(G, node) { + best_map[node] = G.getPartitionIndex(node); + } endfor + + best_cut = cur_cut; + } + } + + forall_nodes(G, node) { + G.setPartitionIndex(node, best_map[node]); + } endfor +} +} +} diff --git a/parallel/modified_kahip/lib/partition/graph_partitioner.h b/parallel/modified_kahip/lib/partition/graph_partitioner.h index 96e458ad..3215c891 100644 --- a/parallel/modified_kahip/lib/partition/graph_partitioner.h +++ b/parallel/modified_kahip/lib/partition/graph_partitioner.h @@ -13,24 +13,24 @@ #include "data_structure/graph_access.h" #include "partition_config.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class graph_partitioner { public: - graph_partitioner(); - virtual ~graph_partitioner(); + graph_partitioner(); + virtual ~graph_partitioner(); - void perform_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); - void perform_recursive_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); + void perform_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); + void perform_recursive_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); private: - void perform_recursive_partitioning_internal(PartitionConfig & graph_partitioner_config, - graph_access & G, - PartitionID lb, PartitionID ub); - void single_run( PartitionConfig & config, graph_access & G); + void perform_recursive_partitioning_internal(PartitionConfig & graph_partitioner_config, + graph_access & G, + PartitionID lb, PartitionID ub); + void single_run( PartitionConfig & config, graph_access & G); - unsigned m_global_k; - int m_global_upper_bound; - int m_rnd_bal; + unsigned m_global_k; + int m_global_upper_bound; + int m_rnd_bal; }; - +} #endif /* end of include guard: PARTITION_OL9XTLU4 */ diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/bipartition.cpp b/parallel/modified_kahip/lib/partition/initial_partitioning/bipartition.cpp index 310d70b2..75f19aa0 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/bipartition.cpp +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/bipartition.cpp @@ -12,7 +12,7 @@ #include "timer.h" #include "uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { bipartition::bipartition() { } @@ -26,273 +26,274 @@ void bipartition::initial_partition( const PartitionConfig & config, graph_access & G, int* partition_map) { - timer t; - t.restart(); - unsigned iterations = config.bipartition_tries; - EdgeWeight best_cut = std::numeric_limits::max(); - int best_load = std::numeric_limits::max(); + timer t; + t.restart(); + unsigned iterations = config.bipartition_tries; + EdgeWeight best_cut = std::numeric_limits::max(); + int best_load = std::numeric_limits::max(); - for( unsigned i = 0; i < iterations; i++) { - if(config.bipartition_algorithm == BIPARTITION_BFS) { - grow_regions_bfs(config, G); - } else if( config.bipartition_algorithm == BIPARTITION_FM) { - grow_regions_fm(config, G); - } + for( unsigned i = 0; i < iterations; i++) { + if(config.bipartition_algorithm == BIPARTITION_BFS) { + grow_regions_bfs(config, G); + } else if( config.bipartition_algorithm == BIPARTITION_FM) { + grow_regions_fm(config, G); + } - G.set_partition_count(2); + G.set_partition_count(2); - post_fm(config, G); + post_fm(config, G); - quality_metrics qm; - EdgeWeight curcut = qm.edge_cut(G); + quality_metrics qm; + EdgeWeight curcut = qm.edge_cut(G); - int lhs_block_weight = 0; - int rhs_block_weight = 0; + int lhs_block_weight = 0; + int rhs_block_weight = 0; - forall_nodes(G, node) { - if(G.getPartitionIndex(node) == 0) { - lhs_block_weight += G.getNodeWeight(node); - } else { - rhs_block_weight += G.getNodeWeight(node); - } - } endfor + forall_nodes(G, node) { + if(G.getPartitionIndex(node) == 0) { + lhs_block_weight += G.getNodeWeight(node); + } else { + rhs_block_weight += G.getNodeWeight(node); + } + } endfor - int lhs_overload = std::max(lhs_block_weight - config.target_weights[0],0); - int rhs_overload = std::max(rhs_block_weight - config.target_weights[1],0); + int lhs_overload = std::max(lhs_block_weight - config.target_weights[0],0); + int rhs_overload = std::max(rhs_block_weight - config.target_weights[1],0); - if(curcut < best_cut || (curcut == best_cut && lhs_overload + rhs_block_weight < best_load) ) { - //store it - best_cut = curcut; - best_load = lhs_overload + rhs_overload; + if(curcut < best_cut || (curcut == best_cut && lhs_overload + rhs_block_weight < best_load) ) { + //store it + best_cut = curcut; + best_load = lhs_overload + rhs_overload; - forall_nodes(G, n) { - partition_map[n] = G.getPartitionIndex(n); - } endfor - } + forall_nodes(G, n) { + partition_map[n] = G.getPartitionIndex(n); + } endfor +} - } - PRINT(std::cout << "bipartition took " << t.elapsed() << std::endl;) + } + PRINT(std::cout << "bipartition took " << t.elapsed() << std::endl;) } -void bipartition::initial_partition( const PartitionConfig & config, - const unsigned int seed, - graph_access & G, +void bipartition::initial_partition( const PartitionConfig & config, + const unsigned int seed, + graph_access & G, int* xadj, - int* adjncy, - int* vwgt, + int* adjncy, + int* vwgt, int* adjwgt, int* partition_map) { - std::cout << "not implemented yet" << std::endl; + std::cout << "not implemented yet" << std::endl; } void bipartition::post_fm(const PartitionConfig & config, graph_access & G) { - refinement* refine = new quotient_graph_refinement(); - complete_boundary* boundary = new complete_boundary(&G); - boundary->build(); - - PartitionConfig initial_cfg = config; - initial_cfg.fm_search_limit = config.bipartition_post_fm_limits; - initial_cfg.refinement_type = REFINEMENT_TYPE_FM; - initial_cfg.refinement_scheduling_algorithm = REFINEMENT_SCHEDULING_ACTIVE_BLOCKS; - initial_cfg.bank_account_factor = 5; - initial_cfg.rebalance = true; - initial_cfg.softrebalance = true; - initial_cfg.upper_bound_partition = 100000000; - initial_cfg.initial_bipartitioning = true; - refine->perform_refinement(initial_cfg, G, *boundary); - - delete refine; - delete boundary; + refinement* refine = new quotient_graph_refinement(); + complete_boundary* boundary = new complete_boundary(&G); + boundary->build(); + + PartitionConfig initial_cfg = config; + initial_cfg.fm_search_limit = config.bipartition_post_fm_limits; + initial_cfg.refinement_type = REFINEMENT_TYPE_FM; + initial_cfg.refinement_scheduling_algorithm = REFINEMENT_SCHEDULING_ACTIVE_BLOCKS; + initial_cfg.bank_account_factor = 5; + initial_cfg.rebalance = true; + initial_cfg.softrebalance = true; + initial_cfg.upper_bound_partition = 100000000; + initial_cfg.initial_bipartitioning = true; + refine->perform_refinement(initial_cfg, G, *boundary); + + delete refine; + delete boundary; } NodeID bipartition::find_start_node( const PartitionConfig & config, graph_access & G) { - NodeID startNode = random_functions::nextInt(0, G.number_of_nodes()-1); - NodeID lastNode = startNode; - - int counter = G.number_of_nodes(); - while( G.getNodeDegree(startNode) == 0 && --counter > 0) { - startNode = random_functions::nextInt(0, G.number_of_nodes()-1); + NodeID startNode = random_functions::nextInt(0, G.number_of_nodes()-1); + NodeID lastNode = startNode; + + int counter = G.number_of_nodes(); + while( G.getNodeDegree(startNode) == 0 && --counter > 0) { + startNode = random_functions::nextInt(0, G.number_of_nodes()-1); + } + + //now perform a bfs to get a partition + for( unsigned i = 0; i < 3; i++) { + std::vector touched(G.number_of_nodes(), false); + startNode = lastNode; + touched[startNode] = true; + + std::queue* bfsqueue = new std::queue; + bfsqueue->push(startNode); + while(!bfsqueue->empty()) { + NodeID source = bfsqueue->front(); + lastNode = source; + bfsqueue->pop(); + + forall_out_edges(G, e, source) { + NodeID target = G.getEdgeTarget(e); + if(!touched[target]) { + touched[target] = true; + bfsqueue->push(target); } + } endfor +} + delete bfsqueue; - //now perform a bfs to get a partition - for( unsigned i = 0; i < 3; i++) { - std::vector touched(G.number_of_nodes(), false); - startNode = lastNode; - touched[startNode] = true; - - std::queue* bfsqueue = new std::queue; - bfsqueue->push(startNode); - while(!bfsqueue->empty()) { - NodeID source = bfsqueue->front(); - lastNode = source; - bfsqueue->pop(); - - forall_out_edges(G, e, source) { - NodeID target = G.getEdgeTarget(e); - if(!touched[target]) { - touched[target] = true; - bfsqueue->push(target); - } - } endfor - } - delete bfsqueue; - - } - return lastNode; + } + return lastNode; } void bipartition::grow_regions_bfs(const PartitionConfig & config, graph_access & G) { - if(G.number_of_nodes() == 0) return; - - NodeID startNode = random_functions::nextInt(0, G.number_of_nodes()-1); - if(config.buffoon) { startNode = find_start_node(config, G); } // more likely to produce connected partitions - - std::vector touched(G.number_of_nodes(), false); - touched[startNode] = true; - NodeWeight cur_partition_weight = 0; - - forall_nodes(G, node) { - G.setPartitionIndex(node, 1); - } endfor - - NodeID nodes_left = G.number_of_nodes()-1; - - //now perform a bfs to get a partition - std::queue* bfsqueue = new std::queue; - bfsqueue->push(startNode); - for(;;) { - if( nodes_left == 1 ) { - //only one node left --> we have to break - break; - } - - if(bfsqueue->empty() && nodes_left > 0) { - //disconnected graph -> find a new start node among those that havent been touched - NodeID k = random_functions::nextInt(0, nodes_left-1); - NodeID start_node = 0; - forall_nodes(G, node) { - if(!touched[node]) { - if(k == 0) { - if( G.getNodeDegree(node) != 0) { - start_node = node; - break; - } else { - G.setPartitionIndex(node, 0); - nodes_left--; - cur_partition_weight += G.getNodeWeight(node); - touched[node] = true; - - if(cur_partition_weight >= (NodeWeight) config.grow_target) break; - } - } else { - k--; - } - } - } endfor - - if(cur_partition_weight >= (NodeWeight) config.grow_target) break; - - bfsqueue->push(start_node); - touched[start_node] = true; - } else if (bfsqueue->empty() && nodes_left == 0) { - break; - } - - NodeID source = bfsqueue->front(); - bfsqueue->pop(); - G.setPartitionIndex(source, 0); - - nodes_left--; - cur_partition_weight += G.getNodeWeight(source); - - if(cur_partition_weight >= (NodeWeight) config.grow_target) break; - - forall_out_edges(G, e, source) { - NodeID target = G.getEdgeTarget(e); - if(!touched[target]) { - touched[target] = true; - bfsqueue->push(target); - } - } endfor + if(G.number_of_nodes() == 0) return; + + NodeID startNode = random_functions::nextInt(0, G.number_of_nodes()-1); + if(config.buffoon) { startNode = find_start_node(config, G); } // more likely to produce connected partitions + + std::vector touched(G.number_of_nodes(), false); + touched[startNode] = true; + NodeWeight cur_partition_weight = 0; + + forall_nodes(G, node) { + G.setPartitionIndex(node, 1); + } endfor + + NodeID nodes_left = G.number_of_nodes()-1; + + //now perform a bfs to get a partition + std::queue* bfsqueue = new std::queue; + bfsqueue->push(startNode); + for(;;) { + if( nodes_left == 1 ) { + //only one node left --> we have to break + break; + } + + if(bfsqueue->empty() && nodes_left > 0) { + //disconnected graph -> find a new start node among those that havent been touched + NodeID k = random_functions::nextInt(0, nodes_left-1); + NodeID start_node = 0; + forall_nodes(G, node) { + if(!touched[node]) { + if(k == 0) { + if( G.getNodeDegree(node) != 0) { + start_node = node; + break; + } else { + G.setPartitionIndex(node, 0); + nodes_left--; + cur_partition_weight += G.getNodeWeight(node); + touched[node] = true; + + if(cur_partition_weight >= (NodeWeight) config.grow_target) break; + } + } else { + k--; + } } - delete bfsqueue; + } endfor + + if(cur_partition_weight >= (NodeWeight) config.grow_target) break; + + bfsqueue->push(start_node); + touched[start_node] = true; + } else if (bfsqueue->empty() && nodes_left == 0) { + break; + } + + NodeID source = bfsqueue->front(); + bfsqueue->pop(); + G.setPartitionIndex(source, 0); + + nodes_left--; + cur_partition_weight += G.getNodeWeight(source); + + if(cur_partition_weight >= (NodeWeight) config.grow_target) break; + + forall_out_edges(G, e, source) { + NodeID target = G.getEdgeTarget(e); + if(!touched[target]) { + touched[target] = true; + bfsqueue->push(target); + } + } endfor +} + delete bfsqueue; } void bipartition::grow_regions_fm(const PartitionConfig & config, graph_access & G) { - if(G.number_of_nodes() == 0) return; - - //NodeID startNode = random_functions::nextInt(0, G.number_of_nodes()-1); - NodeID startNode = find_start_node(config, G); - - std::vector touched(G.number_of_nodes(), false); - touched[startNode] = true; - NodeWeight cur_partition_weight = 0; - - forall_nodes(G, node) { - G.setPartitionIndex(node, 1); - } endfor - - NodeID nodes_left = G.number_of_nodes()-1; - - //now perform a pseudo dijkstra to get a partition - maxNodeHeap* queue = new maxNodeHeap(); - queue->insert(startNode, 0); // in this case the gain doesn't really matter - - for(;;) { - if( nodes_left == 1 ) { - //only one node left --> we have to break - break; - } - - if(queue->empty() && nodes_left > 0) { - //disconnected graph -> find a new start node among those that havent been touched - NodeID k = random_functions::nextInt(0, nodes_left-1); - NodeID start_node = 0; - forall_nodes(G, node) { - if(!touched[node]) { - if(k == 0) { - start_node = node; - break; - } else { - k--; - } - } - } endfor - - queue->insert(start_node, 0); - touched[start_node] = true; - } else if (queue->empty() && nodes_left == 0) { - break; - } - - NodeID source = queue->deleteMax(); - G.setPartitionIndex(source, 0); - - nodes_left--; - cur_partition_weight += G.getNodeWeight(source); - - if(cur_partition_weight >= (NodeWeight)config.grow_target) break; - - forall_out_edges(G, e, source) { - NodeID target = G.getEdgeTarget(e); - if(G.getPartitionIndex(target) == 1) { //then we might need to update the gain! - Gain gain = compute_gain(G, target, 0); - touched[target] = true; - - if(queue->contains(target)) { - //change the gain - queue->changeKey(target, gain); - } else { - //insert - queue->insert(target, gain); - } - - } - } endfor + if(G.number_of_nodes() == 0) return; + + //NodeID startNode = random_functions::nextInt(0, G.number_of_nodes()-1); + NodeID startNode = find_start_node(config, G); + + std::vector touched(G.number_of_nodes(), false); + touched[startNode] = true; + NodeWeight cur_partition_weight = 0; + + forall_nodes(G, node) { + G.setPartitionIndex(node, 1); + } endfor + + NodeID nodes_left = G.number_of_nodes()-1; + + //now perform a pseudo dijkstra to get a partition + maxNodeHeap* queue = new maxNodeHeap(); + queue->insert(startNode, 0); // in this case the gain doesn't really matter + + for(;;) { + if( nodes_left == 1 ) { + //only one node left --> we have to break + break; + } + + if(queue->empty() && nodes_left > 0) { + //disconnected graph -> find a new start node among those that havent been touched + NodeID k = random_functions::nextInt(0, nodes_left-1); + NodeID start_node = 0; + forall_nodes(G, node) { + if(!touched[node]) { + if(k == 0) { + start_node = node; + break; + } else { + k--; + } + } + } endfor + + queue->insert(start_node, 0); + touched[start_node] = true; + } else if (queue->empty() && nodes_left == 0) { + break; + } + + NodeID source = queue->deleteMax(); + G.setPartitionIndex(source, 0); + + nodes_left--; + cur_partition_weight += G.getNodeWeight(source); + + if(cur_partition_weight >= (NodeWeight)config.grow_target) break; + + forall_out_edges(G, e, source) { + NodeID target = G.getEdgeTarget(e); + if(G.getPartitionIndex(target) == 1) { //then we might need to update the gain! + Gain gain = compute_gain(G, target, 0); + touched[target] = true; + + if(queue->contains(target)) { + //change the gain + queue->changeKey(target, gain); + } else { + //insert + queue->insert(target, gain); } - delete queue; + + } + } endfor +} + delete queue; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/bipartition.h b/parallel/modified_kahip/lib/partition/initial_partitioning/bipartition.h index 7200ee35..a5d41529 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/bipartition.h +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/bipartition.h @@ -9,32 +9,32 @@ #define BIPARTITION_7I4IR31Y #include "initial_partitioner.h" - +namespace kahip::modified { class bipartition : public initial_partitioner { - public: - bipartition(); - virtual ~bipartition(); - - void initial_partition( const PartitionConfig & config, - const unsigned int seed, - graph_access & G, - int* partition_map); - - void initial_partition( const PartitionConfig & config, - const unsigned int seed, - graph_access & G, - int* xadj, - int* adjncy, - int* vwgt, - int* adjwgt, - int* partition_map); - - private: - void grow_regions_bfs(const PartitionConfig & config, graph_access & G); - void grow_regions_fm(const PartitionConfig & config, graph_access & G); - NodeID find_start_node( const PartitionConfig & config, graph_access & G); - void post_fm(const PartitionConfig & config, graph_access & G); - inline Gain compute_gain( graph_access & G, NodeID node, PartitionID targeting_partition); +public: + bipartition(); + virtual ~bipartition(); + + void initial_partition( const PartitionConfig & config, + const unsigned int seed, + graph_access & G, + int* partition_map); + + void initial_partition( const PartitionConfig & config, + const unsigned int seed, + graph_access & G, + int* xadj, + int* adjncy, + int* vwgt, + int* adjwgt, + int* partition_map); + +private: + void grow_regions_bfs(const PartitionConfig & config, graph_access & G); + void grow_regions_fm(const PartitionConfig & config, graph_access & G); + NodeID find_start_node( const PartitionConfig & config, graph_access & G); + void post_fm(const PartitionConfig & config, graph_access & G); + inline Gain compute_gain( graph_access & G, NodeID node, PartitionID targeting_partition); }; @@ -52,6 +52,6 @@ inline Gain bipartition::compute_gain( graph_access & G, NodeID node, PartitionI return gain; } - +} #endif /* end of include guard: BIPARTITION_7I4IR31Y */ diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partition_bipartition.cpp b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partition_bipartition.cpp index 73c3e7cb..98abccde 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partition_bipartition.cpp +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partition_bipartition.cpp @@ -10,7 +10,7 @@ #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.h" #include "uncoarsening/refinement/mixed_refinement.h" #include "graph_partitioner.h" - +namespace kahip::modified { initial_partition_bipartition::initial_partition_bipartition() { } @@ -22,52 +22,52 @@ initial_partition_bipartition::~initial_partition_bipartition() { void initial_partition_bipartition::initial_partition( const PartitionConfig & config, const unsigned int seed, graph_access & G, int* partition_map) { - graph_partitioner gp; - PartitionConfig rec_config = config; - rec_config.initial_partitioning_type = INITIAL_PARTITIONING_BIPARTITION; - rec_config.initial_partitioning_repetitions = 0; - rec_config.global_cycle_iterations = 1; - rec_config.use_wcycles = false; - rec_config.use_fullmultigrid = false; - rec_config.fm_search_limit = config.bipartition_post_ml_limits; - rec_config.matching_type = MATCHING_GPA; - rec_config.permutation_quality = PERMUTATION_QUALITY_GOOD; - rec_config.initial_partitioning = true; - rec_config.graph_allready_partitioned = false; - rec_config.label_propagation_refinement = false; - - if( config.cluster_coarsening_during_ip == true) { - rec_config.matching_type = CLUSTER_COARSENING; - rec_config.cluster_coarsening_factor = 12; - rec_config.ensemble_clusterings = false; - } - - - std::streambuf* backup = std::cout.rdbuf(); - std::ofstream ofs; - ofs.open("/dev/null"); - std::cout.rdbuf(ofs.rdbuf()); - - gp.perform_recursive_partitioning(rec_config, G); - - ofs.close(); - std::cout.rdbuf(backup); + graph_partitioner gp; + PartitionConfig rec_config = config; + rec_config.initial_partitioning_type = INITIAL_PARTITIONING_BIPARTITION; + rec_config.initial_partitioning_repetitions = 0; + rec_config.global_cycle_iterations = 1; + rec_config.use_wcycles = false; + rec_config.use_fullmultigrid = false; + rec_config.fm_search_limit = config.bipartition_post_ml_limits; + rec_config.matching_type = MATCHING_GPA; + rec_config.permutation_quality = PERMUTATION_QUALITY_GOOD; + rec_config.initial_partitioning = true; + rec_config.graph_allready_partitioned = false; + rec_config.label_propagation_refinement = false; + + if( config.cluster_coarsening_during_ip == true) { + rec_config.matching_type = CLUSTER_COARSENING; + rec_config.cluster_coarsening_factor = 12; + rec_config.ensemble_clusterings = false; + } + + + std::streambuf* backup = std::cout.rdbuf(); + std::ofstream ofs; + ofs.open("/dev/null"); + std::cout.rdbuf(ofs.rdbuf()); + + gp.perform_recursive_partitioning(rec_config, G); + + ofs.close(); + std::cout.rdbuf(backup); + + forall_nodes(G, n) { + partition_map[n] = G.getPartitionIndex(n); + } endfor - forall_nodes(G, n) { - partition_map[n] = G.getPartitionIndex(n); - } endfor - -} +} -void initial_partition_bipartition::initial_partition( const PartitionConfig & config, - const unsigned int seed, - graph_access & G, +void initial_partition_bipartition::initial_partition( const PartitionConfig & config, + const unsigned int seed, + graph_access & G, int* xadj, - int* adjncy, - int* vwgt, + int* adjncy, + int* vwgt, int* adjwgt, int* partition_map) { - std::cout << "not implemented yet" << std::endl; + std::cout << "not implemented yet" << std::endl; } - +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partition_bipartition.h b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partition_bipartition.h index ac652079..2989eb8b 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partition_bipartition.h +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partition_bipartition.h @@ -9,23 +9,23 @@ #define INITIAL_PARTITION_BIPARTITION_HMA7329W #include "initial_partitioner.h" - +namespace kahip::modified { class initial_partition_bipartition : public initial_partitioner { public: - initial_partition_bipartition(); - virtual ~initial_partition_bipartition(); + initial_partition_bipartition(); + virtual ~initial_partition_bipartition(); - void initial_partition( const PartitionConfig & config, const unsigned int seed, graph_access & G, int* partition_map); + void initial_partition( const PartitionConfig & config, const unsigned int seed, graph_access & G, int* partition_map); - void initial_partition( const PartitionConfig & config, const unsigned int seed, - graph_access & G, - int* xadj, - int* adjncy, - int* vwgt, - int* adjwgt, - int* partition_map); + void initial_partition( const PartitionConfig & config, const unsigned int seed, + graph_access & G, + int* xadj, + int* adjncy, + int* vwgt, + int* adjwgt, + int* partition_map); }; - +} #endif /* end of include guard: INITIAL_PARTITION_BIPARTITION_HMA7329W */ diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioner.cpp b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioner.cpp index 63669ec9..ad861e88 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioner.cpp +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioner.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "initial_partitioner.h" - +namespace kahip::modified { initial_partitioner::initial_partitioner() { } @@ -14,4 +14,5 @@ initial_partitioner::initial_partitioner() { initial_partitioner::~initial_partitioner() { } +} diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioner.h b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioner.h index f3a176f3..4e71e814 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioner.h +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioner.h @@ -10,25 +10,25 @@ #include "partition_config.h" #include "data_structure/graph_access.h" - +namespace kahip::modified { class initial_partitioner { - public: - initial_partitioner( ); - virtual ~initial_partitioner(); +public: + initial_partitioner( ); + virtual ~initial_partitioner(); - virtual void initial_partition( const PartitionConfig & config, const unsigned int seed, - graph_access & G, - int* xadj, - int* adjncy, - int* vwgt, - int* adjwgt, - int* partition_map) = 0; + virtual void initial_partition( const PartitionConfig & config, const unsigned int seed, + graph_access & G, + int* xadj, + int* adjncy, + int* vwgt, + int* adjwgt, + int* partition_map) = 0; - virtual void initial_partition(const PartitionConfig & config, - const unsigned int seed, - graph_access & G, - int* partition_map) = 0; + virtual void initial_partition(const PartitionConfig & config, + const unsigned int seed, + graph_access & G, + int* partition_map) = 0; }; - +} #endif /* end of include guard: INITIAL_PARTITIONER_TJKC6RWY */ diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioning.cpp b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioning.cpp index 9fe7623b..cd004341 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioning.cpp +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioning.cpp @@ -16,7 +16,7 @@ #include "quality_metrics.h" #include "random_functions.h" #include "timer.h" - +namespace kahip::modified { initial_partitioning::initial_partitioning() { } @@ -26,99 +26,99 @@ initial_partitioning::~initial_partitioning() { } void initial_partitioning::perform_initial_partitioning(const PartitionConfig & config, graph_hierarchy & hierarchy) { - graph_access& G = *hierarchy.get_coarsest(); - perform_initial_partitioning(config, G); + graph_access& G = *hierarchy.get_coarsest(); + perform_initial_partitioning(config, G); } void initial_partitioning::perform_initial_partitioning(const PartitionConfig & config, graph_access & G) { - initial_partitioner* partition = NULL; - switch(config.initial_partitioning_type) { - case INITIAL_PARTITIONING_RECPARTITION: - partition = new initial_partition_bipartition(); - break; - case INITIAL_PARTITIONING_BIPARTITION: - partition = new bipartition(); - break; - - - } - - quality_metrics qm; - EdgeWeight best_cut; - int* best_map = new int[G.number_of_nodes()]; - if(config.graph_allready_partitioned && !config.omit_given_partitioning) { - best_cut = qm.edge_cut(G); - forall_nodes(G, n) { - best_map[n] = G.getPartitionIndex(n); - } endfor - } else { - best_cut = std::numeric_limits::max(); - } - - timer t; - t.restart(); - int* partition_map = new int[G.number_of_nodes()]; - unsigned reps_to_do = (unsigned) std::max((int)ceil(config.initial_partitioning_repetitions/(double)log2(config.k)),2); - - if(config.initial_partitioning_repetitions == 0) { - reps_to_do = 1; - } - if(config.eco) { - //bound the number of initial partitioning repetions - reps_to_do = std::min((int)config.minipreps, (int)reps_to_do); - } - - PRINT(std::cout << "no of initial partitioning repetitions = " << reps_to_do << std::endl;); - PRINT(std::cout << "no of nodes for partition = " << G.number_of_nodes() << std::endl;); - if(!((config.graph_allready_partitioned && config.no_new_initial_partitioning) || config.omit_given_partitioning)) { - for(unsigned int rep = 0; rep < reps_to_do; rep++) { - unsigned seed = random_functions::nextInt(0, std::numeric_limits::max()); - PartitionConfig working_config = config; - working_config.combine = false; - partition->initial_partition(working_config, seed, G, partition_map); - - EdgeWeight cur_cut = qm.edge_cut(G, partition_map); - if(cur_cut < best_cut) { - PRINT(std::cout << "log>" << "improved the current initial partitiong from " << best_cut - << " to " << cur_cut << std::endl;) - - forall_nodes(G, n) { - best_map[n] = partition_map[n]; - } endfor - - best_cut = cur_cut; - if(best_cut == 0) break; - } - } - - forall_nodes(G, n) { - G.setPartitionIndex(n,best_map[n]); - } endfor - } - - G.set_partition_count(config.k); - - PRINT(std::cout << "initial partitioning took " << t.elapsed() << std::endl;) - PRINT(std::cout << "log>" << "current initial balance " << qm.balance(G) << std::endl;) - - if(config.initial_partition_optimize || config.combine) { - initial_refinement iniref; - iniref.optimize(config, G, best_cut); - } - - PRINT(std::cout << "log>" << "final current initial partitiong from " << best_cut - << " to " << best_cut << std::endl;) - - if(!(config.graph_allready_partitioned && config.no_new_initial_partitioning)) { - PRINT(std::cout << "finalinitialcut " << best_cut << std::endl;) - PRINT(std::cout << "log>" << "final current initial balance " << qm.balance(G) << std::endl;) - } - - ASSERT_TRUE(graph_partition_assertions::assert_graph_has_kway_partition(config, G)); - - delete[] partition_map; - delete[] best_map; - delete partition; + initial_partitioner* partition = NULL; + switch(config.initial_partitioning_type) { + case INITIAL_PARTITIONING_RECPARTITION: + partition = new initial_partition_bipartition(); + break; + case INITIAL_PARTITIONING_BIPARTITION: + partition = new bipartition(); + break; + + + } + + quality_metrics qm; + EdgeWeight best_cut; + int* best_map = new int[G.number_of_nodes()]; + if(config.graph_allready_partitioned && !config.omit_given_partitioning) { + best_cut = qm.edge_cut(G); + forall_nodes(G, n) { + best_map[n] = G.getPartitionIndex(n); + } endfor +} else { + best_cut = std::numeric_limits::max(); +} + + timer t; + t.restart(); + int* partition_map = new int[G.number_of_nodes()]; + unsigned reps_to_do = (unsigned) std::max((int)ceil(config.initial_partitioning_repetitions/(double)log2(config.k)),2); + + if(config.initial_partitioning_repetitions == 0) { + reps_to_do = 1; + } + if(config.eco) { + //bound the number of initial partitioning repetions + reps_to_do = std::min((int)config.minipreps, (int)reps_to_do); + } + + PRINT(std::cout << "no of initial partitioning repetitions = " << reps_to_do << std::endl;); + PRINT(std::cout << "no of nodes for partition = " << G.number_of_nodes() << std::endl;); + if(!((config.graph_allready_partitioned && config.no_new_initial_partitioning) || config.omit_given_partitioning)) { + for(unsigned int rep = 0; rep < reps_to_do; rep++) { + unsigned seed = random_functions::nextInt(0, std::numeric_limits::max()); + PartitionConfig working_config = config; + working_config.combine = false; + partition->initial_partition(working_config, seed, G, partition_map); + + EdgeWeight cur_cut = qm.edge_cut(G, partition_map); + if(cur_cut < best_cut) { + PRINT(std::cout << "log>" << "improved the current initial partitiong from " << best_cut + << " to " << cur_cut << std::endl;) + + forall_nodes(G, n) { + best_map[n] = partition_map[n]; + } endfor + + best_cut = cur_cut; + if(best_cut == 0) break; + } + } + + forall_nodes(G, n) { + G.setPartitionIndex(n,best_map[n]); + } endfor +} + + G.set_partition_count(config.k); + + PRINT(std::cout << "initial partitioning took " << t.elapsed() << std::endl;) + PRINT(std::cout << "log>" << "current initial balance " << qm.balance(G) << std::endl;) + + if(config.initial_partition_optimize || config.combine) { + initial_refinement iniref; + iniref.optimize(config, G, best_cut); + } + + PRINT(std::cout << "log>" << "final current initial partitiong from " << best_cut + << " to " << best_cut << std::endl;) + + if(!(config.graph_allready_partitioned && config.no_new_initial_partitioning)) { + PRINT(std::cout << "finalinitialcut " << best_cut << std::endl;) + PRINT(std::cout << "log>" << "final current initial balance " << qm.balance(G) << std::endl;) } + ASSERT_TRUE(graph_partition_assertions::assert_graph_has_kway_partition(config, G)); + + delete[] partition_map; + delete[] best_map; + delete partition; +} +} diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioning.h b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioning.h index 892bf8af..62c51bb0 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioning.h +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_partitioning.h @@ -10,14 +10,14 @@ #include "data_structure/graph_hierarchy.h" #include "partition_config.h" - +namespace kahip::modified { class initial_partitioning { public: - initial_partitioning( ); - virtual ~initial_partitioning(); - void perform_initial_partitioning(const PartitionConfig & config, graph_hierarchy & hierarchy); - void perform_initial_partitioning(const PartitionConfig & config, graph_access & G); + initial_partitioning( ); + virtual ~initial_partitioning(); + void perform_initial_partitioning(const PartitionConfig & config, graph_hierarchy & hierarchy); + void perform_initial_partitioning(const PartitionConfig & config, graph_access & G); }; - +} #endif /* end of include guard: INITIAL_PARTITIONING_D7VA0XO9 */ diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_refinement/initial_refinement.cpp b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_refinement/initial_refinement.cpp index 18ef8f5c..39ecfc33 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_refinement/initial_refinement.cpp +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_refinement/initial_refinement.cpp @@ -8,7 +8,7 @@ #include "initial_refinement.h" #include "coarsening/coarsening.h" #include "uncoarsening/uncoarsening.h" - +namespace kahip::modified { initial_refinement::initial_refinement() { } @@ -19,27 +19,28 @@ initial_refinement::~initial_refinement() { int initial_refinement::optimize( const PartitionConfig & config, graph_access & G, EdgeWeight & initial_cut) { - PartitionConfig partition_config = config; - partition_config.graph_allready_partitioned = true; - partition_config.stop_rule = STOP_RULE_STRONG; - partition_config.fm_search_limit = partition_config.initial_partition_optimize_fm_limits; - partition_config.kway_fm_search_limit = partition_config.initial_partition_optimize_fm_limits; - partition_config.local_multitry_fm_alpha = partition_config.initial_partition_optimize_multitry_fm_alpha; - partition_config.local_multitry_rounds = partition_config.initial_partition_optimize_multitry_rounds; - partition_config.matching_type = MATCHING_GPA; - partition_config.gpa_grow_paths_between_blocks = false; - partition_config.kaffpa_perfectly_balanced_refinement = false; // for runtime reasons - - graph_hierarchy hierarchy; - - coarsening coarsen; - coarsen.perform_coarsening(partition_config, G, hierarchy); - - //ommit initial partitioning since we have the partition allread given - uncoarsening uncoarsen; - int improvement = 0; - improvement = uncoarsen.perform_uncoarsening(partition_config, hierarchy); - initial_cut -= improvement; - - return improvement; -} + PartitionConfig partition_config = config; + partition_config.graph_allready_partitioned = true; + partition_config.stop_rule = STOP_RULE_STRONG; + partition_config.fm_search_limit = partition_config.initial_partition_optimize_fm_limits; + partition_config.kway_fm_search_limit = partition_config.initial_partition_optimize_fm_limits; + partition_config.local_multitry_fm_alpha = partition_config.initial_partition_optimize_multitry_fm_alpha; + partition_config.local_multitry_rounds = partition_config.initial_partition_optimize_multitry_rounds; + partition_config.matching_type = MATCHING_GPA; + partition_config.gpa_grow_paths_between_blocks = false; + partition_config.kaffpa_perfectly_balanced_refinement = false; // for runtime reasons + + graph_hierarchy hierarchy; + + coarsening coarsen; + coarsen.perform_coarsening(partition_config, G, hierarchy); + + //ommit initial partitioning since we have the partition allread given + uncoarsening uncoarsen; + int improvement = 0; + improvement = uncoarsen.perform_uncoarsening(partition_config, hierarchy); + initial_cut -= improvement; + + return improvement; +} +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_refinement/initial_refinement.h b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_refinement/initial_refinement.h index 2f0e27e6..cccd0bd8 100644 --- a/parallel/modified_kahip/lib/partition/initial_partitioning/initial_refinement/initial_refinement.h +++ b/parallel/modified_kahip/lib/partition/initial_partitioning/initial_refinement/initial_refinement.h @@ -10,16 +10,16 @@ #include "data_structure/graph_access.h" #include "partition_config.h" - +namespace kahip::modified { class initial_refinement { public: - initial_refinement( ); - virtual ~initial_refinement(); + initial_refinement( ); + virtual ~initial_refinement(); - int optimize( const PartitionConfig & config, - graph_access & G, - EdgeWeight & initial_cut); + int optimize( const PartitionConfig & config, + graph_access & G, + EdgeWeight & initial_cut); }; - +} #endif /* end of include guard: INITIAL_REFINEMENT_LDIIF5CG */ diff --git a/parallel/modified_kahip/lib/partition/partition_config.h b/parallel/modified_kahip/lib/partition/partition_config.h index aed9294f..cac89afa 100644 --- a/parallel/modified_kahip/lib/partition/partition_config.h +++ b/parallel/modified_kahip/lib/partition/partition_config.h @@ -9,321 +9,321 @@ #define PARTITION_CONFIG_DI1ES4T0 #include "definitions.h" - +namespace kahip::modified { // Configuration for the partitioning. struct PartitionConfig { - PartitionConfig() {} + PartitionConfig() {} + + //============================================================ + //=======================MATCHING============================= + //============================================================ + bool edge_rating_tiebreaking; + + EdgeRating edge_rating; + + PermutationQuality permutation_quality; + + MatchingType matching_type; + + bool match_islands; + + bool first_level_random_matching; + + bool rate_first_level_inner_outer; + + NodeWeight max_vertex_weight; - //============================================================ - //=======================MATCHING============================= - //============================================================ - bool edge_rating_tiebreaking; + NodeWeight largest_graph_weight; - EdgeRating edge_rating; - - PermutationQuality permutation_quality; + unsigned aggressive_random_levels; - MatchingType matching_type; - - bool match_islands; + bool disable_max_vertex_weight_constraint; - bool first_level_random_matching; - - bool rate_first_level_inner_outer; + //============================================================ + //===================INITIAL PARTITIONING===================== + //============================================================ + unsigned int initial_partitioning_repetitions; - NodeWeight max_vertex_weight; - - NodeWeight largest_graph_weight; + unsigned int minipreps; - unsigned aggressive_random_levels; - - bool disable_max_vertex_weight_constraint; + bool refined_bubbling; - //============================================================ - //===================INITIAL PARTITIONING===================== - //============================================================ - unsigned int initial_partitioning_repetitions; + InitialPartitioningType initial_partitioning_type; - unsigned int minipreps; + bool initial_partition_optimize; - bool refined_bubbling; + BipartitionAlgorithm bipartition_algorithm; - InitialPartitioningType initial_partitioning_type; + bool initial_partitioning; - bool initial_partition_optimize; + int bipartition_tries; - BipartitionAlgorithm bipartition_algorithm; + int bipartition_post_fm_limits; - bool initial_partitioning; + int bipartition_post_ml_limits; - int bipartition_tries; + //============================================================ + //====================REFINEMENT PARAMETERS=================== + //============================================================ + bool corner_refinement_enabled; - int bipartition_post_fm_limits; + bool use_bucket_queues; - int bipartition_post_ml_limits; + RefinementType refinement_type; - //============================================================ - //====================REFINEMENT PARAMETERS=================== - //============================================================ - bool corner_refinement_enabled; + PermutationQuality permutation_during_refinement; - bool use_bucket_queues; + ImbalanceType imbalance; - RefinementType refinement_type; + unsigned bubbling_iterations; - PermutationQuality permutation_during_refinement; + unsigned kway_rounds; - ImbalanceType imbalance; + bool quotient_graph_refinement_disabled; - unsigned bubbling_iterations; - - unsigned kway_rounds; - - bool quotient_graph_refinement_disabled; + KWayStopRule kway_stop_rule; - KWayStopRule kway_stop_rule; + double kway_adaptive_limits_alpha; - double kway_adaptive_limits_alpha; + double kway_adaptive_limits_beta; - double kway_adaptive_limits_beta; + unsigned max_flow_iterations; - unsigned max_flow_iterations; + unsigned local_multitry_rounds; - unsigned local_multitry_rounds; - - unsigned local_multitry_fm_alpha; + unsigned local_multitry_fm_alpha; - bool graph_allready_partitioned; + bool graph_allready_partitioned; - unsigned int fm_search_limit; - - unsigned int kway_fm_search_limit; + unsigned int fm_search_limit; - NodeWeight upper_bound_partition; + unsigned int kway_fm_search_limit; - double bank_account_factor; + NodeWeight upper_bound_partition; - RefinementSchedulingAlgorithm refinement_scheduling_algorithm; + double bank_account_factor; - bool most_balanced_minimum_cuts; - - unsigned toposort_iterations; + RefinementSchedulingAlgorithm refinement_scheduling_algorithm; - bool softrebalance; + bool most_balanced_minimum_cuts; - bool rebalance; + unsigned toposort_iterations; - double flow_region_factor; + bool softrebalance; - bool gpa_grow_paths_between_blocks; + bool rebalance; - //======================================= - //==========GLOBAL SEARCH PARAMETERS===== - //======================================= - unsigned global_cycle_iterations; + double flow_region_factor; - bool use_wcycles; + bool gpa_grow_paths_between_blocks; - bool use_fullmultigrid; + //======================================= + //==========GLOBAL SEARCH PARAMETERS===== + //======================================= + unsigned global_cycle_iterations; - unsigned level_split; + bool use_wcycles; - bool no_new_initial_partitioning; + bool use_fullmultigrid; - bool omit_given_partitioning; + unsigned level_split; - StopRule stop_rule; + bool no_new_initial_partitioning; - int num_vert_stop_factor; - - bool no_change_convergence; + bool omit_given_partitioning; - //======================================= - //===PERFECTLY BALANCED PARTITIONING ==== - //======================================= + StopRule stop_rule; + + int num_vert_stop_factor; + + bool no_change_convergence; + + //======================================= + //===PERFECTLY BALANCED PARTITIONING ==== + //======================================= bool remove_negative_cycles; - bool kaba_include_removal_of_paths; + bool kaba_include_removal_of_paths; - bool kaba_enable_zero_weight_cycles; + bool kaba_enable_zero_weight_cycles; - double kabaE_internal_bal; + double kabaE_internal_bal; - CycleRefinementAlgorithm cycle_refinement_algorithm; + CycleRefinementAlgorithm cycle_refinement_algorithm; - int kaba_internal_no_aug_steps_aug; + int kaba_internal_no_aug_steps_aug; - unsigned kaba_packing_iterations; + unsigned kaba_packing_iterations; - bool kaba_flip_packings; + bool kaba_flip_packings; - MLSRule kaba_lsearch_p; // more localized search pseudo directed + MLSRule kaba_lsearch_p; // more localized search pseudo directed - bool kaffpa_perfectly_balanced_refinement; + bool kaffpa_perfectly_balanced_refinement; - unsigned kaba_unsucc_iterations; + unsigned kaba_unsucc_iterations; - - //======================================= - //============PAR_PSEUDOMH / MH ========= - //======================================= + + //======================================= + //============PAR_PSEUDOMH / MH ========= + //======================================= double time_limit; - double epsilon; + double epsilon; unsigned no_unsuc_reps; unsigned local_partitioning_repetitions; - bool mh_plain_repetitions; - - bool mh_easy_construction; + bool mh_plain_repetitions; + + bool mh_easy_construction; - bool mh_enable_gal_combine; + bool mh_enable_gal_combine; - bool mh_no_mh; + bool mh_no_mh; - bool mh_print_log; + bool mh_print_log; - int mh_flip_coin; + int mh_flip_coin; - int mh_initial_population_fraction; + int mh_initial_population_fraction; - bool mh_disable_cross_combine; + bool mh_disable_cross_combine; - bool mh_cross_combine_original_k; + bool mh_cross_combine_original_k; - bool mh_disable_nc_combine; + bool mh_disable_nc_combine; - bool mh_disable_combine; + bool mh_disable_combine; - bool mh_enable_quickstart; + bool mh_enable_quickstart; - bool mh_disable_diversify_islands; + bool mh_disable_diversify_islands; - bool mh_diversify; + bool mh_diversify; - bool mh_diversify_best; + bool mh_diversify_best; - bool mh_enable_tournament_selection; + bool mh_enable_tournament_selection; - bool mh_optimize_communication_volume; + bool mh_optimize_communication_volume; - unsigned mh_num_ncs_to_compute; + unsigned mh_num_ncs_to_compute; - unsigned mh_pool_size; + unsigned mh_pool_size; - bool combine; // in this case the second index is filled and edges between both partitions are not contracted + bool combine; // in this case the second index is filled and edges between both partitions are not contracted - unsigned initial_partition_optimize_fm_limits; + unsigned initial_partition_optimize_fm_limits; - unsigned initial_partition_optimize_multitry_fm_alpha; + unsigned initial_partition_optimize_multitry_fm_alpha; - unsigned initial_partition_optimize_multitry_rounds; + unsigned initial_partition_optimize_multitry_rounds; - unsigned walshaw_mh_repetitions; + unsigned walshaw_mh_repetitions; - unsigned scaleing_factor; + unsigned scaleing_factor; - bool scale_back; + bool scale_back; bool suppress_partitioner_output; - unsigned maxT; - - unsigned maxIter; - //======================================= - //===============BUFFOON================= - //======================================= - bool disable_hard_rebalance; + unsigned maxT; - bool buffoon; + unsigned maxIter; + //======================================= + //===============BUFFOON================= + //======================================= + bool disable_hard_rebalance; - bool kabapE; - - bool mh_penalty_for_unconnected; - //======================================= - //===============MISC==================== - //======================================= - std::string input_partition; + bool buffoon; - int seed; + bool kabapE; - bool fast; + bool mh_penalty_for_unconnected; + //======================================= + //===============MISC==================== + //======================================= + std::string input_partition; - bool eco; + int seed; - bool strong; + bool fast; - // number of blocks the graph should be partitioned in - PartitionID k; + bool eco; - bool compute_vertex_separator; + bool strong; - bool only_first_level; + // number of blocks the graph should be partitioned in + PartitionID k; - bool use_balance_singletons; + bool compute_vertex_separator; - int amg_iterations; + bool only_first_level; - std::string graph_filename; + bool use_balance_singletons; - bool kaffpa_perfectly_balance; + int amg_iterations; - //======================================= - //===========SNW PARTITIONING============ - //======================================= - NodeOrderingType node_ordering; + std::string graph_filename; - int cluster_coarsening_factor; + bool kaffpa_perfectly_balance; - bool ensemble_clusterings; + //======================================= + //===========SNW PARTITIONING============ + //======================================= + NodeOrderingType node_ordering; - int label_iterations; + int cluster_coarsening_factor; - int label_iterations_refinement; + bool ensemble_clusterings; - int number_of_clusterings; + int label_iterations; - bool label_propagation_refinement; + int label_iterations_refinement; - double balance_factor; + int number_of_clusterings; - bool cluster_coarsening_during_ip; + bool label_propagation_refinement; - bool set_upperbound; + double balance_factor; - int repetitions; - - //======================================= - //=========LABEL PROPAGATION============= - //======================================= - NodeWeight cluster_upperbound; + bool cluster_coarsening_during_ip; - bool ultra_fast_kaffpaE_interfacecall; + bool set_upperbound; - //======================================= - //=========INITIAL PARTITIONING========== - //======================================= + int repetitions; - // variables controling the size of the blocks during - // multilevel recursive bisection - // (for the case where k is not a power of 2) - std::vector target_weights; + //======================================= + //=========LABEL PROPAGATION============= + //======================================= + NodeWeight cluster_upperbound; - bool initial_bipartitioning; + bool ultra_fast_kaffpaE_interfacecall; - int grow_target; + //======================================= + //=========INITIAL PARTITIONING========== + //======================================= - //======================================= - //===============Shared Mem OMP========== - //======================================= - bool enable_omp; + // variables controling the size of the blocks during + // multilevel recursive bisection + // (for the case where k is not a power of 2) + std::vector target_weights; - void LogDump(FILE *out) const { - } -}; + bool initial_bipartitioning; + + int grow_target; + //======================================= + //===============Shared Mem OMP========== + //======================================= + bool enable_omp; + + void LogDump(FILE *out) const { + } +}; +} #endif /* end of include guard: PARTITION_CONFIG_DI1ES4T0 */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.cpp index 943a5211..e733dcf0 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.cpp @@ -17,7 +17,7 @@ #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.h" #include "uncoarsening/refinement/kway_graph_refinement/kway_stop_rule.h" #include "uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h" - +namespace kahip::modified { unsigned long advanced_models::conflicts = 0; @@ -36,171 +36,171 @@ bool advanced_models::compute_vertex_movements_rebalance_ultra( PartitionConfig unsigned & steps) { - graph_access G_bar; - boundary.getUnderlyingQuotientGraph(G_bar); + graph_access G_bar; + boundary.getUnderlyingQuotientGraph(G_bar); - aqg.prepare(config, G, G_bar, steps); + aqg.prepare(config, G, G_bar, steps); - std::vector feasable_edge; - std::vector id_mapping; - NodeID s; NodeID t; // start vertex - do { - graph_access cycle_problem; - build_rebalance_model( config, G, G_bar, boundary, aqg, - feasable_edge, steps, cycle_problem, s, t, id_mapping); + std::vector feasable_edge; + std::vector id_mapping; + NodeID s; NodeID t; // start vertex + do { + graph_access cycle_problem; + build_rebalance_model( config, G, G_bar, boundary, aqg, + feasable_edge, steps, cycle_problem, s, t, id_mapping); - //******************************************************************** - //solve the problem - //******************************************************************** - cycle_search cs; - std::vector path; - cs.find_shortest_path(cycle_problem, s, t, path); + //******************************************************************** + //solve the problem + //******************************************************************** + cycle_search cs; + std::vector path; + cs.find_shortest_path(cycle_problem, s, t, path); - //detect conflict -- a block should be at most one time in a cycle - bool conflict_detected = handle_ultra_model_conflicts(config, cycle_problem, - boundary, id_mapping, - feasable_edge, path, - s, aqg, true); - if(!conflict_detected) { - perform_augmented_move(config, G, boundary, path, s, t, aqg); - return true; - } - } while( true ); // at some point the model will become feasable! (otherwise the method would not have been entered and the fall back algorithm would have been applied + //detect conflict -- a block should be at most one time in a cycle + bool conflict_detected = handle_ultra_model_conflicts(config, cycle_problem, + boundary, id_mapping, + feasable_edge, path, + s, aqg, true); + if(!conflict_detected) { + perform_augmented_move(config, G, boundary, path, s, t, aqg); + return true; + } + } while( true ); // at some point the model will become feasable! (otherwise the method would not have been entered and the fall back algorithm would have been applied - return false; + return false; } -bool advanced_models::compute_vertex_movements_rebalance( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - augmented_Qgraph & aqg, +bool advanced_models::compute_vertex_movements_rebalance( PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + augmented_Qgraph & aqg, unsigned & steps) { - graph_access cycle_problem; - graph_access G_bar; - boundary.getUnderlyingQuotientGraph(G_bar); - - aqg.prepare(config, G, G_bar, steps); - - //*********************************************************************** - //build the model - //*********************************************************************** - NodeID number_of_nodes = G_bar.number_of_nodes()*steps + 2; - EdgeID number_of_edges = G_bar.number_of_edges()*steps + 2*number_of_nodes; - - cycle_problem.start_construction(number_of_nodes, number_of_edges); - NodeID s = number_of_nodes - 2; - NodeID t = number_of_nodes - 1 ; - - for( unsigned s_idx = 0; s_idx < steps; s_idx++) { - //create a new layer - forall_nodes(G_bar, lhs) { - NodeID cur_node = cycle_problem.new_node(); - forall_out_edges(G_bar, e, lhs) { - EdgeID rhs = G_bar.getEdgeTarget(e); - - //find the right edge in the augmented quotient graph - boundary_pair bp; - bp.k = config.k; - bp.lhs = lhs; - bp.rhs = rhs; - - - unsigned load_difference = s_idx + 1; - if( aqg.exists_vmovements_of_diff(bp, load_difference) ) { - EdgeID e_bar = cycle_problem.new_edge(cur_node, s_idx*config.k+rhs); - cycle_problem.setEdgeWeight(e_bar, -aqg.get_gain_of_vmovements(bp, load_difference)); - } - - } endfor - - //create a backward edge if it can take s_idx+1 vertices - if( boundary.getBlockWeight(lhs) + s_idx < config.upper_bound_partition) { - EdgeID e_bar = cycle_problem.new_edge(cur_node, t); - cycle_problem.setEdgeWeight(e_bar, 0); - } - } endfor - } + graph_access cycle_problem; + graph_access G_bar; + boundary.getUnderlyingQuotientGraph(G_bar); - s = cycle_problem.new_node(); + aqg.prepare(config, G, G_bar, steps); - //now connect s to all vertices of the layer graph with weight zero - for( unsigned s_idx = 0; s_idx < steps; s_idx++) { - forall_nodes(G_bar, node) { - if( boundary.getBlockWeight(node) > config.upper_bound_partition ) { - EdgeID e = cycle_problem.new_edge(s, s_idx*config.k + node); - cycle_problem.setEdgeWeight(e, 0); - } - } endfor - } + //*********************************************************************** + //build the model + //*********************************************************************** + NodeID number_of_nodes = G_bar.number_of_nodes()*steps + 2; + EdgeID number_of_edges = G_bar.number_of_edges()*steps + 2*number_of_nodes; - t = cycle_problem.new_node(); - cycle_problem.finish_construction(); + cycle_problem.start_construction(number_of_nodes, number_of_edges); + NodeID s = number_of_nodes - 2; + NodeID t = number_of_nodes - 1 ; - //************************************************************************************* - //solve shortest path problem in model - //************************************************************************************* - //check wether t is reachable from s by performing a bfs - std::deque* bfsqueue = new std::deque; - std::vector touched(cycle_problem.number_of_nodes(), false); - bfsqueue->push_back(s); - touched[s] = true; + for( unsigned s_idx = 0; s_idx < steps; s_idx++) { + //create a new layer + forall_nodes(G_bar, lhs) { + NodeID cur_node = cycle_problem.new_node(); + forall_out_edges(G_bar, e, lhs) { + EdgeID rhs = G_bar.getEdgeTarget(e); - cycle_search cs; - std::vector path; - cs.find_shortest_path(cycle_problem, s, t, path); + //find the right edge in the augmented quotient graph + boundary_pair bp; + bp.k = config.k; + bp.lhs = lhs; + bp.rhs = rhs; - //perform the found movements - perform_augmented_move(config, G, boundary, path, s, t, aqg); - return true; + unsigned load_difference = s_idx + 1; + if( aqg.exists_vmovements_of_diff(bp, load_difference) ) { + EdgeID e_bar = cycle_problem.new_edge(cur_node, s_idx*config.k+rhs); + cycle_problem.setEdgeWeight(e_bar, -aqg.get_gain_of_vmovements(bp, load_difference)); + } + + } endfor + + //create a backward edge if it can take s_idx+1 vertices + if( boundary.getBlockWeight(lhs) + s_idx < config.upper_bound_partition) { + EdgeID e_bar = cycle_problem.new_edge(cur_node, t); + cycle_problem.setEdgeWeight(e_bar, 0); + } + } endfor } -bool advanced_models::compute_vertex_movements_ultra_model( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - augmented_Qgraph & aqg, - unsigned & steps, bool zero_weight_cycle) { - graph_access G_bar; - boundary.getUnderlyingQuotientGraph(G_bar); + s = cycle_problem.new_node(); - if(!zero_weight_cycle) { - aqg.prepare(config, G, G_bar, steps); - } + //now connect s to all vertices of the layer graph with weight zero + for( unsigned s_idx = 0; s_idx < steps; s_idx++) { + forall_nodes(G_bar, node) { + if( boundary.getBlockWeight(node) > config.upper_bound_partition ) { + EdgeID e = cycle_problem.new_edge(s, s_idx*config.k + node); + cycle_problem.setEdgeWeight(e, 0); + } + } endfor +} - bool found_some; - std::vector feasable_edge; - std::vector id_mapping; - NodeID s; // start vertex - do { - graph_access cycle_problem; - build_ultra_model( config, G, G_bar, boundary, aqg, feasable_edge, steps, cycle_problem, s, id_mapping); - - //******************************************************************** - //solve the problem - //******************************************************************** - cycle_search cs; std::vector cycle; - if( zero_weight_cycle ) { - found_some = cs.find_zero_weight_cycle(cycle_problem, s, cycle); - } else { - found_some = cs.find_negative_cycle(cycle_problem, s, cycle); - } - - if(found_some) { - //detect conflict -- a block should be at most one time in a cycle - bool conflict_detected = handle_ultra_model_conflicts(config, cycle_problem, - boundary, id_mapping, - feasable_edge, cycle, s, aqg); - if(!conflict_detected) { - perform_augmented_move(config, G, boundary, cycle, s, s, aqg); - return true; - } - } - } while( found_some ); - - return false; + t = cycle_problem.new_node(); + cycle_problem.finish_construction(); + + //************************************************************************************* + //solve shortest path problem in model + //************************************************************************************* + //check wether t is reachable from s by performing a bfs + std::deque* bfsqueue = new std::deque; + std::vector touched(cycle_problem.number_of_nodes(), false); + bfsqueue->push_back(s); + touched[s] = true; + + cycle_search cs; + std::vector path; + cs.find_shortest_path(cycle_problem, s, t, path); + + //perform the found movements + perform_augmented_move(config, G, boundary, path, s, t, aqg); + + return true; } +bool advanced_models::compute_vertex_movements_ultra_model( PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + augmented_Qgraph & aqg, + unsigned & steps, bool zero_weight_cycle) { + graph_access G_bar; + boundary.getUnderlyingQuotientGraph(G_bar); + + if(!zero_weight_cycle) { + aqg.prepare(config, G, G_bar, steps); + } + + bool found_some; + std::vector feasable_edge; + std::vector id_mapping; + NodeID s; // start vertex + do { + graph_access cycle_problem; + build_ultra_model( config, G, G_bar, boundary, aqg, feasable_edge, steps, cycle_problem, s, id_mapping); + + //******************************************************************** + //solve the problem + //******************************************************************** + cycle_search cs; std::vector cycle; + if( zero_weight_cycle ) { + found_some = cs.find_zero_weight_cycle(cycle_problem, s, cycle); + } else { + found_some = cs.find_negative_cycle(cycle_problem, s, cycle); + } + + if(found_some) { + //detect conflict -- a block should be at most one time in a cycle + bool conflict_detected = handle_ultra_model_conflicts(config, cycle_problem, + boundary, id_mapping, + feasable_edge, cycle, s, aqg); + if(!conflict_detected) { + perform_augmented_move(config, G, boundary, cycle, s, s, aqg); + return true; + } + } + } while( found_some ); + + return false; +} +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.h index ef00d29b..39485cf8 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.h @@ -16,76 +16,76 @@ #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h" #include "uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class advanced_models { - public: - advanced_models(); - virtual ~advanced_models(); - - bool compute_vertex_movements_rebalance( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - augmented_Qgraph & aqg, - unsigned & s); - - bool compute_vertex_movements_rebalance_ultra( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - augmented_Qgraph & aqg, - unsigned & s); - - bool compute_vertex_movements_ultra_model( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - augmented_Qgraph & aqg, - unsigned & s, bool zero_weight_cycle); - - void perform_augmented_move( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - std::vector & cycle, - NodeID & s, NodeID & t, - augmented_Qgraph & aqg); - - static unsigned long conflicts; - private: - inline - bool build_ultra_model( PartitionConfig & config, - graph_access & G, - graph_access & G_bar, - complete_boundary & boundary, - augmented_Qgraph & aqg, - std::vector & feasable_edge, - unsigned & steps, - graph_access & cycle_problem, NodeID & s, - std::vector & id_mapping); - - inline - bool build_rebalance_model( PartitionConfig & config, - graph_access & G, - graph_access & G_bar, - complete_boundary & boundary, - augmented_Qgraph & aqg, - std::vector & feasable_edge, - unsigned & steps, - graph_access & cycle_problem, NodeID & s, NodeID & t, - std::vector & id_mapping); - - - inline - bool handle_ultra_model_conflicts( PartitionConfig & config, - graph_access & cycle_problem, - complete_boundary & boundary, - std::vector & id_mapping, - std::vector & feasable_edge, - std::vector< NodeID > & cycle, - NodeID & s, augmented_Qgraph & aqg, bool remove_only_between_layers = false); - - inline - bool cycleorpath_has_conflicts( PartitionConfig & config, - complete_boundary & boundary, - std::vector< NodeID > & cycleorpath, - NodeID & s, augmented_Qgraph & aqg); +public: + advanced_models(); + virtual ~advanced_models(); + + bool compute_vertex_movements_rebalance( PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + augmented_Qgraph & aqg, + unsigned & s); + + bool compute_vertex_movements_rebalance_ultra( PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + augmented_Qgraph & aqg, + unsigned & s); + + bool compute_vertex_movements_ultra_model( PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + augmented_Qgraph & aqg, + unsigned & s, bool zero_weight_cycle); + + void perform_augmented_move( PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + std::vector & cycle, + NodeID & s, NodeID & t, + augmented_Qgraph & aqg); + + static unsigned long conflicts; +private: + inline + bool build_ultra_model( PartitionConfig & config, + graph_access & G, + graph_access & G_bar, + complete_boundary & boundary, + augmented_Qgraph & aqg, + std::vector & feasable_edge, + unsigned & steps, + graph_access & cycle_problem, NodeID & s, + std::vector & id_mapping); + + inline + bool build_rebalance_model( PartitionConfig & config, + graph_access & G, + graph_access & G_bar, + complete_boundary & boundary, + augmented_Qgraph & aqg, + std::vector & feasable_edge, + unsigned & steps, + graph_access & cycle_problem, NodeID & s, NodeID & t, + std::vector & id_mapping); + + + inline + bool handle_ultra_model_conflicts( PartitionConfig & config, + graph_access & cycle_problem, + complete_boundary & boundary, + std::vector & id_mapping, + std::vector & feasable_edge, + std::vector< NodeID > & cycle, + NodeID & s, augmented_Qgraph & aqg, bool remove_only_between_layers = false); + + inline + bool cycleorpath_has_conflicts( PartitionConfig & config, + complete_boundary & boundary, + std::vector< NodeID > & cycleorpath, + NodeID & s, augmented_Qgraph & aqg); @@ -607,5 +607,5 @@ bool advanced_models::build_ultra_model( PartitionConfig & config, return false; } - +} #endif /* end of include guard: ADVANCED_MODELS_PR6SXN3G */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.cpp index d03d3308..dd196d07 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "augmented_Qgraph.h" - +namespace kahip::modified { augmented_Qgraph::augmented_Qgraph() : m_max_vertex_weight_difference(0) { } @@ -14,4 +14,4 @@ augmented_Qgraph::augmented_Qgraph() : m_max_vertex_weight_difference(0) { augmented_Qgraph::~augmented_Qgraph() { } - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.h index ddf0a958..2250cdcb 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.h @@ -15,7 +15,7 @@ #include "definitions.h" #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h" #include "uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.h" - +namespace kahip::modified { struct pairwise_local_search { // a single two way local search std::vector gains; std::vector vertex_movements; @@ -116,11 +116,11 @@ inline bool augmented_Qgraph::exists_vmovements_of_diff( boundary_pair & bp, unsigned & diff) { unsigned internal_idx = diff - 1; if( m_aqg[bp].local_searches.size() > 0 ) { - if(m_aqg[bp].search_to_use.size() > internal_idx) { - if(m_aqg[bp].search_to_use[internal_idx] != -1) { - return true; - } - } + if(m_aqg[bp].search_to_use.size() > internal_idx) { + if(m_aqg[bp].search_to_use[internal_idx] != -1) { + return true; + } + } } return false; @@ -234,5 +234,5 @@ bool augmented_Qgraph::check_conflict( const PartitionConfig & config, return (node == m_aqg[bp].local_searches[local_searches_to_use].vertex_movements[0]); } - +} #endif /* end of include guard: AUGMENTED_QUOTIENT_GRAPH_E5ZEJUBV */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.cpp index 588c3cae..fb8b6d6e 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.cpp @@ -16,7 +16,7 @@ #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.h" #include "uncoarsening/refinement/kway_graph_refinement/kway_stop_rule.h" #include "uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h" - +namespace kahip::modified { augmented_Qgraph_fabric::augmented_Qgraph_fabric() { } @@ -25,535 +25,535 @@ augmented_Qgraph_fabric::~augmented_Qgraph_fabric() { } void augmented_Qgraph_fabric::cleanup_eligible() { - for( unsigned i = 0; i < m_tomake_eligible.size(); i++) { - m_eligible[m_tomake_eligible[i]] = true; - } - m_tomake_eligible.clear(); - } + for( unsigned i = 0; i < m_tomake_eligible.size(); i++) { + m_eligible[m_tomake_eligible[i]] = true; + } + m_tomake_eligible.clear(); +} -bool augmented_Qgraph_fabric::build_augmented_quotient_graph( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - augmented_Qgraph & aqg, +bool augmented_Qgraph_fabric::build_augmented_quotient_graph( PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + augmented_Qgraph & aqg, unsigned & s, bool rebalance, bool plus) { - graph_access G_bar; - boundary.getUnderlyingQuotientGraph(G_bar); - if(m_eligible.size() != G.number_of_nodes()) { - m_eligible.resize(G.number_of_nodes()); - forall_nodes(G, node) { - m_eligible[node] = true; - } endfor - } else { - cleanup_eligible(); - } + graph_access G_bar; + boundary.getUnderlyingQuotientGraph(G_bar); + if(m_eligible.size() != G.number_of_nodes()) { + m_eligible.resize(G.number_of_nodes()); + forall_nodes(G, node) { + m_eligible[node] = true; + } endfor +} else { + cleanup_eligible(); +} - if(!rebalance) { - std::vector vec_bpd; - forall_nodes(G_bar, lhs) { - forall_out_edges(G_bar, e, lhs) { - EdgeID rhs = G_bar.getEdgeTarget(e); - - block_pair_difference bpd; - bpd.lhs = lhs; - bpd.rhs = rhs; - vec_bpd.push_back(bpd); - } endfor - } endfor - - - for( unsigned j = 0; j < config.kaba_packing_iterations; j++) { - random_functions::permutate_vector_good_small(vec_bpd); - bool variant_to_use = plus; - for( unsigned i = 0; i < vec_bpd.size(); i++) { - boundary_pair bp; - bp.k = config.k; - bp.lhs = vec_bpd[i].lhs; - bp.rhs = vec_bpd[i].rhs; - - if( plus && config.kaba_flip_packings) { - //best of both worlds - variant_to_use = random_functions::nextBool(); - } - - local_search( config, variant_to_use, G, boundary, aqg, bp, s); - } - } - } else { - std::vector vec_bpd; - bool graph_model_will_be_feasable = false; - forall_nodes(G_bar, lhs) { - forall_out_edges(G_bar, e, lhs) { - EdgeID rhs = G_bar.getEdgeTarget(e); - - block_pair_difference bpd; - bpd.lhs = lhs; - bpd.rhs = rhs; - vec_bpd.push_back(bpd); - - //make the underlying model feasable - if( boundary.getBlockWeight(lhs) > config.upper_bound_partition - && boundary.getBlockWeight(rhs) < config.upper_bound_partition - && !graph_model_will_be_feasable) { - boundary_pair bp; - bp.k = config.k; - bp.lhs = lhs; - bp.rhs = rhs; - - bool success = local_search( config, false, G, boundary, aqg, bp, s); - - if( success ) { - graph_model_will_be_feasable = true; - } // the else case can happen if the quotient graph data structure is not up to date - } - } endfor - } endfor - - if( !graph_model_will_be_feasable) { - // fall back solution - std::deque* bfsqueue = new std::deque; - std::vector< int > parent(G_bar.number_of_nodes(), -1); - - std::vector start_vertices; - std::vector candidates; - forall_nodes(G_bar, lhs) { - if( boundary.getBlockWeight(lhs) > config.upper_bound_partition ) { - start_vertices.push_back(lhs); - } else if ( boundary.getBlockWeight(lhs) < config.upper_bound_partition) { - candidates.push_back(lhs); - } - } endfor - - random_functions::permutate_vector_good_small(start_vertices); - for( unsigned i = 0; i < start_vertices.size(); i++) { - bfsqueue->push_back(start_vertices[i]); - parent[start_vertices[i]] = start_vertices[i]; - } - - while(!bfsqueue->empty()) { - NodeID lhs = bfsqueue->front(); - bfsqueue->pop_front(); - - forall_out_edges(G_bar, e, lhs) { - NodeID rhs = G_bar.getEdgeTarget(e); - - if(parent[rhs] == -1 && boundary.getDirectedBoundary(lhs, lhs, rhs).size() > 0) { - parent[rhs] = lhs; - bfsqueue->push_back(rhs); - } - } endfor - } - - delete bfsqueue; - - int cur_block; - int start_block; - bool candiate_set_was_empty = false; - std::vector tmp_candidates; - tmp_candidates = candidates; // for the connected component case - do { - if(candidates.size() == 0) { - candiate_set_was_empty = true; - break; - } - unsigned int r_idx = random_functions::nextInt(0, candidates.size()-1); - cur_block = candidates[r_idx]; - std::swap(candidates[r_idx], candidates[candidates.size()-1]); - candidates.pop_back(); - - } while( parent[cur_block] == -1 ); // in this case the vertex is not reachable - - //special case for more connected components, - //move a random node from an overloaded block to cur_block (which is a connected component) - if(candiate_set_was_empty) { - unsigned int r_idx = random_functions::nextInt(0, tmp_candidates.size()-1); - PartitionID cur_block = tmp_candidates[r_idx]; - - do { - unsigned int node = random_functions::nextInt(0, G.number_of_nodes()-1); - PartitionID nodes_block = G.getPartitionIndex(node); - if( nodes_block != cur_block - && boundary.getBlockWeight(nodes_block) > config.upper_bound_partition) { - PartitionID from = G.getPartitionIndex(node); - PartitionID to = cur_block; - perform_simple_move( config, G, boundary, node,from, to); - return true; - } - } while( true ); - } // else - - start_block = cur_block; - std::unordered_map< PartitionID, bool > allready_performed_local_search; - - while( boundary.getBlockWeight( cur_block ) <= config.upper_bound_partition ) { - boundary_pair bp; - bp.k = config.k; - bp.lhs = parent[cur_block]; - bp.rhs = cur_block; - - cur_block = parent[cur_block]; - bool success = local_search( config, false, G, boundary, aqg, bp, 1); - - if(!success) { - candidates.push_back( start_block ); - rebalance_fall_back(config, G, G_bar, boundary, candidates, parent, aqg); // this happens on dense graphs if no node was eligible - return true; - } - allready_performed_local_search[config.k*bp.lhs+bp.rhs] = true; - } - for( unsigned j = 0; j < config.kaba_packing_iterations; j++) { - random_functions::permutate_vector_good_small(vec_bpd); - - for( unsigned i = 0; i < vec_bpd.size(); i++) { - boundary_pair bp; - bp.k = config.k; - bp.lhs = vec_bpd[i].lhs; - bp.rhs = vec_bpd[i].rhs; - - local_search( config, false, G, boundary, aqg, bp, s); - } - } - } else { - for( unsigned j = 0; j < config.kaba_packing_iterations; j++) { - random_functions::permutate_vector_good_small(vec_bpd); - - for( unsigned i = 0; i < vec_bpd.size(); i++) { - boundary_pair bp; - bp.k = config.k; - bp.lhs = vec_bpd[i].lhs; - bp.rhs = vec_bpd[i].rhs; - - local_search( config, false, G, boundary, aqg, bp, s); - } - } - } + if(!rebalance) { + std::vector vec_bpd; + forall_nodes(G_bar, lhs) { + forall_out_edges(G_bar, e, lhs) { + EdgeID rhs = G_bar.getEdgeTarget(e); + + block_pair_difference bpd; + bpd.lhs = lhs; + bpd.rhs = rhs; + vec_bpd.push_back(bpd); + } endfor +} endfor + + +for( unsigned j = 0; j < config.kaba_packing_iterations; j++) { + random_functions::permutate_vector_good_small(vec_bpd); + bool variant_to_use = plus; + for( unsigned i = 0; i < vec_bpd.size(); i++) { + boundary_pair bp; + bp.k = config.k; + bp.lhs = vec_bpd[i].lhs; + bp.rhs = vec_bpd[i].rhs; + + if( plus && config.kaba_flip_packings) { + //best of both worlds + variant_to_use = random_functions::nextBool(); + } + + local_search( config, variant_to_use, G, boundary, aqg, bp, s); + } +} + } else { + std::vector vec_bpd; + bool graph_model_will_be_feasable = false; + forall_nodes(G_bar, lhs) { + forall_out_edges(G_bar, e, lhs) { + EdgeID rhs = G_bar.getEdgeTarget(e); + + block_pair_difference bpd; + bpd.lhs = lhs; + bpd.rhs = rhs; + vec_bpd.push_back(bpd); + + //make the underlying model feasable + if( boundary.getBlockWeight(lhs) > config.upper_bound_partition + && boundary.getBlockWeight(rhs) < config.upper_bound_partition + && !graph_model_will_be_feasable) { + boundary_pair bp; + bp.k = config.k; + bp.lhs = lhs; + bp.rhs = rhs; + + bool success = local_search( config, false, G, boundary, aqg, bp, s); + + if( success ) { + graph_model_will_be_feasable = true; + } // the else case can happen if the quotient graph data structure is not up to date + } + } endfor +} endfor + +if( !graph_model_will_be_feasable) { + // fall back solution + std::deque* bfsqueue = new std::deque; + std::vector< int > parent(G_bar.number_of_nodes(), -1); + + std::vector start_vertices; + std::vector candidates; + forall_nodes(G_bar, lhs) { + if( boundary.getBlockWeight(lhs) > config.upper_bound_partition ) { + start_vertices.push_back(lhs); + } else if ( boundary.getBlockWeight(lhs) < config.upper_bound_partition) { + candidates.push_back(lhs); + } + } endfor + + random_functions::permutate_vector_good_small(start_vertices); + for( unsigned i = 0; i < start_vertices.size(); i++) { + bfsqueue->push_back(start_vertices[i]); + parent[start_vertices[i]] = start_vertices[i]; + } + + while(!bfsqueue->empty()) { + NodeID lhs = bfsqueue->front(); + bfsqueue->pop_front(); + + forall_out_edges(G_bar, e, lhs) { + NodeID rhs = G_bar.getEdgeTarget(e); + + if(parent[rhs] == -1 && boundary.getDirectedBoundary(lhs, lhs, rhs).size() > 0) { + parent[rhs] = lhs; + bfsqueue->push_back(rhs); + } + } endfor +} - } + delete bfsqueue; + + int cur_block; + int start_block; + bool candiate_set_was_empty = false; + std::vector tmp_candidates; + tmp_candidates = candidates; // for the connected component case + do { + if(candidates.size() == 0) { + candiate_set_was_empty = true; + break; + } + unsigned int r_idx = random_functions::nextInt(0, candidates.size()-1); + cur_block = candidates[r_idx]; + std::swap(candidates[r_idx], candidates[candidates.size()-1]); + candidates.pop_back(); + + } while( parent[cur_block] == -1 ); // in this case the vertex is not reachable + + //special case for more connected components, + //move a random node from an overloaded block to cur_block (which is a connected component) + if(candiate_set_was_empty) { + unsigned int r_idx = random_functions::nextInt(0, tmp_candidates.size()-1); + PartitionID cur_block = tmp_candidates[r_idx]; + + do { + unsigned int node = random_functions::nextInt(0, G.number_of_nodes()-1); + PartitionID nodes_block = G.getPartitionIndex(node); + if( nodes_block != cur_block + && boundary.getBlockWeight(nodes_block) > config.upper_bound_partition) { + PartitionID from = G.getPartitionIndex(node); + PartitionID to = cur_block; + perform_simple_move( config, G, boundary, node,from, to); + return true; + } + } while( true ); + } // else + + start_block = cur_block; + std::unordered_map< PartitionID, bool > allready_performed_local_search; + + while( boundary.getBlockWeight( cur_block ) <= config.upper_bound_partition ) { + boundary_pair bp; + bp.k = config.k; + bp.lhs = parent[cur_block]; + bp.rhs = cur_block; + + cur_block = parent[cur_block]; + bool success = local_search( config, false, G, boundary, aqg, bp, 1); + + if(!success) { + candidates.push_back( start_block ); + rebalance_fall_back(config, G, G_bar, boundary, candidates, parent, aqg); // this happens on dense graphs if no node was eligible + return true; + } + allready_performed_local_search[config.k*bp.lhs+bp.rhs] = true; + } + for( unsigned j = 0; j < config.kaba_packing_iterations; j++) { + random_functions::permutate_vector_good_small(vec_bpd); + + for( unsigned i = 0; i < vec_bpd.size(); i++) { + boundary_pair bp; + bp.k = config.k; + bp.lhs = vec_bpd[i].lhs; + bp.rhs = vec_bpd[i].rhs; + + local_search( config, false, G, boundary, aqg, bp, s); + } + } +} else { + for( unsigned j = 0; j < config.kaba_packing_iterations; j++) { + random_functions::permutate_vector_good_small(vec_bpd); + + for( unsigned i = 0; i < vec_bpd.size(); i++) { + boundary_pair bp; + bp.k = config.k; + bp.lhs = vec_bpd[i].lhs; + bp.rhs = vec_bpd[i].rhs; + + local_search( config, false, G, boundary, aqg, bp, s); + } + } +} + + } - return false; + return false; } -bool augmented_Qgraph_fabric::construct_local_searches_on_qgraph_edge( PartitionConfig & config, graph_access & G, - complete_boundary & boundary, augmented_Qgraph & aqg, - boundary_pair & pair, +bool augmented_Qgraph_fabric::construct_local_searches_on_qgraph_edge( PartitionConfig & config, graph_access & G, + complete_boundary & boundary, augmented_Qgraph & aqg, + boundary_pair & pair, unsigned s, bool plus) { - PartitionID lhs = pair.lhs; - PartitionID rhs = pair.rhs; - - //initialize todo list - PartialBoundary & lhs_b = boundary.getDirectedBoundary(lhs, lhs, rhs); - std::vector lhs_boundary; // todo list - forall_boundary_nodes(lhs_b, node) { - if(m_eligible[node]) { - lhs_boundary.push_back(node); - } - } endfor - - if(lhs_boundary.size() == 0) { - //nothing todo - return false; - } - - commons = kway_graph_refinement_commons::getInstance(config); - for( unsigned i = 0; i < 1; i++) { - - if(lhs_boundary.size() == 0) return false; - - pairwise_local_search pls; - - NodeID start_node = lhs_boundary[0]; - find_eligible_start_node( G, lhs, rhs, lhs_boundary, m_eligible, start_node); - - if(!m_eligible[start_node]) return false; // in this case the lhs_boundary was empty and we cant move a node - - if(plus) { - more_locallized_search(config, G, boundary, lhs, rhs, start_node, s, pls); - } else { - directed_more_locallized_search(config, G, boundary, lhs, rhs, start_node, s, pls); - } - - aqg.commit_pairwise_local_search(pair, pls); - - if( plus ) { - // keep things simple - boundary_pair opp_pair = pair; - std::swap(opp_pair.lhs, opp_pair.rhs); - aqg.commit_pairwise_local_search(opp_pair, pls); - } - } - return true; + PartitionID lhs = pair.lhs; + PartitionID rhs = pair.rhs; + + //initialize todo list + PartialBoundary & lhs_b = boundary.getDirectedBoundary(lhs, lhs, rhs); + std::vector lhs_boundary; // todo list + forall_boundary_nodes(lhs_b, node) { + if(m_eligible[node]) { + lhs_boundary.push_back(node); + } + } endfor + + if(lhs_boundary.size() == 0) { + //nothing todo + return false; + } + + commons = kway_graph_refinement_commons::getInstance(config); + for( unsigned i = 0; i < 1; i++) { + + if(lhs_boundary.size() == 0) return false; + + pairwise_local_search pls; + + NodeID start_node = lhs_boundary[0]; + find_eligible_start_node( G, lhs, rhs, lhs_boundary, m_eligible, start_node); + + if(!m_eligible[start_node]) return false; // in this case the lhs_boundary was empty and we cant move a node + + if(plus) { + more_locallized_search(config, G, boundary, lhs, rhs, start_node, s, pls); + } else { + directed_more_locallized_search(config, G, boundary, lhs, rhs, start_node, s, pls); + } + + aqg.commit_pairwise_local_search(pair, pls); + + if( plus ) { + // keep things simple + boundary_pair opp_pair = pair; + std::swap(opp_pair.lhs, opp_pair.rhs); + aqg.commit_pairwise_local_search(opp_pair, pls); + } + } + return true; } //this method performes a directed localized local search and UNDOs them //these searches are for the augmented qgraph structure for balanced graph partitioning -void augmented_Qgraph_fabric::directed_more_locallized_search(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, +void augmented_Qgraph_fabric::directed_more_locallized_search(PartitionConfig & config, graph_access & G, + complete_boundary & boundary, PartitionID & lhs, PartitionID & rhs, NodeID start_node, unsigned & number_of_swaps, pairwise_local_search & pls) { - commons = kway_graph_refinement_commons::getInstance(config); - EdgeWeight max_degree = G.getMaxDegree(); - refinement_pq* queue = new bucket_pq(max_degree); + commons = kway_graph_refinement_commons::getInstance(config); + EdgeWeight max_degree = G.getMaxDegree(); + refinement_pq* queue = new bucket_pq(max_degree); - EdgeWeight int_degree = 0; - EdgeWeight ext_degree = 0; - m_twfm.int_ext_degree(G, start_node, lhs, rhs, int_degree, ext_degree); + EdgeWeight int_degree = 0; + EdgeWeight ext_degree = 0; + m_twfm.int_ext_degree(G, start_node, lhs, rhs, int_degree, ext_degree); - Gain gain = ext_degree - int_degree; - queue->insert(start_node, gain); - - if(queue->empty()) {delete queue; return;} + Gain gain = ext_degree - int_degree; + queue->insert(start_node, gain); - ////roll forwards - int movements = 0; - int overall_gain = 0; + if(queue->empty()) {delete queue; return;} - kway_stop_rule* stopping_rule = new kway_simple_stop_rule(config); + ////roll forwards + int movements = 0; + int overall_gain = 0; - int min_cut_index = 0; - int step_limit = 200; - EdgeWeight input_cut = boundary.getEdgeCut(lhs, rhs); - EdgeWeight min_cut = input_cut; - PartitionID from = lhs; - PartitionID to = rhs; + kway_stop_rule* stopping_rule = new kway_simple_stop_rule(config); - for(movements = 0; movements < (int)number_of_swaps; movements++) { - if( queue->empty() ) { - break; - } - if( stopping_rule->search_should_stop(min_cut_index, movements, step_limit) ) break; + int min_cut_index = 0; + int step_limit = 200; + EdgeWeight input_cut = boundary.getEdgeCut(lhs, rhs); + EdgeWeight min_cut = input_cut; + PartitionID from = lhs; + PartitionID to = rhs; + for(movements = 0; movements < (int)number_of_swaps; movements++) { + if( queue->empty() ) { + break; + } + if( stopping_rule->search_should_stop(min_cut_index, movements, step_limit) ) break; - Gain gain = queue->maxValue(); - NodeID node = queue->deleteMax(); - move_node(config, G, node, queue, boundary, from, to); + Gain gain = queue->maxValue(); + NodeID node = queue->deleteMax(); - overall_gain += gain; - input_cut -= gain; - - stopping_rule->push_statistics(gain); + move_node(config, G, node, queue, boundary, from, to); - if(input_cut < min_cut) { - min_cut_index = movements; - min_cut = input_cut; - } + overall_gain += gain; + input_cut -= gain; - pls.vertex_movements.push_back(node); - pls.block_movements.push_back(to); - pls.gains.push_back(overall_gain); - m_tomake_eligible.push_back(node); - } + stopping_rule->push_statistics(gain); - ////roll backwards - int idx = pls.vertex_movements.size()-1; - for(; idx >= 0; idx--) { - NodeID node = pls.vertex_movements[idx]; - move_node(config, G, node, queue, boundary, to, from); - - //block the neighboring nodes to avoid conflicts - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(m_eligible[target]) m_tomake_eligible.push_back(target); - m_eligible[target] = false; - } endfor - } - delete queue; - delete stopping_rule; + if(input_cut < min_cut) { + min_cut_index = movements; + min_cut = input_cut; + } + + pls.vertex_movements.push_back(node); + pls.block_movements.push_back(to); + pls.gains.push_back(overall_gain); + m_tomake_eligible.push_back(node); + } + + ////roll backwards + int idx = pls.vertex_movements.size()-1; + for(; idx >= 0; idx--) { + NodeID node = pls.vertex_movements[idx]; + move_node(config, G, node, queue, boundary, to, from); + + //block the neighboring nodes to avoid conflicts + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(m_eligible[target]) m_tomake_eligible.push_back(target); + m_eligible[target] = false; + } endfor +} + delete queue; + delete stopping_rule; } //this method performes a directed localized local search and UNDOs them //these searches are for the augmented qgraph structure for balanced graph partitioning -void augmented_Qgraph_fabric::more_locallized_search(PartitionConfig & config, graph_access & G, +void augmented_Qgraph_fabric::more_locallized_search(PartitionConfig & config, graph_access & G, complete_boundary & boundary, PartitionID & lhs, PartitionID & rhs, NodeID start_node, unsigned & number_of_swaps, pairwise_local_search & pls) { - commons = kway_graph_refinement_commons::getInstance(config); - refinement_pq* queue_lhs = NULL; - refinement_pq* queue_rhs = NULL; - EdgeWeight max_degree = G.getMaxDegree(); - queue_lhs = new bucket_pq(max_degree); - queue_rhs = new bucket_pq(max_degree); - - EdgeWeight int_degree = 0; - EdgeWeight ext_degree = 0; - m_twfm.int_ext_degree(G, start_node, lhs, rhs, int_degree, ext_degree); - - Gain gain = ext_degree - int_degree; - queue_lhs->insert(start_node, gain); - - //===================================== - // find a start node for the rhs queue - //===================================== - NodeID start_node_rhs = start_node; // some dummy initialization - Gain max_gain = std::numeric_limits::min(); - forall_out_edges(G, e, start_node) { - NodeID target = G.getEdgeTarget(e); - if( G.getPartitionIndex(target) == rhs && m_eligible[target]) { - m_twfm.int_ext_degree(G, target, rhs, lhs, int_degree, ext_degree); - if( ext_degree - int_degree > max_gain ) { - max_gain = ext_degree - int_degree; - start_node_rhs = target; - } - } - } endfor - //===================================== - - if( m_eligible[start_node_rhs] && start_node_rhs != start_node) { - queue_rhs->insert(start_node_rhs, max_gain); + commons = kway_graph_refinement_commons::getInstance(config); + refinement_pq* queue_lhs = NULL; + refinement_pq* queue_rhs = NULL; + EdgeWeight max_degree = G.getMaxDegree(); + queue_lhs = new bucket_pq(max_degree); + queue_rhs = new bucket_pq(max_degree); + + EdgeWeight int_degree = 0; + EdgeWeight ext_degree = 0; + m_twfm.int_ext_degree(G, start_node, lhs, rhs, int_degree, ext_degree); + + Gain gain = ext_degree - int_degree; + queue_lhs->insert(start_node, gain); + + //===================================== + // find a start node for the rhs queue + //===================================== + NodeID start_node_rhs = start_node; // some dummy initialization + Gain max_gain = std::numeric_limits::min(); + forall_out_edges(G, e, start_node) { + NodeID target = G.getEdgeTarget(e); + if( G.getPartitionIndex(target) == rhs && m_eligible[target]) { + m_twfm.int_ext_degree(G, target, rhs, lhs, int_degree, ext_degree); + if( ext_degree - int_degree > max_gain ) { + max_gain = ext_degree - int_degree; + start_node_rhs = target; + } + } + } endfor + //===================================== + + if( m_eligible[start_node_rhs] && start_node_rhs != start_node) { + queue_rhs->insert(start_node_rhs, max_gain); + } + + if(queue_lhs->empty() || queue_rhs->empty()) {delete queue_lhs; delete queue_rhs; return;} + // queues initalized + + ////roll forwards + int movements = 0; + int overall_gain = 0; + + kway_stop_rule* stopping_rule = new kway_simple_stop_rule(config); + + int min_cut_index = 0; + int step_limit = 200; + EdgeWeight input_cut = boundary.getEdgeCut(lhs, rhs); + EdgeWeight min_cut = input_cut; + PartitionID from = lhs; + PartitionID to = rhs; + + refinement_pq * queue = NULL; + refinement_pq * to_queue = NULL; + + int diff = 0; + + for(movements = 0; movements < (int)number_of_swaps; movements++) { + if( queue_lhs->empty() || queue_rhs->empty()) { + break; + } + if( stopping_rule->search_should_stop(min_cut_index, movements, step_limit) ) break; + + + Gain gain_lhs = queue_lhs->maxValue(); + Gain gain_rhs = queue_rhs->maxValue(); + + bool coin = false; + switch(config.kaba_lsearch_p) { + case COIN_DIFFTIE: + coin = random_functions::nextBool(); + if(coin) { + if( gain_rhs > gain_lhs ) { + queue = queue_rhs; + } else if ( gain_rhs < gain_lhs ) { + queue = queue_lhs; + } else { + queue = queue_lhs; } - - if(queue_lhs->empty() || queue_rhs->empty()) {delete queue_lhs; delete queue_rhs; return;} - // queues initalized - - ////roll forwards - int movements = 0; - int overall_gain = 0; - - kway_stop_rule* stopping_rule = new kway_simple_stop_rule(config); - - int min_cut_index = 0; - int step_limit = 200; - EdgeWeight input_cut = boundary.getEdgeCut(lhs, rhs); - EdgeWeight min_cut = input_cut; - PartitionID from = lhs; - PartitionID to = rhs; - - refinement_pq * queue = NULL; - refinement_pq * to_queue = NULL; - - int diff = 0; - - for(movements = 0; movements < (int)number_of_swaps; movements++) { - if( queue_lhs->empty() || queue_rhs->empty()) { - break; - } - if( stopping_rule->search_should_stop(min_cut_index, movements, step_limit) ) break; - - - Gain gain_lhs = queue_lhs->maxValue(); - Gain gain_rhs = queue_rhs->maxValue(); - - bool coin = false; - switch(config.kaba_lsearch_p) { - case COIN_DIFFTIE: - coin = random_functions::nextBool(); - if(coin) { - if( gain_rhs > gain_lhs ) { - queue = queue_rhs; - } else if ( gain_rhs < gain_lhs ) { - queue = queue_lhs; - } else { - queue = queue_lhs; - } - } else { - queue = queue_lhs; - } - break; - - case COIN_RNDTIE: - coin = random_functions::nextBool(); - if(coin) { - if( gain_rhs > gain_lhs ) { - queue = queue_rhs; - } else if ( gain_rhs < gain_lhs ) { - queue = queue_lhs; - } else { - coin = random_functions::nextBool(); - if(coin) { - queue = queue_rhs; - } else { - queue = queue_lhs; - } - } - } else { - queue = queue_lhs; - } - break; - case NOCOIN_DIFFTIE: - if( gain_rhs > gain_lhs ) { - queue = queue_rhs; - } else if ( gain_rhs < gain_lhs ) { - queue = queue_lhs; - } else { - queue = queue_lhs; - } - break; - - case NOCOIN_RNDTIE: - if( gain_rhs > gain_lhs ) { - queue = queue_rhs; - } else if ( gain_rhs < gain_lhs ) { - queue = queue_lhs; - } else { - coin = random_functions::nextBool(); - if(coin) { - queue = queue_rhs; - } else { - queue = queue_lhs; - } - } - break; - } - - NodeID node = queue->deleteMax(); - if( queue == queue_rhs ) { - from = rhs; - to = lhs; - gain = gain_rhs; - to_queue = queue_lhs; - diff += G.getNodeWeight(node); - } else { - from = lhs; - to = rhs; - gain = gain_lhs; - to_queue = queue_rhs; - diff -= G.getNodeWeight(node); - } - - - move_node(config, G, node, queue, to_queue, boundary, from, to); - - overall_gain += gain; - input_cut -= gain; - - stopping_rule->push_statistics(gain); - - if(input_cut < min_cut && diff == 0) { - min_cut = input_cut; - - pls.vertex_movements.clear(); - pls.block_movements.clear(); - pls.gains.clear(); - } else { - pls.vertex_movements.push_back(node); - pls.block_movements.push_back(to); - pls.gains.push_back(overall_gain); - } - m_tomake_eligible.push_back(node); + } else { + queue = queue_lhs; + } + break; + + case COIN_RNDTIE: + coin = random_functions::nextBool(); + if(coin) { + if( gain_rhs > gain_lhs ) { + queue = queue_rhs; + } else if ( gain_rhs < gain_lhs ) { + queue = queue_lhs; + } else { + coin = random_functions::nextBool(); + if(coin) { + queue = queue_rhs; + } else { + queue = queue_lhs; + } } - - ////roll backwards - int idx = pls.vertex_movements.size()-1; - //int idx = movements-1; - for(; idx >= 0; idx--) { - NodeID node = pls.vertex_movements[idx]; - PartitionID from = G.getPartitionIndex(node); - PartitionID to = from == lhs ? rhs : lhs; - perform_simple_move( config, G, boundary, node, from, to); - - //block the neighboring nodes to avoid conflicts - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(m_eligible[target]) m_tomake_eligible.push_back(target); - m_eligible[target] = false; - } endfor + } else { + queue = queue_lhs; + } + break; + case NOCOIN_DIFFTIE: + if( gain_rhs > gain_lhs ) { + queue = queue_rhs; + } else if ( gain_rhs < gain_lhs ) { + queue = queue_lhs; + } else { + queue = queue_lhs; } + break; - delete queue_lhs; - delete queue_rhs; - delete stopping_rule; + case NOCOIN_RNDTIE: + if( gain_rhs > gain_lhs ) { + queue = queue_rhs; + } else if ( gain_rhs < gain_lhs ) { + queue = queue_lhs; + } else { + coin = random_functions::nextBool(); + if(coin) { + queue = queue_rhs; + } else { + queue = queue_lhs; + } + } + break; + } + + NodeID node = queue->deleteMax(); + if( queue == queue_rhs ) { + from = rhs; + to = lhs; + gain = gain_rhs; + to_queue = queue_lhs; + diff += G.getNodeWeight(node); + } else { + from = lhs; + to = rhs; + gain = gain_lhs; + to_queue = queue_rhs; + diff -= G.getNodeWeight(node); + } + + + move_node(config, G, node, queue, to_queue, boundary, from, to); + + overall_gain += gain; + input_cut -= gain; + + stopping_rule->push_statistics(gain); + + if(input_cut < min_cut && diff == 0) { + min_cut = input_cut; + + pls.vertex_movements.clear(); + pls.block_movements.clear(); + pls.gains.clear(); + } else { + pls.vertex_movements.push_back(node); + pls.block_movements.push_back(to); + pls.gains.push_back(overall_gain); + } + m_tomake_eligible.push_back(node); + } + + ////roll backwards + int idx = pls.vertex_movements.size()-1; + //int idx = movements-1; + for(; idx >= 0; idx--) { + NodeID node = pls.vertex_movements[idx]; + PartitionID from = G.getPartitionIndex(node); + PartitionID to = from == lhs ? rhs : lhs; + perform_simple_move( config, G, boundary, node, from, to); + + //block the neighboring nodes to avoid conflicts + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(m_eligible[target]) m_tomake_eligible.push_back(target); + m_eligible[target] = false; + } endfor } + delete queue_lhs; + delete queue_rhs; + delete stopping_rule; +} +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.h index 1ba44bf2..72b27f3c 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.h @@ -16,107 +16,107 @@ #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h" #include "uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class augmented_Qgraph_fabric { - public: - augmented_Qgraph_fabric( ); - virtual ~augmented_Qgraph_fabric(); - - //return false if the network will be feasable for the desired model - //returns true iff rebalance = true and the fall back solution has been applied - bool build_augmented_quotient_graph( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - augmented_Qgraph & aqg, - unsigned & s, bool rebalance, bool plus = false); - - void cleanup_eligible(); - - private: - bool construct_local_searches_on_qgraph_edge( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - augmented_Qgraph & aqg, - boundary_pair & pair, - unsigned s, - bool plus); - - bool local_search(PartitionConfig & config, - bool plus, - graph_access & G, +public: + augmented_Qgraph_fabric( ); + virtual ~augmented_Qgraph_fabric(); + + //return false if the network will be feasable for the desired model + //returns true iff rebalance = true and the fall back solution has been applied + bool build_augmented_quotient_graph( PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + augmented_Qgraph & aqg, + unsigned & s, bool rebalance, bool plus = false); + + void cleanup_eligible(); + +private: + bool construct_local_searches_on_qgraph_edge( PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + augmented_Qgraph & aqg, + boundary_pair & pair, + unsigned s, + bool plus); + + bool local_search(PartitionConfig & config, + bool plus, + graph_access & G, + complete_boundary & boundary, + augmented_Qgraph & aqg, + boundary_pair & bp, + unsigned s); + + + void directed_more_locallized_search(PartitionConfig & config, graph_access & G, + complete_boundary & boundary, + PartitionID & lhs, PartitionID & rhs, + NodeID start_node, unsigned & number_of_swaps, pairwise_local_search & pls); + +public: + void more_locallized_search(PartitionConfig & config, graph_access & G, + complete_boundary & boundary, + PartitionID & lhs, PartitionID & rhs, + NodeID start_node, unsigned & number_of_swaps, pairwise_local_search & pls); +private: + void directed_more_locallized_search_all_bnd(PartitionConfig & config, graph_access & G, + complete_boundary & boundary, + PartitionID & lhs, PartitionID & rhs, + unsigned & number_of_swaps, pairwise_local_search & pls); + + void move_node(PartitionConfig & config, + graph_access & G, + NodeID & node, + refinement_pq * queue, + complete_boundary & boundary, + PartitionID & from, + PartitionID & to); + + void move_node(PartitionConfig & config, + graph_access & G, + NodeID & node, + refinement_pq * queue, + refinement_pq * to_queue, + complete_boundary & boundary, + PartitionID & from, + PartitionID & to); + + Gain find_eligible_start_node( graph_access & G, + PartitionID & lhs, + PartitionID & rhs, + std::vector & lhs_boundary, + std::vector & eligible, + NodeID & start_node, bool rebalance = false); + + void rebalance_fall_back(PartitionConfig & config, + graph_access & G, + graph_access & G_bar, + complete_boundary & boundary, + std::vector< NodeID > & candidates, + std::vector< int > & parent, + augmented_Qgraph & aqg); + + void perform_simple_move( PartitionConfig & config, + graph_access & G, complete_boundary & boundary, - augmented_Qgraph & aqg, - boundary_pair & bp, - unsigned s); - - - void directed_more_locallized_search(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, - PartitionID & lhs, PartitionID & rhs, - NodeID start_node, unsigned & number_of_swaps, pairwise_local_search & pls); - - public: - void more_locallized_search(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, - PartitionID & lhs, PartitionID & rhs, - NodeID start_node, unsigned & number_of_swaps, pairwise_local_search & pls); - private: - void directed_more_locallized_search_all_bnd(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, - PartitionID & lhs, PartitionID & rhs, - unsigned & number_of_swaps, pairwise_local_search & pls); - - void move_node(PartitionConfig & config, - graph_access & G, - NodeID & node, - refinement_pq * queue, - complete_boundary & boundary, - PartitionID & from, - PartitionID & to); - - void move_node(PartitionConfig & config, - graph_access & G, - NodeID & node, - refinement_pq * queue, - refinement_pq * to_queue, - complete_boundary & boundary, - PartitionID & from, - PartitionID & to); - - Gain find_eligible_start_node( graph_access & G, - PartitionID & lhs, - PartitionID & rhs, - std::vector & lhs_boundary, - std::vector & eligible, - NodeID & start_node, bool rebalance = false); - - void rebalance_fall_back(PartitionConfig & config, - graph_access & G, - graph_access & G_bar, - complete_boundary & boundary, - std::vector< NodeID > & candidates, - std::vector< int > & parent, - augmented_Qgraph & aqg); - - void perform_simple_move( PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - NodeID & node, - PartitionID & from, - PartitionID & to); - - kway_graph_refinement_commons* commons; - two_way_fm m_twfm; - std::vector m_eligible; - std::vector m_tomake_eligible; + NodeID & node, + PartitionID & from, + PartitionID & to); + + kway_graph_refinement_commons* commons; + two_way_fm m_twfm; + std::vector m_eligible; + std::vector m_tomake_eligible; }; -inline -Gain augmented_Qgraph_fabric::find_eligible_start_node( graph_access & G, - PartitionID & lhs, - PartitionID & rhs, - std::vector & lhs_boundary, +inline +Gain augmented_Qgraph_fabric::find_eligible_start_node( graph_access & G, + PartitionID & lhs, + PartitionID & rhs, + std::vector & lhs_boundary, std::vector & eligible_, NodeID & start_node, bool rebalance) { //select start node @@ -136,8 +136,8 @@ Gain augmented_Qgraph_fabric::find_eligible_start_node( graph_access & G, m_twfm.int_ext_degree(G, node, lhs, rhs, int_degree, ext_degree); if( ext_degree - int_degree > max_gain) { //todo tiebreaking max_gain = ext_degree - int_degree; - } - } + } + } } if( max_gain == std::numeric_limits::min() ) { @@ -152,14 +152,14 @@ Gain augmented_Qgraph_fabric::find_eligible_start_node( graph_access & G, EdgeWeight int_degree = 0; EdgeWeight ext_degree = 0; m_twfm.int_ext_degree(G, node, lhs, rhs, int_degree, ext_degree); - if( ext_degree - int_degree == max_gain) { - eligibles.push_back(node); - } - } + if( ext_degree - int_degree == max_gain) { + eligibles.push_back(node); + } + } } random_idx = random_functions::nextInt(0, eligibles.size()-1); - start_node = eligibles[random_idx]; + start_node = eligibles[random_idx]; for( unsigned i = 0; i < lhs_boundary.size(); i++) { NodeID node = lhs_boundary[i]; @@ -175,15 +175,15 @@ Gain augmented_Qgraph_fabric::find_eligible_start_node( graph_access & G, } inline -void augmented_Qgraph_fabric::rebalance_fall_back(PartitionConfig & config, - graph_access & G, - graph_access & G_bar, - complete_boundary & boundary, - std::vector< NodeID > & candidates, +void augmented_Qgraph_fabric::rebalance_fall_back(PartitionConfig & config, + graph_access & G, + graph_access & G_bar, + complete_boundary & boundary, + std::vector< NodeID > & candidates, std::vector< int > & parent, augmented_Qgraph & aqg) { - std::vector eligible_(G.number_of_nodes(), true); + std::vector eligible_(G.number_of_nodes(), true); random_functions::permutate_vector_good_small(candidates); std::vector best_path; @@ -228,17 +228,17 @@ void augmented_Qgraph_fabric::rebalance_fall_back(PartitionConfig & config, perform_simple_move(config, G, boundary, node, lhs, rhs); } - } + } - //undo these changes - for( unsigned i = 0; i < cur_path.size(); i++) { - perform_simple_move(config, G, boundary, cur_path[i].node, cur_path[i].to, cur_path[i].from); - } + //undo these changes + for( unsigned i = 0; i < cur_path.size(); i++) { + perform_simple_move(config, G, boundary, cur_path[i].node, cur_path[i].to, cur_path[i].from); + } - if(cur_path_gain > best_path_gain) { - best_path = cur_path; - best_path_gain = cur_path_gain; - } + if(cur_path_gain > best_path_gain) { + best_path = cur_path; + best_path_gain = cur_path_gain; + } } @@ -407,7 +407,7 @@ bool augmented_Qgraph_fabric::local_search(PartitionConfig & config, unsigned s ) { return construct_local_searches_on_qgraph_edge( config, G, boundary, aqg, pair, s, plus); } - +} #endif /* end of include guard: AUGMENTED_QGRAPH_FABRIC_MULTITRY_FM_PVGY97EW*/ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_definitions.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_definitions.h index f9185c2d..a660a02b 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_definitions.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_definitions.h @@ -11,7 +11,7 @@ #include #include "uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h" - +namespace kahip::modified { struct undo_struct { NodeID node; PartitionID to; @@ -28,6 +28,6 @@ struct data_qgraph_edge { }; typedef std::unordered_map edge_movements; - +} #endif /* end of include guard: DEFINITIONS_4GQMW8PZ */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp index 9834a433..e8d06c0b 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp @@ -11,7 +11,7 @@ #include "augmented_Qgraph_fabric.h" #include "cycle_refinement.h" #include "quality_metrics.h" - +namespace kahip::modified { cycle_refinement::cycle_refinement() { } @@ -23,170 +23,171 @@ cycle_refinement::~cycle_refinement() { EdgeWeight cycle_refinement::perform_refinement(PartitionConfig & partition_config, graph_access & G, complete_boundary & boundary) { - Gain overall_gain = 0; - PartitionConfig copy = partition_config; - - switch(partition_config.cycle_refinement_algorithm) { - case CYCLE_REFINEMENT_ALGORITHM_ULTRA_MODEL: - overall_gain = greedy_ultra_model(copy, G, boundary); - break; - case CYCLE_REFINEMENT_ALGORITHM_ULTRA_MODEL_PLUS: - overall_gain = greedy_ultra_model_plus(copy, G, boundary); - break; - case CYCLE_REFINEMENT_ALGORITHM_PLAYFIELD: - //dropbox for new algorithms - overall_gain = playfield_algorithm(copy, G, boundary); - break; - } - - return overall_gain; + Gain overall_gain = 0; + PartitionConfig copy = partition_config; + + switch(partition_config.cycle_refinement_algorithm) { + case CYCLE_REFINEMENT_ALGORITHM_ULTRA_MODEL: + overall_gain = greedy_ultra_model(copy, G, boundary); + break; + case CYCLE_REFINEMENT_ALGORITHM_ULTRA_MODEL_PLUS: + overall_gain = greedy_ultra_model_plus(copy, G, boundary); + break; + case CYCLE_REFINEMENT_ALGORITHM_PLAYFIELD: + //dropbox for new algorithms + overall_gain = playfield_algorithm(copy, G, boundary); + break; + } + + return overall_gain; } -EdgeWeight cycle_refinement::playfield_algorithm(PartitionConfig & partition_config, - graph_access & G, +EdgeWeight cycle_refinement::playfield_algorithm(PartitionConfig & partition_config, + graph_access & G, complete_boundary & boundary) { - greedy_ultra_model(partition_config, G, boundary); - greedy_ultra_model_plus(partition_config, G, boundary); - return 0; + greedy_ultra_model(partition_config, G, boundary); + greedy_ultra_model_plus(partition_config, G, boundary); + return 0; } -EdgeWeight cycle_refinement::greedy_ultra_model(PartitionConfig & partition_config, - graph_access & G, +EdgeWeight cycle_refinement::greedy_ultra_model(PartitionConfig & partition_config, + graph_access & G, complete_boundary & boundary) { - augmented_Qgraph_fabric augmented_fabric; - unsigned s = partition_config.kaba_internal_no_aug_steps_aug; - bool something_changed = false; - bool overloaded = false; - unsigned unsucc_count = 0; - - do { - augmented_Qgraph aqg; - augmented_fabric.build_augmented_quotient_graph(partition_config, G, boundary, aqg, s, false); - something_changed = m_advanced_modelling.compute_vertex_movements_ultra_model(partition_config, - G, - boundary, - aqg, - s, - false); - if( something_changed ) { - unsucc_count = 0; - } else { - unsucc_count++; - } - - if(unsucc_count > 2 - && unsucc_count <= partition_config.kaba_unsucc_iterations - && partition_config.kaba_enable_zero_weight_cycles) { - something_changed = m_advanced_modelling.compute_vertex_movements_ultra_model(partition_config, - G, - boundary, - aqg, - s, - true); - } - - if(unsucc_count >= partition_config.kaba_unsucc_iterations ) { - graph_access G_bar; - boundary.getUnderlyingQuotientGraph(G_bar); - overloaded = false; - forall_nodes(G_bar, block) { - if(boundary.getBlockWeight(block) > partition_config.upper_bound_partition ) { - overloaded = true; - break; - } - } endfor - - if(overloaded) { - augmented_Qgraph aqg_rebal; - bool movs_allready_performed = augmented_fabric.build_augmented_quotient_graph(partition_config, - G, - boundary, - aqg_rebal, - s, true); - if(!movs_allready_performed) { - m_advanced_modelling.compute_vertex_movements_rebalance(partition_config, - G, - boundary, - aqg_rebal, s); - } // else the fall back solution has been applied - } - } - } while(unsucc_count < partition_config.kaba_unsucc_iterations || (overloaded)); - - return 0; + augmented_Qgraph_fabric augmented_fabric; + unsigned s = partition_config.kaba_internal_no_aug_steps_aug; + bool something_changed = false; + bool overloaded = false; + unsigned unsucc_count = 0; + + do { + augmented_Qgraph aqg; + augmented_fabric.build_augmented_quotient_graph(partition_config, G, boundary, aqg, s, false); + something_changed = m_advanced_modelling.compute_vertex_movements_ultra_model(partition_config, + G, + boundary, + aqg, + s, + false); + if( something_changed ) { + unsucc_count = 0; + } else { + unsucc_count++; + } + + if(unsucc_count > 2 + && unsucc_count <= partition_config.kaba_unsucc_iterations + && partition_config.kaba_enable_zero_weight_cycles) { + something_changed = m_advanced_modelling.compute_vertex_movements_ultra_model(partition_config, + G, + boundary, + aqg, + s, + true); + } + + if(unsucc_count >= partition_config.kaba_unsucc_iterations ) { + graph_access G_bar; + boundary.getUnderlyingQuotientGraph(G_bar); + overloaded = false; + forall_nodes(G_bar, block) { + if(boundary.getBlockWeight(block) > partition_config.upper_bound_partition ) { + overloaded = true; + break; + } + } endfor + + if(overloaded) { + augmented_Qgraph aqg_rebal; + bool movs_allready_performed = augmented_fabric.build_augmented_quotient_graph(partition_config, + G, + boundary, + aqg_rebal, + s, true); + if(!movs_allready_performed) { + m_advanced_modelling.compute_vertex_movements_rebalance(partition_config, + G, + boundary, + aqg_rebal, s); + } // else the fall back solution has been applied + } + } + } while(unsucc_count < partition_config.kaba_unsucc_iterations || (overloaded)); + + return 0; } -EdgeWeight cycle_refinement::greedy_ultra_model_plus(PartitionConfig & partition_config, - graph_access & G, +EdgeWeight cycle_refinement::greedy_ultra_model_plus(PartitionConfig & partition_config, + graph_access & G, complete_boundary & boundary) { - unsigned s = partition_config.kaba_internal_no_aug_steps_aug; - bool something_changed = false; - bool overloaded = false; - - - augmented_Qgraph_fabric augmented_fabric; - bool first_level = true; - forall_nodes(G, node) { - if(G.getNodeWeight(node) != 1) { - first_level = false; - break; - } - } endfor - - int unsucc_count = 0; - do { - augmented_Qgraph aqg; - augmented_fabric.build_augmented_quotient_graph(partition_config, G, boundary, aqg, s, false, true); - something_changed = m_advanced_modelling.compute_vertex_movements_ultra_model(partition_config, - G, - boundary, - aqg, - s, - false); - - if( something_changed ) { - unsucc_count = 0; - } else { - unsucc_count++; - } - - if(unsucc_count > 2 && unsucc_count < 19) { - something_changed = m_advanced_modelling.compute_vertex_movements_ultra_model(partition_config, - G, - boundary, - aqg, - s, true); - } - - if(unsucc_count > 19 && first_level) { - graph_access G_bar; - boundary.getUnderlyingQuotientGraph(G_bar); - overloaded = false; - forall_nodes(G_bar, block) { - if(boundary.getBlockWeight(block) > partition_config.upper_bound_partition ) { - overloaded = true; - break; - } - } endfor - - if(overloaded) { - augmented_Qgraph aqg_rebal; - bool moves_performed = augmented_fabric.build_augmented_quotient_graph(partition_config, - G, - boundary, - aqg_rebal, - s, true, true); - if(!moves_performed) { - m_advanced_modelling.compute_vertex_movements_rebalance(partition_config, - G, boundary, - aqg_rebal, s); - } // else the fall back solution has been applied - } - - } - } while(unsucc_count < 20 || overloaded); - - return 0; + unsigned s = partition_config.kaba_internal_no_aug_steps_aug; + bool something_changed = false; + bool overloaded = false; + + + augmented_Qgraph_fabric augmented_fabric; + bool first_level = true; + forall_nodes(G, node) { + if(G.getNodeWeight(node) != 1) { + first_level = false; + break; + } + } endfor + + int unsucc_count = 0; + do { + augmented_Qgraph aqg; + augmented_fabric.build_augmented_quotient_graph(partition_config, G, boundary, aqg, s, false, true); + something_changed = m_advanced_modelling.compute_vertex_movements_ultra_model(partition_config, + G, + boundary, + aqg, + s, + false); + + if( something_changed ) { + unsucc_count = 0; + } else { + unsucc_count++; + } + + if(unsucc_count > 2 && unsucc_count < 19) { + something_changed = m_advanced_modelling.compute_vertex_movements_ultra_model(partition_config, + G, + boundary, + aqg, + s, true); + } + + if(unsucc_count > 19 && first_level) { + graph_access G_bar; + boundary.getUnderlyingQuotientGraph(G_bar); + overloaded = false; + forall_nodes(G_bar, block) { + if(boundary.getBlockWeight(block) > partition_config.upper_bound_partition ) { + overloaded = true; + break; + } + } endfor + + if(overloaded) { + augmented_Qgraph aqg_rebal; + bool moves_performed = augmented_fabric.build_augmented_quotient_graph(partition_config, + G, + boundary, + aqg_rebal, + s, true, true); + if(!moves_performed) { + m_advanced_modelling.compute_vertex_movements_rebalance(partition_config, + G, boundary, + aqg_rebal, s); + } // else the fall back solution has been applied + } + + } + } while(unsucc_count < 20 || overloaded); + + return 0; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.h index 8bd94fed..7f56041c 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.h @@ -15,32 +15,32 @@ #include "random_functions.h" #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class cycle_refinement : public refinement{ - public: - cycle_refinement(); - virtual ~cycle_refinement(); +public: + cycle_refinement(); + virtual ~cycle_refinement(); - EdgeWeight perform_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary); + EdgeWeight perform_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary); - private: - EdgeWeight greedy_ultra_model(PartitionConfig & partition_config, - graph_access & G, - complete_boundary & boundary); +private: + EdgeWeight greedy_ultra_model(PartitionConfig & partition_config, + graph_access & G, + complete_boundary & boundary); - EdgeWeight greedy_ultra_model_plus(PartitionConfig & partition_config, - graph_access & G, - complete_boundary & boundary); + EdgeWeight greedy_ultra_model_plus(PartitionConfig & partition_config, + graph_access & G, + complete_boundary & boundary); - EdgeWeight playfield_algorithm(PartitionConfig & partition_config, - graph_access & G, - complete_boundary & boundary); + EdgeWeight playfield_algorithm(PartitionConfig & partition_config, + graph_access & G, + complete_boundary & boundary); - advanced_models m_advanced_modelling; + advanced_models m_advanced_modelling; }; - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.cpp index 2c937261..3642d7e8 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "greedy_neg_cycle.h" - +namespace kahip::modified { greedy_neg_cycle::greedy_neg_cycle() { } @@ -14,4 +14,4 @@ greedy_neg_cycle::greedy_neg_cycle() { greedy_neg_cycle::~greedy_neg_cycle() { } - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.h index dc780943..7baea72a 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.h @@ -16,7 +16,7 @@ #include "problem_factory.h" #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h" #include "uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h" - +namespace kahip::modified { class greedy_neg_cycle { public: greedy_neg_cycle(); @@ -283,5 +283,5 @@ inline void greedy_neg_cycle::init_gains( PartitionConfig & partition_config, } - +} #endif /* end of include guard: GREEDY_NEG_CYCLE_IVBKH6WD */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.cpp index dbafc9bf..20e478fb 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "problem_factory.h" - +namespace kahip::modified { problem_factory::problem_factory() { } @@ -14,4 +14,4 @@ problem_factory::problem_factory() { problem_factory::~problem_factory() { } - +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.h index 7edb395b..bcda5f0b 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.h @@ -11,30 +11,30 @@ #include "definitions.h" #include "partition_config.h" #include "uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h" - +namespace kahip::modified { class problem_factory { - public: - problem_factory(); - virtual ~problem_factory(); - - void build_cycle_problem( PartitionConfig & partition_config, - complete_boundary & boundary, - graph_access & G_bar, - graph_access & cycle_problem, - NodeID & s); - - void build_cycle_problem_with_reverse( PartitionConfig & partition_config, - complete_boundary & boundary, - graph_access & G_bar, - graph_access & cycle_problem, - NodeID & s); - - void build_shortest_path_problem( PartitionConfig & partition_config, - complete_boundary & boundary, - graph_access & G_bar, - graph_access & shortest_path_problem, - NodeID & s, - NodeID & t); +public: + problem_factory(); + virtual ~problem_factory(); + + void build_cycle_problem( PartitionConfig & partition_config, + complete_boundary & boundary, + graph_access & G_bar, + graph_access & cycle_problem, + NodeID & s); + + void build_cycle_problem_with_reverse( PartitionConfig & partition_config, + complete_boundary & boundary, + graph_access & G_bar, + graph_access & cycle_problem, + NodeID & s); + + void build_shortest_path_problem( PartitionConfig & partition_config, + complete_boundary & boundary, + graph_access & G_bar, + graph_access & shortest_path_problem, + NodeID & s, + NodeID & t); }; @@ -139,5 +139,5 @@ void problem_factory::build_shortest_path_problem( PartitionConfig & partition_c shortest_path_problem.finish_construction(); } - +} #endif /* end of include guard: PROBLEM_FACTORY_KHGQXT9H */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.cpp index f2a419ea..d1b8a24f 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.cpp @@ -13,7 +13,7 @@ #include "kway_stop_rule.h" #include "quality_metrics.h" #include "random_functions.h" - +namespace kahip::modified { kway_graph_refinement::kway_graph_refinement() { } @@ -24,70 +24,70 @@ kway_graph_refinement::~kway_graph_refinement() { EdgeWeight kway_graph_refinement::perform_refinement(PartitionConfig & config, graph_access & G, complete_boundary & boundary) { - commons = kway_graph_refinement_commons::getInstance(config); - kway_graph_refinement_core refinement_core; - - EdgeWeight overall_improvement = 0; - int max_number_of_swaps = (int)(G.number_of_nodes()); - bool sth_changed = config.no_change_convergence; + commons = kway_graph_refinement_commons::getInstance(config); + kway_graph_refinement_core refinement_core; + + EdgeWeight overall_improvement = 0; + int max_number_of_swaps = (int)(G.number_of_nodes()); + bool sth_changed = config.no_change_convergence; - for( unsigned i = 0; i < config.kway_rounds || sth_changed; i++) { - EdgeWeight improvement = 0; + for( unsigned i = 0; i < config.kway_rounds || sth_changed; i++) { + EdgeWeight improvement = 0; - boundary_starting_nodes start_nodes; - setup_start_nodes(config, G, boundary, start_nodes); + boundary_starting_nodes start_nodes; + setup_start_nodes(config, G, boundary, start_nodes); - if(start_nodes.size() == 0) return 0; // nothing to refine + if(start_nodes.size() == 0) return 0; // nothing to refine - //metis steplimit - int step_limit = (int)((config.kway_fm_search_limit/100.0)*max_number_of_swaps); - step_limit = std::max(step_limit, 15); + //metis steplimit + int step_limit = (int)((config.kway_fm_search_limit/100.0)*max_number_of_swaps); + step_limit = std::max(step_limit, 15); - vertex_moved_hashtable moved_idx; - improvement += refinement_core.single_kway_refinement_round(config, G, boundary, - start_nodes, step_limit, - moved_idx); + vertex_moved_hashtable moved_idx; + improvement += refinement_core.single_kway_refinement_round(config, G, boundary, + start_nodes, step_limit, + moved_idx); - sth_changed = improvement != 0 && config.no_change_convergence; - if(improvement == 0) break; - overall_improvement += improvement; + sth_changed = improvement != 0 && config.no_change_convergence; + if(improvement == 0) break; + overall_improvement += improvement; - } + } - ASSERT_TRUE(overall_improvement >= 0); + ASSERT_TRUE(overall_improvement >= 0); - return (EdgeWeight) overall_improvement; + return (EdgeWeight) overall_improvement; } void kway_graph_refinement::setup_start_nodes(PartitionConfig & config, graph_access & G, complete_boundary & boundary, boundary_starting_nodes & start_nodes) { - QuotientGraphEdges quotient_graph_edges; - boundary.getQuotientGraphEdges(quotient_graph_edges); - - std::unordered_map allready_contained; - - for( unsigned i = 0; i < quotient_graph_edges.size(); i++) { - boundary_pair & ret_value = quotient_graph_edges[i]; - PartitionID lhs = ret_value.lhs; - PartitionID rhs = ret_value.rhs; - - PartialBoundary & partial_boundary_lhs = boundary.getDirectedBoundary(lhs, lhs, rhs); - forall_boundary_nodes(partial_boundary_lhs, cur_bnd_node) { - ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), lhs); - if(allready_contained.find(cur_bnd_node) == allready_contained.end() ) { - start_nodes.push_back(cur_bnd_node); - allready_contained[cur_bnd_node] = true; - } - } endfor - - PartialBoundary & partial_boundary_rhs = boundary.getDirectedBoundary(rhs, lhs, rhs); - forall_boundary_nodes(partial_boundary_rhs, cur_bnd_node) { - ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), rhs); - if(allready_contained.find(cur_bnd_node) == allready_contained.end()) { - start_nodes.push_back(cur_bnd_node); - allready_contained[cur_bnd_node] = true; - } - } endfor - } + QuotientGraphEdges quotient_graph_edges; + boundary.getQuotientGraphEdges(quotient_graph_edges); + + std::unordered_map allready_contained; + + for( unsigned i = 0; i < quotient_graph_edges.size(); i++) { + boundary_pair & ret_value = quotient_graph_edges[i]; + PartitionID lhs = ret_value.lhs; + PartitionID rhs = ret_value.rhs; + + PartialBoundary & partial_boundary_lhs = boundary.getDirectedBoundary(lhs, lhs, rhs); + forall_boundary_nodes(partial_boundary_lhs, cur_bnd_node) { + ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), lhs); + if(allready_contained.find(cur_bnd_node) == allready_contained.end() ) { + start_nodes.push_back(cur_bnd_node); + allready_contained[cur_bnd_node] = true; + } + } endfor + + PartialBoundary & partial_boundary_rhs = boundary.getDirectedBoundary(rhs, lhs, rhs); + forall_boundary_nodes(partial_boundary_rhs, cur_bnd_node) { + ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), rhs); + if(allready_contained.find(cur_bnd_node) == allready_contained.end()) { + start_nodes.push_back(cur_bnd_node); + allready_contained[cur_bnd_node] = true; + } + } endfor +} +} } - diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.h index 5d4bb124..6319df2e 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.h @@ -16,25 +16,25 @@ #include "random_functions.h" #include "uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class kway_graph_refinement : public refinement { - public: - kway_graph_refinement( ); - virtual ~kway_graph_refinement(); - - EdgeWeight perform_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary); - - void setup_start_nodes(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - boundary_starting_nodes & start_nodes); - - private: - - kway_graph_refinement_commons* commons; -}; +public: + kway_graph_refinement( ); + virtual ~kway_graph_refinement(); + + EdgeWeight perform_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary); + void setup_start_nodes(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + boundary_starting_nodes & start_nodes); + +private: + + kway_graph_refinement_commons* commons; +}; +} #endif /* end of include guard: KWAY_GRAPH_REFINEMENT_PVGY97EW */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.cpp index bf21b630..f1b5d7b9 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.cpp @@ -10,7 +10,7 @@ #endif #include "kway_graph_refinement_commons.h" - +namespace kahip::modified { std::vector* kway_graph_refinement_commons::m_instances = NULL; kway_graph_refinement_commons::kway_graph_refinement_commons() { @@ -22,35 +22,36 @@ kway_graph_refinement_commons::~kway_graph_refinement_commons() { kway_graph_refinement_commons* kway_graph_refinement_commons::getInstance( PartitionConfig & config ) { - bool created = false; - #ifdef USE_OPENMP - int max_threads = omp_get_max_threads(); - #pragma omp critical - #else - int max_threads = 1; - #endif - { - if( m_instances == NULL ) { - m_instances = new std::vector< kway_graph_refinement_commons*>(max_threads, NULL); - } - } - #ifdef USE_OPENMP - int id = omp_get_thread_num(); - #else - int id = 0; - #endif - if((*m_instances)[id] == NULL) { - (*m_instances)[id] = new kway_graph_refinement_commons(); - (*m_instances)[id]->init(config); - created = true; - } - - if(created == false) { - if(config.k != (*m_instances)[id]->getUnderlyingK()) { - //should be a very rare case - (*m_instances)[id]->init(config); - } - } - - return (*m_instances)[id]; + bool created = false; +#ifdef USE_OPENMP + int max_threads = omp_get_max_threads(); +#pragma omp critical +#else + int max_threads = 1; +#endif + { + if( m_instances == NULL ) { + m_instances = new std::vector< kway_graph_refinement_commons*>(max_threads, NULL); + } + } +#ifdef USE_OPENMP + int id = omp_get_thread_num(); +#else + int id = 0; +#endif + if((*m_instances)[id] == NULL) { + (*m_instances)[id] = new kway_graph_refinement_commons(); + (*m_instances)[id]->init(config); + created = true; + } + + if(created == false) { + if(config.k != (*m_instances)[id]->getUnderlyingK()) { + //should be a very rare case + (*m_instances)[id]->init(config); + } + } + + return (*m_instances)[id]; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h index 6b39d9fe..a95a4b05 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h @@ -16,45 +16,45 @@ #include "random_functions.h" #include "uncoarsening/refinement/refinement.h" #include "uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h" - +namespace kahip::modified { class kway_graph_refinement_commons { - public: +public: - virtual ~kway_graph_refinement_commons(); + virtual ~kway_graph_refinement_commons(); - void init( PartitionConfig & config ); + void init( PartitionConfig & config ); - bool incident_to_more_than_two_partitions(graph_access & G, NodeID & node); + bool incident_to_more_than_two_partitions(graph_access & G, NodeID & node); - EdgeWeight compute_gain(graph_access & G, - NodeID & node, - PartitionID & max_gainer, - EdgeWeight & ext_degree); + EdgeWeight compute_gain(graph_access & G, + NodeID & node, + PartitionID & max_gainer, + EdgeWeight & ext_degree); - bool int_ext_degree( graph_access & G, - const NodeID & node, - const PartitionID lhs, - const PartitionID rhs, - EdgeWeight & int_degree, - EdgeWeight & ext_degree); + bool int_ext_degree( graph_access & G, + const NodeID & node, + const PartitionID lhs, + const PartitionID rhs, + EdgeWeight & int_degree, + EdgeWeight & ext_degree); - static kway_graph_refinement_commons* getInstance( PartitionConfig & config ); + static kway_graph_refinement_commons* getInstance( PartitionConfig & config ); - inline unsigned getUnderlyingK(); + inline unsigned getUnderlyingK(); - private: - kway_graph_refinement_commons( ); +private: + kway_graph_refinement_commons( ); - //for efficient computation of internal and external degrees - struct round_struct { - unsigned round; - EdgeWeight local_degree; - }; + //for efficient computation of internal and external degrees + struct round_struct { + unsigned round; + EdgeWeight local_degree; + }; - static - std::vector* m_instances; - std::vector m_local_degrees; - unsigned m_round; + static + std::vector* m_instances; + std::vector m_local_degrees; + unsigned m_round; }; @@ -178,7 +178,7 @@ inline Gain kway_graph_refinement_commons::compute_gain(graph_access & G, return max_degree-m_local_degrees[source_partition].local_degree; } - +} #endif /* end of include guard: KWAY_GRAPH_REFINEMENT_COMMONS_PVGY97EW */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.cpp index 076f918e..8ffc6e38 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.cpp @@ -13,7 +13,7 @@ #include "kway_stop_rule.h" #include "quality_metrics.h" #include "random_functions.h" - +namespace kahip::modified { kway_graph_refinement_core::kway_graph_refinement_core() { } @@ -26,147 +26,147 @@ EdgeWeight kway_graph_refinement_core::single_kway_refinement_round(PartitionCon boundary_starting_nodes & start_nodes, int step_limit, vertex_moved_hashtable & moved_idx) { - std::unordered_map touched_blocks; - return single_kway_refinement_round_internal(config, G, boundary, start_nodes, - step_limit, moved_idx, false, touched_blocks); + std::unordered_map touched_blocks; + return single_kway_refinement_round_internal(config, G, boundary, start_nodes, + step_limit, moved_idx, false, touched_blocks); } -EdgeWeight kway_graph_refinement_core::single_kway_refinement_round(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - boundary_starting_nodes & start_nodes, - int step_limit, +EdgeWeight kway_graph_refinement_core::single_kway_refinement_round(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + boundary_starting_nodes & start_nodes, + int step_limit, vertex_moved_hashtable & moved_idx, std::unordered_map & touched_blocks) { - return single_kway_refinement_round_internal(config, G, boundary, start_nodes, - step_limit, moved_idx, true, touched_blocks); + return single_kway_refinement_round_internal(config, G, boundary, start_nodes, + step_limit, moved_idx, true, touched_blocks); } -EdgeWeight kway_graph_refinement_core::single_kway_refinement_round_internal(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - boundary_starting_nodes & start_nodes, +EdgeWeight kway_graph_refinement_core::single_kway_refinement_round_internal(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + boundary_starting_nodes & start_nodes, int step_limit, vertex_moved_hashtable & moved_idx, bool compute_touched_partitions, std::unordered_map & touched_blocks) { - commons = kway_graph_refinement_commons::getInstance(config); - refinement_pq* queue = NULL; - if(config.use_bucket_queues) { - EdgeWeight max_degree = G.getMaxDegree(); - queue = new bucket_pq(max_degree); - } else { - queue = new maxNodeHeap(); - } + commons = kway_graph_refinement_commons::getInstance(config); + refinement_pq* queue = NULL; + if(config.use_bucket_queues) { + EdgeWeight max_degree = G.getMaxDegree(); + queue = new bucket_pq(max_degree); + } else { + queue = new maxNodeHeap(); + } + + init_queue_with_boundary(config, G, start_nodes, queue, moved_idx); - init_queue_with_boundary(config, G, start_nodes, queue, moved_idx); - - if(queue->empty()) {delete queue; return 0;} + if(queue->empty()) {delete queue; return 0;} - std::vector transpositions; - std::vector from_partitions; - std::vector to_partitions; + std::vector transpositions; + std::vector from_partitions; + std::vector to_partitions; - int max_number_of_swaps = (int)(G.number_of_nodes()); - int min_cut_index = -1; + int max_number_of_swaps = (int)(G.number_of_nodes()); + int min_cut_index = -1; - EdgeWeight cut = std::numeric_limits::max()/2; // so we dont need to compute the edge cut - EdgeWeight initial_cut = cut; + EdgeWeight cut = std::numeric_limits::max()/2; // so we dont need to compute the edge cut + EdgeWeight initial_cut = cut; - //roll forwards - EdgeWeight best_cut = cut; - int number_of_swaps = 0; - int movements = 0; + //roll forwards + EdgeWeight best_cut = cut; + int number_of_swaps = 0; + int movements = 0; - kway_stop_rule* stopping_rule = NULL; - switch(config.kway_stop_rule) { - case KWAY_SIMPLE_STOP_RULE: - stopping_rule = new kway_simple_stop_rule(config); - break; - case KWAY_ADAPTIVE_STOP_RULE: - stopping_rule = new kway_adaptive_stop_rule(config); - break; + kway_stop_rule* stopping_rule = NULL; + switch(config.kway_stop_rule) { + case KWAY_SIMPLE_STOP_RULE: + stopping_rule = new kway_simple_stop_rule(config); + break; + case KWAY_ADAPTIVE_STOP_RULE: + stopping_rule = new kway_adaptive_stop_rule(config); + break; - } + } - for(number_of_swaps = 0, movements = 0; movements < max_number_of_swaps; movements++, number_of_swaps++) { - if( queue->empty() ) break; - if( stopping_rule->search_should_stop(min_cut_index, number_of_swaps, step_limit) ) break; + for(number_of_swaps = 0, movements = 0; movements < max_number_of_swaps; movements++, number_of_swaps++) { + if( queue->empty() ) break; + if( stopping_rule->search_should_stop(min_cut_index, number_of_swaps, step_limit) ) break; - Gain gain = queue->maxValue(); - NodeID node = queue->deleteMax(); + Gain gain = queue->maxValue(); + NodeID node = queue->deleteMax(); #ifndef NDEBUG - PartitionID maxgainer; - EdgeWeight ext_degree; - ASSERT_TRUE(moved_idx[node].index == NOT_MOVED); - ASSERT_EQ(gain, commons->compute_gain(G, node, maxgainer, ext_degree)); - ASSERT_TRUE(ext_degree > 0); + PartitionID maxgainer; + EdgeWeight ext_degree; + ASSERT_TRUE(moved_idx[node].index == NOT_MOVED); + ASSERT_EQ(gain, commons->compute_gain(G, node, maxgainer, ext_degree)); + ASSERT_TRUE(ext_degree > 0); #endif - PartitionID from = G.getPartitionIndex(node); - bool successfull = move_node(config, G, node, moved_idx, queue, boundary); - - if(successfull) { - cut -= gain; - stopping_rule->push_statistics(gain); - - bool accept_equal = random_functions::nextBool(); - if( cut < best_cut || ( cut == best_cut && accept_equal )) { - best_cut = cut; - min_cut_index = number_of_swaps; - if(cut < best_cut) - stopping_rule->reset_statistics(); - } - - from_partitions.push_back(from); - to_partitions.push_back(G.getPartitionIndex(node)); - transpositions.push_back(node); - } else { - number_of_swaps--; //because it wasnt swaps - } - moved_idx[node].index = MOVED; - ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); - ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); - - } - - ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); - ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); - - //roll backwards - for(number_of_swaps--; number_of_swaps>min_cut_index; number_of_swaps--) { - ASSERT_TRUE(transpositions.size() > 0); - - NodeID node = transpositions.back(); - transpositions.pop_back(); - - PartitionID to = from_partitions.back(); - from_partitions.pop_back(); - to_partitions.pop_back(); - - move_node_back(config, G, node, to, moved_idx, queue, boundary); - } - - - //reconstruct the touched partitions - if(compute_touched_partitions) { - ASSERT_EQ(from_partitions.size(), to_partitions.size()); - for(unsigned i = 0; i < from_partitions.size(); i++) { - touched_blocks[from_partitions[i]] = from_partitions[i]; - touched_blocks[to_partitions[i]] = to_partitions[i]; - } - } - - ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); - ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); - - delete queue; - delete stopping_rule; - return initial_cut - best_cut; + PartitionID from = G.getPartitionIndex(node); + bool successfull = move_node(config, G, node, moved_idx, queue, boundary); + + if(successfull) { + cut -= gain; + stopping_rule->push_statistics(gain); + + bool accept_equal = random_functions::nextBool(); + if( cut < best_cut || ( cut == best_cut && accept_equal )) { + best_cut = cut; + min_cut_index = number_of_swaps; + if(cut < best_cut) + stopping_rule->reset_statistics(); + } + + from_partitions.push_back(from); + to_partitions.push_back(G.getPartitionIndex(node)); + transpositions.push_back(node); + } else { + number_of_swaps--; //because it wasnt swaps + } + moved_idx[node].index = MOVED; + ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); + ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); + + } + + ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); + ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); + + //roll backwards + for(number_of_swaps--; number_of_swaps>min_cut_index; number_of_swaps--) { + ASSERT_TRUE(transpositions.size() > 0); + + NodeID node = transpositions.back(); + transpositions.pop_back(); + + PartitionID to = from_partitions.back(); + from_partitions.pop_back(); + to_partitions.pop_back(); + + move_node_back(config, G, node, to, moved_idx, queue, boundary); + } + + + //reconstruct the touched partitions + if(compute_touched_partitions) { + ASSERT_EQ(from_partitions.size(), to_partitions.size()); + for(unsigned i = 0; i < from_partitions.size(); i++) { + touched_blocks[from_partitions[i]] = from_partitions[i]; + touched_blocks[to_partitions[i]] = to_partitions[i]; + } + } + + ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); + ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); + + delete queue; + delete stopping_rule; + return initial_cut - best_cut; } void kway_graph_refinement_core::init_queue_with_boundary(const PartitionConfig & config, @@ -174,50 +174,50 @@ void kway_graph_refinement_core::init_queue_with_boundary(const PartitionConfig std::vector & bnd_nodes, refinement_pq * queue, vertex_moved_hashtable & moved_idx) { - if(config.permutation_during_refinement == PERMUTATION_QUALITY_FAST) { - random_functions::permutate_vector_fast(bnd_nodes, false); - } else if(config.permutation_during_refinement == PERMUTATION_QUALITY_GOOD) { - random_functions::permutate_vector_good(bnd_nodes, false); - } - - for( unsigned int i = 0; i < bnd_nodes.size(); i++) { - NodeID node = bnd_nodes[i]; - - if( moved_idx.find(node) == moved_idx.end() ) { - PartitionID max_gainer; - EdgeWeight ext_degree; - //compute gain - Gain gain = commons->compute_gain(G, node, max_gainer, ext_degree); - queue->insert(node, gain); - moved_idx[node].index = NOT_MOVED; - } - } + if(config.permutation_during_refinement == PERMUTATION_QUALITY_FAST) { + random_functions::permutate_vector_fast(bnd_nodes, false); + } else if(config.permutation_during_refinement == PERMUTATION_QUALITY_GOOD) { + random_functions::permutate_vector_good(bnd_nodes, false); + } + + for( unsigned int i = 0; i < bnd_nodes.size(); i++) { + NodeID node = bnd_nodes[i]; + + if( moved_idx.find(node) == moved_idx.end() ) { + PartitionID max_gainer; + EdgeWeight ext_degree; + //compute gain + Gain gain = commons->compute_gain(G, node, max_gainer, ext_degree); + queue->insert(node, gain); + moved_idx[node].index = NOT_MOVED; + } + } } -void kway_graph_refinement_core::move_node_back(PartitionConfig & config, - graph_access & G, +void kway_graph_refinement_core::move_node_back(PartitionConfig & config, + graph_access & G, NodeID & node, - PartitionID & to, - vertex_moved_hashtable & moved_idx, - refinement_pq * queue, + PartitionID & to, + vertex_moved_hashtable & moved_idx, + refinement_pq * queue, complete_boundary & boundary) { - PartitionID from = G.getPartitionIndex(node); - G.setPartitionIndex(node, to); + PartitionID from = G.getPartitionIndex(node); + G.setPartitionIndex(node, to); - boundary_pair pair; - pair.k = config.k; - pair.lhs = from; - pair.rhs = to; + boundary_pair pair; + pair.k = config.k; + pair.lhs = from; + pair.rhs = to; - //update all boundaries - boundary.postMovedBoundaryNodeUpdates(node, &pair, true, true); + //update all boundaries + boundary.postMovedBoundaryNodeUpdates(node, &pair, true, true); - NodeWeight this_nodes_weight = G.getNodeWeight(node); - boundary.setBlockNoNodes(from, boundary.getBlockNoNodes(from)-1); - boundary.setBlockNoNodes(to, boundary.getBlockNoNodes(to)+1); - boundary.setBlockWeight( from, boundary.getBlockWeight(from)-this_nodes_weight); - boundary.setBlockWeight( to, boundary.getBlockWeight(to)+this_nodes_weight); + NodeWeight this_nodes_weight = G.getNodeWeight(node); + boundary.setBlockNoNodes(from, boundary.getBlockNoNodes(from)-1); + boundary.setBlockNoNodes(to, boundary.getBlockNoNodes(to)+1); + boundary.setBlockWeight( from, boundary.getBlockWeight(from)-this_nodes_weight); + boundary.setBlockWeight( to, boundary.getBlockWeight(to)+this_nodes_weight); +} } - diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.h index 712ceb1b..77b24c0a 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.h @@ -17,65 +17,65 @@ #include "tools/random_functions.h" #include "uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class kway_graph_refinement_core { - public: - kway_graph_refinement_core( ); - virtual ~kway_graph_refinement_core(); - - EdgeWeight single_kway_refinement_round(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - boundary_starting_nodes & start_nodes, - int step_limit, - vertex_moved_hashtable & moved_idx ); - - EdgeWeight single_kway_refinement_round(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - boundary_starting_nodes & start_nodes, - int step_limit, - vertex_moved_hashtable & moved_idx, - std::unordered_map & touched_blocks); - - - private: - EdgeWeight single_kway_refinement_round_internal(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - boundary_starting_nodes & start_nodes, - int step_limit, - vertex_moved_hashtable & moved_idx, - bool compute_touched_partitions, - std::unordered_map & touched_blocks); - - - void init_queue_with_boundary(const PartitionConfig & config, - graph_access & G, - std::vector & bnd_nodes, - refinement_pq * queue, - vertex_moved_hashtable & moved_idx); - - inline bool move_node(PartitionConfig & config, - graph_access & G, - NodeID & node, - vertex_moved_hashtable & moved_idx, - refinement_pq * queue, - complete_boundary & boundary); - - inline void move_node_back(PartitionConfig & config, - graph_access & G, - NodeID & node, - PartitionID & to, - vertex_moved_hashtable & moved_idx, - refinement_pq * queue, - complete_boundary & boundary); - - void initialize_partition_moves_array(PartitionConfig & config, - complete_boundary & boundary, - std::vector & partition_move_valid); - - kway_graph_refinement_commons* commons; +public: + kway_graph_refinement_core( ); + virtual ~kway_graph_refinement_core(); + + EdgeWeight single_kway_refinement_round(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + boundary_starting_nodes & start_nodes, + int step_limit, + vertex_moved_hashtable & moved_idx ); + + EdgeWeight single_kway_refinement_round(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + boundary_starting_nodes & start_nodes, + int step_limit, + vertex_moved_hashtable & moved_idx, + std::unordered_map & touched_blocks); + + +private: + EdgeWeight single_kway_refinement_round_internal(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + boundary_starting_nodes & start_nodes, + int step_limit, + vertex_moved_hashtable & moved_idx, + bool compute_touched_partitions, + std::unordered_map & touched_blocks); + + + void init_queue_with_boundary(const PartitionConfig & config, + graph_access & G, + std::vector & bnd_nodes, + refinement_pq * queue, + vertex_moved_hashtable & moved_idx); + + inline bool move_node(PartitionConfig & config, + graph_access & G, + NodeID & node, + vertex_moved_hashtable & moved_idx, + refinement_pq * queue, + complete_boundary & boundary); + + inline void move_node_back(PartitionConfig & config, + graph_access & G, + NodeID & node, + PartitionID & to, + vertex_moved_hashtable & moved_idx, + refinement_pq * queue, + complete_boundary & boundary); + + void initialize_partition_moves_array(PartitionConfig & config, + complete_boundary & boundary, + std::vector & partition_move_valid); + + kway_graph_refinement_commons* commons; }; inline bool kway_graph_refinement_core::move_node(PartitionConfig & config, @@ -143,6 +143,6 @@ inline bool kway_graph_refinement_core::move_node(PartitionConfig & config, return true; } - +} #endif //[> end of include guard: KWAY_GRAPH_REFINEMENT_PVGY97EW <] diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_stop_rule.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_stop_rule.h index 87bfed77..a452c53f 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_stop_rule.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_stop_rule.h @@ -7,7 +7,7 @@ #ifndef KWAY_STOP_RULE_ULPK0ZTF #define KWAY_STOP_RULE_ULPK0ZTF - +namespace kahip::modified { class kway_stop_rule { public: kway_stop_rule(PartitionConfig & config) {}; @@ -90,7 +90,7 @@ inline bool kway_adaptive_stop_rule::search_should_stop(unsigned int min_cut_idx return m_steps*m_expected_gain*m_expected_gain > pconfig->kway_adaptive_limits_alpha * m_expected_variance2 + pconfig->kway_adaptive_limits_beta && (m_steps != 1); } - +} #endif /* end of include guard: KWAY_STOP_RULE_ULPK0ZTF */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.cpp index 3ffac667..39e8c9ce 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.cpp @@ -14,7 +14,7 @@ #include "quality_metrics.h" #include "random_functions.h" #include "uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h" - +namespace kahip::modified { multitry_kway_fm::multitry_kway_fm() { } @@ -26,151 +26,151 @@ int multitry_kway_fm::perform_refinement(PartitionConfig & config, graph_access complete_boundary & boundary, unsigned rounds, bool init_neighbors, unsigned alpha) { - commons = kway_graph_refinement_commons::getInstance(config); - - unsigned tmp_alpha = config.kway_adaptive_limits_alpha; - KWayStopRule tmp_stop = config.kway_stop_rule; - config.kway_adaptive_limits_alpha = alpha; - config.kway_stop_rule = KWAY_ADAPTIVE_STOP_RULE; - - int overall_improvement = 0; - for( unsigned i = 0; i < rounds; i++) { - boundary_starting_nodes start_nodes; - boundary.setup_start_nodes_all(G, start_nodes); - if(start_nodes.size() == 0) { - return 0; - }// nothing to refine - - //now we do something with the start nodes - //convert it into a list - std::vector todolist; - for(unsigned i = 0; i < start_nodes.size(); i++) { - todolist.push_back(start_nodes[i]); - } - - std::unordered_map touched_blocks; - EdgeWeight improvement = start_more_locallized_search(config, G, boundary, - init_neighbors, false, touched_blocks, - todolist); - if( improvement == 0 ) break; - overall_improvement += improvement; + commons = kway_graph_refinement_commons::getInstance(config); - } + unsigned tmp_alpha = config.kway_adaptive_limits_alpha; + KWayStopRule tmp_stop = config.kway_stop_rule; + config.kway_adaptive_limits_alpha = alpha; + config.kway_stop_rule = KWAY_ADAPTIVE_STOP_RULE; - ASSERT_TRUE(overall_improvement >= 0); + int overall_improvement = 0; + for( unsigned i = 0; i < rounds; i++) { + boundary_starting_nodes start_nodes; + boundary.setup_start_nodes_all(G, start_nodes); + if(start_nodes.size() == 0) { + return 0; + }// nothing to refine - config.kway_adaptive_limits_alpha = tmp_alpha; - config.kway_stop_rule = tmp_stop; + //now we do something with the start nodes + //convert it into a list + std::vector todolist; + for(unsigned i = 0; i < start_nodes.size(); i++) { + todolist.push_back(start_nodes[i]); + } + + std::unordered_map touched_blocks; + EdgeWeight improvement = start_more_locallized_search(config, G, boundary, + init_neighbors, false, touched_blocks, + todolist); + if( improvement == 0 ) break; + overall_improvement += improvement; + + } + + ASSERT_TRUE(overall_improvement >= 0); + + config.kway_adaptive_limits_alpha = tmp_alpha; + config.kway_stop_rule = tmp_stop; + + return (int) overall_improvement; - return (int) overall_improvement; - } -int multitry_kway_fm::perform_refinement_around_parts(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, bool init_neighbors, - unsigned alpha, - PartitionID & lhs, PartitionID & rhs, +int multitry_kway_fm::perform_refinement_around_parts(PartitionConfig & config, graph_access & G, + complete_boundary & boundary, bool init_neighbors, + unsigned alpha, + PartitionID & lhs, PartitionID & rhs, std::unordered_map & touched_blocks) { - commons = kway_graph_refinement_commons::getInstance(config); - - unsigned tmp_alpha = config.kway_adaptive_limits_alpha; - KWayStopRule tmp_stop = config.kway_stop_rule; - config.kway_adaptive_limits_alpha = alpha; - config.kway_stop_rule = KWAY_ADAPTIVE_STOP_RULE; - int overall_improvement = 0; - - for( unsigned i = 0; i < config.local_multitry_rounds; i++) { - boundary_starting_nodes start_nodes; - boundary.setup_start_nodes_around_blocks(G, lhs, rhs, start_nodes); - - if(start_nodes.size() == 0) { return 0; }// nothing to refine - - //now we do something with the start nodes - std::vector todolist; - for(unsigned i = 0; i < start_nodes.size(); i++) { - todolist.push_back(start_nodes[i]); - } - - EdgeWeight improvement = start_more_locallized_search(config, G, boundary, - init_neighbors, true, - touched_blocks, todolist); - if( improvement == 0 ) break; - - overall_improvement += improvement; - } - - config.kway_adaptive_limits_alpha = tmp_alpha; - config.kway_stop_rule = tmp_stop; - ASSERT_TRUE(overall_improvement >= 0); - return (int) overall_improvement; + commons = kway_graph_refinement_commons::getInstance(config); + + unsigned tmp_alpha = config.kway_adaptive_limits_alpha; + KWayStopRule tmp_stop = config.kway_stop_rule; + config.kway_adaptive_limits_alpha = alpha; + config.kway_stop_rule = KWAY_ADAPTIVE_STOP_RULE; + int overall_improvement = 0; + + for( unsigned i = 0; i < config.local_multitry_rounds; i++) { + boundary_starting_nodes start_nodes; + boundary.setup_start_nodes_around_blocks(G, lhs, rhs, start_nodes); + + if(start_nodes.size() == 0) { return 0; }// nothing to refine + + //now we do something with the start nodes + std::vector todolist; + for(unsigned i = 0; i < start_nodes.size(); i++) { + todolist.push_back(start_nodes[i]); + } + + EdgeWeight improvement = start_more_locallized_search(config, G, boundary, + init_neighbors, true, + touched_blocks, todolist); + if( improvement == 0 ) break; + + overall_improvement += improvement; + } + + config.kway_adaptive_limits_alpha = tmp_alpha; + config.kway_stop_rule = tmp_stop; + ASSERT_TRUE(overall_improvement >= 0); + return (int) overall_improvement; } -int multitry_kway_fm::start_more_locallized_search(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, bool init_neighbors, - bool compute_touched_blocks, - std::unordered_map & touched_blocks, +int multitry_kway_fm::start_more_locallized_search(PartitionConfig & config, graph_access & G, + complete_boundary & boundary, bool init_neighbors, + bool compute_touched_blocks, + std::unordered_map & touched_blocks, std::vector & todolist) { - random_functions::permutate_vector_good(todolist, false); - commons = kway_graph_refinement_commons::getInstance(config); - - kway_graph_refinement_core refinement_core; - int local_step_limit = 0; - - vertex_moved_hashtable moved_idx; - unsigned idx = todolist.size()-1; - int overall_improvement = 0; - - while(!todolist.empty()) { - int random_idx = random_functions::nextInt(0, idx); - NodeID node = todolist[random_idx]; - - PartitionID maxgainer; - EdgeWeight extdeg = 0; - commons->compute_gain(G, node, maxgainer, extdeg); - - if(moved_idx.find(node) == moved_idx.end() && extdeg > 0) { - boundary_starting_nodes real_start_nodes; - real_start_nodes.push_back(node); - - if(init_neighbors) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(moved_idx.find(target) == moved_idx.end()) { - extdeg = 0; - commons->compute_gain(G, target, maxgainer, extdeg); - if(extdeg > 0) { - real_start_nodes.push_back(target); - } - } - } endfor - } - int improvement = 0; - if(compute_touched_blocks) { - improvement = refinement_core.single_kway_refinement_round(config, G, - boundary, real_start_nodes, - local_step_limit, moved_idx, - touched_blocks); - if(improvement < 0) { - std::cout << "buf error improvement < 0" << std::endl; - } - } else { - improvement = refinement_core.single_kway_refinement_round(config, G, - boundary, real_start_nodes, - local_step_limit, moved_idx); - if(improvement < 0) { - std::cout << "buf error improvement < 0" << std::endl; - } - } - - overall_improvement += improvement; - - } - - if(moved_idx.size() > 0.05*G.number_of_nodes()) break; - std::swap(todolist[random_idx], todolist[idx--]); todolist.pop_back(); + random_functions::permutate_vector_good(todolist, false); + commons = kway_graph_refinement_commons::getInstance(config); + + kway_graph_refinement_core refinement_core; + int local_step_limit = 0; + + vertex_moved_hashtable moved_idx; + unsigned idx = todolist.size()-1; + int overall_improvement = 0; + + while(!todolist.empty()) { + int random_idx = random_functions::nextInt(0, idx); + NodeID node = todolist[random_idx]; + + PartitionID maxgainer; + EdgeWeight extdeg = 0; + commons->compute_gain(G, node, maxgainer, extdeg); + + if(moved_idx.find(node) == moved_idx.end() && extdeg > 0) { + boundary_starting_nodes real_start_nodes; + real_start_nodes.push_back(node); + + if(init_neighbors) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(moved_idx.find(target) == moved_idx.end()) { + extdeg = 0; + commons->compute_gain(G, target, maxgainer, extdeg); + if(extdeg > 0) { + real_start_nodes.push_back(target); + } + } + } endfor +} + int improvement = 0; + if(compute_touched_blocks) { + improvement = refinement_core.single_kway_refinement_round(config, G, + boundary, real_start_nodes, + local_step_limit, moved_idx, + touched_blocks); + if(improvement < 0) { + std::cout << "buf error improvement < 0" << std::endl; + } + } else { + improvement = refinement_core.single_kway_refinement_round(config, G, + boundary, real_start_nodes, + local_step_limit, moved_idx); + if(improvement < 0) { + std::cout << "buf error improvement < 0" << std::endl; } + } - return overall_improvement; -} + overall_improvement += improvement; + + } + + if(moved_idx.size() > 0.05*G.number_of_nodes()) break; + std::swap(todolist[random_idx], todolist[idx--]); todolist.pop_back(); + } + return overall_improvement; +} +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.h index a151cd07..d38e5399 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.h @@ -13,34 +13,34 @@ #include "definitions.h" #include "kway_graph_refinement_commons.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class multitry_kway_fm { - public: - multitry_kway_fm( ); - virtual ~multitry_kway_fm(); - - int perform_refinement(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, unsigned rounds, - bool init_neighbors, unsigned alpha); - - int perform_refinement_around_parts(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, bool init_neighbors, - unsigned alpha, - PartitionID & lhs, PartitionID & rhs, - std::unordered_map & touched_blocks); - - - private: - int start_more_locallized_search(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, - bool init_neighbors, - bool compute_touched_blocks, - std::unordered_map & touched_blocks, - std::vector & todolist); - - kway_graph_refinement_commons* commons; +public: + multitry_kway_fm( ); + virtual ~multitry_kway_fm(); + + int perform_refinement(PartitionConfig & config, graph_access & G, + complete_boundary & boundary, unsigned rounds, + bool init_neighbors, unsigned alpha); + + int perform_refinement_around_parts(PartitionConfig & config, graph_access & G, + complete_boundary & boundary, bool init_neighbors, + unsigned alpha, + PartitionID & lhs, PartitionID & rhs, + std::unordered_map & touched_blocks); + + +private: + int start_more_locallized_search(PartitionConfig & config, graph_access & G, + complete_boundary & boundary, + bool init_neighbors, + bool compute_touched_blocks, + std::unordered_map & touched_blocks, + std::vector & todolist); + + kway_graph_refinement_commons* commons; }; - +} #endif /* end of include guard: MULTITRY_KWAYFM_PVGY97EW */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.cpp index 0874c8fd..69a3c9ed 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.cpp @@ -8,7 +8,7 @@ #include "label_propagation_refinement.h" #include "partition/coarsening/clustering/node_ordering.h" #include "tools/random_functions.h" - +namespace kahip::modified { label_propagation_refinement::label_propagation_refinement() { } @@ -20,138 +20,139 @@ label_propagation_refinement::~label_propagation_refinement() { EdgeWeight label_propagation_refinement::perform_refinement(PartitionConfig & partition_config, graph_access & G, complete_boundary & boundary) { - NodeWeight block_upperbound = partition_config.upper_bound_partition; - - // in this case the _matching paramter is not used - // coarse_mappng stores cluster id and the mapping (it is identical) - std::vector hash_map(G.number_of_nodes(),0); - std::vector permutation(G.number_of_nodes()); - std::vector cluster_sizes(partition_config.k, 0); - - node_ordering n_ordering; - n_ordering.order_nodes(partition_config, G, permutation); - - std::queue< NodeID > * Q = new std::queue< NodeID >(); - std::queue< NodeID > * next_Q = new std::queue< NodeID >(); - std::vector * Q_contained = new std::vector(G.number_of_nodes(), false); - std::vector * next_Q_contained = new std::vector (G.number_of_nodes(), false); - forall_nodes(G, node) { - cluster_sizes[G.getPartitionIndex(node)] += G.getNodeWeight(node); - Q->push(permutation[node]); + NodeWeight block_upperbound = partition_config.upper_bound_partition; + + // in this case the _matching paramter is not used + // coarse_mappng stores cluster id and the mapping (it is identical) + std::vector hash_map(G.number_of_nodes(),0); + std::vector permutation(G.number_of_nodes()); + std::vector cluster_sizes(partition_config.k, 0); + + node_ordering n_ordering; + n_ordering.order_nodes(partition_config, G, permutation); + + std::queue< NodeID > * Q = new std::queue< NodeID >(); + std::queue< NodeID > * next_Q = new std::queue< NodeID >(); + std::vector * Q_contained = new std::vector(G.number_of_nodes(), false); + std::vector * next_Q_contained = new std::vector (G.number_of_nodes(), false); + forall_nodes(G, node) { + cluster_sizes[G.getPartitionIndex(node)] += G.getNodeWeight(node); + Q->push(permutation[node]); + } endfor + + for( int j = 0; j < partition_config.label_iterations_refinement; j++) { + unsigned int change_counter = 0; + while( !Q->empty() ) { + NodeID node = Q->front(); + Q->pop(); + (*Q_contained)[node] = false; + + //now move the node to the cluster that is most common in the neighborhood + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + hash_map[G.getPartitionIndex(target)]+=G.getEdgeWeight(e); + //std::cout << "curblock " << G.getPartitionIndex(target) << std::endl; + } endfor + + //second sweep for finding max and resetting array + PartitionID max_block = G.getPartitionIndex(node); + PartitionID my_block = G.getPartitionIndex(node); + + PartitionID max_value = 0; + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID cur_block = G.getPartitionIndex(target); + PartitionID cur_value = hash_map[cur_block]; + if((cur_value > max_value || (cur_value == max_value && random_functions::nextBool())) + && (cluster_sizes[cur_block] + G.getNodeWeight(node) < block_upperbound || (cur_block == my_block && cluster_sizes[my_block] <= partition_config.upper_bound_partition))) + //&& (!partition_config.graph_allready_partitioned || G.getPartitionIndex(node) == G.getPartitionIndex(target)) + //&& (!partition_config.combine || G.getSecondPartitionIndex(node) == G.getSecondPartitionIndex(target))) + { + max_value = cur_value; + max_block = cur_block; + } + + hash_map[cur_block] = 0; + } endfor + + cluster_sizes[G.getPartitionIndex(node)] -= G.getNodeWeight(node); + cluster_sizes[max_block] += G.getNodeWeight(node); + bool changed_label = G.getPartitionIndex(node) != max_block; + change_counter += changed_label; + G.setPartitionIndex(node, max_block); + //std::cout << "maxblock " << max_block << std::endl; + + if(changed_label) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(!(*next_Q_contained)[target]) { + next_Q->push(target); + (*next_Q_contained)[target] = true; + } } endfor +} + } - for( int j = 0; j < partition_config.label_iterations_refinement; j++) { - unsigned int change_counter = 0; - while( !Q->empty() ) { - NodeID node = Q->front(); - Q->pop(); - (*Q_contained)[node] = false; - - //now move the node to the cluster that is most common in the neighborhood - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - hash_map[G.getPartitionIndex(target)]+=G.getEdgeWeight(e); - //std::cout << "curblock " << G.getPartitionIndex(target) << std::endl; - } endfor - - //second sweep for finding max and resetting array - PartitionID max_block = G.getPartitionIndex(node); - PartitionID my_block = G.getPartitionIndex(node); - - PartitionID max_value = 0; - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID cur_block = G.getPartitionIndex(target); - PartitionID cur_value = hash_map[cur_block]; - if((cur_value > max_value || (cur_value == max_value && random_functions::nextBool())) - && (cluster_sizes[cur_block] + G.getNodeWeight(node) < block_upperbound || (cur_block == my_block && cluster_sizes[my_block] <= partition_config.upper_bound_partition))) - //&& (!partition_config.graph_allready_partitioned || G.getPartitionIndex(node) == G.getPartitionIndex(target)) - //&& (!partition_config.combine || G.getSecondPartitionIndex(node) == G.getSecondPartitionIndex(target))) - { - max_value = cur_value; - max_block = cur_block; - } - - hash_map[cur_block] = 0; - } endfor - - cluster_sizes[G.getPartitionIndex(node)] -= G.getNodeWeight(node); - cluster_sizes[max_block] += G.getNodeWeight(node); - bool changed_label = G.getPartitionIndex(node) != max_block; - change_counter += changed_label; - G.setPartitionIndex(node, max_block); - //std::cout << "maxblock " << max_block << std::endl; - - if(changed_label) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(!(*next_Q_contained)[target]) { - next_Q->push(target); - (*next_Q_contained)[target] = true; - } - } endfor - } - } - - std::swap( Q, next_Q); - std::swap( Q_contained, next_Q_contained); + std::swap( Q, next_Q); + std::swap( Q_contained, next_Q_contained); - } - - - delete Q; - delete next_Q; - delete Q_contained; - delete next_Q_contained; - - - // in this case the _matching paramter is not used - // coarse_mappng stores cluster id and the mapping (it is identical) - //std::vector hash_map(G.number_of_nodes(),0); - //std::vector permutation(G.number_of_nodes()); - //std::vector cluster_sizes(partition_config.k,0); - - //forall_nodes(G, node) { - //cluster_sizes[G.getPartitionIndex(node)] += G.getNodeWeight(node); - //} endfor - - //random_functions::permutate_vector_fast(permutation, true); - //NodeWeight block_upperbound = partition_config.upper_bound_partition; - - //for( int j = 0; j < partition_config.label_iterations; j++) { - //forall_nodes(G, i) { - //NodeID node = permutation[i]; - ////move the node to the cluster that is most common in the neighborhood - - //forall_out_edges(G, e, node) { - //NodeID target = G.getEdgeTarget(e); - //hash_map[G.getPartitionIndex(target)]+=G.getEdgeWeight(e); - //} endfor - - ////second sweep for finding max and resetting array - //PartitionID max_block = G.getPartitionIndex(node); - //PartitionID my_block = G.getPartitionIndex(node); - - //PartitionID max_value = 0; - //forall_out_edges(G, e, node) { - //NodeID target = G.getEdgeTarget(e); - //PartitionID cur_block = G.getPartitionIndex(target); - //PartitionID cur_value = hash_map[cur_block]; - //if((cur_value > max_value || (cur_value == max_value && random_functions::nextBool())) - //&& (cluster_sizes[cur_block] + G.getNodeWeight(node) < block_upperbound || cur_block == my_block)) - //{ - //max_value = cur_value; - //max_block = cur_block; - //} - - //hash_map[cur_block] = 0; - //} endfor - //cluster_sizes[G.getPartitionIndex(node)] -= G.getNodeWeight(node); - //cluster_sizes[max_block] += G.getNodeWeight(node); - //G.setPartitionIndex(node,max_block); - //} endfor - //} - - return 0; + } + + + delete Q; + delete next_Q; + delete Q_contained; + delete next_Q_contained; + + + // in this case the _matching paramter is not used + // coarse_mappng stores cluster id and the mapping (it is identical) + //std::vector hash_map(G.number_of_nodes(),0); + //std::vector permutation(G.number_of_nodes()); + //std::vector cluster_sizes(partition_config.k,0); + + //forall_nodes(G, node) { + //cluster_sizes[G.getPartitionIndex(node)] += G.getNodeWeight(node); + //} endfor + + //random_functions::permutate_vector_fast(permutation, true); + //NodeWeight block_upperbound = partition_config.upper_bound_partition; + + //for( int j = 0; j < partition_config.label_iterations; j++) { + //forall_nodes(G, i) { + //NodeID node = permutation[i]; + ////move the node to the cluster that is most common in the neighborhood + + //forall_out_edges(G, e, node) { + //NodeID target = G.getEdgeTarget(e); + //hash_map[G.getPartitionIndex(target)]+=G.getEdgeWeight(e); + //} endfor + + ////second sweep for finding max and resetting array + //PartitionID max_block = G.getPartitionIndex(node); + //PartitionID my_block = G.getPartitionIndex(node); + + //PartitionID max_value = 0; + //forall_out_edges(G, e, node) { + //NodeID target = G.getEdgeTarget(e); + //PartitionID cur_block = G.getPartitionIndex(target); + //PartitionID cur_value = hash_map[cur_block]; + //if((cur_value > max_value || (cur_value == max_value && random_functions::nextBool())) + //&& (cluster_sizes[cur_block] + G.getNodeWeight(node) < block_upperbound || cur_block == my_block)) + //{ + //max_value = cur_value; + //max_block = cur_block; + //} + + //hash_map[cur_block] = 0; + //} endfor + //cluster_sizes[G.getPartitionIndex(node)] -= G.getNodeWeight(node); + //cluster_sizes[max_block] += G.getNodeWeight(node); + //G.setPartitionIndex(node,max_block); + //} endfor + //} + + return 0; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.h index 99c46ca1..00c6104c 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.h @@ -11,16 +11,16 @@ #include "definitions.h" #include "../refinement.h" - +namespace kahip::modified { class label_propagation_refinement : public refinement { public: - label_propagation_refinement(); - virtual ~label_propagation_refinement(); + label_propagation_refinement(); + virtual ~label_propagation_refinement(); - virtual EdgeWeight perform_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary); + virtual EdgeWeight perform_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary); }; - +} #endif /* end of include guard: LABEL_PROPAGATION_REFINEMENT_R4XW141Y */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/mixed_refinement.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/mixed_refinement.cpp index 990a2ce4..103f5287 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/mixed_refinement.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/mixed_refinement.cpp @@ -10,7 +10,7 @@ #include "kway_graph_refinement/multitry_kway_fm.h" #include "mixed_refinement.h" #include "quotient_graph_refinement/quotient_graph_refinement.h" - +namespace kahip::modified { mixed_refinement::mixed_refinement() { } @@ -20,48 +20,48 @@ mixed_refinement::~mixed_refinement() { } EdgeWeight mixed_refinement::perform_refinement(PartitionConfig & config, graph_access & G, complete_boundary & boundary) { - refinement* refine = new quotient_graph_refinement(); - refinement* kway = new kway_graph_refinement(); - multitry_kway_fm* multitry_kway = new multitry_kway_fm(); - cycle_refinement* cycle_refine = new cycle_refinement(); + refinement* refine = new quotient_graph_refinement(); + refinement* kway = new kway_graph_refinement(); + multitry_kway_fm* multitry_kway = new multitry_kway_fm(); + cycle_refinement* cycle_refine = new cycle_refinement(); - EdgeWeight overall_improvement = 0; - //call refinement - if(config.no_change_convergence) { - bool sth_changed = true; - while(sth_changed) { - EdgeWeight improvement = 0; - if(config.corner_refinement_enabled) { - improvement += kway->perform_refinement(config, G, boundary); - } + EdgeWeight overall_improvement = 0; + //call refinement + if(config.no_change_convergence) { + bool sth_changed = true; + while(sth_changed) { + EdgeWeight improvement = 0; + if(config.corner_refinement_enabled) { + improvement += kway->perform_refinement(config, G, boundary); + } - if(!config.quotient_graph_refinement_disabled) { - improvement += refine->perform_refinement(config, G, boundary); - } + if(!config.quotient_graph_refinement_disabled) { + improvement += refine->perform_refinement(config, G, boundary); + } - overall_improvement += improvement; - sth_changed = improvement != 0; - } + overall_improvement += improvement; + sth_changed = improvement != 0; + } - } else { - if(config.corner_refinement_enabled) { - overall_improvement += kway->perform_refinement(config, G, boundary); - } + } else { + if(config.corner_refinement_enabled) { + overall_improvement += kway->perform_refinement(config, G, boundary); + } - if(!config.quotient_graph_refinement_disabled) { - overall_improvement += refine->perform_refinement(config, G, boundary); - } + if(!config.quotient_graph_refinement_disabled) { + overall_improvement += refine->perform_refinement(config, G, boundary); + } - if(config.kaffpa_perfectly_balanced_refinement) { - overall_improvement += cycle_refine->perform_refinement(config, G, boundary); - } - } + if(config.kaffpa_perfectly_balanced_refinement) { + overall_improvement += cycle_refine->perform_refinement(config, G, boundary); + } + } - delete refine; - delete kway; - delete multitry_kway; - delete cycle_refine; + delete refine; + delete kway; + delete multitry_kway; + delete cycle_refine; - return overall_improvement; + return overall_improvement; +} } - diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/mixed_refinement.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/mixed_refinement.h index e746b513..636da22c 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/mixed_refinement.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/mixed_refinement.h @@ -10,16 +10,16 @@ #include "definitions.h" #include "refinement.h" - +namespace kahip::modified { class mixed_refinement : public refinement { public: - mixed_refinement( ); - virtual ~mixed_refinement(); + mixed_refinement( ); + virtual ~mixed_refinement(); - virtual EdgeWeight perform_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary); + virtual EdgeWeight perform_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary); }; - +} #endif /* end of include guard: MIXED_REFINEMENT_XJC6COP3 */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/partition_accept_rule.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/partition_accept_rule.h index 2a0e7894..158f8d4a 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/partition_accept_rule.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/partition_accept_rule.h @@ -10,49 +10,49 @@ #include "partition_config.h" #include "random_functions.h" - +namespace kahip::modified { class partition_accept_rule { - public: - partition_accept_rule( ) {}; - virtual ~partition_accept_rule() {}; +public: + partition_accept_rule( ) {}; + virtual ~partition_accept_rule() {}; - virtual bool accept_partition(PartitionConfig & config, - const EdgeWeight edge_cut, - const NodeWeight lhs_part_weight, - const NodeWeight rhs_part_weight, - const PartitionID lhs, - const PartitionID rhs, - bool & rebalance ) = 0; + virtual bool accept_partition(PartitionConfig & config, + const EdgeWeight edge_cut, + const NodeWeight lhs_part_weight, + const NodeWeight rhs_part_weight, + const PartitionID lhs, + const PartitionID rhs, + bool & rebalance ) = 0; }; class normal_partition_accept_rule : public partition_accept_rule { - public: - normal_partition_accept_rule(PartitionConfig & config, - const EdgeWeight initial_cut, - const NodeWeight initial_lhs_part_weight, - const NodeWeight initial_rhs_part_weight); - virtual ~normal_partition_accept_rule() {}; - - bool accept_partition(PartitionConfig & config, - const EdgeWeight edge_cut, - const NodeWeight lhs_part_weight, - const NodeWeight rhs_part_weight, - const PartitionID lhs, - const PartitionID rhs, - - bool & rebalance); - private: - EdgeWeight best_cut; - NodeWeight cur_lhs_part_weight; - NodeWeight cur_rhs_part_weight; - NodeWeight difference; +public: + normal_partition_accept_rule(PartitionConfig & config, + const EdgeWeight initial_cut, + const NodeWeight initial_lhs_part_weight, + const NodeWeight initial_rhs_part_weight); + virtual ~normal_partition_accept_rule() {}; + + bool accept_partition(PartitionConfig & config, + const EdgeWeight edge_cut, + const NodeWeight lhs_part_weight, + const NodeWeight rhs_part_weight, + const PartitionID lhs, + const PartitionID rhs, + + bool & rebalance); +private: + EdgeWeight best_cut; + NodeWeight cur_lhs_part_weight; + NodeWeight cur_rhs_part_weight; + NodeWeight difference; }; -normal_partition_accept_rule::normal_partition_accept_rule(PartitionConfig & config, - const EdgeWeight initial_cut, - const NodeWeight initial_lhs_part_weight, +normal_partition_accept_rule::normal_partition_accept_rule(PartitionConfig & config, + const EdgeWeight initial_cut, + const NodeWeight initial_lhs_part_weight, const NodeWeight initial_rhs_part_weight) { best_cut = initial_cut; @@ -61,72 +61,72 @@ normal_partition_accept_rule::normal_partition_accept_rule(PartitionConfig & con difference = abs((int)cur_lhs_part_weight - (int)cur_rhs_part_weight); } -bool normal_partition_accept_rule::accept_partition(PartitionConfig & config, - const EdgeWeight edge_cut, - const NodeWeight lhs_part_weight, +bool normal_partition_accept_rule::accept_partition(PartitionConfig & config, + const EdgeWeight edge_cut, + const NodeWeight lhs_part_weight, const NodeWeight rhs_part_weight, - const PartitionID lhs, - const PartitionID rhs, + const PartitionID lhs, + const PartitionID rhs, bool & rebalance) { NodeWeight cur_diff = abs((int)lhs_part_weight - (int)rhs_part_weight); bool better_cut_within_balance = edge_cut < best_cut; if(config.softrebalance) { - better_cut_within_balance = edge_cut <= best_cut; + better_cut_within_balance = edge_cut <= best_cut; } - better_cut_within_balance = better_cut_within_balance && - lhs_part_weight < config.upper_bound_partition - && rhs_part_weight < config.upper_bound_partition; + better_cut_within_balance = better_cut_within_balance && + lhs_part_weight < config.upper_bound_partition + && rhs_part_weight < config.upper_bound_partition; - if( (better_cut_within_balance - || (cur_diff < difference && edge_cut == best_cut)) + if( (better_cut_within_balance + || (cur_diff < difference && edge_cut == best_cut)) && lhs_part_weight > 0 && rhs_part_weight > 0 ) { best_cut = edge_cut; difference = cur_diff; rebalance = false; - return true; - - } else if(rebalance) { - if(cur_diff < difference - || (cur_diff <= difference && edge_cut < best_cut)) { - best_cut = edge_cut; - difference = cur_diff; - return true; - } - } - return false; + return true; + + } else if(rebalance) { + if(cur_diff < difference + || (cur_diff <= difference && edge_cut < best_cut)) { + best_cut = edge_cut; + difference = cur_diff; + return true; + } + } + return false; } class ip_partition_accept_rule : public partition_accept_rule { - public: - ip_partition_accept_rule(PartitionConfig & config, - const EdgeWeight initial_cut, - const NodeWeight initial_lhs_part_weight, - const NodeWeight initial_rhs_part_weight, - const PartitionID lhs, - const PartitionID rhs); - virtual ~ip_partition_accept_rule() {}; - - bool accept_partition(PartitionConfig & config, - const EdgeWeight edge_cut, - const NodeWeight lhs_part_weight, - const NodeWeight rhs_part_weight, - const PartitionID lhs, - const PartitionID rhs, - bool & rebalance); - private: - EdgeWeight best_cut; - int cur_lhs_overload; - int cur_rhs_overload; +public: + ip_partition_accept_rule(PartitionConfig & config, + const EdgeWeight initial_cut, + const NodeWeight initial_lhs_part_weight, + const NodeWeight initial_rhs_part_weight, + const PartitionID lhs, + const PartitionID rhs); + virtual ~ip_partition_accept_rule() {}; + + bool accept_partition(PartitionConfig & config, + const EdgeWeight edge_cut, + const NodeWeight lhs_part_weight, + const NodeWeight rhs_part_weight, + const PartitionID lhs, + const PartitionID rhs, + bool & rebalance); +private: + EdgeWeight best_cut; + int cur_lhs_overload; + int cur_rhs_overload; }; -ip_partition_accept_rule::ip_partition_accept_rule(PartitionConfig & config, - const EdgeWeight initial_cut, - const NodeWeight initial_lhs_part_weight, +ip_partition_accept_rule::ip_partition_accept_rule(PartitionConfig & config, + const EdgeWeight initial_cut, + const NodeWeight initial_lhs_part_weight, const NodeWeight initial_rhs_part_weight, - const PartitionID lhs, + const PartitionID lhs, const PartitionID rhs) { best_cut = initial_cut; @@ -134,20 +134,20 @@ ip_partition_accept_rule::ip_partition_accept_rule(PartitionConfig & config, cur_rhs_overload = std::max( (int)initial_rhs_part_weight - config.target_weights[rhs],0); } -bool ip_partition_accept_rule::accept_partition(PartitionConfig & config, - const EdgeWeight edge_cut, - const NodeWeight lhs_part_weight, +bool ip_partition_accept_rule::accept_partition(PartitionConfig & config, + const EdgeWeight edge_cut, + const NodeWeight lhs_part_weight, const NodeWeight rhs_part_weight, - const PartitionID lhs, - const PartitionID rhs, + const PartitionID lhs, + const PartitionID rhs, bool & rebalance) { bool better_cut_within_balance = edge_cut <= best_cut; int act_lhs_overload = std::max( (int)lhs_part_weight - config.target_weights[lhs],0); int act_rhs_overload = std::max( (int)rhs_part_weight - config.target_weights[rhs],0); - better_cut_within_balance = better_cut_within_balance && - act_lhs_overload == 0 && act_rhs_overload == 0; + better_cut_within_balance = better_cut_within_balance && + act_lhs_overload == 0 && act_rhs_overload == 0; if( act_rhs_overload == 0 && act_lhs_overload == 0 ) config.rebalance = false; @@ -159,20 +159,20 @@ bool ip_partition_accept_rule::accept_partition(PartitionConfig & config, cur_lhs_overload = act_lhs_overload; cur_rhs_overload = act_rhs_overload; return true; - } + } } else { - if( (better_cut_within_balance - || (act_rhs_overload + act_lhs_overload < cur_lhs_overload + cur_rhs_overload && edge_cut == best_cut)) + if( (better_cut_within_balance + || (act_rhs_overload + act_lhs_overload < cur_lhs_overload + cur_rhs_overload && edge_cut == best_cut)) && lhs_part_weight > 0 && rhs_part_weight > 0 ) { best_cut = edge_cut; cur_lhs_overload = act_lhs_overload; cur_rhs_overload = act_rhs_overload; - return true; + return true; - } + } } return false; } - +} #endif /* end of include guard: PARTITION_ACCEPT_RULE_4RXUS4P9 */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/queue_selection_strategie.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/queue_selection_strategie.h index e1634a57..8f48970e 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/queue_selection_strategie.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/queue_selection_strategie.h @@ -8,130 +8,170 @@ #ifndef QUEUESELECTIONSTRATEGIE_H_ #define QUEUESELECTIONSTRATEGIE_H_ - +namespace kahip::modified { class queue_selection_strategy { - public: - queue_selection_strategy(PartitionConfig & config) : m_config ( config ) {}; - virtual ~queue_selection_strategy() {}; - virtual void selectQueue(int lhs_part_weight, int rhs_part_weight, - PartitionID lhs, PartitionID rhs, - PartitionID & from, PartitionID & to, - refinement_pq * lhs_queue, refinement_pq * rhs_queue, - refinement_pq** from_queue, refinement_pq** to_queue) = 0; - protected: - PartitionConfig m_config; +public: + queue_selection_strategy(PartitionConfig & config) : m_config ( config ) {}; + virtual ~queue_selection_strategy() {}; + virtual void selectQueue(int lhs_part_weight, int rhs_part_weight, + PartitionID lhs, PartitionID rhs, + PartitionID & from, PartitionID & to, + refinement_pq * lhs_queue, refinement_pq * rhs_queue, + refinement_pq** from_queue, refinement_pq** to_queue) = 0; +protected: + PartitionConfig m_config; }; class queue_selection_diffusion : public queue_selection_strategy { - public: - queue_selection_diffusion(PartitionConfig & config) : queue_selection_strategy(config) {}; - inline void selectQueue(int lhs_part_weight, int rhs_part_weight, - PartitionID lhs, PartitionID rhs, - PartitionID & from, PartitionID & to, - refinement_pq * lhs_queue, refinement_pq * rhs_queue, - refinement_pq** from_queue, refinement_pq** to_queue ) { - if (lhs_part_weight > rhs_part_weight) { - *from_queue = lhs_queue; - *to_queue = rhs_queue; - from = lhs; - to = rhs; - } else { - *from_queue = rhs_queue; - *to_queue = lhs_queue; - from = rhs; - to = lhs; - } +public: + queue_selection_diffusion(PartitionConfig & config) : queue_selection_strategy(config) {}; + inline void selectQueue(int lhs_part_weight, int rhs_part_weight, + PartitionID lhs, PartitionID rhs, + PartitionID & from, PartitionID & to, + refinement_pq * lhs_queue, refinement_pq * rhs_queue, + refinement_pq** from_queue, refinement_pq** to_queue ) { + if (lhs_part_weight > rhs_part_weight) { + *from_queue = lhs_queue; + *to_queue = rhs_queue; + from = lhs; + to = rhs; + } else { + *from_queue = rhs_queue; + *to_queue = lhs_queue; + from = rhs; + to = lhs; } + } }; class queue_selection_topgain : public queue_selection_strategy { - public: - queue_selection_topgain(PartitionConfig & config) : queue_selection_strategy(config) {}; - inline void selectQueue(int lhs_part_weight, int rhs_part_weight, - PartitionID lhs, PartitionID rhs, - PartitionID & from, PartitionID & to, - refinement_pq * lhs_queue, refinement_pq * rhs_queue, - refinement_pq** from_queue, refinement_pq** to_queue ){ - - if( lhs_queue->empty() ) { - *from_queue = rhs_queue; - *to_queue = lhs_queue; - from = rhs; - to = lhs; - return; - } - if( rhs_queue->empty() ) { - *from_queue = lhs_queue; - *to_queue = rhs_queue; - from = lhs; - to = rhs; - return; - } - - Gain lhsGain = lhs_queue->maxValue(); - Gain rhsGain = rhs_queue->maxValue(); +public: + queue_selection_topgain(PartitionConfig & config) : queue_selection_strategy(config) {}; + inline void selectQueue(int lhs_part_weight, int rhs_part_weight, + PartitionID lhs, PartitionID rhs, + PartitionID & from, PartitionID & to, + refinement_pq * lhs_queue, refinement_pq * rhs_queue, + refinement_pq** from_queue, refinement_pq** to_queue ){ + + if( lhs_queue->empty() ) { + *from_queue = rhs_queue; + *to_queue = lhs_queue; + from = rhs; + to = lhs; + return; + } + if( rhs_queue->empty() ) { + *from_queue = lhs_queue; + *to_queue = rhs_queue; + from = lhs; + to = rhs; + return; + } - if(lhsGain > rhsGain){ - *from_queue = lhs_queue; - *to_queue = rhs_queue; - from = lhs; - to = rhs; - } else { - *from_queue = rhs_queue; - *to_queue = lhs_queue; - from = rhs; - to = lhs; - } + Gain lhsGain = lhs_queue->maxValue(); + Gain rhsGain = rhs_queue->maxValue(); + + if(lhsGain > rhsGain){ + *from_queue = lhs_queue; + *to_queue = rhs_queue; + from = lhs; + to = rhs; + } else { + *from_queue = rhs_queue; + *to_queue = lhs_queue; + from = rhs; + to = lhs; } + } }; class queue_selection_topgain_diffusion : public queue_selection_strategy { - public: - queue_selection_topgain_diffusion(PartitionConfig & config) : queue_selection_strategy(config) { - qdiff = new queue_selection_diffusion(m_config); - }; - - ~queue_selection_topgain_diffusion() { - delete qdiff; - }; - - inline void selectQueue(int lhs_part_weight, int rhs_part_weight, - PartitionID lhs, PartitionID rhs, - PartitionID & from, PartitionID & to, - refinement_pq * lhs_queue, refinement_pq * rhs_queue, - refinement_pq** from_queue, refinement_pq** to_queue ) { - - if( lhs_queue->empty() ) { - *from_queue = rhs_queue; - *to_queue = lhs_queue; - from = rhs; - to = lhs; - return; - } - if( rhs_queue->empty() ) { - *from_queue = lhs_queue; - *to_queue = rhs_queue; - from = lhs; - to = rhs; - return; - } +public: + queue_selection_topgain_diffusion(PartitionConfig & config) : queue_selection_strategy(config) { + qdiff = new queue_selection_diffusion(m_config); + }; + + ~queue_selection_topgain_diffusion() { + delete qdiff; + }; + + inline void selectQueue(int lhs_part_weight, int rhs_part_weight, + PartitionID lhs, PartitionID rhs, + PartitionID & from, PartitionID & to, + refinement_pq * lhs_queue, refinement_pq * rhs_queue, + refinement_pq** from_queue, refinement_pq** to_queue ) { + + if( lhs_queue->empty() ) { + *from_queue = rhs_queue; + *to_queue = lhs_queue; + from = rhs; + to = lhs; + return; + } + if( rhs_queue->empty() ) { + *from_queue = lhs_queue; + *to_queue = rhs_queue; + from = lhs; + to = rhs; + return; + } - Gain lhsGain = lhs_queue->maxValue(); - Gain rhsGain = rhs_queue->maxValue(); + Gain lhsGain = lhs_queue->maxValue(); + Gain rhsGain = rhs_queue->maxValue(); - if (lhsGain == rhsGain) { - qdiff->selectQueue(lhs_part_weight, rhs_part_weight, - lhs, rhs, - from, to, - lhs_queue, rhs_queue, - from_queue, to_queue); - - return; - } - if(lhsGain > rhsGain){ + if (lhsGain == rhsGain) { + qdiff->selectQueue(lhs_part_weight, rhs_part_weight, + lhs, rhs, + from, to, + lhs_queue, rhs_queue, + from_queue, to_queue); + + return; + } + if(lhsGain > rhsGain){ + *from_queue = lhs_queue; + *to_queue = rhs_queue; + from = lhs; + to = rhs; + } else { + *from_queue = rhs_queue; + *to_queue = lhs_queue; + from = rhs; + to = lhs; + } + } +private: + queue_selection_strategy* qdiff; +}; + +class queue_selection_diffusion_block_targets : public queue_selection_strategy { +public: + queue_selection_diffusion_block_targets(PartitionConfig & config) : queue_selection_strategy(config) { + qdiff = new queue_selection_topgain_diffusion(config); + }; + + virtual ~queue_selection_diffusion_block_targets() { + delete qdiff; + } + + inline void selectQueue(int lhs_part_weight, int rhs_part_weight, + PartitionID lhs, PartitionID rhs, + PartitionID & from, PartitionID & to, + refinement_pq * lhs_queue, refinement_pq * rhs_queue, + refinement_pq** from_queue, refinement_pq** to_queue ) { + int lhs_overload = std::max( lhs_part_weight - m_config.target_weights[0],0); + int rhs_overload = std::max( rhs_part_weight - m_config.target_weights[1],0); + if( lhs_overload == 0 && rhs_overload == 0) { + qdiff->selectQueue(lhs_part_weight, rhs_part_weight, + lhs, rhs, + from, to, + lhs_queue, rhs_queue, + from_queue, to_queue); + } else { + if (lhs_overload > rhs_overload) { *from_queue = lhs_queue; *to_queue = rhs_queue; from = lhs; @@ -143,50 +183,11 @@ class queue_selection_topgain_diffusion : public queue_selection_strategy { to = lhs; } } - private: - queue_selection_strategy* qdiff; -}; -class queue_selection_diffusion_block_targets : public queue_selection_strategy { - public: - queue_selection_diffusion_block_targets(PartitionConfig & config) : queue_selection_strategy(config) { - qdiff = new queue_selection_topgain_diffusion(config); - }; - - virtual ~queue_selection_diffusion_block_targets() { - delete qdiff; - } - - inline void selectQueue(int lhs_part_weight, int rhs_part_weight, - PartitionID lhs, PartitionID rhs, - PartitionID & from, PartitionID & to, - refinement_pq * lhs_queue, refinement_pq * rhs_queue, - refinement_pq** from_queue, refinement_pq** to_queue ) { - int lhs_overload = std::max( lhs_part_weight - m_config.target_weights[0],0); - int rhs_overload = std::max( rhs_part_weight - m_config.target_weights[1],0); - if( lhs_overload == 0 && rhs_overload == 0) { - qdiff->selectQueue(lhs_part_weight, rhs_part_weight, - lhs, rhs, - from, to, - lhs_queue, rhs_queue, - from_queue, to_queue); - } else { - if (lhs_overload > rhs_overload) { - *from_queue = lhs_queue; - *to_queue = rhs_queue; - from = lhs; - to = rhs; - } else { - *from_queue = rhs_queue; - *to_queue = lhs_queue; - from = rhs; - to = lhs; - } - } - - } + } - private: - queue_selection_strategy* qdiff; +private: + queue_selection_strategy* qdiff; }; +} #endif diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/search_stop_rule.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/search_stop_rule.h index d0c6ac81..1b459f4a 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/search_stop_rule.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/search_stop_rule.h @@ -7,25 +7,25 @@ #ifndef SEARCH_STOP_RULE_R20GH6IN #define SEARCH_STOP_RULE_R20GH6IN - +namespace kahip::modified { class stop_rule { - public: - stop_rule( ) {}; - virtual ~stop_rule() {}; +public: + stop_rule( ) {}; + virtual ~stop_rule() {}; - virtual bool search_should_stop(unsigned int min_cut_idx, - unsigned int cur_idx, - unsigned int search_limit) = 0; + virtual bool search_should_stop(unsigned int min_cut_idx, + unsigned int cur_idx, + unsigned int search_limit) = 0; }; class easy_stop_rule : public stop_rule { - public: - easy_stop_rule( ) {}; - virtual ~easy_stop_rule() {}; +public: + easy_stop_rule( ) {}; + virtual ~easy_stop_rule() {}; - bool search_should_stop(unsigned int min_cut_idx, - unsigned int cur_idx, - unsigned int search_limit); + bool search_should_stop(unsigned int min_cut_idx, + unsigned int cur_idx, + unsigned int search_limit); }; inline bool easy_stop_rule::search_should_stop(unsigned min_cut_idx, @@ -33,5 +33,5 @@ inline bool easy_stop_rule::search_should_stop(unsigned min_cut_idx, unsigned int search_limit) { return cur_idx - min_cut_idx > search_limit; } - +} #endif /* end of include guard: SEARCH_STOP_RULE_R20GH6IN */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp index cc548029..c020a741 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp @@ -15,7 +15,7 @@ #include "tools/quality_metrics.h" #include "two_way_fm.h" #include "uncoarsening/refinement/quotient_graph_refinement/partial_boundary.h" - +namespace kahip::modified { two_way_fm::two_way_fm() { } @@ -35,335 +35,335 @@ EdgeWeight two_way_fm::perform_refinement(PartitionConfig & cfg, EdgeWeight & cut, bool & something_changed) { - PartitionConfig config = cfg;//copy it since we make changes on that - if(lhs_start_nodes.size() == 0 or rhs_start_nodes.size() == 0) return 0; // nothing to refine - - quality_metrics qm; - ASSERT_NEQ(pair->lhs, pair->rhs); - ASSERT_TRUE(assert_directed_boundary_condition(G, boundary, pair->lhs, pair->rhs)); - ASSERT_EQ( cut, qm.edge_cut(G, pair->lhs, pair->rhs)); - - refinement_pq* lhs_queue = NULL; - refinement_pq* rhs_queue = NULL; - if(config.use_bucket_queues) { - EdgeWeight max_degree = G.getMaxDegree(); - lhs_queue = new bucket_pq(max_degree); - rhs_queue = new bucket_pq(max_degree); - } else { - lhs_queue = new maxNodeHeap(); - rhs_queue = new maxNodeHeap(); - } - - init_queue_with_boundary(config, G, lhs_start_nodes, lhs_queue, pair->lhs, pair->rhs); - init_queue_with_boundary(config, G, rhs_start_nodes, rhs_queue, pair->rhs, pair->lhs); - - queue_selection_strategy* topgain_queue_select = new queue_selection_topgain(config); - queue_selection_strategy* diffusion_queue_select = new queue_selection_diffusion(config); - queue_selection_strategy* diffusion_queue_select_block_target = new queue_selection_diffusion_block_targets(config); - - vertex_moved_hashtable moved_idx; - - std::vector transpositions; - - EdgeWeight inital_cut = cut; - int max_number_of_swaps = (int)(boundary.getBlockNoNodes(pair->lhs) + boundary.getBlockNoNodes(pair->rhs)); - int step_limit = (int)((config.fm_search_limit/100.0)*max_number_of_swaps); - step_limit = std::max(step_limit, 15); - int min_cut_index = -1; - - refinement_pq* from_queue = 0; - refinement_pq* to_queue = 0; - - PartitionID from = 0; - PartitionID to = 0; - - NodeWeight * from_part_weight = 0; - NodeWeight * to_part_weight = 0; - - stop_rule* st_rule = new easy_stop_rule(); - partition_accept_rule* accept_partition = NULL; - if(config.initial_bipartitioning) { - accept_partition = new ip_partition_accept_rule(config, cut,lhs_part_weight, rhs_part_weight, pair->lhs, pair->rhs); - } else { - accept_partition = new normal_partition_accept_rule(config, cut,lhs_part_weight, rhs_part_weight); - } - queue_selection_strategy* q_select; - - if(config.softrebalance || config.rebalance || config.initial_bipartitioning) { - if(config.initial_bipartitioning) { - q_select = diffusion_queue_select_block_target; - } else { - q_select = diffusion_queue_select; - } - } else { - q_select = topgain_queue_select; - } - - //roll forwards - EdgeWeight best_cut = cut; - int number_of_swaps = 0; - for(number_of_swaps = 0; number_of_swaps < max_number_of_swaps; number_of_swaps++) { - if(st_rule->search_should_stop(min_cut_index, number_of_swaps, step_limit)) break; - - if(lhs_queue->empty() && rhs_queue->empty()) { - break; - } - - q_select->selectQueue(lhs_part_weight, rhs_part_weight, - pair->lhs, pair->rhs, - from,to, - lhs_queue, rhs_queue, - &from_queue, &to_queue); - - if(!from_queue->empty()) { - Gain gain = from_queue->maxValue(); - NodeID node = from_queue->deleteMax(); - - ASSERT_TRUE(moved_idx[node].index == NOT_MOVED); - - boundary.setBlockNoNodes(from, boundary.getBlockNoNodes(from)-1); - boundary.setBlockNoNodes(to, boundary.getBlockNoNodes(to)+1); - - if(from == pair->lhs) { - from_part_weight = &lhs_part_weight; - to_part_weight = &rhs_part_weight; - } else { - from_part_weight = &rhs_part_weight; - to_part_weight = &lhs_part_weight; - } - - move_node(config, G, node, moved_idx, - from_queue, to_queue, - from, to, - pair, - from_part_weight, to_part_weight, - boundary); - - cut -= gain; - - if( accept_partition->accept_partition(config, cut, lhs_part_weight, rhs_part_weight, pair->lhs, pair->rhs, config.rebalance)) { - ASSERT_TRUE( cut <= best_cut || config.rebalance); - if( cut < best_cut ) { - something_changed = true; - } - best_cut = cut; - min_cut_index = number_of_swaps; - } - - transpositions.push_back(node); - moved_idx[node].index = MOVED; - } else { - break; - } - - } - - ASSERT_TRUE(assert_directed_boundary_condition(G, boundary, pair->lhs, pair->rhs)); - ASSERT_EQ( cut, qm.edge_cut(G, pair->lhs, pair->rhs)); - - //roll backwards - for(number_of_swaps--; number_of_swaps > min_cut_index; number_of_swaps--) { - ASSERT_TRUE(transpositions.size() > 0); - - NodeID node = transpositions.back(); - transpositions.pop_back(); - - PartitionID nodes_partition = G.getPartitionIndex(node); - - if(nodes_partition == pair->lhs) { - from_queue = lhs_queue; - to_queue = rhs_queue; - from = pair->lhs; - to = pair->rhs; - from_part_weight = &lhs_part_weight; - to_part_weight = &rhs_part_weight; - } else { - from_queue = rhs_queue; - to_queue = lhs_queue; - from = pair->rhs; - to = pair->lhs; - from_part_weight = &rhs_part_weight; - to_part_weight = &lhs_part_weight; - - } - - boundary.setBlockNoNodes(from, boundary.getBlockNoNodes(from)-1); - boundary.setBlockNoNodes(to, boundary.getBlockNoNodes(to)+1); - - move_node_back(config, G, node, moved_idx, - from_queue, to_queue, - from, to, - pair, - from_part_weight, - to_part_weight, - boundary); + PartitionConfig config = cfg;//copy it since we make changes on that + if(lhs_start_nodes.size() == 0 or rhs_start_nodes.size() == 0) return 0; // nothing to refine + + quality_metrics qm; + ASSERT_NEQ(pair->lhs, pair->rhs); + ASSERT_TRUE(assert_directed_boundary_condition(G, boundary, pair->lhs, pair->rhs)); + ASSERT_EQ( cut, qm.edge_cut(G, pair->lhs, pair->rhs)); + + refinement_pq* lhs_queue = NULL; + refinement_pq* rhs_queue = NULL; + if(config.use_bucket_queues) { + EdgeWeight max_degree = G.getMaxDegree(); + lhs_queue = new bucket_pq(max_degree); + rhs_queue = new bucket_pq(max_degree); + } else { + lhs_queue = new maxNodeHeap(); + rhs_queue = new maxNodeHeap(); + } + + init_queue_with_boundary(config, G, lhs_start_nodes, lhs_queue, pair->lhs, pair->rhs); + init_queue_with_boundary(config, G, rhs_start_nodes, rhs_queue, pair->rhs, pair->lhs); + + queue_selection_strategy* topgain_queue_select = new queue_selection_topgain(config); + queue_selection_strategy* diffusion_queue_select = new queue_selection_diffusion(config); + queue_selection_strategy* diffusion_queue_select_block_target = new queue_selection_diffusion_block_targets(config); + + vertex_moved_hashtable moved_idx; + + std::vector transpositions; + + EdgeWeight inital_cut = cut; + int max_number_of_swaps = (int)(boundary.getBlockNoNodes(pair->lhs) + boundary.getBlockNoNodes(pair->rhs)); + int step_limit = (int)((config.fm_search_limit/100.0)*max_number_of_swaps); + step_limit = std::max(step_limit, 15); + int min_cut_index = -1; + + refinement_pq* from_queue = 0; + refinement_pq* to_queue = 0; + + PartitionID from = 0; + PartitionID to = 0; + + NodeWeight * from_part_weight = 0; + NodeWeight * to_part_weight = 0; + + stop_rule* st_rule = new easy_stop_rule(); + partition_accept_rule* accept_partition = NULL; + if(config.initial_bipartitioning) { + accept_partition = new ip_partition_accept_rule(config, cut,lhs_part_weight, rhs_part_weight, pair->lhs, pair->rhs); + } else { + accept_partition = new normal_partition_accept_rule(config, cut,lhs_part_weight, rhs_part_weight); + } + queue_selection_strategy* q_select; + + if(config.softrebalance || config.rebalance || config.initial_bipartitioning) { + if(config.initial_bipartitioning) { + q_select = diffusion_queue_select_block_target; + } else { + q_select = diffusion_queue_select; + } + } else { + q_select = topgain_queue_select; + } + + //roll forwards + EdgeWeight best_cut = cut; + int number_of_swaps = 0; + for(number_of_swaps = 0; number_of_swaps < max_number_of_swaps; number_of_swaps++) { + if(st_rule->search_should_stop(min_cut_index, number_of_swaps, step_limit)) break; + + if(lhs_queue->empty() && rhs_queue->empty()) { + break; + } + + q_select->selectQueue(lhs_part_weight, rhs_part_weight, + pair->lhs, pair->rhs, + from,to, + lhs_queue, rhs_queue, + &from_queue, &to_queue); + + if(!from_queue->empty()) { + Gain gain = from_queue->maxValue(); + NodeID node = from_queue->deleteMax(); + + ASSERT_TRUE(moved_idx[node].index == NOT_MOVED); + + boundary.setBlockNoNodes(from, boundary.getBlockNoNodes(from)-1); + boundary.setBlockNoNodes(to, boundary.getBlockNoNodes(to)+1); + + if(from == pair->lhs) { + from_part_weight = &lhs_part_weight; + to_part_weight = &rhs_part_weight; + } else { + from_part_weight = &rhs_part_weight; + to_part_weight = &lhs_part_weight; + } + + move_node(config, G, node, moved_idx, + from_queue, to_queue, + from, to, + pair, + from_part_weight, to_part_weight, + boundary); + + cut -= gain; + + if( accept_partition->accept_partition(config, cut, lhs_part_weight, rhs_part_weight, pair->lhs, pair->rhs, config.rebalance)) { + ASSERT_TRUE( cut <= best_cut || config.rebalance); + if( cut < best_cut ) { + something_changed = true; } - - //clean up - cut = best_cut; - - boundary.setEdgeCut(pair, best_cut); - boundary.setBlockWeight(pair->lhs, lhs_part_weight); - boundary.setBlockWeight(pair->rhs, rhs_part_weight); - - delete lhs_queue; - delete rhs_queue; - delete topgain_queue_select; - delete diffusion_queue_select; - delete diffusion_queue_select_block_target; - delete st_rule; - delete accept_partition; - - ASSERT_EQ( cut, qm.edge_cut(G, pair->lhs, pair->rhs)); - ASSERT_TRUE(assert_directed_boundary_condition(G, boundary, pair->lhs, pair->rhs)); - ASSERT_TRUE( (int)inital_cut-(int)best_cut >= 0 || cfg.rebalance); - // the computed partition shouldnt have a edge cut which is worse than the initial one - return inital_cut-best_cut; + best_cut = cut; + min_cut_index = number_of_swaps; + } + + transpositions.push_back(node); + moved_idx[node].index = MOVED; + } else { + break; + } + + } + + ASSERT_TRUE(assert_directed_boundary_condition(G, boundary, pair->lhs, pair->rhs)); + ASSERT_EQ( cut, qm.edge_cut(G, pair->lhs, pair->rhs)); + + //roll backwards + for(number_of_swaps--; number_of_swaps > min_cut_index; number_of_swaps--) { + ASSERT_TRUE(transpositions.size() > 0); + + NodeID node = transpositions.back(); + transpositions.pop_back(); + + PartitionID nodes_partition = G.getPartitionIndex(node); + + if(nodes_partition == pair->lhs) { + from_queue = lhs_queue; + to_queue = rhs_queue; + from = pair->lhs; + to = pair->rhs; + from_part_weight = &lhs_part_weight; + to_part_weight = &rhs_part_weight; + } else { + from_queue = rhs_queue; + to_queue = lhs_queue; + from = pair->rhs; + to = pair->lhs; + from_part_weight = &rhs_part_weight; + to_part_weight = &lhs_part_weight; + + } + + boundary.setBlockNoNodes(from, boundary.getBlockNoNodes(from)-1); + boundary.setBlockNoNodes(to, boundary.getBlockNoNodes(to)+1); + + move_node_back(config, G, node, moved_idx, + from_queue, to_queue, + from, to, + pair, + from_part_weight, + to_part_weight, + boundary); + } + + //clean up + cut = best_cut; + + boundary.setEdgeCut(pair, best_cut); + boundary.setBlockWeight(pair->lhs, lhs_part_weight); + boundary.setBlockWeight(pair->rhs, rhs_part_weight); + + delete lhs_queue; + delete rhs_queue; + delete topgain_queue_select; + delete diffusion_queue_select; + delete diffusion_queue_select_block_target; + delete st_rule; + delete accept_partition; + + ASSERT_EQ( cut, qm.edge_cut(G, pair->lhs, pair->rhs)); + ASSERT_TRUE(assert_directed_boundary_condition(G, boundary, pair->lhs, pair->rhs)); + ASSERT_TRUE( (int)inital_cut-(int)best_cut >= 0 || cfg.rebalance); + // the computed partition shouldnt have a edge cut which is worse than the initial one + return inital_cut-best_cut; } -void two_way_fm::move_node(const PartitionConfig & config, +void two_way_fm::move_node(const PartitionConfig & config, graph_access & G, const NodeID & node, vertex_moved_hashtable & moved_idx, refinement_pq * from_queue, refinement_pq * to_queue, - PartitionID from, + PartitionID from, PartitionID to, - boundary_pair * pair, + boundary_pair * pair, NodeWeight * from_part_weight, NodeWeight * to_part_weight, complete_boundary & boundary) { - //move node - G.setPartitionIndex(node, to); - boundary.deleteNode(node, from, pair); - - EdgeWeight int_degree_node = 0; - EdgeWeight ext_degree_node = 0; - bool difficult_update = int_ext_degree(G, node, to, from, int_degree_node, ext_degree_node); - - - if(ext_degree_node > 0) { - boundary.insert(node, to, pair); + //move node + G.setPartitionIndex(node, to); + boundary.deleteNode(node, from, pair); + + EdgeWeight int_degree_node = 0; + EdgeWeight ext_degree_node = 0; + bool difficult_update = int_ext_degree(G, node, to, from, int_degree_node, ext_degree_node); + + + if(ext_degree_node > 0) { + boundary.insert(node, to, pair); + } + + if(difficult_update) + boundary.postMovedBoundaryNodeUpdates(node, pair, true, false); + + + NodeWeight this_nodes_weight = G.getNodeWeight(node); + (*from_part_weight) -= this_nodes_weight; + (*to_part_weight) += this_nodes_weight; + + //update neighbors + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID targets_partition = G.getPartitionIndex(target); + + if((targets_partition != from && targets_partition != to)) { + continue; + } + + EdgeWeight int_degree = 0; + EdgeWeight ext_degree = 0; + + PartitionID other_partition = targets_partition == from ? to : from; + int_ext_degree(G, target, targets_partition, other_partition, int_degree, ext_degree); + + refinement_pq * queue_to_update = 0; + if(targets_partition == from) { + queue_to_update = from_queue; + } else { + queue_to_update = to_queue; + } + + Gain gain = ext_degree - int_degree; + if(queue_to_update->contains(target)) { + if(ext_degree == 0) { + queue_to_update->deleteNode(target); + boundary.deleteNode(target, targets_partition, pair); + } else { + queue_to_update->changeKey(target, gain); + } + } else { + if(ext_degree > 0) { + if(moved_idx[target].index == NOT_MOVED) { + queue_to_update->insert(target, gain); } + boundary.insert(target, targets_partition, pair); + } else { + boundary.deleteNode(target, targets_partition, pair); + } + } - if(difficult_update) - boundary.postMovedBoundaryNodeUpdates(node, pair, true, false); - - - NodeWeight this_nodes_weight = G.getNodeWeight(node); - (*from_part_weight) -= this_nodes_weight; - (*to_part_weight) += this_nodes_weight; - - //update neighbors - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID targets_partition = G.getPartitionIndex(target); - - if((targets_partition != from && targets_partition != to)) { - continue; - } - - EdgeWeight int_degree = 0; - EdgeWeight ext_degree = 0; - - PartitionID other_partition = targets_partition == from ? to : from; - int_ext_degree(G, target, targets_partition, other_partition, int_degree, ext_degree); - - refinement_pq * queue_to_update = 0; - if(targets_partition == from) { - queue_to_update = from_queue; - } else { - queue_to_update = to_queue; - } - - Gain gain = ext_degree - int_degree; - if(queue_to_update->contains(target)) { - if(ext_degree == 0) { - queue_to_update->deleteNode(target); - boundary.deleteNode(target, targets_partition, pair); - } else { - queue_to_update->changeKey(target, gain); - } - } else { - if(ext_degree > 0) { - if(moved_idx[target].index == NOT_MOVED) { - queue_to_update->insert(target, gain); - } - boundary.insert(target, targets_partition, pair); - } else { - boundary.deleteNode(target, targets_partition, pair); - } - } - - } endfor + } endfor } -void two_way_fm::move_node_back(const PartitionConfig & config, +void two_way_fm::move_node_back(const PartitionConfig & config, graph_access & G, const NodeID & node, vertex_moved_hashtable & moved_idx, refinement_pq * from_queue, refinement_pq * to_queue, - PartitionID from, + PartitionID from, PartitionID to, - boundary_pair * pair, + boundary_pair * pair, NodeWeight * from_part_weight, NodeWeight * to_part_weight, complete_boundary & boundary) { - ASSERT_NEQ(from, to); - ASSERT_EQ(from, G.getPartitionIndex(node)); + ASSERT_NEQ(from, to); + ASSERT_EQ(from, G.getPartitionIndex(node)); - //move node - G.setPartitionIndex(node, to); - boundary.deleteNode(node, from, pair); + //move node + G.setPartitionIndex(node, to); + boundary.deleteNode(node, from, pair); - EdgeWeight int_degree_node = 0; - EdgeWeight ext_degree_node = 0; - bool update_difficult = int_ext_degree(G, node, to, from, int_degree_node, ext_degree_node); + EdgeWeight int_degree_node = 0; + EdgeWeight ext_degree_node = 0; + bool update_difficult = int_ext_degree(G, node, to, from, int_degree_node, ext_degree_node); - if(ext_degree_node > 0) { - boundary.insert(node, to, pair); - } + if(ext_degree_node > 0) { + boundary.insert(node, to, pair); + } - if(update_difficult) { - boundary.postMovedBoundaryNodeUpdates(node, pair, true, false); - } + if(update_difficult) { + boundary.postMovedBoundaryNodeUpdates(node, pair, true, false); + } - NodeWeight this_nodes_weight = G.getNodeWeight(node); - (*from_part_weight) -= this_nodes_weight; - (*to_part_weight) += this_nodes_weight; + NodeWeight this_nodes_weight = G.getNodeWeight(node); + (*from_part_weight) -= this_nodes_weight; + (*to_part_weight) += this_nodes_weight; - //update neighbors - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID targets_partition = G.getPartitionIndex(target); + //update neighbors + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID targets_partition = G.getPartitionIndex(target); - if((targets_partition != from && targets_partition != to)) { - //at most difficult update nec. - continue; //they dont need to be updated during this refinement - } + if((targets_partition != from && targets_partition != to)) { + //at most difficult update nec. + continue; //they dont need to be updated during this refinement + } - EdgeWeight int_degree = 0; - EdgeWeight ext_degree = 0; + EdgeWeight int_degree = 0; + EdgeWeight ext_degree = 0; - PartitionID other_partition = targets_partition == from ? to : from; - int_ext_degree(G, target, targets_partition, other_partition, int_degree, ext_degree); + PartitionID other_partition = targets_partition == from ? to : from; + int_ext_degree(G, target, targets_partition, other_partition, int_degree, ext_degree); - if(boundary.contains(target, targets_partition, pair)) { - if(ext_degree == 0) { - boundary.deleteNode(target, targets_partition, pair); - } - } else { - if(ext_degree > 0) { - boundary.insert(target, targets_partition, pair); - } - } + if(boundary.contains(target, targets_partition, pair)) { + if(ext_degree == 0) { + boundary.deleteNode(target, targets_partition, pair); + } + } else { + if(ext_degree > 0) { + boundary.insert(target, targets_partition, pair); + } + } - } endfor + } endfor } @@ -371,78 +371,78 @@ void two_way_fm::move_node_back(const PartitionConfig & config, void two_way_fm::init_queue_with_boundary(const PartitionConfig & config, graph_access & G, std::vector & bnd_nodes, - refinement_pq * queue, - PartitionID partition_of_boundary, + refinement_pq * queue, + PartitionID partition_of_boundary, PartitionID other) { - if(config.permutation_during_refinement == PERMUTATION_QUALITY_FAST) { - random_functions::permutate_vector_fast(bnd_nodes, false); - } else if(config.permutation_during_refinement == PERMUTATION_QUALITY_GOOD) { - random_functions::permutate_vector_good(bnd_nodes, false); - } + if(config.permutation_during_refinement == PERMUTATION_QUALITY_FAST) { + random_functions::permutate_vector_fast(bnd_nodes, false); + } else if(config.permutation_during_refinement == PERMUTATION_QUALITY_GOOD) { + random_functions::permutate_vector_good(bnd_nodes, false); + } - for( unsigned int i = 0, end = bnd_nodes.size(); i < end; i++) { - NodeID cur_bnd_node = bnd_nodes[i]; - //compute gain - EdgeWeight int_degree = 0; - EdgeWeight ext_degree = 0; + for( unsigned int i = 0, end = bnd_nodes.size(); i < end; i++) { + NodeID cur_bnd_node = bnd_nodes[i]; + //compute gain + EdgeWeight int_degree = 0; + EdgeWeight ext_degree = 0; - int_ext_degree(G, cur_bnd_node, partition_of_boundary, other, int_degree, ext_degree); + int_ext_degree(G, cur_bnd_node, partition_of_boundary, other, int_degree, ext_degree); - Gain gain = ext_degree - int_degree; - queue->insert(cur_bnd_node, gain); - ASSERT_TRUE(ext_degree > 0); - ASSERT_EQ(partition_of_boundary, G.getPartitionIndex(cur_bnd_node)); - } + Gain gain = ext_degree - int_degree; + queue->insert(cur_bnd_node, gain); + ASSERT_TRUE(ext_degree > 0); + ASSERT_EQ(partition_of_boundary, G.getPartitionIndex(cur_bnd_node)); + } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////// Assertions for this class//////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#ifndef NDEBUG -bool two_way_fm::assert_only_boundary_nodes(graph_access & G, PartialBoundary & lhs_boundary, +#ifndef NDEBUG +bool two_way_fm::assert_only_boundary_nodes(graph_access & G, PartialBoundary & lhs_boundary, PartitionID lhs, PartitionID rhs) { - forall_boundary_nodes(lhs_boundary, cur_bnd_node) { - EdgeWeight int_degree = 0; - EdgeWeight ext_degree = 0; + forall_boundary_nodes(lhs_boundary, cur_bnd_node) { + EdgeWeight int_degree = 0; + EdgeWeight ext_degree = 0; - int_ext_degree(G, cur_bnd_node, lhs, rhs, int_degree, ext_degree); + int_ext_degree(G, cur_bnd_node, lhs, rhs, int_degree, ext_degree); - ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), lhs); - ASSERT_TRUE(ext_degree > 0); - } endfor - return true; + ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), lhs); + ASSERT_TRUE(ext_degree > 0); + } endfor + return true; } -bool two_way_fm::assert_every_boundary_nodes(graph_access & G, PartialBoundary & lhs_boundary, +bool two_way_fm::assert_every_boundary_nodes(graph_access & G, PartialBoundary & lhs_boundary, PartitionID lhs, PartitionID rhs) { - forall_nodes(G, n) { - EdgeWeight int_degree = 0; - EdgeWeight ext_degree = 0; - if(G.getPartitionIndex(n) == lhs) { - int_ext_degree(G, n, lhs, rhs, int_degree, ext_degree); + forall_nodes(G, n) { + EdgeWeight int_degree = 0; + EdgeWeight ext_degree = 0; + if(G.getPartitionIndex(n) == lhs) { + int_ext_degree(G, n, lhs, rhs, int_degree, ext_degree); - if(ext_degree > 0) { - ASSERT_TRUE(lhs_boundary.contains(n)); - } - } - } endfor + if(ext_degree > 0) { + ASSERT_TRUE(lhs_boundary.contains(n)); + } + } + } endfor - return true; + return true; } -bool two_way_fm::assert_directed_boundary_condition(graph_access & G, complete_boundary & boundary, +bool two_way_fm::assert_directed_boundary_condition(graph_access & G, complete_boundary & boundary, PartitionID lhs, PartitionID rhs) { - ASSERT_TRUE(assert_only_boundary_nodes(G, boundary.getDirectedBoundary(lhs, lhs, rhs) , lhs, rhs)); - ASSERT_TRUE(assert_only_boundary_nodes(G, boundary.getDirectedBoundary(rhs, lhs, rhs) , rhs, lhs)); - ASSERT_TRUE(assert_every_boundary_nodes(G, boundary.getDirectedBoundary(lhs, lhs, rhs) , lhs, rhs)); - ASSERT_TRUE(assert_every_boundary_nodes(G, boundary.getDirectedBoundary(rhs, lhs, rhs) , rhs, lhs)); - return true; + ASSERT_TRUE(assert_only_boundary_nodes(G, boundary.getDirectedBoundary(lhs, lhs, rhs) , lhs, rhs)); + ASSERT_TRUE(assert_only_boundary_nodes(G, boundary.getDirectedBoundary(rhs, lhs, rhs) , rhs, lhs)); + ASSERT_TRUE(assert_every_boundary_nodes(G, boundary.getDirectedBoundary(lhs, lhs, rhs) , lhs, rhs)); + ASSERT_TRUE(assert_every_boundary_nodes(G, boundary.getDirectedBoundary(rhs, lhs, rhs) , rhs, lhs)); + return true; +} } - #endif diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.h index 2d48978f..3775179a 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.h @@ -18,88 +18,87 @@ #include "uncoarsening/refinement/quotient_graph_refinement/partial_boundary.h" #include "uncoarsening/refinement/quotient_graph_refinement/two_way_refinement.h" #include "vertex_moved_hashtable.h" - - +namespace kahip::modified { class two_way_fm : public two_way_refinement { - public: - two_way_fm( ); - virtual ~two_way_fm(); - EdgeWeight perform_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - std::vector & lhs_pq_start_nodes, - std::vector & rhs_pq_start_nodes, - boundary_pair * refinement_pair, - NodeWeight & lhs_part_weight, - NodeWeight & rhs_part_weight, - EdgeWeight & cut, - bool & something_changed); - - inline bool int_ext_degree(graph_access & G, - const NodeID & node, - const PartitionID lhs, - const PartitionID rhs, - EdgeWeight & int_degree, - EdgeWeight & ext_degree); - - - private: - void init_queue_with_boundary(const PartitionConfig & config, - graph_access & G, - std::vector &bnd_nodes, - refinement_pq * queue, - PartitionID partition_of_boundary, - PartitionID other); - - - void move_node(const PartitionConfig & config, - graph_access & G, - const NodeID & node, - vertex_moved_hashtable & moved_idx, - refinement_pq * from_queue, - refinement_pq * to_queue, - PartitionID from, - PartitionID to, - boundary_pair * pair, - NodeWeight * from_part_weight, - NodeWeight * to_part_weight, - complete_boundary & boundary); - - void move_node_back(const PartitionConfig & config, - graph_access & G, - const NodeID & node, - vertex_moved_hashtable & moved_idx, - refinement_pq * from_queue, - refinement_pq * to_queue, - PartitionID from, - PartitionID to, - boundary_pair * pair, - NodeWeight * from_part_weight, - NodeWeight * to_part_weight, - complete_boundary & boundary); - - - /////////////////////////////////////////////////////////////////////////// - //Assertions - /////////////////////////////////////////////////////////////////////////// +public: + two_way_fm( ); + virtual ~two_way_fm(); + EdgeWeight perform_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + std::vector & lhs_pq_start_nodes, + std::vector & rhs_pq_start_nodes, + boundary_pair * refinement_pair, + NodeWeight & lhs_part_weight, + NodeWeight & rhs_part_weight, + EdgeWeight & cut, + bool & something_changed); + + inline bool int_ext_degree(graph_access & G, + const NodeID & node, + const PartitionID lhs, + const PartitionID rhs, + EdgeWeight & int_degree, + EdgeWeight & ext_degree); + + +private: + void init_queue_with_boundary(const PartitionConfig & config, + graph_access & G, + std::vector &bnd_nodes, + refinement_pq * queue, + PartitionID partition_of_boundary, + PartitionID other); + + + void move_node(const PartitionConfig & config, + graph_access & G, + const NodeID & node, + vertex_moved_hashtable & moved_idx, + refinement_pq * from_queue, + refinement_pq * to_queue, + PartitionID from, + PartitionID to, + boundary_pair * pair, + NodeWeight * from_part_weight, + NodeWeight * to_part_weight, + complete_boundary & boundary); + + void move_node_back(const PartitionConfig & config, + graph_access & G, + const NodeID & node, + vertex_moved_hashtable & moved_idx, + refinement_pq * from_queue, + refinement_pq * to_queue, + PartitionID from, + PartitionID to, + boundary_pair * pair, + NodeWeight * from_part_weight, + NodeWeight * to_part_weight, + complete_boundary & boundary); + + + /////////////////////////////////////////////////////////////////////////// + //Assertions + /////////////////////////////////////////////////////////////////////////// #ifndef NDEBUG - //assert that every node in the lhs boundary has external degree > 0 - bool assert_only_boundary_nodes(graph_access & G, - PartialBoundary & lhs_boundary, - PartitionID lhs, + //assert that every node in the lhs boundary has external degree > 0 + bool assert_only_boundary_nodes(graph_access & G, + PartialBoundary & lhs_boundary, + PartitionID lhs, + PartitionID rhs); + + //assert that every node with ext degree > 0 is lhs boundary + bool assert_every_boundary_nodes(graph_access & G, + PartialBoundary & lhs_boundary, + PartitionID lhs, + PartitionID rhs); + + //check all of the possible compinations of the two assertions above + bool assert_directed_boundary_condition(graph_access & G, + complete_boundary & boundary, + PartitionID lhs, PartitionID rhs); - - //assert that every node with ext degree > 0 is lhs boundary - bool assert_every_boundary_nodes(graph_access & G, - PartialBoundary & lhs_boundary, - PartitionID lhs, - PartitionID rhs); - - //check all of the possible compinations of the two assertions above - bool assert_directed_boundary_condition(graph_access & G, - complete_boundary & boundary, - PartitionID lhs, - PartitionID rhs); #endif }; @@ -135,6 +134,6 @@ inline bool two_way_fm::int_ext_degree( graph_access & G, return update_is_difficult; } - +} #endif /* end of include guard: TWO_WAY_FM_YLYN82Y1 */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h index f42768ca..d399f1fa 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/vertex_moved_hashtable.h @@ -12,11 +12,11 @@ #include "definitions.h" #include "limits.h" - +namespace kahip::modified { struct compare_nodes { - bool operator()(const NodeID lhs, const NodeID rhs) const { - return (lhs == rhs); - } + bool operator()(const NodeID lhs, const NodeID rhs) const { + return (lhs == rhs); + } }; @@ -24,18 +24,18 @@ const NodeID NOT_MOVED = std::numeric_limits::max(); const NodeID MOVED = 0; struct moved_index { - NodeID index; - moved_index() { - index = NOT_MOVED; - } + NodeID index; + moved_index() { + index = NOT_MOVED; + } }; struct hash_nodes { - size_t operator()(const NodeID idx) const { - return idx; - } + size_t operator()(const NodeID idx) const { + return idx; + } }; typedef std::unordered_map vertex_moved_hashtable; - +} #endif diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/boundary_lookup.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/boundary_lookup.h index 0a6b385a..5e781d62 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/boundary_lookup.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/boundary_lookup.h @@ -13,7 +13,7 @@ #include "definitions.h" #include "limits.h" #include "partial_boundary.h" - +namespace kahip::modified { struct boundary_pair { PartitionID k; PartitionID lhs; @@ -24,7 +24,7 @@ struct boundary_pair { struct compare_boundary_pair { bool operator()(const boundary_pair pair_a, const boundary_pair pair_b) const { bool eq = (pair_a.lhs == pair_b.lhs && pair_a.rhs == pair_b.rhs); - eq = eq || (pair_a.lhs == pair_b.rhs && pair_a.rhs == pair_b.lhs); + eq = eq || (pair_a.lhs == pair_b.rhs && pair_a.rhs == pair_b.lhs); return eq; } }; @@ -54,23 +54,22 @@ struct data_boundary_pair { }; struct hash_boundary_pair_directed{ - size_t operator()(const boundary_pair pair) const { + size_t operator()(const boundary_pair pair) const { return pair.lhs*pair.k + pair.rhs; - } + } }; struct hash_boundary_pair{ - size_t operator()(const boundary_pair pair) const { - if(pair.lhs < pair.rhs) + size_t operator()(const boundary_pair pair) const { + if(pair.lhs < pair.rhs) return pair.lhs*pair.k + pair.rhs; - else + else return pair.rhs*pair.k + pair.lhs; - } + } }; typedef std::unordered_map block_pairs; - - +} #endif /* end of include guard: BOUNDARY_LOOKUP_2JMSKBSI */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.cpp index 6221c455..51c40880 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.cpp @@ -7,101 +7,102 @@ #include "complete_boundary.h" #include "quality_metrics.h" - +namespace kahip::modified { complete_boundary::complete_boundary(graph_access * G) { - m_graph_ref = G; - m_pb_lhs_lazy = 0; - m_pb_rhs_lazy = 0; - m_last_pair = 0; - m_last_key = -1; - m_block_infos.resize(G->get_partition_count()); - delete Q.graphref; - Q.graphref = NULL; + m_graph_ref = G; + m_pb_lhs_lazy = 0; + m_pb_rhs_lazy = 0; + m_last_pair = 0; + m_last_key = -1; + m_block_infos.resize(G->get_partition_count()); + delete Q.graphref; + Q.graphref = NULL; } complete_boundary::~complete_boundary() { } -void complete_boundary::postMovedBoundaryNodeUpdates(NodeID node, boundary_pair * pair, +void complete_boundary::postMovedBoundaryNodeUpdates(NodeID node, boundary_pair * pair, bool update_edge_cuts, bool update_all_boundaries) { - graph_access & G = *m_graph_ref; - PartitionID to = m_graph_ref->getPartitionIndex(node); - PartitionID from = to == pair->lhs ? pair->rhs : pair->lhs; - ASSERT_NEQ(from, to); - - //First delete this node from all incidient partition boudnary and decreas the edgecut (from, target_partition != to) - //then insert it in the right target - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID targetPartition = G.getPartitionIndex(target); - - if(update_all_boundaries || targetPartition != to ) { - //delete - boundary_pair delete_bp; - delete_bp.k = m_graph_ref->get_partition_count(); - delete_bp.lhs = from; - delete_bp.rhs = targetPartition; - - EdgeWeight edge_weight = G.getEdgeWeight(e); - if(targetPartition != from) { - deleteNode(node, from, &delete_bp); - - bool target_is_still_incident = false; - //this should only be delete if there is other incident partition - forall_out_edges(G, t_e, target) { - NodeID targets_target = G.getEdgeTarget(t_e); - NodeID targets_target_partition = G.getPartitionIndex(targets_target); - if(targets_target_partition == from) { - //since partition index of node is to it cant be node, and this edge is - //a widness that target can remain in this boundary - target_is_still_incident = true; - break; - } - } endfor + graph_access & G = *m_graph_ref; + PartitionID to = m_graph_ref->getPartitionIndex(node); + PartitionID from = to == pair->lhs ? pair->rhs : pair->lhs; + ASSERT_NEQ(from, to); + + //First delete this node from all incidient partition boudnary and decreas the edgecut (from, target_partition != to) + //then insert it in the right target + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID targetPartition = G.getPartitionIndex(target); + + if(update_all_boundaries || targetPartition != to ) { + //delete + boundary_pair delete_bp; + delete_bp.k = m_graph_ref->get_partition_count(); + delete_bp.lhs = from; + delete_bp.rhs = targetPartition; + + EdgeWeight edge_weight = G.getEdgeWeight(e); + if(targetPartition != from) { + deleteNode(node, from, &delete_bp); + + bool target_is_still_incident = false; + //this should only be delete if there is other incident partition + forall_out_edges(G, t_e, target) { + NodeID targets_target = G.getEdgeTarget(t_e); + NodeID targets_target_partition = G.getPartitionIndex(targets_target); + if(targets_target_partition == from) { + //since partition index of node is to it cant be node, and this edge is + //a widness that target can remain in this boundary + target_is_still_incident = true; + break; + } + } endfor - if(!target_is_still_incident) - deleteNode(target, targetPartition, &delete_bp); + if(!target_is_still_incident) + deleteNode(target, targetPartition, &delete_bp); - if(update_edge_cuts) { - m_pairs[delete_bp].edge_cut -= edge_weight; - } - } + if(update_edge_cuts) { + m_pairs[delete_bp].edge_cut -= edge_weight; + } + } - if(targetPartition != to) { - //insert - boundary_pair insert_bp; - insert_bp.k = m_graph_ref->get_partition_count(); - insert_bp.lhs = to; - insert_bp.rhs = targetPartition; + if(targetPartition != to) { + //insert + boundary_pair insert_bp; + insert_bp.k = m_graph_ref->get_partition_count(); + insert_bp.lhs = to; + insert_bp.rhs = targetPartition; - insert(node, to, &insert_bp); - insert(target, targetPartition, &insert_bp); + insert(node, to, &insert_bp); + insert(target, targetPartition, &insert_bp); - if(update_edge_cuts) { - m_pairs[insert_bp].edge_cut += edge_weight; - } - } - } - } endfor -} + if(update_edge_cuts) { + m_pairs[insert_bp].edge_cut += edge_weight; + } + } + } + } endfor +} void complete_boundary::balance_singletons(const PartitionConfig & config, graph_access & G) { - for( unsigned i = 0; i < m_singletons.size(); i++) { - NodeWeight min = m_block_infos[0].block_weight; - PartitionID p = 0; - for( unsigned j = 0; j < m_block_infos.size(); j++) { - if( m_block_infos[j].block_weight < min ) { - min = m_block_infos[j].block_weight; - p = j; - } - } - - NodeID node = m_singletons[i]; - if( m_block_infos[p].block_weight + G.getNodeWeight(node) <= config.upper_bound_partition) { - m_block_infos[G.getPartitionIndex(node)].block_weight -= G.getNodeWeight(node); - m_block_infos[p].block_weight += G.getNodeWeight(node); - G.setPartitionIndex(node, p); - } - } + for( unsigned i = 0; i < m_singletons.size(); i++) { + NodeWeight min = m_block_infos[0].block_weight; + PartitionID p = 0; + for( unsigned j = 0; j < m_block_infos.size(); j++) { + if( m_block_infos[j].block_weight < min ) { + min = m_block_infos[j].block_weight; + p = j; + } + } + + NodeID node = m_singletons[i]; + if( m_block_infos[p].block_weight + G.getNodeWeight(node) <= config.upper_bound_partition) { + m_block_infos[G.getPartitionIndex(node)].block_weight -= G.getNodeWeight(node); + m_block_infos[p].block_weight += G.getNodeWeight(node); + G.setPartitionIndex(node, p); + } + } } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h index 4f666b58..e651f741 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h @@ -16,7 +16,7 @@ #include "data_structure/graph_access.h" #include "partial_boundary.h" #include "partition_config.h" - +namespace kahip::modified { struct block_informations { NodeWeight block_weight; NodeID block_no_nodes; @@ -25,77 +25,77 @@ struct block_informations { typedef std::vector QuotientGraphEdges; class complete_boundary { - public: - complete_boundary(graph_access * G ); - virtual ~complete_boundary(); - - void build(); - void build_from_coarser(complete_boundary * coarser_boundary, NodeID coarser_no_nodes, CoarseMapping * cmapping); - - inline void insert(NodeID node, PartitionID insert_node_into, boundary_pair * pair); - inline bool contains(NodeID node, PartitionID partition, boundary_pair * pair); - inline void deleteNode(NodeID node, PartitionID partition, boundary_pair * pair); - void postMovedBoundaryNodeUpdates(NodeID target, boundary_pair * pair, - bool update_edge_cuts, bool update_all_boundaries); - void balance_singletons(const PartitionConfig & config, graph_access & G); - - inline NodeID size(PartitionID partition, boundary_pair * pair); - - inline NodeWeight getBlockWeight(PartitionID partition); - inline NodeWeight getBlockNoNodes(PartitionID partition); - inline EdgeWeight getEdgeCut(boundary_pair * pair); - inline EdgeWeight getEdgeCut(PartitionID lhs, PartitionID rhs); - - inline void setBlockWeight(PartitionID partition, NodeWeight weight); - inline void setBlockNoNodes(PartitionID partition, NodeID no_nodes); - inline void setEdgeCut(boundary_pair * pair, EdgeWeight edge_cut); - - inline void getQuotientGraphEdges(QuotientGraphEdges & qgraph_edges); - inline PartialBoundary& getDirectedBoundary(PartitionID partition, PartitionID lhs, PartitionID rhs); - - inline void setup_start_nodes(graph_access & G, PartitionID partition, - boundary_pair & bp, boundary_starting_nodes & start_nodes); - - inline void setup_start_nodes_around_blocks(graph_access & G, PartitionID & lhs, PartitionID & rhs, - boundary_starting_nodes & start_nodes); - - inline void setup_start_nodes_all(graph_access & G, boundary_starting_nodes & start_nodes); - - inline void get_max_norm(); - inline void getUnderlyingQuotientGraph( graph_access & qgraph ); - inline void getNeighbors(PartitionID & block, std::vector & neighbors); - - private: - //updates lazy values that the access functions need - inline void update_lazy_values(boundary_pair * pair); - - //lazy members to avoid hashtable loop ups - PartialBoundary* m_pb_lhs_lazy; - PartialBoundary* m_pb_rhs_lazy; - PartitionID m_lazy_lhs; - PartitionID m_lazy_rhs; - boundary_pair* m_last_pair; - size_t m_last_key; - hash_boundary_pair m_hbp; - - graph_access * m_graph_ref; - //implicit quotient graph structure - // - block_pairs m_pairs; - std::vector m_block_infos; - - //explicit quotient graph structure / may be outdated! - graph_access Q; - std::vector< NodeID > m_singletons; - - ////////////////////////////////////////////////////////////// - ///////// Data Structure Invariants - ////////////////////////////////////////////////////////////// +public: + complete_boundary(graph_access * G ); + virtual ~complete_boundary(); + + void build(); + void build_from_coarser(complete_boundary * coarser_boundary, NodeID coarser_no_nodes, CoarseMapping * cmapping); + + inline void insert(NodeID node, PartitionID insert_node_into, boundary_pair * pair); + inline bool contains(NodeID node, PartitionID partition, boundary_pair * pair); + inline void deleteNode(NodeID node, PartitionID partition, boundary_pair * pair); + void postMovedBoundaryNodeUpdates(NodeID target, boundary_pair * pair, + bool update_edge_cuts, bool update_all_boundaries); + void balance_singletons(const PartitionConfig & config, graph_access & G); + + inline NodeID size(PartitionID partition, boundary_pair * pair); + + inline NodeWeight getBlockWeight(PartitionID partition); + inline NodeWeight getBlockNoNodes(PartitionID partition); + inline EdgeWeight getEdgeCut(boundary_pair * pair); + inline EdgeWeight getEdgeCut(PartitionID lhs, PartitionID rhs); + + inline void setBlockWeight(PartitionID partition, NodeWeight weight); + inline void setBlockNoNodes(PartitionID partition, NodeID no_nodes); + inline void setEdgeCut(boundary_pair * pair, EdgeWeight edge_cut); + + inline void getQuotientGraphEdges(QuotientGraphEdges & qgraph_edges); + inline PartialBoundary& getDirectedBoundary(PartitionID partition, PartitionID lhs, PartitionID rhs); + + inline void setup_start_nodes(graph_access & G, PartitionID partition, + boundary_pair & bp, boundary_starting_nodes & start_nodes); + + inline void setup_start_nodes_around_blocks(graph_access & G, PartitionID & lhs, PartitionID & rhs, + boundary_starting_nodes & start_nodes); + + inline void setup_start_nodes_all(graph_access & G, boundary_starting_nodes & start_nodes); + + inline void get_max_norm(); + inline void getUnderlyingQuotientGraph( graph_access & qgraph ); + inline void getNeighbors(PartitionID & block, std::vector & neighbors); + +private: + //updates lazy values that the access functions need + inline void update_lazy_values(boundary_pair * pair); + + //lazy members to avoid hashtable loop ups + PartialBoundary* m_pb_lhs_lazy; + PartialBoundary* m_pb_rhs_lazy; + PartitionID m_lazy_lhs; + PartitionID m_lazy_rhs; + boundary_pair* m_last_pair; + size_t m_last_key; + hash_boundary_pair m_hbp; + + graph_access * m_graph_ref; + //implicit quotient graph structure + // + block_pairs m_pairs; + std::vector m_block_infos; + + //explicit quotient graph structure / may be outdated! + graph_access Q; + std::vector< NodeID > m_singletons; + + ////////////////////////////////////////////////////////////// +///////// Data Structure Invariants +////////////////////////////////////////////////////////////// #ifndef NDEBUG - public: - bool assert_bnodes_in_boundaries(); - bool assert_boundaries_are_bnodes(); -#endif +public: + bool assert_bnodes_in_boundaries(); + bool assert_boundaries_are_bnodes(); +#endif }; @@ -128,22 +128,22 @@ inline void complete_boundary::build() { bp.lhs = source_partition; bp.rhs = target_partition; update_lazy_values(&bp); - m_pairs[bp].edge_cut += G.getEdgeWeight(e); + m_pairs[bp].edge_cut += G.getEdgeWeight(e); insert(n, source_partition, &bp); } } endfor } endfor - block_pairs::iterator iter; - for(iter = m_pairs.begin(); iter != m_pairs.end(); iter++ ) { + block_pairs::iterator iter; + for(iter = m_pairs.begin(); iter != m_pairs.end(); iter++ ) { data_boundary_pair& value = iter->second; value.edge_cut /= 2; } } -inline void complete_boundary::build_from_coarser(complete_boundary * coarser_boundary, - NodeID coarser_no_nodes, +inline void complete_boundary::build_from_coarser(complete_boundary * coarser_boundary, + NodeID coarser_no_nodes, CoarseMapping * cmapping) { graph_access & G = *m_graph_ref; @@ -157,15 +157,15 @@ inline void complete_boundary::build_from_coarser(complete_boundary * coarser_bo PartitionID rhs = coarser_qgraph_edges[i].rhs; PartialBoundary& lhs_b = coarser_boundary->getDirectedBoundary(lhs, lhs, rhs); PartialBoundary& rhs_b = coarser_boundary->getDirectedBoundary(rhs, lhs, rhs); - + forall_boundary_nodes(lhs_b, n) { coarse_is_border_node[n] = true; - } endfor - + } endfor + forall_boundary_nodes(rhs_b, n) { coarse_is_border_node[n] = true; } endfor - + } for(PartitionID block = 0; block < G.get_partition_count(); block++) { @@ -183,7 +183,7 @@ inline void complete_boundary::build_from_coarser(complete_boundary * coarser_bo NodeID coarse_node = (*cmapping)[n]; if(!coarse_is_border_node[coarse_node]) continue; - + forall_out_edges(G, e, n) { NodeID targetID = G.getEdgeTarget(e); PartitionID target_partition = G.getPartitionIndex(targetID); @@ -195,7 +195,7 @@ inline void complete_boundary::build_from_coarser(complete_boundary * coarser_bo bp.lhs = source_partition; bp.rhs = target_partition; update_lazy_values(&bp); - m_pairs[bp].edge_cut += G.getEdgeWeight(e); + m_pairs[bp].edge_cut += G.getEdgeWeight(e); insert(n, source_partition, &bp); } } endfor @@ -205,8 +205,8 @@ inline void complete_boundary::build_from_coarser(complete_boundary * coarser_bo setBlockWeight(p, coarser_boundary->getBlockWeight(p)); } - block_pairs::iterator iter; - for(iter = m_pairs.begin(); iter != m_pairs.end(); iter++ ) { + block_pairs::iterator iter; + for(iter = m_pairs.begin(); iter != m_pairs.end(); iter++ ) { data_boundary_pair& value = iter->second; value.edge_cut /= 2; } @@ -214,7 +214,7 @@ inline void complete_boundary::build_from_coarser(complete_boundary * coarser_bo inline void complete_boundary::insert(NodeID node, PartitionID insert_node_into, boundary_pair * pair) { update_lazy_values(pair); - ASSERT_TRUE((m_lazy_lhs == pair->lhs && m_lazy_rhs == pair->rhs) + ASSERT_TRUE((m_lazy_lhs == pair->lhs && m_lazy_rhs == pair->rhs) || (m_lazy_lhs == pair->rhs && m_lazy_rhs == pair->lhs)); if(insert_node_into == m_lazy_lhs) { @@ -223,7 +223,7 @@ inline void complete_boundary::insert(NodeID node, PartitionID insert_node_into, } else { ASSERT_EQ(m_graph_ref->getPartitionIndex(node),m_lazy_rhs); m_pb_rhs_lazy->insert(node); - } + } } inline bool complete_boundary::contains(NodeID node, PartitionID partition, boundary_pair * pair){ @@ -234,7 +234,7 @@ inline bool complete_boundary::contains(NodeID node, PartitionID partition, boun } else { ASSERT_EQ(m_graph_ref->getPartitionIndex(node),m_lazy_rhs); return m_pb_rhs_lazy->contains(node); - } + } } inline void complete_boundary::deleteNode(NodeID node, PartitionID partition, boundary_pair * pair) { @@ -243,7 +243,7 @@ inline void complete_boundary::deleteNode(NodeID node, PartitionID partition, bo m_pb_lhs_lazy->deleteNode(node); } else { m_pb_rhs_lazy->deleteNode(node); - } + } } inline NodeID complete_boundary::size(PartitionID partition, boundary_pair * pair){ @@ -252,7 +252,7 @@ inline NodeID complete_boundary::size(PartitionID partition, boundary_pair * pai return m_pb_lhs_lazy->size(); } else { return m_pb_rhs_lazy->size(); - } + } } inline NodeWeight complete_boundary::getBlockWeight(PartitionID partition){ @@ -292,8 +292,8 @@ inline void complete_boundary::setEdgeCut(boundary_pair * pair, EdgeWeight edge_ inline void complete_boundary::getQuotientGraphEdges(QuotientGraphEdges & qgraph_edges) { //the quotient graph is stored implicitly in the pairs hashtable - block_pairs::iterator iter; - for(iter = m_pairs.begin(); iter != m_pairs.end(); iter++ ) { + block_pairs::iterator iter; + for(iter = m_pairs.begin(); iter != m_pairs.end(); iter++ ) { boundary_pair key = iter->first; qgraph_edges.push_back(key); } @@ -310,16 +310,16 @@ inline PartialBoundary& complete_boundary::getDirectedBoundary(PartitionID parti return *m_pb_lhs_lazy; } else { return *m_pb_rhs_lazy; - } + } } inline void complete_boundary::update_lazy_values(boundary_pair * pair) { ASSERT_NEQ(pair->lhs, pair->rhs); - + boundary_pair & bp = *pair; - size_t key = m_hbp(bp); + size_t key = m_hbp(bp); if(key != m_last_key) { - data_boundary_pair & dbp = m_pairs[*pair]; + data_boundary_pair & dbp = m_pairs[*pair]; if(!dbp.initialized) { m_pairs[*pair].lhs = pair->lhs; m_pairs[*pair].rhs = pair->rhs; @@ -334,9 +334,9 @@ inline void complete_boundary::update_lazy_values(boundary_pair * pair) { m_last_key = key; } } -void complete_boundary::setup_start_nodes(graph_access & G, - PartitionID partition, - boundary_pair & bp, +void complete_boundary::setup_start_nodes(graph_access & G, + PartitionID partition, + boundary_pair & bp, boundary_starting_nodes & start_nodes) { start_nodes.resize(size(partition, &bp)); @@ -353,65 +353,65 @@ void complete_boundary::setup_start_nodes(graph_access & G, } inline void complete_boundary::get_max_norm() { - QuotientGraphEdges qgraph_edges; - getQuotientGraphEdges(qgraph_edges); - double max = 0; - for( unsigned i = 0; i < qgraph_edges.size(); i++) { - boundary_pair & pair = qgraph_edges[i]; - - if( m_pairs[pair].edge_cut > max ) { - max = m_pairs[pair].edge_cut; - } - } - - std::cout << "max norm is " << max << std::endl; + QuotientGraphEdges qgraph_edges; + getQuotientGraphEdges(qgraph_edges); + double max = 0; + for( unsigned i = 0; i < qgraph_edges.size(); i++) { + boundary_pair & pair = qgraph_edges[i]; + + if( m_pairs[pair].edge_cut > max ) { + max = m_pairs[pair].edge_cut; + } + } + + std::cout << "max norm is " << max << std::endl; } inline void complete_boundary::getUnderlyingQuotientGraph( graph_access & Q_bar ) { - basicGraph * graphref = new basicGraph; - - if(Q_bar.graphref != NULL) { + basicGraph * graphref = new basicGraph; + + if(Q_bar.graphref != NULL) { delete Q_bar.graphref; - } - Q_bar.graphref = graphref; - - std::vector< std::vector< std::pair > > building_tool; - building_tool.resize(m_block_infos.size()); - - block_pairs::iterator iter; - for(iter = m_pairs.begin(); iter != m_pairs.end(); iter++ ) { - boundary_pair cur_pair = iter->first; - - std::pair qedge_lhs; - qedge_lhs.first = cur_pair.rhs; - qedge_lhs.second = m_pairs[cur_pair].edge_cut; - building_tool[cur_pair.lhs].push_back(qedge_lhs); - - std::pair qedge_rhs; - qedge_rhs.first = cur_pair.lhs; - qedge_rhs.second = m_pairs[cur_pair].edge_cut; - building_tool[cur_pair.rhs].push_back(qedge_rhs); - } - - Q_bar.start_construction(building_tool.size(), 2*m_pairs.size()); - - for( unsigned p = 0; p < building_tool.size(); p++) { - NodeID node = Q_bar.new_node(); - Q_bar.setNodeWeight(node, m_block_infos[p].block_weight); - - for( unsigned j = 0; j < building_tool[p].size(); j++) { - EdgeID e = Q_bar.new_edge(node, building_tool[p][j].first); - Q_bar.setEdgeWeight(e, building_tool[p][j].second); - } - } - - Q_bar.finish_construction(); + } + Q_bar.graphref = graphref; + + std::vector< std::vector< std::pair > > building_tool; + building_tool.resize(m_block_infos.size()); + + block_pairs::iterator iter; + for(iter = m_pairs.begin(); iter != m_pairs.end(); iter++ ) { + boundary_pair cur_pair = iter->first; + + std::pair qedge_lhs; + qedge_lhs.first = cur_pair.rhs; + qedge_lhs.second = m_pairs[cur_pair].edge_cut; + building_tool[cur_pair.lhs].push_back(qedge_lhs); + + std::pair qedge_rhs; + qedge_rhs.first = cur_pair.lhs; + qedge_rhs.second = m_pairs[cur_pair].edge_cut; + building_tool[cur_pair.rhs].push_back(qedge_rhs); + } + + Q_bar.start_construction(building_tool.size(), 2*m_pairs.size()); + + for( unsigned p = 0; p < building_tool.size(); p++) { + NodeID node = Q_bar.new_node(); + Q_bar.setNodeWeight(node, m_block_infos[p].block_weight); + + for( unsigned j = 0; j < building_tool[p].size(); j++) { + EdgeID e = Q_bar.new_edge(node, building_tool[p][j].first); + Q_bar.setEdgeWeight(e, building_tool[p][j].second); + } + } + + Q_bar.finish_construction(); } inline void complete_boundary::getNeighbors(PartitionID & block, std::vector & neighbors) { //lazy if(Q.graphref == NULL) { - getUnderlyingQuotientGraph(Q); + getUnderlyingQuotientGraph(Q); // note that the quotient graph structure currently does not get updated } @@ -421,8 +421,8 @@ inline void complete_boundary::getNeighbors(PartitionID & block, std::vector lhs_neighbors; @@ -437,7 +437,7 @@ void complete_boundary::setup_start_nodes_around_blocks(graph_access & G, PartialBoundary & partial_boundary_lhs = getDirectedBoundary(lhs, lhs, neighbor); forall_boundary_nodes(partial_boundary_lhs, cur_bnd_node) { ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), lhs); - if(allready_contained.find(cur_bnd_node) == allready_contained.end() ) { + if(allready_contained.find(cur_bnd_node) == allready_contained.end() ) { start_nodes.push_back(cur_bnd_node); allready_contained[cur_bnd_node] = true; } @@ -446,7 +446,7 @@ void complete_boundary::setup_start_nodes_around_blocks(graph_access & G, PartialBoundary & partial_boundary_neighbor = getDirectedBoundary(neighbor, lhs, neighbor); forall_boundary_nodes(partial_boundary_neighbor, cur_bnd_node) { ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), neighbor); - if(allready_contained.find(cur_bnd_node) == allready_contained.end()) { + if(allready_contained.find(cur_bnd_node) == allready_contained.end()) { start_nodes.push_back(cur_bnd_node); allready_contained[cur_bnd_node] = true; } @@ -458,7 +458,7 @@ void complete_boundary::setup_start_nodes_around_blocks(graph_access & G, PartialBoundary & partial_boundary_rhs = getDirectedBoundary(rhs, rhs, neighbor); forall_boundary_nodes(partial_boundary_rhs, cur_bnd_node) { ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), rhs); - if(allready_contained.find(cur_bnd_node) == allready_contained.end() ) { + if(allready_contained.find(cur_bnd_node) == allready_contained.end() ) { start_nodes.push_back(cur_bnd_node); allready_contained[cur_bnd_node] = true; } @@ -467,7 +467,7 @@ void complete_boundary::setup_start_nodes_around_blocks(graph_access & G, PartialBoundary & partial_boundary_neighbor = getDirectedBoundary(neighbor, rhs, neighbor); forall_boundary_nodes(partial_boundary_neighbor, cur_bnd_node) { ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), neighbor); - if(allready_contained.find(cur_bnd_node) == allready_contained.end()) { + if(allready_contained.find(cur_bnd_node) == allready_contained.end()) { start_nodes.push_back(cur_bnd_node); allready_contained[cur_bnd_node] = true; } @@ -481,16 +481,16 @@ void complete_boundary::setup_start_nodes_all(graph_access & G, boundary_startin getQuotientGraphEdges(quotient_graph_edges); std::unordered_map allready_contained; - + for( unsigned i = 0; i < quotient_graph_edges.size(); i++) { boundary_pair & ret_value = quotient_graph_edges[i]; - PartitionID lhs = ret_value.lhs; + PartitionID lhs = ret_value.lhs; PartitionID rhs = ret_value.rhs; PartialBoundary & partial_boundary_lhs = getDirectedBoundary(lhs, lhs, rhs); forall_boundary_nodes(partial_boundary_lhs, cur_bnd_node) { ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), lhs); - if(allready_contained.find(cur_bnd_node) == allready_contained.end() ) { + if(allready_contained.find(cur_bnd_node) == allready_contained.end() ) { start_nodes.push_back(cur_bnd_node); allready_contained[cur_bnd_node] = true; } @@ -499,7 +499,7 @@ void complete_boundary::setup_start_nodes_all(graph_access & G, boundary_startin PartialBoundary & partial_boundary_rhs = getDirectedBoundary(rhs, lhs, rhs); forall_boundary_nodes(partial_boundary_rhs, cur_bnd_node) { ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), rhs); - if(allready_contained.find(cur_bnd_node) == allready_contained.end()) { + if(allready_contained.find(cur_bnd_node) == allready_contained.end()) { start_nodes.push_back(cur_bnd_node); allready_contained[cur_bnd_node] = true; } @@ -535,14 +535,14 @@ inline bool complete_boundary::assert_bnodes_in_boundaries() { lhs_part_weight += G.getNodeWeight(n); lhs_no_nodes++; } else if(source_partition == rhs){ - rhs_part_weight += G.getNodeWeight(n); + rhs_part_weight += G.getNodeWeight(n); rhs_no_nodes++; } forall_out_edges(G, e, n) { NodeID targetID = G.getEdgeTarget(e); PartitionID target_partition = G.getPartitionIndex(targetID); - bool is_cut_edge = (source_partition == lhs && target_partition == rhs) + bool is_cut_edge = (source_partition == lhs && target_partition == rhs) || (source_partition == rhs && target_partition == lhs); if(is_cut_edge) { @@ -557,7 +557,7 @@ inline bool complete_boundary::assert_bnodes_in_boundaries() { ASSERT_EQ(m_block_infos[rhs].block_weight, rhs_part_weight); ASSERT_EQ(m_block_infos[lhs].block_no_nodes, lhs_no_nodes); ASSERT_EQ(m_block_infos[rhs].block_no_nodes, rhs_no_nodes); - ASSERT_EQ(m_pairs[bp].edge_cut,edge_cut/2); + ASSERT_EQ(m_pairs[bp].edge_cut,edge_cut/2); } } @@ -567,12 +567,12 @@ inline bool complete_boundary::assert_bnodes_in_boundaries() { inline bool complete_boundary::assert_boundaries_are_bnodes() { graph_access & G = *m_graph_ref; forall_nodes(G, n) { - PartitionID partition = G.getPartitionIndex(n); - forall_out_edges(G, e, n) { - NodeID target = G.getEdgeTarget(e); - PartitionID targets_partition = G.getPartitionIndex(target); + PartitionID partition = G.getPartitionIndex(n); + forall_out_edges(G, e, n) { + NodeID target = G.getEdgeTarget(e); + PartitionID targets_partition = G.getPartitionIndex(target); - if(partition != targets_partition) { + if(partition != targets_partition) { boundary_pair bp; bp.k = G.get_partition_count(); bp.lhs = partition; @@ -581,19 +581,20 @@ inline bool complete_boundary::assert_boundaries_are_bnodes() { ASSERT_TRUE(contains(n, partition, &bp)); ASSERT_TRUE(contains(target, targets_partition, &bp)); - } - } endfor - - } endfor - QuotientGraphEdges qgraph_edges; - getQuotientGraphEdges(qgraph_edges); - for( unsigned i = 0; i < qgraph_edges.size(); i++) { - boundary_pair & pair = qgraph_edges[i]; - ASSERT_NEQ(pair.lhs, pair.rhs); - } + } + } endfor + + } endfor + QuotientGraphEdges qgraph_edges; + getQuotientGraphEdges(qgraph_edges); + for( unsigned i = 0; i < qgraph_edges.size(); i++) { + boundary_pair & pair = qgraph_edges[i]; + ASSERT_NEQ(pair.lhs, pair.rhs); + } return true; } +} #endif // #ifndef NDEBUG #endif /* end of include guard: COMPLETE_BOUNDARY_URZZFDEI */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.cpp index 4338a2d7..788ed4b2 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.cpp @@ -10,7 +10,7 @@ #include "boundary_bfs.h" #include "random_functions.h" - +namespace kahip::modified { boundary_bfs::boundary_bfs() { } @@ -27,55 +27,55 @@ bool boundary_bfs::boundary_bfs_search(graph_access & G, NodeWeight & stripe_weight, bool flow_tiebreaking) { - std::queue node_queue; - std::vector deepth(G.number_of_nodes(), -1); - int cur_deepth = 0; - - if(flow_tiebreaking) { - random_functions::permutate_vector_good(start_nodes, false); - } - /*************************** - * Initialize the Queue - * *************************/ - NodeWeight accumulated_weight = 0; - for(unsigned int i = 0; i < start_nodes.size(); i++) { - node_queue.push(start_nodes[i]); - ASSERT_TRUE(G.getPartitionIndex(start_nodes[i]) == partition); - deepth[start_nodes[i]] = cur_deepth; - reached_nodes.push_back(start_nodes[i]); - accumulated_weight += G.getNodeWeight(start_nodes[i]); - } - ++cur_deepth; + std::queue node_queue; + std::vector deepth(G.number_of_nodes(), -1); + int cur_deepth = 0; + + if(flow_tiebreaking) { + random_functions::permutate_vector_good(start_nodes, false); + } + /*************************** + * Initialize the Queue + * *************************/ + NodeWeight accumulated_weight = 0; + for(unsigned int i = 0; i < start_nodes.size(); i++) { + node_queue.push(start_nodes[i]); + ASSERT_TRUE(G.getPartitionIndex(start_nodes[i]) == partition); + deepth[start_nodes[i]] = cur_deepth; + reached_nodes.push_back(start_nodes[i]); + accumulated_weight += G.getNodeWeight(start_nodes[i]); + } + ++cur_deepth; - if(accumulated_weight >= upper_bound_no_nodes) { - stripe_weight = accumulated_weight; - return false; - } - /*************************** - * Do the BFS - ***************************/ - while (!node_queue.empty()) { - if(accumulated_weight >= upper_bound_no_nodes) break; - NodeID n = node_queue.front(); - node_queue.pop(); + if(accumulated_weight >= upper_bound_no_nodes) { + stripe_weight = accumulated_weight; + return false; + } + /*************************** + * Do the BFS + ***************************/ + while (!node_queue.empty()) { + if(accumulated_weight >= upper_bound_no_nodes) break; + NodeID n = node_queue.front(); + node_queue.pop(); - if (deepth[n] == cur_deepth) { - cur_deepth++; - } - forall_out_edges(G,e,n) { - NodeID t = G.getEdgeTarget(e); - if(deepth[t] == -1 && G.getPartitionIndex(t) == partition + if (deepth[n] == cur_deepth) { + cur_deepth++; + } + forall_out_edges(G,e,n) { + NodeID t = G.getEdgeTarget(e); + if(deepth[t] == -1 && G.getPartitionIndex(t) == partition && accumulated_weight + G.getNodeWeight(t) <= upper_bound_no_nodes) { - deepth[t] = cur_deepth; - node_queue.push(t); - reached_nodes.push_back(t); - accumulated_weight += G.getNodeWeight(t); - } - } endfor - } - bool some_to_do = stripe_weight != accumulated_weight; - stripe_weight = accumulated_weight; - return some_to_do; + deepth[t] = cur_deepth; + node_queue.push(t); + reached_nodes.push_back(t); + accumulated_weight += G.getNodeWeight(t); + } + } endfor + } + bool some_to_do = stripe_weight != accumulated_weight; + stripe_weight = accumulated_weight; + return some_to_do; } - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.h index 872f9f4d..7e595d1c 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.h @@ -10,20 +10,20 @@ #include "data_structure/graph_access.h" #include "partition_config.h" - +namespace kahip::modified { class boundary_bfs { - public: - boundary_bfs( ); - virtual ~boundary_bfs(); +public: + boundary_bfs( ); + virtual ~boundary_bfs(); - bool boundary_bfs_search(graph_access & G, - std::vector & start_nodes, - PartitionID partition, - NodeWeight upper_bound_no_nodes, - std::vector & reached_nodes, - NodeWeight & stripe_weight, - bool flow_tiebreaking); + bool boundary_bfs_search(graph_access & G, + std::vector & start_nodes, + PartitionID partition, + NodeWeight upper_bound_no_nodes, + std::vector & reached_nodes, + NodeWeight & stripe_weight, + bool flow_tiebreaking); }; - +} #endif /* end of include guard: BOUNDARY_BFS_4AJLJJAB */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.cpp index 2d38ef2a..5ad0dbd2 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.cpp @@ -16,8 +16,7 @@ #include "edge_cut_flow_solver.h" #include "flow_macros.h" #include "most_balanced_minimum_cuts/most_balanced_minimum_cuts.h" - - +namespace kahip::modified { edge_cut_flow_solver::edge_cut_flow_solver() { } @@ -32,158 +31,158 @@ EdgeID edge_cut_flow_solver::regions_no_edges( graph_access & G, std::vector & outer_lhs_boundary_nodes, std::vector & outer_rhs_boundary_nodes ) { - EdgeID no_of_edges = 0; - unsigned idx = 0; - for( unsigned i = 0; i < lhs_boundary_stripe.size(); i++, idx++) { - NodeID node = lhs_boundary_stripe[i]; - bool is_outer_boundary = false; - forall_out_edges(G, e, node) { - if(G.getPartitionIndex(G.getEdgeTarget(e)) == BOUNDARY_STRIPE_NODE) no_of_edges++; - else is_outer_boundary = true; - } endfor - if(is_outer_boundary) { - outer_lhs_boundary_nodes.push_back(idx); - } - } - - for( unsigned i = 0; i < rhs_boundary_stripe.size(); i++, idx++) { - NodeID node = rhs_boundary_stripe[i]; - bool is_outer_boundary = false; - forall_out_edges(G, e, node) { - if(G.getPartitionIndex(G.getEdgeTarget(e)) == BOUNDARY_STRIPE_NODE) no_of_edges++; - else is_outer_boundary = true; - } endfor - if(is_outer_boundary) { - outer_rhs_boundary_nodes.push_back(idx); - } - } - - return no_of_edges; + EdgeID no_of_edges = 0; + unsigned idx = 0; + for( unsigned i = 0; i < lhs_boundary_stripe.size(); i++, idx++) { + NodeID node = lhs_boundary_stripe[i]; + bool is_outer_boundary = false; + forall_out_edges(G, e, node) { + if(G.getPartitionIndex(G.getEdgeTarget(e)) == BOUNDARY_STRIPE_NODE) no_of_edges++; + else is_outer_boundary = true; + } endfor + if(is_outer_boundary) { + outer_lhs_boundary_nodes.push_back(idx); + } + } + + for( unsigned i = 0; i < rhs_boundary_stripe.size(); i++, idx++) { + NodeID node = rhs_boundary_stripe[i]; + bool is_outer_boundary = false; + forall_out_edges(G, e, node) { + if(G.getPartitionIndex(G.getEdgeTarget(e)) == BOUNDARY_STRIPE_NODE) no_of_edges++; + else is_outer_boundary = true; + } endfor + if(is_outer_boundary) { + outer_rhs_boundary_nodes.push_back(idx); + } + } + + return no_of_edges; } -EdgeWeight edge_cut_flow_solver::convert_ds( const PartitionConfig & config, - graph_access & G, - PartitionID & lhs, - PartitionID & rhs, +EdgeWeight edge_cut_flow_solver::convert_ds( const PartitionConfig & config, + graph_access & G, + PartitionID & lhs, + PartitionID & rhs, std::vector & lhs_boundary_stripe, std::vector & rhs_boundary_stripe, - std::vector & new_to_old_ids, - long *n_ad, - long* m_ad, - node** nodes_ad, - arc** arcs_ad, + std::vector & new_to_old_ids, + long *n_ad, + long* m_ad, + node** nodes_ad, + arc** arcs_ad, long ** cap_ad, - node** source_ad, - node** sink_ad, + node** source_ad, + node** sink_ad, long* node_min_ad, EdgeID & no_edges_in_flow_graph) { - //should soon be refactored - #include "convert_ds_variables.h" - - //building up the graph as in parse.h of hi_pr code - NodeID idx = 0; - new_to_old_ids.resize(lhs_boundary_stripe.size() + rhs_boundary_stripe.size()); - std::unordered_map old_to_new; - for( unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { - G.setPartitionIndex(lhs_boundary_stripe[i], BOUNDARY_STRIPE_NODE); - new_to_old_ids[idx] = lhs_boundary_stripe[i]; - old_to_new[lhs_boundary_stripe[i]] = idx++ ; - } - for( unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { - G.setPartitionIndex(rhs_boundary_stripe[i], BOUNDARY_STRIPE_NODE); - new_to_old_ids[idx] = rhs_boundary_stripe[i]; - old_to_new[rhs_boundary_stripe[i]] = idx++; - } + //should soon be refactored +#include "convert_ds_variables.h" + + //building up the graph as in parse.h of hi_pr code + NodeID idx = 0; + new_to_old_ids.resize(lhs_boundary_stripe.size() + rhs_boundary_stripe.size()); + std::unordered_map old_to_new; + for( unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { + G.setPartitionIndex(lhs_boundary_stripe[i], BOUNDARY_STRIPE_NODE); + new_to_old_ids[idx] = lhs_boundary_stripe[i]; + old_to_new[lhs_boundary_stripe[i]] = idx++ ; + } + for( unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { + G.setPartitionIndex(rhs_boundary_stripe[i], BOUNDARY_STRIPE_NODE); + new_to_old_ids[idx] = rhs_boundary_stripe[i]; + old_to_new[rhs_boundary_stripe[i]] = idx++; + } + + std::vector outer_lhs_boundary; + std::vector outer_rhs_boundary; + EdgeID no_edges = regions_no_edges(G, lhs_boundary_stripe, rhs_boundary_stripe, + lhs, rhs, + outer_lhs_boundary, outer_rhs_boundary); + no_edges_in_flow_graph = no_edges; + + if(outer_lhs_boundary.size() == 0 || outer_rhs_boundary.size() == 0) return false; + n = lhs_boundary_stripe.size() + rhs_boundary_stripe.size() + 2; //+source and target + m = no_edges + outer_lhs_boundary.size() + outer_rhs_boundary.size(); + + nodes = (node*) calloc ( n+2, sizeof(node) ); + arcs = (arc*) calloc ( 2*m+1, sizeof(arc) ); + arc_tail = (long*) calloc ( 2*m, sizeof(long) ); + arc_first= (long*) calloc ( n+2, sizeof(long) ); + acap = (long*) calloc ( 2*m, sizeof(long) ); + arc_current = arcs; + + node_max = 0; + node_min = n; + + unsigned nodeoffset = 1; + source = n - 2 + nodeoffset; + sink = source+1; + idx = 0; + for( unsigned i = 0; i < lhs_boundary_stripe.size(); i++, idx++) { + NodeID node = lhs_boundary_stripe[i]; + NodeID sourceID = idx + nodeoffset; + forall_out_edges(G, e, node) { + if(G.getPartitionIndex(G.getEdgeTarget(e)) == BOUNDARY_STRIPE_NODE) { + NodeID targetID = old_to_new[G.getEdgeTarget(e)] + nodeoffset; + EdgeWeight capacity = G.getEdgeWeight(e); + tail = sourceID; + head = targetID; + cap = capacity; + + createEdge() +} + } endfor +} - std::vector outer_lhs_boundary; - std::vector outer_rhs_boundary; - EdgeID no_edges = regions_no_edges(G, lhs_boundary_stripe, rhs_boundary_stripe, - lhs, rhs, - outer_lhs_boundary, outer_rhs_boundary); - no_edges_in_flow_graph = no_edges; - - if(outer_lhs_boundary.size() == 0 || outer_rhs_boundary.size() == 0) return false; - n = lhs_boundary_stripe.size() + rhs_boundary_stripe.size() + 2; //+source and target - m = no_edges + outer_lhs_boundary.size() + outer_rhs_boundary.size(); - - nodes = (node*) calloc ( n+2, sizeof(node) ); - arcs = (arc*) calloc ( 2*m+1, sizeof(arc) ); - arc_tail = (long*) calloc ( 2*m, sizeof(long) ); - arc_first= (long*) calloc ( n+2, sizeof(long) ); - acap = (long*) calloc ( 2*m, sizeof(long) ); - arc_current = arcs; - - node_max = 0; - node_min = n; - - unsigned nodeoffset = 1; - source = n - 2 + nodeoffset; - sink = source+1; - idx = 0; - for( unsigned i = 0; i < lhs_boundary_stripe.size(); i++, idx++) { - NodeID node = lhs_boundary_stripe[i]; - NodeID sourceID = idx + nodeoffset; - forall_out_edges(G, e, node) { - if(G.getPartitionIndex(G.getEdgeTarget(e)) == BOUNDARY_STRIPE_NODE) { - NodeID targetID = old_to_new[G.getEdgeTarget(e)] + nodeoffset; - EdgeWeight capacity = G.getEdgeWeight(e); - tail = sourceID; - head = targetID; - cap = capacity; - - createEdge() - } - } endfor - } + for( unsigned i = 0; i < rhs_boundary_stripe.size(); i++, idx++) { + NodeID node = rhs_boundary_stripe[i]; + NodeID sourceID = idx + nodeoffset; + forall_out_edges(G, e, node) { + if(G.getPartitionIndex(G.getEdgeTarget(e)) == BOUNDARY_STRIPE_NODE) { + NodeID targetID = old_to_new[G.getEdgeTarget(e)] + nodeoffset; + EdgeWeight capacity = G.getEdgeWeight(e); + tail = sourceID; + head = targetID; + cap = capacity; + + createEdge() +} + } endfor +} - for( unsigned i = 0; i < rhs_boundary_stripe.size(); i++, idx++) { - NodeID node = rhs_boundary_stripe[i]; - NodeID sourceID = idx + nodeoffset; - forall_out_edges(G, e, node) { - if(G.getPartitionIndex(G.getEdgeTarget(e)) == BOUNDARY_STRIPE_NODE) { - NodeID targetID = old_to_new[G.getEdgeTarget(e)] + nodeoffset; - EdgeWeight capacity = G.getEdgeWeight(e); - tail = sourceID; - head = targetID; - cap = capacity; - - createEdge() - } - } endfor - } + //connect source and target with outer boundary nodes + long max_capacity = std::numeric_limits::max(); + for(unsigned i = 0; i < outer_lhs_boundary.size(); i++) { + NodeID targetID = outer_lhs_boundary[i]+ nodeoffset; + tail = source; + head = targetID; + cap = max_capacity; - //connect source and target with outer boundary nodes - long max_capacity = std::numeric_limits::max(); - for(unsigned i = 0; i < outer_lhs_boundary.size(); i++) { - NodeID targetID = outer_lhs_boundary[i]+ nodeoffset; - tail = source; - head = targetID; - cap = max_capacity; + createEdge() +} - createEdge() - } + for(unsigned i = 0; i < outer_rhs_boundary.size(); i++) { + NodeID sourceID = outer_rhs_boundary[i]+ nodeoffset; + tail = sourceID; + head = sink; + cap = max_capacity; - for(unsigned i = 0; i < outer_rhs_boundary.size(); i++) { - NodeID sourceID = outer_rhs_boundary[i]+ nodeoffset; - tail = sourceID; - head = sink; - cap = max_capacity; + createEdge() +} - createEdge() - } + //this is so dirty ;) +#include "linear_ordering_n_assign.h" - //this is so dirty ;) - #include "linear_ordering_n_assign.h" - - /* Thanks God! all is done */ - return true; + /* Thanks God! all is done */ + return true; } -EdgeWeight edge_cut_flow_solver::get_min_flow_max_cut(const PartitionConfig & config, - graph_access & G, - PartitionID & lhs, - PartitionID & rhs, +EdgeWeight edge_cut_flow_solver::get_min_flow_max_cut(const PartitionConfig & config, + graph_access & G, + PartitionID & lhs, + PartitionID & rhs, std::vector & lhs_boundary_stripe, std::vector & rhs_boundary_stripe, std::vector & new_to_old_ids, @@ -192,112 +191,112 @@ EdgeWeight edge_cut_flow_solver::get_min_flow_max_cut(const PartitionConfig & co NodeWeight & rhs_stripe_weight, std::vector & new_rhs_nodes) { - node *j = NULL; - int cc; - bucket *l; - - - globUpdtFreq = GLOB_UPDT_FREQ; - - EdgeID no_edges_in_flow_graph = 0; - bool do_something = convert_ds(config, G, lhs, rhs, lhs_boundary_stripe, rhs_boundary_stripe, new_to_old_ids, &n, - &m, - &nodes, - &arcs, - &cap, - &source, - &sink, - &nMin, - no_edges_in_flow_graph ); - - if(!do_something) return initial_cut; - - cc = internal_allocDS(); - if ( cc ) { fprintf ( stderr, "Allocation error\n"); exit ( 1 ); } - - internal_init(); - internal_stage_one( ); - - if(config.most_balanced_minimum_cuts) { - internal_stage_two(); - } - - /* check if mincut is saturated */ - aMax = dMax = 0; - for (l = buckets; l < buckets + n; l++) { - l->firstActive = sentinelNode; - l->firstInactive = sentinelNode; - } - internal_global_update(); - - if(!config.most_balanced_minimum_cuts) { - forAllNodes(j) { - if (j->d < n) { - new_rhs_nodes.push_back(nNode(j)-1); - } + node *j = NULL; + int cc; + bucket *l; + + + globUpdtFreq = GLOB_UPDT_FREQ; + + EdgeID no_edges_in_flow_graph = 0; + bool do_something = convert_ds(config, G, lhs, rhs, lhs_boundary_stripe, rhs_boundary_stripe, new_to_old_ids, &n, + &m, + &nodes, + &arcs, + &cap, + &source, + &sink, + &nMin, + no_edges_in_flow_graph ); + + if(!do_something) return initial_cut; + + cc = internal_allocDS(); + if ( cc ) { fprintf ( stderr, "Allocation error\n"); exit ( 1 ); } + + internal_init(); + internal_stage_one( ); + + if(config.most_balanced_minimum_cuts) { + internal_stage_two(); + } + + /* check if mincut is saturated */ + aMax = dMax = 0; + for (l = buckets; l < buckets + n; l++) { + l->firstActive = sentinelNode; + l->firstInactive = sentinelNode; + } + internal_global_update(); + + if(!config.most_balanced_minimum_cuts) { + forAllNodes(j) { + if (j->d < n) { + new_rhs_nodes.push_back(nNode(j)-1); + } + } + } else { + node *i; + node *t; + long ni, na, flow_value, back_flow_value; + arc *a; + arc *innerStopA; + + NodeID no_nodes_flow_graph = lhs_boundary_stripe.size() + rhs_boundary_stripe.size()+2; + graph_access residualGraph; + residualGraph.start_construction(no_nodes_flow_graph,2*no_edges_in_flow_graph); + + //(u.v) \in E iff (u,v) in E and f_uv < c(u,v) + // or (v,u) in E and f_vu > 0 + forAllNodes(i) { + ni = nNode(i); + NodeID node = residualGraph.new_node(); // for each node here create a new node + + if( (unsigned)(ni - 1) < new_to_old_ids.size()) { //note: unsigned has been introduced without testing + residualGraph.setNodeWeight( node, G.getNodeWeight(new_to_old_ids[ni-1])); + } + + forAllArcs(i,a) { + na = nArc(a); + if ( cap[na] > 0 ) { + flow_value = cap[na] - a->resCap; + + if( flow_value < cap[na] ) { + //create that edge + residualGraph.new_edge(node, nNode(a->head)-1); + } else { + //check wether backwards edge has positive flow + t = a->head; + arc* outarc; + bool prev_found = false; + //we cannot use the makro here because it would overwrite stopA! + for (outarc = t->first, innerStopA = (t+1)->first; outarc != innerStopA; outarc++) { + if(nNode(outarc->head) == ni) { + + back_flow_value = cap[nArc(outarc)] - outarc->resCap; + if(back_flow_value > 0) { + residualGraph.new_edge(node, nNode(a->head)-1); + break; } - } else { - node *i; - node *t; - long ni, na, flow_value, back_flow_value; - arc *a; - arc *innerStopA; - - NodeID no_nodes_flow_graph = lhs_boundary_stripe.size() + rhs_boundary_stripe.size()+2; - graph_access residualGraph; - residualGraph.start_construction(no_nodes_flow_graph,2*no_edges_in_flow_graph); - - //(u.v) \in E iff (u,v) in E and f_uv < c(u,v) - // or (v,u) in E and f_vu > 0 - forAllNodes(i) { - ni = nNode(i); - NodeID node = residualGraph.new_node(); // for each node here create a new node - - if( (unsigned)(ni - 1) < new_to_old_ids.size()) { //note: unsigned has been introduced without testing - residualGraph.setNodeWeight( node, G.getNodeWeight(new_to_old_ids[ni-1])); - } - - forAllArcs(i,a) { - na = nArc(a); - if ( cap[na] > 0 ) { - flow_value = cap[na] - a->resCap; - - if( flow_value < cap[na] ) { - //create that edge - residualGraph.new_edge(node, nNode(a->head)-1); - } else { - //check wether backwards edge has positive flow - t = a->head; - arc* outarc; - bool prev_found = false; - //we cannot use the makro here because it would overwrite stopA! - for (outarc = t->first, innerStopA = (t+1)->first; outarc != innerStopA; outarc++) { - if(nNode(outarc->head) == ni) { - - back_flow_value = cap[nArc(outarc)] - outarc->resCap; - if(back_flow_value > 0) { - residualGraph.new_edge(node, nNode(a->head)-1); - break; - } - if(prev_found) { - break; - } - prev_found = true; - } - } - - } - } - } + if(prev_found) { + break; } + prev_found = true; + } + } - residualGraph.finish_construction(); - NodeWeight average_partition_weight = ceil(config.largest_graph_weight / config.k); - NodeWeight perfect_rhs_stripe_weight = abs((int)average_partition_weight - (int)rhs_part_weight+(int) rhs_stripe_weight); - - most_balanced_minimum_cuts mbmc; - mbmc.compute_good_balanced_min_cut(residualGraph, config, perfect_rhs_stripe_weight, new_rhs_nodes); + } } - return flow; -} + } + } + + residualGraph.finish_construction(); + NodeWeight average_partition_weight = ceil(config.largest_graph_weight / config.k); + NodeWeight perfect_rhs_stripe_weight = abs((int)average_partition_weight - (int)rhs_part_weight+(int) rhs_stripe_weight); + most_balanced_minimum_cuts mbmc; + mbmc.compute_good_balanced_min_cut(residualGraph, config, perfect_rhs_stripe_weight, new_rhs_nodes); + } + return flow; +} +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.h index 45f7c6d6..53007ac0 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.h @@ -9,54 +9,53 @@ #define EDGE_FLOW_SOLVER_4P49OMM #include "flow_solver.h" - +namespace kahip::modified { class edge_cut_flow_solver : public flow_solver { - public: - edge_cut_flow_solver( ); - virtual ~edge_cut_flow_solver(); - - EdgeWeight get_min_flow_max_cut(const PartitionConfig & config, - graph_access & G, - PartitionID & lhs, - PartitionID & rhs, - std::vector & lhs_boundary_stripe, - std::vector & rhs_boundary_stripe, - std::vector & new_to_old_ids, - EdgeWeight & initial_cut, - NodeWeight & rhs_part_weight, - NodeWeight & rhs_stripe_weight, - std::vector & new_rhs_nodes); - - EdgeID regions_no_edges(graph_access & G, - std::vector & lhs_boundary_stripe, - std::vector & rhs_boundary_stripe, - PartitionID & lhs, - PartitionID & rhs, - std::vector & outer_lhs_boundary_nodes, - std::vector & outer_rhs_boundary_nodes ); - - - //modified parse code from hi_pr - EdgeWeight convert_ds(const PartitionConfig & config, - graph_access & G, - PartitionID & lhs, - PartitionID & rhs, - std::vector & lhs_boundary_stripe, - std::vector & rhs_boundary_stripe, - std::vector & new_to_old_ids, - long *n_ad, - long* m_ad, - node** nodes_ad, - arc** arcs_ad, - long ** cap_ad, - node** source_ad, - node** sink_ad, - long* node_min_ad, - EdgeID & no_edge_in_flow_graph); +public: + edge_cut_flow_solver( ); + virtual ~edge_cut_flow_solver(); + + EdgeWeight get_min_flow_max_cut(const PartitionConfig & config, + graph_access & G, + PartitionID & lhs, + PartitionID & rhs, + std::vector & lhs_boundary_stripe, + std::vector & rhs_boundary_stripe, + std::vector & new_to_old_ids, + EdgeWeight & initial_cut, + NodeWeight & rhs_part_weight, + NodeWeight & rhs_stripe_weight, + std::vector & new_rhs_nodes); + + EdgeID regions_no_edges(graph_access & G, + std::vector & lhs_boundary_stripe, + std::vector & rhs_boundary_stripe, + PartitionID & lhs, + PartitionID & rhs, + std::vector & outer_lhs_boundary_nodes, + std::vector & outer_rhs_boundary_nodes ); + + + //modified parse code from hi_pr + EdgeWeight convert_ds(const PartitionConfig & config, + graph_access & G, + PartitionID & lhs, + PartitionID & rhs, + std::vector & lhs_boundary_stripe, + std::vector & rhs_boundary_stripe, + std::vector & new_to_old_ids, + long *n_ad, + long* m_ad, + node** nodes_ad, + arc** arcs_ad, + long ** cap_ad, + node** source_ad, + node** sink_ad, + long* node_min_ad, + EdgeID & no_edge_in_flow_graph); }; - - +} #endif /* end of include guard: FLOW_SOLVER_4P49OMM */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_macros.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_macros.h index 1d234a14..d1049f17 100755 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_macros.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_macros.h @@ -34,8 +34,6 @@ #define nNode( i ) ( (i) - nodes + nMin ) #define nArc( a ) ( ( a == NULL )? -1 : (a) - arcs ) -#define min( a, b ) ( ( (a) < (b) ) ? a : b ) - #define createEdge()\ {\ arc_first[tail + 1] ++; \ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.cpp index 37131c6c..34c4040b 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.cpp @@ -14,430 +14,429 @@ #include #include #include -#include +#include #include #include "flow_solver.h" #include "flow_macros.h" #include "most_balanced_minimum_cuts/most_balanced_minimum_cuts.h" - - +namespace kahip::modified { flow_solver::flow_solver() { - pushCnt = 0; /* number of pushes */ - relabelCnt = 0; /* number of internal_relabels */ - updateCnt = 0; /* number of updates */ - gapCnt = 0; /* number of internal_gaps */ - gNodeCnt = 0; /* number of nodes after internal_gap */ - workSinceUpdate = 0; /* the number of arc scans since last update */ - nodes = NULL; - arcs = NULL; - cap = NULL; - buckets = NULL; - free_nodes = NULL; + pushCnt = 0; /* number of pushes */ + relabelCnt = 0; /* number of internal_relabels */ + updateCnt = 0; /* number of updates */ + gapCnt = 0; /* number of internal_gaps */ + gNodeCnt = 0; /* number of nodes after internal_gap */ + workSinceUpdate = 0; /* the number of arc scans since last update */ + nodes = NULL; + arcs = NULL; + cap = NULL; + buckets = NULL; + free_nodes = NULL; } flow_solver::~flow_solver() { - free(arcs); - free(cap); - free(buckets); - free(free_nodes); + free(arcs); + free(cap); + free(buckets); + free(free_nodes); } void flow_solver::internal_stage_one() { - node *i; - bucket *l; /* current bucket */ + node *i; + bucket *l; /* current bucket */ #if defined(INIT_UPDATE) || defined(OLD_INIT) || defined(WAVE_INIT) - internal_global_update (); + internal_global_update (); #endif - workSinceUpdate = 0; + workSinceUpdate = 0; #ifdef WAVE_INIT - internal_wave(); -#endif + internal_wave(); +#endif - /* main loop */ - while ( aMax >= aMin ) { - l = buckets + aMax; - i = l->firstActive; + /* main loop */ + while ( aMax >= aMin ) { + l = buckets + aMax; + i = l->firstActive; - if (i == sentinelNode) { - aMax--; - } - else { - aRemove(l,i); - assert(i->excess > 0); - internal_discharge (i); - - if (aMax < aMin) - break; - - /* is it time for global update? */ - if (workSinceUpdate * globUpdtFreq > nm) { - internal_global_update (); - workSinceUpdate = 0; - } + if (i == sentinelNode) { + aMax--; + } + else { + aRemove(l,i); + assert(i->excess > 0); + internal_discharge (i); - } + if (aMax < aMin) + break; + + /* is it time for global update? */ + if (workSinceUpdate * globUpdtFreq > nm) { + internal_global_update (); + workSinceUpdate = 0; + } + + } - } /* end of the main loop */ + } /* end of the main loop */ - flow = sink -> excess; -} + flow = sink -> excess; +} void flow_solver::internal_stage_two() { - node *i, *j, *tos, *bos, *restart, *r; - arc *a; - cType delta; - - /* deal with self-loops */ - forAllNodes(i) { - forAllArcs(i,a) - if ( a -> head == i ) { - a -> resCap = cap[a - arcs]; + node *i, *j, *tos, *bos, *restart, *r; + arc *a; + cType delta; + + /* deal with self-loops */ + forAllNodes(i) { + forAllArcs(i,a) + if ( a -> head == i ) { + a -> resCap = cap[a - arcs]; + } + } + + /* initialize */ + tos = bos = NULL; + forAllNodes(i) { + i -> d = WHITE; + // buckets[i-nodes].firstActive = NULL; + buckets[i-nodes].firstActive = sentinelNode; + i -> current = i -> first; + } + + /* eliminate flow cycles, topologicaly order vertices */ + forAllNodes(i) + if (( i -> d == WHITE ) && ( i -> excess > 0 ) && + ( i != source ) && ( i != sink )) { + r = i; + r -> d = GREY; + do { + for ( ; i->current != (i+1)->first; i->current++) { + a = i -> current; + if (( cap[a - arcs] == 0 ) && ( a -> resCap > 0 )) { + j = a -> head; + if ( j -> d == WHITE ) { + /* start scanning j */ + j -> d = GREY; + buckets[j-nodes].firstActive = i; + i = j; + break; + } + else + if ( j -> d == GREY ) { + /* find minimum flow on the cycle */ + delta = a -> resCap; + while ( 1 ) { + delta = std::min( delta, j -> current -> resCap ); + if ( j == i ) + break; + else + j = j -> current -> head; + } + + /* remove delta flow units */ + j = i; + while ( 1 ) { + a = j -> current; + a -> resCap -= delta; + a -> rev -> resCap += delta; + j = a -> head; + if ( j == i ) + break; + } + + /* backup DFS to the first saturated arc */ + restart = i; + for ( j = i -> current -> head; j != i; j = a -> head ) { + a = j -> current; + if (( j -> d == WHITE ) || ( a -> resCap == 0 )) { + j -> current -> head -> d = WHITE; + if ( j -> d != WHITE ) + restart = j; } - } + } - /* initialize */ - tos = bos = NULL; - forAllNodes(i) { - i -> d = WHITE; - // buckets[i-nodes].firstActive = NULL; - buckets[i-nodes].firstActive = sentinelNode; - i -> current = i -> first; - } - - /* eliminate flow cycles, topologicaly order vertices */ - forAllNodes(i) - if (( i -> d == WHITE ) && ( i -> excess > 0 ) && - ( i != source ) && ( i != sink )) { - r = i; - r -> d = GREY; - do { - for ( ; i->current != (i+1)->first; i->current++) { - a = i -> current; - if (( cap[a - arcs] == 0 ) && ( a -> resCap > 0 )) { - j = a -> head; - if ( j -> d == WHITE ) { - /* start scanning j */ - j -> d = GREY; - buckets[j-nodes].firstActive = i; - i = j; - break; - } - else - if ( j -> d == GREY ) { - /* find minimum flow on the cycle */ - delta = a -> resCap; - while ( 1 ) { - delta = min ( delta, j -> current -> resCap ); - if ( j == i ) - break; - else - j = j -> current -> head; - } - - /* remove delta flow units */ - j = i; - while ( 1 ) { - a = j -> current; - a -> resCap -= delta; - a -> rev -> resCap += delta; - j = a -> head; - if ( j == i ) - break; - } - - /* backup DFS to the first saturated arc */ - restart = i; - for ( j = i -> current -> head; j != i; j = a -> head ) { - a = j -> current; - if (( j -> d == WHITE ) || ( a -> resCap == 0 )) { - j -> current -> head -> d = WHITE; - if ( j -> d != WHITE ) - restart = j; - } - } - - if ( restart != i ) { - i = restart; - i->current++; - break; - } - } - } - } - - if (i->current == (i+1)->first) { - /* scan of i complete */ - i -> d = BLACK; - if ( i != source ) { - if ( bos == NULL ) { - bos = i; - tos = i; - } - else { - i -> bNext = tos; - tos = i; - } - } - - if ( i != r ) { - i = buckets[i-nodes].firstActive; - i->current++; - } - else - break; - } - } while ( 1 ); + if ( restart != i ) { + i = restart; + i->current++; + break; + } + } } - - - /* return excesses */ - /* note that sink is not on the stack */ - if ( bos != NULL ) { - for ( i = tos; i != bos; i = i -> bNext ) { - a = i -> first; - while ( i -> excess > 0 ) { - if (( cap[a - arcs] == 0 ) && ( a -> resCap > 0 )) { - if (a->resCap < i->excess) - delta = a->resCap; - else - delta = i->excess; - a -> resCap -= delta; - a -> rev -> resCap += delta; - i -> excess -= delta; - a -> head -> excess += delta; - } - a++; - } + } + + if (i->current == (i+1)->first) { + /* scan of i complete */ + i -> d = BLACK; + if ( i != source ) { + if ( bos == NULL ) { + bos = i; + tos = i; + } + else { + i -> bNext = tos; + tos = i; + } } - /* now do the bottom */ - i = bos; - a = i -> first; - while ( i -> excess > 0 ) { - if (( cap[a - arcs] == 0 ) && ( a -> resCap > 0 )) { - if (a->resCap < i->excess) - delta = a->resCap; - else - delta = i->excess; - a -> resCap -= delta; - a -> rev -> resCap += delta; - i -> excess -= delta; - a -> head -> excess += delta; - } - a++; + + if ( i != r ) { + i = buckets[i-nodes].firstActive; + i->current++; } + else + break; + } + } while ( 1 ); + } + + + /* return excesses */ + /* note that sink is not on the stack */ + if ( bos != NULL ) { + for ( i = tos; i != bos; i = i -> bNext ) { + a = i -> first; + while ( i -> excess > 0 ) { + if (( cap[a - arcs] == 0 ) && ( a -> resCap > 0 )) { + if (a->resCap < i->excess) + delta = a->resCap; + else + delta = i->excess; + a -> resCap -= delta; + a -> rev -> resCap += delta; + i -> excess -= delta; + a -> head -> excess += delta; } + a++; + } + } + /* now do the bottom */ + i = bos; + a = i -> first; + while ( i -> excess > 0 ) { + if (( cap[a - arcs] == 0 ) && ( a -> resCap > 0 )) { + if (a->resCap < i->excess) + delta = a->resCap; + else + delta = i->excess; + a -> resCap -= delta; + a -> rev -> resCap += delta; + i -> excess -= delta; + a -> head -> excess += delta; + } + a++; + } + } } void flow_solver::internal_global_update() { - node *i, *j; /* node pointers */ - arc *a; /* current arc pointers */ - bucket *l, *jL; /* bucket */ - long curDist, jD; - long state; - - - updateCnt ++; - - /* initialization */ - - forAllNodes(i) - i -> d = n; - sink -> d = 0; - - for (l = buckets; l <= buckets + dMax; l++) { - l -> firstActive = sentinelNode; - l -> firstInactive = sentinelNode; + node *i, *j; /* node pointers */ + arc *a; /* current arc pointers */ + bucket *l, *jL; /* bucket */ + long curDist, jD; + long state; + + + updateCnt ++; + + /* initialization */ + + forAllNodes(i) + i -> d = n; + sink -> d = 0; + + for (l = buckets; l <= buckets + dMax; l++) { + l -> firstActive = sentinelNode; + l -> firstInactive = sentinelNode; + } + + dMax = aMax = 0; + aMin = n; + + /* breadth first search */ + + // add sink to bucket zero + + iAdd(buckets, sink); + for (curDist = 0; 1; curDist++) { + + state = 0; + l = buckets + curDist; + jD = curDist + 1; + jL = l + 1; + /* + jL -> firstActive = sentinelNode; + jL -> firstInactive = sentinelNode; + */ + + if ((l->firstActive == sentinelNode) && + (l->firstInactive == sentinelNode)) + break; + + while (1) { + + switch (state) { + case 0: + i = l->firstInactive; + state = 1; + break; + case 1: + i = i->bNext; + break; + case 2: + i = l->firstActive; + state = 3; + break; + case 3: + i = i->bNext; + break; + default: + assert(0); + break; + } + + if (i == sentinelNode) { + if (state == 1) { + state = 2; + continue; } - - dMax = aMax = 0; - aMin = n; - - /* breadth first search */ - - // add sink to bucket zero - - iAdd(buckets, sink); - for (curDist = 0; 1; curDist++) { - - state = 0; - l = buckets + curDist; - jD = curDist + 1; - jL = l + 1; - /* - jL -> firstActive = sentinelNode; - jL -> firstInactive = sentinelNode; - */ - - if ((l->firstActive == sentinelNode) && - (l->firstInactive == sentinelNode)) - break; - - while (1) { - - switch (state) { - case 0: - i = l->firstInactive; - state = 1; - break; - case 1: - i = i->bNext; - break; - case 2: - i = l->firstActive; - state = 3; - break; - case 3: - i = i->bNext; - break; - default: - assert(0); - break; - } - - if (i == sentinelNode) { - if (state == 1) { - state = 2; - continue; - } - else { - assert(state == 3); - break; - } - } - - /* scanning arcs incident to node i */ - forAllArcs(i,a) { - if (a->rev->resCap > 0 ) { - j = a->head; - if (j->d == n) { - j->d = jD; - j->current = j->first; - if (jD > dMax) dMax = jD; - - if (j->excess > 0) { - /* put into active list */ - aAdd(jL,j); - } - else { - /* put into inactive list */ - iAdd(jL,j); - } - } - } - } /* node i is scanned */ - } + else { + assert(state == 3); + break; } + } + + /* scanning arcs incident to node i */ + forAllArcs(i,a) { + if (a->rev->resCap > 0 ) { + j = a->head; + if (j->d == n) { + j->d = jD; + j->current = j->first; + if (jD > dMax) dMax = jD; + + if (j->excess > 0) { + /* put into active list */ + aAdd(jL,j); + } + else { + /* put into inactive list */ + iAdd(jL,j); + } + } + } + } /* node i is scanned */ + } + } } /* end of global update */ void flow_solver::internal_check_max() { - bucket *l; + bucket *l; - for (l = buckets + dMax + 1; l < buckets + n; l++) { - assert(l->firstActive == sentinelNode); - assert(l->firstInactive == sentinelNode); - } + for (l = buckets + dMax + 1; l < buckets + n; l++) { + assert(l->firstActive == sentinelNode); + assert(l->firstInactive == sentinelNode); + } } void flow_solver::internal_init( ) { - node *i; /* current node */ - int overflowDetected; - bucket *l; - arc *a; + node *i; /* current node */ + int overflowDetected; + bucket *l; + arc *a; #ifdef EXCESS_TYPE_LONG - double testExcess; + double testExcess; #endif #ifndef OLD_INIT - unsigned long delta; + unsigned long delta; #endif - // initialize excesses + // initialize excesses - forAllNodes(i) { - i->excess = 0; - i->current = i->first; - forAllArcs(i, a) - a->resCap = cap[a-arcs]; - } + forAllNodes(i) { + i->excess = 0; + i->current = i->first; + forAllArcs(i, a) + a->resCap = cap[a-arcs]; + } - for (l = buckets; l <= buckets + n-1; l++) { - l -> firstActive = sentinelNode; - l -> firstInactive = sentinelNode; - } + for (l = buckets; l <= buckets + n-1; l++) { + l -> firstActive = sentinelNode; + l -> firstInactive = sentinelNode; + } - overflowDetected = 0; + overflowDetected = 0; #ifdef EXCESS_TYPE_LONG - testExcess = 0; - forAllArcs(source,a) { - if (a->head != source) { - testExcess += a->resCap; - } - } - if (testExcess > MAXLONG) { - printf("c WARNING: excess overflow. See README for details.\nc\n"); - overflowDetected = 1; - } + testExcess = 0; + forAllArcs(source,a) { + if (a->head != source) { + testExcess += a->resCap; + } + } + if (testExcess > MAXLONG) { + printf("c WARNING: excess overflow. See README for details.\nc\n"); + overflowDetected = 1; + } #endif #ifdef OLD_INIT - source -> excess = MAXLONG; + source -> excess = MAXLONG; #else - if (overflowDetected) { - source -> excess = MAXLONG; - } - else { - source->excess = 0; - forAllArcs(source,a) { - if (a->head != source) { - pushCnt ++; - delta = a -> resCap; - a -> resCap -= delta; - (a -> rev) -> resCap += delta; - a->head->excess += delta; - } - } - } - - /* setup labels and buckets */ - l = buckets + 1; - - aMax = 0; - aMin = n; - - forAllNodes(i) { - if (i == sink) { - i->d = 0; - iAdd(buckets,i); - continue; - } - if ((i == source) && (!overflowDetected)) { - i->d = n; - } - else - i->d = 1; - if (i->excess > 0) { - /* put into active list */ - aAdd(l,i); - } - else { /* i -> excess == 0 */ - /* put into inactive list */ - if (i->d < n) - iAdd(l,i); - } - } - dMax = 1; + if (overflowDetected) { + source -> excess = MAXLONG; + } + else { + source->excess = 0; + forAllArcs(source,a) { + if (a->head != source) { + pushCnt ++; + delta = a -> resCap; + a -> resCap -= delta; + (a -> rev) -> resCap += delta; + a->head->excess += delta; + } + } + } + + /* setup labels and buckets */ + l = buckets + 1; + + aMax = 0; + aMin = n; + + forAllNodes(i) { + if (i == sink) { + i->d = 0; + iAdd(buckets,i); + continue; + } + if ((i == source) && (!overflowDetected)) { + i->d = n; + } + else + i->d = 1; + if (i->excess > 0) { + /* put into active list */ + aAdd(l,i); + } + else { /* i -> excess == 0 */ + /* put into inactive list */ + if (i->d < n) + iAdd(l,i); + } + } + dMax = 1; #endif } /* end of init */ @@ -445,20 +444,20 @@ void flow_solver::internal_init( ) int flow_solver::internal_allocDS( ) { - nm = ALPHA * n + m; - /* - queue = (node**) calloc ( n, sizeof (node*) ); - if ( queue == NULL ) return ( 1 ); - qLast = queue + n - 1; - qInit(); - */ - buckets = (bucket*) calloc ( n+2, sizeof (bucket) ); - if ( buckets == NULL ) return ( 1 ); + nm = ALPHA * n + m; + /* + queue = (node**) calloc ( n, sizeof (node*) ); + if ( queue == NULL ) return ( 1 ); + qLast = queue + n - 1; + qInit(); + */ + buckets = (bucket*) calloc ( n+2, sizeof (bucket) ); + if ( buckets == NULL ) return ( 1 ); - sentinelNode = nodes + n; - sentinelNode->first = arcs + 2*m; + sentinelNode = nodes + n; + sentinelNode->first = arcs + 2*m; - return ( 0 ); + return ( 0 ); } /* end of allocate */ @@ -467,39 +466,39 @@ int flow_solver::internal_allocDS( ) int flow_solver::internal_gap (bucket* emptyB) { - bucket *l; - node *i; - long r; /* index of the bucket before l */ - int cc; /* cc = 1 if no nodes with positive excess before - the internal_gap */ - - gapCnt ++; - r = ( emptyB - buckets ) - 1; - - /* set labels of nodes beyond the internal_gap to "infinity" */ - for ( l = emptyB + 1; l <= buckets + dMax; l ++ ) { - /* this does nothing for high level selection - for (i = l -> firstActive; i != sentinelNode; i = i -> bNext) { - i -> d = n; - gNodeCnt++; - } - l -> firstActive = sentinelNode; - */ - - for ( i = l -> firstInactive; i != sentinelNode; i = i -> bNext ) { - i -> d = n; - gNodeCnt ++; - } + bucket *l; + node *i; + long r; /* index of the bucket before l */ + int cc; /* cc = 1 if no nodes with positive excess before + the internal_gap */ - l -> firstInactive = sentinelNode; - } + gapCnt ++; + r = ( emptyB - buckets ) - 1; + + /* set labels of nodes beyond the internal_gap to "infinity" */ + for ( l = emptyB + 1; l <= buckets + dMax; l ++ ) { + /* this does nothing for high level selection + for (i = l -> firstActive; i != sentinelNode; i = i -> bNext) { + i -> d = n; + gNodeCnt++; + } + l -> firstActive = sentinelNode; + */ + + for ( i = l -> firstInactive; i != sentinelNode; i = i -> bNext ) { + i -> d = n; + gNodeCnt ++; + } - cc = ( aMin > r ) ? 1 : 0; + l -> firstInactive = sentinelNode; + } - dMax = r; - aMax = r; + cc = ( aMin > r ) ? 1 : 0; - return ( cc ); + dMax = r; + aMax = r; + + return ( cc ); } @@ -508,43 +507,43 @@ int flow_solver::internal_gap (bucket* emptyB) long flow_solver::internal_relabel (node *i) { - node *j; - long minD; /* minimum d of a node reachable from i */ - arc *minA; /* an arc which leads to the node with minimal d */ - arc *a; + node *j; + long minD; /* minimum d of a node reachable from i */ + arc *minA; /* an arc which leads to the node with minimal d */ + arc *a; - assert(i->excess > 0); + assert(i->excess > 0); - relabelCnt++; - workSinceUpdate += BETA; + relabelCnt++; + workSinceUpdate += BETA; - i->d = minD = n; - minA = NULL; + i->d = minD = n; + minA = NULL; - /* find the minimum */ - forAllArcs(i,a) { - workSinceUpdate++; - if (a -> resCap > 0) { - j = a -> head; - if (j->d < minD) { - minD = j->d; - minA = a; - } - } - } + /* find the minimum */ + forAllArcs(i,a) { + workSinceUpdate++; + if (a -> resCap > 0) { + j = a -> head; + if (j->d < minD) { + minD = j->d; + minA = a; + } + } + } - minD++; + minD++; - if (minD < n) { + if (minD < n) { - i->d = minD; - i->current = minA; + i->d = minD; + i->current = minA; - if (dMax < minD) dMax = minD; + if (dMax < minD) dMax = minD; - } /* end of minD < n */ + } /* end of minD < n */ - return ( minD ); + return ( minD ); } /* end of internal_relabel */ @@ -554,93 +553,93 @@ long flow_solver::internal_relabel (node *i) void flow_solver::internal_discharge (node* i) { - node *j; /* sucsessor of i */ - long jD; /* d of the next bucket */ - bucket *lj; /* j's bucket */ - bucket *l; /* i's bucket */ - arc *a; /* current arc (i,j) */ - cType delta; - arc *stopA; - - assert(i->excess > 0); - assert(i != sink); - do { - - jD = i->d - 1; - l = buckets + i->d; - - /* scanning arcs outgoing from i */ - for (a = i->current, stopA = (i+1)->first; a != stopA; a++) { - if (a -> resCap > 0) { - j = a -> head; - - if (j->d == jD) { - pushCnt ++; - if (a->resCap < i->excess) - delta = a->resCap; - else - delta = i->excess; - a->resCap -= delta; - a->rev->resCap += delta; - - if (j != sink) { - - lj = buckets + jD; - - if (j->excess == 0) { - /* remove j from inactive list */ - iDelete(lj,j); - /* add j to active list */ - aAdd(lj,j); - } - } - - j -> excess += delta; - i -> excess -= delta; - - if (i->excess == 0) break; - - } /* j belongs to the next bucket */ - } /* a is not saturated */ - } /* end of scanning arcs from i */ - - if (a == stopA) { - /* i must be internal_relabeled */ - internal_relabel (i); - - if (i->d == n) break; - if ((l -> firstActive == sentinelNode) && - (l -> firstInactive == sentinelNode) - ) - internal_gap (l); - - if (i->d == n) break; - } - else { - /* i no longer active */ - i->current = a; - /* put i on inactive list */ - iAdd(l,i); - break; - } - } while (1); + node *j; /* sucsessor of i */ + long jD; /* d of the next bucket */ + bucket *lj; /* j's bucket */ + bucket *l; /* i's bucket */ + arc *a; /* current arc (i,j) */ + cType delta; + arc *stopA; + + assert(i->excess > 0); + assert(i != sink); + do { + + jD = i->d - 1; + l = buckets + i->d; + + /* scanning arcs outgoing from i */ + for (a = i->current, stopA = (i+1)->first; a != stopA; a++) { + if (a -> resCap > 0) { + j = a -> head; + + if (j->d == jD) { + pushCnt ++; + if (a->resCap < i->excess) + delta = a->resCap; + else + delta = i->excess; + a->resCap -= delta; + a->rev->resCap += delta; + + if (j != sink) { + + lj = buckets + jD; + + if (j->excess == 0) { + /* remove j from inactive list */ + iDelete(lj,j); + /* add j to active list */ + aAdd(lj,j); + } + } + + j -> excess += delta; + i -> excess -= delta; + + if (i->excess == 0) break; + + } /* j belongs to the next bucket */ + } /* a is not saturated */ + } /* end of scanning arcs from i */ + + if (a == stopA) { + /* i must be internal_relabeled */ + internal_relabel (i); + + if (i->d == n) break; + if ((l -> firstActive == sentinelNode) && + (l -> firstInactive == sentinelNode) + ) + internal_gap (l); + + if (i->d == n) break; + } + else { + /* i no longer active */ + i->current = a; + /* put i on inactive list */ + iAdd(l,i); + break; + } + } while (1); } // go from higher to lower buckets, push flow void flow_solver::internal_wave() { - node *i; - bucket *l; + node *i; + bucket *l; - for (l = buckets + aMax; l > buckets; l--) { - for (i = l->firstActive; i != sentinelNode; i = l->firstActive) { - aRemove(l,i); + for (l = buckets + aMax; l > buckets; l--) { + for (i = l->firstActive; i != sentinelNode; i = l->firstActive) { + aRemove(l,i); - assert(i->excess > 0); - internal_discharge (i); + assert(i->excess > 0); + internal_discharge (i); - } - } + } + } } - +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.h index d2ffbd6e..518eebf4 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.h @@ -11,57 +11,57 @@ #include "data_structure/graph_access.h" #include "partition_config.h" #include "types.h" - +namespace kahip::modified { class flow_solver { - public: - flow_solver( ); - virtual ~flow_solver(); +public: + flow_solver( ); + virtual ~flow_solver(); - //************************************************************************************************* - //code copied from hi_pr. make this local variables so that we can run the flow code multiple times - //************************************************************************************************* - void internal_stage_one ( ); - void internal_stage_two ( ); + //************************************************************************************************* + //code copied from hi_pr. make this local variables so that we can run the flow code multiple times + //************************************************************************************************* + void internal_stage_one ( ); + void internal_stage_two ( ); - void internal_global_update(); - void internal_check_max(); - void internal_init( ); - int internal_allocDS( ); - void internal_wave(); - void internal_discharge(node* i); - long internal_relabel(node *i); - int internal_gap(bucket* emptyB); + void internal_global_update(); + void internal_check_max(); + void internal_init( ); + int internal_allocDS( ); + void internal_wave(); + void internal_discharge(node* i); + long internal_relabel(node *i); + int internal_gap(bucket* emptyB); - long n; /* number of nodes */ - long m; /* number of arcs */ - long nm; /* n + ALPHA * m */ - long nMin; /* smallest node id */ - node *nodes; /*[> array of nodes <]*/ - node *free_nodes; /*[> array of nodes <]*/ - arc *arcs; /* array of arcs */ - bucket *buckets; /* array of buckets */ - cType *cap; /* array of capacities */ - node *source; /* source node pointer */ - node *sink; /* sink node pointer */ - long dMax; /* maximum label */ - long aMax; /* maximum actie node label */ - long aMin; /* minimum active node label */ - double flow; /* flow value */ - long pushCnt; /* number of pushes */ - long relabelCnt; /* number of relabels */ - long updateCnt; /* number of updates */ - long gapCnt; /* number of gaps */ - long gNodeCnt; /* number of nodes after gap */ - float t, t2; /* for saving times */ - node *sentinelNode; /* end of the node list marker */ - arc *stopA; /* used in forAllArcs */ - long workSinceUpdate; /* the number of arc scans since last update */ - float globUpdtFreq; /* global update frequency */ + long n; /* number of nodes */ + long m; /* number of arcs */ + long nm; /* n + ALPHA * m */ + long nMin; /* smallest node id */ + node *nodes; /*[> array of nodes <]*/ + node *free_nodes; /*[> array of nodes <]*/ + arc *arcs; /* array of arcs */ + bucket *buckets; /* array of buckets */ + cType *cap; /* array of capacities */ + node *source; /* source node pointer */ + node *sink; /* sink node pointer */ + long dMax; /* maximum label */ + long aMax; /* maximum actie node label */ + long aMin; /* minimum active node label */ + double flow; /* flow value */ + long pushCnt; /* number of pushes */ + long relabelCnt; /* number of relabels */ + long updateCnt; /* number of updates */ + long gapCnt; /* number of gaps */ + long gNodeCnt; /* number of nodes after gap */ + float t, t2; /* for saving times */ + node *sentinelNode; /* end of the node list marker */ + arc *stopA; /* used in forAllArcs */ + long workSinceUpdate; /* the number of arc scans since last update */ + float globUpdtFreq; /* global update frequency */ - long i_dist; - node *i_next, *i_prev; + long i_dist; + node *i_next, *i_prev; }; - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.cpp index 2f4bb9fa..65acb4b8 100755 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "timer.h" - +namespace kahip::modified { float timer () { struct rusage r; @@ -14,6 +14,6 @@ float timer () getrusage(0, &r); return (float)(r.ru_utime.tv_sec+r.ru_utime.tv_usec/(float)1000000); } - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.h index b65b61db..0c1a2616 100755 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.h @@ -10,7 +10,7 @@ #include #include - +namespace kahip::modified { float timer (); - +} #endif diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/types.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/types.h index e36ac59a..169813ff 100755 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/types.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/types.h @@ -8,7 +8,7 @@ #ifndef TYPES_FIK18Y5X #define TYPES_FIK18Y5X - +namespace kahip::modified { #ifdef EXCESS_TYPE_LONG typedef unsigned long excessType; #else @@ -25,15 +25,15 @@ typedef /* arc */ struct nodeSt *head; /* arc head */ struct arcSt *rev; /* reverse arc */ } - arc; +arc; typedef /* node */ struct nodeSt { arc *first; /* first outgoing arc */ arc *current; /* current outgoing arc */ - excessType excess; /* excess at the node - change to double if needed */ + excessType excess; /* excess at the node + change to double if needed */ long d; /* distance label */ struct nodeSt *bNext; /* next node in bucket */ struct nodeSt *bPrev; /* previous node in bucket */ @@ -43,8 +43,8 @@ typedef /* node */ typedef /* bucket */ struct bucketSt { - node *firstActive; /* first node with positive excess */ - node *firstInactive; /* first node with zero excess */ + node *firstActive; /* first node with positive excess */ + node *firstInactive; /* first node with zero excess */ } bucket; - +} #endif /* end of include guard: TYPES_FIK18Y5X */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.cpp index 34d12366..f476c7c0 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.cpp @@ -10,7 +10,7 @@ #include "algorithms/strongly_connected_components.h" #include "algorithms/topological_sort.h" #include "most_balanced_minimum_cuts.h" - +namespace kahip::modified { most_balanced_minimum_cuts::most_balanced_minimum_cuts() { } @@ -24,147 +24,147 @@ void most_balanced_minimum_cuts::compute_good_balanced_min_cut( graph_access & r NodeWeight & perfect_rhs_weight, std::vector< NodeID > & new_rhs_nodes ) { - strongly_connected_components scc; - std::vector components(residualGraph.number_of_nodes()); - int comp_count = scc.strong_components(residualGraph,components); - - std::vector< std::vector > comp_nodes(comp_count); - std::vector< NodeWeight > comp_weights(comp_count, 0); - - forall_nodes(residualGraph, node) { - comp_nodes[components[node]].push_back(node); - comp_weights[components[node]] += residualGraph.getNodeWeight(node); - } endfor - - NodeID s = residualGraph.number_of_nodes()-2; - NodeID t = residualGraph.number_of_nodes()-1; - int comp_of_s = components[s]; - int comp_of_t = components[t]; - - graph_access scc_graph; - build_internal_scc_graph( residualGraph, components, comp_count, scc_graph); - - std::vector comp_for_rhs; - compute_new_rhs(scc_graph, config, comp_weights, comp_of_s, comp_of_t, perfect_rhs_weight, comp_for_rhs); - - //add comp_for_rhs nodes to new rhs - for( unsigned i = 0; i < comp_for_rhs.size(); i++) { - int cur_component = comp_for_rhs[i]; - if(cur_component != comp_of_s && cur_component != comp_of_t) { - for( unsigned j = 0; j < comp_nodes[cur_component].size(); j++) { - new_rhs_nodes.push_back(comp_nodes[cur_component][j]); - } - } - } + strongly_connected_components scc; + std::vector components(residualGraph.number_of_nodes()); + int comp_count = scc.strong_components(residualGraph,components); + + std::vector< std::vector > comp_nodes(comp_count); + std::vector< NodeWeight > comp_weights(comp_count, 0); + + forall_nodes(residualGraph, node) { + comp_nodes[components[node]].push_back(node); + comp_weights[components[node]] += residualGraph.getNodeWeight(node); + } endfor + + NodeID s = residualGraph.number_of_nodes()-2; + NodeID t = residualGraph.number_of_nodes()-1; + int comp_of_s = components[s]; + int comp_of_t = components[t]; + + graph_access scc_graph; + build_internal_scc_graph( residualGraph, components, comp_count, scc_graph); + + std::vector comp_for_rhs; + compute_new_rhs(scc_graph, config, comp_weights, comp_of_s, comp_of_t, perfect_rhs_weight, comp_for_rhs); + + //add comp_for_rhs nodes to new rhs + for( unsigned i = 0; i < comp_for_rhs.size(); i++) { + int cur_component = comp_for_rhs[i]; + if(cur_component != comp_of_s && cur_component != comp_of_t) { + for( unsigned j = 0; j < comp_nodes[cur_component].size(); j++) { + new_rhs_nodes.push_back(comp_nodes[cur_component][j]); + } + } + } } -void most_balanced_minimum_cuts::compute_new_rhs( graph_access & scc_graph, +void most_balanced_minimum_cuts::compute_new_rhs( graph_access & scc_graph, const PartitionConfig & config, std::vector< NodeWeight > & comp_weights, int comp_of_s, int comp_of_t, NodeWeight optimal_rhs_stripe_weight, std::vector & comp_for_rhs) { - - //all successors of s cant be in any closure so they are marked invalid / this is basically a bfs in a DAG - std::vector valid_to_add(scc_graph.number_of_nodes(), true); - std::queue node_queue; - node_queue.push(comp_of_s); - valid_to_add[comp_of_s] = false; - - while (!node_queue.empty()) { - NodeID node = node_queue.front(); - node_queue.pop(); - forall_out_edges(scc_graph, e, node) { - NodeID target = scc_graph.getEdgeTarget(e); - if(valid_to_add[target] == true) { - valid_to_add[target] = false; - node_queue.push(target); - } - } endfor - } - std::vector tmp_comp_for_rhs; - int best_diff = std::numeric_limits::max(); - for(unsigned i = 0; i < config.toposort_iterations; i++) { - topological_sort ts; - std::vector sorted_sequence; - ts.sort(scc_graph, sorted_sequence); - - tmp_comp_for_rhs.clear(); - - bool t_contained = false; - int cur_rhs_weight = 0; - int diff = 0; - for( unsigned idx = 0; idx < sorted_sequence.size(); idx++) { - int cur_component = sorted_sequence[idx]; - - if( cur_component == comp_of_t ) { - t_contained = true; - } - - if(valid_to_add[cur_component]) { - int tmpdiff = optimal_rhs_stripe_weight - cur_rhs_weight - comp_weights[cur_component]; - bool would_break = tmpdiff <= 0 && t_contained; - if(!would_break) { - tmp_comp_for_rhs.push_back(cur_component); - cur_rhs_weight += comp_weights[cur_component]; - } else { - //decide wether we should add this component now - if(abs(tmpdiff) < abs(diff)) { - //the add it - tmp_comp_for_rhs.push_back(cur_component); - cur_rhs_weight += comp_weights[cur_component]; - diff = optimal_rhs_stripe_weight - cur_rhs_weight; - } - break; - } - - } - - diff = optimal_rhs_stripe_weight - cur_rhs_weight; - if( diff <= 0 && t_contained) { - break; - } - - } - if(abs(diff) < best_diff) { - best_diff = abs(diff); - comp_for_rhs = tmp_comp_for_rhs; - } + //all successors of s cant be in any closure so they are marked invalid / this is basically a bfs in a DAG + std::vector valid_to_add(scc_graph.number_of_nodes(), true); + std::queue node_queue; + node_queue.push(comp_of_s); + valid_to_add[comp_of_s] = false; + + while (!node_queue.empty()) { + NodeID node = node_queue.front(); + node_queue.pop(); + forall_out_edges(scc_graph, e, node) { + NodeID target = scc_graph.getEdgeTarget(e); + if(valid_to_add[target] == true) { + valid_to_add[target] = false; + node_queue.push(target); + } + } endfor +} + std::vector tmp_comp_for_rhs; + int best_diff = std::numeric_limits::max(); + for(unsigned i = 0; i < config.toposort_iterations; i++) { + topological_sort ts; + std::vector sorted_sequence; + ts.sort(scc_graph, sorted_sequence); + + tmp_comp_for_rhs.clear(); + + bool t_contained = false; + int cur_rhs_weight = 0; + int diff = 0; + for( unsigned idx = 0; idx < sorted_sequence.size(); idx++) { + int cur_component = sorted_sequence[idx]; + + if( cur_component == comp_of_t ) { + t_contained = true; + } + + if(valid_to_add[cur_component]) { + int tmpdiff = optimal_rhs_stripe_weight - cur_rhs_weight - comp_weights[cur_component]; + bool would_break = tmpdiff <= 0 && t_contained; + if(!would_break) { + tmp_comp_for_rhs.push_back(cur_component); + cur_rhs_weight += comp_weights[cur_component]; + } else { + //decide wether we should add this component now + if(abs(tmpdiff) < abs(diff)) { + //the add it + tmp_comp_for_rhs.push_back(cur_component); + cur_rhs_weight += comp_weights[cur_component]; + diff = optimal_rhs_stripe_weight - cur_rhs_weight; + } + break; } + + } + + diff = optimal_rhs_stripe_weight - cur_rhs_weight; + if( diff <= 0 && t_contained) { + break; + } + + } + if(abs(diff) < best_diff) { + best_diff = abs(diff); + comp_for_rhs = tmp_comp_for_rhs; + } + + } } -void most_balanced_minimum_cuts::build_internal_scc_graph( graph_access & residualGraph, - std::vector & components, - int comp_count, +void most_balanced_minimum_cuts::build_internal_scc_graph( graph_access & residualGraph, + std::vector & components, + int comp_count, graph_access & scc_graph) { - std::vector< std::vector > edges(comp_count); - unsigned edge_count = 0; - forall_nodes(residualGraph, node) { - forall_out_edges(residualGraph, e, node) { - NodeID target = residualGraph.getEdgeTarget(e); - if(components[node] != components[target]) { - edges[components[node]].push_back(components[target]); - edge_count++; - } - } endfor - } endfor - - //build_scc_graph - scc_graph.start_construction(comp_count, edge_count); - for( unsigned i = 0; i < (unsigned) comp_count; i++) { - NodeID node = scc_graph.new_node(); - std::unordered_map allready_contained; - for(unsigned j = 0; j < edges[i].size(); j++) { - if(allready_contained.find(edges[i][j]) == allready_contained.end()) { - scc_graph.new_edge(node, edges[i][j]); - allready_contained[edges[i][j]] = true; - } - } - } - - scc_graph.finish_construction(); + std::vector< std::vector > edges(comp_count); + unsigned edge_count = 0; + forall_nodes(residualGraph, node) { + forall_out_edges(residualGraph, e, node) { + NodeID target = residualGraph.getEdgeTarget(e); + if(components[node] != components[target]) { + edges[components[node]].push_back(components[target]); + edge_count++; + } + } endfor +} endfor + +//build_scc_graph +scc_graph.start_construction(comp_count, edge_count); + for( unsigned i = 0; i < (unsigned) comp_count; i++) { + NodeID node = scc_graph.new_node(); + std::unordered_map allready_contained; + for(unsigned j = 0; j < edges[i].size(); j++) { + if(allready_contained.find(edges[i][j]) == allready_contained.end()) { + scc_graph.new_edge(node, edges[i][j]); + allready_contained[edges[i][j]] = true; + } + } + } + + scc_graph.finish_construction(); +} } - diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.h index 3b82bc95..ad05b64c 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.h @@ -10,31 +10,31 @@ #include "data_structure/graph_access.h" #include "partition_config.h" - +namespace kahip::modified { class most_balanced_minimum_cuts { - public: - most_balanced_minimum_cuts(); - virtual ~most_balanced_minimum_cuts(); - - void compute_good_balanced_min_cut( graph_access & residualGraph, - const PartitionConfig & config, - NodeWeight & perfect_rhs_weight, - std::vector< NodeID > & new_rhs_node ); +public: + most_balanced_minimum_cuts(); + virtual ~most_balanced_minimum_cuts(); - private: - void build_internal_scc_graph( graph_access & residualGraph, - std::vector & components, - int comp_count, - graph_access & scc_graph); - - void compute_new_rhs( graph_access & scc_graph, + void compute_good_balanced_min_cut( graph_access & residualGraph, const PartitionConfig & config, - std::vector< NodeWeight > & comp_weights, - int comp_of_s, - int comp_of_t, - NodeWeight optimal_rhs_weight, - std::vector & comp_for_rhs); + NodeWeight & perfect_rhs_weight, + std::vector< NodeID > & new_rhs_node ); + +private: + void build_internal_scc_graph( graph_access & residualGraph, + std::vector & components, + int comp_count, + graph_access & scc_graph); + + void compute_new_rhs( graph_access & scc_graph, + const PartitionConfig & config, + std::vector< NodeWeight > & comp_weights, + int comp_of_s, + int comp_of_t, + NodeWeight optimal_rhs_weight, + std::vector & comp_for_rhs); }; - +} #endif /* end of include guard: MOST_BALANCED_MINIMUM_CUTS_SBD5CS */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.cpp index c796d3ef..0177a5d6 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.cpp @@ -15,7 +15,7 @@ #include "flow_solving_kernel/edge_cut_flow_solver.h" #include "quality_metrics.h" #include "two_way_flow_refinement.h" - +namespace kahip::modified { two_way_flow_refinement::two_way_flow_refinement() { } @@ -35,258 +35,258 @@ EdgeWeight two_way_flow_refinement::perform_refinement(PartitionConfig & config, EdgeWeight & cut, bool & something_changed) { - EdgeWeight retval = iterativ_flow_iteration(config, G, boundary, lhs_pq_start_nodes, rhs_pq_start_nodes, - refinement_pair, lhs_part_weight, rhs_part_weight, cut, something_changed); + EdgeWeight retval = iterativ_flow_iteration(config, G, boundary, lhs_pq_start_nodes, rhs_pq_start_nodes, + refinement_pair, lhs_part_weight, rhs_part_weight, cut, something_changed); - if(retval > 0) { - something_changed = true; - } + if(retval > 0) { + something_changed = true; + } - return retval; + return retval; } -EdgeWeight two_way_flow_refinement::iterativ_flow_iteration(PartitionConfig & config, +EdgeWeight two_way_flow_refinement::iterativ_flow_iteration(PartitionConfig & config, graph_access & G, - complete_boundary & boundary, - std::vector & lhs_pq_start_nodes, + complete_boundary & boundary, + std::vector & lhs_pq_start_nodes, std::vector & rhs_pq_start_nodes, - boundary_pair * refinement_pair, + boundary_pair * refinement_pair, NodeWeight & lhs_part_weight, NodeWeight & rhs_part_weight, EdgeWeight & cut, bool & something_changed) { - if(lhs_pq_start_nodes.size() == 0 or rhs_pq_start_nodes.size() == 0) return 0; // nothing to refine - ASSERT_TRUE(lhs_part_weight < config.upper_bound_partition && rhs_part_weight < config.upper_bound_partition); - - PartitionID lhs = refinement_pair->lhs; - PartitionID rhs = refinement_pair->rhs; - boundary_bfs bfs_region_searcher; - - double region_factor = config.flow_region_factor; - unsigned max_iterations = config.max_flow_iterations; - unsigned iteration = 0; - - std::vector lhs_nodes; - std::vector rhs_nodes; - - EdgeWeight cur_improvement = 1; - EdgeWeight best_cut = cut; - bool sumoverweight = lhs_part_weight + rhs_part_weight > 2*config.upper_bound_partition; - if(sumoverweight) { - return 0; - } - - NodeWeight average_partition_weight = ceil(config.largest_graph_weight / config.k); - while(cur_improvement > 0 && iteration < max_iterations) { - NodeWeight upper_bound_no_lhs = (NodeWeight)std::max((100.0+region_factor*config.imbalance)/100.0*(average_partition_weight) - rhs_part_weight,0.0); - NodeWeight upper_bound_no_rhs = (NodeWeight)std::max((100.0+region_factor*config.imbalance)/100.0*(average_partition_weight) - lhs_part_weight,0.0); - - std::vector lhs_boundary_stripe; - NodeWeight lhs_stripe_weight = 0; - if(!bfs_region_searcher.boundary_bfs_search(G, lhs_pq_start_nodes, lhs, - upper_bound_no_lhs, lhs_boundary_stripe, - lhs_stripe_weight, true)) { - - EdgeWeight improvement = cut-best_cut; - cut = best_cut; - return improvement; - } - - - std::vector rhs_boundary_stripe; - NodeWeight rhs_stripe_weight = 0; - if(!bfs_region_searcher.boundary_bfs_search(G, rhs_pq_start_nodes, rhs, - upper_bound_no_rhs, rhs_boundary_stripe, - rhs_stripe_weight, true)) { - - EdgeWeight improvement = cut-best_cut; - cut = best_cut; - return improvement; - } - - std::vector new_rhs_nodes; - std::vector new_to_old_ids; - - edge_cut_flow_solver fsolve; - EdgeWeight new_cut = fsolve.get_min_flow_max_cut(config, G, - lhs, rhs, - lhs_boundary_stripe, rhs_boundary_stripe, - new_to_old_ids, cut, - rhs_part_weight, - rhs_stripe_weight, - new_rhs_nodes); - - NodeWeight new_lhs_part_weight = 0; - NodeWeight new_rhs_part_weight = 0; - NodeWeight new_lhs_stripe_weight = 0; - NodeWeight new_rhs_stripe_weight = 0; - NodeID no_nodes_flow_graph = lhs_boundary_stripe.size() + rhs_boundary_stripe.size(); - - for(unsigned i = 0; i < new_rhs_nodes.size(); i++) { - NodeID new_rhs_node = new_rhs_nodes[i]; - if(new_rhs_node < no_nodes_flow_graph) { // not target and source - NodeID old_node_id = new_to_old_ids[new_rhs_node]; - new_rhs_stripe_weight += G.getNodeWeight(old_node_id); - G.setPartitionIndex(old_node_id, BOUNDARY_STRIPE_NODE-1); - } - } - - for(unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { - if( G.getPartitionIndex(lhs_boundary_stripe[i]) == BOUNDARY_STRIPE_NODE) { - new_lhs_stripe_weight += G.getNodeWeight(lhs_boundary_stripe[i]); - } - } - - for(unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { - if( G.getPartitionIndex(rhs_boundary_stripe[i]) == BOUNDARY_STRIPE_NODE) { - new_lhs_stripe_weight += G.getNodeWeight(rhs_boundary_stripe[i]); - } - } - new_lhs_part_weight = boundary.getBlockWeight(lhs) + ((int)new_lhs_stripe_weight-(int)lhs_stripe_weight) ; - new_rhs_part_weight = boundary.getBlockWeight(rhs) + ((int)new_rhs_stripe_weight-(int)rhs_stripe_weight) ; - - bool partition_is_feasable = false; - if(config.most_balanced_minimum_cuts) { - partition_is_feasable = new_lhs_part_weight < config.upper_bound_partition - && new_rhs_part_weight < config.upper_bound_partition - && (new_cut < best_cut || abs((int)new_lhs_part_weight - (int) new_rhs_part_weight) < abs((int)lhs_part_weight - (int)rhs_part_weight)); - } else { - partition_is_feasable = new_lhs_part_weight < config.upper_bound_partition - && new_rhs_part_weight < config.upper_bound_partition && new_cut < best_cut; - } - - if(partition_is_feasable) { - // then this partition can be accepted - apply_partition_and_update_boundary( config, G, refinement_pair, - lhs, rhs, boundary, - lhs_boundary_stripe, rhs_boundary_stripe, - lhs_stripe_weight, - rhs_stripe_weight, - new_to_old_ids, - new_rhs_nodes); - - boundary.setEdgeCut(refinement_pair, new_cut); - - lhs_part_weight = boundary.getBlockWeight(lhs); - rhs_part_weight = boundary.getBlockWeight(rhs); - ASSERT_TRUE(lhs_part_weight < config.upper_bound_partition && rhs_part_weight < config.upper_bound_partition); - - - cur_improvement = best_cut - new_cut; - best_cut = new_cut; - - if(2*region_factor < config.flow_region_factor) { - region_factor *= 2; - } else { - region_factor = config.flow_region_factor; - } - if(region_factor == config.flow_region_factor) { - //in that case we are finished - break; - } - if( iteration+1 < max_iterations ) { - // update the start nodes for the bfs - lhs_pq_start_nodes.clear(); - boundary.setup_start_nodes(G, lhs, *refinement_pair, lhs_pq_start_nodes); - - rhs_pq_start_nodes.clear(); - boundary.setup_start_nodes(G, rhs, *refinement_pair, rhs_pq_start_nodes); - } - - - } else { - //undo changes - for( unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { - G.setPartitionIndex(lhs_boundary_stripe[i], lhs); - } - for( unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { - G.setPartitionIndex(rhs_boundary_stripe[i], rhs); - } - - //smaller the region_factor - region_factor = std::max(region_factor/2,1.0); - if(new_cut == best_cut) { - break; - } - } - iteration++; - } - - - ASSERT_TRUE(lhs_part_weight < config.upper_bound_partition && rhs_part_weight < config.upper_bound_partition); - EdgeWeight improvement = cut-best_cut; - cut = best_cut; - return improvement; + if(lhs_pq_start_nodes.size() == 0 or rhs_pq_start_nodes.size() == 0) return 0; // nothing to refine + ASSERT_TRUE(lhs_part_weight < config.upper_bound_partition && rhs_part_weight < config.upper_bound_partition); + + PartitionID lhs = refinement_pair->lhs; + PartitionID rhs = refinement_pair->rhs; + boundary_bfs bfs_region_searcher; + + double region_factor = config.flow_region_factor; + unsigned max_iterations = config.max_flow_iterations; + unsigned iteration = 0; + + std::vector lhs_nodes; + std::vector rhs_nodes; + + EdgeWeight cur_improvement = 1; + EdgeWeight best_cut = cut; + bool sumoverweight = lhs_part_weight + rhs_part_weight > 2*config.upper_bound_partition; + if(sumoverweight) { + return 0; + } + + NodeWeight average_partition_weight = ceil(config.largest_graph_weight / config.k); + while(cur_improvement > 0 && iteration < max_iterations) { + NodeWeight upper_bound_no_lhs = (NodeWeight)std::max((100.0+region_factor*config.imbalance)/100.0*(average_partition_weight) - rhs_part_weight,0.0); + NodeWeight upper_bound_no_rhs = (NodeWeight)std::max((100.0+region_factor*config.imbalance)/100.0*(average_partition_weight) - lhs_part_weight,0.0); + + std::vector lhs_boundary_stripe; + NodeWeight lhs_stripe_weight = 0; + if(!bfs_region_searcher.boundary_bfs_search(G, lhs_pq_start_nodes, lhs, + upper_bound_no_lhs, lhs_boundary_stripe, + lhs_stripe_weight, true)) { + + EdgeWeight improvement = cut-best_cut; + cut = best_cut; + return improvement; + } + + + std::vector rhs_boundary_stripe; + NodeWeight rhs_stripe_weight = 0; + if(!bfs_region_searcher.boundary_bfs_search(G, rhs_pq_start_nodes, rhs, + upper_bound_no_rhs, rhs_boundary_stripe, + rhs_stripe_weight, true)) { + + EdgeWeight improvement = cut-best_cut; + cut = best_cut; + return improvement; + } + + std::vector new_rhs_nodes; + std::vector new_to_old_ids; + + edge_cut_flow_solver fsolve; + EdgeWeight new_cut = fsolve.get_min_flow_max_cut(config, G, + lhs, rhs, + lhs_boundary_stripe, rhs_boundary_stripe, + new_to_old_ids, cut, + rhs_part_weight, + rhs_stripe_weight, + new_rhs_nodes); + + NodeWeight new_lhs_part_weight = 0; + NodeWeight new_rhs_part_weight = 0; + NodeWeight new_lhs_stripe_weight = 0; + NodeWeight new_rhs_stripe_weight = 0; + NodeID no_nodes_flow_graph = lhs_boundary_stripe.size() + rhs_boundary_stripe.size(); + + for(unsigned i = 0; i < new_rhs_nodes.size(); i++) { + NodeID new_rhs_node = new_rhs_nodes[i]; + if(new_rhs_node < no_nodes_flow_graph) { // not target and source + NodeID old_node_id = new_to_old_ids[new_rhs_node]; + new_rhs_stripe_weight += G.getNodeWeight(old_node_id); + G.setPartitionIndex(old_node_id, BOUNDARY_STRIPE_NODE-1); + } + } + + for(unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { + if( G.getPartitionIndex(lhs_boundary_stripe[i]) == BOUNDARY_STRIPE_NODE) { + new_lhs_stripe_weight += G.getNodeWeight(lhs_boundary_stripe[i]); + } + } + + for(unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { + if( G.getPartitionIndex(rhs_boundary_stripe[i]) == BOUNDARY_STRIPE_NODE) { + new_lhs_stripe_weight += G.getNodeWeight(rhs_boundary_stripe[i]); + } + } + new_lhs_part_weight = boundary.getBlockWeight(lhs) + ((int)new_lhs_stripe_weight-(int)lhs_stripe_weight) ; + new_rhs_part_weight = boundary.getBlockWeight(rhs) + ((int)new_rhs_stripe_weight-(int)rhs_stripe_weight) ; + + bool partition_is_feasable = false; + if(config.most_balanced_minimum_cuts) { + partition_is_feasable = new_lhs_part_weight < config.upper_bound_partition + && new_rhs_part_weight < config.upper_bound_partition + && (new_cut < best_cut || abs((int)new_lhs_part_weight - (int) new_rhs_part_weight) < abs((int)lhs_part_weight - (int)rhs_part_weight)); + } else { + partition_is_feasable = new_lhs_part_weight < config.upper_bound_partition + && new_rhs_part_weight < config.upper_bound_partition && new_cut < best_cut; + } + + if(partition_is_feasable) { + // then this partition can be accepted + apply_partition_and_update_boundary( config, G, refinement_pair, + lhs, rhs, boundary, + lhs_boundary_stripe, rhs_boundary_stripe, + lhs_stripe_weight, + rhs_stripe_weight, + new_to_old_ids, + new_rhs_nodes); + + boundary.setEdgeCut(refinement_pair, new_cut); + + lhs_part_weight = boundary.getBlockWeight(lhs); + rhs_part_weight = boundary.getBlockWeight(rhs); + ASSERT_TRUE(lhs_part_weight < config.upper_bound_partition && rhs_part_weight < config.upper_bound_partition); + + + cur_improvement = best_cut - new_cut; + best_cut = new_cut; + + if(2*region_factor < config.flow_region_factor) { + region_factor *= 2; + } else { + region_factor = config.flow_region_factor; + } + if(region_factor == config.flow_region_factor) { + //in that case we are finished + break; + } + if( iteration+1 < max_iterations ) { + // update the start nodes for the bfs + lhs_pq_start_nodes.clear(); + boundary.setup_start_nodes(G, lhs, *refinement_pair, lhs_pq_start_nodes); + + rhs_pq_start_nodes.clear(); + boundary.setup_start_nodes(G, rhs, *refinement_pair, rhs_pq_start_nodes); + } + + + } else { + //undo changes + for( unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { + G.setPartitionIndex(lhs_boundary_stripe[i], lhs); + } + for( unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { + G.setPartitionIndex(rhs_boundary_stripe[i], rhs); + } + + //smaller the region_factor + region_factor = std::max(region_factor/2,1.0); + if(new_cut == best_cut) { + break; + } + } + iteration++; + } + + + ASSERT_TRUE(lhs_part_weight < config.upper_bound_partition && rhs_part_weight < config.upper_bound_partition); + EdgeWeight improvement = cut-best_cut; + cut = best_cut; + return improvement; } -void two_way_flow_refinement::apply_partition_and_update_boundary( const PartitionConfig & config, - graph_access & G, +void two_way_flow_refinement::apply_partition_and_update_boundary( const PartitionConfig & config, + graph_access & G, boundary_pair * refinement_pair, - PartitionID & lhs, + PartitionID & lhs, PartitionID & rhs, - complete_boundary & boundary, + complete_boundary & boundary, std::vector & lhs_boundary_stripe, std::vector & rhs_boundary_stripe, - NodeWeight & lhs_stripe_weight, - NodeWeight & rhs_stripe_weight, + NodeWeight & lhs_stripe_weight, + NodeWeight & rhs_stripe_weight, std::vector & new_to_old_ids, std::vector & new_rhs_nodes) { - NodeID no_nodes_flow_graph = lhs_boundary_stripe.size() + rhs_boundary_stripe.size(); - NodeWeight new_lhs_stripe_weight = 0; - NodeWeight new_rhs_stripe_weight = 0; - - NodeWeight new_rhs_stripe_no_nodes = 0; - NodeWeight new_lhs_stripe_no_nodes = 0; + NodeID no_nodes_flow_graph = lhs_boundary_stripe.size() + rhs_boundary_stripe.size(); + NodeWeight new_lhs_stripe_weight = 0; + NodeWeight new_rhs_stripe_weight = 0; - for(unsigned i = 0; i < new_rhs_nodes.size(); i++) { - NodeID new_rhs_node = new_rhs_nodes[i]; - if(new_rhs_node < no_nodes_flow_graph) { // not target and source - NodeID old_node_id = new_to_old_ids[new_rhs_node]; - G.setPartitionIndex(old_node_id, rhs); - new_rhs_stripe_weight += G.getNodeWeight(old_node_id); - new_rhs_stripe_no_nodes++; - } - } + NodeWeight new_rhs_stripe_no_nodes = 0; + NodeWeight new_lhs_stripe_no_nodes = 0; - for(unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { - if( G.getPartitionIndex(lhs_boundary_stripe[i]) == BOUNDARY_STRIPE_NODE) { - G.setPartitionIndex(lhs_boundary_stripe[i], lhs); - new_lhs_stripe_weight += G.getNodeWeight(lhs_boundary_stripe[i]); - new_lhs_stripe_no_nodes++; - } - } + for(unsigned i = 0; i < new_rhs_nodes.size(); i++) { + NodeID new_rhs_node = new_rhs_nodes[i]; + if(new_rhs_node < no_nodes_flow_graph) { // not target and source + NodeID old_node_id = new_to_old_ids[new_rhs_node]; + G.setPartitionIndex(old_node_id, rhs); + new_rhs_stripe_weight += G.getNodeWeight(old_node_id); + new_rhs_stripe_no_nodes++; + } + } - for(unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { - if( G.getPartitionIndex(rhs_boundary_stripe[i]) == BOUNDARY_STRIPE_NODE) { - G.setPartitionIndex(rhs_boundary_stripe[i], lhs); - new_lhs_stripe_weight += G.getNodeWeight(rhs_boundary_stripe[i]); - new_lhs_stripe_no_nodes++; - } - } + for(unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { + if( G.getPartitionIndex(lhs_boundary_stripe[i]) == BOUNDARY_STRIPE_NODE) { + G.setPartitionIndex(lhs_boundary_stripe[i], lhs); + new_lhs_stripe_weight += G.getNodeWeight(lhs_boundary_stripe[i]); + new_lhs_stripe_no_nodes++; + } + } + for(unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { + if( G.getPartitionIndex(rhs_boundary_stripe[i]) == BOUNDARY_STRIPE_NODE) { + G.setPartitionIndex(rhs_boundary_stripe[i], lhs); + new_lhs_stripe_weight += G.getNodeWeight(rhs_boundary_stripe[i]); + new_lhs_stripe_no_nodes++; + } + } - // ********** fix the boundary data structure ***************** - boundary.setBlockWeight(lhs, boundary.getBlockWeight(lhs) + ((int)new_lhs_stripe_weight-(int)lhs_stripe_weight) ); - boundary.setBlockWeight(rhs, boundary.getBlockWeight(rhs) + ((int)new_rhs_stripe_weight-(int)rhs_stripe_weight) ); - boundary.setBlockNoNodes(lhs, boundary.getBlockNoNodes(lhs) + ((int)new_lhs_stripe_no_nodes -(int)lhs_boundary_stripe.size()) ); - boundary.setBlockNoNodes(rhs, boundary.getBlockNoNodes(rhs) + ((int)new_rhs_stripe_no_nodes -(int)rhs_boundary_stripe.size()) ); + // ********** fix the boundary data structure ***************** + boundary.setBlockWeight(lhs, boundary.getBlockWeight(lhs) + ((int)new_lhs_stripe_weight-(int)lhs_stripe_weight) ); + boundary.setBlockWeight(rhs, boundary.getBlockWeight(rhs) + ((int)new_rhs_stripe_weight-(int)rhs_stripe_weight) ); + boundary.setBlockNoNodes(lhs, boundary.getBlockNoNodes(lhs) + ((int)new_lhs_stripe_no_nodes -(int)lhs_boundary_stripe.size()) ); + boundary.setBlockNoNodes(rhs, boundary.getBlockNoNodes(rhs) + ((int)new_rhs_stripe_no_nodes -(int)rhs_boundary_stripe.size()) ); - //this can be improved by only calling this method on the nodes that changed the partition - for(unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { - boundary.postMovedBoundaryNodeUpdates(lhs_boundary_stripe[i], refinement_pair, false, true); - } - for(unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { - boundary.postMovedBoundaryNodeUpdates(rhs_boundary_stripe[i], refinement_pair, false, true); - } + //this can be improved by only calling this method on the nodes that changed the partition + for(unsigned i = 0; i < lhs_boundary_stripe.size(); i++) { + boundary.postMovedBoundaryNodeUpdates(lhs_boundary_stripe[i], refinement_pair, false, true); + } -} + for(unsigned i = 0; i < rhs_boundary_stripe.size(); i++) { + boundary.postMovedBoundaryNodeUpdates(rhs_boundary_stripe[i], refinement_pair, false, true); + } +} +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.h index 65fe4506..d75b398f 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.h @@ -14,49 +14,49 @@ #include "uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h" #include "uncoarsening/refinement/quotient_graph_refinement/partial_boundary.h" #include "uncoarsening/refinement/quotient_graph_refinement/two_way_refinement.h" - +namespace kahip::modified { class two_way_flow_refinement : public two_way_refinement { - public: - two_way_flow_refinement( ); - virtual ~two_way_flow_refinement(); - - EdgeWeight perform_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - std::vector & lhs_pq_start_nodes, - std::vector & rhs_pq_start_nodes, - boundary_pair * refinement_pair, - NodeWeight & lhs_part_weight, - NodeWeight & rhs_part_weight, - EdgeWeight & cut, - bool & something_changed); - private: - EdgeWeight iterativ_flow_iteration(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - std::vector & lhs_pq_start_nodes, - std::vector & rhs_pq_start_nodes, - boundary_pair * refinement_pair, - NodeWeight & lhs_part_weight, - NodeWeight & rhs_part_weight, - EdgeWeight & cut, - bool & something_changed); - - void apply_partition_and_update_boundary(const PartitionConfig & config, - graph_access & G, - boundary_pair * refinement_pair, - PartitionID & lhs, - PartitionID & rhs, - complete_boundary & boundary, - std::vector & lhs_boundary_stripe, - std::vector & rhs_boundary_stripe, - NodeWeight & lhs_stripe_weight, - NodeWeight & rhs_stripe_weight, - std::vector & new_to_old_ids, - std::vector & new_rhs_nodes); +public: + two_way_flow_refinement( ); + virtual ~two_way_flow_refinement(); + + EdgeWeight perform_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + std::vector & lhs_pq_start_nodes, + std::vector & rhs_pq_start_nodes, + boundary_pair * refinement_pair, + NodeWeight & lhs_part_weight, + NodeWeight & rhs_part_weight, + EdgeWeight & cut, + bool & something_changed); +private: + EdgeWeight iterativ_flow_iteration(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + std::vector & lhs_pq_start_nodes, + std::vector & rhs_pq_start_nodes, + boundary_pair * refinement_pair, + NodeWeight & lhs_part_weight, + NodeWeight & rhs_part_weight, + EdgeWeight & cut, + bool & something_changed); + + void apply_partition_and_update_boundary(const PartitionConfig & config, + graph_access & G, + boundary_pair * refinement_pair, + PartitionID & lhs, + PartitionID & rhs, + complete_boundary & boundary, + std::vector & lhs_boundary_stripe, + std::vector & rhs_boundary_stripe, + NodeWeight & lhs_stripe_weight, + NodeWeight & rhs_stripe_weight, + std::vector & new_to_old_ids, + std::vector & new_rhs_nodes); }; - +} #endif /* end of include guard: TWO_WAY_FLOW_REFINEMENT_BVTL6G49 */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.cpp index 5dc7bdc8..fcdbf134 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "partial_boundary.h" - +namespace kahip::modified { PartialBoundary::PartialBoundary() { } @@ -14,4 +14,4 @@ PartialBoundary::PartialBoundary() { PartialBoundary::~PartialBoundary() { } - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.h index 7739e601..48fe63bf 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.h @@ -10,7 +10,7 @@ #include #include "definitions.h" - +namespace kahip::modified { struct compare_nodes_contains { bool operator()(const NodeID lhs, const NodeID rhs) const { return (lhs == rhs); @@ -19,32 +19,32 @@ struct compare_nodes_contains { struct is_boundary { - bool contains; - is_boundary() { + bool contains; + is_boundary() { contains = false; - } + } }; struct hash_boundary_nodes { - size_t operator()(const NodeID idx) const { + size_t operator()(const NodeID idx) const { return idx; - } + } }; typedef std::unordered_map is_boundary_node_hashtable; class PartialBoundary { - public: - PartialBoundary( ); - virtual ~PartialBoundary(); +public: + PartialBoundary( ); + virtual ~PartialBoundary(); - bool contains(NodeID node); - void insert(NodeID node); - void deleteNode(NodeID node); - NodeID size(); + bool contains(NodeID node); + void insert(NodeID node); + void deleteNode(NodeID node); + NodeID size(); - is_boundary_node_hashtable internal_boundary; + is_boundary_node_hashtable internal_boundary; }; inline bool PartialBoundary::contains(NodeID node) { @@ -62,7 +62,7 @@ inline void PartialBoundary::deleteNode(NodeID node) { inline NodeID PartialBoundary::size() { return internal_boundary.size(); } - +} //iterator for #define forall_boundary_nodes(boundary, n) { is_boundary_node_hashtable::iterator iter; NodeID n; for(iter = boundary.internal_boundary.begin(); iter != boundary.internal_boundary.end(); iter++ ) { n = iter->first; diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.cpp index 0c29096f..a28311f1 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.cpp @@ -16,7 +16,7 @@ #include "quotient_graph_scheduling/simple_quotient_graph_scheduler.h" #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.h" #include "uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.h" - +namespace kahip::modified { quotient_graph_refinement::quotient_graph_refinement() { } @@ -31,237 +31,237 @@ void quotient_graph_refinement::setup_start_nodes(graph_access & G, complete_boundary & boundary, boundary_starting_nodes & start_nodes) { - start_nodes.resize(boundary.size(partition, &bp)); - NodeID cur_idx = 0; + start_nodes.resize(boundary.size(partition, &bp)); + NodeID cur_idx = 0; - PartitionID lhs = bp.lhs; - PartitionID rhs = bp.rhs; - PartialBoundary & lhs_b = boundary.getDirectedBoundary(partition, lhs, rhs); + PartitionID lhs = bp.lhs; + PartitionID rhs = bp.rhs; + PartialBoundary & lhs_b = boundary.getDirectedBoundary(partition, lhs, rhs); - forall_boundary_nodes(lhs_b, cur_bnd_node) { - ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), partition); - start_nodes[cur_idx++] = cur_bnd_node; - } endfor + forall_boundary_nodes(lhs_b, cur_bnd_node) { + ASSERT_EQ(G.getPartitionIndex(cur_bnd_node), partition); + start_nodes[cur_idx++] = cur_bnd_node; + } endfor } EdgeWeight quotient_graph_refinement::perform_refinement(PartitionConfig & config, graph_access & G, complete_boundary & boundary) { - ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); - ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); - - QuotientGraphEdges qgraph_edges; - boundary.getQuotientGraphEdges(qgraph_edges); - quotient_graph_scheduling* scheduler = NULL; - - int factor = ceil(config.bank_account_factor*qgraph_edges.size()); - switch(config.refinement_scheduling_algorithm) { - case REFINEMENT_SCHEDULING_FAST: - scheduler = new simple_quotient_graph_scheduler(config, qgraph_edges, factor); - break; - case REFINEMENT_SCHEDULING_ACTIVE_BLOCKS: - scheduler = new active_block_quotient_graph_scheduler(config, qgraph_edges, factor); - break; - case REFINEMENT_SCHEDULING_ACTIVE_BLOCKS_REF_KWAY: - scheduler = new active_block_quotient_graph_scheduler(config, qgraph_edges, factor); - break; - } - - EdgeWeight overall_improvement = 0; - unsigned int no_of_pairwise_improvement_steps = 0; - quality_metrics qm; - - do { - no_of_pairwise_improvement_steps++; - // ********** preconditions ******************** - ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); - ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); - // *************** end ************************* - - if(scheduler->hasFinished()) break; //fetch the case where we have no qgraph edges - - boundary_pair & bp = scheduler->getNext(); - PartitionID lhs = bp.lhs; - PartitionID rhs = bp.rhs; - - NodeWeight lhs_part_weight = boundary.getBlockWeight(lhs); - NodeWeight rhs_part_weight = boundary.getBlockWeight(rhs); - - EdgeWeight initial_cut_value = boundary.getEdgeCut(&bp); - if( initial_cut_value < 0 ) continue; // quick fix, for bug 02 (very rare cross combine bug / coarsest level) ! - - bool something_changed = false; - -#ifndef NDEBUG - EdgeWeight oldcut = initial_cut_value; + ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); + ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); + + QuotientGraphEdges qgraph_edges; + boundary.getQuotientGraphEdges(qgraph_edges); + quotient_graph_scheduling* scheduler = NULL; + + int factor = ceil(config.bank_account_factor*qgraph_edges.size()); + switch(config.refinement_scheduling_algorithm) { + case REFINEMENT_SCHEDULING_FAST: + scheduler = new simple_quotient_graph_scheduler(config, qgraph_edges, factor); + break; + case REFINEMENT_SCHEDULING_ACTIVE_BLOCKS: + scheduler = new active_block_quotient_graph_scheduler(config, qgraph_edges, factor); + break; + case REFINEMENT_SCHEDULING_ACTIVE_BLOCKS_REF_KWAY: + scheduler = new active_block_quotient_graph_scheduler(config, qgraph_edges, factor); + break; + } + + EdgeWeight overall_improvement = 0; + unsigned int no_of_pairwise_improvement_steps = 0; + quality_metrics qm; + + do { + no_of_pairwise_improvement_steps++; + // ********** preconditions ******************** + ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); + ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); + // *************** end ************************* + + if(scheduler->hasFinished()) break; //fetch the case where we have no qgraph edges + + boundary_pair & bp = scheduler->getNext(); + PartitionID lhs = bp.lhs; + PartitionID rhs = bp.rhs; + + NodeWeight lhs_part_weight = boundary.getBlockWeight(lhs); + NodeWeight rhs_part_weight = boundary.getBlockWeight(rhs); + + EdgeWeight initial_cut_value = boundary.getEdgeCut(&bp); + if( initial_cut_value < 0 ) continue; // quick fix, for bug 02 (very rare cross combine bug / coarsest level) ! + + bool something_changed = false; + +#ifndef NDEBUG + EdgeWeight oldcut = initial_cut_value; #endif - PartitionConfig cfg = config; - EdgeWeight improvement = perform_a_two_way_refinement(cfg, G, boundary, bp, - lhs, rhs, - lhs_part_weight, rhs_part_weight, - initial_cut_value, something_changed); - - overall_improvement += improvement; - - EdgeWeight multitry_improvement = 0; - if(config.refinement_scheduling_algorithm == REFINEMENT_SCHEDULING_ACTIVE_BLOCKS_REF_KWAY ) { - multitry_kway_fm kway_ref; - std::unordered_map touched_blocks; - - multitry_improvement = kway_ref.perform_refinement_around_parts(cfg, G, - boundary, true, - config.local_multitry_fm_alpha, lhs, rhs, - touched_blocks); - - if(multitry_improvement > 0) { - ((active_block_quotient_graph_scheduler*)scheduler)->activate_blocks(touched_blocks); - } - - } - - qgraph_edge_statistics stat(improvement, &bp, something_changed); - scheduler->pushStatistics(stat); - - //**************** assertions / postconditions ************************** - ASSERT_TRUE( oldcut - improvement == qm.edge_cut(G, lhs, rhs) - || config.refinement_scheduling_algorithm == REFINEMENT_SCHEDULING_ACTIVE_BLOCKS_REF_KWAY); - ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); - ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); - ASSERT_TRUE(boundary.getBlockNoNodes(lhs)>0); - ASSERT_TRUE(boundary.getBlockNoNodes(rhs)>0); - //*************************** end **************************************** - } while(!scheduler->hasFinished()); - - delete scheduler; - return overall_improvement; + PartitionConfig cfg = config; + EdgeWeight improvement = perform_a_two_way_refinement(cfg, G, boundary, bp, + lhs, rhs, + lhs_part_weight, rhs_part_weight, + initial_cut_value, something_changed); + + overall_improvement += improvement; + + EdgeWeight multitry_improvement = 0; + if(config.refinement_scheduling_algorithm == REFINEMENT_SCHEDULING_ACTIVE_BLOCKS_REF_KWAY ) { + multitry_kway_fm kway_ref; + std::unordered_map touched_blocks; + + multitry_improvement = kway_ref.perform_refinement_around_parts(cfg, G, + boundary, true, + config.local_multitry_fm_alpha, lhs, rhs, + touched_blocks); + + if(multitry_improvement > 0) { + ((active_block_quotient_graph_scheduler*)scheduler)->activate_blocks(touched_blocks); + } + + } + + qgraph_edge_statistics stat(improvement, &bp, something_changed); + scheduler->pushStatistics(stat); + + //**************** assertions / postconditions ************************** + ASSERT_TRUE( oldcut - improvement == qm.edge_cut(G, lhs, rhs) + || config.refinement_scheduling_algorithm == REFINEMENT_SCHEDULING_ACTIVE_BLOCKS_REF_KWAY); + ASSERT_TRUE(boundary.assert_bnodes_in_boundaries()); + ASSERT_TRUE(boundary.assert_boundaries_are_bnodes()); + ASSERT_TRUE(boundary.getBlockNoNodes(lhs)>0); + ASSERT_TRUE(boundary.getBlockNoNodes(rhs)>0); + //*************************** end **************************************** + } while(!scheduler->hasFinished()); + + delete scheduler; + return overall_improvement; } -EdgeWeight quotient_graph_refinement::perform_a_two_way_refinement(PartitionConfig & config, +EdgeWeight quotient_graph_refinement::perform_a_two_way_refinement(PartitionConfig & config, graph_access & G, complete_boundary & boundary, boundary_pair & bp, - PartitionID & lhs, + PartitionID & lhs, PartitionID & rhs, NodeWeight & lhs_part_weight, NodeWeight & rhs_part_weight, EdgeWeight & initial_cut_value, bool & something_changed) { - two_way_fm pair_wise_refinement; - two_way_flow_refinement pair_wise_flow; - - std::vector lhs_bnd_nodes; - setup_start_nodes(G, lhs, bp, boundary, lhs_bnd_nodes); - - std::vector rhs_bnd_nodes; + two_way_fm pair_wise_refinement; + two_way_flow_refinement pair_wise_flow; + + std::vector lhs_bnd_nodes; + setup_start_nodes(G, lhs, bp, boundary, lhs_bnd_nodes); + + std::vector rhs_bnd_nodes; + setup_start_nodes(G, rhs, bp, boundary, rhs_bnd_nodes); + + something_changed = false; + EdgeWeight improvement = 0; + + quality_metrics qm; + if(config.refinement_type == REFINEMENT_TYPE_FM_FLOW || config.refinement_type == REFINEMENT_TYPE_FM) { + improvement = pair_wise_refinement.perform_refinement(config, + G, + boundary, + lhs_bnd_nodes, + rhs_bnd_nodes, + &bp, + lhs_part_weight, + rhs_part_weight, + initial_cut_value, + something_changed); + ASSERT_TRUE(improvement >= 0 || config.rebalance); + } + + if(config.refinement_type == REFINEMENT_TYPE_FM_FLOW || config.refinement_type == REFINEMENT_TYPE_FLOW){ + lhs_bnd_nodes.clear(); + setup_start_nodes(G, lhs, bp, boundary, lhs_bnd_nodes); + + rhs_bnd_nodes.clear(); + setup_start_nodes(G, rhs, bp, boundary, rhs_bnd_nodes); + + EdgeWeight _improvement = pair_wise_flow.perform_refinement(config, + G, + boundary, + lhs_bnd_nodes, + rhs_bnd_nodes, + &bp, + lhs_part_weight, + rhs_part_weight, + initial_cut_value, + something_changed); + + ASSERT_TRUE(_improvement >= 0 || config.rebalance); + improvement += _improvement; + } + + bool only_one_block_is_overloaded = boundary.getBlockWeight(lhs) > config.upper_bound_partition; + only_one_block_is_overloaded = only_one_block_is_overloaded + || boundary.getBlockWeight(rhs) > config.upper_bound_partition; + only_one_block_is_overloaded = only_one_block_is_overloaded && + (boundary.getBlockWeight(lhs) <= config.upper_bound_partition || + boundary.getBlockWeight(rhs) <= config.upper_bound_partition); + + if(only_one_block_is_overloaded) { + + PartitionConfig cfg = config; + cfg.softrebalance = true; + cfg.rebalance = false; + + lhs_bnd_nodes.clear(); + setup_start_nodes(G, lhs, bp, boundary, lhs_bnd_nodes); + + rhs_bnd_nodes.clear(); + setup_start_nodes(G, rhs, bp, boundary, rhs_bnd_nodes); + + improvement += pair_wise_refinement.perform_refinement(cfg, + G, + boundary, + lhs_bnd_nodes, + rhs_bnd_nodes, + &bp, + lhs_part_weight, + rhs_part_weight, + initial_cut_value, + something_changed); + + ASSERT_TRUE(improvement >= 0 || config.rebalance); + + if(!config.disable_hard_rebalance && !config.kaffpa_perfectly_balanced_refinement && !config.initial_bipartitioning) { + only_one_block_is_overloaded = boundary.getBlockWeight(lhs) > config.upper_bound_partition; + only_one_block_is_overloaded = only_one_block_is_overloaded + || boundary.getBlockWeight(rhs) > config.upper_bound_partition; + only_one_block_is_overloaded = only_one_block_is_overloaded && + (boundary.getBlockWeight(lhs) <= config.upper_bound_partition || + boundary.getBlockWeight(rhs) <= config.upper_bound_partition); + + if(only_one_block_is_overloaded) { + cfg.softrebalance = true; + cfg.rebalance = true; + + lhs_bnd_nodes.clear(); + setup_start_nodes(G, lhs, bp, boundary, lhs_bnd_nodes); + + rhs_bnd_nodes.clear(); setup_start_nodes(G, rhs, bp, boundary, rhs_bnd_nodes); - something_changed = false; - EdgeWeight improvement = 0; - - quality_metrics qm; - if(config.refinement_type == REFINEMENT_TYPE_FM_FLOW || config.refinement_type == REFINEMENT_TYPE_FM) { - improvement = pair_wise_refinement.perform_refinement(config, - G, - boundary, - lhs_bnd_nodes, - rhs_bnd_nodes, - &bp, - lhs_part_weight, - rhs_part_weight, - initial_cut_value, - something_changed); - ASSERT_TRUE(improvement >= 0 || config.rebalance); - } - - if(config.refinement_type == REFINEMENT_TYPE_FM_FLOW || config.refinement_type == REFINEMENT_TYPE_FLOW){ - lhs_bnd_nodes.clear(); - setup_start_nodes(G, lhs, bp, boundary, lhs_bnd_nodes); - - rhs_bnd_nodes.clear(); - setup_start_nodes(G, rhs, bp, boundary, rhs_bnd_nodes); - - EdgeWeight _improvement = pair_wise_flow.perform_refinement(config, - G, - boundary, - lhs_bnd_nodes, - rhs_bnd_nodes, - &bp, - lhs_part_weight, - rhs_part_weight, - initial_cut_value, - something_changed); - - ASSERT_TRUE(_improvement >= 0 || config.rebalance); - improvement += _improvement; - } - - bool only_one_block_is_overloaded = boundary.getBlockWeight(lhs) > config.upper_bound_partition; - only_one_block_is_overloaded = only_one_block_is_overloaded - || boundary.getBlockWeight(rhs) > config.upper_bound_partition; - only_one_block_is_overloaded = only_one_block_is_overloaded && - (boundary.getBlockWeight(lhs) <= config.upper_bound_partition || - boundary.getBlockWeight(rhs) <= config.upper_bound_partition); - - if(only_one_block_is_overloaded) { - - PartitionConfig cfg = config; - cfg.softrebalance = true; - cfg.rebalance = false; - - lhs_bnd_nodes.clear(); - setup_start_nodes(G, lhs, bp, boundary, lhs_bnd_nodes); - - rhs_bnd_nodes.clear(); - setup_start_nodes(G, rhs, bp, boundary, rhs_bnd_nodes); - - improvement += pair_wise_refinement.perform_refinement(cfg, - G, - boundary, - lhs_bnd_nodes, - rhs_bnd_nodes, - &bp, - lhs_part_weight, - rhs_part_weight, - initial_cut_value, - something_changed); - - ASSERT_TRUE(improvement >= 0 || config.rebalance); - - if(!config.disable_hard_rebalance && !config.kaffpa_perfectly_balanced_refinement && !config.initial_bipartitioning) { - only_one_block_is_overloaded = boundary.getBlockWeight(lhs) > config.upper_bound_partition; - only_one_block_is_overloaded = only_one_block_is_overloaded - || boundary.getBlockWeight(rhs) > config.upper_bound_partition; - only_one_block_is_overloaded = only_one_block_is_overloaded && - (boundary.getBlockWeight(lhs) <= config.upper_bound_partition || - boundary.getBlockWeight(rhs) <= config.upper_bound_partition); - - if(only_one_block_is_overloaded) { - cfg.softrebalance = true; - cfg.rebalance = true; - - lhs_bnd_nodes.clear(); - setup_start_nodes(G, lhs, bp, boundary, lhs_bnd_nodes); - - rhs_bnd_nodes.clear(); - setup_start_nodes(G, rhs, bp, boundary, rhs_bnd_nodes); - - improvement += pair_wise_refinement.perform_refinement(cfg, - G, - boundary, - lhs_bnd_nodes, - rhs_bnd_nodes, - &bp, - lhs_part_weight, - rhs_part_weight, - initial_cut_value, - something_changed); - - } - } - } - - return improvement; + improvement += pair_wise_refinement.perform_refinement(cfg, + G, + boundary, + lhs_bnd_nodes, + rhs_bnd_nodes, + &bp, + lhs_part_weight, + rhs_part_weight, + initial_cut_value, + something_changed); + + } + } + } + + return improvement; +} } - diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.h index 3bf6a2af..7a753c77 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.h @@ -10,33 +10,33 @@ #include "definitions.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class quotient_graph_refinement : public refinement { - public: - quotient_graph_refinement( ); - virtual ~quotient_graph_refinement(); - - EdgeWeight perform_refinement(PartitionConfig & config, graph_access & G, complete_boundary & boundary); - - void setup_start_nodes(graph_access & G, - PartitionID partition, - boundary_pair & bp, - complete_boundary & boundary, - boundary_starting_nodes & start_nodes); - - private: - EdgeWeight perform_a_two_way_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - boundary_pair & bp, - PartitionID & lhs, - PartitionID & rhs, - NodeWeight & lhs_part_weight, - NodeWeight & rhs_part_weight, - EdgeWeight & cut, - bool & something_changed); +public: + quotient_graph_refinement( ); + virtual ~quotient_graph_refinement(); + + EdgeWeight perform_refinement(PartitionConfig & config, graph_access & G, complete_boundary & boundary); + + void setup_start_nodes(graph_access & G, + PartitionID partition, + boundary_pair & bp, + complete_boundary & boundary, + boundary_starting_nodes & start_nodes); + +private: + EdgeWeight perform_a_two_way_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + boundary_pair & bp, + PartitionID & lhs, + PartitionID & rhs, + NodeWeight & lhs_part_weight, + NodeWeight & rhs_part_weight, + EdgeWeight & cut, + bool & something_changed); }; - +} #endif /* end of include guard: QUOTIENT_GRAPH_REFINEMENT_A0Y1Y6LL */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.cpp index 646080f9..16755ef2 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.cpp @@ -6,22 +6,22 @@ *****************************************************************************/ #include "active_block_quotient_graph_scheduler.h" - -active_block_quotient_graph_scheduler::active_block_quotient_graph_scheduler( const PartitionConfig & config, +namespace kahip::modified { +active_block_quotient_graph_scheduler::active_block_quotient_graph_scheduler( const PartitionConfig & config, QuotientGraphEdges & qgraph_edges, unsigned int bank_account) : m_quotient_graph_edges(qgraph_edges) { - m_is_block_active.resize(config.k); - for( unsigned int i = 0; i < m_is_block_active.size(); i++) { - m_is_block_active[i] = true; - } - - m_no_of_active_blocks = config.k; - init(); + m_is_block_active.resize(config.k); + for( unsigned int i = 0; i < m_is_block_active.size(); i++) { + m_is_block_active[i] = true; + } + + m_no_of_active_blocks = config.k; + init(); } active_block_quotient_graph_scheduler::~active_block_quotient_graph_scheduler() { } - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.h index f185151b..c596bac4 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.h @@ -13,27 +13,27 @@ #include "partition_config.h" #include "quotient_graph_scheduling.h" #include "random_functions.h" - +namespace kahip::modified { class active_block_quotient_graph_scheduler : public quotient_graph_scheduling { - public: - active_block_quotient_graph_scheduler( const PartitionConfig & config, - QuotientGraphEdges & qgraph_edges, - unsigned int bank_account); +public: + active_block_quotient_graph_scheduler( const PartitionConfig & config, + QuotientGraphEdges & qgraph_edges, + unsigned int bank_account); - virtual ~active_block_quotient_graph_scheduler(); + virtual ~active_block_quotient_graph_scheduler(); - virtual bool hasFinished(); - virtual boundary_pair & getNext(); - virtual void pushStatistics(qgraph_edge_statistics & statistic); - virtual void init(); + virtual bool hasFinished(); + virtual boundary_pair & getNext(); + virtual void pushStatistics(qgraph_edge_statistics & statistic); + virtual void init(); - void activate_blocks(std::unordered_map & blocks); + void activate_blocks(std::unordered_map & blocks); - private: - QuotientGraphEdges & m_quotient_graph_edges; - QuotientGraphEdges m_active_quotient_graph_edges; - PartitionID m_no_of_active_blocks; - std::vector m_is_block_active; +private: + QuotientGraphEdges & m_quotient_graph_edges; + QuotientGraphEdges m_active_quotient_graph_edges; + PartitionID m_no_of_active_blocks; + std::vector m_is_block_active; }; inline void active_block_quotient_graph_scheduler::init() { @@ -41,8 +41,8 @@ inline void active_block_quotient_graph_scheduler::init() { m_active_quotient_graph_edges.clear(); for( unsigned int i = 0; i < m_quotient_graph_edges.size(); i++) { - PartitionID lhs = m_quotient_graph_edges[i].lhs; - PartitionID rhs = m_quotient_graph_edges[i].rhs; + PartitionID lhs = m_quotient_graph_edges[i].lhs; + PartitionID rhs = m_quotient_graph_edges[i].rhs; if(m_is_block_active[lhs]) m_no_of_active_blocks++; if(m_is_block_active[rhs]) m_no_of_active_blocks++; @@ -64,14 +64,14 @@ inline bool active_block_quotient_graph_scheduler::hasFinished( ) { init(); } - return m_no_of_active_blocks == 0; + return m_no_of_active_blocks == 0; } inline boundary_pair & active_block_quotient_graph_scheduler::getNext( ) { boundary_pair & ret_value = m_active_quotient_graph_edges.back(); m_active_quotient_graph_edges.pop_back(); - return ret_value; + return ret_value; } inline void active_block_quotient_graph_scheduler::pushStatistics(qgraph_edge_statistics & statistic) { @@ -84,8 +84,8 @@ inline void active_block_quotient_graph_scheduler::pushStatistics(qgraph_edge_st inline void active_block_quotient_graph_scheduler::activate_blocks(std::unordered_map & blocks) { std::unordered_map::iterator it; for(it = blocks.begin(); it != blocks.end(); ++it) { - m_is_block_active[it->first] = true; + m_is_block_active[it->first] = true; } } - +} #endif /* end of include guard: ACTIVE_BLOCK_QUOTIENT_GRAPH_SCHEDULER_2QATIGSY */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.cpp index ee1e251a..77358e16 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.cpp @@ -6,11 +6,11 @@ *****************************************************************************/ #include "quotient_graph_scheduling.h" - +namespace kahip::modified { quotient_graph_scheduling::quotient_graph_scheduling() { } quotient_graph_scheduling::~quotient_graph_scheduling() { } - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.h index 69ac3ed9..04f48e7b 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.h @@ -10,29 +10,29 @@ #include "definitions.h" #include "uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h" - +namespace kahip::modified { struct qgraph_edge_statistics { - EdgeWeight improvement; - bool something_changed; - boundary_pair* pair; + EdgeWeight improvement; + bool something_changed; + boundary_pair* pair; - qgraph_edge_statistics(EdgeWeight _improvement, - boundary_pair* bp, - bool change) : improvement(_improvement), something_changed(change), pair(bp){ - } + qgraph_edge_statistics(EdgeWeight _improvement, + boundary_pair* bp, + bool change) : improvement(_improvement), something_changed(change), pair(bp){ + } }; class quotient_graph_scheduling { - public: - quotient_graph_scheduling(); - virtual ~quotient_graph_scheduling(); +public: + quotient_graph_scheduling(); + virtual ~quotient_graph_scheduling(); - virtual bool hasFinished() = 0; - virtual boundary_pair & getNext() = 0; - virtual void pushStatistics(qgraph_edge_statistics & statistic) = 0; + virtual bool hasFinished() = 0; + virtual boundary_pair & getNext() = 0; + virtual void pushStatistics(qgraph_edge_statistics & statistic) = 0; }; - +} #endif /* end of include guard: QUOTIENT_GRAPH_SCHEDULING_NEFT9H3J */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.cpp index 74270d6f..ee79d734 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.cpp @@ -7,22 +7,22 @@ #include "random_functions.h" #include "simple_quotient_graph_scheduler.h" - -simple_quotient_graph_scheduler::simple_quotient_graph_scheduler(PartitionConfig & config, +namespace kahip::modified { +simple_quotient_graph_scheduler::simple_quotient_graph_scheduler(PartitionConfig & config, QuotientGraphEdges & qgraph_edges, unsigned int account) { - unsigned added_edges = 0; - for( unsigned i = 0; i < (unsigned)ceil(config.bank_account_factor) && added_edges <= account; i++) { - random_functions::permutate_vector_good_small(qgraph_edges); - for( unsigned i = 0; i < qgraph_edges.size() && added_edges <= account; i++) { - m_quotient_graph_edges_pool.push_back(qgraph_edges[i]); - added_edges++; - } - } + unsigned added_edges = 0; + for( unsigned i = 0; i < (unsigned)ceil(config.bank_account_factor) && added_edges <= account; i++) { + random_functions::permutate_vector_good_small(qgraph_edges); + for( unsigned i = 0; i < qgraph_edges.size() && added_edges <= account; i++) { + m_quotient_graph_edges_pool.push_back(qgraph_edges[i]); + added_edges++; + } + } } simple_quotient_graph_scheduler::~simple_quotient_graph_scheduler() { } - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.h index 0fed53b4..16116486 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.h @@ -10,8 +10,8 @@ #include "partition_config.h" #include "quotient_graph_scheduling.h" - -class simple_quotient_graph_scheduler : public quotient_graph_scheduling { +namespace kahip::modified { +class simple_quotient_graph_scheduler : public quotient_graph_scheduling { public: simple_quotient_graph_scheduler(PartitionConfig & config, QuotientGraphEdges & qgraph_edges, @@ -36,5 +36,5 @@ inline boundary_pair & simple_quotient_graph_scheduler::getNext( ) { m_quotient_graph_edges_pool.pop_back(); return ret_value; } - +} #endif /* end of include guard: SIMPLE_QUOTIENT_GRAPH_SCHEDULER_YG9BEBH0 */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/two_way_refinement.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/two_way_refinement.h index 31bee55d..33744cba 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/two_way_refinement.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/two_way_refinement.h @@ -9,24 +9,24 @@ #define TWO_WAY_REFINEMENT_INTERFACE_1ZWCSI0J #include "definitions.h" - +namespace kahip::modified { class two_way_refinement{ - public: - two_way_refinement( ) {}; - virtual ~two_way_refinement() {}; +public: + two_way_refinement( ) {}; + virtual ~two_way_refinement() {}; - virtual EdgeWeight perform_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - std::vector & lhs_pq_start_nodes, - std::vector & rhs_pq_start_nodes, - boundary_pair * refinement_pair, - NodeWeight & lhs_part_weight, - NodeWeight & rhs_part_weight, - EdgeWeight & cut, - bool & something_changed) = 0; + virtual EdgeWeight perform_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + std::vector & lhs_pq_start_nodes, + std::vector & rhs_pq_start_nodes, + boundary_pair * refinement_pair, + NodeWeight & lhs_part_weight, + NodeWeight & rhs_part_weight, + EdgeWeight & cut, + bool & something_changed) = 0; }; - +} #endif /* end of include guard: TWO_WAY_REFINEMENT_INTERFACE_1ZWCSI0J */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/refinement.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/refinement.cpp index 83de9564..b56b23d7 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/refinement.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/refinement.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "refinement.h" - +namespace kahip::modified { refinement::refinement() { } @@ -14,6 +14,6 @@ refinement::refinement() { refinement::~refinement() { } - +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/refinement.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/refinement.h index 778e3beb..da13fc96 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/refinement.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/refinement.h @@ -12,15 +12,16 @@ #include "partition_config.h" #include "quotient_graph_refinement/complete_boundary.h" +namespace kahip::modified { class refinement { public: - refinement( ); - virtual ~refinement(); - - virtual EdgeWeight perform_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary) = 0; -}; + refinement( ); + virtual ~refinement(); + virtual EdgeWeight perform_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary) = 0; +}; +} #endif /* end of include guard: REFINEMENT_UJN9IBHM */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_bucket_queue.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_bucket_queue.h index 0e3e746e..3a832227 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_bucket_queue.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_bucket_queue.h @@ -9,47 +9,48 @@ #define TABU_BUCKET_PQ_EM8YJPA9 #include - //this PQ is specalized for Tabu Search, it only contains non-tabu moves //there is a second PQ that contains tabu moves #include "data_structure/matrix/normal_matrix.h" #include "data_structure/priority_queues/priority_queue_interface.h" #include "random_functions.h" +namespace kahip::modified { + class tabu_bucket_queue { - public: - tabu_bucket_queue( PartitionConfig & config, const EdgeWeight & gain_span, NodeID number_of_nodes ); - - virtual ~tabu_bucket_queue() { delete m_queue_index; delete m_gains;}; - - NodeID size(); - void insert(NodeID id, PartitionID block, Gain gain); - bool empty(); - - Gain maxValue(); - std::pair maxElement(); - std::pair deleteMax(); - - void decreaseKey(NodeID node, PartitionID block, Gain newGain); - void increaseKey(NodeID node, PartitionID block, Gain newGain); - - void changeKey(NodeID element, PartitionID block, Gain newKey); - Gain getKey(NodeID element, PartitionID block); - void deleteNode(NodeID node, PartitionID block); - - bool contains(NodeID node, PartitionID block); - private: - normal_matrix* m_queue_index; - normal_matrix* m_gains; - NodeID m_elements; - EdgeWeight m_gain_span; - unsigned m_max_idx; //points to the non-empty bucket with the largest gain - - std::vector< std::vector< std::pair > > m_buckets; +public: + tabu_bucket_queue( PartitionConfig & config, const EdgeWeight & gain_span, NodeID number_of_nodes ); + + virtual ~tabu_bucket_queue() { delete m_queue_index; delete m_gains;}; + + NodeID size(); + void insert(NodeID id, PartitionID block, Gain gain); + bool empty(); + + Gain maxValue(); + std::pair maxElement(); + std::pair deleteMax(); + + void decreaseKey(NodeID node, PartitionID block, Gain newGain); + void increaseKey(NodeID node, PartitionID block, Gain newGain); + + void changeKey(NodeID element, PartitionID block, Gain newKey); + Gain getKey(NodeID element, PartitionID block); + void deleteNode(NodeID node, PartitionID block); + + bool contains(NodeID node, PartitionID block); +private: + normal_matrix* m_queue_index; + normal_matrix* m_gains; + NodeID m_elements; + EdgeWeight m_gain_span; + unsigned m_max_idx; //points to the non-empty bucket with the largest gain + + std::vector< std::vector< std::pair > > m_buckets; }; -inline tabu_bucket_queue::tabu_bucket_queue( PartitionConfig & config, - const EdgeWeight & gain_span_input, +inline tabu_bucket_queue::tabu_bucket_queue( PartitionConfig & config, + const EdgeWeight & gain_span_input, NodeID number_of_nodes ) { m_elements = 0; m_gain_span = gain_span_input; @@ -60,62 +61,62 @@ inline tabu_bucket_queue::tabu_bucket_queue( PartitionConfig & config, } inline NodeID tabu_bucket_queue::size() { - return m_elements; + return m_elements; } inline void tabu_bucket_queue::insert(NodeID node, PartitionID block, Gain gain) { unsigned address = gain + m_gain_span; if(address > m_max_idx) { - m_max_idx = address; + m_max_idx = address; } - + std::pair< NodeID, PartitionID > p; p.first = node; p.second = block; - m_buckets[address].push_back( p ); + m_buckets[address].push_back( p ); m_queue_index->set_xy(node, block, m_buckets[address].size() - 1); //store position m_gains->set_xy(node, block, gain); - + m_elements++; } inline bool tabu_bucket_queue::empty( ) { - return m_elements == 0; + return m_elements == 0; } inline Gain tabu_bucket_queue::maxValue( ) { - return m_max_idx - m_gain_span; + return m_max_idx - m_gain_span; } inline std::pair tabu_bucket_queue::maxElement( ) { - return m_buckets[m_max_idx].back(); + return m_buckets[m_max_idx].back(); } inline std::pair tabu_bucket_queue::deleteMax() { - unsigned rnd_idx = random_functions::nextInt(0, m_buckets[m_max_idx].size()-1); - swap(m_buckets[m_max_idx][rnd_idx], m_buckets[m_max_idx].back()); - m_queue_index->set_xy(m_buckets[m_max_idx][rnd_idx].first, m_buckets[m_max_idx][rnd_idx].second, rnd_idx); - - std::pair< NodeID, PartitionID > p; - p = m_buckets[m_max_idx].back(); - m_buckets[m_max_idx].pop_back(); - - m_queue_index->set_xy(p.first, p.second, NOTINQUEUE); //erase(node, block); - m_gains->set_xy(p.first, p.second, NOTINQUEUE); - - if( m_buckets[m_max_idx].size() == 0 ) { - //update max_idx - while( m_max_idx != 0 ) { - m_max_idx--; - if(m_buckets[m_max_idx].size() > 0) { - break; - } - } - } - - m_elements--; - return p; + unsigned rnd_idx = random_functions::nextInt(0, m_buckets[m_max_idx].size()-1); + swap(m_buckets[m_max_idx][rnd_idx], m_buckets[m_max_idx].back()); + m_queue_index->set_xy(m_buckets[m_max_idx][rnd_idx].first, m_buckets[m_max_idx][rnd_idx].second, rnd_idx); + + std::pair< NodeID, PartitionID > p; + p = m_buckets[m_max_idx].back(); + m_buckets[m_max_idx].pop_back(); + + m_queue_index->set_xy(p.first, p.second, NOTINQUEUE); //erase(node, block); + m_gains->set_xy(p.first, p.second, NOTINQUEUE); + + if( m_buckets[m_max_idx].size() == 0 ) { + //update max_idx + while( m_max_idx != 0 ) { + m_max_idx--; + if(m_buckets[m_max_idx].size() > 0) { + break; + } + } + } + + m_elements--; + return p; } inline void tabu_bucket_queue::decreaseKey(NodeID node, PartitionID block, Gain new_gain) { @@ -170,6 +171,6 @@ inline void tabu_bucket_queue::deleteNode(NodeID node, PartitionID block) { inline bool tabu_bucket_queue::contains(NodeID node, PartitionID block) { return m_queue_index->get_xy(node, block) != NOTINQUEUE; } - +} #endif /* end of include guard: BUCKET_PQ_EM8YJPA9 */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_moves_queue.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_moves_queue.h index 1767ed53..9a0ad45c 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_moves_queue.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_moves_queue.h @@ -12,7 +12,7 @@ #include #include "data_structure/matrix/normal_matrix.h" - +namespace kahip::modified { struct TabuTimePair { int time; NodeID node; @@ -28,20 +28,20 @@ struct comparePair{ typedef std::priority_queue< TabuTimePair, std::vector< TabuTimePair >, comparePair > PQ; class tabu_moves_queue { - public: - tabu_moves_queue( ); - virtual ~tabu_moves_queue() { }; +public: + tabu_moves_queue( ); + virtual ~tabu_moves_queue() { }; - NodeID size(); - bool empty(); + NodeID size(); + bool empty(); - void insert(NodeID node, PartitionID block, int time); - int minValue(); - std::pair deleteMin(); + void insert(NodeID node, PartitionID block, int time); + int minValue(); + std::pair deleteMin(); - bool contains(NodeID node, PartitionID block); - private: - PQ m_priority_queue; + bool contains(NodeID node, PartitionID block); +private: + PQ m_priority_queue; }; inline tabu_moves_queue::tabu_moves_queue() { @@ -74,5 +74,5 @@ inline std::pair tabu_moves_queue::deleteMin() { m_priority_queue.pop(); return p; } - +} #endif /* end of include guard: BUCKET_PQ_EM8YJPA9 */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp index bf32efd3..afb31cca 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp @@ -13,7 +13,7 @@ #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.h" #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h" #include "uncoarsening/refinement/kway_graph_refinement/kway_stop_rule.h" - +namespace kahip::modified { tabu_search::tabu_search() { } @@ -23,227 +23,228 @@ tabu_search::~tabu_search() { } EdgeWeight tabu_search::perform_refinement(PartitionConfig & config, graph_access & G, complete_boundary & boundary) { - quality_metrics qm; - EdgeWeight input_cut = qm.edge_cut(G); - EdgeWeight cur_cut = input_cut; - EdgeWeight best_cut = input_cut; - std::vector< PartitionID > bestmap(G.number_of_nodes(), 0); - forall_nodes(G, node) { - bestmap[node] = G.getPartitionIndex(node); + quality_metrics qm; + EdgeWeight input_cut = qm.edge_cut(G); + EdgeWeight cur_cut = input_cut; + EdgeWeight best_cut = input_cut; + std::vector< PartitionID > bestmap(G.number_of_nodes(), 0); + forall_nodes(G, node) { + bestmap[node] = G.getPartitionIndex(node); + } endfor + + + EdgeWeight max_degree = G.getMaxDegree(); + tabu_bucket_queue* queue = new tabu_bucket_queue(config, max_degree, G.number_of_nodes()); + tabu_moves_queue* tabu_moves = new tabu_moves_queue(); + + matrix* T = new normal_matrix(G.number_of_nodes(), config.k); + matrix* gamma = new normal_matrix(G.number_of_nodes(), config.k); + + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID target_block = G.getPartitionIndex(target); + gamma->set_xy( node, target_block, gamma->get_xy(node, target_block) + 1); + } endfor +} endfor + +forall_nodes(G, node) { + bool is_bnd = false; + PartitionID pIdx = G.getPartitionIndex(node); + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( pIdx != G.getPartitionIndex(target)) { + is_bnd = true; + break; + } + } endfor + + if(is_bnd) { + for( unsigned block = 0; block < config.k; block++) { + if( gamma->get_xy(node, block) > 0 && G.getPartitionIndex(node) != block) { + queue->insert(node, block, gamma->get_xy(node, block) - gamma->get_xy(node, G.getPartitionIndex(node))); + } else { + tabu_moves->insert(node, block, 0); + } + } + } + } endfor + + unsigned no_impro_iterations = 0; + config.maxT = random_functions::nextInt(50, 3000); + unsigned iteration_limit = std::min((int)(2*G.number_of_nodes()), 40000); + + std::vector< std::pair > undo_buffer; + undo_buffer.reserve(G.number_of_edges()); + + std::vector cur_state(G.number_of_nodes()); + int best_idx = -1; int round_counter = -1; unsigned iteration = 0; + + for( iteration = 0, round_counter = 0; iteration < config.maxIter; iteration++) { + if(!queue->empty()) { + Gain gain = queue->maxValue(); + std::pair< NodeID, PartitionID > p = queue->deleteMax(); + NodeID node = p.first; + NodeID block = p.second; + NodeID from = G.getPartitionIndex(node); + + if( boundary.getBlockWeight(block) + 1 < config.upper_bound_partition && from != block) { + boundary.setBlockWeight(from, boundary.getBlockWeight(from) - 1); + boundary.setBlockWeight(block, boundary.getBlockWeight(block) + 1); + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + gamma->set_xy( target, from, gamma->get_xy(target, from) - 1); + gamma->set_xy( target, block, gamma->get_xy(target, block) + 1); + } endfor + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID target_block = G.getPartitionIndex(target); + + for( unsigned i = 0; i < config.k; i++) { + if(queue->contains( target, i )) { + if( gamma->get_xy(target, i) == 0) { + queue->deleteNode(target, i); + } else { + queue->changeKey(target, i, gamma->get_xy(target, i) - gamma->get_xy(target, target_block)); + } + } else { + if( gamma->get_xy(target, i) > 0 && T->get_xy(target, i) < (int)iteration) { + queue->insert(target, i, gamma->get_xy(target, i) - gamma->get_xy(target, target_block)); + } + } + } } endfor - - EdgeWeight max_degree = G.getMaxDegree(); - tabu_bucket_queue* queue = new tabu_bucket_queue(config, max_degree, G.number_of_nodes()); - tabu_moves_queue* tabu_moves = new tabu_moves_queue(); + std::pair< NodeID, PartitionID > undo_move; + undo_move.first = node; + undo_move.second = G.getPartitionIndex(node); + + undo_buffer.push_back(undo_move); + round_counter++; + + G.setPartitionIndex(node, block); + + forall_out_edges(G, e, node) { + for( unsigned i = 0; i < config.k; i++) { + if(queue->contains( node, i)) { + if( gamma->get_xy(node, i) == 0) { + queue->deleteNode(node,i); + } else { + queue->changeKey(node, i, gamma->get_xy(node, i) - gamma->get_xy(node, block)); + } + } else { + if(gamma->get_xy(node, i) > 0 && T->get_xy(node, i) < (int)iteration) { + queue->insert(node, i, gamma->get_xy(node, i) - gamma->get_xy(node, block)); + } + } + } + } endfor + + cur_cut -= gain; + } + + + unsigned tenure = config.maxT;//random_functions::nextInt( config.maxT, 2*config.maxT); + tenure = compute_tenure(iteration, tenure); + unsigned small_offset = random_functions::nextInt(1,3); + T->set_xy(node, block, iteration + tenure + small_offset); + tabu_moves->insert(node, block, iteration + tenure + small_offset); + if( T->get_xy( node, from) < (int)iteration ) { + T->set_xy(node, from, iteration + tenure); + tabu_moves->insert(node, from, iteration + tenure); + } + + if(queue->contains(node, from) ) { + queue->deleteNode(node, from); + } - matrix* T = new normal_matrix(G.number_of_nodes(), config.k); - matrix* gamma = new normal_matrix(G.number_of_nodes(), config.k); + if(queue->contains(node, block) ) { + queue->deleteNode(node, block); + } + } + + //update the best cut found + if( cur_cut < best_cut ) { + best_idx = undo_buffer.size() - 1 ; + best_cut = cur_cut; + no_impro_iterations = 0; + } else { + no_impro_iterations++; + } + + + if( round_counter >= (int)G.number_of_edges() ) { + + if( best_idx != -1 ) { forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID target_block = G.getPartitionIndex(target); - gamma->set_xy( node, target_block, gamma->get_xy(node, target_block) + 1); - } endfor + cur_state[node] = G.getPartitionIndex(node); } endfor + for( int idx = undo_buffer.size()-1; idx > best_idx; idx--) { + G.setPartitionIndex( undo_buffer[idx].first, undo_buffer[idx].second ); + } forall_nodes(G, node) { - bool is_bnd = false; - PartitionID pIdx = G.getPartitionIndex(node); - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( pIdx != G.getPartitionIndex(target)) { - is_bnd = true; - break; - } - } endfor - - if(is_bnd) { - for( unsigned block = 0; block < config.k; block++) { - if( gamma->get_xy(node, block) > 0 && G.getPartitionIndex(node) != block) { - queue->insert(node, block, gamma->get_xy(node, block) - gamma->get_xy(node, G.getPartitionIndex(node))); - } else { - tabu_moves->insert(node, block, 0); - } - } - } + bestmap[node] = G.getPartitionIndex(node); + G.setPartitionIndex(node, cur_state[node]); } endfor - - unsigned no_impro_iterations = 0; - config.maxT = random_functions::nextInt(50, 3000); - unsigned iteration_limit = std::min((int)(2*G.number_of_nodes()), 40000); - - std::vector< std::pair > undo_buffer; - undo_buffer.reserve(G.number_of_edges()); - - std::vector cur_state(G.number_of_nodes()); - int best_idx = -1; int round_counter = -1; unsigned iteration = 0; - - for( iteration = 0, round_counter = 0; iteration < config.maxIter; iteration++) { - if(!queue->empty()) { - Gain gain = queue->maxValue(); - std::pair< NodeID, PartitionID > p = queue->deleteMax(); - NodeID node = p.first; - NodeID block = p.second; - NodeID from = G.getPartitionIndex(node); - - if( boundary.getBlockWeight(block) + 1 < config.upper_bound_partition && from != block) { - boundary.setBlockWeight(from, boundary.getBlockWeight(from) - 1); - boundary.setBlockWeight(block, boundary.getBlockWeight(block) + 1); - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - gamma->set_xy( target, from, gamma->get_xy(target, from) - 1); - gamma->set_xy( target, block, gamma->get_xy(target, block) + 1); - } endfor - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID target_block = G.getPartitionIndex(target); - - for( unsigned i = 0; i < config.k; i++) { - if(queue->contains( target, i )) { - if( gamma->get_xy(target, i) == 0) { - queue->deleteNode(target, i); - } else { - queue->changeKey(target, i, gamma->get_xy(target, i) - gamma->get_xy(target, target_block)); - } - } else { - if( gamma->get_xy(target, i) > 0 && T->get_xy(target, i) < (int)iteration) { - queue->insert(target, i, gamma->get_xy(target, i) - gamma->get_xy(target, target_block)); - } - } - } - } endfor - - std::pair< NodeID, PartitionID > undo_move; - undo_move.first = node; - undo_move.second = G.getPartitionIndex(node); - - undo_buffer.push_back(undo_move); - round_counter++; - - G.setPartitionIndex(node, block); - - forall_out_edges(G, e, node) { - for( unsigned i = 0; i < config.k; i++) { - if(queue->contains( node, i)) { - if( gamma->get_xy(node, i) == 0) { - queue->deleteNode(node,i); - } else { - queue->changeKey(node, i, gamma->get_xy(node, i) - gamma->get_xy(node, block)); - } - } else { - if(gamma->get_xy(node, i) > 0 && T->get_xy(node, i) < (int)iteration) { - queue->insert(node, i, gamma->get_xy(node, i) - gamma->get_xy(node, block)); - } - } - } - } endfor - - cur_cut -= gain; - } - - - unsigned tenure = config.maxT;//random_functions::nextInt( config.maxT, 2*config.maxT); - tenure = compute_tenure(iteration, tenure); - unsigned small_offset = random_functions::nextInt(1,3); - T->set_xy(node, block, iteration + tenure + small_offset); - tabu_moves->insert(node, block, iteration + tenure + small_offset); - if( T->get_xy( node, from) < (int)iteration ) { - T->set_xy(node, from, iteration + tenure); - tabu_moves->insert(node, from, iteration + tenure); - } - - if(queue->contains(node, from) ) { - queue->deleteNode(node, from); - } - - if(queue->contains(node, block) ) { - queue->deleteNode(node, block); - } - - } - - //update the best cut found - if( cur_cut < best_cut ) { - best_idx = undo_buffer.size() - 1 ; - best_cut = cur_cut; - no_impro_iterations = 0; - } else { - no_impro_iterations++; - } - - - if( round_counter >= (int)G.number_of_edges() ) { - - if( best_idx != -1 ) { - forall_nodes(G, node) { - cur_state[node] = G.getPartitionIndex(node); - } endfor - - for( int idx = undo_buffer.size()-1; idx > best_idx; idx--) { - G.setPartitionIndex( undo_buffer[idx].first, undo_buffer[idx].second ); - } - forall_nodes(G, node) { - bestmap[node] = G.getPartitionIndex(node); - G.setPartitionIndex(node, cur_state[node]); - } endfor - } - undo_buffer.clear(); - best_idx = -1; - round_counter = -1; - } - - if(no_impro_iterations > iteration_limit) { - break; - } - - //reinsert the buffer - if( !tabu_moves->empty() ) { - while( tabu_moves->minValue() <= (int)iteration ) { - std::pair< NodeID, PartitionID > p = tabu_moves->deleteMin(); - NodeID node = p.first; - NodeID block = p.second; - - if( block == G.getPartitionIndex(node) ) { - unsigned tenure = compute_tenure(iteration, config.maxT); - T->set_xy(node, block, iteration + tenure); - tabu_moves->insert(node, block,iteration + tenure); - } else { - if(gamma->get_xy(node, block) > 0) { - queue->insert( p.first, p.second, gamma->get_xy(node, block) - gamma->get_xy(node, G.getPartitionIndex(node))); - } - } - } - } +} + undo_buffer.clear(); + best_idx = -1; + round_counter = -1; + } + + if(no_impro_iterations > iteration_limit) { + break; + } + + //reinsert the buffer + if( !tabu_moves->empty() ) { + while( tabu_moves->minValue() <= (int)iteration ) { + std::pair< NodeID, PartitionID > p = tabu_moves->deleteMin(); + NodeID node = p.first; + NodeID block = p.second; + + if( block == G.getPartitionIndex(node) ) { + unsigned tenure = compute_tenure(iteration, config.maxT); + T->set_xy(node, block, iteration + tenure); + tabu_moves->insert(node, block,iteration + tenure); + } else { + if(gamma->get_xy(node, block) > 0) { + queue->insert( p.first, p.second, gamma->get_xy(node, block) - gamma->get_xy(node, G.getPartitionIndex(node))); + } } - if( best_idx != -1 ) { - forall_nodes(G, node) { - cur_state[node] = G.getPartitionIndex(node); - } endfor + } + } + } + if( best_idx != -1 ) { + forall_nodes(G, node) { + cur_state[node] = G.getPartitionIndex(node); + } endfor - for( int idx = undo_buffer.size()-1; idx > best_idx; idx--) { - G.setPartitionIndex( undo_buffer[idx].first, undo_buffer[idx].second ); - } + for( int idx = undo_buffer.size()-1; idx > best_idx; idx--) { + G.setPartitionIndex( undo_buffer[idx].first, undo_buffer[idx].second ); + } - forall_nodes(G, node) { - bestmap[node] = G.getPartitionIndex(node); - G.setPartitionIndex(node, cur_state[node]); - } endfor + forall_nodes(G, node) { + bestmap[node] = G.getPartitionIndex(node); + G.setPartitionIndex(node, cur_state[node]); + } endfor - } +} - forall_nodes(G, node) { - G.setPartitionIndex(node, bestmap[node]); - } endfor + forall_nodes(G, node) { + G.setPartitionIndex(node, bestmap[node]); + } endfor - delete T; - delete gamma; - delete queue; - delete tabu_moves; + delete T; + delete gamma; + delete queue; + delete tabu_moves; - return 0; + return 0; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_search.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_search.h index c61c36a1..e70ef2dc 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_search.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/tabu_search/tabu_search.h @@ -13,54 +13,54 @@ #include "definitions.h" #include "uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class tabu_search : public refinement { - public: - tabu_search(); - virtual ~tabu_search(); +public: + tabu_search(); + virtual ~tabu_search(); - virtual EdgeWeight perform_refinement(PartitionConfig & config, - graph_access & G, - complete_boundary & boundary); + virtual EdgeWeight perform_refinement(PartitionConfig & config, + graph_access & G, + complete_boundary & boundary); - private: - unsigned compute_tenure(unsigned iteration, unsigned max_iteration) { - std::vector< double > b(15,0); - b[0] = 1/8.0; - b[1] = 2/8.0; - b[2] = 1/8.0; - b[3] = 4/8.0; - b[4] = 1/8.0; - b[5] = 2/8.0; - b[6] = 1/8.0; - b[7] = 8/8.0; - b[8] = 1/8.0; - b[9] = 2/8.0; - b[10] = 1/8.0; - b[11] = 4/8.0; - b[12] = 1/8.0; - b[13] = 2/8.0; - b[14] = 1/8.0; +private: + unsigned compute_tenure(unsigned iteration, unsigned max_iteration) { + std::vector< double > b(15,0); + b[0] = 1/8.0; + b[1] = 2/8.0; + b[2] = 1/8.0; + b[3] = 4/8.0; + b[4] = 1/8.0; + b[5] = 2/8.0; + b[6] = 1/8.0; + b[7] = 8/8.0; + b[8] = 1/8.0; + b[9] = 2/8.0; + b[10] = 1/8.0; + b[11] = 4/8.0; + b[12] = 1/8.0; + b[13] = 2/8.0; + b[14] = 1/8.0; - //compute i - unsigned i = 1; - unsigned x = 4*max_iteration*b[0]; - while( true ) { - if( iteration >= x ) { - x = x + 4*max_iteration*b[i%15]; - i++; - } else { - i--; - break; - } - } - return max_iteration*b[i%15]; - + //compute i + unsigned i = 1; + unsigned x = 4*max_iteration*b[0]; + while( true ) { + if( iteration >= x ) { + x = x + 4*max_iteration*b[i%15]; + i++; + } else { + i--; + break; + } } + return max_iteration*b[i%15]; - kway_graph_refinement_commons* commons; - matrix* m; -}; + } + kway_graph_refinement_commons* commons; + matrix* m; +}; +} #endif /* end of include guard: TABU_SEARCH_RC6W8GGX */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_algorithm.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_algorithm.cpp index 83de2245..a132aaec 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_algorithm.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_algorithm.cpp @@ -11,7 +11,7 @@ #include "uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.h" #include "vertex_separator_algorithm.h" #include "vertex_separator_flow_solver.h" - +namespace kahip::modified { vertex_separator_algorithm::vertex_separator_algorithm() { } @@ -25,135 +25,136 @@ void vertex_separator_algorithm::compute_vertex_separator(const PartitionConfig complete_boundary & boundary, std::vector & overall_separator) { - PartitionConfig cfg = config; - cfg.bank_account_factor = 1; + PartitionConfig cfg = config; + cfg.bank_account_factor = 1; + + QuotientGraphEdges qgraph_edges; + boundary.getQuotientGraphEdges(qgraph_edges); + + quotient_graph_scheduling* scheduler = new simple_quotient_graph_scheduler(cfg, qgraph_edges,qgraph_edges.size()); + + std::unordered_map allready_separator; + do { + boundary_pair & bp = scheduler->getNext(); + PartitionID lhs = bp.lhs; + PartitionID rhs = bp.rhs; + + boundary_starting_nodes start_nodes_lhs; + boundary_starting_nodes start_nodes_rhs; + + PartialBoundary & lhs_b = boundary.getDirectedBoundary(lhs, lhs, rhs); + PartialBoundary & rhs_b = boundary.getDirectedBoundary(rhs, lhs, rhs); + + forall_boundary_nodes(lhs_b, cur_bnd_node) { + if(allready_separator.find(cur_bnd_node) == allready_separator.end()) { + start_nodes_lhs.push_back(cur_bnd_node); + } + } endfor + + forall_boundary_nodes(rhs_b, cur_bnd_node) { + if(allready_separator.find(cur_bnd_node) == allready_separator.end()) { + start_nodes_rhs.push_back(cur_bnd_node); + } + } endfor + + vertex_separator_flow_solver vsfs; + std::vector separator; + vsfs.find_separator(config, G, lhs, rhs, start_nodes_lhs, start_nodes_rhs, separator); + for( unsigned i = 0; i < separator.size(); i++) { + allready_separator[separator[i]] = true; + } + //*************************** end **************************************** + } while(!scheduler->hasFinished()); + + + // now print the computed vertex separator to disk + std::unordered_map::iterator it; + for( it = allready_separator.begin(); it != allready_separator.end(); ++it) { + overall_separator.push_back(it->first); + } + is_vertex_separator(G, allready_separator); +} + +void vertex_separator_algorithm::compute_vertex_separator(const PartitionConfig & config, + graph_access & G, + complete_boundary & boundary) { - QuotientGraphEdges qgraph_edges; - boundary.getQuotientGraphEdges(qgraph_edges); + std::vector overall_separator; + compute_vertex_separator(config, G, boundary, overall_separator); - quotient_graph_scheduling* scheduler = new simple_quotient_graph_scheduler(cfg, qgraph_edges,qgraph_edges.size()); + // write the partition to the disc + std::stringstream filename; + filename << "tmpseparator" << config.k; + graph_io::writeVector(overall_separator, filename.str()); +} - std::unordered_map allready_separator; - do { - boundary_pair & bp = scheduler->getNext(); - PartitionID lhs = bp.lhs; - PartitionID rhs = bp.rhs; +void vertex_separator_algorithm::compute_vertex_separator_simple(const PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + std::vector & overall_separator) { - boundary_starting_nodes start_nodes_lhs; - boundary_starting_nodes start_nodes_rhs; + PartitionConfig cfg = config; + cfg.bank_account_factor = 1; - PartialBoundary & lhs_b = boundary.getDirectedBoundary(lhs, lhs, rhs); - PartialBoundary & rhs_b = boundary.getDirectedBoundary(rhs, lhs, rhs); + QuotientGraphEdges qgraph_edges; + boundary.getQuotientGraphEdges(qgraph_edges); - forall_boundary_nodes(lhs_b, cur_bnd_node) { - if(allready_separator.find(cur_bnd_node) == allready_separator.end()) { - start_nodes_lhs.push_back(cur_bnd_node); - } - } endfor + quotient_graph_scheduling* scheduler = new simple_quotient_graph_scheduler(cfg, qgraph_edges,qgraph_edges.size()); - forall_boundary_nodes(rhs_b, cur_bnd_node) { - if(allready_separator.find(cur_bnd_node) == allready_separator.end()) { - start_nodes_rhs.push_back(cur_bnd_node); - } - } endfor + std::unordered_map allready_separator; + do { + boundary_pair & bp = scheduler->getNext(); + PartitionID lhs = bp.lhs; + PartitionID rhs = bp.rhs; - vertex_separator_flow_solver vsfs; - std::vector separator; - vsfs.find_separator(config, G, lhs, rhs, start_nodes_lhs, start_nodes_rhs, separator); - for( unsigned i = 0; i < separator.size(); i++) { - allready_separator[separator[i]] = true; - } - //*************************** end **************************************** - } while(!scheduler->hasFinished()); + boundary_starting_nodes start_nodes_lhs; + boundary_starting_nodes start_nodes_rhs; + PartialBoundary & lhs_b = boundary.getDirectedBoundary(lhs, lhs, rhs); + PartialBoundary & rhs_b = boundary.getDirectedBoundary(rhs, lhs, rhs); - // now print the computed vertex separator to disk - std::unordered_map::iterator it; - for( it = allready_separator.begin(); it != allready_separator.end(); ++it) { - overall_separator.push_back(it->first); + if(lhs_b.size() < rhs_b.size()) { + forall_boundary_nodes(lhs_b, cur_bnd_node) { + if(allready_separator.find(cur_bnd_node) == allready_separator.end()) { + //overall_separator.push_back(cur_bnd_node); + allready_separator[cur_bnd_node] = true; } - is_vertex_separator(G, allready_separator); + } endfor +} else { + forall_boundary_nodes(rhs_b, cur_bnd_node) { + if(allready_separator.find(cur_bnd_node) == allready_separator.end()) { + //overall_separator.push_back(cur_bnd_node); + allready_separator[cur_bnd_node] = true; + } + } endfor } -void vertex_separator_algorithm::compute_vertex_separator(const PartitionConfig & config, - graph_access & G, - complete_boundary & boundary) { - - std::vector overall_separator; - compute_vertex_separator(config, G, boundary, overall_separator); - - // write the partition to the disc - std::stringstream filename; - filename << "tmpseparator" << config.k; - graph_io::writeVector(overall_separator, filename.str()); -} + //*************************** end **************************************** + } while(!scheduler->hasFinished()); -void vertex_separator_algorithm::compute_vertex_separator_simple(const PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - std::vector & overall_separator) { - PartitionConfig cfg = config; - cfg.bank_account_factor = 1; - - QuotientGraphEdges qgraph_edges; - boundary.getQuotientGraphEdges(qgraph_edges); - - quotient_graph_scheduling* scheduler = new simple_quotient_graph_scheduler(cfg, qgraph_edges,qgraph_edges.size()); - - std::unordered_map allready_separator; - do { - boundary_pair & bp = scheduler->getNext(); - PartitionID lhs = bp.lhs; - PartitionID rhs = bp.rhs; - - boundary_starting_nodes start_nodes_lhs; - boundary_starting_nodes start_nodes_rhs; - - PartialBoundary & lhs_b = boundary.getDirectedBoundary(lhs, lhs, rhs); - PartialBoundary & rhs_b = boundary.getDirectedBoundary(rhs, lhs, rhs); - - if(lhs_b.size() < rhs_b.size()) { - forall_boundary_nodes(lhs_b, cur_bnd_node) { - if(allready_separator.find(cur_bnd_node) == allready_separator.end()) { - //overall_separator.push_back(cur_bnd_node); - allready_separator[cur_bnd_node] = true; - } - } endfor - } else { - forall_boundary_nodes(rhs_b, cur_bnd_node) { - if(allready_separator.find(cur_bnd_node) == allready_separator.end()) { - //overall_separator.push_back(cur_bnd_node); - allready_separator[cur_bnd_node] = true; - } - } endfor - } - - //*************************** end **************************************** - } while(!scheduler->hasFinished()); - - - // now print the computed vertex separator to disk - std::unordered_map::iterator it; - for( it = allready_separator.begin(); it != allready_separator.end(); ++it) { - overall_separator.push_back(it->first); - } - is_vertex_separator(G, allready_separator); + // now print the computed vertex separator to disk + std::unordered_map::iterator it; + for( it = allready_separator.begin(); it != allready_separator.end(); ++it) { + overall_separator.push_back(it->first); + } + is_vertex_separator(G, allready_separator); } bool vertex_separator_algorithm::is_vertex_separator(graph_access & G, std::unordered_map & separator) { - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( G.getPartitionIndex(node) != G.getPartitionIndex(target)) { - // in this case one of them has to be a separator - if( separator.find(node) == separator.end() && - separator.find(target) == separator.end()) { - std::cout << "not a separator!" << std::endl; - ASSERT_TRUE(false); - } - } - } endfor - } endfor - return true; + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( G.getPartitionIndex(node) != G.getPartitionIndex(target)) { + // in this case one of them has to be a separator + if( separator.find(node) == separator.end() && + separator.find(target) == separator.end()) { + std::cout << "not a separator!" << std::endl; + ASSERT_TRUE(false); + } + } + } endfor +} endfor +return true; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_algorithm.h b/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_algorithm.h index a066ca8c..cda4028c 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_algorithm.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_algorithm.h @@ -13,30 +13,30 @@ #include "data_structure/graph_access.h" #include "partition_config.h" #include "uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h" - +namespace kahip::modified { class vertex_separator_algorithm { - public: - vertex_separator_algorithm(); - virtual ~vertex_separator_algorithm(); +public: + vertex_separator_algorithm(); + virtual ~vertex_separator_algorithm(); - void compute_vertex_separator(const PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - std::vector & overall_separator); + void compute_vertex_separator(const PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + std::vector & overall_separator); - void compute_vertex_separator_simple(const PartitionConfig & config, - graph_access & G, - complete_boundary & boundary, - std::vector & overall_separator); + void compute_vertex_separator_simple(const PartitionConfig & config, + graph_access & G, + complete_boundary & boundary, + std::vector & overall_separator); - void compute_vertex_separator(const PartitionConfig & config, - graph_access & G, - complete_boundary & boundary); + void compute_vertex_separator(const PartitionConfig & config, + graph_access & G, + complete_boundary & boundary); - //ASSERTIONS - bool is_vertex_separator(graph_access & G, std::unordered_map & separator); + //ASSERTIONS + bool is_vertex_separator(graph_access & G, std::unordered_map & separator); }; - +} #endif /* end of include guard: VERTEX_SEPARTATOR_ALGORITHM_XUDNZZM8 */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_flow_solver.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_flow_solver.cpp index cd435da3..cb53efe2 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_flow_solver.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_flow_solver.cpp @@ -12,7 +12,7 @@ #include "vertex_separator_flow_solver.h" #include "flow_solving_kernel/flow_macros.h" - +namespace kahip::modified { vertex_separator_flow_solver::vertex_separator_flow_solver() { } @@ -29,191 +29,191 @@ void vertex_separator_flow_solver::find_separator(const PartitionConfig & config boundary_starting_nodes rhs_nodes, std::vector & separator) { - if(lhs_nodes.size() == 0 || rhs_nodes.size() == 0) return; - - node *j = NULL; - int cc; - bucket *l; - - - globUpdtFreq = GLOB_UPDT_FREQ; - std::vector new_to_old_ids; - - EdgeID no_edges_in_flow_graph = 0; - bool success = construct_flow_pb(config, G, lhs, rhs, lhs_nodes, rhs_nodes, new_to_old_ids, - &n, - &m, - &nodes, - &arcs, - &cap, - &source, - &sink, - &nMin, - no_edges_in_flow_graph ); - - - cc = internal_allocDS(); - if(!success) return; - if ( cc ) { fprintf ( stderr, "Allocation error\n"); exit ( 1 ); } - - internal_init(); - internal_stage_one( ); - - internal_stage_two(); - - /* check if mincut is saturated */ - aMax = dMax = 0; - for (l = buckets; l < buckets + n; l++) { - l->firstActive = sentinelNode; - l->firstInactive = sentinelNode; - } - internal_global_update(); - - std::vector S; - forAllNodes(j) { - if (!(j->d < n)) { - if((unsigned) (nNode(j) - 1) < (int)lhs_nodes.size() + rhs_nodes.size()) { //Note: unsigned has been introduced without testing - S.push_back(new_to_old_ids[nNode(j) -1]); - } - } - } - - std::sort(lhs_nodes.begin(), lhs_nodes.end()); - std::sort(rhs_nodes.begin(), rhs_nodes.end()); - std::sort(S.begin(), S.end()); - - std::vector separator_tmp(lhs_nodes.size() + rhs_nodes.size(), -1); - std::vector::iterator it; - it = std::set_intersection(rhs_nodes.begin(), rhs_nodes.end(), S.begin(), S.end(), separator_tmp.begin()); - - - for( unsigned i = 0; i < separator_tmp.size(); i++) { - if(separator_tmp[i] != -1) { - separator.push_back(separator_tmp[i]); - } - } - std::vector::iterator it2; - std::vector separator_tmp2(lhs_nodes.size() + rhs_nodes.size(), -1); - it2 = std::set_difference(lhs_nodes.begin(), lhs_nodes.end(), S.begin(), S.end(), separator_tmp2.begin()); - for( unsigned i = 0; i < separator_tmp2.size(); i++) { - if(separator_tmp2[i] != -1) { - separator.push_back(separator_tmp2[i]); - } - } + if(lhs_nodes.size() == 0 || rhs_nodes.size() == 0) return; + + node *j = NULL; + int cc; + bucket *l; + + + globUpdtFreq = GLOB_UPDT_FREQ; + std::vector new_to_old_ids; + + EdgeID no_edges_in_flow_graph = 0; + bool success = construct_flow_pb(config, G, lhs, rhs, lhs_nodes, rhs_nodes, new_to_old_ids, + &n, + &m, + &nodes, + &arcs, + &cap, + &source, + &sink, + &nMin, + no_edges_in_flow_graph ); + + + cc = internal_allocDS(); + if(!success) return; + if ( cc ) { fprintf ( stderr, "Allocation error\n"); exit ( 1 ); } + + internal_init(); + internal_stage_one( ); + + internal_stage_two(); + + /* check if mincut is saturated */ + aMax = dMax = 0; + for (l = buckets; l < buckets + n; l++) { + l->firstActive = sentinelNode; + l->firstInactive = sentinelNode; + } + internal_global_update(); + + std::vector S; + forAllNodes(j) { + if (!(j->d < n)) { + if((unsigned) (nNode(j) - 1) < (int)lhs_nodes.size() + rhs_nodes.size()) { //Note: unsigned has been introduced without testing + S.push_back(new_to_old_ids[nNode(j) -1]); + } + } + } + + std::sort(lhs_nodes.begin(), lhs_nodes.end()); + std::sort(rhs_nodes.begin(), rhs_nodes.end()); + std::sort(S.begin(), S.end()); + + std::vector separator_tmp(lhs_nodes.size() + rhs_nodes.size(), -1); + std::vector::iterator it; + it = std::set_intersection(rhs_nodes.begin(), rhs_nodes.end(), S.begin(), S.end(), separator_tmp.begin()); + + + for( unsigned i = 0; i < separator_tmp.size(); i++) { + if(separator_tmp[i] != -1) { + separator.push_back(separator_tmp[i]); + } + } + std::vector::iterator it2; + std::vector separator_tmp2(lhs_nodes.size() + rhs_nodes.size(), -1); + it2 = std::set_difference(lhs_nodes.begin(), lhs_nodes.end(), S.begin(), S.end(), separator_tmp2.begin()); + for( unsigned i = 0; i < separator_tmp2.size(); i++) { + if(separator_tmp2[i] != -1) { + separator.push_back(separator_tmp2[i]); + } + } } -bool vertex_separator_flow_solver::construct_flow_pb( const PartitionConfig & config, - graph_access & G, - PartitionID & lhs, - PartitionID & rhs, +bool vertex_separator_flow_solver::construct_flow_pb( const PartitionConfig & config, + graph_access & G, + PartitionID & lhs, + PartitionID & rhs, std::vector & lhs_nodes, std::vector & rhs_nodes, - std::vector & new_to_old_ids, - long *n_ad, - long* m_ad, - node** nodes_ad, - arc** arcs_ad, + std::vector & new_to_old_ids, + long *n_ad, + long* m_ad, + node** nodes_ad, + arc** arcs_ad, long ** cap_ad, - node** source_ad, - node** sink_ad, + node** source_ad, + node** sink_ad, long* node_min_ad, - EdgeID & no_edges_in_flow_graph) { - - //very dirty for loading variables :). some time this should all be refactored. for now we can focus on the important stuff. - #include "../refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/convert_ds_variables.h" - - //building up the graph as in parse.h of hi_pr code - //first we have to count the number of edges - // s to lhs + rhs to t + lhs to rhs - unsigned no_edges = 0; - for( unsigned i = 0; i < lhs_nodes.size(); i++) { - NodeID node = lhs_nodes[i]; - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(rhs == G.getPartitionIndex(target)) { - ++no_edges; - } - } endfor - } - - //build mappings from old to new node ids and reverse - NodeID idx = 0; - new_to_old_ids.resize(lhs_nodes.size() + rhs_nodes.size()); - std::unordered_map old_to_new; - for( unsigned i = 0; i < lhs_nodes.size(); i++) { - new_to_old_ids[idx] = lhs_nodes[i]; - old_to_new[lhs_nodes[i]] = idx++ ; - } - for( unsigned i = 0; i < rhs_nodes.size(); i++) { - new_to_old_ids[idx] = rhs_nodes[i]; - old_to_new[rhs_nodes[i]] = idx++; - } - - n = lhs_nodes.size() + rhs_nodes.size() + 2; //+source and target - m = no_edges + lhs_nodes.size() + rhs_nodes.size(); - - nodes = (node*) calloc ( n+2, sizeof(node) ); - arcs = (arc*) calloc ( 2*m+1, sizeof(arc) ); - arc_tail = (long*) calloc ( 2*m, sizeof(long) ); - arc_first= (long*) calloc ( n+2, sizeof(long) ); - acap = (long*) calloc ( 2*m, sizeof(long) ); - arc_current = arcs; - - node_max = 0; - node_min = n; - - if(n == 2) return false; - - unsigned nodeoffset = 1; - source = n - 2 + nodeoffset; - sink = source+1; - - idx = 0; - long max_capacity = std::numeric_limits::max(); - //insert directed edges from L to R - for( unsigned i = 0; i < lhs_nodes.size(); i++, idx++) { - NodeID node = lhs_nodes[i]; - NodeID sourceID = idx + nodeoffset; - forall_out_edges(G, e, node) { - if(G.getPartitionIndex(G.getEdgeTarget(e)) == rhs) { - NodeID targetID = old_to_new[G.getEdgeTarget(e)] + nodeoffset; - tail = sourceID; - head = targetID; - cap = max_capacity; - - createEdge() - } - } endfor - } - - //connect source and target with outer boundary nodes - for(unsigned i = 0; i < lhs_nodes.size(); i++) { - NodeID targetID = old_to_new[lhs_nodes[i]]+nodeoffset; - tail = source; - head = targetID; - cap = G.getNodeWeight(lhs_nodes[i]); - - createEdge() - } - - for(unsigned i = 0; i < rhs_nodes.size(); i++) { - NodeID sourceID = old_to_new[rhs_nodes[i]]+ nodeoffset; - tail = sourceID; - head = sink; - cap = G.getNodeWeight(rhs_nodes[i]); - - createEdge() - } - - //very dirty - #include "../refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/linear_ordering_n_assign.h" - /* Thanks God! all is done */ - - return true; + EdgeID & no_edges_in_flow_graph) { + + //very dirty for loading variables :). some time this should all be refactored. for now we can focus on the important stuff. +#include "../refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/convert_ds_variables.h" + + //building up the graph as in parse.h of hi_pr code + //first we have to count the number of edges + // s to lhs + rhs to t + lhs to rhs + unsigned no_edges = 0; + for( unsigned i = 0; i < lhs_nodes.size(); i++) { + NodeID node = lhs_nodes[i]; + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(rhs == G.getPartitionIndex(target)) { + ++no_edges; + } + } endfor +} + //build mappings from old to new node ids and reverse + NodeID idx = 0; + new_to_old_ids.resize(lhs_nodes.size() + rhs_nodes.size()); + std::unordered_map old_to_new; + for( unsigned i = 0; i < lhs_nodes.size(); i++) { + new_to_old_ids[idx] = lhs_nodes[i]; + old_to_new[lhs_nodes[i]] = idx++ ; + } + for( unsigned i = 0; i < rhs_nodes.size(); i++) { + new_to_old_ids[idx] = rhs_nodes[i]; + old_to_new[rhs_nodes[i]] = idx++; + } + + n = lhs_nodes.size() + rhs_nodes.size() + 2; //+source and target + m = no_edges + lhs_nodes.size() + rhs_nodes.size(); + + nodes = (node*) calloc ( n+2, sizeof(node) ); + arcs = (arc*) calloc ( 2*m+1, sizeof(arc) ); + arc_tail = (long*) calloc ( 2*m, sizeof(long) ); + arc_first= (long*) calloc ( n+2, sizeof(long) ); + acap = (long*) calloc ( 2*m, sizeof(long) ); + arc_current = arcs; + + node_max = 0; + node_min = n; + + if(n == 2) return false; + + unsigned nodeoffset = 1; + source = n - 2 + nodeoffset; + sink = source+1; + + idx = 0; + long max_capacity = std::numeric_limits::max(); + //insert directed edges from L to R + for( unsigned i = 0; i < lhs_nodes.size(); i++, idx++) { + NodeID node = lhs_nodes[i]; + NodeID sourceID = idx + nodeoffset; + forall_out_edges(G, e, node) { + if(G.getPartitionIndex(G.getEdgeTarget(e)) == rhs) { + NodeID targetID = old_to_new[G.getEdgeTarget(e)] + nodeoffset; + tail = sourceID; + head = targetID; + cap = max_capacity; + + createEdge() +} + } endfor +} + + //connect source and target with outer boundary nodes + for(unsigned i = 0; i < lhs_nodes.size(); i++) { + NodeID targetID = old_to_new[lhs_nodes[i]]+nodeoffset; + tail = source; + head = targetID; + cap = G.getNodeWeight(lhs_nodes[i]); + + createEdge() +} + for(unsigned i = 0; i < rhs_nodes.size(); i++) { + NodeID sourceID = old_to_new[rhs_nodes[i]]+ nodeoffset; + tail = sourceID; + head = sink; + cap = G.getNodeWeight(rhs_nodes[i]); + createEdge() } + //very dirty +#include "../refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/linear_ordering_n_assign.h" + /* Thanks God! all is done */ + + return true; + + + +} +} diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_flow_solver.h b/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_flow_solver.h index 49b0e947..b9562073 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_flow_solver.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/separator/vertex_separator_flow_solver.h @@ -9,39 +9,39 @@ #define VERTEX_SEPARATOR_FLOW_SOLVER_FLA4518Q #include "flow_solving_kernel/flow_solver.h" - +namespace kahip::modified { class vertex_separator_flow_solver : public flow_solver { public: - vertex_separator_flow_solver(); - virtual ~vertex_separator_flow_solver(); - - bool construct_flow_pb( const PartitionConfig & config, - graph_access & G, - PartitionID & lhs, - PartitionID & rhs, - std::vector & lhs_boundary_stripe, - std::vector & rhs_boundary_stripe, - std::vector & new_to_old_ids, - long *n_ad, - long* m_ad, - node** nodes_ad, - arc** arcs_ad, - long ** cap_ad, - node** source_ad, - node** sink_ad, - long* node_min_ad, - EdgeID & no_edges_in_flow_graph); - - - void find_separator(const PartitionConfig & config, - graph_access & G, - PartitionID lhs, - PartitionID rhs, - boundary_starting_nodes start_nodes_lhs, - boundary_starting_nodes start_nodes_rhs, - std::vector & separator); + vertex_separator_flow_solver(); + virtual ~vertex_separator_flow_solver(); + + bool construct_flow_pb( const PartitionConfig & config, + graph_access & G, + PartitionID & lhs, + PartitionID & rhs, + std::vector & lhs_boundary_stripe, + std::vector & rhs_boundary_stripe, + std::vector & new_to_old_ids, + long *n_ad, + long* m_ad, + node** nodes_ad, + arc** arcs_ad, + long ** cap_ad, + node** source_ad, + node** sink_ad, + long* node_min_ad, + EdgeID & no_edges_in_flow_graph); + + + void find_separator(const PartitionConfig & config, + graph_access & G, + PartitionID lhs, + PartitionID rhs, + boundary_starting_nodes start_nodes_lhs, + boundary_starting_nodes start_nodes_rhs, + std::vector & separator); }; - +} #endif /* end of include guard: VERTEX_SEPARATOR_FLOW_SOLVER_FLA4518Q */ diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/uncoarsening.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/uncoarsening.cpp index 978a292c..b6780573 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/uncoarsening.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/uncoarsening.cpp @@ -13,8 +13,7 @@ #include "refinement/refinement.h" #include "separator/vertex_separator_algorithm.h" #include "uncoarsening.h" - - +namespace kahip::modified { uncoarsening::uncoarsening() { } @@ -24,83 +23,83 @@ uncoarsening::~uncoarsening() { } int uncoarsening::perform_uncoarsening(const PartitionConfig & config, graph_hierarchy & hierarchy) { - int improvement = 0; - - PartitionConfig cfg = config; - refinement* refine = NULL; - - if(config.label_propagation_refinement) { - refine = new label_propagation_refinement(); - } else { - refine = new mixed_refinement(); - } - - graph_access * coarsest = hierarchy.get_coarsest(); - PRINT(std::cout << "log>" << "unrolling graph with " << coarsest->number_of_nodes() << std::endl;) - - complete_boundary* finer_boundary = NULL; - complete_boundary* coarser_boundary = NULL; - if(!config.label_propagation_refinement) { - coarser_boundary = new complete_boundary(coarsest); - coarser_boundary->build(); - } - double factor = config.balance_factor; - cfg.upper_bound_partition = ((!hierarchy.isEmpty()) * factor +1.0)*config.upper_bound_partition; - improvement += (int)refine->perform_refinement(cfg, *coarsest, *coarser_boundary); - - NodeID coarser_no_nodes = coarsest->number_of_nodes(); - graph_access* finest = NULL; - graph_access* to_delete = NULL; - unsigned int hierarchy_deepth = hierarchy.size(); - - while(!hierarchy.isEmpty()) { - graph_access* G = hierarchy.pop_finer_and_project(); - - PRINT(std::cout << "log>" << "unrolling graph with " << G->number_of_nodes()<< std::endl;) - - if(!config.label_propagation_refinement) { - finer_boundary = new complete_boundary(G); - finer_boundary->build_from_coarser(coarser_boundary, coarser_no_nodes, hierarchy.get_mapping_of_current_finer()); - } - - //call refinement - double cur_factor = factor/(hierarchy_deepth-hierarchy.size()); - cfg.upper_bound_partition = ((!hierarchy.isEmpty()) * cur_factor+1.0)*config.upper_bound_partition; - PRINT(std::cout << "cfg upperbound " << cfg.upper_bound_partition << std::endl;) - improvement += (int)refine->perform_refinement(cfg, *G, *finer_boundary); - ASSERT_TRUE(graph_partition_assertions::assert_graph_has_kway_partition(config, *G)); - - if(config.use_balance_singletons && !config.label_propagation_refinement) { - finer_boundary->balance_singletons( config, *G ); - } - - // update boundary pointers - if(!config.label_propagation_refinement) delete coarser_boundary; - coarser_boundary = finer_boundary; - coarser_no_nodes = G->number_of_nodes(); - - //clean up - if(to_delete != NULL) { - delete to_delete; - } - if(!hierarchy.isEmpty()) { - to_delete = G; - } - - finest = G; - } - - if(config.compute_vertex_separator) { - PRINT(std::cout << "now computing a vertex separator from the given edge separator" << std::endl;) - vertex_separator_algorithm vsa; - vsa.compute_vertex_separator(config, *finest, *finer_boundary); - } - - delete refine; - if(finer_boundary != NULL) delete finer_boundary; - delete coarsest; - - return improvement; + int improvement = 0; + + PartitionConfig cfg = config; + refinement* refine = NULL; + + if(config.label_propagation_refinement) { + refine = new label_propagation_refinement(); + } else { + refine = new mixed_refinement(); + } + + graph_access * coarsest = hierarchy.get_coarsest(); + PRINT(std::cout << "log>" << "unrolling graph with " << coarsest->number_of_nodes() << std::endl;) + + complete_boundary* finer_boundary = NULL; + complete_boundary* coarser_boundary = NULL; + if(!config.label_propagation_refinement) { + coarser_boundary = new complete_boundary(coarsest); + coarser_boundary->build(); + } + double factor = config.balance_factor; + cfg.upper_bound_partition = ((!hierarchy.isEmpty()) * factor +1.0)*config.upper_bound_partition; + improvement += (int)refine->perform_refinement(cfg, *coarsest, *coarser_boundary); + + NodeID coarser_no_nodes = coarsest->number_of_nodes(); + graph_access* finest = NULL; + graph_access* to_delete = NULL; + unsigned int hierarchy_deepth = hierarchy.size(); + + while(!hierarchy.isEmpty()) { + graph_access* G = hierarchy.pop_finer_and_project(); + + PRINT(std::cout << "log>" << "unrolling graph with " << G->number_of_nodes()<< std::endl;) + + if(!config.label_propagation_refinement) { + finer_boundary = new complete_boundary(G); + finer_boundary->build_from_coarser(coarser_boundary, coarser_no_nodes, hierarchy.get_mapping_of_current_finer()); + } + + //call refinement + double cur_factor = factor/(hierarchy_deepth-hierarchy.size()); + cfg.upper_bound_partition = ((!hierarchy.isEmpty()) * cur_factor+1.0)*config.upper_bound_partition; + PRINT(std::cout << "cfg upperbound " << cfg.upper_bound_partition << std::endl;) + improvement += (int)refine->perform_refinement(cfg, *G, *finer_boundary); + ASSERT_TRUE(graph_partition_assertions::assert_graph_has_kway_partition(config, *G)); + + if(config.use_balance_singletons && !config.label_propagation_refinement) { + finer_boundary->balance_singletons( config, *G ); + } + + // update boundary pointers + if(!config.label_propagation_refinement) delete coarser_boundary; + coarser_boundary = finer_boundary; + coarser_no_nodes = G->number_of_nodes(); + + //clean up + if(to_delete != NULL) { + delete to_delete; + } + if(!hierarchy.isEmpty()) { + to_delete = G; + } + + finest = G; + } + + if(config.compute_vertex_separator) { + PRINT(std::cout << "now computing a vertex separator from the given edge separator" << std::endl;) + vertex_separator_algorithm vsa; + vsa.compute_vertex_separator(config, *finest, *finer_boundary); + } + + delete refine; + if(finer_boundary != NULL) delete finer_boundary; + delete coarsest; + + return improvement; +} } - diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/uncoarsening.h b/parallel/modified_kahip/lib/partition/uncoarsening/uncoarsening.h index c08247d0..69321e8a 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/uncoarsening.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/uncoarsening.h @@ -10,14 +10,14 @@ #include "data_structure/graph_hierarchy.h" #include "partition_config.h" - +namespace kahip::modified { class uncoarsening { public: - uncoarsening( ); - virtual ~uncoarsening(); - - int perform_uncoarsening(const PartitionConfig & config, graph_hierarchy & hierarchy); -}; + uncoarsening( ); + virtual ~uncoarsening(); + int perform_uncoarsening(const PartitionConfig & config, graph_hierarchy & hierarchy); +}; +} #endif /* end of include guard: UNCOARSENING_XSN847F2 */ diff --git a/parallel/modified_kahip/lib/partition/w_cycles/wcycle_partitioner.cpp b/parallel/modified_kahip/lib/partition/w_cycles/wcycle_partitioner.cpp index 41899c89..637edf5f 100644 --- a/parallel/modified_kahip/lib/partition/w_cycles/wcycle_partitioner.cpp +++ b/parallel/modified_kahip/lib/partition/w_cycles/wcycle_partitioner.cpp @@ -21,177 +21,178 @@ #include "uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.h" #include "uncoarsening/refinement/refinement.h" #include "wcycle_partitioner.h" - +namespace kahip::modified { int wcycle_partitioner::perform_partitioning(const PartitionConfig & config, graph_access & G) { - PartitionConfig cfg = config; + PartitionConfig cfg = config; - if(config.stop_rule == STOP_RULE_SIMPLE) { - m_coarsening_stop_rule = new simple_stop_rule(cfg, G.number_of_nodes()); - } else { - m_coarsening_stop_rule = new multiple_k_stop_rule(cfg, G.number_of_nodes()); - } + if(config.stop_rule == STOP_RULE_SIMPLE) { + m_coarsening_stop_rule = new simple_stop_rule(cfg, G.number_of_nodes()); + } else { + m_coarsening_stop_rule = new multiple_k_stop_rule(cfg, G.number_of_nodes()); + } - int improvement = (int) perform_partitioning_recursive(cfg, G, NULL); - delete m_coarsening_stop_rule; + int improvement = (int) perform_partitioning_recursive(cfg, G, NULL); + delete m_coarsening_stop_rule; - return improvement; + return improvement; } -int wcycle_partitioner::perform_partitioning_recursive( PartitionConfig & partition_config, - graph_access & G, +int wcycle_partitioner::perform_partitioning_recursive( PartitionConfig & partition_config, + graph_access & G, complete_boundary ** c_boundary) { - //if graph not small enough - // perform matching two times - // perform coarsening two times - // call rekursive - //else - // initial partitioning - // - //refinement - NodeID no_of_coarser_vertices = G.number_of_nodes(); - NodeID no_of_finer_vertices = G.number_of_nodes(); - int improvement = 0; - - edge_ratings rating(partition_config); - CoarseMapping* coarse_mapping = new CoarseMapping(); - - graph_access* finer = &G; - matching* edge_matcher = NULL; - contraction* contracter = new contraction(); - PartitionConfig copy_of_partition_config = partition_config; - graph_access* coarser = new graph_access(); - - Matching edge_matching; - NodePermutationMap permutation; - - coarsening_configurator coarsening_config; - coarsening_config.configure_coarsening(partition_config, &edge_matcher, m_level); - - rating.rate(*finer, m_level); - - edge_matcher->match(partition_config, *finer, edge_matching, *coarse_mapping, no_of_coarser_vertices, permutation); - delete edge_matcher; - - if(partition_config.graph_allready_partitioned) { - contracter->contract_partitioned(partition_config, *finer, - *coarser, edge_matching, - *coarse_mapping, no_of_coarser_vertices, - permutation); - } else { - contracter->contract(partition_config, *finer, - *coarser, edge_matching, - *coarse_mapping, no_of_coarser_vertices, + //if graph not small enough + // perform matching two times + // perform coarsening two times + // call rekursive + //else + // initial partitioning + // + //refinement + NodeID no_of_coarser_vertices = G.number_of_nodes(); + NodeID no_of_finer_vertices = G.number_of_nodes(); + int improvement = 0; + + edge_ratings rating(partition_config); + CoarseMapping* coarse_mapping = new CoarseMapping(); + + graph_access* finer = &G; + matching* edge_matcher = NULL; + contraction* contracter = new contraction(); + PartitionConfig copy_of_partition_config = partition_config; + graph_access* coarser = new graph_access(); + + Matching edge_matching; + NodePermutationMap permutation; + + coarsening_configurator coarsening_config; + coarsening_config.configure_coarsening(partition_config, &edge_matcher, m_level); + + rating.rate(*finer, m_level); + + edge_matcher->match(partition_config, *finer, edge_matching, *coarse_mapping, no_of_coarser_vertices, permutation); + delete edge_matcher; + + if(partition_config.graph_allready_partitioned) { + contracter->contract_partitioned(partition_config, *finer, + *coarser, edge_matching, + *coarse_mapping, no_of_coarser_vertices, permutation); - } - - coarser->set_partition_count(partition_config.k); - complete_boundary* coarser_boundary = NULL; - refinement* refine = NULL; - - if(!partition_config.label_propagation_refinement) { - coarser_boundary = new complete_boundary(coarser); - refine = new mixed_refinement(); - } else { - refine = new label_propagation_refinement(); - } - - if(!m_coarsening_stop_rule->stop(no_of_finer_vertices, no_of_coarser_vertices)) { - - PartitionConfig cfg; cfg = partition_config; - - double factor = partition_config.balance_factor; - cfg.upper_bound_partition = (factor +1.0)*partition_config.upper_bound_partition; + } else { + contracter->contract(partition_config, *finer, + *coarser, edge_matching, + *coarse_mapping, no_of_coarser_vertices, + permutation); + } - initial_partitioning init_part; - init_part.perform_initial_partitioning(cfg, *coarser); + coarser->set_partition_count(partition_config.k); + complete_boundary* coarser_boundary = NULL; + refinement* refine = NULL; - if(!partition_config.label_propagation_refinement) coarser_boundary->build(); + if(!partition_config.label_propagation_refinement) { + coarser_boundary = new complete_boundary(coarser); + refine = new mixed_refinement(); + } else { + refine = new label_propagation_refinement(); + } - improvement += refine->perform_refinement(cfg, *coarser, *coarser_boundary); - m_deepest_level = m_level + 1; - } else { - m_level++; + if(!m_coarsening_stop_rule->stop(no_of_finer_vertices, no_of_coarser_vertices)) { - improvement += perform_partitioning_recursive( partition_config, *coarser, &coarser_boundary); - partition_config.graph_allready_partitioned = true; + PartitionConfig cfg; cfg = partition_config; - if(m_level % partition_config.level_split == 0 ) { + double factor = partition_config.balance_factor; + cfg.upper_bound_partition = (factor +1.0)*partition_config.upper_bound_partition; - if(!partition_config.use_fullmultigrid - || m_have_been_level_down.find(m_level) == m_have_been_level_down.end()) { + initial_partitioning init_part; + init_part.perform_initial_partitioning(cfg, *coarser); - if(!partition_config.label_propagation_refinement) { - delete coarser_boundary; + if(!partition_config.label_propagation_refinement) coarser_boundary->build(); - coarser_boundary = new complete_boundary(coarser); - } - m_have_been_level_down[m_level] = true; + improvement += refine->perform_refinement(cfg, *coarser, *coarser_boundary); + m_deepest_level = m_level + 1; + } else { + m_level++; - // configurate the algorithm to use the same amount - // of imbalance as was allowed on this level - PartitionConfig cfg; - cfg = partition_config; - cfg.set_upperbound = false; + improvement += perform_partitioning_recursive( partition_config, *coarser, &coarser_boundary); + partition_config.graph_allready_partitioned = true; - double cur_factor = partition_config.balance_factor/(m_deepest_level-m_level); - cfg.upper_bound_partition = ( (m_level != 0) * cur_factor+1.0)*partition_config.upper_bound_partition; + if(m_level % partition_config.level_split == 0 ) { - // do the next arm of the F-cycle - improvement += perform_partitioning_recursive( cfg, *coarser, &coarser_boundary); - } - } + if(!partition_config.use_fullmultigrid + || m_have_been_level_down.find(m_level) == m_have_been_level_down.end()) { - m_level--; - - } - - if(partition_config.use_balance_singletons && !partition_config.label_propagation_refinement) { - coarser_boundary->balance_singletons( partition_config, *coarser ); - } - - //project - graph_access& fRef = *finer; - graph_access& cRef = *coarser; - forall_nodes(fRef, n) { - NodeID coarser_node = (*coarse_mapping)[n]; - PartitionID coarser_partition_id = cRef.getPartitionIndex(coarser_node); - fRef.setPartitionIndex(n, coarser_partition_id); - } endfor - - finer->set_partition_count(coarser->get_partition_count()); - complete_boundary* current_boundary = NULL; if(!partition_config.label_propagation_refinement) { - current_boundary = new complete_boundary(finer); - current_boundary->build_from_coarser(coarser_boundary, no_of_coarser_vertices, coarse_mapping ); - } + delete coarser_boundary; - PartitionConfig cfg; cfg = partition_config; - double cur_factor = partition_config.balance_factor/(m_deepest_level-m_level); - - //only set the upperbound if it is the first time - //we go down the F-cycle - if( partition_config.set_upperbound ) { - cfg.upper_bound_partition = ( (m_level != 0) * cur_factor+1.0)*partition_config.upper_bound_partition; - } else { - cfg.upper_bound_partition = partition_config.upper_bound_partition; + coarser_boundary = new complete_boundary(coarser); } + m_have_been_level_down[m_level] = true; - improvement += refine->perform_refinement(cfg, *finer, *current_boundary); - - if(c_boundary != NULL) { - delete *c_boundary; - *c_boundary = current_boundary; - } else { - if( current_boundary != NULL ) delete current_boundary; - } + // configurate the algorithm to use the same amount + // of imbalance as was allowed on this level + PartitionConfig cfg; + cfg = partition_config; + cfg.set_upperbound = false; - //std::cout << "finer " << no_of_finer_vertices << std::endl; - delete contracter; - delete coarse_mapping; - delete coarser_boundary; - delete coarser; - delete refine; - - return improvement; + double cur_factor = partition_config.balance_factor/(m_deepest_level-m_level); + cfg.upper_bound_partition = ( (m_level != 0) * cur_factor+1.0)*partition_config.upper_bound_partition; + + // do the next arm of the F-cycle + improvement += perform_partitioning_recursive( cfg, *coarser, &coarser_boundary); + } + } + + m_level--; + + } + + if(partition_config.use_balance_singletons && !partition_config.label_propagation_refinement) { + coarser_boundary->balance_singletons( partition_config, *coarser ); + } + + //project + graph_access& fRef = *finer; + graph_access& cRef = *coarser; + forall_nodes(fRef, n) { + NodeID coarser_node = (*coarse_mapping)[n]; + PartitionID coarser_partition_id = cRef.getPartitionIndex(coarser_node); + fRef.setPartitionIndex(n, coarser_partition_id); + } endfor + + finer->set_partition_count(coarser->get_partition_count()); + complete_boundary* current_boundary = NULL; + if(!partition_config.label_propagation_refinement) { + current_boundary = new complete_boundary(finer); + current_boundary->build_from_coarser(coarser_boundary, no_of_coarser_vertices, coarse_mapping ); + } + + PartitionConfig cfg; cfg = partition_config; + double cur_factor = partition_config.balance_factor/(m_deepest_level-m_level); + + //only set the upperbound if it is the first time + //we go down the F-cycle + if( partition_config.set_upperbound ) { + cfg.upper_bound_partition = ( (m_level != 0) * cur_factor+1.0)*partition_config.upper_bound_partition; + } else { + cfg.upper_bound_partition = partition_config.upper_bound_partition; + } + + improvement += refine->perform_refinement(cfg, *finer, *current_boundary); + + if(c_boundary != NULL) { + delete *c_boundary; + *c_boundary = current_boundary; + } else { + if( current_boundary != NULL ) delete current_boundary; + } + + //std::cout << "finer " << no_of_finer_vertices << std::endl; + delete contracter; + delete coarse_mapping; + delete coarser_boundary; + delete coarser; + delete refine; + + return improvement; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/w_cycles/wcycle_partitioner.h b/parallel/modified_kahip/lib/partition/w_cycles/wcycle_partitioner.h index 2bf78d62..21deef21 100644 --- a/parallel/modified_kahip/lib/partition/w_cycles/wcycle_partitioner.h +++ b/parallel/modified_kahip/lib/partition/w_cycles/wcycle_partitioner.h @@ -13,24 +13,24 @@ #include "data_structure/graph_access.h" #include "partition_config.h" #include "uncoarsening/refinement/refinement.h" - +namespace kahip::modified { class wcycle_partitioner { - public: - wcycle_partitioner( ) : m_level(0) {}; - virtual ~wcycle_partitioner() {}; - int perform_partitioning( const PartitionConfig & config, - graph_access & G); +public: + wcycle_partitioner( ) : m_level(0) {}; + virtual ~wcycle_partitioner() {}; + int perform_partitioning( const PartitionConfig & config, + graph_access & G); - private: - int perform_partitioning_recursive( PartitionConfig & partition_config, - graph_access & G, - complete_boundary ** c_boundary); +private: + int perform_partitioning_recursive( PartitionConfig & partition_config, + graph_access & G, + complete_boundary ** c_boundary); - unsigned m_level; - unsigned m_deepest_level; - stop_rule* m_coarsening_stop_rule; + unsigned m_level; + unsigned m_deepest_level; + stop_rule* m_coarsening_stop_rule; - std::unordered_map m_have_been_level_down; + std::unordered_map m_have_been_level_down; }; - +} #endif /* end of include guard: WCYCLE_PARTITIONER_EPNDQMK */ diff --git a/parallel/modified_kahip/lib/tools/graph_communication.cpp b/parallel/modified_kahip/lib/tools/graph_communication.cpp index 9ecdff85..d40caffc 100644 --- a/parallel/modified_kahip/lib/tools/graph_communication.cpp +++ b/parallel/modified_kahip/lib/tools/graph_communication.cpp @@ -8,7 +8,7 @@ #include #include "graph_communication.h" - +namespace kahip::modified { graph_communication::graph_communication() { } @@ -18,52 +18,53 @@ graph_communication::~graph_communication() { } void graph_communication::broadcast_graph( graph_access & G, unsigned root) { - int rank; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); - //first B-Cast number of nodes and number of edges - unsigned number_of_nodes = 0; - unsigned number_of_edges = 0; - - std::vector< int > buffer(2,0); - if(rank == (int)root) { - buffer[0] = G.number_of_nodes(); - buffer[1] = G.number_of_edges(); - } - MPI_Bcast(&buffer[0], 2, MPI_INT, root, MPI_COMM_WORLD); + //first B-Cast number of nodes and number of edges + unsigned number_of_nodes = 0; + unsigned number_of_edges = 0; + + std::vector< int > buffer(2,0); + if(rank == (int)root) { + buffer[0] = G.number_of_nodes(); + buffer[1] = G.number_of_edges(); + } + MPI_Bcast(&buffer[0], 2, MPI_INT, root, MPI_COMM_WORLD); - number_of_nodes = buffer[0]; - number_of_edges = buffer[1]; + number_of_nodes = buffer[0]; + number_of_edges = buffer[1]; - int* xadj; - int* adjncy; - int* vwgt; - int* adjwgt; + int* xadj; + int* adjncy; + int* vwgt; + int* adjwgt; - if( rank == (int)root) { - xadj = G.UNSAFE_metis_style_xadj_array(); - adjncy = G.UNSAFE_metis_style_adjncy_array(); + if( rank == (int)root) { + xadj = G.UNSAFE_metis_style_xadj_array(); + adjncy = G.UNSAFE_metis_style_adjncy_array(); - vwgt = G.UNSAFE_metis_style_vwgt_array(); - adjwgt = G.UNSAFE_metis_style_adjwgt_array(); - } else { - xadj = new int[number_of_nodes+1]; - adjncy = new int[number_of_edges]; + vwgt = G.UNSAFE_metis_style_vwgt_array(); + adjwgt = G.UNSAFE_metis_style_adjwgt_array(); + } else { + xadj = new int[number_of_nodes+1]; + adjncy = new int[number_of_edges]; - vwgt = new int[number_of_nodes]; - adjwgt = new int[number_of_edges]; - } + vwgt = new int[number_of_nodes]; + adjwgt = new int[number_of_edges]; + } - MPI_Bcast(xadj, number_of_nodes+1, MPI_INT, root, MPI_COMM_WORLD); - MPI_Bcast(adjncy, number_of_edges , MPI_INT, root, MPI_COMM_WORLD); - MPI_Bcast(vwgt, number_of_nodes , MPI_INT, root, MPI_COMM_WORLD); - MPI_Bcast(adjwgt, number_of_edges , MPI_INT, root, MPI_COMM_WORLD); + MPI_Bcast(xadj, number_of_nodes+1, MPI_INT, root, MPI_COMM_WORLD); + MPI_Bcast(adjncy, number_of_edges , MPI_INT, root, MPI_COMM_WORLD); + MPI_Bcast(vwgt, number_of_nodes , MPI_INT, root, MPI_COMM_WORLD); + MPI_Bcast(adjwgt, number_of_edges , MPI_INT, root, MPI_COMM_WORLD); - G.build_from_metis_weighted( number_of_nodes, xadj, adjncy, vwgt, adjwgt); + G.build_from_metis_weighted( number_of_nodes, xadj, adjncy, vwgt, adjwgt); - delete[] xadj; - delete[] adjncy; - delete[] vwgt; - delete[] adjwgt; + delete[] xadj; + delete[] adjncy; + delete[] vwgt; + delete[] adjwgt; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/tools/graph_communication.h b/parallel/modified_kahip/lib/tools/graph_communication.h index e52d2e6e..94d03284 100644 --- a/parallel/modified_kahip/lib/tools/graph_communication.h +++ b/parallel/modified_kahip/lib/tools/graph_communication.h @@ -10,14 +10,15 @@ #include "data_structure/graph_access.h" +namespace kahip::modified { class graph_communication { public: - graph_communication(); - virtual ~graph_communication(); + graph_communication(); + virtual ~graph_communication(); - void broadcast_graph( graph_access & G, unsigned root); + void broadcast_graph( graph_access & G, unsigned root); }; - +} #endif /* end of include guard: GRAPH_COMMUNICATION_J5Q2P80G */ diff --git a/parallel/modified_kahip/lib/tools/graph_extractor.cpp b/parallel/modified_kahip/lib/tools/graph_extractor.cpp index e0c7e8ab..1c63eea7 100644 --- a/parallel/modified_kahip/lib/tools/graph_extractor.cpp +++ b/parallel/modified_kahip/lib/tools/graph_extractor.cpp @@ -7,7 +7,7 @@ #include #include "graph_extractor.h" - +namespace kahip::modified { graph_extractor::graph_extractor() { } @@ -21,174 +21,174 @@ void graph_extractor::extract_block(graph_access & G, PartitionID block, std::vector & mapping) { - // build reverse mapping - std::vector reverse_mapping; - NodeID nodes = 0; - NodeID dummy_node = G.number_of_nodes() + 1; - forall_nodes(G, node) { - if(G.getPartitionIndex(node) == block) { - reverse_mapping.push_back(nodes++); - } else { - reverse_mapping.push_back(dummy_node); - } - } endfor - - extracted_block.start_construction(nodes, G.number_of_edges()); - - forall_nodes(G, node) { - if(G.getPartitionIndex(node) == block) { - NodeID new_node = extracted_block.new_node(); - mapping.push_back(node); - extracted_block.setNodeWeight( new_node, G.getNodeWeight(node)); - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( G.getPartitionIndex( target ) == block ) { - EdgeID new_edge = extracted_block.new_edge(new_node, reverse_mapping[target]); - extracted_block.setEdgeWeight(new_edge, G.getEdgeWeight(e)); - } - } endfor - } - } endfor - - extracted_block.finish_construction(); + // build reverse mapping + std::vector reverse_mapping; + NodeID nodes = 0; + NodeID dummy_node = G.number_of_nodes() + 1; + forall_nodes(G, node) { + if(G.getPartitionIndex(node) == block) { + reverse_mapping.push_back(nodes++); + } else { + reverse_mapping.push_back(dummy_node); + } + } endfor + + extracted_block.start_construction(nodes, G.number_of_edges()); + + forall_nodes(G, node) { + if(G.getPartitionIndex(node) == block) { + NodeID new_node = extracted_block.new_node(); + mapping.push_back(node); + extracted_block.setNodeWeight( new_node, G.getNodeWeight(node)); + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( G.getPartitionIndex( target ) == block ) { + EdgeID new_edge = extracted_block.new_edge(new_node, reverse_mapping[target]); + extracted_block.setEdgeWeight(new_edge, G.getEdgeWeight(e)); + } + } endfor } + } endfor + extracted_block.finish_construction(); +} -void graph_extractor::extract_two_blocks(graph_access & G, - graph_access & extracted_block_lhs, - graph_access & extracted_block_rhs, + +void graph_extractor::extract_two_blocks(graph_access & G, + graph_access & extracted_block_lhs, + graph_access & extracted_block_rhs, std::vector & mapping_lhs, std::vector & mapping_rhs, NodeWeight & partition_weight_lhs, NodeWeight & partition_weight_rhs) { - PartitionID lhs = 0; - PartitionID rhs = 1; - - // build reverse mapping - std::vector reverse_mapping_lhs; - std::vector reverse_mapping_rhs; - NodeID nodes_lhs = 0; - NodeID nodes_rhs = 0; - partition_weight_lhs = 0; - partition_weight_rhs = 0; - NodeID dummy_node = G.number_of_nodes() + 1; - - forall_nodes(G, node) { - if(G.getPartitionIndex(node) == lhs) { - reverse_mapping_lhs.push_back(nodes_lhs++); - reverse_mapping_rhs.push_back(dummy_node); - partition_weight_lhs += G.getNodeWeight(node); - } else { - reverse_mapping_rhs.push_back(nodes_rhs++); - reverse_mapping_lhs.push_back(dummy_node); - partition_weight_rhs += G.getNodeWeight(node); - } - } endfor - - extracted_block_lhs.start_construction(nodes_lhs, G.number_of_edges()); - extracted_block_rhs.start_construction(nodes_rhs, G.number_of_edges()); - - forall_nodes(G, node) { - if(G.getPartitionIndex(node) == lhs) { - NodeID new_node = extracted_block_lhs.new_node(); - mapping_lhs.push_back(node); - extracted_block_lhs.setNodeWeight(new_node, G.getNodeWeight(node)); - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( G.getPartitionIndex( target ) == lhs) { - EdgeID new_edge = extracted_block_lhs.new_edge(new_node, reverse_mapping_lhs[target]); - extracted_block_lhs.setEdgeWeight( new_edge, G.getEdgeWeight(e)); - } - } endfor - - } else { - NodeID new_node = extracted_block_rhs.new_node(); - mapping_rhs.push_back(node); - extracted_block_rhs.setNodeWeight(new_node, G.getNodeWeight(node)); - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( G.getPartitionIndex( target ) == rhs) { - EdgeID new_edge = extracted_block_rhs.new_edge(new_node, reverse_mapping_rhs[target]); - extracted_block_rhs.setEdgeWeight( new_edge, G.getEdgeWeight(e)); - } - } endfor - } - } endfor - - extracted_block_lhs.finish_construction(); - extracted_block_rhs.finish_construction(); + PartitionID lhs = 0; + PartitionID rhs = 1; + + // build reverse mapping + std::vector reverse_mapping_lhs; + std::vector reverse_mapping_rhs; + NodeID nodes_lhs = 0; + NodeID nodes_rhs = 0; + partition_weight_lhs = 0; + partition_weight_rhs = 0; + NodeID dummy_node = G.number_of_nodes() + 1; + + forall_nodes(G, node) { + if(G.getPartitionIndex(node) == lhs) { + reverse_mapping_lhs.push_back(nodes_lhs++); + reverse_mapping_rhs.push_back(dummy_node); + partition_weight_lhs += G.getNodeWeight(node); + } else { + reverse_mapping_rhs.push_back(nodes_rhs++); + reverse_mapping_lhs.push_back(dummy_node); + partition_weight_rhs += G.getNodeWeight(node); + } + } endfor + + extracted_block_lhs.start_construction(nodes_lhs, G.number_of_edges()); + extracted_block_rhs.start_construction(nodes_rhs, G.number_of_edges()); + + forall_nodes(G, node) { + if(G.getPartitionIndex(node) == lhs) { + NodeID new_node = extracted_block_lhs.new_node(); + mapping_lhs.push_back(node); + extracted_block_lhs.setNodeWeight(new_node, G.getNodeWeight(node)); + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( G.getPartitionIndex( target ) == lhs) { + EdgeID new_edge = extracted_block_lhs.new_edge(new_node, reverse_mapping_lhs[target]); + extracted_block_lhs.setEdgeWeight( new_edge, G.getEdgeWeight(e)); + } + } endfor + +} else { + NodeID new_node = extracted_block_rhs.new_node(); + mapping_rhs.push_back(node); + extracted_block_rhs.setNodeWeight(new_node, G.getNodeWeight(node)); + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( G.getPartitionIndex( target ) == rhs) { + EdgeID new_edge = extracted_block_rhs.new_edge(new_node, reverse_mapping_rhs[target]); + extracted_block_rhs.setEdgeWeight( new_edge, G.getEdgeWeight(e)); + } + } endfor +} + } endfor + + extracted_block_lhs.finish_construction(); + extracted_block_rhs.finish_construction(); } // Method takes a number of nodes and extracts the underlying subgraph from G // it also assignes block informations -void graph_extractor::extract_two_blocks_connected(graph_access & G, +void graph_extractor::extract_two_blocks_connected(graph_access & G, std::vector lhs_nodes, std::vector rhs_nodes, - PartitionID lhs, + PartitionID lhs, PartitionID rhs, graph_access & pair, std::vector & mapping) { - //// build reverse mapping - std::unordered_map reverse_mapping; - NodeID nodes = 0; - EdgeID edges = 0; // upper bound for number of edges - - for( unsigned i = 0; i < lhs_nodes.size(); i++) { - NodeID node = lhs_nodes[i]; - reverse_mapping[node] = nodes; - edges += G.getNodeDegree(lhs_nodes[i]); - nodes++; - } - for( unsigned i = 0; i < rhs_nodes.size(); i++) { - NodeID node = rhs_nodes[i]; - reverse_mapping[node] = nodes; - edges += G.getNodeDegree(rhs_nodes[i]); - nodes++; - } - - pair.start_construction(nodes, edges); + //// build reverse mapping + std::unordered_map reverse_mapping; + NodeID nodes = 0; + EdgeID edges = 0; // upper bound for number of edges + + for( unsigned i = 0; i < lhs_nodes.size(); i++) { + NodeID node = lhs_nodes[i]; + reverse_mapping[node] = nodes; + edges += G.getNodeDegree(lhs_nodes[i]); + nodes++; + } + for( unsigned i = 0; i < rhs_nodes.size(); i++) { + NodeID node = rhs_nodes[i]; + reverse_mapping[node] = nodes; + edges += G.getNodeDegree(rhs_nodes[i]); + nodes++; + } + + pair.start_construction(nodes, edges); + + for( unsigned i = 0; i < lhs_nodes.size(); i++) { + NodeID node = lhs_nodes[i]; + NodeID new_node = pair.new_node(); + mapping.push_back(node); + + pair.setNodeWeight(new_node, G.getNodeWeight(node)); + pair.setPartitionIndex(new_node, 0); + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( G.getPartitionIndex( target ) == lhs || G.getPartitionIndex( target ) == rhs ) { + EdgeID new_edge = pair.new_edge(new_node, reverse_mapping[target]); + pair.setEdgeWeight(new_edge, G.getEdgeWeight(e)); + } + } endfor - for( unsigned i = 0; i < lhs_nodes.size(); i++) { - NodeID node = lhs_nodes[i]; - NodeID new_node = pair.new_node(); - mapping.push_back(node); - - pair.setNodeWeight(new_node, G.getNodeWeight(node)); - pair.setPartitionIndex(new_node, 0); - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( G.getPartitionIndex( target ) == lhs || G.getPartitionIndex( target ) == rhs ) { - EdgeID new_edge = pair.new_edge(new_node, reverse_mapping[target]); - pair.setEdgeWeight(new_edge, G.getEdgeWeight(e)); - } - } endfor - - } - - for( unsigned i = 0; i < rhs_nodes.size(); i++) { - NodeID node = rhs_nodes[i]; - NodeID new_node = pair.new_node(); - mapping.push_back(node); +} - pair.setNodeWeight(new_node, G.getNodeWeight(node)); - pair.setPartitionIndex(new_node, 1); + for( unsigned i = 0; i < rhs_nodes.size(); i++) { + NodeID node = rhs_nodes[i]; + NodeID new_node = pair.new_node(); + mapping.push_back(node); - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( G.getPartitionIndex( target ) == lhs || G.getPartitionIndex( target ) == rhs ) { - EdgeID new_edge = pair.new_edge(new_node, reverse_mapping[target]); - pair.setEdgeWeight(new_edge, G.getEdgeWeight(e)); - } - } endfor + pair.setNodeWeight(new_node, G.getNodeWeight(node)); + pair.setPartitionIndex(new_node, 1); - } + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( G.getPartitionIndex( target ) == lhs || G.getPartitionIndex( target ) == rhs ) { + EdgeID new_edge = pair.new_edge(new_node, reverse_mapping[target]); + pair.setEdgeWeight(new_edge, G.getEdgeWeight(e)); + } + } endfor - pair.finish_construction(); } + pair.finish_construction(); +} +} diff --git a/parallel/modified_kahip/lib/tools/graph_extractor.h b/parallel/modified_kahip/lib/tools/graph_extractor.h index 6ea3e553..ef2ae02d 100644 --- a/parallel/modified_kahip/lib/tools/graph_extractor.h +++ b/parallel/modified_kahip/lib/tools/graph_extractor.h @@ -10,35 +10,35 @@ #include "data_structure/graph_access.h" #include "definitions.h" - +namespace kahip::modified { class graph_extractor { - public: - graph_extractor(); - virtual ~graph_extractor(); - - void extract_block(graph_access & G, - graph_access & extracted_block, - PartitionID block, - std::vector & mapping); - - void extract_two_blocks(graph_access & G, - graph_access & extracted_block_lhs, - graph_access & extracted_block_rhs, - std::vector & mapping_lhs, - std::vector & mapping_rhs, - NodeWeight & partition_weight_lhs, - NodeWeight & partition_weight_rhs); - - void extract_two_blocks_connected(graph_access & G, - std::vector lhs_nodes, - std::vector rhs_nodes, - PartitionID lhs, - PartitionID rhs, - graph_access & pair, - std::vector & mapping) ; +public: + graph_extractor(); + virtual ~graph_extractor(); + + void extract_block(graph_access & G, + graph_access & extracted_block, + PartitionID block, + std::vector & mapping); + + void extract_two_blocks(graph_access & G, + graph_access & extracted_block_lhs, + graph_access & extracted_block_rhs, + std::vector & mapping_lhs, + std::vector & mapping_rhs, + NodeWeight & partition_weight_lhs, + NodeWeight & partition_weight_rhs); + + void extract_two_blocks_connected(graph_access & G, + std::vector lhs_nodes, + std::vector rhs_nodes, + PartitionID lhs, + PartitionID rhs, + graph_access & pair, + std::vector & mapping) ; }; - +} #endif /* end of include guard: GRAPH_EXTRACTOR_PDUTVIEF */ diff --git a/parallel/modified_kahip/lib/tools/graph_partition_assertions.h b/parallel/modified_kahip/lib/tools/graph_partition_assertions.h index 0a0fcd37..fd22d394 100644 --- a/parallel/modified_kahip/lib/tools/graph_partition_assertions.h +++ b/parallel/modified_kahip/lib/tools/graph_partition_assertions.h @@ -10,31 +10,31 @@ #include "data_structure/graph_access.h" #include "partition_config.h" - +namespace kahip::modified { class graph_partition_assertions { - public: - graph_partition_assertions( ) {}; - virtual ~graph_partition_assertions() {}; +public: + graph_partition_assertions( ) {}; + virtual ~graph_partition_assertions() {}; - static bool assert_graph_has_kway_partition(const PartitionConfig & config, graph_access & G) { - bool* allpartsthere = new bool[config.k]; - for(unsigned int i = 0; i < config.k; i++) { - allpartsthere[i] = false; - } + static bool assert_graph_has_kway_partition(const PartitionConfig & config, graph_access & G) { + bool* allpartsthere = new bool[config.k]; + for(unsigned int i = 0; i < config.k; i++) { + allpartsthere[i] = false; + } - forall_nodes(G, n) { - allpartsthere[G.getPartitionIndex(n)] = true; - } endfor + forall_nodes(G, n) { + allpartsthere[G.getPartitionIndex(n)] = true; + } endfor - for(unsigned int i = 0; i < config.k; i++) { - ASSERT_TRUE(allpartsthere[i]); - } + for(unsigned int i = 0; i < config.k; i++) { + ASSERT_TRUE(allpartsthere[i]); + } - delete[] allpartsthere; - return true; - }; + delete[] allpartsthere; + return true; + }; }; - +} #endif /* end of include guard: GRAPH_PARTITION_ASSERTIONS_609QZZDM */ diff --git a/parallel/modified_kahip/lib/tools/misc.cpp b/parallel/modified_kahip/lib/tools/misc.cpp index bf1f116f..b5d12b00 100644 --- a/parallel/modified_kahip/lib/tools/misc.cpp +++ b/parallel/modified_kahip/lib/tools/misc.cpp @@ -7,7 +7,7 @@ #include "misc.h" #include "quality_metrics.h" - +namespace kahip::modified { misc::misc() { } @@ -17,33 +17,34 @@ misc::~misc() { } void misc::balance_singletons(const PartitionConfig & config, graph_access & G) { - quality_metrics qm; - std::vector< NodeID > singletons; - std::vector< NodeWeight > block_sizes(config.k,0); - - forall_nodes(G, node) { - block_sizes[G.getPartitionIndex(node)] += G.getNodeWeight(node); - - if(G.getNodeDegree(node) == 0) { - singletons.push_back(node); - } - } endfor - - // use buckets? - for( unsigned i = 0; i < singletons.size(); i++) { - NodeWeight min = block_sizes[0]; - PartitionID p = 0; - for( unsigned j = 0; j < config.k; j++) { - if( block_sizes[j] < min ) { - min = block_sizes[j]; - p = j; - } - } - - NodeID node = singletons[i]; - block_sizes[G.getPartitionIndex(node)] -= G.getNodeWeight(node); - block_sizes[p] += G.getNodeWeight(node); - G.setPartitionIndex(node, p); - } - std::cout << "log> balance after assigning singletons " << qm.balance(G) << std::endl; + quality_metrics qm; + std::vector< NodeID > singletons; + std::vector< NodeWeight > block_sizes(config.k,0); + + forall_nodes(G, node) { + block_sizes[G.getPartitionIndex(node)] += G.getNodeWeight(node); + + if(G.getNodeDegree(node) == 0) { + singletons.push_back(node); + } + } endfor + + // use buckets? + for( unsigned i = 0; i < singletons.size(); i++) { + NodeWeight min = block_sizes[0]; + PartitionID p = 0; + for( unsigned j = 0; j < config.k; j++) { + if( block_sizes[j] < min ) { + min = block_sizes[j]; + p = j; + } + } + + NodeID node = singletons[i]; + block_sizes[G.getPartitionIndex(node)] -= G.getNodeWeight(node); + block_sizes[p] += G.getNodeWeight(node); + G.setPartitionIndex(node, p); + } + std::cout << "log> balance after assigning singletons " << qm.balance(G) << std::endl; } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/tools/misc.h b/parallel/modified_kahip/lib/tools/misc.h index 07d37131..0867f445 100644 --- a/parallel/modified_kahip/lib/tools/misc.h +++ b/parallel/modified_kahip/lib/tools/misc.h @@ -10,14 +10,14 @@ #include "data_structure/graph_access.h" #include "partition_config.h" - +namespace kahip::modified { class misc { public: - misc(); - virtual ~misc(); + misc(); + virtual ~misc(); - void balance_singletons(const PartitionConfig & config, graph_access & G); + void balance_singletons(const PartitionConfig & config, graph_access & G); }; - +} #endif /* end of include guard: MISC_C6QUUWLI */ diff --git a/parallel/modified_kahip/lib/tools/partition_snapshooter.cpp b/parallel/modified_kahip/lib/tools/partition_snapshooter.cpp index 9b3e070b..34a9b559 100644 --- a/parallel/modified_kahip/lib/tools/partition_snapshooter.cpp +++ b/parallel/modified_kahip/lib/tools/partition_snapshooter.cpp @@ -10,73 +10,73 @@ #include "definitions.h" #include "graph_io.h" #include "partition_snapshooter.h" - +namespace kahip::modified { partition_snapshooter* partition_snapshooter::m_instance = NULL; partition_snapshooter::partition_snapshooter() { - m_buffer_size = 500; - m_idx = 0; + m_buffer_size = 500; + m_idx = 0; } partition_snapshooter::~partition_snapshooter() { - flush_buffer(); + flush_buffer(); } partition_snapshooter * partition_snapshooter::getInstance() { - if( m_instance == NULL ) { - m_instance = new partition_snapshooter(); - } - return m_instance; + if( m_instance == NULL ) { + m_instance = new partition_snapshooter(); + } + return m_instance; } void partition_snapshooter::addSnapshot(graph_access & G) { - std::cout << "idx " << m_partition_map_buffer.size() << std::endl; - std::vector* partition_map = new std::vector(); - m_partition_map_buffer.push_back(partition_map); + std::cout << "idx " << m_partition_map_buffer.size() << std::endl; + std::vector* partition_map = new std::vector(); + m_partition_map_buffer.push_back(partition_map); - forall_nodes(G, node) { - partition_map->push_back(G.getPartitionIndex(node)); - } endfor + forall_nodes(G, node) { + partition_map->push_back(G.getPartitionIndex(node)); + } endfor - if( m_partition_map_buffer.size() > m_buffer_size) { - flush_buffer(); - } + if( m_partition_map_buffer.size() > m_buffer_size) { + flush_buffer(); + } } void partition_snapshooter::addSnapshot(graph_access & G, std::vector & ext_partition_map) { - std::vector* partition_map = new std::vector(); - m_partition_map_buffer.push_back(partition_map); + std::vector* partition_map = new std::vector(); + m_partition_map_buffer.push_back(partition_map); - forall_nodes(G, node) { - partition_map->push_back(ext_partition_map[node]); - } endfor + forall_nodes(G, node) { + partition_map->push_back(ext_partition_map[node]); + } endfor - if( m_partition_map_buffer.size() > m_buffer_size) { - flush_buffer(); - } + if( m_partition_map_buffer.size() > m_buffer_size) { + flush_buffer(); + } } void partition_snapshooter::flush_buffer() { - for( unsigned i = 0; i < m_partition_map_buffer.size(); i++) { - std::stringstream snapshot_name; - snapshot_name << "snapshot_" << m_idx; + for( unsigned i = 0; i < m_partition_map_buffer.size(); i++) { + std::stringstream snapshot_name; + snapshot_name << "snapshot_" << m_idx; - graph_io::writeVector(*(m_partition_map_buffer[i]), snapshot_name.str()); + graph_io::writeVector(*(m_partition_map_buffer[i]), snapshot_name.str()); - m_idx++; - } + m_idx++; + } - //flush buffer - for( int i = m_partition_map_buffer.size()-1; i >= 0; i--) { - delete m_partition_map_buffer[i]; - m_partition_map_buffer.pop_back(); - } + //flush buffer + for( int i = m_partition_map_buffer.size()-1; i >= 0; i--) { + delete m_partition_map_buffer[i]; + m_partition_map_buffer.pop_back(); + } } void partition_snapshooter::set_buffer_size( unsigned int new_buffer_size ) { - m_buffer_size = new_buffer_size; + m_buffer_size = new_buffer_size; +} } - diff --git a/parallel/modified_kahip/lib/tools/partition_snapshooter.h b/parallel/modified_kahip/lib/tools/partition_snapshooter.h index 5ec796cd..8879898a 100644 --- a/parallel/modified_kahip/lib/tools/partition_snapshooter.h +++ b/parallel/modified_kahip/lib/tools/partition_snapshooter.h @@ -9,30 +9,30 @@ #define PARTITION_SNAPSHOOTER_LGCUMS2I #include "data_structure/graph_access.h" - +namespace kahip::modified { //buffered partition snapshooter (singleton) class partition_snapshooter { - public: - static partition_snapshooter * getInstance(); +public: + static partition_snapshooter * getInstance(); - void addSnapshot(graph_access & G); - void addSnapshot(graph_access & G, std::vector & partition_map); + void addSnapshot(graph_access & G); + void addSnapshot(graph_access & G, std::vector & partition_map); - //flushes buffer to disk - void flush_buffer(); - void set_buffer_size( unsigned int new_buffer_size ); - private: - partition_snapshooter(); - partition_snapshooter(const partition_snapshooter&) {} + //flushes buffer to disk + void flush_buffer(); + void set_buffer_size( unsigned int new_buffer_size ); +private: + partition_snapshooter(); + partition_snapshooter(const partition_snapshooter&) {} - virtual ~partition_snapshooter(); - static partition_snapshooter* m_instance; + virtual ~partition_snapshooter(); + static partition_snapshooter* m_instance; - unsigned int m_buffer_size; - unsigned int m_idx; + unsigned int m_buffer_size; + unsigned int m_idx; - std::vector< std::vector< PartitionID >* > m_partition_map_buffer; + std::vector< std::vector< PartitionID >* > m_partition_map_buffer; }; - +} #endif /* end of include guard: PARTITION_SNAPSHOOTER_LGCUMS2I */ diff --git a/parallel/modified_kahip/lib/tools/quality_metrics.cpp b/parallel/modified_kahip/lib/tools/quality_metrics.cpp index cd2d7234..b2d6c11e 100644 --- a/parallel/modified_kahip/lib/tools/quality_metrics.cpp +++ b/parallel/modified_kahip/lib/tools/quality_metrics.cpp @@ -12,7 +12,7 @@ #include "data_structure/union_find.h" #include - +namespace kahip::modified { quality_metrics::quality_metrics() { } @@ -20,195 +20,195 @@ quality_metrics::~quality_metrics () { } EdgeWeight quality_metrics::edge_cut(graph_access & G) { - EdgeWeight edgeCut = 0; - forall_nodes(G, n) { - PartitionID partitionIDSource = G.getPartitionIndex(n); - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); - PartitionID partitionIDTarget = G.getPartitionIndex(targetNode); - - if (partitionIDSource != partitionIDTarget) { - edgeCut += G.getEdgeWeight(e); - } - } endfor - } endfor - return edgeCut/2; + EdgeWeight edgeCut = 0; + forall_nodes(G, n) { + PartitionID partitionIDSource = G.getPartitionIndex(n); + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); + PartitionID partitionIDTarget = G.getPartitionIndex(targetNode); + + if (partitionIDSource != partitionIDTarget) { + edgeCut += G.getEdgeWeight(e); + } + } endfor +} endfor +return edgeCut/2; } EdgeWeight quality_metrics::edge_cut(graph_access & G, int * partition_map) { - EdgeWeight edgeCut = 0; - forall_nodes(G, n) { - PartitionID partitionIDSource = partition_map[n]; - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); - PartitionID partitionIDTarget = partition_map[targetNode]; - - if (partitionIDSource != partitionIDTarget) { - edgeCut += G.getEdgeWeight(e); - } - } endfor - } endfor - return edgeCut/2; + EdgeWeight edgeCut = 0; + forall_nodes(G, n) { + PartitionID partitionIDSource = partition_map[n]; + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); + PartitionID partitionIDTarget = partition_map[targetNode]; + + if (partitionIDSource != partitionIDTarget) { + edgeCut += G.getEdgeWeight(e); + } + } endfor +} endfor +return edgeCut/2; } EdgeWeight quality_metrics::edge_cut(graph_access & G, PartitionID lhs, PartitionID rhs) { - EdgeWeight edgeCut = 0; - forall_nodes(G, n) { - PartitionID partitionIDSource = G.getPartitionIndex(n); - if(partitionIDSource != lhs) continue; - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); - PartitionID partitionIDTarget = G.getPartitionIndex(targetNode); - - if(partitionIDTarget == rhs) { - edgeCut += G.getEdgeWeight(e); - } - } endfor - } endfor - return edgeCut; + EdgeWeight edgeCut = 0; + forall_nodes(G, n) { + PartitionID partitionIDSource = G.getPartitionIndex(n); + if(partitionIDSource != lhs) continue; + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); + PartitionID partitionIDTarget = G.getPartitionIndex(targetNode); + + if(partitionIDTarget == rhs) { + edgeCut += G.getEdgeWeight(e); + } + } endfor +} endfor +return edgeCut; } EdgeWeight quality_metrics::edge_cut_connected(graph_access & G, int * partition_map) { - EdgeWeight edgeCut = 0; - EdgeWeight sumEW = 0; - forall_nodes(G, n) { - PartitionID partitionIDSource = partition_map[n]; - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); - PartitionID partitionIDTarget = partition_map[targetNode]; - - if (partitionIDSource != partitionIDTarget) { - edgeCut += G.getEdgeWeight(e); - } - sumEW+=G.getEdgeWeight(e); - } endfor - } endfor - union_find uf(G.number_of_nodes()); - forall_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if(partition_map[node] == partition_map[target]) { - uf.Union(node, target); - } - } endfor - } endfor - - std::unordered_map size_right; - forall_nodes(G, node) { - size_right[uf.Find(node)] = 1; - } endfor - - - std::cout << "number of connected comp " << size_right.size() << std::endl; - if( size_right.size() == G.get_partition_count()) { - return edgeCut/2; - } else { - return edgeCut/2+sumEW*size_right.size(); - } + EdgeWeight edgeCut = 0; + EdgeWeight sumEW = 0; + forall_nodes(G, n) { + PartitionID partitionIDSource = partition_map[n]; + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); + PartitionID partitionIDTarget = partition_map[targetNode]; + + if (partitionIDSource != partitionIDTarget) { + edgeCut += G.getEdgeWeight(e); + } + sumEW+=G.getEdgeWeight(e); + } endfor +} endfor +union_find uf(G.number_of_nodes()); + forall_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if(partition_map[node] == partition_map[target]) { + uf.Union(node, target); + } + } endfor +} endfor + +std::unordered_map size_right; + forall_nodes(G, node) { + size_right[uf.Find(node)] = 1; + } endfor + + + std::cout << "number of connected comp " << size_right.size() << std::endl; + if( size_right.size() == G.get_partition_count()) { + return edgeCut/2; + } else { + return edgeCut/2+sumEW*size_right.size(); + } } EdgeWeight quality_metrics::max_communication_volume(graph_access & G, int * partition_map) { - std::vector block_volume(G.get_partition_count(),0); - forall_nodes(G, node) { - PartitionID block = partition_map[node]; - std::vector block_incident(G.get_partition_count(), false); - block_incident[block] = true; - - int num_incident_blocks = 0; - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID target_block = partition_map[target]; - if(!block_incident[target_block]) { - block_incident[target_block] = true; - num_incident_blocks++; - } - } endfor - block_volume[block] += num_incident_blocks; + std::vector block_volume(G.get_partition_count(),0); + forall_nodes(G, node) { + PartitionID block = partition_map[node]; + std::vector block_incident(G.get_partition_count(), false); + block_incident[block] = true; + + int num_incident_blocks = 0; + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID target_block = partition_map[target]; + if(!block_incident[target_block]) { + block_incident[target_block] = true; + num_incident_blocks++; + } } endfor + block_volume[block] += num_incident_blocks; + } endfor - EdgeWeight max_comm_volume = *(std::max_element(block_volume.begin(), block_volume.end())); - return max_comm_volume; + EdgeWeight max_comm_volume = *(std::max_element(block_volume.begin(), block_volume.end())); + return max_comm_volume; } EdgeWeight quality_metrics::max_communication_volume(graph_access & G) { - std::vector block_volume(G.get_partition_count(),0); - forall_nodes(G, node) { - PartitionID block = G.getPartitionIndex(node); - std::vector block_incident(G.get_partition_count(), false); - block_incident[block] = true; - int num_incident_blocks = 0; - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID target_block = G.getPartitionIndex(target); - if(!block_incident[target_block]) { - block_incident[target_block] = true; - num_incident_blocks++; - } - } endfor - block_volume[block] += num_incident_blocks; + std::vector block_volume(G.get_partition_count(),0); + forall_nodes(G, node) { + PartitionID block = G.getPartitionIndex(node); + std::vector block_incident(G.get_partition_count(), false); + block_incident[block] = true; + int num_incident_blocks = 0; + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID target_block = G.getPartitionIndex(target); + if(!block_incident[target_block]) { + block_incident[target_block] = true; + num_incident_blocks++; + } } endfor + block_volume[block] += num_incident_blocks; + } endfor - EdgeWeight max_comm_volume = *(std::max_element(block_volume.begin(), block_volume.end())); - return max_comm_volume; + EdgeWeight max_comm_volume = *(std::max_element(block_volume.begin(), block_volume.end())); + return max_comm_volume; } int quality_metrics::boundary_nodes(graph_access& G) { - int no_of_boundary_nodes = 0; - forall_nodes(G, n) { - PartitionID partitionIDSource = G.getPartitionIndex(n); - - forall_out_edges(G, e, n) { - NodeID targetNode = G.getEdgeTarget(e); - PartitionID partitionIDTarget = G.getPartitionIndex(targetNode); - - if (partitionIDSource != partitionIDTarget) { - no_of_boundary_nodes++; - break; - } - } endfor - } endfor - return no_of_boundary_nodes; + int no_of_boundary_nodes = 0; + forall_nodes(G, n) { + PartitionID partitionIDSource = G.getPartitionIndex(n); + + forall_out_edges(G, e, n) { + NodeID targetNode = G.getEdgeTarget(e); + PartitionID partitionIDTarget = G.getPartitionIndex(targetNode); + + if (partitionIDSource != partitionIDTarget) { + no_of_boundary_nodes++; + break; + } + } endfor +} endfor +return no_of_boundary_nodes; } double quality_metrics::balance(graph_access& G) { - std::vector part_weights(G.get_partition_count(), 0); + std::vector part_weights(G.get_partition_count(), 0); - double overallWeight = 0; + double overallWeight = 0; - forall_nodes(G, n) { - PartitionID curPartition = G.getPartitionIndex(n); - part_weights[curPartition] += G.getNodeWeight(n); - overallWeight += G.getNodeWeight(n); - } endfor + forall_nodes(G, n) { + PartitionID curPartition = G.getPartitionIndex(n); + part_weights[curPartition] += G.getNodeWeight(n); + overallWeight += G.getNodeWeight(n); + } endfor - double balance_part_weight = ceil(overallWeight / (double)G.get_partition_count()); - double cur_max = -1; + double balance_part_weight = ceil(overallWeight / (double)G.get_partition_count()); + double cur_max = -1; - forall_blocks(G, p) { - double cur = part_weights[p]; - if (cur > cur_max) { - cur_max = cur; - } - } endfor + forall_blocks(G, p) { + double cur = part_weights[p]; + if (cur > cur_max) { + cur_max = cur; + } + } endfor - double percentage = cur_max/balance_part_weight; - return percentage; + double percentage = cur_max/balance_part_weight; + return percentage; } EdgeWeight quality_metrics::objective(const PartitionConfig & config, graph_access & G, int* partition_map) { - if(config.mh_optimize_communication_volume) { - return max_communication_volume(G, partition_map); - } else if(config.mh_penalty_for_unconnected) { - return edge_cut_connected(G, partition_map); - } else { - return edge_cut(G, partition_map); - } + if(config.mh_optimize_communication_volume) { + return max_communication_volume(G, partition_map); + } else if(config.mh_penalty_for_unconnected) { + return edge_cut_connected(G, partition_map); + } else { + return edge_cut(G, partition_map); + } +} } - diff --git a/parallel/modified_kahip/lib/tools/quality_metrics.h b/parallel/modified_kahip/lib/tools/quality_metrics.h index 428b419c..7f29860f 100644 --- a/parallel/modified_kahip/lib/tools/quality_metrics.h +++ b/parallel/modified_kahip/lib/tools/quality_metrics.h @@ -11,22 +11,22 @@ #include "data_structure/graph_access.h" #include "data_structure/matrix/matrix.h" #include "partition_config.h" - +namespace kahip::modified { class quality_metrics { public: - quality_metrics(); - virtual ~quality_metrics (); + quality_metrics(); + virtual ~quality_metrics (); - EdgeWeight edge_cut(graph_access & G); - EdgeWeight edge_cut(graph_access & G, int * partition_map); - EdgeWeight edge_cut(graph_access & G, PartitionID lhs, PartitionID rhs); - EdgeWeight max_communication_volume(graph_access & G); - EdgeWeight max_communication_volume(graph_access & G, int * partition_map); - EdgeWeight objective(const PartitionConfig & config, graph_access & G, int * partition_map); - EdgeWeight edge_cut_connected(graph_access & G, int * partition_map); - int boundary_nodes(graph_access & G); - double balance(graph_access & G); + EdgeWeight edge_cut(graph_access & G); + EdgeWeight edge_cut(graph_access & G, int * partition_map); + EdgeWeight edge_cut(graph_access & G, PartitionID lhs, PartitionID rhs); + EdgeWeight max_communication_volume(graph_access & G); + EdgeWeight max_communication_volume(graph_access & G, int * partition_map); + EdgeWeight objective(const PartitionConfig & config, graph_access & G, int * partition_map); + EdgeWeight edge_cut_connected(graph_access & G, int * partition_map); + int boundary_nodes(graph_access & G); + double balance(graph_access & G); }; - +} #endif /* end of include guard: QUALITY_METRICS_10HC2I5M */ diff --git a/parallel/modified_kahip/lib/tools/random_functions.cpp b/parallel/modified_kahip/lib/tools/random_functions.cpp index 46c909bf..ad5ddca3 100644 --- a/parallel/modified_kahip/lib/tools/random_functions.cpp +++ b/parallel/modified_kahip/lib/tools/random_functions.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "random_functions.h" - +namespace kahip::modified { MersenneTwister random_functions::m_mt; int random_functions::m_seed = 0; @@ -15,3 +15,4 @@ random_functions::random_functions() { random_functions::~random_functions() { } +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/tools/random_functions.h b/parallel/modified_kahip/lib/tools/random_functions.h index b44d1a79..39d218bf 100644 --- a/parallel/modified_kahip/lib/tools/random_functions.h +++ b/parallel/modified_kahip/lib/tools/random_functions.h @@ -15,151 +15,152 @@ #include "definitions.h" #include "partition_config.h" -typedef std::mt19937 MersenneTwister; +namespace kahip::modified { +using MersenneTwister = std::mt19937; class random_functions { - public: - random_functions(); - virtual ~random_functions(); - - template - static void circular_permutation(std::vector & vec) { - if(vec.size() < 2) return; - for( unsigned int i = 0; i < vec.size(); i++) { - vec[i] = i; - } - - unsigned int size = vec.size(); - std::uniform_int_distribution A(0,size-1); - std::uniform_int_distribution B(0,size-1); - - for( unsigned int i = 0; i < size; i++) { - unsigned int posA = A(m_mt); - unsigned int posB = B(m_mt); - - while(posB == posA) { - posB = B(m_mt); - } - - if( posA != vec[posB] && posB != vec[posA]) { - std::swap(vec[posA], vec[posB]); - } - } - - } - - template - static void permutate_vector_fast(std::vector & vec, bool init) { - if(init) { - for( unsigned int i = 0; i < vec.size(); i++) { - vec[i] = i; - } - } - - if(vec.size() < 10) return; - - int distance = 20; - std::uniform_int_distribution A(0, distance); - unsigned int size = vec.size()-4; - for( unsigned int i = 0; i < size; i++) { - unsigned int posA = i; - unsigned int posB = (posA + A(m_mt))%size; - std::swap(vec[posA], vec[posB]); - std::swap(vec[posA+1], vec[posB+1]); - std::swap(vec[posA+2], vec[posB+2]); - std::swap(vec[posA+3], vec[posB+3]); - } - } +public: + random_functions(); + virtual ~random_functions(); + + template + static void circular_permutation(std::vector & vec) { + if(vec.size() < 2) return; + for( unsigned int i = 0; i < vec.size(); i++) { + vec[i] = i; + } + + unsigned int size = vec.size(); + std::uniform_int_distribution A(0,size-1); + std::uniform_int_distribution B(0,size-1); - template - static void permutate_vector_good(std::vector & vec, bool init) { - if(init) { - for( unsigned int i = 0; i < vec.size(); i++) { - vec[i] = (sometype)i; - } - } - - if(vec.size() < 10) { - permutate_vector_good_small(vec); - return; - } - unsigned int size = vec.size(); - std::uniform_int_distribution A(0,size - 4); - std::uniform_int_distribution B(0,size - 4); - - for( unsigned int i = 0; i < size; i++) { - unsigned int posA = A(m_mt); - unsigned int posB = B(m_mt); - std::swap(vec[posA], vec[posB]); - std::swap(vec[posA+1], vec[posB+1]); - std::swap(vec[posA+2], vec[posB+2]); - std::swap(vec[posA+3], vec[posB+3]); - - } + for( unsigned int i = 0; i < size; i++) { + unsigned int posA = A(m_mt); + unsigned int posB = B(m_mt); + + while(posB == posA) { + posB = B(m_mt); } - template - static void permutate_vector_good_small(std::vector & vec) { - if(vec.size() < 2) return; - unsigned int size = vec.size(); - std::uniform_int_distribution A(0,size-1); - std::uniform_int_distribution B(0,size-1); - - for( unsigned int i = 0; i < size; i++) { - unsigned int posA = A(m_mt); - unsigned int posB = B(m_mt); - std::swap(vec[posA], vec[posB]); - } + if( posA != vec[posB] && posB != vec[posA]) { + std::swap(vec[posA], vec[posB]); } + } - template - static void permutate_entries(const PartitionConfig & partition_config, - std::vector & vec, - bool init) { - if(init) { - for( unsigned int i = 0; i < vec.size(); i++) { - vec[i] = i; - } - } - - switch(partition_config.permutation_quality) { - case PERMUTATION_QUALITY_NONE: break; - case PERMUTATION_QUALITY_FAST: permutate_vector_fast(vec, false); break; - case PERMUTATION_QUALITY_GOOD: permutate_vector_good(vec, false); break; - } + } + template + static void permutate_vector_fast(std::vector & vec, bool init) { + if(init) { + for( unsigned int i = 0; i < vec.size(); i++) { + vec[i] = i; } - - static bool nextBool() { - std::bernoulli_distribution bernoulli(0.5); - return bernoulli(m_mt); } + if(vec.size() < 10) return; + + int distance = 20; + std::uniform_int_distribution A(0, distance); + unsigned int size = vec.size()-4; + for( unsigned int i = 0; i < size; i++) { + unsigned int posA = i; + unsigned int posB = (posA + A(m_mt))%size; + std::swap(vec[posA], vec[posB]); + std::swap(vec[posA+1], vec[posB+1]); + std::swap(vec[posA+2], vec[posB+2]); + std::swap(vec[posA+3], vec[posB+3]); + } + } - //including lb and rb - static unsigned nextInt(unsigned int lb, unsigned int rb) { - std::uniform_int_distribution A(lb,rb); - return A(m_mt); + template + static void permutate_vector_good(std::vector & vec, bool init) { + if(init) { + for( unsigned int i = 0; i < vec.size(); i++) { + vec[i] = (sometype)i; + } } - static double nextDouble(double lb, double rb) { - double rnbr = (double) rand() / (double) RAND_MAX; // rnd in 0,1 - double length = rb - lb; - rnbr *= length; - rnbr += lb; + if(vec.size() < 10) { + permutate_vector_good_small(vec); + return; + } + unsigned int size = vec.size(); + std::uniform_int_distribution A(0,size - 4); + std::uniform_int_distribution B(0,size - 4); + + for( unsigned int i = 0; i < size; i++) { + unsigned int posA = A(m_mt); + unsigned int posB = B(m_mt); + std::swap(vec[posA], vec[posB]); + std::swap(vec[posA+1], vec[posB+1]); + std::swap(vec[posA+2], vec[posB+2]); + std::swap(vec[posA+3], vec[posB+3]); - return rnbr; + } + } + + template + static void permutate_vector_good_small(std::vector & vec) { + if(vec.size() < 2) return; + unsigned int size = vec.size(); + std::uniform_int_distribution A(0,size-1); + std::uniform_int_distribution B(0,size-1); + + for( unsigned int i = 0; i < size; i++) { + unsigned int posA = A(m_mt); + unsigned int posB = B(m_mt); + std::swap(vec[posA], vec[posB]); + } + } + + template + static void permutate_entries(const PartitionConfig & partition_config, + std::vector & vec, + bool init) { + if(init) { + for( unsigned int i = 0; i < vec.size(); i++) { + vec[i] = i; + } } - static void setSeed(int seed) { - m_seed = seed; - srand(seed); - m_mt.seed(m_seed); + switch(partition_config.permutation_quality) { + case PERMUTATION_QUALITY_NONE: break; + case PERMUTATION_QUALITY_FAST: permutate_vector_fast(vec, false); break; + case PERMUTATION_QUALITY_GOOD: permutate_vector_good(vec, false); break; } - private: - static int m_seed; - static MersenneTwister m_mt; -}; + } + + static bool nextBool() { + std::bernoulli_distribution bernoulli(0.5); + return bernoulli(m_mt); + } + + //including lb and rb + static unsigned nextInt(unsigned int lb, unsigned int rb) { + std::uniform_int_distribution A(lb,rb); + return A(m_mt); + } + + static double nextDouble(double lb, double rb) { + double rnbr = (double) rand() / (double) RAND_MAX; // rnd in 0,1 + double length = rb - lb; + rnbr *= length; + rnbr += lb; + + return rnbr; + } + + static void setSeed(int seed) { + m_seed = seed; + srand(seed); + m_mt.seed(m_seed); + } + +private: + static int m_seed; + static MersenneTwister m_mt; +}; +} #endif /* end of include guard: RANDOM_FUNCTIONS_RMEPKWYT */ diff --git a/parallel/modified_kahip/lib/tools/timer.h b/parallel/modified_kahip/lib/tools/timer.h index c5abe388..34c5dbcf 100644 --- a/parallel/modified_kahip/lib/tools/timer.h +++ b/parallel/modified_kahip/lib/tools/timer.h @@ -11,31 +11,31 @@ #include #include #include - +namespace kahip::modified { class timer { - public: - timer() { - m_start = timestamp(); - } - - void restart() { - m_start = timestamp(); - } - - double elapsed() { - return timestamp()-m_start; - } - - private: - - /** Returns a timestamp ('now') in seconds (incl. a fractional part). */ - inline double timestamp() { - struct timeval tp; - gettimeofday(&tp, NULL); - return double(tp.tv_sec) + tp.tv_usec / 1000000.; - } - - double m_start; -}; - +public: + timer() { + m_start = timestamp(); + } + + void restart() { + m_start = timestamp(); + } + + double elapsed() { + return timestamp()-m_start; + } + +private: + + /** Returns a timestamp ('now') in seconds (incl. a fractional part). */ + inline double timestamp() { + struct timeval tp; + gettimeofday(&tp, NULL); + return double(tp.tv_sec) + tp.tv_usec / 1000000.; + } + + double m_start; +}; +} #endif /* end of include guard: TIMER_9KPDEP */ diff --git a/parallel/parallel_src/lib/data_structure/parallel_graph_access.h b/parallel/parallel_src/lib/data_structure/parallel_graph_access.h index 77053bd6..35ca8de8 100644 --- a/parallel/parallel_src/lib/data_structure/parallel_graph_access.h +++ b/parallel/parallel_src/lib/data_structure/parallel_graph_access.h @@ -8,7 +8,7 @@ #ifndef PARALLEL_GRAPH_ACCESS_X6O9MRS8 #define PARALLEL_GRAPH_ACCESS_X6O9MRS8 - +#include #include #include #include @@ -92,33 +92,26 @@ class ghost_node_communication { ~ghost_node_communication() = default; - inline + void setGraphReference( parallel_graph_access * G ) { m_G = G; }; - inline + void init( ) { - m_num_adjacent = 0; - for( PEID peID = 0; peID < (PEID)m_adjacent_processors.size(); peID++) { - if( m_adjacent_processors[peID] ) { - m_num_adjacent++; - } - } + m_num_adjacent = getNumberOfAdjacentPEs(); }; - inline + void add_adjacent_processor( PEID peID) { m_adjacent_processors[peID] = true; - }; + }; - inline void set_skip_limit( ULONG skip_limit ) { m_skip_limit = skip_limit; } - inline void set_desired_rounds( ULONG desired_rounds) { m_desired_rounds = desired_rounds; } @@ -135,18 +128,12 @@ class ghost_node_communication { inline void addLabel(NodeID node, NodeID label); - inline bool is_adjacent_PE(PEID peID) { return m_adjacent_processors[peID]; } - inline PEID getNumberOfAdjacentPEs() { - PEID counter = 0; - for( PEID peID = 0; peID < (PEID)m_adjacent_processors.size(); peID++) { - if( m_adjacent_processors[peID] ) counter++; - } - return counter; + return static_cast(std::ranges::count( m_adjacent_processors, true)); } private: From dfe22abdf9fedd928700291384c70f09f4b4c89b Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 11 Oct 2024 13:58:47 +0100 Subject: [PATCH 50/64] Clang-tidy errors fixed --- .../lib/algorithms/cycle_search.cpp | 8 ----- .../lib/algorithms/cycle_search.h | 3 -- .../strongly_connected_components.cpp | 7 ----- .../strongly_connected_components.h | 6 ++-- .../lib/algorithms/topological_sort.cpp | 7 ----- .../lib/algorithms/topological_sort.h | 3 -- .../lib/data_structure/matrix/matrix.h | 11 +++++-- .../lib/data_structure/matrix/normal_matrix.h | 9 +++--- .../priority_queues/bucket_pq.h | 29 +++++++++-------- .../priority_queues/maxNodeHeap.h | 31 +++++++++---------- .../priority_queue_interface.h | 9 ++++-- parallel/modified_kahip/lib/io/graph_io.cpp | 7 ----- parallel/modified_kahip/lib/io/graph_io.h | 10 ++++-- .../lib/parallel_mh/diversifyer.h | 10 ++++-- .../galinier_combine/construct_partition.cpp | 7 ----- .../galinier_combine/construct_partition.h | 10 ++++-- .../galinier_combine/gal_combine.cpp | 8 ----- .../galinier_combine/gal_combine.h | 10 ++++-- 18 files changed, 82 insertions(+), 103 deletions(-) diff --git a/parallel/modified_kahip/lib/algorithms/cycle_search.cpp b/parallel/modified_kahip/lib/algorithms/cycle_search.cpp index 1e6e3c43..2a73d8f1 100644 --- a/parallel/modified_kahip/lib/algorithms/cycle_search.cpp +++ b/parallel/modified_kahip/lib/algorithms/cycle_search.cpp @@ -14,14 +14,6 @@ namespace kahip::modified { double cycle_search::total_time = 0; -cycle_search::cycle_search() { - -} - -cycle_search::~cycle_search() { - -} - void cycle_search::find_random_cycle(graph_access & G, std::vector & cycle) { //first perform a bfs starting from a random node and build the parent array std::deque* bfsqueue = new std::deque; diff --git a/parallel/modified_kahip/lib/algorithms/cycle_search.h b/parallel/modified_kahip/lib/algorithms/cycle_search.h index d8bfe095..4d1b2fd2 100644 --- a/parallel/modified_kahip/lib/algorithms/cycle_search.h +++ b/parallel/modified_kahip/lib/algorithms/cycle_search.h @@ -12,9 +12,6 @@ namespace kahip::modified { class cycle_search { public: - cycle_search(); - virtual ~cycle_search(); - void find_random_cycle(graph_access & G, std::vector & cycle); //returns true if a negative cycle was found, else false diff --git a/parallel/modified_kahip/lib/algorithms/strongly_connected_components.cpp b/parallel/modified_kahip/lib/algorithms/strongly_connected_components.cpp index e23c43c2..c3f14227 100644 --- a/parallel/modified_kahip/lib/algorithms/strongly_connected_components.cpp +++ b/parallel/modified_kahip/lib/algorithms/strongly_connected_components.cpp @@ -10,13 +10,6 @@ #include "strongly_connected_components.h" namespace kahip::modified { -strongly_connected_components::strongly_connected_components() { - -} - -strongly_connected_components::~strongly_connected_components() { - -} int strongly_connected_components::strong_components( graph_access & G, std::vector & comp_num) { diff --git a/parallel/modified_kahip/lib/algorithms/strongly_connected_components.h b/parallel/modified_kahip/lib/algorithms/strongly_connected_components.h index 21328f0a..bfe477fc 100644 --- a/parallel/modified_kahip/lib/algorithms/strongly_connected_components.h +++ b/parallel/modified_kahip/lib/algorithms/strongly_connected_components.h @@ -16,8 +16,6 @@ namespace kahip::modified { class strongly_connected_components { public: - strongly_connected_components(); - virtual ~strongly_connected_components(); int strong_components( graph_access & G, std::vector & comp_num); @@ -27,8 +25,8 @@ class strongly_connected_components { std::stack & unfinished, std::stack & roots); private: - int m_dfscount; - int m_comp_count; + int m_dfscount = 0; + int m_comp_count = 0; }; } diff --git a/parallel/modified_kahip/lib/algorithms/topological_sort.cpp b/parallel/modified_kahip/lib/algorithms/topological_sort.cpp index 7ee4d163..a0740b3b 100644 --- a/parallel/modified_kahip/lib/algorithms/topological_sort.cpp +++ b/parallel/modified_kahip/lib/algorithms/topological_sort.cpp @@ -10,13 +10,6 @@ #include "random_functions.h" #include "topological_sort.h" namespace kahip::modified { -topological_sort::topological_sort() { - -} - -topological_sort::~topological_sort() { - -} void topological_sort::sort( graph_access & G, std::vector & sorted_sequence) { std::vector dfsnum(G.number_of_nodes(), -1); diff --git a/parallel/modified_kahip/lib/algorithms/topological_sort.h b/parallel/modified_kahip/lib/algorithms/topological_sort.h index af0352a5..fffe03e5 100644 --- a/parallel/modified_kahip/lib/algorithms/topological_sort.h +++ b/parallel/modified_kahip/lib/algorithms/topological_sort.h @@ -16,9 +16,6 @@ namespace kahip::modified { class topological_sort { public: - topological_sort(); - virtual ~topological_sort(); - void sort( graph_access & SG, std::vector & sorted_sequence); void sort_dfs(NodeID node, graph_access & G, diff --git a/parallel/modified_kahip/lib/data_structure/matrix/matrix.h b/parallel/modified_kahip/lib/data_structure/matrix/matrix.h index 85a2771f..dd85dff7 100644 --- a/parallel/modified_kahip/lib/data_structure/matrix/matrix.h +++ b/parallel/modified_kahip/lib/data_structure/matrix/matrix.h @@ -11,12 +11,19 @@ namespace kahip::modified { class matrix { public: matrix(unsigned int dim_x, unsigned int dim_y) {}; - matrix() {}; - virtual ~matrix() {}; + matrix() = default; // Default constructor + virtual ~matrix() = default; // Default destructor + + // Explicitly default the other special member functions + matrix(const matrix&) = default; // Copy constructor + matrix& operator=(const matrix&) = default; // Copy assignment operator + matrix(matrix&&) = default; // Move constructor + matrix& operator=(matrix&&) = default; // Move assignment operator virtual int get_xy(unsigned int x, unsigned int y) = 0; virtual void set_xy(unsigned int x, unsigned int y, int value) = 0; }; } + #endif /* end of include guard: MATRIX_BHHZ9T7P */ diff --git a/parallel/modified_kahip/lib/data_structure/matrix/normal_matrix.h b/parallel/modified_kahip/lib/data_structure/matrix/normal_matrix.h index 6e536ff2..e249747c 100644 --- a/parallel/modified_kahip/lib/data_structure/matrix/normal_matrix.h +++ b/parallel/modified_kahip/lib/data_structure/matrix/normal_matrix.h @@ -17,18 +17,17 @@ class normal_matrix : public matrix { m_lazy_init_val ( lazy_init_val ) { m_internal_matrix.resize(m_dim_x); //allocate the rest lazy }; - virtual ~normal_matrix() {}; - inline int get_xy(unsigned int x, unsigned int y) { - if( m_internal_matrix[x].size() == 0 ) { + int get_xy(unsigned int x, unsigned int y) override { + if( m_internal_matrix[x].empty() ) { return m_lazy_init_val; } return m_internal_matrix[x][y]; }; - inline void set_xy(unsigned int x, unsigned int y, int value) { + void set_xy(unsigned int x, unsigned int y, int value) override { //resize the fields lazy - if( m_internal_matrix[x].size() == 0 ) { + if( m_internal_matrix[x].empty() ) { m_internal_matrix[x].resize(m_dim_y); for( unsigned y_1 = 0; y_1 < m_dim_y; y_1++) { m_internal_matrix[x][y_1] = m_lazy_init_val; diff --git a/parallel/modified_kahip/lib/data_structure/priority_queues/bucket_pq.h b/parallel/modified_kahip/lib/data_structure/priority_queues/bucket_pq.h index 5a2d3513..2093193b 100644 --- a/parallel/modified_kahip/lib/data_structure/priority_queues/bucket_pq.h +++ b/parallel/modified_kahip/lib/data_structure/priority_queues/bucket_pq.h @@ -15,26 +15,25 @@ namespace kahip::modified { class bucket_pq : public priority_queue_interface { public: - bucket_pq( const EdgeWeight & gain_span ); + explicit bucket_pq( const EdgeWeight & gain_span ); - virtual ~bucket_pq() {}; - NodeID size(); - void insert(NodeID id, Gain gain); - bool empty(); + NodeID size() override; + void insert(NodeID node, Gain gain) override; + bool empty() override; - Gain maxValue(); - NodeID maxElement(); - NodeID deleteMax(); + Gain maxValue() override; + NodeID maxElement() override; + NodeID deleteMax() override; - void decreaseKey(NodeID node, Gain newGain); - void increaseKey(NodeID node, Gain newGain); + void decreaseKey(NodeID node, Gain new_gain) override; + void increaseKey(NodeID node, Gain new_gain) override; - void changeKey(NodeID element, Gain newKey); - Gain getKey(NodeID element); - void deleteNode(NodeID node); + void changeKey(NodeID node, Gain new_gain) override; + Gain getKey(NodeID node) override; + void deleteNode(NodeID node) override; - bool contains(NodeID node); + bool contains(NodeID node) override; private: NodeID m_elements; EdgeWeight m_gain_span; @@ -57,7 +56,7 @@ inline NodeID bucket_pq::size() { } inline void bucket_pq::insert(NodeID node, Gain gain) { - unsigned address = gain + m_gain_span; + unsigned const address = gain + m_gain_span; if(address > m_max_idx) { m_max_idx = address; } diff --git a/parallel/modified_kahip/lib/data_structure/priority_queues/maxNodeHeap.h b/parallel/modified_kahip/lib/data_structure/priority_queues/maxNodeHeap.h index 3c9b7b45..8f45b91d 100644 --- a/parallel/modified_kahip/lib/data_structure/priority_queues/maxNodeHeap.h +++ b/parallel/modified_kahip/lib/data_structure/priority_queues/maxNodeHeap.h @@ -15,13 +15,12 @@ #include "data_structure/priority_queues/priority_queue_interface.h" namespace kahip::modified { -typedef int Key; +using Key = int; template < typename Data > class QElement { public: QElement( Data data, Key key, int index ) : m_data(data), m_key (key), m_index(index) {}; - virtual ~QElement() {}; Data & get_data() { return m_data; @@ -62,26 +61,24 @@ class maxNodeHeap : public priority_queue_interface { Data( NodeID node ) : node(node) {}; }; - typedef QElement PQElement; + using PQElement = QElement; - maxNodeHeap() {}; - virtual ~maxNodeHeap() {}; - NodeID size(); - bool empty(); + NodeID size() override; + bool empty() override; - bool contains(NodeID node); - void insert(NodeID id, Gain gain); + bool contains(NodeID node) override; + void insert(NodeID node, Gain gain) override; - NodeID deleteMax(); - void deleteNode(NodeID node); - NodeID maxElement(); - Gain maxValue(); + NodeID deleteMax() override; + void deleteNode(NodeID node) override; + NodeID maxElement() override; + Gain maxValue() override; - void decreaseKey(NodeID node, Gain gain); - void increaseKey(NodeID node, Gain gain); - void changeKey(NodeID node, Gain gain); - Gain getKey(NodeID node); + void decreaseKey(NodeID node, Gain gain) override; + void increaseKey(NodeID node, Gain gain) override; + void changeKey(NodeID node, Gain gain) override; + Gain getKey(NodeID node) override; private: std::vector< PQElement > m_elements; // elements that contain the data diff --git a/parallel/modified_kahip/lib/data_structure/priority_queues/priority_queue_interface.h b/parallel/modified_kahip/lib/data_structure/priority_queues/priority_queue_interface.h index 40cfa282..38c07ff3 100644 --- a/parallel/modified_kahip/lib/data_structure/priority_queues/priority_queue_interface.h +++ b/parallel/modified_kahip/lib/data_structure/priority_queues/priority_queue_interface.h @@ -12,8 +12,13 @@ namespace kahip::modified { class priority_queue_interface { public: - priority_queue_interface( ) {}; - virtual ~priority_queue_interface() {}; + priority_queue_interface( ) = default; + virtual ~priority_queue_interface() = default; + // Explicitly default the other special member functions + priority_queue_interface(const priority_queue_interface&) = default; // Copy constructor + priority_queue_interface& operator=(const priority_queue_interface&) = default; // Copy assignment operator + priority_queue_interface(priority_queue_interface&&) = default; // Move constructor + priority_queue_interface& operator=(priority_queue_interface&&) = default; // Move assignment operator /* returns the size of the priority queue */ virtual NodeID size() = 0; diff --git a/parallel/modified_kahip/lib/io/graph_io.cpp b/parallel/modified_kahip/lib/io/graph_io.cpp index db7a7037..1c120491 100644 --- a/parallel/modified_kahip/lib/io/graph_io.cpp +++ b/parallel/modified_kahip/lib/io/graph_io.cpp @@ -8,13 +8,6 @@ #include #include "graph_io.h" namespace kahip::modified { -graph_io::graph_io() { - -} - -graph_io::~graph_io() { - -} int graph_io::writeGraphWeighted(graph_access & G, std::string filename) { std::ofstream f(filename.c_str()); diff --git a/parallel/modified_kahip/lib/io/graph_io.h b/parallel/modified_kahip/lib/io/graph_io.h index 92aeff13..59cb5576 100644 --- a/parallel/modified_kahip/lib/io/graph_io.h +++ b/parallel/modified_kahip/lib/io/graph_io.h @@ -21,8 +21,14 @@ namespace kahip::modified { class graph_io { public: - graph_io(); - virtual ~graph_io () ; + graph_io() = default; // Default constructor + virtual ~graph_io() = default; // Default destructor + + // Explicitly default the other special member functions + graph_io(const graph_io&) = default; // Copy constructor + graph_io& operator=(const graph_io&) = default; // Copy assignment operator + graph_io(graph_io&&) = default; // Move constructor + graph_io& operator=(graph_io&&) = default; // Move assignment operator static int readGraphWeighted(graph_access & G, std::string filename); diff --git a/parallel/modified_kahip/lib/parallel_mh/diversifyer.h b/parallel/modified_kahip/lib/parallel_mh/diversifyer.h index b7e85e03..d84fd3af 100644 --- a/parallel/modified_kahip/lib/parallel_mh/diversifyer.h +++ b/parallel/modified_kahip/lib/parallel_mh/diversifyer.h @@ -12,8 +12,14 @@ namespace kahip::modified { class diversifyer { public: - diversifyer() {} ; - virtual ~diversifyer() {}; + diversifyer() = default; // Default constructor + virtual ~diversifyer() = default; // Default destructor + + // Explicitly default the other special member functions + diversifyer(const diversifyer&) = default; // Copy constructor + diversifyer& operator=(const diversifyer&) = default; // Copy assignment operator + diversifyer(diversifyer&&) = default; // Move constructor + diversifyer& operator=(diversifyer&&) = default; // Move assignment operator void diversify(PartitionConfig & config) { //diversify edge rating: diff --git a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.cpp b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.cpp index 2ba9f2bf..228e3966 100644 --- a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.cpp +++ b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.cpp @@ -16,13 +16,6 @@ #include "uncoarsening/refinement/tabu_search/tabu_search.h" namespace kahip::modified { -construct_partition::construct_partition() { - -} - -construct_partition::~construct_partition() { - -} void construct_partition::construct_starting_from_partition( PartitionConfig & config, graph_access & G) { std::vector< std::queue< NodeID > > queues(config.k); diff --git a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.h b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.h index a56b281d..fd4b0eed 100644 --- a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.h +++ b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/construct_partition.h @@ -14,8 +14,14 @@ namespace kahip::modified { class construct_partition { public: - construct_partition(); - virtual ~construct_partition(); + construct_partition() = default; // Default constructor + virtual ~construct_partition() = default; // Default destructor + + // Explicitly default the other special member functions + construct_partition(const construct_partition&) = default; // Copy constructor + construct_partition& operator=(const construct_partition&) = default; // Copy assignment operator + construct_partition(construct_partition&&) = default; // Move constructor + construct_partition& operator=(construct_partition&&) = default; // Move assignment operator void construct_starting_from_partition( PartitionConfig & config, graph_access & G); void createIndividuum( PartitionConfig & config, graph_access & G, diff --git a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.cpp b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.cpp index d60461bc..045f4f66 100644 --- a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.cpp +++ b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.cpp @@ -18,14 +18,6 @@ #include "uncoarsening/refinement/refinement.h" #include "uncoarsening/refinement/tabu_search/tabu_search.h" namespace kahip::modified { -gal_combine::gal_combine() { - -} - -gal_combine::~gal_combine() { - -} - // implements our version of gal combine // compute a matching between blocks (greedily) diff --git a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.h b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.h index ff6006ce..0c823baf 100644 --- a/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.h +++ b/parallel/modified_kahip/lib/parallel_mh/galinier_combine/gal_combine.h @@ -13,8 +13,14 @@ namespace kahip::modified { class gal_combine { public: - gal_combine(); - virtual ~gal_combine(); + gal_combine() = default; // Default constructor + virtual ~gal_combine() = default; // Default destructor + + // Explicitly default the other special member functions + gal_combine(const gal_combine&) = default; // Copy constructor + gal_combine& operator=(const gal_combine&) = default; // Copy assignment operator + gal_combine(gal_combine&&) = default; // Move constructor + gal_combine& operator=(gal_combine&&) = default; // Move assignment operator void perform_gal_combine( PartitionConfig & config, graph_access & G); }; From 84d9b3681c7d446852c52bccc98659034066b4ba Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 11 Oct 2024 15:46:10 +0100 Subject: [PATCH 51/64] Parhip namespace Added Parhip Namespace --- .gitignore | 2 +- CMakeLists.txt | 70 +- parallel/modified_kahip/app/configuration.h | 66 +- .../modified_kahip/app/interface_test.cpp | 2 +- .../modified_kahip/app/parse_parameters.h | 4 +- .../interface/kaHIP_interface.cpp | 12 +- .../lib/partition/graph_partitioner.cpp | 8 +- .../lib/partition/graph_partitioner.h | 4 +- parallel/parallel_src/app/configuration.h | 36 +- parallel/parallel_src/app/dspac.cpp | 197 +-- .../app/edge_list_to_metis_graph.cpp | 2 +- .../app/friendster_list_to_metis_graph.cpp | 2 +- parallel/parallel_src/app/graph2binary.cpp | 1 + .../app/graph2binary_external.cpp | 1 + parallel/parallel_src/app/parhip.cpp | 2 +- .../parallel_src/app/parse_dspac_parameters.h | 14 +- parallel/parallel_src/app/parse_parameters.h | 32 +- parallel/parallel_src/app/readbgf.cpp | 2 +- parallel/parallel_src/app/toolbox.cpp | 2 +- .../interface/parhip_interface.cpp | 2 +- .../lib/communication/dummy_operations.cpp | 97 +- .../lib/communication/dummy_operations.h | 10 +- .../lib/communication/mpi_tools.cpp | 370 ++-- .../lib/communication/mpi_tools.h | 8 +- .../lib/communication/mpi_types.h | 27 +- .../lib/data_structure/balance_management.cpp | 4 +- .../lib/data_structure/balance_management.h | 26 +- .../balance_management_coarsening.cpp | 37 +- .../balance_management_coarsening.h | 4 +- .../balance_management_refinement.cpp | 35 +- .../balance_management_refinement.h | 4 +- .../lib/data_structure/hashed_graph.h | 15 +- .../data_structure/linear_probing_hashmap.h | 4 +- .../linear_probing_hashmap_ll.h | 3 +- .../lib/data_structure/next_prime.h | 3 +- .../data_structure/parallel_graph_access.cpp | 57 +- .../data_structure/parallel_graph_access.h | 154 +- parallel/parallel_src/lib/definitions.h | 14 +- .../distributed_partitioner.cpp | 658 +++---- .../distributed_partitioner.h | 4 +- .../distributed_evolutionary_partitioning.cpp | 342 ++-- .../distributed_evolutionary_partitioning.h | 10 +- .../initial_partitioning.cpp | 18 +- .../initial_partitioning.h | 10 +- .../random_initial_partitioning.cpp | 33 +- .../random_initial_partitioning.h | 12 +- .../lib/distributed_partitioning/stop_rule.h | 5 +- parallel/parallel_src/lib/dspac/dspac.cpp | 743 ++++---- parallel/parallel_src/lib/dspac/dspac.h | 4 +- .../lib/dspac/edge_balanced_graph_io.cpp | 377 ++-- .../lib/dspac/edge_balanced_graph_io.h | 12 +- .../parallel_src/lib/io/parallel_graph_io.cpp | 1524 ++++++++--------- .../parallel_src/lib/io/parallel_graph_io.h | 69 +- .../lib/io/parallel_vector_io.cpp | 426 ++--- .../parallel_src/lib/io/parallel_vector_io.h | 4 +- .../parallel_block_down_propagation.cpp | 277 +-- .../parallel_block_down_propagation.h | 20 +- .../parallel_contraction.cpp | 383 ++--- .../parallel_contraction.h | 7 +- .../parallel_projection.cpp | 267 +-- .../parallel_projection.h | 4 +- .../parallel_label_compress/hmap_wrapper.h | 108 +- .../parallel_label_compress/node_ordering.cpp | 4 +- .../parallel_label_compress/node_ordering.h | 30 +- .../parallel_label_compress.h | 166 +- parallel/parallel_src/lib/partition_config.h | 120 +- .../lib/tools/distributed_quality_metrics.cpp | 450 ++--- .../lib/tools/distributed_quality_metrics.h | 24 +- parallel/parallel_src/lib/tools/helpers.h | 4 +- .../lib/tools/random_functions.cpp | 3 +- .../parallel_src/lib/tools/random_functions.h | 256 +-- parallel/parallel_src/lib/tools/timer.h | 52 +- 72 files changed, 3886 insertions(+), 3873 deletions(-) diff --git a/.gitignore b/.gitignore index a239015e..9735c1b4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ build/ deploy/ .idea **/.DS_Store -cmake-build-dev +cmake-build-* diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f05ef58..28a1a549 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,24 +22,24 @@ endif() option(NONATIVEOPTIMIZATIONS "Disable --march=native optimizations" OFF) # tweak compiler flags -CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) -if(COMPILER_SUPPORTS_FUNROLL_LOOPS) - add_definitions(-funroll-loops) -endif() -CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) -if(COMPILER_SUPPORTS_FNOSTACKLIMITS) - add_definitions(-fno-stack-limit) -endif() +#CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) +#if(COMPILER_SUPPORTS_FUNROLL_LOOPS) +# add_definitions(-funroll-loops) +#endif() +#CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) +#if(COMPILER_SUPPORTS_FNOSTACKLIMITS) +# add_definitions(-fno-stack-limit) +#endif() #CHECK_CXX_COMPILER_FLAG(-Wall COMPILER_SUPPORTS_WALL) #if(COMPILER_SUPPORTS_WALL) # add_definitions(-Wall) #endif() -CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) -if(COMPILER_SUPPORTS_MARCH_NATIVE) - if( NOT NONATIVEOPTIMIZATIONS ) - add_definitions(-march=native) - endif() -endif() +#CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) +#if(COMPILER_SUPPORTS_MARCH_NATIVE) +# if( NOT NONATIVEOPTIMIZATIONS ) +# add_definitions(-march=native) +# endif() +#endif() #CHECK_CXX_COMPILER_FLAG(-fpermissive COMPILER_SUPPORTS_FPERMISSIVE) #if(COMPILER_SUPPORTS_FPERMISSIVE) # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") @@ -62,7 +62,7 @@ else() message(WARNING "OpenMP not available, activating workaround") add_library(OpenMP::OpenMP_CXX IMPORTED INTERFACE) set_property(TARGET OpenMP::OpenMP_CXX PROPERTY INTERFACE_COMPILE_OPTIONS "") - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/misc) + target_include_directories(OpenMP::OpenMP_CXX INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/misc) endif() find_package(metis CONFIG) if(metis_FOUND) @@ -209,7 +209,7 @@ set(LIBKAFFPA_SOURCE_FILES lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp) add_library(libkaffpa OBJECT ${LIBKAFFPA_SOURCE_FILES}) -target_link_libraries(libkaffpa PUBLIC kahip_headers argtable3) +target_link_libraries(libkaffpa PRIVATE kahip_headers kahip_options kahip_warnings argtable3) if(NOT NOMPI) set(LIBKAFFPA_PARALLEL_SOURCE_FILES @@ -221,7 +221,7 @@ if(NOT NOMPI) lib/tools/graph_communication.cpp lib/tools/mpi_tools.cpp) add_library(libkaffpa_parallel OBJECT ${LIBKAFFPA_PARALLEL_SOURCE_FILES}) - target_link_libraries(libkaffpa_parallel PRIVATE kahip_headers MPI::MPI_CXX) + target_link_libraries(libkaffpa_parallel PRIVATE kahip_headers kahip_options kahip_warnings MPI::MPI_CXX) endif() set(LIBMAPPING_SOURCE_FILES @@ -234,11 +234,11 @@ set(LIBMAPPING_SOURCE_FILES lib/mapping/mapping_algorithms.cpp lib/mapping/construct_mapping.cpp) add_library(libmapping OBJECT ${LIBMAPPING_SOURCE_FILES}) -target_link_libraries(libmapping PRIVATE kahip_headers) +target_link_libraries(libmapping PRIVATE kahip_headers kahip_options kahip_warnings) set(LIBSPAC_SOURCE_FILES lib/spac/spac.cpp) add_library(libspac OBJECT ${LIBSPAC_SOURCE_FILES}) -target_link_libraries(libspac PRIVATE kahip_headers) +target_link_libraries(libspac PRIVATE kahip_headers kahip_options kahip_warnings) set(NODE_ORDERING_SOURCE_FILES lib/node_ordering/min_degree_ordering.cpp @@ -246,7 +246,7 @@ set(NODE_ORDERING_SOURCE_FILES lib/node_ordering/ordering_tools.cpp lib/node_ordering/reductions.cpp) add_library(libnodeordering OBJECT ${NODE_ORDERING_SOURCE_FILES}) -target_link_libraries(libnodeordering PRIVATE kahip_headers) +target_link_libraries(libnodeordering PRIVATE kahip_headers kahip_options kahip_warnings) # generate targets for each binary @@ -254,7 +254,7 @@ add_executable(kaffpa app/kaffpa.cpp) target_link_libraries(kaffpa PRIVATE libkaffpa libmapping) target_compile_definitions(kaffpa PRIVATE "-DMODE_KAFFPA") target_link_libraries(kaffpa PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(kaffpa PRIVATE kahip_headers) +target_link_libraries(kaffpa PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS kaffpa DESTINATION bin) if (USE_TCMALLOC) find_library(TCMALLOC_LIB tcmalloc) @@ -270,7 +270,7 @@ add_executable(global_multisection app/global_multisection.cpp) target_link_libraries(global_multisection PRIVATE libkaffpa libmapping) target_compile_definitions(global_multisection PRIVATE "-DMODE_KAFFPA -DMODE_GLOBALMS") target_link_libraries(global_multisection PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(global_multisection PRIVATE kahip_headers) +target_link_libraries(global_multisection PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS global_multisection DESTINATION bin) if (USE_TCMALLOC) find_library(TCMALLOC_LIB tcmalloc) @@ -286,14 +286,14 @@ add_executable(evaluator app/evaluator.cpp) target_link_libraries(evaluator PRIVATE libkaffpa libmapping) target_compile_definitions(evaluator PRIVATE "-DMODE_EVALUATOR") target_link_libraries(evaluator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(evaluator PRIVATE kahip_headers) +target_link_libraries(evaluator PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS evaluator DESTINATION bin) add_executable(edge_evaluator app/edge_evaluator.cpp) target_link_libraries(edge_evaluator PRIVATE libkaffpa libmapping) target_compile_definitions(edge_evaluator PRIVATE "-DMODE_EVALUATOR") target_link_libraries(edge_evaluator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(edge_evaluator PRIVATE kahip_headers) +target_link_libraries(edge_evaluator PRIVATE kahip_headers kahip_options kahip_warnings) target_link_libraries(edge_evaluator PRIVATE argtable3) install(TARGETS edge_evaluator DESTINATION bin) @@ -301,21 +301,21 @@ add_executable(node_separator app/node_separator_ml.cpp) target_link_libraries(node_separator PRIVATE libkaffpa libmapping) target_compile_definitions(node_separator PRIVATE "-DMODE_NODESEP") target_link_libraries(node_separator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(node_separator PRIVATE kahip_headers) +target_link_libraries(node_separator PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS node_separator DESTINATION bin) add_executable(label_propagation app/label_propagation.cpp) target_link_libraries(label_propagation PRIVATE libkaffpa libmapping) target_compile_definitions(label_propagation PRIVATE "-DMODE_LABELPROPAGATION") target_link_libraries(label_propagation PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(label_propagation PRIVATE kahip_headers) +target_link_libraries(label_propagation PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS label_propagation DESTINATION bin) add_executable(partition_to_vertex_separator app/partition_to_vertex_separator.cpp) target_link_libraries(partition_to_vertex_separator PRIVATE libkaffpa libmapping) target_compile_definitions(partition_to_vertex_separator PRIVATE "-DMODE_PARTITIONTOVERTEXSEPARATOR") target_link_libraries(partition_to_vertex_separator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(partition_to_vertex_separator PRIVATE kahip_headers) +target_link_libraries(partition_to_vertex_separator PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS partition_to_vertex_separator DESTINATION bin) add_executable(interface_test misc/example_library_call/interface_test.cpp interface/kaHIP_interface.cpp) @@ -323,7 +323,7 @@ target_link_libraries(interface_test PRIVATE libkaffpa libmapping libnodeorderin target_include_directories(interface_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(interface_test PRIVATE "-DMODE_KAFFPA") target_link_libraries(interface_test PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(interface_test PRIVATE kahip_headers) +target_link_libraries(interface_test PRIVATE kahip_headers kahip_options kahip_warnings) if(metis_FOUND) target_link_libraries(interface_test PUBLIC metis GKlib) endif() @@ -334,7 +334,7 @@ if(NOT NOMPI) target_link_libraries(kaffpaE PRIVATE libkaffpa libmapping libkaffpa_parallel) target_compile_definitions(kaffpaE PRIVATE "-DMODE_KAFFPAE") target_link_libraries(kaffpaE PRIVATE MPI::MPI_CXX OpenMP::OpenMP_CXX) - target_link_libraries(kaffpaE PRIVATE kahip_headers) + target_link_libraries(kaffpaE PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS kaffpaE DESTINATION bin) endif() @@ -342,21 +342,21 @@ add_executable(graphchecker app/graphchecker.cpp) target_link_libraries(graphchecker PRIVATE libkaffpa libmapping) target_compile_definitions(graphchecker PRIVATE "-DMODE_GRAPHCHECKER") target_link_libraries(graphchecker PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(graphchecker PRIVATE kahip_headers) +target_link_libraries(graphchecker PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS graphchecker DESTINATION bin) add_executable(edge_partitioning app/spac.cpp) target_link_libraries(edge_partitioning PRIVATE libkaffpa libmapping libspac) target_compile_definitions(edge_partitioning PRIVATE "-DMODE_KAFFPA") target_link_libraries(edge_partitioning PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(edge_partitioning PRIVATE kahip_headers) +target_link_libraries(edge_partitioning PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS edge_partitioning DESTINATION bin) add_executable(node_ordering app/node_ordering.cpp) target_link_libraries(node_ordering PRIVATE libkaffpa libnodeordering) target_compile_definitions(node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING") target_link_libraries(node_ordering PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(node_ordering PRIVATE kahip_headers) +target_link_libraries(node_ordering PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS node_ordering DESTINATION bin) if(metis_FOUND) @@ -364,7 +364,7 @@ if(metis_FOUND) target_link_libraries(fast_node_ordering PRIVATE libkaffpa libmapping libnodeordering) target_compile_definitions(fast_node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING -DFASTORDERING") target_link_libraries(fast_node_ordering PRIVATE OpenMP::OpenMP_CXX metis GKlib) - target_link_libraries(fast_node_ordering PRIVATE kahip_headers) + target_link_libraries(fast_node_ordering PRIVATE kahip_headers kahip_options kahip_warnings) install(TARGETS fast_node_ordering DESTINATION bin) endif() @@ -375,7 +375,7 @@ target_link_libraries(kahip PRIVATE libkaffpa libmapping libnodeordering libspac target_include_directories(kahip PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(kahip PRIVATE "-DMODE_KAFFPA") target_link_libraries(kahip PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(kahip PRIVATE kahip_headers) +target_link_libraries(kahip PRIVATE kahip_headers kahip_options kahip_warnings) if(metis_FOUND) target_link_libraries(kahip PRIVATE metis GKlib) endif() @@ -388,7 +388,7 @@ target_include_directories(kahip_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inter target_compile_definitions(kahip_static PRIVATE "-DMODE_KAFFPA") target_link_libraries(kahip_static PRIVATE OpenMP::OpenMP_CXX) set_target_properties(kahip_static PROPERTIES PUBLIC_HEADER interface/kaHIP_interface.h) -target_link_libraries(kahip_static PRIVATE kahip_headers) +target_link_libraries(kahip_static PRIVATE kahip_headers kahip_options kahip_warnings) if(metis_FOUND) target_link_libraries(kahip_static PRIVATE metis GKlib) endif() diff --git a/parallel/modified_kahip/app/configuration.h b/parallel/modified_kahip/app/configuration.h index b90929b6..0c800d61 100644 --- a/parallel/modified_kahip/app/configuration.h +++ b/parallel/modified_kahip/app/configuration.h @@ -9,21 +9,21 @@ #define CONFIGURATION_3APG5V7Z #include "partition/partition_config.h" - +namespace kahip::modified { class configuration { - public: - configuration() {} ; - virtual ~configuration() {}; - - void strong(kahip::modified::PartitionConfig & config ); - void eco(kahip::modified::PartitionConfig & config ); - void fast(kahip::modified::PartitionConfig & config ); - void standard(kahip::modified::PartitionConfig & config ); - void standardsnw(kahip::modified::PartitionConfig & config ); - - void fastsocial(kahip::modified::PartitionConfig & config ); - void ecosocial(kahip::modified::PartitionConfig & config ); - void strongsocial(kahip::modified::PartitionConfig & config ); +public: + configuration() {} ; + virtual ~configuration() {}; + + void strong(kahip::modified::PartitionConfig & config ); + void eco(kahip::modified::PartitionConfig & config ); + void fast(kahip::modified::PartitionConfig & config ); + void standard(kahip::modified::PartitionConfig & config ); + void standardsnw(kahip::modified::PartitionConfig & config ); + + void fastsocial(kahip::modified::PartitionConfig & config ); + void ecosocial(kahip::modified::PartitionConfig & config ); + void strongsocial(kahip::modified::PartitionConfig & config ); }; inline void configuration::strong( @@ -46,7 +46,7 @@ inline void configuration::strong( partition_config.kway_adaptive_limits_alpha = 10; partition_config.kway_rounds = 10; partition_config.rate_first_level_inner_outer = true; - partition_config.use_wcycles = false; + partition_config.use_wcycles = false; partition_config.no_new_initial_partitioning = true; partition_config.use_fullmultigrid = true; partition_config.most_balanced_minimum_cuts = true; @@ -54,8 +54,8 @@ inline void configuration::strong( partition_config.local_multitry_rounds = 10; partition_config.mh_initial_population_fraction = 10; - partition_config.mh_flip_coin = 1; - partition_config.epsilon = 3; + partition_config.mh_flip_coin = 1; + partition_config.epsilon = 3; partition_config.initial_partitioning_type = INITIAL_PARTITIONING_RECPARTITION; partition_config.bipartition_tries = 4; @@ -72,7 +72,7 @@ inline void configuration::eco( standard(partition_config); partition_config.eco = true; partition_config.aggressive_random_levels = std::max(2, (int)(7 - log2(partition_config.k))); - + partition_config.kway_rounds = std::min(5, (int)log2(partition_config.k)); partition_config.matching_type = MATCHING_RANDOM_GPA; partition_config.permutation_quality = PERMUTATION_QUALITY_NONE; @@ -85,7 +85,7 @@ inline void configuration::eco( partition_config.kway_stop_rule = KWAY_SIMPLE_STOP_RULE; partition_config.kway_fm_search_limit = 1; partition_config.mh_initial_population_fraction = 50; - partition_config.mh_flip_coin = 1; + partition_config.mh_flip_coin = 1; partition_config.initial_partitioning_type = INITIAL_PARTITIONING_RECPARTITION; partition_config.bipartition_tries = 4; @@ -101,11 +101,11 @@ inline void configuration::fast( partition_config.fast = true; if(partition_config.k > 8) { partition_config.quotient_graph_refinement_disabled = true; - partition_config.kway_fm_search_limit = 0; - partition_config.kway_stop_rule = KWAY_SIMPLE_STOP_RULE; - partition_config.corner_refinement_enabled = true; + partition_config.kway_fm_search_limit = 0; + partition_config.kway_stop_rule = KWAY_SIMPLE_STOP_RULE; + partition_config.corner_refinement_enabled = true; } else { - partition_config.corner_refinement_enabled = false; + partition_config.corner_refinement_enabled = false; } partition_config.permutation_quality = PERMUTATION_QUALITY_FAST; partition_config.permutation_during_refinement = PERMUTATION_QUALITY_NONE; @@ -143,11 +143,11 @@ inline void configuration::standard( partition_config.minipreps = 10; partition_config.enable_omp = false; partition_config.combine = false; - partition_config.epsilon = 3; + partition_config.epsilon = 3; partition_config.buffoon = false; partition_config.ultra_fast_kaffpaE_interfacecall = false; - partition_config.time_limit = 0; + partition_config.time_limit = 0; partition_config.mh_pool_size = 5; partition_config.mh_plain_repetitions = false; partition_config.no_unsuc_reps = 10; @@ -156,7 +156,7 @@ inline void configuration::standard( partition_config.mh_disable_nc_combine = false; partition_config.mh_disable_cross_combine = false; partition_config.mh_disable_combine = false; - partition_config.mh_enable_quickstart = false; + partition_config.mh_enable_quickstart = false; partition_config.mh_disable_diversify_islands = false; partition_config.mh_diversify = true; partition_config.mh_diversify_best = false; @@ -167,8 +167,8 @@ inline void configuration::standard( partition_config.mh_print_log = false; partition_config.mh_penalty_for_unconnected = false; partition_config.mh_no_mh = false; - partition_config.mh_optimize_communication_volume = false; - partition_config.use_bucket_queues = true; + partition_config.mh_optimize_communication_volume = false; + partition_config.use_bucket_queues = true; partition_config.walshaw_mh_repetitions = 50; partition_config.scaleing_factor = 1; partition_config.scale_back = false; @@ -178,7 +178,7 @@ inline void configuration::standard( partition_config.suppress_partitioner_output = false; - if( partition_config.k <= 4 ) { + if( partition_config.k <= 4 ) { partition_config.bipartition_post_fm_limits = 30; partition_config.bipartition_post_ml_limits = 6; } else { @@ -249,13 +249,13 @@ inline void configuration::standard( partition_config.maxIter = 500000; if( partition_config.k <= 8 ) { - partition_config.kaba_internal_no_aug_steps_aug = 15; + partition_config.kaba_internal_no_aug_steps_aug = 15; } else { - partition_config.kaba_internal_no_aug_steps_aug = 7; + partition_config.kaba_internal_no_aug_steps_aug = 7; } partition_config.kaba_unsucc_iterations = 6; - partition_config.initial_bipartitioning = false; + partition_config.initial_bipartitioning = false; partition_config.kabapE = false; @@ -344,5 +344,5 @@ inline void configuration::strongsocial( } - +} #endif /* end of include guard: CONFIGURATION_3APG5V7Z */ diff --git a/parallel/modified_kahip/app/interface_test.cpp b/parallel/modified_kahip/app/interface_test.cpp index 8f0fe7d9..f3e2efab 100644 --- a/parallel/modified_kahip/app/interface_test.cpp +++ b/parallel/modified_kahip/app/interface_test.cpp @@ -28,7 +28,7 @@ int main(int argn, char **argv) { - + using namespace kahip::modified; PartitionConfig partition_config; std::string graph_filename; diff --git a/parallel/modified_kahip/app/parse_parameters.h b/parallel/modified_kahip/app/parse_parameters.h index 4323e44c..89416392 100644 --- a/parallel/modified_kahip/app/parse_parameters.h +++ b/parallel/modified_kahip/app/parse_parameters.h @@ -12,7 +12,7 @@ #include #endif #include "configuration.h" - +namespace kahip::modified { int parse_parameters(int argn, char **argv, PartitionConfig & partition_config, std::string & graph_filename, @@ -754,5 +754,5 @@ int parse_parameters(int argn, char **argv, return 0; } - +} #endif /* end of include guard: PARSE_PARAMETERS_GPJMGSM8 */ diff --git a/parallel/modified_kahip/interface/kaHIP_interface.cpp b/parallel/modified_kahip/interface/kaHIP_interface.cpp index 3f8065d0..27e21192 100644 --- a/parallel/modified_kahip/interface/kaHIP_interface.cpp +++ b/parallel/modified_kahip/interface/kaHIP_interface.cpp @@ -99,7 +99,7 @@ void internal_kaffpa_call(PartitionConfig & partition_config, cout.rdbuf(backup); } - +} void kaffpa(int* n, int* vwgt, @@ -113,6 +113,7 @@ void kaffpa(int* n, int mode, int* edgecut, int* part) { + using namespace kahip::modified; configuration cfg; PartitionConfig partition_config; partition_config.k = *nparts; @@ -146,7 +147,7 @@ void kaffpa(int* n, internal_kaffpa_call(partition_config, suppress_output, n, vwgt, xadj, adjcwgt, adjncy, nparts, imbalance, edgecut, part); } - +namespace kahip::modified { void internal_nodeseparator_call(PartitionConfig & partition_config, bool suppress_output, int* n, @@ -193,7 +194,7 @@ void internal_nodeseparator_call(PartitionConfig & partition_config, ofs.close(); cout.rdbuf(backup); } - +} void node_separator(int* n, int* vwgt, @@ -207,6 +208,7 @@ void node_separator(int* n, int mode, int* num_separator_vertices, int** separator) { + using namespace kahip::modified; configuration cfg; PartitionConfig partition_config; partition_config.k = *nparts; @@ -256,7 +258,7 @@ void kaffpaE(int* n, int* edgecut, double* balance, int* part) { - + using namespace kahip::modified; configuration cfg; PartitionConfig partition_config; partition_config.k = *nparts; @@ -324,5 +326,5 @@ void kaffpaE(int* n, //ofs.close(); //cout.rdbuf(backup); } -} + diff --git a/parallel/modified_kahip/lib/partition/graph_partitioner.cpp b/parallel/modified_kahip/lib/partition/graph_partitioner.cpp index 5775c68d..4ad2e176 100644 --- a/parallel/modified_kahip/lib/partition/graph_partitioner.cpp +++ b/parallel/modified_kahip/lib/partition/graph_partitioner.cpp @@ -14,14 +14,8 @@ #include "uncoarsening/uncoarsening.h" #include "uncoarsening/refinement/mixed_refinement.h" #include "w_cycles/wcycle_partitioner.h" -namespace kahip::modified { -graph_partitioner::graph_partitioner() { - -} -graph_partitioner::~graph_partitioner() { - -} +namespace kahip::modified { void graph_partitioner::perform_recursive_partitioning(PartitionConfig & config, graph_access & G) { m_global_k = config.k; diff --git a/parallel/modified_kahip/lib/partition/graph_partitioner.h b/parallel/modified_kahip/lib/partition/graph_partitioner.h index 3215c891..39f1ab17 100644 --- a/parallel/modified_kahip/lib/partition/graph_partitioner.h +++ b/parallel/modified_kahip/lib/partition/graph_partitioner.h @@ -16,8 +16,8 @@ namespace kahip::modified { class graph_partitioner { public: - graph_partitioner(); - virtual ~graph_partitioner(); + graph_partitioner() = default; + virtual ~graph_partitioner() = default; void perform_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); void perform_recursive_partitioning(PartitionConfig & graph_partitioner_config, graph_access & G); diff --git a/parallel/parallel_src/app/configuration.h b/parallel/parallel_src/app/configuration.h index e2326823..63b42e9f 100644 --- a/parallel/parallel_src/app/configuration.h +++ b/parallel/parallel_src/app/configuration.h @@ -10,17 +10,17 @@ #define CONFIGURATION_3APG5V7ZA #include "partition_config.h" - +namespace parhip { class configuration { - public: - configuration() {} ; - virtual ~configuration() {}; +public: + configuration() {} ; + virtual ~configuration() {}; - void standard( PPartitionConfig & config ); - void ultrafast( PPartitionConfig & config ); - void fast( PPartitionConfig & config ); - void eco( PPartitionConfig & config ); - void strong( PPartitionConfig & config ); + void standard( PPartitionConfig & config ); + void ultrafast( PPartitionConfig & config ); + void fast( PPartitionConfig & config ); + void eco( PPartitionConfig & config ); + void strong( PPartitionConfig & config ); }; inline void configuration::ultrafast( PPartitionConfig & partition_config ) { @@ -56,13 +56,13 @@ inline void configuration::standard( PPartitionConfig & partition_config ) { partition_config.k = 2; partition_config.inbalance = 3; partition_config.epsilon = 3; - partition_config.time_limit = 0; - partition_config.evolutionary_time_limit = 0; + partition_config.time_limit = 0; + partition_config.evolutionary_time_limit = 0; partition_config.log_num_verts = 16; partition_config.edge_factor = 16; - partition_config.generate_rgg = false; - partition_config.generate_ba = false; - partition_config.comm_rounds = 128; + partition_config.generate_rgg = false; + partition_config.generate_ba = false; + partition_config.comm_rounds = 128; partition_config.label_iterations = 4; partition_config.label_iterations_coarsening = 3; partition_config.label_iterations_refinement = 6; @@ -76,7 +76,7 @@ inline void configuration::standard( PPartitionConfig & partition_config ) { partition_config.no_refinement_in_last_iteration = false; partition_config.ht_fill_factor = 1.6; partition_config.eco = false; - partition_config.binary_io_window_size = 64; + partition_config.binary_io_window_size = 64; partition_config.barabasi_albert_mindegree = 5; partition_config.compute_degree_sequence_ba = true; partition_config.compute_degree_sequence_k_first = false; @@ -84,10 +84,10 @@ inline void configuration::standard( PPartitionConfig & partition_config ) { partition_config.kronecker_internal_only = false; partition_config.generate_ba_32bit = false; partition_config.n = 0; - partition_config.save_partition = false; - partition_config.save_partition_binary = false; + partition_config.save_partition = false; + partition_config.save_partition_binary = false; partition_config.vertex_degree_weights = false; partition_config.converter_evaluate = false; } - +} #endif /* end of include guard: CONFIGURATION_3APG5V7Z */ diff --git a/parallel/parallel_src/app/dspac.cpp b/parallel/parallel_src/app/dspac.cpp index 3b854630..f84fb0ef 100644 --- a/parallel/parallel_src/app/dspac.cpp +++ b/parallel/parallel_src/app/dspac.cpp @@ -30,10 +30,12 @@ #include "tools/distributed_quality_metrics.h" #include "dspac/dspac.h" #include "dspac/edge_balanced_graph_io.h" - +namespace parhip { static void executeParhip(parallel_graph_access &G, PPartitionConfig &partitionConfig); +} int main(int argn, char **argv) { + using namespace parhip; MPI_Init(&argn, &argv); int rank, size; @@ -136,115 +138,116 @@ int main(int argn, char **argv) { MPI_Barrier(MPI_COMM_WORLD); MPI_Finalize(); } - +namespace parhip { static void executeParhip(parallel_graph_access &G, PPartitionConfig &partitionConfig) { - timer t; - int rank, size; - MPI_Comm communicator = MPI_COMM_WORLD; - MPI_Comm_rank(communicator, &rank); - MPI_Comm_size(communicator, &size); + timer t; + int rank, size; + MPI_Comm communicator = MPI_COMM_WORLD; + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); - if (communicator != MPI_COMM_NULL) { - MPI_Comm_rank(communicator, &rank); - MPI_Comm_size(communicator, &size); + if (communicator != MPI_COMM_NULL) { + MPI_Comm_rank(communicator, &rank); + MPI_Comm_size(communicator, &size); - if (rank == ROOT) { - PRINT(std::cout << "log> cluster coarsening factor is set to " - << partitionConfig.cluster_coarsening_factor << std::endl;) - } + if (rank == ROOT) { + PRINT(std::cout << "log> cluster coarsening factor is set to " + << partitionConfig.cluster_coarsening_factor << std::endl;) + } - partitionConfig.stop_factor /= partitionConfig.k; - if (rank != 0) partitionConfig.seed = partitionConfig.seed * size + rank; - srand(static_cast(partitionConfig.seed)); - - random_functions::setSeed(partitionConfig.seed); - parallel_graph_access::set_comm_rounds(partitionConfig.comm_rounds / size); - parallel_graph_access::set_comm_rounds_up(partitionConfig.comm_rounds / size); - distributed_partitioner::generate_random_choices(partitionConfig); - - G.printMemoryUsage(std::cout); - - //compute some stats - EdgeWeight interPEedges = 0; - EdgeWeight localEdges = 0; - forall_local_nodes(G, node) - { - forall_out_edges(G, e, node) - { - NodeID target = G.getEdgeTarget(e); - if (!G.is_local_node(target)) { - interPEedges++; - } else { - localEdges++; - } - } - endfor - } - endfor + partitionConfig.stop_factor /= partitionConfig.k; + if (rank != 0) partitionConfig.seed = partitionConfig.seed * size + rank; + srand(static_cast(partitionConfig.seed)); - EdgeWeight globalInterEdges = 0; - EdgeWeight globalIntraEdges = 0; - MPI_Reduce(&interPEedges, &globalInterEdges, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, ROOT, communicator); - MPI_Reduce(&localEdges, &globalIntraEdges, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, ROOT, communicator); + random_functions::setSeed(partitionConfig.seed); + parallel_graph_access::set_comm_rounds(partitionConfig.comm_rounds / size); + parallel_graph_access::set_comm_rounds_up(partitionConfig.comm_rounds / size); + distributed_partitioner::generate_random_choices(partitionConfig); - if (rank == ROOT) { - std::cout << "log> ghost edges " << globalInterEdges / (double) G.number_of_global_edges() << std::endl; - std::cout << "log> local edges " << globalIntraEdges / (double) G.number_of_global_edges() << std::endl; - } + G.printMemoryUsage(std::cout); - t.restart(); - double epsilon = (partitionConfig.inbalance) / 100.0; - if (partitionConfig.vertex_degree_weights) { - throw std::logic_error("not allowed to overwrite vertex degrees"); + //compute some stats + EdgeWeight interPEedges = 0; + EdgeWeight localEdges = 0; + forall_local_nodes(G, node) + { + forall_out_edges(G, e, node) + { + NodeID target = G.getEdgeTarget(e); + if (!G.is_local_node(target)) { + interPEedges++; } else { - partitionConfig.number_of_overall_nodes = G.number_of_global_nodes(); - partitionConfig.upper_bound_partition = - (1 + epsilon) * ceil(G.number_of_global_nodes() / (double) partitionConfig.k); + localEdges++; } + } + endfor + } + endfor + EdgeWeight globalInterEdges = 0; + EdgeWeight globalIntraEdges = 0; + MPI_Reduce(&interPEedges, &globalInterEdges, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, ROOT, communicator); + MPI_Reduce(&localEdges, &globalIntraEdges, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, ROOT, communicator); - distributed_partitioner dpart; - dpart.perform_partitioning(communicator, partitionConfig, G); - - MPI_Barrier(communicator); - - double running_time = t.elapsed(); - distributed_quality_metrics qm; - EdgeWeight edge_cut = qm.edge_cut(G, communicator); - double balance = qm.balance(partitionConfig, G, communicator); - PRINT(double - balance_load = qm.balance_load(partitionConfig, G, communicator);) - PRINT(double - balance_load_dist = qm.balance_load_dist(partitionConfig, G, communicator);) - - if (rank == ROOT) { - std::cout << "log>" << "=====================================" << std::endl; - std::cout << "log>" << "============AND WE R DONE============" << std::endl; - std::cout << "log>" << "=====================================" << std::endl; - std::cout << "log>total partitioning time elapsed " << running_time << std::endl; - std::cout << "log>final edge cut " << edge_cut << std::endl; - std::cout << "log>final balance " << balance << std::endl; - PRINT(std::cout << "log>final balance load " << balance_load << std::endl;) - PRINT(std::cout << "log>final balance load dist " << balance_load_dist << std::endl;) - } - PRINT(qm.comm_vol(partitionConfig, G, communicator);) - PRINT(qm.comm_vol_dist(G, communicator);) + if (rank == ROOT) { + std::cout << "log> ghost edges " << globalInterEdges / (double) G.number_of_global_edges() << std::endl; + std::cout << "log> local edges " << globalIntraEdges / (double) G.number_of_global_edges() << std::endl; + } + + t.restart(); + double epsilon = (partitionConfig.inbalance) / 100.0; + if (partitionConfig.vertex_degree_weights) { + throw std::logic_error("not allowed to overwrite vertex degrees"); + } else { + partitionConfig.number_of_overall_nodes = G.number_of_global_nodes(); + partitionConfig.upper_bound_partition = + (1 + epsilon) * ceil(G.number_of_global_nodes() / (double) partitionConfig.k); } - MPI_Status st; - int flag; + + distributed_partitioner dpart; + dpart.perform_partitioning(communicator, partitionConfig, G); + + MPI_Barrier(communicator); + + double running_time = t.elapsed(); + distributed_quality_metrics qm; + EdgeWeight edge_cut = qm.edge_cut(G, communicator); + double balance = qm.balance(partitionConfig, G, communicator); + PRINT(double + balance_load = qm.balance_load(partitionConfig, G, communicator);) + PRINT(double + balance_load_dist = qm.balance_load_dist(partitionConfig, G, communicator);) + + if (rank == ROOT) { + std::cout << "log>" << "=====================================" << std::endl; + std::cout << "log>" << "============AND WE R DONE============" << std::endl; + std::cout << "log>" << "=====================================" << std::endl; + std::cout << "log>total partitioning time elapsed " << running_time << std::endl; + std::cout << "log>final edge cut " << edge_cut << std::endl; + std::cout << "log>final balance " << balance << std::endl; + PRINT(std::cout << "log>final balance load " << balance_load << std::endl;) + PRINT(std::cout << "log>final balance load dist " << balance_load_dist << std::endl;) + } + PRINT(qm.comm_vol(partitionConfig, G, communicator);) + PRINT(qm.comm_vol_dist(G, communicator);) +} + + MPI_Status st; + int flag; + MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &st); + while (flag) { + std::cout << "attention: still incoming messages! rank " << rank << " from " << st.MPI_SOURCE << std::endl; + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + MPI_Status rst; + std::vector message; + message.resize(message_length); + MPI_Recv(&message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, st.MPI_TAG, MPI_COMM_WORLD, &rst); MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &st); - while (flag) { - std::cout << "attention: still incoming messages! rank " << rank << " from " << st.MPI_SOURCE << std::endl; - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - MPI_Status rst; - std::vector message; - message.resize(message_length); - MPI_Recv(&message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, st.MPI_TAG, MPI_COMM_WORLD, &rst); - MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &st); - }; - MPI_Barrier(MPI_COMM_WORLD); + }; + MPI_Barrier(MPI_COMM_WORLD); } +} \ No newline at end of file diff --git a/parallel/parallel_src/app/edge_list_to_metis_graph.cpp b/parallel/parallel_src/app/edge_list_to_metis_graph.cpp index ede1bd1f..4b917d44 100644 --- a/parallel/parallel_src/app/edge_list_to_metis_graph.cpp +++ b/parallel/parallel_src/app/edge_list_to_metis_graph.cpp @@ -22,7 +22,7 @@ using namespace std; int main(int argn, char **argv) { - + using namespace parhip; MPI_Init(&argn, &argv); PPartitionConfig partition_config; diff --git a/parallel/parallel_src/app/friendster_list_to_metis_graph.cpp b/parallel/parallel_src/app/friendster_list_to_metis_graph.cpp index 9b21366d..008823e7 100644 --- a/parallel/parallel_src/app/friendster_list_to_metis_graph.cpp +++ b/parallel/parallel_src/app/friendster_list_to_metis_graph.cpp @@ -22,7 +22,7 @@ using namespace std; int main(int argn, char **argv) { - + using namespace parhip; MPI_Init(&argn, &argv); /* starts MPI */ PPartitionConfig partition_config; std::string graph_filename; diff --git a/parallel/parallel_src/app/graph2binary.cpp b/parallel/parallel_src/app/graph2binary.cpp index 988b57d3..9273101e 100644 --- a/parallel/parallel_src/app/graph2binary.cpp +++ b/parallel/parallel_src/app/graph2binary.cpp @@ -17,6 +17,7 @@ const long header_count = 3; int main(int argn, char **argv) { + using namespace parhip; std::cout << "program converts a METIS graph file into a binary (distributed graph format) file. " << std::endl; MPI_Init(&argn, &argv); /* starts MPI */ diff --git a/parallel/parallel_src/app/graph2binary_external.cpp b/parallel/parallel_src/app/graph2binary_external.cpp index 41330b02..67c10c40 100644 --- a/parallel/parallel_src/app/graph2binary_external.cpp +++ b/parallel/parallel_src/app/graph2binary_external.cpp @@ -16,6 +16,7 @@ const long header_count = 3; int main(int argn, char **argv) { + using namespace parhip; std::cout << "program converts a METIS graph file into a binary (distributed graph format) file. " << std::endl; MPI_Init(&argn, &argv); /* starts MPI */ diff --git a/parallel/parallel_src/app/parhip.cpp b/parallel/parallel_src/app/parhip.cpp index f6ffff66..00618a4d 100644 --- a/parallel/parallel_src/app/parhip.cpp +++ b/parallel/parallel_src/app/parhip.cpp @@ -29,7 +29,7 @@ #include "tools/distributed_quality_metrics.h" int main(int argn, char **argv) { - + using namespace parhip; MPI_Init(&argn, &argv); /* starts MPI */ PPartitionConfig partition_config; diff --git a/parallel/parallel_src/app/parse_dspac_parameters.h b/parallel/parallel_src/app/parse_dspac_parameters.h index f86f866f..12e93f91 100644 --- a/parallel/parallel_src/app/parse_dspac_parameters.h +++ b/parallel/parallel_src/app/parse_dspac_parameters.h @@ -10,7 +10,7 @@ #include #include #include "configuration.h" - +namespace parhip { struct DspacConfig { EdgeWeight infinity; PartitionID k; @@ -34,9 +34,9 @@ int parse_dspac_parameters(int argn, char **argv, PPartitionConfig &partition_co // Define argtable. void *argtable[] = { - help, filename, k, seed, infinity, preconfiguration, imbalance, partition_filename, save_partition, save_partition_binary, end - - }; + help, filename, k, seed, infinity, preconfiguration, imbalance, partition_filename, save_partition, save_partition_binary, end + +}; // Parse arguments. int nerrors = arg_parse(argn, argv, argtable); @@ -113,11 +113,11 @@ int parse_dspac_parameters(int argn, char **argv, PPartitionConfig &partition_co } if(save_partition->count > 0) { - partition_config.save_partition = true; + partition_config.save_partition = true; } if(save_partition_binary->count > 0) { - partition_config.save_partition_binary = true; + partition_config.save_partition_binary = true; } if (imbalance->count > 0) { @@ -133,5 +133,5 @@ int parse_dspac_parameters(int argn, char **argv, PPartitionConfig &partition_co return 0; } - +} #endif // KAHIP_PARSE_DSPAC_PARAMETERS_H diff --git a/parallel/parallel_src/app/parse_parameters.h b/parallel/parallel_src/app/parse_parameters.h index aa95acc2..737b2eb9 100644 --- a/parallel/parallel_src/app/parse_parameters.h +++ b/parallel/parallel_src/app/parse_parameters.h @@ -13,8 +13,8 @@ #include #include "configuration.h" #include "version.h" - -int parse_parameters(int argn, char **argv, +namespace parhip { +int parse_parameters(int argn, char **argv, PPartitionConfig & partition_config, std::string & graph_filename) { @@ -58,10 +58,10 @@ int parse_parameters(int argn, char **argv, void* argtable[] = { #ifdef PARALLEL_LABEL_COMPRESSION help, filename, user_seed, version, k, inbalance, preconfiguration, vertex_degree_weights, - save_partition, save_partition_binary, -#elif defined TOOLBOX + save_partition, save_partition_binary, +#elif defined TOOLBOX help, filename, k_opt, input_partition_filename, save_partition, save_partition_binary, converter_evaluate, -#endif +#endif end }; @@ -129,7 +129,7 @@ int parse_parameters(int argn, char **argv, } } -#else +#else if(filename->count > 0) { graph_filename = filename->sval[0]; } @@ -165,17 +165,17 @@ int parse_parameters(int argn, char **argv, partition_config.vertex_degree_weights = true; } - if(converter_evaluate->count > 0) { - partition_config.converter_evaluate = true; - } + if(converter_evaluate->count > 0) { + partition_config.converter_evaluate = true; + } - if(save_partition->count > 0) { - partition_config.save_partition = true; - } + if(save_partition->count > 0) { + partition_config.save_partition = true; + } - if(save_partition_binary->count > 0) { - partition_config.save_partition_binary = true; - } + if(save_partition_binary->count > 0) { + partition_config.save_partition_binary = true; + } if(n->count > 0) { partition_config.n = pow(10,n->ival[0]); @@ -282,5 +282,5 @@ int parse_parameters(int argn, char **argv, arg_freetable(argtable_fordeletion, sizeof(argtable_fordeletion) / sizeof(argtable_fordeletion[0])); return 0; } - +} #endif /* end of include guard: PARSE_PARAMETERS_GPJMGSM8 */ diff --git a/parallel/parallel_src/app/readbgf.cpp b/parallel/parallel_src/app/readbgf.cpp index 89509586..733acf62 100644 --- a/parallel/parallel_src/app/readbgf.cpp +++ b/parallel/parallel_src/app/readbgf.cpp @@ -18,7 +18,7 @@ const long header_count = 3; int main(int argn, char **argv) { - + using namespace parhip; MPI_Init(&argn, &argv); /* starts MPI */ int rank, size; diff --git a/parallel/parallel_src/app/toolbox.cpp b/parallel/parallel_src/app/toolbox.cpp index 58863567..06077bca 100644 --- a/parallel/parallel_src/app/toolbox.cpp +++ b/parallel/parallel_src/app/toolbox.cpp @@ -29,7 +29,7 @@ #include "tools/distributed_quality_metrics.h" int main(int argn, char **argv) { - + using namespace parhip; MPI_Init(&argn, &argv); /* starts MPI */ PPartitionConfig partition_config; diff --git a/parallel/parallel_src/interface/parhip_interface.cpp b/parallel/parallel_src/interface/parhip_interface.cpp index b76bb54f..cd4147db 100644 --- a/parallel/parallel_src/interface/parhip_interface.cpp +++ b/parallel/parallel_src/interface/parhip_interface.cpp @@ -12,7 +12,7 @@ void ParHIPPartitionKWay(idxtype *vtxdist, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *nparts, double* imbalance, bool suppress_output, int seed, int mode, int *edgecut, idxtype *part, MPI_Comm *comm) { - + using namespace parhip; std::streambuf* backup = std::cout.rdbuf(); diff --git a/parallel/parallel_src/lib/communication/dummy_operations.cpp b/parallel/parallel_src/lib/communication/dummy_operations.cpp index 27e5ab9e..5904c6b1 100644 --- a/parallel/parallel_src/lib/communication/dummy_operations.cpp +++ b/parallel/parallel_src/lib/communication/dummy_operations.cpp @@ -8,7 +8,7 @@ #include #include #include "dummy_operations.h" - +namespace parhip { dummy_operations::dummy_operations() { } @@ -18,57 +18,58 @@ dummy_operations::~dummy_operations() { } void dummy_operations::run_collective_dummy_operations() { - int rank, size; - MPI_Comm_rank( MPI_COMM_WORLD, &rank); - MPI_Comm_size( MPI_COMM_WORLD, &size); - - // Run Broadcast - { - int x; - MPI_Comm_rank( MPI_COMM_WORLD, &x); - MPI_Bcast(&x, 1, MPI_INT, 0, MPI_COMM_WORLD); - } - // Run Allgather. - { - int x, size; - MPI_Comm_rank( MPI_COMM_WORLD, &x); - MPI_Comm_size( MPI_COMM_WORLD, &size); - - std::vector rcv(size); - MPI_Allgather(&x, 1, MPI_INT, &rcv[0], 1, MPI_INT, MPI_COMM_WORLD); - } + int rank, size; + MPI_Comm_rank( MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &size); - // Run Allreduce. - { - int x; - MPI_Comm_rank( MPI_COMM_WORLD, &x); - - int y = 0; - MPI_Allreduce(&x, &y, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - } + // Run Broadcast + { + int x; + MPI_Comm_rank( MPI_COMM_WORLD, &x); + MPI_Bcast(&x, 1, MPI_INT, 0, MPI_COMM_WORLD); + } + // Run Allgather. + { + int x, size; + MPI_Comm_rank( MPI_COMM_WORLD, &x); + MPI_Comm_size( MPI_COMM_WORLD, &size); + + std::vector rcv(size); + MPI_Allgather(&x, 1, MPI_INT, &rcv[0], 1, MPI_INT, MPI_COMM_WORLD); + } + + // Run Allreduce. + { + int x; + MPI_Comm_rank( MPI_COMM_WORLD, &x); + + int y = 0; + MPI_Allreduce(&x, &y, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + } - // Dummy Prefix Sum - { - int x = 1; - int y = 0; + // Dummy Prefix Sum + { + int x = 1; + int y = 0; - MPI_Scan(&x, &y, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - } + MPI_Scan(&x, &y, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + } - // Run Alltoallv. - { - std::vector snd(size); - std::vector rcv(size); - std::vector scounts(size, 1); - std::vector rcounts(size, 1); - std::vector sdispls(size); - std::vector rdispls(size); - for (int i = 0, iend = sdispls.size(); i < iend; ++i) { - sdispls[i] = rdispls[i] = i; - } - MPI_Alltoallv(&snd[0], &scounts[0], &sdispls[0], MPI_INT, - &rcv[0], &rcounts[0], &rdispls[0], MPI_INT, MPI_COMM_WORLD); - } + // Run Alltoallv. + { + std::vector snd(size); + std::vector rcv(size); + std::vector scounts(size, 1); + std::vector rcounts(size, 1); + std::vector sdispls(size); + std::vector rdispls(size); + for (int i = 0, iend = sdispls.size(); i < iend; ++i) { + sdispls[i] = rdispls[i] = i; + } + MPI_Alltoallv(&snd[0], &scounts[0], &sdispls[0], MPI_INT, + &rcv[0], &rcounts[0], &rdispls[0], MPI_INT, MPI_COMM_WORLD); + } } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/communication/dummy_operations.h b/parallel/parallel_src/lib/communication/dummy_operations.h index 6bf25251..766f8297 100644 --- a/parallel/parallel_src/lib/communication/dummy_operations.h +++ b/parallel/parallel_src/lib/communication/dummy_operations.h @@ -8,14 +8,14 @@ #ifndef DUMMY_OPERATIONS_UVZ6V6T7 #define DUMMY_OPERATIONS_UVZ6V6T7 - +namespace parhip { class dummy_operations { public: - dummy_operations(); - virtual ~dummy_operations(); + dummy_operations(); + virtual ~dummy_operations(); - void run_collective_dummy_operations(); + void run_collective_dummy_operations(); }; - +} #endif /* end of include guard: DUMMY_OPERATIONS_UVZ6V6T7 */ diff --git a/parallel/parallel_src/lib/communication/mpi_tools.cpp b/parallel/parallel_src/lib/communication/mpi_tools.cpp index 8352797e..6bf37092 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.cpp +++ b/parallel/parallel_src/lib/communication/mpi_tools.cpp @@ -11,210 +11,210 @@ #include "io/parallel_vector_io.h" #include "io/parallel_graph_io.h" #include "mpi_tools.h" - +namespace parhip { // currently this method is for debugging purposses only // later on this may be a parallel io routine void mpi_tools::collect_and_write_labels( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G) { - int rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - std::vector< NodeID > labels; - - if( rank == ROOT ) { - labels.resize(G.number_of_global_nodes()); - forall_local_nodes(G, node) { - labels[node] = G.getNodeLabel(node); - } endfor - } else { - //pack the data - forall_local_nodes(G, node) { - labels.push_back(G.getGlobalID(node)); - labels.push_back(G.getNodeLabel(node)); - } endfor - } - - if( rank == ROOT ) { - int counter = 0; - while( counter < size-1) { - // wait for incomming message of an adjacent processor - int flag; MPI_Status st; - MPI_Iprobe(MPI_ANY_SOURCE, rank, communicator, &flag, &st); - - while( flag ) { - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector message; message.resize(message_length); - - MPI_Status rst; - MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, st.MPI_TAG, communicator, &rst); - counter++; - - for( int i = 0; i < message_length-1; i+=2) { - NodeID global_id = message[i]; - NodeID label = message[i+1]; - - labels[global_id] = label; - } - MPI_Iprobe(MPI_ANY_SOURCE, rank, communicator, &flag, &st); - } - } - - } else { - MPI_Request rq; - MPI_Isend( &labels[0], labels.size(), MPI_UNSIGNED_LONG_LONG, ROOT, rank+12*size, communicator, &rq); - } + int rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + + std::vector< NodeID > labels; + + if( rank == ROOT ) { + labels.resize(G.number_of_global_nodes()); + forall_local_nodes(G, node) { + labels[node] = G.getNodeLabel(node); + } endfor +} else { + //pack the data + forall_local_nodes(G, node) { + labels.push_back(G.getGlobalID(node)); + labels.push_back(G.getNodeLabel(node)); + } endfor +} + + if( rank == ROOT ) { + int counter = 0; + while( counter < size-1) { + // wait for incomming message of an adjacent processor + int flag; MPI_Status st; + MPI_Iprobe(MPI_ANY_SOURCE, rank, communicator, &flag, &st); - if( rank == ROOT ) { - std::string clustering_filename("tmpclustering"); - parallel_vector_io pvio; - pvio.writeVectorSequentially(labels, clustering_filename); + while( flag ) { + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector message; message.resize(message_length); + + MPI_Status rst; + MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, st.MPI_TAG, communicator, &rst); + counter++; + + for( int i = 0; i < message_length-1; i+=2) { + NodeID global_id = message[i]; + NodeID label = message[i+1]; + + labels[global_id] = label; } - MPI_Barrier(communicator); + MPI_Iprobe(MPI_ANY_SOURCE, rank, communicator, &flag, &st); + } + } + + } else { + MPI_Request rq; + MPI_Isend( &labels[0], labels.size(), MPI_UNSIGNED_LONG_LONG, ROOT, rank+12*size, communicator, &rq); + } + + if( rank == ROOT ) { + std::string clustering_filename("tmpclustering"); + parallel_vector_io pvio; + pvio.writeVectorSequentially(labels, clustering_filename); + } + MPI_Barrier(communicator); } -void mpi_tools::collect_parallel_graph_to_local_graph( MPI_Comm communicator, PPartitionConfig & config, +void mpi_tools::collect_parallel_graph_to_local_graph( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G, complete_graph_access & Q) { - int rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - std::vector< NodeID > message; - - if( rank == ROOT ) { - Q.start_construction( G.number_of_global_nodes(), G.number_of_global_edges(), - G.number_of_global_nodes(), G.number_of_global_edges(), false); // no update of comm_rounds! - Q.set_range(0, G.number_of_global_nodes()); // this graph should contain all global edges - forall_local_nodes(G, node) { - NodeID cur_node = Q.new_node(); - Q.setNodeWeight(cur_node, G.getNodeWeight(node)); - Q.setSecondPartitionIndex(cur_node, G.getSecondPartitionIndex(node)); - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - EdgeID e_bar = Q.new_edge(cur_node, G.getGlobalID(target)); - Q.setEdgeWeight(e_bar, G.getEdgeWeight(e)); - } endfor - } endfor - } else { - // layout: no local nodes, no local edges, node_1, pidx, weight, degree,its edges: e_1, w_1, ...,node_2, ... - message.push_back(G.number_of_local_nodes()); - forall_local_nodes(G, node) { - //message.push_back(G.getGlobalID(node)); - message.push_back(G.getSecondPartitionIndex(node)); - message.push_back(G.getNodeWeight(node)); - message.push_back(G.getNodeDegree(node)); - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - message.push_back(G.getGlobalID(target)); - message.push_back(G.getEdgeWeight(e)); - } endfor - } endfor - } - - if( rank == ROOT) { - for( int i = 1; i < size; i++) { - int flag; MPI_Status st; - MPI_Iprobe(i, 13*size, communicator, &flag, &st); - - while(!flag) { MPI_Iprobe(i, 13*size, communicator, &flag, &st); } - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector rmessage; rmessage.resize(message_length); - - MPI_Status rst; - MPI_Recv( &rmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, st.MPI_TAG, communicator, &rst); - - NodeID no_nodes = rmessage[0]; - NodeID pos = 1; - for( ULONG node = 0; node < no_nodes; node++) { - NodeID cur_node = Q.new_node(); - Q.setSecondPartitionIndex(cur_node, rmessage[pos++]); - Q.setNodeWeight(cur_node, rmessage[pos++]); - - EdgeID degree = rmessage[pos++]; - for( ULONG e = 0; e < degree; e++) { - EdgeID e_bar = Q.new_edge(cur_node, rmessage[pos++]); - Q.setEdgeWeight(e_bar, rmessage[pos++]); - } - } - - } - } else { - MPI_Request rq; - MPI_Isend( &message[0], message.size(), MPI_UNSIGNED_LONG_LONG, ROOT, 13*size, communicator, &rq); - } + int rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + + std::vector< NodeID > message; + + if( rank == ROOT ) { + Q.start_construction( G.number_of_global_nodes(), G.number_of_global_edges(), + G.number_of_global_nodes(), G.number_of_global_edges(), false); // no update of comm_rounds! + Q.set_range(0, G.number_of_global_nodes()); // this graph should contain all global edges + forall_local_nodes(G, node) { + NodeID cur_node = Q.new_node(); + Q.setNodeWeight(cur_node, G.getNodeWeight(node)); + Q.setSecondPartitionIndex(cur_node, G.getSecondPartitionIndex(node)); + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + EdgeID e_bar = Q.new_edge(cur_node, G.getGlobalID(target)); + Q.setEdgeWeight(e_bar, G.getEdgeWeight(e)); + } endfor +} endfor +} else { + // layout: no local nodes, no local edges, node_1, pidx, weight, degree,its edges: e_1, w_1, ...,node_2, ... + message.push_back(G.number_of_local_nodes()); + forall_local_nodes(G, node) { + //message.push_back(G.getGlobalID(node)); + message.push_back(G.getSecondPartitionIndex(node)); + message.push_back(G.getNodeWeight(node)); + message.push_back(G.getNodeDegree(node)); + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + message.push_back(G.getGlobalID(target)); + message.push_back(G.getEdgeWeight(e)); + } endfor +} endfor +} + + if( rank == ROOT) { + for( int i = 1; i < size; i++) { + int flag; MPI_Status st; + MPI_Iprobe(i, 13*size, communicator, &flag, &st); + + while(!flag) { MPI_Iprobe(i, 13*size, communicator, &flag, &st); } + + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector rmessage; rmessage.resize(message_length); + + MPI_Status rst; + MPI_Recv( &rmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, st.MPI_TAG, communicator, &rst); - if( rank == ROOT ) { - Q.finish_construction(); + NodeID no_nodes = rmessage[0]; + NodeID pos = 1; + for( ULONG node = 0; node < no_nodes; node++) { + NodeID cur_node = Q.new_node(); + Q.setSecondPartitionIndex(cur_node, rmessage[pos++]); + Q.setNodeWeight(cur_node, rmessage[pos++]); + + EdgeID degree = rmessage[pos++]; + for( ULONG e = 0; e < degree; e++) { + EdgeID e_bar = Q.new_edge(cur_node, rmessage[pos++]); + Q.setEdgeWeight(e_bar, rmessage[pos++]); } + } + + } + } else { + MPI_Request rq; + MPI_Isend( &message[0], message.size(), MPI_UNSIGNED_LONG_LONG, ROOT, 13*size, communicator, &rq); + } - MPI_Barrier(communicator); + if( rank == ROOT ) { + Q.finish_construction(); + } + + MPI_Barrier(communicator); } void mpi_tools::distribute_local_graph(MPI_Comm communicator, - PPartitionConfig& config, - complete_graph_access& G) { - int rank; - MPI_Comm_rank(communicator, &rank); - - // first B-Cast number of nodes and number of edges - ULONG number_of_nodes = 0; - ULONG number_of_edges = 0; - - std::vector buffer(2, 0); - if (rank == static_cast(ROOT)) { - buffer[0] = G.number_of_global_nodes(); - buffer[1] = G.number_of_global_edges(); - } - MPI_Bcast(&buffer[0], 2, MPI_INT, ROOT, communicator); - - number_of_nodes = buffer[0]; - number_of_edges = buffer[1]; - - int* xadj; - int* adjncy; - int* vwgt; - int* adjwgt; - - if (rank == static_cast(ROOT)) { - xadj = G.UNSAFE_metis_style_xadj_array(); - adjncy = G.UNSAFE_metis_style_adjncy_array(); - - vwgt = G.UNSAFE_metis_style_vwgt_array(); - adjwgt = G.UNSAFE_metis_style_adjwgt_array(); - } else { - xadj = new int[number_of_nodes + 1]; - adjncy = new int[number_of_edges]; - - vwgt = new int[number_of_nodes]; - adjwgt = new int[number_of_edges]; - } - MPI_Bcast(xadj, number_of_nodes + 1, MPI_INT, ROOT, communicator); - MPI_Bcast(adjncy, number_of_edges, MPI_INT, ROOT, communicator); - MPI_Bcast(vwgt, number_of_nodes, MPI_INT, ROOT, communicator); - MPI_Bcast(adjwgt, number_of_edges, MPI_INT, ROOT, communicator); - - G.build_from_metis_weighted(number_of_nodes, xadj, adjncy, vwgt, adjwgt); - - delete[] xadj; - delete[] adjncy; - delete[] vwgt; - delete[] adjwgt; + PPartitionConfig& config, + complete_graph_access& G) { + int rank; + MPI_Comm_rank(communicator, &rank); + + // first B-Cast number of nodes and number of edges + ULONG number_of_nodes = 0; + ULONG number_of_edges = 0; + + std::vector buffer(2, 0); + if (rank == static_cast(ROOT)) { + buffer[0] = G.number_of_global_nodes(); + buffer[1] = G.number_of_global_edges(); + } + MPI_Bcast(&buffer[0], 2, MPI_INT, ROOT, communicator); + + number_of_nodes = buffer[0]; + number_of_edges = buffer[1]; + + int* xadj; + int* adjncy; + int* vwgt; + int* adjwgt; + + if (rank == static_cast(ROOT)) { + xadj = G.UNSAFE_metis_style_xadj_array(); + adjncy = G.UNSAFE_metis_style_adjncy_array(); + + vwgt = G.UNSAFE_metis_style_vwgt_array(); + adjwgt = G.UNSAFE_metis_style_adjwgt_array(); + } else { + xadj = new int[number_of_nodes + 1]; + adjncy = new int[number_of_edges]; + + vwgt = new int[number_of_nodes]; + adjwgt = new int[number_of_edges]; + } + MPI_Bcast(xadj, number_of_nodes + 1, MPI_INT, ROOT, communicator); + MPI_Bcast(adjncy, number_of_edges, MPI_INT, ROOT, communicator); + MPI_Bcast(vwgt, number_of_nodes, MPI_INT, ROOT, communicator); + MPI_Bcast(adjwgt, number_of_edges, MPI_INT, ROOT, communicator); + + G.build_from_metis_weighted(number_of_nodes, xadj, adjncy, vwgt, adjwgt); + + delete[] xadj; + delete[] adjncy; + delete[] vwgt; + delete[] adjwgt; } auto mpi::exchange_num_messages(std::vector const& num_sent_per_rank, - MPI_Comm communicator) -> std::vector { - PEID size; - MPI_Comm_size(communicator, &size); - // Preparing receive buffers for the lengths - std::vector num_recv_from_rank(size, 0); - MPI_Alltoall(num_sent_per_rank.data(), 1, MPI_INT, num_recv_from_rank.data(), - 1, MPI_INT, communicator); - return num_recv_from_rank; + MPI_Comm communicator) -> std::vector { + PEID size; + MPI_Comm_size(communicator, &size); + // Preparing receive buffers for the lengths + std::vector num_recv_from_rank(size, 0); + MPI_Alltoall(num_sent_per_rank.data(), 1, MPI_INT, num_recv_from_rank.data(), + 1, MPI_INT, communicator); + return num_recv_from_rank; } - +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/communication/mpi_tools.h b/parallel/parallel_src/lib/communication/mpi_tools.h index 1efa5ef7..ab1b37fc 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.h +++ b/parallel/parallel_src/lib/communication/mpi_tools.h @@ -19,9 +19,9 @@ #include "data_structure/parallel_graph_access.h" #include "mpi_types.h" #include "partition_config.h" - +namespace parhip { class mpi_tools { - public: +public: void collect_and_write_labels(MPI_Comm communicator, PPartitionConfig& config, parallel_graph_access& G); @@ -152,7 +152,7 @@ auto all_to_all(Input const& sends, MPI_Comm communicator) throw std::runtime_error("mpi::pack_messages(): send_offsets.size() (" + std::to_string(send_offsets.size()) + ") != mpi size (" + std::to_string(size) + ")"); - } + } // Exchanging message sizes auto const recv_lengths = exchange_num_messages(send_lengths, communicator); @@ -192,5 +192,5 @@ auto all_to_all(Input const& sends, MPI_Comm communicator) {recv_packed_messages, recv_offsets, recv_lengths}); } } // namespace mpi - +} #endif /* end of include guard: MPI_TOOLS_HMESDXF2 */ diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index 8e864654..f9aae782 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -17,7 +17,7 @@ #include #include #include - +namespace parhip { namespace mpi { namespace details { // Define an enum to represent the kind of MPI data @@ -97,10 +97,10 @@ struct mpi_data_kind_trait { // Macro to specialize mpi_data_kind_trait for unique base types #define MPI_BASE_TYPE_KIND(type) \ - template <> \ - struct mpi_data_kind_trait { \ - static constexpr mpi_data_kinds kind = mpi_data_kinds::base; \ - }; +template <> \ +struct mpi_data_kind_trait { \ +static constexpr mpi_data_kinds kind = mpi_data_kinds::base; \ +}; // Specializations for unique base types MPI_BASE_TYPE_KIND(char) @@ -129,12 +129,12 @@ struct mpi_datatype_trait; // Macro to specialize mpi_datatype_trait for unique base types #define MPI_DATATYPE_TRAIT(type, mpi_type_const) \ - template <> \ - struct mpi_datatype_trait { \ - static MPI_Datatype get_mpi_type() { \ - return mpi_type_const; \ - } \ - }; +template <> \ +struct mpi_datatype_trait { \ +static MPI_Datatype get_mpi_type() { \ +return mpi_type_const; \ +} \ +}; // Map unique base types to MPI_Datatypes MPI_DATATYPE_TRAIT(char, MPI_CHAR) @@ -238,7 +238,7 @@ auto get_mpi_datatype() -> MPI_Datatype { static_assert(sizeof(DataType) == 0, "Unsupported data type for MPI communication"); return MPI_DATATYPE_NULL; // This line will never be reached due to - // static_assert + // static_assert } } @@ -307,4 +307,5 @@ struct MyType { friend bool operator!=(const MyType& lhs, const MyType& rhs) { return !(lhs == rhs); } -}; \ No newline at end of file +}; +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/data_structure/balance_management.cpp b/parallel/parallel_src/lib/data_structure/balance_management.cpp index fa67585e..15425d9c 100644 --- a/parallel/parallel_src/lib/data_structure/balance_management.cpp +++ b/parallel/parallel_src/lib/data_structure/balance_management.cpp @@ -7,7 +7,7 @@ #include "balance_management.h" #include "data_structure/parallel_graph_access.h" - +namespace parhip { balance_management::balance_management( parallel_graph_access * G, NodeID total_num_labels ) : m_G ( G ), m_total_num_labels ( total_num_labels ) { @@ -16,5 +16,5 @@ balance_management::balance_management( parallel_graph_access * G, NodeID total_ balance_management::~balance_management() { } - +} diff --git a/parallel/parallel_src/lib/data_structure/balance_management.h b/parallel/parallel_src/lib/data_structure/balance_management.h index 2454e959..f30c32aa 100644 --- a/parallel/parallel_src/lib/data_structure/balance_management.h +++ b/parallel/parallel_src/lib/data_structure/balance_management.h @@ -15,27 +15,27 @@ /* This class gives you the amount of weight that is currently in a block. * The information can be inaccurate but correct after a call of * update_block_sizes_globally. */ - +namespace parhip { class parallel_graph_access; class balance_management { public: - balance_management( parallel_graph_access * G, NodeID total_num_labels); - virtual ~balance_management(); + balance_management( parallel_graph_access * G, NodeID total_num_labels); + virtual ~balance_management(); - virtual NodeWeight getBlockSize( PartitionID block ) = 0; - virtual void setBlockSize( PartitionID block, NodeWeight block_size ) = 0; - virtual void update_non_contained_block_balance( PartitionID from, PartitionID to, NodeWeight node_weight) = 0; + virtual NodeWeight getBlockSize( PartitionID block ) = 0; + virtual void setBlockSize( PartitionID block, NodeWeight block_size ) = 0; + virtual void update_non_contained_block_balance( PartitionID from, PartitionID to, NodeWeight node_weight) = 0; - // init local and total block sizes - virtual void init() = 0; - virtual void update() = 0; + // init local and total block sizes + virtual void init() = 0; + virtual void update() = 0; protected: - balance_management() {}; - parallel_graph_access * m_G; - NodeID m_total_num_labels; + balance_management() {}; + parallel_graph_access * m_G; + NodeID m_total_num_labels; }; - +} #endif /* end of include guard: BALANCE_MANAGEMENT_NJRUTX5K */ diff --git a/parallel/parallel_src/lib/data_structure/balance_management_coarsening.cpp b/parallel/parallel_src/lib/data_structure/balance_management_coarsening.cpp index a74cf877..a0121991 100644 --- a/parallel/parallel_src/lib/data_structure/balance_management_coarsening.cpp +++ b/parallel/parallel_src/lib/data_structure/balance_management_coarsening.cpp @@ -8,35 +8,36 @@ #include #include "balance_management_coarsening.h" #include "data_structure/parallel_graph_access.h" - -balance_management_coarsening::balance_management_coarsening(parallel_graph_access * G, PartitionID total_num_labels) +namespace parhip { +balance_management_coarsening::balance_management_coarsening(parallel_graph_access * G, PartitionID total_num_labels) : balance_management( G, total_num_labels) { - init(); + init(); } balance_management_coarsening::~balance_management_coarsening() { - + } void balance_management_coarsening::init( ) { - forall_local_nodes((*m_G), node) { - PartitionID label = m_G->getNodeLabel(node); - if( m_fuzzy_block_weights.find(label) == m_fuzzy_block_weights.end() ) { - m_fuzzy_block_weights[label] = 0; - } + forall_local_nodes((*m_G), node) { + PartitionID label = m_G->getNodeLabel(node); + if( m_fuzzy_block_weights.find(label) == m_fuzzy_block_weights.end() ) { + m_fuzzy_block_weights[label] = 0; + } - m_fuzzy_block_weights[label] += m_G->getNodeWeight(node); - } endfor + m_fuzzy_block_weights[label] += m_G->getNodeWeight(node); + } endfor - forall_ghost_nodes((*m_G),node) { - PartitionID label = m_G->getNodeLabel(node); - if( m_fuzzy_block_weights.find(label) == m_fuzzy_block_weights.end() ) { - m_fuzzy_block_weights[label] = 0; - } - m_fuzzy_block_weights[label] += m_G->getNodeWeight(node); - } endfor + forall_ghost_nodes((*m_G),node) { + PartitionID label = m_G->getNodeLabel(node); + if( m_fuzzy_block_weights.find(label) == m_fuzzy_block_weights.end() ) { + m_fuzzy_block_weights[label] = 0; + } + m_fuzzy_block_weights[label] += m_G->getNodeWeight(node); + } endfor } void balance_management_coarsening::update( ) { } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/data_structure/balance_management_coarsening.h b/parallel/parallel_src/lib/data_structure/balance_management_coarsening.h index cdc6aae1..56869a54 100644 --- a/parallel/parallel_src/lib/data_structure/balance_management_coarsening.h +++ b/parallel/parallel_src/lib/data_structure/balance_management_coarsening.h @@ -9,7 +9,7 @@ #define BALANCE_MANAGEMENT_COARSENING_TS6EZN5A #include "balance_management.h" - +namespace parhip { class parallel_graph_access; class balance_management_coarsening : public balance_management { @@ -57,5 +57,5 @@ void balance_management_coarsening::update_non_contained_block_balance( Partitio m_fuzzy_block_weights[to] += node_weight; } } - +} #endif /* end of include guard: BALANCE_MANAGEMENT_COARSENING_TS6EZN5A */ diff --git a/parallel/parallel_src/lib/data_structure/balance_management_refinement.cpp b/parallel/parallel_src/lib/data_structure/balance_management_refinement.cpp index 18d3c515..4d264eff 100644 --- a/parallel/parallel_src/lib/data_structure/balance_management_refinement.cpp +++ b/parallel/parallel_src/lib/data_structure/balance_management_refinement.cpp @@ -7,34 +7,35 @@ #include "balance_management_refinement.h" #include "parallel_graph_access.h" - +namespace parhip { balance_management_refinement::balance_management_refinement(parallel_graph_access * G, PartitionID total_num_labels) : balance_management( G, total_num_labels) { - m_total_block_weights.resize( total_num_labels ); - m_local_block_weights.resize( total_num_labels ); + m_total_block_weights.resize( total_num_labels ); + m_local_block_weights.resize( total_num_labels ); + + for( long block = 0; block < (long) total_num_labels; block++) { + m_local_block_weights[block] = 0; + m_total_block_weights[block] = 0; + } - for( long block = 0; block < (long) total_num_labels; block++) { - m_local_block_weights[block] = 0; - m_total_block_weights[block] = 0; - } - - init(); + init(); } balance_management_refinement::~balance_management_refinement() { - + } // init local and total block sizes void balance_management_refinement::init() { - forall_local_nodes((*m_G), node) { - PartitionID label = m_G->getNodeLabel(node); - m_local_block_weights[label] += m_G->getNodeWeight(node); - } endfor - update(); + forall_local_nodes((*m_G), node) { + PartitionID label = m_G->getNodeLabel(node); + m_local_block_weights[label] += m_G->getNodeWeight(node); + } endfor + update(); } void balance_management_refinement::update() { - MPI_Allreduce(&m_local_block_weights[0], &m_total_block_weights[0], - m_total_num_labels, MPI_UNSIGNED_LONG_LONG, MPI_SUM, m_G->getCommunicator()); + MPI_Allreduce(&m_local_block_weights[0], &m_total_block_weights[0], + m_total_num_labels, MPI_UNSIGNED_LONG_LONG, MPI_SUM, m_G->getCommunicator()); } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/data_structure/balance_management_refinement.h b/parallel/parallel_src/lib/data_structure/balance_management_refinement.h index 72fc0428..7cff7098 100644 --- a/parallel/parallel_src/lib/data_structure/balance_management_refinement.h +++ b/parallel/parallel_src/lib/data_structure/balance_management_refinement.h @@ -9,7 +9,7 @@ #define BALANCE_MANAGEMENT_REFINEMENT_ZHYKQBYB #include "balance_management.h" - +namespace parhip { class parallel_graph_access; class balance_management_refinement : public balance_management { @@ -41,5 +41,5 @@ inline NodeWeight balance_management_refinement::getBlockSize( PartitionID block ) { return m_total_block_weights[block]; } - +} #endif /* end of include guard: BALANCE_MANAGEMENT_REFINEMENT_ZHYKQBYB */ diff --git a/parallel/parallel_src/lib/data_structure/hashed_graph.h b/parallel/parallel_src/lib/data_structure/hashed_graph.h index 4ba62df0..047080aa 100644 --- a/parallel/parallel_src/lib/data_structure/hashed_graph.h +++ b/parallel/parallel_src/lib/data_structure/hashed_graph.h @@ -12,7 +12,7 @@ #include "definitions.h" #include "limits.h" - +namespace parhip { struct hashed_edge { NodeID k; NodeID source; @@ -23,7 +23,7 @@ struct hashed_edge { struct compare_hashed_edge { bool operator()(const hashed_edge e_1, const hashed_edge e_2) const { bool eq = (e_1.source == e_2.source && e_1.target == e_2.target); - eq = eq || (e_1.source == e_2.target && e_1.target == e_2.source); + eq = eq || (e_1.source == e_2.target && e_1.target == e_2.source); return eq; } }; @@ -37,15 +37,14 @@ struct data_hashed_edge{ }; struct hash_hashed_edge { - ULONG operator()(const hashed_edge e) const { - if(e.source < e.target) + ULONG operator()(const hashed_edge e) const { + if(e.source < e.target) return e.source*e.k + e.target; - else + else return e.target*e.k + e.source; - } + } }; typedef std::unordered_map hashed_graph; - - +} #endif /* end of include guard: HASHED_GRAPH_DG1JG7O0 */ diff --git a/parallel/parallel_src/lib/data_structure/linear_probing_hashmap.h b/parallel/parallel_src/lib/data_structure/linear_probing_hashmap.h index bd1745f3..d7cd26e2 100644 --- a/parallel/parallel_src/lib/data_structure/linear_probing_hashmap.h +++ b/parallel/parallel_src/lib/data_structure/linear_probing_hashmap.h @@ -9,7 +9,7 @@ #define LINEAR_PROBING_HASHMAP_KQ738TKS #include - +namespace parhip { const NodeID NOT_CONTAINED = std::numeric_limits::max(); struct KeyValuePair { @@ -103,6 +103,6 @@ class linear_probing_hashmap { std::vector< KeyValuePair > m_internal_map; std::stack< NodeID > m_contained_key_positions; }; - +} #endif /* end of include guard: LINEAR_PROBING_HASHMAP_KQ738TKS */ diff --git a/parallel/parallel_src/lib/data_structure/linear_probing_hashmap_ll.h b/parallel/parallel_src/lib/data_structure/linear_probing_hashmap_ll.h index a0342626..f81b96ff 100644 --- a/parallel/parallel_src/lib/data_structure/linear_probing_hashmap_ll.h +++ b/parallel/parallel_src/lib/data_structure/linear_probing_hashmap_ll.h @@ -9,6 +9,7 @@ #define LINEAR_PROBING_HASHMAP_LL_KQ738TKS #include +namespace parhip { const ULONG NOT_CONTAINED_LL = std::numeric_limits::max(); @@ -103,6 +104,6 @@ class linear_probing_hashmap_ll { std::vector< KeyValuePair > m_internal_map; std::stack< ULONG > m_contained_key_positions; }; - +} #endif /* end of include guard: LINEAR_PROBING_HASHMAP_KQ738TKS */ diff --git a/parallel/parallel_src/lib/data_structure/next_prime.h b/parallel/parallel_src/lib/data_structure/next_prime.h index 10131fce..44e81baa 100644 --- a/parallel/parallel_src/lib/data_structure/next_prime.h +++ b/parallel/parallel_src/lib/data_structure/next_prime.h @@ -7,6 +7,7 @@ #ifndef NEXT_PRIME #define NEXT_PRIME +namespace parhip { static const std::size_t small_primes[] = { @@ -135,6 +136,6 @@ next_prime(std::size_t n) } return n; } - +} #endif /* end of include guard: */ diff --git a/parallel/parallel_src/lib/data_structure/parallel_graph_access.cpp b/parallel/parallel_src/lib/data_structure/parallel_graph_access.cpp index ef13e121..b96b7e55 100644 --- a/parallel/parallel_src/lib/data_structure/parallel_graph_access.cpp +++ b/parallel/parallel_src/lib/data_structure/parallel_graph_access.cpp @@ -8,9 +8,8 @@ #include "balance_management_coarsening.h" #include "balance_management_refinement.h" #include "parallel_graph_access.h" - - -ULONG parallel_graph_access::m_comm_rounds = 128; +namespace parhip { +ULONG parallel_graph_access::m_comm_rounds = 128; ULONG parallel_graph_access::m_comm_rounds_up = 128; parallel_graph_access::parallel_graph_access( MPI_Comm communicator ) : m_num_local_nodes(0), @@ -19,56 +18,56 @@ parallel_graph_access::parallel_graph_access( MPI_Comm communicator ) : m_num_lo m_num_ghost_nodes(0), m_max_node_degree(0), m_bm(NULL) { - m_communicator = communicator; - MPI_Comm_rank( m_communicator, &rank); - MPI_Comm_size( m_communicator, &size); - - m_gnc = new ghost_node_communication(m_communicator); - m_gnc->setGraphReference(this); + m_communicator = communicator; + MPI_Comm_rank( m_communicator, &rank); + MPI_Comm_size( m_communicator, &size); + + m_gnc = new ghost_node_communication(m_communicator); + m_gnc->setGraphReference(this); } parallel_graph_access::~parallel_graph_access() { - m_comm_rounds = std::min(m_comm_rounds, m_comm_rounds_up); - delete m_gnc; - if ( m_bm ) delete m_bm; + m_comm_rounds = std::min(m_comm_rounds, m_comm_rounds_up); + delete m_gnc; + if ( m_bm ) delete m_bm; } void parallel_graph_access::init_balance_management( PPartitionConfig & config ) { - if( m_bm != NULL ) { - delete m_bm; - } + if( m_bm != NULL ) { + delete m_bm; + } - if( config.total_num_labels != config.k ) { - m_bm = new balance_management_coarsening( this, config.total_num_labels ); - } else { - m_bm = new balance_management_refinement( this, config.total_num_labels ); - } + if( config.total_num_labels != config.k ) { + m_bm = new balance_management_coarsening( this, config.total_num_labels ); + } else { + m_bm = new balance_management_refinement( this, config.total_num_labels ); + } } void parallel_graph_access::update_non_contained_block_balance( PartitionID from, PartitionID to, NodeWeight node_weight) { - m_bm->update_non_contained_block_balance( from, to, node_weight); + m_bm->update_non_contained_block_balance( from, to, node_weight); } void parallel_graph_access::update_block_weights() { - m_bm->update(); + m_bm->update(); } void parallel_graph_access::update_ghost_node_data( bool check_iteration_counter ) { - m_gnc->update_ghost_node_data( check_iteration_counter ); + m_gnc->update_ghost_node_data( check_iteration_counter ); } void parallel_graph_access::update_ghost_node_data_global() { - m_gnc->update_ghost_node_data_global(); + m_gnc->update_ghost_node_data_global(); } void parallel_graph_access::update_ghost_node_data_finish() { - m_gnc->update_ghost_node_data_finish(); + m_gnc->update_ghost_node_data_finish(); } void parallel_graph_access::set_comm_rounds(ULONG comm_rounds) { - m_comm_rounds = comm_rounds; - set_comm_rounds_up(comm_rounds); + m_comm_rounds = comm_rounds; + set_comm_rounds_up(comm_rounds); } void parallel_graph_access::set_comm_rounds_up(ULONG comm_rounds) { - m_comm_rounds_up = comm_rounds; + m_comm_rounds_up = comm_rounds; } - +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/data_structure/parallel_graph_access.h b/parallel/parallel_src/lib/data_structure/parallel_graph_access.h index 35ca8de8..d52d1fe4 100644 --- a/parallel/parallel_src/lib/data_structure/parallel_graph_access.h +++ b/parallel/parallel_src/lib/data_structure/parallel_graph_access.h @@ -20,38 +20,38 @@ #include "definitions.h" #include "partition_config.h" #include "tools/timer.h" - +namespace parhip { struct Node { - EdgeID firstEdge; + EdgeID firstEdge; }; struct NodeData { - NodeID label; - PartitionID block; // a given partition of the graph (for v-cycles) - NodeWeight weight; // save a little bit of memory - bool is_interface_node; // save a little bit of memory + NodeID label; + PartitionID block; // a given partition of the graph (for v-cycles) + NodeWeight weight; // save a little bit of memory + bool is_interface_node; // save a little bit of memory }; //struct NodeData { - //NodeID label; - //PartitionID block:15; // a given partition of the graph (for v-cycles) - //NodeWeight weight:47; // save a little bit of memory - //bool is_interface_node:1; // save a little bit of memory +//NodeID label; +//PartitionID block:15; // a given partition of the graph (for v-cycles) +//NodeWeight weight:47; // save a little bit of memory +//bool is_interface_node:1; // save a little bit of memory //}; //struct AdditionalNonLocalNodeData { - //PEID peID:15; // save a little bit of memory - //NodeID globalID:48; +//PEID peID:15; // save a little bit of memory +//NodeID globalID:48; //}; struct AdditionalNonLocalNodeData { - PEID peID; // save a little bit of memory - NodeID globalID; + PEID peID; // save a little bit of memory + NodeID globalID; }; struct Edge { - NodeID local_target; - EdgeWeight weight; + NodeID local_target; + EdgeWeight weight; }; //makros - graph access @@ -71,9 +71,9 @@ class ghost_node_communication { MPI_Comm_rank( m_communicator, &m_rank); MPI_Comm_size( m_communicator, &m_size); - - m_PE_packed.resize(m_size); - m_adjacent_processors.resize(m_size); + + m_PE_packed.resize(m_size); + m_adjacent_processors.resize(m_size); for( PEID peID = 0; peID < (PEID) m_PE_packed.size(); peID++) { m_PE_packed[ peID ] = false; m_adjacent_processors[ peID ] = false; @@ -95,12 +95,12 @@ class ghost_node_communication { void setGraphReference( parallel_graph_access * G ) { m_G = G; - }; + }; void init( ) { m_num_adjacent = getNumberOfAdjacentPEs(); - }; + }; @@ -116,13 +116,13 @@ class ghost_node_communication { m_desired_rounds = desired_rounds; } - inline + inline void update_ghost_node_data( bool check_iteration_counter ); - inline + inline void update_ghost_node_data_finish(); - inline + inline void update_ghost_node_data_global(); inline @@ -138,14 +138,14 @@ class ghost_node_communication { private: - inline + inline void receive_messages_of_neighbors(); parallel_graph_access * m_G; PEID m_size; PEID m_rank; NodeID m_iteration_counter; // this counter is used to manage the communication rounds - ULONG m_skip_limit; + ULONG m_skip_limit; bool m_first_send; ULONG m_send_iteration; @@ -157,7 +157,7 @@ class ghost_node_communication { ULONG m_desired_rounds; // store the number of adjacent processors ( a block is a neighbor iff there is an edge between the subgraphs ) - PEID m_num_adjacent; + PEID m_num_adjacent; std::vector< bool > m_PE_packed; std::vector< std::vector< NodeID > > m_send_buffers_A; // buffers to send messages @@ -175,17 +175,17 @@ class parallel_graph_access { friend class ghost_node_communication; - parallel_graph_access( ) : m_num_local_nodes(0), - from(0), + parallel_graph_access( ) : m_num_local_nodes(0), + from(0), to(0), - m_num_ghost_nodes(0), m_max_node_degree(0), m_bm(NULL) { - m_communicator = MPI_COMM_WORLD; - MPI_Comm_rank( m_communicator, &rank); - MPI_Comm_size( m_communicator, &size); + m_num_ghost_nodes(0), m_max_node_degree(0), m_bm(NULL) { + m_communicator = MPI_COMM_WORLD; + MPI_Comm_rank( m_communicator, &rank); + MPI_Comm_size( m_communicator, &size); - m_gnc = new ghost_node_communication(m_communicator); - m_gnc->setGraphReference(this); - }; + m_gnc = new ghost_node_communication(m_communicator); + m_gnc->setGraphReference(this); + }; parallel_graph_access( MPI_Comm communicator ); @@ -205,7 +205,7 @@ class parallel_graph_access { m_global_m = global_m; m_ghost_adddata_array_offset = n+1; m_bm = NULL; - m_cur_degree = 0; + m_cur_degree = 0; //resizes property arrays m_nodes.resize(n+1); @@ -214,13 +214,13 @@ class parallel_graph_access { m_nodes[node].firstEdge = e; m_divisor = ceil(global_n / (double)size); - // every PE has to make same amount communication iterations - // we use ceil an check afterwards wether everyone has done the right - // amount of communication rounds + // every PE has to make same amount communication iterations + // we use ceil an check afterwards wether everyone has done the right + // amount of communication rounds if( update_comm_rounds ) { m_comm_rounds = std::max(m_comm_rounds, 8ULL); - m_gnc->set_desired_rounds(m_comm_rounds); - m_gnc->set_skip_limit(ceil(n/(double)m_comm_rounds)); + m_gnc->set_desired_rounds(m_comm_rounds); + m_gnc->set_skip_limit(ceil(n/(double)m_comm_rounds)); } }; @@ -264,7 +264,7 @@ class parallel_graph_access { }; NodeID new_node() { - m_cur_degree = 0; + m_cur_degree = 0; ASSERT_TRUE(m_building_graph); return node++; }; @@ -275,25 +275,25 @@ class parallel_graph_access { // build ghost nodes on the fly if( from <= target && target <= to) { - m_edges[e].local_target = target - from; + m_edges[e].local_target = target - from; } else { m_nodes_data[source].is_interface_node = true; // check wether this is already a ghost node if(m_global_to_local_id.find(target) != m_global_to_local_id.end()) { // this node is already a ghost node - m_edges[e].local_target = m_global_to_local_id[target]; + m_edges[e].local_target = m_global_to_local_id[target]; } else { // we need to create a new ghost node m_global_to_local_id[target] = m_num_nodes++; - m_edges[e].local_target = m_global_to_local_id[target]; + m_edges[e].local_target = m_global_to_local_id[target]; //create the ghost node in the array Node dummy; dummy.firstEdge = 0; m_nodes.push_back(dummy); - NodeData dummy_data; + NodeData dummy_data; dummy_data.label = target; dummy_data.block = 0; dummy_data.is_interface_node = false; @@ -302,8 +302,8 @@ class parallel_graph_access { // add addtional data AdditionalNonLocalNodeData add_data; - //has to be changed once we implement better load balancing - //add_data.peID = target / m_divisor; + //has to be changed once we implement better load balancing + //add_data.peID = target / m_divisor; add_data.peID = get_PEID_from_range_array(target); add_data.globalID = target; @@ -325,11 +325,11 @@ class parallel_graph_access { } } m_last_source = source; - m_cur_degree++; + m_cur_degree++; - if( m_cur_degree > m_max_node_degree ) { - m_max_node_degree = m_cur_degree; - } + if( m_cur_degree > m_max_node_degree ) { + m_max_node_degree = m_cur_degree; + } return e_bar; }; @@ -349,9 +349,9 @@ class parallel_graph_access { m_gnc->init(); }; - NodeID get_max_degree() { - return m_max_node_degree; - } + NodeID get_max_degree() { + return m_max_node_degree; + } /* ============================================================= */ /* methods handeling balance */ /* ============================================================= */ @@ -389,15 +389,15 @@ class parallel_graph_access { EdgeID get_first_edge(NodeID node); EdgeID get_first_invalid_edge(NodeID node); - NodeID getNodeLabel(NodeID node); - void setNodeLabel(NodeID node, NodeID label); + NodeID getNodeLabel(NodeID node); + void setNodeLabel(NodeID node, NodeID label); - NodeID getSecondPartitionIndex(NodeID node); - void setSecondPartitionIndex(NodeID node, NodeID label); + NodeID getSecondPartitionIndex(NodeID node); + void setSecondPartitionIndex(NodeID node, NodeID label); - NodeWeight getNodeWeight(NodeID node); - void setNodeWeight(NodeID node, NodeWeight weight); + NodeWeight getNodeWeight(NodeID node); + void setNodeWeight(NodeID node, NodeWeight weight); EdgeID getNodeDegree(NodeID node); EdgeID getNodeNumGhostNodes(NodeID node); @@ -418,8 +418,8 @@ class parallel_graph_access { return m_communicator; } - EdgeWeight getEdgeWeight(EdgeID e); - void setEdgeWeight(EdgeID e, EdgeWeight weight); + EdgeWeight getEdgeWeight(EdgeID e); + void setEdgeWeight(EdgeID e, EdgeWeight weight); NodeID getEdgeTarget(EdgeID e); @@ -427,7 +427,7 @@ class parallel_graph_access { //these methods are usally called to communicate data PEID getTargetPE(NodeID node); - //input is a global id + //input is a global id //output is the local id NodeID getLocalID(NodeID node) { if( from <= node && node <= to ) { @@ -458,8 +458,8 @@ class parallel_graph_access { void update_ghost_node_data_finish(); void update_ghost_node_data_global(); - static void set_comm_rounds(ULONG comm_rounds); - static void set_comm_rounds_up(ULONG comm_rounds); + static void set_comm_rounds(ULONG comm_rounds); + static void set_comm_rounds_up(ULONG comm_rounds); /* ============================================================= */ /* info */ @@ -493,10 +493,10 @@ class parallel_graph_access { /* ============================================================= */ private: // the graph representation itself - // local and ghost nodes in one array, + // local and ghost nodes in one array, // local nodes are stored in the beginning // ghost nodes in the end of the array - std::vector m_nodes; + std::vector m_nodes; std::vector m_nodes_data; std::vector m_edges; @@ -504,7 +504,7 @@ class parallel_graph_access { std::vector m_add_non_local_node_data; // NodeID to CNode for ghost nodes and local nodes - std::vector m_nodes_to_cnode; + std::vector m_nodes_to_cnode; // stores the ranges for which a processor is responsible for // m_range_array[i]= starting position of PE i @@ -513,27 +513,27 @@ class parallel_graph_access { std::unordered_map m_global_to_local_id; - NodeID m_ghost_adddata_array_offset; // node id of ghost node - offset to get the position in add data + NodeID m_ghost_adddata_array_offset; // node id of ghost node - offset to get the position in add data NodeID m_divisor; // needed to compute the target id of a ghost node NodeID m_num_local_nodes; // store the number of local / non-ghost nodes NodeID from; // each process stores nodes [from. to] - NodeID to; - + NodeID to; + // construction properties bool m_building_graph; NodeID m_last_source; NodeID m_num_ghost_nodes; NodeID node; //current node that is constructed EdgeID e; //current edge that is constructed - NodeID m_num_nodes; + NodeID m_num_nodes; NodeID m_global_n; // global number of nodes NodeID m_global_m; // global number of edges static ULONG m_comm_rounds; // global number of edges static ULONG m_comm_rounds_up; // global number of edges - NodeID m_max_node_degree; - NodeID m_cur_degree; + NodeID m_max_node_degree; + NodeID m_cur_degree; PEID size; PEID rank; @@ -1026,5 +1026,5 @@ inline void ghost_node_communication::update_ghost_node_data_global() { MPI_Barrier(m_communicator); } - +} #endif /* end of include guard: PARALLEL_GRAPH_ACCESS_X6O9MRS8 */ diff --git a/parallel/parallel_src/lib/definitions.h b/parallel/parallel_src/lib/definitions.h index 98c1d716..22655351 100644 --- a/parallel/parallel_src/lib/definitions.h +++ b/parallel/parallel_src/lib/definitions.h @@ -23,6 +23,7 @@ #define PRINT(x) do {} while (false); #endif +namespace parhip { /********************************************** * Constants * ********************************************/ @@ -41,8 +42,8 @@ constexpr PEID ROOT = 0; typedef enum { PERMUTATION_QUALITY_NONE, - PERMUTATION_QUALITY_FAST, - PERMUTATION_QUALITY_GOOD + PERMUTATION_QUALITY_FAST, + PERMUTATION_QUALITY_GOOD } PermutationQuality; typedef enum { @@ -62,13 +63,12 @@ struct source_target_pair { }; typedef enum { - RANDOM_NODEORDERING, + RANDOM_NODEORDERING, DEGREE_NODEORDERING, - LEASTGHOSTNODESFIRST_DEGREE_NODEODERING, - DEGREE_LEASTGHOSTNODESFIRST_NODEODERING + LEASTGHOSTNODESFIRST_DEGREE_NODEODERING, + DEGREE_LEASTGHOSTNODESFIRST_NODEODERING } NodeOrderingType; - - +} #endif //Tag Listing of Isend Operations(they should be unique per level) diff --git a/parallel/parallel_src/lib/distributed_partitioning/distributed_partitioner.cpp b/parallel/parallel_src/lib/distributed_partitioning/distributed_partitioner.cpp index d487f734..a886dfd2 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/distributed_partitioner.cpp +++ b/parallel/parallel_src/lib/distributed_partitioning/distributed_partitioner.cpp @@ -18,33 +18,33 @@ #include "tools/distributed_quality_metrics.h" #include "tools/random_functions.h" #include "data_structure/linear_probing_hashmap.h" - +namespace parhip { std::vector< NodeID > distributed_partitioner::m_cf = std::vector< NodeID >(); std::vector< NodeID > distributed_partitioner::m_sf = std::vector< NodeID >(); std::vector< NodeID > distributed_partitioner::m_lic = std::vector< NodeID >(); distributed_partitioner::distributed_partitioner() { - m_total_graph_weight = std::numeric_limits< NodeWeight >::max(); - m_cur_rnd_choice = 0; - m_level = -1; - m_cycle = 0; + m_total_graph_weight = std::numeric_limits< NodeWeight >::max(); + m_cur_rnd_choice = 0; + m_level = -1; + m_cycle = 0; } distributed_partitioner::~distributed_partitioner() { } void distributed_partitioner::generate_random_choices( PPartitionConfig & config ){ - for( int i = 0; i < config.num_tries; i++) { - for( int j = 0; j < config.num_vcycles; j++) { - m_cf.push_back(random_functions::nextDouble( 10, 25 )); - m_sf.push_back(random_functions::nextInt( 20, 500 )); - m_lic.push_back(random_functions::nextInt( 2, 15 )); - } - } + for( int i = 0; i < config.num_tries; i++) { + for( int j = 0; j < config.num_vcycles; j++) { + m_cf.push_back(random_functions::nextDouble( 10, 25 )); + m_sf.push_back(random_functions::nextInt( 20, 500 )); + m_lic.push_back(random_functions::nextInt( 2, 15 )); + } + } } void distributed_partitioner::perform_recursive_partitioning( PPartitionConfig & partition_config, parallel_graph_access & G) { - perform_partitioning( MPI_COMM_WORLD, partition_config, G); + perform_partitioning( MPI_COMM_WORLD, partition_config, G); } void distributed_partitioner::perform_recursive_partitioning( MPI_Comm communicator, PPartitionConfig & partition_config, parallel_graph_access & G) { @@ -52,383 +52,383 @@ void distributed_partitioner::perform_recursive_partitioning( MPI_Comm communica void distributed_partitioner::perform_partitioning( PPartitionConfig & partition_config, parallel_graph_access & G) { - perform_partitioning( MPI_COMM_WORLD, partition_config, G); + perform_partitioning( MPI_COMM_WORLD, partition_config, G); } void distributed_partitioner::perform_partitioning( MPI_Comm communicator, PPartitionConfig & partition_config, parallel_graph_access & G) { - timer t; - double elapsed = 0; - m_cur_rnd_choice = 0; - PPartitionConfig config = partition_config; - config.vcycle = false; - - PEID rank; - MPI_Comm_rank( communicator, &rank); - - for( int cycle = 0; cycle < partition_config.num_vcycles; cycle++) { - t.restart(); - m_cycle = cycle; - - if(cycle+1 == partition_config.num_vcycles && partition_config.no_refinement_in_last_iteration) { - config.label_iterations_refinement = 0; - } - - vcycle( communicator, config, G ); - - if( rank == ROOT ) { - PRINT(std::cout << "log>cycle: " << m_cycle << " uncoarsening took " << m_t.elapsed() << std::endl;) - } + timer t; + double elapsed = 0; + m_cur_rnd_choice = 0; + PPartitionConfig config = partition_config; + config.vcycle = false; + + PEID rank; + MPI_Comm_rank( communicator, &rank); + + for( int cycle = 0; cycle < partition_config.num_vcycles; cycle++) { + t.restart(); + m_cycle = cycle; + + if(cycle+1 == partition_config.num_vcycles && partition_config.no_refinement_in_last_iteration) { + config.label_iterations_refinement = 0; + } + + vcycle( communicator, config, G ); + + if( rank == ROOT ) { + PRINT(std::cout << "log>cycle: " << m_cycle << " uncoarsening took " << m_t.elapsed() << std::endl;) +} #ifndef NDEBUG - check_labels(communicator, config, G); + check_labels(communicator, config, G); #endif - elapsed += t.elapsed(); + elapsed += t.elapsed(); #ifndef NOOUTPUT - distributed_quality_metrics qm; - EdgeWeight edge_cut = qm.edge_cut( G, communicator ); - double balance = qm.balance( config, G, communicator ); - - if( rank == ROOT ) { - std::cout << "log>cycle: " << cycle << " k " << config.k << " cut " << edge_cut << " balance " << balance << " time " << elapsed << std::endl; - } -#endif - t.restart(); - m_t.restart(); - if( cycle+1 < config.num_vcycles ) { - forall_local_nodes(G, node) { - G.setSecondPartitionIndex(node, G.getNodeLabel(node)); - G.setNodeLabel(node, G.getGlobalID(node)); - } endfor - - forall_ghost_nodes(G, node) { - G.setSecondPartitionIndex(node, G.getNodeLabel(node)); - G.setNodeLabel(node, G.getGlobalID(node)); - } endfor - } - - config.vcycle = true; - - if( rank == ROOT && config.eco ) { - config.cluster_coarsening_factor = m_cf[m_cur_rnd_choice++]; - } - - if(config.eco) { - MPI_Bcast(&(config.cluster_coarsening_factor), 1, MPI_DOUBLE, ROOT, communicator); - - //std::cout << "cf " << config.cluster_coarsening_factor << std::endl; - } - config.evolutionary_time_limit = 0; - elapsed += t.elapsed(); - MPI_Barrier(communicator); - - } + distributed_quality_metrics qm; + EdgeWeight edge_cut = qm.edge_cut( G, communicator ); + double balance = qm.balance( config, G, communicator ); + + if( rank == ROOT ) { + std::cout << "log>cycle: " << cycle << " k " << config.k << " cut " << edge_cut << " balance " << balance << " time " << elapsed << std::endl; + } +#endif + t.restart(); + m_t.restart(); + if( cycle+1 < config.num_vcycles ) { + forall_local_nodes(G, node) { + G.setSecondPartitionIndex(node, G.getNodeLabel(node)); + G.setNodeLabel(node, G.getGlobalID(node)); + } endfor + + forall_ghost_nodes(G, node) { + G.setSecondPartitionIndex(node, G.getNodeLabel(node)); + G.setNodeLabel(node, G.getGlobalID(node)); + } endfor +} + + config.vcycle = true; + + if( rank == ROOT && config.eco ) { + config.cluster_coarsening_factor = m_cf[m_cur_rnd_choice++]; + } + + if(config.eco) { + MPI_Bcast(&(config.cluster_coarsening_factor), 1, MPI_DOUBLE, ROOT, communicator); + + //std::cout << "cf " << config.cluster_coarsening_factor << std::endl; + } + config.evolutionary_time_limit = 0; + elapsed += t.elapsed(); + MPI_Barrier(communicator); + + } } void distributed_partitioner::vcycle( MPI_Comm communicator, PPartitionConfig & partition_config, parallel_graph_access & G) { - PPartitionConfig config = partition_config; + PPartitionConfig config = partition_config; - mpi_tools mpitools; - timer t; + mpi_tools mpitools; + timer t; - if( m_total_graph_weight == std::numeric_limits< NodeWeight >::max() ) { - m_total_graph_weight = G.number_of_global_nodes(); - } + if( m_total_graph_weight == std::numeric_limits< NodeWeight >::max() ) { + m_total_graph_weight = G.number_of_global_nodes(); + } + + PEID rank; + MPI_Comm_rank( communicator, &rank); - PEID rank; - MPI_Comm_rank( communicator, &rank); - #ifndef NOOUTPUT - if( rank == ROOT ) { - std::cout << "log>" << "=====================================" << std::endl; - std::cout << "log>" << "=============NEXT LEVEL==============" << std::endl; - std::cout << "log>" << "=====================================" << std::endl; - } + if( rank == ROOT ) { + std::cout << "log>" << "=====================================" << std::endl; + std::cout << "log>" << "=============NEXT LEVEL==============" << std::endl; + std::cout << "log>" << "=====================================" << std::endl; + } #endif - t.restart(); - - - m_level++; - config.label_iterations = config.label_iterations_coarsening; - config.total_num_labels = G.number_of_global_nodes(); - // - if( config.cluster_coarsening_factor > 100 ) { - config.upper_bound_cluster = std::max(100, (int)(config.upper_bound_partition/(1.0*config.cluster_coarsening_factor))); - } else { - config.upper_bound_cluster = (int)(config.upper_bound_partition/(1.0*config.cluster_coarsening_factor)); - } - G.init_balance_management( config ); + t.restart(); - //parallel_label_compress< std::unordered_map< NodeID, NodeWeight> > plc; - parallel_label_compress< linear_probing_hashmap > plc; - plc.perform_parallel_label_compression ( config, G, true); + + m_level++; + config.label_iterations = config.label_iterations_coarsening; + config.total_num_labels = G.number_of_global_nodes(); + // + if( config.cluster_coarsening_factor > 100 ) { + config.upper_bound_cluster = std::max(100, (int)(config.upper_bound_partition/(1.0*config.cluster_coarsening_factor))); + } else { + config.upper_bound_cluster = (int)(config.upper_bound_partition/(1.0*config.cluster_coarsening_factor)); + } + G.init_balance_management( config ); + + //parallel_label_compress< std::unordered_map< NodeID, NodeWeight> > plc; + parallel_label_compress< linear_probing_hashmap > plc; + plc.perform_parallel_label_compression ( config, G, true); #ifndef NOOUTPUT - if( rank == ROOT ) { - std::cout << "log>cycle: " << m_cycle << " level: " << m_level << " parallel label compression took " << t.elapsed() << std::endl; - } + if( rank == ROOT ) { + std::cout << "log>cycle: " << m_cycle << " level: " << m_level << " parallel label compression took " << t.elapsed() << std::endl; + } #endif - parallel_graph_access Q(communicator); - t.restart(); + parallel_graph_access Q(communicator); + t.restart(); - { - parallel_contraction parallel_contract; - parallel_contract.contract_to_distributed_quotient( communicator, config, G, Q); // contains one Barrier + { + parallel_contraction parallel_contract; + parallel_contract.contract_to_distributed_quotient( communicator, config, G, Q); // contains one Barrier + + parallel_block_down_propagation pbdp; + if( config.vcycle ) { + // in this case we have to propagate the partitionindex down + pbdp.propagate_block_down( communicator, config, G, Q); + } + + MPI_Barrier(communicator); + } - parallel_block_down_propagation pbdp; - if( config.vcycle ) { - // in this case we have to propagate the partitionindex down - pbdp.propagate_block_down( communicator, config, G, Q); - } - - MPI_Barrier(communicator); - } - #ifndef NOOUTPUT - if( rank == ROOT ) { - std::cout << "log>cycle: " << m_cycle << " level: " << m_level << " contraction took " << t.elapsed() << std::endl; - std::cout << "log>cycle: " << m_cycle << " level: " << m_level << " coarse nodes n=" << Q.number_of_global_nodes() << ", coarse edges m=" << Q.number_of_global_edges() << std::endl; - } + if( rank == ROOT ) { + std::cout << "log>cycle: " << m_cycle << " level: " << m_level << " contraction took " << t.elapsed() << std::endl; + std::cout << "log>cycle: " << m_cycle << " level: " << m_level << " coarse nodes n=" << Q.number_of_global_nodes() << ", coarse edges m=" << Q.number_of_global_edges() << std::endl; + } #endif - if( !contraction_stop_decision.contraction_stop(config, G, Q)) { - vcycle( communicator, config, Q); - } else { + if( !contraction_stop_decision.contraction_stop(config, G, Q)) { + vcycle( communicator, config, Q); + } else { #ifndef NOOUTPUT - if( rank == ROOT ) { - std::cout << "log>" << "=====================================" << std::endl; - std::cout << "log>" << "================ IP =================" << std::endl; - std::cout << "log>" << "=====================================" << std::endl; - std::cout << "log>cycle: " << m_cycle << " total number of levels " << (m_level+1) << std::endl; - std::cout << "log>cycle: " << m_cycle << " number of coarsest nodes " << Q.number_of_global_nodes() << std::endl; - std::cout << "log>cycle: " << m_cycle << " number of coarsest edges " << Q.number_of_global_edges() << std::endl; - std::cout << "log>cycle: " << m_cycle << " coarsening took " << m_t.elapsed() << std::endl; - } + if( rank == ROOT ) { + std::cout << "log>" << "=====================================" << std::endl; + std::cout << "log>" << "================ IP =================" << std::endl; + std::cout << "log>" << "=====================================" << std::endl; + std::cout << "log>cycle: " << m_cycle << " total number of levels " << (m_level+1) << std::endl; + std::cout << "log>cycle: " << m_cycle << " number of coarsest nodes " << Q.number_of_global_nodes() << std::endl; + std::cout << "log>cycle: " << m_cycle << " number of coarsest edges " << Q.number_of_global_edges() << std::endl; + std::cout << "log>cycle: " << m_cycle << " coarsening took " << m_t.elapsed() << std::endl; + } #endif - t.restart(); + t.restart(); - initial_partitioning_algorithm ip; - ip.perform_partitioning( communicator, config, Q ); + initial_partitioning_algorithm ip; + ip.perform_partitioning( communicator, config, Q ); #ifndef NOOUTPUT - if( rank == ROOT ) { - std::cout << "log>cycle: " << m_cycle << " initial partitioning took " << t.elapsed() << std::endl; - } - m_t.restart(); + if( rank == ROOT ) { + std::cout << "log>cycle: " << m_cycle << " initial partitioning took " << t.elapsed() << std::endl; + } + m_t.restart(); #endif - } + } #ifndef NOOUTPUT - if( rank == ROOT ) { - std::cout << "log>" << "=====================================" << std::endl; - std::cout << "log>" << "============PREV LEVEL ==============" << std::endl; - std::cout << "log>" << "=====================================" << std::endl; - } + if( rank == ROOT ) { + std::cout << "log>" << "=====================================" << std::endl; + std::cout << "log>" << "============PREV LEVEL ==============" << std::endl; + std::cout << "log>" << "=====================================" << std::endl; + } #endif - t.restart(); - parallel_projection parallel_project; - parallel_project.parallel_project( communicator, G, Q ); // contains a Barrier + t.restart(); + parallel_projection parallel_project; + parallel_project.parallel_project( communicator, G, Q ); // contains a Barrier #ifndef NOOUTPUT - if( rank == ROOT ) { - std::cout << "log>cycle: " << m_cycle << " level: " << m_level << " projection took " << t.elapsed() << std::endl; - } + if( rank == ROOT ) { + std::cout << "log>cycle: " << m_cycle << " level: " << m_level << " projection took " << t.elapsed() << std::endl; + } #endif - t.restart(); - config.label_iterations = config.label_iterations_refinement; + t.restart(); + config.label_iterations = config.label_iterations_refinement; - if( config.label_iterations != 0 ) { - config.total_num_labels = config.k; - config.upper_bound_cluster = config.upper_bound_partition; + if( config.label_iterations != 0 ) { + config.total_num_labels = config.k; + config.upper_bound_cluster = config.upper_bound_partition; - G.init_balance_management( config ); - PPartitionConfig working_config = config; - working_config.vcycle = false; // assure that we actually can improve the cut + G.init_balance_management( config ); + PPartitionConfig working_config = config; + working_config.vcycle = false; // assure that we actually can improve the cut - parallel_label_compress< std::vector< NodeWeight> > plc_refinement; - plc_refinement.perform_parallel_label_compression( working_config, G, false, false); - } + parallel_label_compress< std::vector< NodeWeight> > plc_refinement; + plc_refinement.perform_parallel_label_compression( working_config, G, false, false); + } #ifndef NOOUTPUT - if( rank == ROOT ) { - std::cout << "log>cycle: " << m_cycle <<" level: " << m_level << " label compression refinement took " << t.elapsed() << std::endl; - } + if( rank == ROOT ) { + std::cout << "log>cycle: " << m_cycle <<" level: " << m_level << " label compression refinement took " << t.elapsed() << std::endl; + } #endif - m_level--; + m_level--; } void distributed_partitioner::check_labels( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G) { - PEID m_rank, m_size; - MPI_Comm_rank( communicator, &m_rank); - MPI_Comm_size( communicator, &m_size); - - std::vector< std::vector< NodeID > > send_buffers; // buffers to send messages - send_buffers.resize(m_size); - std::vector m_PE_packed; - m_PE_packed.resize(m_size); - for( unsigned peID = 0; peID < m_PE_packed.size(); peID++) { - m_PE_packed[ peID ] = false; + PEID m_rank, m_size; + MPI_Comm_rank( communicator, &m_rank); + MPI_Comm_size( communicator, &m_size); + + std::vector< std::vector< NodeID > > send_buffers; // buffers to send messages + send_buffers.resize(m_size); + std::vector m_PE_packed; + m_PE_packed.resize(m_size); + for( unsigned peID = 0; peID < m_PE_packed.size(); peID++) { + m_PE_packed[ peID ] = false; + } + + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + PEID peID = G.getTargetPE(target); + if( !m_PE_packed[peID] ) { // make sure a node is sent at most once + send_buffers[peID].push_back(G.getGlobalID(node)); + send_buffers[peID].push_back(G.getNodeLabel(node)); + m_PE_packed[peID] = true; } + } + } endfor + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + m_PE_packed[G.getTargetPE(target)] = false; + } + } endfor +} endfor + +//send all neighbors their packages using Isends +//a neighbor that does not receive something gets a specific token +for( PEID peID = 0; peID < (PEID)send_buffers.size(); peID++) { + if( G.is_adjacent_PE(peID) ) { + //now we have to send a message + if( send_buffers[peID].size() == 0 ){ + // length 1 encode no message + send_buffers[peID].push_back(0); + } + + MPI_Request rq; int tag = peID+17*m_size; + MPI_Isend( &send_buffers[peID][0], + send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, tag, communicator, &rq); + + } +} - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - PEID peID = G.getTargetPE(target); - if( !m_PE_packed[peID] ) { // make sure a node is sent at most once - send_buffers[peID].push_back(G.getGlobalID(node)); - send_buffers[peID].push_back(G.getNodeLabel(node)); - m_PE_packed[peID] = true; - } - } - } endfor - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - m_PE_packed[G.getTargetPE(target)] = false; - } - } endfor - } endfor - - //send all neighbors their packages using Isends - //a neighbor that does not receive something gets a specific token - for( PEID peID = 0; peID < (PEID)send_buffers.size(); peID++) { - if( G.is_adjacent_PE(peID) ) { - //now we have to send a message - if( send_buffers[peID].size() == 0 ){ - // length 1 encode no message - send_buffers[peID].push_back(0); - } - - MPI_Request rq; int tag = peID+17*m_size; - MPI_Isend( &send_buffers[peID][0], - send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, tag, communicator, &rq); - - } - } + //receive incomming + PEID counter = 0; + while( counter < G.getNumberOfAdjacentPEs()) { + // wait for incomming message of an adjacent processor + unsigned int tag = m_rank+17*m_size; + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, tag, communicator, &st); - //receive incomming - PEID counter = 0; - while( counter < G.getNumberOfAdjacentPEs()) { - // wait for incomming message of an adjacent processor - unsigned int tag = m_rank+17*m_size; - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, tag, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - - std::vector message; message.resize(message_length); - - MPI_Status rst; - MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, tag, communicator, &rst); - - counter++; - - // now integrate the changes - if(message_length == 1) continue; // nothing to do - - for( int i = 0; i < message_length-1; i+=2) { - NodeID global_id = message[i]; - NodeID label = message[i+1]; - - if(G.getNodeLabel(G.getLocalID(global_id)) != label) { - std::cout << "labels not ok" << std::endl; - exit(0); - } - } - } + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + + std::vector message; message.resize(message_length); + + MPI_Status rst; + MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, tag, communicator, &rst); + + counter++; + + // now integrate the changes + if(message_length == 1) continue; // nothing to do - MPI_Barrier(communicator); + for( int i = 0; i < message_length-1; i+=2) { + NodeID global_id = message[i]; + NodeID label = message[i+1]; + + if(G.getNodeLabel(G.getLocalID(global_id)) != label) { + std::cout << "labels not ok" << std::endl; + exit(0); + } + } + } + + MPI_Barrier(communicator); } void distributed_partitioner::check( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G) { - PEID m_rank, m_size; - MPI_Comm_rank( communicator, &m_rank); - MPI_Comm_size( communicator, &m_size); - - std::vector< std::vector< NodeID > > send_buffers; // buffers to send messages - send_buffers.resize(m_size); - std::vector m_PE_packed; - m_PE_packed.resize(m_size); - for( unsigned peID = 0; peID < m_PE_packed.size(); peID++) { - m_PE_packed[ peID ] = false; + PEID m_rank, m_size; + MPI_Comm_rank( communicator, &m_rank); + MPI_Comm_size( communicator, &m_size); + + std::vector< std::vector< NodeID > > send_buffers; // buffers to send messages + send_buffers.resize(m_size); + std::vector m_PE_packed; + m_PE_packed.resize(m_size); + for( unsigned peID = 0; peID < m_PE_packed.size(); peID++) { + m_PE_packed[ peID ] = false; + } + + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + PEID peID = G.getTargetPE(target); + if( !m_PE_packed[peID] ) { // make sure a node is sent at most once + send_buffers[peID].push_back(G.getGlobalID(node)); + send_buffers[peID].push_back(G.getSecondPartitionIndex(node)); + m_PE_packed[peID] = true; } + } + } endfor + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + m_PE_packed[G.getTargetPE(target)] = false; + } + } endfor +} endfor + +//send all neighbors their packages using Isends +//a neighbor that does not receive something gets a specific token +for( PEID peID = 0; peID < (PEID)send_buffers.size(); peID++) { + if( G.is_adjacent_PE(peID) ) { + //now we have to send a message + if( send_buffers[peID].size() == 0 ){ + // length 1 encode no message + send_buffers[peID].push_back(0); + } + + MPI_Request rq; + MPI_Isend( &send_buffers[peID][0], + send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, peID+17*m_size, communicator, &rq); + } +} - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - PEID peID = G.getTargetPE(target); - if( !m_PE_packed[peID] ) { // make sure a node is sent at most once - send_buffers[peID].push_back(G.getGlobalID(node)); - send_buffers[peID].push_back(G.getSecondPartitionIndex(node)); - m_PE_packed[peID] = true; - } - } - } endfor - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - m_PE_packed[G.getTargetPE(target)] = false; - } - } endfor - } endfor - - //send all neighbors their packages using Isends - //a neighbor that does not receive something gets a specific token - for( PEID peID = 0; peID < (PEID)send_buffers.size(); peID++) { - if( G.is_adjacent_PE(peID) ) { - //now we have to send a message - if( send_buffers[peID].size() == 0 ){ - // length 1 encode no message - send_buffers[peID].push_back(0); - } - - MPI_Request rq; - MPI_Isend( &send_buffers[peID][0], - send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, peID+17*m_size, communicator, &rq); - } - } + //receive incomming + PEID counter = 0; + while( counter < G.getNumberOfAdjacentPEs()) { + // wait for incomming message of an adjacent processor + unsigned int tag = m_rank+17*m_size; + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, tag, communicator, &st); - //receive incomming - PEID counter = 0; - while( counter < G.getNumberOfAdjacentPEs()) { - // wait for incomming message of an adjacent processor - unsigned int tag = m_rank+17*m_size; - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, tag, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - - std::vector message; message.resize(message_length); - - MPI_Status rst; - MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, tag, communicator, &rst); - - counter++; - - // now integrate the changes - if(message_length == 1) continue; // nothing to do - - for( int i = 0; i < message_length-1; i+=2) { - NodeID global_id = message[i]; - NodeID label = message[i+1]; - - if(G.getSecondPartitionIndex(G.getLocalID(global_id)) != label) { - std::cout << "second partition index weird" << std::endl; - exit(0); - } - } - } + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - MPI_Barrier(communicator); -} + std::vector message; message.resize(message_length); + + MPI_Status rst; + MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, tag, communicator, &rst); + counter++; + + // now integrate the changes + if(message_length == 1) continue; // nothing to do + + for( int i = 0; i < message_length-1; i+=2) { + NodeID global_id = message[i]; + NodeID label = message[i+1]; + + if(G.getSecondPartitionIndex(G.getLocalID(global_id)) != label) { + std::cout << "second partition index weird" << std::endl; + exit(0); + } + } + } + + MPI_Barrier(communicator); +} +} diff --git a/parallel/parallel_src/lib/distributed_partitioning/distributed_partitioner.h b/parallel/parallel_src/lib/distributed_partitioning/distributed_partitioner.h index cf5fe493..07c3c7c0 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/distributed_partitioner.h +++ b/parallel/parallel_src/lib/distributed_partitioning/distributed_partitioner.h @@ -12,7 +12,7 @@ #include "partition_config.h" #include "data_structure/parallel_graph_access.h" #include "stop_rule.h" - +namespace parhip { class distributed_partitioner { public: distributed_partitioner(); @@ -41,6 +41,6 @@ class distributed_partitioner { int m_cycle; timer m_t; }; - +} #endif /* end of include guard: DISTRIBUTED_PARTITIONER_ZYL2XF6R */ diff --git a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp index 805d44e5..ec3b346c 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp +++ b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp @@ -11,7 +11,7 @@ #include "parallel_contraction_projection/parallel_projection.h" #include "io/parallel_graph_io.h" #include "tools/distributed_quality_metrics.h" - +namespace parhip { distributed_evolutionary_partitioning::distributed_evolutionary_partitioning() { } @@ -23,193 +23,193 @@ distributed_evolutionary_partitioning::~distributed_evolutionary_partitioning() void distributed_evolutionary_partitioning::perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & Q) { - mpi_tools mpitools; - parallel_graph_access Q_bar; - distributed_quality_metrics dqm; - mpitools.collect_parallel_graph_to_local_graph( communicator, config, Q, Q_bar); - mpitools.distribute_local_graph( communicator, config, Q_bar); - - - - int n = Q_bar.number_of_local_nodes(); - int nparts = config.k; // k-way partitioning. - - int* xadj = Q_bar.UNSAFE_metis_style_xadj_array(); - int* adjncy = Q_bar.UNSAFE_metis_style_adjncy_array(); - - int* vwgt = Q_bar.UNSAFE_metis_style_vwgt_array(); - int* adjwgt = Q_bar.UNSAFE_metis_style_adjwgt_array(); - - int* partition_map = new int[n]; - - PEID rank; MPI_Comm_rank( communicator, &rank); - - EdgeWeight prev_cut = 0; - NodeWeight prev_max_block_weight = 0; - - if( config.vcycle && rank == 0) { - forall_local_nodes(Q_bar, node) { - partition_map[node] = Q_bar.getSecondPartitionIndex(node); - } endfor - - prev_cut = dqm.local_edge_cut(Q_bar, partition_map, communicator); - prev_max_block_weight = dqm.local_max_block_weight(config, Q_bar, partition_map, communicator); - //std::cout << "prev cut " << prev_cut << std::endl; - //std::cout << "prev max block " << prev_max_block_weight << std::endl; - } - - if( config.vcycle ) { - MPI_Bcast(partition_map, n, MPI_INT, ROOT, communicator); - MPI_Bcast(&prev_cut, 1 , MPI_LONG, ROOT, communicator); - MPI_Bcast(&prev_max_block_weight, 1 , MPI_LONG, ROOT, communicator); - } - - double inbalance = config.inbalance/100.0; - int edgecut = 0; - double balance = 0; - bool graph_partitioned = config.vcycle; - - int mode = 0; - - switch( config.initial_partitioning_algorithm ) { - case KAFFPAESTRONG: - mode = STRONG; - break; - case KAFFPAEECO: - mode = ECO; - break; - case KAFFPAEFAST: - mode = FAST; - break; - case KAFFPAEULTRAFASTSNW: - mode = ULTRAFASTSOCIAL; - break; - case KAFFPAEFASTSNW: - mode = FASTSOCIAL; - break; - case KAFFPAEECOSNW: - mode = ECOSOCIAL; - break; - case KAFFPAESTRONGSNW: - mode = STRONGSOCIAL; - break; - default: - mode = FASTSOCIAL; - break; - } - - if(config.vcycle) { - forall_local_nodes(Q_bar, node) { - Q_bar.setNodeLabel(node, partition_map[node]); - } endfor - } - - timer t; - + mpi_tools mpitools; + parallel_graph_access Q_bar; + distributed_quality_metrics dqm; + mpitools.collect_parallel_graph_to_local_graph( communicator, config, Q, Q_bar); + mpitools.distribute_local_graph( communicator, config, Q_bar); + + + + int n = Q_bar.number_of_local_nodes(); + int nparts = config.k; // k-way partitioning. + + int* xadj = Q_bar.UNSAFE_metis_style_xadj_array(); + int* adjncy = Q_bar.UNSAFE_metis_style_adjncy_array(); + + int* vwgt = Q_bar.UNSAFE_metis_style_vwgt_array(); + int* adjwgt = Q_bar.UNSAFE_metis_style_adjwgt_array(); + + int* partition_map = new int[n]; + + PEID rank; MPI_Comm_rank( communicator, &rank); + + EdgeWeight prev_cut = 0; + NodeWeight prev_max_block_weight = 0; + + if( config.vcycle && rank == 0) { + forall_local_nodes(Q_bar, node) { + partition_map[node] = Q_bar.getSecondPartitionIndex(node); + } endfor + + prev_cut = dqm.local_edge_cut(Q_bar, partition_map, communicator); + prev_max_block_weight = dqm.local_max_block_weight(config, Q_bar, partition_map, communicator); + //std::cout << "prev cut " << prev_cut << std::endl; + //std::cout << "prev max block " << prev_max_block_weight << std::endl; + } + + if( config.vcycle ) { + MPI_Bcast(partition_map, n, MPI_INT, ROOT, communicator); + MPI_Bcast(&prev_cut, 1 , MPI_LONG, ROOT, communicator); + MPI_Bcast(&prev_max_block_weight, 1 , MPI_LONG, ROOT, communicator); + } + + double inbalance = config.inbalance/100.0; + int edgecut = 0; + double balance = 0; + bool graph_partitioned = config.vcycle; + + int mode = 0; + + switch( config.initial_partitioning_algorithm ) { + case KAFFPAESTRONG: + mode = STRONG; + break; + case KAFFPAEECO: + mode = ECO; + break; + case KAFFPAEFAST: + mode = FAST; + break; + case KAFFPAEULTRAFASTSNW: + mode = ULTRAFASTSOCIAL; + break; + case KAFFPAEFASTSNW: + mode = FASTSOCIAL; + break; + case KAFFPAEECOSNW: + mode = ECOSOCIAL; + break; + case KAFFPAESTRONGSNW: + mode = STRONGSOCIAL; + break; + default: + mode = FASTSOCIAL; + break; + } + + if(config.vcycle) { + forall_local_nodes(Q_bar, node) { + Q_bar.setNodeLabel(node, partition_map[node]); + } endfor +} + + timer t; + #ifdef NOOUTPUT - std::streambuf* backup = std::cout.rdbuf(); - std::ofstream ofs; - ofs.open("/dev/null"); - std::cout.rdbuf(ofs.rdbuf()); + std::streambuf* backup = std::cout.rdbuf(); + std::ofstream ofs; + ofs.open("/dev/null"); + std::cout.rdbuf(ofs.rdbuf()); #endif #ifdef DETERMINISTIC_PARHIP - kaffpaE(&n, - vwgt, - xadj, - adjwgt, - adjncy, - &nparts, - &inbalance, - false, // supress output - graph_partitioned, - 0, // time limit set to zero, so only the initial population is created - config.seed, - mode, - communicator, - &edgecut, - &balance, - partition_map); + kaffpaE(&n, + vwgt, + xadj, + adjwgt, + adjncy, + &nparts, + &inbalance, + false, // supress output + graph_partitioned, + 0, // time limit set to zero, so only the initial population is created + config.seed, + mode, + communicator, + &edgecut, + &balance, + partition_map); #else - kaffpaE(&n, - vwgt, - xadj, - adjwgt, - adjncy, - &nparts, - &inbalance, - false, // supress output - graph_partitioned, - config.evolutionary_time_limit, // time limit - config.seed, - mode, - communicator, - &edgecut, - &balance, - partition_map); + kaffpaE(&n, + vwgt, + xadj, + adjwgt, + adjncy, + &nparts, + &inbalance, + false, // supress output + graph_partitioned, + config.evolutionary_time_limit, // time limit + config.seed, + mode, + communicator, + &edgecut, + &balance, + partition_map); #endif - + #ifdef NOOUTPUT - ofs.close(); - std::cout.rdbuf(backup); + ofs.close(); + std::cout.rdbuf(backup); #endif - if( rank == (int)ROOT) { - PRINT(std::cout << "partitioner call took " << t.elapsed() << std::endl;); - } + if( rank == (int)ROOT) { + PRINT(std::cout << "partitioner call took " << t.elapsed() << std::endl;); + } #ifndef NOOUTPUT - if( rank == (int)ROOT) { - std::cout << "log>cut computed by IP algorithm " << edgecut << std::endl; - std::cout << "log>balance computed by IP algorithm "<< balance << std::endl; - } + if( rank == (int)ROOT) { + std::cout << "log>cut computed by IP algorithm " << edgecut << std::endl; + std::cout << "log>balance computed by IP algorithm "<< balance << std::endl; + } #endif - if( !config.vcycle ) { - forall_local_nodes(Q_bar, node) { - Q_bar.setNodeLabel(node, partition_map[node]); - } endfor - } else { - NodeWeight cur_max_block_weight = dqm.local_max_block_weight(config, Q_bar, partition_map, communicator); - - //balance and cut improved - bool accept = (cur_max_block_weight <= prev_max_block_weight || balance <= 1.03) && (EdgeWeight)edgecut <= prev_cut; - // or we previously have not been feasible and now are feasible - accept = accept || (prev_max_block_weight >= config.upper_bound_partition && cur_max_block_weight <= config.upper_bound_partition); - - if( accept ) { - if( rank == (int)ROOT) { - PRINT(std::cout << "log>update criterion reached, updating partition" << std::endl;) - - } - forall_local_nodes(Q_bar, node) { - Q_bar.setNodeLabel(node, partition_map[node]); - } endfor - } else { - if( rank == (int)ROOT) { - PRINT(std::cout << "update criterion not reached, not updating partition" << std::endl;) - } - } - } - - parallel_projection parallel_project_init; - parallel_project_init.initial_assignment( Q, Q_bar ); + if( !config.vcycle ) { + forall_local_nodes(Q_bar, node) { + Q_bar.setNodeLabel(node, partition_map[node]); + } endfor +} else { + NodeWeight cur_max_block_weight = dqm.local_max_block_weight(config, Q_bar, partition_map, communicator); + + //balance and cut improved + bool accept = (cur_max_block_weight <= prev_max_block_weight || balance <= 1.03) && (EdgeWeight)edgecut <= prev_cut; + // or we previously have not been feasible and now are feasible + accept = accept || (prev_max_block_weight >= config.upper_bound_partition && cur_max_block_weight <= config.upper_bound_partition); + + if( accept ) { + if( rank == (int)ROOT) { + PRINT(std::cout << "log>update criterion reached, updating partition" << std::endl;) + +} + forall_local_nodes(Q_bar, node) { + Q_bar.setNodeLabel(node, partition_map[node]); + } endfor +} else { + if( rank == (int)ROOT) { + PRINT(std::cout << "update criterion not reached, not updating partition" << std::endl;) +} +} +} + + parallel_projection parallel_project_init; + parallel_project_init.initial_assignment( Q, Q_bar ); #ifndef NOOUTPUT - edgecut = dqm.edge_cut(Q, communicator); - balance = dqm.balance(config, Q, communicator); - if( rank == (int)ROOT) { - std::cout << "log>cur edge cut " << edgecut << std::endl; - std::cout << "log>cur balance " << balance << std::endl; - } + edgecut = dqm.edge_cut(Q, communicator); + balance = dqm.balance(config, Q, communicator); + if( rank == (int)ROOT) { + std::cout << "log>cur edge cut " << edgecut << std::endl; + std::cout << "log>cur balance " << balance << std::endl; + } #endif - delete[] xadj; - delete[] adjncy; - delete[] vwgt; - delete[] adjwgt; - delete[] partition_map; + delete[] xadj; + delete[] adjncy; + delete[] vwgt; + delete[] adjwgt; + delete[] partition_map; +} } - diff --git a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.h b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.h index 17adcc6f..81f2416f 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.h +++ b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.h @@ -10,13 +10,13 @@ #include "partition_config.h" #include "data_structure/parallel_graph_access.h" - +namespace parhip { class distributed_evolutionary_partitioning { public: - distributed_evolutionary_partitioning(); - virtual ~distributed_evolutionary_partitioning(); + distributed_evolutionary_partitioning(); + virtual ~distributed_evolutionary_partitioning(); - void perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G); + void perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G); }; - +} #endif /* end of include guard: DISTRIBUTED_EVOLUTIONARY_PARTITIONING_OJ2RIKR7 */ diff --git a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp index 3a7d2be6..247dfa90 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp +++ b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp @@ -8,7 +8,7 @@ #include "distributed_evolutionary_partitioning.h" #include "initial_partitioning.h" #include "random_initial_partitioning.h" - +namespace parhip { initial_partitioning_algorithm::initial_partitioning_algorithm() { } @@ -19,12 +19,12 @@ initial_partitioning_algorithm::~initial_partitioning_algorithm() { void initial_partitioning_algorithm::perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & Q) { - if( config.initial_partitioning_algorithm == RANDOMIP) { - random_initial_partitioning dist_rpart; - dist_rpart.perform_partitioning( communicator, config, Q ); - } else { - distributed_evolutionary_partitioning dist_epart; - dist_epart.perform_partitioning( communicator, config, Q); - } + if( config.initial_partitioning_algorithm == RANDOMIP) { + random_initial_partitioning dist_rpart; + dist_rpart.perform_partitioning( communicator, config, Q ); + } else { + distributed_evolutionary_partitioning dist_epart; + dist_epart.perform_partitioning( communicator, config, Q); + } +} } - diff --git a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.h b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.h index e08f2c79..96133cc6 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.h +++ b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.h @@ -10,14 +10,14 @@ #include "partition_config.h" #include "data_structure/parallel_graph_access.h" - +namespace parhip { class initial_partitioning_algorithm { public: - initial_partitioning_algorithm(); - virtual ~initial_partitioning_algorithm(); + initial_partitioning_algorithm(); + virtual ~initial_partitioning_algorithm(); - void perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G); + void perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G); }; - +} #endif /* end of include guard: INITIAL_PARTITIONING_SFMCJN2U */ diff --git a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.cpp b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.cpp index bd0b51b9..c2a48ddc 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.cpp +++ b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.cpp @@ -9,7 +9,7 @@ #include "data_structure/parallel_graph_access.h" #include "tools/random_functions.h" #include "tools/distributed_quality_metrics.h" - +namespace parhip { random_initial_partitioning::random_initial_partitioning() { } @@ -21,22 +21,23 @@ random_initial_partitioning::~random_initial_partitioning() { void random_initial_partitioning::perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G) { - forall_local_nodes(G, node) { - G.setNodeLabel(node, random_functions::nextInt(0ULL, config.k-1)); - } endfor - - G.update_ghost_node_data_global(); // exchange the labels of ghost nodes + forall_local_nodes(G, node) { + G.setNodeLabel(node, random_functions::nextInt(0ULL, config.k-1)); + } endfor + + G.update_ghost_node_data_global(); // exchange the labels of ghost nodes + + distributed_quality_metrics qm; + EdgeWeight edgecut = qm.edge_cut(G, communicator ); + double balance = qm.balance(config, G, communicator ); - distributed_quality_metrics qm; - EdgeWeight edgecut = qm.edge_cut(G, communicator ); - double balance = qm.balance(config, G, communicator ); + PEID rank; + MPI_Comm_rank( communicator, &rank); - PEID rank; - MPI_Comm_rank( communicator, &rank); - - if( rank == (int)ROOT) { - std::cout << "log>initial edge edge cut " << edgecut << std::endl; - std::cout << "log>initial imbalance " << balance << std::endl; - } + if( rank == (int)ROOT) { + std::cout << "log>initial edge edge cut " << edgecut << std::endl; + std::cout << "log>initial imbalance " << balance << std::endl; + } } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.h b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.h index 620613ad..90eb6577 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.h +++ b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.h @@ -10,16 +10,16 @@ #include #include "partition_config.h" - +namespace parhip { class parallel_graph_access; class random_initial_partitioning { public: - random_initial_partitioning(); - virtual ~random_initial_partitioning(); - - void perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G); -}; + random_initial_partitioning(); + virtual ~random_initial_partitioning(); + void perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G); +}; +} #endif /* end of include guard: RANDOM_INITIAL_PARTITIONING_FM8LJSI0 */ diff --git a/parallel/parallel_src/lib/distributed_partitioning/stop_rule.h b/parallel/parallel_src/lib/distributed_partitioning/stop_rule.h index b571645c..03548591 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/stop_rule.h +++ b/parallel/parallel_src/lib/distributed_partitioning/stop_rule.h @@ -10,7 +10,7 @@ #include "data_structure/parallel_graph_access.h" #include "partition_config.h" - +namespace parhip { class stop_rule { public: stop_rule() {} ; @@ -22,6 +22,5 @@ class stop_rule { return false; } }; - - +} #endif /* end of include guard: STOP_RULE_23YOZ7GX */ diff --git a/parallel/parallel_src/lib/dspac/dspac.cpp b/parallel/parallel_src/lib/dspac/dspac.cpp index 0ecd1235..203fb8c4 100644 --- a/parallel/parallel_src/lib/dspac/dspac.cpp +++ b/parallel/parallel_src/lib/dspac/dspac.cpp @@ -7,253 +7,253 @@ *****************************************************************************/ #include "dspac.h" - +namespace parhip { dspac::dspac(parallel_graph_access &graph, MPI_Comm comm, EdgeWeight infinity) : m_comm(comm), m_infinity(infinity), m_input_graph(graph) { } void dspac::construct(parallel_graph_access &split_graph) { - assert(assert_adjacency_lists_sorted()); - MPI_Barrier(m_comm); - internal_construct(split_graph); - MPI_Barrier(m_comm); - assert(assert_sanity_checks(split_graph)); + assert(assert_adjacency_lists_sorted()); + MPI_Barrier(m_comm); + internal_construct(split_graph); + MPI_Barrier(m_comm); + assert(assert_sanity_checks(split_graph)); } void dspac::internal_construct(parallel_graph_access &split_graph) { - int size, rank; - MPI_Comm_size(m_comm, &size); - MPI_Comm_rank(m_comm, &rank); - const NodeID n = m_input_graph.number_of_global_nodes(); + int size, rank; + MPI_Comm_size(m_comm, &size); + MPI_Comm_rank(m_comm, &rank); + const NodeID n = m_input_graph.number_of_global_nodes(); #ifndef NDEBUG - const NodeID m = m_input_graph.number_of_global_edges(); + const NodeID m = m_input_graph.number_of_global_edges(); #endif - timer construction_timer; - - // we construct the split nodes from..(to - 1) on this node - auto edge_range_array = m_input_graph.get_edge_range_array(); - assert(assert_edge_range_array_ok(edge_range_array)); - const std::size_t from = edge_range_array[rank]; // inclusive - const std::size_t to = edge_range_array[rank + 1]; // exclusive - assert(to <= m_input_graph.number_of_global_edges()); - - // we need the number of vertices of degree 1 or 2 to calculate the dimension of the split graph - NodeID local_number_of_deg_1_or_2_vertices = 0; - for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { - EdgeID deg = m_input_graph.getNodeDegree(v); - if (deg == 1 || deg == 2) { - ++local_number_of_deg_1_or_2_vertices; - } - } - - NodeID global_number_of_deg_1_or_2_vertices = 0; - MPI_Allreduce(&local_number_of_deg_1_or_2_vertices, &global_number_of_deg_1_or_2_vertices, 1, - MPI_UNSIGNED_LONG_LONG, MPI_SUM, m_comm); - if (rank == 0) { - std::cout << "[dspac::internal_construct()] Up to MPI_Allreduce() took " - << construction_timer.elapsed() << std::endl; - construction_timer.restart(); + timer construction_timer; + + // we construct the split nodes from..(to - 1) on this node + auto edge_range_array = m_input_graph.get_edge_range_array(); + assert(assert_edge_range_array_ok(edge_range_array)); + const std::size_t from = edge_range_array[rank]; // inclusive + const std::size_t to = edge_range_array[rank + 1]; // exclusive + assert(to <= m_input_graph.number_of_global_edges()); + + // we need the number of vertices of degree 1 or 2 to calculate the dimension of the split graph + NodeID local_number_of_deg_1_or_2_vertices = 0; + for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { + EdgeID deg = m_input_graph.getNodeDegree(v); + if (deg == 1 || deg == 2) { + ++local_number_of_deg_1_or_2_vertices; } - - // calculate split graph dimensions - const NodeID local_number_of_split_nodes = m_input_graph.number_of_local_edges(); - const EdgeID global_number_of_split_nodes = m_input_graph.number_of_global_edges(); - - assert(3 * m_input_graph.number_of_local_edges() >= 2 * local_number_of_deg_1_or_2_vertices); - const EdgeID local_number_of_split_edges = 3 * m_input_graph.number_of_local_edges() - - 2 * local_number_of_deg_1_or_2_vertices; - - assert(3 * m_input_graph.number_of_global_edges() >= 2 * global_number_of_deg_1_or_2_vertices); - const NodeID global_number_of_split_edges = 3 * m_input_graph.number_of_global_edges() - - 2 * global_number_of_deg_1_or_2_vertices; - - // this array stores the distribution of nodes across PEs, namely PE i stores nodes - // node_range_array[i]..node_range_array[i + 1]-1 - // the default loader sets a wrong value for node_range_array[size] though, so we need to fix that for our purposes - // here - auto node_range_array = m_input_graph.get_range_array(); - node_range_array[size] = m_input_graph.number_of_global_nodes(); - assert(assert_node_range_array_ok(node_range_array)); - - std::vector> first_split_node_on(size); - - // first, reserve memory for adjacent PEs - for (PEID pe = 0; pe < size; ++pe) { - if (m_input_graph.is_adjacent_PE(pe) || pe == rank) { - first_split_node_on[pe].resize(m_input_graph.number_of_local_nodes()); - } - } - - // then fill the reserved memory - for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { - PEID current_pe = -1; - for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { - NodeID u = m_input_graph.getEdgeTarget(e); - PEID pe = m_input_graph.is_local_node(u) ? static_cast(rank) : m_input_graph.getTargetPE(u); - - if (pe != current_pe) { - assert(first_split_node_on[pe].size() == m_input_graph.number_of_local_nodes()); - first_split_node_on[pe][v] = from + e; - current_pe = pe; - } - } + } + + NodeID global_number_of_deg_1_or_2_vertices = 0; + MPI_Allreduce(&local_number_of_deg_1_or_2_vertices, &global_number_of_deg_1_or_2_vertices, 1, + MPI_UNSIGNED_LONG_LONG, MPI_SUM, m_comm); + if (rank == 0) { + std::cout << "[dspac::internal_construct()] Up to MPI_Allreduce() took " + << construction_timer.elapsed() << std::endl; + construction_timer.restart(); + } + + // calculate split graph dimensions + const NodeID local_number_of_split_nodes = m_input_graph.number_of_local_edges(); + const EdgeID global_number_of_split_nodes = m_input_graph.number_of_global_edges(); + + assert(3 * m_input_graph.number_of_local_edges() >= 2 * local_number_of_deg_1_or_2_vertices); + const EdgeID local_number_of_split_edges = 3 * m_input_graph.number_of_local_edges() + - 2 * local_number_of_deg_1_or_2_vertices; + + assert(3 * m_input_graph.number_of_global_edges() >= 2 * global_number_of_deg_1_or_2_vertices); + const NodeID global_number_of_split_edges = 3 * m_input_graph.number_of_global_edges() + - 2 * global_number_of_deg_1_or_2_vertices; + + // this array stores the distribution of nodes across PEs, namely PE i stores nodes + // node_range_array[i]..node_range_array[i + 1]-1 + // the default loader sets a wrong value for node_range_array[size] though, so we need to fix that for our purposes + // here + auto node_range_array = m_input_graph.get_range_array(); + node_range_array[size] = m_input_graph.number_of_global_nodes(); + assert(assert_node_range_array_ok(node_range_array)); + + std::vector> first_split_node_on(size); + + // first, reserve memory for adjacent PEs + for (PEID pe = 0; pe < size; ++pe) { + if (m_input_graph.is_adjacent_PE(pe) || pe == rank) { + first_split_node_on[pe].resize(m_input_graph.number_of_local_nodes()); } - - if (rank == 0) { - std::cout << "[dspac::internal_construct()] Preparation of first_split_node_on[] took " - << construction_timer.elapsed() << std::endl; - construction_timer.restart(); + } + + // then fill the reserved memory + for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { + PEID current_pe = -1; + for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { + NodeID u = m_input_graph.getEdgeTarget(e); + PEID pe = m_input_graph.is_local_node(u) ? static_cast(rank) : m_input_graph.getTargetPE(u); + + if (pe != current_pe) { + assert(first_split_node_on[pe].size() == m_input_graph.number_of_local_nodes()); + first_split_node_on[pe][v] = from + e; + current_pe = pe; + } } - - // once created, this array has the following semantic: say we have an edge vu in the original graph where - // v is on our PE and u is on any PE - // when we create the split graph, when need to connect one split vertex of v and one of u to represent the vu edge - // in the split graph - // so when we connect the split vertices of v, we use first_split_node[globalId(u)] as the split node id of u and - // then increment it by one, so that when need a split vertex of u again on this PE, we use the next one and so on - std::vector first_split_node(n); // contains global node ids - - // receive the messages from adjacent PEs and place them at the right position in first_split_node: the messages - // from PE i should be placed starting at node_range_array[i] - std::vector requests; - - // send the messages to adjacent PEs - for (PEID pe = 0; pe < size; ++pe) { - if (m_input_graph.is_adjacent_PE(pe)) { - assert(rank != pe); - - NodeID *buf = &first_split_node_on[pe][0]; - const std::size_t count = first_split_node_on[pe].size(); - - assert(count == node_range_array[rank + 1] - node_range_array[rank]); - assert(count < std::numeric_limits::max()); - - MPI_Request *request = new MPI_Request; - MPI_Isend(buf, static_cast(count), MPI_UNSIGNED_LONG_LONG, pe, 0, m_comm, request); - requests.push_back(request); - } + } + + if (rank == 0) { + std::cout << "[dspac::internal_construct()] Preparation of first_split_node_on[] took " + << construction_timer.elapsed() << std::endl; + construction_timer.restart(); + } + + // once created, this array has the following semantic: say we have an edge vu in the original graph where + // v is on our PE and u is on any PE + // when we create the split graph, when need to connect one split vertex of v and one of u to represent the vu edge + // in the split graph + // so when we connect the split vertices of v, we use first_split_node[globalId(u)] as the split node id of u and + // then increment it by one, so that when need a split vertex of u again on this PE, we use the next one and so on + std::vector first_split_node(n); // contains global node ids + + // receive the messages from adjacent PEs and place them at the right position in first_split_node: the messages + // from PE i should be placed starting at node_range_array[i] + std::vector requests; + + // send the messages to adjacent PEs + for (PEID pe = 0; pe < size; ++pe) { + if (m_input_graph.is_adjacent_PE(pe)) { + assert(rank != pe); + + NodeID *buf = &first_split_node_on[pe][0]; + const std::size_t count = first_split_node_on[pe].size(); + + assert(count == node_range_array[rank + 1] - node_range_array[rank]); + assert(count < std::numeric_limits::max()); + + MPI_Request *request = new MPI_Request; + MPI_Isend(buf, static_cast(count), MPI_UNSIGNED_LONG_LONG, pe, 0, m_comm, request); + requests.push_back(request); } + } - // copy own data from first_split_node_on to first_split_node - assert(first_split_node_on[rank].size() == m_input_graph.number_of_local_nodes()); - assert(first_split_node_on[rank].size() == node_range_array[rank + 1] - node_range_array[rank]); - assert(first_split_node.data() + node_range_array[rank] + m_input_graph.number_of_local_nodes() - <= (&first_split_node[n - 1]) + 1); - std::copy(first_split_node_on[rank].begin(), first_split_node_on[rank].end(), - first_split_node.begin() + node_range_array[rank]); + // copy own data from first_split_node_on to first_split_node + assert(first_split_node_on[rank].size() == m_input_graph.number_of_local_nodes()); + assert(first_split_node_on[rank].size() == node_range_array[rank + 1] - node_range_array[rank]); + assert(first_split_node.data() + node_range_array[rank] + m_input_graph.number_of_local_nodes() + <= (&first_split_node[n - 1]) + 1); + std::copy(first_split_node_on[rank].begin(), first_split_node_on[rank].end(), + first_split_node.begin() + node_range_array[rank]); - // receive messages from adjacent neighbors - for (PEID pe = 0; pe < size; ++pe) { - if (m_input_graph.is_adjacent_PE(pe)) { - assert(rank != pe); + // receive messages from adjacent neighbors + for (PEID pe = 0; pe < size; ++pe) { + if (m_input_graph.is_adjacent_PE(pe)) { + assert(rank != pe); - NodeID *buf = &first_split_node[node_range_array[pe]]; - const NodeID count = node_range_array[pe + 1] - node_range_array[pe]; + NodeID *buf = &first_split_node[node_range_array[pe]]; + const NodeID count = node_range_array[pe + 1] - node_range_array[pe]; - assert(node_range_array[pe] + count <= first_split_node.size()); - assert(count < std::numeric_limits::max()); + assert(node_range_array[pe] + count <= first_split_node.size()); + assert(count < std::numeric_limits::max()); - MPI_Recv(buf, static_cast(count), MPI_UNSIGNED_LONG_LONG, pe, 0, m_comm, MPI_STATUS_IGNORE); - } + MPI_Recv(buf, static_cast(count), MPI_UNSIGNED_LONG_LONG, pe, 0, m_comm, MPI_STATUS_IGNORE); } - - // wait for own messages to be received - for (MPI_Request *request : requests) { - MPI_Wait(request, MPI_STATUS_IGNORE); - delete request; + } + + // wait for own messages to be received + for (MPI_Request *request : requests) { + MPI_Wait(request, MPI_STATUS_IGNORE); + delete request; + } + + if (rank == 0) { + std::cout << "[dspac::internal_construct()] first_split_node[] communication took " + << construction_timer.elapsed() << std::endl; + construction_timer.restart(); + } + + // we no longer need first_split_node_on from now on since it's copied to first_split_node on each PE + first_split_node_on.clear(); + + // now we construct the split graph + split_graph.start_construction(local_number_of_split_nodes, local_number_of_split_edges, + global_number_of_split_nodes, global_number_of_split_edges); + split_graph.set_range_array(edge_range_array); + split_graph.set_range(from, to - 1); + + NodeID nodes_created = 0; + EdgeID edges_created = 0; + + for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { + EdgeID deg = m_input_graph.getNodeDegree(v); + if (deg == 0) { // explicitly skip isolated nodes + continue; } - if (rank == 0) { - std::cout << "[dspac::internal_construct()] first_split_node[] communication took " - << construction_timer.elapsed() << std::endl; - construction_timer.restart(); + for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { + NodeID u = m_input_graph.getEdgeTarget(e); + NodeID global_u = m_input_graph.getGlobalID(u); + + // create the split node + ++nodes_created; + NodeID split_node = split_graph.new_node(); + assert(split_node == e); + + split_graph.setNodeWeight(split_node, 1); + split_graph.setNodeLabel(split_node, from + split_node); + split_graph.setSecondPartitionIndex(split_node, 0); + + // create dominant edge + ++edges_created; + assert(global_u < first_split_node.size()); + NodeID target_node = first_split_node[global_u]; + EdgeID dominant_edge = split_graph.new_edge(split_node, target_node); + ++first_split_node[global_u]; + split_graph.setEdgeWeight(dominant_edge, m_infinity); + + // create auxiliary edges + bool first = (e == m_input_graph.get_first_edge(v)); + bool last = (e + 1 == m_input_graph.get_first_invalid_edge(v)); + + if (deg == 2) { + // degree 2: we create a path with a single edge in the split graph + ++edges_created; + int target_offset = first ? 1 : -1; + assert(0 <= split_node + target_offset && split_node + target_offset < local_number_of_split_nodes); + EdgeID auxiliary_edge = split_graph.new_edge(split_node, from + split_node + target_offset); + split_graph.setEdgeWeight(auxiliary_edge, 1); + } else if (deg > 2) { + // degree > 2: we create a cycle with all split nodes, thus we need a edge to the previous and one to + // the next node in the cycle + ++edges_created; + int next_offset = last ? -(static_cast(deg) - 1) : 1; + NodeID global_next = from + split_node + next_offset; + assert(from == split_graph.get_from_range()); + assert(split_graph.get_from_range() <= global_next && global_next <= split_graph.get_to_range()); + + EdgeID next_auxiliary_edge = split_graph.new_edge(split_node, global_next); + split_graph.setEdgeWeight(next_auxiliary_edge, 1); + + ++edges_created; + int prev_offset = first ? static_cast(deg) - 1 : -1; + NodeID global_prev = from + split_node + prev_offset; + assert(split_graph.get_from_range() <= global_prev && global_prev <= split_graph.get_to_range()); + EdgeID prev_auxiliary_edge = split_graph.new_edge(split_node, global_prev); + split_graph.setEdgeWeight(prev_auxiliary_edge, 1); + } else { + assert(deg == 1); + // nothing to do for leaves + } } + } - // we no longer need first_split_node_on from now on since it's copied to first_split_node on each PE - first_split_node_on.clear(); - - // now we construct the split graph - split_graph.start_construction(local_number_of_split_nodes, local_number_of_split_edges, - global_number_of_split_nodes, global_number_of_split_edges); - split_graph.set_range_array(edge_range_array); - split_graph.set_range(from, to - 1); - - NodeID nodes_created = 0; - EdgeID edges_created = 0; + if (rank == 0) { + std::cout << "[dspac::internal_construct()] Local construction took " + << construction_timer.elapsed() << std::endl; + construction_timer.restart(); + } - for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { - EdgeID deg = m_input_graph.getNodeDegree(v); - if (deg == 0) { // explicitly skip isolated nodes - continue; - } - - for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { - NodeID u = m_input_graph.getEdgeTarget(e); - NodeID global_u = m_input_graph.getGlobalID(u); - - // create the split node - ++nodes_created; - NodeID split_node = split_graph.new_node(); - assert(split_node == e); - - split_graph.setNodeWeight(split_node, 1); - split_graph.setNodeLabel(split_node, from + split_node); - split_graph.setSecondPartitionIndex(split_node, 0); - - // create dominant edge - ++edges_created; - assert(global_u < first_split_node.size()); - NodeID target_node = first_split_node[global_u]; - EdgeID dominant_edge = split_graph.new_edge(split_node, target_node); - ++first_split_node[global_u]; - split_graph.setEdgeWeight(dominant_edge, m_infinity); - - // create auxiliary edges - bool first = (e == m_input_graph.get_first_edge(v)); - bool last = (e + 1 == m_input_graph.get_first_invalid_edge(v)); - - if (deg == 2) { - // degree 2: we create a path with a single edge in the split graph - ++edges_created; - int target_offset = first ? 1 : -1; - assert(0 <= split_node + target_offset && split_node + target_offset < local_number_of_split_nodes); - EdgeID auxiliary_edge = split_graph.new_edge(split_node, from + split_node + target_offset); - split_graph.setEdgeWeight(auxiliary_edge, 1); - } else if (deg > 2) { - // degree > 2: we create a cycle with all split nodes, thus we need a edge to the previous and one to - // the next node in the cycle - ++edges_created; - int next_offset = last ? -(static_cast(deg) - 1) : 1; - NodeID global_next = from + split_node + next_offset; - assert(from == split_graph.get_from_range()); - assert(split_graph.get_from_range() <= global_next && global_next <= split_graph.get_to_range()); - - EdgeID next_auxiliary_edge = split_graph.new_edge(split_node, global_next); - split_graph.setEdgeWeight(next_auxiliary_edge, 1); - - ++edges_created; - int prev_offset = first ? static_cast(deg) - 1 : -1; - NodeID global_prev = from + split_node + prev_offset; - assert(split_graph.get_from_range() <= global_prev && global_prev <= split_graph.get_to_range()); - EdgeID prev_auxiliary_edge = split_graph.new_edge(split_node, global_prev); - split_graph.setEdgeWeight(prev_auxiliary_edge, 1); - } else { - assert(deg == 1); - // nothing to do for leaves - } - } - } - - if (rank == 0) { - std::cout << "[dspac::internal_construct()] Local construction took " - << construction_timer.elapsed() << std::endl; - construction_timer.restart(); - } - - assert(nodes_created == local_number_of_split_nodes); - assert(edges_created == local_number_of_split_edges); - split_graph.finish_construction(); + assert(nodes_created == local_number_of_split_nodes); + assert(edges_created == local_number_of_split_edges); + split_graph.finish_construction(); } /** @@ -262,84 +262,84 @@ void dspac::internal_construct(parallel_graph_access &split_graph) { */ bool dspac::assert_sanity_checks(parallel_graph_access &split_graph) { #ifndef NDEBUG - assert(split_graph.number_of_local_nodes() == m_input_graph.number_of_local_edges()); - for (NodeID v = 0; v < split_graph.number_of_local_nodes(); ++v) { - // isolated vertices should be removed for now - assert(0 < split_graph.getNodeDegree(v) && split_graph.getNodeDegree(v) <= 3); - - // make sure that the edge weights are correct, i.e. auxiliary edges have edge weight 1 and - // dominant edges have edge weight m_infinity - EdgeID firstEdge = split_graph.get_first_edge(v); - switch (split_graph.getNodeDegree(v)) { - case 3: // fall through intended - assert(split_graph.getEdgeWeight(firstEdge + 2) == 1); - - case 2: - assert(split_graph.getEdgeWeight(firstEdge + 1) == 1); - - case 1: - assert(split_graph.getEdgeWeight(firstEdge) == m_infinity); - break; - - default: - assert(false); - } + assert(split_graph.number_of_local_nodes() == m_input_graph.number_of_local_edges()); + for (NodeID v = 0; v < split_graph.number_of_local_nodes(); ++v) { + // isolated vertices should be removed for now + assert(0 < split_graph.getNodeDegree(v) && split_graph.getNodeDegree(v) <= 3); + + // make sure that the edge weights are correct, i.e. auxiliary edges have edge weight 1 and + // dominant edges have edge weight m_infinity + EdgeID firstEdge = split_graph.get_first_edge(v); + switch (split_graph.getNodeDegree(v)) { + case 3: // fall through intended + assert(split_graph.getEdgeWeight(firstEdge + 2) == 1); + + case 2: + assert(split_graph.getEdgeWeight(firstEdge + 1) == 1); + + case 1: + assert(split_graph.getEdgeWeight(firstEdge) == m_infinity); + break; + + default: + assert(false); + } + } + + // this part checks that the auxiliary edges are connected to the right nodes + for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { + EdgeID deg = m_input_graph.getNodeDegree(v); + if (deg == 0) { // explicitly skip isolated nodes + continue; } - // this part checks that the auxiliary edges are connected to the right nodes - for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { - EdgeID deg = m_input_graph.getNodeDegree(v); - if (deg == 0) { // explicitly skip isolated nodes - continue; + for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { + bool first = (e == m_input_graph.get_first_edge(v)); + bool last = (e + 1 == m_input_graph.get_first_invalid_edge(v)); + + if (deg == 1) { + if (split_graph.get_first_edge(e) + 1 < split_graph.number_of_local_edges()) { + // degree 1 node --> no auxiliary edges --> next edge must be a dominant edge of another node + assert(split_graph.getEdgeWeight(split_graph.get_first_edge(e) + 1) == m_infinity); + } + } else if (deg == 2) { + if (first) { + // first split node --> auxiliary edge must target the second split node + assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e + 1); + } else if (last) { + // second split node --> auxiliary edge must target the first split node + assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e - 1); + } else { + assert(false); } - for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { - bool first = (e == m_input_graph.get_first_edge(v)); - bool last = (e + 1 == m_input_graph.get_first_invalid_edge(v)); - - if (deg == 1) { - if (split_graph.get_first_edge(e) + 1 < split_graph.number_of_local_edges()) { - // degree 1 node --> no auxiliary edges --> next edge must be a dominant edge of another node - assert(split_graph.getEdgeWeight(split_graph.get_first_edge(e) + 1) == m_infinity); - } - } else if (deg == 2) { - if (first) { - // first split node --> auxiliary edge must target the second split node - assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e + 1); - } else if (last) { - // second split node --> auxiliary edge must target the first split node - assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e - 1); - } else { - assert(false); - } - - // a dominant edge must follow a single auxiliary edge - if (split_graph.get_first_edge(e) + 2 < split_graph.number_of_local_edges()) { - assert(split_graph.getEdgeWeight(split_graph.get_first_edge(e) + 2) == m_infinity); - } - } else if (deg > 2) { - if (first) { - assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e + 1); - assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 2) == e + (deg - 1)); - } else if (last) { - assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e - (deg - 1)); - assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 2) == e - 1); - } else { - assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e + 1); - assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 2) == e - 1); - } - - // a dominant edge must follow after two auxiliary edges - if (split_graph.get_first_edge(e) + 3 < split_graph.number_of_local_edges()) { - assert(split_graph.getEdgeWeight(split_graph.get_first_edge(e) + 3) == m_infinity); - } - } else { - assert(false); - } + // a dominant edge must follow a single auxiliary edge + if (split_graph.get_first_edge(e) + 2 < split_graph.number_of_local_edges()) { + assert(split_graph.getEdgeWeight(split_graph.get_first_edge(e) + 2) == m_infinity); + } + } else if (deg > 2) { + if (first) { + assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e + 1); + assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 2) == e + (deg - 1)); + } else if (last) { + assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e - (deg - 1)); + assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 2) == e - 1); + } else { + assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 1) == e + 1); + assert(split_graph.getEdgeTarget(split_graph.get_first_edge(e) + 2) == e - 1); } + + // a dominant edge must follow after two auxiliary edges + if (split_graph.get_first_edge(e) + 3 < split_graph.number_of_local_edges()) { + assert(split_graph.getEdgeWeight(split_graph.get_first_edge(e) + 3) == m_infinity); + } + } else { + assert(false); + } } + } #endif - return true; + return true; } /** @@ -348,110 +348,111 @@ bool dspac::assert_sanity_checks(parallel_graph_access &split_graph) { */ bool dspac::assert_adjacency_lists_sorted() { #ifndef NDEBUG - for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { - if (m_input_graph.getNodeDegree(v) == 0) { - continue; - } + for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { + if (m_input_graph.getNodeDegree(v) == 0) { + continue; + } - NodeID local_first_neighbor = m_input_graph.getEdgeTarget(m_input_graph.get_first_edge(v)); - auto global_first_neighbor = static_cast(m_input_graph.getGlobalID(local_first_neighbor)); - NodeID cur = global_first_neighbor; - for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { - NodeID u = m_input_graph.getEdgeTarget(e); - assert(cur <= m_input_graph.getGlobalID(u)); - } + NodeID local_first_neighbor = m_input_graph.getEdgeTarget(m_input_graph.get_first_edge(v)); + auto global_first_neighbor = static_cast(m_input_graph.getGlobalID(local_first_neighbor)); + NodeID cur = global_first_neighbor; + for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { + NodeID u = m_input_graph.getEdgeTarget(e); + assert(cur <= m_input_graph.getGlobalID(u)); } + } #endif - return true; + return true; } bool dspac::assert_edge_range_array_ok(const std::vector &edge_range_array) { - int size, rank; - MPI_Comm_size(m_comm, &size); - MPI_Comm_rank(m_comm, &rank); - assert(edge_range_array.size() == size + 1); - assert(edge_range_array[0] == 0); - assert(edge_range_array[size] == m_input_graph.number_of_global_edges()); - assert(m_input_graph.number_of_local_edges() == edge_range_array[rank + 1] - edge_range_array[rank]); - for (std::size_t pe = 0; pe < (size_t)size; ++pe) - assert(edge_range_array[pe] <= edge_range_array[pe + 1]); - return true; + int size, rank; + MPI_Comm_size(m_comm, &size); + MPI_Comm_rank(m_comm, &rank); + assert(edge_range_array.size() == size + 1); + assert(edge_range_array[0] == 0); + assert(edge_range_array[size] == m_input_graph.number_of_global_edges()); + assert(m_input_graph.number_of_local_edges() == edge_range_array[rank + 1] - edge_range_array[rank]); + for (std::size_t pe = 0; pe < (size_t)size; ++pe) + assert(edge_range_array[pe] <= edge_range_array[pe + 1]); + return true; } bool dspac::assert_node_range_array_ok(const std::vector &node_range_array) { - int size, rank; - MPI_Comm_size(m_comm, &size); - MPI_Comm_rank(m_comm, &rank); - assert(node_range_array.size() == size + 1); - assert(node_range_array[0] == 0); - assert(node_range_array[size] == m_input_graph.number_of_global_nodes()); - assert(m_input_graph.number_of_local_nodes() == node_range_array[rank + 1] - node_range_array[rank]); - return true; + int size, rank; + MPI_Comm_size(m_comm, &size); + MPI_Comm_rank(m_comm, &rank); + assert(node_range_array.size() == size + 1); + assert(node_range_array[0] == 0); + assert(node_range_array[size] == m_input_graph.number_of_global_nodes()); + assert(m_input_graph.number_of_local_nodes() == node_range_array[rank + 1] - node_range_array[rank]); + return true; } std::vector dspac::project_partition(parallel_graph_access &split_graph, const std::vector &permutation) { - std::vector edge_partition(m_input_graph.number_of_local_edges()); + std::vector edge_partition(m_input_graph.number_of_local_edges()); - for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { - for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { - edge_partition[permutation[e]] = split_graph.getNodeLabel(e); - } + for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { + for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { + edge_partition[permutation[e]] = split_graph.getNodeLabel(e); } + } - MPI_Barrier(m_comm); - return edge_partition; + MPI_Barrier(m_comm); + return edge_partition; } EdgeWeight dspac::calculate_vertex_cut(PartitionID k, const std::vector &edge_partition) { - EdgeWeight local_cost = 0; + EdgeWeight local_cost = 0; - std::vector counted(k); - for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { - if (m_input_graph.getNodeDegree(v) == 0) { - continue; - } + std::vector counted(k); + for (NodeID v = 0; v < m_input_graph.number_of_local_nodes(); ++v) { + if (m_input_graph.getNodeDegree(v) == 0) { + continue; + } - for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { - PartitionID p = edge_partition[e]; - if (!counted[p]) { - counted[p] = true; - ++local_cost; - } - } + for (EdgeID e = m_input_graph.get_first_edge(v); e < m_input_graph.get_first_invalid_edge(v); ++e) { + PartitionID p = edge_partition[e]; + if (!counted[p]) { + counted[p] = true; + ++local_cost; + } + } - counted.clear(); - counted.resize(k); + counted.clear(); + counted.resize(k); - assert(local_cost > 0); - --local_cost; - } + assert(local_cost > 0); + --local_cost; + } - EdgeWeight global_cost; - MPI_Reduce(&local_cost, &global_cost, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, 0, m_comm); - return global_cost; + EdgeWeight global_cost; + MPI_Reduce(&local_cost, &global_cost, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, 0, m_comm); + return global_cost; } void dspac::fix_cut_dominant_edges(parallel_graph_access &split_graph) { - for (NodeID v = 0; v < split_graph.number_of_local_nodes(); ++v) { - EdgeID e_vu = split_graph.get_first_edge(v); - NodeID u = split_graph.getEdgeTarget(e_vu); - - PartitionID part_v = split_graph.getNodeLabel(v); - PartitionID part_u = split_graph.getNodeLabel(u); - if (part_v != part_u) { - NodeWeight part_v_size = split_graph.getBlockSize(part_v); - NodeWeight part_u_size = split_graph.getBlockSize(part_u); - - if (part_v_size < part_u_size) { - split_graph.setNodeLabel(u, part_v); - split_graph.setBlockSize(part_v, part_v_size + 1); - split_graph.setBlockSize(part_u, part_u_size - 1); - } else { - split_graph.setNodeLabel(v, part_u); - split_graph.setBlockSize(part_v, part_v_size - 1); - split_graph.setBlockSize(part_u, part_u_size + 1); - } - } + for (NodeID v = 0; v < split_graph.number_of_local_nodes(); ++v) { + EdgeID e_vu = split_graph.get_first_edge(v); + NodeID u = split_graph.getEdgeTarget(e_vu); + + PartitionID part_v = split_graph.getNodeLabel(v); + PartitionID part_u = split_graph.getNodeLabel(u); + if (part_v != part_u) { + NodeWeight part_v_size = split_graph.getBlockSize(part_v); + NodeWeight part_u_size = split_graph.getBlockSize(part_u); + + if (part_v_size < part_u_size) { + split_graph.setNodeLabel(u, part_v); + split_graph.setBlockSize(part_v, part_v_size + 1); + split_graph.setBlockSize(part_u, part_u_size - 1); + } else { + split_graph.setNodeLabel(v, part_u); + split_graph.setBlockSize(part_v, part_v_size - 1); + split_graph.setBlockSize(part_u, part_u_size + 1); + } } - split_graph.update_block_weights(); + } + split_graph.update_block_weights(); } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/dspac/dspac.h b/parallel/parallel_src/lib/dspac/dspac.h index 8138190f..c251e2e5 100644 --- a/parallel/parallel_src/lib/dspac/dspac.h +++ b/parallel/parallel_src/lib/dspac/dspac.h @@ -12,7 +12,7 @@ #include #include "data_structure/parallel_graph_access.h" #include "definitions.h" - +namespace parhip { class dspac { public: dspac(parallel_graph_access &graph, MPI_Comm comm, EdgeWeight infinity); @@ -33,5 +33,5 @@ class dspac { EdgeWeight m_infinity; parallel_graph_access &m_input_graph; }; - +} #endif // KAHIP_DSPAC_H diff --git a/parallel/parallel_src/lib/dspac/edge_balanced_graph_io.cpp b/parallel/parallel_src/lib/dspac/edge_balanced_graph_io.cpp index b152658e..e901e5a7 100644 --- a/parallel/parallel_src/lib/dspac/edge_balanced_graph_io.cpp +++ b/parallel/parallel_src/lib/dspac/edge_balanced_graph_io.cpp @@ -10,7 +10,7 @@ #include #include "edge_balanced_graph_io.h" - +namespace parhip { static constexpr ULONG FILE_TYPE_VERSION = 3; static constexpr ULONG HEADER_SIZE = 3; @@ -29,154 +29,154 @@ static ULONG adjacencyListOffsetToEdgeID(ULONG numberOfNodes, ULONG offset); void edge_balanced_graph_io::read_binary_graph_edge_balanced(parallel_graph_access &G, const std::string &filename, const PPartitionConfig &config, std::vector &permutation) { - int rank; - int size; - MPI_Comm_size(MPI_COMM_WORLD, &size); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - read_binary_graph_edge_balanced(G, filename, config, permutation, rank, size); + int rank; + int size; + MPI_Comm_size(MPI_COMM_WORLD, &size); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + read_binary_graph_edge_balanced(G, filename, config, permutation, rank, size); } void edge_balanced_graph_io::read_binary_graph_edge_balanced(parallel_graph_access &G, const std::string &filename, const PPartitionConfig &config, std::vector &permutation, int rank, int size) { - // header[0] = version number - // header[1] = number of nodes - // header[2] = number of edges - std::array header{0}; - - // read header on ROOT; if it fails, throw, otherwise broadcast this to other PEs - int success = 0; - if (rank == ROOT) { - std::ifstream headerIn(filename, std::ios::binary | std::ios::in); - if (headerIn) { - success = 1; - headerIn.read((char *) (&header[0]), 3 * sizeof(ULONG)); - } - headerIn.close(); - } - - MPI_Bcast(&success, 1, MPI_INT, ROOT, MPI_COMM_WORLD); - if (success != 1) { - throw std::ios_base::failure("unable to read graph file"); + // header[0] = version number + // header[1] = number of nodes + // header[2] = number of edges + std::array header{0}; + + // read header on ROOT; if it fails, throw, otherwise broadcast this to other PEs + int success = 0; + if (rank == ROOT) { + std::ifstream headerIn(filename, std::ios::binary | std::ios::in); + if (headerIn) { + success = 1; + headerIn.read((char *) (&header[0]), 3 * sizeof(ULONG)); } + headerIn.close(); + } + + MPI_Bcast(&success, 1, MPI_INT, ROOT, MPI_COMM_WORLD); + if (success != 1) { + throw std::ios_base::failure("unable to read graph file"); + } + + MPI_Bcast(&header[0], header.size(), MPI_UNSIGNED_LONG_LONG, ROOT, MPI_COMM_WORLD); + ULONG version = header[0]; + ULONG n = header[1]; + ULONG m = header[2]; + + if (rank == ROOT) { + std::cout << "n=" << n << ", m=" << m << std::endl; + } + + if (version != FILE_TYPE_VERSION) { + throw std::ios_base::failure("wrong file type version"); + } + + /* + * next, we determine the number of vertices on each PE such that the number of edges are almost evenly + * distributed + */ + std::ifstream in(filename, std::ios::binary | std::ios::in); + in.exceptions(std::ios_base::failbit | std::ios_base::badbit); + ULONG from = calculateFromNode(in, n, m, rank, size); // inclusive! + ULONG to = calculateToNode(in, n, m, rank, size); // inclusive! + ULONG numberOfLocalNodes = to - from + 1; + ULONG numberOfLocalEdges = readNumberOfEdgesInRange(in, n, from, to); + + std::cout << "peID=" << rank << ": from=" << from << ", to=" << to << ", numberOfLocalNodes=" + << numberOfLocalNodes << ", numberOfLocalEdges=" << numberOfLocalEdges << std::endl; + + in.close(); + + permutation.resize(numberOfLocalEdges); + std::iota(permutation.begin(), permutation.end(), 0); + + // to construct the vertex range array, send 'from' to all other PEs + std::vector nodeRanges(static_cast(size + 1)); + MPI_Allgather(&from, 1, MPI_UNSIGNED_LONG_LONG, &nodeRanges[0], 1, MPI_UNSIGNED_LONG_LONG, MPI_COMM_WORLD); + nodeRanges[size] = n; + + /* + * the last part, i.e. loading the graph, is the same as in parallel_graph_io::readGraphBinary() + */ + PEID windowSize = std::min(size, config.binary_io_window_size); + PEID lowPE = 0; + PEID highPE = windowSize; + + while (lowPE < size) { + if (rank >= lowPE && rank < highPE) { + std::ifstream in(filename, std::ios::binary | std::ios::in); + in.exceptions(std::ios_base::failbit | std::ios_base::badbit); + + // extract splitters for split graph construction + std::vector edgeRanges(static_cast(size + 1)); + for (PEID pe = 0; pe < size; ++pe) { + ULONG peFrom = nodeRanges[pe]; // inclusive + ULONG peTo = nodeRanges[pe + 1]; // exclusive + ULONG peLocalNodes = peTo - peFrom; + + ULONG peStartPos = (HEADER_SIZE + peFrom) * sizeof(ULONG); + NodeID peFirstNodeOffset, peLastNodeOffset; + + in.seekg(peStartPos); + in.read((char *) &peFirstNodeOffset, sizeof(ULONG)); + in.seekg(peStartPos + peLocalNodes * sizeof(ULONG)); + in.read((char *) &peLastNodeOffset, sizeof(ULONG)); + + EdgeID peLocalEdges = (peLastNodeOffset - peFirstNodeOffset) / sizeof(ULONG); + edgeRanges[pe + 1] = edgeRanges[pe] + peLocalEdges; + } + + // load and construction, just like parallel_graph_io::readGraphBinary() + ULONG startPos = (HEADER_SIZE + from) * sizeof(ULONG); + NodeID *vertexOffsets = new NodeID[numberOfLocalNodes + 1]; + in.seekg(startPos); + in.read((char *) vertexOffsets, static_cast((numberOfLocalNodes + 1) * sizeof(ULONG))); + + ULONG edgeStartPos = vertexOffsets[0]; + EdgeID numReads = vertexOffsets[numberOfLocalNodes] - vertexOffsets[0]; + EdgeID numEdgesToRead = numReads / sizeof(ULONG); + EdgeID *edges = new EdgeID[numEdgesToRead]; + in.seekg(edgeStartPos); + in.read((char *) edges, static_cast(numEdgesToRead * sizeof(ULONG))); + + G.start_construction(numberOfLocalNodes, numberOfLocalEdges, n, m); + G.set_range(from, to); + G.set_range_array(nodeRanges); + G.set_edge_range_array(edgeRanges); + + ULONG pos = 0; + for (NodeID i = 0; i < numberOfLocalNodes; ++i) { + NodeID node = G.new_node(); + G.setNodeWeight(node, 1); + G.setNodeLabel(node, from + node); + G.setSecondPartitionIndex(node, 0); + + NodeID degree = (vertexOffsets[i + 1] - vertexOffsets[i]) / sizeof(ULONG); + + std::sort(permutation.begin() + pos, permutation.begin() + pos + degree, [&](EdgeID a, EdgeID b) { + return edges[a] < edges[b]; + }); + std::sort(edges + pos, edges + pos + degree); + + for (ULONG j = 0; j < degree; ++j, ++pos) { + NodeID target = edges[pos]; + EdgeID edge = G.new_edge(node, target); + G.setEdgeWeight(edge, 1); + } + } - MPI_Bcast(&header[0], header.size(), MPI_UNSIGNED_LONG_LONG, ROOT, MPI_COMM_WORLD); - ULONG version = header[0]; - ULONG n = header[1]; - ULONG m = header[2]; - - if (rank == ROOT) { - std::cout << "n=" << n << ", m=" << m << std::endl; - } + G.finish_construction(); + in.close(); - if (version != FILE_TYPE_VERSION) { - throw std::ios_base::failure("wrong file type version"); + delete[] edges; + delete[] vertexOffsets; } - /* - * next, we determine the number of vertices on each PE such that the number of edges are almost evenly - * distributed - */ - std::ifstream in(filename, std::ios::binary | std::ios::in); - in.exceptions(std::ios_base::failbit | std::ios_base::badbit); - ULONG from = calculateFromNode(in, n, m, rank, size); // inclusive! - ULONG to = calculateToNode(in, n, m, rank, size); // inclusive! - ULONG numberOfLocalNodes = to - from + 1; - ULONG numberOfLocalEdges = readNumberOfEdgesInRange(in, n, from, to); - - std::cout << "peID=" << rank << ": from=" << from << ", to=" << to << ", numberOfLocalNodes=" - << numberOfLocalNodes << ", numberOfLocalEdges=" << numberOfLocalEdges << std::endl; - - in.close(); - - permutation.resize(numberOfLocalEdges); - std::iota(permutation.begin(), permutation.end(), 0); - - // to construct the vertex range array, send 'from' to all other PEs - std::vector nodeRanges(static_cast(size + 1)); - MPI_Allgather(&from, 1, MPI_UNSIGNED_LONG_LONG, &nodeRanges[0], 1, MPI_UNSIGNED_LONG_LONG, MPI_COMM_WORLD); - nodeRanges[size] = n; - - /* - * the last part, i.e. loading the graph, is the same as in parallel_graph_io::readGraphBinary() - */ - PEID windowSize = std::min(size, config.binary_io_window_size); - PEID lowPE = 0; - PEID highPE = windowSize; - - while (lowPE < size) { - if (rank >= lowPE && rank < highPE) { - std::ifstream in(filename, std::ios::binary | std::ios::in); - in.exceptions(std::ios_base::failbit | std::ios_base::badbit); - - // extract splitters for split graph construction - std::vector edgeRanges(static_cast(size + 1)); - for (PEID pe = 0; pe < size; ++pe) { - ULONG peFrom = nodeRanges[pe]; // inclusive - ULONG peTo = nodeRanges[pe + 1]; // exclusive - ULONG peLocalNodes = peTo - peFrom; - - ULONG peStartPos = (HEADER_SIZE + peFrom) * sizeof(ULONG); - NodeID peFirstNodeOffset, peLastNodeOffset; - - in.seekg(peStartPos); - in.read((char *) &peFirstNodeOffset, sizeof(ULONG)); - in.seekg(peStartPos + peLocalNodes * sizeof(ULONG)); - in.read((char *) &peLastNodeOffset, sizeof(ULONG)); - - EdgeID peLocalEdges = (peLastNodeOffset - peFirstNodeOffset) / sizeof(ULONG); - edgeRanges[pe + 1] = edgeRanges[pe] + peLocalEdges; - } - - // load and construction, just like parallel_graph_io::readGraphBinary() - ULONG startPos = (HEADER_SIZE + from) * sizeof(ULONG); - NodeID *vertexOffsets = new NodeID[numberOfLocalNodes + 1]; - in.seekg(startPos); - in.read((char *) vertexOffsets, static_cast((numberOfLocalNodes + 1) * sizeof(ULONG))); - - ULONG edgeStartPos = vertexOffsets[0]; - EdgeID numReads = vertexOffsets[numberOfLocalNodes] - vertexOffsets[0]; - EdgeID numEdgesToRead = numReads / sizeof(ULONG); - EdgeID *edges = new EdgeID[numEdgesToRead]; - in.seekg(edgeStartPos); - in.read((char *) edges, static_cast(numEdgesToRead * sizeof(ULONG))); - - G.start_construction(numberOfLocalNodes, numberOfLocalEdges, n, m); - G.set_range(from, to); - G.set_range_array(nodeRanges); - G.set_edge_range_array(edgeRanges); - - ULONG pos = 0; - for (NodeID i = 0; i < numberOfLocalNodes; ++i) { - NodeID node = G.new_node(); - G.setNodeWeight(node, 1); - G.setNodeLabel(node, from + node); - G.setSecondPartitionIndex(node, 0); - - NodeID degree = (vertexOffsets[i + 1] - vertexOffsets[i]) / sizeof(ULONG); - - std::sort(permutation.begin() + pos, permutation.begin() + pos + degree, [&](EdgeID a, EdgeID b) { - return edges[a] < edges[b]; - }); - std::sort(edges + pos, edges + pos + degree); - - for (ULONG j = 0; j < degree; ++j, ++pos) { - NodeID target = edges[pos]; - EdgeID edge = G.new_edge(node, target); - G.setEdgeWeight(edge, 1); - } - } - - G.finish_construction(); - in.close(); - - delete[] edges; - delete[] vertexOffsets; - } - - lowPE += windowSize; - highPE += windowSize; - MPI_Barrier(MPI_COMM_WORLD); - } + lowPE += windowSize; + highPE += windowSize; + MPI_Barrier(MPI_COMM_WORLD); + } } /** @@ -185,61 +185,61 @@ void edge_balanced_graph_io::read_binary_graph_edge_balanced(parallel_graph_acce * that one. */ static ULONG calculateFromNode(std::ifstream &in, ULONG numberOfNodes, ULONG numberOfEdges, int rank, int size) { - if (rank == 0) { - return 0; - } - if (rank == size) { - return numberOfNodes; + if (rank == 0) { + return 0; + } + if (rank == size) { + return numberOfNodes; + } + + // calculate the number of edges that should come before the first edge on this PE + ULONG chunk = numberOfEdges / size; + ULONG remainder = numberOfEdges % size; + ULONG target = rank * chunk + std::min(static_cast(rank), remainder); + + // find the first node that is incident to an edge greater than target + // this should be the first edge on this PE + // a.first = node id + // a.second = first edge id + std::pair a{0, 0}; + std::pair b{numberOfNodes - 1, numberOfEdges - 1}; + + while (b.first - a.first > 1) { + std::pair mid; + mid.first = (a.first + b.first) / 2; + mid.second = readFirstEdge(in, numberOfNodes, mid.first); + + if (mid.second < target) { + a = mid; + } else { + b = mid; } - // calculate the number of edges that should come before the first edge on this PE - ULONG chunk = numberOfEdges / size; - ULONG remainder = numberOfEdges % size; - ULONG target = rank * chunk + std::min(static_cast(rank), remainder); - - // find the first node that is incident to an edge greater than target - // this should be the first edge on this PE - // a.first = node id - // a.second = first edge id - std::pair a{0, 0}; - std::pair b{numberOfNodes - 1, numberOfEdges - 1}; - - while (b.first - a.first > 1) { - std::pair mid; - mid.first = (a.first + b.first) / 2; - mid.second = readFirstEdge(in, numberOfNodes, mid.first); - - if (mid.second < target) { - a = mid; - } else { - b = mid; - } + assert(b.first >= a.first); + } - assert(b.first >= a.first); - } - - assert(a.second <= target && target <= b.second); - assert(b.first < numberOfNodes); - return b.first; + assert(a.second <= target && target <= b.second); + assert(b.first < numberOfNodes); + return b.first; } /** * Same as calculateFromNode(), but calculates that last node that should be on the given PE. */ static ULONG calculateToNode(std::ifstream &in, ULONG numberOfNodes, ULONG numberOfEdges, int rank, int size) { - return calculateFromNode(in, numberOfNodes, numberOfEdges, rank + 1, size) - 1; + return calculateFromNode(in, numberOfNodes, numberOfEdges, rank + 1, size) - 1; } /** * Determines the number of edges that are incident to nodes in [from, to]. */ static ULONG readNumberOfEdgesInRange(std::ifstream &in, ULONG numberOfNodes, ULONG from, ULONG to) { - if (from == to + 1) { - return 0; - } - ULONG firstEdge = readFirstEdge(in, numberOfNodes, from); - ULONG firstInvalidEdge = readFirstInvalidEdge(in, numberOfNodes, to); - return firstInvalidEdge - firstEdge; + if (from == to + 1) { + return 0; + } + ULONG firstEdge = readFirstEdge(in, numberOfNodes, from); + ULONG firstInvalidEdge = readFirstInvalidEdge(in, numberOfNodes, to); + return firstInvalidEdge - firstEdge; } @@ -247,26 +247,27 @@ static ULONG readNumberOfEdgesInRange(std::ifstream &in, ULONG numberOfNodes, UL * Reads G.get_first_edge(NodeID) from a binary graph file. */ static ULONG readFirstEdge(std::ifstream &in, ULONG numberOfNodes, ULONG node) { - assert(node <= numberOfNodes); + assert(node <= numberOfNodes); - ULONG pos = (HEADER_SIZE + node) * sizeof(ULONG); - in.seekg(pos); + ULONG pos = (HEADER_SIZE + node) * sizeof(ULONG); + in.seekg(pos); - ULONG entry = 0; - in.read((char *) (&entry), sizeof(ULONG)); - return adjacencyListOffsetToEdgeID(numberOfNodes, entry); + ULONG entry = 0; + in.read((char *) (&entry), sizeof(ULONG)); + return adjacencyListOffsetToEdgeID(numberOfNodes, entry); } /** * Translates a vertex offset read from a binary graph file to an EdgeID. */ static ULONG adjacencyListOffsetToEdgeID(ULONG numberOfNodes, ULONG offset) { - return (offset / sizeof(ULONG)) - HEADER_SIZE - (numberOfNodes + 1); + return (offset / sizeof(ULONG)) - HEADER_SIZE - (numberOfNodes + 1); } /** * Reads G.get_first_invalid_edge(NodeID) from a binary graph file. */ static ULONG readFirstInvalidEdge(std::ifstream &in, ULONG numberOfNodes, ULONG node) { - return readFirstEdge(in, numberOfNodes, node + 1); + return readFirstEdge(in, numberOfNodes, node + 1); } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/dspac/edge_balanced_graph_io.h b/parallel/parallel_src/lib/dspac/edge_balanced_graph_io.h index 62222cb0..76198fad 100644 --- a/parallel/parallel_src/lib/dspac/edge_balanced_graph_io.h +++ b/parallel/parallel_src/lib/dspac/edge_balanced_graph_io.h @@ -14,14 +14,14 @@ #include "definitions.h" #include "data_structure/parallel_graph_access.h" #include "partition_config.h" - +namespace parhip { class edge_balanced_graph_io { public: - static void read_binary_graph_edge_balanced(parallel_graph_access &G, const std::string &filename, - const PPartitionConfig &config, std::vector &permutation, int rank, int size); + static void read_binary_graph_edge_balanced(parallel_graph_access &G, const std::string &filename, + const PPartitionConfig &config, std::vector &permutation, int rank, int size); - static void read_binary_graph_edge_balanced(parallel_graph_access &G, const std::string &filename, - const PPartitionConfig &config, std::vector &permutation); + static void read_binary_graph_edge_balanced(parallel_graph_access &G, const std::string &filename, + const PPartitionConfig &config, std::vector &permutation); }; - +} #endif // KAHIP_EDGEBALANCED_GRAPH_IO_H diff --git a/parallel/parallel_src/lib/io/parallel_graph_io.cpp b/parallel/parallel_src/lib/io/parallel_graph_io.cpp index 5d945867..9719532f 100644 --- a/parallel/parallel_src/lib/io/parallel_graph_io.cpp +++ b/parallel/parallel_src/lib/io/parallel_graph_io.cpp @@ -19,7 +19,7 @@ #include "parallel_graph_io.h" #include "tools/helpers.h" - +namespace parhip { const ULONG fileTypeVersionNumber = 3; const ULONG header_count = 3; @@ -37,848 +37,848 @@ int parallel_graph_io::readGraphWeighted(PPartitionConfig & config, std::string filename, PEID peID, PEID comm_size, MPI_Comm communicator) { - std::string metis_ending(".graph"); - std::string bin_ending(".bgf"); - - if( hasEnding(filename, metis_ending) ) { - std::stringstream ss; - ss << filename << bin_ending; - if(file_exists(ss.str())) { - return readGraphBinary(config, G, ss.str(), peID, comm_size, communicator); - } else { - return readGraphWeightedFlexible(G, filename, peID, comm_size, communicator); - } - } + std::string metis_ending(".graph"); + std::string bin_ending(".bgf"); + + if( hasEnding(filename, metis_ending) ) { + std::stringstream ss; + ss << filename << bin_ending; + if(file_exists(ss.str())) { + return readGraphBinary(config, G, ss.str(), peID, comm_size, communicator); + } else { + return readGraphWeightedFlexible(G, filename, peID, comm_size, communicator); + } + } + + if( hasEnding(filename, bin_ending) ) { + return readGraphBinary(config, G, filename, peID, comm_size, communicator); + } + + //non of both is true -- try metis format + return readGraphWeightedFlexible(G, filename, peID, comm_size, communicator); +} - if( hasEnding(filename, bin_ending) ) { - return readGraphBinary(config, G, filename, peID, comm_size, communicator); +int parallel_graph_io::readGraphWeightedFlexible(parallel_graph_access & G, + std::string filename, + PEID peID, PEID comm_size, MPI_Comm communicator) { + std::string line; + + // open file for reading + std::ifstream in(filename.c_str()); + if (!in) { + std::cerr << "Error opening " << filename << std::endl; + return 1; + } + + NodeID nmbNodes; + EdgeID nmbEdges; + + std::getline(in,line); + //skip comments + while( line[0] == '%' ) { + std::getline(in, line); + } + + int ew = 0; + std::stringstream ss(line); + ss >> nmbNodes; + ss >> nmbEdges; + ss >> ew; + + if(ew != 0) { + in.close(); + if(peID == 0) std::cout << "graph is weighted --> using a different IO routine" << std::endl; + return readGraphWeightedMETIS_fixed(G, filename, peID, comm_size, communicator); + } + + // pe p reads the lines p*ceil(n/size) to (p+1)floor(n/size) lines of that file + ULONG from = peID * ceil(nmbNodes / (double)comm_size); + ULONG to = (peID+1) * ceil(nmbNodes / (double)comm_size) - 1; + to = std::min(to, nmbNodes-1); + + ULONG local_no_nodes = to - from + 1; + PRINT(std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl;); + + std::vector< std::vector< NodeID > > local_edge_lists; + local_edge_lists.resize(local_no_nodes); + + ULONG counter = 0; + NodeID node_counter = 0; + EdgeID edge_counter = 0; + + char *oldstr, *newstr; + while( std::getline(in, line) ) { + if( counter > to ) { + break; + } + if (line[0] == '%') { // a comment in the file + continue; + } + + if( counter >= from ) { + oldstr = &line[0]; + newstr = 0; + + for (;;) { + NodeID target; + target = (NodeID) strtol(oldstr, &newstr, 10); + + if (target == 0) { + break; } - //non of both is true -- try metis format - return readGraphWeightedFlexible(G, filename, peID, comm_size, communicator); -} + oldstr = newstr; -int parallel_graph_io::readGraphWeightedFlexible(parallel_graph_access & G, - std::string filename, - PEID peID, PEID comm_size, MPI_Comm communicator) { - std::string line; + local_edge_lists[node_counter].push_back(target); + edge_counter++; - // open file for reading - std::ifstream in(filename.c_str()); - if (!in) { - std::cerr << "Error opening " << filename << std::endl; - return 1; - } + } - NodeID nmbNodes; - EdgeID nmbEdges; + node_counter++; + } - std::getline(in,line); - //skip comments - while( line[0] == '%' ) { - std::getline(in, line); - } + counter++; - int ew = 0; - std::stringstream ss(line); - ss >> nmbNodes; - ss >> nmbEdges; - ss >> ew; + if( in.eof() ) { + break; + } + } - if(ew != 0) { - in.close(); - if(peID == 0) std::cout << "graph is weighted --> using a different IO routine" << std::endl; - return readGraphWeightedMETIS_fixed(G, filename, peID, comm_size, communicator); - } - - // pe p reads the lines p*ceil(n/size) to (p+1)floor(n/size) lines of that file - ULONG from = peID * ceil(nmbNodes / (double)comm_size); - ULONG to = (peID+1) * ceil(nmbNodes / (double)comm_size) - 1; - to = std::min(to, nmbNodes-1); - - ULONG local_no_nodes = to - from + 1; - PRINT(std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl;); - - std::vector< std::vector< NodeID > > local_edge_lists; - local_edge_lists.resize(local_no_nodes); - - ULONG counter = 0; - NodeID node_counter = 0; - EdgeID edge_counter = 0; - - char *oldstr, *newstr; - while( std::getline(in, line) ) { - if( counter > to ) { - break; - } - if (line[0] == '%') { // a comment in the file - continue; - } - - if( counter >= from ) { - oldstr = &line[0]; - newstr = 0; - - for (;;) { - NodeID target; - target = (NodeID) strtol(oldstr, &newstr, 10); - - if (target == 0) { - break; - } - - oldstr = newstr; - - local_edge_lists[node_counter].push_back(target); - edge_counter++; - - } - - node_counter++; - } - - counter++; - - if( in.eof() ) { - break; - } - } + MPI_Barrier(communicator); - MPI_Barrier(communicator); - - G.start_construction(local_no_nodes, 2*edge_counter, nmbNodes, 2*nmbEdges); - G.set_range(from, to); + G.start_construction(local_no_nodes, 2*edge_counter, nmbNodes, 2*nmbEdges); + G.set_range(from, to); - std::vector< NodeID > vertex_dist( comm_size+1, 0 ); - for( PEID peID = 0; peID <= comm_size; peID++) { - vertex_dist[peID] = peID * ceil(nmbNodes / (double)comm_size); // from positions - } - G.set_range_array(vertex_dist); - - for (NodeID i = 0; i < local_no_nodes; ++i) { - NodeID node = G.new_node(); - G.setNodeWeight(node, 1); - G.setNodeLabel(node, from+node); - G.setSecondPartitionIndex(node, 0); - - for( ULONG j = 0; j < local_edge_lists[i].size(); j++) { - NodeID target = local_edge_lists[i][j]-1; // -1 since there are no nodes with id 0 in the file - EdgeID e = G.new_edge(node, target); - G.setEdgeWeight(e, 1); - } - } + std::vector< NodeID > vertex_dist( comm_size+1, 0 ); + for( PEID peID = 0; peID <= comm_size; peID++) { + vertex_dist[peID] = peID * ceil(nmbNodes / (double)comm_size); // from positions + } + G.set_range_array(vertex_dist); + + for (NodeID i = 0; i < local_no_nodes; ++i) { + NodeID node = G.new_node(); + G.setNodeWeight(node, 1); + G.setNodeLabel(node, from+node); + G.setSecondPartitionIndex(node, 0); + + for( ULONG j = 0; j < local_edge_lists[i].size(); j++) { + NodeID target = local_edge_lists[i][j]-1; // -1 since there are no nodes with id 0 in the file + EdgeID e = G.new_edge(node, target); + G.setEdgeWeight(e, 1); + } + } - G.finish_construction(); - MPI_Barrier(communicator); + G.finish_construction(); + MPI_Barrier(communicator); - return 0; + return 0; } -//int parallel_graph_io::readGraphWeightedMETISFast(parallel_graph_access & G, - //std::string filename, - //PEID peID, PEID comm_size, MPI_Comm communicator) { - //std::string line; - - //// open file for reading - //std::ifstream in(filename.c_str()); - //if (!in) { - //std::cerr << "Error opening " << filename << std::endl; - //return 1; - //} - - //NodeID nmbNodes; - //EdgeID nmbEdges; - - //std::getline(in,line); - ////skip comments - //while( line[0] == '%' ) { - //std::getline(in, line); - //} - - //int ew = 0; - //std::stringstream ss(line); - //ss >> nmbNodes; - //ss >> nmbEdges; - //ss >> ew; - - //// pe p reads the lines p*ceil(n/size) to (p+1)floor(n/size) lines of that file - //ULONG from = peID * ceil(nmbNodes / (double)comm_size); - //ULONG to = (peID+1) * ceil(nmbNodes / (double)comm_size) - 1; - //to = std::min(to, nmbNodes-1); - - //ULONG local_no_nodes = to - from + 1; - //std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl; - - //std::vector< std::vector< NodeID > > local_edge_lists; - //local_edge_lists.resize(local_no_nodes); - - //ULONG counter = 0; - //NodeID node_counter = 0; - //EdgeID edge_counter = 0; - - //char *oldstr, *newstr; - //while( std::getline(in, line) ) { - //if( counter > to ) { - //break; - //} - //if (line[0] == '%') { // a comment in the file - //continue; - //} - - //if( counter >= from ) { - //oldstr = &line[0]; - //newstr = 0; - - //for (;;) { - //NodeID target; - //target = (NodeID) strtol(oldstr, &newstr, 10); - - //if (target == 0) { - //break; - //} - - //oldstr = newstr; - - //local_edge_lists[node_counter].push_back(target); - //edge_counter++; - - //} - - //node_counter++; - //} - - //counter++; - - //if( in.eof() ) { - //break; - //} - //} - - //MPI_Barrier(communicator); - - //G.start_construction(local_no_nodes, 2*edge_counter, nmbNodes, 2*nmbEdges); - //G.set_range(from, to); - - //for (NodeID i = 0; i < local_no_nodes; ++i) { - //NodeID node = G.new_node(); - //G.setNodeWeight(node, 1); - //G.setNodeLabel(node, from+node); - //G.setSecondPartitionIndex(node, 0); - - //for( ULONG j = 0; j < local_edge_lists[i].size(); j++) { - //NodeID target = local_edge_lists[i][j]-1; // -1 since there are no nodes with id 0 in the file - //EdgeID e = G.new_edge(node, target); - //G.setEdgeWeight(e, 1); - //} - //} - - //G.finish_construction(); - //MPI_Barrier(communicator); - //return 0; +//int parallel_graph_io::readGraphWeightedMETISFast(parallel_graph_access & G, +//std::string filename, +//PEID peID, PEID comm_size, MPI_Comm communicator) { +//std::string line; + +//// open file for reading +//std::ifstream in(filename.c_str()); +//if (!in) { +//std::cerr << "Error opening " << filename << std::endl; +//return 1; //} -// we start with the simplest version of IO -// where each process reads the graph sequentially -// -//int parallel_graph_io::readGraphWeightedMETIS(parallel_graph_access & G, - //std::string filename, - //PEID peID, PEID comm_size, MPI_Comm communicator) { - //std::string line; - - //// open file for reading - //std::ifstream in(filename.c_str()); - //if (!in) { - //std::cerr << "Error opening " << filename << std::endl; - //return 1; - //} - - //NodeID nmbNodes; - //EdgeID nmbEdges; - - //std::getline(in,line); - ////skip comments - //while( line[0] == '%' ) { - //std::getline(in, line); - //} - - //int ew = 0; - //std::stringstream ss(line); - //ss >> nmbNodes; - //ss >> nmbEdges; - //ss >> ew; - - //if(ew == 1) { - //std::cout << "io of weighted graphs not supported yet" << std::endl; - //exit(0); - //} else if (ew == 11) { - //std::cout << "io of weighted graphs not supported yet" << std::endl; - //exit(0); - //} else if (ew == 10) { - //std::cout << "io of weighted graphs not supported yet" << std::endl; - //exit(0); - //} - - //// pe p reads the lines p*ceil(n/size) to (p+1)floor(n/size) lines of that file - //ULONG from = peID * ceil(nmbNodes / (double)comm_size); - //ULONG to = (peID+1) * ceil(nmbNodes / (double)comm_size) - 1; - //to = std::min(to, nmbNodes-1); - - //ULONG local_no_nodes = to - from + 1; - //std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl; - - //std::vector< std::vector< NodeID > > local_edge_lists; - //local_edge_lists.resize(local_no_nodes); - - - ////std::getline(in, line); - //ULONG counter = 0; - //NodeID node_counter = 0; - //EdgeID edge_counter = 0; - - //while( std::getline(in, line) ) { - //if( counter > to ) { - //break; - //} - //if (line[0] == '%') { // a comment in the file - //continue; - //} - - //if( counter >= from ) { - //std::stringstream ss(line); - - //NodeID target; - //while( ss >> target ) { - //local_edge_lists[node_counter].push_back(target); - //edge_counter++; - //} - //node_counter++; - //} - - //counter++; - - //if( in.eof() ) { - //break; - //} - //} - - //MPI_Barrier(communicator); - - //G.start_construction(local_no_nodes, 2*edge_counter, nmbNodes, 2*nmbEdges); - //G.set_range(from, to); - - //for (NodeID i = 0; i < local_no_nodes; ++i) { - //NodeID node = G.new_node(); - //G.setNodeWeight(node, 1); - //G.setNodeLabel(node, from+node); - //G.setSecondPartitionIndex(node, 0); - - //for( ULONG j = 0; j < local_edge_lists[i].size(); j++) { - //NodeID target = local_edge_lists[i][j]-1; // -1 since there are no nodes with id 0 in the file - //EdgeID e = G.new_edge(node, target); - //G.setEdgeWeight(e, 1); - //} - //} - - //G.finish_construction(); - //return 0; + +//NodeID nmbNodes; +//EdgeID nmbEdges; + +//std::getline(in,line); +////skip comments +//while( line[0] == '%' ) { +//std::getline(in, line); //} -int parallel_graph_io::writeGraphExternallyBinary(std::string input_filename, std::string output_filename) { +//int ew = 0; +//std::stringstream ss(line); +//ss >> nmbNodes; +//ss >> nmbEdges; +//ss >> ew; - std::string line; +//// pe p reads the lines p*ceil(n/size) to (p+1)floor(n/size) lines of that file +//ULONG from = peID * ceil(nmbNodes / (double)comm_size); +//ULONG to = (peID+1) * ceil(nmbNodes / (double)comm_size) - 1; +//to = std::min(to, nmbNodes-1); - // open file for reading - std::ifstream in(input_filename.c_str()); - if (!in) { - std::cerr << "Error opening " << input_filename << std::endl; - return 1; - } +//ULONG local_no_nodes = to - from + 1; +//std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl; - NodeID n; - EdgeID m; +//std::vector< std::vector< NodeID > > local_edge_lists; +//local_edge_lists.resize(local_no_nodes); - std::getline(in,line); - //skip comments - while( line[0] == '%' ) { - std::getline(in, line); - } +//ULONG counter = 0; +//NodeID node_counter = 0; +//EdgeID edge_counter = 0; - int ew = 0; - std::stringstream ss(line); - ss >> n; - ss >> m; - ss >> ew; - - m *= 2; - - std::ofstream outfile; - outfile.open(output_filename.c_str(), std::ios::binary | std::ios::out); - outfile.write((char*)(&fileTypeVersionNumber), sizeof( ULONG )); - outfile.write((char*)(&n), sizeof( ULONG )); - outfile.write((char*)(&m), sizeof( ULONG )); - - NodeID offset = (header_count + n + 1) * (sizeof(ULONG)); - - while( std::getline(in, line) ) { - if (line[0] == '%') { // a comment in the file - continue; - } - - std::stringstream ss(line); - - EdgeID edge_counter = 0; - NodeID target; - while( ss >> target ) { - edge_counter++; - } - outfile.write((char*)(&offset), sizeof( ULONG )); - offset += edge_counter*sizeof( ULONG ); - - if( in.eof() ) { - break; - } - } - outfile.write((char*)(&offset), sizeof( ULONG )); - in.close(); - - // second stream to actually write the edges - std::ifstream second_in(input_filename.c_str()); - std::getline(second_in,line); - //skip comments - while( line[0] == '%' ) { - std::getline(second_in, line); - } +//char *oldstr, *newstr; +//while( std::getline(in, line) ) { +//if( counter > to ) { +//break; +//} +//if (line[0] == '%') { // a comment in the file +//continue; +//} - while( std::getline(second_in, line) ) { - if (line[0] == '%') { // a comment in the file - continue; - } +//if( counter >= from ) { +//oldstr = &line[0]; +//newstr = 0; - std::stringstream ss(line); +//for (;;) { +//NodeID target; +//target = (NodeID) strtol(oldstr, &newstr, 10); - NodeID target; - while( ss >> target ) { - target -= 1; - outfile.write((char*)(&target), sizeof( ULONG )); - } +//if (target == 0) { +//break; +//} - if( second_in.eof() ) { - break; - } - } - second_in.close(); - - return 0; +//oldstr = newstr; -} +//local_edge_lists[node_counter].push_back(target); +//edge_counter++; -int parallel_graph_io::writeGraphSequentiallyBinary(complete_graph_access & G, std::string filename) { +//} - std::ofstream outfile; - outfile.open(filename.c_str(), std::ios::binary | std::ios::out); - PEID size; MPI_Comm_size( MPI_COMM_WORLD, &size); - - if( size > 1 ) { - std::cout << "currently only one process supported." << std::endl; - return 0; - } +//node_counter++; +//} - std::cout << "Writing graph " << filename << std::endl; - printf("Writing graph with n = %lld, m = %lld\n", G.number_of_global_nodes(), G.number_of_global_edges()); +//counter++; - //write version number - outfile.write((char*)(&fileTypeVersionNumber), sizeof( ULONG )); +//if( in.eof() ) { +//break; +//} +//} - //write number of nodes etc - NodeID n = G.number_of_global_nodes(); - NodeID m = G.number_of_global_edges(); +//MPI_Barrier(communicator); - outfile.write((char*)(&n), sizeof( ULONG )); - outfile.write((char*)(&m), sizeof( ULONG )); +//G.start_construction(local_no_nodes, 2*edge_counter, nmbNodes, 2*nmbEdges); +//G.set_range(from, to); - NodeID * offset_array = new NodeID[n+1]; - ULONG pos = 0; - NodeID offset = (header_count + G.number_of_global_nodes() + 1) * (sizeof(ULONG)); +//for (NodeID i = 0; i < local_no_nodes; ++i) { +//NodeID node = G.new_node(); +//G.setNodeWeight(node, 1); +//G.setNodeLabel(node, from+node); +//G.setSecondPartitionIndex(node, 0); - forall_local_nodes(G, node) { - offset_array[pos++] = offset; - offset += G.getNodeDegree(node) * (sizeof(ULONG)); - } endfor +//for( ULONG j = 0; j < local_edge_lists[i].size(); j++) { +//NodeID target = local_edge_lists[i][j]-1; // -1 since there are no nodes with id 0 in the file +//EdgeID e = G.new_edge(node, target); +//G.setEdgeWeight(e, 1); +//} +//} - offset_array[pos] = offset; - outfile.write((char*)(offset_array), (n+1)*sizeof(ULONG)); - delete[] offset_array; +//G.finish_construction(); +//MPI_Barrier(communicator); +//return 0; +//} +// we start with the simplest version of IO +// where each process reads the graph sequentially +// +//int parallel_graph_io::readGraphWeightedMETIS(parallel_graph_access & G, +//std::string filename, +//PEID peID, PEID comm_size, MPI_Comm communicator) { +//std::string line; + +//// open file for reading +//std::ifstream in(filename.c_str()); +//if (!in) { +//std::cerr << "Error opening " << filename << std::endl; +//return 1; +//} - NodeID * edge_array = new NodeID[m]; - pos = 0; +//NodeID nmbNodes; +//EdgeID nmbEdges; - // now write the edges - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - edge_array[pos++] = target; - } endfor - } endfor - outfile.write((char*)(edge_array), (m)*sizeof(ULONG)); - outfile.close(); - delete[] edge_array; +//std::getline(in,line); +////skip comments +//while( line[0] == '%' ) { +//std::getline(in, line); +//} + +//int ew = 0; +//std::stringstream ss(line); +//ss >> nmbNodes; +//ss >> nmbEdges; +//ss >> ew; + +//if(ew == 1) { +//std::cout << "io of weighted graphs not supported yet" << std::endl; +//exit(0); +//} else if (ew == 11) { +//std::cout << "io of weighted graphs not supported yet" << std::endl; +//exit(0); +//} else if (ew == 10) { +//std::cout << "io of weighted graphs not supported yet" << std::endl; +//exit(0); +//} + +//// pe p reads the lines p*ceil(n/size) to (p+1)floor(n/size) lines of that file +//ULONG from = peID * ceil(nmbNodes / (double)comm_size); +//ULONG to = (peID+1) * ceil(nmbNodes / (double)comm_size) - 1; +//to = std::min(to, nmbNodes-1); + +//ULONG local_no_nodes = to - from + 1; +//std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl; + +//std::vector< std::vector< NodeID > > local_edge_lists; +//local_edge_lists.resize(local_no_nodes); + + +////std::getline(in, line); +//ULONG counter = 0; +//NodeID node_counter = 0; +//EdgeID edge_counter = 0; + +//while( std::getline(in, line) ) { +//if( counter > to ) { +//break; +//} +//if (line[0] == '%') { // a comment in the file +//continue; +//} + +//if( counter >= from ) { +//std::stringstream ss(line); + +//NodeID target; +//while( ss >> target ) { +//local_edge_lists[node_counter].push_back(target); +//edge_counter++; +//} +//node_counter++; +//} + +//counter++; + +//if( in.eof() ) { +//break; +//} +//} + +//MPI_Barrier(communicator); + +//G.start_construction(local_no_nodes, 2*edge_counter, nmbNodes, 2*nmbEdges); +//G.set_range(from, to); + +//for (NodeID i = 0; i < local_no_nodes; ++i) { +//NodeID node = G.new_node(); +//G.setNodeWeight(node, 1); +//G.setNodeLabel(node, from+node); +//G.setSecondPartitionIndex(node, 0); + +//for( ULONG j = 0; j < local_edge_lists[i].size(); j++) { +//NodeID target = local_edge_lists[i][j]-1; // -1 since there are no nodes with id 0 in the file +//EdgeID e = G.new_edge(node, target); +//G.setEdgeWeight(e, 1); +//} +//} + +//G.finish_construction(); +//return 0; +//} + +int parallel_graph_io::writeGraphExternallyBinary(std::string input_filename, std::string output_filename) { + std::string line; + + // open file for reading + std::ifstream in(input_filename.c_str()); + if (!in) { + std::cerr << "Error opening " << input_filename << std::endl; + return 1; + } + + NodeID n; + EdgeID m; + + std::getline(in,line); + //skip comments + while( line[0] == '%' ) { + std::getline(in, line); + } + + int ew = 0; + std::stringstream ss(line); + ss >> n; + ss >> m; + ss >> ew; + + m *= 2; + + std::ofstream outfile; + outfile.open(output_filename.c_str(), std::ios::binary | std::ios::out); + outfile.write((char*)(&fileTypeVersionNumber), sizeof( ULONG )); + outfile.write((char*)(&n), sizeof( ULONG )); + outfile.write((char*)(&m), sizeof( ULONG )); + + NodeID offset = (header_count + n + 1) * (sizeof(ULONG)); + + while( std::getline(in, line) ) { + if (line[0] == '%') { // a comment in the file + continue; + } + + std::stringstream ss(line); + + EdgeID edge_counter = 0; + NodeID target; + while( ss >> target ) { + edge_counter++; + } + outfile.write((char*)(&offset), sizeof( ULONG )); + offset += edge_counter*sizeof( ULONG ); + + if( in.eof() ) { + break; + } + } + outfile.write((char*)(&offset), sizeof( ULONG )); + in.close(); + + // second stream to actually write the edges + std::ifstream second_in(input_filename.c_str()); + std::getline(second_in,line); + //skip comments + while( line[0] == '%' ) { + std::getline(second_in, line); + } + + while( std::getline(second_in, line) ) { + if (line[0] == '%') { // a comment in the file + continue; + } + + std::stringstream ss(line); + + NodeID target; + while( ss >> target ) { + target -= 1; + outfile.write((char*)(&target), sizeof( ULONG )); + } + + if( second_in.eof() ) { + break; + } + } + second_in.close(); + + return 0; - return 0; } -int parallel_graph_io::readGraphBinary(PPartitionConfig & config, parallel_graph_access & G, - std::string filename, - PEID peID, PEID size, MPI_Comm communicator) { - - // read header - std::vector< ULONG > buffer(3, 0); - int success = 0; - if( peID == ROOT) { - std::cout << "Reading binary graph ..." << std::endl; - std::ifstream file; - file.open(filename.c_str(), std::ios::binary | std::ios::in); - if(file) { - success = 1; - file.read((char*)(&buffer[0]), 3*sizeof(ULONG)); - } - file.close(); - } +int parallel_graph_io::writeGraphSequentiallyBinary(complete_graph_access & G, std::string filename) { - MPI_Bcast(&success, 1, MPI_INT, ROOT, communicator); + std::ofstream outfile; + outfile.open(filename.c_str(), std::ios::binary | std::ios::out); + PEID size; MPI_Comm_size( MPI_COMM_WORLD, &size); - if( !success ) { - if( peID == ROOT ) std::cout << "problem to open the file" << std::endl; - MPI_Finalize(); - exit(0); - } + if( size > 1 ) { + std::cout << "currently only one process supported." << std::endl; + return 0; + } - MPI_Bcast(&buffer[0], 3, MPI_LONG, ROOT, communicator); - ULONG version = buffer[0]; - NodeID n = buffer[1]; - NodeID m = buffer[2]; + std::cout << "Writing graph " << filename << std::endl; + printf("Writing graph with n = %lld, m = %lld\n", G.number_of_global_nodes(), G.number_of_global_edges()); - if(peID == ROOT) std::cout << "version: " << version << " n: "<< n << " m: " << m << std::endl; - if( version != fileTypeVersionNumber ) { - if(peID == ROOT) std::cout << "filetype version missmatch" << std::endl; - MPI_Finalize(); exit(0); - } + //write version number + outfile.write((char*)(&fileTypeVersionNumber), sizeof( ULONG )); - PEID window_size = std::min(config.binary_io_window_size, size); - PEID lowPE = 0; - PEID highPE = window_size; + //write number of nodes etc + NodeID n = G.number_of_global_nodes(); + NodeID m = G.number_of_global_edges(); - - while ( lowPE < size ) { - if( peID >= lowPE && peID < highPE ) { - std::ifstream file; - file.open(filename.c_str(), std::ios::binary | std::ios::in); - - ULONG from = peID * ceil(n / (double)size); - ULONG to = (peID +1) * ceil(n / (double)size) - 1; - to = std::min(to, n-1); - - ULONG local_no_nodes = to - from + 1; - std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl; - - // read the offsets - ULONG start_pos = (header_count + from)*(sizeof(ULONG)); - NodeID* vertex_offsets = new NodeID[local_no_nodes+1]; // we also need the next vertex offset - file.seekg(start_pos); - file.read((char*)(vertex_offsets), (local_no_nodes+1)*sizeof(ULONG)); - - ULONG edge_start_pos = vertex_offsets[0]; - EdgeID num_reads = vertex_offsets[local_no_nodes]-vertex_offsets[0]; - EdgeID num_edges_to_read = num_reads/sizeof(ULONG); - EdgeID* edges = new EdgeID[num_edges_to_read]; // we also need the next vertex offset - file.seekg(edge_start_pos); - file.read((char*)(edges), (num_edges_to_read)*sizeof(ULONG)); - - G.start_construction(local_no_nodes, num_edges_to_read, n, m); - G.set_range(from, to); - - std::vector< NodeID > vertex_dist( size+1, 0 ); - for( PEID peID = 0; peID <= size; peID++) { - vertex_dist[peID] = peID * ceil(n / (double)size); // from positions - } - G.set_range_array(vertex_dist); - - ULONG pos = 0; - for (NodeID i = 0; i < local_no_nodes; ++i) { - NodeID node = G.new_node(); - G.setNodeWeight(node, 1); - G.setNodeLabel(node, from+node); - G.setSecondPartitionIndex(node, 0); - - NodeID degree = (vertex_offsets[i+1] - vertex_offsets[i]) / sizeof(ULONG); - for( ULONG j = 0; j < degree; j++, pos++) { - NodeID target = edges[pos]; - EdgeID e = G.new_edge(node, target); - G.setEdgeWeight(e, 1); - } - } - - G.finish_construction(); - - delete[] vertex_offsets; - delete[] edges; - file.close(); - } - lowPE += window_size; - highPE += window_size; - MPI_Barrier(communicator); + outfile.write((char*)(&n), sizeof( ULONG )); + outfile.write((char*)(&m), sizeof( ULONG )); + + NodeID * offset_array = new NodeID[n+1]; + ULONG pos = 0; + NodeID offset = (header_count + G.number_of_global_nodes() + 1) * (sizeof(ULONG)); + + forall_local_nodes(G, node) { + offset_array[pos++] = offset; + offset += G.getNodeDegree(node) * (sizeof(ULONG)); + } endfor + + offset_array[pos] = offset; + outfile.write((char*)(offset_array), (n+1)*sizeof(ULONG)); + delete[] offset_array; + + NodeID * edge_array = new NodeID[m]; + pos = 0; + + // now write the edges + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + edge_array[pos++] = target; + } endfor +} endfor +outfile.write((char*)(edge_array), (m)*sizeof(ULONG)); + outfile.close(); + delete[] edge_array; + + + return 0; +} + +int parallel_graph_io::readGraphBinary(PPartitionConfig & config, parallel_graph_access & G, + std::string filename, + PEID peID, PEID size, MPI_Comm communicator) { + + // read header + std::vector< ULONG > buffer(3, 0); + int success = 0; + if( peID == ROOT) { + std::cout << "Reading binary graph ..." << std::endl; + std::ifstream file; + file.open(filename.c_str(), std::ios::binary | std::ios::in); + if(file) { + success = 1; + file.read((char*)(&buffer[0]), 3*sizeof(ULONG)); + } + file.close(); + } + + MPI_Bcast(&success, 1, MPI_INT, ROOT, communicator); + + if( !success ) { + if( peID == ROOT ) std::cout << "problem to open the file" << std::endl; + MPI_Finalize(); + exit(0); + } + + MPI_Bcast(&buffer[0], 3, MPI_LONG, ROOT, communicator); + ULONG version = buffer[0]; + NodeID n = buffer[1]; + NodeID m = buffer[2]; + + if(peID == ROOT) std::cout << "version: " << version << " n: "<< n << " m: " << m << std::endl; + if( version != fileTypeVersionNumber ) { + if(peID == ROOT) std::cout << "filetype version missmatch" << std::endl; + MPI_Finalize(); exit(0); + } + + PEID window_size = std::min(config.binary_io_window_size, size); + PEID lowPE = 0; + PEID highPE = window_size; + + + while ( lowPE < size ) { + if( peID >= lowPE && peID < highPE ) { + std::ifstream file; + file.open(filename.c_str(), std::ios::binary | std::ios::in); + + ULONG from = peID * ceil(n / (double)size); + ULONG to = (peID +1) * ceil(n / (double)size) - 1; + to = std::min(to, n-1); + + ULONG local_no_nodes = to - from + 1; + std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl; + + // read the offsets + ULONG start_pos = (header_count + from)*(sizeof(ULONG)); + NodeID* vertex_offsets = new NodeID[local_no_nodes+1]; // we also need the next vertex offset + file.seekg(start_pos); + file.read((char*)(vertex_offsets), (local_no_nodes+1)*sizeof(ULONG)); + + ULONG edge_start_pos = vertex_offsets[0]; + EdgeID num_reads = vertex_offsets[local_no_nodes]-vertex_offsets[0]; + EdgeID num_edges_to_read = num_reads/sizeof(ULONG); + EdgeID* edges = new EdgeID[num_edges_to_read]; // we also need the next vertex offset + file.seekg(edge_start_pos); + file.read((char*)(edges), (num_edges_to_read)*sizeof(ULONG)); + + G.start_construction(local_no_nodes, num_edges_to_read, n, m); + G.set_range(from, to); + + std::vector< NodeID > vertex_dist( size+1, 0 ); + for( PEID peID = 0; peID <= size; peID++) { + vertex_dist[peID] = peID * ceil(n / (double)size); // from positions + } + G.set_range_array(vertex_dist); + + ULONG pos = 0; + for (NodeID i = 0; i < local_no_nodes; ++i) { + NodeID node = G.new_node(); + G.setNodeWeight(node, 1); + G.setNodeLabel(node, from+node); + G.setSecondPartitionIndex(node, 0); + + NodeID degree = (vertex_offsets[i+1] - vertex_offsets[i]) / sizeof(ULONG); + for( ULONG j = 0; j < degree; j++, pos++) { + NodeID target = edges[pos]; + EdgeID e = G.new_edge(node, target); + G.setEdgeWeight(e, 1); } - - return 0; + } + + G.finish_construction(); + + delete[] vertex_offsets; + delete[] edges; + file.close(); + } + lowPE += window_size; + highPE += window_size; + MPI_Barrier(communicator); + } + + return 0; } -int parallel_graph_io::writeGraphParallelSimple(parallel_graph_access & G, +int parallel_graph_io::writeGraphParallelSimple(parallel_graph_access & G, std::string filename, MPI_Comm communicator) { - PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - if( rank == ROOT ) { - std::ofstream f(filename.c_str()); - f << G.number_of_global_nodes() << " " << G.number_of_global_edges()/2 << std::endl; - - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - f << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " ; - } endfor - f << "\n"; - } endfor - - f.close(); - } - - for( int i = 1; i < size; i++) { - MPI_Barrier(communicator); - - if( rank == i ) { - std::ofstream f; - f.open(filename.c_str(), std::ofstream::out | std::ofstream::app); - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - f << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " ; - } endfor - f << "\n"; - } endfor - f.close(); - } - } + PEID rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + if( rank == ROOT ) { + std::ofstream f(filename.c_str()); + f << G.number_of_global_nodes() << " " << G.number_of_global_edges()/2 << std::endl; + + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + f << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " ; + } endfor + f << "\n"; + } endfor + + f.close(); + } + + for( int i = 1; i < size; i++) { + MPI_Barrier(communicator); + + if( rank == i ) { + std::ofstream f; + f.open(filename.c_str(), std::ofstream::out | std::ofstream::app); + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + f << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " ; + } endfor + f << "\n"; + } endfor + f.close(); + } + } - MPI_Barrier(communicator); - - return 0; + MPI_Barrier(communicator); + + return 0; } -int parallel_graph_io::writeGraphWeightedParallelSimple(parallel_graph_access & G, +int parallel_graph_io::writeGraphWeightedParallelSimple(parallel_graph_access & G, std::string filename, MPI_Comm communicator) { - PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - if( rank == ROOT ) { - std::ofstream f(filename.c_str()); - f << G.number_of_global_nodes() << " " << G.number_of_global_edges()/2 << " 11" << std::endl; - - forall_local_nodes(G, node) { - f << G.getNodeWeight(node) ; - forall_out_edges(G, e, node) { - f << " " << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " << G.getEdgeWeight(e) ; - } endfor - f << "\n"; - } endfor - - f.close(); - } - - for( PEID i = 1; i < size; i++) { - MPI_Barrier(communicator); - - if( rank == i ) { - std::ofstream f; - f.open(filename.c_str(), std::ofstream::out | std::ofstream::app); - forall_local_nodes(G, node) { - f << G.getNodeWeight(node) ; - forall_out_edges(G, e, node) { - f << " " << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " << G.getEdgeWeight(e) ; - } endfor - f << "\n"; - } endfor - f.close(); - } - } + PEID rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + + if( rank == ROOT ) { + std::ofstream f(filename.c_str()); + f << G.number_of_global_nodes() << " " << G.number_of_global_edges()/2 << " 11" << std::endl; + + forall_local_nodes(G, node) { + f << G.getNodeWeight(node) ; + forall_out_edges(G, e, node) { + f << " " << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " << G.getEdgeWeight(e) ; + } endfor + f << "\n"; + } endfor + + f.close(); + } + + for( PEID i = 1; i < size; i++) { + MPI_Barrier(communicator); + + if( rank == i ) { + std::ofstream f; + f.open(filename.c_str(), std::ofstream::out | std::ofstream::app); + forall_local_nodes(G, node) { + f << G.getNodeWeight(node) ; + forall_out_edges(G, e, node) { + f << " " << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " << G.getEdgeWeight(e) ; + } endfor + f << "\n"; + } endfor + f.close(); + } + } - MPI_Barrier(communicator); - - return 0; + MPI_Barrier(communicator); + + return 0; } int parallel_graph_io::writeGraphWeightedSequentially(complete_graph_access & G, std::string filename) { - std::ofstream f(filename.c_str()); - f << G.number_of_global_nodes() << " " << G.number_of_global_edges()/2 << " 11" << std::endl; - - forall_local_nodes(G, node) { - f << G.getNodeWeight(node) ; - forall_out_edges(G, e, node) { - f << " " << (G.getEdgeTarget(e)+1) << " " << G.getEdgeWeight(e) ; - } endfor - f << "\n"; - } endfor - - f.close(); - return 0; + std::ofstream f(filename.c_str()); + f << G.number_of_global_nodes() << " " << G.number_of_global_edges()/2 << " 11" << std::endl; + + forall_local_nodes(G, node) { + f << G.getNodeWeight(node) ; + forall_out_edges(G, e, node) { + f << " " << (G.getEdgeTarget(e)+1) << " " << G.getEdgeWeight(e) ; + } endfor + f << "\n"; + } endfor + + f.close(); + return 0; } int parallel_graph_io::writeGraphSequentially(complete_graph_access & G, std::ofstream & f) { - f << G.number_of_global_nodes() << " " << G.number_of_global_edges()/2 << std::endl; - - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - f << " " << (G.getEdgeTarget(e)+1) ; - } endfor - f << "\n"; - } endfor - return 0; + f << G.number_of_global_nodes() << " " << G.number_of_global_edges()/2 << std::endl; + + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + f << " " << (G.getEdgeTarget(e)+1) ; + } endfor + f << "\n"; + } endfor + return 0; } int parallel_graph_io::writeGraphSequentially(complete_graph_access & G, std::string filename) { - std::ofstream f(filename.c_str()); - writeGraphSequentially(G, f); - f.close(); - return 0; + std::ofstream f(filename.c_str()); + writeGraphSequentially(G, f); + f.close(); + return 0; } -// we start with the simplest version of IO +// we start with the simplest version of IO // where each process reads the graph sequentially // TODO write weighted code and fully parallel io code -int parallel_graph_io::readGraphWeightedMETIS_fixed(parallel_graph_access & G, - std::string filename, +int parallel_graph_io::readGraphWeightedMETIS_fixed(parallel_graph_access & G, + std::string filename, PEID peID, PEID comm_size, MPI_Comm communicator) { - std::string line; - - // open file for reading - std::ifstream in(filename.c_str()); - if (!in) { - std::cerr << "Error opening " << filename << std::endl; - return 1; + std::string line; + + // open file for reading + std::ifstream in(filename.c_str()); + if (!in) { + std::cerr << "Error opening " << filename << std::endl; + return 1; + } + + NodeID nmbNodes; + EdgeID nmbEdges; + + std::getline(in,line); + //skip comments + while( line[0] == '%' ) { + std::getline(in, line); + } + + int ew = 0; + std::stringstream ss(line); + ss >> nmbNodes; + ss >> nmbEdges; + ss >> ew; + + // pe p reads the lines p*ceil(n/size) to (p+1)floor(n/size) lines of that file + ULONG from = peID * ceil(nmbNodes / (double)comm_size); + ULONG to = (peID+1) * ceil(nmbNodes / (double)comm_size) - 1; + to = std::min(to, nmbNodes-1); + + unsigned long local_no_nodes = to - from + 1; + std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl; + + std::vector< std::vector< NodeID > > local_edge_lists; + local_edge_lists.resize(local_no_nodes); + + std::vector< std::vector< NodeID > > local_edge_weights; + local_edge_weights.resize(local_no_nodes); + + std::vector< NodeID > local_node_weights; + + bool read_ew = false; + bool read_nw = false; + + if(ew == 1) { + read_ew = true; + } else if (ew == 11) { + read_ew = true; + read_nw = true; + } else if (ew == 10) { + read_nw = true; + } + + //std::getline(in, line); + unsigned long counter = 0; + NodeID node_counter = 0; + EdgeID edge_counter = 0; + + while( std::getline(in, line) ) { + if( counter > to ) { + break; + } + if (line[0] == '%') { // a comment in the file + continue; + } + + if( counter >= from ) { + std::stringstream ss(line); + + NodeWeight weight = 1; + if( read_nw ) { + ss >> weight; + } + local_node_weights.push_back(weight); + + NodeID target; + while( ss >> target ) { + EdgeWeight edge_weight = 1; + if( read_ew ) { + ss >> edge_weight; } - NodeID nmbNodes; - EdgeID nmbEdges; + local_edge_weights[node_counter].push_back(edge_weight); + local_edge_lists[node_counter].push_back(target); + edge_counter++; + } + node_counter++; + } - std::getline(in,line); - //skip comments - while( line[0] == '%' ) { - std::getline(in, line); - } + counter++; - int ew = 0; - std::stringstream ss(line); - ss >> nmbNodes; - ss >> nmbEdges; - ss >> ew; - - // pe p reads the lines p*ceil(n/size) to (p+1)floor(n/size) lines of that file - ULONG from = peID * ceil(nmbNodes / (double)comm_size); - ULONG to = (peID+1) * ceil(nmbNodes / (double)comm_size) - 1; - to = std::min(to, nmbNodes-1); - - unsigned long local_no_nodes = to - from + 1; - std::cout << "peID " << peID << " from " << from << " to " << to << " amount " << local_no_nodes << std::endl; - - std::vector< std::vector< NodeID > > local_edge_lists; - local_edge_lists.resize(local_no_nodes); - - std::vector< std::vector< NodeID > > local_edge_weights; - local_edge_weights.resize(local_no_nodes); - - std::vector< NodeID > local_node_weights; - - bool read_ew = false; - bool read_nw = false; - - if(ew == 1) { - read_ew = true; - } else if (ew == 11) { - read_ew = true; - read_nw = true; - } else if (ew == 10) { - read_nw = true; - } + if( in.eof() ) { + break; + } + } - //std::getline(in, line); - unsigned long counter = 0; - NodeID node_counter = 0; - EdgeID edge_counter = 0; - - while( std::getline(in, line) ) { - if( counter > to ) { - break; - } - if (line[0] == '%') { // a comment in the file - continue; - } - - if( counter >= from ) { - std::stringstream ss(line); - - NodeWeight weight = 1; - if( read_nw ) { - ss >> weight; - } - local_node_weights.push_back(weight); - - NodeID target; - while( ss >> target ) { - EdgeWeight edge_weight = 1; - if( read_ew ) { - ss >> edge_weight; - } - - local_edge_weights[node_counter].push_back(edge_weight); - local_edge_lists[node_counter].push_back(target); - edge_counter++; - } - node_counter++; - } - - counter++; - - if( in.eof() ) { - break; - } - } + MPI_Barrier(communicator); + G.start_construction(local_no_nodes, 2*edge_counter, nmbNodes, 2*nmbEdges); + G.set_range(from, to); - MPI_Barrier(communicator); - G.start_construction(local_no_nodes, 2*edge_counter, nmbNodes, 2*nmbEdges); - G.set_range(from, to); + std::vector< NodeID > vertex_dist( comm_size+1, 0 ); + for( PEID peID = 0; peID <= comm_size; peID++) { + vertex_dist[peID] = peID * ceil(nmbNodes / (double)comm_size); // from positions + } + G.set_range_array(vertex_dist); - std::vector< NodeID > vertex_dist( comm_size+1, 0 ); - for( PEID peID = 0; peID <= comm_size; peID++) { - vertex_dist[peID] = peID * ceil(nmbNodes / (double)comm_size); // from positions - } - G.set_range_array(vertex_dist); - - for (NodeID i = 0; i < local_no_nodes; ++i) { - NodeID node = G.new_node(); - G.setNodeWeight(node, local_node_weights[i]); - G.setNodeLabel(node, from+node); - G.setSecondPartitionIndex(node, 0); - - - for( unsigned j = 0; j < local_edge_lists[i].size(); j++) { - NodeID target = local_edge_lists[i][j]-1; // -1 since there are no nodes with id 0 in the file - EdgeID e = G.new_edge(node, target); - G.setEdgeWeight(e, local_edge_weights[i][j]); - } - } + for (NodeID i = 0; i < local_no_nodes; ++i) { + NodeID node = G.new_node(); + G.setNodeWeight(node, local_node_weights[i]); + G.setNodeLabel(node, from+node); + G.setSecondPartitionIndex(node, 0); - G.finish_construction(); - MPI_Barrier(communicator); - return 0; + for( unsigned j = 0; j < local_edge_lists[i].size(); j++) { + NodeID target = local_edge_lists[i][j]-1; // -1 since there are no nodes with id 0 in the file + EdgeID e = G.new_edge(node, target); + G.setEdgeWeight(e, local_edge_weights[i][j]); + } + } -} + G.finish_construction(); + MPI_Barrier(communicator); + return 0; + +} +} diff --git a/parallel/parallel_src/lib/io/parallel_graph_io.h b/parallel/parallel_src/lib/io/parallel_graph_io.h index 02d18e26..6a22b2c6 100644 --- a/parallel/parallel_src/lib/io/parallel_graph_io.h +++ b/parallel/parallel_src/lib/io/parallel_graph_io.h @@ -16,56 +16,57 @@ #include "partition_config.h" #define MAXLINE 50000000 +namespace parhip { class parallel_graph_io { - public: - parallel_graph_io(); - virtual ~parallel_graph_io(); +public: + parallel_graph_io(); + virtual ~parallel_graph_io(); - static int readGraphWeighted(PPartitionConfig & config, parallel_graph_access & G, - std::string filename, - PEID peID, - PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); + static int readGraphWeighted(PPartitionConfig & config, parallel_graph_access & G, + std::string filename, + PEID peID, + PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); - //static int readGraphWeightedMETIS(parallel_graph_access & G, - //std::string filename, - //PEID peID, - //PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); + //static int readGraphWeightedMETIS(parallel_graph_access & G, + //std::string filename, + //PEID peID, + //PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); - static int readGraphWeightedFlexible(parallel_graph_access & G, std::string filename, PEID peID, PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); + static int readGraphWeightedFlexible(parallel_graph_access & G, std::string filename, PEID peID, PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); - //static int readGraphWeightedMETISFast(parallel_graph_access & G, - //std::string filename, - //PEID peID, - //PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); + //static int readGraphWeightedMETISFast(parallel_graph_access & G, + //std::string filename, + //PEID peID, + //PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); - static int readGraphBinary(PPartitionConfig & config, parallel_graph_access & G, - std::string filename, - PEID peID, - PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); + static int readGraphBinary(PPartitionConfig & config, parallel_graph_access & G, + std::string filename, + PEID peID, + PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); - static int writeGraphParallelSimple(parallel_graph_access & G, - std::string filename, MPI_Comm communicator = MPI_COMM_WORLD); + static int writeGraphParallelSimple(parallel_graph_access & G, + std::string filename, MPI_Comm communicator = MPI_COMM_WORLD); - static int writeGraphWeightedParallelSimple(parallel_graph_access & G, - std::string filename, MPI_Comm communicator = MPI_COMM_WORLD); + static int writeGraphWeightedParallelSimple(parallel_graph_access & G, + std::string filename, MPI_Comm communicator = MPI_COMM_WORLD); - static int writeGraphWeightedSequentially(complete_graph_access & G, - std::string filename); + static int writeGraphWeightedSequentially(complete_graph_access & G, + std::string filename); - static int writeGraphSequentially(complete_graph_access & G, - std::string filename); + static int writeGraphSequentially(complete_graph_access & G, + std::string filename); - static int writeGraphSequentially(complete_graph_access & G, - std::ofstream & f); + static int writeGraphSequentially(complete_graph_access & G, + std::ofstream & f); - static int writeGraphSequentiallyBinary(complete_graph_access & G, std::string filename); + static int writeGraphSequentiallyBinary(complete_graph_access & G, std::string filename); - static int writeGraphExternallyBinary(std::string intput_filename, std::string output_filename); + static int writeGraphExternallyBinary(std::string intput_filename, std::string output_filename); - static int readGraphWeightedMETIS_fixed(parallel_graph_access & G, std::string filename, PEID peID, PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); + static int readGraphWeightedMETIS_fixed(parallel_graph_access & G, std::string filename, PEID peID, PEID comm_size, MPI_Comm communicator = MPI_COMM_WORLD); }; - +} #endif /* end of include guard: PARALLEL_GRAPH_IO_8HHCKD13 */ diff --git a/parallel/parallel_src/lib/io/parallel_vector_io.cpp b/parallel/parallel_src/lib/io/parallel_vector_io.cpp index c7cf2b19..3c729b6a 100644 --- a/parallel/parallel_src/lib/io/parallel_vector_io.cpp +++ b/parallel/parallel_src/lib/io/parallel_vector_io.cpp @@ -11,7 +11,7 @@ #include #include "parallel_vector_io.h" #include "tools/helpers.h" - +namespace parhip { parallel_vector_io::parallel_vector_io() { } @@ -24,241 +24,241 @@ void parallel_vector_io::writePartitionBinaryParallelPosix(PPartitionConfig & co parallel_graph_access & G, std::string filename) { - PEID rank, size; - MPI_Comm_rank( MPI_COMM_WORLD, &rank); - MPI_Comm_size( MPI_COMM_WORLD, &size); - - if( rank == ROOT ) { - ULONG n = G.number_of_global_nodes(); - int output_fd = open(filename.c_str(), O_WRONLY | O_CREAT, 0644); - write(output_fd, (char*)(&fileTypeVersionNumberPartition), sizeof( ULONG )); - write(output_fd, (char*)(&n), sizeof( ULONG )); - close(output_fd); - } - - MPI_Barrier(MPI_COMM_WORLD); - PEID window_size = std::min(config.binary_io_window_size, size); - PEID lowPE = 0; - PEID highPE = window_size; - while ( lowPE < size ) { - if( rank >= lowPE && rank < highPE ) { - int output_fd = open(filename.c_str(), O_WRONLY, 0644); - - ULONG from = G.get_from_range(); - ULONG start_pos = (header_count_partition + from)*(sizeof(ULONG)); - lseek( output_fd, start_pos, SEEK_SET); - - std::vector< ULONG > partition_ids(G.number_of_local_nodes(),0); - forall_local_nodes(G, node) { - ULONG block = G.getNodeLabel(node); - partition_ids[node]= block; - } endfor - - write(output_fd, (char*)(&partition_ids[0]), G.number_of_local_nodes()*sizeof( ULONG )); - close(output_fd); - } - lowPE += window_size; - highPE += window_size; - MPI_Barrier(MPI_COMM_WORLD); - } - - MPI_Barrier(MPI_COMM_WORLD); + PEID rank, size; + MPI_Comm_rank( MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &size); + + if( rank == ROOT ) { + ULONG n = G.number_of_global_nodes(); + int output_fd = open(filename.c_str(), O_WRONLY | O_CREAT, 0644); + write(output_fd, (char*)(&fileTypeVersionNumberPartition), sizeof( ULONG )); + write(output_fd, (char*)(&n), sizeof( ULONG )); + close(output_fd); + } + + MPI_Barrier(MPI_COMM_WORLD); + PEID window_size = std::min(config.binary_io_window_size, size); + PEID lowPE = 0; + PEID highPE = window_size; + while ( lowPE < size ) { + if( rank >= lowPE && rank < highPE ) { + int output_fd = open(filename.c_str(), O_WRONLY, 0644); + + ULONG from = G.get_from_range(); + ULONG start_pos = (header_count_partition + from)*(sizeof(ULONG)); + lseek( output_fd, start_pos, SEEK_SET); + + std::vector< ULONG > partition_ids(G.number_of_local_nodes(),0); + forall_local_nodes(G, node) { + ULONG block = G.getNodeLabel(node); + partition_ids[node]= block; + } endfor + + write(output_fd, (char*)(&partition_ids[0]), G.number_of_local_nodes()*sizeof( ULONG )); + close(output_fd); + } + lowPE += window_size; + highPE += window_size; + MPI_Barrier(MPI_COMM_WORLD); + } + + MPI_Barrier(MPI_COMM_WORLD); } void parallel_vector_io::writePartitionBinaryParallel(PPartitionConfig & config, - parallel_graph_access & G, + parallel_graph_access & G, std::string filename) { - PEID rank, size; - MPI_Comm_rank( MPI_COMM_WORLD, &rank); - MPI_Comm_size( MPI_COMM_WORLD, &size); - - if( rank == ROOT ) { - // ROOT writes head - ULONG n = G.number_of_global_nodes(); - std::ofstream outfile; - outfile.open(filename.c_str(), std::ios::binary | std::ios::out); - outfile.write((char*)(&fileTypeVersionNumberPartition), sizeof( ULONG )); - outfile.write((char*)(&n), sizeof( ULONG )); - outfile.close(); - - } - - MPI_Barrier(MPI_COMM_WORLD); - PEID window_size = 1; - PEID lowPE = 0; - PEID highPE = window_size; - while ( lowPE < size ) { - if( rank >= lowPE && rank < highPE ) { - std::ofstream file; - file.open(filename.c_str(), std::ios::binary | std::ios::out | std::ios::app); - - ULONG from = G.get_from_range(); - ULONG start_pos = (header_count_partition + from)*(sizeof(ULONG)); - file.seekp(start_pos); - - std::vector< ULONG > partition_ids(G.number_of_local_nodes(),0); - forall_local_nodes(G, node) { - ULONG block = G.getNodeLabel(node); - partition_ids[node]= block; - } endfor - file.write((char*)(&partition_ids[0]), G.number_of_local_nodes()*sizeof( ULONG )); - - file.close(); - } - lowPE += window_size; - highPE += window_size; - MPI_Barrier(MPI_COMM_WORLD); - } - - MPI_Barrier(MPI_COMM_WORLD); + PEID rank, size; + MPI_Comm_rank( MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &size); + + if( rank == ROOT ) { + // ROOT writes head + ULONG n = G.number_of_global_nodes(); + std::ofstream outfile; + outfile.open(filename.c_str(), std::ios::binary | std::ios::out); + outfile.write((char*)(&fileTypeVersionNumberPartition), sizeof( ULONG )); + outfile.write((char*)(&n), sizeof( ULONG )); + outfile.close(); + + } + + MPI_Barrier(MPI_COMM_WORLD); + PEID window_size = 1; + PEID lowPE = 0; + PEID highPE = window_size; + while ( lowPE < size ) { + if( rank >= lowPE && rank < highPE ) { + std::ofstream file; + file.open(filename.c_str(), std::ios::binary | std::ios::out | std::ios::app); + + ULONG from = G.get_from_range(); + ULONG start_pos = (header_count_partition + from)*(sizeof(ULONG)); + file.seekp(start_pos); + + std::vector< ULONG > partition_ids(G.number_of_local_nodes(),0); + forall_local_nodes(G, node) { + ULONG block = G.getNodeLabel(node); + partition_ids[node]= block; + } endfor + file.write((char*)(&partition_ids[0]), G.number_of_local_nodes()*sizeof( ULONG )); + + file.close(); + } + lowPE += window_size; + highPE += window_size; + MPI_Barrier(MPI_COMM_WORLD); + } + + MPI_Barrier(MPI_COMM_WORLD); } void parallel_vector_io::readPartitionBinaryParallel(PPartitionConfig & config, - parallel_graph_access & G, + parallel_graph_access & G, std::string filename) { - PEID rank, size; - MPI_Comm_rank( MPI_COMM_WORLD, &rank); - MPI_Comm_size( MPI_COMM_WORLD, &size); - - if( rank == ROOT ) { - // ROOT reades head - std::cout << "reading binary partition" << std::endl; - std::ifstream file; - file.open(filename.c_str(), std::ios::binary | std::ios::in); - std::vector< ULONG > buffer(2, 0); - if(file) { - file.read((char*)(&buffer[0]), 2*sizeof(ULONG)); - if( buffer[0] != fileTypeVersionNumberPartition ) { - std::cout << "filetype version mismatch " << buffer[0] << "!=" << fileTypeVersionNumberPartition << std::endl; - exit(0); - } - if( buffer[1] != G.number_of_global_nodes()) { - std::cout << "wrong number of nodes in partition file" << std::endl; - exit(0); - } - } - file.close(); - } - - PEID window_size = std::min(config.binary_io_window_size, size); - PEID lowPE = 0; - PEID highPE = window_size; - while ( lowPE < size ) { - if( rank >= lowPE && rank < highPE ) { - std::ifstream file; - file.open(filename.c_str(), std::ios::binary | std::ios::in); - - ULONG from = G.get_from_range(); - ULONG ids_to_read = G.number_of_local_nodes(); - ULONG start_pos = (header_count_partition + from)*(sizeof(ULONG)); - file.seekg(start_pos); - - std::vector< ULONG > partition_ids(ids_to_read, 0); - file.read((char*)(&partition_ids[0]), ids_to_read*sizeof( ULONG )); - file.close(); - forall_local_nodes(G, node) { - G.setNodeLabel(node, partition_ids[node]); - } endfor - - } - lowPE += window_size; - highPE += window_size; - MPI_Barrier(MPI_COMM_WORLD); - } - - MPI_Barrier(MPI_COMM_WORLD); - G.update_ghost_node_data_global(); - MPI_Barrier(MPI_COMM_WORLD); + PEID rank, size; + MPI_Comm_rank( MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &size); + + if( rank == ROOT ) { + // ROOT reades head + std::cout << "reading binary partition" << std::endl; + std::ifstream file; + file.open(filename.c_str(), std::ios::binary | std::ios::in); + std::vector< ULONG > buffer(2, 0); + if(file) { + file.read((char*)(&buffer[0]), 2*sizeof(ULONG)); + if( buffer[0] != fileTypeVersionNumberPartition ) { + std::cout << "filetype version mismatch " << buffer[0] << "!=" << fileTypeVersionNumberPartition << std::endl; + exit(0); + } + if( buffer[1] != G.number_of_global_nodes()) { + std::cout << "wrong number of nodes in partition file" << std::endl; + exit(0); + } + } + file.close(); + } + + PEID window_size = std::min(config.binary_io_window_size, size); + PEID lowPE = 0; + PEID highPE = window_size; + while ( lowPE < size ) { + if( rank >= lowPE && rank < highPE ) { + std::ifstream file; + file.open(filename.c_str(), std::ios::binary | std::ios::in); + + ULONG from = G.get_from_range(); + ULONG ids_to_read = G.number_of_local_nodes(); + ULONG start_pos = (header_count_partition + from)*(sizeof(ULONG)); + file.seekg(start_pos); + + std::vector< ULONG > partition_ids(ids_to_read, 0); + file.read((char*)(&partition_ids[0]), ids_to_read*sizeof( ULONG )); + file.close(); + forall_local_nodes(G, node) { + G.setNodeLabel(node, partition_ids[node]); + } endfor + } + lowPE += window_size; + highPE += window_size; + MPI_Barrier(MPI_COMM_WORLD); + } + MPI_Barrier(MPI_COMM_WORLD); + G.update_ghost_node_data_global(); + MPI_Barrier(MPI_COMM_WORLD); +} -void parallel_vector_io::writePartitionSimpleParallel(parallel_graph_access & G, + +void parallel_vector_io::writePartitionSimpleParallel(parallel_graph_access & G, std::string filename) { - PEID rank, size; - MPI_Comm_rank( MPI_COMM_WORLD, &rank); - MPI_Comm_size( MPI_COMM_WORLD, &size); - - if( rank == ROOT ) { - std::ofstream f(filename.c_str()); + PEID rank, size; + MPI_Comm_rank( MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &size); - forall_local_nodes(G, node) { - f << G.getNodeLabel(node) ; - f << std::endl; - } endfor + if( rank == ROOT ) { + std::ofstream f(filename.c_str()); - f.close(); - } + forall_local_nodes(G, node) { + f << G.getNodeLabel(node) ; + f << std::endl; + } endfor + + f.close(); + } + + for( int i = 1; i < size; i++) { + MPI_Barrier(MPI_COMM_WORLD); + + if( rank == i ) { + std::ofstream f; + f.open(filename.c_str(), std::ofstream::out | std::ofstream::app); + forall_local_nodes(G, node) { + f << G.getNodeLabel(node) ; + f << std::endl; + } endfor + f.close(); + } + } + MPI_Barrier(MPI_COMM_WORLD); - for( int i = 1; i < size; i++) { - MPI_Barrier(MPI_COMM_WORLD); - - if( rank == i ) { - std::ofstream f; - f.open(filename.c_str(), std::ofstream::out | std::ofstream::app); - forall_local_nodes(G, node) { - f << G.getNodeLabel(node) ; - f << std::endl; - } endfor - f.close(); - } - } - MPI_Barrier(MPI_COMM_WORLD); - } -void parallel_vector_io::readPartition(PPartitionConfig & config, parallel_graph_access & G, +void parallel_vector_io::readPartition(PPartitionConfig & config, parallel_graph_access & G, std::string filename) { - std::string text_ending(".txtp"); - std::string bin_ending(".binp"); + std::string text_ending(".txtp"); + std::string bin_ending(".binp"); - if( hasEnding(filename, text_ending) ) { - return readPartitionSimpleParallel(G, filename); - } + if( hasEnding(filename, text_ending) ) { + return readPartitionSimpleParallel(G, filename); + } - if( hasEnding(filename, bin_ending) ) { - return readPartitionBinaryParallel(config, G, filename); - } + if( hasEnding(filename, bin_ending) ) { + return readPartitionBinaryParallel(config, G, filename); + } } -void parallel_vector_io::readPartitionSimpleParallel(parallel_graph_access & G, +void parallel_vector_io::readPartitionSimpleParallel(parallel_graph_access & G, std::string filename) { - PEID rank, size; - MPI_Comm_rank( MPI_COMM_WORLD, &rank); - MPI_Comm_size( MPI_COMM_WORLD, &size); - - MPI_Barrier(MPI_COMM_WORLD); - if( rank == ROOT ) { std::cout << "reading text partition" << std::endl; } - - std::string line; - // open file for reading - std::ifstream in(filename.c_str()); - if (!in) { - std::cerr << "Error opening file" << filename << std::endl; - return; - } - - NodeID counter = 0; - NodeID from = G.get_from_range(); - NodeID to = G.get_to_range(); - - std::getline(in, line); - while( !in.eof() ) { - if( counter > to ) { - break; - } - - if( counter >= from ) { - PartitionID block_id = (PartitionID) atof(line.c_str()); - G.setNodeLabel(counter-from, block_id); - } - counter++; - std::getline(in, line); - } - - MPI_Barrier(MPI_COMM_WORLD); - G.update_ghost_node_data_global(); - MPI_Barrier(MPI_COMM_WORLD); + PEID rank, size; + MPI_Comm_rank( MPI_COMM_WORLD, &rank); + MPI_Comm_size( MPI_COMM_WORLD, &size); + + MPI_Barrier(MPI_COMM_WORLD); + if( rank == ROOT ) { std::cout << "reading text partition" << std::endl; } + + std::string line; + // open file for reading + std::ifstream in(filename.c_str()); + if (!in) { + std::cerr << "Error opening file" << filename << std::endl; + return; + } + + NodeID counter = 0; + NodeID from = G.get_from_range(); + NodeID to = G.get_to_range(); + + std::getline(in, line); + while( !in.eof() ) { + if( counter > to ) { + break; + } + + if( counter >= from ) { + PartitionID block_id = (PartitionID) atof(line.c_str()); + G.setNodeLabel(counter-from, block_id); + } + counter++; + std::getline(in, line); + } + + MPI_Barrier(MPI_COMM_WORLD); + G.update_ghost_node_data_global(); + MPI_Barrier(MPI_COMM_WORLD); } - +} diff --git a/parallel/parallel_src/lib/io/parallel_vector_io.h b/parallel/parallel_src/lib/io/parallel_vector_io.h index 9b51c0f7..ac030469 100644 --- a/parallel/parallel_src/lib/io/parallel_vector_io.h +++ b/parallel/parallel_src/lib/io/parallel_vector_io.h @@ -18,7 +18,7 @@ #include "parallel_graph_io.h" #include "partition_config.h" - +namespace parhip { const ULONG fileTypeVersionNumberPartition = 1; const ULONG header_count_partition = 2; @@ -81,5 +81,5 @@ void parallel_vector_io::readVectorSequentially(std::vector & vec, s in.close(); } - +} #endif /* end of include guard: PARALLEL_VECTOR_IO_BZVNZ570 */ diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_block_down_propagation.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_block_down_propagation.cpp index e45b1358..5bdd8d08 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_block_down_propagation.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_block_down_propagation.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "parallel_block_down_propagation.h" - +namespace parhip { parallel_block_down_propagation::parallel_block_down_propagation() { } @@ -20,151 +20,152 @@ void parallel_block_down_propagation::propagate_block_down( MPI_Comm communicato parallel_graph_access & Q) { - std::unordered_map< NodeID, NodeID > coarse_block_ids; - - forall_local_nodes(G, node) { - NodeID cur_cnode = G.getCNode( node ); - coarse_block_ids[cur_cnode] = G.getSecondPartitionIndex( node ); - } endfor - - PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - NodeID divisor = ceil( Q.number_of_global_nodes()/(double)size); - - m_messages.resize(size); - - //now distribute the block idw - //pack messages - for( auto it = coarse_block_ids.begin(); it != coarse_block_ids.end(); it++) { - NodeID node = it->first; - NodeID block = it->second; - PEID peID = node / divisor; - - m_messages[ peID ].push_back( node ); - m_messages[ peID ].push_back( block ); - } - - for( PEID peID = 0; peID < size; peID++) { - if( peID != rank ) { - if( m_messages[peID].size() == 0 ){ - m_messages[peID].push_back(std::numeric_limits::max()); - } - - MPI_Request rq; - MPI_Isend( &m_messages[peID][0], - m_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID+10*size, communicator, &rq ); - } - } - - if( m_messages[ rank ].size() != 0 ) { - for( ULONG i = 0; i < (ULONG)m_messages[rank].size()-1; i+=2) { - NodeID globalID = m_messages[rank][i]; - NodeID node = Q.getLocalID(globalID); - NodeWeight block = m_messages[rank][i+1]; - Q.setSecondPartitionIndex(node , block); - } - } - - PEID counter = 0; - while( counter < size - 1) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+10*size, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - - std::vector incmessage; incmessage.resize(message_length); - - MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+10*size, communicator, &rst); - counter++; - - // now integrate the changes - if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do - - for( ULONG i = 0; i < incmessage.size()-1; i+=2) { - NodeID globalID = incmessage[i]; - NodeWeight block = incmessage[i+1]; - NodeID node = Q.getLocalID(globalID); - Q.setSecondPartitionIndex( node , block); - } - } - - update_ghost_nodes_blocks( communicator, Q ); + std::unordered_map< NodeID, NodeID > coarse_block_ids; + + forall_local_nodes(G, node) { + NodeID cur_cnode = G.getCNode( node ); + coarse_block_ids[cur_cnode] = G.getSecondPartitionIndex( node ); + } endfor + + PEID rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + + NodeID divisor = ceil( Q.number_of_global_nodes()/(double)size); + + m_messages.resize(size); + + //now distribute the block idw + //pack messages + for( auto it = coarse_block_ids.begin(); it != coarse_block_ids.end(); it++) { + NodeID node = it->first; + NodeID block = it->second; + PEID peID = node / divisor; + + m_messages[ peID ].push_back( node ); + m_messages[ peID ].push_back( block ); + } + + for( PEID peID = 0; peID < size; peID++) { + if( peID != rank ) { + if( m_messages[peID].size() == 0 ){ + m_messages[peID].push_back(std::numeric_limits::max()); + } + + MPI_Request rq; + MPI_Isend( &m_messages[peID][0], + m_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, + peID, peID+10*size, communicator, &rq ); + } + } + + if( m_messages[ rank ].size() != 0 ) { + for( ULONG i = 0; i < (ULONG)m_messages[rank].size()-1; i+=2) { + NodeID globalID = m_messages[rank][i]; + NodeID node = Q.getLocalID(globalID); + NodeWeight block = m_messages[rank][i+1]; + Q.setSecondPartitionIndex(node , block); + } + } + + PEID counter = 0; + while( counter < size - 1) { + // wait for incomming message of an adjacent processor + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, rank+10*size, communicator, &st); + + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + + std::vector incmessage; incmessage.resize(message_length); + + MPI_Status rst; + MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+10*size, communicator, &rst); + counter++; + + // now integrate the changes + if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do + + for( ULONG i = 0; i < incmessage.size()-1; i+=2) { + NodeID globalID = incmessage[i]; + NodeWeight block = incmessage[i+1]; + NodeID node = Q.getLocalID(globalID); + Q.setSecondPartitionIndex( node , block); + } + } + + update_ghost_nodes_blocks( communicator, Q ); } void parallel_block_down_propagation::update_ghost_nodes_blocks( MPI_Comm communicator, parallel_graph_access & G ) { - PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - m_send_buffers.resize(size); - std::vector< bool > PE_packed(size, false); - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - PEID peID = G.getTargetPE(target); - if( !PE_packed[peID] ) { // make sure a node is sent at most once - m_send_buffers[peID].push_back(G.getGlobalID(node)); - m_send_buffers[peID].push_back(G.getSecondPartitionIndex(node)); - PE_packed[peID] = true; - } - } - } endfor - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - PE_packed[G.getTargetPE(target)] = false; - } - } endfor - } endfor - - //send all neighbors their packages using Isends - //a neighbor that does not receive something gets a specific token - for( PEID peID = 0; peID < (PEID)m_send_buffers.size(); peID++) { - if( G.is_adjacent_PE(peID) ) { - //now we have to send a message - if( m_send_buffers[peID].size() == 0 ){ - // length 1 encode no message - m_send_buffers[peID].push_back(0); - } - - MPI_Request rq; - MPI_Isend( &m_send_buffers[peID][0], - m_send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, peID+11*size, communicator, &rq); - } + PEID rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + + m_send_buffers.resize(size); + std::vector< bool > PE_packed(size, false); + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + PEID peID = G.getTargetPE(target); + if( !PE_packed[peID] ) { // make sure a node is sent at most once + m_send_buffers[peID].push_back(G.getGlobalID(node)); + m_send_buffers[peID].push_back(G.getSecondPartitionIndex(node)); + PE_packed[peID] = true; } + } + } endfor + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + PE_packed[G.getTargetPE(target)] = false; + } + } endfor +} endfor + +//send all neighbors their packages using Isends +//a neighbor that does not receive something gets a specific token +for( PEID peID = 0; peID < (PEID)m_send_buffers.size(); peID++) { + if( G.is_adjacent_PE(peID) ) { + //now we have to send a message + if( m_send_buffers[peID].size() == 0 ){ + // length 1 encode no message + m_send_buffers[peID].push_back(0); + } + + MPI_Request rq; + MPI_Isend( &m_send_buffers[peID][0], + m_send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, peID+11*size, communicator, &rq); + } +} - //receive incomming - PEID counter = 0; - while( counter < G.getNumberOfAdjacentPEs()) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+11*size, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector message; message.resize(message_length); + //receive incomming + PEID counter = 0; + while( counter < G.getNumberOfAdjacentPEs()) { + // wait for incomming message of an adjacent processor + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, rank+11*size, communicator, &st); - MPI_Status rst; - MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+11*size, communicator, &rst); - counter++; + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector message; message.resize(message_length); - // now integrate the changes - if(message_length == 1) continue; // nothing to do + MPI_Status rst; + MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+11*size, communicator, &rst); + counter++; - for( int i = 0; i < message_length-1; i+=2) { - NodeID global_id = message[i]; - NodeWeight block = message[i+1]; + // now integrate the changes + if(message_length == 1) continue; // nothing to do - G.setSecondPartitionIndex( G.getLocalID(global_id), block ); - } - } + for( int i = 0; i < message_length-1; i+=2) { + NodeID global_id = message[i]; + NodeWeight block = message[i+1]; + + G.setSecondPartitionIndex( G.getLocalID(global_id), block ); + } + } } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_block_down_propagation.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_block_down_propagation.h index d0b36ab2..b27079b5 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_block_down_propagation.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_block_down_propagation.h @@ -10,23 +10,23 @@ #include "data_structure/parallel_graph_access.h" #include "partition_config.h" - +namespace parhip { class parallel_block_down_propagation { public: - parallel_block_down_propagation(); - virtual ~parallel_block_down_propagation(); + parallel_block_down_propagation(); + virtual ~parallel_block_down_propagation(); - void propagate_block_down( MPI_Comm communicator, PPartitionConfig & config, - parallel_graph_access & G, - parallel_graph_access & Q); + void propagate_block_down( MPI_Comm communicator, PPartitionConfig & config, + parallel_graph_access & G, + parallel_graph_access & Q); private: - void update_ghost_nodes_blocks( MPI_Comm communicator, parallel_graph_access & G ); + void update_ghost_nodes_blocks( MPI_Comm communicator, parallel_graph_access & G ); - std::vector< std::vector< NodeID > > m_messages; - std::vector< std::vector< NodeID > > m_send_buffers; // buffers to send messages + std::vector< std::vector< NodeID > > m_messages; + std::vector< std::vector< NodeID > > m_send_buffers; // buffers to send messages }; - +} #endif /* end of include guard: PARALLEL_BLOCK_DOWN_PROPAGATION_SRTCMH8F */ diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 710c0ecb..b9bf46fd 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -8,41 +8,41 @@ #include "parallel_contraction.h" #include "data_structure/hashed_graph.h" #include "tools/helpers.h" - -void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicator, PPartitionConfig & config, +namespace parhip { +void parallel_contraction::contract_to_distributed_quotient( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & G, parallel_graph_access & Q) { - NodeID number_of_distinct_labels; // equals global number of coarse nodes + NodeID number_of_distinct_labels; // equals global number of coarse nodes + + // maps old ids to new ids in interval [0, ...., num_of_distinct_labels + // and stores this information only for the local nodes + std::unordered_map< NodeID, NodeID > label_mapping; - // maps old ids to new ids in interval [0, ...., num_of_distinct_labels - // and stores this information only for the local nodes - std::unordered_map< NodeID, NodeID > label_mapping; + compute_label_mapping( communicator, G, number_of_distinct_labels, label_mapping); - compute_label_mapping( communicator, G, number_of_distinct_labels, label_mapping); - - // compute the projection table - G.allocate_node_to_cnode(); - forall_local_nodes(G, node) { - G.setCNode( node, label_mapping[ G.getNodeLabel( node )]); - } endfor + // compute the projection table + G.allocate_node_to_cnode(); + forall_local_nodes(G, node) { + G.setCNode( node, label_mapping[ G.getNodeLabel( node )]); + } endfor - get_nodes_to_cnodes_ghost_nodes( communicator, G ); + get_nodes_to_cnodes_ghost_nodes( communicator, G ); - //now we can really build the edges of the quotient graph - hashed_graph hG; - std::unordered_map< NodeID, NodeWeight > node_weights; + //now we can really build the edges of the quotient graph + hashed_graph hG; + std::unordered_map< NodeID, NodeWeight > node_weights; - build_quotient_graph_locally( G, number_of_distinct_labels, hG, node_weights); - - MPI_Barrier(communicator); + build_quotient_graph_locally( G, number_of_distinct_labels, hG, node_weights); - m_messages.clear(); - m_out_messages.clear(); - m_send_buffers.clear(); + MPI_Barrier(communicator); - redistribute_hased_graph_and_build_graph_locally( communicator, hG, node_weights, number_of_distinct_labels, Q ); - update_ghost_nodes_weights( communicator, Q ); + m_messages.clear(); + m_out_messages.clear(); + m_send_buffers.clear(); + + redistribute_hased_graph_and_build_graph_locally( communicator, hG, node_weights, number_of_distinct_labels, Q ); + update_ghost_nodes_weights( communicator, Q ); } // MPI AlltoAll based implementation @@ -71,11 +71,11 @@ void parallel_contraction::compute_label_mapping( endfor for (PEID peID = 0; peID < size; peID++) { - std::unordered_map::iterator it; - for (it = filter.at(peID).begin(); it != filter.at(peID).end(); ++it) { - m_messages.at(peID).push_back(it->first); - } - } + std::unordered_map::iterator it; + for (it = filter.at(peID).begin(); it != filter.at(peID).end(); ++it) { + m_messages.at(peID).push_back(it->first); + } + } auto const local_labels_byPE = mpi::all_to_all(m_messages, communicator); @@ -162,107 +162,107 @@ void parallel_contraction::compute_label_mapping( } void parallel_contraction::get_nodes_to_cnodes_ghost_nodes( MPI_Comm communicator, parallel_graph_access & G ) { - PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - std::vector< bool > PE_packed( size, false ); - m_send_buffers.clear(); - m_send_buffers.resize( size ); - - forall_local_nodes(G, node) { - if(G.is_interface_node(node)) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - PEID peID = G.getTargetPE(target); - if( !PE_packed[peID] ) { // make sure a node is sent at most once - m_send_buffers[peID].push_back(G.getGlobalID(node)); - m_send_buffers[peID].push_back(G.getCNode(node)); - PE_packed[peID] = true; - } - } - } endfor - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - PE_packed[G.getTargetPE(target)] = false; - } - } endfor - } - } endfor - - //send all neighbors their packages using Isends - //a neighbor that does not receive something gets a specific token - for( PEID peID = 0; peID < (PEID)m_send_buffers.size(); peID++) { - if( G.is_adjacent_PE(peID) ) { - //now we have to send a message - if( m_send_buffers[peID].size() == 0 ){ - // length 1 encode no message - m_send_buffers[peID].push_back(0); - } - - MPI_Request rq; - MPI_Isend( &m_send_buffers[peID][0], - m_send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, peID+6*size, communicator, &rq); - } - } + PEID rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + + std::vector< bool > PE_packed( size, false ); + m_send_buffers.clear(); + m_send_buffers.resize( size ); - ////receive incomming - PEID num_adjacent = G.getNumberOfAdjacentPEs(); - PEID counter = 0; - while( counter < num_adjacent ) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+6*size, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector message; message.resize(message_length); - - MPI_Status rst; - MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+6*size, communicator, &rst); - counter++; - - // now integrate the changes - if(message_length == 1) continue; // nothing to do - - for( int i = 0; i < message_length-1; i+=2) { - NodeID global_id = message[i]; - NodeID cnode = message[i+1]; - - G.setCNode( G.getLocalID(global_id), cnode); - } + forall_local_nodes(G, node) { + if(G.is_interface_node(node)) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + PEID peID = G.getTargetPE(target); + if( !PE_packed[peID] ) { // make sure a node is sent at most once + m_send_buffers[peID].push_back(G.getGlobalID(node)); + m_send_buffers[peID].push_back(G.getCNode(node)); + PE_packed[peID] = true; + } + } + } endfor + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + PE_packed[G.getTargetPE(target)] = false; } + } endfor } + } endfor + + //send all neighbors their packages using Isends + //a neighbor that does not receive something gets a specific token + for( PEID peID = 0; peID < (PEID)m_send_buffers.size(); peID++) { + if( G.is_adjacent_PE(peID) ) { + //now we have to send a message + if( m_send_buffers[peID].size() == 0 ){ + // length 1 encode no message + m_send_buffers[peID].push_back(0); + } + + MPI_Request rq; + MPI_Isend( &m_send_buffers[peID][0], + m_send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, peID+6*size, communicator, &rq); + } + } + + ////receive incomming + PEID num_adjacent = G.getNumberOfAdjacentPEs(); + PEID counter = 0; + while( counter < num_adjacent ) { + // wait for incomming message of an adjacent processor + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, rank+6*size, communicator, &st); + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector message; message.resize(message_length); -void parallel_contraction::build_quotient_graph_locally( parallel_graph_access & G, - NodeID number_of_distinct_labels, - hashed_graph & hG, + MPI_Status rst; + MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+6*size, communicator, &rst); + counter++; + + // now integrate the changes + if(message_length == 1) continue; // nothing to do + + for( int i = 0; i < message_length-1; i+=2) { + NodeID global_id = message[i]; + NodeID cnode = message[i+1]; + + G.setCNode( G.getLocalID(global_id), cnode); + } + } +} + + +void parallel_contraction::build_quotient_graph_locally( parallel_graph_access & G, + NodeID number_of_distinct_labels, + hashed_graph & hG, std::unordered_map< NodeID, NodeWeight > & node_weights) { - forall_local_nodes(G, node) { - NodeID cur_cnode = G.getCNode( node ); - if( node_weights.find(cur_cnode) == node_weights.end()) { - node_weights[cur_cnode] = 0; - } - - node_weights[cur_cnode] += G.getNodeWeight( node ); - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - NodeID target_cnode = G.getCNode(target); - if( cur_cnode != target_cnode ) { - // update the edge - hashed_edge he; - he.k = number_of_distinct_labels; - he.source = cur_cnode; - he.target = target_cnode; - - hG[he].weight += G.getEdgeWeight(e); - } - } endfor - } endfor + forall_local_nodes(G, node) { + NodeID cur_cnode = G.getCNode( node ); + if( node_weights.find(cur_cnode) == node_weights.end()) { + node_weights[cur_cnode] = 0; + } + + node_weights[cur_cnode] += G.getNodeWeight( node ); + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + NodeID target_cnode = G.getCNode(target); + if( cur_cnode != target_cnode ) { + // update the edge + hashed_edge he; + he.k = number_of_distinct_labels; + he.source = cur_cnode; + he.target = target_cnode; + + hG[he].weight += G.getEdgeWeight(e); + } + } endfor +} endfor } void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( @@ -409,73 +409,74 @@ void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( void parallel_contraction::update_ghost_nodes_weights( MPI_Comm communicator, parallel_graph_access & G ) { - PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - //std::vector< std::vector< NodeID > > send_buffers; // buffers to send messages - m_send_buffers.resize(size); - std::vector< bool > PE_packed(size, false); - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - PEID peID = G.getTargetPE(target); - if( !PE_packed[peID] ) { // make sure a node is sent at most once - m_send_buffers[peID].push_back(G.getGlobalID(node)); - m_send_buffers[peID].push_back(G.getNodeWeight(node)); - PE_packed[peID] = true; - } - } - } endfor - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node(target) ) { - PE_packed[G.getTargetPE(target)] = false; - } - } endfor - } endfor - - //send all neighbors their packages using Isends - //a neighbor that does not receive something gets a specific token - for( PEID peID = 0; peID < (PEID)m_send_buffers.size(); peID++) { - if( G.is_adjacent_PE(peID) ) { - //now we have to send a message - if( m_send_buffers[peID].size() == 0 ){ - // length 1 encode no message - m_send_buffers[peID].push_back(0); - } - - MPI_Request rq; - MPI_Isend( &m_send_buffers[peID][0], - m_send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, peID+9*size, communicator, &rq); - } - } + PEID rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); - //receive incomming - PEID counter = 0; - while( counter < G.getNumberOfAdjacentPEs()) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+9*size, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector message; message.resize(message_length); - - MPI_Status rst; - MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+9*size, communicator, &rst); - counter++; - - // now integrate the changes - if(message_length == 1) continue; // nothing to do - - for( int i = 0; i < message_length-1; i+=2) { - NodeID global_id = message[i]; - NodeWeight weight = message[i+1]; - - G.setNodeWeight( G.getLocalID(global_id), weight); - } + //std::vector< std::vector< NodeID > > send_buffers; // buffers to send messages + m_send_buffers.resize(size); + std::vector< bool > PE_packed(size, false); + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + PEID peID = G.getTargetPE(target); + if( !PE_packed[peID] ) { // make sure a node is sent at most once + m_send_buffers[peID].push_back(G.getGlobalID(node)); + m_send_buffers[peID].push_back(G.getNodeWeight(node)); + PE_packed[peID] = true; } + } + } endfor + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node(target) ) { + PE_packed[G.getTargetPE(target)] = false; + } + } endfor +} endfor + +//send all neighbors their packages using Isends +//a neighbor that does not receive something gets a specific token +for( PEID peID = 0; peID < (PEID)m_send_buffers.size(); peID++) { + if( G.is_adjacent_PE(peID) ) { + //now we have to send a message + if( m_send_buffers[peID].size() == 0 ){ + // length 1 encode no message + m_send_buffers[peID].push_back(0); + } + + MPI_Request rq; + MPI_Isend( &m_send_buffers[peID][0], + m_send_buffers[peID].size(), MPI_UNSIGNED_LONG_LONG, peID, peID+9*size, communicator, &rq); + } +} + + //receive incomming + PEID counter = 0; + while( counter < G.getNumberOfAdjacentPEs()) { + // wait for incomming message of an adjacent processor + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, rank+9*size, communicator, &st); + + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector message; message.resize(message_length); + + MPI_Status rst; + MPI_Recv( &message[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+9*size, communicator, &rst); + counter++; + + // now integrate the changes + if(message_length == 1) continue; // nothing to do + + for( int i = 0; i < message_length-1; i+=2) { + NodeID global_id = message[i]; + NodeWeight weight = message[i+1]; + + G.setNodeWeight( G.getLocalID(global_id), weight); + } + } } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h index ac151890..2950a044 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.h @@ -12,15 +12,15 @@ #include "data_structure/hashed_graph.h" #include "data_structure/parallel_graph_access.h" #include "partition_config.h" - +namespace parhip { class parallel_contraction { - public: +public: void contract_to_distributed_quotient(MPI_Comm communicator, PPartitionConfig& config, parallel_graph_access& G, parallel_graph_access& Q); - private: +private: // compute mapping of labels id into contiguous intervall [0,...,num_lables) void compute_label_mapping(MPI_Comm communicator, parallel_graph_access& G, @@ -65,4 +65,5 @@ struct bundled_node_weight { NodeWeight weight; }; } // namespace contraction +} #endif /* end of include guard: PARALLEL_CONTRACTION_64O127GD */ diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp index 317182a6..6ba0a383 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.cpp @@ -8,7 +8,7 @@ #include "parallel_projection.h" #include "communication/mpi_tools.h" - +namespace parhip { parallel_projection::parallel_projection() { } @@ -19,140 +19,141 @@ parallel_projection::~parallel_projection() { //issue recv before send void parallel_projection::parallel_project( MPI_Comm communicator, parallel_graph_access & finer, parallel_graph_access & coarser ) { - PEID rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - NodeID divisor = ceil(coarser.number_of_global_nodes() / (double)size); - - m_messages.resize(size); - - std::unordered_map< NodeID, std::vector< NodeID > > cnode_to_nodes; - forall_local_nodes(finer, node) { - NodeID cnode = finer.getCNode(node); - //std::cout << "cnode " << cnode << std::endl; - if( coarser.is_local_node_from_global_id(cnode) ) { - NodeID new_label = coarser.getNodeLabel(coarser.getLocalID(cnode)); - finer.setNodeLabel(node, new_label); - } else { - //we have to request it from another PE - PEID peID = cnode / divisor; // cnode is - - if( cnode_to_nodes.find( cnode ) == cnode_to_nodes.end()) { - m_messages[peID].push_back(cnode); // we are requesting the label of this node - } - - cnode_to_nodes[cnode].push_back(node); - } - } endfor - - for( PEID peID = 0; peID < size; peID++) { - if( peID != rank ) { - if( m_messages[peID].size() == 0 ){ - m_messages[peID].push_back(std::numeric_limits::max()); - } - - MPI_Request rq; - MPI_Isend( &m_messages[peID][0], - m_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID+size, communicator, &rq); - } - } - - std::vector< std::vector< NodeID > > out_messages(size); - auto const inc_mess_byPE = mpi::all_to_all( m_messages, communicator); - - for(auto const& incmessage : inc_mess_byPE) { - - } - - PEID counter = 0; - while( counter < size - 1) { - // wait for incomming message of an adjacent processor - MPI_Status st; - MPI_Probe(MPI_ANY_SOURCE, rank+size, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector incmessage; incmessage.resize(message_length); - - MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+size, communicator, &rst); - counter++; - //----- size of messeges - PEID peID = st.MPI_SOURCE; - // now integrate the changes - if( incmessage[0] == std::numeric_limits< NodeID >::max()) { - out_messages[peID].push_back(std::numeric_limits< NodeID >::max()); - MPI_Request rq; - MPI_Isend( &out_messages[peID][0], - out_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID+2*size, communicator, &rq); - - continue; // nothing to do - } - - - - for( int i = 0; i < message_length; i++) { - NodeID cnode = coarser.getLocalID(incmessage[i]); - out_messages[peID].push_back(coarser.getNodeLabel(cnode)); - } - // -- Handling messages and building response - MPI_Request rq; - MPI_Isend( &out_messages[peID][0], - out_messages[peID].size(), - MPI_UNSIGNED_LONG_LONG, - peID, peID+2*size, communicator, &rq); - // -- Sending responses - } - - counter = 0; - while( counter < size - 1) { - // wait for incomming message of an adjacent processor - MPI_Status st; ULONG tag = rank+2*size; - MPI_Probe(MPI_ANY_SOURCE, tag, communicator, &st); - - int message_length; - MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); - std::vector incmessage; incmessage.resize(message_length); - - MPI_Status rst; - MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, tag, communicator, &rst); - counter++; - // -- get sizes and revcs - // now integrate the changes - if( incmessage[0] == std::numeric_limits< NodeID >::max()) { - continue; // nothing to do - } - - PEID peID = st.MPI_SOURCE; - for( ULONG i = 0; i < (ULONG)incmessage.size(); i++) { - std::vector< NodeID > & proj = cnode_to_nodes[m_messages[peID][i]]; - NodeID label = incmessage[i]; - - for( ULONG j = 0; j < proj.size(); j++) { - finer.setNodeLabel(proj[j], label); - } - } - } - - finer.update_ghost_node_data_global(); // blocking + PEID rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + + NodeID divisor = ceil(coarser.number_of_global_nodes() / (double)size); + + m_messages.resize(size); + + std::unordered_map< NodeID, std::vector< NodeID > > cnode_to_nodes; + forall_local_nodes(finer, node) { + NodeID cnode = finer.getCNode(node); + //std::cout << "cnode " << cnode << std::endl; + if( coarser.is_local_node_from_global_id(cnode) ) { + NodeID new_label = coarser.getNodeLabel(coarser.getLocalID(cnode)); + finer.setNodeLabel(node, new_label); + } else { + //we have to request it from another PE + PEID peID = cnode / divisor; // cnode is + + if( cnode_to_nodes.find( cnode ) == cnode_to_nodes.end()) { + m_messages[peID].push_back(cnode); // we are requesting the label of this node + } + + cnode_to_nodes[cnode].push_back(node); + } + } endfor + + for( PEID peID = 0; peID < size; peID++) { + if( peID != rank ) { + if( m_messages[peID].size() == 0 ){ + m_messages[peID].push_back(std::numeric_limits::max()); + } + + MPI_Request rq; + MPI_Isend( &m_messages[peID][0], + m_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, + peID, peID+size, communicator, &rq); + } + } + + std::vector< std::vector< NodeID > > out_messages(size); + auto const inc_mess_byPE = mpi::all_to_all( m_messages, communicator); + + for(auto const& incmessage : inc_mess_byPE) { + + } + + PEID counter = 0; + while( counter < size - 1) { + // wait for incomming message of an adjacent processor + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, rank+size, communicator, &st); + + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector incmessage; incmessage.resize(message_length); + + MPI_Status rst; + MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+size, communicator, &rst); + counter++; + //----- size of messeges + PEID peID = st.MPI_SOURCE; + // now integrate the changes + if( incmessage[0] == std::numeric_limits< NodeID >::max()) { + out_messages[peID].push_back(std::numeric_limits< NodeID >::max()); + MPI_Request rq; + MPI_Isend( &out_messages[peID][0], + out_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, + peID, peID+2*size, communicator, &rq); + + continue; // nothing to do + } + + + + for( int i = 0; i < message_length; i++) { + NodeID cnode = coarser.getLocalID(incmessage[i]); + out_messages[peID].push_back(coarser.getNodeLabel(cnode)); + } + // -- Handling messages and building response + MPI_Request rq; + MPI_Isend( &out_messages[peID][0], + out_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, + peID, peID+2*size, communicator, &rq); + // -- Sending responses + } + + counter = 0; + while( counter < size - 1) { + // wait for incomming message of an adjacent processor + MPI_Status st; ULONG tag = rank+2*size; + MPI_Probe(MPI_ANY_SOURCE, tag, communicator, &st); + + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector incmessage; incmessage.resize(message_length); + + MPI_Status rst; + MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, tag, communicator, &rst); + counter++; + // -- get sizes and revcs + // now integrate the changes + if( incmessage[0] == std::numeric_limits< NodeID >::max()) { + continue; // nothing to do + } + + PEID peID = st.MPI_SOURCE; + for( ULONG i = 0; i < (ULONG)incmessage.size(); i++) { + std::vector< NodeID > & proj = cnode_to_nodes[m_messages[peID][i]]; + NodeID label = incmessage[i]; + + for( ULONG j = 0; j < proj.size(); j++) { + finer.setNodeLabel(proj[j], label); + } + } + } + + finer.update_ghost_node_data_global(); // blocking } //initial assignment after initial partitioning void parallel_projection::initial_assignment( parallel_graph_access & G, complete_graph_access & Q) { - forall_local_nodes(G, node) { - G.setNodeLabel(node, Q.getNodeLabel(G.getGlobalID(node))); - if( G.is_interface_node(node) ) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node( target ) ) { - G.setNodeLabel(target, Q.getNodeLabel(G.getGlobalID(target))); - } - } endfor - } - } endfor + forall_local_nodes(G, node) { + G.setNodeLabel(node, Q.getNodeLabel(G.getGlobalID(node))); + if( G.is_interface_node(node) ) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node( target ) ) { + G.setNodeLabel(target, Q.getNodeLabel(G.getGlobalID(target))); + } + } endfor +} + } endfor } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.h b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.h index 6266c523..1b9f0dca 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.h +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_projection.h @@ -9,7 +9,7 @@ #define PARALLEL_PROJECTION_HBRCPQ0P #include "data_structure/parallel_graph_access.h" - +namespace parhip { class parallel_projection { public: parallel_projection(); @@ -22,7 +22,7 @@ class parallel_projection { private: std::vector< std::vector< NodeID > > m_messages; }; - +} diff --git a/parallel/parallel_src/lib/parallel_label_compress/hmap_wrapper.h b/parallel/parallel_src/lib/parallel_label_compress/hmap_wrapper.h index df2dbd98..0e063c80 100644 --- a/parallel/parallel_src/lib/parallel_label_compress/hmap_wrapper.h +++ b/parallel/parallel_src/lib/parallel_label_compress/hmap_wrapper.h @@ -9,95 +9,95 @@ #define HMAP_WRAPPER_RQFK3ARC #include "data_structure/linear_probing_hashmap.h" - +namespace parhip { template class hmap_wrapper { - public: +public: - hmap_wrapper(PPartitionConfig & config) { - m_config = config; - }; + hmap_wrapper(PPartitionConfig & config) { + m_config = config; + }; - virtual ~hmap_wrapper() {}; + virtual ~hmap_wrapper() {}; - void init( NodeID max_fill_count ); - void clear(); - NodeWeight & operator[](NodeID node); + void init( NodeID max_fill_count ); + void clear(); + NodeWeight & operator[](NodeID node); private: - T mapping_type; - PPartitionConfig m_config; + T mapping_type; + PPartitionConfig m_config; }; template <> class hmap_wrapper < linear_probing_hashmap > { - public: +public: - hmap_wrapper(PPartitionConfig & config) { - m_config = config; - }; + hmap_wrapper(PPartitionConfig & config) { + m_config = config; + }; - virtual ~hmap_wrapper() {}; + virtual ~hmap_wrapper() {}; - void init(NodeID max_fill_count ) {mapping_type.init(max_fill_count, m_config.ht_fill_factor);}; - void clear() { mapping_type.clear(); }; - NodeWeight & operator[](NodeID node) {return mapping_type[node];}; + void init(NodeID max_fill_count ) {mapping_type.init(max_fill_count, m_config.ht_fill_factor);}; + void clear() { mapping_type.clear(); }; + NodeWeight & operator[](NodeID node) {return mapping_type[node];}; - private: - linear_probing_hashmap mapping_type; - PPartitionConfig m_config; +private: + linear_probing_hashmap mapping_type; + PPartitionConfig m_config; }; template <> class hmap_wrapper > { - public: +public: - hmap_wrapper(PPartitionConfig & config) { - m_config = config; - }; + hmap_wrapper(PPartitionConfig & config) { + m_config = config; + }; - virtual ~hmap_wrapper() {}; + virtual ~hmap_wrapper() {}; - void init( NodeID max_fill_count ) {}; - void clear() { mapping_type.clear(); }; - NodeWeight & operator[](NodeID node) {return mapping_type[node];}; + void init( NodeID max_fill_count ) {}; + void clear() { mapping_type.clear(); }; + NodeWeight & operator[](NodeID node) {return mapping_type[node];}; - private: - std::unordered_map mapping_type; - PPartitionConfig m_config; +private: + std::unordered_map mapping_type; + PPartitionConfig m_config; }; template <> class hmap_wrapper > { - public: +public: - hmap_wrapper(PPartitionConfig & config) { - m_config = config; - }; + hmap_wrapper(PPartitionConfig & config) { + m_config = config; + }; - virtual ~hmap_wrapper() {}; + virtual ~hmap_wrapper() {}; - void init( NodeID max_fill ) { - mapping_type.resize(m_config.k); - for( ULONG k = 0; k < m_config.k; k++) { - mapping_type[k] = 0; - } + void init( NodeID max_fill ) { + mapping_type.resize(m_config.k); + for( ULONG k = 0; k < m_config.k; k++) { + mapping_type[k] = 0; + } - }; + }; - void clear() { - for( ULONG k = 0; k < m_config.k; k++) { - mapping_type[k] = 0; - } - }; + void clear() { + for( ULONG k = 0; k < m_config.k; k++) { + mapping_type[k] = 0; + } + }; - NodeWeight & operator[](NodeID node) {return mapping_type[node];}; + NodeWeight & operator[](NodeID node) {return mapping_type[node];}; - private: - std::vector mapping_type; - PPartitionConfig m_config; +private: + std::vector mapping_type; + PPartitionConfig m_config; }; - +} #endif /* end of include guard: HMAP_WRAPPER_RQFK3ARC */ diff --git a/parallel/parallel_src/lib/parallel_label_compress/node_ordering.cpp b/parallel/parallel_src/lib/parallel_label_compress/node_ordering.cpp index f4be6241..1aa66200 100644 --- a/parallel/parallel_src/lib/parallel_label_compress/node_ordering.cpp +++ b/parallel/parallel_src/lib/parallel_label_compress/node_ordering.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "node_ordering.h" - +namespace parhip { node_ordering::node_ordering() { } @@ -14,4 +14,4 @@ node_ordering::node_ordering() { node_ordering::~node_ordering() { } - +} diff --git a/parallel/parallel_src/lib/parallel_label_compress/node_ordering.h b/parallel/parallel_src/lib/parallel_label_compress/node_ordering.h index 6d0f6588..313e7033 100644 --- a/parallel/parallel_src/lib/parallel_label_compress/node_ordering.h +++ b/parallel/parallel_src/lib/parallel_label_compress/node_ordering.h @@ -14,7 +14,7 @@ #include "partition_config.h" #include "data_structure/parallel_graph_access.h" #include "tools/random_functions.h" - +namespace parhip { class node_ordering { public: node_ordering(); @@ -28,44 +28,44 @@ class node_ordering { switch( config.node_ordering ) { case RANDOM_NODEORDERING: order_nodes_random(config, G, ordered_nodes); - break; + break; case DEGREE_NODEORDERING: order_nodes_degree(config, G, ordered_nodes); - break; + break; case LEASTGHOSTNODESFIRST_DEGREE_NODEODERING: order_leastghostnodes_nodes_degree(config, G, ordered_nodes); - break; + break; case DEGREE_LEASTGHOSTNODESFIRST_NODEODERING: order_nodes_degree_leastghostnodes(config, G, ordered_nodes); - break; - } + break; + } } - void order_nodes_random(const PPartitionConfig & config, parallel_graph_access & G, std::vector< NodeID > & ordered_nodes) { + void order_nodes_random(const PPartitionConfig & config, parallel_graph_access & G, std::vector< NodeID > & ordered_nodes) { random_functions::permutate_vector_fast(ordered_nodes, false); } - void order_nodes_degree(const PPartitionConfig & config, parallel_graph_access & G, std::vector< NodeID > & ordered_nodes) { - std::sort( ordered_nodes.begin(), ordered_nodes.end(), + void order_nodes_degree(const PPartitionConfig & config, parallel_graph_access & G, std::vector< NodeID > & ordered_nodes) { + std::sort( ordered_nodes.begin(), ordered_nodes.end(), [&]( const NodeID & lhs, const NodeID & rhs) -> bool { return (G.getNodeDegree(lhs) < G.getNodeDegree(rhs)); }); } - void order_leastghostnodes_nodes_degree(const PPartitionConfig & config, parallel_graph_access & G, std::vector< NodeID > & ordered_nodes) { - std::sort( ordered_nodes.begin(), ordered_nodes.end(), + void order_leastghostnodes_nodes_degree(const PPartitionConfig & config, parallel_graph_access & G, std::vector< NodeID > & ordered_nodes) { + std::sort( ordered_nodes.begin(), ordered_nodes.end(), [&]( const NodeID & lhs, const NodeID & rhs) -> bool { return (G.getNodeDegree(lhs) < G.getNodeDegree(rhs)); }); } - void order_nodes_degree_leastghostnodes(const PPartitionConfig & config, parallel_graph_access & G, std::vector< NodeID > & ordered_nodes) { - std::sort( ordered_nodes.begin(), ordered_nodes.end(), + void order_nodes_degree_leastghostnodes(const PPartitionConfig & config, parallel_graph_access & G, std::vector< NodeID > & ordered_nodes) { + std::sort( ordered_nodes.begin(), ordered_nodes.end(), [&]( const NodeID & lhs, const NodeID & rhs) -> bool { - return (G.getNodeDegree(lhs) < G.getNodeDegree(rhs)); + return (G.getNodeDegree(lhs) < G.getNodeDegree(rhs)); }); } }; - +} #endif /* end of include guard: NODE_ORDERING_HM1YMLB1 */ diff --git a/parallel/parallel_src/lib/parallel_label_compress/parallel_label_compress.h b/parallel/parallel_src/lib/parallel_label_compress/parallel_label_compress.h index 28fd7873..999b0eab 100644 --- a/parallel/parallel_src/lib/parallel_label_compress/parallel_label_compress.h +++ b/parallel/parallel_src/lib/parallel_label_compress/parallel_label_compress.h @@ -14,99 +14,99 @@ #include "hmap_wrapper.h" #include "node_ordering.h" - -template +namespace parhip { +template class parallel_label_compress { - public: - parallel_label_compress() {}; - virtual ~parallel_label_compress() {}; - - void perform_parallel_label_compression( PPartitionConfig & config, - parallel_graph_access & G, bool balance, bool for_coarsening = true) { - - if( config.label_iterations == 0) return; - NodeWeight cluster_upperbound = config.upper_bound_cluster; - - std::vector< NodeID > permutation( G.number_of_local_nodes() ); - if( for_coarsening ) { - node_ordering no; - no.order_nodes( config, G, permutation); - } else { - random_functions::permutate_vector_fast( permutation, true); - } - - //std::unordered_map hash_map; - hmap_wrapper< T > hash_map(config); - hash_map.init( G.get_max_degree() ); - for( ULONG i = 0; i < config.label_iterations; i++) { - NodeID prev_node = 0; - forall_local_nodes(G, rnode) { - NodeID node = permutation[rnode]; // use the current random node - - //move the node to the cluster that is most common in the neighborhood - //second sweep for finding max and resetting array - PartitionID max_block = G.getNodeLabel(node); - PartitionID old_block = G.getNodeLabel(node); - PartitionID max_value = 0; - NodeWeight node_weight = G.getNodeWeight(node); - bool own_block_balanced = G.getBlockSize(old_block) <= cluster_upperbound || !balance; - - if( G.getNodeDegree(node) == 0) { - // find a block to assign it to - if(config.vcycle) { - NodeWeight prev_block_size = G.getBlockSize( G.getNodeLabel( prev_node ) ); - bool same_block = G.getSecondPartitionIndex(prev_node)==G.getSecondPartitionIndex(node); - if( prev_block_size + node_weight <= cluster_upperbound && same_block ) { - max_block = G.getNodeLabel( prev_node ); - } - } else { - NodeWeight prev_block_size = G.getBlockSize( G.getNodeLabel( prev_node ) ); - if( prev_block_size + node_weight <= cluster_upperbound) { - max_block = G.getNodeLabel( prev_node ); - } - } +public: + parallel_label_compress() {}; + virtual ~parallel_label_compress() {}; + + void perform_parallel_label_compression( PPartitionConfig & config, + parallel_graph_access & G, bool balance, bool for_coarsening = true) { + + if( config.label_iterations == 0) return; + NodeWeight cluster_upperbound = config.upper_bound_cluster; + + std::vector< NodeID > permutation( G.number_of_local_nodes() ); + if( for_coarsening ) { + node_ordering no; + no.order_nodes( config, G, permutation); + } else { + random_functions::permutate_vector_fast( permutation, true); + } + //std::unordered_map hash_map; + hmap_wrapper< T > hash_map(config); + hash_map.init( G.get_max_degree() ); + for( ULONG i = 0; i < config.label_iterations; i++) { + NodeID prev_node = 0; + forall_local_nodes(G, rnode) { + NodeID node = permutation[rnode]; // use the current random node + + //move the node to the cluster that is most common in the neighborhood + //second sweep for finding max and resetting array + PartitionID max_block = G.getNodeLabel(node); + PartitionID old_block = G.getNodeLabel(node); + PartitionID max_value = 0; + NodeWeight node_weight = G.getNodeWeight(node); + bool own_block_balanced = G.getBlockSize(old_block) <= cluster_upperbound || !balance; + + if( G.getNodeDegree(node) == 0) { + // find a block to assign it to + if(config.vcycle) { + NodeWeight prev_block_size = G.getBlockSize( G.getNodeLabel( prev_node ) ); + bool same_block = G.getSecondPartitionIndex(prev_node)==G.getSecondPartitionIndex(node); + if( prev_block_size + node_weight <= cluster_upperbound && same_block ) { + max_block = G.getNodeLabel( prev_node ); + } } else { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID cur_block = G.getNodeLabel(target); - hash_map[cur_block] += G.getEdgeWeight(e); - PartitionID cur_value = hash_map[cur_block]; - - bool improvement = cur_value > max_value; - improvement |= cur_value == max_value && random_functions::nextBool(); - - bool sizeconstraint = G.getBlockSize(cur_block) + node_weight <= cluster_upperbound; - sizeconstraint |= cur_block == old_block; - - bool cycle = !config.vcycle; - cycle |= G.getSecondPartitionIndex( node ) == G.getSecondPartitionIndex(target); - - bool balancing = own_block_balanced || cur_block != old_block; - if( improvement && sizeconstraint && cycle && balancing) { - max_value = cur_value; - max_block = cur_block; - } - } endfor + NodeWeight prev_block_size = G.getBlockSize( G.getNodeLabel( prev_node ) ); + if( prev_block_size + node_weight <= cluster_upperbound) { + max_block = G.getNodeLabel( prev_node ); + } } - if( old_block != max_block ) { - G.setNodeLabel(node, max_block); + } else { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID cur_block = G.getNodeLabel(target); + hash_map[cur_block] += G.getEdgeWeight(e); + PartitionID cur_value = hash_map[cur_block]; - G.setBlockSize(old_block, G.getBlockSize(old_block) - node_weight); - G.setBlockSize(max_block, G.getBlockSize(max_block) + node_weight); - } + bool improvement = cur_value > max_value; + improvement |= cur_value == max_value && random_functions::nextBool(); + + bool sizeconstraint = G.getBlockSize(cur_block) + node_weight <= cluster_upperbound; + sizeconstraint |= cur_block == old_block; - prev_node = node; - G.update_ghost_node_data(); - hash_map.clear(); + bool cycle = !config.vcycle; + cycle |= G.getSecondPartitionIndex( node ) == G.getSecondPartitionIndex(target); - } endfor - G.update_ghost_node_data_finish(); - } + bool balancing = own_block_balanced || cur_block != old_block; + if( improvement && sizeconstraint && cycle && balancing) { + max_value = cur_value; + max_block = cur_block; + } + } endfor + } + + if( old_block != max_block ) { + G.setNodeLabel(node, max_block); + + G.setBlockSize(old_block, G.getBlockSize(old_block) - node_weight); + G.setBlockSize(max_block, G.getBlockSize(max_block) + node_weight); + } + + prev_node = node; + G.update_ghost_node_data(); + hash_map.clear(); + + } endfor + G.update_ghost_node_data_finish(); } + } }; - +} #endif /* end of include guard: PARALLEL_LABEL_COMPRESS_9ME4H8DK */ diff --git a/parallel/parallel_src/lib/partition_config.h b/parallel/parallel_src/lib/partition_config.h index 0a4ef1f3..fb4fd648 100644 --- a/parallel/parallel_src/lib/partition_config.h +++ b/parallel/parallel_src/lib/partition_config.h @@ -9,121 +9,121 @@ #define PARTITION_CONFIG_DI1ES4T0A #include "definitions.h" - +namespace parhip { // Configuration for the partitioning. struct PPartitionConfig { - PPartitionConfig() {} + PPartitionConfig() {} + + //======================================= + //============ Graph Gen================= + //======================================= + int log_num_verts; - //======================================= - //============ Graph Gen================= - //======================================= - int log_num_verts; + long edge_factor; - long edge_factor; + bool generate_rgg; - bool generate_rgg; + bool generate_ba; - bool generate_ba; + //======================================= + //============ Communication ============ + //======================================= - //======================================= - //============ Communication ============ - //======================================= + ULONG comm_rounds; - ULONG comm_rounds; + //======================================= + //============ Global Data=============== + //======================================= - //======================================= - //============ Global Data=============== - //======================================= + NodeID number_of_overall_nodes; - NodeID number_of_overall_nodes; + //======================================= + //===============MISC==================== + //======================================= - //======================================= - //===============MISC==================== - //======================================= + PermutationQuality permutation_quality; - PermutationQuality permutation_quality; + unsigned int label_iterations; - unsigned int label_iterations; - - unsigned int label_iterations_coarsening; + unsigned int label_iterations_coarsening; - unsigned int label_iterations_refinement; + unsigned int label_iterations_refinement; - double cluster_coarsening_factor; + double cluster_coarsening_factor; - double time_limit; + double time_limit; - unsigned epsilon; + unsigned epsilon; - unsigned inbalance; + unsigned inbalance; - std::string input_partition; + std::string input_partition; - int seed; + int seed; - PartitionID k; + PartitionID k; - std::string graph_filename; + std::string graph_filename; - std::string input_partition_filename; + std::string input_partition_filename; - int evolutionary_time_limit; + int evolutionary_time_limit; NodeWeight upper_bound_partition; NodeWeight upper_bound_cluster; - NodeID total_num_labels; + NodeID total_num_labels; - InitialPartitioningAlgorithm initial_partitioning_algorithm; + InitialPartitioningAlgorithm initial_partitioning_algorithm; - int stop_factor; + int stop_factor; - bool vcycle; + bool vcycle; - int num_vcycles; + int num_vcycles; - int num_tries; // number of repetitions to perform + int num_tries; // number of repetitions to perform - NodeOrderingType node_ordering; + NodeOrderingType node_ordering; - bool no_refinement_in_last_iteration; + bool no_refinement_in_last_iteration; - double ht_fill_factor; + double ht_fill_factor; - bool eco; + bool eco; int binary_io_window_size; - ULONG barabasi_albert_mindegree; + ULONG barabasi_albert_mindegree; - bool compute_degree_sequence_ba; + bool compute_degree_sequence_ba; - bool compute_degree_sequence_k_first; + bool compute_degree_sequence_k_first; - bool kronecker_internal_only; + bool kronecker_internal_only; - ULONG k_deg; + ULONG k_deg; - bool generate_ba_32bit; + bool generate_ba_32bit; - ULONG n; + ULONG n; bool save_partition; bool save_partition_binary; - bool vertex_degree_weights; + bool vertex_degree_weights; - bool converter_evaluate; + bool converter_evaluate; - //======================================= - //===============Shared Mem OMP========== - //======================================= - void LogDump(FILE *out) const { - } + //======================================= + //===============Shared Mem OMP========== + //======================================= + void LogDump(FILE *out) const { + } }; - +} #endif /* end of include guard: PARTITION_CONFIG_DI1ES4T0 */ diff --git a/parallel/parallel_src/lib/tools/distributed_quality_metrics.cpp b/parallel/parallel_src/lib/tools/distributed_quality_metrics.cpp index 80f72938..3529f2e7 100644 --- a/parallel/parallel_src/lib/tools/distributed_quality_metrics.cpp +++ b/parallel/parallel_src/lib/tools/distributed_quality_metrics.cpp @@ -7,7 +7,7 @@ #include #include "distributed_quality_metrics.h" - +namespace parhip { distributed_quality_metrics::distributed_quality_metrics() { } @@ -17,288 +17,288 @@ distributed_quality_metrics::~distributed_quality_metrics() { } EdgeWeight distributed_quality_metrics::edge_cut_second( parallel_graph_access & G, MPI_Comm communicator ) { - EdgeWeight local_cut = 0; - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( G.getSecondPartitionIndex( node ) != G.getSecondPartitionIndex(target)) { - local_cut += G.getEdgeWeight(e); - } - } endfor - } endfor - - EdgeWeight global_cut = 0; - MPI_Allreduce(&local_cut, &global_cut, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - - return global_cut/2; + EdgeWeight local_cut = 0; + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( G.getSecondPartitionIndex( node ) != G.getSecondPartitionIndex(target)) { + local_cut += G.getEdgeWeight(e); + } + } endfor +} endfor + +EdgeWeight global_cut = 0; + MPI_Allreduce(&local_cut, &global_cut, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + + return global_cut/2; } EdgeWeight distributed_quality_metrics::local_edge_cut( parallel_graph_access & G, int* partition_map, MPI_Comm communicator ) { - EdgeWeight local_cut = 0; - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( partition_map[ node ] != partition_map[ target ]) { - local_cut += G.getEdgeWeight(e); - } - } endfor - } endfor - - return local_cut/2; + EdgeWeight local_cut = 0; + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( partition_map[ node ] != partition_map[ target ]) { + local_cut += G.getEdgeWeight(e); + } + } endfor +} endfor + +return local_cut/2; } EdgeWeight distributed_quality_metrics::edge_cut( parallel_graph_access & G, MPI_Comm communicator ) { - EdgeWeight local_cut = 0; - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( G.getNodeLabel( node ) != G.getNodeLabel(target)) { - local_cut += G.getEdgeWeight(e); - } - } endfor - } endfor - - EdgeWeight global_cut = 0; - MPI_Allreduce(&local_cut, &global_cut, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - - return global_cut/2; + EdgeWeight local_cut = 0; + forall_local_nodes(G, node) { + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( G.getNodeLabel( node ) != G.getNodeLabel(target)) { + local_cut += G.getEdgeWeight(e); + } + } endfor +} endfor + +EdgeWeight global_cut = 0; + MPI_Allreduce(&local_cut, &global_cut, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + + return global_cut/2; } NodeWeight distributed_quality_metrics::local_max_block_weight( PPartitionConfig & config, parallel_graph_access & G, int * partition_map, MPI_Comm communicator ) { - std::vector block_weights(config.k, 0); + std::vector block_weights(config.k, 0); - NodeWeight graph_vertex_weight = 0; + NodeWeight graph_vertex_weight = 0; - forall_local_nodes(G, n) { - PartitionID curPartition = partition_map[n]; - block_weights[curPartition] += G.getNodeWeight(n); - graph_vertex_weight += G.getNodeWeight(n); - } endfor + forall_local_nodes(G, n) { + PartitionID curPartition = partition_map[n]; + block_weights[curPartition] += G.getNodeWeight(n); + graph_vertex_weight += G.getNodeWeight(n); + } endfor - NodeWeight cur_max = 0; + NodeWeight cur_max = 0; - for( PartitionID block = 0; block < config.k; block++) { - NodeWeight cur_weight = block_weights[block]; - if (cur_weight > cur_max) { - cur_max = cur_weight; - } - } + for( PartitionID block = 0; block < config.k; block++) { + NodeWeight cur_weight = block_weights[block]; + if (cur_weight > cur_max) { + cur_max = cur_weight; + } + } - return cur_max; + return cur_max; } double distributed_quality_metrics::balance( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ) { - std::vector block_weights(config.k, 0); + std::vector block_weights(config.k, 0); - NodeWeight local_graph_vertex_weight = 0; + NodeWeight local_graph_vertex_weight = 0; - forall_local_nodes(G, n) { - PartitionID curPartition = G.getNodeLabel(n); - block_weights[curPartition] += G.getNodeWeight(n); - local_graph_vertex_weight += G.getNodeWeight(n); - } endfor + forall_local_nodes(G, n) { + PartitionID curPartition = G.getNodeLabel(n); + block_weights[curPartition] += G.getNodeWeight(n); + local_graph_vertex_weight += G.getNodeWeight(n); + } endfor - std::vector overall_weights(config.k, 0); - MPI_Allreduce(&block_weights[0], &overall_weights[0], config.k, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + std::vector overall_weights(config.k, 0); + MPI_Allreduce(&block_weights[0], &overall_weights[0], config.k, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - NodeWeight graph_vertex_weight = 0; - MPI_Allreduce(&local_graph_vertex_weight, &graph_vertex_weight, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + NodeWeight graph_vertex_weight = 0; + MPI_Allreduce(&local_graph_vertex_weight, &graph_vertex_weight, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - double balance_part_weight = ceil(graph_vertex_weight / (double)config.k); - double cur_max = -1; + double balance_part_weight = ceil(graph_vertex_weight / (double)config.k); + double cur_max = -1; - for( PartitionID block = 0; block < config.k; block++) { - double cur = overall_weights[block]; - if (cur > cur_max) { - cur_max = cur; - } - } + for( PartitionID block = 0; block < config.k; block++) { + double cur = overall_weights[block]; + if (cur > cur_max) { + cur_max = cur; + } + } - double percentage = cur_max/balance_part_weight; - return percentage; + double percentage = cur_max/balance_part_weight; + return percentage; } double distributed_quality_metrics::balance_second( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ) { - std::vector block_weights(config.k, 0); + std::vector block_weights(config.k, 0); - NodeWeight local_graph_vertex_weight = 0; + NodeWeight local_graph_vertex_weight = 0; - forall_local_nodes(G, n) { - PartitionID curPartition = G.getSecondPartitionIndex(n); - block_weights[curPartition] += G.getNodeWeight(n); - local_graph_vertex_weight += G.getNodeWeight(n); - } endfor + forall_local_nodes(G, n) { + PartitionID curPartition = G.getSecondPartitionIndex(n); + block_weights[curPartition] += G.getNodeWeight(n); + local_graph_vertex_weight += G.getNodeWeight(n); + } endfor - std::vector overall_weights(config.k, 0); - MPI_Allreduce(&block_weights[0], &overall_weights[0], config.k, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + std::vector overall_weights(config.k, 0); + MPI_Allreduce(&block_weights[0], &overall_weights[0], config.k, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - NodeWeight graph_vertex_weight = 0; - MPI_Allreduce(&local_graph_vertex_weight, &graph_vertex_weight, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + NodeWeight graph_vertex_weight = 0; + MPI_Allreduce(&local_graph_vertex_weight, &graph_vertex_weight, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - double balance_part_weight = ceil(graph_vertex_weight / (double)config.k); - double cur_max = -1; + double balance_part_weight = ceil(graph_vertex_weight / (double)config.k); + double cur_max = -1; - for( PartitionID block = 0; block < config.k; block++) { - double cur = overall_weights[block]; - if (cur > cur_max) { - cur_max = cur; - } - } + for( PartitionID block = 0; block < config.k; block++) { + double cur = overall_weights[block]; + if (cur > cur_max) { + cur_max = cur; + } + } - double percentage = cur_max/balance_part_weight; - return percentage; + double percentage = cur_max/balance_part_weight; + return percentage; } double distributed_quality_metrics::balance_load( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ) { - std::vector block_weights(config.k, 0); + std::vector block_weights(config.k, 0); - NodeWeight local_weight = 0; - forall_local_nodes(G, n) { - PartitionID curPartition = G.getNodeLabel(n); - block_weights[curPartition] += G.getNodeWeight(n)+G.getNodeDegree(n); - local_weight += G.getNodeWeight(n)+G.getNodeDegree(n); - } endfor + NodeWeight local_weight = 0; + forall_local_nodes(G, n) { + PartitionID curPartition = G.getNodeLabel(n); + block_weights[curPartition] += G.getNodeWeight(n)+G.getNodeDegree(n); + local_weight += G.getNodeWeight(n)+G.getNodeDegree(n); + } endfor - std::vector overall_weights(config.k, 0); - MPI_Allreduce(&block_weights[0], &overall_weights[0], config.k, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + std::vector overall_weights(config.k, 0); + MPI_Allreduce(&block_weights[0], &overall_weights[0], config.k, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - NodeWeight total_weight = 0; - MPI_Allreduce(&local_weight, &total_weight, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + NodeWeight total_weight = 0; + MPI_Allreduce(&local_weight, &total_weight, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - double balance_part_weight = ceil(total_weight / (double)config.k); - double cur_max = -1; + double balance_part_weight = ceil(total_weight / (double)config.k); + double cur_max = -1; - for( PartitionID block = 0; block < config.k; block++) { - double cur = overall_weights[block]; - if (cur > cur_max) { - cur_max = cur; - } - } + for( PartitionID block = 0; block < config.k; block++) { + double cur = overall_weights[block]; + if (cur > cur_max) { + cur_max = cur; + } + } - double percentage = cur_max/balance_part_weight; - return percentage; + double percentage = cur_max/balance_part_weight; + return percentage; } double distributed_quality_metrics::balance_load_dist( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ) { - int rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - std::vector block_weights(size, 0); - - NodeWeight local_weight = 0; - forall_local_nodes(G, n) { - block_weights[rank] += G.getNodeWeight(n)+G.getNodeDegree(n); - local_weight += G.getNodeWeight(n)+G.getNodeDegree(n); - } endfor - - std::vector overall_weights(size, 0); - MPI_Allreduce(&block_weights[0], &overall_weights[0], size, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - - NodeWeight total_weight = 0; - MPI_Allreduce(&local_weight, &total_weight, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - - double balance_part_weight = ceil(total_weight / (double)size); - double cur_max = -1; - - for( PartitionID block = 0; block < (PartitionID)size; block++) { - double cur = overall_weights[block]; - if (cur > cur_max) { - cur_max = cur; - } - } + int rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + + std::vector block_weights(size, 0); + + NodeWeight local_weight = 0; + forall_local_nodes(G, n) { + block_weights[rank] += G.getNodeWeight(n)+G.getNodeDegree(n); + local_weight += G.getNodeWeight(n)+G.getNodeDegree(n); + } endfor + + std::vector overall_weights(size, 0); + MPI_Allreduce(&block_weights[0], &overall_weights[0], size, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + + NodeWeight total_weight = 0; + MPI_Allreduce(&local_weight, &total_weight, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - double percentage = cur_max/balance_part_weight; - return percentage; + double balance_part_weight = ceil(total_weight / (double)size); + double cur_max = -1; + + for( PartitionID block = 0; block < (PartitionID)size; block++) { + double cur = overall_weights[block]; + if (cur > cur_max) { + cur_max = cur; + } + } + + double percentage = cur_max/balance_part_weight; + return percentage; } // measure the communication volume of the current graph distribution EdgeWeight distributed_quality_metrics::comm_vol( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ) { - EdgeWeight local_comm_vol = 0; int rank; - MPI_Comm_rank( communicator, &rank); - - std::vector block_volume(config.k, 0); - forall_local_nodes(G, node) { - std::vector block_incident(config.k, false); - PartitionID block = G.getNodeLabel( node ); - block_incident[block] = true; - int num_incident_blocks = 0; - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - PartitionID target_block = G.getNodeLabel( target ); - if(!block_incident[target_block]) { - block_incident[target_block] = true; - num_incident_blocks++; - } - } endfor - block_volume[block] += num_incident_blocks; - } endfor - - std::vector overall_weights(config.k, 0); - MPI_Allreduce(&block_volume[0], &overall_weights[0], config.k, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); - - if( rank == ROOT ) { - EdgeWeight total_comm_vol = 0; - for( PEID i = 0; i < (PEID)overall_weights.size(); i++) { - total_comm_vol += overall_weights[i]; - } - EdgeWeight max_comm_vol = *(std::max_element(overall_weights.begin(), overall_weights.end())); - EdgeWeight min_comm_vol = *(std::min_element(overall_weights.begin(), overall_weights.end())); - - std::cout << "log> total vol part " << total_comm_vol << std::endl; - std::cout << "log> max vol part " << max_comm_vol << std::endl; - std::cout << "log> min vol part " << min_comm_vol << std::endl; - std::cout << "log> vol part ratio " << max_comm_vol/(double)min_comm_vol << std::endl; - } - - return local_comm_vol; + EdgeWeight local_comm_vol = 0; int rank; + MPI_Comm_rank( communicator, &rank); + + std::vector block_volume(config.k, 0); + forall_local_nodes(G, node) { + std::vector block_incident(config.k, false); + PartitionID block = G.getNodeLabel( node ); + block_incident[block] = true; + int num_incident_blocks = 0; + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + PartitionID target_block = G.getNodeLabel( target ); + if(!block_incident[target_block]) { + block_incident[target_block] = true; + num_incident_blocks++; + } + } endfor + block_volume[block] += num_incident_blocks; + } endfor + + std::vector overall_weights(config.k, 0); + MPI_Allreduce(&block_volume[0], &overall_weights[0], config.k, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + + if( rank == ROOT ) { + EdgeWeight total_comm_vol = 0; + for( PEID i = 0; i < (PEID)overall_weights.size(); i++) { + total_comm_vol += overall_weights[i]; + } + EdgeWeight max_comm_vol = *(std::max_element(overall_weights.begin(), overall_weights.end())); + EdgeWeight min_comm_vol = *(std::min_element(overall_weights.begin(), overall_weights.end())); + + std::cout << "log> total vol part " << total_comm_vol << std::endl; + std::cout << "log> max vol part " << max_comm_vol << std::endl; + std::cout << "log> min vol part " << min_comm_vol << std::endl; + std::cout << "log> vol part ratio " << max_comm_vol/(double)min_comm_vol << std::endl; + } + + return local_comm_vol; } // measure the communication volume of the current graph distribution EdgeWeight distributed_quality_metrics::comm_vol_dist( parallel_graph_access & G, MPI_Comm communicator ) { - EdgeWeight local_comm_vol = 0; - int rank, size; - MPI_Comm_rank( communicator, &rank); - MPI_Comm_size( communicator, &size); - - forall_local_nodes(G, node) { - std::vector block_incident(size, false); - block_incident[rank] = true; - int num_incident_blocks = 0; - - forall_out_edges(G, e, node) { - NodeID target = G.getEdgeTarget(e); - if( !G.is_local_node( target ) ) { - PartitionID target_block = G.getTargetPE( target ); - if(!block_incident[target_block]) { - block_incident[target_block] = true; - num_incident_blocks++; - } - } - } endfor - local_comm_vol += num_incident_blocks; - } endfor - - EdgeWeight total_comm_vol = 0; - EdgeWeight max_comm_vol = 0; - EdgeWeight min_comm_vol = 0; - - MPI_Reduce(&local_comm_vol, &total_comm_vol, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, ROOT, communicator); - MPI_Reduce(&local_comm_vol, &max_comm_vol, 1, MPI_UNSIGNED_LONG_LONG, MPI_MAX, ROOT, communicator); - MPI_Reduce(&local_comm_vol, &min_comm_vol, 1, MPI_UNSIGNED_LONG_LONG, MPI_MIN, ROOT, communicator); - - if( rank == ROOT ) { - std::cout << "log> total vol currentdist " << total_comm_vol << std::endl; - std::cout << "log> max vol currentdist " << max_comm_vol << std::endl; - std::cout << "log> min vol currentdist " << min_comm_vol << std::endl; - std::cout << "log> vol dist currentratio " << max_comm_vol/(double)min_comm_vol << std::endl; + EdgeWeight local_comm_vol = 0; + int rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); + + forall_local_nodes(G, node) { + std::vector block_incident(size, false); + block_incident[rank] = true; + int num_incident_blocks = 0; + + forall_out_edges(G, e, node) { + NodeID target = G.getEdgeTarget(e); + if( !G.is_local_node( target ) ) { + PartitionID target_block = G.getTargetPE( target ); + if(!block_incident[target_block]) { + block_incident[target_block] = true; + num_incident_blocks++; } - - return local_comm_vol; + } + } endfor + local_comm_vol += num_incident_blocks; + } endfor + + EdgeWeight total_comm_vol = 0; + EdgeWeight max_comm_vol = 0; + EdgeWeight min_comm_vol = 0; + + MPI_Reduce(&local_comm_vol, &total_comm_vol, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, ROOT, communicator); + MPI_Reduce(&local_comm_vol, &max_comm_vol, 1, MPI_UNSIGNED_LONG_LONG, MPI_MAX, ROOT, communicator); + MPI_Reduce(&local_comm_vol, &min_comm_vol, 1, MPI_UNSIGNED_LONG_LONG, MPI_MIN, ROOT, communicator); + + if( rank == ROOT ) { + std::cout << "log> total vol currentdist " << total_comm_vol << std::endl; + std::cout << "log> max vol currentdist " << max_comm_vol << std::endl; + std::cout << "log> min vol currentdist " << min_comm_vol << std::endl; + std::cout << "log> vol dist currentratio " << max_comm_vol/(double)min_comm_vol << std::endl; + } + + return local_comm_vol; +} } - diff --git a/parallel/parallel_src/lib/tools/distributed_quality_metrics.h b/parallel/parallel_src/lib/tools/distributed_quality_metrics.h index 7b534f1f..25ddd767 100644 --- a/parallel/parallel_src/lib/tools/distributed_quality_metrics.h +++ b/parallel/parallel_src/lib/tools/distributed_quality_metrics.h @@ -11,23 +11,23 @@ #include "definitions.h" #include "data_structure/parallel_graph_access.h" #include "partition_config.h" - +namespace parhip { class distributed_quality_metrics { public: - distributed_quality_metrics(); - virtual ~distributed_quality_metrics(); + distributed_quality_metrics(); + virtual ~distributed_quality_metrics(); - EdgeWeight local_edge_cut( parallel_graph_access & G, int * partition_map, MPI_Comm communicator ); - EdgeWeight edge_cut( parallel_graph_access & G, MPI_Comm communicator ); - EdgeWeight edge_cut_second( parallel_graph_access & G, MPI_Comm communicator ); - NodeWeight local_max_block_weight( PPartitionConfig & config, parallel_graph_access & G, int * partition_map, MPI_Comm communicator ); - double balance( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ); - double balance_load( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ); - double balance_load_dist( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ); - double balance_second( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ); + EdgeWeight local_edge_cut( parallel_graph_access & G, int * partition_map, MPI_Comm communicator ); + EdgeWeight edge_cut( parallel_graph_access & G, MPI_Comm communicator ); + EdgeWeight edge_cut_second( parallel_graph_access & G, MPI_Comm communicator ); + NodeWeight local_max_block_weight( PPartitionConfig & config, parallel_graph_access & G, int * partition_map, MPI_Comm communicator ); + double balance( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ); + double balance_load( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ); + double balance_load_dist( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ); + double balance_second( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ); EdgeWeight comm_vol( PPartitionConfig & config, parallel_graph_access & G, MPI_Comm communicator ); EdgeWeight comm_vol_dist( parallel_graph_access & G, MPI_Comm communicator ); }; - +} #endif /* end of include guard: DISTRIBUTED_QUALITY_METRICS_UAVSEXBT */ diff --git a/parallel/parallel_src/lib/tools/helpers.h b/parallel/parallel_src/lib/tools/helpers.h index 185f413f..982dd784 100644 --- a/parallel/parallel_src/lib/tools/helpers.h +++ b/parallel/parallel_src/lib/tools/helpers.h @@ -12,7 +12,7 @@ #include #include #include - +namespace parhip { class helpers { public: helpers() {}; @@ -55,5 +55,5 @@ inline bool file_exists(const std::string& name) { return false; } } - +} #endif /* end of include guard: HELPERS_ZUTE7MAJ */ diff --git a/parallel/parallel_src/lib/tools/random_functions.cpp b/parallel/parallel_src/lib/tools/random_functions.cpp index 46c909bf..d47e5c98 100644 --- a/parallel/parallel_src/lib/tools/random_functions.cpp +++ b/parallel/parallel_src/lib/tools/random_functions.cpp @@ -6,7 +6,7 @@ *****************************************************************************/ #include "random_functions.h" - +namespace parhip { MersenneTwister random_functions::m_mt; int random_functions::m_seed = 0; @@ -15,3 +15,4 @@ random_functions::random_functions() { random_functions::~random_functions() { } +} \ No newline at end of file diff --git a/parallel/parallel_src/lib/tools/random_functions.h b/parallel/parallel_src/lib/tools/random_functions.h index 30168add..022ccfa6 100644 --- a/parallel/parallel_src/lib/tools/random_functions.h +++ b/parallel/parallel_src/lib/tools/random_functions.h @@ -14,153 +14,153 @@ #include "definitions.h" #include "partition_config.h" - +namespace parhip { typedef std::mt19937 MersenneTwister; class random_functions { - public: - random_functions(); - virtual ~random_functions(); - - template - static void circular_permutation(std::vector & vec) { - if(vec.size() < 2) return; - for( ULONG i = 0; i < vec.size(); i++) { - vec[i] = i; - } - - ULONG size = vec.size(); - std::uniform_int_distribution A(0,size-1); - std::uniform_int_distribution B(0,size-1); - - for( ULONG i = 0; i < size; i++) { - ULONG posA = A(m_mt); - ULONG posB = B(m_mt); - - while(posB == posA) { - posB = B(m_mt); - } - - if( posA != vec[posB] && posB != vec[posA]) { - std::swap(vec[posA], vec[posB]); - } - } - - } - - template - static void permutate_vector_fast(std::vector & vec, bool init) { - if(init) { - for( ULONG i = 0; i < vec.size(); i++) { - vec[i] = i; - } - } - - if(vec.size() < 10) return; - - int distance = 20; - std::uniform_int_distribution A(0, distance); - ULONG size = vec.size()-4; - for( ULONG i = 0; i < size; i++) { - ULONG posA = i; - ULONG posB = (posA + A(m_mt))%size; - std::swap(vec[posA], vec[posB]); - std::swap(vec[posA+1], vec[posB+1]); - std::swap(vec[posA+2], vec[posB+2]); - std::swap(vec[posA+3], vec[posB+3]); - } - } +public: + random_functions(); + virtual ~random_functions(); + + template + static void circular_permutation(std::vector & vec) { + if(vec.size() < 2) return; + for( ULONG i = 0; i < vec.size(); i++) { + vec[i] = i; + } - template - static void permutate_vector_good(std::vector & vec, bool init) { - if(init) { - for( ULONG i = 0; i < vec.size(); i++) { - vec[i] = (sometype)i; - } - } - - if(vec.size() < 10) { - permutate_vector_good_small(vec); - return; - } - ULONG size = vec.size(); - std::uniform_int_distribution A(0,size - 4); - std::uniform_int_distribution B(0,size - 4); - - for( ULONG i = 0; i < size; i++) { - ULONG posA = A(m_mt); - ULONG posB = B(m_mt); - std::swap(vec[posA], vec[posB]); - std::swap(vec[posA+1], vec[posB+1]); - std::swap(vec[posA+2], vec[posB+2]); - std::swap(vec[posA+3], vec[posB+3]); - - } + ULONG size = vec.size(); + std::uniform_int_distribution A(0,size-1); + std::uniform_int_distribution B(0,size-1); + + for( ULONG i = 0; i < size; i++) { + ULONG posA = A(m_mt); + ULONG posB = B(m_mt); + + while(posB == posA) { + posB = B(m_mt); } - template - static void permutate_vector_good_small(std::vector & vec) { - if(vec.size() < 2) return; - ULONG size = vec.size(); - std::uniform_int_distribution A(0,size-1); - std::uniform_int_distribution B(0,size-1); - - for( ULONG i = 0; i < size; i++) { - ULONG posA = A(m_mt); - ULONG posB = B(m_mt); - std::swap(vec[posA], vec[posB]); - } + if( posA != vec[posB] && posB != vec[posA]) { + std::swap(vec[posA], vec[posB]); } + } - template - static void permutate_entries(const PPartitionConfig & partition_config, - std::vector & vec, - bool init) { - if(init) { - for( ULONG i = 0; i < vec.size(); i++) { - vec[i] = i; - } - } - - switch(partition_config.permutation_quality) { - case PERMUTATION_QUALITY_NONE: break; - case PERMUTATION_QUALITY_FAST: permutate_vector_fast(vec, false); break; - case PERMUTATION_QUALITY_GOOD: permutate_vector_good(vec, false); break; - } + } + template + static void permutate_vector_fast(std::vector & vec, bool init) { + if(init) { + for( ULONG i = 0; i < vec.size(); i++) { + vec[i] = i; } + } - static bool nextBool() { - std::bernoulli_distribution bernoulli(0.5); - return bernoulli(m_mt); + if(vec.size() < 10) return; + + int distance = 20; + std::uniform_int_distribution A(0, distance); + ULONG size = vec.size()-4; + for( ULONG i = 0; i < size; i++) { + ULONG posA = i; + ULONG posB = (posA + A(m_mt))%size; + std::swap(vec[posA], vec[posB]); + std::swap(vec[posA+1], vec[posB+1]); + std::swap(vec[posA+2], vec[posB+2]); + std::swap(vec[posA+3], vec[posB+3]); } + } - //including lb and rb - template - static sometype nextInt(sometype lb, sometype rb) { - std::uniform_int_distribution A(lb,rb); - return A(m_mt); + template + static void permutate_vector_good(std::vector & vec, bool init) { + if(init) { + for( ULONG i = 0; i < vec.size(); i++) { + vec[i] = (sometype)i; } + } + if(vec.size() < 10) { + permutate_vector_good_small(vec); + return; + } + ULONG size = vec.size(); + std::uniform_int_distribution A(0,size - 4); + std::uniform_int_distribution B(0,size - 4); + + for( ULONG i = 0; i < size; i++) { + ULONG posA = A(m_mt); + ULONG posB = B(m_mt); + std::swap(vec[posA], vec[posB]); + std::swap(vec[posA+1], vec[posB+1]); + std::swap(vec[posA+2], vec[posB+2]); + std::swap(vec[posA+3], vec[posB+3]); - static double nextDouble(double lb, double rb) { - double rnbr = (double) rand() / (double) RAND_MAX; // rnd in 0,1 - double length = rb - lb; - rnbr *= length; - rnbr += lb; - - return rnbr; + } + } + + template + static void permutate_vector_good_small(std::vector & vec) { + if(vec.size() < 2) return; + ULONG size = vec.size(); + std::uniform_int_distribution A(0,size-1); + std::uniform_int_distribution B(0,size-1); + + for( ULONG i = 0; i < size; i++) { + ULONG posA = A(m_mt); + ULONG posB = B(m_mt); + std::swap(vec[posA], vec[posB]); + } + } + + template + static void permutate_entries(const PPartitionConfig & partition_config, + std::vector & vec, + bool init) { + if(init) { + for( ULONG i = 0; i < vec.size(); i++) { + vec[i] = i; + } } - static void setSeed(int seed) { - m_seed = seed; - srand(seed); - m_mt.seed(m_seed); + switch(partition_config.permutation_quality) { + case PERMUTATION_QUALITY_NONE: break; + case PERMUTATION_QUALITY_FAST: permutate_vector_fast(vec, false); break; + case PERMUTATION_QUALITY_GOOD: permutate_vector_good(vec, false); break; } - private: - static int m_seed; - static MersenneTwister m_mt; -}; + } + + static bool nextBool() { + std::bernoulli_distribution bernoulli(0.5); + return bernoulli(m_mt); + } + + //including lb and rb + template + static sometype nextInt(sometype lb, sometype rb) { + std::uniform_int_distribution A(lb,rb); + return A(m_mt); + } + + static double nextDouble(double lb, double rb) { + double rnbr = (double) rand() / (double) RAND_MAX; // rnd in 0,1 + double length = rb - lb; + rnbr *= length; + rnbr += lb; + + return rnbr; + } + + static void setSeed(int seed) { + m_seed = seed; + srand(seed); + m_mt.seed(m_seed); + } + +private: + static int m_seed; + static MersenneTwister m_mt; +}; +} #endif /* end of include guard: RANDOM_FUNCTIONS_RMEPKWYT */ diff --git a/parallel/parallel_src/lib/tools/timer.h b/parallel/parallel_src/lib/tools/timer.h index c5abe388..1826afa4 100644 --- a/parallel/parallel_src/lib/tools/timer.h +++ b/parallel/parallel_src/lib/tools/timer.h @@ -11,31 +11,31 @@ #include #include #include - +namespace parhip { class timer { - public: - timer() { - m_start = timestamp(); - } - - void restart() { - m_start = timestamp(); - } - - double elapsed() { - return timestamp()-m_start; - } - - private: - - /** Returns a timestamp ('now') in seconds (incl. a fractional part). */ - inline double timestamp() { - struct timeval tp; - gettimeofday(&tp, NULL); - return double(tp.tv_sec) + tp.tv_usec / 1000000.; - } - - double m_start; -}; - +public: + timer() { + m_start = timestamp(); + } + + void restart() { + m_start = timestamp(); + } + + double elapsed() { + return timestamp()-m_start; + } + +private: + + /** Returns a timestamp ('now') in seconds (incl. a fractional part). */ + inline double timestamp() { + struct timeval tp; + gettimeofday(&tp, NULL); + return double(tp.tv_sec) + tp.tv_usec / 1000000.; + } + + double m_start; +}; +} #endif /* end of include guard: TIMER_9KPDEP */ From 728dd859972790ac9a9e51e3bb1a66cceac65673 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Fri, 11 Oct 2024 16:48:50 +0100 Subject: [PATCH 52/64] Unity builds working --- CMakeLists.txt | 28 ++++++------- cmake/KaHIPSettings.cmake | 13 ++++++ cmake/Linker.cmake | 31 ++++++++++++++ cmake/StandardProjectSettings.cmake | 40 +++++++++++++++++++ .../lib/partition/coarsening/contraction.cpp | 8 +--- .../lib/partition/coarsening/contraction.h | 4 +- .../2way_fm_refinement/two_way_fm.cpp | 2 +- .../complete_boundary.h | 3 +- .../parallel_contraction_mpi_test.cpp | 1 + .../parallel_contraction_test.cpp | 1 + 10 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 cmake/Linker.cmake create mode 100644 cmake/StandardProjectSettings.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 28a1a549..9cff4d76 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,7 +254,7 @@ add_executable(kaffpa app/kaffpa.cpp) target_link_libraries(kaffpa PRIVATE libkaffpa libmapping) target_compile_definitions(kaffpa PRIVATE "-DMODE_KAFFPA") target_link_libraries(kaffpa PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(kaffpa PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(kaffpa PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS kaffpa DESTINATION bin) if (USE_TCMALLOC) find_library(TCMALLOC_LIB tcmalloc) @@ -270,7 +270,7 @@ add_executable(global_multisection app/global_multisection.cpp) target_link_libraries(global_multisection PRIVATE libkaffpa libmapping) target_compile_definitions(global_multisection PRIVATE "-DMODE_KAFFPA -DMODE_GLOBALMS") target_link_libraries(global_multisection PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(global_multisection PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(global_multisection PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS global_multisection DESTINATION bin) if (USE_TCMALLOC) find_library(TCMALLOC_LIB tcmalloc) @@ -286,7 +286,7 @@ add_executable(evaluator app/evaluator.cpp) target_link_libraries(evaluator PRIVATE libkaffpa libmapping) target_compile_definitions(evaluator PRIVATE "-DMODE_EVALUATOR") target_link_libraries(evaluator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(evaluator PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(evaluator PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS evaluator DESTINATION bin) add_executable(edge_evaluator app/edge_evaluator.cpp) @@ -301,21 +301,21 @@ add_executable(node_separator app/node_separator_ml.cpp) target_link_libraries(node_separator PRIVATE libkaffpa libmapping) target_compile_definitions(node_separator PRIVATE "-DMODE_NODESEP") target_link_libraries(node_separator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(node_separator PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(node_separator PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS node_separator DESTINATION bin) add_executable(label_propagation app/label_propagation.cpp) target_link_libraries(label_propagation PRIVATE libkaffpa libmapping) target_compile_definitions(label_propagation PRIVATE "-DMODE_LABELPROPAGATION") target_link_libraries(label_propagation PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(label_propagation PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(label_propagation PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS label_propagation DESTINATION bin) add_executable(partition_to_vertex_separator app/partition_to_vertex_separator.cpp) target_link_libraries(partition_to_vertex_separator PRIVATE libkaffpa libmapping) target_compile_definitions(partition_to_vertex_separator PRIVATE "-DMODE_PARTITIONTOVERTEXSEPARATOR") target_link_libraries(partition_to_vertex_separator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(partition_to_vertex_separator PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(partition_to_vertex_separator PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS partition_to_vertex_separator DESTINATION bin) add_executable(interface_test misc/example_library_call/interface_test.cpp interface/kaHIP_interface.cpp) @@ -323,7 +323,7 @@ target_link_libraries(interface_test PRIVATE libkaffpa libmapping libnodeorderin target_include_directories(interface_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(interface_test PRIVATE "-DMODE_KAFFPA") target_link_libraries(interface_test PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(interface_test PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(interface_test PRIVATE kahip_headers kahip_options kahip_warnings argtable3) if(metis_FOUND) target_link_libraries(interface_test PUBLIC metis GKlib) endif() @@ -334,7 +334,7 @@ if(NOT NOMPI) target_link_libraries(kaffpaE PRIVATE libkaffpa libmapping libkaffpa_parallel) target_compile_definitions(kaffpaE PRIVATE "-DMODE_KAFFPAE") target_link_libraries(kaffpaE PRIVATE MPI::MPI_CXX OpenMP::OpenMP_CXX) - target_link_libraries(kaffpaE PRIVATE kahip_headers kahip_options kahip_warnings) + target_link_libraries(kaffpaE PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS kaffpaE DESTINATION bin) endif() @@ -342,21 +342,21 @@ add_executable(graphchecker app/graphchecker.cpp) target_link_libraries(graphchecker PRIVATE libkaffpa libmapping) target_compile_definitions(graphchecker PRIVATE "-DMODE_GRAPHCHECKER") target_link_libraries(graphchecker PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(graphchecker PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(graphchecker PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS graphchecker DESTINATION bin) add_executable(edge_partitioning app/spac.cpp) target_link_libraries(edge_partitioning PRIVATE libkaffpa libmapping libspac) target_compile_definitions(edge_partitioning PRIVATE "-DMODE_KAFFPA") target_link_libraries(edge_partitioning PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(edge_partitioning PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(edge_partitioning PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS edge_partitioning DESTINATION bin) add_executable(node_ordering app/node_ordering.cpp) target_link_libraries(node_ordering PRIVATE libkaffpa libnodeordering) target_compile_definitions(node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING") target_link_libraries(node_ordering PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(node_ordering PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(node_ordering PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS node_ordering DESTINATION bin) if(metis_FOUND) @@ -364,7 +364,7 @@ if(metis_FOUND) target_link_libraries(fast_node_ordering PRIVATE libkaffpa libmapping libnodeordering) target_compile_definitions(fast_node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING -DFASTORDERING") target_link_libraries(fast_node_ordering PRIVATE OpenMP::OpenMP_CXX metis GKlib) - target_link_libraries(fast_node_ordering PRIVATE kahip_headers kahip_options kahip_warnings) + target_link_libraries(fast_node_ordering PRIVATE kahip_headers kahip_options kahip_warnings argtable3) install(TARGETS fast_node_ordering DESTINATION bin) endif() @@ -375,7 +375,7 @@ target_link_libraries(kahip PRIVATE libkaffpa libmapping libnodeordering libspac target_include_directories(kahip PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(kahip PRIVATE "-DMODE_KAFFPA") target_link_libraries(kahip PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(kahip PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(kahip PRIVATE kahip_headers kahip_options kahip_warnings argtable3) if(metis_FOUND) target_link_libraries(kahip PRIVATE metis GKlib) endif() @@ -388,7 +388,7 @@ target_include_directories(kahip_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inter target_compile_definitions(kahip_static PRIVATE "-DMODE_KAFFPA") target_link_libraries(kahip_static PRIVATE OpenMP::OpenMP_CXX) set_target_properties(kahip_static PROPERTIES PUBLIC_HEADER interface/kaHIP_interface.h) -target_link_libraries(kahip_static PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries(kahip_static PRIVATE kahip_headers kahip_options kahip_warnings argtable3) if(metis_FOUND) target_link_libraries(kahip_static PRIVATE metis GKlib) endif() diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index 948f8e4a..9a7ad784 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -8,6 +8,8 @@ include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) kahip_supports_sanitizers() option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) +option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" ON) +option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" OFF) option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) option(kahip_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) @@ -20,6 +22,10 @@ option(kahip_ENABLE_CPPCHECK "Enable cpp-check analysis" OFF) option(kahip_ENABLE_IWYU "Enable `include_what_you_use`" OFF) option(kahip_ENABLE_CACHE "Enable ccache" OFF) +if (kahip_ENABLE_UNITY_BUILD) + set(CMAKE_UNITY_BUILD ON) +endif () + if(kahip_ENABLE_IPO) kahip_enable_ipo() @@ -28,6 +34,13 @@ endif() add_library(kahip_warnings INTERFACE) add_library(kahip_options INTERFACE) +include(${CMAKE_SOURCE_DIR}/cmake/StandardProjectSettings.cmake) + +if(kahip_ENABLE_USER_LINKER) + include(${CMAKE_SOURCE_DIR}/cmake/Linker.cmake) + kahip_configure_linker(kahip_options) +endif() + kahip_set_project_warnings( kahip_warnings ${kahip_WARNINGS_AS_ERRORS} diff --git a/cmake/Linker.cmake b/cmake/Linker.cmake new file mode 100644 index 00000000..1b0b06eb --- /dev/null +++ b/cmake/Linker.cmake @@ -0,0 +1,31 @@ +macro(kahip_configure_linker project_name) + include(CheckCXXCompilerFlag) + + set(USER_LINKER_OPTION + "lld" + CACHE STRING "Linker to be used") + set(USER_LINKER_OPTION_VALUES "lld" "gold" "bfd" "mold") + set_property(CACHE USER_LINKER_OPTION PROPERTY STRINGS ${USER_LINKER_OPTION_VALUES}) + list( + FIND + USER_LINKER_OPTION_VALUES + ${USER_LINKER_OPTION} + USER_LINKER_OPTION_INDEX) + + if(${USER_LINKER_OPTION_INDEX} EQUAL -1) + message( + STATUS + "Using custom linker: '${USER_LINKER_OPTION}', explicitly supported entries are ${USER_LINKER_OPTION_VALUES}") + endif() + + if(NOT myproject_ENABLE_USER_LINKER) + return() + endif() + + set(LINKER_FLAG "-fuse-ld=${USER_LINKER_OPTION}") + + check_cxx_compiler_flag(${LINKER_FLAG} CXX_SUPPORTS_USER_LINKER) + if(CXX_SUPPORTS_USER_LINKER) + target_compile_options(${project_name} INTERFACE ${LINKER_FLAG}) + endif() +endmacro() diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake new file mode 100644 index 00000000..5e4f4025 --- /dev/null +++ b/cmake/StandardProjectSettings.cmake @@ -0,0 +1,40 @@ +# Set a default build type if none was specified +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") + set(CMAKE_BUILD_TYPE + RelWithDebInfo + CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui, ccmake + set_property( + CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS + "Debug" + "Release" + "MinSizeRel" + "RelWithDebInfo") +endif() + +# Generate compile_commands.json to make it easier to work with clang based tools +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Enhance error reporting and compiler messages +if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + if(WIN32) + # On Windows cuda nvcc uses cl and not clang + add_compile_options($<$:-fcolor-diagnostics> $<$:-fcolor-diagnostics>) + else() + add_compile_options(-fcolor-diagnostics) + endif() +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(WIN32) + # On Windows cuda nvcc uses cl and not gcc + add_compile_options($<$:-fdiagnostics-color=always> + $<$:-fdiagnostics-color=always>) + else() + add_compile_options(-fdiagnostics-color=always) + endif() +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER 1900) + add_compile_options(/diagnostics:column) +else() + message(STATUS "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler.") +endif() diff --git a/parallel/modified_kahip/lib/partition/coarsening/contraction.cpp b/parallel/modified_kahip/lib/partition/coarsening/contraction.cpp index e72dc103..0b72999a 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/contraction.cpp +++ b/parallel/modified_kahip/lib/partition/coarsening/contraction.cpp @@ -8,14 +8,8 @@ #include "contraction.h" #include "../uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h" #include "macros_assertions.h" -namespace kahip::modified { -contraction::contraction() { - -} -contraction::~contraction() { - -} +namespace kahip::modified { // for documentation see technical reports of christian schulz void contraction::contract(const PartitionConfig & partition_config, diff --git a/parallel/modified_kahip/lib/partition/coarsening/contraction.h b/parallel/modified_kahip/lib/partition/coarsening/contraction.h index e9003cb9..80a3a7d4 100644 --- a/parallel/modified_kahip/lib/partition/coarsening/contraction.h +++ b/parallel/modified_kahip/lib/partition/coarsening/contraction.h @@ -17,8 +17,8 @@ typedef NodeID Regions; class contraction { public: - contraction(); - virtual ~contraction(); + contraction() = default; + virtual ~contraction() = default; void contract(const PartitionConfig & partition_config, graph_access & finer, diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp index c020a741..2b17d6a2 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp @@ -444,5 +444,5 @@ bool two_way_fm::assert_directed_boundary_condition(graph_access & G, complete_b ASSERT_TRUE(assert_every_boundary_nodes(G, boundary.getDirectedBoundary(rhs, lhs, rhs) , rhs, lhs)); return true; } -} #endif +} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h index e651f741..ab32304e 100644 --- a/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h +++ b/parallel/modified_kahip/lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.h @@ -594,7 +594,6 @@ inline bool complete_boundary::assert_boundaries_are_bnodes() { return true; } -} #endif // #ifndef NDEBUG - +} #endif /* end of include guard: COMPLETE_BOUNDARY_URZZFDEI */ diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp index 79ac7d6f..c435b013 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_mpi_test.cpp @@ -12,6 +12,7 @@ #include "communication/mpi_tools.h" #include "parallel_contraction_projection/parallel_contraction.h" +using namespace parhip; struct MyTestType{ int a; diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index 6a9c5b13..becaeafe 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -13,6 +13,7 @@ #include "parallel_contraction_projection/parallel_contraction.h" #include "communication/mpi_tools.h" +using namespace parhip; TEST_CASE("flattening vector of messages", "[unit][mpi]") { SECTION("Empty Vector") { From e6404d46968428cf5eb69d6509815da2c59f8379 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 15 Oct 2024 11:34:30 +0100 Subject: [PATCH 53/64] Fixing compiling flags --- CMakeLists.txt | 46 +------------------ cmake/KaHIPSettings.cmake | 20 +++++++- cmake/StandardProjectSettings.cmake | 6 +++ parallel/modified_kahip/CMakeLists.txt | 3 +- .../lib/tools/random_functions.cpp | 18 -------- .../lib/tools/random_functions.h | 17 +++---- parallel/parallel_src/CMakeLists.txt | 3 +- parallel/parallel_src/app/configuration.h | 17 ++++--- parallel/parallel_src/app/parse_parameters.h | 33 ++++++++----- .../lib/communication/mpi_tools.cpp | 3 +- .../lib/communication/mpi_types.h | 4 +- parallel/parallel_src/lib/definitions.h | 38 ++++++++------- .../distributed_evolutionary_partitioning.cpp | 14 +++--- .../initial_partitioning.cpp | 3 +- .../parallel_label_compress/node_ordering.h | 10 ++-- .../lib/tools/distributed_quality_metrics.cpp | 8 +--- .../lib/tools/distributed_quality_metrics.h | 3 -- parallel/parallel_src/lib/tools/helpers.h | 5 +- .../lib/tools/random_functions.cpp | 18 -------- .../parallel_src/lib/tools/random_functions.h | 28 +++++------ 20 files changed, 116 insertions(+), 181 deletions(-) delete mode 100644 parallel/modified_kahip/lib/tools/random_functions.cpp delete mode 100644 parallel/parallel_src/lib/tools/random_functions.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cff4d76..3e9f7a03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,58 +1,14 @@ -cmake_minimum_required(VERSION 3.25..28) +cmake_minimum_required(VERSION 3.26..28) include(CheckCXXCompilerFlag) - project(KaHIP LANGUAGES C CXX) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(KaHIPSettings) -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -# if no build mode is specified build in release mode -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Release") -endif() - # --march=nativeflag option(NONATIVEOPTIMIZATIONS "Disable --march=native optimizations" OFF) -# tweak compiler flags -#CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) -#if(COMPILER_SUPPORTS_FUNROLL_LOOPS) -# add_definitions(-funroll-loops) -#endif() -#CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) -#if(COMPILER_SUPPORTS_FNOSTACKLIMITS) -# add_definitions(-fno-stack-limit) -#endif() -#CHECK_CXX_COMPILER_FLAG(-Wall COMPILER_SUPPORTS_WALL) -#if(COMPILER_SUPPORTS_WALL) -# add_definitions(-Wall) -#endif() -#CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) -#if(COMPILER_SUPPORTS_MARCH_NATIVE) -# if( NOT NONATIVEOPTIMIZATIONS ) -# add_definitions(-march=native) -# endif() -#endif() -#CHECK_CXX_COMPILER_FLAG(-fpermissive COMPILER_SUPPORTS_FPERMISSIVE) -#if(COMPILER_SUPPORTS_FPERMISSIVE) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") -#endif() -#CHECK_CXX_COMPILER_FLAG(-Wno-unused-result COMPILER_SUPPORTS_UNUSED) -#if(COMPILER_SUPPORTS_UNUSED) -# add_definitions(-Wno-unused-result) -#endif() -#CHECK_CXX_COMPILER_FLAG(-Wno-sign-compare COMPILER_SUPPORTS_NOSIGNCOMP) -#if(COMPILER_SUPPORTS_NOSIGNCOMP) -# add_definitions(-Wno-sign-compare) -#endif() - # Check dependencies find_package(OpenMP) diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index 9a7ad784..bb4d560d 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -9,7 +9,7 @@ kahip_supports_sanitizers() option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" ON) -option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" OFF) +option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" ON) option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) option(kahip_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) @@ -70,4 +70,20 @@ endif() if(kahip_ENABLE_IWYU) kahip_enable_include_what_you_use() -endif() \ No newline at end of file +endif() + +# tweak compiler flags +target_compile_features(kahip_options INTERFACE cxx_std_20) +CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) +target_compile_options(kahip_options INTERFACE + $<$:-funroll-loops> +) +CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) +target_compile_options(kahip_options INTERFACE + $<$:-funroll-loops> +) +CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) +target_compile_options(kahip_options INTERFACE + $<$,$>,$,$>>:-march=native> +) + diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake index 5e4f4025..e9a3f706 100644 --- a/cmake/StandardProjectSettings.cmake +++ b/cmake/StandardProjectSettings.cmake @@ -16,6 +16,12 @@ endif() # Generate compile_commands.json to make it easier to work with clang based tools set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) # Enhance error reporting and compiler messages if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") diff --git a/parallel/modified_kahip/CMakeLists.txt b/parallel/modified_kahip/CMakeLists.txt index b60c5ebe..97afdea9 100644 --- a/parallel/modified_kahip/CMakeLists.txt +++ b/parallel/modified_kahip/CMakeLists.txt @@ -11,8 +11,7 @@ set(LIBMODIFIED_KAFFPA_SOURCE_FILES lib/algorithms/topological_sort.cpp lib/io/graph_io.cpp lib/tools/quality_metrics.cpp - lib/tools/random_functions.cpp - lib/tools/graph_extractor.cpp + lib/tools/graph_extractor.cpp lib/tools/misc.cpp lib/tools/partition_snapshooter.cpp lib/partition/graph_partitioner.cpp diff --git a/parallel/modified_kahip/lib/tools/random_functions.cpp b/parallel/modified_kahip/lib/tools/random_functions.cpp deleted file mode 100644 index ad5ddca3..00000000 --- a/parallel/modified_kahip/lib/tools/random_functions.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/****************************************************************************** - * random_functions.cpp - * * - * Source of KaHIP -- Karlsruhe High Quality Partitioning. - * Christian Schulz - *****************************************************************************/ - -#include "random_functions.h" -namespace kahip::modified { -MersenneTwister random_functions::m_mt; -int random_functions::m_seed = 0; - -random_functions::random_functions() { -} - -random_functions::~random_functions() { -} -} \ No newline at end of file diff --git a/parallel/modified_kahip/lib/tools/random_functions.h b/parallel/modified_kahip/lib/tools/random_functions.h index 39d218bf..d6345d00 100644 --- a/parallel/modified_kahip/lib/tools/random_functions.h +++ b/parallel/modified_kahip/lib/tools/random_functions.h @@ -20,8 +20,6 @@ using MersenneTwister = std::mt19937; class random_functions { public: - random_functions(); - virtual ~random_functions(); template static void circular_permutation(std::vector & vec) { @@ -144,23 +142,20 @@ class random_functions { } static double nextDouble(double lb, double rb) { - double rnbr = (double) rand() / (double) RAND_MAX; // rnd in 0,1 - double length = rb - lb; - rnbr *= length; - rnbr += lb; - - return rnbr; + std::uniform_real_distribution A(lb,rb); + return A(m_mt); } static void setSeed(int seed) { m_seed = seed; srand(seed); - m_mt.seed(m_seed); + std::seed_seq mt_seed{seed}; + m_mt.seed(mt_seed); } private: - static int m_seed; - static MersenneTwister m_mt; + inline static int m_seed = 0; + inline static MersenneTwister m_mt; }; } #endif /* end of include guard: RANDOM_FUNCTIONS_RMEPKWYT */ diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index e3019e9a..74658f47 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -34,8 +34,7 @@ set(LIBPARALLEL_SOURCE_FILES lib/communication/dummy_operations.cpp lib/io/parallel_graph_io.cpp lib/io/parallel_vector_io.cpp - lib/tools/random_functions.cpp - lib/tools/distributed_quality_metrics.cpp) + lib/tools/distributed_quality_metrics.cpp) add_library(parallel ${LIBPARALLEL_SOURCE_FILES}) target_link_libraries(parallel PRIVATE kahip_options kahip_warnings) target_link_libraries(parallel PUBLIC parhip_headers) diff --git a/parallel/parallel_src/app/configuration.h b/parallel/parallel_src/app/configuration.h index 63b42e9f..827466d6 100644 --- a/parallel/parallel_src/app/configuration.h +++ b/parallel/parallel_src/app/configuration.h @@ -24,7 +24,8 @@ class configuration { }; inline void configuration::ultrafast( PPartitionConfig & partition_config ) { - partition_config.initial_partitioning_algorithm = KAFFPAEULTRAFASTSNW; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAEULTRAFASTSNW; partition_config.no_refinement_in_last_iteration = true; partition_config.stop_factor = 18000; partition_config.num_vcycles = 1; @@ -32,13 +33,15 @@ inline void configuration::ultrafast( PPartitionConfig & partition_config ) { inline void configuration::fast( PPartitionConfig & partition_config ) { - partition_config.initial_partitioning_algorithm = KAFFPAEULTRAFASTSNW; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAEULTRAFASTSNW; partition_config.no_refinement_in_last_iteration = true; partition_config.stop_factor = 18000; } inline void configuration::eco( PPartitionConfig & partition_config ) { - partition_config.initial_partitioning_algorithm = KAFFPAEFASTSNW; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAEFASTSNW; partition_config.no_refinement_in_last_iteration = true; partition_config.stop_factor = 18000; int size; MPI_Comm_size(MPI_COMM_WORLD, &size); @@ -48,7 +51,8 @@ inline void configuration::eco( PPartitionConfig & partition_config ) { } inline void configuration::strong( PPartitionConfig & partition_config ) { - partition_config.initial_partitioning_algorithm = KAFFPAESTRONGSNW; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAESTRONGSNW; } inline void configuration::standard( PPartitionConfig & partition_config ) { @@ -67,12 +71,13 @@ inline void configuration::standard( PPartitionConfig & partition_config ) { partition_config.label_iterations_coarsening = 3; partition_config.label_iterations_refinement = 6; partition_config.cluster_coarsening_factor = 14; - partition_config.initial_partitioning_algorithm = KAFFPAEFASTSNW; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAEFASTSNW; partition_config.stop_factor = 14000; partition_config.vcycle = false; partition_config.num_vcycles = 2; partition_config.num_tries = 10; - partition_config.node_ordering = DEGREE_NODEORDERING; + partition_config.node_ordering = NodeOrderingType::DEGREE_NODEORDERING; partition_config.no_refinement_in_last_iteration = false; partition_config.ht_fill_factor = 1.6; partition_config.eco = false; diff --git a/parallel/parallel_src/app/parse_parameters.h b/parallel/parallel_src/app/parse_parameters.h index 737b2eb9..ec92c286 100644 --- a/parallel/parallel_src/app/parse_parameters.h +++ b/parallel/parallel_src/app/parse_parameters.h @@ -243,19 +243,26 @@ int parse_parameters(int argn, char **argv, if(initial_partitioning_algorithm->count > 0) { if(strcmp("kaffpaEstrong", initial_partitioning_algorithm->sval[0]) == 0) { - partition_config.initial_partitioning_algorithm = KAFFPAESTRONG; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAESTRONG; } else if (strcmp("kaffpaEeco",initial_partitioning_algorithm->sval[0]) == 0) { - partition_config.initial_partitioning_algorithm = KAFFPAEECO; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAEECO; } else if (strcmp("kaffpaEfast", initial_partitioning_algorithm->sval[0]) == 0) { - partition_config.initial_partitioning_algorithm = KAFFPAEFAST; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAEFAST; } else if (strcmp("fastsocial", initial_partitioning_algorithm->sval[0]) == 0) { - partition_config.initial_partitioning_algorithm = KAFFPAEFASTSNW; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAEFASTSNW; } else if (strcmp("ecosocial", initial_partitioning_algorithm->sval[0]) == 0) { - partition_config.initial_partitioning_algorithm = KAFFPAEECOSNW; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAEECOSNW; } else if (strcmp("strongsocial", initial_partitioning_algorithm->sval[0]) == 0) { - partition_config.initial_partitioning_algorithm = KAFFPAESTRONGSNW; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::KAFFPAESTRONGSNW; } else if (strcmp("random", initial_partitioning_algorithm->sval[0]) == 0) { - partition_config.initial_partitioning_algorithm = RANDOMIP; + partition_config.initial_partitioning_algorithm = + InitialPartitioningAlgorithm::RANDOMIP; } else { fprintf(stderr, "Invalid initial partitioning algorithm: \"%s\"\n", initial_partitioning_algorithm->sval[0]); arg_freetable(argtable_fordeletion, sizeof(argtable_fordeletion) / sizeof(argtable_fordeletion[0])); @@ -265,13 +272,17 @@ int parse_parameters(int argn, char **argv, if(node_ordering->count > 0) { if(strcmp("random", node_ordering->sval[0]) == 0) { - partition_config.node_ordering = RANDOM_NODEORDERING; + partition_config.node_ordering = + NodeOrderingType::RANDOM_NODEORDERING; } else if (strcmp("degree", node_ordering->sval[0]) == 0) { - partition_config.node_ordering = DEGREE_NODEORDERING; + partition_config.node_ordering = + NodeOrderingType::DEGREE_NODEORDERING; } else if (strcmp("leastghostnodesfirst_degree", node_ordering->sval[0]) == 0) { - partition_config.node_ordering = LEASTGHOSTNODESFIRST_DEGREE_NODEODERING; + partition_config.node_ordering = + NodeOrderingType::LEASTGHOSTNODESFIRST_DEGREE_NODEODERING; } else if (strcmp("degree_leastghostnodesfirst", node_ordering->sval[0]) == 0) { - partition_config.node_ordering = DEGREE_LEASTGHOSTNODESFIRST_NODEODERING; + partition_config.node_ordering = + NodeOrderingType::DEGREE_LEASTGHOSTNODESFIRST_NODEODERING; } else { fprintf(stderr, "Invalid node ordering variant: \"%s\"\n", node_ordering->sval[0]); arg_freetable(argtable_fordeletion, sizeof(argtable_fordeletion) / sizeof(argtable_fordeletion[0])); diff --git a/parallel/parallel_src/lib/communication/mpi_tools.cpp b/parallel/parallel_src/lib/communication/mpi_tools.cpp index 6bf37092..7a4ee3ba 100644 --- a/parallel/parallel_src/lib/communication/mpi_tools.cpp +++ b/parallel/parallel_src/lib/communication/mpi_tools.cpp @@ -4,13 +4,12 @@ * Source of KaHIP -- Karlsruhe High Quality Partitioning. * Christian Schulz *****************************************************************************/ - #include -#include #include "io/parallel_vector_io.h" #include "io/parallel_graph_io.h" #include "mpi_tools.h" + namespace parhip { // currently this method is for debugging purposses only // later on this may be a parallel io routine diff --git a/parallel/parallel_src/lib/communication/mpi_types.h b/parallel/parallel_src/lib/communication/mpi_types.h index f9aae782..99bba5ba 100644 --- a/parallel/parallel_src/lib/communication/mpi_types.h +++ b/parallel/parallel_src/lib/communication/mpi_types.h @@ -49,7 +49,7 @@ inline int mpi_type_cleanup(MPI_Comm /*comm*/, std::scoped_lock const lock{mpi_type_mutex}; auto& custom_types = get_custom_mpi_types(); - for (MPI_Datatype& datatype : custom_types) { + for (auto&& datatype : custom_types) { if (datatype != MPI_DATATYPE_NULL) { MPI_Type_free(&datatype); datatype = MPI_DATATYPE_NULL; @@ -251,7 +251,7 @@ struct mpi::details::mpi_datatype_trait { static MPI_Datatype const mpi_type = []() -> MPI_Datatype { MPI_Datatype mpi_type_val = MPI_DATATYPE_NULL; - constexpr size_t num_fields = cista::arity(); + constexpr std::size_t num_fields = cista::arity(); std::vector block_lengths(num_fields, 1); std::vector types; std::vector offsets; diff --git a/parallel/parallel_src/lib/definitions.h b/parallel/parallel_src/lib/definitions.h index 22655351..c0d2114f 100644 --- a/parallel/parallel_src/lib/definitions.h +++ b/parallel/parallel_src/lib/definitions.h @@ -7,14 +7,12 @@ #ifndef DEFINITIONS_H_CHRA #define DEFINITIONS_H_CHRA +#include -#include -#include -#include +#include +#include "macros_assertions.h" +#include -#include "limits.h" -#include "macros_assertions.h" -#include "stdio.h" // allows us to disable most of the output during partitioning #ifndef NOOUTPUT @@ -29,24 +27,24 @@ namespace parhip { * ********************************************/ //Types needed for the parallel graph ds //we use long since we want to partition huge graphs -typedef unsigned long long ULONG; -typedef unsigned int UINT; -typedef unsigned long long NodeID; -typedef unsigned long long EdgeID; -typedef unsigned long long PartitionID; -typedef unsigned long long NodeWeight; -typedef unsigned long long EdgeWeight; -typedef int PEID; +using ULONG = unsigned long long; +using UINT = unsigned int; +using NodeID = unsigned long long; +using EdgeID = unsigned long long; +using PartitionID = unsigned long long; +using NodeWeight = unsigned long long; +using EdgeWeight = unsigned long long; +using PEID = int; constexpr PEID ROOT = 0; -typedef enum { +enum class PermutationQuality : std::uint8_t { PERMUTATION_QUALITY_NONE, PERMUTATION_QUALITY_FAST, PERMUTATION_QUALITY_GOOD -} PermutationQuality; +}; -typedef enum { +enum class InitialPartitioningAlgorithm : std::uint8_t { KAFFPAESTRONG, KAFFPAEECO, KAFFPAEFAST, @@ -55,19 +53,19 @@ typedef enum { KAFFPAEECOSNW, KAFFPAESTRONGSNW, RANDOMIP -} InitialPartitioningAlgorithm; +}; struct source_target_pair { NodeID source; NodeID target; }; -typedef enum { +enum class NodeOrderingType : std::uint8_t { RANDOM_NODEORDERING, DEGREE_NODEORDERING, LEASTGHOSTNODESFIRST_DEGREE_NODEODERING, DEGREE_LEASTGHOSTNODESFIRST_NODEODERING -} NodeOrderingType; +}; } #endif diff --git a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp index ec3b346c..a12f89f3 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp +++ b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp @@ -72,25 +72,25 @@ void distributed_evolutionary_partitioning::perform_partitioning( MPI_Comm commu int mode = 0; switch( config.initial_partitioning_algorithm ) { - case KAFFPAESTRONG: + case InitialPartitioningAlgorithm::KAFFPAESTRONG: mode = STRONG; break; - case KAFFPAEECO: + case InitialPartitioningAlgorithm::KAFFPAEECO: mode = ECO; break; - case KAFFPAEFAST: + case InitialPartitioningAlgorithm::KAFFPAEFAST: mode = FAST; break; - case KAFFPAEULTRAFASTSNW: + case InitialPartitioningAlgorithm::KAFFPAEULTRAFASTSNW: mode = ULTRAFASTSOCIAL; break; - case KAFFPAEFASTSNW: + case InitialPartitioningAlgorithm::KAFFPAEFASTSNW: mode = FASTSOCIAL; break; - case KAFFPAEECOSNW: + case InitialPartitioningAlgorithm::KAFFPAEECOSNW: mode = ECOSOCIAL; break; - case KAFFPAESTRONGSNW: + case InitialPartitioningAlgorithm::KAFFPAESTRONGSNW: mode = STRONGSOCIAL; break; default: diff --git a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp index 247dfa90..d34e5803 100644 --- a/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp +++ b/parallel/parallel_src/lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp @@ -19,7 +19,8 @@ initial_partitioning_algorithm::~initial_partitioning_algorithm() { void initial_partitioning_algorithm::perform_partitioning( MPI_Comm communicator, PPartitionConfig & config, parallel_graph_access & Q) { - if( config.initial_partitioning_algorithm == RANDOMIP) { + if( config.initial_partitioning_algorithm == + InitialPartitioningAlgorithm::RANDOMIP) { random_initial_partitioning dist_rpart; dist_rpart.perform_partitioning( communicator, config, Q ); } else { diff --git a/parallel/parallel_src/lib/parallel_label_compress/node_ordering.h b/parallel/parallel_src/lib/parallel_label_compress/node_ordering.h index 313e7033..65541bc4 100644 --- a/parallel/parallel_src/lib/parallel_label_compress/node_ordering.h +++ b/parallel/parallel_src/lib/parallel_label_compress/node_ordering.h @@ -26,16 +26,18 @@ class node_ordering { } endfor switch( config.node_ordering ) { - case RANDOM_NODEORDERING: + case NodeOrderingType::RANDOM_NODEORDERING: order_nodes_random(config, G, ordered_nodes); break; - case DEGREE_NODEORDERING: + case NodeOrderingType::DEGREE_NODEORDERING: order_nodes_degree(config, G, ordered_nodes); break; - case LEASTGHOSTNODESFIRST_DEGREE_NODEODERING: + case NodeOrderingType:: + LEASTGHOSTNODESFIRST_DEGREE_NODEODERING: order_leastghostnodes_nodes_degree(config, G, ordered_nodes); break; - case DEGREE_LEASTGHOSTNODESFIRST_NODEODERING: + case NodeOrderingType:: + DEGREE_LEASTGHOSTNODESFIRST_NODEODERING: order_nodes_degree_leastghostnodes(config, G, ordered_nodes); break; } diff --git a/parallel/parallel_src/lib/tools/distributed_quality_metrics.cpp b/parallel/parallel_src/lib/tools/distributed_quality_metrics.cpp index 3529f2e7..64a632c0 100644 --- a/parallel/parallel_src/lib/tools/distributed_quality_metrics.cpp +++ b/parallel/parallel_src/lib/tools/distributed_quality_metrics.cpp @@ -6,15 +6,9 @@ *****************************************************************************/ #include +#include "definitions.h" #include "distributed_quality_metrics.h" namespace parhip { -distributed_quality_metrics::distributed_quality_metrics() { - -} - -distributed_quality_metrics::~distributed_quality_metrics() { - -} EdgeWeight distributed_quality_metrics::edge_cut_second( parallel_graph_access & G, MPI_Comm communicator ) { EdgeWeight local_cut = 0; diff --git a/parallel/parallel_src/lib/tools/distributed_quality_metrics.h b/parallel/parallel_src/lib/tools/distributed_quality_metrics.h index 25ddd767..02065934 100644 --- a/parallel/parallel_src/lib/tools/distributed_quality_metrics.h +++ b/parallel/parallel_src/lib/tools/distributed_quality_metrics.h @@ -14,9 +14,6 @@ namespace parhip { class distributed_quality_metrics { public: - distributed_quality_metrics(); - virtual ~distributed_quality_metrics(); - EdgeWeight local_edge_cut( parallel_graph_access & G, int * partition_map, MPI_Comm communicator ); EdgeWeight edge_cut( parallel_graph_access & G, MPI_Comm communicator ); EdgeWeight edge_cut_second( parallel_graph_access & G, MPI_Comm communicator ); diff --git a/parallel/parallel_src/lib/tools/helpers.h b/parallel/parallel_src/lib/tools/helpers.h index 982dd784..f6e9e053 100644 --- a/parallel/parallel_src/lib/tools/helpers.h +++ b/parallel/parallel_src/lib/tools/helpers.h @@ -12,12 +12,11 @@ #include #include #include +#include + namespace parhip { class helpers { public: - helpers() {}; - virtual ~helpers() {}; - template void filter_duplicates( std::vector< vectortype > & input, Compare comparator_function, Equal equal_function); }; diff --git a/parallel/parallel_src/lib/tools/random_functions.cpp b/parallel/parallel_src/lib/tools/random_functions.cpp deleted file mode 100644 index d47e5c98..00000000 --- a/parallel/parallel_src/lib/tools/random_functions.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/****************************************************************************** - * random_functions.cpp - * * - * Source of KaHIP -- Karlsruhe High Quality Partitioning. - * Christian Schulz - *****************************************************************************/ - -#include "random_functions.h" -namespace parhip { -MersenneTwister random_functions::m_mt; -int random_functions::m_seed = 0; - -random_functions::random_functions() { -} - -random_functions::~random_functions() { -} -} \ No newline at end of file diff --git a/parallel/parallel_src/lib/tools/random_functions.h b/parallel/parallel_src/lib/tools/random_functions.h index 022ccfa6..2fc23f05 100644 --- a/parallel/parallel_src/lib/tools/random_functions.h +++ b/parallel/parallel_src/lib/tools/random_functions.h @@ -8,20 +8,17 @@ #ifndef RANDOM_FUNCTIONS_RMEPKWYT #define RANDOM_FUNCTIONS_RMEPKWYT -#include #include #include #include "definitions.h" #include "partition_config.h" namespace parhip { -typedef std::mt19937 MersenneTwister; + +using MersenneTwister = std::mt19937; class random_functions { public: - random_functions(); - virtual ~random_functions(); - template static void circular_permutation(std::vector & vec) { if(vec.size() < 2) return; @@ -123,9 +120,9 @@ class random_functions { } switch(partition_config.permutation_quality) { - case PERMUTATION_QUALITY_NONE: break; - case PERMUTATION_QUALITY_FAST: permutate_vector_fast(vec, false); break; - case PERMUTATION_QUALITY_GOOD: permutate_vector_good(vec, false); break; + case PermutationQuality::PERMUTATION_QUALITY_NONE: break; + case PermutationQuality::PERMUTATION_QUALITY_FAST: permutate_vector_fast(vec, false); break; + case PermutationQuality::PERMUTATION_QUALITY_GOOD: permutate_vector_good(vec, false); break; } } @@ -144,23 +141,20 @@ class random_functions { static double nextDouble(double lb, double rb) { - double rnbr = (double) rand() / (double) RAND_MAX; // rnd in 0,1 - double length = rb - lb; - rnbr *= length; - rnbr += lb; - - return rnbr; + std::uniform_real_distribution A(lb,rb); + return A(m_mt); } static void setSeed(int seed) { m_seed = seed; srand(seed); - m_mt.seed(m_seed); + std::seed_seq mt_seed{seed}; + m_mt.seed(mt_seed); } private: - static int m_seed; - static MersenneTwister m_mt; + inline static int m_seed = 0; + inline static MersenneTwister m_mt; }; } #endif /* end of include guard: RANDOM_FUNCTIONS_RMEPKWYT */ From 3f974a22f9a640ac8587a3a4e4fc9ebe4f526a9d Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 15 Oct 2024 11:56:25 +0100 Subject: [PATCH 54/64] Project setup macro --- CMakeLists.txt | 5 +- cmake/KaHIPSettings.cmake | 180 +++++++++--------- .../parallel_contraction_test.cpp | 2 +- 3 files changed, 95 insertions(+), 92 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e9f7a03..95666f51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,10 +5,7 @@ project(KaHIP list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(KaHIPSettings) - -# --march=nativeflag -option(NONATIVEOPTIMIZATIONS "Disable --march=native optimizations" OFF) - +kahip_project_configure() # Check dependencies find_package(OpenMP) diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index bb4d560d..ae20f7ff 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -1,89 +1,95 @@ -include(${CMAKE_SOURCE_DIR}/cmake/Cache.cmake) -include(${CMAKE_SOURCE_DIR}/cmake/CompilerWarnings.cmake) -include(${CMAKE_SOURCE_DIR}/cmake/InterproceduralOptimization.cmake) -include(${CMAKE_SOURCE_DIR}/cmake/Sanitizers.cmake) -include(${CMAKE_SOURCE_DIR}/cmake/StaticAnalyzers.cmake) -include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) - -kahip_supports_sanitizers() - -option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) -option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" ON) -option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" ON) -option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) -option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) -option(kahip_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) -option(kahip_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) -option(kahip_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" ${SUPPORTS_UBSAN}) -option(kahip_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) -option(kahip_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) -option(kahip_ENABLE_CLANG_TIDY "Enable clang-tidy" OFF) -option(kahip_ENABLE_CPPCHECK "Enable cpp-check analysis" OFF) -option(kahip_ENABLE_IWYU "Enable `include_what_you_use`" OFF) -option(kahip_ENABLE_CACHE "Enable ccache" OFF) - -if (kahip_ENABLE_UNITY_BUILD) - set(CMAKE_UNITY_BUILD ON) -endif () - - -if(kahip_ENABLE_IPO) - kahip_enable_ipo() -endif() - -add_library(kahip_warnings INTERFACE) -add_library(kahip_options INTERFACE) - -include(${CMAKE_SOURCE_DIR}/cmake/StandardProjectSettings.cmake) - -if(kahip_ENABLE_USER_LINKER) - include(${CMAKE_SOURCE_DIR}/cmake/Linker.cmake) - kahip_configure_linker(kahip_options) -endif() - -kahip_set_project_warnings( - kahip_warnings - ${kahip_WARNINGS_AS_ERRORS} - "" - "" - "" - "") - -if (kahip_ENABLE_SANITIZERS) - kahip_enable_sanitizers( - kahip_options - ${kahip_ENABLE_SANITIZER_ADDRESS} - ${kahip_ENABLE_SANITIZER_LEAK} - ${kahip_ENABLE_SANITIZER_UNDEFINED} - ${kahip_ENABLE_SANITIZER_THREAD} - ${kahip_ENABLE_SANITIZER_MEMORY}) -endif () - -if(kahip_ENABLE_CLANG_TIDY) - kahip_enable_clang_tidy(kahip_options ${kahip_WARNINGS_AS_ERRORS}) -endif() - -if(kahip_ENABLE_CPPCHECK) - kahip_enable_cppcheck(${kahip_WARNINGS_AS_ERRORS} "" # override cppcheck options +macro(kahip_project_configure) + message(STATUS "Configuring Kahip") + + include(${CMAKE_SOURCE_DIR}/cmake/Cache.cmake) + include(${CMAKE_SOURCE_DIR}/cmake/CompilerWarnings.cmake) + include(${CMAKE_SOURCE_DIR}/cmake/InterproceduralOptimization.cmake) + include(${CMAKE_SOURCE_DIR}/cmake/Sanitizers.cmake) + include(${CMAKE_SOURCE_DIR}/cmake/StaticAnalyzers.cmake) + include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) + + kahip_supports_sanitizers() + + option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) + option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" ON) + option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" ON) + option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) + option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) + option(kahip_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) + option(kahip_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) + option(kahip_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" ${SUPPORTS_UBSAN}) + option(kahip_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) + option(kahip_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) + option(kahip_ENABLE_CLANG_TIDY "Enable clang-tidy" OFF) + option(kahip_ENABLE_CPPCHECK "Enable cpp-check analysis" OFF) + option(kahip_ENABLE_IWYU "Enable `include_what_you_use`" OFF) + option(kahip_ENABLE_CACHE "Enable ccache" OFF) + option(NONATIVEOPTIMIZATIONS "Disable --march=native optimizations" OFF) + + if (kahip_ENABLE_UNITY_BUILD) + set(CMAKE_UNITY_BUILD ON) + endif () + + + if(kahip_ENABLE_IPO) + kahip_enable_ipo() + endif() + + add_library(kahip_warnings INTERFACE) + add_library(kahip_options INTERFACE) + + include(${CMAKE_SOURCE_DIR}/cmake/StandardProjectSettings.cmake) + + if(kahip_ENABLE_USER_LINKER) + include(${CMAKE_SOURCE_DIR}/cmake/Linker.cmake) + kahip_configure_linker(kahip_options) + endif() + + kahip_set_project_warnings( + kahip_warnings + ${kahip_WARNINGS_AS_ERRORS} + "" + "" + "" + "") + + if (kahip_ENABLE_SANITIZERS) + kahip_enable_sanitizers( + kahip_options + ${kahip_ENABLE_SANITIZER_ADDRESS} + ${kahip_ENABLE_SANITIZER_LEAK} + ${kahip_ENABLE_SANITIZER_UNDEFINED} + ${kahip_ENABLE_SANITIZER_THREAD} + ${kahip_ENABLE_SANITIZER_MEMORY}) + endif () + + if(kahip_ENABLE_CLANG_TIDY) + kahip_enable_clang_tidy(kahip_options ${kahip_WARNINGS_AS_ERRORS}) + endif() + + if(kahip_ENABLE_CPPCHECK) + kahip_enable_cppcheck(${kahip_WARNINGS_AS_ERRORS} "" # override cppcheck options + ) + endif() + + if(kahip_ENABLE_IWYU) + kahip_enable_include_what_you_use() + endif() + + # tweak compiler flags + target_compile_features(kahip_options INTERFACE cxx_std_20) + message(VERBOSE "Checking compiler feature support") + CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) + target_compile_options(kahip_options INTERFACE + $<$:-funroll-loops> + ) + CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) + target_compile_options(kahip_options INTERFACE + $<$:-funroll-loops> + ) + CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) + target_compile_options(kahip_options INTERFACE + $<$,$>,$,$>>:-march=native> ) -endif() - -if(kahip_ENABLE_IWYU) - kahip_enable_include_what_you_use() -endif() - -# tweak compiler flags -target_compile_features(kahip_options INTERFACE cxx_std_20) -CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) -target_compile_options(kahip_options INTERFACE - $<$:-funroll-loops> -) -CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) -target_compile_options(kahip_options INTERFACE - $<$:-funroll-loops> -) -CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) -target_compile_options(kahip_options INTERFACE - $<$,$>,$,$>>:-march=native> -) +endmacro(kahip_project_configure) \ No newline at end of file diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index becaeafe..06d62a33 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -76,7 +76,7 @@ TEST_CASE("flattening vector of messages", "[unit][mpi]") { TEST_CASE("Packing and Unpacking for messages", "[unit][mpi]") { SECTION("Empty Vector") { - constexpr std::vector > m_empty{}; + const std::vector > m_empty{}; auto const packed = mpi::pack_messages(m_empty); auto const unpacked = mpi::unpack_messages(packed); From d4ba62a76877d255b164e415f4c1f3000187e2a8 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 15 Oct 2024 12:11:36 +0100 Subject: [PATCH 55/64] cmake formatted --- .gersemirc | 10 + CMakeLists.txt | 569 +++++++++++------- cmake/Cache.cmake | 57 +- cmake/CompilerWarnings.cmake | 211 +++---- cmake/FindGurobi.cmake | 42 +- cmake/InterproceduralOptimization.cmake | 14 +- cmake/KaHIPSettings.cmake | 196 +++--- cmake/Linker.cmake | 51 +- cmake/Sanitizers.cmake | 215 ++++--- cmake/StandardProjectSettings.cmake | 65 +- cmake/StaticAnalyzers.cmake | 205 ++++--- cmake/Utilities.cmake | 173 ++---- extern/argtable3-3.2.2/CMakeLists.txt | 6 +- .../argtable3-3.2.2/examples/CMakeLists.txt | 19 +- extern/argtable3-3.2.2/tests/CMakeLists.txt | 86 +-- parallel/modified_kahip/CMakeLists.txt | 174 +++--- parallel/parallel_src/CMakeLists.txt | 196 ++++-- .../parallel_src/extern/cista/CMakeLists.txt | 213 +++---- .../extern/cista/tools/doctest/CMakeLists.txt | 8 +- .../tools/to_tuple_generator/CMakeLists.txt | 2 +- .../extern/cista/tools/uniter/CMakeLists.txt | 2 +- .../extern/cista/tools/wyhash/CMakeLists.txt | 2 +- .../extern/cista/tools/xxh3/CMakeLists.txt | 2 +- parallel/parallel_src/tests/CMakeLists.txt | 44 +- 24 files changed, 1449 insertions(+), 1113 deletions(-) create mode 100644 .gersemirc diff --git a/.gersemirc b/.gersemirc new file mode 100644 index 00000000..f18049c9 --- /dev/null +++ b/.gersemirc @@ -0,0 +1,10 @@ +cache: true +color: false +definitions: [cmake] +indent: 4 +line_length: 80 +list_expansion: favour-inlining +quiet: false +unsafe: false +warn_about_unknown_commands: true +workers: max \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 95666f51..7072b839 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,51 +1,54 @@ cmake_minimum_required(VERSION 3.26..28) include(CheckCXXCompilerFlag) -project(KaHIP - LANGUAGES C CXX) +project(KaHIP LANGUAGES C CXX) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(KaHIPSettings) -kahip_project_configure() # Check dependencies find_package(OpenMP) if(OpenMP_CXX_FOUND) - message(STATUS "OpenMP support detected") + message(STATUS "OpenMP support detected") else() - message(WARNING "OpenMP not available, activating workaround") - add_library(OpenMP::OpenMP_CXX IMPORTED INTERFACE) - set_property(TARGET OpenMP::OpenMP_CXX PROPERTY INTERFACE_COMPILE_OPTIONS "") - target_include_directories(OpenMP::OpenMP_CXX INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/misc) + message(WARNING "OpenMP not available, activating workaround") + add_library(OpenMP::OpenMP_CXX IMPORTED INTERFACE) + set_property( + TARGET OpenMP::OpenMP_CXX + PROPERTY INTERFACE_COMPILE_OPTIONS "" + ) + target_include_directories( + OpenMP::OpenMP_CXX + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/misc + ) endif() find_package(metis CONFIG) if(metis_FOUND) - message(STATUS "Metis support detected") - find_library(GKlib CONFIG) - if (NOT GKlib_FOUND) - message(STATUS "Metis requires GKlib, but GKlib was not found") - set(metis_FOUND OFF) - else() - add_definitions("-DUSEMETIS") - if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - include_directories(/usr/local/include) + message(STATUS "Metis support detected") + find_library(GKlib CONFIG) + if(NOT GKlib_FOUND) + message(STATUS "Metis requires GKlib, but GKlib was not found") + set(metis_FOUND OFF) + else() + add_definitions("-DUSEMETIS") + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + include_directories(/usr/local/include) + endif() endif() - endif() endif() # 64 Bit option option(64BITMODE "64 bit mode" OFF) if(64BITMODE) - add_definitions("-DMODE64BITEDGES") - add_definitions("-DPOINTER64=1") + add_definitions("-DMODE64BITEDGES") + add_definitions("-DPOINTER64=1") endif() # optimized output option(OPTIMIZED_OUTPUT "optimized output" OFF) if(OPTIMIZED_OUTPUT) - add_definitions("-DKAFFPAOUTPUT") + add_definitions("-DKAFFPAOUTPUT") endif() - # Optionally disable all MPI-dependent targets option(NOMPI "disable all targets that depend on MPI (kaffpaE, ParHIP)" OFF) @@ -57,29 +60,29 @@ option(DETERMINISTIC_PARHIP "enforce deterministic computations in ParHIP" OFF) # Report which MPI we actually found, # may need use MPI_HOME hint to get the proper one (eg, on openSUSE) if(NOT NOMPI) - # Always look for MPI since kaffpaE also requires it - find_package(MPI REQUIRED) + # Always look for MPI since kaffpaE also requires it + find_package(MPI REQUIRED) - if(${MPI_C_FOUND}) - message("MPI detected (can use MPI_HOME hint to direct the detection...)") - message(STATUS "MPI include: ${MPI_C_INCLUDE_DIRS}") - message(STATUS "MPI library: ${MPI_C_LIBRARIES}") - endif() + if(${MPI_C_FOUND}) + message( + "MPI detected (can use MPI_HOME hint to direct the detection...)" + ) + message(STATUS "MPI include: ${MPI_C_INCLUDE_DIRS}") + message(STATUS "MPI library: ${MPI_C_LIBRARIES}") + endif() - if(PARHIP) - message(STATUS "ParHIP build requested") - else() - message(STATUS "ParHIP build disabled") - endif() + if(PARHIP) + message(STATUS "ParHIP build requested") + else() + message(STATUS "ParHIP build disabled") + endif() else() - message(STATUS "Build without MPI dependency: kaffpaE and ParHIP disabled") + message(STATUS "Build without MPI dependency: kaffpaE and ParHIP disabled") endif() - # tcmalloc option(USE_TCMALLOC "if available, link against tcmalloc" OFF) - # ILP improver option(USE_ILP "build local ILP improver - introduces dependency on Gurobi" OFF) @@ -88,165 +91,220 @@ add_subdirectory(extern/argtable3-3.2.2) add_library(kahip_headers INTERFACE) target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) -target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/app) -target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib) -target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io) -target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) -target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) -target_include_directories(kahip_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) +target_include_directories( + kahip_headers + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/app +) +target_include_directories( + kahip_headers + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib +) +target_include_directories( + kahip_headers + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io +) +target_include_directories( + kahip_headers + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition +) +target_include_directories( + kahip_headers + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement +) +target_include_directories( + kahip_headers + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools +) add_library(kahip_version INTERFACE) -target_include_directories(kahip_version INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/version) +target_include_directories( + kahip_version + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/version +) target_link_libraries(kahip_headers INTERFACE kahip_version) set(LIBKAFFPA_SOURCE_FILES - lib/data_structure/graph_hierarchy.cpp - lib/algorithms/strongly_connected_components.cpp - lib/algorithms/topological_sort.cpp - lib/algorithms/push_relabel.cpp - lib/io/graph_io.cpp - lib/tools/quality_metrics.cpp - lib/tools/random_functions.cpp - lib/tools/graph_extractor.cpp - lib/tools/misc.cpp - lib/tools/partition_snapshooter.cpp - lib/partition/graph_partitioner.cpp - lib/partition/w_cycles/wcycle_partitioner.cpp - lib/partition/coarsening/coarsening.cpp - lib/partition/coarsening/contraction.cpp - lib/partition/coarsening/edge_rating/edge_ratings.cpp - lib/partition/coarsening/matching/matching.cpp - lib/partition/coarsening/matching/random_matching.cpp - lib/partition/coarsening/matching/gpa/path.cpp - lib/partition/coarsening/matching/gpa/gpa_matching.cpp - lib/partition/coarsening/matching/gpa/path_set.cpp - lib/partition/coarsening/clustering/node_ordering.cpp - lib/partition/coarsening/clustering/size_constraint_label_propagation.cpp - lib/partition/initial_partitioning/initial_partitioning.cpp - lib/partition/initial_partitioning/initial_partitioner.cpp - lib/partition/initial_partitioning/initial_partition_bipartition.cpp - lib/partition/initial_partitioning/initial_refinement/initial_refinement.cpp - lib/partition/initial_partitioning/bipartition.cpp - lib/partition/initial_partitioning/initial_node_separator.cpp - lib/partition/uncoarsening/uncoarsening.cpp - lib/partition/uncoarsening/separator/area_bfs.cpp - lib/partition/uncoarsening/separator/vertex_separator_algorithm.cpp - lib/partition/uncoarsening/separator/vertex_separator_flow_solver.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.cpp - lib/partition/uncoarsening/refinement/mixed_refinement.cpp - lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.cpp - lib/partition/uncoarsening/refinement/refinement.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/cut_flow_problem_solver.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.cpp - lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.cpp - lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.cpp - lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.cpp - lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.cpp - lib/partition/uncoarsening/refinement/node_separators/greedy_ns_local_search.cpp - lib/partition/uncoarsening/refinement/node_separators/fm_ns_local_search.cpp - lib/partition/uncoarsening/refinement/node_separators/localized_fm_ns_local_search.cpp - lib/algorithms/cycle_search.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp - lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp) + lib/data_structure/graph_hierarchy.cpp + lib/algorithms/strongly_connected_components.cpp + lib/algorithms/topological_sort.cpp + lib/algorithms/push_relabel.cpp + lib/io/graph_io.cpp + lib/tools/quality_metrics.cpp + lib/tools/random_functions.cpp + lib/tools/graph_extractor.cpp + lib/tools/misc.cpp + lib/tools/partition_snapshooter.cpp + lib/partition/graph_partitioner.cpp + lib/partition/w_cycles/wcycle_partitioner.cpp + lib/partition/coarsening/coarsening.cpp + lib/partition/coarsening/contraction.cpp + lib/partition/coarsening/edge_rating/edge_ratings.cpp + lib/partition/coarsening/matching/matching.cpp + lib/partition/coarsening/matching/random_matching.cpp + lib/partition/coarsening/matching/gpa/path.cpp + lib/partition/coarsening/matching/gpa/gpa_matching.cpp + lib/partition/coarsening/matching/gpa/path_set.cpp + lib/partition/coarsening/clustering/node_ordering.cpp + lib/partition/coarsening/clustering/size_constraint_label_propagation.cpp + lib/partition/initial_partitioning/initial_partitioning.cpp + lib/partition/initial_partitioning/initial_partitioner.cpp + lib/partition/initial_partitioning/initial_partition_bipartition.cpp + lib/partition/initial_partitioning/initial_refinement/initial_refinement.cpp + lib/partition/initial_partitioning/bipartition.cpp + lib/partition/initial_partitioning/initial_node_separator.cpp + lib/partition/uncoarsening/uncoarsening.cpp + lib/partition/uncoarsening/separator/area_bfs.cpp + lib/partition/uncoarsening/separator/vertex_separator_algorithm.cpp + lib/partition/uncoarsening/separator/vertex_separator_flow_solver.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.cpp + lib/partition/uncoarsening/refinement/mixed_refinement.cpp + lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.cpp + lib/partition/uncoarsening/refinement/refinement.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/cut_flow_problem_solver.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.cpp + lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.cpp + lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.cpp + lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.cpp + lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.cpp + lib/partition/uncoarsening/refinement/node_separators/greedy_ns_local_search.cpp + lib/partition/uncoarsening/refinement/node_separators/fm_ns_local_search.cpp + lib/partition/uncoarsening/refinement/node_separators/localized_fm_ns_local_search.cpp + lib/algorithms/cycle_search.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp + lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp +) add_library(libkaffpa OBJECT ${LIBKAFFPA_SOURCE_FILES}) -target_link_libraries(libkaffpa PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + libkaffpa + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) if(NOT NOMPI) - set(LIBKAFFPA_PARALLEL_SOURCE_FILES - lib/parallel_mh/parallel_mh_async.cpp - lib/parallel_mh/population.cpp - lib/parallel_mh/galinier_combine/gal_combine.cpp - lib/parallel_mh/galinier_combine/construct_partition.cpp - lib/parallel_mh/exchange/exchanger.cpp - lib/tools/graph_communication.cpp - lib/tools/mpi_tools.cpp) - add_library(libkaffpa_parallel OBJECT ${LIBKAFFPA_PARALLEL_SOURCE_FILES}) - target_link_libraries(libkaffpa_parallel PRIVATE kahip_headers kahip_options kahip_warnings MPI::MPI_CXX) + set(LIBKAFFPA_PARALLEL_SOURCE_FILES + lib/parallel_mh/parallel_mh_async.cpp + lib/parallel_mh/population.cpp + lib/parallel_mh/galinier_combine/gal_combine.cpp + lib/parallel_mh/galinier_combine/construct_partition.cpp + lib/parallel_mh/exchange/exchanger.cpp + lib/tools/graph_communication.cpp + lib/tools/mpi_tools.cpp + ) + add_library(libkaffpa_parallel OBJECT ${LIBKAFFPA_PARALLEL_SOURCE_FILES}) + target_link_libraries( + libkaffpa_parallel + PRIVATE kahip_headers kahip_options kahip_warnings MPI::MPI_CXX + ) endif() set(LIBMAPPING_SOURCE_FILES - lib/mapping/local_search_mapping.cpp - lib/mapping/full_search_space.cpp - lib/mapping/full_search_space_pruned.cpp - lib/mapping/communication_graph_search_space.cpp - lib/mapping/fast_construct_mapping.cpp - lib/mapping/construct_distance_matrix.cpp - lib/mapping/mapping_algorithms.cpp - lib/mapping/construct_mapping.cpp) + lib/mapping/local_search_mapping.cpp + lib/mapping/full_search_space.cpp + lib/mapping/full_search_space_pruned.cpp + lib/mapping/communication_graph_search_space.cpp + lib/mapping/fast_construct_mapping.cpp + lib/mapping/construct_distance_matrix.cpp + lib/mapping/mapping_algorithms.cpp + lib/mapping/construct_mapping.cpp +) add_library(libmapping OBJECT ${LIBMAPPING_SOURCE_FILES}) -target_link_libraries(libmapping PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries( + libmapping + PRIVATE kahip_headers kahip_options kahip_warnings +) set(LIBSPAC_SOURCE_FILES lib/spac/spac.cpp) add_library(libspac OBJECT ${LIBSPAC_SOURCE_FILES}) -target_link_libraries(libspac PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries( + libspac + PRIVATE kahip_headers kahip_options kahip_warnings +) set(NODE_ORDERING_SOURCE_FILES - lib/node_ordering/min_degree_ordering.cpp - lib/node_ordering/nested_dissection.cpp - lib/node_ordering/ordering_tools.cpp - lib/node_ordering/reductions.cpp) + lib/node_ordering/min_degree_ordering.cpp + lib/node_ordering/nested_dissection.cpp + lib/node_ordering/ordering_tools.cpp + lib/node_ordering/reductions.cpp +) add_library(libnodeordering OBJECT ${NODE_ORDERING_SOURCE_FILES}) -target_link_libraries(libnodeordering PRIVATE kahip_headers kahip_options kahip_warnings) - +target_link_libraries( + libnodeordering + PRIVATE kahip_headers kahip_options kahip_warnings +) # generate targets for each binary add_executable(kaffpa app/kaffpa.cpp) target_link_libraries(kaffpa PRIVATE libkaffpa libmapping) target_compile_definitions(kaffpa PRIVATE "-DMODE_KAFFPA") target_link_libraries(kaffpa PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(kaffpa PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + kaffpa + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) install(TARGETS kaffpa DESTINATION bin) -if (USE_TCMALLOC) - find_library(TCMALLOC_LIB tcmalloc) - if (TCMALLOC_LIB) - target_link_libraries(kaffpa ${TCMALLOC_LIB}) - message(STATUS "Using tcmalloc: ${TCMALLOC_LIB}") - else () - message(STATUS "tcmalloc enabled but unavailable on this system") - endif () -endif () +if(USE_TCMALLOC) + find_library(TCMALLOC_LIB tcmalloc) + if(TCMALLOC_LIB) + target_link_libraries(kaffpa ${TCMALLOC_LIB}) + message(STATUS "Using tcmalloc: ${TCMALLOC_LIB}") + else() + message(STATUS "tcmalloc enabled but unavailable on this system") + endif() +endif() add_executable(global_multisection app/global_multisection.cpp) target_link_libraries(global_multisection PRIVATE libkaffpa libmapping) -target_compile_definitions(global_multisection PRIVATE "-DMODE_KAFFPA -DMODE_GLOBALMS") +target_compile_definitions( + global_multisection + PRIVATE "-DMODE_KAFFPA -DMODE_GLOBALMS" +) target_link_libraries(global_multisection PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(global_multisection PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + global_multisection + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) install(TARGETS global_multisection DESTINATION bin) -if (USE_TCMALLOC) - find_library(TCMALLOC_LIB tcmalloc) - if (TCMALLOC_LIB) - target_link_libraries(global_multisection ${TCMALLOC_LIB}) - message(STATUS "Using tcmalloc: ${TCMALLOC_LIB}") - else () - message(STATUS "tcmalloc enabled but unavailable on this system") - endif () -endif () +if(USE_TCMALLOC) + find_library(TCMALLOC_LIB tcmalloc) + if(TCMALLOC_LIB) + target_link_libraries(global_multisection ${TCMALLOC_LIB}) + message(STATUS "Using tcmalloc: ${TCMALLOC_LIB}") + else() + message(STATUS "tcmalloc enabled but unavailable on this system") + endif() +endif() add_executable(evaluator app/evaluator.cpp) target_link_libraries(evaluator PRIVATE libkaffpa libmapping) target_compile_definitions(evaluator PRIVATE "-DMODE_EVALUATOR") target_link_libraries(evaluator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(evaluator PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + evaluator + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) install(TARGETS evaluator DESTINATION bin) add_executable(edge_evaluator app/edge_evaluator.cpp) target_link_libraries(edge_evaluator PRIVATE libkaffpa libmapping) target_compile_definitions(edge_evaluator PRIVATE "-DMODE_EVALUATOR") target_link_libraries(edge_evaluator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(edge_evaluator PRIVATE kahip_headers kahip_options kahip_warnings) +target_link_libraries( + edge_evaluator + PRIVATE kahip_headers kahip_options kahip_warnings +) target_link_libraries(edge_evaluator PRIVATE argtable3) install(TARGETS edge_evaluator DESTINATION bin) @@ -254,125 +312,212 @@ add_executable(node_separator app/node_separator_ml.cpp) target_link_libraries(node_separator PRIVATE libkaffpa libmapping) target_compile_definitions(node_separator PRIVATE "-DMODE_NODESEP") target_link_libraries(node_separator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(node_separator PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + node_separator + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) install(TARGETS node_separator DESTINATION bin) add_executable(label_propagation app/label_propagation.cpp) target_link_libraries(label_propagation PRIVATE libkaffpa libmapping) target_compile_definitions(label_propagation PRIVATE "-DMODE_LABELPROPAGATION") target_link_libraries(label_propagation PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(label_propagation PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + label_propagation + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) install(TARGETS label_propagation DESTINATION bin) -add_executable(partition_to_vertex_separator app/partition_to_vertex_separator.cpp) -target_link_libraries(partition_to_vertex_separator PRIVATE libkaffpa libmapping) -target_compile_definitions(partition_to_vertex_separator PRIVATE "-DMODE_PARTITIONTOVERTEXSEPARATOR") +add_executable( + partition_to_vertex_separator + app/partition_to_vertex_separator.cpp +) +target_link_libraries( + partition_to_vertex_separator + PRIVATE libkaffpa libmapping +) +target_compile_definitions( + partition_to_vertex_separator + PRIVATE "-DMODE_PARTITIONTOVERTEXSEPARATOR" +) target_link_libraries(partition_to_vertex_separator PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(partition_to_vertex_separator PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + partition_to_vertex_separator + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) install(TARGETS partition_to_vertex_separator DESTINATION bin) -add_executable(interface_test misc/example_library_call/interface_test.cpp interface/kaHIP_interface.cpp) -target_link_libraries(interface_test PRIVATE libkaffpa libmapping libnodeordering libspac) -target_include_directories(interface_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/interface) +add_executable( + interface_test + misc/example_library_call/interface_test.cpp + interface/kaHIP_interface.cpp +) +target_link_libraries( + interface_test + PRIVATE libkaffpa libmapping libnodeordering libspac +) +target_include_directories( + interface_test + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/interface +) target_compile_definitions(interface_test PRIVATE "-DMODE_KAFFPA") target_link_libraries(interface_test PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(interface_test PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + interface_test + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) if(metis_FOUND) - target_link_libraries(interface_test PUBLIC metis GKlib) + target_link_libraries(interface_test PUBLIC metis GKlib) endif() install(TARGETS interface_test DESTINATION bin) if(NOT NOMPI) - add_executable(kaffpaE app/kaffpaE.cpp) - target_link_libraries(kaffpaE PRIVATE libkaffpa libmapping libkaffpa_parallel) - target_compile_definitions(kaffpaE PRIVATE "-DMODE_KAFFPAE") - target_link_libraries(kaffpaE PRIVATE MPI::MPI_CXX OpenMP::OpenMP_CXX) - target_link_libraries(kaffpaE PRIVATE kahip_headers kahip_options kahip_warnings argtable3) - install(TARGETS kaffpaE DESTINATION bin) + add_executable(kaffpaE app/kaffpaE.cpp) + target_link_libraries( + kaffpaE + PRIVATE libkaffpa libmapping libkaffpa_parallel + ) + target_compile_definitions(kaffpaE PRIVATE "-DMODE_KAFFPAE") + target_link_libraries(kaffpaE PRIVATE MPI::MPI_CXX OpenMP::OpenMP_CXX) + target_link_libraries( + kaffpaE + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 + ) + install(TARGETS kaffpaE DESTINATION bin) endif() add_executable(graphchecker app/graphchecker.cpp) target_link_libraries(graphchecker PRIVATE libkaffpa libmapping) target_compile_definitions(graphchecker PRIVATE "-DMODE_GRAPHCHECKER") target_link_libraries(graphchecker PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(graphchecker PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + graphchecker + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) install(TARGETS graphchecker DESTINATION bin) add_executable(edge_partitioning app/spac.cpp) target_link_libraries(edge_partitioning PRIVATE libkaffpa libmapping libspac) target_compile_definitions(edge_partitioning PRIVATE "-DMODE_KAFFPA") target_link_libraries(edge_partitioning PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(edge_partitioning PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + edge_partitioning + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) install(TARGETS edge_partitioning DESTINATION bin) add_executable(node_ordering app/node_ordering.cpp) target_link_libraries(node_ordering PRIVATE libkaffpa libnodeordering) -target_compile_definitions(node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING") +target_compile_definitions( + node_ordering + PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING" +) target_link_libraries(node_ordering PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(node_ordering PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + node_ordering + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) install(TARGETS node_ordering DESTINATION bin) if(metis_FOUND) - add_executable(fast_node_ordering app/fast_node_ordering.cpp) - target_link_libraries(fast_node_ordering PRIVATE libkaffpa libmapping libnodeordering) - target_compile_definitions(fast_node_ordering PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING -DFASTORDERING") - target_link_libraries(fast_node_ordering PRIVATE OpenMP::OpenMP_CXX metis GKlib) - target_link_libraries(fast_node_ordering PRIVATE kahip_headers kahip_options kahip_warnings argtable3) - install(TARGETS fast_node_ordering DESTINATION bin) + add_executable(fast_node_ordering app/fast_node_ordering.cpp) + target_link_libraries( + fast_node_ordering + PRIVATE libkaffpa libmapping libnodeordering + ) + target_compile_definitions( + fast_node_ordering + PRIVATE "-DMODE_NODESEP -DMODE_NODEORDERING -DFASTORDERING" + ) + target_link_libraries( + fast_node_ordering + PRIVATE OpenMP::OpenMP_CXX metis GKlib + ) + target_link_libraries( + fast_node_ordering + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 + ) + install(TARGETS fast_node_ordering DESTINATION bin) endif() - # Shared interface library add_library(kahip SHARED interface/kaHIP_interface.cpp) -target_link_libraries(kahip PRIVATE libkaffpa libmapping libnodeordering libspac) +target_link_libraries( + kahip + PRIVATE libkaffpa libmapping libnodeordering libspac +) target_include_directories(kahip PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) target_compile_definitions(kahip PRIVATE "-DMODE_KAFFPA") target_link_libraries(kahip PRIVATE OpenMP::OpenMP_CXX) -target_link_libraries(kahip PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +target_link_libraries( + kahip + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) if(metis_FOUND) - target_link_libraries(kahip PRIVATE metis GKlib) + target_link_libraries(kahip PRIVATE metis GKlib) endif() install(TARGETS kahip DESTINATION lib) # Static interface library add_library(kahip_static interface/kaHIP_interface.cpp) -target_link_libraries(kahip_static PRIVATE libkaffpa libmapping libnodeordering libspac) -target_include_directories(kahip_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) +target_link_libraries( + kahip_static + PRIVATE libkaffpa libmapping libnodeordering libspac +) +target_include_directories( + kahip_static + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface +) target_compile_definitions(kahip_static PRIVATE "-DMODE_KAFFPA") target_link_libraries(kahip_static PRIVATE OpenMP::OpenMP_CXX) -set_target_properties(kahip_static PROPERTIES PUBLIC_HEADER interface/kaHIP_interface.h) -target_link_libraries(kahip_static PRIVATE kahip_headers kahip_options kahip_warnings argtable3) +set_target_properties( + kahip_static + PROPERTIES PUBLIC_HEADER interface/kaHIP_interface.h +) +target_link_libraries( + kahip_static + PRIVATE kahip_headers kahip_options kahip_warnings argtable3 +) if(metis_FOUND) - target_link_libraries(kahip_static PRIVATE metis GKlib) + target_link_libraries(kahip_static PRIVATE metis GKlib) endif() -install(TARGETS kahip_static - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - PUBLIC_HEADER DESTINATION include - ) +install( + TARGETS kahip_static + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + PUBLIC_HEADER DESTINATION include +) # ParHIP if(NOT NOMPI AND PARHIP) - add_subdirectory(parallel/modified_kahip) - add_subdirectory(parallel/parallel_src) + add_subdirectory(parallel/modified_kahip) + add_subdirectory(parallel/parallel_src) endif() if(USE_ILP) - find_package(Gurobi REQUIRED) - MESSAGE("Using Gurobi for ILP solver in ilp_improve") - add_executable(ilp_improve app/ilp_improve.cpp) - target_link_libraries(ilp_improve PRIVATE libkaffpa libmapping) - target_include_directories(ilp_improve PUBLIC ${GUROBI_INCLUDE_DIR}) - target_compile_definitions(ilp_improve PRIVATE "-DMODE_ILPIMPROVE") - target_link_libraries(ilp_improve PRIVATE OpenMP::OpenMP_CXX ${GUROBI_LIBRARIES}) - - add_executable(ilp_exact app/ilp_exact.cpp) - target_link_libraries(ilp_exact PRIVATE libkaffpa libmapping) - target_include_directories(ilp_exact PUBLIC ${GUROBI_INCLUDE_DIR}) - target_compile_definitions(ilp_exact PRIVATE "-DMODE_ILPIMPROVE -DMODE_ILPEXACT") - target_link_libraries(ilp_exact PRIVATE OpenMP::OpenMP_CXX ${GUROBI_LIBRARIES}) - + find_package(Gurobi REQUIRED) + message("Using Gurobi for ILP solver in ilp_improve") + add_executable(ilp_improve app/ilp_improve.cpp) + target_link_libraries(ilp_improve PRIVATE libkaffpa libmapping) + target_include_directories(ilp_improve PUBLIC ${GUROBI_INCLUDE_DIR}) + target_compile_definitions(ilp_improve PRIVATE "-DMODE_ILPIMPROVE") + target_link_libraries( + ilp_improve + PRIVATE OpenMP::OpenMP_CXX ${GUROBI_LIBRARIES} + ) + + add_executable(ilp_exact app/ilp_exact.cpp) + target_link_libraries(ilp_exact PRIVATE libkaffpa libmapping) + target_include_directories(ilp_exact PUBLIC ${GUROBI_INCLUDE_DIR}) + target_compile_definitions( + ilp_exact + PRIVATE "-DMODE_ILPIMPROVE -DMODE_ILPEXACT" + ) + target_link_libraries( + ilp_exact + PRIVATE OpenMP::OpenMP_CXX ${GUROBI_LIBRARIES} + ) endif() # pybind11 module diff --git a/cmake/Cache.cmake b/cmake/Cache.cmake index b73e7694..1e7e1658 100644 --- a/cmake/Cache.cmake +++ b/cmake/Cache.cmake @@ -1,33 +1,34 @@ # Enable cache if available function(kahip_enable_cache) - set(CACHE_OPTION - "ccache" - CACHE STRING "Compiler cache to be used") - set(CACHE_OPTION_VALUES "ccache" "sccache") - set_property(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES}) - list( - FIND - CACHE_OPTION_VALUES - ${CACHE_OPTION} - CACHE_OPTION_INDEX) + set(CACHE_OPTION "ccache" CACHE STRING "Compiler cache to be used") + set(CACHE_OPTION_VALUES "ccache" "sccache") + set_property(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES}) + list(FIND CACHE_OPTION_VALUES ${CACHE_OPTION} CACHE_OPTION_INDEX) - if(${CACHE_OPTION_INDEX} EQUAL -1) - message( - STATUS - "Using custom compiler cache system: '${CACHE_OPTION}', explicitly supported entries are ${CACHE_OPTION_VALUES}" - ) - endif() + if(${CACHE_OPTION_INDEX} EQUAL -1) + message( + STATUS + "Using custom compiler cache system: '${CACHE_OPTION}', explicitly supported entries are ${CACHE_OPTION_VALUES}" + ) + endif() - find_program(CACHE_BINARY NAMES ${CACHE_OPTION_VALUES}) - if(CACHE_BINARY) - message(STATUS "${CACHE_BINARY} found and enabled") - set(CMAKE_CXX_COMPILER_LAUNCHER - ${CACHE_BINARY} - CACHE FILEPATH "CXX compiler cache used") - set(CMAKE_C_COMPILER_LAUNCHER - ${CACHE_BINARY} - CACHE FILEPATH "C compiler cache used") - else() - message(WARNING "${CACHE_OPTION} is enabled but was not found. Not using it") - endif() + find_program(CACHE_BINARY NAMES ${CACHE_OPTION_VALUES}) + if(CACHE_BINARY) + message(STATUS "${CACHE_BINARY} found and enabled") + set(CMAKE_CXX_COMPILER_LAUNCHER + ${CACHE_BINARY} + CACHE FILEPATH + "CXX compiler cache used" + ) + set(CMAKE_C_COMPILER_LAUNCHER + ${CACHE_BINARY} + CACHE FILEPATH + "C compiler cache used" + ) + else() + message( + WARNING + "${CACHE_OPTION} is enabled but was not found. Not using it" + ) + endif() endfunction() diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index 0fc0c2e0..2018895a 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -3,115 +3,120 @@ # https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md function( - kahip_set_project_warnings - project_name - WARNINGS_AS_ERRORS - MSVC_WARNINGS - CLANG_WARNINGS - GCC_WARNINGS - CUDA_WARNINGS) - if("${MSVC_WARNINGS}" STREQUAL "") - set(MSVC_WARNINGS - /W4 # Baseline reasonable warnings - /w14242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data - /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data - /w14263 # 'function': member function does not override any base class virtual member function - /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not - # be destructed correctly - /w14287 # 'operator': unsigned/negative constant mismatch - /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside - # the for-loop scope - /w14296 # 'operator': expression is always 'boolean_value' - /w14311 # 'variable': pointer truncation from 'type1' to 'type2' - /w14545 # expression before comma evaluates to a function which is missing an argument list - /w14546 # function call before comma missing argument list - /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect - /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? - /w14555 # expression has no effect; expected expression with side- effect - /w14619 # pragma warning: there is no warning number 'number' - /w14640 # Enable warning on thread un-safe static member initialization - /w14826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. - /w14905 # wide string literal cast to 'LPSTR' - /w14906 # string literal cast to 'LPWSTR' - /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied - /permissive- # standards conformance mode for MSVC compiler. - ) - endif() + kahip_set_project_warnings + project_name + WARNINGS_AS_ERRORS + MSVC_WARNINGS + CLANG_WARNINGS + GCC_WARNINGS + CUDA_WARNINGS +) + if("${MSVC_WARNINGS}" STREQUAL "") + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not + # be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside + # the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + endif() - if("${CLANG_WARNINGS}" STREQUAL "") - set(CLANG_WARNINGS - -Wall - -Wextra # reasonable and standard - -Wshadow # warn the user if a variable declaration shadows one from a parent context - -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps - # catch hard to track down memory errors - -Wold-style-cast # warn for c-style casts - -Wcast-align # warn for potential performance problem casts - -Wunused # warn on anything being unused - -Woverloaded-virtual # warn if you overload (not override) a virtual function - -Wpedantic # warn if non-standard C++ is used - -Wconversion # warn on type conversions that may lose data - -Wno-sign-conversion # warn on sign conversions - -Wnull-dereference # warn if a null dereference is detected - -Wdouble-promotion # warn if float is implicit promoted to double - -Wformat=2 # warn on security issues around functions that format output (ie printf) - -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation - -Wno-float-conversion - ) - endif() + if("${CLANG_WARNINGS}" STREQUAL "") + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps + # catch hard to track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wno-sign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output (ie printf) + -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation + -Wno-float-conversion + ) + endif() - if("${GCC_WARNINGS}" STREQUAL "") - set(GCC_WARNINGS - ${CLANG_WARNINGS} - -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist - -Wduplicated-cond # warn if if / else chain has duplicated conditions - -Wduplicated-branches # warn if if / else branches have duplicated code - -Wlogical-op # warn about logical operations being used where bitwise were probably wanted - -Wuseless-cast # warn if you perform a cast to the same type - -Wsuggest-override # warn if an overridden member function is not marked 'override' or 'final' - ) - endif() + if("${GCC_WARNINGS}" STREQUAL "") + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were probably wanted + -Wuseless-cast # warn if you perform a cast to the same type + -Wsuggest-override # warn if an overridden member function is not marked 'override' or 'final' + ) + endif() - if("${CUDA_WARNINGS}" STREQUAL "") - set(CUDA_WARNINGS - -Wall - -Wextra - -Wunused - -Wconversion - -Wshadow - # TODO add more Cuda warnings - ) - endif() + if("${CUDA_WARNINGS}" STREQUAL "") + set(CUDA_WARNINGS + -Wall + -Wextra + -Wunused + -Wconversion + -Wshadow + # TODO add more Cuda warnings + ) + endif() - if(WARNINGS_AS_ERRORS) - message(TRACE "Warnings are treated as errors") - list(APPEND CLANG_WARNINGS -Werror) - list(APPEND GCC_WARNINGS -Werror) - list(APPEND MSVC_WARNINGS /WX) - endif() + if(WARNINGS_AS_ERRORS) + message(TRACE "Warnings are treated as errors") + list(APPEND CLANG_WARNINGS -Werror) + list(APPEND GCC_WARNINGS -Werror) + list(APPEND MSVC_WARNINGS /WX) + endif() - if(MSVC) - set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS}) - elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") - set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS}) - elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS}) - else() - message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") - # TODO support Intel compiler - endif() + if(MSVC) + set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS}) + else() + message( + AUTHOR_WARNING + "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'" + ) + # TODO support Intel compiler + endif() - # use the same warning flags for C - set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}") + # use the same warning flags for C + set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}") - set(PROJECT_WARNINGS_CUDA "${CUDA_WARNINGS}") + set(PROJECT_WARNINGS_CUDA "${CUDA_WARNINGS}") - target_compile_options( - ${project_name} - INTERFACE # C++ warnings - $<$:${PROJECT_WARNINGS_CXX}> - # C warnings - $<$:${PROJECT_WARNINGS_C}> - # Cuda warnings - $<$:${PROJECT_WARNINGS_CUDA}>) + target_compile_options( + ${project_name} + INTERFACE # C++ warnings + $<$:${PROJECT_WARNINGS_CXX}> + # C warnings + $<$:${PROJECT_WARNINGS_C}> + # Cuda warnings + $<$:${PROJECT_WARNINGS_CUDA}> + ) endfunction() diff --git a/cmake/FindGurobi.cmake b/cmake/FindGurobi.cmake index 79b48c92..768d6dc0 100644 --- a/cmake/FindGurobi.cmake +++ b/cmake/FindGurobi.cmake @@ -6,35 +6,29 @@ # GUROBI_INCLUDE_DIRS - The Gurobi include directories # GUROBI_LIBRARIES - The libraries needed to use Gurobi -find_path(GUROBI_INCLUDE_DIR - NAMES gurobi_c++.h - PATHS "$ENV{GUROBI_HOME}/include" - ) - -find_library( GUROBI_LIBRARY - NAMES gurobi90 - gurobi81 - gurobi80 - gurobi75 - PATHS "$ENV{GUROBI_HOME}/lib" - ) +find_path( + GUROBI_INCLUDE_DIR + NAMES gurobi_c++.h + PATHS "$ENV{GUROBI_HOME}/include" +) +find_library( + GUROBI_LIBRARY + NAMES gurobi90 gurobi81 gurobi80 gurobi75 + PATHS "$ENV{GUROBI_HOME}/lib" +) -find_library( GUROBI_CXX_LIBRARY - NAMES gurobi_g++5.2 - PATHS "$ENV{GUROBI_HOME}/lib" - ) +find_library( + GUROBI_CXX_LIBRARY + NAMES gurobi_g++5.2 + PATHS "$ENV{GUROBI_HOME}/lib" +) set(GUROBI_INCLUDE_DIRS "${GUROBI_INCLUDE_DIR}") set(GUROBI_LIBRARIES "${GUROBI_CXX_LIBRARY};${GUROBI_LIBRARY}") -if (GUROBI_INCLUDE_DIRS AND GUROBI_LIBRARIES) - set(GUROBI_FOUND TRUE) +if(GUROBI_INCLUDE_DIRS AND GUROBI_LIBRARIES) + set(GUROBI_FOUND TRUE) endif() -mark_as_advanced( - GUROBI_INCLUDE_DIRS - GUROBI_LIBRARIES -) - - +mark_as_advanced(GUROBI_INCLUDE_DIRS GUROBI_LIBRARIES) diff --git a/cmake/InterproceduralOptimization.cmake b/cmake/InterproceduralOptimization.cmake index c8713ac2..000d70f5 100644 --- a/cmake/InterproceduralOptimization.cmake +++ b/cmake/InterproceduralOptimization.cmake @@ -1,9 +1,9 @@ macro(kahip_enable_ipo) - include(CheckIPOSupported) - check_ipo_supported(RESULT result OUTPUT output) - if(result) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) - else() - message(SEND_ERROR "IPO is not supported: ${output}") - endif() + include(CheckIPOSupported) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) + else() + message(SEND_ERROR "IPO is not supported: ${output}") + endif() endmacro() diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index ae20f7ff..c5d00c0a 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -1,95 +1,107 @@ -macro(kahip_project_configure) - message(STATUS "Configuring Kahip") - - include(${CMAKE_SOURCE_DIR}/cmake/Cache.cmake) - include(${CMAKE_SOURCE_DIR}/cmake/CompilerWarnings.cmake) - include(${CMAKE_SOURCE_DIR}/cmake/InterproceduralOptimization.cmake) - include(${CMAKE_SOURCE_DIR}/cmake/Sanitizers.cmake) - include(${CMAKE_SOURCE_DIR}/cmake/StaticAnalyzers.cmake) - include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) - - kahip_supports_sanitizers() - - option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) - option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" ON) - option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" ON) - option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) - option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) - option(kahip_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) - option(kahip_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) - option(kahip_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" ${SUPPORTS_UBSAN}) - option(kahip_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) - option(kahip_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) - option(kahip_ENABLE_CLANG_TIDY "Enable clang-tidy" OFF) - option(kahip_ENABLE_CPPCHECK "Enable cpp-check analysis" OFF) - option(kahip_ENABLE_IWYU "Enable `include_what_you_use`" OFF) - option(kahip_ENABLE_CACHE "Enable ccache" OFF) - option(NONATIVEOPTIMIZATIONS "Disable --march=native optimizations" OFF) - - if (kahip_ENABLE_UNITY_BUILD) - set(CMAKE_UNITY_BUILD ON) - endif () - - - if(kahip_ENABLE_IPO) - kahip_enable_ipo() - endif() - - add_library(kahip_warnings INTERFACE) - add_library(kahip_options INTERFACE) - - include(${CMAKE_SOURCE_DIR}/cmake/StandardProjectSettings.cmake) - - if(kahip_ENABLE_USER_LINKER) - include(${CMAKE_SOURCE_DIR}/cmake/Linker.cmake) - kahip_configure_linker(kahip_options) - endif() - - kahip_set_project_warnings( - kahip_warnings - ${kahip_WARNINGS_AS_ERRORS} - "" - "" - "" - "") - - if (kahip_ENABLE_SANITIZERS) - kahip_enable_sanitizers( - kahip_options - ${kahip_ENABLE_SANITIZER_ADDRESS} - ${kahip_ENABLE_SANITIZER_LEAK} - ${kahip_ENABLE_SANITIZER_UNDEFINED} - ${kahip_ENABLE_SANITIZER_THREAD} - ${kahip_ENABLE_SANITIZER_MEMORY}) - endif () - - if(kahip_ENABLE_CLANG_TIDY) - kahip_enable_clang_tidy(kahip_options ${kahip_WARNINGS_AS_ERRORS}) - endif() - - if(kahip_ENABLE_CPPCHECK) - kahip_enable_cppcheck(${kahip_WARNINGS_AS_ERRORS} "" # override cppcheck options - ) - endif() +message(STATUS "Configuring Kahip") + +include(${CMAKE_SOURCE_DIR}/cmake/Cache.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/CompilerWarnings.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/InterproceduralOptimization.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/Sanitizers.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/StaticAnalyzers.cmake) +include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) + +kahip_supports_sanitizers() + +option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) +option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" ON) +option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" ON) +option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) +option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) +option( + kahip_ENABLE_SANITIZER_ADDRESS + "Enable address sanitizer" + ${SUPPORTS_ASAN} +) +option(kahip_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) +option( + kahip_ENABLE_SANITIZER_UNDEFINED + "Enable undefined sanitizer" + ${SUPPORTS_UBSAN} +) +option(kahip_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) +option(kahip_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) +option(kahip_ENABLE_CLANG_TIDY "Enable clang-tidy" OFF) +option(kahip_ENABLE_CPPCHECK "Enable cpp-check analysis" OFF) +option(kahip_ENABLE_IWYU "Enable `include_what_you_use`" OFF) +option(kahip_ENABLE_CACHE "Enable ccache" OFF) +option(NONATIVEOPTIMIZATIONS "Disable --march=native optimizations" OFF) + +if(kahip_ENABLE_UNITY_BUILD) + set(CMAKE_UNITY_BUILD ON) +endif() + +if(kahip_ENABLE_IPO) + kahip_enable_ipo() +endif() + +add_library(kahip_warnings INTERFACE) +add_library(kahip_options INTERFACE) + +include(${CMAKE_SOURCE_DIR}/cmake/StandardProjectSettings.cmake) + +if(kahip_ENABLE_USER_LINKER) + include(${CMAKE_SOURCE_DIR}/cmake/Linker.cmake) + kahip_configure_linker(kahip_options) +endif() + +kahip_set_project_warnings( + kahip_warnings + ${kahip_WARNINGS_AS_ERRORS} + "" + "" + "" + "" +) + +if(kahip_ENABLE_SANITIZERS) + kahip_enable_sanitizers( + kahip_options + ${kahip_ENABLE_SANITIZER_ADDRESS} + ${kahip_ENABLE_SANITIZER_LEAK} + ${kahip_ENABLE_SANITIZER_UNDEFINED} + ${kahip_ENABLE_SANITIZER_THREAD} + ${kahip_ENABLE_SANITIZER_MEMORY} + ) +endif() - if(kahip_ENABLE_IWYU) - kahip_enable_include_what_you_use() - endif() +if(kahip_ENABLE_CLANG_TIDY) + kahip_enable_clang_tidy(kahip_options ${kahip_WARNINGS_AS_ERRORS}) +endif() - # tweak compiler flags - target_compile_features(kahip_options INTERFACE cxx_std_20) - message(VERBOSE "Checking compiler feature support") - CHECK_CXX_COMPILER_FLAG(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) - target_compile_options(kahip_options INTERFACE - $<$:-funroll-loops> - ) - CHECK_CXX_COMPILER_FLAG(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) - target_compile_options(kahip_options INTERFACE - $<$:-funroll-loops> +if(kahip_ENABLE_CPPCHECK) + kahip_enable_cppcheck( + ${kahip_WARNINGS_AS_ERRORS} + "" # override cppcheck options ) - CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) - target_compile_options(kahip_options INTERFACE - $<$,$>,$,$>>:-march=native> - ) - -endmacro(kahip_project_configure) \ No newline at end of file +endif() + +if(kahip_ENABLE_IWYU) + kahip_enable_include_what_you_use() +endif() + +# tweak compiler flags +target_compile_features(kahip_options INTERFACE cxx_std_20) +message(VERBOSE "Checking compiler feature support") +check_cxx_compiler_flag(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) +target_compile_options( + kahip_options + INTERFACE $<$:-funroll-loops> +) +check_cxx_compiler_flag(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) +target_compile_options( + kahip_options + INTERFACE $<$:-funroll-loops> +) +check_cxx_compiler_flag(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) +target_compile_options( + kahip_options + INTERFACE + $<$,$>,$,$>>:-march=native> +) diff --git a/cmake/Linker.cmake b/cmake/Linker.cmake index 1b0b06eb..94c78042 100644 --- a/cmake/Linker.cmake +++ b/cmake/Linker.cmake @@ -1,31 +1,34 @@ macro(kahip_configure_linker project_name) - include(CheckCXXCompilerFlag) + include(CheckCXXCompilerFlag) - set(USER_LINKER_OPTION - "lld" - CACHE STRING "Linker to be used") - set(USER_LINKER_OPTION_VALUES "lld" "gold" "bfd" "mold") - set_property(CACHE USER_LINKER_OPTION PROPERTY STRINGS ${USER_LINKER_OPTION_VALUES}) - list( - FIND - USER_LINKER_OPTION_VALUES - ${USER_LINKER_OPTION} - USER_LINKER_OPTION_INDEX) + set(USER_LINKER_OPTION "lld" CACHE STRING "Linker to be used") + set(USER_LINKER_OPTION_VALUES "lld" "gold" "bfd" "mold") + set_property( + CACHE USER_LINKER_OPTION + PROPERTY STRINGS ${USER_LINKER_OPTION_VALUES} + ) + list( + FIND + USER_LINKER_OPTION_VALUES + ${USER_LINKER_OPTION} + USER_LINKER_OPTION_INDEX + ) - if(${USER_LINKER_OPTION_INDEX} EQUAL -1) - message( - STATUS - "Using custom linker: '${USER_LINKER_OPTION}', explicitly supported entries are ${USER_LINKER_OPTION_VALUES}") - endif() + if(${USER_LINKER_OPTION_INDEX} EQUAL -1) + message( + STATUS + "Using custom linker: '${USER_LINKER_OPTION}', explicitly supported entries are ${USER_LINKER_OPTION_VALUES}" + ) + endif() - if(NOT myproject_ENABLE_USER_LINKER) - return() - endif() + if(NOT myproject_ENABLE_USER_LINKER) + return() + endif() - set(LINKER_FLAG "-fuse-ld=${USER_LINKER_OPTION}") + set(LINKER_FLAG "-fuse-ld=${USER_LINKER_OPTION}") - check_cxx_compiler_flag(${LINKER_FLAG} CXX_SUPPORTS_USER_LINKER) - if(CXX_SUPPORTS_USER_LINKER) - target_compile_options(${project_name} INTERFACE ${LINKER_FLAG}) - endif() + check_cxx_compiler_flag(${LINKER_FLAG} CXX_SUPPORTS_USER_LINKER) + if(CXX_SUPPORTS_USER_LINKER) + target_compile_options(${project_name} INTERFACE ${LINKER_FLAG}) + endif() endmacro() diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake index fc52a113..f2f80786 100644 --- a/cmake/Sanitizers.cmake +++ b/cmake/Sanitizers.cmake @@ -1,104 +1,143 @@ macro(kahip_supports_sanitizers) - if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND NOT WIN32) - set(SUPPORTS_UBSAN ON) - else() - set(SUPPORTS_UBSAN OFF) - endif() + if( + ( + CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" + OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*" + ) + AND NOT WIN32 + ) + set(SUPPORTS_UBSAN ON) + else() + set(SUPPORTS_UBSAN OFF) + endif() - if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND WIN32) - set(SUPPORTS_ASAN OFF) - else() - set(SUPPORTS_ASAN ON) - endif() + if( + ( + CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" + OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*" + ) + AND WIN32 + ) + set(SUPPORTS_ASAN OFF) + else() + set(SUPPORTS_ASAN ON) + endif() endmacro() - function( - kahip_enable_sanitizers project_name - ENABLE_SANITIZER_ADDRESS - ENABLE_SANITIZER_LEAK - ENABLE_SANITIZER_UNDEFINED_BEHAVIOR - ENABLE_SANITIZER_THREAD - ENABLE_SANITIZER_MEMORY) + kahip_enable_sanitizers + project_name + ENABLE_SANITIZER_ADDRESS + ENABLE_SANITIZER_LEAK + ENABLE_SANITIZER_UNDEFINED_BEHAVIOR + ENABLE_SANITIZER_THREAD + ENABLE_SANITIZER_MEMORY +) + if( + CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang" + ) + set(SANITIZERS "") - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") - set(SANITIZERS "") - - if(${ENABLE_SANITIZER_ADDRESS}) - list(APPEND SANITIZERS "address") - endif() + if(${ENABLE_SANITIZER_ADDRESS}) + list(APPEND SANITIZERS "address") + endif() - if(${ENABLE_SANITIZER_LEAK}) - list(APPEND SANITIZERS "leak") - endif() + if(${ENABLE_SANITIZER_LEAK}) + list(APPEND SANITIZERS "leak") + endif() - if(${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR}) - list(APPEND SANITIZERS "undefined") - endif() + if(${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR}) + list(APPEND SANITIZERS "undefined") + endif() - if(${ENABLE_SANITIZER_THREAD}) - if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) - message(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled") - else() - list(APPEND SANITIZERS "thread") - endif() - endif() + if(${ENABLE_SANITIZER_THREAD}) + if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) + message( + WARNING + "Thread sanitizer does not work with Address and Leak sanitizer enabled" + ) + else() + list(APPEND SANITIZERS "thread") + endif() + endif() - if(${ENABLE_SANITIZER_MEMORY} AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") - message( - WARNING - "Memory sanitizer requires all the code (including libc++) to be MSan-instrumented otherwise it reports false positives" - ) - if("address" IN_LIST SANITIZERS - OR "thread" IN_LIST SANITIZERS - OR "leak" IN_LIST SANITIZERS) - message(WARNING "Memory sanitizer does not work with Address, Thread or Leak sanitizer enabled") - else() - list(APPEND SANITIZERS "memory") - endif() - endif() - elseif(MSVC) - if(${ENABLE_SANITIZER_ADDRESS}) - list(APPEND SANITIZERS "address") - endif() - if(${ENABLE_SANITIZER_LEAK} - OR ${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR} - OR ${ENABLE_SANITIZER_THREAD} - OR ${ENABLE_SANITIZER_MEMORY}) - message(WARNING "MSVC only supports address sanitizer") + if( + ${ENABLE_SANITIZER_MEMORY} + AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang" + ) + message( + WARNING + "Memory sanitizer requires all the code (including libc++) to be MSan-instrumented otherwise it reports false positives" + ) + if( + "address" IN_LIST SANITIZERS + OR "thread" IN_LIST SANITIZERS + OR "leak" IN_LIST SANITIZERS + ) + message( + WARNING + "Memory sanitizer does not work with Address, Thread or Leak sanitizer enabled" + ) + else() + list(APPEND SANITIZERS "memory") + endif() + endif() + elseif(MSVC) + if(${ENABLE_SANITIZER_ADDRESS}) + list(APPEND SANITIZERS "address") + endif() + if( + ${ENABLE_SANITIZER_LEAK} + OR ${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR} + OR ${ENABLE_SANITIZER_THREAD} + OR ${ENABLE_SANITIZER_MEMORY} + ) + message(WARNING "MSVC only supports address sanitizer") + endif() endif() - endif() - list( - JOIN - SANITIZERS - "," - LIST_OF_SANITIZERS) + list(JOIN SANITIZERS "," LIST_OF_SANITIZERS) - if(LIST_OF_SANITIZERS) - if(NOT - "${LIST_OF_SANITIZERS}" - STREQUAL - "") - if(NOT MSVC) - target_compile_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) - target_link_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) - else() - string(FIND "$ENV{PATH}" "$ENV{VSINSTALLDIR}" index_of_vs_install_dir) - if("${index_of_vs_install_dir}" STREQUAL "-1") - message( - SEND_ERROR - "Using MSVC sanitizers requires setting the MSVC environment before building the project. Please manually open the MSVC command prompt and rebuild the project." - ) + if(LIST_OF_SANITIZERS) + if(NOT "${LIST_OF_SANITIZERS}" STREQUAL "") + if(NOT MSVC) + target_compile_options( + ${project_name} + INTERFACE -fsanitize=${LIST_OF_SANITIZERS} + ) + target_link_options( + ${project_name} + INTERFACE -fsanitize=${LIST_OF_SANITIZERS} + ) + else() + string( + FIND + "$ENV{PATH}" + "$ENV{VSINSTALLDIR}" + index_of_vs_install_dir + ) + if("${index_of_vs_install_dir}" STREQUAL "-1") + message( + SEND_ERROR + "Using MSVC sanitizers requires setting the MSVC environment before building the project. Please manually open the MSVC command prompt and rebuild the project." + ) + endif() + target_compile_options( + ${project_name} + INTERFACE + /fsanitize=${LIST_OF_SANITIZERS} + /Zi + /INCREMENTAL:NO + ) + target_compile_definitions( + ${project_name} + INTERFACE + _DISABLE_VECTOR_ANNOTATION + _DISABLE_STRING_ANNOTATION + ) + target_link_options(${project_name} INTERFACE /INCREMENTAL:NO) + endif() endif() - target_compile_options(${project_name} INTERFACE /fsanitize=${LIST_OF_SANITIZERS} /Zi /INCREMENTAL:NO) - target_compile_definitions(${project_name} INTERFACE _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION) - target_link_options(${project_name} INTERFACE /INCREMENTAL:NO) - endif() endif() - endif() - endfunction() - - - diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake index e9a3f706..ba0457c0 100644 --- a/cmake/StandardProjectSettings.cmake +++ b/cmake/StandardProjectSettings.cmake @@ -1,17 +1,20 @@ # Set a default build type if none was specified if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") - set(CMAKE_BUILD_TYPE - RelWithDebInfo - CACHE STRING "Choose the type of build." FORCE) - # Set the possible values of build type for cmake-gui, ccmake - set_property( - CACHE CMAKE_BUILD_TYPE - PROPERTY STRINGS - "Debug" - "Release" - "MinSizeRel" - "RelWithDebInfo") + message( + STATUS + "Setting build type to 'RelWithDebInfo' as none was specified." + ) + set(CMAKE_BUILD_TYPE + RelWithDebInfo + CACHE STRING + "Choose the type of build." + FORCE + ) + # Set the possible values of build type for cmake-gui, ccmake + set_property( + CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo" + ) endif() # Generate compile_commands.json to make it easier to work with clang based tools @@ -25,22 +28,30 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) # Enhance error reporting and compiler messages if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") - if(WIN32) - # On Windows cuda nvcc uses cl and not clang - add_compile_options($<$:-fcolor-diagnostics> $<$:-fcolor-diagnostics>) - else() - add_compile_options(-fcolor-diagnostics) - endif() + if(WIN32) + # On Windows cuda nvcc uses cl and not clang + add_compile_options( + $<$:-fcolor-diagnostics> + $<$:-fcolor-diagnostics> + ) + else() + add_compile_options(-fcolor-diagnostics) + endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - if(WIN32) - # On Windows cuda nvcc uses cl and not gcc - add_compile_options($<$:-fdiagnostics-color=always> - $<$:-fdiagnostics-color=always>) - else() - add_compile_options(-fdiagnostics-color=always) - endif() + if(WIN32) + # On Windows cuda nvcc uses cl and not gcc + add_compile_options( + $<$:-fdiagnostics-color=always> + $<$:-fdiagnostics-color=always> + ) + else() + add_compile_options(-fdiagnostics-color=always) + endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER 1900) - add_compile_options(/diagnostics:column) + add_compile_options(/diagnostics:column) else() - message(STATUS "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler.") + message( + STATUS + "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler." + ) endif() diff --git a/cmake/StaticAnalyzers.cmake b/cmake/StaticAnalyzers.cmake index 54650cc5..a1b5d17e 100644 --- a/cmake/StaticAnalyzers.cmake +++ b/cmake/StaticAnalyzers.cmake @@ -1,112 +1,129 @@ macro(kahip_enable_cppcheck WARNINGS_AS_ERRORS CPPCHECK_OPTIONS) - find_program(CPPCHECK cppcheck) - if(CPPCHECK) + find_program(CPPCHECK cppcheck) + if(CPPCHECK) + if(CMAKE_GENERATOR MATCHES ".*Visual Studio.*") + set(CPPCHECK_TEMPLATE "vs") + else() + set(CPPCHECK_TEMPLATE "gcc") + endif() - if(CMAKE_GENERATOR MATCHES ".*Visual Studio.*") - set(CPPCHECK_TEMPLATE "vs") - else() - set(CPPCHECK_TEMPLATE "gcc") - endif() + if("${CPPCHECK_OPTIONS}" STREQUAL "") + # Enable all warnings that are actionable by the user of this toolset + # style should enable the other 3, but we'll be explicit just in case + set(SUPPRESS_DIR "*:${CMAKE_CURRENT_BINARY_DIR}/_deps/*.h") + message(STATUS "CPPCHECK_OPTIONS suppress: ${SUPPRESS_DIR}") + set(CMAKE_CXX_CPPCHECK + ${CPPCHECK} + --template=${CPPCHECK_TEMPLATE} + --enable=style,performance,warning,portability + --inline-suppr + # We cannot act on a bug/missing feature of cppcheck + --suppress=cppcheckError + --suppress=internalAstError + # if a file does not have an internalAstError, we get an unmatchedSuppression error + --suppress=unmatchedSuppression + # noisy and incorrect sometimes + --suppress=passedByValue + # ignores code that cppcheck thinks is invalid C++ + --suppress=syntaxError + --suppress=preprocessorErrorDirective + --inconclusive + --suppress=${SUPPRESS_DIR} + ) + else() + # if the user provides a CPPCHECK_OPTIONS with a template specified, it will override this template + set(CMAKE_CXX_CPPCHECK + ${CPPCHECK} + --template=${CPPCHECK_TEMPLATE} + ${CPPCHECK_OPTIONS} + ) + endif() - if("${CPPCHECK_OPTIONS}" STREQUAL "") - # Enable all warnings that are actionable by the user of this toolset - # style should enable the other 3, but we'll be explicit just in case - set(SUPPRESS_DIR "*:${CMAKE_CURRENT_BINARY_DIR}/_deps/*.h") - message(STATUS "CPPCHECK_OPTIONS suppress: ${SUPPRESS_DIR}") - set(CMAKE_CXX_CPPCHECK - ${CPPCHECK} - --template=${CPPCHECK_TEMPLATE} - --enable=style,performance,warning,portability - --inline-suppr - # We cannot act on a bug/missing feature of cppcheck - --suppress=cppcheckError - --suppress=internalAstError - # if a file does not have an internalAstError, we get an unmatchedSuppression error - --suppress=unmatchedSuppression - # noisy and incorrect sometimes - --suppress=passedByValue - # ignores code that cppcheck thinks is invalid C++ - --suppress=syntaxError - --suppress=preprocessorErrorDirective - --inconclusive - --suppress=${SUPPRESS_DIR}) + if(NOT "${CMAKE_CXX_STANDARD}" STREQUAL "") + set(CMAKE_CXX_CPPCHECK + ${CMAKE_CXX_CPPCHECK} + --std=c++${CMAKE_CXX_STANDARD} + ) + endif() + if(${WARNINGS_AS_ERRORS}) + list(APPEND CMAKE_CXX_CPPCHECK --error-exitcode=2) + endif() else() - # if the user provides a CPPCHECK_OPTIONS with a template specified, it will override this template - set(CMAKE_CXX_CPPCHECK ${CPPCHECK} --template=${CPPCHECK_TEMPLATE} ${CPPCHECK_OPTIONS}) - endif() - - if(NOT - "${CMAKE_CXX_STANDARD}" - STREQUAL - "") - set(CMAKE_CXX_CPPCHECK ${CMAKE_CXX_CPPCHECK} --std=c++${CMAKE_CXX_STANDARD}) - endif() - if(${WARNINGS_AS_ERRORS}) - list(APPEND CMAKE_CXX_CPPCHECK --error-exitcode=2) + message( + ${WARNING_MESSAGE} + "cppcheck requested but executable not found" + ) endif() - else() - message(${WARNING_MESSAGE} "cppcheck requested but executable not found") - endif() endmacro() macro(kahip_enable_clang_tidy target WARNINGS_AS_ERRORS) + find_program(CLANGTIDY clang-tidy) + if(CLANGTIDY) + if(NOT CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + get_target_property( + TARGET_PCH + ${target} + INTERFACE_PRECOMPILE_HEADERS + ) - find_program(CLANGTIDY clang-tidy) - if(CLANGTIDY) - if(NOT - CMAKE_CXX_COMPILER_ID - MATCHES - ".*Clang") + if("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND") + get_target_property(TARGET_PCH ${target} PRECOMPILE_HEADERS) + endif() - get_target_property(TARGET_PCH ${target} INTERFACE_PRECOMPILE_HEADERS) + if(NOT ("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND")) + message( + SEND_ERROR + "clang-tidy cannot be enabled with non-clang compiler and PCH, clang-tidy fails to handle gcc's PCH file" + ) + endif() + endif() - if("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND") - get_target_property(TARGET_PCH ${target} PRECOMPILE_HEADERS) - endif() - - if(NOT ("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND")) - message( - SEND_ERROR - "clang-tidy cannot be enabled with non-clang compiler and PCH, clang-tidy fails to handle gcc's PCH file") - endif() - endif() + # construct the clang-tidy command line + set(CLANG_TIDY_OPTIONS + ${CLANGTIDY} + -extra-arg=-Wno-unknown-warning-option + -extra-arg=-Wno-ignored-optimization-argument + -extra-arg=-Wno-unused-command-line-argument + -p + ) + # set standard + if(NOT "${CMAKE_CXX_STANDARD}" STREQUAL "") + if("${CLANG_TIDY_OPTIONS_DRIVER_MODE}" STREQUAL "cl") + set(CLANG_TIDY_OPTIONS + ${CLANG_TIDY_OPTIONS} + -extra-arg=/std:c++${CMAKE_CXX_STANDARD} + ) + else() + set(CLANG_TIDY_OPTIONS + ${CLANG_TIDY_OPTIONS} + -extra-arg=-std=c++${CMAKE_CXX_STANDARD} + ) + endif() + endif() - # construct the clang-tidy command line - set(CLANG_TIDY_OPTIONS - ${CLANGTIDY} - -extra-arg=-Wno-unknown-warning-option - -extra-arg=-Wno-ignored-optimization-argument - -extra-arg=-Wno-unused-command-line-argument - -p) - # set standard - if(NOT - "${CMAKE_CXX_STANDARD}" - STREQUAL - "") - if("${CLANG_TIDY_OPTIONS_DRIVER_MODE}" STREQUAL "cl") - set(CLANG_TIDY_OPTIONS ${CLANG_TIDY_OPTIONS} -extra-arg=/std:c++${CMAKE_CXX_STANDARD}) - else() - set(CLANG_TIDY_OPTIONS ${CLANG_TIDY_OPTIONS} -extra-arg=-std=c++${CMAKE_CXX_STANDARD}) - endif() - endif() + # set warnings as errors + if(${WARNINGS_AS_ERRORS}) + list(APPEND CLANG_TIDY_OPTIONS -warnings-as-errors=*) + endif() - # set warnings as errors - if(${WARNINGS_AS_ERRORS}) - list(APPEND CLANG_TIDY_OPTIONS -warnings-as-errors=*) + message("Also setting clang-tidy globally") + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_OPTIONS}) + else() + message( + ${WARNING_MESSAGE} + "clang-tidy requested but executable not found" + ) endif() - - message("Also setting clang-tidy globally") - set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_OPTIONS}) - else() - message(${WARNING_MESSAGE} "clang-tidy requested but executable not found") - endif() endmacro() macro(kahip_enable_include_what_you_use) - find_program(INCLUDE_WHAT_YOU_USE include-what-you-use) - if(INCLUDE_WHAT_YOU_USE) - set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE}) - else() - message(${WARNING_MESSAGE} "include-what-you-use requested but executable not found") - endif() + find_program(INCLUDE_WHAT_YOU_USE include-what-you-use) + if(INCLUDE_WHAT_YOU_USE) + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE}) + else() + message( + ${WARNING_MESSAGE} + "include-what-you-use requested but executable not found" + ) + endif() endmacro() diff --git a/cmake/Utilities.cmake b/cmake/Utilities.cmake index e278f518..ec164b8b 100644 --- a/cmake/Utilities.cmake +++ b/cmake/Utilities.cmake @@ -1,139 +1,86 @@ # find a substring from a string by a given prefix such as VCVARSALL_ENV_START -function( - find_substring_by_prefix - output - prefix - input) - # find the prefix - string(FIND "${input}" "${prefix}" prefix_index) - if("${prefix_index}" STREQUAL "-1") - message(SEND_ERROR "Could not find ${prefix} in ${input}") - endif() - # find the start index - string(LENGTH "${prefix}" prefix_length) - math(EXPR start_index "${prefix_index} + ${prefix_length}") +function(find_substring_by_prefix output prefix input) + # find the prefix + string(FIND "${input}" "${prefix}" prefix_index) + if("${prefix_index}" STREQUAL "-1") + message(SEND_ERROR "Could not find ${prefix} in ${input}") + endif() + # find the start index + string(LENGTH "${prefix}" prefix_length) + math(EXPR start_index "${prefix_index} + ${prefix_length}") - string( - SUBSTRING "${input}" - "${start_index}" - "-1" - _output) - set("${output}" - "${_output}" - PARENT_SCOPE) + string(SUBSTRING "${input}" "${start_index}" "-1" _output) + set("${output}" "${_output}" PARENT_SCOPE) endfunction() # A function to set environment variables of CMake from the output of `cmd /c set` function(set_env_from_string env_string) - # replace ; in paths with __sep__ so we can split on ; - string( - REGEX - REPLACE ";" - "__sep__" - env_string_sep_added - "${env_string}") + # replace ; in paths with __sep__ so we can split on ; + string(REGEX REPLACE ";" "__sep__" env_string_sep_added "${env_string}") - # the variables are separated by \r?\n - string( - REGEX - REPLACE "\r?\n" - ";" - env_list - "${env_string_sep_added}") + # the variables are separated by \r?\n + string(REGEX REPLACE "\r?\n" ";" env_list "${env_string_sep_added}") - foreach(env_var ${env_list}) - # split by = - string( - REGEX - REPLACE "=" - ";" - env_parts - "${env_var}") + foreach(env_var ${env_list}) + # split by = + string(REGEX REPLACE "=" ";" env_parts "${env_var}") - list(LENGTH env_parts env_parts_length) - if("${env_parts_length}" EQUAL "2") - # get the variable name and value - list( - GET - env_parts - 0 - env_name) - list( - GET - env_parts - 1 - env_value) + list(LENGTH env_parts env_parts_length) + if("${env_parts_length}" EQUAL "2") + # get the variable name and value + list(GET env_parts 0 env_name) + list(GET env_parts 1 env_value) - # recover ; in paths - string( - REGEX - REPLACE "__sep__" - ";" - env_value - "${env_value}") + # recover ; in paths + string(REGEX REPLACE "__sep__" ";" env_value "${env_value}") - # set env_name to env_value - set(ENV{${env_name}} "${env_value}") + # set env_name to env_value + set(ENV{${env_name}} "${env_value}") - # update cmake program path - if("${env_name}" EQUAL "PATH") - list(APPEND CMAKE_PROGRAM_PATH ${env_value}) - endif() - endif() - endforeach() + # update cmake program path + if("${env_name}" EQUAL "PATH") + list(APPEND CMAKE_PROGRAM_PATH ${env_value}) + endif() + endif() + endforeach() endfunction() function(get_all_targets var) - set(targets) - get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR}) - set(${var} - ${targets} - PARENT_SCOPE) + set(targets) + get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR}) + set(${var} ${targets} PARENT_SCOPE) endfunction() function(get_all_installable_targets var) - set(targets) - get_all_targets(targets) - foreach(_target ${targets}) - get_target_property(_target_type ${_target} TYPE) - if(NOT - ${_target_type} - MATCHES - ".*LIBRARY|EXECUTABLE") - list(REMOVE_ITEM targets ${_target}) - endif() - endforeach() - set(${var} - ${targets} - PARENT_SCOPE) + set(targets) + get_all_targets(targets) + foreach(_target ${targets}) + get_target_property(_target_type ${_target} TYPE) + if(NOT ${_target_type} MATCHES ".*LIBRARY|EXECUTABLE") + list(REMOVE_ITEM targets ${_target}) + endif() + endforeach() + set(${var} ${targets} PARENT_SCOPE) endfunction() macro(get_all_targets_recursive targets dir) - get_property( - subdirectories - DIRECTORY ${dir} - PROPERTY SUBDIRECTORIES) - foreach(subdir ${subdirectories}) - get_all_targets_recursive(${targets} ${subdir}) - endforeach() + get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES) + foreach(subdir ${subdirectories}) + get_all_targets_recursive(${targets} ${subdir}) + endforeach() - get_property( - current_targets - DIRECTORY ${dir} - PROPERTY BUILDSYSTEM_TARGETS) - list(APPEND ${targets} ${current_targets}) + get_property(current_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS) + list(APPEND ${targets} ${current_targets}) endmacro() function(is_verbose var) - if("CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "VERBOSE" - OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "DEBUG" - OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "TRACE") - set(${var} - ON - PARENT_SCOPE) - else() - set(${var} - OFF - PARENT_SCOPE) - endif() + if( + "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "VERBOSE" + OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "DEBUG" + OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "TRACE" + ) + set(${var} ON PARENT_SCOPE) + else() + set(${var} OFF PARENT_SCOPE) + endif() endfunction() diff --git a/extern/argtable3-3.2.2/CMakeLists.txt b/extern/argtable3-3.2.2/CMakeLists.txt index d3ace5fa..6b3cad8d 100644 --- a/extern/argtable3-3.2.2/CMakeLists.txt +++ b/extern/argtable3-3.2.2/CMakeLists.txt @@ -1,2 +1,6 @@ add_library(argtable3 argtable3.c) -target_include_directories(argtable3 SYSTEM PUBLIC ${CMAKE_SOURCE_DIR}/extern/argtable3-3.2.2/) \ No newline at end of file +target_include_directories( + argtable3 + SYSTEM + PUBLIC ${CMAKE_SOURCE_DIR}/extern/argtable3-3.2.2/ +) diff --git a/extern/argtable3-3.2.2/examples/CMakeLists.txt b/extern/argtable3-3.2.2/examples/CMakeLists.txt index 67e22218..79173bb1 100644 --- a/extern/argtable3-3.2.2/examples/CMakeLists.txt +++ b/extern/argtable3-3.2.2/examples/CMakeLists.txt @@ -29,26 +29,29 @@ ################################################################################ if(ARGTABLE3_ENABLE_ARG_REX_DEBUG) - add_definitions(-DARG_REX_DEBUG) + add_definitions(-DARG_REX_DEBUG) endif() if(NOT ARGTABLE3_REPLACE_GETOPT) - add_definitions(-DARG_REPLACE_GETOPT=0) + add_definitions(-DARG_REPLACE_GETOPT=0) endif() if(ARGTABLE3_LONG_ONLY) - add_definitions(-DARG_LONG_ONLY) + add_definitions(-DARG_LONG_ONLY) endif() file(GLOB EXAMPLES_SOURCES RELATIVE ${PROJECT_SOURCE_DIR}/examples *.c) if(UNIX) - set(ARGTABLE3_EXTRA_LIBS m) + set(ARGTABLE3_EXTRA_LIBS m) endif() foreach(examples_src ${EXAMPLES_SOURCES}) - string(REPLACE ".c" "" examplename ${examples_src}) - add_executable(${examplename} ${PROJECT_SOURCE_DIR}/examples/${examples_src}) - target_include_directories(${examplename} PRIVATE ${PROJECT_SOURCE_DIR}/src) - target_link_libraries(${examplename} argtable3 ${ARGTABLE3_EXTRA_LIBS}) + string(REPLACE ".c" "" examplename ${examples_src}) + add_executable( + ${examplename} + ${PROJECT_SOURCE_DIR}/examples/${examples_src} + ) + target_include_directories(${examplename} PRIVATE ${PROJECT_SOURCE_DIR}/src) + target_link_libraries(${examplename} argtable3 ${ARGTABLE3_EXTRA_LIBS}) endforeach() diff --git a/extern/argtable3-3.2.2/tests/CMakeLists.txt b/extern/argtable3-3.2.2/tests/CMakeLists.txt index 97c6b593..4bfaf53d 100644 --- a/extern/argtable3-3.2.2/tests/CMakeLists.txt +++ b/extern/argtable3-3.2.2/tests/CMakeLists.txt @@ -29,75 +29,81 @@ ################################################################################ if(ARGTABLE3_ENABLE_ARG_REX_DEBUG) - add_definitions(-DARG_REX_DEBUG) + add_definitions(-DARG_REX_DEBUG) endif() if(NOT ARGTABLE3_REPLACE_GETOPT) - add_definitions(-DARG_REPLACE_GETOPT=0) + add_definitions(-DARG_REPLACE_GETOPT=0) endif() if(ARGTABLE3_LONG_ONLY) - add_definitions(-DARG_LONG_ONLY) + add_definitions(-DARG_LONG_ONLY) endif() set(TEST_PUBLIC_SRC_FILES - testall.c - testarglit.c - testargstr.c - testargint.c - testargdate.c - testargdbl.c - testargfile.c - testargrex.c - testargdstr.c - testargcmd.c - CuTest.c + testall.c + testarglit.c + testargstr.c + testargint.c + testargdate.c + testargdbl.c + testargfile.c + testargrex.c + testargdstr.c + testargcmd.c + CuTest.c ) -set(TEST_SRC_FILES - ${TEST_PUBLIC_SRC_FILES} - testarghashtable.c -) +set(TEST_SRC_FILES ${TEST_PUBLIC_SRC_FILES} testarghashtable.c) if(UNIX) - set(ARGTABLE3_EXTRA_LIBS m) + set(ARGTABLE3_EXTRA_LIBS m) endif() if(BUILD_SHARED_LIBS) - add_executable(test_shared ${TEST_PUBLIC_SRC_FILES}) - target_compile_definitions(test_shared PRIVATE -DARGTABLE3_TEST_PUBLIC_ONLY) - target_include_directories(test_shared PRIVATE ${PROJECT_SOURCE_DIR}/src) - target_link_libraries(test_shared argtable3 ${ARGTABLE3_EXTRA_LIBS}) - add_custom_command(TARGET test_shared POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "$" - "$" - ) + add_executable(test_shared ${TEST_PUBLIC_SRC_FILES}) + target_compile_definitions(test_shared PRIVATE -DARGTABLE3_TEST_PUBLIC_ONLY) + target_include_directories(test_shared PRIVATE ${PROJECT_SOURCE_DIR}/src) + target_link_libraries(test_shared argtable3 ${ARGTABLE3_EXTRA_LIBS}) + add_custom_command( + TARGET test_shared + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy_if_different "$" + "$" + ) - add_test(NAME test_shared COMMAND "$") + add_test(NAME test_shared COMMAND "$") else() - add_executable(test_static ${TEST_SRC_FILES}) - target_include_directories(test_static PRIVATE ${PROJECT_SOURCE_DIR}/src) - target_link_libraries(test_static argtable3 ${ARGTABLE3_EXTRA_LIBS}) + add_executable(test_static ${TEST_SRC_FILES}) + target_include_directories(test_static PRIVATE ${PROJECT_SOURCE_DIR}/src) + target_link_libraries(test_static argtable3 ${ARGTABLE3_EXTRA_LIBS}) - add_test(NAME test_static COMMAND "$") + add_test(NAME test_static COMMAND "$") endif() add_executable(test_src ${TEST_SRC_FILES} ${ARGTABLE3_SRC_FILES}) target_include_directories(test_src PRIVATE ${PROJECT_SOURCE_DIR}/src) target_link_libraries(test_src ${ARGTABLE3_EXTRA_LIBS}) -add_custom_command(OUTPUT ${ARGTABLE3_AMALGAMATION_SRC_FILE} - COMMAND "${PROJECT_SOURCE_DIR}/tools/build" dist - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tools" +add_custom_command( + OUTPUT ${ARGTABLE3_AMALGAMATION_SRC_FILE} + COMMAND "${PROJECT_SOURCE_DIR}/tools/build" dist + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tools" ) -add_executable(test_amalgamation ${TEST_SRC_FILES} ${ARGTABLE3_AMALGAMATION_SRC_FILE}) +add_executable( + test_amalgamation + ${TEST_SRC_FILES} + ${ARGTABLE3_AMALGAMATION_SRC_FILE} +) target_include_directories(test_amalgamation PRIVATE ${PROJECT_SOURCE_DIR}/src) target_link_libraries(test_amalgamation ${ARGTABLE3_EXTRA_LIBS}) -add_custom_command(TARGET test_amalgamation PRE_BUILD - COMMAND "${PROJECT_SOURCE_DIR}/tools/build" dist - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tools" +add_custom_command( + TARGET test_amalgamation + PRE_BUILD + COMMAND "${PROJECT_SOURCE_DIR}/tools/build" dist + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/tools" ) add_test(NAME test_src COMMAND "$") diff --git a/parallel/modified_kahip/CMakeLists.txt b/parallel/modified_kahip/CMakeLists.txt index 97afdea9..e7762250 100644 --- a/parallel/modified_kahip/CMakeLists.txt +++ b/parallel/modified_kahip/CMakeLists.txt @@ -1,87 +1,119 @@ add_library(modified_kahip_headers INTERFACE) -target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib) -target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) -target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) -target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io) -target_include_directories(modified_kahip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) +target_include_directories( + modified_kahip_headers + BEFORE + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib +) +target_include_directories( + modified_kahip_headers + BEFORE + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools +) +target_include_directories( + modified_kahip_headers + BEFORE + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition +) +target_include_directories( + modified_kahip_headers + BEFORE + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io +) +target_include_directories( + modified_kahip_headers + BEFORE + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement +) set(LIBMODIFIED_KAFFPA_SOURCE_FILES - lib/data_structure/graph_hierarchy.cpp - lib/algorithms/strongly_connected_components.cpp - lib/algorithms/topological_sort.cpp - lib/io/graph_io.cpp - lib/tools/quality_metrics.cpp - lib/tools/graph_extractor.cpp - lib/tools/misc.cpp - lib/tools/partition_snapshooter.cpp - lib/partition/graph_partitioner.cpp - lib/partition/w_cycles/wcycle_partitioner.cpp - lib/partition/coarsening/coarsening.cpp - lib/partition/coarsening/contraction.cpp - lib/partition/coarsening/edge_rating/edge_ratings.cpp - lib/partition/coarsening/matching/matching.cpp - lib/partition/coarsening/matching/random_matching.cpp - lib/partition/coarsening/matching/gpa/path.cpp - lib/partition/coarsening/matching/gpa/gpa_matching.cpp - lib/partition/coarsening/matching/gpa/path_set.cpp - lib/partition/coarsening/clustering/node_ordering.cpp - lib/partition/coarsening/clustering/size_constraint_label_propagation.cpp - lib/partition/initial_partitioning/initial_partitioning.cpp - lib/partition/initial_partitioning/initial_partitioner.cpp - lib/partition/initial_partitioning/initial_partition_bipartition.cpp - lib/partition/initial_partitioning/initial_refinement/initial_refinement.cpp - lib/partition/initial_partitioning/bipartition.cpp - lib/partition/uncoarsening/uncoarsening.cpp - lib/partition/uncoarsening/separator/vertex_separator_algorithm.cpp - lib/partition/uncoarsening/separator/vertex_separator_flow_solver.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.cpp - lib/partition/uncoarsening/refinement/mixed_refinement.cpp - lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.cpp - lib/partition/uncoarsening/refinement/refinement.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.cpp - lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.cpp - lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.cpp - lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.cpp - lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.cpp - lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.cpp - lib/algorithms/cycle_search.cpp - lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp - lib/parallel_mh/galinier_combine/gal_combine.cpp - lib/parallel_mh/galinier_combine/construct_partition.cpp - lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp) + lib/data_structure/graph_hierarchy.cpp + lib/algorithms/strongly_connected_components.cpp + lib/algorithms/topological_sort.cpp + lib/io/graph_io.cpp + lib/tools/quality_metrics.cpp + lib/tools/graph_extractor.cpp + lib/tools/misc.cpp + lib/tools/partition_snapshooter.cpp + lib/partition/graph_partitioner.cpp + lib/partition/w_cycles/wcycle_partitioner.cpp + lib/partition/coarsening/coarsening.cpp + lib/partition/coarsening/contraction.cpp + lib/partition/coarsening/edge_rating/edge_ratings.cpp + lib/partition/coarsening/matching/matching.cpp + lib/partition/coarsening/matching/random_matching.cpp + lib/partition/coarsening/matching/gpa/path.cpp + lib/partition/coarsening/matching/gpa/gpa_matching.cpp + lib/partition/coarsening/matching/gpa/path_set.cpp + lib/partition/coarsening/clustering/node_ordering.cpp + lib/partition/coarsening/clustering/size_constraint_label_propagation.cpp + lib/partition/initial_partitioning/initial_partitioning.cpp + lib/partition/initial_partitioning/initial_partitioner.cpp + lib/partition/initial_partitioning/initial_partition_bipartition.cpp + lib/partition/initial_partitioning/initial_refinement/initial_refinement.cpp + lib/partition/initial_partitioning/bipartition.cpp + lib/partition/uncoarsening/uncoarsening.cpp + lib/partition/uncoarsening/separator/vertex_separator_algorithm.cpp + lib/partition/uncoarsening/separator/vertex_separator_flow_solver.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/greedy_neg_cycle.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/problem_factory.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph.cpp + lib/partition/uncoarsening/refinement/mixed_refinement.cpp + lib/partition/uncoarsening/refinement/label_propagation_refinement/label_propagation_refinement.cpp + lib/partition/uncoarsening/refinement/refinement.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/2way_fm_refinement/two_way_fm.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/two_way_flow_refinement.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/boundary_bfs.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/flow_solver.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/edge_cut_flow_solver.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/flow_solving_kernel/timer.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement/most_balanced_minimum_cuts/most_balanced_minimum_cuts.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_refinement.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/complete_boundary.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/partial_boundary.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/quotient_graph_scheduling.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/simple_quotient_graph_scheduler.cpp + lib/partition/uncoarsening/refinement/quotient_graph_refinement/quotient_graph_scheduling/active_block_quotient_graph_scheduler.cpp + lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement.cpp + lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_core.cpp + lib/partition/uncoarsening/refinement/kway_graph_refinement/kway_graph_refinement_commons.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/augmented_Qgraph_fabric.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/advanced_models.cpp + lib/partition/uncoarsening/refinement/kway_graph_refinement/multitry_kway_fm.cpp + lib/algorithms/cycle_search.cpp + lib/partition/uncoarsening/refinement/cycle_improvements/cycle_refinement.cpp + lib/parallel_mh/galinier_combine/gal_combine.cpp + lib/parallel_mh/galinier_combine/construct_partition.cpp + lib/partition/uncoarsening/refinement/tabu_search/tabu_search.cpp +) add_library(libmodified_kaffpa OBJECT ${LIBMODIFIED_KAFFPA_SOURCE_FILES}) target_link_libraries(libmodified_kaffpa PRIVATE kahip_options kahip_warnings) target_link_libraries(libmodified_kaffpa PRIVATE modified_kahip_headers) target_link_libraries(libmodified_kaffpa PRIVATE MPI::MPI_CXX) - set(LIBMODIFIED_KAFFPA_PARALLEL_SOURCE_FILES - lib/parallel_mh/parallel_mh_async.cpp - lib/parallel_mh/population.cpp - lib/parallel_mh/exchange/exchanger.cpp - lib/tools/graph_communication.cpp) -add_library(libmodified_kaffpa_async OBJECT ${LIBMODIFIED_KAFFPA_PARALLEL_SOURCE_FILES}) + lib/parallel_mh/parallel_mh_async.cpp + lib/parallel_mh/population.cpp + lib/parallel_mh/exchange/exchanger.cpp + lib/tools/graph_communication.cpp +) +add_library( + libmodified_kaffpa_async + OBJECT + ${LIBMODIFIED_KAFFPA_PARALLEL_SOURCE_FILES} +) target_link_libraries(libmodified_kaffpa_async PRIVATE modified_kahip_headers) target_link_libraries(libmodified_kaffpa_async PRIVATE MPI::MPI_CXX) add_library(libmodified_kahip_interface STATIC interface/kaHIP_interface.cpp) -target_link_libraries(libmodified_kahip_interface PRIVATE libmodified_kaffpa libmodified_kaffpa_async) +target_link_libraries( + libmodified_kahip_interface + PRIVATE libmodified_kaffpa libmodified_kaffpa_async +) target_link_libraries(libmodified_kahip_interface PRIVATE OpenMP::OpenMP_CXX) target_link_libraries(libmodified_kahip_interface PRIVATE MPI::MPI_CXX) -target_link_libraries(libmodified_kahip_interface PRIVATE modified_kahip_headers) +target_link_libraries( + libmodified_kahip_interface + PRIVATE modified_kahip_headers +) target_include_directories(libmodified_kahip_interface PUBLIC interface) diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index 74658f47..a6815bdb 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -1,40 +1,65 @@ if(NOT OPTIMIZED_OUTPUT) - add_definitions("-DNOOUTPUT") + add_definitions("-DNOOUTPUT") endif() if(DETERMINISTIC_PARHIP) - add_definitions("-DDETERMINISTIC_PARHIP") + add_definitions("-DDETERMINISTIC_PARHIP") endif() add_subdirectory(extern/cista) add_library(parhip_headers INTERFACE) -target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/app) -target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib) -target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools) -target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition) -target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io) -target_include_directories(parhip_headers BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement) - +target_include_directories( + parhip_headers + BEFORE + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/app +) +target_include_directories( + parhip_headers + BEFORE + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib +) +target_include_directories( + parhip_headers + BEFORE + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/tools +) +target_include_directories( + parhip_headers + BEFORE + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition +) +target_include_directories( + parhip_headers + BEFORE + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib/io +) +target_include_directories( + parhip_headers + BEFORE + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/lib/partition/uncoarsening/refinement/quotient_graph_refinement/flow_refinement +) set(LIBPARALLEL_SOURCE_FILES - lib/data_structure/parallel_graph_access.cpp - lib/data_structure/balance_management.cpp - lib/data_structure/balance_management_refinement.cpp - lib/data_structure/balance_management_coarsening.cpp - lib/parallel_label_compress/node_ordering.cpp - lib/parallel_contraction_projection/parallel_contraction.cpp - lib/parallel_contraction_projection/parallel_block_down_propagation.cpp - lib/parallel_contraction_projection/parallel_projection.cpp - lib/distributed_partitioning/distributed_partitioner.cpp - lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp - lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp - lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.cpp - lib/communication/mpi_tools.cpp - lib/communication/dummy_operations.cpp - lib/io/parallel_graph_io.cpp - lib/io/parallel_vector_io.cpp - lib/tools/distributed_quality_metrics.cpp) + lib/data_structure/parallel_graph_access.cpp + lib/data_structure/balance_management.cpp + lib/data_structure/balance_management_refinement.cpp + lib/data_structure/balance_management_coarsening.cpp + lib/parallel_label_compress/node_ordering.cpp + lib/parallel_contraction_projection/parallel_contraction.cpp + lib/parallel_contraction_projection/parallel_block_down_propagation.cpp + lib/parallel_contraction_projection/parallel_projection.cpp + lib/distributed_partitioning/distributed_partitioner.cpp + lib/distributed_partitioning/initial_partitioning/initial_partitioning.cpp + lib/distributed_partitioning/initial_partitioning/distributed_evolutionary_partitioning.cpp + lib/distributed_partitioning/initial_partitioning/random_initial_partitioning.cpp + lib/communication/mpi_tools.cpp + lib/communication/dummy_operations.cpp + lib/io/parallel_graph_io.cpp + lib/io/parallel_vector_io.cpp + lib/tools/distributed_quality_metrics.cpp +) add_library(parallel ${LIBPARALLEL_SOURCE_FILES}) target_link_libraries(parallel PRIVATE kahip_options kahip_warnings) target_link_libraries(parallel PUBLIC parhip_headers) @@ -44,37 +69,41 @@ target_link_libraries(parallel PUBLIC kahip_version) target_link_libraries(parallel PUBLIC MPI::MPI_CXX cista::cista) set(LIBGRAPH2BGF_SOURCE_FILES - lib/data_structure/parallel_graph_access.cpp - lib/io/parallel_graph_io.cpp - lib/data_structure/balance_management.cpp - lib/data_structure/balance_management_refinement.cpp - lib/data_structure/balance_management_coarsening.cpp) + lib/data_structure/parallel_graph_access.cpp + lib/io/parallel_graph_io.cpp + lib/data_structure/balance_management.cpp + lib/data_structure/balance_management_refinement.cpp + lib/data_structure/balance_management_coarsening.cpp +) add_library(libgraph2bgf OBJECT ${LIBGRAPH2BGF_SOURCE_FILES}) target_link_libraries(libgraph2bgf PUBLIC MPI::MPI_CXX) target_link_libraries(libgraph2bgf PUBLIC parhip_headers) - set(LIBEDGELIST_SOURCE_FILES - lib/data_structure/parallel_graph_access.cpp - lib/io/parallel_graph_io.cpp - lib/data_structure/balance_management.cpp - lib/data_structure/balance_management_refinement.cpp - lib/data_structure/balance_management_coarsening.cpp) + lib/data_structure/parallel_graph_access.cpp + lib/io/parallel_graph_io.cpp + lib/data_structure/balance_management.cpp + lib/data_structure/balance_management_refinement.cpp + lib/data_structure/balance_management_coarsening.cpp +) add_library(libedgelist STATIC ${LIBEDGELIST_SOURCE_FILES}) target_link_libraries(libedgelist PUBLIC MPI::MPI_CXX argtable3) target_link_libraries(libedgelist PUBLIC parhip_headers) - set(LIBDSPAC_SOURCE_FILES - lib/dspac/dspac.cpp - lib/dspac/edge_balanced_graph_io.cpp) + lib/dspac/dspac.cpp + lib/dspac/edge_balanced_graph_io.cpp +) add_library(libdspac STATIC ${LIBDSPAC_SOURCE_FILES}) target_link_libraries(libdspac PUBLIC MPI::MPI_CXX) -target_link_libraries(libdspac PUBLIC parhip_headers) - +target_link_libraries(libdspac PUBLIC parhip_headers) add_executable(parhip app/parhip.cpp) -target_compile_definitions(parhip PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") +target_compile_definitions( + parhip + PRIVATE + "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION" +) target_link_libraries(parhip PRIVATE kahip_options kahip_warnings) target_link_libraries(parhip PRIVATE parallel) target_link_libraries(parhip PRIVATE MPI::MPI_CXX) @@ -82,33 +111,55 @@ target_link_libraries(parhip PRIVATE argtable3) install(TARGETS parhip DESTINATION bin) add_executable(toolbox app/toolbox.cpp) -target_compile_definitions(toolbox PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DTOOLBOX") +target_compile_definitions( + toolbox + PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DTOOLBOX" +) target_link_libraries(toolbox PRIVATE parallel) target_link_libraries(toolbox PRIVATE MPI::MPI_CXX) install(TARGETS toolbox DESTINATION bin) add_executable(graph2binary app/graph2binary.cpp) -target_compile_definitions(graph2binary PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION -DGRAPH2DGF") +target_compile_definitions( + graph2binary + PRIVATE + "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION -DGRAPH2DGF" +) target_link_libraries(graph2binary PRIVATE libgraph2bgf) target_link_libraries(graph2binary PRIVATE kahip_version) target_link_libraries(graph2binary PRIVATE MPI::MPI_CXX) install(TARGETS graph2binary DESTINATION bin) add_executable(graph2binary_external app/graph2binary_external.cpp) -target_compile_definitions(graph2binary_external PRIVATE "-DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION -DGRAPH2DGF") +target_compile_definitions( + graph2binary_external + PRIVATE + "-DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION -DGRAPH2DGF" +) target_link_libraries(graph2binary_external PRIVATE libgraph2bgf) target_link_libraries(graph2binary_external PRIVATE kahip_version) install(TARGETS graph2binary_external DESTINATION bin) add_executable(readbgf app/readbgf.cpp) -target_compile_definitions(readbgf PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") +target_compile_definitions( + readbgf + PRIVATE + "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION" +) target_link_libraries(readbgf PRIVATE libgraph2bgf) target_link_libraries(readbgf PRIVATE kahip_version) install(TARGETS readbgf DESTINATION bin) add_executable(edge_list_to_metis_graph app/edge_list_to_metis_graph.cpp) -target_compile_definitions(edge_list_to_metis_graph PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DKRONECKER_GENERATOR_PROGRAM") -target_link_libraries(edge_list_to_metis_graph PRIVATE libmodified_kahip_interface) +target_compile_definitions( + edge_list_to_metis_graph + PRIVATE + "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DKRONECKER_GENERATOR_PROGRAM" +) +target_link_libraries( + edge_list_to_metis_graph + PRIVATE libmodified_kahip_interface +) target_link_libraries(edge_list_to_metis_graph PRIVATE libedgelist) target_link_libraries(edge_list_to_metis_graph PRIVATE kahip_version) install(TARGETS edge_list_to_metis_graph DESTINATION bin) @@ -119,33 +170,54 @@ install(TARGETS edge_list_to_metis_graph DESTINATION bin) #install(TARGETS friendster_list_to_metis_graph DESTINATION bin) add_executable(dspac app/dspac.cpp) -target_compile_definitions(dspac PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") +target_compile_definitions( + dspac + PRIVATE + "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION" +) target_link_libraries(dspac PRIVATE parallel) target_link_libraries(dspac PRIVATE libdspac) install(TARGETS dspac DESTINATION bin) add_library(parhip_interface SHARED interface/parhip_interface.cpp) -target_compile_definitions(parhip_interface PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") -target_include_directories(parhip_interface PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) +target_compile_definitions( + parhip_interface + PRIVATE + "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION" +) +target_include_directories( + parhip_interface + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface +) target_link_libraries(parhip_interface PRIVATE parallel) -set_target_properties(parhip_interface PROPERTIES PUBLIC_HEADER interface/parhip_interface.h) -install(TARGETS parhip_interface - LIBRARY DESTINATION lib - PUBLIC_HEADER DESTINATION include - ) +set_target_properties( + parhip_interface + PROPERTIES PUBLIC_HEADER interface/parhip_interface.h +) +install( + TARGETS parhip_interface + LIBRARY DESTINATION lib + PUBLIC_HEADER DESTINATION include +) add_library(parhip_interface_static interface/parhip_interface.cpp) -target_compile_definitions(parhip_interface_static PRIVATE "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION") -target_include_directories(parhip_interface_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface) +target_compile_definitions( + parhip_interface_static + PRIVATE + "-DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY -DPARALLEL_LABEL_COMPRESSION" +) +target_include_directories( + parhip_interface_static + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface +) target_link_libraries(parhip_interface_static PRIVATE parallel) install(TARGETS parhip_interface_static DESTINATION lib) - # Temporary Testing set(ENABLE_TESTING ON) -if (ENABLE_TESTING) +if(ENABLE_TESTING) find_package(Catch2 REQUIRED CONFIG) enable_testing() message("Building Tests") add_subdirectory(tests) -endif () \ No newline at end of file +endif() diff --git a/parallel/parallel_src/extern/cista/CMakeLists.txt b/parallel/parallel_src/extern/cista/CMakeLists.txt index a2af5217..b1391b36 100755 --- a/parallel/parallel_src/extern/cista/CMakeLists.txt +++ b/parallel/parallel_src/extern/cista/CMakeLists.txt @@ -1,137 +1,146 @@ cmake_minimum_required(VERSION 3.8) -if (NOT DEFINED PROJECT_NAME AND CISTA_HASH STREQUAL "FNV1A") - set(CISTA_INSTALL ON) +if(NOT DEFINED PROJECT_NAME AND CISTA_HASH STREQUAL "FNV1A") + set(CISTA_INSTALL ON) endif() project(cista LANGUAGES CXX VERSION 0.7) include(GNUInstallDirs) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR - "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") - set(cista-compile-flags - -Wno-unknown-warning-option - -Wno-global-constructors - -Wno-exit-time-destructors - -fno-strict-aliasing - -Weverything - -Wno-c++98-compat - -Wno-c++98-compat-pedantic - -Wno-newline-eof - -Wno-missing-prototypes - -Wno-padded - -Wno-double-promotion - -Wno-undef - -Wno-undefined-reinterpret-cast - -Wno-float-conversion - -Wno-gnu-zero-variadic-macro-arguments - -Wno-unknown-pragmas - -Wno-documentation-unknown-command - -Wno-reserved-identifier - -Wno-weak-vtables - -Wno-unneeded-member-function - -Wno-unused-member-function - -Wno-unsafe-buffer-usage - -Wno-deprecated-declarations - -Wno-ctad-maybe-unsupported - -Wno-self-assign-overloaded - -Werror - ) +if( + "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" + OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang" +) + set(cista-compile-flags + -Wno-unknown-warning-option + -Wno-global-constructors + -Wno-exit-time-destructors + -fno-strict-aliasing + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-newline-eof + -Wno-missing-prototypes + -Wno-padded + -Wno-double-promotion + -Wno-undef + -Wno-undefined-reinterpret-cast + -Wno-float-conversion + -Wno-gnu-zero-variadic-macro-arguments + -Wno-unknown-pragmas + -Wno-documentation-unknown-command + -Wno-reserved-identifier + -Wno-weak-vtables + -Wno-unneeded-member-function + -Wno-unused-member-function + -Wno-unsafe-buffer-usage + -Wno-deprecated-declarations + -Wno-ctad-maybe-unsupported + -Wno-self-assign-overloaded + -Werror + ) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(cista-compile-flags -Wall -Wextra) + set(cista-compile-flags -Wall -Wextra) endif() option(CISTA_ZERO_OUT "zero out fresh memory for valgrind" OFF) option(CISTA_COVERAGE "generate coverage report" OFF) -option(CISTA_GENERATE_TO_TUPLE "generate include/cista/reflection/to_tuple.h" OFF) +option( + CISTA_GENERATE_TO_TUPLE + "generate include/cista/reflection/to_tuple.h" + OFF +) option(CISTA_USE_MIMALLOC "compile with mimalloc support" OFF) set(CISTA_HASH "FNV1A" CACHE STRING "Options: FNV1A XXH3 WYHASH WYHASH_FASTEST") add_library(cista INTERFACE) -if (CISTA_HASH STREQUAL "XXH3") - add_subdirectory(tools/xxh3) - target_link_libraries(cista INTERFACE xxh3) +if(CISTA_HASH STREQUAL "XXH3") + add_subdirectory(tools/xxh3) + target_link_libraries(cista INTERFACE xxh3) elseif(CISTA_HASH STREQUAL "WYHASH" OR CISTA_HASH STREQUAL "WYHASH_FASTEST") - add_subdirectory(tools/wyhash) - target_link_libraries(cista INTERFACE wyhash) + add_subdirectory(tools/wyhash) + target_link_libraries(cista INTERFACE wyhash) endif() target_compile_definitions(cista INTERFACE CISTA_${CISTA_HASH}=1) -if (CISTA_ZERO_OUT) - target_compile_definitions(cista INTERFACE CISTA_ZERO_OUT=1) +if(CISTA_ZERO_OUT) + target_compile_definitions(cista INTERFACE CISTA_ZERO_OUT=1) endif() -if (CISTA_USE_MIMALLOC) - target_compile_definitions(cista INTERFACE CISTA_USE_MIMALLOC=1) +if(CISTA_USE_MIMALLOC) + target_compile_definitions(cista INTERFACE CISTA_USE_MIMALLOC=1) endif() -target_include_directories(cista SYSTEM INTERFACE - $ - $) +target_include_directories( + cista + SYSTEM + INTERFACE + $ + $ +) target_compile_features(cista INTERFACE cxx_std_17) -if (${CISTA_GENERATE_TO_TUPLE}) - add_subdirectory(tools/to_tuple_generator EXCLUDE_FROM_ALL) - add_custom_target(generate_to_tuple - COMMAND to_tuple_generator - 64 # max number of supported member fields - > ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/to_tuple.h - ) - add_dependencies(cista generate_to_tuple) +if(${CISTA_GENERATE_TO_TUPLE}) + add_subdirectory(tools/to_tuple_generator EXCLUDE_FROM_ALL) + add_custom_target( + generate_to_tuple + COMMAND + to_tuple_generator 64 # max number of supported member fields + > ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/to_tuple.h + ) + add_dependencies(cista generate_to_tuple) endif() file(GLOB_RECURSE cista-include-files include/*.h*) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/cista.h - COMMAND uniter - ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/serialization.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/comparable.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/printable.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/member_index.h - > ${CMAKE_CURRENT_BINARY_DIR}/cista.h - DEPENDS ${cista-include-files} + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/cista.h + COMMAND + uniter ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/serialization.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/comparable.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/printable.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/cista/reflection/member_index.h > + ${CMAKE_CURRENT_BINARY_DIR}/cista.h + DEPENDS ${cista-include-files} ) add_library(cista::cista ALIAS cista) # Export targets when not used via `add_subdirectory` -if (CISTA_INSTALL) - include(CMakePackageConfigHelpers) - set(CISTA_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/cista") - - configure_package_config_file( - ${CMAKE_CURRENT_LIST_DIR}/CMake/cistaConfig.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/cistaConfig.cmake - INSTALL_DESTINATION ${CISTA_CMAKE_CONFIG_DESTINATION} - ) - - install( - TARGETS cista - EXPORT cistaTargets - DESTINATION ${CMAKE_INSTALL_LIBDIR} - ) - - install( - EXPORT cistaTargets - NAMESPACE cista:: - DESTINATION ${CISTA_CMAKE_CONFIG_DESTINATION} - ) - - install( - DIRECTORY "include/" - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - ) - - write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/cistaConfigVersion.cmake" - COMPATIBILITY SameMajorVersion - ) - - install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/cistaConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/cistaConfigVersion.cmake" - DESTINATION ${CISTA_CMAKE_CONFIG_DESTINATION} - ) +if(CISTA_INSTALL) + include(CMakePackageConfigHelpers) + set(CISTA_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/cista") + + configure_package_config_file( + ${CMAKE_CURRENT_LIST_DIR}/CMake/cistaConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/cistaConfig.cmake + INSTALL_DESTINATION ${CISTA_CMAKE_CONFIG_DESTINATION} + ) + + install( + TARGETS cista + EXPORT cistaTargets + DESTINATION + ${CMAKE_INSTALL_LIBDIR} + ) + + install( + EXPORT cistaTargets + NAMESPACE cista:: + DESTINATION ${CISTA_CMAKE_CONFIG_DESTINATION} + ) + + install(DIRECTORY "include/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/cistaConfigVersion.cmake" + COMPATIBILITY SameMajorVersion + ) + + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/cistaConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cistaConfigVersion.cmake" + DESTINATION ${CISTA_CMAKE_CONFIG_DESTINATION} + ) endif() diff --git a/parallel/parallel_src/extern/cista/tools/doctest/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/doctest/CMakeLists.txt index 0bc78c74..fb4c6652 100644 --- a/parallel/parallel_src/extern/cista/tools/doctest/CMakeLists.txt +++ b/parallel/parallel_src/extern/cista/tools/doctest/CMakeLists.txt @@ -1,5 +1,9 @@ project(doctest) add_library(cista-doctest STATIC doctest.cc) -target_include_directories(cista-doctest SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) -target_compile_features(cista-doctest PUBLIC cxx_std_11) \ No newline at end of file +target_include_directories( + cista-doctest + SYSTEM + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} +) +target_compile_features(cista-doctest PUBLIC cxx_std_11) diff --git a/parallel/parallel_src/extern/cista/tools/to_tuple_generator/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/to_tuple_generator/CMakeLists.txt index e784232a..3ab222df 100644 --- a/parallel/parallel_src/extern/cista/tools/to_tuple_generator/CMakeLists.txt +++ b/parallel/parallel_src/extern/cista/tools/to_tuple_generator/CMakeLists.txt @@ -1,3 +1,3 @@ project(to_tuple_generator) -add_executable(to_tuple_generator to_tuple_generator.cc) \ No newline at end of file +add_executable(to_tuple_generator to_tuple_generator.cc) diff --git a/parallel/parallel_src/extern/cista/tools/uniter/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/uniter/CMakeLists.txt index d740e5b7..8195ea79 100644 --- a/parallel/parallel_src/extern/cista/tools/uniter/CMakeLists.txt +++ b/parallel/parallel_src/extern/cista/tools/uniter/CMakeLists.txt @@ -1,4 +1,4 @@ project(uniter) add_executable(uniter uniter.cc) -target_compile_features(uniter PRIVATE cxx_std_17) \ No newline at end of file +target_compile_features(uniter PRIVATE cxx_std_17) diff --git a/parallel/parallel_src/extern/cista/tools/wyhash/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/wyhash/CMakeLists.txt index 6e06e438..840540e0 100755 --- a/parallel/parallel_src/extern/cista/tools/wyhash/CMakeLists.txt +++ b/parallel/parallel_src/extern/cista/tools/wyhash/CMakeLists.txt @@ -1,3 +1,3 @@ project(wyhash) add_library(wyhash INTERFACE) -target_include_directories(wyhash SYSTEM INTERFACE .) \ No newline at end of file +target_include_directories(wyhash SYSTEM INTERFACE .) diff --git a/parallel/parallel_src/extern/cista/tools/xxh3/CMakeLists.txt b/parallel/parallel_src/extern/cista/tools/xxh3/CMakeLists.txt index 78a57a75..4c3894ce 100755 --- a/parallel/parallel_src/extern/cista/tools/xxh3/CMakeLists.txt +++ b/parallel/parallel_src/extern/cista/tools/xxh3/CMakeLists.txt @@ -1,3 +1,3 @@ project(xxh3) add_library(xxh3 INTERFACE) -target_include_directories(xxh3 SYSTEM INTERFACE .) \ No newline at end of file +target_include_directories(xxh3 SYSTEM INTERFACE .) diff --git a/parallel/parallel_src/tests/CMakeLists.txt b/parallel/parallel_src/tests/CMakeLists.txt index 8502419d..bc8dc254 100644 --- a/parallel/parallel_src/tests/CMakeLists.txt +++ b/parallel/parallel_src/tests/CMakeLists.txt @@ -1,7 +1,7 @@ # Automatically enable catch2 to generate ctest targets find_package(fmt REQUIRED CONFIG) include(ProcessorCount) -ProcessorCount(N) +processorcount(N) set(mpi_runner $,"","mpirun;-np;${N}">) @@ -15,22 +15,37 @@ target_link_libraries(catch_mpi_runner PUBLIC MPI::MPI_CXX) target_link_libraries(catch_mpi_runner PUBLIC Catch2::Catch2) # Parallel contraction -add_executable(parallel_contraction_test parallel_contraction/parallel_contraction_test.cpp) -target_link_libraries(parallel_contraction_test PRIVATE Catch2::Catch2WithMain parallel) -target_link_libraries(parallel_contraction_test PRIVATE libmodified_kahip_interface fmt::fmt) +add_executable( + parallel_contraction_test + parallel_contraction/parallel_contraction_test.cpp +) +target_link_libraries( + parallel_contraction_test + PRIVATE Catch2::Catch2WithMain parallel +) +target_link_libraries( + parallel_contraction_test + PRIVATE libmodified_kahip_interface fmt::fmt +) catch_discover_tests( parallel_contraction_test TEST_PREFIX "unit-" OUTPUT_DIR . - OUTPUT_PREFIX "unit-") - + OUTPUT_PREFIX "unit-" +) # Parallel contraction -add_executable(parallel_contraction_mpi_test parallel_contraction/parallel_contraction_mpi_test.cpp) +add_executable( + parallel_contraction_mpi_test + parallel_contraction/parallel_contraction_mpi_test.cpp +) target_link_libraries(parallel_contraction_mpi_test PRIVATE catch_mpi_runner) target_link_libraries(parallel_contraction_mpi_test PRIVATE parallel) -target_link_libraries(parallel_contraction_mpi_test PRIVATE libmodified_kahip_interface fmt::fmt) +target_link_libraries( + parallel_contraction_mpi_test + PRIVATE libmodified_kahip_interface fmt::fmt +) catch_discover_tests( parallel_contraction_mpi_test @@ -39,7 +54,14 @@ catch_discover_tests( REPORTER xml OUTPUT_DIR . OUTPUT_PREFIX "unit-" - OUTPUT_SUFFIX .xml) + OUTPUT_SUFFIX .xml +) -target_link_libraries(parallel_contraction_test PRIVATE kahip_options kahip_warnings) -target_link_libraries(parallel_contraction_mpi_test PRIVATE kahip_options kahip_warnings) \ No newline at end of file +target_link_libraries( + parallel_contraction_test + PRIVATE kahip_options kahip_warnings +) +target_link_libraries( + parallel_contraction_mpi_test + PRIVATE kahip_options kahip_warnings +) From 95b4c79810e143d6045fd78f08e102a678f41c50 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 15 Oct 2024 15:25:09 +0100 Subject: [PATCH 56/64] Added options throughout --- cmake/KaHIPSettings.cmake | 46 +++++++++---------- parallel/modified_kahip/CMakeLists.txt | 2 + parallel/parallel_src/CMakeLists.txt | 11 +++++ parallel/parallel_src/lib/tools/timer.h | 28 ++++------- .../parallel_contraction_test.cpp | 1 + 5 files changed, 47 insertions(+), 41 deletions(-) diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index c5d00c0a..90a63ddb 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -9,9 +9,9 @@ include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) kahip_supports_sanitizers() -option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) -option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" ON) -option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" ON) +option(kahip_ENABLE_IPO "Enable IPO/LTO" OFF) +option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" OFF) +option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" OFF) option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) option( @@ -46,6 +46,26 @@ add_library(kahip_options INTERFACE) include(${CMAKE_SOURCE_DIR}/cmake/StandardProjectSettings.cmake) +# tweak compiler flags +target_compile_features(kahip_options INTERFACE cxx_std_20) +message(VERBOSE "Checking compiler feature support") +check_cxx_compiler_flag(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) +target_compile_options( + kahip_options + INTERFACE $<$:-funroll-loops> +) +check_cxx_compiler_flag(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) +target_compile_options( + kahip_options + INTERFACE $<$:-funroll-loops> +) +check_cxx_compiler_flag(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) +target_compile_options( + kahip_options + INTERFACE + $<$,$>,$,$>>:-march=native> +) + if(kahip_ENABLE_USER_LINKER) include(${CMAKE_SOURCE_DIR}/cmake/Linker.cmake) kahip_configure_linker(kahip_options) @@ -85,23 +105,3 @@ endif() if(kahip_ENABLE_IWYU) kahip_enable_include_what_you_use() endif() - -# tweak compiler flags -target_compile_features(kahip_options INTERFACE cxx_std_20) -message(VERBOSE "Checking compiler feature support") -check_cxx_compiler_flag(-funroll-loops COMPILER_SUPPORTS_FUNROLL_LOOPS) -target_compile_options( - kahip_options - INTERFACE $<$:-funroll-loops> -) -check_cxx_compiler_flag(-fno-stack-limit COMPILER_SUPPORTS_FNOSTACKLIMITS) -target_compile_options( - kahip_options - INTERFACE $<$:-funroll-loops> -) -check_cxx_compiler_flag(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) -target_compile_options( - kahip_options - INTERFACE - $<$,$>,$,$>>:-march=native> -) diff --git a/parallel/modified_kahip/CMakeLists.txt b/parallel/modified_kahip/CMakeLists.txt index e7762250..42af2131 100644 --- a/parallel/modified_kahip/CMakeLists.txt +++ b/parallel/modified_kahip/CMakeLists.txt @@ -104,6 +104,7 @@ add_library( ) target_link_libraries(libmodified_kaffpa_async PRIVATE modified_kahip_headers) target_link_libraries(libmodified_kaffpa_async PRIVATE MPI::MPI_CXX) +target_link_libraries(libmodified_kaffpa_async PRIVATE kahip_options kahip_warnings) add_library(libmodified_kahip_interface STATIC interface/kaHIP_interface.cpp) target_link_libraries( @@ -116,4 +117,5 @@ target_link_libraries( libmodified_kahip_interface PRIVATE modified_kahip_headers ) +target_link_libraries(libmodified_kahip_interface PRIVATE kahip_options kahip_warnings) target_include_directories(libmodified_kahip_interface PUBLIC interface) diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index a6815bdb..b4e973dc 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -78,6 +78,7 @@ set(LIBGRAPH2BGF_SOURCE_FILES add_library(libgraph2bgf OBJECT ${LIBGRAPH2BGF_SOURCE_FILES}) target_link_libraries(libgraph2bgf PUBLIC MPI::MPI_CXX) target_link_libraries(libgraph2bgf PUBLIC parhip_headers) +target_link_libraries(libgraph2bgf PRIVATE kahip_options kahip_warnings) set(LIBEDGELIST_SOURCE_FILES lib/data_structure/parallel_graph_access.cpp @@ -89,6 +90,7 @@ set(LIBEDGELIST_SOURCE_FILES add_library(libedgelist STATIC ${LIBEDGELIST_SOURCE_FILES}) target_link_libraries(libedgelist PUBLIC MPI::MPI_CXX argtable3) target_link_libraries(libedgelist PUBLIC parhip_headers) +target_link_libraries(libedgelist PRIVATE kahip_options kahip_warnings) set(LIBDSPAC_SOURCE_FILES lib/dspac/dspac.cpp @@ -97,6 +99,7 @@ set(LIBDSPAC_SOURCE_FILES add_library(libdspac STATIC ${LIBDSPAC_SOURCE_FILES}) target_link_libraries(libdspac PUBLIC MPI::MPI_CXX) target_link_libraries(libdspac PUBLIC parhip_headers) +target_link_libraries(libdspac PRIVATE kahip_options kahip_warnings) add_executable(parhip app/parhip.cpp) target_compile_definitions( @@ -117,6 +120,7 @@ target_compile_definitions( ) target_link_libraries(toolbox PRIVATE parallel) target_link_libraries(toolbox PRIVATE MPI::MPI_CXX) +target_link_libraries(toolbox PRIVATE kahip_options kahip_warnings) install(TARGETS toolbox DESTINATION bin) add_executable(graph2binary app/graph2binary.cpp) @@ -128,6 +132,7 @@ target_compile_definitions( target_link_libraries(graph2binary PRIVATE libgraph2bgf) target_link_libraries(graph2binary PRIVATE kahip_version) target_link_libraries(graph2binary PRIVATE MPI::MPI_CXX) +target_link_libraries(graph2binary PRIVATE kahip_options kahip_warnings) install(TARGETS graph2binary DESTINATION bin) add_executable(graph2binary_external app/graph2binary_external.cpp) @@ -138,6 +143,7 @@ target_compile_definitions( ) target_link_libraries(graph2binary_external PRIVATE libgraph2bgf) target_link_libraries(graph2binary_external PRIVATE kahip_version) +target_link_libraries(graph2binary_external PRIVATE kahip_options kahip_warnings) install(TARGETS graph2binary_external DESTINATION bin) add_executable(readbgf app/readbgf.cpp) @@ -148,6 +154,7 @@ target_compile_definitions( ) target_link_libraries(readbgf PRIVATE libgraph2bgf) target_link_libraries(readbgf PRIVATE kahip_version) +target_link_libraries(readbgf PRIVATE kahip_options kahip_warnings) install(TARGETS readbgf DESTINATION bin) add_executable(edge_list_to_metis_graph app/edge_list_to_metis_graph.cpp) @@ -162,6 +169,7 @@ target_link_libraries( ) target_link_libraries(edge_list_to_metis_graph PRIVATE libedgelist) target_link_libraries(edge_list_to_metis_graph PRIVATE kahip_version) +target_link_libraries(edge_list_to_metis_graph PRIVATE kahip_options kahip_warnings) install(TARGETS edge_list_to_metis_graph DESTINATION bin) #add_executable(friendster_list_to_metis_graph app/friendster_list_to_metis_graph.cpp $) @@ -177,6 +185,7 @@ target_compile_definitions( ) target_link_libraries(dspac PRIVATE parallel) target_link_libraries(dspac PRIVATE libdspac) +target_link_libraries(dspac PRIVATE kahip_options kahip_warnings) install(TARGETS dspac DESTINATION bin) add_library(parhip_interface SHARED interface/parhip_interface.cpp) @@ -190,6 +199,7 @@ target_include_directories( PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface ) target_link_libraries(parhip_interface PRIVATE parallel) +target_link_libraries(parhip_interface PRIVATE kahip_options kahip_warnings) set_target_properties( parhip_interface PROPERTIES PUBLIC_HEADER interface/parhip_interface.h @@ -211,6 +221,7 @@ target_include_directories( PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interface ) target_link_libraries(parhip_interface_static PRIVATE parallel) +target_link_libraries(parhip_interface_static PRIVATE kahip_options kahip_warnings) install(TARGETS parhip_interface_static DESTINATION lib) # Temporary Testing diff --git a/parallel/parallel_src/lib/tools/timer.h b/parallel/parallel_src/lib/tools/timer.h index 1826afa4..2c996335 100644 --- a/parallel/parallel_src/lib/tools/timer.h +++ b/parallel/parallel_src/lib/tools/timer.h @@ -8,34 +8,26 @@ #ifndef TIMER_9KPDEP #define TIMER_9KPDEP -#include -#include -#include +#include + namespace parhip { + class timer { public: - timer() { - m_start = timestamp(); - } + timer() : m_start{clock::now()} {} void restart() { - m_start = timestamp(); + m_start = clock::now(); } - double elapsed() { - return timestamp()-m_start; + [[nodiscard]] double elapsed() const noexcept { + return std::chrono::duration(clock::now() - m_start).count(); } private: - - /** Returns a timestamp ('now') in seconds (incl. a fractional part). */ - inline double timestamp() { - struct timeval tp; - gettimeofday(&tp, NULL); - return double(tp.tv_sec) + tp.tv_usec / 1000000.; - } - - double m_start; + using clock = std::chrono::steady_clock; + std::chrono::time_point m_start; }; + } #endif /* end of include guard: TIMER_9KPDEP */ diff --git a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp index 06d62a33..5e91d60e 100644 --- a/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp +++ b/parallel/parallel_src/tests/parallel_contraction/parallel_contraction_test.cpp @@ -11,6 +11,7 @@ #include #include "parallel_contraction_projection/parallel_contraction.h" +#include "tools/timer.h" #include "communication/mpi_tools.h" using namespace parhip; From aac365a9c458c147da62079361e437c6b2754971 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 15 Oct 2024 17:16:15 +0100 Subject: [PATCH 57/64] Error reverted --- .../parallel_contraction.cpp | 333 +++++++++++------- 1 file changed, 206 insertions(+), 127 deletions(-) diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index b9bf46fd..1ca526e9 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -265,146 +265,225 @@ void parallel_contraction::build_quotient_graph_locally( parallel_graph_access & } endfor } -void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( - MPI_Comm communicator, - hashed_graph& hG, - std::unordered_map& node_weights, - NodeID number_of_cnodes, - parallel_graph_access& Q) { - PEID rank = 0; - PEID size = 0; - MPI_Comm_rank(communicator, &rank); - MPI_Comm_size(communicator, &size); - NodeID const divisor = ceil(number_of_cnodes / static_cast(size)); - std::vector> messages(size); - m_messages.clear(); - m_messages.resize(size); +void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( MPI_Comm communicator, hashed_graph & hG, + std::unordered_map< NodeID, NodeWeight > & node_weights, + NodeID number_of_cnodes, + parallel_graph_access & Q ) { + PEID rank, size; + MPI_Comm_rank( communicator, &rank); + MPI_Comm_size( communicator, &size); - // build messages - hashed_graph::iterator it; - for (it = hG.begin(); it != hG.end(); it++) { - data_hashed_edge& e = it->second; - hashed_edge he = it->first; - PEID peID = he.source / divisor; - messages[peID].emplace_back(he.source, he.target, e.weight); - peID = he.target / divisor; - messages[peID].emplace_back(he.target, he.source, e.weight); - } + NodeID divisor = ceil( number_of_cnodes/(double)size); - // build the local part of the graph - // - auto const local_msg_byPE = mpi::all_to_all(messages, communicator); + //std::vector< std::vector< NodeID > > messages; + m_messages.resize(size); - hashed_graph local_graph; - for (PEID peID = 0; peID < size; peID++) { - if (!local_msg_byPE[peID].empty()) { - for (auto packed_edge : local_msg_byPE[peID]) { - hashed_edge he{}; - he.k = number_of_cnodes; - he.source = packed_edge.source; - he.target = packed_edge.target; - - local_graph[he].weight += packed_edge.weight; - } - } - } + //build messages + hashed_graph::iterator it; + for( it = hG.begin(); it != hG.end(); it++) { + data_hashed_edge & e = it->second; + hashed_edge he = it->first; - ULONG from = rank * ceil(number_of_cnodes / (double)size); - ULONG to = (rank + 1) * ceil(number_of_cnodes / (double)size) - 1; - // handle the case where we dont have local edges - from = std::min(from, number_of_cnodes); - to = std::min(to, number_of_cnodes - 1); - ULONG local_num_cnodes = to - from + 1; - - std::vector>> sorted_graph; - sorted_graph.resize(local_num_cnodes); - - EdgeID edge_counter = 0; - for (it = local_graph.begin(); it != local_graph.end(); it++) { - data_hashed_edge& e = it->second; - hashed_edge he = it->first; - - if (from <= he.target && he.target <= to) { - std::pair edge; - edge.first = he.target; - edge.second = e.weight / 4; - - std::pair e_bar; - e_bar.first = he.source; - e_bar.second = e.weight / 4; - - sorted_graph[he.target - from].push_back(e_bar); - sorted_graph[he.source - from].push_back(edge); - edge_counter += 2; - } else { - std::pair edge; - edge.first = he.target; - edge.second = e.weight / 2; - sorted_graph[he.source - from].push_back(edge); - edge_counter++; - } - } + PEID peID = he.source / divisor; + m_messages[ peID ].push_back( he.source ); + m_messages[ peID ].push_back( he.target ); + m_messages[ peID ].push_back( e.weight ); - ULONG global_edges = 0; - MPI_Allreduce(&edge_counter, &global_edges, 1, MPI_UNSIGNED_LONG_LONG, - MPI_SUM, communicator); + peID = he.target / divisor; + m_messages[ peID ].push_back( he.target ); + m_messages[ peID ].push_back( he.source ); + m_messages[ peID ].push_back( e.weight ); + } - Q.start_construction(local_num_cnodes, edge_counter, number_of_cnodes, - global_edges); - Q.set_range(from, to); + // now flood the network + for( PEID peID = 0; peID < size; peID++) { + if( peID != rank ) { + if( m_messages[peID].size() == 0 ){ + m_messages[peID].push_back(std::numeric_limits::max()); + } + + MPI_Request rq; + MPI_Isend( &m_messages[peID][0], + m_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, + peID, peID+7*size, communicator, &rq); + } + } - std::vector vertex_dist(size + 1, 0); - for (PEID peID = 0; peID <= size; peID++) { - vertex_dist[peID] = std::min( - number_of_cnodes, - (NodeID)(peID * - ceil(number_of_cnodes / (double)size))); // from positions - } - // vertex_dist[size] = std::min(to, number_of_cnodes - 1); - Q.set_range_array(vertex_dist); - - for (NodeID i = 0; i < local_num_cnodes; ++i) { - NodeID node = Q.new_node(); - NodeID globalID = from + node; - Q.setNodeWeight(node, 0); - Q.setNodeLabel(node, globalID); - - for (EdgeID e = 0; e < sorted_graph[node].size(); e++) { - NodeID target = sorted_graph[node][e].first; - EdgeID e_bar = Q.new_edge(node, target); - Q.setEdgeWeight(e_bar, sorted_graph[node][e].second); - } - } + // build the local part of the graph + // + std::vector< std::vector< NodeID > > local_msg_byPE; + local_msg_byPE.resize(size); - Q.finish_construction(); - for (PEID peID = 0; peID < size; peID++) { - m_messages[peID].clear(); - } - // now distribute the node weights - // pack messages - std::vector> weight_messages( - size); - std::unordered_map::iterator wit; - for (wit = node_weights.begin(); wit != node_weights.end(); wit++) { - NodeID node = wit->first; - NodeWeight weight = wit->second; - PEID peID = node / divisor; - weight_messages[peID].emplace_back(node, weight); - } + if( m_messages[ rank ].size() != 0 ) { + local_msg_byPE[rank] = m_messages[rank]; + } - auto const node_weights_byPE = mpi::all_to_all(weight_messages, communicator); + PEID counter = 0; + while( counter < size - 1) { + // wait for incomming message of an adjacent processor + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, rank+7*size, communicator, &st); - for (auto& message_byPE : node_weights_byPE) { - if (message_byPE.empty()) { - for (auto& [globalID, weight] : message_byPE) { - NodeID node = globalID - from; - Q.setNodeWeight(node, Q.getNodeWeight(node) + weight); - } - } - } + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector incmessage; incmessage.resize(message_length); + + MPI_Status rst; + MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+7*size, communicator, &rst); + counter++; + + // now integrate the changes + if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do + + + PEID peID = rst.MPI_SOURCE; + local_msg_byPE[peID] = incmessage; + } + + hashed_graph local_graph; + for( PEID peID = 0; peID < size; peID++) { + if(local_msg_byPE[peID].size() > 0) { + for( ULONG i = 0; i < local_msg_byPE[peID].size()-2; i+=3) { + hashed_edge he; + he.k = number_of_cnodes; + he.source = local_msg_byPE[peID][i]; + he.target = local_msg_byPE[peID][i+1]; + + local_graph[he].weight += local_msg_byPE[peID][i+2]; + }} + } + + + ULONG from = rank * ceil(number_of_cnodes / (double)size); + ULONG to = (rank+1) * ceil(number_of_cnodes / (double)size) - 1; + // handle the case where we dont have local edges + from = std::min(from, number_of_cnodes); + to = std::min(to, number_of_cnodes - 1); + ULONG local_num_cnodes = to - from + 1; + + std::vector < std::vector< std::pair > > sorted_graph; + sorted_graph.resize( local_num_cnodes ); + + EdgeID edge_counter = 0; + for( it = local_graph.begin(); it != local_graph.end(); it++) { + data_hashed_edge & e = it->second; + hashed_edge he = it->first; + + if( from <= he.target && he.target <= to) { + std::pair< NodeID, NodeWeight > edge; + edge.first = he.target; + edge.second = e.weight/4; + + std::pair< NodeID, NodeWeight > e_bar; + e_bar.first = he.source; + e_bar.second = e.weight/4; + + sorted_graph[ he.target - from ].push_back( e_bar); + sorted_graph[ he.source - from ].push_back( edge ); + edge_counter+=2; + } else { + std::pair< NodeID, NodeWeight > edge; + edge.first = he.target; + edge.second = e.weight/2; + sorted_graph[ he.source - from ].push_back( edge ); + edge_counter++; + } + } + + ULONG global_edges = 0; + MPI_Allreduce(&edge_counter, &global_edges, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, communicator); + + Q.start_construction(local_num_cnodes, edge_counter, number_of_cnodes, global_edges); + Q.set_range(from, to); + + std::vector< NodeID > vertex_dist( size+1, 0 ); + for( PEID peID = 0; peID <= size; peID++) { + vertex_dist[peID] = std::min(number_of_cnodes, (NodeID) (peID * ceil(number_of_cnodes / (double)size))); // from positions + } + //vertex_dist[size] = std::min(to, number_of_cnodes - 1); + Q.set_range_array(vertex_dist); + + for (NodeID i = 0; i < local_num_cnodes; ++i) { + NodeID node = Q.new_node(); + NodeID globalID = from+node; + Q.setNodeWeight(node, 0); + Q.setNodeLabel(node, globalID); + + for( EdgeID e = 0; e < sorted_graph[node].size(); e++) { + NodeID target = sorted_graph[node][e].first; + EdgeID e_bar = Q.new_edge(node, target); + Q.setEdgeWeight(e_bar, sorted_graph[node][e].second); + } + } + + Q.finish_construction(); + + for( PEID peID = 0; peID < size; peID++) { + m_messages[peID].clear(); + } + //now distribute the node weights + //pack messages + std::unordered_map< NodeID, NodeWeight >::iterator wit; + for( wit = node_weights.begin(); wit != node_weights.end(); wit++) { + NodeID node = wit->first; + NodeWeight weight = wit->second; + PEID peID = node / divisor; + + m_messages[ peID ].push_back( node ); + m_messages[ peID ].push_back( weight ); + } + + for( PEID peID = 0; peID < size; peID++) { + if( peID != rank ) { + if( m_messages[peID].size() == 0 ){ + m_messages[peID].push_back(std::numeric_limits::max()); + } + + MPI_Request rq; + MPI_Isend( &m_messages[peID][0], + m_messages[peID].size(), + MPI_UNSIGNED_LONG_LONG, + peID, peID+8*size, communicator, &rq); + } + } + + if( m_messages[ rank ].size() != 0 ) { + for( ULONG i = 0; i < m_messages[rank].size()-1; i+=2) { + NodeID globalID = m_messages[rank][i]; + NodeID node = globalID - from; + NodeWeight weight = m_messages[rank][i+1]; + Q.setNodeWeight( node , Q.getNodeWeight(node) + weight); + } + } + + counter = 0; + while( counter < size - 1) { + // wait for incomming message of an adjacent processor + MPI_Status st; + MPI_Probe(MPI_ANY_SOURCE, rank+8*size, communicator, &st); + + int message_length; + MPI_Get_count(&st, MPI_UNSIGNED_LONG_LONG, &message_length); + std::vector incmessage; incmessage.resize(message_length); + + MPI_Status rst; + MPI_Recv( &incmessage[0], message_length, MPI_UNSIGNED_LONG_LONG, st.MPI_SOURCE, rank+8*size, communicator, &rst); + counter++; + + // now integrate the changes + if( incmessage[0] == std::numeric_limits< NodeID >::max()) continue; // nothing to do + + for( ULONG i = 0; i < incmessage.size()-1; i+=2) { + NodeID globalID = incmessage[i]; + NodeWeight weight = incmessage[i+1]; + NodeID node = globalID - from; + Q.setNodeWeight( node , Q.getNodeWeight(node) + weight); + } + } } From 4447fa359dbc6288ca865dd1bd208d8dce0ddffc Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Tue, 15 Oct 2024 17:21:20 +0100 Subject: [PATCH 58/64] Ranged-based loops --- .../parallel_contraction.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 1ca526e9..3edf77a1 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -281,10 +281,10 @@ void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( MPI m_messages.resize(size); //build messages - hashed_graph::iterator it; - for( it = hG.begin(); it != hG.end(); it++) { - data_hashed_edge & e = it->second; - hashed_edge he = it->first; + //hashed_graph::iterator it; + for(auto & it : hG) { + data_hashed_edge & e = it.second; + hashed_edge he = it.first; PEID peID = he.source / divisor; m_messages[ peID ].push_back( he.source ); @@ -369,9 +369,9 @@ void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( MPI sorted_graph.resize( local_num_cnodes ); EdgeID edge_counter = 0; - for( it = local_graph.begin(); it != local_graph.end(); it++) { - data_hashed_edge & e = it->second; - hashed_edge he = it->first; + for(auto & it : local_graph) { + data_hashed_edge & e = it.second; + hashed_edge he = it.first; if( from <= he.target && he.target <= to) { std::pair< NodeID, NodeWeight > edge; From ad9e832634fb5a38456ca740d0d73919de004792 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Wed, 16 Oct 2024 11:29:19 +0100 Subject: [PATCH 59/64] LTO on Cray --- cmake/KaHIPSettings.cmake | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index 90a63ddb..0222c391 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -37,13 +37,18 @@ if(kahip_ENABLE_UNITY_BUILD) set(CMAKE_UNITY_BUILD ON) endif() -if(kahip_ENABLE_IPO) - kahip_enable_ipo() -endif() - add_library(kahip_warnings INTERFACE) add_library(kahip_options INTERFACE) +if(kahip_ENABLE_IPO) + if ($CMAKE_CXX_COMPILER_ID MATCHES "CrayClang") + target_compile_options(kahip_options INTERFACE -flto) + target_link_options(kahip_options INTERFACE -flto) + else () + kahip_enable_ipo() + endif () +endif() + include(${CMAKE_SOURCE_DIR}/cmake/StandardProjectSettings.cmake) # tweak compiler flags From ac05b4f3748bfb7e23e7118cb9f6969a2db39fa2 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Wed, 16 Oct 2024 15:43:31 +0100 Subject: [PATCH 60/64] Project setup macro --- cmake/InterproceduralOptimization.cmake | 6 +++--- cmake/KaHIPSettings.cmake | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cmake/InterproceduralOptimization.cmake b/cmake/InterproceduralOptimization.cmake index 000d70f5..9dfdd553 100644 --- a/cmake/InterproceduralOptimization.cmake +++ b/cmake/InterproceduralOptimization.cmake @@ -1,9 +1,9 @@ -macro(kahip_enable_ipo) +function(kahip_enable_ipo) include(CheckIPOSupported) check_ipo_supported(RESULT result OUTPUT output) if(result) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) else() - message(SEND_ERROR "IPO is not supported: ${output}") + message(WARNING "IPO is not supported: ${output}") endif() -endmacro() +endfunction() diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index 0222c391..a0aa5c1d 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -2,15 +2,14 @@ message(STATUS "Configuring Kahip") include(${CMAKE_SOURCE_DIR}/cmake/Cache.cmake) include(${CMAKE_SOURCE_DIR}/cmake/CompilerWarnings.cmake) -include(${CMAKE_SOURCE_DIR}/cmake/InterproceduralOptimization.cmake) include(${CMAKE_SOURCE_DIR}/cmake/Sanitizers.cmake) include(${CMAKE_SOURCE_DIR}/cmake/StaticAnalyzers.cmake) include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) kahip_supports_sanitizers() -option(kahip_ENABLE_IPO "Enable IPO/LTO" OFF) -option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" OFF) +option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) +option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" ON) option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" OFF) option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) @@ -41,10 +40,12 @@ add_library(kahip_warnings INTERFACE) add_library(kahip_options INTERFACE) if(kahip_ENABLE_IPO) - if ($CMAKE_CXX_COMPILER_ID MATCHES "CrayClang") + if (${CMAKE_CXX_COMPILER_ID} MATCHES "Cray.*") + message(VERBOSE "IPO: Cray compiler found") target_compile_options(kahip_options INTERFACE -flto) target_link_options(kahip_options INTERFACE -flto) else () + include(${CMAKE_SOURCE_DIR}/cmake/InterproceduralOptimization.cmake) kahip_enable_ipo() endif () endif() From 5c5caca2676b28663c3940949d5d60cb14a2ee83 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Wed, 16 Oct 2024 15:48:33 +0100 Subject: [PATCH 61/64] Testing off for caliper run --- parallel/parallel_src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parallel/parallel_src/CMakeLists.txt b/parallel/parallel_src/CMakeLists.txt index b4e973dc..8a4d0ba9 100644 --- a/parallel/parallel_src/CMakeLists.txt +++ b/parallel/parallel_src/CMakeLists.txt @@ -225,7 +225,7 @@ target_link_libraries(parhip_interface_static PRIVATE kahip_options kahip_warnin install(TARGETS parhip_interface_static DESTINATION lib) # Temporary Testing -set(ENABLE_TESTING ON) +set(ENABLE_TESTING OFF) if(ENABLE_TESTING) find_package(Catch2 REQUIRED CONFIG) enable_testing() From d835659601e68c00b0713f086437114f0b156158 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 17 Oct 2024 11:02:52 +0100 Subject: [PATCH 62/64] No more unity --- .gitmodules | 3 +++ cmake/KaHIPSettings.cmake | 4 +++- extern/caliper | 1 + .../parallel_contraction.cpp | 8 ++++---- 4 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 .gitmodules create mode 160000 extern/caliper diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..6faa7b24 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "extern/caliper"] + path = extern/caliper + url = https://github.com/LLNL/Caliper.git diff --git a/cmake/KaHIPSettings.cmake b/cmake/KaHIPSettings.cmake index a0aa5c1d..5af30f3e 100644 --- a/cmake/KaHIPSettings.cmake +++ b/cmake/KaHIPSettings.cmake @@ -9,7 +9,7 @@ include(${CMAKE_SOURCE_DIR}/cmake/Utilities.cmake) kahip_supports_sanitizers() option(kahip_ENABLE_IPO "Enable IPO/LTO" ON) -option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" ON) +option(kahip_ENABLE_UNITY_BUILD "Enable Unity Build Mode" OFF) option(kahip_ENABLE_USER_LINKER "Enable user-selected linker" OFF) option(kahip_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) option(kahip_ENABLE_SANITIZERS "Enable sanitizers" OFF) @@ -36,6 +36,8 @@ if(kahip_ENABLE_UNITY_BUILD) set(CMAKE_UNITY_BUILD ON) endif() +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON) + add_library(kahip_warnings INTERFACE) add_library(kahip_options INTERFACE) diff --git a/extern/caliper b/extern/caliper new file mode 160000 index 00000000..063c3974 --- /dev/null +++ b/extern/caliper @@ -0,0 +1 @@ +Subproject commit 063c39746b2f1c498b65c0752cb940dcf9bd7697 diff --git a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp index 3edf77a1..63ee3852 100644 --- a/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp +++ b/parallel/parallel_src/lib/parallel_contraction_projection/parallel_contraction.cpp @@ -427,10 +427,10 @@ void parallel_contraction::redistribute_hased_graph_and_build_graph_locally( MPI } //now distribute the node weights //pack messages - std::unordered_map< NodeID, NodeWeight >::iterator wit; - for( wit = node_weights.begin(); wit != node_weights.end(); wit++) { - NodeID node = wit->first; - NodeWeight weight = wit->second; + //std::unordered_map< NodeID, NodeWeight >::iterator wit; + for(auto & node_weight : node_weights) { + NodeID node = node_weight.first; + NodeWeight weight = node_weight.second; PEID peID = node / divisor; m_messages[ peID ].push_back( node ); From 941734027959068b563f3ab391b390e29af9be40 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 17 Oct 2024 11:38:12 +0100 Subject: [PATCH 63/64] No Warning in release mode --- cmake/CompilerWarnings.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index 2018895a..36c12d36 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -113,10 +113,10 @@ function( target_compile_options( ${project_name} INTERFACE # C++ warnings - $<$:${PROJECT_WARNINGS_CXX}> + $<$,$>:$<$:${PROJECT_WARNINGS_CXX}>> # C warnings - $<$:${PROJECT_WARNINGS_C}> + $<$,$>:$<$:${PROJECT_WARNINGS_C}>> # Cuda warnings - $<$:${PROJECT_WARNINGS_CUDA}> + $<$,$>:$<$:${PROJECT_WARNINGS_CUDA}>> ) endfunction() From 382c0c62cce3ee408eae69256706ba9070cb1ec6 Mon Sep 17 00:00:00 2001 From: Erich Essmann Date: Thu, 17 Oct 2024 17:19:56 +0100 Subject: [PATCH 64/64] Edge list converter fixed --- .../app/edge_list_to_metis_graph.cpp | 286 +++++++++++------- .../data_structure/parallel_graph_access.h | 35 +++ .../parallel_src/lib/io/parallel_graph_io.cpp | 14 +- 3 files changed, 218 insertions(+), 117 deletions(-) diff --git a/parallel/parallel_src/app/edge_list_to_metis_graph.cpp b/parallel/parallel_src/app/edge_list_to_metis_graph.cpp index 4b917d44..653068c8 100644 --- a/parallel/parallel_src/app/edge_list_to_metis_graph.cpp +++ b/parallel/parallel_src/app/edge_list_to_metis_graph.cpp @@ -4,120 +4,186 @@ * Source of KaHIP -- Karlsruhe High Quality Partitioning. * Christian Schulz *****************************************************************************/ - -#include -#include -#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include -#include -#include -#include "partition_config.h" -#include "parse_parameters.h" + #include "data_structure/hashed_graph.h" #include "data_structure/parallel_graph_access.h" #include "io/parallel_graph_io.h" +#include "parse_parameters.h" +#include "partition_config.h" -using namespace std; - -int main(int argn, char **argv) -{ - using namespace parhip; - MPI_Init(&argn, &argv); - - PPartitionConfig partition_config; - std::string graph_filename; - - int ret_code = parse_parameters(argn, argv, - partition_config, - graph_filename); - - if(ret_code) { - MPI_Finalize(); - return 0; - } - - - std::ifstream in(graph_filename.c_str()); - if (!in) { - std::cerr << "Error opening " << graph_filename << std::endl; - return 1; - } - - std::string line; - std::getline(in, line); // skip first line - std::cout << line << std::endl; - - std::unordered_map< NodeID, std::unordered_map< NodeID, int> > source_targets; - - std::cout << "starting io" << std::endl; - EdgeID edge_counter = 0; - EdgeID selfloops = 0; - - NodeID source; - NodeID target; - while( !in.eof() ) { - std::getline(in, line); - std::stringstream ss(line); - - ss >> source; - ss >> target; - - if( source == target ) { - std::getline(in, line); - selfloops++; - continue; - } - - if( source_targets[source].find(target) == source_targets[source].end() ) { - source_targets[source][target] = 0; - } - if( source_targets[target].find(source) == source_targets[target].end() ) { - source_targets[target][source] = 0; - } - - source_targets[source][target] += 1; - source_targets[target][source] += 1; - } - - std::cout << "selfloops " << selfloops << std::endl; - std::cout << "io done" << std::endl; - - NodeID distinct_nodes = source_targets.size(); - std::unordered_map< NodeID, NodeID > map_orignal_id_to_consequtive; - //std::unordered_map< NodeID, std::unordered_map< NodeID, bool > >::iterator it; - NodeID counter = 0; - for( auto it = source_targets.begin(); it != source_targets.end(); it++) { - if( map_orignal_id_to_consequtive.find(it->first) == map_orignal_id_to_consequtive.end()) { - map_orignal_id_to_consequtive[it->first] = counter++; - } - edge_counter += it->second.size(); - - } - std::cout << "starting construction" << std::endl; - - complete_graph_access G; - G.start_construction( distinct_nodes, edge_counter, distinct_nodes, edge_counter); - G.set_range(0, distinct_nodes); - - EdgeID my_count = 0; - for( auto it = source_targets.begin(); it != source_targets.end(); it++) { - NodeID node = G.new_node(); - - for( auto edge_it = source_targets[it->first].begin(); - source_targets[it->first].end() != edge_it; - edge_it++) { - G.new_edge(node, map_orignal_id_to_consequtive[edge_it->first]); - my_count += edge_it->second; - } - } - G.finish_construction(); - std::cout << "my_count " << my_count << std::endl; - std::cout << "my_count/2+selfloops " << (my_count/2+selfloops) << std::endl; - - std::string outputfilename("converted.graph"); - parallel_graph_io::writeGraphSequentially(G, outputfilename); - - - return 0; +namespace fs = std::filesystem; + +int main(int argc, char** argv) { + using namespace parhip; + MPI_Init(&argc, &argv); + + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + PPartitionConfig partition_config{}; + std::string graph_filename; + + if (argc != 2) { + if (rank == ROOT) { + std::cout << "usage: "; + std::cout << "edge_list_to_metis inputfilename" << std::endl; + } + MPI_Finalize(); + return EXIT_FAILURE; + } + graph_filename = argv[1]; + + if (!fs::exists(graph_filename)) { + if (rank == 0) { + for (int i = 0; i < argc; ++i) { + std::cout << argv[i] << std::endl; + } + std::cerr << std::format("Error: File '{}' does not exist.\n", + graph_filename); + } + MPI_Finalize(); + return EXIT_FAILURE; + } + + std::ifstream in_file(graph_filename); + if (!in_file.is_open()) { + if (rank == 0) { + std::cerr << std::format("Error: Could not open file '{}'.\n", + graph_filename); + } + MPI_Finalize(); + return EXIT_FAILURE; + } + + if (rank == 0) { + std::cout << "Starting IO...\n"; + } + + std::unordered_map> source_targets; + EdgeID selfloops = 0; + + std::string line; + while (std::getline(in_file, line)) { + if (line.empty()) { + continue; + } + + std::string_view const line_view(line); + auto comma_pos = line_view.find(','); + + if (comma_pos == std::string_view::npos) { + if (rank == 0) { + std::cerr << std::format("Malformed line (missing comma): '{}'\n", + line); + } + continue; + } + + std::string_view const source_str = line_view.substr(0, comma_pos); + std::string_view const target_str = line_view.substr(comma_pos + 1); + + NodeID source = 0; + NodeID target = 0; + + auto source_result = std::from_chars( + source_str.data(), source_str.data() + source_str.size(), source); + auto target_result = std::from_chars( + target_str.data(), target_str.data() + target_str.size(), target); + + if (source_result.ec != std::errc{} || target_result.ec != std::errc{}) { + if (rank == 0) { + std::cerr << std::format( + "Error parsing line '{}': invalid number format.\n", line); + } + continue; + } + + if (source == target) { + ++selfloops; + continue; + } + + ++source_targets[source][target]; + ++source_targets[target][source]; + } + + if (rank == 0) { + std::cout << std::format("Self-loops detected: {}\n", selfloops); + std::cout << "IO completed.\n"; + } + + // Map original node IDs to consecutive IDs + std::unordered_map node_id_mapping; + NodeID consecutive_id = 0; + + for (auto const& [node_id, _] : source_targets) { + node_id_mapping[node_id] = consecutive_id++; + } + + EdgeID edge_counter = 0; + for (auto const& [_, targets] : source_targets) { + edge_counter += targets.size(); + } + + if (rank == 0) { + std::cout << "Starting graph construction...\n"; + } + + complete_graph_access G; + G.start_construction(consecutive_id, edge_counter, consecutive_id, + edge_counter); + G.set_range(0, consecutive_id); + + EdgeID total_edge_weight = 0; + + for (auto const& [node_id, targets] : source_targets) { + NodeID const new_node = G.new_node(); + + for (auto const& [target_id, weight] : targets) { + G.new_edge(new_node, node_id_mapping[target_id]); + total_edge_weight += weight; + } + } + + G.finish_construction(); + + if (rank == 0) { + std::cout << std::format("Total edge weight: {}\n", total_edge_weight); + std::cout << std::format( + "Adjusted edge count (accounting for self-loops): {}\n", + ((total_edge_weight / 2) + selfloops)); + } + + // Generate output filename by replacing the input file's extension with + // ".graph" + fs::path input_path(graph_filename); + input_path.replace_extension(".graph"); + std::string output_filename = input_path.string(); + + if (rank == 0) { + int const write_status = parallel_graph_io::writeGraphSequentially(G, output_filename); + if (write_status != 0) { + std::cerr << std::format("Error writing graph to '{}'.\n", + output_filename); + MPI_Finalize(); + return EXIT_FAILURE; + } else { + std::cout << std::format("Graph successfully written to '{}'.\n", + output_filename); + } + } + + MPI_Finalize(); + return EXIT_SUCCESS; } diff --git a/parallel/parallel_src/lib/data_structure/parallel_graph_access.h b/parallel/parallel_src/lib/data_structure/parallel_graph_access.h index d52d1fe4..90bfdedc 100644 --- a/parallel/parallel_src/lib/data_structure/parallel_graph_access.h +++ b/parallel/parallel_src/lib/data_structure/parallel_graph_access.h @@ -61,6 +61,7 @@ struct Edge { #define forall_out_edges(G,e,n) { for(EdgeID e = G.get_first_edge(n), end = G.get_first_invalid_edge(n); e < end; ++e) { #define endfor }} + class parallel_graph_access; //handle communication of data associated with ghost nodes @@ -1026,5 +1027,39 @@ inline void ghost_node_communication::update_ghost_node_data_global() { MPI_Barrier(m_communicator); } + +// Modern and safe graph traversal functions +// Function to iterate over all local nodes +template +void for_all_local_nodes(parallel_graph_access& G, Func func) { + for (NodeID n = 0; n < G.number_of_local_nodes(); ++n) { + func(n); + } +} + +// Function to iterate over all ghost nodes +template +void for_all_ghost_nodes(parallel_graph_access& G, Func func) { + for (NodeID node = G.number_of_local_nodes()+1, end = G.number_of_local_nodes()+1+G.number_of_ghost_nodes(); node < end; ++node) { + func(node); + } +} + +// Function to iterate over all local edges +template +void for_all_local_edges(parallel_graph_access& G, Func func) { + for (EdgeID e = 0; e < G.number_of_local_edges(); ++e) { + func(e); + } +} + +// Function to iterate over all outgoing edges of a node +template +void for_all_out_edges(parallel_graph_access& G, NodeID n, Func func) { + for (EdgeID e = G.get_first_edge(n); e < G.get_first_invalid_edge(n); ++e) { + func(e); + } +} + } #endif /* end of include guard: PARALLEL_GRAPH_ACCESS_X6O9MRS8 */ diff --git a/parallel/parallel_src/lib/io/parallel_graph_io.cpp b/parallel/parallel_src/lib/io/parallel_graph_io.cpp index 9719532f..2d90e26d 100644 --- a/parallel/parallel_src/lib/io/parallel_graph_io.cpp +++ b/parallel/parallel_src/lib/io/parallel_graph_io.cpp @@ -640,12 +640,12 @@ int parallel_graph_io::writeGraphParallelSimple(parallel_graph_access & G, std::ofstream f(filename.c_str()); f << G.number_of_global_nodes() << " " << G.number_of_global_edges()/2 << std::endl; - forall_local_nodes(G, node) { - forall_out_edges(G, e, node) { - f << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " ; - } endfor + for_all_local_nodes(G, [&](PEID node) { + for_all_out_edges(G, node, [&](EdgeID e) { + f << (G.getGlobalID(G.getEdgeTarget(e))+1) << " " ; + }); f << "\n"; - } endfor + }); f.close(); } @@ -735,7 +735,7 @@ int parallel_graph_io::writeGraphSequentially(complete_graph_access & G, std::of forall_local_nodes(G, node) { forall_out_edges(G, e, node) { - f << " " << (G.getEdgeTarget(e)+1) ; + f << " " << (G.getEdgeTarget(e)+1) ; } endfor f << "\n"; } endfor @@ -743,7 +743,7 @@ int parallel_graph_io::writeGraphSequentially(complete_graph_access & G, std::of } int parallel_graph_io::writeGraphSequentially(complete_graph_access & G, std::string filename) { - std::ofstream f(filename.c_str()); + std::ofstream f(filename); writeGraphSequentially(G, f); f.close(); return 0;