eymiha_units 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. data/gem_package.rb +4 -4
  2. data/html/classes/Eymiha/AmbiguousUnitsException.html +119 -0
  3. data/html/classes/Eymiha/MissingUnitsException.html +119 -0
  4. data/html/classes/Eymiha/NumericWithUnits.html +867 -0
  5. data/html/classes/Eymiha/NumericWithUnits.src/M000021.html +18 -0
  6. data/html/classes/Eymiha/NumericWithUnits.src/M000022.html +18 -0
  7. data/html/classes/Eymiha/NumericWithUnits.src/M000023.html +18 -0
  8. data/html/classes/Eymiha/NumericWithUnits.src/M000024.html +30 -0
  9. data/html/classes/Eymiha/NumericWithUnits.src/M000025.html +30 -0
  10. data/html/classes/Eymiha/NumericWithUnits.src/M000027.html +18 -0
  11. data/html/classes/Eymiha/NumericWithUnits.src/M000028.html +20 -0
  12. data/html/classes/Eymiha/NumericWithUnits.src/M000029.html +32 -0
  13. data/html/classes/Eymiha/NumericWithUnits.src/M000030.html +18 -0
  14. data/html/classes/Eymiha/NumericWithUnits.src/M000031.html +30 -0
  15. data/html/classes/Eymiha/NumericWithUnits.src/M000032.html +30 -0
  16. data/html/classes/Eymiha/NumericWithUnits.src/M000033.html +22 -0
  17. data/html/classes/Eymiha/NumericWithUnits.src/M000034.html +22 -0
  18. data/html/classes/Eymiha/NumericWithUnits.src/M000035.html +32 -0
  19. data/html/classes/Eymiha/NumericWithUnits.src/M000036.html +32 -0
  20. data/html/classes/Eymiha/NumericWithUnits.src/M000037.html +18 -0
  21. data/html/classes/Eymiha/NumericWithUnits.src/M000038.html +18 -0
  22. data/html/classes/Eymiha/NumericWithUnits.src/M000039.html +24 -0
  23. data/html/classes/Eymiha/NumericWithUnits.src/M000040.html +26 -0
  24. data/html/classes/Eymiha/NumericWithUnits.src/M000041.html +18 -0
  25. data/html/classes/Eymiha/NumericWithUnits.src/M000042.html +18 -0
  26. data/html/classes/Eymiha/NumericWithUnits.src/M000043.html +18 -0
  27. data/html/classes/Eymiha/NumericWithUnits.src/M000044.html +20 -0
  28. data/html/classes/Eymiha/NumericWithUnits.src/M000045.html +18 -0
  29. data/html/classes/Eymiha/NumericWithUnits.src/M000046.html +25 -0
  30. data/html/classes/Eymiha/NumericWithUnits.src/M000047.html +18 -0
  31. data/html/classes/Eymiha/NumericWithUnits.src/M000048.html +19 -0
  32. data/html/classes/Eymiha/Units.html +378 -0
  33. data/html/classes/Eymiha/Units.src/M000052.html +18 -0
  34. data/html/classes/Eymiha/Units.src/M000053.html +18 -0
  35. data/html/classes/Eymiha/Units.src/M000054.html +20 -0
  36. data/html/classes/Eymiha/Units.src/M000055.html +30 -0
  37. data/html/classes/Eymiha/Units.src/M000056.html +33 -0
  38. data/html/classes/Eymiha/Units.src/M000057.html +18 -0
  39. data/html/classes/Eymiha/Units.src/M000058.html +21 -0
  40. data/html/classes/Eymiha/Units.src/M000059.html +18 -0
  41. data/html/classes/Eymiha/Units.src/M000060.html +18 -0
  42. data/html/classes/Eymiha/Units.src/M000061.html +18 -0
  43. data/html/classes/Eymiha/Units.src/M000062.html +18 -0
  44. data/html/classes/Eymiha/Units.src/M000063.html +18 -0
  45. data/html/classes/Eymiha/Units.src/M000064.html +28 -0
  46. data/html/classes/Eymiha/UnitsException.html +119 -0
  47. data/html/classes/Eymiha/UnitsHash.html +351 -0
  48. data/html/classes/Eymiha/UnitsHash.src/M000002.html +18 -0
  49. data/html/classes/Eymiha/UnitsHash.src/M000003.html +26 -0
  50. data/html/classes/Eymiha/UnitsHash.src/M000004.html +25 -0
  51. data/html/classes/Eymiha/UnitsHash.src/M000005.html +18 -0
  52. data/html/classes/Eymiha/UnitsHash.src/M000006.html +18 -0
  53. data/html/classes/Eymiha/UnitsHash.src/M000007.html +18 -0
  54. data/html/classes/Eymiha/UnitsHash.src/M000008.html +22 -0
  55. data/html/classes/Eymiha/UnitsHash.src/M000010.html +21 -0
  56. data/html/classes/Eymiha/UnitsHash.src/M000011.html +19 -0
  57. data/html/classes/Eymiha/UnitsHash.src/M000012.html +27 -0
  58. data/html/classes/Eymiha/UnitsHash.src/M000013.html +18 -0
  59. data/html/classes/Eymiha/UnitsMeasure.html +302 -0
  60. data/html/classes/Eymiha/UnitsMeasure.src/M000014.html +23 -0
  61. data/html/classes/Eymiha/UnitsMeasure.src/M000015.html +18 -0
  62. data/html/classes/Eymiha/UnitsMeasure.src/M000016.html +18 -0
  63. data/html/classes/Eymiha/UnitsMeasure.src/M000017.html +18 -0
  64. data/html/classes/Eymiha/UnitsMeasure.src/M000018.html +18 -0
  65. data/html/classes/Eymiha/UnitsMeasure.src/M000019.html +18 -0
  66. data/html/classes/Eymiha/UnitsMeasure.src/M000020.html +18 -0
  67. data/html/classes/Eymiha/UnitsSystem.html +196 -0
  68. data/html/classes/Eymiha/UnitsSystem.src/M000050.html +31 -0
  69. data/html/classes/Eymiha/UnitsSystem.src/M000051.html +18 -0
  70. data/html/classes/Eymiha/UnitsUnit.html +166 -0
  71. data/html/classes/Eymiha/UnitsUnit.src/M000049.html +18 -0
  72. data/html/classes/Eymiha.html +143 -0
  73. data/html/classes/Numeric.html +182 -0
  74. data/html/classes/Numeric.src/M000001.html +50 -0
  75. data/html/created.rid +1 -0
  76. data/html/files/lib/eymiha/units/definitions/area_rb.html +101 -0
  77. data/html/files/lib/eymiha/units/definitions/length_rb.html +101 -0
  78. data/html/files/lib/eymiha/units/definitions/mass_rb.html +101 -0
  79. data/html/files/lib/eymiha/units/definitions/measures_rb.html +113 -0
  80. data/html/files/lib/eymiha/units/definitions/time_rb.html +101 -0
  81. data/html/files/lib/eymiha/units/definitions/velocity_rb.html +101 -0
  82. data/html/files/lib/eymiha/units/definitions/volume_rb.html +101 -0
  83. data/html/files/lib/eymiha/units/numeric_rb.html +116 -0
  84. data/html/files/lib/eymiha/units/numeric_with_units_rb.html +109 -0
  85. data/html/files/lib/eymiha/units/object_rb.html +108 -0
  86. data/html/files/lib/eymiha/units/units_exception_rb.html +101 -0
  87. data/html/files/lib/eymiha/units/units_hash_rb.html +108 -0
  88. data/html/files/lib/eymiha/units/units_measure_rb.html +109 -0
  89. data/html/files/lib/eymiha/units/units_rb.html +115 -0
  90. data/html/files/lib/eymiha/units/units_system_rb.html +109 -0
  91. data/html/files/lib/eymiha/units/units_unit_rb.html +109 -0
  92. data/html/files/lib/eymiha/units_rb.html +115 -0
  93. data/html/fr_class_index.html +37 -0
  94. data/html/fr_file_index.html +43 -0
  95. data/html/fr_method_index.html +90 -0
  96. data/html/index.html +24 -0
  97. data/html/rdoc-style.css +208 -0
  98. data/lib/{units → eymiha/units}/definitions/area.rb +0 -0
  99. data/lib/{units → eymiha/units}/definitions/length.rb +0 -0
  100. data/lib/{units → eymiha/units}/definitions/mass.rb +0 -0
  101. data/lib/eymiha/units/definitions/measures.rb +6 -0
  102. data/lib/{units → eymiha/units}/definitions/time.rb +0 -0
  103. data/lib/{units → eymiha/units}/definitions/velocity.rb +0 -0
  104. data/lib/{units → eymiha/units}/definitions/volume.rb +0 -0
  105. data/lib/{units → eymiha/units}/numeric.rb +4 -3
  106. data/lib/eymiha/units/numeric_with_units.rb +640 -0
  107. data/lib/{units → eymiha/units}/object.rb +1 -1
  108. data/lib/eymiha/units/units.rb +234 -0
  109. data/lib/eymiha/units/units_exception.rb +18 -0
  110. data/lib/eymiha/units/units_hash.rb +118 -0
  111. data/lib/eymiha/units/units_measure.rb +95 -0
  112. data/lib/eymiha/units/units_system.rb +89 -0
  113. data/lib/eymiha/units/units_unit.rb +63 -0
  114. data/lib/eymiha/units.rb +4 -0
  115. data/test/tc_definitions.rb +1 -1
  116. data/test/tc_formatting.rb +2 -2
  117. data/test/tc_formatting_derived.rb +2 -2
  118. data/test/tc_measure_create.rb +2 -2
  119. data/test/tc_measure_derive.rb +2 -2
  120. data/test/tc_system_create.rb +2 -2
  121. data/test/tc_unit_ambiguity.rb +2 -2
  122. data/test/tc_unit_arithmetic.rb +2 -2
  123. data/test/tc_unit_create.rb +2 -2
  124. data/test/tc_unit_derive.rb +2 -2
  125. data/test/tc_unit_equality.rb +2 -2
  126. data/test/tc_unit_forward_reference.rb +2 -2
  127. data/test/tc_unit_greek.rb +2 -2
  128. data/test/tc_unit_hash.rb +2 -2
  129. data/test/tc_unit_identifiers.rb +2 -2
  130. data/test/tc_unit_rank.rb +2 -2
  131. data/test/tc_uses_1.rb +2 -2
  132. data/test/tc_uses_2.rb +2 -2
  133. metadata +190 -86
  134. data/lib/units/definitions/measures.rb +0 -6
  135. data/lib/units/numeric_with_units.rb +0 -635
  136. data/lib/units/units.rb +0 -229
  137. data/lib/units/units_exception.rb +0 -14
  138. data/lib/units/units_hash.rb +0 -112
  139. data/lib/units/units_measure.rb +0 -91
  140. data/lib/units/units_system.rb +0 -85
  141. data/lib/units/units_unit.rb +0 -60
  142. data/lib/units.rb +0 -4
@@ -0,0 +1,640 @@
1
+ require 'eymiha/units/units'
2
+ require 'set'
3
+
4
+ module Eymiha
5
+
6
+ # A NumericWithUnits is the intersection of a Numeric and a UnitsHash.
7
+ #
8
+ # Unit-sensitive coding is made much easier using an object that transparently
9
+ # adds units to a common everyday Numerics. Everything that can be done to a
10
+ # Numeric is still there, but explicit and implicit unit conversions are
11
+ # present in all of the operations.
12
+ #
13
+ # With this, numbers with units can be created easily. For example,
14
+ #
15
+ # 2.feet # a length of 2 feet
16
+ # 5.inches^2 # an area of 5 square inches
17
+ # 44.5.ft/sec # a velocity of 44.5 feet per second
18
+ #
19
+ # This should provide a good starting point for using the Units framework.
20
+ # Also pay attention to the examples given in the method documentation; some
21
+ # of the dynamic features of the framework are exposed in them.
22
+ class NumericWithUnits
23
+
24
+ include Comparable
25
+
26
+ @@debug = false
27
+
28
+ def self.debug=(value)
29
+ @@debug = value
30
+ end
31
+
32
+ # A Numeric containing the numeric part of the instance
33
+ attr_accessor :numeric
34
+ # A UnitsHash containing the units part of the instance
35
+ attr_accessor :unit
36
+ attr_accessor :original # :nodoc:
37
+
38
+ # Returns a new NumericWithUnits instance whose numeric part is set to
39
+ # numeric and whose units part is set to a units hash for the unit raised
40
+ # to the provided power.
41
+ def initialize(numeric,unit,power=1)
42
+ @numeric, @unit = numeric, units_hash(unit)**power
43
+ end
44
+
45
+ def units_hash(unit) # :nodoc:
46
+ (unit.kind_of? UnitsHash) ? unit : UnitsHash.new(unit)
47
+ end
48
+
49
+ # Returns a String representation of the instance, using the named format
50
+ # if provided.
51
+ #
52
+ # 15.5.minutes.to_s # 15.5 minutes
53
+ # 15.5.minutes.to_seconds.to_s # 930.0 seconds
54
+ # 15.5.minutes.in_seconds.to_s # 930.0 seconds
55
+ # 15.5.minutes.seconds.to_s # 930.0 seconds
56
+ # (15.5.minutes+1).seconds.to_s # 990.0 seconds
57
+ # (15.5.minutes.seconds+1).to_s # 931.0 seconds
58
+ # 10.feet_per_minute.to_s # 10 ft / min
59
+ # seconds_per_hour.to_s # 3600.0
60
+ # 14.5.inches.to_s(:feet_inches_and_32s) # "1 foot 2-16/32 inches"
61
+ def to_s(format = nil)
62
+ format == nil ? "#{numeric} #{unit.to_s(numeric)}" : self.format(format)
63
+ end
64
+
65
+ def promote_original # :nodoc:
66
+ @numeric, @unit = original.numeric, original.unit
67
+ end
68
+
69
+ # Compares the numeric and units parts of the instance with the value. If
70
+ # the value is a Numeric, the units are assumed to match. If the
71
+ # UnitsMeasures don't match, a UnitsException is raised.
72
+ #
73
+ # 2.ft <=> 1.yd # -1
74
+ # 3.ft <=> 1.yd # 0
75
+ # 4.ft <=> 1.yd # 1
76
+ # 4.ft <=> 3.5 # 1
77
+ # 4.ft <=> 2.minutes # UnitsException
78
+ def <=>(value)
79
+ if derived?
80
+ reduce <=> value
81
+ elsif value.kind_of? NumericWithUnits
82
+ if value.derived?
83
+ self <=> value.reduce
84
+ else
85
+ align(value).numeric <=> value.numeric
86
+ end
87
+ elsif value.kind_of? Numeric
88
+ numeric <=> value
89
+ else
90
+ raise UnitsException.new("units mismatch")
91
+ end
92
+ end
93
+
94
+ # Returns true if the value and the instance are within a distance epsilon
95
+ # of each other. If the value is a Numeric, the units are assumed to match.
96
+ # If the UnitsMeasures don't match, a UnitsException is raised.
97
+ #
98
+ # 1.ft.approximately_equals? 0.33.yd # false
99
+ # 1.ft.approximately_equals? 0.333333.yd # true
100
+ def approximately_equals?(value,epsilon=Numeric.epsilon)
101
+ if derived?
102
+ reduce.approximately_equals?(value,epsilon)
103
+ elsif value.kind_of? NumericWithUnits
104
+ if value.derived?
105
+ approximately_equals?(value.reduce,epsilon)
106
+ else
107
+ align(value).numeric.approximately_equals?(value.numeric,epsilon)
108
+ end
109
+ elsif value.kind_of? Numeric
110
+ numeric.approximately_equals?(value,epsilon)
111
+ else
112
+ raise UnitsException.new("units mismatch")
113
+ end
114
+ end
115
+
116
+ alias :=~ :approximately_equals?
117
+
118
+ # Unary plus returns a copy of the instance.
119
+ def +@
120
+ clone
121
+ end
122
+
123
+ # Unary minus returns a copy of the instance with its numeric part negated.
124
+ def -@
125
+ value = clone
126
+ value.numeric = -(value.numeric)
127
+ value
128
+ end
129
+
130
+ # Returns a new NumericWithUnits containing the sum of the instance and
131
+ # the value. If the value is a Numeric, the units are assumed to match.
132
+ # If the UnitsMeasures don't match, a UnitsException is raised.
133
+ #
134
+ # 6.inches + 1.foot # 18 inches
135
+ # 6.inches + 1 # 7 inches
136
+ # 6.inches + 1.sec # UnitsException
137
+ def +(value)
138
+ if derived?
139
+ reduce+value
140
+ elsif value.kind_of? NumericWithUnits
141
+ if value.derived?
142
+ self+value.reduce
143
+ else
144
+ aligned_value = align(value)
145
+ aligned_value.numeric += value.numeric
146
+ aligned_value
147
+ end
148
+ elsif value.kind_of? Numeric
149
+ NumericWithUnits.new(numeric+value,unit)
150
+ else
151
+ raise UnitsException.new("units mismatch")
152
+ end
153
+ end
154
+
155
+ # Returns a new NumericWithUnits containing the difference between the
156
+ # instance and the value. If the value is a Numeric, the units are assumed
157
+ # to match. If the UnitsMeasures don't match, a UnitsException is raised.
158
+ #
159
+ # 6.inches - 1.foot # -6 inches
160
+ # 6.inches - 1 # 5 inches
161
+ # 6.inches - 1.sec # UnitsException
162
+ def -(value)
163
+ self + (-value)
164
+ end
165
+
166
+ # Returns a new NumericWithUnits containing the product of the instance and
167
+ # the value. The units of the two factors are merged. If the value is not a
168
+ # Numeric nor NumericWithUnits, a UnitsException is thrown.
169
+ #
170
+ # 6.in * 2.ft # 1 foot^2
171
+ # 6.in * 2 # 12 inches
172
+ # 6.in * 2.sec # 12 ft sec
173
+ # 6.in * "hello" # UnitsException
174
+ def *(value)
175
+ if derived?
176
+ reduce*value
177
+ elsif value.kind_of? NumericWithUnits
178
+ if value.derived?
179
+ self*value.reduce
180
+ else
181
+ extend(value,1)
182
+ end
183
+ elsif value.kind_of? Numeric
184
+ NumericWithUnits.new(numeric*value,unit)
185
+ else
186
+ raise UnitsException.new("units mismatch")
187
+ end
188
+ end
189
+
190
+ # Returns a new NumericWithUnits containing the value of the instance
191
+ # divided by the value. The units of the result are the units of the
192
+ # instance merged with the recipricol of the units of the value. If the
193
+ # value is neither a Numeric nor NumericWithUnits, a UnitsException is
194
+ # thrown.
195
+ #
196
+ # 6.in / 2.ft # 0.25
197
+ # 6.in / 2 # 3 inches
198
+ # 6.in / 2.sec # 3 ft / sec
199
+ # 6.in / "hello" # UnitsException
200
+ def /(value)
201
+ if derived?
202
+ reduce/value
203
+ elsif value.kind_of? NumericWithUnits
204
+ if value.derived?
205
+ self/value.reduce
206
+ else
207
+ extend(value,-1)
208
+ end
209
+ elsif value.kind_of? Numeric
210
+ NumericWithUnits.new(numeric/value,unit)
211
+ else
212
+ raise UnitsException.new("units mismatch")
213
+ end
214
+ end
215
+
216
+ # Returns a new NumericWithUnits containing the numeric and units parts of
217
+ # the instance both raised to the valueth power. If the value is not
218
+ # a Numeric, a UnitsException is thrown.
219
+ #
220
+ # 6.in ** 3 # 216 in^3
221
+ # 6.in ** 3.in # UnitsException
222
+ def **(value)
223
+ if (value.kind_of? Numeric) && !(value.kind_of? NumericWithUnits)
224
+ extend(nil,value)
225
+ else
226
+ raise UnitsException.new("units mismatch")
227
+ end
228
+ end
229
+
230
+ # Returns a new NumericWithUnits containing the numeric and units parts of
231
+ # the instance with just the units raised to the valueth power. If the value
232
+ # is not a Numeric, a UnitsException is thrown.
233
+ #
234
+ # 6.in ^ 3 # 6 in^3
235
+ # 6.in ^ 3.in # UnitsException
236
+ def ^(value)
237
+ if (value.kind_of? Numeric) && !(value.kind_of? NumericWithUnits)
238
+ NumericWithUnits.new(numeric,unit,value)
239
+ else
240
+ raise UnitsException.new("units mismatch")
241
+ end
242
+ end
243
+
244
+ # Returns a new NumericWithUnits containing the numeric part of the instance
245
+ # modulo the numeric part of the value, with the units part equal to the
246
+ # instance's units part. If the value is a Numeric, the units are assumed
247
+ # to match. If the UnitsMeasures don't match, a UnitsException is raised.
248
+ #
249
+ # 30.in % 2.ft # 0.5 feet
250
+ # 5.in % 2.3 # 0.4 inches
251
+ # 5.in % 2.sec # UnitsException
252
+ def %(value)
253
+ if derived?
254
+ reduce%value
255
+ elsif value.kind_of? NumericWithUnits
256
+ if value.derived?
257
+ self%value.reduce
258
+ else
259
+ aligned_value = align(value)
260
+ aligned_value.numeric = aligned_value.numeric % value.numeric
261
+ aligned_value
262
+ end
263
+ elsif value.kind_of? Numeric
264
+ NumericWithUnits.new(numeric % value,unit)
265
+ else
266
+ raise UnitsException.new("units mismatch")
267
+ end
268
+ end
269
+
270
+ # Returns a new NumericWithUnits containing the numeric part of the value
271
+ # modulo the numeric part of the instance, with the units part equal to the
272
+ # instance's units part. If the value is a Numeric, the units are assumed
273
+ # to match. If the UnitsMeasures don't match, a UnitsException is raised.
274
+ #
275
+ # 30.in % 2.ft # 24 inches
276
+ # 5.in % 2.3 # 2.3 inches
277
+ # 5.in % 2.sec # UnitsException
278
+ def inv_mod(value)
279
+ if derived?
280
+ reduce.inv_mod value
281
+ elsif value.kind_of? NumericWithUnits
282
+ if value.derived?
283
+ self.inv_mod value.reduce
284
+ else
285
+ aligned_value = align(value)
286
+ aligned_value.numeric = value.numeric % aligned_value.numeric
287
+ aligned_value
288
+ end
289
+ elsif value.kind_of? Numeric
290
+ NumericWithUnits.new(value % numeric,unit)
291
+ else
292
+ raise UnitsException.new("units mismatch")
293
+ end
294
+ end
295
+
296
+ def can_align(target,exact=true) # :nodoc:
297
+ ru, tru = reduce.unit, target.reduce.unit
298
+ !exact || (ru == tru)
299
+ end
300
+
301
+ def reduce_power(p,f,type) # :nodoc:
302
+ if type == :whole_powers
303
+ pa = p.abs
304
+ (p == 0) ? [p,f,0] : (pa < f) ? [p, f, pa/p] : [p, f, p/f]
305
+ else
306
+ (p == 0) ? [p,f,0] : [p, f, (1.0*p)/f]
307
+ end
308
+ end
309
+
310
+ def common_power(ps) # :nodoc:
311
+ ps.values.collect {|a| a[2]}.inject {|p,e| p.abs < e.abs ? p : e}
312
+ end
313
+
314
+ def revise_power(rp,p) # :nodoc:
315
+ [rp[0], rp[1], p, rp[0]-p*rp[1]]
316
+ end
317
+
318
+ # Sets and returns the type of exponentiation merging during alignment.
319
+ def self.derived_align_type=(type)
320
+ @@derived_align_type = type
321
+ end
322
+
323
+ # Returns the type of exponentiation merging during alignment.
324
+ # * :whole_powers require exponents to have integer values
325
+ # * :fractional_powers allow exponents to have rational values
326
+ def self.derived_align_type
327
+ @@derived_align_type
328
+ end
329
+
330
+ @@derived_align_type = :whole_powers
331
+
332
+ # Returns a new NumericWithUnits whose value is equivalent to that of the
333
+ # instance, but whose units are aligned to the target, according to the
334
+ # value of derived_align_type. If all is true, then the UnitsMeasure of the
335
+ # instance and the target must match exactly, or else a UnitsException is
336
+ # raised.
337
+ #
338
+ # (80.miles_per_hour).align(1.min,false) # 1.3333333 mi / min
339
+ def align(target,all=true,type=@@derived_align_type)
340
+ if target.kind_of? Array
341
+ piece_align(target)
342
+ elsif !derived? && !target.derived?
343
+ simple_align(target,all)
344
+ else
345
+ power_align(target,all,type)
346
+ end
347
+ end
348
+
349
+ def simple_align(target,all=true) # :nodoc:
350
+ puts "simple align #{self} #{target}" if @@debug
351
+ factor = 1
352
+ target_unit = UnitsHash.new
353
+ unit.each do |tu,tv|
354
+ su = target.unit.keys.select {|u|
355
+ u.units_measure.equal? tu.units_measure }
356
+ if tu.equals.kind_of? Array
357
+ m = tu.equals.select{|u| u.unit[su[0]]}
358
+ m = tu.equals.collect{|u|
359
+ u.align(1.unite(su[0]))}.compact if m.size == 0
360
+ factor *= (m[0].numeric)**tv
361
+ target_unit[su[0]] = tv
362
+ else
363
+ if su.size == 1
364
+ e = su[0].equals
365
+ if e.kind_of? Array
366
+ m = e.select{|u|
367
+ u.unit[tu]}
368
+ m = e.equals.collect{|u|
369
+ u.align(1.unite(su[0]))}.compact if m.size == 0
370
+ factor *= (1.0/m[0].numeric)**tv
371
+ else
372
+ factor *= (1.0*tu.equals.numeric/e.numeric)**tv
373
+ end
374
+ target_unit[su[0]] = tv
375
+ else
376
+ target_unit[tu] = tv
377
+ end
378
+ end
379
+ end
380
+ raise UnitsException.new("units mismatch") if
381
+ all && (target.unit != target_unit)
382
+ nwu = NumericWithUnits.new(numeric*factor,target_unit)
383
+ puts " simple_align returning #{nwu}" if @@debug
384
+ nwu
385
+ end
386
+
387
+ def power_align(target,all=true,type=@@derived_align_type) # :nodoc:
388
+ puts "power_align #{self} #{target}" if @@debug
389
+ raise UnitsException.new("units mismatch") unless can_align(target,all)
390
+ factor = 1
391
+ target_unit = UnitsHash.new
392
+ unit_reduce = reduce
393
+ puts " unit_reduce #{unit_reduce}" if @@debug
394
+ target.unit.each do |tu,tv|
395
+ t_u = {}
396
+ tt_u = {}
397
+ tu_reduce = tu.equals.reduce
398
+ found = tu_reduce.unit.each do |ttu,ttv|
399
+ su = unit_reduce.unit.keys.select {|u|
400
+ u.units_measure.equal? ttu.units_measure }
401
+ break false if su.size == 0
402
+ tt_u[ttu] = reduce_power(unit_reduce.unit[ttu],ttv,type)
403
+ end
404
+ if found
405
+ cp = common_power(tt_u)
406
+ puts " cp #{cp}" if @@debug
407
+ tt_u.each {|k,v| tt_u[k] = revise_power(v,cp)}
408
+ t_u[tu] = tv**cp
409
+ t_factor = tu_reduce.numeric**cp
410
+ puts " t_factor #{t_factor}" if @@debug
411
+ target_unit[tu] = cp
412
+ puts " tu.equals.numeric**cp #{tu.equals.numeric**cp}" if @@debug
413
+ factor *= t_factor/(tu.equals.numeric**cp)
414
+ puts " factor #{factor}" if @@debug
415
+ tt_u.each {|k,v|
416
+ unit_reduce.numeric /= t_factor
417
+ unit_reduce.unit[k] = v[3]}
418
+ end
419
+ end
420
+ puts " unit_reduce #{unit_reduce}" if @@debug
421
+ unit_redux = unit_reduce.simple_align(self,false)
422
+ puts " unit_redux #{unit_redux}" if @@debug
423
+ result_unit = target_unit.merge(unit_redux)
424
+ raise UnitsException.new("units mismatch") if
425
+ all && (target.unit != result_unit)
426
+ nwu = NumericWithUnits.new(factor*unit_redux.numeric,result_unit)
427
+ puts " power_align returning #{nwu}" if @@debug
428
+ nwu
429
+ end
430
+
431
+ def piece_align(pieces,type=@@derived_align_type) # :nodoc:
432
+ puts "piece_align #{self} #{pieces}" if @@debug
433
+ factor = 1
434
+ target_unit = UnitsHash.new
435
+ unit_reduce = reduce
436
+ pieces.each do |p|
437
+ p.unit.each do |tu,tv|
438
+ t_u = {}
439
+ tt_u = {}
440
+ tu_reduce = tu.equals.reduce
441
+ found = tu_reduce.unit.each do |ttu,ttv|
442
+ su = unit_reduce.unit.keys.select {|u|
443
+ u.units_measure.equal? ttu.units_measure }
444
+ break false if su.size == 0
445
+ tt_u[ttu] = reduce_power(unit_reduce.unit[ttu],ttv,type)
446
+ end
447
+ if found
448
+ tt_u.each {|k,v| tt_u[k] = revise_power(v,tv)}
449
+ t_u[tu] = tv
450
+ t_factor = tu_reduce.numeric**tv
451
+ target_unit[tu] = tv
452
+ factor *= t_factor/(tu.equals.numeric**tv)
453
+ tt_u.each {|k,v|
454
+ unit_reduce.numeric /= t_factor
455
+ unit_reduce.unit[k] = v[3]}
456
+ end
457
+ end
458
+ end
459
+ raise UnitsException.new("units mismatch") if unit_reduce.unit.has_units?
460
+ nwu = NumericWithUnits.new(factor*unit_reduce.numeric,target_unit)
461
+ puts " piece_align returning #{nwu}" if @@debug
462
+ nwu
463
+ end
464
+
465
+ # Returns a new NumericWithUnits whose value is extended by raising it to
466
+ # the power, or by multiplying it by units raised to the power.
467
+ #
468
+ # 7.miles.extend(nil,2) # 49 mi^2
469
+ # 5.feet.extend(10.ft,2) # 500 ft^3
470
+ def extend(units,power)
471
+ if !units
472
+ NumericWithUnits.new(numeric**power,unit**power)
473
+ else
474
+ value = align(units,false)
475
+ extended_numeric = value.numeric*(units.numeric**power)
476
+ extended_unit = value.unit.merge(units,power)
477
+ (extended_unit.size == 0)? extended_numeric :
478
+ NumericWithUnits.new(extended_numeric,extended_unit)
479
+ end
480
+ end
481
+
482
+ def NumericWithUnits.commutative_operator(op,old,calc) # :nodoc:
483
+ ([] <<
484
+ "alias :old_#{old} :#{op}" <<
485
+ "def #{op}(value)" <<
486
+ " (value.kind_of? NumericWithUnits) ? #{calc} : old_#{old}(value)" <<
487
+ "end").
488
+ join("\r\n")
489
+ end
490
+
491
+ def NumericWithUnits.create_commutative_operators(klasses) # :nodoc:
492
+ commutative_operators ||=
493
+ ([] <<
494
+ commutative_operator( "*", "multiply", "value * self" ) <<
495
+ commutative_operator( "/", "divide", "(value**-1) * self" ) <<
496
+ commutative_operator( "+", "add", "value + self" ) <<
497
+ commutative_operator( "-", "subtract", "(-value) + self" ) <<
498
+ commutative_operator( "%", "modulo", "value.inv_mod(self)" ) <<
499
+ commutative_operator( "<=>", "compare", "-(value <=> self)" ) <<
500
+ commutative_operator( ">", "gt", "(value < self)" ) <<
501
+ commutative_operator( "<", "lt", "(value > self)" ) <<
502
+ commutative_operator( ">=", "gteq", "(value <= self)" ) <<
503
+ commutative_operator( "<=", "lteq", "(value >= self)" ) <<
504
+ commutative_operator( "==", "eq", "(value == self)" ) <<
505
+ commutative_operator( "=~", "approxeq", "(value =~ self)" )).
506
+ join("\r\n")
507
+ klasses.each { |klass| klass.class_eval commutative_operators }
508
+ end
509
+
510
+ def method_missing(method,*args) # :nodoc:
511
+ begin
512
+ s = method.to_s
513
+ ms = s.split '_'
514
+ if ms[0] == 'to'
515
+ convert! s.gsub(/^to_/,"")
516
+ elsif ms[0] == 'in'
517
+ convert s.gsub(/^in_/,"")
518
+ elsif ms.select{|e| e == 'per'}.size > 0
519
+ convert_per method
520
+ else
521
+ convert s
522
+ end
523
+ rescue Exception
524
+ value = numeric.send(method,*args)
525
+ if (value.kind_of? String)
526
+ "#{value} #{unit.to_s(numeric)}"
527
+ else
528
+ NumericWithUnits.new(value,unit)
529
+ end
530
+ end
531
+ end
532
+
533
+ def convert_per method # :nodoc:
534
+ ps = method.to_s.split '_per_'
535
+ raise UnitsException.new('invalid per method') if ps.size != 2
536
+ numerator = 1.unite(ps[0])
537
+ numerator_units = Set.new numerator.unit.keys
538
+ denominator = 1.unite(ps[1])
539
+ denominator_units = Set.new denominator.unit.keys
540
+ positives = unit.keys.collect{|k| unit[k] > 0 ? k : nil}.compact!
541
+ negatives = unit.keys.collect{|k| unit[k] < 0 ? k : nil}.compact!
542
+ numerator_positives =
543
+ Set.new 1.unite(positives).align(numerator,false).unit.keys
544
+ numerator_negatives =
545
+ Set.new 1.unite(negatives).align(numerator,false).unit.keys
546
+ denominator_positives =
547
+ Set.new 1.unite(positives).align(denominator,false).unit.keys
548
+ denominator_negatives =
549
+ Set.new 1.unite(negatives).align(denominator,false).unit.keys
550
+ if (numerator_units == numerator_positives) &&
551
+ (denominator_units == denominator_negatives)
552
+ convert(ps[0]+'_and_'+ps[1])
553
+ elsif (numerator_units == numerator_negatives) &&
554
+ (denominator_units == denominator_positives)
555
+ convert(ps[0]+'_and_'+ps[1])**-1
556
+ else
557
+ raise UnitsException.new('invalid per units')
558
+ end
559
+ end
560
+
561
+ alias :old_kind_of? :kind_of?
562
+
563
+ # Returns true if the instance or it's numeric part is a kind of klass.
564
+ #
565
+ # 28.feet.kind_of? String # false
566
+ # 28.feet.kind_of? NumericWithUnits # true
567
+ # 28.feet.kind_of? Numeric # true
568
+ # 28.feet.kind_of? Integer # true
569
+ # 28.feet.kind_of? Float # false
570
+ # 28.0.feet.kind_of? Float # true
571
+ #
572
+ # Note while NumericWithUnits actually descends from Object, it acts as if
573
+ # it is inherited from the class of the numeric part of the instance, since
574
+ # it forwards any unknown method calls to it. In this way the duck really
575
+ # is a duck.
576
+ def kind_of?(klass)
577
+ (numeric.kind_of? klass)? true : old_kind_of?(klass)
578
+ end
579
+
580
+ # Returns a new NumericWithUnits whose numeric part is the target of the
581
+ # Numeric's unite method.
582
+ #
583
+ # 28.ft^2.unite("seconds") # 28 seconds
584
+ def unite(target_unit=nil,power=1,measure=nil)
585
+ numeric.unite(target_unit,power,measure)
586
+ end
587
+
588
+ # Returns a copy of the instance converted to the target_units. Note that
589
+ # the conversion is only with respect to the UnitsMeasures of the
590
+ # target_units - the remainder of the units will remain unconverted.
591
+ def convert(target_units=nil)
592
+ target_units ? align(1.unite(target_units),false) : clone
593
+ end
594
+
595
+ # Converts the instance itself.
596
+ def convert!(target_units=nil)
597
+ result = convert(target_units)
598
+ self.numeric, self.unit = result.numeric, result.unit
599
+ self
600
+ end
601
+
602
+ # Returns the UnitsMeasures in the units part of the instance.
603
+ def measure
604
+ unit.measure
605
+ end
606
+
607
+ # Returns a String formatted using the named format defined in the
608
+ # instance's measure. Raises a UnitsException if either the unit part of
609
+ # the instance has no defined UnitsMeasure or a format with the given name
610
+ # does not exist in that UnitsMeasure.
611
+ def format(name=nil)
612
+ if name == nil
613
+ to_s
614
+ else
615
+ raise UnitsException.new("system not explicit") if (measure == nil)
616
+ format = measure.formats[name]
617
+ raise UnitsException.new("missing format") if format == nil
618
+ format.call(self)
619
+ end
620
+ end
621
+
622
+ # Returns true if a component of unit part of the instance has a derived
623
+ # UnitsMeasure.
624
+ def derived?
625
+ unit.derived?
626
+ end
627
+
628
+ # Return a new NumericWithUnits that is equivalent to the instance but whose
629
+ # unit contains no derived UnitMeasures.
630
+ def reduce
631
+ puts "reduce unit.reduce #{unit.reduce}" if @@debug
632
+ numeric * unit.reduce
633
+ end
634
+
635
+ end
636
+
637
+
638
+ NumericWithUnits.create_commutative_operators [ Fixnum, Bignum, Float ]
639
+
640
+ end
@@ -1,4 +1,4 @@
1
- require 'units/units'
1
+ require 'eymiha/units/units'
2
2
 
3
3
  class Object # :nodoc:
4
4