#!/usr/bin/env ruby

class Module
  def memoize method_name
    memo ||= { }
    orig_method = instance_method(method_name)
    define_method method_name do |*args|
      memo.fetch(Array.new(args).unshift(self)) do |key|
        val = orig_method.bind(self).call(*args)
        memo[key] = val
      end
    end
  end
end

class Computer
  def factor(n)
    raise ArgumentError if (!n.is_a?(Integer) or n < 0)
    return 1 if n == 0
    n * factor(n-1)
  end
end


require 'benchmark'

array = (1..100000).map { rand 100 }
computer = Computer.new

Benchmark.bm(20) do |x|
  x.report("normal factor:") { array.each { |i| computer.factor(i) } }
  Computer.instance_eval { memoize :factor }
  x.report("memoize installed:") { array.each { |i| computer.factor(i) } }
  x.report("all cached:") { array.each { |i| computer.factor(i) } }
end
