File Coverage

blib/lib/Lingua/SRP/Num2Word.pm
Criterion Covered Total %
statement 61 112 54.4
branch 34 76 44.7
condition 20 42 47.6
subroutine 8 10 80.0
pod 3 3 100.0
total 126 243 51.8


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; mode:folding; coding:utf-8; -*-
2              
3             package Lingua::SRP::Num2Word;
4             # ABSTRACT: Number to word conversion in Serbian
5              
6 1     1   121542 use 5.16.0;
  1         4  
7 1     1   6 use utf8;
  1         2  
  1         13  
8 1     1   33 use warnings;
  1         2  
  1         78  
9              
10             # {{{ use block
11              
12 1     1   7 use Carp;
  1         3  
  1         101  
13 1     1   682 use Export::Attrs;
  1         14128  
  1         7  
14              
15             # }}}
16             # {{{ var block
17             our $VERSION = '0.2603300';
18             my %token1 = qw( 0 нула 1 један 2 два
19             3 три 4 четири 5 пет
20             6 шест 7 седам 8 осам
21             9 девет 10 десет 11 једанаест
22             12 дванаест 13 тринаест 14 четрнаест
23             15 петнаест 16 шеснаест 17 седамнаест
24             18 осамнаест 19 деветнаест
25             );
26             my %token2 = qw( 20 двадесет 30 тридесет 40 четрдесет
27             50 педесет 60 шездесет 70 седамдесет
28             80 осамдесет 90 деведесет
29             );
30             my %token3 = ( 100, 'сто', 200, 'двеста', 300, 'триста',
31             400, 'четиристо', 500, 'петсто', 600, 'шестсто',
32             700, 'седамсто', 800, 'осамсто', 900, 'деветсто'
33             );
34              
35             # }}}
36              
37             # {{{ num2srp_cardinal number to string conversion
38              
39             sub num2srp_cardinal :Export {
40 44     44 1 279184 my $result = '';
41 44         96 my $number = shift;
42              
43 44 100 66     943 croak 'You should specify a number from interval [0, 999_999_999]'
      66        
      100        
44             if !defined $number
45             || $number !~ m{\A\d+\z}xms
46             || $number < 0
47             || $number > 999_999_999;
48              
49 42         91 my $reminder = 0;
50              
51 42 100       133 if ($number < 20) {
    100          
    100          
    100          
    50          
52 18         50 $result = $token1{$number};
53             }
54             elsif ($number < 100) {
55 9         19 $reminder = $number % 10;
56 9 100       22 if ($reminder == 0) {
57 3         12 $result = $token2{$number};
58             }
59             else {
60 6         32 $result = $token2{$number - $reminder}.' '.num2srp_cardinal($reminder);
61             }
62             }
63             elsif ($number < 1_000) {
64 7         16 $reminder = $number % 100;
65 7 100       16 if ($reminder != 0) {
66 3         29 $result = $token3{$number - $reminder}.' '.num2srp_cardinal($reminder);
67             }
68             else {
69 4         11 $result = $token3{$number};
70             }
71             }
72             elsif ($number < 1_000_000) {
73 6         14 $reminder = $number % 1_000;
74 6 50       21 my $tmp1 = ($reminder != 0) ? ' '.num2srp_cardinal($reminder) : '';
75 6         20 my $tmp2 = substr($number, 0, length($number)-3);
76 6         15 my $tmp3 = $tmp2 % 100;
77 6         9 my $tmp4 = $tmp2 % 10;
78              
79 6 50 66     24 if ($tmp3 < 9 || $tmp3 > 20) {
80 6 100 100     41 if ($tmp4 == 1 && $tmp2 == 1) {
    100 66        
    100          
81 1         2 $tmp2 = 'хиљада';
82             }
83             elsif ($tmp4 == 1) {
84 1         5 $tmp2 = num2srp_cardinal($tmp2 - $tmp4).' једна хиљада';
85             }
86             elsif($tmp4 > 1 && $tmp4 < 5) {
87             # 2-4: feminine "две" for 2, genitive plural "хиљаде"
88 3 100 66     17 if ($tmp2 == 2) {
    100          
89 1         3 $tmp2 = 'две хиљаде';
90             }
91             elsif ($tmp2 == 3 || $tmp2 == 4) {
92 1         5 $tmp2 = num2srp_cardinal($tmp2).' хиљаде';
93             }
94             else {
95             # e.g. 22, 23, 24, 32, 33, 34...
96 1 50       6 if ($tmp4 == 2) {
97 1         6 $tmp2 = num2srp_cardinal($tmp2 - $tmp4).' две хиљаде';
98             }
99             else {
100 0         0 $tmp2 = num2srp_cardinal($tmp2).' хиљаде';
101             }
102             }
103             }
104             else {
105 1         4 $tmp2 = num2srp_cardinal($tmp2).' хиљада';
106             }
107             }
108             else {
109 0         0 $tmp2 = num2srp_cardinal($tmp2).' хиљада';
110             }
111 6         22 $result = $tmp2.$tmp1;
112             }
113             elsif ($number < 1_000_000_000) {
114 2         5 $reminder = $number % 1_000_000;
115 2 50       7 my $tmp1 = ($reminder != 0) ? ' '.num2srp_cardinal($reminder) : '';
116 2         7 my $tmp2 = substr($number, 0, length($number)-6);
117 2         7 my $tmp3 = $tmp2 % 100;
118 2         3 my $tmp4 = $tmp2 % 10;
119              
120 2 50 33     10 if ($tmp3 < 9 || $tmp3 > 20) {
121 2 100 66     18 if ($tmp4 == 1 && $tmp2 == 1) {
    50 33        
    50          
122 1         3 $tmp2 = 'један милион';
123             }
124             elsif ($tmp4 == 1) {
125 0         0 $tmp2 = num2srp_cardinal($tmp2 - $tmp4).' један милион';
126             }
127             elsif($tmp4 > 1 && $tmp4 < 5) {
128 1         5 $tmp2 = num2srp_cardinal($tmp2).' милиона';
129             }
130             else {
131 0         0 $tmp2 = num2srp_cardinal($tmp2).' милиона';
132             }
133             }
134             else {
135 0         0 $tmp2 = num2srp_cardinal($tmp2).' милиона';
136             }
137              
138 2         7 $result = $tmp2.$tmp1;
139             }
140              
141 42         132 return $result;
142 1     1   1010 }
  1         3  
  1         7  
143              
144             # }}}
145              
146             # {{{ num2srp_ordinal number to ordinal string conversion
147              
148             sub num2srp_ordinal :Export {
149 0     0 1   my $number = shift;
150              
151 0 0 0       croak 'You should specify a number from interval [0, 999_999_999]'
      0        
      0        
152             if !defined $number
153             || $number !~ m{\A\d+\z}xms
154             || $number < 0
155             || $number > 999_999_999;
156              
157             # Irregular ordinals 0-10
158 0           my %irregular = (
159             0 => 'нулти',
160             1 => 'први',
161             2 => 'други',
162             3 => 'трећи',
163             4 => 'четврти',
164             5 => 'пети',
165             6 => 'шести',
166             7 => 'седми',
167             8 => 'осми',
168             9 => 'девети',
169             10 => 'десети',
170             );
171              
172 0 0         return $irregular{$number} if exists $irregular{$number};
173              
174             # Irregular teens 11-19
175 0           my %teens = (
176             11 => 'једанаести',
177             12 => 'дванаести',
178             13 => 'тринаести',
179             14 => 'четрнаести',
180             15 => 'петнаести',
181             16 => 'шеснаести',
182             17 => 'седамнаести',
183             18 => 'осамнаести',
184             19 => 'деветнаести',
185             );
186              
187 0 0         return $teens{$number} if exists $teens{$number};
188              
189             # Tens ordinals
190 0           my %tens_ord = (
191             20 => 'двадесети',
192             30 => 'тридесети',
193             40 => 'четрдесети',
194             50 => 'педесети',
195             60 => 'шездесети',
196             70 => 'седамдесети',
197             80 => 'осамдесети',
198             90 => 'деведесети',
199             );
200              
201             # Hundreds ordinals
202 0           my %hundreds_ord = (
203             100 => 'стоти',
204             200 => 'двестоти',
205             300 => 'тристоти',
206             400 => 'четиристоти',
207             500 => 'петстоти',
208             600 => 'шестстоти',
209             700 => 'седамстоти',
210             800 => 'осамстоти',
211             900 => 'деветстоти',
212             );
213              
214             # For numbers >= 1_000_000
215 0 0         if ($number >= 1_000_000) {
216 0           my $millions = int($number / 1_000_000);
217 0           my $remainder = $number % 1_000_000;
218 0 0         if ($remainder == 0) {
219 0 0         if ($millions == 1) {
220 0           return 'милионити';
221             }
222 0           return num2srp_cardinal($millions) . ' милионити';
223             }
224 0           my $prefix = num2srp_cardinal($millions);
225 0 0         my $mil_word = ($millions == 1) ? 'један милион' : 'милиона';
226 0           return $prefix . ' ' . $mil_word . ' ' . num2srp_ordinal($remainder);
227             }
228              
229 0 0         if ($number >= 1_000) {
230 0           my $thousands = int($number / 1_000);
231 0           my $remainder = $number % 1_000;
232 0 0         if ($remainder == 0) {
233 0 0         if ($thousands == 1) {
234 0           return 'хиљадити';
235             }
236 0           return num2srp_cardinal($thousands) . ' хиљадити';
237             }
238 0           my $thou_cardinal;
239 0 0 0       if ($thousands == 1) {
    0          
    0          
240 0           $thou_cardinal = 'хиљада';
241             }
242             elsif ($thousands == 2) {
243 0           $thou_cardinal = 'две хиљаде';
244             }
245             elsif ($thousands >= 3 && $thousands <= 4) {
246 0           $thou_cardinal = num2srp_cardinal($thousands) . ' хиљаде';
247             }
248             else {
249 0           $thou_cardinal = num2srp_cardinal($thousands) . ' хиљада';
250             }
251 0           return $thou_cardinal . ' ' . num2srp_ordinal($remainder);
252             }
253              
254 0 0         if ($number >= 100) {
255 0           my $h = int($number / 100) * 100;
256 0           my $remainder = $number % 100;
257 0 0         if ($remainder == 0) {
258 0           return $hundreds_ord{$h};
259             }
260 0           return $token3{$h} . ' ' . num2srp_ordinal($remainder);
261             }
262              
263             # 20-99 compound
264 0 0         if ($number >= 20) {
265 0           my $t = int($number / 10) * 10;
266 0           my $remainder = $number % 10;
267 0 0         if ($remainder == 0) {
268 0           return $tens_ord{$t};
269             }
270 0           return $tens_ord{$t} . ' ' . $irregular{$remainder};
271             }
272              
273             # Should not reach here
274 0           return;
275 1     1   1060 }
  1         2  
  1         5  
276              
277             # }}}
278              
279             # {{{ capabilities declare supported features
280              
281             sub capabilities {
282             return {
283 0     0 1   cardinal => 1,
284             ordinal => 1,
285             };
286             }
287              
288             # }}}
289             1;
290              
291             __END__