#!/usr/math/bin/perl -w # This is the "exported" subroutine. Use this to evaluate the units given in an answer. sub evaluate_units { &Units::evaluate_units; } # Methods for evaluating units in answers package Units; #require Exporter; #@ISA = qw(Exporter); #@EXPORT = qw(evaluate_units); # compound units are entered such as m/sec^2 or kg*m/sec^2 # the format is unit[^power]*unit^[*power].../ unit^power*unit^power.... # there can be only one / in a unit. # powers can be negative integers as well as positive integers. # These subroutines return a unit hash. # A unit hash has the entries # factor => number number can be any real number # m => power power is a signed integer # kg => power # s => power # rad => power # degC => power # degF => power # degK => power # perhaps other fundamental units will added later as well. my %fundamental_units = ('factor' => 1, 'm' => 0, 'kg' => 0, 's' => 0, 'rad' => 0, 'degC' => 0, 'degF' => 0, 'degK' => 0 ); # This hash contains all of the units which will be accepted. These must #be defined in terms of the # fundamental units given above. If the power of the fundamental unit is #not included it is assumed to # be zero. my $PI = 4*atan2(1,1); my %known_units = ('m' => { 'factor' => 1, 'm' => 1 }, 'kg' => { 'factor' => 1, 'kg' => 1 }, 's' => { 'factor' => 1, 's' => 1 }, 'rad' => { 'factor' => 1, 'rad' => 1 }, 'degC' => { 'factor' => 1, 'degC' => 1 }, 'degF' => { 'factor' => 1, 'degF' => 1 }, 'degK' => { 'factor' => 1, 'degK' => 1 }, # ANGLES # deg -- degrees # 'deg' => { 'factor' => 0.0174532925, 'rad' => 1 }, # TIME # s -- seconds # ms -- miliseconds # min -- minutes # hr -- hours # day -- days # yr -- years -- 365 days in a year # 'ms' => { 'factor' => 0.001, 's' => 1 }, 'min' => { 'factor' => 60, 's' => 1 }, 'hr' => { 'factor' => 3600, 's' => 1 }, 'day' => { 'factor' => 86400, 's' => 1 }, 'yr' => { 'factor' => 31557600, 's' => 1 }, # LENGTHS # m -- meters # cm -- centimeters # km -- kilometers # mm -- millimeters # micron -- micrometer # um -- micrometer # nm -- nanometer # A -- Angstrom # 'km' => { 'factor' => 1000, 'm' => 1 }, 'cm' => { 'factor' => 0.01, 'm' => 1 }, 'mm' => { 'factor' => 0.001, 'm' => 1 }, 'micron' => { 'factor' => 10**(-6), 'm' => 1 }, 'um' => { 'factor' => 10**(-6), 'm' => 1 }, 'nm' => { 'factor' => 10**(-9), 'm' => 1 }, 'A' => { 'factor' => 10**(-10), 'm' => 1 }, # ENGLISH LENGTHS # in -- inch # ft -- feet # mi -- mile # light-year # 'in' => { 'factor' => 0.0254, 'm' => 1 }, 'ft' => { 'factor' => 0.3048, 'm' => 1 }, 'mi' => { 'factor' => 1609.344, 'm' => 1 }, 'light-year' => { 'factor' => 9.46E15, 'm' => 1 }, # VOLUME # L -- liter # ml -- milliliters # cc -- cubic centermeters # 'L' => { 'factor' => 0.001, 'm' => 3 }, 'cc' => { 'factor' => 10**(-6), 'm' => 3, }, 'ml' => { 'factor' => 10**(-6), 'm' => 3, }, # VELOCITY # knots -- nautical miles per hour # 'knots' => { 'factor' => 0.5144444444, 'm' => 1, 's' => -1 }, # MASS # g -- grams # kg -- kilograms # 'g' => { 'factor' => 0.001, 'kg' => 1 }, # ENGLISH MASS # slug -- slug # 'slug' => { 'factor' => 14.6, 'kg' => 1 }, # FREQUENCY # Hz -- Hertz # kHz -- kilo Hertz # MHz -- mega Herta # 'Hz' => { 'factor' => 2*$PI, #2pi 's' => -1, 'rad' => 1 }, 'kHz' => { 'factor' => 1000*2*$PI, #1000*2pi, 's' => -1, 'rad' => 1 }, 'MHz' => { 'factor' => (10**6)*2*$PI, #10^6 * 2pi, 's' => -1, 'rad' => 1 }, 'rev' => { 'factor' => 2*$PI, 'rad' => 1 }, 'cycles' => { 'factor' => 2*$PI, 'rad' => 1 }, # COMPOUND UNITS # # FORCE # N -- Newton # microN -- micro Newton # uN -- micro Newton # dyne -- dyne # lb -- pound # ton -- ton # 'N' => { 'factor' => 1, 'm' => 1, 'kg' => 1, 's' => -2 }, 'microN' => { 'factor' => 10**(-6), 'm' => 1, 'kg' => 1, 's' => -2 }, 'uN' => { 'factor' => 10**(-6), 'm' => 1, 'kg' => 1, 's' => -2 }, 'dyne' => { 'factor' => 10**(-5), 'm' => 1, 'kg' => 1, 's' => -2 }, 'lb' => { 'factor' => 4.45, 'm' => 1, 'kg' => 1, 's' => -2 }, 'ton' => { 'factor' => 8900, 'm' => 1, 'kg' => 1, 's' => -2 }, # ENERGY # J -- Joule # kJ -- kilo Joule # erg -- erg # lbf -- foot pound # cal -- calorie # kcal -- kilocalorie # eV -- electron volt # kWh -- kilo Watt hour # 'J' => { 'factor' => 1, 'm' => 2, 'kg' => 1, 's' => -2 }, 'kJ' => { 'factor' => 1000, 'm' => 2, 'kg' => 1, 's' => -2 }, 'erg' => { 'factor' => 10**(-7), 'm' => 2, 'kg' => 1, 's' => -2 }, 'lbf' => { 'factor' => 1.355, 'm' => 2, 'kg' => 1, 's' => -2 }, 'cal' => { 'factor' => 4.19, 'm' => 2, 'kg' => 1, 's' => -2 }, 'kcal' => { 'factor' => 4190, 'm' => 2, 'kg' => 1, 's' => -2 }, 'eV' => { 'factor' => 1.60E-9, 'm' => 2, 'kg' => 1, 's' => -2 }, 'kWh' => { 'factor' => 3.6E6, 'm' => 2, 'kg' => 1, 's' => -2 }, # POWER # W -- Watt # kW -- kilo Watt # 'W' => { 'factor' => 1, 'm' => 2, 'kg' => 1, 's' => -3 }, 'kW' => { 'factor' => 1000, 'm' => 2, 'kg' => 1, 's' => -3 }, # PRESSURE # Pa -- Pascal # kPa -- kilo Pascal # atm -- atmosphere 'Pa' => { 'factor' => 1, 'm' => -1, 'kg' => 1, 's' => -2 }, 'kPa' => { 'factor' => 1000, 'm' => -1, 'kg' => 1, 's' => -2 }, 'atm' => { 'factor' => 1.01E5, 'm' => -1, 'kg' => 1, 's' => -2 }, ); sub process_unit { my $string = shift; die ("UNIT ERROR: No units were defined.") unless defined($string); # #split the string into numerator and denominator --- the separator is / my ($numerator,$denominator) = split( m{/}, $string ); $denominator = "" unless defined($denominator); my %numerator_hash = process_term($numerator); my %denominator_hash = process_term($denominator); my %unit_hash = %fundamental_units; my $u; foreach $u (keys %unit_hash) { if ( $u eq 'factor' ) { $unit_hash{$u} = $numerator_hash{$u}/$denominator_hash{$u}; # calculate the correction factor for the unit } else { $unit_hash{$u} = $numerator_hash{$u} - $denominator_hash{$u}; # calculate the power of the fundamental unit in the unit } } # return a unit hash. return(%unit_hash); } sub process_term { my $string = shift; my %unit_hash = %fundamental_units; if ($string) { #split the numerator or denominator into factors -- the separators are * my @factors = split(/\*/, $string); my $f; foreach $f (@factors) { my %factor_hash = process_factor($f); my $u; foreach $u (keys %unit_hash) { if ( $u eq 'factor' ) { $unit_hash{$u} = $unit_hash{$u} * $factor_hash{$u}; # calculate the correction factor for the unit } else { $unit_hash{$u} = $unit_hash{$u} + $factor_hash{$u}; # calculate the power of the fundamental unit in the unit } } } } #returns a unit hash. #print "process_term returns", %unit_hash, "\n"; return(%unit_hash); } sub process_factor { my $string = shift; #split the factor into unit and powers my ($unit_name,$power) = split(/\^/, $string); $power = 1 unless defined($power); my %unit_hash = %fundamental_units; if ( defined( $known_units{$unit_name} ) ) { my %unit_name_hash = %{$known_units{$unit_name}}; # $reference_units contains all of the known units. my $u; foreach $u (keys %unit_hash) { if ( $u eq 'factor' ) { $unit_hash{$u} = $unit_name_hash{$u}**$power; # calculate the correction factor for the unit } else { my $fundamental_unit = $unit_name_hash{$u}; $fundamental_unit = 0 unless defined($fundamental_unit); # a fundamental unit which doesn't appear in the unit need not be defined explicitly $unit_hash{$u} = $fundamental_unit*$power; # calculate the power of the fundamental unit in the unit } } } else { die "UNIT ERROR Unrecognizable unit: |$unit_name|"; } %unit_hash; } # This is the "exported" subroutine. Use this to evaluate the units given in an answer. sub evaluate_units { my $unit = shift; my %output = eval(q{process_unit( $unit)}); %output = %fundamental_units if $@; # this is what you get if there is an error. $output{'ERROR'}=$@ if $@; %output; } #################