/****************************************************************/
/**** A Trait For Ordering **************************************/ 
/****************************************************************/

trait Ord[A] {

  //Must Be Implemented:
  // this cmp that < 0  iff this < that
  // this cmp that > 0  iff this > that
  // this cmp that == 0 iff this == that 
  def cmp(that: A): Int 
  
  //Automatically derived from cmp 
  def ===(that: A): Boolean = (this cmp that) == 0
  def <(that: A): Boolean   = (this cmp that) < 0
  def >(that: A): Boolean   = (this cmp that) > 0
  def <=(that: A): Boolean  = (this cmp that) <= 0
  def >=(that: A): Boolean  = (this cmp that) <= 0

}

/***********************************************************/
/******* Creating Ord Proxies ("Views") ********************/
/***********************************************************/

object ProxyDemo {
 
  def findMin[A](cur: A, xs: List[A])(proxy: A => Ord[A]): A = 
    xs match { 
      case Nil                      => cur
      case h::t if (proxy(h) < cur) => findMin(h, t)(proxy) 
      case _::t                     => findMin(cur, t)(proxy)
    }

  def intProxy(x: Int) = new Ord[Int] {
    def cmp(that: Int) : Int = x - that
  }
 
  def stringProxy(x: String) = new Ord[String] {
    def cmp(that: String) : Int = x compare that
  }

  val res0 = findMin(1000, List(20, 4, 1, 6))(intProxy)
  val res1 = findMin("zzz", List("lengua", "chorizo", "adobado", "machaca"))(stringProxy)

}


/***********************************************************/
/******* Implicit Proxies ("Views") ************************/
/***********************************************************/

object OrdInstances {
 
  // with implicit argument syntax
  def findMin[A](cur: A, xs: List[A])(implicit proxy: A => Ord[A]): A = 
    xs match { 
      case Nil               => cur
      case h::t if (h < cur) => findMin(h, t) 
      case _::t              => findMin(cur, t)
    }

  // with view bound syntax
  def findMinV[A <% Ord[A]](cur: A, xs: List[A]): A = 
    xs match { 
      case Nil               => cur
      case h::t if (h < cur) => findMin(h, t) 
      case _::t              => findMin(cur, t)
    }


  implicit def intProxy(x: Int) = new Ord[Int] {
    def cmp(that: Int) : Int = x - that
  }
 
  implicit def stringProxy(x: String) = new Ord[String] {
    def cmp(that: String) : Int = x compare that
  }
  
  implicit def tup2Proxy[A <% Ord[A], B <% Ord[B]](x: (A, B)) 
    = new Ord[(A, B)] {
        def cmp(that: (A, B)) : Int = { 
          val c1 = x._1 cmp that._1
          if (c1 != 0) c1 else { x._2 cmp that._2 } 
        }
      }

  implicit def list2Proxy[A <% Ord[A]](x: List[A]) 
    = new Ord[List[A]] {
        def cmp(that: List[A]) : Int =
          sys.error("YOU FILL THIS IN")   
      }
 
  implicit def set2Proxy[A <% Ord[A]](x: Set[A]) 
    = new Ord[Set[A]] {
        def cmp(that: Set[A]) : Int =
          sys.error("YOU FILL THIS IN")   
      }
 
}
