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 +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
|