package gs.std.v0.core

/** Opaque type for the natural numbers (including 0).
  */
opaque type Nat = Int

/** Opaque type for the natural numbers (including 0).
  */
object Nat:

  sealed trait Invalid
  object Invalid extends Invalid

  /** The number 0.
    */
  final val Zero: Nat = 0

  /** The number 1.
    */
  final val One: Nat = 1

  /** Express the given integer as a natural number.
    *
    * Throws an `IllegalArgumentException` if a negative value is given as
    * input.
    *
    * @param value
    *   The input integer.
    * @return
    *   The [[Nat]] instance.
    */
  def apply(value: Int): Nat =
    if value >= 0 then value
    else throw new IllegalArgumentException("Nat values must be 0 or greater.")

  def validate(value: Int): Either[Invalid, Nat] =
    if value >= 0 then Right(value) else Left(Invalid)

  given CanEqual[Nat, Nat] = CanEqual.derived

  given Ordering[Nat] with

    /** @inheritDocs
      */
    def compare(
      x: Nat,
      y: Nat
    ): Int = x - y

  extension (nat: Nat)
    /** @return
      *   The underlying integer.
      */
    def unwrap(): Int = nat

    /** @return
      *   The next integer.
      */
    def next(): Nat = nat + 1

    /** @return
      *   The next integer.
      */
    def increment(): Nat = nat + 1

    /** Add some natural number to this one.
      *
      * @param that
      *   The number to add.
      * @return
      *   The sum of the numbers.
      */
    def +(that: Nat): Nat = nat + that

    /** Multiply this natural number by some other natural number.
      *
      * @param that
      *   The number to multiply by.
      * @return
      *   The product of the numbers.
      */
    def *(that: Nat): Nat = nat * that

end Nat
