/// Const equivalent of [`std::cmp::min`](core::cmp::min)
///
/// The arguments must implement the [`ConstCmp`] trait.
/// Non-standard library types must define a `const_eq` method taking a reference.
///
/// Returns the `$left` argument if both compare equal.
///
/// # Example
///
/// ```rust
/// const M: u32 = konst::cmp::min!(3u32, 5);
/// assert_eq!(M, 3);
/// ```
///
/// [`ConstCmp`]: crate::cmp::ConstCmp
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
pub use crate::__min as min;

#[doc(hidden)]
#[macro_export]
macro_rules! __min {
    ($left:expr, $right:expr) => {
        match ($left, $right) {
            (left, right) => {
                if let $crate::__::Greater = $crate::cmp::const_cmp!(left, right) {
                    right
                } else {
                    left
                }
            }
        }
    };
}

/// Const equivalent of [`std::cmp::max`](core::cmp::max)
///
/// The arguments must implement the [`ConstCmp`] trait.
/// Non-standard library types must define a `const_eq` method taking a reference.
///
/// Returns the `$right` argument if both compare equal.
///
/// # Example
///
/// ```rust
/// const M: &str = konst::cmp::max!("world", "hello");
/// assert_eq!(M, "world");
/// ```
///
/// [`ConstCmp`]: crate::cmp::ConstCmp
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
pub use crate::__max as max;

#[doc(hidden)]
#[macro_export]
macro_rules! __max {
    ($left:expr, $right:expr) => {
        match ($left, $right) {
            (left, right) => {
                if let $crate::__::Greater = $crate::cmp::const_cmp!(left, right) {
                    left
                } else {
                    right
                }
            }
        }
    };
}

////////////////////////////////////////////////////////////////////////////////

/// Const equivalent of [`std::cmp::min_by`](core::cmp::min_by)
///
/// Returns the `$left` argument if both compare equal.
///
#[doc = crate::docs::closure_arg_annotated_params_limitations_docs!("")]
///
/// # Example
///
/// ```rust
/// use konst::cmp::const_cmp;
///
/// // passing a pseudo-closure as the comparator
/// const AAA: u32 = konst::cmp::min_by!(3u32, 10, |&l, &r| const_cmp!(l, r / 4));
/// assert_eq!(AAA, 10);
///
/// // Both arguments compare equal, so the first argument (`12`) is returned.
/// const MIN_OF_EQ: u32 = konst::cmp::min_by!(12, 6, |l, r: &u32| const_cmp!(*l % 3, *r % 3));
/// assert_eq!(MIN_OF_EQ, 12);
///
/// const fn cmp_len(l: &str, r: &str) -> std::cmp::Ordering {
///     const_cmp!(l.len(), r.len())
/// }
///
/// // passing a function as the comparator
/// const BBB: &str = konst::cmp::min_by!("he", "bar", cmp_len);
/// assert_eq!(BBB, "he");
/// ```
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
pub use crate::__min_by as min_by;

#[doc(hidden)]
#[macro_export]
macro_rules! __min_by {
    ($left:expr, $right:expr, $($comparator:tt)*) => {
        $crate::__parse_closure_2!{
            ($crate::__min_by_inner)
            ($left, $right,)
            (min_by),

            $($comparator)*
        }
    };
}

/// Const equivalent of [`std::cmp::max_by`](core::cmp::max_by)
///
/// Returns the `$right` argument if both compare equal.
///
#[doc = crate::docs::closure_arg_annotated_params_limitations_docs!("")]
///
/// # Example
///
/// ```rust
/// use konst::cmp::const_cmp;
///
/// // passing a pseudo-closure as the comparator
/// const AAA: u32 = konst::cmp::max_by!(3u32, 10, |&l, &r| const_cmp!(l, r / 4));
/// assert_eq!(AAA, 3);
///
/// // Both arguments compare equal, so the second argument (`6`) is returned.
/// const MAX_OF_EQ: u32 = konst::cmp::max_by!(12, 6, |l: &u32, r| const_cmp!(*l % 3, *r % 3));
/// assert_eq!(MAX_OF_EQ, 6);
///
/// const fn cmp_len(l: &str, r: &str) -> std::cmp::Ordering {
///     konst::cmp::const_cmp!(l.len(), r.len())
/// }
///
/// // passing a function as the comparator
/// const BBB: &str = konst::cmp::max_by!("he", "bar", cmp_len);
/// assert_eq!(BBB, "bar");
/// ```
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
pub use crate::__max_by as max_by;

#[doc(hidden)]
#[macro_export]
macro_rules! __max_by {
    ($left:expr, $right:expr, $($comparator:tt)*) => {
        $crate::__parse_closure_2!{
            ($crate::__max_by_inner)
            ($left, $right,)
            (max_by),

            $($comparator)*
        }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! __min_by_inner {
    (
        $left:expr, $right:expr,
        ($($closure_params:tt)*) $(-> $ret_ty:ty)? $ret_val:block
    ) => {
        match [$left, $right] {
            [left, right] => {
                let $($closure_params)* = (&left, &right);
                let ret_val: $crate::__::Ordering = $ret_val;
                if let $crate::__::Greater = ret_val {
                    right
                } else {
                    left
                }
            }
        }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! __max_by_inner {
    (
        $left:expr, $right:expr,
        ($($closure_params:tt)*) $(-> $ret_ty:ty)? $ret_val:block
    ) => {
        match [$left, $right] {
            [left, right] => {
                let $($closure_params)* = (&left, &right);
                let ret_val: $crate::__::Ordering = $ret_val;
                if let $crate::__::Greater = ret_val {
                    left
                } else {
                    right
                }
            }
        }
    };
}

////////////////////////////////////////////////////////////////////////////////

/// Const equivalent of [`std::cmp::min_by_key`](core::cmp::min_by_key)
///
/// The type returned by the comparator must implement the [`ConstCmp`] trait.
/// Non-standard library types must define a `const_eq` method taking a reference.
///
/// Returns the `$left` argument if both compare equal.
///
#[doc = crate::docs::closure_arg_annotated_params_limitations_docs!("")]
///
/// # Example
///
/// ```rust
/// // passing a pseudo-closure as the comparator
/// const AAA: u32 = konst::cmp::min_by_key!(3u32, 10, |x| *x % 4);
/// assert_eq!(AAA, 10);
///
/// // Both arguments compare equal, so the first argument (`16`) is returned.
/// const MIN_OF_EQ: u32 = konst::cmp::min_by_key!(16u32, 8, |x| *x % 4);
/// assert_eq!(MIN_OF_EQ, 16);
///
/// // passing a function as the comparator
/// const BBB: &str = konst::cmp::min_by_key!("foo", "he", str::len);
/// assert_eq!(BBB, "he");
/// ```
///
/// [`ConstCmp`]: crate::cmp::ConstCmp
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
pub use crate::__min_by_key as min_by_key;

#[doc(hidden)]
#[macro_export]
macro_rules! __min_by_key {
    ($left:expr, $right:expr, $($comparator:tt)*) => {
        $crate::__parse_closure_1!{
            ($crate::__minmax_by_key)
            ($left, $right, Greater,)
            (min_by_key),

            $($comparator)*
        }
    };
}

/// Const equivalent of [`std::cmp::max_by_key`](core::cmp::max_by_key)
///
/// The type returned by the comparator must implement the [`ConstCmp`] trait.
/// Non-standard library types must define a `const_eq` method taking a reference.
///
/// Returns the `$right` argument if both compare equal.
///
#[doc = crate::docs::closure_arg_annotated_params_limitations_docs!("")]
///
/// # Example
///
/// ```rust
/// // passing a pseudo-closure as the comparator
/// const AAA: u32 = konst::cmp::max_by_key!(3u32, 10, |x| *x % 4);
/// assert_eq!(AAA, 3);
///
/// // Both arguments compare equal, so the second argument (`6`) is returned.
/// const MAX_OF_EQ: u32 = konst::cmp::max_by_key!(12, 6, |x: &u32| *x % 4);
/// assert_eq!(MAX_OF_EQ, 6);
///
/// // passing a function as the comparator
/// const BBB: &str = konst::cmp::max_by_key!("he", "bar", str::len);
/// assert_eq!(BBB, "bar");
/// ```
///
/// [`ConstCmp`]: crate::cmp::ConstCmp
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
pub use crate::__max_by_key as max_by_key;

#[doc(hidden)]
#[macro_export]
macro_rules! __max_by_key {
    ($left:expr, $right:expr, $($comparator:tt)*) => {
        $crate::__parse_closure_1!{
            ($crate::__minmax_by_key)
            ($right, $left, Less,)
            (max_by_key),

            $($comparator)*
        }
    };
}

#[macro_export]
#[doc(hidden)]
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
macro_rules! __minmax_by_key {
    (
        $left:expr, $right:expr, $ord:ident,
        ($($elem:tt)*) $(-> $ret_ty:ty)? $v:block
    ) => {
        match [$left, $right] {
            [left, right] => {
                let left_key $(: $ret_ty)? = {
                    let $($elem)* = &left;
                    $v
                };

                let right_key $(: $ret_ty)? = {
                    let $($elem)* = &right;
                    $v
                };

                if let $crate::__::$ord = $crate::cmp::const_cmp!(left_key, right_key) {
                    right
                } else {
                    left
                }
            }
        }
    };
}
