Refactored: Reuse existing code
[TortoiseGit.git] / ext / hunspell / suggestmgr.cxx
blob04c317715f97d92e6a63d01631c7fef470f5240b
1 #include "license.hunspell"
2 #include "license.myspell"
4 #include <stdlib.h>
5 #include <string.h>
6 #include <stdio.h>
7 #include <ctype.h>
9 #include "suggestmgr.hxx"
10 #include "htypes.hxx"
11 #include "csutil.hxx"
13 const w_char W_VLINE = { '\0', '|' };
15 SuggestMgr::SuggestMgr(const char * tryme, int maxn,
16 AffixMgr * aptr)
19 // register affix manager and check in string of chars to
20 // try when building candidate suggestions
21 pAMgr = aptr;
23 csconv = get_current_cs(SPELL_ENCODING);
25 ckeyl = 0;
26 ckey = NULL;
27 ckey_utf = NULL;
29 ctryl = 0;
30 ctry = NULL;
31 ctry_utf = NULL;
33 utf8 = 0;
34 langnum = 0;
35 complexprefixes = 0;
37 maxSug = maxn;
38 nosplitsugs = 0;
39 maxngramsugs = MAXNGRAMSUGS;
40 maxcpdsugs = MAXCOMPOUNDSUGS;
42 if (pAMgr) {
43 langnum = pAMgr->get_langnum();
44 ckey = pAMgr->get_key_string();
45 nosplitsugs = pAMgr->get_nosplitsugs();
46 if (pAMgr->get_maxngramsugs() >= 0)
47 maxngramsugs = pAMgr->get_maxngramsugs();
48 utf8 = pAMgr->get_utf8();
49 if (pAMgr->get_maxcpdsugs() >= 0)
50 maxcpdsugs = pAMgr->get_maxcpdsugs();
51 if (!utf8)
53 char * enc = pAMgr->get_encoding();
54 csconv = get_current_cs(enc);
55 free(enc);
57 complexprefixes = pAMgr->get_complexprefixes();
60 if (ckey) {
61 if (utf8) {
62 w_char t[MAXSWL];
63 ckeyl = u8_u16(t, MAXSWL, ckey);
64 ckey_utf = (w_char *) malloc(ckeyl * sizeof(w_char));
65 if (ckey_utf) memcpy(ckey_utf, t, ckeyl * sizeof(w_char));
66 else ckeyl = 0;
67 } else {
68 ckeyl = strlen(ckey);
72 if (tryme) {
73 ctry = mystrdup(tryme);
74 if (ctry) ctryl = strlen(ctry);
75 if (ctry && utf8) {
76 w_char t[MAXSWL];
77 ctryl = u8_u16(t, MAXSWL, tryme);
78 ctry_utf = (w_char *) malloc(ctryl * sizeof(w_char));
79 if (ctry_utf) memcpy(ctry_utf, t, ctryl * sizeof(w_char));
80 else ctryl = 0;
86 SuggestMgr::~SuggestMgr()
88 pAMgr = NULL;
89 if (ckey) free(ckey);
90 ckey = NULL;
91 if (ckey_utf) free(ckey_utf);
92 ckey_utf = NULL;
93 ckeyl = 0;
94 if (ctry) free(ctry);
95 ctry = NULL;
96 if (ctry_utf) free(ctry_utf);
97 ctry_utf = NULL;
98 ctryl = 0;
99 maxSug = 0;
100 #ifdef MOZILLA_CLIENT
101 delete [] csconv;
102 #endif
105 int SuggestMgr::testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest,
106 int * timer, clock_t * timelimit) {
107 int cwrd = 1;
108 if (ns == maxSug) return maxSug;
109 for (int k=0; k < ns; k++) {
110 if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;
112 if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) {
113 wlst[ns] = mystrdup(candidate);
114 if (wlst[ns] == NULL) {
115 for (int j=0; j<ns; j++) free(wlst[j]);
116 return -1;
118 ns++;
120 return ns;
123 // generate suggestions for a misspelled word
124 // pass in address of array of char * pointers
125 // onlycompoundsug: probably bad suggestions (need for ngram sugs, too)
127 int SuggestMgr::suggest(char*** slst, const char * w, int nsug,
128 int * onlycompoundsug)
130 int nocompoundtwowords = 0;
131 char ** wlst;
132 w_char word_utf[MAXSWL];
133 int wl = 0;
134 int nsugorig = nsug;
135 char w2[MAXWORDUTF8LEN];
136 const char * word = w;
137 int oldSug = 0;
139 // word reversing wrapper for complex prefixes
140 if (complexprefixes) {
141 strcpy(w2, w);
142 if (utf8) reverseword_utf(w2); else reverseword(w2);
143 word = w2;
146 if (*slst) {
147 wlst = *slst;
148 } else {
149 wlst = (char **) malloc(maxSug * sizeof(char *));
150 if (wlst == NULL) return -1;
151 for (int i = 0; i < maxSug; i++) {
152 wlst[i] = NULL;
156 if (utf8) {
157 wl = u8_u16(word_utf, MAXSWL, word);
158 if (wl == -1) {
159 *slst = wlst;
160 return nsug;
164 for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) {
166 // limit compound suggestion
167 if (cpdsuggest > 0) oldSug = nsug;
169 // suggestions for an uppercase word (html -> HTML)
170 if ((nsug < maxSug) && (nsug > -1)) {
171 nsug = (utf8) ? capchars_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
172 capchars(wlst, word, nsug, cpdsuggest);
175 // perhaps we made a typical fault of spelling
176 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
177 nsug = replchars(wlst, word, nsug, cpdsuggest);
180 // perhaps we made chose the wrong char from a related set
181 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
182 nsug = mapchars(wlst, word, nsug, cpdsuggest);
185 // only suggest compound words when no other suggestion
186 if ((cpdsuggest == 0) && (nsug > nsugorig)) nocompoundtwowords=1;
188 // did we swap the order of chars by mistake
189 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
190 nsug = (utf8) ? swapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
191 swapchar(wlst, word, nsug, cpdsuggest);
194 // did we swap the order of non adjacent chars by mistake
195 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
196 nsug = (utf8) ? longswapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
197 longswapchar(wlst, word, nsug, cpdsuggest);
200 // did we just hit the wrong key in place of a good char (case and keyboard)
201 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
202 nsug = (utf8) ? badcharkey_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
203 badcharkey(wlst, word, nsug, cpdsuggest);
206 // did we add a char that should not be there
207 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
208 nsug = (utf8) ? extrachar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
209 extrachar(wlst, word, nsug, cpdsuggest);
213 // did we forgot a char
214 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
215 nsug = (utf8) ? forgotchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
216 forgotchar(wlst, word, nsug, cpdsuggest);
219 // did we move a char
220 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
221 nsug = (utf8) ? movechar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
222 movechar(wlst, word, nsug, cpdsuggest);
225 // did we just hit the wrong key in place of a good char
226 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
227 nsug = (utf8) ? badchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
228 badchar(wlst, word, nsug, cpdsuggest);
231 // did we double two characters
232 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
233 nsug = (utf8) ? doubletwochars_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
234 doubletwochars(wlst, word, nsug, cpdsuggest);
237 // perhaps we forgot to hit space and two words ran together
238 if (!nosplitsugs && (nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) {
239 nsug = twowords(wlst, word, nsug, cpdsuggest);
242 } // repeating ``for'' statement compounding support
244 if (nsug < 0) {
245 // we ran out of memory - we should free up as much as possible
246 for (int i = 0; i < maxSug; i++)
247 if (wlst[i] != NULL) free(wlst[i]);
248 free(wlst);
249 wlst = NULL;
252 if (!nocompoundtwowords && (nsug > 0) && onlycompoundsug) *onlycompoundsug = 1;
254 *slst = wlst;
255 return nsug;
258 // generate suggestions for a word with typical mistake
259 // pass in address of array of char * pointers
260 #ifdef HUNSPELL_EXPERIMENTAL
261 int SuggestMgr::suggest_auto(char*** slst, const char * w, int nsug)
263 int nocompoundtwowords = 0;
264 char ** wlst;
265 int oldSug;
267 char w2[MAXWORDUTF8LEN];
268 const char * word = w;
270 // word reversing wrapper for complex prefixes
271 if (complexprefixes) {
272 strcpy(w2, w);
273 if (utf8) reverseword_utf(w2); else reverseword(w2);
274 word = w2;
277 if (*slst) {
278 wlst = *slst;
279 } else {
280 wlst = (char **) malloc(maxSug * sizeof(char *));
281 if (wlst == NULL) return -1;
284 for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) {
286 // limit compound suggestion
287 if (cpdsuggest > 0) oldSug = nsug;
289 // perhaps we made a typical fault of spelling
290 if ((nsug < maxSug) && (nsug > -1))
291 nsug = replchars(wlst, word, nsug, cpdsuggest);
293 // perhaps we made chose the wrong char from a related set
294 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs)))
295 nsug = mapchars(wlst, word, nsug, cpdsuggest);
297 if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1;
299 // perhaps we forgot to hit space and two words ran together
301 if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs)) && check_forbidden(word, strlen(word))) {
302 nsug = twowords(wlst, word, nsug, cpdsuggest);
305 } // repeating ``for'' statement compounding support
307 if (nsug < 0) {
308 for (int i=0;i<maxSug; i++)
309 if (wlst[i] != NULL) free(wlst[i]);
310 free(wlst);
311 return -1;
314 *slst = wlst;
315 return nsug;
317 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
319 // suggestions for an uppercase word (html -> HTML)
320 int SuggestMgr::capchars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
322 char candidate[MAXSWUTF8L];
323 w_char candidate_utf[MAXSWL];
324 memcpy(candidate_utf, word, wl * sizeof(w_char));
325 mkallcap_utf(candidate_utf, wl, langnum);
326 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
327 return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
330 // suggestions for an uppercase word (html -> HTML)
331 int SuggestMgr::capchars(char** wlst, const char * word, int ns, int cpdsuggest)
333 char candidate[MAXSWUTF8L];
334 strcpy(candidate, word);
335 mkallcap(candidate, csconv);
336 return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
339 // suggestions for when chose the wrong char out of a related set
340 int SuggestMgr::mapchars(char** wlst, const char * word, int ns, int cpdsuggest)
342 char candidate[MAXSWUTF8L];
343 clock_t timelimit;
344 int timer;
345 candidate[0] = '\0';
347 int wl = strlen(word);
348 if (wl < 2 || ! pAMgr) return ns;
350 int nummap = pAMgr->get_nummap();
351 struct mapentry* maptable = pAMgr->get_maptable();
352 if (maptable==NULL) return ns;
354 timelimit = clock();
355 timer = MINTIMER;
356 return map_related(word, (char *) &candidate, 0, 0, wlst, cpdsuggest, ns, maptable, nummap, &timer, &timelimit);
359 int SuggestMgr::map_related(const char * word, char * candidate, int wn, int cn,
360 char** wlst, int cpdsuggest, int ns,
361 const mapentry* maptable, int nummap, int * timer, clock_t * timelimit)
363 if (*(word + wn) == '\0') {
364 int cwrd = 1;
365 *(candidate + cn) = '\0';
366 int wl = strlen(candidate);
367 for (int m=0; m < ns; m++)
368 if (strcmp(candidate, wlst[m]) == 0) cwrd = 0;
369 if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) {
370 if (ns < maxSug) {
371 wlst[ns] = mystrdup(candidate);
372 if (wlst[ns] == NULL) return -1;
373 ns++;
376 return ns;
378 int in_map = 0;
379 for (int j = 0; j < nummap; j++) {
380 for (int k = 0; k < maptable[j].len; k++) {
381 int len = strlen(maptable[j].set[k]);
382 if (strncmp(maptable[j].set[k], word + wn, len) == 0) {
383 in_map = 1;
384 for (int l = 0; l < maptable[j].len; l++) {
385 strcpy(candidate + cn, maptable[j].set[l]);
386 ns = map_related(word, candidate, wn + len, strlen(candidate), wlst,
387 cpdsuggest, ns, maptable, nummap, timer, timelimit);
388 if (!(*timer)) return ns;
393 if (!in_map) {
394 *(candidate + cn) = *(word + wn);
395 ns = map_related(word, candidate, wn + 1, cn + 1, wlst, cpdsuggest,
396 ns, maptable, nummap, timer, timelimit);
398 return ns;
401 // suggestions for a typical fault of spelling, that
402 // differs with more, than 1 letter from the right form.
403 int SuggestMgr::replchars(char** wlst, const char * word, int ns, int cpdsuggest)
405 char candidate[MAXSWUTF8L];
406 const char * r;
407 int lenr, lenp;
408 int wl = strlen(word);
409 if (wl < 2 || ! pAMgr) return ns;
410 int numrep = pAMgr->get_numrep();
411 struct replentry* reptable = pAMgr->get_reptable();
412 if (reptable==NULL) return ns;
413 for (int i=0; i < numrep; i++ ) {
414 r = word;
415 lenr = strlen(reptable[i].pattern2);
416 lenp = strlen(reptable[i].pattern);
417 // search every occurence of the pattern in the word
418 while ((r=strstr(r, reptable[i].pattern)) != NULL && (!reptable[i].end || strlen(r) == strlen(reptable[i].pattern)) &&
419 (!reptable[i].start || r == word)) {
420 strcpy(candidate, word);
421 if (r-word + lenr + strlen(r+lenp) >= MAXSWUTF8L) break;
422 strcpy(candidate+(r-word),reptable[i].pattern2);
423 strcpy(candidate+(r-word)+lenr, r+lenp);
424 ns = testsug(wlst, candidate, wl-lenp+lenr, ns, cpdsuggest, NULL, NULL);
425 if (ns == -1) return -1;
426 // check REP suggestions with space
427 char * sp = strchr(candidate, ' ');
428 if (sp) {
429 char * prev = candidate;
430 while (sp) {
431 *sp = '\0';
432 if (checkword(prev, strlen(prev), 0, NULL, NULL)) {
433 int oldns = ns;
434 *sp = ' ';
435 ns = testsug(wlst, sp + 1, strlen(sp + 1), ns, cpdsuggest, NULL, NULL);
436 if (ns == -1) return -1;
437 if (oldns < ns) {
438 free(wlst[ns - 1]);
439 wlst[ns - 1] = mystrdup(candidate);
440 if (!wlst[ns - 1]) return -1;
443 *sp = ' ';
444 prev = sp + 1;
445 sp = strchr(prev, ' ');
448 r++; // search for the next letter
451 return ns;
454 // perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation)
455 int SuggestMgr::doubletwochars(char** wlst, const char * word, int ns, int cpdsuggest)
457 char candidate[MAXSWUTF8L];
458 int state=0;
459 int wl = strlen(word);
460 if (wl < 5 || ! pAMgr) return ns;
461 for (int i=2; i < wl; i++ ) {
462 if (word[i]==word[i-2]) {
463 state++;
464 if (state==3) {
465 strcpy(candidate,word);
466 strcpy(candidate+i-1,word+i+1);
467 ns = testsug(wlst, candidate, wl-2, ns, cpdsuggest, NULL, NULL);
468 if (ns == -1) return -1;
469 state=0;
471 } else {
472 state=0;
475 return ns;
478 // perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation)
479 int SuggestMgr::doubletwochars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
481 w_char candidate_utf[MAXSWL];
482 char candidate[MAXSWUTF8L];
483 int state=0;
484 if (wl < 5 || ! pAMgr) return ns;
485 for (int i=2; i < wl; i++) {
486 if (w_char_eq(word[i], word[i-2])) {
487 state++;
488 if (state==3) {
489 memcpy(candidate_utf, word, (i - 1) * sizeof(w_char));
490 memcpy(candidate_utf+i-1, word+i+1, (wl-i-1) * sizeof(w_char));
491 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl-2);
492 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
493 if (ns == -1) return -1;
494 state=0;
496 } else {
497 state=0;
500 return ns;
503 // error is wrong char in place of correct one (case and keyboard related version)
504 int SuggestMgr::badcharkey(char ** wlst, const char * word, int ns, int cpdsuggest)
506 char tmpc;
507 char candidate[MAXSWUTF8L];
508 int wl = strlen(word);
509 strcpy(candidate, word);
510 // swap out each char one by one and try uppercase and neighbor
511 // keyboard chars in its place to see if that makes a good word
513 for (int i=0; i < wl; i++) {
514 tmpc = candidate[i];
515 // check with uppercase letters
516 candidate[i] = csconv[((unsigned char)tmpc)].cupper;
517 if (tmpc != candidate[i]) {
518 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
519 if (ns == -1) return -1;
520 candidate[i] = tmpc;
522 // check neighbor characters in keyboard string
523 if (!ckey) continue;
524 char * loc = strchr(ckey, tmpc);
525 while (loc) {
526 if ((loc > ckey) && (*(loc - 1) != '|')) {
527 candidate[i] = *(loc - 1);
528 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
529 if (ns == -1) return -1;
531 if ((*(loc + 1) != '|') && (*(loc + 1) != '\0')) {
532 candidate[i] = *(loc + 1);
533 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
534 if (ns == -1) return -1;
536 loc = strchr(loc + 1, tmpc);
538 candidate[i] = tmpc;
540 return ns;
543 // error is wrong char in place of correct one (case and keyboard related version)
544 int SuggestMgr::badcharkey_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
546 w_char tmpc;
547 w_char candidate_utf[MAXSWL];
548 char candidate[MAXSWUTF8L];
549 memcpy(candidate_utf, word, wl * sizeof(w_char));
550 // swap out each char one by one and try all the tryme
551 // chars in its place to see if that makes a good word
552 for (int i=0; i < wl; i++) {
553 tmpc = candidate_utf[i];
554 // check with uppercase letters
555 mkallcap_utf(candidate_utf + i, 1, langnum);
556 if (!w_char_eq(tmpc, candidate_utf[i])) {
557 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
558 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
559 if (ns == -1) return -1;
560 candidate_utf[i] = tmpc;
562 // check neighbor characters in keyboard string
563 if (!ckey) continue;
564 w_char * loc = ckey_utf;
565 while ((loc < (ckey_utf + ckeyl)) && !w_char_eq(*loc, tmpc)) loc++;
566 while (loc < (ckey_utf + ckeyl)) {
567 if ((loc > ckey_utf) && !w_char_eq(*(loc - 1), W_VLINE)) {
568 candidate_utf[i] = *(loc - 1);
569 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
570 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
571 if (ns == -1) return -1;
573 if (((loc + 1) < (ckey_utf + ckeyl)) && !w_char_eq(*(loc + 1), W_VLINE)) {
574 candidate_utf[i] = *(loc + 1);
575 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
576 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
577 if (ns == -1) return -1;
579 do { loc++; } while ((loc < (ckey_utf + ckeyl)) && !w_char_eq(*loc, tmpc));
581 candidate_utf[i] = tmpc;
583 return ns;
586 // error is wrong char in place of correct one
587 int SuggestMgr::badchar(char ** wlst, const char * word, int ns, int cpdsuggest)
589 char tmpc;
590 char candidate[MAXSWUTF8L];
591 clock_t timelimit = clock();
592 int timer = MINTIMER;
593 int wl = strlen(word);
594 strcpy(candidate, word);
595 // swap out each char one by one and try all the tryme
596 // chars in its place to see if that makes a good word
597 for (int j=0; j < ctryl; j++) {
598 for (int i=wl-1; i >= 0; i--) {
599 tmpc = candidate[i];
600 if (ctry[j] == tmpc) continue;
601 candidate[i] = ctry[j];
602 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, &timer, &timelimit);
603 if (ns == -1) return -1;
604 if (!timer) return ns;
605 candidate[i] = tmpc;
608 return ns;
611 // error is wrong char in place of correct one
612 int SuggestMgr::badchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
614 w_char tmpc;
615 w_char candidate_utf[MAXSWL];
616 char candidate[MAXSWUTF8L];
617 clock_t timelimit = clock();
618 int timer = MINTIMER;
619 memcpy(candidate_utf, word, wl * sizeof(w_char));
620 // swap out each char one by one and try all the tryme
621 // chars in its place to see if that makes a good word
622 for (int j=0; j < ctryl; j++) {
623 for (int i=wl-1; i >= 0; i--) {
624 tmpc = candidate_utf[i];
625 if (w_char_eq(tmpc, ctry_utf[j])) continue;
626 candidate_utf[i] = ctry_utf[j];
627 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
628 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit);
629 if (ns == -1) return -1;
630 if (!timer) return ns;
631 candidate_utf[i] = tmpc;
634 return ns;
637 // error is word has an extra letter it does not need
638 int SuggestMgr::extrachar_utf(char** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
640 char candidate[MAXSWUTF8L];
641 w_char candidate_utf[MAXSWL];
642 w_char * p;
643 w_char tmpc = W_VLINE; // not used value, only for VCC warning message
644 if (wl < 2) return ns;
645 // try omitting one char of word at a time
646 memcpy(candidate_utf, word, wl * sizeof(w_char));
647 for (p = candidate_utf + wl - 1; p >= candidate_utf; p--) {
648 w_char tmpc2 = *p;
649 if (p < candidate_utf + wl - 1) *p = tmpc;
650 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl - 1);
651 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
652 if (ns == -1) return -1;
653 tmpc = tmpc2;
655 return ns;
658 // error is word has an extra letter it does not need
659 int SuggestMgr::extrachar(char** wlst, const char * word, int ns, int cpdsuggest)
661 char tmpc = '\0';
662 char candidate[MAXSWUTF8L];
663 char * p;
664 int wl = strlen(word);
665 if (wl < 2) return ns;
666 // try omitting one char of word at a time
667 strcpy (candidate, word);
668 for (p = candidate + wl - 1; p >=candidate; p--) {
669 char tmpc2 = *p;
670 *p = tmpc;
671 ns = testsug(wlst, candidate, wl-1, ns, cpdsuggest, NULL, NULL);
672 if (ns == -1) return -1;
673 tmpc = tmpc2;
675 return ns;
678 // error is missing a letter it needs
679 int SuggestMgr::forgotchar(char ** wlst, const char * word, int ns, int cpdsuggest)
681 char candidate[MAXSWUTF8L];
682 char * p;
683 clock_t timelimit = clock();
684 int timer = MINTIMER;
685 int wl = strlen(word);
686 // try inserting a tryme character before every letter (and the null terminator)
687 for (int i = 0; i < ctryl; i++) {
688 strcpy(candidate, word);
689 for (p = candidate + wl; p >= candidate; p--) {
690 *(p+1) = *p;
691 *p = ctry[i];
692 ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, &timer, &timelimit);
693 if (ns == -1) return -1;
694 if (!timer) return ns;
697 return ns;
700 // error is missing a letter it needs
701 int SuggestMgr::forgotchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
703 w_char candidate_utf[MAXSWL];
704 char candidate[MAXSWUTF8L];
705 w_char * p;
706 clock_t timelimit = clock();
707 int timer = MINTIMER;
708 // try inserting a tryme character at the end of the word and before every letter
709 for (int i = 0; i < ctryl; i++) {
710 memcpy (candidate_utf, word, wl * sizeof(w_char));
711 for (p = candidate_utf + wl; p >= candidate_utf; p--) {
712 *(p + 1) = *p;
713 *p = ctry_utf[i];
714 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1);
715 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit);
716 if (ns == -1) return -1;
717 if (!timer) return ns;
720 return ns;
724 /* error is should have been two words */
725 int SuggestMgr::twowords(char ** wlst, const char * word, int ns, int cpdsuggest)
727 char candidate[MAXSWUTF8L];
728 char * p;
729 int c1, c2;
730 int forbidden = 0;
731 int cwrd;
733 int wl=strlen(word);
734 if (wl < 3) return ns;
736 if (langnum == LANG_hu) forbidden = check_forbidden(word, wl);
738 strcpy(candidate + 1, word);
739 // split the string into two pieces after every char
740 // if both pieces are good words make them a suggestion
741 for (p = candidate + 1; p[1] != '\0'; p++) {
742 p[-1] = *p;
743 // go to end of the UTF-8 character
744 while (utf8 && ((p[1] & 0xc0) == 0x80)) {
745 *p = p[1];
746 p++;
748 if (utf8 && p[1] == '\0') break; // last UTF-8 character
749 *p = '\0';
750 c1 = checkword(candidate,strlen(candidate), cpdsuggest, NULL, NULL);
751 if (c1) {
752 c2 = checkword((p+1),strlen(p+1), cpdsuggest, NULL, NULL);
753 if (c2) {
754 *p = ' ';
756 // spec. Hungarian code (need a better compound word support)
757 if ((langnum == LANG_hu) && !forbidden &&
758 // if 3 repeating letter, use - instead of space
759 (((p[-1] == p[1]) && (((p>candidate+1) && (p[-1] == p[-2])) || (p[-1] == p[2]))) ||
760 // or multiple compounding, with more, than 6 syllables
761 ((c1 == 3) && (c2 >= 2)))) *p = '-';
763 cwrd = 1;
764 for (int k=0; k < ns; k++)
765 if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;
766 if (ns < maxSug) {
767 if (cwrd) {
768 wlst[ns] = mystrdup(candidate);
769 if (wlst[ns] == NULL) return -1;
770 ns++;
772 } else return ns;
773 // add two word suggestion with dash, if TRY string contains
774 // "a" or "-"
775 // NOTE: cwrd doesn't modified for REP twoword sugg.
776 if (ctry && (strchr(ctry, 'a') || strchr(ctry, '-')) &&
777 mystrlen(p + 1) > 1 &&
778 mystrlen(candidate) - mystrlen(p) > 1) {
779 *p = '-';
780 for (int k=0; k < ns; k++)
781 if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;
782 if (ns < maxSug) {
783 if (cwrd) {
784 wlst[ns] = mystrdup(candidate);
785 if (wlst[ns] == NULL) return -1;
786 ns++;
788 } else return ns;
793 return ns;
797 // error is adjacent letter were swapped
798 int SuggestMgr::swapchar(char ** wlst, const char * word, int ns, int cpdsuggest)
800 char candidate[MAXSWUTF8L];
801 char * p;
802 char tmpc;
803 int wl=strlen(word);
804 // try swapping adjacent chars one by one
805 strcpy(candidate, word);
806 for (p = candidate; p[1] != 0; p++) {
807 tmpc = *p;
808 *p = p[1];
809 p[1] = tmpc;
810 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
811 if (ns == -1) return -1;
812 p[1] = *p;
813 *p = tmpc;
815 // try double swaps for short words
816 // ahev -> have, owudl -> would
817 if (wl == 4 || wl == 5) {
818 candidate[0] = word[1];
819 candidate[1] = word[0];
820 candidate[2] = word[2];
821 candidate[wl - 2] = word[wl - 1];
822 candidate[wl - 1] = word[wl - 2];
823 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
824 if (ns == -1) return -1;
825 if (wl == 5) {
826 candidate[0] = word[0];
827 candidate[1] = word[2];
828 candidate[2] = word[1];
829 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
830 if (ns == -1) return -1;
833 return ns;
836 // error is adjacent letter were swapped
837 int SuggestMgr::swapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
839 w_char candidate_utf[MAXSWL];
840 char candidate[MAXSWUTF8L];
841 w_char * p;
842 w_char tmpc;
843 int len = 0;
844 // try swapping adjacent chars one by one
845 memcpy (candidate_utf, word, wl * sizeof(w_char));
846 for (p = candidate_utf; p < (candidate_utf + wl - 1); p++) {
847 tmpc = *p;
848 *p = p[1];
849 p[1] = tmpc;
850 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
851 if (len == 0) len = strlen(candidate);
852 ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL);
853 if (ns == -1) return -1;
854 p[1] = *p;
855 *p = tmpc;
857 // try double swaps for short words
858 // ahev -> have, owudl -> would, suodn -> sound
859 if (wl == 4 || wl == 5) {
860 candidate_utf[0] = word[1];
861 candidate_utf[1] = word[0];
862 candidate_utf[2] = word[2];
863 candidate_utf[wl - 2] = word[wl - 1];
864 candidate_utf[wl - 1] = word[wl - 2];
865 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
866 ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL);
867 if (ns == -1) return -1;
868 if (wl == 5) {
869 candidate_utf[0] = word[0];
870 candidate_utf[1] = word[2];
871 candidate_utf[2] = word[1];
872 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
873 ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL);
874 if (ns == -1) return -1;
877 return ns;
880 // error is not adjacent letter were swapped
881 int SuggestMgr::longswapchar(char ** wlst, const char * word, int ns, int cpdsuggest)
883 char candidate[MAXSWUTF8L];
884 char * p;
885 char * q;
886 char tmpc;
887 int wl=strlen(word);
888 // try swapping not adjacent chars one by one
889 strcpy(candidate, word);
890 for (p = candidate; *p != 0; p++) {
891 for (q = candidate; *q != 0; q++) {
892 if (abs((int)(p-q)) > 1) {
893 tmpc = *p;
894 *p = *q;
895 *q = tmpc;
896 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
897 if (ns == -1) return -1;
898 *q = *p;
899 *p = tmpc;
903 return ns;
907 // error is adjacent letter were swapped
908 int SuggestMgr::longswapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
910 w_char candidate_utf[MAXSWL];
911 char candidate[MAXSWUTF8L];
912 w_char * p;
913 w_char * q;
914 w_char tmpc;
915 // try swapping not adjacent chars
916 memcpy (candidate_utf, word, wl * sizeof(w_char));
917 for (p = candidate_utf; p < (candidate_utf + wl); p++) {
918 for (q = candidate_utf; q < (candidate_utf + wl); q++) {
919 if (abs((int)(p-q)) > 1) {
920 tmpc = *p;
921 *p = *q;
922 *q = tmpc;
923 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
924 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
925 if (ns == -1) return -1;
926 *q = *p;
927 *p = tmpc;
931 return ns;
934 // error is a letter was moved
935 int SuggestMgr::movechar(char ** wlst, const char * word, int ns, int cpdsuggest)
937 char candidate[MAXSWUTF8L];
938 char * p;
939 char * q;
940 char tmpc;
942 int wl=strlen(word);
943 // try moving a char
944 strcpy(candidate, word);
945 for (p = candidate; *p != 0; p++) {
946 for (q = p + 1; (*q != 0) && ((q - p) < 10); q++) {
947 tmpc = *(q-1);
948 *(q-1) = *q;
949 *q = tmpc;
950 if ((q-p) < 2) continue; // omit swap char
951 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
952 if (ns == -1) return -1;
954 strcpy(candidate, word);
956 for (p = candidate + wl - 1; p > candidate; p--) {
957 for (q = p - 1; (q >= candidate) && ((p - q) < 10); q--) {
958 tmpc = *(q+1);
959 *(q+1) = *q;
960 *q = tmpc;
961 if ((p-q) < 2) continue; // omit swap char
962 ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
963 if (ns == -1) return -1;
965 strcpy(candidate, word);
967 return ns;
970 // error is a letter was moved
971 int SuggestMgr::movechar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
973 w_char candidate_utf[MAXSWL];
974 char candidate[MAXSWUTF8L];
975 w_char * p;
976 w_char * q;
977 w_char tmpc;
978 // try moving a char
979 memcpy (candidate_utf, word, wl * sizeof(w_char));
980 for (p = candidate_utf; p < (candidate_utf + wl); p++) {
981 for (q = p + 1; (q < (candidate_utf + wl)) && ((q - p) < 10); q++) {
982 tmpc = *(q-1);
983 *(q-1) = *q;
984 *q = tmpc;
985 if ((q-p) < 2) continue; // omit swap char
986 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
987 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
988 if (ns == -1) return -1;
990 memcpy (candidate_utf, word, wl * sizeof(w_char));
992 for (p = candidate_utf + wl - 1; p > candidate_utf; p--) {
993 for (q = p - 1; (q >= candidate_utf) && ((p - q) < 10); q--) {
994 tmpc = *(q+1);
995 *(q+1) = *q;
996 *q = tmpc;
997 if ((p-q) < 2) continue; // omit swap char
998 u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
999 ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
1000 if (ns == -1) return -1;
1002 memcpy (candidate_utf, word, wl * sizeof(w_char));
1004 return ns;
1007 // generate a set of suggestions for very poorly spelled words
1008 int SuggestMgr::ngsuggest(char** wlst, char * w, int ns, HashMgr** pHMgr, int md)
1011 int i, j;
1012 int lval;
1013 int sc, scphon;
1014 int lp, lpphon;
1015 int nonbmp = 0;
1017 // exhaustively search through all root words
1018 // keeping track of the MAX_ROOTS most similar root words
1019 struct hentry * roots[MAX_ROOTS];
1020 char * rootsphon[MAX_ROOTS];
1021 int scores[MAX_ROOTS];
1022 int scoresphon[MAX_ROOTS];
1023 for (i = 0; i < MAX_ROOTS; i++) {
1024 roots[i] = NULL;
1025 scores[i] = -100 * i;
1026 rootsphon[i] = NULL;
1027 scoresphon[i] = -100 * i;
1029 lp = MAX_ROOTS - 1;
1030 lpphon = MAX_ROOTS - 1;
1031 scphon = -20000;
1032 int low = NGRAM_LOWERING;
1034 char w2[MAXWORDUTF8LEN];
1035 char f[MAXSWUTF8L];
1036 char * word = w;
1038 // word reversing wrapper for complex prefixes
1039 if (complexprefixes) {
1040 strcpy(w2, w);
1041 if (utf8) reverseword_utf(w2); else reverseword(w2);
1042 word = w2;
1045 char mw[MAXSWUTF8L];
1046 w_char u8[MAXSWL];
1047 int nc = strlen(word);
1048 int n = (utf8) ? u8_u16(u8, MAXSWL, word) : nc;
1050 // set character based ngram suggestion for words with non-BMP Unicode characters
1051 if (n == -1) {
1052 utf8 = 0; // XXX not state-free
1053 n = nc;
1054 nonbmp = 1;
1055 low = 0;
1058 struct hentry* hp = NULL;
1059 int col = -1;
1060 phonetable * ph = (pAMgr) ? pAMgr->get_phonetable() : NULL;
1061 char target[MAXSWUTF8L];
1062 char candidate[MAXSWUTF8L];
1063 if (ph) {
1064 if (utf8) {
1065 w_char _w[MAXSWL];
1066 int _wl = u8_u16(_w, MAXSWL, word);
1067 mkallcap_utf(_w, _wl, langnum);
1068 u16_u8(candidate, MAXSWUTF8L, _w, _wl);
1069 } else {
1070 strcpy(candidate, word);
1071 if (!nonbmp) mkallcap(candidate, csconv);
1073 phonet(candidate, target, nc, *ph); // XXX phonet() is 8-bit (nc, not n)
1076 FLAG forbiddenword = pAMgr ? pAMgr->get_forbiddenword() : FLAG_NULL;
1077 FLAG nosuggest = pAMgr ? pAMgr->get_nosuggest() : FLAG_NULL;
1078 FLAG nongramsuggest = pAMgr ? pAMgr->get_nongramsuggest() : FLAG_NULL;
1079 FLAG onlyincompound = pAMgr ? pAMgr->get_onlyincompound() : FLAG_NULL;
1081 for (i = 0; i < md; i++) {
1082 while (0 != (hp = (pHMgr[i])->walk_hashtable(col, hp))) {
1083 if ((hp->astr) && (pAMgr) &&
1084 (TESTAFF(hp->astr, forbiddenword, hp->alen) ||
1085 TESTAFF(hp->astr, ONLYUPCASEFLAG, hp->alen) ||
1086 TESTAFF(hp->astr, nosuggest, hp->alen) ||
1087 TESTAFF(hp->astr, nongramsuggest, hp->alen) ||
1088 TESTAFF(hp->astr, onlyincompound, hp->alen))) continue;
1090 sc = ngram(3, word, HENTRY_WORD(hp), NGRAM_LONGER_WORSE + low) +
1091 leftcommonsubstring(word, HENTRY_WORD(hp));
1093 // check special pronounciation
1094 if ((hp->var & H_OPT_PHON) && copy_field(f, HENTRY_DATA(hp), MORPH_PHON)) {
1095 int sc2 = ngram(3, word, f, NGRAM_LONGER_WORSE + low) +
1096 + leftcommonsubstring(word, f);
1097 if (sc2 > sc) sc = sc2;
1100 scphon = -20000;
1101 if (ph && (sc > 2) && (abs(n - (int) hp->clen) <= 3)) {
1102 char target2[MAXSWUTF8L];
1103 if (utf8) {
1104 w_char _w[MAXSWL];
1105 int _wl = u8_u16(_w, MAXSWL, HENTRY_WORD(hp));
1106 mkallcap_utf(_w, _wl, langnum);
1107 u16_u8(candidate, MAXSWUTF8L, _w, _wl);
1108 } else {
1109 strcpy(candidate, HENTRY_WORD(hp));
1110 mkallcap(candidate, csconv);
1112 phonet(candidate, target2, -1, *ph);
1113 scphon = 2 * ngram(3, target, target2, NGRAM_LONGER_WORSE);
1116 if (sc > scores[lp]) {
1117 scores[lp] = sc;
1118 roots[lp] = hp;
1119 lval = sc;
1120 for (j=0; j < MAX_ROOTS; j++)
1121 if (scores[j] < lval) {
1122 lp = j;
1123 lval = scores[j];
1128 if (scphon > scoresphon[lpphon]) {
1129 scoresphon[lpphon] = scphon;
1130 rootsphon[lpphon] = HENTRY_WORD(hp);
1131 lval = scphon;
1132 for (j=0; j < MAX_ROOTS; j++)
1133 if (scoresphon[j] < lval) {
1134 lpphon = j;
1135 lval = scoresphon[j];
1140 // find minimum threshold for a passable suggestion
1141 // mangle original word three differnt ways
1142 // and score them to generate a minimum acceptable score
1143 int thresh = 0;
1144 for (int sp = 1; sp < 4; sp++) {
1145 if (utf8) {
1146 for (int k=sp; k < n; k+=4) *((unsigned short *) u8 + k) = '*';
1147 u16_u8(mw, MAXSWUTF8L, u8, n);
1148 thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH + low);
1149 } else {
1150 strcpy(mw, word);
1151 for (int k=sp; k < n; k+=4) *(mw + k) = '*';
1152 thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH + low);
1155 thresh = thresh / 3;
1156 thresh--;
1158 // now expand affixes on each of these root words and
1159 // and use length adjusted ngram scores to select
1160 // possible suggestions
1161 char * guess[MAX_GUESS];
1162 char * guessorig[MAX_GUESS];
1163 int gscore[MAX_GUESS];
1164 for(i=0;i<MAX_GUESS;i++) {
1165 guess[i] = NULL;
1166 guessorig[i] = NULL;
1167 gscore[i] = -100 * i;
1170 lp = MAX_GUESS - 1;
1172 struct guessword * glst;
1173 glst = (struct guessword *) calloc(MAX_WORDS,sizeof(struct guessword));
1174 if (! glst) {
1175 if (nonbmp) utf8 = 1;
1176 return ns;
1179 for (i = 0; i < MAX_ROOTS; i++) {
1180 if (roots[i]) {
1181 struct hentry * rp = roots[i];
1182 int nw = pAMgr->expand_rootword(glst, MAX_WORDS, HENTRY_WORD(rp), rp->blen,
1183 rp->astr, rp->alen, word, nc,
1184 ((rp->var & H_OPT_PHON) ? copy_field(f, HENTRY_DATA(rp), MORPH_PHON) : NULL));
1186 for (int k = 0; k < nw ; k++) {
1187 sc = ngram(n, word, glst[k].word, NGRAM_ANY_MISMATCH + low) +
1188 leftcommonsubstring(word, glst[k].word);
1190 if (sc > thresh) {
1191 if (sc > gscore[lp]) {
1192 if (guess[lp]) {
1193 free (guess[lp]);
1194 if (guessorig[lp]) {
1195 free(guessorig[lp]);
1196 guessorig[lp] = NULL;
1199 gscore[lp] = sc;
1200 guess[lp] = glst[k].word;
1201 guessorig[lp] = glst[k].orig;
1202 lval = sc;
1203 for (j=0; j < MAX_GUESS; j++)
1204 if (gscore[j] < lval) {
1205 lp = j;
1206 lval = gscore[j];
1208 } else {
1209 free(glst[k].word);
1210 if (glst[k].orig) free(glst[k].orig);
1212 } else {
1213 free(glst[k].word);
1214 if (glst[k].orig) free(glst[k].orig);
1219 free(glst);
1221 // now we are done generating guesses
1222 // sort in order of decreasing score
1225 bubblesort(&guess[0], &guessorig[0], &gscore[0], MAX_GUESS);
1226 if (ph) bubblesort(&rootsphon[0], NULL, &scoresphon[0], MAX_ROOTS);
1228 // weight suggestions with a similarity index, based on
1229 // the longest common subsequent algorithm and resort
1231 int is_swap = 0;
1232 int re = 0;
1233 double fact = 1.0;
1234 if (pAMgr) {
1235 int maxd = pAMgr->get_maxdiff();
1236 if (maxd >= 0) fact = (10.0 - maxd)/5.0;
1239 for (i=0; i < MAX_GUESS; i++) {
1240 if (guess[i]) {
1241 // lowering guess[i]
1242 char gl[MAXSWUTF8L];
1243 int len;
1244 if (utf8) {
1245 w_char _w[MAXSWL];
1246 len = u8_u16(_w, MAXSWL, guess[i]);
1247 mkallsmall_utf(_w, len, langnum);
1248 u16_u8(gl, MAXSWUTF8L, _w, len);
1249 } else {
1250 strcpy(gl, guess[i]);
1251 if (!nonbmp) mkallsmall(gl, csconv);
1252 len = strlen(guess[i]);
1255 int _lcs = lcslen(word, gl);
1257 // same characters with different casing
1258 if ((n == len) && (n == _lcs)) {
1259 gscore[i] += 2000;
1260 break;
1262 // using 2-gram instead of 3, and other weightening
1264 re = ngram(2, word, gl, NGRAM_ANY_MISMATCH + low + NGRAM_WEIGHTED) +
1265 ngram(2, gl, word, NGRAM_ANY_MISMATCH + low + NGRAM_WEIGHTED);
1267 gscore[i] =
1268 // length of longest common subsequent minus length difference
1269 2 * _lcs - abs((int) (n - len)) +
1270 // weight length of the left common substring
1271 leftcommonsubstring(word, gl) +
1272 // weight equal character positions
1273 (!nonbmp && commoncharacterpositions(word, gl, &is_swap) ? 1: 0) +
1274 // swap character (not neighboring)
1275 ((is_swap) ? 10 : 0) +
1276 // ngram
1277 ngram(4, word, gl, NGRAM_ANY_MISMATCH + low) +
1278 // weighted ngrams
1279 re +
1280 // different limit for dictionaries with PHONE rules
1281 (ph ? (re < len * fact ? -1000 : 0) : (re < (n + len)*fact? -1000 : 0));
1285 bubblesort(&guess[0], &guessorig[0], &gscore[0], MAX_GUESS);
1287 // phonetic version
1288 if (ph) for (i=0; i < MAX_ROOTS; i++) {
1289 if (rootsphon[i]) {
1290 // lowering rootphon[i]
1291 char gl[MAXSWUTF8L];
1292 int len;
1293 if (utf8) {
1294 w_char _w[MAXSWL];
1295 len = u8_u16(_w, MAXSWL, rootsphon[i]);
1296 mkallsmall_utf(_w, len, langnum);
1297 u16_u8(gl, MAXSWUTF8L, _w, len);
1298 } else {
1299 strcpy(gl, rootsphon[i]);
1300 if (!nonbmp) mkallsmall(gl, csconv);
1301 len = strlen(rootsphon[i]);
1304 // heuristic weigthing of ngram scores
1305 scoresphon[i] += 2 * lcslen(word, gl) - abs((int) (n - len)) +
1306 // weight length of the left common substring
1307 leftcommonsubstring(word, gl);
1311 if (ph) bubblesort(&rootsphon[0], NULL, &scoresphon[0], MAX_ROOTS);
1313 // copy over
1314 int oldns = ns;
1316 int same = 0;
1317 for (i=0; i < MAX_GUESS; i++) {
1318 if (guess[i]) {
1319 if ((ns < oldns + maxngramsugs) && (ns < maxSug) && (!same || (gscore[i] > 1000))) {
1320 int unique = 1;
1321 // leave only excellent suggestions, if exists
1322 if (gscore[i] > 1000) same = 1; else if (gscore[i] < -100) {
1323 same = 1;
1324 // keep the best ngram suggestions, unless in ONLYMAXDIFF mode
1325 if (ns > oldns || (pAMgr && pAMgr->get_onlymaxdiff())) {
1326 free(guess[i]);
1327 if (guessorig[i]) free(guessorig[i]);
1328 continue;
1331 for (j = 0; j < ns; j++) {
1332 // don't suggest previous suggestions or a previous suggestion with prefixes or affixes
1333 if ((!guessorig[i] && strstr(guess[i], wlst[j])) ||
1334 (guessorig[i] && strstr(guessorig[i], wlst[j])) ||
1335 // check forbidden words
1336 !checkword(guess[i], strlen(guess[i]), 0, NULL, NULL)) unique = 0;
1338 if (unique) {
1339 wlst[ns++] = guess[i];
1340 if (guessorig[i]) {
1341 free(guess[i]);
1342 wlst[ns-1] = guessorig[i];
1344 } else {
1345 free(guess[i]);
1346 if (guessorig[i]) free(guessorig[i]);
1348 } else {
1349 free(guess[i]);
1350 if (guessorig[i]) free(guessorig[i]);
1355 oldns = ns;
1356 if (ph) for (i=0; i < MAX_ROOTS; i++) {
1357 if (rootsphon[i]) {
1358 if ((ns < oldns + MAXPHONSUGS) && (ns < maxSug)) {
1359 int unique = 1;
1360 for (j = 0; j < ns; j++) {
1361 // don't suggest previous suggestions or a previous suggestion with prefixes or affixes
1362 if (strstr(rootsphon[i], wlst[j]) ||
1363 // check forbidden words
1364 !checkword(rootsphon[i], strlen(rootsphon[i]), 0, NULL, NULL)) unique = 0;
1366 if (unique) {
1367 wlst[ns++] = mystrdup(rootsphon[i]);
1368 if (!wlst[ns - 1]) return ns - 1;
1374 if (nonbmp) utf8 = 1;
1375 return ns;
1379 // see if a candidate suggestion is spelled correctly
1380 // needs to check both root words and words with affixes
1382 // obsolote MySpell-HU modifications:
1383 // return value 2 and 3 marks compounding with hyphen (-)
1384 // `3' marks roots without suffix
1385 int SuggestMgr::checkword(const char * word, int len, int cpdsuggest, int * timer, clock_t * timelimit)
1387 struct hentry * rv=NULL;
1388 struct hentry * rv2=NULL;
1389 int nosuffix = 0;
1391 // check time limit
1392 if (timer) {
1393 (*timer)--;
1394 if (!(*timer) && timelimit) {
1395 if ((clock() - *timelimit) > TIMELIMIT) return 0;
1396 *timer = MAXPLUSTIMER;
1400 if (pAMgr) {
1401 if (cpdsuggest==1) {
1402 if (pAMgr->get_compound()) {
1403 rv = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 1, 0); //EXT
1404 if (rv && (!(rv2 = pAMgr->lookup(word)) || !rv2->astr ||
1405 !(TESTAFF(rv2->astr,pAMgr->get_forbiddenword(),rv2->alen) ||
1406 TESTAFF(rv2->astr,pAMgr->get_nosuggest(),rv2->alen)))) return 3; // XXX obsolote categorisation + only ICONV needs affix flag check?
1408 return 0;
1411 rv = pAMgr->lookup(word);
1413 if (rv) {
1414 if ((rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)
1415 || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen))) return 0;
1416 while (rv) {
1417 if (rv->astr && (TESTAFF(rv->astr,pAMgr->get_needaffix(),rv->alen) ||
1418 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1419 TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) {
1420 rv = rv->next_homonym;
1421 } else break;
1423 } else rv = pAMgr->prefix_check(word, len, 0); // only prefix, and prefix + suffix XXX
1425 if (rv) {
1426 nosuffix=1;
1427 } else {
1428 rv = pAMgr->suffix_check(word, len, 0, NULL, NULL, 0, NULL); // only suffix
1431 if (!rv && pAMgr->have_contclass()) {
1432 rv = pAMgr->suffix_check_twosfx(word, len, 0, NULL, FLAG_NULL);
1433 if (!rv) rv = pAMgr->prefix_check_twosfx(word, len, 1, FLAG_NULL);
1436 // check forbidden words
1437 if ((rv) && (rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen) ||
1438 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1439 TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen) ||
1440 TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) return 0;
1442 if (rv) { // XXX obsolote
1443 if ((pAMgr->get_compoundflag()) &&
1444 TESTAFF(rv->astr, pAMgr->get_compoundflag(), rv->alen)) return 2 + nosuffix;
1445 return 1;
1448 return 0;
1451 int SuggestMgr::check_forbidden(const char * word, int len)
1453 struct hentry * rv = NULL;
1455 if (pAMgr) {
1456 rv = pAMgr->lookup(word);
1457 if (rv && rv->astr && (TESTAFF(rv->astr,pAMgr->get_needaffix(),rv->alen) ||
1458 TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL;
1459 if (!(pAMgr->prefix_check(word,len,1)))
1460 rv = pAMgr->suffix_check(word,len, 0, NULL, NULL, 0, NULL); // prefix+suffix, suffix
1461 // check forbidden words
1462 if ((rv) && (rv->astr) && TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)) return 1;
1464 return 0;
1467 #ifdef HUNSPELL_EXPERIMENTAL
1468 // suggest possible stems
1469 int SuggestMgr::suggest_pos_stems(char*** slst, const char * w, int nsug)
1471 char ** wlst;
1473 struct hentry * rv = NULL;
1475 char w2[MAXSWUTF8L];
1476 const char * word = w;
1478 // word reversing wrapper for complex prefixes
1479 if (complexprefixes) {
1480 strcpy(w2, w);
1481 if (utf8) reverseword_utf(w2); else reverseword(w2);
1482 word = w2;
1485 int wl = strlen(word);
1488 if (*slst) {
1489 wlst = *slst;
1490 } else {
1491 wlst = (char **) calloc(maxSug, sizeof(char *));
1492 if (wlst == NULL) return -1;
1495 rv = pAMgr->suffix_check(word, wl, 0, NULL, wlst, maxSug, &nsug);
1497 // delete dash from end of word
1498 if (nsug > 0) {
1499 for (int j=0; j < nsug; j++) {
1500 if (wlst[j][strlen(wlst[j]) - 1] == '-') wlst[j][strlen(wlst[j]) - 1] = '\0';
1504 *slst = wlst;
1505 return nsug;
1507 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
1510 char * SuggestMgr::suggest_morph(const char * w)
1512 char result[MAXLNLEN];
1513 char * r = (char *) result;
1514 char * st;
1516 struct hentry * rv = NULL;
1518 *result = '\0';
1520 if (! pAMgr) return NULL;
1522 char w2[MAXSWUTF8L];
1523 const char * word = w;
1525 // word reversing wrapper for complex prefixes
1526 if (complexprefixes) {
1527 strcpy(w2, w);
1528 if (utf8) reverseword_utf(w2); else reverseword(w2);
1529 word = w2;
1532 rv = pAMgr->lookup(word);
1534 while (rv) {
1535 if ((!rv->astr) || !(TESTAFF(rv->astr, pAMgr->get_forbiddenword(), rv->alen) ||
1536 TESTAFF(rv->astr, pAMgr->get_needaffix(), rv->alen) ||
1537 TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) {
1538 if (!HENTRY_FIND(rv, MORPH_STEM)) {
1539 mystrcat(result, " ", MAXLNLEN);
1540 mystrcat(result, MORPH_STEM, MAXLNLEN);
1541 mystrcat(result, word, MAXLNLEN);
1543 if (HENTRY_DATA(rv)) {
1544 mystrcat(result, " ", MAXLNLEN);
1545 mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
1547 mystrcat(result, "\n", MAXLNLEN);
1549 rv = rv->next_homonym;
1552 st = pAMgr->affix_check_morph(word,strlen(word));
1553 if (st) {
1554 mystrcat(result, st, MAXLNLEN);
1555 free(st);
1558 if (pAMgr->get_compound() && (*result == '\0'))
1559 pAMgr->compound_check_morph(word, strlen(word),
1560 0, 0, 100, 0,NULL, 0, &r, NULL);
1562 return (*result) ? mystrdup(line_uniq(result, MSEP_REC)) : NULL;
1565 #ifdef HUNSPELL_EXPERIMENTAL
1566 char * SuggestMgr::suggest_morph_for_spelling_error(const char * word)
1568 char * p = NULL;
1569 char ** wlst = (char **) calloc(maxSug, sizeof(char *));
1570 if (!**wlst) return NULL;
1571 // we will use only the first suggestion
1572 for (int i = 0; i < maxSug - 1; i++) wlst[i] = "";
1573 int ns = suggest(&wlst, word, maxSug - 1, NULL);
1574 if (ns == maxSug) {
1575 p = suggest_morph(wlst[maxSug - 1]);
1576 free(wlst[maxSug - 1]);
1578 if (wlst) free(wlst);
1579 return p;
1581 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
1583 /* affixation */
1584 char * SuggestMgr::suggest_hentry_gen(hentry * rv, char * pattern)
1586 char result[MAXLNLEN];
1587 *result = '\0';
1588 int sfxcount = get_sfxcount(pattern);
1590 if (get_sfxcount(HENTRY_DATA(rv)) > sfxcount) return NULL;
1592 if (HENTRY_DATA(rv)) {
1593 char * aff = pAMgr->morphgen(HENTRY_WORD(rv), rv->blen, rv->astr, rv->alen,
1594 HENTRY_DATA(rv), pattern, 0);
1595 if (aff) {
1596 mystrcat(result, aff, MAXLNLEN);
1597 mystrcat(result, "\n", MAXLNLEN);
1598 free(aff);
1602 // check all allomorphs
1603 char allomorph[MAXLNLEN];
1604 char * p = NULL;
1605 if (HENTRY_DATA(rv)) p = (char *) strstr(HENTRY_DATA2(rv), MORPH_ALLOMORPH);
1606 while (p) {
1607 struct hentry * rv2 = NULL;
1608 p += MORPH_TAG_LEN;
1609 int plen = fieldlen(p);
1610 strncpy(allomorph, p, plen);
1611 allomorph[plen] = '\0';
1612 rv2 = pAMgr->lookup(allomorph);
1613 while (rv2) {
1614 // if (HENTRY_DATA(rv2) && get_sfxcount(HENTRY_DATA(rv2)) <= sfxcount) {
1615 if (HENTRY_DATA(rv2)) {
1616 char * st = (char *) strstr(HENTRY_DATA2(rv2), MORPH_STEM);
1617 if (st && (strncmp(st + MORPH_TAG_LEN,
1618 HENTRY_WORD(rv), fieldlen(st + MORPH_TAG_LEN)) == 0)) {
1619 char * aff = pAMgr->morphgen(HENTRY_WORD(rv2), rv2->blen, rv2->astr, rv2->alen,
1620 HENTRY_DATA(rv2), pattern, 0);
1621 if (aff) {
1622 mystrcat(result, aff, MAXLNLEN);
1623 mystrcat(result, "\n", MAXLNLEN);
1624 free(aff);
1628 rv2 = rv2->next_homonym;
1630 p = strstr(p + plen, MORPH_ALLOMORPH);
1633 return (*result) ? mystrdup(result) : NULL;
1636 char * SuggestMgr::suggest_gen(char ** desc, int n, char * pattern) {
1637 char result[MAXLNLEN];
1638 char result2[MAXLNLEN];
1639 char newpattern[MAXLNLEN];
1640 *newpattern = '\0';
1641 if (n == 0) return 0;
1642 *result2 = '\0';
1643 struct hentry * rv = NULL;
1644 if (!pAMgr) return NULL;
1646 // search affixed forms with and without derivational suffixes
1647 while(1) {
1649 for (int k = 0; k < n; k++) {
1650 *result = '\0';
1651 // add compound word parts (except the last one)
1652 char * s = (char *) desc[k];
1653 char * part = strstr(s, MORPH_PART);
1654 if (part) {
1655 char * nextpart = strstr(part + 1, MORPH_PART);
1656 while (nextpart) {
1657 copy_field(result + strlen(result), part, MORPH_PART);
1658 part = nextpart;
1659 nextpart = strstr(part + 1, MORPH_PART);
1661 s = part;
1664 char **pl;
1665 char tok[MAXLNLEN];
1666 strcpy(tok, s);
1667 char * alt = strstr(tok, " | ");
1668 while (alt) {
1669 alt[1] = MSEP_ALT;
1670 alt = strstr(alt, " | ");
1672 int pln = line_tok(tok, &pl, MSEP_ALT);
1673 for (int i = 0; i < pln; i++) {
1674 // remove inflectional and terminal suffixes
1675 char * is = strstr(pl[i], MORPH_INFL_SFX);
1676 if (is) *is = '\0';
1677 char * ts = strstr(pl[i], MORPH_TERM_SFX);
1678 while (ts) {
1679 *ts = '_';
1680 ts = strstr(pl[i], MORPH_TERM_SFX);
1682 char * st = strstr(s, MORPH_STEM);
1683 if (st) {
1684 copy_field(tok, st, MORPH_STEM);
1685 rv = pAMgr->lookup(tok);
1686 while (rv) {
1687 char newpat[MAXLNLEN];
1688 strcpy(newpat, pl[i]);
1689 strcat(newpat, pattern);
1690 char * sg = suggest_hentry_gen(rv, newpat);
1691 if (!sg) sg = suggest_hentry_gen(rv, pattern);
1692 if (sg) {
1693 char ** gen;
1694 int genl = line_tok(sg, &gen, MSEP_REC);
1695 free(sg);
1696 sg = NULL;
1697 for (int j = 0; j < genl; j++) {
1698 if (strstr(pl[i], MORPH_SURF_PFX)) {
1699 int r2l = strlen(result2);
1700 result2[r2l] = MSEP_REC;
1701 strcpy(result2 + r2l + 1, result);
1702 copy_field(result2 + strlen(result2), pl[i], MORPH_SURF_PFX);
1703 mystrcat(result2, gen[j], MAXLNLEN);
1704 } else {
1705 sprintf(result2 + strlen(result2), "%c%s%s",
1706 MSEP_REC, result, gen[j]);
1709 freelist(&gen, genl);
1711 rv = rv->next_homonym;
1715 freelist(&pl, pln);
1718 if (*result2 || !strstr(pattern, MORPH_DERI_SFX)) break;
1719 strcpy(newpattern, pattern);
1720 pattern = newpattern;
1721 char * ds = strstr(pattern, MORPH_DERI_SFX);
1722 while (ds) {
1723 strncpy(ds, MORPH_TERM_SFX, MORPH_TAG_LEN);
1724 ds = strstr(pattern, MORPH_DERI_SFX);
1727 return (*result2 ? mystrdup(result2) : NULL);
1731 // generate an n-gram score comparing s1 and s2
1732 int SuggestMgr::ngram(int n, char * s1, const char * s2, int opt)
1734 int nscore = 0;
1735 int ns;
1736 int l1;
1737 int l2;
1738 int test = 0;
1740 if (utf8) {
1741 w_char su1[MAXSWL];
1742 w_char su2[MAXSWL];
1743 l1 = u8_u16(su1, MAXSWL, s1);
1744 l2 = u8_u16(su2, MAXSWL, s2);
1745 if ((l2 <= 0) || (l1 == -1)) return 0;
1746 // lowering dictionary word
1747 if (opt & NGRAM_LOWERING) mkallsmall_utf(su2, l2, langnum);
1748 for (int j = 1; j <= n; j++) {
1749 ns = 0;
1750 for (int i = 0; i <= (l1-j); i++) {
1751 int k = 0;
1752 for (int l = 0; l <= (l2-j); l++) {
1753 for (k = 0; k < j; k++) {
1754 w_char * c1 = su1 + i + k;
1755 w_char * c2 = su2 + l + k;
1756 if ((c1->l != c2->l) || (c1->h != c2->h)) break;
1758 if (k == j) {
1759 ns++;
1760 break;
1763 if (k != j && opt & NGRAM_WEIGHTED) {
1764 ns--;
1765 test++;
1766 if (i == 0 || i == l1-j) ns--; // side weight
1769 nscore = nscore + ns;
1770 if (ns < 2 && !(opt & NGRAM_WEIGHTED)) break;
1772 } else {
1773 l2 = strlen(s2);
1774 if (l2 == 0) return 0;
1775 l1 = strlen(s1);
1776 char *t = mystrdup(s2);
1777 if (opt & NGRAM_LOWERING) mkallsmall(t, csconv);
1778 for (int j = 1; j <= n; j++) {
1779 ns = 0;
1780 for (int i = 0; i <= (l1-j); i++) {
1781 char c = *(s1 + i + j);
1782 *(s1 + i + j) = '\0';
1783 if (strstr(t,(s1+i))) {
1784 ns++;
1785 } else if (opt & NGRAM_WEIGHTED) {
1786 ns--;
1787 test++;
1788 if (i == 0 || i == l1-j) ns--; // side weight
1790 *(s1 + i + j ) = c;
1792 nscore = nscore + ns;
1793 if (ns < 2 && !(opt & NGRAM_WEIGHTED)) break;
1795 free(t);
1798 ns = 0;
1799 if (opt & NGRAM_LONGER_WORSE) ns = (l2-l1)-2;
1800 if (opt & NGRAM_ANY_MISMATCH) ns = abs(l2-l1)-2;
1801 ns = (nscore - ((ns > 0) ? ns : 0));
1802 return ns;
1805 // length of the left common substring of s1 and (decapitalised) s2
1806 int SuggestMgr::leftcommonsubstring(char * s1, const char * s2) {
1807 if (utf8) {
1808 w_char su1[MAXSWL];
1809 w_char su2[MAXSWL];
1810 su1[0].l = su2[0].l = su1[0].h = su2[0].h = 0;
1811 // decapitalize dictionary word
1812 if (complexprefixes) {
1813 int l1 = u8_u16(su1, MAXSWL, s1);
1814 int l2 = u8_u16(su2, MAXSWL, s2);
1815 if (*((short *)su1+l1-1) == *((short *)su2+l2-1)) return 1;
1816 } else {
1817 int i;
1818 u8_u16(su1, 1, s1);
1819 u8_u16(su2, 1, s2);
1820 unsigned short idx = (su2->h << 8) + su2->l;
1821 unsigned short otheridx = (su1->h << 8) + su1->l;
1822 if (otheridx != idx &&
1823 (otheridx != unicodetolower(idx, langnum))) return 0;
1824 int l1 = u8_u16(su1, MAXSWL, s1);
1825 int l2 = u8_u16(su2, MAXSWL, s2);
1826 for(i = 1; (i < l1) && (i < l2) &&
1827 (su1[i].l == su2[i].l) && (su1[i].h == su2[i].h); i++);
1828 return i;
1830 } else {
1831 if (complexprefixes) {
1832 int l1 = strlen(s1);
1833 int l2 = strlen(s2);
1834 if (*(s2+l1-1) == *(s2+l2-1)) return 1;
1835 } else {
1836 char * olds = s1;
1837 // decapitalise dictionary word
1838 if ((*s1 != *s2) && (*s1 != csconv[((unsigned char)*s2)].clower)) return 0;
1839 do {
1840 s1++; s2++;
1841 } while ((*s1 == *s2) && (*s1 != '\0'));
1842 return (int)(s1 - olds);
1845 return 0;
1848 int SuggestMgr::commoncharacterpositions(char * s1, const char * s2, int * is_swap) {
1849 int num = 0;
1850 int diff = 0;
1851 int diffpos[2];
1852 *is_swap = 0;
1853 if (utf8) {
1854 w_char su1[MAXSWL];
1855 w_char su2[MAXSWL];
1856 int l1 = u8_u16(su1, MAXSWL, s1);
1857 int l2 = u8_u16(su2, MAXSWL, s2);
1858 // decapitalize dictionary word
1859 if (complexprefixes) {
1860 mkallsmall_utf(su2+l2-1, 1, langnum);
1861 } else {
1862 mkallsmall_utf(su2, 1, langnum);
1864 for (int i = 0; (i < l1) && (i < l2); i++) {
1865 if (((short *) su1)[i] == ((short *) su2)[i]) {
1866 num++;
1867 } else {
1868 if (diff < 2) diffpos[diff] = i;
1869 diff++;
1872 if ((diff == 2) && (l1 == l2) &&
1873 (((short *) su1)[diffpos[0]] == ((short *) su2)[diffpos[1]]) &&
1874 (((short *) su1)[diffpos[1]] == ((short *) su2)[diffpos[0]])) *is_swap = 1;
1875 } else {
1876 int i;
1877 char t[MAXSWUTF8L];
1878 strcpy(t, s2);
1879 // decapitalize dictionary word
1880 if (complexprefixes) {
1881 int l2 = strlen(t);
1882 *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower;
1883 } else {
1884 mkallsmall(t, csconv);
1886 for (i = 0; (*(s1+i) != 0) && (*(t+i) != 0); i++) {
1887 if (*(s1+i) == *(t+i)) {
1888 num++;
1889 } else {
1890 if (diff < 2) diffpos[diff] = i;
1891 diff++;
1894 if ((diff == 2) && (*(s1+i) == 0) && (*(t+i) == 0) &&
1895 (*(s1+diffpos[0]) == *(t+diffpos[1])) &&
1896 (*(s1+diffpos[1]) == *(t+diffpos[0]))) *is_swap = 1;
1898 return num;
1901 int SuggestMgr::mystrlen(const char * word) {
1902 if (utf8) {
1903 w_char w[MAXSWL];
1904 return u8_u16(w, MAXSWL, word);
1905 } else return strlen(word);
1908 // sort in decreasing order of score
1909 void SuggestMgr::bubblesort(char** rword, char** rword2, int* rsc, int n )
1911 int m = 1;
1912 while (m < n) {
1913 int j = m;
1914 while (j > 0) {
1915 if (rsc[j-1] < rsc[j]) {
1916 int sctmp = rsc[j-1];
1917 char * wdtmp = rword[j-1];
1918 rsc[j-1] = rsc[j];
1919 rword[j-1] = rword[j];
1920 rsc[j] = sctmp;
1921 rword[j] = wdtmp;
1922 if (rword2) {
1923 wdtmp = rword2[j-1];
1924 rword2[j-1] = rword2[j];
1925 rword2[j] = wdtmp;
1927 j--;
1928 } else break;
1930 m++;
1932 return;
1935 // longest common subsequence
1936 void SuggestMgr::lcs(const char * s, const char * s2, int * l1, int * l2, char ** result) {
1937 int n, m;
1938 w_char su[MAXSWL];
1939 w_char su2[MAXSWL];
1940 char * b;
1941 char * c;
1942 int i;
1943 int j;
1944 if (utf8) {
1945 m = u8_u16(su, MAXSWL, s);
1946 n = u8_u16(su2, MAXSWL, s2);
1947 } else {
1948 m = strlen(s);
1949 n = strlen(s2);
1951 c = (char *) malloc((m + 1) * (n + 1));
1952 b = (char *) malloc((m + 1) * (n + 1));
1953 if (!c || !b) {
1954 if (c) free(c);
1955 if (b) free(b);
1956 *result = NULL;
1957 return;
1959 for (i = 1; i <= m; i++) c[i*(n+1)] = 0;
1960 for (j = 0; j <= n; j++) c[j] = 0;
1961 for (i = 1; i <= m; i++) {
1962 for (j = 1; j <= n; j++) {
1963 if ( ((utf8) && (*((short *) su+i-1) == *((short *)su2+j-1)))
1964 || ((!utf8) && ((*(s+i-1)) == (*(s2+j-1))))) {
1965 c[i*(n+1) + j] = c[(i-1)*(n+1) + j-1]+1;
1966 b[i*(n+1) + j] = LCS_UPLEFT;
1967 } else if (c[(i-1)*(n+1) + j] >= c[i*(n+1) + j-1]) {
1968 c[i*(n+1) + j] = c[(i-1)*(n+1) + j];
1969 b[i*(n+1) + j] = LCS_UP;
1970 } else {
1971 c[i*(n+1) + j] = c[i*(n+1) + j-1];
1972 b[i*(n+1) + j] = LCS_LEFT;
1976 *result = b;
1977 free(c);
1978 *l1 = m;
1979 *l2 = n;
1982 int SuggestMgr::lcslen(const char * s, const char* s2) {
1983 int m;
1984 int n;
1985 int i;
1986 int j;
1987 char * result;
1988 int len = 0;
1989 lcs(s, s2, &m, &n, &result);
1990 if (!result) return 0;
1991 i = m;
1992 j = n;
1993 while ((i != 0) && (j != 0)) {
1994 if (result[i*(n+1) + j] == LCS_UPLEFT) {
1995 len++;
1996 i--;
1997 j--;
1998 } else if (result[i*(n+1) + j] == LCS_UP) {
1999 i--;
2000 } else j--;
2002 free(result);
2003 return len;