numeric_with_unit 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []