From db282cb533848ed09279572e3bb01b3b0f3d71ae Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Mon, 29 May 2023 18:43:57 -0700 Subject: [PATCH] add generate-all-products --- src/Main.cc | 39 ++++++++++++++++++ src/Product.cc | 106 +++++++++++++++++++++++++++++++++++++++++++++---- src/Product.hh | 7 ++++ 3 files changed, 145 insertions(+), 7 deletions(-) diff --git a/src/Main.cc b/src/Main.cc index 25556fca..fa2acea8 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -227,6 +227,7 @@ enum class Behavior { REPLAY_LOG, CAT_CLIENT, GENERATE_PRODUCT, + GENERATE_ALL_PRODUCTS, INSPECT_PRODUCT, PRODUCT_SPEED_TEST, }; @@ -438,6 +439,8 @@ int main(int argc, char** argv) { behavior = Behavior::EXTRACT_BML; } else if (!strcmp(argv[x], "generate-product")) { behavior = Behavior::GENERATE_PRODUCT; + } else if (!strcmp(argv[x], "generate-all-products")) { + behavior = Behavior::GENERATE_ALL_PRODUCTS; } else if (!strcmp(argv[x], "inspect-product")) { behavior = Behavior::INSPECT_PRODUCT; } else if (!strcmp(argv[x], "product-speed-test")) { @@ -1166,6 +1169,42 @@ int main(int argc, char** argv) { break; } + case Behavior::GENERATE_ALL_PRODUCTS: { + auto products = generate_all_products(); + fprintf(stdout, "%zu (0x%zX) products found\n", products.size(), products.size()); + for (const auto& it : products) { + fprintf(stdout, "Valid product: %08" PRIX32, it.first); + for (uint8_t where : it.second) { + fprintf(stdout, " (domain=%hhu, subdomain=%hhu)", + static_cast((where >> 2) & 3), + static_cast(where & 3)); + } + fputc('\n', stdout); + } + + atomic num_valid_products = 0; + mutex output_lock; + auto thread_fn = [&](uint64_t product, size_t) -> bool { + for (uint8_t domain = 0; domain < 3; domain++) { + for (uint8_t subdomain = 0; subdomain < 3; subdomain++) { + if (product_is_valid_fast(product, domain, subdomain)) { + num_valid_products++; + lock_guard g(output_lock); + fprintf(stdout, "Valid product: %08" PRIX64 " (domain=%hhu, subdomain=%hhu)\n", product, domain, subdomain); + } + } + } + return false; + }; + auto progress_fn = [&](uint64_t, uint64_t, uint64_t current_value, uint64_t) -> void { + uint64_t num_found = num_valid_products.load(); + fprintf(stderr, "... %08" PRIX64 " %" PRId64 " (0x%" PRIX64 ") found\r", + current_value, num_found, num_found); + }; + parallel_range(thread_fn, 0, 0x100000000, num_threads, progress_fn); + break; + } + case Behavior::INSPECT_PRODUCT: { if (!input_filename) { throw invalid_argument("no product given"); diff --git a/src/Product.cc b/src/Product.cc index 2301d745..aaa36c03 100644 --- a/src/Product.cc +++ b/src/Product.cc @@ -1,3 +1,5 @@ +#include "Product.hh" + #include #include @@ -1123,6 +1125,18 @@ static bool check_prime3(uint64_t prime3) { return primes3_set[offset]; } +static char replace_nybble_forward(uint8_t v) { + static const uint8_t values[16] = { + 0x5, 0xA, 0x7, 0x6, 0xD, 0x2, 0xC, 0x1, 0xF, 0x0, 0x8, 0xB, 0x3, 0xE, 0x9, 0x4}; + return values[v & 0x0F]; +} + +static char replace_nybble_reverse(uint8_t v) { + static const uint8_t values[16] = { + 0x9, 0x7, 0x5, 0xC, 0xF, 0x0, 0x3, 0x2, 0xA, 0xE, 0x1, 0xB, 0x6, 0x4, 0xD, 0x8}; + return values[v & 0x0F]; +} + static char replace_char_forward(char ch) { if (ch >= '0' && ch <= '9') { return "5A76D2C1F0"[ch - '0']; @@ -1165,6 +1179,28 @@ static uint64_t decode_product_str(const string& s) { return product; } +static uint32_t decode_product_int(uint32_t v) { + return (replace_nybble_forward(v >> 28) << 28) | + (replace_nybble_forward(v >> 24) << 24) | + (replace_nybble_forward(v >> 20) << 20) | + (replace_nybble_forward(v >> 16) << 16) | + (replace_nybble_forward(v >> 12) << 12) | + (replace_nybble_forward(v >> 8) << 8) | + (replace_nybble_forward(v >> 4) << 4) | + (replace_nybble_forward(v)); +} + +static uint32_t encode_product_int(uint32_t v) { + return (replace_nybble_reverse(v >> 28) << 28) | + (replace_nybble_reverse(v >> 24) << 24) | + (replace_nybble_reverse(v >> 20) << 20) | + (replace_nybble_reverse(v >> 16) << 16) | + (replace_nybble_reverse(v >> 12) << 12) | + (replace_nybble_reverse(v >> 8) << 8) | + (replace_nybble_reverse(v >> 4) << 4) | + (replace_nybble_reverse(v)); +} + static pair compute_offset1_and_limit1( uint8_t domain, uint8_t subdomain) { if (domain > 2) { @@ -1202,12 +1238,7 @@ bool product_is_valid(const string& s, uint8_t domain, uint8_t subdomain) { return false; } -bool product_is_valid_fast(const string& s, uint8_t domain, uint8_t subdomain) { - uint64_t product = decode_product_str(s); - if (product == INVALID_PRODUCT) { - return false; - } - +bool decoded_product_is_valid_fast(uint32_t product, uint8_t domain, uint8_t subdomain) { auto [offset1_start, limit1] = compute_offset1_and_limit1(domain, subdomain); if (limit1 == 0) { return false; @@ -1233,6 +1264,18 @@ bool product_is_valid_fast(const string& s, uint8_t domain, uint8_t subdomain) { return false; } +bool product_is_valid_fast(const string& s, uint8_t domain, uint8_t subdomain) { + uint64_t product = decode_product_str(s); + if (product == INVALID_PRODUCT) { + return false; + } + return decoded_product_is_valid_fast(product, domain, subdomain); +} + +bool product_is_valid_fast(uint32_t product, uint8_t domain, uint8_t subdomain) { + return decoded_product_is_valid_fast(decode_product_int(product), domain, subdomain); +} + string generate_product(uint8_t domain, uint8_t subdomain) { size_t offset1, limit1; if (domain == 0) { @@ -1262,6 +1305,55 @@ string generate_product(uint8_t domain, uint8_t subdomain) { return ret; } +unordered_map generate_all_products(uint8_t domain, uint8_t subdomain) { + vector domains; + if (domain == 0xFF) { + domains.emplace_back(0x00); + domains.emplace_back(0x01); + domains.emplace_back(0x02); + } else { + domains.emplace_back(domain); + } + + vector subdomains; + if (subdomain == 0xFF) { + subdomains.emplace_back(0x00); + subdomains.emplace_back(0x01); + subdomains.emplace_back(0x02); + } else { + subdomains.emplace_back(subdomain); + } + + unordered_map ret; + for (uint8_t domain : domains) { + size_t offset1, limit1; + if (domain == 0) { + offset1 = 0x00; + limit1 = 0x03; + } else if (domain == 1) { + offset1 = 0x1E; + limit1 = 0x21; + } else if (domain == 2) { + offset1 = 0x3C; + limit1 = 0x3F; + } else { + throw runtime_error("invalid domain"); + } + + for (uint8_t subdomain : subdomains) { + size_t index1 = offset1 + (subdomain % (limit1 - offset1)); + for (size_t index2 = 0; index2 < sizeof(primes2) / sizeof(primes2[0]); index2++) { + for (size_t index3 = 0; index3 < sizeof(primes3) / sizeof(primes3[0]); index3++) { + uint32_t value = primes1[index1] * primes2[index2] * primes3[index3]; + ret[encode_product_int(value)].push_back(((domain << 2) & 3) | (subdomain & 3)); + } + fprintf(stderr, "... domain=%hhu subdomain=%hhu index2=%zu products=%zu (0x%zX)\n", domain, subdomain, index2, ret.size(), ret.size()); + } + } + } + return ret; +} + void product_speed_test(uint64_t seed) { uint32_t effective_seed = (seed & 0xFFFFFFFF00000000) ? random_object() : seed; fprintf(stderr, "Product speed test with seed=%08" PRIX32 "\n", effective_seed); @@ -1275,7 +1367,7 @@ void product_speed_test(uint64_t seed) { uint64_t start = now(); bool is_valid_fast = product_is_valid_fast(s, 1, 0xFF); - time_fast = now() - start; + time_fast += now() - start; start = now(); bool is_valid_slow = product_is_valid(s, 1, 0xFF); diff --git a/src/Product.hh b/src/Product.hh index 16ff4727..5c19258e 100644 --- a/src/Product.hh +++ b/src/Product.hh @@ -3,11 +3,18 @@ #include #include +#include bool product_is_valid( const std::string& s, uint8_t domain, uint8_t subdomain = 0xFF); bool product_is_valid_fast( const std::string& s, uint8_t domain, uint8_t subdomain = 0xFF); +bool product_is_valid_fast( + uint32_t product, uint8_t domain, uint8_t subdomain); +bool decoded_product_is_valid_fast( + uint32_t product, uint8_t domain, uint8_t subdomain); + std::string generate_product(uint8_t domain, uint8_t subdomain = 0xFF); +std::unordered_map generate_all_products(uint8_t domain = 0xFF, uint8_t subdomain = 0xFF); void product_speed_test(uint64_t seed = 0xFFFFFFFFFFFFFFFF);