quantify 1.1.0 → 1.2.0

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