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.
@@ -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.select { |base| base.is_numerator? }
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.select { |base| base.is_denominator? }
70
+ @base_units.denominator_units
117
71
  end
118
72
 
119
73
  # Convenient accessor method for pluralized names
120
74
  def pluralized_name
121
- derive_name :plural
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.all? { |base| base.is_si_unit? }
80
+ @base_units.is_si_unit?
128
81
  end
129
82
 
130
83
  def is_non_si_unit?
131
- @base_units.any? { |base| base.is_non_si_unit? }
84
+ @base_units.is_non_si_unit?
132
85
  end
133
86
 
134
87
  def is_base_quantity_si_unit?
135
- @base_units.all? { |base| base.is_base_quantity_si_unit? }
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
- # Cancel base units across numerator and denominator. If similar units occur
150
- # in both the numerator and denominator, they can be cancelled, i.e. their
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
- protected
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
- def initialize_attributes
233
- self.dimensions = derive_dimensions
234
- self.name = derive_name
235
- self.symbol = derive_symbol
236
- self.factor = derive_factor
237
- self.label = derive_label
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
- # Partially consolidate base units, i.e. numerator and denomiator are
241
- # consolidated separately. This means that two instances of the same unit
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
- new_base_units = []
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
- # Derive a representation of the physical dimensions of the compound unit
256
- # by multilying together the dimensions of each of the base units.
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
- # square metres per second
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 derive_name(inflection=:singular)
276
- unit_name = ""
277
- unless numerator_units.empty?
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
- # Derive a symbol for the unit based on the symbols of the base units
295
- #
296
- # Get the units in order first so that the denominator values (those
297
- # with negative powers) follow the numerators
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 derive_symbol
304
- @base_units.sort do |base,next_unit|
305
- next_unit.index <=> base.index
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
- # Derive a label for the comound unit. This follows the format used in the
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
- unless denominator_units.empty?
328
- format = ( unit_label.empty? ? :label : :reciprocalized_label )
329
- unit_label << "/" unless unit_label.empty?
330
- denominator_units.inject(unit_label) do |label,base|
331
- label << "·" unless unit_label.empty? || unit_label =~ /\/\z/
332
- label << base.send(format)
333
- end
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
- # Derive the multiplicative factor for the unit based on those of the base
339
- # units
340
- #
341
- def derive_factor
342
- @base_units.inject(1) do |factor,base|
343
- factor * base.factor
344
- end
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