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.
- data/COPYING +340 -0
- data/ChangeLog +14 -0
- data/History.txt +6 -0
- data/Manifest.txt +18 -0
- data/README.en +56 -0
- data/README.txt +26 -0
- data/Rakefile +28 -0
- data/extconf.rb +51 -0
- data/lib/quanty/fact.rb +228 -0
- data/lib/quanty/main.rb +159 -0
- data/lib/quanty/parse.rb +852 -0
- data/lib/quanty.rb +7 -0
- data/mkdump.rb +4 -0
- data/parse.y +106 -0
- data/quanty-en.rd +107 -0
- data/quanty-ja.rd +102 -0
- data/test.rb +14 -0
- data/units.dat +3549 -0
- metadata +107 -0
data/lib/quanty/fact.rb
ADDED
@@ -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
|
data/lib/quanty/main.rb
ADDED
@@ -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
|