Passed
Push — master ( c2a3b6...15a61d )
by Chris
01:00 queued 14s
created

abydos.stemmer._lovins   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 1246
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 52
eloc 471
dl 0
loc 1246
ccs 105
cts 105
cp 1
rs 7.44
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
A Lovins._cond_y() 0 22 1
A Lovins._cond_u() 0 22 1
A Lovins._cond_g() 0 22 1
A Lovins._cond_k() 0 24 1
A Lovins._cond_n() 0 28 4
A Lovins._cond_c() 0 22 1
A Lovins._cond_bb() 0 25 1
A Lovins._recode24() 0 22 2
A Lovins._cond_l() 0 24 1
A Lovins._cond_j() 0 22 1
A Lovins._cond_i() 0 22 1
A Lovins._recode28() 0 22 2
A Lovins._cond_cc() 0 22 1
A Lovins._cond_z() 0 22 1
A Lovins._cond_x() 0 24 1
A Lovins._cond_aa() 0 24 1
A Lovins._cond_v() 0 22 1
A Lovins._cond_m() 0 22 1
A Lovins._cond_b() 0 22 1
A Lovins._cond_e() 0 22 1
A Lovins._cond_d() 0 22 1
C Lovins.stem() 0 67 10
A Lovins._cond_p() 0 22 1
A Lovins._cond_h() 0 24 1
A Lovins._cond_f() 0 22 1
A Lovins._cond_s() 0 24 1
A Lovins._cond_o() 0 22 1
A Lovins._recode32() 0 22 2
A Lovins._recode9() 0 22 2
A Lovins._cond_q() 0 24 1
A Lovins._cond_t() 0 24 1
A Lovins._cond_w() 0 22 1
A Lovins._recode30() 0 22 2
B Lovins.__init__() 0 338 1
A Lovins._cond_r() 0 22 1

How to fix   Complexity   

Complexity

Complex classes like abydos.stemmer._lovins often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# Copyright 2014-2020 by Christopher C. Little.
2
# This file is part of Abydos.
3
#
4
# Abydos is free software: you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# Abydos is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with Abydos. If not, see <http://www.gnu.org/licenses/>.
16
17
"""abydos.stemmer._lovins.
18
19 1
Lovins stemmer.
20
"""
21
22
from unicodedata import normalize
23
24 1
from ._stemmer import _Stemmer
25
26
__all__ = ['Lovins']
27
28
29
class Lovins(_Stemmer):
30
    """Lovins stemmer.
31 1
32
    The Lovins stemmer is described in Julie Beth Lovins's article
33 1
    :cite:`Lovins:1968`.
34
35 1
    .. versionadded:: 0.3.6
36 1
    """
37
38 1
    def _cond_b(self, word, suffix_len):
39 1
        """Return Lovins' condition B.
40
41 1
        Parameters
42
        ----------
43
        word : str
44 1
            Word to check
45
        suffix_len : int
46
            Suffix length
47
48
        Returns
49
        -------
50
        bool
51
            True if condition is met
52
53 1
54
        .. versionadded:: 0.2.0
55
        .. versionchanged:: 0.3.6
56
            Encapsulated in class
57
58
        """
59
        return len(word) - suffix_len >= 3
60
61
    def _cond_c(self, word, suffix_len):
62
        """Return Lovins' condition C.
63
64
        Parameters
65
        ----------
66
        word : str
67
            Word to check
68
        suffix_len : int
69
            Suffix length
70
71
        Returns
72
        -------
73
        bool
74 1
            True if condition is met
75
76 1
77
        .. versionadded:: 0.2.0
78
        .. versionchanged:: 0.3.6
79
            Encapsulated in class
80
81
        """
82
        return len(word) - suffix_len >= 4
83
84
    def _cond_d(self, word, suffix_len):
85
        """Return Lovins' condition D.
86
87
        Parameters
88
        ----------
89
        word : str
90
            Word to check
91
        suffix_len : int
92
            Suffix length
93
94
        Returns
95
        -------
96
        bool
97 1
            True if condition is met
98
99 1
100
        .. versionadded:: 0.2.0
101
        .. versionchanged:: 0.3.6
102
            Encapsulated in class
103
104
        """
105
        return len(word) - suffix_len >= 5
106
107
    def _cond_e(self, word, suffix_len):
108
        """Return Lovins' condition E.
109
110
        Parameters
111
        ----------
112
        word : str
113
            Word to check
114
        suffix_len : int
115
            Suffix length
116
117
        Returns
118
        -------
119
        bool
120 1
            True if condition is met
121
122 1
123
        .. versionadded:: 0.2.0
124
        .. versionchanged:: 0.3.6
125
            Encapsulated in class
126
127
        """
128
        return word[-suffix_len - 1] != 'e'
129
130
    def _cond_f(self, word, suffix_len):
131
        """Return Lovins' condition F.
132
133
        Parameters
134
        ----------
135
        word : str
136
            Word to check
137
        suffix_len : int
138
            Suffix length
139
140
        Returns
141
        -------
142
        bool
143 1
            True if condition is met
144
145 1
146
        .. versionadded:: 0.2.0
147
        .. versionchanged:: 0.3.6
148
            Encapsulated in class
149
150
        """
151
        return len(word) - suffix_len >= 3 and word[-suffix_len - 1] != 'e'
152
153
    def _cond_g(self, word, suffix_len):
154
        """Return Lovins' condition G.
155
156
        Parameters
157
        ----------
158
        word : str
159
            Word to check
160
        suffix_len : int
161
            Suffix length
162
163
        Returns
164
        -------
165
        bool
166 1
            True if condition is met
167
168 1
169
        .. versionadded:: 0.2.0
170
        .. versionchanged:: 0.3.6
171
            Encapsulated in class
172
173
        """
174
        return len(word) - suffix_len >= 3 and word[-suffix_len - 1] == 'f'
175
176
    def _cond_h(self, word, suffix_len):
177
        """Return Lovins' condition H.
178
179
        Parameters
180
        ----------
181
        word : str
182
            Word to check
183
        suffix_len : int
184
            Suffix length
185
186
        Returns
187
        -------
188
        bool
189 1
            True if condition is met
190
191 1
192
        .. versionadded:: 0.2.0
193
        .. versionchanged:: 0.3.6
194
            Encapsulated in class
195
196
        """
197
        return (
198
            word[-suffix_len - 1] == 't'
199
            or word[-suffix_len - 2 : -suffix_len] == 'll'
200
        )
201
202
    def _cond_i(self, word, suffix_len):
203
        """Return Lovins' condition I.
204
205
        Parameters
206
        ----------
207
        word : str
208
            Word to check
209
        suffix_len : int
210
            Suffix length
211
212 1
        Returns
213
        -------
214
        bool
215
            True if condition is met
216
217 1
218
        .. versionadded:: 0.2.0
219
        .. versionchanged:: 0.3.6
220
            Encapsulated in class
221
222
        """
223
        return word[-suffix_len - 1] not in {'e', 'o'}
224
225
    def _cond_j(self, word, suffix_len):
226
        """Return Lovins' condition J.
227
228
        Parameters
229
        ----------
230
        word : str
231
            Word to check
232
        suffix_len : int
233
            Suffix length
234
235
        Returns
236
        -------
237
        bool
238 1
            True if condition is met
239
240 1
241
        .. versionadded:: 0.2.0
242
        .. versionchanged:: 0.3.6
243
            Encapsulated in class
244
245
        """
246
        return word[-suffix_len - 1] not in {'a', 'e'}
247
248
    def _cond_k(self, word, suffix_len):
249
        """Return Lovins' condition K.
250
251
        Parameters
252
        ----------
253
        word : str
254
            Word to check
255
        suffix_len : int
256
            Suffix length
257
258
        Returns
259
        -------
260
        bool
261 1
            True if condition is met
262
263 1
264
        .. versionadded:: 0.2.0
265
        .. versionchanged:: 0.3.6
266
            Encapsulated in class
267
268
        """
269
        return (len(word) - suffix_len >= 3) and (
270
            word[-suffix_len - 1] in {'i', 'l'}
271
            or (word[-suffix_len - 3] == 'u' and word[-suffix_len - 1] == 'e')
272
        )
273
274
    def _cond_l(self, word, suffix_len):
275
        """Return Lovins' condition L.
276
277
        Parameters
278
        ----------
279
        word : str
280
            Word to check
281
        suffix_len : int
282
            Suffix length
283
284 1
        Returns
285
        -------
286
        bool
287
            True if condition is met
288
289 1
290
        .. versionadded:: 0.2.0
291
        .. versionchanged:: 0.3.6
292
            Encapsulated in class
293
294
        """
295
        return (
296
            word[-suffix_len - 1] not in {'s', 'u', 'x'}
297
            or word[-suffix_len - 1] == 'os'
298
        )
299
300
    def _cond_m(self, word, suffix_len):
301
        """Return Lovins' condition M.
302
303
        Parameters
304
        ----------
305
        word : str
306
            Word to check
307
        suffix_len : int
308
            Suffix length
309
310 1
        Returns
311
        -------
312
        bool
313
            True if condition is met
314
315 1
316
        .. versionadded:: 0.2.0
317
        .. versionchanged:: 0.3.6
318
            Encapsulated in class
319
320
        """
321
        return word[-suffix_len - 1] not in {'a', 'c', 'e', 'm'}
322
323
    def _cond_n(self, word, suffix_len):
324
        """Return Lovins' condition N.
325
326
        Parameters
327
        ----------
328
        word : str
329
            Word to check
330
        suffix_len : int
331
            Suffix length
332
333
        Returns
334
        -------
335
        bool
336 1
            True if condition is met
337
338 1
339
        .. versionadded:: 0.2.0
340
        .. versionchanged:: 0.3.6
341
            Encapsulated in class
342
343
        """
344
        if len(word) - suffix_len >= 3:
345
            if word[-suffix_len - 3] == 's':
346
                if len(word) - suffix_len >= 4:
347
                    return True
348
            else:
349
                return True
350
        return False
351
352
    def _cond_o(self, word, suffix_len):
353
        """Return Lovins' condition O.
354
355
        Parameters
356
        ----------
357
        word : str
358
            Word to check
359 1
        suffix_len : int
360 1
            Suffix length
361 1
362 1
        Returns
363
        -------
364 1
        bool
365 1
            True if condition is met
366
367 1
368
        .. versionadded:: 0.2.0
369
        .. versionchanged:: 0.3.6
370
            Encapsulated in class
371
372
        """
373
        return word[-suffix_len - 1] in {'i', 'l'}
374
375
    def _cond_p(self, word, suffix_len):
376
        """Return Lovins' condition P.
377
378
        Parameters
379
        ----------
380
        word : str
381
            Word to check
382
        suffix_len : int
383
            Suffix length
384
385
        Returns
386
        -------
387
        bool
388 1
            True if condition is met
389
390 1
391
        .. versionadded:: 0.2.0
392
        .. versionchanged:: 0.3.6
393
            Encapsulated in class
394
395
        """
396
        return word[-suffix_len - 1] != 'c'
397
398
    def _cond_q(self, word, suffix_len):
399
        """Return Lovins' condition Q.
400
401
        Parameters
402
        ----------
403
        word : str
404
            Word to check
405
        suffix_len : int
406
            Suffix length
407
408
        Returns
409
        -------
410
        bool
411 1
            True if condition is met
412
413 1
414
        .. versionadded:: 0.2.0
415
        .. versionchanged:: 0.3.6
416
            Encapsulated in class
417
418
        """
419
        return len(word) - suffix_len >= 3 and word[-suffix_len - 1] not in {
420
            'l',
421
            'n',
422
        }
423
424
    def _cond_r(self, word, suffix_len):
425
        """Return Lovins' condition R.
426
427
        Parameters
428
        ----------
429
        word : str
430
            Word to check
431
        suffix_len : int
432
            Suffix length
433
434 1
        Returns
435
        -------
436
        bool
437
            True if condition is met
438
439 1
440
        .. versionadded:: 0.2.0
441
        .. versionchanged:: 0.3.6
442
            Encapsulated in class
443
444
        """
445
        return word[-suffix_len - 1] in {'n', 'r'}
446
447
    def _cond_s(self, word, suffix_len):
448
        """Return Lovins' condition S.
449
450
        Parameters
451
        ----------
452
        word : str
453
            Word to check
454
        suffix_len : int
455
            Suffix length
456
457
        Returns
458
        -------
459
        bool
460 1
            True if condition is met
461
462 1
463
        .. versionadded:: 0.2.0
464
        .. versionchanged:: 0.3.6
465
            Encapsulated in class
466
467
        """
468
        return word[-suffix_len - 2 : -suffix_len] == 'dr' or (
469
            word[-suffix_len - 1] == 't'
470
            and word[-suffix_len - 2 : -suffix_len] != 'tt'
471
        )
472
473
    def _cond_t(self, word, suffix_len):
474
        """Return Lovins' condition T.
475
476
        Parameters
477
        ----------
478
        word : str
479
            Word to check
480
        suffix_len : int
481
            Suffix length
482
483 1
        Returns
484
        -------
485
        bool
486
            True if condition is met
487
488 1
489
        .. versionadded:: 0.2.0
490
        .. versionchanged:: 0.3.6
491
            Encapsulated in class
492
493
        """
494
        return (
495
            word[-suffix_len - 1] in {'s', 't'}
496
            and word[-suffix_len - 2 : -suffix_len] != 'ot'
497
        )
498
499
    def _cond_u(self, word, suffix_len):
500
        """Return Lovins' condition U.
501
502
        Parameters
503
        ----------
504
        word : str
505
            Word to check
506
        suffix_len : int
507
            Suffix length
508
509 1
        Returns
510
        -------
511
        bool
512
            True if condition is met
513
514 1
515
        .. versionadded:: 0.2.0
516
        .. versionchanged:: 0.3.6
517
            Encapsulated in class
518
519
        """
520
        return word[-suffix_len - 1] in {'l', 'm', 'n', 'r'}
521
522
    def _cond_v(self, word, suffix_len):
523
        """Return Lovins' condition V.
524
525
        Parameters
526
        ----------
527
        word : str
528
            Word to check
529
        suffix_len : int
530
            Suffix length
531
532
        Returns
533
        -------
534
        bool
535 1
            True if condition is met
536
537 1
538
        .. versionadded:: 0.2.0
539
        .. versionchanged:: 0.3.6
540
            Encapsulated in class
541
542
        """
543
        return word[-suffix_len - 1] == 'c'
544
545
    def _cond_w(self, word, suffix_len):
546
        """Return Lovins' condition W.
547
548
        Parameters
549
        ----------
550
        word : str
551
            Word to check
552
        suffix_len : int
553
            Suffix length
554
555
        Returns
556
        -------
557
        bool
558 1
            True if condition is met
559
560 1
561
        .. versionadded:: 0.2.0
562
        .. versionchanged:: 0.3.6
563
            Encapsulated in class
564
565
        """
566
        return word[-suffix_len - 1] not in {'s', 'u'}
567
568
    def _cond_x(self, word, suffix_len):
569
        """Return Lovins' condition X.
570
571
        Parameters
572
        ----------
573
        word : str
574
            Word to check
575
        suffix_len : int
576
            Suffix length
577
578
        Returns
579
        -------
580
        bool
581 1
            True if condition is met
582
583 1
584
        .. versionadded:: 0.2.0
585
        .. versionchanged:: 0.3.6
586
            Encapsulated in class
587
588
        """
589
        return word[-suffix_len - 1] in {'i', 'l'} or (
590
            word[-suffix_len - 3 : -suffix_len] == 'u'
591
            and word[-suffix_len - 1] == 'e'
592
        )
593
594
    def _cond_y(self, word, suffix_len):
595
        """Return Lovins' condition Y.
596
597
        Parameters
598
        ----------
599
        word : str
600
            Word to check
601
        suffix_len : int
602
            Suffix length
603
604 1
        Returns
605
        -------
606
        bool
607
            True if condition is met
608
609 1
610
        .. versionadded:: 0.2.0
611
        .. versionchanged:: 0.3.6
612
            Encapsulated in class
613
614
        """
615
        return word[-suffix_len - 2 : -suffix_len] == 'in'
616
617
    def _cond_z(self, word, suffix_len):
618
        """Return Lovins' condition Z.
619
620
        Parameters
621
        ----------
622
        word : str
623
            Word to check
624
        suffix_len : int
625
            Suffix length
626
627
        Returns
628
        -------
629
        bool
630 1
            True if condition is met
631
632 1
633
        .. versionadded:: 0.2.0
634
        .. versionchanged:: 0.3.6
635
            Encapsulated in class
636
637
        """
638
        return word[-suffix_len - 1] != 'f'
639
640
    def _cond_aa(self, word, suffix_len):
641
        """Return Lovins' condition AA.
642
643
        Parameters
644
        ----------
645
        word : str
646
            Word to check
647
        suffix_len : int
648
            Suffix length
649
650
        Returns
651
        -------
652
        bool
653 1
            True if condition is met
654
655 1
656
        .. versionadded:: 0.2.0
657
        .. versionchanged:: 0.3.6
658
            Encapsulated in class
659
660
        """
661
        return word[-suffix_len - 1] in {'d', 'f', 'l', 't'} or word[
662
            -suffix_len - 2 : -suffix_len
663
        ] in {'ph', 'th', 'er', 'or', 'es'}
664
665
    def _cond_bb(self, word, suffix_len):
666
        """Return Lovins' condition BB.
667
668
        Parameters
669
        ----------
670
        word : str
671
            Word to check
672
        suffix_len : int
673
            Suffix length
674
675
        Returns
676 1
        -------
677
        bool
678
            True if condition is met
679
680 1
681
        .. versionadded:: 0.2.0
682
        .. versionchanged:: 0.3.6
683
            Encapsulated in class
684
685
        """
686
        return (
687
            len(word) - suffix_len >= 3
688
            and word[-suffix_len - 3 : -suffix_len] != 'met'
689
            and word[-suffix_len - 4 : -suffix_len] != 'ryst'
690
        )
691
692
    def _cond_cc(self, word, suffix_len):
693
        """Return Lovins' condition CC.
694
695
        Parameters
696
        ----------
697
        word : str
698
            Word to check
699
        suffix_len : int
700
            Suffix length
701 1
702
        Returns
703
        -------
704
        bool
705
            True if condition is met
706
707 1
708
        .. versionadded:: 0.2.0
709
        .. versionchanged:: 0.3.6
710
            Encapsulated in class
711
712
        """
713
        return word[-suffix_len - 1] == 'l'
714
715
    def _recode9(self, stem):
716
        """Return Lovins' conditional recode rule 9.
717
718
        Parameters
719
        ----------
720
        stem : str
721
            Word to stem
722
723
        Returns
724
        -------
725
        str
726
            Word stripped of suffix
727
728 1
729
        .. versionadded:: 0.2.0
730 1
        .. versionchanged:: 0.3.6
731
            Encapsulated in class
732
733
        """
734
        if stem[-3:-2] in {'a', 'i', 'o'}:
735
            return stem
736
        return stem[:-2] + 'l'
737
738
    def _recode24(self, stem):
739
        """Return Lovins' conditional recode rule 24.
740
741
        Parameters
742
        ----------
743
        stem : str
744
            Word to stem
745
746
        Returns
747
        -------
748
        str
749 1
            Word stripped of suffix
750 1
751 1
752
        .. versionadded:: 0.2.0
753 1
        .. versionchanged:: 0.3.6
754
            Encapsulated in class
755
756
        """
757
        if stem[-4:-3] == 's':
758
            return stem
759
        return stem[:-1] + 's'
760
761
    def _recode28(self, stem):
762
        """Return Lovins' conditional recode rule 28.
763
764
        Parameters
765
        ----------
766
        stem : str
767
            Word to stem
768
769
        Returns
770
        -------
771
        str
772 1
            Word stripped of suffix
773 1
774 1
775
        .. versionadded:: 0.2.0
776 1
        .. versionchanged:: 0.3.6
777
            Encapsulated in class
778
779
        """
780
        if stem[-4:-3] in {'p', 't'}:
781
            return stem
782
        return stem[:-1] + 's'
783
784
    def _recode30(self, stem):
785
        """Return Lovins' conditional recode rule 30.
786
787
        Parameters
788
        ----------
789
        stem : str
790
            Word to stem
791
792
        Returns
793
        -------
794
        str
795 1
            Word stripped of suffix
796 1
797 1
798
        .. versionadded:: 0.2.0
799 1
        .. versionchanged:: 0.3.6
800
            Encapsulated in class
801
802
        """
803
        if stem[-4:-3] == 'm':
804
            return stem
805
        return stem[:-1] + 's'
806
807
    def _recode32(self, stem):
808
        """Return Lovins' conditional recode rule 32.
809
810
        Parameters
811
        ----------
812
        stem : str
813
            Word to stem
814
815
        Returns
816
        -------
817
        str
818 1
            Word stripped of suffix
819 1
820 1
821
        .. versionadded:: 0.2.0
822 1
        .. versionchanged:: 0.3.6
823
            Encapsulated in class
824
825
        """
826
        if stem[-3:-2] == 'n':
827
            return stem
828
        return stem[:-1] + 's'
829
830
    _suffix = {}
831
    _recode = ()
832
833
    def __init__(self):
834
        """Initialize the stemmer.
835
836
        .. versionadded:: 0.3.6
837
838
        """
839
        self._suffix = {
840
            'alistically': self._cond_b,
841 1
            'arizability': None,
842 1
            'izationally': self._cond_b,
843 1
            'antialness': None,
844
            'arisations': None,
845 1
            'arizations': None,
846 1
            'entialness': None,
847
            'allically': self._cond_c,
848 1
            'antaneous': None,
849
            'antiality': None,
850
            'arisation': None,
851
            'arization': None,
852
            'ationally': self._cond_b,
853
            'ativeness': None,
854 1
            'eableness': self._cond_e,
855
            'entations': None,
856
            'entiality': None,
857
            'entialize': None,
858
            'entiation': None,
859
            'ionalness': None,
860
            'istically': None,
861
            'itousness': None,
862
            'izability': None,
863
            'izational': None,
864
            'ableness': None,
865
            'arizable': None,
866
            'entation': None,
867
            'entially': None,
868
            'eousness': None,
869
            'ibleness': None,
870
            'icalness': None,
871
            'ionalism': None,
872
            'ionality': None,
873
            'ionalize': None,
874
            'iousness': None,
875
            'izations': None,
876
            'lessness': None,
877
            'ability': None,
878
            'aically': None,
879
            'alistic': self._cond_b,
880
            'alities': None,
881
            'ariness': self._cond_e,
882
            'aristic': None,
883
            'arizing': None,
884
            'ateness': None,
885
            'atingly': None,
886
            'ational': self._cond_b,
887
            'atively': None,
888
            'ativism': None,
889
            'elihood': self._cond_e,
890
            'encible': None,
891
            'entally': None,
892
            'entials': None,
893
            'entiate': None,
894
            'entness': None,
895
            'fulness': None,
896
            'ibility': None,
897
            'icalism': None,
898
            'icalist': None,
899
            'icality': None,
900
            'icalize': None,
901
            'ication': self._cond_g,
902
            'icianry': None,
903
            'ination': None,
904
            'ingness': None,
905
            'ionally': None,
906
            'isation': None,
907
            'ishness': None,
908
            'istical': None,
909
            'iteness': None,
910
            'iveness': None,
911
            'ivistic': None,
912
            'ivities': None,
913
            'ization': self._cond_f,
914
            'izement': None,
915
            'oidally': None,
916
            'ousness': None,
917
            'aceous': None,
918
            'acious': self._cond_b,
919
            'action': self._cond_g,
920
            'alness': None,
921
            'ancial': None,
922
            'ancies': None,
923
            'ancing': self._cond_b,
924
            'ariser': None,
925
            'arized': None,
926
            'arizer': None,
927
            'atable': None,
928
            'ations': self._cond_b,
929
            'atives': None,
930
            'eature': self._cond_z,
931
            'efully': None,
932
            'encies': None,
933
            'encing': None,
934
            'ential': None,
935
            'enting': self._cond_c,
936
            'entist': None,
937
            'eously': None,
938
            'ialist': None,
939
            'iality': None,
940
            'ialize': None,
941
            'ically': None,
942
            'icance': None,
943
            'icians': None,
944
            'icists': None,
945
            'ifully': None,
946
            'ionals': None,
947
            'ionate': self._cond_d,
948
            'ioning': None,
949
            'ionist': None,
950
            'iously': None,
951
            'istics': None,
952
            'izable': self._cond_e,
953
            'lessly': None,
954
            'nesses': None,
955
            'oidism': None,
956
            'acies': None,
957
            'acity': None,
958
            'aging': self._cond_b,
959
            'aical': None,
960
            'alist': None,
961
            'alism': self._cond_b,
962
            'ality': None,
963
            'alize': None,
964
            'allic': self._cond_bb,
965
            'anced': self._cond_b,
966
            'ances': self._cond_b,
967
            'antic': self._cond_c,
968
            'arial': None,
969
            'aries': None,
970
            'arily': None,
971
            'arity': self._cond_b,
972
            'arize': None,
973
            'aroid': None,
974
            'ately': None,
975
            'ating': self._cond_i,
976
            'ation': self._cond_b,
977
            'ative': None,
978
            'ators': None,
979
            'atory': None,
980
            'ature': self._cond_e,
981
            'early': self._cond_y,
982
            'ehood': None,
983
            'eless': None,
984
            'elity': None,
985
            'ement': None,
986
            'enced': None,
987
            'ences': None,
988
            'eness': self._cond_e,
989
            'ening': self._cond_e,
990
            'ental': None,
991
            'ented': self._cond_c,
992
            'ently': None,
993
            'fully': None,
994
            'ially': None,
995
            'icant': None,
996
            'ician': None,
997
            'icide': None,
998
            'icism': None,
999
            'icist': None,
1000
            'icity': None,
1001
            'idine': self._cond_i,
1002
            'iedly': None,
1003
            'ihood': None,
1004
            'inate': None,
1005
            'iness': None,
1006
            'ingly': self._cond_b,
1007
            'inism': self._cond_j,
1008
            'inity': self._cond_cc,
1009
            'ional': None,
1010
            'ioned': None,
1011
            'ished': None,
1012
            'istic': None,
1013
            'ities': None,
1014
            'itous': None,
1015
            'ively': None,
1016
            'ivity': None,
1017
            'izers': self._cond_f,
1018
            'izing': self._cond_f,
1019
            'oidal': None,
1020
            'oides': None,
1021
            'otide': None,
1022
            'ously': None,
1023
            'able': None,
1024
            'ably': None,
1025
            'ages': self._cond_b,
1026
            'ally': self._cond_b,
1027
            'ance': self._cond_b,
1028
            'ancy': self._cond_b,
1029
            'ants': self._cond_b,
1030
            'aric': None,
1031
            'arly': self._cond_k,
1032
            'ated': self._cond_i,
1033
            'ates': None,
1034
            'atic': self._cond_b,
1035
            'ator': None,
1036
            'ealy': self._cond_y,
1037
            'edly': self._cond_e,
1038
            'eful': None,
1039
            'eity': None,
1040
            'ence': None,
1041
            'ency': None,
1042
            'ened': self._cond_e,
1043
            'enly': self._cond_e,
1044
            'eous': None,
1045
            'hood': None,
1046
            'ials': None,
1047
            'ians': None,
1048
            'ible': None,
1049
            'ibly': None,
1050
            'ical': None,
1051
            'ides': self._cond_l,
1052
            'iers': None,
1053
            'iful': None,
1054
            'ines': self._cond_m,
1055
            'ings': self._cond_n,
1056
            'ions': self._cond_b,
1057
            'ious': None,
1058
            'isms': self._cond_b,
1059
            'ists': None,
1060
            'itic': self._cond_h,
1061
            'ized': self._cond_f,
1062
            'izer': self._cond_f,
1063
            'less': None,
1064
            'lily': None,
1065
            'ness': None,
1066
            'ogen': None,
1067
            'ward': None,
1068
            'wise': None,
1069
            'ying': self._cond_b,
1070
            'yish': None,
1071
            'acy': None,
1072
            'age': self._cond_b,
1073
            'aic': None,
1074
            'als': self._cond_bb,
1075
            'ant': self._cond_b,
1076
            'ars': self._cond_o,
1077
            'ary': self._cond_f,
1078
            'ata': None,
1079
            'ate': None,
1080
            'eal': self._cond_y,
1081
            'ear': self._cond_y,
1082
            'ely': self._cond_e,
1083
            'ene': self._cond_e,
1084
            'ent': self._cond_c,
1085
            'ery': self._cond_e,
1086
            'ese': None,
1087
            'ful': None,
1088
            'ial': None,
1089
            'ian': None,
1090
            'ics': None,
1091
            'ide': self._cond_l,
1092
            'ied': None,
1093
            'ier': None,
1094
            'ies': self._cond_p,
1095
            'ily': None,
1096
            'ine': self._cond_m,
1097
            'ing': self._cond_n,
1098
            'ion': self._cond_q,
1099
            'ish': self._cond_c,
1100
            'ism': self._cond_b,
1101
            'ist': None,
1102
            'ite': self._cond_aa,
1103
            'ity': None,
1104
            'ium': None,
1105
            'ive': None,
1106
            'ize': self._cond_f,
1107
            'oid': None,
1108
            'one': self._cond_r,
1109
            'ous': None,
1110
            'ae': None,
1111
            'al': self._cond_bb,
1112
            'ar': self._cond_x,
1113
            'as': self._cond_b,
1114
            'ed': self._cond_e,
1115
            'en': self._cond_f,
1116
            'es': self._cond_e,
1117
            'ia': None,
1118
            'ic': None,
1119
            'is': None,
1120
            'ly': self._cond_b,
1121
            'on': self._cond_s,
1122
            'or': self._cond_t,
1123
            'um': self._cond_u,
1124
            'us': self._cond_v,
1125
            'yl': self._cond_r,
1126
            "'s": None,
1127
            "s'": None,
1128
            'a': None,
1129
            'e': None,
1130
            'i': None,
1131
            'o': None,
1132
            's': self._cond_w,
1133
            'y': self._cond_b,
1134
        }
1135
1136
        self._recode = (
1137
            ('iev', 'ief'),
1138
            ('uct', 'uc'),
1139
            ('umpt', 'um'),
1140
            ('rpt', 'rb'),
1141
            ('urs', 'ur'),
1142
            ('istr', 'ister'),
1143
            ('metr', 'meter'),
1144
            ('olv', 'olut'),
1145
            ('ul', self._recode9),
1146
            ('bex', 'bic'),
1147
            ('dex', 'dic'),
1148
            ('pex', 'pic'),
1149
            ('tex', 'tic'),
1150
            ('ax', 'ac'),
1151 1
            ('ex', 'ec'),
1152
            ('ix', 'ic'),
1153
            ('lux', 'luc'),
1154
            ('uad', 'uas'),
1155
            ('vad', 'vas'),
1156
            ('cid', 'cis'),
1157
            ('lid', 'lis'),
1158
            ('erid', 'eris'),
1159
            ('pand', 'pans'),
1160
            ('end', self._recode24),
1161
            ('ond', 'ons'),
1162
            ('lud', 'lus'),
1163
            ('rud', 'rus'),
1164
            ('her', self._recode28),
1165
            ('mit', 'mis'),
1166
            ('ent', self._recode30),
1167
            ('ert', 'ers'),
1168
            ('et', self._recode32),
1169
            ('yt', 'ys'),
1170
            ('yz', 'ys'),
1171
        )
1172
1173
    def stem(self, word):
1174
        """Return Lovins stem.
1175
1176
        Parameters
1177
        ----------
1178
        word : str
1179
            The word to stem
1180
1181
        Returns
1182
        -------
1183
        str
1184
            Word stem
1185
1186
        Examples
1187
        --------
1188 1
        >>> stmr = Lovins()
1189
        >>> stmr.stem('reading')
1190
        'read'
1191
        >>> stmr.stem('suspension')
1192
        'suspens'
1193
        >>> stmr.stem('elusiveness')
1194
        'elus'
1195
1196
1197
        .. versionadded:: 0.2.0
1198
        .. versionchanged:: 0.3.6
1199
            Encapsulated in class
1200
1201
        """
1202
        # lowercase, normalize, and compose
1203
        word = normalize('NFC', word.lower())
1204
1205
        for suffix_len in range(11, 0, -1):
1206
            ending = word[-suffix_len:]
1207
            if (
1208
                ending in self._suffix
1209
                and len(word) - suffix_len >= 2
1210
                and (
1211
                    self._suffix[ending] is None
1212
                    or self._suffix[ending](word, suffix_len)
1213
                )
1214
            ):
1215
                word = word[:-suffix_len]
1216
                break
1217
1218 1
        if word[-2:] in {
1219
            'bb',
1220 1
            'dd',
1221 1
            'gg',
1222 1
            'll',
1223
            'mm',
1224
            'nn',
1225
            'pp',
1226
            'rr',
1227
            'ss',
1228
            'tt',
1229
        }:
1230 1
            word = word[:-1]
1231 1
1232
        for ending, replacement in self._recode:
1233 1
            if word.endswith(ending):
1234
                if callable(replacement):
1235
                    word = replacement(word)
1236
                else:
1237
                    word = word[: -len(ending)] + replacement
1238
1239
        return word
1240
1241
1242
if __name__ == '__main__':
1243
    import doctest
1244
1245
    doctest.testmod()
1246