numeric_with_unit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 48d05ab9f9aa01a815b412324ef7a04b198041db
4
+ data.tar.gz: 5b55204a0a0b340dfe941ea141e29f6977197a34
5
+ SHA512:
6
+ metadata.gz: d52e9021767bdbfee29dde88feaa8a64790475db07c0cee5df91c79f546e88604d08097f95a4730088df620336760d74d68f7f1f8ac9bf583f87ab7505b71a18
7
+ data.tar.gz: e3d277dee997ad68c2b38c8a4c06d6404b60bc1fb8d0328661055b5f75904afc834a8a6513d07c0d0e56688ae0577f6c35352c3fc1020080a92f8d8e0fea20ff
@@ -0,0 +1,193 @@
1
+ # coding: utf-8
2
+
3
+ require 'numeric_with_unit/unit'
4
+
5
+ class NumericWithUnit
6
+ include Comparable
7
+
8
+ attr_reader :value, :unit
9
+
10
+ def initialize(value, unit)
11
+ @value = value
12
+ @unit = unit.is_a?(Unit) ? unit : Unit[unit]
13
+ end
14
+
15
+ def inspect
16
+ "#{@value.inspect} [#{@unit.symbol}] #{unit.dimension.inspect}"
17
+ end
18
+
19
+ def to_s
20
+ "#{@value.to_s} #{@unit.symbol}"
21
+ end
22
+
23
+ # otherがNumericWithUnitで次元が同じだったらsi単位に変換して比較、そうでなければ比較できない(nil)
24
+ def <=>(other)
25
+ if other.is_a?(self.class) and @unit.dimension_equal? other.unit
26
+ @unit.to_si(@value) <=> other.unit.to_si(other.value)
27
+ end
28
+ end
29
+
30
+ def succ
31
+ self.class.new(@value.succ, @unit)
32
+ end
33
+
34
+ def to_i
35
+ @value.to_i
36
+ end
37
+
38
+ def to_f
39
+ @value.to_f
40
+ end
41
+
42
+ def to_nwu(unit)
43
+ new_unit = unit.is_a?(Unit) ? unit : Unit[unit]
44
+
45
+ unless @unit.dimension_equal? new_unit
46
+ raise DimensionError, "Dimensions are different between #{@unit.symbol}#{@unit.dimension} #{new_unit.symbol}#{new_unit.dimension}"
47
+ end
48
+
49
+ new_value = new_unit.from_si(@unit.to_si(@value))
50
+ self.class.new(new_value, new_unit)
51
+ end
52
+ alias :[] :to_nwu
53
+
54
+ def +@
55
+ self
56
+ end
57
+
58
+ def -@
59
+ self.class.new(-@value, @unit)
60
+ end
61
+
62
+ def +(other)
63
+ nwu = if other.is_a? self.class
64
+ other
65
+ else
66
+ self.class.new(other, Unit.new)
67
+ end
68
+ add_with_other_unit(nwu)
69
+ end
70
+
71
+ def -(other)
72
+ self + (-other)
73
+ end
74
+
75
+ def *(other)
76
+ case other
77
+ when self.class
78
+ multiply_with_other_unit(other)
79
+ else
80
+ self.class.new(@value*other, @unit)
81
+ end
82
+ end
83
+
84
+ def /(other)
85
+ case other
86
+ when self.class
87
+ devide_with_other_unit(other)
88
+ else
89
+ self.class.new(@value/other, @unit)
90
+ end
91
+ end
92
+
93
+ def coerce(other)
94
+ if other.is_a?(self.class)
95
+ [other, self]
96
+ else
97
+ [self.class.new(other, Unit.new), self]
98
+ end
99
+ end
100
+
101
+ def **(num)
102
+ # Dimension Check
103
+ @unit.derivation.each do |k,v|
104
+ res = v * num
105
+ raise DimensionError, "Dimension of #{k.symbol}(#{v}*#{num}) must be Integer" unless res.to_i == res # 判定方法見なおせ
106
+ end
107
+
108
+ self.class.new(@value**num, @unit**num)
109
+ end
110
+
111
+ def root(num)
112
+ self**(Rational(1,num))
113
+ end
114
+ def sqrt; root(2) end # 平方根
115
+ def cbrt; root(3) end # 立方根
116
+
117
+ private
118
+
119
+ def add_with_other_unit(other)
120
+ if @unit.dimension_equal? other.unit
121
+ v1 = @unit.to_si(@value)
122
+ v2 = other.unit.to_si(other.value)
123
+ vr = @unit.from_si(v1+v2)
124
+ self.class.new(vr, @unit)
125
+ else
126
+ raise DimensionError, "Dimensions are different between #{@unit.dimension} #{other.unit.dimension}"
127
+ end
128
+ end
129
+
130
+ def multiply_with_other_unit(other)
131
+ onwu = adjust_other_unit(other)
132
+ self.class.new(@value * onwu.value, @unit * onwu.unit)
133
+ end
134
+
135
+ def devide_with_other_unit(other)
136
+ onwu = adjust_other_unit(other)
137
+ self.class.new(@value / onwu.value, @unit / onwu.unit)
138
+ end
139
+
140
+ # なるべくselfと同じ単位を使用するようにotherを変換します。
141
+ def adjust_other_unit(other)
142
+ if @unit.derivation.any?{|k,v| k == other.unit} # [L/min]*[min]などのケース
143
+ other
144
+ elsif h = @unit.derivation.find{|k,v| k.dimension_equal? other.unit} # [L/min]*[s]などのケース
145
+ other[ h.first ]
146
+ elsif @unit.dimension_equal? other.unit # [mm]*[cm]などのケース
147
+ other[@unit]
148
+ else
149
+ other
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+
156
+
157
+ class NumericWithUnit
158
+ class DimensionError < StandardError; end
159
+ end
160
+
161
+
162
+
163
+ class Fixnum
164
+ def to_nwu(unit)
165
+ NumericWithUnit.new(self, unit)
166
+ end
167
+ end
168
+
169
+ class Bignum
170
+ def to_nwu(unit)
171
+ NumericWithUnit.new(self, unit)
172
+ end
173
+ end
174
+
175
+ class Numeric
176
+ def to_nwu(unit)
177
+ NumericWithUnit.new(self, unit)
178
+ end
179
+ end
180
+
181
+ class String
182
+ def to_nwu(mthd=:to_r)
183
+ m = self.match /(?<value>.+) (?<unit>.+)/ # 適当
184
+ NumericWithUnit[m[:value].__send__(mthd), m[:unit]]
185
+ end
186
+ end
187
+
188
+
189
+
190
+ # unit definition
191
+ require 'numeric_with_unit/base_unit'
192
+ require 'numeric_with_unit/common_unit'
193
+
@@ -0,0 +1,179 @@
1
+ # coding: utf-8
2
+
3
+ #
4
+ # SI base units & SI derived units & Units in use with SI
5
+ #
6
+
7
+ require 'numeric_with_unit/unit'
8
+
9
+ class NumericWithUnit
10
+ # Dimensionless
11
+ Unit << Unit.new do |conf|
12
+ conf.symbol = ''
13
+ end
14
+
15
+ #
16
+ # SI base units
17
+ #
18
+
19
+ # Length
20
+ Unit << Unit.new do |conf|
21
+ conf.symbol = 'm'
22
+ conf.dimension[:L] = 1
23
+ conf.si = true
24
+ end
25
+
26
+ # Mass
27
+ Unit << Unit.new do |conf|
28
+ conf.symbol = 'kg'
29
+ conf.dimension[:M] = 1
30
+ conf.si = true
31
+ end
32
+ Unit['g'] = "1/1000".to_r, 'kg' # for compatible
33
+
34
+ # Time
35
+ Unit << Unit.new do |conf|
36
+ conf.symbol = 's'
37
+ conf.dimension[:T] = 1
38
+ conf.si = true
39
+ end
40
+
41
+ # Electric Current
42
+ Unit << Unit.new do |conf|
43
+ conf.symbol = 'A'
44
+ conf.dimension[:I] = 1
45
+ conf.si = true
46
+ end
47
+
48
+ # Thermodynamic Temperature
49
+ Unit << Unit.new do |conf|
50
+ conf.symbol = 'K'
51
+ conf.dimension[:Θ] = 1
52
+ conf.si = true
53
+ end
54
+
55
+ # Amout of Substance
56
+ Unit << Unit.new do |conf|
57
+ conf.symbol = 'mol'
58
+ conf.dimension[:N] = 1
59
+ conf.si = true
60
+ end
61
+
62
+ # Luminous Intensity
63
+ Unit << Unit.new do |conf|
64
+ conf.symbol = 'cd'
65
+ conf.dimension[:J] = 1
66
+ conf.si = true
67
+ end
68
+
69
+
70
+
71
+ #
72
+ # SI derived units
73
+ #
74
+
75
+ # Frequency
76
+ Unit['Hz'] = '/s'
77
+
78
+ # Angle
79
+ Unit['rad'] = 'm/m'
80
+ Unit['°'] = Math::PI/180, 'rad'
81
+ Unit['′'] = "1/60".to_r, '°'
82
+ Unit['″'] = "1/60".to_r, '′'
83
+
84
+ # Solid Angle
85
+ Unit['sr'] = 'm2/m2'
86
+
87
+ # Force
88
+ Unit['N'] = 'kg.m/s2'
89
+
90
+ # Pressure
91
+ Unit['Pa'] = 'N/m2'
92
+
93
+ # Energy
94
+ Unit['J'] = 'N.m'
95
+
96
+ # Power
97
+ Unit['W'] = 'J/s'
98
+
99
+ # Electric Charge
100
+ Unit['C'] = 's.A'
101
+
102
+ # Voltage
103
+ Unit['V'] = 'W/A'
104
+
105
+ # Electriccal Capacitance
106
+ Unit['F'] = 'C/V'
107
+
108
+ # Electriccal Resistance
109
+ Unit['Ω'] = 'V/A'
110
+ Unit['ohm'] = 'Ω'
111
+
112
+ # Electriccal Conductance
113
+ Unit['S'] = 'A/V'
114
+
115
+ # Magnetic Flux
116
+ Unit['Wb'] = 'J/A'
117
+
118
+ # Magnetic Field Strength
119
+ Unit['T'] = 'Wb/m2'
120
+
121
+ # Inductance
122
+ Unit['H'] = 'V.s/A'
123
+
124
+ # Temperature
125
+ Unit << Unit.new do |conf|
126
+ k = Unit['K']
127
+ intercept = "273.15".to_r
128
+
129
+ conf.symbol = '℃'
130
+ conf.dimension = k.dimension
131
+ conf.from_si{|x| k.from_si(x)-intercept}
132
+ conf.to_si{|x| k.to_si(x+intercept)}
133
+ end
134
+ Unit['degC'] = '℃'
135
+
136
+ # Luminouse flux
137
+ Unit['lx'] = 'cd.sr'
138
+
139
+ # Radioactivity
140
+ Unit['Bq'] = '/s'
141
+
142
+ # Absorbed Dose
143
+ Unit['Gy'] = 'J/kg'
144
+
145
+ # Equivalent Dose
146
+ Unit['Sv'] = 'J/kg'
147
+
148
+ # Catalytic Activity
149
+ Unit['kat'] = 'mol/s'
150
+
151
+
152
+
153
+ #
154
+ # Units in use with SI
155
+ #
156
+
157
+ # Time
158
+ Unit['min'] = 60, 's'
159
+ Unit['hr'] = 60, 'min'
160
+
161
+ # Area
162
+ Unit['a'] = 100, 'm2'
163
+
164
+ # Volume
165
+ Unit['L'] = 'dm3'
166
+
167
+ # Mass
168
+ Unit['t'] = 1000, 'kg'
169
+
170
+ # Energy
171
+ Unit['eV'] = "1.6021765314e-19".to_r, 'J'
172
+
173
+ # Mass
174
+ Unit['u'] = "1.6605388628e-27".to_r, 'kg'
175
+ Unit['Da'] = 'u'
176
+
177
+ # Length
178
+ Unit['ua'] = "1.495978706916e11".to_r, 'm'
179
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+
3
+ require 'numeric_with_unit/base_unit'
4
+
5
+ Unit['cal'] = "4.184".to_r, 'J'
6
+
7
+ Unit['Gal'] = 'cm/s2'
8
+
9
+ Unit['dyn'] = 'g.cm/s2'
10
+
11
+ Unit['erg'] = 'g.cm2/s2'
12
+
13
+ Unit['Ba'] = 'g/(cm.s2)'
14
+
15
+ Unit['P'] = 'g/(cm.s)'
16
+ Unit['poise'] = 'g/(cm.s)'
17
+
18
+ Unit['St'] = 'cm2/s'
19
+
@@ -0,0 +1,112 @@
1
+ # coding: utf-8
2
+
3
+ #
4
+ # 独断と偏見による一般的な単位
5
+ #
6
+
7
+ require 'numeric_with_unit/unit'
8
+ require 'numeric_with_unit/base_unit'
9
+
10
+ class NumericWithUnit
11
+ # Dimensionless
12
+ Unit['-'] = ''
13
+ Unit['1'] = ''
14
+
15
+ # Time
16
+ Unit['day'] = 24, 'hr'
17
+ Unit['week'] = 7, 'day'
18
+ Unit['month'] = 30, 'day' # 30日固定
19
+ Unit['year'] = 12, 'month' # 360日固定
20
+
21
+ # Mass
22
+ Unit['ton'] = 1000, 'kg'
23
+ Unit['oz'] = "28.349523125".to_r, 'g'
24
+ Unit['lb'] = 16, 'oz'
25
+
26
+ # Temperature
27
+ Unit['degC'] = '℃'
28
+ Unit['degR'] = "5/9".to_r, 'K'
29
+ Unit << Unit.new do |conf|
30
+ degr = Unit['degR']
31
+ intercept = "459.67".to_r
32
+
33
+ conf.symbol = 'degF'
34
+ conf.dimension = degr.dimension
35
+ conf.from_si{|x| degr.from_si(x)-intercept}
36
+ conf.to_si{|x| degr.to_si(x+intercept)}
37
+ end
38
+
39
+ # Length
40
+ Unit['Å'] = 10**-10, 'm'
41
+ Unit['yd'] = "0.9144".to_r, 'm'
42
+ Unit['ft'] = "1/3".to_r, 'yd'
43
+ Unit['in'] = "1/12".to_r, 'ft'
44
+ Unit['mi'] = 5280, 'ft'
45
+
46
+ # Volume
47
+ Unit['cc'] = 'cm3'
48
+ Unit['bbl'] = "0.158987294928".to_r, 'm3'
49
+
50
+ # Force
51
+ Unit['kgf'] = "9.80665".to_r, 'N'
52
+ Unit['lbf'] = "4.4482216152605".to_r, 'N'
53
+
54
+ # Power
55
+ Unit['PS'] = 75, 'kgf.m/s' # 仏馬力。小文字[ps]だとpico secondと区別がつかないため大文字で定義
56
+ Unit['HP'] = 550, 'lbf.ft/s' # 英馬力
57
+
58
+ # Pressure
59
+ Unit['bar'] = 1e5, 'Pa'
60
+ Unit['atm'] = 101325, 'Pa'
61
+ Unit['Torr'] = "101325/760".to_r, 'Pa'
62
+ Unit['mmHg'] = "101325/760".to_r, 'Pa'
63
+ Unit['mHg'] = "1/1000".to_r, 'mmHg' # for compatible
64
+ Unit['mH2O'] = "9806.65".to_r, 'Pa'
65
+ Unit['mAq'] = 'mH2O'
66
+ Unit['psi'] = "6894.76".to_r, 'Pa'
67
+
68
+ # Guage圧の扱いどうしようか?
69
+ Unit << Unit.new do |conf|
70
+ pa = Unit['Pa']
71
+ atm = 101325
72
+
73
+ conf.symbol = 'PaG'
74
+ conf.dimension = pa.dimension
75
+ conf.from_si{|x| pa.from_si(x)-atm}
76
+ conf.to_si{|x| pa.to_si(x+atm)}
77
+ end
78
+
79
+ # Speed
80
+ Unit['kph'] = 'km/hr'
81
+
82
+ # Flowrate
83
+ Unit['lpm'] = 'L/min'
84
+
85
+ # Viscosity
86
+ Unit['P'] = "1/10".to_r, 'Pa.s'
87
+
88
+ # Kinetic Viscosity
89
+ Unit['St'] = 'cm2/s'
90
+
91
+ # Energy
92
+ Unit['cal'] = "4.184".to_r, 'J' # 熱力学カロリー
93
+ Unit['MMkcal'] = 10**6 * 10**3, 'cal'
94
+ Unit['Btu'] = "1055.05585262".to_r, 'J' # 国際蒸気表(IT)Btu
95
+ Unit['MMBtu'] = 10**6, 'Btu'
96
+
97
+ # Ratio
98
+ Unit['%'] = 10**-2, ''
99
+ Unit['‰'] = 10**-3, ''
100
+ Unit['ppm'] = 10**-6, ''
101
+ Unit['ppb'] = 10**-9, ''
102
+
103
+
104
+ # Infomation
105
+ Unit << Unit.new do |conf|
106
+ conf.symbol = 'bit'
107
+ # conf.dimension[:] = # 情報量の次元って何?
108
+ end
109
+ Unit['B'] = 8, 'bit'
110
+ Unit['bps'] = 'bit/s'
111
+ Unit['Bps'] = 'B/s'
112
+ end
@@ -0,0 +1,43 @@
1
+ # coding: utf-8
2
+
3
+ require 'numeric_with_unit/base_unit'
4
+
5
+ Unit['yd'] = "0.9144".to_r , 'm'
6
+ Unit['ft'] = "1/3".to_r, 'yd'
7
+ Unit['th'] = "1/12000".to_r, 'ft'
8
+ Unit['in'] = "1/12".to_r, 'ft'
9
+ Unit['ch'] = 66, 'ft'
10
+ Unit['fur'] = 660, 'ft'
11
+ Unit['mi'] = 5280, 'ft'
12
+ Unit['lea'] = 15840, 'ft'
13
+
14
+
15
+ Unit['floz'] = "2.84130625e-05".to_r, 'm3'
16
+ Unit['gi'] = 5, 'floz'
17
+ Unit['pt'] = 20, 'floz'
18
+ Unit['qt'] = 40, 'floz'
19
+ Unit['gal'] = 160, 'floz'
20
+
21
+ Unit['bbl'] = "0.158987294928".to_r, 'm3'
22
+
23
+
24
+ Unit['oz'] = "0.45359237".to_r, 'kg'
25
+ Unit['gr'] = "1/7000".to_r, 'oz'
26
+ Unit['dr'] = "1/256".to_r, 'oz'
27
+ Unit['lb'] = "1/16".to_r, 'oz'
28
+ Unit['st'] = 14, 'oz'
29
+ Unit['qtr'] = 28, 'oz'
30
+ Unit['cwt'] = 112, 'oz'
31
+ Unit['t'] = 2240, 'oz'
32
+
33
+
34
+ Unit['degR'] = "9/5".to_r, 'K'
35
+
36
+ Unit << Unit.new do |conf|
37
+ deg_r = Unit['degR']
38
+ conf.symbol = '℉'
39
+ conf.dimension = deg_r.dimension
40
+ conf.from_si = ->(x){(deg_r.from_si(x)) - 459.67}
41
+ conf.to_si = ->(x){deg_r.to_si(x + 459.67)}
42
+ end
43
+ Unit['degF'] = '℉'
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+
3
+ require 'numeric_with_unit/base_unit'
4
+
5
+ # Naural Units
6
+ # Speed
7
+ Unit['c0'] = "299792458.0".to_r, 'm/s'
8
+ # Action
9
+ Unit['ħ'] = "1.0545716818e−34".to_r, 'J.s'
10
+ Unit['h'] = 'ħ' # alias
11
+ # Mass
12
+ Unit['me'] = "9.109382616e−31".to_r, 'kg'
13
+ # Time
14
+ Unit['ħ/(me.(c0)2)'] = "1.288088667786e-21".to_r, 's' # なんかきもい
@@ -0,0 +1,353 @@
1
+ # coding: utf-8
2
+
3
+
4
+ class NumericWithUnit
5
+ class Unit
6
+ class Config
7
+ attr_reader :symbol, :dimension, :derivation
8
+ attr_reader :si, :proportional
9
+
10
+ def initialize(parent=nil)
11
+ @symbol = nil
12
+ @dimension = Hash.new(0)
13
+ @from_si = nil
14
+ @to_si = nil
15
+ @derivation = Hash.new(0)
16
+ @si = false
17
+
18
+ @parent = parent
19
+ end
20
+
21
+ def compile
22
+ @dimension.delete_if{|k,v| v.zero?}
23
+ @derivation.delete_if{|k,v| v.zero?}
24
+ @derivation.delete_if{|k,v| k.symbol.nil?}
25
+
26
+ if @derivation.empty?
27
+ @from_si ||= ->(x){x}
28
+ @to_si ||= ->(x){x}
29
+ @derivation[@parent] += 1 unless @parent.nil?
30
+ else # configにderivationが与えられた時は、derivationをもとに@dimension,@symbol,@to_si,@from_siを設定
31
+ h = @derivation.sort_by{|u,v| u.symbol}.sort_by{|u,v| v} # ←どうしよう
32
+
33
+ s1 = h.select{|u,v| v > 0}.map{|u,v| u.symbol + ((v.abs>1) ? v.abs.to_s : '')}.join('.')
34
+ s2 = h.select{|u,v| v < 0}.map{|u,v| u.symbol + ((v.abs>1) ? v.abs.to_s : '')}.join('.')
35
+ @symbol = s1 + (s2.empty? ? '' : "/(#{s2})")
36
+
37
+ @derivation.each do |u,v|
38
+ u.dimension.each do |d,i|
39
+ @dimension[d] += i*v
40
+ end
41
+ end
42
+
43
+ @from_si = @derivation.map{|u,v|
44
+ prc = if v > 0
45
+ ->(x){u.from_si(x)}
46
+ else
47
+ ->(x){x.quo(u.from_si(1)-u.from_si(0))} # ℃とKの変換のような場合に、変換式の切片を消すため。変換式が線形じゃないケースは想定していない
48
+ end
49
+ [prc, v.abs]
50
+ }.map{|prc,v|
51
+ ->(x){ v.times{x = prc[x]}; x }
52
+ }.reduce{|memo, prc|
53
+ ->(x){memo[prc[x]]}
54
+ }
55
+
56
+ @to_si = @derivation.map{|u,v|
57
+ prc = if v > 0
58
+ ->(x){u.to_si(x)}
59
+ else
60
+ ->(x){x.quo(u.to_si(1)-u.to_si(0))} # ℃とKの変換のような場合に、変換式の切片を消すため。変換式が線形じゃないケースは想定していない
61
+ end
62
+ [prc, v.abs]
63
+ }.map{|prc,v|
64
+ ->(x){ v.times{x = prc[x]}; x }
65
+ }.reduce{|memo, prc|
66
+ ->(x){memo[prc[x]]}
67
+ }
68
+ end
69
+
70
+ self
71
+ end
72
+
73
+ def symbol=(arg)
74
+ @symbol = arg.to_s
75
+ end
76
+
77
+ def dimension=(arg)
78
+ raise unless arg.is_a?(Hash)
79
+ @dimension = arg
80
+ end
81
+
82
+ def from_si=(arg)
83
+ raise unless arg.is_a?(Proc)
84
+ @from_si = arg
85
+ end
86
+
87
+ def from_si(&block)
88
+ if block_given?
89
+ @from_si = block
90
+ else
91
+ @from_si
92
+ end
93
+ end
94
+
95
+ def to_si=(arg)
96
+ raise unless arg.is_a?(Proc)
97
+ @to_si = arg
98
+ end
99
+
100
+ def to_si(&block)
101
+ if block_given?
102
+ @to_si = block
103
+ else
104
+ @to_si
105
+ end
106
+ end
107
+
108
+ def si=(arg)
109
+ raise unless [TrueClass, FalseClass].any?{|klass|arg.is_a?(klass)}
110
+ @si = arg
111
+ end
112
+
113
+ def derivation=(arg)
114
+ raise unless arg.is_a?(Hash)
115
+ @derivation = arg
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+
122
+
123
+ class NumericWithUnit
124
+ class Unit
125
+ @@list = []
126
+ @@prefix = {
127
+ 'Y' => 10**24,
128
+ 'Z' => 10**21,
129
+ 'E' => 10**18,
130
+ 'P' => 10**15,
131
+ 'T' => 10**12,
132
+ 'G' => 10**9,
133
+ 'M' => 10**6,
134
+ 'k' => 10**3,
135
+ 'h' => 10**2,
136
+ 'da' => 10**1,
137
+ 'd' => 10**-1,
138
+ 'c' => 10**-2,
139
+ 'm' => 10**-3,
140
+ 'μ' => 10**-6,
141
+ 'u' => 10**-6,
142
+ 'n' => 10**-9,
143
+ 'p' => 10**-12,
144
+ 'f' => 10**-15,
145
+ 'a' => 10**-18,
146
+ 'z' => 10**-21,
147
+ 'y' => 10**-24,
148
+ 'Ki' => 2**10,
149
+ 'Mi' => 2**20,
150
+ 'Gi' => 2**30,
151
+ 'Ti' => 2**40,
152
+ 'Pi' => 2**50,
153
+ 'Ei' => 2**60,
154
+ 'Zi' => 2**70,
155
+ 'Yi' => 2**80
156
+ }
157
+
158
+ # class methods
159
+
160
+ def self.[](arg)
161
+ self.parse(arg.to_s)
162
+ end
163
+
164
+ def self.[]=(key, arg)
165
+ if arg.is_a?(Array) and arg.size == 2
166
+ a = [key, arg.first]
167
+ u = arg.last
168
+ else
169
+ a = [key]
170
+ u = arg
171
+ end
172
+ @@list << (u.is_a?(self) ? u : self[u]).cast(*a)
173
+ end
174
+
175
+ def self.list
176
+ @@list.map(&:symbol)
177
+ end
178
+
179
+ def self.<<(arg)
180
+ if arg.is_a?(self)
181
+ @@list << arg
182
+ else
183
+ @@list << Unit[arg]
184
+ end
185
+ end
186
+
187
+ def self.delete(unit_symbol)
188
+ @@list.delete_if{|unit| unit.symbol == unit_symbol}
189
+ end
190
+
191
+ def self.assign
192
+ @@list << self.new{|config| yield(config)}
193
+ end
194
+
195
+ def self.parse(unit_str)
196
+ a = parse_1st(unit_str)
197
+ parse_2nd([a])
198
+ end
199
+
200
+
201
+ # 文字列を配列にパース
202
+ # ex. 'J/(kg.K)' -> [#<Unit:J>, ['/', #<Unit:kg>, '.', #<Unit:K>]]
203
+ # とても手続き的な書き方で禿げる
204
+ def self.parse_1st(unit_str)
205
+ i = @@list.rindex{|u| u.symbol == unit_str}
206
+ return @@list[i] if i
207
+
208
+ return unit_str if unit_str =~ /^[\.\/]$/
209
+
210
+ # 再帰で呼び出す用
211
+ rec = ->(arg){__send__(__method__, arg)}
212
+
213
+ a = unit_str.scan(/(?<=\().*(?=\))|[\.\/]|[^\(\)\.\/]+/)
214
+ return a.map{|elem| rec[elem]} if a.size > 1
215
+
216
+ m = unit_str.match(/-?\d+$/)
217
+ return m.to_s if m and m.pre_match.empty?
218
+ return [rec[m.pre_match], m.to_s] if m
219
+
220
+ m = unit_str.match(/^(?<prefix>#{@@prefix.keys.join('|')})(?<unit>#{list.join('|')})$/)
221
+ return rec[m[:unit]].cast(unit_str, @@prefix[m[:prefix]]) if m
222
+
223
+ raise NoUnitError, "\"#{unit_str}\" is not assigned"
224
+ end
225
+ private_class_method :parse_1st
226
+
227
+ # 配列を組立単位に変換
228
+ # derivationにそのまま突っ込んだほうがすっきりする気がする
229
+ def self.parse_2nd(unit_array)
230
+ # 再帰で呼び出す用
231
+ rec = ->(arg){__send__(__method__, arg)}
232
+
233
+ buff_ary = []
234
+ buff_unit = ''
235
+ buff_sign = 1
236
+
237
+ unit_array.each do |elem|
238
+ case elem
239
+ when self
240
+ buff_ary << elem ** buff_sign
241
+ when '.'
242
+ buff_sign = 1
243
+ when '/'
244
+ buff_sign = -1
245
+ when Array
246
+ buff_ary << rec[elem] ** buff_sign
247
+ when /^-?\d+$/
248
+ buff_ary[-1] **= elem.to_i
249
+ end
250
+ end
251
+
252
+ buff_ary.reduce(:*)
253
+ end
254
+ private_class_method :parse_2nd
255
+
256
+ end
257
+
258
+
259
+
260
+ class Unit
261
+
262
+ # Instance Methods
263
+
264
+ # attr_accessor :symbol
265
+ attr_reader :symbol
266
+ attr_reader :dimension, :derivation
267
+
268
+ def initialize
269
+
270
+ # Unit::Configとinitializeの役割が分離できていないので見なおせ
271
+ config = Config.new(self)
272
+ yield(config) if block_given?
273
+ config.compile
274
+
275
+ @symbol = config.symbol
276
+ @dimension = config.dimension
277
+ @from_si = config.from_si
278
+ @to_si = config.to_si
279
+
280
+ @derivation = config.derivation
281
+ end
282
+
283
+ def cast(new_symbol, factor = 1)
284
+ self.class.new do |conf|
285
+ conf.symbol = new_symbol
286
+ conf.dimension = @dimension
287
+ conf.from_si = ->(x){from_si(x.quo(factor))}
288
+ conf.to_si = ->(x){to_si(x * factor)}
289
+ end
290
+ end
291
+
292
+ def to_s
293
+ @symbol
294
+ end
295
+
296
+ def to_si(value)
297
+ @to_si[value]
298
+ end
299
+
300
+ def from_si(value)
301
+ @from_si[value]
302
+ end
303
+
304
+
305
+ def dimensionless?
306
+ @dimension.all?{|k,v| v.zero?}
307
+ end
308
+
309
+ def dimension_equal?(other_unit)
310
+ (@dimension.keys | other_unit.dimension.keys).all?{|k|
311
+ @dimension[k] == other_unit.dimension[k]
312
+ }
313
+ end
314
+
315
+ def ==(other)
316
+ if other.is_a?(self.class)
317
+ symbol == other.symbol and dimension == other.dimension
318
+ else
319
+ super
320
+ end
321
+ end
322
+
323
+ def *(other_unit)
324
+ self.class.new do |conf|
325
+ @derivation.each{|k, v| conf.derivation[k] += v}
326
+ other_unit.derivation.each{|k, v| conf.derivation[k] += v}
327
+ end
328
+ end
329
+
330
+ def /(other_unit)
331
+ self.class.new do |conf|
332
+ @derivation.each{|k, v| conf.derivation[k] += v}
333
+ other_unit.derivation.each{|k, v| conf.derivation[k] -= v}
334
+ end
335
+ end
336
+
337
+ def **(num)
338
+ if num.zero?
339
+ self.class.new
340
+ else
341
+ self.class.new do |conf|
342
+ # ここto_iでOKか?v*numが整数じゃなければraiseすべき?→すべき→NumericWithUnitでやるべき?
343
+ # Unitでは整数じゃない次数の単位は許容すべきか否か→していい気がする
344
+ @derivation.each{|k, v| conf.derivation[k] = (v*num).to_i}
345
+ end
346
+ end
347
+ end
348
+ end
349
+
350
+ class Unit
351
+ class NoUnitError < StandardError; end
352
+ end
353
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ # 1234['m3/kg']のように書けるようにします。
4
+ # Numeric#[]、Fixnum#[]、Bignum#[]をオーバーライドします。
5
+
6
+ require 'numeric_with_unit'
7
+
8
+ class NumericWithUnit
9
+ module NumUtil
10
+ def [](unit)
11
+ NumericWithUnit.new(self.rationalize, unit) # ratoinalizeする?
12
+ end
13
+ end
14
+ end
15
+
16
+ class Fixnum
17
+ prepend NumericWithUnit::NumUtil
18
+ end
19
+
20
+ class Bignum
21
+ prepend NumericWithUnit::NumUtil
22
+ end
23
+
24
+ class Numeric
25
+ prepend NumericWithUnit::NumUtil
26
+ end
27
+
@@ -0,0 +1,66 @@
1
+ # coding: utf-8
2
+
3
+ # 123.m2.K_W のように書けるようにします。
4
+ # method_missing をオーバーライドします。
5
+
6
+ require 'numeric_with_unit'
7
+
8
+ class NumericWithUnit
9
+ def method_missing(*args)
10
+ if args.size == 1
11
+ unit_str = args.first.to_s.gsub('_', '/')
12
+ unit_chain_util(Unit[unit_str])
13
+ else
14
+ raise Unit::NoUnitError
15
+ end
16
+ rescue Unit::NoUnitError
17
+ super
18
+ end
19
+
20
+ attr_writer :unit_chain
21
+
22
+ private
23
+ def unit_chain_util(unit)
24
+ ucs = @unit_chain || []
25
+ ucs.map!{|nwu, u| [nwu, u * unit]}
26
+ ucs << [self, unit]
27
+
28
+ if i = ucs.index{|nwu, u| nwu.unit.dimension_equal? u}
29
+ nwu, nu = *ucs[i]
30
+ nwu[nu]
31
+ else
32
+ nnwu = self.class.new(@value, @unit*unit)
33
+ nnwu.unit_chain = ucs
34
+ nnwu
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ class NumericWithUnit
41
+ module NumUtil
42
+ def method_missing(*args)
43
+ if args.size == 1
44
+ unit_str = args.first.to_s.gsub('_', '/')
45
+ self.rationalize.to_nwu(unit_str) # util2は利便性優先なのでratoinalizeしてしまいます
46
+ else
47
+ raise Unit::NoUnitError
48
+ end
49
+ rescue Unit::NoUnitError
50
+ super
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+ class Fixnum
57
+ prepend NumericWithUnit::NumUtil
58
+ end
59
+
60
+ class Bignum
61
+ prepend NumericWithUnit::NumUtil
62
+ end
63
+
64
+ class Numeric
65
+ prepend NumericWithUnit::NumUtil
66
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: numeric_with_unit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - diaphragm
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: calclation numeric with unit!!!!
14
+ email:
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/numeric_with_unit.rb
20
+ - lib/numeric_with_unit/base_unit.rb
21
+ - lib/numeric_with_unit/cgs_unit.rb
22
+ - lib/numeric_with_unit/common_unit.rb
23
+ - lib/numeric_with_unit/imperial_unit.rb
24
+ - lib/numeric_with_unit/natural_unit.rb
25
+ - lib/numeric_with_unit/unit.rb
26
+ - lib/numeric_with_unit/util.rb
27
+ - lib/numeric_with_unit/util2.rb
28
+ homepage: https://github.com/diaphragm/ruby-numeric-with-unit
29
+ licenses:
30
+ - MIT
31
+ metadata: {}
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project:
48
+ rubygems_version: 2.2.2
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: calclation numeric with unit
52
+ test_files: []