File: implementation.rb

package info (click to toggle)
ruby-backports 3.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 2,460 kB
  • ctags: 703
  • sloc: ruby: 6,195; makefile: 24
file content (94 lines) | stat: -rw-r--r-- 2,602 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
require "backports/tools/arguments"
require "backports/random/MT19937"
require "backports/random/bits_and_bytes"

module Backports
  class Random
    # 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.
    #
    module Implementation
      attr_reader :seed

      def initialize(seed = 0)
        super()
        srand(seed)
      end

      def srand(new_seed = 0)
        new_seed = Backports.coerce_to_int(new_seed)
        old, @seed = @seed, new_seed.nonzero? || Random.new_seed
        @mt = MT19937[ @seed ]
        old
      end

      def rand(limit = Backports::Undefined)
        case limit
          when Backports::Undefined
            @mt.random_float
          when Float
            limit * @mt.random_float unless limit <= 0
          when Range
            _rand_range(limit)
          else
            limit = Backports.coerce_to_int(limit)
            @mt.random_integer(limit) unless limit <= 0
        end || raise(ArgumentError, "invalid argument #{limit}")
      end

      def bytes(nb)
        nb = Backports.coerce_to_int(nb)
        raise ArgumentError, "negative size" if nb < 0
        @mt.random_bytes(nb)
      end

      def ==(other)
        other.is_a?(Random) &&
          seed == other.seed &&
          left == other.send(:left) &&
          state == other.send(:state)
      end

      def marshal_dump
        @mt.marshal_dump << @seed
      end

      def marshal_load(ary)
        @seed = ary.pop
        @mt = MT19937.allocate
        @mt.marshal_load(ary)
      end

    private
      def state
        @mt.state_as_bignum
      end

      def left
        @mt.left
      end

      def _rand_range(limit)
        range = limit.end - limit.begin
        if (!range.is_a?(Float)) && range.respond_to?(:to_int) && range = Backports.coerce_to_int(range)
          range += 1 unless limit.exclude_end?
          limit.begin + @mt.random_integer(range) unless range <= 0
        elsif range = Backports.coerce_to(range, Float, :to_f)
          if range < 0
            nil
          elsif limit.exclude_end?
            limit.begin + @mt.random_float * range unless range <= 0
          else
            # cheat a bit... this will reduce the nb of random bits
            loop do
              r = @mt.random_float * range * 1.0001
              break limit.begin + r unless r > range
            end
          end
        end
      end
    end
  end
end