/*
Functional implementation
Functional decomposition (patterns)
*/
/*
trait Nat
 
case object Zero extends Nat
case class Succ(n: Nat) extends Nat
case object Infty extends Nat
 
def isZero(n: Nat): Boolean =
  n match {
    case Zero => true
    case _ => false
  }
 
def add(n: Nat, m: Nat): Nat =
  n match {
    case Zero => m
    case Succ(np) => Succ(add(np,m))
    case Infty => n
  }
 
def equals(n: Nat, m: Nat): Boolean =
  (n,m) match {
    case (Zero, Zero) => true
    case (Succ(np), Succ(mp)) => equals(np,mp)
    case (Infty,Infty) => true
    case _ => false
  }
 
//add(Succ(Succ(Zero)), Succ(Zero))
 
def subtract(n: Nat, m: Nat): Nat =
  (n,m) match {
    case (Zero,_) => Zero
    case (_,Zero) => n
    case (Succ(np),Succ(mp)) => subtract(np,mp)
    case (Infty,_) => n
  }
*/
 
 
// OOriented implementation
// OOriented decomposition
trait Nat {
  def isZero: Boolean
  def add(other: Nat): Nat
  def equals(other: Nat): Boolean
  def subtract(other: Nat): Nat
}
 
case object Zero extends Nat {
  override def isZero: Boolean = true
  override def add(other: Nat):Nat = other
  override def equals(other: Nat): Boolean =
    other match {
      case Zero => true
      case _ => false
    }
  override def subtract(other: Nat): Nat = this
}
 
case class Succ(n: Nat) extends Nat {
  override def isZero: Boolean = false
  override def add(other: Nat): Nat =
    Succ(n.add(other))
  override def equals(other: Nat): Boolean =
    other match {
      case Zero => false
      case Succ(mp) => n.equals(mp)
    }
  override def subtract(other: Nat): Nat =
    other match {
      case Zero => this
      case Succ(mp) => n.subtract(mp)
    }
 
}
 
case object Infty extends Nat {
  override def isZero: Boolean = false
  override def add(other: Nat): Nat = this
  override def equals(other: Nat): Boolean =
    other match {
      case Infty => true
      case _ => false
    }
  override def subtract(other: Nat): Nat = this
 
}
 
 
// Scenario 1: We want to add a new function: subtraction:
// What do we have to do?!
// OO implementation
 
// Scenario 2: We want to add a new value to the type