package trick_units ; %units_info = ( "s" => ["seconds", "Standard", "Time"], "ps" => ["pico-seconds", "Standard", "Time"], "ns" => ["nano-seconds", "Standard", "Time"], "us" => ["micro-seconds", "Standard", "Time"], "ms" => ["milli-seconds", "Standard", "Time"], "cs" => ["centi-seconds", "Standard", "Time"], "ds" => ["deci-seconds", "Standard", "Time"], "das" => ["deca-seconds", "Standard", "Time"], "hs" => ["hecto-seconds", "Standard", "Time"], "ks" => ["kilo-seconds", "Standard", "Time"], "Ms" => ["mega-seconds", "Standard", "Time"], "Gs" => ["giga-seconds", "Standard", "Time"], "Ts" => ["tera-seconds", "Standard", "Time"], "min" => ["minutes", "Standard", "Time"], "hr" => ["hours", "Standard", "Time"], "day" => ["days", "Standard", "Time"], "m" => ["meters", "Metric", "Length"], "M" => ["meters", "Metric", "Length"], "pm" => ["pico-meters", "Metric", "Length"], "nm" => ["nano-meters", "Metric", "Length"], "um" => ["micro-meters", "Metric", "Length"], "mm" => ["milli-meters", "Metric", "Length"], "cm" => ["centi-meters", "Metric", "Length"], "dm" => ["deci-meters", "Metric", "Length"], "dam" => ["deca-meters", "Metric", "Length"], "hm" => ["hecto-meters", "Metric", "Length"], "km" => ["kilo-meters", "Metric", "Length"], "Mm" => ["mega-meters", "Metric", "Length"], "Gm" => ["giga-meters", "Metric", "Length"], "Tm" => ["tera-meters", "Metric", "Length"], "ft" => ["feet", "English", "Length"], "kft" => ["kilo-feet", "English", "Length"], "in" => ["inches", "English", "Length"], "yd" => ["yards", "English", "Length"], "mi" => ["miles", "English", "Length"], "n.m." => ["nautical-miles", "English", "Length"], "r" => ["radians", "Standard", "Angle"], "pr" => ["pico-radians", "Standard", "Angle"], "nr" => ["nano-radians", "Standard", "Angle"], "ur" => ["micro-radians", "Standard", "Angle"], "mr" => ["milli-radians", "Standard", "Angle"], "cr" => ["centi-radians", "Standard", "Angle"], "dr" => ["deci-radians", "Standard", "Angle"], "dar" => ["deca-radians", "Standard", "Angle"], "kr" => ["kilo-radians", "Standard", "Angle"], "Mr" => ["mega-radians", "Standard", "Angle"], "Gr" => ["giga-radians", "Standard", "Angle"], "Tr" => ["tera-radians", "Standard", "Angle"], "d" => ["degrees", "Standard", "Angle"], "as" => ["arc-seconds", "Standard", "Angle"], "am" => ["arc-minutes", "Standard", "Angle"], "rev" => ["revolutions", "Standard", "Angle"], "g" => ["grams", "Metric", "Mass"], "pg" => ["pico-grams", "Metric", "Mass"], "ng" => ["nano-grams", "Metric", "Mass"], "ug" => ["micro-grams", "Metric", "Mass"], "mg" => ["milli-grams", "Metric", "Mass"], "cg" => ["centi-grams", "Metric", "Mass"], "dg" => ["deci-grams", "Metric", "Mass"], "dag" => ["deca-grams", "Metric", "Mass"], "hg" => ["hecto-grams", "Metric", "Mass"], "kg" => ["kilo-grams", "Metric", "Mass"], "Mg" => ["mega-grams", "Metric", "Mass"], "Gg" => ["giga-grams", "Metric", "Mass"], "Tg" => ["tera-grams", "Metric", "Mass"], "mt" => ["metric-tons", "Metric", "Mass"], "sl" => ["slugs", "English", "Mass"], "lbm" => ["pound(mass)", "English", "Mass"], "N" => ["newtons", "Metric", "Force"], "pN" => ["pico-newtons", "Metric", "Force"], "nN" => ["nano-newtons", "Metric", "Force"], "uN" => ["micro-newtons", "Metric", "Force"], "mN" => ["milli-newtons", "Metric", "Force"], "cN" => ["centi-newtons", "Metric", "Force"], "dN" => ["deci-newtons", "Metric", "Force"], "daN" => ["deca-newtons", "Metric", "Force"], "hN" => ["hecto-newtons", "Metric", "Force"], "kN" => ["kilo-newtons", "Metric", "Force"], "MN" => ["mega-newtons", "Metric", "Force"], "GN" => ["giga-newtons", "Metric", "Force"], "TN" => ["tera-newtons", "Metric", "Force"], "oz" => ["ounzes", "English", "Force"], "lbf" => ["pound(force)", "English", "Force"], "v" => ["volts", "Standard", "Voltage"], "pv" => ["pico-volts", "Standard", "Voltage"], "nv" => ["nano-volts", "Standard", "Voltage"], "uv" => ["micro-volts", "Standard", "Voltage"], "mv" => ["milli-volts", "Standard", "Voltage"], "cv" => ["centi-volts", "Standard", "Voltage"], "dv" => ["deci-volts", "Standard", "Voltage"], "dav" => ["deca-volts", "Standard", "Voltage"], "hv" => ["hecto-volts", "Standard", "Voltage"], "kv" => ["kilo-volts", "Standard", "Voltage"], "Mv" => ["mega-volts", "Standard", "Voltage"], "Gv" => ["giga-volts", "Standard", "Voltage"], "Tv" => ["tera-volts", "Standard", "Voltage"], "amp" => ["amperes", "Standard", "Current"], "pamp" => ["pico-amperes", "Standard", "Current"], "namp" => ["nano-amperes", "Standard", "Current"], "uamp" => ["micro-amperes", "Standard", "Current"], "mamp" => ["milli-amperes", "Standard", "Current"], "camp" => ["centi-amperes", "Standard", "Current"], "damp" => ["deci-amperes", "Standard", "Current"], "daamp" => ["deca-amperes", "Standard", "Current"], "hamp" => ["hecto-amperes", "Standard", "Current"], "kamp" => ["kilo-amperes", "Standard", "Current"], "Mamp" => ["mega-amperes", "Standard", "Current"], "Gamp" => ["giga-amperes", "Standard", "Current"], "Tamp" => ["tera-amperes", "Standard", "Current"], "ohm" => ["ohms", "Standard", "Resistance"], "pohm" => ["pico-ohms", "Standard", "Resistance"], "nohm" => ["nano-ohms", "Standard", "Resistance"], "uohm" => ["micro-ohms", "Standard", "Resistance"], "mohm" => ["milli-ohms", "Standard", "Resistance"], "cohm" => ["centi-ohms", "Standard", "Resistance"], "dohm" => ["deci-ohms", "Standard", "Resistance"], "daohm" => ["deca-ohms", "Standard", "Resistance"], "hohm" => ["hecto-ohms", "Standard", "Resistance"], "kohm" => ["kilo-ohms", "Standard", "Resistance"], "Mohm" => ["mega-ohms", "Standard", "Resistance"], "Gohm" => ["giga-ohms", "Standard", "Resistance"], "Tohm" => ["tera-ohms", "Standard", "Resistance"], "C" => ["degrees-celsius", "Metric", "Temperature"], "K" => ["degrees-kelvin", "Metric", "Temperature"], "F" => ["degrees-fahrenheit", "English", "Temperature"], "R" => ["degrees-rankine", "English", "Temperature"], "dB" => ["decibels", "Standard", "Sound"], "J" => ["joule", "Metric", "Energy"], "pJ" => ["pico-joule", "Metric", "Energy"], "nJ" => ["nano-joule", "Metric", "Energy"], "uJ" => ["micro-joule", "Metric", "Energy"], "mJ" => ["milli-joule", "Metric", "Energy"], "cJ" => ["centi-joule", "Metric", "Energy"], "dJ" => ["deci-joule", "Metric", "Energy"], "daJ" => ["deca-joule", "Metric", "Energy"], "hJ" => ["hecto-joule", "Metric", "Energy"], "kJ" => ["kilo-joule", "Metric", "Energy"], "MJ" => ["mega-joule", "Metric", "Energy"], "GJ" => ["giga-joule", "Metric", "Energy"], "TJ" => ["tera-joule", "Metric", "Energy"], "BTU" => ["british-thermal", "English", "Energy"], "TNT" => ["ton-tnt", "English", "Energy"],"MHz" => ["mega-hertz", "Metric", "Frequency"], "GHz" => ["giga-hertz", "Metric", "Frequency"], "THz" => ["tera-hertz", "Metric", "Frequency"], "W" => ["watt", "Metric", "Power"], "pW" => ["pico-watt", "Metric", "Power"], "nW" => ["nano-watt", "Metric", "Power"], "uW" => ["micro-watt", "Metric", "Power"], "mW" => ["milli-watt", "Metric", "Power"], "cW" => ["centi-watt", "Metric", "Power"], "dW" => ["deci-watt", "Metric", "Power"], "daW" => ["deca-watt", "Metric", "Power"], "hW" => ["hecto-watt", "Metric", "Power"], "kW" => ["kilo-watt", "Metric", "Power"], "MW" => ["mega-watt", "Metric", "Power"], "GW" => ["giga-watt", "Metric", "Power"], "TW" => ["tera-watt", "Metric", "Power"], "hp" => ["horse-power", "English", "Power"], "Pa" => ["pascal", "Metric", "Pressure"], "pPa" => ["pico-pascal", "Metric", "Pressure"], "nPa" => ["nano-pascal", "Metric", "Pressure"], "uPa" => ["micro-pascal", "Metric", "Pressure"], "mPa" => ["milli-pascal", "Metric", "Pressure"], "cPa" => ["centi-pascal", "Metric", "Pressure"], "dPa" => ["deci-pascal", "Metric", "Pressure"], "daPa" => ["deca-pascal", "Metric", "Pressure"], "hPa" => ["hecto-pascal", "Metric", "Pressure"], "kPa" => ["kilo-pascal", "Metric", "Pressure"], "MPa" => ["mega-pascal", "Metric", "Pressure"], "GPa" => ["giga-pascal", "Metric", "Pressure"], "TPa" => ["tera-pascal", "Metric", "Pressure"], "psi" => ["pounds-sq-inch", "English", "Pressure"], "atm" => ["atmospheres", "Metric", "Pressure"], "mmHg" => ["milli-meters-mercury", "Metric", "Pressure"], "inHg" => ["inches-mercury", "English", "Pressure"], "mmWater" => ["milli-meters-water", "Metric", "Pressure"], "inWater" => ["inches-water", "English", "Pressure"], "l" => ["liter", "Metric", "Volume"], "pl" => ["pico-liter", "Metric", "Volume"], "nl" => ["nano-liter", "Metric", "Volume"], "ul" => ["micro-liter", "Metric", "Volume"], "ml" => ["milli-liter", "Metric", "Volume"], "cl" => ["centi-liter", "Metric", "Volume"], "dl" => ["deci-liter", "Metric", "Volume"], "dal" => ["deca-liter", "Metric", "Volume"], "hl" => ["hecto-liter", "Metric", "Volume"], "kl" => ["kilo-liter", "Metric", "Volume"], "Ml" => ["mega-liter", "Metric", "Volume"], "Gl" => ["giga-liter", "Metric", "Volume"], "Tl" => ["tera-liter", "Metric", "Volume"], "gal" => ["gallon", "English", "Volume"], "floz" => ["fluid-ounce", "English", "Volume"], "--" => ["", "Standard", "Unitless"], "cnt" => ["", "Standard", "Unitless"], "1" => ["", "Standard", "Unitless"], "one" => ["", "Standard", "Unitless"], "mol" => ["", "Standard", "Unitless"], "Hz" => ["hertz", "Metric", "Frequency"], "pHz" => ["pico-hertz", "Metric", "Frequency"], "nz" => ["nano-hertz", "Metric", "Frequency"], "uHz" => ["micro-hertz", "Metric", "Frequency"], "mHz" => ["milli-hertz", "Metric", "Frequency"], "cHz" => ["centi-hertz", "Metric", "Frequency"], "dHz" => ["deci-hertz", "Metric", "Frequency"], "daHz" => ["deca-hertz", "Metric", "Frequency"], "hHz" => ["hecto-hertz", "Metric", "Frequency"], "kHz" => ["kilo-hertz", "Metric", "Frequency"], "MHz" => ["mega-hertz", "Metric", "Frequency"], "GHz" => ["giga-hertz", "Metric", "Frequency"], "THz" => ["tera-hertz", "Metric", "Frequency"], ); sub list_units { foreach $unit_primitive ( sort ( keys %units_info ) ) { printf ( STDOUT "\"%s\" means \"%s\", a %s unit of %s.\n", $unit_primitive, $units_info{$unit_primitive}[0], $units_info{$unit_primitive}[1], $units_info{$unit_primitive}[2] ); } } sub is_valid_primitive { my ($unit_primitive) = @_; if ( $units_info{$unit_primitive} ) { return 1; } else { return 0; } } sub validate_unit_spec { my ($unit_spec) = @_; my $warn_msg = "" ; my @valid_parts = (); # If the string starts with a parenthesis, then it is a release-07 dual units spec. if ( $unit_spec =~ /^\(/ ) { my @part_attrs = (0,0); my @part_class = (0,0); # Strip out right and left parens and whitespace. $unit_spec =~ s/^\(|\s|\)$//g ; $unit_spec =~ s/\^//g ; # Split the dual unit spec into its metric and english parts. @dual_unit_parts = split /\|/, $unit_spec ; # There should be either one or two parts. $number_of_parts = scalar( @dual_unit_parts ); if ( $number_of_parts == 1 ) { for (my $ii = 0 ; $ii < $number_of_parts ; $ii ++ ) { my $unit_factor; # Split the unit spec, using '*' and '/' as delimiters. my @unit_factors = split /\/|\*/ , $dual_unit_parts[$ii] ; foreach $unit_factor ( @unit_factors ) { # Substitute out any exponents unless it is just "1" all by itself. $unit_factor =~ s/[1-3]+$//g if ( $unit_factor ne "1" ); $unit_system = $units_info{$unit_factor}[1]; if ( $unit_system eq "Metric") { # The ii'th part contains metric primitives. $part_attrs[$ii] |= 1 ; } elsif ( $unit_system eq "Standard") { # The ii'th part contains english primitives. $part_attrs[$ii] |= 2; } elsif ( $unit_system eq "English") { # The ii'th part contains standard primitives. $part_attrs[$ii] |= 4; } else { # The ii'th part contains stuff we dont recognize. # It's broke. $part_attrs[$ii] |= 8; } } if ( $part_attrs[$ii] == 2 ) { $part_class[$ii] = 1; # pure standard } elsif ( $part_attrs[$ii] == 1 || $part_attrs[$ii] == 3 ) { @part_class[$ii] = 2; # metric (possibly mixed with standard )"kHz" => ["kilo-hertz", "Metric", "Frequency"], "pHz" => ["pico-hertz", "Metric", "Frequency"], "nHz" => ["nano-hertz", "Metric", "Frequency"], "uHz" => ["micro-hertz", "Metric", "Frequency"], "mHz" => ["milli-hertz", "Metric", "Frequency"], "cHz" => ["centi-hertz", "Metric", "Frequency"], "dHz" => ["deci-hertz", "Metric", "Frequency"], "daHz" => ["deca-hertz", "Metric", "Frequency"], "hHz" => ["hecto-hertz", "Metric", "Frequency"], "kHz" => ["kilo-hertz", "Metric", "Frequency"], "MHz" => ["mega-hertz", "Metric", "Frequency"], "GHz" => ["giga-hertz", "Metric", "Frequency"], "THz" => ["tera-hertz", "Metric", "Frequency"], } elsif ( $part_attrs[$ii] == 4 || $part_attrs[$ii] == 6 ) { $part_class[$ii] = 3; # english (possibly mixed with standard ) } else { $part_class[$ii] = 0; # invalid } if ( $part_class[$ii] ) { push @valid_parts, $dual_unit_parts[$ii]; } } if ( $number_of_parts == scalar ( @valid_parts )) { if ( $number_of_parts == 2 ) { if ( ( $part_class[0] == $part_class[1] ) && ( $part_class[0] != 1 ) ) { @valid_parts = (); } elsif ( ($part_class[0] == 3) || ( ($part_class[0] == 1) && ($part_class[1] == 2) ) ) { $temp = $valid_parts[0]; $valid_parts[0] = $valid_parts[1]; $valid_parts[1] = $temp; } } } else { @valid_parts = (); } } elsif ( $number_of_parts == 2 ) { $warn_msg = "Dual units are no longer supported" ; return ($warn_msg , @valid_parts) ; } } else { my ($orig_unit_spec) = $unit_spec ; # Pre 07, old style units $superset_units = "sl|rev" ; $std_units = "1|s|M|r|kg|N|v|amp|ohm|C|dB|J|W|Pa|--" ; $derived_units = "min|hr|day|ft|in|mm|cm|km|yd|mi|nm|n\.m\.|kft|d|as|am|mr|lbm|g|mt|kN|oz|lbf|kv|mamp|K|R|F|BTU|TNT|hp|psi|atm|cnt|one|mol|pJ|nJ|uJ|mJ|cJ |dJ|daJ|hJ|kJ|MJ|GJ|TJ|pW|nW|uW|mW|cW|dW|daW|hW|kW|MW|GW|TW|Pa|pPa|nPa|uPa|mPa|cPa|dPa|daPa|hPa|kPa|MPa|GPa|TPa|pHz|nHz|uHz|mHz|cHz|dHz|daHz|hHz|kHz|MHz|GHz|THz" ; my $first_product = 0; my @elements = (); $unit_spec =~ s/\*//g; # Remove any asterisks that may incorrectly represent an exponent/power. $unit_spec =~ s/\^//g; # Remove any "^" that may incorrectly represent an exponent/power. if ( $unit_spec =~ s/^1?\/// ) { $first_product = 1; push @elements, "1"; } while ( $unit_spec =~ s/([^\/]+)(\/|$)// ) { $unit_product = $1 ; if ( !$first_product ) {"pHz" => ["pico-hertz", "Metric", "Frequency"], "nHz" => ["nano-hertz", "Metric", "Frequency"], "uHz" => ["micro-hertz", "Metric", "Frequency"], "mHz" => ["milli-hertz", "Metric", "Frequency"], "cHz" => ["centi-hertz", "Metric", "Frequency"], "dHz" => ["deci-hertz", "Metric", "Frequency"], "daHz" => ["deca-hertz", "Metric", "Frequency"], "hHz" => ["hecto-hertz", "Metric", "Frequency"], "kHz" => ["kilo-hertz", "Metric", "Frequency"], "MHz" => ["mega-hertz", "Metric", "Frequency"], "GHz" => ["giga-hertz", "Metric", "Frequency"], "THz" => ["tera-hertz", "Metric", "Frequency"], $first_product = 1; } else { push @elements, "/"; } $unit_product =~ s/^(($superset_units|$std_units|$derived_units)([123]?))// ; if ( $1 ne "" ) { if ( $1 eq "nm" ) { $warn_msg = " Units \"nm\" assumed to be nautical miles." . " Please clarify by using \"n.m.\" or \"(n.m.)\". Use \"(nm)\" for nanometers." ; push @elements, "n.m."; } else { push @elements, $1; } while ( $unit_product =~ s/^(($superset_units|$std_units|$derived_units)([123]?))// ) { push @elements, "*", $1; } } else { # Units product contains no primitives at all. Not good. @elements = (); last; } if ( $unit_product ne "" ) { @elements = (); last; } } $valid_parts[0] = join '', @elements; # convert old "M" to standard "m" $valid_parts[0] =~ s/M/m/g ; if ( $valid_parts[0] eq "" ) { $warn_msg = " No valid units found. If you are using \"m\" for meters, enclose the units in parenthesis. i.e. \"(m)\"\n" ; $warn_msg .=" Defaulting units to \"--\" instead of \"$orig_unit_spec\"" ; # Provide a default unit for validity $valid_parts[0] = "--" ; } } return ($warn_msg , @valid_parts) ; }