object Decorators {
    
  object profile { 
    
    private var cm: Map[String, Int] =  Map().withDefault(_ => 0)

    def count(name: String) = 
      cm(name)
   
    def reset(name: String) = 
      cm += name -> 0

    def apply[A, B](name: String)(f: A => B) = new Function1[A, B] {
      def apply(x: A) = {
        val n = cm(name)
        cm += name -> (1 + n)
        f(x)
      }
    }
  }
 
  object trace {
    def apply[A, B](name: String)(f: A => B) : Function1[A, B] = new Function1[A, B] {
      // You may add more fields here
      def apply (x: A): B = sys.error("TO BE DONE")
    }
  } 
  
  object memo {
    def apply[A, B](f: A => B) : Function1[A, B] = new Function1[A, B] {
      // You may add more fields here
      def apply (x: A): B = sys.error("TO BE DONE")
    }
  }

}

object DecoratorTest {

  val fac : Int => Int = 
    Decorators.profile("fac") {
      (n: Int) => if (n <= 1) { 1 } else { n * fac(n-1) }
    }

  val fib : Int => Int = 
    { case 0 => 1
      case 1 => 1
      case n => fib(n-1) + fib(n-2)
    }
  
  val fibP : Int => Int = 
    Decorators.profile ("fibP") { 
      case 0 => 1
      case 1 => 1
      case n => fibP(n-1) + fibP(n-2)
    }
  } 
