class BetterRanges::SparseRange

Attributes

data[R]

Public Class Methods

new(*data) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 5
def initialize(*data)
  @data = [*data].map! do |x|
    if x.is_a?(Enumerable)
      return nil if x.none?
      return x.data.clone if x.is_a?(SparseRange)
    end
    x
  end
  optimize
end

Public Instance Methods

&(x) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 87
def &(x)
  intersect = SparseRange.new
  intersect_data = intersect.data

  i = 0
  next_val = lambda do
    throw :done unless i < @data.length
    v = read_val(@data[i])
    i += 1
    v
  end

  catch(:done) do
    other = (x.is_a?(SparseRange) ? x : SparseRange.new(x)).data

    start, finish = next_val.call
    other.each do |r|
      other_start, other_finish = read_val(r)
      start, finish = next_val.call while finish < other_start

      until other_finish < start
        first = [start, other_start].max
        last = [finish, other_finish].min

        intersect_data << write_val(first, last)

        start = last.succ
        if start < other_finish
          other_start = start
          start, finish = next_val.call
        end
      end
    end
  end

  intersect
end
Also aliased as: intersect
+(x)
Alias for: |
-(x) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 50
def -(x)
  diff = SparseRange.new
  diff_data = diff.data

  i = 0
  next_val = lambda do
    throw :done unless i < @data.length
    v = read_val(@data[i])
    i += 1
    v
  end

  catch(:done) do
    other = (x.is_a?(SparseRange) ? x : SparseRange.new(x)).data

    start, finish = next_val.call
    other.each do |r|
      other_start, other_finish = read_val(r)

      while finish < other_start
        diff_data << write_val(start, finish)
        start, finish = next_val.call
      end
      next if other_finish < start

      diff_data << (start.succ == other_start ? start : (start...other_start)) if start < other_start

      start = other_finish.succ
      start, finish = next_val.call if start > finish
    end
    diff_data << write_val(start, finish)
    loop { diff_data << write_val(*next_val.call) }
  end

  diff
end
Also aliased as: minus, difference
<<(x) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 125
def <<(x)
  @data << [*(x.is_a?(SparseRange) ? x.data : x)]
  optimize

  self
end
Also aliased as: add
==(x) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 150
def ==(x)
  other = (x.is_a?(SparseRange) ? x : SparseRange.new(x)).data
  i = 0
  (@data.length == other.length) && @data.all? do |e|
    o = other[i]
    i += 1
    (e == o) || (read_val(e) == read_val(o))
  end
end
Also aliased as: eql?
===(x)
Alias for: include?
add(x)
Alias for: <<
cover?(x)
Alias for: include?
difference(x)
Alias for: -
each(&block) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 16
def each(&block)
  Enumerator.new do |yielder|
    @data.each do |r|
      yield_each(r) { |x| yielder.yield x }
    end
  end.each(&block)
end
empty?() click to toggle source
# File lib/better_ranges/sparse_range.rb, line 142
def empty?
  @data.empty? || size == 0
end
eql?(x)
Alias for: ==
hash() click to toggle source

TODO: Calculate hash without creating the temp array

# File lib/better_ranges/sparse_range.rb, line 161
def hash
  @data.map(&method(:read_val)).hash
end
include?(x) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 136
def include?(x)
  @data.any? do |r|
    r.is_a?(Range) ? r.include?(x) : x == r
  end
end
Also aliased as: cover?, ===
inspect() click to toggle source
# File lib/better_ranges/sparse_range.rb, line 132
def inspect
  @data.inspect
end
intersect(x)
Alias for: &
last() click to toggle source
# File lib/better_ranges/sparse_range.rb, line 42
def last
  read_val(@data.last).last
end
minus(x)
Alias for: -
size() click to toggle source
# File lib/better_ranges/sparse_range.rb, line 146
def size
  @data.reduce(0) { |a, e| a + (e.is_a?(Range) ? e.count : 1) }
end
sparse_each(&block) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 24
def sparse_each(&block)
  @data.each do |x|
    x.is_a?(Range) ? x : (x..x)
  end
end
step(num = 1, &block) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 30
def step(num = 1, &block)
  i = 0
  Enumerator.new do |yielder|
    @data.each do |r|
      yield_each(r) do |x|
        yielder.yield x if (i % num) == 0
        i += 1
      end
    end
  end.each(&block)
end
union(x)
Alias for: |
|(x) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 46
def |(x)
  SparseRange.new(@data, *x)
end
Also aliased as: +, union

Protected Instance Methods

yield_each(e) { |x| ... } click to toggle source
# File lib/better_ranges/sparse_range.rb, line 183
def yield_each(e)
  if e.is_a?(Range)
    e.each { |x| yield x }
  else
    yield e
  end
end

Private Instance Methods

optimize() click to toggle source
# File lib/better_ranges/sparse_range.rb, line 205
def optimize
  @data.flatten!
  @data.compact!
  @data.sort! { |a, b| (a <=> b) || (read_val(a) <=> read_val(b)) }

  fixed = []
  start, finish = read_val(@data.first)

  for i in (1...@data.length)
    first, last = read_val(@data[i])

    if finish.succ >= first
      finish = last if last > finish
    else
      fixed << write_val(start, finish)
      start, finish = first, last
    end
  end
  fixed << write_val(start, finish) if finish

  @data = fixed
end
read_val(x) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 193
def read_val(x)
  if x.is_a?(Range)
    [x.first, (x.exclude_end? ? x.max : x.last)]
  else
    [x, x]
  end
end
write_val(start, finish) click to toggle source
# File lib/better_ranges/sparse_range.rb, line 201
def write_val(start, finish)
  (start != finish ? (start..finish) : finish)
end