# $Id: array.rb,v 1.5 2002/12/10 13:29:16 knu Exp $

require 'features/ruby18/base'

class Array
  alias __ruby16_initialize initialize
  alias __ruby16_fill fill
  alias __ruby16_select select

  def initialize(asize=0, val=nil)
    case asize
    when Array
      replace(asize)
      return
    when Fixnum
      if block_given?
	asize.times { |i|
	  self.push yield(i)
	}
	return
      end
    end
    __ruby16_initialize(asize, val)
  end

  def __ruby18_abs_index(i)
    if i < 0
      i += size 
      return nil if i < 0
    end
    i    
  end
  private :__ruby18_abs_index

  def __ruby18_abs_range(r)
    b = __ruby18_abs_index(r.begin)
    e = __ruby18_abs_index(r.end)

    b && e or
      raise RangeError, "#{r} out of range"

    if r.exclude_end?
      b...e
    else
      b..e
    end
  end
  private :__ruby18_abs_range

  def fetch(i, *x)
    j = __ruby18_abs_index(i)

    return self[j] if j && j < size

    ifnone, = *x

    if block_given?
      x.empty? or
	raise ArgumentError, "wrong number of arguments"

      return yield(i)
    end

    x.empty? and
      raise IndexError, "index #{i} out of array"

    return ifnone
  end

  def fill(*x)
    block_given? or return __ruby16_fill(*x)

    case x.size
    when 2
      b, len = *x

      b = __ruby18_abs_index(b) || 0
    when 1
      x = x[0]

      if x.is_a?(Range)
	r = __ruby18_abs_range(x)
	b = r.begin
	len = r.size
      else
	b = __ruby18_abs_index(x) || 0

	return self if size <= b

	len = size - b
      end
    when 0
      b = 0
      len = size
    else
      raise ArgumentError, "wrong number of arguments(#{x.size} for 2)"
    end

    return self if len <= 0

    for i in b...(b + len)
      self[i] = yield(i)
    end

    self
  end

  def insert(i, *x)
    x.empty? and
      raise ArgumentError, "wrong number of arguments(at least 2)"

    self[i,0] = x
    self
  end

  def select(*x, &block)
    if block
      __ruby16_select(*x, &block)
    else
      indexes(*x)
    end
  end

  def zip(*args, &block)
    arrays = Array.new(1 + args.size)
    arrays[0] = self
    args.each_with_index { |e, i|
      e.is_a?(Array) or raise TypeError, "argument must be an Array"
      arrays[1 + i] = e
    }

    if block
      each_with_index do |e, i|
	tmp = arrays.map { |ary| ary[i] }
	block.call(tmp)
      end

      nil
    else
      results = Array.new(arrays.size)

      each_with_index do |e, i|
	tmp = arrays.map { |ary| ary[i] }
	results[i] = tmp
      end

      results
    end
  end

  def transpose()
    return dup if empty?

    alen = size
    elen = -1
    results = nil

    each_with_index do |e, i|
      e.is_a?(Array) or raise TypeError, "argument must be an Array"
      tmp = e

      if elen < 0	# first element
	elen = tmp.size
	results = Array.new(elen)
	elen.times { |j| results[j] = Array.new(alen) }
      elsif elen != tmp.size
	raise IndexError, "element size differ (#{tmp.size} should be #{elen})"
      end
      elen.times { |j| results[j][i] = tmp[j] }
    end

    results
  end
end
