module Enumerable
  class StopIteration < IndexError
  end

  class Enumerator
    include Enumerable

    def initialize(obj, method = :each, *args)
      @obj = obj
      @meth = method
      @iter = method(method)
    end

    def each(&block)
      if block_given?
        @obj.each(&block)
      else
        self
      end
    end

    def with_index
      n = 0
      each do |e|
        yield(e, n)
        n += 1
      end
    end

    alias each_with_index with_index

    def next
      # Generator
    end

    def rewind
      # Generator
    end
  end

  def each_slice(n)
    raise(ArgumentError, "invalid slice size") if n <= 0

    sum = []

    each do |e|
      sum.push(e)
      if sum.size == n
        yield(sum)
        sum = []
      end
    end

    yield(sum) if sum.size > 0

    return nil
  end

  alias enum_slice each_slice

  def each_cons(n)
    raise(ArgumentError, "invalid size") if n <= 0

    sum = []

    each do |e|
      sum.shift if sum.size == n
      sum.push(e)
      if sum.size == n
        yield(sum)
      end
    end

    return nil
  end

  alias enum_cons each_cons
end

module Kernel
  def enum_for(*args)
    Enumerable::Enumerator.new(self, *args)
  end

  alias to_enum enum_for
end

[1,2,3].enum_for(:each).map{|n| n + 1 }
[1,2,3].enum_for(:each).with_index{|n, i| p n => i }

puts '', "each_cons(3):"
(1..10).each_cons(3){|n| p n }

puts '', "each_slice(3):"
(1..10).each_slice(3){|n| p n }