File Coverage

blib/lib/Lingua/SLV/Num2Word.pm
Criterion Covered Total %
statement 57 104 54.8
branch 29 66 43.9
condition 10 30 33.3
subroutine 8 10 80.0
pod 3 3 100.0
total 107 213 50.2


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; mode:folding; coding:utf-8; -*-
2              
3             package Lingua::SLV::Num2Word;
4             # ABSTRACT: Number to word conversion in Slovenian
5              
6 1     1   90539 use 5.16.0;
  1         3  
7 1     1   4 use utf8;
  1         1  
  1         9  
8 1     1   20 use warnings;
  1         2  
  1         49  
9              
10             # {{{ use block
11              
12 1     1   4 use Carp;
  1         2  
  1         64  
13 1     1   476 use Export::Attrs;
  1         7935  
  1         6  
14              
15             # }}}
16             # {{{ var block
17             our $VERSION = '0.2603300';
18             my %token1 = qw( 0 nič 1 ena 2 dva
19             3 tri 4 štiri 5 pet
20             6 šest 7 sedem 8 osem
21             9 devet 10 deset 11 enajst
22             12 dvanajst 13 trinajst 14 štirinajst
23             15 petnajst 16 šestnajst 17 sedemnajst
24             18 osemnajst 19 devetnajst
25             );
26             my %token2 = qw( 20 dvajset 30 trideset 40 štirideset
27             50 petdeset 60 šestdeset 70 sedemdeset
28             80 osemdeset 90 devetdeset
29             );
30             my %token3 = ( 100, 'sto', 200, 'dvesto', 300, 'tristo',
31             400, 'štiristo', 500, 'petsto', 600, 'šeststo',
32             700, 'sedemsto', 800, 'osemsto', 900, 'devetsto'
33             );
34              
35             # }}}
36              
37             # {{{ num2slv_cardinal number to string conversion
38              
39             sub num2slv_cardinal :Export {
40 24     24 1 130333 my $result = '';
41 24         31 my $number = shift;
42              
43 24 50 33     207 croak 'You should specify a number from interval [0, 999_999_999]'
      33        
      33        
44             if !defined $number
45             || $number !~ m{\A\d+\z}xms
46             || $number < 0
47             || $number > 999_999_999;
48              
49 24         29 my $reminder = 0;
50              
51 24 100       62 if ($number < 20) {
    100          
    100          
    100          
    50          
52 11         22 $result = $token1{$number};
53             }
54             elsif ($number < 100) {
55 4         7 $reminder = $number % 10;
56 4 100       6 if ($reminder == 0) {
57 1         3 $result = $token2{$number};
58             }
59             else {
60             # Slovenian: units + "in" + tens (like German)
61 3         18 $result = $token1{$reminder}.'in'.$token2{$number - $reminder};
62             }
63             }
64             elsif ($number < 1_000) {
65 3         4 $reminder = $number % 100;
66 3 100       9 if ($reminder != 0) {
67 1         6 $result = $token3{$number - $reminder}.' '.num2slv_cardinal($reminder);
68             }
69             else {
70 2         4 $result = $token3{$number};
71             }
72             }
73             elsif ($number < 1_000_000) {
74 2         4 $reminder = $number % 1_000;
75 2 50       4 my $tmp1 = ($reminder != 0) ? ' '.num2slv_cardinal($reminder) : '';
76 2         4 my $tmp2 = substr($number, 0, length($number)-3);
77              
78 2 100       5 if ($tmp2 == 1) {
79 1         1 $tmp2 = 'tisoč';
80             }
81             else {
82 1         3 $tmp2 = num2slv_cardinal($tmp2).' tisoč';
83             }
84 2         4 $result = $tmp2.$tmp1;
85             }
86             elsif ($number < 1_000_000_000) {
87 4         6 $reminder = $number % 1_000_000;
88 4 50       9 my $tmp1 = ($reminder != 0) ? ' '.num2slv_cardinal($reminder) : '';
89 4         9 my $tmp2 = substr($number, 0, length($number)-6);
90 4         7 my $tmp3 = $tmp2 % 100;
91 4         5 my $tmp4 = $tmp2 % 10;
92 4         5 my $mega;
93              
94             # Slovenian million declension:
95             # 1 -> milijon
96             # 2 -> dva milijona (dual)
97             # 3-4 -> milijone (plural nominative)
98             # 5+, 11-19 -> milijonov (genitive plural)
99 4 50 33     32 if ($tmp3 >= 11 && $tmp3 <= 19) {
    100 66        
    50 66        
    100 66        
    50          
    100          
100             # teens always use genitive plural
101 0         0 $mega = 'milijonov';
102             }
103             elsif ($tmp4 == 1 && $tmp2 == 1) {
104 1         3 $mega = 'milijon';
105             }
106             elsif ($tmp4 == 1) {
107             # 21, 31, ... -> en milijon
108 0         0 $mega = 'milijon';
109             }
110             elsif ($tmp4 == 2 && $tmp2 == 2) {
111 1         2 $mega = 'milijona'; # dual
112             }
113             elsif ($tmp4 == 2) {
114 0         0 $mega = 'milijona'; # dual ending
115             }
116             elsif ($tmp4 == 3 || $tmp4 == 4) {
117 1         2 $mega = 'milijone';
118             }
119             else {
120 1         2 $mega = 'milijonov';
121             }
122              
123 4 100       7 if ($tmp2 == 1) {
124 1         2 $tmp2 = 'en '.$mega;
125             }
126             else {
127 3         5 $tmp2 = num2slv_cardinal($tmp2).' '.$mega;
128             }
129              
130 4         10 $result = $tmp2.$tmp1;
131             }
132              
133 24         64 return $result;
134 1     1   488 }
  1         2  
  1         5  
135              
136             # }}}
137              
138             # {{{ num2slv_ordinal number to ordinal string conversion
139              
140             sub num2slv_ordinal :Export {
141 0     0 1   my $number = shift;
142              
143 0 0 0       croak 'You should specify a number from interval [0, 999_999_999]'
      0        
      0        
144             if !defined $number
145             || $number !~ m{\A\d+\z}xms
146             || $number < 0
147             || $number > 999_999_999;
148              
149             # Irregular ordinals 0-10
150 0           my %irregular = (
151             0 => 'ničti',
152             1 => 'prvi',
153             2 => 'drugi',
154             3 => 'tretji',
155             4 => 'četrti',
156             5 => 'peti',
157             6 => 'šesti',
158             7 => 'sedmi',
159             8 => 'osmi',
160             9 => 'deveti',
161             10 => 'deseti',
162             );
163              
164 0 0         return $irregular{$number} if exists $irregular{$number};
165              
166             # Irregular teens 11-19
167 0           my %teens = (
168             11 => 'enajsti',
169             12 => 'dvanajsti',
170             13 => 'trinajsti',
171             14 => 'štirinajsti',
172             15 => 'petnajsti',
173             16 => 'šestnajsti',
174             17 => 'sedemnajsti',
175             18 => 'osemnajsti',
176             19 => 'devetnajsti',
177             );
178              
179 0 0         return $teens{$number} if exists $teens{$number};
180              
181             # Tens ordinals
182 0           my %tens_ord = (
183             20 => 'dvajseti',
184             30 => 'trideseti',
185             40 => 'štirideseti',
186             50 => 'petdeseti',
187             60 => 'šestdeseti',
188             70 => 'sedemdeseti',
189             80 => 'osemdeseti',
190             90 => 'devetdeseti',
191             );
192              
193             # Hundreds ordinals
194 0           my %hundreds_ord = (
195             100 => 'stoti',
196             200 => 'dvestoti',
197             300 => 'tristoti',
198             400 => 'štiristoti',
199             500 => 'petstoti',
200             600 => 'šeststoti',
201             700 => 'sedemstoti',
202             800 => 'osemstoti',
203             900 => 'devetstoti',
204             );
205              
206             # For numbers >= 1_000_000
207 0 0         if ($number >= 1_000_000) {
208 0           my $millions = int($number / 1_000_000);
209 0           my $remainder = $number % 1_000_000;
210 0 0         if ($remainder == 0) {
211 0 0         if ($millions == 1) {
212 0           return 'milijonti';
213             }
214 0           return num2slv_cardinal($millions) . ' milijonti';
215             }
216 0           my $prefix = num2slv_cardinal($millions);
217 0 0         my $mil_word = ($millions == 1) ? 'en milijon' : 'milijonov';
218 0           return $prefix . ' ' . $mil_word . ' ' . num2slv_ordinal($remainder);
219             }
220              
221 0 0         if ($number >= 1_000) {
222 0           my $thousands = int($number / 1_000);
223 0           my $remainder = $number % 1_000;
224 0 0         if ($remainder == 0) {
225 0 0         if ($thousands == 1) {
226 0           return 'tisočti';
227             }
228 0           return num2slv_cardinal($thousands) . ' tisočti';
229             }
230 0           my $thou_cardinal;
231 0 0         if ($thousands == 1) {
232 0           $thou_cardinal = 'tisoč';
233             }
234             else {
235 0           $thou_cardinal = num2slv_cardinal($thousands) . ' tisoč';
236             }
237 0           return $thou_cardinal . ' ' . num2slv_ordinal($remainder);
238             }
239              
240 0 0         if ($number >= 100) {
241 0           my $h = int($number / 100) * 100;
242 0           my $remainder = $number % 100;
243 0 0         if ($remainder == 0) {
244 0           return $hundreds_ord{$h};
245             }
246 0           return $token3{$h} . ' ' . num2slv_ordinal($remainder);
247             }
248              
249             # 20-99 compound: Slovenian uses unit+in+tens for cardinals,
250             # but ordinals follow the same compound pattern
251 0 0         if ($number >= 20) {
252 0           my $t = int($number / 10) * 10;
253 0           my $remainder = $number % 10;
254 0 0         if ($remainder == 0) {
255 0           return $tens_ord{$t};
256             }
257 0           return $tens_ord{$t} . ' ' . $irregular{$remainder};
258             }
259              
260             # Should not reach here
261 0           return;
262 1     1   622 }
  1         1  
  1         4  
263              
264             # }}}
265              
266             # {{{ capabilities declare supported features
267              
268             sub capabilities {
269             return {
270 0     0 1   cardinal => 1,
271             ordinal => 1,
272             };
273             }
274              
275             # }}}
276             1;
277              
278             __END__