# <!-- rdoc-file=compar.c -->
# The Comparable mixin is used by classes whose objects may be ordered. The
# class must define the `<=>` operator, which compares the receiver against
# another object, returning a value less than 0, returning 0, or returning a
# value greater than 0, depending on whether the receiver is less than, equal
# to, or greater than the other object. If the other object is not comparable
# then the `<=>` operator should return `nil`. Comparable uses `<=>` to
# implement the conventional comparison operators (`<`, `<=`, `==`, `>=`, and
# `>`) and the method `between?`.
#
#     class StringSorter
#       include Comparable
#
#       attr :str
#       def <=>(other)
#         str.size <=> other.str.size
#       end
#
#       def initialize(str)
#         @str = str
#       end
#
#       def inspect
#         @str
#       end
#     end
#
#     s1 = StringSorter.new("Z")
#     s2 = StringSorter.new("YY")
#     s3 = StringSorter.new("XXX")
#     s4 = StringSorter.new("WWWW")
#     s5 = StringSorter.new("VVVVV")
#
#     s1 < s2                       #=> true
#     s4.between?(s1, s3)           #=> false
#     s4.between?(s3, s5)           #=> true
#     [ s3, s2, s5, s4, s1 ].sort   #=> [Z, YY, XXX, WWWW, VVVVV]
#
# ## What's Here
#
# Module Comparable provides these methods, all of which use method `#<=>`:
#
# *   #<: Returns whether `self` is less than the given object.
# *   #<=: Returns whether `self` is less than or equal to the given object.
# *   #==: Returns whether `self` is equal to the given object.
# *   #>: Returns whether `self` is greater than the given object.
# *   #>=: Returns whether `self` is greater than or equal to the given object.
# *   #between?: Returns `true` if `self` is between two given objects.
# *   #clamp: For given objects `min` and `max`, or range `(min..max)`, returns:
#
#     *   `min` if `(self <=> min) < 0`.
#     *   `max` if `(self <=> max) > 0`.
#     *   `self` otherwise.
#
module Comparable : _WithSpaceshipOperator
  # <!--
  #   rdoc-file=compar.c
  #   - obj < other    -> true or false
  # -->
  # Compares two objects based on the receiver's `<=>` method, returning true if
  # it returns a value less than 0.
  #
  def <: (untyped other) -> bool

  # <!--
  #   rdoc-file=compar.c
  #   - obj <= other    -> true or false
  # -->
  # Compares two objects based on the receiver's `<=>` method, returning true if
  # it returns a value less than or equal to 0.
  #
  def <=: (untyped other) -> bool

  # <!--
  #   rdoc-file=compar.c
  #   - obj == other    -> true or false
  # -->
  # Compares two objects based on the receiver's `<=>` method, returning true if
  # it returns 0. Also returns true if *obj* and *other* are the same object.
  #
  def ==: (untyped other) -> bool

  # <!--
  #   rdoc-file=compar.c
  #   - obj > other    -> true or false
  # -->
  # Compares two objects based on the receiver's `<=>` method, returning true if
  # it returns a value greater than 0.
  #
  def >: (untyped other) -> bool

  # <!--
  #   rdoc-file=compar.c
  #   - obj >= other    -> true or false
  # -->
  # Compares two objects based on the receiver's `<=>` method, returning true if
  # it returns a value greater than or equal to 0.
  #
  def >=: (untyped other) -> bool

  # <!--
  #   rdoc-file=compar.c
  #   - obj.between?(min, max)    -> true or false
  # -->
  # Returns `false` if *obj* `<=>` *min* is less than zero or if *obj* `<=>` *max*
  # is greater than zero, `true` otherwise.
  #
  #     3.between?(1, 5)               #=> true
  #     6.between?(1, 5)               #=> false
  #     'cat'.between?('ant', 'dog')   #=> true
  #     'gnu'.between?('ant', 'dog')   #=> false
  #
  def between?: (untyped min, untyped max) -> bool

  # <!--
  #   rdoc-file=compar.c
  #   - obj.clamp(min, max) ->  obj
  #   - obj.clamp(range)    ->  obj
  # -->
  # In `(min, max)` form, returns *min* if *obj* `<=>` *min* is less than zero,
  # *max* if *obj* `<=>` *max* is greater than zero, and *obj* otherwise.
  #
  #     12.clamp(0, 100)         #=> 12
  #     523.clamp(0, 100)        #=> 100
  #     -3.123.clamp(0, 100)     #=> 0
  #
  #     'd'.clamp('a', 'f')      #=> 'd'
  #     'z'.clamp('a', 'f')      #=> 'f'
  #
  # If *min* is `nil`, it is considered smaller than *obj*, and if *max* is `nil`,
  # it is considered greater than *obj*.
  #
  #     -20.clamp(0, nil)           #=> 0
  #     523.clamp(nil, 100)         #=> 100
  #
  # In `(range)` form, returns *range.begin* if *obj* `<=>` *range.begin* is less
  # than zero, *range.end* if *obj* `<=>` *range.end* is greater than zero, and
  # *obj* otherwise.
  #
  #     12.clamp(0..100)         #=> 12
  #     523.clamp(0..100)        #=> 100
  #     -3.123.clamp(0..100)     #=> 0
  #
  #     'd'.clamp('a'..'f')      #=> 'd'
  #     'z'.clamp('a'..'f')      #=> 'f'
  #
  # If *range.begin* is `nil`, it is considered smaller than *obj*, and if
  # *range.end* is `nil`, it is considered greater than *obj*.
  #
  #     -20.clamp(0..)           #=> 0
  #     523.clamp(..100)         #=> 100
  #
  # When *range.end* is excluded and not `nil`, an exception is raised.
  #
  #     100.clamp(0...100)       # ArgumentError
  #
  def clamp: [A, B] (A min, B max) -> (self | A | B)
           | [A] (Range[A]) -> (self | A)
end

# This interface defines the condition for Comparable mixin.
#
interface Comparable::_WithSpaceshipOperator
  # `<=>` operator must return Integer or `nil`.
  # If `other` is greater than `self`, it returns a positive Integer.
  # If `other` equals to `self`, it returns zero.
  # If `other` is less than `self`, it returns a positive Integer.
  # If no comparison is defined with `other` and `self`, it returns `nil`.
  #
  def <=>: (untyped other) -> Integer?
end
