File Coverage

blib/lib/Math/Calc/Units/Convert/Metric.pm
Criterion Covered Total %
statement 66 69 95.6
branch 21 26 80.7
condition 6 14 42.8
subroutine 15 16 93.7
pod 0 13 0.0
total 108 138 78.2


line stmt bran cond sub pod time code
1             package Math::Calc::Units::Convert::Metric;
2 1     1   14 use base 'Math::Calc::Units::Convert::Base';
  1         1  
  1         611  
3 1     1   5 use strict;
  1         2  
  1         28  
4              
5 1     1   4 use vars qw(%niceSmallMetric %metric %pref %abbrev %reverse_abbrev $metric_prefix_test);
  1         1  
  1         968  
6              
7             %niceSmallMetric = ( milli => 1e-3,
8             micro => 1e-6,
9             nano => 1e-9,
10             pico => 1e-12,
11             femto => 1e-15,
12             );
13              
14             %metric = ( kilo => 1e3,
15             mega => 1e6,
16             giga => 1e9,
17             tera => 1e12,
18             peta => 1e15,
19             exa => 1e18,
20             centi => 1e-2,
21             %niceSmallMetric,
22             );
23              
24             %pref = ( unit => 1.0,
25             kilo => 0.8,
26             mega => 0.8,
27             giga => 0.8,
28             tera => 0.7,
29             peta => 0.6,
30             exa => 0.3,
31             centi => 0.1,
32             milli => 0.8,
33             micro => 0.8,
34             nano => 0.6,
35             pico => 0.4,
36             femto => 0.3,
37             );
38              
39             %abbrev = ( k => 'kilo',
40             M => 'mega',
41             G => 'giga',
42             T => 'tera',
43             P => 'peta',
44             E => 'exa',
45             c => 'centi',
46             m => 'milli',
47             u => 'micro',
48             n => 'nano',
49             p => 'pico',
50             f => 'femto',
51             );
52              
53             %reverse_abbrev = reverse %abbrev;
54              
55             # Cannot use the above tables directly because this class must be
56             # overridable. So the following three methods (get_metric,
57             # get_abbrev, and get_prefix) are the only things that are specific to
58             # this class. All other methods can be used unchanged in subclasses.
59              
60             sub pref_score {
61 292     292 0 436 my ($self, $unitName) = @_;
62 292         770 my $prefix = $self->get_prefix($unitName);
63 292   100     1051 $unitName = substr($unitName, length($prefix || ""));
64 292 100       939 my $prefix_pref = defined($prefix) ? $self->prefix_pref($prefix) : 1;
65 292         1105 return $prefix_pref * $self->SUPER::pref_score($unitName);
66             }
67              
68             sub get_metric {
69 82     82 0 137 my ($self, $what) = @_;
70 82         377 return $metric{$what};
71             }
72              
73             sub get_abbrev {
74 5     5 0 10 my ($self, $what) = @_;
75 5         19 return $abbrev{$what};
76             }
77              
78             $metric_prefix_test = qr/^(${\join("|",keys %metric)})/i;
79              
80             sub get_prefix {
81 712     712 0 911 my ($self, $what) = @_;
82 712 100       3757 if ($what =~ $metric_prefix_test) {
83 150         767 return $1;
84             } else {
85 562         1562 return;
86             }
87             }
88              
89             sub get_prefixes {
90 5     5 0 11 my ($self, $options) = @_;
91 5 100       18 if ($options->{small}) {
92 3         15 return grep { $metric{$_} < 1 } keys %metric;
  36         90  
93             } else {
94 2         13 return keys %metric;
95             }
96             }
97              
98             sub get_abbrev_prefix {
99 133     133 0 188 my ($self, $what) = @_;
100 133         226 my $prefix = substr($what, 0, 1);
101 133 100 100     913 if ($abbrev{$prefix} || $abbrev{lc($prefix)}) {
102 32         324 return $prefix;
103             } else {
104 101         511 return;
105             }
106             }
107              
108             sub variants {
109 15     15 0 25 my ($self, $base) = @_;
110 15         68 my @main = $self->SUPER::variants($base);
111 15         23 my @variants;
112 15         32 for my $u (@main) {
113 30         153 push @variants, $u, map { "$_$u" } $self->get_prefixes();
  180         366  
114             }
115 15         92 return @variants;
116             }
117              
118             sub prefix_pref {
119 44     44 0 64 my ($self, $prefix) = @_;
120 44   33     171 return $pref{lc($prefix)} || $pref{unit};
121             }
122              
123             # demetric : string => mult x base
124             #
125             # (pronounced de-metric, not demmetric or deme trick)
126             #
127             sub demetric {
128 684     684 0 897 my ($self, $string) = @_;
129 684 100       1692 if (my $prefix = $self->get_prefix($string)) {
130 253         439 my $base = substr($string, length($prefix));
131 253         859 return ($self->get_metric($prefix), $base);
132             } else {
133 431         1623 return (1, $string);
134             }
135             }
136              
137             # expand : char => ( prefix )
138             #
139             sub expand {
140 5     5 0 14 my ($self, $char) = @_;
141 5         7 my @expansions;
142 5         5 my ($exact, $lower);
143 5 50 0     28 if ($exact = $self->get_abbrev($char)) {
    0          
144 5         21 push @expansions, $exact;
145             } elsif (($char ne lc($char)) && ($lower = $self->get_abbrev(lc($char)))) {
146 0         0 push @expansions, $lower;
147             }
148 5         22 return @expansions;
149             }
150              
151             # simple_convert : unitName x unitName -> multiple:number
152             #
153             # A little weird, because it allows centimegamilliwatts
154             #
155             # Example:
156             # megadouble -> millisingle
157             #
158             # (mult_from, base_from) is (1_000_000, double)
159             # (mult_to, base_to) is (.001, single)
160             # submult is 2 (from converting double -> single)
161             #
162             # return submult * (mult_from / mult_to) = 2_000_000_000
163             #
164             sub simple_convert {
165 650     650 0 1069 my ($self, $from, $to) = @_;
166              
167 650 100       1799 my ($mult_from, $base_from) = $self->demetric($from) or return;
168 626 50       2724 my ($mult_to, $base_to) = $self->demetric($to) or return;
169              
170 626         2227 my $submult = $self->SUPER::simple_convert($base_from, $base_to);
171 626 100       1811 return if ! defined $submult;
172              
173 473         2174 return $submult * ($mult_from / $mult_to);
174             }
175              
176             sub metric_abbreviation {
177 0     0 0 0 my ($self, $prefix) = @_;
178 0   0     0 return $reverse_abbrev{$prefix} || $prefix;
179             }
180              
181             sub render {
182 47     47 0 95 my ($self, $val, $name, $power, $options) = @_;
183 47 100       119 if ($options->{abbreviate}) {
184 12         40 my $stem = $self->canonical_unit;
185 12 100       137 if ($name =~ /(\w+)\Q$stem\E$/) {
186 3         13 my $prefix = $reverse_abbrev{$1};
187 3 50       8 if (defined($prefix)) {
188 3         13 $name = $prefix . $self->abbreviated_canonical_unit;
189             }
190             }
191             }
192 47         210 return $self->SUPER::render($val, $name, $power, $options);
193             }
194              
195             1;