Browse Source

update: enhance CommonCode module

master
Dnomd343 1 year ago
parent
commit
cc2042f9cf
  1. 89
      src/klotski_core/common_code/common_code.cc
  2. 154
      src/klotski_core/common_code/common_code.h
  3. 8
      src/klotski_core/common_code/convert.cc
  4. 16
      src/klotski_core/common_code/serialize.cc
  5. 4
      test/codec/short_code.cc

89
src/klotski_core/common_code/common_code.cc

@ -1,90 +1,65 @@
#include "common.h" #include "common.h"
#include "common_code.h" #include "common_code.h"
using klotski::CommonCode;
namespace std {
template<>
struct hash<klotski::CommonCode> {
std::size_t operator()(const klotski::CommonCode &c) const {
return std::hash<uint64_t>()(c.unwrap());
}
};
template<>
struct equal_to<klotski::CommonCode> {
bool operator()(const klotski::CommonCode &c1, const klotski::CommonCode &c2) const {
return c1.unwrap() == c2.unwrap();
}
};
}
namespace klotski { 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 { using Common::range_reverse;
return this->code != common_code.code;
}
std::ostream& operator<<(std::ostream &out, const CommonCode &self) { bool CommonCode::valid() const noexcept {
char str[10]; return CommonCode::check(code_);
sprintf(str, "%09lX", self.code);
out << str;
return out;
}
} }
namespace klotski { CommonCode CommonCode::create(uint64_t common_code) {
bool CommonCode::valid() const noexcept { return CommonCode(common_code); // create from uint64_t
return CommonCode::check(code); }
}
CommonCode CommonCode::create(uint64_t common_code) { CommonCode CommonCode::unsafe_create(uint64_t common_code) noexcept { // create without check
return CommonCode(common_code); // create from uint64_t auto tmp = CommonCode(); // init directly
} tmp.code_ = common_code;
return tmp;
}
CommonCode CommonCode::unsafe_create(uint64_t common_code) noexcept { // create without check CommonCode::CommonCode(uint64_t common_code) {
auto tmp = CommonCode(); // init directly if (!CommonCode::check(common_code)) { // check input common code
tmp.code = common_code; throw klotski::CommonCodeException("common code invalid");
return tmp;
} }
code_ = common_code;
}
CommonCode::CommonCode(uint64_t common_code) { std::ostream& operator<<(std::ostream &out, const CommonCode &self) {
if (!CommonCode::check(common_code)) { // check input common code char str[10];
throw klotski::CommonCodeException("common code invalid"); sprintf(str, "%09lX", self.code_);
} out << str;
code = common_code; return out;
}
} }
bool CommonCode::check(uint64_t common_code) noexcept { // whether common code is valid bool CommonCode::check(uint64_t common_code) noexcept { // whether common code is valid
/// M_1x1 M_1x2 M_2x1 M_2x2 /// M_1x1 | M_1x2 | M_2x1 | M_2x2
/// 1 0 0 0 1 1 0 0 1 0 0 0 1 1 0 0 /// 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 /// 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_1x1 = 0b1;
constexpr uint32_t M_1x2 = 0b11; constexpr uint32_t M_1x2 = 0b11;
constexpr uint32_t M_2x1 = 0b10001; constexpr uint32_t M_2x1 = 0b10001;
constexpr uint32_t M_2x2 = 0b110011; constexpr uint32_t M_2x2 = 0b110011;
/// 2x2 address check (high 32-bits) /// 2x2 address check (high 32-bit)
uint32_t head = common_code >> 32; uint32_t head = common_code >> 32;
if (head >= 16 || (head & 0b11) == 0b11) { // check 2x2 block address if (head >= 16 || (head & 0b11) == 0b11) { // check 2x2 block address
return false; // invalid common code return false; // invalid common code
} }
/// check range status (low 32-bits) /// check range status (low 32-bit)
int space_num = 0; int space_num = 0;
uint32_t mask = M_2x2 << head; // fill 2x2 block uint32_t mask = M_2x2 << head; // fill 2x2 block
auto range = Common::range_reverse((uint32_t)common_code); auto range = range_reverse((uint32_t)common_code); // extract range
for (int addr = 0;; range >>= 2) { // traverse every 2-bits
for (int addr = 0;; range >>= 2) { // traverse every 2-bit
while ((mask >> addr) & 0b1) { while ((mask >> addr) & 0b1) {
++addr; // search next unfilled block ++addr; // search next unfilled block
} }
if (addr >= 20) { 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) { switch (range & 0b11) {
case 0b00: /// space case 0b00: /// space
@ -109,3 +84,5 @@ bool CommonCode::check(uint64_t common_code) noexcept { // whether common code i
} }
} }
} }
} // namespace klotski

154
src/klotski_core/common_code/common_code.h

@ -1,11 +1,11 @@
#pragma once #pragma once
/// CommonCode is a generic klotski encoding that records an valid case using /// 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 /// 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 /// 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 /// 00 01 02 03
/// 04 05 06 07 00 01 02 /// 04 05 06 07 00 01 02
@ -14,22 +14,22 @@
/// 16 17 18 19 12 13 14 /// 16 17 18 19 12 13 14
/// Treat spaces as special blocks, there can be four kinds of blocks in total, /// 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 /// which are `00` `01` `10` `11`. Arrange them according to their position and
/// size, and we can get a binary sequence. /// size, and we can get a binary sequence.
/// 2x2 -> # # | 2x1 -> # | 1x2 -> # # | 1x1 -> # /// 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 /// 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 /// is set to 32-bit. In addition, for the convenience of reading, it is stipulated
/// stipulated that the sequence starts from the high bit, and the remaining bits /// that the sequence starts from the high bit, and the remaining bits should be
/// should be filled with `0`. /// filled with `0`.
/// Putting the content of the `head` in the upper 4-bits, and the lower 32-bits /// Putting the content of the `head` in the upper 4-bit, and the lower 32-bit
/// to store the sequence content, a 36-bits length code can be obtained, which /// 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 /// 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 /// 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 /// 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 /// omitted, and it can be completed to 9 digits when decoding, but note that if
@ -59,69 +59,71 @@
#include "short_code.h" #include "short_code.h"
namespace klotski { namespace klotski {
class RawCode; // import for convert interface
class ShortCode; class RawCode;
class ShortCode;
class CommonCodeException : public std::runtime_error {
public: class CommonCodeException : public std::runtime_error {
CommonCodeException() : std::runtime_error("invalid common code") {} public:
explicit CommonCodeException(const std::string &msg) : std::runtime_error(msg) {} CommonCodeException() : std::runtime_error("invalid common code") {}
~CommonCodeException() noexcept override = default; explicit CommonCodeException(const std::string &msg) : std::runtime_error(msg) {}
}; ~CommonCodeException() noexcept override = default;
};
class CommonCode {
uint64_t code; class CommonCode {
CommonCode() = default; // unsafe initialize 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; 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 public:
bool valid() const noexcept; /// Validity check
static bool check(uint64_t common_code) noexcept; bool valid() const noexcept;
static bool check(uint64_t common_code) noexcept;
/// Operators of CommonCode
bool operator==(const CommonCode &common_code) const noexcept; /// Operators of CommonCode
bool operator!=(const CommonCode &common_code) const noexcept; constexpr explicit operator uint64_t() const noexcept { return code_; }
constexpr explicit operator uint64_t() const noexcept { return code; } friend std::ostream& operator<<(std::ostream &out, const CommonCode &self);
friend std::ostream& operator<<(std::ostream &out, const CommonCode &self);
/// Export functions
/// Export functions RawCode to_raw_code() const noexcept;
RawCode to_raw_code() const noexcept; ShortCode to_short_code() const noexcept;
ShortCode to_short_code() const noexcept; std::string to_string(bool shorten = false) const noexcept;
std::string to_string(bool shorten = false) const; constexpr uint64_t unwrap() const noexcept { return code_; }
constexpr uint64_t unwrap() const noexcept { return code; }
/// CommonCode constructors
/// CommonCode constructors explicit CommonCode(uint64_t common_code);
explicit CommonCode(uint64_t common_code); explicit CommonCode(RawCode &&raw_code) noexcept;
explicit CommonCode(RawCode &&raw_code) noexcept; explicit CommonCode(ShortCode &&short_code) noexcept;
explicit CommonCode(ShortCode &&short_code) noexcept; explicit CommonCode(std::string &&common_code);
explicit CommonCode(std::string &&common_code); explicit CommonCode(const RawCode &raw_code) noexcept;
explicit CommonCode(const RawCode &raw_code) noexcept; explicit CommonCode(const ShortCode &short_code) noexcept;
explicit CommonCode(const ShortCode &short_code) noexcept; explicit CommonCode(const std::string &common_code);
explicit CommonCode(const std::string &common_code);
/// Static initialization
/// Static initialization static CommonCode create(uint64_t common_code);
static CommonCode create(uint64_t common_code); static CommonCode unsafe_create(uint64_t common_code) noexcept;
static CommonCode unsafe_create(uint64_t common_code) noexcept;
static CommonCode from_string(std::string &&common_code);
static CommonCode from_string(std::string &&common_code); static CommonCode from_string(const 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(uint64_t raw_code); static CommonCode from_raw_code(RawCode &&raw_code) noexcept;
static CommonCode from_raw_code(RawCode &&raw_code) noexcept; static CommonCode from_raw_code(const 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(uint32_t short_code); static CommonCode from_short_code(ShortCode &&short_code) noexcept;
static CommonCode from_short_code(ShortCode &&short_code) noexcept; static CommonCode from_short_code(std::string &&short_code);
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 ShortCode &short_code) noexcept; static CommonCode from_short_code(const std::string &short_code);
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!=(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, 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

8
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 { 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 { 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 ------------------------- /// ------------------------- ShortCode to CommonCode -------------------------
CommonCode::CommonCode(ShortCode &&short_code) noexcept { 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 { 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) { CommonCode CommonCode::from_short_code(uint32_t short_code) {

16
src/klotski_core/common_code/serialize.cc

@ -5,18 +5,18 @@ using klotski::CommonCodeException;
/// -------------------------- CommonCode to String --------------------------- /// -------------------------- CommonCode to String ---------------------------
std::string CommonCode::to_string(bool shorten) const { std::string CommonCode::to_string(bool shorten) const noexcept {
return string_encode(code, shorten); return string_encode(code_, shorten);
} }
/// -------------------------- String to CommonCode --------------------------- /// -------------------------- String to CommonCode ---------------------------
CommonCode::CommonCode(std::string &&common_code) { 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) { 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) { 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); return binary_count(bin >> 1);
} }
std::string CommonCode::string_encode(uint64_t common_code, bool shorten) noexcept { // convert uint64_t code to string std::string CommonCode::string_encode(uint64_t common_code, bool shorten) noexcept { // convert from uint64_t
char result[10]; // max length 9-bits char result[10]; // max length 9-bit
sprintf(result, "%09lX", common_code); sprintf(result, "%09lX", common_code);
if (shorten) { // remove `0` after 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 result[1] = '\0'; // only keep first character
return result; return result;
} }
@ -57,7 +57,7 @@ std::string CommonCode::string_encode(uint64_t common_code, bool shorten) noexce
return result; // char* -> std::string 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 /// check string length
if (common_code.length() > 9 || common_code.empty()) { // check string length if (common_code.length() > 9 || common_code.empty()) { // check string length
throw CommonCodeException("common code should length 1 ~ 9"); throw CommonCodeException("common code should length 1 ~ 9");

4
test/codec/short_code.cc

@ -34,7 +34,7 @@ TEST(ShortCode, speed_up) {
std::thread threads[4]; std::thread threads[4];
/// speed up to normal mode /// speed up to normal mode
EXPECT_EQ(BasicRanges::status(), BasicRanges::NO_INIT); EXPECT_EQ(BasicRanges::status(), BasicRanges::NOT_INIT);
for (auto &t : threads) { for (auto &t : threads) {
t = std::thread(ShortCode::speed_up, ShortCode::NORMAL); t = std::thread(ShortCode::speed_up, ShortCode::NORMAL);
} }
@ -46,7 +46,7 @@ TEST(ShortCode, speed_up) {
EXPECT_EQ(BasicRanges::status(), BasicRanges::AVAILABLE); EXPECT_EQ(BasicRanges::status(), BasicRanges::AVAILABLE);
/// speed up to fast mode /// speed up to fast mode
EXPECT_EQ(AllCases::status(), AllCases::NO_INIT); EXPECT_EQ(AllCases::status(), AllCases::NOT_INIT);
for (auto &t : threads) { for (auto &t : threads) {
t = std::thread(ShortCode::speed_up, ShortCode::FAST); t = std::thread(ShortCode::speed_up, ShortCode::FAST);
} }

Loading…
Cancel
Save