radix-firstbanco 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,217 @@
1
+ require 'radix/base'
2
+
3
+ module Radix
4
+
5
+ # Radix separator used in string and array representations.
6
+ DOT = '.'
7
+
8
+ # Division character for rational numbers
9
+ DIV = '/'
10
+
11
+ # Default seperator character.
12
+ DIVIDER = " "
13
+
14
+ ##
15
+ # Radix::Numeric class inherits from Ruby's Numeric class. It is then
16
+ # subclassed by Radix::Integer and Radix::Float.
17
+ #
18
+ # @todo Make immutable, but best way to do it?
19
+ # @example First suggestion
20
+ # class << self
21
+ # alias_method :_new, :new
22
+ # private :_new
23
+ # end
24
+ # @example Second suggestion
25
+ # def self.new(value, base=10)
26
+ # @cache ||= {}
27
+ # @cache[[value, base]] ||= _new(value, base)
28
+ # end
29
+ class Numeric < ::Numeric
30
+
31
+ ##
32
+ # Addition, binary operation.
33
+ #
34
+ # @param [Radix::Numeric] other
35
+ #
36
+ # @return [Radix::Numeric] Result of arithmetic operation.
37
+ def +(other)
38
+ operation(:+, other)
39
+ end
40
+
41
+ ##
42
+ # Subtraction, binary operation.
43
+ #
44
+ # @param [Radix::Numeric] other
45
+ #
46
+ # @return [Radix::Numeric] Result of arithmetic operation.
47
+ def -(other)
48
+ operation(:-, other)
49
+ end
50
+
51
+ ##
52
+ # Multiplication, binary operation.
53
+ #
54
+ # @param [Radix::Numeric] other
55
+ #
56
+ # @return [Radix::Numeric] Result of arithmetic operation.
57
+ def *(other)
58
+ operation(:*, other)
59
+ end
60
+
61
+ ##
62
+ # Division, binary operation.
63
+ #
64
+ # @param [Radix::Numeric] other
65
+ #
66
+ # @return [Radix::Numeric] Result of arithmetic operation.
67
+ def /(other)
68
+ operation(:/, other)
69
+ end
70
+
71
+ private
72
+
73
+ ##
74
+ # Parses the value of the base and character set to use.
75
+ #
76
+ # @param [Fixnum, Array<String>] base
77
+ # The value of the base, or a set of characters to use as
78
+ # representation of the base.
79
+ #
80
+ # @note If an array of String characters is passed, its length is the
81
+ # value of the base level.
82
+ #
83
+ # @return [Array<(Fixnum, [Array<String>, nil])>] Two part array:
84
+ # 0 - Fixnum value of the base.
85
+ # 1 - Nil, or Array of characters representing the base values.
86
+ def parse_base(base)
87
+ case base
88
+ when Array
89
+ code = base
90
+ base = base.size
91
+ else
92
+ code = nil
93
+ base = base
94
+ end
95
+ return base, code
96
+ end
97
+
98
+ ##
99
+ # Simply returns the passed value. Used for simplifying creation of
100
+ # Radix::Numeric instances.
101
+ #
102
+ # @param [Radix::Float, Radix::Integer] value Given value.
103
+ # @param [Fixnum, Array<String>] base Desired base.
104
+ #
105
+ # @return [Radix::Float, Radix::Integer] The passed value.
106
+ def parse_numeric(value, base)
107
+ value
108
+ end
109
+
110
+ ##
111
+ # If a float style string is passed in for +value+, e.g. "9.5", the
112
+ # decimal will simply be truncated. So "9.x" would become "9".
113
+ #
114
+ # @param [String] value
115
+ # Given value.
116
+ #
117
+ # @param [Fixnum, Array<String>] base
118
+ # Desired base.
119
+ #
120
+ # @return [Radix::Float, Radix::Integer] The passed value.
121
+ def parse_string(value, base)
122
+ digits = value.split(//)
123
+ parse_array(digits, base)
124
+ end
125
+
126
+ ##
127
+ # Take an Array in the form of [d1, d2, ..., DOT, d-1, d-2, ...]
128
+ # and convert it to base ten, and store in @value.
129
+ #
130
+ # @param [Array<String, Numeric>] value Given value.
131
+ # @param [Fixnum, Array<String>] base Desired base.
132
+ #
133
+ # @return [Fixnum] Decimal version of passed array in base context.
134
+ def parse_array(value, base)
135
+ value = value.dup
136
+
137
+ if value.first == '-'
138
+ neg = true
139
+ value.shift
140
+ else
141
+ neg = false
142
+ end
143
+
144
+ value = base_decode(value)
145
+
146
+ ## raise an error if any digit is not less than base
147
+ raise ArgumentError if value.any?{ |e| ::Numeric === e && base < e }
148
+
149
+ v = decimal(value, base)
150
+
151
+ neg ? -v : v # Returns negated v if value array.first == "-"
152
+ end
153
+
154
+ ##
155
+ # Convert array of values of a different base to decimal. This handles
156
+ # integer values. The method for Radix::Float is slighly different.
157
+ #
158
+ # @param [Array<Numeric, String>] digits
159
+ # Representation of base values.
160
+ #
161
+ # @param [Fixnum, Array<String>] base
162
+ # The base to convert from.
163
+ #
164
+ # @return [Integer] The digits of base converted to decimal.
165
+ def decimal(digits, base)
166
+ e = digits.size - 1
167
+ v = 0
168
+ digits.each do |n|
169
+ v += n.to_i * base**e
170
+ e -= 1
171
+ end
172
+ v
173
+ end
174
+
175
+ ##
176
+ # Map array of values to base encoding. If no encoding is defined
177
+ # this simply returns the +digits+ unchanged.
178
+ #
179
+ # @param [Array<String, Numeric>] digits
180
+ #
181
+ # @return [Array<String, Fixnum>] Encoded digits, or digits if @code is nil
182
+ def base_encode(digits)
183
+ return digits unless @code
184
+ digits.map do |i|
185
+ case i
186
+ when '-', DOT, DIV
187
+ i
188
+ else
189
+ code[i]
190
+ end
191
+ end
192
+ end
193
+
194
+ ##
195
+ # Decode an encoded array. Defaults to BASE::B62 if self.code is not set.
196
+ #
197
+ # @param [Array<String, Numeric>] digits The encoded characters.
198
+ #
199
+ # @return [Array<String, Numeric>] Decoded array.
200
+ def base_decode(digits)
201
+ #return digits unless code
202
+ code = self.code || BASE::B62
203
+ digits.map do |c|
204
+ case c
205
+ when '-', DOT, DIV
206
+ c
207
+ when ::Numeric
208
+ c
209
+ else
210
+ code.index(c) # TODO: Could speed up with an reverse index.
211
+ end
212
+ end
213
+ end
214
+
215
+ end
216
+
217
+ end
@@ -0,0 +1,311 @@
1
+ require 'rational'
2
+ require 'radix/numeric'
3
+
4
+ module Radix
5
+
6
+ ##
7
+ # Represents rational numbers.
8
+ #
9
+ # @!attribute [r] value
10
+ # @return [Rational] Ruby Rational representation of self in Base-10.
11
+ # @!attribute [r] base
12
+ # @return [Fixnum] The base level of Rational instance.
13
+ # @!attribute [r] code
14
+ # @return [Array<String>, nil] Substitution chars or nil if default.
15
+ class Rational < Numeric
16
+
17
+ ##
18
+ # Alternative to #new.
19
+ #
20
+ # @return [Radix::Rational]
21
+ def self.[](n,d=nil,b=10)
22
+ new(n,d,b)
23
+ end
24
+
25
+ ##
26
+ # Stores the Rational value in Base-10.
27
+ #
28
+ # @return [Rational]
29
+ attr :value
30
+
31
+ ##
32
+ # Base of the number.
33
+ #
34
+ # @return [Fixnum] The base level of Rational instance.
35
+ attr :base
36
+
37
+ ##
38
+ # Base encoding table.
39
+ #
40
+ # @return [Array<String>, nil] Substitution chars or nil if default.
41
+ attr :code
42
+
43
+ private
44
+
45
+ ##
46
+ # Create a new Radix::Rational instance.
47
+ # @example
48
+ # Rational.new(<Integer>, <Integer>, <Integer>)
49
+ # Rational.new(<Rational>, <Integer>)
50
+ # @param [Radix::Rational, ::Rational, Fixnum] numerator A rational number
51
+ # or a fixnum for the numerator of a new Rational.
52
+ # @param [Fixnum] denominator Denominator for new Rational.
53
+ # @param [Fixnum] base Base level for new Rational.
54
+ # @return [void]
55
+ def initialize(numerator, denominator=nil, base=10)
56
+ case numerator
57
+ when ::Rational, Rational
58
+ ratn = numerator
59
+ base = denominator
60
+ @value = Rational(ratn.numerator, ratn.denominator)
61
+ else
62
+ n = parse_value(numerator, base)
63
+ d = parse_value(denominator, base)
64
+ @value = Rational(n, d)
65
+ end
66
+ @base, @code = parse_base(base)
67
+ end
68
+
69
+ ##
70
+ # Parses String, Array, Radix::Float, Radix::Integer or Ruby numerics and
71
+ # returns the decimal value from base context for storage in @value.
72
+ #
73
+ # @param [Fixnum] base
74
+ def parse_value(value, base)
75
+ case value
76
+ when Float, Integer # Radix
77
+ parse_numeric(value.to_i, base)
78
+ when ::Array
79
+ parse_array(value, base)
80
+ when ::String
81
+ parse_string(value, base)
82
+ when ::Numeric
83
+ parse_numeric(value.to_i, base)
84
+ end
85
+ end
86
+
87
+ public
88
+
89
+ ##
90
+ # The numerator.
91
+ #
92
+ # @return [Fixnum] The numerator of Radix::Rational
93
+ def numerator
94
+ @value.numerator
95
+ end
96
+
97
+ ##
98
+ # The denominator.
99
+ #
100
+ # @return [Fixnum] The denominator of Radix::Rational
101
+ def denominator
102
+ @value.denominator
103
+ end
104
+
105
+ ##
106
+ # Is the value negative?
107
+ #
108
+ # @return [Boolean] Returns true if value < 0.
109
+ def negative?
110
+ value < 0
111
+ end
112
+
113
+ ##
114
+ # Convert rational to new base.
115
+ #
116
+ # @param [Fixnum] base Desired base.
117
+ # @return [Radix::Rational] Returns new Radix::Rational in passed base.
118
+ def convert(base)
119
+ self.class.new(numerator, denominator, base)
120
+ end
121
+
122
+ ##
123
+ # Convert to rational.
124
+ #
125
+ # @return [Rational] Returns the value.
126
+ def to_r
127
+ value
128
+ end
129
+
130
+ ##
131
+ # Convert to Float by dividing the numerator by the denominator.
132
+ #
133
+ # Returns the converted value. [Float]
134
+ def to_f
135
+ numerator.to_f / denominator.to_f
136
+ end
137
+
138
+ ##
139
+ # Convert to Integer by converting to Float first then
140
+ # appling #to_i to the float.
141
+ #
142
+ # Returns the converted value. [Integer]
143
+ def to_i
144
+ to_f.to_i
145
+ end
146
+
147
+ ##
148
+ # Translate value into an array of places. Uses current base unless
149
+ # specified.
150
+ #
151
+ # @param [Fixnum] base Desired base.
152
+ # @return [Array<Fixnum, String>] Array of place values.
153
+ def to_a(base=nil)
154
+ if base
155
+ convert(base).digits_encoded
156
+ else
157
+ digits_encoded
158
+ end
159
+ end
160
+
161
+ ##
162
+ # Convert the value into a string representation of the given base.
163
+ #
164
+ # @param [Fixnum] base The base to convert.
165
+ # @param [String] divider The string char(s) to divided with.
166
+ # @return [String] Translated value.
167
+ def to_s(base=nil, divider=nil)
168
+ divider = divider.to_s if divider
169
+ if base
170
+ convert(base).to_s(nil, divider)
171
+ else
172
+ if code
173
+ digits_encoded.join(divider)
174
+ else
175
+ if @base > 10
176
+ digits.join(divider || DIVIDER)
177
+ else
178
+ digits.join(divider)
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ ##
185
+ # Simple equality requires equal values only.
186
+ #
187
+ # @todo This may need improvement to be more percise.
188
+ # @param [#to_f] other The value to compare to.
189
+ # @return [Boolean] True if equal values.
190
+ def ==(other)
191
+ a, b = self.to_f, other.to_f
192
+ a == b
193
+ end
194
+
195
+ ##
196
+ # Returns an irreducible version of self in current base.
197
+ #
198
+ # @todo Is this method neccesary since @value is a Ruby Rational and
199
+ # therefore already irreducible?
200
+ # @return [Radix::Rational]
201
+ def reduce
202
+ self.class.new(Rational(numerator, denominator), base)
203
+ end
204
+
205
+ ##
206
+ # Returns an array representation of the numerator and denominator with
207
+ # each column's value.
208
+ #
209
+ # @return [Array<String, Fixnum>] Values per column of @base as array.
210
+ # Prepended with "-" if negative.
211
+ def digits
212
+ n = base_conversion(numerator, base)
213
+ d = base_conversion(denominator, base)
214
+ i = n + ['/'] + d
215
+ i.unshift('-') if negative?
216
+ i
217
+ end
218
+
219
+ ##
220
+ # Returns digits, or coded version of digits if @code.
221
+ #
222
+ # @return [Array<String, Fixnum>] Values per column of @base as array.
223
+ # Prepended with "-" if negative. Or encoded version if @code is
224
+ # defined.
225
+ def digits_encoded
226
+ base_encode(digits)
227
+ end
228
+
229
+ ##
230
+ # Creates a string representation of self.
231
+ #
232
+ # @return [String] String rep of self.digits and @base.
233
+ def inspect
234
+ "#{digits.join(' ')} (#{base})"
235
+ end
236
+
237
+ ##
238
+ # Returns new Radix::Rational of passed value in base-10 and self as an
239
+ # Array.
240
+ #
241
+ # @return [Array<(Radix::Rational, Radix::Rational)>]
242
+ def coerce(value)
243
+ [Radix::Rational.new(value), self]
244
+ end
245
+
246
+ private
247
+
248
+ ##
249
+ # Perform passed arithmetic operation.
250
+ #
251
+ # @param [#to_r] other
252
+ # @return [Radix::Rational] Returns the result of the operation in @base.
253
+ def operation(op, other)
254
+ x = value.__send__(op, other.to_r)
255
+ self.class.new(x, base)
256
+ end
257
+
258
+ ##
259
+ # Perform base conversion.
260
+ #
261
+ # @return [Array<Fixnum, String>] Array of places.
262
+ def base_conversion(value, base)
263
+ #if value < 0
264
+ # @negative, value = true, value.abs
265
+ #end
266
+ i = value.abs
267
+
268
+ a = []
269
+ while i > 0
270
+ i, r = i.divmod(base)
271
+ a << r
272
+ end
273
+
274
+ a << 0 if a.empty?
275
+
276
+ a.reverse
277
+ end
278
+
279
+ end
280
+
281
+ end
282
+
283
+ class ::Array
284
+ ##
285
+ # Adds convenience method for creating a Radix::Rational.
286
+ #
287
+ # @todo Keep #br? Or find another way?
288
+ # @return [Radix::Rational]
289
+ def br(base=nil)
290
+ args = dup
291
+ args << base if base
292
+ Radix::Rational.new(*args)
293
+ end
294
+ end
295
+
296
+ if RUBY_VERSION < '1.9'
297
+ class ::Float
298
+
299
+ ##
300
+ # Adds a #to_r method to pre-1.9 ruby Rationals.
301
+ #
302
+ # @return [Rational]
303
+ def to_r
304
+ n, f = to_s.split('.')
305
+ d = (10 ** f.size).to_i
306
+ n = (n.to_i * d) + f.to_i
307
+ Rational(n, d)
308
+ end
309
+ end
310
+ end
311
+