radix 1.1.0 → 2.0.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/{HISTORY → HISTORY.rdoc} +14 -0
- data/LICENSE +205 -789
- data/README.rdoc +29 -28
- data/doc/01_synopsis.rdoc +60 -0
- data/doc/02_integer.rdoc +48 -0
- data/doc/03_float.rdoc +36 -0
- data/doc/04_rational.rdoc +27 -0
- data/{qed/radix_base.rdoc → doc/05_base.rdoc} +35 -24
- data/{qed → doc}/applique/ae.rb +0 -0
- data/doc/applique/qed.rb +1 -0
- data/lib/radix.rb +42 -1
- data/lib/radix/base.rb +81 -85
- data/lib/radix/float.rb +263 -0
- data/lib/radix/integer.rb +263 -0
- data/lib/radix/meta/package +2 -2
- data/lib/radix/meta/profile +5 -5
- data/lib/radix/numeric.rb +144 -0
- data/lib/radix/operator.rb +0 -30
- data/lib/radix/rational.rb +203 -0
- data/meta/package +2 -2
- data/meta/profile +5 -5
- data/test/02_integer.rdoc +256 -0
- data/test/03_float.rdoc +294 -0
- data/test/04_rational.rdoc +84 -0
- data/test/05_base.rdoc +78 -0
- data/test/applique/ae.rb +1 -0
- data/test/applique/qed.rb +1 -0
- metadata +25 -12
- data/lib/radix/number.rb +0 -84
- data/qed/radix_operator.rdoc +0 -27
data/lib/radix/float.rb
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'radix/numeric'
|
2
|
+
|
3
|
+
module Radix
|
4
|
+
|
5
|
+
# Radix::Float is simple a Ruby Float class that can handle bases.
|
6
|
+
#
|
7
|
+
# TODO: Make fully immutable. After that we can catch @digits and
|
8
|
+
# the library should be a good bit faster.
|
9
|
+
class Float < Numeric
|
10
|
+
|
11
|
+
# Internal floating point value.
|
12
|
+
attr :value
|
13
|
+
|
14
|
+
# Base of the number.
|
15
|
+
attr :base
|
16
|
+
|
17
|
+
# Base encoding table.
|
18
|
+
attr :code
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
#
|
23
|
+
def initialize(value, base=10)
|
24
|
+
@value = parse_value(value, base)
|
25
|
+
@base, @code = parse_base(base)
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
def parse_value(value, base)
|
30
|
+
case value
|
31
|
+
when Float, Integer # Radix
|
32
|
+
parse_numeric(value.to_f, base)
|
33
|
+
when ::Array
|
34
|
+
parse_array(value, base)
|
35
|
+
when ::String
|
36
|
+
parse_string(value, base)
|
37
|
+
when ::Numeric
|
38
|
+
parse_numeric(value.to_f, base)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
public
|
43
|
+
|
44
|
+
#
|
45
|
+
def to_i
|
46
|
+
to_f.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
alias_method :to_int, :to_i
|
51
|
+
|
52
|
+
#
|
53
|
+
def to_f
|
54
|
+
value.to_f
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
def to_a(base=nil)
|
59
|
+
if base
|
60
|
+
convert(base).digits_encoded
|
61
|
+
else
|
62
|
+
digits_encoded
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
def to_s(base=nil, divider=nil)
|
68
|
+
divider = divider.to_s if divider
|
69
|
+
if base
|
70
|
+
convert(base).to_s(nil, divider)
|
71
|
+
else
|
72
|
+
if code
|
73
|
+
digits_encoded.join(divider)
|
74
|
+
else
|
75
|
+
if @base > 10
|
76
|
+
digits.join(divider || DIVIDER)
|
77
|
+
else
|
78
|
+
digits.join(divider)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
def inspect
|
86
|
+
"#{digits.join(' ')} (#{base})"
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
def digits
|
91
|
+
i, f = base_conversion(value, base)
|
92
|
+
if negative?
|
93
|
+
['-'] + i + [DOT] + f
|
94
|
+
else
|
95
|
+
i + [DOT] + f
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
def digits_encoded
|
101
|
+
base_encode(digits)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns true if the number negative?
|
105
|
+
def negative?
|
106
|
+
value < 0
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
def convert(new_base)
|
111
|
+
self.class.new(value, new_base)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Power
|
115
|
+
def **(other)
|
116
|
+
operation(:**, other)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Modulo
|
120
|
+
def %(other)
|
121
|
+
operation(:%, other)
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
alias_method :modulo, :%
|
126
|
+
|
127
|
+
#
|
128
|
+
def abs
|
129
|
+
self.class.new(value.abs, base)
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
def ceil
|
134
|
+
self.class.new(value.ceil, base)
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
def floor
|
139
|
+
self.class.new(value.floor, base)
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
def round
|
144
|
+
return self.class.new((value + 0.5).floor, base) if self > 0.0
|
145
|
+
return self.class.new((value - 0.5).ceil, base) if self < 0.0
|
146
|
+
return self.class.new(0, base)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Strict equality requires same class as well as value.
|
150
|
+
def eql?(num)
|
151
|
+
self.class.equal?(num.class) && self == num
|
152
|
+
end
|
153
|
+
|
154
|
+
# Simple equality requires equal values only.
|
155
|
+
def ==(other)
|
156
|
+
case other
|
157
|
+
when Float, Integer # Radix
|
158
|
+
value == other.value
|
159
|
+
else
|
160
|
+
value == other
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
#
|
165
|
+
def <=>(other)
|
166
|
+
to_f <=> other.to_f
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
#def infinite?
|
171
|
+
# digits[0,2] == [0, DOT]
|
172
|
+
#end
|
173
|
+
|
174
|
+
#
|
175
|
+
#def finite?
|
176
|
+
# !infinite
|
177
|
+
#end
|
178
|
+
|
179
|
+
#
|
180
|
+
#def nan?
|
181
|
+
# digits[-2,2] == [DOT, 0]
|
182
|
+
#end
|
183
|
+
|
184
|
+
#
|
185
|
+
def coerce(o)
|
186
|
+
[Radix::Float.new(o), self]
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
# Perform arthmetic operation.
|
192
|
+
def operation(op, other)
|
193
|
+
a = self.to_f
|
194
|
+
b = other.to_f
|
195
|
+
x = a.__send__(op, b)
|
196
|
+
Radix::Float.new(x, base)
|
197
|
+
end
|
198
|
+
|
199
|
+
#
|
200
|
+
def base_conversion(value, base, prec=10)
|
201
|
+
#if value < 0
|
202
|
+
# @negative, value = true, value.abs
|
203
|
+
#end
|
204
|
+
value = value.to_f.abs
|
205
|
+
|
206
|
+
i, f = split_float(value)
|
207
|
+
|
208
|
+
a = []
|
209
|
+
while i > 0
|
210
|
+
i, r = i.divmod(base)
|
211
|
+
a << r
|
212
|
+
end
|
213
|
+
|
214
|
+
#c = [] # f-cache
|
215
|
+
p = prec
|
216
|
+
b = []
|
217
|
+
while !f.zero?
|
218
|
+
k = (f * base)
|
219
|
+
r, f = split_float(k)
|
220
|
+
#c.include?(f) ? break : c << f
|
221
|
+
break if p == 0; p -= 1
|
222
|
+
b << r
|
223
|
+
end
|
224
|
+
|
225
|
+
b << 0 if b.empty?
|
226
|
+
|
227
|
+
[a.reverse, b]
|
228
|
+
end
|
229
|
+
|
230
|
+
#
|
231
|
+
def decimal(digits, base)
|
232
|
+
i, f = split_digits(digits)
|
233
|
+
e = i.size - 1
|
234
|
+
v = 0
|
235
|
+
(i + f).each do |n|
|
236
|
+
v += n * base**e
|
237
|
+
e -= 1
|
238
|
+
end
|
239
|
+
v
|
240
|
+
end
|
241
|
+
|
242
|
+
#
|
243
|
+
def split_digits(value)
|
244
|
+
if d = value.index(DOT) || value.index('.')
|
245
|
+
i, f = value[0...d], value[d+1..-1]
|
246
|
+
else
|
247
|
+
i, f = value, [0]
|
248
|
+
end
|
249
|
+
i.map!{ |x| x.to_i }
|
250
|
+
f.map!{ |x| x.to_i }
|
251
|
+
return i, f
|
252
|
+
end
|
253
|
+
|
254
|
+
#
|
255
|
+
def split_float(float)
|
256
|
+
i, f = float.to_s.split('.')
|
257
|
+
return i.to_i, ('0.'+f).to_f
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
|
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'radix/numeric'
|
2
|
+
|
3
|
+
module Radix
|
4
|
+
|
5
|
+
#
|
6
|
+
class Integer < Numeric
|
7
|
+
|
8
|
+
# Stores the numerc value as normal number.
|
9
|
+
attr :value
|
10
|
+
|
11
|
+
# Base of the number.
|
12
|
+
attr :base
|
13
|
+
|
14
|
+
# Base encoding table.
|
15
|
+
attr :code
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
#
|
20
|
+
def initialize(value, base=10)
|
21
|
+
@value = parse_value(value, base)
|
22
|
+
@base, @code = parse_base(base)
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
def parse_value(value, base)
|
27
|
+
case value
|
28
|
+
when Integer, Float # Radix
|
29
|
+
parse_numeric(value.to_i, base)
|
30
|
+
when ::Array
|
31
|
+
parse_array(value, base)
|
32
|
+
when ::String
|
33
|
+
parse_string(value, base)
|
34
|
+
when ::Numeric
|
35
|
+
parse_numeric(value, base)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Take an Array in the form of [..., d2, d1, d0] and convert it to
|
40
|
+
# base ten, and store in @value.
|
41
|
+
#
|
42
|
+
# If a float style array is passed in for +value+, e.g. [9, '.', 5],
|
43
|
+
# the fractional part will simply be truncated.
|
44
|
+
def parse_array(value, base)
|
45
|
+
if i = value.index(DOT)
|
46
|
+
value = [0...i]
|
47
|
+
end
|
48
|
+
super(value, base)
|
49
|
+
end
|
50
|
+
|
51
|
+
## digits << #Radix.convert(d, base, 10).to_i
|
52
|
+
|
53
|
+
public
|
54
|
+
|
55
|
+
#
|
56
|
+
def to_i
|
57
|
+
value.to_i #(sign + convert(10).digits.join('')).to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
alias_method :to_int, :to_i
|
62
|
+
|
63
|
+
#
|
64
|
+
def to_f
|
65
|
+
value.to_f #(sign + convert(10).digits.join('')).to_f
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
def to_a(base=nil)
|
70
|
+
if base
|
71
|
+
convert(base).digits_encoded
|
72
|
+
else
|
73
|
+
digits_encoded
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
def to_s(base=nil, divider=nil)
|
79
|
+
divider = divider.to_s if divider
|
80
|
+
if base
|
81
|
+
convert(base).to_s(nil, divider)
|
82
|
+
else
|
83
|
+
if code
|
84
|
+
digits_encoded.join(divider)
|
85
|
+
else
|
86
|
+
if @base > 10
|
87
|
+
digits.join(divider || DIVIDER)
|
88
|
+
else
|
89
|
+
digits.join(divider)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
def inspect
|
97
|
+
"#{digits.join(' ')} (#{base})"
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
def digits
|
102
|
+
i = base_conversion(value, base)
|
103
|
+
i.unshift('-') if negative?
|
104
|
+
i
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
def digits_encoded
|
109
|
+
base_encode(digits)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns true if the number is negative.
|
113
|
+
def negative?
|
114
|
+
value < 0
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
def convert(base)
|
119
|
+
self.class.new(value, base)
|
120
|
+
#new_digits = Radix::Base.convert_base(digits, base, new_base)
|
121
|
+
#self.class.new(new_digits, new_base)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Addition
|
125
|
+
def +(other)
|
126
|
+
operation(:+, other)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Subtraction
|
130
|
+
def -(other)
|
131
|
+
operation(:-, other)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Multiplication
|
135
|
+
def *(other)
|
136
|
+
operation(:*, other)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Division
|
140
|
+
def /(other)
|
141
|
+
operation(:/, other)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Power
|
145
|
+
def **(other)
|
146
|
+
operation(:**, other)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Modulo
|
150
|
+
def %(other)
|
151
|
+
operation(:%, other)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Asymmetric binary shift operator.
|
155
|
+
def <<(o)
|
156
|
+
Radix::Integer.new(to_int << o.to_int, base)
|
157
|
+
end
|
158
|
+
|
159
|
+
# AND bit operator
|
160
|
+
def &(o)
|
161
|
+
Radix::Integer.new(to_int & o.to_int, base)
|
162
|
+
end
|
163
|
+
|
164
|
+
#
|
165
|
+
def abs
|
166
|
+
self.class.new(value.abs, base)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Strict equality requires same class as well as value.
|
170
|
+
def eql?(num)
|
171
|
+
self.class.equal?(num.class) && self == num
|
172
|
+
end
|
173
|
+
|
174
|
+
# Simple equality requires equal values only.
|
175
|
+
# TODO: Handle Float and Radix::Float.
|
176
|
+
def ==(other)
|
177
|
+
case other
|
178
|
+
when Float, Integer # Radix
|
179
|
+
value == other.value
|
180
|
+
else
|
181
|
+
value == other
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
def <=>(other)
|
187
|
+
value <=> other.to_f # to_num
|
188
|
+
end
|
189
|
+
|
190
|
+
#
|
191
|
+
def coerce(value)
|
192
|
+
[Radix::Integer.new(value), self]
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
|
197
|
+
# Perform arthmetic operation.
|
198
|
+
def operation(op, other)
|
199
|
+
a = self.to_i
|
200
|
+
b = other.to_i
|
201
|
+
x = a.__send__(op, b)
|
202
|
+
self.class.new(x, base)
|
203
|
+
end
|
204
|
+
|
205
|
+
#
|
206
|
+
def base_conversion(value, base)
|
207
|
+
#if value < 0
|
208
|
+
# @negative, value = true, value.abs
|
209
|
+
#end
|
210
|
+
i = value.abs
|
211
|
+
|
212
|
+
a = []
|
213
|
+
while i > 0
|
214
|
+
i, r = i.divmod(base)
|
215
|
+
a << r
|
216
|
+
end
|
217
|
+
|
218
|
+
a.reverse
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
### Want to see the hard way to do addition?
|
226
|
+
##
|
227
|
+
## def +(other)
|
228
|
+
## other = convert_other(other)
|
229
|
+
##
|
230
|
+
## result = []
|
231
|
+
## index = -1
|
232
|
+
## carry = 0
|
233
|
+
##
|
234
|
+
## x1 = self.digits[index]
|
235
|
+
## x2 = other.digits[index]
|
236
|
+
## while x1 or x2
|
237
|
+
## r = (x1||0) + (x2||0) + carry
|
238
|
+
## if r >= base
|
239
|
+
## result.unshift(r - base)
|
240
|
+
## carry = 1
|
241
|
+
## else
|
242
|
+
## result.unshift(r)
|
243
|
+
## carry = 0
|
244
|
+
## end
|
245
|
+
## index -= 1
|
246
|
+
## x1 = self.digits[index]
|
247
|
+
## x2 = other.digits[index]
|
248
|
+
## end
|
249
|
+
## result << carry if carry != 0
|
250
|
+
## Radix::Integer.new(result, base)
|
251
|
+
## end
|
252
|
+
##
|
253
|
+
## def convert_other(other)
|
254
|
+
## case other
|
255
|
+
## when Radix::Integer
|
256
|
+
## other.convert(base)
|
257
|
+
## when Integer
|
258
|
+
## Radix::Integer.new(other, base)
|
259
|
+
## when String, Array
|
260
|
+
## Radix::Integer.new(other, base)
|
261
|
+
## end
|
262
|
+
## end
|
263
|
+
##
|