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