File Coverage

blib/lib/Math/Aronson.pm
Criterion Covered Total %
statement 87 93 93.5
branch 27 34 79.4
condition 8 13 61.5
subroutine 12 13 92.3
pod 2 2 100.0
total 136 155 87.7


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012, 2017, 2019 Kevin Ryde
2              
3             # This file is part of Math-Aronson.
4             #
5             # Math-Aronson is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Math-Aronson is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Math-Aronson. If not, see .
17              
18             package Math::Aronson;
19 3     3   11138 use 5.004;
  3         19  
20 3     3   16 use strict;
  3         6  
  3         58  
21 3     3   17 use Carp;
  3         5  
  3         290  
22              
23             # uncomment this to run the ### lines
24             #use Devel::Comments;
25              
26 3     3   22 use vars '$VERSION';
  3         12  
  3         399  
27             $VERSION = 10;
28              
29             # maybe a hi=>$limit option to stop the ret or queue building up beyond a
30             # desired point
31              
32              
33             my $unaccent;
34             BEGIN {
35 3 50   3   62311 if (eval "use Unicode::Normalize 'normalize'; 1") {
  3     3   1535  
  3         5495  
  3         163  
36             $unaccent = sub {
37             ### unaccent: $_[0]
38             # uncombine the latin-1 etc equivs then strip the zero-width marks
39 3     3   1704 ($_[0] = normalize('D',$_[0])) =~ s/\pM+//g;
  3         41  
  3         44  
  216         446  
40 3         3199 };
41             } else {
42             $unaccent = sub {
43             # latin-1, generated by devel/unaccent.pl
44 0         0 $_[0] =~ tr/\300\301\302\303\304\305\307\310\311\312\313\314\315\316\317\321\322\323\324\325\326\331\332\333\334\335\340\341\342\343\344\345\347\350\351\352\353\354\355\356\357\361\362\363\364\365\366\371\372\373\374\375\377/AAAAAACEEEEIIIINOOOOOUUUUYaaaaaaceeeeiiiinooooouuuuyy/;
45 0         0 };
46             }
47             }
48              
49             my %default_letter = ('en' => 'T',
50             'fr' => 'E');
51             my %default_initial_string = ('en' => 'is the',
52             'fr' => 'est la');
53             sub new {
54 17     17 1 3780 my $class = shift;
55             ### Aronson new(): @_
56              
57 17         33 my @ret;
58 17         72 my $self = bless { ret => \@ret,
59             queue => [ ],
60             @_
61             }, $class;
62              
63             # 1 or '' for use with xor
64 17         52 $self->{'lying'} = !! $self->{'lying'};
65              
66 17   100     83 my $lang = ($self->{'lang'} ||= 'en'); # default
67 17 100       44 if ($lang eq 'en') {
    50          
68 16         83 %$self = (conjunctions_word => 'and',
69             %$self);
70             } elsif ($lang eq 'fr') {
71 1         6 %$self = (conjunctions_word => 'et',
72             %$self);
73             }
74             # for oeis_anum()
75 17 50       61 $self->{'lang'} = ($self->{'ordinal_func'} ? 'func' : lc($lang));
76              
77 17         33 my $without_conjunctions = delete $self->{'without_conjunctions'};
78 17         27 my $conjunctions_word = delete $self->{'conjunctions_word'};
79              
80 17 100 33     92 $self->{'conjunctions'}
    50          
81             = (($lang eq 'en' && $conjunctions_word ne 'and')
82             && ($lang eq 'fr' && $conjunctions_word ne 'et')
83             ? 'x'
84             : ($without_conjunctions ? 0 : 1));
85              
86             $self->{'ordinal_func'} ||=
87             ($lang eq 'en' ? do {
88 16         991 require Lingua::EN::Numbers;
89 16         4246 Lingua::EN::Numbers->VERSION(1.01); # 1.01 rewrite
90 16         85 \&Lingua::EN::Numbers::num2en_ordinal
91             }
92             : $lang eq 'fr' ? do {
93 1         489 require Lingua::FR::Numbers;
94 1         1807 \&_fr_ordinal
95             }
96 17 50 33     70 : do {
    100          
97 0         0 require Lingua::Any::Numbers;
98             sub {
99 0     0   0 return Lingua::Any::Numbers::to_ordinal($_[0], $lang);
100             }
101 0         0 });
102              
103             my $without_conjunctions_func
104             = $self->{'without_conjunctions_func'}
105             = ($without_conjunctions && defined $conjunctions_word
106 17 100 66     75 ? do {
107 3         7 $conjunctions_word = lc($conjunctions_word);
108 106     106   416 sub { $_[0] =~ s/\b\Q$conjunctions_word\E\b// }
109 3         15 }
110             : \&_conjunctions_noop); # no change to strings
111              
112 17         34 my $initial_string = delete $self->{'initial_string'};
113 17         33 my $letter = $self->{'letter'};
114              
115 17 100       38 if (! defined $initial_string) {
116 13 100       30 if (! $letter) {
117             # default 'T' for en or 'E' for fr
118 8         29 $letter = $default_letter{$lang};
119             }
120 13 50       63 if (! defined ($initial_string = $default_initial_string{$lang})) {
121 0         0 croak 'No default initial_string for language \'',$lang,'\'';
122             }
123 13         25 $initial_string = $letter . $initial_string;
124             }
125              
126 17         50 &$unaccent ($initial_string);
127 17         264 $initial_string = lc ($initial_string);
128              
129 17         51 &$without_conjunctions_func ($initial_string);
130 17         112 $initial_string =~ s/(\W|_)+//g; # strip non alphas
131             ### initial: $initial_string
132              
133 17 100       50 if (! defined $letter) {
134 4 50       15 if (defined $initial_string) {
135             # initial_string but no letter, take letter as first alphabetical
136 4         13 $letter = substr($initial_string,0,1);
137             } else {
138             }
139             }
140              
141 17 100       36 unless (length($letter)) {
142             # empty letter string no good as will match endlessly, change to a space
143             # which will never match
144 1         2 $letter = ' ';
145             }
146 17         44 $self->{'letter'} = $letter = lc($letter);
147              
148             # my $upto = 1;
149             push @ret,
150 17         56 grep {(substr($initial_string,$_-1,1) eq $letter) ^ $self->{'lying'}}
  95         182  
151             1 .. (1 + length($initial_string)-1);
152 17         35 $self->{'upto'} = 1 + length($initial_string);
153             ### initial: $self
154 17         52 return $self;
155             }
156              
157       110     sub _conjunctions_noop {
158             }
159              
160             sub _fr_ordinal {
161 19     19   86 my $str = Lingua::FR::Numbers::ordinate_to_fr($_[0]);
162             # Feminine "E est la premiere lettre ..."
163 19 100       1298 if ($str eq 'premier') { $str = 'premiere'; }
  1         3  
164 19         55 return $str;
165             }
166              
167              
168             sub next {
169 477     477 1 1478 my ($self) = @_;
170 477         615 my $ret = $self->{'ret'};
171 477         565 for (;;) {
172 676 100       1227 if (my $n = shift @$ret) {
173 472         571 push @{$self->{'queue'}}, $n;
  472         703  
174 472         968 return $n;
175             }
176              
177 204   100     251 my $k = shift @{$self->{'queue'}}
178             || return; # end of sequence
179              
180 199         252 my $str = &{$self->{'ordinal_func'}}($k);
  199         373  
181             ### orig str: $str
182 199         9623 &{$self->{'without_conjunctions_func'}}($str);
  199         404  
183 199         434 &$unaccent ($str);
184 199         2222 $str = lc ($str);
185              
186             # could be s/[[:punct:][:space:]]+//g, but [::] new in 5.005 or something
187 199         1032 $str =~ s/(\W|_)+//g; # strip non alphas
188             ### munged str: $str
189              
190 199         339 my $upto = $self->{'upto'};
191 199         285 my $letter = $self->{'letter'};
192             push @$ret,
193 199         612 grep {(substr($str,$_-$upto,1) eq $letter) ^ $self->{'lying'}}
  2467         3700  
194             $upto .. ($upto + length($str)-1);
195              
196 199         420 $self->{'upto'} += length($str);
197             ### now upto: $self->{'upto'}
198             ### ret: $ret
199             ### queue: $self->{'queue'}
200             }
201             }
202              
203             1;
204             __END__