Skip to content

Commit

Permalink
Merge pull request #172 from twitter/range_set_improvements
Browse files Browse the repository at this point in the history
Minor improvements to RangeSet
  • Loading branch information
camertron committed Dec 22, 2015
2 parents ca8ae1c + 9fc162b commit 66bcff7
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 5 deletions.
76 changes: 71 additions & 5 deletions lib/twitter_cldr/utils/range_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ module Utils
# than as single elements. By definition, RangeSets contain no duplicates.
class RangeSet

include Enumerable

attr_reader :ranges

class << self
Expand Down Expand Up @@ -93,11 +95,9 @@ def to_set
def include?(obj)
case obj
when Numeric
ranges.any? { |range| range.include?(obj) }
includes_numeric?(obj)
when Range
ranges.any? do |range|
range.first <= obj.first && range.last >= obj.last
end
includes_range?(obj)
else
false
end
Expand Down Expand Up @@ -161,7 +161,73 @@ def difference(range_set)
union(range_set).subtract(intersection(range_set))
end

protected
def each_range(&block)
ranges.each(&block)
end

def each(&block)
if block_given?
ranges.each do |range|
range.each(&block)
end
else
to_enum(__method__)
end
end

private

def includes_numeric?(num)
result = bsearch do |range|
if num >= range.first && num <= range.last
0
elsif num < range.first
-1
else
1
end
end

!!result
end

def includes_range?(range)
bsearch do |cur_range|
fo = front_overlap?(cur_range, range)
ro = rear_overlap?(cur_range, range)

return false if fo || ro

if full_overlap?(cur_range, range)
0
elsif range.first < cur_range.first
-1
else
1
end
end
end

def bsearch
low = 0
mid = 0
high = ranges.size - 1

while low <= high
mid = (low + high) / 2

case yield(ranges[mid])
when 0
return ranges[mid]
when -1
high = mid - 1
else
low = mid + 1
end
end

nil
end

def overlap?(range1, range2)
is_numeric_range?(range1) && is_numeric_range?(range2) && (
Expand Down
16 changes: 16 additions & 0 deletions spec/utils/range_set_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,20 @@
expect(set.to_a).to eq([1..15])
end
end

describe "#each_range" do
let(:set) { RangeSet.new([5..10, 15..20]) }

it "yields each individual range to the block" do
expect(set.each_range.to_a).to eq([5..10, 15..20])
end
end

describe "#each" do
let(:set) { RangeSet.new([5..10]) }

it "yields each number the set contains (as opposed to each range)" do
expect(set.each.to_a).to eq([5, 6, 7, 8, 9, 10])
end
end
end

0 comments on commit 66bcff7

Please sign in to comment.