1 // Copyright Louis Dionne 2013-2017
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
4 
5 #include <boost/hana/all_of.hpp>
6 #include <boost/hana/assert.hpp>
7 #include <boost/hana/cartesian_product.hpp>
8 #include <boost/hana/contains.hpp>
9 #include <boost/hana/core/to.hpp>
10 #include <boost/hana/equal.hpp>
11 #include <boost/hana/functional/demux.hpp>
12 #include <boost/hana/fuse.hpp>
13 #include <boost/hana/integral_constant.hpp>
14 #include <boost/hana/minus.hpp>
15 #include <boost/hana/mod.hpp>
16 #include <boost/hana/not.hpp>
17 #include <boost/hana/not_equal.hpp>
18 #include <boost/hana/or.hpp>
19 #include <boost/hana/plus.hpp>
20 #include <boost/hana/set.hpp>
21 #include <boost/hana/transform.hpp>
22 #include <boost/hana/tuple.hpp>
23 namespace hana = boost::hana;
24 using namespace hana::literals;
25 
26 
27 // A function that can have an arbitrary compile-time set of values as a domain
28 // and co-domain. This is most likely purely of theoretical interest, but it
29 // allows creating functions with very complex domains and co-domains that are
30 // computed at compile-time.
31 
32 struct Function { };
33 
34 template <typename Domain, typename Codomain, typename F>
35 struct function_type {
36     using hana_tag = Function;
37 
38     Domain domain_;
39     Codomain codomain_;
40     F f_;
41 
42     template <typename X>
operator ()function_type43     constexpr auto operator()(X x) const {
44         BOOST_HANA_CONSTANT_ASSERT(boost::hana::contains(domain_, x));
45         return f_(x);
46     }
47 };
48 
49 template <typename ...F, typename ...G>
operator ==(function_type<F...> f,function_type<G...> g)50 constexpr auto operator==(function_type<F...> f, function_type<G...> g)
51 { return hana::equal(f, g); }
52 
53 template <typename ...F, typename ...G>
operator !=(function_type<F...> f,function_type<G...> g)54 constexpr auto operator!=(function_type<F...> f, function_type<G...> g)
55 { return hana::not_equal(f, g); }
56 
57 
__anon7a4326870102(auto domain, auto codomain) 58 auto function = [](auto domain, auto codomain) {
59     return [=](auto definition) {
60         return function_type<decltype(domain), decltype(codomain), decltype(definition)>{
61             domain, codomain, definition
62         };
63     };
64 };
65 
66 template <typename Function>
domain(Function f)67 constexpr auto domain(Function f)
68 { return f.domain_; }
69 
70 template <typename Function>
codomain(Function f)71 constexpr auto codomain(Function f)
72 { return f.codomain_; }
73 
74 template <typename Function>
range(Function f)75 constexpr auto range(Function f) {
76     // We must convert to hana::tuple first because hana::set is not a Functor
77     return hana::to_set(hana::transform(hana::to_tuple(domain(f)), f));
78 }
79 
80 template <typename P, typename Q>
implies(P p,Q q)81 constexpr auto implies(P p, Q q) {
82     return hana::or_(hana::not_(p), q);
83 }
84 
85 template <typename F>
is_injective(F f)86 constexpr auto is_injective(F f) {
87     auto dom = hana::to_tuple(domain(f));
88     auto pairs = hana::cartesian_product(hana::make_tuple(dom, dom));
89     return hana::all_of(pairs, hana::fuse([&](auto x, auto y) {
90         return implies(hana::not_equal(x, y), hana::not_equal(f(x), f(y)));
91     }));
92 }
93 
94 template <typename F>
is_onto(F f)95 constexpr auto is_onto(F f) {
96     return codomain(f) == range(f);
97 }
98 
99 namespace boost { namespace hana {
100     template <>
101     struct equal_impl<Function, Function> {
102         template <typename F, typename G>
applyboost::hana::equal_impl103         static constexpr auto apply(F f, G g) {
104             return domain(f) == domain(g) &&
105                    hana::all_of(domain(f), hana::demux(hana::equal)(f, g));
106         }
107     };
108 }} // end namespace boost::hana
109 
main()110 int main() {
111     auto f = function(hana::make_set(1_c, 2_c, 3_c), hana::make_set(1_c, 2_c, 3_c, 4_c, 5_c, 6_c))(
112         [](auto x) { return x + 1_c; }
113     );
114 
115     auto g = function(hana::make_set(1_c, 2_c, 3_c), hana::make_set(2_c, 3_c, 4_c))(
116         [](auto x) { return x + 1_c; }
117     );
118 
119     auto h = function(hana::make_set(1_c, 2_c, 3_c), hana::make_set(0_c, 1_c, 2_c))(
120         [](auto x) { return x - 1_c; }
121     );
122 
123     BOOST_HANA_CONSTANT_CHECK(f == g);
124     BOOST_HANA_CONSTANT_CHECK(f != h);
125     BOOST_HANA_CONSTANT_CHECK(f(1_c) == 2_c);
126 
127     BOOST_HANA_CONSTANT_CHECK(range(f) == hana::make_set(4_c, 3_c, 2_c));
128     BOOST_HANA_CONSTANT_CHECK(range(g) == hana::make_set(2_c, 3_c, 4_c));
129     BOOST_HANA_CONSTANT_CHECK(range(h) == hana::make_set(0_c, 1_c, 2_c));
130 
131     BOOST_HANA_CONSTANT_CHECK(hana::not_(is_onto(f)));
132     BOOST_HANA_CONSTANT_CHECK(is_onto(g));
133     BOOST_HANA_CONSTANT_CHECK(is_onto(h));
134 
135     auto even = function(hana::make_set(1_c, 2_c, 3_c), hana::make_set(hana::true_c, hana::false_c))(
136         [](auto x) { return x % 2_c == 0_c; }
137     );
138 
139     BOOST_HANA_CONSTANT_CHECK(is_injective(f));
140     BOOST_HANA_CONSTANT_CHECK(is_injective(g));
141     BOOST_HANA_CONSTANT_CHECK(is_injective(h));
142     BOOST_HANA_CONSTANT_CHECK(hana::not_(is_injective(even)));
143 }
144