tdk-api-wrapper 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,6 +25,503 @@ var TDKNetworkError = class extends TDKError {
25
25
  }
26
26
  };
27
27
 
28
+ // src/morphology.ts
29
+ var TURKISH_VOWELS = "ae\u0131io\xF6u\xFC";
30
+ function isVowel(ch) {
31
+ return TURKISH_VOWELS.includes(ch);
32
+ }
33
+ var TURKISH_SUFFIXES = [
34
+ // 9-letter composite suffixes
35
+ "lerimizden",
36
+ "lar\u0131m\u0131zdan",
37
+ "lerinizden",
38
+ "lar\u0131n\u0131zdan",
39
+ // 8-letter composite suffixes
40
+ "lerinin",
41
+ "lar\u0131n\u0131n",
42
+ "lerinde",
43
+ "lar\u0131nda",
44
+ "lerinden",
45
+ "lar\u0131ndan",
46
+ "leriyle",
47
+ "lar\u0131yla",
48
+ "lerini",
49
+ "lar\u0131n\u0131",
50
+ "lerimize",
51
+ "lar\u0131m\u0131za",
52
+ "lerimizle",
53
+ "lar\u0131m\u0131zla",
54
+ "lerinizin",
55
+ "lar\u0131n\u0131z\u0131n",
56
+ "lerinizde",
57
+ "lar\u0131n\u0131zda",
58
+ "d\u0131\u011F\u0131ndan",
59
+ "di\u011Finden",
60
+ "du\u011Fundan",
61
+ "d\xFC\u011F\xFCnden",
62
+ "t\u0131\u011F\u0131ndan",
63
+ "ti\u011Finden",
64
+ "tu\u011Fundan",
65
+ "t\xFC\u011F\xFCnden",
66
+ // 7-letter composite suffixes
67
+ "ecektir",
68
+ "acakt\u0131r",
69
+ "ece\u011Fim",
70
+ "aca\u011F\u0131m",
71
+ "eceksin",
72
+ "acaks\u0131n",
73
+ "ece\u011Fiz",
74
+ "aca\u011F\u0131z",
75
+ "lerimiz",
76
+ "lar\u0131m\u0131z",
77
+ "leriniz",
78
+ "lar\u0131n\u0131z",
79
+ "umuzdan",
80
+ "\xFCm\xFCzden",
81
+ "inizden",
82
+ "\u0131n\u0131zdan",
83
+ "\xFCn\xFCzden",
84
+ "d\u0131\u011F\u0131nda",
85
+ "di\u011Finde",
86
+ "du\u011Funda",
87
+ "d\xFC\u011F\xFCnde",
88
+ "t\u0131\u011F\u0131nda",
89
+ "ti\u011Finde",
90
+ "tu\u011Funda",
91
+ "t\xFC\u011F\xFCnde",
92
+ "mas\u0131na",
93
+ "mesine",
94
+ "\u0131yorsunuz",
95
+ "iyorsunuz",
96
+ "uyorsunuz",
97
+ "\xFCyorsunuz",
98
+ "yorsunuz",
99
+ // 6-letter composite suffixes
100
+ "iyorsa",
101
+ "iyorduk",
102
+ "iyordu",
103
+ "iyormu\u015F",
104
+ "\u0131yorsa",
105
+ "\u0131yorduk",
106
+ "\u0131yordu",
107
+ "\u0131yormu\u015F",
108
+ "uyorsa",
109
+ "uyorduk",
110
+ "uyordu",
111
+ "uyormu\u015F",
112
+ "\xFCyorsa",
113
+ "\xFCyorduk",
114
+ "\xFCyordu",
115
+ "\xFCyormu\u015F",
116
+ "\u0131yorsun",
117
+ "iyorsun",
118
+ "uyorsun",
119
+ "\xFCyorsun",
120
+ "\u0131yorlar",
121
+ "iyorlar",
122
+ "uyorlar",
123
+ "\xFCyorlar",
124
+ "iyoruz",
125
+ "\u0131yoruz",
126
+ "uyoruz",
127
+ "\xFCyoruz",
128
+ "imizin",
129
+ "\u0131m\u0131z\u0131n",
130
+ "umuzun",
131
+ "\xFCm\xFCz\xFCn",
132
+ "imizde",
133
+ "\u0131m\u0131zda",
134
+ "umuzda",
135
+ "\xFCm\xFCzde",
136
+ "imizden",
137
+ "\u0131m\u0131zdan",
138
+ "imizle",
139
+ "\u0131m\u0131zla",
140
+ "umuzla",
141
+ "\xFCm\xFCzle",
142
+ "lerdir",
143
+ "lard\u0131r",
144
+ "mu\u015Ftur",
145
+ "mi\u015Ftir",
146
+ "mu\u015Ftur",
147
+ "m\xFC\u015Ft\xFCr",
148
+ "lerden",
149
+ "lardan",
150
+ "lerine",
151
+ "lar\u0131na",
152
+ "leriyle",
153
+ "lar\u0131yla",
154
+ "seniz",
155
+ "san\u0131z",
156
+ "diniz",
157
+ "d\u0131n\u0131z",
158
+ "dunuz",
159
+ "d\xFCn\xFCz",
160
+ "tiniz",
161
+ "t\u0131n\u0131z",
162
+ "tunuz",
163
+ "t\xFCn\xFCz",
164
+ "siniz",
165
+ "s\u0131n\u0131z",
166
+ "sunuz",
167
+ "s\xFCn\xFCz",
168
+ "d\u0131k\xE7a",
169
+ "dik\xE7e",
170
+ "duk\xE7a",
171
+ "d\xFCk\xE7e",
172
+ "t\u0131k\xE7a",
173
+ "tik\xE7e",
174
+ "tuk\xE7a",
175
+ "t\xFCk\xE7e",
176
+ "\u0131rken",
177
+ "irken",
178
+ "urken",
179
+ "\xFCrken",
180
+ "arken",
181
+ "erken",
182
+ // 5-letter suffixes
183
+ "lerde",
184
+ "larda",
185
+ "lerle",
186
+ "larla",
187
+ "lerin",
188
+ "lar\u0131n",
189
+ "lerim",
190
+ "lar\u0131m",
191
+ "dirler",
192
+ "d\u0131rlar",
193
+ "d\xFCrler",
194
+ "durlar",
195
+ "tirler",
196
+ "t\u0131rlar",
197
+ "t\xFCrler",
198
+ "turlar",
199
+ "siniz",
200
+ "s\u0131n\u0131z",
201
+ "sunuz",
202
+ "s\xFCn\xFCz",
203
+ "yorum",
204
+ "yorsun",
205
+ "uyoruz",
206
+ "yorsunuz",
207
+ "yorlar",
208
+ "eceks",
209
+ "acaks",
210
+ "eyim",
211
+ "ay\u0131m",
212
+ "indik",
213
+ "\u0131nd\u0131k",
214
+ "unduk",
215
+ "\xFCnd\xFCk",
216
+ "ildik",
217
+ "\u0131ld\u0131k",
218
+ "ulduk",
219
+ "\xFCld\xFCk",
220
+ "meden",
221
+ "madan",
222
+ "y\u0131n\u0131z",
223
+ "yiniz",
224
+ "yunuz",
225
+ "y\xFCn\xFCz",
226
+ // 4-letter suffixes
227
+ "imiz",
228
+ "\u0131m\u0131z",
229
+ "umuz",
230
+ "\xFCm\xFCz",
231
+ "iniz",
232
+ "\u0131n\u0131z",
233
+ "unuz",
234
+ "\xFCn\xFCz",
235
+ "leri",
236
+ "lar\u0131",
237
+ "idir",
238
+ "\u0131d\u0131r",
239
+ "udur",
240
+ "\xFCd\xFCr",
241
+ "ecek",
242
+ "acak",
243
+ "erek",
244
+ "arak",
245
+ "ince",
246
+ "\u0131nca",
247
+ "unca",
248
+ "\xFCnce",
249
+ "ken",
250
+ "meli",
251
+ "mal\u0131",
252
+ "iyor",
253
+ "\u0131yor",
254
+ "uyor",
255
+ "\xFCyor",
256
+ "mi\u015Fti",
257
+ "m\u0131\u015Ft\u0131",
258
+ "mu\u015Ftu",
259
+ "m\xFC\u015Ft\xFC",
260
+ "seydi",
261
+ "sayd\u0131",
262
+ "ydim",
263
+ "yd\u0131m",
264
+ "ydum",
265
+ "yd\xFCm",
266
+ "tiler",
267
+ "t\u0131lar",
268
+ "diler",
269
+ "d\u0131lar",
270
+ "ikten",
271
+ "\u0131ktan",
272
+ "uktan",
273
+ "\xFCkten",
274
+ // 3-letter suffixes
275
+ "ler",
276
+ "lar",
277
+ "den",
278
+ "dan",
279
+ "ten",
280
+ "tan",
281
+ "dir",
282
+ "d\u0131r",
283
+ "dur",
284
+ "d\xFCr",
285
+ "tir",
286
+ "t\u0131r",
287
+ "tur",
288
+ "t\xFCr",
289
+ "nin",
290
+ "n\u0131n",
291
+ "nun",
292
+ "n\xFCn",
293
+ "yle",
294
+ "yla",
295
+ "mi\u015F",
296
+ "m\u0131\u015F",
297
+ "mu\u015F",
298
+ "m\xFC\u015F",
299
+ "dim",
300
+ "d\u0131m",
301
+ "dum",
302
+ "d\xFCm",
303
+ "tim",
304
+ "t\u0131m",
305
+ "tum",
306
+ "t\xFCm",
307
+ "din",
308
+ "d\u0131n",
309
+ "dun",
310
+ "d\xFCn",
311
+ "tin",
312
+ "t\u0131n",
313
+ "tun",
314
+ "t\xFCn",
315
+ "dik",
316
+ "d\u0131k",
317
+ "duk",
318
+ "d\xFCk",
319
+ "tik",
320
+ "t\u0131k",
321
+ "tuk",
322
+ "t\xFCk",
323
+ "ydi",
324
+ "yd\u0131",
325
+ "ydu",
326
+ "yd\xFC",
327
+ "yim",
328
+ "y\u0131m",
329
+ "yum",
330
+ "y\xFCm",
331
+ "sin",
332
+ "s\u0131n",
333
+ "sun",
334
+ "s\xFCn",
335
+ "siz",
336
+ "s\u0131z",
337
+ "suz",
338
+ "s\xFCz",
339
+ "lik",
340
+ "l\u0131k",
341
+ "luk",
342
+ "l\xFCk",
343
+ "ici",
344
+ "\u0131c\u0131",
345
+ "ucu",
346
+ "\xFCc\xFC",
347
+ "gen",
348
+ "gan",
349
+ "ken",
350
+ "kan",
351
+ "len",
352
+ "lan",
353
+ "le\u015F",
354
+ "la\u015F",
355
+ "mek",
356
+ "mak",
357
+ "yor",
358
+ // 2-letter suffixes
359
+ "de",
360
+ "da",
361
+ "te",
362
+ "ta",
363
+ "im",
364
+ "\u0131m",
365
+ "um",
366
+ "\xFCm",
367
+ "in",
368
+ "\u0131n",
369
+ "un",
370
+ "\xFCn",
371
+ "iz",
372
+ "\u0131z",
373
+ "uz",
374
+ "\xFCz",
375
+ "si",
376
+ "s\u0131",
377
+ "su",
378
+ "s\xFC",
379
+ "ye",
380
+ "ya",
381
+ "le",
382
+ "la",
383
+ "di",
384
+ "d\u0131",
385
+ "du",
386
+ "d\xFC",
387
+ "ti",
388
+ "t\u0131",
389
+ "tu",
390
+ "t\xFC",
391
+ "se",
392
+ "sa",
393
+ "ce",
394
+ "ca",
395
+ "\xE7e",
396
+ "\xE7a",
397
+ "me",
398
+ "ma",
399
+ "ip",
400
+ "\u0131p",
401
+ "up",
402
+ "\xFCp",
403
+ "en",
404
+ "an",
405
+ "i\u015F",
406
+ "\u0131\u015F",
407
+ "u\u015F",
408
+ "\xFC\u015F",
409
+ "li",
410
+ "l\u0131",
411
+ "lu",
412
+ "l\xFC",
413
+ "ci",
414
+ "c\u0131",
415
+ "cu",
416
+ "c\xFC",
417
+ "\xE7i",
418
+ "\xE7\u0131",
419
+ "\xE7u",
420
+ "\xE7\xFC",
421
+ // 1-letter suffixes (vowels / basic case endings)
422
+ "e",
423
+ "a",
424
+ "i",
425
+ "\u0131",
426
+ "u",
427
+ "\xFC"
428
+ ];
429
+ function restoreConsonantSoftening(stem) {
430
+ if (stem.length < 2)
431
+ return [];
432
+ const last = stem.slice(-1);
433
+ const base = stem.slice(0, -1);
434
+ switch (last) {
435
+ case "b":
436
+ return [base + "p"];
437
+ case "c":
438
+ return [base + "\xE7"];
439
+ case "d":
440
+ return [base + "t"];
441
+ case "\u011F":
442
+ return [base + "k"];
443
+ case "g":
444
+ return [base + "k"];
445
+ default:
446
+ return [];
447
+ }
448
+ }
449
+ function restoreVowelDrop(stem) {
450
+ if (stem.length < 3)
451
+ return [];
452
+ const c1 = stem[stem.length - 2];
453
+ const c2 = stem[stem.length - 1];
454
+ if (!isVowel(c1) && !isVowel(c2)) {
455
+ const vowelsInBase = stem.slice(0, -2).split("").filter(isVowel);
456
+ if (vowelsInBase.length > 0) {
457
+ const lastVowel = vowelsInBase[vowelsInBase.length - 1];
458
+ let inserted = "i";
459
+ if ("a\u0131".includes(lastVowel))
460
+ inserted = "\u0131";
461
+ else if ("ei".includes(lastVowel))
462
+ inserted = "i";
463
+ else if ("ou".includes(lastVowel))
464
+ inserted = "u";
465
+ else if ("\xF6\xFC".includes(lastVowel))
466
+ inserted = "\xFC";
467
+ return [stem.slice(0, -1) + inserted + c2];
468
+ }
469
+ }
470
+ return [];
471
+ }
472
+ function restoreInfinitive(stem) {
473
+ if (stem.length < 2)
474
+ return [];
475
+ const vowelsInBase = stem.split("").filter(isVowel);
476
+ if (vowelsInBase.length === 0)
477
+ return [];
478
+ const lastVowel = vowelsInBase[vowelsInBase.length - 1];
479
+ return "a\u0131ou".includes(lastVowel) ? [stem + "mak"] : [stem + "mek"];
480
+ }
481
+ function getStemCandidates(word, minStemLength = 2, maxDepth = 4) {
482
+ if (!word || word.trim().length === 0)
483
+ return [];
484
+ const raw = word.trim();
485
+ const normalized = raw.toLocaleLowerCase("tr-TR");
486
+ const candidatesWithWeight = [];
487
+ const seen = /* @__PURE__ */ new Set();
488
+ if (raw.includes("'") || raw.includes("\u2019")) {
489
+ const apostropheStem = normalized.split(/['’]/)[0];
490
+ if (apostropheStem.length >= minStemLength) {
491
+ candidatesWithWeight.push({ candidate: apostropheStem, baseLength: apostropheStem.length + 10 });
492
+ seen.add(apostropheStem);
493
+ }
494
+ }
495
+ let frontier = [normalized];
496
+ for (let depth = 0; depth < maxDepth; depth++) {
497
+ const nextFrontier = [];
498
+ for (const current of frontier) {
499
+ for (const suffix of TURKISH_SUFFIXES) {
500
+ if (current.length - suffix.length >= minStemLength && current.endsWith(suffix)) {
501
+ const stem = current.slice(0, -suffix.length);
502
+ const hardened = restoreConsonantSoftening(stem);
503
+ const vowelDropped = restoreVowelDrop(stem);
504
+ const verbalBases = [stem, ...hardened];
505
+ const infinitives = verbalBases.flatMap((v) => restoreInfinitive(v));
506
+ const variants = [stem, ...hardened, ...vowelDropped, ...infinitives];
507
+ for (const variant of variants) {
508
+ if (!seen.has(variant) && variant !== normalized) {
509
+ seen.add(variant);
510
+ nextFrontier.push(variant);
511
+ candidatesWithWeight.push({ candidate: variant, baseLength: stem.length });
512
+ }
513
+ }
514
+ }
515
+ }
516
+ }
517
+ if (nextFrontier.length === 0)
518
+ break;
519
+ frontier = nextFrontier;
520
+ }
521
+ candidatesWithWeight.sort((a, b) => b.baseLength - a.baseLength);
522
+ return [...new Set(candidatesWithWeight.map((c) => c.candidate))];
523
+ }
524
+
28
525
  // src/tdk.ts
29
526
  import * as fs from "fs";
30
527
  import * as path from "path";
@@ -116,6 +613,8 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
116
613
  static wordCache = /* @__PURE__ */ new Map();
117
614
  static dailyContentCache = null;
118
615
  static autocompleteCache = [];
616
+ static autocompleteSet = /* @__PURE__ */ new Set();
617
+ static stemCache = /* @__PURE__ */ new Map();
119
618
  /**
120
619
  * Enables or disables in-memory caching for API requests.
121
620
  */
@@ -132,6 +631,8 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
132
631
  this.wordCache.clear();
133
632
  this.dailyContentCache = null;
134
633
  this.autocompleteCache = [];
634
+ this.autocompleteSet.clear();
635
+ this.stemCache.clear();
135
636
  }
136
637
  static delay(ms) {
137
638
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -238,6 +739,17 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
238
739
  return [];
239
740
  }
240
741
  }
742
+ /**
743
+ * Ensures TDK's ~81k headword list is loaded in memory for fast O(1) set operations.
744
+ */
745
+ static async ensureAutocompleteLoaded() {
746
+ if (this.autocompleteCache.length === 0) {
747
+ this.autocompleteCache = await this.fetchAutocompleteData();
748
+ this.autocompleteSet = new Set(
749
+ this.autocompleteCache.map((w) => w.toLocaleLowerCase("tr-TR"))
750
+ );
751
+ }
752
+ }
241
753
  /**
242
754
  * Returns autocomplete suggestions for a given prefix, searched over TDK's
243
755
  * full headword list (see `fetchAutocompleteData`). The list is fetched
@@ -247,12 +759,81 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
247
759
  static async getSuggestions(prefix) {
248
760
  if (!prefix || prefix.trim() === "")
249
761
  return [];
250
- if (this.autocompleteCache.length === 0) {
251
- this.autocompleteCache = await this.fetchAutocompleteData();
252
- }
762
+ await this.ensureAutocompleteLoaded();
253
763
  const cleanPrefix = prefix.trim().toLocaleLowerCase("tr-TR");
254
764
  return this.autocompleteCache.filter((w) => w.toLocaleLowerCase("tr-TR").startsWith(cleanPrefix)).slice(0, 10);
255
765
  }
766
+ /**
767
+ * Checks whether a word exists as a known headword in TDK dictionary.
768
+ * Checks in-memory autocompleteSet (81k headwords) if loaded, or queries TDK API.
769
+ */
770
+ static async isHeadword(word) {
771
+ if (!word || word.trim() === "")
772
+ return false;
773
+ const clean = word.trim().toLocaleLowerCase("tr-TR");
774
+ await this.ensureAutocompleteLoaded();
775
+ if (this.autocompleteSet.size > 0) {
776
+ return this.autocompleteSet.has(clean);
777
+ }
778
+ try {
779
+ const results = await this.getWord(clean);
780
+ return results.length > 0;
781
+ } catch {
782
+ return false;
783
+ }
784
+ }
785
+ /**
786
+ * Generates candidate roots for a given Turkish word using progressive BFS suffix stripping,
787
+ * consonant mutation restoration, and vowel drop restoration.
788
+ */
789
+ static getStemCandidates(word) {
790
+ return getStemCandidates(word);
791
+ }
792
+ /**
793
+ * Finds the dictionary root (headword) of a word by checking direct existence
794
+ * and evaluating candidate stems generated by morphological analysis.
795
+ * Returns the root headword string if found, or null if no match in TDK.
796
+ */
797
+ static async findRoot(word) {
798
+ if (!word || word.trim() === "")
799
+ return null;
800
+ const clean = word.trim().toLocaleLowerCase("tr-TR");
801
+ if (this.stemCache.has(clean)) {
802
+ return this.stemCache.get(clean);
803
+ }
804
+ if (await this.isHeadword(clean)) {
805
+ this.stemCache.set(clean, clean);
806
+ return clean;
807
+ }
808
+ const candidates = getStemCandidates(clean);
809
+ for (const candidate of candidates) {
810
+ if (await this.isHeadword(candidate)) {
811
+ this.stemCache.set(clean, candidate);
812
+ return candidate;
813
+ }
814
+ }
815
+ this.stemCache.set(clean, null);
816
+ return null;
817
+ }
818
+ /**
819
+ * Performs morphological stemming on a Turkish word.
820
+ * Returns a StemResult containing the original word, resolved root, and whether it is inflected.
821
+ */
822
+ static async stem(word) {
823
+ if (!word || word.trim() === "")
824
+ return null;
825
+ const clean = word.trim().toLocaleLowerCase("tr-TR");
826
+ const root = await this.findRoot(word);
827
+ if (!root) {
828
+ return null;
829
+ }
830
+ return {
831
+ word,
832
+ root,
833
+ isInflected: root !== clean,
834
+ candidates: getStemCandidates(word)
835
+ };
836
+ }
256
837
  /**
257
838
  * Returns a list of proverbs and idioms containing the word.
258
839
  */
@@ -469,6 +1050,17 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
469
1050
  return { isCorrect: false, word, suggestion: mixMatch.dogru };
470
1051
  }
471
1052
  }
1053
+ const root = await this.findRoot(word);
1054
+ if (root) {
1055
+ const cleanWord2 = word.trim().toLocaleLowerCase("tr-TR");
1056
+ const isInflected = root !== cleanWord2;
1057
+ return {
1058
+ isCorrect: true,
1059
+ word,
1060
+ isInflected,
1061
+ root
1062
+ };
1063
+ }
472
1064
  if (this.autocompleteCache.length === 0) {
473
1065
  this.autocompleteCache = await this.fetchAutocompleteData();
474
1066
  }
@@ -965,13 +1557,28 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
965
1557
  const unique = [...new Set(words)];
966
1558
  const analyses = [];
967
1559
  for (const word of unique) {
968
- const results = await this.getWord(word);
969
- const found = results.length > 0;
1560
+ let results = await this.getWord(word);
1561
+ let found = results.length > 0;
1562
+ let root;
1563
+ let isInflected;
1564
+ if (!found) {
1565
+ const resolvedRoot = await this.findRoot(word);
1566
+ if (resolvedRoot) {
1567
+ results = await this.getWord(resolvedRoot);
1568
+ if (results.length > 0) {
1569
+ found = true;
1570
+ root = resolvedRoot;
1571
+ isInflected = true;
1572
+ }
1573
+ }
1574
+ }
970
1575
  analyses.push({
971
1576
  word,
972
1577
  found,
973
1578
  meaning: found ? this.firstMeaning(results) : null,
974
- origin: found ? results[0].lisan || "T\xFCrk\xE7e" : null
1579
+ origin: found ? results[0].lisan || "T\xFCrk\xE7e" : null,
1580
+ root,
1581
+ isInflected
975
1582
  });
976
1583
  await this.delay(200);
977
1584
  }
@@ -1069,5 +1676,12 @@ export {
1069
1676
  TDKError,
1070
1677
  TDKValidationError,
1071
1678
  TDKNetworkError,
1679
+ TURKISH_VOWELS,
1680
+ isVowel,
1681
+ TURKISH_SUFFIXES,
1682
+ restoreConsonantSoftening,
1683
+ restoreVowelDrop,
1684
+ restoreInfinitive,
1685
+ getStemCandidates,
1072
1686
  TDK
1073
1687
  };