Class: SY::Quantity

Inherits:
Object
  • Object
show all
Includes:
NameMagic
Defined in:
lib/sy/quantity.rb

Overview

Quantity.

Constant Summary collapse

RELATIVE_QUANTITY_NAME_SUFFIX =

name_set_hook do |name, new_instance, old_name|

new_instance.protect!; name

end

"±"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(relative: nil, composition: nil, of: nil, measure: nil, amount: nil, coerces: [], coerces_to: [], **nn) ⇒ Quantity

Standard constructor of a metrological quantity. A quantity may have a name and a dimension.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/sy/quantity.rb', line 59

def initialize( relative: nil,
                composition: nil,
                of: nil,
                measure: nil,
                amount: nil,
                coerces: [],
                coerces_to: [],
                **nn )
  puts "Quantity init relative: #{relative}, composition: #{composition}, measure: #{measure}, #{nn}" if SY::DEBUG
  @units = [] # array of units as favored by this quantity
  @relative = relative
  if composition.nil? then
    puts "Composition not given, dimension expected." if SY::DEBUG
    @dimension = SY.Dimension( of )
  else
    puts "Composition received (#{composition})." if SY::DEBUG
    @composition = SY::Composition[ composition ]
    @dimension = @composition.dimension
  end
  @measure = measure.is_a?( SY::Measure ) ? measure :
    if measure.nil? then
      if amount.nil? then nil else
        SY::Measure.simple_scale( amount )
      end
    else
      fail ArgumentError, ":amount and :measure shouldn't be both supplied" unless amount.nil?
      SY::Measure.simple_scale( measure )
    end
  coerces( *Array( coerces ) )
  Array( coerces_to ).each { |qnt| qnt.coerces self }
  puts "Composition of the initialized instance is #{composition}." if SY::DEBUG
  puts "Initialized instance is #{relative? ? :relative : :absolute}" if SY::DEBUG
  puts "Initialized instance object_id is #{object_id}" if SY::DEBUG
end

Instance Attribute Details

#compositionObject (readonly)

Creates a composition from a dimension, or acts as composition getter if this has already been specified.



154
155
156
# File 'lib/sy/quantity.rb', line 154

def composition
  @composition
end

#dimensionObject (readonly)

Returns the value of attribute dimension.



15
16
17
# File 'lib/sy/quantity.rb', line 15

def dimension
  @dimension
end

#unitsObject (readonly)

Returns the value of attribute units.



15
16
17
# File 'lib/sy/quantity.rb', line 15

def units
  @units
end

Class Method Details

.dimensionless(*args) ⇒ Object Also known as: zero

Dimensionless quantity constructor alias.

Raises:

  • (TErr)


47
48
49
50
51
52
# File 'lib/sy/quantity.rb', line 47

def dimensionless *args
  

.of(*args) ⇒ Object

Dimension-based quantity constructor. Examples: Quantity.of Dimension.new( "L.T⁻²" ) Quantity.of "L.T⁻²"



22
23
24
25
26
27
28
29
30
31
# File 'lib/sy/quantity.rb', line 22

def of *args
  

.standard(of: nil) ⇒ Object

Standard quantity. Example: Quantity.standard of: Dimension.new( "L.T⁻²" ) or <tt>Quantity.standard of: “L.T⁻²” (Both should give Acceleration as their result.)



39
40
41
42
43
# File 'lib/sy/quantity.rb', line 39

def standard( of: nil )
  fail ArgumentError, "Dimension (:of argument) must be given!" if of.nil?
  puts "Constructing standard quantity of #{of} dimension" if SY::DEBUG
  return SY.Dimension( of ).standard_quantity
end

Instance Method Details

#*(q2) ⇒ Object

Quantity multiplication.



309
310
311
312
313
314
# File 'lib/sy/quantity.rb', line 309

def * q2
  puts "#{self.name} * #{q2.name}" if SY::DEBUG
  rel = [ self, q2 ].any? &:relative
  ( SY::Composition[ self => 1 ] + SY::Composition[ q2 => 1 ] )
    .to_quantity relative: rel
end

#**(num) ⇒ Object

Quantity raising to a number.



327
328
329
330
# File 'lib/sy/quantity.rb', line 327

def ** num
  puts "#{self.name} ** #{num}" if SY::DEBUG
  SY::Composition[ self => num ].to_quantity relative: relative?
end

#/(q2) ⇒ Object

Quantity division.



318
319
320
321
322
323
# File 'lib/sy/quantity.rb', line 318

def / q2
  puts "#{self.name} / #{q2.name}" if SY::DEBUG
  rel = [ self, q2 ].any? &:relative?
  ( SY::Composition[ self => 1 ] - SY::Composition[ q2 => 1 ] )
    .to_quantity relative: rel
end

#absoluteObject

Absolute quantity related to this quantity.



256
257
258
# File 'lib/sy/quantity.rb', line 256

def absolute
  absolute? ? self : colleague
end

#absolute?Boolean

Is the quantity absolute? (Opposite of #relative?)

Returns:

  • (Boolean)


222
223
224
# File 'lib/sy/quantity.rb', line 222

def absolute?
  not relative?
end

#coerce(other) ⇒ Object



376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/sy/quantity.rb', line 376

def coerce other
  case other
  when Numeric then
    return SY::Amount.relative, self
  when SY::Quantity then
    # By default, coercion between quantities doesn't exist. The basic
    # purpose of having quantities is to avoid mutual mixing of
    # incompatible magnitudes, as in "one cannot sum pears with apples".
    # 
    if other == self then
      return other, self
    else
      raise SY::QuantityError, "#{other} and #{self} do not mix!"
    end
  else
    raise TErr, "#{self} cannot be coerced into a #{other.class}!"
  end
end

#coerces(*other_quantities) ⇒ Object

Quantities explicitly coerced by this quantity.



105
106
107
108
109
# File 'lib/sy/quantity.rb', line 105

def coerces *other_quantities
  if other_quantities.empty? then @coerces ||= [] else
    other_quantities.each { |qnt| coerces << qnt }
  end
end

#coerces?(other) ⇒ Boolean

Is the quantity supplied as the argument coerced by this quantity?

Returns:

  • (Boolean)


113
114
115
116
117
118
119
# File 'lib/sy/quantity.rb', line 113

def coerces? other
  other == self || coerces.include?( other ) ||
    colleague.coerces.include?( other.colleague ) ||
    if simple? then false else
      composition.coerces? other.composition
    end
end

#colleagueObject

For an absolute quantity, colleague is the corresponding relative quantity. Vice-versa, for a relative quantity, colleague is its absolute quantity.



235
236
237
# File 'lib/sy/quantity.rb', line 235

def colleague
  @colleague ||= construct_colleague
end

#dimensionless?Boolean

Is the quantity dimensionless?

Returns:

  • (Boolean)


334
335
336
# File 'lib/sy/quantity.rb', line 334

def dimensionless?
  dimension.zero?
end

#inspectObject

Inspect string.



372
373
374
# File 'lib/sy/quantity.rb', line 372

def inspect
  "#<Quantity:#{to_s}>"
end

#irreducible?Boolean

Irreducible quantity is one which cannot or should not be reduced to its components in the process of quantity simplification.

Returns:

  • (Boolean)


147
148
149
# File 'lib/sy/quantity.rb', line 147

def irreducible?
  simple? or protected?
end

#magnitude(amount) ⇒ Object

Constructs a absolute magnitude of this quantity.



268
269
270
271
272
273
274
# File 'lib/sy/quantity.rb', line 268

def magnitude amount
  puts "self.object_id is #{object_id}" if SY::DEBUG
  puts "composition is #{composition}" if SY::DEBUG
  puts "Constructing #{self}#magnitude with amount #{amount}." if SY::DEBUG
  Magnitude().new( of: self, amount: amount )
    .tap { puts "#{self}#magnitude constructed!" if SY::DEBUG }
end

#measure(of: nil) ⇒ Object

Creates a measure of a specified other quantity. If no :of is specified, simply acts as a getter of @measure attribute.

Raises:



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/sy/quantity.rb', line 194

def measure( of: nil )
  return @measure if of.nil? # act as simple getter if :of not specified
  puts "#{self.inspect} asked about measure of #{of}" if SY::DEBUG
  return SY::Measure.identity if of == self or of == colleague
  raise SY::DimensionError, "#{self} vs. #{of}!" unless same_dimension? of
  return of.measure( of: of.standard ).inverse if standardish?
  m = begin
        puts "composition is #{composition}, class #{composition.class}" if SY::DEBUG
        measure ||
          colleague.measure ||
          composition.infer_measure
      rescue NoMethodError
        fail SY::QuantityError, "Measure of #{of} by #{self} impossible!"
      end
  return m if of.standardish?
  puts "#{of} not standardish, obtained measure relates to #{standard}, and " +
    "it will have to be extended to #{of}." if SY::DEBUG
  return m * standard.measure( of: of )
end

#new_standard_unit(amount: nil, measure: nil, **nn) ⇒ Object

Constructor of a new standard unit (replacing current @standard_unit). For standard units, amount is implicitly 1. So :amount argument here has different meaning – it sets the measure of the quantity. Measure can also be specified more explicitly by :measure named argument.



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/sy/quantity.rb', line 288

def new_standard_unit( amount: nil, measure: nil, **nn )
  explain_amount_of_standard_units if amount.is_a? Numeric # n00b help
  # For standard units, amount has special meaning of setting up mapping.
  if measure then
    raise ArgumentError, "When :measure is specified, :amount must not be " +
      "expliticly specified." unless amount.nil?
    raise TypeError, ":measure argument must be a SY::Measure!" unless
      measure.is_a? SY::Measure
    set_measure( measure )
  else
    set_measure( SY::Measure.simple_scale( amount.nil? ? 1 : amount.amount ) )
  end
  # Replace @standard_unit with the newly constructed unit.
  Unit().instance_variable_set( :@standard,
                                unit( **nn ).tap do |u|
                                  ( units.unshift u ).uniq!
                                end )
end

#protect!Object

Protects quantity from decomposition.



130
131
132
133
134
# File 'lib/sy/quantity.rb', line 130

def protect!
  @protected = true
  @composition ||= SY::Composition.singular self
  return self
end

#protected?Boolean

Protected quantity is not allowed to be decomposed in the process of quantity simplification.

Returns:

  • (Boolean)


124
125
126
# File 'lib/sy/quantity.rb', line 124

def protected?
  @protected
end

#read(magnitude_of_other_quantity) ⇒ Object

Converts magnitude of another quantity to a magnitude of this quantity.



178
179
180
181
182
# File 'lib/sy/quantity.rb', line 178

def read magnitude_of_other_quantity
  other_quantity = magnitude_of_other_quantity.quantity
  other_amount = magnitude_of_other_quantity.amount
  magnitude measure( of: other_quantity ).r.( other_amount )
end

#relativeObject

Relative quantity related to this quantity.



228
229
230
# File 'lib/sy/quantity.rb', line 228

def relative
  relative? ? self : colleague
end

#relative?Boolean

Is the quantity relative?

Returns:

  • (Boolean)


216
217
218
# File 'lib/sy/quantity.rb', line 216

def relative?
  @relative ? true : false
end

#set_colleague(q2) ⇒ Object

Acts as colleague setter.

Raises:



241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/sy/quantity.rb', line 241

def set_colleague q2
  raise SY::DimensionError, "Mismatch: #{self}, #{q2}!" unless
    same_dimension? q2
  raise SY::QuantityError, "#{self} an #{q2} are both " +
    "{relative? ? 'relative' : 'absolute'}!" if relative? == q2.relative?
  if measure && q2.measure then
    raise SY::QuantityError, "Measure mismatch: #{self}, #{q2}!" unless
      measure == q2.measure
  end
  @colleague = q2
  q2.instance_variable_set :@colleague, self
end

#set_composition(comp) ⇒ Object

Acts as composition setter (dimension must match).



160
161
162
163
164
# File 'lib/sy/quantity.rb', line 160

def set_composition comp
  @composition = SY::Composition[ comp ]
    .aT "composition, when redefined after initialization,",
        "match the dimension" do |comp| comp.dimension == dimension end
end

#set_measure(measure) ⇒ Object

Acts as setter of measure (of the pertinent standard quantity).



168
169
170
171
172
173
174
# File 'lib/sy/quantity.rb', line 168

def set_measure measure
  @measure = if measure.is_a?( SY::Measure ) then
               measure
             else
               SY::Measure.simple_scale( measure )
             end
end

#simple?Boolean

Simple quantity is one with simple composition. If nontrivial composition is known for the colleague, it is assumed that the same composition would apply for this quantity, so it is not simple.

Returns:

  • (Boolean)


98
99
100
101
# File 'lib/sy/quantity.rb', line 98

def simple?
  c

#standardObject

Returns the standard quantity for this quantity’s dimension.



346
347
348
349
350
# File 'lib/sy/quantity.rb', line 346

def standard
  puts "Dimension of this quantity is #{dimension}" if SY::DEBUG
  puts "Its standard quantity is #{dimension.standard_quantity}" if SY::DEBUG
  dimension.standard_quantity
end

#standard!Object

Make the quantity standard for its dimension.



340
341
342
# File 'lib/sy/quantity.rb', line 340

def standard!
  SY::Dimension.standard_quantities[ dimension ] = self
end

#standard?Boolean

Is the dimension standard?

Returns:

  • (Boolean)


354
355
356
# File 'lib/sy/quantity.rb', line 354

def standard?
  self == standard
end

#standard_unitObject

Reader of standard unit.



262
263
264
# File 'lib/sy/quantity.rb', line 262

def standard_unit
  Unit().standard
end

#standardish?Boolean

Is the dimension or its colleague standard?

Returns:

  • (Boolean)


360
361
362
# File 'lib/sy/quantity.rb', line 360

def standardish?
  standard? || colleague.standard?
end

#to_sObject

A string briefly describing the quantity.



366
367
368
# File 'lib/sy/quantity.rb', line 366

def to_s
  name.nil? ? "[#{dimension}]" : name.to_s
end

#unit(**nn) ⇒ Object

Constructs a new unit of this quantity.



278
279
280
281
# File 'lib/sy/quantity.rb', line 278

def unit **nn
  Unit().new( nn.update( of: self ) )
    .tap { |u| ( units << u ).uniq! } # add it to the @units array
end

#unprotect!Object

Unprotects quantity from decomposition.



138
139
140
141
142
# File 'lib/sy/quantity.rb', line 138

def unprotect!
  @protected = false
  @composition = nil if @composition == SY::Composition.singular( self )
  return self
end

#write(amount_of_this_quantity, other_quantity) ⇒ Object

Converts an amount of this quantity to a magnitude of other quantity.



186
187
188
189
# File 'lib/sy/quantity.rb', line 186

def write amount_of_this_quantity, other_quantity
  measure( of: other_quantity )
    .write( magnitude( amount_of_this_quantity ), other_quantity )
end