require'more_math'moduleMoreMath# This class implements a continued fraction of the form:## b_1# a_0 + -------------------------# b_2# a_1 + --------------------# b_3# a_2 + ---------------# b_4# a_3 + ----------# b_5# a_4 + -----# ...#classContinuedFraction# Creates a continued fraction instance. With the defaults for_a { 1 } and# for_b { 1 } it approximates the golden ration phi if evaluated.definitialize@a=proc{1.0}@b=proc{1.0}end# Creates a ContinuedFraction instances and passes its arguments to a call# to for_a.defself.for_a(arg=nil,&block)new.for_a(arg,&block)end# Creates a ContinuedFraction instances and passes its arguments to a call# to for_b.defself.for_b(arg=nil,&block)new.for_b(arg,&block)end# This method either takes a block or an argument +arg+. The argument +arg+# has to respond to an integer index n >= 0 and return the value a_n. The# block has to return the value for a_n when +n+ is passed as the first# argument to the block. If a_n is dependent on an +x+ value (see the call# method) the +x+ will be the second argument of the block.deffor_a(arg=nil,&block)ifargand!block@a=argelsifblockand!arg@a=blockelseraiseArgumentError,"exactly one argument or one block required"endselfend# This method either takes a block or an argument +arg+. The argument +arg+# has to respond to an integer index n >= 1 and return the value b_n. The# block has to return the value for b_n when +n+ is passed as the first# argument to the block. If b_n is dependent on an +x+ value (see the call# method) the +x+ will be the second argument of the block.deffor_b(arg=nil,&block)ifargand!block@b=argelsifblockand!arg@b=blockelseraiseArgumentError,"exactly one argument or one block required"endselfend# Returns the value for a_n or a_n(x).defa(n,x=nil)result=ifx@a[n,x]else@a[n]endandresult.to_fend# Returns the value for b_n or b_n(x).defb(n,x=nil)result=ifx@b[n,x]else@b[n]endandresult.to_fend# Evaluates the continued fraction for the value +x+ (if any) with the# accuracy +epsilon+ and +max_iterations+ as the maximum number of# iterations using the Wallis-method with scaling.defcall(x=nil,epsilon=1E-16,max_iterations=1<<31)c_0,c_1=1.0,a(0,x)c_1==nilandreturn0/0.0d_0,d_1=0.0,1.0result=c_1/d_1n=0error=1/0.0$DEBUGandwarn"n=%u, a=%f, b=nil, c=%f, d=%f result=%f, error=nil"%[n,c_1,c_1,d_1,result]whilen<max_iterationsanderror>epsilonn+=1a_n,b_n=a(n,x),b(n,x)a_nandb_norbreakc_2=a_n*c_1+b_n*c_0d_2=a_n*d_1+b_n*d_0ifc_2.infinite?ord_2.infinite?ifa_n!=0c_2=c_1+(b_n/a_n*c_0)d_2=d_1+(b_n/a_n*d_0)elsifb_n!=0c_2=(a_n/b_n*c_1)+c_0d_2=(a_n/b_n*d_1)+d_0elseraiseErrno::ERANGEendendr=c_2/d_2error=(r/result-1).absresult=r$DEBUGandwarn"n=%u, a=%f, b=%f, c=%f, d=%f, result=%f, error=%.16f"%[n,a_n,b_n,c_1,d_1,result,error]c_0,c_1=c_1,c_2d_0,d_1=d_1,d_2endn>=max_iterationsandraiseErrno::ERANGEresultendalias[]call# Returns this continued fraction as a Proc object which takes the same# arguments like its call method does.defto_procproc{|*a|call(*a)}endendend