# CREDIT Thomas Hafner

module Enumerable

  class << self

    # Provides the cross-product of two or more Enumerables.
    # This is the class-level method. The instance method
    # calls on this.
    #
    #   Enumerable.cross([1,2], [4], ["apple", "banana"])
    #   #=> [[1, 4, "apple"], [1, 4, "banana"], [2, 4, "apple"], [2, 4, "banana"]]
    #
    #   Enumerable.cross([1,2], [3,4])
    #   #=> [[1, 3], [1, 4], [2, 3], [2, 4]]

    def cartesian_product(*enums, &block)
      result = [[]]
      while [] != enums
        t, result = result, []
        b, *enums = enums
        t.each do |a|
          b.each do |n|
            result << a + [n]
          end
        end
      end
      if block_given?
        result.each{ |e| block.call(e) }
      else
        result
      end
    end

    alias :cart :cartesian_product
  end

  # The instance level version of <tt>Enumerable::cartesian_product</tt>.
  #
  #   a = []
  #   [1,2].cart([4,5]){|elem| a << elem }
  #   a  #=> [[1, 4],[1, 5],[2, 4],[2, 5]]

  def cartesian_product(*enums, &block)
    Enumerable.cartesian_product(self, *enums, &block)
  end

  alias :cart :cartesian_product
end



#  _____         _
# |_   _|__  ___| |_
#   | |/ _ \/ __| __|
#   | |  __/\__ \ |_
#   |_|\___||___/\__|
#
=begin test

  require 'test/unit'

  class TestEnumerableClass < Test::Unit::TestCase

    def test_cart_01
      i = [[1,2], [3,4]]
      o = [[1, 3], [1, 4], [2, 3], [2, 4]]
      assert_equal( o, Enumerable.cart(*i) )
    end

    def test_cart_02
      i = [[1,2], [4], ["apple", "banana"]]
      o = [[1, 4, "apple"], [1, 4, "banana"], [2, 4, "apple"], [2, 4, "banana"]]
      assert_equal( o, Enumerable.cart(*i) )
    end

  end

  class TestEnumerable < Test::Unit::TestCase

    def test_cart_01
      a = [1,2,3].cart([4,5,6])
      assert_equal( [[1, 4],[1, 5],[1, 6],[2, 4],[2, 5],[2, 6],[3, 4],[3, 5],[3, 6]], a )
    end

    def test_cart_02
      a = []
      [1,2,3].cart([4,5,6]) {|elem| a << elem }
      assert_equal( [[1, 4],[1, 5],[1, 6],[2, 4],[2, 5],[2, 6],[3, 4],[3, 5],[3, 6]], a )
    end

  end

=end
