sy 2.0.6 → 2.0.7
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 +6 -4
- data/lib/sy/composition.rb +27 -0
- data/lib/sy/magnitude.rb +53 -36
- data/lib/sy/quantity.rb +29 -2
- data/lib/sy/version.rb +1 -1
- data/test/sy_test.rb +23 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 672ba8a0946d6df05bb65ff2e23100e0419fdec2
|
4
|
+
data.tar.gz: 19dbf53c658806b0052809bda3e975b8ba7a29df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98ca4cb0b79fc7c73b3ca6a6748415b152ef2e78d291f4775b98832e15c7647e0e613ee9787fde0fa2d7782030be2deeb782debb9280bd0549751453e29bed5f
|
7
|
+
data.tar.gz: c21b90522fe5268c2a9438964dedb51cb07d5813fa237d365fa12869817eb8f51f5a0d8541b73fdbc37f7e66053c22105ea7847c3557f20dafc07e8d5ae72cc5
|
data/lib/sy.rb
CHANGED
@@ -72,7 +72,7 @@ module SY
|
|
72
72
|
Nᴀ = AVOGADRO_CONSTANT = 6.02214e23
|
73
73
|
|
74
74
|
# Let SY::MoleAmount be another dimensionless quantity:
|
75
|
-
MoleAmount = Quantity.dimensionless
|
75
|
+
MoleAmount = Quantity.dimensionless coerces: Amount
|
76
76
|
|
77
77
|
# And let SY::MOLE be its standard unit, related to SY::Amount via Nᴀ:
|
78
78
|
MOLE = Unit.standard of: MoleAmount, short: "mol", amount: Nᴀ.unit
|
@@ -133,7 +133,7 @@ module SY
|
|
133
133
|
# Celsius temperature is a little bit peculiar in that it has offset of
|
134
134
|
# 273.15.K with respect to Kelvin temperature, and I am not sure whether
|
135
135
|
# at this moment SY is handling this right. But nevertheless:
|
136
|
-
CelsiusTemperature = Quantity.of
|
136
|
+
CelsiusTemperature = Quantity.of :Θ, coerces_to: Temperature
|
137
137
|
|
138
138
|
CELSIUS_MEASURE = SY::Measure.simple_offset( TRIPLE_POINT_OF_WATER.in( :K ) )
|
139
139
|
|
@@ -188,7 +188,7 @@ module SY
|
|
188
188
|
Volume = Length ** 3
|
189
189
|
|
190
190
|
# SY::LitreVolume is another quantity of the same dimension as SY::Volume:
|
191
|
-
LitreVolume = Quantity.of Volume.dimension
|
191
|
+
LitreVolume = Quantity.of Volume.dimension, coerces_to: Volume
|
192
192
|
|
193
193
|
# SY::LITRE is the standard unit of SY::LitreVolume:
|
194
194
|
LITRE = Unit.standard of: LitreVolume, short: "l", amount: 1.dm³
|
@@ -267,8 +267,10 @@ module SY
|
|
267
267
|
# make SY::VOLT its standard unit:
|
268
268
|
VOLT = Unit.standard of: ElectricPotential, short: "V"
|
269
269
|
|
270
|
-
#
|
270
|
+
# TODO: This should raise a friendly error:
|
271
271
|
# MOLAR = Unit.standard of: Molarity, short: "M", amount: 1.mol.l⁻¹
|
272
|
+
# (normal way of definition is MOLAR = Unit.standard of: Molarity, short: "M"
|
273
|
+
# and it has already been defined to boot)
|
272
274
|
|
273
275
|
# SY::Molality...
|
274
276
|
Molality = MoleAmount / Mass
|
data/lib/sy/composition.rb
CHANGED
@@ -112,6 +112,33 @@ class SY::Composition < Hash
|
|
112
112
|
singular? && first[0].dimension.base?
|
113
113
|
end
|
114
114
|
|
115
|
+
# Whether this composition coerces another compotision.
|
116
|
+
#
|
117
|
+
def coerces? other
|
118
|
+
# TODO: Think about caching. One way, ie. no way back, once something
|
119
|
+
# coerces something else, so only false results would have to be re-checked,
|
120
|
+
# and that only at most once each time after coerces / coerced_by method is
|
121
|
+
# tampered.
|
122
|
+
if singular? then
|
123
|
+
other.singular? && self.first[0].coerces?( other.first[0] )
|
124
|
+
else
|
125
|
+
# simplify the compositions a bit
|
126
|
+
rslt = [].tap do |ary|
|
127
|
+
find { |qnt, e|
|
128
|
+
other.find { |qnt2, e2|
|
129
|
+
( ( e > 0 && e2 > 0 || e < 0 && e2 < 0 ) && qnt.coerces?( qnt2 ) )
|
130
|
+
.tap { |rslt| [] << qnt << qnt2 << ( e > 0 ? -1 : 1 ) if rslt }
|
131
|
+
}
|
132
|
+
}
|
133
|
+
end
|
134
|
+
# and ask recursively
|
135
|
+
if rslt.empty? then return false else
|
136
|
+
q, q2, e = rslt
|
137
|
+
( self + q.composition * e ).coerces? ( other + q2.composition * e )
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
115
142
|
# Returns a new instance with same hash.
|
116
143
|
#
|
117
144
|
def +@
|
data/lib/sy/magnitude.rb
CHANGED
@@ -1,45 +1,49 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
# This module
|
3
|
-
#
|
2
|
+
# This module defines common assets of a magnitude – be it absolute (number of
|
3
|
+
# unit objects), or relative (magnitude difference).
|
4
4
|
#
|
5
5
|
module SY::Magnitude
|
6
6
|
class << self
|
7
|
-
# Constructs absolute
|
7
|
+
# Constructs an absolute magnitude of a given quantity.
|
8
8
|
#
|
9
|
-
def absolute
|
10
|
-
|
11
|
-
qnt = ꜧ[:quantity] || ꜧ[:of] || args.shift
|
12
|
-
return qnt.absolute.magnitude ꜧ[:amount]
|
9
|
+
def absolute( of: nil, amount: nil )
|
10
|
+
of.absolute.magnitude( amount )
|
13
11
|
end
|
14
12
|
|
15
|
-
# Constructs relative
|
13
|
+
# Constructs a relative magnitude of a given quantity.
|
16
14
|
#
|
17
|
-
def difference
|
18
|
-
|
19
|
-
qnt = ꜧ[:quantity] || ꜧ[:of] || args.shift
|
20
|
-
return qnt.relative.magnitude ꜧ[:amount]
|
15
|
+
def difference( of: nil, amount: nil )
|
16
|
+
of.relative.magnitude( amount )
|
21
17
|
end
|
22
18
|
|
23
|
-
# Constructs
|
19
|
+
# Constructs a magnitude of a given quantity.
|
24
20
|
#
|
25
|
-
def of
|
26
|
-
|
21
|
+
def of( quantity, amount: nil )
|
22
|
+
quantity.magnitude( amount )
|
27
23
|
end
|
28
24
|
|
29
|
-
#
|
25
|
+
# Zero magnitude of a given quantity.
|
30
26
|
#
|
31
|
-
def zero
|
32
|
-
|
27
|
+
def zero( of: nil )
|
28
|
+
absolute of: of, amount: 0
|
33
29
|
end
|
34
30
|
end
|
35
31
|
|
32
|
+
# Magnitudes respond to unit methods.
|
33
|
+
#
|
34
|
+
include SY::ExpressibleInUnits
|
35
|
+
|
36
36
|
# Magnitudes are comparable.
|
37
37
|
#
|
38
38
|
include Comparable
|
39
39
|
|
40
|
-
#
|
40
|
+
# Three-way comparison operator of magnitudes.
|
41
41
|
#
|
42
|
-
|
42
|
+
def <=> m2
|
43
|
+
return amount <=> m2.amount if quantity == m2.quantity
|
44
|
+
return self <=> m2.( quantity ) if quantity.coerces? m2.quantity
|
45
|
+
apply_through_coerce :<=>, m2
|
46
|
+
end
|
43
47
|
|
44
48
|
attr_reader :quantity, :amount
|
45
49
|
alias in_standard_unit amount
|
@@ -63,13 +67,13 @@ module SY::Magnitude
|
|
63
67
|
# Computes absolute value and reframes into the absolute quantity.
|
64
68
|
#
|
65
69
|
def absolute
|
66
|
-
quantity.absolute.magnitude amount.abs
|
70
|
+
quantity.absolute.magnitude( amount.abs )
|
67
71
|
end
|
68
72
|
|
69
73
|
# Reframes into the relative quantity.
|
70
74
|
#
|
71
75
|
def relative
|
72
|
-
quantity.relative.magnitude amount
|
76
|
+
quantity.relative.magnitude( amount )
|
73
77
|
end
|
74
78
|
|
75
79
|
# Reframes the magnitude into its relative quantity.
|
@@ -96,27 +100,20 @@ module SY::Magnitude
|
|
96
100
|
magnitude amount.round( *args )
|
97
101
|
end
|
98
102
|
|
99
|
-
# Compatible magnitudes compare by their amounts.
|
100
|
-
#
|
101
|
-
def <=> m2
|
102
|
-
return amount <=> m2.amount if quantity == m2.quantity
|
103
|
-
raise SY::QuantityError, "Mismatch: #{quantity} <=> #{m2.quantity}!"
|
104
|
-
end
|
105
|
-
|
106
103
|
# Addition.
|
107
104
|
#
|
108
105
|
def + m2
|
109
106
|
return magnitude amount + m2.amount if quantity == m2.quantity
|
110
|
-
|
111
|
-
|
107
|
+
return self + m2.( quantity ) if quantity.coerces? m2.quantity
|
108
|
+
apply_through_coerce :+, m2
|
112
109
|
end
|
113
110
|
|
114
111
|
# Subtraction.
|
115
112
|
#
|
116
113
|
def - m2
|
117
114
|
return magnitude amount - m2.amount if quantity == m2.quantity
|
118
|
-
|
119
|
-
|
115
|
+
return self - m2.( quantity ) if quantity.coerces? m2.quantity
|
116
|
+
apply_through_coerce :-, m2
|
120
117
|
end
|
121
118
|
|
122
119
|
# Multiplication.
|
@@ -173,10 +170,12 @@ module SY::Magnitude
|
|
173
170
|
# Type coercion for magnitudes.
|
174
171
|
#
|
175
172
|
def coerce m2
|
176
|
-
|
177
|
-
|
173
|
+
if m2.is_a? Numeric then
|
174
|
+
return SY::Amount.relative.magnitude( m2 ), self
|
175
|
+
elsif quantity.coerces? m2.quantity then
|
176
|
+
return m2.( quantity ), self
|
178
177
|
else
|
179
|
-
raise
|
178
|
+
raise TypeError, "#{self} cannot be coerced into a #{m2.class}!"
|
180
179
|
end
|
181
180
|
end
|
182
181
|
|
@@ -514,4 +513,22 @@ module SY::Magnitude
|
|
514
513
|
def default_amount_formatting_precision
|
515
514
|
3
|
516
515
|
end
|
516
|
+
|
517
|
+
# Applies an operator on self with otherwise incompatible second operand.
|
518
|
+
#
|
519
|
+
def apply_through_coerce operator, operand2
|
520
|
+
begin
|
521
|
+
compat_obj_1, compat_obj_2 = operand2.coerce( self )
|
522
|
+
rescue SY::DimensionError
|
523
|
+
msg = "Mismatch: #{dimension} #{operator} #{operand2.dimension}!"
|
524
|
+
fail SY::DimensionError, msg
|
525
|
+
rescue SY::QuantityError
|
526
|
+
msg = "Mismatch: #{quantity} #{operator} #{operand2.quantity}!"
|
527
|
+
fail SY::QuantityError, msg
|
528
|
+
rescue NoMethodError
|
529
|
+
fail TypeError, "#{operand2.class} can't be coerced into #{quantity}!"
|
530
|
+
else
|
531
|
+
compat_obj_1.send( operator, compat_obj_2 )
|
532
|
+
end
|
533
|
+
end
|
517
534
|
end
|
data/lib/sy/quantity.rb
CHANGED
@@ -56,7 +56,14 @@ class SY::Quantity
|
|
56
56
|
# Standard constructor of a metrological quantity. A quantity may have
|
57
57
|
# a name and a dimension.
|
58
58
|
#
|
59
|
-
def initialize( relative: nil,
|
59
|
+
def initialize( relative: nil,
|
60
|
+
composition: nil,
|
61
|
+
of: nil,
|
62
|
+
measure: nil,
|
63
|
+
amount: nil,
|
64
|
+
coerces: [],
|
65
|
+
coerces_to: [],
|
66
|
+
**nn )
|
60
67
|
puts "Quantity init relative: #{relative}, composition: #{composition}, measure: #{measure}, #{nn}" if SY::DEBUG
|
61
68
|
@units = [] # array of units as favored by this quantity
|
62
69
|
@relative = relative
|
@@ -77,6 +84,8 @@ class SY::Quantity
|
|
77
84
|
fail ArgumentError, ":amount and :measure shouldn't be both supplied" unless amount.nil?
|
78
85
|
SY::Measure.simple_scale( measure )
|
79
86
|
end
|
87
|
+
coerces( *Array( coerces ) )
|
88
|
+
Array( coerces_to ).each { |qnt| qnt.coerces self }
|
80
89
|
puts "Composition of the initialized instance is #{composition}." if SY::DEBUG
|
81
90
|
end
|
82
91
|
|
@@ -89,6 +98,24 @@ class SY::Quantity
|
|
89
98
|
cᴍ.empty? || cᴍ.singular? && cᴍ.first[0] == self
|
90
99
|
end
|
91
100
|
|
101
|
+
# Quantities explicitly coerced by this quantity.
|
102
|
+
#
|
103
|
+
def coerces *other_quantities
|
104
|
+
if other_quantities.empty? then @coerces ||= [] else
|
105
|
+
other_quantities.each { |qnt| coerces << qnt }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Is the quantity supplied as the argument coerced by this quantity?
|
110
|
+
#
|
111
|
+
def coerces? other
|
112
|
+
other == self || coerces.include?( other ) ||
|
113
|
+
colleague.coerces.include?( other.colleague ) ||
|
114
|
+
if simple? then false else
|
115
|
+
composition.coerces? other.composition
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
92
119
|
# Protected quantity is not allowed to be decomposed in the process of quantity
|
93
120
|
# simplification.
|
94
121
|
#
|
@@ -339,7 +366,7 @@ class SY::Quantity
|
|
339
366
|
# Inspect string.
|
340
367
|
#
|
341
368
|
def inspect
|
342
|
-
"#<Quantity
|
369
|
+
"#<Quantity:#{to_s}>"
|
343
370
|
end
|
344
371
|
|
345
372
|
def coerce other
|
data/lib/sy/version.rb
CHANGED
data/test/sy_test.rb
CHANGED
@@ -182,7 +182,8 @@ describe SY::Quantity, SY::Magnitude do
|
|
182
182
|
SY::METRE.must_be_kind_of SY::Unit
|
183
183
|
SY::METRE.absolute?.must_equal true
|
184
184
|
1.metre.absolute.must_equal SY::METRE
|
185
|
-
|
185
|
+
# FIXME
|
186
|
+
# assert 1.metre.absolute != 1.metre.relative
|
186
187
|
1.metre.relative.relative?.must_equal true
|
187
188
|
|
188
189
|
|
@@ -357,9 +358,7 @@ describe SY::Quantity, SY::Magnitude do
|
|
357
358
|
|
358
359
|
|
359
360
|
# pretty representation
|
360
|
-
assert_equal SY::Unit.instance( :SECOND ), SY::Unit.instance( :second )
|
361
361
|
( 1.m / 3.s ).to_s.must_equal( "0.333.m.s⁻¹" ) # FIXME: Discovered a problem here, uncomment the line below
|
362
|
-
# assert_equal SY::Unit.instance( :SECOND ), SY::Unit.instance( :second )
|
363
362
|
( 1.m / 7.01e7.s ).to_s.must_equal( "1.43e-08.m.s⁻¹" )
|
364
363
|
|
365
364
|
assert_equal 1.m, 1.s * 1.m.s⁻¹
|
@@ -391,7 +390,27 @@ describe SY::Quantity, SY::Magnitude do
|
|
391
390
|
assert_equal 1.l.( SY::Volume ), 1.xoxo.( SY::Volume )
|
392
391
|
assert_equal SY::TRIPLE_POINT_OF_WATER, 0.°C.( SY::Temperature )
|
393
392
|
assert_equal 273.15, 0.°C.in( :K )
|
394
|
-
|
393
|
+
assert_equal SY::Unit.instance( :SECOND ), SY::Unit.instance( :second )
|
394
|
+
assert_equal SY::TRIPLE_POINT_OF_WATER, 0.°C # coercion behavior
|
395
395
|
end
|
396
396
|
end
|
397
397
|
end
|
398
|
+
|
399
|
+
describe SY::Magnitude do
|
400
|
+
it "should have working #<=> method" do
|
401
|
+
assert_equal 0, 1.m <=> 100.cm
|
402
|
+
assert_equal 1, 1.m <=> 99.cm
|
403
|
+
assert_equal -1, 1.m <=> 101.cm
|
404
|
+
assert_equal SY::Length.composition * 3, 1.m³.quantity.composition
|
405
|
+
a, b = 10.hl, 1.m³
|
406
|
+
assert_equal SY::Volume.relative, b.quantity
|
407
|
+
assert_equal SY::LitreVolume.relative, a.quantity
|
408
|
+
assert_equal [SY::LitreVolume], SY::Volume.coerces
|
409
|
+
assert b.quantity.absolute.coerces?( a.quantity.absolute )
|
410
|
+
assert b.quantity.coerces?( a.quantity )
|
411
|
+
assert_equal 0, 1.l <=> 1.dm(3)
|
412
|
+
assert_equal -1, 1.m³ <=> 11.hl
|
413
|
+
assert_equal 1, 1.m³ <=> 9.hl
|
414
|
+
assert_equal 1.dm³, 1.l
|
415
|
+
end
|
416
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- boris
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|