File Coverage

blib/lib/Math/Aronson.pm
Criterion Covered Total %
statement 89 95 93.6
branch 27 34 79.4
condition 8 13 61.5
subroutine 12 13 92.3
pod 2 2 100.0
total 138 157 87.9


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012 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   36790 use 5.004;
  3         13  
  3         124  
20 3     3   18 use strict;
  3         6  
  3         95  
21 3     3   15 use Carp;
  3         9  
  3         244  
22              
23             # uncomment this to run the ### lines
24             #use Devel::Comments;
25              
26 3     3   15 use vars '$VERSION';
  3         6  
  3         496  
27             $VERSION = 9;
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   172049 if (eval "use Unicode::Normalize 'normalize'; 1") {
  3     3   7234  
  3         16643  
  3         3239  
36             $unaccent = sub {
37             ### unaccent: $_[0]
38             # uncombine the latin-1 etc equivs then strip the zero-width marks
39 3     3   8576 ($_[0] = normalize('D',$_[0])) =~ s/\pM+//g;
  3         37  
  3         51  
  216         620  
40 3         4011 };
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 8243 my $class = shift;
55             ### Aronson new(): @_
56              
57 17         32 my @ret;
58 17         127 my $self = bless { ret => \@ret,
59             queue => [ ],
60             @_
61             }, $class;
62              
63             # 1 or '' for use with xor
64 17         62 $self->{'lying'} = !! $self->{'lying'};
65              
66 17   100     87 my $lang = ($self->{'lang'} ||= 'en'); # default
67 17 100       48 if ($lang eq 'en') {
    50          
68 16         159 %$self = (conjunctions_word => 'and',
69             %$self);
70             } elsif ($lang eq 'fr') {
71 1         8 %$self = (conjunctions_word => 'et',
72             %$self);
73             }
74             # for oeis_anum()
75 17 50       84 $self->{'lang'} = ($self->{'ordinal_func'} ? 'func' : lc($lang));
76              
77 17         39 my $without_conjunctions = delete $self->{'without_conjunctions'};
78 17         40 my $conjunctions_word = delete $self->{'conjunctions_word'};
79              
80 17 100 33     149 $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         12995 require Lingua::EN::Numbers;
89 16         7284 Lingua::EN::Numbers->VERSION(1.01); # 1.01 rewrite
90 16         104 \&Lingua::EN::Numbers::num2en_ordinal
91             }
92             : $lang eq 'fr' ? do {
93 1         1122 require Lingua::FR::Numbers;
94 1         7829 \&_fr_ordinal
95             }
96 17 50 33     73 : 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     102 ? do {
107 3         8 $conjunctions_word = lc($conjunctions_word);
108 106     106   500 sub { $_[0] =~ s/\b\Q$conjunctions_word\E\b// }
109 3         22 }
110             : \&_conjunctions_noop); # no change to strings
111              
112 17         38 my $initial_string = delete $self->{'initial_string'};
113 17         29 my $letter = $self->{'letter'};
114              
115 17 100       46 if (! defined $initial_string) {
116 13 100       30 if (! $letter) {
117             # default 'T' for en or 'E' for fr
118 8         21 $letter = $default_letter{$lang};
119             }
120 13 50       47 if (! defined ($initial_string = $default_initial_string{$lang})) {
121 0         0 croak 'No default initial_string for language \'',$lang,'\'';
122             }
123 13         31 $initial_string = $letter . $initial_string;
124             }
125              
126 17         45 &$unaccent ($initial_string);
127 17         310 $initial_string = lc ($initial_string);
128              
129 17         38 &$without_conjunctions_func ($initial_string);
130 17         142 $initial_string =~ s/(\W|_)+//g; # strip non alphas
131             ### initial: $initial_string
132              
133 17 100       51 if (! defined $letter) {
134 4 50       11 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       43 unless (length($letter)) {
142             # empty letter string no good as will match endlessly, change to a space
143             # which will never match
144 1         3 $letter = ' ';
145             }
146 17         60 $self->{'letter'} = $letter = lc($letter);
147              
148             # my $upto = 1;
149 95         213 push @ret,
150 17         85 grep {(substr($initial_string,$_-1,1) eq $letter) ^ $self->{'lying'}}
151             1 .. (1 + length($initial_string)-1);
152 17         41 $self->{'upto'} = 1 + length($initial_string);
153             ### initial: $self
154 17         73 return $self;
155             }
156              
157 110     110   137 sub _conjunctions_noop {
158             }
159              
160             sub _fr_ordinal {
161 19     19   56 my $str = Lingua::FR::Numbers::ordinate_to_fr($_[0]);
162             # Feminine "E est la premiere lettre ..."
163 19 100       1204 if ($str eq 'premier') { $str = 'premiere'; }
  1         2  
164 19         35 return $str;
165             }
166              
167              
168             sub next {
169 477     477 1 1825 my ($self) = @_;
170 477         617 my $ret = $self->{'ret'};
171 477         498 for (;;) {
172 676 100       1430 if (my $n = shift @$ret) {
173 472         458 push @{$self->{'queue'}}, $n;
  472         916  
174 472         1375 return $n;
175             }
176              
177 204   100     232 my $k = shift @{$self->{'queue'}}
178             || return; # end of sequence
179              
180 199         387 my $str = &{$self->{'ordinal_func'}}($k);
  199         994  
181             ### orig str: $str
182 199         9008 &{$self->{'without_conjunctions_func'}}($str);
  199         632  
183 199         363 &$unaccent ($str);
184 199         2550 $str = lc ($str);
185              
186             # could be s/[[:punct:][:space:]]+//g, but [::] new in 5.005 or something
187 199         1428 $str =~ s/(\W|_)+//g; # strip non alphas
188             ### munged str: $str
189              
190 199         347 my $upto = $self->{'upto'};
191 199         282 my $letter = $self->{'letter'};
192 2467         4326 push @$ret,
193 199         645 grep {(substr($str,$_-$upto,1) eq $letter) ^ $self->{'lying'}}
194             $upto .. ($upto + length($str)-1);
195              
196 199         493 $self->{'upto'} += length($str);
197             ### now upto: $self->{'upto'}
198             ### ret: $ret
199             ### queue: $self->{'queue'}
200             }
201             }
202              
203             1;
204             __END__