File Coverage

blib/lib/Lingua/VIE/Num2Word.pm
Criterion Covered Total %
statement 27 74 36.4
branch 2 54 3.7
condition 4 57 7.0
subroutine 9 11 81.8
pod 3 3 100.0
total 45 199 22.6


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; eval: (folding-mode 1); coding:utf-8 -*-
2             package Lingua::VIE::Num2Word;
3             # ABSTRACT: Number to word conversion in Vietnamese
4              
5 1     1   85604 use 5.16.0;
  1         3  
6 1     1   4 use utf8;
  1         1  
  1         10  
7 1     1   19 use warnings;
  1         2  
  1         48  
8              
9             # {{{ use block
10              
11 1     1   5 use Carp;
  1         2  
  1         94  
12 1     1   510 use Export::Attrs;
  1         8700  
  1         36  
13 1     1   662 use Readonly;
  1         3842  
  1         512  
14              
15             # }}}
16             # {{{ variable declarations
17              
18             my Readonly::Scalar $COPY = 'Copyright (c) PetaMem, s.r.o. 2002-present';
19             our $VERSION = '0.2603300';
20              
21             # }}}
22              
23             # {{{ num2vie_cardinal convert number to text
24              
25             sub num2vie_cardinal :Export {
26 2     2 1 147116 my $positive = shift;
27              
28 2 50 33     36 croak 'You should specify a number from interval [0, 999_999_999]'
      33        
      33        
29             if !defined $positive
30             || $positive !~ m{\A\d+\z}xms
31             || $positive < 0
32             || $positive > 999_999_999;
33              
34 2         11 my @units = qw(không một hai ba bốn năm sáu bảy tám chín);
35              
36 2 50 33     17 return $units[$positive] if ($positive >= 0 && $positive < 10); # 0 .. 9
37 0 0         return 'mười' if ($positive == 10); # 10
38              
39             # 11 .. 19
40 0 0 0       if ($positive > 10 && $positive < 20) {
41 0           my $unit = $positive % 10;
42 0 0         return 'mười'
    0          
43             . ($unit == 5 ? ' lăm'
44             : $unit == 4 ? ' bốn'
45             : ' ' . $units[$unit]);
46             }
47              
48 0           my $out;
49             my $remain;
50              
51             # 20 .. 99
52 0 0 0       if ($positive > 19 && $positive < 100) {
    0 0        
    0 0        
    0 0        
53 0           my $tens = int($positive / 10);
54 0           $remain = $positive % 10;
55              
56 0           $out = $units[$tens] . ' mươi';
57 0 0         if ($remain == 0) {
    0          
    0          
    0          
58             # nothing
59             }
60             elsif ($remain == 1) {
61 0           $out .= ' mốt';
62             }
63             elsif ($remain == 5) {
64 0           $out .= ' lăm';
65             }
66             elsif ($remain == 4) {
67 0           $out .= ' tư';
68             }
69             else {
70 0           $out .= ' ' . $units[$remain];
71             }
72             }
73             # 100 .. 999
74             elsif ($positive > 99 && $positive < 1000) {
75 0           my $hundreds = int($positive / 100);
76 0           $remain = $positive % 100;
77              
78 0           $out = $units[$hundreds] . ' trăm';
79 0 0 0       if ($remain > 0 && $remain < 10) {
    0          
80 0           $out .= ' lẻ ' . $units[$remain];
81             }
82             elsif ($remain >= 10) {
83 0           $out .= ' ' . num2vie_cardinal($remain);
84             }
85             }
86             # 1_000 .. 999_999
87             elsif ($positive > 999 && $positive < 1_000_000) {
88 0           my $thousands = int($positive / 1000);
89 0           $remain = $positive % 1000;
90              
91 0           $out = num2vie_cardinal($thousands) . ' nghìn';
92 0 0 0       if ($remain > 0 && $remain < 10) {
    0 0        
    0          
93 0           $out .= ' không trăm lẻ ' . $units[$remain];
94             }
95             elsif ($remain >= 10 && $remain < 100) {
96 0           $out .= ' không trăm ' . num2vie_cardinal($remain);
97             }
98             elsif ($remain >= 100) {
99 0           $out .= ' ' . num2vie_cardinal($remain);
100             }
101             }
102             # 1_000_000 .. 999_999_999
103             elsif ($positive > 999_999 && $positive < 1_000_000_000) {
104 0           my $millions = int($positive / 1_000_000);
105 0           $remain = $positive % 1_000_000;
106              
107 0           $out = num2vie_cardinal($millions) . ' triệu';
108 0 0 0       if ($remain > 0 && $remain < 1000) {
    0          
109             # e.g. 1_000_005 -> một triệu không nghìn không trăm lẻ năm
110 0           my $sub_hundreds = $remain % 100;
111 0           my $sub_tens = int($remain / 100);
112 0           $out .= ' không nghìn';
113 0 0 0       if ($sub_tens == 0 && $sub_hundreds > 0 && $sub_hundreds < 10) {
    0 0        
    0 0        
114 0           $out .= ' không trăm lẻ ' . $units[$sub_hundreds];
115             }
116             elsif ($sub_tens == 0 && $sub_hundreds >= 10) {
117 0           $out .= ' không trăm ' . num2vie_cardinal($sub_hundreds);
118             }
119             elsif ($sub_tens > 0) {
120 0           $out .= ' ' . num2vie_cardinal($remain);
121             }
122             }
123             elsif ($remain >= 1000) {
124 0           $out .= ' ' . num2vie_cardinal($remain);
125             }
126             }
127              
128 0           return $out;
129 1     1   7 }
  1         1  
  1         6  
130              
131             # }}}
132              
133              
134             # {{{ num2vie_ordinal convert number to ordinal text
135              
136             sub num2vie_ordinal :Export {
137 0     0 1   my $number = shift;
138              
139 0 0 0       croak 'You should specify a number from interval [1, 999_999_999]'
      0        
      0        
140             if !defined $number
141             || $number !~ m{\A\d+\z}xms
142             || $number < 1
143             || $number > 999_999_999;
144              
145             # Vietnamese ordinals: thứ + cardinal
146             # Special cases: 1st = "thứ nhất", 4th = "thứ tư"
147 0 0         return 'thứ nhất' if $number == 1;
148 0 0         return 'thứ tư' if $number == 4;
149              
150 0           my $cardinal = num2vie_cardinal($number);
151              
152 0           return 'thứ ' . $cardinal;
153 1     1   381 }
  1         2  
  1         4  
154              
155             # }}}
156              
157             # {{{ capabilities declare supported features
158              
159             sub capabilities {
160             return {
161 0     0 1   cardinal => 1,
162             ordinal => 1,
163             };
164             }
165              
166             # }}}
167             1;
168              
169             __END__