File Coverage

blib/lib/Lingua/ZHO/Numbers.pm
Criterion Covered Total %
statement 69 81 85.1
branch 21 32 65.6
condition 20 35 57.1
subroutine 11 15 73.3
pod 6 6 100.0
total 127 169 75.1


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; eval: (folding-mode 1); coding:utf-8; -*-
2             #
3             # (c) 2003-2010 PetaMem, s.r.o.
4             #
5              
6             package Lingua::ZHO::Numbers;
7             # ABSTRACT: Number 2 word conversion in ZHO.
8              
9             # {{{ use block
10              
11 1     1   144840 use 5.10.1;
  1         6  
12 1     1   6 use strict;
  1         2  
  1         36  
13 1     1   7 use Carp;
  1         12  
  1         91  
14 1     1   7 use Exporter;
  1         2  
  1         66  
15 1     1   6 use base 'Exporter';
  1         2  
  1         221  
16 1     1   9 use vars qw($Charset @EXPORT_OK);
  1         2  
  1         612  
17              
18             # }}}
19             # {{{ variables declaration
20             our $VERSION = '0.2603230';
21              
22             @EXPORT_OK = 'number_to_zh';
23              
24             $Charset = 'pinyin';
25              
26             our %MAP = (
27             ($] >= 5.006) ? eval ## no critic
28             q(
29             'traditional' => {
30             mag => [ '', split(' ', "\x{842c} \x{5104} \x{5146} \x{4eac} \x{5793} \x{79ed} \x{7a70} \x{6e9d} \x{6f97} \x{6b63} \x{8f09} \x{6975} \x{6046}\x{6cb3}\x{6c99} \x{963f}\x{50e7}\x{7947} \x{90a3}\x{7531}\x{4ed6} \x{4e0d}\x{53ef}\x{601d}\x{8b70} \x{7121}\x{91cf}\x{5927}\x{6578}") ],
31             ord => [ '', split(' ', "\x{5341} \x{767e} \x{5343}") ],
32             dig => [ split(' ', "\x{96f6} \x{4e00} \x{4e8c} \x{4e09} \x{56db} \x{4e94} \x{516d} \x{4e03} \x{516b} \x{4e5d} \x{5341}") ],
33             dot => "\x{9ede}",
34             neg => "\x{8ca0}",
35             },
36             'simplified' => {
37             mag => [ '', split(' ', "\x{4e07} \x{4ebf} \x{5146} \x{4eac} \x{5793} \x{79ed} \x{7a70} \x{6c9f} \x{6da7} \x{6b63} \x{8f7d} \x{6781} \x{6052}\x{6cb3}\x{6c99} \x{963f}\x{50e7}\x{7957} \x{90a3}\x{7531}\x{4ed6} \x{4e0d}\x{53ef}\x{601d}\x{8bae} \x{65e0}\x{91cf}\x{5927}\x{6570}") ],
38             ord => [ '', split(' ', "\x{5341} \x{767e} \x{5343}") ],
39             dig => [ split(' ', "\x{96f6} \x{4e00} \x{4e8c} \x{4e09} \x{56db} \x{4e94} \x{516d} \x{4e03} \x{516b} \x{4e5d} \x{5341}") ],
40             dot => "\x{70b9}",
41             neg => "\x{8d1f}",
42             },
43             ) : (),
44             'big5' => {
45             mag => [ '', split(' ', "\xB8U \xBB\xF5 \xA5\xFC \xA8\xCA \xAB\xB2 \xD2\xF1 \xF6\xF8 \xB7\xBE \xBC\xEE \xA5\xBF \xB8\xFC \xB7\xA5 \xAB\xED\xAAe\xA8F \xAA\xFC\xB9\xAC\xAC\xE9 \xA8\xBA\xA5\xD1\xA5L \xA4\xA3\xA5i\xAB\xE4\xC4\xB3 \xB5L\xB6q\xA4j\xBC\xC6") ],
46             ord => [ '', split(' ', "\xA4Q \xA6\xCA \xA4d") ],
47             dig => [ split(' ', "\xB9s \xA4\@ \xA4G \xA4T \xA5| \xA4\xAD \xA4\xBB \xA4C \xA4K \xA4E \xA4Q") ],
48             dot => "\xC2I",
49             neg => "\xADt",
50             },
51             'gb' => {
52             mag => [ '', split(' ', "\xCD\xF2 \xD2\xDA \xD5\xD7 \xBE\xA9 \xDB\xF2 \xEF\xF6 \xF0\xA6 \xB9\xB5 \xBD\xA7 \xD5\xFD \xD4\xD8 \xBC\xAB \xBA\xE3\xBA\xD3\xC9\xB3 \xB0\xA2\xC9\xAE\xEC\xF3 \xC4\xC7\xD3\xC9\xCB\xFB \xB2\xBB\xBF\xC9\xCB\xBC\xD2\xE9 \xCE\xDE\xC1\xBF\xB4\xF3\xCA\xFD") ],
53             ord => [ '', split(' ', "\xCA\xAE \xB0\xD9 \xC7\xA7") ],
54             dig => [ split(' ', "\xC1\xE3 \xD2\xBB \xB6\xFE \xC8\xFD \xCB\xC4 \xCE\xE5 \xC1\xF9 \xC6\xDF \xB0\xCB \xBE\xC5 \xCA\xAE") ],
55             dot => "\xB5\xE3",
56             neg => "\xB8\xBA",
57             },
58             'pinyin' => {
59             mag => [ '', map {$_ } qw(
60             Wan Yi Zhao Jing Gai Zi Rang Gou Jian Zheng Zai Ji
61             HengHeSha AZengZhi NaYouTa BuKeSiYi WuLiangDaShu
62             ) ],
63             ord => [ '', map {$_ } qw(Shi Bai Qian) ],
64             dig => [ qw(Ling Yi Er San Si Wu Liu Qi Ba Jiu Shi) ],
65             dot => ' Dian ',
66             neg => 'Fu ',
67             },
68             );
69             # }}}
70              
71             # {{{ import
72              
73             sub import {
74 1     1   13 my ($class, $charset) = @_;
75 1         4 $class->charset($charset);
76 1         122 return $class->export_to_level(1, $class);
77             }
78              
79             # }}}
80             # {{{ charset
81              
82             sub charset {
83 4     4 1 2864 my ($class, $charset) = @_;
84              
85 1     1   19 no strict 'refs'; ## no critic
  1         3  
  1         1363  
86 4 100       18 return ${"$class\::Charset"} unless defined $charset;
  1         5  
87              
88 3 50 33     33 $charset = 'gb' if $charset =~ /^gb/i or $charset =~ /^euc-cn$/i;
89 3 100       14 $charset = 'big5' if $charset =~ /big5/i;
90 3 50       7 return ${"$class\::Charset"} = lc($charset) if exists ${"$class\::MAP"}{lc($charset)};
  3         13  
  3         23  
91             }
92              
93             # }}}
94             # {{{ map_zho
95              
96             sub map_zho {
97 0     0 1 0 return \%MAP;
98             }
99              
100             # }}}
101             # {{{ new
102              
103             sub new {
104 0     0 1 0 my ($class, $num) = @_;
105 0         0 bless (\$num, $class);
106             }
107              
108             # }}}
109             # {{{ parse
110              
111             sub parse {
112 0     0 1 0 my ($self, $num) = @_;
113 0         0 ${$self} = $num;
  0         0  
114             }
115              
116             # }}}
117             # {{{ get_string
118              
119             sub get_string {
120 0     0 1 0 my ($self) = @_;
121 0         0 return number_to_zh($$self);
122             }
123              
124             # }}}
125             # {{{ number_to_zh
126              
127             sub number_to_zh {
128 12     12 1 239508 my @a = @_;
129 12         59 return __PACKAGE__->_convert($MAP{$Charset}, @a);
130             }
131              
132             # }}}
133             # {{{ convert
134              
135             sub _convert {
136 12     12   31 my ($class, $map, $input) = @_;
137              
138 12 100 66     184 croak 'You should specify a number from interval [0, trillion)'
      66        
139             if !defined $input
140             || $input !~ m{\A[\-\.\d]+\z}xms
141             || $input >= 10 ** 15;
142              
143              
144 8         24 $input =~ s/[^\d\.\-]//;
145              
146 8         13 my @dig = @{$map->{dig}};
  8         44  
147 8         21 my @ord = @{$map->{ord}};
  8         28  
148 8         12 my @mag = @{$map->{mag}};
  8         56  
149 8         17 my $dot = $map->{dot};
150 8         15 my $neg = $map->{neg};
151              
152 8         36 my $out = '';
153 8         13 my $delta;
154 8 50       26 if ($input =~ s/\.(.*)//) {
155 0         0 $delta = $1;
156             }
157              
158 8 50       26 $out = $neg if $input =~ s/^\-//;
159 8         25 $input =~ s/^0+//;
160 8   100     35 $input ||= '0';
161              
162 8         12 my @chunks;
163 8         90 unshift @chunks, $1 while ($input =~ s/(\d{1,4})$//g);
164 8         16 my $mag = $#chunks;
165 8 50       900 my $zero = ($] >= 5.005) ? eval 'qr/$dig[0]$/' : quotemeta($dig[0]) . '$'; ## no critic
166              
167 8         43 foreach my $num (@chunks) {
168 12         20 my $tmp = '';
169              
170 12         31 for (reverse 0..3) {
171 48         155 my $n = int($num / (10 ** $_)) % 10;
172 48 100 100     159 next unless $tmp or $n;
173 20 50 33     89 $tmp .= $dig[$n] unless ($n == 0 and $tmp =~ $zero)
      66        
      33        
      33        
174             or ($_ == 1 and $n == 1 and not $tmp);
175 20 50       74 $tmp .= $ord[$_] if $n;
176             }
177              
178 12 50       60 $tmp =~ s/$zero// unless $tmp eq $dig[0];
179 12 100       32 $tmp .= $mag[$mag] if $tmp;
180 12 50 66     44 $tmp = $dig[0].$tmp if $num < 1000 and $mag != $#chunks
      33        
181             and $out !~ $zero;
182 12         25 $out .= $tmp;
183 12         25 $mag--;
184             }
185              
186 8 50       39 $out =~ s/$zero// unless $out eq $dig[0];
187              
188 8 50       22 if ($delta) {
189 0         0 $delta =~ s/^0\.//;
190 0         0 $out .= $dot;
191 0         0 $out .= $dig[$_] for split(//, $delta);
192             }
193              
194 8   66     85 return $out || $dig[0];
195             }
196              
197             # }}}
198              
199             1;
200             __END__