sy 2.0.2 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c1ff970013f49cfd8677a9dad33067247e8a2df5
4
- data.tar.gz: 6f3c4c65c65f0a90994a699703474ba9d73a9c73
3
+ metadata.gz: fd4b854cf2fceae1fb55d2e657f219807f88f6c6
4
+ data.tar.gz: 730fec5dad579f42e482d49cbaad66b56e901685
5
5
  SHA512:
6
- metadata.gz: 513505ef33494723ffb05ab0aa0d8aa9bddd9a4663d15aceecb74bd96a3a5530f81c150e30ff0f3874ffed8dc8f5e4a4536d75db8bc629d85becd042332a1298
7
- data.tar.gz: 1e5cab9de6e72cdde63d6b4e43a71d1dfa4fb6a1ba49380695ac10fdc6d6de83d8b00255386e9cee2250c9587382dfaac69a4cddab5e37d29d714e7acdbb8d77
6
+ metadata.gz: 2b2c9f2994e1d30db45eeeed40f952aad46e619a3935db909b2446f2b08f169525216cca37f49747abaf3dbc30213f38da505dd3e0ef0d4e84e678bf00d67807
7
+ data.tar.gz: ca82955c039d47f775292f3b1ee9c92ef6cba91ea0b0b99af4ae4f2666f90b0f9366b267b8e98299d195d94902d82dcb50f68fb632a6e32a3fb9fe4d0f314030
data/lib/sy.rb CHANGED
@@ -14,7 +14,7 @@ require 'active_support/core_ext/string/starts_ends_with'
14
14
  require_relative 'sy/version'
15
15
  require_relative 'sy/expressible_in_units'
16
16
  require_relative 'sy/fixed_assets_of_the_module'
17
- require_relative 'sy/mapping'
17
+ require_relative 'sy/measure'
18
18
  require_relative 'sy/dimension'
19
19
  require_relative 'sy/quantity'
20
20
  require_relative 'sy/composition'
@@ -141,8 +141,12 @@ module SY
141
141
  CelsiusTemperature = Quantity.of :Θ
142
142
 
143
143
  # Degree celsius is SY::CELSIUS
144
- CELSIUS = Unit.standard of: CelsiusTemperature, short: '°C'
145
- # FIXME: Patch CelsiusTemperature to make it work with SY::Temperature
144
+ CELSIUS = Unit.standard of: CelsiusTemperature, short: '°C', measure: SY::Measure.simple_offset( TRIPLE_POINT_OF_WATER.in( :K ) )
145
+
146
+ class << CelsiusTemperature
147
+ # FIXME: Patch CelsiusTemperature to make it work with SY::Temperature
148
+ end
149
+
146
150
  # alias :°C :celsius # with U+00B0 DEGREE SIGN
147
151
  # alias :˚C :celsius # with U+02DA RING ABOVE
148
152
  # alias :℃ :celsius # U+2103 DEGREE CELSIUS
@@ -258,7 +262,7 @@ module SY
258
262
  VOLT = Unit.standard of: ElectricPotential, short: "V"
259
263
 
260
264
  # FIXME: This should raise a friendly error:
261
- # MOLAR = Unit.standard of: Molarity, abbreviation: "M", amount: 1.mol.l⁻¹
265
+ # MOLAR = Unit.standard of: Molarity, short: "M", amount: 1.mol.l⁻¹
262
266
 
263
267
  # SY::Molality...
264
268
  Molality = MoleAmount / Mass
@@ -1,26 +1,25 @@
1
- #encoding: utf-8
2
-
1
+ # -*- coding: utf-8 -*-
3
2
  # Qualities specific to absolute magnitudes.
4
3
  #
5
4
  module SY::AbsoluteMagnitude
6
5
  # Absolute magnitude constructor takes :quantity (alias :of) named argument,
7
6
  # and :amount named argument, where amount must be nonnegative.
8
7
  #
9
- def initialize args={}
10
- @quantity = args[:quantity] || args[:of]
11
- amnt = args[:amount]
12
- @amount = case amnt
13
- when Numeric then amnt
8
+ def initialize( of: ( fail ArgumentError, ":of argument missing!" ),
9
+ amount: nil )
10
+ @quantity = of
11
+ @amount = case amount
12
+ when Numeric then amount
14
13
  when nil then 1
15
14
  else
16
15
  begin
17
- amnt.amount
16
+ amount.amount
18
17
  rescue NameError, NoMethodError
19
- amnt
18
+ amount
20
19
  end
21
20
  end
22
- raise SY::MagnitudeError,
23
- "Unsigned magnitudes canot have negative amount!" if @amount < 0
21
+ fail SY::MagnitudeError, "Unsigned magnitudes may not have negative " +
22
+ "amount!" if @amount < 0
24
23
  end
25
24
 
26
25
  # For absolute magnitudes, #+ method always returns a result framed in
@@ -1,5 +1,4 @@
1
- #encoding: utf-8
2
-
1
+ # -*- coding: utf-8 -*-
3
2
  # Composition of quantities.
4
3
  #
5
4
  class SY::Composition < Hash
@@ -11,6 +10,7 @@ class SY::Composition < Hash
11
10
  SR << -> ꜧ {
12
11
  ꜧ.reject! { |qnt, _| qnt == SY::Amount || qnt == SY::Amount.relative }
13
12
  }
13
+
14
14
  # Relative quantities of the composition are absolutized:
15
15
  SR << -> ꜧ {
16
16
  ꜧ.select { |qnt, _| qnt.relative? }.each { |qnt, exp|
@@ -18,10 +18,14 @@ class SY::Composition < Hash
18
18
  ꜧ.update qnt.absolute => exp
19
19
  }
20
20
  }
21
+
21
22
  # Any quantities with exponent zero can be deleted:
22
23
  SR << -> ꜧ {
23
24
  ꜧ.reject! { |_, exp| exp == 0 }
24
25
  }
26
+
27
+ # TODO: This undocumented simplification rule simplifies MoleAmount and
28
+ # LitreVolume into Molarity.
25
29
  #
26
30
  SR << -> ꜧ {
27
31
  begin
@@ -103,6 +107,8 @@ class SY::Composition < Hash
103
107
  # is a base dimension.
104
108
  #
105
109
  def atomic?
110
+ puts "composition is #{self}" if SY::DEBUG
111
+ puts "first[0].dimension is #{first[0].dimension}" if SY::DEBUG
106
112
  singular? && first[0].dimension.base?
107
113
  end
108
114
 
@@ -170,21 +176,28 @@ class SY::Composition < Hash
170
176
  SY::Quantity.new args.merge( composition: self )
171
177
  end
172
178
 
173
- # Dimension of a quantity composition is the sum of its dimensions.
179
+ # Dimension of a composition is the sum of its member quantities' dimensions.
174
180
  #
175
181
  def dimension
176
182
  map { |qnt, exp| qnt.dimension * exp }.reduce SY::Dimension.zero, :+
177
183
  end
178
184
 
179
- # Infers the mapping of the composition's quantity. (Mapping
180
- # defines mapping of a quantity to the standard quantity of its dimension.)
185
+ # Infers the measure of the composition's quantity. ('Measure' means measure
186
+ # of the pertinent standard quantity.)
181
187
  #
182
- def infer_mapping
183
- puts "#infer_mapping; hash is #{self}" if SY::DEBUG
188
+ def infer_measure
189
+ puts "#infer_measure; hash is #{self}" if SY::DEBUG
184
190
  map do |qnt, exp|
185
- qnt.standardish? ? SY::Mapping.identity :
186
- qnt.mapping_to( qnt.standard ) ** exp
187
- end.reduce( SY::Mapping.identity, :* )
191
+ puts "#infer_measure: doing quantity #{qnt} with exponent #{exp}!" if SY::DEBUG
192
+ if qnt.standardish? then
193
+ puts "#{qnt} standardish" if SY::DEBUG
194
+ SY::Measure.identity
195
+ else
196
+ puts "#{qnt} not standardish" if SY::DEBUG
197
+ puts "its measure is #{qnt.measure}, class #{qnt.measure.class}" if SY::DEBUG
198
+ qnt.measure( of: qnt.standard ) ** exp
199
+ end
200
+ end.reduce( SY::Measure.identity, :* )
188
201
  end
189
202
 
190
203
  # Simple composition is one that is either empty or singular.
data/lib/sy/dimension.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
-
3
2
  # This class represents physical dimension of a metrological quantity.
4
3
  #
5
4
  class SY::Dimension
@@ -1,5 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
-
3
2
  # This mixin provides ability to respond to SY unit symbol methods.
4
3
  #
5
4
  module SY::ExpressibleInUnits
@@ -1,5 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
-
3
2
  # Here, fixed assets of the main module are set up.
4
3
  #
5
4
  module SY
data/lib/sy/imperial.rb CHANGED
@@ -18,6 +18,8 @@ module SY
18
18
 
19
19
  # === Volume
20
20
  PINT = Unit.of Volume, amount: 568.26125.cm³
21
+ # FIXME: PINT = Unit.of Volume, amount: 568.26125.ml didn't work, it gave 1000 times more value
22
+ # something is wrong with the conversion mechanics
21
23
  QUART = Unit.of Volume, amount: 2.pint
22
24
  GALLON = Unit.of Volume, short: 'gal', amount: 8.pint
23
25
 
data/lib/sy/magnitude.rb CHANGED
@@ -1,22 +1,21 @@
1
- #encoding: utf-8
2
-
3
- # In physics, difference between absolute and relative magnitudes is well
4
- # understood. The magnitude class here represents absolute magnitude – physical
5
- # number of unit objects making up the amount of some metrological quantity.
6
- # Amounts of absolute magnitudes may not be negative. When one desires to
7
- # represent <em>difference</me> between magnitudes, which can be positive as
8
- # well as negative, relative magnitude has to be used.
1
+ # -*- coding: utf-8 -*-
2
+ # This class here represents absolute magnitude – physical number of unit
3
+ # objects, that make up the amount of some metrological quantity. Amount of
4
+ # an absolute magnitudes may not be negative – physical amounts cannot have
5
+ # negative number of unit objects. But as for the <em>difference</me> between
6
+ # magnitudes, this can be positive as well as negative relative magnitudes
7
+ # are used for this purpose.
9
8
  #
10
9
  # While ordinary #+ and #- methods of absolute magnitudes return relative
11
- # relative magnitudes, absolute magnitudes have additional methods #add and
12
- # #subtract, which return absolute magnitudes (it is the responsibility of the
13
- # caller to avoid negative results). Furthermore, absolute magnitudes have
14
- # special subtraction method #take, which guards against subtracting more than
15
- # the magnitude's amount.
10
+ # magnitudes, absolute magnitudes have additional methods #add and #subtract,
11
+ # that return absolute magnitudes (it is the responsibility of the caller to
12
+ # avoid negative results). Furthermore, absolute magnitudes have one more
13
+ # special method #take, which perfoms #subtract whilst protecting against
14
+ # subtraction of more than, there is to take.
16
15
  #
17
16
  module SY::Magnitude
18
17
  class << self
19
- # Constructor of absolute magnitudes of a given quantity.
18
+ # Constructs absolute magnitudes of a given quantity.
20
19
  #
21
20
  def absolute *args
22
21
  ꜧ = args.extract_options!
@@ -24,7 +23,7 @@ module SY::Magnitude
24
23
  return qnt.absolute.magnitude ꜧ[:amount]
25
24
  end
26
25
 
27
- # Constructor of relative magnitudes of a given quantity.
26
+ # Constructs relative magnitudes of a given quantity.
28
27
  #
29
28
  def difference *args
30
29
  ꜧ = args.extract_options!
@@ -32,13 +31,13 @@ module SY::Magnitude
32
31
  return qnt.relative.magnitude ꜧ[:amount]
33
32
  end
34
33
 
35
- # Constructor of magnitudes of a given quantity.
34
+ # Constructs magnitudes of a given quantity.
36
35
  #
37
36
  def of qnt, args={}
38
37
  return qnt.magnitude args[:amount]
39
38
  end
40
39
 
41
- # Constructor of zero magnitude of a given quantity.
40
+ # Returns zero magnitude of a given quantity.
42
41
  #
43
42
  def zero
44
43
  return absolute 0
@@ -204,8 +203,8 @@ module SY::Magnitude
204
203
  #
205
204
  def reframe q2
206
205
  case q2
207
- when SY::Quantity then q2.import self
208
- when SY::Unit then q2.quantity.import self
206
+ when SY::Quantity then q2.read self
207
+ when SY::Unit then q2.quantity.read self
209
208
  else raise TypeError, "Unable to reframe into a #{q2.class}!" end
210
209
  end
211
210
 
@@ -215,8 +214,8 @@ module SY::Magnitude
215
214
  #
216
215
  def call q2
217
216
  case q2
218
- when SY::Quantity then q2.relative.import self
219
- when SY::Unit then q2.quantity.relative.import self
217
+ when SY::Quantity then q2.relative.read self
218
+ when SY::Unit then q2.quantity.relative.read self
220
219
  else raise TypeError, "Unable to reframe into a #{q2.class}!" end
221
220
  end
222
221
 
@@ -392,6 +391,7 @@ module SY::Magnitude
392
391
  end
393
392
 
394
393
  rescue
394
+ fail
395
395
  number_ς = number_format % amount
396
396
  [ number_ς, "unit[#{quantity}]" ].join '.'
397
397
  end
data/lib/sy/mapping.rb CHANGED
@@ -1,79 +1,132 @@
1
- #encoding: utf-8
2
-
3
- # Represents relationship of two quantities. Provides import and export
4
- # conversion closures. Instances are immutable and have 2 attributes:
1
+ # -*- coding: utf-8 -*-
2
+ # Represents a certain way, that a quantity <em>measures</em> another quantity
3
+ # (reference quantity). Instance has two attributes:
5
4
  #
6
- # * im - import closure, converting amount of quantity 1 into quantity 2
7
- # * ex - export closure, converting amount of quantity 2 into quantity 1
5
+ # * @r - read closure, for converting from the measured reference quantity.
6
+ # * @w - write closure, for converting back to the reference quantity.
8
7
  #
9
- # Convenience methods for mapping magnitudes are:
10
- #
11
- # * import - like im, but operates on magnitudes
12
- # * export - like ex, but operates on magnitudes
8
+ # Convenience methods #read and #write facilitate their use.
13
9
  #
14
- class SY::Mapping
10
+ class SY::Measure
15
11
  class << self
12
+ # Identity measure.
13
+ #
16
14
  def identity
17
- new 1
15
+ simple_scale 1
16
+ end
17
+
18
+ # Simple scaling measure. (Eg. pounds vs kilograms)
19
+ #
20
+ def simple_scale scale
21
+ new( ratio: scale )
22
+ end
23
+
24
+ # Simple offset measure. (Such as °C)
25
+ #
26
+ def simple_offset offset
27
+ new( r: lambda { |ref_amnt| ref_amnt - offset },
28
+ w: lambda { |amnt| amnt + offset } )
29
+ end
30
+
31
+ # Linear (scaled offset) measure. Expects a hash with 2 points demonstrating
32
+ # the relationship: { ref_amount_1 => amount_1, ref_amount_2 => amount_2 }.
33
+ # (Example: Fahrenheit degrees vs. Kelvins.)
34
+ #
35
+ def linear hsh
36
+ ref_amnt_1, amnt_1, ref_amnt_2, amnt_2 = hsh.to_a.flatten
37
+ scale = ( ref_amnt_2 - ref_amnt_1 ) / ( amnt_2 - amnt_1 )
38
+ new( r: lambda { |ref_amnt| amnt_1 + ( ref_amnt - ref_amnt_1 ) / scale },
39
+ w: lambda { |amnt| ref_amnt_1 + ( amnt - amnt_1 ) * scale } )
40
+ end
41
+
42
+ # Logarithmic.
43
+ #
44
+ def logarithmic base=Math::E
45
+ new( r: lambda { |ref_amnt| Math.log ref_amnt, base },
46
+ w: lambda { |amnt| base ** amnt } )
47
+ end
48
+
49
+ # Negative logarithmic.
50
+ #
51
+ def negative_logarithmic base=Math::E
52
+ new( r: lambda { |ref_amnt| -Math.log( ref_amnt, base ) },
53
+ w: lambda { |amnt| base ** ( -amnt ) } )
18
54
  end
19
55
  end
20
56
 
21
- attr_reader :ex, :im, :ratio
57
+ attr_reader :r, :w, :ratio
22
58
 
23
- # Takes either a magnitude (1 argument), or 2 named arguments :im, :ex
24
- # speficying amount import and export closure. For a magnitude, these
25
- # closures are constructed automatically, assuming simple ratio rule.
59
+ # The constructor expects :r and :w arguments for read and write closure.
26
60
  #
27
- def initialize arg
28
- case arg
29
- when Hash then
30
- @ex, @im = arg[:ex], arg[:im]
61
+ def initialize ratio: nil,
62
+ r: ( fail ArgumentError, ":r closure not specified!" unless ratio ),
63
+ w: ( fail ArgumentError, ":w closure not specified!" unless ratio )
64
+ if ratio.nil?
65
+ fail TypeError, ":r and :w arguments must both be closures!" unless
66
+ r.is_a?( Proc ) && w.is_a?( Proc )
67
+ @ratio, @r, @w = nil, r, w
31
68
  else
32
- @ratio = r = arg
33
- @ex = lambda { |amount1| amount1 * r }
34
- @im = lambda { |amount2| amount2 / r }
69
+ fail ArgumentError, ":r or :w must not be given if :ratio given!" if r || w
70
+ @ratio = ratio
71
+ @r = lambda { |ref_amnt| ref_amnt / ratio }
72
+ @w = lambda { |amnt| amnt * ratio }
35
73
  end
36
74
  end
37
75
 
38
- def import magnitude, from_quantity
39
- from_quantity.magnitude @im.( magnitude.amount )
76
+ # Convenience method to read a magnitude of a reference quantity.
77
+ #
78
+ def read magnitude_of_reference_quantity, quantity
79
+ quantity.magnitude r.( magnitude_of_reference_quantity.amount )
40
80
  end
41
81
 
42
- def export magnitude, to_quantity
43
- to_quantity.magnitude @ex.( magnitude.amount )
82
+ # Convenience method to convert a magnitude back to the reference quantity.
83
+ #
84
+ def write magnitude, reference_quantity
85
+ reference_quantity.magnitude w.( magnitude.amount )
44
86
  end
45
87
 
88
+ # Inverse measure.
89
+ #
46
90
  def inverse
47
- self.class.new begin
48
- 1 / @ratio
49
- rescue NoMethodError, TypeError
50
- i, e = im, ex
51
- { im: e, ex: i } # swap closures
52
- end
91
+ if ratio.nil? then
92
+ self.class.new( r: w, w: r ) # swap closures
93
+ else
94
+ self.class.new( ratio: 1 / ratio )
95
+ end
53
96
  end
54
97
 
55
- def * r2 # mapping composition
56
- ç.new begin
57
- @ratio * r2.ratio
58
- rescue NoMethodError, TypeError
59
- i1, i2, e1, e2 = im, r2.im, ex, r2.ex
60
- { ex: lambda { |a1| e2.( e1.( a1 ) ) }, # export compose
61
- im: lambda { |a2| i1.( i2.( a2 ) ) } } # import compose
62
- end
98
+ # Measure composition (like f * g function composition).
99
+ #
100
+ def * other
101
+ if ratio.nil? then
102
+ r1, r2, w1, w2 = r, other.r, w, other.w
103
+ self.class.new( r: lambda { |ref_amnt| r1.( r2.( ref_amnt ) ) },
104
+ w: lambda { |amnt| w2.( w1.( amnt ) ) } )
105
+ else
106
+ self.class.new( ratio: ratio * other.ratio )
107
+ end
63
108
  end
64
109
 
65
- def / r2
66
- self * r2.inverse
110
+ # Measure composition with inverse of another measure.
111
+ #
112
+ def / other
113
+ self * other.inverse
67
114
  end
68
115
 
116
+ # Measure power.
117
+ #
69
118
  def ** n
70
- ç.new begin
71
- n == 1 ? @ratio * 1 : @ratio ** n
72
- rescue NoMethodError, TypeError
73
- i, e = im, ex
74
- { ex: lambda { |a1| n.times.reduce a1 do |m, _| e.( m ) end },
75
- im: lambda { |a2| n.times.reduce a2 do |m, _| i.( m ) end } }
76
- end
119
+ if ratio.nil? then
120
+ r_closure, w_closure = r, w
121
+ self.class.new( r: lambda { |ref_amnt|
122
+ n.times.inject ref_amnt do |m, _| r_closure.( m ) end
123
+ },
124
+ w: lambda { |amnt|
125
+ n.times.inject amnt do |m, _| w_closure.( m ) end
126
+ } )
127
+ else
128
+ self.class.new( ratio: ratio ** n )
129
+ end
77
130
  end
78
131
 
79
132
  protected