src/gleeps/stdlib/order.gleam

/// Represents the result of a single comparison to determine the precise
/// ordering of two values.
///
pub type Order {
  /// Less-than
  Lt

  /// Equal
  Eq

  /// Greater than
  Gt
}

/// Inverts an order, so less-than becomes greater-than and greater-than
/// becomes less-than.
///
/// ## Examples
///
/// ```gleam
/// assert negate(Lt) == Gt
/// ```
///
/// ```gleam
/// assert negate(Eq) == Eq
/// ```
///
/// ```gleam
/// assert negate(Gt) == Lt
/// ```
///
pub fn negate(order: Order) -> Order {
  case order {
    Lt -> Gt
    Eq -> Eq
    Gt -> Lt
  }
}

/// Produces a numeric representation of the order.
///
/// ## Examples
///
/// ```gleam
/// assert to_int(Lt) == -1
/// ```
///
/// ```gleam
/// assert to_int(Eq) == 0
/// ```
///
/// ```gleam
/// assert to_int(Gt) == 1
/// ```
///
pub fn to_int(order: Order) -> Int {
  case order {
    Lt -> -1
    Eq -> 0
    Gt -> 1
  }
}

/// Compares two `Order` values to one another, producing a new `Order`.
///
/// ## Examples
///
/// ```gleam
/// assert compare(Eq, with: Lt) == Gt
/// ```
///
pub fn compare(a: Order, with b: Order) -> Order {
  case a, b {
    x, y if x == y -> Eq
    Lt, _ | Eq, Gt -> Lt
    _, _ -> Gt
  }
}

/// Inverts an ordering function, so less-than becomes greater-than and greater-than
/// becomes less-than.
///
/// ## Examples
///
/// ```gleam
/// import gleam/int
/// import gleam/list
///
/// assert list.sort([1, 5, 4], by: reverse(int.compare)) == [5, 4, 1]
/// ```
///
pub fn reverse(orderer: fn(a, a) -> Order) -> fn(a, a) -> Order {
  fn(a, b) { orderer(b, a) }
}

/// Return a fallback `Order` in case the first argument is `Eq`.
///
/// ## Examples
///
/// ```gleam
/// import gleam/int
///
/// assert break_tie(in: int.compare(1, 1), with: Lt) == Lt
/// ```
///
/// ```gleam
/// import gleam/int
///
/// assert break_tie(in: int.compare(1, 0), with: Eq) == Gt
/// ```
///
pub fn break_tie(in order: Order, with other: Order) -> Order {
  case order {
    Lt | Gt -> order
    Eq -> other
  }
}

/// Invokes a fallback function returning an `Order` in case the first argument
/// is `Eq`.
///
/// This can be useful when the fallback comparison might be expensive and it
/// needs to be delayed until strictly necessary.
///
/// ## Examples
///
/// ```gleam
/// import gleam/int
///
/// assert lazy_break_tie(in: int.compare(1, 1), with: fn() { Lt }) == Lt
/// ```
///
/// ```gleam
/// import gleam/int
///
/// assert lazy_break_tie(in: int.compare(1, 0), with: fn() { Eq }) == Gt
/// ```
///
pub fn lazy_break_tie(in order: Order, with comparison: fn() -> Order) -> Order {
  case order {
    Lt | Gt -> order
    Eq -> comparison()
  }
}