quanty 1.1.0

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.
@@ -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