phys-units 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,123 @@
1
+ # parse.y
2
+ #
3
+ # Copyright (c) 2001-2013 Masahiro Tanaka <masa16.tanaka@gmail.com>
4
+ #
5
+ # This program is free software.
6
+ # You can distribute/modify this program under the terms of
7
+ # the GNU General Public License version 3 or later.
8
+
9
+ class Parse
10
+
11
+ prechigh
12
+ left '|'
13
+ right POW
14
+ nonassoc '(' WORD NUMBER UFUNC
15
+ left '*' MULTIPLY
16
+ left DIV
17
+ left UNARY
18
+ left '+' '-'
19
+ preclow
20
+
21
+ rule
22
+
23
+ target: expr
24
+ | DIV list { result = Unit.inv(val[1]) }
25
+ ;
26
+
27
+ expr: list
28
+ | '-' list = UNARY { result = -val[1] }
29
+ | expr '+' expr { result = val[0] + val[2] }
30
+ | expr '-' expr { result = val[0] - val[2] }
31
+ | expr '*' expr { result = val[0] * val[2] }
32
+ | expr DIV expr { result = val[0] / val[2] }
33
+ ;
34
+
35
+ numexpr: NUMBER
36
+ | numexpr '|' numexpr { result = Unit.rdiv(val[0],val[2]) }
37
+ ;
38
+
39
+ pexpr: '(' expr ')' { result = val[1] }
40
+ ;
41
+
42
+ list: numexpr
43
+ | pexpr
44
+ | WORD { result = Unit.word(val[0]) }
45
+ | list list = MULTIPLY { result = val[0] * val[1] }
46
+ | list POW list { result = val[0]** val[2] }
47
+ | list POW '-' list = POW { result = val[0]**(-val[3]) }
48
+ | UFUNC pexpr { result = Unit.func(val[0],val[1]) }
49
+ ;
50
+
51
+ end
52
+
53
+ ---- header ----
54
+
55
+ # -*- coding: utf-8 -*-
56
+ # parse.y, parse.rb
57
+ #
58
+ # by Masahiro Tanaka <masa16.tanaka@gmail.com>
59
+ #
60
+ module Phys
61
+ class Unit
62
+ ---- inner ----
63
+
64
+ def build_num(ov,ud,pw)
65
+ if ud.nil? && pw.nil?
66
+ #ov.to_i
67
+ Rational(ov.to_i)
68
+ else
69
+ m1 = ud ? ud.size : 0
70
+ pw = pw ? pw.to_i : 0
71
+ m2 = pw-m1
72
+ ov = ov ? ov.to_i : 0
73
+ ud = ud ? ud.to_i : 0
74
+ a = ov*10**m1 + ud
75
+ b = 1
76
+ a *= 10**m2 if m2>0
77
+ b *= 10**(-m2) if m2<0
78
+ Rational(a,b)
79
+ end
80
+ end
81
+
82
+ def parse( str )
83
+ return Unit.new(str) if str.empty?
84
+ #p str
85
+ @q = []
86
+
87
+ c = Unit.unit_chars
88
+
89
+ while str.size > 0 do
90
+ case str
91
+ when /\A[\s]+/o
92
+ when /\A(\d+)(?:(?:\.(\d*))?(?:[eE]([+-]?\d+))?)/o
93
+ @q.push [:NUMBER, build_num($1,$2,$3)]
94
+ when /\A(sin|cos|tan|log|ln|log2)\b/o
95
+ @q.push [:UFUNC, $&]
96
+ when /\A\//o
97
+ @q.push [:DIV, $&]
98
+ when /\Aper\b/o
99
+ @q.push [:DIV, $&]
100
+ when /\A[^#{c}0-9,.-]+([^#{c}$-]*[^#{c}1-9,.])?/o
101
+ @q.push [:WORD, $&]
102
+ when /\A[%'"]'?/o
103
+ @q.push [:WORD, $&]
104
+ when /\A\^|\A\*\*/o
105
+ @q.push [:POW, $&]
106
+ when /\A./o
107
+ @q.push [$&,$&]
108
+ end
109
+ str = $' #'
110
+ end
111
+ @q.push [false, '$end']
112
+
113
+ do_parse
114
+ end
115
+
116
+ def next_token
117
+ @q.shift
118
+ end
119
+
120
+ ---- footer ----
121
+
122
+ end
123
+ end
@@ -0,0 +1,252 @@
1
+ #
2
+ # phys/units/quantity.rb
3
+ #
4
+ # Copyright (c) 2001-2013 Masahiro Tanaka <masa16.tanaka@gmail.com>
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU General Public License version 3 or later.
9
+
10
+ module Phys
11
+
12
+ def Quantity(*a)
13
+ Quantity.new(*a)
14
+ end
15
+
16
+ #== Usage
17
+ # require 'phys/units'
18
+ # Q=Phys::Quantity
19
+ # Q[1.23,'km'] + Q[4.56,'m'] #=> Phys::Quanty[1.23456,'km']
20
+ # Q[123,'mile'] / Q[2,'hr'] #=> Phys::Quanty[61,'mile/hr']
21
+ # Q[61,'miles/hr'].want('m/s') #=> Phys::Quanty[27.26944,'m/s']
22
+ # Q[1.0,'are'] == Q[10,'m']**2 #=> true
23
+ # Q[70,'tempF'] + Q[10,'tempC'] #=> Phys::Quantity[88,'tempF']
24
+ # Q[20,'tempC'].want('tempF') #=> Phys::Quantity[68,'tempF']
25
+ # Math.cos(Q[60,'degree'].to_f) #=> 0.5
26
+ class Quantity
27
+
28
+ class << self
29
+ # Same as Quantity.new.
30
+ def [](*a)
31
+ self.new(*a)
32
+ end
33
+ end
34
+
35
+ # Initialize a new quantity.
36
+ # _value_: Numeric value of quantity.
37
+ # _expr_: Unit string. Result of Unit.parse(_expr_) is used as a unit.
38
+ # _unit_: (optional) Unit of quantity instead of parsing _expr_.
39
+ def initialize(value,expr=nil,unit=nil)
40
+ @val = value
41
+ expr = expr.to_s if Symbol===expr
42
+ @expr = (expr=='') ? nil : expr
43
+ @unit = unit
44
+ if @unit.nil?
45
+ @unit = Unit.parse(@expr||1)
46
+ elsif !@unit.kind_of? Unit
47
+ raise ArgumentError, "third arg must be Phys::Unit"
48
+ end
49
+ end
50
+
51
+ attr_reader :val
52
+ attr_reader :expr
53
+ attr_reader :unit
54
+
55
+ # Returns the value of the quantity.
56
+ alias value val
57
+
58
+ # Conversion to a quantity in another _expr_ unit.
59
+ def want(expr)
60
+ unit = Unit.parse(expr)
61
+ val = unit.convert(self)
62
+ self.class.new( val, expr, unit )
63
+ end
64
+ alias convert want
65
+
66
+ # Addition of two quantities.
67
+ # Operation is made after the unit of _other_ is
68
+ # converted to the unit of _self_.
69
+ # Exception is raised if unit conversion is failed.
70
+ # Returns an instance of Quantity class in the unit of former quantity.
71
+ def +(other)
72
+ val = @val + @unit.convert_scale(other)
73
+ self.class.new( val, @expr, @unit )
74
+ end
75
+
76
+ # Subtraction of two quantities.
77
+ # Operation is made after the unit of _other_ is
78
+ # converted to the unit of _self_.
79
+ # Exception is raised if unit conversion is failed.
80
+ # Returns an instance of Quantity class in the unit of former quantity.
81
+ def -(other)
82
+ val = @val - @unit.convert_scale(other)
83
+ self.class.new( val, @expr, @unit )
84
+ end
85
+
86
+ %w[abs ceil round floor truncate].each do |s|
87
+ define_method(s) do
88
+ self.class.new( @val.send(s), @expr, @unit )
89
+ end
90
+ end
91
+
92
+ # Unary Plus. Returns self.
93
+ def +@ ; self.class.new( @val, @expr, @unit ) end
94
+
95
+ # Unary Minus. Returns the negated quantity.
96
+ def -@ ; self.class.new( -@val, @expr, @unit ) end
97
+
98
+ # Comparison of quantities. Comparison is made after
99
+ # converting _other_ to a quantity in the unit of _self_.
100
+ def <=> (other); @val <=> @unit.convert(other) end
101
+
102
+ # Comparison of quantities. Comparison is made after
103
+ # converting _other_ to a quantity in the unit of _self_.
104
+ def == (other); @val == @unit.convert(other) end
105
+
106
+ # Comparison of quantities. Comparison is made after
107
+ # converting _other_ to a quantity in the unit of _self_.
108
+ def >= (other); @val >= @unit.convert(other) end
109
+
110
+ # Comparison of quantities. Comparison is made after
111
+ # converting _other_ to a quantity in the unit of _self_.
112
+ def <= (other); @val <= @unit.convert(other) end
113
+
114
+ # Comparison of quantities. Comparison is made after
115
+ # converting _other_ to a quantity in the unit of _self_.
116
+ def < (other); @val < @unit.convert(other) end
117
+
118
+ # Comparison of quantities. Comparison is made after
119
+ # converting _other_ to a quantity in the unit of _self_.
120
+ def > (other); @val > @unit.convert(other) end
121
+
122
+ # Power of a quantity.
123
+ # Returns an instance of Quantity class in a powerd unit.
124
+ def **(n)
125
+ if @expr.nil?
126
+ expr = nil
127
+ elsif /^[A-Za-z_]+&/o =~ @expr
128
+ expr = @expr+'^'+n.to_s
129
+ else
130
+ expr = '('+@expr+')^'+n.to_s+''
131
+ end
132
+ self.class.new( @val**n, expr, @unit**n )
133
+ end
134
+
135
+ def enclose_expr #:nodoc:
136
+ return nil if @expr.nil?
137
+ if /\/|\||per/o =~ @expr
138
+ '('+@expr+')'
139
+ else
140
+ @expr
141
+ end
142
+ end
143
+
144
+ def enclose_expr_div #:nodoc:
145
+ return nil if @expr.nil?
146
+ if /\w[^\w]+\w/o =~ @expr
147
+ '/('+@expr+')'
148
+ else
149
+ '/'+@expr
150
+ end
151
+ end
152
+
153
+ # Multiplication of two quantities.
154
+ # Returns an instance of Quantity class in a multiplied unit.
155
+ def *(other)
156
+ if Quantity===other
157
+ a = [self.enclose_expr, other.enclose_expr]
158
+ a.delete(nil)
159
+ self.class.new( @val*other.val, a.join(' '), @unit*other.unit )
160
+ else
161
+ self.class.new( @val*other, @expr, @unit )
162
+ end
163
+ end
164
+
165
+ # Division of two quantities.
166
+ # Returns an instance of Quantity class in a divided unit.
167
+ %w[/ div quo].each do |s|
168
+ define_method(s) do |other|
169
+ if Quantity===other
170
+ a = [self.enclose_expr, other.enclose_expr_div]
171
+ a.delete(nil)
172
+ self.class.new( @val.send(s,other.val), a.join, @unit/other.unit )
173
+ else
174
+ self.class.new( @val.send(s,other), @expr, @unit )
175
+ end
176
+ end
177
+ end
178
+ alias fdiv quo
179
+
180
+ %w[% remainder].each do |s|
181
+ define_method(s) do |other|
182
+ other = (Quantity===other) ? other.val : other
183
+ self.class.new( @val.send(s,other), @expr, @unit )
184
+ end
185
+ end
186
+ alias modulo %
187
+
188
+ def coerce(other)
189
+ [ self.class.new(other), self ]
190
+ end
191
+
192
+ def abs
193
+ self.class.new( @val.abs, @expr, @unit )
194
+ end
195
+
196
+ def abs2
197
+ self**2
198
+ end
199
+
200
+ # Conversion to base unit.
201
+ # Returns the quantity converted to a base unit.
202
+ def to_base_unit
203
+ unit = @unit.base_unit
204
+ val = unit.convert(self)
205
+ expr = unit.unit_string
206
+ self.class.new( val, expr, unit )
207
+ end
208
+ alias to_si to_base_unit
209
+ alias to_SI to_base_unit
210
+
211
+ # Conversion to Numeric.
212
+ # Returns Numeric if the unit is dimensionless.
213
+ # Raises an Error if the unit is non-dminensionless.
214
+ def to_numeric
215
+ @unit.convert_to_numeric(@val)
216
+ end
217
+
218
+ def to_f
219
+ to_numeric.to_f
220
+ end
221
+ alias to_float to_f
222
+
223
+ def to_i
224
+ to_numeric.to_i
225
+ end
226
+ alias to_int to_i
227
+ alias to_integer to_i
228
+
229
+ def to_r
230
+ to_numeric.to_r
231
+ end
232
+ alias to_rational to_r
233
+
234
+ def to_s
235
+ if @expr
236
+ expr = ",'" +@expr+"'"
237
+ else
238
+ expr = ""
239
+ end
240
+ self.class.to_s+"["+Unit::Utils.num_inspect(@val)+expr+"]"
241
+ end
242
+
243
+ def inspect
244
+ if @expr
245
+ expr = "," +@expr.inspect
246
+ else
247
+ expr = ""
248
+ end
249
+ "#<"+self.class.to_s+" "+Unit::Utils.num_inspect(@val)+expr+", "+@unit.inspect+">"
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,446 @@
1
+ #
2
+ # phys/units/unit.rb
3
+ #
4
+ # Copyright (c) 2001-2013 Masahiro Tanaka <masa16.tanaka@gmail.com>
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU General Public License version 3 or later.
9
+
10
+ module Phys
11
+
12
+ class Unit
13
+
14
+ LIST = {}
15
+ PREFIX = {}
16
+
17
+ def self.prefix_regex
18
+ @@prefix_regex
19
+ end
20
+
21
+ def initialize(arg,expr=nil,offset=nil)
22
+ case arg
23
+ when Numeric
24
+ arg = Rational(arg) if Integer===arg
25
+ @factor = arg
26
+ alloc_dim(expr)
27
+ when Phys::Unit
28
+ replace(arg)
29
+ when String
30
+ @name = arg
31
+ if expr.kind_of? Phys::Unit
32
+ @factor = expr.factor
33
+ @offset = expr.offset
34
+ alloc_dim expr.dim
35
+ else
36
+ @expr = expr
37
+ end
38
+ else
39
+ raise TypeError,"invalid argument : #{arg.inspect}"
40
+ end
41
+ end
42
+
43
+ attr_reader :name, :offset, :expr
44
+
45
+ def dim
46
+ use_dimension
47
+ @dim
48
+ end
49
+ alias dimension dim
50
+
51
+ def factor
52
+ use_dimension
53
+ @factor
54
+ end
55
+
56
+ def dimension_value
57
+ 1
58
+ end
59
+
60
+ def replace(x)
61
+ @name = x.name.dup if x.name
62
+ @factor = x.factor
63
+ @offset = x.offset
64
+ alloc_dim x.dim
65
+ end
66
+
67
+ def alloc_dim(hash=nil)
68
+ case hash
69
+ when Hash
70
+ @dim = hash.dup
71
+ else
72
+ @dim = {}
73
+ end
74
+ @dim.default = 0
75
+ end
76
+
77
+ def use_dimension
78
+ return if @dim && @factor
79
+ if @expr && @dim.nil?
80
+ puts "unit='#{@name}', parsing '#{@expr}'..." if Unit.debug
81
+ unit = Unit.parse(@expr)
82
+ case unit
83
+ when Unit
84
+ @dim = unit.dim
85
+ @factor = unit.factor
86
+ if @dim.nil? || @factor.nil?
87
+ raise UnitParseError,"parse error : #{unit.inspect}"
88
+ end
89
+ when Numeric
90
+ @factor = unit
91
+ alloc_dim
92
+ else
93
+ raise UnitParseError,"parse error : #{self.inspect}"
94
+ end
95
+ else
96
+ raise UnitParseError,"undefined unit?: #{self.inspect}"
97
+ end
98
+ end
99
+
100
+ def inspect
101
+ a = [Utils.num_inspect(@factor), @dim.inspect]
102
+ a << "@name="+@name.inspect if @name
103
+ a << "@expr="+@expr.inspect if @expr
104
+ a << "@offset="+@offset.inspect if @offset
105
+ a << "@dimensionless=true" if @dimensionless
106
+ if @dimension_value && @dimension_value!=1
107
+ a << "@dimension_value="+@dimension_value.inspect
108
+ end
109
+ s = a.join(",")
110
+ "#<#{self.class} #{s}>"
111
+ end
112
+
113
+ def unit_string
114
+ use_dimension
115
+ a = []
116
+ a << Utils.num_inspect(@factor) if @factor!=1
117
+ a += @dim.map do |k,d|
118
+ if d==1
119
+ k
120
+ else
121
+ "#{k}^#{d}"
122
+ end
123
+ end
124
+ a.join(" ")
125
+ end
126
+ alias string_form unit_string
127
+
128
+ # Unit conversion
129
+
130
+ def conversion_factor
131
+ use_dimension
132
+ f = @factor
133
+ @dim.each do |k,d|
134
+ if d != 0
135
+ u = LIST[k]
136
+ if u.dimensionless?
137
+ f *= u.dimension_value**d
138
+ end
139
+ end
140
+ end
141
+ f
142
+ end
143
+
144
+ def scalar?
145
+ use_dimension
146
+ (@dim.nil? || @dim.empty?) && @factor==1
147
+ end
148
+
149
+ def dimensionless_deleted
150
+ use_dimension
151
+ hash = @dim.dup
152
+ hash.delete_if{|k,v| LIST[k].dimensionless?}
153
+ end
154
+
155
+ def dimensionless?
156
+ use_dimension
157
+ @dim.each_key.all?{|k| LIST[k].dimensionless?}
158
+ end
159
+
160
+ def same_dimension?(x)
161
+ dimensionless_deleted == x.dimensionless_deleted
162
+ end
163
+ alias same_dim? same_dimension?
164
+
165
+ def assert_dimensionless
166
+ if !dimensionless?
167
+ raise UnitConversionError,"Not dimensionless: #{self.inspect}"
168
+ end
169
+ end
170
+
171
+ def assert_same_dimension(x)
172
+ if !same_dimension?(x)
173
+ raise UnitConversionError,"Different dimension: #{self.inspect} and #{x.inspect}"
174
+ end
175
+ end
176
+
177
+ def convert(q)
178
+ if Quantity===q
179
+ assert_same_dimension(q.unit)
180
+ v = q.unit.convert_to_base(q.value)
181
+ convert_from_base(v)
182
+ else
183
+ q / to_num
184
+ end
185
+ end
186
+
187
+ def convert_scale(q)
188
+ convert(q)
189
+ end
190
+
191
+ def convert_to_base(x)
192
+ x * conversion_factor
193
+ end
194
+
195
+ def convert_from_base(x)
196
+ x / conversion_factor
197
+ end
198
+
199
+ def convert_to_numeric(x)
200
+ assert_dimensionless
201
+ x * conversion_factor
202
+ end
203
+
204
+ def to_num
205
+ assert_dimensionless
206
+ conversion_factor
207
+ end
208
+
209
+ def convert_to_float(x)
210
+ convert_to_numeric(x).to_f
211
+ end
212
+
213
+ def base_unit
214
+ Unit.new(1,dim)
215
+ end
216
+
217
+ # Unit operation
218
+
219
+ def operable?
220
+ true
221
+ end
222
+
223
+ def check_operable
224
+ if !operable?
225
+ raise UnitOperationError,"non-operable for #{inspect}"
226
+ end
227
+ end
228
+
229
+ def check_operable2(x)
230
+ if !(operable? && x.operable?)
231
+ raise UnitOperationError,"non-operable: #{inspect} and #{x.inspect}"
232
+ end
233
+ end
234
+
235
+ def dimension_binop(other)
236
+ x = self.dim
237
+ y = other.dim
238
+ if Hash===x
239
+ if Hash===y
240
+ keys = x.keys | y.keys
241
+ dims = {}
242
+ dims.default = 0
243
+ keys.each do |k|
244
+ v = yield( x[k]||0, y[k]||0 )
245
+ dims[k] = v if v!=0
246
+ end
247
+ dims
248
+ else
249
+ x.dup
250
+ end
251
+ else
252
+ raise "dimensin not defined"
253
+ end
254
+ end
255
+
256
+ def dimension_uop
257
+ x = self.dim
258
+ if Hash===x
259
+ dims = {}
260
+ dims.default = 0
261
+ x.each do |k,d|
262
+ v = yield( d )
263
+ dims[k] = v if v!=0
264
+ end
265
+ dims
266
+ else
267
+ raise "dimensin not defined"
268
+ end
269
+ end
270
+
271
+ def +(x)
272
+ x = Unit.cast(x)
273
+ check_operable2(x)
274
+ assert_same_dimension(x)
275
+ Unit.new(@factor+x.factor,@dim.dup)
276
+ end
277
+
278
+ def -(x)
279
+ x = Unit.cast(x)
280
+ check_operable2(x)
281
+ assert_same_dimension(x)
282
+ Unit.new(@factor-x.factor,@dim.dup)
283
+ end
284
+
285
+ def -@
286
+ check_operable
287
+ use_dimension
288
+ Unit.new(-@factor,@dim.dup)
289
+ end
290
+
291
+ def +@
292
+ self
293
+ end
294
+
295
+ def *(x)
296
+ y = Unit.cast(x)
297
+ if scalar?
298
+ return y
299
+ elsif y.scalar?
300
+ return self
301
+ end
302
+ check_operable2(y)
303
+ dims = dimension_binop(y){|a,b| a+b}
304
+ factor = self.factor * y.factor
305
+ Unit.new(factor,dims)
306
+ end
307
+
308
+ def /(x)
309
+ y = Unit.cast(x)
310
+ if scalar?
311
+ return y.inv
312
+ elsif y.scalar?
313
+ return self
314
+ end
315
+ check_operable2(y)
316
+ dims = dimension_binop(y){|a,b| a-b}
317
+ factor = self.factor / y.factor
318
+ Unit.new(factor,dims)
319
+ end
320
+
321
+ def rdiv(x)
322
+ y = Unit.cast(x)
323
+ if scalar?
324
+ return y.inv
325
+ elsif y.scalar?
326
+ return self
327
+ end
328
+ check_operable2(y)
329
+ dims = dimension_binop(y){|a,b| a-b}
330
+ factor = Rational(self.factor,x.factor)
331
+ Unit.new(factor,dims)
332
+ end
333
+
334
+ def self.rdiv(x,y)
335
+ Unit.cast(x).rdiv(y)
336
+ end
337
+
338
+ def inv
339
+ check_operable
340
+ dims = dimension_uop{|a| -a}
341
+ Unit.new(Rational(1,self.factor), dims)
342
+ end
343
+
344
+ def **(x)
345
+ check_operable
346
+ m = Utils.as_numeric(x)
347
+ dims = dimension_uop{|a| a*m}
348
+ Unit.new(@factor**m,dims)
349
+ end
350
+
351
+ def self.func(fn, x)
352
+ fn = 'log' if fn == 'ln'
353
+ m = Unit.new(x).to_num
354
+ Unit.new( Math.send(fn,m) )
355
+ end
356
+
357
+ def ==(x)
358
+ use_dimension
359
+ @factor == x.factor && @dim == x.dim &&
360
+ offset == x.offset && dimension_value == x.dimension_value
361
+ end
362
+
363
+ def coerce(x)
364
+ [Unit.find_unit(x), self]
365
+ end
366
+
367
+ end # Unit
368
+
369
+
370
+ class BaseUnit < Unit
371
+ def initialize(s,dimless=false,v=nil)
372
+ case s
373
+ when String
374
+ @name = s
375
+ @factor = 1
376
+ @dim = {s=>1}
377
+ @dim.default = 0
378
+ @dimensionless = dimless
379
+ @dimension_value = v || 1
380
+ else
381
+ raise ArgumentError "BaseUnit#initialize: arg must be string: #{s}"
382
+ end
383
+ end
384
+
385
+ def replace(x)
386
+ super(x)
387
+ @dimensionless = x.dimensionless
388
+ @dimension_value = x.dimension_value
389
+ end
390
+
391
+ def use_dimension
392
+ end
393
+
394
+ def dimensionless?
395
+ @dimensionless
396
+ end
397
+
398
+ def dimensionless_deleted
399
+ if @dimensionless
400
+ {}
401
+ else
402
+ @dim.dup
403
+ end
404
+ end
405
+
406
+ attr_reader :dimension_value
407
+ end
408
+
409
+
410
+ class OffsetUnit < Unit
411
+
412
+ def self.define(name,unit,offset=nil)
413
+ LIST[name] = self.new(name,unit,offset)
414
+ end
415
+
416
+ def initialize(name,arg,offset)
417
+ super(name,arg)
418
+ @offset = offset
419
+ if offset.nil?
420
+ raise ArgumentError,"offset is not supplied"
421
+ end
422
+ end
423
+
424
+ def convert_to_base(x)
425
+ x * conversion_factor + @offset
426
+ end
427
+
428
+ def convert_from_base(x)
429
+ (x - @offset) / conversion_factor
430
+ end
431
+
432
+ def convert_scale(q)
433
+ if Quantity===q
434
+ assert_same_dimension(q.unit)
435
+ v = q.value * q.unit.conversion_factor
436
+ v = v / self.conversion_factor
437
+ else
438
+ raise TypeError,"not Quantitiy: #{q.inspect}"
439
+ end
440
+ end
441
+
442
+ def operable?
443
+ false
444
+ end
445
+ end
446
+ end