nio 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/nio/repdec.rb ADDED
@@ -0,0 +1,496 @@
1
+ # repdec.rb -- Repeating Decimals (Repeating Numerals, actually)
2
+
3
+ # Copyright (C) 2003-2005, Javier Goizueta <javier@goizueta.info>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License
7
+ # as published by the Free Software Foundation; either version 2
8
+ # of the License, or (at your option) any later version.
9
+
10
+ require 'nio/tools'
11
+ module Nio
12
+
13
+ class RepDecError <StandardError
14
+ end
15
+
16
+ class DigitsDef
17
+ include StateEquivalent
18
+ def initialize(ds='0123456789', cs=true)
19
+ @digits = ds
20
+ @casesens = cs
21
+ @dncase = (ds.downcase==ds)
22
+ @radix = @digits.size
23
+ end
24
+ def is_digit?(ch_code)
25
+ ch_code = set_case(ch_code) unless @casesens
26
+ @digits.include?(ch_code)
27
+ end
28
+ def digit_value(ch_code)
29
+ ch_code = set_case(ch_code) unless @casesens
30
+ @digits.index(ch_code)
31
+ end
32
+ def digit_char(v)
33
+ @digits[v]
34
+ end
35
+ def digit_char_safe(v)
36
+ v>=0 && v<@radix ? @digits[v] : nil
37
+ end
38
+ def radix
39
+ @radix
40
+ end
41
+ def DigitsDef.base(b,dncase=false,casesens=false)
42
+ dgs = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[0,b]
43
+ dgs.downcase! if dncase
44
+ DigitsDef.new(dgs,casesens)
45
+ end
46
+ private
47
+ def set_case(ch_code)
48
+ ch_code = ch_code.chr if ch_code.kind_of?(Numeric)
49
+ @dncase ? ch_code.downcase[0] : ch_code.upcase[0]
50
+ end
51
+ end
52
+
53
+
54
+ # RepDec handles repeating decimals (repeating numerals actually)
55
+ class RepDec
56
+ include StateEquivalent
57
+
58
+ class Opt # :nodoc:
59
+ include StateEquivalent
60
+ def initialize() #default options
61
+
62
+ @begin_rep = '<'
63
+ @end_rep = '>'
64
+
65
+ @auto_rep = '...'
66
+
67
+ @dec_sep = '.'
68
+ @grp_sep = ','
69
+ @grp = [] # [3] for thousands separators
70
+
71
+ @inf_txt = 'Infinity'
72
+ @nan_txt = 'NaN'
73
+
74
+ @digits = DigitsDef.new
75
+ @digits_defined = false
76
+
77
+ @max_d = 2048
78
+
79
+ end
80
+ attr_accessor :begin_rep, :end_rep, :auto_rep, :dec_sep, :grp_sep, :grp, :max_d
81
+ attr_accessor :nan_txt, :inf_txt
82
+
83
+ def set_delim(begin_d,end_d='')
84
+ @begin_rep = begin_d
85
+ @end_rep = end_d
86
+ return self
87
+ end
88
+ def set_suffix(a)
89
+ @auto_rep = a
90
+ return self
91
+ end
92
+ def set_sep(d)
93
+ @dec_sep = a
94
+ return self
95
+ end
96
+ def set_grouping(sep,g=[])
97
+ @grp_sep = a
98
+ @grp = g
99
+ return self
100
+ end
101
+ def set_special(nan_txt, inf_txt)
102
+ @nan_txt = nan_txt
103
+ @inf_txt = inf_txt
104
+ return self
105
+ end
106
+
107
+ def set_digits(ds, dncase=false, casesens=false)
108
+ if ds
109
+ @digits_defined = true
110
+ if ds.kind_of?(DigitsDef)
111
+ @digits = ds
112
+ elsif ds.kind_of?(Numeric)
113
+ @digits = DigitsDef.base(ds, dncase, casesens)
114
+ else
115
+ @digits = DigitsDef.new(ds,casesens)
116
+ end
117
+ else
118
+ @digits = DigitsDef.new
119
+ @digits_defined = false
120
+ end
121
+ self
122
+ end
123
+
124
+ attr_accessor :digits
125
+ def digits_defined?
126
+ @digits_defined
127
+ end
128
+
129
+ end
130
+
131
+ DEF_OPT=Opt.new
132
+
133
+
134
+ def initialize(b=10)
135
+ setZ(b)
136
+ end
137
+
138
+ def setZ(b=10)
139
+ @ip = 0;
140
+ @d = [];
141
+ @rep_i = nil;
142
+ @sign = 0;
143
+ @radix = b;
144
+ self
145
+ end
146
+
147
+ def setS(str, opt=DEF_OPT)
148
+ setZ(opt.digits_defined? ? opt.digits.radix : @radix);
149
+ sgn,i_str,f_str,ri,detect_rep = RepDec.parse(str,opt)
150
+ if i_str.kind_of?(Symbol)
151
+ @ip = i_str
152
+ else
153
+ @ip = i_str.to_i(@radix); # this assumes conventional digits
154
+ end
155
+ @sign = sgn
156
+ @rep_i = ri if ri
157
+ f_str.each_byte{|b| @d.push opt.digits.digit_value(b)} unless f_str.nil?
158
+
159
+ if detect_rep then
160
+
161
+ for l in 1..(@d.length/2)
162
+ l = @d.length/2 + 1 - l;
163
+ if @d[-l..-1]==@d[-2*l...-l]
164
+
165
+ for m in 1..l
166
+ if l.modulo(m)==0 then
167
+ reduce_l = true;
168
+ for i in 2..l/m
169
+ if @d[-m..-1]!=@d[-i*m...-i*m+m] then
170
+ reduce_l = false;
171
+ break;
172
+ end
173
+ end
174
+ if reduce_l then
175
+ l = m
176
+ break
177
+ end
178
+ end
179
+ end
180
+
181
+
182
+ @rep_i = @d.length - 2*l;
183
+ l.times { @d.pop }
184
+
185
+
186
+ while @d.length >= 2*l && @d[-l..-1]==@d[-2*l...-l]
187
+
188
+ @rep_i = @d.length - 2*l;
189
+ l.times { @d.pop }
190
+
191
+ end
192
+
193
+ break
194
+ end
195
+ end
196
+
197
+ end
198
+
199
+
200
+ if @rep_i!=nil then
201
+ if @d.length==@rep_i+1 && @d[@rep_i]==0 then
202
+ @rep_i = nil;
203
+ @d.pop;
204
+ end
205
+ end
206
+ @d.pop while @d[@d.length-1]==0
207
+
208
+ self
209
+ end
210
+
211
+ def RepDec.parse(str, opt=DEF_OPT)
212
+ sgn,i_str,f_str,ri,detect_rep = nil,nil,nil,nil,nil
213
+
214
+ i = 0;
215
+ l = str.length;
216
+
217
+ detect_rep = false;
218
+
219
+
220
+ i += 1 while i<str.length && str[i,1] =~/\s/
221
+
222
+
223
+ neg = false;
224
+
225
+ neg = true if str[i,1]=='-'
226
+ i += 1 if str[i,1]=='-' || str[i,1]=='+'
227
+
228
+
229
+ i += 1 while i<str.length && str[i,1] =~/\s/
230
+
231
+
232
+ str.upcase!
233
+ if str[i,opt.nan_txt.size]==opt.nan_txt.upcase
234
+ i_str = :indeterminate;
235
+ elsif str[i,opt.inf_txt.size]==opt.inf_txt.upcase
236
+ i_str = neg ? :neginfinity : :posinfinity;
237
+ end
238
+
239
+ unless i_str
240
+ i_str = "0";
241
+ while i<l && str[i,1]!=opt.dec_sep
242
+ break if str[i,opt.auto_rep.length]==opt.auto_rep && opt.auto_rep!=''
243
+ i_str += str[i,1] if str[i,1]!=opt.grp_sep
244
+ i += 1;
245
+ end
246
+ sgn = neg ? -1 : +1
247
+ i += 1; # skip the decimal separator
248
+ end
249
+
250
+ unless i_str.kind_of?(Symbol)
251
+ j = 0;
252
+ f_str = ''
253
+ while i<l
254
+ ch = str[i,1];
255
+ if ch==opt.begin_rep then
256
+ ri = j;
257
+ elsif ch==opt.end_rep then
258
+ i = l;
259
+ elsif ch==opt.auto_rep[0,1] then
260
+ detect_rep = true;
261
+ i = l;
262
+ else
263
+ f_str << ch
264
+ j += 1;
265
+ end
266
+ i += 1;
267
+ end
268
+ end
269
+ return [sgn,i_str,f_str,ri,detect_rep]
270
+ end
271
+
272
+ def getS(nrep=0, opt=DEF_OPT)
273
+ raise RepDecError,"Base mismatch: #{opt.digits.radix} when #{@radix} was expected." if opt.digits_defined? && @radix!=opt.digits.radix
274
+
275
+ if !ip.is_a?(Integer) then
276
+ str=opt.nan_txt if ip==:indeterminate;
277
+ str=opt.inf_txt if ip==:posinfinity
278
+ str='-'+opt.inf_txt if ip==:neginfinity
279
+ return str;
280
+ end
281
+
282
+ s = "";
283
+ s += '-' if @sign<0
284
+ s += RepDec.group_digits(@ip.to_s(@radix),opt);
285
+ s += opt.dec_sep if @d.length>0;
286
+ for i in 0...@d.length
287
+ break if nrep>0 && @rep_i==i;
288
+ s += opt.begin_rep if i==@rep_i;
289
+ s << opt.digits.digit_char(@d[i])
290
+ end;
291
+ if nrep>0 then
292
+ if @rep_i!=nil then
293
+ nrep += 1;
294
+ nrep.times do
295
+ for i in @rep_i...@d.length
296
+ s << opt.digits.digit_char(@d[i])
297
+ end
298
+ end
299
+
300
+ check = RepDec.new;
301
+ check.setS s+opt.auto_rep, opt;
302
+ #print " s=",s,"\n"
303
+ #print " self=",self.to_s,"\n"
304
+ while check!=self
305
+ for i in @rep_i...@d.length
306
+ s << opt.digits.digit_char(@d[i])
307
+ end
308
+ check.setS s+opt.auto_rep, opt;
309
+ end
310
+
311
+ s += opt.auto_rep;
312
+ end
313
+ else
314
+ s += opt.end_rep if @rep_i!=nil;
315
+ end
316
+ return s;
317
+ end
318
+
319
+ def to_s()
320
+ getS
321
+ end
322
+
323
+ def normalize!(remove_trailing_zeros=true)
324
+ if ip.is_a?(Integer)
325
+ if @rep_i!=nil && @rep_i==@d.length-1 && @d[@rep_i]==(@radix-1) then
326
+ @d.pop;
327
+ @rep_i = nil;
328
+
329
+ i = @d.length-1;
330
+ carry = 1;
331
+ while carry>0 && i>=0
332
+ @d[i] += carry;
333
+ carry = 0;
334
+ if @d[i]>(@radix) then
335
+ carry = 1;
336
+ @d[i]=0;
337
+ @d.pop if i==@d.length;
338
+ end
339
+ i -= 1;
340
+ end
341
+ @ip += carry;
342
+
343
+ end
344
+
345
+ if @rep_i!=nil && @rep_i>=@d.length
346
+ @rep_i = nil
347
+ end
348
+
349
+ if @rep_i!=nil && @rep_i>=0
350
+ unless @d[@rep_i..-1].find {|x| x!=0}
351
+ @d = @d[0...@rep_i]
352
+ @rep_i = nil
353
+ end
354
+ end
355
+ if @rep_i==nil && remove_trailing_zeros
356
+ while @d[@d.length-1]==0
357
+ @d.pop
358
+ end
359
+ end
360
+
361
+ end
362
+ end
363
+
364
+ def copy()
365
+ c = clone
366
+ c.d = d.clone
367
+ return c;
368
+ end
369
+
370
+ def ==(c)
371
+ a = copy;
372
+ b = c.copy;
373
+ a.normalize!
374
+ b.normalize!
375
+ return a.ip==b.ip && a.d==b.d && a.rep_i==b.rep_i
376
+ end
377
+
378
+ #def !=(c)
379
+ # return !(self==c);
380
+ #end
381
+
382
+ def setQ(x,y, opt=DEF_OPT)
383
+ @radix = opt.digits.radix if opt.digits_defined?
384
+ xy_sign = x==0 ? 0 : x<0 ? -1 : +1;
385
+ xy_sign = -xy_sign if y<0;
386
+ @sign = xy_sign
387
+ x = x.abs;
388
+ y = y.abs;
389
+
390
+ @d = [];
391
+ @rep_i = nil;
392
+
393
+ if y==0 then
394
+ if x==0 then
395
+ @ip = :indeterminate
396
+ else
397
+ @ip = xy_sign==-1 ? :neginfinity : :posinfinity
398
+ end
399
+ return self
400
+ end
401
+
402
+ k = [];
403
+ @ip = x.div(y) #x/y;
404
+ x -= @ip*y;
405
+ i = 0;
406
+ ended = false;
407
+
408
+ max_d = opt.max_d
409
+ while x>0 && @rep_i==nil && (max_d<=0 || i<max_d)
410
+ @rep_i = k.index(x)
411
+ if @rep_i.nil? then
412
+ k.push x;
413
+ x *= @radix
414
+ @d.push x.div(y) # x/y;
415
+ x-= @d[i]*y;
416
+ i += 1;
417
+ end
418
+ end
419
+ self
420
+ end
421
+
422
+ def getQ(opt=DEF_OPT)
423
+ raise RepDecError,"Base mismatch: #{opt.digits.radix} when #{@radix} was expected." if opt.digits_defined? && @radix!=opt.digits.radix
424
+
425
+ if !ip.is_a?(Integer) then
426
+ y = 0;
427
+ x=0 if ip==:indeterminate;
428
+ x=1 if ip==:posinfinity
429
+ x=-1 if ip==:neginfinity
430
+ return x,y;
431
+ end if
432
+
433
+
434
+ n = @d.length
435
+ a = @ip
436
+ b = a
437
+ for i in 0...n
438
+ a*=@radix
439
+ a+=@d[i];
440
+ if @rep_i!=nil && i<@rep_i
441
+ b *= @radix
442
+ b += @d[i];
443
+ end
444
+ end
445
+
446
+ x = a
447
+ x -= b if @rep_i!=nil
448
+
449
+ y = @radix**n
450
+ y -= @radix**@rep_i if @rep_i!=nil
451
+
452
+ d = Nio.gcd(x,y)
453
+ x /= d
454
+ y /= d
455
+
456
+ x = -x if @sign<0
457
+
458
+ return x,y;
459
+ end
460
+
461
+ #protected
462
+
463
+ attr_reader :d, :ip, :rep_i, :sign;
464
+ attr_writer :d, :ip, :rep_i, :sign;
465
+
466
+ end
467
+
468
+
469
+ def RepDec.group_digits(digits, opt)
470
+ if opt.grp_sep!=nil && opt.grp_sep!='' && opt.grp.length>0
471
+ grouped = ''
472
+ i = 0
473
+ while digits.length>0
474
+ l = opt.grp[i]
475
+ l = digits.length if l>digits.length
476
+ grouped = opt.grp_sep + grouped if grouped.length>0
477
+ grouped = digits[-l,l] + grouped
478
+ digits = digits[0,digits.length-l]
479
+ i += 1 if i<opt.grp.length-1
480
+ end
481
+ grouped
482
+ else
483
+ digits
484
+ end
485
+ end
486
+
487
+ module_function
488
+
489
+ def gcd(a,b)
490
+ while b!=0 do
491
+ a,b = b, a.modulo(b)
492
+ end
493
+ return a.abs;
494
+ end
495
+
496
+ end