line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Validate::CodiceFiscale; |
2
|
2
|
|
|
2
|
|
78003
|
use v5.24; |
|
2
|
|
|
|
|
16
|
|
3
|
2
|
|
|
2
|
|
10
|
use Carp; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
117
|
|
4
|
2
|
|
|
2
|
|
582
|
use experimental qw< signatures >; |
|
2
|
|
|
|
|
3604
|
|
|
2
|
|
|
|
|
16
|
|
5
|
|
|
|
|
|
|
{ our $VERSION = '0.001' } |
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
363
|
use List::Util 'sum'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
233
|
|
8
|
2
|
|
|
2
|
|
1122
|
use Time::Local 'timegm'; |
|
2
|
|
|
|
|
4681
|
|
|
2
|
|
|
|
|
114
|
|
9
|
2
|
|
|
2
|
|
16
|
use Exporter 'import'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
5171
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our @EXPORT_OK = qw< assert_valid_cf is_valid_cf validate_cf r >; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# PUBLIC interface |
14
|
|
|
|
|
|
|
|
15
|
0
|
|
|
0
|
1
|
0
|
sub assert_valid_cf ($cf, %options) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
16
|
0
|
0
|
|
|
|
0
|
my $errors = validate_cf($cf, all_errors => 0, %options) or return; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
defined(my $ecb = $options{on_error}) |
19
|
0
|
0
|
|
|
|
0
|
or croak join ', ', $errors->@*; |
20
|
|
|
|
|
|
|
|
21
|
0
|
|
|
|
|
0
|
my $exception = $ecb->($errors->@*); |
22
|
0
|
|
|
|
|
0
|
die $exception; # just as a fallback, $ecb might throw by itself |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
} ## end sub assert_valid_cf |
25
|
|
|
|
|
|
|
|
26
|
17
|
|
|
17
|
1
|
13530
|
sub is_valid_cf ($cf, %options) { |
|
17
|
|
|
|
|
35
|
|
|
17
|
|
|
|
|
27
|
|
|
17
|
|
|
|
|
26
|
|
27
|
17
|
|
|
|
|
33
|
my $error = 0; |
28
|
17
|
|
|
14
|
|
100
|
_validate_cf($cf, $options{data}, sub { $error = 1; return 0 }); |
|
14
|
|
|
|
|
26
|
|
|
14
|
|
|
|
|
65
|
|
29
|
17
|
|
|
|
|
91
|
return !$error; |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
|
32
|
17
|
|
|
17
|
1
|
5243
|
sub validate_cf ($cf, %options) { |
|
17
|
|
|
|
|
30
|
|
|
17
|
|
|
|
|
27
|
|
|
17
|
|
|
|
|
33
|
|
33
|
17
|
|
100
|
|
|
57
|
my $data = $options{data} // undef; |
34
|
|
|
|
|
|
|
|
35
|
17
|
|
50
|
|
|
55
|
my $collect_all_errors = $options{all_errors} // 1; |
36
|
17
|
|
|
|
|
31
|
my @errors; |
37
|
17
|
|
|
17
|
|
26
|
my $callback = sub ($msg) { |
|
17
|
|
|
|
|
31
|
|
|
17
|
|
|
|
|
22
|
|
38
|
17
|
|
|
|
|
31
|
push @errors, $msg; |
39
|
17
|
|
|
|
|
67
|
return $collect_all_errors; |
40
|
17
|
|
|
|
|
73
|
}; |
41
|
|
|
|
|
|
|
|
42
|
17
|
|
|
|
|
46
|
_validate_cf($cf, $data, $callback); |
43
|
|
|
|
|
|
|
|
44
|
17
|
100
|
|
|
|
96
|
return scalar(@errors) ? \@errors : undef; |
45
|
|
|
|
|
|
|
} ## end sub validate_cf |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# The following is useful for one-lines: |
48
|
|
|
|
|
|
|
# |
49
|
|
|
|
|
|
|
# $ perl -MValidate::CodiceFiscale=r -er bcadfe88a48h501p |
50
|
|
|
|
|
|
|
# |
51
|
0
|
|
|
0
|
1
|
0
|
sub r (@args) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
52
|
0
|
0
|
|
|
|
0
|
@args = @ARGV unless @args; |
53
|
0
|
|
|
|
|
0
|
my $i = 0; |
54
|
0
|
|
|
|
|
0
|
my $n = 0; |
55
|
0
|
|
|
|
|
0
|
for my $cf (@ARGV) { |
56
|
0
|
0
|
|
|
|
0
|
if (my $errors = validate_cf($cf)) { |
57
|
0
|
|
|
|
|
0
|
say "$i not ok - " . join(', ', $errors->@*); |
58
|
0
|
|
|
|
|
0
|
++$n; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
else { |
61
|
0
|
|
|
|
|
0
|
say "$i ok - $cf"; |
62
|
|
|
|
|
|
|
} |
63
|
0
|
|
|
|
|
0
|
++$i; |
64
|
|
|
|
|
|
|
} ## end for my $cf (@ARGV) |
65
|
0
|
0
|
|
|
|
0
|
return $n ? 1 : 0; |
66
|
|
|
|
|
|
|
} ## end sub r |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
exit r(@ARGV) unless caller(); # modulino |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# PRIVATE interface |
71
|
|
|
|
|
|
|
|
72
|
34
|
|
|
34
|
|
46
|
sub _validate_cf ($cf, $data, $cb) { |
|
34
|
|
|
|
|
53
|
|
|
34
|
|
|
|
|
57
|
|
|
34
|
|
|
|
|
51
|
|
|
34
|
|
|
|
|
43
|
|
73
|
34
|
|
|
|
|
62
|
state $consonant = qr{(?imxs:[BCDFGHJKLMNPQRSTVWXYZ])}; |
74
|
34
|
|
|
|
|
72
|
state $vowel = qr{(?imxs:[AEIOU])}; |
75
|
34
|
|
|
|
|
258
|
state $namish = qr{(?imxs: |
76
|
|
|
|
|
|
|
$consonant $consonant $consonant # includes CCX, CXX, XXX |
77
|
|
|
|
|
|
|
| $consonant $consonant $vowel |
78
|
|
|
|
|
|
|
| $consonant $vowel $vowel |
79
|
|
|
|
|
|
|
| $consonant $vowel X |
80
|
|
|
|
|
|
|
| $vowel $vowel $vowel |
81
|
|
|
|
|
|
|
| $vowel $vowel X |
82
|
|
|
|
|
|
|
| $vowel X X |
83
|
|
|
|
|
|
|
)}; |
84
|
34
|
|
|
|
|
69
|
state $digitish = qr{(?imxs:[0-9LMNPQRSTUV])}; |
85
|
|
|
|
|
|
|
|
86
|
34
|
100
|
|
|
|
89
|
return $cb->('invalid length') if length($cf) != 16; |
87
|
|
|
|
|
|
|
|
88
|
32
|
|
|
|
|
65
|
$cf = uc($cf); |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
return |
91
|
32
|
100
|
100
|
|
|
425
|
if substr($cf, 0, 3) !~ m{\A$namish\z}mxs |
92
|
|
|
|
|
|
|
&& !$cb->('invalid surname'); |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
return |
95
|
30
|
100
|
100
|
|
|
275
|
if substr($cf, 3, 3) !~ m{\A$namish\z}mxs |
96
|
|
|
|
|
|
|
&& !$cb->('invalid name'); |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
return |
99
|
29
|
100
|
100
|
|
|
88
|
if !_is_valid_cf_date(substr($cf, 6, 5)) |
100
|
|
|
|
|
|
|
&& !$cb->('invalid birth date'); |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
return |
103
|
27
|
100
|
100
|
|
|
213
|
if substr($cf, 11, 4) !~ m{\A [A-Z] $digitish{3} \z}mxs |
104
|
|
|
|
|
|
|
&& !$cb->('invalid birth place'); |
105
|
|
|
|
|
|
|
|
106
|
26
|
|
|
|
|
55
|
my $checksum = _cf_checksum($cf); |
107
|
|
|
|
|
|
|
return |
108
|
26
|
100
|
100
|
|
|
111
|
if $checksum ne substr($cf, -1, 1) |
109
|
|
|
|
|
|
|
&& !$cb->("invalid checksum (should be: $checksum)"); |
110
|
|
|
|
|
|
|
|
111
|
24
|
100
|
|
|
|
66
|
return unless $data; |
112
|
|
|
|
|
|
|
|
113
|
14
|
100
|
|
|
|
42
|
if (defined(my $surname = $data->{surname})) { |
114
|
|
|
|
|
|
|
return |
115
|
4
|
100
|
100
|
|
|
10
|
if substr($cf, 0, 3) ne _compact_surname($surname) |
116
|
|
|
|
|
|
|
&& !$cb->('surname mismatch'); |
117
|
|
|
|
|
|
|
} |
118
|
13
|
100
|
|
|
|
35
|
if (defined(my $name = $data->{name})) { |
119
|
|
|
|
|
|
|
return |
120
|
4
|
100
|
100
|
|
|
11
|
if substr($cf, 3, 3) ne _compact_name($name) |
121
|
|
|
|
|
|
|
&& !$cb->('name mismatch'); |
122
|
|
|
|
|
|
|
} |
123
|
12
|
100
|
|
|
|
26
|
if (defined(my $birthdate = $data->{birthdate})) { |
124
|
4
|
|
|
|
|
9
|
my ($male, $female) = _compact_birthdates($birthdate); |
125
|
4
|
|
|
|
|
20
|
my $got = _normalized_birthdate(substr($cf, 6, 5)); |
126
|
|
|
|
|
|
|
return |
127
|
4
|
100
|
66
|
|
|
44
|
if ($got ne $male) |
|
|
|
100
|
|
|
|
|
128
|
|
|
|
|
|
|
&& ($got ne $female) |
129
|
|
|
|
|
|
|
&& !$cb->('birth date mismatch'); |
130
|
|
|
|
|
|
|
} ## end if (defined(my $birthdate...)) |
131
|
11
|
100
|
|
|
|
34
|
if (defined(my $sex = $data->{sex})) { |
132
|
6
|
|
|
|
|
16
|
my $got = _normalized_birthdate(substr($cf, 6, 5)); |
133
|
6
|
|
|
|
|
18
|
my $day = substr($got, -2, 2) + 0; |
134
|
|
|
|
|
|
|
return |
135
|
6
|
100
|
66
|
|
|
45
|
if ((lc($sex) eq 'm' && $day > 31) |
|
|
|
100
|
|
|
|
|
136
|
|
|
|
|
|
|
|| (lc($sex) eq 'f' && $day < 41)) |
137
|
|
|
|
|
|
|
&& !$cb->('sex mismatch'); |
138
|
|
|
|
|
|
|
} ## end if (defined(my $sex = ...)) |
139
|
10
|
100
|
|
|
|
26
|
if (defined(my $place = $data->{birthplace})) { |
140
|
4
|
|
|
|
|
9
|
my $got = _normalized_birthplace(substr($cf, 11, 4)); |
141
|
4
|
|
|
|
|
10
|
$place = _normalized_birthplace($place); |
142
|
|
|
|
|
|
|
return |
143
|
4
|
100
|
100
|
|
|
41
|
if fc($got) ne fc($place) |
144
|
|
|
|
|
|
|
&& !$cb->('birth place mismatch'); |
145
|
|
|
|
|
|
|
} ## end if (defined(my $place ...)) |
146
|
|
|
|
|
|
|
|
147
|
9
|
|
|
|
|
25
|
return; |
148
|
|
|
|
|
|
|
} ## end sub _validate_cf |
149
|
|
|
|
|
|
|
|
150
|
26
|
|
|
26
|
|
43
|
sub _cf_checksum ($cf) { |
|
26
|
|
|
|
|
35
|
|
|
26
|
|
|
|
|
30
|
|
151
|
26
|
|
|
|
|
344
|
state $odd_checksums = { |
152
|
|
|
|
|
|
|
0 => 1, |
153
|
|
|
|
|
|
|
1 => 0, |
154
|
|
|
|
|
|
|
2 => 5, |
155
|
|
|
|
|
|
|
3 => 7, |
156
|
|
|
|
|
|
|
4 => 9, |
157
|
|
|
|
|
|
|
5 => 13, |
158
|
|
|
|
|
|
|
6 => 15, |
159
|
|
|
|
|
|
|
7 => 17, |
160
|
|
|
|
|
|
|
8 => 19, |
161
|
|
|
|
|
|
|
9 => 21, |
162
|
|
|
|
|
|
|
A => 1, |
163
|
|
|
|
|
|
|
B => 0, |
164
|
|
|
|
|
|
|
C => 5, |
165
|
|
|
|
|
|
|
D => 7, |
166
|
|
|
|
|
|
|
E => 9, |
167
|
|
|
|
|
|
|
F => 13, |
168
|
|
|
|
|
|
|
G => 15, |
169
|
|
|
|
|
|
|
H => 17, |
170
|
|
|
|
|
|
|
I => 19, |
171
|
|
|
|
|
|
|
J => 21, |
172
|
|
|
|
|
|
|
K => 2, |
173
|
|
|
|
|
|
|
L => 4, |
174
|
|
|
|
|
|
|
M => 18, |
175
|
|
|
|
|
|
|
N => 20, |
176
|
|
|
|
|
|
|
O => 11, |
177
|
|
|
|
|
|
|
P => 3, |
178
|
|
|
|
|
|
|
Q => 6, |
179
|
|
|
|
|
|
|
R => 8, |
180
|
|
|
|
|
|
|
S => 12, |
181
|
|
|
|
|
|
|
T => 14, |
182
|
|
|
|
|
|
|
U => 16, |
183
|
|
|
|
|
|
|
V => 10, |
184
|
|
|
|
|
|
|
W => 22, |
185
|
|
|
|
|
|
|
X => 25, |
186
|
|
|
|
|
|
|
Y => 24, |
187
|
|
|
|
|
|
|
Z => 23, |
188
|
|
|
|
|
|
|
}, |
189
|
|
|
|
|
|
|
my $even_checksums = { |
190
|
|
|
|
|
|
|
0 => 0, |
191
|
|
|
|
|
|
|
1 => 1, |
192
|
|
|
|
|
|
|
2 => 2, |
193
|
|
|
|
|
|
|
3 => 3, |
194
|
|
|
|
|
|
|
4 => 4, |
195
|
|
|
|
|
|
|
5 => 5, |
196
|
|
|
|
|
|
|
6 => 6, |
197
|
|
|
|
|
|
|
7 => 7, |
198
|
|
|
|
|
|
|
8 => 8, |
199
|
|
|
|
|
|
|
9 => 9, |
200
|
|
|
|
|
|
|
A => 0, |
201
|
|
|
|
|
|
|
B => 1, |
202
|
|
|
|
|
|
|
C => 2, |
203
|
|
|
|
|
|
|
D => 3, |
204
|
|
|
|
|
|
|
E => 4, |
205
|
|
|
|
|
|
|
F => 5, |
206
|
|
|
|
|
|
|
G => 6, |
207
|
|
|
|
|
|
|
H => 7, |
208
|
|
|
|
|
|
|
I => 8, |
209
|
|
|
|
|
|
|
J => 9, |
210
|
|
|
|
|
|
|
K => 10, |
211
|
|
|
|
|
|
|
L => 11, |
212
|
|
|
|
|
|
|
M => 12, |
213
|
|
|
|
|
|
|
N => 13, |
214
|
|
|
|
|
|
|
O => 14, |
215
|
|
|
|
|
|
|
P => 15, |
216
|
|
|
|
|
|
|
Q => 16, |
217
|
|
|
|
|
|
|
R => 17, |
218
|
|
|
|
|
|
|
S => 18, |
219
|
|
|
|
|
|
|
T => 19, |
220
|
|
|
|
|
|
|
U => 20, |
221
|
|
|
|
|
|
|
V => 21, |
222
|
|
|
|
|
|
|
W => 22, |
223
|
|
|
|
|
|
|
X => 23, |
224
|
|
|
|
|
|
|
Y => 24, |
225
|
|
|
|
|
|
|
Z => 25, |
226
|
|
|
|
|
|
|
}; |
227
|
26
|
|
|
|
|
51
|
state $checksums_for = [$odd_checksums, $even_checksums]; |
228
|
26
|
|
|
|
|
130
|
my @chars = split m{}mxs, substr($cf, 0, 15); # no checksum |
229
|
26
|
|
|
|
|
108
|
my $sum = sum map { $checksums_for->[$_ % 2]{$chars[$_]} } 0 .. $#chars; |
|
390
|
|
|
|
|
770
|
|
230
|
26
|
|
|
|
|
186
|
chr(ord('A') + ($sum % 26)); |
231
|
|
|
|
|
|
|
} ## end sub _cf_checksum |
232
|
|
|
|
|
|
|
|
233
|
47
|
|
|
47
|
|
70
|
sub _normalized_string ($string, @positions) { |
|
47
|
|
|
|
|
63
|
|
|
47
|
|
|
|
|
84
|
|
|
47
|
|
|
|
|
60
|
|
234
|
47
|
|
|
|
|
67
|
state $letters = [qw< L M N P Q R S T U V >]; |
235
|
47
|
|
|
|
|
62
|
state $digit_for = {map { $letters->[$_] => $_ } 0 .. $letters->$#*}; |
|
10
|
|
|
|
|
20
|
|
236
|
47
|
|
|
|
|
90
|
for my $i (@positions) { |
237
|
180
|
|
|
|
|
271
|
my $current = substr($string, $i, 1); |
238
|
|
|
|
|
|
|
substr($string, $i, 1, $digit_for->{$current}) |
239
|
180
|
50
|
|
|
|
352
|
if exists $digit_for->{$current}; |
240
|
|
|
|
|
|
|
} |
241
|
47
|
|
|
|
|
134
|
return $string; |
242
|
|
|
|
|
|
|
} ## end sub _normalized_string |
243
|
|
|
|
|
|
|
|
244
|
8
|
|
|
8
|
|
13
|
sub _normalized_birthplace ($place) { _normalized_string($place, 1 .. 3) } |
|
8
|
|
|
|
|
20
|
|
|
8
|
|
|
|
|
12
|
|
|
8
|
|
|
|
|
23
|
|
245
|
39
|
|
|
39
|
|
60
|
sub _normalized_birthdate ($date) { _normalized_string($date, 0, 1, 3, 4) } |
|
39
|
|
|
|
|
68
|
|
|
39
|
|
|
|
|
52
|
|
|
39
|
|
|
|
|
78
|
|
246
|
|
|
|
|
|
|
|
247
|
29
|
|
|
29
|
|
42
|
sub _is_valid_cf_date ($date) { |
|
29
|
|
|
|
|
73
|
|
|
29
|
|
|
|
|
43
|
|
248
|
29
|
|
|
|
|
48
|
state $mlf = [split m{}mxs, 'ABCDEHLMPRST']; |
249
|
29
|
|
|
|
|
47
|
state $month_for = {map { $mlf->[$_] => $_ } 0 .. $mlf->$#*}; |
|
12
|
|
|
|
|
28
|
|
250
|
|
|
|
|
|
|
|
251
|
29
|
|
|
|
|
63
|
$date = _normalized_birthdate($date); |
252
|
29
|
|
|
|
|
120
|
my ($y, $mc, $d) = $date =~ m{\A(\d\d)([ABCDEHLMPRST])(\d\d)\z}mxs; |
253
|
29
|
|
|
|
|
75
|
my $m = $month_for->{$mc}; |
254
|
29
|
|
|
|
|
94
|
$_ += 0 for ($d, $y); |
255
|
29
|
100
|
|
|
|
65
|
$d -= 40 if $d > 40; |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
return !0 |
258
|
|
|
|
|
|
|
if (($m != 1) || ($y % 100) || ($d < 29)) |
259
|
29
|
100
|
33
|
|
|
120
|
&& eval { timegm(30, 30, 12, $d, $m, $y + 1900); 1 }; |
|
29
|
|
66
|
|
|
103
|
|
|
25
|
|
|
|
|
757
|
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# We have $y = 0 but we might have uncertainty as to which exact century |
262
|
|
|
|
|
|
|
# we have to consider, and they have different opinions about being leap |
263
|
|
|
|
|
|
|
# or not. |
264
|
|
|
|
|
|
|
|
265
|
4
|
50
|
|
|
|
977
|
return !1 if $d != 29; # this check is trivial |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# try out "meaningful" centuries. CF was introduced in 19xx so it makes |
268
|
|
|
|
|
|
|
# sense to check from 19 up to the current one. We mean "century" as |
269
|
|
|
|
|
|
|
# "whatever year without the last two digits", so 20th century is 19 |
270
|
0
|
|
|
|
|
0
|
my $current_century = 19 + int((gmtime)[5] / 100); |
271
|
0
|
|
|
|
|
0
|
for my $century (reverse(19 .. $current_century)) { |
272
|
0
|
0
|
|
|
|
0
|
return !0 if eval { timegm(30, 30, 12, $d, $m, $century * 100); 1 }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
0
|
|
|
|
|
0
|
return !1; |
276
|
|
|
|
|
|
|
} ## end sub _is_valid_cf_date |
277
|
|
|
|
|
|
|
|
278
|
4
|
|
|
4
|
|
5
|
sub _compact_birthdates ($birthdate) { |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
6
|
|
279
|
4
|
|
|
|
|
9
|
state $month_letter_for = ['', split m{}mxs, 'ABCDEHLMPRST']; |
280
|
4
|
|
|
|
|
21
|
my ($y, $m, $d) = split m{\D}mxs, $birthdate; |
281
|
4
|
50
|
|
|
|
13
|
($y, $d) = ($d, $y) if $d > 31; |
282
|
4
|
|
|
|
|
7
|
$y %= 100; |
283
|
4
|
|
|
|
|
10
|
$m = $month_letter_for->[$m + 0]; |
284
|
4
|
|
|
|
|
19
|
map { sprintf '%02d%s%02d', $y, $m, $_ } ($d, $d + 40); |
|
8
|
|
|
|
|
39
|
|
285
|
|
|
|
|
|
|
} ## end sub _compact_birthdates |
286
|
|
|
|
|
|
|
|
287
|
4
|
|
|
4
|
|
7
|
sub _compact_surname ($surname) { |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
7
|
|
288
|
4
|
|
|
|
|
9
|
my ($cs, $vs) = _consonants_and_vowels($surname); |
289
|
4
|
|
|
|
|
15
|
my @retval = ($cs->@*, $vs->@*, ('X') x 3); |
290
|
4
|
|
|
|
|
32
|
return join '', @retval[0 .. 2]; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
4
|
|
|
4
|
|
7
|
sub _compact_name ($name) { |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
5
|
|
294
|
4
|
|
|
|
|
8
|
my ($cs, $vs) = _consonants_and_vowels($name); |
295
|
4
|
100
|
|
|
|
16
|
splice $cs->@*, 1, 1 if $cs->@* > 3; |
296
|
4
|
|
|
|
|
24
|
my @retval = ($cs->@*, $vs->@*, ('X') x 3); |
297
|
4
|
|
|
|
|
35
|
return join '', @retval[0 .. 2]; |
298
|
|
|
|
|
|
|
} ## end sub _compact_name |
299
|
|
|
|
|
|
|
|
300
|
8
|
|
|
8
|
|
10
|
sub _consonants_and_vowels ($string) { |
|
8
|
|
|
|
|
13
|
|
|
8
|
|
|
|
|
11
|
|
301
|
8
|
|
|
|
|
13
|
my (@consonants, @vowels); |
302
|
8
|
|
|
|
|
34
|
for my $char (grep { m{[A-Z]}mxs } split m{}mxs, uc($string)) { |
|
42
|
|
|
|
|
105
|
|
303
|
42
|
100
|
|
|
|
105
|
if ($char =~ m{[AEIOU]}mxs) { push @vowels, $char } |
|
20
|
|
|
|
|
40
|
|
304
|
22
|
|
|
|
|
42
|
else { push @consonants, $char } |
305
|
|
|
|
|
|
|
} |
306
|
8
|
|
|
|
|
24
|
return (\@consonants, \@vowels); |
307
|
|
|
|
|
|
|
} ## end sub _consonants_and_vowels |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
1; |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
__END__ |