Browse Source

perf: optimize the AllCases algorithm

legacy
Dnomd343 7 months ago
parent
commit
467c8badeb
  1. 1
      src/core/CMakeLists.txt
  2. 14
      src/core/all_cases/all_cases.h
  3. 42
      src/core/all_cases/internal/all_cases.cc
  4. 6
      src/core/all_cases/internal/basic_ranges.cc
  5. 113
      src/core/all_cases/internal/derive.cc
  6. 60
      src/core/benchmark/group.cc
  7. 28
      src/core/main.cc
  8. 150
      src/core/ranges/internal/derive.cc
  9. 11
      src/core/ranges/ranges.h
  10. 524
      src/core/ranges/statistics.py
  11. 59
      src/core_test/group_tmp/group_extend.cc
  12. 4
      src/core_test/group_tmp/group_union.cc

1
src/core/CMakeLists.txt

@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD 23)
set(KLOTSKI_CORE_SRC set(KLOTSKI_CORE_SRC
all_cases/internal/basic_ranges.cc all_cases/internal/basic_ranges.cc
all_cases/internal/all_cases.cc all_cases/internal/all_cases.cc
all_cases/internal/derive.cc
common_code/internal/common_code.cc common_code/internal/common_code.cc
common_code/internal/serialize.cc common_code/internal/serialize.cc

14
src/core/all_cases/all_cases.h

@ -127,6 +127,20 @@ private:
KLSK_INSTANCE(AllCases) KLSK_INSTANCE(AllCases)
}; };
inline const std::vector<uint32_t>& get_reversed() {
static auto value = []() {
std::vector<uint32_t> ranges {BasicRanges::instance().fetch()};
for (auto &x : ranges) {
x = range_reverse(x);
}
return ranges;
}();
return value;
}
void global_derive(const std::vector<uint32_t> &range, std::vector<uint32_t> &output, int head);
void global_derive_pro(const std::vector<uint32_t> &range, const std::vector<uint32_t> &reversed, std::vector<uint32_t> &output, int head);
// ------------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------------- //
} // namespace klotski::cases } // namespace klotski::cases

42
src/core/all_cases/internal/all_cases.cc

@ -22,13 +22,47 @@ consteval static std::array<int, 12> heads() {
static void build_cases(const int head, Ranges &release) { static void build_cases(const int head, Ranges &release) {
release.clear(); release.clear();
release.reserve(ALL_CASES_NUM[head]); release.reserve(ALL_CASES_NUM[head]);
BasicRanges::instance().fetch().derive(head, release);
// klotski::cases::global_derive(BasicRanges::instance().fetch(), release, head);
klotski::cases::global_derive_pro(BasicRanges::instance().fetch(), klotski::cases::get_reversed(), release, head);
// BasicRanges::instance().fetch().derive(head, release);
} }
void AllCases::build() { void AllCases::build() {
build_parallel([](auto &&func) {
func(); // TODO: lock here
});
// klotski::cases::get_reversed();
std::vector<uint32_t> reversed {BasicRanges::instance().fetch()};
for (auto &x : reversed) {
x = range_reverse(x);
}
// std::vector<uint32_t> reversed;
// std::ranges::transform(BasicRanges::instance().fetch(), std::back_inserter(reversed), [](uint32_t x) { return range_reverse(x); });
// auto &reversed = get_reversed();
for (auto head : heads()) {
// build_cases(head, get_cases()[head]);
auto &release = get_cases()[head];
release.clear();
release.reserve(ALL_CASES_NUM[head]);
// klotski::cases::global_derive(BasicRanges::instance().fetch(), release, head);
// klotski::cases::global_derive_pro(BasicRanges::instance().fetch(), reversed, release, head);
klotski::cases::global_derive_pro(reversed, BasicRanges::instance().fetch(), release, head);
}
available_ = true;
// build_parallel([](auto &&func) {
// func();
// });
} }
void AllCases::build_parallel(Executor &&executor) { void AllCases::build_parallel(Executor &&executor) {

6
src/core/all_cases/internal/basic_ranges.cc

@ -63,7 +63,7 @@ void BasicRanges::build_ranges(Ranges &ranges) {
} }
} while (flags.size() > 2); // merge until only one interval remains } while (flags.size() > 2); // merge until only one interval remains
for (auto &x : ranges) { // for (auto &x : ranges) {
x = range_reverse(x); // flip every 2-bit // x = range_reverse(x); // flip every 2-bit
} // }
} }

113
src/core/all_cases/internal/derive.cc

@ -0,0 +1,113 @@
#include "utils/utility.h"
#include "ranges/ranges.h"
#include "all_cases/all_cases.h"
using klotski::cases::Ranges;
/// Check whether the combination of head and range is valid.
static int check_range(const int head, uint32_t range) {
uint32_t flags = 0b110011 << head; // fill 2x2 block
for (int addr = 0, offset = 1; range; range >>= 2, ++offset) { // traverse every 2-bit
const auto num = std::countr_one(flags);
addr += num; // next unfilled block
flags >>= num;
switch (range & 0b11) {
case 0b00: // space
case 0b11: // 1x1 block
flags |= 0b1;
continue;
case 0b01: // 1x2 block
if (flags & 0b10 || addr % 4 == 3) { // invalid case
return offset; // broken offset
}
flags |= 0b11;
continue;
case 0b10: // 2x1 block
if (flags & 0b10000 || addr > 15) { // invalid case
return offset; // broken offset
}
flags |= 0b10001;
}
}
return 0; // pass check
}
void klotski::cases::global_derive(const std::vector<uint32_t> &range, std::vector<uint32_t> &output, int head) {
for (uint32_t index = 0; index < range.size(); ++index) {
if (const auto offset = check_range(head, range[index])) { // invalid case
// if (offset > 14) {
// continue;
// }
int left_offset = (16 - offset) * 2;
uint32_t min_next = ((range_reverse(range[index]) >> left_offset) + 1) << left_offset;
// if (offset > 5) {
while (range_reverse(range[++index]) < min_next) {} // located next range
--index;
// } else {
// auto begin = reversed.begin() + index;
// auto kk = std::lower_bound(begin, reversed.end(), min_next) - begin;
// index += kk - 1;
// }
// uint32_t tmp = 1U << (32 - offset * 2); // distance to next possible range
/// !! <- broken
/// ( xx xx xx ) xx xx xx ... [reversed range]
/// +1 00 00 00 ... (delta)
// tmp += range_reverse(range[index]) & ~(tmp - 1);
// TODO: overflow here in some type_id
// TODO: -> tmp > range[-1]
// TODO: maybe using binary search here
// while (range_reverse(range[++index]) < tmp) {} // located next range
// --index;
continue;
}
output.emplace_back(range_reverse(range[index])); // release valid case
}
}
void klotski::cases::global_derive_pro(const std::vector<uint32_t> &range, const std::vector<uint32_t> &reversed, std::vector<uint32_t> &output, int head) {
// uint32_t reversed_max = reversed.back();
for (uint32_t index = 0; index < range.size(); ++index) {
if (const auto offset = check_range(head, range[index])) { // invalid case
if (offset > 14) {
continue;
}
int left_offset = (16 - offset) * 2;
uint32_t min_next = ((reversed[index] >> left_offset) + 1) << left_offset;
if (offset > 5) {
while (reversed[++index] < min_next) {} // located next range
--index;
} else {
auto begin = reversed.begin() + index;
auto kk = std::lower_bound(begin, reversed.end(), min_next) - begin;
index += kk - 1;
}
// uint32_t tmp = 1U << (32 - offset * 2); // distance to next possible range
/// !! <- broken
/// ( xx xx xx ) xx xx xx ... [reversed range]
/// +1 00 00 00 ... (delta)
// tmp += range_reverse(range[index]) & ~(tmp - 1);
// TODO: overflow here in some type_id
// TODO: -> tmp > range[-1]
// TODO: maybe using binary search here
// while (range_reverse(range[++index]) < tmp) {} // located next range
// --index;
continue;
}
output.emplace_back(range_reverse(range[index])); // release valid case
}
}

60
src/core/benchmark/group.cc

@ -68,7 +68,7 @@ static void CommonCodeToTypeId(benchmark::State &state) {
for (auto code : samples) { for (auto code : samples) {
volatile auto ret = klotski::cases::common_code_to_type_id(code); // volatile auto ret = klotski::cases::common_code_to_type_id(code);
} }
} }
@ -90,7 +90,7 @@ static void RawCodeToTypeId(benchmark::State &state) {
for (auto _ : state) { for (auto _ : state) {
for (auto code : samples) { for (auto code : samples) {
volatile auto ret = klotski::cases::raw_code_to_type_id(code); // volatile auto ret = klotski::cases::raw_code_to_type_id(code);
} }
} }
@ -105,7 +105,7 @@ static void GroupExtend(benchmark::State &state) {
for (auto _ : state) { for (auto _ : state) {
volatile auto ret = klotski::cases::group_extend_from_seed(src); // volatile auto ret = klotski::cases::group_extend_from_seed(src);
// std::cout << ret.size() << std::endl; // std::cout << ret.size() << std::endl;
} }
@ -175,6 +175,7 @@ static void OriginBasicRanges(benchmark::State &state) {
static void OriginAllCases(benchmark::State &state) { static void OriginAllCases(benchmark::State &state) {
klotski::cases::BasicRanges::instance().build(); klotski::cases::BasicRanges::instance().build();
klotski::cases::get_reversed();
for (auto _ : state) { for (auto _ : state) {
auto &pp = klotski::cases::AllCases::instance(); auto &pp = klotski::cases::AllCases::instance();
@ -188,13 +189,58 @@ static void RangesDerive(benchmark::State &state) {
auto &basic_ranges = klotski::cases::BasicRanges::instance().fetch(); auto &basic_ranges = klotski::cases::BasicRanges::instance().fetch();
klotski::cases::Ranges flip {basic_ranges};
for (auto &x : flip) {
x = klotski::range_reverse(x);
}
klotski::cases::BidiRanges bidi_ranges;
for (auto x : basic_ranges) {
bidi_ranges.emplace_back(klotski::cases::bidi_t {.r1 = x, .r2 = klotski::range_reverse(x)});
}
klotski::cases::Ranges results; klotski::cases::Ranges results;
results.reserve(klotski::cases::ALL_CASES_NUM[5]); // results.reserve(klotski::cases::ALL_CASES_NUM[5]);
results.reserve(klotski::cases::ALL_CASES_NUM_);
for (auto _ : state) { for (auto _ : state) {
results.clear(); results.clear();
basic_ranges.derive(5, results); // results.reserve(klotski::cases::ALL_CASES_NUM[5]);
// basic_ranges.derive(5, results);
klotski::cases::derive_demo(basic_ranges, flip, results, 0);
klotski::cases::derive_demo(basic_ranges, flip, results, 1);
klotski::cases::derive_demo(basic_ranges, flip, results, 2);
klotski::cases::derive_demo(basic_ranges, flip, results, 4);
klotski::cases::derive_demo(basic_ranges, flip, results, 5);
klotski::cases::derive_demo(basic_ranges, flip, results, 6);
klotski::cases::derive_demo(basic_ranges, flip, results, 8);
klotski::cases::derive_demo(basic_ranges, flip, results, 9);
klotski::cases::derive_demo(basic_ranges, flip, results, 10);
klotski::cases::derive_demo(basic_ranges, flip, results, 12);
klotski::cases::derive_demo(basic_ranges, flip, results, 13);
klotski::cases::derive_demo(basic_ranges, flip, results, 14);
// klotski::cases::derive_demo_pro(bidi_ranges, results, 0);
// klotski::cases::derive_demo_pro(bidi_ranges, results, 1);
// klotski::cases::derive_demo_pro(bidi_ranges, results, 2);
//
// klotski::cases::derive_demo_pro(bidi_ranges, results, 4);
// klotski::cases::derive_demo_pro(bidi_ranges, results, 5);
// klotski::cases::derive_demo_pro(bidi_ranges, results, 6);
//
// klotski::cases::derive_demo_pro(bidi_ranges, results, 8);
// klotski::cases::derive_demo_pro(bidi_ranges, results, 9);
// klotski::cases::derive_demo_pro(bidi_ranges, results, 10);
//
// klotski::cases::derive_demo_pro(bidi_ranges, results, 12);
// klotski::cases::derive_demo_pro(bidi_ranges, results, 13);
// klotski::cases::derive_demo_pro(bidi_ranges, results, 14);
} }
@ -212,8 +258,8 @@ static void RangesDerive(benchmark::State &state) {
// BENCHMARK(OriginBasicRanges)->Unit(benchmark::kMillisecond); // BENCHMARK(OriginBasicRanges)->Unit(benchmark::kMillisecond);
// BENCHMARK(OriginAllCases)->Unit(benchmark::kMillisecond); BENCHMARK(OriginAllCases)->Unit(benchmark::kMillisecond);
BENCHMARK(RangesDerive)->Unit(benchmark::kMillisecond); // BENCHMARK(RangesDerive)->Unit(benchmark::kMillisecond);
BENCHMARK_MAIN(); BENCHMARK_MAIN();

28
src/core/main.cc

@ -27,6 +27,34 @@ using klotski::codec::SHORT_CODE_LIMIT;
int main() { int main() {
const auto start = clock(); const auto start = clock();
auto &basic_ranges = klotski::cases::BasicRanges::instance().fetch();
klotski::cases::Ranges flip {basic_ranges};
for (auto &x : flip) {
x = klotski::range_reverse(x);
}
klotski::cases::Ranges results;
results.reserve(klotski::cases::ALL_CASES_NUM_);
klotski::cases::derive_demo(basic_ranges, flip, results, 0);
klotski::cases::derive_demo(basic_ranges, flip, results, 1);
klotski::cases::derive_demo(basic_ranges, flip, results, 2);
klotski::cases::derive_demo(basic_ranges, flip, results, 4);
klotski::cases::derive_demo(basic_ranges, flip, results, 5);
klotski::cases::derive_demo(basic_ranges, flip, results, 6);
klotski::cases::derive_demo(basic_ranges, flip, results, 8);
klotski::cases::derive_demo(basic_ranges, flip, results, 9);
klotski::cases::derive_demo(basic_ranges, flip, results, 10);
klotski::cases::derive_demo(basic_ranges, flip, results, 12);
klotski::cases::derive_demo(basic_ranges, flip, results, 13);
klotski::cases::derive_demo(basic_ranges, flip, results, 14);
// std::cout << results.size() << " vs " << klotski::cases::ALL_CASES_NUM_ << std::endl;
// auto raw_code = RawCode::from_common_code(0x1A9BF0C00)->unwrap(); // auto raw_code = RawCode::from_common_code(0x1A9BF0C00)->unwrap();
// auto ret = klotski::cases::group_extend_from_seed(raw_code); // auto ret = klotski::cases::group_extend_from_seed(raw_code);
// //

150
src/core/ranges/internal/derive.cc

@ -1,3 +1,7 @@
#include <iostream>
#include <format>
#include "utils/utility.h" #include "utils/utility.h"
#include "ranges/ranges.h" #include "ranges/ranges.h"
@ -31,6 +35,152 @@ static int check_range(const int head, uint32_t range) {
return 0; // pass check return 0; // pass check
} }
// 0: [7, 8, 9, 10, 11, 12, 13, 14, 15]
// 1: [9, 10, 11, 12, 13, 14]
// 2: [8, 9, 10, 11, 12, 13]
// 3: [7, 8, 9, 10, 11, 12]
// 5: [8, 9, 10, 11, 12, 13]
// 10: [7, 8, 9, 10, 11, 12]
// 15: [8, 9, 10, 11, 12]
// 190: [5, 6, 7, 8]
// 284: [6, 7, 8]
// 327: [6, 7, 8]
// 591: [5, 6, 7, 8]
// 810: [5, 6, 7, 8]
// 895: [6, 7, 8]
// 1784: [4, 5, 6, 7]
// 2276: [5, 6, 7]
// 2447: [5, 6, 7]
// 5245: [4, 5, 6]
// 6346: [4, 5, 6]
// 6687: [5, 6]
// 15162: [3, 4, 5]
// 17588: [4, 5]
// 18271: [4, 5]
// 43243: [3, 4]
// 48548: [3]
// 48554: [3, 4]
// 49919: [4]
// 122103: [2]
// 122124: [2, 3]
// 133652: [3]
// 136382: [3]
// 342265: [2]
// 367139: [2]
void klotski::cases::derive_demo(const std::vector<uint32_t> &range, const std::vector<uint32_t> &reversed, std::vector<uint32_t> &output, int head) {
uint32_t reversed_max = reversed.back();
for (uint32_t index = 0; index < range.size(); ++index) {
if (const auto offset = check_range(head, range[index])) { // invalid case
if (offset > 14) {
continue;
}
// uint32_t index_bak = index;
int left_offset = (16 - offset) * 2;
uint32_t min_next = ((reversed[index] >> left_offset) + 1) << left_offset;
// min_next = std::min(min_next, reversed_max);
// std::cout << min_next << " vs " << reversed_max << std::endl;
if (offset > 5) {
while (reversed[++index] < min_next) { // located next range
// if (index > range.size()) {
// std::cout << "get it" << std::endl;
// }
}
--index;
} else {
auto begin = reversed.begin() + index;
auto kk = std::lower_bound(begin, reversed.end(), min_next) - begin;
index += kk - 1;
}
// std::cout << index << " vs " << index_bak << std::endl;
// auto end_distance = range.size() - index;
// auto real_distance = index - index_bak;
// std::cout << std::format("{} {} {}", head, real_distance, offset) << std::endl;
// std::cout << real_distance << " vs " << end_distance << std::endl;
// uint32_t tmp = 1U << (32 - offset * 2); // distance to next possible range
/// !! <- broken
/// ( xx xx xx ) xx xx xx ... [reversed range]
/// +1 00 00 00 ... (delta)
// tmp += range_reverse(range[index]) & ~(tmp - 1);
// TODO: overflow here in some type_id
// TODO: -> tmp > range[-1]
// TODO: maybe using binary search here
// while (range_reverse(range[++index]) < tmp) {} // located next range
// --index;
continue;
}
output.emplace_back(range_reverse(range[index])); // release valid case
}
}
void klotski::cases::derive_demo_pro(const klotski::cases::BidiRanges &bidi_range, std::vector<uint32_t> &output, int head) {
for (uint32_t index = 0; index < bidi_range.size(); ++index) {
if (const auto offset = check_range(head, bidi_range[index].r1)) { // invalid case
// if (offset > 14) {
// continue;
// }
// uint32_t index_bak = index;
int left_offset = (16 - offset) * 2;
uint32_t min_next = ((bidi_range[index].r2 >> left_offset) + 1) << left_offset;
// min_next = std::min(min_next, reversed_max);
// std::cout << min_next << " vs " << reversed_max << std::endl;
// if (offset > 5) {
while (bidi_range[++index].r2 < min_next) { // located next range
// if (index > range.size()) {
// std::cout << "get it" << std::endl;
// }
}
--index;
// } else {
// auto begin = reversed.begin() + index;
// auto kk = std::lower_bound(begin, reversed.end(), min_next) - begin;
// index += kk - 1;
// }
// std::cout << index << " vs " << index_bak << std::endl;
// auto end_distance = range.size() - index;
// auto real_distance = index - index_bak;
// std::cout << std::format("{} {} {}", head, real_distance, offset) << std::endl;
// std::cout << real_distance << " vs " << end_distance << std::endl;
// uint32_t tmp = 1U << (32 - offset * 2); // distance to next possible range
/// !! <- broken
/// ( xx xx xx ) xx xx xx ... [reversed range]
/// +1 00 00 00 ... (delta)
// tmp += range_reverse(range[index]) & ~(tmp - 1);
// TODO: overflow here in some type_id
// TODO: -> tmp > range[-1]
// TODO: maybe using binary search here
// while (range_reverse(range[++index]) < tmp) {} // located next range
// --index;
continue;
}
output.emplace_back(bidi_range[index].r2); // release valid case
}
}
void Ranges::derive(const int head, Ranges &output) const { void Ranges::derive(const int head, Ranges &output) const {
for (uint32_t index = 0; index < size(); ++index) { for (uint32_t index = 0; index < size(); ++index) {
if (const auto offset = check_range(head, (*this)[index])) { // invalid case if (const auto offset = check_range(head, (*this)[index])) { // invalid case

11
src/core/ranges/ranges.h

@ -5,6 +5,13 @@
namespace klotski::cases { namespace klotski::cases {
struct bidi_t {
uint32_t r1;
uint32_t r2;
};
typedef std::vector<bidi_t> BidiRanges;
class Ranges : public std::vector<uint32_t> { class Ranges : public std::vector<uint32_t> {
public: public:
/// Spawn klotski-ranges that match the specified block numbers. /// Spawn klotski-ranges that match the specified block numbers.
@ -14,6 +21,10 @@ public:
void derive(int head, Ranges &output) const; void derive(int head, Ranges &output) const;
}; };
void derive_demo(const std::vector<uint32_t> &range, const std::vector<uint32_t> &reversed, std::vector<uint32_t> &output, int head);
void derive_demo_pro(const BidiRanges &bidi_range, std::vector<uint32_t> &output, int head);
// TODO: add RangesUnion here // TODO: add RangesUnion here
// TODO: -> spawn from Ranges / export std::vector<CommonCode> // TODO: -> spawn from Ranges / export std::vector<CommonCode>

524
src/core/ranges/statistics.py

@ -0,0 +1,524 @@
#!/usr/bin/env python3
def load():
lines = open('tmp.txt').read().splitlines()
tmp = [x.split(' ') for x in lines]
data = [(int(x[0]), int(x[1]), int(x[2])) for x in tmp]
return data
def statis(data: list[tuple[int, int]]):
# data -> (distance, offset)
distances = sorted({x[0] for x in data})
for distance in distances:
all_offsets = [x[1] for x in data if x[0] == distance]
offsets = sorted(set(all_offsets))
# print(distance, offsets)
# for offset in offsets:
# print(offset, all_offsets.count(offset))
offset_num = {x: all_offsets.count(x) for x in offsets}
print(f'{distance} -> {offset_num}')
if __name__ == '__main__':
raw_data = load()
for head in range(16):
if head % 4 == 3:
continue
print(f'head = {head}')
statis([(x[1], x[2]) for x in raw_data])
# statis([(x[1], x[2]) for x in raw_data if x[0] == head])
print()
"""
0 -> {7: 177, 8: 2619, 9: 21087, 10: 156415, 11: 756356, 12: 1755849, 13: 1981898, 14: 1057653, 15: 212797}
1 -> {9: 18544, 10: 192582, 11: 596880, 12: 785128, 13: 460136, 14: 98136}
2 -> {8: 3578, 9: 15480, 10: 18792, 11: 8920, 12: 1760, 13: 120}
3 -> {7: 498, 8: 1963, 9: 2215, 10: 1000, 11: 192, 12: 13}
4 -> {8: 67}
5 -> {8: 1722, 9: 42570, 10: 178524, 11: 275405, 12: 178288, 13: 40830}
8 -> {7: 8}
10 -> {7: 490, 8: 5889, 9: 11075, 10: 7000, 11: 1728, 12: 143}
13 -> {6: 1}
15 -> {8: 7852, 9: 57590, 10: 120000, 11: 96384, 12: 26468}
16 -> {6: 50, 7: 433, 8: 625, 9: 297, 10: 53, 11: 3}
32 -> {7: 866, 8: 2500, 9: 1782, 10: 424, 11: 30}
42 -> {7: 23}
43 -> {7: 410, 8: 6875, 9: 16929, 10: 13091, 11: 3039}
57 -> {6: 189, 7: 457, 8: 295, 9: 68, 10: 5}
95 -> {6: 7}
98 -> {6: 182, 7: 1371, 8: 1475, 9: 476, 10: 45}
119 -> {7: 1828, 8: 7670, 9: 8160, 10: 2510}
184 -> {5: 1}
190 -> {5: 52, 6: 276, 7: 221, 8: 47, 9: 2}
284 -> {6: 552, 7: 884, 8: 282, 9: 16}
326 -> {6: 10}
327 -> {6: 266, 7: 2431, 8: 2679, 9: 494}
591 -> {5: 162, 6: 269, 7: 112, 8: 13}
806 -> {5: 6}
810 -> {5: 156, 6: 807, 7: 560, 8: 91}
895 -> {6: 1076, 7: 2912, 8: 1560}
1784 -> {4: 18, 5: 73, 6: 32, 7: 3}
2276 -> {5: 146, 6: 128, 7: 18}
2446 -> {5: 9}
2447 -> {5: 64, 6: 352, 7: 171}
5245 -> {4: 51, 5: 33, 6: 5}
6341 -> {4: 1}
6346 -> {4: 50, 5: 99, 6: 25}
6687 -> {5: 132, 6: 130}
15147 -> {3: 1}
15162 -> {3: 20, 4: 33, 5: 2}
17588 -> {4: 66, 5: 8}
18270 -> {4: 2}
18271 -> {4: 31, 5: 22}
43243 -> {3: 31, 4: 13}
48548 -> {3: 3}
48554 -> {3: 28, 4: 39}
49919 -> {4: 52}
122103 -> {2: 1}
122124 -> {2: 5, 3: 3}
133652 -> {3: 6}
136382 -> {3: 2}
136383 -> {3: 1}
342265 -> {2: 5}
367139 -> {2: 2}
367146 -> {2: 3}
953634 -> {1: 1}
953662 -> {1: 1}
"""
"""
head = 0
0 -> {7: 24, 8: 357, 9: 2834, 10: 19468, 11: 85928, 12: 181284, 13: 185611, 14: 89839, 15: 16369}
1 -> {9: 2548, 10: 23998, 11: 67680, 12: 80822, 13: 42756, 14: 8178}
2 -> {8: 460, 9: 1932, 10: 2262, 11: 1024, 12: 190, 13: 12}
3 -> {7: 46, 8: 161, 9: 177, 10: 79, 11: 15, 12: 1}
4 -> {8: 8}
5 -> {8: 222, 9: 5313, 10: 21489, 11: 31616, 12: 19247, 13: 4083}
10 -> {7: 46, 8: 483, 9: 885, 10: 553, 11: 135, 12: 11}
15 -> {8: 644, 9: 4602, 10: 9480, 11: 7530, 12: 2036}
16 -> {6: 2, 7: 20, 8: 25, 9: 9, 10: 1}
32 -> {7: 40, 8: 100, 9: 54, 10: 8}
43 -> {7: 20, 8: 275, 9: 513, 10: 247}
57 -> {6: 13, 7: 25, 8: 10, 9: 1}
98 -> {6: 13, 7: 75, 8: 50, 9: 7}
119 -> {7: 100, 8: 260, 9: 120}
190 -> {5: 5, 6: 23, 7: 18, 8: 3}
284 -> {6: 46, 7: 72, 8: 18}
327 -> {6: 23, 7: 198, 8: 171}
591 -> {5: 11, 6: 18, 7: 8, 8: 1}
810 -> {5: 11, 6: 54, 7: 40, 8: 7}
895 -> {6: 72, 7: 208, 8: 120}
1784 -> {5: 2, 6: 1}
2276 -> {5: 4, 6: 4}
2447 -> {5: 2, 6: 11}
15162 -> {3: 1, 4: 1}
17588 -> {4: 2}
18271 -> {4: 1}
43243 -> {3: 3, 4: 1}
48554 -> {3: 3, 4: 3}
49919 -> {4: 4}
122124 -> {2: 1}
342265 -> {2: 1}
367146 -> {2: 1}
head = 1
0 -> {7: 9, 8: 165, 9: 1464, 10: 10920, 11: 54215, 12: 130359, 13: 151147, 14: 81673, 15: 16369}
1 -> {9: 1320, 10: 14456, 11: 47280, 12: 64758, 13: 38684, 14: 8178}
2 -> {8: 226, 9: 1104, 10: 1518, 11: 800, 12: 170, 13: 12}
3 -> {7: 21, 8: 88, 9: 113, 10: 59, 11: 13, 12: 1}
4 -> {8: 8}
5 -> {8: 105, 9: 3036, 10: 14421, 11: 24700, 12: 17221, 13: 4083}
10 -> {7: 21, 8: 264, 9: 565, 10: 413, 11: 117, 12: 11}
15 -> {8: 352, 9: 2938, 10: 7080, 11: 6526, 12: 2036}
16 -> {6: 1, 7: 12, 8: 18, 9: 8, 10: 1}
32 -> {7: 24, 8: 72, 9: 48, 10: 8}
43 -> {7: 12, 8: 198, 9: 456, 10: 247}
57 -> {6: 7, 7: 15, 8: 8, 9: 1}
98 -> {6: 7, 7: 45, 8: 40, 9: 7}
119 -> {7: 60, 8: 208, 9: 120}
190 -> {5: 2, 6: 12, 7: 12, 8: 3}
284 -> {6: 24, 7: 48, 8: 18}
327 -> {6: 12, 7: 132, 8: 171}
591 -> {5: 4, 6: 10, 7: 6, 8: 1}
810 -> {5: 4, 6: 30, 7: 30, 8: 7}
895 -> {6: 40, 7: 156, 8: 120}
15162 -> {4: 1}
17588 -> {4: 2}
18271 -> {4: 1}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
122124 -> {2: 1, 3: 1}
133652 -> {3: 2}
136383 -> {3: 1}
342265 -> {2: 1}
367146 -> {2: 1}
953662 -> {1: 1}
head = 2
0 -> {7: 24, 8: 359, 9: 2838, 10: 19461, 11: 85764, 12: 181037, 13: 185611, 14: 89839, 15: 16369}
1 -> {9: 2556, 10: 24076, 11: 67800, 12: 80822, 13: 42756, 14: 8178}
2 -> {8: 460, 9: 1932, 10: 2262, 11: 1024, 12: 190, 13: 12}
3 -> {7: 44, 8: 165, 9: 179, 10: 79, 11: 15, 12: 1}
4 -> {8: 8}
5 -> {8: 222, 9: 5313, 10: 21489, 11: 31616, 12: 19247, 13: 4083}
10 -> {7: 44, 8: 495, 9: 895, 10: 553, 11: 135, 12: 11}
15 -> {8: 660, 9: 4654, 10: 9480, 11: 7530, 12: 2036}
16 -> {6: 2, 7: 19, 8: 27, 9: 10, 10: 1}
32 -> {7: 38, 8: 108, 9: 60, 10: 8}
43 -> {7: 19, 8: 297, 9: 570, 10: 247}
57 -> {6: 14, 7: 23, 8: 9, 9: 1}
98 -> {6: 14, 7: 69, 8: 45, 9: 7}
119 -> {7: 92, 8: 234, 9: 120}
190 -> {5: 5, 6: 23, 7: 18, 8: 3}
284 -> {6: 46, 7: 72, 8: 18}
327 -> {6: 23, 7: 198, 8: 171}
591 -> {5: 7, 6: 16, 7: 8, 8: 1}
810 -> {5: 7, 6: 48, 7: 40, 8: 7}
895 -> {6: 64, 7: 208, 8: 120}
5245 -> {4: 2, 5: 1}
6346 -> {4: 2, 5: 3}
6687 -> {5: 4}
15162 -> {3: 1, 4: 1}
17588 -> {4: 2}
18271 -> {4: 1}
43243 -> {3: 3, 4: 1}
48554 -> {3: 3, 4: 3}
49919 -> {4: 4}
122124 -> {2: 1}
342265 -> {2: 1}
367146 -> {2: 1}
head = 4
0 -> {7: 13, 8: 186, 9: 1638, 10: 12049, 11: 58254, 12: 135573, 13: 153173, 14: 81673, 15: 16369}
1 -> {9: 1464, 10: 15808, 11: 50040, 12: 66264, 13: 38684, 14: 8178}
2 -> {8: 244, 9: 1172, 10: 1584, 11: 816, 12: 170, 13: 12}
3 -> {7: 21, 8: 92, 9: 115, 10: 59, 11: 13, 12: 1}
5 -> {8: 122, 9: 3223, 10: 15048, 11: 25194, 12: 17221, 13: 4083}
10 -> {7: 21, 8: 276, 9: 575, 10: 413, 11: 117, 12: 11}
15 -> {8: 368, 9: 2990, 10: 7080, 11: 6526, 12: 2036}
16 -> {7: 4, 8: 12, 9: 7, 10: 1}
32 -> {7: 8, 8: 48, 9: 42, 10: 8}
43 -> {7: 4, 8: 132, 9: 399, 10: 247}
190 -> {5: 2, 6: 8, 7: 6, 8: 1}
284 -> {6: 16, 7: 24, 8: 6}
327 -> {6: 8, 7: 66, 8: 57}
591 -> {5: 7, 6: 16, 7: 8, 8: 1}
810 -> {5: 7, 6: 48, 7: 40, 8: 7}
895 -> {6: 64, 7: 208, 8: 120}
1784 -> {4: 1, 5: 3, 6: 1}
2276 -> {5: 6, 6: 4}
2447 -> {5: 3, 6: 11}
5245 -> {4: 4, 5: 5, 6: 1}
6346 -> {4: 4, 5: 15, 6: 5}
6687 -> {5: 20, 6: 26}
15162 -> {3: 1, 4: 1}
17588 -> {4: 2}
18271 -> {4: 1}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
342265 -> {2: 1}
367139 -> {2: 1}
953634 -> {1: 1}
head = 5
0 -> {7: 5, 8: 124, 9: 1161, 10: 8742, 11: 43440, 12: 105459, 13: 126813, 14: 73507, 15: 16369}
1 -> {9: 1044, 10: 11492, 11: 37800, 12: 53714, 13: 34612, 14: 8178}
2 -> {8: 162, 9: 840, 10: 1176, 11: 648, 12: 150, 13: 12}
3 -> {7: 11, 8: 55, 9: 77, 10: 44, 11: 11, 12: 1}
5 -> {8: 81, 9: 2310, 10: 11172, 11: 20007, 12: 15195, 13: 4083}
10 -> {7: 11, 8: 165, 9: 385, 10: 308, 11: 99, 12: 11}
15 -> {8: 220, 9: 2002, 10: 5280, 11: 5522, 12: 2036}
190 -> {5: 1, 6: 6, 7: 5, 8: 1}
284 -> {6: 12, 7: 20, 8: 6}
327 -> {6: 6, 7: 55, 8: 57}
591 -> {5: 7, 6: 14, 7: 7, 8: 1}
810 -> {5: 7, 6: 42, 7: 35, 8: 7}
895 -> {6: 56, 7: 182, 8: 120}
1784 -> {4: 2, 5: 9, 6: 6, 7: 1}
2276 -> {5: 18, 6: 24, 7: 6}
2447 -> {5: 9, 6: 66, 7: 57}
5245 -> {4: 5, 5: 5, 6: 1}
6346 -> {4: 5, 5: 15, 6: 5}
6687 -> {5: 20, 6: 26}
15162 -> {3: 2, 4: 4, 5: 1}
17588 -> {4: 8, 5: 4}
18271 -> {4: 4, 5: 11}
43243 -> {3: 3, 4: 1}
48548 -> {3: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
122103 -> {2: 1}
122124 -> {2: 1, 3: 1}
133652 -> {3: 2}
136382 -> {3: 1}
342265 -> {2: 1}
367139 -> {2: 1}
head = 6
0 -> {7: 13, 8: 189, 9: 1635, 10: 11994, 11: 58832, 12: 137293, 13: 154186, 14: 81673, 15: 16369}
1 -> {9: 1464, 10: 15522, 11: 49200, 12: 65762, 13: 38684, 14: 8178}
2 -> {8: 244, 9: 1172, 10: 1584, 11: 816, 12: 170, 13: 12}
3 -> {7: 13, 8: 68, 9: 101, 10: 57, 11: 13, 12: 1}
5 -> {8: 122, 9: 3223, 10: 15048, 11: 25194, 12: 17221, 13: 4083}
10 -> {7: 13, 8: 204, 9: 505, 10: 399, 11: 117, 12: 11}
15 -> {8: 272, 9: 2626, 10: 6840, 11: 6526, 12: 2036}
57 -> {6: 4, 7: 12, 8: 7, 9: 1}
98 -> {6: 4, 7: 36, 8: 35, 9: 7}
119 -> {7: 48, 8: 182, 9: 120}
190 -> {5: 2, 6: 8, 7: 6, 8: 1}
284 -> {6: 16, 7: 24, 8: 6}
327 -> {6: 8, 7: 66, 8: 57}
591 -> {5: 7, 6: 16, 7: 8, 8: 1}
810 -> {5: 7, 6: 48, 7: 40, 8: 7}
895 -> {6: 64, 7: 208, 8: 120}
1784 -> {4: 1, 5: 3, 6: 1}
2276 -> {5: 6, 6: 4}
2447 -> {5: 3, 6: 11}
5245 -> {4: 6, 5: 5, 6: 1}
6341 -> {4: 1}
6346 -> {4: 5, 5: 15, 6: 5}
6687 -> {5: 20, 6: 26}
15147 -> {3: 1}
15162 -> {3: 2, 4: 4}
17588 -> {4: 8}
18270 -> {4: 2}
18271 -> {4: 2}
43243 -> {3: 6, 4: 2}
48548 -> {3: 2}
48554 -> {3: 4, 4: 6}
49919 -> {4: 8}
122124 -> {2: 1, 3: 1}
133652 -> {3: 2}
136382 -> {3: 1}
head = 8
0 -> {7: 18, 8: 204, 9: 1439, 10: 10794, 11: 54722, 12: 131998, 13: 152149, 14: 81673, 15: 16369}
1 -> {9: 1172, 10: 13130, 11: 43200, 12: 59738, 13: 36648, 14: 8178}
2 -> {8: 320, 9: 1368, 10: 1698, 11: 832, 12: 170, 13: 12}
3 -> {7: 30, 8: 133, 9: 163, 10: 77, 11: 15, 12: 1}
4 -> {8: 8}
5 -> {8: 152, 9: 3762, 10: 16131, 11: 25688, 12: 17221, 13: 4083}
10 -> {7: 30, 8: 399, 9: 815, 10: 539, 11: 135, 12: 11}
15 -> {8: 532, 9: 4238, 10: 9240, 11: 7530, 12: 2036}
16 -> {6: 5, 7: 26, 8: 29, 9: 10, 10: 1}
32 -> {7: 52, 8: 116, 9: 60, 10: 8}
43 -> {7: 26, 8: 319, 9: 570, 10: 247}
57 -> {6: 18, 7: 55, 8: 44, 9: 12, 10: 1}
98 -> {6: 18, 7: 165, 8: 220, 9: 84, 10: 9}
119 -> {7: 220, 8: 1144, 9: 1440, 10: 502}
190 -> {5: 2, 6: 8, 7: 6, 8: 1}
284 -> {6: 16, 7: 24, 8: 6}
327 -> {6: 8, 7: 66, 8: 57}
591 -> {5: 17, 6: 23, 7: 9, 8: 1}
806 -> {5: 2}
810 -> {5: 15, 6: 69, 7: 45, 8: 7}
895 -> {6: 92, 7: 234, 8: 120}
1784 -> {4: 3, 5: 12, 6: 3}
2276 -> {5: 24, 6: 12}
2446 -> {5: 5}
2447 -> {5: 7, 6: 33}
5245 -> {4: 11, 5: 8, 6: 1}
6346 -> {4: 11, 5: 24, 6: 5}
6687 -> {5: 32, 6: 26}
15162 -> {3: 3, 4: 6, 5: 1}
17588 -> {4: 12, 5: 4}
18271 -> {4: 6, 5: 11}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 9
0 -> {7: 5, 8: 103, 9: 863, 10: 6189, 11: 32410, 12: 85591, 13: 111607, 14: 69424, 15: 16369}
1 -> {9: 748, 10: 8866, 11: 31440, 12: 47690, 13: 32576, 14: 8178}
2 -> {8: 154, 9: 812, 10: 1164, 11: 648, 12: 150, 13: 12}
3 -> {7: 25, 8: 110, 9: 138, 10: 68, 11: 14, 12: 1}
4 -> {8: 8}
5 -> {8: 69, 9: 2233, 10: 11058, 11: 20007, 12: 15195, 13: 4083}
10 -> {7: 25, 8: 330, 9: 690, 10: 476, 11: 126, 12: 11}
15 -> {8: 440, 9: 3588, 10: 8160, 11: 7028, 12: 2036}
16 -> {6: 6, 7: 52, 8: 93, 9: 56, 10: 13, 11: 1}
32 -> {7: 104, 8: 372, 9: 336, 10: 104, 11: 10}
43 -> {7: 52, 8: 1023, 9: 3192, 10: 3211, 11: 1013}
57 -> {6: 19, 7: 58, 8: 45, 9: 12, 10: 1}
98 -> {6: 19, 7: 174, 8: 225, 9: 84, 10: 9}
119 -> {7: 232, 8: 1170, 9: 1440, 10: 502}
190 -> {5: 8, 6: 41, 7: 37, 8: 11, 9: 1}
284 -> {6: 82, 7: 148, 8: 66, 9: 8}
326 -> {6: 2}
327 -> {6: 39, 7: 407, 8: 627, 9: 247}
591 -> {5: 26, 6: 35, 7: 11, 8: 1}
806 -> {5: 2}
810 -> {5: 24, 6: 105, 7: 55, 8: 7}
895 -> {6: 140, 7: 286, 8: 120}
1784 -> {4: 5, 5: 20, 6: 10, 7: 1}
2276 -> {5: 40, 6: 40, 7: 6}
2446 -> {5: 3}
2447 -> {5: 17, 6: 110, 7: 57}
5245 -> {4: 7, 5: 5, 6: 1}
6346 -> {4: 7, 5: 15, 6: 5}
6687 -> {5: 20, 6: 26}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 10
0 -> {7: 18, 8: 222, 9: 1694, 10: 10423, 11: 48002, 12: 115822, 13: 137978, 14: 77590, 15: 16369}
1 -> {9: 1536, 10: 16406, 11: 51120, 12: 66766, 13: 38684, 14: 8178}
2 -> {8: 320, 9: 1368, 10: 1698, 11: 832, 12: 170, 13: 12}
3 -> {7: 30, 8: 133, 9: 163, 10: 77, 11: 15, 12: 1}
4 -> {8: 8}
5 -> {8: 152, 9: 3762, 10: 16131, 11: 25688, 12: 17221, 13: 4083}
10 -> {7: 30, 8: 399, 9: 815, 10: 539, 11: 135, 12: 11}
15 -> {8: 532, 9: 4238, 10: 9240, 11: 7530, 12: 2036}
16 -> {6: 5, 7: 26, 8: 29, 9: 10, 10: 1}
32 -> {7: 52, 8: 116, 9: 60, 10: 8}
43 -> {7: 26, 8: 319, 9: 570, 10: 247}
57 -> {6: 26, 7: 65, 8: 46, 9: 12, 10: 1}
98 -> {6: 26, 7: 195, 8: 230, 9: 84, 10: 9}
119 -> {7: 260, 8: 1196, 9: 1440, 10: 502}
190 -> {5: 6, 6: 40, 7: 27, 8: 4}
284 -> {6: 80, 7: 108, 8: 24}
326 -> {6: 4}
327 -> {6: 36, 7: 297, 8: 228}
591 -> {5: 31, 6: 52, 7: 20, 8: 2}
806 -> {5: 2}
810 -> {5: 29, 6: 156, 7: 100, 8: 14}
895 -> {6: 208, 7: 520, 8: 240}
1784 -> {4: 3, 5: 12, 6: 7, 7: 1}
2276 -> {5: 24, 6: 28, 7: 6}
2446 -> {5: 1}
2447 -> {5: 11, 6: 77, 7: 57}
5245 -> {4: 4, 5: 1}
6346 -> {4: 4, 5: 3}
6687 -> {5: 4}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 12
0 -> {7: 7, 8: 109, 9: 1467, 10: 14381, 11: 69851, 12: 155802, 13: 168367, 14: 85756, 15: 16369}
1 -> {9: 1368, 10: 15236, 11: 49080, 12: 65762, 13: 38684, 14: 8178}
2 -> {8: 104, 9: 360, 10: 330, 11: 104, 12: 10}
3 -> {7: 53, 8: 199, 9: 210, 10: 89, 11: 16, 12: 1}
5 -> {8: 52, 9: 990, 10: 3135, 11: 3211, 12: 1013}
10 -> {7: 53, 8: 597, 9: 1050, 10: 623, 11: 144, 12: 11}
15 -> {8: 796, 9: 5460, 10: 10680, 11: 8032, 12: 2036}
16 -> {6: 6, 7: 69, 8: 85, 9: 30, 10: 3}
32 -> {7: 138, 8: 340, 9: 180, 10: 24}
42 -> {7: 7}
43 -> {7: 62, 8: 935, 9: 1710, 10: 741}
57 -> {6: 46, 7: 110, 8: 69, 9: 15, 10: 1}
95 -> {6: 5}
98 -> {6: 41, 7: 330, 8: 345, 9: 105, 10: 9}
119 -> {7: 440, 8: 1794, 9: 1800, 10: 502}
184 -> {5: 1}
190 -> {5: 9, 6: 53, 7: 48, 8: 13, 9: 1}
284 -> {6: 106, 7: 192, 8: 78, 9: 8}
326 -> {6: 4}
327 -> {6: 49, 7: 528, 8: 741, 9: 247}
591 -> {5: 15, 6: 23, 7: 9, 8: 1}
810 -> {5: 15, 6: 69, 7: 45, 8: 7}
895 -> {6: 92, 7: 234, 8: 120}
1784 -> {4: 1, 5: 4, 6: 1}
2276 -> {5: 8, 6: 4}
2447 -> {5: 4, 6: 11}
5245 -> {4: 4, 5: 1}
6346 -> {4: 4, 5: 3}
6687 -> {5: 4}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 13
0 -> {7: 21, 8: 281, 9: 1934, 10: 16983, 11: 94877, 12: 239808, 13: 286889, 14: 159250, 15: 32738}
1 -> {9: 1516, 10: 16068, 11: 50280, 12: 66264, 13: 38684, 14: 8178}
2 -> {8: 424, 9: 1768, 10: 2046, 11: 936, 12: 180, 13: 12}
3 -> {7: 90, 8: 315, 9: 303, 10: 114, 11: 18, 12: 1}
4 -> {8: 3}
5 -> {8: 209, 9: 4862, 10: 19437, 11: 28899, 12: 18234, 13: 4083}
8 -> {7: 4}
10 -> {7: 86, 8: 945, 9: 1515, 10: 798, 11: 162, 12: 11}
13 -> {6: 1}
15 -> {8: 1260, 9: 7878, 10: 13680, 11: 9036, 12: 2036}
16 -> {6: 14, 7: 128, 8: 188, 9: 92, 10: 17, 11: 1}
32 -> {7: 256, 8: 752, 9: 552, 10: 136, 11: 10}
42 -> {7: 12}
43 -> {7: 116, 8: 2068, 9: 5244, 10: 4199, 11: 1013}
57 -> {6: 27, 7: 68, 8: 47, 9: 12, 10: 1}
95 -> {6: 2}
98 -> {6: 25, 7: 204, 8: 235, 9: 84, 10: 9}
119 -> {7: 272, 8: 1222, 9: 1440, 10: 502}
190 -> {5: 5, 6: 27, 7: 19, 8: 3}
284 -> {6: 54, 7: 76, 8: 18}
327 -> {6: 27, 7: 209, 8: 171}
591 -> {5: 15, 6: 23, 7: 9, 8: 1}
810 -> {5: 15, 6: 69, 7: 45, 8: 7}
895 -> {6: 92, 7: 234, 8: 120}
1784 -> {4: 1, 5: 4, 6: 1}
2276 -> {5: 8, 6: 4}
2447 -> {5: 4, 6: 11}
5245 -> {4: 4, 5: 1}
6346 -> {4: 4, 5: 3}
6687 -> {5: 4}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
head = 14
0 -> {7: 20, 8: 320, 9: 2120, 10: 15011, 11: 70061, 12: 155823, 13: 168367, 14: 85756, 15: 16369}
1 -> {9: 1808, 10: 17524, 11: 51960, 12: 66766, 13: 38684, 14: 8178}
2 -> {8: 460, 9: 1652, 10: 1470, 11: 440, 12: 40}
3 -> {7: 114, 8: 444, 9: 476, 10: 198, 11: 34, 12: 2}
4 -> {8: 16}
5 -> {8: 214, 9: 4543, 10: 13965, 11: 13585, 12: 4052}
8 -> {7: 4}
10 -> {7: 110, 8: 1332, 9: 2380, 10: 1386, 11: 306, 12: 22}
15 -> {8: 1776, 9: 12376, 10: 23760, 11: 17068, 12: 4072}
16 -> {6: 9, 7: 77, 8: 119, 9: 65, 10: 14, 11: 1}
32 -> {7: 154, 8: 476, 9: 390, 10: 112, 11: 10}
42 -> {7: 4}
43 -> {7: 73, 8: 1309, 9: 3705, 10: 3458, 11: 1013}
57 -> {6: 15, 7: 26, 8: 10, 9: 1}
98 -> {6: 15, 7: 78, 8: 50, 9: 7}
119 -> {7: 104, 8: 260, 9: 120}
190 -> {5: 5, 6: 27, 7: 19, 8: 3}
284 -> {6: 54, 7: 76, 8: 18}
327 -> {6: 27, 7: 209, 8: 171}
591 -> {5: 15, 6: 23, 7: 9, 8: 1}
810 -> {5: 15, 6: 69, 7: 45, 8: 7}
895 -> {6: 92, 7: 234, 8: 120}
1784 -> {4: 1, 5: 4, 6: 1}
2276 -> {5: 8, 6: 4}
2447 -> {5: 4, 6: 11}
5245 -> {4: 4, 5: 1}
6346 -> {4: 4, 5: 3}
6687 -> {5: 4}
15162 -> {3: 2, 4: 3}
17588 -> {4: 6}
18271 -> {4: 3}
43243 -> {3: 2, 4: 1}
48554 -> {3: 2, 4: 3}
49919 -> {4: 4}
"""

59
src/core_test/group_tmp/group_extend.cc

@ -5,21 +5,21 @@
#include <__format/format_functions.h> #include <__format/format_functions.h>
TEST(Group, group_extend) { TEST(Group, group_extend) {
auto src = klotski::codec::RawCode::from_common_code(0x1A9BF0C00)->unwrap(); // auto src = klotski::codec::RawCode::from_common_code(0x1A9BF0C00)->unwrap();
auto codes = klotski::cases::group_extend_from_seed(src); // auto codes = klotski::cases::group_extend_from_seed(src);
//
for (auto &code: codes) { // for (auto &code: codes) {
auto raw_code = klotski::codec::RawCode::create(code).value(); // auto raw_code = klotski::codec::RawCode::create(code).value();
code = raw_code.to_common_code().unwrap(); // code = raw_code.to_common_code().unwrap();
} // }
std::sort(codes.begin(), codes.end()); // std::sort(codes.begin(), codes.end());
EXPECT_EQ(codes.size(), 25955); // EXPECT_EQ(codes.size(), 25955);
//
auto index = std::lower_bound(codes.begin(), codes.end(), 0x1A9BF0C00) - codes.begin(); // auto index = std::lower_bound(codes.begin(), codes.end(), 0x1A9BF0C00) - codes.begin();
EXPECT_EQ(index, 7472); // EXPECT_EQ(index, 7472);
//
auto hash_ret = hash::xxh3(codes.data(), codes.size() * sizeof(uint64_t)); // auto hash_ret = hash::xxh3(codes.data(), codes.size() * sizeof(uint64_t));
EXPECT_EQ(hash_ret, 0x91BD28A749312A6D); // EXPECT_EQ(hash_ret, 0x91BD28A749312A6D);
} }
static std::vector<std::tuple<int, int, int>> target_nums() { static std::vector<std::tuple<int, int, int>> target_nums() {
@ -34,7 +34,7 @@ static std::vector<std::tuple<int, int, int>> target_nums() {
} }
} }
// results.resize(203); results.resize(203);
return results; return results;
} }
@ -51,18 +51,37 @@ TEST(Group, ranges) {
// std::vector<uint32_t> ranges; // std::vector<uint32_t> ranges;
for (auto [n, n_2x1, n_1x1] : target_nums()) { for (auto [n, n_2x1, n_1x1] : target_nums()) {
kk.spawn_more(n, n_2x1, n_1x1); kk.spawn(n, n_2x1, n_1x1);
// kk.spawn_more(n, n_2x1, n_1x1);
// auto kk = klotski::cases::spawn_ranges(n, n_2x1, n_1x1); // auto kk = klotski::cases::spawn_ranges(n, n_2x1, n_1x1);
// ranges.insert(ranges.end(), kk.begin(), kk.end()); // ranges.insert(ranges.end(), kk.begin(), kk.end());
} }
EXPECT_EQ(kk.ranges_.size(), 7311921); EXPECT_EQ(kk.size(), 7311885);
klotski::cases::Ranges pp {kk};
for (auto &x : pp) {
x = klotski::range_reverse(x);
}
// auto hash_ret = hash::xxh3(kk.ranges_);
// EXPECT_EQ(hash_ret, 0xA1E247B01D5A9545);
klotski::cases::Ranges result;
// kk.derive(5, result);
klotski::cases::derive_demo(kk, pp, result, 5);
EXPECT_EQ(result.size(), 1355971);
// std::cout << result.size() << std::endl;
EXPECT_EQ(hash::xxh3(result), 0xf00ecc84b6f678a6);
auto hash_ret = hash::xxh3(kk.ranges_); // std::cout << std::format("{:x}", hash::xxh3(result)) << std::endl;
EXPECT_EQ(hash_ret, 0xA1E247B01D5A9545);
} }

4
src/core_test/group_tmp/group_union.cc

@ -128,7 +128,7 @@ TEST(Group, common_code) {
auto common_code = (head << 32 | range); auto common_code = (head << 32 | range);
EXPECT_EQ(klotski::cases::common_code_to_type_id(common_code), get_type_id(common_block_num(common_code))); // EXPECT_EQ(klotski::cases::common_code_to_type_id(common_code), get_type_id(common_block_num(common_code)));
} }
} }
@ -141,7 +141,7 @@ TEST(Group, raw_code) {
auto raw_code = CommonCode::unsafe_create(head << 32 | range).to_raw_code().unwrap(); auto raw_code = CommonCode::unsafe_create(head << 32 | range).to_raw_code().unwrap();
EXPECT_EQ(klotski::cases::raw_code_to_type_id(raw_code), get_type_id(raw_block_num(raw_code))); // EXPECT_EQ(klotski::cases::raw_code_to_type_id(raw_code), get_type_id(raw_block_num(raw_code)));
} }
} }

Loading…
Cancel
Save