sy 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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