Browse Source

update: enhance CommonCode module

master
Dnomd343 2 years 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_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 {
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

154
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

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 {
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) {

16
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");

4
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);
}

Loading…
Cancel
Save