quanty 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,228 @@
1
+ #
2
+ # quanty/fact.rb
3
+ #
4
+ # Copyright (c) 2001 Masahiro Tanaka <masa@ir.isas.ac.jp>
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 2 or later.
9
+
10
+ class Quanty
11
+
12
+ class Fact < Hash
13
+ Self = self
14
+ Parser = Parse.new
15
+
16
+ attr_reader :factor
17
+
18
+ # Basic units: Fact.new("m",true) => {"m"=>1}
19
+ # Derivative units: Fact.new("km") => 1000*{"m"=>1}
20
+ def initialize(key=nil,base=false)
21
+ self.default = 0.0
22
+ @factor = 1.0
23
+ case key
24
+ when Numeric
25
+ @factor = key
26
+ when String
27
+ if base
28
+ store(key, 1.0)
29
+ else
30
+ decomp(key)
31
+ end
32
+ when Self
33
+ replace(key)
34
+ end
35
+ end
36
+
37
+ def []=(key,val)
38
+ if val == 0
39
+ delete(key)
40
+ else
41
+ super(key,val)
42
+ end
43
+ end
44
+
45
+ def replace(other)
46
+ @factor = other.factor
47
+ super(other)
48
+ end
49
+
50
+ def dup
51
+ Fact.new(self)
52
+ end
53
+
54
+ def find_prefix(a,n)
55
+ Prefix.each{ |key,factor|
56
+ if /^#{key}-?/u =~ a && (unit = List[b=$']) && b.size>n
57
+ #p [a,b,factor]
58
+ return Fact.new(b).fac!(factor)
59
+ end
60
+ }
61
+ nil
62
+ end
63
+
64
+ def decomp(a)
65
+ if /^([µA-Za-z_]+([A-Za-z_0-9-]+[A-Za-z_])?)$|^[$%'"]'?$/ou =~ a
66
+ #if /^[A-Za-z_0-9$%-]+$/o =~ a
67
+ unit = List[a] || find_prefix(a,0) ||
68
+ if a.size>3 && /chs$/ou !~ a && /(.*[a-rt-y])s$/ou =~ a
69
+ b = $1
70
+ List[b] || find_prefix(b,2) ||
71
+ if a.size>4 && /(.*s|.*z|.*ch)es$/ou =~ a
72
+ b = $1
73
+ List[b] || find_prefix(b,2)
74
+ end
75
+ end
76
+ else
77
+ unit = Parser.parse(a)
78
+ end
79
+ unless unit
80
+ raise "`%s': unknown unit"%a
81
+ end
82
+ @factor *= factor if factor
83
+ mul!(unit)
84
+ end
85
+
86
+ def mul!(other)
87
+ raise unless other.kind_of?(Fact)
88
+ other.each{ |key,val| self[key] += val }
89
+ delete_if{ |key,val| val == 0 }
90
+ @factor *= other.factor
91
+ self
92
+ end
93
+
94
+ def * (other)
95
+ dup.mul!(other)
96
+ end
97
+
98
+ def div!(other)
99
+ raise unless other.kind_of?(Fact)
100
+ other.each{ |key,val| self[key] -= val }
101
+ delete_if{ |key,val| val == 0 }
102
+ @factor /= other.factor
103
+ self
104
+ end
105
+
106
+ def / (other)
107
+ dup.div!(other)
108
+ end
109
+
110
+ def pow!(other)
111
+ raise unless other.kind_of?(Numeric)
112
+ each{ |key,val| self[key] = other*val }
113
+ @factor **= other
114
+ self
115
+ end
116
+
117
+ def ** (other)
118
+ dup.pow!(other)
119
+ end
120
+
121
+ def fac!(other)
122
+ raise unless other.kind_of?(Numeric)
123
+ @factor *= other
124
+ self
125
+ end
126
+
127
+ def inspect
128
+ @factor.to_s+"*"+super
129
+ end
130
+
131
+ def to_s
132
+ a = []
133
+ each{|k,v|
134
+ if v != 1
135
+ v = v.to_i if v%1 == 0
136
+ k += v.to_s
137
+ end
138
+ a.push k
139
+ }
140
+ @factor.to_s+" "+a.join(" ")
141
+ end
142
+
143
+ def null?
144
+ each_value{ |val| return false if val != 0 }
145
+ true
146
+ end
147
+
148
+ alias __equal__ :==
149
+
150
+ def ==(other)
151
+ if other.kind_of?(Numeric)
152
+ null? && @factor==other
153
+ else
154
+ __equal__(other) && @factor==other.factor
155
+ end
156
+ end
157
+
158
+ # check only dimension
159
+ def ===(other)
160
+ if other.kind_of?(Numeric)
161
+ null?
162
+ else
163
+ __equal__(other)
164
+ end
165
+ end
166
+
167
+ def to_f
168
+ raise inspect + ": not null unit" unless null?
169
+ @factor
170
+ end
171
+
172
+ class << self
173
+ def mkdump filename
174
+ Prefix.clear
175
+ List.clear
176
+ #s = open("units.succ","w")
177
+ #f = open("units.fail","w")
178
+ open("units.dat","r").readlines.each do |str|
179
+ if /^([µA-Za-z_0-9%$"'-]+)\s+([^#]+)/u =~ str
180
+ name,repr = $1,$2.strip
181
+ # conversion due to the different rule from GNU units:
182
+ # A / B C => A / (B C)
183
+ if /\//u =~ repr #/
184
+ pre,suf = $`,$'.strip
185
+ if /\s/u =~ suf
186
+ repr = pre + ' / (' + suf + ')'
187
+ end
188
+ end
189
+ if repr=="!"
190
+ List[name] = Fact.new(name,true).freeze
191
+ elsif /-$/u =~ name
192
+ Prefix[name[0..-2]] = Prefix[repr] || (List[repr] || repr).to_f
193
+ else
194
+ #p [name,repr]
195
+ List[name] = Fact.new(repr).freeze
196
+ end
197
+ #s.print str
198
+ #rescue
199
+ #f.print str
200
+ end
201
+ end
202
+ #Prefix.each{|key,val| p [key,val]}
203
+ #List.each{|key,val| p [key,val]}
204
+ Marshal.dump( [Prefix, List], open(filename,"w") )
205
+ end
206
+ end
207
+
208
+ end # class Fact
209
+
210
+
211
+ ### Loading unit data ###
212
+ =begin
213
+ fn = nil
214
+ $:.each{ |dir|
215
+ fn = dir + "/quanty/units.dump"
216
+ break if FileTest.exist?( fn )
217
+ fn = nil
218
+ }
219
+ =end
220
+ fn = File.expand_path('units.dump', File.dirname(__FILE__))
221
+ fn = nil unless FileTest.exist?( fn )
222
+ if fn
223
+ Prefix, List = Marshal.load(open(fn,"r"))
224
+ else
225
+ Prefix, List = {},{}
226
+ end
227
+
228
+ end # class Quanty
@@ -0,0 +1,159 @@
1
+ #
2
+ # quanty/main.rb
3
+ #
4
+ # Copyright (c) 2001 Masahiro Tanaka <masa@ir.isas.ac.jp>
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 2 or later.
9
+
10
+ # require 'fact.rb'
11
+
12
+ def Quanty(*a)
13
+ Quanty.new(*a)
14
+ end
15
+
16
+ class Quanty #< Numeric
17
+ Self = self
18
+ RadianUnit = Quanty::Fact.new('radian')
19
+
20
+ def initialize(*a)
21
+ case a.size
22
+ when 1
23
+ if String === a[0]
24
+ @val,@unit,@fact = 1.0, a[0], nil
25
+ else
26
+ @val,@unit,@fact = a[0], '', nil
27
+ end
28
+ when 2..3
29
+ @val,@unit,@fact = a
30
+ else
31
+ raise ArgumentError, 'wrong # of arguments'
32
+ end
33
+ unless Fact === @fact
34
+ @fact = Fact.new(@unit)
35
+ end
36
+ end
37
+
38
+ attr_reader :val
39
+ attr_reader :unit
40
+ attr_reader :fact
41
+ alias value val
42
+
43
+ def adjust(other)
44
+ if other.kind_of?(Self)
45
+ unless @fact === other.fact
46
+ raise "not same unit: %s != %s" % [@unit,other.unit]
47
+ end
48
+ other.val * ( other.fact.factor / @fact.factor )
49
+ else
50
+ raise @unit + ": not null unit" unless @fact.null?
51
+ other / @fact.factor
52
+ end
53
+ end
54
+
55
+ def want(unit)
56
+ obj = Self.new(unit)
57
+ val = obj.adjust(self)
58
+ Self.new( val, unit, obj.fact )
59
+ end
60
+
61
+ def + (other)
62
+ val = @val + adjust(other)
63
+ if @unit==''
64
+ val
65
+ else
66
+ Self.new( val, @unit, @fact )
67
+ end
68
+ end
69
+
70
+ def - (other)
71
+ val = @val - adjust(other)
72
+ if @unit==''
73
+ val
74
+ else
75
+ Self.new( val, @unit, @fact )
76
+ end
77
+ end
78
+
79
+ def +@ ; Self.new( @val, @unit, @fact ) end
80
+ def -@ ; Self.new( -@val, @unit, @fact ) end
81
+
82
+ def <=> (other); @val <=> adjust(other) end
83
+ def == (other); @val == adjust(other) end
84
+ def >= (other); @val >= adjust(other) end
85
+ def <= (other); @val <= adjust(other) end
86
+ def < (other); @val < adjust(other) end
87
+ def > (other); @val > adjust(other) end
88
+
89
+ def **(n)
90
+ if /^[A-Za-z_]+&/ou =~ @unit
91
+ unit = @unit+'^'+n.to_s
92
+ else
93
+ unit = '('+@unit+')^'+n.to_s+''
94
+ end
95
+ Self.new( @val**n, unit, @fact**n )
96
+ end
97
+
98
+ def * (other)
99
+ if other.kind_of?(Self)
100
+ unit = other.unit
101
+ unless @unit.empty?
102
+ if unit.empty?
103
+ unit = @unit
104
+ else
105
+ if /\A[A-Za-z_]/ou =~ unit
106
+ unit = @unit+' '+unit
107
+ else
108
+ unit = @unit+' ('+unit+')'
109
+ end
110
+ end
111
+ end
112
+ Self.new( @val*other.val, unit, @fact*other.fact )
113
+ else
114
+ Self.new( @val*other, @unit, @fact )
115
+ end
116
+ end
117
+
118
+ def / (other)
119
+ if other.kind_of?(Self)
120
+ unit = other.unit
121
+ if unit.empty?
122
+ unit = @unit
123
+ else
124
+ if /\A[A-Za-z_-]+((\^|\*\*)?[0-9.]+)?$/ou =~ unit
125
+ unit = '/ '+unit
126
+ else
127
+ unit = '/ ('+unit+')'
128
+ end
129
+ unit = @unit+' '+unit unless @unit.empty?
130
+ end
131
+ Self.new( @val/other.val, unit, @fact/other.fact )
132
+ else
133
+ Self.new( @val/other, @unit, @fact )
134
+ end
135
+ end
136
+
137
+ def coerce(other)
138
+ [ Self.new(other), self ]
139
+ end
140
+
141
+ def to_f
142
+ if @fact.null?
143
+ @val * @fact.factor
144
+ elsif @fact === RadianUnit
145
+ want('radian').value
146
+ else
147
+ raise 'cannot convert into non-dimensional Float'
148
+ end
149
+ end
150
+
151
+ def to_s
152
+ @val.to_s + "[" + @unit + "]"
153
+ end
154
+
155
+ def inspect
156
+ "Quanty(" + @val.to_s + ",'" + @unit + "')"
157
+ end
158
+
159
+ end