class Regexp::MatchLength

def self.of(obj)

def self.of(obj)
  exp = obj.is_a?(Regexp::Expression::Base) ? obj : Regexp::Parser.parse(obj)
  exp.match_length
end

def each(opts = {})

def each(opts = {})
  return enum_for(__method__, opts) unless block_given?
  limit = opts[:limit] || 1000
  yielded = 0
  (min..max).each do |num|
    next unless include?(num)
    yield(num)
    break if (yielded += 1) >= limit
  end
end

def endless_each

def endless_each
  return enum_for(__method__) unless block_given?
  (min..max).each { |num| yield(num) if include?(num) }
end

def fixed?

def fixed?
  min == max
end

def include?(length)

def include?(length)
  test_regexp.match?('X' * length)
end

def initialize(exp, opts = {})

def initialize(exp, opts = {})
  self.exp_class = exp.class
  self.min_rep = exp.repetitions.min
  self.max_rep = exp.repetitions.max
  if (base = opts[:base])
    self.base_min = base
    self.base_max = base
    self.reify = ->{ '.' * base }
  else
    self.base_min = opts.fetch(:base_min)
    self.base_max = opts.fetch(:base_max)
    self.reify = opts.fetch(:reify)
  end
end

def inspect

def inspect
  type = exp_class.name.sub('Regexp::Expression::', '')
  "#<#{self.class}<#{type}> min=#{min} max=#{max}>"
end

def max

def max
  max_rep * base_max
end

def min

def min
  min_rep * base_min
end

def minmax

def minmax
  [min, max]
end

def test_regexp

ruby >= 2.4
def test_regexp
  @test_regexp ||= /^#{to_re}$/
end

def test_regexp

def test_regexp
  @test_regexp ||= /^#{to_re}$/.tap { |r| def r.match?(s); !!match(s) end }
end

def to_re

def to_re
  /(?:#{reify.call}){#{min_rep},#{max_rep unless max_rep == Float::INFINITY}}/
end