Class: Pulo::Quantity

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/pulo/quantity/quantity.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value = nil, unit = nil) ⇒ Quantity



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/pulo/quantity/quantity.rb', line 90

def initialize(value=nil, unit=nil)
  value ||= 1.0
  if unit
    if unit.is_a?(Symbol)
      raise "Unit #{unit.to_s} not defined for #{self.class.quantity_name}." unless self.class.units[unit]

      self.unit=self.class.units[unit]
    else
      self.unit=unit
    end
  else
    self.unit=self.class.base_unit
  end
  self.value=Float(value) unless self.value.is_a?(Float)
  self
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_sym, *arguments, &block) ⇒ Object

Pass unknown methods through to the underlying value (Float) if it responds. eg floor and modulo methods



119
120
121
122
123
# File 'lib/pulo/quantity/quantity.rb', line 119

def method_missing(method_sym, *arguments, &block)
  if self.value.respond_to?(method_sym)
    self.value.send(method_sym,*arguments,&block)
  end
end

Class Attribute Details

.base_unitObject

Returns the value of attribute base_unit.



47
48
49
# File 'lib/pulo/quantity/quantity.rb', line 47

def base_unit
  @base_unit
end

.dimensionsObject

Returns the value of attribute dimensions.



48
49
50
# File 'lib/pulo/quantity/quantity.rb', line 48

def dimensions
  @dimensions
end

Instance Attribute Details

#unitObject

Instance variables and methods - Child Quantities



88
89
90
# File 'lib/pulo/quantity/quantity.rb', line 88

def unit
  @unit
end

#valueObject

Instance variables and methods - Child Quantities



88
89
90
# File 'lib/pulo/quantity/quantity.rb', line 88

def value
  @value
end

Class Method Details

.best_si_unit(scale) ⇒ Object



50
51
52
53
54
# File 'lib/pulo/quantity/quantity.rb', line 50

def best_si_unit(scale)
  @si_unit_scales.min_by do |unit|
    (scale-unit[0]).abs
  end[1]
end

.method_missing(method_sym, *arguments, &block) ⇒ Object



56
57
58
59
# File 'lib/pulo/quantity/quantity.rb', line 56

def method_missing(method_sym, *arguments, &block)
  puts "#{quantity_name} doesn't have a unit #{method_sym}."
  puts "Available units are: " + units.map{|unt| unt[1].name }.join(', ')
end

.quantity_nameObject



61
62
63
# File 'lib/pulo/quantity/quantity.rb', line 61

def quantity_name
  self.name.split('::')[1]
end

.si_unit_scalesObject



44
# File 'lib/pulo/quantity/quantity.rb', line 44

def si_unit_scales; @si_unit_scales ||={};end

.synonymsObject



45
# File 'lib/pulo/quantity/quantity.rb', line 45

def synonyms; @synonyms ||=[]; end

.unitsObject



43
# File 'lib/pulo/quantity/quantity.rb', line 43

def units; @units ||={}; end

.units_sortedObject



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/pulo/quantity/quantity.rb', line 65

def units_sorted
  self.units.values.sort do |a,b|
    next -1 if a.is_si? && !b.is_si?

    next 1 if !a.is_si? && b.is_si?

    if a.is_si?
      next -1 if a.scale<b.scale
      next 1
    else
      if a.si_convert_unit==b.si_convert_unit
        next a.si_convert_factor<=>b.si_convert_factor
      else
        a.si_convert_unit<=>b.si_convert_unit
      end
    end
  end
end

Instance Method Details

#*(other) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/pulo/quantity/quantity.rb', line 159

def *(other)
  case
    when other.is_a?(Numeric)
      self.class.new self.value*other,self.unit
    when other.is_a?(Quantity)
      new_dims=self.class.dimensions+other.class.dimensions

      #get both quantities to their equivalent SI if needed

      q1=self; q1=q1.to_si unless q1.is_si?
      q2=other; q2=q2.to_si unless q2.is_si?

      target_scale=q1.unit.scale+q2.unit.scale
      target_value=q1.value*q2.value
      existing_or_new_quantity new_dims,target_scale,target_value
    else
      raise  QuantitiesException.new("Cannot multiply a #{other.class.name} and a #{self.class.name}")
  end
end

#**(power) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
# File 'lib/pulo/quantity/quantity.rb', line 194

def **(power)
  raise  QuantitiesException.new('Can only raise a quantity to an integer power') unless power.is_a?(Integer)

  new_dims=self.class.dimensions*power
  q1=self; q1=q1.to_si unless q1.is_si?

  target_scale=q1.unit.scale*power
  target_value=q1.value**power

  existing_or_new_quantity new_dims,target_scale,target_value
end

#+(other) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/pulo/quantity/quantity.rb', line 131

def +(other)
  case
    when other.is_a?(Numeric)
      self.class.new self.value+other,self.unit
    when other.class.dimensions==self.class.dimensions
      if self.unit==other.unit
        self.class.new self.value+other.value,self.unit
      else
        self.class.new self.value+other.send(self.unit.name).value,self.unit
      end
    else
      raise  QuantitiesException.new("Cannot add a #{other.class.name} to a #{self.class.name}")
  end
end

#-(other) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/pulo/quantity/quantity.rb', line 145

def -(other)
  case
    when other.is_a?(Numeric)
      self.class.new self.value-other,self.unit
    when other.class.dimensions==self.class.dimensions
      if self.unit==other.unit
        self.class.new self.value-other.value,self.unit
      else
        self.class.new self.value-other.send(self.unit.name).value,self.unit
      end
    else
      raise  QuantitiesException.new("Cannot minus a #{other.class.name} from a #{self.class.name}")
  end
end

#-@Object



128
129
130
# File 'lib/pulo/quantity/quantity.rb', line 128

def -@
  self.class.new -self.value,self.unit
end

#/(other) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/pulo/quantity/quantity.rb', line 177

def /(other)
  case
    when other.is_a?(Numeric)
      self.class.new self.value/other,self.unit
    when other.is_a?(Quantity)
      new_dims=self.class.dimensions-other.class.dimensions

      q1=self; q1=q1.to_si unless q1.is_si?
      q2=other; q2=q2.to_si unless q2.is_si?

      target_scale=q1.unit.scale-q2.unit.scale
      target_value=q1.value/q2.value
      existing_or_new_quantity new_dims,target_scale,target_value
    else
      raise  QuantitiesException.new("Cannot divide a #{self.class.name} by a #{other.class.name}")
  end
end

#<=>(other) ⇒ Object



221
222
223
224
225
226
227
228
229
# File 'lib/pulo/quantity/quantity.rb', line 221

def <=>(other)
  unless (other.is_a?(Quantity) && self.class.dimensions==other.class.dimensions) || (other.is_a?(Numeric) && self.class==Dimensionless)
    raise  QuantitiesException.new("Can only compare quantities with same dimensions (given: #{self.class} and #{other.class}).")
  end
  if other.is_a?(Numeric)
    other=Dimensionless.n(other)
  end
  to_base_unit.value<=>other.to_base_unit.value
end

#dimensionsObject



107
108
109
# File 'lib/pulo/quantity/quantity.rb', line 107

def dimensions
  self.class.dimensions
end

#existing_or_new_quantity(new_dims, target_scale, target_value) ⇒ Object



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/pulo/quantity/quantity.rb', line 231

def existing_or_new_quantity(new_dims, target_scale, target_value)
  if Pulo.quantities[new_dims]
    klass=Pulo.quantities[new_dims][0]
    unit=klass.best_si_unit Math.log10(target_value.abs) + target_scale
    klass.new(target_value*10**(target_scale-unit.scale), unit)
  else
    qname=new_dims.to_s(true).gsub(/-/, '_')
    QuantityBuilder.build(qname) do
      # noinspection RubyArgCount

      dimensions new_dims.spec
      si_unit '0.0'+qname, '', new_dims.to_s, 1.0
      unless target_scale==0
        si_unit target_scale.to_s+qname, '', new_dims.to_s+'*10^'+target_scale.to_s, 1.0*10**target_scale
      end
    end.klass.send(target_scale.to_s+qname, target_value)
  end
end

#inObject



116
# File 'lib/pulo/quantity/quantity.rb', line 116

def in; self; end

#inverseObject



125
126
127
# File 'lib/pulo/quantity/quantity.rb', line 125

def inverse
  Dimensionless.new/self
end

#is_si?Boolean



253
254
255
# File 'lib/pulo/quantity/quantity.rb', line 253

def is_si?
  @unit.is_si?
end

#rescaleObject

Converts to SI and most ‘natural’ scale



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/pulo/quantity/quantity.rb', line 263

def rescale
  unless self.is_si?
    return self.to_base_unit.rescale
  end
  scale=Math.log10(self.value)+self.unit.scale

  unit_scales=self.class.si_unit_scales.sort

  if scale<unit_scales[0][0]
    return self.send unit_scales[0][1].name
  end
  if scale>=unit_scales.last[0]
    return self.send unit_scales.last[1].name
  end
  unit_scales.each_cons(2) do |us|
    if us[0][0]<=scale && us[1][0]>scale
      return self.send us[0][1].name
    end
  end
end

#rt(power) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/pulo/quantity/quantity.rb', line 205

def rt(power)
  raise  QuantitiesException.new('Can only do integer roots') unless power.is_a?(Integer)

  self.class.dimensions.spec.each do |dim|
    if dim[1]/power.to_f % 1 != 0
      raise  QuantitiesException.new('Root would lead to non-integer dimensions')
    end
  end
  new_dims=self.class.dimensions/power

  q1=self; q1=q1.to_si unless q1.is_si?

  target_scale=q1.unit.scale/power
  target_value=q1.value**(1.0/power)
  existing_or_new_quantity new_dims,target_scale,target_value
end

#toObject



115
# File 'lib/pulo/quantity/quantity.rb', line 115

def to; self; end

#to_base_unitObject



249
250
251
# File 'lib/pulo/quantity/quantity.rb', line 249

def to_base_unit
  self.send(self.class.base_unit.name)
end

#to_s(precision = nil, supress_quantity_names = false) ⇒ Object



111
112
113
# File 'lib/pulo/quantity/quantity.rb', line 111

def to_s(precision=nil, supress_quantity_names=false)
  "#{self.class.quantity_name + ': ' unless Pulo.supress_quantity_names || supress_quantity_names || self.class==Dimensionless}#{NumberToRoundedConverter.convert(@value,precision)} #{@unit.abbreviation}"
end

#to_siObject



257
258
259
260
# File 'lib/pulo/quantity/quantity.rb', line 257

def to_si
  return self if self.is_si?
  self.send(self.unit.si_convert_unit)
end