From cc2042f9cfc6431406e1fae71d2bef9fa68a808d Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Sat, 8 Apr 2023 15:02:22 +0800 Subject: [PATCH] update: enhance CommonCode module --- src/klotski_core/common_code/common_code.cc | 89 +++++------ src/klotski_core/common_code/common_code.h | 154 ++++++++++---------- src/klotski_core/common_code/convert.cc | 8 +- src/klotski_core/common_code/serialize.cc | 16 +- test/codec/short_code.cc | 4 +- 5 files changed, 125 insertions(+), 146 deletions(-) diff --git a/src/klotski_core/common_code/common_code.cc b/src/klotski_core/common_code/common_code.cc index 4d4cd16..57ceeb6 100644 --- a/src/klotski_core/common_code/common_code.cc +++ b/src/klotski_core/common_code/common_code.cc @@ -1,90 +1,65 @@ #include "common.h" #include "common_code.h" -using klotski::CommonCode; - -namespace std { - template<> - struct hash { - std::size_t operator()(const klotski::CommonCode &c) const { - return std::hash()(c.unwrap()); - } - }; - - template<> - struct equal_to { - bool operator()(const klotski::CommonCode &c1, const klotski::CommonCode &c2) const { - return c1.unwrap() == c2.unwrap(); - } - }; -} - namespace klotski { - bool CommonCode::operator==(const CommonCode &common_code) const noexcept { - return this->code == common_code.code; - } - bool CommonCode::operator!=(const CommonCode &common_code) const noexcept { - return this->code != common_code.code; - } +using Common::range_reverse; - std::ostream& operator<<(std::ostream &out, const CommonCode &self) { - char str[10]; - sprintf(str, "%09lX", self.code); - out << str; - return out; - } +bool CommonCode::valid() const noexcept { + return CommonCode::check(code_); } -namespace klotski { - bool CommonCode::valid() const noexcept { - return CommonCode::check(code); - } +CommonCode CommonCode::create(uint64_t common_code) { + return CommonCode(common_code); // create from uint64_t +} - CommonCode CommonCode::create(uint64_t common_code) { - return CommonCode(common_code); // create from uint64_t - } +CommonCode CommonCode::unsafe_create(uint64_t common_code) noexcept { // create without check + auto tmp = CommonCode(); // init directly + tmp.code_ = common_code; + return tmp; +} - CommonCode CommonCode::unsafe_create(uint64_t common_code) noexcept { // create without check - auto tmp = CommonCode(); // init directly - tmp.code = common_code; - return tmp; +CommonCode::CommonCode(uint64_t common_code) { + if (!CommonCode::check(common_code)) { // check input common code + throw klotski::CommonCodeException("common code invalid"); } + code_ = common_code; +} - CommonCode::CommonCode(uint64_t common_code) { - if (!CommonCode::check(common_code)) { // check input common code - throw klotski::CommonCodeException("common code invalid"); - } - code = common_code; - } +std::ostream& operator<<(std::ostream &out, const CommonCode &self) { + char str[10]; + sprintf(str, "%09lX", self.code_); + out << str; + return out; } bool CommonCode::check(uint64_t common_code) noexcept { // whether common code is valid - /// M_1x1 M_1x2 M_2x1 M_2x2 - /// 1 0 0 0 1 1 0 0 1 0 0 0 1 1 0 0 - /// 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 - /// ... ... ... ... + /// M_1x1 | M_1x2 | M_2x1 | M_2x2 + /// 1 0 0 0 | 1 1 0 0 | 1 0 0 0 | 1 1 0 0 + /// 0 0 0 0 | 0 0 0 0 | 1 0 0 0 | 1 1 0 0 + /// ... | ... | ... | ... constexpr uint32_t M_1x1 = 0b1; constexpr uint32_t M_1x2 = 0b11; constexpr uint32_t M_2x1 = 0b10001; constexpr uint32_t M_2x2 = 0b110011; - /// 2x2 address check (high 32-bits) + /// 2x2 address check (high 32-bit) uint32_t head = common_code >> 32; if (head >= 16 || (head & 0b11) == 0b11) { // check 2x2 block address return false; // invalid common code } - /// check range status (low 32-bits) + /// check range status (low 32-bit) int space_num = 0; uint32_t mask = M_2x2 << head; // fill 2x2 block - auto range = Common::range_reverse((uint32_t)common_code); - for (int addr = 0;; range >>= 2) { // traverse every 2-bits + auto range = range_reverse((uint32_t)common_code); // extract range + + for (int addr = 0;; range >>= 2) { // traverse every 2-bit while ((mask >> addr) & 0b1) { ++addr; // search next unfilled block } if (addr >= 20) { - return !range && space_num > 1; // empty range and >= 2 space + return !range && space_num > 1; // range is empty and space num >= 2 } switch (range & 0b11) { case 0b00: /// space @@ -109,3 +84,5 @@ bool CommonCode::check(uint64_t common_code) noexcept { // whether common code i } } } + +} // namespace klotski diff --git a/src/klotski_core/common_code/common_code.h b/src/klotski_core/common_code/common_code.h index fd05e46..e89ed3a 100644 --- a/src/klotski_core/common_code/common_code.h +++ b/src/klotski_core/common_code/common_code.h @@ -1,11 +1,11 @@ #pragma once /// CommonCode is a generic klotski encoding that records an valid case using -/// 36-bits lengths, and stored in a `uint64_t`. +/// 36-bit lengths, and stored in a `uint64_t`. /// Since there is only one `2x2` block, it is encoded separately. Its upper /// left corner is called `head`, it has 12 possible positions and is encoded -/// using 4-bits length (0 ~ 15). +/// using 4-bit length (0 ~ 15). /// /// 00 01 02 03 /// 04 05 06 07 00 01 02 @@ -14,22 +14,22 @@ /// 16 17 18 19 12 13 14 /// Treat spaces as special blocks, there can be four kinds of blocks in total, -/// namely `space`, `1x2`, `2x1`, `1x1`. Each of them is represented by 2-bits, +/// namely `space`, `1x2`, `2x1`, `1x1`. Each of them is represented by 2-bit, /// which are `00` `01` `10` `11`. Arrange them according to their position and /// size, and we can get a binary sequence. /// 2x2 -> # # | 2x1 -> # | 1x2 -> # # | 1x1 -> # /// # # | # | | -/// This sequence can have up to 16 blocks, aka 32-bits in length. Therefore, in +/// This sequence can have up to 16 blocks, aka 32-bit in length. Therefore, in /// order to be compatible with all cases, the length of this part of the code -/// is set to 32-bits. In addition, for the convenience of reading, it is -/// stipulated that the sequence starts from the high bit, and the remaining bits -/// should be filled with `0`. +/// is set to 32-bit. In addition, for the convenience of reading, it is stipulated +/// that the sequence starts from the high bit, and the remaining bits should be +/// filled with `0`. -/// Putting the content of the `head` in the upper 4-bits, and the lower 32-bits -/// to store the sequence content, a 36-bits length code can be obtained, which +/// Putting the content of the `head` in the upper 4-bit, and the lower 32-bit +/// to store the sequence content, a 36-bit length code can be obtained, which /// corresponds to any valid layout one-to-one. When CommonCode is converted into -/// a string, just directly export the hexadecimal data, and get a 9-bits string +/// a string, just directly export the hexadecimal data, and get a 9-bit string /// encoding. Characters are not case-sensitive, but it is recommended to use /// uppercase letters. In addition, the last `0` of the string is allowed to be /// omitted, and it can be completed to 9 digits when decoding, but note that if @@ -59,69 +59,71 @@ #include "short_code.h" namespace klotski { - class RawCode; // import for convert interface - class ShortCode; - - class CommonCodeException : public std::runtime_error { - public: - CommonCodeException() : std::runtime_error("invalid common code") {} - explicit CommonCodeException(const std::string &msg) : std::runtime_error(msg) {} - ~CommonCodeException() noexcept override = default; - }; - - class CommonCode { - uint64_t code; - CommonCode() = default; // unsafe initialize - - static inline uint64_t string_decode(const std::string &common_code); - static inline std::string string_encode(uint64_t common_code, bool shorten) noexcept; - - public: - /// CommonCode validity check - bool valid() const noexcept; - static bool check(uint64_t common_code) noexcept; - - /// Operators of CommonCode - bool operator==(const CommonCode &common_code) const noexcept; - bool operator!=(const CommonCode &common_code) const noexcept; - constexpr explicit operator uint64_t() const noexcept { return code; } - friend std::ostream& operator<<(std::ostream &out, const CommonCode &self); - - /// Export functions - RawCode to_raw_code() const noexcept; - ShortCode to_short_code() const noexcept; - std::string to_string(bool shorten = false) const; - constexpr uint64_t unwrap() const noexcept { return code; } - - /// CommonCode constructors - explicit CommonCode(uint64_t common_code); - explicit CommonCode(RawCode &&raw_code) noexcept; - explicit CommonCode(ShortCode &&short_code) noexcept; - explicit CommonCode(std::string &&common_code); - explicit CommonCode(const RawCode &raw_code) noexcept; - explicit CommonCode(const ShortCode &short_code) noexcept; - explicit CommonCode(const std::string &common_code); - - /// Static initialization - static CommonCode create(uint64_t common_code); - static CommonCode unsafe_create(uint64_t common_code) noexcept; - - static CommonCode from_string(std::string &&common_code); - static CommonCode from_string(const std::string &common_code); - - static CommonCode from_raw_code(uint64_t raw_code); - static CommonCode from_raw_code(RawCode &&raw_code) noexcept; - static CommonCode from_raw_code(const RawCode &raw_code) noexcept; - - static CommonCode from_short_code(uint32_t short_code); - static CommonCode from_short_code(ShortCode &&short_code) noexcept; - static CommonCode from_short_code(std::string &&short_code); - static CommonCode from_short_code(const ShortCode &short_code) noexcept; - static CommonCode from_short_code(const std::string &short_code); - }; - - inline bool operator==(uint64_t c1, const CommonCode &c2) noexcept { return c1 == c2.unwrap(); } - inline bool operator!=(uint64_t c1, const CommonCode &c2) noexcept { return c1 != c2.unwrap(); } - inline bool operator==(const CommonCode &c1, uint64_t c2) noexcept { return c1.unwrap() == c2; } - inline bool operator!=(const CommonCode &c1, uint64_t c2) noexcept { return c1.unwrap() != c2; } -} + +class RawCode; +class ShortCode; + +class CommonCodeException : public std::runtime_error { +public: + CommonCodeException() : std::runtime_error("invalid common code") {} + explicit CommonCodeException(const std::string &msg) : std::runtime_error(msg) {} + ~CommonCodeException() noexcept override = default; +}; + +class CommonCode { + uint64_t code_; + CommonCode() = default; // unsafe initialize + + static inline uint64_t string_decode(const std::string &common_code); + static inline std::string string_encode(uint64_t common_code, bool shorten) noexcept; + +public: + /// Validity check + bool valid() const noexcept; + static bool check(uint64_t common_code) noexcept; + + /// Operators of CommonCode + constexpr explicit operator uint64_t() const noexcept { return code_; } + friend std::ostream& operator<<(std::ostream &out, const CommonCode &self); + + /// Export functions + RawCode to_raw_code() const noexcept; + ShortCode to_short_code() const noexcept; + std::string to_string(bool shorten = false) const noexcept; + constexpr uint64_t unwrap() const noexcept { return code_; } + + /// CommonCode constructors + explicit CommonCode(uint64_t common_code); + explicit CommonCode(RawCode &&raw_code) noexcept; + explicit CommonCode(ShortCode &&short_code) noexcept; + explicit CommonCode(std::string &&common_code); + explicit CommonCode(const RawCode &raw_code) noexcept; + explicit CommonCode(const ShortCode &short_code) noexcept; + explicit CommonCode(const std::string &common_code); + + /// Static initialization + static CommonCode create(uint64_t common_code); + static CommonCode unsafe_create(uint64_t common_code) noexcept; + + static CommonCode from_string(std::string &&common_code); + static CommonCode from_string(const std::string &common_code); + + static CommonCode from_raw_code(uint64_t raw_code); + static CommonCode from_raw_code(RawCode &&raw_code) noexcept; + static CommonCode from_raw_code(const RawCode &raw_code) noexcept; + + static CommonCode from_short_code(uint32_t short_code); + static CommonCode from_short_code(ShortCode &&short_code) noexcept; + static CommonCode from_short_code(std::string &&short_code); + static CommonCode from_short_code(const ShortCode &short_code) noexcept; + static CommonCode from_short_code(const std::string &short_code); +}; + +inline bool operator==(uint64_t c1, const CommonCode &c2) noexcept { return c1 == c2.unwrap(); } +inline bool operator!=(uint64_t c1, const CommonCode &c2) noexcept { return c1 != c2.unwrap(); } +inline bool operator==(const CommonCode &c1, uint64_t c2) noexcept { return c1.unwrap() == c2; } +inline bool operator!=(const CommonCode &c1, uint64_t c2) noexcept { return c1.unwrap() != c2; } +inline bool operator==(const CommonCode &c1, const CommonCode &c2) noexcept { return c1.unwrap() == c2.unwrap(); } +inline bool operator!=(const CommonCode &c1, const CommonCode &c2) noexcept { return c1.unwrap() != c2.unwrap(); } + +} // namespace klotski diff --git a/src/klotski_core/common_code/convert.cc b/src/klotski_core/common_code/convert.cc index e9b6a5d..340c521 100644 --- a/src/klotski_core/common_code/convert.cc +++ b/src/klotski_core/common_code/convert.cc @@ -31,21 +31,21 @@ CommonCode CommonCode::from_raw_code(const RawCode &raw_code) noexcept { } CommonCode::CommonCode(RawCode &&raw_code) noexcept { - code = raw_code.to_common_code().code; // convert from raw code + code_ = raw_code.to_common_code().code_; // convert from raw code } CommonCode::CommonCode(const RawCode &raw_code) noexcept { - code = raw_code.to_common_code().code; // convert from raw code + code_ = raw_code.to_common_code().code_; // convert from raw code } /// ------------------------- ShortCode to CommonCode ------------------------- CommonCode::CommonCode(ShortCode &&short_code) noexcept { - code = short_code.to_common_code().code; // convert from short code + code_ = short_code.to_common_code().code_; // convert from short code } CommonCode::CommonCode(const ShortCode &short_code) noexcept { - code = short_code.to_common_code().code; // convert from short code + code_ = short_code.to_common_code().code_; // convert from short code } CommonCode CommonCode::from_short_code(uint32_t short_code) { diff --git a/src/klotski_core/common_code/serialize.cc b/src/klotski_core/common_code/serialize.cc index 626aae9..7e92dcf 100644 --- a/src/klotski_core/common_code/serialize.cc +++ b/src/klotski_core/common_code/serialize.cc @@ -5,18 +5,18 @@ using klotski::CommonCodeException; /// -------------------------- CommonCode to String --------------------------- -std::string CommonCode::to_string(bool shorten) const { - return string_encode(code, shorten); +std::string CommonCode::to_string(bool shorten) const noexcept { + return string_encode(code_, shorten); } /// -------------------------- String to CommonCode --------------------------- CommonCode::CommonCode(std::string &&common_code) { - code = string_decode(common_code); // load from string + code_ = string_decode(common_code); // load from string } CommonCode::CommonCode(const std::string &common_code) { - code = string_decode(common_code); // load from string + code_ = string_decode(common_code); // load from string } CommonCode CommonCode::from_string(std::string &&common_code) { @@ -44,11 +44,11 @@ inline uint8_t last_zero_num(uint32_t bin) { // get last zero number return binary_count(bin >> 1); } -std::string CommonCode::string_encode(uint64_t common_code, bool shorten) noexcept { // convert uint64_t code to string - char result[10]; // max length 9-bits +std::string CommonCode::string_encode(uint64_t common_code, bool shorten) noexcept { // convert from uint64_t + char result[10]; // max length 9-bit sprintf(result, "%09lX", common_code); if (shorten) { // remove `0` after common code - if ((uint32_t)common_code == 0x00'00'00'00) { // low 32-bits are zero + if ((uint32_t)common_code == 0x00'00'00'00) { // low 32-bit are zero result[1] = '\0'; // only keep first character return result; } @@ -57,7 +57,7 @@ std::string CommonCode::string_encode(uint64_t common_code, bool shorten) noexce return result; // char* -> std::string } -uint64_t CommonCode::string_decode(const std::string &common_code) { // convert from 1 ~ 9 bits string +uint64_t CommonCode::string_decode(const std::string &common_code) { // convert from (1 ~ 9)-bit string /// check string length if (common_code.length() > 9 || common_code.empty()) { // check string length throw CommonCodeException("common code should length 1 ~ 9"); diff --git a/test/codec/short_code.cc b/test/codec/short_code.cc index 4c118b7..05932a7 100644 --- a/test/codec/short_code.cc +++ b/test/codec/short_code.cc @@ -34,7 +34,7 @@ TEST(ShortCode, speed_up) { std::thread threads[4]; /// speed up to normal mode - EXPECT_EQ(BasicRanges::status(), BasicRanges::NO_INIT); + EXPECT_EQ(BasicRanges::status(), BasicRanges::NOT_INIT); for (auto &t : threads) { t = std::thread(ShortCode::speed_up, ShortCode::NORMAL); } @@ -46,7 +46,7 @@ TEST(ShortCode, speed_up) { EXPECT_EQ(BasicRanges::status(), BasicRanges::AVAILABLE); /// speed up to fast mode - EXPECT_EQ(AllCases::status(), AllCases::NO_INIT); + EXPECT_EQ(AllCases::status(), AllCases::NOT_INIT); for (auto &t : threads) { t = std::thread(ShortCode::speed_up, ShortCode::FAST); }