package Math::Calc::Units::Convert::Metric;
use base 'Math::Calc::Units::Convert::Base';
use strict;
use vars qw(%niceSmallMetric %metric %pref %abbrev %reverse_abbrev $metric_prefix_test);
%niceSmallMetric = ( milli => 1e-3,
micro => 1e-6,
nano => 1e-9,
pico => 1e-12,
femto => 1e-15,
);
%metric = ( kilo => 1e3,
mega => 1e6,
giga => 1e9,
tera => 1e12,
peta => 1e15,
exa => 1e18,
centi => 1e-2,
%niceSmallMetric,
);
%pref = ( unit => 1.0,
kilo => 0.8,
mega => 0.8,
giga => 0.8,
tera => 0.7,
peta => 0.6,
exa => 0.3,
centi => 0.1,
milli => 0.8,
micro => 0.8,
nano => 0.6,
pico => 0.4,
femto => 0.3,
);
%abbrev = ( k => 'kilo',
M => 'mega',
G => 'giga',
T => 'tera',
P => 'peta',
E => 'exa',
c => 'centi',
m => 'milli',
u => 'micro',
n => 'nano',
p => 'pico',
f => 'femto',
);
%reverse_abbrev = reverse %abbrev;
# Cannot use the above tables directly because this class must be
# overridable. So the following three methods (get_metric,
# get_abbrev, and get_prefix) are the only things that are specific to
# this class. All other methods can be used unchanged in subclasses.
sub pref_score {
my ($self, $unitName) = @_;
my $prefix = $self->get_prefix($unitName);
$unitName = substr($unitName, length($prefix || ""));
my $prefix_pref = defined($prefix) ? $self->prefix_pref($prefix) : 1;
return $prefix_pref * $self->SUPER::pref_score($unitName);
}
sub get_metric {
my ($self, $what) = @_;
return $metric{$what};
}
sub get_abbrev {
my ($self, $what) = @_;
return $abbrev{$what};
}
$metric_prefix_test = qr/^(${\join("|",keys %metric)})/i;
sub get_prefix {
my ($self, $what) = @_;
if ($what =~ $metric_prefix_test) {
return $1;
} else {
return;
}
}
sub get_prefixes {
my ($self, $options) = @_;
if ($options->{small}) {
return grep { $metric{$_} < 1 } keys %metric;
} else {
return keys %metric;
}
}
sub get_abbrev_prefix {
my ($self, $what) = @_;
my $prefix = substr($what, 0, 1);
if ($abbrev{$prefix} || $abbrev{lc($prefix)}) {
return $prefix;
} else {
return;
}
}
sub variants {
my ($self, $base) = @_;
my @main = $self->SUPER::variants($base);
my @variants;
for my $u (@main) {
push @variants, $u, map { "$_$u" } $self->get_prefixes();
}
return @variants;
}
sub prefix_pref {
my ($self, $prefix) = @_;
return $pref{lc($prefix)} || $pref{unit};
}
# demetric : string => mult x base
#
# (pronounced de-metric, not demmetric or deme trick)
#
sub demetric {
my ($self, $string) = @_;
if (my $prefix = $self->get_prefix($string)) {
my $base = substr($string, length($prefix));
return ($self->get_metric($prefix), $base);
} else {
return (1, $string);
}
}
# expand : char => ( prefix )
#
sub expand {
my ($self, $char) = @_;
my @expansions;
my ($exact, $lower);
if ($exact = $self->get_abbrev($char)) {
push @expansions, $exact;
} elsif (($char ne lc($char)) && ($lower = $self->get_abbrev(lc($char)))) {
push @expansions, $lower;
}
return @expansions;
}
# simple_convert : unitName x unitName -> multiple:number
#
# A little weird, because it allows centimegamilliwatts
#
# Example:
# megadouble -> millisingle
#
# (mult_from, base_from) is (1_000_000, double)
# (mult_to, base_to) is (.001, single)
# submult is 2 (from converting double -> single)
#
# return submult * (mult_from / mult_to) = 2_000_000_000
#
sub simple_convert {
my ($self, $from, $to) = @_;
my ($mult_from, $base_from) = $self->demetric($from) or return;
my ($mult_to, $base_to) = $self->demetric($to) or return;
my $submult = $self->SUPER::simple_convert($base_from, $base_to);
return if ! defined $submult;
return $submult * ($mult_from / $mult_to);
}
sub metric_abbreviation {
my ($self, $prefix) = @_;
return $reverse_abbrev{$prefix} || $prefix;
}
sub render {
my ($self, $val, $name, $power, $options) = @_;
if ($options->{abbreviate}) {
my $stem = $self->canonical_unit;
if ($name =~ /(\w+)\Q$stem\E$/) {
my $prefix = $reverse_abbrev{$1};
if (defined($prefix)) {
$name = $prefix . $self->abbreviated_canonical_unit;
}
}
}
return $self->SUPER::render($val, $name, $power, $options);
}
1;
Copyright 2K16 - 2K18 Indonesian Hacker Rulez