File Coverage

blib/lib/Lingua/MKD/Num2Word.pm
Criterion Covered Total %
statement 60 104 57.6
branch 32 64 50.0
condition 14 24 58.3
subroutine 9 11 81.8
pod 3 3 100.0
total 118 206 57.2


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; mode:folding; coding:utf-8; -*-
2              
3             package Lingua::MKD::Num2Word;
4             # ABSTRACT: Number to word conversion in Macedonian
5              
6 1     1   109173 use 5.16.0;
  1         3  
7 1     1   4 use utf8;
  1         2  
  1         10  
8 1     1   21 use warnings;
  1         1  
  1         51  
9              
10             # {{{ use block
11              
12 1     1   5 use Carp;
  1         5  
  1         58  
13 1     1   497 use Export::Attrs;
  1         8107  
  1         7  
14              
15             # }}}
16             # {{{ var block
17             our $VERSION = '0.2603300';
18              
19             my %token1 = qw( 0 нула 1 еден 2 два
20             3 три 4 четири 5 пет
21             6 шест 7 седум 8 осум
22             9 девет 10 десет 11 единаесет
23             12 дванаесет 13 тринаесет 14 четиринаесет
24             15 петнаесет 16 шестнаесет 17 седумнаесет
25             18 осумнаесет 19 деветнаесет
26             );
27             my %token2 = qw( 20 дваесет 30 триесет 40 четириесет
28             50 педесет 60 шеесет 70 седумдесет
29             80 осумдесет 90 деведесет
30             );
31             my %token3 = ( 100, 'сто', 200, 'двесте', 300, 'триста',
32             400, 'четиристотини', 500, 'петстотини', 600, 'шестстотини',
33             700, 'седумстотини', 800, 'осумстотини', 900, 'деветстотини'
34             );
35              
36             # }}}
37              
38             # {{{ _num_to_words internal: convert 1-999 to words
39              
40             sub _num_to_words {
41 34     34   55 my ($number) = @_;
42              
43 34 100       100 return $token1{$number} if exists $token1{$number};
44              
45 21 100       30 if ($number < 100) {
46 6         13 my $rem = $number % 10;
47 6 100       22 if ($rem == 0) {
48 4         12 return $token2{$number};
49             }
50 2         14 return $token2{$number - $rem} . ' и ' . $token1{$rem};
51             }
52              
53 15         23 my $hund_rem = $number % 100;
54 15         23 my $hund_val = $number - $hund_rem;
55              
56 15 100       19 if ($hund_rem == 0) {
57 6         16 return $token3{$hund_val};
58             }
59              
60             # hundreds + remainder: "и" goes before the last component
61             # 101 = сто и еден, 120 = сто и дваесет, 123 = сто дваесет и три
62 9         12 my $tens_rem = $hund_rem % 10;
63 9 100 100     39 if ($hund_rem < 20 || $tens_rem == 0) {
64             # single component remainder: hundred "и" remainder
65 2         7 return $token3{$hund_val} . ' и ' . _num_to_words($hund_rem);
66             }
67              
68             # two-component remainder (e.g. 23): hundred tens "и" unit
69 7         28 return $token3{$hund_val} . ' ' . $token2{$hund_rem - $tens_rem} . ' и ' . $token1{$tens_rem};
70             }
71              
72             # }}}
73              
74             # {{{ num2mkd_cardinal number to string conversion
75              
76             sub num2mkd_cardinal :Export {
77 36     36 1 149402 my $number = shift;
78              
79 36 100 100     660 croak 'You should specify a number from interval [0, 999_999_999]'
      66        
      100        
80             if !defined $number
81             || $number !~ m{\A\d+\z}xms
82             || $number < 0
83             || $number > 999_999_999;
84              
85 33 100       64 return $token1{0} if $number == 0;
86              
87 32         42 my $result = '';
88              
89             # {{{ millions
90 32 100       59 if ($number >= 1_000_000) {
91 5         11 my $millions = int($number / 1_000_000);
92 5         7 $number %= 1_000_000;
93              
94 5 100       11 if ($millions == 1) {
95 2         3 $result = 'еден милион';
96             }
97             else {
98 3         7 $result = _num_to_words($millions) . ' милиони';
99             }
100             }
101             # }}}
102              
103             # {{{ thousands
104 32 100       53 if ($number >= 1_000) {
105 10         25 my $thousands = int($number / 1_000);
106 10         13 $number %= 1_000;
107              
108 10 100       22 $result .= ' ' if $result ne '';
109              
110 10 100       23 if ($thousands == 1) {
    100          
111 6         13 $result .= 'илјада';
112             }
113             elsif ($thousands == 2) {
114 1         3 $result .= 'две илјади';
115             }
116             else {
117 3         7 $result .= _num_to_words($thousands) . ' илјади';
118             }
119             }
120             # }}}
121              
122             # {{{ remainder (0-999)
123 32 100       49 if ($number > 0) {
124 26 100       44 if ($result ne '') {
125             # "и" before remainder when there is no compound hundreds group:
126             # i.e. remainder < 100 (no hundreds at all) or round hundreds (100,200,...,900)
127 7 100 100     38 if ($number < 100 || $number % 100 == 0) {
128 4         11 $result .= ' и ' . _num_to_words($number);
129             }
130             else {
131 3         5 $result .= ' ' . _num_to_words($number);
132             }
133             }
134             else {
135 19         31 $result = _num_to_words($number);
136             }
137             }
138             # }}}
139              
140 32         73 return $result;
141 1     1   525 }
  1         3  
  1         4  
142              
143             # }}}
144              
145             # {{{ num2mkd_ordinal number to ordinal string conversion
146              
147             sub num2mkd_ordinal :Export {
148 0     0 1   my $number = shift;
149              
150 0 0 0       croak 'You should specify a number from interval [0, 999_999_999]'
      0        
      0        
151             if !defined $number
152             || $number !~ m{\A\d+\z}xms
153             || $number < 0
154             || $number > 999_999_999;
155              
156             # Irregular ordinals 0-10
157             # Macedonian ordinals (masculine): прв, втор, трет, четврт, пет + -ти suffix
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             # Teens ordinals 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 _num_to_words($millions) . ' милионити';
223             }
224 0 0         my $prefix = ($millions == 1) ? 'еден милион' :
225             _num_to_words($millions) . ' милиони';
226 0           return $prefix . ' ' . num2mkd_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 _num_to_words($thousands) . ' илјадити';
237             }
238 0           my $thou_cardinal;
239 0 0         if ($thousands == 1) {
    0          
240 0           $thou_cardinal = 'илјада';
241             }
242             elsif ($thousands == 2) {
243 0           $thou_cardinal = 'две илјади';
244             }
245             else {
246 0           $thou_cardinal = _num_to_words($thousands) . ' илјади';
247             }
248 0           return $thou_cardinal . ' ' . num2mkd_ordinal($remainder);
249             }
250              
251 0 0         if ($number >= 100) {
252 0           my $h = int($number / 100) * 100;
253 0           my $remainder = $number % 100;
254 0 0         if ($remainder == 0) {
255 0           return $hundreds_ord{$h};
256             }
257 0           return $token3{$h} . ' ' . num2mkd_ordinal($remainder);
258             }
259              
260             # 20-99 compound
261 0 0         if ($number >= 20) {
262 0           my $t = int($number / 10) * 10;
263 0           my $remainder = $number % 10;
264 0 0         if ($remainder == 0) {
265 0           return $tens_ord{$t};
266             }
267             # Macedonian compound: tens "и" unit-ordinal
268 0           return $tens_ord{$t} . ' и ' . $irregular{$remainder};
269             }
270              
271             # Should not reach here
272 0           return;
273 1     1   584 }
  1         1  
  1         4  
274              
275             # }}}
276              
277             # {{{ capabilities declare supported features
278              
279             sub capabilities {
280             return {
281 0     0 1   cardinal => 1,
282             ordinal => 1,
283             };
284             }
285              
286             # }}}
287             1;
288              
289             __END__