diff --git a/src/klotski_core/group/build_cases.cc b/src/klotski_core/group/build_cases.cc index 2488189..eb9f860 100644 --- a/src/klotski_core/group/build_cases.cc +++ b/src/klotski_core/group/build_cases.cc @@ -12,6 +12,18 @@ namespace klotski { using Common::check_range; using Common::range_reverse; +uint32_t Group::group_size(const GroupId &group_id) { + return group_cases(group_seed(group_id)).size(); +} + +uint32_t Group::group_size(const RawCode &raw_code) { + return group_cases(raw_code).size(); +} + +uint32_t Group::group_size(const CommonCode &common_code) { + return group_size(common_code.to_raw_code()); +} + std::vector Group::group_cases(const CommonCode &common_code) { return group_cases(common_code.to_raw_code()); } diff --git a/src/klotski_core/group/group.h b/src/klotski_core/group/group.h index 645b1b9..84614c4 100644 --- a/src/klotski_core/group/group.h +++ b/src/klotski_core/group/group.h @@ -7,6 +7,7 @@ namespace klotski { const uint32_t TYPE_ID_LIMIT = 203; +const uint32_t ALL_GROUP_NUM = 25422; /// ----------------------------------------- Type ID ----------------------------------------- @@ -84,6 +85,11 @@ class Group { public: /// ----------------------------------- group seeds ----------------------------------- + /// Get the size of the specified group. + static uint32_t group_size(const GroupId &group_id); + static uint32_t group_size(const RawCode &raw_code); + static uint32_t group_size(const CommonCode &common_code); + /// Get the minimum CommonCode of the specified group. static CommonCode group_seed(const GroupId &group_id); static CommonCode group_seed(const RawCode &raw_code); diff --git a/test/group/build_cases.cc b/test/group/build_cases.cc index 83cbbcd..ded1637 100644 --- a/test/group/build_cases.cc +++ b/test/group/build_cases.cc @@ -1,16 +1,15 @@ #include #include "md5.h" #include "group.h" +#include "type_id.h" +#include "tiny_pool.h" #include "common_code.h" +#include "group_seeds.h" #include "gtest/gtest.h" -#include "tiny_pool.h" - -#include "group/type_id.h" -#include "group/group_seeds.h" - using klotski::Group; using klotski::TypeId; +using klotski::GroupId; using klotski::AllCases; using klotski::RawCode; @@ -19,44 +18,53 @@ using klotski::CommonCode; using klotski::GROUP_SEEDS; using klotski::TYPE_ID_SIZE; +using klotski::ALL_GROUP_NUM; using klotski::TYPE_ID_LIMIT; using klotski::SHORT_CODE_LIMIT; +using klotski::TYPE_ID_GROUP_NUM; using klotski::ALL_CASES_SIZE_SUM; const char GROUP_INFO_MD5[] = "976bf22530085210e68a6a4e67053506"; TEST(Group, all_cases) { - std::vector> all_cases; + std::array, TYPE_ID_LIMIT> all_cases; + auto build = [&all_cases](TypeId type_id) { + auto cases = Group::all_cases(type_id); // build test data + EXPECT_EQ(cases.size(), TYPE_ID_SIZE[type_id.unwrap()]); // verify cases number + for (auto &&common_code : cases) { + EXPECT_EQ(TypeId(common_code), type_id); // verify type id + } + EXPECT_EQ(std::is_sorted(cases.begin(), cases.end()), true); // verify data order + all_cases[type_id.unwrap()] = cases; + }; + + auto pool = TinyPool(); for (uint32_t type_id = 0; type_id < TYPE_ID_LIMIT; ++type_id) { - all_cases.emplace_back(Group::all_cases(TypeId(type_id))); // build test data + pool.submit(build, TypeId(type_id)); } + pool.boot(); + pool.join(); // wait data build complete - std::vector combine; + std::vector combine; combine.reserve(ALL_CASES_SIZE_SUM); - for (uint32_t id = 0; id < TYPE_ID_LIMIT; ++id) { - EXPECT_EQ(all_cases[id].size(), TYPE_ID_SIZE[id]); // verify cases number - for (auto &&common_code : all_cases[id]) { - EXPECT_EQ(TypeId(common_code).unwrap(), id); // verify type id - combine.emplace_back(common_code.unwrap()); - } - std::is_sorted(all_cases[id].begin(), all_cases[id].end()); // verify data order + for (auto &&cases : all_cases) { + combine.insert(combine.end(), cases.begin(), cases.end()); } EXPECT_EQ(combine.size(), ALL_CASES_SIZE_SUM); // verify sum auto all_cases_release = AllCases::release(); std::stable_sort(combine.begin(), combine.end()); - for (uint32_t i = 0; i < combine.size(); ++i) { - EXPECT_EQ(combine[i], all_cases_release[i]); // verify after combined - } + EXPECT_EQ(combine, all_cases_release); // verify combined cases } TEST(Group, group_cases) { - auto test = [](CommonCode seed) -> std::vector { - auto group_raw = Group::group_cases(RawCode::from_common_code(seed)); + auto build = [](CommonCode seed) -> std::vector { + auto group_raw = Group::group_cases(seed); std::vector group(group_raw.begin(), group_raw.end()); // convert as CommonCodes EXPECT_EQ(seed, std::min_element(group.begin(), group.end())->unwrap()); // confirm min seed + EXPECT_EQ(group.size(), Group::group_size(seed)); // verify group size - uint32_t type_id = TypeId(CommonCode(seed)).unwrap(); // current type id + uint32_t type_id = TypeId(seed).unwrap(); // current type id for (auto &&elem : group) { EXPECT_EQ(TypeId(elem).unwrap(), type_id); // verify type id of group cases } @@ -67,7 +75,7 @@ TEST(Group, group_cases) { std::vector>> futures; for (auto &&seed : GROUP_SEEDS) { futures.emplace_back( - pool.submit(test, CommonCode::unsafe_create(seed)) + pool.submit(build, CommonCode::unsafe_create(seed)) ); } @@ -75,61 +83,100 @@ TEST(Group, group_cases) { std::vector all_cases; all_cases.reserve(ALL_CASES_SIZE_SUM); for (auto &&f : futures) { - auto ret = f.get(); - all_cases.insert(all_cases.end(), ret.begin(), ret.end()); + auto cases = f.get(); + all_cases.insert(all_cases.end(), cases.begin(), cases.end()); // combine build result } std::sort(all_cases.begin(), all_cases.end()); EXPECT_EQ(all_cases, AllCases::release()); // verify all released cases } -TEST(Group, build_group) { +TEST(Group, group_seeds) { + std::vector all_seeds; + all_seeds.reserve(ALL_GROUP_NUM); + for (uint32_t type_id = 0; type_id < TYPE_ID_LIMIT; ++type_id) { + auto seeds = Group::group_seeds(TypeId(type_id)); + for (auto &&seed : seeds) { + EXPECT_EQ(TypeId(seed).unwrap(), type_id); // verify type id of seeds + } + all_seeds.insert(all_seeds.end(), seeds.begin(), seeds.end()); + + std::vector sub_seeds; + sub_seeds.reserve(TYPE_ID_GROUP_NUM[type_id]); + for (uint32_t group_id = 0; group_id < TYPE_ID_GROUP_NUM[type_id]; ++group_id) { + sub_seeds.emplace_back(Group::group_seed(GroupId(type_id, group_id))); + } + std::sort(seeds.begin(), seeds.end()); + std::sort(sub_seeds.begin(), sub_seeds.end()); // don't verify seeds order for now + EXPECT_EQ(seeds, sub_seeds); // verify group seeds + } + std::vector group_seeds(GROUP_SEEDS, GROUP_SEEDS + ALL_GROUP_NUM); + EXPECT_EQ(all_seeds, group_seeds); // verify group seeds + auto test = [](CommonCode seed) { + EXPECT_EQ(Group::group_seed(seed), seed); // verify group seed fetch + EXPECT_EQ(Group::group_seed(seed.to_raw_code()), seed); + }; + auto pool = TinyPool(); + for (auto &&seed : GROUP_SEEDS) { // traverse all seeds + pool.submit(test, CommonCode::unsafe_create(seed)); + } + pool.boot(); + pool.join(); } TEST(Group, build_groups) { - struct group_info_t { - uint16_t group_id; - uint32_t group_index; - }; - ShortCode::speed_up(ShortCode::FAST); - std::vector all_cases(SHORT_CODE_LIMIT); + std::vector all_cases(SHORT_CODE_LIMIT); - std::vector group_ids; + auto test = [&all_cases](TypeId type_id) { + auto groups = Group::build_groups(type_id); + EXPECT_EQ(groups.size(), TYPE_ID_GROUP_NUM[type_id.unwrap()]); // verify groups num - for (uint16_t type_id = 0; type_id < klotski::TYPE_ID_LIMIT; ++type_id) { std::vector group_sizes; - std::vector group_seeds; - auto groups = Group::build_groups(TypeId(type_id)); - group_sizes.reserve(groups.size()); - group_seeds.reserve(groups.size()); - - for (uint32_t id = 0; id < groups.size(); ++id) { - group_sizes.emplace_back(groups[id].size()); // record size - std::sort(groups[id].begin(), groups[id].end()); // sort for group index - for (uint32_t index = 0; index < groups[id].size(); ++index) { - all_cases[groups[id][index].to_short_code().unwrap()] = { - .group_id = static_cast(id), + std::map> group_seeds; // + + for (uint32_t group_id = 0; group_id < groups.size(); ++group_id) { + auto group = Group::build_group(GroupId(type_id, group_id)); + std::sort(group.begin(), group.end()); + std::sort(groups[group_id].begin(), groups[group_id].end()); + EXPECT_EQ(groups[group_id], group); // verify group data + EXPECT_EQ(group.size(), Group::group_size(GroupId(type_id, group_id))); // verify group size + EXPECT_EQ(*group.begin(), Group::group_seed(GroupId(type_id, group_id))); // verify group seed + + for (uint32_t index = 0; index < group.size(); ++index) { + all_cases[group[index].to_short_code().unwrap()] = { // storage group info + .type_id = 0, + .group_id = static_cast(group_id), .group_index = index, }; - EXPECT_EQ(TypeId(groups[id][index]).unwrap(), type_id); // verify type id + EXPECT_EQ(TypeId(group[index]), type_id); // verify released type id } - group_seeds.emplace_back(groups[id][0]); // record seed - } - // TODO: check group_seeds and group_sizes + group_seeds[group.size()].emplace_back(*group.begin()); // storage group seeds + group_sizes.emplace_back(group.size()); // storage group size + } - // TODO: verify GROUP_OFFSET + EXPECT_EQ(std::is_sorted(group_sizes.rbegin(), group_sizes.rend()), true); // verify group size order + for (auto &&tmp : group_seeds) { + EXPECT_EQ(std::is_sorted(tmp.second.begin(), tmp.second.end()), true); // verify group cases order + } + }; + auto pool = TinyPool(); + ShortCode::speed_up(ShortCode::FAST); + for (uint32_t type_id = 0; type_id < TYPE_ID_LIMIT; ++type_id) { + pool.submit(test, TypeId(type_id)); } - - // TODO: verify GROUP_SEEDS / GROUP_SEEDS_INDEX / GROUP_SEEDS_INDEX_REV + pool.boot(); + pool.join(); char buffer[9]; - std::string group_info; + std::string group_info_str; for (auto &&tmp : all_cases) { sprintf(buffer, "%d,%d\n", tmp.group_id, tmp.group_index); - group_info += buffer; + group_info_str += buffer; } - auto group_info_md5 = md5(group_info.c_str(), group_info.size()); + auto group_info_md5 = md5(group_info_str.c_str(), group_info_str.size()); EXPECT_STREQ(group_info_md5.c_str(), GROUP_INFO_MD5); // verify all group info } + +// TODO: verify GROUP_SEEDS_INDEX_REV