diff --git a/src/core_test/codec/common_code.cc b/src/core_test/codec/common_code.cc index 8c4e9a5..e6daf3a 100644 --- a/src/core_test/codec/common_code.cc +++ b/src/core_test/codec/common_code.cc @@ -16,7 +16,7 @@ using klotski::codec::CommonCode; using klotski::cases::AllCases; using klotski::cases::ALL_CASES_NUM_; -TEST(CommonCode, validity) { +TEST(CommonCode, basic) { EXPECT_NE(CommonCode::check(0x3'A9'BF'0C'00), true); // invalid 2x2 block EXPECT_NE(CommonCode::check(0x1'D9'BF'0C'00), true); // invalid block range EXPECT_NE(CommonCode::check(0x1'A9'BF'FC'00), true); // less than 2 space @@ -26,15 +26,15 @@ TEST(CommonCode, validity) { EXPECT_FALSE(CommonCode::from_string("0123456789").has_value()); // length > 9 EXPECT_FALSE(CommonCode::from_string("123J432A9").has_value()); // with invalid `J` - EXPECT_TRUE(CommonCode::unsafe_create(0x1'A9BF0C00).is_horizontal_mirror()); - EXPECT_FALSE(CommonCode::unsafe_create(0x4'FEA13400).is_horizontal_mirror()); - EXPECT_FALSE(CommonCode::unsafe_create(0x1'A9BF0C00).is_vertical_mirror()); - EXPECT_FALSE(CommonCode::unsafe_create(0x4'FEA13400).is_vertical_mirror()); + EXPECT_FALSE(CommonCode::unsafe_create(TEST_MIRROR_1).is_vertical_mirror()); + EXPECT_TRUE(CommonCode::unsafe_create(TEST_MIRROR_1).is_horizontal_mirror()); + EXPECT_EQ(CommonCode::unsafe_create(TEST_MIRROR_1).to_vertical_mirror(), TEST_MIRROR_1_VM); + EXPECT_EQ(CommonCode::unsafe_create(TEST_MIRROR_1).to_horizontal_mirror(), TEST_MIRROR_1_HM); - EXPECT_EQ(CommonCode::unsafe_create(0x1'A9BF0C00).to_horizontal_mirror(), 0x1'A9BF0C00); - EXPECT_EQ(CommonCode::unsafe_create(0x4'FEA13400).to_horizontal_mirror(), 0x6'BFA47000); - EXPECT_EQ(CommonCode::unsafe_create(0x1'A9BF0C00).to_vertical_mirror(), 0xD'C3BE6800); - EXPECT_EQ(CommonCode::unsafe_create(0x4'FEA13400).to_vertical_mirror(), 0x8'346AFC00); + EXPECT_FALSE(CommonCode::unsafe_create(TEST_MIRROR_2).is_vertical_mirror()); + EXPECT_FALSE(CommonCode::unsafe_create(TEST_MIRROR_2).is_horizontal_mirror()); + EXPECT_EQ(CommonCode::unsafe_create(TEST_MIRROR_2).to_vertical_mirror(), TEST_MIRROR_2_VM); + EXPECT_EQ(CommonCode::unsafe_create(TEST_MIRROR_2).to_horizontal_mirror(), TEST_MIRROR_2_HM); #ifndef KLSK_NDEBUG std::ostringstream out; @@ -43,6 +43,20 @@ TEST(CommonCode, validity) { #endif } +TEST(CommonCode, exporter) { + auto common_code = CommonCode::unsafe_create(TEST_C_CODE); + EXPECT_EQ(common_code.unwrap(), TEST_C_CODE); + EXPECT_EQ(common_code.to_string(), TEST_C_CODE_STR); + EXPECT_EQ(common_code.to_raw_code(), TEST_R_CODE); + EXPECT_EQ(common_code.to_short_code(), TEST_S_CODE); + + auto code_shorten = common_code.to_string(true); + EXPECT_EQ(CommonCode::from_string(code_shorten), common_code); + + auto code_normal = common_code.to_string(false); + EXPECT_EQ(CommonCode::from_string(code_normal), common_code); +} + TEST(CommonCode, operators) { auto common_code = CommonCode::unsafe_create(TEST_C_CODE); EXPECT_EQ(static_cast(common_code), TEST_C_CODE); // uint64_t cast @@ -80,21 +94,7 @@ TEST(CommonCode, operators) { EXPECT_GT(CommonCode::unsafe_create(TEST_C_CODE + 1), common_code); // CommonCode > CommonCode } -TEST(CommonCode, exporter) { - auto common_code = CommonCode::unsafe_create(TEST_C_CODE); - EXPECT_EQ(common_code.unwrap(), TEST_C_CODE); - EXPECT_EQ(common_code.to_string(), TEST_C_CODE_STR); - EXPECT_EQ(common_code.to_raw_code(), TEST_R_CODE); - EXPECT_EQ(common_code.to_short_code(), TEST_S_CODE); - - auto code_shorten = common_code.to_string(true); - EXPECT_EQ(CommonCode::from_string(code_shorten), common_code); - - auto code_normal = common_code.to_string(false); - EXPECT_EQ(CommonCode::from_string(code_normal), common_code); -} - -TEST(CommonCode, initializate) { +TEST(CommonCode, initialize) { auto raw_code = RawCode::unsafe_create(TEST_R_CODE); auto short_code = ShortCode::unsafe_create(TEST_S_CODE); auto common_code = CommonCode::unsafe_create(TEST_C_CODE); @@ -147,65 +147,73 @@ TEST(CommonCode, initializate) { EXPECT_EQ(CommonCode::from_short_code(TEST_S_CODE_STR), TEST_C_CODE); } -// TODO: global test function -// -> check -// -> string_decode / string_encode / string_encode_shorten -// -> check_mirror / get_vertical_mirror / get_horizontal_mirror - TEST(CommonCode, code_verify) { - BS::thread_pool pool; - pool.detach_sequence(0, 16, [](const uint64_t head) { - for (auto range : AllCases::instance().fetch()[head]) { - auto code = head << 32 | range; - EXPECT_TRUE(CommonCode::check(code)); // verify all cases + common_code_parallel([](std::span codes) { + for (auto code : codes) { + EXPECT_TRUE(CommonCode::check(code.unwrap())); // verify all cases } }); - pool.wait(); } -TEST(CommonCode, code_string) { - auto test_func = [](CommonCode code) { - auto code_shorten = code.to_string(true); // with shorten - auto code_normal = code.to_string(false); // without shorten - EXPECT_TRUE(code_normal.starts_with(code_shorten)); - - EXPECT_LE(code_shorten.size(), 9); // length -> (0, 9] - EXPECT_NE(code_shorten.size(), 0); - if (code != 0) { // skip special code string `0` - EXPECT_NE(code_shorten.back(), '0'); +TEST(CommonCode, code_mirror) { + common_code_parallel([](std::span codes) { + for (auto code : codes) { + const auto mirror_v = code.to_vertical_mirror(); + EXPECT_TRUE(CommonCode::check(mirror_v.unwrap())); + EXPECT_EQ(mirror_v.to_vertical_mirror(), code); + EXPECT_FALSE(mirror_v.is_vertical_mirror()); // not exist + EXPECT_NE(mirror_v, code); + + const auto mirror_h = code.to_horizontal_mirror(); + EXPECT_TRUE(CommonCode::check(mirror_h.unwrap())); + EXPECT_EQ(mirror_h.to_horizontal_mirror(), code); + if (mirror_h.is_horizontal_mirror()) { + EXPECT_EQ(mirror_h, code); + } else { + EXPECT_NE(mirror_h, code); + } } - EXPECT_EQ(CommonCode::from_string(code_shorten), code); // test upper cases - std::transform(code_shorten.begin(), code_shorten.end(), code_shorten.begin(), ::tolower); - EXPECT_EQ(CommonCode::from_string(code_shorten), code); // test lower cases + }); +} - EXPECT_EQ(code_normal.size(), 9); // length = 9 - for (auto c : code_normal) { - EXPECT_TRUE((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')); - } - EXPECT_EQ(CommonCode::from_string(code_normal), code); // test upper cases - std::transform(code_normal.begin(), code_normal.end(), code_normal.begin(), ::tolower); - EXPECT_EQ(CommonCode::from_string(code_normal), code); // test lower cases - }; +TEST(CommonCode, code_string) { + common_code_parallel([](std::span codes) { + for (auto code : codes) { + auto code_shorten = code.to_string(true); // with shorten + auto code_normal = code.to_string(false); // without shorten + EXPECT_TRUE(code_normal.starts_with(code_shorten)); + EXPECT_EQ(std::format("{:09X}", code.unwrap()), code_normal); + + EXPECT_LE(code_shorten.size(), 9); // length -> (0, 9] + EXPECT_NE(code_shorten.size(), 0); + if (code != 0) { // skip special code string `0` + EXPECT_NE(code_shorten.back(), '0'); + } + EXPECT_EQ(CommonCode::from_string(code_shorten), code); // test upper cases + std::transform(code_shorten.begin(), code_shorten.end(), code_shorten.begin(), ::tolower); + EXPECT_EQ(CommonCode::from_string(code_shorten), code); // test lower cases - BS::thread_pool pool; - pool.detach_sequence(0, 16, [&test_func](const uint64_t head) { - for (auto range : AllCases::instance().fetch()[head]) { - test_func(CommonCode::unsafe_create(head << 32 | range)); + EXPECT_EQ(code_normal.size(), 9); // length = 9 + for (auto c : code_normal) { + EXPECT_TRUE((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')); + } + EXPECT_EQ(CommonCode::from_string(code_normal), code); // test upper cases + std::transform(code_normal.begin(), code_normal.end(), code_normal.begin(), ::tolower); + EXPECT_EQ(CommonCode::from_string(code_normal), code); // test lower cases } }); - pool.wait(); } TEST(CommonCode, DISABLED_global_verify) { BS::thread_pool pool; - auto futures = pool.submit_blocks(0ULL, 0x10'0000'0000ULL, [](uint64_t start, uint64_t end) { - std::vector archive; + auto futures = pool.submit_blocks(0ULL, 0x10'0000'0000ULL, [](auto start, auto end) { + std::vector codes; for (uint64_t common_code = start; common_code < end; ++common_code) { // brute-force search if (CommonCode::check(common_code)) { - archive.emplace_back(common_code); // found valid common code + codes.emplace_back(common_code); // found valid common code } } - return archive; + return codes; }, 0x1000); // split as 4096 pieces std::vector result; diff --git a/src/core_test/codec/helper/codec.cc b/src/core_test/codec/helper/codec.cc index 4b59f9a..19a2fc4 100644 --- a/src/core_test/codec/helper/codec.cc +++ b/src/core_test/codec/helper/codec.cc @@ -3,14 +3,15 @@ #include "codec.h" void head_parallel(std::function &&func) { - BS::thread_pool pool; - // TODO: skip invalid head - pool.detach_sequence(0, 16, [func = std::move(func)](const uint64_t head) { - if (head == 3 || head == 7 || head == 11 || head == 15) { - return; - } - func(head); + constexpr auto heads = std::to_array({ + 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14 }); + BS::thread_pool pool; + for (auto head : heads) { + pool.detach_task([head, &func] { + func(head); + }); + } pool.wait(); } @@ -25,3 +26,21 @@ std::vector all_common_codes() { } return common_codes; } + +void common_code_parallel(std::function)> &&func) { + + static auto codes = AllCases::instance().fetch().codes(); + + BS::thread_pool pool; + + // TODO: enhance performance + + pool.detach_blocks((uint64_t)0, codes.size(), [func = std::move(func)](auto start, auto end) { + + func(std::span {codes.data() + start, end - start}); + + }, 16); + + pool.wait(); + +} diff --git a/src/core_test/codec/helper/codec.h b/src/core_test/codec/helper/codec.h index 4d55c9c..e836eb0 100644 --- a/src/core_test/codec/helper/codec.h +++ b/src/core_test/codec/helper/codec.h @@ -1,14 +1,23 @@ #pragma once +#include #include #include "all_cases/all_cases.h" +#include "short_code/short_code.h" +#include "common_code/common_code.h" using klotski::cases::AllCases; +using klotski::codec::ShortCode; +using klotski::codec::CommonCode; using klotski::cases::ALL_CASES_NUM_; /// Build all valid CommonCodes. std::vector all_common_codes(); /// Spawn all valid klotski headers in parallel. -void head_parallel(std::function &&func); +void head_parallel(std::function &&func); + +void short_code_parallel(std::function)> &&func); + +void common_code_parallel(std::function)> &&func); diff --git a/src/core_test/codec/helper/sample.h b/src/core_test/codec/helper/sample.h index ee03abf..bba6be3 100644 --- a/src/core_test/codec/helper/sample.h +++ b/src/core_test/codec/helper/sample.h @@ -5,32 +5,40 @@ // ----------------------------------------------------------------------------------------- // -/// Valid klotski RawCode +/// Valid klotski RawCode sample. constexpr uint64_t TEST_R_CODE = 0x0603'EDF5'CAFF'F5E2; -/// Valid klotski ShortCode +/// Valid klotski ShortCode sample. constexpr uint32_t TEST_S_CODE = 4091296; -const std::string TEST_S_CODE_STR = "4WVE1"; // TODO: using `std::string_view` -#define TEST_S_CODE_STR_RV std::string(TEST_S_CODE_STR) // TODO: remove r-value +constexpr std::string_view TEST_S_CODE_STR = "4WVE1"; -/// Valid klotski CommonCode +/// Valid klotski CommonCode sample. constexpr uint64_t TEST_C_CODE = 0x1'A9BF'0C00; -const std::string TEST_C_CODE_STR = "1A9BF0C00"; -#define TEST_C_CODE_STR_RV std::string(TEST_C_CODE_STR) // TODO: remove r-value +constexpr std::string_view TEST_C_CODE_STR = "1A9BF0C00"; // ----------------------------------------------------------------------------------------- // -/// Invalid klotski RawCode +/// Invalid klotski RawCode sample. constexpr uint64_t TEST_R_CODE_ERR = 0x1603'ED00'CAFF'F5E2; -/// Invalid klotski ShortCode +/// Invalid klotski ShortCode sample. constexpr uint32_t TEST_S_CODE_ERR = 1234564323; -const std::string TEST_S_CODE_STR_ERR = "ZZZZZZ"; -#define TEST_S_CODE_STR_ERR_RV std::string(TEST_S_CODE_STR_ERR) // TODO: remove r-value +constexpr std::string_view TEST_S_CODE_STR_ERR = "ZZZZZZ"; -/// Invalid klotski CommonCode +/// Invalid klotski CommonCode sample. constexpr uint64_t TEST_C_CODE_ERR = 0x3'A9BF'0C00; -const static std::string TEST_C_CODE_STR_ERR = "0123456789"; -#define TEST_C_CODE_STR_ERR_RV std::string(TEST_C_CODE_STR_ERR) // TODO: remove r-value +constexpr std::string_view TEST_C_CODE_STR_ERR = "0123456789"; + +// ----------------------------------------------------------------------------------------- // + +/// CommonCode with horizontal symmetry. +constexpr uint64_t TEST_MIRROR_1 = 0x1A9BF0C00; +constexpr uint64_t TEST_MIRROR_1_VM = 0xDC3BE6800; // vertical mirror +constexpr uint64_t TEST_MIRROR_1_HM = 0x1A9BF0C00; // horizontal mirror + +/// CommonCode without vertical or horizontal symmetry. +constexpr uint64_t TEST_MIRROR_2 = 0x4FEA13400; +constexpr uint64_t TEST_MIRROR_2_VM = 0x8346AFC00; // vertical mirror +constexpr uint64_t TEST_MIRROR_2_HM = 0x6BFA47000; // horizontal mirror // ----------------------------------------------------------------------------------------- //