File Coverage

blib/lib/Math/Calc/Units.pm
Criterion Covered Total %
statement 46 47 97.8
branch 5 8 62.5
condition 2 3 66.6
subroutine 12 12 100.0
pod 0 4 0.0
total 65 74 87.8


line stmt bran cond sub pod time code
1             package Math::Calc::Units;
2              
3 1     1   1199 use Math::Calc::Units::Compute qw(compute);
  1         9  
  1         71  
4 1     1   8 use Math::Calc::Units::Rank qw(render render_unit choose_juicy_ones);
  1         2  
  1         65  
5 1     1   7 use Math::Calc::Units::Convert;
  1         2  
  1         41  
6              
7 1     1   6 use base 'Exporter';
  1         2  
  1         114  
8 1     1   7 use vars qw($VERSION @EXPORT_OK);
  1         2  
  1         80  
9             BEGIN {
10 1     1   3 $VERSION = '1.07';
11 1         19 @EXPORT_OK = qw(calc readable convert equal exact);
12             }
13 1     1   7 use strict;
  1         60  
  1         374  
14              
15             # calc : string -> string
16             # calc : string x true -> magnitude x string
17             sub calc ($;$) {
18 5     5 0 799 my ($expr, $exact) = @_;
19 5         17 my $v = compute($expr);
20 1 50       10 return $exact ? ($v->[0], render_unit($v->[1])) : render($v);
21             }
22              
23             # readable : string -> ( string )
24             sub readable {
25 12     12 0 1325 my $expr = shift;
26 12         22 my %options;
27 12 50       43 if (@_ == 1) {
28 0         0 $options{verbose} = shift;
29             } else {
30 12         37 %options = @_;
31             }
32 12         53 my $v = compute($expr);
33 12         59 return map { render($_, \%options) } choose_juicy_ones($v, \%options);
  42         156  
34             }
35              
36             # convert : string x string [ x boolean ] -> string
37             sub convert ($$;$) {
38 8     8 0 1216 my ($expr, $units, $exact) = @_;
39 8         31 my $v = compute($expr);
40 8         38 my $u = compute("# $units");
41 8         55 my $c = Math::Calc::Units::Convert::convert($v, $u->[1]);
42 7 50       71 return $exact ? ($c->[0], render_unit($c->[1])) : render($c);
43             }
44              
45             # equal : string x string -> boolean
46 1     1   7 use constant EPSILON => 1e-12;
  1         2  
  1         348  
47             sub equal {
48 38     38 0 1623 my ($u, $v) = @_;
49 38         132 $u = compute($u);
50 38         113 $v = compute($v);
51 38         135 $v = Math::Calc::Units::Convert::convert($v, $u->[1]);
52 37         122 $u = $u->[0];
53 37         58 $v = $v->[0];
54 37 100 66     134 return 1 if ($u == 0) && abs($v) < EPSILON;
55 35         282 return abs(($u-$v)/$u) < EPSILON;
56             }
57              
58             if (!(caller)) {
59             my $verbose;
60             my %options;
61             if ($ARGV[0] eq '-v') { shift; $options{verbose} = 1; }
62             if ($ARGV[0] eq '-a') { shift; $options{abbreviate} = 1; }
63             print "$_\n" foreach readable($ARGV[0], %options);
64             }
65              
66             =head1 NAME
67              
68             Math::Calc::Units - Human-readable unit-aware calculator
69              
70             =head1 SYNOPSIS
71              
72             use Math::Calc::Units qw(calc readable convert equal);
73              
74             print "It will take ".calc("10MB/(384Kbps)")." to download\n";
75              
76             my @alternative_descriptions = readable("10MB/(384Kbps)");
77              
78             print "A week is ".convert("1 week", "seconds")." long\n";
79              
80             if (equal("$rate bytes / sec", "1 MB/sec")) { ... };
81              
82             =head1 DESCRIPTION
83              
84             C is a simple calculator that keeps track of units. It
85             currently handles combinations of byte sizes and duration only,
86             although adding any other multiplicative types is easy. Any unknown
87             type is treated as a unique user type (with some effort to map English
88             plurals to their singular forms).
89              
90             The primary intended use is via the C script that prints out
91             all of the "readable" variants of a value. For example, C<"3 bytes">
92             will only produce C<"3 byte">, but C<"3 byte / sec"> produces the
93             original along with C<"180 byte / minute">, C<"10.55 kilobyte / hour">,
94             etc.
95              
96             The C interface only provides for string-based
97             computations, which could result in a large loss of precision for some
98             applications. If you need the exact result, you may pass in an extra
99             parameter C<'exact'> to C or C, causing them to return a
100             2-element list containing the numerical result and a string describing
101             the units of that result:
102              
103             my ($value, $units) = convert("10MB/sec", "GB/day");
104              
105             (In scalar context, they just return the numeric value.)
106              
107             =head2 Examples of use
108              
109             =over 4
110              
111             =item * Estimate transmission rates (e.g., 10MB at 384 kilobit/sec)
112              
113             =item * Estimate performance characteristics (e.g., disk I/O rates)
114              
115             =item * Figure out how long something will take to complete
116              
117             =back
118              
119             I tend to work on performance-sensitive code that involves a lot of
120             network and disk traffic, so I wrote this tool after I became very
121             sick of constantly converting KB/sec to GB/day when trying to figure
122             out how long a run is going to take, or what the theoretical maximum
123             performance would be if we were 100% disk bound. Now I can't live
124             without it.
125              
126             =head2 Contraindications
127              
128             If you are just trying to convert from one unit to another, you'll
129             probably be better off with C or C. This
130             module really only makes sense when you're converting to and from
131             human-readable values.
132              
133             =head1 AUTHOR
134              
135             Steve Fink
136              
137             =head1 SEE ALSO
138              
139             ucalc, Math::Units, Convert::Units.
140              
141             =cut
142              
143             1;