=begin rdoc
My personal Ruby utilities

Author:: Jan-Ole Esleben (esleben@cl.uni-heidelberg.de)
Embedded Documentation Tool:: rdoc
=end

class Object
  def deepcopy
    self
  end
end


module Enumerable
  def deepcopy
    if self.methods.include? 'push'
      begin
        a = self.class.new
      rescue ArgumentError
        return self
      end
      each do | el |
        a.push el.deepcopy
      end
      a
    else
      self
    end
  end
end


class Array
  # Reduces the array to a single value; this method works like +map+
  # except that its block actually takes _two_ arguments, the previous
  # result and the next element.
  # 
  # Example: [1, 2, 3].reduce(0) { |x, y| x+y } -> 6
  #
  # The _first_ result is a new instance of the first array element's class 
  # by default, or +initval+ if it's set.
  # Note that nil _cannot_ be used as initial value!
  def reduce(initval=nil)
    return nil if empty?
    initval = self[0].class.new unless initval
    res = yield initval, self[0]
    self[1..-1].each do | el |
      res = yield res, el
    end
    res
  end
end


class Hash
  # Return the value for +key+ from the hash; if +key+ does not exist,
  # return _the result of calling the associated block_.
  #
  # Usually, this would probably be used as in this example (the example
  # ensures that each element gets its _own_ Array):
  #
  # +h.setdefault(key){Array.new} << val+
  def setdefault key
    self[key]=yield unless has_key? key 
    self[key]
  end

  def deepcopy
    h = Hash.new
    each_pair do | k, v |
      h[k.deepcopy] = v.deepcopy
    end
    h
  end
end


class String
  def deepcopy
    s = String.new
    length.times do | idx |
      s += self[idx..idx]
    end
    s
  end
end


def test
  nel0, nel1 = [0, 1], [2, 3]
  el1, el2 = "123", {nel0=>nel1}
  a = [1, el1, el2]
  puts a.inspect
  b = a.deepcopy
  puts b.inspect
  puts "a==b: #{a==b}"
  b[2][[0, 1]] = 4
  puts a.inspect
  puts b.inspect
end

