# repdec.rb -- Repeating Decimals (Repeating Numerals, actually) # Copyright (C) 2003-2005, Javier Goizueta # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. require 'nio/tools' module Nio class RepDecError =0 && v<@radix ? @digits[v] : nil end def radix @radix end def DigitsDef.base(b,dncase=false,casesens=false) dgs = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[0,b] dgs.downcase! if dncase DigitsDef.new(dgs,casesens) end private def set_case(ch_code) ch_code = ch_code.chr if ch_code.kind_of?(Numeric) @dncase ? ch_code.downcase[0] : ch_code.upcase[0] end end # RepDec handles repeating decimals (repeating numerals actually) class RepDec include StateEquivalent class Opt # :nodoc: include StateEquivalent def initialize() #default options @begin_rep = '<' @end_rep = '>' @auto_rep = '...' @dec_sep = '.' @grp_sep = ',' @grp = [] # [3] for thousands separators @inf_txt = 'Infinity' @nan_txt = 'NaN' @digits = DigitsDef.new @digits_defined = false @max_d = 5000 end attr_accessor :begin_rep, :end_rep, :auto_rep, :dec_sep, :grp_sep, :grp, :max_d attr_accessor :nan_txt, :inf_txt def set_delim(begin_d,end_d='') @begin_rep = begin_d @end_rep = end_d return self end def set_suffix(a) @auto_rep = a return self end def set_sep(d) @dec_sep = a return self end def set_grouping(sep,g=[]) @grp_sep = a @grp = g return self end def set_special(nan_txt, inf_txt) @nan_txt = nan_txt @inf_txt = inf_txt return self end def set_digits(ds, dncase=false, casesens=false) if ds @digits_defined = true if ds.kind_of?(DigitsDef) @digits = ds elsif ds.kind_of?(Numeric) @digits = DigitsDef.base(ds, dncase, casesens) else @digits = DigitsDef.new(ds,casesens) end else @digits = DigitsDef.new @digits_defined = false end self end attr_accessor :digits def digits_defined? @digits_defined end end DEF_OPT=Opt.new def initialize(b=10) setZ(b) end def setZ(b=10) @ip = 0; @d = []; @rep_i = nil; @sign = 0; @radix = b; self end def setS(str, opt=DEF_OPT) setZ(opt.digits_defined? ? opt.digits.radix : @radix); sgn,i_str,f_str,ri,detect_rep = RepDec.parse(str,opt) if i_str.kind_of?(Symbol) @ip = i_str else @ip = i_str.to_i(@radix); # this assumes conventional digits end @sign = sgn @rep_i = ri if ri f_str.each_byte{|b| @d.push opt.digits.digit_value(b)} unless f_str.nil? if detect_rep then for l in 1..(@d.length/2) l = @d.length/2 + 1 - l; if @d[-l..-1]==@d[-2*l...-l] for m in 1..l if l.modulo(m)==0 then reduce_l = true; for i in 2..l/m if @d[-m..-1]!=@d[-i*m...-i*m+m] then reduce_l = false; break; end end if reduce_l then l = m break end end end @rep_i = @d.length - 2*l; l.times { @d.pop } while @d.length >= 2*l && @d[-l..-1]==@d[-2*l...-l] @rep_i = @d.length - 2*l; l.times { @d.pop } end break end end end if @rep_i!=nil then if @d.length==@rep_i+1 && @d[@rep_i]==0 then @rep_i = nil; @d.pop; end end @d.pop while @d[@d.length-1]==0 self end def RepDec.parse(str, opt=DEF_OPT) sgn,i_str,f_str,ri,detect_rep = nil,nil,nil,nil,nil i = 0; l = str.length; detect_rep = false; i += 1 while i0; for i in 0...@d.length break if nrep>0 && @rep_i==i; s += opt.begin_rep if i==@rep_i; s << opt.digits.digit_char(@d[i]) end; if nrep>0 then if @rep_i!=nil then nrep += 1; nrep.times do for i in @rep_i...@d.length s << opt.digits.digit_char(@d[i]) end end check = RepDec.new; check.setS s+opt.auto_rep, opt; #print " s=",s,"\n" #print " self=",self.to_s,"\n" while check!=self for i in @rep_i...@d.length s << opt.digits.digit_char(@d[i]) end check.setS s+opt.auto_rep, opt; end s += opt.auto_rep; end else s += opt.end_rep if @rep_i!=nil; end return s; end def to_s() getS end def normalize!(remove_trailing_zeros=true) if ip.is_a?(Integer) if @rep_i!=nil && @rep_i==@d.length-1 && @d[@rep_i]==(@radix-1) then @d.pop; @rep_i = nil; i = @d.length-1; carry = 1; while carry>0 && i>=0 @d[i] += carry; carry = 0; if @d[i]>(@radix) then carry = 1; @d[i]=0; @d.pop if i==@d.length; end i -= 1; end @ip += carry; end if @rep_i!=nil && @rep_i>=@d.length @rep_i = nil end if @rep_i!=nil && @rep_i>=0 unless @d[@rep_i..-1].find {|x| x!=0} @d = @d[0...@rep_i] @rep_i = nil end end if @rep_i==nil && remove_trailing_zeros while @d[@d.length-1]==0 @d.pop end end end end def copy() c = clone c.d = d.clone return c; end def ==(c) a = copy; b = c.copy; a.normalize! b.normalize! return a.ip==b.ip && a.d==b.d && a.rep_i==b.rep_i end #def !=(c) # return !(self==c); #end # Change the maximum number of digits that RepDec objects # can handle. def RepDec.maximum_number_of_digits=(n) @max_d = [n,2048].max end # Return the maximum number of digits that RepDec objects # can handle. def RepDec.maximum_number_of_digits @max_d end def setQ(x,y, opt=DEF_OPT) @radix = opt.digits.radix if opt.digits_defined? xy_sign = x==0 ? 0 : x<0 ? -1 : +1; xy_sign = -xy_sign if y<0; @sign = xy_sign x = x.abs; y = y.abs; @d = []; @rep_i = nil; if y==0 then if x==0 then @ip = :indeterminate else @ip = xy_sign==-1 ? :neginfinity : :posinfinity end return self end k = {}; @ip = x.div(y) #x/y; x -= @ip*y; i = 0; ended = false; max_d = opt.max_d while x>0 && @rep_i==nil && (max_d<=0 || i0 grouped = '' i = 0 while digits.length>0 l = opt.grp[i] l = digits.length if l>digits.length grouped = opt.grp_sep + grouped if grouped.length>0 grouped = digits[-l,l] + grouped digits = digits[0,digits.length-l] i += 1 if i