sy 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e78755a352e971c6b349b4caf3e35f67e95876dd
4
+ data.tar.gz: 43903464572d42927c2a6ac4359c1341c3f71f79
5
+ SHA512:
6
+ metadata.gz: 6f817d558c1f3be298243a703ade9c6945d22f3a6febada5fb86e7c375986ea1b5b8d78f7f8d5d463086feea69a5010cb33b199a5e27c513d0a94d1582e9aed9
7
+ data.tar.gz: 2e953fc6121f7d30419d48493e80b45cb18ea6295eeb8fb5c708bc1f0851f7c9410e334adb742b1c09f32029aca246ad35657342fcf63912312d70f80b362dd3
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *~
2
+ .#*
3
+ \#*#
4
+ *.gem
5
+ *.rbc
6
+ .bundle
7
+ .config
8
+ .yardoc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sy.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 boris
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Sy
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'sy'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install sy
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/lib/sy.rb ADDED
@@ -0,0 +1,274 @@
1
+ #encoding: utf-8
2
+
3
+ require 'y_support/null_object'
4
+ require 'y_support/name_magic'
5
+ require 'y_support/core_ext/hash'
6
+ require 'y_support/typing'
7
+ require 'y_support/unicode'
8
+ require 'y_support/abstract_algebra'
9
+
10
+ require 'active_support/core_ext/module/delegation'
11
+ require 'active_support/core_ext/array/extract_options'
12
+ require 'active_support/core_ext/string/starts_ends_with'
13
+
14
+ require_relative 'sy/version'
15
+ require_relative 'sy/expressible_in_units'
16
+ require_relative 'sy/fixed_assets_of_the_module'
17
+ require_relative 'sy/mapping'
18
+ require_relative 'sy/dimension'
19
+ require_relative 'sy/quantity'
20
+ require_relative 'sy/composition'
21
+ require_relative 'sy/magnitude'
22
+ require_relative 'sy/absolute_magnitude'
23
+ require_relative 'sy/signed_magnitude'
24
+ require_relative 'sy/unit'
25
+
26
+ # The most prominent feature of SY is, that it extends the Numeric class
27
+ # with methods corresponding to units and their abbreviations.
28
+ #
29
+ # In other words, we can say 5.metre, or Rational( 5, 2 ).metre, and the
30
+ # computer will understand, that these numbers represent magnitudes of the
31
+ # physical quantity SY::Length expressed in the unit SY::METRE. Equally,
32
+ # we can use abbreviations (such as 5.m, 2.5.m), prefixes (such as 5.km,
33
+ # 5.kilometre, 5.km), exponents (such as 5.m² for 5 square metres) and
34
+ # chaining (such as 5.m.s⁻¹ to denote speed of 5 metres per second).
35
+ #
36
+ # You should definitely learn how to type Unicode exponent characters, such
37
+ # as ², ³, ⁻¹ etc. It is possible to use alterantive syntax such as 5.m.s(-1)
38
+ # instead of 5.m.s⁻¹, but you should avoid it whenever possible. Unicode
39
+ # exponents make the physical models that you will be constructing with SY
40
+ # much more readable. And we know that code is (usually) write once, read
41
+ # many times. So it pays off to type an extra keystroke when writing the to
42
+ # make the model more readable for the many subsequent revisions.
43
+ #
44
+ # One more remark here would be, that due to the fact, that many unit names
45
+ # and abbreviations are very short and common words, there can be collisions.
46
+ # For example ActiveSupport already provides handling for time units (hour,
47
+ # minute, second etc.), which would collide with SY methods of the same name.
48
+ # Since SY relies on method_missing, if these methods are already defined for
49
+ # numerics, SY method_missing will not activate and ActiveSupport methods will
50
+ # be used. In this particular case, SY methods still can be invoked using
51
+ # abbreviations (5.s, 5.h, 5.min)
52
+ #
53
+ Numeric.class_exec { include ::SY::ExpressibleInUnits }
54
+
55
+ # === Instead of introduction
56
+ #
57
+ # SY module defines certain usual constants, quantities and units. The best
58
+ # introduction to how SY works would be if we take a look at the examples
59
+ # of the most common quantities and units right here in the SY module:
60
+ #
61
+ module SY
62
+ # Let SY::Amount be a standard dimensionless quantity:
63
+ Amount = Quantity.standard of: Dimension.zero
64
+
65
+ # Convenience constructor of amounts:
66
+ def self.Amount number
67
+ SY::Amount.relative.magnitude number
68
+ end
69
+
70
+ # AVOGADRO_CONSTANT (Nᴀ) is a certain well-known amount of things:
71
+ Nᴀ = AVOGADRO_CONSTANT = SY.Amount 6.02214e23
72
+
73
+ # Let SY::UNIT be a standard unit of SY::Amount. Note that the upcase name
74
+ # of the constant "UNIT" implies, via YSupport's NameMagic mixin, that the
75
+ # name of the object becomes :unit and that it is possible to use syntax
76
+ # such as 42.unit to create magnitudes of SY::Amount.
77
+ UNIT = Unit.standard of: Amount
78
+
79
+ # Let SY::MoleAmount be another dimensionless quantity:
80
+ MoleAmount = Quantity.dimensionless
81
+
82
+ # And let SY::MOLE be its standard unit, related to SY::Amount via Nᴀ:
83
+ MOLE = Unit.standard of: MoleAmount, short: "mol", amount: Nᴀ.unit
84
+
85
+ # === Basic dimension L (length)
86
+
87
+ # Let SY::Length be a standard quantity of basic dimension L:
88
+ Length = Quantity.standard of: :L
89
+
90
+ # Let SY::METRE be its standard unit.
91
+ METRE = Unit.standard of: Length, short: "m"
92
+
93
+ # === Basic dimension M (mass)
94
+
95
+ # Let SY::Mass be a standard quantity of basic dimension M:
96
+ Mass = Quantity.standard of: :M
97
+
98
+ # Let SY::KILOGRAM be its standard unit:
99
+ KILOGRAM = Unit.standard of: Mass, short: "kg"
100
+ # Let SY::GRAM be another unit of SY::Mass, equal to 0.001.kg:
101
+ GRAM = Unit.of Mass, amount: 0.001.kg, short: "g"
102
+ # Let SY::TON be another...
103
+ TON = Unit.of Mass, amount: 1000.kg, short: "t"
104
+ # And SY::DALTON another...
105
+ DALTON = Unit.of Mass, short: "Da", amount: 1.66053892173e-27.kg
106
+
107
+ # === Basic dimension T
108
+
109
+ # Let SY::Time be a standard quantity of basic dimension T:
110
+ Time = Quantity.standard of: :T
111
+
112
+ # Let SY::SECOND be its standard unit:
113
+ SECOND = Unit.standard of: Time, short: "s"
114
+ # Let SY::MINUTE be another unit:
115
+ MINUTE = Unit.of Time, short: "min", amount: 60.s
116
+ # And SY::HOUR another:
117
+ HOUR = Unit.of Time, short: "h", amount: 60.min
118
+
119
+ # === Basic dimension Q
120
+
121
+ # Let SY::ElectricCharge be a standard quantity of basic dimension Q:
122
+ ElectricCharge = Quantity.standard of: :Q
123
+
124
+ # And SY::COULOMB be its standard unit:
125
+ COULOMB = Unit.standard of: ElectricCharge, short: "C"
126
+
127
+ # === Basic dimension Θ
128
+
129
+ # Let SY::Temperature be a standard quantity of basic dimension Θ:
130
+ Temperature = Quantity.standard of: :Θ
131
+
132
+ # And SY::KELVIN be its standard unit:
133
+ KELVIN = Unit.standard of: Temperature, short: "K"
134
+
135
+ # Now let us define a useful constant:
136
+ TP_H₂O = TRIPLE_POINT_OF_WATER = 273.15.K
137
+
138
+ # Celsius temperature is a little bit peculiar in that it has offset of
139
+ # 273.15.K with respect to Kelvin temperature, and I am not sure whether
140
+ # at this moment SY is handling this right. But nevertheless:
141
+ CelsiusTemperature = Quantity.of :Θ
142
+
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
146
+ # alias :°C :celsius # with U+00B0 DEGREE SIGN
147
+ # alias :˚C :celsius # with U+02DA RING ABOVE
148
+ # alias :℃ :celsius # U+2103 DEGREE CELSIUS
149
+
150
+ # FahrenheitTemperature = Quantity.of :Θ
151
+ # FAHRENHEIT = Unit.standard of: FahrenheitTemperature, short: '°F'
152
+ # # alias :°F :fahrenheit # with U+00B0 DEGREE SIGN
153
+ # # alias :˚F :fahrenheit # with U+02DA RING ABOVE
154
+ # # alias :℉ :fahrenheit # U+2109 DEGREE FAHRENHEIT
155
+ # # FIXME: Patch FahrenheitTemperature to make it work with SY::Temperature
156
+
157
+
158
+ # HUMAN_BODY_TEMPERATURE = 37.°C.( KELVIN )
159
+ # STANDARD_TEMPERATURE = 25.°C.( KELVIN )
160
+ HUMAN_BODY_TEMPERATURE = TP_H₂O + 37.K
161
+ STANDARD_LABORATORY_TEMPERATURE = TP_H₂O + 25.K
162
+
163
+ # === Dimensionless quantities
164
+
165
+ # For now, these are just unimplemented proposals of what users might expect
166
+ # from SY:
167
+ #
168
+ # degree, alias deg, ° # angle measure
169
+ # arcminute, alias ʹ, ′ # angle measure
170
+ # arcsecond, alias ʹʹ, ′′, ″
171
+
172
+ # === Quantities of composite dimensions
173
+
174
+ # Quantity SY::Area is obtained by raising quantity SY::Length to 2:
175
+ Area = Length ** 2
176
+
177
+ # Quantity SY::Volume is obtained by raising quantity SY::Length to 3:
178
+ Volume = Length ** 3
179
+
180
+ # SY::LitreVolume is another quantity of the same dimension as SY::Volume:
181
+ LitreVolume = Quantity.of Volume.dimension
182
+
183
+ # SY::LITRE is the standard unit of SY::LitreVolume:
184
+ LITRE = Unit.standard of: LitreVolume, short: "l", amount: 1.dm³
185
+
186
+ # At this point, there are certain things to note. Since standard units of
187
+ # SY::Area and SY::Volume have not been specified, they are assumed to be
188
+ # simply 1.metre², resp. 1.metre³. But LitreVolume, whose standard unit
189
+ # has been named litre, with abbreviation "l", will from now on present
190
+ # its magnitudes expressed in litres, rather than cubic metres. While
191
+ # theoretically, LitreVolume and Volume both have dimension L³ and both
192
+ # can be used to express volume, LitreVolume in SY conveys the context of
193
+ # chemistry.
194
+
195
+ # SY::Molarity is obtained by dividing SY::MoleAmount by SY::LitreVolume:
196
+ Molarity = ( MoleAmount / LitreVolume ).protect!
197
+
198
+ # Standard unit of SY::Molarity is SY::MOLAR:
199
+ MOLAR = Unit.standard of: Molarity, abbreviation: "M"
200
+
201
+ # Let us now note the #protect! directive at the line above defining
202
+ # SY::Molarity. Method #protect! prevents Molarity from understanding itself
203
+ # as merely L⁻³ (or 1/metre³), as would follow from its dimensional analysis.
204
+ # Method #protect! causes Molarity to appreciate its identity as :molar,
205
+ # which is exactly what chemists expect.
206
+
207
+ # SY::Frequency, a quantity that many will expect:
208
+ Frequency = 1 / Time
209
+
210
+ # SY::HERTZ is its unit:
211
+ HERTZ = Unit.of Frequency, short: "Hz"
212
+ # Fixme: it would be expected that 1.s(-1) would not present itself as 1.Hz,
213
+ # provided that we did not make :hertz standard unit of Frequency
214
+
215
+ # Define SY::Speed as SY::Length / SY::Time and make it a standard quantity
216
+ # of its dimension.
217
+ Speed = ( Length / Time ).standard!
218
+
219
+ # Similar for SY::Acceleration:
220
+ Acceleration = ( Speed / Time ).standard!
221
+
222
+ # For SY::Force...
223
+ Force = ( Acceleration * Mass ).standard!
224
+
225
+ # This time, make SY::NEWTON its standard unit:
226
+ NEWTON = Unit.standard of: Force, short: "N"
227
+
228
+ # For SY::Energy...
229
+ Energy = ( Force * Length ).standard!
230
+
231
+ # make SY::JOULE its standard unit:
232
+ JOULE = Unit.standard of: Energy, short: "J"
233
+ # SY::CALORIE means thermochemical calorie:
234
+ CALORIE = Unit.of Energy, short: "cal", amount: 4.184.J
235
+
236
+ # SY::Power...
237
+ Power = ( Energy / Time ).standard!
238
+
239
+ # make SY::WATT its standard unit:
240
+ WATT = Unit.standard of: Power, short: "W"
241
+
242
+ # SY::Pressure...
243
+ Pressure = ( Force / Area ).standard!
244
+
245
+ # make SY::PASCAL its standard unit:
246
+ PASCAL = Unit.standard of: Pressure, short: "Pa"
247
+
248
+ # SY::ElectricCurrent...
249
+ ElectricCurrent = ( ElectricCharge / Time ).standard!
250
+
251
+ # make SY::AMPERE its standard unit:
252
+ AMPERE = Unit.standard of: ElectricCurrent, short: "A"
253
+
254
+ # SY::ElectricPotential...
255
+ ElectricPotential = ( Energy / ElectricCharge ).standard!
256
+
257
+ # make SY::VOLT its standard unit:
258
+ VOLT = Unit.standard of: ElectricPotential, short: "V"
259
+
260
+ # FIXME: This should raise a friendly error:
261
+ # MOLAR = Unit.standard of: Molarity, abbreviation: "M", amount: 1.mol.l⁻¹
262
+
263
+ # SY::Molality...
264
+ Molality = MoleAmount / Mass
265
+
266
+ # make SY::MOLAL its unit (but don't make it a standard unit...):
267
+ MOLAL = Unit.of Molality
268
+
269
+ # SY::Molecularity...
270
+ Molecularity = Amount / LitreVolume
271
+
272
+ # Having defined Joules and Kelvins, we can spell out the Boltzmann constant:
273
+ Kʙ = BOLTZMANN_CONSTANT = 1.380648813e-23.J.K⁻¹
274
+ end
@@ -0,0 +1,96 @@
1
+ #encoding: utf-8
2
+
3
+ # Qualities specific to absolute magnitudes.
4
+ #
5
+ module SY::AbsoluteMagnitude
6
+ # Absolute magnitude constructor takes :quantity (alias :of) named argument,
7
+ # and :amount named argument, where amount must be nonnegative.
8
+ #
9
+ def initialize args={}
10
+ @quantity = args[:quantity] || args[:of]
11
+ amnt = args[:amount]
12
+ @amount = case amnt
13
+ when Numeric then amnt
14
+ when nil then 1
15
+ else
16
+ begin
17
+ amnt.amount
18
+ rescue NameError, NoMethodError
19
+ amnt
20
+ end
21
+ end
22
+ raise SY::MagnitudeError,
23
+ "Unsigned magnitudes canot have negative amount!" if @amount < 0
24
+ end
25
+
26
+ # For absolute magnitudes, #+ method always returns a result framed in
27
+ # corresponding relative quantity.
28
+ #
29
+ def + m2
30
+ return magnitude amount + m2.amount if m2.quantity == quantity.relative
31
+ return quantity.relative.magnitude( amount + m2.amount ) if
32
+ quantity == m2.quantity
33
+ return self if m2.equal? SY::ZERO
34
+ # o1, o2 = m2.coerce( self )
35
+ # return o1 + o2
36
+ raise SY::QuantityError, "Unable to perform #{quantity} + #{m2.quantity}!"
37
+ end
38
+
39
+ # Addition of absolute magnitudes that returns a result framed as
40
+ # absolute quantity.
41
+ #
42
+ def add m2
43
+ return magnitude( amount + m2.amount ) if quantity == m2.quantity
44
+ return self if m2.equal? SY::ZERO
45
+ # o1, o2 = m2.coerce( self )
46
+ # return o1.add o2
47
+ raise SY::QuantityError, "Unable to perform #add with #{m2.quantity}!"
48
+ end
49
+
50
+ # For absolute magnitudes, #- method always returns a result framed in
51
+ # corresponding relative quantity.
52
+ #
53
+ def - m2
54
+ return magnitude amount - m2.amount if m2.quantity == quantity.relative
55
+ return quantity.relative.magnitude( amount - m2.amount ) if
56
+ quantity == m2.quantity
57
+ return self if m2.equal? SY::ZERO
58
+ # o1, o2 = m2.coerce( self )
59
+ # return o1 - o2
60
+ raise( SY::QuantityError, "Unable to perform #{quantity} - #{m2.quantity}!" )
61
+ end
62
+
63
+ # Subtraction of absolute magnitudes that returns a result framed as
64
+ # absolute quantity. (With caller being responsible for the result being
65
+ # nonnegative.)
66
+ #
67
+ def subtract m2
68
+ return magnitude( amount + m2.amount ) if quantity == m2.quantity
69
+ return self if m2.equal? SY::ZERO
70
+ # o1, o2 = m2.coerce( self )
71
+ # return o1.subtract o2
72
+ raise( SY::QuantityError, "Unable to perform #add with #{m2.quantity}!" )
73
+ end
74
+
75
+ # "Subtraction" of absolute magnitudes, that never takes more thant the
76
+ # amount from which subtraction is being performed. But for this reason,
77
+ # unlike regular #subtract, it is not known in advance what amount will
78
+ # be subtracted. Returns an array of two values: first one is the amount
79
+ # actually subtracted (which may differ from the amount asked for), and
80
+ # the second is the actual result of the subtraction (amount left). The
81
+ # latter will be zero if attempt is made to subtract greater amount from
82
+ # a smaller one.
83
+ #
84
+ def take other
85
+ actually_taken = [ self, other ].min
86
+ return [ actually_taken, other.subtract( take ) ]
87
+ end
88
+
89
+ private
90
+
91
+ # String describing this class.
92
+ #
93
+ def çς
94
+ "Magnitude"
95
+ end
96
+ end # class SY::Magnitude