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 +4 -4
- data/lib/sy.rb +8 -4
- data/lib/sy/absolute_magnitude.rb +10 -11
- data/lib/sy/composition.rb +23 -10
- data/lib/sy/dimension.rb +0 -1
- data/lib/sy/expressible_in_units.rb +0 -1
- data/lib/sy/fixed_assets_of_the_module.rb +0 -1
- data/lib/sy/imperial.rb +2 -0
- data/lib/sy/magnitude.rb +21 -21
- data/lib/sy/mapping.rb +103 -50
- data/lib/sy/measure.rb +135 -0
- data/lib/sy/quantity.rb +112 -107
- data/lib/sy/signed_magnitude.rb +5 -5
- data/lib/sy/unit.rb +12 -18
- data/lib/sy/version.rb +1 -1
- data/test/sy_test.rb +232 -232
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd4b854cf2fceae1fb55d2e657f219807f88f6c6
|
4
|
+
data.tar.gz: 730fec5dad579f42e482d49cbaad66b56e901685
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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
|
-
|
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,
|
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
|
-
#
|
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
|
10
|
-
|
11
|
-
|
12
|
-
@amount = case
|
13
|
-
when Numeric then
|
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
|
-
|
16
|
+
amount.amount
|
18
17
|
rescue NameError, NoMethodError
|
19
|
-
|
18
|
+
amount
|
20
19
|
end
|
21
20
|
end
|
22
|
-
|
23
|
-
|
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
|
data/lib/sy/composition.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
#
|
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
|
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
|
180
|
-
#
|
185
|
+
# Infers the measure of the composition's quantity. ('Measure' means measure
|
186
|
+
# of the pertinent standard quantity.)
|
181
187
|
#
|
182
|
-
def
|
183
|
-
puts "#
|
188
|
+
def infer_measure
|
189
|
+
puts "#infer_measure; hash is #{self}" if SY::DEBUG
|
184
190
|
map do |qnt, exp|
|
185
|
-
qnt
|
186
|
-
|
187
|
-
|
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
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
|
-
#
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# number of unit objects
|
6
|
-
#
|
7
|
-
#
|
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
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# special
|
15
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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.
|
208
|
-
when SY::Unit then q2.quantity.
|
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.
|
219
|
-
when SY::Unit then q2.quantity.relative.
|
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
|
-
#
|
2
|
-
|
3
|
-
#
|
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
|
-
# *
|
7
|
-
# *
|
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
|
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::
|
10
|
+
class SY::Measure
|
15
11
|
class << self
|
12
|
+
# Identity measure.
|
13
|
+
#
|
16
14
|
def identity
|
17
|
-
|
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 :
|
57
|
+
attr_reader :r, :w, :ratio
|
22
58
|
|
23
|
-
#
|
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
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
33
|
-
@
|
34
|
-
@
|
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
|
-
|
39
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
66
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|