Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement min_element #100

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions example/src/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,27 @@ IS_SAME(
/// [transpose]
)

HIDE(
/// [min_element]
using vals = metal::list<bool, int, long, char>;

// strict (i.e., irreflexive) ordering: _first_ minimum
template<class x, class y>
using smaller = metal::number<(sizeof(x) < sizeof(y))>;
IS_SAME(metal::min_element<vals, metal::lambda<smaller>>, bool);

// reflexive ordering: _last_ minimum
template<class x, class y>
using not_larger = metal::number<(sizeof(x) <= sizeof(y))>;
IS_SAME(metal::min_element<vals, metal::lambda<not_larger>>, char);

// can be used with Numbers; more customizable than metal::min
using nums = metal::numbers<4, 42, 17, 3, 20>;
IS_SAME(metal::min_element<nums, metal::lambda<metal::less>>, metal::number<3>);
IS_SAME(metal::min_element<nums>, metal::number<3>); // use default ordering
/// [min_element]
)

#if !defined(METAL_WORKAROUND)
HIDE(
/// [accumulate]
Expand Down
1 change: 1 addition & 0 deletions include/metal/list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "list/iota.hpp"
#include "list/join.hpp"
#include "list/list.hpp"
#include "list/min_element.hpp"
#include "list/none_of.hpp"
#include "list/partition.hpp"
#include "list/powerset.hpp"
Expand Down
122 changes: 122 additions & 0 deletions include/metal/list/min_element.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#ifndef METAL_LIST_MIN_ELEMENT_HPP
#define METAL_LIST_MIN_ELEMENT_HPP

#include "../config.hpp"
#include "../detail/sfinae.hpp"
#include "../lambda/lambda.hpp"
#include "../number/less.hpp"

namespace metal {
/// \cond
namespace detail {
template<class lbd = metal::lambda<metal::less>>
struct _min_element;
}
/// \endcond

/// \ingroup list
///
/// ### Description
/// Finds a minimum element in a \list according to an ordering relation.
///
/// \note{The _first_ minimum element is returned if the ordering relation is [strict].}
/// [strict]:
/// https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings
///
/// ### Usage
/// For any \list `l` and \lambda `lbd`
/// \code
/// using result = metal::min_element<l, lbd>;
/// \endcode
///
/// \pre: `l` is not empty, and for any two \values `val_i` and `val_j`
/// contained in `l` `metal::invoke<lbd, val_i, val_j>` returns a \number
/// \returns: \value
/// \semantics:
/// If `l` contains elements `val_0, ..., val_k, ..., val_m-1`, then
/// \code
/// using result = val_k;
/// \endcode
/// where `val_k` is an element in `l` such that
/// * `metal::invoke<lbd, val_k, val_j>{} != false` for all `j` in
/// `[0, k)` and
/// * `metal::invoke<lbd, val_i, val_k>{} == false` for all
/// `i` in `(k, m-1]`.
///
/// \tip{`lbd` may be omitted, in which case it defaults to `metal::lambda<metal::less>`.}
///
/// ### Example
/// \snippet list.cpp min_element
///
/// ### See Also
/// \see min, sort
#if !defined(METAL_WORKAROUND)
template<class seq, class lbd = metal::lambda<metal::less>>
using min_element =
detail::call<detail::_min_element<lbd>::template type, seq>;
#else
// MSVC 14 has shabby SFINAE support in case of default alias template args
template<class seq, class... lbd>
using min_element =
detail::call<detail::_min_element<lbd...>::template type, seq>;
#endif
}

#include "../list/list.hpp"
#include "../number/if.hpp"
#include "../value/fold_left.hpp"

namespace metal {
/// \cond
namespace detail {
template<class x, class y>
struct _min_element_min_impl {
template<template<class...> class expr>
using type = if_<expr<y, x>, y, x>;
};

template<template<class...> class expr>
struct _min_element_min {
template<class x, class y>
using type =
forward<_min_element_min_impl<x, y>::template type, expr>;
};

template<class x, class y>
struct _min_element_gcc_4_7_aliaser {
template<template<class...> class expr>
using type = typename _min_element_min<expr>::template type<x, y>;
};

template<template<class...> class expr>
struct _min_element_gcc_4_7_alias {
template<class x, class y>
using type = typename _min_element_gcc_4_7_aliaser<
x, y>::template type<expr>;

using lbd = lambda<type>;
};

template<class seq>
struct _min_element_impl {};

template<class... vals>
struct _min_element_impl<list<vals...>> {
template<template<class...> class expr>
using type = fold_left<
typename _min_element_gcc_4_7_alias<expr>::lbd, vals...>;
};

template<class lbd>
struct _min_element {};

template<template<class...> class expr>
struct _min_element<lambda<expr>> {
template<class seq>
using type = forward<_min_element_impl<seq>::template type, expr>;
};
}
/// \endcond
}

#endif
2 changes: 1 addition & 1 deletion include/metal/list/sort.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ namespace metal {
/// \snippet list.cpp sort
///
/// ### See Also
/// \see list, reverse, rotate
/// \see list, min_element, reverse, rotate
#if !defined(METAL_WORKAROUND)
template<class seq, class lbd = metal::lambda<metal::less>>
using sort = detail::call<
Expand Down
2 changes: 1 addition & 1 deletion include/metal/number/min.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace metal {
/// \snippet number.cpp min
///
/// ### See Also
/// \see number, greater, less, max
/// \see number, greater, less, max, min_element
template<class... nums>
using min = fold_left<lambda<detail::min>, if_<is_number<nums>, nums>...>;
}
Expand Down
77 changes: 77 additions & 0 deletions test/unit/src/metal/list/min_element.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include <metal.hpp>

#include "test.hpp"

#define MATRIX(M, N) \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, VALUE(M)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, VALUE(M), VALUE(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, VALUE(M), NUMBER(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, VALUE(M), PAIR(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, VALUE(M), LIST(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, VALUE(M), MAP(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, VALUE(M), LAMBDA(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, NUMBER(M)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, NUMBER(M), VALUE(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, NUMBER(M), NUMBER(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, NUMBER(M), PAIR(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, NUMBER(M), LIST(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, NUMBER(M), MAP(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, NUMBER(M), LAMBDA(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, NUMBER(M), LAMBDA(_)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, PAIR(M)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, PAIR(M), VALUE(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, PAIR(M), NUMBER(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, PAIR(M), PAIR(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, PAIR(M), LIST(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, PAIR(M), MAP(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, PAIR(M), LAMBDA(N)>), (BOOL(N == 2))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, PAIR(M), LAMBDA(_)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LIST(M)>), (BOOL(M == 1))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LIST(M), VALUE(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LIST(M), NUMBER(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LIST(M), PAIR(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LIST(M), LIST(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LIST(M), MAP(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LIST(M), LAMBDA(N)>), (BOOL((N == 2 && M > 0) || M == 1))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LIST(M), LAMBDA(_)>), (BOOL(M == 1))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, MAP(M)>), (BOOL(M == 1))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, MAP(M), VALUE(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, MAP(M), NUMBER(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, MAP(M), PAIR(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, MAP(M), LIST(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, MAP(M), MAP(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, MAP(M), LAMBDA(N)>), (BOOL((N == 2 && M > 0) || M == 1))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, MAP(M), LAMBDA(_)>), (BOOL(M == 1))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, metal::list<NUMBERS(M)>>), (BOOL(M > 0))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, metal::list<NUMBERS(M)>, VALUE(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, metal::list<NUMBERS(M)>, NUMBER(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, metal::list<NUMBERS(M)>, PAIR(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, metal::list<NUMBERS(M)>, LIST(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, metal::list<NUMBERS(M)>, MAP(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, metal::list<NUMBERS(M)>, metal::list<NUMBERS(N)>>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, metal::list<NUMBERS(M)>, LAMBDA(N)>), (BOOL((N == 2 && M > 0) || M == 1))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, metal::list<NUMBERS(M)>, LAMBDA(_)>), (BOOL(M == 1))); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(M)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(M), VALUE(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(M), NUMBER(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(M), PAIR(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(M), LIST(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(M), MAP(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(M), LAMBDA(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(M), LAMBDA(_)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(_)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(_), VALUE(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(_), NUMBER(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(_), PAIR(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(_), LIST(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(_), MAP(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(_), LAMBDA(N)>), (FALSE)); \
CHECK((metal::is_invocable<metal::lambda<metal::min_element>, LAMBDA(_), LAMBDA(_)>), (FALSE)); \
CHECK((metal::min_element<metal::list<NUMBERSX20(INC(M))>, metal::lambda<metal::greater>>), (metal::number<M>)); \
CHECK((metal::min_element<metal::list<NUMBERSX20(INC(M))>, metal::lambda<metal::less>>), (metal::number<0>)); \
CHECK((metal::min_element<metal::list<NUMBERSX20(INC(M))>>), (metal::number<0>)); \
CHECK((metal::min_element<metal::list<ENUM(INC(N), NUMBERS FIX(INC(M)))>, metal::lambda<metal::less>>), (metal::number<0>)); \
CHECK((metal::min_element<metal::list<ENUM(INC(N), NUMBERS FIX(INC(M)))>>), (metal::number<0>)); \
/**/

GEN(MATRIX)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice test coverage!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is derived from the test of metal::sort.

It bothers me that the test is passing while the example fails on GCC 4.7.