line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Math::Units; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Copyright 1997, 1998 Ken Fox |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or modify |
6
|
|
|
|
|
|
|
# it under the terms of either: |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
# a) the GNU General Public License as published by the Free |
9
|
|
|
|
|
|
|
# Software Foundation; either version 1, or (at your option) any |
10
|
|
|
|
|
|
|
# later version, or |
11
|
|
|
|
|
|
|
# |
12
|
|
|
|
|
|
|
# b) the "Artistic License," the text of which is distributed with |
13
|
|
|
|
|
|
|
# Perl 5. If you need a copy of this license, please write to |
14
|
|
|
|
|
|
|
# me at <fox@vulpes.com> and I will be happy to send one. |
15
|
|
|
|
|
|
|
# |
16
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful, |
17
|
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
18
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either |
19
|
|
|
|
|
|
|
# the GNU General Public License or the Artistic License for more |
20
|
|
|
|
|
|
|
# details. |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 NAME |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
Math::Units - Unit conversion |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 SYNOPSIS |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
use Math::Units qw(convert); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my $out_value = convert($in_value, 'in unit', 'out unit'); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=head1 DESCRIPTION |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
The Math::Units module converts a numeric value in one unit of measurement |
35
|
|
|
|
|
|
|
to some other unit. The units must be compatible, i.e. length can not be |
36
|
|
|
|
|
|
|
converted to volume. If a conversion can not be made an exception is thrown. |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
A combination chaining and reduction algorithm is used to perform the most |
39
|
|
|
|
|
|
|
direct unit conversion possible. Units may be written in several different |
40
|
|
|
|
|
|
|
styles. An abbreviation table is used to convert from common long-form unit |
41
|
|
|
|
|
|
|
names to the (more or less) standard abbreviations that the units module uses |
42
|
|
|
|
|
|
|
internally. All multiplicative unit conversions are cached so that future |
43
|
|
|
|
|
|
|
conversions can be performed very quickly. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
Too many units, prefixes and abbreviations are supported to list here. See |
46
|
|
|
|
|
|
|
the source code for a complete listing. |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head1 TODO |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
I beleive this module has great potential, if you have any ideas or patches feel free to submit them to rt.cpan.org. |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
'units' program test like 'gunits' |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
other tests |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
POD about what units/abbr/etc can be used with the function |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
general cleanup |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
Mr. Fox's original TODO: |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
1. There should be a set of routines for adding new unit formulas, |
63
|
|
|
|
|
|
|
reductions and conversions. |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
2. Some conversions can be automatically generated from a reduction. (This |
66
|
|
|
|
|
|
|
has to be done carefully because conversions are bi-directional while |
67
|
|
|
|
|
|
|
reductions *must* be consistently uni-directional.) |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
3. It would be nice to simplify the default conversions using the |
70
|
|
|
|
|
|
|
yet-to-be-written solution to #2. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
4. There are many units (several in the GNU unit program for example) that |
73
|
|
|
|
|
|
|
aren't defined here. Since I was (un)fortunately born in the U.S., I |
74
|
|
|
|
|
|
|
have a(n) (in)correct belief of what the standard units are. Please let |
75
|
|
|
|
|
|
|
me know if I've messed anything up! |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head1 EXAMPLES |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
print "5 mm == ", convert(5, 'mm', 'in'), " inches\n"; |
80
|
|
|
|
|
|
|
print "72 degrees Farenheit == ", convert(72, 'F', 'C'), " degrees Celsius\n"; |
81
|
|
|
|
|
|
|
print "1 gallon == ", convert(1, 'gallon', 'cm^3'), " cubic centimeters\n"; |
82
|
|
|
|
|
|
|
print "4500 rpm == ", convert(4500, 'rpm', 'Hz'), " Hertz\n"; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=cut |
85
|
|
|
|
|
|
|
|
86
|
2
|
|
|
2
|
|
43932
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
85
|
|
87
|
2
|
|
|
2
|
|
9
|
use vars qw($VERSION @ISA @EXPORT_OK); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
207
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
$VERSION = 1.3; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
require Exporter; |
92
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
93
|
|
|
|
|
|
|
@EXPORT_OK = qw(convert print_conversion); |
94
|
|
|
|
|
|
|
|
95
|
2
|
|
|
2
|
|
10
|
use Carp; |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
8408
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# Prefixes are used to alter the magnitude of a unit. They |
98
|
|
|
|
|
|
|
# can *not* be chained together to form compound prefixes. |
99
|
|
|
|
|
|
|
# (For special cases of compound prefixes, you can enter an |
100
|
|
|
|
|
|
|
# abbreviation that rewrites the compound prefix to a single |
101
|
|
|
|
|
|
|
# prefix of the right magnitude.) |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
my %prefixes = ( |
104
|
|
|
|
|
|
|
'T' => 1e12, |
105
|
|
|
|
|
|
|
'G' => 1e9, |
106
|
|
|
|
|
|
|
'M' => 1e6, |
107
|
|
|
|
|
|
|
'k' => 1000, |
108
|
|
|
|
|
|
|
'h' => 100, |
109
|
|
|
|
|
|
|
'da' => 10, |
110
|
|
|
|
|
|
|
'd' => .1, |
111
|
|
|
|
|
|
|
'c' => .01, |
112
|
|
|
|
|
|
|
'm' => .001, |
113
|
|
|
|
|
|
|
'µ' => 1e-6, |
114
|
|
|
|
|
|
|
'n' => 1e-9, |
115
|
|
|
|
|
|
|
'dn' => 1e-10, |
116
|
|
|
|
|
|
|
'p' => 1e-12, |
117
|
|
|
|
|
|
|
'f' => 1e-15 |
118
|
|
|
|
|
|
|
); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Formulas and reductions are carefully chosen expressions that |
121
|
|
|
|
|
|
|
# define a unit in terms of other units (and constants). The |
122
|
|
|
|
|
|
|
# unit conversion algorithm always applies a formula definition, |
123
|
|
|
|
|
|
|
# but only uses a reduction as a last ditch effort to make the |
124
|
|
|
|
|
|
|
# conversion. The reason for this is that reductions can lead |
125
|
|
|
|
|
|
|
# to very long chains of unit conversions. However, in most |
126
|
|
|
|
|
|
|
# conversions a single factor can be used which will skip the |
127
|
|
|
|
|
|
|
# entire reduction process (and improve accuracy besides). |
128
|
|
|
|
|
|
|
# |
129
|
|
|
|
|
|
|
# Always express a unit in terms of more fundamental units. |
130
|
|
|
|
|
|
|
# Loops are not detected and will cause the conversion algorithm |
131
|
|
|
|
|
|
|
# to hang. (Adding units is intended to be easy, but not |
132
|
|
|
|
|
|
|
# trivial.) |
133
|
|
|
|
|
|
|
# |
134
|
|
|
|
|
|
|
# See below for conversion examples. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
my %formulas = ( |
137
|
|
|
|
|
|
|
'are' => '100 m^2', # as in hectare |
138
|
|
|
|
|
|
|
'l' => 'm^3/1000', # liter |
139
|
|
|
|
|
|
|
'tonne' => '1000 kg', # metric ton |
140
|
|
|
|
|
|
|
'N' => 'kg m/s^2', # newton |
141
|
|
|
|
|
|
|
'dyn' => 'cm gram/s^2', |
142
|
|
|
|
|
|
|
'Pa' => 'N/m^2', # pascal |
143
|
|
|
|
|
|
|
'bar' => '1e5 Pa', |
144
|
|
|
|
|
|
|
'barye' => 'dyne/cm^2', |
145
|
|
|
|
|
|
|
'kine' => 'cm/s', |
146
|
|
|
|
|
|
|
'bole' => 'g kine', |
147
|
|
|
|
|
|
|
'pond' => 'gram gee', |
148
|
|
|
|
|
|
|
'glug' => 'pond s^2/cm', |
149
|
|
|
|
|
|
|
'J' => 'N m', # joule |
150
|
|
|
|
|
|
|
'W' => 'J/s', # watt |
151
|
|
|
|
|
|
|
'gee' => '9.80665 m/s^2', # Earth gravity |
152
|
|
|
|
|
|
|
'atm' => '101325 Pa', # Earth atmosphere |
153
|
|
|
|
|
|
|
'Hg' => '13.5951 pond/cm^3', # mercury (used in: inches Hg) |
154
|
|
|
|
|
|
|
'water' => 'pond/cm^3', # water (used in: inches water) |
155
|
|
|
|
|
|
|
'mach' => '331.46 m/s', # speed of sound |
156
|
|
|
|
|
|
|
'coulomb' => 'A s', |
157
|
|
|
|
|
|
|
'V' => 'W/A', # volt |
158
|
|
|
|
|
|
|
'ohm' => 'V/A', |
159
|
|
|
|
|
|
|
'siemens' => 'A/V', |
160
|
|
|
|
|
|
|
'farad' => 'coulomb/V', |
161
|
|
|
|
|
|
|
'Wb' => 'V s', # weber |
162
|
|
|
|
|
|
|
'henry' => 'Wb/A', |
163
|
|
|
|
|
|
|
'tesla' => 'Wb/m^2', |
164
|
|
|
|
|
|
|
'Hz' => 'cycle/s', # hertz |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
'lbf' => 'lb gee', # pounds of force |
167
|
|
|
|
|
|
|
'tonf' => 'ton gee', # tons of force |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
'duty' => 'ft lbf', |
170
|
|
|
|
|
|
|
'celo' => 'ft/s^2', |
171
|
|
|
|
|
|
|
'jerk' => 'ft/s^3', |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
'slug' => 'lbf s^2/ft', |
174
|
|
|
|
|
|
|
'reyn' => 'psi sec', # viscosity |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
'psi' => 'lbf/in^2', # pounds per square inch |
177
|
|
|
|
|
|
|
'tsi' => 'tonf/in^2', # tons per square inch |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
'ouncedal' => 'oz ft/s^2', # force which accelerates an ounce at 1 ft/s^2 |
180
|
|
|
|
|
|
|
'poundal' => 'lb ft/s^2', # same thing for a pound |
181
|
|
|
|
|
|
|
'tondal' => 'ton ft/s^2', # and for a ton |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
'hp' => '550 ft lbf/s', # horse power |
184
|
|
|
|
|
|
|
'nauticalmile' => '1852 m', |
185
|
|
|
|
|
|
|
'mil' => '.001 in' |
186
|
|
|
|
|
|
|
); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# The base units are: |
189
|
|
|
|
|
|
|
# |
190
|
|
|
|
|
|
|
# m .............. meter (length) meter^2 (area) meter^3 (volume) |
191
|
|
|
|
|
|
|
# g .............. gram (mass) |
192
|
|
|
|
|
|
|
# s .............. second (time) |
193
|
|
|
|
|
|
|
# deg ............ degree (angular measure) |
194
|
|
|
|
|
|
|
# A .............. ampere (current) |
195
|
|
|
|
|
|
|
# C .............. degrees Celsius (temperature) |
196
|
|
|
|
|
|
|
# Cd ............. Celsius degrees (temperature change) |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
my %reductions = ( |
199
|
|
|
|
|
|
|
'in' => '0.0254 m', # inches |
200
|
|
|
|
|
|
|
'pnt' => 'in/72', # PostScript points |
201
|
|
|
|
|
|
|
'ft' => '12 in', # feet |
202
|
|
|
|
|
|
|
'yd' => '3 ft', # yards |
203
|
|
|
|
|
|
|
'mi' => '5280 ft', # miles |
204
|
|
|
|
|
|
|
'kip' => '1000 lbf', # kilo pounds |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
'barrel' => '42 gal', # barrels |
207
|
|
|
|
|
|
|
'gal' => '231 in^3', # gallons |
208
|
|
|
|
|
|
|
'qt' => 'gal/4', # quarts |
209
|
|
|
|
|
|
|
'pt' => 'qt/2', # pints |
210
|
|
|
|
|
|
|
'gill' => 'pt/4', # gills |
211
|
|
|
|
|
|
|
'floz' => 'pt/16', # fluid ounces |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
'Fd' => '1.8 Cd', # Farenheit degrees (change) |
214
|
|
|
|
|
|
|
'Kd' => 'Cd', # Kelvins (change) |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
'min' => '60 s', # minutes |
217
|
|
|
|
|
|
|
'hr' => '60 min', # hours |
218
|
|
|
|
|
|
|
'day' => '24 hr', # days |
219
|
|
|
|
|
|
|
'wk' => '7 day', # weeks |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
'lb' => '453.59237 g', # pounds |
222
|
|
|
|
|
|
|
'oz' => 'lb/16', # ounces |
223
|
|
|
|
|
|
|
'dr' => 'oz/16', # drams |
224
|
|
|
|
|
|
|
'gr' => 'lb/7000', # grains |
225
|
|
|
|
|
|
|
'ton' => '2000 lb', # tons |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
'cycle' => '360 deg', # complete revolution = 1 cycle |
228
|
|
|
|
|
|
|
'rad' => '180 deg/3.14159265358979323846', # radians |
229
|
|
|
|
|
|
|
'grad' => '9 deg/10', # gradians |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
'troypound' => '5760 gr', # troy pound |
232
|
|
|
|
|
|
|
'troyounce' => 'troypound/12', # troy ounce |
233
|
|
|
|
|
|
|
'pennyweight' => 'troyounce/20', # penny weight |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
'carat' => '0.2 gm' # carat |
236
|
|
|
|
|
|
|
); |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
# Abbreviations are simple text conversions that convert a pattern |
239
|
|
|
|
|
|
|
# expression (i.e. a Perl regular expression) into a different form. |
240
|
|
|
|
|
|
|
# Usually these convert from the long, spelled out form of a unit |
241
|
|
|
|
|
|
|
# to the unit's abbreviated form. Plural forms are also eliminated. |
242
|
|
|
|
|
|
|
# A few small bows to standard spoken units are also available. |
243
|
|
|
|
|
|
|
# |
244
|
|
|
|
|
|
|
# Examples: |
245
|
|
|
|
|
|
|
# |
246
|
|
|
|
|
|
|
# meters => m |
247
|
|
|
|
|
|
|
# kilometers => k-meters => k-m |
248
|
|
|
|
|
|
|
# grams/cc => grams/cm^3 => g/cm^3 |
249
|
|
|
|
|
|
|
# meters per second => m/s |
250
|
|
|
|
|
|
|
# cubic inches => cu-in |
251
|
|
|
|
|
|
|
# feet squared => ft^2 |
252
|
|
|
|
|
|
|
# hectares => h-are |
253
|
|
|
|
|
|
|
# |
254
|
|
|
|
|
|
|
# Abbreviation substitutions are applied IN THE GIVEN ORDER to the unit |
255
|
|
|
|
|
|
|
# until no more abbreviations match. As in the formula and |
256
|
|
|
|
|
|
|
# reduction expressions, be careful to avoid rewriting loops. Also, |
257
|
|
|
|
|
|
|
# be aware that longer abbreviations should appear first to avoid |
258
|
|
|
|
|
|
|
# the possibility of an unintended rewrite. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
my @abbreviations = ( |
261
|
|
|
|
|
|
|
'\bper\b' => '\/', |
262
|
|
|
|
|
|
|
'\bsq(uare)?\s+' => 'sq,', |
263
|
|
|
|
|
|
|
'\bcu(bic)?\s+' => 'cu,', |
264
|
|
|
|
|
|
|
'\s+squared\b' => '^2', |
265
|
|
|
|
|
|
|
'\s+cubed\b' => '^3', |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
'\bmicrons?\b' => 'µ,m', |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
'\bdecinano-?' => 'dn,', |
270
|
|
|
|
|
|
|
'\btera-?' => 'T,', |
271
|
|
|
|
|
|
|
'\bgiga-?' => 'G,', |
272
|
|
|
|
|
|
|
'\bmega-?' => 'M,', |
273
|
|
|
|
|
|
|
'\bkilo-?' => 'k,', |
274
|
|
|
|
|
|
|
'\bhecto-?' => 'h,', |
275
|
|
|
|
|
|
|
'\bdeka-?' => 'da,', |
276
|
|
|
|
|
|
|
'\bdeca-?' => 'da,', |
277
|
|
|
|
|
|
|
'\bdeci-?' => 'd,', |
278
|
|
|
|
|
|
|
'\bcenti-?' => 'c,', |
279
|
|
|
|
|
|
|
'\bmilli-?' => 'm,', |
280
|
|
|
|
|
|
|
'\bmicro-?' => 'µ,', |
281
|
|
|
|
|
|
|
'\bnano-?' => 'n,', |
282
|
|
|
|
|
|
|
'\bpico-?' => 'p,', |
283
|
|
|
|
|
|
|
'\bfemto-?' => 'f,', |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
'\bdn-' => 'dn,', |
286
|
|
|
|
|
|
|
'\bT-' => 'T,', |
287
|
|
|
|
|
|
|
'\bG-' => 'G,', |
288
|
|
|
|
|
|
|
'\bM-' => 'M,', |
289
|
|
|
|
|
|
|
'\bk-' => 'k,', |
290
|
|
|
|
|
|
|
'\bh-' => 'h,', |
291
|
|
|
|
|
|
|
'\bda-' => 'da,', |
292
|
|
|
|
|
|
|
'\bda-' => 'da,', |
293
|
|
|
|
|
|
|
'\bd-' => 'd,', |
294
|
|
|
|
|
|
|
'\bc-' => 'c,', |
295
|
|
|
|
|
|
|
'\bm-' => 'm,', |
296
|
|
|
|
|
|
|
'\bµ-' => 'µ,', |
297
|
|
|
|
|
|
|
'\bn-' => 'n,', |
298
|
|
|
|
|
|
|
'\bp-' => 'p,', |
299
|
|
|
|
|
|
|
'\bf-' => 'f,', |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
'\b[Rr][Pp][Mm]\b' => 'cycle\/min', |
302
|
|
|
|
|
|
|
'\bhz\b' => 'Hz', |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
'\b[Cc]elsius\b' => 'C', |
305
|
|
|
|
|
|
|
'\b[Ff]arenheit\b' => 'F', |
306
|
|
|
|
|
|
|
'\b[Kk]elvins?\b' => 'K', |
307
|
|
|
|
|
|
|
'\bdegs?\s+C\b' => 'C', |
308
|
|
|
|
|
|
|
'\bdegs?\s+F\b' => 'F', |
309
|
|
|
|
|
|
|
'\bC\s+change\b' => 'Cd', |
310
|
|
|
|
|
|
|
'\bF\s+change\b' => 'Fd', |
311
|
|
|
|
|
|
|
'\bK\s+change\b' => 'Kd', |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
'\bdegs\b' => 'deg', |
314
|
|
|
|
|
|
|
'\bdegrees?\b' => 'deg', |
315
|
|
|
|
|
|
|
'\brads\b' => 'rad', |
316
|
|
|
|
|
|
|
'\bradians?\b' => 'rad', |
317
|
|
|
|
|
|
|
'\bgrads\b' => 'grad', |
318
|
|
|
|
|
|
|
'\bgradians?\b' => 'grad', |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
'\bangstroms?\b' => 'dn,m', |
321
|
|
|
|
|
|
|
'\bcc\b' => 'cm^3', |
322
|
|
|
|
|
|
|
'\bhectares?\b' => 'h,are', |
323
|
|
|
|
|
|
|
'\bmils?\b' => 'm,in', |
324
|
|
|
|
|
|
|
'amperes?\b' => 'A', |
325
|
|
|
|
|
|
|
'amps?\b' => 'A', |
326
|
|
|
|
|
|
|
'days\b' => 'day', |
327
|
|
|
|
|
|
|
'drams?\b' => 'dr', |
328
|
|
|
|
|
|
|
'dynes?\b' => 'dyn', |
329
|
|
|
|
|
|
|
'feet\b' => 'ft', |
330
|
|
|
|
|
|
|
'foot\b' => 'ft', |
331
|
|
|
|
|
|
|
'gallons?\b' => 'gal', |
332
|
|
|
|
|
|
|
'gm\b' => 'g', |
333
|
|
|
|
|
|
|
'grams?\b' => 'g', |
334
|
|
|
|
|
|
|
'grains?\b' => 'gr', |
335
|
|
|
|
|
|
|
'hours?\b' => 'hr', |
336
|
|
|
|
|
|
|
'inch(es)?\b' => 'in', |
337
|
|
|
|
|
|
|
'joules?\b' => 'J', |
338
|
|
|
|
|
|
|
'lbs\b' => 'lb', |
339
|
|
|
|
|
|
|
'lbm\b' => 'lb', |
340
|
|
|
|
|
|
|
'liters?\b' => 'l', |
341
|
|
|
|
|
|
|
'meters?\b' => 'm', |
342
|
|
|
|
|
|
|
'miles?\b' => 'mi', |
343
|
|
|
|
|
|
|
'minutes?\b' => 'min', |
344
|
|
|
|
|
|
|
'newtons?\b' => 'N', |
345
|
|
|
|
|
|
|
'ounces?\b' => 'oz', |
346
|
|
|
|
|
|
|
'pascals?\b' => 'Pa', |
347
|
|
|
|
|
|
|
'pints?\b' => 'pt', |
348
|
|
|
|
|
|
|
'points?\b' => 'pnt', |
349
|
|
|
|
|
|
|
'pounds?\b' => 'lb', |
350
|
|
|
|
|
|
|
'quarts?\b' => 'qt', |
351
|
|
|
|
|
|
|
'seconds?\b' => 's', |
352
|
|
|
|
|
|
|
'secs?\b' => 's', |
353
|
|
|
|
|
|
|
'watts?\b' => 'W', |
354
|
|
|
|
|
|
|
'weeks?\b' => 'wk', |
355
|
|
|
|
|
|
|
'yards?\b' => 'yd' |
356
|
|
|
|
|
|
|
); |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# The conversion table *must* define unit conversion in terms |
359
|
|
|
|
|
|
|
# of the base units, not in terms of units with prefixes. This |
360
|
|
|
|
|
|
|
# table will be used to generate the initial conversion factors |
361
|
|
|
|
|
|
|
# used in simple unit to unit conversion. Inverse factors will |
362
|
|
|
|
|
|
|
# be automatically generated where possible. As new unit |
363
|
|
|
|
|
|
|
# conversion paths are discovered, the combined conversion |
364
|
|
|
|
|
|
|
# factors will be added to the table. No conversion factors |
365
|
|
|
|
|
|
|
# should be entered for units that are defined in the formula |
366
|
|
|
|
|
|
|
# table. (Many or all of the reductions will be redundantly |
367
|
|
|
|
|
|
|
# defined in the conversions table. The reductions table uses |
368
|
|
|
|
|
|
|
# a more general format which makes automatic conversion a |
369
|
|
|
|
|
|
|
# bit tricky.) |
370
|
|
|
|
|
|
|
# |
371
|
|
|
|
|
|
|
# The entire purpose of the conversion table is to allow a |
372
|
|
|
|
|
|
|
# more direct unit conversion path. The reduction algorithm |
373
|
|
|
|
|
|
|
# will always find a conversion (if one exists) but it may |
374
|
|
|
|
|
|
|
# use many more multiplies than if the conversion table is |
375
|
|
|
|
|
|
|
# used directly. |
376
|
|
|
|
|
|
|
# |
377
|
|
|
|
|
|
|
# Here is an example contrasting the two approaches. Given |
378
|
|
|
|
|
|
|
# the following base facts: |
379
|
|
|
|
|
|
|
# |
380
|
|
|
|
|
|
|
# reductions: in -> m, ft -> in, yd -> ft |
381
|
|
|
|
|
|
|
# conversions: in <-> m, ft <-> in, yd <-> ft |
382
|
|
|
|
|
|
|
# |
383
|
|
|
|
|
|
|
# convert feet to yards: |
384
|
|
|
|
|
|
|
# |
385
|
|
|
|
|
|
|
# by reduction: ft -> in -> m <- in <- ft <- yd |
386
|
|
|
|
|
|
|
# by conversion: ft -> yd |
387
|
|
|
|
|
|
|
# |
388
|
|
|
|
|
|
|
# This demonstrates that fewer intermediate multiplies are |
389
|
|
|
|
|
|
|
# performed in the direct conversion approach over the reduction |
390
|
|
|
|
|
|
|
# approach. However, the following problem can not be easily |
391
|
|
|
|
|
|
|
# solved in the direct conversion approach: |
392
|
|
|
|
|
|
|
# |
393
|
|
|
|
|
|
|
# convert square meters to inch * feet: |
394
|
|
|
|
|
|
|
# |
395
|
|
|
|
|
|
|
# by reduction: m^2 -> area <- m m <- m feet <- inch feet |
396
|
|
|
|
|
|
|
# by conversion: m^2 -> no match! |
397
|
|
|
|
|
|
|
# |
398
|
|
|
|
|
|
|
# Conversion can't solve this problem unless it first breaks up |
399
|
|
|
|
|
|
|
# square meters into meter * meter. Simple in this case, but very |
400
|
|
|
|
|
|
|
# hard to generalize. |
401
|
|
|
|
|
|
|
# |
402
|
|
|
|
|
|
|
# In summary, the direct conversion system uses fewer intermediate |
403
|
|
|
|
|
|
|
# conversions for better accuracy (and possibly performance but |
404
|
|
|
|
|
|
|
# that isn't really an issue). The reduction system is more |
405
|
|
|
|
|
|
|
# general in that it can solve conversion problems that the direct |
406
|
|
|
|
|
|
|
# conversion system can't. |
407
|
|
|
|
|
|
|
# |
408
|
|
|
|
|
|
|
# Examples: |
409
|
|
|
|
|
|
|
# |
410
|
|
|
|
|
|
|
# m -> in is solved by m -> in |
411
|
|
|
|
|
|
|
# in -> m is solved by in -> m (inverses are automatically generated) |
412
|
|
|
|
|
|
|
# qt -> ft^3 is solved by qt -> gal -> in^3 -> ft^3 |
413
|
|
|
|
|
|
|
# l -> ft^3 is solved by l -> m^3 -> in^3 -> ft^3 |
414
|
|
|
|
|
|
|
# K -> F is solved by K -> C -> F |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
my %conversions = ( |
417
|
|
|
|
|
|
|
'in,m' => 0.0254, |
418
|
|
|
|
|
|
|
'in,pnt' => 72, |
419
|
|
|
|
|
|
|
'ft,in' => 12, |
420
|
|
|
|
|
|
|
'yd,ft' => 3, |
421
|
|
|
|
|
|
|
'mi,ft' => 5280, |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
'barrel,gal' => 42, |
424
|
|
|
|
|
|
|
'gal,in^3' => 231, |
425
|
|
|
|
|
|
|
'gal,qt' => 4, |
426
|
|
|
|
|
|
|
'qt,pt' => 2, |
427
|
|
|
|
|
|
|
'pt,floz' => 16, |
428
|
|
|
|
|
|
|
'pt,gill' => 4, |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
'C,F' => sub { $_[0] * 1.8 + 32 }, |
431
|
|
|
|
|
|
|
'F,C' => sub { ( $_[0] - 32 ) / 1.8 }, |
432
|
|
|
|
|
|
|
'K,C' => sub { $_[0] - 273.15 }, |
433
|
|
|
|
|
|
|
'C,K' => sub { $_[0] + 273.15 }, |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
'Cd,Fd' => 1.8, |
436
|
|
|
|
|
|
|
'Kd,Cd' => 1, |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
'wk,day' => 7, |
439
|
|
|
|
|
|
|
'day,hr' => 24, |
440
|
|
|
|
|
|
|
'hr,min' => 60, |
441
|
|
|
|
|
|
|
'min,s' => 60, |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
'dollar,cent' => 100, |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
'lb,g' => 453.59237, |
446
|
|
|
|
|
|
|
'lb,oz' => 16, |
447
|
|
|
|
|
|
|
'lb,gr' => 7000, |
448
|
|
|
|
|
|
|
'oz,dr' => 16, |
449
|
|
|
|
|
|
|
'ton,lb' => 2000, |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
'cycle,deg' => 360, |
452
|
|
|
|
|
|
|
'rad,deg' => 180 / 3.14159265358979323846, |
453
|
|
|
|
|
|
|
'grad,deg' => 9 / 10, |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
'troypound,gr' => 5760, |
456
|
|
|
|
|
|
|
'troypound,troyounce' => 12, |
457
|
|
|
|
|
|
|
'troyounce,pennyweight' => 20, |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
'carat,gm' => .2 |
460
|
|
|
|
|
|
|
); |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
my $factors_computed = 0; # have the base conversion factors been computed? |
463
|
|
|
|
|
|
|
my %factor = (); # conversion factors for base units |
464
|
|
|
|
|
|
|
my %conversion_history = (); # history of conversion factors for raw unit strings |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
sub register_factor { |
467
|
45
|
|
|
45
|
0
|
63
|
my ( $u1, $u2, $f ) = @_; |
468
|
|
|
|
|
|
|
|
469
|
45
|
|
|
|
|
98
|
$factor{$u1}{$u2} = $f; |
470
|
45
|
100
|
|
|
|
200
|
$factor{$u2}{$u1} = 1 / $f if ( ref($f) ne "CODE" ); |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
sub print_unit($\%) { |
474
|
0
|
|
|
0
|
0
|
0
|
my ( $prefix, $u_group ) = @_; |
475
|
0
|
|
|
|
|
0
|
my ( $num_str, $den_str, $u, $dim ); |
476
|
|
|
|
|
|
|
|
477
|
0
|
|
|
|
|
0
|
$num_str = ""; |
478
|
0
|
|
|
|
|
0
|
$den_str = ""; |
479
|
|
|
|
|
|
|
|
480
|
0
|
|
|
|
|
0
|
while ( ( $u, $dim ) = each %{$u_group} ) { |
|
0
|
|
|
|
|
0
|
|
481
|
0
|
0
|
|
|
|
0
|
if ( $u eq "1" ) { $prefix *= $dim } |
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
482
|
0
|
|
|
|
|
0
|
elsif ( $dim > 1 ) { $num_str .= "$u^$dim " } |
483
|
0
|
|
|
|
|
0
|
elsif ( $dim == 1 ) { $num_str .= "$u " } |
484
|
0
|
|
|
|
|
0
|
elsif ( $dim == -1 ) { $den_str .= "$u " } |
485
|
0
|
|
|
|
|
0
|
elsif ( $dim < -1 ) { $den_str .= join( "", $u, "^", -$dim, " " ) } |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
|
488
|
0
|
0
|
|
|
|
0
|
$num_str .= "$prefix " if ( $prefix != 1 ); |
489
|
|
|
|
|
|
|
|
490
|
0
|
|
|
|
|
0
|
chop $num_str; |
491
|
0
|
|
|
|
|
0
|
chop $den_str; |
492
|
|
|
|
|
|
|
|
493
|
0
|
0
|
|
|
|
0
|
$num_str = "1" if ( !$num_str ); |
494
|
|
|
|
|
|
|
|
495
|
0
|
|
|
|
|
0
|
print $num_str; |
496
|
0
|
0
|
|
|
|
0
|
print "/", $den_str if ($den_str); |
497
|
0
|
|
|
|
|
0
|
print "\n"; |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
my $current_prefix; |
501
|
|
|
|
|
|
|
my %current_group; |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
sub merge_simple_unit { |
504
|
305
|
|
|
305
|
0
|
400
|
my ( $prefix, $u, $dim ) = @_; |
505
|
|
|
|
|
|
|
|
506
|
305
|
100
|
|
|
|
563
|
if ( $dim > 1 ) { $current_prefix *= $prefix**$dim } |
|
36
|
|
|
|
|
1176
|
|
507
|
305
|
100
|
|
|
|
586
|
if ( $dim == 1 ) { $current_prefix *= $prefix } |
|
184
|
100
|
|
|
|
249
|
|
|
|
100
|
|
|
|
|
|
508
|
49
|
|
|
|
|
75
|
elsif ( $dim == -1 ) { $current_prefix /= $prefix } |
509
|
36
|
|
|
|
|
63
|
elsif ( $dim < -1 ) { $current_prefix /= $prefix**-$dim } |
510
|
|
|
|
|
|
|
|
511
|
305
|
100
|
|
|
|
679
|
if ( $u ne "1" ) { |
512
|
266
|
100
|
|
|
|
585
|
if ( defined( $current_group{$u} ) ) { $current_group{$u} += $dim } |
|
19
|
|
|
|
|
26
|
|
513
|
247
|
|
|
|
|
378
|
else { $current_group{$u} = $dim } |
514
|
|
|
|
|
|
|
|
515
|
266
|
100
|
|
|
|
857
|
delete $current_group{$u} if ( $current_group{$u} == 0 ); |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
sub reduce_simple_unit { |
520
|
330
|
|
|
330
|
0
|
412
|
my ( $u, $dim, $apply_reductions ) = @_; |
521
|
330
|
|
|
|
|
273
|
my ($p); |
522
|
|
|
|
|
|
|
|
523
|
330
|
100
|
|
|
|
649
|
if ( defined( $formulas{$u} ) ) { |
524
|
45
|
|
|
|
|
93
|
reduce_unit( $formulas{$u}, $dim, $apply_reductions ); |
525
|
45
|
|
|
|
|
126
|
return; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
|
528
|
285
|
100
|
100
|
|
|
977
|
if ( $apply_reductions && defined( $reductions{$u} ) ) { |
|
|
100
|
|
|
|
|
|
529
|
16
|
|
|
|
|
27
|
reduce_unit( $reductions{$u}, $dim, $apply_reductions ); |
530
|
16
|
|
|
|
|
36
|
return; |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
elsif ( defined( $factor{$u} ) ) { |
533
|
236
|
|
|
|
|
403
|
merge_simple_unit( 1, $u, $dim ); |
534
|
236
|
|
|
|
|
597
|
return; |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
|
537
|
33
|
|
|
|
|
123
|
foreach $p ( keys %prefixes ) { |
538
|
370
|
100
|
|
|
|
3998
|
if ( $u =~ /^$p,?(.+)/ ) { |
539
|
35
|
100
|
|
|
|
115
|
if ( defined( $formulas{$1} ) ) { |
540
|
3
|
|
|
|
|
11
|
merge_simple_unit( $prefixes{$p}, "1", $dim ); |
541
|
3
|
|
|
|
|
8
|
reduce_unit( $formulas{$1}, $dim, $apply_reductions ); |
542
|
3
|
|
|
|
|
13
|
return; |
543
|
|
|
|
|
|
|
} |
544
|
32
|
50
|
33
|
|
|
127
|
if ( $apply_reductions && defined( $reductions{$1} ) ) { |
|
|
100
|
|
|
|
|
|
545
|
0
|
|
|
|
|
0
|
merge_simple_unit( $prefixes{$p}, "1", $dim ); |
546
|
0
|
|
|
|
|
0
|
reduce_unit( $reductions{$1}, $dim, $apply_reductions ); |
547
|
0
|
|
|
|
|
0
|
return; |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
elsif ( defined( $factor{$1} ) ) { |
550
|
30
|
|
|
|
|
94
|
merge_simple_unit( $prefixes{$p}, $1, $dim ); |
551
|
30
|
|
|
|
|
144
|
return; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
|
556
|
0
|
|
|
|
|
0
|
Carp::croak "unknown unit '$u' used"; |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
sub reduce_unit { |
560
|
228
|
|
|
228
|
0
|
270
|
my ( $u_group, $dim, $apply_reductions ) = @_; |
561
|
228
|
|
|
|
|
234
|
my ($u); |
562
|
|
|
|
|
|
|
|
563
|
228
|
|
|
|
|
218
|
foreach $u ( keys %{$u_group} ) { |
|
228
|
|
|
|
|
577
|
|
564
|
366
|
100
|
|
|
|
574
|
if ( $u eq "1" ) { |
565
|
36
|
|
|
|
|
73
|
merge_simple_unit( $u_group->{$u}, $u, $dim ); |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
else { |
568
|
330
|
|
|
|
|
763
|
reduce_simple_unit( $u, $dim * $u_group->{$u}, $apply_reductions ); |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
sub canonicalize_unit_list (\@$$) { |
574
|
300
|
|
|
300
|
0
|
378
|
my ( $units, $u_group, $denomenator ) = @_; |
575
|
300
|
|
|
|
|
262
|
my ( $u, $dim ); |
576
|
|
|
|
|
|
|
|
577
|
300
|
|
|
|
|
271
|
foreach $u ( @{$units} ) { |
|
300
|
|
|
|
|
534
|
|
578
|
369
|
100
|
|
|
|
633
|
next if ( !$u ); |
579
|
|
|
|
|
|
|
|
580
|
367
|
100
|
|
|
|
1113
|
if ( $u =~ s/\^(.+)$// ) { # unit of higher dimension, e.g. "cm^3" |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
581
|
57
|
|
|
|
|
99
|
$dim = $1; |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
elsif ( $u =~ /^sq,(.+)/ ) { # square unit, e.g. "sq-in" |
584
|
0
|
|
|
|
|
0
|
$u = $1; |
585
|
0
|
|
|
|
|
0
|
$dim = 2; |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
elsif ( $u =~ /^cu,(.+)/ ) { # cubic unit, e.g. "cu-in" |
588
|
2
|
|
|
|
|
6
|
$u = $1; |
589
|
2
|
|
|
|
|
3
|
$dim = 3; |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
else { |
592
|
308
|
|
|
|
|
575
|
$dim = 1; |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
367
|
100
|
|
|
|
635
|
$dim = -$dim if ($denomenator); |
596
|
|
|
|
|
|
|
|
597
|
367
|
100
|
|
|
|
1176
|
if ( $u =~ /^-?\d+(?:\.\d+)?(?:e-?\d+)?$/ ) { |
598
|
44
|
100
|
|
|
|
100
|
if ( $dim == 1 ) { $dim = $u } |
|
30
|
100
|
|
|
|
35
|
|
599
|
13
|
|
|
|
|
25
|
elsif ( $dim == -1 ) { $dim = 1 / $u } |
600
|
1
|
|
|
|
|
3
|
else { $dim = $u**$dim } |
601
|
44
|
|
|
|
|
56
|
$u = "1"; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
|
604
|
367
|
100
|
|
|
|
676
|
if ( defined( $u_group->{$u} ) ) { |
605
|
11
|
100
|
|
|
|
23
|
if ( $u eq "1" ) { $u_group->{$u} *= $dim } |
|
2
|
|
|
|
|
10
|
|
606
|
9
|
|
|
|
|
30
|
else { $u_group->{$u} += $dim } |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
else { |
609
|
356
|
|
|
|
|
1321
|
$u_group->{$u} = $dim; |
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
sub canonicalize_unit_string ($$) { |
615
|
225
|
|
|
225
|
0
|
290
|
my ( $units, $u_group ) = @_; |
616
|
225
|
|
|
|
|
230
|
my ( $num, $den, $u, @units ); |
617
|
|
|
|
|
|
|
|
618
|
225
|
|
|
|
|
6584
|
substitute_abbreviations( \$units ); |
619
|
225
|
|
|
|
|
438
|
$units =~ tr [*][ ]; |
620
|
225
|
|
|
|
|
485
|
$units =~ s/\s*\^\s*/\^/g; |
621
|
225
|
|
|
|
|
284
|
$units =~ s/-\s*(\D)/ $1/g; |
622
|
|
|
|
|
|
|
|
623
|
225
|
100
|
|
|
|
601
|
if ( $units =~ m|^([^/]*)/(.*)| ) { |
624
|
75
|
|
|
|
|
147
|
$num = $1; |
625
|
75
|
|
|
|
|
110
|
$den = $2; |
626
|
75
|
|
|
|
|
81
|
$den =~ tr [/][ ]; |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
else { |
629
|
150
|
|
|
|
|
215
|
$num = $units; |
630
|
150
|
|
|
|
|
176
|
$den = ""; |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
|
633
|
225
|
|
|
|
|
616
|
@units = split( /\s+/, $num ); |
634
|
225
|
50
|
|
|
|
461
|
if ( scalar @units ) { |
635
|
225
|
|
|
|
|
494
|
canonicalize_unit_list( @units, $u_group, 0 ); |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
|
638
|
225
|
|
|
|
|
570
|
@units = split( /\s+/, $den ); |
639
|
225
|
100
|
|
|
|
462
|
if ( scalar @units ) { |
640
|
75
|
|
|
|
|
135
|
canonicalize_unit_list( @units, $u_group, 1 ); |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
|
643
|
225
|
|
|
|
|
495
|
$u_group; |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
sub reduce_toplevel_unit ($\%) { |
647
|
152
|
|
|
152
|
0
|
212
|
my ( $units, $u_group ) = @_; |
648
|
|
|
|
|
|
|
|
649
|
152
|
|
|
|
|
270
|
canonicalize_unit_string( $units, $u_group ); |
650
|
|
|
|
|
|
|
|
651
|
152
|
|
|
|
|
165
|
$current_prefix = 1; |
652
|
152
|
|
|
|
|
275
|
%current_group = (); |
653
|
|
|
|
|
|
|
|
654
|
152
|
|
|
|
|
278
|
reduce_unit( $u_group, 1, 0 ); |
655
|
|
|
|
|
|
|
|
656
|
152
|
|
|
|
|
313
|
%{$u_group} = %current_group; |
|
152
|
|
|
|
|
460
|
|
657
|
|
|
|
|
|
|
|
658
|
152
|
|
|
|
|
297
|
$current_prefix; |
659
|
|
|
|
|
|
|
} |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
sub finish_reducing_toplevel_unit (\%) { |
662
|
12
|
|
|
12
|
0
|
17
|
my ($u_group) = @_; |
663
|
|
|
|
|
|
|
|
664
|
12
|
|
|
|
|
11
|
$current_prefix = 1; |
665
|
12
|
|
|
|
|
19
|
%current_group = (); |
666
|
|
|
|
|
|
|
|
667
|
12
|
|
|
|
|
18
|
reduce_unit( $u_group, 1, 1 ); |
668
|
|
|
|
|
|
|
|
669
|
12
|
|
|
|
|
22
|
%{$u_group} = %current_group; |
|
12
|
|
|
|
|
27
|
|
670
|
|
|
|
|
|
|
|
671
|
12
|
|
|
|
|
22
|
$current_prefix; |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
sub get_factor { |
675
|
200
|
|
|
200
|
0
|
275
|
my ( $u1, $u2 ) = @_; |
676
|
|
|
|
|
|
|
|
677
|
200
|
100
|
|
|
|
539
|
( $u1 eq $u2 ) ? 1 : $factor{$u1}{$u2}; |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
my $combined_f; |
681
|
|
|
|
|
|
|
my $combined_f_useless; |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
sub attempt_direct_conversion { |
684
|
165
|
|
|
165
|
0
|
232
|
my ( $value, $u1, $u1_dim, $u2, $u2_dim ) = @_; |
685
|
165
|
|
|
|
|
159
|
my ($f); |
686
|
|
|
|
|
|
|
|
687
|
165
|
100
|
|
|
|
283
|
if ( $u1_dim != $u2_dim ) { |
688
|
48
|
100
|
|
|
|
119
|
$u1 = "$u1^$u1_dim" if ( $u1_dim != 1 ); |
689
|
48
|
100
|
|
|
|
106
|
$u2 = "$u2^$u2_dim" if ( $u2_dim != 1 ); |
690
|
48
|
|
|
|
|
57
|
$u1_dim = 1; |
691
|
|
|
|
|
|
|
} |
692
|
|
|
|
|
|
|
|
693
|
165
|
100
|
|
|
|
243
|
if ( $u1_dim < 0 ) { |
694
|
35
|
|
|
|
|
36
|
$u1_dim = -$u1_dim; |
695
|
35
|
|
|
|
|
53
|
$f = get_factor( $u2, $u1 ); |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
else { |
698
|
130
|
|
|
|
|
215
|
$f = get_factor( $u1, $u2 ); |
699
|
|
|
|
|
|
|
} |
700
|
|
|
|
|
|
|
|
701
|
165
|
100
|
|
|
|
361
|
if ( defined($f) ) { |
702
|
100
|
100
|
|
|
|
287
|
if ( ref($f) eq "CODE" ) { |
|
|
100
|
|
|
|
|
|
703
|
18
|
|
|
|
|
37
|
$value = &$f( $value, $u1_dim ); |
704
|
18
|
|
|
|
|
30
|
$combined_f_useless = 1; |
705
|
|
|
|
|
|
|
} |
706
|
|
|
|
|
|
|
elsif ( $f != 1 ) { |
707
|
38
|
100
|
|
|
|
88
|
$f = $f**$u1_dim if ( $u1_dim > 1 ); # $u1_dim is non-negative |
708
|
38
|
|
|
|
|
36
|
$value *= $f; |
709
|
38
|
|
|
|
|
45
|
$combined_f *= $f; |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
|
712
|
100
|
|
|
|
|
195
|
return $value; |
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
|
715
|
65
|
|
|
|
|
86
|
undef; |
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
my %tmp_u_history; |
719
|
|
|
|
|
|
|
my @tmp_u_path; |
720
|
|
|
|
|
|
|
my @tmp_dim_path; |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
my $tmp_value; |
723
|
|
|
|
|
|
|
my $tmp_uX; |
724
|
|
|
|
|
|
|
my $tmp_uX_dim; |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
sub apply_factor_chain { |
727
|
14
|
|
|
14
|
0
|
18
|
my $chained_f = 1.0; |
728
|
14
|
|
|
|
|
15
|
my $chained_f_useless = 0; |
729
|
|
|
|
|
|
|
|
730
|
14
|
|
|
|
|
19
|
push @tmp_u_path, $tmp_uX; |
731
|
14
|
|
|
|
|
22
|
my $final = scalar(@tmp_u_path) - 1; |
732
|
14
|
|
|
|
|
20
|
my $original_value = $tmp_value; |
733
|
|
|
|
|
|
|
|
734
|
14
|
|
|
|
|
18
|
my ( $i, $f, $dim ); |
735
|
|
|
|
|
|
|
|
736
|
14
|
|
|
|
|
36
|
for ( $i = 0; $i < $final; ++$i ) { |
737
|
35
|
|
|
|
|
42
|
$dim = $tmp_dim_path[$i]; |
738
|
|
|
|
|
|
|
|
739
|
35
|
|
|
|
|
69
|
$f = get_factor( $tmp_u_path[$i], $tmp_u_path[ $i + 1 ] ); |
740
|
|
|
|
|
|
|
|
741
|
35
|
100
|
|
|
|
71
|
if ( defined($f) ) { |
742
|
31
|
100
|
|
|
|
87
|
if ( ref($f) eq "CODE" ) { |
|
|
50
|
|
|
|
|
|
743
|
6
|
50
|
|
|
|
14
|
if ( $dim < 0 ) { |
744
|
0
|
|
|
|
|
0
|
$dim = -$dim; |
745
|
0
|
|
|
|
|
0
|
$f = get_factor( $tmp_u_path[ $i + 1 ], $tmp_u_path[$i] ); |
746
|
|
|
|
|
|
|
} |
747
|
6
|
|
|
|
|
26
|
$tmp_value = &$f( $tmp_value, $dim ); |
748
|
6
|
|
|
|
|
15
|
$chained_f_useless = 1; |
749
|
|
|
|
|
|
|
} |
750
|
|
|
|
|
|
|
elsif ( $f != 1 ) { |
751
|
25
|
100
|
|
|
|
72
|
$f = $f**$dim if ( $dim != 1 ); # $dim can be either negative or positive |
752
|
25
|
|
|
|
|
26
|
$tmp_value *= $f; |
753
|
25
|
|
|
|
|
61
|
$chained_f *= $f; |
754
|
|
|
|
|
|
|
} |
755
|
|
|
|
|
|
|
} |
756
|
|
|
|
|
|
|
} |
757
|
|
|
|
|
|
|
|
758
|
14
|
100
|
|
|
|
25
|
if ($chained_f_useless) { |
759
|
3
|
|
|
|
|
6
|
$combined_f_useless = 1; |
760
|
|
|
|
|
|
|
} |
761
|
|
|
|
|
|
|
else { |
762
|
11
|
|
|
|
|
16
|
my $u1 = $tmp_u_path[0]; |
763
|
11
|
50
|
33
|
|
|
65
|
if ( exists( $factor{$u1} ) && exists( $factor{$tmp_uX} ) ) { |
764
|
11
|
|
|
|
|
17
|
my $u1_dim = $tmp_dim_path[0]; |
765
|
|
|
|
|
|
|
|
766
|
11
|
100
|
|
|
|
28
|
$u1 = "$u1^$u1_dim" if ( $u1_dim != 1 ); |
767
|
11
|
100
|
|
|
|
22
|
$tmp_uX = "$tmp_uX^$tmp_uX_dim" if ( $tmp_uX_dim != 1 ); |
768
|
|
|
|
|
|
|
|
769
|
11
|
|
|
|
|
27
|
register_factor( $u1, $tmp_uX, $chained_f ); |
770
|
11
|
|
|
|
|
17
|
$combined_f *= $chained_f; |
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
} |
773
|
|
|
|
|
|
|
|
774
|
14
|
|
|
|
|
81
|
die "OK\n"; |
775
|
|
|
|
|
|
|
} |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
sub breadth_first_factor_search { |
778
|
236
|
|
|
236
|
0
|
296
|
my ( $level, $u, $dim ) = @_; |
779
|
236
|
|
|
|
|
214
|
my $attempts = 0; |
780
|
|
|
|
|
|
|
|
781
|
331
|
|
|
|
|
495
|
SEARCH: |
782
|
|
|
|
|
|
|
{ |
783
|
236
|
|
|
|
|
213
|
$tmp_u_history{$u} = 1; |
784
|
|
|
|
|
|
|
|
785
|
331
|
|
|
|
|
308
|
++$attempts; |
786
|
|
|
|
|
|
|
|
787
|
331
|
|
|
|
|
381
|
push @tmp_u_path, $u; |
788
|
331
|
|
|
|
|
357
|
push @tmp_dim_path, $dim; |
789
|
|
|
|
|
|
|
|
790
|
331
|
100
|
|
|
|
485
|
if ( $level == 0 ) { |
791
|
188
|
100
|
100
|
|
|
555
|
if ( $dim == $tmp_uX_dim && defined( $factor{$u}{$tmp_uX} ) ) { |
792
|
14
|
|
|
|
|
29
|
apply_factor_chain(); |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
else { |
796
|
143
|
|
|
|
|
151
|
my $child; |
797
|
143
|
|
|
|
|
126
|
foreach $child ( keys %{ $factor{$u} } ) { |
|
143
|
|
|
|
|
385
|
|
798
|
291
|
100
|
|
|
|
572
|
if ( !defined( $tmp_u_history{$child} ) ) { |
799
|
165
|
|
|
|
|
293
|
breadth_first_factor_search( $level - 1, $child, $dim ); |
800
|
|
|
|
|
|
|
} |
801
|
|
|
|
|
|
|
} |
802
|
|
|
|
|
|
|
} |
803
|
|
|
|
|
|
|
|
804
|
300
|
100
|
|
|
|
561
|
if ( $attempts < 2 ) { |
805
|
209
|
100
|
|
|
|
263
|
if ( $dim == 1 ) { |
806
|
115
|
100
|
|
|
|
229
|
if ( $u =~ /^([^^]+)\^(.+)/ ) { |
807
|
1
|
|
|
|
|
4
|
$u = $1; |
808
|
1
|
|
|
|
|
2
|
$dim = $2; |
809
|
|
|
|
|
|
|
|
810
|
1
|
50
|
|
|
|
5
|
redo SEARCH if ( !defined( $tmp_u_history{$u} ) ); |
811
|
|
|
|
|
|
|
} |
812
|
|
|
|
|
|
|
} |
813
|
|
|
|
|
|
|
else { |
814
|
94
|
|
|
|
|
157
|
$u = "$u^$dim"; |
815
|
94
|
|
|
|
|
107
|
$dim = 1; |
816
|
|
|
|
|
|
|
|
817
|
94
|
50
|
|
|
|
208
|
redo SEARCH if ( !defined( $tmp_u_history{$u} ) ); |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
} |
820
|
|
|
|
|
|
|
} |
821
|
|
|
|
|
|
|
|
822
|
205
|
|
|
|
|
381
|
while ( $attempts-- > 0 ) { |
823
|
296
|
|
|
|
|
262
|
pop @tmp_u_path; |
824
|
296
|
|
|
|
|
806
|
pop @tmp_dim_path; |
825
|
|
|
|
|
|
|
} |
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
sub attempt_indirect_conversion { |
829
|
24
|
|
|
24
|
0
|
39
|
my ( $input_value, $u1, $u1_dim, $uX, $uX_dim ) = @_; |
830
|
|
|
|
|
|
|
|
831
|
24
|
|
|
|
|
24
|
$tmp_value = $input_value; |
832
|
24
|
|
|
|
|
32
|
$tmp_uX = $uX; |
833
|
24
|
|
|
|
|
28
|
$tmp_uX_dim = $uX_dim; |
834
|
|
|
|
|
|
|
|
835
|
24
|
|
|
|
|
25
|
eval { |
836
|
24
|
|
|
|
|
23
|
my $level; |
837
|
24
|
|
|
|
|
67
|
for ( $level = 0; $level < 4; ++$level ) { |
838
|
71
|
|
|
|
|
125
|
%tmp_u_history = (); |
839
|
71
|
|
|
|
|
94
|
@tmp_u_path = (); |
840
|
71
|
|
|
|
|
79
|
@tmp_dim_path = (); |
841
|
|
|
|
|
|
|
|
842
|
71
|
|
|
|
|
126
|
breadth_first_factor_search( $level, $u1, $u1_dim ); |
843
|
|
|
|
|
|
|
} |
844
|
|
|
|
|
|
|
}; |
845
|
|
|
|
|
|
|
|
846
|
24
|
100
|
|
|
|
72
|
return undef if ( $@ ne "OK\n" ); |
847
|
|
|
|
|
|
|
|
848
|
14
|
|
|
|
|
23
|
return $tmp_value; |
849
|
|
|
|
|
|
|
} |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
sub perform_unit_conversion ($\%\%) { |
852
|
82
|
|
|
82
|
0
|
101
|
my ( $value, $u1_group, $u2_group ) = @_; |
853
|
82
|
|
|
|
|
101
|
my ( $u1, $u1_dim ); |
854
|
0
|
|
|
|
|
0
|
my ( $u2, $u2_dim ); |
855
|
0
|
|
|
|
|
0
|
my ($new_value); |
856
|
|
|
|
|
|
|
|
857
|
82
|
|
|
|
|
183
|
DIRECT_UNIT_CONVERSION: |
858
|
82
|
|
|
|
|
83
|
foreach $u1 ( keys %{$u1_group} ) { |
859
|
122
|
|
|
|
|
157
|
$u1_dim = $u1_group->{$u1}; |
860
|
|
|
|
|
|
|
|
861
|
122
|
|
|
|
|
109
|
foreach $u2 ( keys %{$u2_group} ) { |
|
122
|
|
|
|
|
206
|
|
862
|
165
|
|
|
|
|
194
|
$u2_dim = $u2_group->{$u2}; |
863
|
|
|
|
|
|
|
|
864
|
165
|
|
|
|
|
285
|
$new_value = attempt_direct_conversion( $value, $u1, $u1_dim, $u2, $u2_dim ); |
865
|
|
|
|
|
|
|
|
866
|
165
|
100
|
|
|
|
350
|
if ( defined($new_value) ) { |
867
|
100
|
|
|
|
|
104
|
$value = $new_value; |
868
|
100
|
|
|
|
|
149
|
delete $u1_group->{$u1}; |
869
|
100
|
|
|
|
|
110
|
delete $u2_group->{$u2}; |
870
|
100
|
|
|
|
|
250
|
next DIRECT_UNIT_CONVERSION; |
871
|
|
|
|
|
|
|
} |
872
|
|
|
|
|
|
|
} |
873
|
|
|
|
|
|
|
} |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
INDIRECT_UNIT_CONVERSION: |
876
|
82
|
|
|
|
|
131
|
foreach $u1 ( keys %{$u1_group} ) { |
|
82
|
|
|
|
|
183
|
|
877
|
22
|
|
|
|
|
28
|
$u1_dim = $u1_group->{$u1}; |
878
|
|
|
|
|
|
|
|
879
|
22
|
|
|
|
|
24
|
foreach $u2 ( keys %{$u2_group} ) { |
|
22
|
|
|
|
|
33
|
|
880
|
24
|
|
|
|
|
33
|
$u2_dim = $u2_group->{$u2}; |
881
|
|
|
|
|
|
|
|
882
|
24
|
|
|
|
|
61
|
$new_value = attempt_indirect_conversion( $value, $u1, $u1_dim, $u2, $u2_dim ); |
883
|
|
|
|
|
|
|
|
884
|
24
|
100
|
|
|
|
68
|
if ( defined($new_value) ) { |
885
|
14
|
|
|
|
|
21
|
$value = $new_value; |
886
|
14
|
|
|
|
|
28
|
delete $u1_group->{$u1}; |
887
|
14
|
|
|
|
|
15
|
delete $u2_group->{$u2}; |
888
|
14
|
|
|
|
|
43
|
next INDIRECT_UNIT_CONVERSION; |
889
|
|
|
|
|
|
|
} |
890
|
|
|
|
|
|
|
} |
891
|
|
|
|
|
|
|
} |
892
|
|
|
|
|
|
|
|
893
|
82
|
100
|
100
|
|
|
114
|
if ( scalar keys %{$u1_group} || scalar keys %{$u2_group} ) { |
|
82
|
|
|
|
|
233
|
|
|
77
|
|
|
|
|
159
|
|
894
|
6
|
|
|
|
|
7
|
$tmp_value = $value; |
895
|
6
|
|
|
|
|
26
|
die "REDUCE\n"; |
896
|
|
|
|
|
|
|
} |
897
|
|
|
|
|
|
|
|
898
|
76
|
|
|
|
|
175
|
$value; |
899
|
|
|
|
|
|
|
} |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
sub compute_base_factors { |
902
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
# register all of the direct unit-to-unit conversion factors |
904
|
|
|
|
|
|
|
|
905
|
1
|
|
|
1
|
0
|
2
|
my ( $pair, $f, $u1, $u2 ); |
906
|
1
|
|
|
|
|
8
|
while ( ( $pair, $f ) = each %conversions ) { |
907
|
34
|
|
|
|
|
65
|
( $u1, $u2 ) = split( /,/, $pair ); |
908
|
34
|
|
|
|
|
54
|
register_factor( $u1, $u2, $f ); |
909
|
|
|
|
|
|
|
} |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
# build a fast pattern substitution function by eval'ing a |
912
|
|
|
|
|
|
|
# subroutine generated by concatenating all the abbreviation |
913
|
|
|
|
|
|
|
# substitution commands together. |
914
|
|
|
|
|
|
|
|
915
|
1
|
|
|
|
|
3
|
my $code = "sub substitute_abbreviations { my(\$units) = \@_; SUBST: {\n"; |
916
|
1
|
|
|
|
|
3
|
my ( $pattern, $subst ); |
917
|
|
|
|
|
|
|
|
918
|
1
|
|
|
|
|
2
|
my $i = 0; |
919
|
1
|
|
|
|
|
4
|
while ( $i < scalar @abbreviations ) { |
920
|
88
|
|
|
|
|
100
|
$pattern = $abbreviations[ $i++ ]; |
921
|
88
|
|
|
|
|
103
|
$subst = $abbreviations[ $i++ ]; |
922
|
|
|
|
|
|
|
|
923
|
88
|
|
|
|
|
226
|
$code .= " redo SUBST if (\$\$units =~ s/$pattern/$subst/g);\n"; |
924
|
|
|
|
|
|
|
} |
925
|
|
|
|
|
|
|
|
926
|
1
|
|
|
|
|
2
|
$code .= "} }"; |
927
|
|
|
|
|
|
|
|
928
|
1
|
100
|
|
225
|
0
|
2172
|
eval $code; |
|
225
|
50
|
|
|
|
306
|
|
|
225
|
100
|
|
|
|
233
|
|
|
266
|
100
|
|
|
|
663
|
|
|
264
|
50
|
|
|
|
1007
|
|
|
264
|
100
|
|
|
|
566
|
|
|
262
|
50
|
|
|
|
569
|
|
|
261
|
50
|
|
|
|
470
|
|
|
261
|
50
|
|
|
|
454
|
|
|
256
|
50
|
|
|
|
455
|
|
|
256
|
50
|
|
|
|
501
|
|
|
256
|
50
|
|
|
|
455
|
|
|
256
|
50
|
|
|
|
439
|
|
|
256
|
50
|
|
|
|
542
|
|
|
256
|
50
|
|
|
|
4840
|
|
|
256
|
50
|
|
|
|
649
|
|
|
256
|
100
|
|
|
|
593
|
|
|
256
|
50
|
|
|
|
408
|
|
|
256
|
50
|
|
|
|
447
|
|
|
256
|
50
|
|
|
|
487
|
|
|
254
|
50
|
|
|
|
413
|
|
|
254
|
50
|
|
|
|
602
|
|
|
254
|
50
|
|
|
|
425
|
|
|
254
|
50
|
|
|
|
1078
|
|
|
254
|
50
|
|
|
|
415
|
|
|
254
|
50
|
|
|
|
428
|
|
|
254
|
50
|
|
|
|
458
|
|
|
254
|
50
|
|
|
|
415
|
|
|
254
|
50
|
|
|
|
397
|
|
|
254
|
50
|
|
|
|
538
|
|
|
254
|
50
|
|
|
|
612
|
|
|
254
|
100
|
|
|
|
503
|
|
|
254
|
50
|
|
|
|
417
|
|
|
254
|
50
|
|
|
|
621
|
|
|
254
|
50
|
|
|
|
782
|
|
|
253
|
50
|
|
|
|
446
|
|
|
253
|
100
|
|
|
|
636
|
|
|
253
|
50
|
|
|
|
469
|
|
|
253
|
50
|
|
|
|
399
|
|
|
253
|
50
|
|
|
|
619
|
|
|
250
|
50
|
|
|
|
616
|
|
|
250
|
50
|
|
|
|
394
|
|
|
250
|
50
|
|
|
|
454
|
|
|
250
|
50
|
|
|
|
426
|
|
|
250
|
50
|
|
|
|
415
|
|
|
250
|
50
|
|
|
|
395
|
|
|
250
|
50
|
|
|
|
409
|
|
|
250
|
50
|
|
|
|
469
|
|
|
250
|
50
|
|
|
|
609
|
|
|
250
|
50
|
|
|
|
461
|
|
|
250
|
50
|
|
|
|
468
|
|
|
250
|
50
|
|
|
|
425
|
|
|
250
|
100
|
|
|
|
411
|
|
|
250
|
100
|
|
|
|
415
|
|
|
250
|
100
|
|
|
|
430
|
|
|
250
|
50
|
|
|
|
441
|
|
|
248
|
50
|
|
|
|
484
|
|
|
246
|
50
|
|
|
|
401
|
|
|
245
|
50
|
|
|
|
409
|
|
|
245
|
50
|
|
|
|
408
|
|
|
245
|
100
|
|
|
|
450
|
|
|
245
|
100
|
|
|
|
463
|
|
|
245
|
50
|
|
|
|
404
|
|
|
245
|
50
|
|
|
|
420
|
|
|
244
|
100
|
|
|
|
393
|
|
|
243
|
100
|
|
|
|
396
|
|
|
243
|
50
|
|
|
|
591
|
|
|
243
|
50
|
|
|
|
452
|
|
|
242
|
100
|
|
|
|
449
|
|
|
240
|
50
|
|
|
|
425
|
|
|
240
|
50
|
|
|
|
383
|
|
|
240
|
50
|
|
|
|
440
|
|
|
237
|
50
|
|
|
|
371
|
|
|
237
|
100
|
|
|
|
402
|
|
|
237
|
50
|
|
|
|
466
|
|
|
237
|
50
|
|
|
|
398
|
|
|
237
|
50
|
|
|
|
419
|
|
|
236
|
100
|
|
|
|
389
|
|
|
236
|
50
|
|
|
|
350
|
|
|
236
|
50
|
|
|
|
389
|
|
|
236
|
50
|
|
|
|
372
|
|
|
235
|
100
|
|
|
|
374
|
|
|
235
|
50
|
|
|
|
394
|
|
|
235
|
100
|
|
|
|
407
|
|
|
235
|
100
|
|
|
|
384
|
|
|
234
|
50
|
|
|
|
368
|
|
|
234
|
50
|
|
|
|
370
|
|
|
232
|
50
|
|
|
|
413
|
|
|
225
|
|
|
|
|
386
|
|
|
225
|
|
|
|
|
541
|
|
|
225
|
|
|
|
|
714
|
|
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
# simplify all the formulas and reductions up front so that |
931
|
|
|
|
|
|
|
# multiple rewrite passes aren't required during unit expansion |
932
|
|
|
|
|
|
|
|
933
|
1
|
|
|
|
|
30
|
foreach $u1 ( keys %formulas ) { |
934
|
43
|
|
|
|
|
101
|
$formulas{$u1} = canonicalize_unit_string( $formulas{$u1}, {} ); |
935
|
|
|
|
|
|
|
} |
936
|
|
|
|
|
|
|
|
937
|
1
|
|
|
|
|
13
|
foreach $u1 ( keys %reductions ) { |
938
|
30
|
|
|
|
|
62
|
$reductions{$u1} = canonicalize_unit_string( $reductions{$u1}, {} ); |
939
|
|
|
|
|
|
|
} |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
# mark this function completed because it only runs once |
942
|
|
|
|
|
|
|
|
943
|
1
|
|
|
|
|
6
|
$factors_computed = 1; |
944
|
|
|
|
|
|
|
} |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
sub print_conversion { |
947
|
0
|
|
|
0
|
0
|
0
|
my ( $value, $u1, $u2 ) = @_; |
948
|
0
|
|
|
|
|
0
|
my $my_result = Convert( $value, $u1, $u2 ); |
949
|
|
|
|
|
|
|
|
950
|
0
|
|
|
|
|
0
|
print "$value $u1 == $my_result $u2\n"; |
951
|
0
|
|
|
|
|
0
|
$my_result; |
952
|
|
|
|
|
|
|
} |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
sub convert { |
955
|
213
|
|
|
213
|
0
|
109671
|
my ( $value, $u1, $u2 ) = @_; |
956
|
213
|
|
|
|
|
261
|
my ( %u1_group, %u2_group ); |
957
|
0
|
|
|
|
|
0
|
my ( $u1_prefix, $u2_prefix ); |
958
|
0
|
|
|
|
|
0
|
my ($f); |
959
|
|
|
|
|
|
|
|
960
|
213
|
50
|
|
|
|
497
|
return ($value) if ( $u1 eq $u2 ); |
961
|
213
|
100
|
|
|
|
919
|
if ( defined( $f = $conversion_history{$u1}{$u2} ) ) { |
962
|
137
|
|
|
|
|
535
|
return ( $value * $f ); |
963
|
|
|
|
|
|
|
} |
964
|
|
|
|
|
|
|
|
965
|
76
|
100
|
|
|
|
254
|
if ( !$factors_computed ) { |
966
|
1
|
|
|
|
|
4
|
compute_base_factors(); |
967
|
|
|
|
|
|
|
} |
968
|
|
|
|
|
|
|
|
969
|
76
|
|
|
|
|
174
|
$u1_prefix = reduce_toplevel_unit( $u1, %u1_group ); |
970
|
76
|
|
|
|
|
143
|
$u2_prefix = reduce_toplevel_unit( $u2, %u2_group ); |
971
|
|
|
|
|
|
|
|
972
|
76
|
|
|
|
|
191
|
$combined_f = $u1_prefix / $u2_prefix; |
973
|
76
|
|
|
|
|
84
|
$combined_f_useless = 0; |
974
|
76
|
|
|
|
|
95
|
$value *= $combined_f; |
975
|
|
|
|
|
|
|
|
976
|
76
|
|
|
|
|
94
|
eval { $value = perform_unit_conversion( $value, %u1_group, %u2_group ); }; |
|
76
|
|
|
|
|
189
|
|
977
|
|
|
|
|
|
|
|
978
|
76
|
100
|
|
|
|
159
|
if ($@) { |
979
|
6
|
50
|
|
|
|
16
|
if ( $@ eq "REDUCE\n" ) { |
980
|
6
|
|
|
|
|
13
|
$u1_prefix = finish_reducing_toplevel_unit(%u1_group); |
981
|
6
|
|
|
|
|
12
|
$u2_prefix = finish_reducing_toplevel_unit(%u2_group); |
982
|
|
|
|
|
|
|
|
983
|
6
|
|
|
|
|
9
|
$f = $u1_prefix / $u2_prefix; |
984
|
|
|
|
|
|
|
|
985
|
6
|
50
|
|
|
|
13
|
if ( !$combined_f_useless ) { |
986
|
6
|
|
|
|
|
7
|
$combined_f *= $f; |
987
|
|
|
|
|
|
|
} |
988
|
|
|
|
|
|
|
|
989
|
6
|
|
|
|
|
8
|
$value = $tmp_value * $f; |
990
|
|
|
|
|
|
|
|
991
|
6
|
|
|
|
|
5
|
eval { $value = perform_unit_conversion( $value, %u1_group, %u2_group ); }; |
|
6
|
|
|
|
|
24
|
|
992
|
|
|
|
|
|
|
|
993
|
6
|
50
|
|
|
|
16
|
if ($@) { |
994
|
0
|
0
|
|
|
|
0
|
if ( $@ eq "REDUCE\n" ) { |
995
|
0
|
|
|
|
|
0
|
Carp::croak "conversion of unit '$u1' to '$u2' failed (incompatible units?)"; |
996
|
|
|
|
|
|
|
} |
997
|
|
|
|
|
|
|
else { |
998
|
0
|
|
|
|
|
0
|
Carp::croak $@; |
999
|
|
|
|
|
|
|
} |
1000
|
|
|
|
|
|
|
} |
1001
|
|
|
|
|
|
|
} |
1002
|
|
|
|
|
|
|
else { |
1003
|
0
|
|
|
|
|
0
|
Carp::croak "impossible! $@"; |
1004
|
|
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
} |
1006
|
|
|
|
|
|
|
|
1007
|
76
|
100
|
|
|
|
140
|
if ( !$combined_f_useless ) { |
1008
|
55
|
|
|
|
|
185
|
$conversion_history{$u1}{$u2} = $combined_f; |
1009
|
|
|
|
|
|
|
} |
1010
|
|
|
|
|
|
|
|
1011
|
76
|
|
|
|
|
395
|
$value; |
1012
|
|
|
|
|
|
|
} |
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
1; |