diff --git a/example/src/list.cpp b/example/src/list.cpp index 8a579e6c..9c5e01a7 100644 --- a/example/src/list.cpp +++ b/example/src/list.cpp @@ -657,6 +657,27 @@ IS_SAME( /// [transpose] ) +HIDE( +/// [min_element] +using vals = metal::list; + +// strict (i.e., irreflexive) ordering: _first_ minimum +template +using smaller = metal::number<(sizeof(x) < sizeof(y))>; +IS_SAME(metal::min_element>, bool); + + // reflexive ordering: _last_ minimum +template +using not_larger = metal::number<(sizeof(x) <= sizeof(y))>; +IS_SAME(metal::min_element>, 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>, metal::number<3>); +IS_SAME(metal::min_element, metal::number<3>); // use default ordering +/// [min_element] +) + #if !defined(METAL_WORKAROUND) HIDE( /// [accumulate] diff --git a/include/metal/list.hpp b/include/metal/list.hpp index 5548852b..76f0850a 100644 --- a/include/metal/list.hpp +++ b/include/metal/list.hpp @@ -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" diff --git a/include/metal/list/min_element.hpp b/include/metal/list/min_element.hpp new file mode 100644 index 00000000..3dea0028 --- /dev/null +++ b/include/metal/list/min_element.hpp @@ -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> + 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; + /// \endcode + /// + /// \pre: `l` is not empty, and for any two \values `val_i` and `val_j` + /// contained in `l` `metal::invoke` 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{} != false` for all `j` in + /// `[0, k)` and + /// * `metal::invoke{} == false` for all + /// `i` in `(k, m-1]`. + /// + /// \tip{`lbd` may be omitted, in which case it defaults to `metal::lambda`.} + /// + /// ### Example + /// \snippet list.cpp min_element + /// + /// ### See Also + /// \see min, sort +#if !defined(METAL_WORKAROUND) + template> + using min_element = + detail::call::template type, seq>; +#else + // MSVC 14 has shabby SFINAE support in case of default alias template args + template + using min_element = + detail::call::template type, seq>; +#endif +} + +#include "../list/list.hpp" +#include "../number/if.hpp" +#include "../value/fold_left.hpp" + +namespace metal { + /// \cond + namespace detail { + template + struct _min_element_min_impl { + template class expr> + using type = if_, y, x>; + }; + + template class expr> + struct _min_element_min { + template + using type = + forward<_min_element_min_impl::template type, expr>; + }; + + template + struct _min_element_gcc_4_7_aliaser { + template class expr> + using type = typename _min_element_min::template type; + }; + + template class expr> + struct _min_element_gcc_4_7_alias { + template + using type = typename _min_element_gcc_4_7_aliaser< + x, y>::template type; + + using lbd = lambda; + }; + + template + struct _min_element_impl {}; + + template + struct _min_element_impl> { + template class expr> + using type = fold_left< + typename _min_element_gcc_4_7_alias::lbd, vals...>; + }; + + template + struct _min_element {}; + + template class expr> + struct _min_element> { + template + using type = forward<_min_element_impl::template type, expr>; + }; + } + /// \endcond +} + +#endif diff --git a/include/metal/list/sort.hpp b/include/metal/list/sort.hpp index edd361c1..190fe769 100644 --- a/include/metal/list/sort.hpp +++ b/include/metal/list/sort.hpp @@ -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> using sort = detail::call< diff --git a/include/metal/number/min.hpp b/include/metal/number/min.hpp index c548e5a6..d66362d5 100644 --- a/include/metal/number/min.hpp +++ b/include/metal/number/min.hpp @@ -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 using min = fold_left, if_, nums>...>; } diff --git a/test/unit/src/metal/list/min_element.cpp b/test/unit/src/metal/list/min_element.cpp new file mode 100644 index 00000000..5c388896 --- /dev/null +++ b/test/unit/src/metal/list/min_element.cpp @@ -0,0 +1,77 @@ +#include + +#include "test.hpp" + +#define MATRIX(M, N) \ + CHECK((metal::is_invocable, VALUE(M)>), (FALSE)); \ + CHECK((metal::is_invocable, VALUE(M), VALUE(N)>), (FALSE)); \ + CHECK((metal::is_invocable, VALUE(M), NUMBER(N)>), (FALSE)); \ + CHECK((metal::is_invocable, VALUE(M), PAIR(N)>), (FALSE)); \ + CHECK((metal::is_invocable, VALUE(M), LIST(N)>), (FALSE)); \ + CHECK((metal::is_invocable, VALUE(M), MAP(N)>), (FALSE)); \ + CHECK((metal::is_invocable, VALUE(M), LAMBDA(N)>), (FALSE)); \ + CHECK((metal::is_invocable, NUMBER(M)>), (FALSE)); \ + CHECK((metal::is_invocable, NUMBER(M), VALUE(N)>), (FALSE)); \ + CHECK((metal::is_invocable, NUMBER(M), NUMBER(N)>), (FALSE)); \ + CHECK((metal::is_invocable, NUMBER(M), PAIR(N)>), (FALSE)); \ + CHECK((metal::is_invocable, NUMBER(M), LIST(N)>), (FALSE)); \ + CHECK((metal::is_invocable, NUMBER(M), MAP(N)>), (FALSE)); \ + CHECK((metal::is_invocable, NUMBER(M), LAMBDA(N)>), (FALSE)); \ + CHECK((metal::is_invocable, NUMBER(M), LAMBDA(_)>), (FALSE)); \ + CHECK((metal::is_invocable, PAIR(M)>), (FALSE)); \ + CHECK((metal::is_invocable, PAIR(M), VALUE(N)>), (FALSE)); \ + CHECK((metal::is_invocable, PAIR(M), NUMBER(N)>), (FALSE)); \ + CHECK((metal::is_invocable, PAIR(M), PAIR(N)>), (FALSE)); \ + CHECK((metal::is_invocable, PAIR(M), LIST(N)>), (FALSE)); \ + CHECK((metal::is_invocable, PAIR(M), MAP(N)>), (FALSE)); \ + CHECK((metal::is_invocable, PAIR(M), LAMBDA(N)>), (BOOL(N == 2))); \ + CHECK((metal::is_invocable, PAIR(M), LAMBDA(_)>), (FALSE)); \ + CHECK((metal::is_invocable, LIST(M)>), (BOOL(M == 1))); \ + CHECK((metal::is_invocable, LIST(M), VALUE(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LIST(M), NUMBER(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LIST(M), PAIR(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LIST(M), LIST(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LIST(M), MAP(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LIST(M), LAMBDA(N)>), (BOOL((N == 2 && M > 0) || M == 1))); \ + CHECK((metal::is_invocable, LIST(M), LAMBDA(_)>), (BOOL(M == 1))); \ + CHECK((metal::is_invocable, MAP(M)>), (BOOL(M == 1))); \ + CHECK((metal::is_invocable, MAP(M), VALUE(N)>), (FALSE)); \ + CHECK((metal::is_invocable, MAP(M), NUMBER(N)>), (FALSE)); \ + CHECK((metal::is_invocable, MAP(M), PAIR(N)>), (FALSE)); \ + CHECK((metal::is_invocable, MAP(M), LIST(N)>), (FALSE)); \ + CHECK((metal::is_invocable, MAP(M), MAP(N)>), (FALSE)); \ + CHECK((metal::is_invocable, MAP(M), LAMBDA(N)>), (BOOL((N == 2 && M > 0) || M == 1))); \ + CHECK((metal::is_invocable, MAP(M), LAMBDA(_)>), (BOOL(M == 1))); \ + CHECK((metal::is_invocable, metal::list>), (BOOL(M > 0))); \ + CHECK((metal::is_invocable, metal::list, VALUE(N)>), (FALSE)); \ + CHECK((metal::is_invocable, metal::list, NUMBER(N)>), (FALSE)); \ + CHECK((metal::is_invocable, metal::list, PAIR(N)>), (FALSE)); \ + CHECK((metal::is_invocable, metal::list, LIST(N)>), (FALSE)); \ + CHECK((metal::is_invocable, metal::list, MAP(N)>), (FALSE)); \ + CHECK((metal::is_invocable, metal::list, metal::list>), (FALSE)); \ + CHECK((metal::is_invocable, metal::list, LAMBDA(N)>), (BOOL((N == 2 && M > 0) || M == 1))); \ + CHECK((metal::is_invocable, metal::list, LAMBDA(_)>), (BOOL(M == 1))); \ + CHECK((metal::is_invocable, LAMBDA(M)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(M), VALUE(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(M), NUMBER(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(M), PAIR(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(M), LIST(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(M), MAP(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(M), LAMBDA(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(M), LAMBDA(_)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(_)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(_), VALUE(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(_), NUMBER(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(_), PAIR(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(_), LIST(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(_), MAP(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(_), LAMBDA(N)>), (FALSE)); \ + CHECK((metal::is_invocable, LAMBDA(_), LAMBDA(_)>), (FALSE)); \ + CHECK((metal::min_element, metal::lambda>), (metal::number)); \ + CHECK((metal::min_element, metal::lambda>), (metal::number<0>)); \ + CHECK((metal::min_element>), (metal::number<0>)); \ + CHECK((metal::min_element, metal::lambda>), (metal::number<0>)); \ + CHECK((metal::min_element>), (metal::number<0>)); \ +/**/ + +GEN(MATRIX)