Commit ·
defb949
1
Parent(s): 434c5af
Upload 2 files
Browse files- ChatIPC.cpp +177 -55
ChatIPC.cpp
CHANGED
|
@@ -309,7 +309,7 @@ static std::vector<std::string> tokenize_whitespace(const std::string &s){
|
|
| 309 |
return out;
|
| 310 |
}
|
| 311 |
|
| 312 |
-
// Static helper to check dictionary presence using
|
| 313 |
static inline bool check_dictionary(const std::string &word) {
|
| 314 |
return !dictionary_pos_for_token(word).empty();
|
| 315 |
}
|
|
@@ -484,8 +484,78 @@ static std::optional<std::string> get_valid_base_form(const std::string &word) {
|
|
| 484 |
return std::nullopt;
|
| 485 |
}
|
| 486 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
static std::vector<std::string> tokenize_others(const std::string &s) {
|
| 488 |
-
// Step A: Check if the entire input string already has a dictionary definition.
|
| 489 |
if (check_dictionary(s)) {
|
| 490 |
std::string lower_s;
|
| 491 |
lower_s.reserve(s.size());
|
|
@@ -495,15 +565,29 @@ static std::vector<std::string> tokenize_others(const std::string &s) {
|
|
| 495 |
return { lower_s };
|
| 496 |
}
|
| 497 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
std::vector<std::string> out;
|
| 499 |
std::string cur;
|
| 500 |
|
| 501 |
-
// Lambda to flush the accumulated token buffer, validate it, and add to output
|
| 502 |
auto flush = [&]() {
|
| 503 |
if (!cur.empty()) {
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 507 |
}
|
| 508 |
cur.clear();
|
| 509 |
}
|
|
@@ -513,26 +597,42 @@ static std::vector<std::string> tokenize_others(const std::string &s) {
|
|
| 513 |
unsigned char uc = static_cast<unsigned char>(s[i]);
|
| 514 |
char ch = static_cast<char>(uc);
|
| 515 |
|
| 516 |
-
//
|
| 517 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 518 |
flush();
|
| 519 |
continue;
|
| 520 |
}
|
| 521 |
|
| 522 |
const bool is_upper = std::isupper(uc) != 0;
|
| 523 |
bool camel_boundary = false;
|
| 524 |
-
|
| 525 |
if (is_upper && !cur.empty()) {
|
| 526 |
unsigned char prev = static_cast<unsigned char>(s[i - 1]);
|
| 527 |
const bool prev_lower_or_digit = (std::islower(prev) != 0) || (std::isdigit(prev) != 0);
|
| 528 |
const bool prev_upper = std::isupper(prev) != 0;
|
| 529 |
-
const bool next_lower =
|
| 530 |
-
(i + 1 < s.size()) && (std::islower(static_cast<unsigned char>(s[i + 1])) != 0);
|
| 531 |
-
|
| 532 |
camel_boundary = prev_lower_or_digit || (prev_upper && next_lower);
|
| 533 |
}
|
| 534 |
|
| 535 |
-
// Split on transitions between alphabetical characters and digits (e.g., "book5read")
|
| 536 |
bool digit_boundary = false;
|
| 537 |
if (!cur.empty()) {
|
| 538 |
unsigned char prev = static_cast<unsigned char>(s[i - 1]);
|
|
@@ -553,7 +653,6 @@ static std::vector<std::string> tokenize_others(const std::string &s) {
|
|
| 553 |
flush();
|
| 554 |
}
|
| 555 |
}
|
| 556 |
-
|
| 557 |
flush();
|
| 558 |
return out;
|
| 559 |
}
|
|
@@ -635,64 +734,87 @@ static void build_def_tokens_cache(){
|
|
| 635 |
global_def_tokens_cache.clear();
|
| 636 |
global_pos_cache.clear();
|
| 637 |
|
| 638 |
-
|
| 639 |
-
global_pos_cache.reserve(global_dictionary_entries.size());
|
| 640 |
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
std::vector<std::string>
|
| 644 |
-
|
| 645 |
-
auto toks = tokenize_others(def);
|
| 646 |
-
all_def_toks.insert(all_def_toks.end(), toks.begin(), toks.end());
|
| 647 |
-
}
|
| 648 |
|
| 649 |
-
|
|
|
|
|
|
|
| 650 |
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
if (c == ';' || c == ',') {
|
| 656 |
-
if (!current_sub.empty()) {
|
| 657 |
-
sub_words.push_back(current_sub);
|
| 658 |
-
current_sub.clear();
|
| 659 |
-
}
|
| 660 |
-
} else {
|
| 661 |
-
current_sub.push_back(c);
|
| 662 |
}
|
| 663 |
-
}
|
| 664 |
-
if (!current_sub.empty()) {
|
| 665 |
-
sub_words.push_back(current_sub);
|
| 666 |
-
}
|
| 667 |
|
| 668 |
-
|
| 669 |
-
for (size_t i = 0; i < sub_words.size(); ++i) {
|
| 670 |
-
const std::string key = normalize_dictionary_key(sub_words[i]);
|
| 671 |
-
if (key.empty()) continue;
|
| 672 |
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 677 |
} else {
|
| 678 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 679 |
}
|
|
|
|
|
|
|
| 680 |
}
|
|
|
|
| 681 |
|
| 682 |
-
|
| 683 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 684 |
}
|
| 685 |
}
|
| 686 |
|
| 687 |
-
//
|
| 688 |
-
|
| 689 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 690 |
std::sort(v.begin(), v.end());
|
| 691 |
v.erase(std::unique(v.begin(), v.end()), v.end());
|
| 692 |
}
|
| 693 |
|
| 694 |
-
|
| 695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 696 |
std::sort(v.begin(), v.end());
|
| 697 |
v.erase(std::unique(v.begin(), v.end()), v.end());
|
| 698 |
}
|
|
|
|
| 309 |
return out;
|
| 310 |
}
|
| 311 |
|
| 312 |
+
// Static helper to check dictionary presence using the existing pos cache
|
| 313 |
static inline bool check_dictionary(const std::string &word) {
|
| 314 |
return !dictionary_pos_for_token(word).empty();
|
| 315 |
}
|
|
|
|
| 484 |
return std::nullopt;
|
| 485 |
}
|
| 486 |
|
| 487 |
+
// Helper arrays for number-to-words
|
| 488 |
+
static const char* const ones[] = {"", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
| 489 |
+
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen",
|
| 490 |
+
"seventeen", "eighteen", "nineteen"};
|
| 491 |
+
static const char* const tens[] = {"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
|
| 492 |
+
static const char* const scales[] = {"", "thousand", "million", "billion", "trillion", "quadrillion",
|
| 493 |
+
"quintillion", "sextillion", "septillion", "octillion", "nonillion",
|
| 494 |
+
"decillion", "undecillion", "duodecillion", "tredecillion",
|
| 495 |
+
"quattuordecillion", "quindecillion", "sexdecillion", "septendecillion",
|
| 496 |
+
"octodecillion", "novemdecillion", "vigintillion", "googol"};
|
| 497 |
+
|
| 498 |
+
static std::vector<std::string> digit_to_words(const std::string& s) {
|
| 499 |
+
std::vector<std::string> words;
|
| 500 |
+
if (s.empty()) return words;
|
| 501 |
+
|
| 502 |
+
// Strip leading zeros
|
| 503 |
+
size_t first_non_zero = s.find_first_not_of('0');
|
| 504 |
+
if (first_non_zero == std::string::npos) {
|
| 505 |
+
words.push_back("zero");
|
| 506 |
+
return words;
|
| 507 |
+
}
|
| 508 |
+
std::string num = s.substr(first_non_zero);
|
| 509 |
+
|
| 510 |
+
// If it's absurdly large (e.g. googolplex logic), fall back to single digits to prevent overflow
|
| 511 |
+
// and maintain semantic definitions of the numbers.
|
| 512 |
+
if (num.length() > 69) {
|
| 513 |
+
for (char c : num) {
|
| 514 |
+
words.push_back(ones[c - '0']);
|
| 515 |
+
}
|
| 516 |
+
return words;
|
| 517 |
+
}
|
| 518 |
+
|
| 519 |
+
int scale_idx = 0;
|
| 520 |
+
while (!num.empty()) {
|
| 521 |
+
int chunk_len = std::min<int>(3, num.length());
|
| 522 |
+
std::string chunk_str = num.substr(num.length() - chunk_len);
|
| 523 |
+
num = num.substr(0, num.length() - chunk_len);
|
| 524 |
+
|
| 525 |
+
int chunk = std::stoi(chunk_str);
|
| 526 |
+
if (chunk > 0) {
|
| 527 |
+
std::vector<std::string> chunk_words;
|
| 528 |
+
int h = chunk / 100;
|
| 529 |
+
int remainder = chunk % 100;
|
| 530 |
+
|
| 531 |
+
if (h > 0) {
|
| 532 |
+
chunk_words.push_back(ones[h]);
|
| 533 |
+
chunk_words.push_back("hundred");
|
| 534 |
+
}
|
| 535 |
+
|
| 536 |
+
if (remainder > 0) {
|
| 537 |
+
if (remainder < 20) {
|
| 538 |
+
chunk_words.push_back(ones[remainder]);
|
| 539 |
+
} else {
|
| 540 |
+
chunk_words.push_back(tens[remainder / 10]);
|
| 541 |
+
if (remainder % 10 > 0) {
|
| 542 |
+
chunk_words.push_back(ones[remainder % 10]);
|
| 543 |
+
}
|
| 544 |
+
}
|
| 545 |
+
}
|
| 546 |
+
|
| 547 |
+
if (scale_idx > 0 && scale_idx < static_cast<int>(sizeof(scales)/sizeof(scales[0]))) {
|
| 548 |
+
chunk_words.push_back(scales[scale_idx]);
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
words.insert(words.begin(), chunk_words.begin(), chunk_words.end());
|
| 552 |
+
}
|
| 553 |
+
scale_idx++;
|
| 554 |
+
}
|
| 555 |
+
return words;
|
| 556 |
+
}
|
| 557 |
+
|
| 558 |
static std::vector<std::string> tokenize_others(const std::string &s) {
|
|
|
|
| 559 |
if (check_dictionary(s)) {
|
| 560 |
std::string lower_s;
|
| 561 |
lower_s.reserve(s.size());
|
|
|
|
| 565 |
return { lower_s };
|
| 566 |
}
|
| 567 |
|
| 568 |
+
// Convert pure digits to words
|
| 569 |
+
bool is_all_digits = !s.empty() && std::all_of(s.begin(), s.end(), [](unsigned char c) {
|
| 570 |
+
return std::isdigit(c) != 0;
|
| 571 |
+
});
|
| 572 |
+
if (is_all_digits) {
|
| 573 |
+
return digit_to_words(s);
|
| 574 |
+
}
|
| 575 |
+
|
| 576 |
std::vector<std::string> out;
|
| 577 |
std::string cur;
|
| 578 |
|
|
|
|
| 579 |
auto flush = [&]() {
|
| 580 |
if (!cur.empty()) {
|
| 581 |
+
// Check numbers inside mixed tokens (e.g. "word123")
|
| 582 |
+
bool cur_all_digits = std::all_of(cur.begin(), cur.end(), [](unsigned char c) { return std::isdigit(c) != 0; });
|
| 583 |
+
if (cur_all_digits) {
|
| 584 |
+
auto words = digit_to_words(cur);
|
| 585 |
+
out.insert(out.end(), words.begin(), words.end());
|
| 586 |
+
} else {
|
| 587 |
+
auto valid_base = get_valid_base_form(cur);
|
| 588 |
+
if (valid_base.has_value()) {
|
| 589 |
+
out.push_back(valid_base.value());
|
| 590 |
+
}
|
| 591 |
}
|
| 592 |
cur.clear();
|
| 593 |
}
|
|
|
|
| 597 |
unsigned char uc = static_cast<unsigned char>(s[i]);
|
| 598 |
char ch = static_cast<char>(uc);
|
| 599 |
|
| 600 |
+
// Check for Operator/Symbol extraction
|
| 601 |
+
if (std::ispunct(uc) || (std::isprint(uc) && !std::isalnum(uc) && !std::isspace(uc))) { flush();
|
| 602 |
+
std::string sym(1, ch);
|
| 603 |
+
|
| 604 |
+
// Check for double-character operators (==, <=, >=, !=, ++, --, &&, ||, <<, >>)
|
| 605 |
+
if (i + 1 < s.size()) {
|
| 606 |
+
unsigned char nxt_uc = static_cast<unsigned char>(s[i+1]);
|
| 607 |
+
if (std::ispunct(nxt_uc) || (std::isprint(nxt_uc) && !std::isalnum(nxt_uc) && !std::isspace(nxt_uc))) { std::string sym2 = sym + static_cast<char>(nxt_uc);
|
| 608 |
+
if (check_dictionary(sym2)) {
|
| 609 |
+
out.push_back(sym2);
|
| 610 |
+
++i;
|
| 611 |
+
continue;
|
| 612 |
+
}
|
| 613 |
+
}
|
| 614 |
+
}
|
| 615 |
+
if (check_dictionary(sym)) {
|
| 616 |
+
out.push_back(sym);
|
| 617 |
+
}
|
| 618 |
+
continue;
|
| 619 |
+
}
|
| 620 |
+
|
| 621 |
+
if (std::isspace(uc)) {
|
| 622 |
flush();
|
| 623 |
continue;
|
| 624 |
}
|
| 625 |
|
| 626 |
const bool is_upper = std::isupper(uc) != 0;
|
| 627 |
bool camel_boundary = false;
|
|
|
|
| 628 |
if (is_upper && !cur.empty()) {
|
| 629 |
unsigned char prev = static_cast<unsigned char>(s[i - 1]);
|
| 630 |
const bool prev_lower_or_digit = (std::islower(prev) != 0) || (std::isdigit(prev) != 0);
|
| 631 |
const bool prev_upper = std::isupper(prev) != 0;
|
| 632 |
+
const bool next_lower = (i + 1 < s.size()) && (std::islower(static_cast<unsigned char>(s[i + 1])) != 0);
|
|
|
|
|
|
|
| 633 |
camel_boundary = prev_lower_or_digit || (prev_upper && next_lower);
|
| 634 |
}
|
| 635 |
|
|
|
|
| 636 |
bool digit_boundary = false;
|
| 637 |
if (!cur.empty()) {
|
| 638 |
unsigned char prev = static_cast<unsigned char>(s[i - 1]);
|
|
|
|
| 653 |
flush();
|
| 654 |
}
|
| 655 |
}
|
|
|
|
| 656 |
flush();
|
| 657 |
return out;
|
| 658 |
}
|
|
|
|
| 734 |
global_def_tokens_cache.clear();
|
| 735 |
global_pos_cache.clear();
|
| 736 |
|
| 737 |
+
const int num_entries = static_cast<int>(global_dictionary_entries.size());
|
|
|
|
| 738 |
|
| 739 |
+
#pragma omp parallel
|
| 740 |
+
{
|
| 741 |
+
std::unordered_map<std::string, std::vector<std::string>> local_def_cache;
|
| 742 |
+
std::unordered_map<std::string, std::vector<std::string>> local_pos_cache;
|
|
|
|
|
|
|
|
|
|
| 743 |
|
| 744 |
+
#pragma omp for schedule(guided) nowait
|
| 745 |
+
for (int i = 0; i < num_entries; ++i){
|
| 746 |
+
const auto &entry = global_dictionary_entries[i];
|
| 747 |
|
| 748 |
+
std::vector<std::string> all_def_toks;
|
| 749 |
+
for (const auto &def : entry.definitions){
|
| 750 |
+
auto toks = tokenize_others(def);
|
| 751 |
+
all_def_toks.insert(all_def_toks.end(), toks.begin(), toks.end());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 752 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 753 |
|
| 754 |
+
std::string pos = normalize_pos_tag(entry.pos);
|
|
|
|
|
|
|
|
|
|
| 755 |
|
| 756 |
+
std::vector<std::string> sub_words;
|
| 757 |
+
std::string current_sub;
|
| 758 |
+
for (char c : entry.word) {
|
| 759 |
+
if (c == ';' || c == ',') {
|
| 760 |
+
if (!current_sub.empty()) {
|
| 761 |
+
sub_words.push_back(current_sub);
|
| 762 |
+
current_sub.clear();
|
| 763 |
+
}
|
| 764 |
} else {
|
| 765 |
+
current_sub.push_back(c);
|
| 766 |
+
}
|
| 767 |
+
}
|
| 768 |
+
if (!current_sub.empty()) sub_words.push_back(current_sub);
|
| 769 |
+
|
| 770 |
+
for (size_t j = 0; j < sub_words.size(); ++j) {
|
| 771 |
+
const std::string key = normalize_dictionary_key(sub_words[j]);
|
| 772 |
+
if (key.empty()) continue;
|
| 773 |
+
|
| 774 |
+
if (!pos.empty()) {
|
| 775 |
+
if (j == sub_words.size() - 1) {
|
| 776 |
+
local_pos_cache[key].push_back(std::move(pos));
|
| 777 |
+
} else {
|
| 778 |
+
local_pos_cache[key].push_back(pos);
|
| 779 |
+
}
|
| 780 |
}
|
| 781 |
+
auto &defs = local_def_cache[key];
|
| 782 |
+
defs.insert(defs.end(), all_def_toks.begin(), all_def_toks.end());
|
| 783 |
}
|
| 784 |
+
}
|
| 785 |
|
| 786 |
+
#pragma omp critical
|
| 787 |
+
{
|
| 788 |
+
for (auto &pr : local_def_cache) {
|
| 789 |
+
auto &vec = global_def_tokens_cache[pr.first];
|
| 790 |
+
vec.insert(vec.end(), std::make_move_iterator(pr.second.begin()), std::make_move_iterator(pr.second.end()));
|
| 791 |
+
}
|
| 792 |
+
for (auto &pr : local_pos_cache) {
|
| 793 |
+
auto &vec = global_pos_cache[pr.first];
|
| 794 |
+
vec.insert(vec.end(), std::make_move_iterator(pr.second.begin()), std::make_move_iterator(pr.second.end()));
|
| 795 |
+
}
|
| 796 |
}
|
| 797 |
}
|
| 798 |
|
| 799 |
+
// Extract keys to use OpenMP parallel-for deduplication over hash map structures
|
| 800 |
+
std::vector<std::string> def_keys;
|
| 801 |
+
def_keys.reserve(global_def_tokens_cache.size());
|
| 802 |
+
for (const auto &pr : global_def_tokens_cache) def_keys.push_back(pr.first);
|
| 803 |
+
|
| 804 |
+
#pragma omp parallel for schedule(dynamic)
|
| 805 |
+
for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(def_keys.size()); ++i){
|
| 806 |
+
auto &v = global_def_tokens_cache[def_keys[i]];
|
| 807 |
std::sort(v.begin(), v.end());
|
| 808 |
v.erase(std::unique(v.begin(), v.end()), v.end());
|
| 809 |
}
|
| 810 |
|
| 811 |
+
std::vector<std::string> pos_keys;
|
| 812 |
+
pos_keys.reserve(global_pos_cache.size());
|
| 813 |
+
for (const auto &pr : global_pos_cache) pos_keys.push_back(pr.first);
|
| 814 |
+
|
| 815 |
+
#pragma omp parallel for schedule(dynamic)
|
| 816 |
+
for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(pos_keys.size()); ++i){
|
| 817 |
+
auto &v = global_pos_cache[pos_keys[i]];
|
| 818 |
std::sort(v.begin(), v.end());
|
| 819 |
v.erase(std::unique(v.begin(), v.end()), v.end());
|
| 820 |
}
|