File Coverage

blib/lib/Lingua/KOR/Num2Word.pm
Criterion Covered Total %
statement 27 67 40.3
branch 2 42 4.7
condition 4 39 10.2
subroutine 9 11 81.8
pod 3 3 100.0
total 45 162 27.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::KOR::Num2Word;
4             # ABSTRACT: Number to word conversion in Korean
5              
6 1     1   94632 use 5.16.0;
  1         3  
7 1     1   5 use utf8;
  1         1  
  1         24  
8 1     1   23 use warnings;
  1         1  
  1         46  
9              
10             # {{{ use block
11              
12 1     1   4 use Carp;
  1         3  
  1         60  
13 1     1   448 use Export::Attrs;
  1         8489  
  1         5  
14 1     1   588 use Readonly;
  1         4013  
  1         440  
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             # {{{ num2kor_cardinal convert number to text
25              
26             sub num2kor_cardinal :Export {
27 2     2 1 135102 my $positive = shift;
28              
29 2 50 33     34 croak 'You should specify a number from interval [0, 999_999_999_999]'
      33        
      33        
30             if !defined $positive
31             || $positive !~ m{\A\d+\z}xms
32             || $positive < 0
33             || $positive > 999_999_999_999;
34              
35 2         13 my @digits = ('영', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구');
36              
37 2 50 33     15 return $digits[$positive] if ($positive >= 0 && $positive <= 9); # 0 .. 9
38 0 0         return '십' if ($positive == 10); # 10
39              
40 0           my $out; # string for return value construction
41             my $one_idx; # index for digits array
42 0           my $remain; # remainder
43              
44 0 0 0       if ($positive > 10 && $positive < 100) { # 11 .. 99
    0 0        
    0 0        
    0 0        
    0 0        
45 0           $one_idx = int ($positive / 10);
46 0           $remain = $positive % 10;
47              
48 0 0         $out = $one_idx > 1 ? "$digits[$one_idx]십" : '십';
49 0 0         $out .= $remain ? $digits[$remain] : '';
50             }
51             elsif ($positive > 99 && $positive < 1000) { # 100 .. 999
52 0           $one_idx = int ($positive / 100);
53 0           $remain = $positive % 100;
54              
55 0 0         $out = $one_idx > 1 ? "$digits[$one_idx]백" : '백';
56 0 0         $out .= $remain ? num2kor_cardinal($remain) : '';
57             }
58             elsif ($positive > 999 && $positive < 10_000) { # 1000 .. 9999
59 0           $one_idx = int ($positive / 1000);
60 0           $remain = $positive % 1000;
61              
62 0 0         $out = $one_idx > 1 ? "$digits[$one_idx]천" : '천';
63 0 0         $out .= $remain ? num2kor_cardinal($remain) : '';
64             }
65             elsif ($positive > 9_999 && $positive < 100_000_000) { # 10000 .. 99_999_999
66 0           $one_idx = int ($positive / 10000);
67 0           $remain = $positive % 10000;
68              
69 0           $out = num2kor_cardinal($one_idx) . '만';
70 0 0         $out .= $remain ? num2kor_cardinal($remain) : '';
71             }
72             elsif ($positive > 99_999_999
73             && $positive < 1_000_000_000_000) { # 100_000_000 .. 999_999_999_999
74 0           $one_idx = int ($positive / 100_000_000);
75 0           $remain = $positive % 100_000_000;
76              
77 0           $out = num2kor_cardinal($one_idx) . '억';
78 0 0         $out .= $remain ? num2kor_cardinal($remain) : '';
79             }
80              
81 0           return $out;
82 1     1   8 }
  1         2  
  1         7  
83              
84             # }}}
85              
86              
87             # {{{ num2kor_ordinal convert number to ordinal text
88              
89             sub num2kor_ordinal :Export {
90 0     0 1   my $number = shift;
91              
92 0 0 0       croak 'You should specify a number from interval [1, 999_999_999_999]'
      0        
      0        
93             if !defined $number
94             || $number !~ m{\A\d+\z}xms
95             || $number < 1
96             || $number > 999_999_999_999;
97              
98             # Korean ordinals use native Korean numbers + 번째 (beonjjae).
99             # 1st is the special form 첫 번째 (cheot beonjjae).
100             # Native Korean numbers exist for 1-99; beyond that, use
101             # Sino-Korean cardinal + 번째.
102              
103 0 0         return '첫 번째' if $number == 1;
104              
105             # Native Korean ones (adnominal/counter forms used before 번째)
106             # Index maps to digit value: 0=unused, 1=한, 2=두, ...
107 0           my @native_ones = ('', '한', '두', '세', '네', '다섯', '여섯', '일곱', '여덟', '아홉');
108 0           my @native_tens = ('열', '스물', '서른', '마흔', '쉰', '예순', '일흔', '여든', '아흔');
109              
110 0 0 0       if ($number >= 2 && $number <= 99) {
111 0           my $tens = int($number / 10);
112 0           my $ones = $number % 10;
113              
114 0           my $out = '';
115 0 0         $out .= $native_tens[$tens - 1] if $tens > 0;
116 0 0         $out .= $native_ones[$ones] if $ones > 0;
117 0           $out .= ' 번째';
118 0           return $out;
119             }
120              
121             # For 100+, fall back to Sino-Korean cardinal + 번째
122 0           return num2kor_cardinal($number) . ' 번째';
123 1     1   373 }
  1         1  
  1         4  
124              
125             # }}}
126              
127             # {{{ capabilities declare supported features
128              
129             sub capabilities {
130             return {
131 0     0 1   cardinal => 1,
132             ordinal => 1,
133             };
134             }
135              
136             # }}}
137             1;
138              
139             __END__