From 4085709ae1736fb755955d3d52b6b73c15853506 Mon Sep 17 00:00:00 2001 From: Ryan Lopopolo Date: Mon, 8 Jul 2019 19:23:24 -0700 Subject: Specialize Enumerable#max and Enumerable#min for Range This patch prevents a hang for pathalogical (large) Ranges when computing max and min. Range inherits its implementation of max and min from Enumerable. Enumerable implements max and min by calling each. For Range objects, this is unnecessary since we know the max and the min by the end and begin attributes. It is also very slow. This code hangs unnecessarily: (0..2**32).max # ... hang (0..2**32).min # ... hang This patch overrides max and min after including enumerable to yield based on the begin and end methods. --- mrblib/range.rb | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/mrblib/range.rb b/mrblib/range.rb index 392cc2274..746cb322c 100644 --- a/mrblib/range.rb +++ b/mrblib/range.rb @@ -15,7 +15,8 @@ class Range val = self.first last = self.last - if val.kind_of?(Fixnum) && last.kind_of?(Fixnum) # fixnums are special + # 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 @@ -64,4 +65,67 @@ end # 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 -- cgit v1.2.3