From 1402e36698375797477a3f31e763549a49500bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=A1vio=20Lisb=C3=B4a?= Date: Mon, 17 Apr 2017 01:25:41 -0300 Subject: [PATCH] #17: adl/oct: Create Component Unit `adl/oct/traits` - Module `adl/oct/traits` created - Major corrections and some additions in the module `adl/oct/limits` - Minor corrections in the module `adl/char_helper` --- CMakeLists.txt | 2 + Makefile | 24 +- include/adl/char_helper.hpp | 10 +- include/adl/oct.fwd.hpp | 10 +- include/adl/oct/limits.hpp | 60 ++-- include/adl/oct/traits.hpp | 379 +++++++++++++++++++++ test/include/adl_catch/macros.hpp | 1 + test/unit/adl/oct/traits.unit.cpp | 536 ++++++++++++++++++++++++++++++ 8 files changed, 988 insertions(+), 34 deletions(-) create mode 100644 include/adl/oct/traits.hpp create mode 100644 test/unit/adl/oct/traits.unit.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d48bc67..b7ccefb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,7 @@ target_sources(${api} INTERFACE "${api_dir_root}/adl/oct.fwd.hpp" "${api_dir_root}/adl/oct/domain_space.hpp" "${api_dir_root}/adl/oct/limits.hpp" + "${api_dir_root}/adl/oct/traits.hpp" # adl/oct/dsl "${api_dir_root}/adl/oct/dsl.hpp" @@ -127,6 +128,7 @@ add_executable(${test} # unit:adl/oct "${test_dir_unit}/adl/oct/limits.unit.cpp" + "${test_dir_unit}/adl/oct/traits.unit.cpp" ) target_link_libraries(${test} ${test_iapi}) enable_testing() diff --git a/Makefile b/Makefile index 061c777..cf86b11 100644 --- a/Makefile +++ b/Makefile @@ -6,15 +6,33 @@ SHELL := /bin/bash RM := rm -rf MKDIR := mkdir -p BUILD_DIR := ./build +BUILD_DIRS := $(BUILD_DIR) cmake-build-* bin SRC_DIR := .. +IDEA_FILES := .idea +ECLIPSE_FILES := .project .cproject .settings +KDEVELOP_FILES := *.kdev4 .kdev4 + all: $(BUILD_DIR)/Makefile $(MAKE) -C $(BUILD_DIR) -$(BUILD_DIR)/Makefile: +clean: $(BUILD_DIR)/Makefile + $(MAKE) -C $(BUILD_DIR) clean + +distclean: + $(RM) $(BUILD_DIRS) + +$(BUILD_DIR)/Makefile: CMakeLists.txt $(MKDIR) $(BUILD_DIR) > /dev/null cd $(BUILD_DIR) > /dev/null 2>&1 && cmake $(SRC_DIR) -distclean: - $(RM) $(BUILD_DIR) +eclipse-clean: + $(RM) $(ECLIPSE_FILES) + +kdevelop-clean: + $(RM) $(KDEVELOP_FILES) + +idea-clean: + $(RM) $(IDEA_FILES) +ide-clean: eclipse-clean kdevelop-clean idea-clean diff --git a/include/adl/char_helper.hpp b/include/adl/char_helper.hpp index 626bcf9..995928e 100644 --- a/include/adl/char_helper.hpp +++ b/include/adl/char_helper.hpp @@ -151,8 +151,8 @@ struct adl_CLASS char_helper : public char_definitions { noexcept(noexcept(String().empty()) && noexcept(String(""))); constexpr static const char_type* rfind(const char_type* p, std::size_t count, const char_type& ch); constexpr static bool is_digit(const char_type& c) noexcept; - template constexpr inline T digit_to_integer(const char_type& c) noexcept; - template constexpr static T to_integer(T fallback_value, const char_type* c, size_t size = 0); + template constexpr static T digit_to_integer(const char_type& c) noexcept; + template constexpr static T to_integer(T fallback_value, const char_type* c, size_t size = 0) noexcept; private: constexpr static bool is_finished_(const char_type* s, size_t s_size, size_t s_count); @@ -169,7 +169,7 @@ struct adl_CLASS char_helper : public char_definitions { bool found = false, bool negative = false, char_digit_base base = char_digit_base::decimal - ); + ) noexcept; }; template adl_API int string_printf(std::string& buffer, const char* fmt, Args... args); @@ -353,7 +353,7 @@ constexpr T char_helper::digit_to_integer(const char_type& c) noexcept { template template -constexpr T char_helper::to_integer(T fallback_value, const char_type* c, size_t size) { +constexpr T char_helper::to_integer(T fallback_value, const char_type* c, size_t size) noexcept { return choose_valid_(to_integer_(c, size, 0, T(0)), fallback_value); } @@ -423,7 +423,7 @@ constexpr std::pair char_helper::to_integer_( bool found, bool negative, char_digit_base base -) { +) noexcept { return !is_finished_(c, c_size, c_count) ? is_digit(c[c_count]) ? to_integer_( diff --git a/include/adl/oct.fwd.hpp b/include/adl/oct.fwd.hpp index 0bf10e1..d6f9320 100644 --- a/include/adl/oct.fwd.hpp +++ b/include/adl/oct.fwd.hpp @@ -25,7 +25,7 @@ enum class domain_space; using default_var_id_type = int; template class value_limits; -template struct var_id_limits; +template struct var_id_limits; /** * The hard-limit maximum number of octagonal variables supported for a given variable id's type. @@ -49,6 +49,14 @@ constexpr static std::size_t max_oct_variables = template constexpr static std::size_t max_octdiff_variables = max_oct_variables * 2; +// +// traits.hpp +// +template > struct value_traits; +template > struct var_id_traits; +template struct var_traits; + + adl_END_MAIN_MODULE #endif // adl__oct__fwd__hpp__ diff --git a/include/adl/oct/limits.hpp b/include/adl/oct/limits.hpp index 9ab6b87..aaadeb8 100644 --- a/include/adl/oct/limits.hpp +++ b/include/adl/oct/limits.hpp @@ -21,9 +21,11 @@ adl_BEGIN_MAIN_MODULE(oct) */ template struct value_limits : public std::numeric_limits { + using value_type = ValueType; using std::numeric_limits::has_infinity; using std::numeric_limits::infinity; using std::numeric_limits::max; + using std::numeric_limits::is_specialized; constexpr static ValueType top(); }; @@ -34,13 +36,13 @@ struct value_limits : public std::numeric_limits { * @tparam Domain The domain to which the limits apply. * @tparam VarIdType The variable's underlying ID type. */ -template +template struct var_id_limits { // Types - using var_id_type = VarIdType; + using var_id_type = default_var_id_type; // Constexpr static values - constexpr static bool valid = false; + constexpr static const bool valid = false; constexpr static const domain_space space = Domain; }; @@ -49,16 +51,17 @@ struct var_id_limits { * @tparam VarIdType The variable ID's type. * @see adl::oct::var_id_limits<> */ -template -struct var_id_limits { +template <> +struct var_id_limits { // Types - using var_id_type = VarIdType; + using var_id_type = default_var_id_type; + using counterpart_var_id_limits = var_id_limits; // Constexpr static values - constexpr static bool valid = true; + constexpr static const bool valid = true; constexpr static const domain_space space = domain_space::oct; - constexpr static oct::domain_space counterpart_space = oct::domain_space::octdiff; - constexpr static const std::size_t max_variables = max_oct_variables; + constexpr static const domain_space counterpart_space = oct::domain_space::octdiff; + constexpr static const std::size_t max_variables = max_oct_variables; constexpr static const var_id_type invalid_var_id = var_id_type(0); constexpr static const var_id_type first_var_id = var_id_type(1); constexpr static const var_id_type last_var_id = var_id_type(max_variables - 1); @@ -67,6 +70,9 @@ struct var_id_limits { constexpr static const var_id_type rbegin_var_id = last_var_id; constexpr static const var_id_type rend_var_id = 0; + constexpr static const var_id_type min_var_id_value = -last_var_id; + constexpr static const var_id_type max_var_id_value = end_var_id; + constexpr static const char *const base_var_name_format = "x%d"; constexpr static const char *const positive_var_name_format = "%s"; constexpr static const char *const negative_var_name_format = "-%s"; @@ -76,25 +82,29 @@ struct var_id_limits { * Specialization of `adl::oct::var_id_limits` for the octagonal-difference domain space. * @tparam VarIdType The variable ID's type. */ -template -struct var_id_limits { +template <> +struct var_id_limits { // Types - using var_id_type = VarIdType; + using var_id_type = default_var_id_type; + using counterpart_var_id_limits = var_id_limits; // Constexpr static values - constexpr static bool valid = true; - constexpr static oct::domain_space space = oct::domain_space::octdiff; - constexpr static oct::domain_space counterpart_space = oct::domain_space::oct; - constexpr static std::size_t max_variables = max_octdiff_variables; - constexpr static var_id_type invalid_var_id = var_id_limits::invalid_var_id; - constexpr static var_id_type first_var_id = var_id_limits::first_var_id; - constexpr static var_id_type last_var_id = var_id_type(max_variables - 3); - constexpr static var_id_type begin_var_id = first_var_id; - constexpr static var_id_type end_var_id = var_id_type(last_var_id + 2); - constexpr static var_id_type rbegin_var_id = last_var_id; - constexpr static var_id_type rend_var_id = var_id_limits::rend_var_id; - - constexpr static const char *const base_var_name_format = var_id_limits::base_var_name_format; + constexpr static const bool valid = true; + constexpr static const domain_space space = oct::domain_space::octdiff; + constexpr static const domain_space counterpart_space = oct::domain_space::oct; + constexpr static const std::size_t max_variables = max_octdiff_variables; + constexpr static const var_id_type invalid_var_id = var_id_limits::invalid_var_id; + constexpr static const var_id_type first_var_id = var_id_limits::first_var_id; + constexpr static const var_id_type last_var_id = var_id_type(max_variables - 3); + constexpr static const var_id_type begin_var_id = first_var_id; + constexpr static const var_id_type end_var_id = var_id_type(last_var_id + 2); + constexpr static const var_id_type rbegin_var_id = last_var_id; + constexpr static const var_id_type rend_var_id = var_id_limits::rend_var_id; + + constexpr static const var_id_type min_var_id_value = 0; + constexpr static const var_id_type max_var_id_value = end_var_id; + + constexpr static const char *const base_var_name_format = var_id_limits::base_var_name_format; constexpr static const char *const positive_var_name_format = "%s__pos"; constexpr static const char *const negative_var_name_format = "%s__neg"; }; diff --git a/include/adl/oct/traits.hpp b/include/adl/oct/traits.hpp new file mode 100644 index 0000000..27869b7 --- /dev/null +++ b/include/adl/oct/traits.hpp @@ -0,0 +1,379 @@ +// $flisboac 2017-04-08 +/** + * @file traits.hpp + */ +#ifndef adl__oct__traits__hpp__ +#define adl__oct__traits__hpp__ + + +#include "adl/std/string_view.hpp" +#include "adl/char_helper.hpp" + +#include "adl.cfg.hpp" +#include "adl/oct.fwd.hpp" + +#include "adl/oct/domain_space.hpp" +#include "adl/oct/limits.hpp" + +// +// [[ API ]] +// +adl_BEGIN_MAIN_MODULE(oct) + +template +struct adl_CLASS value_traits { + // Types + using value_limits = ValueLimits; + + // Static assertions + static_assert(value_limits::is_specialized, + "The value_limits class is not valid. Please, specialize it correctly " + "for the provided value type."); + static_assert(std::is_same::value, + "The ValueType provided must be the same as ValueLimits::value_type."); + + // Constexpr static values + constexpr static bool valid = true; +}; + + +template +struct adl_CLASS var_id_traits { + // Types + using var_id_limits = VarIdLimits; + using var_id_type = typename var_id_limits::var_id_type; + + // Constexpr static values + constexpr static bool valid = true; + constexpr static auto space = var_id_limits::space; + constexpr static auto counterpart_space = var_id_limits::counterpart_space; + + // static utility functions + template constexpr static void static_assert_valid_arithmetic_type() noexcept; + + // static property functions + constexpr static bool is_valid_id(var_id_type id) noexcept; + constexpr static bool is_id_in_range(var_id_type id) noexcept; + constexpr static bool is_positive_id(var_id_type id) noexcept; + constexpr static bool is_negative_id(var_id_type id) noexcept; + + // static functions + constexpr static var_id_type normalize_id(var_id_type id) noexcept; + constexpr static var_id_type negate_id(var_id_type id) noexcept; + constexpr static var_id_type increment_id(var_id_type id, size_t offset = 1) noexcept; + constexpr static var_id_type decrement_id(var_id_type id, size_t offset = 1) noexcept; + constexpr static bool id_equals(var_id_type id1, var_id_type id2) noexcept; + constexpr static int id_compare(var_id_type id1, var_id_type id2) noexcept; + constexpr static int id_sign(var_id_type id) noexcept; + constexpr static const char* id_sign_format(var_id_type id) noexcept; + + // static conversion functions + constexpr static var_id_type name_to_id(char const* name) noexcept; + constexpr static var_id_type name_to_id(string_view name) noexcept; + static var_id_type name_to_id(std::string name); + template constexpr static var_id_type arithmetic_to_range(N value) noexcept; + template constexpr static var_id_type arithmetic_to_valid(N value) noexcept; + template constexpr static N id_to_arithmetic(var_id_type id, N fallback_value = N()) noexcept; + constexpr static var_id_type id_to_range(var_id_type id) noexcept; // first-end + constexpr static var_id_type id_to_valid(var_id_type id) noexcept; // first-last + constexpr static std::size_t id_to_index(var_id_type id) noexcept; + constexpr static var_id_type id_to_counterpart(var_id_type id) noexcept; + constexpr static var_id_type id_to_normal_oct(var_id_type id) noexcept; + static void id_to_name( + std::string& name, + var_id_type id, + std::string const& base_name_format = var_id_limits::base_var_name_format, + std::string const& pos_neg_format = std::string()); + static std::string id_to_name( + var_id_type id, + std::string const& base_name_format = var_id_limits::base_var_name_format, + std::string const& pos_neg_format = std::string()); + +private: + constexpr static var_id_type raw_normalize_id_(var_id_type id) noexcept; + template constexpr static var_id_type arithmetic_to_id_limits_(N value) noexcept; +}; + +template +struct adl_CLASS var_traits { + constexpr static bool valid = false; +#if 0 + // Types + using var_id_traits = oct::var_id_traits; + using var_id_limits = typename var_id_traits::var_id_limits; + using var_id_type = typename var_id_traits::var_id_type + using var_type = VarType; + using counterpart_var_type = VarType; + using vexpr_type = oct_vexpr; + using counterpart_vexpr_type = octdiff_vexpr; + + // Constexpr static values + constexpr static bool valid = false; + constexpr static auto space = var_id_traits::space; + constexpr static auto counterpart_space = var_id_traits::counterpart_space; + + // static functions + constexpr static var_type var_with_id(const var_type& var, var_id_type id); + constexpr static counterpart_var_type var_to_counterpart(const var_type& var, var_id_type counterpart_id); +#endif +}; + +adl_END_MAIN_MODULE + + +// +// [[ TEMPLATE IMPLEMENTATION ]] +// +adl_BEGIN_MAIN_MODULE(oct) + +// +// var_id_traits +// + +template +template +constexpr void var_id_traits::static_assert_valid_arithmetic_type() noexcept { + using return_limits = std::numeric_limits; + static_assert(std::is_arithmetic::value, + "Value type must be arithmetic."); +} + +template +constexpr bool var_id_traits::is_valid_id(var_id_type id) noexcept { + const var_id_type nid = var_id_limits::space == domain_space::oct ? raw_normalize_id_(id) : id; + return (nid >= var_id_limits::first_var_id && nid <= var_id_limits::last_var_id); +} + +template +constexpr bool var_id_traits::is_id_in_range(var_id_type id) noexcept { + return is_valid_id(id) || id == var_id_limits::end_var_id; +} + +template +constexpr bool var_id_traits::is_positive_id(var_id_type id) noexcept { + return var_id_limits::space == domain_space::octdiff + ? bool(id & 1) + : (id > 0); +} + +template +constexpr bool var_id_traits::is_negative_id(var_id_type id) noexcept { + return var_id_limits::space == domain_space::octdiff + ? !bool(id & 1) + : (id < 0); +} + +template +constexpr typename adl::oct::var_id_traits::var_id_type +var_id_traits::normalize_id(var_id_type id) noexcept { + return is_valid_id(id) + ? raw_normalize_id_(id) + : var_id_limits::invalid_var_id; +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::negate_id(var_id_type id) noexcept { + return is_valid_id(id) + ? var_id_limits::space == domain_space::oct + ? -id + : (id & 1) ? id - 1 : id + 1 + : var_id_limits::invalid_var_id; +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::increment_id( + var_id_type id, + size_t offset +) noexcept { + auto iid = normalize_id(id); + if (is_valid_id(iid)) iid += (var_id_limits::space == domain_space::octdiff ? offset << 1 : offset); + return is_valid_id(iid) + ? id + : var_id_limits::invalid_var_id; +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::decrement_id( + var_id_type id, + size_t offset +) noexcept { + auto iid = normalize_id(id); + if (is_valid_id(iid)) iid -= (var_id_limits::space == domain_space::octdiff ? offset << 1 : offset); + return is_valid_id(iid) + ? id + : var_id_limits::invalid_var_id; +} + +template +constexpr bool var_id_traits::id_equals(var_id_type a, var_id_type b) noexcept { + return a == b; +} + +template +constexpr int var_id_traits::id_compare(var_id_type a, var_id_type b) noexcept { + return space == domain_space::oct + ? var_id_traits::id_compare( + id_to_counterpart(a), id_to_counterpart(b)) + : (b < a) - (a < b); +} + +template +constexpr int var_id_traits::id_sign(var_id_type id) noexcept { + return is_valid_id(id) + ? (is_negative_id(id) ? -1 : 1) + : 0; +} + +template +constexpr char const* var_id_traits::id_sign_format(var_id_type id) noexcept { + return is_valid_id(id) + ? is_negative_id(id) + ? var_id_limits::negative_var_name_format + : var_id_limits::positive_var_name_format + : ""; +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::raw_normalize_id_(var_id_type id) noexcept { + return var_id_limits::space == domain_space::oct + ? (id < 0) ? -id : id + : (id & 1) ? id - 1 : id; +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::name_to_id(char const* name) noexcept { + return name_to_id(string_view(name)); +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::name_to_id(string_view name) noexcept { + constexpr long long int fallback_value = var_id_limits::invalid_var_id; + return space == domain_space::oct + ? arithmetic_to_valid(char_helper::to_integer(fallback_value, name.data(), name.length())) + : var_id_limits::invalid_var_id; +} + +template +adl_IMPL typename var_id_traits::var_id_type +var_id_traits::name_to_id(std::string name) { + return name_to_id(string_view(name.data(), name.size())); +} + +template +template +constexpr typename var_id_traits::var_id_type +var_id_traits::arithmetic_to_range(N value) noexcept { + var_id_type id = arithmetic_to_id_limits_(value); + return id_to_range(var_id_type(value)); +} + +template +template +constexpr typename var_id_traits::var_id_type +var_id_traits::arithmetic_to_valid(N value) noexcept { + var_id_type id = arithmetic_to_id_limits_(value); + return id_to_valid(var_id_type(value)); +} + +template +template +constexpr N var_id_traits::id_to_arithmetic(var_id_type id, N fallback_value) noexcept { + using return_limits = std::numeric_limits; + static_assert_valid_arithmetic_type(); + // Here, we depend on type promotion... + if (return_limits::max() >= id && return_limits::lowest() <= id) return N(id); + return fallback_value; +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::id_to_range(var_id_type id) noexcept { + return is_id_in_range(id) + ? var_id_type(id) + : var_id_limits::invalid_var_id; +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::id_to_valid(var_id_type id) noexcept { + return is_valid_id(id) + ? var_id_type(id) + : var_id_limits::invalid_var_id; +} + +template +constexpr std::size_t var_id_traits::id_to_index(var_id_type id) noexcept { + return var_id_limits::space == domain_space::octdiff + ? size_t(id - 1) + : var_id_traits::id_to_index(id_to_counterpart(id)); +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::id_to_counterpart(var_id_type id) noexcept { + return id == var_id_limits::end_var_id + ? var_id_traits::var_id_limits::end_var_id + : is_valid_id(id) + ? var_id_limits::space == domain_space::oct + ? var_id_type((id < 0) ? (-id << 1) : (id << 1) - 1) // for oct + : var_id_type((id & 1) ? -(id >> 1): (id >> 1) + 1) + : var_id_limits::invalid_var_id; +} + +template +constexpr typename var_id_traits::var_id_type +var_id_traits::id_to_normal_oct(var_id_type id) noexcept { + return var_id_limits::space == domain_space::oct + ? normalize_id(id) + : id_to_counterpart(normalize_id(id)); +} + +template +adl_IMPL void var_id_traits::id_to_name( + std::string& name, + var_id_type id, + std::string const& base_name_format, + std::string const& pos_neg_format +) { + if (is_valid_id(id)) { + string_printf(name, base_name_format, id_to_normal_oct(id)); + + if (!pos_neg_format.empty()) { + std::string formatted_name; + string_printf(formatted_name, pos_neg_format, name); + name = formatted_name; + } + } +} + +template +adl_IMPL std::string var_id_traits::id_to_name( + var_id_type id, + std::string const& base_name_format, + std::string const& pos_neg_format +) { + std::string name; + id_to_name(name, id, base_name_format, pos_neg_format); + return name; +} + +template +template +constexpr typename var_id_traits::var_id_type +var_id_traits::arithmetic_to_id_limits_(N value) noexcept { + using return_limits = std::numeric_limits; + static_assert_valid_arithmetic_type(); + if (value >= var_id_limits::min_var_id_value && value <= var_id_limits::max_var_id_value) return var_id_type(value); + return var_id_limits::invalid_var_id; +} + +adl_END_MAIN_MODULE + + +#endif //adl__oct__traits__hpp__ diff --git a/test/include/adl_catch/macros.hpp b/test/include/adl_catch/macros.hpp index 68214c3..1e0d919 100644 --- a/test/include/adl_catch/macros.hpp +++ b/test/include/adl_catch/macros.hpp @@ -9,6 +9,7 @@ #include "catch.hpp" #define REQUIRE_SECTION(test__, name__) SECTION( (name__) ) { REQUIRE( (test__) ); } +#define REQUIRE_SECTION_NOTHROW(test__, name__) SECTION( (name__) ) { REQUIRE_NOTHROW( (test__) ); } #define REQUIRE_SECTION_THROWS_AS(test__, exception__, name__) SECTION( (name__) ) { REQUIRE_THROWS_AS( (test__), exception__ ); } #endif //adl_catch__macros__hpp__ diff --git a/test/unit/adl/oct/traits.unit.cpp b/test/unit/adl/oct/traits.unit.cpp new file mode 100644 index 0000000..d565af9 --- /dev/null +++ b/test/unit/adl/oct/traits.unit.cpp @@ -0,0 +1,536 @@ +// $flisboac 2017-04-08 +#include "adl_catch.hpp" +#include "adl/assert.hpp" +#include "adl/oct/traits.hpp" + +#define FOR_OCT " [for domain_space::oct]" +#define FOR_OCTDIFF " [for domain_space::octdiff]" + +#define FOR_POSITIVE_ID " [for non-negated (positive) id]" +#define FOR_NEGATIVE_ID " [for negated (negative) id]" +#define FOR_ID_GT_0 " [for id > 0]" +#define FOR_ID_LT_0 " [for id < 0]" +#define FOR_ID_ZERO " [for id == 0 (the invalid id value)]" +#define FOR_NORMALIZED_ID " [for normalized (positive) id]" +#define FOR_VALID_ID " [for valid id (is_valid_id)]" +#define FOR_INVALID_ID " [for non-valid id (!is_valid_id)]" +#define FOR_INBOUND_ID " [for id within bounds (is_id_in_range)]" +#define FOR_OUTBOUND_ID " [for id outside bounds (is_id_in_range)]" +#define FOR_FIRST_ID " [for id == first_var_id]" +#define FOR_LAST_ID " [for id == last_var_id]" +#define FOR_END_ID " [for id == end_var_id]" +#define FOR_STRINGP_NAME " [for `char const*` name]" +#define FOR_STRINGV_NAME " [for `string_view` name]" +#define FOR_STRING_NAME " [for `std::string` name]" + +namespace { + +namespace imports_ { + using namespace adl; + using domain_space = adl::oct::domain_space; + using oct_traits = adl::oct::var_id_traits; + using octdiff_traits = adl::oct::var_id_traits; + using var_id_type = oct::default_var_id_type; + using oct_limits = oct_traits::var_id_limits; + using octdiff_limits = octdiff_traits::var_id_limits; + + adl_static_assert((oct_traits::valid)); + adl_static_assert((octdiff_traits::valid)); +} + +template +static inline void test_definitions_values_() { + using limits = typename Traits::var_id_limits; + constexpr int domain_space = (int) Traits::space; + constexpr auto max_variables = limits::max_variables; + constexpr auto invalid_var_id = limits::invalid_var_id; + constexpr auto first_var_id = limits::first_var_id; + constexpr auto last_var_id = limits::last_var_id; + constexpr auto begin_var_id = limits::begin_var_id; + constexpr auto end_var_id = limits::end_var_id; + constexpr auto rbegin_var_id = limits::rbegin_var_id; + constexpr auto rend_var_id = limits::rend_var_id; + constexpr auto min_var_id = limits::min_var_id_value; + constexpr auto max_var_id = limits::max_var_id_value; + + SECTION("checking values") { + + // To show, fail some test. Useful for debugging + INFO(domain_space << "::max_variables = " << max_variables); + INFO(domain_space << "::invalid_var_id = " << invalid_var_id); + INFO(domain_space << "::first_var_id = " << first_var_id); + INFO(domain_space << "::last_var_id = " << last_var_id); + INFO(domain_space << "::begin_var_id = " << begin_var_id); + INFO(domain_space << "::end_var_id = " << end_var_id); + INFO(domain_space << "::rbegin_var_id = " << rbegin_var_id); + INFO(domain_space << "::rend_var_id = " << rend_var_id); + INFO(domain_space << "::min_var_id = " << min_var_id); + INFO(domain_space << "::max_var_id = " << max_var_id); + + REQUIRE((limits::first_var_id - 1 == limits::rend_var_id)); + REQUIRE((limits::begin_var_id > 0)); + REQUIRE((limits::last_var_id > 0)); + REQUIRE((limits::rbegin_var_id == limits::last_var_id)); + REQUIRE((limits::rend_var_id == limits::invalid_var_id)); + REQUIRE((limits::end_var_id <= limits::max_variables + 1)); + } +} + +} + +TEST_CASE("adl/oct/traits.hpp:var_id", "[adl][adl/oct][adl/oct/traits]") { + using namespace imports_; + + constexpr var_id_type ov_0 = 0, + ov_p = 1, + ov_n = -1, + ov_p1 = 3, + ov_n1 = -3, + ov_p2 = 4, + ov_n2 = -4, + ov_p3 = 1, + ov_n3 = -1, + ov_L = -oct_limits::last_var_id, // conceptually, this is the last value, non-normalized + ov_e = oct_limits::end_var_id, + ov_E = -oct_limits::end_var_id, // conceptually, end_var_id + 1 + ov_f = oct_limits::first_var_id, + ov_F = -oct_limits::first_var_id, + ov_l = oct_limits::last_var_id; + constexpr var_id_type odv_0 = 0, + odv_p = 1, + odv_n = -1, + odv_p1 = 5, + odv_n1 = 6, + odv_p2 = 7, + odv_n2 = 8, + odv_p3 = 1, + odv_n3 = 2, + odv_L = octdiff_limits::last_var_id + 1, + odv_e = octdiff_limits::end_var_id, + odv_E = octdiff_limits::end_var_id + 1, + odv_f = octdiff_limits::first_var_id, + odv_F = octdiff_limits::first_var_id + 1, + odv_l = octdiff_limits::last_var_id; + + constexpr const char *const ov_p1_name = "x3"; + constexpr const char *const ov_n1_name = "-x3"; + constexpr const char *const ov_x_name = "-xl"; + constexpr const string_view ov_p1_vname(ov_p1_name); + constexpr const string_view ov_n1_vname(ov_n1_name); + constexpr const string_view ov_x_vname(ov_x_name); + const std::string ov_p1_sname(ov_p1_name); + const std::string ov_n1_sname(ov_n1_name); + const std::string ov_x_sname(ov_x_name); + + constexpr size_t ov_p1_index = ov_p1 - 1; + constexpr size_t ov_n1_index = ov_p1 - 1; // it is calculated from the normalized id! + constexpr size_t odv_p1_index = odv_p1 - 1; + constexpr size_t odv_n1_index = odv_n1 - 1; + + test_definitions_values_(); + test_definitions_values_(); + REQUIRE( (octdiff_limits::max_variables == (oct_limits::max_variables * 2)) ); + REQUIRE( (oct_limits::last_var_id + 1 == oct_limits::end_var_id) ); + REQUIRE( (octdiff_limits::last_var_id + 2 == octdiff_limits::end_var_id) ); + + // + // [ static property functions ] + // + + SECTION("is_valid_id(id) [must be within bounds of the valid ids limits, excluding invalids and ends]") { + + adl_static_assert( (oct_traits::is_valid_id(0), true) ); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_valid_id(ov_p)), FOR_ID_GT_0 FOR_INBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_valid_id(ov_n)), FOR_ID_LT_0 FOR_INBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_valid_id(ov_f)), FOR_FIRST_ID FOR_NORMALIZED_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_valid_id(ov_F)), FOR_FIRST_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_valid_id(ov_l)), FOR_LAST_ID FOR_NORMALIZED_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_valid_id(ov_L)), FOR_LAST_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (!oct_traits::is_valid_id(ov_0)), FOR_ID_ZERO FOR_OCT); + REQUIRE_SECTION_NOTHROW( (!oct_traits::is_valid_id(ov_e)), FOR_END_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (!oct_traits::is_valid_id(ov_E)), FOR_OUTBOUND_ID FOR_OCT); + + adl_static_assert( (octdiff_traits::is_valid_id(0), true) ); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_valid_id(odv_p)), FOR_ID_GT_0 FOR_INBOUND_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_valid_id(odv_f)), FOR_FIRST_ID FOR_NORMALIZED_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_valid_id(odv_F)), FOR_FIRST_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_valid_id(odv_l)), FOR_LAST_ID FOR_NORMALIZED_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_valid_id(odv_L)), FOR_LAST_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (!octdiff_traits::is_valid_id(odv_0)), FOR_ID_ZERO FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (!octdiff_traits::is_valid_id(odv_n)), FOR_ID_LT_0 FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (!octdiff_traits::is_valid_id(odv_e)), FOR_END_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (!octdiff_traits::is_valid_id(odv_E)), FOR_OUTBOUND_ID FOR_OCTDIFF); + } + + SECTION("is_id_in_range(id) [should be true for id == end, id == invalid and first <= id <= last]") { + + adl_static_assert( (oct_traits::is_id_in_range(0), true) ); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_id_in_range(0)), FOR_ID_ZERO FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_id_in_range(ov_f)), FOR_FIRST_ID FOR_NORMALIZED_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_id_in_range(ov_F)), FOR_FIRST_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_id_in_range(ov_l)), FOR_LAST_ID FOR_NORMALIZED_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_id_in_range(ov_L)), FOR_LAST_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_id_in_range(ov_p1)), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_id_in_range(ov_n1)), FOR_NEGATIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_id_in_range(ov_e)), FOR_END_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (!oct_traits::is_id_in_range(ov_E)), FOR_OUTBOUND_ID FOR_OCT); + + adl_static_assert( (octdiff_traits::is_id_in_range(0), true) ); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_id_in_range(0)), FOR_ID_ZERO FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_id_in_range(odv_f)), FOR_FIRST_ID FOR_NORMALIZED_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_id_in_range(odv_F)), FOR_FIRST_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_id_in_range(odv_l)), FOR_LAST_ID FOR_NORMALIZED_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_id_in_range(odv_L)), FOR_LAST_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_id_in_range(odv_p1)), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_id_in_range(odv_n1)), FOR_NEGATIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_id_in_range(odv_e)), FOR_END_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (!octdiff_traits::is_id_in_range(odv_n)), FOR_ID_LT_0 FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (!octdiff_traits::is_id_in_range(odv_E)), FOR_OUTBOUND_ID FOR_OCTDIFF); + } + + SECTION("is_positive_id(id) [should be true if id refers to a non-negated variable]") { + + adl_static_assert( (oct_traits::is_positive_id(0), true) ); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_positive_id(ov_p)), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (!oct_traits::is_positive_id(ov_n)), FOR_NEGATIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_positive_id(ov_p1)), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (!oct_traits::is_positive_id(ov_n1)), FOR_NEGATIVE_ID FOR_OCT); + + // octdiff cannot receive negative values for ids, because it's outside its working range + adl_static_assert( (octdiff_traits::is_positive_id(0), true) ); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_positive_id(odv_p)), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_positive_id(odv_p1)), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (!octdiff_traits::is_positive_id(odv_n1)), FOR_NEGATIVE_ID FOR_OCTDIFF); + } + + SECTION("is_negative_id(id) [should be true if id refers to a negated variable]") { + + adl_static_assert( (oct_traits::is_negative_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (!oct_traits::is_negative_id(ov_p)), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_negative_id(ov_n)), FOR_NEGATIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (!oct_traits::is_negative_id(ov_p1)), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( ( oct_traits::is_negative_id(ov_n1)), FOR_NEGATIVE_ID FOR_OCT); + + // octdiff cannot receive negative values for ids, because it's outside its working range + adl_static_assert( (octdiff_traits::is_negative_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (!octdiff_traits::is_negative_id(odv_p)), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (!octdiff_traits::is_negative_id(odv_p1)), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( ( octdiff_traits::is_negative_id(odv_n1)), FOR_NEGATIVE_ID FOR_OCTDIFF); + } + + // + // [ static functions ] + // + + SECTION("normalize_id(id) [should return the positive (non-negated) version of the variable id]") { + + adl_static_assert( (oct_traits::normalize_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (oct_traits::normalize_id(0) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::normalize_id(ov_e) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::normalize_id(ov_E) == 0), FOR_OUTBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::normalize_id(ov_p1) == ov_p1), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::normalize_id(ov_n1) == ov_p1), FOR_NEGATIVE_ID FOR_OCT); + + adl_static_assert( (octdiff_traits::normalize_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::normalize_id(0) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::normalize_id(odv_e) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::normalize_id(odv_E) == 0), FOR_OUTBOUND_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::normalize_id(odv_n) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::normalize_id(odv_p1) == odv_p1), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::normalize_id(odv_n1) == odv_p1), FOR_NEGATIVE_ID FOR_OCTDIFF); + } + + SECTION("negate_id(id) [returns the variable's opposite occurrence (negated)]") { + + adl_static_assert( (oct_traits::negate_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (oct_traits::negate_id(0) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::negate_id(ov_e) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::negate_id(ov_E) == 0), FOR_OUTBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::negate_id(ov_p1) == ov_n1), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::negate_id(ov_n1) == ov_p1), FOR_NEGATIVE_ID FOR_OCT); + + adl_static_assert( (octdiff_traits::negate_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::negate_id(0) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::negate_id(odv_e) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::negate_id(odv_E) == 0), FOR_OUTBOUND_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::negate_id(odv_n) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::negate_id(odv_p1) == odv_n1), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::negate_id(odv_n1) == odv_p1), FOR_NEGATIVE_ID FOR_OCTDIFF); + } + + SECTION("increment_id(id) [should increment the normalized value of id, resulting in a positive variable]") { + + adl_static_assert( (oct_traits::increment_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (oct_traits::increment_id(0) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::increment_id(ov_e) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::increment_id(ov_E) == 0), FOR_OUTBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::increment_id(ov_p1) == ov_p2), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::increment_id(ov_n1) == ov_p2), FOR_NEGATIVE_ID FOR_OCT); + // TODO Increment with offset != 1 + + adl_static_assert( (octdiff_traits::increment_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::increment_id(0) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::increment_id(odv_e) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::increment_id(odv_E) == 0), FOR_OUTBOUND_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::increment_id(odv_n) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::increment_id(odv_p1) == odv_p2), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::increment_id(odv_n1) == odv_p2), FOR_NEGATIVE_ID FOR_OCTDIFF); + // TODO Increment with offset != 1 + } + + SECTION("decrement_id(id) [should decrement the normalized value of id, resulting in a positive variable]") { + + adl_static_assert( (oct_traits::decrement_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (oct_traits::decrement_id(0) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::decrement_id(ov_e) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::decrement_id(ov_E) == 0), FOR_OUTBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::decrement_id(ov_p2) == ov_p1), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::decrement_id(ov_n2) == ov_p1), FOR_NEGATIVE_ID FOR_OCT); + // TODO Decrement with offset != 1 + + adl_static_assert( (octdiff_traits::decrement_id(0), true) ); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::decrement_id(0) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::decrement_id(odv_e) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::decrement_id(odv_E) == 0), FOR_OUTBOUND_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::decrement_id(odv_n) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::decrement_id(odv_p2) == odv_p1), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::decrement_id(odv_n2) == odv_p1), FOR_NEGATIVE_ID FOR_OCTDIFF); + // TODO Decrement with offset != 1 + } + + SECTION("id_equals(a, b) [should compare ids a and b, considering negated-ness, positive ids before]") { + + adl_static_assert( (oct_traits::id_equals(0, 0), true) ); + // TODO Implementation + + adl_static_assert( (octdiff_traits::id_equals(0, 0), true) ); + // TODO Implementation + } + + SECTION("id_compare(a, b) [should compare ids a and b, considering negated-ness, positive ids before]") { + // , ..., + adl_static_assert( (oct_traits::id_compare(0, 0), true) ); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_p1, ov_p1) == 0), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_n1, ov_n1) == 0), FOR_OCT) + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_0, ov_p1) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_0, ov_n1) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_E, ov_p1) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_E, ov_n1) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_p1, ov_e) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_n1, ov_e) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_p1, ov_n1) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_p1, ov_p2) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_p1, ov_n2) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_n1, ov_p2) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_n1, ov_n2) == -1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_p1, ov_0) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_n1, ov_0) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_p1, ov_E) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_n1, ov_E) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_e, ov_p1) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_e, ov_n1) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_n1, ov_p1) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_p2, ov_p1) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_p2, ov_n1) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_n2, ov_p1) == 1), FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_compare(ov_n2, ov_n1) == 1), FOR_OCT); + + adl_static_assert( (octdiff_traits::id_compare(0, 0), true) ); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_p1, odv_p1) == 0), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_n1, odv_n1) == 0), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_0, odv_p1) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_0, odv_n1) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_E, odv_p1) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_E, odv_n1) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_p1, odv_e) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_n1, odv_e) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_p1, odv_n1) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_p1, odv_p2) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_p1, odv_n2) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_n1, odv_p2) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_n1, odv_n2) == -1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_p1, odv_0) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_n1, odv_0) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_p1, odv_E) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_n1, odv_E) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_e, odv_p1) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_e, odv_n1) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_n1, odv_p1) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_p2, odv_p1) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_p2, odv_n1) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_n2, odv_p1) == 1), FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_compare(odv_n2, odv_n1) == 1), FOR_OCTDIFF); + } + + SECTION("id_sign(id) [should return -1 if id is negated or 1 if id is non-negated, else return 0]") { + + adl_static_assert( (oct_traits::id_sign(0), true) ); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_sign(0) == 0), FOR_ID_ZERO FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_sign(ov_e) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_sign(ov_E) == 0), FOR_OUTBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_sign(ov_p) == 1), FOR_ID_LT_0 FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_sign(ov_n) == -1), FOR_ID_GT_0 FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_sign(ov_p1) == 1), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_sign(ov_n1) == -1), FOR_NEGATIVE_ID FOR_OCT); + + adl_static_assert( (octdiff_traits::id_sign(0), true) ); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_sign(0) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_sign(odv_e) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_sign(odv_E) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_sign(odv_n) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_sign(odv_p1) == 1), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_sign(odv_n1) == -1), FOR_NEGATIVE_ID FOR_OCTDIFF); + } + + SECTION("id_sign_format(id) [should return the variable's default sign format") { + + adl_static_assert( (oct_traits::id_sign_format(0), true) ); + REQUIRE_SECTION_NOTHROW( (nullptr != oct_traits::id_sign_format(0) && string_view(oct_traits::id_sign_format(0)).empty()), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (nullptr != oct_traits::id_sign_format(ov_e) && string_view(oct_traits::id_sign_format(ov_e)).empty()), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (nullptr != oct_traits::id_sign_format(ov_E) && string_view(oct_traits::id_sign_format(ov_E)).empty()), FOR_OUTBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (nullptr != oct_traits::id_sign_format(ov_p1) && string_view(oct_traits::id_sign_format(ov_p1)) == oct_limits::positive_var_name_format), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (nullptr != oct_traits::id_sign_format(ov_n1) && string_view(oct_traits::id_sign_format(ov_n1)) == oct_limits::negative_var_name_format), FOR_NEGATIVE_ID FOR_OCT); + + adl_static_assert( (octdiff_traits::id_sign_format(0), true) ); + REQUIRE_SECTION_NOTHROW( (nullptr != octdiff_traits::id_sign_format(0) && string_view(octdiff_traits::id_sign_format(0)).empty()), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (nullptr != octdiff_traits::id_sign_format(odv_e) && string_view(octdiff_traits::id_sign_format(odv_e)).empty()), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (nullptr != octdiff_traits::id_sign_format(odv_E) && string_view(octdiff_traits::id_sign_format(odv_E)).empty()), FOR_OUTBOUND_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (nullptr != octdiff_traits::id_sign_format(odv_n) && string_view(octdiff_traits::id_sign_format(odv_n)).empty()), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (nullptr != oct_traits::id_sign_format(odv_p1) && string_view(octdiff_traits::id_sign_format(ov_p1)) == octdiff_limits::positive_var_name_format), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (nullptr != oct_traits::id_sign_format(odv_n1) && string_view(octdiff_traits::id_sign_format(ov_n1)) == octdiff_limits::negative_var_name_format), FOR_NEGATIVE_ID FOR_OCTDIFF); + } + + // + // [ static conversion functions ] + // + + SECTION("name_to_id(name) [" + "must return valid variable IDs for partially numeric octagon-only variable names, " + "or the invalid variable ID otherwise]" + ) { + adl_static_assert( (oct_traits::name_to_id(ov_p1_name), true) ); + adl_static_assert( (oct_traits::name_to_id(ov_p1_vname), true) ); + REQUIRE_SECTION_NOTHROW( (oct_traits::name_to_id(ov_p1_name) == ov_p1), FOR_POSITIVE_ID FOR_STRINGP_NAME FOR_OCT ); + REQUIRE_SECTION_NOTHROW( (oct_traits::name_to_id(ov_n1_name) == ov_n1), FOR_NEGATIVE_ID FOR_STRINGP_NAME FOR_OCT ); + REQUIRE_SECTION_NOTHROW( (oct_traits::name_to_id(ov_x_name) == oct_limits::invalid_var_id), FOR_INVALID_ID FOR_STRINGP_NAME FOR_OCT ); + REQUIRE_SECTION_NOTHROW( (oct_traits::name_to_id(ov_p1_vname) == ov_p1), FOR_POSITIVE_ID FOR_STRINGV_NAME FOR_OCT ); + REQUIRE_SECTION_NOTHROW( (oct_traits::name_to_id(ov_n1_vname) == ov_n1), FOR_NEGATIVE_ID FOR_STRINGV_NAME FOR_OCT ); + REQUIRE_SECTION_NOTHROW( (oct_traits::name_to_id(ov_x_vname) == oct_limits::invalid_var_id), FOR_INVALID_ID FOR_STRINGV_NAME FOR_OCT ); + REQUIRE_SECTION( (oct_traits::name_to_id(ov_p1_sname) == ov_p1), FOR_POSITIVE_ID FOR_STRING_NAME FOR_OCT ); + REQUIRE_SECTION( (oct_traits::name_to_id(ov_n1_sname) == ov_n1), FOR_NEGATIVE_ID FOR_STRING_NAME FOR_OCT ); + REQUIRE_SECTION( (oct_traits::name_to_id(ov_x_sname) == oct_limits::invalid_var_id), FOR_INVALID_ID FOR_STRING_NAME FOR_OCT ); + + // NOTE Conversion from octdiff variable name to octdiff variable ID is prohibited atm. + } + + SECTION("arithmetic_to_range(value) [" + "must return a valid variable ID if the value passed is within the variable ID type's bounds (e.g. first-end), " + "else invalid_var_id" + ) { + adl_static_assert( (oct_traits::arithmetic_to_range(0), true) ); + // TODO Implementation + + adl_static_assert( (octdiff_traits::arithmetic_to_range(0), true) ); + // TODO Implementation + } + + SECTION("arithmetic_to_valid(value) [" + "must return a valid variable ID if the value passed is a valid variable ID (e.g. first-last), " + "else invalid_var_id]" + ) { + adl_static_assert( (oct_traits::arithmetic_to_valid(0), true) ); + // TODO Implementation + + adl_static_assert( (octdiff_traits::arithmetic_to_valid(0), true) ); + // TODO Implementation + } + + SECTION("id_to_arithmetic(id) [" + "should return the id only if it's within bounds, " + "else return default-constructed value]" + ) { + + constexpr auto fallback_value_ = 9999.9f; + + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_arithmetic(ov_0, fallback_value_) == oct_limits::invalid_var_id), FOR_ID_ZERO FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_arithmetic(ov_E, fallback_value_) == fallback_value_), FOR_OUTBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_arithmetic(ov_e, fallback_value_) == ov_e), FOR_END_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_arithmetic(ov_p1, fallback_value_) == ov_p1), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_arithmetic(ov_n1, fallback_value_) == ov_n1), FOR_NEGATIVE_ID FOR_OCT); + + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_arithmetic(odv_0, fallback_value_) == octdiff_limits::invalid_var_id), FOR_ID_ZERO FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_arithmetic(odv_E, fallback_value_) == fallback_value_), FOR_OUTBOUND_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_arithmetic(odv_n, fallback_value_) == fallback_value_), FOR_ID_LT_0 FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_arithmetic(odv_e, fallback_value_) == odv_e), FOR_END_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_arithmetic(odv_p1, fallback_value_) == odv_p1), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_arithmetic(odv_n1, fallback_value_) == odv_n1), FOR_NEGATIVE_ID FOR_OCTDIFF); + } + + SECTION("id_to_range(id) [" + "must return the provided variable ID if the ID itself is a variable ID within bounds (e.g. first-end), " + "else return invalid_var_id]" + ) { + adl_static_assert( (oct_traits::id_to_range(0), true) ); + // TODO Implementation + + adl_static_assert( (octdiff_traits::id_to_range(0), true) ); + // TODO Implementation + } + + SECTION("id_to_valid(id) [" + "must return the provided variable ID if the ID itself is a valid variable ID (e.g. first-last), " + "else return invalid_var_id]" + ) { + adl_static_assert( (oct_traits::id_to_valid(0), true) ); + // TODO Implementation + + adl_static_assert( (octdiff_traits::id_to_valid(0), true) ); + // TODO Implementation + } + + SECTION("id_to_index(id) [should return a size_t value suitable for indexing a memory region (e.g. array)]") { + // invalid values result in undefined behaviour + + adl_static_assert( (oct_traits::id_to_index(0), true) ); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_index(ov_p1) == ov_p1_index), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_index(ov_n1) == ov_n1_index), FOR_NEGATIVE_ID FOR_OCT); + + adl_static_assert( (octdiff_traits::id_to_index(0), true) ); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_index(odv_p1) == odv_p1_index), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_index(odv_n1) == odv_n1_index), FOR_NEGATIVE_ID FOR_OCTDIFF); + } + + SECTION("id_to_counterpart(id) [should return id's counterpart variable in the opposite domain]") { + + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_counterpart(0) == 0), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_counterpart(ov_E) == 0), FOR_OUTBOUND_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_counterpart(ov_e) == odv_e), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_counterpart(ov_p1) == odv_p1), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_counterpart(ov_n1) == odv_n1), FOR_NEGATIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_counterpart(ov_p3) == odv_p3), FOR_POSITIVE_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (oct_traits::id_to_counterpart(ov_n3) == odv_n3), FOR_NEGATIVE_ID FOR_OCT); + + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_counterpart(0) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_counterpart(odv_E) == 0), FOR_OUTBOUND_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_counterpart(odv_n) == 0), FOR_INVALID_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_counterpart(odv_e) == ov_e), FOR_INVALID_ID FOR_OCT); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_counterpart(odv_p1) == ov_p1), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_counterpart(odv_n1) == ov_n1), FOR_NEGATIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_counterpart(odv_p3) == ov_p3), FOR_POSITIVE_ID FOR_OCTDIFF); + REQUIRE_SECTION_NOTHROW( (octdiff_traits::id_to_counterpart(odv_n3) == ov_n3), FOR_NEGATIVE_ID FOR_OCTDIFF); + } + + SECTION("id_to_normal_oct(id) [should return a normalized oct-space variable ID]") { + + adl_static_assert( (oct_traits::id_to_normal_oct(0), true) ); + // TODO Implementation + + adl_static_assert( (octdiff_traits::id_to_normal_oct(0), true) ); + // TODO Implementation + } + + SECTION("id_to_name(id) [should return a name representation of a variable ID, considering the sign]") { + + // TODO Implementation + } +} \ No newline at end of file