summaryrefslogtreecommitdiffhomepage
path: root/mrblib/range.rb
blob: 746cb322cfb7ddda3be79218629d4e7f0ecddabc (plain)
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
##
# Range
#
# ISO 15.2.14
class Range

  ##
  # Calls the given block for each element of +self+
  # and pass the respective element.
  #
  # ISO 15.2.14.4.4
  def each(&block)
    return to_enum :each unless block

    val = self.first
    last = self.last

    # numerics are special
    if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
      lim = last
      lim += 1 unless exclude_end?
      i = val
      while i < lim
        block.call(i)
        i += 1
      end
      return self
    end

    if val.kind_of?(String) && last.kind_of?(String) # strings are special
      if val.respond_to? :upto
        return val.upto(last, exclude_end?, &block)
      else
        str_each = true
      end
    end

    raise TypeError, "can't iterate" unless val.respond_to? :succ

    return self if (val <=> last) > 0

    while (val <=> last) < 0
      block.call(val)
      val = val.succ
      if str_each
        break if val.size > last.size
      end
    end

    block.call(val) if !exclude_end? && (val <=> last) == 0
    self
  end

  # redefine #hash 15.3.1.3.15
  def hash
    h = first.hash ^ last.hash
    h += 1 if self.exclude_end?
    h
  end
end

##
# Range is enumerable
#
# ISO 15.2.14.3
class Range
  include Enumerable

  def max(&block)
    val = self.first
    last = self.last
    # numerics are special
    if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
      return nil if val > last
      return nil if val == last && exclude_end?

      max = last
      max -= 1 if exclude_end?
      max = val if block && block.call(val, last) > 0
      return max
    end

    max = nil
    each do |item|
      max =
        if max.nil?
          item
        elsif block && block.call(max, item) > 0
          item
        elsif item > max
          item
        else
          max
        end
    end
    max
  end

  def min(&block)
    val = self.first
    last = self.last

    # numerics are special
    if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
      return nil if val > last
      return nil if val == last && exclude_end?

      min = val
      if block && block.call(val, last) > 0
        min = last
        min -= 1 if exclude_end?
      end
      return min
    end

    min = nil
    each do |item|
      min =
        if min.nil?
          item
        elsif block && block.call(min, item) < 0
          item
        elsif item < min
          item
        else
          min
        end
    end
    min
  end
end