1 /*
2     Copyright (C) 2013 Tom Bachmann
3 
4     This file is part of FLINT.
5 
6     FLINT is free software: you can redistribute it and/or modify it under
7     the terms of the GNU Lesser General Public License (LGPL) as published
8     by the Free Software Foundation; either version 2.1 of the License, or
9     (at your option) any later version.  See <http://www.gnu.org/licenses/>.
10 */
11 
12 // Lazy tuple class (for use in expression templates)
13 // Note that assignment and comparison are performed elementwise, and types
14 // need not match (even for equality) as long as the operations can be performed
15 // on underlying types.
16 
17 #ifndef FLINTXX_LTUPLE_H
18 #define FLINTXX_LTUPLE_H
19 
20 #ifndef FLINT_LTUPLE_PLACEHOLDER_NAME
21 #define FLINT_LTUPLE_PLACEHOLDER_NAME _
22 #endif
23 
24 #include "expression.h"
25 #include "tuple.h"
26 
27 namespace flint {
28 // For lazy get<n>, this operation type is created.
29 namespace operations {
30 template<unsigned n> struct ltuple_get_op { };
31 } // operations
32 
33 namespace detail {
34 // Empty marker type
35 struct INSTANTIATE_FROM_TUPLE { };
36 
37 // Traits for the ltuple expression template get<> operation. If the ltuple is
38 // an immediate, return references. Else if the return type is an expression
39 // template, return an expression template. Otherwise, evaluate the ltuple and
40 // return a copy of the entry.
41 template<unsigned n, class Underlying, class Operation, class Data, class Expr,
42     class Enable = void>
43 struct ltuple_get_traits
44 {
45     typedef unary_op_helper<operations::ltuple_get_op<n>, Expr> uoh;
46     typedef typename uoh::return_t type;
47     typedef type ctype;
getltuple_get_traits48     static type get(const Expr& e)
49     {
50         return uoh::make(e);
51     }
52 };
53 
54 template<unsigned n, class Underlying, class Data, class Expr>
55 struct ltuple_get_traits<n, Underlying, operations::immediate, Data, Expr>
56 {
57     typedef mp::tuple_get<Underlying, n> getter;
58     typedef typename getter::type btype;
59     typedef typename traits::forwarding<btype>::type ctype;
60     typedef typename traits::reference<btype>::type type;
61 
62     static type get(Expr& t)
63     {
64         return getter::get(t._data().inner);
65     }
66     static ctype get(const Expr& t)
67     {
68         return getter::get(t._data().inner);
69     }
70 };
71 
72 template<unsigned n, class Underlying, class Operation, class Data, class Expr>
73 struct ltuple_get_traits<n, Underlying, Operation, Data, Expr,
74     typename mp::enable_if<mp::and_<
75         mp::not_<mp::equal_types<Operation, operations::immediate> >,
76         mp::not_<traits::is_expression<typename mp::tuple_get<Underlying, n>::type> >
77       > >::type>
78 {
79     typedef mp::tuple_get<Underlying, n> getter;
80     typedef typename getter::type type;
81     typedef type ctype;
82 
83     static type get(const Expr& t)
84     {
85         return getter::get(t.evaluate()._data().inner);
86     }
87 };
88 
89 // Instances of this can be passed to ltuple[ref]() and will be replaced by
90 // temporaries of the right type before assigment.
91 struct IGNORED_TYPE { };
92 template<class Ltuple, class To, class Enable = void>
93 struct ltuple_instantiate_ignored_types;
94 } // detail
95 
96 // The ltuple expression template class. Underlying is a tuple type.
97 template<class Underlying, class Operation, class Data>
98 class ltuple_expression
99     : public expression<derived_wrapper2<ltuple_expression, Underlying>,
100                         Operation, Data>
101 {
102 public:
103     typedef expression<derived_wrapper2< ::flint::ltuple_expression, Underlying>,
104                 Operation, Data> base_t;
105     // internal
106     typedef void IS_LTUPLE_EXPRESSION;
107 
108     typedef Underlying underlying_t;
109 
110     ltuple_expression() {}
111 
112     template<class T>
113     ltuple_expression(detail::INSTANTIATE_FROM_TUPLE i, const T& t)
114         : base_t(i, t) {}
115 
116     template<class T>
117     ltuple_expression& operator=(const T& t)
118     {
119         detail::ltuple_instantiate_ignored_types<
120             ltuple_expression, T> inst(*this, t);
121         inst.set(t);
122         return *this;
123     }
124 
125     template<unsigned n>
126     typename detail::ltuple_get_traits<n, Underlying,
127              Operation, Data, ltuple_expression>::type get()
128     {
129         return detail::ltuple_get_traits<
130             n, Underlying, Operation, Data, ltuple_expression>::get(*this);
131     }
132     template<unsigned n>
133     typename detail::ltuple_get_traits<n, Underlying,
134              Operation, Data, ltuple_expression>::ctype
135     get() const
136     {
137         return detail::ltuple_get_traits<
138             n, Underlying, Operation, Data, ltuple_expression>::get(*this);
139     }
140 
141     typename base_t::evaluated_t create_temporary() const
142     {
143         return typename base_t::evaluated_t(detail::INSTANTIATE_FROM_TUPLE(),
144                 mp::htuples::fill<underlying_t>(tools::temporaries_filler(*this)));
145     }
146 
147 protected:
148     explicit ltuple_expression(const Data& d) : base_t(d) {}
149 
150     template<class D, class O, class Da>
151     friend class expression;
152 };
153 
154 namespace detail {
155 template<class Underlying>
156 struct ltuple_data
157 {
158     Underlying inner;
159 
160     ltuple_data() {}
161 
162     template<class T>
163     ltuple_data(INSTANTIATE_FROM_TUPLE, const T& t) : inner(t) {}
164 };
165 
166 template<class T> struct to_ref : traits::reference<T> { };
167 template<class T> struct to_srcref
168     : traits::reference<typename traits::make_const<T>::type> { };
169 
170 template<template<class>class Transform, class Tuple>
171 struct transform_tuple
172 {
173     typedef tuple<typename Transform<typename Tuple::head_t>::type,
174               typename transform_tuple<Transform, typename Tuple::tail_t>::type>
175                   type;
176 };
177 template<template<class>class Transform>
178 struct transform_tuple<Transform, empty_tuple>
179 {
180     typedef empty_tuple type;
181 };
182 } // detail
183 
184 // Helper for building ltuple types.
185 template<class Underlying>
186 struct make_ltuple
187 {
188     typedef ltuple_expression<Underlying, operations::immediate,
189               detail::ltuple_data<Underlying> > type;
190 
191     typedef typename detail::transform_tuple<detail::to_ref, Underlying>::type
192         Underlying_ref;
193     typedef typename detail::transform_tuple<detail::to_srcref, Underlying>::type
194         Underlying_srcref;
195 
196     typedef ltuple_expression<Underlying_ref, operations::immediate,
197               detail::ltuple_data<Underlying_ref> > ref_type;
198     typedef ltuple_expression<Underlying_srcref, operations::immediate,
199               detail::ltuple_data<Underlying_srcref> > srcref_type;
200 };
201 
202 namespace traits {
203 template<class Tuple, class Enable = void>
204 struct is_ltuple_expr : mp::false_ { };
205 template<class Tuple>
206 struct is_ltuple_expr<Tuple, typename Tuple::IS_LTUPLE_EXPRESSION>
207     : mp::true_ { };
208 
209 // enable evaluation directly into tuple
210 template<class To, class From>
211 struct can_evaluate_into<To, From,
212     typename mp::enable_if<mp::and_<is_ltuple_expr<From>,
213         is_ltuple_expr<To>, mp::not_<mp::equal_types<To, From> >
214       > >::type> : mp::true_ { };
215 } // traits
216 
217 namespace detail {
218 template<class Ltuple, class To, class Enable>
219 struct ltuple_instantiate_ignored_types
220 {
221     // degenerate case: To is not a tuple
222     Ltuple& saved;
223     ltuple_instantiate_ignored_types(Ltuple& s, const To&) : saved(s) {}
224     void set(const To& to) {saved.set(to);}
225 };
226 template<class ToTuple, class FromTuple>
227 struct tuple_instantiate_ignored
228 {
229     typedef tuple_instantiate_ignored<typename ToTuple::tail_t,
230                 typename FromTuple::tail_t> next_t;
231     typedef typename traits::reference<typename ToTuple::head_t>::type ref_t;
232     typedef tuple<ref_t, typename next_t::type> type;
233     next_t next;
234     ref_t ref;
235 
236     template<class From>
237     tuple_instantiate_ignored(ToTuple& to, const From& from)
238         : next(to.tail, from), ref(to.head) {}
239 
240     type get()
241     {
242         return type(ref, next.get());
243     }
244 };
245 template<>
246 struct tuple_instantiate_ignored<empty_tuple, empty_tuple>
247 {
248     typedef empty_tuple type;
249     template<class F>
250     tuple_instantiate_ignored(empty_tuple, const F&) {}
251     empty_tuple get() {return empty_tuple();}
252 };
253 template<class ToTail, class From, class FromTail>
254 struct tuple_instantiate_ignored<
255     tuple<IGNORED_TYPE&, ToTail>, tuple<From, FromTail> >
256 {
257     typedef tuple_instantiate_ignored<ToTail, FromTail> next_t;
258     typedef typename traits::reference<From>::type ref_t;
259     typedef tuple<ref_t, typename next_t::type> type;
260     next_t next;
261     From tmp;
262 
263     template<class ToTuple, class FromExpr>
264     tuple_instantiate_ignored(ToTuple& to, const FromExpr& from)
265         : next(to.tail, from),
266           tmp(rules::instantiate_temporaries<FromExpr, From>::get(from)) {}
267 
268     type get()
269     {
270         return type(tmp, next.get());
271     }
272 };
273 template<class Ltuple, class T>
274 struct ltuple_instantiate_ignored_types<Ltuple, T,
275     typename mp::enable_if<traits::is_ltuple_expr<T> >::type>
276 {
277     typedef tuple_instantiate_ignored<
278         typename Ltuple::underlying_t, typename T::underlying_t> tii_t;
279     tii_t tii;
280     ltuple_instantiate_ignored_types(Ltuple& l, const T& t)
281         : tii(l._data().inner, t) {}
282     void set(const T& t)
283     {
284         typename make_ltuple<typename tii_t::type>::type(INSTANTIATE_FROM_TUPLE(),
285             tii.get()).set(t);
286     }
287 };
288 }
289 
290 namespace rules {
291 template<class Tuple1, class Tuple2>
292 struct assignment<Tuple1, Tuple2,
293     typename mp::enable_if<mp::and_<
294         traits::is_ltuple_expr<Tuple2>,
295         traits::is_ltuple_expr<Tuple1> > >::type>
296 {
297     static void doit(Tuple1& to, const Tuple2& from)
298     {
299         to._data().inner.set(from._data().inner);
300     }
301 };
302 
303 template<class Tuple1, class Tuple2>
304 struct equals<Tuple1, Tuple2,
305     typename mp::enable_if<mp::and_<
306         traits::is_ltuple_expr<Tuple2>,
307         traits::is_ltuple_expr<Tuple1> > >::type>
308 {
309     static bool get(const Tuple1& to, const Tuple2& from)
310     {
311         return to._data().inner.equals_elementwise(from._data().inner);
312     }
313 };
314 
315 template<class Tuple, unsigned n>
316 struct unary_expression<operations::ltuple_get_op<n>, Tuple,
317     typename mp::enable_if<mp::and_<
318         traits::is_ltuple_expr<Tuple>,
319         traits::is_immediate<Tuple> > >::type>
320 {
321     typedef typename mp::tuple_get<typename Tuple::underlying_t, n>::type
322         return_t;
323     template<class R>
324     static void doit(R& to, const Tuple& from)
325     {
326         to = from.template get<n>();
327     }
328 };
329 } // rules
330 
331 // TODO we would really like variadic templates / lvalue references here
332 
333 // Helpers to build ltuples from (references to) arguments.
334 
335 template<class T>
336 inline typename make_ltuple<typename mp::make_tuple<T>::type>::type
337 ltuple(const T& t)
338 {
339     return typename make_ltuple<typename mp::make_tuple<T>::type>::type(
340             detail::INSTANTIATE_FROM_TUPLE(),
341             mp::make_tuple<T>::make(t));
342 }
343 template<class T, class U>
344 inline typename make_ltuple<typename mp::make_tuple<T, U>::type>::type
345 ltuple(const T& t, const U& u)
346 {
347     return typename make_ltuple<typename mp::make_tuple<T, U>::type>::type(
348             detail::INSTANTIATE_FROM_TUPLE(),
349             mp::make_tuple<T, U>::make(t, u));
350 }
351 
352 template<class T, class U, class V>
353 inline typename make_ltuple<typename mp::make_tuple<T, U, V>::type>::type
354 ltuple(const T& t, const U& u, const V& v)
355 {
356     return typename make_ltuple<typename mp::make_tuple<T, U, V>::type>::type(
357             detail::INSTANTIATE_FROM_TUPLE(),
358             mp::make_tuple<T, U, V>::make(t, u, v));
359 }
360 template<class T, class U, class V, class W>
361 inline typename make_ltuple<typename mp::make_tuple<T, U, V, W>::type>::type
362 ltuple(const T& t, const U& u, const V& v, const W& w)
363 {
364     return typename make_ltuple<typename mp::make_tuple<T, U, V, W>::type>::type(
365             detail::INSTANTIATE_FROM_TUPLE(),
366             mp::make_tuple<T, U, V, W>::make(t, u, v, w));
367 }
368 template<class T>
369 inline typename make_ltuple<typename mp::make_tuple<T&>::type>::type
370 ltupleref(T& t)
371 {
372     return typename make_ltuple<typename mp::make_tuple<T&>::type>::type(
373             detail::INSTANTIATE_FROM_TUPLE(),
374             mp::make_tuple<T&>::make(t));
375 }
376 template<class T, class U>
377 inline typename make_ltuple<typename mp::make_tuple<T&, U&>::type>::type
378 ltupleref(T& t, U& u)
379 {
380     return typename make_ltuple<typename mp::make_tuple<T&, U&>::type>::type(
381             detail::INSTANTIATE_FROM_TUPLE(),
382             mp::make_tuple<T&, U&>::make(t, u));
383 }
384 template<class T, class U, class V>
385 inline typename make_ltuple<typename mp::make_tuple<T&, U&, V&>::type>::type
386 ltupleref(T& t, U& u, V& v)
387 {
388     return typename make_ltuple<typename mp::make_tuple<T&, U&, V&>::type>::type(
389             detail::INSTANTIATE_FROM_TUPLE(),
390             mp::make_tuple<T&, U&, V&>::make(t, u, v));
391 }
392 template<class T, class U, class V, class W>
393 inline typename make_ltuple<typename mp::make_tuple<T&, U&, V&, W&>::type>::type
394 ltupleref(T& t, U& u, V& v, W& w)
395 {
396     return typename make_ltuple<typename mp::make_tuple<T&, U&, V&, W&>::type>::type(
397             detail::INSTANTIATE_FROM_TUPLE(),
398             mp::make_tuple<T&, U&, V&, W&>::make(t, u, v, w));
399 }
400 
401 // static placeholder
402 static detail::IGNORED_TYPE FLINT_LTUPLE_PLACEHOLDER_NAME;
403 
404 namespace detail {
405 void remove_compiler_warning(
406         detail::IGNORED_TYPE* = &FLINT_LTUPLE_PLACEHOLDER_NAME);
407 } // detail
408 } // flint
409 
410 #endif
411