quantify 1.1.0 → 1.2.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.
- data/.rvmrc +1 -0
- data/Gemfile +14 -0
- data/README +10 -10
- data/Rakefile +65 -0
- data/VERSION +1 -0
- data/lib/quantify/config.rb +10 -14
- data/lib/quantify/dimensions.rb +1 -1
- data/lib/quantify/quantify.rb +2 -35
- data/lib/quantify/unit/base_unit.rb +29 -25
- data/lib/quantify/unit/compound_base_unit.rb +21 -22
- data/lib/quantify/unit/compound_base_unit_list.rb +260 -0
- data/lib/quantify/unit/compound_unit.rb +78 -218
- data/lib/quantify/unit/unit.rb +119 -4
- data/lib/quantify.rb +1 -0
- data/quantify.gemspec +90 -0
- data/spec/compound_unit_spec.rb +124 -13
- data/spec/quantify_spec.rb +0 -14
- data/spec/quantity_spec.rb +6 -6
- data/spec/unit_spec.rb +57 -39
- metadata +130 -32
@@ -0,0 +1,260 @@
|
|
1
|
+
|
2
|
+
module Quantify
|
3
|
+
module Unit
|
4
|
+
class CompoundBaseUnitList < Array
|
5
|
+
|
6
|
+
# Returns a string represents of the units contained within <tt>units</tt>
|
7
|
+
# based on <tt>attribute</tt> and <tt>unit_delimiter</tt>.
|
8
|
+
#
|
9
|
+
def self.attribute_string(units,attribute,unit_delimiter,reciprocal=false)
|
10
|
+
string = ""
|
11
|
+
units.each do |base|
|
12
|
+
string << unit_delimiter unless string.empty?
|
13
|
+
string << base.send(attribute,reciprocal)
|
14
|
+
end
|
15
|
+
return string
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns an CompoundBaseUnitList containing only the base units which have
|
19
|
+
# positive indices
|
20
|
+
def numerator_units
|
21
|
+
CompoundBaseUnitList.new select { |base| base.is_numerator? }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns an CompoundBaseUnitList containing only the base units which have
|
25
|
+
# negative indices
|
26
|
+
def denominator_units
|
27
|
+
CompoundBaseUnitList.new select { |base| base.is_denominator? }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns <tt>true</tt> if all base untis are SI units
|
31
|
+
def is_si_unit?
|
32
|
+
all? { |base| base.is_si_unit? }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns <tt>true</tt> if all base untis are not SI units
|
36
|
+
def is_non_si_unit?
|
37
|
+
any? { |base| base.is_non_si_unit? }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns <tt>true</tt> if all base untis are SI base units
|
41
|
+
def is_base_quantity_si_unit?
|
42
|
+
all? { |base| base.is_base_quantity_si_unit? }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Cancel base units across numerator and denominator. If similar units occur
|
46
|
+
# in both the numerator and denominator, they can be cancelled, i.e. their
|
47
|
+
# powers reduced correspondingly until one is removed.
|
48
|
+
#
|
49
|
+
# This method is useful when wanting to remove specific units that can be
|
50
|
+
# cancelled from the compound unit configuration while retaining the
|
51
|
+
# remaining units in the current format.
|
52
|
+
#
|
53
|
+
# If no other potentially cancelable units need to be retained, the method
|
54
|
+
# #consolidate_base_units! can be called with the :full argument instead
|
55
|
+
#
|
56
|
+
# This method takes an arbitrary number of arguments which represent the units
|
57
|
+
# which are required to be cancelled (string, symbol or object)
|
58
|
+
#
|
59
|
+
def cancel!(*units)
|
60
|
+
units.each do |unit|
|
61
|
+
raise Exceptions::InvalidArgumentError, "Cannot cancel by a compound unit" if unit.is_a? Unit::Compound
|
62
|
+
unit = Unit.for unit unless unit.is_a? Unit::Base
|
63
|
+
|
64
|
+
numerator_unit = numerator_units.find { |base| unit.is_equivalent_to? base.unit }
|
65
|
+
denominator_unit = denominator_units.find { |base| unit.is_equivalent_to? base.unit }
|
66
|
+
|
67
|
+
if numerator_unit && denominator_unit
|
68
|
+
cancel_value = [numerator_unit.index,denominator_unit.index].min.abs
|
69
|
+
numerator_unit.index -= cancel_value
|
70
|
+
denominator_unit.index += cancel_value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
consolidate_numerator_and_denominator!
|
74
|
+
end
|
75
|
+
|
76
|
+
# Consilidates base quantities by finding multiple instances of the same unit
|
77
|
+
# type and reducing them into a single unit represenation, by altering the
|
78
|
+
# repsective index. It has the effect of raising units to powers and cancelling
|
79
|
+
# those which appear in the numerator AND denominator
|
80
|
+
#
|
81
|
+
# This is a class method which takes an arbitrary array of base units as an
|
82
|
+
# argument. This means that consolidation can be performed on either all
|
83
|
+
# base units or just a subset - the numerator or denominator units.
|
84
|
+
#
|
85
|
+
def consolidate!
|
86
|
+
new_base_units = []
|
87
|
+
while size > 0 do
|
88
|
+
new_base = shift
|
89
|
+
next if new_base.unit.is_dimensionless?
|
90
|
+
|
91
|
+
new_base.index = select do |other_base|
|
92
|
+
new_base.unit.is_equivalent_to? other_base.unit
|
93
|
+
end.inject(new_base.index) do |index,other_base|
|
94
|
+
delete other_base
|
95
|
+
index += other_base.index
|
96
|
+
end
|
97
|
+
new_base_units << new_base unless new_base.is_dimensionless?
|
98
|
+
end
|
99
|
+
replace(new_base_units)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Similar to #consolidate_base_units! but operates on the numerator and denomiator
|
103
|
+
# are separately. This means that two instances of the same unit
|
104
|
+
# should not occur in the numerator OR denominator (rather they are combined
|
105
|
+
# and the index changed accordingly), but similar units are not cancelled
|
106
|
+
# across the numerator and denominators.
|
107
|
+
#
|
108
|
+
def consolidate_numerator_and_denominator!
|
109
|
+
replace(numerator_units.consolidate! + denominator_units.consolidate!)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Make compound unit use consistent units for representing each physical
|
113
|
+
# quantity. For example, lb/kg => kg/kg.
|
114
|
+
#
|
115
|
+
# The units to use for particular physical dimension can be specified. If
|
116
|
+
# no unit is specified for a physical quantity which is represented in the
|
117
|
+
# <tt>self</tt>, then the first unit found for that physical quantity is
|
118
|
+
# used as the canonical one.
|
119
|
+
#
|
120
|
+
def rationalize!(*required_units)
|
121
|
+
each do |base|
|
122
|
+
new_unit = required_units.map { |unit| Unit.for(unit) }.find { |unit| unit.dimensions == base.unit.dimensions } ||
|
123
|
+
find { |other_base| other_base.unit.dimensions == base.unit.dimensions }.unit
|
124
|
+
base.unit = new_unit
|
125
|
+
end
|
126
|
+
consolidate_numerator_and_denominator!
|
127
|
+
end
|
128
|
+
|
129
|
+
# Similar to #rationalize_base_units! but operates on the numerator and
|
130
|
+
# denomiator are separately. This means that different units representing
|
131
|
+
# the same physical quantity may remain across the numerator and
|
132
|
+
# denominator units.
|
133
|
+
#
|
134
|
+
def rationalize_numerator_and_denominator!(*units)
|
135
|
+
replace(numerator_units.rationalize!(*units) + denominator_units.rationalize!(*units))
|
136
|
+
end
|
137
|
+
|
138
|
+
# Derive a representation of the physical dimensions of the compound unit
|
139
|
+
# by multilying together the dimensions of each of the base units.
|
140
|
+
#
|
141
|
+
def dimensions
|
142
|
+
inject(Dimensions.dimensionless) { |dimension,base| dimension * base.dimensions }
|
143
|
+
end
|
144
|
+
|
145
|
+
# Derive a name for the unit based on the names of the base units
|
146
|
+
#
|
147
|
+
# Both singluar and plural names can be derived. In the case of pluralized
|
148
|
+
# names, the last unit in the numerator is pluralized. Singular names are
|
149
|
+
# assumed by default, in which case no argument is required.
|
150
|
+
#
|
151
|
+
# Format for names includes the phrase 'per' to differentiate denominator
|
152
|
+
# units and words, rather than numbers, for representing powers, e.g.
|
153
|
+
#
|
154
|
+
# square metres per second
|
155
|
+
#
|
156
|
+
def name(plural=false)
|
157
|
+
attribute_string_with_denominator_syntax(:name, " per ", " ", plural)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Derive a symbol for the unit based on the symbols of the base units
|
161
|
+
#
|
162
|
+
# Get the units in order first so that the denominator values (those
|
163
|
+
# with negative powers) follow the numerators
|
164
|
+
#
|
165
|
+
# Symbol format use unit symbols, with numerator symbols followed by
|
166
|
+
# denominator symbols and powers expressed using the "^" notation with 'true'
|
167
|
+
# values (i.e. preservation of minus signs).
|
168
|
+
#
|
169
|
+
def symbol
|
170
|
+
if Unit.use_symbol_denominator_syntax?
|
171
|
+
attribute_string_with_denominator_syntax(:symbol,Unit.symbol_denominator_delimiter, Unit.symbol_unit_delimiter)
|
172
|
+
else
|
173
|
+
attribute_string_with_indices_only(:symbol,Unit.symbol_unit_delimiter)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Derive a label for the comound unit. This follows the format used in the
|
178
|
+
# JScience library in using a middot notation ("·") to spearate units and
|
179
|
+
# slash notation "/" to separate numerator and denominator. Since the
|
180
|
+
# denominator is differentiated, denominator unit powers are rendered in
|
181
|
+
# absolute terms (i.e. minus sign omitted) except when no numerator values
|
182
|
+
# are present.
|
183
|
+
#
|
184
|
+
def label
|
185
|
+
attribute_string_with_denominator_syntax(:label, "/", "·")
|
186
|
+
end
|
187
|
+
|
188
|
+
# Derive the multiplicative factor for the unit based on those of the base
|
189
|
+
# units
|
190
|
+
#
|
191
|
+
def factor
|
192
|
+
inject(1) { |factor,base| factor * base.factor }
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
|
197
|
+
def initialize_copy(source)
|
198
|
+
super
|
199
|
+
map! { |base_unit| base_unit.clone }
|
200
|
+
end
|
201
|
+
|
202
|
+
# Return a string representing an attribute of <tt>self</tt> containing
|
203
|
+
# no numerator-denominator structure but rather representing each unit
|
204
|
+
# entirely in terms of a unit and and index. e.g. m s^-1
|
205
|
+
#
|
206
|
+
def attribute_string_with_indices_only(attribute,unit_delimiter)
|
207
|
+
units = sort { |base,next_unit| next_unit.index <=> base.index }
|
208
|
+
CompoundBaseUnitList.attribute_string(units,attribute,unit_delimiter).strip
|
209
|
+
end
|
210
|
+
|
211
|
+
# Return a string representing an attribute of <tt>self</tt> containing a
|
212
|
+
# numerator-denominator structure, e.g. m/s
|
213
|
+
#
|
214
|
+
def attribute_string_with_denominator_syntax(attribute,denominator_delimiter,unit_delimiter,plural=false)
|
215
|
+
|
216
|
+
attribute = attribute.to_sym
|
217
|
+
numerator_string = ""
|
218
|
+
denominator_string = ""
|
219
|
+
numerator_units = self.numerator_units
|
220
|
+
denominator_units = self.denominator_units
|
221
|
+
|
222
|
+
unless numerator_units.empty?
|
223
|
+
numerator_units_size = numerator_units.size
|
224
|
+
# Handle pluralisation of last unit if pluralised form required, e.g.
|
225
|
+
# tonne kilometres per hour
|
226
|
+
last_unit = numerator_units.pop if attribute == :name && plural == true
|
227
|
+
numerator_string << CompoundBaseUnitList.attribute_string(numerator_units,attribute,unit_delimiter)
|
228
|
+
numerator_string << unit_delimiter + last_unit.pluralized_name if last_unit
|
229
|
+
if Unit.use_symbol_parentheses?
|
230
|
+
if numerator_units_size > 1 && !denominator_units.empty?
|
231
|
+
numerator_string = "(#{numerator_string.strip})"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
unless denominator_units.empty?
|
237
|
+
# If no numerator exists then indices should be reciprocalized since
|
238
|
+
# they do NOT occur after the denominator delimeter (e.g. "/")
|
239
|
+
reciprocal = !numerator_string.empty?
|
240
|
+
denominator_string << CompoundBaseUnitList.attribute_string(denominator_units,attribute,unit_delimiter,reciprocal)
|
241
|
+
if Unit.use_symbol_parentheses?
|
242
|
+
if denominator_units.size > 1 && !numerator_string.empty?
|
243
|
+
denominator_string = "(#{denominator_string.strip})"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
string = numerator_string
|
249
|
+
if (!denominator_string.empty? && !numerator_string.empty?) ||
|
250
|
+
(!denominator_string.empty? && numerator_string.empty? && attribute == :name)
|
251
|
+
string << denominator_delimiter
|
252
|
+
end
|
253
|
+
string << denominator_string
|
254
|
+
|
255
|
+
return string.strip
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
@@ -20,58 +20,6 @@ module Quantify
|
|
20
20
|
# The Compound class provides support for arbitrarily defined compound units
|
21
21
|
# which don't have well-established names.
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
# Consilidates base quantities by finding multiple instances of the same unit
|
26
|
-
# type and reducing them into a single unit represenation, by altering the
|
27
|
-
# repsective index. It has the effect of raising units to powers and cancelling
|
28
|
-
# those which appear in the numerator AND denominator
|
29
|
-
#
|
30
|
-
# This is a class method which takes an arbitrary array of base units as an
|
31
|
-
# argument. This means that consolidation can be performed on either all
|
32
|
-
# base units or just a subset - the numerator or denominator units.
|
33
|
-
#
|
34
|
-
def self.consolidate_base_units(base_units)
|
35
|
-
raise Exceptions::InvalidArgumentError, "Must provide an array of base units" unless base_units.is_a? Array
|
36
|
-
|
37
|
-
new_base_units = []
|
38
|
-
|
39
|
-
while base_units.size > 0 do
|
40
|
-
new_base = base_units.shift
|
41
|
-
next if new_base.unit.is_dimensionless?
|
42
|
-
|
43
|
-
new_base.index = base_units.select do |other_base|
|
44
|
-
new_base.unit.is_equivalent_to? other_base.unit
|
45
|
-
end.inject(new_base.index) do |index,other_base|
|
46
|
-
base_units.delete other_base
|
47
|
-
index += other_base.index
|
48
|
-
end
|
49
|
-
|
50
|
-
new_base_units << new_base unless new_base.is_dimensionless?
|
51
|
-
end
|
52
|
-
return new_base_units
|
53
|
-
end
|
54
|
-
|
55
|
-
# Make compound unit use consistent units for representing each physical
|
56
|
-
# quantity. For example, lb/kg => kg/kg.
|
57
|
-
#
|
58
|
-
# This is a class method which takes an arbitrary array of base units as an
|
59
|
-
# argument. This means that consolidation can be performed on either all
|
60
|
-
# base units or just a subset - e.g. the numerator or denominator units.
|
61
|
-
#
|
62
|
-
# The units to use for particular physical dimension can be specified
|
63
|
-
# following the inital argument. If no unit is specified for a physical
|
64
|
-
# quantity which is represented in the array of base units, then the first
|
65
|
-
# unit found for that physical quantity is used as the canonical one.
|
66
|
-
#
|
67
|
-
def self.rationalize_base_units(base_units=[],*required_units)
|
68
|
-
base_units.each do |base|
|
69
|
-
new_unit = required_units.map { |unit| Unit.for(unit) }.find { |unit| unit.measures == base.measures } ||
|
70
|
-
base_units.find { |unit| unit.measures == base.measures }.unit
|
71
|
-
base.unit = new_unit
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
23
|
attr_reader :base_units, :acts_as_equivalent_unit
|
76
24
|
|
77
25
|
# Initialize a compound unit by providing an array containing a represenation
|
@@ -87,7 +35,7 @@ module Quantify
|
|
87
35
|
# explicit index
|
88
36
|
#
|
89
37
|
def initialize(*units)
|
90
|
-
@base_units =
|
38
|
+
@base_units = CompoundBaseUnitList.new
|
91
39
|
units.each do |unit|
|
92
40
|
if unit.is_a? CompoundBaseUnit
|
93
41
|
@base_units << unit
|
@@ -105,100 +53,43 @@ module Quantify
|
|
105
53
|
consolidate_numerator_and_denominator_units!
|
106
54
|
end
|
107
55
|
|
56
|
+
# Refresh all unit attributes. Can be used following a change to unit attribute
|
57
|
+
# global configurations
|
58
|
+
#
|
59
|
+
def refresh_attributes
|
60
|
+
initialize_attributes
|
61
|
+
end
|
108
62
|
|
109
63
|
# Returns an array containing only the base units which have positive indices
|
110
64
|
def numerator_units
|
111
|
-
@base_units.
|
65
|
+
@base_units.numerator_units
|
112
66
|
end
|
113
67
|
|
114
68
|
# Returns an array containing only the base units which have negative indices
|
115
69
|
def denominator_units
|
116
|
-
@base_units.
|
70
|
+
@base_units.denominator_units
|
117
71
|
end
|
118
72
|
|
119
73
|
# Convenient accessor method for pluralized names
|
120
74
|
def pluralized_name
|
121
|
-
|
75
|
+
@base_units.name(true)
|
122
76
|
end
|
123
77
|
|
124
78
|
# Determine is a unit object represents an SI named unit.
|
125
|
-
#
|
126
79
|
def is_si_unit?
|
127
|
-
@base_units.
|
80
|
+
@base_units.is_si_unit?
|
128
81
|
end
|
129
82
|
|
130
83
|
def is_non_si_unit?
|
131
|
-
@base_units.
|
84
|
+
@base_units.is_non_si_unit?
|
132
85
|
end
|
133
86
|
|
134
87
|
def is_base_quantity_si_unit?
|
135
|
-
@base_units.
|
136
|
-
end
|
137
|
-
|
138
|
-
# Consolidate base units. A 'full' consolidation is performed, i.e.
|
139
|
-
# consolidation across numerator and denominator. This is equivalent to the
|
140
|
-
# automatic partial consolidation AND a cancelling of units (i.e.
|
141
|
-
# #cancel_base_units!)
|
142
|
-
#
|
143
|
-
def consolidate_base_units!
|
144
|
-
@base_units = Compound.consolidate_base_units(@base_units)
|
145
|
-
initialize_attributes
|
146
|
-
return self
|
88
|
+
@base_units.is_base_quantity_si_unit?
|
147
89
|
end
|
148
90
|
|
149
|
-
|
150
|
-
|
151
|
-
# powers reduced correspondingly until one is removed.
|
152
|
-
#
|
153
|
-
# This method is useful when wanting to remove specific units that can be
|
154
|
-
# cancelled from the compound unit configuration while retaining the
|
155
|
-
# remaining units in the current format.
|
156
|
-
#
|
157
|
-
# If no other potentially cancelable units need to be retained, the method
|
158
|
-
# #consolidate_base_units! can be called with the :full argument instead
|
159
|
-
#
|
160
|
-
# This method takes an arbitrary number of arguments which represent the units
|
161
|
-
# which are required to be cancelled (string, symbol or object)
|
162
|
-
#
|
163
|
-
def cancel_base_units!(*units)
|
164
|
-
units.each do |unit|
|
165
|
-
raise Exceptions::InvalidArgumentError, "Cannot cancel by a compound unit" if unit.is_a? Unit::Compound
|
166
|
-
unit = Unit.for unit unless unit.is_a? Unit::Base
|
167
|
-
|
168
|
-
numerator_unit = numerator_units.find { |base| unit.is_equivalent_to? base.unit }
|
169
|
-
denominator_unit = denominator_units.find { |base| unit.is_equivalent_to? base.unit }
|
170
|
-
|
171
|
-
if numerator_unit && denominator_unit
|
172
|
-
cancel_value = [numerator_unit.index,denominator_unit.index].min.abs
|
173
|
-
numerator_unit.index -= cancel_value
|
174
|
-
denominator_unit.index += cancel_value
|
175
|
-
end
|
176
|
-
end
|
177
|
-
consolidate_numerator_and_denominator_units!
|
178
|
-
end
|
179
|
-
|
180
|
-
# Make the base units of self use consistent units for each physical quantity
|
181
|
-
# represented. For example, lb/kg => kg/kg.
|
182
|
-
#
|
183
|
-
# By default, units are rationalized within the the numerator and denominator
|
184
|
-
# respectively. That is, different units representing the same physical
|
185
|
-
# quantity may appear across the numerator and denominator, but not within
|
186
|
-
# each. To fully rationalize the base units of self, pass in the symbol
|
187
|
-
# :full as a first argument. Otherwise :partial is passed as the default.
|
188
|
-
#
|
189
|
-
# The units to use for particular physical dimension can be specified
|
190
|
-
# following the inital argument. If no unit is specified for a physical
|
191
|
-
# quantity which is represented in the array of base units, then the first
|
192
|
-
# unit found for that physical quantity is used as the canonical one.
|
193
|
-
#
|
194
|
-
def rationalize_base_units!(scope=:partial,*units)
|
195
|
-
if scope == :full
|
196
|
-
Compound.rationalize_base_units(@base_units,*units)
|
197
|
-
else
|
198
|
-
Compound.rationalize_base_units(numerator_units,*units)
|
199
|
-
Compound.rationalize_base_units(denominator_units,*units)
|
200
|
-
end
|
201
|
-
consolidate_numerator_and_denominator_units!
|
91
|
+
def has_multiple_base_units?
|
92
|
+
@base_units.size > 1
|
202
93
|
end
|
203
94
|
|
204
95
|
# Return a known unit which is equivalent to self in terms of its physical
|
@@ -227,121 +118,90 @@ module Quantify
|
|
227
118
|
end
|
228
119
|
end
|
229
120
|
|
230
|
-
|
121
|
+
# Cancel base units across numerator and denominator. If similar units occur
|
122
|
+
# in both the numerator and denominator, they can be cancelled, i.e. their
|
123
|
+
# powers reduced correspondingly until one is removed.
|
124
|
+
#
|
125
|
+
# This method is useful when wanting to remove specific units that can be
|
126
|
+
# cancelled from the compound unit configuration while retaining the
|
127
|
+
# remaining units in the current format.
|
128
|
+
#
|
129
|
+
# If no other potentially cancelable units need to be retained, the method
|
130
|
+
# #consolidate_base_units! can be called with the :full argument instead
|
131
|
+
#
|
132
|
+
# This method takes an arbitrary number of arguments which represent the units
|
133
|
+
# which are required to be cancelled (string, symbol or object)
|
134
|
+
#
|
135
|
+
def cancel_base_units!(*units)
|
136
|
+
@base_units.cancel!(*units)
|
137
|
+
initialize_attributes
|
138
|
+
end
|
231
139
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
140
|
+
# Consilidates base quantities by finding multiple instances of the same unit
|
141
|
+
# type and reducing them into a single unit represenation, by altering the
|
142
|
+
# repsective index. It has the effect of raising units to powers and cancelling
|
143
|
+
# those which appear in the numerator AND denominator
|
144
|
+
#
|
145
|
+
def consolidate_base_units!
|
146
|
+
@base_units.consolidate!
|
147
|
+
initialize_attributes
|
238
148
|
end
|
239
149
|
|
240
|
-
#
|
241
|
-
#
|
150
|
+
# Similar to #consolidate_base_units! but operates on the numerator and
|
151
|
+
# denomiator are separately. This means that two instances of the same unit
|
242
152
|
# should not occur in the numerator OR denominator (rather they are combined
|
243
153
|
# and the index changed accordingly), but similar units are not cancelled
|
244
154
|
# across the numerator and denominators.
|
245
155
|
#
|
246
156
|
def consolidate_numerator_and_denominator_units!
|
247
|
-
|
248
|
-
new_base_units += Compound.consolidate_base_units(numerator_units)
|
249
|
-
new_base_units += Compound.consolidate_base_units(denominator_units)
|
250
|
-
@base_units = new_base_units
|
157
|
+
@base_units.consolidate_numerator_and_denominator!
|
251
158
|
initialize_attributes
|
252
|
-
return self
|
253
159
|
end
|
254
160
|
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
258
|
-
def derive_dimensions
|
259
|
-
@base_units.inject(Dimensions.dimensionless) do |dimension,base|
|
260
|
-
dimension * base.dimensions
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
# Derive a name for the unit based on the names of the base units
|
265
|
-
#
|
266
|
-
# Both singluar and plural names can be derived. In the case of pluralized
|
267
|
-
# names, the last unit in the numerator is pluralized. Singular names are
|
268
|
-
# assumed by default, in which case no argument is required.
|
269
|
-
#
|
270
|
-
# Format for names includes the phrase 'per' to differentiate denominator
|
271
|
-
# units and words, rather than numbers, for representing powers, e.g.
|
161
|
+
# Make compound unit use consistent units for representing each physical
|
162
|
+
# quantity. For example, lb/kg => kg/kg.
|
272
163
|
#
|
273
|
-
#
|
164
|
+
# The units to use for particular physical dimension can be specified. If
|
165
|
+
# no unit is specified for a physical quantity which is represented in the
|
166
|
+
# <tt>self</tt>, then the first unit found for that physical quantity is
|
167
|
+
# used as the canonical one.
|
274
168
|
#
|
275
|
-
def
|
276
|
-
|
277
|
-
|
278
|
-
units = numerator_units
|
279
|
-
last_unit = units.pop if inflection == :plural
|
280
|
-
units.inject(unit_name) do |name,base|
|
281
|
-
name << base.name + " "
|
282
|
-
end
|
283
|
-
unit_name << last_unit.pluralized_name + " " if last_unit
|
284
|
-
end
|
285
|
-
unless denominator_units.empty?
|
286
|
-
unit_name << "per "
|
287
|
-
denominator_units.inject(unit_name) do |name,base|
|
288
|
-
name << base.name + " "
|
289
|
-
end
|
290
|
-
end
|
291
|
-
return unit_name.strip
|
169
|
+
def rationalize_base_units!(*units)
|
170
|
+
@base_units.rationalize!(*units)
|
171
|
+
initialize_attributes
|
292
172
|
end
|
293
173
|
|
294
|
-
#
|
295
|
-
#
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
299
|
-
# Symbol format use unit symbols, with numerator symbols followed by
|
300
|
-
# denominator symbols and powers expressed using the "^" notation with 'true'
|
301
|
-
# values (i.e. preservation of minus signs).
|
174
|
+
# Similar to #rationalize_base_units! but operates on the numerator and
|
175
|
+
# denomiator are separately. This means that different units representing
|
176
|
+
# the same physical quantity may remain across the numerator and
|
177
|
+
# denominator units.
|
302
178
|
#
|
303
|
-
def
|
304
|
-
@base_units.
|
305
|
-
|
306
|
-
end.inject('') do |symbol,base|
|
307
|
-
symbol << base.symbol + " "
|
308
|
-
end.strip
|
179
|
+
def rationalize_numerator_and_denominator_units!(*units)
|
180
|
+
@base_units.rationalize_numerator_and_denominator!(*units)
|
181
|
+
initialize_attributes
|
309
182
|
end
|
310
183
|
|
311
|
-
|
312
|
-
# JScience library in using a middot notation ("·") to spearate units and
|
313
|
-
# slash notation "/" to separate numerator and denominator. Since the
|
314
|
-
# denominator is differentiated, denominator unit powers are rendered in
|
315
|
-
# absolute terms (i.e. minus sign omitted) except when no numerator values
|
316
|
-
# are present.
|
317
|
-
#
|
318
|
-
def derive_label
|
319
|
-
unit_label = ""
|
320
|
-
unless numerator_units.empty?
|
321
|
-
numerator_units.inject(unit_label) do |label,base|
|
322
|
-
label << "·" unless unit_label.empty?
|
323
|
-
label << base.label
|
324
|
-
end
|
325
|
-
end
|
184
|
+
private
|
326
185
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
end
|
335
|
-
return unit_label
|
186
|
+
def initialize_attributes
|
187
|
+
self.dimensions = @base_units.dimensions
|
188
|
+
self.name = @base_units.name
|
189
|
+
self.symbol = @base_units.symbol
|
190
|
+
self.factor = @base_units.factor
|
191
|
+
self.label = @base_units.label
|
192
|
+
return self
|
336
193
|
end
|
337
194
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
195
|
+
def options_for_prefixed_version(prefix)
|
196
|
+
raise Exceptions::InvalidArgumentError, "No valid prefixes exist for unit: #{self.name}" if has_multiple_base_units?
|
197
|
+
base = @base_units.first
|
198
|
+
base.unit = base.unit.with_prefix(prefix)
|
199
|
+
return base
|
200
|
+
end
|
201
|
+
|
202
|
+
def initialize_copy(source)
|
203
|
+
super
|
204
|
+
instance_variable_set("@base_units", @base_units.clone)
|
345
205
|
end
|
346
206
|
|
347
207
|
end
|