class Array
  # Standard in Ruby 1.9.2 See official documentation[http://ruby-doc.org/core-1.9/classes/Enumerable.html]
  def keep_if
    return to_enum(:keep_if) unless block_given?
    delete_if{|elem| !yield elem}
  end unless method_defined? :keep_if

  if [1].product([2]){break false}
    def product_with_block(*arg, &block)
      return product_without_block(*arg) unless block_given?
      # Same implementation as 1.8.7, but yielding
      arg.map!{|ary| Backports.coerce_to_ary(ary)}
      arg.reverse! # to get the results in the same order as in MRI, vary the last argument first
      arg.push self

      outer_lambda = arg.inject(block) do |proc, values|
        lambda do |partial|
          values.each do |val|
            proc.call(partial.dup << val)
          end
        end
      end

      outer_lambda.call([])
      self
    end
    Backports.alias_method_chain self, :product, :block
  end

  # Note: Combinations are not yielded in the same order as MRI.
  # This is not a bug; the spec states that the order is implementation dependent
  def repeated_combination(num)
    return to_enum(:repeated_combination, num) unless block_given?
    num = Backports.coerce_to_int(num)
    if num <= 0
      yield [] if num == 0
    else
      indices = Array.new(num, 0)
      indices[-1] = size
      while dec = indices.find_index(&:nonzero?)
        indices[0..dec] = Array.new dec+1, indices[dec]-1
        yield values_at(*indices)
      end
    end
    self
  end unless method_defined? :repeated_combination

  # Note: Permutations are not yielded in the same order as MRI.
  # This is not a bug; the spec states that the order is implementation dependent
  def repeated_permutation(num)
    return to_enum(:repeated_permutation, num) unless block_given?
    num = Backports.coerce_to_int(num)
    if num <= 0
      yield [] if num == 0
    else
      indices = Array.new(num, 0)
      indices[-1] = size
      while dec = indices.find_index(&:nonzero?)
        indices[0...dec] = Array.new dec, size-1
        indices[dec] -= 1
        yield values_at(*indices)
      end
    end
    self
  end unless method_defined? :repeated_permutation

  def rotate(n=1)
    Array.new(self).rotate!(n)
  end unless method_defined? :rotate

  def rotate!(n=1)
    n = Backports.coerce_to_int(n) % (empty? ? 1 : size)
    concat(shift(n))
  end unless method_defined? :rotate!

  def select!
    return to_enum(:select!) unless block_given?
    reject!{|elem| ! yield elem}
  end unless method_defined? :select!

  def sort_by!
    return to_enum(:sort_by!) unless block_given?
    raise "can't modify frozen array" if frozen?
    replace sort_by{|e| yield e}
  end unless method_defined? :sort_by!

  unless [1,2].uniq!{}
    def uniq_with_block!
      return uniq_without_block! unless block_given?
      replace self if frozen? # force error
      u = uniq{|e| yield e}
      replace u unless u.size == size
    end
    Backports.alias_method_chain self, :uniq!, :block
  end

  unless [1,2].uniq{}.size == 1
    def uniq_with_block
      return uniq_without_block unless block_given?
      h = {}
      each do |elem|
        h[yield(elem)] ||= elem
      end
      h.values
    end
    Backports.alias_method_chain self, :uniq, :block
  end

end