object Exponentiation {
  import scala.annotation.tailrec

  @tailrec def powI[N](n: N, exponent: Int)(implicit num: Integral[N]): N = {
    import num._
    exponent match {
      case 0 => one
      case _ if exponent % 2 == 0 => powI((n * n), (exponent / 2))
      case _ => powI(n, (exponent - 1)) * n
    }
  }

  @tailrec def powF[N](n: N, exponent: Int)(implicit num: Fractional[N]): N = {
    import num._
    exponent match {
      case 0 => one
      case _ if exponent < 0 => one / powF(n, exponent.abs)
      case _ if exponent % 2 == 0 => powF((n * n), (exponent / 2))
      case _ => powF(n, (exponent - 1)) * n
    }
  }

  class ExponentI[N : Integral](n: N) {
    def \u2191(exponent: Int): N = powI(n, exponent)
  }

  class ExponentF[N : Fractional](n: N) {
    def \u2191(exponent: Int): N = powF(n, exponent)
  }

  object ExponentI {
    implicit def toExponentI[N : Integral](n: N): ExponentI[N] = new ExponentI(n)
  }

  object ExponentF {
    implicit def toExponentF[N : Fractional](n: N): ExponentF[N] = new ExponentF(n)
  }

  object Exponents {
    implicit def toExponent(n: Int): ExponentI[Int] = new ExponentI(n)
    implicit def toExponent(n: Double): ExponentF[Double] = new ExponentF(n)
  }
}