File Coverage

blib/lib/Lingua/ZHO/Numbers.pm
Criterion Covered Total %
statement 70 82 85.3
branch 21 32 65.6
condition 20 35 57.1
subroutine 11 15 73.3
pod 6 6 100.0
total 128 170 75.2


line stmt bran cond sub pod time code
1             # For Emacs: -*- mode:cperl; mode:folding; 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   22997 use 5.10.1;
  1         2  
  1         34  
12 1     1   4 use strict;
  1         1  
  1         29  
13 1     1   5 use Carp;
  1         9  
  1         70  
14 1     1   4 use Exporter;
  1         1  
  1         30  
15 1     1   4 use base 'Exporter';
  1         2  
  1         66  
16 1     1   4 use vars qw($Charset $VERSION @EXPORT_OK);
  1         1  
  1         402  
17              
18             # }}}
19             # {{{ variables declaration
20              
21             $Lingua::ZHO::Numbers::VERSION = 0.1192;
22              
23             @EXPORT_OK = 'number_to_zh';
24              
25             $Charset = 'pinyin';
26              
27             our %MAP = (
28             ($] >= 5.006) ? eval ## no critic
29             q(
30             'traditional' => {
31             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}") ],
32             ord => [ '', split(' ', "\x{5341} \x{767e} \x{5343}") ],
33             dig => [ split(' ', "\x{96f6} \x{4e00} \x{4e8c} \x{4e09} \x{56db} \x{4e94} \x{516d} \x{4e03} \x{516b} \x{4e5d} \x{5341}") ],
34             dot => "\x{9ede}",
35             neg => "\x{8ca0}",
36             },
37             'simplified' => {
38             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}") ],
39             ord => [ '', split(' ', "\x{5341} \x{767e} \x{5343}") ],
40             dig => [ split(' ', "\x{96f6} \x{4e00} \x{4e8c} \x{4e09} \x{56db} \x{4e94} \x{516d} \x{4e03} \x{516b} \x{4e5d} \x{5341}") ],
41             dot => "\x{70b9}",
42             neg => "\x{8d1f}",
43             },
44             ) : (),
45             'big5' => {
46             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") ],
47             ord => [ '', split(' ', "\xA4Q \xA6\xCA \xA4d") ],
48             dig => [ split(' ', "\xB9s \xA4\@ \xA4G \xA4T \xA5| \xA4\xAD \xA4\xBB \xA4C \xA4K \xA4E \xA4Q") ],
49             dot => "\xC2I",
50             neg => "\xADt",
51             },
52             'gb' => {
53             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") ],
54             ord => [ '', split(' ', "\xCA\xAE \xB0\xD9 \xC7\xA7") ],
55             dig => [ split(' ', "\xC1\xE3 \xD2\xBB \xB6\xFE \xC8\xFD \xCB\xC4 \xCE\xE5 \xC1\xF9 \xC6\xDF \xB0\xCB \xBE\xC5 \xCA\xAE") ],
56             dot => "\xB5\xE3",
57             neg => "\xB8\xBA",
58             },
59             'pinyin' => {
60             mag => [ '', map {$_ } qw(
61             Wan Yi Zhao Jing Gai Zi Rang Gou Jian Zheng Zai Ji
62             HengHeSha AZengZhi NaYouTa BuKeSiYi WuLiangDaShu
63             ) ],
64             ord => [ '', map {$_ } qw(Shi Bai Qian) ],
65             dig => [ qw(Ling Yi Er San Si Wu Liu Qi Ba Jiu Shi) ],
66             dot => ' Dian ',
67             neg => 'Fu ',
68             },
69             );
70             # }}}
71              
72             # {{{ import
73              
74             sub import {
75 1     1   6 my ($class, $charset) = @_;
76 1         2 $class->charset($charset);
77 1         59 return $class->export_to_level(1, $class);
78             }
79              
80             # }}}
81             # {{{ charset
82              
83             sub charset {
84 4     4 1 2192 my ($class, $charset) = @_;
85              
86 1     1   6 no strict 'refs'; ## no critic
  1         1  
  1         833  
87 4 100       12 return ${"$class\::Charset"} unless defined $charset;
  1         4  
88              
89 3 50 33     20 $charset = 'gb' if $charset =~ /^gb/i or $charset =~ /^euc-cn$/i;
90 3 100       10 $charset = 'big5' if $charset =~ /big5/i;
91 3 50       3 return ${"$class\::Charset"} = lc($charset) if exists ${"$class\::MAP"}{lc($charset)};
  3         10  
  3         15  
92             }
93              
94             # }}}
95             # {{{ map_zho
96              
97             sub map_zho {
98 0     0 1 0 return \%MAP;
99             }
100              
101             # }}}
102             # {{{ new
103              
104             sub new {
105 0     0 1 0 my ($class, $num) = @_;
106 0         0 bless (\$num, $class);
107             }
108              
109             # }}}
110             # {{{ parse
111              
112             sub parse {
113 0     0 1 0 my ($self, $num) = @_;
114 0         0 ${$self} = $num;
  0         0  
115             }
116              
117             # }}}
118             # {{{ get_string
119              
120             sub get_string {
121 0     0 1 0 my ($self) = @_;
122 0         0 return number_to_zh($$self);
123             }
124              
125             # }}}
126             # {{{ number_to_zh
127              
128             sub number_to_zh {
129 12     12 1 2653 my @a = @_;
130 12         37 return __PACKAGE__->_convert($MAP{$Charset}, @a);
131             }
132              
133             # }}}
134             # {{{ convert
135              
136             sub _convert {
137 12     12   18 my ($class, $map, $input) = @_;
138              
139 12 100 66     130 croak 'You should specify a number from interval [0, trillion)'
      66        
140             if !defined $input
141             || $input !~ m{\A[\-\.\d]+\z}xms
142             || $input >= 10 ** 15;
143              
144              
145 8         11 $input =~ s/[^\d\.\-]//;
146              
147 8         8 my @dig = @{$map->{dig}};
  8         35  
148 8         10 my @ord = @{$map->{ord}};
  8         20  
149 8         9 my @mag = @{$map->{mag}};
  8         34  
150 8         9 my $dot = $map->{dot};
151 8         9 my $neg = $map->{neg};
152              
153 8         8 my $out = '';
154 8         7 my $delta;
155 8 50       16 if ($input =~ s/\.(.*)//) {
156 0         0 $delta = $1;
157             }
158              
159 8 50       14 $out = $neg if $input =~ s/^\-//;
160 8         14 $input =~ s/^0+//;
161 8   100     19 $input ||= '0';
162              
163 8         8 my @chunks;
164 8         73 unshift @chunks, $1 while ($input =~ s/(\d{1,4})$//g);
165 8         11 my $mag = $#chunks;
166 8 50       488 my $zero = ($] >= 5.005) ? eval 'qr/$dig[0]$/' : quotemeta($dig[0]) . '$'; ## no critic
167              
168 8         26 foreach my $num (@chunks) {
169 12         17 my $tmp = '';
170              
171 12         19 for (reverse 0..3) {
172 48         76 my $n = int($num / (10 ** $_)) % 10;
173 48 100 100     149 next unless $tmp or $n;
174 20 50 33     136 $tmp .= $dig[$n] unless ($n == 0 and $tmp =~ $zero)
      66        
      33        
      33        
175             or ($_ == 1 and $n == 1 and not $tmp);
176 20 50       47 $tmp .= $ord[$_] if $n;
177             }
178              
179 12 50       41 $tmp =~ s/$zero// unless $tmp eq $dig[0];
180 12 100       25 $tmp .= $mag[$mag] if $tmp;
181 12 50 66     40 $tmp = $dig[0].$tmp if $num < 1000 and $mag != $#chunks
      33        
182             and $out !~ $zero;
183 12         17 $out .= $tmp;
184 12         31 $mag--;
185             }
186              
187 8 50       643 $out =~ s/$zero// unless $out eq $dig[0];
188              
189 8 50       17 if ($delta) {
190 0         0 $delta =~ s/^0\.//;
191 0         0 $out .= $dot;
192 0         0 $out .= $dig[$_] for split(//, $delta);
193             }
194              
195 8   66     61 return $out || $dig[0];
196             }
197              
198             # }}}
199              
200             1;
201             __END__