sy 2.0.2 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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