File Coverage

lib/In/Korean/Numbers/SinoKorean.pm
Criterion Covered Total %
statement 63 63 100.0
branch 28 32 87.5
condition 9 9 100.0
subroutine 10 10 100.0
pod 3 7 42.8
total 113 121 93.3


line stmt bran cond sub pod time code
1             package In::Korean::Numbers::SinoKorean;
2              
3 1     1   36187 use POSIX;
  1         10716  
  1         7  
4 1     1   7951 use strict;
  1         3  
  1         62  
5 1     1   7 use warnings;
  1         7  
  1         1132  
6              
7             our $VERSION = '0.04'; # Also update POD version below
8              
9             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
10              
11             # Map Hangul to integer
12             my %int_to_char_map = (
13             0 => "\x{C601}",
14             1 => "\x{C77C}",
15             2 => "\x{C774}",
16             3 => "\x{C0BC}",
17             4 => "\x{C0AC}",
18             5 => "\x{C624}",
19             6 => "\x{C721}",
20             7 => "\x{CE60}",
21             8 => "\x{D314}",
22             9 => "\x{AD6C}",
23             10 => "\x{C2ED}",
24             100 => "\x{BC31}",
25             1000 =>"\x{CC9C}",
26             10000 => "\x{B9CC}",
27             );
28              
29             my %char_to_int_map; # Lazily created from %int_to_char_map
30              
31             # All numbers are expressed as a combination of the following units
32             my @units = (10000, 1000, 100, 10, 1);
33              
34             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
35             sub new {
36 1     1 1 437 my $class = shift;
37 1         5 return bless {}, $class;
38             }
39              
40             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
41             sub getHangul {
42              
43 114     114 1 25203 my( $self, $num ) = get_args( @_ );
44              
45 114 50       246 return undef if not defined $num;
46              
47             # Must be positive integer
48 114 100       559 return undef if not is_positive_int_or_zero( $num );
49            
50 111         178 my @hangul = ();
51              
52 111         126 my $remaining = $num;
53              
54 111         162 foreach my $unit ( @units ) {
55              
56 525 100       972 last if $remaining == 0; # Performance reasons only
57              
58             # Find the mutiple for the current unit.
59             # E.g., 502,217 for key=10,000, then multiple = 52
60 491         1008 my $multiple = floor( $remaining / $unit );
61              
62 491 100       1130 next if $multiple < 1;
63              
64 183         207 $remaining %= $unit;
65              
66             # Recursively call to get value greater than 10
67 183 100       390 my $multiple_str = $multiple >= 10 ? getHangul( $self, $multiple ) : int_to_char( $multiple );
68              
69             # Don't push Hangul value for 1 unless currently handling 1 unit
70 183 100 100     573 push @hangul, $multiple_str unless $multiple == 1 && $unit != 1;
71 183 100       459 push @hangul, int_to_char( $unit ) if $unit != 1;
72             }
73              
74 111 100       731 return join( '', @hangul ) if @hangul;
75              
76 2         11 return "\x{C601}";
77             }
78              
79             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
80             sub getInt {
81              
82 104     104 1 230 my( $self, $hangul ) = get_args( @_ );
83              
84 104 50       226 return undef if not defined $hangul;
85              
86             # Tokenize so process each character separately
87 104         502 my @tokens = split( //, $hangul );
88            
89 104         296 my $total = 0;
90            
91 104         212 while( @tokens ) {
92 184         567 my $char_char = shift @tokens;
93 184         317 my $char_int = char_to_int( $char_char );
94              
95             # If invalid input, return undef
96 184 50       786 return undef if ! defined( $char_int );
97            
98             # If 만, multiply everything by 10,000
99 184 100 100     1391 if ( $char_char eq "\x{B9CC}") {
    100 100        
100 8 100       195 if ( $total ) {
101 6         7 $total *= 10000;
102             } else {
103 2         21 $total = 10000;
104             }
105 8         23 next;
106             }
107              
108             # If:
109             # (1) char value is greater than 9,
110             # (2) no more characters left
111             # (3) next character is 만
112             # Then add value
113             elsif ( $char_int > 9 || ! @tokens || $tokens[0] eq "\x{B9CC}" ) {
114 112         281 $total += $char_int;
115             }
116              
117             # If char 0-9 and not final, get next char (units)
118             else {
119 64         90 my $unit_char = shift @tokens;
120 64         269 my $unit_int = char_to_int( $unit_char );
121 64         368 $total += ( $char_int * $unit_int );
122             }
123             }
124              
125 104         520 return $total;
126             }
127              
128             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
129             # Returns $self and $val values from arguments. Handles
130             # presence of $self (if o-o) and absence of $self (if
131             # procedural).
132             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133             sub get_args {
134 218     218 0 243 my( $self, $val );
135              
136 218 100       717 if ( @_ >= 2 ) {
    50          
137 113         362 ( $self, $val ) = @_;
138             } elsif ( @_ == 1 ) {
139 105         186 ( $val ) = @_;
140             }
141            
142 218         469 return ( $self, $val );
143             }
144              
145             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
146             # Converts integer (e.g., 1) to Hangul block (e.g., 일) using
147             # %int_to_char_map.
148             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
149             sub int_to_char {
150 283     283 0 439 my $int = shift;
151 283         615 return $int_to_char_map{ $int };
152             }
153              
154             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
155             # Converts hangul block (e.g., 일) to integer (e.g., 1) using
156             # %char_to_int_map.
157             #
158             # Note that %char_to_int_map is lazily created from
159             # %int_to_char_map.
160             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
161             sub char_to_int {
162 248     248 0 435 my $char = shift;
163              
164 248 100       1151 if (! %char_to_int_map ) {
165 1         2 %char_to_int_map = ();
166              
167 1         6 for my $int ( keys %int_to_char_map ) {
168 14         17 my $hangul = $int_to_char_map{ $int };
169 14         47 $char_to_int_map{ $hangul } = $int; # Inverse of int_to_char_map
170             }
171             }
172              
173 248         403 my $int = $char_to_int_map{ $char };
174              
175 248         720 return $int;
176             }
177              
178             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
179             # Returns true if value is a positive integer or zero.
180             #
181             # Source: http://www.perlmonks.org/?node_id=614452
182             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
183             sub is_positive_int_or_zero {
184 114     114 0 177 my $val = shift;
185 114         266 $val =~ s/^\s+//; # leading whitespace
186 114         185 $val =~ s/\s+$//; # trailing whitespace
187 114         662 return $val =~ /^[+]?\d+$/;
188             }
189              
190             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
191             1;
192             __END__