# = elementwise.rb
#
# CREDIT Martin DeMello
#
# TODO depreate #ew in favor of ~@ operator ?

require 'facets/more/functor'

module Enumerable

  # Returns an elementwise Functor designed to make R-like
  # elementwise operations possible.
  #
  #   [1,2].elements + 3          #=> [4,5]
  #   [1,2].elements + [4,5]      #=> [5,7]
  #   [1,2].elements + [[4,5],3]  #=> [[5,7],[4,5]
  #
  #--
  # Special thanks to Martin DeMello for helping to develop this.
  #++

  def elementwise
    @_elementwise_functor ||= Functor.new do |op,*args|
      a = args.collect do |arg|
        if arg.kind_of?(Enumerable)
          ln = ( arg.length > self.length ? self.length : arg.length )
          self[0...ln].zip(arg[0...ln]).collect{ |a,b| a.send(op,b) }
          #self[0...ln].zip(arg[0...1n]).collect{ |a,b| b ? a.send(op,b) : nil }
        else
          self.collect{ |a| a.send(op,arg) }
        end
      end
      a.flatten! if args.length == 1
      a
    end
  end

  alias_method :ew, :elementwise

end


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

  require 'test/unit'

  class TestElementWise < Test::Unit::TestCase

    def test_elementwise
      a = [1,2,3]
      b = [4,5]
      assert_equal( [4,5,6], a.elementwise + 3 )
      assert_equal( [5,7], a.elementwise + b )
      assert_equal( [[5,7],[3,4,5]], a.elementwise.+(b,2) )
      assert_equal( [[5,7],[4,5,6]], a.elementwise.+(b,3) )
    end

    def test_ew
      a = [1,2,3]
      b = [4,5]
      assert_equal( [4,5,6], a.ew + 3 )
      assert_equal( [5,7], a.ew + b )
      assert_equal( [[5,7],[3,4,5]], a.ew.+(b,2) )
      assert_equal( [[5,7],[4,5,6]], a.ew.+(b,3) )
    end

  end

=end
