diff options
Diffstat (limited to 'libs/hunspell/src/suggestmgr.c++')
| -rw-r--r-- | libs/hunspell/src/suggestmgr.c++ | 294 |
1 files changed, 199 insertions, 95 deletions
diff --git a/libs/hunspell/src/suggestmgr.c++ b/libs/hunspell/src/suggestmgr.c++ index 73ea91e3a3..6b363debd5 100644 --- a/libs/hunspell/src/suggestmgr.c++ +++ b/libs/hunspell/src/suggestmgr.c++ @@ -1,7 +1,7 @@ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * - * Copyright (C) 2002-2017 Németh László + * Copyright (C) 2002-2022 Németh László * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with @@ -72,6 +72,7 @@ #include <string.h> #include <stdio.h> #include <ctype.h> +#include <time.h> #include "suggestmgr.hxx" #include "htypes.hxx" @@ -79,6 +80,8 @@ const w_char W_VLINE = {'\0', '|'}; +#define MAX_CHAR_DISTANCE 4 + SuggestMgr::SuggestMgr(const char* tryme, unsigned int maxn, AffixMgr* aptr) { // register affix manager and check in string of chars to // try when building candidate suggestions @@ -132,6 +135,11 @@ SuggestMgr::SuggestMgr(const char* tryme, unsigned int maxn, AffixMgr* aptr) { ctryl = u8_u16(ctry_utf, tryme); } } + + // language with possible dash usage + // (latin letters or dash in TRY characters) + lang_with_dash_usage = (ctry && + ((strchr(ctry, '-') != NULL) || (strchr(ctry, 'a') != NULL))); } SuggestMgr::~SuggestMgr() { @@ -169,10 +177,13 @@ void SuggestMgr::testsug(std::vector<std::string>& wlst, } } -// generate suggestions for a misspelled word -// pass in address of array of char * pointers -// onlycompoundsug: probably bad suggestions (need for ngram sugs, too) -void SuggestMgr::suggest(std::vector<std::string>& slst, +/* generate suggestions for a misspelled word + * pass in address of array of char * pointers + * onlycompoundsug: probably bad suggestions (need for ngram sugs, too) + * return value: true, if there is a good suggestion + * (REP, ph: or a dictionary word pair) + */ +bool SuggestMgr::suggest(std::vector<std::string>& slst, const char* w, int* onlycompoundsug) { int nocompoundtwowords = 0; @@ -182,6 +193,7 @@ void SuggestMgr::suggest(std::vector<std::string>& slst, std::string w2; const char* word = w; size_t oldSug = 0; + bool good_suggestion = false; // word reversing wrapper for complex prefixes if (complexprefixes) { @@ -196,34 +208,49 @@ void SuggestMgr::suggest(std::vector<std::string>& slst, if (utf8) { wl = u8_u16(word_utf, word); if (wl == -1) { - return; + return false; } } - for (int cpdsuggest = 0; (cpdsuggest < 2) && (nocompoundtwowords == 0); + for (int cpdsuggest = 0; (cpdsuggest < 2) && (nocompoundtwowords == 0) && !good_suggestion; cpdsuggest++) { + + clock_t timelimit; + // initialize both in non-compound and compound cycles + timelimit = clock(); + // limit compound suggestion if (cpdsuggest > 0) oldSug = slst.size(); // suggestions for an uppercase word (html -> HTML) if (slst.size() < maxSug) { + size_t i = slst.size(); if (utf8) - capchars_utf(slst, &word_utf[0], wl, cpdsuggest); + capchars_utf(slst, word_utf.data(), wl, cpdsuggest); else capchars(slst, word, cpdsuggest); + if (slst.size() > i) + good_suggestion = true; } // perhaps we made a typical fault of spelling if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { + size_t i = slst.size(); replchars(slst, word, cpdsuggest); + if (slst.size() > i) + good_suggestion = true; } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // perhaps we made chose the wrong char from a related set if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { mapchars(slst, word, cpdsuggest); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // only suggest compound words when no other suggestion if ((cpdsuggest == 0) && (slst.size() > nsugorig)) @@ -232,77 +259,99 @@ void SuggestMgr::suggest(std::vector<std::string>& slst, // did we swap the order of chars by mistake if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { if (utf8) - swapchar_utf(slst, &word_utf[0], wl, cpdsuggest); + swapchar_utf(slst, word_utf.data(), wl, cpdsuggest); else swapchar(slst, word, cpdsuggest); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // did we swap the order of non adjacent chars by mistake if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { if (utf8) - longswapchar_utf(slst, &word_utf[0], wl, cpdsuggest); + longswapchar_utf(slst, word_utf.data(), wl, cpdsuggest); else longswapchar(slst, word, cpdsuggest); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // did we just hit the wrong key in place of a good char (case and keyboard) if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { if (utf8) - badcharkey_utf(slst, &word_utf[0], wl, cpdsuggest); + badcharkey_utf(slst, word_utf.data(), wl, cpdsuggest); else badcharkey(slst, word, cpdsuggest); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // did we add a char that should not be there if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { if (utf8) - extrachar_utf(slst, &word_utf[0], wl, cpdsuggest); + extrachar_utf(slst, word_utf.data(), wl, cpdsuggest); else extrachar(slst, word, cpdsuggest); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // did we forgot a char if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { if (utf8) - forgotchar_utf(slst, &word_utf[0], wl, cpdsuggest); + forgotchar_utf(slst, word_utf.data(), wl, cpdsuggest); else forgotchar(slst, word, cpdsuggest); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // did we move a char if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { if (utf8) - movechar_utf(slst, &word_utf[0], wl, cpdsuggest); + movechar_utf(slst, word_utf.data(), wl, cpdsuggest); else movechar(slst, word, cpdsuggest); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // did we just hit the wrong key in place of a good char if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { if (utf8) - badchar_utf(slst, &word_utf[0], wl, cpdsuggest); + badchar_utf(slst, word_utf.data(), wl, cpdsuggest); else badchar(slst, word, cpdsuggest); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // did we double two characters if ((slst.size() < maxSug) && (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { if (utf8) - doubletwochars_utf(slst, &word_utf[0], wl, cpdsuggest); + doubletwochars_utf(slst, word_utf.data(), wl, cpdsuggest); else doubletwochars(slst, word, cpdsuggest); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; // perhaps we forgot to hit space and two words ran together - if (!nosplitsugs && (slst.size() < maxSug) && - (!cpdsuggest || (slst.size() < oldSug + maxcpdsugs))) { - twowords(slst, word, cpdsuggest); + // (dictionary word pairs have top priority here, so + // we always suggest them, in despite of nosplitsugs, and + // drop compound word and other suggestions) + if (!cpdsuggest || (!nosplitsugs && slst.size() < oldSug + maxcpdsugs)) { + good_suggestion = twowords(slst, word, cpdsuggest, good_suggestion); } + if (clock() > timelimit + TIMELIMIT_SUGGESTION) + return good_suggestion; } // repeating ``for'' statement compounding support if (!nocompoundtwowords && (!slst.empty()) && onlycompoundsug) *onlycompoundsug = 1; + + return good_suggestion; } // suggestions for an uppercase word (html -> HTML) @@ -450,8 +499,11 @@ int SuggestMgr::replchars(std::vector<std::string>& wlst, return wlst.size(); } -// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -// -> vacacation) +// perhaps we doubled two characters +// (for example vacation -> vacacation) +// The recognized pattern with regex back-references: +// "(.)(.)\1\2\1" or "..(.)(.)\1\2" + int SuggestMgr::doubletwochars(std::vector<std::string>& wlst, const char* word, int cpdsuggest) { @@ -462,7 +514,7 @@ int SuggestMgr::doubletwochars(std::vector<std::string>& wlst, for (int i = 2; i < wl; i++) { if (word[i] == word[i - 2]) { state++; - if (state == 3) { + if (state == 3 || (state == 2 && i >= 4)) { std::string candidate(word, word + i - 1); candidate.insert(candidate.end(), word + i + 1, word + wl); testsug(wlst, candidate, cpdsuggest, NULL, NULL); @@ -475,8 +527,11 @@ int SuggestMgr::doubletwochars(std::vector<std::string>& wlst, return wlst.size(); } -// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -// -> vacacation) +// perhaps we doubled two characters +// (for example vacation -> vacacation) +// The recognized pattern with regex back-references: +// "(.)(.)\1\2\1" or "..(.)(.)\1\2" + int SuggestMgr::doubletwochars_utf(std::vector<std::string>& wlst, const w_char* word, int wl, @@ -487,7 +542,7 @@ int SuggestMgr::doubletwochars_utf(std::vector<std::string>& wlst, for (int i = 2; i < wl; i++) { if (word[i] == word[i - 2]) { state++; - if (state == 3) { + if (state == 3 || (state == 2 && i >= 4)) { std::vector<w_char> candidate_utf(word, word + i - 1); candidate_utf.insert(candidate_utf.end(), word + i + 1, word + wl); std::string candidate; @@ -721,17 +776,22 @@ int SuggestMgr::forgotchar_utf(std::vector<std::string>& wlst, return wlst.size(); } -/* error is should have been two words */ -int SuggestMgr::twowords(std::vector<std::string>& wlst, +/* error is should have been two words + * return value is true, if there is a dictionary word pair, + * or there was already a good suggestion before calling + * this function. + */ +bool SuggestMgr::twowords(std::vector<std::string>& wlst, const char* word, - int cpdsuggest) { + int cpdsuggest, + bool good) { int c2; int forbidden = 0; int cwrd; int wl = strlen(word); if (wl < 3) - return wlst.size(); + return false; if (langnum == LANG_hu) forbidden = check_forbidden(word, wl); @@ -750,63 +810,87 @@ int SuggestMgr::twowords(std::vector<std::string>& wlst, } if (utf8 && p[1] == '\0') break; // last UTF-8 character - *p = '\0'; - int c1 = checkword(candidate, cpdsuggest, NULL, NULL); - if (c1) { - c2 = checkword((p + 1), cpdsuggest, NULL, NULL); - if (c2) { - *p = ' '; - - // spec. Hungarian code (need a better compound word support) - if ((langnum == LANG_hu) && !forbidden && - // if 3 repeating letter, use - instead of space - (((p[-1] == p[1]) && - (((p > candidate + 1) && (p[-1] == p[-2])) || (p[-1] == p[2]))) || - // or multiple compounding, with more, than 6 syllables - ((c1 == 3) && (c2 >= 2)))) - *p = '-'; - - cwrd = 1; - for (size_t k = 0; k < wlst.size(); ++k) { - if (wlst[k] == candidate) { - cwrd = 0; - break; - } - } - if (wlst.size() < maxSug) { - if (cwrd) { - wlst.push_back(candidate); - } - } else { - free(candidate); - return wlst.size(); + + // Suggest only word pairs, if they are listed in the dictionary. + // For example, adding "a lot" to the English dic file will + // result only "alot" -> "a lot" suggestion instead of + // "alto, slot, alt, lot, allot, aloft, aloe, clot, plot, blot, a lot". + // Note: using "ph:alot" keeps the other suggestions: + // a lot ph:alot + // alot -> a lot, alto, slot... + *p = ' '; + if (!cpdsuggest && checkword(candidate, cpdsuggest, NULL, NULL)) { + // remove not word pair suggestions + if (!good) { + good = true; + wlst.clear(); + } + wlst.insert(wlst.begin(), candidate); + } + + // word pairs with dash? + if (lang_with_dash_usage) { + *p = '-'; + + if (!cpdsuggest && checkword(candidate, cpdsuggest, NULL, NULL)) { + // remove not word pair suggestions + if (!good) { + good = true; + wlst.clear(); } - // add two word suggestion with dash, if TRY string contains - // "a" or "-" - // NOTE: cwrd doesn't modified for REP twoword sugg. - if (ctry && (strchr(ctry, 'a') || strchr(ctry, '-')) && - mystrlen(p + 1) > 1 && mystrlen(candidate) - mystrlen(p) > 1) { - *p = '-'; + wlst.insert(wlst.begin(), candidate); + } + } + + if (wlst.size() < maxSug && !nosplitsugs && !good) { + *p = '\0'; + int c1 = checkword(candidate, cpdsuggest, NULL, NULL); + if (c1) { + c2 = checkword((p + 1), cpdsuggest, NULL, NULL); + if (c2) { + // spec. Hungarian code (TODO need a better compound word support) + if ((langnum == LANG_hu) && !forbidden && + // if 3 repeating letter, use - instead of space + (((p[-1] == p[1]) && + (((p > candidate + 1) && (p[-1] == p[-2])) || (p[-1] == p[2]))) || + // or multiple compounding, with more, than 6 syllables + ((c1 == 3) && (c2 >= 2)))) + *p = '-'; + else + *p = ' '; + + cwrd = 1; for (size_t k = 0; k < wlst.size(); ++k) { if (wlst[k] == candidate) { cwrd = 0; break; } } - if (wlst.size() < maxSug) { - if (cwrd) { + + if (cwrd && (wlst.size() < maxSug)) wlst.push_back(candidate); + + // add two word suggestion with dash, depending on the language + // Note that cwrd doesn't modified for REP twoword sugg. + if ( !nosplitsugs && lang_with_dash_usage && + mystrlen(p + 1) > 1 && mystrlen(candidate) - mystrlen(p) > 1) { + *p = '-'; + for (size_t k = 0; k < wlst.size(); ++k) { + if (wlst[k] == candidate) { + cwrd = 0; + break; + } } - } else { - free(candidate); - return wlst.size(); + + if ((wlst.size() < maxSug) && cwrd) + wlst.push_back(candidate); } } } } } free(candidate); - return wlst.size(); + return good; } // error is adjacent letter were swapped @@ -891,7 +975,8 @@ int SuggestMgr::longswapchar(std::vector<std::string>& wlst, // try swapping not adjacent chars one by one for (std::string::iterator p = candidate.begin(); p < candidate.end(); ++p) { for (std::string::iterator q = candidate.begin(); q < candidate.end(); ++q) { - if (std::abs(std::distance(q, p)) > 1) { + size_t distance = std::abs(std::distance(q, p)); + if (distance > 1 && distance <= MAX_CHAR_DISTANCE) { std::swap(*p, *q); testsug(wlst, candidate, cpdsuggest, NULL, NULL); std::swap(*p, *q); @@ -910,7 +995,8 @@ int SuggestMgr::longswapchar_utf(std::vector<std::string>& wlst, // try swapping not adjacent chars for (std::vector<w_char>::iterator p = candidate_utf.begin(); p < candidate_utf.end(); ++p) { for (std::vector<w_char>::iterator q = candidate_utf.begin(); q < candidate_utf.end(); ++q) { - if (std::abs(std::distance(q, p)) > 1) { + size_t distance = std::abs(std::distance(q, p)); + if (distance > 1 && distance <= MAX_CHAR_DISTANCE) { std::swap(*p, *q); std::string candidate; u16_u8(candidate, candidate_utf); @@ -932,7 +1018,7 @@ int SuggestMgr::movechar(std::vector<std::string>& wlst, // try moving a char for (std::string::iterator p = candidate.begin(); p < candidate.end(); ++p) { - for (std::string::iterator q = p + 1; q < candidate.end() && std::distance(p, q) < 10; ++q) { + for (std::string::iterator q = p + 1; q < candidate.end() && std::distance(p, q) <= MAX_CHAR_DISTANCE; ++q) { std::swap(*q, *(q - 1)); if (std::distance(p, q) < 2) continue; // omit swap char @@ -942,7 +1028,7 @@ int SuggestMgr::movechar(std::vector<std::string>& wlst, } for (std::string::reverse_iterator p = candidate.rbegin(), pEnd = candidate.rend() - 1; p != pEnd; ++p) { - for (std::string::reverse_iterator q = p + 1, qEnd = candidate.rend(); q != qEnd && std::distance(p, q) < 10; ++q) { + for (std::string::reverse_iterator q = p + 1, qEnd = candidate.rend(); q != qEnd && std::distance(p, q) <= MAX_CHAR_DISTANCE; ++q) { std::swap(*q, *(q - 1)); if (std::distance(p, q) < 2) continue; // omit swap char @@ -965,7 +1051,7 @@ int SuggestMgr::movechar_utf(std::vector<std::string>& wlst, // try moving a char for (std::vector<w_char>::iterator p = candidate_utf.begin(); p < candidate_utf.end(); ++p) { - for (std::vector<w_char>::iterator q = p + 1; q < candidate_utf.end() && std::distance(p, q) < 10; ++q) { + for (std::vector<w_char>::iterator q = p + 1; q < candidate_utf.end() && std::distance(p, q) <= MAX_CHAR_DISTANCE; ++q) { std::swap(*q, *(q - 1)); if (std::distance(p, q) < 2) continue; // omit swap char @@ -977,7 +1063,7 @@ int SuggestMgr::movechar_utf(std::vector<std::string>& wlst, } for (std::vector<w_char>::reverse_iterator p = candidate_utf.rbegin(); p < candidate_utf.rend(); ++p) { - for (std::vector<w_char>::reverse_iterator q = p + 1; q < candidate_utf.rend() && std::distance(p, q) < 10; ++q) { + for (std::vector<w_char>::reverse_iterator q = p + 1; q < candidate_utf.rend() && std::distance(p, q) <= MAX_CHAR_DISTANCE; ++q) { std::swap(*q, *(q - 1)); if (std::distance(p, q) < 2) continue; // omit swap char @@ -994,7 +1080,8 @@ int SuggestMgr::movechar_utf(std::vector<std::string>& wlst, // generate a set of suggestions for very poorly spelled words void SuggestMgr::ngsuggest(std::vector<std::string>& wlst, const char* w, - const std::vector<HashMgr*>& rHMgr) { + const std::vector<HashMgr*>& rHMgr, + int captype) { int lval; int sc; int lp, lpphon; @@ -1071,18 +1158,34 @@ void SuggestMgr::ngsuggest(std::vector<std::string>& wlst, u8_u16(w_word, word); u8_u16(w_target, target); } - + std::string f; std::vector<w_char> w_f; - + for (size_t i = 0; i < rHMgr.size(); ++i) { while (0 != (hp = rHMgr[i]->walk_hashtable(col, hp))) { - if ((hp->astr) && (pAMgr) && - (TESTAFF(hp->astr, forbiddenword, hp->alen) || - TESTAFF(hp->astr, ONLYUPCASEFLAG, hp->alen) || - TESTAFF(hp->astr, nosuggest, hp->alen) || - TESTAFF(hp->astr, nongramsuggest, hp->alen) || - TESTAFF(hp->astr, onlyincompound, hp->alen))) + // skip exceptions + if ( + // skip it, if the word length different by 5 or + // more characters (to avoid strange suggestions) + // (except Unicode characters over BMP) + (((abs(n - hp->clen) > 4) && !nonbmp)) || + // don't suggest capitalized dictionary words for + // lower case misspellings in ngram suggestions, except + // - PHONE usage, or + // - in the case of German, where not only proper + // nouns are capitalized, or + // - the capitalized word has special pronunciation + ((captype == NOCAP) && (hp->var & H_OPT_INITCAP) && + !ph && (langnum != LANG_de) && !(hp->var & H_OPT_PHON)) || + // or it has one of the following special flags + ((hp->astr) && (pAMgr) && + (TESTAFF(hp->astr, forbiddenword, hp->alen) || + TESTAFF(hp->astr, ONLYUPCASEFLAG, hp->alen) || + TESTAFF(hp->astr, nosuggest, hp->alen) || + TESTAFF(hp->astr, nongramsuggest, hp->alen) || + TESTAFF(hp->astr, onlyincompound, hp->alen))) + ) continue; if (utf8) { @@ -1105,7 +1208,7 @@ void SuggestMgr::ngsuggest(std::vector<std::string>& wlst, sc = ngram(3, word, f, NGRAM_LONGER_WORSE) + leftcommon; } - // check special pronounciation + // check special pronunciation f.clear(); if ((hp->var & H_OPT_PHON) && copy_field(f, HENTRY_DATA(hp), MORPH_PHON)) { @@ -1559,7 +1662,8 @@ int SuggestMgr::checkword(const std::string& word, if (rv) { if ((rv->astr) && (TESTAFF(rv->astr, pAMgr->get_forbiddenword(), rv->alen) || - TESTAFF(rv->astr, pAMgr->get_nosuggest(), rv->alen))) + TESTAFF(rv->astr, pAMgr->get_nosuggest(), rv->alen) || + TESTAFF(rv->astr, pAMgr->get_substandard(), rv->alen))) return 0; while (rv) { if (rv->astr && @@ -1584,7 +1688,7 @@ int SuggestMgr::checkword(const std::string& word, if (!rv && pAMgr->have_contclass()) { rv = pAMgr->suffix_check_twosfx(word.c_str(), word.size(), 0, NULL, FLAG_NULL); if (!rv) - rv = pAMgr->prefix_check_twosfx(word.c_str(), word.size(), 1, FLAG_NULL); + rv = pAMgr->prefix_check_twosfx(word.c_str(), word.size(), 0, FLAG_NULL); } // check forbidden words @@ -1649,15 +1753,15 @@ std::string SuggestMgr::suggest_morph(const std::string& in_w) { TESTAFF(rv->astr, pAMgr->get_needaffix(), rv->alen) || TESTAFF(rv->astr, pAMgr->get_onlyincompound(), rv->alen))) { if (!HENTRY_FIND(rv, MORPH_STEM)) { - result.append(" "); + result.push_back(MSEP_FLD); result.append(MORPH_STEM); result.append(w); } if (HENTRY_DATA(rv)) { - result.append(" "); + result.push_back(MSEP_FLD); result.append(HENTRY_DATA2(rv)); } - result.append("\n"); + result.push_back(MSEP_REC); } rv = rv->next_homonym; } @@ -1713,7 +1817,7 @@ std::string SuggestMgr::suggest_hentry_gen(hentry* rv, const char* pattern) { HENTRY_DATA(rv), pattern, 0); if (!aff.empty()) { result.append(aff); - result.append("\n"); + result.push_back(MSEP_REC); } } @@ -1737,7 +1841,7 @@ std::string SuggestMgr::suggest_hentry_gen(hentry* rv, const char* pattern) { rv2->alen, HENTRY_DATA(rv2), pattern, 0); if (!aff.empty()) { result.append(aff); - result.append("\n"); + result.push_back(MSEP_REC); } } } @@ -1936,7 +2040,7 @@ int SuggestMgr::leftcommonsubstring( int l2 = su2.size(); // decapitalize dictionary word if (complexprefixes) { - if (su1[l1 - 1] == su2[l2 - 1]) + if (l1 && l2 && su1[l1 - 1] == su2[l2 - 1]) return 1; } else { unsigned short idx = su2.empty() ? 0 : (su2[0].h << 8) + su2[0].l; |
