lib/unitwise/scale.rb
module Unitwise
# A Unitwise::Scale represents a value and a unit, sort of like a vector, it
# has two components. In this case, it's a value and unit rather than a
# magnitude and direction. This class should be considered mostly privateish.
class Scale
liner :value, :unit
include Unitwise::Compatible
def initialize(value, unit)
self.value = if value.is_a? self.class
value.convert_to(unit).value
else
value
end
self.unit = unit
freeze
end
# Set the unit vector.
# @param value [String, Unitwise::Unit]
# @api public
def unit=(value)
@unit = value.is_a?(Unit) ? value : Unit.new(value)
end
# List the atoms associated with this scale's unit.
# @return [Array]
# @api public
def atoms
unit.atoms
end
# List the terms associated with this scale's unit.
# @return [Array]
# @api public
def terms
unit.terms
end
# Is this scale's unit special?
# @return [true, false]
# @api public
def special?
unit.special?
end
# Get a scalar value for this scale.
# @param magnitude [Numeric] An optional magnitude on this scale.
# @return [Numeric] A scalar value on a linear scale
# @api public
def scalar(magnitude = value)
if special?
unit.scalar(magnitude)
else
Number.rationalize(value) * Number.rationalize(unit.scalar)
end
end
# Get a magnitude based on a linear scale value. Only used by scales with
# special atoms in it's hierarchy.
# @param scalar [Numeric] A linear scalar value
# @return [Numeric] The equivalent magnitude on this scale
# @api public
def magnitude(scalar = scalar())
if special?
unit.magnitude(scalar)
else
value * unit.magnitude
end
end
# The base terms this scale's unit is derived from
# @return [Array] An array of Unitwise::Term
# @api public
def root_terms
unit.root_terms
end
memoize :root_terms
# How far away is this instances unit from the deepest level atom.
# @return [Integer]
# @api public
def depth
unit.depth + 1
end
memoize :depth
# Attempts to coerce the value to the simplest Numeric that fully expresses
# it's value. For instance a value of 1.0 would return 1, a value of
# #<BigDecimal:7f9558d559b8,'0.45E1',18(18)> would return 4.5.
# @return [Numeric]
# @api public
def simplified_value
Unitwise::Number.simplify(value)
end
memoize :simplified_value
def expression
unit.expression
end
# Convert to a simple string representing the scale.
# @api public
def to_s(mode = nil)
unit_string = unit.to_s(mode)
if unit_string && unit_string != '1'
"#{simplified_value} #{unit_string}"
else
simplified_value.to_s
end
end
def inspect
"#<#{self.class} value=#{simplified_value} unit=#{unit}>"
end
# Redefine hash for apropriate hash/key lookup
# @api semipublic
def hash
[value, unit.to_s, self.class].hash
end
memoize :hash
# Redefine hash equality to match the hashes
# @api semipublic
def eql?(other)
hash == other.hash
end
end
end