pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / analyzers / common / src / java / org / apache / lucene / analysis / el / GreekStemmer.java
1 package org.apache.lucene.analysis.el;
2
3 import org.apache.lucene.analysis.CharArraySet;
4 import org.apache.lucene.util.Version;
5
6 import java.util.Arrays;
7
8 /**
9  * Licensed to the Apache Software Foundation (ASF) under one or more
10  * contributor license agreements.  See the NOTICE file distributed with
11  * this work for additional information regarding copyright ownership.
12  * The ASF licenses this file to You under the Apache License, Version 2.0
13  * (the "License"); you may not use this file except in compliance with
14  * the License.  You may obtain a copy of the License at
15  *
16  *     http://www.apache.org/licenses/LICENSE-2.0
17  *
18  * Unless required by applicable law or agreed to in writing, software
19  * distributed under the License is distributed on an "AS IS" BASIS,
20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  * See the License for the specific language governing permissions and
22  * limitations under the License.
23  */
24
25 /**
26  * A stemmer for Greek words, according to: <i>Development of a Stemmer for the
27  * Greek Language.</i> Georgios Ntais
28  * <p>
29  * NOTE: Input is expected to be casefolded for Greek (including folding of final
30  * sigma to sigma), and with diacritics removed. This can be achieved with 
31  * either {@link GreekLowerCaseFilter} or ICUFoldingFilter.
32  * @lucene.experimental
33  */
34 public class GreekStemmer {
35   public int stem(char s[], int len) {
36     if (len < 4) // too short
37       return len;
38     
39     final int origLen = len;
40     // "short rules": if it hits one of these, it skips the "long list"
41     len = rule0(s, len);
42     len = rule1(s, len);
43     len = rule2(s, len);
44     len = rule3(s, len);
45     len = rule4(s, len);
46     len = rule5(s, len);
47     len = rule6(s, len);
48     len = rule7(s, len);
49     len = rule8(s, len);
50     len = rule9(s, len);
51     len = rule10(s, len);
52     len = rule11(s, len);
53     len = rule12(s, len);
54     len = rule13(s, len);
55     len = rule14(s, len);
56     len = rule15(s, len);
57     len = rule16(s, len);
58     len = rule17(s, len);
59     len = rule18(s, len);
60     len = rule19(s, len);
61     len = rule20(s, len);
62     // "long list"
63     if (len == origLen)
64       len = rule21(s, len);
65     
66     return rule22(s, len);
67   }
68
69   private int rule0(char s[], int len) {
70     if (len > 9 && (endsWith(s, len, "καθεστωτοσ")
71         || endsWith(s, len, "καθεστωτων")))
72       return len - 4;
73     
74     if (len > 8 && (endsWith(s, len, "γεγονοτοσ")
75         || endsWith(s, len, "γεγονοτων")))
76       return len - 4;
77     
78     if (len > 8 && endsWith(s, len, "καθεστωτα"))
79       return len - 3;
80     
81     if (len > 7 && (endsWith(s, len, "τατογιου")
82         || endsWith(s, len, "τατογιων")))
83       return len - 4;
84     
85     if (len > 7 && endsWith(s, len, "γεγονοτα"))
86       return len - 3;
87     
88     if (len > 7 && endsWith(s, len, "καθεστωσ"))
89       return len - 2;
90     
91     if (len > 6 && (endsWith(s, len, "σκαγιου"))
92         || endsWith(s, len, "σκαγιων")
93         || endsWith(s, len, "ολογιου")
94         || endsWith(s, len, "ολογιων")
95         || endsWith(s, len, "κρεατοσ")
96         || endsWith(s, len, "κρεατων")
97         || endsWith(s, len, "περατοσ")
98         || endsWith(s, len, "περατων")
99         || endsWith(s, len, "τερατοσ")
100         || endsWith(s, len, "τερατων"))
101       return len - 4;
102     
103     if (len > 6 && endsWith(s, len, "τατογια"))
104       return len - 3;
105     
106     if (len > 6 && endsWith(s, len, "γεγονοσ"))
107       return len - 2;
108     
109     if (len > 5 && (endsWith(s, len, "φαγιου")
110         || endsWith(s, len, "φαγιων")
111         || endsWith(s, len, "σογιου")
112         || endsWith(s, len, "σογιων")))
113       return len - 4;
114     
115     if (len > 5 && (endsWith(s, len, "σκαγια")
116         || endsWith(s, len, "ολογια")
117         || endsWith(s, len, "κρεατα")
118         || endsWith(s, len, "περατα")
119         || endsWith(s, len, "τερατα")))
120       return len - 3;
121     
122     if (len > 4 && (endsWith(s, len, "φαγια")
123         || endsWith(s, len, "σογια")
124         || endsWith(s, len, "φωτοσ")
125         || endsWith(s, len, "φωτων")))
126       return len - 3;
127     
128     if (len > 4 && (endsWith(s, len, "κρεασ")
129         || endsWith(s, len, "περασ")
130         || endsWith(s, len, "τερασ")))
131       return len - 2;
132     
133     if (len > 3 && endsWith(s, len, "φωτα"))
134       return len - 2;
135     
136     if (len > 2 && endsWith(s, len, "φωσ"))
137       return len - 1;
138     
139     return len;
140   }
141
142   private int rule1(char s[], int len) {
143     if (len > 4 && (endsWith(s, len, "αδεσ") || endsWith(s, len, "αδων"))) {
144       len -= 4;
145       if (!(endsWith(s, len, "οκ") ||
146           endsWith(s, len, "μαμ") ||
147           endsWith(s, len, "μαν") ||
148           endsWith(s, len, "μπαμπ") ||
149           endsWith(s, len, "πατερ") ||
150           endsWith(s, len, "γιαγι") ||
151           endsWith(s, len, "νταντ") ||
152           endsWith(s, len, "κυρ") ||
153           endsWith(s, len, "θει") ||
154           endsWith(s, len, "πεθερ")))
155         len += 2; // add back -αδ
156     }
157     return len;
158   }
159   
160   private int rule2(char s[], int len) {
161     if (len > 4 && (endsWith(s, len, "εδεσ") || endsWith(s, len, "εδων"))) {
162       len -= 4;
163       if (endsWith(s, len, "οπ") ||
164           endsWith(s, len, "ιπ") ||
165           endsWith(s, len, "εμπ") ||
166           endsWith(s, len, "υπ") ||
167           endsWith(s, len, "γηπ") ||
168           endsWith(s, len, "δαπ") ||
169           endsWith(s, len, "κρασπ") ||
170           endsWith(s, len, "μιλ"))
171         len += 2; // add back -εδ
172     }
173     return len;
174   }
175   
176   private int rule3(char s[], int len) {
177     if (len > 5 && (endsWith(s, len, "ουδεσ") || endsWith(s, len, "ουδων"))) {
178       len -= 5;
179       if (endsWith(s, len, "αρκ") ||
180           endsWith(s, len, "καλιακ") ||
181           endsWith(s, len, "πεταλ") ||
182           endsWith(s, len, "λιχ") ||
183           endsWith(s, len, "πλεξ") ||
184           endsWith(s, len, "σκ") ||
185           endsWith(s, len, "σ") ||
186           endsWith(s, len, "φλ") ||
187           endsWith(s, len, "φρ") ||
188           endsWith(s, len, "βελ") ||
189           endsWith(s, len, "λουλ") ||
190           endsWith(s, len, "χν") ||
191           endsWith(s, len, "σπ") ||
192           endsWith(s, len, "τραγ") ||
193           endsWith(s, len, "φε"))
194         len += 3; // add back -ουδ
195     }
196     return len;
197   }
198   
199   private static final CharArraySet exc4 = new CharArraySet(Version.LUCENE_31,
200       Arrays.asList("θ", "δ", "ελ", "γαλ", "ν", "π", "ιδ", "παρ"),
201       false);
202   
203   private int rule4(char s[], int len) {   
204     if (len > 3 && (endsWith(s, len, "εωσ") || endsWith(s, len, "εων"))) {
205       len -= 3;
206       if (exc4.contains(s, 0, len))
207         len++; // add back -ε
208     }
209     return len;
210   }
211   
212   private int rule5(char s[], int len) {
213     if (len > 2 && endsWith(s, len, "ια")) {
214       len -= 2;
215       if (endsWithVowel(s, len))
216         len++; // add back -ι
217     } else if (len > 3 && (endsWith(s, len, "ιου") || endsWith(s, len, "ιων"))) {
218       len -= 3;
219       if (endsWithVowel(s, len))
220         len++; // add back -ι
221     }
222     return len;
223   }
224
225   private static final CharArraySet exc6 = new CharArraySet(Version.LUCENE_31,
226       Arrays.asList("αλ", "αδ", "ενδ", "αμαν", "αμμοχαλ", "ηθ", "ανηθ",
227           "αντιδ", "φυσ", "βρωμ", "γερ", "εξωδ", "καλπ", "καλλιν", "καταδ",
228           "μουλ", "μπαν", "μπαγιατ", "μπολ", "μποσ", "νιτ", "ξικ", "συνομηλ",
229           "πετσ", "πιτσ", "πικαντ", "πλιατσ", "ποστελν", "πρωτοδ", "σερτ",
230           "συναδ", "τσαμ", "υποδ", "φιλον", "φυλοδ", "χασ"), 
231        false);
232
233   private int rule6(char s[], int len) {
234     boolean removed = false;
235     if (len > 3 && (endsWith(s, len, "ικα") || endsWith(s, len, "ικο"))) {
236       len -= 3;
237       removed = true;
238     } else if (len > 4 && (endsWith(s, len, "ικου") || endsWith(s, len, "ικων"))) {
239       len -= 4;
240       removed = true;
241     }
242     
243     if (removed) {
244       if (endsWithVowel(s, len) || exc6.contains(s, 0, len))
245         len += 2; // add back -ικ
246     }
247     return len;
248   }
249   
250   private static final CharArraySet exc7 = new CharArraySet(Version.LUCENE_31,
251       Arrays.asList("αναπ", "αποθ", "αποκ", "αποστ", "βουβ", "ξεθ", "ουλ",
252           "πεθ", "πικρ", "ποτ", "σιχ", "χ"), 
253       false);
254   
255   private int rule7(char s[], int len) {
256     if (len == 5 && endsWith(s, len, "αγαμε"))
257       return len - 1;
258     
259     if (len > 7 && endsWith(s, len, "ηθηκαμε"))
260       len -= 7;
261     else if (len > 6 && endsWith(s, len, "ουσαμε"))
262       len -= 6;
263     else if (len > 5 && (endsWith(s, len, "αγαμε") ||
264              endsWith(s, len, "ησαμε") ||
265              endsWith(s, len, "ηκαμε")))
266       len -= 5;
267     
268     if (len > 3 && endsWith(s, len, "αμε")) {
269       len -= 3;
270       if (exc7.contains(s, 0, len))
271         len += 2; // add back -αμ
272     }
273
274     return len;
275   }
276
277   private static final CharArraySet exc8a = new CharArraySet(Version.LUCENE_31,
278       Arrays.asList("τρ", "τσ"),
279       false);
280
281   private static final CharArraySet exc8b = new CharArraySet(Version.LUCENE_31,
282       Arrays.asList("βετερ", "βουλκ", "βραχμ", "γ", "δραδουμ", "θ", "καλπουζ",
283           "καστελ", "κορμορ", "λαοπλ", "μωαμεθ", "μ", "μουσουλμ", "ν", "ουλ",
284           "π", "πελεκ", "πλ", "πολισ", "πορτολ", "σαρακατσ", "σουλτ",
285           "τσαρλατ", "ορφ", "τσιγγ", "τσοπ", "φωτοστεφ", "χ", "ψυχοπλ", "αγ",
286           "ορφ", "γαλ", "γερ", "δεκ", "διπλ", "αμερικαν", "ουρ", "πιθ",
287           "πουριτ", "σ", "ζωντ", "ικ", "καστ", "κοπ", "λιχ", "λουθηρ", "μαιντ",
288           "μελ", "σιγ", "σπ", "στεγ", "τραγ", "τσαγ", "φ", "ερ", "αδαπ",
289           "αθιγγ", "αμηχ", "ανικ", "ανοργ", "απηγ", "απιθ", "ατσιγγ", "βασ",
290           "βασκ", "βαθυγαλ", "βιομηχ", "βραχυκ", "διατ", "διαφ", "ενοργ",
291           "θυσ", "καπνοβιομηχ", "καταγαλ", "κλιβ", "κοιλαρφ", "λιβ",
292           "μεγλοβιομηχ", "μικροβιομηχ", "νταβ", "ξηροκλιβ", "ολιγοδαμ",
293           "ολογαλ", "πενταρφ", "περηφ", "περιτρ", "πλατ", "πολυδαπ", "πολυμηχ",
294           "στεφ", "ταβ", "τετ", "υπερηφ", "υποκοπ", "χαμηλοδαπ", "ψηλοταβ"),
295       false);
296   
297   private int rule8(char s[], int len) {
298     boolean removed = false;
299     
300     if (len > 8 && endsWith(s, len, "ιουντανε")) {
301       len -= 8;
302       removed = true;
303     } else if (len > 7 && endsWith(s, len, "ιοντανε") ||
304         endsWith(s, len, "ουντανε") ||
305         endsWith(s, len, "ηθηκανε")) {
306       len -= 7;
307       removed = true;
308     } else if (len > 6 && endsWith(s, len, "ιοτανε") ||
309         endsWith(s, len, "οντανε") ||
310         endsWith(s, len, "ουσανε")) {
311       len -= 6;
312       removed = true;
313     } else if (len > 5 && endsWith(s, len, "αγανε") ||
314         endsWith(s, len, "ησανε") ||
315         endsWith(s, len, "οτανε") ||
316         endsWith(s, len, "ηκανε")) {
317       len -= 5;
318       removed = true;
319     }
320     
321     if (removed && exc8a.contains(s, 0, len)) {
322       // add -αγαν (we removed > 4 chars so its safe)
323       len += 4;
324       s[len - 4] = 'α';
325       s[len - 3] = 'γ';
326       s[len - 2] = 'α';
327       s[len - 1] = 'ν';
328     }
329     
330     if (len > 3 && endsWith(s, len, "ανε")) {
331       len -= 3;
332       if (endsWithVowelNoY(s, len) || exc8b.contains(s, 0, len)) {
333         len += 2; // add back -αν
334       }
335     }
336     
337     return len;
338   }
339   
340   private static final CharArraySet exc9 = new CharArraySet(Version.LUCENE_31,
341       Arrays.asList("αβαρ", "βεν", "εναρ", "αβρ", "αδ", "αθ", "αν", "απλ",
342           "βαρον", "ντρ", "σκ", "κοπ", "μπορ", "νιφ", "παγ", "παρακαλ", "σερπ",
343           "σκελ", "συρφ", "τοκ", "υ", "δ", "εμ", "θαρρ", "θ"), 
344       false);
345   
346   private int rule9(char s[], int len) {
347     if (len > 5 && endsWith(s, len, "ησετε"))
348       len -= 5;
349     
350     if (len > 3 && endsWith(s, len, "ετε")) {
351       len -= 3;
352       if (exc9.contains(s, 0, len) ||
353           endsWithVowelNoY(s, len) ||
354           endsWith(s, len, "οδ") ||
355           endsWith(s, len, "αιρ") ||
356           endsWith(s, len, "φορ") ||
357           endsWith(s, len, "ταθ") ||
358           endsWith(s, len, "διαθ") ||
359           endsWith(s, len, "σχ") ||
360           endsWith(s, len, "ενδ") ||
361           endsWith(s, len, "ευρ") ||
362           endsWith(s, len, "τιθ") ||
363           endsWith(s, len, "υπερθ") ||
364           endsWith(s, len, "ραθ") ||
365           endsWith(s, len, "ενθ") ||
366           endsWith(s, len, "ροθ") ||
367           endsWith(s, len, "σθ") ||
368           endsWith(s, len, "πυρ") ||
369           endsWith(s, len, "αιν") ||
370           endsWith(s, len, "συνδ") ||
371           endsWith(s, len, "συν") ||
372           endsWith(s, len, "συνθ") ||
373           endsWith(s, len, "χωρ") ||
374           endsWith(s, len, "πον") ||
375           endsWith(s, len, "βρ") ||
376           endsWith(s, len, "καθ") ||
377           endsWith(s, len, "ευθ") ||
378           endsWith(s, len, "εκθ") ||
379           endsWith(s, len, "νετ") ||
380           endsWith(s, len, "ρον") ||
381           endsWith(s, len, "αρκ") ||
382           endsWith(s, len, "βαρ") ||
383           endsWith(s, len, "βολ") ||
384           endsWith(s, len, "ωφελ")) {
385         len += 2; // add back -ετ
386       }
387     }
388     
389     return len;
390   }
391
392   private int rule10(char s[], int len) {
393     if (len > 5 && (endsWith(s, len, "οντασ") || endsWith(s, len, "ωντασ"))) {
394       len -= 5;
395       if (len == 3 && endsWith(s, len, "αρχ")) {
396         len += 3; // add back *ντ
397         s[len - 3] = 'ο';
398       }
399       if (endsWith(s, len, "κρε")) {
400         len += 3; // add back *ντ
401         s[len - 3] = 'ω';
402       }
403     }
404     
405     return len;
406   }
407   
408   private int rule11(char s[], int len) {
409     if (len > 6 && endsWith(s, len, "ομαστε")) {
410       len -= 6;
411       if (len == 2 && endsWith(s, len, "ον")) {
412         len += 5; // add back -ομαστ
413       }
414     } else if (len > 7 && endsWith(s, len, "ιομαστε")) {
415       len -= 7;
416       if (len == 2 && endsWith(s, len, "ον")) {
417         len += 5;
418         s[len - 5] = 'ο';
419         s[len - 4] = 'μ';
420         s[len - 3] = 'α';
421         s[len - 2] = 'σ';
422         s[len - 1] = 'τ';
423       }
424     }
425     return len;
426   }
427
428   private static final CharArraySet exc12a = new CharArraySet(Version.LUCENE_31,
429       Arrays.asList("π", "απ", "συμπ", "ασυμπ", "ακαταπ", "αμεταμφ"),
430       false);
431
432   private static final CharArraySet exc12b = new CharArraySet(Version.LUCENE_31,
433       Arrays.asList("αλ", "αρ", "εκτελ", "ζ", "μ", "ξ", "παρακαλ", "αρ", "προ", "νισ"),
434       false);
435   
436   private int rule12(char s[], int len) {
437     if (len > 5 && endsWith(s, len, "ιεστε")) {
438       len -= 5;
439       if (exc12a.contains(s, 0, len))   
440         len += 4; // add back -ιεστ
441     }
442     
443     if (len > 4 && endsWith(s, len, "εστε")) {
444       len -= 4;
445       if (exc12b.contains(s, 0, len))
446         len += 3; // add back -εστ
447     }
448     
449     return len;
450   }
451   
452   private static final CharArraySet exc13 = new CharArraySet(Version.LUCENE_31,
453       Arrays.asList("διαθ", "θ", "παρακαταθ", "προσθ", "συνθ"),
454       false);
455   
456   private int rule13(char s[], int len) {
457     if (len > 6 && endsWith(s, len, "ηθηκεσ")) {
458       len -= 6;
459     } else if (len > 5 && (endsWith(s, len, "ηθηκα") || endsWith(s, len, "ηθηκε"))) {
460       len -= 5;
461     }
462     
463     boolean removed = false;
464     
465     if (len > 4 && endsWith(s, len, "ηκεσ")) {
466       len -= 4;
467       removed = true;
468     } else if (len > 3 && (endsWith(s, len, "ηκα") || endsWith(s, len, "ηκε"))) {
469       len -= 3;
470       removed = true;
471     }
472
473     if (removed && (exc13.contains(s, 0, len) 
474         || endsWith(s, len, "σκωλ")
475         || endsWith(s, len, "σκουλ")
476         || endsWith(s, len, "ναρθ")
477         || endsWith(s, len, "σφ")
478         || endsWith(s, len, "οθ")
479         || endsWith(s, len, "πιθ"))) { 
480       len += 2; // add back the -ηκ
481     }
482     
483     return len;
484   }
485   
486   private static final CharArraySet exc14 = new CharArraySet(Version.LUCENE_31,
487       Arrays.asList("φαρμακ", "χαδ", "αγκ", "αναρρ", "βρομ", "εκλιπ", "λαμπιδ",
488           "λεχ", "μ", "πατ", "ρ", "λ", "μεδ", "μεσαζ", "υποτειν", "αμ", "αιθ",
489           "ανηκ", "δεσποζ", "ενδιαφερ", "δε", "δευτερευ", "καθαρευ", "πλε",
490           "τσα"), 
491       false);
492
493   private int rule14(char s[], int len) {
494     boolean removed = false;
495     
496     if (len > 5 && endsWith(s, len, "ουσεσ")) {
497       len -= 5;
498       removed = true;
499     } else if (len > 4 && (endsWith(s, len, "ουσα") || endsWith(s, len, "ουσε"))) {
500       len -= 4;
501       removed = true;
502     }
503     
504     if (removed && (exc14.contains(s, 0, len) 
505         || endsWithVowel(s, len)
506         || endsWith(s, len, "ποδαρ")
507         || endsWith(s, len, "βλεπ")
508         || endsWith(s, len, "πανταχ")
509         || endsWith(s, len, "φρυδ") 
510         || endsWith(s, len, "μαντιλ")
511         || endsWith(s, len, "μαλλ")
512         || endsWith(s, len, "κυματ")
513         || endsWith(s, len, "λαχ")
514         || endsWith(s, len, "ληγ")
515         || endsWith(s, len, "φαγ")
516         || endsWith(s, len, "ομ")
517         || endsWith(s, len, "πρωτ"))) {
518       len += 3; // add back -ουσ
519     }
520
521    return len;
522   }
523   
524   private static final CharArraySet exc15a = new CharArraySet(Version.LUCENE_31,
525       Arrays.asList("αβαστ", "πολυφ", "αδηφ", "παμφ", "ρ", "ασπ", "αφ", "αμαλ",
526           "αμαλλι", "ανυστ", "απερ", "ασπαρ", "αχαρ", "δερβεν", "δροσοπ",
527           "ξεφ", "νεοπ", "νομοτ", "ολοπ", "ομοτ", "προστ", "προσωποπ", "συμπ",
528           "συντ", "τ", "υποτ", "χαρ", "αειπ", "αιμοστ", "ανυπ", "αποτ",
529           "αρτιπ", "διατ", "εν", "επιτ", "κροκαλοπ", "σιδηροπ", "λ", "ναυ",
530           "ουλαμ", "ουρ", "π", "τρ", "μ"), 
531       false);
532   
533   private static final CharArraySet exc15b = new CharArraySet(Version.LUCENE_31,
534       Arrays.asList("ψοφ", "ναυλοχ"),
535       false);
536   
537   private int rule15(char s[], int len) {
538     boolean removed = false;
539     if (len > 4 && endsWith(s, len, "αγεσ")) {
540       len -= 4;
541       removed = true;
542     } else if (len > 3 && (endsWith(s, len, "αγα") || endsWith(s, len, "αγε"))) {
543       len -= 3;
544       removed = true;
545     }
546     
547     if (removed) {
548       final boolean cond1 = exc15a.contains(s, 0, len) 
549         || endsWith(s, len, "οφ")
550         || endsWith(s, len, "πελ")
551         || endsWith(s, len, "χορτ")
552         || endsWith(s, len, "λλ")
553         || endsWith(s, len, "σφ")
554         || endsWith(s, len, "ρπ")
555         || endsWith(s, len, "φρ")
556         || endsWith(s, len, "πρ")
557         || endsWith(s, len, "λοχ")
558         || endsWith(s, len, "σμην");
559       
560       final boolean cond2 = exc15b.contains(s, 0, len)
561         || endsWith(s, len, "κολλ");
562       
563       if (cond1 && !cond2)
564         len += 2; // add back -αγ  
565     }
566     
567     return len;
568   }
569   
570   private static final CharArraySet exc16 = new CharArraySet(Version.LUCENE_31,
571       Arrays.asList("ν", "χερσον", "δωδεκαν", "ερημον", "μεγαλον", "επταν"),
572       false);
573   
574   private int rule16(char s[], int len) {
575     boolean removed = false;
576     if (len > 4 && endsWith(s, len, "ησου")) {
577       len -= 4;
578       removed = true;
579     } else if (len > 3 && (endsWith(s, len, "ησε") || endsWith(s, len, "ησα"))) {
580       len -= 3;
581       removed = true;
582     }
583     
584     if (removed && exc16.contains(s, 0, len))
585       len += 2; // add back -ησ
586     
587     return len;
588   }
589   
590   private static final CharArraySet exc17 = new CharArraySet(Version.LUCENE_31,
591       Arrays.asList("ασβ", "σβ", "αχρ", "χρ", "απλ", "αειμν", "δυσχρ", "ευχρ", "κοινοχρ", "παλιμψ"),
592       false);
593   
594   private int rule17(char s[], int len) {
595     if (len > 4 && endsWith(s, len, "ηστε")) {
596       len -= 4;
597       if (exc17.contains(s, 0, len))
598         len += 3; // add back the -ηστ
599     }
600     
601     return len;
602   }
603   
604   private static final CharArraySet exc18 = new CharArraySet(Version.LUCENE_31,
605       Arrays.asList("ν", "ρ", "σπι", "στραβομουτσ", "κακομουτσ", "εξων"),
606       false);
607   
608   private int rule18(char s[], int len) {
609     boolean removed = false;
610     
611     if (len > 6 && (endsWith(s, len, "ησουνε") || endsWith(s, len, "ηθουνε"))) {
612       len -= 6;
613       removed = true;
614     } else if (len > 4 && endsWith(s, len, "ουνε")) {
615       len -= 4;
616       removed = true;
617     }
618     
619     if (removed && exc18.contains(s, 0, len)) {
620       len += 3;
621       s[len - 3] = 'ο';
622       s[len - 2] = 'υ';
623       s[len - 1] = 'ν';
624     }
625     return len;
626   }
627   
628   private static final CharArraySet exc19 = new CharArraySet(Version.LUCENE_31,
629       Arrays.asList("παρασουσ", "φ", "χ", "ωριοπλ", "αζ", "αλλοσουσ", "ασουσ"),
630       false);
631   
632   private int rule19(char s[], int len) {
633     boolean removed = false;
634     
635     if (len > 6 && (endsWith(s, len, "ησουμε") || endsWith(s, len, "ηθουμε"))) {
636       len -= 6;
637       removed = true;
638     } else if (len > 4 && endsWith(s, len, "ουμε")) {
639       len -= 4;
640       removed = true;
641     }
642     
643     if (removed && exc19.contains(s, 0, len)) {
644       len += 3;
645       s[len - 3] = 'ο';
646       s[len - 2] = 'υ';
647       s[len - 1] = 'μ';
648     }
649     return len;
650   }
651   
652   private int rule20(char s[], int len) {
653     if (len > 5 && (endsWith(s, len, "ματων") || endsWith(s, len, "ματοσ")))
654       len -= 3;
655     else if (len > 4 && endsWith(s, len, "ματα"))
656       len -= 2;
657     return len;
658   }
659
660   private int rule21(char s[], int len) {
661     if (len > 9 && endsWith(s, len, "ιοντουσαν"))
662       return len - 9;
663     
664     if (len > 8 && (endsWith(s, len, "ιομασταν") ||
665         endsWith(s, len, "ιοσασταν") ||
666         endsWith(s, len, "ιουμαστε") ||
667         endsWith(s, len, "οντουσαν")))
668       return len - 8;
669     
670     if (len > 7 && (endsWith(s, len, "ιεμαστε") ||
671         endsWith(s, len, "ιεσαστε") ||
672         endsWith(s, len, "ιομουνα") ||
673         endsWith(s, len, "ιοσαστε") ||
674         endsWith(s, len, "ιοσουνα") ||
675         endsWith(s, len, "ιουνται") ||
676         endsWith(s, len, "ιουνταν") ||
677         endsWith(s, len, "ηθηκατε") ||
678         endsWith(s, len, "ομασταν") ||
679         endsWith(s, len, "οσασταν") ||
680         endsWith(s, len, "ουμαστε")))
681       return len - 7;
682     
683     if (len > 6 && (endsWith(s, len, "ιομουν") ||
684         endsWith(s, len, "ιονταν") ||
685         endsWith(s, len, "ιοσουν") ||
686         endsWith(s, len, "ηθειτε") ||
687         endsWith(s, len, "ηθηκαν") ||
688         endsWith(s, len, "ομουνα") ||
689         endsWith(s, len, "οσαστε") ||
690         endsWith(s, len, "οσουνα") ||
691         endsWith(s, len, "ουνται") ||
692         endsWith(s, len, "ουνταν") ||
693         endsWith(s, len, "ουσατε")))
694       return len - 6;
695     
696     if (len > 5 && (endsWith(s, len, "αγατε") ||
697         endsWith(s, len, "ιεμαι") ||
698         endsWith(s, len, "ιεται") ||
699         endsWith(s, len, "ιεσαι") ||
700         endsWith(s, len, "ιοταν") ||
701         endsWith(s, len, "ιουμα") ||
702         endsWith(s, len, "ηθεισ") ||
703         endsWith(s, len, "ηθουν") ||
704         endsWith(s, len, "ηκατε") ||
705         endsWith(s, len, "ησατε") ||
706         endsWith(s, len, "ησουν") ||
707         endsWith(s, len, "ομουν") ||
708         endsWith(s, len, "ονται") ||
709         endsWith(s, len, "ονταν") ||
710         endsWith(s, len, "οσουν") ||
711         endsWith(s, len, "ουμαι") ||
712         endsWith(s, len, "ουσαν")))
713       return len - 5;
714     
715     if (len > 4 && (endsWith(s, len, "αγαν") ||
716         endsWith(s, len, "αμαι") ||
717         endsWith(s, len, "ασαι") ||
718         endsWith(s, len, "αται") ||
719         endsWith(s, len, "ειτε") ||
720         endsWith(s, len, "εσαι") ||
721         endsWith(s, len, "εται") ||
722         endsWith(s, len, "ηδεσ") ||
723         endsWith(s, len, "ηδων") ||
724         endsWith(s, len, "ηθει") ||
725         endsWith(s, len, "ηκαν") ||
726         endsWith(s, len, "ησαν") ||
727         endsWith(s, len, "ησει") ||
728         endsWith(s, len, "ησεσ") ||
729         endsWith(s, len, "ομαι") ||
730         endsWith(s, len, "οταν")))
731       return len - 4;
732     
733     if (len > 3 && (endsWith(s, len, "αει") ||
734         endsWith(s, len, "εισ") ||
735         endsWith(s, len, "ηθω") ||
736         endsWith(s, len, "ησω") ||
737         endsWith(s, len, "ουν") ||
738         endsWith(s, len, "ουσ")))
739       return len - 3;
740     
741     if (len > 2 && (endsWith(s, len, "αν") ||
742         endsWith(s, len, "ασ") ||
743         endsWith(s, len, "αω") ||
744         endsWith(s, len, "ει") ||
745         endsWith(s, len, "εσ") ||
746         endsWith(s, len, "ησ") ||
747         endsWith(s, len, "οι") ||
748         endsWith(s, len, "οσ") ||
749         endsWith(s, len, "ου") ||
750         endsWith(s, len, "υσ") ||
751         endsWith(s, len, "ων")))
752       return len - 2;
753     
754     if (len > 1 && endsWithVowel(s, len))
755       return len - 1;
756
757     return len;
758   }
759   
760   private int rule22(char s[], int len) {
761     if (endsWith(s, len, "εστερ") ||
762         endsWith(s, len, "εστατ"))
763       return len - 5;
764     
765     if (endsWith(s, len, "οτερ") ||
766         endsWith(s, len, "οτατ") ||
767         endsWith(s, len, "υτερ") ||
768         endsWith(s, len, "υτατ") ||
769         endsWith(s, len, "ωτερ") ||
770         endsWith(s, len, "ωτατ"))
771       return len - 4;
772
773     return len;
774   }
775
776   private boolean endsWith(char s[], int len, String suffix) {
777     final int suffixLen = suffix.length();
778     if (suffixLen > len)
779       return false;
780     for (int i = suffixLen - 1; i >= 0; i--)
781       if (s[len -(suffixLen - i)] != suffix.charAt(i))
782         return false;
783     
784     return true;
785   }
786   
787   private boolean endsWithVowel(char s[], int len) {
788     if (len == 0)
789       return false;
790     switch(s[len - 1]) {
791       case 'α':
792       case 'ε':
793       case 'η':
794       case 'ι':
795       case 'ο':
796       case 'υ':
797       case 'ω':
798         return true;
799       default:
800         return false;
801     }
802   }
803   
804   private boolean endsWithVowelNoY(char s[], int len) {
805     if (len == 0)
806       return false;
807     switch(s[len - 1]) {
808       case 'α':
809       case 'ε':
810       case 'η':
811       case 'ι':
812       case 'ο':
813       case 'ω':
814         return true;
815       default:
816         return false;
817     }
818   }
819 }