moduleRSpecmoduleCore# @private## Methods used internally by the backports.## This code was (mostly) ported from the backports gem found at# https://github.com/marcandre/backports which is subject to this license:## =========================================================================## Copyright (c) 2009 Marc-Andre Lafortune## Permission is hereby granted, free of charge, to any person obtaining# a copy of this software and associated documentation files (the# "Software"), to deal in the Software without restriction, including# without limitation the rights to use, copy, modify, merge, publish,# distribute, sublicense, and/or sell copies of the Software, and to# permit persons to whom the Software is furnished to do so, subject to# the following conditions:## The above copyright notice and this permission notice shall be# included in all copies or substantial portions of the Software.## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.## =========================================================================## The goal is to provide a random number generator in Ruby versions that do# not have one. This was added to support localization of random spec# ordering.## These were in multiple files in backports, but merged into one here.moduleBackports# Helper method to coerce a value into a specific class.# Raises a TypeError if the coercion fails or the returned value# is not of the right class.# (from Rubinius)defself.coerce_to(obj,cls,meth)returnobjifobj.kind_of?(cls)beginret=obj.__send__(meth)rescueException=>eraiseTypeError,"Coercion error: #{obj.inspect}.#{meth} => #{cls} failed:\n"\"(#{e.message})"endraiseTypeError,"Coercion error: obj.#{meth} did NOT return a #{cls} (was #{ret.class})"unlessret.kind_of?clsretend# @privatedefself.coerce_to_int(obj)coerce_to(obj,Integer,:to_int)end# Used internally to make it easy to deal with optional arguments# (from Rubinius)Undefined=Object.new# @privateclassRandom# @private# An implementation of Mersenne Twister MT19937 in RubyclassMT19937STATE_SIZE=624LAST_STATE=STATE_SIZE-1PAD_32_BITS=0xffffffff# See seed=definitialize(seed)self.seed=seedendLAST_31_BITS=0x7fffffffOFFSET=397# Generates a completely new state out of the previous one.defnext_stateSTATE_SIZE.timesdo|i|mix=@state[i]&0x80000000|@state[i+1-STATE_SIZE]&0x7fffffff@state[i]=@state[i+OFFSET-STATE_SIZE]^(mix>>1)@state[i]^=0x9908b0dfifmix.odd?end@last_read=-1end# Seed must be either an Integer (only the first 32 bits will be used)# or an Array of Integers (of which only the first 32 bits will be used)## No conversion or type checking is done at this leveldefseed=(seed)caseseedwhenInteger@state=Array.new(STATE_SIZE)@state[0]=seed&PAD_32_BITS(1..LAST_STATE).eachdo|i|@state[i]=(1812433253*(@state[i-1]^@state[i-1]>>30)+i)&PAD_32_BITSend@last_read=LAST_STATEwhenArrayself.seed=19650218i=1j=0[STATE_SIZE,seed.size].max.timesdo@state[i]=(@state[i]^(@state[i-1]^@state[i-1]>>30)*1664525)+j+seed[j]&PAD_32_BITSif(i+=1)>=STATE_SIZE@state[0]=@state[-1]i=1endj=0if(j+=1)>=seed.sizeend(STATE_SIZE-1).timesdo@state[i]=(@state[i]^(@state[i-1]^@state[i-1]>>30)*1566083941)-i&PAD_32_BITSif(i+=1)>=STATE_SIZE@state[0]=@state[-1]i=1endend@state[0]=0x80000000elseraiseArgumentError,"Seed must be an Integer or an Array"endend# Returns a random Integer from the range 0 ... (1 << 32)defrandom_32_bitsnext_stateif@last_read>=LAST_STATE@last_read+=1y=@state[@last_read]# Temperingy^=(y>>11)y^=(y<<7)&0x9d2c5680y^=(y<<15)&0xefc60000y^=(y>>18)end# Supplement the MT19937 class with methods to do# conversions the same way as MRI.# No argument checking is done here either.FLOAT_FACTOR=1.0/9007199254740992.0# generates a random number on [0,1) with 53-bit resolutiondefrandom_float((random_32_bits>>5)*67108864.0+(random_32_bits>>6))*FLOAT_FACTOR;end# Returns an integer within 0...uptodefrandom_integer(upto)n=upto-1nb_full_32=0whilen>PAD_32_BITSn>>=32nb_full_32+=1endmask=mask_32_bits(n)beginrand=random_32_bits&masknb_full_32.timesdorand<<=32rand|=random_32_bitsendenduntilrand<uptorandenddefrandom_bytes(nb)nb_32_bits=(nb+3)/4random=nb_32_bits.times.map{random_32_bits}random.pack("L"*nb_32_bits)[0,nb]enddefstate_as_bignumb=0@state.each_with_indexdo|val,i|b|=val<<(32*i)endbenddefleft# It's actually the number of words left + 1, as per MRI...MT19937::STATE_SIZE-@last_readenddefmarshal_dump[state_as_bignum,left]enddefmarshal_load(ary)b,left=ary@last_read=MT19937::STATE_SIZE-left@state=Array.new(STATE_SIZE)STATE_SIZE.timesdo|i|@state[i]=b&PAD_32_BITSb>>=32endend# Convert an Integer seed of arbitrary size to either a single 32 bit integer, or an Array of 32 bit integersdefself.convert_seed(seed)seed=seed.abslong_values=[]beginlong_values<<(seed&PAD_32_BITS)seed>>=32enduntilseed==0long_values.popiflong_values[-1]==1&&long_values.size>1# Done to allow any kind of sequence of integerslong_values.size>1?long_values:long_values.firstenddefself.[](seed)new(convert_seed(seed))endprivateMASK_BY=[1,2,4,8,16]defmask_32_bits(n)MASK_BY.eachdo|shift|n|=n>>shiftendnendend# @private# Implementation corresponding to the actual Random class of Ruby# The actual random generator (mersenne twister) is in MT19937.# Ruby specific conversions are handled in bits_and_bytes.# The high level stuff (argument checking) is done here.moduleImplementationattr_reader:seeddefinitialize(seed=0)super()seed_randseedenddefseed_rand(new_seed=0)new_seed=Backports.coerce_to_int(new_seed)@seed=nilunlessdefined?(@seed)old,@seed=@seed,new_seed.nonzero?||Random.new_seed@mt=MT19937[@seed]oldenddefrand(limit=Backports::Undefined)caselimitwhenBackports::Undefined@mt.random_floatwhenFloatlimit*@mt.random_floatunlesslimit<=0whenRange_rand_range(limit)elselimit=Backports.coerce_to_int(limit)@mt.random_integer(limit)unlesslimit<=0end||raise(ArgumentError,"invalid argument #{limit}")enddefbytes(nb)nb=Backports.coerce_to_int(nb)raiseArgumentError,"negative size"ifnb<0@mt.random_bytes(nb)enddef==(other)other.is_a?(Random)&&seed==other.seed&&left==other.send(:left)&&state==other.send(:state)enddefmarshal_dump@mt.marshal_dump<<@seedenddefmarshal_load(ary)@seed=ary.pop@mt=MT19937.allocate@mt.marshal_load(ary)endprivatedefstate@mt.state_as_bignumenddefleft@mt.leftenddef_rand_range(limit)range=limit.end-limit.beginif(!range.is_a?(Float))&&range.respond_to?(:to_int)&&range=Backports.coerce_to_int(range)range+=1unlesslimit.exclude_end?limit.begin+@mt.random_integer(range)unlessrange<=0elsifrange=Backports.coerce_to(range,Float,:to_f)ifrange<0nilelsiflimit.exclude_end?limit.begin+@mt.random_float*rangeunlessrange<=0else# cheat a bit... this will reduce the nb of random bitsloopdor=@mt.random_float*range*1.0001breaklimit.begin+runlessr>rangeendendendendenddefself.new_seed(2**62)+Kernel.rand(2**62)endendclassRandomincludeImplementationclass<<selfincludeImplementationendendendendend