File Coverage

blib/lib/Math/Units/PhysicalValue.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1              
2             package Math::Units::PhysicalValue;
3              
4 14     14   137579 use strict;
  14         288  
  14         961  
5 14     14   10173 use Math::Units::PhysicalValue::AutoUnit;
  0            
  0            
6              
7             use Carp;
8             use base qw(Exporter);
9             use Math::Units qw(convert);
10             use Number::Format;
11             use Math::BigFloat;
12             use overload
13             '+' => \&pv_add,
14             '*' => \&pv_mul,
15             '**' => \&pv_mulmul,
16             'sqrt' => \&pv_sqrt,
17             '-' => \&pv_sub,
18             '/' => \&pv_div,
19             '++' => \&pv_inc,
20             '--' => \&pv_dec,
21             '==' => \&pv_num_eq,
22             '<' => \&pv_num_lt,
23             '>' => \&pv_num_gt,
24             '<=' => \&pv_num_lte,
25             '>=' => \&pv_num_gte,
26             'eq' => \&pv_str_eq,
27             'ne' => \&pv_str_ne,
28             '""' => \&pv_print,
29             '<=>' => \&pv_ncmp,
30             'cmp' => \&pv_scmp,
31             'bool' => \&pv_bool;
32              
33             our $VERSION = 1.0007;
34              
35             our $StrictTypes = 0; # throws errors on unknown units
36             our $PrintPrecision = 2;
37             our $fmt;
38             $fmt = new Number::Format if not defined $fmt;
39              
40             our @EXPORT_OK = qw(pv PV G);
41             our @AUTO_PLURALS = ();
42              
43             # NOTE: AUTO_PLURALS and G are not documented because they are still experimental
44              
45             1;
46              
47             sub G { Math::Units::PhysicalValue->new( "6.672e-11 N m^2 / kg^2" ) }
48              
49             # PV {{{
50             sub PV {
51             my $v = shift;
52              
53             return Math::Units::PhysicalValue->new( $v );
54             }
55             *pv = *PV;
56             # }}}
57              
58             # new {{{
59             sub new {
60             my $class = shift;
61             my $value = shift;
62             my $this = bless [], $class;
63              
64             $value = 0 unless defined $value;
65              
66             if( $value =~ m/^\s*([\-\,\.\de]+)\s*([\s\w\^\d\.\/\*]*)$/ ) {
67             my ($v, $u) = ($1, $2);
68              
69             $v =~ s/\,//g;
70             $u =~ s/\^/**/g;
71             $u =~ s/(\w+(?:\*\*\d+)?)\s+(\w+(?:\*\*\d+)?)/$1*$2/g;
72             $u =~ s/\s//g;
73              
74             if ( $StrictTypes ) {
75             eval { convert(3.1415926, $u, '') };
76             if( $@ =~ /unknown unit/ ) {
77             my $e = $@;
78             $e =~ s/ at .*PhysicalValue.*//s;
79             croak $e;
80             }
81             }
82              
83             $u =~ s/\b$_->[1]\b/$_->[0]/sg for @AUTO_PLURALS;
84              
85             $this->[0] = Math::BigFloat->new($v);
86             $this->[1] = new Math::Units::PhysicalValue::AutoUnit $u;
87              
88             } else {
89             croak "value passed to PhysicalValue->new(\"$value\") was not understood";
90             }
91              
92             return $this;
93             }
94             # }}}
95             # deunit {{{
96             sub deunit {
97             my $this = shift;
98              
99             return $this->[0];
100             }
101             # }}}
102              
103             # pv_add {{{
104             sub pv_add {
105             my ($lhs, $rhs) = @_;
106            
107             $rhs = ref($lhs)->new($rhs eq "0" ? "0 $lhs->[1]" : $rhs) unless ref $rhs eq ref $lhs;
108              
109             my $v;
110             eval {
111             $v = convert(@$lhs, $rhs->[1]);
112             };
113              
114             if( $@ ) {
115             my $e = $@;
116             $e =~ s/'1'/''/;
117             $e =~ s/ at .*PhysicalValue.*//s;
118             croak $e;
119             }
120              
121             $v += $rhs->[0];
122              
123             return bless [ $v, $rhs->[1] ], ref $lhs;
124             }
125             # }}}
126             # pv_mul {{{
127             sub pv_mul {
128             my ($lhs, $rhs) = @_;
129              
130             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
131              
132             my ($v, $u) = (@$lhs);
133              
134             $v *= $rhs->[0];
135             $u *= $rhs->[1];
136              
137             return bless [ $v, $u ], ref $lhs;
138             }
139             # }}}
140             # pv_mulmul {{{
141             sub pv_mulmul {
142             my ($lhs, $rhs) = @_;
143              
144             croak "right hand side must be a scalar (ie no units)" if ref($rhs);
145              
146             my ($v, $u) = (@$lhs);
147              
148             $v = $v ** $rhs;
149             $u = $u ** $rhs;
150              
151             return bless [ $v, $u ], ref $lhs;
152             }
153             # }}}
154             # pv_sqrt {{{
155             sub pv_sqrt {
156             my ($lhs) = @_;
157              
158             my ($v, $u) = (@$lhs);
159              
160             $v = sqrt( $v );
161             $u = sqrt( $u );
162              
163             return bless [ $v, $u ], ref $lhs;
164             }
165             # }}}
166             # pv_div {{{
167             sub pv_div {
168             my ($lhs, $rhs, $assbackwards) = @_;
169              
170             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
171             return $rhs / $lhs if $assbackwards;
172              
173             my ($v, $u) = (@$lhs);
174              
175             $v /= $rhs->[0];
176             $u /= $rhs->[1];
177              
178             return bless [ $v, $u ], ref $lhs;
179             }
180             # }}}
181              
182             # pv_sub {{{
183             sub pv_sub {
184             my ($lhs, $rhs, $assbackwards) = @_;
185              
186             $rhs = ref($lhs)->new($rhs eq "0" ? "0 $lhs->[1]" : $rhs) unless ref $rhs eq ref $lhs;
187             return ($rhs - $lhs) if $assbackwards;
188              
189             return $lhs->pv_add( $rhs->pv_mul(-1) );
190             }
191             # }}}
192              
193             # pv_inc {{{
194             sub pv_inc {
195             my $this = shift;
196              
197             $this->[0] ++;
198            
199             return $this;
200             }
201             # }}}
202             # pv_dec {{{
203             sub pv_dec {
204             my $this = shift;
205              
206             $this->[0] --;
207            
208             return $this;
209             }
210             # }}}
211              
212             # pv_str_eq {{{
213             sub pv_str_eq {
214             my ($lhs, $rhs) = @_;
215              
216             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
217              
218             my $v;
219             eval {
220             $v = convert(@$rhs, $lhs->[1]);
221             };
222              
223             $rhs->[0] = $v;
224             $rhs->[1] = $lhs->[1];
225              
226             if( $@ ) {
227             my $e = $@;
228             $e =~ s/'1'/''/;
229             $e =~ s/ at .*PhysicalValue.*//s;
230             croak $e;
231             }
232              
233             return "$lhs" eq "$rhs";
234             }
235             # }}}
236             # pv_str_ne {{{
237             sub pv_str_ne {
238             my ($lhs, $rhs) = @_;
239              
240             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
241              
242             my $v;
243             eval {
244             $v = convert(@$rhs, $lhs->[1]);
245             };
246              
247             $rhs->[0] = $v;
248             $rhs->[1] = $lhs->[1];
249              
250             if( $@ ) {
251             my $e = $@;
252             $e =~ s/'1'/''/;
253             $e =~ s/ at .*PhysicalValue.*//s;
254             croak $e;
255             }
256              
257             return "$lhs" ne "$rhs";
258             }
259             # }}}
260             # pv_num_eq {{{
261             sub pv_num_eq {
262             my ($lhs, $rhs) = @_;
263              
264             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
265              
266             my $v;
267             eval {
268             $v = convert(@$rhs, $lhs->[1]);
269             };
270              
271             if( $@ ) {
272             my $e = $@;
273             $e =~ s/'1'/''/;
274             $e =~ s/ at .*PhysicalValue.*//s;
275             croak $e;
276             }
277              
278             return $lhs->[0] == $v;
279             }
280             # }}}
281             # pv_num_lt {{{
282             sub pv_num_lt {
283             my ($lhs, $rhs, $assbackwards) = @_;
284              
285             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
286             return $rhs < $lhs if $assbackwards;
287              
288             my $v;
289             eval {
290             $v = convert(@$rhs, $lhs->[1]);
291             };
292              
293             if( $@ ) {
294             my $e = $@;
295             $e =~ s/'1'/''/;
296             $e =~ s/ at .*PhysicalValue.*//s;
297             croak $e;
298             }
299              
300             return $lhs->[0] < $v;
301             }
302             # }}}
303             # pv_num_gt {{{
304             sub pv_num_gt {
305             my ($lhs, $rhs, $assbackwards) = @_;
306              
307             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
308             return $rhs > $lhs if $assbackwards;
309              
310             my $v;
311             eval {
312             $v = convert(@$rhs, $lhs->[1]);
313             };
314              
315             if( $@ ) {
316             my $e = $@;
317             $e =~ s/'1'/''/;
318             $e =~ s/ at .*PhysicalValue.*//s;
319             croak $e;
320             }
321              
322             return $lhs->[0] > $v;
323             }
324             # }}}
325             # pv_num_lte {{{
326             sub pv_num_lte {
327             my ($lhs, $rhs, $assbackwards) = @_;
328              
329             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
330             return $rhs <= $lhs if $assbackwards;
331              
332             my $v;
333             eval {
334             $v = convert(@$rhs, $lhs->[1]);
335             };
336              
337             if( $@ ) {
338             my $e = $@;
339             $e =~ s/'1'/''/;
340             $e =~ s/ at .*PhysicalValue.*//s;
341             croak $e;
342             }
343              
344             return $lhs->[0] <= $v;
345             }
346             # }}}
347             # pv_num_gte {{{
348             sub pv_num_gte {
349             my ($lhs, $rhs, $assbackwards) = @_;
350              
351             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
352             return $rhs >= $lhs if $assbackwards;
353              
354             my $v;
355             eval {
356             $v = convert(@$rhs, $lhs->[1]);
357             };
358              
359             if( $@ ) {
360             my $e = $@;
361             $e =~ s/'1'/''/;
362             $e =~ s/ at .*PhysicalValue.*//s;
363             croak $e;
364             }
365              
366             return $lhs->[0] >= $v;
367             }
368             # }}}
369              
370             # pv_print {{{
371             sub pv_print {
372             my $this = shift;
373             my ($v, $u) = @$this;
374              
375             if( $u->{unit} == 1 ) {
376             $u = "";
377             } else {
378             $u = " $u";
379             if( $v != 1 ) {
380             $u =~ s/\b$_->[0]\b/$_->[1]/sg for @AUTO_PLURALS;
381             }
382             }
383              
384             return $v . $u if $PrintPrecision < 0;
385              
386             # temprary fix until I hear back from the Number::Format guy
387              
388             # $v->bstr; returns a string number
389             # $v->bsstr; returns a string in scinoti
390             # we can maybe use sstr later?
391              
392             $v = $v->bstr;
393              
394             my $f = join('', $fmt->format_number( $v, $PrintPrecision ), $u);
395             if( $f =~ m/^\S*e/ ) {
396             $f = $v . $u;
397             $f =~ s/e\+(\d+)/e$1/g;
398             $f =~ s/^([\.\-\d]+)(?=e)/$fmt->format_number( $1, $PrintPrecision )/e if $PrintPrecision >= 0;
399             }
400             return $f;
401              
402             # original numbers
403              
404             =cut
405             return "$v $u" if $PrintPrecision < 0;
406             return join(" ", $fmt->format_number( $v, $PrintPrecision ), $u);
407             =cut
408              
409             }
410             # }}}
411             # pv_bool {{{
412             sub pv_bool {
413             my $this = shift;
414             my ($v, $u) = @$this;
415              
416             return $v;
417             }
418             # }}}
419             # pv_ncmp {{{
420             sub pv_ncmp {
421             my ($lhs, $rhs, $assbackwards) = @_;
422              
423             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
424             return $rhs <=> $lhs if $assbackwards;
425              
426             return -1 if $lhs < $rhs;
427             return 1 if $lhs > $rhs;
428             return 0;
429             }
430             # }}}
431             # pv_scmp {{{
432             sub pv_scmp {
433             my ($lhs, $rhs, $assbackwards) = @_;
434              
435             $rhs = ref($lhs)->new($rhs) unless ref $rhs eq ref $lhs;
436             return $rhs cmp $lhs if $assbackwards;
437              
438             return -1 if "$lhs" lt "$rhs";
439             return 1 if "$lhs" gt "$rhs";
440             return 0;
441             }
442             # }}}
443             # sci {{{
444             sub sci {
445             my $this = shift;
446             my $digits = shift;
447             my ($v, $u) = @$this;
448             my $e = 0;
449             $e = int( log($v) / log(10) ) unless $v == 0;
450              
451             if( $u->{unit} == 1 ) {
452             $u = "";
453             } else {
454             $u = " $u";
455             }
456              
457             croak "please use 0 or more sigfigs..." if $digits < 0;
458              
459             # $v->bstr; returns a string number
460             # $v->bsstr; returns a string in scinoti
461             # we can maybe use sstr later?
462              
463             $v /= (10 ** $e);
464             $v = $v->bstr;
465              
466             $v = $fmt->format_number($v, $digits-1) . "e$e";
467              
468             return $v . $u;
469             }
470             # }}}