File Coverage

blib/lib/Lingua/HUN/Num2Word.pm
Criterion Covered Total %
statement 28 105 26.6
branch 2 68 2.9
condition 3 51 5.8
subroutine 9 13 69.2
pod 3 3 100.0
total 45 240 18.7


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; eval: (folding-mode 1); coding:utf-8 -*-
2              
3             package Lingua::HUN::Num2Word;
4             # ABSTRACT: Number to word conversion in Hungarian
5              
6 1     1   130370 use 5.16.0;
  1         5  
7 1     1   6 use utf8;
  1         3  
  1         14  
8 1     1   34 use warnings;
  1         2  
  1         73  
9              
10             # {{{ use block
11              
12 1     1   7 use Carp;
  1         4  
  1         92  
13 1     1   621 use Export::Attrs;
  1         11381  
  1         8  
14 1     1   1761 use Readonly;
  1         6218  
  1         563  
15              
16             # }}}
17             # {{{ variable declarations
18              
19             my Readonly::Scalar $COPY = 'Copyright (c) PetaMem, s.r.o. 2002-present';
20             our $VERSION = '0.2603300';
21              
22             # }}}
23              
24             # {{{ num2hun_cardinal convert number to text
25              
26             sub num2hun_cardinal :Export {
27 2     2 1 222535 my $positive = shift;
28              
29 2 50 33     39 croak 'You should specify a number from interval [0, 999_999_999]'
      33        
      33        
30             if !defined $positive
31             || $positive !~ m{\A\d+\z}xms
32             || $positive < 0
33             || $positive > 999_999_999;
34              
35 2         13 my @ones = qw(nulla egy kettő három négy öt hat hét nyolc kilenc);
36 2         10 my @tens = qw(tíz húsz harminc negyven ötven hatvan hetven nyolcvan kilencven);
37              
38             # 0 .. 9
39 2 50       14 return $ones[$positive] if $positive < 10;
40              
41             # 10 .. 19
42 0 0 0       if ($positive >= 10 && $positive < 20) {
43 0 0         return 'tíz' if $positive == 10;
44 0           return 'tizen' . $ones[$positive - 10];
45             }
46              
47             # 20 .. 29
48 0 0 0       if ($positive >= 20 && $positive < 30) {
49 0 0         return 'húsz' if $positive == 20;
50 0           return 'huszon' . $ones[$positive - 20];
51             }
52              
53             # 30 .. 99
54 0 0 0       if ($positive >= 30 && $positive < 100) {
55 0           my $ten_idx = int($positive / 10);
56 0           my $remain = $positive % 10;
57              
58 0           my $out = $tens[$ten_idx - 1];
59 0 0         $out .= $ones[$remain] if $remain;
60 0           return $out;
61             }
62              
63 0           my $out;
64             my $idx;
65 0           my $remain;
66              
67             # 100 .. 999
68 0 0 0       if ($positive >= 100 && $positive < 1000) {
    0 0        
    0 0        
69 0           $idx = int($positive / 100);
70 0           $remain = $positive % 100;
71              
72 0 0         $out = $idx == 1 ? 'száz' : _compound_cardinal($idx) . 'száz';
73 0 0         $out .= $remain ? num2hun_cardinal($remain) : '';
74             }
75             # 1000 .. 999_999
76             elsif ($positive >= 1000 && $positive < 1_000_000) {
77 0           $idx = int($positive / 1000);
78 0           $remain = $positive % 1000;
79              
80 0 0         $out = $idx == 1 ? 'ezer' : _compound_cardinal($idx) . 'ezer';
81 0 0         $out .= $remain ? num2hun_cardinal($remain) : '';
82             }
83             # 1_000_000 .. 999_999_999
84             elsif ($positive >= 1_000_000 && $positive < 1_000_000_000) {
85 0           $idx = int($positive / 1_000_000);
86 0           $remain = $positive % 1_000_000;
87              
88 0           $out = _compound_cardinal($idx) . 'millió';
89 0 0         $out .= $remain ? '-' . num2hun_cardinal($remain) : '';
90             }
91              
92 0           return $out;
93 1     1   10 }
  1         1  
  1         10  
94              
95             # }}}
96             # {{{ _compound_cardinal cardinal form using két instead of kettő
97              
98             sub _compound_cardinal {
99 0     0     my $positive = shift;
100              
101 0           my $text = num2hun_cardinal($positive);
102 0           $text =~ s{kettő}{két}gxms;
103              
104 0           return $text;
105             }
106              
107             # }}}
108              
109              
110             # {{{ num2hun_ordinal convert number to ordinal text
111              
112             sub num2hun_ordinal :Export {
113 0     0 1   my $number = shift;
114              
115 0 0 0       croak 'You should specify a number from interval [1, 999_999_999]'
      0        
      0        
116             if !defined $number
117             || $number !~ m{\A\d+\z}xms
118             || $number < 1
119             || $number > 999_999_999;
120              
121             # Fully irregular forms (only for standalone 1 and 2)
122 0 0         return 'első' if $number == 1;
123 0 0         return 'második' if $number == 2;
124              
125 0           return _hun_ordinal_compound($number);
126 1     1   523 }
  1         2  
  1         5  
127              
128             # }}}
129             # {{{ _hun_ordinal_compound ordinal for any number (compound-safe)
130              
131             # Internal: returns the compound ordinal form for any number.
132             # Unlike the public function, 1 -> egyedik, 2 -> kettedik (not első/második).
133             sub _hun_ordinal_compound {
134 0     0     my $number = shift;
135              
136             # Ordinal forms of single digits (compound-safe)
137 0           my @ones_ord = (
138             q{}, # 0 - unused
139             'egyedik', # 1
140             'kettedik', # 2
141             'harmadik', # 3
142             'negyedik', # 4
143             'ötödik', # 5
144             'hatodik', # 6
145             'hetedik', # 7
146             'nyolcadik', # 8
147             'kilencedik', # 9
148             );
149              
150             # Simple 1-9: direct lookup
151 0 0 0       return $ones_ord[$number] if $number >= 1 && $number <= 9;
152              
153             # Ordinal forms of round tens
154 0           my @tens_ord = (
155             q{}, # 0
156             'tizedik', # 10
157             'huszadik', # 20
158             'harmincadik', # 30
159             'negyvenedik', # 40
160             'ötvenedik', # 50
161             'hatvanadik', # 60
162             'hetvenedik', # 70
163             'nyolcvanadik', # 80
164             'kilencvenedik', # 90
165             );
166              
167             # 10-99
168 0 0 0       if ($number >= 10 && $number < 100) {
169 0           my $ten_idx = int($number / 10);
170 0           my $remain = $number % 10;
171              
172 0 0         return $tens_ord[$ten_idx] if $remain == 0;
173              
174             # Compound: cardinal prefix of tens + ordinal of ones
175             # 10s use "tizen-", 20s use "huszon-", 30+ use cardinal tens form
176 0           my $prefix;
177 0 0         if ($ten_idx == 1) { $prefix = 'tizen'; }
  0 0          
178 0           elsif ($ten_idx == 2) { $prefix = 'huszon'; }
179             else {
180 0           my @tens = qw(tíz húsz harminc negyven ötven hatvan hetven nyolcvan kilencven);
181 0           $prefix = $tens[$ten_idx - 1];
182             }
183              
184 0           return $prefix . $ones_ord[$remain];
185             }
186              
187             # 100-999
188 0 0 0       if ($number >= 100 && $number < 1000) {
189 0           my $hun_idx = int($number / 100);
190 0           my $remain = $number % 100;
191              
192 0 0         if ($remain == 0) {
193 0 0         return 'századik' if $hun_idx == 1;
194 0           return _compound_cardinal($hun_idx) . 'századik';
195             }
196              
197 0 0         my $prefix = $hun_idx == 1 ? 'száz' : _compound_cardinal($hun_idx) . 'száz';
198 0           return $prefix . _hun_ordinal_compound($remain);
199             }
200              
201             # 1000-999_999
202 0 0 0       if ($number >= 1000 && $number < 1_000_000) {
203 0           my $tho_idx = int($number / 1000);
204 0           my $remain = $number % 1000;
205              
206 0 0         if ($remain == 0) {
207 0 0         return 'ezredik' if $tho_idx == 1;
208 0           return _compound_cardinal($tho_idx) . 'ezredik';
209             }
210              
211 0 0         my $prefix = $tho_idx == 1 ? 'ezer' : _compound_cardinal($tho_idx) . 'ezer';
212 0           return $prefix . _hun_ordinal_compound($remain);
213             }
214              
215             # 1_000_000-999_999_999
216 0 0 0       if ($number >= 1_000_000 && $number < 1_000_000_000) {
217 0           my $mil_idx = int($number / 1_000_000);
218 0           my $remain = $number % 1_000_000;
219              
220 0 0         if ($remain == 0) {
221 0           return _compound_cardinal($mil_idx) . 'milliomodik';
222             }
223              
224 0           my $prefix = _compound_cardinal($mil_idx) . 'millió-';
225 0           return $prefix . _hun_ordinal_compound($remain);
226             }
227              
228 0           return;
229             }
230              
231             # }}}
232              
233             # {{{ capabilities declare supported features
234              
235             sub capabilities {
236             return {
237 0     0 1   cardinal => 1,
238             ordinal => 1,
239             };
240             }
241              
242             # }}}
243             1;
244              
245             __END__