require "units/units" # The unit part of a NumericWithUnits is a UnitsHash - a Hash from UnitsUnit # instances to powers. class UnitsHash < Hash @@debug = false def self.debug=(value) @@debug = value end def initialize(unit = nil,power = 1) # :nodoc if unit if unit.kind_of? UnitsUnit self[unit] = power elsif unit.kind_of? UnitsHash unit.each { |k,v| self[k] = v*power } else raise UnitsException.new("invalid unit: #{unit}") end end end # Returns a String reprentation of the instance. If there is only one item in # the UnitsHash, whole names are rendered; otherwise abbreviations are used. def to_s(numeric = 1,use_abbrevs = false) if size == 1 && (su = select {|k,v| v == 1}).size == 1 && !use_abbrevs su = su.to_a[0][0] ((numeric == 1)? su.name : su.plural).gsub(/_/,' ') else abbrevs_to_s end end def abbrevs_to_s # :nodoc: p = select {|k,v| v > 0}.sort{|e1,e2| e1[1] <=> e2[1]}.collect {|e| "#{e[0].abbrevs[0] || e[0].name}#{(e[1] == 1) ? "" : "^#{e[1]}"}"} n = select {|k,v| v < 0}.sort{|e1,e2| e1[1] <=> e2[1]}.collect {|e| "#{e[0].abbrevs[0] || e[0].name}#{(e[1] == -1) ? "" : "^#{-e[1]}"}"} numerator = (p.size > 0)? p.join(" ") : (n.size > 0)? "1" : "" denominator = (n.size > 0)? ((p.size > 0)? " / " : "/")+n.join(" ") : "" "#{numerator}#{denominator}" end # Raises and returns the instance after each of its exponents has been raised # by the given power. def power!(power) self.each { |k,v| self[k] = v*power } end # Returns a new UnitsHash whose value is the instance raised to the given # power. def **(power) clone.power!(power) end # Returns a new UnitsHash whose value is the instance's units and powers have # been merged with the given value raised to the power. def merge(value,power=1) clone.merge!(value,power) end # Merges and returns the instance after the value raised to the power has # been merged into it. def merge!(value,power=1) value.unit.each { |k,v| self[k] = (self[k] || 0) + v*power delete k if self[k] == 0 } self end alias :* :merge # Returns the UnitsMeasure of the instance if defined. def measure measure = UnitsMeasure.new each { |unit,power| measure.merge_derivation unit.units_system.units_measure => power } Units.find_by_derivation(measure.derived) end # Returns true is any of the instance's components are derived. def derived? keys.select{|unit| unit.units_system.units_measure.derived != nil}.size > 0 end # Return a NumericWithUnits that represents the instance when all derived # units have been replaced with the units from which they derive. def reduce puts "UnitsHash:reduce #{to_s}" if @@debug factor = 1.0 new_unit = UnitsHash.new each do |unit,power| puts " #{unit} #{power}" if @@debug factor *= unit.equals.numeric**power puts " #{factor}" if @@debug new_unit.merge!(unit.equals,power) end factor.unite new_unit end # Return true if the instance is not equal to 1, ie. has at least one non-zero # exponent. def has_units? (self.select {|k,v| v != 0.0}).size > 0 end end