// Copyright 2025 Memgraph Ltd. // // Use of this software is governed by the Business Source License // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source // License, and you may not use this file except in compliance with the Business Source License. // // As of the Change Date specified in that file, in accordance with // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. #pragma once #include #include #include #include #include #include #include #include namespace mg_test_utility { constexpr double ABSOLUTE_ERROR_EPSILON{10e-4}; constexpr double AVERAGE_ABSOLUTE_ERROR_EPSILON{10e-5}; /// This class is threadsafe class Timer { public: explicit Timer() : start_time_(std::chrono::steady_clock::now()) {} template > TDuration Elapsed() const { return std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time_); } private: std::chrono::steady_clock::time_point start_time_; }; /// ///@brief Method that calculates the maximum absolute error between vector components. /// The vector type must be an arithmetic type. Vectors must have the same size. /// ///@param result Container that stores calculated values ///@param correct Container that stores accurate values ///@return maximum absolute error /// template T MaxAbsoluteError(const std::vector &result, const std::vector &correct) { static_assert(std::is_arithmetic_v, "mg_test_utility::MaxAbsoluteError expects the vector type to be an arithmetic type.\n"); assert(result.size() == correct.size()); auto size = correct.size(); T max_absolute_error = 0; for (auto index = 0; index < size; index++) { T absolute_distance = std::abs(result[index] - correct[index]); max_absolute_error = std::max(max_absolute_error, absolute_distance); } return max_absolute_error; } /// ///@brief Method that calculates the average absolute error between vector components. /// The vector type must be an arithmetic type. Vectors must have the same size. /// ///@param result Container that stores calculated values ///@param correct Container that stores accurate values ///@return average absolute error /// template double AverageAbsoluteError(const std::vector &result, const std::vector &correct) { static_assert(std::is_arithmetic_v, "mg_test_utility::AverageAbsoluteError expects the vector type to be an arithmetic type.\n"); assert(result.size() == correct.size()); auto size = correct.size(); T manhattan_distance = 0; for (auto index = 0; index < size; index++) { T absolute_distance = std::abs(result[index] - correct[index]); manhattan_distance += absolute_distance; } double average_absolute_error = 0; if (size > 0) average_absolute_error = static_cast(manhattan_distance) / size; return average_absolute_error; } /// ///@brief A method that determines whether given vectors are the same within the defined tolerance. /// The vector type must be an arithmetic type. Vectors must have the same size. /// ///@param result Container that stores calculated values ///@param correct Container that stores accurate values ///@return true if the maximum absolute error is lesser than ABSOLUTE_ERROR_EPSILON /// and the average absolute error is lesser than AVERAGE_ABSOLUTE_ERROR_EPSILON, /// false otherwise /// template bool TestEqualVectors(const std::vector &result, const std::vector &correct, double absolute_error = ABSOLUTE_ERROR_EPSILON, double average_error = AVERAGE_ABSOLUTE_ERROR_EPSILON) { static_assert(std::is_arithmetic::value, "Not an arithmetic type!"); return MaxAbsoluteError(result, correct) < absolute_error && AverageAbsoluteError(result, correct) < absolute_error; } /// ///@brief A method that determines whether given unordered maps with (int, arithmetic_type) values are the same within /// defined tolerance. Maps must have the same keys. /// ///@param result Container storing calculated results ///@param correct Container storing correct values ///@param absolute_error Absolute error between any two pairs of resulting and correct value ///@return true if the maximum absolute error is lesser than ABSOLUTE_ERROR_EPSILON, false otherwise /// template bool TestEqualUnorderedMaps(const std::unordered_map &result, const std::unordered_map &correct, double absolute_error = ABSOLUTE_ERROR_EPSILON) { static_assert(std::is_arithmetic::value, "Not an arithmetic type!"); if (result.size() != correct.size()) return false; auto get_map_keys = [](const std::unordered_map &map) -> std::set { std::set keys; for (const auto [k, v] : map) { keys.insert(k); } return keys; }; auto keys_result = get_map_keys(result); auto keys_correct = get_map_keys(correct); if (keys_result != keys_correct) return false; return std::equal(keys_result.begin(), keys_result.end(), keys_correct.begin(), [&result, &correct, &absolute_error](auto key_result, auto key_correct) { return std::abs(result.at(key_result) - correct.at(key_correct)) <= absolute_error; }); } /// ///@brief A method that determines whether given vectors with (int, arithmetic_type) values are the same within the /// defined tolerance. Vectors must have the same size. /// ///@param result Container that stores calculated values ///@param correct Container that stores accurate values ///@param absolute_error Absolute error between any two pairs of resulting and correct value ///@return true if the maximum absolute error is lesser than ABSOLUTE_ERROR_EPSILON false otherwise /// template bool TestEqualVectorPairs(const std::vector> &result, const std::vector> &correct, double absolute_error = ABSOLUTE_ERROR_EPSILON) { if (result.size() != correct.size()) return false; std::vector> result_cp(result); std::vector> correct_cp(correct); std::sort(result_cp.begin(), result_cp.end()); std::sort(correct_cp.begin(), correct_cp.end()); return std::equal( result_cp.begin(), result_cp.end(), correct_cp.begin(), [&absolute_error](auto result_cp, auto correct_cp) { return result_cp.first == correct_cp.first && std::abs(result_cp.second - correct_cp.second) <= absolute_error; }); } } // namespace mg_test_utility