def zero(f)
  return lambda {|x| x}
end
Zero = lambda { |f| zero(f) }

def succ(n)
  return lambda { |f| lambda { |x| f.(n.(f).(x)) } }
end

Three = succ(succ(succ(Zero)))

def add(n, m)
  return lambda { |f| lambda { |x| m.(f).(n.(f).(x)) } }
end

def mult(n, m)
  return lambda { |f| lambda { |x| m.(n.(f)).(x) } }
end

def power(b, e)
  return e.(b)
end

def int_from_couch(f)
  countup = lambda { |i| i+1 }
  f.(countup).(0)
end

def couch_from_int(x)
  countdown = lambda { |i|
    case i 
      when 0 then Zero 
      else succ(countdown.(i-1))
    end
  }
  countdown.(x)
end

Four  = couch_from_int(4)

puts [ add(Three, Four),
       mult(Three, Four),
       power(Three, Four),
       power(Four, Three) ].map {|f| int_from_couch(f) }