File Coverage

blib/lib/Lingua/LIT/Num2Word.pm
Criterion Covered Total %
statement 25 125 20.0
branch 2 70 2.8
condition 3 51 5.8
subroutine 8 12 66.6
pod 3 3 100.0
total 41 261 15.7


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; mode:folding; coding:utf-8; -*-
2              
3             package Lingua::LIT::Num2Word;
4             # ABSTRACT: Number to word conversion in Lithuanian
5              
6 1     1   109676 use 5.16.0;
  1         3  
7 1     1   6 use utf8;
  1         1  
  1         13  
8 1     1   20 use warnings;
  1         2  
  1         57  
9              
10             # {{{ use block
11              
12 1     1   6 use Carp;
  1         1  
  1         62  
13 1     1   473 use Export::Attrs;
  1         7998  
  1         6  
14              
15             # }}}
16             # {{{ var block
17             our $VERSION = '0.2603300';
18             my %token1 = qw( 0 nulis 1 vienas 2 du
19             3 trys 4 keturi 5 penki
20             6 šeši 7 septyni 8 aštuoni
21             9 devyni 10 dešimt 11 vienuolika
22             12 dvylika 13 trylika 14 keturiolika
23             15 penkiolika 16 šešiolika 17 septyniolika
24             18 aštuoniolika 19 devyniolika
25             );
26             my %token2 = qw( 20 dvidešimt 30 trisdešimt
27             40 keturiasdešimt 50 penkiasdešimt
28             60 šešiasdešimt 70 septyniasdešimt
29             80 aštuoniasdešimt 90 devyniasdešimt
30             );
31              
32             # }}}
33              
34             # {{{ _decline choose singular/plural/genitive form
35              
36             sub _decline {
37 0     0   0 my ($count, $singular, $plural, $genitive) = @_;
38              
39 0         0 my $last_two = $count % 100;
40 0         0 my $last_one = $count % 10;
41              
42             # teens (11-19) always take genitive plural
43 0 0 0     0 if ($last_two >= 10 && $last_two <= 19) {
44 0         0 return $genitive;
45             }
46              
47             # last digit 1 => singular
48 0 0       0 if ($last_one == 1) {
49 0         0 return $singular;
50             }
51              
52             # last digit 0 => genitive plural
53 0 0       0 if ($last_one == 0) {
54 0         0 return $genitive;
55             }
56              
57             # last digit 2-9 => plural nominative
58 0         0 return $plural;
59             }
60              
61             # }}}
62             # {{{ _hundreds convert hundreds part
63              
64             sub _hundreds {
65 0     0   0 my ($number) = @_;
66              
67 0         0 my $h = int($number / 100);
68              
69 0 0       0 if ($h == 1) {
70 0         0 return 'vienas šimtas';
71             }
72              
73 0         0 return $token1{$h} . ' ' . _decline($h, 'šimtas', 'šimtai', 'šimtų');
74             }
75              
76             # }}}
77             # {{{ num2lit_cardinal number to string conversion
78              
79             sub num2lit_cardinal :Export {
80 2     2 1 181087 my $result = '';
81 2         5 my $number = shift;
82              
83 2 50 33     30 croak 'You should specify a number from interval [0, 999_999_999]'
      33        
      33        
84             if !defined $number
85             || $number !~ m{\A\d+\z}xms
86             || $number < 0
87             || $number > 999_999_999;
88              
89             # 0-19
90 2 50       7 if ($number < 20) {
91 2         10 return $token1{$number};
92             }
93              
94             # 20-99
95 0 0         if ($number < 100) {
96 0           my $remainder = $number % 10;
97 0           $result = $token2{$number - $remainder};
98 0 0         if ($remainder != 0) {
99 0           $result .= ' ' . $token1{$remainder};
100             }
101 0           return $result;
102             }
103              
104             # 100-999
105 0 0         if ($number < 1_000) {
106 0           my $remainder = $number % 100;
107 0           $result = _hundreds($number);
108 0 0         if ($remainder != 0) {
109 0           $result .= ' ' . num2lit_cardinal($remainder);
110             }
111 0           return $result;
112             }
113              
114             # 1_000-999_999
115 0 0         if ($number < 1_000_000) {
116 0           my $remainder = $number % 1_000;
117 0           my $thousands = int($number / 1_000);
118 0           my $suffix = _decline($thousands, 'tūkstantis', 'tūkstančiai', 'tūkstančių');
119              
120 0 0         if ($thousands == 1) {
    0          
121 0           $result = 'vienas tūkstantis';
122             }
123             elsif ($thousands < 20) {
124 0           $result = $token1{$thousands} . ' ' . $suffix;
125             }
126             else {
127 0           $result = num2lit_cardinal($thousands) . ' ' . $suffix;
128             }
129              
130 0 0         if ($remainder != 0) {
131 0           $result .= ' ' . num2lit_cardinal($remainder);
132             }
133 0           return $result;
134             }
135              
136             # 1_000_000-999_999_999
137 0 0         if ($number < 1_000_000_000) {
138 0           my $remainder = $number % 1_000_000;
139 0           my $millions = int($number / 1_000_000);
140 0           my $suffix = _decline($millions, 'milijonas', 'milijonai', 'milijonų');
141              
142 0 0         if ($millions == 1) {
    0          
143 0           $result = 'vienas milijonas';
144             }
145             elsif ($millions < 20) {
146 0           $result = $token1{$millions} . ' ' . $suffix;
147             }
148             else {
149 0           $result = num2lit_cardinal($millions) . ' ' . $suffix;
150             }
151              
152 0 0         if ($remainder != 0) {
153 0           $result .= ' ' . num2lit_cardinal($remainder);
154             }
155 0           return $result;
156             }
157              
158 0           return $result;
159 1     1   604 }
  1         1  
  1         5  
160              
161             # }}}
162              
163              
164             # {{{ num2lit_ordinal convert number to ordinal text
165              
166             sub num2lit_ordinal :Export {
167 0     0 1   my $number = shift;
168              
169 0 0 0       croak 'You should specify a number from interval [1, 999_999_999]'
      0        
      0        
170             if !defined $number
171             || $number !~ m{\A\d+\z}xms
172             || $number < 1
173             || $number > 999_999_999;
174              
175             # Unique ordinal forms for 1-19 (masculine nominative singular)
176 0           my %ordinals = (
177             1 => 'pirmas',
178             2 => 'antras',
179             3 => 'trečias',
180             4 => 'ketvirtas',
181             5 => 'penktas',
182             6 => 'šeštas',
183             7 => 'septintas',
184             8 => 'aštuntas',
185             9 => 'devintas',
186             10 => 'dešimtas',
187             11 => 'vienuoliktas',
188             12 => 'dvyliktas',
189             13 => 'tryliktas',
190             14 => 'keturioliktas',
191             15 => 'penkioliktas',
192             16 => 'šešioliktas',
193             17 => 'septynioliktas',
194             18 => 'aštuonioliktas',
195             19 => 'devynioliktas',
196             );
197              
198 0 0         return $ordinals{$number} if exists $ordinals{$number};
199              
200             # Round tens: ordinal tens forms
201 0           my %ordinal_tens = (
202             20 => 'dvidešimtas',
203             30 => 'trisdešimtas',
204             40 => 'keturiasdešimtas',
205             50 => 'penkiasdešimtas',
206             60 => 'šešiasdešimtas',
207             70 => 'septyniasdešimtas',
208             80 => 'aštuoniasdešimtas',
209             90 => 'devyniasdešimtas',
210             );
211              
212 0 0         return $ordinal_tens{$number} if exists $ordinal_tens{$number};
213              
214             # Compound 21-99: cardinal tens + ordinal unit
215 0 0 0       if ($number > 20 && $number < 100) {
216 0           my $remain = $number % 10;
217 0           my $tens = $number - $remain;
218 0           return $token2{$tens} . ' ' . num2lit_ordinal($remain);
219             }
220              
221             # Round hundreds
222 0 0 0       if ($number >= 100 && $number < 1000 && $number % 100 == 0) {
      0        
223 0           my $h = int($number / 100);
224 0 0         if ($h == 1) {
225 0           return 'šimtasis';
226             }
227 0           return $token1{$h} . ' šimtasis';
228             }
229              
230             # Compound hundreds
231 0 0 0       if ($number >= 100 && $number < 1000) {
232 0           my $remain = $number % 100;
233 0           return _hundreds($number) . ' ' . num2lit_ordinal($remain);
234             }
235              
236             # Round thousands
237 0 0 0       if ($number >= 1000 && $number < 1_000_000 && $number % 1000 == 0) {
      0        
238 0           my $thousands = int($number / 1000);
239 0 0         if ($thousands == 1) {
240 0           return 'tūkstantasis';
241             }
242 0           return num2lit_cardinal($thousands) . ' tūkstantasis';
243             }
244              
245             # Compound thousands
246 0 0 0       if ($number >= 1000 && $number < 1_000_000) {
247 0           my $thousands = int($number / 1000);
248 0           my $remain = $number % 1000;
249 0           my $suffix = _decline($thousands, 'tūkstantis', 'tūkstančiai', 'tūkstančių');
250              
251 0           my $prefix;
252 0 0         if ($thousands == 1) {
    0          
253 0           $prefix = 'vienas tūkstantis';
254             }
255             elsif ($thousands < 20) {
256 0           $prefix = $token1{$thousands} . ' ' . $suffix;
257             }
258             else {
259 0           $prefix = num2lit_cardinal($thousands) . ' ' . $suffix;
260             }
261 0           return $prefix . ' ' . num2lit_ordinal($remain);
262             }
263              
264             # Round millions
265 0 0 0       if ($number >= 1_000_000 && $number < 1_000_000_000 && $number % 1_000_000 == 0) {
      0        
266 0           my $millions = int($number / 1_000_000);
267 0 0         if ($millions == 1) {
268 0           return 'milijonasis';
269             }
270 0           return num2lit_cardinal($millions) . ' milijonasis';
271             }
272              
273             # Compound millions
274 0 0 0       if ($number >= 1_000_000 && $number < 1_000_000_000) {
275 0           my $millions = int($number / 1_000_000);
276 0           my $remain = $number % 1_000_000;
277 0           my $suffix = _decline($millions, 'milijonas', 'milijonai', 'milijonų');
278              
279 0           my $prefix;
280 0 0         if ($millions == 1) {
    0          
281 0           $prefix = 'vienas milijonas';
282             }
283             elsif ($millions < 20) {
284 0           $prefix = $token1{$millions} . ' ' . $suffix;
285             }
286             else {
287 0           $prefix = num2lit_cardinal($millions) . ' ' . $suffix;
288             }
289 0           return $prefix . ' ' . num2lit_ordinal($remain);
290             }
291              
292 0           return;
293 1     1   648 }
  1         7  
  1         4  
294              
295             # }}}
296              
297             # {{{ capabilities declare supported features
298              
299             sub capabilities {
300             return {
301 0     0 1   cardinal => 1,
302             ordinal => 1,
303             };
304             }
305              
306             # }}}
307             1;
308              
309             __END__