ruby-units 1.4.3 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.txt +6 -0
- data/README.md +19 -6
- data/VERSION +1 -1
- data/lib/ruby-units.rb +8 -13
- data/lib/ruby_units.rb +8 -13
- data/lib/ruby_units/array.rb +4 -4
- data/lib/ruby_units/cache.rb +2 -2
- data/lib/ruby_units/definition.rb +6 -6
- data/lib/ruby_units/fixnum.rb +2 -2
- data/lib/ruby_units/math.rb +12 -12
- data/lib/ruby_units/namespaced.rb +16 -0
- data/lib/ruby_units/numeric.rb +3 -3
- data/lib/ruby_units/string.rb +5 -5
- data/lib/ruby_units/time.rb +9 -8
- data/lib/ruby_units/unit.rb +1408 -1385
- data/lib/ruby_units/unit_definitions.rb +3 -3
- data/lib/ruby_units/unit_definitions/base.rb +16 -16
- data/lib/ruby_units/unit_definitions/prefix.rb +2 -2
- data/lib/ruby_units/unit_definitions/standard.rb +268 -268
- data/lib/ruby_units/version.rb +7 -5
- data/ruby-units.gemspec +3 -2
- metadata +4 -3
    
        data/CHANGELOG.txt
    CHANGED
    
    | @@ -1,5 +1,11 @@ | |
| 1 1 | 
             
            Change Log for Ruby-units
         | 
| 2 2 | 
             
            =========================
         | 
| 3 | 
            +
            2013-07-19  1.4.4   * Fix issue #4 -- .best_prefix method
         | 
| 4 | 
            +
                                * Fix issue #60 -- Consider placing Unit in a module
         | 
| 5 | 
            +
                                * Fix issue #75 -- Siemens is kind of conductance not resistance
         | 
| 6 | 
            +
                                * Fix issue #36 -- Don't require spaces in units
         | 
| 7 | 
            +
                                * Fix issue #68 -- ditto
         | 
| 8 | 
            +
                                * Fix issue #16 -- ditto
         | 
| 3 9 | 
             
            2013-06-11  1.4.3   * Fix issue #70 -- Support passing Time objects to Time.at
         | 
| 4 10 | 
             
                                * Fix issue #72 -- Remove non-existent RakeFile from gemspec
         | 
| 5 11 | 
             
                                * Fix issue #71 -- Fix YAML test failure
         | 
    
        data/README.md
    CHANGED
    
    | @@ -2,9 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            [](http://travis-ci.org/olbrich/ruby-units)
         | 
| 4 4 |  | 
| 5 | 
            -
            Kevin C. Olbrich, Ph.D. | 
| 6 | 
            -
             | 
| 7 | 
            -
            [Sciwerks.com](http://www.sciwerks.com)
         | 
| 5 | 
            +
            Kevin C. Olbrich, Ph.D.
         | 
| 8 6 |  | 
| 9 7 | 
             
            Project page: [http://github.com/olbrich/ruby-units](http://github.com/olbrich/ruby-units)
         | 
| 10 8 |  | 
| @@ -34,9 +32,9 @@ This package may be installed using:  `gem install ruby-units` | |
| 34 32 | 
             
            ## Rules:
         | 
| 35 33 | 
             
            1. only 1 quantity per unit (with 2 exceptions... 6'5" and '8 lbs 8 oz')
         | 
| 36 34 | 
             
            2. use SI notation when possible
         | 
| 37 | 
            -
            3.  | 
| 35 | 
            +
            3. spaces in units are allowed, but ones like '11/m' will be recognized as '11 1/m'.
         | 
| 38 36 |  | 
| 39 | 
            -
            ## Unit  | 
| 37 | 
            +
            ## Unit compatibility:
         | 
| 40 38 | 
             
            Many methods require that the units of two operands are compatible.  Compatible units are those that can be easily converted into each other, such as 'meters' and 'feet'.
         | 
| 41 39 |  | 
| 42 40 | 
             
                unit1 =~ unit2                  #=> true if units are compatible
         | 
| @@ -170,4 +168,19 @@ This is useful for changing display names, adding aliases, etc. | |
| 170 168 | 
             
                Unit.redefine!("cup") do |cup|
         | 
| 171 169 | 
             
                  cup.display_name  = "cup"
         | 
| 172 170 | 
             
                end
         | 
| 173 | 
            -
             | 
| 171 | 
            +
             | 
| 172 | 
            +
             | 
| 173 | 
            +
            ### Namespaced Class
         | 
| 174 | 
            +
             | 
| 175 | 
            +
            Sometimes the default class 'Unit' may conflict with other gems or applications.  Internally ruby-units defines itself using the RubyUnits namespace.
         | 
| 176 | 
            +
            The actual class of a unit is the RubyUnits::Unit.  For simplicity and backwards compatiblity, the '::Unit' class is defined as an alias to '::RubyUnits::Unit'.
         | 
| 177 | 
            +
             | 
| 178 | 
            +
            To load ruby-units without this alias...
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                require 'ruby-units/namespaced'
         | 
| 181 | 
            +
             | 
| 182 | 
            +
            When using bundler...
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                gem 'ruby-units', require: 'namespaced'
         | 
| 185 | 
            +
             | 
| 186 | 
            +
            Note: when using the namespaced version, the Unit('unit string') helper will not be defined.
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            1.4. | 
| 1 | 
            +
            1.4.4
         | 
    
        data/lib/ruby-units.rb
    CHANGED
    
    | @@ -1,14 +1,9 @@ | |
| 1 1 | 
             
            $LOAD_PATH << File.dirname(__FILE__)
         | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
            require 'ruby_units/object'
         | 
| 11 | 
            -
            require 'ruby_units/string'
         | 
| 12 | 
            -
            require 'ruby_units/unit'
         | 
| 13 | 
            -
            require 'ruby_units/fixnum'
         | 
| 14 | 
            -
            require 'ruby_units/unit_definitions'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'ruby_units/namespaced'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # only include the Unit('unit') helper if we aren't fully namespaced
         | 
| 6 | 
            +
            require_relative 'ruby_units/object'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
             | 
| 9 | 
            +
            Unit = RubyUnits::Unit
         | 
    
        data/lib/ruby_units.rb
    CHANGED
    
    | @@ -1,14 +1,9 @@ | |
| 1 1 | 
             
            $LOAD_PATH << File.dirname(__FILE__)
         | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
            require 'ruby_units/object'
         | 
| 11 | 
            -
            require 'ruby_units/string'
         | 
| 12 | 
            -
            require 'ruby_units/unit'
         | 
| 13 | 
            -
            require 'ruby_units/fixnum'
         | 
| 14 | 
            -
            require 'ruby_units/unit_definitions'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'ruby_units/namespaced'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # only include the Unit('unit') helper if we aren't fully namespaced
         | 
| 6 | 
            +
            require_relative 'ruby_units/object'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
             | 
| 9 | 
            +
            Unit = RubyUnits::Unit
         | 
    
        data/lib/ruby_units/array.rb
    CHANGED
    
    | @@ -1,11 +1,11 @@ | |
| 1 1 | 
             
            class Array
         | 
| 2 2 | 
             
              # Construct a unit from an array
         | 
| 3 | 
            -
              # @example [1, 'mm'].to_unit => Unit("1 mm")
         | 
| 4 | 
            -
              # @return (see Unit#initialize)
         | 
| 3 | 
            +
              # @example [1, 'mm'].to_unit => RubyUnits::Unit("1 mm")
         | 
| 4 | 
            +
              # @return (see RubyUnits::Unit#initialize)
         | 
| 5 5 | 
             
              # @param [Object] other convert to same units as passed
         | 
| 6 6 | 
             
              def to_unit(other = nil)
         | 
| 7 | 
            -
                other ? Unit.new(self).convert_to(other) : Unit.new(self)
         | 
| 7 | 
            +
                other ? RubyUnits::Unit.new(self).convert_to(other) : RubyUnits::Unit.new(self)
         | 
| 8 8 | 
             
              end
         | 
| 9 9 | 
             
              alias :unit :to_unit
         | 
| 10 10 | 
             
              alias :u :to_unit
         | 
| 11 | 
            -
            end
         | 
| 11 | 
            +
            end
         | 
    
        data/lib/ruby_units/cache.rb
    CHANGED
    
    
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            class Unit < Numeric
         | 
| 1 | 
            +
            class RubyUnits::Unit < Numeric
         | 
| 2 2 |  | 
| 3 3 | 
             
              # Handle the definition of units
         | 
| 4 4 | 
             
              class Definition
         | 
| @@ -36,8 +36,8 @@ class Unit < Numeric | |
| 36 36 | 
             
                  @aliases      ||= (_definition[0] || [_name])
         | 
| 37 37 | 
             
                  @scalar       ||= _definition[1]
         | 
| 38 38 | 
             
                  @kind         ||= _definition[2]
         | 
| 39 | 
            -
                  @numerator    ||= _definition[3] || Unit::UNITY_ARRAY
         | 
| 40 | 
            -
                  @denominator  ||= _definition[4] || Unit::UNITY_ARRAY
         | 
| 39 | 
            +
                  @numerator    ||= _definition[3] || RubyUnits::Unit::UNITY_ARRAY
         | 
| 40 | 
            +
                  @denominator  ||= _definition[4] || RubyUnits::Unit::UNITY_ARRAY
         | 
| 41 41 | 
             
                  @display_name ||= @aliases.first
         | 
| 42 42 | 
             
                end
         | 
| 43 43 |  | 
| @@ -90,11 +90,11 @@ class Unit < Numeric | |
| 90 90 | 
             
                # units are base units if the scalar is one, and the unit is defined in terms of itself.
         | 
| 91 91 | 
             
                # @return [Boolean]
         | 
| 92 92 | 
             
                def base?
         | 
| 93 | 
            -
                  (self.denominator     == Unit::UNITY_ARRAY) &&
         | 
| 94 | 
            -
                  (self.numerator       != Unit::UNITY_ARRAY) &&
         | 
| 93 | 
            +
                  (self.denominator     == RubyUnits::Unit::UNITY_ARRAY) &&
         | 
| 94 | 
            +
                  (self.numerator       != RubyUnits::Unit::UNITY_ARRAY) &&
         | 
| 95 95 | 
             
                  (self.numerator.size  == 1) &&
         | 
| 96 96 | 
             
                  (self.scalar          == 1) &&
         | 
| 97 97 | 
             
                  (self.numerator.first == self.name)
         | 
| 98 98 | 
             
                end
         | 
| 99 99 | 
             
              end
         | 
| 100 | 
            -
            end
         | 
| 100 | 
            +
            end
         | 
    
        data/lib/ruby_units/fixnum.rb
    CHANGED
    
    | @@ -8,7 +8,7 @@ if RUBY_VERSION < "1.9" | |
| 8 8 | 
             
                # @return [Unit, Integer]
         | 
| 9 9 | 
             
                def quo_with_units(other)
         | 
| 10 10 | 
             
                  case other
         | 
| 11 | 
            -
                  when Unit
         | 
| 11 | 
            +
                  when RubyUnits::Unit
         | 
| 12 12 | 
             
                    self * other.inverse
         | 
| 13 13 | 
             
                  else
         | 
| 14 14 | 
             
                    quo_without_units(other)
         | 
| @@ -19,4 +19,4 @@ if RUBY_VERSION < "1.9" | |
| 19 19 | 
             
                alias / quo_with_units
         | 
| 20 20 | 
             
              end
         | 
| 21 21 | 
             
              # :nocov_19:
         | 
| 22 | 
            -
            end
         | 
| 22 | 
            +
            end
         | 
    
        data/lib/ruby_units/math.rb
    CHANGED
    
    | @@ -7,7 +7,7 @@ module Math | |
| 7 7 | 
             
              alias :unit_sqrt :sqrt
         | 
| 8 8 | 
             
              # @return [Numeric]
         | 
| 9 9 | 
             
              def sqrt(n)
         | 
| 10 | 
            -
                if Unit === n
         | 
| 10 | 
            +
                if RubyUnits::Unit === n
         | 
| 11 11 | 
             
                  (n**(Rational(1,2))).to_unit 
         | 
| 12 12 | 
             
                else
         | 
| 13 13 | 
             
                  unit_sqrt(n)
         | 
| @@ -23,7 +23,7 @@ module Math | |
| 23 23 | 
             
                alias :unit_cbrt :cbrt
         | 
| 24 24 | 
             
                # @return [Numeric]
         | 
| 25 25 | 
             
                def cbrt(n)
         | 
| 26 | 
            -
                  if Unit === n
         | 
| 26 | 
            +
                  if RubyUnits::Unit === n
         | 
| 27 27 | 
             
                    (n**(Rational(1,3))).to_unit 
         | 
| 28 28 | 
             
                  else
         | 
| 29 29 | 
             
                    unit_cbrt(n)
         | 
| @@ -39,7 +39,7 @@ module Math | |
| 39 39 | 
             
              alias :unit_sin :sin
         | 
| 40 40 | 
             
              # @return [Numeric]
         | 
| 41 41 | 
             
              def sin(n)
         | 
| 42 | 
            -
               Unit === n ? unit_sin(n.convert_to('radian').scalar) : unit_sin(n)
         | 
| 42 | 
            +
               RubyUnits::Unit === n ? unit_sin(n.convert_to('radian').scalar) : unit_sin(n)
         | 
| 43 43 | 
             
              end
         | 
| 44 44 | 
             
              # @return [Numeric]
         | 
| 45 45 | 
             
              module_function :unit_sin
         | 
| @@ -49,7 +49,7 @@ module Math | |
| 49 49 | 
             
              alias :unit_cos :cos
         | 
| 50 50 | 
             
              # @return [Numeric]
         | 
| 51 51 | 
             
              def cos(n)
         | 
| 52 | 
            -
                Unit === n ? unit_cos(n.convert_to('radian').scalar) : unit_cos(n)
         | 
| 52 | 
            +
                RubyUnits::Unit === n ? unit_cos(n.convert_to('radian').scalar) : unit_cos(n)
         | 
| 53 53 | 
             
              end
         | 
| 54 54 | 
             
              # @return [Numeric]
         | 
| 55 55 | 
             
              module_function :unit_cos
         | 
| @@ -59,7 +59,7 @@ module Math | |
| 59 59 | 
             
              alias :unit_sinh :sinh
         | 
| 60 60 | 
             
              # @return [Numeric]
         | 
| 61 61 | 
             
              def sinh(n)
         | 
| 62 | 
            -
                Unit === n ? unit_sinh(n.convert_to('radian').scalar) : unit_sinh(n)
         | 
| 62 | 
            +
                RubyUnits::Unit === n ? unit_sinh(n.convert_to('radian').scalar) : unit_sinh(n)
         | 
| 63 63 | 
             
              end
         | 
| 64 64 | 
             
              # @return [Numeric]
         | 
| 65 65 | 
             
              module_function :unit_sinh
         | 
| @@ -69,7 +69,7 @@ module Math | |
| 69 69 | 
             
              alias :unit_cosh :cosh
         | 
| 70 70 | 
             
              # @return [Numeric]
         | 
| 71 71 | 
             
              def cosh(n)
         | 
| 72 | 
            -
                Unit === n ? unit_cosh(n.convert_to('radian').scalar) : unit_cosh(n)
         | 
| 72 | 
            +
                RubyUnits::Unit === n ? unit_cosh(n.convert_to('radian').scalar) : unit_cosh(n)
         | 
| 73 73 | 
             
              end
         | 
| 74 74 | 
             
              # @return [Numeric]
         | 
| 75 75 | 
             
              module_function :unit_cosh
         | 
| @@ -79,7 +79,7 @@ module Math | |
| 79 79 | 
             
              alias :unit_tan :tan
         | 
| 80 80 | 
             
              # @return [Numeric]
         | 
| 81 81 | 
             
              def tan(n)
         | 
| 82 | 
            -
               Unit === n ? unit_tan(n.convert_to('radian').scalar) : unit_tan(n)
         | 
| 82 | 
            +
               RubyUnits::Unit === n ? unit_tan(n.convert_to('radian').scalar) : unit_tan(n)
         | 
| 83 83 | 
             
              end
         | 
| 84 84 | 
             
              # @return [Numeric]
         | 
| 85 85 | 
             
              module_function :tan
         | 
| @@ -89,7 +89,7 @@ module Math | |
| 89 89 | 
             
              alias :unit_tanh :tanh
         | 
| 90 90 | 
             
              # @return [Numeric]
         | 
| 91 91 | 
             
              def tanh(n)
         | 
| 92 | 
            -
                Unit === n ? unit_tanh(n.convert_to('radian').scalar) : unit_tanh(n)
         | 
| 92 | 
            +
                RubyUnits::Unit === n ? unit_tanh(n.convert_to('radian').scalar) : unit_tanh(n)
         | 
| 93 93 | 
             
              end
         | 
| 94 94 | 
             
              # @return [Numeric]
         | 
| 95 95 | 
             
              module_function :unit_tanh
         | 
| @@ -100,7 +100,7 @@ module Math | |
| 100 100 | 
             
              # Convert parameters to consistent units and perform the function
         | 
| 101 101 | 
             
              # @return [Numeric]
         | 
| 102 102 | 
             
              def hypot(x,y)
         | 
| 103 | 
            -
                if Unit === x && Unit === y
         | 
| 103 | 
            +
                if RubyUnits::Unit === x && RubyUnits::Unit === y
         | 
| 104 104 | 
             
                  (x**2 + y**2)**(1/2)
         | 
| 105 105 | 
             
                else
         | 
| 106 106 | 
             
                  unit_hypot(x,y)
         | 
| @@ -115,9 +115,9 @@ module Math | |
| 115 115 | 
             
              # @return [Numeric]
         | 
| 116 116 | 
             
              def atan2(x,y)
         | 
| 117 117 | 
             
                case
         | 
| 118 | 
            -
                when (x.is_a?(Unit) && y.is_a?(Unit)) && (x !~ y)
         | 
| 119 | 
            -
                  raise ArgumentError, "Incompatible Units"
         | 
| 120 | 
            -
                when (x.is_a?(Unit) && y.is_a?(Unit)) && (x =~ y)
         | 
| 118 | 
            +
                when (x.is_a?(RubyUnits::Unit) && y.is_a?(RubyUnits::Unit)) && (x !~ y)
         | 
| 119 | 
            +
                  raise ArgumentError, "Incompatible RubyUnits::Units"
         | 
| 120 | 
            +
                when (x.is_a?(RubyUnits::Unit) && y.is_a?(RubyUnits::Unit)) && (x =~ y)
         | 
| 121 121 | 
             
                  Math::unit_atan2(x.base_scalar, y.base_scalar)
         | 
| 122 122 | 
             
                else
         | 
| 123 123 | 
             
                  Math::unit_atan2(x,y)
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            $LOAD_PATH << File.dirname(__FILE__)
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            # require_relative this file to avoid creating an class alias from Unit to RubyUnits::Unit
         | 
| 5 | 
            +
            require_relative 'version'
         | 
| 6 | 
            +
            require_relative 'definition'
         | 
| 7 | 
            +
            require_relative 'cache'
         | 
| 8 | 
            +
            require_relative 'array'
         | 
| 9 | 
            +
            require_relative 'date'
         | 
| 10 | 
            +
            require_relative 'time'
         | 
| 11 | 
            +
            require_relative 'math'
         | 
| 12 | 
            +
            require_relative 'numeric'
         | 
| 13 | 
            +
            require_relative 'string'
         | 
| 14 | 
            +
            require_relative 'unit'
         | 
| 15 | 
            +
            require_relative 'fixnum'
         | 
| 16 | 
            +
            require_relative 'unit_definitions'
         | 
    
        data/lib/ruby_units/numeric.rb
    CHANGED
    
    | @@ -1,9 +1,9 @@ | |
| 1 1 | 
             
            class Numeric
         | 
| 2 2 | 
             
              # make a unitless unit with a given scalar
         | 
| 3 | 
            -
              # @return (see Unit#initialize)
         | 
| 3 | 
            +
              # @return (see RubyUnits::Unit#initialize)
         | 
| 4 4 | 
             
              def to_unit(other = nil)
         | 
| 5 | 
            -
                other ? Unit.new(self, other) : Unit.new(self)
         | 
| 5 | 
            +
                other ? RubyUnits::Unit.new(self, other) : RubyUnits::Unit.new(self)
         | 
| 6 6 | 
             
              end
         | 
| 7 7 | 
             
              alias :unit :to_unit
         | 
| 8 8 | 
             
              alias :u :to_unit
         | 
| 9 | 
            -
            end
         | 
| 9 | 
            +
            end
         | 
    
        data/lib/ruby_units/string.rb
    CHANGED
    
    | @@ -1,9 +1,9 @@ | |
| 1 1 | 
             
            require 'time'
         | 
| 2 2 | 
             
            class String
         | 
| 3 3 | 
             
              # make a string into a unit
         | 
| 4 | 
            -
              # @return (see Unit#initialize)
         | 
| 4 | 
            +
              # @return (see RubyUnits::Unit#initialize)
         | 
| 5 5 | 
             
              def to_unit(other = nil)
         | 
| 6 | 
            -
                other ? Unit.new(self).convert_to(other) : Unit.new(self)
         | 
| 6 | 
            +
                other ? RubyUnits::Unit.new(self).convert_to(other) : RubyUnits::Unit.new(self)
         | 
| 7 7 | 
             
              end
         | 
| 8 8 | 
             
              alias :unit :to_unit
         | 
| 9 9 | 
             
              alias :u :to_unit
         | 
| @@ -16,7 +16,7 @@ class String | |
| 16 16 | 
             
              def %(*args)
         | 
| 17 17 | 
             
            		return "" if self.empty?
         | 
| 18 18 | 
             
                case 
         | 
| 19 | 
            -
                when args.first.is_a?(Unit)
         | 
| 19 | 
            +
                when args.first.is_a?(RubyUnits::Unit)
         | 
| 20 20 | 
             
                  args.first.to_s(self)
         | 
| 21 21 | 
             
                when (!defined?(Uncertain).nil? && args.first.is_a?(Uncertain))
         | 
| 22 22 | 
             
                  args.first.to_s(self)
         | 
| @@ -27,8 +27,8 @@ class String | |
| 27 27 | 
             
                end
         | 
| 28 28 | 
             
              end
         | 
| 29 29 |  | 
| 30 | 
            -
              # @param (see Unit#convert_to)
         | 
| 31 | 
            -
              # @return (see Unit#convert_to)
         | 
| 30 | 
            +
              # @param (see RubyUnits::Unit#convert_to)
         | 
| 31 | 
            +
              # @return (see RubyUnits::Unit#convert_to)
         | 
| 32 32 | 
             
              def convert_to(other)
         | 
| 33 33 | 
             
                self.unit.convert_to(other)
         | 
| 34 34 | 
             
              end
         | 
    
        data/lib/ruby_units/time.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            require 'time'
         | 
| 1 2 | 
             
            #
         | 
| 2 3 | 
             
            # Time math is handled slightly differently.  The difference is considered to be an exact duration if
         | 
| 3 4 | 
             
            # the subtracted value is in hours, minutes, or seconds.  It is rounded to the nearest day if the offset
         | 
| @@ -13,21 +14,21 @@ class Time | |
| 13 14 | 
             
              # epoch
         | 
| 14 15 | 
             
              # @param [Time] arg
         | 
| 15 16 | 
             
              # @param [Integer] ms
         | 
| 16 | 
            -
              # @return [Unit, Time]
         | 
| 17 | 
            +
              # @return [RubyUnits::Unit, Time]
         | 
| 17 18 | 
             
              def self.at(arg,ms=0)
         | 
| 18 19 | 
             
                case arg
         | 
| 19 20 | 
             
                when Time
         | 
| 20 21 | 
             
                  unit_time_at(arg)
         | 
| 21 | 
            -
                when Unit
         | 
| 22 | 
            +
                when RubyUnits::Unit
         | 
| 22 23 | 
             
                  unit_time_at(arg.convert_to("s").scalar, ms)
         | 
| 23 24 | 
             
                else
         | 
| 24 25 | 
             
                  unit_time_at(arg, ms)
         | 
| 25 26 | 
             
                end
         | 
| 26 27 | 
             
              end
         | 
| 27 28 |  | 
| 28 | 
            -
              # @return (see Unit#initialize)
         | 
| 29 | 
            +
              # @return (see RubyUnits::Unit#initialize)
         | 
| 29 30 | 
             
              def to_unit(other = nil)
         | 
| 30 | 
            -
                other ? Unit.new(self).convert_to(other) : Unit.new(self)
         | 
| 31 | 
            +
                other ? RubyUnits::Unit.new(self).convert_to(other) : RubyUnits::Unit.new(self)
         | 
| 31 32 | 
             
              end
         | 
| 32 33 | 
             
              alias :unit :to_unit
         | 
| 33 34 | 
             
              alias :u :to_unit
         | 
| @@ -39,10 +40,10 @@ class Time | |
| 39 40 | 
             
              end
         | 
| 40 41 |  | 
| 41 42 | 
             
              alias :unit_add :+
         | 
| 42 | 
            -
              # @return [Unit, Time]
         | 
| 43 | 
            +
              # @return [RubyUnits::Unit, Time]
         | 
| 43 44 | 
             
              def +(other)
         | 
| 44 45 | 
             
                case other
         | 
| 45 | 
            -
                when Unit
         | 
| 46 | 
            +
                when RubyUnits::Unit
         | 
| 46 47 | 
             
                  other = other.convert_to('d').round.convert_to('s') if ['y', 'decade', 'century'].include? other.units  
         | 
| 47 48 | 
             
                  begin
         | 
| 48 49 | 
             
                    unit_add(other.convert_to('s').scalar)
         | 
| @@ -63,10 +64,10 @@ class Time | |
| 63 64 |  | 
| 64 65 | 
             
              alias :unit_sub :-
         | 
| 65 66 |  | 
| 66 | 
            -
              # @return [Unit, Time]
         | 
| 67 | 
            +
              # @return [RubyUnits::Unit, Time]
         | 
| 67 68 | 
             
              def -(other)
         | 
| 68 69 | 
             
                case other
         | 
| 69 | 
            -
                when Unit
         | 
| 70 | 
            +
                when RubyUnits::Unit
         | 
| 70 71 | 
             
                  other = other.convert_to('d').round.convert_to('s') if ['y', 'decade', 'century'].include? other.units  
         | 
| 71 72 | 
             
                  begin
         | 
| 72 73 | 
             
                    unit_sub(other.convert_to('s').scalar)
         | 
    
        data/lib/ruby_units/unit.rb
    CHANGED
    
    | @@ -23,1569 +23,1592 @@ end | |
| 23 23 | 
             
            #
         | 
| 24 24 | 
             
            # To add or override a unit definition, add a code block like this..
         | 
| 25 25 | 
             
            # @example Define a new unit
         | 
| 26 | 
            -
            #  Unit.define("foobar") do |unit|
         | 
| 26 | 
            +
            #  RubyUnits::Unit.define("foobar") do |unit|
         | 
| 27 27 | 
             
            #    unit.aliases    = %w{foo fb foo-bar}
         | 
| 28 | 
            -
            #    unit.definition = Unit("1 baz")
         | 
| 28 | 
            +
            #    unit.definition = RubyUnits::Unit.new("1 baz")
         | 
| 29 29 | 
             
            #  end
         | 
| 30 30 | 
             
            #
         | 
| 31 31 | 
             
            # @todo fix class variables so they conform to standard naming conventions and refactor away as many of them as possible
         | 
| 32 32 | 
             
            # @todo pull caching out into its own class
         | 
| 33 33 | 
             
            # @todo refactor internal representation of units
         | 
| 34 34 | 
             
            # @todo method to determine best natural prefix
         | 
| 35 | 
            -
             | 
| 36 | 
            -
               | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
              @@PREFIX_MAP       = {}
         | 
| 40 | 
            -
              @@UNIT_MAP         = {}
         | 
| 41 | 
            -
              @@UNIT_VALUES      = {}
         | 
| 42 | 
            -
              @@UNIT_REGEX       = nil
         | 
| 43 | 
            -
              @@UNIT_MATCH_REGEX = nil
         | 
| 44 | 
            -
              UNITY              = '<1>'
         | 
| 45 | 
            -
              UNITY_ARRAY        = [UNITY]
         | 
| 46 | 
            -
              FEET_INCH_REGEX    = /(\d+)\s*(?:'|ft|feet)\s*(\d+)\s*(?:"|in|inches)/
         | 
| 47 | 
            -
              TIME_REGEX         = /(\d+)*:(\d+)*:*(\d+)*[:,]*(\d+)*/
         | 
| 48 | 
            -
              LBS_OZ_REGEX       = /(\d+)\s*(?:#|lbs|pounds|pound-mass)+[\s,]*(\d+)\s*(?:oz|ounces)/
         | 
| 49 | 
            -
              SCI_NUMBER         = %r{([+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*)}
         | 
| 50 | 
            -
              RATIONAL_NUMBER    = /\(?([+-]?\d+)\/(\d+)\)?/
         | 
| 51 | 
            -
              COMPLEX_NUMBER     = /#{SCI_NUMBER}?#{SCI_NUMBER}i\b/
         | 
| 52 | 
            -
              NUMBER_REGEX       = /#{SCI_NUMBER}*\s*(.+)?/
         | 
| 53 | 
            -
              UNIT_STRING_REGEX  = /#{SCI_NUMBER}*\s*([^\/]*)\/*(.+)*/
         | 
| 54 | 
            -
              TOP_REGEX          = /([^ \*]+)(?:\^|\*\*)([\d-]+)/
         | 
| 55 | 
            -
              BOTTOM_REGEX       = /([^* ]+)(?:\^|\*\*)(\d+)/
         | 
| 56 | 
            -
              UNCERTAIN_REGEX    = /#{SCI_NUMBER}\s*\+\/-\s*#{SCI_NUMBER}\s(.+)/
         | 
| 57 | 
            -
              COMPLEX_REGEX      = /#{COMPLEX_NUMBER}\s?(.+)?/
         | 
| 58 | 
            -
              RATIONAL_REGEX     = /#{RATIONAL_NUMBER}\s?(.+)?/
         | 
| 59 | 
            -
              KELVIN             = ['<kelvin>']
         | 
| 60 | 
            -
              FAHRENHEIT         = ['<fahrenheit>']
         | 
| 61 | 
            -
              RANKINE            = ['<rankine>']
         | 
| 62 | 
            -
              CELSIUS            = ['<celsius>']
         | 
| 63 | 
            -
              @@TEMP_REGEX       = nil
         | 
| 64 | 
            -
              SIGNATURE_VECTOR = [
         | 
| 65 | 
            -
                                  :length,
         | 
| 66 | 
            -
                                  :time,
         | 
| 67 | 
            -
                                  :temperature,
         | 
| 68 | 
            -
                                  :mass,
         | 
| 69 | 
            -
                                  :current,
         | 
| 70 | 
            -
                                  :substance,
         | 
| 71 | 
            -
                                  :luminosity,
         | 
| 72 | 
            -
                                  :currency,
         | 
| 73 | 
            -
                                  :memory,
         | 
| 74 | 
            -
                                  :angle
         | 
| 75 | 
            -
                                  ]
         | 
| 76 | 
            -
              @@KINDS = {
         | 
| 77 | 
            -
                -312078       =>  :elastance,
         | 
| 78 | 
            -
                -312058       =>  :resistance,
         | 
| 79 | 
            -
                -312038       =>  :inductance,
         | 
| 80 | 
            -
                -152040       =>  :magnetism,
         | 
| 81 | 
            -
                -152038       =>  :magnetism,
         | 
| 82 | 
            -
                -152058       =>  :potential,
         | 
| 83 | 
            -
                -7997         =>  :specific_volume,
         | 
| 84 | 
            -
                -79           =>  :snap,
         | 
| 85 | 
            -
                -59           =>  :jolt,
         | 
| 86 | 
            -
                -39           =>  :acceleration,
         | 
| 87 | 
            -
                -38           =>  :radiation,
         | 
| 88 | 
            -
                -20           =>  :frequency,
         | 
| 89 | 
            -
                -19           =>  :speed,
         | 
| 90 | 
            -
                -18           =>  :viscosity,
         | 
| 91 | 
            -
                -17           =>  :volumetric_flow,
         | 
| 92 | 
            -
                -1            =>  :wavenumber,
         | 
| 93 | 
            -
                0             =>  :unitless,
         | 
| 94 | 
            -
                1             =>  :length,
         | 
| 95 | 
            -
                2             =>  :area,
         | 
| 96 | 
            -
                3             =>  :volume,
         | 
| 97 | 
            -
                20            =>  :time,
         | 
| 98 | 
            -
                400           =>  :temperature,
         | 
| 99 | 
            -
                7941          =>  :yank,
         | 
| 100 | 
            -
                7942          =>  :power,
         | 
| 101 | 
            -
                7959          =>  :pressure,
         | 
| 102 | 
            -
                7962          =>  :energy,
         | 
| 103 | 
            -
                7979          =>  :viscosity,
         | 
| 104 | 
            -
                7961          =>  :force,
         | 
| 105 | 
            -
                7981          =>  :momentum,
         | 
| 106 | 
            -
                7982          =>  :angular_momentum,
         | 
| 107 | 
            -
                7997          =>  :density,
         | 
| 108 | 
            -
                7998          =>  :area_density,
         | 
| 109 | 
            -
                8000          =>  :mass,
         | 
| 110 | 
            -
                152020        =>  :radiation_exposure,
         | 
| 111 | 
            -
                159999        =>  :magnetism,
         | 
| 112 | 
            -
                160000        =>  :current,
         | 
| 113 | 
            -
                160020        =>  :charge,
         | 
| 114 | 
            -
                312058        =>  :resistance,
         | 
| 115 | 
            -
                312078        =>  :capacitance,
         | 
| 116 | 
            -
                3199980       =>  :activity,
         | 
| 117 | 
            -
                3199997       =>  :molar_concentration,
         | 
| 118 | 
            -
                3200000       =>  :substance,
         | 
| 119 | 
            -
                63999998      =>  :illuminance,
         | 
| 120 | 
            -
                64000000      =>  :luminous_power,
         | 
| 121 | 
            -
                1280000000    =>  :currency,
         | 
| 122 | 
            -
                25600000000   =>  :memory,
         | 
| 123 | 
            -
                511999999980  =>  :angular_velocity,
         | 
| 124 | 
            -
                512000000000  =>  :angle
         | 
| 125 | 
            -
                }
         | 
| 126 | 
            -
              @@cached_units = {}
         | 
| 127 | 
            -
              @@base_unit_cache = {}
         | 
| 128 | 
            -
             | 
| 129 | 
            -
              # setup internal arrays and hashes
         | 
| 130 | 
            -
              # @return [true]
         | 
| 131 | 
            -
              def self.setup
         | 
| 132 | 
            -
                self.clear_cache
         | 
| 35 | 
            +
            module RubyUnits
         | 
| 36 | 
            +
              class Unit < Numeric
         | 
| 37 | 
            +
                VERSION            = Unit::Version::STRING
         | 
| 38 | 
            +
                @@definitions      = {}
         | 
| 133 39 | 
             
                @@PREFIX_VALUES    = {}
         | 
| 134 40 | 
             
                @@PREFIX_MAP       = {}
         | 
| 135 | 
            -
                @@UNIT_VALUES      = {}
         | 
| 136 41 | 
             
                @@UNIT_MAP         = {}
         | 
| 42 | 
            +
                @@UNIT_VALUES      = {}
         | 
| 137 43 | 
             
                @@UNIT_REGEX       = nil
         | 
| 138 44 | 
             
                @@UNIT_MATCH_REGEX = nil
         | 
| 139 | 
            -
                 | 
| 45 | 
            +
                UNITY              = '<1>'
         | 
| 46 | 
            +
                UNITY_ARRAY        = [UNITY]
         | 
| 47 | 
            +
                FEET_INCH_REGEX    = /(\d+)\s*(?:'|ft|feet)\s*(\d+)\s*(?:"|in|inches)/
         | 
| 48 | 
            +
                TIME_REGEX         = /(\d+)*:(\d+)*:*(\d+)*[:,]*(\d+)*/
         | 
| 49 | 
            +
                LBS_OZ_REGEX       = /(\d+)\s*(?:#|lbs|pounds|pound-mass)+[\s,]*(\d+)\s*(?:oz|ounces)/
         | 
| 50 | 
            +
                SCI_NUMBER         = %r{([+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*)}
         | 
| 51 | 
            +
                RATIONAL_NUMBER    = /\(?([+-]?\d+)\/(\d+)\)?/
         | 
| 52 | 
            +
                COMPLEX_NUMBER     = /#{SCI_NUMBER}?#{SCI_NUMBER}i\b/
         | 
| 53 | 
            +
                NUMBER_REGEX       = /#{SCI_NUMBER}*\s*(.+)?/
         | 
| 54 | 
            +
                UNIT_STRING_REGEX  = /#{SCI_NUMBER}*\s*([^\/]*)\/*(.+)*/
         | 
| 55 | 
            +
                TOP_REGEX          = /([^ \*]+)(?:\^|\*\*)([\d-]+)/
         | 
| 56 | 
            +
                BOTTOM_REGEX       = /([^* ]+)(?:\^|\*\*)(\d+)/
         | 
| 57 | 
            +
                UNCERTAIN_REGEX    = /#{SCI_NUMBER}\s*\+\/-\s*#{SCI_NUMBER}\s(.+)/
         | 
| 58 | 
            +
                COMPLEX_REGEX      = /#{COMPLEX_NUMBER}\s?(.+)?/
         | 
| 59 | 
            +
                RATIONAL_REGEX     = /#{RATIONAL_NUMBER}\s?(.+)?/
         | 
| 60 | 
            +
                KELVIN             = ['<kelvin>']
         | 
| 61 | 
            +
                FAHRENHEIT         = ['<fahrenheit>']
         | 
| 62 | 
            +
                RANKINE            = ['<rankine>']
         | 
| 63 | 
            +
                CELSIUS            = ['<celsius>']
         | 
| 64 | 
            +
                @@TEMP_REGEX       = nil
         | 
| 65 | 
            +
                SIGNATURE_VECTOR   = [
         | 
| 66 | 
            +
                    :length,
         | 
| 67 | 
            +
                    :time,
         | 
| 68 | 
            +
                    :temperature,
         | 
| 69 | 
            +
                    :mass,
         | 
| 70 | 
            +
                    :current,
         | 
| 71 | 
            +
                    :substance,
         | 
| 72 | 
            +
                    :luminosity,
         | 
| 73 | 
            +
                    :currency,
         | 
| 74 | 
            +
                    :information,
         | 
| 75 | 
            +
                    :angle
         | 
| 76 | 
            +
                ]
         | 
| 77 | 
            +
                @@KINDS            = {
         | 
| 78 | 
            +
                    -312078      => :elastance,
         | 
| 79 | 
            +
                    -312058      => :resistance,
         | 
| 80 | 
            +
                    -312038      => :inductance,
         | 
| 81 | 
            +
                    -152040      => :magnetism,
         | 
| 82 | 
            +
                    -152038      => :magnetism,
         | 
| 83 | 
            +
                    -152058      => :potential,
         | 
| 84 | 
            +
                    -7997        => :specific_volume,
         | 
| 85 | 
            +
                    -79          => :snap,
         | 
| 86 | 
            +
                    -59          => :jolt,
         | 
| 87 | 
            +
                    -39          => :acceleration,
         | 
| 88 | 
            +
                    -38          => :radiation,
         | 
| 89 | 
            +
                    -20          => :frequency,
         | 
| 90 | 
            +
                    -19          => :speed,
         | 
| 91 | 
            +
                    -18          => :viscosity,
         | 
| 92 | 
            +
                    -17          => :volumetric_flow,
         | 
| 93 | 
            +
                    -1           => :wavenumber,
         | 
| 94 | 
            +
                    0            => :unitless,
         | 
| 95 | 
            +
                    1            => :length,
         | 
| 96 | 
            +
                    2            => :area,
         | 
| 97 | 
            +
                    3            => :volume,
         | 
| 98 | 
            +
                    20           => :time,
         | 
| 99 | 
            +
                    400          => :temperature,
         | 
| 100 | 
            +
                    7941         => :yank,
         | 
| 101 | 
            +
                    7942         => :power,
         | 
| 102 | 
            +
                    7959         => :pressure,
         | 
| 103 | 
            +
                    7962         => :energy,
         | 
| 104 | 
            +
                    7979         => :viscosity,
         | 
| 105 | 
            +
                    7961         => :force,
         | 
| 106 | 
            +
                    7981         => :momentum,
         | 
| 107 | 
            +
                    7982         => :angular_momentum,
         | 
| 108 | 
            +
                    7997         => :density,
         | 
| 109 | 
            +
                    7998         => :area_density,
         | 
| 110 | 
            +
                    8000         => :mass,
         | 
| 111 | 
            +
                    152020       => :radiation_exposure,
         | 
| 112 | 
            +
                    159999       => :magnetism,
         | 
| 113 | 
            +
                    160000       => :current,
         | 
| 114 | 
            +
                    160020       => :charge,
         | 
| 115 | 
            +
                    312058       => :conductance,
         | 
| 116 | 
            +
                    312078       => :capacitance,
         | 
| 117 | 
            +
                    3199980      => :activity,
         | 
| 118 | 
            +
                    3199997      => :molar_concentration,
         | 
| 119 | 
            +
                    3200000      => :substance,
         | 
| 120 | 
            +
                    63999998     => :illuminance,
         | 
| 121 | 
            +
                    64000000     => :luminous_power,
         | 
| 122 | 
            +
                    1280000000   => :currency,
         | 
| 123 | 
            +
                    25600000000  => :information,
         | 
| 124 | 
            +
                    511999999980 => :angular_velocity,
         | 
| 125 | 
            +
                    512000000000 => :angle
         | 
| 126 | 
            +
                }
         | 
| 127 | 
            +
                @@cached_units     = {}
         | 
| 128 | 
            +
                @@base_unit_cache  = {}
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                # setup internal arrays and hashes
         | 
| 131 | 
            +
                # @return [true]
         | 
| 132 | 
            +
                def self.setup
         | 
| 133 | 
            +
                  self.clear_cache
         | 
| 134 | 
            +
                  @@PREFIX_VALUES    = {}
         | 
| 135 | 
            +
                  @@PREFIX_MAP       = {}
         | 
| 136 | 
            +
                  @@UNIT_VALUES      = {}
         | 
| 137 | 
            +
                  @@UNIT_MAP         = {}
         | 
| 138 | 
            +
                  @@UNIT_REGEX       = nil
         | 
| 139 | 
            +
                  @@UNIT_MATCH_REGEX = nil
         | 
| 140 | 
            +
                  @@PREFIX_REGEX     = nil
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                  @@definitions.each do |name, definition|
         | 
| 143 | 
            +
                    self.use_definition(definition)
         | 
| 144 | 
            +
                  end
         | 
| 140 145 |  | 
| 141 | 
            -
             | 
| 142 | 
            -
                   | 
| 146 | 
            +
                  RubyUnits::Unit.new(1)
         | 
| 147 | 
            +
                  return true
         | 
| 143 148 | 
             
                end
         | 
| 144 149 |  | 
| 145 | 
            -
                Unit.new(1)
         | 
| 146 | 
            -
                return true
         | 
| 147 | 
            -
              end
         | 
| 148 | 
            -
             | 
| 149 150 |  | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 151 | 
            +
                # determine if a unit is already defined
         | 
| 152 | 
            +
                # @param [String] unit
         | 
| 153 | 
            +
                # @return [Boolean]
         | 
| 154 | 
            +
                def self.defined?(unit)
         | 
| 155 | 
            +
                  self.definitions.values.any? { |d| d.aliases.include?(unit) }
         | 
| 156 | 
            +
                end
         | 
| 156 157 |  | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 158 | 
            +
                # return the unit definition for a unit
         | 
| 159 | 
            +
                # @param [String] unit
         | 
| 160 | 
            +
                # @return [RubyUnits::Unit::Definition, nil]
         | 
| 161 | 
            +
                def self.definition(_unit)
         | 
| 162 | 
            +
                  unit = (_unit =~ /^<.+>$/) ? _unit : "<#{_unit}>"
         | 
| 163 | 
            +
                  return @@definitions[unit]
         | 
| 164 | 
            +
                end
         | 
| 164 165 |  | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 167 | 
            -
             | 
| 168 | 
            -
             | 
| 169 | 
            -
             | 
| 166 | 
            +
                # return a list of all defined units
         | 
| 167 | 
            +
                # @return [Array]
         | 
| 168 | 
            +
                def self.definitions
         | 
| 169 | 
            +
                  return @@definitions
         | 
| 170 | 
            +
                end
         | 
| 170 171 |  | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
             | 
| 176 | 
            -
             | 
| 177 | 
            -
             | 
| 178 | 
            -
             | 
| 179 | 
            -
             | 
| 180 | 
            -
             | 
| 181 | 
            -
             | 
| 182 | 
            -
             | 
| 183 | 
            -
             | 
| 184 | 
            -
             | 
| 185 | 
            -
             | 
| 186 | 
            -
             | 
| 187 | 
            -
             | 
| 188 | 
            -
             | 
| 189 | 
            -
             | 
| 190 | 
            -
             | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 193 | 
            -
             | 
| 172 | 
            +
                # @param  [RubyUnits::Unit::Definition|String] unit_definition
         | 
| 173 | 
            +
                # @param  [Block] block
         | 
| 174 | 
            +
                # @return [RubyUnits::Unit::Definition]
         | 
| 175 | 
            +
                # @raise  [ArgumentError] when passed a non-string if using the block form
         | 
| 176 | 
            +
                # Unpack a unit definition and add it to the array of defined units
         | 
| 177 | 
            +
                #
         | 
| 178 | 
            +
                # @example Block form
         | 
| 179 | 
            +
                #   RubyUnits::Unit.define('foobar') do |foobar|
         | 
| 180 | 
            +
                #     foobar.definition = RubyUnits::Unit.new("1 baz")
         | 
| 181 | 
            +
                #   end
         | 
| 182 | 
            +
                #
         | 
| 183 | 
            +
                # @example RubyUnits::Unit::Definition form
         | 
| 184 | 
            +
                #   unit_definition = RubyUnits::Unit::Definition.new("foobar") {|foobar| foobar.definition = RubyUnits::Unit.new("1 baz")}
         | 
| 185 | 
            +
                #   RubyUnits::Unit.define(unit_definition)
         | 
| 186 | 
            +
                def self.define(unit_definition, &block)
         | 
| 187 | 
            +
                  if block_given?
         | 
| 188 | 
            +
                    raise ArgumentError, "When using the block form of RubyUnits::Unit.define, pass the name of the unit" unless unit_definition.instance_of?(String)
         | 
| 189 | 
            +
                    unit_definition = RubyUnits::Unit::Definition.new(unit_definition, &block)
         | 
| 190 | 
            +
                  end
         | 
| 191 | 
            +
                  RubyUnits::Unit.definitions[unit_definition.name] = unit_definition
         | 
| 192 | 
            +
                  RubyUnits::Unit.use_definition(unit_definition)
         | 
| 193 | 
            +
                  return unit_definition
         | 
| 194 | 
            +
                end
         | 
| 194 195 |  | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 197 | 
            -
             | 
| 198 | 
            -
             | 
| 199 | 
            -
             | 
| 200 | 
            -
             | 
| 201 | 
            -
             | 
| 202 | 
            -
             | 
| 203 | 
            -
             | 
| 204 | 
            -
             | 
| 205 | 
            -
             | 
| 206 | 
            -
             | 
| 196 | 
            +
                # @param [String] name Name of unit to redefine
         | 
| 197 | 
            +
                # @param [Block] block
         | 
| 198 | 
            +
                # @raise [ArgumentError] if a block is not given
         | 
| 199 | 
            +
                # @yield [RubyUnits::Unit::Definition]
         | 
| 200 | 
            +
                # @return (see RubyUnits::Unit.define)
         | 
| 201 | 
            +
                # Get the definition for a unit and allow it to be redefined
         | 
| 202 | 
            +
                def self.redefine!(name, &block)
         | 
| 203 | 
            +
                  raise ArgumentError, "A block is required to redefine a unit" unless block_given?
         | 
| 204 | 
            +
                  unit_definition = self.definition(name)
         | 
| 205 | 
            +
                  yield unit_definition
         | 
| 206 | 
            +
                  self.define(unit_definition)
         | 
| 207 | 
            +
                end
         | 
| 207 208 |  | 
| 208 | 
            -
             | 
| 209 | 
            -
             | 
| 210 | 
            -
             | 
| 211 | 
            -
             | 
| 212 | 
            -
             | 
| 213 | 
            -
             | 
| 214 | 
            -
             | 
| 209 | 
            +
                # @param [String] name of unit to undefine
         | 
| 210 | 
            +
                # @return (see RubyUnits::Unit.setup)
         | 
| 211 | 
            +
                # Undefine a unit.  Will not raise an exception for unknown units.
         | 
| 212 | 
            +
                def self.undefine!(unit)
         | 
| 213 | 
            +
                  @@definitions.delete("<#{unit}>")
         | 
| 214 | 
            +
                  RubyUnits::Unit.setup
         | 
| 215 | 
            +
                end
         | 
| 215 216 |  | 
| 216 | 
            -
             | 
| 217 | 
            +
                include Comparable
         | 
| 217 218 |  | 
| 218 | 
            -
             | 
| 219 | 
            -
             | 
| 219 | 
            +
                # @return [Numeric]
         | 
| 220 | 
            +
                attr_accessor :scalar
         | 
| 220 221 |  | 
| 221 | 
            -
             | 
| 222 | 
            -
             | 
| 222 | 
            +
                # @return [Array]
         | 
| 223 | 
            +
                attr_accessor :numerator
         | 
| 223 224 |  | 
| 224 | 
            -
             | 
| 225 | 
            -
             | 
| 225 | 
            +
                # @return [Array]
         | 
| 226 | 
            +
                attr_accessor :denominator
         | 
| 226 227 |  | 
| 227 | 
            -
             | 
| 228 | 
            -
             | 
| 228 | 
            +
                # @return [Integer]
         | 
| 229 | 
            +
                attr_accessor :signature
         | 
| 229 230 |  | 
| 230 | 
            -
             | 
| 231 | 
            -
             | 
| 231 | 
            +
                # @return [Numeric]
         | 
| 232 | 
            +
                attr_accessor :base_scalar
         | 
| 232 233 |  | 
| 233 | 
            -
             | 
| 234 | 
            -
             | 
| 234 | 
            +
                # @return [Array]
         | 
| 235 | 
            +
                attr_accessor :base_numerator
         | 
| 235 236 |  | 
| 236 | 
            -
             | 
| 237 | 
            -
             | 
| 237 | 
            +
                # @return [Array]
         | 
| 238 | 
            +
                attr_accessor :base_denominator
         | 
| 238 239 |  | 
| 239 | 
            -
             | 
| 240 | 
            -
             | 
| 240 | 
            +
                # @return [String]
         | 
| 241 | 
            +
                attr_accessor :output
         | 
| 241 242 |  | 
| 242 | 
            -
             | 
| 243 | 
            -
             | 
| 243 | 
            +
                # @return [String]
         | 
| 244 | 
            +
                attr_accessor :unit_name
         | 
| 244 245 |  | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 247 | 
            -
             | 
| 248 | 
            -
             | 
| 249 | 
            -
             | 
| 250 | 
            -
             | 
| 246 | 
            +
                # needed to make complex units play nice -- otherwise not detected as a complex_generic
         | 
| 247 | 
            +
                # @param [Class]
         | 
| 248 | 
            +
                # @return [Boolean]
         | 
| 249 | 
            +
                def kind_of?(klass)
         | 
| 250 | 
            +
                  self.scalar.kind_of?(klass)
         | 
| 251 | 
            +
                end
         | 
| 251 252 |  | 
| 252 | 
            -
             | 
| 253 | 
            -
             | 
| 254 | 
            -
             | 
| 255 | 
            -
             | 
| 256 | 
            -
             | 
| 257 | 
            -
             | 
| 258 | 
            -
             | 
| 259 | 
            -
             | 
| 260 | 
            -
             | 
| 261 | 
            -
             | 
| 262 | 
            -
             | 
| 263 | 
            -
             | 
| 264 | 
            -
             | 
| 253 | 
            +
                # Used to copy one unit to another
         | 
| 254 | 
            +
                # @param [Unit] from Unit to copy definition from
         | 
| 255 | 
            +
                # @return [Unit]
         | 
| 256 | 
            +
                def copy(from)
         | 
| 257 | 
            +
                  @scalar      = from.scalar
         | 
| 258 | 
            +
                  @numerator   = from.numerator
         | 
| 259 | 
            +
                  @denominator = from.denominator
         | 
| 260 | 
            +
                  @is_base     = from.is_base?
         | 
| 261 | 
            +
                  @signature   = from.signature
         | 
| 262 | 
            +
                  @base_scalar = from.base_scalar
         | 
| 263 | 
            +
                  @unit_name = from.unit_name rescue nil
         | 
| 264 | 
            +
                  return self
         | 
| 265 | 
            +
                end
         | 
| 265 266 |  | 
| 266 | 
            -
             | 
| 267 | 
            -
             | 
| 267 | 
            +
                if RUBY_VERSION < "1.9"
         | 
| 268 | 
            +
                  # :nocov_19:
         | 
| 268 269 |  | 
| 269 | 
            -
             | 
| 270 | 
            -
             | 
| 271 | 
            -
             | 
| 272 | 
            -
             | 
| 273 | 
            -
             | 
| 270 | 
            +
                  # a list of properties to emit to yaml
         | 
| 271 | 
            +
                  # @return [Array]
         | 
| 272 | 
            +
                  def to_yaml_properties
         | 
| 273 | 
            +
                    %w{@scalar @numerator @denominator @signature @base_scalar}
         | 
| 274 | 
            +
                  end
         | 
| 274 275 |  | 
| 275 | 
            -
             | 
| 276 | 
            -
             | 
| 277 | 
            -
             | 
| 278 | 
            -
             | 
| 279 | 
            -
             | 
| 280 | 
            -
             | 
| 281 | 
            -
             | 
| 282 | 
            -
             | 
| 283 | 
            -
             | 
| 276 | 
            +
                  # basically a copy of the basic to_yaml.  Needed because otherwise it ends up coercing the object to a string
         | 
| 277 | 
            +
                  # before YAML'izing it.
         | 
| 278 | 
            +
                  # @param [Hash] opts
         | 
| 279 | 
            +
                  # @return [String]
         | 
| 280 | 
            +
                  def to_yaml(opts = {})
         | 
| 281 | 
            +
                    YAML::quick_emit(object_id, opts) do |out|
         | 
| 282 | 
            +
                      out.map(taguri, to_yaml_style) do |map|
         | 
| 283 | 
            +
                        for m in to_yaml_properties do
         | 
| 284 | 
            +
                          map.add(m[1..-1], instance_variable_get(m))
         | 
| 285 | 
            +
                        end
         | 
| 284 286 | 
             
                      end
         | 
| 285 287 | 
             
                    end
         | 
| 286 288 | 
             
                  end
         | 
| 289 | 
            +
                  # :nocov_19:
         | 
| 287 290 | 
             
                end
         | 
| 288 | 
            -
                # :nocov_19:
         | 
| 289 | 
            -
              end
         | 
| 290 291 |  | 
| 291 | 
            -
             | 
| 292 | 
            -
             | 
| 293 | 
            -
             | 
| 294 | 
            -
             | 
| 295 | 
            -
             | 
| 296 | 
            -
             | 
| 297 | 
            -
             | 
| 298 | 
            -
             | 
| 299 | 
            -
             | 
| 300 | 
            -
             | 
| 301 | 
            -
             | 
| 302 | 
            -
             | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
             | 
| 306 | 
            -
             | 
| 307 | 
            -
             | 
| 308 | 
            -
             | 
| 309 | 
            -
             | 
| 310 | 
            -
             | 
| 311 | 
            -
             | 
| 312 | 
            -
             | 
| 313 | 
            -
             | 
| 314 | 
            -
             | 
| 315 | 
            -
             | 
| 316 | 
            -
             | 
| 317 | 
            -
             | 
| 318 | 
            -
             | 
| 319 | 
            -
             | 
| 320 | 
            -
             | 
| 321 | 
            -
             | 
| 322 | 
            -
             | 
| 323 | 
            -
             | 
| 324 | 
            -
             | 
| 325 | 
            -
             | 
| 292 | 
            +
                # Create a new Unit object.  Can be initialized using a String, a Hash, an Array, Time, DateTime
         | 
| 293 | 
            +
                #
         | 
| 294 | 
            +
                # @example Valid options include:
         | 
| 295 | 
            +
                #  "5.6 kg*m/s^2"
         | 
| 296 | 
            +
                #  "5.6 kg*m*s^-2"
         | 
| 297 | 
            +
                #  "5.6 kilogram*meter*second^-2"
         | 
| 298 | 
            +
                #  "2.2 kPa"
         | 
| 299 | 
            +
                #  "37 degC"
         | 
| 300 | 
            +
                #  "1"  -- creates a unitless constant with value 1
         | 
| 301 | 
            +
                #  "GPa"  -- creates a unit with scalar 1 with units 'GPa'
         | 
| 302 | 
            +
                #  "6'4\"""  -- recognized as 6 feet + 4 inches
         | 
| 303 | 
            +
                #  "8 lbs 8 oz" -- recognized as 8 lbs + 8 ounces
         | 
| 304 | 
            +
                #  [1, 'kg']
         | 
| 305 | 
            +
                #  {:scalar => 1, :numerator=>'kg'}
         | 
| 306 | 
            +
                #
         | 
| 307 | 
            +
                # @param [Unit,String,Hash,Array,Date,Time,DateTime] options
         | 
| 308 | 
            +
                # @return [Unit]
         | 
| 309 | 
            +
                # @raise [ArgumentError] if absolute value of a temperature is less than absolute zero
         | 
| 310 | 
            +
                # @raise [ArgumentError] if no unit is specified
         | 
| 311 | 
            +
                # @raise [ArgumentError] if an invalid unit is specified
         | 
| 312 | 
            +
                def initialize(*options)
         | 
| 313 | 
            +
                  @scalar      = nil
         | 
| 314 | 
            +
                  @base_scalar = nil
         | 
| 315 | 
            +
                  @unit_name   = nil
         | 
| 316 | 
            +
                  @signature   = nil
         | 
| 317 | 
            +
                  @output      = {}
         | 
| 318 | 
            +
                  raise ArgumentError, "Invalid Unit Format" if options[0].nil?
         | 
| 319 | 
            +
                  if options.size == 2
         | 
| 320 | 
            +
                    # options[0] is the scalar
         | 
| 321 | 
            +
                    # options[1] is a unit string
         | 
| 322 | 
            +
                    begin
         | 
| 323 | 
            +
                      cached = @@cached_units[options[1]] * options[0]
         | 
| 324 | 
            +
                      copy(cached)
         | 
| 325 | 
            +
                    rescue
         | 
| 326 | 
            +
                      initialize("#{options[0]} #{(options[1].units rescue options[1])}")
         | 
| 327 | 
            +
                    end
         | 
| 328 | 
            +
                    return
         | 
| 326 329 | 
             
                  end
         | 
| 327 | 
            -
                   | 
| 328 | 
            -
             | 
| 329 | 
            -
             | 
| 330 | 
            -
             | 
| 331 | 
            -
             | 
| 332 | 
            -
             | 
| 333 | 
            -
                     | 
| 334 | 
            -
             | 
| 335 | 
            -
             | 
| 336 | 
            -
                     | 
| 330 | 
            +
                  if options.size == 3
         | 
| 331 | 
            +
                    options[1] = options[1].join if options[1].kind_of?(Array)
         | 
| 332 | 
            +
                    options[2] = options[2].join if options[2].kind_of?(Array)
         | 
| 333 | 
            +
                    begin
         | 
| 334 | 
            +
                      cached = @@cached_units["#{options[1]}/#{options[2]}"] * options[0]
         | 
| 335 | 
            +
                      copy(cached)
         | 
| 336 | 
            +
                    rescue
         | 
| 337 | 
            +
                      initialize("#{options[0]} #{options[1]}/#{options[2]}")
         | 
| 338 | 
            +
                    end
         | 
| 339 | 
            +
                    return
         | 
| 337 340 | 
             
                  end
         | 
| 338 | 
            -
                  return
         | 
| 339 | 
            -
                end
         | 
| 340 341 |  | 
| 341 | 
            -
             | 
| 342 | 
            -
             | 
| 343 | 
            -
             | 
| 344 | 
            -
             | 
| 345 | 
            -
             | 
| 346 | 
            -
             | 
| 347 | 
            -
             | 
| 348 | 
            -
             | 
| 349 | 
            -
             | 
| 350 | 
            -
             | 
| 351 | 
            -
             | 
| 352 | 
            -
             | 
| 353 | 
            -
             | 
| 354 | 
            -
             | 
| 355 | 
            -
             | 
| 356 | 
            -
             | 
| 357 | 
            -
             | 
| 358 | 
            -
             | 
| 359 | 
            -
             | 
| 360 | 
            -
             | 
| 361 | 
            -
             | 
| 362 | 
            -
             | 
| 363 | 
            -
             | 
| 364 | 
            -
             | 
| 365 | 
            -
             | 
| 366 | 
            -
             | 
| 367 | 
            -
             | 
| 368 | 
            -
             | 
| 369 | 
            -
             | 
| 370 | 
            -
                end
         | 
| 371 | 
            -
                self.update_base_scalar
         | 
| 372 | 
            -
                raise ArgumentError, "Temperatures must not be less than absolute zero" if self.is_temperature? &&  self.base_scalar < 0
         | 
| 373 | 
            -
                unary_unit = self.units || ""
         | 
| 374 | 
            -
                if options.first.instance_of?(String)
         | 
| 375 | 
            -
                  opt_scalar, opt_units = Unit.parse_into_numbers_and_units(options[0])
         | 
| 376 | 
            -
                  unless @@cached_units.keys.include?(opt_units) || (opt_units =~ /(#{Unit.temp_regex})|(pounds|lbs[ ,]\d+ ounces|oz)|('\d+")|(ft|feet[ ,]\d+ in|inch|inches)|%|(#{TIME_REGEX})|i\s?(.+)?|±|\+\/-/)
         | 
| 377 | 
            -
                    @@cached_units[opt_units] = (self.scalar == 1 ? self : opt_units.unit) if opt_units && !opt_units.empty?
         | 
| 342 | 
            +
                  case options[0]
         | 
| 343 | 
            +
                    when Unit
         | 
| 344 | 
            +
                      copy(options[0])
         | 
| 345 | 
            +
                      return
         | 
| 346 | 
            +
                    when Hash
         | 
| 347 | 
            +
                      @scalar      = options[0][:scalar] || 1
         | 
| 348 | 
            +
                      @numerator   = options[0][:numerator] || UNITY_ARRAY
         | 
| 349 | 
            +
                      @denominator = options[0][:denominator] || UNITY_ARRAY
         | 
| 350 | 
            +
                      @signature   = options[0][:signature]
         | 
| 351 | 
            +
                    when Array
         | 
| 352 | 
            +
                      initialize(*options[0])
         | 
| 353 | 
            +
                      return
         | 
| 354 | 
            +
                    when Numeric
         | 
| 355 | 
            +
                      @scalar    = options[0]
         | 
| 356 | 
            +
                      @numerator = @denominator = UNITY_ARRAY
         | 
| 357 | 
            +
                    when Time
         | 
| 358 | 
            +
                      @scalar      = options[0].to_f
         | 
| 359 | 
            +
                      @numerator   = ['<second>']
         | 
| 360 | 
            +
                      @denominator = UNITY_ARRAY
         | 
| 361 | 
            +
                    when DateTime, Date
         | 
| 362 | 
            +
                      @scalar      = options[0].ajd
         | 
| 363 | 
            +
                      @numerator   = ['<day>']
         | 
| 364 | 
            +
                      @denominator = UNITY_ARRAY
         | 
| 365 | 
            +
                    when /^\s*$/
         | 
| 366 | 
            +
                      raise ArgumentError, "No Unit Specified"
         | 
| 367 | 
            +
                    when String
         | 
| 368 | 
            +
                      parse(options[0])
         | 
| 369 | 
            +
                    else
         | 
| 370 | 
            +
                      raise ArgumentError, "Invalid Unit Format"
         | 
| 378 371 | 
             
                  end
         | 
| 372 | 
            +
                  self.update_base_scalar
         | 
| 373 | 
            +
                  raise ArgumentError, "Temperatures must not be less than absolute zero" if self.is_temperature? && self.base_scalar < 0
         | 
| 374 | 
            +
                  unary_unit = self.units || ""
         | 
| 375 | 
            +
                  if options.first.instance_of?(String)
         | 
| 376 | 
            +
                    opt_scalar, opt_units = RubyUnits::Unit.parse_into_numbers_and_units(options[0])
         | 
| 377 | 
            +
                    unless @@cached_units.keys.include?(opt_units) || (opt_units =~ /(#{RubyUnits::Unit.temp_regex})|(pounds|lbs[ ,]\d+ ounces|oz)|('\d+")|(ft|feet[ ,]\d+ in|inch|inches)|%|(#{TIME_REGEX})|i\s?(.+)?|±|\+\/-/)
         | 
| 378 | 
            +
                      @@cached_units[opt_units] = (self.scalar == 1 ? self : opt_units.unit) if opt_units && !opt_units.empty?
         | 
| 379 | 
            +
                    end
         | 
| 380 | 
            +
                  end
         | 
| 381 | 
            +
                  unless @@cached_units.keys.include?(unary_unit) || (unary_unit =~ /#{RubyUnits::Unit.temp_regex}/) then
         | 
| 382 | 
            +
                    @@cached_units[unary_unit] = (self.scalar == 1 ? self : unary_unit.unit)
         | 
| 383 | 
            +
                  end
         | 
| 384 | 
            +
                  [@scalar, @numerator, @denominator, @base_scalar, @signature, @is_base].each { |x| x.freeze }
         | 
| 385 | 
            +
                  return self
         | 
| 379 386 | 
             
                end
         | 
| 380 | 
            -
                unless @@cached_units.keys.include?(unary_unit) || (unary_unit =~ /#{Unit.temp_regex}/) then
         | 
| 381 | 
            -
                  @@cached_units[unary_unit] = (self.scalar == 1 ? self : unary_unit.unit)
         | 
| 382 | 
            -
                end
         | 
| 383 | 
            -
                [@scalar, @numerator, @denominator, @base_scalar, @signature, @is_base].each {|x| x.freeze}
         | 
| 384 | 
            -
                return self
         | 
| 385 | 
            -
              end
         | 
| 386 387 |  | 
| 387 | 
            -
             | 
| 388 | 
            -
             | 
| 389 | 
            -
             | 
| 390 | 
            -
             | 
| 391 | 
            -
             | 
| 392 | 
            -
             | 
| 388 | 
            +
                # @todo: figure out how to handle :counting units.  This method should probably return :counting instead of :unitless for 'each'
         | 
| 389 | 
            +
                # return the kind of the unit (:mass, :length, etc...)
         | 
| 390 | 
            +
                # @return [Symbol]
         | 
| 391 | 
            +
                def kind
         | 
| 392 | 
            +
                  return @@KINDS[self.signature]
         | 
| 393 | 
            +
                end
         | 
| 393 394 |  | 
| 394 | 
            -
             | 
| 395 | 
            -
             | 
| 396 | 
            -
             | 
| 397 | 
            -
             | 
| 398 | 
            -
             | 
| 395 | 
            +
                # @private
         | 
| 396 | 
            +
                # @return [Hash]
         | 
| 397 | 
            +
                def self.cached
         | 
| 398 | 
            +
                  return @@cached_units
         | 
| 399 | 
            +
                end
         | 
| 399 400 |  | 
| 400 | 
            -
             | 
| 401 | 
            -
             | 
| 402 | 
            -
             | 
| 403 | 
            -
             | 
| 404 | 
            -
             | 
| 405 | 
            -
             | 
| 406 | 
            -
             | 
| 407 | 
            -
             | 
| 401 | 
            +
                # @private
         | 
| 402 | 
            +
                # @return [true]
         | 
| 403 | 
            +
                def self.clear_cache
         | 
| 404 | 
            +
                  @@cached_units    = {}
         | 
| 405 | 
            +
                  @@base_unit_cache = {}
         | 
| 406 | 
            +
                  RubyUnits::Unit.new(1)
         | 
| 407 | 
            +
                  return true
         | 
| 408 | 
            +
                end
         | 
| 408 409 |  | 
| 409 | 
            -
             | 
| 410 | 
            -
             | 
| 411 | 
            -
             | 
| 412 | 
            -
             | 
| 413 | 
            -
             | 
| 410 | 
            +
                # @private
         | 
| 411 | 
            +
                # @return [Hash]
         | 
| 412 | 
            +
                def self.base_unit_cache
         | 
| 413 | 
            +
                  return @@base_unit_cache
         | 
| 414 | 
            +
                end
         | 
| 414 415 |  | 
| 415 | 
            -
             | 
| 416 | 
            -
             | 
| 417 | 
            -
             | 
| 418 | 
            -
             | 
| 419 | 
            -
             | 
| 420 | 
            -
             | 
| 421 | 
            -
             | 
| 422 | 
            -
             | 
| 416 | 
            +
                # @example parse strings
         | 
| 417 | 
            +
                #   "1 minute in seconds"
         | 
| 418 | 
            +
                # @param [String] input
         | 
| 419 | 
            +
                # @return [Unit]
         | 
| 420 | 
            +
                def self.parse(input)
         | 
| 421 | 
            +
                  first, second = input.scan(/(.+)\s(?:in|to|as)\s(.+)/i).first
         | 
| 422 | 
            +
                  return second.nil? ? first.unit : first.unit.convert_to(second)
         | 
| 423 | 
            +
                end
         | 
| 423 424 |  | 
| 424 | 
            -
             | 
| 425 | 
            -
             | 
| 426 | 
            -
             | 
| 427 | 
            -
              end
         | 
| 428 | 
            -
              alias :unit :to_unit
         | 
| 429 | 
            -
             | 
| 430 | 
            -
              # Is this unit in base form?
         | 
| 431 | 
            -
              # @return [Boolean]
         | 
| 432 | 
            -
              def is_base?
         | 
| 433 | 
            -
                return @is_base if defined? @is_base
         | 
| 434 | 
            -
                @is_base = (@numerator + @denominator).compact.uniq.
         | 
| 435 | 
            -
                                                        map {|unit| Unit.definition(unit)}.
         | 
| 436 | 
            -
                                                        all? {|element| element.unity? || element.base? }
         | 
| 437 | 
            -
                return @is_base
         | 
| 438 | 
            -
              end
         | 
| 439 | 
            -
              alias :base? :is_base?
         | 
| 440 | 
            -
             | 
| 441 | 
            -
              # convert to base SI units
         | 
| 442 | 
            -
              # results of the conversion are cached so subsequent calls to this will be fast
         | 
| 443 | 
            -
              # @return [Unit]
         | 
| 444 | 
            -
              # @todo this is brittle as it depends on the display_name of a unit, which can be changed
         | 
| 445 | 
            -
              def to_base
         | 
| 446 | 
            -
                return self if self.is_base?
         | 
| 447 | 
            -
                if @@UNIT_MAP[self.units] =~ /\A<(?:temp|deg)[CRF]>\Z/
         | 
| 448 | 
            -
                  if RUBY_VERSION < "1.9"
         | 
| 449 | 
            -
                    # :nocov_19:
         | 
| 450 | 
            -
                    @signature = @@KINDS.index(:temperature)
         | 
| 451 | 
            -
                    # :nocov_19:
         | 
| 452 | 
            -
                  else
         | 
| 453 | 
            -
                    #:nocov:
         | 
| 454 | 
            -
                    @signature = @@KINDS.key(:temperature)
         | 
| 455 | 
            -
                    #:nocov:
         | 
| 456 | 
            -
                  end
         | 
| 457 | 
            -
                  base = case
         | 
| 458 | 
            -
                  when self.is_temperature?
         | 
| 459 | 
            -
                    self.convert_to('tempK')
         | 
| 460 | 
            -
                  when self.is_degree?
         | 
| 461 | 
            -
                    self.convert_to('degK')
         | 
| 462 | 
            -
                  end
         | 
| 463 | 
            -
                  return base
         | 
| 425 | 
            +
                # @return [Unit]
         | 
| 426 | 
            +
                def to_unit
         | 
| 427 | 
            +
                  self
         | 
| 464 428 | 
             
                end
         | 
| 465 429 |  | 
| 466 | 
            -
                 | 
| 467 | 
            -
                return cached if cached
         | 
| 430 | 
            +
                alias :unit :to_unit
         | 
| 468 431 |  | 
| 469 | 
            -
                 | 
| 470 | 
            -
                 | 
| 471 | 
            -
                 | 
| 472 | 
            -
             | 
| 473 | 
            -
                   | 
| 474 | 
            -
             | 
| 475 | 
            -
             | 
| 476 | 
            -
             | 
| 477 | 
            -
                    num << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
         | 
| 478 | 
            -
                    den << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
         | 
| 479 | 
            -
                  end
         | 
| 432 | 
            +
                # Is this unit in base form?
         | 
| 433 | 
            +
                # @return [Boolean]
         | 
| 434 | 
            +
                def is_base?
         | 
| 435 | 
            +
                  return @is_base if defined? @is_base
         | 
| 436 | 
            +
                  @is_base = (@numerator + @denominator).compact.uniq.
         | 
| 437 | 
            +
                      map { |unit| RubyUnits::Unit.definition(unit) }.
         | 
| 438 | 
            +
                      all? { |element| element.unity? || element.base? }
         | 
| 439 | 
            +
                  return @is_base
         | 
| 480 440 | 
             
                end
         | 
| 481 | 
            -
             | 
| 482 | 
            -
             | 
| 483 | 
            -
             | 
| 484 | 
            -
             | 
| 485 | 
            -
             | 
| 486 | 
            -
             | 
| 487 | 
            -
             | 
| 441 | 
            +
             | 
| 442 | 
            +
                alias :base? :is_base?
         | 
| 443 | 
            +
             | 
| 444 | 
            +
                # convert to base SI units
         | 
| 445 | 
            +
                # results of the conversion are cached so subsequent calls to this will be fast
         | 
| 446 | 
            +
                # @return [Unit]
         | 
| 447 | 
            +
                # @todo this is brittle as it depends on the display_name of a unit, which can be changed
         | 
| 448 | 
            +
                def to_base
         | 
| 449 | 
            +
                  return self if self.is_base?
         | 
| 450 | 
            +
                  if @@UNIT_MAP[self.units] =~ /\A<(?:temp|deg)[CRF]>\Z/
         | 
| 451 | 
            +
                    if RUBY_VERSION < "1.9"
         | 
| 452 | 
            +
                      # :nocov_19:
         | 
| 453 | 
            +
                      @signature = @@KINDS.index(:temperature)
         | 
| 454 | 
            +
                      # :nocov_19:
         | 
| 455 | 
            +
                    else
         | 
| 456 | 
            +
                      #:nocov:
         | 
| 457 | 
            +
                      @signature = @@KINDS.key(:temperature)
         | 
| 458 | 
            +
                      #:nocov:
         | 
| 459 | 
            +
                    end
         | 
| 460 | 
            +
                    base = case
         | 
| 461 | 
            +
                             when self.is_temperature?
         | 
| 462 | 
            +
                               self.convert_to('tempK')
         | 
| 463 | 
            +
                             when self.is_degree?
         | 
| 464 | 
            +
                               self.convert_to('degK')
         | 
| 465 | 
            +
                           end
         | 
| 466 | 
            +
                    return base
         | 
| 488 467 | 
             
                  end
         | 
| 489 | 
            -
                end
         | 
| 490 468 |  | 
| 491 | 
            -
             | 
| 492 | 
            -
             | 
| 493 | 
            -
             | 
| 494 | 
            -
             | 
| 495 | 
            -
             | 
| 496 | 
            -
             | 
| 497 | 
            -
             | 
| 498 | 
            -
             | 
| 499 | 
            -
             | 
| 500 | 
            -
              # Generate human readable output.
         | 
| 501 | 
            -
              # If the name of a unit is passed, the unit will first be converted to the target unit before output.
         | 
| 502 | 
            -
              # some named conversions are available
         | 
| 503 | 
            -
              #
         | 
| 504 | 
            -
              # @example
         | 
| 505 | 
            -
              #  unit.to_s(:ft) - outputs in feet and inches (e.g., 6'4")
         | 
| 506 | 
            -
              #  unit.to_s(:lbs) - outputs in pounds and ounces (e.g, 8 lbs, 8 oz)
         | 
| 507 | 
            -
              #
         | 
| 508 | 
            -
              # You can also pass a standard format string (i.e., '%0.2f')
         | 
| 509 | 
            -
              # or a strftime format string.
         | 
| 510 | 
            -
              #
         | 
| 511 | 
            -
              # output is cached so subsequent calls for the same format will be fast
         | 
| 512 | 
            -
              #
         | 
| 513 | 
            -
              # @param [Symbol] target_units
         | 
| 514 | 
            -
              # @return [String]
         | 
| 515 | 
            -
              def to_s(target_units=nil)
         | 
| 516 | 
            -
                out = @output[target_units]
         | 
| 517 | 
            -
                if out
         | 
| 518 | 
            -
                  return out
         | 
| 519 | 
            -
                else
         | 
| 520 | 
            -
                  case target_units
         | 
| 521 | 
            -
                  when :ft
         | 
| 522 | 
            -
                    inches = self.convert_to("in").scalar.to_int
         | 
| 523 | 
            -
                    out = "#{(inches / 12).truncate}\'#{(inches % 12).round}\""
         | 
| 524 | 
            -
                  when :lbs
         | 
| 525 | 
            -
                    ounces = self.convert_to("oz").scalar.to_int
         | 
| 526 | 
            -
                    out = "#{(ounces / 16).truncate} lbs, #{(ounces % 16).round} oz"
         | 
| 527 | 
            -
                  when String
         | 
| 528 | 
            -
                    out = case target_units
         | 
| 529 | 
            -
                    when /(%[\-+\.\w#]+)\s*(.+)*/       #format string like '%0.2f in'
         | 
| 530 | 
            -
                      begin
         | 
| 531 | 
            -
                        if $2 #unit specified, need to convert
         | 
| 532 | 
            -
                          self.convert_to($2).to_s($1)
         | 
| 533 | 
            -
                        else
         | 
| 534 | 
            -
                          "#{$1 % @scalar} #{$2 || self.units}".strip
         | 
| 535 | 
            -
                        end
         | 
| 536 | 
            -
                      rescue # parse it like a strftime format string
         | 
| 537 | 
            -
                        (DateTime.new(0) + self).strftime(target_units)
         | 
| 538 | 
            -
                      end
         | 
| 539 | 
            -
                    when /(\S+)/ #unit only 'mm' or '1/mm'
         | 
| 540 | 
            -
                      self.convert_to($1).to_s
         | 
| 469 | 
            +
                  cached = ((@@base_unit_cache[self.units] * self.scalar) rescue nil)
         | 
| 470 | 
            +
                  return cached if cached
         | 
| 471 | 
            +
             | 
| 472 | 
            +
                  num = []
         | 
| 473 | 
            +
                  den = []
         | 
| 474 | 
            +
                  q   = 1
         | 
| 475 | 
            +
                  for unit in @numerator.compact do
         | 
| 476 | 
            +
                    if @@PREFIX_VALUES[unit]
         | 
| 477 | 
            +
                      q *= @@PREFIX_VALUES[unit]
         | 
| 541 478 | 
             
                    else
         | 
| 542 | 
            -
                       | 
| 479 | 
            +
                      q *= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
         | 
| 480 | 
            +
                      num << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
         | 
| 481 | 
            +
                      den << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
         | 
| 543 482 | 
             
                    end
         | 
| 544 | 
            -
                   | 
| 545 | 
            -
             | 
| 546 | 
            -
                     | 
| 547 | 
            -
                       | 
| 483 | 
            +
                  end
         | 
| 484 | 
            +
                  for unit in @denominator.compact do
         | 
| 485 | 
            +
                    if @@PREFIX_VALUES[unit]
         | 
| 486 | 
            +
                      q /= @@PREFIX_VALUES[unit]
         | 
| 548 487 | 
             
                    else
         | 
| 549 | 
            -
                       | 
| 550 | 
            -
             | 
| 488 | 
            +
                      q /= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
         | 
| 489 | 
            +
                      den << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
         | 
| 490 | 
            +
                      num << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
         | 
| 491 | 
            +
                    end
         | 
| 551 492 | 
             
                  end
         | 
| 552 | 
            -
             | 
| 553 | 
            -
                   | 
| 493 | 
            +
             | 
| 494 | 
            +
                  num = num.flatten.compact
         | 
| 495 | 
            +
                  den = den.flatten.compact
         | 
| 496 | 
            +
                  num = UNITY_ARRAY if num.empty?
         | 
| 497 | 
            +
                  base                         = RubyUnits::Unit.new(RubyUnits::Unit.eliminate_terms(q, num, den))
         | 
| 498 | 
            +
                  @@base_unit_cache[self.units]=base
         | 
| 499 | 
            +
                  return base * @scalar
         | 
| 554 500 | 
             
                end
         | 
| 555 | 
            -
              end
         | 
| 556 501 |  | 
| 557 | 
            -
             | 
| 558 | 
            -
             | 
| 559 | 
            -
             | 
| 560 | 
            -
             | 
| 561 | 
            -
                 | 
| 562 | 
            -
                 | 
| 563 | 
            -
             | 
| 502 | 
            +
                alias :base :to_base
         | 
| 503 | 
            +
             | 
| 504 | 
            +
                # Generate human readable output.
         | 
| 505 | 
            +
                # If the name of a unit is passed, the unit will first be converted to the target unit before output.
         | 
| 506 | 
            +
                # some named conversions are available
         | 
| 507 | 
            +
                #
         | 
| 508 | 
            +
                # @example
         | 
| 509 | 
            +
                #  unit.to_s(:ft) - outputs in feet and inches (e.g., 6'4")
         | 
| 510 | 
            +
                #  unit.to_s(:lbs) - outputs in pounds and ounces (e.g, 8 lbs, 8 oz)
         | 
| 511 | 
            +
                #
         | 
| 512 | 
            +
                # You can also pass a standard format string (i.e., '%0.2f')
         | 
| 513 | 
            +
                # or a strftime format string.
         | 
| 514 | 
            +
                #
         | 
| 515 | 
            +
                # output is cached so subsequent calls for the same format will be fast
         | 
| 516 | 
            +
                #
         | 
| 517 | 
            +
                # @param [Symbol] target_units
         | 
| 518 | 
            +
                # @return [String]
         | 
| 519 | 
            +
                def to_s(target_units=nil)
         | 
| 520 | 
            +
                  out = @output[target_units]
         | 
| 521 | 
            +
                  if out
         | 
| 522 | 
            +
                    return out
         | 
| 523 | 
            +
                  else
         | 
| 524 | 
            +
                    case target_units
         | 
| 525 | 
            +
                      when :ft
         | 
| 526 | 
            +
                        inches = self.convert_to("in").scalar.to_int
         | 
| 527 | 
            +
                        out    = "#{(inches / 12).truncate}\'#{(inches % 12).round}\""
         | 
| 528 | 
            +
                      when :lbs
         | 
| 529 | 
            +
                        ounces = self.convert_to("oz").scalar.to_int
         | 
| 530 | 
            +
                        out    = "#{(ounces / 16).truncate} lbs, #{(ounces % 16).round} oz"
         | 
| 531 | 
            +
                      when String
         | 
| 532 | 
            +
                        out = case target_units
         | 
| 533 | 
            +
                                when /(%[\-+\.\w#]+)\s*(.+)*/ #format string like '%0.2f in'
         | 
| 534 | 
            +
                                  begin
         | 
| 535 | 
            +
                                    if $2 #unit specified, need to convert
         | 
| 536 | 
            +
                                      self.convert_to($2).to_s($1)
         | 
| 537 | 
            +
                                    else
         | 
| 538 | 
            +
                                      "#{$1 % @scalar} #{$2 || self.units}".strip
         | 
| 539 | 
            +
                                    end
         | 
| 540 | 
            +
                                  rescue # parse it like a strftime format string
         | 
| 541 | 
            +
                                    (DateTime.new(0) + self).strftime(target_units)
         | 
| 542 | 
            +
                                  end
         | 
| 543 | 
            +
                                when /(\S+)/ #unit only 'mm' or '1/mm'
         | 
| 544 | 
            +
                                  self.convert_to($1).to_s
         | 
| 545 | 
            +
                                else
         | 
| 546 | 
            +
                                  raise "unhandled case"
         | 
| 547 | 
            +
                              end
         | 
| 548 | 
            +
                      else
         | 
| 549 | 
            +
                        out = case @scalar
         | 
| 550 | 
            +
                                when Rational
         | 
| 551 | 
            +
                                  "#{@scalar} #{self.units}"
         | 
| 552 | 
            +
                                else
         | 
| 553 | 
            +
                                  "#{'%g' % @scalar} #{self.units}"
         | 
| 554 | 
            +
                              end.strip
         | 
| 555 | 
            +
                    end
         | 
| 556 | 
            +
                    @output[target_units] = out
         | 
| 557 | 
            +
                    return out
         | 
| 558 | 
            +
                  end
         | 
| 559 | 
            +
                end
         | 
| 564 560 |  | 
| 565 | 
            -
             | 
| 566 | 
            -
             | 
| 567 | 
            -
             | 
| 568 | 
            -
             | 
| 569 | 
            -
             | 
| 570 | 
            -
             | 
| 571 | 
            -
             | 
| 561 | 
            +
                # Normally pretty prints the unit, but if you really want to see the guts of it, pass ':dump'
         | 
| 562 | 
            +
                # @deprecated
         | 
| 563 | 
            +
                # @return [String]
         | 
| 564 | 
            +
                def inspect(option=nil)
         | 
| 565 | 
            +
                  return super() if option == :dump
         | 
| 566 | 
            +
                  return self.to_s
         | 
| 567 | 
            +
                end
         | 
| 572 568 |  | 
| 573 | 
            -
             | 
| 574 | 
            -
             | 
| 575 | 
            -
             | 
| 576 | 
            -
                 | 
| 577 | 
            -
             | 
| 578 | 
            -
             | 
| 579 | 
            -
             | 
| 580 | 
            -
              # returns the 'degree' unit associated with a temperature unit
         | 
| 581 | 
            -
              # @example '100 tempC'.unit.temperature_scale #=> 'degC'
         | 
| 582 | 
            -
              # @return [String] possible values: degC, degF, degR, or degK
         | 
| 583 | 
            -
              def temperature_scale
         | 
| 584 | 
            -
                return nil unless self.is_temperature?
         | 
| 585 | 
            -
                return "deg#{@@UNIT_MAP[self.units][/temp([CFRK])/,1]}"
         | 
| 586 | 
            -
              end
         | 
| 569 | 
            +
                # true if unit is a 'temperature', false if a 'degree' or anything else
         | 
| 570 | 
            +
                # @return [Boolean]
         | 
| 571 | 
            +
                # @todo use unit definition to determine if it's a temperature instead of a regex
         | 
| 572 | 
            +
                def is_temperature?
         | 
| 573 | 
            +
                  return self.is_degree? && (!(@@UNIT_MAP[self.units] =~ /temp[CFRK]/).nil?)
         | 
| 574 | 
            +
                end
         | 
| 587 575 |  | 
| 588 | 
            -
             | 
| 589 | 
            -
              # false, even if the units are "unitless" like 'radians, each, etc'
         | 
| 590 | 
            -
              # @return [Boolean]
         | 
| 591 | 
            -
              def unitless?
         | 
| 592 | 
            -
                return(@numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY)
         | 
| 593 | 
            -
              end
         | 
| 576 | 
            +
                alias :temperature? :is_temperature?
         | 
| 594 577 |  | 
| 595 | 
            -
             | 
| 596 | 
            -
             | 
| 597 | 
            -
             | 
| 598 | 
            -
             | 
| 599 | 
            -
              # @raise [NoMethodError] when other does not define <=>
         | 
| 600 | 
            -
              # @raise [ArgumentError] when units are not compatible
         | 
| 601 | 
            -
              def <=>(other)
         | 
| 602 | 
            -
                case
         | 
| 603 | 
            -
                when !self.base_scalar.respond_to?(:<=>)
         | 
| 604 | 
            -
                  raise NoMethodError, "undefined method `<=>' for #{self.base_scalar.inspect}"
         | 
| 605 | 
            -
                when other.nil?
         | 
| 606 | 
            -
                  return self.base_scalar <=> nil
         | 
| 607 | 
            -
                when !self.is_temperature? && other.zero?
         | 
| 608 | 
            -
                  return self.base_scalar <=> 0
         | 
| 609 | 
            -
                when other.instance_of?(Unit)
         | 
| 610 | 
            -
                  raise ArgumentError, "Incompatible Units (#{self.units} !~ #{other.units})" unless self =~ other
         | 
| 611 | 
            -
                  return self.base_scalar <=> other.base_scalar
         | 
| 612 | 
            -
                else
         | 
| 613 | 
            -
                  x,y = coerce(other)
         | 
| 614 | 
            -
                  return x <=> y
         | 
| 578 | 
            +
                # true if a degree unit or equivalent.
         | 
| 579 | 
            +
                # @return [Boolean]
         | 
| 580 | 
            +
                def is_degree?
         | 
| 581 | 
            +
                  return self.kind == :temperature
         | 
| 615 582 | 
             
                end
         | 
| 616 | 
            -
              end
         | 
| 617 583 |  | 
| 618 | 
            -
             | 
| 619 | 
            -
             | 
| 620 | 
            -
             | 
| 621 | 
            -
             | 
| 622 | 
            -
             | 
| 623 | 
            -
             | 
| 624 | 
            -
             | 
| 625 | 
            -
             | 
| 626 | 
            -
              def ==(other)
         | 
| 627 | 
            -
                case
         | 
| 628 | 
            -
                when other.respond_to?(:zero?) && other.zero?
         | 
| 629 | 
            -
                  return self.zero?
         | 
| 630 | 
            -
                when other.instance_of?(Unit)
         | 
| 631 | 
            -
                  return false unless self =~ other
         | 
| 632 | 
            -
                  return self.base_scalar == other.base_scalar
         | 
| 633 | 
            -
                else
         | 
| 634 | 
            -
                  begin
         | 
| 635 | 
            -
                    x,y = coerce(other)
         | 
| 636 | 
            -
                    return x == y
         | 
| 637 | 
            -
                  rescue ArgumentError # return false when object cannot be coerced
         | 
| 638 | 
            -
                    return false
         | 
| 639 | 
            -
                  end
         | 
| 584 | 
            +
                alias :degree? :is_degree?
         | 
| 585 | 
            +
             | 
| 586 | 
            +
                # returns the 'degree' unit associated with a temperature unit
         | 
| 587 | 
            +
                # @example '100 tempC'.unit.temperature_scale #=> 'degC'
         | 
| 588 | 
            +
                # @return [String] possible values: degC, degF, degR, or degK
         | 
| 589 | 
            +
                def temperature_scale
         | 
| 590 | 
            +
                  return nil unless self.is_temperature?
         | 
| 591 | 
            +
                  return "deg#{@@UNIT_MAP[self.units][/temp([CFRK])/, 1]}"
         | 
| 640 592 | 
             
                end
         | 
| 641 | 
            -
              end
         | 
| 642 593 |  | 
| 643 | 
            -
             | 
| 644 | 
            -
             | 
| 645 | 
            -
             | 
| 646 | 
            -
             | 
| 647 | 
            -
             | 
| 648 | 
            -
              # @note if you want to do a regexp comparison of the unit string do this ...
         | 
| 649 | 
            -
              #  unit.units =~ /regexp/
         | 
| 650 | 
            -
              # @param [Object] other
         | 
| 651 | 
            -
              # @return [Boolean]
         | 
| 652 | 
            -
              def =~(other)
         | 
| 653 | 
            -
                case other
         | 
| 654 | 
            -
                when Unit
         | 
| 655 | 
            -
                  self.signature == other.signature
         | 
| 656 | 
            -
                else
         | 
| 657 | 
            -
                  begin
         | 
| 658 | 
            -
                    x,y = coerce(other)
         | 
| 659 | 
            -
                    return x =~ y
         | 
| 660 | 
            -
                  rescue ArgumentError
         | 
| 661 | 
            -
                    return false
         | 
| 662 | 
            -
                  end
         | 
| 594 | 
            +
                # returns true if no associated units
         | 
| 595 | 
            +
                # false, even if the units are "unitless" like 'radians, each, etc'
         | 
| 596 | 
            +
                # @return [Boolean]
         | 
| 597 | 
            +
                def unitless?
         | 
| 598 | 
            +
                  return(@numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY)
         | 
| 663 599 | 
             
                end
         | 
| 664 | 
            -
              end
         | 
| 665 600 |  | 
| 666 | 
            -
             | 
| 667 | 
            -
             | 
| 668 | 
            -
             | 
| 669 | 
            -
             | 
| 670 | 
            -
             | 
| 671 | 
            -
             | 
| 672 | 
            -
             | 
| 673 | 
            -
             | 
| 674 | 
            -
             | 
| 675 | 
            -
             | 
| 676 | 
            -
             | 
| 677 | 
            -
             | 
| 678 | 
            -
             | 
| 679 | 
            -
             | 
| 680 | 
            -
             | 
| 681 | 
            -
             | 
| 682 | 
            -
             | 
| 683 | 
            -
             | 
| 684 | 
            -
             | 
| 601 | 
            +
                # Compare two Unit objects. Throws an exception if they are not of compatible types.
         | 
| 602 | 
            +
                # Comparisons are done based on the value of the unit in base SI units.
         | 
| 603 | 
            +
                # @param [Object] other
         | 
| 604 | 
            +
                # @return [-1|0|1|nil]
         | 
| 605 | 
            +
                # @raise [NoMethodError] when other does not define <=>
         | 
| 606 | 
            +
                # @raise [ArgumentError] when units are not compatible
         | 
| 607 | 
            +
                def <=>(other)
         | 
| 608 | 
            +
                  case
         | 
| 609 | 
            +
                    when !self.base_scalar.respond_to?(:<=>)
         | 
| 610 | 
            +
                      raise NoMethodError, "undefined method `<=>' for #{self.base_scalar.inspect}"
         | 
| 611 | 
            +
                    when other.nil?
         | 
| 612 | 
            +
                      return self.base_scalar <=> nil
         | 
| 613 | 
            +
                    when !self.is_temperature? && other.zero?
         | 
| 614 | 
            +
                      return self.base_scalar <=> 0
         | 
| 615 | 
            +
                    when other.instance_of?(Unit)
         | 
| 616 | 
            +
                      raise ArgumentError, "Incompatible Units (#{self.units} !~ #{other.units})" unless self =~ other
         | 
| 617 | 
            +
                      return self.base_scalar <=> other.base_scalar
         | 
| 618 | 
            +
                    else
         | 
| 619 | 
            +
                      x, y = coerce(other)
         | 
| 620 | 
            +
                      return x <=> y
         | 
| 685 621 | 
             
                  end
         | 
| 686 622 | 
             
                end
         | 
| 687 | 
            -
              end
         | 
| 688 623 |  | 
| 689 | 
            -
             | 
| 690 | 
            -
               | 
| 691 | 
            -
             | 
| 692 | 
            -
             | 
| 693 | 
            -
             | 
| 694 | 
            -
             | 
| 695 | 
            -
             | 
| 696 | 
            -
             | 
| 697 | 
            -
             | 
| 698 | 
            -
              # @raise [ArgumentError] when units are not compatible
         | 
| 699 | 
            -
              # @raise [ArgumentError] when adding a fixed time or date to a time span
         | 
| 700 | 
            -
              def +(other)
         | 
| 701 | 
            -
                case other
         | 
| 702 | 
            -
                when Unit
         | 
| 624 | 
            +
                # Compare Units for equality
         | 
| 625 | 
            +
                # this is necessary mostly for Complex units.  Complex units do not have a <=> operator
         | 
| 626 | 
            +
                # so we define this one here so that we can properly check complex units for equality.
         | 
| 627 | 
            +
                # Units of incompatible types are not equal, except when they are both zero and neither is a temperature
         | 
| 628 | 
            +
                # Equality checks can be tricky since round off errors may make essentially equivalent units
         | 
| 629 | 
            +
                # appear to be different.
         | 
| 630 | 
            +
                # @param [Object] other
         | 
| 631 | 
            +
                # @return [Boolean]
         | 
| 632 | 
            +
                def ==(other)
         | 
| 703 633 | 
             
                  case
         | 
| 704 | 
            -
             | 
| 705 | 
            -
             | 
| 706 | 
            -
             | 
| 707 | 
            -
             | 
| 708 | 
            -
             | 
| 709 | 
            -
             | 
| 710 | 
            -
             | 
| 711 | 
            -
             | 
| 712 | 
            -
                         | 
| 634 | 
            +
                    when other.respond_to?(:zero?) && other.zero?
         | 
| 635 | 
            +
                      return self.zero?
         | 
| 636 | 
            +
                    when other.instance_of?(Unit)
         | 
| 637 | 
            +
                      return false unless self =~ other
         | 
| 638 | 
            +
                      return self.base_scalar == other.base_scalar
         | 
| 639 | 
            +
                    else
         | 
| 640 | 
            +
                      begin
         | 
| 641 | 
            +
                        x, y = coerce(other)
         | 
| 642 | 
            +
                        return x == y
         | 
| 643 | 
            +
                      rescue ArgumentError # return false when object cannot be coerced
         | 
| 644 | 
            +
                        return false
         | 
| 713 645 | 
             
                      end
         | 
| 646 | 
            +
                  end
         | 
| 647 | 
            +
                end
         | 
| 648 | 
            +
             | 
| 649 | 
            +
                # check to see if units are compatible, but not the scalar part
         | 
| 650 | 
            +
                # this check is done by comparing signatures for performance reasons
         | 
| 651 | 
            +
                # if passed a string, it will create a unit object with the string and then do the comparison
         | 
| 652 | 
            +
                # @example this permits a syntax like:
         | 
| 653 | 
            +
                #  unit =~ "mm"
         | 
| 654 | 
            +
                # @note if you want to do a regexp comparison of the unit string do this ...
         | 
| 655 | 
            +
                #  unit.units =~ /regexp/
         | 
| 656 | 
            +
                # @param [Object] other
         | 
| 657 | 
            +
                # @return [Boolean]
         | 
| 658 | 
            +
                def =~(other)
         | 
| 659 | 
            +
                  case other
         | 
| 660 | 
            +
                    when Unit
         | 
| 661 | 
            +
                      self.signature == other.signature
         | 
| 714 662 | 
             
                    else
         | 
| 715 | 
            -
                       | 
| 716 | 
            -
             | 
| 717 | 
            -
             | 
| 718 | 
            -
             | 
| 719 | 
            -
             | 
| 663 | 
            +
                      begin
         | 
| 664 | 
            +
                        x, y = coerce(other)
         | 
| 665 | 
            +
                        return x =~ y
         | 
| 666 | 
            +
                      rescue ArgumentError
         | 
| 667 | 
            +
                        return false
         | 
| 668 | 
            +
                      end
         | 
| 720 669 | 
             
                  end
         | 
| 721 | 
            -
                when Date, Time
         | 
| 722 | 
            -
                  raise ArgumentError, "Date and Time objects represent fixed points in time and cannot be added to a Unit"
         | 
| 723 | 
            -
                else
         | 
| 724 | 
            -
                  x,y = coerce(other)
         | 
| 725 | 
            -
                  y + x
         | 
| 726 670 | 
             
                end
         | 
| 727 | 
            -
              end
         | 
| 728 671 |  | 
| 729 | 
            -
             | 
| 730 | 
            -
             | 
| 731 | 
            -
             | 
| 732 | 
            -
             | 
| 733 | 
            -
             | 
| 734 | 
            -
             | 
| 735 | 
            -
             | 
| 736 | 
            -
                 | 
| 737 | 
            -
             | 
| 738 | 
            -
             | 
| 739 | 
            -
             | 
| 740 | 
            -
             | 
| 741 | 
            -
             | 
| 742 | 
            -
             | 
| 743 | 
            -
             | 
| 672 | 
            +
                alias :compatible? :=~
         | 
| 673 | 
            +
                alias :compatible_with? :=~
         | 
| 674 | 
            +
             | 
| 675 | 
            +
                # Compare two units.  Returns true if quantities and units match
         | 
| 676 | 
            +
                # @example
         | 
| 677 | 
            +
                #   RubyUnits::Unit.new("100 cm") === RubyUnits::Unit.new("100 cm")   # => true
         | 
| 678 | 
            +
                #   RubyUnits::Unit.new("100 cm") === RubyUnits::Unit.new("1 m")      # => false
         | 
| 679 | 
            +
                # @param [Object] other
         | 
| 680 | 
            +
                # @return [Boolean]
         | 
| 681 | 
            +
                def ===(other)
         | 
| 682 | 
            +
                  case other
         | 
| 683 | 
            +
                    when Unit
         | 
| 684 | 
            +
                      (self.scalar == other.scalar) && (self.units == other.units)
         | 
| 685 | 
            +
                    else
         | 
| 686 | 
            +
                      begin
         | 
| 687 | 
            +
                        x, y = coerce(other)
         | 
| 688 | 
            +
                        return x === y
         | 
| 689 | 
            +
                      rescue ArgumentError
         | 
| 690 | 
            +
                        return false
         | 
| 744 691 | 
             
                      end
         | 
| 745 | 
            -
             | 
| 692 | 
            +
                  end
         | 
| 693 | 
            +
                end
         | 
| 694 | 
            +
             | 
| 695 | 
            +
                alias :same? :===
         | 
| 696 | 
            +
                alias :same_as? :===
         | 
| 697 | 
            +
             | 
| 698 | 
            +
                # Add two units together.  Result is same units as receiver and scalar and base_scalar are updated appropriately
         | 
| 699 | 
            +
                # throws an exception if the units are not compatible.
         | 
| 700 | 
            +
                # It is possible to add Time objects to units of time
         | 
| 701 | 
            +
                # @param [Object] other
         | 
| 702 | 
            +
                # @return [Unit]
         | 
| 703 | 
            +
                # @raise [ArgumentError] when two temperatures are added
         | 
| 704 | 
            +
                # @raise [ArgumentError] when units are not compatible
         | 
| 705 | 
            +
                # @raise [ArgumentError] when adding a fixed time or date to a time span
         | 
| 706 | 
            +
                def +(other)
         | 
| 707 | 
            +
                  case other
         | 
| 708 | 
            +
                    when Unit
         | 
| 746 709 | 
             
                      case
         | 
| 747 | 
            -
                        when  | 
| 748 | 
            -
                           | 
| 749 | 
            -
                        when self | 
| 750 | 
            -
                           | 
| 751 | 
            -
             | 
| 752 | 
            -
             | 
| 710 | 
            +
                        when self.zero?
         | 
| 711 | 
            +
                          other.dup
         | 
| 712 | 
            +
                        when self =~ other
         | 
| 713 | 
            +
                          raise ArgumentError, "Cannot add two temperatures" if ([self, other].all? { |x| x.is_temperature? })
         | 
| 714 | 
            +
                          if [self, other].any? { |x| x.is_temperature? }
         | 
| 715 | 
            +
                            if self.is_temperature?
         | 
| 716 | 
            +
                              RubyUnits::Unit.new(:scalar => (self.scalar + other.convert_to(self.temperature_scale).scalar), :numerator => @numerator, :denominator => @denominator, :signature => @signature)
         | 
| 717 | 
            +
                            else
         | 
| 718 | 
            +
                              RubyUnits::Unit.new(:scalar => (other.scalar + self.convert_to(other.temperature_scale).scalar), :numerator => other.numerator, :denominator => other.denominator, :signature => other.signature)
         | 
| 719 | 
            +
                            end
         | 
| 720 | 
            +
                          else
         | 
| 721 | 
            +
                            @q ||= ((@@cached_units[self.units].scalar / @@cached_units[self.units].base_scalar) rescue (self.units.unit.to_base.scalar))
         | 
| 722 | 
            +
                            RubyUnits::Unit.new(:scalar => (self.base_scalar + other.base_scalar)*@q, :numerator => @numerator, :denominator => @denominator, :signature => @signature)
         | 
| 723 | 
            +
                          end
         | 
| 753 724 | 
             
                        else
         | 
| 754 | 
            -
                           | 
| 755 | 
            -
                          Unit.new(:scalar=>(self.base_scalar - other.base_scalar)*@q, :numerator=>@numerator, :denominator=>@denominator, :signature=>@signature)
         | 
| 725 | 
            +
                          raise ArgumentError, "Incompatible Units ('#{self}' not compatible with '#{other}')"
         | 
| 756 726 | 
             
                      end
         | 
| 727 | 
            +
                    when Date, Time
         | 
| 728 | 
            +
                      raise ArgumentError, "Date and Time objects represent fixed points in time and cannot be added to a Unit"
         | 
| 757 729 | 
             
                    else
         | 
| 758 | 
            -
             | 
| 759 | 
            -
             | 
| 760 | 
            -
             | 
| 761 | 
            -
                  raise ArgumentError, "Date and Time objects represent fixed points in time and cannot be subtracted from to a Unit, which can only represent time spans"
         | 
| 762 | 
            -
                else
         | 
| 763 | 
            -
                    x,y = coerce(other)
         | 
| 764 | 
            -
                    return y-x
         | 
| 730 | 
            +
                      x, y = coerce(other)
         | 
| 731 | 
            +
                      y + x
         | 
| 732 | 
            +
                  end
         | 
| 765 733 | 
             
                end
         | 
| 766 | 
            -
              end
         | 
| 767 734 |  | 
| 768 | 
            -
             | 
| 769 | 
            -
             | 
| 770 | 
            -
             | 
| 771 | 
            -
             | 
| 772 | 
            -
             | 
| 773 | 
            -
                 | 
| 774 | 
            -
                 | 
| 775 | 
            -
                   | 
| 776 | 
            -
             | 
| 777 | 
            -
             | 
| 778 | 
            -
             | 
| 779 | 
            -
             | 
| 780 | 
            -
             | 
| 781 | 
            -
             | 
| 782 | 
            -
             | 
| 783 | 
            -
             | 
| 735 | 
            +
                # Subtract two units. Result is same units as receiver and scalar and base_scalar are updated appropriately
         | 
| 736 | 
            +
                # @param [Numeric] other
         | 
| 737 | 
            +
                # @return [Unit]
         | 
| 738 | 
            +
                # @raise [ArgumentError] when subtracting a temperature from a degree
         | 
| 739 | 
            +
                # @raise [ArgumentError] when units are not compatible
         | 
| 740 | 
            +
                # @raise [ArgumentError] when subtracting a fixed time from a time span
         | 
| 741 | 
            +
                def -(other)
         | 
| 742 | 
            +
                  case other
         | 
| 743 | 
            +
                    when Unit
         | 
| 744 | 
            +
                      case
         | 
| 745 | 
            +
                        when self.zero?
         | 
| 746 | 
            +
                          if other.zero?
         | 
| 747 | 
            +
                            other.dup * -1 # preserve Units class
         | 
| 748 | 
            +
                          else
         | 
| 749 | 
            +
                            -other.dup
         | 
| 750 | 
            +
                          end
         | 
| 751 | 
            +
                        when self =~ other
         | 
| 752 | 
            +
                          case
         | 
| 753 | 
            +
                            when [self, other].all? { |x| x.is_temperature? }
         | 
| 754 | 
            +
                              RubyUnits::Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator => KELVIN, :denominator => UNITY_ARRAY, :signature => @signature).convert_to(self.temperature_scale)
         | 
| 755 | 
            +
                            when self.is_temperature?
         | 
| 756 | 
            +
                              RubyUnits::Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator => ['<tempK>'], :denominator => UNITY_ARRAY, :signature => @signature).convert_to(self)
         | 
| 757 | 
            +
                            when other.is_temperature?
         | 
| 758 | 
            +
                              raise ArgumentError, "Cannot subtract a temperature from a differential degree unit"
         | 
| 759 | 
            +
                            else
         | 
| 760 | 
            +
                              @q ||= ((@@cached_units[self.units].scalar / @@cached_units[self.units].base_scalar) rescue (self.units.unit.scalar/self.units.unit.to_base.scalar))
         | 
| 761 | 
            +
                              RubyUnits::Unit.new(:scalar => (self.base_scalar - other.base_scalar)*@q, :numerator => @numerator, :denominator => @denominator, :signature => @signature)
         | 
| 762 | 
            +
                          end
         | 
| 763 | 
            +
                        else
         | 
| 764 | 
            +
                          raise ArgumentError, "Incompatible Units ('#{self}' not compatible with '#{other}')"
         | 
| 765 | 
            +
                      end
         | 
| 766 | 
            +
                    when Time
         | 
| 767 | 
            +
                      raise ArgumentError, "Date and Time objects represent fixed points in time and cannot be subtracted from to a Unit, which can only represent time spans"
         | 
| 768 | 
            +
                    else
         | 
| 769 | 
            +
                      x, y = coerce(other)
         | 
| 770 | 
            +
                      return y-x
         | 
| 771 | 
            +
                  end
         | 
| 784 772 | 
             
                end
         | 
| 785 | 
            -
              end
         | 
| 786 773 |  | 
| 787 | 
            -
             | 
| 788 | 
            -
             | 
| 789 | 
            -
             | 
| 790 | 
            -
             | 
| 791 | 
            -
             | 
| 792 | 
            -
             | 
| 793 | 
            -
             | 
| 794 | 
            -
             | 
| 795 | 
            -
             | 
| 796 | 
            -
             | 
| 797 | 
            -
             | 
| 798 | 
            -
             | 
| 799 | 
            -
             | 
| 800 | 
            -
             | 
| 801 | 
            -
             | 
| 802 | 
            -
             | 
| 803 | 
            -
                   | 
| 804 | 
            -
                else
         | 
| 805 | 
            -
                  x,y = coerce(other)
         | 
| 806 | 
            -
                  return y / x
         | 
| 774 | 
            +
                # Multiply two units.
         | 
| 775 | 
            +
                # @param [Numeric] other
         | 
| 776 | 
            +
                # @return [Unit]
         | 
| 777 | 
            +
                # @raise [ArgumentError] when attempting to multiply two temperatures
         | 
| 778 | 
            +
                def *(other)
         | 
| 779 | 
            +
                  case other
         | 
| 780 | 
            +
                    when Unit
         | 
| 781 | 
            +
                      raise ArgumentError, "Cannot multiply by temperatures" if [other, self].any? { |x| x.is_temperature? }
         | 
| 782 | 
            +
                      opts = RubyUnits::Unit.eliminate_terms(@scalar*other.scalar, @numerator + other.numerator, @denominator + other.denominator)
         | 
| 783 | 
            +
                      opts.merge!(:signature => @signature + other.signature)
         | 
| 784 | 
            +
                      return RubyUnits::Unit.new(opts)
         | 
| 785 | 
            +
                    when Numeric
         | 
| 786 | 
            +
                      return RubyUnits::Unit.new(:scalar => @scalar*other, :numerator => @numerator, :denominator => @denominator, :signature => @signature)
         | 
| 787 | 
            +
                    else
         | 
| 788 | 
            +
                      x, y = coerce(other)
         | 
| 789 | 
            +
                      return x * y
         | 
| 790 | 
            +
                  end
         | 
| 807 791 | 
             
                end
         | 
| 808 | 
            -
              end
         | 
| 809 792 |  | 
| 810 | 
            -
             | 
| 811 | 
            -
             | 
| 812 | 
            -
             | 
| 813 | 
            -
             | 
| 814 | 
            -
             | 
| 815 | 
            -
             | 
| 816 | 
            -
                 | 
| 817 | 
            -
             | 
| 818 | 
            -
             | 
| 819 | 
            -
             | 
| 820 | 
            -
             | 
| 793 | 
            +
                # Divide two units.
         | 
| 794 | 
            +
                # Throws an exception if divisor is 0
         | 
| 795 | 
            +
                # @param [Numeric] other
         | 
| 796 | 
            +
                # @return [Unit]
         | 
| 797 | 
            +
                # @raise [ZeroDivisionError] if divisor is zero
         | 
| 798 | 
            +
                # @raise [ArgumentError] if attempting to divide a temperature by another temperature
         | 
| 799 | 
            +
                def /(other)
         | 
| 800 | 
            +
                  case other
         | 
| 801 | 
            +
                    when Unit
         | 
| 802 | 
            +
                      raise ZeroDivisionError if other.zero?
         | 
| 803 | 
            +
                      raise ArgumentError, "Cannot divide with temperatures" if [other, self].any? { |x| x.is_temperature? }
         | 
| 804 | 
            +
                      opts = RubyUnits::Unit.eliminate_terms(@scalar/other.scalar, @numerator + other.denominator, @denominator + other.numerator)
         | 
| 805 | 
            +
                      opts.merge!(:signature => @signature - other.signature)
         | 
| 806 | 
            +
                      return RubyUnits::Unit.new(opts)
         | 
| 807 | 
            +
                    when Numeric
         | 
| 808 | 
            +
                      raise ZeroDivisionError if other.zero?
         | 
| 809 | 
            +
                      return RubyUnits::Unit.new(:scalar => @scalar/other, :numerator => @numerator, :denominator => @denominator, :signature => @signature)
         | 
| 810 | 
            +
                    else
         | 
| 811 | 
            +
                      x, y = coerce(other)
         | 
| 812 | 
            +
                      return y / x
         | 
| 813 | 
            +
                  end
         | 
| 821 814 | 
             
                end
         | 
| 822 | 
            -
              end
         | 
| 823 | 
            -
             | 
| 824 | 
            -
              # perform a modulo on a unit, will raise an exception if the units are not compatible
         | 
| 825 | 
            -
              # @param [Object] other
         | 
| 826 | 
            -
              # @return [Integer]
         | 
| 827 | 
            -
              def %(other)
         | 
| 828 | 
            -
                return self.divmod(other).last
         | 
| 829 | 
            -
              end
         | 
| 830 815 |  | 
| 831 | 
            -
             | 
| 832 | 
            -
             | 
| 833 | 
            -
             | 
| 834 | 
            -
             | 
| 835 | 
            -
             | 
| 836 | 
            -
             | 
| 837 | 
            -
             | 
| 838 | 
            -
             | 
| 839 | 
            -
             | 
| 840 | 
            -
             | 
| 841 | 
            -
             | 
| 842 | 
            -
             | 
| 843 | 
            -
              # @raise [ArgumentError] when attempting to raise to a complex number
         | 
| 844 | 
            -
              # @raise [ArgumentError] when an invalid exponent is passed
         | 
| 845 | 
            -
              def **(other)
         | 
| 846 | 
            -
                raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
         | 
| 847 | 
            -
                if other.kind_of?(Numeric)
         | 
| 848 | 
            -
                  return self.inverse if other == -1
         | 
| 849 | 
            -
                  return self if other == 1
         | 
| 850 | 
            -
                  return 1 if other.zero?
         | 
| 851 | 
            -
                end
         | 
| 852 | 
            -
                case other
         | 
| 853 | 
            -
                when Rational
         | 
| 854 | 
            -
                  return self.power(other.numerator).root(other.denominator)
         | 
| 855 | 
            -
                when Integer
         | 
| 856 | 
            -
                  return self.power(other)
         | 
| 857 | 
            -
                when Float
         | 
| 858 | 
            -
                  return self**(other.to_i) if other == other.to_i
         | 
| 859 | 
            -
                  valid = (1..9).map {|x| 1/x}
         | 
| 860 | 
            -
                  raise ArgumentError, "Not a n-th root (1..9), use 1/n" unless valid.include? other.abs
         | 
| 861 | 
            -
                  return self.root((1/other).to_int)
         | 
| 862 | 
            -
                when Complex
         | 
| 863 | 
            -
                  raise ArgumentError, "exponentiation of complex numbers is not yet supported."
         | 
| 864 | 
            -
                else
         | 
| 865 | 
            -
                  raise ArgumentError, "Invalid Exponent"
         | 
| 816 | 
            +
                # divide two units and return quotient and remainder
         | 
| 817 | 
            +
                # when both units are in the same units we just use divmod on the raw scalars
         | 
| 818 | 
            +
                # otherwise we use the scalar of the base unit which will be a float
         | 
| 819 | 
            +
                # @param [Object] other
         | 
| 820 | 
            +
                # @return [Array]
         | 
| 821 | 
            +
                def divmod(other)
         | 
| 822 | 
            +
                  raise ArgumentError, "Incompatible Units" unless self =~ other
         | 
| 823 | 
            +
                  if self.units == other.units
         | 
| 824 | 
            +
                    return self.scalar.divmod(other.scalar)
         | 
| 825 | 
            +
                  else
         | 
| 826 | 
            +
                    return self.to_base.scalar.divmod(other.to_base.scalar)
         | 
| 827 | 
            +
                  end
         | 
| 866 828 | 
             
                end
         | 
| 867 | 
            -
              end
         | 
| 868 829 |  | 
| 869 | 
            -
             | 
| 870 | 
            -
             | 
| 871 | 
            -
             | 
| 872 | 
            -
             | 
| 873 | 
            -
             | 
| 874 | 
            -
              def power(n)
         | 
| 875 | 
            -
                raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
         | 
| 876 | 
            -
                raise ArgumentError, "Exponent must an Integer" unless n.kind_of?(Integer)
         | 
| 877 | 
            -
                return self.inverse if n == -1
         | 
| 878 | 
            -
                return 1 if n.zero?
         | 
| 879 | 
            -
                return self if n == 1
         | 
| 880 | 
            -
                if n > 0 then
         | 
| 881 | 
            -
                  return (1..(n-1).to_i).inject(self) {|product, x| product * self}
         | 
| 882 | 
            -
                else
         | 
| 883 | 
            -
                  return (1..-(n-1).to_i).inject(self) {|product, x| product / self}
         | 
| 830 | 
            +
                # perform a modulo on a unit, will raise an exception if the units are not compatible
         | 
| 831 | 
            +
                # @param [Object] other
         | 
| 832 | 
            +
                # @return [Integer]
         | 
| 833 | 
            +
                def %(other)
         | 
| 834 | 
            +
                  return self.divmod(other).last
         | 
| 884 835 | 
             
                end
         | 
| 885 | 
            -
              end
         | 
| 886 836 |  | 
| 887 | 
            -
             | 
| 888 | 
            -
             | 
| 889 | 
            -
             | 
| 890 | 
            -
             | 
| 891 | 
            -
             | 
| 892 | 
            -
             | 
| 893 | 
            -
             | 
| 894 | 
            -
             | 
| 895 | 
            -
                 | 
| 896 | 
            -
                 | 
| 897 | 
            -
                raise ArgumentError | 
| 898 | 
            -
                 | 
| 899 | 
            -
                 | 
| 900 | 
            -
             | 
| 901 | 
            -
                 | 
| 902 | 
            -
             | 
| 903 | 
            -
             | 
| 904 | 
            -
             | 
| 905 | 
            -
             | 
| 906 | 
            -
             | 
| 907 | 
            -
             | 
| 908 | 
            -
                   | 
| 909 | 
            -
             | 
| 910 | 
            -
             | 
| 911 | 
            -
             | 
| 912 | 
            -
             | 
| 913 | 
            -
             | 
| 914 | 
            -
             | 
| 915 | 
            -
             | 
| 916 | 
            -
             | 
| 917 | 
            -
             | 
| 918 | 
            -
             | 
| 919 | 
            -
             | 
| 920 | 
            -
             | 
| 837 | 
            +
                # Exponentiate.  Only takes integer powers.
         | 
| 838 | 
            +
                # Note that anything raised to the power of 0 results in a Unit object with a scalar of 1, and no units.
         | 
| 839 | 
            +
                # Throws an exception if exponent is not an integer.
         | 
| 840 | 
            +
                # Ideally this routine should accept a float for the exponent
         | 
| 841 | 
            +
                # It should then convert the float to a rational and raise the unit by the numerator and root it by the denominator
         | 
| 842 | 
            +
                # but, sadly, floats can't be converted to rationals.
         | 
| 843 | 
            +
                #
         | 
| 844 | 
            +
                # For now, if a rational is passed in, it will be used, otherwise we are stuck with integers and certain floats < 1
         | 
| 845 | 
            +
                # @param [Numeric] other
         | 
| 846 | 
            +
                # @return [Unit]
         | 
| 847 | 
            +
                # @raise [ArgumentError] when raising a temperature to a power
         | 
| 848 | 
            +
                # @raise [ArgumentError] when n not in the set integers from (1..9)
         | 
| 849 | 
            +
                # @raise [ArgumentError] when attempting to raise to a complex number
         | 
| 850 | 
            +
                # @raise [ArgumentError] when an invalid exponent is passed
         | 
| 851 | 
            +
                def **(other)
         | 
| 852 | 
            +
                  raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
         | 
| 853 | 
            +
                  if other.kind_of?(Numeric)
         | 
| 854 | 
            +
                    return self.inverse if other == -1
         | 
| 855 | 
            +
                    return self if other == 1
         | 
| 856 | 
            +
                    return 1 if other.zero?
         | 
| 857 | 
            +
                  end
         | 
| 858 | 
            +
                  case other
         | 
| 859 | 
            +
                    when Rational
         | 
| 860 | 
            +
                      return self.power(other.numerator).root(other.denominator)
         | 
| 861 | 
            +
                    when Integer
         | 
| 862 | 
            +
                      return self.power(other)
         | 
| 863 | 
            +
                    when Float
         | 
| 864 | 
            +
                      return self**(other.to_i) if other == other.to_i
         | 
| 865 | 
            +
                      valid = (1..9).map { |x| 1/x }
         | 
| 866 | 
            +
                      raise ArgumentError, "Not a n-th root (1..9), use 1/n" unless valid.include? other.abs
         | 
| 867 | 
            +
                      return self.root((1/other).to_int)
         | 
| 868 | 
            +
                    when Complex
         | 
| 869 | 
            +
                      raise ArgumentError, "exponentiation of complex numbers is not yet supported."
         | 
| 870 | 
            +
                    else
         | 
| 871 | 
            +
                      raise ArgumentError, "Invalid Exponent"
         | 
| 872 | 
            +
                  end
         | 
| 873 | 
            +
                end
         | 
| 921 874 |  | 
| 922 | 
            -
             | 
| 923 | 
            -
             | 
| 924 | 
            -
             | 
| 925 | 
            -
                 | 
| 926 | 
            -
             | 
| 875 | 
            +
                # returns the unit raised to the n-th power
         | 
| 876 | 
            +
                # @param [Integer] n
         | 
| 877 | 
            +
                # @return [Unit]
         | 
| 878 | 
            +
                # @raise [ArgumentError] when attempting to raise a temperature to a power
         | 
| 879 | 
            +
                # @raise [ArgumentError] when n is not an integer
         | 
| 880 | 
            +
                def power(n)
         | 
| 881 | 
            +
                  raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
         | 
| 882 | 
            +
                  raise ArgumentError, "Exponent must an Integer" unless n.kind_of?(Integer)
         | 
| 883 | 
            +
                  return self.inverse if n == -1
         | 
| 884 | 
            +
                  return 1 if n.zero?
         | 
| 885 | 
            +
                  return self if n == 1
         | 
| 886 | 
            +
                  if n > 0 then
         | 
| 887 | 
            +
                    return (1..(n-1).to_i).inject(self) { |product, x| product * self }
         | 
| 888 | 
            +
                  else
         | 
| 889 | 
            +
                    return (1..-(n-1).to_i).inject(self) { |product, x| product / self }
         | 
| 890 | 
            +
                  end
         | 
| 891 | 
            +
                end
         | 
| 927 892 |  | 
| 928 | 
            -
             | 
| 929 | 
            -
             | 
| 930 | 
            -
             | 
| 931 | 
            -
             | 
| 932 | 
            -
             | 
| 933 | 
            -
             | 
| 934 | 
            -
             | 
| 935 | 
            -
             | 
| 936 | 
            -
             | 
| 937 | 
            -
             | 
| 938 | 
            -
             | 
| 939 | 
            -
             | 
| 940 | 
            -
             | 
| 941 | 
            -
             | 
| 942 | 
            -
             | 
| 943 | 
            -
             | 
| 944 | 
            -
             | 
| 945 | 
            -
             | 
| 946 | 
            -
             | 
| 947 | 
            -
             | 
| 948 | 
            -
             | 
| 949 | 
            -
             | 
| 950 | 
            -
             | 
| 951 | 
            -
             | 
| 952 | 
            -
                  raise ArgumentError, "Receiver is not a temperature unit" unless self.degree?
         | 
| 953 | 
            -
                  start_unit = self.units
         | 
| 954 | 
            -
                  target_unit = other.units rescue other
         | 
| 955 | 
            -
                  unless @base_scalar
         | 
| 956 | 
            -
                    @base_scalar = case @@UNIT_MAP[start_unit]
         | 
| 957 | 
            -
                    when '<tempC>'
         | 
| 958 | 
            -
                      @scalar + 273.15
         | 
| 959 | 
            -
                    when '<tempK>'
         | 
| 960 | 
            -
                      @scalar
         | 
| 961 | 
            -
                    when '<tempF>'
         | 
| 962 | 
            -
                      (@scalar+459.67)*Rational(5,9)
         | 
| 963 | 
            -
                    when '<tempR>'
         | 
| 964 | 
            -
                      @scalar*Rational(5,9)
         | 
| 965 | 
            -
                    end
         | 
| 893 | 
            +
                # Calculates the n-th root of a unit
         | 
| 894 | 
            +
                # if n < 0, returns 1/unit^(1/n)
         | 
| 895 | 
            +
                # @param [Integer] n
         | 
| 896 | 
            +
                # @return [Unit]
         | 
| 897 | 
            +
                # @raise [ArgumentError] when attemptint to take the root of a temperature
         | 
| 898 | 
            +
                # @raise [ArgumentError] when n is not an integer
         | 
| 899 | 
            +
                # @raise [ArgumentError] when n is 0
         | 
| 900 | 
            +
                def root(n)
         | 
| 901 | 
            +
                  raise ArgumentError, "Cannot take the root of a temperature" if self.is_temperature?
         | 
| 902 | 
            +
                  raise ArgumentError, "Exponent must an Integer" unless n.kind_of?(Integer)
         | 
| 903 | 
            +
                  raise ArgumentError, "0th root undefined" if n.zero?
         | 
| 904 | 
            +
                  return self if n == 1
         | 
| 905 | 
            +
                  return self.root(n.abs).inverse if n < 0
         | 
| 906 | 
            +
             | 
| 907 | 
            +
                  vec = self.unit_signature_vector
         | 
| 908 | 
            +
                  vec =vec.map { |x| x % n }
         | 
| 909 | 
            +
                  raise ArgumentError, "Illegal root" unless vec.max == 0
         | 
| 910 | 
            +
                  num = @numerator.dup
         | 
| 911 | 
            +
                  den = @denominator.dup
         | 
| 912 | 
            +
             | 
| 913 | 
            +
                  for item in @numerator.uniq do
         | 
| 914 | 
            +
                    x = num.find_all { |i| i==item }.size
         | 
| 915 | 
            +
                    r = ((x/n)*(n-1)).to_int
         | 
| 916 | 
            +
                    r.times { |y| num.delete_at(num.index(item)) }
         | 
| 966 917 | 
             
                  end
         | 
| 967 | 
            -
             | 
| 968 | 
            -
                   | 
| 969 | 
            -
                     | 
| 970 | 
            -
             | 
| 971 | 
            -
                     | 
| 972 | 
            -
                  when '<tempF>'
         | 
| 973 | 
            -
                    @base_scalar * Rational(9,5) - 459.67
         | 
| 974 | 
            -
                  when '<tempR>'
         | 
| 975 | 
            -
                    @base_scalar * Rational(9,5)
         | 
| 918 | 
            +
             | 
| 919 | 
            +
                  for item in @denominator.uniq do
         | 
| 920 | 
            +
                    x = den.find_all { |i| i==item }.size
         | 
| 921 | 
            +
                    r = ((x/n)*(n-1)).to_int
         | 
| 922 | 
            +
                    r.times { |y| den.delete_at(den.index(item)) }
         | 
| 976 923 | 
             
                  end
         | 
| 977 | 
            -
                   | 
| 978 | 
            -
             | 
| 979 | 
            -
             | 
| 980 | 
            -
             | 
| 981 | 
            -
             | 
| 982 | 
            -
             | 
| 983 | 
            -
             | 
| 984 | 
            -
             | 
| 924 | 
            +
                  q = @scalar < 0 ? (-1)**Rational(1, n) * (@scalar.abs)**Rational(1, n) : @scalar**Rational(1, n)
         | 
| 925 | 
            +
                  return RubyUnits::Unit.new(:scalar => q, :numerator => num, :denominator => den)
         | 
| 926 | 
            +
                end
         | 
| 927 | 
            +
             | 
| 928 | 
            +
                # returns inverse of Unit (1/unit)
         | 
| 929 | 
            +
                # @return [Unit]
         | 
| 930 | 
            +
                def inverse
         | 
| 931 | 
            +
                  return RubyUnits::Unit.new("1") / self
         | 
| 932 | 
            +
                end
         | 
| 933 | 
            +
             | 
| 934 | 
            +
                # convert to a specified unit string or to the same units as another Unit
         | 
| 935 | 
            +
                #
         | 
| 936 | 
            +
                #  unit.convert_to "kg"   will covert to kilograms
         | 
| 937 | 
            +
                #  unit1.convert_to unit2 converts to same units as unit2 object
         | 
| 938 | 
            +
                #
         | 
| 939 | 
            +
                # To convert a Unit object to match another Unit object, use:
         | 
| 940 | 
            +
                #  unit1 >>= unit2
         | 
| 941 | 
            +
                #
         | 
| 942 | 
            +
                # Special handling for temperature conversions is supported.  If the Unit object is converted
         | 
| 943 | 
            +
                # from one temperature unit to another, the proper temperature offsets will be used.
         | 
| 944 | 
            +
                # Supports Kelvin, Celsius, Fahrenheit, and Rankine scales.
         | 
| 945 | 
            +
                #
         | 
| 946 | 
            +
                # @note If temperature is part of a compound unit, the temperature will be treated as a differential
         | 
| 947 | 
            +
                #   and the units will be scaled appropriately.
         | 
| 948 | 
            +
                # @param [Object] other
         | 
| 949 | 
            +
                # @return [Unit]
         | 
| 950 | 
            +
                # @raise [ArgumentError] when attempting to convert a degree to a temperature
         | 
| 951 | 
            +
                # @raise [ArgumentError] when target unit is unknown
         | 
| 952 | 
            +
                # @raise [ArgumentError] when target unit is incompatible
         | 
| 953 | 
            +
                def convert_to(other)
         | 
| 954 | 
            +
                  return self if other.nil?
         | 
| 955 | 
            +
                  return self if TrueClass === other
         | 
| 956 | 
            +
                  return self if FalseClass === other
         | 
| 957 | 
            +
                  if (Unit === other && other.is_temperature?) || (String === other && other =~ /temp[CFRK]/)
         | 
| 958 | 
            +
                    raise ArgumentError, "Receiver is not a temperature unit" unless self.degree?
         | 
| 959 | 
            +
                    start_unit = self.units
         | 
| 960 | 
            +
                    target_unit = other.units rescue other
         | 
| 961 | 
            +
                    unless @base_scalar
         | 
| 962 | 
            +
                      @base_scalar = case @@UNIT_MAP[start_unit]
         | 
| 963 | 
            +
                                       when '<tempC>'
         | 
| 964 | 
            +
                                         @scalar + 273.15
         | 
| 965 | 
            +
                                       when '<tempK>'
         | 
| 966 | 
            +
                                         @scalar
         | 
| 967 | 
            +
                                       when '<tempF>'
         | 
| 968 | 
            +
                                         (@scalar+459.67)*Rational(5, 9)
         | 
| 969 | 
            +
                                       when '<tempR>'
         | 
| 970 | 
            +
                                         @scalar*Rational(5, 9)
         | 
| 971 | 
            +
                                     end
         | 
| 972 | 
            +
                    end
         | 
| 973 | 
            +
                    q= case @@UNIT_MAP[target_unit]
         | 
| 974 | 
            +
                         when '<tempC>'
         | 
| 975 | 
            +
                           @base_scalar - 273.15
         | 
| 976 | 
            +
                         when '<tempK>'
         | 
| 977 | 
            +
                           @base_scalar
         | 
| 978 | 
            +
                         when '<tempF>'
         | 
| 979 | 
            +
                           @base_scalar * Rational(9, 5) - 459.67
         | 
| 980 | 
            +
                         when '<tempR>'
         | 
| 981 | 
            +
                           @base_scalar * Rational(9, 5)
         | 
| 982 | 
            +
                       end
         | 
| 983 | 
            +
                    return RubyUnits::Unit.new("#{q} #{target_unit}")
         | 
| 985 984 | 
             
                  else
         | 
| 986 | 
            -
                     | 
| 985 | 
            +
                    case other
         | 
| 986 | 
            +
                      when Unit
         | 
| 987 | 
            +
                        return self if other.units == self.units
         | 
| 988 | 
            +
                        target = other
         | 
| 989 | 
            +
                      when String
         | 
| 990 | 
            +
                        target = RubyUnits::Unit.new(other)
         | 
| 991 | 
            +
                      else
         | 
| 992 | 
            +
                        raise ArgumentError, "Unknown target units"
         | 
| 993 | 
            +
                    end
         | 
| 994 | 
            +
                    raise ArgumentError, "Incompatible Units" unless self =~ target
         | 
| 995 | 
            +
                    _numerator1   = @numerator.map { |x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x }.map { |i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
         | 
| 996 | 
            +
                    _denominator1 = @denominator.map { |x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x }.map { |i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
         | 
| 997 | 
            +
                    _numerator2   = target.numerator.map { |x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x }.map { |x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
         | 
| 998 | 
            +
                    _denominator2 = target.denominator.map { |x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x }.map { |x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
         | 
| 999 | 
            +
             | 
| 1000 | 
            +
                    q = @scalar * ((_numerator1 + _denominator2).inject(1) { |product, n| product*n }) /
         | 
| 1001 | 
            +
                        ((_numerator2 + _denominator1).inject(1) { |product, n| product*n })
         | 
| 1002 | 
            +
                    return RubyUnits::Unit.new(:scalar => q, :numerator => target.numerator, :denominator => target.denominator, :signature => target.signature)
         | 
| 987 1003 | 
             
                  end
         | 
| 988 | 
            -
             | 
| 989 | 
            -
             | 
| 990 | 
            -
             | 
| 991 | 
            -
             | 
| 992 | 
            -
                  _denominator2 = target.denominator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
         | 
| 1004 | 
            +
                end
         | 
| 1005 | 
            +
             | 
| 1006 | 
            +
                alias :>> :convert_to
         | 
| 1007 | 
            +
                alias :to :convert_to
         | 
| 993 1008 |  | 
| 994 | 
            -
             | 
| 995 | 
            -
             | 
| 996 | 
            -
             | 
| 1009 | 
            +
                # converts the unit back to a float if it is unitless.  Otherwise raises an exception
         | 
| 1010 | 
            +
                # @return [Float]
         | 
| 1011 | 
            +
                # @raise [RuntimeError] when not unitless
         | 
| 1012 | 
            +
                def to_f
         | 
| 1013 | 
            +
                  return @scalar.to_f if self.unitless?
         | 
| 1014 | 
            +
                  raise RuntimeError, "Cannot convert '#{self.to_s}' to Float unless unitless.  Use Unit#scalar"
         | 
| 997 1015 | 
             
                end
         | 
| 998 | 
            -
              end
         | 
| 999 | 
            -
              alias :>> :convert_to
         | 
| 1000 | 
            -
              alias :to :convert_to
         | 
| 1001 | 
            -
             | 
| 1002 | 
            -
              # converts the unit back to a float if it is unitless.  Otherwise raises an exception
         | 
| 1003 | 
            -
              # @return [Float]
         | 
| 1004 | 
            -
              # @raise [RuntimeError] when not unitless
         | 
| 1005 | 
            -
              def to_f
         | 
| 1006 | 
            -
                return @scalar.to_f if self.unitless?
         | 
| 1007 | 
            -
                raise RuntimeError, "Cannot convert '#{self.to_s}' to Float unless unitless.  Use Unit#scalar"
         | 
| 1008 | 
            -
              end
         | 
| 1009 1016 |  | 
| 1010 | 
            -
             | 
| 1011 | 
            -
             | 
| 1012 | 
            -
             | 
| 1013 | 
            -
             | 
| 1014 | 
            -
             | 
| 1015 | 
            -
             | 
| 1016 | 
            -
             | 
| 1017 | 
            +
                # converts the unit back to a complex if it is unitless.  Otherwise raises an exception
         | 
| 1018 | 
            +
                # @return [Complex]
         | 
| 1019 | 
            +
                # @raise [RuntimeError] when not unitless
         | 
| 1020 | 
            +
                def to_c
         | 
| 1021 | 
            +
                  return Complex(@scalar) if self.unitless?
         | 
| 1022 | 
            +
                  raise RuntimeError, "Cannot convert '#{self.to_s}' to Complex unless unitless.  Use Unit#scalar"
         | 
| 1023 | 
            +
                end
         | 
| 1017 1024 |  | 
| 1018 | 
            -
             | 
| 1019 | 
            -
             | 
| 1020 | 
            -
             | 
| 1021 | 
            -
             | 
| 1022 | 
            -
             | 
| 1023 | 
            -
             | 
| 1024 | 
            -
             | 
| 1025 | 
            -
              alias :to_int :to_i
         | 
| 1026 | 
            -
             | 
| 1027 | 
            -
              # if unitless, returns a Rational, otherwise raises an error
         | 
| 1028 | 
            -
              # @return [Rational]
         | 
| 1029 | 
            -
              # @raise [RuntimeError] when not unitless
         | 
| 1030 | 
            -
              def to_r
         | 
| 1031 | 
            -
                return @scalar.to_r if self.unitless?
         | 
| 1032 | 
            -
                raise RuntimeError, "Cannot convert '#{self.to_s}' to Rational unless unitless.  Use Unit#scalar"
         | 
| 1033 | 
            -
              end
         | 
| 1025 | 
            +
                # if unitless, returns an int, otherwise raises an error
         | 
| 1026 | 
            +
                # @return [Integer]
         | 
| 1027 | 
            +
                # @raise [RuntimeError] when not unitless
         | 
| 1028 | 
            +
                def to_i
         | 
| 1029 | 
            +
                  return @scalar.to_int if self.unitless?
         | 
| 1030 | 
            +
                  raise RuntimeError, "Cannot convert '#{self.to_s}' to Integer unless unitless.  Use Unit#scalar"
         | 
| 1031 | 
            +
                end
         | 
| 1034 1032 |  | 
| 1035 | 
            -
             | 
| 1036 | 
            -
              # @return [String]
         | 
| 1037 | 
            -
              def as_json(*args)
         | 
| 1038 | 
            -
                to_s
         | 
| 1039 | 
            -
              end
         | 
| 1033 | 
            +
                alias :to_int :to_i
         | 
| 1040 1034 |  | 
| 1041 | 
            -
             | 
| 1042 | 
            -
             | 
| 1043 | 
            -
             | 
| 1044 | 
            -
                 | 
| 1045 | 
            -
             | 
| 1046 | 
            -
             | 
| 1047 | 
            -
                output_denominator = []
         | 
| 1048 | 
            -
                num                = @numerator.clone.compact
         | 
| 1049 | 
            -
                den                = @denominator.clone.compact
         | 
| 1050 | 
            -
             | 
| 1051 | 
            -
                if @numerator == UNITY_ARRAY
         | 
| 1052 | 
            -
                  output_numerator << "1"
         | 
| 1053 | 
            -
                else
         | 
| 1054 | 
            -
                  while defn = Unit.definition(num.shift) do
         | 
| 1055 | 
            -
                    if defn && defn.prefix?
         | 
| 1056 | 
            -
                      output_numerator << defn.display_name + Unit.definition(num.shift).display_name
         | 
| 1057 | 
            -
                    else
         | 
| 1058 | 
            -
                      output_numerator << defn.display_name
         | 
| 1059 | 
            -
                    end
         | 
| 1060 | 
            -
                  end
         | 
| 1035 | 
            +
                # if unitless, returns a Rational, otherwise raises an error
         | 
| 1036 | 
            +
                # @return [Rational]
         | 
| 1037 | 
            +
                # @raise [RuntimeError] when not unitless
         | 
| 1038 | 
            +
                def to_r
         | 
| 1039 | 
            +
                  return @scalar.to_r if self.unitless?
         | 
| 1040 | 
            +
                  raise RuntimeError, "Cannot convert '#{self.to_s}' to Rational unless unitless.  Use Unit#scalar"
         | 
| 1061 1041 | 
             
                end
         | 
| 1062 1042 |  | 
| 1063 | 
            -
                 | 
| 1043 | 
            +
                # Returns string formatted for json
         | 
| 1044 | 
            +
                # @return [String]
         | 
| 1045 | 
            +
                def as_json(*args)
         | 
| 1046 | 
            +
                  to_s
         | 
| 1047 | 
            +
                end
         | 
| 1048 | 
            +
             | 
| 1049 | 
            +
                # returns the 'unit' part of the Unit object without the scalar
         | 
| 1050 | 
            +
                # @return [String]
         | 
| 1051 | 
            +
                def units(with_prefix = true)
         | 
| 1052 | 
            +
                  return "" if @numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY
         | 
| 1053 | 
            +
                  output_numerator   = []
         | 
| 1064 1054 | 
             
                  output_denominator = []
         | 
| 1065 | 
            -
             | 
| 1066 | 
            -
                   | 
| 1067 | 
            -
             | 
| 1068 | 
            -
             | 
| 1069 | 
            -
                     | 
| 1070 | 
            -
             | 
| 1055 | 
            +
                  num                = @numerator.clone.compact
         | 
| 1056 | 
            +
                  den                = @denominator.clone.compact
         | 
| 1057 | 
            +
             | 
| 1058 | 
            +
                  if @numerator == UNITY_ARRAY
         | 
| 1059 | 
            +
                    output_numerator << "1"
         | 
| 1060 | 
            +
                  else
         | 
| 1061 | 
            +
                    while defn = RubyUnits::Unit.definition(num.shift) do
         | 
| 1062 | 
            +
                      if defn && defn.prefix?
         | 
| 1063 | 
            +
                        if with_prefix
         | 
| 1064 | 
            +
                          output_numerator << (defn.display_name + RubyUnits::Unit.definition(num.shift).display_name)
         | 
| 1065 | 
            +
                        end
         | 
| 1066 | 
            +
                      else
         | 
| 1067 | 
            +
                        output_numerator << defn.display_name
         | 
| 1068 | 
            +
                      end
         | 
| 1071 1069 | 
             
                    end
         | 
| 1072 1070 | 
             
                  end
         | 
| 1073 | 
            -
                end
         | 
| 1074 1071 |  | 
| 1075 | 
            -
             | 
| 1076 | 
            -
             | 
| 1077 | 
            -
             | 
| 1078 | 
            -
             | 
| 1079 | 
            -
                       | 
| 1080 | 
            -
             | 
| 1081 | 
            -
             | 
| 1082 | 
            -
             | 
| 1083 | 
            -
             | 
| 1084 | 
            -
             | 
| 1085 | 
            -
             | 
| 1086 | 
            -
             | 
| 1087 | 
            -
             | 
| 1088 | 
            -
              def -@
         | 
| 1089 | 
            -
                return -@scalar if self.unitless?
         | 
| 1090 | 
            -
                return (self.dup * -1)
         | 
| 1091 | 
            -
              end
         | 
| 1072 | 
            +
                  if @denominator == UNITY_ARRAY
         | 
| 1073 | 
            +
                    output_denominator = []
         | 
| 1074 | 
            +
                  else
         | 
| 1075 | 
            +
                    while defn = RubyUnits::Unit.definition(den.shift) do
         | 
| 1076 | 
            +
                      if defn && defn.prefix?
         | 
| 1077 | 
            +
                        if with_prefix
         | 
| 1078 | 
            +
                          output_denominator << (defn.display_name + RubyUnits::Unit.definition(den.shift).display_name)
         | 
| 1079 | 
            +
                        end
         | 
| 1080 | 
            +
                      else
         | 
| 1081 | 
            +
                        output_denominator << defn.display_name
         | 
| 1082 | 
            +
                      end
         | 
| 1083 | 
            +
                    end
         | 
| 1084 | 
            +
                  end
         | 
| 1092 1085 |  | 
| 1093 | 
            -
               | 
| 1094 | 
            -
             | 
| 1095 | 
            -
             | 
| 1096 | 
            -
             | 
| 1097 | 
            -
             | 
| 1098 | 
            -
             | 
| 1086 | 
            +
                  on  = output_numerator.uniq.
         | 
| 1087 | 
            +
                      map { |x| [x, output_numerator.count(x)] }.
         | 
| 1088 | 
            +
                      map { |element, power| ("#{element}".strip + (power > 1 ? "^#{power}" : '')) }
         | 
| 1089 | 
            +
                  od  = output_denominator.uniq.
         | 
| 1090 | 
            +
                      map { |x| [x, output_denominator.count(x)] }.
         | 
| 1091 | 
            +
                      map { |element, power| ("#{element}".strip + (power > 1 ? "^#{power}" : '')) }
         | 
| 1092 | 
            +
                  out = "#{on.join('*')}#{od.empty? ? '' : '/' + od.join('*')}".strip
         | 
| 1093 | 
            +
                  return out
         | 
| 1094 | 
            +
                end
         | 
| 1099 1095 |  | 
| 1100 | 
            -
             | 
| 1101 | 
            -
             | 
| 1102 | 
            -
             | 
| 1103 | 
            -
             | 
| 1104 | 
            -
             | 
| 1105 | 
            -
             | 
| 1096 | 
            +
                # negates the scalar of the Unit
         | 
| 1097 | 
            +
                # @return [Numeric,Unit]
         | 
| 1098 | 
            +
                def -@
         | 
| 1099 | 
            +
                  return -@scalar if self.unitless?
         | 
| 1100 | 
            +
                  return (self.dup * -1)
         | 
| 1101 | 
            +
                end
         | 
| 1106 1102 |  | 
| 1107 | 
            -
             | 
| 1108 | 
            -
             | 
| 1109 | 
            -
                 | 
| 1110 | 
            -
             | 
| 1111 | 
            -
             | 
| 1103 | 
            +
                # absolute value of a unit
         | 
| 1104 | 
            +
                # @return [Numeric,Unit]
         | 
| 1105 | 
            +
                def abs
         | 
| 1106 | 
            +
                  return @scalar.abs if self.unitless?
         | 
| 1107 | 
            +
                  return RubyUnits::Unit.new(@scalar.abs, @numerator, @denominator)
         | 
| 1108 | 
            +
                end
         | 
| 1112 1109 |  | 
| 1113 | 
            -
             | 
| 1110 | 
            +
                # ceil of a unit
         | 
| 1114 1111 | 
             
                # @return [Numeric,Unit]
         | 
| 1115 | 
            -
                def  | 
| 1116 | 
            -
                  return @scalar. | 
| 1117 | 
            -
                  return Unit.new(@scalar. | 
| 1112 | 
            +
                def ceil
         | 
| 1113 | 
            +
                  return @scalar.ceil if self.unitless?
         | 
| 1114 | 
            +
                  return RubyUnits::Unit.new(@scalar.ceil, @numerator, @denominator)
         | 
| 1118 1115 | 
             
                end
         | 
| 1119 | 
            -
             | 
| 1116 | 
            +
             | 
| 1120 1117 | 
             
                # @return [Numeric,Unit]
         | 
| 1121 | 
            -
                def  | 
| 1122 | 
            -
                  return @scalar. | 
| 1123 | 
            -
                  return Unit.new(@scalar. | 
| 1118 | 
            +
                def floor
         | 
| 1119 | 
            +
                  return @scalar.floor if self.unitless?
         | 
| 1120 | 
            +
                  return RubyUnits::Unit.new(@scalar.floor, @numerator, @denominator)
         | 
| 1124 1121 | 
             
                end
         | 
| 1125 | 
            -
              end
         | 
| 1126 1122 |  | 
| 1127 | 
            -
             | 
| 1128 | 
            -
             | 
| 1129 | 
            -
             | 
| 1130 | 
            -
             | 
| 1131 | 
            -
             | 
| 1123 | 
            +
                if RUBY_VERSION < '1.9'
         | 
| 1124 | 
            +
                  # @return [Numeric,Unit]
         | 
| 1125 | 
            +
                  def round
         | 
| 1126 | 
            +
                    return @scalar.round if self.unitless?
         | 
| 1127 | 
            +
                    return RubyUnits::Unit.new(@scalar.round, @numerator, @denominator)
         | 
| 1128 | 
            +
                  end
         | 
| 1129 | 
            +
                else
         | 
| 1130 | 
            +
                  # @return [Numeric,Unit]
         | 
| 1131 | 
            +
                  def round(ndigits = 0)
         | 
| 1132 | 
            +
                    return @scalar.round(ndigits) if self.unitless?
         | 
| 1133 | 
            +
                    return RubyUnits::Unit.new(@scalar.round(ndigits), @numerator, @denominator)
         | 
| 1134 | 
            +
                  end
         | 
| 1135 | 
            +
                end
         | 
| 1132 1136 |  | 
| 1133 | 
            -
             | 
| 1134 | 
            -
             | 
| 1135 | 
            -
             | 
| 1136 | 
            -
             | 
| 1137 | 
            -
             | 
| 1138 | 
            -
                raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i
         | 
| 1139 | 
            -
                return Unit.new(@scalar.to_i.succ, @numerator, @denominator)
         | 
| 1140 | 
            -
              end
         | 
| 1141 | 
            -
              alias :next :succ
         | 
| 1142 | 
            -
             | 
| 1143 | 
            -
              # returns previous unit in a range.  '2 mm'.unit.pred #=> '1 mm'.unit
         | 
| 1144 | 
            -
              # only works when the scalar is an integer
         | 
| 1145 | 
            -
              # @return [Unit]
         | 
| 1146 | 
            -
              # @raise [ArgumentError] when scalar is not equal to an integer
         | 
| 1147 | 
            -
              def pred
         | 
| 1148 | 
            -
                raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i
         | 
| 1149 | 
            -
                return Unit.new(@scalar.to_i.pred, @numerator, @denominator)
         | 
| 1150 | 
            -
              end
         | 
| 1137 | 
            +
                # @return [Numeric, Unit]
         | 
| 1138 | 
            +
                def truncate
         | 
| 1139 | 
            +
                  return @scalar.truncate if self.unitless?
         | 
| 1140 | 
            +
                  return RubyUnits::Unit.new(@scalar.truncate, @numerator, @denominator)
         | 
| 1141 | 
            +
                end
         | 
| 1151 1142 |  | 
| 1152 | 
            -
             | 
| 1153 | 
            -
             | 
| 1154 | 
            -
             | 
| 1155 | 
            -
                 | 
| 1156 | 
            -
             | 
| 1157 | 
            -
             | 
| 1143 | 
            +
                # returns next unit in a range.  '1 mm'.unit.succ #=> '2 mm'.unit
         | 
| 1144 | 
            +
                # only works when the scalar is an integer
         | 
| 1145 | 
            +
                # @return [Unit]
         | 
| 1146 | 
            +
                # @raise [ArgumentError] when scalar is not equal to an integer
         | 
| 1147 | 
            +
                def succ
         | 
| 1148 | 
            +
                  raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i
         | 
| 1149 | 
            +
                  return RubyUnits::Unit.new(@scalar.to_i.succ, @numerator, @denominator)
         | 
| 1150 | 
            +
                end
         | 
| 1158 1151 |  | 
| 1159 | 
            -
             | 
| 1160 | 
            -
              # defined by DateTime
         | 
| 1161 | 
            -
              # @return [DateTime]
         | 
| 1162 | 
            -
              def to_datetime
         | 
| 1163 | 
            -
                return DateTime.new!(self.convert_to('d').scalar)
         | 
| 1164 | 
            -
              end
         | 
| 1152 | 
            +
                alias :next :succ
         | 
| 1165 1153 |  | 
| 1166 | 
            -
             | 
| 1167 | 
            -
             | 
| 1168 | 
            -
                return  | 
| 1169 | 
            -
             | 
| 1154 | 
            +
                # returns previous unit in a range.  '2 mm'.unit.pred #=> '1 mm'.unit
         | 
| 1155 | 
            +
                # only works when the scalar is an integer
         | 
| 1156 | 
            +
                # @return [Unit]
         | 
| 1157 | 
            +
                # @raise [ArgumentError] when scalar is not equal to an integer
         | 
| 1158 | 
            +
                def pred
         | 
| 1159 | 
            +
                  raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i
         | 
| 1160 | 
            +
                  return RubyUnits::Unit.new(@scalar.to_i.pred, @numerator, @denominator)
         | 
| 1161 | 
            +
                end
         | 
| 1170 1162 |  | 
| 1171 | 
            -
             | 
| 1172 | 
            -
             | 
| 1173 | 
            -
             | 
| 1174 | 
            -
             | 
| 1175 | 
            -
             | 
| 1163 | 
            +
                # Tries to make a Time object from current unit.  Assumes the current unit hold the duration in seconds from the epoch.
         | 
| 1164 | 
            +
                # @return [Time]
         | 
| 1165 | 
            +
                def to_time
         | 
| 1166 | 
            +
                  return Time.at(self)
         | 
| 1167 | 
            +
                end
         | 
| 1176 1168 |  | 
| 1177 | 
            -
             | 
| 1178 | 
            -
              # @return [Unit]
         | 
| 1179 | 
            -
              def ago
         | 
| 1180 | 
            -
                return self.before
         | 
| 1181 | 
            -
              end
         | 
| 1169 | 
            +
                alias :time :to_time
         | 
| 1182 1170 |  | 
| 1183 | 
            -
             | 
| 1184 | 
            -
             | 
| 1185 | 
            -
             | 
| 1186 | 
            -
                 | 
| 1187 | 
            -
             | 
| 1188 | 
            -
                  return (time_point - self rescue time_point.to_datetime - self)
         | 
| 1189 | 
            -
                else
         | 
| 1190 | 
            -
                  raise ArgumentError, "Must specify a Time, Date, or DateTime"
         | 
| 1171 | 
            +
                # convert a duration to a DateTime.  This will work so long as the duration is the duration from the zero date
         | 
| 1172 | 
            +
                # defined by DateTime
         | 
| 1173 | 
            +
                # @return [DateTime]
         | 
| 1174 | 
            +
                def to_datetime
         | 
| 1175 | 
            +
                  return DateTime.new!(self.convert_to('d').scalar)
         | 
| 1191 1176 | 
             
                end
         | 
| 1192 | 
            -
             | 
| 1193 | 
            -
             | 
| 1194 | 
            -
             | 
| 1195 | 
            -
             | 
| 1196 | 
            -
              # @param [Time, Date, DateTime] time_point
         | 
| 1197 | 
            -
              # @return [Unit]
         | 
| 1198 | 
            -
              # @raise [ArgumentError] when time point is not a Time, Date, or DateTime
         | 
| 1199 | 
            -
              def since(time_point)
         | 
| 1200 | 
            -
                case time_point
         | 
| 1201 | 
            -
                when Time
         | 
| 1202 | 
            -
                  return (Time.now - time_point).unit('s').convert_to(self)
         | 
| 1203 | 
            -
                when DateTime, Date
         | 
| 1204 | 
            -
                  return (DateTime.now - time_point).unit('d').convert_to(self)
         | 
| 1205 | 
            -
                else
         | 
| 1206 | 
            -
                  raise ArgumentError, "Must specify a Time, Date, or DateTime"
         | 
| 1177 | 
            +
             | 
| 1178 | 
            +
                # @return [Date]
         | 
| 1179 | 
            +
                def to_date
         | 
| 1180 | 
            +
                  return Date.new0(self.convert_to('d').scalar)
         | 
| 1207 1181 | 
             
                end
         | 
| 1208 | 
            -
              end
         | 
| 1209 1182 |  | 
| 1210 | 
            -
             | 
| 1211 | 
            -
             | 
| 1212 | 
            -
             | 
| 1213 | 
            -
             | 
| 1214 | 
            -
                case time_point
         | 
| 1215 | 
            -
                when Time
         | 
| 1216 | 
            -
                  return (time_point - Time.now).unit('s').convert_to(self)
         | 
| 1217 | 
            -
                when DateTime, Date
         | 
| 1218 | 
            -
                  return (time_point - DateTime.now).unit('d').convert_to(self)
         | 
| 1219 | 
            -
                else
         | 
| 1220 | 
            -
                  raise ArgumentError, "Must specify a Time, Date, or DateTime"
         | 
| 1183 | 
            +
                # true if scalar is zero
         | 
| 1184 | 
            +
                # @return [Boolean]
         | 
| 1185 | 
            +
                def zero?
         | 
| 1186 | 
            +
                  return self.base_scalar.zero?
         | 
| 1221 1187 | 
             
                end
         | 
| 1222 | 
            -
              end
         | 
| 1223 1188 |  | 
| 1224 | 
            -
             | 
| 1225 | 
            -
             | 
| 1226 | 
            -
             | 
| 1227 | 
            -
             | 
| 1228 | 
            -
              def from(time_point)
         | 
| 1229 | 
            -
                case time_point
         | 
| 1230 | 
            -
                when Time, DateTime, Date
         | 
| 1231 | 
            -
                  return (time_point + self rescue time_point.to_datetime + self)
         | 
| 1232 | 
            -
                else
         | 
| 1233 | 
            -
                  raise ArgumentError, "Must specify a Time, Date, or DateTime"
         | 
| 1189 | 
            +
                # @example '5 min'.unit.ago
         | 
| 1190 | 
            +
                # @return [Unit]
         | 
| 1191 | 
            +
                def ago
         | 
| 1192 | 
            +
                  return self.before
         | 
| 1234 1193 | 
             
                end
         | 
| 1235 | 
            -
             | 
| 1236 | 
            -
             | 
| 1237 | 
            -
             | 
| 1238 | 
            -
             | 
| 1239 | 
            -
             | 
| 1240 | 
            -
             | 
| 1241 | 
            -
             | 
| 1242 | 
            -
             | 
| 1243 | 
            -
             | 
| 1244 | 
            -
             | 
| 1245 | 
            -
             | 
| 1246 | 
            -
             | 
| 1247 | 
            -
                 | 
| 1248 | 
            -
             | 
| 1249 | 
            -
             | 
| 1250 | 
            -
                 | 
| 1251 | 
            -
             | 
| 1194 | 
            +
             | 
| 1195 | 
            +
                # @example '5 min'.before(time)
         | 
| 1196 | 
            +
                # @return [Unit]
         | 
| 1197 | 
            +
                def before(time_point = ::Time.now)
         | 
| 1198 | 
            +
                  case time_point
         | 
| 1199 | 
            +
                    when Time, Date, DateTime
         | 
| 1200 | 
            +
                      return (time_point - self rescue time_point.to_datetime - self)
         | 
| 1201 | 
            +
                    else
         | 
| 1202 | 
            +
                      raise ArgumentError, "Must specify a Time, Date, or DateTime"
         | 
| 1203 | 
            +
                  end
         | 
| 1204 | 
            +
                end
         | 
| 1205 | 
            +
             | 
| 1206 | 
            +
                alias :before_now :before
         | 
| 1207 | 
            +
             | 
| 1208 | 
            +
                # @example 'min'.since(time)
         | 
| 1209 | 
            +
                # @param [Time, Date, DateTime] time_point
         | 
| 1210 | 
            +
                # @return [Unit]
         | 
| 1211 | 
            +
                # @raise [ArgumentError] when time point is not a Time, Date, or DateTime
         | 
| 1212 | 
            +
                def since(time_point)
         | 
| 1213 | 
            +
                  case time_point
         | 
| 1214 | 
            +
                    when Time
         | 
| 1215 | 
            +
                      return (Time.now - time_point).unit('s').convert_to(self)
         | 
| 1216 | 
            +
                    when DateTime, Date
         | 
| 1217 | 
            +
                      return (DateTime.now - time_point).unit('d').convert_to(self)
         | 
| 1218 | 
            +
                    else
         | 
| 1219 | 
            +
                      raise ArgumentError, "Must specify a Time, Date, or DateTime"
         | 
| 1220 | 
            +
                  end
         | 
| 1252 1221 | 
             
                end
         | 
| 1253 | 
            -
              end
         | 
| 1254 1222 |  | 
| 1255 | 
            -
             | 
| 1256 | 
            -
             | 
| 1223 | 
            +
                # @example 'min'.until(time)
         | 
| 1224 | 
            +
                # @param [Time, Date, DateTime] time_point
         | 
| 1225 | 
            +
                # @return [Unit]
         | 
| 1226 | 
            +
                def until(time_point)
         | 
| 1227 | 
            +
                  case time_point
         | 
| 1228 | 
            +
                    when Time
         | 
| 1229 | 
            +
                      return (time_point - Time.now).unit('s').convert_to(self)
         | 
| 1230 | 
            +
                    when DateTime, Date
         | 
| 1231 | 
            +
                      return (time_point - DateTime.now).unit('d').convert_to(self)
         | 
| 1232 | 
            +
                    else
         | 
| 1233 | 
            +
                      raise ArgumentError, "Must specify a Time, Date, or DateTime"
         | 
| 1234 | 
            +
                  end
         | 
| 1235 | 
            +
                end
         | 
| 1257 1236 |  | 
| 1258 | 
            -
             | 
| 1259 | 
            -
             | 
| 1260 | 
            -
             | 
| 1261 | 
            -
                 | 
| 1262 | 
            -
             | 
| 1263 | 
            -
                   | 
| 1264 | 
            -
             | 
| 1265 | 
            -
             | 
| 1266 | 
            -
             | 
| 1267 | 
            -
             | 
| 1237 | 
            +
                # @example '5 min'.from(time)
         | 
| 1238 | 
            +
                # @param [Time, Date, DateTime] time_point
         | 
| 1239 | 
            +
                # @return [Time, Date, DateTime]
         | 
| 1240 | 
            +
                # @raise [ArgumentError] when passed argument is not a Time, Date, or DateTime
         | 
| 1241 | 
            +
                def from(time_point)
         | 
| 1242 | 
            +
                  case time_point
         | 
| 1243 | 
            +
                    when Time, DateTime, Date
         | 
| 1244 | 
            +
                      return (time_point + self rescue time_point.to_datetime + self)
         | 
| 1245 | 
            +
                    else
         | 
| 1246 | 
            +
                      raise ArgumentError, "Must specify a Time, Date, or DateTime"
         | 
| 1247 | 
            +
                  end
         | 
| 1268 1248 | 
             
                end
         | 
| 1269 | 
            -
              end
         | 
| 1270 1249 |  | 
| 1271 | 
            -
             | 
| 1272 | 
            -
             | 
| 1273 | 
            -
              # @raise [ArgumentError] when exponent associated with a unit is > 20 or < -20
         | 
| 1274 | 
            -
              def unit_signature_vector
         | 
| 1275 | 
            -
                return self.to_base.unit_signature_vector unless self.is_base?
         | 
| 1276 | 
            -
                vector = Array.new(SIGNATURE_VECTOR.size,0)
         | 
| 1277 | 
            -
                # it's possible to have a kind that misses the array... kinds like :counting
         | 
| 1278 | 
            -
                # are more like prefixes, so don't use them to calculate the vector
         | 
| 1279 | 
            -
                @numerator.map {|element| Unit.definition(element)}.each do |definition|
         | 
| 1280 | 
            -
                  index = SIGNATURE_VECTOR.index(definition.kind)
         | 
| 1281 | 
            -
                  vector[index] += 1 if index
         | 
| 1282 | 
            -
                end
         | 
| 1283 | 
            -
                @denominator.map {|element| Unit.definition(element)}.each do |definition|
         | 
| 1284 | 
            -
                  index = SIGNATURE_VECTOR.index(definition.kind)
         | 
| 1285 | 
            -
                  vector[index] -= 1 if index
         | 
| 1286 | 
            -
                end
         | 
| 1287 | 
            -
                raise ArgumentError, "Power out of range (-20 < net power of a unit < 20)" if vector.any? {|x| x.abs >=20}
         | 
| 1288 | 
            -
                return vector
         | 
| 1289 | 
            -
              end
         | 
| 1250 | 
            +
                alias :after :from
         | 
| 1251 | 
            +
                alias :from_now :from
         | 
| 1290 1252 |  | 
| 1291 | 
            -
             | 
| 1253 | 
            +
                # automatically coerce objects to units when possible
         | 
| 1254 | 
            +
                # if an object defines a 'to_unit' method, it will be coerced using that method
         | 
| 1255 | 
            +
                # @param [Object, #to_unit]
         | 
| 1256 | 
            +
                # @return [Array]
         | 
| 1257 | 
            +
                def coerce(other)
         | 
| 1258 | 
            +
                  if other.respond_to? :to_unit
         | 
| 1259 | 
            +
                    return [other.to_unit, self]
         | 
| 1260 | 
            +
                  end
         | 
| 1261 | 
            +
                  case other
         | 
| 1262 | 
            +
                    when Unit
         | 
| 1263 | 
            +
                      return [other, self]
         | 
| 1264 | 
            +
                    else
         | 
| 1265 | 
            +
                      return [RubyUnits::Unit.new(other), self]
         | 
| 1266 | 
            +
                  end
         | 
| 1267 | 
            +
                end
         | 
| 1292 1268 |  | 
| 1293 | 
            -
             | 
| 1294 | 
            -
             | 
| 1295 | 
            -
               | 
| 1296 | 
            -
             | 
| 1297 | 
            -
             | 
| 1298 | 
            -
             | 
| 1299 | 
            -
             | 
| 1269 | 
            +
                # returns a new unit that has been
         | 
| 1270 | 
            +
                def best_prefix
         | 
| 1271 | 
            +
                  _best_prefix =  if (self.kind == :information)
         | 
| 1272 | 
            +
                    @@PREFIX_VALUES.key(2**((Math.log(self.base_scalar,2) / 10.0).floor * 10))
         | 
| 1273 | 
            +
                  else
         | 
| 1274 | 
            +
                    @@PREFIX_VALUES.key(10**((Math.log10(self.base_scalar) / 3.0).floor * 3))
         | 
| 1275 | 
            +
                  end
         | 
| 1276 | 
            +
                  self.to(RubyUnits::Unit.new(@@PREFIX_MAP.key(_best_prefix)+self.units(false)))
         | 
| 1277 | 
            +
                end
         | 
| 1300 1278 |  | 
| 1301 | 
            -
             | 
| 1302 | 
            -
             | 
| 1303 | 
            -
              #
         | 
| 1304 | 
            -
              # Novak, G.S., Jr. "Conversion of units of measurement", IEEE Transactions on Software Engineering, 21(8), Aug 1995, pp.651-661
         | 
| 1305 | 
            -
              # @see http://doi.ieeecomputersociety.org/10.1109/32.403789
         | 
| 1306 | 
            -
              # @return [Array]
         | 
| 1307 | 
            -
              def unit_signature
         | 
| 1308 | 
            -
                return @signature unless @signature.nil?
         | 
| 1309 | 
            -
                vector = unit_signature_vector
         | 
| 1310 | 
            -
                vector.each_with_index {|item,index| vector[index] = item * 20**index}
         | 
| 1311 | 
            -
                @signature=vector.inject(0) {|sum,n| sum+n}
         | 
| 1312 | 
            -
                return @signature
         | 
| 1313 | 
            -
              end
         | 
| 1279 | 
            +
                # Protected and Private Functions that should only be called from this class
         | 
| 1280 | 
            +
                protected
         | 
| 1314 1281 |  | 
| 1315 | 
            -
             | 
| 1316 | 
            -
             | 
| 1317 | 
            -
             | 
| 1318 | 
            -
             | 
| 1319 | 
            -
             | 
| 1320 | 
            -
             | 
| 1321 | 
            -
                den = d.dup
         | 
| 1322 | 
            -
             | 
| 1323 | 
            -
                num.delete_if {|v| v == UNITY}
         | 
| 1324 | 
            -
                den.delete_if {|v| v == UNITY}
         | 
| 1325 | 
            -
                combined = Hash.new(0)
         | 
| 1326 | 
            -
             | 
| 1327 | 
            -
                i = 0
         | 
| 1328 | 
            -
                loop do
         | 
| 1329 | 
            -
                  break if i > num.size
         | 
| 1330 | 
            -
                  if @@PREFIX_VALUES.has_key? num[i]
         | 
| 1331 | 
            -
                    k = [num[i],num[i+1]]
         | 
| 1332 | 
            -
                    i += 2
         | 
| 1282 | 
            +
                # figure out what the scalar part of the base unit for this unit is
         | 
| 1283 | 
            +
                # @return [nil]
         | 
| 1284 | 
            +
                def update_base_scalar
         | 
| 1285 | 
            +
                  if self.is_base?
         | 
| 1286 | 
            +
                    @base_scalar = @scalar
         | 
| 1287 | 
            +
                    @signature   = unit_signature
         | 
| 1333 1288 | 
             
                  else
         | 
| 1334 | 
            -
                     | 
| 1335 | 
            -
                     | 
| 1289 | 
            +
                    base         = self.to_base
         | 
| 1290 | 
            +
                    @base_scalar = base.scalar
         | 
| 1291 | 
            +
                    @signature   = base.signature
         | 
| 1336 1292 | 
             
                  end
         | 
| 1337 | 
            -
                  combined[k] += 1 unless k.nil? || k == UNITY
         | 
| 1338 1293 | 
             
                end
         | 
| 1339 1294 |  | 
| 1340 | 
            -
                 | 
| 1341 | 
            -
                 | 
| 1342 | 
            -
             | 
| 1295 | 
            +
                # calculates the unit signature vector used by unit_signature
         | 
| 1296 | 
            +
                # @return [Array]
         | 
| 1297 | 
            +
                # @raise [ArgumentError] when exponent associated with a unit is > 20 or < -20
         | 
| 1298 | 
            +
                def unit_signature_vector
         | 
| 1299 | 
            +
                  return self.to_base.unit_signature_vector unless self.is_base?
         | 
| 1300 | 
            +
                  vector = Array.new(SIGNATURE_VECTOR.size, 0)
         | 
| 1301 | 
            +
                  # it's possible to have a kind that misses the array... kinds like :counting
         | 
| 1302 | 
            +
                  # are more like prefixes, so don't use them to calculate the vector
         | 
| 1303 | 
            +
                  @numerator.map { |element| RubyUnits::Unit.definition(element) }.each do |definition|
         | 
| 1304 | 
            +
                    index = SIGNATURE_VECTOR.index(definition.kind)
         | 
| 1305 | 
            +
                    vector[index] += 1 if index
         | 
| 1306 | 
            +
                  end
         | 
| 1307 | 
            +
                  @denominator.map { |element| RubyUnits::Unit.definition(element) }.each do |definition|
         | 
| 1308 | 
            +
                    index = SIGNATURE_VECTOR.index(definition.kind)
         | 
| 1309 | 
            +
                    vector[index] -= 1 if index
         | 
| 1310 | 
            +
                  end
         | 
| 1311 | 
            +
                  raise ArgumentError, "Power out of range (-20 < net power of a unit < 20)" if vector.any? { |x| x.abs >=20 }
         | 
| 1312 | 
            +
                  return vector
         | 
| 1313 | 
            +
                end
         | 
| 1314 | 
            +
             | 
| 1315 | 
            +
             | 
| 1316 | 
            +
                private
         | 
| 1317 | 
            +
             | 
| 1318 | 
            +
                # used by #dup to duplicate a Unit
         | 
| 1319 | 
            +
                # @param [Unit] other
         | 
| 1320 | 
            +
                # @private
         | 
| 1321 | 
            +
                def initialize_copy(other)
         | 
| 1322 | 
            +
                  @numerator   = other.numerator.dup
         | 
| 1323 | 
            +
                  @denominator = other.denominator.dup
         | 
| 1324 | 
            +
                end
         | 
| 1325 | 
            +
             | 
| 1326 | 
            +
                # calculates the unit signature id for use in comparing compatible units and simplification
         | 
| 1327 | 
            +
                # the signature is based on a simple classification of units and is based on the following publication
         | 
| 1328 | 
            +
                #
         | 
| 1329 | 
            +
                # Novak, G.S., Jr. "Conversion of units of measurement", IEEE Transactions on Software Engineering, 21(8), Aug 1995, pp.651-661
         | 
| 1330 | 
            +
                # @see http://doi.ieeecomputersociety.org/10.1109/32.403789
         | 
| 1331 | 
            +
                # @return [Array]
         | 
| 1332 | 
            +
                def unit_signature
         | 
| 1333 | 
            +
                  return @signature unless @signature.nil?
         | 
| 1334 | 
            +
                  vector = unit_signature_vector
         | 
| 1335 | 
            +
                  vector.each_with_index { |item, index| vector[index] = item * 20**index }
         | 
| 1336 | 
            +
                  @signature=vector.inject(0) { |sum, n| sum+n }
         | 
| 1337 | 
            +
                  return @signature
         | 
| 1338 | 
            +
                end
         | 
| 1339 | 
            +
             | 
| 1340 | 
            +
                # @param [Numeric] q quantity
         | 
| 1341 | 
            +
                # @param [Array] n numerator
         | 
| 1342 | 
            +
                # @param [Array] d denominator
         | 
| 1343 | 
            +
                # @return [Hash]
         | 
| 1344 | 
            +
                def self.eliminate_terms(q, n, d)
         | 
| 1345 | 
            +
                  num = n.dup
         | 
| 1346 | 
            +
                  den = d.dup
         | 
| 1347 | 
            +
             | 
| 1348 | 
            +
                  num.delete_if { |v| v == UNITY }
         | 
| 1349 | 
            +
                  den.delete_if { |v| v == UNITY }
         | 
| 1350 | 
            +
                  combined = Hash.new(0)
         | 
| 1351 | 
            +
             | 
| 1352 | 
            +
                  i = 0
         | 
| 1353 | 
            +
                  loop do
         | 
| 1354 | 
            +
                    break if i > num.size
         | 
| 1355 | 
            +
                    if @@PREFIX_VALUES.has_key? num[i]
         | 
| 1356 | 
            +
                      k = [num[i], num[i+1]]
         | 
| 1357 | 
            +
                      i += 2
         | 
| 1358 | 
            +
                    else
         | 
| 1359 | 
            +
                      k = num[i]
         | 
| 1360 | 
            +
                      i += 1
         | 
| 1361 | 
            +
                    end
         | 
| 1362 | 
            +
                    combined[k] += 1 unless k.nil? || k == UNITY
         | 
| 1363 | 
            +
                  end
         | 
| 1364 | 
            +
             | 
| 1365 | 
            +
                  j = 0
         | 
| 1366 | 
            +
                  loop do
         | 
| 1367 | 
            +
                    break if j > den.size
         | 
| 1343 1368 | 
             
                    if @@PREFIX_VALUES.has_key? den[j]
         | 
| 1344 | 
            -
                      k = [den[j],den[j+1]]
         | 
| 1369 | 
            +
                      k = [den[j], den[j+1]]
         | 
| 1345 1370 | 
             
                      j += 2
         | 
| 1346 1371 | 
             
                    else
         | 
| 1347 1372 | 
             
                      k = den[j]
         | 
| 1348 1373 | 
             
                      j += 1
         | 
| 1349 1374 | 
             
                    end
         | 
| 1350 | 
            -
             | 
| 1351 | 
            -
                end
         | 
| 1352 | 
            -
             | 
| 1353 | 
            -
                num = []
         | 
| 1354 | 
            -
                den = []
         | 
| 1355 | 
            -
                for key, value in combined do
         | 
| 1356 | 
            -
                  case
         | 
| 1357 | 
            -
                  when value > 0
         | 
| 1358 | 
            -
                    value.times {num << key}
         | 
| 1359 | 
            -
                  when value < 0
         | 
| 1360 | 
            -
                    value.abs.times {den << key}
         | 
| 1375 | 
            +
                    combined[k] -= 1 unless k.nil? || k == UNITY
         | 
| 1361 1376 | 
             
                  end
         | 
| 1362 | 
            -
                end
         | 
| 1363 | 
            -
                num = UNITY_ARRAY if num.empty?
         | 
| 1364 | 
            -
                den = UNITY_ARRAY if den.empty?
         | 
| 1365 | 
            -
                return {:scalar=>q, :numerator=>num.flatten.compact, :denominator=>den.flatten.compact}
         | 
| 1366 | 
            -
              end
         | 
| 1367 1377 |  | 
| 1368 | 
            -
             | 
| 1369 | 
            -
             | 
| 1370 | 
            -
             | 
| 1371 | 
            -
             | 
| 1372 | 
            -
             | 
| 1373 | 
            -
             | 
| 1374 | 
            -
             | 
| 1375 | 
            -
             | 
| 1376 | 
            -
             | 
| 1377 | 
            -
              #  6'4"  -- recognized as 6 feet + 4 inches
         | 
| 1378 | 
            -
              #  8 lbs 8 oz -- recognized as 8 lbs + 8 ounces
         | 
| 1379 | 
            -
              # @return [nil | Unit]
         | 
| 1380 | 
            -
              # @todo This should either be a separate class or at least a class method
         | 
| 1381 | 
            -
              def parse(passed_unit_string="0")
         | 
| 1382 | 
            -
                unit_string = passed_unit_string.dup
         | 
| 1383 | 
            -
                if unit_string =~ /\$\s*(#{NUMBER_REGEX})/
         | 
| 1384 | 
            -
                  unit_string = "#{$1} USD"
         | 
| 1385 | 
            -
                end
         | 
| 1386 | 
            -
                unit_string.gsub!("\u00b0".force_encoding('utf-8'), 'deg') if RUBY_VERSION >= '1.9' && unit_string.encoding == Encoding::UTF_8
         | 
| 1387 | 
            -
             | 
| 1388 | 
            -
                unit_string.gsub!(/%/,'percent')
         | 
| 1389 | 
            -
                unit_string.gsub!(/'/,'feet')
         | 
| 1390 | 
            -
                unit_string.gsub!(/"/,'inch')
         | 
| 1391 | 
            -
                unit_string.gsub!(/#/,'pound')
         | 
| 1392 | 
            -
             | 
| 1393 | 
            -
                #:nocov:
         | 
| 1394 | 
            -
                #:nocov_19:
         | 
| 1395 | 
            -
                if defined?(Uncertain) && unit_string =~ /(\+\/-|±)/
         | 
| 1396 | 
            -
                  value, uncertainty, unit_s = unit_string.scan(UNCERTAIN_REGEX)[0]
         | 
| 1397 | 
            -
                  result = unit_s.unit * Uncertain.new(value.to_f,uncertainty.to_f)
         | 
| 1398 | 
            -
                  copy(result)
         | 
| 1399 | 
            -
                  return
         | 
| 1400 | 
            -
                end
         | 
| 1401 | 
            -
                #:nocov:
         | 
| 1402 | 
            -
                #:nocov_19:
         | 
| 1403 | 
            -
             | 
| 1404 | 
            -
                if defined?(Complex) && unit_string =~ COMPLEX_NUMBER
         | 
| 1405 | 
            -
                  real, imaginary, unit_s = unit_string.scan(COMPLEX_REGEX)[0]
         | 
| 1406 | 
            -
                  result = Unit(unit_s || '1') * Complex(real.to_f,imaginary.to_f)
         | 
| 1407 | 
            -
                  copy(result)
         | 
| 1408 | 
            -
                  return
         | 
| 1409 | 
            -
                end
         | 
| 1410 | 
            -
             | 
| 1411 | 
            -
                if defined?(Rational) && unit_string =~ RATIONAL_NUMBER
         | 
| 1412 | 
            -
                  numerator, denominator, unit_s = unit_string.scan(RATIONAL_REGEX)[0]
         | 
| 1413 | 
            -
                  result = Unit(unit_s || '1') * Rational(numerator.to_i,denominator.to_i)
         | 
| 1414 | 
            -
                  copy(result)
         | 
| 1415 | 
            -
                  return
         | 
| 1416 | 
            -
                end
         | 
| 1417 | 
            -
             | 
| 1418 | 
            -
                unit_string =~ NUMBER_REGEX
         | 
| 1419 | 
            -
                unit = @@cached_units[$2]
         | 
| 1420 | 
            -
                mult = ($1.empty? ? 1.0 : $1.to_f) rescue 1.0
         | 
| 1421 | 
            -
                mult = mult.to_int if (mult.to_int == mult)
         | 
| 1422 | 
            -
                if unit
         | 
| 1423 | 
            -
                  copy(unit)
         | 
| 1424 | 
            -
                  @scalar *= mult
         | 
| 1425 | 
            -
                  @base_scalar *= mult
         | 
| 1426 | 
            -
                  return self
         | 
| 1427 | 
            -
                end
         | 
| 1428 | 
            -
                unit_string.gsub!(/<(#{@@UNIT_REGEX})><(#{@@UNIT_REGEX})>/, '\1*\2')
         | 
| 1429 | 
            -
                unit_string.gsub!(/[<>]/,"")
         | 
| 1430 | 
            -
             | 
| 1431 | 
            -
                if unit_string =~ /:/
         | 
| 1432 | 
            -
                  hours, minutes, seconds, microseconds = unit_string.scan(TIME_REGEX)[0]
         | 
| 1433 | 
            -
                  raise ArgumentError, "Invalid Duration" if [hours, minutes, seconds, microseconds].all? {|x| x.nil?}
         | 
| 1434 | 
            -
                  result = "#{hours || 0} h".unit +
         | 
| 1435 | 
            -
                           "#{minutes || 0} minutes".unit +
         | 
| 1436 | 
            -
                           "#{seconds || 0} seconds".unit +
         | 
| 1437 | 
            -
                           "#{microseconds || 0} usec".unit
         | 
| 1438 | 
            -
                  copy(result)
         | 
| 1439 | 
            -
                  return
         | 
| 1440 | 
            -
                end
         | 
| 1441 | 
            -
             | 
| 1442 | 
            -
                # Special processing for unusual unit strings
         | 
| 1443 | 
            -
                # feet -- 6'5"
         | 
| 1444 | 
            -
                feet, inches = unit_string.scan(FEET_INCH_REGEX)[0]
         | 
| 1445 | 
            -
                if (feet && inches)
         | 
| 1446 | 
            -
                  result = Unit.new("#{feet} ft") + Unit.new("#{inches} inches")
         | 
| 1447 | 
            -
                  copy(result)
         | 
| 1448 | 
            -
                  return
         | 
| 1449 | 
            -
                end
         | 
| 1450 | 
            -
             | 
| 1451 | 
            -
                # weight -- 8 lbs 12 oz
         | 
| 1452 | 
            -
                pounds, oz = unit_string.scan(LBS_OZ_REGEX)[0]
         | 
| 1453 | 
            -
                if (pounds && oz)
         | 
| 1454 | 
            -
                  result = Unit.new("#{pounds} lbs") + Unit.new("#{oz} oz")
         | 
| 1455 | 
            -
                  copy(result)
         | 
| 1456 | 
            -
                  return
         | 
| 1457 | 
            -
                end
         | 
| 1458 | 
            -
             | 
| 1459 | 
            -
                # more than one per.  I.e., "1 m/s/s"
         | 
| 1460 | 
            -
                raise( ArgumentError, "'#{passed_unit_string}' Unit not recognized") if unit_string.count('/') > 1
         | 
| 1461 | 
            -
                raise( ArgumentError, "'#{passed_unit_string}' Unit not recognized") if unit_string.scan(/\s[02-9]/).size > 0
         | 
| 1462 | 
            -
                @scalar, top, bottom = unit_string.scan(UNIT_STRING_REGEX)[0]  #parse the string into parts
         | 
| 1463 | 
            -
                top.scan(TOP_REGEX).each do |item|
         | 
| 1464 | 
            -
                  n = item[1].to_i
         | 
| 1465 | 
            -
                  x = "#{item[0]} "
         | 
| 1466 | 
            -
                  case
         | 
| 1467 | 
            -
                    when n>=0
         | 
| 1468 | 
            -
                      top.gsub!(/#{item[0]}(\^|\*\*)#{n}/) {|s| x * n}
         | 
| 1469 | 
            -
                    when n<0
         | 
| 1470 | 
            -
                      bottom = "#{bottom} #{x * -n}"; top.gsub!(/#{item[0]}(\^|\*\*)#{n}/,"")
         | 
| 1378 | 
            +
                  num = []
         | 
| 1379 | 
            +
                  den = []
         | 
| 1380 | 
            +
                  for key, value in combined do
         | 
| 1381 | 
            +
                    case
         | 
| 1382 | 
            +
                      when value > 0
         | 
| 1383 | 
            +
                        value.times { num << key }
         | 
| 1384 | 
            +
                      when value < 0
         | 
| 1385 | 
            +
                        value.abs.times { den << key }
         | 
| 1386 | 
            +
                    end
         | 
| 1471 1387 | 
             
                  end
         | 
| 1388 | 
            +
                  num = UNITY_ARRAY if num.empty?
         | 
| 1389 | 
            +
                  den = UNITY_ARRAY if den.empty?
         | 
| 1390 | 
            +
                  return { :scalar => q, :numerator => num.flatten.compact, :denominator => den.flatten.compact }
         | 
| 1472 1391 | 
             
                end
         | 
| 1473 | 
            -
                bottom.gsub!(BOTTOM_REGEX) {|s| "#{$1} " * $2.to_i} if bottom
         | 
| 1474 | 
            -
                @scalar = @scalar.to_f unless @scalar.nil? || @scalar.empty?
         | 
| 1475 | 
            -
                @scalar = 1 unless @scalar.kind_of? Numeric
         | 
| 1476 | 
            -
                @scalar = @scalar.to_int if (@scalar.to_int == @scalar)
         | 
| 1477 1392 |  | 
| 1478 | 
            -
                 | 
| 1479 | 
            -
                 | 
| 1480 | 
            -
                 | 
| 1481 | 
            -
                 | 
| 1393 | 
            +
                # parse a string into a unit object.
         | 
| 1394 | 
            +
                # Typical formats like :
         | 
| 1395 | 
            +
                #  "5.6 kg*m/s^2"
         | 
| 1396 | 
            +
                #  "5.6 kg*m*s^-2"
         | 
| 1397 | 
            +
                #  "5.6 kilogram*meter*second^-2"
         | 
| 1398 | 
            +
                #  "2.2 kPa"
         | 
| 1399 | 
            +
                #  "37 degC"
         | 
| 1400 | 
            +
                #  "1"  -- creates a unitless constant with value 1
         | 
| 1401 | 
            +
                #  "GPa"  -- creates a unit with scalar 1 with units 'GPa'
         | 
| 1402 | 
            +
                #  6'4"  -- recognized as 6 feet + 4 inches
         | 
| 1403 | 
            +
                #  8 lbs 8 oz -- recognized as 8 lbs + 8 ounces
         | 
| 1404 | 
            +
                # @return [nil | Unit]
         | 
| 1405 | 
            +
                # @todo This should either be a separate class or at least a class method
         | 
| 1406 | 
            +
                def parse(passed_unit_string="0")
         | 
| 1407 | 
            +
                  unit_string = passed_unit_string.dup
         | 
| 1408 | 
            +
                  if unit_string =~ /\$\s*(#{NUMBER_REGEX})/
         | 
| 1409 | 
            +
                    unit_string = "#{$1} USD"
         | 
| 1410 | 
            +
                  end
         | 
| 1411 | 
            +
                  unit_string.gsub!("\u00b0".force_encoding('utf-8'), 'deg') if RUBY_VERSION >= '1.9' && unit_string.encoding == Encoding::UTF_8
         | 
| 1412 | 
            +
             | 
| 1413 | 
            +
                  unit_string.gsub!(/%/, 'percent')
         | 
| 1414 | 
            +
                  unit_string.gsub!(/'/, 'feet')
         | 
| 1415 | 
            +
                  unit_string.gsub!(/"/, 'inch')
         | 
| 1416 | 
            +
                  unit_string.gsub!(/#/, 'pound')
         | 
| 1417 | 
            +
             | 
| 1418 | 
            +
                  #:nocov:
         | 
| 1419 | 
            +
                  #:nocov_19:
         | 
| 1420 | 
            +
                  if defined?(Uncertain) && unit_string =~ /(\+\/-|±)/
         | 
| 1421 | 
            +
                    value, uncertainty, unit_s = unit_string.scan(UNCERTAIN_REGEX)[0]
         | 
| 1422 | 
            +
                    result                     = unit_s.unit * Uncertain.new(value.to_f, uncertainty.to_f)
         | 
| 1423 | 
            +
                    copy(result)
         | 
| 1424 | 
            +
                    return
         | 
| 1425 | 
            +
                  end
         | 
| 1426 | 
            +
                  #:nocov:
         | 
| 1427 | 
            +
                  #:nocov_19:
         | 
| 1482 1428 |  | 
| 1483 | 
            -
             | 
| 1484 | 
            -
             | 
| 1485 | 
            -
             | 
| 1486 | 
            -
             | 
| 1429 | 
            +
                  if defined?(Complex) && unit_string =~ COMPLEX_NUMBER
         | 
| 1430 | 
            +
                    real, imaginary, unit_s = unit_string.scan(COMPLEX_REGEX)[0]
         | 
| 1431 | 
            +
                    result                  = RubyUnits::Unit.new(unit_s || '1') * Complex(real.to_f, imaginary.to_f)
         | 
| 1432 | 
            +
                    copy(result)
         | 
| 1433 | 
            +
                    return
         | 
| 1434 | 
            +
                  end
         | 
| 1487 1435 |  | 
| 1488 | 
            -
             | 
| 1489 | 
            -
             | 
| 1490 | 
            -
             | 
| 1436 | 
            +
                  if defined?(Rational) && unit_string =~ RATIONAL_NUMBER
         | 
| 1437 | 
            +
                    numerator, denominator, unit_s = unit_string.scan(RATIONAL_REGEX)[0]
         | 
| 1438 | 
            +
                    result                         = RubyUnits::Unit.new(unit_s || '1') * Rational(numerator.to_i, denominator.to_i)
         | 
| 1439 | 
            +
                    copy(result)
         | 
| 1440 | 
            +
                    return
         | 
| 1441 | 
            +
                  end
         | 
| 1491 1442 |  | 
| 1492 | 
            -
             | 
| 1493 | 
            -
             | 
| 1494 | 
            -
             | 
| 1443 | 
            +
                  unit_string =~ NUMBER_REGEX
         | 
| 1444 | 
            +
                  unit = @@cached_units[$2]
         | 
| 1445 | 
            +
                  mult = ($1.empty? ? 1.0 : $1.to_f) rescue 1.0
         | 
| 1446 | 
            +
                  mult = mult.to_int if (mult.to_int == mult)
         | 
| 1447 | 
            +
                  if unit
         | 
| 1448 | 
            +
                    copy(unit)
         | 
| 1449 | 
            +
                    @scalar      *= mult
         | 
| 1450 | 
            +
                    @base_scalar *= mult
         | 
| 1451 | 
            +
                    return self
         | 
| 1452 | 
            +
                  end
         | 
| 1453 | 
            +
                  unit_string.gsub!(/<(#{@@UNIT_REGEX})><(#{@@UNIT_REGEX})>/, '\1*\2')
         | 
| 1454 | 
            +
                  unit_string.gsub!(/[<>]/, "")
         | 
| 1455 | 
            +
             | 
| 1456 | 
            +
                  if unit_string =~ /:/
         | 
| 1457 | 
            +
                    hours, minutes, seconds, microseconds = unit_string.scan(TIME_REGEX)[0]
         | 
| 1458 | 
            +
                    raise ArgumentError, "Invalid Duration" if [hours, minutes, seconds, microseconds].all? { |x| x.nil? }
         | 
| 1459 | 
            +
                    result = "#{hours || 0} h".unit +
         | 
| 1460 | 
            +
                        "#{minutes || 0} minutes".unit +
         | 
| 1461 | 
            +
                        "#{seconds || 0} seconds".unit +
         | 
| 1462 | 
            +
                        "#{microseconds || 0} usec".unit
         | 
| 1463 | 
            +
                    copy(result)
         | 
| 1464 | 
            +
                    return
         | 
| 1465 | 
            +
                  end
         | 
| 1495 1466 |  | 
| 1496 | 
            -
             | 
| 1497 | 
            -
             | 
| 1498 | 
            -
             | 
| 1499 | 
            -
             | 
| 1467 | 
            +
                  # Special processing for unusual unit strings
         | 
| 1468 | 
            +
                  # feet -- 6'5"
         | 
| 1469 | 
            +
                  feet, inches = unit_string.scan(FEET_INCH_REGEX)[0]
         | 
| 1470 | 
            +
                  if (feet && inches)
         | 
| 1471 | 
            +
                    result = RubyUnits::Unit.new("#{feet} ft") + RubyUnits::Unit.new("#{inches} inches")
         | 
| 1472 | 
            +
                    copy(result)
         | 
| 1473 | 
            +
                    return
         | 
| 1474 | 
            +
                  end
         | 
| 1500 1475 |  | 
| 1501 | 
            -
             | 
| 1502 | 
            -
             | 
| 1503 | 
            -
             | 
| 1504 | 
            -
             | 
| 1505 | 
            -
             | 
| 1476 | 
            +
                  # weight -- 8 lbs 12 oz
         | 
| 1477 | 
            +
                  pounds, oz = unit_string.scan(LBS_OZ_REGEX)[0]
         | 
| 1478 | 
            +
                  if (pounds && oz)
         | 
| 1479 | 
            +
                    result = RubyUnits::Unit.new("#{pounds} lbs") + RubyUnits::Unit.new("#{oz} oz")
         | 
| 1480 | 
            +
                    copy(result)
         | 
| 1481 | 
            +
                    return
         | 
| 1482 | 
            +
                  end
         | 
| 1506 1483 |  | 
| 1507 | 
            -
               | 
| 1508 | 
            -
             | 
| 1509 | 
            -
             | 
| 1510 | 
            -
             | 
| 1511 | 
            -
             | 
| 1512 | 
            -
             | 
| 1513 | 
            -
             | 
| 1514 | 
            -
             | 
| 1515 | 
            -
             | 
| 1516 | 
            -
             | 
| 1517 | 
            -
             | 
| 1518 | 
            -
             | 
| 1519 | 
            -
                complex   = %r{#{sci}{2,2}i}
         | 
| 1520 | 
            -
                anynumber = %r{(?:(#{complex}|#{rational}|#{sci})\b)?\s?([\D].*)?}
         | 
| 1521 | 
            -
                num, unit = string.scan(anynumber).first
         | 
| 1522 | 
            -
             | 
| 1523 | 
            -
                return [case num
         | 
| 1524 | 
            -
                  when NilClass
         | 
| 1525 | 
            -
                    1
         | 
| 1526 | 
            -
                  when complex
         | 
| 1527 | 
            -
                    if num.respond_to?(:to_c)
         | 
| 1528 | 
            -
                      num.to_c
         | 
| 1529 | 
            -
                    else
         | 
| 1530 | 
            -
                      #:nocov_19:
         | 
| 1531 | 
            -
                      Complex(*num.scan(/(#{sci})(#{sci})i/).flatten.map {|n| n.to_i})
         | 
| 1532 | 
            -
                      #:nocov_19:
         | 
| 1484 | 
            +
                                                                                # more than one per.  I.e., "1 m/s/s"
         | 
| 1485 | 
            +
                  raise(ArgumentError, "'#{passed_unit_string}' Unit not recognized") if unit_string.count('/') > 1
         | 
| 1486 | 
            +
                  raise(ArgumentError, "'#{passed_unit_string}' Unit not recognized") if unit_string.scan(/\s[02-9]/).size > 0
         | 
| 1487 | 
            +
                  @scalar, top, bottom = unit_string.scan(UNIT_STRING_REGEX)[0] #parse the string into parts
         | 
| 1488 | 
            +
                  top.scan(TOP_REGEX).each do |item|
         | 
| 1489 | 
            +
                    n = item[1].to_i
         | 
| 1490 | 
            +
                    x = "#{item[0]} "
         | 
| 1491 | 
            +
                    case
         | 
| 1492 | 
            +
                      when n>=0
         | 
| 1493 | 
            +
                        top.gsub!(/#{item[0]}(\^|\*\*)#{n}/) { |s| x * n }
         | 
| 1494 | 
            +
                      when n<0
         | 
| 1495 | 
            +
                        bottom = "#{bottom} #{x * -n}"; top.gsub!(/#{item[0]}(\^|\*\*)#{n}/, "")
         | 
| 1533 1496 | 
             
                    end
         | 
| 1534 | 
            -
                   | 
| 1535 | 
            -
             | 
| 1536 | 
            -
                   | 
| 1537 | 
            -
             | 
| 1538 | 
            -
             | 
| 1539 | 
            -
             | 
| 1497 | 
            +
                  end
         | 
| 1498 | 
            +
                  bottom.gsub!(BOTTOM_REGEX) { |s| "#{$1} " * $2.to_i } if bottom
         | 
| 1499 | 
            +
                  @scalar = @scalar.to_f unless @scalar.nil? || @scalar.empty?
         | 
| 1500 | 
            +
                  @scalar = 1 unless @scalar.kind_of? Numeric
         | 
| 1501 | 
            +
                  @scalar = @scalar.to_int if (@scalar.to_int == @scalar)
         | 
| 1502 | 
            +
             | 
| 1503 | 
            +
                  @numerator   ||= UNITY_ARRAY
         | 
| 1504 | 
            +
                  @denominator ||= UNITY_ARRAY
         | 
| 1505 | 
            +
                  @numerator = top.scan(RubyUnits::Unit.unit_match_regex).delete_if { |x| x.empty? }.compact if top
         | 
| 1506 | 
            +
                  @denominator = bottom.scan(RubyUnits::Unit.unit_match_regex).delete_if { |x| x.empty? }.compact if bottom
         | 
| 1507 | 
            +
             | 
| 1508 | 
            +
                  # eliminate all known terms from this string.  This is a quick check to see if the passed unit
         | 
| 1509 | 
            +
                  # contains terms that are not defined.
         | 
| 1510 | 
            +
                  used = "#{top} #{bottom}".to_s.gsub(RubyUnits::Unit.unit_match_regex, '').gsub(/[\d\*, "'_^\/\$]/, '')
         | 
| 1511 | 
            +
                  raise(ArgumentError, "'#{passed_unit_string}' Unit not recognized") unless used.empty?
         | 
| 1512 | 
            +
             | 
| 1513 | 
            +
                  @numerator = @numerator.map do |item|
         | 
| 1514 | 
            +
                    @@PREFIX_MAP[item[0]] ? [@@PREFIX_MAP[item[0]], @@UNIT_MAP[item[1]]] : [@@UNIT_MAP[item[1]]]
         | 
| 1515 | 
            +
                  end.flatten.compact.delete_if { |x| x.empty? }
         | 
| 1516 | 
            +
             | 
| 1517 | 
            +
                  @denominator = @denominator.map do |item|
         | 
| 1518 | 
            +
                    @@PREFIX_MAP[item[0]] ? [@@PREFIX_MAP[item[0]], @@UNIT_MAP[item[1]]] : [@@UNIT_MAP[item[1]]]
         | 
| 1519 | 
            +
                  end.flatten.compact.delete_if { |x| x.empty? }
         | 
| 1520 | 
            +
             | 
| 1521 | 
            +
                  @numerator = UNITY_ARRAY if @numerator.empty?
         | 
| 1522 | 
            +
                  @denominator = UNITY_ARRAY if @denominator.empty?
         | 
| 1523 | 
            +
                  return self
         | 
| 1524 | 
            +
                end
         | 
| 1540 1525 |  | 
| 1541 | 
            -
             | 
| 1542 | 
            -
             | 
| 1543 | 
            -
             | 
| 1544 | 
            -
             | 
| 1545 | 
            -
             | 
| 1546 | 
            -
                @@UNIT_REGEX ||= @@UNIT_MAP.keys.sort_by {|unit_name| [unit_name.length, unit_name]}.reverse.join('|')
         | 
| 1547 | 
            -
              end
         | 
| 1526 | 
            +
                # return an array of base units
         | 
| 1527 | 
            +
                # @return [Array]
         | 
| 1528 | 
            +
                def self.base_units
         | 
| 1529 | 
            +
                  return @@base_units ||= @@definitions.dup.delete_if { |_, defn| !defn.base? }.keys.map { |u| RubyUnits::Unit.new(u) }
         | 
| 1530 | 
            +
                end
         | 
| 1548 1531 |  | 
| 1549 | 
            -
             | 
| 1550 | 
            -
             | 
| 1551 | 
            -
             | 
| 1552 | 
            -
             | 
| 1553 | 
            -
                 | 
| 1554 | 
            -
             | 
| 1532 | 
            +
                # parse a string consisting of a number and a unit string
         | 
| 1533 | 
            +
                # @param [String] string
         | 
| 1534 | 
            +
                # @return [Array] consisting of [Numeric, "unit"]
         | 
| 1535 | 
            +
                # @private
         | 
| 1536 | 
            +
                def self.parse_into_numbers_and_units(string)
         | 
| 1537 | 
            +
                  # scientific notation.... 123.234E22, -123.456e-10
         | 
| 1538 | 
            +
                  sci       = %r{[+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*}
         | 
| 1539 | 
            +
                  # rational numbers.... -1/3, 1/5, 20/100
         | 
| 1540 | 
            +
                  rational  = %r{[+-]?\d+\/\d+}
         | 
| 1541 | 
            +
                  # complex numbers... -1.2+3i, +1.2-3.3i
         | 
| 1542 | 
            +
                  complex   = %r{#{sci}{2,2}i}
         | 
| 1543 | 
            +
                  anynumber = %r{(?:(#{complex}|#{rational}|#{sci})\b)?\s?([^\d\.].*)?}
         | 
| 1544 | 
            +
                  num, unit = string.scan(anynumber).first
         | 
| 1545 | 
            +
             | 
| 1546 | 
            +
                  return [case num
         | 
| 1547 | 
            +
                            when NilClass
         | 
| 1548 | 
            +
                              1
         | 
| 1549 | 
            +
                            when complex
         | 
| 1550 | 
            +
                              if num.respond_to?(:to_c)
         | 
| 1551 | 
            +
                                num.to_c
         | 
| 1552 | 
            +
                              else
         | 
| 1553 | 
            +
                                #:nocov_19:
         | 
| 1554 | 
            +
                                Complex(*num.scan(/(#{sci})(#{sci})i/).flatten.map { |n| n.to_i })
         | 
| 1555 | 
            +
                                #:nocov_19:
         | 
| 1556 | 
            +
                              end
         | 
| 1557 | 
            +
                            when rational
         | 
| 1558 | 
            +
                              Rational(*num.split("/").map { |x| x.to_i })
         | 
| 1559 | 
            +
                            else
         | 
| 1560 | 
            +
                              num.to_f
         | 
| 1561 | 
            +
                          end, unit.to_s.strip]
         | 
| 1562 | 
            +
                end
         | 
| 1555 1563 |  | 
| 1556 | 
            -
             | 
| 1557 | 
            -
             | 
| 1558 | 
            -
             | 
| 1559 | 
            -
             | 
| 1560 | 
            -
                 | 
| 1561 | 
            -
             | 
| 1564 | 
            +
                # return a fragment of a regex to be used for matching units or reconstruct it if hasn't been used yet.
         | 
| 1565 | 
            +
                # Unit names are reverse sorted by length so the regexp matcher will prefer longer and more specific names
         | 
| 1566 | 
            +
                # @return [String]
         | 
| 1567 | 
            +
                # @private
         | 
| 1568 | 
            +
                def self.unit_regex
         | 
| 1569 | 
            +
                  @@UNIT_REGEX ||= @@UNIT_MAP.keys.sort_by { |unit_name| [unit_name.length, unit_name] }.reverse.join('|')
         | 
| 1570 | 
            +
                end
         | 
| 1562 1571 |  | 
| 1563 | 
            -
             | 
| 1564 | 
            -
                 | 
| 1572 | 
            +
                # return a regex used to match units
         | 
| 1573 | 
            +
                # @return [RegExp]
         | 
| 1574 | 
            +
                # @private
         | 
| 1575 | 
            +
                def self.unit_match_regex
         | 
| 1576 | 
            +
                  @@UNIT_MATCH_REGEX ||= /(#{RubyUnits::Unit.prefix_regex})*?(#{RubyUnits::Unit.unit_regex})\b/
         | 
| 1577 | 
            +
                end
         | 
| 1578 | 
            +
             | 
| 1579 | 
            +
                # return a regexp fragment used to match prefixes
         | 
| 1580 | 
            +
                # @return [String]
         | 
| 1581 | 
            +
                # @private
         | 
| 1582 | 
            +
                def self.prefix_regex
         | 
| 1583 | 
            +
                  return @@PREFIX_REGEX ||= @@PREFIX_MAP.keys.sort_by { |prefix| [prefix.length, prefix] }.reverse.join('|')
         | 
| 1584 | 
            +
                end
         | 
| 1585 | 
            +
             | 
| 1586 | 
            +
                def self.temp_regex
         | 
| 1587 | 
            +
                  @@TEMP_REGEX ||= Regexp.new "(?:#{
         | 
| 1565 1588 | 
             
                  temp_units=%w(tempK tempC tempF tempR degK degC degF degR)
         | 
| 1566 | 
            -
                  aliases=temp_units.map{|unit| d=Unit.definition(unit); d && d.aliases}.flatten.compact
         | 
| 1567 | 
            -
                  regex_str= aliases.empty? ? '(?!x)x' : aliases.join('|')
         | 
| 1589 | 
            +
                  aliases   =temp_units.map { |unit| d=RubyUnits::Unit.definition(unit); d && d.aliases }.flatten.compact
         | 
| 1590 | 
            +
                  regex_str = aliases.empty? ? '(?!x)x' : aliases.join('|')
         | 
| 1568 1591 | 
             
                  regex_str
         | 
| 1569 | 
            -
             | 
| 1570 | 
            -
             | 
| 1592 | 
            +
                  })"
         | 
| 1593 | 
            +
                end
         | 
| 1571 1594 |  | 
| 1572 | 
            -
             | 
| 1573 | 
            -
             | 
| 1574 | 
            -
             | 
| 1575 | 
            -
             | 
| 1576 | 
            -
             | 
| 1577 | 
            -
             | 
| 1578 | 
            -
             | 
| 1579 | 
            -
             | 
| 1580 | 
            -
             | 
| 1581 | 
            -
             | 
| 1582 | 
            -
             | 
| 1583 | 
            -
             | 
| 1584 | 
            -
             | 
| 1585 | 
            -
             | 
| 1586 | 
            -
             | 
| 1587 | 
            -
             | 
| 1595 | 
            +
                # inject a definition into the internal array and set it up for use
         | 
| 1596 | 
            +
                # @private
         | 
| 1597 | 
            +
                def self.use_definition(definition)
         | 
| 1598 | 
            +
                  @@UNIT_MATCH_REGEX = nil #invalidate the unit match regex
         | 
| 1599 | 
            +
                  @@TEMP_REGEX       = nil #invalidate the temp regex
         | 
| 1600 | 
            +
                  if definition.prefix?
         | 
| 1601 | 
            +
                    @@PREFIX_VALUES[definition.name] = definition.scalar
         | 
| 1602 | 
            +
                    definition.aliases.each { |_alias| @@PREFIX_MAP[_alias] = definition.name }
         | 
| 1603 | 
            +
                    @@PREFIX_REGEX = nil #invalidate the prefix regex
         | 
| 1604 | 
            +
                  else
         | 
| 1605 | 
            +
                    @@UNIT_VALUES[definition.name]          = {}
         | 
| 1606 | 
            +
                    @@UNIT_VALUES[definition.name][:scalar] = definition.scalar
         | 
| 1607 | 
            +
                    @@UNIT_VALUES[definition.name][:numerator] = definition.numerator if definition.numerator
         | 
| 1608 | 
            +
                    @@UNIT_VALUES[definition.name][:denominator] = definition.denominator if definition.denominator
         | 
| 1609 | 
            +
                    definition.aliases.each { |_alias| @@UNIT_MAP[_alias] = definition.name }
         | 
| 1610 | 
            +
                    @@UNIT_REGEX = nil #invalidate the unit regex
         | 
| 1611 | 
            +
                  end
         | 
| 1588 1612 | 
             
                end
         | 
| 1589 1613 | 
             
              end
         | 
| 1590 | 
            -
             | 
| 1591 1614 | 
             
            end
         |